Skip to content

chore(ui): track Storybook-driven UI polish after file-boundary governance #390

Description

@Astro-Han

Context

#363 is closed. That issue was about file boundaries: move unrelated responsibilities out of sink files before any visual polish. The work landed through #370, #372, #377, #378, #381, #383, and #385.

That gives us a cleaner codebase to change, but it does not make the UI easier to judge. The next phase needs a different habit: put the important states of each surface into Storybook first, then polish against those states.

This issue exists to make UI polish reviewable. If a PR changes spacing, hierarchy, copy density, focus treatment, or responsive behavior, reviewers should be able to see the affected states without driving the whole app into that state by hand.

It also keeps boundaries clear. The app-shell.tsx boundary cleanup is already separate in-flight work and should stay non-visual. It may unlock renderer-owned fixtures for command/search/settings stories, but it should not block stories for components that already live under packages/ui.

Completed precedent:

Current Evidence

Current main has clearer file boundaries and a Storybook baseline. It does not yet have enough product-surface stories for polish work. Several user-facing surfaces have states that are hard to compare safely in the running app alone:

Surface Why it needs Storybook before polish
Sidebar / SessionListPanel Dense navigation, empty/search/filter states, row actions, blocked/running/stale status, long titles, and narrow width.
Chat / ChatView + Composer Empty chat, streaming, tool activity, branch banners, import actions, and disabled/pending composer states.
ToolActivity Tool-result variants, errors, truncation, overlays, subagent/explore/web-search/file-diff/terminal previews.
Command palette / search modal Keyboard-heavy surfaces with grouped results, empty/loading/error/disabled states, and content search results.
Settings pages Bot, memory, provider, and permission-center pages have async state, validation, pending actions, and long copy.

Calibration counter-example: a large file or component does not automatically belong here. A surface enters this issue only when Storybook can expose user-visible states that would otherwise be hard to inspect, compare, or regression-test.

Order Of Work

  1. Keep the app-shell.tsx cleanup separate and non-visual. It is not a global blocker for Storybook work. packages/ui surfaces can use local fixture data now; command/search and settings stories may wait for extracted renderer-shell fixtures if needed.
  2. Storyboard one product surface at a time. Add or extend stories for real states before changing visuals.
  3. Pair polish with covered states. Once a surface has a state board, refine spacing, hierarchy, copy density, affordances, and responsive behavior for that same surface.
  4. Keep PRs surface-scoped. One PR should cover one surface or one tight family of states, with screenshots or Storybook references in the PR body.
  5. Keep labels sequential. Use PR1, PR2, PR3, and so on. Do not use sub-labels for issue tracking.

Invariants

  • Storybook is a design and regression aid, not a replacement for production tests.
  • Do not redesign unrelated surfaces while polishing one surface.
  • Do not add visual-only abstractions before stories prove the pattern repeats.
  • Cover important states first: empty, loading, error, disabled, pending, streaming, long content, narrow viewport, and destructive/confirmation flows where relevant.
  • Preserve accessibility contracts: keyboard paths, focus behavior, labels, and reduced-motion expectations.
  • Avoid broad CSS rewrites unless they are tied to the current surface and verified against its stories.
  • Use existing UI primitives and tokens unless a story shows a concrete gap.

Slices

  • PR1: Sidebar/session-list storyboard. Shipped in test(ui): add sidebar session-list stories #393. Cover SessionListPanel states: empty, long list, search/filter, active row actions, blocked/running/stale status, long titles, and narrow width.

  • PR2: Chat surface storyboard. Shipped in test(ui): add chat surface stories #394. Cover ChatView + Composer states: empty chat, streaming, tool activity present, branch banner, pending/disabled composer, import actions, and long messages.

  • PR3: ToolActivity storyboard. Shipped in test(ui): add tool activity stories #395. Cover core tool-result variants: terminal, file diff, web search, subagent, explore agent, office document, errors, truncation, overlay preview, and copy feedback.

  • PR4: Command/search storyboard. Shipped in test(ui): add command search stories #402. Cover command palette and content search modal as one keyboard/modal family: empty, loading, grouped results, disabled command, error, blocked privacy state, keyboard-focused selection, and no-results.

  • PR5: Settings critical-page storyboard. Shipped in test(ui): add provider settings stories #401 for the provider/OAuth settings family. Start with the highest-risk settings page, and keep the PR scoped to one page or one tight settings family. Cover bot settings, memory settings, provider/OAuth details, or permission center only where async state or validation makes polish risky.

  • PR6: First paired surface-scoped polish PR. In progress in refactor(ui): migrate sidebar visual properties to cva variants #403. After one surface's stories exist, polish that same surface with screenshots/story references and narrow verification. Later settings/storyboard or polish work should continue with the next integer PR label.

Out Of Scope

  • Reopening refactor: split sink files before visual polish #363 or bundling more sink-file governance into this issue.
  • Polishing production UI before the relevant story states exist.
  • Full visual redesign, brand refresh, or navigation IA rewrite.
  • Changing Electron main/preload/storage/runtime behavior except for small story fixture support if proven necessary.
  • Making Storybook mandatory CI until a separate decision says it is stable enough.
  • Splitting large files solely for line count.

References

中文对照

背景

#363 已经关闭。那个 issue 处理的是文件边界:先把 sink files 里混在一起的职责拆开,再进入视觉打磨。相关工作已经通过 #370#372#377#378#381#383#385 落地。

