@linbin-mk/dsh-brand-deepseek 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +8 -7
- package/README.md +8 -7
- package/lib/client.js +90 -95
- package/lib/index.js +17 -21
- package/package.json +55 -33
package/README.en.md
CHANGED
|
@@ -20,13 +20,13 @@ It fills two single slots: `sidebar.brand.mark` (the whale mark) and `sidebar.br
|
|
|
20
20
|
- a **custom style** toggle — off restores the official default brand (`DSH 本地构建` / `DSH Local Build` + build-version badge): the brand slots are unregistered, so the shell fallback renders;
|
|
21
21
|
- a **hero headline** toggle — switches the blank-session title row independently (defaults to on);
|
|
22
22
|
- **trajectory tab** and **session log button** toggles — show or hide those two pieces of harness chrome in the conversation header (both default to on). Implemented purely at the DOM level: no harness source is touched and the data behind them (trajectory records, export) is unaffected;
|
|
23
|
-
- **ten brand colors** (the current scheme — official DeepSeek blue — is the default). Choices apply immediately and
|
|
23
|
+
- **ten brand colors** (the current scheme — official DeepSeek blue — is the default). Choices apply immediately and are written back to the Host configuration, so they survive reloads.
|
|
24
24
|
- **Live preview** — the settings preview panel mirrors both states; with the custom style off it renders the shell fallback (fish mark, localized label and the build-version badge).
|
|
25
25
|
|
|
26
26
|
## Requirements
|
|
27
27
|
|
|
28
28
|
- Node.js `^22.19` or `>=24`
|
|
29
|
-
- DeepSeek Harness `0.1.
|
|
29
|
+
- DeepSeek Harness `0.1.7-alpha.1` or a compatible `0.1.7` prerelease, on a **Web profile** that provides `ctx.slots`, `ctx.configForms` and `ctx.locale`
|
|
30
30
|
- The client modules the plugin declares (`@deepseek-ai/dsh-client-ui-primitives`, `-ui-sidebar`, `-ui-settings`, `-ui-renderer`, `-locale`, `dsh-api-remotes`) — the default Web profile already ships them
|
|
31
31
|
- No path dependency on a Harness checkout
|
|
32
32
|
|
|
@@ -58,7 +58,7 @@ dsh plugin --profile web-brand remove @linbin-mk/dsh-brand-deepseek
|
|
|
58
58
|
|
|
59
59
|
## Settings
|
|
60
60
|
|
|
61
|
-
The host half
|
|
61
|
+
The host half declares the brand style as this plugin's Cordis `Config`: all five fields are `.volatile()`, so every one of them is live-editable, with the defaults below. The settings namespace is the loader row id `brand-deepseek` from `cordis.patch.yml` — **deliberately not the package name**, which is the loader identity (`@linbin-mk/dsh-brand-deepseek`); renaming the package does not invalidate existing settings. The client half reads and writes that row's configuration form through `ctx.configForms.get('brand-deepseek')`, and the harness settings domain owns revision fencing and write serialization, so changes take effect immediately with no restart. On a non-loopback page (settings stay process-local) every write control is disabled instead of pretending to save.
|
|
62
62
|
|
|
63
63
|
| Setting | Default | Effect |
|
|
64
64
|
| --- | --- | --- |
|
|
@@ -70,7 +70,7 @@ The host half registers the `dsh-brand-deepseek` settings namespace. The fields
|
|
|
70
70
|
|
|
71
71
|
## How it works
|
|
72
72
|
|
|
73
|
-
- The **host half** (`lib/index.js`) does
|
|
73
|
+
- The **host half** (`lib/index.js`) does two things: it declares the brand-style Cordis `Config` (five `.volatile()` fields — the whole of the `brand-deepseek` settings namespace) and registers `{ auto: false }` for its own row, since the plugin ships its own settings page and the sidebar Plugins list must not generate a second one.
|
|
74
74
|
- The **client half** (`lib/client.js`) is a CJS bundle built by tsdown that self-registers through `window.__ModuleLoader__.load({ id, factory })`. The `id` must equal the **package name** — the harness keys its client module table by entry name, and a mismatch makes the row unresolvable and takes the whole GUI down while `--dump-config` still looks fine. `tsdown.config.ts` derives the id from `package.json#name` and `scripts/smoke-client.mjs` re-asserts the equality, so a rename cannot drift silently.
|
|
75
75
|
- The **hero style sheet** targets the harness's CSS-module locals by their `<hash>_<local>` shape, scoped to the conversation shell's `[data-composer-seat]` and anchored on the hero's own `_fishHitbox` descendant, so the shared `.headline` locals of other modules (ContextMeter, ApprovalPanel) are never touched. The official default title is hidden under **every** local it has shipped with — `_headlineText` in `0.1.5-rc.1` and the `_titleGroup` wrapper (title plus badge) introduced in `0.1.5-rc.2`; on a harness that never had a given local the matching selector is simply a no-op. Should those names change again, the plugin degrades to the brand title rendering next to the official one instead of erroring or taking the page down.
|
|
76
76
|
- The **two harness-chrome toggles** locate elements by class-name suffix with the localized label as a fallback, and a `MutationObserver` re-applies the state after React remounts the header. If both the class and the label change, the element stays visible rather than being hidden by mistake.
|
|
@@ -81,10 +81,11 @@ The host half registers the `dsh-brand-deepseek` settings namespace. The fields
|
|
|
81
81
|
pnpm install
|
|
82
82
|
pnpm build # src/ -> lib/ (lib/ is committed; installs need no build step)
|
|
83
83
|
pnpm build:check # rebuild and assert lib/ matches what is committed
|
|
84
|
-
pnpm test #
|
|
84
|
+
pnpm test # four smoke tests
|
|
85
|
+
npx tsc -p tsconfig.json --noEmit # type check (build.mjs does not run tsc)
|
|
85
86
|
```
|
|
86
87
|
|
|
87
|
-
`pnpm test` runs `scripts/smoke-client.mjs` (loads `lib/client.js` the way the browser loader does, asserting bundle id == package name plus the export and `inject` contract) and `scripts/smoke-visibility.mjs` (mounts `apply()` on a jsdom document and verifies that both persisted toggles drive the DOM, including the observer re-apply after a remount and the label fallback locator). jsdom is a development dependency of this package only; the plugin never needs it at runtime.
|
|
88
|
+
`pnpm test` runs `scripts/smoke-client.mjs` (loads `lib/client.js` the way the browser loader does, asserting bundle id == package name plus the export and `inject` contract), `scripts/smoke-host.mjs` (loads `lib/index.js` and asserts all five fields are volatile with the documented defaults, and that the host half registers `{ auto: false }` for its **own fiber**), `scripts/smoke-visibility.mjs` (mounts `apply()` on a jsdom document and verifies that both persisted toggles drive the DOM, including the observer re-apply after a remount and the label fallback locator) and `scripts/smoke-config.mjs` (mounts `apply()` over a fake configuration form and verifies that the form is fetched by the `brand-deepseek` row id, that the accepted section drives the runtime, that toggles write through `form.set`, and that a refused write or a non-writable page never pretends to save). jsdom is a development dependency of this package only; the plugin never needs it at runtime.
|
|
88
89
|
|
|
89
90
|
## Troubleshooting
|
|
90
91
|
|
|
@@ -105,7 +106,7 @@ pnpm test # both smoke tests
|
|
|
105
106
|
|
|
106
107
|
## Privacy and scope
|
|
107
108
|
|
|
108
|
-
The plugin makes no network requests, reads no conversation content and reports no telemetry. It does three things only: register slot occupants, read and write the `
|
|
109
|
+
The plugin makes no network requests, reads no conversation content and reports no telemetry. It does three things only: register slot occupants, read and write the `brand-deepseek` configuration row, and hide two DOM elements in the conversation header (the trajectory tab and the session log button) according to your settings. Values live in the harness profile configuration and never leave the machine. Hiding affects rendering only — trajectory data and export are untouched.
|
|
109
110
|
|
|
110
111
|
## License
|
|
111
112
|
|
package/README.md
CHANGED
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
- **自定义样式**开关 —— 关闭即恢复官方默认品牌(`DSH 本地构建` / `DSH Local Build` + 构建版本徽章):品牌插槽整体注销,由外壳回退渲染;
|
|
21
21
|
- **会话页标题**开关 —— 独立切换标题行(默认开启);
|
|
22
22
|
- **轨迹页签**与 **Session 日志按钮**开关 —— 独立显示/隐藏会话页头部这两处外壳元素(默认都开启),纯 DOM 层操作,不影响背后的轨迹数据与导出功能;
|
|
23
|
-
- **十种品牌颜色**可选(当前配色 = 官方 DeepSeek
|
|
23
|
+
- **十种品牌颜色**可选(当前配色 = 官方 DeepSeek 蓝,为默认项),选择后立即生效并写回 Host 配置,刷新后仍在。
|
|
24
24
|
- **实时预览** —— 设置页的预览区跟随两个状态渲染:自定义样式关闭时展示当前外壳回退品牌(鱼形标志、本地化标签与构建版本徽章)。
|
|
25
25
|
|
|
26
26
|
## 要求
|
|
27
27
|
|
|
28
28
|
- Node.js `^22.19` 或 `>=24`
|
|
29
|
-
- DeepSeek Harness `0.1.
|
|
29
|
+
- DeepSeek Harness `0.1.7-alpha.1` 或兼容的 `0.1.7` 预发布版本,以及提供 `ctx.slots`、`ctx.configForms` 与 `ctx.locale` 的 **Web profile**
|
|
30
30
|
- Web profile 中需带有插件声明的客户端模块(`@deepseek-ai/dsh-client-ui-primitives`、`-ui-sidebar`、`-ui-settings`、`-ui-renderer`、`-locale`、`dsh-api-remotes`);默认 Web profile 已包含
|
|
31
31
|
- 产物不含任何指向 Harness checkout 的路径依赖
|
|
32
32
|
|
|
@@ -58,7 +58,7 @@ dsh plugin --profile web-brand remove @linbin-mk/dsh-brand-deepseek
|
|
|
58
58
|
|
|
59
59
|
## 设置
|
|
60
60
|
|
|
61
|
-
Host
|
|
61
|
+
Host 半侧把品牌样式声明成本插件的 Cordis `Config`:五个字段全部 `.volatile()`,因此都是可在运行时改写的活配置,默认值如下。设置命名空间就是 `cordis.patch.yml` 里的 loader 行 id `brand-deepseek`——它与包名(加载标识 `@linbin-mk/dsh-brand-deepseek`)**故意不同**,改包名不会让已有设置失效。客户端半侧通过 `ctx.configForms.get('brand-deepseek')` 读写该行的配置表单,写入由 Harness 的设置域做修订号保护与串行化,因此修改立即生效、无需重启 Harness;非 loopback 页面(设置只能留在进程内存里)下写入控件整体禁用,不会假装已保存。
|
|
62
62
|
|
|
63
63
|
| 设置 | 默认值 | 作用 |
|
|
64
64
|
| --- | --- | --- |
|
|
@@ -70,7 +70,7 @@ Host 半侧注册 `dsh-brand-deepseek` 设置命名空间,字段默认值如
|
|
|
70
70
|
|
|
71
71
|
## 工作原理
|
|
72
72
|
|
|
73
|
-
- **Host 半侧**(`lib/index.js
|
|
73
|
+
- **Host 半侧**(`lib/index.js`)只做两件事:声明品牌样式的 Cordis `Config`(五个 `.volatile()` 字段,即设置命名空间 `brand-deepseek` 的全部内容),并为自己这一行声明 `{ auto: false }`——本插件自带设置页,不需要侧边栏「插件」列表再自动生成一个。
|
|
74
74
|
- **客户端半侧**(`lib/client.js`)是一个由 tsdown 打包、以 `window.__ModuleLoader__.load({ id, factory })` 自注册的 CJS bundle。`id` 必须等于**包名**——Harness 用入口名索引客户端模块表,名字不一致时该行解析失败、整个 GUI 起不来(而 `--dump-config` 仍显示正常)。`tsdown.config.ts` 直接从 `package.json` 取 `name` 写进 banner,`scripts/smoke-client.mjs` 再断言一次两者相等,因此重命名不会静默漂移。
|
|
75
75
|
- **标题行样式**按 CSS Module 的 `<hash>_<local>` 形状匹配 Harness 的局部类名,并用会话外壳的 `[data-composer-seat]` 限定作用域、锚定在标题行自己的 `_fishHitbox` 后代上,因此不会波及其他模块(ContextMeter、ApprovalPanel)的同名 `.headline`。官方默认标题按它用过的**每个**局部名隐藏——`0.1.5-rc.1` 的 `_headlineText`,以及 `0.1.5-rc.2` 起包裹标题与徽章的 `_titleGroup`;没出现过该局部名的版本上,对应选择器只是空操作。若将来这些名字被再次改掉,插件退化为「品牌标题 + 官方标题并排显示」,而不是报错或让整个页面起不来。
|
|
76
76
|
- **两个外壳开关**(轨迹页签、Session 日志按钮)按类名后缀定位元素、用本地化文案兜底,并用 `MutationObserver` 在 React 重挂载后重新应用;若两者都改名,元素保持可见,而不是误隐藏。
|
|
@@ -81,10 +81,11 @@ Host 半侧注册 `dsh-brand-deepseek` 设置命名空间,字段默认值如
|
|
|
81
81
|
pnpm install
|
|
82
82
|
pnpm build # src/ -> lib/(lib/ 已提交,安装后无需构建)
|
|
83
83
|
pnpm build:check # 重新构建并断言 lib/ 与提交内容一致
|
|
84
|
-
pnpm test #
|
|
84
|
+
pnpm test # 四个冒烟测试
|
|
85
|
+
npx tsc -p tsconfig.json --noEmit # 类型检查(build.mjs 不跑 tsc)
|
|
85
86
|
```
|
|
86
87
|
|
|
87
|
-
`pnpm test` 依次跑:`scripts/smoke-client.mjs`(按浏览器加载器的真实方式加载 `lib/client.js`,断言 bundle id == 包名、导出与 `inject`
|
|
88
|
+
`pnpm test` 依次跑:`scripts/smoke-client.mjs`(按浏览器加载器的真实方式加载 `lib/client.js`,断言 bundle id == 包名、导出与 `inject` 契约)、`scripts/smoke-host.mjs`(加载 `lib/index.js`,断言五个字段都是 volatile、默认值正确,且 Host 半侧以**自己的 fiber** 注册 `{ auto: false }`)、`scripts/smoke-visibility.mjs`(在 jsdom 里挂载 `apply()`,验证两个持久化开关驱动 DOM,包括重挂载后的观察者补偿与文案兜底定位)和 `scripts/smoke-config.mjs`(用假的配置表单挂载 `apply()`,验证按行 id `brand-deepseek` 取表单、读到的配置驱动运行时、开关经 `form.set` 写回、Host 拒绝写入或页面不可写时不假装已保存)。jsdom 只是本包的开发依赖,运行时不需要。
|
|
88
89
|
|
|
89
90
|
## 常见问题
|
|
90
91
|
|
|
@@ -105,7 +106,7 @@ pnpm test # 两个冒烟测试
|
|
|
105
106
|
|
|
106
107
|
## 隐私与作用域
|
|
107
108
|
|
|
108
|
-
|
|
109
|
+
插件不发起任何网络请求、不读取会话内容、不上报遥测。它只做三件事:注册插槽占位、读写本插件 `brand-deepseek` 这一行的配置、在会话页头部按设置隐藏两个 DOM 元素(轨迹页签、Session 日志按钮)。设置值保存在 Harness 的 profile 配置里,不离开本机。隐藏元素只影响渲染,轨迹数据与导出功能不受影响。
|
|
109
110
|
|
|
110
111
|
## 许可证
|
|
111
112
|
|
package/lib/client.js
CHANGED
|
@@ -196,6 +196,7 @@ window.__ModuleLoader__.load({
|
|
|
196
196
|
trajectoryTab: true,
|
|
197
197
|
sessionLogButton: true,
|
|
198
198
|
color: DEFAULT_BRAND_COLOR,
|
|
199
|
+
writable: false,
|
|
199
200
|
busy: false,
|
|
200
201
|
error: null
|
|
201
202
|
};
|
|
@@ -209,9 +210,24 @@ window.__ModuleLoader__.load({
|
|
|
209
210
|
};
|
|
210
211
|
};
|
|
211
212
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
213
|
+
* Adopt one Host-accepted style without a write (the first read, a write
|
|
214
|
+
* folded back, or an edit made from elsewhere). In-flight or failed
|
|
215
|
+
* operations keep their own status, so an accepted push never masks an error.
|
|
216
|
+
* @param state - accepted brand-style values.
|
|
217
|
+
* @param writable - whether the Host document accepts writes from this page.
|
|
218
|
+
*/
|
|
219
|
+
adopt = (state, writable) => {
|
|
220
|
+
this.state = {
|
|
221
|
+
...this.state,
|
|
222
|
+
...state,
|
|
223
|
+
writable
|
|
224
|
+
};
|
|
225
|
+
this.emit();
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Reload the accepted style. The load handler also applies the side effects
|
|
229
|
+
* (brand slot registration and the color variable), so both the boot refresh
|
|
230
|
+
* and every page visit converge on the accepted state.
|
|
215
231
|
*/
|
|
216
232
|
refresh = async () => {
|
|
217
233
|
const load = this.handlers?.load;
|
|
@@ -223,14 +239,10 @@ window.__ModuleLoader__.load({
|
|
|
223
239
|
};
|
|
224
240
|
this.emit();
|
|
225
241
|
try {
|
|
226
|
-
const
|
|
242
|
+
const accepted = await load();
|
|
227
243
|
this.state = {
|
|
228
244
|
...this.state,
|
|
229
|
-
|
|
230
|
-
hero,
|
|
231
|
-
trajectoryTab,
|
|
232
|
-
sessionLogButton,
|
|
233
|
-
color,
|
|
245
|
+
...accepted,
|
|
234
246
|
busy: false,
|
|
235
247
|
error: null
|
|
236
248
|
};
|
|
@@ -254,10 +266,10 @@ window.__ModuleLoader__.load({
|
|
|
254
266
|
};
|
|
255
267
|
this.emit();
|
|
256
268
|
try {
|
|
257
|
-
await handler(enabled);
|
|
269
|
+
const accepted = await handler(enabled);
|
|
258
270
|
this.state = {
|
|
259
271
|
...this.state,
|
|
260
|
-
|
|
272
|
+
...accepted,
|
|
261
273
|
busy: false,
|
|
262
274
|
error: null
|
|
263
275
|
};
|
|
@@ -281,10 +293,10 @@ window.__ModuleLoader__.load({
|
|
|
281
293
|
};
|
|
282
294
|
this.emit();
|
|
283
295
|
try {
|
|
284
|
-
await handler(hero);
|
|
296
|
+
const accepted = await handler(hero);
|
|
285
297
|
this.state = {
|
|
286
298
|
...this.state,
|
|
287
|
-
|
|
299
|
+
...accepted,
|
|
288
300
|
busy: false,
|
|
289
301
|
error: null
|
|
290
302
|
};
|
|
@@ -308,10 +320,10 @@ window.__ModuleLoader__.load({
|
|
|
308
320
|
};
|
|
309
321
|
this.emit();
|
|
310
322
|
try {
|
|
311
|
-
await handler(visible);
|
|
323
|
+
const accepted = await handler(visible);
|
|
312
324
|
this.state = {
|
|
313
325
|
...this.state,
|
|
314
|
-
|
|
326
|
+
...accepted,
|
|
315
327
|
busy: false,
|
|
316
328
|
error: null
|
|
317
329
|
};
|
|
@@ -335,10 +347,10 @@ window.__ModuleLoader__.load({
|
|
|
335
347
|
};
|
|
336
348
|
this.emit();
|
|
337
349
|
try {
|
|
338
|
-
await handler(visible);
|
|
350
|
+
const accepted = await handler(visible);
|
|
339
351
|
this.state = {
|
|
340
352
|
...this.state,
|
|
341
|
-
|
|
353
|
+
...accepted,
|
|
342
354
|
busy: false,
|
|
343
355
|
error: null
|
|
344
356
|
};
|
|
@@ -362,10 +374,10 @@ window.__ModuleLoader__.load({
|
|
|
362
374
|
};
|
|
363
375
|
this.emit();
|
|
364
376
|
try {
|
|
365
|
-
await handler(color);
|
|
377
|
+
const accepted = await handler(color);
|
|
366
378
|
this.state = {
|
|
367
379
|
...this.state,
|
|
368
|
-
|
|
380
|
+
...accepted,
|
|
369
381
|
busy: false,
|
|
370
382
|
error: null
|
|
371
383
|
};
|
|
@@ -414,8 +426,11 @@ window.__ModuleLoader__.load({
|
|
|
414
426
|
* the sidebar preview.
|
|
415
427
|
*
|
|
416
428
|
* The page remounts on every visit (the settings shell renders only the
|
|
417
|
-
* active section), so it always starts from a fresh
|
|
418
|
-
*
|
|
429
|
+
* active section), so it always starts from a fresh adoption of the accepted
|
|
430
|
+
* Config section. Writes go through the plugin's Config form, owned by the
|
|
431
|
+
* apply half; every control is disabled while a write is in flight and when
|
|
432
|
+
* the Host document cannot accept writes from this page (a non-loopback page
|
|
433
|
+
* keeps no durable settings).
|
|
419
434
|
*/
|
|
420
435
|
/**
|
|
421
436
|
* The sidebar's local-build badge format (ui-sidebar `localBuildVersion()`):
|
|
@@ -574,7 +589,7 @@ window.__ModuleLoader__.load({
|
|
|
574
589
|
window.clearTimeout(timer);
|
|
575
590
|
};
|
|
576
591
|
}, [state.enabled]);
|
|
577
|
-
const disabled = state.busy;
|
|
592
|
+
const disabled = state.busy || !state.writable;
|
|
578
593
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
579
594
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("settings.title") }),
|
|
580
595
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("settings.intro") }),
|
|
@@ -1028,20 +1043,31 @@ window.__ModuleLoader__.load({
|
|
|
1028
1043
|
"brand.heroDefaultBadge": "Preview"
|
|
1029
1044
|
};
|
|
1030
1045
|
//#endregion
|
|
1046
|
+
//#region src/settings.ts
|
|
1047
|
+
/**
|
|
1048
|
+
* Brand-style configuration shared by the plugin's two faces.
|
|
1049
|
+
*
|
|
1050
|
+
* The host half declares its Cordis Config as the volatile projection of this
|
|
1051
|
+
* shape, and the loader row id becomes the settings namespace; the client half
|
|
1052
|
+
* addresses that entry's live form through {@link BRAND_DEEPSEEK_ENTRY_ID}.
|
|
1053
|
+
* The module carries no imports of its own, so the client bundle inlines
|
|
1054
|
+
* declarations plus one string constant and nothing else.
|
|
1055
|
+
*/
|
|
1056
|
+
/** Loader entry id (`cordis.patch.yml`) owning the brand-style settings. */
|
|
1057
|
+
const BRAND_DEEPSEEK_ENTRY_ID = "brand-deepseek";
|
|
1058
|
+
//#endregion
|
|
1031
1059
|
//#region src/client/index.ts
|
|
1032
1060
|
const inject = [
|
|
1033
1061
|
"slots",
|
|
1034
1062
|
"remote",
|
|
1035
|
-
"
|
|
1063
|
+
"configForms",
|
|
1036
1064
|
"locale"
|
|
1037
1065
|
];
|
|
1038
|
-
/** Settings namespace holding the persisted brand style. */
|
|
1039
|
-
const NAMESPACE = "dsh-brand-deepseek";
|
|
1040
1066
|
/** Document-root color variable the brand SVGs fill from. */
|
|
1041
1067
|
const COLOR_VAR = "--dsh-brand-deepseek-color";
|
|
1042
1068
|
/** DOM id of the injected hero-headline sheet. */
|
|
1043
1069
|
const HERO_STYLE_ID = "dsh-brand-deepseek-hero-style";
|
|
1044
|
-
/** Read the
|
|
1070
|
+
/** Read the accepted brand style; the defaults keep today's look. */
|
|
1045
1071
|
function defaultsOf(stored) {
|
|
1046
1072
|
const value = stored ?? {};
|
|
1047
1073
|
return {
|
|
@@ -1053,7 +1079,7 @@ window.__ModuleLoader__.load({
|
|
|
1053
1079
|
};
|
|
1054
1080
|
}
|
|
1055
1081
|
/**
|
|
1056
|
-
* Client plugin body: locale dictionaries, the
|
|
1082
|
+
* Client plugin body: locale dictionaries, the live brand-style wiring
|
|
1057
1083
|
* (brand slot registration + color variable), and the settings page entry.
|
|
1058
1084
|
* @param ctx - client root context.
|
|
1059
1085
|
*/
|
|
@@ -1062,8 +1088,8 @@ window.__ModuleLoader__.load({
|
|
|
1062
1088
|
zh,
|
|
1063
1089
|
en
|
|
1064
1090
|
}), "dsh-brand-deepseek: dictionaries");
|
|
1065
|
-
const api = ctx.remote.settings;
|
|
1066
1091
|
const t = ctx.locale.bind("dsh-brand-deepseek");
|
|
1092
|
+
const form = ctx.configForms.get(BRAND_DEEPSEEK_ENTRY_ID);
|
|
1067
1093
|
let brandDispose;
|
|
1068
1094
|
const installBrand = () => {
|
|
1069
1095
|
if (brandDispose !== void 0) return;
|
|
@@ -1108,7 +1134,7 @@ window.__ModuleLoader__.load({
|
|
|
1108
1134
|
};
|
|
1109
1135
|
const visibility = createVisibilityController();
|
|
1110
1136
|
visibility.start();
|
|
1111
|
-
/** Reconcile the runtime with one
|
|
1137
|
+
/** Reconcile the runtime with one brand style (idempotent). */
|
|
1112
1138
|
const applyState = (state) => {
|
|
1113
1139
|
if (state.enabled) installBrand();
|
|
1114
1140
|
else uninstallBrand();
|
|
@@ -1119,87 +1145,56 @@ window.__ModuleLoader__.load({
|
|
|
1119
1145
|
visibility.setTrajectoryTab(state.trajectoryTab);
|
|
1120
1146
|
visibility.setSessionLogButton(state.sessionLogButton);
|
|
1121
1147
|
};
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
return
|
|
1148
|
+
/** Whether the Host document accepts writes from this page. */
|
|
1149
|
+
const writable = () => {
|
|
1150
|
+
const snapshot = form.getSnapshot();
|
|
1151
|
+
return snapshot.status === "ready" && snapshot.writable && snapshot.mode === "host";
|
|
1126
1152
|
};
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1153
|
+
/**
|
|
1154
|
+
* Adopt the section the Host last accepted (the schema defaults while none
|
|
1155
|
+
* stands): apply its side effects and publish it to the settings page.
|
|
1156
|
+
*/
|
|
1157
|
+
const adopt = () => {
|
|
1158
|
+
const state = defaultsOf(form.getSnapshot().value);
|
|
1159
|
+
applyState(state);
|
|
1160
|
+
brandStyle.adopt(state, writable());
|
|
1161
|
+
return state;
|
|
1130
1162
|
};
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
};
|
|
1163
|
+
/**
|
|
1164
|
+
* Write one field. A page the Host refuses to persist never pretends to:
|
|
1165
|
+
* the write is skipped, and a refused write leaves the accepted section
|
|
1166
|
+
* standing for the page to re-adopt.
|
|
1167
|
+
*/
|
|
1168
|
+
const write = async (field, value) => {
|
|
1169
|
+
if (!writable()) throw new Error("this page cannot persist plugin settings");
|
|
1170
|
+
if (!await form.set(field, value)) throw new Error(`the Host refused the "${field}" change`);
|
|
1140
1171
|
};
|
|
1141
1172
|
brandStyle.handlers = {
|
|
1142
|
-
load: async () =>
|
|
1143
|
-
const state = await readPersisted();
|
|
1144
|
-
applyState(state);
|
|
1145
|
-
return state;
|
|
1146
|
-
},
|
|
1173
|
+
load: async () => adopt(),
|
|
1147
1174
|
setEnabled: async (enabled) => {
|
|
1148
|
-
await
|
|
1149
|
-
|
|
1150
|
-
path: ["enabled"],
|
|
1151
|
-
value: enabled
|
|
1152
|
-
}]);
|
|
1153
|
-
applyState({
|
|
1154
|
-
...currentOf(),
|
|
1155
|
-
enabled
|
|
1156
|
-
});
|
|
1175
|
+
await write("enabled", enabled);
|
|
1176
|
+
return adopt();
|
|
1157
1177
|
},
|
|
1158
1178
|
setHero: async (hero) => {
|
|
1159
|
-
await
|
|
1160
|
-
|
|
1161
|
-
path: ["hero"],
|
|
1162
|
-
value: hero
|
|
1163
|
-
}]);
|
|
1164
|
-
applyState({
|
|
1165
|
-
...currentOf(),
|
|
1166
|
-
hero
|
|
1167
|
-
});
|
|
1179
|
+
await write("hero", hero);
|
|
1180
|
+
return adopt();
|
|
1168
1181
|
},
|
|
1169
1182
|
setTrajectoryTab: async (trajectoryTab) => {
|
|
1170
|
-
await
|
|
1171
|
-
|
|
1172
|
-
path: ["trajectoryTab"],
|
|
1173
|
-
value: trajectoryTab
|
|
1174
|
-
}]);
|
|
1175
|
-
applyState({
|
|
1176
|
-
...currentOf(),
|
|
1177
|
-
trajectoryTab
|
|
1178
|
-
});
|
|
1183
|
+
await write("trajectoryTab", trajectoryTab);
|
|
1184
|
+
return adopt();
|
|
1179
1185
|
},
|
|
1180
1186
|
setSessionLogButton: async (sessionLogButton) => {
|
|
1181
|
-
await
|
|
1182
|
-
|
|
1183
|
-
path: ["sessionLogButton"],
|
|
1184
|
-
value: sessionLogButton
|
|
1185
|
-
}]);
|
|
1186
|
-
applyState({
|
|
1187
|
-
...currentOf(),
|
|
1188
|
-
sessionLogButton
|
|
1189
|
-
});
|
|
1187
|
+
await write("sessionLogButton", sessionLogButton);
|
|
1188
|
+
return adopt();
|
|
1190
1189
|
},
|
|
1191
1190
|
setColor: async (color) => {
|
|
1192
|
-
await
|
|
1193
|
-
|
|
1194
|
-
path: ["color"],
|
|
1195
|
-
value: color
|
|
1196
|
-
}]);
|
|
1197
|
-
applyState({
|
|
1198
|
-
...currentOf(),
|
|
1199
|
-
color
|
|
1200
|
-
});
|
|
1191
|
+
await write("color", color);
|
|
1192
|
+
return adopt();
|
|
1201
1193
|
}
|
|
1202
1194
|
};
|
|
1195
|
+
ctx.effect(() => form.subscribe(() => {
|
|
1196
|
+
adopt();
|
|
1197
|
+
}), "dsh-brand-deepseek: config form adoption");
|
|
1203
1198
|
brandStyle.refresh();
|
|
1204
1199
|
ctx.effect(() => () => {
|
|
1205
1200
|
visibility.dispose();
|
package/lib/index.js
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
//#region src/index.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/** Config schema; the defaults match the plugin's shipped look. */
|
|
4
|
+
const Config = z.object({
|
|
5
|
+
enabled: z.boolean().default(true).volatile(),
|
|
6
|
+
hero: z.boolean().default(true).volatile(),
|
|
7
|
+
trajectoryTab: z.boolean().default(true).volatile(),
|
|
8
|
+
sessionLogButton: z.boolean().default(true).volatile(),
|
|
9
|
+
color: z.string().default("#4176e6").volatile()
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Host plugin body: declare this instance's settings presentation. The client
|
|
13
|
+
* ships the Settings page, so the registry generates none of its own.
|
|
14
|
+
* @param ctx - host plugin context.
|
|
15
|
+
*/
|
|
7
16
|
function apply(ctx) {
|
|
8
|
-
ctx.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
hero: z.boolean().default(true),
|
|
12
|
-
trajectoryTab: z.boolean().default(true),
|
|
13
|
-
sessionLogButton: z.boolean().default(true),
|
|
14
|
-
color: z.string().default("#4176e6")
|
|
15
|
-
}), { base: {
|
|
16
|
-
enabled: true,
|
|
17
|
-
hero: true,
|
|
18
|
-
trajectoryTab: true,
|
|
19
|
-
sessionLogButton: true,
|
|
20
|
-
color: "#4176e6"
|
|
21
|
-
} });
|
|
22
|
-
return () => {};
|
|
23
|
-
}, "dsh-brand-deepseek: settings namespace");
|
|
17
|
+
ctx.inject(["settings"], (child) => {
|
|
18
|
+
child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
|
|
19
|
+
});
|
|
24
20
|
}
|
|
25
21
|
//#endregion
|
|
26
|
-
export {
|
|
22
|
+
export { Config, apply };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linbin-mk/dsh-brand-deepseek",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Third-party DeepSeek Harness brand plugin: whale mark plus a minimal 'deepseek' wordmark for the Web client sidebar.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "node build.mjs",
|
|
34
34
|
"build:check": "node build.mjs && git diff --exit-code -- lib",
|
|
35
|
-
"test": "node scripts/smoke-client.mjs && node scripts/smoke-visibility.mjs"
|
|
35
|
+
"test": "node scripts/smoke-client.mjs && node scripts/smoke-host.mjs && node scripts/smoke-visibility.mjs && node scripts/smoke-config.mjs"
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"lib/**/*.js",
|
|
@@ -47,43 +47,65 @@
|
|
|
47
47
|
},
|
|
48
48
|
"packageManager": "pnpm@11.7.0",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
50
|
+
"@deepseek-ai/schemastery": "^3.18.3"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
54
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
62
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
53
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
54
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
|
|
55
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-alpha.1",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.7-alpha.1",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.7-alpha.1",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
|
|
62
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-alpha.1"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
|
-
"@deepseek-ai/cordis": {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"@deepseek-ai/dsh-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"@deepseek-ai/dsh-client-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
65
|
+
"@deepseek-ai/cordis": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"@deepseek-ai/dsh-api-remotes": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-conversation": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-sidebar": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@deepseek-ai/dsh-settings": {
|
|
93
|
+
"optional": true
|
|
94
|
+
}
|
|
75
95
|
},
|
|
76
96
|
"devDependencies": {
|
|
77
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
78
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
79
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
80
|
-
"@deepseek-ai/dsh-client-
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-
|
|
82
|
-
"@deepseek-ai/dsh-client-ui-
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
97
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
98
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
|
|
99
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
|
|
100
|
+
"@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
|
|
101
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-alpha.1",
|
|
102
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
|
|
103
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
|
|
104
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.7-alpha.1",
|
|
105
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.7-alpha.1",
|
|
106
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
|
|
107
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-alpha.1",
|
|
108
|
+
"@types/node": "^22.20.4",
|
|
87
109
|
"@types/react": "^18.3.1",
|
|
88
110
|
"jsdom": "^25.0.1",
|
|
89
111
|
"react": "^18.2.0",
|