Skip to content

refactor(ui): unify styles, animations and shared components across Desktop, Gateway and agent-ui - #824

Open
Loosand wants to merge 88 commits into
mainfrom
develop
Open

Loosand wants to merge 88 commits into
mainfrom
develop

Conversation

@Loosand

@Loosand Loosand commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

本次重构统一 Desktop GUI、Gateway WebUI 与共享 agent-ui 的样式、动效和交互基础设施,集中管理颜色、尺寸与动画,减少重复组件、分散 CSS 和命令式 DOM 实现。

同时整理聊天、设置、Skills、MCP 和工作区相关页面,改善布局一致性、响应式适配与交互反馈,并更新对应测试及工程检查配置。

样式架构

  • 统一使用 Tailwind CSS v4,将业务样式迁移到实际消费组件,减少独立 CSS 文件与跨页面样式耦合。
  • 通过 tokens.csssemantic-colors.cssanimations.css 集中维护主题、语义颜色、特殊视觉值与基础动画。
  • 统一常用字号、间距、圆角、按钮尺寸和输入控件规格,清理无引用样式、重复颜色及多余覆盖。
  • 整理亮暗主题、移动端断点、安全区、焦点状态和 reduced-motion 规则。
  • 保留编辑器、终端、第三方生成节点等需要专门维护的样式边界。

共享组件与交互能力

  • 统一 Button、Input、Textarea、Switch、Tabs、RadioGroup、Dialog、Dropdown、Popover、Tooltip 等基础组件的使用方式。
  • 提取 FormField、ChoiceCard、SettingsNotice、SettingsPanel、SettingsSurface 等设置展示组件,减少页面重复结构。
  • 统一空态、加载态、骨架屏、状态徽标及复制反馈。
  • Desktop 与 Gateway 接入共享 Toast,集中处理通知更新、关闭、撤销操作和定时器清理。
  • 合并分散的剪贴板 fallback 与文本复制逻辑。
  • 使用 Motion 处理折叠、弹层、卡片重排及列表排序,清理重复的动画计时器和手工布局测量。

主要页面改动

聊天与会话

  • 调整聊天输入框、模型选择器、会话侧栏、顶部操作区和会话统计栏的布局与展示。
  • 整理消息、推理折叠、工具结果、附件、图片预览、任务进度和 mention 弹层的样式与交互。
  • 优化 Markdown、代码高亮与 Mermaid 展示,并调整流式内容渲染。
  • 统一会话搜索、分享弹窗、顶部页签及相关菜单的基础组件。
  • 修复部分弹层开关动画、焦点恢复和触屏输入场景的问题。

设置与配置

  • 整理 Provider、模型、SSH、Cron、Hooks、系统、远程、CUA、STT、Memory 和备份同步等页面。
  • 统一字段、标签、说明、错误提示、开关、选择卡片和操作按钮的展示方式。
  • 调整模型选择、供应商配置和高级设置布局,改善窄窗口下的换行与操作区排列。
  • 拆分快捷键设置中的键盘布局、渲染与主题样式,移除运行时整段 CSS 注入。
  • 修复设置浮层重开闪烁、资源开关事件冒泡等交互问题。

Skills、MCP 与资源管理

  • 整理已安装列表、商店、导入页和资源卡片,提取可复用的展示组件。
  • 统一筛选、搜索、排序、启停、安装、卸载及批量操作的反馈。
  • 使用共享布局动画处理卡片重排,减少手工维护的测量与计时逻辑。
  • 复用缓存及在途请求,减少页面切换时的空列表和加载跳动。
  • 整理卡片焦点样式、操作区布局和通知展示。

Workbench、Git、终端与 SFTP

  • 使用显式节点引用处理工作区命中测试,减少全局 DOM 查询。
  • 统一拖拽和缩放时的鼠标样式、文本选择限制及退出后的恢复逻辑。
  • 整理 Git Review 滚动条实现,集中维护终端主题与尺寸配置。
  • 复用文本框自动增高逻辑。
  • 拆分 SFTP 路径导航与路径计算,减少主面板职责。

Desktop 与 Gateway WebUI

  • 同步共享组件在桌面端与 WebUI 中的接入方式。
  • 整理 Gateway 登录、聊天、共享历史、设备管理和状态仪表盘页面。
  • 改善移动端聊天、侧栏、设置弹窗及安全区适配。
  • 调整桌面标题栏、右键菜单、加载状态和通知适配,并补充 Windows 兼容性修复。

工程配置与测试

  • 合并分散的 Biome 配置,统一前端格式化与 lint 入口。
  • 提取共享 TypeScript 配置,统一 GUI、WebUI、agent-ui 等模块的检查选项。
  • 更新 CI、Docker 构建及开发脚本相关配置。
  • 补充和调整样式约束、组件交互、键盘操作、布局、缓存与流式渲染测试。

