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

34 KiB
Raw Blame History

2. ClassHelper 灵动岛现状基线(重构的"现状地图")

2.0 阅读与使用方式

  1. 你在重构前必须先读懂本部分。本部分不是评价现状好坏,而是逐文件、逐行号给出"现在到底是什么样"。重构的每一处改动都必须能在本部分找到对应的现状依据。
  2. 本部分所有行号以 repos/ClassHelper-master/ 为工作区相对根,写作 packages/desktop-client/...:行号;与 ClassIsland 对照时写 repos/ClassIsland/...:行号。行号是行区间起点,读源码时按主题名检索同样有效。
  3. 现状基线里出现的所有数值(尺寸、时长、阈值、限制)都是重构时必须先保持、再按新规格替换的对象;不得因为"要改成 ClassIsland 风格"就整体删除,至少要先在迁移层保留等价语义。

2.1 进程、窗口与装配全景

灵动岛是独立渲染进程 + 独立透明 BrowserWindow,与主窗口并存,不占任务栏:

  • 主进程在 app.whenReady() 里 registerIpcHandlers()、registerIslandIpc(),随后调用 island.init(devUrl ? ${devUrl}/island.html : null);生产环境加载 dist/renderer/island.html。依据:packages/desktop-client/src/main/index.ts:188-221、packages/desktop-client/src/main/island.ts:613-617。
  • 主进程把三件事接给灵动岛:island.setOpenAppHandler(打开应用)、island.setEditLayoutHandler(打开编辑器)、island.setMarkReadHandler/setMarkAllReadHandler(把"标为已读"转给主窗口渲染进程)。依据:src/main/index.ts:221-245。
  • 托盘菜单提供「编辑灵动岛布局」,调用 island.editLayout();退出清理时 island.destroy()。依据:src/main/tray.ts:42、src/main/index.ts:304-307。
  • 灵动岛窗口创建参数(重构后仍必须保留这些窗口级语义):frame:false、transparent:true、resizable:false、movable:false、minimizable:false、maximizable:false、fullscreenable:false、skipTaskbar:true、hasShadow:false、focusable:true、alwaysOnTop:true、type:'toolbar',webPreferences 带独立 preload(../preload/island.js)、contextIsolation:true、nodeIntegration:false、sandbox:true、backgroundThrottling:false。依据:src/main/island.ts:480-512。
  • 创建后设置 setAlwaysOnTop(appearance.alwaysOnTop,'screen-saver')、setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true})、拒绝所有新开窗口与非法导航、默认 setIgnoreMouseEvents(true,{forward:true})。依据:src/main/island.ts:514-527;导航白名单实现见 :32-50。
  • 严禁调用 setBackgroundMaterial()。现状代码在 src/main/island.ts:1726-1748 有整段踩坑说明:一旦调用(包括 'none')窗口就不再逐像素透明,卡片外圈会永久留白底;glass 风格因此改用 CSS 半透明色 rgba(32,32,36,0.804) 降级(src/island/classic-island.css:71-77)。

2.2 主进程岛控制器分区地图(src/main/island.ts,共 2445 行)

这是本轮重构最需要拆分的文件。按现状行区间整理如下(ChatGPT 必须按此表定位旧实现):

