# Desktop-App Renderer 设计规范

> 本文件是 **agent 改动 `src/renderer/**` 任何 UI 时的必读硬性规范**，不是参考手册。
> 单一事实源：`src/renderer/styles.css` 的 CSS 变量与 `@theme inline` 块。
> 任何与本文件冲突的现存代码 = 待清理的债，**不要**当作范例复制。

---

## 0. 触发场景

修改/新增以下任何内容时，先把本文件读完：

- `src/renderer/**/*.tsx`、`src/renderer/**/*.css`
- `shared/components/ui/**`（shadcn 基础件）
- 任何包含 `className=` 的 React 文件
- 任何使用 `motion/react` 的动画

---

## 1. 颜色：MUST 用 token，禁止硬编

### 1.1 唯一合法的颜色来源

只能使用通过 `@theme inline` 暴露的 token 工具类，及其 `/N` alpha 变体：

| 用途       | Token 类                                                         |
| ---------- | ---------------------------------------------------------------- |
| 页面底     | `bg-background` / `text-foreground`                              |
| 卡片面     | `bg-card` / `text-card-foreground`                               |
| 浮层面     | `bg-popover` / `text-popover-foreground`                         |
| 主色       | `bg-primary` / `text-primary` / `text-primary-foreground`        |
| 次要面     | `bg-secondary` / `bg-muted` / `bg-accent`                        |
| 次要文字   | `text-muted-foreground`                                          |
| 边/输入/环 | `border-border` / `bg-input` / `ring-ring` / `outline-ring`      |
| 危险       | `bg-destructive` / `text-destructive` / `text-destructive-foreground` |

Alpha 写法只允许 `/5 /10 /15 /20 /25 /30 /40 /50 /60 /70 /80`，例：`bg-primary/10`、`border-border/40`、`text-muted-foreground/60`。

### 1.2 严禁

- **任何 hex / `rgb()` / `hsl()`、Tailwind 默认调色盘（`slate-*`、`zinc-*`、`gray-*`、`sky-*`、`violet-*`、`pink-*`、`orange-*` 等）**。
- 例外：见 §1.3 语义色白名单。
- 不要写自定义 `style={{ color: '#...'}}`。需要透明混合时使用 `color-mix(in srgb, var(--primary) X%, transparent)`，与现有 `styles.css` 一致。

### 1.3 语义色白名单（仅此三种 Tailwind 原色合法）

| 语义     | 类                                                       | 触发条件                  |
| -------- | -------------------------------------------------------- | ------------------------- |
| 成功/运行 | `bg-emerald-500/15` `text-emerald-400`                   | 已启用、运行中、连接成功  |
| 警告/可更新 | `bg-amber-500/15` `text-amber-400`                       | 版本可更新、待办、超时    |
| 错误     | **必须用 `destructive` token**，禁止 `red-*`             | 删除、失败、不可逆操作    |

其他业务标签色（"自定义"、"实验"、"Beta"）一律用 `bg-primary/10 text-primary` 或 `bg-accent/60 text-muted-foreground`，**不要**引入 `violet-*` / `sky-*` / `pink-*`。

---

## 2. 卡片：默认 0 阴影，靠 border + bg 分层

### 2.0 线条粗细：全局统一 1px

- **所有线条（border、divider、ring、outline）必须是 1px**，全局统一，不允许出现粗细混用。
- 默认即 `border` / `ring-1` / `outline-1`（Tailwind 默认就是 1px），**禁止** `border-2`、`border-4`、`ring-2`、`outline-2` 及以上。
- 禁止 `border-[Npx]` 等任意值；如需"加重"分隔，改用更深的 token 颜色（如 `border-border` 替 `border-border/40`）或叠 `bg-*`，**不要**通过加粗线条来强调。
- 选中态、focus 态也走 `ring-1 ring-inset`，不要用 `ring-2` 增加视觉重量。


### 2.1 卡片基线

