diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e95d8ca..aae6666 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -178,6 +178,13 @@ DOM or live LiteGraph state. `recipe_actions.js` own pure or transactional recipe behavior. - `ui_doctor.js`, `model_picker.js`, and `graph_splice.js` own assistant and explicit graph-edit behavior. +- Visual Correction Audit & Gothic Vampire Re-alignment (视觉纠偏与哥特美学回归): + - Purged all 83 high-saturation cyberpunk cyan/blue instances (`#38bdf8`, `#00ffcc`, `#2dd4bf`, `#5eead4`, `#007bff`, `#0ea5e9`, `#06b6d4`) and 140+ `rgba(...)` glow values, re-skinning to an aristocratic Crimson (`#dc143c`), Deep Wine (`#722f37`), and Abyssal Violet (`#8b5cf6`) palette; + - Restrained all oversized diffused glows (`box-shadow` / `filter: drop-shadow`) to tight 4~8px interior magic micro-auras, adhering to vampire gothic nobility; + - Swept legacy system emojis across all 13 frontend modules and `locales.js`, replacing them with pure-code vector SVG icons (`3D Cube`, `Network`, `Magic Wand`, `Folder`, `Image Frame`, `Sliders`, `Trash`, etc.); + - Overhauled `#anomalous-header` into a centered symmetric three-column architecture (`leftGroup`, `centerGroup`, `rightGroup`), eliminating left-side visual voids/black holes; + - Injected gothic diamond lattice textures and dark crimson radial auras into `#anomalous-gallery-panel`, `.anomalous-gallery-grid`, `.anomalous-gallery-container`, and `#anomalous-content`; + - Fixed tooltip overlap hazard by re-aligning bottom toolbar tooltip triggers to center-top (`data-tooltip-pos="top"`), completely preventing horizontal button occlusion. - `locales.js` is the canonical runtime string catalog; `safe_dom.js` is the trusted rich-text boundary. - `web/main.js` and `web/modules/ui_sidebar.js` own theme state management and interactive easter-egg domain toggles, diff --git a/web/main.js b/web/main.js index de4cf47..74e1d60 100644 --- a/web/main.js +++ b/web/main.js @@ -705,7 +705,7 @@ function applyFloatingTriggerPresentation() { const icon = document.createElement('span'); icon.className = 'anomalous-trigger-icon'; icon.setAttribute('aria-hidden', 'true'); - icon.textContent = '📦'; + icon.innerHTML = ''; const label = document.createElement('span'); label.className = 'anomalous-trigger-label'; diff --git a/web/modules/locales.js b/web/modules/locales.js index 16ff9b0..358a404 100644 --- a/web/modules/locales.js +++ b/web/modules/locales.js @@ -102,7 +102,7 @@ export const i18n = { recipeViewGrid: "网格视图", recipeViewList: "列表视图", recipeDragHint: "按住拖拽至画布打开新工作流", - recipeDragTargetCanvas: "🚀 释放以在画布打开新工作流", + recipeDragTargetCanvas: "释放以在画布打开新工作流", recipeStatusReady: "全部就绪", recipeStatusUnverified: "待校验", recipeStatusMissing: "模型缺失", @@ -164,12 +164,12 @@ export const i18n = { materialChars: "{count} 字符", materialCopyPromptText: "复制提示词", notebookSaveError: '保存失败,修改仍留在编辑器中,请重试。', - title: '📦 Anomalous 模型浏览器', + title: 'Anomalous 模型浏览器', scan: '🔄 扫描目录并同步 C 站数据', scanTitle: '扫描目录', scanConfirm: '注意:\n扫描过程将会自动比对 Civitai 并重命名本地模型,同时清理损坏文件。\n确定要开始扫描吗?', scanning: '扫描中...', - scanBg: '🚀 扫描后台启动!', + scanBg: '扫描后台启动!', scanDone: '完成', scanCompleteMsg: '✅ 扫描完成!\n\n⚠️ 提示:由于部分模型已被重命名或去重,请点击 ComfyUI 的 [Refresh] 按钮以同步最新的模型列表。', eco: '🔋 视频悬浮播放 (节能模式)', @@ -190,7 +190,7 @@ export const i18n = { noPreview: '暂无预览图', clickScan: '点击扫描', back: '⬅️ 返回', - delModel: '🗑️ 删除', + delModel: '删除', delConfirm: '确定彻底删除', delConfirm2: '及其所有关联配置、预览图和缓存文件吗?此操作不可逆!', delSuccess: '✅ 删除成功!\n', @@ -204,7 +204,7 @@ export const i18n = { loading: '加载中…', loadingCompatible: '加载中...', backToPrev: '🔙 返回上一层', - applyToCanvas: '➕ 插入节点', + applyToCanvas: '插入节点', applySuccess: '✅ 已添加至工作流', help: '帮助', helpTitle: '📖 使用手册', @@ -215,12 +215,12 @@ export const i18n = {

1. 🔄 前置准备 (扫描向导)
具体位置:界面左侧底部的 扫描向导 (🔄)。
这是最重要的前置操作!请先打开扫描向导执行扫描。切记保持网络畅通。部分 C站限制级模型信息需要在设置中填入 API-KEY。扫描完成后,插件的各项功能基本激活。

2. 🩺 拯救爆红 (模型医生)
具体位置:左侧底部第二个 听诊器 (🩺) 按钮。
当导入别人使用该插件输出的工作流或者图片时,若模型路径爆红,点击模型医生就能智能识别当前所有节点实现正确路径的替换。

3. 🤖 选中交互与参数预设 (节点助手)
具体位置:左侧第三个 机器人 (🤖) 按钮。
在画布上选中节点后,“动作”页可以可视化替换模型,或在兼容的 MODEL + CLIP 链前后插入 LoRA。“参数方案”页会按配方分组读取工作流配方保存的同类型节点参数;点击一条记录即可把参数应用到当前选中节点。例如选中 KSampler 后,可一键载入配方中的采样器、调度器、步数、CFG 与降噪等值;种子等每次运行变化的参数会被忽略。

-

4. 📦 工作流配方、参数方案与素材库
具体位置:打开 创作工作台。
配方会保存工作流、封面、标签、备注、模型引用及常用参数。未闭合的局部配方默认追加到当前画布,可独立运行的整体配方会在新画布打开;两者都支持导入/导出配方包。详情页包含概览、参数方案、图库和版本:可匹配本地模型、查看与复制完整参数、比较历史出图参数、比较或恢复配方版本。主图库或配方图库的图片悬浮后可进入参数详情,把原图、完整工作流和节点参数保存为一个素材快照;素材库可打开完整工作流,节点助手则按当前节点类型筛选并应用其中的参数。参数方案可从当前画布读取同一骨架的新参数,也可编辑安全控件并保存;“应用到当前工作流”只会在骨架匹配后写入参数。

-

5. ⚙️ 个性化配置 (设置面板)
具体位置:左侧底部的 齿轮 (⚙️) 按钮。
可以调节语言、主页面字体大小;进入“模型设置”可选择视频封面始终播放或悬停播放,以及卡片使用流畅缩略图或原始封面。详情页始终保留原图画质。

-

6. 📑 顶部导航与提示词笔记
具体位置:右侧顶部的按钮分别为 模型 (📦)、图库 (🖼️)、创作工作台 (📑)、停靠侧边栏 (◧)。
进入创作工作台的“提示词笔记”,点击新建 ➕。选择基础模型、主模型和 LoRA 后,可通过 📝纯文本/粘贴 填入提示词并进行双语对照、分块、查找替换和标签编辑,最后点击 🚀 发送到画布 一键组装节点。

-

7. 📁 文件夹管理 (Folder Manager)
具体位置:设置 (⚙️) 面板内的 📁 文件夹管理。
可以用来隐藏不需要的内置文件夹。隐藏后的文件夹不仅从侧边栏消失,后端的静默扫描和本地读取也会完全跳过,实现真正的“零性能损耗”。并且可以在这里拖拽调整文件夹在侧边栏显示的顺序。

-

8. 📥 工作流导出与导入 (分享码)
具体位置:设置 (⚙️) 按钮左侧的 导入/导出 (📥) 按钮。
该功能可以将当前的工作流一键编码导出为一段专属分享码,方便发到社交平台。别人只需复制这串分享码,在此界面粘贴即可直接还原导入完整的工作流,并且在导入前系统会自动校验模型是否在本地存在。

-

9. 💬 提交反馈 / 报告问题
具体位置:设置 (⚙️) 面板内的 💬 提交反馈 / 报告问题。
当你遇到插件报错或有新功能建议时,点击该按钮可一键直达 GitHub Issues 页面提交反馈。

+

4. 工作流配方、参数方案与素材库
具体位置:打开 创作工作台。
配方会保存工作流、封面、标签、备注、模型引用及常用参数。未闭合的局部配方默认追加到当前画布,可独立运行的整体配方会在新画布打开;两者都支持导入/导出配方包。详情页包含概览、参数方案、图库和版本:可匹配本地模型、查看与复制完整参数、比较历史出图参数、比较或恢复配方版本。主图库或配方图库的图片悬浮后可进入参数详情,把原图、完整工作流和节点参数保存为一个素材快照;素材库可打开完整工作流,节点助手则按当前节点类型筛选并应用其中的参数。参数方案可从当前画布读取同一骨架的新参数,也可编辑安全控件并保存;“应用到当前工作流”只会在骨架匹配后写入参数。

+

5. 个性化配置 (设置面板)
具体位置:左侧底部的 齿轮 () 按钮。
可以调节语言、主页面字体大小;进入“模型设置”可选择视频封面始终播放或悬停播放,以及卡片使用流畅缩略图或原始封面。详情页始终保留原图画质。

+

6. 📑 顶部导航与提示词笔记
具体位置:右侧顶部的按钮分别为 模型 ()、图库 ()、创作工作台 (📑)、停靠侧边栏 (◧)。
进入创作工作台的“提示词笔记”,点击新建 。选择基础模型、主模型和 LoRA 后,可通过 📝纯文本/粘贴 填入提示词并进行双语对照、分块、查找替换和标签编辑,最后点击 发送到画布 一键组装节点。

+

7. 文件夹管理 (Folder Manager)
具体位置:设置 () 面板内的 文件夹管理。
可以用来隐藏不需要的内置文件夹。隐藏后的文件夹不仅从侧边栏消失,后端的静默扫描和本地读取也会完全跳过,实现真正的“零性能损耗”。并且可以在这里拖拽调整文件夹在侧边栏显示的顺序。

+

8. 工作流导出与导入 (分享码)
具体位置:设置 () 按钮左侧的 导入/导出 () 按钮。
该功能可以将当前的工作流一键编码导出为一段专属分享码,方便发到社交平台。别人只需复制这串分享码,在此界面粘贴即可直接还原导入完整的工作流,并且在导入前系统会自动校验模型是否在本地存在。

+

9. 💬 提交反馈 / 报告问题
具体位置:设置 () 面板内的 💬 提交反馈 / 报告问题。
当你遇到插件报错或有新功能建议时,点击该按钮可一键直达 GitHub Issues 页面提交反馈。

`, notebooks: '笔记本', notebookTitle: '笔记本管理', @@ -252,7 +252,7 @@ export const i18n = { materialImageDetail: '图片参数详情', workbenchFullscreen: '全屏工作台', workbenchWindowed: '窗口模式', - workbenchLoadToCanvas: '⚡ 加载到画布', + workbenchLoadToCanvas: '加载到画布', workbenchLoadSuccess: '工作流已成功加载到 ComfyUI 画布!', workbenchLoadFailed: '无法加载工作流到画布:', workbenchNext: '下一张 (→)', @@ -293,11 +293,11 @@ export const i18n = { materialDenoise: '降噪', materialScheduler: '调度', materialReferencedModels: '引用的模型清单 ({count})', - materialModelBase: '🎯 主模型 / UNet', + materialModelBase: '主模型 / UNet', materialModelLora: '🎨 LoRA 微调层', - materialModelVae: '🖼️ VAE 编码器', + materialModelVae: 'VAE 编码器', materialModelClip: '👁️ 文本编码器 (CLIP)', - materialModelOther: '⚡ 其它模型组件', + materialModelOther: '其它模型组件', materialLoraStrength: '权重', materialModelCountLora: '{count} LoRA', materialModelCountClip: '{count} CLIP', @@ -369,11 +369,11 @@ export const i18n = { materialSourceRecipeMissing: '来源已中断 · 配方已删除:{name}', createNotebook: '新建', saveNotebook: '保存笔记', - deleteNotebook: '🗑️ 删除', - renameNotebook: '✏️ 重命名', + deleteNotebook: '删除', + renameNotebook: '重命名', baseModel: 'Base Model (基础模型)', mainModel: 'Main Model (主模型)', - sendToCanvas: '🚀 发送到画布', + sendToCanvas: '发送到画布', translate: '翻译', newNotebookName: '新笔记本名称', noBaseModel: '请先选择 Base Model', @@ -408,10 +408,10 @@ export const i18n = { detailBackDoctor: '⬅ 返回医生面板', detailBackAssistant: '⬅ 返回助手面板', detailJumpToBottom: '跳到底部', - detailEdit: '⚙️ 编辑', + detailEdit: '编辑', detailNotesTitle: '📓 专属备注 (Notes)', detailEditNotes: '编辑备注', - detailGeneratedGallery: '🖼️ 查看历史生成作品', + detailGeneratedGallery: '查看历史生成作品', detailTrainedWords: '训练词:', detailDescription: '描述:', detailNotes: '备注:', @@ -525,7 +525,7 @@ export const i18n = { assistantPreviewFailed: '无法加载预览', betaFeature: '测试功能', assistantBetaNotice: '“参数方案”联动仍在测试。应用前请保存当前工作流;它会修改选中节点的参数,但会忽略种子等运行时变化值。“动作”页不属于测试范围。', - assistantHistoryTitle: '🖼️ 历史生成图 ({count})', + assistantHistoryTitle: '历史生成图 ({count})', assistantViewAll: '查看全部 →', assistantRestoreWorkflow: '点击恢复此工作流', assistantViewGallery: '点击查看完整图库', @@ -534,7 +534,7 @@ export const i18n = { pickerInsertBeforeTitle: '⬅ 在节点前方插入 LoRA', pickerInsertAfterTitle: '在节点后方插入 LoRA ➡', pickerChangeTitle: '🔀 更换当前模型', - pickerFolders: '📁 文件夹', + pickerFolders: '文件夹', pickerSearch: '🔍 搜索名称或完整路径…', pickerMainAll: '主模型:全部类型', pickerNameAsc: '名称 A–Z', @@ -569,11 +569,11 @@ export const i18n = { pickerNoMatches: '没有符合条件的模型。', pickerOperationFailed: '操作失败:', notebookSelectMain: '请先选择一个主模型。', - mainPreflightTitle: '📥 导入工作流 (预检海关)', + mainPreflightTitle: '导入工作流 (预检海关)', mainPreflightDesc: '请在此处粘贴工作流的 JSON 代码(未来将支持分享码)。我们将为您提取并校验所有模型依赖。', mainCancel: '取消', mainAnalyzePreflight: '🔍 解析并预检', - mainForceLoad: '🚀 强制载入画布', + mainForceLoad: '强制载入画布', mainNoDependencies: '✅ 未检测到任何模型依赖。', mainDetectedModels: '扫描到 {count} 个模型资源:', mainDownloadCivitai: '去 C站下载', @@ -615,14 +615,14 @@ export const i18n = { mainResetFloatingTriggerPosition: '重置悬浮入口位置', mainRetryInit: '插件界面初始化失败,点击可重试', mainRetryInitEn: 'The plugin UI failed to initialize. Click to retry.', - mainExportTitle: '📦 导出工作流分享码', + mainExportTitle: '导出工作流分享码', mainSkeletonOption: '精简版 (推荐 B站评论区, 体积小, 丢失坐标)', mainFullOption: '完整版 (保留所有坐标与颜色, 较长)', mainGenerate: '生成分享码', mainCopyClipboard: '复制到剪贴板', mainClose: '关闭', mainCopied: '✅ 复制成功!', - mainImportTitle: '📥 导入工作流分享码', + mainImportTitle: '导入工作流分享码', mainSharePlaceholder: '粘贴 AMB0- 或 AMB1- 开头的分享码...', mainImportLoad: '导入并加载', mainShareEmpty: '❌ 分享码为空', @@ -630,17 +630,17 @@ export const i18n = { mainDecodeFailed: '❌ 解析失败: ', mainUnifiedTitle: '🔄 工作流分享与导入', mainExportWorkflow: '📤 导出当前工作流为分享码', - mainImportWorkflow: '📥 从分享码导入工作流', + mainImportWorkflow: '从分享码导入工作流', sidebarToggle: '收起/展开侧边栏', sidebarDoctor: '模型医生', sidebarAssistant: '节点助手', - sidebarPreflight: '📥 预检导入工作流', + sidebarPreflight: '预检导入工作流', sidebarSettings: '设置中心', sidebarScanWizard: '📡 模型扫描向导', sidebarScanComplete: '✅ 扫描/重命名已完成!数据已为您更新。', sidebarScanWizardTitle: '扫描向导', sidebarGlobalScanWizard: '全局扫描向导', - sidebarSingleModelScan: '🎯 单模型扫描向导: {files}', + sidebarSingleModelScan: '单模型扫描向导: {files}', sidebarGlobalWizardTitle: '全局扫描向导', sidebarApiKey: 'API 密钥', sidebarApiKeyPrompt: '请输入 Civitai API Key(留空将清除):', @@ -680,12 +680,12 @@ export const i18n = { sidebarExecute: '执行扫描', sidebarSwitchToEnglish: '🌐 Language: EN', sidebarSwitchToChinese: '🌐 语言: 中文', - sidebarImportExport: '📥 导入 / 导出工作流 (分享码)', + sidebarImportExport: '导入 / 导出工作流 (分享码)', sidebarGlobalQuickScan: '🌍 一键全盘极速扫描 (不下载封面/不改名)', sidebarGlobalQuickScanShort: '🌍 一键全盘扫描 (不下载封面/不改名)', sidebarGlobalQuickConfirm: '即将执行极速全盘扫描并同步Hash,此操作不改名也不下载封面,是否继续?', sidebarScanning: '⏳ 扫描中...', - sidebarGlobalStarted: '🚀 全局扫描已后台启动!', + sidebarGlobalStarted: '全局扫描已后台启动!', sidebarScanDone: '✅ 扫描完成', scanProgressTitle: '模型扫描进度', scanProgressPreparing: '正在准备扫描目录…', @@ -712,9 +712,9 @@ export const i18n = { sidebarResetConfirm: '确认重置窗口位置、缩放和停靠状态吗?', sidebarInjectHash: '🟢 注入工作流哈希', sidebarSkipHash: '⚪ 不注入工作流哈希', - sidebarManageFolders: '📁 文件夹管理', + sidebarManageFolders: '文件夹管理', sidebarFeedback: '💬 提交反馈 / 报告问题', - sidebarModelSettings: '🖼️ 模型设置', + sidebarModelSettings: '模型设置', sidebarModelCardSettings: '模型卡片设置', sidebarModelCardDescription: '只调整模型浏览器里的卡片显示,不会修改模型文件或原始封面。', sidebarVideoPlayback: '视频封面播放', @@ -731,7 +731,7 @@ export const i18n = { sidebarExpandAll: '展开全部', sidebarCollapseAll: '收起全部', sidebarSearchModels: '搜索模型...', - sidebarFolderManagerTitle: '📁 侧边栏文件夹管理', + sidebarFolderManagerTitle: '侧边栏文件夹管理', sidebarFolderManagerDesc: '上下拖拽调整侧边栏的显示顺序。点击眼睛图标可隐藏不需要显示的底层文件夹。隐藏的文件夹不会被扫描,完全不占用性能。', sidebarCategoryMode: '分类模式 (ComfyUI)', sidebarPhysicalMode: '物理模式 (本地文件夹)', @@ -756,7 +756,7 @@ export const i18n = { settingsBtn: '设置', closeSettings: '✖ 关闭设置面板', doctorBtn: '节点助手', - importBtn: '📥 导入 / 导出工作流 (分享码)', + importBtn: '导入 / 导出工作流 (分享码)', recipeTitle: '工作流配方', recipeBetaNotice: '工作流配方仍在测试。更新插件以及导入、恢复、删除或应用参数前,请保存当前画布并备份用户 workflows 目录中的 anomalous_recipes 与 anomalous_parameters。', recipeClose: '关闭工作流配方', @@ -938,7 +938,7 @@ export const i18n = { recipeDetailSampler: '采样器', recipeDetailResolution: '分辨率', recipeDetailUnavailable: '不可用', - recipeDetailEditOrigin: '✏️ 编辑来源', + recipeDetailEditOrigin: '编辑来源', recipeOriginDialogTitle: '编辑模型来源信息', recipeOriginOfficialName: '官方模型名称', recipeOriginModelUrl: '官方网址 (Model URL)', @@ -1046,7 +1046,7 @@ export const i18n = { recipeStatusMissing: '🔴 缺少模型 ({count})', recipeStatusChecking: '⏳ 检查环境中…', recipeQuickSpecs: '核心规格', - recipeApplyAllMatches: '⚡ 一键应用全部匹配', + recipeApplyAllMatches: '一键应用全部匹配', recipeApplyingAllMatches: '正在应用全部匹配…', recipeApplyAllMatchesSuccess: '✅ 已成功应用全部匹配!', recipeRawNodesToggle: '节点参数', @@ -1162,7 +1162,7 @@ export const i18n = { recipeViewGrid: "Grid View", recipeViewList: "List View", recipeDragHint: "Hold & drag onto canvas to load workflow", - recipeDragTargetCanvas: "🚀 Drop to load workflow in canvas", + recipeDragTargetCanvas: "Drop to load workflow in canvas", recipeStatusReady: "Ready", recipeStatusUnverified: "Unverified", recipeStatusMissing: "Missing", @@ -1224,12 +1224,12 @@ export const i18n = { materialChars: "{count} characters", materialCopyPromptText: "Copy prompt", notebookSaveError: 'Save failed. Your edits remain in the editor; please retry.', - title: '📦 Anomalous Model Browser', + title: 'Anomalous Model Browser', scan: '🔄 Scan folder and sync Civitai data', scanTitle: 'Scan Folder', scanConfirm: 'Notice:\nThe scan process will automatically compare with Civitai and rename your local files.\nStart scan?', scanning: 'Scanning...', - scanBg: '🚀 Scan started in background!', + scanBg: 'Scan started in background!', scanDone: 'Done', scanCompleteMsg: '✅ Scan Complete!\n\n⚠️ Note: Please click [Refresh] to sync the model list since files were renamed.', eco: '🔋 Eco Mode (Play video on hover)', @@ -1250,7 +1250,7 @@ export const i18n = { noPreview: 'No preview available', clickScan: 'Click Scan', back: '⬅️ Back', - delModel: '🗑️ Delete', + delModel: 'Delete', delConfirm: 'Are you sure you want to permanently delete', delConfirm2: ' and all its associated configs, previews, and cache files? This action is irreversible!', delSuccess: '✅ Delete Complete!\n', @@ -1264,7 +1264,7 @@ export const i18n = { loading: 'Loading…', loadingCompatible: 'Loading...', backToPrev: '🔙 Back to prev', - applyToCanvas: '➕ Add Node', + applyToCanvas: 'Add Node', applySuccess: '✅ Added to workflow', help: 'Help', helpTitle: '📖 User Manual', @@ -1275,12 +1275,12 @@ export const i18n = {

1. 🔄 Prerequisites (Scan Wizard)
Location: The Scan (🔄) icon at the bottom left.
This is the most crucial step! Open the Scan Wizard and execute a scan. Keep your network connected. (API-KEY may be needed in Settings for NSFW Civitai models). Scanning activates all core features.

2. 🩺 Fix Broken Nodes (Model Doctor)
Location: The 2nd icon (Stethoscope 🩺) from the bottom left.
When importing a workflow/image exported by this plugin and nodes turn red, open the Model Doctor to intelligently identify and replace them with correct local paths.

3. 🤖 Node actions and parameter presets (Node Assistant)
Location: The 3rd icon (Robot 🤖) from the bottom left.
After selecting a canvas node, Actions can visually replace its model or insert a LoRA before/after a compatible MODEL + CLIP chain. Parameter Presets reads same-type node values saved by Workflow Recipes and groups them by recipe. Click one record to apply it to the selected node. For example, selecting a KSampler can load its sampler, scheduler, steps, CFG, and denoise values in one click; volatile values such as seeds are ignored.

-

4. 📦 Workflow Recipes and Parameter Sets
Location: Open the Creative Workspace and switch to Workflow Recipes.
A recipe saves the workflow, cover, tags, notes, model references, and common parameters. Open partial recipes append to the current canvas, while independently runnable complete recipes open in a new canvas; both can be shared as import/export packages. Details include Overview, Parameter Set, Gallery, and Versions for local model matching, full parameter viewing/copying, output comparison, and version comparison/restoration. Parameter notes can capture fresh values from a matching live canvas or edit safe controls; Apply to Current Workflow writes only after the recipe skeleton matches. Third-party prompt nodes that cannot be classified reliably can be labelled manually.

-

5. ⚙️ Settings Panel
Location: The Gear (⚙️) icon at the bottom left.
Adjust language and UI scale. Open “Model Settings” to choose always-play or hover-play video covers and optimized thumbnails or original covers. Detail pages always retain original image quality.

-

6. 📑 Top Navigation & Prompt Notes
Location: Top-right tabs Models (📦), Gallery (🖼️), Creative Workspace (📑), and Dock (◧).
Open Prompt Notes in the Creative Workspace and click ➕. Select a Base Model, Main Model, and LoRAs; use 📝 Edit Raw/Paste for bilingual prompts, chunking, find/replace, and tag editing; then click 🚀 Send to Canvas to assemble the nodes.

-

7. 📁 Folder Manager
Location: Inside the Settings (⚙️) panel, click 📁 Manage Folders.
Use this to hide unneeded built-in folders. Hidden folders will be completely skipped by background scans and I/O reads, achieving true "zero performance cost". You can also drag and drop to reorder how folders appear in the sidebar.

-

8. 📥 Workflow Export & Import (Share Code)
Location: The Import/Export (📥) icon to the left of the Settings gear.
You can encode your current workflow into a single share code for easy sharing on social platforms. Others can simply copy this code and paste it here to instantly import the workflow. The system will also automatically pre-check if the required models exist locally.

-

9. 💬 Submit Feedback / Report Bug
Location: Inside the Settings (⚙️) panel, click 💬 Submit Feedback / Report Bug.
If you encounter bugs or have feature requests, click this to go directly to our GitHub Issues page.

+

4. Workflow Recipes and Parameter Sets
Location: Open the Creative Workspace and switch to Workflow Recipes.
A recipe saves the workflow, cover, tags, notes, model references, and common parameters. Open partial recipes append to the current canvas, while independently runnable complete recipes open in a new canvas; both can be shared as import/export packages. Details include Overview, Parameter Set, Gallery, and Versions for local model matching, full parameter viewing/copying, output comparison, and version comparison/restoration. Parameter notes can capture fresh values from a matching live canvas or edit safe controls; Apply to Current Workflow writes only after the recipe skeleton matches. Third-party prompt nodes that cannot be classified reliably can be labelled manually.

+

5. Settings Panel
Location: The Gear () icon at the bottom left.
Adjust language and UI scale. Open “Model Settings” to choose always-play or hover-play video covers and optimized thumbnails or original covers. Detail pages always retain original image quality.

+

6. 📑 Top Navigation & Prompt Notes
Location: Top-right tabs Models (), Gallery (), Creative Workspace (📑), and Dock (◧).
Open Prompt Notes in the Creative Workspace and click . Select a Base Model, Main Model, and LoRAs; use 📝 Edit Raw/Paste for bilingual prompts, chunking, find/replace, and tag editing; then click Send to Canvas to assemble the nodes.

+

7. Folder Manager
Location: Inside the Settings () panel, click Manage Folders.
Use this to hide unneeded built-in folders. Hidden folders will be completely skipped by background scans and I/O reads, achieving true "zero performance cost". You can also drag and drop to reorder how folders appear in the sidebar.

+

8. Workflow Export & Import (Share Code)
Location: The Import/Export () icon to the left of the Settings gear.
You can encode your current workflow into a single share code for easy sharing on social platforms. Others can simply copy this code and paste it here to instantly import the workflow. The system will also automatically pre-check if the required models exist locally.

+

9. 💬 Submit Feedback / Report Bug
Location: Inside the Settings () panel, click 💬 Submit Feedback / Report Bug.
If you encounter bugs or have feature requests, click this to go directly to our GitHub Issues page.

`, notebooks: 'Notebooks', notebookTitle: 'Notebook Manager', @@ -1312,7 +1312,7 @@ export const i18n = { materialImageDetail: 'Image Parameter Details', workbenchFullscreen: 'Fullscreen Workbench', workbenchWindowed: 'Windowed Mode', - workbenchLoadToCanvas: '⚡ Load to Canvas', + workbenchLoadToCanvas: 'Load to Canvas', workbenchLoadSuccess: 'Workflow loaded to ComfyUI canvas successfully!', workbenchLoadFailed: 'Could not load workflow into canvas: ', workbenchNext: 'Next (→)', @@ -1353,11 +1353,11 @@ export const i18n = { materialDenoise: 'Denoise', materialScheduler: 'Scheduler', materialReferencedModels: 'Referenced Models ({count})', - materialModelBase: '🎯 Base Model / UNet', + materialModelBase: 'Base Model / UNet', materialModelLora: '🎨 LoRA Layers', - materialModelVae: '🖼️ VAE', + materialModelVae: 'VAE', materialModelClip: '👁️ Text Encoder (CLIP)', - materialModelOther: '⚡ Other Models', + materialModelOther: 'Other Models', materialLoraStrength: 'Weight', materialModelCountLora: '{count} LoRA', materialModelCountClip: '{count} CLIP', @@ -1429,11 +1429,11 @@ export const i18n = { materialSourceRecipeMissing: 'Source broken · recipe deleted: {name}', createNotebook: 'New', saveNotebook: '💾 Save prompt note', - deleteNotebook: '🗑️ Delete', - renameNotebook: '✏️ Rename', + deleteNotebook: 'Delete', + renameNotebook: 'Rename', baseModel: 'Base Model', mainModel: 'Main Model', - sendToCanvas: '🚀 Send to Canvas', + sendToCanvas: 'Send to Canvas', translate: 'Translate', newNotebookName: 'New Notebook Name', noBaseModel: 'Select a Base Model first', @@ -1468,10 +1468,10 @@ export const i18n = { detailBackDoctor: '⬅ Back to Doctor', detailBackAssistant: '⬅ Back to Assistant', detailJumpToBottom: 'Jump to bottom', - detailEdit: '⚙️ Edit', + detailEdit: 'Edit', detailNotesTitle: '📓 Custom Notes', detailEditNotes: 'Edit Notes', - detailGeneratedGallery: '🖼️ View Generated Gallery', + detailGeneratedGallery: 'View Generated Gallery', detailTrainedWords: 'Trained Words:', detailDescription: 'Description:', detailNotes: 'Notes:', @@ -1585,7 +1585,7 @@ export const i18n = { assistantPreviewFailed: 'Failed to load preview', betaFeature: 'Beta feature', assistantBetaNotice: 'The Parameter Set integration is still in beta. Save the current workflow before applying a preset; it changes the selected node while ignoring volatile values such as seeds. The Actions tab is not part of this beta.', - assistantHistoryTitle: '🖼️ History ({count})', + assistantHistoryTitle: 'History ({count})', assistantViewAll: 'View All →', assistantRestoreWorkflow: 'Click to restore workflow', assistantViewGallery: 'Click to view full gallery', @@ -1594,7 +1594,7 @@ export const i18n = { pickerInsertBeforeTitle: '⬅ Insert LoRA Before Node', pickerInsertAfterTitle: 'Insert LoRA After Node ➡', pickerChangeTitle: '🔀 Change Current Model', - pickerFolders: '📁 Folders', + pickerFolders: 'Folders', pickerSearch: '🔍 Search name or full path…', pickerMainAll: 'Main model: All types', pickerNameAsc: 'Name A–Z', @@ -1629,11 +1629,11 @@ export const i18n = { pickerNoMatches: 'No models match the current filters.', pickerOperationFailed: 'Operation failed: ', notebookSelectMain: 'Please select a Main Model first.', - mainPreflightTitle: '📥 Pre-flight Import Workflow', + mainPreflightTitle: 'Pre-flight Import Workflow', mainPreflightDesc: 'Paste the workflow JSON here. We will extract and validate all model dependencies before loading.', mainCancel: 'Cancel', mainAnalyzePreflight: '🔍 Analyze & Pre-flight', - mainForceLoad: '🚀 Force Load to Canvas', + mainForceLoad: 'Force Load to Canvas', mainNoDependencies: '✅ No model dependencies detected.', mainDetectedModels: 'Detected {count} model resources:', mainDownloadCivitai: 'Download', @@ -1675,14 +1675,14 @@ export const i18n = { mainResetFloatingTriggerPosition: 'Reset floating entry position', mainRetryInit: '插件界面初始化失败,点击可重试', mainRetryInitEn: 'The plugin UI failed to initialize. Click to retry.', - mainExportTitle: '📦 Export Workflow Share Code', + mainExportTitle: 'Export Workflow Share Code', mainSkeletonOption: 'Skeleton (Compact, best for comments, loses coords)', mainFullOption: 'Full (Keeps coords and colors, longer string)', mainGenerate: 'Generate', mainCopyClipboard: 'Copy', mainClose: 'Close', mainCopied: '✅ Copied successfully!', - mainImportTitle: '📥 Import Workflow Share Code', + mainImportTitle: 'Import Workflow Share Code', mainSharePlaceholder: 'Paste AMB0- or AMB1- share code here...', mainImportLoad: 'Import & Load', mainShareEmpty: '❌ Share code is empty', @@ -1690,17 +1690,17 @@ export const i18n = { mainDecodeFailed: '❌ Decode Failed: ', mainUnifiedTitle: '🔄 Workflow Share & Import', mainExportWorkflow: '📤 Export Workflow to Share Code', - mainImportWorkflow: '📥 Import Workflow from Share Code', + mainImportWorkflow: 'Import Workflow from Share Code', sidebarToggle: 'Toggle Sidebar', sidebarDoctor: 'Model Doctor', sidebarAssistant: 'Node Assistant', - sidebarPreflight: '📥 Preflight Import', + sidebarPreflight: 'Preflight Import', sidebarSettings: 'Settings Hub', sidebarScanWizard: '📡 Model Scan Wizard', sidebarScanComplete: '✅ Scan/Rename completed! Data updated.', sidebarScanWizardTitle: 'Scan Wizard', sidebarGlobalScanWizard: 'Global Scan Wizard', - sidebarSingleModelScan: '🎯 Single Model Scan: {files}', + sidebarSingleModelScan: 'Single Model Scan: {files}', sidebarGlobalWizardTitle: 'Global Scan Wizard', sidebarApiKey: 'API Key', sidebarApiKeyPrompt: 'Enter Civitai API Key (leave blank to clear):', @@ -1740,12 +1740,12 @@ export const i18n = { sidebarExecute: 'Execute Scan', sidebarSwitchToEnglish: '🌐 Language: EN', sidebarSwitchToChinese: '🌐 语言: 中文', - sidebarImportExport: '📥 Import / Export Workflow (Share Code)', + sidebarImportExport: 'Import / Export Workflow (Share Code)', sidebarGlobalQuickScan: '🌍 Global Quick Scan (No rename/No media)', sidebarGlobalQuickScanShort: '🌍 Global Quick Scan (No rename/No media)', sidebarGlobalQuickConfirm: 'Start global quick scan to sync all hashes?', sidebarScanning: '⏳ Scanning...', - sidebarGlobalStarted: '🚀 Global Scan started in background!', + sidebarGlobalStarted: 'Global Scan started in background!', sidebarScanDone: '✅ Scan Complete', scanProgressTitle: 'Model scan progress', scanProgressPreparing: 'Preparing scan folders…', @@ -1772,9 +1772,9 @@ export const i18n = { sidebarResetConfirm: 'Reset window position, scale and dock state?', sidebarInjectHash: '🟢 Inject Workflow Hash', sidebarSkipHash: '⚪ Skip Workflow Hash', - sidebarManageFolders: '📁 Manage Folders', + sidebarManageFolders: 'Manage Folders', sidebarFeedback: '💬 Submit Feedback / Report Bug', - sidebarModelSettings: '🖼️ Model Settings', + sidebarModelSettings: 'Model Settings', sidebarModelCardSettings: 'Model Card Settings', sidebarModelCardDescription: 'Only changes cards in the model browser. Model files and original covers remain untouched.', sidebarVideoPlayback: 'Video cover playback', @@ -1791,7 +1791,7 @@ export const i18n = { sidebarExpandAll: 'Expand All', sidebarCollapseAll: 'Collapse All', sidebarSearchModels: 'Search models...', - sidebarFolderManagerTitle: '📁 Manage Folders', + sidebarFolderManagerTitle: 'Manage Folders', sidebarFolderManagerDesc: 'Drag and drop to reorder folders. Click the eye icon to hide unneeded folders. Hidden folders are skipped during scanning and consume zero performance.', sidebarCategoryMode: 'Category Mode (ComfyUI)', sidebarPhysicalMode: 'Physical Mode (Local)', @@ -1816,7 +1816,7 @@ export const i18n = { settingsBtn: 'Settings', closeSettings: '✖ Close Settings', doctorBtn: 'Node Assistant', - importBtn: '📥 Import / Export Workflow (Share Code)', + importBtn: 'Import / Export Workflow (Share Code)', recipeTitle: 'Workflow Recipes', recipeBetaNotice: 'Workflow Recipes are still in beta. Before updating the plugin or importing, restoring, deleting, or applying parameters, save the canvas and back up anomalous_recipes and anomalous_parameters under your user workflows directory.', recipeClose: 'Close Workflow Recipes', @@ -1998,7 +1998,7 @@ export const i18n = { recipeDetailSampler: 'Sampler', recipeDetailResolution: 'Resolution', recipeDetailUnavailable: 'Unavailable', - recipeDetailEditOrigin: '✏️ Edit Origin', + recipeDetailEditOrigin: 'Edit Origin', recipeOriginDialogTitle: 'Edit Model Origin', recipeOriginOfficialName: 'Official Model Name', recipeOriginModelUrl: 'Official Model URL', @@ -2106,7 +2106,7 @@ export const i18n = { recipeStatusMissing: '🔴 Missing Models ({count})', recipeStatusChecking: '⏳ Checking Environment…', recipeQuickSpecs: 'Quick Specs', - recipeApplyAllMatches: '⚡ Apply All Matches', + recipeApplyAllMatches: 'Apply All Matches', recipeApplyingAllMatches: 'Applying all matches…', recipeApplyAllMatchesSuccess: '✅ Applied all local matches!', recipeRawNodesToggle: 'Raw Node Parameters', diff --git a/web/modules/ui_detail.js b/web/modules/ui_detail.js index f5f5e49..083d2ff 100644 --- a/web/modules/ui_detail.js +++ b/web/modules/ui_detail.js @@ -485,7 +485,7 @@ export function showDetail(model) { notesTitle.style.letterSpacing = '0.5px'; const notesEditBtn = document.createElement('button'); - notesEditBtn.innerHTML = '✏️'; + notesEditBtn.innerHTML = ''; notesEditBtn.title = t('detailEditNotes'); notesEditBtn.style.background = 'transparent'; notesEditBtn.style.border = 'none'; @@ -858,7 +858,7 @@ export function showEditModal(model) { coverRow.style.gap = '8px'; const galleryBtn = document.createElement('button'); - galleryBtn.textContent = `🖼️ ${t('detailPickGallery')}`; + galleryBtn.innerHTML = `${t('detailPickGallery')}`; galleryBtn.style.padding = '8px'; galleryBtn.style.background = '#303134'; galleryBtn.style.color = '#8ab4f8'; @@ -875,7 +875,7 @@ export function showEditModal(model) { }; const localBtn = document.createElement('button'); - localBtn.textContent = `📁 ${t('detailUploadLocal')}`; + localBtn.innerHTML = `${t('detailUploadLocal')}`; localBtn.style.padding = '8px'; localBtn.style.background = '#303134'; localBtn.style.color = '#8ab4f8'; @@ -1641,7 +1641,7 @@ export function _openAdvancedModelSelector(initialSelectedMap, onConfirm) { } else { - imgContainer.innerHTML = '📄'; + imgContainer.innerHTML = ''; } card.appendChild(imgContainer); @@ -1882,7 +1882,7 @@ export function _openAdvancedModelSelector(initialSelectedMap, onConfirm) { leftPart.style.display = 'flex'; leftPart.style.alignItems = 'center'; leftPart.style.gap = '8px'; - leftPart.innerHTML = `📁 ${escapeHtml(fData.name)} (${escapeHtml(fData.model_count)})`; + leftPart.innerHTML = ` ${escapeHtml(fData.name)} (${escapeHtml(fData.model_count)})`; const badge = document.createElement('div'); badge.className = 'selection-badge'; diff --git a/web/modules/ui_doctor.js b/web/modules/ui_doctor.js index d7b32ce..b3d3f07 100644 --- a/web/modules/ui_doctor.js +++ b/web/modules/ui_doctor.js @@ -1394,8 +1394,11 @@ export function _openGalleryReplacer(node, w, options = {}) { folders.forEach(folderPath => { const button = document.createElement('button'); const depth = folderPath ? folderPath.split('/').length - 1 : 0; - const label = folderPath ? folderPath.split('/').pop() : t('pickerAllModels'); - button.textContent = `${folderPath ? '📂' : '📦'} ${label} (${folderCounts.get(folderPath) || 0})`; + const label = folderPath ? folderPath.split('/').pop() : t('pickerAllModels'); + const folderIcon = folderPath + ? '' + : ''; + button.innerHTML = `${folderIcon}${escapeHtml(label)} (${folderCounts.get(folderPath) || 0})`; button.title = folderPath || label; button.style.cssText = `text-align:left;padding:7px 8px 7px ${8 + depth * 14}px;border-radius:6px;border:none;cursor:pointer;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:${selectedFolder === folderPath ? '#fff' : '#aaa'};background:${selectedFolder === folderPath ? 'rgba(25,118,210,0.45)' : 'transparent'};`; button.onclick = () => { @@ -1460,7 +1463,7 @@ export function _openGalleryReplacer(node, w, options = {}) { image.style.cssText = 'width:100%;height:100%;object-fit:cover;'; previewBox.appendChild(image); } else { - previewBox.textContent = '🖼️'; + previewBox.innerHTML = ''; } if (isCurrent) { const badge = document.createElement('span'); @@ -1687,6 +1690,23 @@ function renderMaterialPresets(node, container, forceRefresh) { section.append(header, loader); container.appendChild(section); + const url = `/anomalous/materials/by_node_type?type=${encodeURIComponent(node.type)}${forceRefresh ? '&refresh=1' : ''}`; + fetch(url, { cache: 'no-store' }).then(response => { + if (!response.ok) throw new Error('material preset request failed'); + return response.json(); + }).then(payload => { + loader.remove(); + const materials = Array.isArray(payload.materials) ? payload.materials : []; + if (!materials.length) { + const empty = document.createElement('div'); + empty.style.cssText = 'font-size:11px;color:#666;text-align:center;padding:10px;background:rgba(0,0,0,.2);border-radius:8px;border:1px dashed rgba(255,255,255,.1);'; + empty.textContent = t('materialNoNodePresets'); + const loader = document.createElement('div'); + loader.style.cssText = 'font-size:12px;color:#555;text-align:center;padding:10px;'; + loader.textContent = t('loading'); + section.append(header, loader); + container.appendChild(section); + const url = `/anomalous/materials/by_node_type?type=${encodeURIComponent(node.type)}${forceRefresh ? '&refresh=1' : ''}`; fetch(url, { cache: 'no-store' }).then(response => { if (!response.ok) throw new Error('material preset request failed'); @@ -1705,26 +1725,19 @@ function renderMaterialPresets(node, container, forceRefresh) { const group = document.createElement('div'); group.style.cssText = 'display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid rgba(92,202,180,.16);border-radius:9px;background:rgba(92,202,180,.045);'; const name = document.createElement('strong'); - name.textContent = `🖼️ ${material.name || t('materialUntitled')}`; + name.innerHTML = `${escapeHtml(material.name || t('materialUntitled'))}`; name.style.cssText = 'font-size:12px;color:#bcebe1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;'; group.appendChild(name); for (const block of (Array.isArray(material.blocks) ? material.blocks : [])) { const apply = document.createElement('button'); const suffix = material.blocks.length > 1 ? ` #${block.occurrence}` : ''; - const label = `✨ ${block.title || block.type}${suffix}`; + const label = `${block.title || block.type}${suffix}`; apply.textContent = label; apply.style.cssText = 'padding:8px 10px;border:1px solid rgba(255,255,255,.1);border-radius:6px;background:rgba(255,255,255,.05);color:#fff;text-align:left;cursor:pointer;'; apply.onclick = () => { apply.disabled = true; try { applyMaterialToSelectedNode(node, block, material.workflow_hashes, group); - apply.textContent = `✅ ${t('materialNodeApplied')}`; - apply.style.background = 'rgba(46,139,87,.6)'; - - } catch (error) { - console.error('[Anomalous] Failed to apply material node parameters:', error); - apply.textContent = `⚠️ ${t('recipeParameterApplyError')}`; - apply.style.background = 'rgba(180,60,60,.55)'; } window.setTimeout(() => { apply.disabled = false; diff --git a/web/modules/ui_gallery.js b/web/modules/ui_gallery.js index 7588ddf..eff527e 100644 --- a/web/modules/ui_gallery.js +++ b/web/modules/ui_gallery.js @@ -152,7 +152,7 @@ export async function loadGalleryImages(page = 1, reset = false) { const delBtn = document.createElement('button'); delBtn.className = 'anomalous-gallery-delete'; - delBtn.innerHTML = '🗑️'; + delBtn.innerHTML = ''; delBtn.title = t('galleryDelete'); delBtn.onclick = (e) => { @@ -189,7 +189,7 @@ export async function loadGalleryImages(page = 1, reset = false) { btnRow.style.gap = '12px'; const confirmBtn = document.createElement('button'); - confirmBtn.textContent = `🗑️ ${t('galleryDelete')}`; + confirmBtn.innerHTML = `${t('galleryDelete')}`; confirmBtn.style.background = '#dc3545'; confirmBtn.style.color = '#fff'; confirmBtn.style.border = 'none'; diff --git a/web/modules/ui_gallery_detail.js b/web/modules/ui_gallery_detail.js index df6b661..c0f1111 100644 --- a/web/modules/ui_gallery_detail.js +++ b/web/modules/ui_gallery_detail.js @@ -673,11 +673,11 @@ function buildModelsSection(orderedRefs, groups, onOpenModel) { wrap.appendChild(groupEl); }; - renderGroup(t('materialModelBase') || '🎯 主模型 / UNet', groups.base, 'is-base'); - renderGroup(t('materialModelLora') || '🎨 LoRA 微调层', groups.lora, 'is-lora'); - renderGroup(t('materialModelClip') || '👁️ 文本编码器 (CLIP)', groups.clip, 'is-clip'); - renderGroup(t('materialModelVae') || '🖼️ VAE 编码器', groups.vae, 'is-vae'); - renderGroup(t('materialModelOther') || '⚡ 其它模型组件', groups.other, 'is-other'); + renderGroup(t('materialModelBase') || '主模型 / UNet', groups.base, 'is-base'); + renderGroup(t('materialModelLora') || 'LoRA 微调层', groups.lora, 'is-lora'); + renderGroup(t('materialModelClip') || '文本编码器 (CLIP)', groups.clip, 'is-clip'); + renderGroup(t('materialModelVae') || 'VAE 编码器', groups.vae, 'is-vae'); + renderGroup(t('materialModelOther') || '其它模型组件', groups.other, 'is-other'); return wrap; } @@ -1075,10 +1075,10 @@ async function renderInspectorContent(data, item) { tabsBar.className = 'anomalous-workbench-tabs-bar'; const tabs = [ - { id: 'specs', label: `📊 ${t('workbenchTabOverview') || '核心参数'}` }, - { id: 'prompts', label: `💬 ${t('workbenchTabPrompts') || '提示词'}` }, - { id: 'models', label: `🧩 ${t('workbenchTabModels') || '模型与LoRA'}` }, - { id: 'nodes', label: `⚙️ ${t('workbenchTabNodes') || '工作流节点'}` }, + { id: 'specs', label: t('workbenchTabOverview') || '核心参数', icon: '' }, + { id: 'prompts', label: t('workbenchTabPrompts') || '提示词', icon: '' }, + { id: 'models', label: t('workbenchTabModels') || '模型与LoRA', icon: '' }, + { id: 'nodes', label: t('workbenchTabNodes') || '工作流节点', icon: '' }, ]; const tabPanels = {}; @@ -1090,7 +1090,7 @@ async function renderInspectorContent(data, item) { tabBtn.className = (wb.activeTab === tDef.id) ? 'anomalous-workbench-tab-btn is-active' : 'anomalous-workbench-tab-btn'; - tabBtn.textContent = tDef.label; + tabBtn.innerHTML = `${tDef.icon}${tDef.label}`; tabBtn.onclick = () => { wb.activeTab = tDef.id; diff --git a/web/modules/ui_grid.js b/web/modules/ui_grid.js index ee7e6fe..46024e9 100644 --- a/web/modules/ui_grid.js +++ b/web/modules/ui_grid.js @@ -159,7 +159,7 @@ export async function loadModels() { }; const applyBtn = document.createElement('button'); - applyBtn.innerHTML = '➕'; + applyBtn.innerHTML = ``; applyBtn.className = 'anomalous-card-action-btn anomalous-tooltip-target'; applyBtn.removeAttribute('title'); applyBtn.setAttribute('data-tooltip', t('applyToCanvas')); @@ -178,7 +178,7 @@ export async function loadModels() { applyBtn.style.display = 'none'; const singleScanBtn = document.createElement('button'); - singleScanBtn.innerHTML = '🎯'; + singleScanBtn.innerHTML = ``; singleScanBtn.className = 'anomalous-card-action-btn anomalous-tooltip-target'; singleScanBtn.removeAttribute('title'); singleScanBtn.setAttribute('data-tooltip', t('scanModelPrecisely')); @@ -203,7 +203,7 @@ export async function loadModels() { card.appendChild(singleScanBtn); const editBtn = document.createElement('button'); - editBtn.innerHTML = '⚙️'; + editBtn.innerHTML = ``; editBtn.className = 'anomalous-card-action-btn anomalous-tooltip-target'; editBtn.removeAttribute('title'); editBtn.setAttribute('data-tooltip', t('editModel')); diff --git a/web/modules/ui_materials.js b/web/modules/ui_materials.js index 4c6e4ce..73c1982 100644 --- a/web/modules/ui_materials.js +++ b/web/modules/ui_materials.js @@ -266,8 +266,9 @@ function buildMaterialDetailHeader(owner, material) { const headerActions = document.createElement('div'); headerActions.className = 'anomalous-library-detail-actions'; if ((material.capabilities || []).includes('open_workflow')) { - const open = text(headerActions, 'button', `🚀 ${t('materialOpenWorkflow')}`, 'anomalous-btn-primary'); + const open = text(headerActions, 'button', '', 'anomalous-btn-primary'); open.type = 'button'; + open.innerHTML = `${t('materialOpenWorkflow')}`; open.onclick = async () => { open.disabled = true; try { @@ -282,8 +283,9 @@ function buildMaterialDetailHeader(owner, material) { const edit = text(headerActions, 'button', t('materialEditDetails'), 'anomalous-btn-ghost'); edit.type = 'button'; edit.onclick = () => toggleMaterialEditor(owner, material, header); - const remove = text(headerActions, 'button', `🗑️ ${t('materialDelete')}`, 'anomalous-btn-ghost'); + const remove = text(headerActions, 'button', '', 'anomalous-btn-ghost'); remove.type = 'button'; + remove.innerHTML = `${t('materialDelete')}`; remove.onclick = () => deleteMaterial(owner, material); header.appendChild(headerActions); @@ -515,7 +517,7 @@ function renderMaterialCard(owner, material) { const remove = document.createElement('button'); remove.type = 'button'; remove.className = 'anomalous-material-card-action-btn anomalous-material-card-delete'; - remove.innerHTML = '🗑️'; + remove.innerHTML = ''; remove.title = t('materialDelete'); remove.onclick = (e) => { e.stopPropagation(); @@ -529,7 +531,7 @@ function renderMaterialCard(owner, material) { const quickOpen = document.createElement('button'); quickOpen.type = 'button'; quickOpen.className = 'anomalous-material-card-action-btn anomalous-material-card-quick-load'; - quickOpen.innerHTML = '🚀'; + quickOpen.innerHTML = ''; quickOpen.title = t('materialOpenWorkflow'); quickOpen.onclick = async (e) => { e.stopPropagation(); diff --git a/web/modules/ui_notebooks.js b/web/modules/ui_notebooks.js index 33cb125..8ce57fb 100644 --- a/web/modules/ui_notebooks.js +++ b/web/modules/ui_notebooks.js @@ -139,7 +139,8 @@ export async function showNotebooks() { btnRow.style.gap = '5px'; const createBtn = document.createElement('button'); - createBtn.innerHTML = `➕ ${t('createNotebook')}`; + const createBtnHtml = `${t('createNotebook')}`; + createBtn.innerHTML = createBtnHtml; createBtn.className = 'anomalous-btn-primary'; const createInput = document.createElement('input'); @@ -168,7 +169,7 @@ export async function showNotebooks() { createInput.value = ''; } createInput.style.display = 'none'; - createBtn.innerHTML = `➕ ${t('createNotebook')}`; + createBtn.innerHTML = createBtnHtml; } }; @@ -217,7 +218,7 @@ export async function refreshNotebooks(autoOpenFirst = false) { if (this.currentNotebook && this.currentNotebook.filename === nb.filename) { item.classList.add('active'); } - item.innerHTML = `📄 ${escapeHtml(nb.name)}`; + item.innerHTML = `${escapeHtml(nb.name)}`; item.onclick = () => { this.currentNotebook = nb; this.renderNotebookEditor(); diff --git a/web/modules/ui_prompt_composer.js b/web/modules/ui_prompt_composer.js index 2558ca7..544260a 100644 --- a/web/modules/ui_prompt_composer.js +++ b/web/modules/ui_prompt_composer.js @@ -331,21 +331,24 @@ 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 = `📚 ${window.anomalous_browser_lang === 'zh' ? '成型提示词库' : 'Prompt Library'} `; + leftTitleWrap.innerHTML = `${window.anomalous_browser_lang === 'zh' ? '成型提示词库' : 'Prompt Library'} `; // Button: Create New Custom Card (placed in header right, never wraps) - const newCardTriggerBtn = text(leftHeader, 'button', `➕ ${window.anomalous_browser_lang === 'zh' ? '新建词卡' : 'New Card'}`, 'anomalous-btn-ghost anomalous-btn-sm'); + const newCardTriggerBtn = text(leftHeader, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm'); + newCardTriggerBtn.innerHTML = `${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'); // Button 1: Extract Prompts from Selected Canvas Node - const extractNodeBtn = text(leftSubActions, 'button', `🎯 ${window.anomalous_browser_lang === 'zh' ? '从节点提取' : 'From Node'}`, 'anomalous-btn-primary anomalous-btn-sm anomalous-btn-extract-node'); + const extractNodeBtn = text(leftSubActions, 'button', '', 'anomalous-btn-primary anomalous-btn-sm anomalous-btn-extract-node'); + extractNodeBtn.innerHTML = `${window.anomalous_browser_lang === 'zh' ? '从节点提取' : 'From Node'}`; extractNodeBtn.title = window.anomalous_browser_lang === 'zh' ? '读取 ComfyUI 画布当前选中节点的提示词文本并生成词卡' : 'Extract prompt text from selected canvas node into cards'; // Button 2: One-click Sync / Import from Material Library - const importMaterialsBtn = text(leftSubActions, 'button', `📥 ${window.anomalous_browser_lang === 'zh' ? '从素材库导入' : 'Import from Library'}`, 'anomalous-btn-ghost anomalous-btn-sm'); + const importMaterialsBtn = text(leftSubActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm'); + importMaterialsBtn.innerHTML = `${window.anomalous_browser_lang === 'zh' ? '从素材库导入' : 'Import from Library'}`; importMaterialsBtn.title = window.anomalous_browser_lang === 'zh' ? '读取素材库,一键把素材库里沉淀的提示词捞入当前工坊词库' : 'Read and import all prompts from Material Library into workbench'; // Search and category filters bar @@ -360,10 +363,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' ? '⚡ 触发' : '⚡ LoRA' }, + { 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' : ''}`); @@ -394,13 +397,16 @@ function buildPromptComposer(owner, container, options = {}) { const rightActions = text(rightHeader, 'div', '', 'anomalous-mixer-actions'); // Right quick extract: suck into right mixer directly - const rightSuckNodeBtn = text(rightActions, 'button', `🎯 ${t('promptReadSelectedNode')}`, 'anomalous-btn-ghost anomalous-btn-sm'); + const rightSuckNodeBtn = text(rightActions, 'button', '', 'anomalous-btn-ghost anomalous-btn-sm'); + rightSuckNodeBtn.innerHTML = `${t('promptReadSelectedNode')}`; rightSuckNodeBtn.title = window.anomalous_browser_lang === 'zh' ? '直接将画布选中节点的提示词作为积木吸入当前拼装台' : 'Extract node prompt directly into current mixer track'; - const smartSortBtn = text(rightActions, 'button', `🪄 ${t('promptSortByCategory')}`, 'anomalous-mixer-smart-sort-btn'); + const smartSortBtn = text(rightActions, 'button', '', 'anomalous-mixer-smart-sort-btn'); + smartSortBtn.innerHTML = `${t('promptSortByCategory')}`; smartSortBtn.title = window.anomalous_browser_lang === 'zh' ? '按 [通用底模 ➔ 风格氛围 ➔ 主体内容 ➔ LoRA/触发词] 自动排序' : 'Auto sort: [Base ➔ Style ➔ Subject ➔ Trigger]'; - const clearRightBtn = text(rightActions, 'button', '🧹', 'anomalous-btn-ghost'); + const clearRightBtn = text(rightActions, 'button', '', 'anomalous-btn-ghost'); + clearRightBtn.innerHTML = ''; clearRightBtn.title = window.anomalous_browser_lang === 'zh' ? '清空当前拼装池' : 'Clear current track'; // Blocks Container & Dropzone @@ -541,13 +547,13 @@ function buildPromptComposer(owner, container, options = {}) { if (extractedCount > 0) { renderSourceCardsList(); - showWorkbenchToast(`🎯 ${window.anomalous_browser_lang === 'zh' + showWorkbenchToast(window.anomalous_browser_lang === 'zh' ? `已成功从节点【${heading}】提取 ${extractedCount} 段提示词${intoRightMixer ? '并加入组合' : '并加入左侧词库'}!` - : `Successfully extracted ${extractedCount} prompts from [${heading}]!`}`); + : `Successfully extracted ${extractedCount} prompts from [${heading}]!`); } else { anomalousAlert(window.anomalous_browser_lang === 'zh' - ? `⚠️ 选中的节点【${heading}】文本内容为空!` - : `⚠️ The text fields in node [${heading}] are empty!`); + ? `选中的节点【${heading}】文本内容为空!` + : `The text fields in node [${heading}] are empty!`); } } @@ -557,12 +563,14 @@ function buildPromptComposer(owner, container, options = {}) { let materialSyncController = null; async function syncMaterialsIntoSourceDeck() { materialSyncController?.abort(); + if (materialSyncController) { + materialSyncController.abort(); + } const controller = new AbortController(); materialSyncController = controller; importMaterialsBtn.disabled = true; - importMaterialsBtn.textContent = `⏳ ${window.anomalous_browser_lang === 'zh' ? '同步中...' : 'Syncing...'}`; - let addedCount = 0; + importMaterialsBtn.textContent = window.anomalous_browser_lang === 'zh' ? '正在同步素材...' : 'Syncing...'; try { const res = await fetch('/anomalous/materials?category=prompts&limit=150', { signal: controller.signal }); @@ -583,7 +591,6 @@ function buildPromptComposer(owner, container, options = {}) { persisted: true, filename: item.filename, }); - addedCount++; } if (prompts.negative && !sourceCards.some(c => c.content === prompts.negative.trim())) { sourceCards.push({ @@ -595,7 +602,6 @@ function buildPromptComposer(owner, container, options = {}) { persisted: true, filename: item.filename, }); - addedCount++; } } catch (err) {} } @@ -603,9 +609,9 @@ function buildPromptComposer(owner, container, options = {}) { if (controller.signal.aborted || !view.isConnected) return; renderSourceCardsList(); - showWorkbenchToast(`📥 ${window.anomalous_browser_lang === 'zh' + showWorkbenchToast(window.anomalous_browser_lang === 'zh' ? (addedCount > 0 ? `已从素材库成功同步并导入 ${addedCount} 条提示词卡片!` : '素材库提示词已是最新状态,未发现新词条。') - : `Synced from Material Library: ${addedCount} new prompts added!`}`); + : `Synced from Material Library: ${addedCount} new prompts added!`); } catch (error) { if (error.name !== 'AbortError') { await anomalousAlert(t('materialLoadError')); @@ -614,7 +620,7 @@ function buildPromptComposer(owner, container, options = {}) { if (materialSyncController === controller) { materialSyncController = null; importMaterialsBtn.disabled = false; - importMaterialsBtn.textContent = `📥 ${window.anomalous_browser_lang === 'zh' ? '从素材库导入' : 'Import from Library'}`; + importMaterialsBtn.innerHTML = `${window.anomalous_browser_lang === 'zh' ? '从素材库导入' : 'Import from Library'}`; } } } @@ -893,7 +899,8 @@ function buildPromptComposer(owner, container, options = {}) { }; if (card.persisted === false || (card.id.startsWith('node_') && !card.persisted)) { - const saveCardBtn = text(actions, 'button', `💾 ${t('promptSaveCard')}`, 'anomalous-source-action-btn is-save-lib'); + const saveCardBtn = text(actions, 'button', '', 'anomalous-source-action-btn is-save-lib'); + saveCardBtn.innerHTML = `${t('promptSaveCard')}`; saveCardBtn.title = window.anomalous_browser_lang === 'zh' ? '将此卡片存入素材库' : 'Save this card to Material Library'; saveCardBtn.onclick = async (e) => { e.stopPropagation(); @@ -901,7 +908,8 @@ function buildPromptComposer(owner, container, options = {}) { }; } - const dockBtn = text(actions, 'button', `➕ ${t('promptAddToPlan')}`, 'anomalous-source-action-btn is-dock'); + const dockBtn = text(actions, 'button', '', 'anomalous-source-action-btn is-dock'); + dockBtn.innerHTML = `${t('promptAddToPlan')}`; dockBtn.title = window.anomalous_browser_lang === 'zh' ? '加入当前拼装组合' : 'Add into mixer track'; dockBtn.onclick = (e) => { e.stopPropagation(); @@ -919,8 +927,8 @@ function buildPromptComposer(owner, container, options = {}) { const negCount = draft.plan.parts.filter(p => p.role === 'negative').length; const tabs = [ - { key: 'positive', label: `✨ ${window.anomalous_browser_lang === 'zh' ? '正向拼装台' : 'Positive'} (${posCount})` }, - { key: 'negative', label: `🚫 ${window.anomalous_browser_lang === 'zh' ? '负向拼装台' : 'Negative'} (${negCount})` }, + { key: 'positive', label: `${window.anomalous_browser_lang === 'zh' ? '正向拼装台' : 'Positive'} (${posCount})` }, + { key: 'negative', label: `${window.anomalous_browser_lang === 'zh' ? '负向拼装台' : 'Negative'} (${negCount})` }, ]; for (const tab of tabs) { @@ -968,7 +976,13 @@ function buildPromptComposer(owner, container, options = {}) { if (!currentRoleParts.length) { const dropzoneNotice = text(blocksContainer, 'div', '', 'anomalous-assembly-dropzone'); dropzoneNotice.innerHTML = ` -
📥
+
+ + + + + +
${window.anomalous_browser_lang === 'zh' ? '将左侧的成型提示词拖放到这里摆放与拼装' : 'Drop prompt cards from the left panel here'}
${window.anomalous_browser_lang === 'zh' ? '支持自由调换次序,点击【智能理顺】自动按画质底模➔风格➔主体➔LoRA排序' : 'Drag to reorder anytime, or click Smart Sort to auto-align.'}
`; @@ -1257,12 +1271,12 @@ function buildPromptComposer(owner, container, options = {}) { const targets = promptWidgetTargets(node); if (!node || !targets.length) { - text(targetBar, 'span', `💡 ${t('promptSelectTextNode')}`, 'anomalous-prompt-target-info'); + text(targetBar, 'span', t('promptSelectTextNode'), 'anomalous-prompt-target-info'); return; } const info = text(targetBar, 'div', '', 'anomalous-prompt-target-info'); - info.textContent = `🎯 ${t('materialApplyingTo', { name: materialNodeHeading(node), id: node.id })}`; + info.textContent = t('materialApplyingTo', { name: materialNodeHeading(node), id: node.id }); const actions = text(targetBar, 'div', '', 'anomalous-prompt-target-actions'); const widgetSelect = text(actions, 'select', ''); diff --git a/web/modules/ui_recipe_detail.js b/web/modules/ui_recipe_detail.js index 50aafe2..228a6f6 100644 --- a/web/modules/ui_recipe_detail.js +++ b/web/modules/ui_recipe_detail.js @@ -1082,11 +1082,15 @@ function renderOverview(content, owner, recipe, references, finish) { overviewActions.style.alignItems = 'center'; overviewActions.style.gap = '8px'; + const heroAppendIcon = recipe?.workflow_scope === 'partial' + ? `` + : ``; const heroAppend = button( overviewActions, - `${recipe?.workflow_scope === 'partial' ? '🧩' : '🚀'} ${recipeCanvasActionLabel(recipe)}`, + '', 'anomalous-recipe-btn-primary-action', ); + heroAppend.innerHTML = `${heroAppendIcon}${recipeCanvasActionLabel(recipe)}`; heroAppend.style.padding = '8px 16px'; heroAppend.style.fontSize = '0.88rem'; heroAppend.onclick = () => { @@ -1100,11 +1104,13 @@ function renderOverview(content, owner, recipe, references, finish) { appendText(more, 'summary', t('notebookMore')); overviewActions.appendChild(more); - const heroEdit = button(more, '✏️ ' + t('recipeEdit'), 'anomalous-btn-ghost'); + const heroEdit = button(more, '', 'anomalous-btn-ghost'); + heroEdit.innerHTML = `${t('recipeEdit')}`; heroEdit.title = t('recipeEdit'); heroEdit.onclick = () => finish('edit'); - const heroExport = button(more, '📥 ' + t('recipeExport'), 'anomalous-btn-ghost'); + const heroExport = button(more, '', 'anomalous-btn-ghost'); + heroExport.innerHTML = `${t('recipeExport')}`; heroExport.title = t('recipeExport'); heroExport.onclick = async () => { try { @@ -1164,7 +1170,7 @@ function renderOverview(content, owner, recipe, references, finish) { const advanced = document.createElement('details'); advanced.className = 'anomalous-recipe-advanced-info'; advanced.style.marginBottom = '14px'; - appendText(advanced, 'summary', `⚙️ ${t('recipeAdvancedInfo')}`); + appendText(advanced, 'summary', t('recipeAdvancedInfo')); const fingerprint = document.createElement('div'); fingerprint.className = 'anomalous-recipe-advanced-row'; appendText(fingerprint, 'span', `${t('recipeDetailFingerprint')}:`); @@ -2031,7 +2037,7 @@ function renderRawNodesLazy(parent, source, options = {}) { const summary = document.createElement('summary'); summary.className = 'anomalous-recipe-raw-nodes-summary'; - summary.textContent = `⚙️ ${t('recipeRawNodesToggle')} (${ordered.length} ${t('recipeRawNodesCount')}) ▾`; + summary.textContent = `${t('recipeRawNodesToggle')} (${ordered.length} ${t('recipeRawNodesCount')}) ▾`; details.appendChild(summary); let selecting = false; @@ -2436,14 +2442,16 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, } finally { readCurrent.disabled = false; readCurrent.classList.remove('is-busy'); - readCurrent.textContent = originalLabel; + readCurrent.innerHTML = originalLabel; } }; - const readCurrent = button(sidebarActions, '💾 ' + t('recipeParameterReadCurrent'), 'anomalous-preset-btn-primary'); + const readCurrent = button(sidebarActions, '', 'anomalous-preset-btn-primary'); + readCurrent.innerHTML = `${t('recipeParameterReadCurrent')}`; readCurrent.onclick = readCurrentHandler; - const newSnapshot = button(sidebarActions, '➕ ' + t('recipeParameterNew'), 'anomalous-preset-btn-secondary'); + const newSnapshot = button(sidebarActions, '', 'anomalous-preset-btn-secondary'); + newSnapshot.innerHTML = `${t('recipeParameterNew')}`; newSnapshot.onclick = () => { const draft = cloneJson({ workflow: baseSource.workflow, @@ -2532,7 +2540,8 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, ); }; - const rename = button(actions, '✏️', 'anomalous-preset-item-btn'); + const rename = button(actions, '', 'anomalous-preset-item-btn'); + rename.innerHTML = ''; rename.title = t('recipeParameterRename'); rename.onclick = async (e) => { e.stopPropagation(); @@ -2559,7 +2568,8 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, } }; - const remove = button(actions, '🗑️', 'anomalous-preset-item-btn is-delete'); + const remove = button(actions, '', 'anomalous-preset-item-btn is-delete'); + remove.innerHTML = ''; remove.title = t('recipeParameterDelete'); remove.onclick = async (e) => { e.stopPropagation(); @@ -2625,7 +2635,8 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, const consoleActions = document.createElement('div'); consoleActions.className = 'anomalous-preset-console-actions'; - const applyButton = button(consoleActions, '🚀 ' + t('recipeParameterApply'), 'anomalous-preset-btn-primary'); + const applyButton = button(consoleActions, '', 'anomalous-preset-btn-primary'); + applyButton.innerHTML = `${t('recipeParameterApply')}`; applyButton.style.padding = '8px 16px'; applyButton.style.fontSize = '0.88rem'; const applyStatus = appendText(consoleActions, 'small', '', 'anomalous-recipe-header-status'); @@ -2665,7 +2676,8 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, ); if (parameterState?.selectedFilename) { - const renameHeadingBtn = button(consoleActions, '✏️ ' + t('recipeParameterRename'), 'anomalous-btn-ghost'); + const renameHeadingBtn = button(consoleActions, '', 'anomalous-btn-ghost'); + renameHeadingBtn.innerHTML = `${t('recipeParameterRename')}`; renameHeadingBtn.onclick = async () => { const newName = await anomalousPrompt(t('recipeParameterRenamePrompt'), currentName); if (newName === null || !newName.trim() || newName.trim() === currentName) return; @@ -2698,7 +2710,8 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, const thumb = document.createElement('img'); thumb.src = outputImageUrl(gallery.images[0]); compactGallery.appendChild(thumb); - appendText(compactGallery, 'span', `🖼️ ${gallery.images.length}`, 'anomalous-preset-compact-gallery-badge'); + const galleryBadge = appendText(compactGallery, 'span', '', 'anomalous-preset-compact-gallery-badge'); + galleryBadge.innerHTML = `${gallery.images.length}`; compactGallery.onclick = () => { const dialog = document.createElement('dialog'); dialog.className = 'anomalous-recipe-gallery-dialog'; @@ -2724,7 +2737,8 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, card.appendChild(image); const actions = document.createElement('div'); actions.className = 'anomalous-recipe-gallery-card-actions'; - const details = button(actions, `🔎 ${t('materialViewDetails')}`, 'anomalous-btn-primary'); + const details = button(actions, '', 'anomalous-btn-primary'); + details.innerHTML = `${t('materialViewDetails')}`; details.onclick = event => { event.stopPropagation(); const images = gallery.images; @@ -2761,13 +2775,13 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, bentoGrid.className = 'anomalous-recipe-bento-grid'; const bentoItems = [ - { label: t('recipeDetailSteps') || '步数', val: params.steps, icon: '⚡' }, - { label: t('recipeDetailCFG') || 'CFG Scale', val: params.cfg, icon: '🎯' }, - { label: t('recipeDetailSampler') || '采样器', val: params.sampler_name || params.samplers, icon: '🎲' }, - { label: t('recipeDetailScheduler') || '调度器', val: params.scheduler, icon: '📈' }, - { label: t('recipeDetailResolution') || '分辨率', val: resDisplay || params.resolution, icon: '📐' }, - { label: t('recipeDetailDenoise') || '降噪比', val: params.denoise, icon: '🌊' }, - { label: t('recipeDetailSeed') || '随机种子', val: params.seed, icon: '🌱', isSeed: true }, + { label: t('recipeDetailSteps') || '步数', val: params.steps, icon: '' }, + { label: t('recipeDetailCFG') || 'CFG Scale', val: params.cfg, icon: '' }, + { label: t('recipeDetailSampler') || '采样器', val: params.sampler_name || params.samplers, icon: '' }, + { label: t('recipeDetailScheduler') || '调度器', val: params.scheduler, icon: '' }, + { label: t('recipeDetailResolution') || '分辨率', val: resDisplay || params.resolution, icon: '' }, + { label: t('recipeDetailDenoise') || '降噪比', val: params.denoise, icon: '' }, + { label: t('recipeDetailSeed') || '随机种子', val: params.seed, icon: '', isSeed: true }, ].filter(item => item.val !== undefined && item.val !== null && item.val !== ''); if (bentoItems.length > 0) { @@ -2781,7 +2795,10 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, labelRow.style.justifyContent = 'space-between'; labelRow.style.alignItems = 'center'; - appendText(labelRow, 'span', `${item.icon} ${item.label}`, 'anomalous-recipe-bento-label'); + const bentoLabel = document.createElement('span'); + bentoLabel.className = 'anomalous-recipe-bento-label'; + bentoLabel.innerHTML = `${item.icon}${item.label}`; + labelRow.appendChild(bentoLabel); if (item.isSeed) { const copySeedBtn = document.createElement('button'); @@ -2818,14 +2835,15 @@ function renderRecipeParameters(content, owner, recipe, gallery, refreshGallery, if (baseModelVal) { const modelCard = document.createElement('div'); modelCard.className = 'anomalous-recipe-model-highlight-card'; - modelCard.innerHTML = `
🧠${t('recipeDetailBaseModel') || '底模'}
${escapeHtml(String(baseModelVal))}
`; + modelCard.innerHTML = `
${t('recipeDetailBaseModel') || '底模'}
${escapeHtml(String(baseModelVal))}
`; modelsDeck.appendChild(modelCard); } if (Array.isArray(params.loras) && params.loras.length > 0) { const loraSection = document.createElement('div'); loraSection.className = 'anomalous-recipe-lora-stack'; - appendText(loraSection, 'div', `⚡ ${t('recipeDetailLoraSummary') || 'LoRA 阵容'} (${params.loras.length})`, 'anomalous-recipe-bento-label'); + const loraHeader = appendText(loraSection, 'div', '', 'anomalous-recipe-bento-label'); + loraHeader.innerHTML = `${t('recipeDetailLoraSummary') || 'LoRA 阵容'} (${params.loras.length})`; const loraGrid = document.createElement('div'); loraGrid.style.display = 'grid'; diff --git a/web/modules/ui_recipes.js b/web/modules/ui_recipes.js index 8074aa8..6e8c680 100644 --- a/web/modules/ui_recipes.js +++ b/web/modules/ui_recipes.js @@ -851,13 +851,15 @@ function buildRecipeStudioTopbar(owner) { viewSwitch.append(gridBtn, listBtn); right.appendChild(viewSwitch); - const saveBtn = appendText(right, 'button', `💾 ${t('recipeSaveCurrent')}`, 'anomalous-recipe-topbar-btn is-primary'); + const saveBtn = appendText(right, 'button', '', 'anomalous-recipe-topbar-btn is-primary'); saveBtn.dataset.recipeSaveCurrent = 'true'; saveBtn.type = 'button'; + saveBtn.innerHTML = `${t('recipeSaveCurrent')}`; saveBtn.onclick = () => owner.handleSaveRecipe(); - const importBtn = appendText(right, 'button', `📥 ${t('recipeImport')}`, 'anomalous-recipe-topbar-btn'); + const importBtn = appendText(right, 'button', '', 'anomalous-recipe-topbar-btn'); importBtn.type = 'button'; + importBtn.innerHTML = `${t('recipeImport')}`; importBtn.onclick = () => { const input = document.createElement('input'); input.type = 'file'; @@ -974,19 +976,22 @@ function createRecipeQuickSpecs(params) { const loras = Array.isArray(params.loras) ? params.loras : []; if (loras.length > 0) { - const loraTag = appendText(strip, 'span', `🧩 ${loras.length} ${t('recipeCardSpecsLoras')}`, 'anomalous-recipe-spec-tag is-lora'); + const loraTag = appendText(strip, 'span', '', 'anomalous-recipe-spec-tag is-lora'); + loraTag.innerHTML = `${loras.length} ${t('recipeCardSpecsLoras')}`; loraTag.title = loras.map((l) => (typeof l === 'object' && l?.name ? l.name : String(l))).join(', '); } if (params.steps) { const samplerName = params.sampler_name ? ` · ${params.sampler_name}` : ''; - const stepTag = appendText(strip, 'span', `⏱️ ${params.steps} ${t('recipeCardSpecsSteps')}${samplerName}`, 'anomalous-recipe-spec-tag is-step'); + const stepTag = appendText(strip, 'span', '', 'anomalous-recipe-spec-tag is-step'); + stepTag.innerHTML = `${params.steps} ${t('recipeCardSpecsSteps')}${samplerName}`; stepTag.title = `${params.steps} ${t('recipeCardSpecsSteps')}${samplerName}`; } const formattedRes = formatRecipeResolution(params.resolution); if (formattedRes) { - const resTag = appendText(strip, 'span', `📐 ${formattedRes}`, 'anomalous-recipe-spec-tag'); + const resTag = appendText(strip, 'span', '', 'anomalous-recipe-spec-tag'); + resTag.innerHTML = `${formattedRes}`; resTag.title = `${t('recipeCardSpecsResolution')}: ${formattedRes}`; } @@ -1044,19 +1049,23 @@ function createRecipeCard(owner, recipe) { const baseModelStr = data.params?.baseModel || data.params?.baseModels; if (baseModelStr) { - const pill = appendText(bottomChips, 'span', `📦 ${modelDisplayName(baseModelStr)}`, 'anomalous-recipe-cover-pill'); + const pill = document.createElement('span'); + pill.className = 'anomalous-recipe-cover-pill'; + pill.innerHTML = `${escapeHtml(modelDisplayName(baseModelStr))}`; pill.title = String(baseModelStr); + bottomChips.appendChild(pill); } mediaWrap.appendChild(bottomChips); // Scope Pill (Top-right of cover) const isPartial = data.workflow_scope === 'partial'; - appendText( - mediaWrap, - 'span', - isPartial ? '🧩 ' + t('recipeScopePill_partial') : '⚡ ' + t('recipeScopePill_complete'), - `anomalous-recipe-scope-pill ${isPartial ? 'is-partial' : 'is-complete'}`, - ); + const scopePill = document.createElement('span'); + scopePill.className = `anomalous-recipe-scope-pill ${isPartial ? 'is-partial' : 'is-complete'}`; + const scopeSvg = isPartial + ? `` + : ``; + scopePill.innerHTML = `${scopeSvg}${t(isPartial ? 'recipeScopePill_partial' : 'recipeScopePill_complete')}`; + mediaWrap.appendChild(scopePill); card.appendChild(mediaWrap); // 2. Card Body @@ -1093,17 +1102,15 @@ function createRecipeCard(owner, recipe) { body.appendChild(tags); } - // Footer: Primary Action Button ("🚀 载入画布" / "🧩 追加画布") + Mini Actions + // Footer: Primary Action Button + Mini Actions const footer = document.createElement('div'); footer.className = 'anomalous-recipe-card-footer'; - const appendBtn = appendText( - footer, - 'button', - `${isPartial ? '🧩' : '🚀'} ${t(isPartial ? 'recipeAppendCanvas' : 'recipeOpenCanvas')}`, - 'anomalous-recipe-btn-primary-action anomalous-tooltip-target', - ); + const appendBtn = document.createElement('button'); appendBtn.type = 'button'; + appendBtn.className = 'anomalous-recipe-btn-primary-action anomalous-tooltip-target'; + const actionSvg = ``; + appendBtn.innerHTML = `${actionSvg}${t(isPartial ? 'recipeAppendCanvas' : 'recipeOpenCanvas')}`; appendBtn.removeAttribute('title'); appendBtn.setAttribute('data-tooltip', isPartial ? t('recipeAppendCanvas') : t('recipeOpenCanvas')); appendBtn.setAttribute('data-tooltip-pos', 'top'); @@ -1115,12 +1122,15 @@ function createRecipeCard(owner, recipe) { await applyRecipeToCanvas(owner, fullRecipe); }, isPartial ? 'recipeAppendError' : 'recipeOpenError'); }; + footer.appendChild(appendBtn); const miniActions = document.createElement('div'); miniActions.className = 'anomalous-recipe-card-mini-actions'; - const editBtn = appendText(miniActions, 'button', '✏️', 'anomalous-recipe-card-mini-btn anomalous-tooltip-target'); + const editBtn = document.createElement('button'); editBtn.type = 'button'; + editBtn.className = 'anomalous-recipe-card-mini-btn anomalous-tooltip-target'; + editBtn.innerHTML = ``; editBtn.removeAttribute('title'); editBtn.setAttribute('data-tooltip', t('recipeEdit')); editBtn.setAttribute('data-tooltip-pos', 'top'); @@ -1129,8 +1139,10 @@ function createRecipeCard(owner, recipe) { runRecipeCardAction(editBtn, () => editRecipe(owner, recipe?.data || {}, recipe.filename), 'recipeUpdateError'); }; - const exportBtn = appendText(miniActions, 'button', '📥', 'anomalous-recipe-card-mini-btn anomalous-tooltip-target'); + const exportBtn = document.createElement('button'); exportBtn.type = 'button'; + exportBtn.className = 'anomalous-recipe-card-mini-btn anomalous-tooltip-target'; + exportBtn.innerHTML = ``; exportBtn.removeAttribute('title'); exportBtn.setAttribute('data-tooltip', t('recipeExport')); exportBtn.setAttribute('data-tooltip-pos', 'top'); @@ -1139,8 +1151,10 @@ function createRecipeCard(owner, recipe) { runRecipeCardAction(exportBtn, () => exportRecipePackage(recipe.filename), 'recipeExportError'); }; - const removeBtn = appendText(miniActions, 'button', '🗑️', 'anomalous-recipe-card-mini-btn is-delete anomalous-tooltip-target'); + const removeBtn = document.createElement('button'); removeBtn.type = 'button'; + removeBtn.className = 'anomalous-recipe-card-mini-btn is-delete anomalous-tooltip-target'; + removeBtn.innerHTML = ``; removeBtn.removeAttribute('title'); removeBtn.setAttribute('data-tooltip', t('recipeDelete')); removeBtn.setAttribute('data-tooltip-pos', 'top'); diff --git a/web/modules/ui_sidebar.js b/web/modules/ui_sidebar.js index 1311484..09deddc 100644 --- a/web/modules/ui_sidebar.js +++ b/web/modules/ui_sidebar.js @@ -13,13 +13,13 @@ const t = (key, params) => translate(key, params); const SCAN_RADAR_ICON_SVG = ``; const SIDEBAR_ICONS = { - MODELS: ``, + MODELS: ``, GALLERY: ``, - RECIPES: ``, + RECIPES: ``, DOCK: ``, HELP: ``, DOCTOR: ``, - ASSISTANT: ``, + ASSISTANT: ``, SETTINGS: ``, FOLDER: ``, CHEVRON_UP: ``, @@ -218,14 +218,14 @@ export function createDOM() { } }, 1000); - const spacer = document.createElement('div'); - spacer.style.flex = '1 1 auto'; - const leftGroup = document.createElement('div'); - leftGroup.className = 'anomalous-header-group'; + leftGroup.className = 'anomalous-header-group anomalous-header-left'; + + const centerGroup = document.createElement('div'); + centerGroup.className = 'anomalous-header-group anomalous-header-center'; const rightGroup = document.createElement('div'); - rightGroup.className = 'anomalous-header-group'; + rightGroup.className = 'anomalous-header-group anomalous-header-right'; // We will define hideAllPanels as a class method instead of a local closure to make it globally accessible. @@ -341,9 +341,11 @@ export function createDOM() { this.showRecipes(); }; - rightGroup.appendChild(modelsBtn); - rightGroup.appendChild(galleryBtn); - rightGroup.appendChild(nbBtn); + centerGroup.appendChild(modelsBtn); + centerGroup.appendChild(galleryBtn); + centerGroup.appendChild(nbBtn); + leftGroup.appendChild(dockBtn); + leftGroup.appendChild(helpBtn); const apiKeyBtn = document.createElement('button'); apiKeyBtn.id = 'anomalous-api-btn'; @@ -367,7 +369,7 @@ export function createDOM() { scanBtn.removeAttribute('title'); scanBtn.setAttribute('aria-label', t('sidebarScanWizard')); scanBtn.setAttribute('data-tooltip', t('sidebarScanWizard')); - scanBtn.setAttribute('data-tooltip-pos', 'right'); + scanBtn.setAttribute('data-tooltip-pos', 'top'); setScanButtonState(scanBtn, false); scanBtn.style.background = 'transparent'; scanBtn.style.border = 'none'; @@ -728,7 +730,11 @@ export function createDOM() { left.style.gap = '16px'; const iconEl = document.createElement('div'); - iconEl.innerText = icon; + if (typeof icon === 'string' && icon.startsWith('<')) { + iconEl.innerHTML = icon; + } else { + iconEl.innerText = icon; + } iconEl.style.fontSize = '1.4em'; iconEl.style.lineHeight = '1.2'; iconEl.style.width = '24px'; @@ -819,7 +825,7 @@ export function createDOM() { pRow.style.display = 'flex'; pRow.style.alignItems = 'center'; pRow.style.gap = '8px'; - pRow.innerHTML = `💾 ${t('sidebarPhysicalRename')}`; + pRow.innerHTML = `${t('sidebarPhysicalRename')}`; pRow.appendChild(physicalSwitch); const pDesc = document.createElement('div'); @@ -846,7 +852,8 @@ export function createDOM() { dualChannelRow.appendChild(vContainer); dualChannelRow.appendChild(pContainer); - s2List.appendChild(createListRow('📝', 'sidebarNormalize', 'sidebarNormalizeDesc', createMaterialSwitch(enableRename, (s) => { enableRename = s; updateDualChannelUI(); }))); + const editSvg = ''; + s2List.appendChild(createListRow(editSvg, 'sidebarNormalize', 'sidebarNormalizeDesc', createMaterialSwitch(enableRename, (s) => { enableRename = s; updateDualChannelUI(); }))); s2List.lastChild.style.borderBottom = 'none'; s2List.appendChild(dualChannelRow); updateDualChannelUI(); @@ -865,9 +872,12 @@ export function createDOM() { const s3List = document.createElement('div'); const isInject = localStorage.getItem('anomalous_inject_hash') !== 'false'; - s3List.appendChild(createListRow('📦', 'sidebarProvenance', 'sidebarProvenanceDesc', createMaterialSwitch(isInject, (s) => localStorage.setItem('anomalous_inject_hash', s ? 'true' : 'false')))); - s3List.appendChild(createListRow('⚠️', 'sidebarOverwrite', 'sidebarOverwriteDesc', createMaterialSwitch(enableForceOverwrite, (s) => enableForceOverwrite = s))); - s3List.appendChild(createListRow('🪄', 'sidebarSmartFix', 'sidebarSmartFixDesc', createMaterialSwitch(enableAutoCheck, (s) => enableAutoCheck = s))); + const cubeSvg = ''; + const warnSvg = ''; + const wandSvg = ''; + s3List.appendChild(createListRow(cubeSvg, 'sidebarProvenance', 'sidebarProvenanceDesc', createMaterialSwitch(isInject, (s) => localStorage.setItem('anomalous_inject_hash', s ? 'true' : 'false')))); + s3List.appendChild(createListRow(warnSvg, 'sidebarOverwrite', 'sidebarOverwriteDesc', createMaterialSwitch(enableForceOverwrite, (s) => enableForceOverwrite = s))); + s3List.appendChild(createListRow(wandSvg, 'sidebarSmartFix', 'sidebarSmartFixDesc', createMaterialSwitch(enableAutoCheck, (s) => enableAutoCheck = s))); s3List.lastChild.style.borderBottom = 'none'; section3.appendChild(s3List); @@ -1125,11 +1135,10 @@ export function createDOM() { - rightGroup.appendChild(dockBtn); rightGroup.appendChild(closeBtn); header.appendChild(leftGroup); - header.appendChild(spacer); + header.appendChild(centerGroup); header.appendChild(rightGroup); const settingsHubModal = document.createElement('div'); @@ -1159,7 +1168,7 @@ export function createDOM() { scanBtn.removeAttribute('title'); scanBtn.setAttribute('aria-label', t('sidebarScanWizard')); scanBtn.setAttribute('data-tooltip', t('sidebarScanWizard')); - scanBtn.setAttribute('data-tooltip-pos', 'right'); + scanBtn.setAttribute('data-tooltip-pos', 'top'); setScanButtonState(scanBtn, isCurrentlyScanning); helpBtn.innerHTML = `${SIDEBAR_ICONS.HELP}${t('help')}`; nbBtn.removeAttribute('title'); @@ -1168,13 +1177,13 @@ export function createDOM() { nbBtn.innerHTML = `${SIDEBAR_ICONS.RECIPES}${t('recipeTitle')}`; const dBtn = document.getElementById('anomalous-doctor-btn'); - if (dBtn) { dBtn.removeAttribute('title'); dBtn.setAttribute('data-tooltip', t('sidebarDoctor')); dBtn.setAttribute('data-tooltip-pos', 'right'); } + if (dBtn) { dBtn.removeAttribute('title'); dBtn.setAttribute('data-tooltip', t('sidebarDoctor')); dBtn.setAttribute('data-tooltip-pos', 'top'); } const aBtn = document.getElementById('anomalous-assistant-btn'); - if (aBtn) { aBtn.removeAttribute('title'); aBtn.setAttribute('data-tooltip', t('sidebarAssistant')); aBtn.setAttribute('data-tooltip-pos', 'right'); } + if (aBtn) { aBtn.removeAttribute('title'); aBtn.setAttribute('data-tooltip', t('sidebarAssistant')); aBtn.setAttribute('data-tooltip-pos', 'top'); } const iBtn = document.getElementById('anomalous-materials-btn'); - if (iBtn) { iBtn.removeAttribute('title'); iBtn.setAttribute('aria-label', t('materialLibrary')); iBtn.setAttribute('data-tooltip', t('materialLibrary')); iBtn.setAttribute('data-tooltip-pos', 'right'); } + if (iBtn) { iBtn.removeAttribute('title'); iBtn.setAttribute('aria-label', t('materialLibrary')); iBtn.setAttribute('data-tooltip', t('materialLibrary')); iBtn.setAttribute('data-tooltip-pos', 'top'); } const sBtn = document.getElementById('anomalous-global-settings-btn'); - if (sBtn) { sBtn.removeAttribute('title'); sBtn.setAttribute('data-tooltip', t('sidebarSettings')); sBtn.setAttribute('data-tooltip-pos', 'right'); } + if (sBtn) { sBtn.removeAttribute('title'); sBtn.setAttribute('data-tooltip', t('sidebarSettings')); sBtn.setAttribute('data-tooltip-pos', 'top'); } // Reset dynamic panels so they re-render in new language if (window.anomalousBrowserInstance) { @@ -1647,7 +1656,7 @@ export function createDOM() { settingsBtn.removeAttribute('title'); settingsBtn.setAttribute('aria-label', t('sidebarSettings')); settingsBtn.setAttribute('data-tooltip', t('sidebarSettings')); - settingsBtn.setAttribute('data-tooltip-pos', 'right'); + settingsBtn.setAttribute('data-tooltip-pos', 'top'); settingsBtn.style.background = 'transparent'; settingsBtn.style.border = 'none'; settingsBtn.style.borderRadius = '6px'; @@ -1679,7 +1688,7 @@ export function createDOM() { importBtn.removeAttribute('title'); importBtn.setAttribute('aria-label', t('materialLibrary')); importBtn.setAttribute('data-tooltip', t('materialLibrary')); - importBtn.setAttribute('data-tooltip-pos', 'right'); + importBtn.setAttribute('data-tooltip-pos', 'top'); importBtn.innerHTML = ``; importBtn.style.background = 'transparent'; importBtn.style.border = 'none'; @@ -1697,7 +1706,7 @@ export function createDOM() { doctorBtn.removeAttribute('title'); doctorBtn.setAttribute('aria-label', t('sidebarDoctor')); doctorBtn.setAttribute('data-tooltip', t('sidebarDoctor')); - doctorBtn.setAttribute('data-tooltip-pos', 'right'); + doctorBtn.setAttribute('data-tooltip-pos', 'top'); doctorBtn.innerHTML = SIDEBAR_ICONS.DOCTOR; doctorBtn.style.background = 'transparent'; doctorBtn.style.border = 'none'; @@ -1733,7 +1742,7 @@ export function createDOM() { assistantBtn.removeAttribute('title'); assistantBtn.setAttribute('aria-label', t('sidebarAssistant')); assistantBtn.setAttribute('data-tooltip', t('sidebarAssistant')); - assistantBtn.setAttribute('data-tooltip-pos', 'right'); + assistantBtn.setAttribute('data-tooltip-pos', 'top'); assistantBtn.innerHTML = SIDEBAR_ICONS.ASSISTANT; assistantBtn.style.background = 'transparent'; assistantBtn.style.border = 'none'; diff --git a/web/styles.css b/web/styles.css index f299be7..29eddb8 100644 --- a/web/styles.css +++ b/web/styles.css @@ -2,20 +2,22 @@ ANOMALOUS UI GLOBAL DESIGN SYSTEM (Design Tokens & Semantic Colors) ========================================================================== */ :root { - --c-amber: #f59e0b; - --c-amber-glow: rgba(245, 158, 11, 0.45); - --c-cyan: #06b6d4; - --c-cyan-glow: rgba(6, 182, 212, 0.45); - --c-purple: #c084fc; - --c-purple-glow: rgba(192, 132, 252, 0.45); + --c-amber: #d97706; + --c-amber-glow: rgba(217, 119, 6, 0.3); + --c-cyan: #e11d48; + --c-cyan-glow: rgba(225, 29, 72, 0.35); + --c-purple: #8b5cf6; + --c-purple-glow: rgba(139, 92, 246, 0.3); --c-silver: #94a3b8; - --c-silver-glow: rgba(148, 163, 184, 0.35); - --c-blue: #38bdf8; - --c-blue-glow: rgba(56, 189, 248, 0.45); - --c-crimson: #e11d48; - --c-crimson-glow: rgba(225, 29, 72, 0.5); + --c-silver-glow: rgba(148, 163, 184, 0.25); + --c-blue: #dc143c; + --c-blue-glow: rgba(220, 20, 60, 0.35); + --c-crimson: #dc143c; + --c-crimson-glow: rgba(220, 20, 60, 0.45); + --c-wine: #722f37; + --c-wine-glow: rgba(114, 47, 55, 0.4); --c-emerald: #10b981; - --c-emerald-glow: rgba(16, 185, 129, 0.4); + --c-emerald-glow: rgba(16, 185, 129, 0.3); } /* Skeleton Shimmer Loading Placeholder */ @@ -175,7 +177,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro #anomalous-container { width: 90%; height: 90%; - background: radial-gradient(circle at 50% 0%, rgba(56, 189, 248, 0.035) 0%, transparent 65%), var(--bg-color, #1e1e24); + background: radial-gradient(circle at 50% 0%, rgba(220, 20, 60, 0.035) 0%, transparent 65%), var(--bg-color, #1e1e24); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 4px; clip-path: polygon(18px 0, 100% 0, 100% calc(100% - 18px), calc(100% - 18px) 100%, 0 100%, 0 18px); @@ -450,10 +452,10 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro } .anomalous-card-action-btn:hover { - background: rgba(56, 189, 248, 0.25); - border-color: #38bdf8; - color: #38bdf8; - box-shadow: 0 0 10px rgba(56, 189, 248, 0.4); + background: rgba(220, 20, 60, 0.25); + border-color: #dc143c; + color: #ff4d6d; + box-shadow: 0 0 8px 0px rgba(220, 20, 60, 0.5); } .anomalous-scan-progress { @@ -561,6 +563,15 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro flex-direction: column; flex: 1; overflow: hidden; + background-color: #0e0c13; + background-image: + radial-gradient(ellipse at 50% 12%, rgba(220, 20, 60, 0.04) 0%, transparent 65%), + linear-gradient(45deg, rgba(255, 255, 255, 0.012) 25%, transparent 25%), + linear-gradient(-45deg, rgba(255, 255, 255, 0.012) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(255, 255, 255, 0.012) 75%), + linear-gradient(-45deg, transparent 75%, rgba(255, 255, 255, 0.012) 75%); + background-size: 100% 100%, 36px 36px, 36px 36px, 36px 36px, 36px 36px; + background-position: 0 0, 0 0, 0 18px, 18px -18px, -18px 0px; } #anomalous-container { @@ -572,16 +583,16 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro width: 250px; margin: 8px 0 8px 8px; height: calc(100% - 16px); - background: rgba(22, 22, 28, 0.78); + background: rgba(20, 18, 24, 0.82); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); position: relative; - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid rgba(220, 20, 60, 0.15); border-radius: 12px; display: flex; flex-direction: column; transition: width 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), transform 0.3s ease; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.06); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.05); overflow: hidden; box-sizing: border-box; } @@ -594,15 +605,15 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro width: 1px; height: 100%; background: linear-gradient(180deg, - rgba(56, 189, 248, 0) 0%, - rgba(56, 189, 248, 0.35) 20%, - rgba(129, 140, 248, 0.55) 50%, - rgba(192, 132, 252, 0.3) 80%, - rgba(56, 189, 248, 0) 100% + rgba(220, 20, 60, 0) 0%, + rgba(220, 20, 60, 0.45) 20%, + rgba(139, 92, 246, 0.5) 50%, + rgba(220, 20, 60, 0.35) 80%, + rgba(220, 20, 60, 0) 100% ); pointer-events: none; z-index: 5; - box-shadow: 0 0 6px rgba(56, 189, 248, 0.25); + box-shadow: 0 0 6px rgba(220, 20, 60, 0.3); } .anomalous-sidebar-closed #anomalous-sidebar-wrapper { @@ -673,86 +684,86 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro color: #fff; } -/* 2px Neon Active Glow Indicator on Selected Menu */ +/* 2px Razor-thin Gothic Crimson Active Indicator on Selected Menu */ .anomalous-folder-item.active { - background: linear-gradient(90deg, rgba(56, 189, 248, 0.16) 0%, rgba(56, 189, 248, 0.02) 80%, transparent 100%); + background: linear-gradient(90deg, rgba(220, 20, 60, 0.18) 0%, rgba(220, 20, 60, 0.03) 80%, transparent 100%); color: #fff; font-weight: 600; - border-left: 2px solid var(--c-blue, #38bdf8); - box-shadow: inset 2px 0 10px rgba(56, 189, 248, 0.3); + border-left: 2px solid #dc143c; + box-shadow: inset 3px 0 8px rgba(220, 20, 60, 0.35); border-radius: 0 6px 6px 0; } .anomalous-folder-item.active .anomalous-folder-name { color: #fff !important; - text-shadow: 0 0 8px rgba(56, 189, 248, 0.5); + text-shadow: 0 0 6px rgba(220, 20, 60, 0.5); } .theme-abyssal-scarlet .anomalous-folder-item.active { - background: linear-gradient(90deg, rgba(220, 20, 60, 0.22) 0%, rgba(220, 20, 60, 0.04) 80%, transparent 100%); - border-left-color: var(--c-crimson, #e11d48); - box-shadow: inset 2px 0 12px rgba(220, 20, 60, 0.45); + background: linear-gradient(90deg, rgba(220, 20, 60, 0.24) 0%, rgba(220, 20, 60, 0.05) 80%, transparent 100%); + border-left-color: var(--c-crimson, #dc143c); + box-shadow: inset 3px 0 10px rgba(220, 20, 60, 0.5); } .theme-abyssal-scarlet .anomalous-folder-item.active .anomalous-folder-name { - text-shadow: 0 0 8px rgba(220, 20, 60, 0.6); + text-shadow: 0 0 8px rgba(220, 20, 60, 0.7); } -/* Semantic Icon Colors Injection */ +/* Semantic Icon Colors Injection (Gothic Vampire Tone) */ .anomalous-folder-item svg, #anomalous-sidebar h3 svg { - color: var(--c-amber) !important; + color: var(--c-amber, #d97706) !important; filter: drop-shadow(0 0 4px var(--c-amber-glow)); } #anomalous-scan-btn { - color: var(--c-blue) !important; + color: #cbd5e1 !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; } #anomalous-scan-btn:hover { - color: #7dd3fc !important; - filter: drop-shadow(0 0 8px var(--c-blue-glow)) !important; - background: rgba(56, 189, 248, 0.12) !important; + color: #ff4d6d !important; + filter: drop-shadow(0 0 5px rgba(220, 20, 60, 0.6)) !important; + background: rgba(220, 20, 60, 0.12) !important; } #anomalous-materials-btn { - color: var(--c-amber) !important; + color: var(--c-amber, #d97706) !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; } #anomalous-materials-btn:hover { - color: #fde047 !important; - filter: drop-shadow(0 0 8px var(--c-amber-glow)) !important; - background: rgba(245, 158, 11, 0.12) !important; + color: #fbbf24 !important; + filter: drop-shadow(0 0 5px rgba(217, 119, 6, 0.5)) !important; + background: rgba(217, 119, 6, 0.12) !important; } #anomalous-doctor-btn { - color: var(--c-cyan) !important; + color: var(--c-crimson, #dc143c) !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; } #anomalous-doctor-btn:hover { - color: #67e8f9 !important; - filter: drop-shadow(0 0 8px var(--c-cyan-glow)) !important; - background: rgba(6, 182, 212, 0.12) !important; + color: #ff4d6d !important; + filter: drop-shadow(0 0 5px rgba(220, 20, 60, 0.6)) !important; + background: rgba(220, 20, 60, 0.12) !important; } #anomalous-assistant-btn { - color: var(--c-purple) !important; + color: var(--c-purple, #8b5cf6) !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; } #anomalous-assistant-btn:hover { - color: #e9d5ff !important; - filter: drop-shadow(0 0 8px var(--c-purple-glow)) !important; - background: rgba(192, 132, 252, 0.12) !important; + color: #c4b5fd !important; + filter: drop-shadow(0 0 5px rgba(139, 92, 246, 0.6)) !important; + background: rgba(139, 92, 246, 0.14) !important; } #anomalous-global-settings-btn { - color: var(--c-silver) !important; + color: var(--c-silver, #94a3b8) !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; } #anomalous-global-settings-btn:hover { color: #f8fafc !important; - filter: drop-shadow(0 0 8px var(--c-silver-glow)) !important; - background: rgba(148, 163, 184, 0.15) !important; + filter: drop-shadow(0 0 5px rgba(148, 163, 184, 0.4)) !important; + background: rgba(255, 255, 255, 0.08) !important; } @@ -788,7 +799,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro } .anomalous-resizer:hover { - background: #007bff; + background: #dc143c; } .anomalous-split-right { @@ -907,7 +918,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro } .anomalous-compatible-item:hover { - border-color: #007bff; + border-color: #dc143c; background: var(--bg-color, #444); transform: translateY(-2px); } @@ -1051,7 +1062,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro } .anomalous-nb-item.active { - background: #007bff; + background: #dc143c; color: #fff; font-weight: bold; } @@ -1128,7 +1139,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro left: 0; right: 0; height: 2px; - background: linear-gradient(90deg, #38bdf8, #818cf8, #c084fc); + background: linear-gradient(90deg, #dc143c, #8b5cf6, #722f37); transform: scaleX(0); transform-origin: left; transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1); @@ -1262,9 +1273,9 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro .anomalous-nb-minicheck.selected, .anomalous-nb-minilora.selected { - border-color: #2dd4bf; - background: rgba(45, 212, 191, 0.15); - box-shadow: 0 0 12px rgba(45, 212, 191, 0.25); + border-color: #8b5cf6; + background: rgba(139, 92, 246, 0.15); + box-shadow: 0 0 12px rgba(139, 92, 246, 0.25); } .anomalous-nb-minicheck img, @@ -1294,7 +1305,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro .anomalous-nb-minicheck.selected .anomalous-nb-minicheck-name, .anomalous-nb-minilora.selected .anomalous-nb-minilora-name { - color: #2dd4bf; + color: #8b5cf6; font-weight: bold; } @@ -1358,7 +1369,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro .anomalous-nb-visual-tag.hover { background: #2a3140; - border-color: rgba(45, 212, 191, 0.5); + border-color: rgba(139, 92, 246, 0.5); color: #ffffff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } @@ -1387,7 +1398,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro .anomalous-nb-tag-edit { background: #111827; color: #f1f5f9; - border: 1px solid rgba(45, 212, 191, 0.6); + border: 1px solid rgba(139, 92, 246, 0.6); border-radius: 4px; padding: 6px; width: 100%; @@ -1442,7 +1453,7 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro .anomalous-gallery-card:hover { transform: translateY(-3px); - border-color: #007bff; + border-color: #dc143c; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); transition-delay: 0.08s; } @@ -1519,9 +1530,9 @@ html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBro .anomalous-gallery-details:hover { background: rgba(20, 29, 44, 0.94); - border-color: rgba(45, 212, 191, 0.55); - color: #5eead4; - box-shadow: 0 4px 16px rgba(45, 212, 191, 0.25); + border-color: rgba(139, 92, 246, 0.55); + color: #fca5a5; + box-shadow: 0 4px 16px rgba(139, 92, 246, 0.25); transform: translateY(-1px); } @@ -1648,7 +1659,7 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-nb-section-tab.active { - background: #007bff; + background: #dc143c; color: #fff; } @@ -2096,9 +2107,9 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-recipe-cover-choice.selected { - border-color: #00ffcc; - color: #00ffcc; - box-shadow: inset 0 0 0 1px rgba(0, 255, 204, 0.35); + border-color: #dc143c; + color: #dc143c; + box-shadow: inset 0 0 0 1px rgba(220, 20, 60, 0.35); } .anomalous-recipe-pin-picker { @@ -2147,9 +2158,9 @@ dialog.anomalous-gallery-viewer[open] { flex-direction: column; gap: 3px; padding: 8px; - border: 1px solid rgba(0, 255, 204, 0.3); + border: 1px solid rgba(220, 20, 60, 0.3); border-radius: 7px; - background: rgba(0, 255, 204, 0.05); + background: rgba(220, 20, 60, 0.05); } .anomalous-recipe-history-list { @@ -2229,9 +2240,9 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-recipe-detail-tab.active { - border-color: rgba(0, 255, 204, 0.45); - color: #00ffcc; - background: rgba(0, 255, 204, 0.08); + border-color: rgba(220, 20, 60, 0.45); + color: #dc143c; + background: rgba(220, 20, 60, 0.08); } .anomalous-recipe-detail-content { @@ -3512,7 +3523,7 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-recipe-version-row.current { - border-color: rgba(0, 255, 204, 0.45); + border-color: rgba(220, 20, 60, 0.45); } @media (max-width: 680px) { @@ -3940,9 +3951,9 @@ dialog.anomalous-gallery-viewer[open] { gap: 8px; margin-top: 10px; padding: 9px 10px; - border: 1px solid rgba(45, 212, 191, 0.16); + border: 1px solid rgba(139, 92, 246, 0.16); border-radius: 8px; - background: rgba(45, 212, 191, 0.045); + background: rgba(139, 92, 246, 0.045); } .anomalous-recipe-material-selection .anomalous-workbench-node-selection-count { @@ -3986,22 +3997,22 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-recipe-material-node-select:hover { - border-color: rgba(45, 212, 191, 0.6); - background-color: rgba(45, 212, 191, 0.08); + border-color: rgba(139, 92, 246, 0.6); + background-color: rgba(139, 92, 246, 0.08); } .anomalous-recipe-material-node-select:checked { - background-color: #2dd4bf; - border-color: #2dd4bf; + background-color: #8b5cf6; + border-color: #8b5cf6; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230b0f17' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E"); background-size: 11px 11px; background-position: center; background-repeat: no-repeat; - box-shadow: 0 0 8px rgba(45, 212, 191, 0.35); + box-shadow: 0 0 8px rgba(139, 92, 246, 0.35); } .anomalous-recipe-material-node-select:focus-visible { - outline: 2px solid #2dd4bf; + outline: 2px solid #8b5cf6; outline-offset: 2px; } @@ -4243,8 +4254,8 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-pill.is-active { - background: rgba(45, 212, 191, 0.18); - color: #2dd4bf; + background: rgba(139, 92, 246, 0.18); + color: #8b5cf6; font-weight: 600; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); } @@ -4288,7 +4299,7 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-search-input:focus { - border-color: rgba(45, 212, 191, 0.45); + border-color: rgba(139, 92, 246, 0.45); background: rgba(0, 0, 0, 0.4); } @@ -4345,7 +4356,7 @@ dialog.anomalous-gallery-viewer[open] { .anomalous-material-view-btn.is-active { background: rgba(255, 255, 255, 0.09); - color: #2dd4bf; + color: #8b5cf6; } .anomalous-material-topbar-btn { @@ -4370,22 +4381,22 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-studio-toggle { - border-color: rgba(45, 212, 191, 0.3); - color: #2dd4bf; - background: rgba(45, 212, 191, 0.08); + border-color: rgba(139, 92, 246, 0.3); + color: #8b5cf6; + background: rgba(139, 92, 246, 0.08); } .anomalous-material-studio-toggle:hover { - background: rgba(45, 212, 191, 0.16); - border-color: rgba(45, 212, 191, 0.5); - color: #5eead4; + background: rgba(139, 92, 246, 0.16); + border-color: rgba(139, 92, 246, 0.5); + color: #fca5a5; } .anomalous-material-studio-toggle.is-active { background: linear-gradient(135deg, rgba(20, 184, 166, 0.35), rgba(13, 148, 136, 0.45)); - border-color: rgba(45, 212, 191, 0.6); + border-color: rgba(139, 92, 246, 0.6); color: #ffffff; - box-shadow: 0 0 12px rgba(45, 212, 191, 0.25); + box-shadow: 0 0 12px rgba(139, 92, 246, 0.25); } /* 2. 主区域分屏容器 */ @@ -4503,12 +4514,12 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-card:hover { - border-color: rgba(45, 212, 191, 0.4); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35), 0 0 12px rgba(45, 212, 191, 0.12); + border-color: rgba(139, 92, 246, 0.4); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35), 0 0 12px rgba(139, 92, 246, 0.12); } .anomalous-material-card:focus-visible { - outline: 2px solid #2dd4bf; + outline: 2px solid #8b5cf6; outline-offset: 2px; } @@ -4776,8 +4787,8 @@ dialog.anomalous-gallery-viewer[open] { top: 8px; left: 8px; z-index: 5; - color: #2dd4bf; - border-color: rgba(45, 212, 191, 0.3); + color: #8b5cf6; + border-color: rgba(139, 92, 246, 0.3); opacity: 0; } @@ -4786,9 +4797,9 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-card-quick-load:hover { - background: rgba(45, 212, 191, 0.28); - border-color: rgba(45, 212, 191, 0.6); - color: #5eead4; + background: rgba(139, 92, 246, 0.28); + border-color: rgba(139, 92, 246, 0.6); + color: #fca5a5; } .anomalous-material-card-send-studio { @@ -4796,8 +4807,8 @@ dialog.anomalous-gallery-viewer[open] { top: 8px; left: 38px; z-index: 5; - color: #38bdf8; - border-color: rgba(56, 189, 248, 0.3); + color: #dc143c; + border-color: rgba(220, 20, 60, 0.3); opacity: 0; } @@ -4806,8 +4817,8 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-card-send-studio:hover { - background: rgba(56, 189, 248, 0.28); - border-color: rgba(56, 189, 248, 0.6); + background: rgba(220, 20, 60, 0.28); + border-color: rgba(220, 20, 60, 0.6); color: #7dd3fc; } @@ -4826,8 +4837,8 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-source-mark.is-current { - border-color: rgba(45, 212, 191, 0.25); - color: #5eead4; + border-color: rgba(139, 92, 246, 0.25); + color: #fca5a5; } .anomalous-material-source-mark.is-modified, @@ -4989,8 +5000,8 @@ dialog.anomalous-gallery-viewer[open] { .anomalous-library-detail-layout.is-parameter .anomalous-library-detail-image-stage { flex: 0 0 180px; - border-color: rgba(45, 212, 191, 0.18); - background: radial-gradient(circle at 50% 45%, rgba(45, 212, 191, 0.12), rgba(13, 15, 20, 0.9) 72%); + border-color: rgba(139, 92, 246, 0.18); + background: radial-gradient(circle at 50% 45%, rgba(139, 92, 246, 0.12), rgba(13, 15, 20, 0.9) 72%); cursor: default; } @@ -5264,8 +5275,8 @@ dialog.anomalous-gallery-viewer[open] { } .anomalous-material-film-thumb.is-selected { - border-color: rgba(56, 189, 248, 0.7); - box-shadow: 0 0 0 1px rgba(56, 189, 248, 0.35); + border-color: rgba(220, 20, 60, 0.7); + box-shadow: 0 0 0 1px rgba(220, 20, 60, 0.35); } .anomalous-material-film-thumb img, @@ -5866,7 +5877,7 @@ dialog.anomalous-gallery-viewer::backdrop { left: 0; right: 0; height: 2px; - background: linear-gradient(90deg, #38bdf8, #818cf8, #c084fc); + background: linear-gradient(90deg, #dc143c, #8b5cf6, #722f37); transform: scaleX(0); transform-origin: left; transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1); @@ -5879,9 +5890,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-recipe-btn-primary-action:hover { - border-color: #38bdf8; + border-color: #dc143c; color: #ffffff; - box-shadow: 0 0 14px rgba(56, 189, 248, 0.4), inset 0 0 14px rgba(99, 102, 241, 0.3); + box-shadow: 0 0 14px rgba(220, 20, 60, 0.4), inset 0 0 14px rgba(99, 102, 241, 0.3); transform: translateY(-1px); filter: brightness(1.15); } @@ -6021,7 +6032,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-recipe-readiness-banner.is-ready { background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.08)); border: 1px solid rgba(16, 185, 129, 0.35); - color: #a7f3d0; + color: #fda4af; } .anomalous-recipe-readiness-banner.is-warning { @@ -6873,7 +6884,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-material-prompt-card.is-positive { - border-left-color: #2dd4bf; + border-left-color: #8b5cf6; } .anomalous-material-prompt-card.is-negative { @@ -6938,8 +6949,8 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-material-model-card-item.is-primary { padding: 9px 11px; - border-color: rgba(56, 189, 248, 0.22); - background: rgba(56, 189, 248, 0.06); + border-color: rgba(220, 20, 60, 0.22); + background: rgba(220, 20, 60, 0.06); } .anomalous-material-model-peek-counts { @@ -7053,8 +7064,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-material-node-detail.is-selected { - border-color: rgba(45, 212, 191, 0.34); - background: rgba(45, 212, 191, 0.045); + border-color: rgba(139, 92, 246, 0.34); + background: rgba(139, 92, 246, 0.045); } .anomalous-material-node-select { @@ -7077,22 +7088,22 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-material-node-select:hover { - border-color: rgba(45, 212, 191, 0.6); - background-color: rgba(45, 212, 191, 0.08); + border-color: rgba(139, 92, 246, 0.6); + background-color: rgba(139, 92, 246, 0.08); } .anomalous-material-node-select:checked { - background-color: #2dd4bf; - border-color: #2dd4bf; + background-color: #8b5cf6; + border-color: #8b5cf6; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230b0f17' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E"); background-size: 11px 11px; background-position: center; background-repeat: no-repeat; - box-shadow: 0 0 8px rgba(45, 212, 191, 0.35); + box-shadow: 0 0 8px rgba(139, 92, 246, 0.35); } .anomalous-material-node-select:focus-visible { - outline: 2px solid #2dd4bf; + outline: 2px solid #8b5cf6; outline-offset: 2px; } @@ -7147,9 +7158,9 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-workbench-save-selected-btn { flex: 0 0 auto; padding: 5px 9px; - border: 1px solid rgba(45, 212, 191, 0.24); + border: 1px solid rgba(139, 92, 246, 0.24); border-radius: 6px; - background: rgba(45, 212, 191, 0.08); + background: rgba(139, 92, 246, 0.08); color: #8fe1d4; font-size: 0.66rem; cursor: pointer; @@ -7157,8 +7168,8 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-material-node-save:hover, .anomalous-workbench-save-selected-btn:hover:not(:disabled) { - border-color: rgba(45, 212, 191, 0.42); - background: rgba(45, 212, 191, 0.14); + border-color: rgba(139, 92, 246, 0.42); + background: rgba(139, 92, 246, 0.14); color: #d8fff8; } @@ -7376,8 +7387,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-material-model-card-item.is-active { - border-color: rgba(56, 189, 248, 0.45); - background: rgba(56, 189, 248, 0.08); + border-color: rgba(220, 20, 60, 0.45); + background: rgba(220, 20, 60, 0.08); } .anomalous-material-model-thumb { @@ -7450,9 +7461,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-tag-base { - background: rgba(56, 189, 248, 0.12); - color: #38bdf8; - border: 1px solid rgba(56, 189, 248, 0.25); + background: rgba(220, 20, 60, 0.12); + color: #dc143c; + border: 1px solid rgba(220, 20, 60, 0.25); } .anomalous-tag-lora { @@ -7468,9 +7479,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-tag-clip { - background: rgba(45, 212, 191, 0.12); - color: #2dd4bf; - border: 1px solid rgba(45, 212, 191, 0.25); + background: rgba(139, 92, 246, 0.12); + color: #8b5cf6; + border: 1px solid rgba(139, 92, 246, 0.25); } .anomalous-tag-other { @@ -7596,9 +7607,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-tag.is-ext { - background: rgba(56, 189, 248, 0.1); - border-color: rgba(56, 189, 248, 0.25); - color: #38bdf8; + background: rgba(220, 20, 60, 0.1); + border-color: rgba(220, 20, 60, 0.25); + color: #dc143c; font-weight: 700; } @@ -7673,9 +7684,9 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-workbench-tool-btn:hover, .anomalous-workbench-tool-btn.is-active { - background: rgba(45, 212, 191, 0.15); - border-color: rgba(45, 212, 191, 0.35); - color: #2dd4bf; + background: rgba(139, 92, 246, 0.15); + border-color: rgba(139, 92, 246, 0.35); + color: #8b5cf6; } .anomalous-workbench-close-btn { @@ -7770,11 +7781,11 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-workbench-float-nav:hover { opacity: 1; - background: rgba(45, 212, 191, 0.2); - border-color: rgba(45, 212, 191, 0.45); + background: rgba(139, 92, 246, 0.2); + border-color: rgba(139, 92, 246, 0.45); color: #fff; transform: translateY(-50%) scale(1.08); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6), 0 0 16px rgba(45, 212, 191, 0.25); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6), 0 0 16px rgba(139, 92, 246, 0.25); } /* Floating Zoom Bar */ @@ -7851,7 +7862,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-filmstrip::-webkit-scrollbar-thumb:hover { - background: rgba(45, 212, 191, 0.45); + background: rgba(139, 92, 246, 0.45); } .anomalous-workbench-filmstrip-track { @@ -7889,8 +7900,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-filmstrip-thumb.is-active { - border-color: #2dd4bf; - box-shadow: 0 0 16px rgba(45, 212, 191, 0.55), inset 0 0 0 1px #2dd4bf; + border-color: #8b5cf6; + box-shadow: 0 0 8px rgba(139, 92, 246, 0.35), inset 0 0 0 1px #8b5cf6; transform: scale(1.04); } @@ -7911,7 +7922,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-filmstrip-thumb.is-active .anomalous-workbench-thumb-index { - color: #2dd4bf; + color: #8b5cf6; background: rgba(13, 23, 27, 0.92); font-weight: 800; } @@ -7987,8 +7998,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-action-btn.is-primary { - background: linear-gradient(135deg, rgba(14, 165, 233, 0.22) 0%, rgba(45, 212, 191, 0.26) 100%); - border-color: rgba(45, 212, 191, 0.42); + background: linear-gradient(135deg, rgba(14, 165, 233, 0.22) 0%, rgba(139, 92, 246, 0.26) 100%); + border-color: rgba(139, 92, 246, 0.42); color: #e0f2fe; } @@ -7999,7 +8010,7 @@ dialog.anomalous-gallery-viewer::backdrop { left: 0; right: 0; height: 2px; - background: linear-gradient(90deg, #38bdf8, #2dd4bf, #a7f3d0); + background: linear-gradient(90deg, #dc143c, #8b5cf6, #fca5a5); transform: scaleX(0); transform-origin: left; transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1); @@ -8012,10 +8023,10 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-action-btn.is-primary:hover { - background: linear-gradient(135deg, rgba(14, 165, 233, 0.36) 0%, rgba(45, 212, 191, 0.4) 100%); - border-color: #2dd4bf; + background: linear-gradient(135deg, rgba(14, 165, 233, 0.36) 0%, rgba(139, 92, 246, 0.4) 100%); + border-color: #8b5cf6; color: #ffffff; - box-shadow: 0 4px 18px rgba(45, 212, 191, 0.28); + box-shadow: 0 4px 18px rgba(139, 92, 246, 0.28); transform: translateY(-1px); } @@ -8084,7 +8095,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-workbench-tab-btn.is-active { background: rgba(255, 255, 255, 0.09); - color: #2dd4bf; + color: #8b5cf6; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35); } @@ -8139,8 +8150,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-spec-card.is-res { - border-color: rgba(56, 189, 248, 0.25); - background: rgba(56, 189, 248, 0.035); + border-color: rgba(220, 20, 60, 0.25); + background: rgba(220, 20, 60, 0.035); } /* Card Click-to-copy interaction */ @@ -8158,9 +8169,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-spec-card.is-copied { - border-color: rgba(45, 212, 191, 0.6) !important; - background: rgba(45, 212, 191, 0.08) !important; - box-shadow: 0 0 14px rgba(45, 212, 191, 0.25) !important; + border-color: rgba(139, 92, 246, 0.6) !important; + background: rgba(139, 92, 246, 0.08) !important; + box-shadow: 0 0 14px rgba(139, 92, 246, 0.25) !important; } .anomalous-workbench-spec-header { @@ -8196,7 +8207,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-spec-card.is-copyable:focus-visible { - outline: 2px solid rgba(45, 212, 191, 0.55); + outline: 2px solid rgba(139, 92, 246, 0.55); outline-offset: 1px; } @@ -8207,7 +8218,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-spec-copy-icon.is-success { - stroke: #2dd4bf; + stroke: #8b5cf6; } .anomalous-workbench-spec-copy-icon.is-error { @@ -8217,7 +8228,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-workbench-copy-badge { font-size: 10px; font-weight: 600; - color: #2dd4bf; + color: #8b5cf6; animation: anomalous-fade-in 0.15s ease forwards; } @@ -8257,16 +8268,16 @@ dialog.anomalous-gallery-viewer::backdrop { gap: 12px; padding: 10px 14px; border-radius: 10px; - background: linear-gradient(135deg, rgba(45, 212, 191, 0.05) 0%, rgba(56, 189, 248, 0.03) 100%); - border: 1px solid rgba(45, 212, 191, 0.2); + background: linear-gradient(135deg, rgba(139, 92, 246, 0.05) 0%, rgba(220, 20, 60, 0.03) 100%); + border: 1px solid rgba(139, 92, 246, 0.2); margin-top: 4px; transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease; } .anomalous-workbench-generation-save:hover { - background: linear-gradient(135deg, rgba(45, 212, 191, 0.08) 0%, rgba(56, 189, 248, 0.05) 100%); - border-color: rgba(45, 212, 191, 0.35); - box-shadow: 0 3px 12px rgba(45, 212, 191, 0.08); + background: linear-gradient(135deg, rgba(139, 92, 246, 0.08) 0%, rgba(220, 20, 60, 0.05) 100%); + border-color: rgba(139, 92, 246, 0.35); + box-shadow: 0 3px 12px rgba(139, 92, 246, 0.08); } .anomalous-workbench-generation-info { @@ -8289,7 +8300,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-workbench-generation-icon { width: 13px; height: 13px; - stroke: #2dd4bf; + stroke: #8b5cf6; flex-shrink: 0; } @@ -8303,8 +8314,8 @@ dialog.anomalous-gallery-viewer::backdrop { flex-shrink: 0; padding: 6px 13px; border-radius: 7px; - background: linear-gradient(135deg, rgba(45, 212, 191, 0.22), rgba(56, 189, 248, 0.22)); - border: 1px solid rgba(45, 212, 191, 0.45); + background: linear-gradient(135deg, rgba(139, 92, 246, 0.22), rgba(220, 20, 60, 0.22)); + border: 1px solid rgba(139, 92, 246, 0.45); color: #e0f2fe; font-size: 0.74rem; font-weight: 600; @@ -8315,10 +8326,10 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-generation-btn:hover:not(:disabled) { - background: linear-gradient(135deg, rgba(45, 212, 191, 0.36), rgba(56, 189, 248, 0.36)); - border-color: #2dd4bf; + background: linear-gradient(135deg, rgba(139, 92, 246, 0.36), rgba(220, 20, 60, 0.36)); + border-color: #8b5cf6; color: #ffffff; - box-shadow: 0 3px 12px rgba(45, 212, 191, 0.28); + box-shadow: 0 3px 12px rgba(139, 92, 246, 0.28); transform: translateY(-1px); } @@ -8350,8 +8361,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-prompt-card.is-positive { - border-left-color: #2dd4bf; - background: linear-gradient(90deg, rgba(45, 212, 191, 0.04) 0%, rgba(255, 255, 255, 0.02) 40%); + border-left-color: #8b5cf6; + background: linear-gradient(90deg, rgba(139, 92, 246, 0.04) 0%, rgba(255, 255, 255, 0.02) 40%); } .anomalous-workbench-prompt-card.is-negative { @@ -8410,9 +8421,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-mini-action-btn.is-copied { - background: rgba(45, 212, 191, 0.2); - color: #2dd4bf; - border-color: #2dd4bf; + background: rgba(139, 92, 246, 0.2); + color: #8b5cf6; + border-color: #8b5cf6; } .anomalous-workbench-prompt-content { @@ -8539,9 +8550,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-model-tag.is-base { - background: rgba(56, 189, 248, 0.12); - color: #38bdf8; - border: 1px solid rgba(56, 189, 248, 0.25); + background: rgba(220, 20, 60, 0.12); + color: #dc143c; + border: 1px solid rgba(220, 20, 60, 0.25); } .anomalous-workbench-model-tag.is-lora { @@ -8551,9 +8562,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-model-tag.is-clip { - background: rgba(45, 212, 191, 0.12); - color: #2dd4bf; - border: 1px solid rgba(45, 212, 191, 0.25); + background: rgba(139, 92, 246, 0.12); + color: #8b5cf6; + border: 1px solid rgba(139, 92, 246, 0.25); } .anomalous-workbench-model-tag.is-vae { @@ -8592,9 +8603,9 @@ dialog.anomalous-gallery-viewer::backdrop { flex-wrap: wrap; gap: 7px; padding: 8px 10px; - border: 1px solid rgba(45, 212, 191, 0.14); + border: 1px solid rgba(139, 92, 246, 0.14); border-radius: 8px; - background: rgba(45, 212, 191, 0.035); + background: rgba(139, 92, 246, 0.035); } .anomalous-workbench-node-selection-count { @@ -8722,11 +8733,11 @@ dialog.anomalous-gallery-viewer::backdrop { padding: 4px 8px; font-size: 0.88rem; background: rgba(10, 12, 18, 0.92); - border: 1px solid rgba(45, 212, 191, 0.55); + border: 1px solid rgba(139, 92, 246, 0.55); border-radius: 6px; color: #ffffff; outline: none; - box-shadow: 0 0 10px rgba(45, 212, 191, 0.2); + box-shadow: 0 0 10px rgba(139, 92, 246, 0.2); } .anomalous-material-card-subinfo { @@ -8764,8 +8775,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-save-input-wrap input:focus { - border-color: #2dd4bf; - box-shadow: 0 0 0 2px rgba(45, 212, 191, 0.2); + border-color: #8b5cf6; + box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.2); } /* Loading & Error Box */ @@ -8785,8 +8796,8 @@ dialog.anomalous-gallery-viewer::backdrop { width: 28px; height: 28px; border-radius: 50%; - border: 3px solid rgba(45, 212, 191, 0.15); - border-top-color: #2dd4bf; + border: 3px solid rgba(139, 92, 246, 0.15); + border-top-color: #8b5cf6; animation: anomalous-spin 0.8s linear infinite; } @@ -8952,8 +8963,8 @@ dialog.anomalous-gallery-viewer::backdrop { padding: 7px 14px; border-radius: 20px; background: rgba(15, 23, 42, 0.9); - border: 1.5px solid rgba(45, 212, 191, 0.55); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.65), 0 0 16px rgba(45, 212, 191, 0.2); + border: 1.5px solid rgba(139, 92, 246, 0.55); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.65), 0 0 16px rgba(139, 92, 246, 0.2); color: #f1f5f9; font-size: 0.8rem; font-weight: 500; @@ -8963,10 +8974,10 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-material-drag-hint.is-target-valid { - border-color: #2dd4bf; + border-color: #8b5cf6; background: rgba(13, 37, 42, 0.94); - box-shadow: 0 12px 36px rgba(0, 0, 0, 0.75), 0 0 24px rgba(45, 212, 191, 0.45); - color: #5eead4; + box-shadow: 0 12px 36px rgba(0, 0, 0, 0.75), 0 0 24px rgba(139, 92, 246, 0.45); + color: #fca5a5; } /* 4. Prompt Composer Layout (Warm Paper Studio) */ @@ -9034,7 +9045,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-prompt-meta-strip input:focus { - border-color: #2dd4bf; + border-color: #8b5cf6; } .anomalous-prompt-name-input { @@ -9098,8 +9109,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-paper-note.is-positive .anomalous-paper-note-tape { - background: linear-gradient(90deg, #2dd4bf 0%, #0d9488 100%); - box-shadow: 0 0 10px rgba(45, 212, 191, 0.35); + background: linear-gradient(90deg, #8b5cf6 0%, #722f37 100%); + box-shadow: 0 0 10px rgba(139, 92, 246, 0.35); } .anomalous-paper-note.is-negative .anomalous-paper-note-tape { @@ -9126,7 +9137,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-paper-note.is-positive .anomalous-paper-note-title { - color: #5eead4; + color: #fca5a5; } .anomalous-paper-note.is-negative .anomalous-paper-note-title { @@ -9222,7 +9233,7 @@ dialog.anomalous-gallery-viewer::backdrop { padding: 10px 14px; border-radius: 10px; background: rgba(15, 23, 42, 0.6); - border: 1px solid rgba(45, 212, 191, 0.2); + border: 1px solid rgba(139, 92, 246, 0.2); } .anomalous-prompt-target-info { @@ -9295,7 +9306,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-drawer-search input:focus { - border-color: #2dd4bf; + border-color: #8b5cf6; } .anomalous-drawer-list { @@ -9320,7 +9331,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-drawer-item:hover { background: rgba(255, 255, 255, 0.06); - border-color: rgba(45, 212, 191, 0.3); + border-color: rgba(139, 92, 246, 0.3); } .anomalous-drawer-item-title { @@ -9362,9 +9373,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-drawer-item-btn:hover { - background: #2dd4bf; + background: #8b5cf6; color: #0f172a; - border-color: #2dd4bf; + border-color: #8b5cf6; font-weight: 600; } @@ -9416,11 +9427,11 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-mixer-tab-btn.is-active { - background: linear-gradient(135deg, rgba(56, 189, 248, 0.2), rgba(45, 212, 191, 0.25)); - color: #38bdf8; + background: linear-gradient(135deg, rgba(220, 20, 60, 0.2), rgba(139, 92, 246, 0.25)); + color: #dc143c; font-weight: 600; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); - border: 1px solid rgba(56, 189, 248, 0.35); + border: 1px solid rgba(220, 20, 60, 0.35); } .anomalous-mixer-actions { @@ -9440,7 +9451,7 @@ dialog.anomalous-gallery-viewer::backdrop { padding: 5px 12px; border-radius: 6px; border: 1px solid rgba(192, 132, 252, 0.5); - background: linear-gradient(135deg, rgba(168, 85, 247, 0.25) 0%, rgba(56, 189, 248, 0.25) 100%); + background: linear-gradient(135deg, rgba(168, 85, 247, 0.25) 0%, rgba(220, 20, 60, 0.25) 100%); color: #e9d5ff; font-size: 0.78rem; font-weight: 600; @@ -9453,7 +9464,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-mixer-smart-sort-btn:hover { - background: linear-gradient(135deg, rgba(168, 85, 247, 0.4) 0%, rgba(56, 189, 248, 0.4) 100%); + background: linear-gradient(135deg, rgba(168, 85, 247, 0.4) 0%, rgba(220, 20, 60, 0.4) 100%); border-color: #c084fc; color: #ffffff; box-shadow: 0 0 14px rgba(192, 132, 252, 0.45); @@ -9513,7 +9524,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-mixer-block.is-role-positive { - border-left: 3px solid #38bdf8; + border-left: 3px solid #dc143c; } .anomalous-mixer-block.is-role-negative { @@ -9546,8 +9557,8 @@ dialog.anomalous-gallery-viewer::backdrop { right: 0; height: 4px; border-radius: 2px; - background: #38bdf8; - box-shadow: 0 0 8px #38bdf8; + background: #dc143c; + box-shadow: 0 0 8px #dc143c; z-index: 10; } @@ -9559,8 +9570,8 @@ dialog.anomalous-gallery-viewer::backdrop { right: 0; height: 4px; border-radius: 2px; - background: #38bdf8; - box-shadow: 0 0 8px #38bdf8; + background: #dc143c; + box-shadow: 0 0 8px #dc143c; z-index: 10; } @@ -9592,7 +9603,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-mixer-drag-handle:hover { - color: #38bdf8; + color: #dc143c; } .anomalous-mixer-drag-handle:active { @@ -9608,7 +9619,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-mixer-block-toggle input[type="checkbox"] { cursor: pointer; - accent-color: #2dd4bf; + accent-color: #8b5cf6; width: 14px; height: 14px; margin: 0; @@ -9714,7 +9725,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-mixer-block-textarea:focus { background: rgba(0, 0, 0, 0.38); - box-shadow: inset 0 0 0 1px rgba(56, 189, 248, 0.3); + box-shadow: inset 0 0 0 1px rgba(220, 20, 60, 0.3); min-height: 52px; height: 52px; } @@ -9728,7 +9739,7 @@ dialog.anomalous-gallery-viewer::backdrop { background: rgba(22, 28, 42, 0.88); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); - border: 1px solid rgba(56, 189, 248, 0.28); + border: 1px solid rgba(220, 20, 60, 0.28); box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.45); padding: 8px 10px; display: flex; @@ -9741,8 +9752,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-mixer-deck-output:hover { - border-color: #38bdf8; - box-shadow: 0 0 16px rgba(56, 189, 248, 0.25); + border-color: #dc143c; + box-shadow: 0 0 8px rgba(220, 20, 60, 0.25); } .anomalous-mixer-deck-output:active { @@ -9806,7 +9817,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-mixer-drag-hint strong { - color: #38bdf8; + color: #dc143c; } .anomalous-mixer-output-actions { @@ -9823,7 +9834,7 @@ dialog.anomalous-gallery-viewer::backdrop { padding: 10px 16px; border-radius: 8px; background: rgba(15, 23, 42, 0.94); - border: 1px solid rgba(45, 212, 191, 0.5); + border: 1px solid rgba(139, 92, 246, 0.5); color: #f1f5f9; font-size: 0.84rem; font-weight: 500; @@ -9967,7 +9978,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-search-input:focus { - border-color: #38bdf8; + border-color: #dc143c; background: rgba(0, 0, 0, 0.45); } @@ -9997,15 +10008,15 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-workbench-pill.is-active { - background-color: rgba(56, 189, 248, 0.22); + background-color: rgba(220, 20, 60, 0.22); background-image: url('assets/pill_active_glow.webp'); background-size: cover; background-position: center; - border-color: rgba(56, 189, 248, 0.55); + border-color: rgba(220, 20, 60, 0.55); color: #f0fdf4; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7); font-weight: 600; - box-shadow: 0 1px 6px rgba(56, 189, 248, 0.25); + box-shadow: 0 1px 6px rgba(220, 20, 60, 0.25); } /* Inline New Card Creation Form */ @@ -10016,7 +10027,7 @@ dialog.anomalous-gallery-viewer::backdrop { padding: 10px 12px; border-radius: 8px; background: rgba(15, 23, 42, 0.85); - border: 1px solid rgba(45, 212, 191, 0.35); + border: 1px solid rgba(139, 92, 246, 0.35); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); animation: fadeInSlideDown 0.2s ease; flex-shrink: 0; @@ -10030,7 +10041,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-form-title { font-size: 0.82rem; font-weight: 600; - color: #2dd4bf; + color: #8b5cf6; } .anomalous-form-input, @@ -10091,7 +10102,7 @@ dialog.anomalous-gallery-viewer::backdrop { -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.75) 75%, rgba(0, 0, 0, 0.3) 100%); mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.75) 75%, rgba(0, 0, 0, 0.3) 100%); border-radius: 10px; - border: 1px dashed rgba(56, 189, 248, 0.25); + border: 1px dashed rgba(220, 20, 60, 0.25); overflow: hidden; isolation: isolate; } @@ -10130,7 +10141,7 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-source-prompt-card.is-cat-base { - border-left-color: #38bdf8; + border-left-color: #dc143c; } .anomalous-source-prompt-card.is-cat-style { @@ -10235,26 +10246,26 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-source-action-btn.is-dock { - background: rgba(45, 212, 191, 0.15); - border-color: rgba(45, 212, 191, 0.4); - color: #2dd4bf; + background: rgba(139, 92, 246, 0.15); + border-color: rgba(139, 92, 246, 0.4); + color: #8b5cf6; font-weight: 600; } .anomalous-source-action-btn.is-dock:hover { - background: #2dd4bf; + background: #8b5cf6; color: #0f172a; } .anomalous-source-action-btn.is-save-lib { - background: rgba(56, 189, 248, 0.15); - border-color: rgba(56, 189, 248, 0.4); - color: #38bdf8; + background: rgba(220, 20, 60, 0.15); + border-color: rgba(220, 20, 60, 0.4); + color: #dc143c; font-weight: 500; } .anomalous-source-action-btn.is-save-lib:hover { - background: #38bdf8; + background: #dc143c; color: #0f172a; } @@ -10284,7 +10295,7 @@ dialog.anomalous-gallery-viewer::backdrop { .anomalous-role-label input[type="radio"] { cursor: pointer; - accent-color: #38bdf8; + accent-color: #dc143c; margin: 0; } @@ -10318,22 +10329,22 @@ dialog.anomalous-gallery-viewer::backdrop { @keyframes anomalousTrackPulse { 0% { - border-color: rgba(45, 212, 191, 0.45); - box-shadow: inset 0 0 14px rgba(45, 212, 191, 0.12), 0 0 8px rgba(45, 212, 191, 0.15); + border-color: rgba(139, 92, 246, 0.45); + box-shadow: inset 0 0 14px rgba(139, 92, 246, 0.12), 0 0 8px rgba(139, 92, 246, 0.15); } 50% { - border-color: rgba(45, 212, 191, 0.95); - box-shadow: inset 0 0 26px rgba(45, 212, 191, 0.28), 0 0 16px rgba(45, 212, 191, 0.38); + border-color: rgba(139, 92, 246, 0.95); + box-shadow: inset 0 0 26px rgba(139, 92, 246, 0.28), 0 0 16px rgba(139, 92, 246, 0.38); } 100% { - border-color: rgba(45, 212, 191, 0.45); - box-shadow: inset 0 0 14px rgba(45, 212, 191, 0.12), 0 0 8px rgba(45, 212, 191, 0.15); + border-color: rgba(139, 92, 246, 0.45); + box-shadow: inset 0 0 14px rgba(139, 92, 246, 0.12), 0 0 8px rgba(139, 92, 246, 0.15); } } .anomalous-prompt-workbench:has(.is-dragging-source) .anomalous-assembly-track, .is-source-dragging .anomalous-assembly-track { - border: 2px dashed #2dd4bf !important; + border: 2px dashed #8b5cf6 !important; animation: anomalousTrackPulse 1.8s ease-in-out infinite; } @@ -10353,9 +10364,9 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-assembly-dropzone.is-drag-over { - border-color: #38bdf8; - background: rgba(56, 189, 248, 0.08); - box-shadow: inset 0 0 24px rgba(56, 189, 248, 0.2); + border-color: #dc143c; + background: rgba(220, 20, 60, 0.08); + box-shadow: inset 0 0 24px rgba(220, 20, 60, 0.2); transform: scale(0.995); } @@ -10394,12 +10405,12 @@ dialog.anomalous-gallery-viewer::backdrop { background-image: url('assets/btn_cyber_grid.webp') !important; background-size: cover !important; background-position: center !important; - border: 1px solid rgba(56, 189, 248, 0.55) !important; + border: 1px solid rgba(220, 20, 60, 0.55) !important; border-radius: 3px 10px 3px 10px !important; color: #ffffff !important; font-weight: 600 !important; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85) !important; - box-shadow: 0 2px 8px rgba(13, 148, 136, 0.3), inset 0 0 10px rgba(56, 189, 248, 0.2) !important; + box-shadow: 0 2px 8px rgba(13, 148, 136, 0.3), inset 0 0 10px rgba(220, 20, 60, 0.2) !important; transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1) !important; overflow: hidden; } @@ -10429,7 +10440,7 @@ dialog.anomalous-gallery-viewer::backdrop { left: 0; right: 0; height: 2px; - background: linear-gradient(90deg, #38bdf8, #2dd4bf, #a7f3d0); + background: linear-gradient(90deg, #dc143c, #8b5cf6, #fca5a5); transform: scaleX(0); transform-origin: left; transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1); @@ -10442,8 +10453,8 @@ dialog.anomalous-gallery-viewer::backdrop { } .anomalous-btn-extract-node:hover { - border-color: #38bdf8 !important; - box-shadow: 0 0 16px rgba(56, 189, 248, 0.55), inset 0 0 14px rgba(45, 212, 191, 0.35) !important; + border-color: #dc143c !important; + box-shadow: 0 0 16px rgba(220, 20, 60, 0.55), inset 0 0 14px rgba(139, 92, 246, 0.35) !important; transform: translateY(-1px); filter: brightness(1.18); } @@ -10675,7 +10686,7 @@ html.theme-abyssal-scarlet, border-radius: 2px 10px 2px 10px !important; color: #ffe4e6 !important; text-shadow: 0 0 6px rgba(220, 20, 60, 0.8) !important; - box-shadow: 0 4px 16px rgba(220, 20, 60, 0.25) !important; + box-shadow: 0 4px 10px rgba(220, 20, 60, 0.25) !important; } .theme-abyssal-scarlet .anomalous-workbench-action-btn.is-primary:hover { @@ -10712,7 +10723,7 @@ html.theme-abyssal-scarlet, .theme-abyssal-scarlet .anomalous-mixer-smart-sort-btn:hover { border-color: #ff3366 !important; - box-shadow: 0 0 16px rgba(220, 20, 60, 0.65) !important; + box-shadow: 0 0 8px rgba(220, 20, 60, 0.45) !important; transform: translateY(-1px); filter: brightness(1.15); } @@ -10798,18 +10809,18 @@ html.theme-abyssal-scarlet dialog.anomalous-gallery-viewer { } #anomalous-scan-btn:hover svg { - filter: drop-shadow(0 0 4px rgba(56, 189, 248, 0.6)); + filter: drop-shadow(0 0 4px rgba(220, 20, 60, 0.6)); } #anomalous-scan-btn.anomalous-radar-spinning { - color: #38bdf8 !important; - background: rgba(56, 189, 248, 0.12) !important; - border: 1px solid rgba(56, 189, 248, 0.35) !important; + color: #dc143c !important; + background: rgba(220, 20, 60, 0.12) !important; + border: 1px solid rgba(220, 20, 60, 0.35) !important; } #anomalous-scan-btn.anomalous-radar-spinning svg { animation: anomalous-radar-spin 1.4s linear infinite !important; - filter: drop-shadow(0 0 6px rgba(56, 189, 248, 0.75)) !important; + filter: drop-shadow(0 0 6px rgba(220, 20, 60, 0.75)) !important; } @keyframes anomalous-radar-spin { @@ -10888,9 +10899,9 @@ button:hover .anomalous-btn-icon { } #anomalous-global-settings-btn:hover { - color: #38bdf8 !important; - background: rgba(56, 189, 248, 0.12) !important; - border: 1px solid rgba(56, 189, 248, 0.3) !important; + color: #dc143c !important; + background: rgba(220, 20, 60, 0.12) !important; + border: 1px solid rgba(220, 20, 60, 0.3) !important; } /* Abyssal Scarlet Mode overrides for Task 8 icon buttons */ @@ -11104,3 +11115,98 @@ button:hover .anomalous-btn-icon { + + +/* ========================================================================== + GOTHIC VAMPIRE MASTER HEADER & GALLERY LATTICE ENHANCEMENTS + ========================================================================== */ + +#anomalous-header { + height: 60px; + background: linear-gradient(180deg, rgba(26, 16, 24, 0.96) 0%, rgba(14, 8, 16, 0.98) 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + padding: 0 20px !important; + border-bottom: 1px solid rgba(220, 20, 60, 0.28) !important; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.65), inset 0 -1px 0 rgba(220, 20, 60, 0.15) !important; + cursor: move; + position: relative; + z-index: 10; +} + +.anomalous-header-group { + display: flex; + align-items: center; + gap: 12px; +} + +.anomalous-header-left { + display: flex; + align-items: center; + gap: 12px; + flex: 0 0 auto; +} + +.anomalous-header-center { + display: flex !important; + align-items: center !important; + justify-content: center !important; + gap: 12px !important; + flex: 1 1 auto !important; + margin: 0 auto !important; +} + +.anomalous-header-right { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 12px; + flex: 0 0 auto; +} + +#anomalous-header button { + margin-right: 0; + padding: 9px 18px; + background: rgba(30, 18, 28, 0.6); + color: #e2e8f0; + border: 1px solid rgba(220, 20, 60, 0.2); + border-radius: 6px; + cursor: pointer; + white-space: nowrap; + display: flex; + align-items: center; + gap: 8px; + font-size: 1.02em; + font-weight: 500; + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); +} + +#anomalous-header button:hover { + background: rgba(220, 20, 60, 0.15) !important; + border-color: rgba(220, 20, 60, 0.45) !important; + color: #fff !important; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(220, 20, 60, 0.25) !important; +} + +#anomalous-header button.active { + background: linear-gradient(135deg, rgba(220, 20, 60, 0.3) 0%, rgba(114, 47, 55, 0.4) 100%) !important; + border-color: rgba(220, 20, 60, 0.65) !important; + color: #fff !important; + box-shadow: 0 0 10px rgba(220, 20, 60, 0.3) !important; +} + +#anomalous-gallery-panel, +.anomalous-gallery-grid, +.anomalous-gallery-container { + background-color: #0c0812 !important; + background-image: + radial-gradient(ellipse at 50% 15%, rgba(220, 20, 60, 0.08) 0%, transparent 65%), + linear-gradient(135deg, rgba(255, 255, 255, 0.015) 25%, transparent 25%), + linear-gradient(225deg, rgba(255, 255, 255, 0.015) 25%, transparent 25%), + linear-gradient(315deg, rgba(255, 255, 255, 0.015) 25%, transparent 25%), + linear-gradient(45deg, rgba(255, 255, 255, 0.015) 25%, transparent 25%) !important; + background-size: 100% 100%, 28px 28px, 28px 28px, 28px 28px, 28px 28px !important; + background-position: 0 0, 0 0, 14px 0, 14px -14px, 0 14px !important; +}