行区间 现状职责 关键符号
1-130 导入、开发/生产 URL 白名单、窗口尺寸基线与形态增量、收回动画时长、各状态超时表 prodIslandUrl、isAllowedIslandNavigation、BASE、ISLAND_SIZE_DELTA、CLOSE_ANIMATION_MS=360、TIMEOUTS
134-166 对外载荷/动作类型 IslandPushContext、IslandClassStatePayload、IslandActionName
168-460 IslandController 字段与 getter:课程提醒、计时器、窗口、状态、外观、触摸/常驻/编辑、命中、预览、心跳、恢复 lessonCue、collapseTimer、closeTimer、fadeToken、touchMode、residentShapeTimer、previewId、recoverAt
468-634 init():恢复外观、算尺寸、建窗、导航限制、失焦/无响应/心跳监听、加载页面、首帧同步 init、render-process-gone、unresponsive、console-message
636-680 窗口/课表/时间点读取、状态组装、课件状态、就绪判断 getWindow、getState、setCoursewareTransfer
684-767 消息语义:立即展开判定、可点开判定、双播合并、待处理排序、列表布局 isImmediate、isOpenable、mergeNotification、pendingNotifications、listLayout
770-915 视口高度、收到通知的分流、上课/下课状态迁移、下课补弹 refreshViewport、pushNotification、setClassState、flushQueueAfterClass
929-1077 用户动作状态机:展开/收起/展开更多/知道了/整批/标已读/打开应用 handleAction
1079-1214 隐藏、收回动画、立即隐藏、销毁、移除通知 hide、beginClose、finishClose、hideImmediately、destroy、removeNotification
1216-1322 关闭/激活/收起定时/清理/预览收尾 dismissActive、clearPendingForClose、activate、scheduleCollapse、clearTimers
1324-1410 锚点与目标屏、换屏重排、状态落地与推送 resolveAnchor、targetDisplay、syncAnchorDisplay、setState、emit
1412-1512 可见性/焦点/透明度总协调(含上课隐藏守卫) syncWindow
1514-1648 固定包围盒/触摸贴合/重排/窗口矩形 windowBox、syncTouchLayout、relayout、applyWindowLayout、windowBounds
1650-1724 焦点策略、淡入淡出、取消几何动画 applyFocusable、fadeIn、fadeOut、cancelBoundsAnimation
1749-1783 外观热更新 setAppearance
1785-1910 命中开关、岛体矩形、常驻控件/触摸、鼠标避让淡化、触摸模式 setInteractive、setHitRect、updateResidentPointer、setTouchMode
1912-2047 冒烟注入点、坐标换算、兜底岛体矩形、岛体命中判定 setTestInputPassthrough、setHitTestCursor、cursorLocal、expectedHitRect、isCursorOnIsland、syncHitFromCursor
2049-2157 60ms 命中轮询、渲染心跳、窗口自愈(10 分钟 3 次) startHitPoll、checkRendererAlive、recoverWindow
2159-2184 冒烟读取器(交互/窗口/外观) getInteractive、getWindowBounds、getAppearance、emitAppearance
2186-2264 尺寸换算、细缝尺寸、外观夹紧 recomputeSizes、getSliverSize、normalizeAppearance
2266-2334 留白/失焦宽限/预览时长/恢复限流/接口类型 ISLAND_MARGIN=8、BLUR_GRACE_MS=500、BLUR_GRACE_TOUCH_MS=1200、PREVIEW_DURATION_MS=30000、MIN_CARD_HEIGHT=120、RENDERER_ALIVE_TIMEOUT_MS=20000
2335-2445 单例导出与 IPC 注册 island、registerIslandIpc、ISLAND_SIZES/URGENT/CALL/TIMEOUTS 再导出
  1. 重构时不得在这个文件里继续堆逻辑。你必须按 04 部分的模块边界把它拆成 island/ 目录(controller/window/hit/touch/messages/lessons/weather/courseware/ipc),并保留 src/main/island.ts 作为再导出门面(export { island, registerIslandIpc } from './island/index.js'),以免一次性改动 main/index.ts、tray.ts、courseware.ts、smoke.ts 的调用点。

2.3 窗口几何:固定包围盒 + 岛内形变

现状采用「窗口一次建大、岛在画布内形变」的模型(照搬 WinIsland):

  • 形态基线:胶囊 216×34,展开 = 胶囊 + ISLAND_SIZE_DELTA.expanded,紧急/叫人各再加大。依据:src/main/island.ts:72-96、packages/shared/src/constants.ts:344-351。
  • recomputeSizes(appearance) 按用户可调宽高联动所有形态,并计算 BBOX_SIZE = 最大形态 + 2×ISLAND_SHADOW_PAD(留白 10)。依据:src/main/island.ts:2187-2215、packages/shared/src/constants.ts:351。
  • 普通(鼠标)模式窗口尺寸恒为包围盒;触摸模式窗口贴合当前形态;列表形态允许窗口先长高。依据:src/main/island.ts:1521-1553。
  • 窗口位置由锚点推出:居中用 center - round(width/2),左/右贴边留 pad,顶/底同理;自定义模式再夹到工作区内。依据:src/main/island.ts:1615-1648。
  • 渲染进程侧同样维护一套弹簧目标尺寸:胶囊取外观宽高,展开/紧急/叫人取增量,细缝取 6px 宽。依据:src/island/IslandApp.vue:188-204、packages/shared/src/constants.ts:336。
  • 连续圆角:squirclePath(w,h,r) 用超椭圆指数 4.2、每角 14 段;胶囊圆角取 min(r, h/2),展开取 min(48*r/20, w/2, h/2)。依据:src/island/squircle.ts:21-24,44-81、src/island/IslandApp.vue:206-212,343-344。
  • 弹簧:SpringValue 默认 stiffness=0.10、damping=0.68、snapThreshold=0.05px、换向保留 35% 动量、单帧位移 ≤ 剩余距离 20%、MAX_DT=6 帧;渲染进程按 delta * appearance.speed 缩放帧时间。依据:src/island/spring.ts:26-114、src/island/IslandApp.vue:287-332。