重点复测范围

本次改动包含字号、间距、状态色、投影、动画节奏和滚动条表现的调整,建议重点检查:

  • 亮暗主题、窄窗口、移动端和字号缩放下的布局。
  • 聊天发送、流式回复、Markdown/Mermaid、mention 输入及会话切换。
  • 设置表单保存、模型选择、弹层重开和键盘焦点。
  • Skills/MCP 的筛选、安装、启停、排序和批量操作。
  • 工作区拖拽、面板缩放、Git 差异滚动、终端复制及 SFTP 导航。
  • reduced-motion、键盘导航、禁用态和 Windows 下的交互表现。

Loosand and others added 30 commits August 19, 2026 15:50
- Introduced macOS DMG settings in tauri.conf.json, including custom background image and window/app positioning.
- Added a new background image for the DMG installer.
…tion scripts

- Added macOS DMG settings to the Makefile, including background image and volume icon configurations.
- Introduced a new Python script for deterministic DMG layout, ensuring stable Finder metadata.
- Created a verification script to check the integrity of the generated DMG, ensuring required files and symlinks are present.
- Updated GitHub Actions workflow to install the necessary dmgbuild tool for building DMGs.
@yyg-max yyg-max added the governance-exempt Skip PR governance checks label Sep 18, 2026
@yyg-max yyg-max changed the title Develop refactor(ui): unify styles, animations and shared components across Desktop, Gateway and agent-ui Sep 18, 2026
@yyg-max
yyg-max marked this pull request as ready for review September 18, 2026 03:23
coder-hhx and others added 24 commits September 18, 2026 12:00
tauri-bundler aborts the msi bundle for tags like v1.3.6-beta.1 because Windows
Installer requires a numeric-only ProductVersion:

    optional pre-release identifier in app version must be numeric-only and
    cannot be greater than 65535 for msi target

The generated tag overlay now also injects bundle.windows.wix.version
(1.3.6-beta.1 -> 1.3.6.1, build taken from the last numeric prerelease
identifier), so the MSI builds while the app version keeps its tag semantics for
the About page, the updater and artifact names. Stable tags and numeric-only
prereleases (v1.3.6-1) are untouched and still derive from Tauri.
DeepSeek's /responses reply carries the chain of thought as reasoning output
items and responseText() concatenated the text of every item, so
v1.3.6-beta.1 published its thinking as the release notes. normalizeMarkdown()
hid the mistake by prepending the H1 to whatever text came back.

Reasoning items and parts are now dropped, and the notes must start with the
mandated `# LiveAgent <tag>` heading: anything before it is trimmed, and output
that never reaches it falls back to the GitHub generated notes instead of
publishing unverified text. The output budget also grew to 8000 tokens
(AI_RELEASE_NOTES_MAX_OUTPUT_TOKENS) so a reasoning pass cannot consume the
whole completion and leave no room for the notes.
develop 引入的 Sidebar 原语默认给左侧栏加 border-r,深色主题下侧栏底色是纯黑而
--border 亮度 26%,分界处形成一条扎眼的亮线;浅色主题下两者亮度只差 4 个点所以
看不出来。只在 chat-history-sidebar 上给深色模式把边框透明度降到 30%,不改原语、
不影响浅色模式和布局。

Fixes #827
fix(ui): 深色模式下侧边栏与主面板分界线过亮
Improve neutral dark-theme contrast, model group folding and scrollbars, sidebar spacing, and rounded standard dock controls. Remove unnecessary Skills layout animations.

Capture transcript position before switching conversations, restore measured message anchors, and preserve follow state without forcing outgoing chats to the bottom. Add regression coverage and desktop before/after previews.

Closes #830
统一深色控件、模型选择器、聊天侧栏和圆角工具页签;移除多余动画。在切换前保存对话阅读位置,返回时恢复消息锚点与底部跟随状态。包含回归测试及桌面前后截图,不引入独立 Git Graph。相关本地检查及 GitHub CI 已通过。关联需求 #830
打开设置浮层时,AppChrome 会在 flex 列里额外插入一个 32px 的
WindowsTitleBar。它是普通兄弟节点而非绝对定位,下面承载 ChatPage 的
flex-1 容器因此当场少掉 32px;关闭时再还回去。ChatPage 全程未卸载,
只是被压扁重排,于是每次切换都能看到整页上下位移。

跳变还和 300ms 的淡入淡出错位:visible 首帧即为 true(标题栏立刻
插入),active 要等两个 rAF 后才置位(浮层才开始淡入);关闭时标题栏
要等 transitionend 才移除,位移正好落在动画播完、视野最清晰的一刻。

