# Cherry Studio UI Library Current Issues > 更新日期:2026-04-16 > 范围:`packages/ui` > 目的:记录 `@cherrystudio/ui` 当前已识别出的架构问题,作为后续 v2 UI 收口与拆分的依据。 ## 背景 `packages/ui` 当前同时承担了多种职责: 1. React 组件库 2. 设计令牌与主题入口 3. 图标资源与图标代码生成产物 4. Storybook / design reference 5. 迁移过程中的旧栈兼容层 这让它在 monorepo 内部可以继续承担“迁移缓冲区”的角色,但也导致对外边界、发布契约和技术栈约束都不够稳定。 当前最核心的问题不是某一个组件实现有缺陷,而是 `@cherrystudio/ui` 还没有从“内部迁移目录”真正收敛为“边界稳定的 UI 库”。 ## 核心问题 ### 1. 发布契约与实际产物不一致 `packages/ui/package.json` 当前只发布: 1. `dist` 2. `README.md` 但 `exports` 仍然把样式子路径导向 `src/styles/*`: 1. `./styles` 2. `./styles/tokens.css` 3. `./styles/theme.css` 4. `./styles/index.css` 这意味着: 1. 在 workspace 内通过源码路径使用时看起来是可用的 2. 但一旦作为真正 npm 包消费,样式入口很可能不可用 3. UI 包现在依赖 monorepo 源码目录结构,而不是依赖稳定的发布接口 类似地,`./icons` export 指向 `dist/components/icons/index.*`,但当前构建产物并没有对应目录,说明 export 设计与构建配置之间也没有完全对齐。 结论: `@cherrystudio/ui` 现在更像“源码别名入口”,而不是“可独立发布的包”。 ### 2. 组件库边界仍然混入旧技术栈 虽然 v2 的方向是: 1. `@cherrystudio/ui` 2. Tailwind CSS 3. shadcn / Radix 但当前包里仍存在旧依赖残留: 1. `EditableNumber` 直接依赖 `antd` 2. `EditableNumber`、`Scrollbar`、`Sortable`、`HorizontalScrollContainer` 等仍在使用 `styled-components` 3. `tsdown.config.ts` 仍将 `styled-components` 作为 external 保留 4. `package.json` 的 devDependencies 中仍包含 `antd`、`styled-components`、`@types/styled-components` 这类残留的风险不是“包暂时还能跑”,而是: 1. 旧栈继续渗透到公共 API 层 2. renderer 在消费 UI 包时继续背负迁移债务 3. 后续要把 UI 库作为统一设计系统时,技术边界会持续被打破 如果一个公共 UI 包还默认允许旧技术栈存活,那么它就很难成为 v2 UI 重构的真正收口点。 ### 3. 主题系统的所有权不清晰 当前主题系统分散在多个位置: 1. `packages/ui/src/styles/tokens.css` 2. `packages/ui/src/styles/theme.css` 3. `src/renderer/assets/styles/tailwind.css` 4. `src/renderer/hooks/useUserTheme.ts` 目前 renderer 直接导入: 1. `../../../../../packages/ui/src/styles/theme.css` 2. `../../../../../packages/ui/src/components/**/*.{js,ts,jsx,tsx}` 这说明应用层依赖的是 UI 包内部源码路径,而不是包导出的稳定入口。 同时,renderer 自己又在 `tailwind.css` 中补了一批 UI 包未收口的变量;`useUserTheme.ts` 也直接通过 DOM 写入: 1. `--color-primary` 2. `--primary` 3. `--color-primary-soft` 4. `--color-primary-mute` 5. 字体变量 结果是主题能力被拆散成 3 层: 1. token 层 2. theme 映射层 3. runtime 覆写层 但这三层目前没有明确的边界定义,导致以下问题: 1. 某个变量应该归 UI 包还是归应用层维护,责任不清晰 2. 新主题变量是否属于公开 contract,不明确 3. 后续做主题切换、用户自定义主题、组件样式统一时,定位成本会持续变高 ### 4. 文档、目录说明与真实结构漂移 当前 `packages/ui/README.md` 与真实状态不一致,典型表现包括: 1. 仍然提到 `HeroUIProvider` 2. 示例与实际组件结构不一致 3. 目录说明仍是早期组件库结构 4. 对外文档没有准确反映当前发布方式、样式接入方式和限制 这种漂移会带来两个问题: 1. 团队成员难以判断哪些是现行方案,哪些只是迁移历史残留 2. UI 包的“设计目标”与“当前事实”长期不一致,导致重构决策缺少稳定依据 ### 5. 单包职责过多 `packages/ui` 当前把以下内容放在同一个包里: 1. primitives / composites 组件 2. icons runtime catalog 3. icons source assets 4. icon generation scripts 5. design reference 6. Storybook 7. migration 文档 这会导致: 1. 包的定位不清晰 2. 评估变更影响范围时成本高 3. 构建、发布、文档、资源生成等 concerns 相互耦合 4. 很难判断哪些内容属于运行时依赖,哪些只是开发资产 体量大本身不是问题,职责不收口才是问题。 ### 6. 内部实现路径被当成公共依赖使用 包内部大量通过 `@cherrystudio/ui/lib/utils` 引用 `cn` 等内部工具。 这类自引用在 monorepo 中短期可用,但会带来几个问题: 1. 内部模块边界被伪装成稳定包路径 2. 构建配置、路径映射和发布产物更难保持一致 3. 后续调整内部目录结构时,改动半径会被放大 更合理的方式应该是: 1. 包内部使用相对路径或明确的内部 alias 2. 对外只暴露真正承诺稳定的 public API ### 7. 测试覆盖与公开表面积不匹配 当前 `packages/ui` 的测试主要集中在: 1. 少量 primitives 2. CodeEditor 的工具函数 3. 图标脚本工具 但该包实际承载的公开能力远多于当前测试覆盖,包括: 1. 组件导出面 2. 样式入口 3. 图标 registry / fallback 逻辑 4. 旧组件兼容层 5. 主题入口与运行时约定 这会导致一个典型风险: 包的对外表面积已经很大,但“哪些行为是受保护 contract”仍然不清楚。 ## 当前风险排序 ### 高优先级 1. 发布入口与实际产物不一致 2. UI 包继续暴露 `antd` / `styled-components` 迁移债务 3. renderer 依赖 UI 包源码路径而不是稳定包接口 这些问题会直接阻碍 UI 包独立演进,也会影响后续 v2 收口。 ### 中优先级 1. 主题变量 ownership 不清晰 2. 文档与现状漂移 3. 内部实现路径与公共 API 混用 这些问题不一定立刻导致功能故障,但会持续抬高维护成本。 ### 低优先级 1. 包职责过多但尚未拆分 2. 测试覆盖不足但仍能支撑当前内部消费 这两项可以在完成边界收口后继续推进。 ## 建议的收口方向 ### 1. 先修“包是否真的可发布” 先统一发布契约: 1. `exports` 只能指向真实会发布的产物 2. 样式入口需要进入可发布目录,或明确声明该包仅供 workspace 内使用 3. `icons`、`styles` 等子路径 export 需要和构建产物一一对应 这一层不解决,后续所有“组件设计系统化”都仍然建立在不稳定基础上。 ### 2. 把旧栈兼容层与正式 UI 层分离 需要明确哪些组件属于: 1. 正式 v2 UI 能力 2. 迁移期间兼容层 原则上: 1. 正式 UI 层不再继续引入 `antd` 2. 正式 UI 层不再继续引入 `styled-components` 3. 仍依赖旧栈的组件要么迁移,要么隔离到兼容目录,不应继续作为默认公共导出的一部分 ### 3. 明确主题 contract 需要收敛主题系统的三层边界: 1. token 层负责什么 2. theme 映射层负责什么 3. runtime 用户主题覆写层负责什么 同时约定: 1. 哪些 CSS 变量是公开 contract 2. 哪些变量只能在 UI 包内部使用 3. renderer 应通过什么稳定入口接入主题 ### 4. 把 `packages/ui` 的定位从“迁移缓冲区”改成“稳定边界” 长期看,`packages/ui` 应只保留与运行时 UI 强相关的内容: 1. 组件 2. 设计 token / theme contract 3. 图标 runtime 能力 而以下内容可以继续评估是否拆离: 1. 设计参考资料 2. 图标生成脚本 3. 迁移说明文档 4. 纯开发期辅助资源 ## 优化方案 本方案不追求一次性“大拆大改”,而是按“先稳边界,再减耦合,最后做结构优化”的顺序推进。 这样做的原因很简单: 1. 当前 UI 库已经被 renderer 大量直接消费 2. 主题、样式、组件和图标已经形成事实耦合 3. 如果先做拆包或大范围目录调整,回归风险会高于收益 因此更合理的路径是先把 contract 固定下来,再逐步清理迁移债务。 ### 方案目标 这轮优化的目标不是立刻把 `packages/ui` 变成一个完美的独立 design system,而是先完成以下四件事: 1. 让 UI 包具备稳定入口和明确边界 2. 停止旧技术栈继续向公共 UI 层渗透 3. 明确主题 contract 与运行时覆写边界 4. 为后续拆包、精简和独立发布创造条件 ### 阶段划分 #### Phase 1: 边界收口 目标: 让 `@cherrystudio/ui` 先成为一个“入口稳定、对外契约清晰”的包。 主要工作: 1. 统一 `package.json` 中的 `exports`、`files` 和真实构建产物 2. 修正 `styles`、`icons` 等子路径 export 指向 3. 明确该包当前是“workspace-only”还是“可独立发布” 4. 停止 renderer 直接依赖 `packages/ui/src/*` 5. 建立最小 public API 清单 建议原则: 1. 对外只保留真正承诺稳定的入口 2. 包内部结构可以继续演进,但不应再被应用层直接依赖 3. 所有样式入口都必须经过稳定导出路径接入 阶段产出: 1. 一份更新后的 UI 包 public API 列表 2. 一组与构建产物一致的 package exports 3. renderer 对源码路径直连的替换清单 #### Phase 2: 旧栈隔离 目标: 把 `antd` / `styled-components` 从“正式 UI 层”中剥离出去。 主要工作: 1. 盘点所有仍依赖旧栈的组件 2. 将组件分为“正式能力 / 兼容层 / 待删除”三类 3. 优先迁移高频且低风险的旧组件 4. 在迁移完成后移除构建配置中的旧栈依赖保留 建议优先处理的组件: 1. `EditableNumber` 2. `Scrollbar` 3. `HorizontalScrollContainer` 4. `Sortable` 组件治理规则: 1. 正式导出的组件不得继续新增 `antd` 依赖 2. 正式导出的组件不得继续新增 `styled-components` 依赖 3. 尚未迁移完成的旧组件应显式标记为 compatibility,而不是混在默认公共能力里 阶段产出: 1. 旧栈组件清单与分类结果 2. compatibility 组件边界说明 3. 可移除的旧依赖列表 #### Phase 3: 主题系统收口 目标: 明确 token、theme、runtime override 三层职责。 建议分层: 1. `tokens`: 负责设计令牌 2. `theme`: 负责语义映射与 Tailwind 对接 3. `runtime override`: 负责用户主题与运行时覆写 主要工作: 1. 定义公开 CSS variable contract 2. 标记仅供内部使用的变量 3. 收敛 renderer 中补丁式变量定义 4. 约束 `useUserTheme` 只能覆写明确允许覆写的变量 5. 将应用层主题接入切换到稳定导出路径 建议规则: 1. token 负责“值”,不负责运行时行为 2. theme 负责“语义映射”,不负责用户配置读写 3. runtime override 只改 contract 明确开放的变量 CSS 变量分层约定: 1. `--cs-*` 是 design token namespace,来源于 `tokens/*` 2. `--color-*`、`--radius-*`、`--font-*` 是公开 theme contract,默认给组件和外部消费方使用 3. `--cs-theme-*` 是 runtime override input,只用于运行时覆写入口 4. `--primary` 这类变量属于 compatibility alias,只为兼容 shadcn / Tailwind 生态保留,不作为新代码首选 外部消费规则: 1. 普通业务包默认只依赖 `@cherrystudio/ui/styles/theme.css` 2. 普通业务包优先使用 `--color-*` 等公开 contract,不直接绑定 `--cs-brand-500` 这类 primitive token 3. 只有明确需要 token 层能力的设计系统配套包,才允许直接依赖 `@cherrystudio/ui/styles/tokens.css` 4. 运行时主题逻辑只允许写入 `--cs-theme-*` 这类受控入口变量,不直接写派生后的 `--color-*` 结果变量 阶段产出: 1. 一份主题 contract 文档 2. 一份运行时可覆写变量列表 3. renderer 侧主题接入方式收敛结果 #### Phase 4: 职责拆分与长期治理 目标: 在边界稳定后,再判断哪些内容应该继续留在 `packages/ui`,哪些应该拆出。 可评估拆离的内容: 1. 图标原始资产 2. 图标生成脚本 3. design reference 4. Storybook 专用资源 5. 迁移期说明文档 长期期望: 1. `packages/ui` 只保留运行时强相关内容 2. 开发辅助资源与构建素材不再和运行时包强绑定 3. UI 包的职责从“迁移缓冲区”收敛为“稳定基础设施” 阶段产出: 1. 包职责清单 2. 可拆离资源列表 3. 长期目录规划草案 ## 执行顺序建议 推荐顺序如下: 1. 先修发布边界与稳定入口 2. 再切断 renderer 对 `src/*` 的直接依赖 3. 再迁移旧栈组件并清理 external 保留 4. 再收口主题 contract 与 runtime override 5. 最后再做职责拆分和目录优化 这样做的好处是: 1. 每一步都可以独立验证 2. 每一步都能降低未来改动成本 3. 不需要在一开始就承担拆包级风险 ## 工作拆解建议 如果要真正落地,建议把这件事拆成 5 个 workstream 并行推进: ### Workstream A: 包边界与构建 负责: 1. `package.json` 2. `tsdown.config.ts` 3. 构建产物一致性 4. styles/icons export 收口 验收重点: 1. 所有 export 都能命中真实产物 2. 不再依赖源码目录结构对外工作 ### Workstream B: 组件分级与兼容层治理 负责: 1. 组件分类 2. compatibility 策略 3. 旧栈组件迁移优先级 验收重点: 1. 团队能明确知道哪些组件是 v2 正式能力 2. 团队能明确知道哪些组件只是过渡层 ### Workstream C: 主题与样式 contract 负责: 1. tokens/theme/runtime override 分层 2. CSS variable contract 3. renderer 接入收口 验收重点: 1. 应用层不再依赖 UI 包源码样式路径 2. 主题变量的归属清晰 ### Workstream D: 图标与资源治理 负责: 1. runtime icon catalog 2. 图标源文件 3. 生成脚本 4. 是否需要拆离资源包 验收重点: 1. 运行时能力与生成流程边界清晰 2. 图标系统不会继续把 UI 包拖成“资产仓库” ### Workstream E: 文档与使用规范 负责: 1. README 修正 2. public API 文档 3. 迁移指引 4. 团队使用约束 验收重点: 1. 文档描述与真实结构一致 2. 新增组件和样式接入有统一约束 ## 里程碑建议 ### Milestone 1: 能稳定消费 达到以下条件即可视为第一阶段完成: 1. renderer 不再引用 `packages/ui/src/*` 2. styles 与 icons 入口可通过稳定 export 接入 3. README 至少不再误导使用方式 ### Milestone 2: 停止继续欠债 达到以下条件即可视为第二阶段完成: 1. 正式 UI 层不再新增 `antd` 2. 正式 UI 层不再新增 `styled-components` 3. compatibility 范围被显式标注 ### Milestone 3: 主题体系清晰 达到以下条件即可视为第三阶段完成: 1. token/theme/runtime override 边界明确 2. 用户主题只通过受控变量覆写 3. 应用侧不再补丁式兜底 UI 包变量 ### Milestone 4: 包职责可持续 达到以下条件即可视为长期治理进入稳定期: 1. `packages/ui` 的运行时职责清晰 2. 非运行时资源有明确去向 3. UI 包可作为 v2 UI 基础设施持续演进 ## 验收标准 优化完成后,至少应满足以下标准: 1. `@cherrystudio/ui` 的所有公开入口都具备稳定 contract 2. renderer 不再通过源码路径使用 UI 包 3. 正式导出组件不再依赖 `antd` / `styled-components` 4. 主题变量区分出 public contract 和 internal-only contract 5. 文档、构建、导出、消费方式保持一致 ## 不建议的做法 在推进过程中,应避免以下方案: 1. 在 export 和源码路径并存的情况下长期维持“双轨接入” 2. 把所有旧组件都直接塞进正式导出中继续积债 3. 在主题边界未清楚前继续追加 renderer 兜底变量 4. 在发布边界未稳定前先做大规模拆包 这些做法看似能短期推进,实际会让 UI 包继续停留在“迁移缓冲区”状态。 ## 一句话结论 `packages/ui` 当前最大的问题不是“还有一些旧代码”,而是它仍然同时扮演: 1. UI 库 2. 主题源码目录 3. 图标资产仓库 4. 迁移兼容层 5. 内部开发工作区 在 v2 继续推进之前,必须先把它收敛成一个边界稳定、发布契约明确、技术栈一致的 UI 包,否则它很难成为后续 UI 重构的基础设施。