```tsx
<div className="rounded-xl border border-border/50 bg-card/40 backdrop-blur-sm transition-colors duration-200 hover:border-primary/40 hover:bg-card/60">
```

- **形状** `rounded-xl`
- **分层** 边框 `border-border/40~60` + 背景 `bg-card/30~60`（可叠 `backdrop-blur-sm`）
- **hover** 只换边框颜色和背景透明度，**不加阴影、不放大、不平移超过 2px**

### 2.2 阴影规则

| 场景                   | 允许                                |
| ---------------------- | ----------------------------------- |
| 普通卡片（list 项、grid 卡） | **无阴影**                           |
| Popover / Dropdown / Dialog | `shadow-md` 或 `shadow-lg`           |
| 拖拽中的元素           | `shadow-lg`                          |
| Toast / floating bar   | `shadow-md`                          |

**禁止：**

- 卡片 hover 添加 `shadow-*`
- 任何 `shadow-[...]` 自定义任意值
- 带 primary 色的发光阴影，如 `shadow-[0_4px_16px_-6px_var(--primary)]`（这种"发光卡片"观感被明确否决）
- 自定义 `--shadow-*` token，沿用 `styles.css` 已有八档

### 2.3 强调态（被选中、活跃 session）

用 `ring` 而不是 `shadow`：`ring-1 ring-inset ring-primary/30`、`bg-primary/10`、`border-primary/40`。

---

## 3. 圆角

| 元素                                                    | 类             |
| ------------------------------------------------------- | -------------- |
| 卡片、面板、grid 卡                                     | `rounded-xl`   |
| 列表项、输入框、按钮（常规 size）                       | `rounded-lg` 或 `rounded-md` |
| 小 chip / 图标块（h ≤ 9）                               | `rounded-lg`   |
| Pill / 标签 / segmented control / 圆形 icon button      | `rounded-full` |
| Hero 区域、超大装饰容器                                 | `rounded-2xl`（仅一处级别的视觉锚点，慎用） |

禁止 `rounded-3xl` 及以上，禁止任意 `rounded-[Npx]`。

---

## 4. 间距与密度

### 4.1 padding 选择

| 容器                                      | padding             |
| ----------------------------------------- | ------------------- |
| 紧凑列表项（h≈40）                        | `px-3 py-2.5`       |
| 标准卡片                                  | `px-3.5 pt-3 pb-3`  |
| 宽松卡片 / 设置面板                       | `p-4`               |
| 页面外层（page container）                | `px-8` + `pb-8`     |
| Popover 内 menu item                      | `px-2.5 py-1.5`     |

### 4.2 grid gap

| 内容                | gap          |
| ------------------- | ------------ |
| 紧凑卡片网格        | `gap-2.5`    |
| 标准卡片网格        | `gap-3`      |
| 大块统计/Section    | `gap-4` ~ `gap-5` |

grid 自适应：`grid-cols-[repeat(auto-fill,minmax(240px,1fr))]`（小卡）/ `minmax(320px,1fr)`（行式列表）。**禁止**写死 `grid-cols-3`。

### 4.3 字号梯度

仅使用以下七档，**不要**自创：`text-[10px]` `text-[11px]` `text-[12px]` `text-[13px]` `text-[14px]` `text-[15px]` `text-[20px]`+（统计数字一类）。

---

## 5. 动画

### 5.1 总则

UI 是工具，不是 showroom。**hover 只反馈，不表演。**

| 场景       | 允许                                                                        |
| ---------- | --------------------------------------------------------------------------- |
| 卡片 hover | `whileHover={{ y: -2 }}` **上限**；不允许 `scale`、`rotate`、`shadow` 联动 |
| 按钮 hover | `whileHover={{ scale: 1.04 }}` 上限                                          |
| 按钮 tap   | `whileTap={{ scale: 0.94 }}` 下限                                            |
| 进入       | `opacity 0→1 + y 8→0` 或 `+ scale 0.97→1`；duration ≤ 0.5s                  |
| stagger    | `staggerChildren: 0.04~0.06`，`delayChildren ≤ 0.15`                         |

