Repository navigation
tracking(desktop): rebuild the chat surface on Astryx compositions #5793
Description
English
Problem
Scope reviewed against upstream main 7c90bac2d on 2026-10-04. Counts below describe the original audit, not the remaining work.
The Desktop chat surface already uses the Astryx chat primitives that the official ai-chat template uses: ChatLayout, ChatMessage, ghost ChatMessageBubble, ChatToolCalls, ChatMessageMetadata, ChatComposer, and Markdown. It still reads as rough and heavy next to the template, and its CSS is expensive to change.
A side-by-side capture (production Product/Shell Official AppShell stories against the template preview, 1440×1000) and a CSS audit point to one cause with two effects.
- Layout is written by hand. The template has 0 raw layout elements, 2
classNameprops, and 9 AstryxStackcalls in 763 lines. Product TSX has 557 raw layout elements, 597classNameprops, and 133Stackcalls. Every class needs a CSS rule, so renderer CSS grows with product structure: 66 files, 13,262 lines, 356display: flex/griddeclarations. - At the original audit, nothing compared pixels. test(desktop): add storybook pixel-diff capture/compare tool #5847 has since added
scripts/storybook-visual-diff.mjs;scripts/storybook-visual-smoke.mjsstill checks render completion, focus, and the accessibility tree only. At that time, visual fixes landed as local CSS patches with long rationale comments (34% of renderer CSS lines are comments).
The audit also rules out two suspected causes. Private .astryx-* overrides are few (159 occurrences, mostly in the sidebar and composer). Tokens are used consistently: font sizes, radii, and spacing almost always go through tokens.
The most visible defects on the chat surface are:
- The turn process region reads like a log table. Intermediate commentary and reasoning split tool calls into many one-call
ChatToolCallscards. Each card is 800px wide with its chevron far from its content, and 12px muted rows alternate with 14px body text. - Error feedback uses four channels with no rule for choosing one:
toast.error(154 call sites),Banner(138 uses in 51 files), hand-writtenrole="alert"(29 uses in 21 files), and in-transcript notices. The failed-turn banner uses the page-levelcontainer="section"variant, so it renders as a square, full-bleed red block inside the message column.
#4679 aligns shell chrome and settings with Astryx. It does not cover the chat transcript, and its non-goals exclude tool preview cards and the ChatReasoning eject. This tracker owns the chat surface: transcript, turn chrome, composer, and the error feedback these surfaces show.
Goal
The chat surface is composed the way the Astryx template composes it: layout through Astryx components and their props, with product CSS only where Maka adds product behavior. Every visual change on the surface is verified against a before/after pixel capture during implementation.
Work items
Verification loop
- A0 — Add a before/after pixel-capture loop for chat surface stories (test(desktop): add pixel baselines for chat surface stories #5794), delivered by merged PR test(desktop): add storybook pixel-diff capture/compare tool #5847. Local tooling used during implementation, not a CI check. The capture/compare workflow is documented in
apps/desktop/stories/AGENTS.md.
Lossless layout refactor
- A1 — Finish the remaining Composer and chat header/transcript-frame layout work (refactor(ui): replace hand-written chat layout with Astryx Stack #5795). One PR per surface. Skips the process region and failure notices, which B and C1 redesign.
- A1 — User-message layout, including attachments, quote chips and metadata (refactor(ui): move user-message layout onto Astryx primitives #5848).
- A1 — Turn chrome layout, including footer and lineage rows (refactor(ui): move turn chrome onto Astryx layout #5911).
- Shrink the quote-chip, turn-footer, and lineage
Buttonoverrides tothemeProps(moved here from tracking(desktop): align shell chrome and settings with Astryx and DESIGN.md #4679, P3.1). Open PR refactor(ui): restyle chat Buttons through themeProps targets #5928 owns this slice; do not duplicate it.
Process region
- B — Group turn process activity the way the Astryx template does (feat(ui): group turn process activity like the Astryx chat template #5796). Supersedes feat(desktop): bound expanded tool activity in the conversation #4773; relates to feat(desktop): tailor tool result rendering to the content #4863.
Error feedback
- C1 — Define one channel for each kind of error feedback, and move the transcript onto it (feat(desktop): define one channel for each kind of error feedback #5797).
- C2 — Move the remaining surfaces onto the error feedback rules (refactor(desktop): move remaining error feedback onto the channel rules #5798). Starts after C1 lands.
Independent items
- D — Render in-transcript failure and retry notices as card banners (fix(ui): render transcript failure and retry notices as card banners #5799). Can merge before C1.
Order
A0 is available. D can land before C1. The remaining A1 surfaces can proceed with the existing capture loop, outside the areas B and C1 redesign. C2 starts after C1 lands. Base Composer work on #5927 and #5954. Coordinate transcript-frame and long-group scrolling changes with #5942; preserve scroll ownership and rerun dynamic scrolling checks. B and C1 attach their own before/after captures in their PRs.
Coordinates with
- tracking(desktop): align shell chrome and settings with Astryx and DESIGN.md #4679: shell chrome and settings. This tracker takes over its P3.1 item.
- feat(desktop): tailor tool result rendering to the content #4863: tool result detail rendering stays separate from B.
- Context-compaction running toast is not settled on error / abort / replacement reconnect #4854, Desktop shows a generic failure when the Host refuses a Branch or edit-and-resend #5762, Desktop: task refresh fails with opaque persistence_failed; the real transcript error is discarded #5572: specific error feedback defects that C1 and C2 should absorb or unblock.
- bug(desktop): prompt anchor rail overlaps user messages at narrow widths #5615: the prompt anchor rail overlap stays separate.
Non-goals
- A new palette or type scale.
- Changes to transcript projection, streaming, or scroll ownership.
- Changes to plugin slot contracts. Any composer change that touches
conversation.composer.toolbarneeds its own issue. - A bulk removal of CSS comments.
Upstream already uses @astryxdesign/core 0.6.3; #5839 refreshed the patches and #5860 corrected the Spinner animation after the re-port. The former 0.6.3 deferral no longer applies.
Verification
- Every PR in A1 attaches before/after captures produced by the A0 loop and shows zero pixel difference, or lists each accepted difference with its cause.
- Every PR in B, C1, C2, and D includes before and after captures in light and dark produced by the A0 loop.
docs/astryx-surface-file-inventory.mdstays at blocker 0.
中文
问题
2026-10-04 已按 upstream main 7c90bac2d 核对范围。下方统计来自原始审计,不代表当前剩余工作量。
桌面端对话界面已经使用了 Astryx 官方 ai-chat 模板所用的同一套对话原语:ChatLayout、ChatMessage、ghost 形态的 ChatMessageBubble、ChatToolCalls、ChatMessageMetadata、ChatComposer 和 Markdown。但和模板相比,它看起来仍然粗糙、笨重,CSS 的修改成本也很高。
我们把生产环境的 Product/Shell Official AppShell 故事和模板预览放在 1440×1000 下对比截图,并审计了 CSS。结论是一个根因造成了两个结果。
- 布局是手写的。模板 763 行里没有原生布局元素,只有 2 个
className和 9 处 AstryxStack。产品 TSX 里有 557 个原生布局元素、597 个className,Stack只有 133 处。每个 class 都需要一条 CSS 规则,所以渲染层 CSS 随产品结构线性增长:66 个文件,13,262 行,356 条display: flex/grid声明。 - 原始审计时没有像素对比。test(desktop): add storybook pixel-diff capture/compare tool #5847 此后已加入
scripts/storybook-visual-diff.mjs;scripts/storybook-visual-smoke.mjs仍只检查渲染完成、焦点和无障碍树。当时视觉修正以局部 CSS 补丁的形式落地,并附有很长的理由注释(渲染层 CSS 有 34% 的行是注释)。
审计同时排除了两个被怀疑的原因。私有 .astryx-* 覆盖很少(159 处,主要在侧栏和输入框)。token 使用一致:字号、圆角和间距几乎都走 token。
对话界面上最显眼的问题是:
- 每轮的过程区读起来像日志表格。中间的说明文字和思考把工具调用切成许多只有一个调用的
ChatToolCalls卡片。每张卡 800px 宽,展开箭头离内容很远;12px 的灰色行和 14px 的正文交替出现。 - 错误提示有四种渠道,却没有选择规则:
toast.error(154 处调用)、Banner(51 个文件共 138 处)、手写的role="alert"(21 个文件共 29 处),以及对话流内的提示。失败轮次的横幅用的是页面级的container="section",所以在消息栏里显示成直角、满宽的红色色块。
#4679 负责让外壳和设置页对齐 Astryx。它不覆盖对话流,其非目标还明确排除了工具预览卡片和自研的 ChatReasoning。本追踪 issue 负责对话界面:对话流、轮次外框、输入框,以及这些界面上的错误提示。
目标
对话界面按 Astryx 模板的方式组合:布局交给 Astryx 组件及其属性,产品 CSS 只用于 Maka 自己增加的产品行为。界面上的每一次视觉修改,都在实施过程中用前后截图对比来验证。
工作项
验证工具
- A0 — 为对话界面故事提供修改前后像素截图的对比流程(test(desktop): add pixel baselines for chat surface stories #5794),已由 PR test(desktop): add storybook pixel-diff capture/compare tool #5847 合并交付。这是实施时使用的本地工具,不是 CI 检查。截图与比较流程已记录在
apps/desktop/stories/AGENTS.md。
无损布局重构
- A1 — 完成剩余的输入框及对话标题栏/对话流外框布局迁移(refactor(ui): replace hand-written chat layout with Astryx Stack #5795)。每个区域一个 PR。跳过过程区和失败提示,它们由 B 和 C1 重新设计。
- A1 — 用户消息布局,包括附件、引用标签和元数据(refactor(ui): move user-message layout onto Astryx primitives #5848)。
- A1 — 轮次外框布局,包括页脚和分支标记行(refactor(ui): move turn chrome onto Astryx layout #5911)。
- 把引用标签、轮次页脚和分支标记的
Button覆盖收缩为themeProps(从 tracking(desktop): align shell chrome and settings with Astryx and DESIGN.md #4679 的 P3.1 移入)。开放中的 PR refactor(ui): restyle chat Buttons through themeProps targets #5928 负责这个切片,不重复实现。
过程区
- B — 按 Astryx 模板的方式对轮次过程活动分组(feat(ui): group turn process activity like the Astryx chat template #5796)。取代 feat(desktop): bound expanded tool activity in the conversation #4773;与 feat(desktop): tailor tool result rendering to the content #4863 相关。
错误提示
- C1 — 为每类错误提示规定唯一渠道,并先迁移对话流(feat(desktop): define one channel for each kind of error feedback #5797)。
- C2 — 把其余界面迁移到错误提示规则上(refactor(desktop): move remaining error feedback onto the channel rules #5798)。在 C1 合并后开始。
独立工作项
- D — 把对话流内的失败和重试提示改为卡片横幅(fix(ui): render transcript failure and retry notices as card banners #5799)。可以先于 C1 合并。
顺序
A0 已可用。D 可以先于 C1 合并。剩余 A1 区域使用现有截图流程推进,只处理 B 和 C1 不会重新设计的区域。C2 在 C1 合并后开始。输入框工作基于 #5927 和 #5954;对话流外框和长工具组滚动变更需与 #5942 协调,保留滚动控制权并重跑动态滚动检查。B 和 C1 在各自的 PR 里附上前后对比截图。
协同
- tracking(desktop): align shell chrome and settings with Astryx and DESIGN.md #4679:外壳和设置页。本追踪 issue 接手其中的 P3.1。
- feat(desktop): tailor tool result rendering to the content #4863:工具结果详情的渲染与 B 分开处理。
- Context-compaction running toast is not settled on error / abort / replacement reconnect #4854、Desktop shows a generic failure when the Host refuses a Branch or edit-and-resend #5762、Desktop: task refresh fails with opaque persistence_failed; the real transcript error is discarded #5572:具体的错误提示缺陷,C1 和 C2 应吸收或为其扫清障碍。
- bug(desktop): prompt anchor rail overlaps user messages at narrow widths #5615:提示锚点导轨的遮挡问题单独处理。
非目标
- 新的调色板或字号阶梯。
- 修改对话流投影、流式输出或滚动控制权。
- 修改插件槽契约。任何涉及
conversation.composer.toolbar的输入框改动都需要单独的 issue。 - 批量删除 CSS 注释。
上游已使用 @astryxdesign/core 0.6.3;#5839 更新了补丁,#5860 修正了重新移植后的 Spinner 动画。原先的 0.6.3 延期说明已不适用。
验证
- A1 的每个 PR 附上 A0 流程产出的前后截图并做到零像素差异,或逐条列出接受的差异及原因。
- B、C1、C2 和 D 的每个 PR 附上 A0 流程产出的浅色和深色模式前后对比截图。
docs/astryx-surface-file-inventory.md保持 blocker 为 0。
Activity
refactor(ui): restyle chat Buttons through themeProps targets
refactor(ui): restyle chat Buttons through themeProps targets (#5928)