style(prompt-studio): default to left dock, auto-orient track facing canvas, widen cards column to 190px and eliminate topbar wrapping

This commit is contained in:
DemonGatanjieu
2026-09-14 21:44:05 +08:00
parent e1f8bf39d9
commit 96c340b79b
3 changed files with 256 additions and 83 deletions
+6 -5
View File
@@ -193,11 +193,12 @@ DOM or live LiteGraph state.
- **Floating Trigger Button & Anomalous Hypercube Core (悬浮入口魔晶重构)**:
- 重绘旧版扁平纸箱立方体为 **「异象超维魔方核 (Anomalous Hypercube Core)」**:外层等轴测立体超维立方体轮廓,内嵌悬浮发光微晶棱镜(`--amb-accent` 随主题自适应为天青/猩红),核心凝聚一枚四芒星辉奇点(`✦`),兼具 3D 模型容器特征与魔法档案馆星辰意象。
- 悬浮球体容器 (`#anomalous-trigger-btn`) 升级为亚克力磨砂深色毛玻璃材质 (`backdrop-filter: blur(16px)`),悬停微移提亮,图标伴随 12° 优雅微旋与天青/猩红柔和微光,保持高度克制的同时极具辨识度与高级质感。
- **Prompt Studio Dual-Column Workbench (提示词工坊两行顶栏与容器自适应)**:
- Topbar restructured into two clean rows: Row 1 hosts Back, shortened title ("提示词组合"), New, "更多 ▾" dropdown (containing Export), and primary Save Plan; Row 2 hosts expanded preset name and tag inputs.
- Insert position ("添加到前面/后面") relocated into the target node write bar, adjacent to widget selection and write triggers where execution actually happens.
- Left Column ("词卡库"): 280-320px width, clean cards with explicit text role badges (`[正向]` / `[负向]`), 2-line preview, single top drag hint, and secondary node/material extract buttons.
- Responsive Container Query: Uses `@container prompt-workbench (max-width: 850px)` to smoothly stack panels vertically in narrow dock/floating windows without clipping or forced 100% heights.
- **Prompt Studio Dual-Column Workbench (提示词工坊侧边抽屉与双向自适应停靠)**:
- **Default Left Docking & Canvas-Facing Flow**: Defaults to left dock (`is-dock-left`), with Source Cards (190px) on the outer screen edge and Assembly Track on the inner side directly facing the ComfyUI canvas.
- **Bidirectional Column Flipping**: When toggled to right dock, the layout seamlessly swaps column visual order (`order: 1` for Assembly Track facing canvas on left, `order: 2` for Source Cards along right screen edge) so that the Assembly Track is ALWAYS adjacent to the ComfyUI canvas regardless of docking side.
- **Single-Line Unbroken Topbar**: Topbar strictly enforces `flex-wrap: nowrap`, keeping Title, Preset Name input (constrained max-width 140px), Node Extraction (`🎯`), Dock Toggle (`→`/`←`), More dropdown (`··· 更多`), and Close (`✕`) in a unified single row under any drawer width.
- **De-cluttered Left Panel**: Expanded left column from 145px to 190px, purged redundant text buttons into compact 24x24 icon buttons (`[📥]` and `[+]`), shortened category filter pills to clean 2-character chips (`[全部]`, `[底模]`, `[风格]`, `[主体]`, `[触发]`) fitting into a single row, and removed bulky instructional tip banners.
- **Dynamic Floating Action Dock & Popout Modal**: Floating Action Dock slides up only when blocks exist; provides single-click Node Direct Write and popout full-screen Prompt Inspector (`openPromptInspectorModal`) for deep multi-line tuning.
- **Uniform Card Skeletons**: Recipe and material cards enforce "Preview -> 2-Line Clamped Title -> Key Specs -> Footer Action Bar" order, preventing Chinese title clipping; Model cards display titles and filenames at rest without `translateY` collapse and without `backdrop-filter: blur`, fading in action buttons on hover.
- **Custom Tooltip System (`[data-tooltip]`)**: Solid dark capsules (`rgba(18, 20, 26, 0.98)`), 350ms standard hover delay, and keyboard focus-visible support.
+32 -47
View File
@@ -345,15 +345,15 @@ export async function openPromptStudio(owner = this) {
activeStudioDrawer = drawer;
overlay.appendChild(drawer);
// Dock side persistence
const savedSide = localStorage.getItem('anomalous_studio_dock_side') || 'right';
// Dock side persistence (default to left)
const savedSide = localStorage.getItem('anomalous_studio_dock_side') || 'left';
if (savedSide === 'left') {
drawer.classList.add('is-dock-left');
}
// Sidebar width persistence & resize handle
const savedWidth = localStorage.getItem('anomalous_studio_sidebar_width') || '520';
drawer.style.width = `${Math.max(400, Math.min(window.innerWidth * 0.85, parseInt(savedWidth, 10)))}px`;
// Sidebar width persistence & resize handle (default to 580px)
const savedWidth = localStorage.getItem('anomalous_studio_sidebar_width') || '580';
drawer.style.width = `${Math.max(420, Math.min(window.innerWidth * 0.85, parseInt(savedWidth, 10)))}px`;
const resizeHandle = document.createElement('div');
resizeHandle.className = 'anomalous-studio-resize-handle';
@@ -403,8 +403,8 @@ export async function openPromptStudio(owner = this) {
};
resizeHandle.ondblclick = () => {
drawer.style.width = '520px';
localStorage.setItem('anomalous_studio_sidebar_width', '520');
drawer.style.width = '580px';
localStorage.setItem('anomalous_studio_sidebar_width', '580');
};
const onKeydown = (e) => {
@@ -596,10 +596,12 @@ function buildPromptComposer(owner, container, options = {}) {
const dockSideBtn = text(topActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm');
const isLeft = () => activeStudioDrawer?.classList.contains('is-dock-left');
const updateDockBtn = () => {
dockSideBtn.innerHTML = isLeft() ? '⇥' : '⇤';
dockSideBtn.innerHTML = isLeft()
? `<svg style="width:13px;height:13px;vertical-align:middle;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>`
: `<svg style="width:13px;height:13px;vertical-align:middle;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>`;
dockSideBtn.title = isLeft()
? (window.anomalous_browser_lang === 'zh' ? '停靠至右侧' : 'Dock Right')
: (window.anomalous_browser_lang === 'zh' ? '停靠至左侧' : 'Dock Left');
? (window.anomalous_browser_lang === 'zh' ? '停靠至右侧' : 'Dock to Right')
: (window.anomalous_browser_lang === 'zh' ? '停靠至左侧' : 'Dock to Left');
};
updateDockBtn();
dockSideBtn.onclick = () => {
@@ -708,25 +710,20 @@ function buildPromptComposer(owner, container, options = {}) {
// Action button group in leftHeader
const leftHeaderActions = text(leftHeader, 'div', '', 'anomalous-workbench-header-actions');
// Button 1: Extract Prompts from Selected Canvas Node
const extractNodeBtn = text(leftHeaderActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm anomalous-btn-extract-node');
extractNodeBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:3px;vertical-align:-1px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>${window.anomalous_browser_lang === 'zh' ? '提取' : 'Extract'}`;
extractNodeBtn.title = window.anomalous_browser_lang === 'zh' ? '从画布当前选中节点提取提示词并生成词卡' : 'Extract prompt text from selected canvas node into cards';
// Button 1: One-click Sync / Import from Material Library
const importMaterialsBtn = text(leftHeaderActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm anomalous-icon-btn');
importMaterialsBtn.innerHTML = `<svg style="width:13px;height:13px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`;
importMaterialsBtn.title = window.anomalous_browser_lang === 'zh' ? '从素材库同步提示词' : 'Sync prompts from Material Library';
// Button 2: One-click Sync / Import from Material Library
const importMaterialsBtn = text(leftHeaderActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm');
importMaterialsBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:3px;vertical-align:-1px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>${window.anomalous_browser_lang === 'zh' ? '导入' : 'Import'}`;
importMaterialsBtn.title = window.anomalous_browser_lang === 'zh' ? '读取素材库,一键把素材库里沉淀的提示词捞入当前工坊词库' : 'Read and import all prompts from Material Library into workbench';
// Button 3: Create New Custom Card (placed in header right, never wraps)
const newCardTriggerBtn = text(leftHeaderActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm');
newCardTriggerBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:3px;vertical-align:-1px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>${window.anomalous_browser_lang === 'zh' ? '新建' : 'New'}`;
newCardTriggerBtn.title = window.anomalous_browser_lang === 'zh' ? '新建并保存一张提示词卡片' : 'Create a new prompt card';
// Button 2: Create New Custom Card
const newCardTriggerBtn = text(leftHeaderActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm anomalous-icon-btn');
newCardTriggerBtn.innerHTML = `<svg style="width:13px;height:13px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>`;
newCardTriggerBtn.title = window.anomalous_browser_lang === 'zh' ? '新建词卡' : 'Create new prompt card';
// Search and category filters bar
const leftFilterBar = text(leftPanel, 'div', '', 'anomalous-workbench-filter-bar');
const leftSearch = text(leftFilterBar, 'input', '', 'anomalous-workbench-search-input');
leftSearch.placeholder = window.anomalous_browser_lang === 'zh' ? '搜索提示词或分类...' : 'Search prompt snippet...';
leftSearch.placeholder = window.anomalous_browser_lang === 'zh' ? '搜索词卡...' : 'Search cards...';
leftSearch.oninput = () => {
sourceFilterKeyword = leftSearch.value.trim().toLowerCase();
renderSourceCardsList();
@@ -735,10 +732,10 @@ function buildPromptComposer(owner, container, options = {}) {
const leftCategoryPills = text(leftPanel, 'div', '', 'anomalous-workbench-category-pills');
const filterCats = [
{ id: 'all', label: window.anomalous_browser_lang === 'zh' ? '全部' : 'All' },
{ id: 'base', label: window.anomalous_browser_lang === 'zh' ? '通用底模' : 'Base' },
{ id: 'style', label: window.anomalous_browser_lang === 'zh' ? '风格氛围' : 'Style' },
{ id: 'subject', label: window.anomalous_browser_lang === 'zh' ? '主体内容' : 'Subject' },
{ id: 'trigger', label: window.anomalous_browser_lang === 'zh' ? '触发词' : 'Trigger' },
{ id: 'base', label: window.anomalous_browser_lang === 'zh' ? '底模' : 'Base' },
{ id: 'style', label: window.anomalous_browser_lang === 'zh' ? '风格' : 'Style' },
{ id: 'subject', label: window.anomalous_browser_lang === 'zh' ? '主体' : 'Subject' },
{ id: 'trigger', label: window.anomalous_browser_lang === 'zh' ? '触发' : 'Trigger' },
];
filterCats.forEach(cat => {
const pill = text(leftCategoryPills, 'button', cat.label, `anomalous-workbench-pill${sourceFilterCategory === cat.id ? ' is-active' : ''}`);
@@ -754,15 +751,6 @@ function buildPromptComposer(owner, container, options = {}) {
const newCardForm = text(leftPanel, 'div', '', 'anomalous-workbench-new-card-form');
newCardForm.style.display = 'none';
// Usage Tip Banner
const tipBanner = text(leftPanel, 'div', '', 'anomalous-workbench-tip-banner');
tipBanner.innerHTML = `
<svg class="anomalous-tip-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>
</svg>
<span>${window.anomalous_browser_lang === 'zh' ? '点击词卡或拖拽至右侧拼装台,自由调换顺序' : 'Click card or drag to right stage to assemble'}</span>
`;
// Source Cards List
const sourceCardsList = text(leftPanel, 'div', '', 'anomalous-source-cards-list');
@@ -1021,7 +1009,6 @@ function buildPromptComposer(owner, container, options = {}) {
}
}
extractNodeBtn.onclick = () => extractPromptsFromSelectedNode(false);
rightSuckNodeBtn.onclick = () => extractPromptsFromSelectedNode(true);
let materialSyncController = null;
@@ -1034,7 +1021,7 @@ function buildPromptComposer(owner, container, options = {}) {
materialSyncController = controller;
importMaterialsBtn.disabled = true;
importMaterialsBtn.textContent = window.anomalous_browser_lang === 'zh' ? '正在同步素材...' : 'Syncing...';
importMaterialsBtn.innerHTML = `<span style="font-size:11px;">⏳</span>`;
try {
const res = await fetch('/anomalous/materials?category=prompts&limit=150', { signal: controller.signal });
@@ -1084,7 +1071,7 @@ function buildPromptComposer(owner, container, options = {}) {
if (materialSyncController === controller) {
materialSyncController = null;
importMaterialsBtn.disabled = false;
importMaterialsBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:3px;vertical-align:-1px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>${window.anomalous_browser_lang === 'zh' ? '导入' : 'Import'}`;
importMaterialsBtn.innerHTML = `<svg style="width:13px;height:13px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`;
}
}
}
@@ -1428,15 +1415,13 @@ function buildPromptComposer(owner, container, options = {}) {
if (!currentRoleParts.length) {
const dropzoneNotice = text(blocksContainer, 'div', '', 'anomalous-assembly-dropzone');
dropzoneNotice.innerHTML = `
<div style="margin-bottom: 12px; opacity: 0.7;">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
<div style="margin-bottom: 12px; opacity: 0.65;">
<svg width="38" height="38" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 3v12m0 0l-4-4m4 4l4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/>
</svg>
</div>
<div style="font-size: 0.92rem; font-weight: 600; color: #e2e8f0;">${window.anomalous_browser_lang === 'zh' ? '点击下方词卡或拖拽到这里拼装' : 'Click cards below or drop here to assemble'}</div>
<div style="font-size: 0.76rem; color: #64748b; margin-top: 4px;">${window.anomalous_browser_lang === 'zh' ? '支持自由调换次序,点击【智能排序】自动理顺' : 'Drag to reorder anytime, or click Smart Sort.'}</div>
<div style="font-size: 0.88rem; font-weight: 600; color: #f1f5f9;">${window.anomalous_browser_lang === 'zh' ? '点击词卡或拖拽到这里拼装' : 'Click cards or drag here to assemble'}</div>
<div style="font-size: 0.74rem; color: #64748b; margin-top: 5px;">${window.anomalous_browser_lang === 'zh' ? '支持自由拖拽调换次序,点击【智能排序】一键理顺' : 'Reorder freely anytime, or click Smart Sort.'}</div>
`;
// Accept drops on empty dropzone
setupDropzoneListeners(dropzoneNotice);
+218 -31
View File
@@ -9541,31 +9541,40 @@ dialog.anomalous-gallery-viewer::backdrop {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
flex-wrap: nowrap !important;
gap: 8px;
padding-bottom: 6px;
height: 44px;
padding: 0 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(20, 22, 27, 0.95);
box-sizing: border-box;
flex-shrink: 0;
}
.anomalous-prompt-topbar-left {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
flex: 1 1 auto;
}
.anomalous-prompt-topbar-left h3 {
margin: 0;
font-size: 0.98rem;
font-weight: 600;
font-size: 0.92rem;
font-weight: 700;
color: #f8fafc;
letter-spacing: 0.02em;
white-space: nowrap;
flex-shrink: 0;
}
.anomalous-prompt-topbar-actions {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
gap: 4px;
flex-shrink: 0;
flex-wrap: nowrap !important;
}
.anomalous-prompt-close-btn {
@@ -9585,16 +9594,16 @@ dialog.anomalous-gallery-viewer::backdrop {
background: var(--amb-bg-input, rgba(0, 0, 0, 0.35));
border: 1px solid var(--amb-border, rgba(255, 255, 255, 0.14));
border-radius: var(--amb-radius-control, 6px);
padding: 3px 8px;
padding: 2px 8px;
height: 26px;
color: var(--amb-text-main, #f1f5f9);
font-size: 0.78rem;
outline: none;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
box-sizing: border-box;
flex: 1 1 160px;
min-width: 100px;
max-width: 240px;
flex: 0 1 130px;
min-width: 60px;
max-width: 140px;
}
.anomalous-prompt-name-input:focus {
@@ -12830,7 +12839,7 @@ button:hover .anomalous-btn-icon {
position: absolute;
top: 0;
right: 0;
width: 520px;
width: 580px;
max-width: 90vw;
height: 100vh;
background: var(--amb-bg-page, #121316);
@@ -12874,7 +12883,7 @@ button:hover .anomalous-btn-icon {
box-shadow: 0 0 10px rgba(56, 189, 248, 0.6);
}
/* Drawer Workbench Grid */
/* Drawer Workbench Base */
.anomalous-prompt-studio-drawer .anomalous-prompt-workbench {
background: transparent;
padding: 0;
@@ -12883,40 +12892,190 @@ button:hover .anomalous-btn-icon {
flex-direction: column;
}
.anomalous-prompt-studio-drawer .anomalous-prompt-workbench-grid {
/* Left Dock Mode (Default): Left Column = Cards (190px), Right Column = Assembly Track (faces canvas on right) */
.anomalous-prompt-studio-drawer.is-dock-left .anomalous-prompt-workbench-grid {
display: grid !important;
grid-template-columns: 145px minmax(0, 1fr) !important;
grid-template-columns: 190px minmax(0, 1fr) !important;
flex-direction: row !important;
gap: 8px !important;
padding: 6px 8px 8px !important;
gap: 0 !important;
padding: 0 !important;
flex: 1;
min-height: 0;
}
.anomalous-prompt-studio-drawer .anomalous-workbench-left-panel {
.anomalous-prompt-studio-drawer.is-dock-left .anomalous-workbench-left-panel {
order: 1 !important;
flex: none !important;
width: 145px !important;
width: 190px !important;
max-height: 100% !important;
padding: 6px 6px 8px !important;
padding: 8px 10px !important;
border-right: 1px solid rgba(255, 255, 255, 0.08) !important;
border-left: none !important;
background: rgba(18, 20, 24, 0.7) !important;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.anomalous-prompt-studio-drawer .anomalous-workbench-right-panel {
.anomalous-prompt-studio-drawer.is-dock-left .anomalous-workbench-right-panel {
order: 2 !important;
flex: 1 !important;
max-height: 100% !important;
padding: 6px 8px 48px !important; /* reserve bottom space for floating dock */
padding: 8px 12px 52px !important; /* reserve bottom space for floating dock */
position: relative;
box-sizing: border-box;
background: rgba(14, 15, 18, 0.5) !important;
display: flex;
flex-direction: column;
}
/* Right Dock Mode: Right Column = Cards (190px), Left Column = Assembly Track (faces canvas on left) */
.anomalous-prompt-studio-drawer:not(.is-dock-left) .anomalous-prompt-workbench-grid {
display: grid !important;
grid-template-columns: minmax(0, 1fr) 190px !important;
flex-direction: row !important;
gap: 0 !important;
padding: 0 !important;
flex: 1;
min-height: 0;
}
.anomalous-prompt-studio-drawer:not(.is-dock-left) .anomalous-workbench-right-panel {
order: 1 !important;
flex: 1 !important;
max-height: 100% !important;
padding: 8px 12px 52px !important;
position: relative;
box-sizing: border-box;
border-right: 1px solid rgba(255, 255, 255, 0.08) !important;
border-left: none !important;
background: rgba(14, 15, 18, 0.5) !important;
display: flex;
flex-direction: column;
}
.anomalous-prompt-studio-drawer:not(.is-dock-left) .anomalous-workbench-left-panel {
order: 2 !important;
flex: none !important;
width: 190px !important;
max-height: 100% !important;
padding: 8px 10px !important;
border-left: none !important;
border-right: none !important;
background: rgba(18, 20, 24, 0.7) !important;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
/* Column Headers & Action Buttons */
.anomalous-workbench-col-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 30px;
padding: 0 2px;
margin-bottom: 6px;
flex-shrink: 0;
}
.anomalous-workbench-col-title {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 0.84rem;
font-weight: 600;
color: #f1f5f9;
white-space: nowrap;
}
.anomalous-workbench-col-title .anomalous-sub-counter {
font-size: 0.72rem;
color: #64748b;
font-weight: normal;
}
.anomalous-workbench-header-actions {
display: inline-flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.anomalous-workbench-header-actions button.anomalous-icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
border-radius: 5px;
color: #94a3b8;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
cursor: pointer;
transition: all 0.15s ease;
}
.anomalous-workbench-header-actions button.anomalous-icon-btn:hover {
color: #ffffff;
background: rgba(56, 189, 248, 0.18);
border-color: rgba(56, 189, 248, 0.4);
}
/* Category Pills: 5 equal 2-char chips */
.anomalous-workbench-category-pills {
display: flex;
align-items: center;
gap: 3px;
flex-wrap: nowrap;
margin: 4px 0 8px;
}
.anomalous-workbench-pill {
flex: 1 1 0;
text-align: center;
padding: 3px 0;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.03);
color: #94a3b8;
font-size: 0.72rem;
cursor: pointer;
transition: all 0.15s ease;
line-height: 1.3;
white-space: nowrap;
}
.anomalous-workbench-pill:hover {
color: #f1f5f9;
background: rgba(255, 255, 255, 0.08);
}
.anomalous-workbench-pill.is-active {
color: #38bdf8;
background: rgba(56, 189, 248, 0.15);
border-color: rgba(56, 189, 248, 0.4);
font-weight: 600;
}
/* Compact Source Cards in 190px */
.anomalous-source-cards-list {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 4px;
padding-right: 2px;
}
/* Compact Source Cards */
.anomalous-source-card-compact {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
padding: 5px 8px;
background: rgba(255, 255, 255, 0.035);
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 6px;
cursor: pointer;
transition: all 0.15s ease;
@@ -12926,27 +13085,31 @@ button:hover .anomalous-btn-icon {
}
.anomalous-source-card-compact:hover {
background: rgba(56, 189, 248, 0.15);
border-color: rgba(56, 189, 248, 0.4);
background: rgba(56, 189, 248, 0.12);
border-color: rgba(56, 189, 248, 0.35);
transform: translateX(2px);
}
.anomalous-source-card-dot {
width: 7px;
height: 7px;
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
.anomalous-source-card-name {
font-size: 0.74rem;
color: #e2e8f0;
font-size: 0.75rem;
color: #cbd5e1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.anomalous-source-card-compact:hover .anomalous-source-card-name {
color: #ffffff;
}
.anomalous-source-card-add-icon {
font-size: 0.72rem;
color: #64748b;
@@ -12960,6 +13123,30 @@ button:hover .anomalous-btn-icon {
color: #38bdf8;
}
/* Assembly Dropzone Notice */
.anomalous-assembly-dropzone {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
min-height: 260px;
padding: 40px 20px;
border: 1px dashed rgba(255, 255, 255, 0.1);
border-radius: 12px;
background: rgba(255, 255, 255, 0.015);
color: #64748b;
text-align: center;
box-sizing: border-box;
transition: all 0.2s ease;
}
.anomalous-assembly-dropzone:hover,
.anomalous-assembly-dropzone.is-drag-over {
border-color: rgba(56, 189, 248, 0.4);
background: rgba(56, 189, 248, 0.04);
}
/* Weight Wheel Pill in Assembled Blocks */
.anomalous-block-weight-pill {
display: inline-flex;