refactor(prompt-studio): reclaim vertical workspace and display final assembled text on-demand
This commit is contained in:
+8
-8
@@ -150,22 +150,22 @@ DOM or live LiteGraph state.
|
||||
the library and Node Assistant use `ui_material_application.js` for the same receipt.
|
||||
- `ui_prompt_composer.js` owns the Prompt Studio Dual-Column Workbench (提示词工坊左右双分栏工作台);
|
||||
provides a clean, professional studio workspace (`.anomalous-prompt-workbench`) stripped of gaudy neon effects, cyberpunk backgrounds, and pulsing glow animations;
|
||||
features compact vertical spacing (tightened paddings, 28px single-line metadata inputs, and 6px grid gaps) maximizing functional workspace;
|
||||
features responsive docked / sidebar adaptation with dedicated single-column collapse, generous vertical scrolling without arbitrary height clipping, and an inline responsive view switcher (`.anomalous-prompt-view-switcher`: `[🗃️ 词卡库 | 🎛️ 拼装台 | 📑 全部]`) for seamless navigation when docked as a sidebar (`#anomalous-container.anomalous-docked`) or on narrow screens;
|
||||
the Left Column hosts the Ready-to-use Prompt Cards Library (成型提示词库), featuring restrained dark slate card backgrounds (`#24262E`)
|
||||
with category-specific left accent borders (`border-left: 3.5px solid ...`, Base/Style/Subject/Trigger) eliminating color-palette clutter,
|
||||
minimalist empty state container (`.anomalous-source-empty`) with subtle dashed borders,
|
||||
non-wrapping title layout, a dedicated sub-action bar with one-click canvas node prompt extraction (`🎯 从节点提取`,
|
||||
reading selected ComfyUI text nodes like `CLIPTextEncode` with downstream link connection traversal for accurate negative conditioning detection, auto-generating categorised cards) and one-click
|
||||
Material Library batch sync (`📥 从素材库导入`, cancellable fetch scoped to `category=prompts`), plus on-demand persistent card creation
|
||||
(`➕ 新建词卡`, with explicit positive/negative role radios and `/anomalous/save_prompt_plan` backend persistence; temporary cards
|
||||
a streamlined col-header holding a unified action button group (`[🎯 提取]`, `[📥 导入]`, `[➕ 新建]`) that reclaims ~36px vertical height for the card list,
|
||||
one-click canvas node prompt extraction (`🎯 提取`, reading selected ComfyUI text nodes like `CLIPTextEncode` with downstream link connection traversal for accurate negative conditioning detection, auto-generating categorised cards) and one-click
|
||||
Material Library batch sync (`📥 导入`, cancellable fetch scoped to `category=prompts`), plus on-demand persistent card creation
|
||||
(`➕ 新建`, with explicit positive/negative role radios and `/anomalous/save_prompt_plan` backend persistence; temporary cards
|
||||
show `[未保存]` badge with one-click `💾 存入库`), completely eliminating default empty textareas; cards support native HTML5 drag-and-drop;
|
||||
the Right Column hosts the expanded Assembler & Arranger Stage (顺序编排调音台, `minmax(460px, 1fr)`), featuring quick node reading
|
||||
(`🎯 读取选中节点`), compact modular Lego block cards (~56px height, expandable on focus) allowing generous top-to-bottom sequence stacking,
|
||||
a clean dropzone (`.anomalous-assembly-dropzone`) that ingests dragged cards into positive/negative tracks with strict role isolation,
|
||||
(`🎯 读取选中节点`), quick final text toggle (`[📄 最终文本]`), compact modular Lego block cards (~56px height, expandable on focus) allowing generous top-to-bottom sequence stacking,
|
||||
a large, high-clearance dropzone (`.anomalous-assembly-dropzone`, min-height 240px) that ingests dragged cards into positive/negative tracks with strict role isolation,
|
||||
supports bidirectional drag-and-drop reordering with ghost indicator lines, role-aware up/down swapping, instant A/B bypass toggles (greyscale dimming without deleting),
|
||||
interactive category pills, one-click Smart Sort (`🪄 按分类排序`, Base ➔ Style ➔ Subject ➔ Trigger), a target widget dropdown with sticky
|
||||
selection cache (`targetWidgetIndexByNodeId`), and a sticky floating frosted-glass output deck (`.anomalous-mixer-deck-output`, `position: sticky; bottom: 0; backdrop-filter: blur(12px)`)
|
||||
with real-time word/token counts, single-mount canvas dragging (`bindMaterialDrag` onto text nodes), direct target node injection toolbar, and seamless return to Material Library;
|
||||
selection cache (`targetWidgetIndexByNodeId`), and an on-demand collapsible output deck (`.anomalous-mixer-deck-output`): completely hidden when 0 blocks, defaulting to a slim 32px pill summary bar (`✨ 最终正向文本 (X字符 · Y词组) [📋复制] [👁️查看最终文本 ▾]`) when cards exist to prevent premature clutter and screen squashing, and smoothly expandable into a full drawer containing the compiled textarea, whole-deck canvas drag dock (`bindMaterialDrag`), and target node direct write bar;
|
||||
`prompt_composition.js` provides bidirectional schema mapping (`planToWorkbenchDraft` and `workbenchDraftToSavedPlan`) ensuring
|
||||
100% roundtrip data integrity across `anomalous-prompt-plan-v1` and `version: 2`, lossless legacy dual-role splitting and trailing text retention,
|
||||
`categorizePromptSnippet`, `smartSortPromptBlocks`, `assemblePromptBlocks`, as well as backward-compatible text joining;
|
||||
|
||||
@@ -267,6 +267,7 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
let sourceFilterCategory = 'all';
|
||||
let sourceFilterKeyword = '';
|
||||
let draggedBlockId = null;
|
||||
let isOutputExpanded = false;
|
||||
|
||||
// Local in-memory source prompt cards (merged starters + materials + user custom)
|
||||
let sourceCards = [...STARTER_SOURCE_PROMPTS];
|
||||
@@ -387,26 +388,26 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
const leftPanel = text(workbenchGrid, 'section', '', 'anomalous-workbench-left-panel');
|
||||
const leftHeader = text(leftPanel, 'div', '', 'anomalous-workbench-col-header');
|
||||
const leftTitleWrap = text(leftHeader, 'div', '', 'anomalous-workbench-col-title');
|
||||
leftTitleWrap.innerHTML = `<strong>${window.anomalous_browser_lang === 'zh' ? '词卡库' : 'Prompt Library'}</strong> <span class="anomalous-sub-counter"></span><span style="font-size:11px;color:var(--amb-text-muted,#94a3b8);font-weight:normal;margin-left:6px;">${window.anomalous_browser_lang === 'zh' ? '· 拖拽卡片至右侧' : '· Drag to mixer'}</span>`;
|
||||
leftTitleWrap.innerHTML = `<strong>${window.anomalous_browser_lang === 'zh' ? '词卡库' : 'Prompt Library'}</strong> <span class="anomalous-sub-counter"></span>`;
|
||||
|
||||
// Button: Create New Custom Card (placed in header right, never wraps)
|
||||
const newCardTriggerBtn = text(leftHeader, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm');
|
||||
newCardTriggerBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:4px;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 Card'}`;
|
||||
newCardTriggerBtn.title = window.anomalous_browser_lang === 'zh' ? '新建并保存一张提示词卡片' : 'Create a new prompt card';
|
||||
|
||||
// Sub-action bar: Node extract and Material Library import (full-width, balanced)
|
||||
const leftSubActions = text(leftPanel, 'div', '', 'anomalous-workbench-sub-actions');
|
||||
// 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(leftSubActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm anomalous-btn-extract-node');
|
||||
extractNodeBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:4px;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' ? '从节点提取' : 'From Node'}`;
|
||||
extractNodeBtn.title = window.anomalous_browser_lang === 'zh' ? '读取 ComfyUI 画布当前选中节点的提示词文本并生成词卡' : 'Extract prompt text from selected canvas node into cards';
|
||||
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 2: One-click Sync / Import from Material Library
|
||||
const importMaterialsBtn = text(leftSubActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm');
|
||||
importMaterialsBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:4px;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 from 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';
|
||||
|
||||
// Search and category filters bar
|
||||
const leftFilterBar = text(leftPanel, 'div', '', 'anomalous-workbench-filter-bar');
|
||||
const leftSearch = text(leftFilterBar, 'input', '', 'anomalous-workbench-search-input');
|
||||
@@ -452,6 +453,11 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
|
||||
const rightActions = text(rightHeader, 'div', '', 'anomalous-mixer-actions');
|
||||
|
||||
// Right quick view final assembled text
|
||||
const viewFinalBtn = text(rightActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm anomalous-mixer-final-btn');
|
||||
viewFinalBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:4px;vertical-align:-1px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>${window.anomalous_browser_lang === 'zh' ? '最终文本' : 'Final Text'}`;
|
||||
viewFinalBtn.title = window.anomalous_browser_lang === 'zh' ? '查看/展开最终合成的提示词文本' : 'View / Expand final assembled prompt text';
|
||||
|
||||
// Right quick extract: suck into right mixer directly
|
||||
const rightSuckNodeBtn = text(rightActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm');
|
||||
rightSuckNodeBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:4px;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>${t('promptReadSelectedNode')}`;
|
||||
@@ -468,28 +474,83 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
// Blocks Container & Dropzone
|
||||
const blocksContainer = text(rightPanel, 'div', '', 'anomalous-mixer-blocks-container anomalous-assembly-track');
|
||||
|
||||
// Bottom Live Assembled Output Deck
|
||||
const outputDeck = text(rightPanel, 'div', '', 'anomalous-mixer-deck-output');
|
||||
const outputHeader = text(outputDeck, 'div', '', 'anomalous-mixer-output-header');
|
||||
const outputTitle = text(outputHeader, 'div', '', 'anomalous-mixer-output-title');
|
||||
const outputStats = text(outputHeader, 'div', '', 'anomalous-mixer-output-stats');
|
||||
// Bottom Live Assembled Output Deck (Collapsible on-demand)
|
||||
const outputDeck = text(rightPanel, 'div', '', 'anomalous-mixer-deck-output is-collapsed');
|
||||
|
||||
const outputBody = text(outputDeck, 'div', '', 'anomalous-mixer-output-body');
|
||||
// 1. Slim Dock Summary Bar (32px high, always visible when blocks exist)
|
||||
const outputSummary = text(outputDeck, 'div', '', 'anomalous-mixer-output-summary');
|
||||
const summaryLeft = text(outputSummary, 'div', '', 'anomalous-mixer-summary-left');
|
||||
const outputTitle = text(summaryLeft, 'div', '', 'anomalous-mixer-output-title');
|
||||
const outputStats = text(summaryLeft, 'div', '', 'anomalous-mixer-output-stats');
|
||||
|
||||
const summaryRight = text(outputSummary, 'div', '', 'anomalous-mixer-summary-right');
|
||||
const copyOutputBtn = text(summaryRight, 'button', `📋 ${t('copy')}`, 'anomalous-btn-ghost anomalous-btn-sm anomalous-btn-quick-copy');
|
||||
copyOutputBtn.title = window.anomalous_browser_lang === 'zh' ? '复制当前合成的提示词' : 'Copy assembled prompt text';
|
||||
|
||||
const expandOutputBtn = text(summaryRight, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm anomalous-btn-toggle-output');
|
||||
expandOutputBtn.title = window.anomalous_browser_lang === 'zh' ? '展开/收起最终文本' : 'Expand/collapse full text';
|
||||
|
||||
// 2. Expandable Drawer (reveals full textarea, canvas drag dock, and target node write bar)
|
||||
const outputDrawer = text(outputDeck, 'div', '', 'anomalous-mixer-output-drawer');
|
||||
outputDrawer.style.display = 'none';
|
||||
|
||||
const outputBody = text(outputDrawer, 'div', '', 'anomalous-mixer-output-body');
|
||||
const outputTextarea = text(outputBody, 'textarea', '', 'anomalous-mixer-output-textarea');
|
||||
outputTextarea.readOnly = true;
|
||||
outputTextarea.rows = 2;
|
||||
|
||||
const outputFooter = text(outputDeck, 'div', '', 'anomalous-mixer-output-footer');
|
||||
const outputFooter = text(outputDrawer, 'div', '', 'anomalous-mixer-output-footer');
|
||||
const dragHint = text(outputFooter, 'div', '', 'anomalous-mixer-drag-hint');
|
||||
dragHint.innerHTML = `🖐️ <strong>${window.anomalous_browser_lang === 'zh' ? '按住此预览坞' : 'Drag this deck'}</strong> ${window.anomalous_browser_lang === 'zh' ? '直接丢入 ComfyUI 画布节点' : 'onto canvas node'}`;
|
||||
|
||||
const outputActions = text(outputFooter, 'div', '', 'anomalous-mixer-output-actions');
|
||||
const copyOutputBtn = text(outputActions, 'button', `📋 ${t('copy')}`, 'anomalous-btn-ghost');
|
||||
|
||||
// Target Node Direct Write Bar
|
||||
const targetBar = text(rightPanel, 'div', '', 'anomalous-prompt-target-bar');
|
||||
// Target Node Direct Write Bar placed INSIDE outputDrawer so it collapses cleanly
|
||||
const targetBar = text(outputDrawer, 'div', '', 'anomalous-prompt-target-bar');
|
||||
const targetWidgetIndexByNodeId = {};
|
||||
|
||||
function updateOutputDeckState() {
|
||||
const currentRoleParts = draft?.plan?.parts ? draft.plan.parts.filter(p => (p.role || 'positive') === activeTab) : [];
|
||||
const compiledText = (draft?.plan?.[activeTab] || '').trim();
|
||||
const hasContent = currentRoleParts.length > 0 && compiledText.length > 0;
|
||||
|
||||
if (!hasContent) {
|
||||
outputDeck.style.display = 'none';
|
||||
viewFinalBtn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
outputDeck.style.display = 'flex';
|
||||
viewFinalBtn.style.display = 'inline-flex';
|
||||
|
||||
if (isOutputExpanded) {
|
||||
outputDeck.classList.remove('is-collapsed');
|
||||
outputDrawer.style.display = 'flex';
|
||||
expandOutputBtn.innerHTML = window.anomalous_browser_lang === 'zh' ? '收起 ▲' : 'Collapse ▲';
|
||||
viewFinalBtn.classList.add('is-active');
|
||||
} else {
|
||||
outputDeck.classList.add('is-collapsed');
|
||||
outputDrawer.style.display = 'none';
|
||||
expandOutputBtn.innerHTML = window.anomalous_browser_lang === 'zh' ? '查看最终文本 ▾' : 'View Text ▾';
|
||||
viewFinalBtn.classList.remove('is-active');
|
||||
}
|
||||
}
|
||||
|
||||
expandOutputBtn.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
isOutputExpanded = !isOutputExpanded;
|
||||
updateOutputDeckState();
|
||||
};
|
||||
|
||||
outputSummary.onclick = (e) => {
|
||||
if (e.target.closest('button')) return;
|
||||
isOutputExpanded = !isOutputExpanded;
|
||||
updateOutputDeckState();
|
||||
};
|
||||
|
||||
viewFinalBtn.onclick = () => {
|
||||
isOutputExpanded = !isOutputExpanded;
|
||||
updateOutputDeckState();
|
||||
};
|
||||
|
||||
// Bind output deck drag once on mount
|
||||
bindMaterialDrag(outputDeck, owner, {
|
||||
payload: () => {
|
||||
@@ -676,7 +737,7 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
if (materialSyncController === controller) {
|
||||
materialSyncController = null;
|
||||
importMaterialsBtn.disabled = false;
|
||||
importMaterialsBtn.innerHTML = `<svg style="width:12px;height:12px;margin-right:4px;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 from Library'}`;
|
||||
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'}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1275,15 +1336,16 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
const compiledText = draft.plan[activeTab] || '';
|
||||
|
||||
outputTitle.innerHTML = activeTab === 'negative'
|
||||
? `🚫 <strong>${window.anomalous_browser_lang === 'zh' ? '实时合成负向文本' : 'Assembled Negative'}</strong>`
|
||||
: `✨ <strong>${window.anomalous_browser_lang === 'zh' ? '实时合成正向文本' : 'Assembled Positive'}</strong>`;
|
||||
? `🚫 <strong>${window.anomalous_browser_lang === 'zh' ? '最终负向文本' : 'Assembled Negative'}</strong>`
|
||||
: `✨ <strong>${window.anomalous_browser_lang === 'zh' ? '最终正向文本' : 'Assembled Positive'}</strong>`;
|
||||
|
||||
const words = compiledText.trim() ? compiledText.split(/[\s,,\n\r]+/).filter(Boolean).length : 0;
|
||||
outputStats.textContent = window.anomalous_browser_lang === 'zh'
|
||||
? `${compiledText.length} 字符 · 约 ${words} 个词组`
|
||||
: `${compiledText.length} chars · ~${words} tags`;
|
||||
? `(${compiledText.length} 字符 · 约 ${words} 词组)`
|
||||
: `(${compiledText.length} chars · ~${words} tags)`;
|
||||
|
||||
outputTextarea.value = compiledText;
|
||||
updateOutputDeckState();
|
||||
}
|
||||
|
||||
// Smart Sort
|
||||
@@ -1317,8 +1379,9 @@ function buildPromptComposer(owner, container, options = {}) {
|
||||
};
|
||||
|
||||
// Copy preview text
|
||||
copyOutputBtn.onclick = async () => {
|
||||
const textToCopy = outputTextarea.value;
|
||||
copyOutputBtn.onclick = async (e) => {
|
||||
e?.stopPropagation?.();
|
||||
const textToCopy = (draft.plan[activeTab] || outputTextarea.value || '').trim();
|
||||
if (!textToCopy) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
|
||||
+85
-34
@@ -9522,8 +9522,8 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
.anomalous-prompt-composer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 16px 20px 24px;
|
||||
gap: 8px;
|
||||
padding: 8px 12px 10px;
|
||||
overflow-y: auto;
|
||||
max-height: 100%;
|
||||
color: #e2e8f0;
|
||||
@@ -9534,20 +9534,20 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
padding-bottom: 12px;
|
||||
gap: 8px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.anomalous-prompt-topbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.anomalous-prompt-topbar-left h3 {
|
||||
margin: 0;
|
||||
font-size: 1.05rem;
|
||||
font-size: 0.98rem;
|
||||
font-weight: 600;
|
||||
color: #f8fafc;
|
||||
letter-spacing: 0.02em;
|
||||
@@ -9556,16 +9556,16 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
.anomalous-prompt-topbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.anomalous-prompt-meta-strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 12px;
|
||||
margin: 8px 14px 0;
|
||||
gap: 8px;
|
||||
padding: 4px 8px;
|
||||
margin: 2px 10px 0;
|
||||
border-radius: var(--amb-radius-control, 6px);
|
||||
background: var(--amb-bg-panel, #1C1E24);
|
||||
border: 1px solid var(--amb-border, rgba(255, 255, 255, 0.08));
|
||||
@@ -9577,9 +9577,10 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
background: var(--amb-bg-input, #181a22);
|
||||
border: 1px solid var(--amb-border, rgba(255, 255, 255, 0.12));
|
||||
border-radius: var(--amb-radius-control, 6px);
|
||||
padding: 6px 12px;
|
||||
padding: 3px 8px;
|
||||
height: 28px;
|
||||
color: var(--amb-text-main, #f1f5f9);
|
||||
font-size: 0.82rem;
|
||||
font-size: 0.78rem;
|
||||
outline: none;
|
||||
transition: border-color 0.15s ease;
|
||||
box-sizing: border-box;
|
||||
@@ -10299,7 +10300,7 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
box-shadow: inset 0 0 0 1px rgba(220, 20, 60, 0.3);
|
||||
}
|
||||
|
||||
/* 4. Assembled Output Deck & Whole-Deck Canvas Drag (Sticky Floating Glass Deck) */
|
||||
/* 4. Assembled Output Deck & Whole-Deck Canvas Drag (Collapsible Glass Deck) */
|
||||
.anomalous-mixer-deck-output {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
@@ -10310,19 +10311,27 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--amb-border, rgba(255, 255, 255, 0.12));
|
||||
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.35);
|
||||
padding: 8px 10px;
|
||||
padding: 6px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
.anomalous-mixer-deck-output.is-collapsed {
|
||||
padding: 3px 8px;
|
||||
background: rgba(22, 24, 30, 0.92);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.anomalous-mixer-deck-output:hover {
|
||||
border-color: #38bdf8;
|
||||
box-shadow: 0 0 12px rgba(56, 189, 248, 0.25);
|
||||
box-shadow: 0 0 12px rgba(56, 189, 248, 0.2);
|
||||
}
|
||||
|
||||
.theme-abyssal-scarlet .anomalous-mixer-deck-output {
|
||||
@@ -10338,24 +10347,53 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.anomalous-mixer-output-header {
|
||||
.anomalous-mixer-output-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding-bottom: 4px;
|
||||
min-height: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.anomalous-mixer-summary-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.anomalous-mixer-summary-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.anomalous-mixer-output-title {
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.78rem;
|
||||
color: #e2e8f0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.anomalous-mixer-output-stats {
|
||||
font-size: 0.7rem;
|
||||
color: #94a3b8;
|
||||
font-family: monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.anomalous-mixer-output-drawer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding-top: 6px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.anomalous-mixer-output-body {
|
||||
@@ -10374,7 +10412,6 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
padding: 4px 8px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none; /* allows deck drag seamlessly */
|
||||
min-height: 34px;
|
||||
max-height: 52px;
|
||||
}
|
||||
@@ -10402,10 +10439,22 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
color: #dc143c;
|
||||
}
|
||||
|
||||
.anomalous-mixer-output-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
.anomalous-mixer-final-btn.is-active {
|
||||
background: var(--amb-bg-card-hover, rgba(255, 255, 255, 0.15)) !important;
|
||||
border-color: #38bdf8 !important;
|
||||
color: #38bdf8 !important;
|
||||
}
|
||||
|
||||
.anomalous-btn-toggle-output {
|
||||
font-size: 0.72rem !important;
|
||||
padding: 2px 7px !important;
|
||||
color: #38bdf8 !important;
|
||||
border-color: rgba(56, 189, 248, 0.25) !important;
|
||||
}
|
||||
|
||||
.anomalous-btn-quick-copy {
|
||||
font-size: 0.72rem !important;
|
||||
padding: 2px 7px !important;
|
||||
}
|
||||
|
||||
/* 5. Floating Toast Feedback */
|
||||
@@ -10466,10 +10515,10 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
.anomalous-prompt-workbench-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 320px) minmax(460px, 1fr);
|
||||
gap: 14px;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 10px 14px 14px;
|
||||
padding: 6px 10px 8px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -10599,9 +10648,9 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
background: var(--amb-bg-panel, #1C1E24);
|
||||
border: 1px solid var(--amb-border, rgba(255, 255, 255, 0.08));
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
padding: 14px 14px 12px;
|
||||
padding: 10px 10px 8px;
|
||||
box-sizing: border-box;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.anomalous-workbench-right-panel {
|
||||
@@ -10613,8 +10662,8 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding-bottom: 8px;
|
||||
gap: 6px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -11005,7 +11054,8 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
-------------------------------------------------------------------------- */
|
||||
.anomalous-assembly-track {
|
||||
flex: 1 1 0 !important;
|
||||
min-height: 120px;
|
||||
min-height: 200px;
|
||||
height: 100%;
|
||||
max-height: none !important;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@@ -11026,7 +11076,8 @@ dialog.anomalous-gallery-viewer::backdrop {
|
||||
|
||||
.anomalous-assembly-dropzone {
|
||||
flex: 1;
|
||||
min-height: 160px;
|
||||
min-height: 240px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user