2.4 命中、穿透、触摸与"点不动"防护

现状把"命中"当成一级问题处理,重构必须原样保留这些防护:

  1. 窗口默认整块穿透(setIgnoreMouseEvents(true,{forward:true})),由渲染进程 mousemove 命中后上报 island:set-interactive(true);关闭命中一律由主进程按真实光标位置决定(双向下发会让窗口在穿透/接收之间抖动,表现为"点了没反应")。依据:src/main/island.ts:2412-2418、src/preload/island.ts 的 setInteractive、src/island/IslandApp.vue:456-487。
  2. 主进程 60ms 轮询读系统光标,并与渲染进程上报的岛体矩形比对来校正命中(Windows 下 mousemove 转发不可靠)。依据:src/main/island.ts:2049-2078,2026-2047。
  3. 岛体矩形由渲染进程限流上报(最小间隔 50ms,尾随保证最后一次送达)。依据:src/island/IslandApp.vue:412-454。
  4. 触摸屏:渲染进程按 navigator.maxTouchPoints > 0 或首次 touchstart 上报 setTouchMode(true);主进程据此让窗口贴合岛体并始终接收输入,且命中判定的入参被强制为 true。依据:src/island/IslandApp.vue:538-549、src/main/island.ts:245-257,1795-1822,1891-1902。
  5. Windows 专属 setShape 用于触摸模式下把窗口裁成岛体+8px 留白;非 Windows 平台没有该能力,重构必须按 04 的降级方案处理(整窗穿透 + 岛可见区命中),不得依赖 setShape 才能点。依据:src/main/island.ts:278-316。
  6. 失焦收起:展开态获取焦点,点到别处 blur → 回缩胶囊;但必须有两层防护——指针仍在岛上则不收起;刚展开的宽限期内不收起(鼠标 500ms、触摸 1200ms)。依据:src/main/island.ts:557-578,2287-2297,2020-2023。
  7. 窗口「始终可激活」(focusable:true),收起时用 blur() 而不是关掉可激活性——Windows 下 WS_EX_NOACTIVATE 会丢 mousedown,胶囊就点不动。依据:src/main/island.ts:1655-1672。

2.5 消息状态机(fixed 经典模式)

  1. 状态载体 IslandState:mode: 'hidden'|'pill'|'expanded'、active、queued、inClass、currentPeriodEnd、reason、listExpanded、maxCardHeight、lessonReminder、coursewareTransfer、updatedAt。依据:packages/shared/src/types.ts:1198-1230。
  2. 分流规则(必须作为重构的行为基线):紧急(priority==='URGENT')立即展开、无视上课;上课期间普通通知只入队并隐藏;上课期间普通叫人保持胶囊可点、不自动展开;上课期间用户主动点开的通知允许显示(explicitShowId);预览模式(设置页"预览效果")直接展开、失焦不收起、30 秒自动收尾。依据:src/main/island.ts:684-702,794-867,2338-2380。
  3. 双链路合并:同一 id 经 notification:new 与 call:new 双播时必须合并——类型取"更特殊"(call 优先)、优先级取更高,避免叫人被降级成普通通知。依据:src/main/island.ts:704-724。
  4. 排序:按优先级权重(PRIORITY_RANK:LOW 0 / NORMAL 1 / HIGH 2 / URGENT 3)降序,同级按 createdAt 倒序。依据:packages/shared/src/constants.ts:183-209、src/main/island.ts:726-749。
  5. 超时表(毫秒):胶囊 15000、下课后展开 14000、下课后胶囊 25000、展开 20000、紧急 45000、叫人 90000;关闭动画 360;收回时先切胶囊、待动画后再淡出隐藏。依据:src/main/island.ts:121-132,1106-1128。
  6. 收起的判定:只要还有未处理消息(当前或队列)就保持胶囊;队列清空/用户点了知道了/标为已读/进入上课才彻底隐藏。依据:src/main/island.ts:1266-1296。
  7. 多条消息:展开态且待处理 ≥2 时进入列表形态;默认显示 3 条 + 「展开更多(还有 N 条)」;放不下时提示"更多请前往应用内操作";卡片高度由共享 islandListLayout() 计算,主进程与渲染进程必须算同一个数。依据:src/main/island.ts:755-765,770-785、packages/shared/src/constants.ts:379-471、src/island/ClassicNotifications.vue:34-58。
  8. 动作:expand、collapse、expand-list、dismiss、dismiss-all、mark-read、mark-all-read、open-app;其中 mark-read/mark-all-read 通过主进程回调转给主窗口通知中心;作业卡 id 是本地合成 homework-<id>,整批已读时必须排除。依据:src/main/island.ts:150-166,1044-1070、src/renderer/island/bridge.ts:191-211。
  9. 预览/冒烟注入口:preview、setTestInputPassthrough、setHitTestCursor、setBlurCollapseEnabled、setTouchMode、getWindowBounds、getHitRect、getInteractive、isRecovering 等 重构后必须继续存在(大量冒烟断言依赖),不得改名或删除。依据:src/main/island.ts:187-190,416,927-1930,2154-2183。

