From e84e43544a78d248111203787142aa5d901213a3 Mon Sep 17 00:00:00 2001 From: LoveChengke Date: Sat, 17 Oct 2026 15:56:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(island):=20=E5=90=88=E5=B9=B6=E5=B8=B8?= =?UTF-8?q?=E9=A9=BB=E4=B8=8E=E8=87=AA=E5=AE=9A=E4=B9=89=E5=B2=9B=E5=B9=B6?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=BB=8F=E5=85=B8=E9=80=9A=E7=9F=A5=E5=92=8C?= =?UTF-8?q?=E5=8D=B3=E6=97=B6=E8=AF=BE=E8=A1=A8=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev/20261017-implementation.md | 28 + .../src/island/ClassicNotifications.vue | 234 ++++ .../src/island/ComponentNode.vue | 2 +- .../src/island/CustomIsland.vue | 94 +- ...dentIsland.vue => CustomIslandRuntime.vue} | 193 ++-- .../desktop-client/src/island/IslandApp.vue | 996 +----------------- .../src/island/LayoutIslandApp.vue | 13 +- .../src/island/classic-island.css | 743 +++++++++++++ ...resident-island.css => custom-runtime.css} | 143 +-- packages/desktop-client/src/main/island.ts | 52 +- .../desktop-client/src/main/smoke-layout.ts | 71 +- .../desktop-client/src/main/smoke-quick.ts | 1 + .../desktop-client/src/main/smoke-resident.ts | 71 +- packages/desktop-client/src/preload/island.ts | 9 +- .../src/renderer/cache/self-test.ts | 112 ++ .../src/renderer/island/bridge.ts | 50 +- .../views/settings/IslandSettingsView.vue | 192 ++-- .../desktop-client/src/types/desktop.d.ts | 1 + packages/shared/src/island-layout.ts | 5 +- 19 files changed, 1565 insertions(+), 1445 deletions(-) create mode 100644 packages/desktop-client/src/island/ClassicNotifications.vue rename packages/desktop-client/src/island/{ResidentIsland.vue => CustomIslandRuntime.vue} (67%) create mode 100644 packages/desktop-client/src/island/classic-island.css rename packages/desktop-client/src/island/{resident-island.css => custom-runtime.css} (68%) diff --git a/docs/dev/20261017-implementation.md b/docs/dev/20261017-implementation.md index c9f1102..5f33517 100644 --- a/docs/dev/20261017-implementation.md +++ b/docs/dev/20261017-implementation.md @@ -130,3 +130,31 @@ Windows 触摸模式使用 Electron setShape:动画开始放开绘制区域, - 新旧课表界面与岛桥接共用的缓存仍保存整周数据,岛组件只接收当天投影,避免当天响应覆盖整周离线缓存。 - 最终复跑曾遇到本机冷启动超时:桌面第一次界面无响应,原样重跑在已通过的检查之后被旧 45 秒总限时中止;临时后端也超过了原 10 秒就绪等待。外层预算分别调整为 90 秒与 60 秒,心跳 30 秒等单项业务断言不变。 调整后最终复跑通过后端 243/243 与桌面 24/24(含二次启动),桌面整套实际耗时 20.6 秒。 + +## 常驻岛并入自定义灵动岛(最新行为) + +后续产品要求统一两种自定义显示方式,本节取代上文关于可选常驻、铃铛优先与独立通知卡的历史行为。 +`layout` 仍为 fixed/custom;custom 直接显示常态组件,`residentEnabled` 只保留为旧配置兼容字段,归一化为 custom 时开启。 +设置页不再提供独立的常驻开关。编辑器保留常态 rows 与无通知展开 expandedRows,两套内容随本机方案保存。 + +自定义运行时统一为 `CustomIslandRuntime.vue`,不再将旧卡片嵌入组件行。 +经典岛与自定义岛共同使用 `ClassicNotifications.vue` 和 `classic-island.css`,通知类型、排序、徽标、正文、按钮与已读动作只有一份实现。 +纯黑、玻璃、主题色、透明度、字号、圆角、动画开关与速度均来自同一份 island 外观设置;方案保留组件缩放、行距和布局。 +自定义组件行、消息正文与通知操作区居中对齐。无通知展开时显示 expandedRows,有通知时显示共用经典通知内容。 + +自定义模式不再提前截获所有消息并只亮铃铛:与经典岛走相同的 pushNotification/setClassState 状态机。 +紧急通知和紧急叫人立即展开,课中普通通知排队、下课后展开;主动点击铃铛仍可查看待处理消息。 +收起和处理完通知后恢复自定义常态内容。铃铛固定占位,收到消息不横移。空白桌面输入穿透与触摸区域裁剪继续保留。 +外壳使用经典岛同源的 SpringValue 与 squirclePath;宽高和圆角每帧同时推进,保留零过冲钳制、动画速度与后台吸附兜底。 +原生画布固定,隐藏渲染器不运行旧形变;统一心跳不随布局切换重复注册。 + +课表更新链路做了以下修正: + +- 课表变更事件立即取 active-week,学期接口在旁路校正,避免慢学期请求串行挡住新课表。 +- 网络结果先同步到岛,再异步完成缓存写入;请求序号在请求开始前分配,迟到响应和已切换账号的结果不会覆盖当前内容。 +- 实时连接建立及重连时立即刷新;周期补偿从五分钟缩至三十秒,课程边界每秒复算。跨午夜即使上课状态未改变,也同步新一天的课程。 +- 主进程保留最新日课表快照;窗口启动、重建时补发,渲染器通过受限 getSchedules IPC 读回,登出时清空。 +- 自适应同时接收原生窗口尺寸通知,防止隐藏窗口的浏览器 resize 延迟而沿用旧宽度;订阅在组件退出时释放。 + +验证使用隔离配置、本地消息和替换的本地 API,覆盖慢学期接口、变更事件、重连刷新、迟到请求隔离、课表 IPC 即时更新及窗口重新加载恢复。 +桌面视觉回归覆盖经典岛原有状态,以及自定义岛单通知、多通知、常态、空闲展开、窄屏、长文本、触摸和固定原生窗口动画。 diff --git a/packages/desktop-client/src/island/ClassicNotifications.vue b/packages/desktop-client/src/island/ClassicNotifications.vue new file mode 100644 index 0000000..8c7ef62 --- /dev/null +++ b/packages/desktop-client/src/island/ClassicNotifications.vue @@ -0,0 +1,234 @@ + + + diff --git a/packages/desktop-client/src/island/ComponentNode.vue b/packages/desktop-client/src/island/ComponentNode.vue index a76652b..5ab4305 100644 --- a/packages/desktop-client/src/island/ComponentNode.vue +++ b/packages/desktop-client/src/island/ComponentNode.vue @@ -110,7 +110,7 @@ const style = computed(() => { : ({ stretch: 'stretch', start: 'flex-start', center: 'center', end: 'flex-end' } as const)[ n.advanced.align ], - textAlign: n.advanced.align === 'center' ? 'center' : 'start', + textAlign: n.advanced.align === 'start' ? 'start' : n.advanced.align === 'end' ? 'end' : 'center', color: override && a.customForegroundEnabled ? a.foreground : undefined, background: override && a.customBackgroundEnabled ? color : undefined, borderRadius: override && a.customCornerRadiusEnabled ? `${a.cornerRadius}px` : undefined, diff --git a/packages/desktop-client/src/island/CustomIsland.vue b/packages/desktop-client/src/island/CustomIsland.vue index e9500b8..d965f95 100644 --- a/packages/desktop-client/src/island/CustomIsland.vue +++ b/packages/desktop-client/src/island/CustomIsland.vue @@ -7,6 +7,7 @@ import { islandNode, normalizeIslandLayouts, type IslandLayoutSettings, + type IslandAppearance, type IslandComponentKind, type IslandComponentNode, type IslandState, @@ -19,6 +20,7 @@ const props = defineProps<{ settings: IslandLayoutSettings; state: IslandState; editing: boolean; + appearance: IslandAppearance; schedules: ScheduleDto[]; }>(); const emit = defineEmits<{ @@ -84,17 +86,6 @@ function collapseResident() { function onPointerDown(event: PointerEvent) { if (event.pointerType === 'touch' || event.pointerType === 'pen') window.island?.noteResidentTouch(); } -function restoreClassIslandAppearance() { - Object.assign(scheme.value, { - scale: CI.scale, - color: '#000000', - opacity: CI.opacity, - radius: CI.radius, - spacing: CI.rowSpacing, - }); - for (const row of [...scheme.value.rows, ...scheme.value.expandedRows]) row.gap = 12; - save(); -} const selectedId = ref(''), selectedKind = ref('clock'); const panel = ref<'component' | 'row' | 'appearance'>('appearance'); @@ -158,14 +149,15 @@ const shellStyle = computed(() => ({ minHeight: !props.editing && viewport.value.height ? `${viewport.value.height}px` : undefined, maxWidth: `calc(100vw - ${2 * ISLAND_SHADOW_PAD}px)`, maxHeight: `calc(100vh - ${2 * ISLAND_SHADOW_PAD}px)`, - color: scheme.value.foreground, - fontSize: '16px', + color: '#ffffffeb', + fontSize: `${props.appearance.fontSize}px`, '--custom-color': - scheme.value.color + - Math.round(scheme.value.opacity * 255) - .toString(16) - .padStart(2, '0'), - '--custom-radius': `${scheme.value.radius * (draft.value.residentEnabled ? scheme.value.scale : 1)}px`, + props.appearance.style === 'glass' + ? '#202024cd' + : props.appearance.style === 'tinted' + ? '#0b0b0f' + : '#000000', + '--custom-radius': `${props.appearance.radius}px`, '--custom-row-gap': `${scheme.value.spacing}px`, ...(draft.value.residentEnabled ? { @@ -364,6 +356,7 @@ function panMove(e: PointerEvent) { pan.value = { x: panning.originX + e.clientX - panning.x, y: panning.originY + e.clientY - panning.y }; move(e); } +let stopViewport: (() => void) | undefined; let observer: ResizeObserver | undefined, measureTimer: ReturnType | undefined, clock: ReturnType; @@ -399,12 +392,13 @@ onMounted(() => { clock = setInterval(() => (now.value = Date.now()), 1000); observer = new ResizeObserver(measure); if (inner.value) observer.observe(inner.value); - window.island?.onViewport((v) => (viewport.value = v)); + stopViewport = window.island?.onViewport((v) => (viewport.value = v)); measure(); }); onUnmounted(() => { clearInterval(clock); clearTimeout(measureTimer); + stopViewport?.(); observer?.disconnect(); }); @@ -495,15 +489,14 @@ onUnmounted(() => {
-