dsh-dracula-theme 0.1.2 → 0.1.4

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.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ## Preview
14
14
 
15
- ![Dracula skin applied to the DSH Web GUI](assets/preview.webp)
15
+ ![Dracula skin applied to the DSH Web GUI](assets/preview.png)
16
16
 
17
17
  The classic Dracula canvas (`#282a36`) mapped onto the full DSH surface stack — sidebar, conversations, code blocks — with the purple/cyan brand accents from the official Dracula DeepSeek port.
18
18
 
@@ -56,3 +56,11 @@ The token tables are generated: edit the anchors in `palette/dracula.json` (colo
56
56
  ## License
57
57
 
58
58
  MIT — see [LICENSE](LICENSE). Structure modeled on [dsh-catppuccin](https://github.com/zhijun-dai/Catppuccin-dsh-theme) (MIT); token mapping adopted from the official [Dracula DeepSeek port](https://github.com/dracula/deepseek) (MIT).
59
+
60
+ ## Development workflow
61
+
62
+ 1. Edit the anchors in `palette/dracula.json` — colors, the official canvas/brand/code mappings, `grayRamp`, `soft.background`.
63
+ 2. `npm run generate` — rebuilds `themes/*.json` and `lib/client.js` (the browser bundle).
64
+ 3. Validate: `node scripts/check-themes.mjs` (structural token check) — CI runs the same check plus a generated-artifacts diff.
65
+ 4. Test in a live profile: `dsh plugin --profile web add /path/to/this/repo` (local link), restart the profile, pick a skin in **Settings → General → Dracula theme**.
66
+ 5. Release: bump `version` in `package.json`, commit, push, `pnpm publish`, then update the profile to the published version (`dsh plugin --profile web add dsh-dracula-theme`) and restart the app. The profile installs from npm, not from the working tree — local edits only take effect after publishing or via a local link.
package/README.zh.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ## 预览
14
14
 
15
- ![德古拉主题应用在 DSH Web GUI 上的效果](assets/preview.webp)
15
+ ![德古拉主题应用在 DSH Web GUI 上的效果](assets/preview.png)
16
16
 
17
17
  经典德古拉画布(`#282a36`)映射到 DSH 全部界面层 —— 侧边栏、会话、代码块,品牌点缀色取自官方 Dracula DeepSeek 移植版的紫/青配色。
18
18
 
@@ -55,3 +55,11 @@ token 表是生成出来的:修改 `palette/dracula.json` 里的锚点(颜
55
55
  ## 许可
56
56
 
57
57
  MIT —— 见 [LICENSE](LICENSE)。结构参考 [dsh-catppuccin](https://github.com/zhijun-dai/Catppuccin-dsh-theme)(MIT)。
58
+
59
+ ## 开发工作流
60
+
61
+ 1. 修改 `palette/dracula.json` 里的锚点——颜色、官方 canvas/brand/代码映射、`grayRamp`、`soft.background`。
62
+ 2. `npm run generate` —— 重新生成 `themes/*.json` 与 `lib/client.js`(浏览器 bundle)。
63
+ 3. 校验:`node scripts/check-themes.mjs`(token 结构检查)——CI 会跑同样的检查,外加生成产物 diff。
64
+ 4. 在真实 profile 里测试:`dsh plugin --profile web add /path/to/this/repo`(本地 link),重启 profile,在**设置 → 通用 → 德古拉主题**里选择皮肤。
65
+ 5. 发版:`package.json` 升 `version`,提交推送,`pnpm publish`,再把 profile 更新到已发布版本(`dsh plugin --profile web add dsh-dracula-theme`)并重启应用。profile 从 npm 安装,不走工作区——本地改动只有发布后(或通过本地 link)才会生效。
Binary file
package/lib/client.js CHANGED
@@ -45,7 +45,9 @@ window.__ModuleLoader__.load({
45
45
  "tokens": {
46
46
  "--dsw-static-neutral-50": "#efefe9",
47
47
  "--dsw-static-neutral-100": "#e2e2da",
48
+ "--dsw-static-neutral-150": "#d8d8d0",
48
49
  "--dsw-static-neutral-200": "#cdcdc5",
50
+ "--dsw-static-neutral-250": "#c2c2ba",
49
51
  "--dsw-static-neutral-300": "#b6b6ae",
50
52
  "--dsw-static-neutral-400": "#8f8f96",
51
53
  "--dsw-static-neutral-500": "#6d6d7a",
@@ -56,7 +58,6 @@ window.__ModuleLoader__.load({
56
58
  "--dsw-static-neutral-850": "#282a36",
57
59
  "--dsw-static-neutral-900": "#20212b",
58
60
  "--dsw-static-neutral-1000": "#191a21",
59
- "--dsw-static-neutral-description": "Anchor stops of the neutral grayscale ramp (--dsw-static-neutral-*), lightest (00) to darkest (1000). The official port does not override this ramp; values are Dracula-gray interpolations for a self-contained theme.",
60
61
  "--dsw-static-neutral-00": "#f8f8f2",
61
62
  "--dsw-static-neutral-bluish-50": "#f8f8f2",
62
63
  "--dsw-static-neutral-bluish-60": "#282a36",
@@ -89,7 +90,6 @@ window.__ModuleLoader__.load({
89
90
  "--dsw-static-deepseek-600": "#7a5abf",
90
91
  "--dsw-static-deepseek-800": "#44475a",
91
92
  "--dsw-static-deepseek-900": "#383a4a",
92
- "--dsw-static-deepseek-description": "--dsw-static-deepseek-* overrides from the official port. 400 link / brand-text → cyan, 450 brand-primary → purple, 500 hover.",
93
93
  "--dsw-static-deepseek-700-delete": "#5a3e99",
94
94
  "--dsw-static-blue-50": "#eafcff",
95
95
  "--dsw-static-blue-75": "#c6f3fd",
@@ -208,7 +208,6 @@ window.__ModuleLoader__.load({
208
208
  "--dsw-specific-menu": "#2e3040",
209
209
  "--dsw-specific-selector": "#44475a",
210
210
  "--dsw-specific-tip": "#2e3040",
211
- "--shiki-description": "The official Prism token mapping, translated to the DSH shiki vocabulary: comments #6272a4, punctuation #f8f8f2, tags/parameters/this → cyan, numbers/constants/strings/inserted → yellow, keywords/operators/builtins/regex → pink, functions → green, deleted/important → red.",
212
211
  "--shiki-foreground": "#f8f8f2",
213
212
  "--shiki-background": "#282a36",
214
213
  "--shiki-token-comment": "#6272a4",
@@ -234,7 +233,9 @@ window.__ModuleLoader__.load({
234
233
  "tokens": {
235
234
  "--dsw-static-neutral-50": "#efefe9",
236
235
  "--dsw-static-neutral-100": "#e2e2da",
236
+ "--dsw-static-neutral-150": "#d8d8d0",
237
237
  "--dsw-static-neutral-200": "#cdcdc5",
238
+ "--dsw-static-neutral-250": "#c2c2ba",
238
239
  "--dsw-static-neutral-300": "#b6b6ae",
239
240
  "--dsw-static-neutral-400": "#8f8f96",
240
241
  "--dsw-static-neutral-500": "#6d6d7a",
@@ -245,7 +246,6 @@ window.__ModuleLoader__.load({
245
246
  "--dsw-static-neutral-850": "#282a36",
246
247
  "--dsw-static-neutral-900": "#20212b",
247
248
  "--dsw-static-neutral-1000": "#191a21",
248
- "--dsw-static-neutral-description": "Anchor stops of the neutral grayscale ramp (--dsw-static-neutral-*), lightest (00) to darkest (1000). The official port does not override this ramp; values are Dracula-gray interpolations for a self-contained theme.",
249
249
  "--dsw-static-neutral-00": "#f8f8f2",
250
250
  "--dsw-static-neutral-bluish-50": "#f8f8f2",
251
251
  "--dsw-static-neutral-bluish-60": "#282a36",
@@ -278,7 +278,6 @@ window.__ModuleLoader__.load({
278
278
  "--dsw-static-deepseek-600": "#7a5abf",
279
279
  "--dsw-static-deepseek-800": "#44475a",
280
280
  "--dsw-static-deepseek-900": "#383a4a",
281
- "--dsw-static-deepseek-description": "--dsw-static-deepseek-* overrides from the official port. 400 link / brand-text → cyan, 450 brand-primary → purple, 500 hover.",
282
281
  "--dsw-static-deepseek-700-delete": "#5a3e99",
283
282
  "--dsw-static-blue-50": "#eafcff",
284
283
  "--dsw-static-blue-75": "#c6f3fd",
@@ -397,7 +396,6 @@ window.__ModuleLoader__.load({
397
396
  "--dsw-specific-menu": "#343746",
398
397
  "--dsw-specific-selector": "#44475a",
399
398
  "--dsw-specific-tip": "#343746",
400
- "--shiki-description": "The official Prism token mapping, translated to the DSH shiki vocabulary: comments #6272a4, punctuation #f8f8f2, tags/parameters/this → cyan, numbers/constants/strings/inserted → yellow, keywords/operators/builtins/regex → pink, functions → green, deleted/important → red.",
401
399
  "--shiki-foreground": "#f8f8f2",
402
400
  "--shiki-background": "#282a36",
403
401
  "--shiki-token-comment": "#6272a4",
@@ -801,51 +799,59 @@ window.__ModuleLoader__.load({
801
799
  style.remove();
802
800
  }, "dsh-dracula-theme: surface tint lifecycle");
803
801
 
804
- // Restore the saved skin against the boot adoption storm. The
805
- // ThemeService adopts its durable built-in preference
806
- // ("light"/"dark"/"system") from the Host settings scope
807
- // asynchronously after boot; every adoption emits theme/change
808
- // with a built-in preference that overwrites a third-party one.
809
- // In slower boot paths the desktop client's RPC bridge emits
810
- // more, later sync events a fixed retry budget (a handful of
811
- // tries within a few seconds) can be exhausted and the theme
812
- // silently reverts to default. So instead of a time-boxed guard
813
- // we stay armed while the storm is active: re-assert the saved
814
- // skin on every built-in revert, and only yield to user actions
815
- // once the preference has rested on the saved skin for a quiet
816
- // period with no further adoption events.
802
+ // Restore the saved skin and hold it against the theme runtime's
803
+ // built-in preference. The ThemeService adopts its durable
804
+ // built-in preference ("light"/"dark"/"system") from the Host
805
+ // settings scope; every adoption emits theme/change with a
806
+ // built-in preference that overwrites a third-party one. That can
807
+ // happen not only in a boot storm, but ANY time the settings
808
+ // document is re-synced mid-session (another client touching the
809
+ // shared settings.yaml is enough) which silently reverts the
810
+ // UI to the built-in appearance without any user action.
811
+ //
812
+ // Strategy: track the last observed durable value. A built-in
813
+ // preference equal to it is an adoption echo of an unchanged
814
+ // document re-assert the saved skin. A different value is a
815
+ // real user change of the Appearance row (or the first boot
816
+ // adoption, within a short boot window) — the first sighting
817
+ // within the boot window re-asserts, any later one is respected
818
+ // and drops our stored choice, so the user's explicit
819
+ // appearance choice always wins afterwards.
817
820
  const saved = readSavedSkin();
818
- const savedValid = typeof saved === "string" && saved !== DEFAULT_SKIN && SKINS.some((skinDefinition) => skinDefinition.id === saved);
819
- let armed = savedValid;
820
- let quietTimer;
821
- const disarm = () => {
822
- armed = false;
823
- };
824
- const scheduleDisarm = () => {
825
- clearTimeout(quietTimer);
826
- quietTimer = setTimeout(disarm, 4000);
821
+ let savedValid = typeof saved === "string" && saved !== DEFAULT_SKIN && SKINS.some((skinDefinition) => skinDefinition.id === saved);
822
+ let lastSeenDurable = null;
823
+ const bootUntil = Date.now() + 15000;
824
+
825
+ /** Re-assert the saved skin from a fresh task (see the dispatch note below). */
826
+ const reassertSaved = () => {
827
+ if (!savedValid) return;
828
+ setTimeout(() => {
829
+ const pref = ctx.theme.getTheme().preference;
830
+ if (pref !== saved) ctx.theme.setTheme(saved);
831
+ }, 0);
827
832
  };
828
- const reassertAgainstAdoption = () => {
829
- if (!armed) return;
830
- const pref = ctx.theme.getTheme().preference;
831
- if (pref === saved) {
832
- scheduleDisarm();
833
+ /** Handle a built-in preference (light/dark/system) sighting. */
834
+ const considerBuiltin = (pref) => {
835
+ if (pref === lastSeenDurable) {
836
+ // Adoption echo: the document was re-synced with the same
837
+ // durable value. Hold the skin.
838
+ reassertSaved();
833
839
  return;
834
840
  }
835
- if (pref === "light" || pref === "dark" || pref === DEFAULT_SKIN) {
836
- // A built-in won — during boot this can only be a scope
837
- // adoption (nobody reaches the Appearance row that fast).
838
- // Re-assert, then require another quiet period.
839
- ctx.theme.setTheme(saved);
840
- scheduleDisarm();
841
+ lastSeenDurable = pref;
842
+ if (Date.now() <= bootUntil) {
843
+ // First sighting inside the boot window the boot
844
+ // adoption arriving. Restore the saved skin.
845
+ reassertSaved();
846
+ return;
841
847
  }
842
- // Another third-party theme the drop-foreign convention
843
- // below clears our stored choice instead of fighting.
848
+ // A durable change after boot is a real user action on the
849
+ // Appearance row: respect it and drop our stored choice so
850
+ // later echoes of the same value cannot resurrect the skin.
851
+ writeSavedSkin(DEFAULT_SKIN);
852
+ savedValid = false;
844
853
  };
845
- reassertAgainstAdoption();
846
- ctx.effect(() => () => {
847
- clearTimeout(quietTimer);
848
- }, "dsh-dracula-theme: boot restore lifecycle");
854
+ considerBuiltin(ctx.theme.getTheme().preference);
849
855
 
850
856
  const skinStore = createSkinStore();
851
857
  let skinBound;
@@ -854,19 +860,18 @@ window.__ModuleLoader__.load({
854
860
  };
855
861
  ctx.on("theme/change", (snapshot) => {
856
862
  syncSkin(snapshot);
863
+ const pref = snapshot.preference;
864
+ if (pref === DEFAULT_SKIN || pref === "light" || pref === "dark") {
865
+ considerBuiltin(pref);
866
+ return;
867
+ }
857
868
  // If the preference moved to another plugin's third-party theme,
858
869
  // drop our stored choice so only the last-picked plugin restores
859
870
  // at boot (both plugins must implement this convention).
860
- const pref = snapshot.preference;
861
- if (pref !== DEFAULT_SKIN && pref !== "light" && pref !== "dark" && !SKINS.some((skinDefinition) => skinDefinition.id === pref)) {
871
+ if (!SKINS.some((skinDefinition) => skinDefinition.id === pref)) {
862
872
  writeSavedSkin(DEFAULT_SKIN);
873
+ savedValid = false;
863
874
  }
864
- // Re-assert from a fresh task: a re-entrant setTheme inside the
865
- // dispatch is missed by other subscribers (ui-layout's
866
- // ThemePresenter), so the restored skin would never reach the DOM.
867
- setTimeout(() => {
868
- reassertAgainstAdoption();
869
- }, 0);
870
875
  });
871
876
 
872
877
  ctx.effect(() => ctx.locale.register(SETTINGS_NS, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-dracula-theme",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "🧛 Dracula theme for DeepSeek Harness: the classic dark palette (plus the Soft variant) registered into the built-in theme runtime",
5
5
  "license": "MIT",
6
6
  "author": "ossFrankFrank",
@@ -29,7 +29,8 @@
29
29
  "lib/index.js",
30
30
  "lib/client.js",
31
31
  "cordis.patch.yml",
32
- "themes"
32
+ "themes",
33
+ "assets"
33
34
  ],
34
35
  "dsh": {
35
36
  "bundle": {
@@ -5,7 +5,9 @@
5
5
  "tokens": {
6
6
  "--dsw-static-neutral-50": "#efefe9",
7
7
  "--dsw-static-neutral-100": "#e2e2da",
8
+ "--dsw-static-neutral-150": "#d8d8d0",
8
9
  "--dsw-static-neutral-200": "#cdcdc5",
10
+ "--dsw-static-neutral-250": "#c2c2ba",
9
11
  "--dsw-static-neutral-300": "#b6b6ae",
10
12
  "--dsw-static-neutral-400": "#8f8f96",
11
13
  "--dsw-static-neutral-500": "#6d6d7a",
@@ -16,7 +18,6 @@
16
18
  "--dsw-static-neutral-850": "#282a36",
17
19
  "--dsw-static-neutral-900": "#20212b",
18
20
  "--dsw-static-neutral-1000": "#191a21",
19
- "--dsw-static-neutral-description": "Anchor stops of the neutral grayscale ramp (--dsw-static-neutral-*), lightest (00) to darkest (1000). The official port does not override this ramp; values are Dracula-gray interpolations for a self-contained theme.",
20
21
  "--dsw-static-neutral-00": "#f8f8f2",
21
22
  "--dsw-static-neutral-bluish-50": "#f8f8f2",
22
23
  "--dsw-static-neutral-bluish-60": "#282a36",
@@ -49,7 +50,6 @@
49
50
  "--dsw-static-deepseek-600": "#7a5abf",
50
51
  "--dsw-static-deepseek-800": "#44475a",
51
52
  "--dsw-static-deepseek-900": "#383a4a",
52
- "--dsw-static-deepseek-description": "--dsw-static-deepseek-* overrides from the official port. 400 link / brand-text → cyan, 450 brand-primary → purple, 500 hover.",
53
53
  "--dsw-static-deepseek-700-delete": "#5a3e99",
54
54
  "--dsw-static-blue-50": "#eafcff",
55
55
  "--dsw-static-blue-75": "#c6f3fd",
@@ -168,7 +168,6 @@
168
168
  "--dsw-specific-menu": "#343746",
169
169
  "--dsw-specific-selector": "#44475a",
170
170
  "--dsw-specific-tip": "#343746",
171
- "--shiki-description": "The official Prism token mapping, translated to the DSH shiki vocabulary: comments #6272a4, punctuation #f8f8f2, tags/parameters/this → cyan, numbers/constants/strings/inserted → yellow, keywords/operators/builtins/regex → pink, functions → green, deleted/important → red.",
172
171
  "--shiki-foreground": "#f8f8f2",
173
172
  "--shiki-background": "#282a36",
174
173
  "--shiki-token-comment": "#6272a4",
@@ -5,7 +5,9 @@
5
5
  "tokens": {
6
6
  "--dsw-static-neutral-50": "#efefe9",
7
7
  "--dsw-static-neutral-100": "#e2e2da",
8
+ "--dsw-static-neutral-150": "#d8d8d0",
8
9
  "--dsw-static-neutral-200": "#cdcdc5",
10
+ "--dsw-static-neutral-250": "#c2c2ba",
9
11
  "--dsw-static-neutral-300": "#b6b6ae",
10
12
  "--dsw-static-neutral-400": "#8f8f96",
11
13
  "--dsw-static-neutral-500": "#6d6d7a",
@@ -16,7 +18,6 @@
16
18
  "--dsw-static-neutral-850": "#282a36",
17
19
  "--dsw-static-neutral-900": "#20212b",
18
20
  "--dsw-static-neutral-1000": "#191a21",
19
- "--dsw-static-neutral-description": "Anchor stops of the neutral grayscale ramp (--dsw-static-neutral-*), lightest (00) to darkest (1000). The official port does not override this ramp; values are Dracula-gray interpolations for a self-contained theme.",
20
21
  "--dsw-static-neutral-00": "#f8f8f2",
21
22
  "--dsw-static-neutral-bluish-50": "#f8f8f2",
22
23
  "--dsw-static-neutral-bluish-60": "#282a36",
@@ -49,7 +50,6 @@
49
50
  "--dsw-static-deepseek-600": "#7a5abf",
50
51
  "--dsw-static-deepseek-800": "#44475a",
51
52
  "--dsw-static-deepseek-900": "#383a4a",
52
- "--dsw-static-deepseek-description": "--dsw-static-deepseek-* overrides from the official port. 400 link / brand-text → cyan, 450 brand-primary → purple, 500 hover.",
53
53
  "--dsw-static-deepseek-700-delete": "#5a3e99",
54
54
  "--dsw-static-blue-50": "#eafcff",
55
55
  "--dsw-static-blue-75": "#c6f3fd",
@@ -168,7 +168,6 @@
168
168
  "--dsw-specific-menu": "#2e3040",
169
169
  "--dsw-specific-selector": "#44475a",
170
170
  "--dsw-specific-tip": "#2e3040",
171
- "--shiki-description": "The official Prism token mapping, translated to the DSH shiki vocabulary: comments #6272a4, punctuation #f8f8f2, tags/parameters/this → cyan, numbers/constants/strings/inserted → yellow, keywords/operators/builtins/regex → pink, functions → green, deleted/important → red.",
172
171
  "--shiki-foreground": "#f8f8f2",
173
172
  "--shiki-background": "#282a36",
174
173
  "--shiki-token-comment": "#6272a4",