架构总览
XiHan.UI 是一个 pnpm + turbo 的 monorepo。它的组织方式服务于一个目标:让组件的行为独立于渲染它的框架存在。
一个组件的五份产物
以对话框为例,dialog 这个组件在仓库里落成五处:
| 产物 | 位置 | 内容 |
|---|---|---|
| 无头内核 | packages/engine/headless/src/dialog/ | 解剖、状态机、键盘规格表、connect |
| Vue 组件 | packages/adapters/vue/src/components/dialog/ | XhDialogRoot 等一组 defineComponent |
| React 组件 | packages/adapters/react/src/components/dialog/ | XhDialogRoot 等函数组件与 hooks |
| 自定义元素 | packages/adapters/web-components/src/elements/dialog.ts | <xh-dialog>,Light-DOM 行为宿主 |
| 皮肤 | packages/design/styles/css/dialog.css | 纯 CSS,按 data-* 选中 |
组件行为由无头内核定义;三个适配器分别将属性与事件接到 Vue、React 和作者的 DOM,皮肤决定这些状态的外观。
分层与依赖矩阵
层级越低越基础,只能向下依赖。拓扑写在 tooling/eslint-config/src/layers.json,由 dependency-cruiser 在 pnpm boundaries 时强制执行。
| 层 | 包 | 可依赖 |
|---|---|---|
| 1 | core | motion |
| 1 | motion | — |
| 1 | tokens | — |
| 1 | icons | — |
| 1 | pointer | — |
| 1 | viz | — |
| 2 | position | core |
| 2 | code-highlight | core |
| 2 | chat-stream | core |
| 2 | markdown | core |
| 2 | sound | core |
| 2 | animations | core motion |
| 3 | headless | core tokens motion pointer viz |
| 3 | styles | —(纯 CSS,不得依赖任何 JS 包) |
| 3 | backgrounds | core motion |
| 4 | vue | core headless position code-highlight tokens backgrounds sound motion pointer |
| 4 | react | core headless position code-highlight tokens backgrounds sound motion pointer |
| 4 | web-components | core headless position code-highlight tokens backgrounds motion pointer |
除分层外还有三条硬规则,同样由门禁执行:
- 库包的运行时代码不引入第三方依赖。登记在案的例外只有适配器的宿主框架(
vue、react与react-dom)。 styles是纯 CSS。它不依赖任何 JS 包,可以脱离整个 JS 层单独使用。- 依赖版本只从 workspace catalog 取。包内一律使用
catalog:或workspace:协议引用,不内联版本号。
一次交互经过哪些层
以点击对话框的触发器为例:
用户点击
│
▼
适配器把 DOM 事件交给 connect 产出的 onClick (vue / react / web-components)
│
▼
service.send({ type: 'TRIGGER.CLICK' }) (core)
│
▼
状态机转移 closed → open,执行 entry 动作 (core)
│
├─► 行为原语接管:锁滚动、建焦点域、压入层栈 (core)
├─► 浮层族还会请定位引擎算坐标 (position)
│
▼
适配器重新读 connect,把新的 aria-* / data-* 铺到部件上
│
▼
皮肤按 [data-state='open'] 命中新规则,动画播放 (styles)中间三步与框架无关,Vue、React 与 Web Components 适配器各自负责首尾两步。
包一览
按职责分四组。详细依赖关系见包与依赖关系。
内核与原语
| 包 | 职责 |
|---|---|
@xihan-ui/core | 运行时底座:解剖、mergeProps、normalizeProps、Scope、层栈、诊断通道;薄状态机运行时(createMachine、解释器契约、受控值绑定);交互行为原语(消隐层、焦点域、滚动锁、进出场、集合导航、typeahead) |
@xihan-ui/motion | 动效原语:缓动单一真源、纯补间、帧循环、减弱动效偏好、解析解弹簧 |
@xihan-ui/position | 浮层定位引擎,自研,零第三方依赖 |
@xihan-ui/pointer | 指针会话:一根指针从按下到抬起的跟手、过滤与收尾,自研,零依赖 |
@xihan-ui/viz | 图表引擎:比例尺、刻度、形状、坐标轴布局、拾取与降采样,全部是纯函数,自研,零依赖 |
组件与适配器
| 包 | 职责 |
|---|---|
@xihan-ui/headless | 136 个组件的解剖 + 状态机 + connect,无样式、无框架 |
@xihan-ui/vue | Vue 3 适配器 |
@xihan-ui/react | React 19 适配器 |
@xihan-ui/web-components | Web Components 适配器,自研响应式基类 |
表现
| 包 | 职责 |
|---|---|
@xihan-ui/tokens | 设计令牌(DTCG 源)与主题运行时(明暗 / 品牌 / 密度 / 对比度 / 书写方向) |
@xihan-ui/styles | 默认皮肤,按 @layer 分层的纯 CSS |
@xihan-ui/icons | 图标集 |
内容与效果
| 包 | 职责 |
|---|---|
@xihan-ui/chat-stream | AI 协议内核:SSE 读取 → 协议归一 → parts 归约 → 会话 store |
@xihan-ui/markdown | 流式 Markdown 渲染内核,增量切块 + 稳定 key |
@xihan-ui/code-highlight | 代码着色,自研粗粒度词法器;适配器的可选 peer,未安装时渲染纯文本 |
@xihan-ui/backgrounds | WebGL2 背景效果与数据驱动粒子点云 |
@xihan-ui/sound | 纯 Web Audio 程序化 UI 音效,零音频文件 |
@xihan-ui/animations | 现成的进场与注意动效、错开起播、文字拆分 |
tooling/* 下的构建、lint、tsconfig、测试与门禁脚本等内部包一律不发布。
目录结构
XiHan.UI/
├── ui/ # 组件库工作区(pnpm workspace)
│ ├── packages/ # 对外发布的库包,按角色分四组
│ │ ├── adapters/ # vue · react · web-components,按宿主选择
│ │ ├── design/ # tokens · styles · icons,外观
│ │ ├── features/ # markdown · chat-stream · backgrounds · sound · animations · code-highlight,按需选用
│ │ └── engine/ # core · motion · pointer · position · viz · headless
│ └── tooling/ # 内部构建与质量工具
│ ├── build/ # 打包配置与 exports 回写
│ ├── eslint-config/ # lint 规则 + 分层拓扑事实源
│ ├── stylelint-config/
│ ├── testing/ # 一致性 / 无障碍 / 定位三套判据的运行时
│ └── scripts/ # 门禁脚本
└── docs/ # 文档站(VitePress),通过 link: 引用上述库包文档站提供 Vue、React 与自定义元素的真实组件示例;示例源文件在 docs/.vitepress/demos/<组件>/ 下。各框架覆盖由 check-demo-frameworks 核对,不适用项和缺席项分别登记。
技术选型
| 位置 | 选型 |
|---|---|
| 语言 | TypeScript,ESM only(不提供 CJS) |
| 运行时要求(消费端) | Node ≥ 18;浏览器为 2024 年常青版(皮肤用了 @layer、color-mix、oklch) |
| 运行时要求(开发本仓) | Node ≥ 24、pnpm ≥ 11 |
| 构建 | tsdown(库包)+ turbo(任务编排) |
| 样式 | 原生 CSS,@layer 分层,oklch 色彩空间,无预处理器 |
| 测试 | vitest(jsdom)+ Playwright(真实 Chromium) |
| 发布 | changesets,全部库包同属一个 fixed 版本组 |
