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