Files
ClassIsland Prompt Kit 22c1784bab feat: ClassIsland / ClassHelper 灵动岛提示词套件
- 重构版主提示词(14 parts,面向 ClassHelper 灵动岛重构)
- ClassIsland 全量规格提示词(20 parts,含分卷与逐章版本)
- 投喂指南、分卷、逐节文件
- 调研记录、一致性基线、生成与质检脚本
2026-11-12 21:59:58 +08:00

43 KiB
Raw Permalink Blame History

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 四档派生公式(必须逐字实现):

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 或直接落位

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):

# 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 落位。