2.6 经典模式渲染(src/island/IslandApp.vue,683 行)

  1. 卡片结构:.island-root > .island-card > svg.shape + span.content >(tint + pill-layer + ClassicNotifications + LessonReminderContent)。依据:src/island/IslandApp.vue:573-681。
  2. 内容交叉淡入:胶囊层 1 - progress*1.5 退净(进度 2/3 处),展开层 progress² 进入;progress 由高度差推导。依据::304-309,346-351。
  3. 胶囊标题按类型:叫人→"老师叫你"、作业→"新作业"、紧急→"紧急通知"、普通→"新消息";多条时汇总为「新消息:叫人/作业/通知(共 N 条)」。依据::115-146。
  4. 停靠:data-anchor 决定内容贴边(左贴左/右贴右/居中);居中用 left:50% + translateX(-50%),左右/上下停靠留 ISLAND_SHADOW_PAD。依据::353-383、src/island/classic-island.css:207-233。
  5. 形状与投影:路径 fill: var(--wn-bg) + 1px 描边;投影始终声明、只在展开态改参数(避免加/去 filter 造成开合闪一帧)。依据:src/island/classic-island.css:102-153。
  6. 状态色:紧急红 #ff453a、叫人橙 #ff9f0a、作业用 accent;只做内层径向微染 + 描边,不外溢。依据:src/island/IslandApp.vue:151-157、classic-island.css:155-185。
  7. 内容层裁剪:.content{clip-path:var(--card-path)};圆角路径由 SVG path() 注入 CSS 变量 --card-path。依据:classic-island.css:127-133、IslandApp.vue:363-371。
  8. 轮廓保护:island-root.embedded 用于设置页内嵌预览(去掉绝对定位)。依据:classic-island.css:41-47、IslandApp.vue:578-581。

2.7 自定义布局(custom)运行时与数据

  1. 数据根:config.json.islandLayout,默认 layout:'fixed';residentEnabled 仅作兼容字段,custom 即常驻。依据:packages/desktop-client/src/main/config.ts:26-36,198-233、packages/shared/src/island-layout.ts:131-139,384-403。
  2. 组件方案 IslandLayoutSettings:layout、residentEnabled、pointerFadeEnabled/Reversed、lessonReminders、activeSchemeId、schemes[]。依据:packages/shared/src/island-layout.ts:131-150。
  3. 方案 IslandLayoutScheme:id/name/rows/expandedRows/scale/color/foreground/opacity/radius/separated/spacing;rows 为常态行、expandedRows 为"无消息时点击展开"的内容。依据:island-layout.ts:109-130。
  4. 行 IslandLayoutRow:id/name/main/allowNotifications/gap/nodes;main 行唯一(normalize 会强制只留一个)。依据:island-layout.ts:109-116,334-356。
  5. 节点 IslandComponentNode:id/kind/settings/appearance/advanced/children;kind 14 类(date/clock/weather/countdown/schedule/text/separator/slide/rolling/group/stack/notification/homework/call)。依据:island-layout.ts:3-18,77-108。
  6. settings:文本/模板、12 小时制、秒、倒计时目标与目标日、地点/经纬度、显示模式(当前/下一节/今日)、显示数量、显示起止时间、显示进度、排列方向、间距、轮播间隔、滚动速度。依据:island-layout.ts:80-99。
  7. appearance:资源覆盖开关、四档字号、前景/背景/透明度/圆角覆盖、整体透明度;advanced:隐藏规则(in-class/no-message/no-class)、对齐、固定宽/最大宽。依据:island-layout.ts:61-76,100-107。
  8. 归一化边界(重构后仍必须保留的硬边界):最多 12 行、200 节点、6 层、方案 ≤20;颜色必须 #rrggbb;非法 kind 丢弃;重复 id 重命名;main 行唯一。依据:island-layout.ts:256-403(尤其 :269,337,358)。
  9. 自定义运行时:先测量自然内容(compact/details/lesson 三份),再按工作区与方案 scale 适配;两个页面层(常态/详情)交叉淡入,第三层承载课程提醒;铃铛显示未处理数(>99 显示 99+)。依据:src/island/CustomIslandRuntime.vue:58-124,238-323,338-527。
  10. 鼠标避让淡化:常驻模式按指针是否在岛内切换透明度到 CLASSISLAND_ISLAND_METRICS.fadedOpacity=0.05(可反转);触摸/编辑/课程提醒期间暂停。依据:src/main/island.ts:1851-1877、island-layout.ts:35。
  11. 编辑布局时窗口 interview 行为:editingLayout 下不参与命中轮询、不自动收起;island:edit-layout 与托盘入口都走 editLayout()。依据:src/main/island.ts:350-356,1430-1433。
  12. 常驻窗口几何:窗口固定为 min(960, 工作区宽) × min(640, 工作区高 - marginY),内容在画布内弹簧形变;常驻开合期间窗口不做逐帧 setBounds。依据:src/main/island.ts:1522-1528、CustomIslandRuntime.vue:214-216。

