GitTreeDesk — 规格说明书
状态:草案 v0.1 · 2026-10-13 本文档以英文版
spec.md为准,本文件为同步翻译;两者冲突时以英文版为准。
概述
问题背景
在包含几十到上百个仓库的大文件夹中做 Git 相关工作,目前被割裂在 多个工具里,每个只覆盖一部分:
- VSCode 为单仓库优化。打开一个含 50+ 仓库的文件夹时,它会触发全工作区文件监听、逐仓库的 SCM provider、以及对所有"看起来像项目"的目录做 TypeScript 工程解析——"完整体验"本身就正是它卡的原因。
- Git GUI(SourceTree、Fork、GitKraken)单仓库操作体验好,但没有工作区优先的浏览、没有覆盖整个文件夹的文件树、没有内置编辑器,对散落在兄弟目录里的 worktree 支持也很弱。
GitTreeDesk 把这些有用的部分合进一个工具,并且把工作区——而不是仓库——作为工作单元。
产品定位
GitTreeDesk 是工作区优先的多仓库 Git 工作台,不是通用 IDE。
三条原则主导设计取舍:
- 工作区优先。 用户打开的文件夹就是一个工作区,它本身不必是 Git 仓库,里面可以是任意混合:任意深度嵌套的 Git 仓库、链接 worktree、submodule、普通文件夹。
- 默认懒加载 + 增量。 不需要就不扫描、不监听、不解析、不渲染;已知信息只做增量刷新,绝不从头重算。
- 键盘优先、克制的 UI。 每个操作都能从命令面板和快捷键触达;动画绝不能拖慢输入、焦点切换或键盘流。
范围
目标(v1):
- 打 开任意文件夹作为工作区;发现其中所有仓库、worktree、submodule;以带实时状态的树呈现
- 多仓库变更总览;由用户勾选展示哪些仓库、哪些变更类型
- Worktree 一等公民:自动识别、归组到主仓库下、支持从 UI 创建、移动与删除
- 完整的 Git 操作流,对标 SourceTree:stage、commit、stash、branch、merge、rebase(含交互式)、cherry-pick、revert、reset、tag、remote、fetch/pull/push、submodule 更新、worktree 增删、提交元信息改写,都带安全网
- 原生提交图(Git Graph 等价物),逐仓库展示,可在提交上直接操作;另有一条覆盖勾选仓库的工作区活动时间线
- 代码查看与轻编辑:语法高亮、尽力而为的 TypeScript 提示、一键"在 VSCode 中打开"逃生口
- 变更列表与 diff:已暂存/未暂存/未跟踪/冲突,文本与图片 diff,hunk 级与行级暂存
- 集成终端:多 Shell 配置、按目录级联配置、快捷命令
- 一键根据暂存区生成 commit message(离线启发式 + 可选 LLM)
- 现代化、可切换主题的 UI;深色模式一等公民(跟随系统或手动);键盘友好
- 跨平台:Windows、macOS、Linux
- 英文优先,i18n 架构从第一天起就位
非目标(v1):
- 扩展/插件平台、应用市场。
- 完整语言服务(工程级 TS/重构)。由"在 VSCode 中打开"逃生口覆盖。
- 托管平台集成(PR、issue、CI 状态)。后续。
- 远程开发、云同步、账号体系、遥测。
- VSCode 级的三方合并编辑器。v1 的冲突处理:ours/theirs 选择 + 带标记的编辑器打开 + 外部合并工具。
- 运行构建/测试/任 务、调试器、问题面板。
参考场景(验收场景)
以一个代表性的工作区作为验收标的(示例已脱敏):acme/ —— 约 55 个 Git 仓库,分布在 2~3 层分组下(app/、backend/、website/……),链接 worktree 集中放在兄弟目录 __worktree__/(如 mobile-app_worktree-feat-dark-mode),混有非 Git 文件夹,根目录有噪声目录 node_modules/,并且至少有一个中文命名目录(content/中文文档)。
v1 验收标准:
- 打开该工作区后在性能预算内呈现完整树,并流式补充仓库状态,界面全程不被阻塞。
- 树中的每个仓库都可完整操作(全部 Git 操作)。
__worktree__中的条目归组显示在其主仓库下,而不是当作互不相关的文件夹。- 中文目录名与仓库内的中文路径都能正确显示和操作。
术语表
| 术语 | 含义 |
|---|---|
| Workspace(工作区) | 用户打开的根文件夹,不要求是 Git 仓库。 |
| Repository(仓库) | 在工作区内发现 .git(目录或 gitdir 指针文件)的 Git 仓库。 |
| Main worktree(主工作树) | 仓库最初创建时所在的工作树。 |
| Linked worktree(链接工作树) | 通过 git worktree add 创建的附加工作树,可能位于工作区根之外。 |
| Submodule(子模块) | 通过 .gitmodules 注册的仓库,可能尚未初始化。 |
| Git dir / common dir | 链接 worktree 的 per-worktree git dir 与共享的 common dir 不同。仓库身份以 common dir 为准。 |
| Group(分组) | Repos 视图中的虚拟文件夹;可显式配置或取自首层路径段。 |
| Badge(徽标) | 仓库行旁的状态指示(dirty、ahead/behind、stash、进行中操作……)。 |
| Operation state(操作态) | 由 git dir 状态文件(MERGE_HEAD、rebase-merge/、CHERRY_PICK_HEAD 等)检测到的进行中 Git 操作。 |
关键决策(ADR)
决策条目及被否决的备选方案。v1 内生效;若要变更,需更新受影响的章节。
| 决策 | 理由 | 被否决的方案 |
|---|---|---|
自研 Electron 应用(当前稳定版 Electron;主版本在 package.json 中锁定并按计划升级)。 | VSCode 的卡顿是架构性的(全工作区 watcher、逐仓库 SCM provider、TS 工程图)。自研外壳可以从一开始就按多仓库场景设计发现、监听与状态刷新。 | Fork code-oss:百万行级 fork,上游合并负担重,性能问题是架构性的、fork 内修不掉,且约 80% 代码与本产品无关。VSCode 扩展:治不了 watcher/ 扫描的根因,最多做交互验证。 |
Git 引擎 = 系统 git CLI,以参数数组方式 spawn;解析 porcelain 输出。 | 正确性:worktree、submodule、LFS(filter)、credential helper、hooks 及边界行为与命令行 100% 一致——与 VSCode、SourceTree、Fork 同路线。性能靠并发池、index 指纹跳过与缓存。 | isomorphic-git:截至 2026-10 其命令索引缺 rebase、cherry-pick、worktree、submodule、hooks、LFS——直接否决 worktree 与 Git 操作需求;大仓库也慢。libgit2 绑定(nodegit/koffi):worktree/submodule/filter 支持有缺口,hooks 不执行,各平台原生打包麻烦。混合(原生读 + CLI 写):两套引擎,一致性成本翻倍;仅在实测证明必要时再评估。 |
git 可执行文件策略:优先系统 Git(PATH 上的 git,设置中可覆盖)。要求 ≥ 2.23,低于 2.36 给出警告。启动时用 git --version 探测。Windows 额外探测 %ProgramFiles%\Git\cmd\git.exe。内置便携版 Git 属待定问题。 | Git GUI 用户本来就有 Git;内置便携版会带来几十 MB 体积与更新负担。版本下限覆盖 git switch/restore(2.23)与稳定的 porcelain 行为。 | 内置 Git for Windows / 自行发行 —— 后续再说,仅在真实用户撞到"没装 Git"时考虑。 |
| 渲染层技术栈:React 19 + TypeScript、Vite、Tailwind CSS v4、Radix UI 原语(shadcn 模式、组件内置仓库)、Zustand 状态管理、TanStack Virtual 虚拟化、Lucide 图标。 | 热更新友好;Zustand 适合高频状态补丁;Radix 提供无障碍原语且不引入重型组件库。 | Ant Design / MUI:主题定制成本高、视觉语言显旧,与 UI 目标冲突。 |
编辑器 = Monaco,本地打包(禁用 CDN loader——必须完全离线可用)。v1 的 TypeScript 提示使用 Monaco 内置 TS worker;tsserver/LSP 桥接属后续。monaco-languageclient 明确不是 v1 依赖。 | Monaco 是 VSCode 的编辑器内核:100+ 语言高亮、diff 编辑器、worker 中的 TS 语言服务。工程级 LSP 本身是大型子项目;v1 由"在 VSCode 中打开"按钮覆盖重度用户需求。 | CodeMirror 6:更轻但 TS 与 diff 能力弱。 |
终端 = node-pty + @xterm/xterm,pty host 运行在 Electron utilityProcess(pty host 崩溃不得拖垮应用),数据经 MessagePort 流入渲染进程。 | 成熟组合(VSCode 终端栈同思路)。utilityProcess 隔离与 VSCode 的 pty host 同理。 | pty 跑在主进程:原生崩溃会杀掉窗口。 |
| 持久化拆分:用户编写的配置存为 JSON 文件(可移植、可 diff、可提交);SQLite(better-sqlite3)只存可丢弃的缓存/索引——删除它绝不可以丢用户数据。 | 用户可以检查/版本化自己的配置;缓存随时可重建。避免"把用户设置藏进二进制 DB"的经典错误。 | 全部塞 SQLite:配置不透明,不利于同步与手改。 |
| 监听策略:不对整个工作区做递归监听。只监听:(a) 每个仓库的 git dir(限深),(b) 文件树中已展开的目录(LRU 上限),(c) 工作区根目录浅层(深度 2)用于发现新仓库。间隔轮询仅作兜底(默认 60 秒,可配置;watcher 健康时逐仓库关闭)。 | 工作区可能有几十万个目录;按仓库监听 git dir,成本只与仓库数相关,与工作区规模无关。这是让自研外壳兑现的核心性能决策。 | 全工作区递归监听(chokidar/@parcel/watcher 盖住根目录):正是本产品要避免的 VSCode 失效模 式。 |
提交信息生成:先离线启发式流水线(仓库约定探测 + 暂存 diff 分析 → 可编辑草稿);可选 LLM Provider(OpenAI 兼容端点)显式开启,密钥存 OS 钥匙串(safeStorage)。草稿始终可编辑;永不自动 commit。 | 零配置、零网络即可用;LLM 质量是增强而非依赖。隐私:只有用户主动开启时 diff 才离开本机。 | 纯 LLM:强依赖网络与密钥,离线体验差。纯模板:价值太低。 |
i18n:react-i18next,英文原文即文案(keys-as-text),按命名空间拆分 locale JSON,日期/数字用 Intl。v1 内置 en + zh-Hans;新增语言是纯配置。任何用户可见字符串不得硬编码(lint 规则强制)。 | README 已定英文优先;架构必须让翻译是机械工作而非重构。 | 推迟 i18n:日后必然重写。 |
| 仓库身份 = git common dir 的 realpath。 所有视图(Explorer、Repos、SCM、Graph)都以该 id 为主键,因此主工作树与其全部链接 worktree 是同一实体、对应多个工作树。 | worktree 归组可由身份定义自然导出,无任何按名字的启发式。 | 按路径定义身份:worktree 会被误当成互不相关的仓库。 |
破坏性操作的安全网。 在 reset --hard、rebase、交互式 rebase、历史改写、stash drop、branch 删除、worktree 删除之前:创建 refs/gitreedesk/backup/<op>/<yyyymmdd-hhmmss> 指向当前 HEAD(或目标 ref),并记录操作日志(日志存 SQLite,backup ref 存仓库内)。每个受影响的 UI 操作提供会话内及其后的撤销/回滚。 | 历史改写是最危险的一类功能;没有撤销路径的 Git 工作台就是陷阱。backup ref 对常规工作流不可见(refs/gitreedesk/* 默认不出现在 ref 列表),成本极低。 | 不备份:不可接受。完整的 reflog UI:后续再细化。 |
不受信工作区门禁:工作区首次打开时为不受信。不受信工作区不执行任何工作区配置的命令(快捷命令禁用),并借助 -c core.hooksPath=<空目录> 抑制 Git hooks;用户可授予信任,信任状态按工作区根记录在 SQLite。 | 打开陌生人的文件夹不能执行其 post-checkout hook 或终端命令。 | 一律信任:一类众所周知的漏洞。 |
架构
进程模型与包结构
┌─────────────────────────── Electron 应用 ──────────────────────────┐
│ 主进程 │
│ WindowManager · 菜单/快捷键 · Updater · SettingsService │
│ WorkspaceService(发现、注册表、状态调度) │
│ GitService(spawn 并发池、porcelain 解析、操作模型、备份) │
│ WatcherService(@parcel/watcher;逐仓库 git dir + 已展开目录) │
│ CommitMessageService(启发式 + LLM Provider) │
│ TrustStore · CacheDB(better-sqlite3,可丢弃) │
├─────────────────────────────────────────────────────────────────────┤
│ utilityProcess:PTY HOST(node-pty 会话,渲染层刷新后仍存活; │
│ 每个终端一条 MessagePort 通道) │
├─────────────────────────────────────────────────────────────────────┤
│ Preload:contextBridge → 类型化 `window.gtd` API(zod 校验) │
├─────────────────────────────────────────────────────────────────────┤
│ 渲染进程(React,沙箱,无 Node):仅 UI │
│ Zustand stores(工作区/仓库状态/操作/终端/设置) │
│ Monaco(文件、diff)· 提交图(canvas + DOM 行) │
└─────────────────────────────────────────────────────────────────────┘
pnpm workspace 布局(apps/* + packages/*,版本由 catalog 统一管理):
GitTreeDesk/
├── apps/
│ └── desktop/
│ ├── src/main/ # Electron 主进程:服务装配、IPC handler、菜单、信任、更新
│ ├── src/pty-host/ # node-pty 的 utilityProcess 入口
│ ├── src/preload/ # contextBridge API 面
│ ├── src/renderer/ # React 应用(Vite)
│ └── electron-builder.yml
├── packages/
│ ├── shared/ # 跨进程类型、IPC 契约、zod schema、i18n key 类型
│ ├── git-core/ # git runner、porcelain 解析、操作模型、backup ref(纯 Node,不 import Electron;对真实临时仓库做单测)
│ ├── workspace-core/ # 发现、仓库注册表、状态调度、监听计划
│ ├── terminal-core/ # Shell 探测、级联配置解析、快捷命令
│ ├── commit-msg/ # 约定探测、离线草稿、LLM Provider
│ └── ui/ # 共享 React 组件、设计 token、徽标集
├── docs/
└── scripts/ # 开发编排、打包、fixture 工作区生成器
规则:git-core、workspace-core、terminal-core、commit-msg 不得 import Electron;主进程可 import 全部;渲染层只 import shared 与 ui。
仓库发现与身份
范围:v1。
算法(跑在主进程,分块执行以便 UI 流式上屏):
- 从工作区根做 BFS。在包含
.git/(目录)或.git(文件 → 链接 worktree 或 submodule 指针)的目录剪枝,绝不下钻.git。 - 默认跳过:
node_modules、.git、.svn、.hg及.DS_Store类噪声;跳过规则 = 内置默认 ∪ 工作区配置 excludes。除非scan.followSymlinks = true,否则不跟随符号链接。 - 预算:默认
scan.maxDepth = 8、scan.maxEntries = 200000个目录;超预算即停止发现并提示,可调高后继续。 - 确认轮:候选目录在 git 工作池中逐个以
git rev-parse --git-dir --show-toplevel --is-bare-repository --git-common-dir确认,结果写入注册表。 - worktree 关联:对每个确认的仓库执行
git worktree list --porcelain,得到主工作树 + 链接 worktree(路径、分支、locked、prunable)。工作区内的链接 worktree 按 realpath 匹配并挂到父仓库;工作区外的仅在view.showExternalWorktrees开启时显示。 - submodule 识别:含
.gitmodules的仓库打标;submodule 状态仅在展开时惰性读取(git submodule status),初扫绝不执行。 - 增量发现:浅层监听工作区根与一级目录;create/rename 事件只对该子树做局部的迷你扫描。
身份:repoId = realpath(git common dir),worktree 目录移动后仍稳定;注册表保存 relPath、gitDir、commonDir、kind(repository | worktree | bare)、parentRepoId。
允许仓库嵌套在另一仓库的工作树内,并以父子形式展示;父仓库自身 git status 的行为(embedded repo 警告)如实呈现,不做猜测。
Git 执行层(git-core)
范围:v1。
- 唯一原语:
runGit(args, { cwd, env, signal, onStdout }) → { code, stdout, stderr }。只以参数数组 spawn——绝不拼接 shell;路径一律放在--之后。 - 每次调用的环境加固:只读命令用
GIT_OPTIONAL_LOCKS=0;GIT_TERMINAL_PROMPT=0(不挂起等凭据;认证失败转为结构化错误上抛 UI);LC_ALL=C(输出稳定可解析);GIT_PAGER=cat。Windows 上逐命令传-c core.longpaths=true(不修改用户配置)。 - 并发池:每工作区默认 6 个并发进程,优先级队列——交互操作插队到后台状态刷新之前;后台任务可经
AbortSignal取消(杀进程树)。 - 解析器与调用同层:
status --porcelain=v2 -z --branch、for-each-ref、log(流式、NUL 分隔自定义格式)、worktree list --porcelain、submodule status、stash list、rev-list --count。全部解析器在含中文路径、空格、换行文件名的 fixture 仓库上做测试。 - 操作模型:每个写操作定义一次
{ preconditions, command plan, backupRef?, conflictBehavior, postValidate },UI(右键菜单、提交图、命令面板)走同一条代码路径。冲突检测(退出码 + git dir 状态文件)统一处理。 - 凭据流:优先系统 credential helper / SSH agent;可选内置 askpass helper(
GIT_ASKPASS)做安全弹窗(凭据绝不进日志)。push/pull 失败映射为类型化错误(认证 / 非快进 / 网络 / hook 拒绝)。 - hooks:由 Git 自身执行,输出流式进操作对话框;不受信工作区以
core.hooksPath覆盖抑制。
状态模型与刷新生命周期
范围:v1。
每个仓库产出 RepoStatus:
head { ref, sha, detached, unborn } · upstream { ref, ahead, behind }
staged[] · unstaged[] · untracked[] · conflicted[] (路径 + 变更种类;rename 已解析)
operation: none | merge | rebase | rebase-interactive | cherry-pick | revert | bisect
stashCount · worktrees[](branch、path、locked/prunable) · submodulesPresent: bool
刷新规则:
- 事件驱动优先;250–300 ms 防抖合并事件风暴(一次大 checkout 会产生数千事件)。
- 廉价跳过:重跑
git status前先 statindex(mtime + size)、HEAD与操作态标记文件;无变化 ⇒ 复用缓存状态。 - 未跟踪文件:默认
--untracked-files=normal;all为逐仓库可选。未跟踪数量病态的仓库给上限列表 + "显示更多",而不是卡死刷新。 - 后台预热:打开工作区按优先级刷新(可见行 > 已展开节点 > 其余),以池并发执行;结果按批流式推送渲染层(节流 ≤ 30 次 UI 更新/秒)。
- 兜底轮询(默认 60 秒,可配置;watcher 健康时默认关闭)覆盖网络盘与平台监听上限场景。
文件监听
范围:v1。
- 实现:
@parcel/watcher(原生后端:ReadDirectoryChangesW / FSEvents / inotify),每个仓库一组订阅:- 该仓库的 git dir,
depth: 2,关注:HEAD、index、ORIG_HEAD、MERGE_HEAD、rebase-merge/**、rebase-apply/**、CHERRY_PICK_HEAD、REVERT_HEAD、refs/**、packed-refs、stash、BISECT_*。 - 工作树仅覆盖文件树中当前已展开的目录(LRU 上限约 2000 个被监听目录,折叠即释放),关注 create/delete/rename/change。
- 该仓库的 git dir,
- 工作区根浅层监听(
depth: 2)仅用于发现。 - 事件路由:git dir 的 index/HEAD/refs → 失效该仓库状态;工作树事件 → 失效 Explorer 对应子树,若可能影响脏状态则安排防抖后的状态刷新。
- 降级:watcher 报错 / 检测到网络文件系统时,该范围回退为带退避的间隔轮询;状态栏对受影响仓库给出低调的"轮询中"指示。
- 预算:监听总数有界(仓库数 × 1 + 已展开目录);Linux 上超出 inotify 预算时,把最新的订阅降级为轮询并一次性告知。
IPC 契约
范围:v1。 所有通道在 packages/shared 中定义类型,主进程侧用 zod 校验。命名 domain:action。请求走 invoke(promise);流走事件通道(节流);终端数据走专用 MessagePort。
| 通道 | 类型 | 签名(草案) |
|---|---|---|
workspace:open | invoke | (rootPath) → WorkspaceDescriptor |
workspace:scan | 事件流 | discovered: RepoRecord[](分块)、scan:done |
workspace:reposChanged | 事件 | 注册表 diff(added/removed/updated) |
git:status | invoke | (repoId, { refresh?: boolean }) → RepoStatus |
git:statusChanged | 事件(批量) | { repoId, status }[] |
git:op:run | invoke + 进度事件 | (repoId, OpRequest) → OpResult;git:op:progress 流 |
git:log:read | invoke | (repoId, LogQuery, page) → { commits, hasMore } |
git:diff:read | invoke | (repoId, DiffRequest) → DiffPayload(text/binary/image) |
fs:tree:read | invoke | (dirPath) → DirEntry[] |
fs:file:read/write | invoke | path + content/BLOB,含外部变更守卫 |
term:create/write/resize/dispose | invoke + MessagePort | 已解析 profile 的会话 |
commitmsg:generate | invoke + 流 | (repoId, { provider }) → 流式草稿 |
settings:get/set/onDidChange | invoke + 事件 | 分 scope(user/workspace) |
trust:query/grant | invoke | 工作区信任状态 |
持久化
范围:v1。
用户编写的 JSON(写入时不做注释剥离;原子写入;带 $schema 获得编辑器自动补全):
.gitreedesk/workspace.json(工作区根——可提交):
{
"$schema": "https://gitreedesk.dev/schemas/workspace.json",
"name": "acme",
"exclude": ["content/中文文档", "**/fixtures/**"],
"scan": { "maxDepth": 8, "followSymlinks": false },
"groups": [{ "name": "Backend", "match": ["backend/*"] }]
}
.gitreedesk/terminal.json(任意目录——级联,就近优先,见终端章节)。
.gitreedesk/settings.json(工作区级设置覆盖;用户级位于 userData/settings.json)。
userData/keybindings.json(用户快捷键覆盖)。
SQLite 缓存(userData/cache.db,schema 带版本、自动迁移;损坏 ⇒ 删除重建,绝不抛用户可见错误):
CREATE TABLE workspaces (id INTEGER PRIMARY KEY, root TEXT UNIQUE, name TEXT, last_opened_at INTEGER, trusted INTEGER);
CREATE TABLE repos (id INTEGER PRIMARY KEY, workspace_id INT, rel_path TEXT, git_dir TEXT, common_dir TEXT,
kind TEXT, parent_repo_id INT NULL, name TEXT, UNIQUE(workspace_id, rel_path));
CREATE TABLE repo_cache (repo_id INTEGER PRIMARY KEY, status_json TEXT, refs_json TEXT, updated_at INTEGER);
CREATE TABLE commits_cache(repo_id INT, sha TEXT, parents TEXT, author TEXT, ts INTEGER, subject TEXT, refs TEXT,
PRIMARY KEY(repo_id, sha));
CREATE TABLE ui_state (key TEXT PRIMARY KEY, value TEXT); -- 最近工作区/文件、展开节点、图偏好
CREATE TABLE oplog (id INTEGER PRIMARY KEY, repo_id INT, op TEXT, backup_ref TEXT NULL, at INTEGER, undo_state TEXT);
安全与信任模型
范围:v1。
- 渲染层:
contextIsolation: true、sandbox: true、nodeIntegration: false、严格 CSP、will-navigate/setWindowOpenHandler默认拒绝;外链一律在系统浏览器打开。 - 一切文件与 Git 访问都经主进程服务;渲染层传来的路径先解析、再校验落在工作区根内(或显式登记的外部 worktree 路径内)方可使用。
- Git 层与终端快捷命令中没有任何 shell 字符串拼接(只传参数数组)。快捷命令永不自动执行。
- 不受信工作区门禁管辖 hooks、快捷命令,以及(将来的)任务。
- LLM 提交信息生成默认关闭;启用需显式配置,且每次请求在 UI 与设置中展示"将暂存区 diff 发送至 <host>"披露。API key 用
safeStorage(OS 钥匙串)存储;Linux 无钥匙环时降级为仅会话内保存并明确告知,绝不落明文。 - 机密信息不出现在日志、崩溃报告或操作日志中。
i18n、主题、键盘优先、动效
范围:v1。
- i18n:react-i18next;英文原文即源(
t('workspace.emptyState')配同目录locales/en/…json);按功能域拆分命名空间;展示层用Intl.DateTimeFormat/NumberFormat;切换语言即时生效、无需重启;开发期提供伪语言(en-XA)撑长字符串以发现截断。lint 规则在 CI 拦截 JSX 中的字符串字面量。 - 主题:三态
system | light | dark(默认 system,跟随 OS 实时变化)。设计 token 为 CSS 变量,由 Tailwind v4 消费;Monaco 明/暗主题对由同一组 token 生成,编辑区与外壳永不色调漂移。深色模式是设计起点,而非从浅色推导。 - 键盘优先——不变量:所有功能可经命令面板触达;没有任何操作只能鼠标完成;焦点永不被抢(终端/编辑器/对话框焦点规则见 UX 蓝图);动画 ≤ 150 ms、可中断、绝不阻塞焦点或输入;尊重
prefers-reduced-motion;焦点环全局可见。 - 无障碍基线:Radix 原语;所有面板纳入 Tab 序;树/列表全部可方向键导航、Enter/Space 操作;状态用图标 + 颜色 + tooltip 三重表达(绝不只靠颜色)。