### 5.2 spring 默认值

入场用 spring 时统一 `{ type: "spring", stiffness: 280~320, damping: 26 }`，按钮交互 `{ stiffness: 380, damping: 22 }`。

### 5.3 禁止

- 装饰性持续旋转 / 弹跳（loading spinner 除外）
- `whileHover` 同时改 `y` + `scale` + 阴影
- 长于 0.6s 的进入动画
- transition 写 `transition-all`；优先 `transition-colors` 或具体属性

### 5.4 「进行中」指示动画由宿主统一挂载，禁止自写 infinite 关键帧

macOS 主窗口带毛玻璃，页面每出一帧系统都要整窗重新合成；一个 4px 的脉冲点用平滑关键帧逐帧插值，就足以让流式全程 GPU 不闲。实测（Chromium 132）：opacity/transform 关键帧 + `steps(16)` 每秒画 12 帧、主线程≈0；平滑曲线 70 帧；用注册自定义属性做「时钟」画帧同样少，但 Blink 每帧都在主线程重算样式，挂在 `:root` 上时 300ms/s，**不要再走这条路**。

表达「还在进行」的常驻动效（呼吸文字、状态点、波纹、转弧）：

- CSS 只写静止态（相位 0 的外观必须就是正常外观，光晕这类没有动画就该看不见的元素写 `opacity: 0`），**不写 `animation` / `@keyframes … infinite`**。
- 元素带登记在 `shared/lib/live-animations.ts` 里的类名（`.processing-shimmer`、`.tool-call-shimmer-text`、`.astravia-live-dot`、`.todo-label-sheen`、`.todo-marker-spin`、`.activity-dot-halo/-core`、`.send-button-ripple-1/-2`）。宿主用 Web Animations API 挂 `steps(16)` 的 opacity/transform 动画，并把 `startTime` 锁到文档时间线原点：所有指示器同拍，整页每秒最多因此多出 10 帧，主线程零开销。新增一种指示器就往登记表加一行。
- 流式期间的内容动效不要用逐元素动画：短语渐亮靠「最新短语偏暗、下一次放出时变亮」，只随内容更新出帧；rAF 逐帧追随滚动只留给大跳，短距离直接落位；头像小动作在流式中关闭。
- `will-change` 对这类动画没有额外收益（实测主线程提交次数不变），不要为它加。

---

## 6. 图标（`@iconify/tailwind4`）

### 6.1 命名

**新增图标一律优先 solar**（`icon-[solar--*-linear]`，统一用 `linear` 线性风格），其次才是 mdi。存量 `icon-[mdi--*]` 渐进迁移，改动相关组件时顺手替换为 solar 同义图标。禁止引入其他 collection（`lucide`、`tabler` 等），避免 collection 蔓延。

solar 常用对照：菜单 `menu-dots`、关闭 `close-circle`、删除 `trash-bin-trash`、编辑 `pen-2`、文件夹 `folder` / `folder-open`、新建 `add-circle` / `add-folder`、设置 `settings`、铃铛 `bell`、会话 `chat-round-line`、加载旋转 `refresh` + `animate-spin`、折叠箭头 `alt-arrow-down` / `alt-arrow-up`。

### 6.2 尺寸梯度

| 场景                       | 类            |
| -------------------------- | ------------- |
| 行内 inline 状态点         | `h-2.5 w-2.5` |
| chip 内、tag 内            | `h-3 w-3`     |
| 按钮内、菜单项前缀         | `h-3.5 w-3.5` |
| 卡片左侧主图标块（含底色） | `h-4 w-4`     |
| Hero / 空状态居中大图标    | `h-8 w-8` ~ `h-10 w-10` |

图标颜色一律走 token：`text-primary` / `text-muted-foreground/60` / `text-destructive`，禁止 `text-emerald-*` 之外的原色（参 §1.3）。