2.8 课程提醒与课程组件

  1. LessonReminderTracker 只用服务端解析后的 ScheduleDto 计算五类提醒:准备(默认提前 60s)、上课、课间、下课前倒数(默认 60s)、课中进度;每种提醒的进入/退出时间由 maskUntil/endsAt 表达。依据:src/main/lesson-reminder.ts:14-110、packages/shared/src/island-layout.ts:140-158。
  2. 主进程每秒刷新一次提醒;自定义布局下 in-class 提醒不接管岛体(课程组件持续显示进度),ending 提醒只作为课程名右侧秒数徽标。依据:src/main/island.ts:171-213、docs/app/island.md:65-70。
  3. 提醒条 UI:高 40、圆角 8、底色 #20202080;5 个斜切区域(30° 偏移)覆盖式打开/关闭,区段 360ms、错峰延迟 [0,90,120,90,0](开)/反向(关);开 1-(1-p)^4、关 p²;文字延迟 260ms、scale 1.1→1.0、750ms;进度条 2px、transform 50ms linear。依据:src/island/LessonReminderContent.vue:59-123,133-137,195-317。
  4. 经典模式临近下课用课程名右侧蓝色圆形徽标显示剩余秒数(剩余 1 秒显示 -1),不显示"即将下课"遮罩。依据:docs/app/island.md:68-70、CustomIslandRuntime.vue:30-33、ComponentNode.vue:259-266。
  5. 关闭"即将下课倒数"后不得显示徽标;阈值沿用本机提醒设置。依据:docs/app/island.md:69、lesson-reminder.ts:54-60。

2.9 天气现状

  1. 天气只在主进程抓取(固定服务、固定参数、不接受渲染层传 URL):api.open-meteo.com/v1/forecast,取 temperature_2m/weather_code/wind_speed_10m,8 秒超时、redirect:'error'、响应 ≤64KB;成功缓存 15 分钟,失败保留旧值并标 stale/退避 60 秒,缓存最多 32 个坐标。依据:src/main/island-weather.ts:1-73。
  2. 渲染层 WeatherBrief.vue 每分钟刷新一次;天气码映射文案(晴/少云/多云/阴/雾/毛毛雨/雨/雪/雷雨);断网或陈旧时显示"· 缓存";未配置坐标提示"请设置天气地点"。依据:src/island/WeatherBrief.vue:12-90。
  3. 冒烟模式禁止访问外部天气(ELECTRON_SMOKE_TEST==='1' 且使用真实 fetch 时直接抛错)。依据:island-weather.ts:33-34。

2.10 课件传输覆盖

  1. 主进程持有 coursewareTransfer,coursewareShowing 时优先覆盖岛内容;syncWindow 分支强制显示、取消淡入、不接收鼠标;formSize() 返回课件固定尺寸。依据:src/main/island.ts:661-676,1420-1429,1956、src/main/courseware.ts:36。
  2. 课件卡尺寸 424×132,内边距 17px 24px、圆角 24、底色 rgba(20,20,24,.97)、进度条 5px、字号 13/16/12/11;显示文件名、类型、教师、已收/总 MB、进度%、错误行。依据:packages/shared/src/courseware.ts:4、src/island/CoursewareTransfer.vue:12-132。

2.11 渲染入口与组件清单(现状文件级地图)

