dsh-theme-escook 1.0.3 → 1.0.5

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
@@ -1,37 +1,38 @@
1
1
  # dsh-theme-escook
2
2
 
3
- <p align="center">
4
- <img src="https://raw.githubusercontent.com/Simon-yyy/dsh-theme-escook/main/assets/logo.svg" width="96" alt="escook-theme logo" />
5
- </p>
3
+ Four themes for DeepSeek Harness: classic dark, soft dark, classic light, and soft light.
6
4
 
7
- <h3 align="center">🌸 Escook Theme for DeepSeek Harness</h3>
5
+ ## Install and select a theme
8
6
 
9
- <p align="center">
10
- A premium dark violet & amber gold aesthetic theme crafted for DeepSeek Harness Web GUI & Desktop.
11
- </p>
7
+ Install and enable `dsh-theme-escook` in the application's plugin market. Refresh or restart when prompted.
8
+ Open **Settings → General → Escook theme** and select a palette. The controls currently display Chinese labels.
9
+ Selection is saved in local storage and restored after reload. Select **跟随应用** (Follow application)
10
+ to use the application's light, dark, or system appearance controls.
12
11
 
13
- <p align="center">
14
- <img src="https://raw.githubusercontent.com/Simon-yyy/dsh-theme-escook/main/assets/preview.png" alt="escook-theme 4-in-1 preview" width="100%" />
15
- </p>
12
+ The plugin toggle enables the plugin; theme selection is in General settings.
13
+ The official desktop application needs this plugin to add these choices.
16
14
 
17
- ---
15
+ ## Requirements
18
16
 
19
- ## ✨ Features
17
+ Version `1.0.5` requires `@deepseek-ai/dsh-client-ui-theme >=0.2.0-rc.2 <0.3.0-0`.
18
+ Update older applications or continue using an earlier plugin version.
20
19
 
21
- - 🎨 **Signature Dark Aesthetics**: Faithful port of the beloved VS Code `escook-theme` color palette (`#1f2430` deep night base with `#ffcc66` amber gold accent).
22
- - 💻 **Syntax Highlighting**: Enhanced code readability with vibrant pastel tones (Pink keywords, Cyan functions, Mint strings, Amber numbers).
23
- - ⚡ **Full UI Coverage**: Seamlessly styles sidebar, chat bubbles, settings panels, marketplace cards, and scrollbars.
24
- - 🔄 **Live Hot Toggle**: Instant switch in DSH Market Theme tab without server restart.
20
+ Themes use the official theme service to synchronize tokens and light/dark mode.
21
+ Menus use opaque backgrounds; text, icons and buttons have paired colors.
22
+ Contrast tests require at least `4.5:1` for common text in normal, hover and selected states.
23
+ Disabling removes the settings row and registered themes and restores application appearance.
25
24
 
26
- ---
25
+ ## Development
27
26
 