改为让浮层自带标题栏,外层不再随开关增删:
- 删除 AppChrome 的 standaloneTitleBar 条件渲染与对应 prop
- 浮层加 flex flex-col 并在内部渲染 WindowsTitleBar,另套一层
  min-h-0 flex-1 收住 SettingsShell 的 h-full(AppErrorBoundary
  无错时直接透传 children,否则设置内容会顶穿标题栏)

顺带修掉两处同源风险:
- macOS:--app-header-height 原先由 ChatPage 子树里的
  MacOsTitleBarToggle 写入、卸载时 removeProperty,子树一旦重挂
  header 高度就会掉回 48px 兜底。抽成 useMacOsAppHeaderHeight() 挂在
  App 根,卸载写回默认值;tokens.css 的 :root 补静态默认值
- Web:gateway shell 与设置浮层各自堆叠了 h-100vh/h-100svh/
  [height:100dvh],谁生效取决于类名顺序,而 svh 与 dvh 在移动端
  地址栏收起时不等值。统一收敛到 --app-viewport-height,
  由 @supports 决定取 dvh 还是 vh

验证:两端 tsc 通过;test/settings 三个浮层测试全绿;Web 端 vite build
产物中 100svh 仅剩 token 定义、无选择器引用。
test/chat/standard-overlays.test.mjs 在干净基线上同样失败(Windows 下
virtual-core 路径解析问题),与本次改动无关。
在 SettingsShell 里接入 useSettingsEscapeToClose,桌面端与 Web 端共用
同一条路径。返回按钮补 aria-keyshortcuts="Escape",桌面端侧栏按钮右侧
增加 Esc 角标与 title 提示。

监听挂在 window 而不是 document,这一点是有意为之。设置页内部已有多处
ESC 消费方——Base UI 的 Dialog、下拉弹层、内联重命名输入框——它们必须
先拿到按键。这些层都绑在 document 上,而同一目标上的监听器按注册顺序
触发,设置页先于弹窗挂载,因此绑 document 会让本 hook 抢先执行,一次
按键既关弹窗又退回对话。window 在冒泡路径上恒位于 document 之后,与
订阅时机无关,把顺序从挂载巧合变成结构保证。

这个缺陷是先写测试才暴露的:初版绑 document,"被消费的 ESC 不应再关闭
设置页"一例直接失败(closes 为 2)。

其余守卫:
- defaultPrevented:Base UI useDismiss 关闭未被取消时会 preventDefault,
  据此让位给嵌套层
- repeat:长按 ESC 不应按重复节拍反复触发
- isComposing:输入法组字期间的 ESC 用于取消组字,不退出页面

未排除文本输入框:设置项里按 ESC 语义即"我改完了",而确实需要独占该键的
字段(供应商模型搜索、内联重命名)自身已 stopPropagation。

测试覆盖 hook 的各分支、卸载后不再残留监听,以及 SettingsShell 的集成
链路与无障碍属性。test/settings 下 21 个用例全部通过。
普通点选会话只走 openController.open,没有把会话所属工作空间置为当前,
右侧 dock 的文件树/终端/Git 仍停在上一个工作空间。从搜索路径抽出
activateConversationWorkspace,Desktop 与 WebUI 的普通点选分支在打开前
按会话 cwd 激活对应工作空间;同工作空间或无 cwd 时不动。

Refs #787
点选前先激活工作空间会让侧栏作用域先行刷新,旧的当前会话从列表消失
触发兜底新建草稿,把这次打开顶掉——表现为要点两次。改为会话提交后
(afterCommit)再激活其工作空间;Desktop 的 cache-hit 分支补调 afterCommit,
热路径同样生效。cwd 优先取运行时/历史响应的权威 workdir。

Refs #787
The file views walker (fs_list/fs_glob/fs_grep/fs_mention_list) applied
gitignore rules even when the workspace is not a git repository
(require_git(false)) and also honored ignore files in ancestor
directories outside the workspace root (WalkBuilder's default
parents(true)). Entries matching such rules silently vanished from the
file tree - e.g. digit-leading folders matched by a stray [0-9]* rule,
or folders created in Explorer that matched an ancestor's gitignore.

- parents(false): the workspace root is the semantic boundary; outside
  ignore rules no longer leak in.
- require_git(true): gitignore/global/exclude filtering only applies
  inside an actual git repository.
- Existing gitignore-dependent test fixtures now initialize a fake .git
  directory; two regression tests cover the non-repo and ancestor-leak
  cases.