文件 行数 现状职责
src/island/main.ts 30 岛渲染入口:引入 tokens.css、applyMotionTokens()、挂载 LayoutIslandApp
src/island/LayoutIslandApp.vue 76 读取 layout/state/appearance/schedules/timePoints,选择经典/自定义/课件三种渲染
src/island/IslandApp.vue 683 经典岛:胶囊+展开卡+形变+命中+课程提醒
src/island/CustomIslandRuntime.vue 528 自定义常驻:测量适配+三页交叉淡入+铃铛+消息面板
src/island/ComponentNode.vue 456 14 类组件的渲染与课程表折叠规则;编辑态拖拽手柄
src/island/ComponentIcon.vue 36 组件图标(编辑库与铃铛)
src/island/ClassicNotifications.vue 234 单条详情卡/多条列表卡/按钮行(打开应用/标为已读/知道了)
src/island/LessonReminderContent.vue 318 课程提醒条 + 5 区斜切遮罩 + 进度条 + 徽标
src/island/CoursewareTransfer.vue 132 课件传输覆盖卡
src/island/WeatherBrief.vue 91 天气简报组件
src/island/spring.ts 115 SpringValue 帧积分弹簧
src/island/squircle.ts 81 连续圆角路径
src/island/classic-island.css 757 经典岛全部样式(胶囊/展开/列表/按钮/状态色)
src/island/custom-island.css 205 自定义行/组件/铃铛基础样式
src/island/custom-runtime.css 255 常驻三页/停靠/分隔/消息宿主样式
src/island/lesson-ending.css 16 课程名右侧秒数徽标
src/renderer/island/bridge.ts 473 主窗口→主进程的岛数据桥:课表/上课状态/通知/作业/叫人/补弹/已读
src/preload/island.ts 123 岛窗口 contextBridge 白名单
src/preload/index.ts 101 主窗口 contextBridge(含岛推送 API)
src/types/desktop.d.ts 294 桥接契约与 config 形状

2.12 主窗口 → 主进程桥接(src/renderer/island/bridge.ts)

  1. 桥接职责:从 IndexedDB 缓存/服务端取当天课表与完整时间点,每秒复算"是否上课",变化时通过 window.desktop.islandSetClassState 下发给主进程;主进程据此决定通知立即弹/暂存/下课补弹。依据:src/renderer/island/bridge.ts:21-56,148-172,416-435。
  2. 课表来源 /schedules/active-week,同时构建 IslandTimePointDto[](含课间/分割线/隐藏标记),课程名优先取日课表条目、无条目再回退时间点默认课程;离线缓存只有条目时退化为"只有上课时间点"。依据:bridge.ts:57-96,107-136。
  3. 通知投递:普通通知、新作业(带"本机录入免打扰"指纹去重)、叫人(带"请尽快前往"副文案)分别打包成 IslandNotification,随 inClass/currentPeriodEnd 上下文推给主进程。依据:bridge.ts:174-289。
  4. 离线补弹:恢复联网时取最近 5 条离线未读补投,推完清离线起点;冒烟模式跳过。依据:bridge.ts:291-364。
  5. 已读回流:主进程收到岛的"标为已读"后转发给主窗口,桥接订阅并调用通知 store 标记已读(离线先本地改)。依据:bridge.ts:366-405、src/main/index.ts:237-245。
  6. 生命周期:登录后启动、退出登录/切班时停止;停止时必须清定时器、解订阅、清本地课表并主动同步 inClass:false。依据:bridge.ts:437-473、src/renderer/App.vue:64-93、src/renderer/layouts/ClientLayout.vue:396-405。

2.13 配置与数据契约现状

  1. config.json 的岛相关字段:islandLayout(完整方案)与 island(外观);两者都有逐字段归一化与默认值。依据:src/main/config.ts:26-84,124-163,198-233,334-346,435-523;配置文件路径 userData/config.json 见 :165-167。
  2. 写入语义:内存缓存 + 120ms 合并写 + 退出前 flushConfigSync;不允许每次读取都同步读盘。依据:config.ts:235-346。
  3. 外观契约 IslandAppearance:height/width/radius/opacity/accent/fontSize/animations/speed/position/alwaysOnTop/style/idleSliver/marginX/marginY/followCursorDisplay;范围 36–72 / 220–420 / 8–32 / 0.4–1 / 11–20 / 0.5–2 / 0–200 / 0–160。依据:packages/shared/src/types.ts:1145-1195、constants.ts:265-302。
  4. 经典外观默认:width 268、height 44、radius 20、fontSize 13、opacity 1、accent #0a84ff、style 'black'、position 'top-center'、marginX/Y 8。依据:constants.ts:265-290。
  5. 语义色与排版系数:ISLAND_TYPE_SCALE(title 1.08/body 0.95/meta 0.78/badge 0.76/pillTitle 0.92/pillSub 0.74)必须与 classic-island.css 顶部注释一致;所有字号由 --island-font 推导。依据:constants.ts:354-364、classic-island.css:1-7。
  6. CLASSISLAND_ISLAND_METRICS(重构后的目标几何基线,已存在于 shared):rowHeight 40、水平内边距 12、圆角 8(最大 20)、背景不透明度 0.5、缩放 1(0.75–2.5)、字号 14/16/18/20、行间距 5(最大 150)、避让淡出 0.05。依据:packages/shared/src/island-layout.ts:19-36。