28
- ## 📦 Installation
29
-
30
- ```bash
31
- dsh plugin --profile web add dsh-theme-escook
27
+ ```powershell
28
+ npm ci --legacy-peer-deps
29
+ npm run build
30
+ npm test
32
31
  ```
33
32
 
34
- ---
33
+ Native host and browser tests explicitly skip when diagnostic fixtures are unavailable.
34
+ See `contexts/context.md` for fixture requirements and verification limits.
35
+
36
+ ## License
35
37
 
36
- ## 📄 License
37
38
  MIT License © 2026 Simon-yyy / escook (liulongbin1314)
package/README.zh.md CHANGED
@@ -1,45 +1,40 @@
1
1
  # dsh-theme-escook
2
2
 
3
- <p align="center">
4
- <img src="https://raw.githubusercontent.com/Simon-yyy/dsh-theme-escook/main/assets/logo.svg" width="96" alt="escook-theme logo" />
5
- </p>
3
+ 为 DeepSeek Harness 提供经典暗黑、柔和暗黑、经典浅色、柔和浅色四套配色。
6
4
 
7
- <h3 align="center">🌸 彬哥の经典主题 (escook-theme) for DeepSeek Harness</h3>
5
+ ## 安装与切换
8
6
 
9
- <p align="center">
10
- 专为 DeepSeek Harness Web GUI / Desktop 设计的暗夜紫调与琥珀金高亮美学主题。
11
- </p>
7
+ 1. 在应用的插件市场安装并启用 `dsh-theme-escook`,按应用提示重启或刷新。
8
+ 2. 打开 **设置 → 通用 → Escook 主题**,点击配色按钮立即切换。
9
+ 3. 选择会保存在当前浏览器或应用 profile 的本地存储中,刷新或重启后恢复。
10
+ 4. 选择 **跟随应用**,可使用应用原有的浅色、深色、跟随系统外观设置。
12
11
 
13
- <p align="center">
14
- <img src="https://raw.githubusercontent.com/Simon-yyy/dsh-theme-escook/main/assets/preview.png" alt="escook-theme 4 合 1 真实效果预览" width="100%" />
15
- </p>
12
+ 插件列表中的开关表示启用或禁用插件。四套配色在通用设置中选择。
13
+ 官方应用需要安装此插件后才有这些选项;不要求桌面端存在自定义的内置主题接口。
16
14
 
17
- ---
15
+ ## 兼容要求
18
16
 
19
- ## ✨ 主题特性
17
+ `1.0.5` 使用官方主题与插槽服务,要求 `@deepseek-ai/dsh-client-ui-theme >=0.2.0-rc.2 <0.3.0-0`。
18
+ 旧内核请先升级应用,或继续使用旧版插件。
20
19
 
21
- - 🎨 **经典暗色美学**:深度复刻 VS Code 最受喜爱的 `escook-theme` 调色板(深紫暗夜 `#1f2430` 底色 + 标志性琥珀明黄 `#ffcc66` 强调色)。
22
- - 💻 **极客代码高亮**:粉红关键字、天蓝函数名、薄荷绿字符串与鲜橙常量,高对比度护眼舒适。
23
- - ⚡ **全组件覆盖**:侧边栏、对话气泡、设置面板、插件市场、弹窗与滚动条均得到原生级深度适配。
24
- - 🔄 **热插拔切换**:在插件市场“主题”页一键点选切换,无需重启即时生效。
20
+ ## 配色与生命周期
25
21
 
26
- ---
22
+ - 通过 `ctx.theme` 同步颜色、深浅色模式和宿主组件,不使用全局强制 CSS 覆盖。
23
+ - 账户菜单使用不透明背景;按钮、输入框、菜单文字与图标成对设置颜色。
24
+ - 常用文字对比度测试覆盖普通、悬停、选中状态,要求至少 `4.5:1`。
25
+ - 禁用后清理设置行和主题注册,恢复应用外观。再次启用可恢复之前选择。
27
26
 
28
- ## 📦 安装使用
27
+ ## 开发验证
29
28
 
30
- > 💡 **桌面端用户提示**:如果您使用的是 **DSH Desktop 桌面客户端**,客户端内核已原生内置本系列主题,开箱即用,无需在此重复安装。
31
-
32
- ### 方法一:通过 DSH 插件市场安装(推荐)
33
- 1. 打开 DeepSeek Harness 桌面端 / Web;
34
- 2. 进入 **设置 → 插件市场 → 主题** 专区;
35
- 3. 搜索 **`dsh-theme-escook`** 并点击 **安装**,点选即可立即生效!
36
-
37
- ### 方法二:命令行一键安装
38
- ```bash
39
- dsh plugin --profile web add dsh-theme-escook
29
+ ```powershell
30
+ npm ci --legacy-peer-deps
31
+ npm run build
32
+ npm test
40
33
  ```
41
34
 
42
- ---
35
+ 真实宿主和浏览器测试需要诊断文件,未配置时会明确跳过;配置方法见 `contexts/context.md`。
36
+ 本地测试通过不代表用户正在运行的应用已经更新。
37
+
38
+ ## 许可
43
39
 
44
- ## 📄 开源许可
45
40
  MIT License © 2026 Simon-yyy / escook (liulongbin1314)
@@ -0,0 +1,309 @@
1
+ // 自动构建产物,请修改 client/client.js 后运行 npm run build。
2
+ window.__ModuleLoader__.load({
3
+ id: "dsh-theme-escook",
4
+ factory: (require) => {
5
+ const module = { exports: {} };
6
+ const exports = module.exports;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
+
25
+ // client/client.js
26
+ var client_exports = {};
27
+ __export(client_exports, {
28
+ THEME_SCHEMES: () => THEME_SCHEMES,
29
+ apply: () => apply,
30
+ default: () => activate,
31
+ inject: () => inject
32
+ });
33
+ module.exports = __toCommonJS(client_exports);
34
+
35
+ // client/palette.js
36
+ var PALETTES = {
37
+ dark: {
38
+ name: "\u7ECF\u5178\u6697\u9ED1",
39
+ type: "dark",
40
+ bg: "#252526",
41
+ sidebar: "#202021",
42
+ surface: "#29292c",
43
+ layer: "#2d2d30",
44
+ hover: "#363638",
45
+ selected: "#403628",
46
+ accent: "#ef820c",
47
+ accentHover: "#ff9940",
48
+ brand: "#ef820c",
49
+ foreground: "#202021",
50
+ text: "#fafafa",
51
+ secondary: "#cccccc",
52
+ muted: "#b6b6bd",
53
+ border: "#606065",
54
+ code: "#1c1c1d"
55
+ },
56
+ "dark-soft": {
57
+ name: "\u67D4\u548C\u6697\u9ED1",
58
+ type: "dark",
59
+ bg: "#1f2430",
60
+ sidebar: "#191e28",
61
+ surface: "#232834",
62
+ layer: "#262c3b",
63
+ hover: "#333a4c",
64
+ selected: "#41404a",
65
+ accent: "#ffcc66",
66
+ accentHover: "#ffd580",
67
+ brand: "#ffcc66",
68
+ foreground: "#1f2430",
69
+ text: "#eceee9",
70
+ secondary: "#c1c5cf",
71
+ muted: "#b6bcc9",
72
+ border: "#68738a",
73
+ code: "#171b24"
74
+ },
75
+ light: {
76
+ name: "\u7ECF\u5178\u6D45\u8272",
77
+ type: "light",
78
+ bg: "#fdf6e3",
79
+ sidebar: "#f8f0d8",
80
+ surface: "#ffffff",
81
+ layer: "#eee8d5",
82
+ hover: "#e9e2d2",
83
+ selected: "#e1d8cc",
84
+ accent: "#705697",
85
+ accentHover: "#654c89",
86
+ brand: "#705697",
87
+ foreground: "#ffffff",
88
+ text: "#35484f",
89
+ secondary: "#465a61",
90
+ muted: "#4c5e66",
91
+ border: "#8b8173",
92
+ code: "#f5eed8"
93
+ },
94
+ "light-soft": {
95
+ name: "\u67D4\u548C\u6D45\u8272",
96
+ type: "light",
97
+ bg: "#fafafa",
98
+ sidebar: "#f2f2f2",
99
+ surface: "#ffffff",
100
+ layer: "#e8e8e8",
101
+ hover: "#e4e4e4",
102
+ selected: "#f1dfca",
103
+ accent: "#ff9940",
104
+ accentHover: "#f58220",
105
+ brand: "#934400",
106
+ foreground: "#222936",
107
+ text: "#2d3748",
108
+ secondary: "#4a5568",
109
+ muted: "#505d70",
110
+ border: "#838b96",
111
+ code: "#f0f2f5"
112
+ }
113
+ };
114
+ function tokens(p) {
115
+ return {
116
+ "--dsw-alias-bg-base": p.bg,
117
+ "--dsw-alias-bg-layer-1": p.sidebar,
118
+ "--dsw-alias-bg-layer-2": p.surface,
119
+ "--dsw-alias-bg-layer-3": p.layer,
120
+ "--dsw-alias-bg-module-platform": p.layer,
121
+ "--dsw-alias-bg-multi-select": p.selected,
122
+ "--dsw-alias-bg-overlay": p.surface,
123
+ "--dsw-specific-selector": p.layer,
124
+ "--dsw-specific-sidebar-fill": p.sidebar,
125
+ "--dsw-specific-sidebar-nav-item-hover": p.hover,
126
+ "--dsw-specific-sidebar-nav-item-active": p.selected,
127
+ "--dsw-specific-sidebar-nav-item-active-accent": p.brand,
128
+ "--dsw-specific-input-major": p.surface,
129
+ "--dsw-specific-login-input": p.surface,
130
+ "--dsw-specific-bubble": p.surface,
131
+ "--dsw-specific-menu": p.surface,
132
+ "--dsw-menu-surface-fill": p.surface,
133
+ "--dsw-alias-menu-group-header-fill": p.surface,
134
+ "--dsw-alias-menu-icon": p.text,
135
+ "--dsw-hovercard-bg": p.surface,
136
+ "--dsw-alias-tooltip-bg": "#222936",
137
+ "--escook-tooltip-text": "#ffffff",
138
+ "--dsw-alias-button-elevated-fill": p.surface,
139
+ "--dsw-alias-button-floating-fill": p.surface,
140
+ "--dsw-alias-button-floating-hover": p.hover,
141
+ "--dsw-alias-button-ghost-active-fill": p.selected,
142
+ "--dsw-alias-button-ghost-active-hover": p.hover,
143
+ "--dsw-alias-button-ghost-active-border": p.border,
144
+ "--dsw-alias-brand-primary": p.brand,
145
+ "--dsw-alias-link": p.brand,
146
+ "--dsw-alias-button-primary-fill": p.accent,
147
+ "--dsw-alias-button-primary-hover": p.accentHover,
148
+ "--dsw-alias-label-primary-foreground": p.foreground,
149
+ "--dsw-alias-label-primary": p.text,
150
+ "--dsw-alias-label-primary-dimmed": p.secondary,
151
+ "--dsw-alias-label-primary-bluish": p.text,
152
+ "--dsw-alias-label-document-preview": p.secondary,
153
+ "--dsw-alias-label-secondary": p.secondary,
154
+ "--dsw-alias-label-tertiary": p.muted,
155
+ "--dsw-alias-label-dimmed": p.muted,
156
+ "--dsw-alias-label-caption": p.muted,
157
+ "--dsw-alias-interactive-bg-hover": p.hover,
158
+ "--dsw-alias-interactive-bg-active": p.selected,
159
+ "--dsw-alias-border-l1": p.border,
160
+ "--dsw-alias-border-l2": p.border,
161
+ "--dsw-alias-border-l3": p.border,
162
+ "--dsw-alias-border-l4": p.border,
163
+ "--dsw-alias-markdown-code-block": p.code,
164
+ "--dsw-alias-markdown-code-block-banner": p.layer,
165
+ "--dsw-alias-scrollbar-bg-l2": p.border,
166
+ "--dsw-alias-scrollbar-hover-l2": p.brand
167
+ };
168
+ }
169
+ var THEME_SCHEMES = Object.fromEntries(Object.entries(PALETTES).map(([key, palette]) => [key, {
170
+ id: `escook-${key}`,
171
+ name: palette.name,
172
+ type: palette.type,
173
+ colorPreview: palette.accent,
174
+ bgPreview: palette.bg,
175
+ tokens: tokens(palette)
176
+ }]));
177
+
178
+ // client/theme-settings.js
179
+ var import_react = require("react");
180
+ function ThemeSettings({ controller }) {
181
+ const selected = (0, import_react.useSyncExternalStore)(controller.subscribe, controller.getCurrentScheme, controller.getCurrentScheme);
182
+ const options = [...Object.entries(THEME_SCHEMES), ["application", { name: "\u8DDF\u968F\u5E94\u7528" }]];
183
+ return (0, import_react.createElement)(
184
+ "section",
185
+ { className: "escook-settings", "aria-label": "Escook \u4E3B\u9898" },
186
+ (0, import_react.createElement)("h3", null, "Escook \u4E3B\u9898"),
187
+ (0, import_react.createElement)("p", null, "\u9009\u62E9\u914D\u8272\u540E\u7ACB\u5373\u751F\u6548\u3002\u9009\u62E9\u201C\u8DDF\u968F\u5E94\u7528\u201D\u540E\uFF0C\u53EF\u4F7F\u7528\u4E0A\u65B9\u7684\u5E94\u7528\u5916\u89C2\u8BBE\u7F6E\u3002"),
188
+ (0, import_react.createElement)(
189
+ "div",
190
+ { className: "escook-settings-options", role: "group", "aria-label": "\u4E3B\u9898\u914D\u8272" },
191
+ options.map(([key, scheme]) => (0, import_react.createElement)("button", {
192
+ key,
193
+ type: "button",
194
+ "data-escook-scheme": key,
195
+ "aria-pressed": selected === key,
196
+ onClick: () => controller.setScheme(key)
197
+ }, scheme.bgPreview && (0, import_react.createElement)("span", {
198
+ className: "escook-settings-preview",
199
+ "aria-hidden": true,
200
+ style: { background: scheme.bgPreview, borderColor: scheme.colorPreview }
201
+ }, (0, import_react.createElement)("span", { style: { background: scheme.colorPreview } })), scheme.name))
202
+ )
203
+ );
204
+ }
205
+ var SETTINGS_CSS = `
206
+ .escook-settings{padding:16px 0;border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}
207
+ .escook-settings h3{font-size:14px;font-weight:500;margin:0 0 8px}
208
+ .escook-settings p{font-size:13px;color:var(--dsw-alias-label-secondary);margin:0 0 12px;line-height:1.6}
209
+ .escook-settings-options{display:flex;flex-wrap:wrap;gap:8px}
210
+ .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}
211
+ .escook-settings-options button:hover{background:var(--dsw-alias-interactive-bg-hover)}
212
+ .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)}
213
+ .escook-settings-options button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}
214
+ .escook-settings-preview{display:inline-flex;justify-content:center;align-items:center;width:24px;height:24px;border:2px solid;border-radius:6px}
215
+ .escook-settings-preview span{width:10px;height:10px;border-radius:50%}
216
+ `;
217
+
218
+ // client/client.js
219
+ var inject = ["theme", "slots"];
220
+ var STORAGE_KEY = "dsh_theme_escook_scheme";
221
+ function activate(ctx) {
222
+ const cleanups = [];
223
+ const listeners = /* @__PURE__ */ new Set();
224
+ const previousPreference = ctx.theme.getTheme().preference;
225
+ let disposed = false;
226
+ const read = () => {
227
+ try {
228
+ return localStorage.getItem(STORAGE_KEY) || localStorage.getItem("dsh_selected_theme");
229
+ } catch {
230
+ return null;
231
+ }
232
+ };
233
+ const save = (key) => {
234
+ try {
235
+ localStorage.setItem(STORAGE_KEY, key);
236
+ } catch {
237
+ }
238
+ };
239
+ const current = () => Object.keys(THEME_SCHEMES).find((key) => THEME_SCHEMES[key].id === ctx.theme.getTheme().preference) || "application";
240
+ const controller = {
241
+ schemes: Object.keys(THEME_SCHEMES),
242
+ subscribe(listener) {
243
+ listeners.add(listener);
244
+ return () => listeners.delete(listener);
245
+ },
246
+ getCurrentScheme: current,
247
+ setScheme(key) {
248
+ if (disposed) return;
249
+ if (key !== "application" && !THEME_SCHEMES[key]) throw new Error(`\u672A\u77E5 Escook \u4E3B\u9898\uFF1A${key}`);
250
+ ctx.theme.setTheme(key === "application" ? "system" : THEME_SCHEMES[key].id);
251
+ save(key);
252
+ },
253
+ dispose() {
254
+ if (disposed) return;
255
+ disposed = true;
256
+ for (const cleanup of cleanups.reverse()) cleanup();
257
+ listeners.clear();
258
+ if (window.__ESCOOK_THEME__ === controller) delete window.__ESCOOK_THEME__;
259
+ }
260
+ };
261
+ try {
262
+ for (const scheme of Object.values(THEME_SCHEMES)) {
263
+ cleanups.push(ctx.theme.register({ id: scheme.id, colorScheme: scheme.type, tokens: scheme.tokens }));
264
+ }
265
+ cleanups.push(() => {
266
+ if (current() !== "application") {
267
+ const available = ctx.theme.getTheme().themes?.some((theme) => theme.id === previousPreference);
268
+ ctx.theme.setTheme(available || previousPreference === "system" ? previousPreference : "system");
269
+ }
270
+ });
271
+ const style = document.createElement("style");
272
+ style.id = "dsh-theme-escook-settings";
273
+ style.textContent = SETTINGS_CSS;
274
+ document.head.appendChild(style);
275
+ cleanups.push(() => style.parentNode?.removeChild(style));
276
+ const saved = read();
277
+ if (saved !== "application") controller.setScheme(THEME_SCHEMES[saved] ? saved : "dark-soft");
278
+ let previousSnapshot = ctx.theme.getTheme();
279
+ cleanups.push(ctx.on("theme/change", (snapshot) => {
280
+ const previous = previousSnapshot;
281
+ previousSnapshot = snapshot;
282
+ if (Object.values(THEME_SCHEMES).some((scheme) => scheme.id === previous.preference) && snapshot.fontSize !== previous.fontSize && snapshot.preference !== previous.preference) {
283
+ ctx.theme.setTheme(previous.preference);
284
+ return;
285
+ }
286
+ save(current());
287
+ for (const listener of listeners) listener();
288
+ }));
289
+ cleanups.push(ctx.slots.inject("settings.general.item", () => ctx.slots.register({
290
+ name: "settings.general.item",
291
+ id: "dsh-theme-escook",
292
+ order: 12,
293
+ inject: () => ({ controller })
294
+ }, ThemeSettings)));
295
+ window.__ESCOOK_THEME__ = controller;
296
+ return controller;
297
+ } catch (error) {
298
+ controller.dispose();
299
+ throw error;
300
+ }
301
+ }
302
+ var apply = (ctx) => {
303
+ const controller = activate(ctx);
304
+ return () => controller.dispose();
305
+ };
306
+
307
+ return module.exports;
308
+ }
309
+ });
package/client/client.js CHANGED
@@ -1,337 +1,87 @@
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
- };
4
+ export { THEME_SCHEMES };
5
+ export const inject = ['theme', 'slots'];
6
+ const STORAGE_KEY = 'dsh_theme_escook_scheme';
259
7
 
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
- }
279
- };
280
- window.__ESCOOK_THEME__ = themeController;
281
- return themeController;
282
- }
283
-
284
- const styleId = "dsh-theme-escook-styles";
285
- const storageKey = "dsh_theme_escook_scheme";
286
- let styleEl = document.getElementById(styleId);
287
-
288
- function applyScheme(key) {
289
- const validKey = THEME_SCHEMES[key] ? key : "dark-soft";
290
- const scheme = THEME_SCHEMES[validKey];
291
-
292
- if (!styleEl) {
293
- styleEl = document.createElement("style");
294
- styleEl.id = styleId;
295
- document.head.appendChild(styleEl);
296
- }
297
- styleEl.textContent = scheme.css;
298
- document.documentElement.setAttribute("data-dsh-theme", `escook-${validKey}`);
299
- try {
300
- localStorage.setItem(storageKey, validKey);
301
- } catch (e) {}
302
- console.info(`🌸 [dsh-theme-escook] 当前激活配色: ${scheme.name}`);
303
- }
304
-
305
- let savedScheme = "dark-soft";
306
- try {
307
- savedScheme = localStorage.getItem(storageKey) || localStorage.getItem("dsh_selected_theme") || "dark-soft";
308
- } catch (e) {}
309
-
310
- applyScheme(savedScheme);
311
-
312
- const themeController = {
8
+ export default 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 = {
313
22
  schemes: Object.keys(THEME_SCHEMES),
23
+ subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); },
24
+ getCurrentScheme: current,
314
25
  setScheme(key) {
315
- applyScheme(key);
316
- },
317
- getCurrentScheme() {
318
- try {
319
- return localStorage.getItem(storageKey) || "dark-soft";
320
- } catch (e) {
321
- return "dark-soft";
322
- }
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);
323
30
  },
324
31
  dispose() {
325
- if (styleEl && styleEl.parentNode) {
326
- styleEl.parentNode.removeChild(styleEl);
327
- }
328
- document.documentElement.removeAttribute("data-dsh-theme");
329
- if (window.__ESCOOK_THEME__ === themeController) {
330
- delete window.__ESCOOK_THEME__;
331
- }
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__;
332
38
  }
333
39
  };
334
40
 
335
- window.__ESCOOK_THEME__ = themeController;
336
- 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
+ }
337
81
  }
82
+
83
+ // Cordis 会把普通函数识别为构造函数,必须保持不可构造的激活入口。
84
+ export const apply = (ctx) => {
85
+ const controller = activate(ctx);
86
+ return () => controller.dispose();
87
+ };
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,16 +1,18 @@
1
1
  {
2
2
  "name": "dsh-theme-escook",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "🌸 彬哥の经典主题 (escook-theme) for DeepSeek Harness — 优雅暗夜紫调与琥珀金高亮美学主题",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
9
- "test": "node --test test/client-entry.test.mjs test/runtime-compatibility.test.mjs && node test/verify-themes.mjs"
9
+ "build": "node scripts/build-client.mjs",
10
+ "prepack": "npm run build && npm test",
11
+ "test": "node --test test/*.test.mjs && node test/verify-themes.mjs"
10
12
  },
11
13
  "exports": {
12
14
  ".": "./lib/index.js",
13
- "./client": "./client/client.js",
15
+ "./client": "./client/client.bundle.js",
14
16
  "./package.json": "./package.json"
15
17
  },
16
18
  "author": "Simon-yyy",
@@ -52,9 +54,12 @@
52
54
  "LICENSE"
53
55
  ],
54
56
  "peerDependencies": {
55
- "@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"
56
58
  },
57
59
  "devDependencies": {
60
+ "esbuild": "^0.28.2",
61
+ "react": "^18.3.1",
62
+ "react-dom": "^18.3.1",
58
63
  "semver": "^7.7.2"
59
64
  },
60
65
  "publishConfig": {