Codex Proxy RS # 管理端主题系统 浅色、深色、预置配色与自定义主题的配置和实现说明 [界面约定](#界面文案与信息层级) · [Token 模型](#token-模型) · [运行时架构](#运行时架构) · [主题编辑器](#主题编辑器) · [扩展指南](#扩展指南)
在管理端「主题」中调整颜色、字号、圆角和组件样式,保存后应用到当前浏览器。 设置保存在浏览器本地,不会同步到服务器或其他设备。只想换配色时,选择预置主题即可 下文说明实现和扩展方式。颜色使用 Ant Design 十阶色板与项目的明暗角色规则, 中性表面从背景和文字 Seed 派生;Vue 组件通过 CSS Variables 与 Tailwind CSS 4 使用这些值。 项目不依赖 Ant Design 组件库或 CSS-in-JS > [!IMPORTANT] > 主题的目标是调整颜色、密度、圆角与层级,不改变现有页面结构和产品语义。默认主题保留既有表面层级,并满足正常文字的可读性约束; > 成功、警告、错误和业务数据色不会因为品牌色变化而失去原有含义 ## 设计原则 - **Seed 与 Map 可追踪**:主色和功能色均按 Ant Design 从 Seed 派生 P1-P10;浅色 P6 等于 Seed,深色 P6 经过暗色色板适配。全局 `colorPrimary` 保留原始 Seed;功能色 Base 保留 P6。文字和按钮组件可在派生层做对比度校正,Seed 不随之改写 - **主色与表面分离**:品牌色负责交互和强调,页面、容器、浮层与文字由独立的背景和文本 Seed 派生 - **语义独立**:`success`、`warning`、`error`、`info` 不随品牌色隐式改变;未显式配置 `link` 时跟随 `primary`,显式配置后独立派生 - **无边设计**:默认依靠表面色差、间距和轻阴影表达层级;边框只用于焦点、错误和必要分隔 - **运行时可定制**:用户输入在浏览器中实时派生,因此使用 CSS Variables,不使用构建时 SCSS 变量 - **单一事实源**:Store 只保存最小配置,所有 Map、Alias 与未覆盖的 Component Token 均由纯函数生成 - **行为透明**:任意自定义 Seed 都进入统一算法,页面只选择角色。功能色 `text` 与 `on-container` 分别依据中性 Surface 和自身 Container 三态做对比度保护,不使用固定色相的文字锚点覆盖自定义 Seed;交互仍尊重 `prefers-reduced-motion` ## 界面文案与信息层级 管理端以查看状态和完成操作为主,延续紧凑的信息布局、清楚的主次关系,以及表面色差、间距和轻阴影构成的层级 - **先沿用已有设计**:先看原页面和最接近的同类实现,复用布局、控件、操作位置与状态展示,新增能力优先融入原有操作路径 只有现有设计无法承载本次任务时才改变结构,并说明具体问题,不以个人审美或设计技能中的通用建议为由重做页面 - **文案不使用句号和分号**:自行编写的标题、标签、按钮、表单帮助、空状态、提示和错误文案不使用 `。`、句末 `.`、`;` 或 `;`,优先写成短语或简短分句 需要表达多个要点时,按内容关系拆成字段、短行或列表,不把长段落机械替换成逗号串 这项约定针对界面文案,URL、版本号、小数、代码、原始日志和用户输入保留原样 - **页面直显以简洁为主**:标题、标签、按钮使用简短明确的名称或动作,说明只补必要信息,不重复标题、字段值和操作已能表达的内容,不同时用多种说明形式解释同一事实 页面保留当前决策必需的短提示,详细规则、背景原因、示例和低频帮助优先收进已有 Popover 或就近的帮助入口,不把实现细节写成用户教程 重要说明可以在 Popover 中展开,但影响当前选择的关键限制、错误原因和不可逆后果需在操作处保留简明提示,不能全部藏起来 - **按内容选择提示方式**:Tooltip 用于短名称或单句补充,Popover 用于需要阅读、多段说明或含链接的内容,复用项目现有组件 帮助入口需可识别、可通过键盘和触屏打开,图标提供可访问名称,不依赖悬停才能读到必要内容 - **避免大字报**:不堆叠超大标题、口号、整屏介绍、重复副标题或大段说明卡片,页面尽快进入数据、筛选和操作 页面标题与关键指标沿用已有组件的强调方式,辅助说明使用正文或次级文字层级,不通过放大字号、加粗整段或增加大块留白来突出普通说明 - **按任务组织空间**:优先使用紧凑的工具栏、表格、表单和必要的指标卡,卡片承担独立的信息或操作职责,不为每段说明单独套卡 密度以可读、可点击为边界,保留窄屏换行和必要的控件间距 开发时按任务选择参考入口,复用其职责和信息层级,具体布局仍以当前任务为准: | 场景 | 参考入口 | 关注点 | | --- | --- | --- | | 页面头部 | `@codex-proxy/ui` 的 `ZPageHeader`、[系统概览](../frontend/src/views/dashboard/components/DashboardContent.vue) | 简短标题、必要的统计范围或状态、就近操作 | | 内容与指标 | `@codex-proxy/ui` 的 `ZCard`、[用量概览卡片](../frontend/src/views/usage/components/UsageSummaryCards.vue) | 可选说明、紧凑摘要、数值与辅助信息的主次 | | 表单帮助 | [API Key 账号字段](../frontend/src/views/accounts/components/AccountApiKeyFields.vue) | 直接命名字段,在对应位置提供短提示和示例 | | 详细规则 | [自动审批并发帮助](../frontend/src/views/settings/components/ConcurrencyPolicyCard.vue) | 标签旁帮助图标打开 Popover,页面保留短提示,浮层补充规则和示例 | | 账号与用量展示 | [账号额度窗口](../frontend/src/components/account/account-usage-window/index.vue)、[共用用量表格](../frontend/src/components/usage/UsageRecordsTable.vue) | 复用后端展示字段和共享 presenter,管理端与 Key 页保持同一展示口径 | 页面查询、表单状态与纯展示逻辑的归属见 [前端模块职责](architecture.md#34-前端模块职责) ## 语言与字体 应用使用 Vue I18n Composition API,语言标识为 `en`、`zh-Hans`、`zh-Hant`。资源按领域存放在 `frontend/src/i18n/locales/`,使用 `领域.功能.用途` 的语义 key,例如 `accounts.connectionTest.description`,三种语言保持相同 key 和具名参数 默认语言为简体中文,浏览器本地保存的有效语言选择优先;未保存选择、保存值无效或存储不可用时使用简体中文 业务语言包及对应 UI 文案按语言加载,首次只加载当前语言,加载成功后缓存资源。缺失消息显示为空,不回退到其他语言或显示 key;切换时语言包加载失败则保留当前语言 - 标签、帮助、ARIA、通知和图表显式取消息;列定义、选项和展示投影使用响应式计算,不把翻译结果存进业务数据 - 完整句子使用具名参数,含组件的消息使用 `I18nT`;数量使用 Vue I18n 复数规则,不拆词拼接 - 专业缩写在三种语言中保留原样及规范大小写,例如 `TTFT`、`RPM`、`TPM`、`API`、`HTTP`、`WS`、`RC`,直接显示时不翻译或展开,必要的全称和解释放入帮助提示 - 英文使用句首大写和美式拼写,统一 Account、Proxy、Usage、Rate limit、API key、TTFT 等术语,协议标识和用户值保留原样 - 繁体中文只转换简体原文的字形,保持措辞与语序,不替换地区用词,例如 `数据 → 數據`、`设置 → 設置` - 只翻译应用拥有的文本,具体后端错误、上游诊断、日志和独立插件内容保留原文;固定错误码在请求层映射,业务分类码优先保留后端公开的具体原因,无原因时使用本地兜底文案 - 侧边栏语言图标展开就地选项,选择后切换并收起;品牌区固定显示 `Codex Proxy`、`Rust build` 和版本号 - UI 库默认文案由 `ZConfigProvider` 提供,命令式通知通过 `utils/notification.ts` 绑定同一个语言来源,组件约定见 [UI 国际化](../modules/ui/playground/guide/i18n.md) 正文默认使用 Inter Variable,中文及缺失字形按系统无衬线字体栈回退。代码使用 JetBrains Mono Variable,中文由系统字体补齐,不随语言切换额外修改字重。默认值由 UI 库持有,应用负责加载字体包,图表读取同一字体 Token ## 架构概览 ```mermaid flowchart LR A[Pinia 持久化配置] --> B[resolveTheme] B --> C[Seed Token] C --> D[Map Token] D --> E[Alias Token] E --> F[Component Token] F --> G[#cp-runtime-theme] G --> H[Vue / Tailwind / ECharts] B --> I[Theme Editor 草稿] I --> J[Shadow DOM 预览] J --> K[首页画板 / 组件概览] ``` 核心职责保持分离: | 层 | 入口 | 职责 | | --- | --- | --- | | 基础组件 | `@codex-proxy/ui` | 稳定组件与通用函数,不承载业务状态 | | 主题算法 | `@codex-proxy/ui/theme` | 规范化配置,派生 Seed → Map → Alias → Component,生成并提交 CSS Variables | | 样式 | `@codex-proxy/ui/styles.css` | 基线、组件样式与完整工具类样式 | | Tailwind 合同 | `@codex-proxy/ui/tailwind.css` | 公开 Token 名称,供应用自己的 Tailwind 构建使用 | | 状态 | [`stores/modules/theme.ts`](../frontend/src/stores/modules/theme.ts) | 持久化配置、系统明暗偏好、切换动作与动画 | | 编辑器状态 | [`useThemeEditor.ts`](../frontend/src/views/theme/composables/useThemeEditor.ts) | 草稿、修改计数、恢复与保存 | `codex-proxy-ui` 的组件与主题不依赖宿主 Store、路由或 API。应用通过上述公开入口消费;插件页面把 UI、Vue 和 Tailwind CSS 4 样式编译进包内静态资源,不借用宿主运行时模块。 插件页面的标题与副标题由宿主呈现,内容区只渲染业务;主题变化通过宿主桥同步,接入方式见 [SDK 页面与宿主桥](../backend/crates/gateway-plugin/sdk/docs/capabilities.md#页面宿主桥-v2)。 `theme/` 根目录只保留公开入口 `index.ts` 和唯一类型文件 `types.ts`;内部实现按 `core/`、`derive/`、`runtime/` 分层,不增加嵌套 barrel。 纯派生模块不访问 DOM,`theme/runtime/browser.ts` 不包含派生规则,Theme Store 不复制算法。 普通 Map 字段按 camelCase → kebab-case 统一生成 `--cp-*`;Semantic 与 Preset Color 仅维护各自的短角色表。 `ThemeTokenName` 在 `types.ts` 中由 Map 契约推导,无需手写重复的联合类型和对象映射 下文未带宿主路径的 `theme/`、`styles/` 均指 `codex-proxy-ui/src/` 下的目录,联调入口见[开发文档](development.md#源码联调) ## Token 模型 主题采用四层模型: ```text Seed Token → Map Token → Alias Token → Component Token ``` ### Seed Token Seed 是用户配置与持久化的最小事实: ```ts type ThemeMode = 'system' | 'light' | 'dark' type ThemeColorId = 'relay-blue' | 'deep-teal' | 'signal-violet' | 'graphite' | 'custom' interface ThemeCustomization { seed?: ThemeSeedOverrides component?: ThemeComponentOverrides tokenOverrides?: Partial } ``` | 类别 | Seed | | --- | --- | | 品牌 | `colorPrimary`,由预置 ID 或自定义 HEX 提供 | | 功能色 | `colorSuccess`、`colorWarning`、`colorError`、`colorInfo`、`colorLink` | | 中性色 | `colorTextBase`、`colorBgBase` | | 尺寸 | `fontSize`、`sizeUnit`、`sizeStep`、`controlHeight` | | 风格 | `borderRadius`、`shadowStrength` | 表格行高与卡片圆角通过 `customization.component` 的 `tableRowHeight`、`cardBorderRadius` 配置,属于组件尺寸覆盖 持久化键为 `codex-proxy-rs-theme`。Store 不保存完整色板或 CSS Variables,损坏的模式、颜色与自定义值会在 初始化时规范化并回退到默认配置 ### Map Token #### 品牌与功能色 品牌色和功能色使用 `@ant-design/colors` 从一个 Seed 生成十阶色板。本文 P1-P10 指生成器返回数组的第 1-10 项, 不是 Ant Design 组件库再次重映射后的角色编号。项目的映射集中在 `theme/derive/roles.ts`: | 角色 | 浅色 | 深色 | | --- | --- | --- | | 全局 Primary Base | Seed | Seed | | 功能色 Base | P6 | P6 | | Primary / 功能色 Hover、Active | P5、P7 | P8、P6 | | Primary Text Hover、Text、Text Active | P5、P6、P7 | P8、P7、P6 | | Primary On Container | P6 | P7 | | 功能色 Text Hover、Text、Text Active | P5、P7、P7 | P8、P7、P10 | | 功能色 On Container | P6 | P9 | 弱语义 Container 由中性 Container 与对应 Base/Hover/Active 按 recipe 权重混合;描边同样混色,再相对中性 Container 保证 3:1。功能色 Hover/Active 相对中性 Container 保证 3:1,Base 不做该校正。`text` 依据页面、 容器、浮层和交互填充等中性 Surface 校正;`on-container` 单独依据语义 Container 的默认、Hover、Active 三态 校正到至少 4.5:1,避免为了彩色底对比度而削弱中性表面上的颜色辨识度。 对比度校正由 UI 库的主题算法统一执行;互相矛盾的自定义前景/背景组合不保证全部达标 #### 分类与图表色 分类、图表与数据强调使用 Ant Design Preset Color 的角色结构。Blue、Green、Orange、Red 分别复用 `colorInfo`、`colorSuccess`、`colorWarning`、`colorError`,保证通用彩色与可编辑语义 Seed 同源;没有语义对应的 Cyan、Purple 从 `@ant-design/colors` 的 `presetPrimaryColors` 取得 Seed。 Preset 的实心色使用 P6,普通 Container、Strong Container 与边界按 recipe 权重混合;浅色文字取 P7,深色取 P8 并保留 HSL 最低明度 0.7。`text` 相对中性 Container 校正,`on-container` 同时相对普通和 Strong Container 校正到 4.5:1;彩色容器里的文字与图标应使用 `on-container`,普通数值与标签不直接使用 `solid` #### 中性表面与文字 背景与文本 Seed 进入独立的 Surface Map,生成: - `colorBgLayout / Container / Elevated / Spotlight / Mask` - `colorFillSecondary / Tertiary / Quaternary` - `colorText / Heading / Secondary / Tertiary / Quaternary / Disabled` - `colorBorder / BorderSecondary / Split / Shadow` 中继蓝使用稳定的浅色 / 深色中性基线;深海青、古风色与石墨预置额外提供各自的 `colorBgBase`、`colorTextBase` 画像,自定义主色则只向通用中性基线注入少量色温。默认浅色采用中性锚点,默认深色使用 HSL 色调派生;带色温 主题按外观距离平滑过渡到背景和文字 Seed 的 RGB 混色结果。稳定锚点用于 Surface 与 Shadow,Component 从角色派生, 所有预置、自定义 Seed 和用户覆盖仍进入同一条算法,不在页面或组件中追加 HEX 特判。 Input、阴影与其他 Component Token 从 Fill、Surface、Primary 和 Semantic 派生,不在常量文件维护整套颜色表 正常文字同时检查 Layout、Container、Elevated、文字交互背景、三级 Fill,以及控件透明填充在各宿主上的合成色 和浅色的选中 / 选中 Hover 背景。正文、标题和 Secondary 至少 7:1, Tertiary 至少 5.5:1,Quaternary 至少 4.5:1;这些是相对全部上述表面的最低目标,对 Container 的实测比值通常更高。 Disabled 保留独立的弱化颜色,不承担正常信息。控件填充由文字 Seed 和透明度派生,placeholder 继续消费 Quaternary;主按钮白字与功能色文字遵循各自的容器配对规则 ### Alias Token Alias 按视觉角色命名,使用 `--cp-` 命名空间;整体分层对齐 Ant Design,并以 Container / On Container 表达成对的 语义表面与前景: | 角色 | CSS Token | 主要消费者 | | --- | --- | --- | | 品牌交互 | `--cp-color-primary*` | 主操作、选中态、焦点与强调 | | 链接 | `--cp-color-link*` | 普通文本链接 | | 表面 | `--cp-color-bg-*` | 页面、容器、浮层和遮罩 | | 填充 | `--cp-color-fill-*` | 轨道、弱背景和静态层级 | | 文字 | `--cp-color-text*` | 标题、正文和辅助信息 | | 选择 | `--cp-control-item-bg-active*` | Segmented、Select 和选择控件 | | 焦点 | `--cp-control-outline` | 键盘焦点和输入反馈 | | 功能色 | `--cp-color-info / success / warning / error-*` | 系统反馈与状态 | | 预设彩色 | `--cp-color-{blue,cyan,green,orange,purple,red}-{container,container-strong,border,solid,text,on-container}` | 分类标签与数据强调 | Alias 不在页面中追加修色;中性与彩色文字在派生层执行各自的背景配对约束。Component Token 直接覆盖时不会自动重算同组件的其他状态;需要保持梯度关系时应修改 Seed,而不是逐个覆盖 Map Token ### Component Token 当组件需要独立演进时,使用 `component[-part][-variant][-state]-property` 命名,不创造含义重复的全局别名 | 组件 | 代表 Token | | --- | --- | | Button | `--cp-button-primary-color / bg / hover-bg / active-bg`、`--cp-button-secondary-bg / hover-bg / active-bg` | | IconButton | `--cp-icon-button-secondary-bg / hover-bg / active-bg` | | Input | `--cp-input-bg / hover-bg / active-bg / error-active-bg` 与对应 Shadow | | Menu | `--cp-menu-item-selected-bg` | | Popover | `--cp-popover-header-bg` | | Table | `--cp-table-row-bg / stripe-bg / hover-bg / selected-bg / selected-hover-bg / height` | | Card | `--cp-card-bg / border-radius / shadow` | | Layout | `--cp-layout-sider-bg / shadow` | | Scrollbar | `--cp-scrollbar-thumb-bg / hover-bg` | | BrandMark | `--cp-brand-mark-bg` | 组件状态由 UI 库的 Component 派生层统一生成,页面不维护混色系数: | 角色 | 状态与可读性约束 | | --- | --- | | 主按钮 | 白字背景至少达到 4.5:1;Hover / Active 加深,不反向改写 Primary Seed | | 输入控件 | Input、Select、Textarea、InputNumber 共用 Token;常态无边,Hover 与 Focus 保持相同背景和外圈 | | 次级操作 | 普通按钮与图标按钮各用自己的 Secondary Token;透明填充与宿主表面合成,`text` 与 `link` 无常态底色 | | 表格 | 表头、斑马纹、Hover 独立派生;固定列使用不透明合成背景,选中行保留 Primary 语义 | | 错误与焦点 | Hover / Focus 不覆盖错误反馈;焦点外圈不受装饰性阴影强度影响 | | 浮层头部 | 浅色取 Secondary Fill,深色取 Tertiary Fill;箭头与头部使用同一 Token,不借用表格背景 | 明暗模式分别派生,但共用组件合同。表格背景过渡覆盖普通行、选中行和固定列,尊重减少动态效果偏好 可编辑字段与覆盖限制见[主题编辑器](#编辑能力) 应用内品牌图标使用 [`AppBrandMark.vue`](../frontend/src/components/AppBrandMark.vue):浅色模式取 `colorBgSpotlight`,深色模式取 `colorBgElevated`,保持中性暗面与白色图形;浏览器 favicon 继续使用固定黑白 图标,不随主题改变,由 `frontend/public/favicon.svg` 提供,页面通过 `/favicon.svg` 引用 ### 通用颜色消费 主题层禁止声明账号套餐、推理类型或具体页面名称。业务组件只能选择通用 Preset Color 角色,例如 Cyan Container、 Purple Strong Container 或 Purple Solid;同一组颜色仍由运行时色板统一派生。`styles/tokens.css` 只保留白色、透明色与 作用域 `color-scheme`,不保存可换肤值或业务标识色。账户活动热力图以 Success Container 为起点、Success Solid 为终点生成中间密度;浅色与暗色使用同一规则。 图表数据系列也只引用通用 Preset Color Token ## 预置主题 | ID | 名称 | 主色 | 交互方向 | | --- | --- | --- | --- | | `relay-blue` | 中继蓝 | `#5983F4` | 清晰可信的冷蓝强调 | | `deep-teal` | 深海青 | `#0E7C72` | 冷静低饱和的青色强调 | | `signal-violet` | 古风色 | `#A0583D` | 温厚克制的赭色强调 | | `graphite` | 石墨 | `#525B66` | 近单色的灰色强调 | 预置主题改变品牌主色,并可提供与该品牌匹配的浅色 / 深色背景和文字 Seed;不改变字号、密度、圆角或布局。 所有预置与自定义主题走同一条 Surface、Map、Alias 与 Component 派生链路,不存在页面级特判 ## 运行时架构 ### 启动顺序 [`main.ts`](../frontend/src/main.ts) 在 Vue 挂载前初始化主题: ```text createApp → app.use(pinia) → persistedstate 同步水合 Theme Store → themeStore.initializeTheme() → app.use(router / auth) → app.mount('#app') ``` Theme Store 统一读取持久化配置并初始化主题,首个 Vue 组件渲染时即可使用主题变量 ### CSS Variables 提交 全局主题由 `` 中唯一的运行时样式节点承载: ```html ``` 根元素只保留 `data-theme` 与 `data-theme-color` 状态,Token 统一写入运行时样式表。 全局弹窗通过 Teleport 挂到 `body` 后仍继承根变量 > [!NOTE] > Theme Editor 预览是例外:草稿 Token 以内联变量写在影子环境的局部根节点上,仅影响预览,不污染已保存主题 ### 切换与图表 - 用户主动切换时,以点击位置为圆心运行 View Transition;不支持时使用 180ms 颜色过渡 - 初始化、系统主题变化或减少动态效果时直接提交,不播放扩散动画 - 每次有效提交只增加一次 `themeRevision`,相同签名不会重复刷新 - `useThemeColor()` 在正式页面读取根 CSS Variables,在影子预览中优先读取注入的局部 Token - 图表 Option 在主题 revision 或预览 Token 改变后重算,`BaseChart` 通过 `setOption` 更新现有 Canvas ## 主题编辑器 主题编辑器位于一级路由 `/theme`,采用一屏工作台:左侧编辑,右侧预览,顶部保留全局操作。桌面端高度固定为 `100dvh - 3rem`,切换编辑层级或预览类型时不改变外框高度。窄屏头部操作允许自然换行;手机端通过预览与编辑按钮 切换 Token 面板和实时画板,复用相同草稿、预览类型及缩放状态 ### 编辑能力 | 区域 | 能力 | | --- | --- | | 全局 / 颜色 | 模式、预置、自定义主色、功能色、链接、基础文本与背景、派生变量查看 | | 全局 / 尺寸 | 字号、基础间距、尺寸步长、控件高度 | | 全局 / 风格 | 通用圆角、阴影强度 | | 组件 | Action、Form、Surface、Data Display、Navigation、Layout | | 工作流 | 搜索、单项恢复、撤销草稿、恢复默认、保存并应用 | Component Token 只开放对应组件实际消费的白名单字段,不允许在组件目录覆盖全局 Alias。未覆盖项使用全局 Seed 与 Alias 算法,避免主题配置逐渐退化成一份无法维护的完整 CSS 快照。 Input、Button Secondary 与 Icon Button Secondary 的三个背景 Token 支持 HEX Alpha 编辑和保存;Seed、容器与表格背景仍只接受实色。 透明填充先合成再参与默认文字对比度计算,用户显式覆盖 Component Token 时仍由用户负责整组状态的搭配 ### 草稿与保存 编辑器维护 `saved` 与 `draft` 两份状态: 1. 输入只更新草稿和局部预览 2. 修改计数按模式、主题色、Seed、组件值和 Token override 分项统计 3. “撤销草稿”恢复到最近一次已保存配置 4. “保存并应用”先规范化草稿,再原子更新 Theme Store Store 在其他入口发生变化时更新保存基线;没有未保存修改才同步替换当前草稿,避免覆盖正在编辑的内容 ### 隔离预览 [`ThemePreviewScope.vue`](../frontend/src/views/theme/components/ThemePreviewScope.vue) 创建开放 Shadow Root,复制应用 样式,并把预览内容 Teleport 到影子根中。预览可独立切换浅色和深色,不受外层主题影响 - **首页画板**复用真实 `DashboardContent` 和固定 fixture,不请求接口,也不启动自动刷新 - **组件概览**展示基础组件、表格、空状态、骨架、浮层和菜单等关键状态 - 画板固定为 `1600 × 1808`,使用 CSS `zoom` 重排,不使用 `transform: scale()` 长期缩放文字 - 空白区域可拖拽,滚轮以指针为锚点缩放,并提供缩小、100%、放大和适应画板操作 - 编辑面板和组件概览统一使用 `ZScrollbar`,滚动条空闲时自动隐藏 ## 样式与命名约定 ### CSS Token - 全局 Token:`--cp-color-bg-container`、`--cp-font-size` - Component Token:`--cp-table-row-hover-bg`、`--cp-input-active-shadow` - Preset Color Token:`--cp-color-purple-container-strong`、`--cp-color-cyan-on-container` - 业务组件按[通用颜色消费](#通用颜色消费)选择角色,不在主题层声明业务域名称 - Map 与 Component 字段由 `theme/core/tokens.ts` 统一生成 CSS Token;禁止在解析器中再写平行的逐项映射表 - 不使用 `accent`、`soft`、`current`、`subtle` 等与现有角色重叠的平行词汇 ### Vue 与 Tailwind CSS 4 组件优先使用 `bg-cp-*`、`text-cp-*`、`shadow-cp-*`、`rounded-cp-*` 等由 `@theme inline` 暴露的 utility: ```vue
...
``` 仅当值需要在 CSS 函数、SVG 或局部派生中参与计算时,直接读取 `var(--cp-*)`。不在页面组件中重新实现色阶、 对比度或明暗算法 共享包的 `styles/tailwind.css` 是唯一 `@theme inline` 注册表,只注册 Tailwind 名称,不保存主题值。注册表按基础、排版、颜色、圆角、阴影、间距与尺寸排序; 颜色再按基元、表面、主色与链接、语义、预设、数据、组件分组。 Preset 家族按字母排序,每个家族固定使用 `container → container-strong → border → solid → text → on-container`。主题值由 `initializeTheme()` 在 Vue 挂载前动态生成并提交,不维护构建期快照 全局元素基线统一放进共享包 `styles/base.css` 的 `@layer base`,确保组件 utility 可以按 Tailwind 层级正常覆盖;可复用的 原生滚动条声明使用 Tailwind CSS 4 `@utility`。组件内能等价表达的简单 SVG、渐变、原生外观与伪元素优先使用 utility / arbitrary variant;Vue Transition、跨浏览器 Range、动态富文本 `:deep()`、复杂纹理与关键帧继续保留局部 `