2.14 IPC 现状(重构必须保持的契约)

  1. 主窗口桥接:getConfig/saveConfig/clearConfig/appInfo/openExternal;岛:islandPush/islandRemoveNotification/islandSetClassState/islandGetState/islandSetAppearance/islandGetAppearance/onIslandMarkRead/onIslandMarkAllRead;另有 islandEditLayout/onIslandEditorOpen。依据:src/preload/index.ts:25-99。
  2. 岛窗口桥接:getLayout/saveLayout/onLayout、getEditing/setEditing/onEditing、getVisible/onVisibility、reportSize/reportResidentControl/noteResidentTouch/onViewport、getSchedules/onSchedules、getTimePoints/onTimePoints、getState/onState/sendAction、getAppearance/onAppearance/setInteractive/setHitRect/setTouchMode/alive。依据:src/preload/island.ts:14-121。
  3. 主进程侧注册:island:get-schedules、island:get-time-points、island:get-layout、island:save-layout、island:edit-layout、island:get-editing、island:get-visible、island:resident-control、island:resident-touch、island:set-editing、island:content-size、island:remove-notification、island:push、island:class-state、island:alive、island:get-state、island:set-appearance、island:get-appearance、island:set-interactive、island:set-hit-rect、island:set-touch-mode、island:action。依据:src/main/island.ts:2338-2438。
  4. 安全规则:所有 invoke 必须校验发送方是岛窗口(event.sender === island.getWindow()?.webContents);渲染进程不能读写配置(get-layout 是唯一读取口,save-layout 由主进程做归一化)。依据:src/main/island.ts:2339-2356,2345-2350。

2.15 编辑器与设置页现状

  1. 设置页「灵动岛」:布局二选一(fixed/custom)、自定义方案入口、鼠标避让两个开关、外观滑块(宽/高/圆角/透明度/字号/速度)、主题色、视觉风格、位置、左右/上下边距、跟随鼠标屏幕、动画开关、始终置顶、空闲细缝、"测试灵动岛"(本地推一条 30 秒预览通知)。依据:src/renderer/views/settings/IslandSettingsView.vue:26-160,162-392。
  2. 设置页改动即生效:外观走 window.desktop.islandSetAppearance 实时下发,保存才落盘;自定义模式下圆角滑块会同步写当前方案圆角。依据:IslandSettingsView.vue:112-133。
  3. 编辑器是主窗口的设置子路由 /settings/island/editor(不创建额外桌面窗口):上方渲染组件标签/预览、右上角方案菜单(新建/复制/删除/重置/JSON 导入导出)、下方组件库(11 类)+ 检查器;分别编辑常态 rows 与无消息展开 expandedRows。依据:src/renderer/views/settings/IslandEditorView.vue:25-70,290-337,541+、docs/app/island.md:36-48、src/renderer/router/index.ts:69-74。
  4. 编辑器保存:深拷贝 + normalizeIslandLayouts + 串行保存队列(saveQueue)+ 修订号(只有最后一次成功才算 saved)+ 路由离开前等待保存;上限:组件总数 200、方案 20。依据:IslandEditorView.vue:81-105,338-347,379-383。
  5. 拖动/放置:指针事件实现拖拽(beginDrag/startDrag/dragTarget/moveDrag/dropPreview),编辑标签节点用 IslandEditorNode.vue,支持 before/after/inside 三种落点。依据:IslandEditorView.vue:443-539、src/renderer/components/IslandEditorNode.vue:1-124。
  6. 组件设置抽屉:内容/外观/高级三页;内容按 kind 提供字段,外观含独立字号/前景/背景/透明度/圆角/整体透明度,高级含对齐/固定宽/最大宽/隐藏条件;底部支持包裹到容器、复制、删除。依据:src/renderer/components/IslandComponentSettings.vue:26-307。
  7. 编辑预览:编辑器内嵌预览区(preview-canvas/island-preview)用岛内组件渲染,缩放滑块控制;不创建第二个桌面窗口,也不自动切到自定义布局(点"应用"才切)。依据:IslandEditorView.vue:290-299、docs/app/island.md:47、src/renderer/styles/island-editor.css:125-249。

