# Kindle Dashboard 多主题切换方案 ## 1. 背景 当前仓库已经具备一条稳定的单主题链路: - `calendar/` 负责渲染仪表盘网页,并导出 `kindlebg.png` - `dash/` 运行在 Kindle 上,按固定 URL 拉取背景图 - Kindle 本机每分钟只重画时钟区域,不重新联网渲染整页 当前关键约束如下: - Kindle 端默认只认识一个背景地址:`BACKGROUND_URL` - 背景图文件名固定为 `kindlebg.png` - 本机时钟素材与时钟区域参数由 Kindle 本地管理 - KUAL 菜单当前只有启动和调试入口,没有切换主题入口 这意味着“多主题”不能只靠改前端样式,还需要把主题标识、产物路径和 Kindle 侧的选择逻辑一起设计出来。 ## 2. 目标 本方案要解决的问题是: 1. 访问 `calendar` 网站时,可以通过菜单预览不同主题样式 2. 现有日历背景生成流程保持兼容,不因预览主题而被破坏 3. 后续如果需要,`calendar/` 仍能稳定产出多个主题的背景图 4. Kindle 能在不改代码、不重新部署整套程序的前提下切换当前主题 5. 主题切换后,下一次背景刷新即可生效 SH: 主题切换后,即刻拉取一次 6. 后续如果主题不仅改背景,还改表盘素材与时钟区域,也能平滑扩展 非目标: - 不在 Kindle 上重新渲染整张日历页 - 不引入 Kindle 端复杂主题模板引擎 SH: kindle端还是保持,拉背景图片+绘制时钟 - 不要求首次版本就支持任意数量主题的自动发现 SH:主题清单(名称和路径等信息),可以放在calendar项目下,用json文件,比如themes.json,kindle可以一天同步一次该主题,或者主动拉取。 ## 3. 设计原则 ### 3.1 渲染留在 `calendar/` 推荐继续保持现有职责边界: - `calendar/` 负责主题样式、布局、导出 - Kindle 只负责选择当前主题并拉取对应产物 原因: - Kindle 算力和调试体验都不适合承载整页主题渲染 - 当前链路已经验证“远端产图 + 本机分钟级叠时钟”可行 - 主题扩展主要发生在样式层,天然更适合留在前端工程里 SH: 增加主题配置json ### 3.2 Kindle 只保存“当前主题 ID” Kindle 端不保存复杂主题配置,只保存一个很小的状态: - 当前主题名,例如 `paper` 围绕这个主题名决定: - 去哪个 URL 拉取背景图 - 使用哪套本地表盘素材 - 使用哪份时钟区域配置 SH: kindle侧最简化,甚至只需要在固定位置取json来看,calendar支持哪些主题,主题图在哪里取? 时钟大小和和位置等,由calendar维护。kindle所需的信息,可以在主题对应的json中描述,这样kindle不用决定时钟应该画在那里,按json配置即可。 ### 3.3 先做“背景主题切换”,再做“完整主题切换” 建议分两阶段: - 阶段 1:只切换背景主题,保持本地表盘素材不变 - 阶段 2:背景、表盘素材、时钟区域一起切换 这样可以先验证交互链路和配置模型,再扩展到完整主题包。 ### 3.4 预览与导出解耦 主题预览只是 `calendar` 网站访问时的交互能力,不应反向改变现有导出主流程。 建议明确两条链路: - 预览链路:用户访问网站,通过菜单切换 `theme` SH:这个没问题,只要看效果就行了。且效果和kindle上显示一致就行了。 - 导出链路:脚本继续按当前默认行为生成 `calendar/dist/kindlebg.png` SH:主题的路径管理由calendar决定,路径不写死,不通主题不通路径 只有在后续正式接入 Kindle 多主题切换时,再额外增加“按主题导出”的产物目录。 ## 4. 方案总览 ### 4.1 推荐架构 推荐采用“主题目录 + 当前主题状态文件”的方式。 远端发布结构: ```text https://shell.biboer.cn:20001/themes/paper/kindlebg.png https://shell.biboer.cn:20001/themes/paper/dashboard-manifest.json https://shell.biboer.cn:20001/themes/classic/kindlebg.png https://shell.biboer.cn:20001/themes/classic/dashboard-manifest.json ``` Kindle 本地结构: ```text /mnt/us/dashboard/local/theme.env /mnt/us/dashboard/local/themes/paper/ /mnt/us/dashboard/local/themes/classic/ ``` 其中: - `theme.env` 只记录当前主题 ID - 每个主题目录可以逐步承载本地专属素材 ### 4.2 当前主题状态 建议新增本地状态文件: ```sh export THEME_ID=${THEME_ID:-paper} ``` 优先级建议如下: 1. `/mnt/us/dashboard/local/theme.env` 2. `/mnt/us/dashboard/local/env.sh` 中的默认值 3. 代码里的兜底默认值 这样做的好处是: - 切换主题不必改主配置文件 - 主题状态和设备级参数可以分开管理 - 后续可以由 KUAL 菜单脚本单独改写 `theme.env` ## 5. `calendar/` 侧改造建议 ### 5.1 新增 `theme` URL 参数 当前已有 `mode` 参数: - `/?mode=full` - `/?mode=background` - `/?mode=clock-face` 建议扩展为: - `/?mode=full&theme=paper` - `/?mode=background&theme=paper` - `/?mode=background&theme=classic` 约定: - `mode` 决定页面用途 - `theme` 决定视觉主题 ### 5.2 网站访问时提供主题预览菜单 主题预览菜单只服务于网站访问场景,不直接参与背景导出。 建议行为: 1. 用户访问 `/?mode=full` 时,可以在页面菜单中切换主题 2. 菜单切换只更新当前页面的 `theme` 参数 3. 可以把当前主题同步到 URL,便于分享和复现 4. 可以选择是否额外写入 `localStorage`,用于下次访问时记住上次预览主题 菜单本身建议只出现在预览页面,不出现在背景导出页面里。 也就是说: - `/?mode=full&theme=paper` 可以显示主题切换菜单 - `/?mode=background&theme=paper` 只负责导出背景,不显示菜单 ### 5.3 主题定义集中管理 建议在 `calendar/src/` 下引入统一主题描述,例如: ```ts type ThemeId = 'paper' | 'classic' | 'minimal'; type DashboardTheme = { id: ThemeId; label: string; clockRegion: { x: number; y: number; width: number; height: number; }; managedOnKindle: boolean; }; ``` 主题配置至少要能描述: - 主题 ID - 主题显示名 - 时钟区域位置与尺寸 - 是否依赖 Kindle 本地表盘素材 ### 5.4 保持现有导出流程兼容 当前导出主流程建议保持不变: ```text calendar/dist/kindlebg.png calendar/dist/dashboard-manifest.json calendar/dist/clock-region.json ``` 默认规则: - 如果导出脚本未显式传入 `theme`,就使用默认主题,例如 `paper` - 继续保留现有 `kindlebg.png` 单产物出口 - 现有部署和 Kindle 拉图流程无需立刻修改 这样可以保证: - 先落地网站主题预览能力 - 不打断当前背景导出与发布链路 - 后续再增量接入主题化产物 ### 5.5 多主题导出作为附加能力 SH:主题清单由calendar供给,kindle只要依据themes.json获取现有什么主题。kindle切换主题时拉一次该主题对应的背景和主题级json。 当需要支持 Kindle 端切换主题时,再额外增加按主题导出的能力,例如: ```text calendar/dist/themes/paper/kindlebg.png calendar/dist/themes/paper/dashboard-manifest.json calendar/dist/themes/paper/clock-region.json calendar/dist/themes/classic/kindlebg.png calendar/dist/themes/classic/dashboard-manifest.json calendar/dist/themes/classic/clock-region.json ``` 这里的重点是“新增”,不是“替换”: - 原有 `calendar/dist/kindlebg.png` 继续保留 - 主题目录产物只用于多主题发布和 Kindle 切换 - 现有单主题导出脚本不需要被强制改成批量导出 建议后续新增独立命令,例如: - `export:background` 继续生成默认背景 - `export:themes` 额外生成多个主题目录 ### 5.6 `dashboard-manifest.json` 增加主题字段 建议每个主题目录下的 manifest 包含: ```json { "theme": { "id": "paper", "label": "Paper" }, "background": { "path": "kindlebg.png", "url": "https://shell.biboer.cn:20001/themes/paper/kindlebg.png", "refreshIntervalMinutes": 120 }, "clockRegion": { "x": 262, "y": 55, "width": 220, "height": 220 }, "clockFace": { "path": "assets/clock-face.png", "managedOnKindle": true } } ``` 核心变化只有两点: - 增加 `theme.id` - 背景 URL 改为带主题目录 ## 6. Kindle 侧改造建议 ### 6.1 拉取路径由主题拼装 不建议继续把 `BACKGROUND_URL` 写死为单一地址。 建议改成两段式配置: ```sh export BACKGROUND_BASE_URL=${BACKGROUND_BASE_URL:-"https://shell.biboer.cn:20001/themes"} export THEME_ID=${THEME_ID:-paper} ``` 实际拉取地址在运行时拼成: ```text $BACKGROUND_BASE_URL/$THEME_ID/kindlebg.png ``` 这样后续新增主题时,不需要再改 Kindle 端脚本逻辑。 ### 6.2 增加切换脚本 建议新增脚本: ```text /mnt/us/dashboard/switch-theme.sh ``` 行为建议: 1. 校验主题 ID 是否在允许列表内 2. 写入 `/mnt/us/dashboard/local/theme.env` 3. 删除或重置背景更新时间戳 4. 可选:立即重启 `dash.sh`,或等待下一次刷新周期生效 其中第 3 步很重要,因为如果不清空背景更新时间,当前缓存可能会延迟两个小时才被新主题替换。 ### 6.3 KUAL 菜单作为切换入口 推荐在 KUAL 菜单里增加主题项,例如: ```text Kindle Dashboard Theme: Paper Theme: Classic Theme: Minimal Dashboard Debug On Dashboard Debug Off ``` 这样用户在 Kindle 上就能直接切换当前主题,不依赖 SSH。 如果后续主题数量增加到 5 个以上,再考虑改成: - 一个“循环切换下一个主题”的脚本 - 或由脚本生成菜单 JSON ### 6.4 完整主题包的本地目录 当主题不只是改背景,而是还改表盘样式时,建议 Kindle 本地按主题存放资源: ```text /mnt/us/dashboard/local/themes/paper/clock-region.env /mnt/us/dashboard/local/themes/paper/assets/clock-face.png /mnt/us/dashboard/local/themes/paper/assets/hour-hand/000.png /mnt/us/dashboard/local/themes/paper/assets/minute-hand/00.png /mnt/us/dashboard/local/themes/classic/clock-region.env /mnt/us/dashboard/local/themes/classic/assets/clock-face.png ``` 切换主题时同时切换: - 背景拉取路径 - 本地时钟素材根目录 - 时钟区域参数 ## 7. 两种落地级别 ### 7.1 级别 A:只切背景 适用场景: - 多个主题只改卡片风格、字体、配色、排版 - 所有主题共用同一套 Kindle 本地表盘素材 优点: - 改动最小 - 风险最低 - 最快落地 限制: - 不支持不同主题使用不同表盘位置或不同指针样式 SH:现在kindle的时钟绘制不依赖表盘和指针素材吧? ### 7.2 级别 B:完整主题切换 适用场景: - 不同主题拥有不同表盘造型 - 时钟区域位置和尺寸可能变化 - 指针、刻度、中心点等素材也需要按主题变化 优点: - 主题能力完整 - 页面视觉更统一 成本: - Kindle 本地资源管理会更复杂 - 切换时需要同步处理更多状态 ## 8. 推荐实施顺序 建议分四步推进。 ### 第一步:引入主题 ID,但只做单主题兼容 目标: - 网站访问时支持 `theme` - 增加主题预览菜单 - 默认主题仍然只有 `paper` 结果: - 用户可以在网站上切换预览主题 - 现有背景导出行为保持不变 ### 第二步:产出双主题背景 目标: - 增加可选的按主题导出能力 - `calendar/` 能同时生成两个主题目录 - 发布层能稳定提供两组 URL 结果: - 远端资源准备完成 - 现有默认导出链路仍可继续使用 ### 第三步:Kindle 支持背景主题切换 目标: - 增加 `theme.env` - 增加 `switch-theme.sh` - 增加 KUAL 菜单入口 结果: - 用户可以在 Kindle 上切换背景主题 ### 第四步:扩展到完整主题包 目标: - 将时钟素材和时钟区域配置也按主题管理 结果: - 主题系统从“背景切换”升级为“完整主题切换” ## 9. 风险与约束 ### 9.1 主题切换后的缓存失效 如果只改了主题 ID,但没有重置背景更新时间戳,Kindle 可能继续显示旧主题缓存。 因此切换脚本必须显式清理以下状态之一: - `/mnt/us/dashboard/local/state/background-updated-at` - 或缓存背景文件本身 ### 9.2 主题数量增长后的菜单维护 若主题数较多,手写 KUAL 菜单会越来越笨重。 到那时建议: - 用一个脚本根据主题清单生成菜单 - 或保留“下一个主题”这种单按钮切换方式 ### 9.3 远端发布必须保持目录稳定 一旦 Kindle 端按 `/themes//kindlebg.png` 约定拉图,发布层就必须保持目录和文件名稳定,否则会出现主题切换后 404。 ## 10. 推荐结论 推荐采用下面这条路线: 1. 保持“`calendar/` 渲染,Kindle 只拉图和切换主题 ID”的职责边界 2. 先实现网站访问时的主题预览菜单,并保持现有导出流程不变 3. 再增量接入“背景主题切换” 4. 等确认交互和运维链路稳定后,再升级到“完整主题包” 这个路线最符合当前仓库的实际状态: - 复用现有 `mode` 和导出链路 - 不会打断当前 `kindlebg.png` 生成与发布流程 - 对 Kindle 主循环改动小 - 不会把主题复杂度压到 Kindle 设备上 - 后续也能自然扩展到本地表盘素材切换 ## 11. 待确认问题 进入实现前,建议先明确这几个评审点: 1. 网站预览菜单放在哪个区域最合适,是否只在 `mode=full` 显示 SH:这个没有问题,放在顶部的随意位置,可选即可,不影响截图效果。 2. 第一阶段是否只做 2 个主题,例如 `paper` 和 `classic` SH:保持当前主题,命名为default,新增这两个。 3. 第一阶段主题是否允许改变时钟区域位置 SH:这个位置由calendar中时钟的占位位置决定,按主题时钟位置,不是随意改变。 4. KUAL 菜单是否接受“每个主题一个菜单项”的交互形式 SH:统一主题配置,点进去从themes.json获取主题清单,点具体主题,切换主题 5. 远端发布目录是否可以稳定提供 `/themes//kindlebg.png` 如果以上五点都确认,本方案可以直接拆成小步实现。