diff --git a/AGENTS.md b/AGENTS.md index 7b7f70e..9d49092 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,22 @@ UX 请求默认允许调整布局、交互和文案,不代表允许改变文件存储位置、接口协议、工作流语义、模型识别依据或用户数据。 +### 小型 UX 修改快速流程 + +仅调整文案、图标、颜色、间距或局部交互时:查看相关差异 → 定位负责规则 → 最小修改 → 验证受影响界面 → 本地提交。 +同一任务已读过且未变化的规范不反复通读;只读相关专题,不做全仓审查,不顺手重构。 +按第 7 节选择相关检查,不默认运行全部 JS/Python 测试。检查通过后,除非又改了代码或出现新问题,不重复运行。 +没有改变职责、接口或持久化契约时,不更新架构文档或新增经验记录。提交说明由 AI 根据实际差异编写,无需用户代写。 + +### 任务中断后的接续与快照 + +- 开工先检查状态和差异。工作区干净时直接继续,不制造空提交。 +- 上次因额度或其他原因中断,存在属于该任务的改动时,先确认文件范围,再选择性暂存这些文件;不得使用无差别的 `git add .` 收入用户数据、凭据、临时文件或无关工作。 +- 已完成且有验证记录的改动可补正式提交;尚未验证或未完成的改动可先保存 `chore(checkpoint): <实际改动摘要>` 恢复快照,提交正文明确“未完成/验证待完成”、已做检查和下一步。快照是恢复点,不代表验收通过。 +- 后续修改和验证另作正式提交,不自动 amend、reset 或改写快照历史。若无法区分遗留文件归属,保留原状并说明,不猜测打包。 +- 长任务按可独立验证的完整阶段提交,避免把所有 Git 收尾留到最后。测试范围内检查完成后及时提交,不为额外润色拖延快照。 +- 上述快照和正式提交均只保存在本地,未经明确授权不推送。 + ## 2. 模块职责与规模 - `web/main.js` 负责注册和接线;`ui_sidebar.js` 的 `createDOM` 负责外壳装配。新工具的完整实现放在自己的模块,不继续塞进这两个入口。 diff --git a/CHANGELOG.md b/CHANGELOG.md index e03e7b2..cf10d80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Navigation and update guide - Sidebar icons reveal compact text labels on hover or keyboard focus while keeping their existing button size and actions. Detailed hints explain each tool; scan-state and language changes retain labels. Unclipped tooltips ensure explanations are fully readable. -- Added a non-intrusive update guide system: instead of an automatic popup on startup, an amber breathing badge (`!` + '新指引' / 'Guide') appears in the top-right header when a new guide version is unacknowledged. Clicking opens the polished obsidian-glass guide modal with step indicator, and dismissing smoothly hides the badge and stores persistent acknowledgment. The guide can also be replayed from the Help panel footer; see `docs/architecture/update-guide.md`. +- Update guide opens only from the persistent top-right exclamation button or Help. Dismissing never removes the entry. Sidebar labels slide upward on hover/focus; duplicate source-panel and obsolete notice styles were removed. See `docs/architecture/update-guide.md`. ### Model Source Hub (模型来源中控中心) - Added a unified Model Source Hub under Toolbox for dual-scope (current workflow and full library) model source URL inspection, one-click browser navigation to Civitai/HuggingFace/Liblib/ModelScope, custom source editing, workflow metadata synchronization (`extra.anomalous_model_sources`), and instant canvas `Note` node generation for non-intrusive workflow documentation. diff --git a/docs/architecture/frontend.md b/docs/architecture/frontend.md index c96f64a..c99aa70 100644 --- a/docs/architecture/frontend.md +++ b/docs/architecture/frontend.md @@ -25,8 +25,8 @@ Major UI panels live in `web/modules/ui_*.js`. Shared browser state remains on the `AnomalousBrowser` instance. Pure parsing, normalization, comparison, and transaction helpers remain in focused modules rather than acquiring DOM state. -Update-guide content and UI lifecycle are separate modules; the browser show/close -methods only wire them in. Help provides an explicit replay entry. Read +Update-guide content and UI lifecycle are separate modules. The header and Help +open it only on explicit user action; browser close disposes it. Read [update-guide.md](update-guide.md) before changing guide IDs, steps, persistence, or sidebar icon/label transitions. Do not embed guide logic in the main/sidebar bootstrap or reuse its content version as a feature flag. diff --git a/docs/architecture/update-guide.md b/docs/architecture/update-guide.md index 0ace2eb..6fc5ef4 100644 --- a/docs/architecture/update-guide.md +++ b/docs/architecture/update-guide.md @@ -12,27 +12,27 @@ | `web/modules/ui_update_guide.js` | 显示、翻页、关闭、焦点、版本记录;一般内容更新不要修改 | | `web/modules/sidebar_actions.js` | 六个底部按钮的短标签、完整名称和用途提示,不改变点击动作 | | `web/styles.css` | 引导布局和底部图标/文字切换的现有规则 | -| `web/main.js` / `ui_sidebar.js` | 仅在浏览器打开/关闭及帮助页重看时接线 | +| `web/main.js` / `ui_sidebar.js` | 父窗口关闭清理,以及上方按钮和帮助页的手动入口 | ## 后续 AI 更新步骤 1. 先确认功能已实现及真实入口。不要在引导里承诺未上线、暂停或验证失败的功能。 2. 在 `CURRENT_UPDATE_GUIDE.steps` 编写 1–5 个步骤,每项包含唯一 `id`、短 `icon`、`titleKey`、`bodyKey`。 3. 在 `locales.js` 补齐这些键的中文和英文。标题说明变化,正文说明入口和用户该怎么用。禁止 HTML、可执行回调、请求及文件写入。 -4. **只有需要所有用户重新看到时才换引导 `id`**,例如 `2026-10-library-navigation`。修改错字、翻译、样式时保持 ID。不使用时间戳、随机数、Git 提交号或普通版本号自动生成 ID。 +4. **只有引导内容属于新一轮功能变化时才换 `id`**,例如 `2026-10-library-navigation`。修改错字、翻译、样式时保持 ID。不使用时间戳、随机数、Git 提交号或普通版本号自动生成 ID;换 ID 也不能自动弹窗。 5. 配置校验会拒绝重复步骤、空步骤及缺失双语文案。配置错误只跳过引导,不应阻止插件打开;必须修正错误后再交付。 -6. 运行下方测试,再在浏览器验证首次打开、跳过后重开、帮助页重看、Esc 和中英文长文案。 +6. 运行下方相关测试,再在浏览器验证打开插件不弹窗、上方入口、跳过后手动重开、帮助页重看、Esc 和长文案。 7. 修改现有样式规则,不在文件末尾追加覆盖包。没有明确产品决策,不增加强制阅读、自动跳转、网络请求或额外追踪。 ## 生命周期与记录 -- 未确认的更新引导不再强制自动弹窗打扰用户,而是在面板右上角显示克制的暗色感叹号徽标(`#anomalous-update-notice-btn`);用户主动点击时才唤起引导。 +- 面板右上角常驻感叹号按钮(`#anomalous-update-notice-btn`),提供完整的用途提示和 accessible name。只有用户点击该按钮或帮助页入口才打开引导;初始化、打开插件、版本变化都不能触发弹窗。 - 使用原生 `dialog` 的顶层显示和焦点约束,关闭时尽可能恢复原焦点。 -- 完成、跳过、×、Esc、点击空白关闭会记录已读,并触发感叹号徽标平滑淡出消除;关闭父浏览器仅清理,不将未读引导标记为已读。 +- 完成、跳过、×、Esc、点击空白关闭会记录已读,上方入口仍然保留;关闭父浏览器仅清理,不将未读引导标记为已读。入口不需要全局事件或延时隐藏。 - 持久化键为 `anomalous_update_guide_seen:`,值为 `1`。只保存本地已读状态,不发送到服务端。 -- 存储不可用时,当前页面会话仍记住关闭状态;刷新页面后可能再次显示。 +- 存储不可用时,当前页面会话仍记住关闭状态;刷新后仍必须手动点击才能显示。 - 同一时刻只有一个引导。重复调用不叠加弹窗;所有关闭路径通过 `closeUpdateGuide` 释放资源。 -- 帮助页的“查看更新引导”使用 `force: true`,允许重看已确认版本。 +- 上方按钮和帮助页的“查看更新引导”均使用 `force: true`,允许重看已确认版本。 - 引导不自动点击对应功能,也不修改画布、模型、素材、导入导出限制。 ## 底部按钮约束 @@ -48,9 +48,8 @@ ```powershell node --experimental-vm-modules tests/update_guide.mjs -node --experimental-vm-modules tests/prompt_ui_lifecycle.mjs -node --experimental-vm-modules tests/export_availability.mjs ``` `update_guide.mjs` 加载真实模块,检查双语配置、版本记忆、重看、翻页、关闭清理、 父窗口关闭、存储失败及扫描图标替换后的标签。它不能代替实际浏览器的布局与焦点验证。 +只有同时修改工坊生命周期或导入导出行为时,才追加对应测试;纯引导样式不运行无关测试。 diff --git a/web/main.js b/web/main.js index afc612e..0571d8d 100644 --- a/web/main.js +++ b/web/main.js @@ -7,7 +7,7 @@ import { showRecipes, refreshRecipes, renderRecipeList, handleSaveRecipe } from import { showMaterials, refreshMaterials, openSavedMaterial, openMaterialLibrary } from './modules/ui_materials.js'; import { openPromptStudio } from './modules/ui_prompt_composer.js'; import { openPromptTranslator } from './modules/ui_prompt_translator.js'; -import { showUpdateGuide, closeUpdateGuide } from './modules/ui_update_guide.js'; +import { closeUpdateGuide } from './modules/ui_update_guide.js'; import { showImageWorkbench } from './modules/ui_gallery_detail.js'; import { initDoctorPanel, diagnoseNode, renderGlobalDashboard, initAssistantPanel, renderAssistantModelCard, _loadAssistantHistory, _openGalleryReplacer, openLoraInsertionPicker, runGlobalDoctorScan } from './modules/ui_doctor.js'; import { app } from "../../scripts/app.js"; @@ -283,7 +283,6 @@ class AnomalousBrowser { } else { this.loadModels(); } - this.refreshUpdateNotice?.(); } close() { diff --git a/web/modules/locales.js b/web/modules/locales.js index 75b0bcc..17200b5 100644 --- a/web/modules/locales.js +++ b/web/modules/locales.js @@ -19,8 +19,7 @@ export const i18n = { updateGuideNext: "下一步", updateGuideDone: "知道了", updateGuideProgress: "第 {current} / {total} 步", - updateGuideNoticeLabel: "新指引", - updateGuideNoticeTooltip: "新版本功能与界面改版指引 (点击查看)", + updateGuideNoticeTooltip: "查看更新引导", updateGuideNavigationTitle: "悬停,看懂每个入口", updateGuideNavigationBody: "左下角的按钮保持原来的大小。鼠标悬停或用键盘选中时,图标会切换成文字;稍作停留可以查看完整用途。依次为工具箱、扫描、模型医生、节点助手、素材库和设置。", updateGuideTransferTitle: "导入导出,统一到工具箱", @@ -1172,8 +1171,7 @@ export const i18n = { updateGuideNext: "Next", updateGuideDone: "Got it", updateGuideProgress: "Step {current} of {total}", - updateGuideNoticeLabel: "Guide", - updateGuideNoticeTooltip: "New features & interface guide (Click to view)", + updateGuideNoticeTooltip: "View update guide", updateGuideNavigationTitle: "Hover to identify each action", updateGuideNavigationBody: "The lower-left buttons keep their compact size. Hover or focus with the keyboard to replace an icon with a short label; pause for its full description. The actions are Toolbox, Scan, Model Doctor, Node Assistant, Material Library and Settings.", updateGuideTransferTitle: "Workflow sharing lives in Toolbox", diff --git a/web/modules/ui_sidebar.js b/web/modules/ui_sidebar.js index 3a620ab..9defe38 100644 --- a/web/modules/ui_sidebar.js +++ b/web/modules/ui_sidebar.js @@ -8,8 +8,7 @@ import { translate } from './locales.js'; import { escapeHtml } from './safe_dom.js'; import { updateScanProgress, finishScanProgress, failScanProgress } from './scan_progress.js'; import { openModelSourcesModal } from './ui_model_sources.js'; -import { showUpdateGuide, hasAcknowledged } from './ui_update_guide.js'; -import { CURRENT_UPDATE_GUIDE } from './update_guide_data.js'; +import { showUpdateGuide } from './ui_update_guide.js'; import { configureSidebarAction, configureSidebarActions } from './sidebar_actions.js'; const t = (key, params) => translate(key, params); @@ -1191,33 +1190,13 @@ export function createDOM() { updateNoticeBtn.setAttribute('data-tooltip', t('updateGuideNoticeTooltip')); updateNoticeBtn.setAttribute('data-tooltip-pos', 'bottom'); updateNoticeBtn.setAttribute('aria-label', t('updateGuideNoticeTooltip')); - updateNoticeBtn.innerHTML = `!${t('updateGuideNoticeLabel')}`; - updateNoticeBtn.onclick = () => { - showUpdateGuide(this, { force: true }); - }; - - const updateNoticeState = () => { - const isSeen = hasAcknowledged(CURRENT_UPDATE_GUIDE.id); - if (isSeen) { - updateNoticeBtn.style.display = 'none'; - } else { - updateNoticeBtn.style.display = 'inline-flex'; - updateNoticeBtn.style.opacity = '1'; - updateNoticeBtn.style.transform = 'none'; - } - }; - updateNoticeState(); - this.refreshUpdateNotice = updateNoticeState; - - window.addEventListener('anomalous-update-guide-acknowledged', (e) => { - if (e.detail?.id === CURRENT_UPDATE_GUIDE.id) { - updateNoticeBtn.style.opacity = '0'; - updateNoticeBtn.style.transform = 'scale(0.8)'; - setTimeout(() => { - updateNoticeBtn.style.display = 'none'; - }, 300); - } - }); + updateNoticeBtn.type = 'button'; + const updateNoticeIcon = document.createElement('span'); + updateNoticeIcon.className = 'anomalous-update-notice-icon'; + updateNoticeIcon.setAttribute('aria-hidden', 'true'); + updateNoticeIcon.textContent = '!'; + updateNoticeBtn.appendChild(updateNoticeIcon); + updateNoticeBtn.onclick = () => showUpdateGuide(this, { force: true }); rightGroup.appendChild(updateNoticeBtn); rightGroup.appendChild(dockBtn); @@ -1277,7 +1256,6 @@ export function createDOM() { if (updateNoticeBtn) { updateNoticeBtn.setAttribute('data-tooltip', t('updateGuideNoticeTooltip')); updateNoticeBtn.setAttribute('aria-label', t('updateGuideNoticeTooltip')); - updateNoticeBtn.innerHTML = `!${t('updateGuideNoticeLabel')}`; } // Reset dynamic panels so they re-render in new language @@ -2656,25 +2634,7 @@ export function showHelp() { const replayGuideBtn = document.createElement('button'); replayGuideBtn.id = 'anomalous-help-replay-guide-btn'; replayGuideBtn.textContent = t('updateGuideReplay'); - replayGuideBtn.style.padding = '8px 14px'; - replayGuideBtn.style.background = 'rgba(255, 255, 255, 0.05)'; - replayGuideBtn.style.color = '#cbd5e1'; - replayGuideBtn.style.border = '1px solid rgba(255, 255, 255, 0.16)'; - replayGuideBtn.style.borderRadius = '4px'; - replayGuideBtn.style.cursor = 'pointer'; - replayGuideBtn.style.fontSize = '12px'; - replayGuideBtn.style.fontWeight = '500'; - replayGuideBtn.style.transition = 'all 0.15s ease'; - replayGuideBtn.onmouseover = () => { - replayGuideBtn.style.background = 'rgba(255, 255, 255, 0.1)'; - replayGuideBtn.style.borderColor = 'rgba(255, 255, 255, 0.3)'; - replayGuideBtn.style.color = '#ffffff'; - }; - replayGuideBtn.onmouseout = () => { - replayGuideBtn.style.background = 'rgba(255, 255, 255, 0.05)'; - replayGuideBtn.style.borderColor = 'rgba(255, 255, 255, 0.16)'; - replayGuideBtn.style.color = '#cbd5e1'; - }; + replayGuideBtn.type = 'button'; replayGuideBtn.onclick = () => { this.helpModal?.remove(); showUpdateGuide(this, { force: true }); diff --git a/web/modules/ui_update_guide.js b/web/modules/ui_update_guide.js index 0f664ff..c5e0944 100644 --- a/web/modules/ui_update_guide.js +++ b/web/modules/ui_update_guide.js @@ -21,14 +21,11 @@ export function closeUpdateGuide(owner, acknowledge = false) { acknowledgedThisSession.add(current.id); try { localStorage.setItem(storageKey(current.id), '1'); } catch (error) { console.warn('[AMB] Update guide dismissal is saved for this session only.', error); } - if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function') { - window.dispatchEvent(new CustomEvent('anomalous-update-guide-acknowledged', { detail: { id: current.id } })); - } } current.scope.dispose(); } -/** Automatic on browser open; force replays from Help. No graph or file writes. */ +/** Opened only by explicit user action in the header or Help. No graph or file writes. */ export function showUpdateGuide(owner, { force = false, guide = CURRENT_UPDATE_GUIDE } = {}) { if (!owner.modal?.classList.contains('visible')) return false; if (!validateUpdateGuide(guide, i18n)) { diff --git a/web/styles.css b/web/styles.css index fc0fddd..778a8c2 100644 --- a/web/styles.css +++ b/web/styles.css @@ -11862,47 +11862,45 @@ button:hover .anomalous-btn-icon { filter: none; } -#anomalous-sidebar-actions { - display: flex; - align-items: center; - justify-content: space-around; - padding: 10px 8px; - gap: 4px; - box-sizing: border-box; -} - -#anomalous-sidebar-actions button { - display: inline-flex !important; - align-items: center !important; - justify-content: center !important; - width: 32px !important; - height: 32px !important; - border-radius: 6px !important; - box-sizing: border-box !important; - vertical-align: middle !important; - color: var(--amb-text-muted, #9ca3af) !important; - background: transparent !important; - border: 1px solid transparent !important; - transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; -} - -#anomalous-sidebar-actions button:hover { - color: #ffffff !important; - background: rgba(255, 255, 255, 0.08) !important; - border: 1px solid rgba(255, 255, 255, 0.16) !important; - transform: translateY(-1px); - box-shadow: none; -} - -#anomalous-sidebar-actions button svg { - transition: transform 0.2s ease, color 0.2s ease; -} - -#anomalous-sidebar-actions button:hover svg { - transform: scale(1.08); - color: #ffffff !important; -} - +#anomalous-sidebar-actions { + position: relative; + overflow: visible; + display: flex; + align-items: center; + justify-content: space-around; + padding: 10px 8px; + gap: 4px; + box-sizing: border-box; +} + +#anomalous-sidebar-actions button { + position: relative; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + width: 32px !important; + height: 32px !important; + border-radius: 6px !important; + box-sizing: border-box !important; + vertical-align: middle !important; + color: var(--amb-text-muted, #9ca3af) !important; + background: transparent !important; + border: 1px solid transparent !important; + transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease; +} + +#anomalous-sidebar-actions button:hover { + color: #ffffff !important; + background: rgba(255, 255, 255, 0.08) !important; + border: 1px solid rgba(255, 255, 255, 0.16) !important; + transform: none; + box-shadow: none; +} + +#anomalous-sidebar-actions button svg { + transition: opacity 0.15s ease; +} + /* Abyssal Scarlet Mode overrides for Task 8 icon buttons (Sharp Borders & Inner Shadow) */ .theme-abyssal-scarlet #anomalous-doctor-btn:hover { color: #f43f5e !important; @@ -11943,12 +11941,6 @@ button:hover .anomalous-btn-icon { /* 11. Task 9: Master Container Geometric Details, Watermarks & Floating Tooltips */ -/* Empty space watermark on sidebar bottom */ -#anomalous-sidebar-actions { - position: relative; - overflow: hidden; -} - #anomalous-sidebar-actions::before { display: none !important; } @@ -13956,296 +13948,102 @@ button:hover .anomalous-btn-icon { font-size: 0.9rem; } -/* ============================================================================= - UPDATE GUIDE NOTICE INDICATOR (RESTRAINED AMBER BREATH) - ============================================================================= */ -#anomalous-header button.anomalous-update-notice-btn { - background: rgba(245, 158, 11, 0.12) !important; - border-color: rgba(245, 158, 11, 0.38) !important; - color: #fbbf24 !important; - padding: 0 9px 0 6px !important; - font-weight: 600 !important; - letter-spacing: 0.3px !important; - transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease, transform 0.3s ease; - animation: anomalous-amber-breath 3.2s ease-in-out infinite; -} - -#anomalous-header button.anomalous-update-notice-btn:hover { - background: rgba(245, 158, 11, 0.22) !important; - border-color: rgba(245, 158, 11, 0.65) !important; - color: #ffffff !important; - transform: translateY(-1px); - box-shadow: 0 0 10px rgba(245, 158, 11, 0.35) !important; - animation: none; -} - -.anomalous-update-notice-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 14px; - height: 14px; - border-radius: 50%; - background: #f59e0b; - color: #18181b; - font-size: 10px; - transition: border-color 0.15s ease; -} - -.anomalous-sources-search-input:focus { - border-color: #38bdf8; -} - -.anomalous-sources-body { - flex: 1; - overflow-y: auto; - padding: 14px 18px; - display: flex; - flex-direction: column; - gap: 10px; -} - -.anomalous-source-row-item { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.07); - border-radius: 8px; - padding: 10px 14px; - display: flex; - flex-direction: column; - gap: 8px; - transition: border-color 0.15s ease, background 0.15s ease; -} - -.anomalous-source-row-item.has-url { - border-left: 3px solid #10b981; -} - -.anomalous-source-row-item.is-missing-url { - border-left: 3px solid #f59e0b; -} - -.anomalous-source-row-title { - display: flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -.anomalous-source-node-tag { - font-size: 0.72rem; - padding: 1px 6px; - border-radius: 4px; - background: rgba(244, 63, 94, 0.15); - border: 1px solid rgba(244, 63, 94, 0.3); - color: #fb7185; - font-family: monospace; -} - -.anomalous-source-model-name { - font-size: 0.88rem; - color: #f1f5f9; - word-break: break-all; -} - -.anomalous-source-badge-danger { - font-size: 0.7rem; - padding: 1px 5px; - border-radius: 4px; - background: rgba(239, 68, 68, 0.2); - border: 1px solid rgba(239, 68, 68, 0.4); - color: #f87171; -} - -.anomalous-source-hash-chip { - font-size: 0.72rem; - color: #64748b; - margin-left: auto; - font-family: monospace; -} - -.anomalous-source-col-input-row { - display: flex; - align-items: center; - gap: 8px; -} - -.anomalous-source-input-wrap { - flex: 1; - position: relative; - display: flex; - align-items: center; -} - -.anomalous-source-url-input { - width: 100%; - background: rgba(0, 0, 0, 0.4); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 6px; - color: #e2e8f0; - font-size: 0.82rem; - padding: 6px 10px; - padding-right: 96px; - outline: none; - box-sizing: border-box; - font-family: monospace; -} - -.anomalous-source-url-input:focus { - border-color: #38bdf8; - background: rgba(0, 0, 0, 0.6); -} - -.anomalous-source-plat-badge { - position: absolute; - right: 6px; - font-size: 0.7rem; - font-weight: 500; - padding: 2px 7px; - border-radius: 4px; - border: 1px solid; - pointer-events: none; -} - -.anomalous-source-row-actions { - display: flex; - gap: 6px; - align-items: center; - flex-shrink: 0; -} - -.anomalous-btn-jump { - background: linear-gradient(135deg, #0284c7, #0369a1); - border: 1px solid rgba(56, 189, 248, 0.4); - color: #fff; -} - -.anomalous-btn-jump:disabled { - opacity: 0.4; - cursor: not-allowed; -} - -.anomalous-sources-footer { - padding: 10px 18px; - border-top: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(18, 20, 28, 0.95); - display: flex; - align-items: center; - justify-content: space-between; - flex-shrink: 0; -} - -.anomalous-sources-footer-stats { - font-size: 0.82rem; - color: #94a3b8; -} - -.anomalous-sources-footer-actions { - display: flex; - gap: 8px; -} - -.anomalous-sources-empty { - padding: 40px 20px; - text-align: center; - color: #94a3b8; - font-size: 0.9rem; -} - /* ============================================================================= UPDATE GUIDE NOTICE INDICATOR (CLEAN MONOCHROME TECH) ============================================================================= */ +/* Header skin uses important padding/colors; keep this control square within that skin. */ #anomalous-header button.anomalous-update-notice-btn { - background: rgba(255, 255, 255, 0.06) !important; + width: 32px; + min-width: 32px; + padding: 0 !important; + background: transparent !important; border-color: rgba(255, 255, 255, 0.15) !important; - color: #e2e8f0 !important; - padding: 0 10px 0 7px !important; - font-weight: 500 !important; - letter-spacing: 0.2px !important; - transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease, transform 0.3s ease; + color: var(--amb-text-primary, #e2e8f0) !important; } - #anomalous-header button.anomalous-update-notice-btn:hover { - background: rgba(255, 255, 255, 0.12) !important; - border-color: rgba(255, 255, 255, 0.28) !important; - color: #ffffff !important; - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35) !important; + background: rgba(255, 255, 255, 0.08) !important; } - .anomalous-update-notice-icon { display: inline-flex; align-items: center; justify-content: center; - width: 14px; - height: 14px; + width: 16px; + height: 16px; + border: 1px solid currentColor; border-radius: 50%; - background: rgba(255, 255, 255, 0.16); - color: #f1f5f9; - font-size: 10px; - font-weight: 700; - line-height: 1; - margin-right: 4px; + font: 700 12px/1 system-ui, sans-serif; +} +#anomalous-help-replay-guide-btn { + padding: 8px 14px; + background: rgba(255, 255, 255, 0.05); + color: #cbd5e1; + border: 1px solid rgba(255, 255, 255, 0.16); + border-radius: 4px; + cursor: pointer; + font-size: 12px; + font-weight: 500; +} +#anomalous-help-replay-guide-btn:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.3); + color: #fff; +} +#anomalous-update-notice-btn:focus-visible, +#anomalous-help-replay-guide-btn:focus-visible, +.anomalous-update-guide button:focus-visible { + outline: 2px solid var(--amb-accent, #38bdf8); + outline-offset: 2px; } /* ============================================================================= SIDEBAR ACTIONS HOVER REVEAL & TOOLTIPS ============================================================================= */ -#anomalous-sidebar-actions { - overflow: visible !important; -} - -#anomalous-sidebar-actions button { - position: relative !important; -} - -#anomalous-sidebar-actions button .anomalous-action-label { - display: none; - font-size: 11px; - font-weight: 600; - line-height: 1; - letter-spacing: -0.2px; +#anomalous-sidebar-actions .anomalous-action-label { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font: 600 10px/1.2 system-ui, sans-serif; color: inherit; pointer-events: none; white-space: nowrap; - text-align: center; + opacity: 0; + transform: translateY(5px); + transition: opacity 0.15s ease, transform 0.15s ease; } -#anomalous-sidebar-actions button:hover svg, -#anomalous-sidebar-actions button:focus-visible svg { - display: none !important; +#anomalous-sidebar-actions button:is(:hover, :focus-visible) > svg { opacity: 0; } +#anomalous-sidebar-actions button:is(:hover, :focus-visible) .anomalous-action-label { + opacity: 1; + transform: translateY(0); } - -#anomalous-sidebar-actions button:hover .anomalous-action-label, -#anomalous-sidebar-actions button:focus-visible .anomalous-action-label { - display: inline-flex !important; - align-items: center; - justify-content: center; - color: #ffffff !important; - animation: anomalous-action-label-in 0.15s ease; +#anomalous-sidebar-actions button:focus-visible { + outline: 2px solid var(--amb-accent, #38bdf8); + outline-offset: 2px; } - -@keyframes anomalous-action-label-in { - from { opacity: 0; transform: scale(0.92); } - to { opacity: 1; transform: scale(1); } +@media (hover: none) { + #anomalous-sidebar-actions button > svg { opacity: 0; } + #anomalous-sidebar-actions .anomalous-action-label { opacity: 1; transform: none; } } /* Sidebar action tooltips: wide horizontal layout with boundary safety */ #anomalous-sidebar-actions [data-tooltip]::after, #anomalous-sidebar-actions .anomalous-tooltip-target::after { - width: max-content !important; - min-width: 100px !important; - max-width: 220px !important; - white-space: pre-line !important; - text-align: center !important; - line-height: 1.45 !important; - font-size: 11.5px !important; - padding: 7px 11px !important; - border-radius: 6px !important; - word-break: normal !important; - overflow-wrap: break-word !important; - box-sizing: border-box !important; - pointer-events: none !important; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.75), 0 0 0 1px rgba(255, 255, 255, 0.12) !important; + width: max-content; + min-width: 100px; + max-width: 220px; + white-space: pre-line; + text-align: center; + line-height: 1.45; + font-size: 11.5px; + padding: 7px 11px; + border-radius: 6px; + word-break: normal; + overflow-wrap: break-word; + box-sizing: border-box; + pointer-events: none; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.75), 0 0 0 1px rgba(255, 255, 255, 0.12); } /* Align edge buttons so tooltips stay cleanly within the sidebar */ @@ -14253,56 +14051,56 @@ button:hover .anomalous-btn-icon { #anomalous-sidebar-actions button:first-child.anomalous-tooltip-target::after, #anomalous-sidebar-actions button:nth-child(2)[data-tooltip]::after, #anomalous-sidebar-actions button:nth-child(2).anomalous-tooltip-target::after { - left: 0 !important; - right: auto !important; - transform: translateY(4px) !important; + left: 0; + right: auto; + transform: translateY(4px); } -#anomalous-sidebar-actions button:first-child[data-tooltip]:hover::after, -#anomalous-sidebar-actions button:first-child.anomalous-tooltip-target:hover::after, -#anomalous-sidebar-actions button:nth-child(2)[data-tooltip]:hover::after, -#anomalous-sidebar-actions button:nth-child(2).anomalous-tooltip-target:hover::after { - transform: translateY(0) !important; +#anomalous-sidebar-actions button:first-child[data-tooltip]:is(:hover, :focus-visible)::after, +#anomalous-sidebar-actions button:first-child.anomalous-tooltip-target:is(:hover, :focus-visible)::after, +#anomalous-sidebar-actions button:nth-child(2)[data-tooltip]:is(:hover, :focus-visible)::after, +#anomalous-sidebar-actions button:nth-child(2).anomalous-tooltip-target:is(:hover, :focus-visible)::after { + transform: translateY(0); } #anomalous-sidebar-actions button:first-child[data-tooltip]::before, #anomalous-sidebar-actions button:first-child.anomalous-tooltip-target::before, #anomalous-sidebar-actions button:nth-child(2)[data-tooltip]::before, #anomalous-sidebar-actions button:nth-child(2).anomalous-tooltip-target::before { - left: 14px !important; - right: auto !important; - transform: translateY(4px) !important; + left: 14px; + right: auto; + transform: translateY(4px); } -#anomalous-sidebar-actions button:first-child[data-tooltip]:hover::before, -#anomalous-sidebar-actions button:first-child.anomalous-tooltip-target:hover::before, -#anomalous-sidebar-actions button:nth-child(2)[data-tooltip]:hover::before, -#anomalous-sidebar-actions button:nth-child(2).anomalous-tooltip-target:hover::before { - transform: translateY(0) !important; +#anomalous-sidebar-actions button:first-child[data-tooltip]:is(:hover, :focus-visible)::before, +#anomalous-sidebar-actions button:first-child.anomalous-tooltip-target:is(:hover, :focus-visible)::before, +#anomalous-sidebar-actions button:nth-child(2)[data-tooltip]:is(:hover, :focus-visible)::before, +#anomalous-sidebar-actions button:nth-child(2).anomalous-tooltip-target:is(:hover, :focus-visible)::before { + transform: translateY(0); } #anomalous-sidebar-actions button:last-child[data-tooltip]::after, #anomalous-sidebar-actions button:last-child.anomalous-tooltip-target::after { - left: auto !important; - right: 0 !important; - transform: translateY(4px) !important; + left: auto; + right: 0; + transform: translateY(4px); } -#anomalous-sidebar-actions button:last-child[data-tooltip]:hover::after, -#anomalous-sidebar-actions button:last-child.anomalous-tooltip-target:hover::after { - transform: translateY(0) !important; +#anomalous-sidebar-actions button:last-child[data-tooltip]:is(:hover, :focus-visible)::after, +#anomalous-sidebar-actions button:last-child.anomalous-tooltip-target:is(:hover, :focus-visible)::after { + transform: translateY(0); } #anomalous-sidebar-actions button:last-child[data-tooltip]::before, #anomalous-sidebar-actions button:last-child.anomalous-tooltip-target::before { - left: auto !important; - right: 14px !important; - transform: translateY(4px) !important; + left: auto; + right: 14px; + transform: translateY(4px); } -#anomalous-sidebar-actions button:last-child[data-tooltip]:hover::before, -#anomalous-sidebar-actions button:last-child.anomalous-tooltip-target:hover::before { - transform: translateY(0) !important; +#anomalous-sidebar-actions button:last-child[data-tooltip]:is(:hover, :focus-visible)::before, +#anomalous-sidebar-actions button:last-child.anomalous-tooltip-target:is(:hover, :focus-visible)::before { + transform: translateY(0); } /* ============================================================================= @@ -14334,12 +14132,14 @@ dialog.anomalous-update-guide { box-shadow: 0 24px 70px rgba(0, 0, 0, 0.8), 0 0 1px 1px rgba(255, 255, 255, 0.12); color: #f1f5f9; outline: none; - overflow: hidden; + overflow-y: auto; box-sizing: border-box; z-index: 1000000; animation: anomalous-guide-pop 0.25s cubic-bezier(0.16, 1, 0.3, 1); } +dialog.anomalous-update-guide > * { flex-shrink: 0; } + @keyframes anomalous-guide-pop { from { opacity: 0; @@ -14441,6 +14241,7 @@ dialog.anomalous-update-guide::backdrop { display: flex; align-items: center; justify-content: space-between; + flex-wrap: wrap; padding: 14px 20px; background: rgba(0, 0, 0, 0.28); border-top: 1px solid rgba(255, 255, 255, 0.06);