From fc40dead3dd367e29d795d175ec7c7a01117c1fe Mon Sep 17 00:00:00 2001
From: DemonGatanjieu <1027066412@qq.com>
Date: Thu, 10 Sep 2026 19:06:44 +0800
Subject: [PATCH] fix(ui): visual correction audit - return to gothic vampire
aesthetics, sweep emojis, center header, and fix tooltip overlap
---
ARCHITECTURE.md | 7 +
web/main.js | 2 +-
web/modules/locales.js | 148 +++----
web/modules/ui_detail.js | 10 +-
web/modules/ui_doctor.js | 37 +-
web/modules/ui_gallery.js | 4 +-
web/modules/ui_gallery_detail.js | 20 +-
web/modules/ui_grid.js | 6 +-
web/modules/ui_materials.js | 10 +-
web/modules/ui_notebooks.js | 7 +-
web/modules/ui_prompt_composer.js | 72 ++--
web/modules/ui_recipe_detail.js | 66 +--
web/modules/ui_recipes.js | 58 ++-
web/modules/ui_sidebar.js | 67 +--
web/styles.css | 660 +++++++++++++++++-------------
15 files changed, 679 insertions(+), 495 deletions(-)
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;
+}