这让代码更好改,但不等于 UI 更好判断。下一阶段要换一种工作方式:先把每个产品表面的关键状态放进 Storybook,再对着这些状态做打磨。

这个 issue 是为了让 UI polish 变得可 review。如果一个 PR 改了 spacing、层级、文案密度、focus 处理或响应式行为,reviewer 应该能直接看到受影响的状态,而不是手动把整个 app 开到那个状态。

这也用来保持边界清楚。app-shell.tsx 的边界治理已经是单独进行中的工作,并且应该保持非视觉。它可能会为 command/search/settings stories 解出 renderer-owned fixtures,但不应该阻塞已经在 packages/ui 里的组件 stories。

已经完成的前置工作:

当前证据

现在的 main 已经有更清楚的文件边界,也有 Storybook baseline。但要进入 UI polish,还缺产品表面的状态 stories。几个用户经常接触的区域,只靠运行中的 app 很难稳定比较这些状态:

Surface 为什么 polish 前需要 Storybook
Sidebar / SessionListPanel 导航密度高,有 empty/search/filter、row actions、blocked/running/stale status、长标题和窄宽度状态。
Chat / ChatView + Composer 有空对话、streaming、tool activity、branch banner、import actions、pending/disabled composer 等状态。
ToolActivity 有多种 tool-result、错误、截断、overlay、subagent/explore/web-search/file-diff/terminal previews。
Command palette / search modal 高度依赖键盘,有 grouped results、empty/loading/error/disabled states 和 content search results。
Settings pages Bot、memory、provider 和 permission-center 页面有 async state、validation、pending actions 和长文案。

校准反例:文件大或组件大,不代表它自动属于这个 issue。只有当 Storybook 能暴露用户可见状态,而且这些状态在 running app 里难以检查、比较或回归验证时,这个 surface 才进入这里。

工作顺序

  1. app-shell.tsx 治理保持独立,并且保持非视觉。它不是所有 Storybook 工作的全局 blocker。packages/ui 里的 surface 现在就可以用 local fixture data 做 stories;command/search 和 settings stories 如果需要 renderer-shell fixtures,可以等对应抽取落地。
  2. 一次只 storyboard 一个产品表面。先为真实状态添加或扩展 stories,再改视觉。
  3. storyboard 和 polish 配对。有了某个 surface 的 state board 之后,再打磨同一个 surface 的 spacing、层级、文案密度、affordance 和响应式表现。
  4. PR 按 surface 切。一个 PR 覆盖一个 surface,或者一组关系很近的状态,并在 PR body 里放 screenshots 或 Storybook references。
  5. PR 标签按整数顺序走。用 PR1PR2PR3 这种,不用子编号。

不变约束

  • Storybook 是设计和回归辅助,不替代 production tests。
  • 打磨一个 surface 时,不顺手 redesign 其他 surface。
  • stories 证明模式重复之前,不新增只服务视觉的 abstraction。
  • 先覆盖重要状态:empty、loading、error、disabled、pending、streaming、long content、narrow viewport,以及相关的 destructive/confirmation flows。
  • 保留 accessibility contracts:keyboard paths、focus behavior、labels 和 reduced-motion expectations。
  • 避免大范围 CSS rewrite,除非它和当前 surface 直接相关,并且已经用 stories 验证。
  • 默认使用现有 UI primitives 和 tokens,除非 story 暴露了具体缺口。

拆分切片

PR1: Sidebar/session-list storyboard。已由 #393 合并。覆盖 SessionListPanel 的 empty、long list、search/filter、active row actions、blocked/running/stale status、长标题和窄宽度状态。

PR2: Chat surface storyboard。已由 #394 合并。覆盖 ChatView + Composer 的 empty chat、streaming、tool activity present、branch banner、pending/disabled composer、import actions 和 long messages。

PR3: ToolActivity storyboard。已由 #395 合并。覆盖核心 tool-result variants:terminal、file diff、web search、subagent、explore agent、office document、errors、truncation、overlay preview 和 copy feedback。

PR4: Command/search storyboard。已由 #402 合并。把 command palette 和 content search modal 作为一组 keyboard/modal family 覆盖:empty、loading、grouped results、disabled command、error、blocked privacy state、keyboard-focused selection 和 no-results。

PR5: Settings critical-page storyboard。已由 #401 合并,覆盖 provider/OAuth settings family。从风险最高的 settings page 开始,并且把 PR 限定在一个页面或一组关系很近的 settings family。只在 async state 或 validation 让 polish 有风险的地方覆盖 bot settings、memory settings、provider/OAuth details 或 permission center。

PR6: 第一个配对的 surface-scoped polish PR。#403 进行中。有某个 surface 的 stories 之后,再打磨同一个 surface,PR 里带 screenshots/story references,并做窄范围 verification。后续 settings/storyboard 或 polish 工作继续用下一个整数编号。

不在范围内

  • 不重新打开 refactor: split sink files before visual polish #363,也不把更多 sink-file governance 塞进这个 issue。
  • 相关 story states 出来之前,不直接 polish production UI。
  • 不做完整视觉 redesign、brand refresh 或 navigation IA rewrite。
  • 不改 Electron main/preload/storage/runtime behavior,除非为了 story fixture 做很小的支撑,并且已经证明必要。
  • 不把 Storybook 放进 mandatory CI,除非后续单独确认它已经足够稳定。
  • 不因为文件行数大就拆文件。

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions