dsh-theme-escook 1.0.4 → 1.0.6

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/client/client.js CHANGED
@@ -1,346 +1,90 @@
1
- /**
2
- * Escook Theme Client Plugin for DSH (Redesigned Edition)
3
- */
1
+ import { THEME_SCHEMES } from './palette.js';
2
+ import { ThemeSettings, SETTINGS_CSS } from './theme-settings.js';
4
3
 
5
- export const THEME_SCHEMES = {
6
- "dark": {
7
- name: "escook Dark (经典暗黑 · 标志暖阳橙)",
8
- desc: "VS Code 彬哥经典暖调极客深灰搭配标志暖阳橙,正统耐看长效护眼",
9
- type: "dark",
10
- colorPreview: "#ef820c",
11
- bgPreview: "#252526",
12
- css: `
13
- :root, html, body, body[data-ds-dark-theme], [data-theme="escook-dark"] {
14
- --dsw-alias-bg-base: #252526 !important;
15
- --dsw-alias-bg-layer-1: #202021 !important;
16
- --dsw-alias-bg-layer-2: #29292c !important;
17
- --dsw-alias-bg-layer-3: #2d2d30 !important;
18
- --dsw-alias-bg-module-platform: #2d2d30 !important;
19
- --dsw-alias-bg-multi-select: #2d2d30 !important;
20
- --dsw-alias-bg-overlay: #333333 !important;
21
- --dsw-specific-selector: #2d2d30 !important;
22
- --dsw-alias-bg-mask-1: rgba(18, 18, 20, 0.75) !important;
23
- --dsw-specific-sidebar-fill: #202021 !important;
24
- --dsw-specific-sidebar-nav-item-hover: rgba(239, 130, 12, 0.1) !important;
25
- --dsw-specific-sidebar-nav-item-active: rgba(239, 130, 12, 0.2) !important;
26
- --dsw-specific-sidebar-nav-item-active-accent: #ef820c !important;
27
- --dsw-specific-input-major: #29292c !important;
28
- --dsw-specific-bubble: #29292c !important;
29
- --dsw-specific-menu: #202021 !important;
30
- --dsw-hovercard-bg: #29292c !important;
31
- --dsw-alias-tooltip-bg: #1c1c1d !important;
32
- --dsw-alias-button-elevated-fill: #2f2f33 !important;
33
- --dsw-alias-button-floating-hover: #38383c !important;
34
- --dsw-alias-brand-primary: #ef820c !important;
35
- --dsw-alias-button-primary-fill: #ef820c !important;
36
- --dsw-alias-button-primary-hover: #ff9940 !important;
37
- --dsw-alias-label-primary-foreground: #ffffff !important;
38
- --dsw-alias-label-primary: #fafafa !important;
39
- --dsw-alias-label-secondary: #cccccc !important;
40
- --dsw-alias-label-tertiary: #888888 !important;
41
- --dsw-alias-label-dimmed: #555555 !important;
42
- --dsw-alias-label-caption: #888888 !important;
43
- --dsw-alias-interactive-bg-hover: rgba(239, 130, 12, 0.1) !important;
44
- --dsw-alias-interactive-bg-active: rgba(239, 130, 12, 0.2) !important;
45
- --dsw-alias-border-l1: #333333 !important;
46
- --dsw-alias-border-l2: #2a2a2a !important;
47
- --dsw-alias-border-l3: #3e3e3e !important;
48
- --dsw-alias-border-l4: #555555 !important;
49
- --dsw-alias-markdown-code-block: #1c1c1d !important;
50
- --dsw-alias-markdown-code-block-banner: #252526 !important;
51
- --dsw-alias-scrollbar-bg-l2: #333333 !important;
52
- --dsw-alias-scrollbar-hover-l2: #ef820c !important;
53
- }
54
- body, body[data-ds-dark-theme] { background-color: #252526 !important; color: #fafafa !important; }
55
- pre, code, [class*="codeBlock"] {
56
- background-color: #1c1c1d !important;
57
- color: #fafafa !important;
58
- border-color: #333333 !important;
59
- }
60
- [class*="selector"], [class*="themeCube"] {
61
- color: #fafafa !important;
62
- background-color: #2d2d30 !important;
63
- }
64
- [class*="selected"], [class*="themeCube"]:hover {
65
- border-color: #ef820c !important;
66
- }
67
- `
68
- },
69
- "dark-soft": {
70
- name: "escook Dark Soft (柔和暗黑 · 柔光奶杏黄)",
71
- desc: "Ayu 经典深海蓝灰底色搭配温润奶杏黄,细腻柔和长效防疲劳",
72
- type: "dark",
73
- colorPreview: "#ffcc66",
74
- bgPreview: "#1f2430",
75
- css: `
76
- :root, html, body, body[data-ds-dark-theme], [data-theme="escook-dark-soft"] {
77
- --dsw-alias-bg-base: #1f2430 !important;
78
- --dsw-alias-bg-layer-1: #191e28 !important;
79
- --dsw-alias-bg-layer-2: #232834 !important;
80
- --dsw-alias-bg-layer-3: #262c3b !important;
81
- --dsw-alias-bg-module-platform: #262c3b !important;
82
- --dsw-alias-bg-multi-select: #262c3b !important;
83
- --dsw-alias-bg-overlay: #333a4c !important;
84
- --dsw-specific-selector: #262c3b !important;
85
- --dsw-alias-bg-mask-1: rgba(16, 20, 28, 0.7) !important;
86
- --dsw-specific-sidebar-fill: #191e28 !important;
87
- --dsw-specific-sidebar-nav-item-hover: rgba(255, 204, 102, 0.1) !important;
88
- --dsw-specific-sidebar-nav-item-active: rgba(255, 204, 102, 0.18) !important;
89
- --dsw-specific-sidebar-nav-item-active-accent: #ffcc66 !important;
90
- --dsw-specific-input-major: #232834 !important;
91
- --dsw-specific-bubble: #232834 !important;
92
- --dsw-specific-menu: #191e28 !important;
93
- --dsw-hovercard-bg: #191e28 !important;
94
- --dsw-alias-tooltip-bg: #171b24 !important;
95
- --dsw-alias-button-elevated-fill: #2b3140 !important;
96
- --dsw-alias-button-floating-hover: #333a4c !important;
97
- --dsw-alias-brand-primary: #ffcc66 !important;
98
- --dsw-alias-button-primary-fill: #ffcc66 !important;
99
- --dsw-alias-button-primary-hover: #ffd580 !important;
100
- --dsw-alias-label-primary-foreground: #1f2430 !important;
101
- --dsw-alias-label-primary: #cbccc6 !important;
102
- --dsw-alias-label-secondary: #969aa4 !important;
103
- --dsw-alias-label-tertiary: #707a8c !important;
104
- --dsw-alias-label-dimmed: #515764 !important;
105
- --dsw-alias-label-caption: #707a8c !important;
106
- --dsw-alias-interactive-bg-hover: rgba(255, 204, 102, 0.1) !important;
107
- --dsw-alias-interactive-bg-active: rgba(255, 204, 102, 0.18) !important;
108
- --dsw-alias-border-l1: #373e4c !important;
109
- --dsw-alias-border-l2: #2d3340 !important;
110
- --dsw-alias-border-l3: #444c5e !important;
111
- --dsw-alias-border-l4: #515764 !important;
112
- --dsw-alias-markdown-code-block: #171b24 !important;
113
- --dsw-alias-markdown-code-block-banner: #1f2430 !important;
114
- --dsw-alias-scrollbar-bg-l2: #373e4c !important;
115
- --dsw-alias-scrollbar-hover-l2: #ffcc66 !important;
116
- }
117
- body, body[data-ds-dark-theme] { background-color: #1f2430 !important; color: #cbccc6 !important; }
118
- pre, code, [class*="codeBlock"] {
119
- background-color: #171b24 !important;
120
- color: #cbccc6 !important;
121
- border-color: #373e4c !important;
122
- }
123
- [class*="selector"], [class*="themeCube"] {
124
- color: #cbccc6 !important;
125
- background-color: #262c3b !important;
126
- }
127
- [class*="selected"], [class*="themeCube"]:hover {
128
- border-color: #ffcc66 !important;
129
- }
130
- `
131
- },
132
- "light": {
133
- name: "escook Light (经典浅色 · 典雅紫罗兰)",
134
- desc: "Solarized 经典护眼暖米白搭配典雅紫罗兰与青墨文本,温润纸质书卷感",
135
- type: "light",
136
- colorPreview: "#705697",
137
- bgPreview: "#fdf6e3",
138
- css: `
139
- :root, html, body, body[data-ds-dark-theme], [data-theme="escook-light"] {
140
- --dsw-alias-bg-base: #fdf6e3 !important;
141
- --dsw-alias-bg-layer-1: #f8f0d8 !important;
142
- --dsw-alias-bg-layer-2: #ffffff !important;
143
- --dsw-alias-bg-layer-3: #eee8d5 !important;
144
- --dsw-alias-bg-module-platform: #eee8d5 !important;
145
- --dsw-alias-bg-multi-select: #eee8d5 !important;
146
- --dsw-alias-bg-overlay: #e3dac6 !important;
147
- --dsw-specific-selector: #eee8d5 !important;
148
- --dsw-alias-bg-mask-1: rgba(50, 40, 60, 0.25) !important;
149
- --dsw-specific-sidebar-fill: #f8f0d8 !important;
150
- --dsw-specific-sidebar-nav-item-hover: rgba(112, 86, 151, 0.08) !important;
151
- --dsw-specific-sidebar-nav-item-active: rgba(112, 86, 151, 0.16) !important;
152
- --dsw-specific-sidebar-nav-item-active-accent: #705697 !important;
153
- --dsw-specific-input-major: #ffffff !important;
154
- --dsw-specific-bubble: #ffffff !important;
155
- --dsw-specific-menu: #ffffff !important;
156
- --dsw-hovercard-bg: #ffffff !important;
157
- --dsw-alias-tooltip-bg: #2b2638 !important;
158
- --dsw-alias-button-elevated-fill: #f4ecce !important;
159
- --dsw-alias-button-floating-hover: #ece2c4 !important;
160
- --dsw-alias-brand-primary: #705697 !important;
161
- --dsw-alias-button-primary-fill: #705697 !important;
162
- --dsw-alias-button-primary-hover: #876cad !important;
163
- --dsw-alias-label-primary-foreground: #ffffff !important;
164
- --dsw-alias-label-primary: #586e75 !important;
165
- --dsw-alias-label-secondary: #657b83 !important;
166
- --dsw-alias-label-tertiary: #93a1a1 !important;
167
- --dsw-alias-label-dimmed: #b58900 !important;
168
- --dsw-alias-label-caption: #93a1a1 !important;
169
- --dsw-alias-interactive-bg-hover: rgba(112, 86, 151, 0.08) !important;
170
- --dsw-alias-interactive-bg-active: rgba(112, 86, 151, 0.15) !important;
171
- --dsw-alias-border-l1: #e3dac6 !important;
172
- --dsw-alias-border-l2: #ece3cf !important;
173
- --dsw-alias-border-l3: #d8ceb8 !important;
174
- --dsw-alias-border-l4: #b58900 !important;
175
- --dsw-alias-markdown-code-block: #f5eed8 !important;
176
- --dsw-alias-markdown-code-block-banner: #ebe4cc !important;
177
- --dsw-alias-scrollbar-bg-l2: #e3dac6 !important;
178
- --dsw-alias-scrollbar-hover-l2: #705697 !important;
179
- }
180
- body, body[data-ds-dark-theme] { background-color: #fdf6e3 !important; color: #586e75 !important; }
181
- pre, code, [class*="codeBlock"] {
182
- background-color: #f5eed8 !important;
183
- color: #586e75 !important;
184
- border-color: #e3dac6 !important;
185
- }
186
- [class*="selector"], [class*="themeCube"] {
187
- color: #586e75 !important;
188
- background-color: #eee8d5 !important;
189
- }
190
- [class*="selected"], [class*="themeCube"]:hover {
191
- border-color: #705697 !important;
192
- }
193
- `
194
- },
195
- "light-soft": {
196
- name: "escook Light Soft (柔和浅色 · 活力柔和橙)",
197
- desc: "现代极简清透浅灰搭配柔和活力橙,明亮清爽不刺眼",
198
- type: "light",
199
- colorPreview: "#ff9940",
200
- bgPreview: "#fafafa",
201
- css: `
202
- :root, html, body, body[data-ds-dark-theme], [data-theme="escook-light-soft"] {
203
- --dsw-alias-bg-base: #fafafa !important;
204
- --dsw-alias-bg-layer-1: #f2f2f2 !important;
205
- --dsw-alias-bg-layer-2: #ffffff !important;
206
- --dsw-alias-bg-layer-3: #e8e8e8 !important;
207
- --dsw-alias-bg-module-platform: #e8e8e8 !important;
208
- --dsw-alias-bg-multi-select: #e8e8e8 !important;
209
- --dsw-alias-bg-overlay: #dcdcdc !important;
210
- --dsw-specific-selector: #e8e8e8 !important;
211
- --dsw-alias-bg-mask-1: rgba(0, 0, 0, 0.2) !important;
212
- --dsw-specific-sidebar-fill: #f2f2f2 !important;
213
- --dsw-specific-sidebar-nav-item-hover: rgba(255, 153, 64, 0.08) !important;
214
- --dsw-specific-sidebar-nav-item-active: rgba(255, 153, 64, 0.16) !important;
215
- --dsw-specific-sidebar-nav-item-active-accent: #ff9940 !important;
216
- --dsw-specific-input-major: #ffffff !important;
217
- --dsw-specific-bubble: #ffffff !important;
218
- --dsw-specific-menu: #ffffff !important;
219
- --dsw-hovercard-bg: #ffffff !important;
220
- --dsw-alias-tooltip-bg: #2d3748 !important;
221
- --dsw-alias-button-elevated-fill: #eaeaea !important;
222
- --dsw-alias-button-floating-hover: #e0e0e0 !important;
223
- --dsw-alias-brand-primary: #ff9940 !important;
224
- --dsw-alias-button-primary-fill: #ff9940 !important;
225
- --dsw-alias-button-primary-hover: #f58220 !important;
226
- --dsw-alias-label-primary-foreground: #ffffff !important;
227
- --dsw-alias-label-primary: #2d3748 !important;
228
- --dsw-alias-label-secondary: #4a5568 !important;
229
- --dsw-alias-label-tertiary: #718096 !important;
230
- --dsw-alias-label-dimmed: #a0aec0 !important;
231
- --dsw-alias-label-caption: #718096 !important;
232
- --dsw-alias-interactive-bg-hover: rgba(255, 153, 64, 0.08) !important;
233
- --dsw-alias-interactive-bg-active: rgba(255, 153, 64, 0.15) !important;
234
- --dsw-alias-border-l1: #dcdcdc !important;
235
- --dsw-alias-border-l2: #e8e8e8 !important;
236
- --dsw-alias-border-l3: #d0d0d0 !important;
237
- --dsw-alias-border-l4: #a0aec0 !important;
238
- --dsw-alias-markdown-code-block: #f0f2f5 !important;
239
- --dsw-alias-markdown-code-block-banner: #e6e9ee !important;
240
- --dsw-alias-scrollbar-bg-l2: #dcdcdc !important;
241
- --dsw-alias-scrollbar-hover-l2: #ff9940 !important;
242
- }
243
- body, body[data-ds-dark-theme] { background-color: #fafafa !important; color: #2d3748 !important; }
244
- pre, code, [class*="codeBlock"] {
245
- background-color: #f0f2f5 !important;
246
- color: #2d3748 !important;
247
- border-color: #dcdcdc !important;
248
- }
249
- [class*="selector"], [class*="themeCube"] {
250
- color: #2d3748 !important;
251
- background-color: #e8e8e8 !important;
252
- }
253
- [class*="selected"], [class*="themeCube"]:hover {
254
- border-color: #ff9940 !important;
255
- }
256
- `
257
- }
258
- };
259
-
260
- export default function activate(context) {
261
- // 环境感知与防冲突协同:若宿主环境为 DSH Desktop 客户端且已原生内置主题,自动注册最新主题并委派托管
262
- if (typeof window !== "undefined" && window.__DSH_BUILTIN_THEMES__) {
263
- console.info("🌸 [dsh-theme-escook] 检测到当前处于 DSH Desktop 桌面端,正在将插件最新主题矩阵热同步至客户端...");
264
- const builtin = window.__DSH_BUILTIN_THEMES__;
265
- if (typeof builtin.registerThemes === "function") {
266
- builtin.registerThemes(THEME_SCHEMES);
267
- }
268
- const themeController = {
269
- schemes: Object.keys(THEME_SCHEMES),
270
- setScheme(key) {
271
- builtin.apply(key);
272
- },
273
- getCurrentScheme() {
274
- return builtin.getCurrent();
275
- },
276
- dispose() {
277
- // 由桌面端原生管理生命周期
278
- if (window.__ESCOOK_THEME__ === themeController) {
279
- delete window.__ESCOOK_THEME__;
280
- }
281
- }
282
- };
283
- window.__ESCOOK_THEME__ = themeController;
284
- return themeController;
285
- }
286
-
287
- const styleId = "dsh-theme-escook-styles";
288
- const storageKey = "dsh_theme_escook_scheme";
289
- let styleEl = document.getElementById(styleId);
290
-
291
- function applyScheme(key) {
292
- const validKey = THEME_SCHEMES[key] ? key : "dark-soft";
293
- const scheme = THEME_SCHEMES[validKey];
294
-
295
- if (!styleEl) {
296
- styleEl = document.createElement("style");
297
- styleEl.id = styleId;
298
- document.head.appendChild(styleEl);
299
- }
300
- styleEl.textContent = scheme.css;
301
- document.documentElement.setAttribute("data-dsh-theme", `escook-${validKey}`);
302
- try {
303
- localStorage.setItem(storageKey, validKey);
304
- } catch (e) {}
305
- console.info(`🌸 [dsh-theme-escook] 当前激活配色: ${scheme.name}`);
306
- }
307
-
308
- let savedScheme = "dark-soft";
309
- try {
310
- savedScheme = localStorage.getItem(storageKey) || localStorage.getItem("dsh_selected_theme") || "dark-soft";
311
- } catch (e) {}
312
-
313
- applyScheme(savedScheme);
4
+ export { THEME_SCHEMES };
5
+ export const inject = ['theme', 'slots'];
6
+ const STORAGE_KEY = 'dsh_theme_escook_scheme';
314
7
 
315
- const themeController = {
8
+ function activate(ctx) {
9
+ const cleanups = [];
10
+ const listeners = new Set();
11
+ const previousPreference = ctx.theme.getTheme().preference;
12
+ let disposed = false;
13
+ const read = () => {
14
+ try { return localStorage.getItem(STORAGE_KEY) || localStorage.getItem('dsh_selected_theme'); }
15
+ catch { return null; }
16
+ };
17
+ const save = (key) => {
18
+ try { localStorage.setItem(STORAGE_KEY, key); } catch { /* 存储不可用时仍可在当前页面切换。 */ }
19
+ };
20
+ const current = () => Object.keys(THEME_SCHEMES).find((key) => THEME_SCHEMES[key].id === ctx.theme.getTheme().preference) || 'application';
21
+ const controller = {
316
22
  schemes: Object.keys(THEME_SCHEMES),
23
+ subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); },
24
+ getCurrentScheme: current,
317
25
  setScheme(key) {
318
- applyScheme(key);
319
- },
320
- getCurrentScheme() {
321
- try {
322
- return localStorage.getItem(storageKey) || "dark-soft";
323
- } catch (e) {
324
- return "dark-soft";
325
- }
26
+ if (disposed) return;
27
+ if (key !== 'application' && !THEME_SCHEMES[key]) throw new Error(`未知 Escook 主题:${key}`);
28
+ ctx.theme.setTheme(key === 'application' ? 'system' : THEME_SCHEMES[key].id);
29
+ save(key);
326
30
  },
327
31
  dispose() {
328
- if (styleEl && styleEl.parentNode) {
329
- styleEl.parentNode.removeChild(styleEl);
330
- }
331
- document.documentElement.removeAttribute("data-dsh-theme");
332
- if (window.__ESCOOK_THEME__ === themeController) {
333
- delete window.__ESCOOK_THEME__;
334
- }
32
+ if (disposed) return;
33
+ disposed = true;
34
+ // 先停止持久化监听,再撤销注册,避免把卸载时的回退写成用户选择。
35
+ for (const cleanup of cleanups.reverse()) cleanup();
36
+ listeners.clear();
37
+ if (window.__ESCOOK_THEME__ === controller) delete window.__ESCOOK_THEME__;
335
38
  }
336
39
  };
337
40
 
338
- window.__ESCOOK_THEME__ = themeController;
339
- return themeController;
41
+ try {
42
+ for (const scheme of Object.values(THEME_SCHEMES)) {
43
+ cleanups.push(ctx.theme.register({ id: scheme.id, colorScheme: scheme.type, tokens: scheme.tokens }));
44
+ }
45
+ cleanups.push(() => {
46
+ if (current() !== 'application') {
47
+ const available = ctx.theme.getTheme().themes?.some((theme) => theme.id === previousPreference);
48
+ ctx.theme.setTheme(available || previousPreference === 'system' ? previousPreference : 'system');
49
+ }
50
+ });
51
+ const style = document.createElement('style');
52
+ style.id = 'dsh-theme-escook-settings';
53
+ style.textContent = SETTINGS_CSS;
54
+ document.head.appendChild(style);
55
+ cleanups.push(() => style.parentNode?.removeChild(style));
56
+ const saved = read();
57
+ if (saved !== 'application') controller.setScheme(THEME_SCHEMES[saved] ? saved : 'dark-soft');
58
+ let previousSnapshot = ctx.theme.getTheme();
59
+ cleanups.push(ctx.on('theme/change', (snapshot) => {
60
+ const previous = previousSnapshot;
61
+ previousSnapshot = snapshot;
62
+ // 当前内核在写入字体大小时同时采纳原生 preference,保留独立的主题选择。
63
+ if (Object.values(THEME_SCHEMES).some((scheme) => scheme.id === previous.preference)
64
+ && snapshot.fontSize !== previous.fontSize && snapshot.preference !== previous.preference) {
65
+ ctx.theme.setTheme(previous.preference);
66
+ return;
67
+ }
68
+ save(current());
69
+ for (const listener of listeners) listener();
70
+ }));
71
+ cleanups.push(ctx.slots.inject('settings.general.item', () => ctx.slots.register({
72
+ name: 'settings.general.item', id: 'dsh-theme-escook', order: 12,
73
+ inject: () => ({ controller })
74
+ }, ThemeSettings)));
75
+ window.__ESCOOK_THEME__ = controller;
76
+ return controller;
77
+ } catch (error) {
78
+ controller.dispose();
79
+ throw error;
80
+ }
340
81
  }
341
82
 
342
- // 宿主以 Cordis 插件对象激活模块,卸载时释放本插件拥有的资源。
343
- export const apply = (context) => {
344
- const controller = activate(context);
83
+ // Cordis 会把普通函数识别为构造函数,必须保持不可构造的激活入口。
84
+ export const apply = (ctx) => {
85
+ const controller = activate(ctx);
345
86
  return () => controller.dispose();
346
87
  };
88
+
89
+ // 宿主 Loader 优先选择 default,依赖声明和激活入口必须在同一对象上。
90
+ export default { inject, apply };
package/client/palette.js CHANGED
@@ -1,59 +1,84 @@
1
- /**
2
- * Escook Theme Color Palettes Definition (Redesigned & Modernized)
3
- * 100% 对齐 codex-desktop 与官方正统 escook-theme 调色哲学
4
- */
5
-
1
+ // 统一调色来源。文字与交互背景成对定义,浮层使用不透明背景。
6
2
  export const PALETTES = {
7
3
  dark: {
8
- name: "escook Dark (经典暗黑)",
9
- desc: "VS Code 彬哥经典暖调极客深灰搭配标志暖阳橙,正统耐看长效护眼",
10
- type: "dark",
11
- bgBase: "#252526",
12
- bgSidebar: "#202021",
13
- bgSurface: "#29292c",
14
- accent: "#ef820c",
15
- accentHover: "#ff9940",
16
- textPrimary: "#fafafa",
17
- textSecondary: "#cccccc",
18
- border: "#333333"
4
+ name: '经典暗黑', type: 'dark', bg: '#252526', sidebar: '#202021', surface: '#29292c',
5
+ layer: '#2d2d30', hover: '#363638', selected: '#403628', accent: '#ef820c', accentHover: '#ff9940',
6
+ brand: '#ef820c', foreground: '#202021', text: '#fafafa', secondary: '#cccccc', muted: '#b6b6bd', border: '#606065', code: '#1c1c1d'
19
7
  },
20
- darkSoft: {
21
- name: "escook Dark Soft (柔和暗黑)",
22
- desc: "Ayu 经典深海蓝灰底色搭配温润奶杏黄,细腻柔和长效防疲劳",
23
- type: "dark",
24
- bgBase: "#1f2430",
25
- bgSidebar: "#191e28",
26
- bgSurface: "#232834",
27
- accent: "#ffcc66",
28
- accentHover: "#ffd580",
29
- textPrimary: "#cbccc6",
30
- textSecondary: "#969aa4",
31
- border: "#373e4c"
8
+ 'dark-soft': {
9
+ name: '柔和暗黑', type: 'dark', bg: '#1f2430', sidebar: '#191e28', surface: '#232834',
10
+ layer: '#262c3b', hover: '#333a4c', selected: '#41404a', accent: '#ffcc66', accentHover: '#ffd580',
11
+ brand: '#ffcc66', foreground: '#1f2430', text: '#eceee9', secondary: '#c1c5cf', muted: '#b6bcc9', border: '#68738a', code: '#171b24'
32
12
  },
33
13
  light: {
34
- name: "escook Light (经典紫韵浅色)",
35
- desc: "Solarized 经典护眼暖米白搭配典雅紫罗兰与青墨文本,温润纸质书卷感",
36
- type: "light",
37
- bgBase: "#fdf6e3",
38
- bgSidebar: "#f8f0d8",
39
- bgSurface: "#ffffff",
40
- accent: "#705697",
41
- accentHover: "#876cad",
42
- textPrimary: "#586e75",
43
- textSecondary: "#657b83",
44
- border: "#e3dac6"
14
+ name: '经典浅色', type: 'light', bg: '#fdf6e3', sidebar: '#f8f0d8', surface: '#ffffff',
15
+ layer: '#eee8d5', hover: '#e9e2d2', selected: '#e1d8cc', accent: '#705697', accentHover: '#654c89',
16
+ brand: '#705697', foreground: '#ffffff', text: '#35484f', secondary: '#465a61', muted: '#4c5e66', border: '#8b8173', code: '#f5eed8'
45
17
  },
46
- lightSoft: {
47
- name: "escook Light Soft (柔和浅色)",
48
- desc: "现代极简清透浅灰搭配柔和活力橙,明亮清爽不刺眼",
49
- type: "light",
50
- bgBase: "#fafafa",
51
- bgSidebar: "#f2f2f2",
52
- bgSurface: "#ffffff",
53
- accent: "#ff9940",
54
- accentHover: "#f58220",
55
- textPrimary: "#2d3748",
56
- textSecondary: "#4a5568",
57
- border: "#dcdcdc"
18
+ 'light-soft': {
19
+ name: '柔和浅色', type: 'light', bg: '#fafafa', sidebar: '#f2f2f2', surface: '#ffffff',
20
+ layer: '#e8e8e8', hover: '#e4e4e4', selected: '#f1dfca', accent: '#ff9940', accentHover: '#f58220',
21
+ brand: '#934400', foreground: '#222936', text: '#2d3748', secondary: '#4a5568', muted: '#505d70', border: '#838b96', code: '#f0f2f5'
58
22
  }
59
23
  };
24
+
25
+ function tokens(p) {
26
+ return {
27
+ '--dsw-alias-bg-base': p.bg,
28
+ '--dsw-alias-bg-layer-1': p.sidebar,
29
+ '--dsw-alias-bg-layer-2': p.surface,
30
+ '--dsw-alias-bg-layer-3': p.layer,
31
+ '--dsw-alias-bg-module-platform': p.layer,
32
+ '--dsw-alias-bg-multi-select': p.selected,
33
+ '--dsw-alias-bg-overlay': p.surface,
34
+ '--dsw-specific-selector': p.layer,
35
+ '--dsw-specific-sidebar-fill': p.sidebar,
36
+ '--dsw-specific-sidebar-nav-item-hover': p.hover,
37
+ '--dsw-specific-sidebar-nav-item-active': p.selected,
38
+ '--dsw-specific-sidebar-nav-item-active-accent': p.brand,
39
+ '--dsw-specific-input-major': p.surface,
40
+ '--dsw-specific-login-input': p.surface,
41
+ '--dsw-specific-bubble': p.surface,
42
+ '--dsw-specific-menu': p.surface,
43
+ '--dsw-menu-surface-fill': p.surface,
44
+ '--dsw-alias-menu-group-header-fill': p.surface,
45
+ '--dsw-alias-menu-icon': p.text,
46
+ '--dsw-hovercard-bg': p.surface,
47
+ '--dsw-alias-tooltip-bg': '#222936',
48
+ '--escook-tooltip-text': '#ffffff',
49
+ '--dsw-alias-button-elevated-fill': p.surface,
50
+ '--dsw-alias-button-floating-fill': p.surface,
51
+ '--dsw-alias-button-floating-hover': p.hover,
52
+ '--dsw-alias-button-ghost-active-fill': p.selected,
53
+ '--dsw-alias-button-ghost-active-hover': p.hover,
54
+ '--dsw-alias-button-ghost-active-border': p.border,
55
+ '--dsw-alias-brand-primary': p.brand,
56
+ '--dsw-alias-link': p.brand,
57
+ '--dsw-alias-button-primary-fill': p.accent,
58
+ '--dsw-alias-button-primary-hover': p.accentHover,
59
+ '--dsw-alias-label-primary-foreground': p.foreground,
60
+ '--dsw-alias-label-primary': p.text,
61
+ '--dsw-alias-label-primary-dimmed': p.secondary,
62
+ '--dsw-alias-label-primary-bluish': p.text,
63
+ '--dsw-alias-label-document-preview': p.secondary,
64
+ '--dsw-alias-label-secondary': p.secondary,
65
+ '--dsw-alias-label-tertiary': p.muted,
66
+ '--dsw-alias-label-dimmed': p.muted,
67
+ '--dsw-alias-label-caption': p.muted,
68
+ '--dsw-alias-interactive-bg-hover': p.hover,
69
+ '--dsw-alias-interactive-bg-active': p.selected,
70
+ '--dsw-alias-border-l1': p.border,
71
+ '--dsw-alias-border-l2': p.border,
72
+ '--dsw-alias-border-l3': p.border,
73
+ '--dsw-alias-border-l4': p.border,
74
+ '--dsw-alias-markdown-code-block': p.code,
75
+ '--dsw-alias-markdown-code-block-banner': p.layer,
76
+ '--dsw-alias-scrollbar-bg-l2': p.border,
77
+ '--dsw-alias-scrollbar-hover-l2': p.brand
78
+ };
79
+ }
80
+
81
+ export const THEME_SCHEMES = Object.fromEntries(Object.entries(PALETTES).map(([key, palette]) => [key, {
82
+ id: `escook-${key}`, name: palette.name, type: palette.type,
83
+ colorPreview: palette.accent, bgPreview: palette.bg, tokens: tokens(palette)
84
+ }]));
@@ -0,0 +1,32 @@
1
+ import { createElement as h, useSyncExternalStore } from 'react';
2
+ import { THEME_SCHEMES } from './palette.js';
3
+
4
+ export function ThemeSettings({ controller }) {
5
+ const selected = useSyncExternalStore(controller.subscribe, controller.getCurrentScheme, controller.getCurrentScheme);
6
+ const options = [...Object.entries(THEME_SCHEMES), ['application', { name: '跟随应用' }]];
7
+ return h('section', { className: 'escook-settings', 'aria-label': 'Escook 主题' },
8
+ h('h3', null, 'Escook 主题'),
9
+ h('p', null, '选择配色后立即生效。选择“跟随应用”后,可使用上方的应用外观设置。'),
10
+ h('div', { className: 'escook-settings-options', role: 'group', 'aria-label': '主题配色' },
11
+ options.map(([key, scheme]) => h('button', {
12
+ key, type: 'button', 'data-escook-scheme': key, 'aria-pressed': selected === key,
13
+ onClick: () => controller.setScheme(key)
14
+ }, scheme.bgPreview && h('span', {
15
+ className: 'escook-settings-preview', 'aria-hidden': true,
16
+ style: { background: scheme.bgPreview, borderColor: scheme.colorPreview }
17
+ }, h('span', { style: { background: scheme.colorPreview } })), scheme.name))));
18
+ }
19
+
20
+ // 样式仅作用于本插件的选择控件,宿主组件由 theme 服务统一着色。
21
+ export const SETTINGS_CSS = `
22
+ .escook-settings{padding:16px 0;border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}
23
+ .escook-settings h3{font-size:14px;font-weight:500;margin:0 0 8px}
24
+ .escook-settings p{font-size:13px;color:var(--dsw-alias-label-secondary);margin:0 0 12px;line-height:1.6}
25
+ .escook-settings-options{display:flex;flex-wrap:wrap;gap:8px}
26
+ .escook-settings-options button{display:flex;align-items:center;gap:8px;min-height:44px;padding:10px 14px;font:inherit;font-size:14px;border:1px solid var(--dsw-alias-border-l4);border-radius:10px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);cursor:pointer}
27
+ .escook-settings-options button:hover{background:var(--dsw-alias-interactive-bg-hover)}
28
+ .escook-settings-options button[aria-pressed="true"]{background:var(--dsw-alias-interactive-bg-active);border-color:var(--dsw-alias-brand-primary);box-shadow:inset 0 0 0 1px var(--dsw-alias-brand-primary)}
29
+ .escook-settings-options button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}
30
+ .escook-settings-preview{display:inline-flex;justify-content:center;align-items:center;width:24px;height:24px;border:2px solid;border-radius:6px}
31
+ .escook-settings-preview span{width:10px;height:10px;border-radius:50%}
32
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-theme-escook",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "🌸 彬哥の经典主题 (escook-theme) for DeepSeek Harness — 优雅暗夜紫调与琥珀金高亮美学主题",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -8,7 +8,7 @@
8
8
  "scripts": {
9
9
  "build": "node scripts/build-client.mjs",
10
10
  "prepack": "npm run build && npm test",
11
- "test": "node --test test/client-entry.test.mjs test/runtime-compatibility.test.mjs test/host-loader.test.mjs && node test/verify-themes.mjs"
11
+ "test": "node --test test/*.test.mjs && node test/verify-themes.mjs"
12
12
  },
13
13
  "exports": {
14
14
  ".": "./lib/index.js",
@@ -54,10 +54,12 @@
54
54
  "LICENSE"
55
55
  ],
56
56
  "peerDependencies": {
57
- "@deepseek-ai/dsh-client-ui-theme": ">=0.0.1-rc.1 <0.1.0 || >=0.1.0-rc.1 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0"
57
+ "@deepseek-ai/dsh-client-ui-theme": ">=0.2.0-rc.2 <0.3.0-0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "esbuild": "^0.28.2",
61
+ "react": "^18.3.1",
62
+ "react-dom": "^18.3.1",
61
63
  "semver": "^7.7.2"
62
64
  },
63
65
  "publishConfig": {