Fixes #820
resizeItem 的 wasAtEnd 此前用 getVirtualDistanceFromEnd()(列表局部坐标)判
"在末端"。宿主在虚拟列表下方还渲染了 18px shell padding 与 194px 底部占位,
列表末端离真实 clamp 约 212px,于是脱离跟随、停在距底 220px 内的读者都被
当成"在末端",任何底部行尺寸变化都经 applyScrollAdjustment 直写 scrollTop:
AskUserQuestion 卡片到达视觉位移 305px,审批请求 63px,末行流式增高 302px。

改用 getDistanceFromEnd()(DOM scrollHeight 口径,与 _willUpdate 里
followOnAppend 的 isAtEnd 一致)并加 scrollElement 判空;删除不再使用的私有
getVirtualDistanceFromEnd,types 同步并为 scrollEndThreshold 补口径说明。

测试:harness 新增 extraScrollHeight / maxScrollOffset 模拟列表下方的非虚拟
高度;end-anchor-distance 4 例覆盖带内不写、带边不写、真实 clamp 仍钉底、
默认 harness 语义不变。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
主转写区的 useScrollFollow 沿用默认 192px 回贴区:滚轮下行进入该区间的那一
tick 直接 pin 到底,读者看到正文突然上跳(复现页实测单 tick 231px)。改为
reattachZonePx: 0,只在真正到达底部(8px 容差)时恢复跟随;到达底部后向下
滚轮、手势落底、指针在底部释放仍会重新贴底。

工作台背景 Pane 原本手写一套贴底:距底不足 48px 即视为跟随,行数或 revision
一变就写 scrollTop,读者停在底部附近会被每次流式增量吸回,且与主 Pane、
桌面端各 Pane 的引擎语义不一致。换成同一 useScrollFollow(zone 0,会话切换
stickToBottom),ScrollArea 根与视口经 callback ref 接入,删除三处 scrollTop
写入。

新增 transcript-scroll-follow-contract 源码契约测试锁住两处配置。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
scroll-follow-probe.html 挂真实 ScrollArea + GatewayTranscript + useScrollFollow,
拦截 scrollTop/scrollTo 写入并按调用栈归因(follow-engine / virtualizer /
base-ui),用视口顶部那一行的屏幕位移衡量肉眼可见的跳动;?zone= 试回贴区,
?ask=1|2 与 ?approval=1|2 构造挂起的询问卡片与审批请求。
scroll-follow-probe.cdp.mjs 用 Node 内建 WebSocket 直连无头 Chromium 的 CDP,
逐 tick 发滚轮事件记录每步位移与写入方;scroll-follow-probe.eval.mjs 对已
打开的页面即时求值。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
桌面端 ChatTranscript 的 useScrollFollow 显式传 reattachZonePx: BOTTOM_REATTACH_ZONE_PX,
滚轮下行进入距底 192px 的那一 tick 直接 pin 到底,从本分支起的开发版上仍能看到
"快到最新回复结尾时上跳一下"。与 WebUI 同口径改为 0:只在真正到达底部(8px 容差)
时恢复跟随,到达底部后向下滚轮、手势落底、指针在底部释放仍会重新贴底。

BOTTOM_REATTACH_ZONE_PX 保留:它仍是桌面端底部预留带与 WebUI spacer 的最小值,
scrollFollowCore 的注释改写为现状说明。chat-transcript-scroller 契约测试新增一例
锁住零回贴区配置与预留带取值。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The tools panel content kept its saved pixel width anchored right. When the
group shrank (left sidebar expanded) the panel kept its percentage share and
got narrower, so the fixed-width content overflowed to the left and was
clipped by the panel's overflow: hidden, looking as if the main column
covered it. Only keep the fixed width during the open transition; once
settled, size the content to 100% of the panel.
The Git Review branch dropdown listed every local/remote branch with no way
to narrow it down, unlike the composer GitBranchSelector. Add a filter input
at the top of the menu that matches case-insensitively on the full ref name,
resets on close, keeps keystrokes out of the menu typeahead, and shows the
existing no-match / hidden-remote hints.

Closes #842
feat(git-review): 分支切换下拉增加筛选输入框
…ight-jump

fix(ui): 消除对话页与系统设置切换时的高度跳变,并支持 ESC 返回
…evelop

fix(chat): 侧栏点选跨工作空间会话时同步切换工作空间与右侧文件树
…evelop

fix(ui): 右侧工具栏在左侧边栏展开时内容跟随面板宽度重排
…pe-develop

fix(fs): 文件树 ignore 规则限定在工作空间自身的 git 仓库内
…jump-develop

fix(chat): 对话区接近底部时正文突然上跳:去掉回贴区吸附并修正末端锚定口径
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

governance-exempt Skip PR governance checks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants