# 架构与数据流

## 总览

```mermaid
flowchart LR
  Log["持久 Session Log"] --> Meter["官方 dsh-token-meter"]
  Meter --> Projection["tokenUsage 会话投影"]
  Projection --> NativeStats["Harness 原生聊天统计条"]
  Projection --> Dashboard["设置 → Token 用量"]
  Projection --> Reader["ctx.skinTokenUsage.read(session)"]

  Package["dsh-skin-token-dashboard Client"] --> ThemeRuntime["ctx.theme / ThemeRuntime"]
  ThemeRuntime --> Presenter["ui-layout 主题呈现"]
  Picker["设置中的皮肤与背景选择器"] --> ThemeRuntime
  Picker --> BackgroundController["背景偏好控制器"]
  Assets["内嵌摄影背景"] --> BackgroundController
  BackgroundController --> Presenter
```

插件遵循三个边界：

- Token 事实由官方 token-meter 拥有；本插件不重新订阅并折叠事件。
- DOM 主题应用由官方 ui-layout 拥有；本插件只注册语义 Token 和发起主题选择。
- 背景控制器只写根元素上的插件私有属性与 CSS 变量，不读取消息或工作区数据。

## Token 统计链路

### 数据来源

`@deepseek-ai/dsh-token-meter` 在标准 base profile 中注册 `tokenUsage` 投影。投影按完整持久日志计算，而不是按当前页面已经加载的消息计算，因此：

- 向上翻页不会改变总量。
- 上下文压缩不会删除已产生的计费用量。
- usage chunk 与最终 assistant message 的同一步用量不会重复计数。
- reasoning token 已包含在 output 中。

### 设置页面

`TokenUsageSource` 在根设置作用域跟随 `ctx.sessions.list.current`，并订阅当前会话的 `tokenUsage` 投影。切换会话或收到新的投影帧时，“设置 → Token 用量”会同步刷新；未选择会话和投影不可用分别使用独立空状态，不用零值掩盖缺失能力。

### 汇总公式

```text
billedInputTokens =
  uncachedInputTokens
  + cacheReadTokens
  + cacheWriteTokens
totalTokens = billedInputTokens + outputTokens

cacheHitRate =
  billedInputTokens == 0
    ? 0
    : cacheReadTokens / billedInputTokens
```

### API 失败语义

`ctx.skinTokenUsage.read(session)` 在正常标准 profile 中返回汇总。以下情况返回 `undefined`：

- 组合未提供 `tokenUsage` 投影。
- 未来版本返回了不兼容结构。
- 任一计数不是非负安全整数。

插件不会用猜测值掩盖契约错误。

## 主题链路

`themes.ts` 中每个皮肤都是官方 `ThemeDefinition`：

- `id`：全局唯一且避开 `light`、`dark`、`system`。
- `colorScheme`：决定基于亮色还是暗色基础调色板。
- `tokens`：只覆盖 `--dsw-*` 语义变量。

Client 入口用 `ctx.theme.register()` 注册定义。选择器调用 `ctx.theme.setTheme()`；`theme/change` 经 `useSyncExternalStore` 触发组件更新。

第三方主题 ID 是进程内扩展。恢复“跟随系统”会回到 Harness 内置、可持久化的 `system` 偏好。

## 背景链路

`assets/backgrounds` 中的图片来自 `F:\tmp`，提交前统一按 2560px 长边和 JPEG 质量 82 优化。生成脚本把图片编码进 `background-assets.generated.ts`，因此 Desktop 的 `file://` 页面和 Web 页面都无需跨盘读取或额外静态文件路由。

`backgrounds.ts` 负责：

- 校验并恢复 `localStorage` 中的背景 ID。
- 通过稳定快照向 React 选择器同步状态。
- 在文档根元素设置图片、焦点位置和皮肤叠色强度。
- 在插件卸载时清理 data 属性与 CSS 变量。

`styles.ts` 使用当前 `--dsw-*` 主题色生成线性与径向渐变。主题的层级背景使用半透明颜色，使照片在保持文字对比度的同时透过侧边栏和内容层；“使用纯色背景”会完全移除图片层。

## 生命周期与清理

所有注册都绑定 Cordis fiber：

- 主题定义卸载时注销。
- 设置 slot 卸载时移除。
- 样式标签卸载时删除。
- 背景控制器卸载时移除监听器和根元素状态。
- Host service 随插件 fiber 销毁。

因此开发期热重载不会不断累积主题、组件、背景状态或样式。
