@nonamelego/dsh-catppuccin 0.4.2 → 0.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +304 -0
- package/README.md +8 -5
- package/lib/client.js +118 -66
- package/lib/client.js.map +1 -1
- package/lib/index.js +108 -145
- package/lib/tui-themes.js +2 -2
- package/package.json +16 -9
package/README.en.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
<h3 align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/logos/exports/1544x1544_circle.png" width="100" alt="Logo"/><br/>
|
|
3
|
+
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/misc/transparent.png" height="30" width="0px"/>
|
|
4
|
+
Catppuccin for <a href="https://github.com/deepseek-ai/deepseek-harness">DeepSeek Harness</a>
|
|
5
|
+
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/misc/transparent.png" height="30" width="0px"/>
|
|
6
|
+
</h3>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://github.com/NoNameLeGo/dsh-catppuccin-theme/stargazers"><img src="https://img.shields.io/github/stars/NoNameLeGo/dsh-catppuccin-theme?colorA=363a4f&colorB=b7bdf8&style=for-the-badge"></a>
|
|
10
|
+
<a href="https://github.com/NoNameLeGo/dsh-catppuccin-theme/issues"><img src="https://img.shields.io/github/issues/NoNameLeGo/dsh-catppuccin-theme?colorA=363a4f&colorB=f5a97f&style=for-the-badge"></a>
|
|
11
|
+
<a href="https://github.com/NoNameLeGo/dsh-catppuccin-theme/contributors"><img src="https://img.shields.io/github/contributors/NoNameLeGo/dsh-catppuccin-theme?colorA=363a4f&colorB=a6da95&style=for-the-badge"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@nonamelego/dsh-catppuccin"><img src="https://img.shields.io/npm/v/@nonamelego/dsh-catppuccin?colorA=363a4f&colorB=a6da95&style=for-the-badge"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/@nonamelego/dsh-catppuccin"><img src="https://img.shields.io/npm/dt/@nonamelego/dsh-catppuccin?colorA=363a4f&colorB=f5a97f&style=for-the-badge"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
**English** | [中文](README.md)
|
|
17
|
+
|
|
18
|
+
> A theme & glassmorphism plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — Catppuccin flavours for the **Web GUI**, **DSH Desktop** and **dsh-TUI**, plus a switchable frosted-glass skin. Listed on [Awesome DSH Plugin](https://awesome-dsh-plugin.com/p/NoNameLeGo__dsh-catppuccin-theme/).
|
|
19
|
+
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [Introduction](#introduction)
|
|
23
|
+
- [Features](#features)
|
|
24
|
+
- [Previews](#previews)
|
|
25
|
+
- [Installation](#installation)
|
|
26
|
+
- [Usage](#usage)
|
|
27
|
+
- [Glassmorphism](#glassmorphism)
|
|
28
|
+
- [Development](#development)
|
|
29
|
+
- [🙋 FAQ](#-faq)
|
|
30
|
+
- [💝 Credits](#-credits)
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="assets/previews/combined.png" width="100%" alt="DeepSeek Harness under the four Catppuccin flavours"/>
|
|
34
|
+
<br/><br/>
|
|
35
|
+
<img src="assets/previews/glass-combined.png" width="100%" alt="Glass skin · Latte & Mocha"/>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
## Introduction
|
|
39
|
+
|
|
40
|
+
A [Catppuccin](https://github.com/catppuccin/catppuccin) theme plugin for
|
|
41
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — one package that fits
|
|
42
|
+
the **Web GUI** (`dsh web`), **DSH Desktop** and **dsh-TUI** alike: full recolouring plus a
|
|
43
|
+
glass skin on Web / Desktop, and the four official theme palettes auto-synced to the TUI.
|
|
44
|
+
|
|
45
|
+
It ships all four Catppuccin flavours — **Latte**, **Frappé**, **Macchiato**, **Mocha** —
|
|
46
|
+
remapping the whole interface to the matching palette, with a **Catppuccin** row right below
|
|
47
|
+
**Settings → General → Appearance** for one-click switching. Your choice is persisted and
|
|
48
|
+
restored after restarts.
|
|
49
|
+
|
|
50
|
+
It also includes an optional **glassmorphism** skin: the top bar, sidebar, composer,
|
|
51
|
+
stats line, trajectory view, chat bubbles and the new-session button become frosted-glass
|
|
52
|
+
cards, with adjustable blur, frost and backdrop brightness. Glass colours follow the active
|
|
53
|
+
Catppuccin theme automatically.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- 🎨 Four themes: Latte (light), Frappé / Macchiato / Mocha (dark)
|
|
58
|
+
- 🧩 Registered into the official theme system, on a par with the built-in light / dark / system themes
|
|
59
|
+
- 🎯 Full-UI colour coverage — not just one or two accent colours
|
|
60
|
+
- ⚙️ One-click switch in Settings, persisted and restored across restarts
|
|
61
|
+
- 🌐 Bilingual UI (Chinese / English, follows the system language)
|
|
62
|
+
- 🪟 **Glass skin**: frosted glass for the top bar / sidebar / composer / stats line /
|
|
63
|
+
trajectory view / chat bubbles / new-session button, one-click toggle in Settings;
|
|
64
|
+
mica & compatibility modes, adjustable blur, frost and backdrop brightness
|
|
65
|
+
(interaction reference: [DSH-Transparent-UI-Plugin](https://github.com/WYH66666666/DSH-Transparent-UI-Plugin))
|
|
66
|
+
- 🌫️ **Glass details**: gradient blur bands at the top/bottom page edges, a floating glass
|
|
67
|
+
rail when the sidebar is collapsed, a solid background in the theme's own base colour —
|
|
68
|
+
content softens as it scrolls under the panes
|
|
69
|
+
- 🎨 Glass colours follow the current Catppuccin theme
|
|
70
|
+
- 🔄 **Update check**: one-click "Check for updates" in Settings compares the latest npm
|
|
71
|
+
version and gives you a copyable upgrade command
|
|
72
|
+
- 💻 **dsh-TUI terminal themes**: one command installs into dsh-TUI; the four themes sync to
|
|
73
|
+
`~/.dsh-tui/themes/` automatically (see [Installation · dsh-TUI](#dsh-tui-terminal-themes))
|
|
74
|
+
|
|
75
|
+
## Previews
|
|
76
|
+
|
|
77
|
+
Actual screenshots from a local GUI (the header image is a diagonal blend of the four):
|
|
78
|
+
|
|
79
|
+
<details>
|
|
80
|
+
<summary>🌻 Latte (light)</summary>
|
|
81
|
+
<img src="assets/previews/latte.png"/>
|
|
82
|
+
</details>
|
|
83
|
+
<details>
|
|
84
|
+
<summary>🪴 Frappé (dark)</summary>
|
|
85
|
+
<img src="assets/previews/frappe.png"/>
|
|
86
|
+
</details>
|
|
87
|
+
<details>
|
|
88
|
+
<summary>🌺 Macchiato (dark)</summary>
|
|
89
|
+
<img src="assets/previews/macchiato.png"/>
|
|
90
|
+
</details>
|
|
91
|
+
<details>
|
|
92
|
+
<summary>🌿 Mocha (dark)</summary>
|
|
93
|
+
<img src="assets/previews/mocha.png"/>
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
### Glass skin (Mica mode)
|
|
97
|
+
|
|
98
|
+
The frosted-glass effect in light (Latte) and dark (Mocha): the top bar, sidebar,
|
|
99
|
+
chat bubbles, composer and stats line are all glass cards; messages soften as they scroll
|
|
100
|
+
past the page edges; the background is the theme's solid base colour:
|
|
101
|
+
|
|
102
|
+
<details>
|
|
103
|
+
<summary>🌻 Latte (light glass)</summary>
|
|
104
|
+
<img src="assets/previews/glass-latte.png"/>
|
|
105
|
+
</details>
|
|
106
|
+
<details>
|
|
107
|
+
<summary>🌿 Mocha (dark glass)</summary>
|
|
108
|
+
<img src="assets/previews/glass-mocha.png"/>
|
|
109
|
+
</details>
|
|
110
|
+
|
|
111
|
+
## Installation
|
|
112
|
+
|
|
113
|
+
### Option 1: from npm (recommended)
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
dsh plugin --profile web add @nonamelego/dsh-catppuccin
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Restart `dsh web` after installing — `dsh plugin` adds it to the profile's bundles.
|
|
120
|
+
Use the profile name of your choice in place of `web` (e.g. `headless`).
|
|
121
|
+
|
|
122
|
+
**[DSH Desktop](https://github.com/anywhere-labs/deepseek-harness-desktop)**: the desktop build's
|
|
123
|
+
active profile is named `desktop`, so run:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
dsh plugin --profile desktop add @nonamelego/dsh-catppuccin
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Run it in the DSH terminal of the desktop app (`dsh plugin` defaults to the active profile).
|
|
130
|
+
Installing from the repo works the same way: `dsh plugin --profile desktop add https://github.com/NoNameLeGo/dsh-catppuccin-theme`.
|
|
131
|
+
|
|
132
|
+
### Option 2: from the repository
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
dsh plugin --profile web add https://github.com/NoNameLeGo/dsh-catppuccin-theme
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
When installing from git, pnpm may ask you to allow build scripts — follow pnpm's prompt
|
|
139
|
+
and add the package to the profile's `pnpm-workspace.yaml` `allowBuilds`, then run it again.
|
|
140
|
+
|
|
141
|
+
### dsh-TUI (terminal) themes
|
|
142
|
+
|
|
143
|
+
The same package covers the TUI. Install it into the dsh-tui profile:
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
dsh plugin --profile dsh-tui add @nonamelego/dsh-catppuccin
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Installing from the repository works the same way (use the git form while the npm release
|
|
150
|
+
is pending). The package ships a tiny theme-sync plugin row
|
|
151
|
+
(`dsh-catppuccin-tui-themes`, no service dependencies): on every dsh-TUI start it syncs the
|
|
152
|
+
four theme JSONs to `~/.dsh-tui/themes/`, so upgrades pick up the new palettes. After
|
|
153
|
+
installing, launch `dsh --profile dsh-tui` and pick the theme with `/theme` —
|
|
154
|
+
**Catppuccin Latte / Frappé / Macchiato / Mocha**, or jump straight to it with
|
|
155
|
+
`/theme catppuccin-mocha` (your choice persists across restarts).
|
|
156
|
+
|
|
157
|
+
> 💡 Already installed the plugin for the Web GUI and also use dsh-TUI? No need to install
|
|
158
|
+
> it twice: every Web start auto-syncs the themes to `~/.dsh-tui/themes/` (only when the
|
|
159
|
+
> directory already exists).
|
|
160
|
+
|
|
161
|
+
> 📁 Prefer not to install the package? Copy `themes/*.json` into
|
|
162
|
+
> `~/.dsh-tui/themes/` (Windows: `%USERPROFILE%\.dsh-tui\themes\`) by hand — you just won't
|
|
163
|
+
> get updates automatically.
|
|
164
|
+
|
|
165
|
+
> ⚠️ `catppuccin-*.json` belongs to this plugin and is overwritten on sync; rename the files
|
|
166
|
+
> if you want custom themes.
|
|
167
|
+
|
|
168
|
+
> 💡 TUI themes only style the TUI itself — the terminal background is up to your terminal.
|
|
169
|
+
> Pairing it with the matching Catppuccin flavour (see the
|
|
170
|
+
> [Catppuccin ports list](https://github.com/catppuccin/catppuccin#-ports)) looks best.
|
|
171
|
+
|
|
172
|
+
## Usage
|
|
173
|
+
|
|
174
|
+
1. Open the Web GUI (default `http://127.0.0.1:3080`), or the DSH Desktop app.
|
|
175
|
+
2. Go to **Settings → General**.
|
|
176
|
+
3. Find the **Catppuccin** row below **Appearance** and pick a flavour:
|
|
177
|
+
**Latte** (light), **Frappé**, **Macchiato** or **Mocha** (dark).
|
|
178
|
+
4. Choosing **Follow system** reverts to the official theme — it restores the preference
|
|
179
|
+
you had before enabling Catppuccin (light / dark / follow system) instead of forcing a reset.
|
|
180
|
+
|
|
181
|
+
### Glass skin
|
|
182
|
+
|
|
183
|
+
Right below the **Catppuccin theme** row in **Settings → General** you'll find the **Glass** row:
|
|
184
|
+
|
|
185
|
+
- **Master switch**: on — the top bar, sidebar, composer, stats line and trajectory view
|
|
186
|
+
become frosted glass; off — the UI reverts to stock instantly (no refresh needed).
|
|
187
|
+
- **Mode**: **Mica** turns the interface into floating frosted cards; **Compatibility** keeps
|
|
188
|
+
the stock layout and swaps only the material.
|
|
189
|
+
- **Presets**: **Clear / Standard / Frosted** one-click presets; fine-tune with the sliders
|
|
190
|
+
afterwards (a preset lights up when the current knob values match it).
|
|
191
|
+
- **Blur** (0–40 px) and **Frost** (0–100%): the blur radius and opacity of the glass.
|
|
192
|
+
- **Backdrop brightness**: dark mode darkens 0–50, light mode brightens 50–100 (50 = as-is),
|
|
193
|
+
mixed straight into the solid background.
|
|
194
|
+
|
|
195
|
+
Glass colours follow the active theme live; all settings persist across restarts.
|
|
196
|
+
|
|
197
|
+
### Check for plugin updates
|
|
198
|
+
|
|
199
|
+
In **Settings → General**, right below the **Glass** row:
|
|
200
|
+
|
|
201
|
+
- Clicking **Check for updates** compares the latest npm version with the installed one:
|
|
202
|
+
up to date → shows the current version; newer → shows the new version plus a copyable
|
|
203
|
+
upgrade command (the profile name is detected automatically, falling back to `web`).
|
|
204
|
+
- Locally linked / source installs (`link:` / `file:` / git) don't show an npm upgrade
|
|
205
|
+
command — you'll be told to `git pull` or rebuild instead.
|
|
206
|
+
- Channel policy: stable builds follow the `latest` tag; prereleases follow both `latest`
|
|
207
|
+
and `beta` (the upgrade command automatically carries `@beta`). Offline or network
|
|
208
|
+
failures show the reason and offer a retry.
|
|
209
|
+
|
|
210
|
+
## Glassmorphism
|
|
211
|
+
|
|
212
|
+
**Glassmorphism** is a visual style in which panels look like frosted glass — translucent
|
|
213
|
+
fills, backdrop blur (`backdrop-filter: blur()`), and glass details (rim, inner highlight,
|
|
214
|
+
soft shadow) — letting the content behind show through, softened.
|
|
215
|
+
|
|
216
|
+
What this plugin does:
|
|
217
|
+
|
|
218
|
+
- **Seven glass areas**: top bar, sidebar, composer, stats line, trajectory view, chat
|
|
219
|
+
bubbles and the new-session button; in Mica mode they become rounded floating cards and
|
|
220
|
+
chat content scrolls *under* the glass, blurred; the collapsed sidebar becomes a
|
|
221
|
+
floating rail at the edge of the chat area;
|
|
222
|
+
- **Page-edge gradient blur**: a blur band at the top and bottom of the viewport so
|
|
223
|
+
messages are softened as they melt past the edges (borrowed from
|
|
224
|
+
[DSH-Transparent-UI-Plugin](https://github.com/WYH66666666/DSH-Transparent-UI-Plugin)'s
|
|
225
|
+
Aqua skin);
|
|
226
|
+
- **Theme-following colours**: Latte is light glass, Mocha dark glass — switching flavours
|
|
227
|
+
recolours instantly. The page ground is the theme's solid colour; the brightness knob
|
|
228
|
+
mixes white/black straight into it;
|
|
229
|
+
- **One-click toggle**: off restores the stock UI exactly; uninstalling the plugin leaves
|
|
230
|
+
nothing behind.
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
pnpm install
|
|
236
|
+
pnpm typecheck # tsc --noEmit type check
|
|
237
|
+
pnpm test # vitest palette-coverage tests
|
|
238
|
+
pnpm build # tsdown build -> lib/index.js (host) + lib/client.js (browser)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Palettes are produced by a generator script — after editing
|
|
242
|
+
`scripts/generate-palettes.mjs`, rerun:
|
|
243
|
+
|
|
244
|
+
```sh
|
|
245
|
+
node scripts/generate-palettes.mjs
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The changelog draft is generated from your conventional commits (bilingual `EN:` support
|
|
249
|
+
in commit bodies):
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
pnpm changelog:gen # print the draft since the last tag
|
|
253
|
+
pnpm changelog:gen -- --write # write it into the [Unreleased] section
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Local link debugging
|
|
257
|
+
|
|
258
|
+
Clone the repo, link it into a profile and add it to the bundles (use your own paths):
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
pnpm --dir C:\Users\LeGo\.dsh\profiles\web add link:D:\Vibe-Coding\dsh-catppuccin
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Then add `@nonamelego/dsh-catppuccin` to the profile's `package.json`
|
|
265
|
+
`dsh.profile.bundles` and restart `dsh web`. For DSH Desktop use
|
|
266
|
+
`~/.dsh/profiles/desktop` instead.
|
|
267
|
+
|
|
268
|
+
## 🙋 FAQ
|
|
269
|
+
|
|
270
|
+
- **Q: "Why don't I see the Catppuccin themes in the Appearance row?"**\
|
|
271
|
+
A: The stock Appearance row only lists the built-in light / dark / follow-system
|
|
272
|
+
preferences. The four flavours live in the **Catppuccin** row right below it.
|
|
273
|
+
- **Q: "How is my theme choice remembered?"**
|
|
274
|
+
A: The choice persists in DSH's official settings document (the `catppuccin` namespace,
|
|
275
|
+
stored under the DSH home, shared across every DSH instance on the machine), with
|
|
276
|
+
localStorage as an in-browser cache and cross-tab sync. So plain `dsh web`,
|
|
277
|
+
`dsh web --port <custom>`, and **DSH Desktop** (which boots on a fresh random loopback
|
|
278
|
+
port every launch) all restore the preference automatically. The glass switch and every
|
|
279
|
+
knob persist the same way. Since 0.5.0, a legacy `catppuccin-state.json` is migrated
|
|
280
|
+
into the settings document once on first launch (the file is kept as a rollback copy).
|
|
281
|
+
- **Q: "How do I know if this plugin has a new version?"**
|
|
282
|
+
A: Settings → General → **Check Catppuccin plugin updates** compares against npm in one
|
|
283
|
+
click and gives a copyable upgrade command; or run
|
|
284
|
+
`dsh plugin --profile web update @nonamelego/dsh-catppuccin` manually (re-`add` the
|
|
285
|
+
latest version works too). In [DSH Desktop](https://github.com/anywhere-labs/deepseek-harness-desktop),
|
|
286
|
+
use `desktop` as the profile name, or just run `dsh plugin update` in the app's DSH terminal.
|
|
287
|
+
|
|
288
|
+
## 💝 Credits
|
|
289
|
+
|
|
290
|
+
- [Catppuccin](https://github.com/catppuccin/catppuccin) for the palettes and port templates
|
|
291
|
+
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) for the plugin system
|
|
292
|
+
- [DSH-Transparent-UI-Plugin](https://github.com/WYH66666666/DSH-Transparent-UI-Plugin)
|
|
293
|
+
for the glass-skin interaction and implementation reference (mica / compatibility modes,
|
|
294
|
+
blur / frost / brightness knobs)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
<p align="center">
|
|
299
|
+
<img src="https://raw.githubusercontent.com/catppuccin/catppuccin/main/assets/footers/gray0_ctp_on_line.svg?sanitize=true" />
|
|
300
|
+
</p>
|
|
301
|
+
|
|
302
|
+
<p align="center">
|
|
303
|
+
Copyright © 2021-present <a href="https://github.com/catppuccin/catppuccin" target="_blank">Catppuccin Org</a>
|
|
304
|
+
</p>
|
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
<a href="https://www.npmjs.com/package/@nonamelego/dsh-catppuccin"><img src="https://img.shields.io/npm/dt/@nonamelego/dsh-catppuccin?colorA=363a4f&colorB=f5a97f&style=for-the-badge"></a>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
|
+
**中文** | [English](README.en.md)
|
|
17
|
+
|
|
16
18
|
## 目录
|
|
17
19
|
|
|
18
20
|
- [简介](#简介)
|
|
@@ -251,11 +253,12 @@ pnpm --dir C:\Users\LeGo\.dsh\profiles\web add link:D:\Vibe-Coding\dsh-catppucci
|
|
|
251
253
|
A: 官方外观行只列出内置的浅色/深色/跟随系统偏好。四个主题在它正下方的
|
|
252
254
|
**Catppuccin** 行里。
|
|
253
255
|
- Q: **_"我的主题选择是怎么记住的?"_**
|
|
254
|
-
A: 选择持久保存在 DSH
|
|
255
|
-
DSH 共享这份偏好),浏览器 localStorage
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
A: 选择持久保存在 DSH 的官方 settings 文档(`catppuccin` namespace,存于
|
|
257
|
+
DSH home 下,同一台机器的 DSH 共享这份偏好),浏览器 localStorage 作为
|
|
258
|
+
即时缓存与多标签页同步。因此普通 `dsh web`、`dsh web --port` 自定义端口,
|
|
259
|
+
以及 **DSH Desktop**(每次启动用随机回环端口)都能跨重启自动恢复;玻璃质感
|
|
260
|
+
开关与各旋钮同样持久保存。0.5.0 起旧版 `catppuccin-state.json` 会在首次
|
|
261
|
+
启动时一次性迁移进 settings 文档(文件保留作回退)。
|
|
259
262
|
- Q: **_"怎么知道这个插件有没有新版本?"_**
|
|
260
263
|
A: 设置 → 常规 → **检查 Catppuccin 插件更新** 一键检测本插件在 npm 上的最新版本,
|
|
261
264
|
发现新版会给出可复制的升级命令;也可以随时手动执行
|
package/lib/client.js
CHANGED
|
@@ -1037,8 +1037,9 @@ window.__ModuleLoader__.load({
|
|
|
1037
1037
|
observer.disconnect();
|
|
1038
1038
|
};
|
|
1039
1039
|
}
|
|
1040
|
-
/**
|
|
1041
|
-
|
|
1040
|
+
/** Settings namespace both halves address: Host registers it, Client binds
|
|
1041
|
+
* it through `ctx.settingsScope` (see `src/settings-catppuccin.ts`). */
|
|
1042
|
+
const CATPPUCCIN_SETTINGS_NS = "catppuccin";
|
|
1042
1043
|
/** The four registered Catppuccin theme ids. MUST stay in sync with the
|
|
1043
1044
|
* `themeId` of `CATPPUCCIN_FLAVORS` in `src/client/palettes.ts` — guarded by
|
|
1044
1045
|
* `tests/state.spec.ts` — because the persisted flavour value IS the theme id. */
|
|
@@ -1098,6 +1099,23 @@ window.__ModuleLoader__.load({
|
|
|
1098
1099
|
const expected = defaultState();
|
|
1099
1100
|
return state.flavor === expected.flavor && state.glass.enabled === expected.glass.enabled && state.glass.mode === expected.glass.mode && state.glass.blur === expected.glass.blur && state.glass.frost === expected.glass.frost && state.glass.brightness === expected.glass.brightness;
|
|
1100
1101
|
}
|
|
1102
|
+
/** Drop the synthetic `version` field: full state → settings-document section. */
|
|
1103
|
+
function settingsSectionFromState(state) {
|
|
1104
|
+
return {
|
|
1105
|
+
flavor: state.flavor,
|
|
1106
|
+
glass: { ...state.glass }
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
/** Lift a settings-document section back into the full state-shaped contract
|
|
1110
|
+
* (re-sanitized so a hand-edited document can never poison the plugin). */
|
|
1111
|
+
function stateFromSettingsSection(section) {
|
|
1112
|
+
return sanitizeState(section);
|
|
1113
|
+
}
|
|
1114
|
+
/** Whether two settings sections are field-for-field equal (used to skip
|
|
1115
|
+
* redundant mirror writes and to recognize the Host echoing our own write). */
|
|
1116
|
+
function settingsSectionsEqual(a, b) {
|
|
1117
|
+
return a.flavor === b.flavor && a.glass.enabled === b.glass.enabled && a.glass.mode === b.glass.mode && a.glass.blur === b.glass.blur && a.glass.frost === b.glass.frost && a.glass.brightness === b.glass.brightness;
|
|
1118
|
+
}
|
|
1101
1119
|
//#endregion
|
|
1102
1120
|
//#region src/client/glass/glass-layer.ts
|
|
1103
1121
|
/** html attribute selecting the glass layer: CSS hooks and page effects. */
|
|
@@ -1927,75 +1945,98 @@ window.__ModuleLoader__.load({
|
|
|
1927
1945
|
//#endregion
|
|
1928
1946
|
//#region src/client/state-sync.ts
|
|
1929
1947
|
/**
|
|
1930
|
-
*
|
|
1931
|
-
*
|
|
1932
|
-
*
|
|
1933
|
-
* of truth; this module adds the fetch wrapper plus a debounced trailing
|
|
1934
|
-
* writer so rapid knob drags coalesce into a single PUT. Every failure is
|
|
1935
|
-
* non-fatal: the plugin keeps working from browser localStorage alone.
|
|
1948
|
+
* Bind the Catppuccin namespace on this context. The scope derives from the
|
|
1949
|
+
* shared document mirror (no wire read of its own) and its life is bound to
|
|
1950
|
+
* the calling fiber, so binding never blocks on the settings transport.
|
|
1936
1951
|
*/
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
try {
|
|
1940
|
-
const response = await fetch(STATE_ROUTE_PATH, { headers: { accept: "application/json" } });
|
|
1941
|
-
if (!response.ok) return {
|
|
1942
|
-
available: false,
|
|
1943
|
-
state: null
|
|
1944
|
-
};
|
|
1945
|
-
const payload = await response.json();
|
|
1946
|
-
if (payload.ok !== true) return {
|
|
1947
|
-
available: false,
|
|
1948
|
-
state: null
|
|
1949
|
-
};
|
|
1950
|
-
return {
|
|
1951
|
-
available: true,
|
|
1952
|
-
state: payload.state === null || payload.state === void 0 ? null : sanitizeState(payload.state)
|
|
1953
|
-
};
|
|
1954
|
-
} catch {
|
|
1955
|
-
return {
|
|
1956
|
-
available: false,
|
|
1957
|
-
state: null
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1952
|
+
function bindCatppuccinScope(ctx) {
|
|
1953
|
+
return ctx.settingsScope.bind({ namespace: CATPPUCCIN_SETTINGS_NS });
|
|
1960
1954
|
}
|
|
1961
|
-
/**
|
|
1962
|
-
|
|
1955
|
+
/** Whether the scope is usable for durable persistence: the document is
|
|
1956
|
+
* resolved (`ready`) and the Host persistence is active (`host`). A `memory`
|
|
1957
|
+
* scope (non-loopback pages / absent transport) or a not-yet-ready one is
|
|
1958
|
+
* NOT usable — the caller keeps localStorage as the only store. */
|
|
1959
|
+
function isScopeUsable(snapshot) {
|
|
1960
|
+
return snapshot.status === "ready" && snapshot.mode === "host";
|
|
1961
|
+
}
|
|
1962
|
+
/** The local state contract (with the synthetic `version`) for a usable
|
|
1963
|
+
* scope snapshot; `null` while the section is not resolved. */
|
|
1964
|
+
function durableStateFromSnapshot(snapshot) {
|
|
1965
|
+
if (!isScopeUsable(snapshot) || snapshot.value === void 0) return null;
|
|
1966
|
+
return stateFromSettingsSection(snapshot.value);
|
|
1967
|
+
}
|
|
1968
|
+
/** One atomic mutation covering the whole local state (revision-fenced).
|
|
1969
|
+
* Writing every field keeps the section self-contained — a partial write
|
|
1970
|
+
* could otherwise leave a knob stuck on a stale override after a document
|
|
1971
|
+
* edit elsewhere. */
|
|
1972
|
+
function stateToMutateOps(state) {
|
|
1973
|
+
const section = settingsSectionFromState(state);
|
|
1974
|
+
return [
|
|
1975
|
+
{
|
|
1976
|
+
op: "set",
|
|
1977
|
+
path: ["flavor"],
|
|
1978
|
+
value: section.flavor
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
op: "set",
|
|
1982
|
+
path: ["glass", "enabled"],
|
|
1983
|
+
value: section.glass.enabled
|
|
1984
|
+
},
|
|
1985
|
+
{
|
|
1986
|
+
op: "set",
|
|
1987
|
+
path: ["glass", "mode"],
|
|
1988
|
+
value: section.glass.mode
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
op: "set",
|
|
1992
|
+
path: ["glass", "blur"],
|
|
1993
|
+
value: section.glass.blur
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
op: "set",
|
|
1997
|
+
path: ["glass", "frost"],
|
|
1998
|
+
value: section.glass.frost
|
|
1999
|
+
},
|
|
2000
|
+
{
|
|
2001
|
+
op: "set",
|
|
2002
|
+
path: ["glass", "brightness"],
|
|
2003
|
+
value: section.glass.brightness
|
|
2004
|
+
}
|
|
2005
|
+
];
|
|
2006
|
+
}
|
|
2007
|
+
/** Persist the local state through the scope as one atomic mutation.
|
|
2008
|
+
* Resolves `true` when the write was issued. */
|
|
2009
|
+
async function persistStateToScope(scope, state) {
|
|
1963
2010
|
try {
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
headers: {
|
|
1967
|
-
accept: "application/json",
|
|
1968
|
-
"content-type": "application/json"
|
|
1969
|
-
},
|
|
1970
|
-
body: JSON.stringify(sanitizeState(state))
|
|
1971
|
-
})).ok;
|
|
2011
|
+
await scope.mutate(stateToMutateOps(state));
|
|
2012
|
+
return true;
|
|
1972
2013
|
} catch {
|
|
1973
2014
|
return false;
|
|
1974
2015
|
}
|
|
1975
2016
|
}
|
|
1976
2017
|
let pendingTimer;
|
|
1977
|
-
let
|
|
2018
|
+
let pendingWrite;
|
|
1978
2019
|
/**
|
|
1979
2020
|
* Debounced trailing persist: after a burst of changes (a slider drag emits a
|
|
1980
|
-
* stream of snapshot updates) the
|
|
1981
|
-
*
|
|
1982
|
-
*
|
|
2021
|
+
* stream of snapshot updates) the write happens once, a short beat after the
|
|
2022
|
+
* last change. Pass an accessor so the state snapshot is taken at flush time,
|
|
2023
|
+
* never stale.
|
|
1983
2024
|
*/
|
|
1984
|
-
function scheduleDurablePersist(
|
|
2025
|
+
function scheduleDurablePersist(write, delayMs = 300) {
|
|
1985
2026
|
if (pendingTimer !== void 0) window.clearTimeout(pendingTimer);
|
|
1986
|
-
|
|
2027
|
+
pendingWrite = write;
|
|
1987
2028
|
pendingTimer = window.setTimeout(() => {
|
|
1988
2029
|
pendingTimer = void 0;
|
|
1989
|
-
const
|
|
1990
|
-
|
|
1991
|
-
if (
|
|
2030
|
+
const run = pendingWrite;
|
|
2031
|
+
pendingWrite = void 0;
|
|
2032
|
+
if (run !== void 0) Promise.resolve().then(run).catch(() => {});
|
|
1992
2033
|
}, delayMs);
|
|
1993
2034
|
}
|
|
1994
2035
|
/** Drop any queued persist (plugin unload / page teardown). */
|
|
1995
2036
|
function cancelDurablePersist() {
|
|
1996
2037
|
if (pendingTimer !== void 0) window.clearTimeout(pendingTimer);
|
|
1997
2038
|
pendingTimer = void 0;
|
|
1998
|
-
|
|
2039
|
+
pendingWrite = void 0;
|
|
1999
2040
|
}
|
|
2000
2041
|
//#endregion
|
|
2001
2042
|
//#region \0dsh-css:/home/runner/work/dsh-catppuccin-theme/dsh-catppuccin-theme/src/client/glass/glass.module.css.mjs
|
|
@@ -2086,11 +2127,13 @@ window.__ModuleLoader__.load({
|
|
|
2086
2127
|
if (preference !== "light" && preference !== "dark") return false;
|
|
2087
2128
|
return preference === livePick;
|
|
2088
2129
|
}
|
|
2089
|
-
/** Required services: slots + locale (settings rows)
|
|
2130
|
+
/** Required services: slots + locale (settings rows), theme (register +
|
|
2131
|
+
* switch), and the settings scope (durable persistence). */
|
|
2090
2132
|
const inject = [
|
|
2091
2133
|
"slots",
|
|
2092
2134
|
"locale",
|
|
2093
|
-
"theme"
|
|
2135
|
+
"theme",
|
|
2136
|
+
"settingsScope"
|
|
2094
2137
|
];
|
|
2095
2138
|
/**
|
|
2096
2139
|
* Register the Catppuccin dictionaries, the four flavour themes, and the
|
|
@@ -2121,11 +2164,16 @@ window.__ModuleLoader__.load({
|
|
|
2121
2164
|
};
|
|
2122
2165
|
}, "catppuccin: flavour themes");
|
|
2123
2166
|
const glass = new GlassLayer(ctx);
|
|
2167
|
+
const scope = bindCatppuccinScope(ctx);
|
|
2124
2168
|
const buildLocalState = () => ({
|
|
2125
2169
|
version: 1,
|
|
2126
2170
|
flavor: readFlavor(),
|
|
2127
2171
|
glass: glass.getRemoteState()
|
|
2128
2172
|
});
|
|
2173
|
+
const persistLocal = () => {
|
|
2174
|
+
if (!isScopeUsable(scope.getSnapshot())) return;
|
|
2175
|
+
persistStateToScope(scope, buildLocalState());
|
|
2176
|
+
};
|
|
2129
2177
|
ctx.effect(() => {
|
|
2130
2178
|
let liveBuiltinPick = null;
|
|
2131
2179
|
const originalSetTheme = theme.setTheme;
|
|
@@ -2155,22 +2203,26 @@ window.__ModuleLoader__.load({
|
|
|
2155
2203
|
};
|
|
2156
2204
|
window.addEventListener("storage", onStorage);
|
|
2157
2205
|
const offGlass = glass.subscribe(() => {
|
|
2158
|
-
scheduleDurablePersist(
|
|
2206
|
+
scheduleDurablePersist(persistLocal);
|
|
2159
2207
|
});
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
if (
|
|
2164
|
-
|
|
2165
|
-
|
|
2208
|
+
const applyScopeSnapshot = () => {
|
|
2209
|
+
const snapshot = scope.getSnapshot();
|
|
2210
|
+
const state = durableStateFromSnapshot(snapshot);
|
|
2211
|
+
if (state === null) return;
|
|
2212
|
+
const local = buildLocalState();
|
|
2213
|
+
if (snapshot.user === void 0) {
|
|
2214
|
+
if (!isDefaultState(local)) scheduleDurablePersist(persistLocal);
|
|
2166
2215
|
return;
|
|
2167
2216
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2217
|
+
if (settingsSectionsEqual(settingsSectionFromState(local), settingsSectionFromState(state))) return;
|
|
2218
|
+
writeFlavor(state.flavor);
|
|
2219
|
+
glass.applyRemote(state.glass);
|
|
2170
2220
|
applyDesired();
|
|
2171
|
-
}
|
|
2221
|
+
};
|
|
2222
|
+
applyScopeSnapshot();
|
|
2223
|
+
const offScope = scope.subscribe(applyScopeSnapshot);
|
|
2172
2224
|
return () => {
|
|
2173
|
-
|
|
2225
|
+
offScope();
|
|
2174
2226
|
offGlass();
|
|
2175
2227
|
disposer();
|
|
2176
2228
|
window.removeEventListener("storage", onStorage);
|
|
@@ -2193,14 +2245,14 @@ window.__ModuleLoader__.load({
|
|
|
2193
2245
|
if (choice === "off") {
|
|
2194
2246
|
writeFlavor("off");
|
|
2195
2247
|
theme.setTheme(readRestoredPreference());
|
|
2196
|
-
scheduleDurablePersist(
|
|
2248
|
+
scheduleDurablePersist(persistLocal);
|
|
2197
2249
|
return;
|
|
2198
2250
|
}
|
|
2199
2251
|
const flavor = flavorInfo(choice);
|
|
2200
2252
|
if (!flavor) return;
|
|
2201
2253
|
writeFlavor(flavor.themeId);
|
|
2202
2254
|
theme.setTheme(flavor.themeId);
|
|
2203
|
-
scheduleDurablePersist(
|
|
2255
|
+
scheduleDurablePersist(persistLocal);
|
|
2204
2256
|
}
|
|
2205
2257
|
});
|
|
2206
2258
|
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|