Files
spark-store/docs/superpowers/specs/2026-04-10-update-center-icons-design.md

215 lines
6.8 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.
# 更新中心列表图标设计
## 背景
当前 Electron 更新中心已经可以展示更新项、来源、迁移标记、进度和日志,但更新列表仍然只有文字信息,没有应用图标。对于 APM 包、传统 deb 包和迁移项,纯文字列表会降低识别效率,尤其在批量更新和搜索场景下不够直观。
仓库现状里已经存在多套可复用的图标来源逻辑:
1. 主商店卡片通过远程商店 URL 拼接 `icon.png`
2. 已安装应用列表支持本地图标和远程 URL 双来源。
3. 旧 Qt 更新器会为 APM 更新项解析 desktop 与 entries/icons并在无本地图标时继续使用其他数据源。
目标是在更新中心列表中加入应用图标,同时保持最小改动、兼容当前后端结构,并遵循“本地解析优先,其次远程 URL最后占位图标”的策略。
## 目标
1. 在更新中心列表中为每个更新项展示应用图标。
2. 图标来源优先级为:本地解析 > 远程 URL > 前端占位图标。
3. 前后端仅增加一个最小公共字段,不引入复杂的图标对象结构。
4. 图标缺失或加载失败时,界面仍然保持稳定、整齐、不闪烁。
## 非目标
1. 不为图标来源新增额外网络探测请求。
2. 不在本次设计中重构应用详情页、已安装列表或主商店卡片的图标逻辑。
3. 不在 UI 中展示“图标来源”说明文字。
## 方案概览
采用“主进程解析来源、渲染层只展示”的方案:
1. 更新中心主进程在加载更新项时解析图标来源,并将结果写入更新项的 `icon` 字段。
2. 渲染层更新列表只消费 `item.icon`,不参与解析来源。
3. 前端负责单次图片加载失败回退到占位图标。
## 数据结构变化
### 主进程
修改:`electron/main/backend/update-center/types.ts`
`UpdateCenterItem` 增加:
```ts
icon?: string;
```
### 渲染层
修改:`src/global/typedefinition.ts`
`UpdateCenterItem` 增加:
```ts
icon?: string;
```
### Service 映射
修改:`electron/main/backend/update-center/service.ts`
在主进程 snapshot -> renderer snapshot 的映射中透传 `icon` 字段。
## 图标来源策略
### 优先级
每个更新项统一按以下顺序取图标:
1. 本地图标路径
2. 远程商店图标 URL
3. 前端占位图标
### 1. 本地图标路径
#### 传统 deb / Spark 更新项
优先复用仓库中已有的 desktop 文件扫描与 `Icon=` 解析思路,来源参考:
- `electron/main/backend/install-manager.ts`
解析策略:
1. 从已安装包对应的 desktop 文件中读取 `Icon=`
2. 如果解析结果为绝对路径,直接返回。
3. 如果解析结果为图标名,则尝试根据系统图标路径补全。
4. 若无法得到有效路径,则继续下一层来源。
#### APM 更新项
优先复用旧 Qt 更新器已存在的 APM 图标解析逻辑,来源参考:
- `spark-update-tool/src/aptssupdater.cpp`
解析策略:
1. 查找 APM 包的 `entries/applications/*.desktop`
2. 从 desktop 的 `Icon=` 字段中解析图标。
3.`Icon=` 为绝对路径,直接返回。
4.`Icon=` 为图标名,则尝试拼接 APM 包内 `entries/icons/...` 路径。
5. 若仍无结果,则继续下一层来源。
### 2. 远程商店图标 URL
如果本地图标解析失败,则为更新项生成远程图标 URL。
实现原则:
1. 不主动探测 URL 是否可用。
2. 仅按现有商店规则拼接 URL并交给浏览器加载。
3. 浏览器加载失败后由前端回退占位图标。
对 Spark/传统 deb
1. 使用当前商店已有的远程图标拼接规则。
2. 若更新项可以推断出对应 category 和 arch则拼接
`${APM_STORE_BASE_URL}/${arch}/${category}/${pkgname}/icon.png`
对 APM
1. 若仓库中已有 APM 对应商店资源约定,则使用同样的 `icon.png` 规则。
2. 若当前数据无法可靠推断 category则允许直接跳过远程 URL进入前端占位图标。
### 3. 占位图标
如果主进程未能提供 `icon`,或者前端加载失败,则使用统一占位图标。
占位规则:
1. 图标尺寸与正常图标一致。
2. 使用仓库现有品牌资源或统一默认应用图标。
3. 不因失败状态改变列表布局高度或间距。
## 模块边界
新增:
- `electron/main/backend/update-center/icons.ts`
职责:
1. `resolveUpdateItemIcon()`
2. `resolveApmIcon()`
3. `resolveDesktopIcon()`
4. `buildRemoteFallbackIconUrl()`
该模块只负责“根据更新项得到一个 `icon?: string`”,不参与更新队列、安装、刷新、忽略等逻辑。
## 数据流
### 主进程加载更新项
1. 查询并合并更新项。
2. 对每个更新项执行图标解析。
3. 将解析到的 `icon` 字段写入 `UpdateCenterItem`
4.`service.ts` 将该字段透传到渲染层 snapshot。
### 渲染层展示
1. `UpdateCenterItem.vue` 读取 `item.icon`
2. 如果 `item.icon` 为本地绝对路径,则转成 `file://` URL。
3. 如果 `item.icon` 为远程 URL则直接作为图片地址使用。
4. 若图片加载失败,则切换为占位图标,并记住失败状态避免重复尝试。
## UI 设计
### 列表项布局
在更新列表中新增一个固定图标位:
1. 位置:复选框后、应用信息前。
2. 尺寸:`40x40`
3. 样式:圆角矩形,视觉与商店应用卡片图标一致。
4. 图标位固定占位,避免有图和无图的项出现布局跳动。
### 失败回退
前端仅做一次失败回退:
1. 优先渲染 `item.icon`
2. 触发 `@error` 后切换为占位图。
3. 记录该项失败状态,避免反复向无效地址重新请求。
## 测试方案
### 主进程测试
新增或扩展测试覆盖:
1. 本地图标优先于远程 URL。
2. APM 更新项可解析包内 desktop/icons。
3. 传统 deb 更新项可解析 desktop `Icon=`
4. 无本地图标时能生成远程 URL 或返回空值。
### 组件测试
扩展 `UpdateCenterItem.vue` 组件测试:
1.`item.icon` 时渲染图片。
2. 图片加载失败时回退到占位图。
3. 图标存在时不影响当前状态标签、迁移标签、进度条显示。
## 风险与约束
1. 更新项当前不一定总能推断出 category因此远程 URL 兜底对部分项可能不可用;这是可接受的,因为前端还有占位图兜底。
2. 本地图标解析涉及多个来源路径,必须限制在读取路径和拼接路径,不做额外昂贵的同步探测。
3. APM 图标路径依赖当前系统安装结构,若个别包结构不标准,应直接退回远程或占位图,而不是阻断更新列表。
## 决策总结
1. 更新中心增加单字段 `icon?: string`,不引入复杂图标对象。
2. 主进程解析图标来源,渲染层只负责展示和失败回退。
3. 图标来源顺序固定为:本地解析 > 远程 URL > 占位图。
4. UI 仅新增稳定图标位,不改变现有更新列表信息层级。