---

## 7. 桌面端特有

### 7.1 drag-region

- 顶部高度 ≥ 32px 的窗口拖拽区域使用 `.drag-region`（已定义于 `styles.css`）。
- **禁止**在 `drag-region` 内放交互元素，如必须放（设置图标、关闭按钮）请套 `.no-drag`。
- 不要在 `drag-region` 上再写 `cursor-*` 或 `onClick`。

### 7.2 标题栏

新页面顶部留 `<div className="drag-region h-12 shrink-0" />` 作为可拖拽空白，再写页面 header；不要把 header 自身设为 drag-region。

### 7.3 滚动条

`styles.css` 已实现 macOS 风格 overlay scrollbar。**禁止**在组件里手写 `::-webkit-scrollbar` / `scrollbar-width`，否则会撕裂全局风格。

### 7.4 选中态

`::selection` 已用 `color-mix(var(--primary) 15%)`。不要为组件单独覆盖。

### 7.5 暗色为默认

`:root` 即暗色。亮色靠 `[data-theme="light"]`。**只有装饰性效果**（如外发光、网格纹理）才需要 `dark:` / `light:` 变体；token 颜色会自适应，**不要**为每个 token 类再写一份 `dark:`。

---

## 8. 组件使用纪律

来自 `AGENTS.md §1`，本节不再展开，仅划红线：

- 操作按钮**必须**用 `@shared/components/ui/button` 的 `<Button>` + variant，**优先复用通用组件，禁止重复造按钮**。
  - **禁止**手写 `<button>` / `<motion.button>` 充当按钮：不允许自拼 `rounded-full bg-gradient-to-* px-4 py-2 text-primary-foreground` 这类样式。
  - **禁止**给按钮加发光/自定义阴影，如 `shadow-[0_8px_24px_-10px_var(--primary)]`、`shadow-[0_10px_30px_-12px_var(--primary)]`（"肮脏的发光按钮"被明确否决）。主操作直接用 `variant="primary"`，不叠任何 `shadow-*`。
  - 主操作 → `variant="primary"`；次要 → `variant="outline"` / `secondary` / `ghost`；危险 → `variant="destructive"`。空状态 CTA 同样走 `<Button>`，不要单独造一个"更花"的版本。
  - 需要微调位置/间距用 `className` 传入（如 `mt-2`），**不要**重写整套视觉。
- Popover / Dialog / Switch / Select 等先翻 `shared/components/ui/`，不要重写。
- 自定义 toggle / segmented control 时参考现有 `SegmentedControl`、`ToggleSwitch`，沿用相同 token 与 transition。

---

## 9. Lint checklist（agent 提交前自查）

- [ ] 没有 hex / `rgb(` / 默认 Tailwind 调色盘（slate/zinc/sky/violet/pink/orange/red/blue/green，除 §1.3 白名单的 emerald/amber）
- [ ] 没有 `shadow-[...]` 自定义任意值；卡片 hover 无 shadow；按钮无发光阴影
- [ ] 按钮一律用 `<Button>` + variant，没有手写 `<button>` / `<motion.button>` 自拼按钮样式
- [ ] 所有线条 1px：没有 `border-2/4`、`ring-2`、`outline-2`、`border-[Npx]`
- [ ] 没有 `rounded-3xl` / `rounded-[Npx]`
- [ ] 没有 `transition-all`
- [ ] hover `whileHover` 不超过 §5.1 表格
- [ ] 顶栏拖拽区域单独 `<div className="drag-region h-12 shrink-0" />`
- [ ] 没有手写 scrollbar 样式

---

## 10. 已知存量违规（不要复制）

以下文件混入了违规色或视觉，agent **不要**以它们为范例，遇到顺手清理：

- 暂无。`SkillsPage.tsx` 的 `violet-*` 自定义标签已迁移为 `bg-primary/10 text-primary`。
