Files
kindle-calendar/dash/docs/theme-switching-plan.zh.md
2026-03-16 09:24:24 +08:00

13 KiB
Raw Blame History

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 端复杂主题模板引擎
  • 不要求首次版本就支持任意数量主题的自动发现

3. 设计原则

3.1 渲染留在 calendar/

推荐继续保持现有职责边界:

  • calendar/ 负责主题样式、布局、导出
  • Kindle 只负责选择当前主题并拉取对应产物

原因:

  • Kindle 算力和调试体验都不适合承载整页主题渲染
  • 当前链路已经验证“远端产图 + 本机分钟级叠时钟”可行
  • 主题扩展主要发生在样式层,天然更适合留在前端工程里

3.2 Kindle 只保存“当前主题 ID”

Kindle 端不保存复杂主题配置,只保存一个很小的状态:

  • 当前主题名,例如 paper

围绕这个主题名决定:

  • 去哪个 URL 拉取背景图
  • 使用哪套本地表盘素材
  • 使用哪份时钟区域配置

3.3 先做“背景主题切换”,再做“完整主题切换”

建议分两阶段:

  • 阶段 1只切换背景主题保持本地表盘素材不变
  • 阶段 2背景、表盘素材、时钟区域一起切换

这样可以先验证交互链路和配置模型,再扩展到完整主题包。

3.4 预览与导出解耦

主题预览只是 calendar 网站访问时的交互能力,不应反向改变现有导出主流程。

建议明确两条链路:

  • 预览链路:用户访问网站,通过菜单切换 theme
  • 导出链路:脚本继续按当前默认行为生成 calendar/dist/kindlebg.png

只有在后续正式接入 Kindle 多主题切换时,再额外增加“按主题导出”的产物目录。

4. 方案总览

4.1 推荐架构

推荐采用“主题目录 + 当前主题状态文件”的方式。

远端发布结构:

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 本地结构:

/mnt/us/dashboard/local/theme.env
/mnt/us/dashboard/local/themes/paper/
/mnt/us/dashboard/local/themes/classic/

其中:

  • theme.env 只记录当前主题 ID
  • 每个主题目录可以逐步承载本地专属素材

4.2 当前主题状态

建议新增本地状态文件:

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/ 下引入统一主题描述,例如:

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 保持现有导出流程兼容

当前导出主流程建议保持不变:

calendar/dist/kindlebg.png
calendar/dist/dashboard-manifest.json
calendar/dist/clock-region.json

默认规则:

  • 如果导出脚本未显式传入 theme,就使用默认主题,例如 paper
  • 继续保留现有 kindlebg.png 单产物出口
  • 现有部署和 Kindle 拉图流程无需立刻修改

这样可以保证:

  • 先落地网站主题预览能力
  • 不打断当前背景导出与发布链路
  • 后续再增量接入主题化产物

5.5 多主题导出作为附加能力

当需要支持 Kindle 端切换主题时,再额外增加按主题导出的能力,例如:

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 包含:

{
  "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 写死为单一地址。

建议改成两段式配置:

export BACKGROUND_BASE_URL=${BACKGROUND_BASE_URL:-"https://shell.biboer.cn:20001/themes"}
export THEME_ID=${THEME_ID:-paper}

实际拉取地址在运行时拼成:

$BACKGROUND_BASE_URL/$THEME_ID/kindlebg.png

这样后续新增主题时,不需要再改 Kindle 端脚本逻辑。

6.2 增加切换脚本

建议新增脚本:

/mnt/us/dashboard/switch-theme.sh <theme-id>

行为建议:

  1. 校验主题 ID 是否在允许列表内
  2. 写入 /mnt/us/dashboard/local/theme.env
  3. 删除或重置背景更新时间戳
  4. 可选:立即重启 dash.sh,或等待下一次刷新周期生效

其中第 3 步很重要,因为如果不清空背景更新时间,当前缓存可能会延迟两个小时才被新主题替换。

6.3 KUAL 菜单作为切换入口

推荐在 KUAL 菜单里增加主题项,例如:

Kindle Dashboard
Theme: Paper
Theme: Classic
Theme: Minimal
Dashboard Debug On
Dashboard Debug Off

这样用户在 Kindle 上就能直接切换当前主题,不依赖 SSH。

如果后续主题数量增加到 5 个以上,再考虑改成:

  • 一个“循环切换下一个主题”的脚本
  • 或由脚本生成菜单 JSON

6.4 完整主题包的本地目录

当主题不只是改背景,而是还改表盘样式时,建议 Kindle 本地按主题存放资源:

/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 本地表盘素材

优点:

  • 改动最小
  • 风险最低
  • 最快落地

限制:

  • 不支持不同主题使用不同表盘位置或不同指针样式

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/<theme-id>/kindlebg.png 约定拉图,发布层就必须保持目录和文件名稳定,否则会出现主题切换后 404。

10. 推荐结论

推荐采用下面这条路线:

  1. 保持“calendar/ 渲染Kindle 只拉图和切换主题 ID”的职责边界
  2. 先实现网站访问时的主题预览菜单,并保持现有导出流程不变
  3. 再增量接入“背景主题切换”
  4. 等确认交互和运维链路稳定后,再升级到“完整主题包”

这个路线最符合当前仓库的实际状态:

  • 复用现有 mode 和导出链路
  • 不会打断当前 kindlebg.png 生成与发布流程
  • 对 Kindle 主循环改动小
  • 不会把主题复杂度压到 Kindle 设备上
  • 后续也能自然扩展到本地表盘素材切换

11. 待确认问题

进入实现前,建议先明确这几个评审点:

  1. 网站预览菜单放在哪个区域最合适,是否只在 mode=full 显示
  2. 第一阶段是否只做 2 个主题,例如 paperclassic
  3. 第一阶段主题是否允许改变时钟区域位置
  4. KUAL 菜单是否接受“每个主题一个菜单项”的交互形式
  5. 远端发布目录是否可以稳定提供 /themes/<theme-id>/kindlebg.png

如果以上五点都确认,本方案可以直接拆成小步实现。