2.16 既有硬约束(重构不得破坏,逐条对应 AGENTS.md §4)

  1. 保留经典岛连续圆角、投影常驻、触摸输入、失焦宽限、固定窗口几何护栏。依据:repos/ClassHelper-master/AGENTS.md §4「灵动岛」。
  2. 禁止 setBackgroundMaterial();毛玻璃只能是 CSS 半透明。依据:src/main/island.ts:1726-1748。
  3. 消息按 id 去重;通知/叫人双播不得重复;本机录入作业不上岛(指纹 + id 双判定)。依据:src/main/island.ts:713-724、bridge.ts:191-267。
  4. 编辑预览不创建额外桌面窗口;打开编辑页不自动切布局,应用后才启用自定义方案。依据:docs/app/island.md:47。
  5. 默认验证不创建可见岛/托盘;只有显式 --visual 才做桌面像素/动画验收;冒烟模式跳过外部天气与离线补弹。依据:docs/dev/testing.md:9,29-32、island-weather.ts:33-34、bridge.ts:326-331。
  6. 经典岛与自定义岛共用一个原生窗口,切布局不得新开第二个岛。依据:docs/app/island.md:8-10、src/main/island.ts:468-634(单 this.win)。
  7. 左右边距只在左/右停靠生效,居中锚点永远是屏幕中线;marginX 对居中无效必须在 UI 说明。依据:docs/app/island.md:54、IslandSettingsView.vue:88-94。
  8. 所有验证不得打扰真实桌面(不弹窗、不发系统通知、不挪真实鼠标);冒烟注入点只能改"逻辑光标"。依据:src/main/island.ts:1910-1930。

2.17 现状问题清单(重构动因,逐条对应 04 的改造项)

  1. src/main/island.ts 单文件 2445 行,窗口/命中/消息/课程/课件/外观全部耦合,难以按 ClassIsland 规格替换视觉与交互。
  2. 几何与尺寸常量分散在主进程 BASE/SIZES、shared ISLAND_SIZE_DELTA、渲染进程 targetSize() 三处,主/渲染各算一次,改动时容易漂移。
  3. 经典与自定义是两套独立运行时,但共享消息面板、课程提醒、命中上报;重构需要抽出公共"岛壳 + 消息面板 + 课程条"层,避免两套 UI 各改一遍。
  4. 数据里没有版本号:config.islandLayout 无 version,新增 ClassIsland 行/组件/外观字段时无法做确定性迁移与回滚。
  5. 现状行/组件缺少 ClassIsland 的行级外观(背景色/不透明度/圆角/字号覆盖)、行级隐藏规则、行级进度条等字段;只有节点级覆盖。
  6. 提醒只有"条状提醒 + 徽标",没有 ClassIsland 的两阶段气泡与强提醒 Topmost 特效;需要在视觉层新增但不得改变现有消息队列语义。
  7. 主进程状态机把"提醒优先级、上课守卫、预览、课件"混在一个 syncWindow 里,重构需要拆成"状态投影 + 可见性决策 + 几何/命中"三层。
  8. 编辑器保存/校验逻辑在 Vue 侧,主进程只做 normalizeIslandLayouts;重构应把 schema 校验/迁移收口到 shared,编辑器和主进程共用。

2.18 验收标准(第 2 部分)

  1. 你能在不运行程序的情况下,仅凭本部分定位:岛窗口创建位置、消息分流规则、命中轮询、触摸模式、失焦宽限、收回动画、课程提醒、课件覆盖、设置页与编辑器的入口文件与行号。
  2. 你能列出重构前必须保留的 8 条硬约束与 8 条现状问题,并说明每条对应到 04 的哪个改造项。
  3. 你能复述现状数据流:服务端 ScheduleDto → bridge.ts → islandSetClassState → 主进程状态机 → island:state → 渲染进程;以及通知/作业/叫人三条投递链路的差异。
  4. 你能指出任何一处"主进程与渲染进程各算一次"的几何/布局逻辑(窗口包围盒、列表高度、命中矩形、细缝尺寸、字号推导),并确认 04 已把它们收口到单一事实来源。
  5. 本部分引用到的每个数值都能追到 packages/...:行号 或 packages/shared/...:行号;没有"凭印象"的数值。