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

515 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.jsonkindle可以一天同步一次该主题或者主动拉取。
## 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 <theme-id>
```
行为建议:
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/<theme-id>/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/<theme-id>/kindlebg.png`
如果以上五点都确认,本方案可以直接拆成小步实现。