返回 DeepSeek-Reasonix
THEME_PACK.md
根目录 / docs / THEME_PACK.md
1 # Reasonix Theme Pack V2
2
3 Native theme packs for the Reasonix desktop app. Packs are controlled skins:
4 semantic color tokens, density/corner recipes, and optional local images for
5 the home and task/workspace scenes. They **cannot** run CSS, JavaScript, fonts,
6 remote URLs, or SVG scripts. V1 packs remain valid and use the home image in
7 both scenes.
8
9 > Chinese: [THEME_PACK.zh-CN.md](./THEME_PACK.zh-CN.md)
10
11 ## Goals (first release)
12
13 - Built-in styles, user themes, backgrounds, live preview, import/export, local library
14 - Full background on the home (empty) scene; reduced opacity + directional overlay on task scenes
15 - Works with Workbench / Creation and `auto` / `light` / `dark`
16 - **No** online marketplace, cloud sync, or script plugins
17
18 ## Theme experience (settings IA)
19
20 Chat code fences use a language header and an always-visible copy control on
21 the same opaque surface as the source. Syntax colors distinguish keywords,
22 strings, functions, numbers and comments; they are corrected against the final
23 code and diff backgrounds to reach at least 4.5:1 contrast, including inverted
24 and translucent custom palettes. Wallpaper opacity does not affect the code
25 or its header controls. Theme changes recolor existing syntax nodes through CSS.
26 Growing code fences use the same viewer; unchanged highlighted lines retain
27 their DOM, while large appended revisions preserve the colored prefix until
28 idle highlighting catches up. Unknown languages remain plain text.
29
30 Run `pnpm test:code-browser` in `desktop/frontend` for the actual Markdown
31 component's theme, streaming, copy and narrow-viewport regressions.
32
33 Appearance is split into two surfaces (no third entry):
34
35 1. **Appearance overview** — current theme summary, light/dark mode, **one** base-style
36 control, fonts and zoom. Primary action: **Browse themes**.
37 2. **Theme gallery** — official / my themes / base styles tabs, select-to-inspect cards,
38 detail panel with isolated preview, temporary full-app preview, and a single
39 **Apply theme** action. Immersive preview is part of the gallery detail flow.
40
41 State model (schema v2 of `desktop-theme-state.json`):
42
43 | State | Meaning | Persistence |
44 | --- | --- | --- |
45 | `themeMode` | auto / light / dark | desktop config |
46 | `baseStyle` | Graphite…Amber | desktop config (`theme_style`) |
47 | `activeThemeId` | official, user or plugin pack only | `desktop-theme-state.json` |
48 | `selectedThemeId` / `previewThemeId` | gallery selection / temp preview | frontend memory only |
49
50 - `activeThemeId` **must not** store base style ids. Choosing a base style clears the pack.
51 - Applying a pack keeps `baseStyle` as the disable/fallback value.
52 - Light/dark mode is independent of the pack.
53
54 ## Theme kinds
55
56 The gallery has four groups:
57
58 | Kind | Source | Editable | Deletable | Exportable |
59 | --- | --- | --- | --- | --- |
60 | **Base styles** | Six visual directions (Graphite, Aurora, Slate, Carbon, Nocturne, Amber), token-less | no (duplicate first) | no | no |
61 | **Official themes** | Eight read-only packs embedded in the installer (manifest + original background + thumbnail, MIT) | no (duplicate first) | no | no |
62 | **User themes** | Created in the editor, duplicated, or imported as `.reasonix-theme` | yes | yes | yes |
63 | **Plugin themes** | `.reasonix-theme` packs contributed by enabled plugins (Manifest v2 `contributes.themes`), read straight from the plugin root — never copied into the user library | no | no (disable/uninstall the plugin) | no |
64
65 - All 14 built-in ids (6 base + 8 official) are **reserved**: save, import, copy-over
66 and delete all refuse collisions.
67 - Activating an official theme stores only its id in `desktop-theme-state.json` —
68 assets are read from the embedded copy at runtime.
69 - "Duplicate" on a base/official theme creates an ordinary editable user theme
70 (the official background is copied into the user library); the duplicate can
71 then be edited or exported.
72 - v1 states that stored a base id as `activeThemeId` are migrated to `desktop.theme_style`
73 and cleared on load.
74 - Plugin theme ids are external names of the form `plugin:<plugin>:<theme>`; the
75 pack's own `id` keeps following the usual id rules. Invalid contributed files
76 are skipped with a warning in the theme views, never fatal. When the plugin
77 behind the active id is missing, disabled or uninstalled, rendering falls back
78 to the configured base style but the id is **preserved** in
79 `desktop-theme-state.json` — reinstalling the same plugin restores the theme.
80 Save/delete/duplicate/export reject plugin theme ids as read-only.
81
82 ### The eight official themes
83
84 | ID | Name | Base style | Artwork |
85 | --- | --- | --- | --- |
86 | `official-rose-dawn` | Rose Dawn / 玫瑰晨光 | graphite | Ivory dawn, soft roses, original illustrated muse |
87 | `official-fortune-forge` | Fortune Forge / 鸿运工坊 | amber | Vermilion/gold/jade workshop, original lucky programmer |
88 | `official-crimson-horizon` | Crimson Horizon / 赤曜新城 | graphite | Coral-red future city skyline, no people |
89 | `official-sage-breeze` | Sage Breeze / 鼠尾草清风 | slate | Cream paper, sage sprigs, original reader |
90 | `official-spark-notebook` | Spark Notebook / 灵感手账 | aurora | Notebook grid with stationery, original anime adult |
91 | `official-violet-starlight` | Violet Starlight / 紫曜星夜 | nocturne | Blue-violet starfield, butterflies, silhouette muse |
92 | `official-cyan-stage` | Cyan Stage / 青岚舞台 | carbon | Cyan stage, light rings, original digital performer |
93 | `official-noir-gold` | Noir Gold / 黑金序曲 | carbon | Black velvet, gold spotlights, original gentleman |
94
95 Previews are shown inside the app's theme library (Settings → Appearance) from
96 real Reasonix builds. **Screenshots of the app must not be imported as theme
97 backgrounds.** Asset provenance, hashes and licence ledger:
98 [THEME_ASSETS.md](./THEME_ASSETS.md) · generator scripts in
99 `scripts/official-theme-art/` (procedural, fixed seeds, reproducible).
100
101 ## Package format
102
103 Distribute as a `.reasonix-theme` ZIP. The archive root may contain **only**:
104
105 | File | Required | Notes |
106 | --- | --- | --- |
107 | `theme.json` | yes | Manifest (≤ 1 MiB) |
108 | `background.png` / `.jpg` / `.jpeg` / `.webp` | no | Home image ≤ 16 MiB, ≤ 8192×8192 |
109 | `background-task.png` / `.jpg` / `.jpeg` / `.webp` | no | Independent task/workspace image ≤ 16 MiB, ≤ 8192×8192 (V2) |
110
111 ZIP limits: package ≤ 36 MiB; no nested directories, no symlinks, no duplicate entries, no path traversal.
112
113 ### `theme.json` example
114
115 ```json
116 {
117 "schemaVersion": 2,
118 "id": "my-theme",
119 "name": "My Theme",
120 "author": "",
121 "description": "",
122 "license": "",
123 "baseStyle": "graphite",
124 "tokens": {
125 "light": {
126 "bg": "#f4f3ef",
127 "fg": "#111827",
128 "accent": "#2f5fa8"
129 },
130 "dark": {
131 "bg": "#0c0d10",
132 "fg": "#f1f1ef",
133 "accent": "#ff6a3d"
134 }
135 },
136 "recipes": {
137 "density": "comfortable",
138 "corners": "soft"
139 },
140 "background": {
141 "image": "background.webp",
142 "focusX": 0.72,
143 "focusY": 0.45,
144 "safeArea": "left",
145 "homeOpacity": 1,
146 "taskOpacity": 0.28,
147 "overlayStrength": 0.62
148 },
149 "taskBackground": {
150 "image": "background-task.webp",
151 "focusX": 0.5,
152 "focusY": 0.5,
153 "safeArea": "right",
154 "opacity": 0.28,
155 "overlayStrength": 0.62
156 }
157 }
158 ```
159
160 JSON Schema: [theme-pack.schema.json](./theme-pack.schema.json)
161
162 ### Fields
163
164 | Field | Rules |
165 | --- | --- |
166 | `schemaVersion` | `1` or `2`; `taskBackground` requires `2` |
167 | `id` | Lowercase `[a-z][a-z0-9-]*`, reserved: `graphite`, `aurora`, `slate`, `carbon`, `nocturne`, `amber` |
168 | `baseStyle` | One of the six built-in directions; uncovered tokens inherit it |
169 | `tokens.light` / `tokens.dark` | Optional maps of semantic keys → `#RRGGBB` or `#RRGGBBAA` only |
170 | `recipes.density` | `compact` \| `comfortable` |
171 | `recipes.corners` | `square` \| `soft` \| `round` |
172 | `background.image` | Bare file name only (png/jpeg/webp) |
173 | `background.focusX/Y` | 0–1 focal point |
174 | `background.safeArea` | `left` \| `right` \| `center` (task overlay direction) |
175 | `background.homeOpacity` | 0–1 |
176 | `background.taskOpacity` | 0–1 |
177 | `background.overlayStrength` | 0–1 |
178 | `background.paneOpacity` | 0–1 (home scene panel opacity) |
179 | `taskBackground.image` | Optional independent task/workspace image; bare local file name only |
180 | `taskBackground.focusX/Y` | 0–1 focal point |
181 | `taskBackground.safeArea` | `left` \| `right` \| `center` |
182 | `taskBackground.opacity` | 0–1 |
183 | `taskBackground.overlayStrength` | 0–1 |
184 | `taskBackground.paneOpacity` | 0–1 (task scene panel opacity) |
185
186 ### Allowed token keys
187
188 `bg`, `bgSoft`, `bgElev`, `panel`, `sidebar`, `chat`, `workspace`, `workspaceFiles`,
189 `border`, `borderSoft`, `fg`, `fgDim`, `fgFaint`, `accent`, `accentFg`, `ok`, `warn`, `err`
190
191 Colors must **not** include `url()`, gradients, or arbitrary CSS.
192
193 ## Engine behavior
194
195 1. Apply global `auto` / `light` / `dark` and the base visual style.
196 2. Apply the pack overlay (CSS custom properties) **after** stylesheets so it wins over trailing `:root` and Creation locals.
197 3. Root gets `data-theme-pack="<id>"`; the app container gets `data-theme-scene="home|task"`.
198 4. Scene is derived only from whether the current session has content — it does not change chat lifecycle.
199 5. Background is a fixed, non-interactive layer. Task scene dims the image and paints a directional wash (**no** `backdrop-filter`).
200
201 ## Storage
202
203 | Path under Reasonix home | Purpose |
204 | --- | --- |
205 | `desktop-theme-state.json` | Versioned active theme pointer (not `config.toml`) |
206 | `themes/<id>/` | User theme library (`theme.json` + up to two optional scene images) |
207
208 Legacy installs without theme state keep the previous appearance. Old app versions ignore the new directory. CLI theme, prompts, provider requests, and cache keys are unchanged.
209
210 ## Desktop bridge (frontend)
211
212 List / activate / reset / save / delete / copy / import / export / pick background.
213 The UI only receives temporary asset URLs (`/__reasonix_theme_asset/...`) or data URLs — never absolute host paths.
214
215 Import: same id is rejected until the user confirms atomic replace. Built-ins cannot be overwritten or deleted. Corrupt / missing packs fall back to the Graphite path. `/theme reset` and the command palette restore entry clear the pack.
216
217 ## Authoring tips
218
219 1. Start from a built-in direction and override only the tokens you need.
220 2. Prefer WCAG AA contrast (≈ 4.5:1 body text). The editor warns but does not block save.
221 3. Before sharing a pack with a photo or portrait, confirm redistribution rights.
222 4. Do not ship third-party or copyrighted reference assets from other products.
223
224 ## Template
225
226 A minimal, royalty-free starter (no portrait photos):
227
228 ```json
229 {
230 "schemaVersion": 1,
231 "id": "paper-dawn",
232 "name": "Paper Dawn",
233 "author": "Reasonix",
234 "description": "Template theme — solid tokens only, no background image.",
235 "license": "CC0-1.0",
236 "baseStyle": "graphite",
237 "tokens": {
238 "light": {
239 "bg": "#f7f4ef",
240 "panel": "#ffffff",
241 "sidebar": "#f3efe8",
242 "chat": "#fbfaf7",
243 "fg": "#1c1917",
244 "fgDim": "#57534e",
245 "fgFaint": "#a8a29e",
246 "border": "#e7e5e4",
247 "accent": "#c2410c",
248 "accentFg": "#fff7ed",
249 "ok": "#15803d",
250 "warn": "#b45309",
251 "err": "#b91c1c"
252 },
253 "dark": {
254 "bg": "#0c0b0a",
255 "panel": "#171412",
256 "sidebar": "#141210",
257 "chat": "#0c0b0a",
258 "fg": "#f5f5f4",
259 "fgDim": "#a8a29e",
260 "fgFaint": "#78716c",
261 "border": "#292524",
262 "accent": "#fb923c",
263 "accentFg": "#0c0b0a",
264 "ok": "#4ade80",
265 "warn": "#fbbf24",
266 "err": "#f87171"
267 }
268 },
269 "recipes": {
270 "density": "comfortable",
271 "corners": "soft"
272 }
273 }
274 ```
275
276 Zip as `paper-dawn.reasonix-theme` with only `theme.json` at the root.
277
277 lines MARKDOWN