- 重构版主提示词(14 parts,面向 ClassHelper 灵动岛重构) - ClassIsland 全量规格提示词(20 parts,含分卷与逐章版本) - 投喂指南、分卷、逐节文件 - 调研记录、一致性基线、生成与质检脚本
429 lines
43 KiB
Markdown
429 lines
43 KiB
Markdown
## 6. 设计 tokens 与视觉基线(双风格:ClassIsland 默认 / winisland 兼容)
|
||
|
||
本节是整轮重构的「视觉地基」。实现者必须先把 token 落到 `packages/desktop-client/src/island/` 下的独立样式入口,再改任何两个岛的渲染文件。所有像素、颜色、字号、圆角、阴影、透明度必须从这里取值,**禁止**在 `IslandApp.vue`、`CustomIslandRuntime.vue`、`ComponentNode.vue`、`ClassicNotifications.vue`、`LessonReminderContent.vue`、`custom-runtime.css` 里写裸值。
|
||
|
||
### 6.0 双风格模型(必须先建,再谈 token)
|
||
|
||
外观契约新增/改为(`packages/shared/src/types.ts` 的 `IslandAppearance` 扩展,`config.ts` 的 `normalizeIslandAppearance` 同步):
|
||
|
||
| 字段 | 类型 | 默认 | 含义 | 证据 |
|
||
|---|---|---|---|---|
|
||
| `family` | `'classisland' \| 'winisland'` | 新装 `classisland`;**老用户升级为 `winisland`**(见 §6.12 迁移规则) | 视觉家族 | Lead 裁决第 1 条(`parts-r/01-objective.md:60`) |
|
||
| `theme` | `'fluent' \| 'classic'` | `fluent` | 仅 `family='classisland'` 有效;对应 ClassIsland 两套 XamlTheme | `repos/ClassIsland/ClassIsland/App.Services.xaml.cs:297-315` |
|
||
| `colorScheme` | `'light' \| 'dark' \| 'system'` | `system` | 仅 `family='classisland'` 有效;跟随应用深浅色 | `repos/ClassIsland/ClassIsland/Models/Settings.cs:979-989`、`Services/ThemeService.cs:53-63` |
|
||
| `style` | `'black' \| 'glass' \| 'tinted'` | `black` | 仅 `family='winisland'` 有效(现有语义不变) | `packages/shared/src/constants.ts:265-290,324-330` |
|
||
| `animations` | `boolean` | `true` | 岛动画总开关 | `constants.ts:273`;`IslandApp.vue:249` |
|
||
| `speed` | `number` | `1`(0.5–2) | 动画速度倍率(按比例缩放所有时长) | `constants.ts:274`;`IslandApp.vue:291` |
|
||
| `fontSize` | `number` | `13`(11–20) | **base 字号**,四档字号由它派生 | `constants.ts:272`;`IslandApp.vue:527` |
|
||
| `height` / `width` | number | 44 / 268 | **仅 winisland** 的胶囊几何;classisland 下用于「信息行最小宽/高」约束(见 §6.5) | `constants.ts:266-267`;`IslandApp.vue:188-204` |
|
||
| `radius` | number | 20(8–32) | winisland 胶囊基准半径;classisland 下取 `8`(范围 0–20),见 §6.4 | `constants.ts:268`;`IslandApp.vue:209-211` |
|
||
|
||
实现要求:
|
||
|
||
1. 岛根元素必须同时挂三个数据属性,CSS 用它们切 token 与几何:`data-family="classisland|winisland"`、`data-theme="fluent|classic"`、`data-color-scheme="light|dark"`(winisland 恒为 `dark`)。禁止用 JS 拼内联样式切风格。
|
||
2. `family='classisland'` 时**不使用** `spring.ts` / `squircle.ts`:几何用标准 `border-radius` + CSS transition;`family='winisland'` 时继续用弹簧形变与超椭圆路径(Lead 裁决第 4 条,`parts-r/01-objective.md:52`)。
|
||
3. 两套 family 的 token **不得混用**:classisland 的静态值定义在 `--ci-*`;winisland 的调色板保持 `--wn-*`;运行时外观注入继续用 `--island-*`(两套共用这几个动态变量:`--island-font`/`--island-radius`/`--island-accent`/`--island-dur`/`--island-anim`)。
|
||
4. Token 文件必须是**单一来源**:新建 `packages/desktop-client/src/island/tokens.css`,在 `island.html` 的样式链最前面 import(`src/island/main.ts` 已 import `@classhelper/ui/tokens.css`,见 `main.ts:1`),并由 `IslandApp.vue` 与 `CustomIslandRuntime.vue` 共同继承。禁止在 `.vue` 的 `<style>` 里重新定义全局 token。
|
||
5. 主题切换动画只允许改颜色类 token,**绝不**覆盖圆角/间距/字号/动画 token(与主窗口纪律一致:`docs/dev/motion.md:125-126`)。
|
||
|
||
### 6.1 变量分层与命名规范
|
||
|
||
| 层 | 前缀 | 归属 | 是否可被运行时覆盖 | 本重构动作 |
|
||
|---|---|---|---|---|
|
||
| 1 应用外壳 | `--ch-ui-*`、`--ch-*`、`--el-*` | `packages/ui/src/tokens.css`、`theme.css`、`renderer/styles/index.css` | 是(主窗口主题) | **KEEP,不改值**;岛只读引用(如编辑器 UI) |
|
||
| 2 ClassIsland 岛静态 token | `--ci-*` | 新增 `src/island/tokens.css` | 否(改值只能改源码) | **NEW** |
|
||
| 3 岛运行时外观 | `--island-*` | `IslandApp.vue:522-528`、`CustomIslandRuntime.vue:102-123` 注入 | 是(设置页实时) | **KEEP 并扩展**(补 `--island-row-*`) |
|
||
| 4 winisland 调色板 | `--wn-*` | `classic-island.css:60-82`(`.island-card` 内) | 是(`data-style` 切换) | **KEEP**(仅 winisland 生效) |
|
||
| 5 组件方案 | `--custom-*`、`--resident-*`、`--row-gap` | `custom-island.css`、`custom-runtime.css` 由 `scheme` 注入 | 是 | **KEEP**,值按 §6.5 改默认 |
|
||
|
||
命名冲突处理(必读):现有 `LessonReminderContent.vue:133-137` 已在组件作用域注入 `--ci-speed`、`--ci-duration`、`--ci-title-duration`、`--ci-scale-duration`、`--ci-delay`,`ComponentNode.vue`/`custom-runtime.css` 还使用 `--ci-body-font`。这些是**组件级**变量,与本节新增的静态 token 同前缀,容易误伤。处理办法:
|
||
- 新增静态 token 中**禁止**使用这 6 个名字;
|
||
- M5 阶段把 `LessonReminderContent.vue` 的 5 个变量重命名为 `--ci-lesson-speed/-duration/-title-duration/-scale-duration/-delay`,保留旧名作为 alias(`var(--ci-lesson-duration, var(--ci-duration, 200ms))`)一版后删除;
|
||
- `--ci-body-font` 保留(custom 行基准字号),在 tokens.css 中定义为 `var(--ci-fs-body)`。
|
||
|
||
### 6.1.1 `--ci-*` 变量速查表(必须在 `tokens.css` 中一次性定义齐)
|
||
|
||
实现者按此表逐行写入,缺一个都会在第 7/10 部分的样式里解析为空、导致整条声明失效。
|
||
|
||
| 变量 | 默认(classisland 深色) | 用途 |
|
||
|---|---|---|
|
||
| `--ci-bg` | `#202020` | 岛/行背景色(配合 `--ci-opacity-bg`) |
|
||
| `--ci-bg-message` | `#202020` | 阅读消息/详情实底 |
|
||
| `--ci-fill-1` | `rgba(255,255,255,.06)` | 行内浅底(列表行、chip) |
|
||
| `--ci-fill-2` | `rgba(255,255,255,.10)` | 悬停底、按钮底 |
|
||
| `--ci-fill-3` | `rgba(255,255,255,.14)` | 徽标底、强强调底 |
|
||
| `--ci-border` | `rgba(255,255,255,.15)` | 岛/行 1px 描边 |
|
||
| `--ci-border-strong` | `rgba(255,255,255,.24)` | 选中/拖拽目标描边 |
|
||
| `--ci-border-accent` | `var(--ci-accent)` | 强调边(遮罩边缘) |
|
||
| `--ci-divider` | `rgba(255,255,255,.09)` | 行内分隔线 |
|
||
| `--ci-focus-ring` | `var(--ci-accent)` | 焦点环颜色 |
|
||
| `--ci-text-1` | `#FFFFFF` | 主文字 |
|
||
| `--ci-text-2` | `rgba(255,255,255,.855)` | 次要文字 |
|
||
| `--ci-text-3` | `rgba(255,255,255,.773)` | 辅助文字 |
|
||
| `--ci-text-4` | `rgba(255,255,255,.529)` | 装饰文字 |
|
||
| `--ci-text-5` | `rgba(255,255,255,.365)` | 禁用文字 |
|
||
| `--ci-text-on-accent` | `#FFFFFF` | accent 底上文字 |
|
||
| `--ci-accent` | `#00BFFF`(运行时被 `--island-accent` 覆盖) | 强调色 |
|
||
| `--ci-accent-soft` | accent @10% | 强调淡底 |
|
||
| `--ci-accent-soft-strong` | accent @14% | 强调淡底加强 |
|
||
| `--ci-state-urgent` | `#FF453A` | 紧急 |
|
||
| `--ci-state-call` | `#FF9F0A` | 叫人 |
|
||
| `--ci-state-homework` | `var(--ci-accent)` | 作业 |
|
||
| `--ci-state-ok` | `#228B22` | 正常/优 |
|
||
| `--ci-warning` | `#FFA500` | 警告 |
|
||
| `--ci-danger` | `#CD5C5C` | 危险/错误 |
|
||
| `--ci-tint-urgent` | 径向 `rgba(255,69,58,.22)` | 紧急内层染色 |
|
||
| `--ci-tint-call` | 径向 `rgba(255,159,10,.20)` | 叫人内层染色 |
|
||
| `--ci-progress-h` | `3px` | 行内进度条高度 |
|
||
| `--ci-progress-track` | `rgba(255,255,255,.15)` | 进度条轨道 |
|
||
| `--ci-progress-fill` | `var(--ci-accent)` | 进度条填充 |
|
||
| `--ci-progress-radius` | `0` | 进度条圆角 |
|
||
| `--ci-mask-fill` | `var(--ci-accent)` | 斜切遮罩填充 |
|
||
| `--ci-mask-text` | `#FFFFFF` | 遮罩文字 |
|
||
| `--ci-radius-island` | `8px` | 岛圆角(0–20) |
|
||
| `--ci-radius-row` | `8px` | 单行圆角(分体) |
|
||
| `--ci-radius-card` | `8px` | 详情卡/气泡圆角 |
|
||
| `--ci-radius-control` | `4px` | 控件/列表行圆角 |
|
||
| `--ci-radius-pill` | `999px` | 胶囊/徽标圆角 |
|
||
| `--ci-row-h` | `40px` | 行高 |
|
||
| `--ci-row-pad-x` | `12px` | 行内左右内边距 |
|
||
| `--ci-row-min-w` | `20px` | 行内容最小宽 |
|
||
| `--ci-row-gap` | `5px` | 行间距(可调 0–150) |
|
||
| `--ci-comp-gap` | `12px` | 相邻组件间距(单侧 6) |
|
||
| `--ci-shadow-pad` | `10px` | 窗口/阴影留白 |
|
||
| `--ci-shadow-island` | `0 4px 8px 2px rgba(0,0,0,.28)` | 岛投影 |
|
||
| `--ci-shadow-mask` | `0 8px 32px rgba(0,0,0,.4)` | 提醒时投影 |
|
||
| `--ci-shadow-popup` | `0 8px 16px rgba(0,0,0,.45)` | 岛内弹层投影 |
|
||
| `--ci-opacity-bg` | `0.5` | 背景不透明度 |
|
||
| `--ci-opacity-faded` | `0.05` | 指针避让淡化 |
|
||
| `--ci-fs-secondary` | `14px`(派生) | 次要字号 |
|
||
| `--ci-fs-body` | `16px`(派生) | 正文字号 |
|
||
| `--ci-fs-emphasized` | `18px`(派生) | 强调字号 |
|
||
| `--ci-fs-large` | `20px`(派生) | 大号字号 |
|
||
| `--ci-fw-regular` | `400` | 常规字重 |
|
||
| `--ci-fw-medium` | `500` | 中等字重 |
|
||
| `--ci-fw-bold` | `700` | 粗字重 |
|
||
| `--ci-font` | HarmonyOS 栈 | 正文字体族 |
|
||
| `--ci-z-mask` | `2` | 遮罩层 |
|
||
| `--ci-z-mask-text` | `3` | 遮罩文字层 |
|
||
| `--ci-z-popup` | `4` | 岛内弹层 |
|
||
| `--ci-z-drag-ghost` | `10000` | 编辑器拖拽幽灵 |
|
||
| `--ci-dur-instant` | `90ms` | 高频反馈 |
|
||
| `--ci-dur-fast` | `100ms` | 淡出/短过渡 |
|
||
| `--ci-dur-base` | `200ms` | 默认过渡 |
|
||
| `--ci-dur-slow` | `300ms` | 行宽(Fluent) |
|
||
| `--ci-dur-overshoot` | `550ms` | 行宽(Classic) |
|
||
| `--ci-dur-long` | `750ms` | 遮罩缩放 |
|
||
| `--ci-ease-out` | `cubic-bezier(.16,1,.3,1)` | ClassHelper 主缓动 |
|
||
| `--ci-ease-fl-1` | `cubic-bezier(.65,0,.35,1)` | Fluent 行宽 |
|
||
| `--ci-ease-fl-2` | `cubic-bezier(.25,1,.5,1)` | 遮罩文字/缩放 |
|
||
| `--ci-ease-fl-3` | `cubic-bezier(.5,0,.75,0)` | 遮罩退出 |
|
||
| `--ci-ease-overshoot` | `cubic-bezier(0,1,.5,1.1)` | Classic 行宽过冲 |
|
||
| `--ci-ease-quartic-out` | `cubic-bezier(.16,1,.3,1)`【推断】 | 区段扫入 |
|
||
| `--ci-ease-quadratic-in` | `cubic-bezier(.11,0,.5,0)`【推断】 | 区段收拢 |
|
||
| `--ci-ease-sine-in` | `cubic-bezier(.12,0,.39,0)`【推断】 | 覆盖层淡入 |
|
||
| `--ci-dur-mask-region` | `360ms` | 单个斜切区段 |
|
||
| `--ci-dur-mask-stagger` | `120ms` | 区段错峰 |
|
||
| `--ci-delay-mask-text` | `260ms` | 遮罩文字延迟 |
|
||
| `--ci-dur-mask-text` | `250ms` | 遮罩文字淡入 |
|
||
| `--ci-dur-mask-scale` | `750ms` | 遮罩文字缩放 |
|
||
| `--ci-delay-mask` | `600ms` | 遮罩投影延迟 |
|
||
| `--ci-dur-mask-shadow` | `100ms` | 遮罩投影/背景淡入 |
|
||
| `--ci-dur-mask-exit` | `200ms` | 遮罩退出 |
|
||
|
||
|
||
|
||
### 6.2 颜色 token 表
|
||
|
||
#### 6.2.1 背景与表面
|
||
|
||
| Token | ClassIsland 目标值(family=classisland) | ClassHelper 现状(变量/值) | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-bg` | 深色:`#202020` @ `opacity` 0.5(Fluent 用 `SolidBackgroundFillColorSecondaryBrush`,Classic 用 `SolidBackgroundFillColorBaseBrush`);浅色:`#F3F3F3` @ 0.5 | `--wn-bg`:`#000000`(black)/`rgba(32,32,36,0.804)`(glass)/`#0b0b0f`(tinted),见 `classic-island.css:60-82`;custom:`--wn-bg: #20202080`(`custom-runtime.css:241-245`) | **NEW** `--ci-bg`;classisland 下替换 `.island-card` 背景;winisland 保留 `--wn-bg`;custom 在 classisland family 下把方案默认色从 `#000000` 改为 `#202020`(`island-layout.ts:246`),并叠加 0.5 不透明度 |
|
||
| `--ci-bg-message` | `#202020`(不透明,阅读消息/详情时用实底) | `.message-surface { --wn-bg:#202020 }`(`classic-island.css:84-86`、`custom-runtime.css:253-255`) | **KEEP 值**,改名 `--ci-bg-message` 作为语义 token;winisland 继续用 `.message-surface` 覆盖 `--wn-bg` |
|
||
| `--ci-bg-elevated` | `rgba(255,255,255,0.06)`(行内 chip/进度底/列表行底) | `rgba(255,255,255,0.045)`(`.list-row`,`classic-island.css:389`)、`rgba(255,255,255,0.12)`(`.row-badge`、`.pill-type`)、`#ffffff0d`/`#ffffff1a`(custom) | **CHANGE**:classisland 统一到 6%/10%/14% 三档:`--ci-fill-1/2/3` |
|
||
| `--ci-fill-1/2/3` | 6% / 10% / 14% 白(深色);4% / 8% / 12% 黑(浅色) | 同上散落 | **NEW** |
|
||
| `--ci-mask-fill` | 强调色(`AccentFillColorDefaultBrush`,ClassIsland 提醒遮罩填充) | 当前无(ClassHelper 消息用胶囊/详情卡,无遮罩) | **NEW**,classisland fixed 消息气泡与 lesson 提醒共用 |
|
||
| `--ci-tint-urgent` | `rgba(255,69,58,0.22)` 径向 | `.urgent .tint` 同值(`classic-island.css:168-171`) | **KEEP 值**,归入 classisland 状态色 |
|
||
| `--ci-tint-call` | `rgba(255,159,10,0.20)` 径向 | `.call .tint` 同值(`:173-176`) | **KEEP 值** |
|
||
| `--ci-tint-tinted` | accent 30% → transparent 135° 渐变 | `[data-style='tinted'] .tint`(`:178-185`) | **KEEP**(仅 winisland tinted) |
|
||
| `--ci-bg-hover` | `rgba(255,255,255,0.10)`(classisland 行/按钮 hover) | `--wn-surface-hover: rgba(255,255,255,0.18)` / `#ffffff1a` | **CHANGE**:classisland 用 10%,winisland 保留 18% |
|
||
|
||
#### 6.2.2 描边
|
||
|
||
| Token | ClassIsland 目标值 | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-border` | 1px `rgba(255,255,255,0.15)`(深色)/ `rgba(0,0,0,0.08)`(浅色) | `--wn-border: rgba(255,255,255,0.118)`(`classic-island.css:62`);glass `0.157`(`:74`);custom `inset 0 0 0 1px #ffffff1e`(`custom-island.css:13`) | **NEW**;classisland 行容器 `border:1px solid var(--ci-border)`;winisland 保留描边画在 SVG path 上(`stroke-width:1`,`:110-113`) |
|
||
| `--ci-border-strong` | `rgba(255,255,255,0.24)`(选中/拖拽目标) | `#ffffff2b`(`custom-island.css:171`)、`.node-editing:hover` `#ffffff28`(`ComponentNode.vue:432`) | **NEW**,统一 24% |
|
||
| `--ci-border-accent` | accent 100%(遮罩/强提醒强调边) | `--state-color` 混合描边(`classic-island.css:156-159`,62% 状态色) | **CHANGE**:classisland 用 accent 纯色;winisland 保留混合规则 |
|
||
| `--ci-divider` | `rgba(255,255,255,0.09)`(行内分隔) | `--wn-divider: rgba(255,255,255,0.09)`(`:68`) | **KEEP 值**,语义化改名 |
|
||
| `--ci-focus-ring` | 2px accent,`outline-offset:2px`(设置/编辑器)+ `-3px`(岛内按钮) | `#8dafff`/`#adc4ff`/`#85aaff` 三处不同(`custom-island.css:65-68`、`custom-runtime.css:190-193`、`ComponentNode.vue:394`) | **CHANGE**:classisland 统一 `2px solid var(--ci-accent)`;winisland 保留原色 |
|
||
|
||
#### 6.2.3 文字阶梯
|
||
|
||
| Token | ClassIsland 目标值(深色) | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-text-1` | `#FFFFFF`(100%) | `--wn-text-1: rgba(255,255,255,0.92)`(`classic-island.css:63`) | **CHANGE**:classisland 用 100%(ClassIsland 深层级 255 alpha,见 `repos/ClassIsland/ClassIsland.Core/Helpers/UI/ControlColorHelper.cs:24-33`);winisland 保留 0.92 |
|
||
| `--ci-text-2` | `rgba(255,255,255,0.855)` | `--wn-text-2: 0.72`(`:64`) | **CHANGE**:classisland 85.5%;winisland 72% |
|
||
| `--ci-text-3` | `rgba(255,255,255,0.773)` | `--wn-text-3: 0.58`(`:65`) | **CHANGE**:classisland 77.3%;winisland 58% |
|
||
| `--ci-text-4` | `rgba(255,255,255,0.529)` | 无(classic 用 0.34 的 chevron,`:317`) | **NEW** |
|
||
| `--ci-text-5` | `rgba(255,255,255,0.365)` | `#ffffff60`(`custom-island.css:88`)、`#ffffff80`(`custom-runtime.css:145`) | **NEW** |
|
||
| `--ci-text-on-accent` | `#FFFFFF`(深色遮罩上) | `#fff`(`.title`,`classic-island.css:628`)、`#0b0b0f`(`.solid-btn`,`:711`) | **NEW**,遮罩/强提醒文字统一白;winisland solid-btn 保留深色字 |
|
||
| 浅色方案 | `#1B1B1B` / `rgba(0,0,0,0.855)` / `0.773` / `0.529` / `0.365` | 岛当前恒为深色 | **NEW**:classisland family 支持浅色(§6.10) |
|
||
|
||
字号必须同时遵守 `ISLAND_TYPE_SCALE`(winisland)与四档阶梯(classisland),见 §6.3。
|
||
|
||
#### 6.2.4 强调色与状态色
|
||
|
||
| Token | ClassIsland 目标值 | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-accent` | ClassIsland 默认 `#00BFFF`(DeepSkyBlue,`repos/ClassIsland/ClassIsland/Models/Settings.cs:35`);运行时由 `appearance.accent` 覆盖 | `DEFAULT_ISLAND_APPEARANCE.accent = '#0a84ff'`(`constants.ts:271`);`IslandApp.vue:107` 注入 | **KEEP 用户值**;`family='classisland'` 且用户从未改过 accent(仍等于旧默认 `#0a84ff`)时迁移为 `#00BFFF`;否则保留。`--island-accent` 继续作为运行时变量 |
|
||
| `--ci-accent-soft` | accent @ 10%(ClassIsland `CustomizedAccentBarBackground1Brush` 运行时 = accent@10%,`repos/ClassIsland/ClassIsland/Services/ThemeService.cs:66-74`) | `color-mix(in srgb, var(--island-accent) 20%/22%/26%, transparent)`(`classic-island.css:375,524,545`) | **CHANGE**:classisland 统一 10%/14% 两档 `--ci-accent-soft`、`--ci-accent-soft-strong` |
|
||
| `--ci-state-urgent` | `#FF453A` | `#ff453a`(`classic-island.css:170,260,514,551`) | **KEEP 值**,改名 token |
|
||
| `--ci-state-call` | `#FF9F0A` | `#ff9f0a`(`:175,256,519,567`) | **KEEP 值** |
|
||
| `--ci-state-homework` | accent | accent(`classic-island.css:263-265,417-419,523-526`) | **KEEP** |
|
||
| `--ci-state-ok` | `#228B22`(可选用天气/正常态) | 无 | **NEW**(与 ClassIsland AQI 1 同值,见 `Themes/LightTheme.axaml:10`) |
|
||
| `--ci-danger` | `#CD5C5C`(IndianRed,ClassIsland 危险文字) | `#ffb4ab`(课件错误,`CoursewareTransfer.vue:127`) | **KEEP 现状**(课件)+新增 classisland `--ci-danger` |
|
||
| `--ci-warning` | `#FFA500` | 无 | **NEW** |
|
||
| `--ci-on-state` | `#FFFFFF` | `.badge-urgent` `#ff6961` 等 | **KEEP winisland** |
|
||
|
||
#### 6.2.5 进度条与遮罩
|
||
|
||
| Token | ClassIsland 目标值 | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-progress-h` | `3px`(ClassIsland 行内进度条) | `2px`(`.lesson-progress`,`ComponentNode.vue:351`)、`2px`(`.ci-lesson-progress`,`LessonReminderContent.vue:312`)、`5px`(课件,`CoursewareTransfer.vue:111`) | **CHANGE**:classisland family 行内进度条 3px;课件保持 5px(不同组件,不受此 token 管) |
|
||
| `--ci-progress-track` | `rgba(255,255,255,0.15)` | `#ffffff24`(`ComponentNode.vue:352`) | **CHANGE** |
|
||
| `--ci-progress-fill` | `var(--island-accent)` | accent(`ComponentNode.vue:359`、`LessonReminderContent.vue:313`) | **KEEP** |
|
||
| `--ci-progress-radius` | `0`(ClassIsland 进度条直角) | `1px`(`ComponentNode.vue:354`) | **CHANGE**:classisland 0;winisland 保留 1px |
|
||
| `--ci-mask-fill` | accent(斜切遮罩填充,ClassIsland `AccentFillColorDefaultBrush`,`repos/ClassIsland/ClassIsland/XamlThemes/FluentTheme/Styles.axaml:180`) | `var(--island-accent, #60a5fa)`(`LessonReminderContent.vue:251`) | **KEEP 语义**,改用 `--ci-mask-fill` |
|
||
| `--ci-mask-text` | `#FFFFFF`(ClassIsland `TextOnAccentFillColorPrimaryBrush`) | `#fff` | **KEEP** |
|
||
|
||
#### 6.2.6 Fluent / Classic 主题色差(classisland family 内部)
|
||
|
||
| Token | Fluent(默认) | Classic | 证据 |
|
||
|---|---|---|---|
|
||
| 行背景 | `SolidBackgroundFillColorSecondaryBrush`(暗 `#1C1C1C`【推断】) | `SolidBackgroundFillColorBaseBrush`(暗 `#202020`【推断】) | `repos/ClassIsland/ClassIsland/XamlThemes/FluentTheme/Styles.axaml:45`;`ClassicTheme/Styles.axaml:214` |
|
||
| 描边 | 1px `ControlElevationBorderBrush` | 无显式描边 | `FluentTheme:46-47` |
|
||
| 阴影 | `0 4 8 2 #48000000` | 常态无投影(提醒时 `DropShadowEffect blur 8 opacity 1`) | `FluentTheme:49`;`ClassicTheme:36-45,283-287` |
|
||
| 圆角 | 用 `RadiusX`(默认 8,0–20) | 直角(圆角 0) | `FluentTheme:48`;`ClassicTheme:46-59` |
|
||
| 遮罩动画 | 斜切分块 + 文字缩放 | 整块下压/上滑 | 见第 10 部分 |
|
||
|
||
实现:`data-theme="fluent|classic"` 只切换上表 5 项与第 10 部分的动画参数,**不得**切换字号/间距。
|
||
|
||
### 6.3 字体 token
|
||
|
||
| Token | ClassIsland 目标值(classisland family) | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-font` | `'HarmonyOS Sans SC','Microsoft YaHei UI','Noto Sans CJK SC','Noto Sans SC','PingFang SC','Source Han Sans SC',SimHei,sans-serif`(ClassIsland 字体栈,`repos/ClassIsland/ClassIsland/Assets/Fonts/Fonts.axaml:3`) | `-apple-system,'Segoe UI Variable Display','Segoe UI','Microsoft YaHei','PingFang SC','Helvetica Neue',sans-serif`(`classic-island.css:22-25`) | **CHANGE**:classisland family 用新栈;winisland 保留旧栈。**不内置 TTF**(许可与体积原因,HarmonyOS 字体许可见其 `LICENSE.txt`);若后续要内置,只加 `@font-face` 不改栈顺序 |
|
||
| `--ci-font-mono` | 数字等宽:`font-variant-numeric: tabular-nums` | 已用(`lesson-ending.css:11`、`LessonReminderContent.vue:226`) | **KEEP**,抽为工具类 `.ci-num` |
|
||
| `--ci-fs-secondary` | `14px` | 由 `--secondary: (fontSize/13)*14`(`CustomIslandRuntime.vue:119`)、`.pill-sub` 0.74×、`small` 12px(`ComponentNode.vue:436`) | **CHANGE**:classisland 固定 4 档由 `fontSize` 按 14/16/18/20 比例派生(见下) |
|
||
| `--ci-fs-body` | `16px` | `--island-font` 基准 13(winisland) | **CHANGE** |
|
||
| `--ci-fs-emphasized` | `18px` | `--emphasized` 默认 20(`ComponentNode.vue:441`) | **CHANGE** |
|
||
| `--ci-fs-large` | `20px` | `--large` 默认 28(`ComponentNode.vue:444`) | **CHANGE** |
|
||
| `--ci-fw-regular/medium/bold` | 400 / 500 / 700 | 600(`.pill-title`、`.row-title`、`.badge`)、700(`.title`)、600(`.solid-btn`) | **CHANGE**:classisland 行文字 500、标题 700;winisland 保留 600/700 |
|
||
| 行高 | body `1.25`、说明 `1.4`(【推断】按 ClassIsland 40px 行高与 16px 字号比例) | `1.15`(`.pill-title/.pill-sub`)、`1.4`(`.content-text`) | **CHANGE**:classisland 1.25;winisland 保留 1.15 |
|
||
| `appearance.fontSize` | 11–20,classisland 下表示 **body 基准**(默认 16);winisland 保持 13(基准字号) | 同一字段,默认 13 | **CHANGE 语义**:按 family 解释;迁移时 winisland 用户不变,classisland 新装默认 16 |
|
||
|
||
**classisland 四档派生公式(必须逐字实现)**:
|
||
|
||
```text
|
||
ratio = appearance.fontSize / 16 // 默认 16 → 1.0
|
||
--ci-fs-secondary = 14 * ratio
|
||
--ci-fs-body = 16 * ratio
|
||
--ci-fs-emphasized = 18 * ratio
|
||
--ci-fs-large = 20 * ratio
|
||
```
|
||
|
||
这与现有 custom 的派生方式同构(`CustomIslandRuntime.vue:119-122` 用 `/13` 基准),只是基准从 13 换成 16。winisland 继续使用 `ISLAND_TYPE_SCALE`(`constants.ts:354-367`):`title 1.08 / body 0.95 / meta 0.78 / badge 0.76 / pillTitle 0.92 / pillSub 0.74`。
|
||
|
||
### 6.4 圆角 token
|
||
|
||
| Token | classisland 目标 | winisland 现状 | custom 现状 | 迁移策略 |
|
||
|---|---|---|---|---|
|
||
| `--ci-radius-island` | `8px`(0–20 可调;`appearance.radius` 复用该范围) | 20px(8–32;squircle 基准半径) | `scheme.radius` 默认 8 | **NEW**;classisland 用标准 `border-radius`,不生成 squircle path |
|
||
| `--ci-radius-row` | `8px`(分体行与整体行同值) | — | — | **NEW** |
|
||
| `--ci-radius-card` | `8px`(详情卡/气泡) | 展开卡 = `min(48×radius/20, w/2, h/2)`(`IslandApp.vue:209-211`) | `min(appearance.radius, w/2, h/2)`(`CustomIslandRuntime.vue:129-133`) | **NEW** |
|
||
| `--ci-radius-control` | `4px`(按钮/列表行/徽标内控件) | `calc(font×0.5)`(`.list-row`、`.row-icon`) | `6px`/`8px` 混用(`custom-island.css:79,126`) | **CHANGE**:classisland 4px;winisland 保留比例派生 |
|
||
| `--ci-radius-pill` | `999px` | 999px(`.pill-type/.badge/.ghost-btn`) | 7px 圆点 | **KEEP** |
|
||
| `--ci-radius-progress` | `0` | 1px | 1px | **CHANGE**(见 §6.2.5) |
|
||
| squircle 参数 | 不使用 | 指数 `4.2`、每角 14 段(`squircle.ts:21,24`) | 同 winisland(共用 `squirclePath`) | **KEEP**(仅 winisland/custom) |
|
||
| 节点自定义圆角 | 默认 `8`(ClassIsland `RadiusX`) | — | `islandNode().appearance.cornerRadius = 16`(`island-layout.ts:211`) | **CHANGE**:新默认 8;旧配置保留 16(无损迁移,不强制改) |
|
||
|
||
### 6.5 几何与间距 token
|
||
|
||
| Token | classisland 目标 | ClassHelper 现状/变量 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| 行高 `--ci-row-h` | `40px`(fixed 气泡同样恒 40,不随内容增高) | `--resident-height: 40`、`min-height: max(40px, font×2.5)`(`custom-runtime.css:77,110,113`);winisland 胶囊高 `appearance.height` 默认 44 | **NEW**;classisland 行高固定 40(不随字号/内容变;正文最多 2 行、超出省略,不得撑高行——见第 7 部分) |
|
||
| 行内边距 `--ci-row-pad-x` | `12px` | `--resident-padding`/`padding 0 12px`(`custom-runtime.css:76`);winisland `padding: 0 calc(font×0.78)` | **NEW**;classisland 固定 12 |
|
||
| 行最小宽 `--ci-row-min-w` | `20px` | 无 | **NEW**(对应 ClassIsland `MinWidth=20`,`repos/ClassIsland/.../FluentTheme/Styles.axaml:109`) |
|
||
| 行间距 `--ci-row-gap` | `5px`(0–150 可调) | `scheme.spacing` 默认 5(`island-layout.ts:251`)、`.layout-rows gap: var(--custom-row-gap)` | **KEEP 值**,classisland 下用 `--ci-row-gap` 驱动 `--custom-row-gap` |
|
||
| 组件间距 `--ci-comp-gap` | 相邻 12px(单侧 6px) | `--row-gap` 默认 12(`scheme`/row `gap`,`island-layout.ts:169,236`;`custom-runtime.css:117`) | **KEEP**;classisland 组件 padding 单侧 = `calc(var(--row-gap)/2)` 即 6px |
|
||
| 窗口/阴影留白 `--ci-shadow-pad` | `10px` | `ISLAND_SHADOW_PAD = 10`(`constants.ts:351`) | **KEEP** |
|
||
| 自动布置容器 `--ci-window-max` | `960×640`(custom 常驻画布) | `windowBox()`:`min(960, area.width) × min(640, max(60, area.height-marginY))`(`main/island.ts:1521-1528`) | **KEEP** |
|
||
| 固定包围盒(fixed) | `宽 = min(工作区宽, 960)`、`高 = 40 + 2×pad`(pad 默认 20,覆盖阴影);行内容在窗口内按停靠对齐并在超出时截断/省略;消息到达不改变窗口尺寸 | `BBOX_SIZE` 取最大形态 + 20(`island.ts:108-112,1543-1547`) | **CHANGE**:classisland 下包围盒 = 行宽 + 2×10;行宽变化时窗口在隐藏帧改尺寸(保留"不可见时改大小"策略);**单条气泡恒 40px、不因内容扩窗**,`maxCardHeight` 只用于 custom 多条列表与强提醒窗口 |
|
||
| 胶囊几何 | 不适用 | `width 268 / height 44`(`constants.ts:266-267`),`ISLAND_SIZE_DELTA`(`:344-348`)、`ISLAND_SLIVER_WIDTH 6`(`:336`)、`sliver height max(12,min(28,h-22))`(`IslandApp.vue:196`) | **KEEP**(仅 winisland) |
|
||
| 课程提醒条 | 高 40、宽 `max-content`、最小 300、最大 800 | 同(`LessonReminderContent.vue:196-208`) | **KEEP** |
|
||
| 课件卡 | `424×132` | `COURSEWARE_ISLAND_SIZE`(`shared/src/courseware.ts:4`)、padding 17/24、radius 24(`CoursewareTransfer.vue:71-72`) | **KEEP**(独立组件,不套 classisland 行) |
|
||
| 编辑器预览行 | 40px(与真岛一致) | `.preview-row min-height:44px`(`island-editor.css:207`)、`padding 12`、背景 `#202024`(`:193-201`) | **CHANGE**:44→40,背景改 `--ci-bg` |
|
||
| 图标/徽标 | 下课前秒数徽标 30×30 圆形 | `.lesson-ending-badge 30×30`(`lesson-ending.css:3-5`) | **KEEP** |
|
||
|
||
### 6.6 阴影 token
|
||
|
||
| Token | classisland 目标 | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-shadow-island` | `0 4px 8px 2px rgba(0,0,0,0.28)`(即 ClassIsland `0 4 8 2 #48000000`) | 无(winisland 用 `filter: drop-shadow(0 2px 3px rgba(0,0,0,.11))`,`classic-island.css:123,148-153`) | **NEW**:classisland 用 `box-shadow` 挂行容器(矩形 + 8px 圆角),**禁止** `filter: drop-shadow`(会闪帧,`classic-island.css:114-120`) |
|
||
| `--ci-shadow-mask` | `0 8px 32px rgba(0,0,0,0.4)`(提醒时) | 无 | **NEW** |
|
||
| `--ci-shadow-popup` | `0 8px 16px rgba(0,0,0,0.45)`(深色) | `--ch-shadow-flyout`(`index.css:53`) | **NEW**(岛内弹层用) |
|
||
| `--ci-shadow-custom` | 内描边 + 小投影 | `inset 0 0 0 1px #ffffff1e, 0 2px 3px #0000001c`(`custom-island.css:12-14`) | **KEEP**(custom 行);classisland family 下改 `inset 0 0 0 1px var(--ci-border), 0 4px 8px 2px rgba(0,0,0,.28)` |
|
||
| 投影常驻原则 | 只改参数、不增删 `filter`/`box-shadow` 声明 | 已实现(`classic-island.css:114-123`) | **KEEP** |
|
||
|
||
### 6.7 透明与不透明度 token
|
||
|
||
| Token | classisland 目标 | ClassHelper 现状/公式 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| `--ci-opacity-bg` | `0.5`(行背景不透明度,与 ClassIsland 默认 `Opacity=0.5` 一致) | `scheme.opacity` 默认 0.5(`island-layout.ts:248`);winisland `appearance.opacity` 默认 1 | **KEEP 语义分开**:背景不透明度(0.5)≠ 窗口不透明度(1)。classisland 下 `--ci-opacity-bg`=scheme.opacity;winisland 无背景层不透明度概念 |
|
||
| 窗口不透明度 | `1`;阅读消息时强制 `1` | `displayOpacity`:`readingMessage ? 1 : appearance.opacity`(`main/island.ts:233-240`) | **KEEP** |
|
||
| 淡化比例 | `0.05` | `CLASSISLAND_ISLAND_METRICS.fadedOpacity = 0.05`(`shared/src/island-layout.ts:35`),用于 `main/island.ts:1441` | **KEEP** |
|
||
| 玻璃/毛玻璃 | classisland 不使用 | `--wn-bg: rgba(32,32,36,0.804)`(`classic-island.css:73`);**不采样桌面模糊**(文档 `winisland-design-tokens.md:23`) | **KEEP**(winisland only) |
|
||
| 消息实底 | `1.0` 不透明 `#202020` | `.message-surface`(见 §6.2.1) | **KEEP** |
|
||
| 遮罩文字层 | accent 上白字 100% | 同 | **KEEP** |
|
||
| 禁用动画 | 不涉及透明度 | `--island-anim: 0|1`(`IslandApp.vue:527`);`animations=false` 时所有 `transition/animation` 应为 0 或直接落位 | **KEEP 扩展**:classisland 新增 `--ci-anim: 0|1`,与 `--island-anim` 二选一由 family 决定 |
|
||
|
||
### 6.8 层级(z-index)token
|
||
|
||
| 层 | classisland 目标 | ClassHelper 现状 | 迁移策略 |
|
||
|---|---|---|---|
|
||
| 岛根层 | 0 | `.island-root`/`.resident-window` | **KEEP** |
|
||
| 形状/背景层 | 0 | `.shape`(`classic-island.css:103-124`)、`svg.shape` | **KEEP** |
|
||
| 内容层 | 1 | `.content`/`.resident-page` | **KEEP** |
|
||
| 遮罩层(消息/提醒) | 2 | `.ci-mask`(`LessonReminderContent.vue:246-253`)、`.tint` | **NEW 统一**:`--ci-z-mask:2` |
|
||
| 遮罩文字层 | 3 | `.ci-mask-title`(`:254-268`) | **NEW**:`--ci-z-mask-text:3` |
|
||
| 弹层/下拉(岛内) | 4 | 无 | **NEW** |
|
||
| 编辑器拖拽幽灵 | `10000` | `.drag-ghost z-index:10000`(`island-editor.css:331-334`)、`.ch-guide 10000` | **KEEP** |
|
||
| 强提醒窗口 | 独立 BrowserWindow,`alwaysOnTop` + `'screen-saver'` | 岛窗口同级别(`island.ts:514`) | **NEW**:效果窗口层级与岛同级或更高,见第 8 部分 |
|
||
| 岛窗口层级 | `screen-saver`(置顶) | `setAlwaysOnTop(alwaysOnTop,'screen-saver')`(`island.ts:514`) | **KEEP** |
|
||
|
||
### 6.9 DPI 与缩放 token
|
||
|
||
| 规则 | 要求 | 证据 |
|
||
|---|---|---|
|
||
| 逻辑单位 | 岛内全部用 CSS px = Electron DIP;**不手动乘 devicePixelRatio** | `winisland-design-tokens.md:53`、`01-objective.md:86` |
|
||
| 阴影留白 | 10 CSS px(窗口比岛大 20) | `ISLAND_SHADOW_PAD = 10`(`constants.ts:351`);`island.ts:1621-1636` |
|
||
| 画布上限 | custom 常驻窗 ≤ 960×640(或工作区高 − marginY) | `island.ts:1521-1528` |
|
||
| 尺寸最小钳制 | 自定义窗口 ≥ 40 宽/24 高;可用画布 ≥ 40 | `island.ts:389-390`、`CustomIslandRuntime.vue:262-263` |
|
||
| 方案缩放 | `scale` 0.75–2.5(custom 常驻);实际 `fit.scale = min(scheme.scale, availW/naturalW, availH/naturalH)` | `island-layout.ts:371-375`;`CustomIslandRuntime.vue:72-80` |
|
||
| 字号范围 | 11–20(`appearance.fontSize`);classisland 派生 4 档后仍受「行高 40」约束,溢出用省略而非撑行 | `constants.ts:298`;§7 组件规格 |
|
||
| 主窗口缩放 | 设置页/编辑器沿用 `--ch-font-scale`(1 / 关怀 1.15),**不影响岛** | `renderer/styles/index.css:103,973-979` |
|
||
| DPI 断言 | 截图/像素验收按 `devicePixelRatio` 换算基准图;125%/150% 下 ±1px 容差 | `01-objective.md:70` |
|
||
| 屏幕定位 | 全部用工作区 `workArea` + 锚点 + `marginX/marginY`;左右边距只在左/右停靠生效,主停靠中线不受影响 | `winisland-design-tokens.md:26`;`island.ts:1615-1648` |
|
||
|
||
### 6.10 深浅色 token
|
||
|
||
`family='classisland'` 必须同时支持深/浅色;`family='winisland'` 恒为深色(不随应用主题)。
|
||
|
||
| Token | 深色(默认) | 浅色 |
|
||
|---|---|---|
|
||
| `--ci-bg` | `#202020` @0.5 | `#F3F3F3` @0.5 |
|
||
| `--ci-border` | `rgba(255,255,255,0.15)` | `rgba(0,0,0,0.08)` |
|
||
| `--ci-text-1..5` | 白 100/85.5/77.3/52.9/36.5% | 黑 100/85.5/77.3/52.9/36.5% |
|
||
| `--ci-fill-1/2/3` | 白 6/10/14% | 黑 4/8/12% |
|
||
| `--ci-shadow-island` | `0 4px 8px 2px rgba(0,0,0,0.28)` | `0 4px 8px 2px rgba(0,0,0,0.18)` |
|
||
| `--ci-progress-track` | 白 15% | 黑 12% |
|
||
| 遮罩/强提醒 | accent 底 + 白字 | 同(accent 底 + 白字) |
|
||
| 切换时机 | 主窗口 `theme` 变化 → 经 preload 下发 `island:appearance`,岛根 `data-color-scheme` 变;颜色过渡 200ms | 同 |
|
||
| 默认 | `colorScheme='system'`(跟随应用;应用默认 `Theme=2` 深色,`repos/ClassIsland/.../Settings.cs:32`) | — |
|
||
|
||
实现纪律:`html.dark` 式全局覆盖**不适用于岛**(岛是独立渲染入口,`island.html` 不继承主窗口 `html.dark`)。必须显式传 `colorScheme` 并在岛根节点切 `data-color-scheme`。切换只改颜色,几何/动效 token 保持不变(`docs/dev/motion.md:125-130`)。
|
||
|
||
### 6.11 两套风格差异总表(默认选择点)
|
||
|
||
| 维度 | classisland(默认) | winisland(兼容) |
|
||
|---|---|---|
|
||
| fixed 形态 | 信息行(多行,行高恒 40,组件横排,底部 3px 进度条);单条气泡也在 40px 行内、正文最多 2 行省略 | 胶囊 268×44 + 展开详情卡(`ISLAND_SIZE_DELTA` 展开 156×186) |
|
||
| 形状 | 标准 `border-radius: 8px` | squircle 连续圆角(指数 4.2 / 14 段),胶囊满圆 h/2,展开 `min(48r/20,…)` |
|
||
| 形变 | CSS transition(行宽/透明度;行高恒 40 不变) | `SpringValue` 帧积分(stiffness 0.10 / damping 0.68 / 换向 35% / 上限 20% / snap 0.05px) |
|
||
| 内容切换 | 行内容淡入淡出 + 气泡两阶段 | 胶囊层 `1-1.5p` 退净、展开层 `p²` 进入 |
|
||
| 字号 | 14/16/18/20 四档(由 fontSize 派生) | `--island-font`×排版系数(title 1.08/body .95/meta .78/…) |
|
||
| 字重 | 500 正文 / 700 标题 | 600 标题 / 700 大标题 |
|
||
| 颜色 | 深/浅色主题 + Fluent/Classic 两套;背景 0.5 透明 | 纯黑/毛玻璃/主题色渐变三套;恒深色 |
|
||
| 状态色 | accent 遮罩 + 状态描边 | 红/橙/accent 内层微染 + 状态描边 |
|
||
| 进度条 | 3px、直角 | 1–2px、1px 圆角 |
|
||
| 主题切换 | Fluent↔Classic / 深↔浅 各 200ms 颜色过渡 | 无主题概念,`style` 切换即时 |
|
||
| 设置项 | `theme`+`colorScheme` 新增;`height/width/radius` 退化为约束 | `height/width/radius/style/idleSliver` 全部保留 |
|
||
| 默认动画 | 见第 10 部分 classisland 列 | 见第 10 部分 winisland 列 |
|
||
|
||
### 6.12 配置迁移与兼容策略(token 相关部分)
|
||
|
||
1. `config.json.island` 增加 `family`、`theme`、`colorScheme`、`winislandStyle`(可选,默认为旧 `style` 的值)。
|
||
2. **老用户升级**:`family` 缺失 → 判为老用户 → `family='winisland'`,`winislandStyle = 旧 style`,`theme/colorScheme` 写默认值但忽略。**新安装**(无 config 或 `island` 缺失)→ `classisland`。
|
||
3. 旧 `style='black'|'glass'|'tinted'` 保留字段(winisland 使用),同时写入 `winislandStyle`;classisland family 下忽略 `style`。
|
||
4. `appearance.accent` 保持用户值;仅当值等于旧默认 `#0a84ff` 且迁移到 classisland 时改 `#00BFFF`,并在迁移日志中记录。
|
||
5. `appearance.fontSize`:winisland 保持 13;迁移到 classisland 时若仍为 13 → 改为 16(避免 4 档过小),并记录。
|
||
6. `scheme.radius`/`node.cornerRadius` 不做强制改写(无损);classisland 渲染时若 `radius` 超过 20 则钳制到 20(`appearance.radius` 范围扩到 0–32 仅为兼容 winisland,classisland 侧钳制)。
|
||
7. 迁移必须幂等:`normalizeIslandAppearance()` 对已有 `family` 的配置不得再次改值。
|
||
|
||
### 6.13 文件落点与实施顺序
|
||
|
||
| 步骤 | 文件 | 动作 |
|
||
|---|---|---|
|
||
| 1 | `packages/shared/src/types.ts` | 扩展 `IslandAppearance`(family/theme/colorScheme/winislandStyle);导出 `ISLAND_FAMILIES`、`ISLAND_THEMES`、`ISLAND_COLOR_SCHEMES` |
|
||
| 2 | `packages/shared/src/constants.ts` | 新增 classisland 默认值常量(`DEFAULT_CLASSISLAND_FONT=16`、`ISLAND_CLASSISLAND_RANGES`);把 `ISLAND_SIZE_DELTA` 标注为 winisland-only |
|
||
| 3 | `packages/shared/src/island-layout.ts` | `CLASSISLAND_ISLAND_METRICS` 增加 `componentGap:12`、`progressHeight:3`、`rowMinWidth:20`、`shadowPad:10`、字号四档保持现有值 |
|
||
| 4 | 新建 `packages/desktop-client/src/island/tokens.css` | 写 `--ci-*` 静态 token(含 `[data-family]`、`[data-theme]`、`[data-color-scheme]` 组合选择器) |
|
||
| 5 | `src/island/main.ts` | 在 `applyMotionTokens()` 前 import `./tokens.css` |
|
||
| 6 | `IslandApp.vue` / `CustomIslandRuntime.vue` | 根元素挂 `data-family/data-theme/data-color-scheme`;`style` 注入补 `--island-row-*`/`--ci-fs-*` 派生值 |
|
||
| 7 | `classic-island.css` / `custom-island.css` / `custom-runtime.css` / `lesson-ending.css` | 把硬编码颜色/尺寸改为引用 `--ci-*`(winisland 分支保留 `--wn-*`/`--custom-*`);`[data-family='classisland']` 下覆盖布局 |
|
||
| 8 | `src/main/island.ts` | 下发 `family/theme/colorScheme`;窗口包围盒按 classisland 行宽计算(保留固定包围盒与"不可见时改尺寸") |
|
||
| 9 | 设置页/编辑器 | `IslandSettingsView.vue` 增加风格/主题/深浅色;`island-editor.css` 预览行 44→40、背景 token 化 |
|
||
| 10 | `docs/dev/winisland-design-tokens.md`、`docs/app/island.md` | 标注双风格与默认值,新增 `docs/dev/island-refactor.md` 的 token 章 |
|
||
|
||
### 6.14 常见错误与自查清单
|
||
|
||
实现者必须主动规避以下 13 类错误(每一条都对应真实会出现的问题):
|
||
|
||
1. **把 `--ci-*` 写进主窗口的 `renderer/styles/index.css`**:岛是独立渲染入口,主窗口的 `:root` 不共享;写错位置会导致岛内变量解析为空、整条 `transition` 简写失效(`docs/dev/motion.md:58-59` 已记录该坑)。
|
||
2. **漏掉 `island.html` 的样式引入顺序**:`tokens.css` 必须在 `classic-island.css`/`custom-*.css` 之前 import,否则后者的 `var()` 在解析时找不到值。
|
||
3. **用 `html.dark` 给岛做浅色**:岛不加载主窗口的 `<html class="dark">`,必须 `data-color-scheme`。
|
||
4. **在 classisland family 里继续用 squircle**:`squirclePath()` 只在 winisland/custom 分支调用;classisland 用 `border-radius: var(--ci-radius-island)`。
|
||
5. **把 `appearance.opacity` 同时当作背景不透明度**:窗口不透明度(0.4–1,阅读消息强制 1)与背景不透明度(默认 0.5)是两个 token;混用会导致"消息看不清"或"背景太实"。
|
||
6. **给行容器加 `backdrop-filter`**:会模糊整块矩形并破坏逐像素透明;毛玻璃只在 winisland 的 `glass` 里以半透明色模拟(`docs/dev/winisland-design-tokens.md:23-24`)。
|
||
7. **用 `filter: drop-shadow` 做 classisland 行投影**:会新建合成表面导致开合闪帧;classisland 用 `box-shadow`(`classic-island.css:114-120`)。
|
||
8. **在 classisland 分支继续用 `--wn-text-*`**:winisland 的 0.92/0.72/0.58 与 classisland 的 100/85.5/77.3/52.9/36.5 不是一套;混用会让同一行文字出现两种灰。
|
||
9. **字号派生写成固定四档**:classisland 四档必须由 `fontSize/16` 比例派生;写死 14/16/18/20 会让设置页字号滑杆失效。
|
||
10. **把圆角钳制只写在 CSS**:`aspect` 变化时 `appearance.radius` 可能大于行高一半,必须在 JS 里也钳制到 `min(radius, w/2, h/2)`(现有 `IslandApp.vue:334-341` 已做,classisland 分支同样要做)。
|
||
11. **迁移时无条件改用户设置**:只有"仍等于旧默认值"的 accent/fontSize 才允许改;用户调过的值必须原样保留(§6.12)。
|
||
12. **忘记给编辑器预览同步 token**:`island-editor.css` 的预览行、背景、圆角必须与真岛一致,否则"预览一个样、上屏另一个样"。
|
||
13. **把 ClassHelper 旧展开卡行为带进 classisland fixed**:fixed 行高恒 40,气泡/遮罩/进度条都在行内完成,正文最多 2 行省略;**不得**写「行高随内容增高」「行高 200ms 过渡」「为气泡扩窗」,`maxCardHeight` 只服务 custom 多条列表与强提醒窗口。
|
||
|
||
自查命令(无头执行,不启动 Electron):
|
||
|
||
```bash
|
||
# 1) 颜色字面量只允许出现在 token/兼容定义处
|
||
grep -rn "#[0-9a-fA-F]\{6\}" packages/desktop-client/src/island
|
||
# 2) 检查 classisland 分支没有误用 winisland 变量
|
||
grep -rn "\-\-wn-" packages/desktop-client/src/island/classic-island.css | head -40
|
||
# 3) token 是否齐全(每个 --ci- 使用处都能在 tokens.css 找到定义)
|
||
grep -rho "\-\-ci-[a-z0-9-]*" packages/desktop-client/src/island | sort -u > /tmp/used.txt
|
||
grep -ho "\-\-ci-[a-z0-9-]*" packages/desktop-client/src/island/tokens.css | sort -u > /tmp/def.txt
|
||
comm -23 /tmp/used.txt /tmp/def.txt
|
||
# 4) 确认 package.json 无新增依赖
|
||
git diff packages/*/package.json package.json
|
||
```
|
||
|
||
### 6.15 验收标准
|
||
|
||
1. **单一来源**:`grep -rn "#[0-9a-fA-F]\{6\}" packages/desktop-client/src/island` 只允许命中 `tokens.css`、`classic-island.css` 的 `--wn-*` 定义块、`custom-*.css` 的兼容块与 SVG 渐变;两个 `.vue` 文件内联样式里不得出现颜色字面量(`--state-color` 计算值除外)。
|
||
2. **双风格隔离**:切到 `winisland` 后,岛仍显示 268×44 胶囊 + squircle + 弹簧开合,像素 diff 与重构前一致(±1px);切到 `classisland` 后无 squircle path、无弹簧形变,行高 40/圆角 8/进度条 3px。
|
||
3. **默认值**:新安装 `family='classisland'`、`theme='fluent'`、`colorScheme='system'`;旧 fixtures 升级后 `family='winisland'` 且 `winislandStyle` 等于旧 `style`。
|
||
4. **字号**:classisland 下把 `fontSize` 从 16 改到 20,四档变为 17.5/20/22.5/25,行高仍 40 且内容按第 7 部分规则省略;winisland 下改字号时全部文本按排版系数等比缩放。
|
||
5. **颜色**:深/浅色各截一张基准图,`--ci-text-1..5`、背景 0.5、描边 1px、进度条轨道/填充与 §6.2/§6.10 表逐项一致(颜色 ±2/255)。
|
||
6. **Fluent/Classic**:切换主题后,行背景色、描边有无、阴影、圆角(Fluent 8 / Classic 0)按 §6.2.6 变化,字号与间距不变。
|
||
7. **不透明度语义分离**:把行背景不透明度调到 0.2、窗口不透明度调到 0.8,两者独立生效;打开一条消息时窗口不透明度强制 1(`displayOpacity`),关闭后回到 0.8。
|
||
8. **层级**:消息遮罩在内容之上、遮罩文字在遮罩之上;编辑器拖拽幽灵 `z-index:10000` 高于一切;岛窗口始终 `screen-saver` 层级。
|
||
9. **DPI**:100%/125%/150%/200% 四档下,行高 40、圆角 8、字号逻辑值不变,无 0.5px 模糊;截图基准按 devicePixelRatio 换算后 ±1px。
|
||
10. **迁移幂等**:对同一份配置连续运行 `normalizeIslandAppearance()` 三次,输出字节级一致;旧字段 `residentEnabled`、`style` 仍在且语义不变。
|
||
11. **兼容别名**:`--ci-lesson-*` 重命名期间旧 `--ci-duration` 等变量仍生效(至少一个版本),组件视觉无变化。
|
||
12. **无新依赖/无新框架**:`package.json` 依赖零新增;token 只用 CSS 自定义属性,不引入 CSS-in-JS 或新 UI 库。
|
||
13. **fixed 气泡恒定 40px**:构造超长正文(>500 字)的消息,行高仍为 40、正文最多 2 行且以省略号收尾,窗口 `bounds` 不因该消息变化;`maxCardHeight` 只在 custom 多条列表/强提醒窗口路径被读取。
|
||
14. **reduce-motion 口径**:系统开启 `prefers-reduced-motion` 时,岛动画仍由 `appearance.animations`/`speed` 决定(默认不覆盖);仅当 P2 选项 `respectReducedMotion=true` 时按 `animations=false` 落位。
|