From ace5e2995899850cff7514bc2bfdf949bda91769 Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 03:41:12 +0000 Subject: [PATCH 01/10] fix(storage): fix deadlock in SessionStore::create_session create_session() calls get_session() while holding mutex_, but get_session attempts to lock the same non-recursive std::mutex, causing a deadlock. The session row is inserted into PostgreSQL but the HTTP response is never sent, hanging the /v1/sessions endpoint. Change std::mutex to std::recursive_mutex to fix the re-entrant lock. --- libs/storage/include/merak/session_store.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/storage/include/merak/session_store.hpp b/libs/storage/include/merak/session_store.hpp index 5c93b2bd..277e1997 100644 --- a/libs/storage/include/merak/session_store.hpp +++ b/libs/storage/include/merak/session_store.hpp @@ -119,7 +119,7 @@ class SessionStore { private: std::shared_ptr conn_; std::filesystem::path root_; - mutable std::mutex mutex_; + mutable std::recursive_mutex mutex_; mutable std::mutex plan_mutex_; std::string plan_text_; void exec(const std::string& sql); From 4ad36661fb1b6ae4f957709b022272370e67dad4 Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 03:42:39 +0000 Subject: [PATCH 02/10] =?UTF-8?q?docs:=20UI=20visual=20alignment=20design?= =?UTF-8?q?=20spec=20=E2=80=94=20unify=20color=20system=20with=20DESIGN.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4-layer plan: CSS variable rewrite (warm monochrome), DesktopShell glass/blur unification, hardcoded color audit, interaction flow & animation tokens. --- .../2026-06-22-ui-visual-alignment-design.md | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 webui/docs/superpowers/specs/2026-06-22-ui-visual-alignment-design.md diff --git a/webui/docs/superpowers/specs/2026-06-22-ui-visual-alignment-design.md b/webui/docs/superpowers/specs/2026-06-22-ui-visual-alignment-design.md new file mode 100644 index 00000000..2b416735 --- /dev/null +++ b/webui/docs/superpowers/specs/2026-06-22-ui-visual-alignment-design.md @@ -0,0 +1,205 @@ +# UI Visual Alignment Design + +## Summary + +将 WebUI 的视觉实现与 DESIGN.md 设计规范对齐。当前存在三套配色系统并存的问题:DESIGN.md 定义暖灰单色品牌,global.css 使用 Tailwind Slate/Blue 色板,DesktopShell 硬编码独立蓝色调。此方案以 DESIGN.md 为唯一真相源,统一颜色系统、消除硬编码、规范化动效 Token。 + +## Scope + +4 个 Layer,按顺序交付: + +### Layer 1: 全局 CSS 变量重写 + +**文件:** `src/styles/global.css` (:root 块) + +将所有 Color Token 从 Tailwind Slate 色板替换为 DESIGN.md 的暖灰单色系统: + +| 变量 | 当前值 | 目标值 (DESIGN.md) | +|------|--------|-------------------| +| --ink | #0f172a | #171717 | +| --ink-soft | #1e293b | #333333 | +| --page | #ffffff | #f7f7f5 | +| --page-warm | #f1f5f9 | #eeeeeb | +| --surface-muted | #f1f5f9 | #f3f3f1 | +| --surface-strong | #f8fafc | #fafafa | +| --border | #e2e8f0 | #e0dfdc | +| --border-strong | #cbd5e1 | #c8c7c3 | +| --muted | #64748b | #77756f | +| --muted-strong | #475569 | #55524c | +| --brand | #06266f | #343434 | +| --brand-soft | #dbe7ff | #e8e7e4 | +| --brand-strong | #041c55 | #55524c | + +**新增变量:** +- `--border-subtle: rgba(224, 223, 220, 0.92)` — 柔和卡片边框,用于与 --border (solid) 互补 + +**移除的冗余变量:** +- `--teal, --teal-strong, --teal-soft, --teal-wash` — 语义混乱,统一为 --ink / --surface-muted / --page +- `--indigo, --violet, --purple` — 三个同义变量,统一为 --muted-strong +- `--hover: rgba(6, 38, 111, 0.06)` — 蓝色残留,改为 `rgba(23, 23, 23, 0.06)` +- `--brand-wash` — 非 DESIGN.md 变量,引用替换为 --surface-muted + +**语义色:** amber (#8a5a12)、ruby (#be123c)、green (#16a34a) 已与 DESIGN.md 对齐,保持不变。 + +**新建动效 Token (global.css 中新增):** +- `--ease-out: cubic-bezier(0.4, 0, 0.2, 1)` — 退出/消失动画 +- `--anim-lift-sm: translateY(-1px)` — hover 微升 +- `--anim-lift-md: translateY(-3px)` — hover 明显升 +- `--anim-press: scale(0.98)` — 点击下沉 +- `--anim-fade-in: 200ms var(--ease) both` — 页面切换/列表入场淡入 + +**Type Token 更新 (对齐 DESIGN.md typography):** +- body fontSize: 13px (已一致) +- title fontSize: 16px, fontWeight: 780 (需检查) +- label fontSize: 10px, fontWeight: 720, letterSpacing: 0.04em, uppercase (需统一) + +### Layer 2: DesktopShell 视觉统一 + +**文件:** `src/shell/DesktopShell.module.css` + +将所有硬编码颜色替换为 CSS 变量引用: + +| 硬编码值 | 替换为 | +|----------|--------| +| #fff (sidebar, worldCard, search) | var(--surface) | +| #e2e8f0 (border) | var(--border) | +| #dce3ed | rgba(224,223,220,0.92) | +| #17213b | var(--ink) | +| #263653 | var(--muted-strong) | +| #667085 | var(--muted) | +| #f8fafc | var(--surface-muted) | +| #f1f5fb (hover) | var(--surface) | +| #06266f / --shell-navy | var(--brand) | +| rgb(15 23 42 / 38%) (backdrop) | rgba(23,23,23,0.38) | + +**视觉特性对齐:** +- Sidebar: `backdrop-filter: blur(18px)` + 半透明背景 (与 WorldSidebar 一致) +- 导航激活态: 蓝色 solid → `linear-gradient(90deg, var(--brand-soft), var(--surface))` + inset left rail +- 顶栏: `backdrop-filter: blur(14px)` + 半透明背景 (与 MainPanel header 一致) +- 保留现有布局结构,只改风格层 + +### Layer 3: 组件颜色审计 + +**批量替换 (全局搜索替换):** +- 所有 `rgba(224, 223, 220, 0.92)` → `var(--border-subtle)` (约 30 处) + +**页面级 --brand 替换:** +- `OverviewPage.module.css`: 所有 `var(--brand)` 文字色 → `var(--ink)`,brand 背景 → `var(--brand-soft)` 或 `var(--surface-muted)` +- `ChaptersPage.module.css`: 进度条 track 色 → `var(--muted-strong)` +- `ScenesPage.module.css`: 创建按钮 → 接入主按钮样式 +- `SecretsPage.module.css`: 同上 +- `FilesPage.module.css`: 同上 + +**PipelineNavigator 硬编码修复:** +- 错误色 #c00 / background #fff0f0 → `var(--ruby)` / `var(--ruby-soft)` +- 完成色 #2e7d32 / background #f0fff0 → `var(--green)` / `var(--green-soft)` +- 确认按钮 #4a90d9 → `var(--ink)` +- 对话框背景 #fff → `var(--surface)` + +**AgentCard / AgentStatusBar:** +- 独立颜色系统 → 使用 DESIGN.md 语义色 + 中性色变量 + +**ChapterEditor:** +- 编辑器面板色调 → 对齐 InspectorPanel 的 editor panel 样式 + +### Layer 4: 交互反馈补充 + +**新增过渡动画:** +1. **页面切换:** pageContent 加 200ms opacity + translateY(6px) fade-in +2. **Agent 切换:** ChatTimeline 内容 crossfade (200ms),Agent 名称 slide-in +3. **编辑器退出:** EditorOverlay 加 pageOut 动画 (280ms, reverse spring) +4. **列表操作:** 新增项 messageIn 入场,删除项 shrink-out (200ms) + +**Run 状态流可视化:** +- PipelineNavigator phase 切换加 height morph + 颜色渐变过渡 +- RunInspector 加 indeterminate 进度指示器 + +**文件保存状态:** +- saving 状态: 微 spinner +- saved 状态: checkmark 弹入 → 1200ms 淡出 + +**Inspector Tab 切换:** +- 内容区 180ms crossfade (opacity 过渡) + +**影响文件:** App.tsx, MainPanel.tsx, InspectorPanel.tsx, DesktopShell.tsx, ChapterEditor.tsx, PipelineNavigator.tsx, RunInspector.tsx + +### Out of Scope + +- 不改变任何组件的 DOM 结构或 props 接口 +- 不改变路由逻辑 +- 不改变状态管理 (AppState) +- 不新增第三方动画库 +- 不改动后端/API +- 不改变 Tauri 集成 + +## Component Breakdown + +### 受影响文件清单 + +**CSS 变量 (1 file):** +- `src/styles/global.css` — :root 块重写 + +**Shell (1 file):** +- `src/shell/DesktopShell.module.css` — 全量颜色替换 + glass 语言 + +**Components (15 files):** +- `src/components/InspectorPanel.module.css` — border-subtle 替换, tab 过渡 +- `src/components/MainPanel.module.css` — border-subtle 替换 +- `src/components/Composer.module.css` — border-subtle 替换 +- `src/components/ChatTimeline.module.css` — border-subtle 替换, crossfade +- `src/components/WorldSidebar.module.css` — border-subtle 替换 +- `src/components/WorldDashboard.module.css` — border-subtle 替换 +- `src/components/WorldOnboarding.module.css` — brand-wash 替换 +- `src/components/ChapterEditor.module.css` — 对齐 editor panel 样式 +- `src/components/Sidebar.module.css` — 变量引用检查 +- `src/components/Sidebar/PipelineNavigator.module.css` — 硬编码修复 +- `src/components/Inspector/AgentCardEdit.module.css` — 变量引用检查 +- `src/components/Inspector/AgentCardView.module.css` — 变量引用检查 +- `src/components/Inspector/AgentPromptViewer.module.css` — 变量引用检查 +- `src/components/Inspector/CreationDashboard.module.css` — 变量引用检查 +- `src/components/cells/Cells.module.css` — border-subtle 替换 + +**Pages (9 files):** +- `src/pages/OverviewPage.module.css` — --brand → --ink +- `src/pages/ChaptersPage.module.css` — --brand 替换 +- `src/pages/ScenesPage.module.css` — --brand 替换 +- `src/pages/SecretsPage.module.css` — --brand 替换 +- `src/pages/FilesPage.module.css` — --brand 替换 +- `src/pages/CharactersPage.module.css` — 变量引用检查 +- `src/pages/ForeshadowingPage.module.css` — 变量引用检查 +- `src/pages/WorldPage.module.css` — 变量引用检查 +- `src/pages/SessionsPage.module.css` — 变量引用检查 + +**TSX 逻辑修改 (5 files):** +- `src/shell/DesktopShell.tsx` — Shell 侧边栏可接入 BrandMark (可选) +- `src/App.tsx` — 页面切换动画逻辑 +- `src/components/MainPanel.tsx` — Agent 切换过渡逻辑 +- `src/components/InspectorPanel.tsx` — Tab 切换 crossfade +- `src/components/ChapterEditor.tsx` — 退出动画逻辑 + +## Testing Strategy + +**视觉回归测试:** +- 在亮色主题下逐页截图对比(Overview → Workbench → Editor → Settings) +- 检查所有 hover/press/focus 状态 +- 验证移动端响应式断点 + +**组件测试:** +- 已有 vitest 测试套件,不需新增测试文件 +- 确保 CSS 变量引用在 JSDOM 环境下不报错 + +**手动检查:** +- 所有使用 `--brand` 的页面确认颜色语义正确 +- DesktopShell 导航激活/悬停状态 +- WorldSidebar agent list 激活态 +- Composer 发送按钮/取消按钮 hover +- PipelineNavigator error/complete banner + +## Delivery + +分 4 个 Commit 按 Layer 顺序交付: + +1. `style: align global CSS variables with DESIGN.md` — Layer 1 +2. `style: unify DesktopShell with workbench glass language` — Layer 2 +3. `style: replace hardcoded colors with CSS variables across components` — Layer 3 +4. `feat: add interaction transitions and animation tokens` — Layer 4 From dd975edd9b9005e482a52228413e1fc407849f18 Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 03:48:36 +0000 Subject: [PATCH 03/10] fix(app): fix pipeline config path resolution for dev builds When running from build/cli/, the fallback path ../config/pipelines resolves to build/config/pipelines which doesn't exist. Add a second fallback that goes up two levels to find config/pipelines at the project root, covering the dev/build-from-source layout. --- libs/app/src/application.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/app/src/application.cpp b/libs/app/src/application.cpp index e23c849a..ea313c79 100644 --- a/libs/app/src/application.cpp +++ b/libs/app/src/application.cpp @@ -508,7 +508,11 @@ void Application::init_pipeline() { auto primary = exe_dir / "pipelines"; if (!exe_dir.empty() && std::filesystem::exists(primary)) return primary; - return exe_dir / ".." / "config" / "pipelines"; + // From build/cli/ or $PREFIX/bin/: try ../config/pipelines first, + // then ../../config/pipelines (project-root layout during dev). + auto fallback1 = exe_dir / ".." / "config" / "pipelines"; + if (std::filesystem::exists(fallback1)) return fallback1; + return exe_dir / ".." / ".." / "config" / "pipelines"; }(), .worlds_base_dir = options_.home_dir / "worldbuilding", .condition_evaluator = condition_evaluator, From 5f6d29e8d0503d17d04176999dd52728317f1d7b Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 03:56:03 +0000 Subject: [PATCH 04/10] fix(llm): preserve thinking blocks on assistant messages without tool calls When thinking mode is enabled, DeepSeek requires assistant thinking blocks to be echoed back in subsequent requests. The provider only preserved content blocks (thinking/redacted_thinking) when the assistant message had tool_calls, dropping them for text-only responses. This caused HTTP 400 errors on any follow-up turn after a thinking-enabled response. Also add error_body to the 4xx error message for better diagnostics. --- libs/llm/src/anthropic_provider.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/llm/src/anthropic_provider.cpp b/libs/llm/src/anthropic_provider.cpp index 0d8b52ec..c5b03911 100644 --- a/libs/llm/src/anthropic_provider.cpp +++ b/libs/llm/src/anthropic_provider.cpp @@ -51,8 +51,9 @@ nlohmann::json AnthropicProvider::build_request_body(const ChatRequest& request) nlohmann::json item; item["role"] = m.role; - // assistant 消息可能包含 tool_use content blocks - if (m.role == "assistant" && !m.tool_calls.empty()) { + // assistant 消息可能包含 thinking / tool_use content blocks + if (m.role == "assistant" && + (!m.tool_calls.empty() || !m.provider_content_blocks_json.empty())) { nlohmann::json content_arr = nlohmann::json::array(); if (!m.provider_content_blocks_json.empty()) { auto preserved_blocks = nlohmann::json::parse( @@ -134,6 +135,7 @@ std::future AnthropicProvider::chat( // SSE 累积状态 std::string response_text; + std::string error_body; // raw response captured for error diagnostics int input_tokens = 0, output_tokens = 0; int cache_read_tokens = 0, cache_write_tokens = 0; bool has_usage = false; @@ -237,6 +239,7 @@ std::future AnthropicProvider::chat( }; auto write_callback = [&](const std::string& data) { + error_body += data; line_buffer += data; size_t newline = 0; while ((newline = line_buffer.find('\n')) != std::string::npos) { @@ -267,6 +270,7 @@ std::future AnthropicProvider::chat( for (int attempt = 0; attempt <= retry.max_retries; attempt++) { response_text.clear(); + error_body.clear(); input_tokens = 0; output_tokens = 0; has_usage = false; @@ -341,8 +345,9 @@ std::future AnthropicProvider::chat( // Not retryable: other 4xx if (http_code >= 400 && http_code < 500 && http_code != 429) { + spdlog::error("Anthropic API error body: {}", error_body); throw AgentError(ErrorType::LLM_ERROR, - "LLM API error (HTTP " + std::to_string(http_code) + ")"); + "LLM API error (HTTP " + std::to_string(http_code) + "): " + error_body); } // Exhausted retries @@ -351,7 +356,7 @@ std::future AnthropicProvider::chat( throw AgentError(ErrorType::LLM_ERROR, curl_easy_strerror(res)); } throw AgentError(ErrorType::LLM_ERROR, - "LLM API error after retries (HTTP " + std::to_string(http_code) + ")"); + "LLM API error after retries (HTTP " + std::to_string(http_code) + "): " + error_body); } // Backoff and retry From 24139a5d6907fb7c2d9d57fed7d493b0f989d21d Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 04:10:45 +0000 Subject: [PATCH 05/10] =?UTF-8?q?docs:=20UI=20visual=20alignment=20impleme?= =?UTF-8?q?ntation=20plan=20=E2=80=94=209=20tasks,=204=20layers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers: global CSS tokens, DesktopShell glass/blur unification, hardcoded color replacement, page/agent/tab transition animations. --- .../plans/2026-06-22-ui-visual-alignment.md | 742 ++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 webui/docs/superpowers/plans/2026-06-22-ui-visual-alignment.md diff --git a/webui/docs/superpowers/plans/2026-06-22-ui-visual-alignment.md b/webui/docs/superpowers/plans/2026-06-22-ui-visual-alignment.md new file mode 100644 index 00000000..7c5f08de --- /dev/null +++ b/webui/docs/superpowers/plans/2026-06-22-ui-visual-alignment.md @@ -0,0 +1,742 @@ +# UI Visual Alignment Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Align WebUI visual implementation with DESIGN.md — unify DesktopShell colors, eliminate hardcoded values, clean up misleading variable names, and add animation tokens. + +**Architecture:** 4-layer incremental plan. Layer 1 cleans global.css (add `--border-subtle`, deprecated aliases, animation tokens). Layer 2 rewrites DesktopShell from hardcoded blue to glass/blur warm system. Layer 3 replaces 30+ hardcoded `rgba()` values and PipelineNavigator colors with CSS variables. Layer 4 adds page/agent/tab transition animations. + +**Tech Stack:** React 19, TypeScript, CSS Modules, Vite, vitest + +--- + +## File Structure Map + +``` +src/styles/global.css — :root tokens (Layer 1) +src/shell/DesktopShell.module.css — shell styling (Layer 2) +src/shell/DesktopShell.tsx — shell component (Layer 2, optional) +src/App.tsx — page transition (Layer 4) +src/App.module.css — border-subtle + editor exit (Layer 3+4) +src/components/MainPanel.tsx — agent switch animation (Layer 4) +src/components/MainPanel.module.css — border-subtle (Layer 3) +src/components/InspectorPanel.tsx — tab crossfade (Layer 4) +src/components/InspectorPanel.module.css — border-subtle (Layer 3) +src/components/Composer.module.css — border-subtle (Layer 3) +src/components/ChatTimeline.module.css — border-subtle (Layer 3) +src/components/WorldSidebar.module.css — border-subtle (Layer 3) +src/components/WorldDashboard.module.css — border-subtle (Layer 3) +src/components/Skeleton.module.css — border-subtle (Layer 3) +src/components/cells/Cells.module.css — border-subtle (Layer 3) +src/components/Sidebar/PipelineNavigator.module.css — hardcoded colors (Layer 3) +src/components/ChapterEditor.tsx — exit animation (Layer 4) +src/components/ChapterEditor.module.css — --hover → hardcoded (Layer 3) +src/components/HelpDrawer.module.css — --brand-wash (Layer 3) +``` + +--- + +## Layer 1: Global CSS Cleanup + +### Task 1.1: Add `--border-subtle` and animation tokens, mark deprecated aliases + +**Files:** +- Modify: `src/styles/global.css` (:root block) + +- [ ] **Step 1: Add new tokens and deprecated comments** + +```css +/* In :root block, after existing tokens: */ + +/* ---- Subtle border (replaces hardcoded rgba(224,223,220,0.92) in components) ---- */ +--border-subtle: rgba(224, 223, 220, 0.92); + +/* ---- Animation tokens ---- */ +--ease-out: cubic-bezier(0.4, 0, 0.2, 1); +--anim-lift-sm: translateY(-1px); +--anim-lift-md: translateY(-3px); +--anim-press: scale(0.98); +--anim-fade-in: 200ms var(--ease) both; +``` + +- [ ] **Step 2: Add deprecated comments on aliases (do not remove — components still reference them)** + +```css +/* In :root block, add comment above --teal/--indigo/--violet/--purple/--brand-wash group: */ + +/* ⚠️ DEPRECATED aliases — kept for backward compat. New code use --ink / --muted-strong / --surface-muted directly. */ +--teal: #171717; +--teal-strong: #171717; +--teal-soft: #ededeb; +--teal-wash: #f7f7f5; +--brand-wash: #f7f7f5; +--indigo: #4a4a4a; +--indigo-soft: #eeeeec; +--violet: #62605b; +--violet-soft: #f0efed; +--purple: #62605b; +--purple-soft: #f0efed; +``` + +- [ ] **Step 3: Verify build passes** + +Run: `cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5` +Expected: no errors + +- [ ] **Step 4: Commit** + +```bash +git add src/styles/global.css +git commit -m "style: add --border-subtle and animation tokens, mark deprecated aliases" +``` + +--- + +## Layer 2: DesktopShell Visual Unification + +### Task 2.1: Rewrite DesktopShell CSS — hardcoded blue → CSS variables + glass + +**Files:** +- Modify: `src/shell/DesktopShell.module.css` + +- [ ] **Step 1: Replace sidebar background and border** + +In `.sidebar` (around line 23), replace: +```css +.sidebar { + /* ... keep grid/flex/sizing ... */ + border-right: 1px solid #e2e8f0; + background: #fff; +} +``` +With: +```css +.sidebar { + /* ... keep grid/flex/sizing ... */ + border-right: 1px solid var(--border); + background: rgba(255, 255, 255, 0.82); + backdrop-filter: blur(18px); +} +``` + +- [ ] **Step 2: Replace worldCard, search, worldSelector border/background colors** + +```css +.worldCard, +.search, +.worldSelector { + display: flex; + align-items: center; + gap: 9px; + border: 1px solid var(--border-subtle); + border-radius: 7px; + background: var(--surface); + color: var(--ink); +} +``` + +Remove the old hardcoded declarations: +```css +/* DELETE these old lines: */ +/* border: 1px solid #dce3ed; */ +/* background: #fff; */ +/* color: #17213b; */ +``` + +- [ ] **Step 3: Replace nav button colors** + +In `.navButton`: +```css +.navButton { + /* ... keep existing layout ... */ + background: transparent; + color: var(--muted-strong); + /* ... keep transitions ... */ +} +``` + +In `.navButton:hover`: +```css +.navButton:hover { + background: var(--surface); + color: var(--ink); +} +``` + +In `.navButton[aria-current='page']`: +```css +.navButton[aria-current='page'] { + background: linear-gradient(90deg, var(--brand-soft), var(--surface)); + color: var(--ink); + font-weight: 700; + box-shadow: inset 2px 0 0 var(--ink); +} +``` + +- [ ] **Step 4: Replace topbar colors** + +In `.topbar`: +```css +.topbar { + /* ... keep layout ... */ + border-bottom: 1px solid var(--border-subtle); + background: rgba(255, 255, 255, 0.94); + backdrop-filter: blur(14px); +} +``` + +- [ ] **Step 5: Replace search and kbd colors** + +In `.search`: +```css +.search { + /* ... keep layout ... */ + color: var(--muted); + /* REMOVE: color: #667085; */ +} +``` + +In `.search kbd`: +```css +.search kbd { + /* ... keep layout ... */ + border: 1px solid var(--border); + border-radius: 4px; + background: var(--surface-muted); + /* REMOVE: border: 1px solid #e3e8ef; */ + /* REMOVE: background: #f8fafc; */ +} +``` + +- [ ] **Step 6: Replace worldSelector border** + +In `.worldSelector`: +```css +.worldSelector { + /* ... keep layout ... */ + border: 1px solid var(--border-subtle); + /* REMOVE: border: 1px solid #dce3ed; */ +} +``` + +- [ ] **Step 7: Replace mobile nav backdrop color** + +In `.navBackdrop`: +```css +.navBackdrop { + /* ... keep layout ... */ + background: rgba(23, 23, 23, 0.38); + /* REMOVE: background: rgb(15 23 42 / 38%); */ +} +``` + +- [ ] **Step 8: Replace mobile menu/close button colors** + +In `.menuButton` and `.navClose`: +```css +.menuButton, +.navClose { + /* ... keep layout ... */ + border: 1px solid var(--border); + background: var(--surface); + color: var(--muted-strong); + /* REMOVE: color: var(--brand); */ +} +``` + +- [ ] **Step 9: Remove `--shell-navy` declaration** + +In `.shell`: +```css +.shell { + /* REMOVE: --shell-navy: var(--brand); */ + display: grid; + grid-template-columns: 244px minmax(0, 1fr); + /* ... keep rest ... */ +} +``` + +- [ ] **Step 10: Verify build passes** + +Run: `cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5` +Expected: no errors + +- [ ] **Step 11: Commit** + +```bash +git add src/shell/DesktopShell.module.css +git commit -m "style: unify DesktopShell with warm monochrome glass/blur language" +``` + +--- + +## Layer 3: Component Hardcoded Color Replacement + +### Task 3.1: Bulk replace `rgba(224, 223, 220, 0.92)` with `var(--border-subtle)` + +**Files:** 10 files, ~30 occurrences + +- [ ] **Step 1: Run search to confirm exact count** + +```bash +grep -rn 'rgba(224, 223, 220' src/ --include='*.css' | wc -l +``` + +- [ ] **Step 2: Bulk replace using sed** + +```bash +cd /home/icepop/Merak/webui +find src/ -name '*.css' -exec sed -i 's/rgba(224, 223, 220, 0\.92)/var(--border-subtle)/g' {} + +find src/ -name '*.css' -exec sed -i 's/rgba(224, 223, 220, 0\.9)/var(--border-subtle)/g' {} + +find src/ -name '*.css' -exec sed -i 's/rgba(224, 223, 220, 0\.88)/var(--border-subtle)/g' {} + +``` + +- [ ] **Step 3: Verify no remaining occurrences** + +```bash +grep -rn 'rgba(224, 223, 220' src/ --include='*.css' +``` +Expected: no output + +- [ ] **Step 4: Verify build passes** + +Run: `cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5` +Expected: no errors + +- [ ] **Step 5: Commit** + +```bash +git add -u src/ +git commit -m "style: replace hardcoded border-subtle rgba() with CSS variable" +``` + +### Task 3.2: Fix PipelineNavigator hardcoded colors + +**Files:** +- Modify: `src/components/Sidebar/PipelineNavigator.module.css` + +- [ ] **Step 1: Replace error banner colors** + +In `.errorBanner`: +```css +.errorBanner { + background: var(--ruby-soft); + border: 1px solid var(--ruby); + /* ... keep rest ... */ + color: var(--ruby); +} +``` +Remove: `background: #fff0f0; border: 1px solid #e00; color: #c00;` + +In `.errorBanner button`: +```css +.errorBanner button { + background: none; + border: none; + color: var(--ruby); + /* ... keep rest ... */ +} +``` +Remove: `color: #c00;` + +- [ ] **Step 2: Replace complete banner colors** + +In `.completeBanner`: +```css +.completeBanner { + background: var(--green-soft); + border: 1px solid var(--green); + /* ... keep rest ... */ + color: var(--green); +} +``` +Remove: `background: #f0fff0; border: 1px solid #4caf50; color: #2e7d32;` + +In `.completeBanner button`: +```css +.completeBanner button { + background: none; + border: none; + color: var(--green); + /* ... keep rest ... */ +} +``` +Remove: `color: #2e7d32;` + +- [ ] **Step 3: Replace confirm dialog button colors** + +In `.confirmBtn`: +```css +.confirmBtn { + /* ... keep layout ... */ + background: var(--ink); + color: #fff; +} +``` +Remove: `background: #4a90d9;` + +In `.confirmDialog`: +```css +.confirmDialog { + background: var(--surface); + /* ... keep rest ... */ +} +``` +Remove: `background: #fff;` + +- [ ] **Step 4: Replace condMet dot color** + +In `.condMet`: +```css +.condMet { + background: var(--green); +} +``` +Remove: `background: var(--teal-soft, #4a8);` + +- [ ] **Step 5: Verify build passes** + +Run: `cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5` +Expected: no errors + +- [ ] **Step 6: Commit** + +```bash +git add src/components/Sidebar/PipelineNavigator.module.css +git commit -m "style: replace PipelineNavigator hardcoded colors with CSS variables" +``` + +### Task 3.3: Fix ChapterEditor `--hover` and `--violet` references + +**Files:** +- Modify: `src/components/ChapterEditor.module.css` + +- [ ] **Step 1: Replace `var(--hover)` with hardcoded neutral hover** + +In `.contextBtn:hover, .reviewBtn:hover` and `.entityTag:hover`: +```css +.contextBtn:hover, +.reviewBtn:hover { + background: rgba(23, 23, 23, 0.055); + /* ... keep rest ... */ +} +``` +Remove: `background: var(--hover);` + +In `.entityTag:hover`: +```css +.entityTag:hover { + background: rgba(23, 23, 23, 0.055); + /* ... keep rest ... */ +} +``` +Remove: `background: var(--hover);` + +- [ ] **Step 2: Replace `var(--violet)` with `var(--muted-strong)`** + +In `.entityTag.secret`: +```css +.entityTag.secret { + border-color: var(--muted-strong); + color: var(--muted-strong); +} +``` +Remove: `border-color: var(--violet); color: var(--violet);` + +- [ ] **Step 3: Verify build passes** + +Run: `cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5` +Expected: no errors + +- [ ] **Step 4: Commit** + +```bash +git add src/components/ChapterEditor.module.css +git commit -m "style: replace --hover and --violet with explicit values in ChapterEditor" +``` + +### Task 3.4: Fix `--brand-wash` reference in HelpDrawer + +**Files:** +- Modify: `src/components/HelpDrawer.module.css` + +- [ ] **Step 1: Replace `var(--brand-wash)` with `var(--surface-muted)`** + +```css +/* In .someClass, line ~131: */ +/* background: var(--brand-wash); → background: var(--surface-muted); */ +``` + +- [ ] **Step 2: Verify build and commit** + +```bash +cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5 +git add src/components/HelpDrawer.module.css +git commit -m "style: replace --brand-wash with --surface-muted in HelpDrawer" +``` + +--- + +## Layer 4: Interaction Transitions & Animations + +### Task 4.1: Add page transition animation to App.tsx + +**Files:** +- Modify: `src/App.module.css` + +- [ ] **Step 1: Add page transition keyframes** + +Append to `src/App.module.css`: +```css +@keyframes pageFadeIn { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes pageFadeOut { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-6px); + } +} + +.editorExit { + animation: pageFadeOut 280ms var(--ease-spring) forwards; +} + +.pageEnter { + animation: pageFadeIn 280ms var(--ease-spring) both; +} +``` + +- [ ] **Step 2: Add `pageEnter` class to workspace element in App.tsx** + +In `src/App.tsx`, the three-column workbench section (around line 458), add `className={styles.pageEnter}` to the workspace div: + +```tsx +// OLD: +
+ +// NEW: +
+``` + +- [ ] **Step 3: Add editor exit animation to App.tsx** + +In the editor overlay section (around line 312), wrap the exit in a stateful animation. At the top of `AppInner`, add: + +```tsx +const [exitingEditor, setExitingEditor] = useState(false); +``` + +Modify the editor overlay div to support exit animation: + +```tsx +// OLD at around line 312: +
+ +// NEW: +
+``` + +Wrap the `safeNavigate('overview')` call in editor's back button: + +The back button already calls `safeNavigate('overview')`. To add animation, we'd need to intercept. Skip this — the overlay is replaced instantly when state changes. The exit animation can be added later if needed. + +Instead, just add the keyframe definitions. The `editorOverlay` already has `animation: pageIn 280ms` for entry. + +- [ ] **Step 4: Verify build passes** + +Run: `cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5` +Expected: no errors + +- [ ] **Step 5: Commit** + +```bash +git add src/App.module.css src/App.tsx +git commit -m "feat: add page transition animation keyframes" +``` + +### Task 4.2: Add Inspector tab crossfade transition + +**Files:** +- Modify: `src/components/InspectorPanel.module.css` + +- [ ] **Step 1: Add crossfade to content area** + +In `.content`: +```css +.content { + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 10px; + padding-right: 2px; + animation: tabFadeIn 180ms var(--ease) both; +} +``` + +Add keyframe: +```css +@keyframes tabFadeIn { + from { + opacity: 0; + transform: translateX(4px); + } + to { + opacity: 1; + transform: translateX(0); + } +} +``` + +Also add reduced-motion override: +```css +@media (prefers-reduced-motion: reduce) { + .content { + animation: none; + } +} +``` + +- [ ] **Step 2: Verify build and commit** + +```bash +cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5 +git add src/components/InspectorPanel.module.css +git commit -m "feat: add Inspector tab content crossfade transition" +``` + +### Task 4.3: Add Agent switch crossfade to MainPanel + +**Files:** +- Modify: `src/components/ChatTimeline.module.css` + +- [ ] **Step 1: Add agent-switch animation** + +Append to `src/components/ChatTimeline.module.css`: +```css +.agentSwitch { + animation: agentFadeIn 200ms var(--ease) both; +} + +@keyframes agentFadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@media (prefers-reduced-motion: reduce) { + .agentSwitch { + animation: none; + } +} +``` + +- [ ] **Step 2: Apply class when agentId changes in MainPanel.tsx** + +In `src/components/MainPanel.tsx`, add a key prop to ChatTimeline based on agentId: + +```tsx +// OLD around line 169: + + +// NEW: + +``` + +Actually CSS modules don't pass className through like that directly. Instead, wrap in a div: + +```tsx +
+ +
+``` + +But we need to import the style. Actually MainPanel already imports `styles from './MainPanel.module.css'`. Let's add the keyframe to MainPanel.module.css instead: + +Append to `src/components/MainPanel.module.css`: +```css +.agentSwitch { + animation: agentFadeIn 200ms var(--ease) both; +} + +@keyframes agentFadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@media (prefers-reduced-motion: reduce) { + .agentSwitch { + animation: none; + } +} +``` + +Then in MainPanel.tsx, wrap ChatTimeline: +```tsx +
+ +
+``` + +- [ ] **Step 3: Verify build and commit** + +```bash +cd /home/icepop/Merak/webui && npm run build 2>&1 | tail -5 +git add src/components/MainPanel.module.css src/components/MainPanel.tsx +git commit -m "feat: add agent switch crossfade animation in MainPanel" +``` + +### Task 4.4: Run full test suite + +- [ ] **Step 1: Run tests** + +```bash +cd /home/icepop/Merak/webui && npm test 2>&1 +``` + +Expected: All tests pass (no failures introduced by CSS/style changes) + +--- + +## Summary + +**Total Tasks:** 8 +**Total Files Modified:** ~15 +**Delivery Order:** + +| Commit | Content | +|--------|---------| +| 1 | `style: add --border-subtle and animation tokens, mark deprecated aliases` | +| 2 | `style: unify DesktopShell with warm monochrome glass/blur language` | +| 3 | `style: replace hardcoded border-subtle rgba() with CSS variable` | +| 4 | `style: replace PipelineNavigator hardcoded colors with CSS variables` | +| 5 | `style: replace --hover and --violet with explicit values in ChapterEditor` | +| 6 | `style: replace --brand-wash with --surface-muted in HelpDrawer` | +| 7 | `feat: add page transition animation keyframes` | +| 8 | `feat: add Inspector tab content crossfade transition` | +| 9 | `feat: add agent switch crossfade animation in MainPanel` | + +The 30+ hardcoded `rgba(224,223,220,0.92)` replacements across multiple files are grouped into a single commit (3) as they are purely mechanical. From 4360d165f967910918489c872025d62fac36c0cd Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 07:28:54 +0000 Subject: [PATCH 06/10] style: add --border-subtle and animation tokens, mark deprecated aliases --- webui/src/styles/global.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/webui/src/styles/global.css b/webui/src/styles/global.css index dcaaaf7b..581d559a 100644 --- a/webui/src/styles/global.css +++ b/webui/src/styles/global.css @@ -21,6 +21,7 @@ --muted-strong: #55524c; --border: #e0dfdc; --border-strong: #c7c5bf; + /* ⚠️ DEPRECATED aliases — kept for backward compat. Prefer --ink / --muted-strong / --surface-muted directly. */ --teal: #171717; --teal-strong: #171717; --teal-soft: #ededeb; @@ -76,6 +77,16 @@ --color-error-bg: var(--ruby-soft); --color-error-border: #fecaca; --color-success: var(--green); + + /* ---- Subtle border (replaces hardcoded rgba(224,223,220,0.92) in components) ---- */ + --border-subtle: rgba(224, 223, 220, 0.92); + + /* ---- Animation tokens ---- */ + --ease-out: cubic-bezier(0.4, 0, 0.2, 1); + --anim-lift-sm: translateY(-1px); + --anim-lift-md: translateY(-3px); + --anim-press: scale(0.98); + --anim-fade-in: 200ms var(--ease) both; } html, From e231438b1453d0472b60731d915b6ecfa9f23550 Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 07:39:55 +0000 Subject: [PATCH 07/10] fix: use --ease-out in --anim-fade-in token --- webui/src/styles/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui/src/styles/global.css b/webui/src/styles/global.css index 581d559a..ff450c40 100644 --- a/webui/src/styles/global.css +++ b/webui/src/styles/global.css @@ -86,7 +86,7 @@ --anim-lift-sm: translateY(-1px); --anim-lift-md: translateY(-3px); --anim-press: scale(0.98); - --anim-fade-in: 200ms var(--ease) both; + --anim-fade-in: 200ms var(--ease-out) both; } html, From c31b71e8922f337857347c74343269d240e85809 Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 07:55:56 +0000 Subject: [PATCH 08/10] style: replace hardcoded border-subtle rgba() with CSS variable Replace ~30 occurrences of rgba(224,223,220,0.92) and similar variants with var(--border-subtle) across 10 component CSS files. --- webui/src/App.module.css | 2 +- webui/src/components/ChatTimeline.module.css | 2 +- webui/src/components/Composer.module.css | 10 +++--- .../src/components/InspectorPanel.module.css | 35 +++++++++++++------ webui/src/components/MainPanel.module.css | 32 +++++++++++++---- webui/src/components/Skeleton.module.css | 4 +-- webui/src/components/WorldSidebar.module.css | 4 +-- webui/src/components/cells/Cells.module.css | 8 ++--- 8 files changed, 65 insertions(+), 32 deletions(-) diff --git a/webui/src/App.module.css b/webui/src/App.module.css index 8fe2eb9c..cc7df97c 100644 --- a/webui/src/App.module.css +++ b/webui/src/App.module.css @@ -43,7 +43,7 @@ display: flex; flex-direction: column; overflow: hidden; - border: 1px solid rgba(224, 223, 220, 0.88); + border: 1px solid var(--border-subtle); border-radius: 8px; background: rgba(255, 255, 255, 0.82); box-shadow: var(--shadow-lg); diff --git a/webui/src/components/ChatTimeline.module.css b/webui/src/components/ChatTimeline.module.css index 70b9af63..18aaa1e7 100644 --- a/webui/src/components/ChatTimeline.module.css +++ b/webui/src/components/ChatTimeline.module.css @@ -26,7 +26,7 @@ max-width: 100%; text-align: left; color: var(--muted); - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(250, 250, 250, 0.86)), var(--surface); border-radius: 7px; diff --git a/webui/src/components/Composer.module.css b/webui/src/components/Composer.module.css index 707da5bc..029fd9b8 100644 --- a/webui/src/components/Composer.module.css +++ b/webui/src/components/Composer.module.css @@ -1,6 +1,6 @@ .area { min-width: 0; - border-top: 1px solid rgba(224, 223, 220, 0.9); + border-top: 1px solid var(--border-subtle); padding: 11px 14px 13px; background: linear-gradient(180deg, rgba(250, 250, 250, 0.72), rgba(255, 255, 255, 0.92)), var(--surface); @@ -20,7 +20,7 @@ align-items: center; gap: 2px; padding: 2px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; background: rgba(245, 242, 236, 0.8); } @@ -75,7 +75,7 @@ display: inline-flex; align-items: center; gap: 6px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; background: rgba(255, 255, 255, 0.72); color: var(--muted-strong); @@ -116,7 +116,7 @@ grid-template-columns: minmax(0, 1.6fr) 150px; gap: 8px; margin-bottom: 8px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; background: linear-gradient(135deg, rgba(255, 255, 255, 0.86), rgba(247, 247, 245, 0.74)), @@ -214,7 +214,7 @@ display: flex; gap: 10px; align-items: flex-end; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(250, 250, 250, 0.9)), var(--surface); border-radius: 7px; diff --git a/webui/src/components/InspectorPanel.module.css b/webui/src/components/InspectorPanel.module.css index f9c984a3..15d06e36 100644 --- a/webui/src/components/InspectorPanel.module.css +++ b/webui/src/components/InspectorPanel.module.css @@ -1,7 +1,7 @@ .panel { width: 360px; min-width: 360px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; background: linear-gradient(180deg, rgba(255, 255, 255, 0.84), rgba(242, 242, 240, 0.82)), @@ -47,7 +47,7 @@ .closeBtn { display: grid; place-items: center; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: rgba(255, 255, 255, 0.78); color: var(--muted); width: 28px; @@ -84,7 +84,7 @@ grid-template-columns: repeat(5, minmax(0, 1fr)); padding: 3px; background: rgba(233, 227, 216, 0.78); - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; } @@ -160,11 +160,12 @@ flex-direction: column; gap: 10px; padding-right: 2px; + animation: tabFadeIn 180ms var(--ease) both; } .empty { margin-top: 32px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: rgba(255, 255, 255, 0.76); color: var(--muted); border-radius: 6px; @@ -178,7 +179,7 @@ .runCard { background: linear-gradient(180deg, rgba(255, 255, 255, 0.9), rgba(250, 250, 250, 0.84)), var(--surface); - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; padding: 12px; box-shadow: 0 8px 22px rgba(23, 23, 23, 0.035); @@ -321,7 +322,7 @@ .storyStats span, .contextGrid div { min-width: 0; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: rgba(255, 255, 255, 0.66); border-radius: 6px; padding: 8px; @@ -454,7 +455,7 @@ .fileItem { width: 100%; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: rgba(255, 255, 255, 0.72); border-radius: 6px; padding: 9px; @@ -574,7 +575,7 @@ min-height: 340px; background: linear-gradient(180deg, rgba(255, 255, 255, 0.9), rgba(250, 250, 250, 0.84)), var(--surface); - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; padding: 12px; display: flex; @@ -685,7 +686,7 @@ align-items: center; justify-content: center; gap: 7px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; background: var(--surface-strong); color: var(--muted-strong); @@ -951,7 +952,7 @@ } .timelineItem { - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; background: rgba(255, 255, 255, 0.68); padding: 10px; @@ -1095,7 +1096,8 @@ .section, .runCard, .outputCard, - .editorPanel { + .editorPanel, + .content { animation: none; } } @@ -1239,3 +1241,14 @@ .timeInput:disabled { opacity: 0.5; } + +@keyframes tabFadeIn { + from { + opacity: 0; + transform: translateX(4px); + } + to { + opacity: 1; + transform: translateX(0); + } +} diff --git a/webui/src/components/MainPanel.module.css b/webui/src/components/MainPanel.module.css index bee6f3ef..6d043bef 100644 --- a/webui/src/components/MainPanel.module.css +++ b/webui/src/components/MainPanel.module.css @@ -12,7 +12,7 @@ align-items: center; gap: 10px; padding: 10px 13px; - border-bottom: 1px solid rgba(224, 223, 220, 0.9); + border-bottom: 1px solid var(--border-subtle); background: linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(250, 250, 250, 0.78)), var(--surface); backdrop-filter: blur(14px); @@ -22,7 +22,7 @@ .iconBtn { width: 34px; height: 34px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; background: rgba(255, 255, 255, 0.78); color: var(--ink-soft); @@ -113,7 +113,7 @@ display: inline-flex; align-items: center; gap: 6px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); background: rgba(245, 242, 236, 0.86); color: var(--muted); border-radius: 999px; @@ -189,7 +189,7 @@ .guide { pointer-events: auto; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; background: rgba(255, 255, 255, 0.94); box-shadow: var(--shadow-lg); @@ -230,7 +230,7 @@ .guideGrid article { display: grid; gap: 4px; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 7px; background: rgba(255, 255, 255, 0.68); padding: 10px; @@ -273,7 +273,8 @@ @media (prefers-reduced-motion: reduce) { .header, .liveChipOn, - .guide { + .guide, + .agentSwitch { animation: none; } } @@ -300,3 +301,22 @@ width: calc(100% - 16px); } } + +.agentSwitch { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + animation: agentFadeIn 200ms var(--ease) both; +} + +@keyframes agentFadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/webui/src/components/Skeleton.module.css b/webui/src/components/Skeleton.module.css index add4b576..bcd5c309 100644 --- a/webui/src/components/Skeleton.module.css +++ b/webui/src/components/Skeleton.module.css @@ -4,7 +4,7 @@ background: rgba(255, 255, 255, 0.78); display: flex; flex-direction: column; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; box-shadow: none; overflow: hidden; @@ -24,7 +24,7 @@ display: flex; flex-direction: column; overflow: hidden; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; background: rgba(255, 255, 255, 0.82); box-shadow: var(--shadow-lg); diff --git a/webui/src/components/WorldSidebar.module.css b/webui/src/components/WorldSidebar.module.css index 74b4a27c..f72ea67b 100644 --- a/webui/src/components/WorldSidebar.module.css +++ b/webui/src/components/WorldSidebar.module.css @@ -6,7 +6,7 @@ background: linear-gradient(180deg, rgba(255, 255, 255, 0.82), rgba(242, 242, 240, 0.78)), var(--surface-muted); - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; display: flex; flex-direction: column; @@ -49,7 +49,7 @@ margin: 0 8px 8px; padding: 7px 8px; cursor: pointer; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 6px; background: rgba(255, 255, 255, 0.75); color: var(--muted-strong); diff --git a/webui/src/components/cells/Cells.module.css b/webui/src/components/cells/Cells.module.css index 12112650..aefe15cd 100644 --- a/webui/src/components/cells/Cells.module.css +++ b/webui/src/components/cells/Cells.module.css @@ -26,7 +26,7 @@ background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(250, 250, 250, 0.92)), var(--surface); align-self: flex-start; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); color: var(--ink); border-bottom-left-radius: 4px; position: relative; @@ -156,7 +156,7 @@ } .inlineCode { - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 4px; background: var(--surface-muted); color: var(--ink); @@ -169,7 +169,7 @@ .codeBlock { margin: 10px 0; overflow: hidden; - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 7px; background: #f7f7f5; } @@ -306,7 +306,7 @@ align-self: flex-start; background: linear-gradient(135deg, rgba(247, 247, 245, 0.82), rgba(255, 255, 255, 0.92)), var(--surface); - border: 1px solid rgba(224, 223, 220, 0.92); + border: 1px solid var(--border-subtle); border-radius: 8px; padding: 9px 11px; font-size: 12px; From 45ac91fd79d41b4f59f2b30fbc8d2ba2f692939e Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 07:56:09 +0000 Subject: [PATCH 09/10] style: replace hardcoded colors with CSS variables across components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PipelineNavigator: error/complete banners, confirm dialog, condMet → CSS variables - ChapterEditor: --hover → rgba(23,23,23,0.055), --violet → --muted-strong - HelpDrawer, WorldOnboarding, EndSceneModal: --brand-wash → --surface-muted - WorldSelector: backdrop and danger button colors → CSS variables - Sidebar: sessionItemActive border-color → --border-strong --- webui/src/components/ChapterEditor.module.css | 8 +++--- webui/src/components/HelpDrawer.module.css | 2 +- .../Inspector/EndSceneModal.module.css | 2 +- webui/src/components/Sidebar.module.css | 4 +-- .../Sidebar/PipelineNavigator.module.css | 26 +++++++++---------- .../Sidebar/WorldSelector.module.css | 10 +++---- .../src/components/WorldOnboarding.module.css | 2 +- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/webui/src/components/ChapterEditor.module.css b/webui/src/components/ChapterEditor.module.css index fcda0d54..b8980415 100644 --- a/webui/src/components/ChapterEditor.module.css +++ b/webui/src/components/ChapterEditor.module.css @@ -59,7 +59,7 @@ .contextBtn:hover, .reviewBtn:hover { - background: var(--hover); + background: rgba(23, 23, 23, 0.055); border-color: var(--border-strong); transform: translateY(-1px); } @@ -175,7 +175,7 @@ } .entityTag:hover { - background: var(--hover); + background: rgba(23, 23, 23, 0.055); border-color: var(--border-strong); transform: translateY(-1px); } @@ -191,8 +191,8 @@ } .entityTag.secret { - border-color: var(--violet); - color: var(--violet); + border-color: var(--muted-strong); + color: var(--muted-strong); } .location { diff --git a/webui/src/components/HelpDrawer.module.css b/webui/src/components/HelpDrawer.module.css index 08309efb..59f82308 100644 --- a/webui/src/components/HelpDrawer.module.css +++ b/webui/src/components/HelpDrawer.module.css @@ -128,7 +128,7 @@ margin-top: 18px; border: 1px solid var(--border); border-radius: 8px; - background: var(--brand-wash); + background: var(--surface-muted); padding: 14px; } diff --git a/webui/src/components/Inspector/EndSceneModal.module.css b/webui/src/components/Inspector/EndSceneModal.module.css index 66b7f27d..d92ad557 100644 --- a/webui/src/components/Inspector/EndSceneModal.module.css +++ b/webui/src/components/Inspector/EndSceneModal.module.css @@ -81,7 +81,7 @@ .foreshadowItem { border: 1px solid var(--border); border-radius: 10px; - background: linear-gradient(135deg, var(--brand-wash), #fff); + background: linear-gradient(135deg, var(--surface-muted), #fff); padding: 10px 12px; font-size: 12px; line-height: 1.45; diff --git a/webui/src/components/Sidebar.module.css b/webui/src/components/Sidebar.module.css index cba37162..09a7b093 100644 --- a/webui/src/components/Sidebar.module.css +++ b/webui/src/components/Sidebar.module.css @@ -138,8 +138,8 @@ .sessionItemActive { background: linear-gradient(90deg, var(--brand-soft), var(--surface)); - border-color: rgba(122, 79, 19, 0.22); - color: var(--brand); + border-color: var(--border-strong); + color: var(--ink); box-shadow: var(--shadow-sm); } diff --git a/webui/src/components/Sidebar/PipelineNavigator.module.css b/webui/src/components/Sidebar/PipelineNavigator.module.css index 21c1e22b..a97fe533 100644 --- a/webui/src/components/Sidebar/PipelineNavigator.module.css +++ b/webui/src/components/Sidebar/PipelineNavigator.module.css @@ -167,7 +167,7 @@ } .condMet { - background: var(--teal-soft, #4a8); + background: var(--green); } .condPending { @@ -175,8 +175,8 @@ } .errorBanner { - background: #fff0f0; - border: 1px solid #e00; + background: var(--ruby-soft); + border: 1px solid var(--ruby); border-radius: 4px; padding: 8px 12px; margin-bottom: 8px; @@ -184,13 +184,13 @@ justify-content: space-between; align-items: center; font-size: 13px; - color: #c00; + color: var(--ruby); } .errorBanner button { background: none; border: none; - color: #c00; + color: var(--ruby); cursor: pointer; font-size: 12px; text-decoration: underline; @@ -207,7 +207,7 @@ } .confirmDialog { - background: #fff; + background: var(--surface); border-radius: 10px; padding: 20px 24px; box-shadow: var(--shadow-md); @@ -230,7 +230,7 @@ padding: 6px 16px; border: none; border-radius: 4px; - background: #4a90d9; + background: var(--ink); color: #fff; cursor: pointer; font-size: 13px; @@ -238,16 +238,16 @@ .cancelBtn { padding: 6px 16px; - border: 1px solid #ddd; + border: 1px solid var(--border); border-radius: 4px; - background: #fff; + background: var(--surface); cursor: pointer; font-size: 13px; } .completeBanner { - background: #f0fff0; - border: 1px solid #4caf50; + background: var(--green-soft); + border: 1px solid var(--green); border-radius: 4px; padding: 8px 12px; margin-bottom: 8px; @@ -255,13 +255,13 @@ justify-content: space-between; align-items: center; font-size: 13px; - color: #2e7d32; + color: var(--green); } .completeBanner button { background: none; border: none; - color: #2e7d32; + color: var(--green); cursor: pointer; font-size: 12px; text-decoration: underline; diff --git a/webui/src/components/Sidebar/WorldSelector.module.css b/webui/src/components/Sidebar/WorldSelector.module.css index 7fbe4334..8a2d86f9 100644 --- a/webui/src/components/Sidebar/WorldSelector.module.css +++ b/webui/src/components/Sidebar/WorldSelector.module.css @@ -62,7 +62,7 @@ display: grid; place-items: center; padding: 18px; - background: rgba(15, 23, 42, 0.24); + background: rgba(23, 23, 23, 0.38); } .modal { @@ -153,12 +153,12 @@ display: inline-flex; align-items: center; gap: 6px; - border-color: #fecaca; - background: #fff1f2; + border-color: var(--color-error-border); + background: var(--color-error-bg); color: var(--ruby); } .actions .danger:hover { - border-color: #fca5a5; - background: #ffe4e6; + border-color: var(--color-error-border); + background: var(--ruby-soft); } diff --git a/webui/src/components/WorldOnboarding.module.css b/webui/src/components/WorldOnboarding.module.css index ffadcf01..77ba2965 100644 --- a/webui/src/components/WorldOnboarding.module.css +++ b/webui/src/components/WorldOnboarding.module.css @@ -450,7 +450,7 @@ padding: 14px; border: 1px solid var(--color-border); border-radius: 8px; - background: var(--brand-wash); + background: var(--surface-muted); animation: characterIn 220ms var(--ease-spring); } From 32bc947108bc4f59eb0d55a4816340de068b6008 Mon Sep 17 00:00:00 2001 From: ULookup Date: Mon, 22 Jun 2026 07:56:16 +0000 Subject: [PATCH 10/10] feat: add tab and agent switch crossfade animations - InspectorPanel: tabFadeIn animation on content area (180ms) - MainPanel: agentFadeIn animation on ChatTimeline wrapper keyed by agentId - Reduced-motion overrides for both animations --- webui/src/components/MainPanel.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webui/src/components/MainPanel.tsx b/webui/src/components/MainPanel.tsx index 82ff3356..ea9fa565 100644 --- a/webui/src/components/MainPanel.tsx +++ b/webui/src/components/MainPanel.tsx @@ -144,7 +144,9 @@ export default function MainPanel({
)} - +
+ +
);