deepseek-harness-background 0.2.0 → 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.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![GitHub stars](https://img.shields.io/github/stars/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/stargazers)
4
4
  [![GitHub release](https://img.shields.io/github/v/release/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/releases)
5
+ [![npm version](https://img.shields.io/npm/v/deepseek-harness-background?style=flat-square&logo=npm&label=npm)](https://www.npmjs.com/package/deepseek-harness-background)
6
+ [![npm downloads](https://img.shields.io/npm/dt/deepseek-harness-background?style=flat-square&logo=npm)](https://www.npmjs.com/package/deepseek-harness-background)
5
7
  [![CI](https://img.shields.io/github/actions/workflow/status/HaoyueQin/deepseek-harness-background/ci.yml?style=flat-square&logo=githubactions&logoColor=white)](https://github.com/HaoyueQin/deepseek-harness-background/actions)
6
8
  [![GitHub issues](https://img.shields.io/github/issues/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/issues)
7
9
  [![GitHub last commit](https://img.shields.io/github/last-commit/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/commits)
@@ -91,7 +93,7 @@ dsh --profile web
91
93
  - The **settings row** lives in the official General settings section (`settings.general.item` slot), next to the Appearance row. Its chrome uses only `--dsw-alias-*` design tokens (buttons / pills / segmented control / slider track match the official shell); sliders are native `input[type=range]` with 5% / 1–2px steps and release-commit.
92
94
  - The plugin's own host routes (`/api/bg-wallpaper/*`: `settings`, `upload`, `image/<id>`) read/write the section and serve uploads with same-origin + size caps + MIME/signature checks + a path-escape fence. A custom route family is used because the api-proxy settings allowlist does not expose third-party namespaces over the settings RPC.
93
95
  - The background is drawn as a fixed `z-index:-2` wallpaper layer plus a `z-index:-1` scrim on `body`, toggled by the `data-dsh-bg` attribute; the scrim switches white/black by `data-ds-dark-theme` in the injected stylesheet; the frosted-glass effect overrides the shell's surface design tokens.
94
- - Uploads live under `$DSH_HOME/deepseek-harness-background/` (content-addressed ids). Switching to a new image or clearing the background deletes the superseded upload file, so the directory never accumulates dead images. Disable / uninstall leaves nothing behind.
96
+ - Uploads live under `$DSH_HOME/deepseek-harness-background/` (content-addressed ids). Switching to a new image or clearing the background deletes the superseded upload file, so the directory does not accumulate dead images in normal use. (An upload that is never saved into the section — e.g. the tab closes right after an upload — can leave one orphaned file behind.) Disable / uninstall leaves nothing behind.
95
97
 
96
98
  ## Development
97
99
 
package/README.zh.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![GitHub stars](https://img.shields.io/github/stars/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/stargazers)
4
4
  [![GitHub release](https://img.shields.io/github/v/release/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/releases)
5
+ [![npm version](https://img.shields.io/npm/v/deepseek-harness-background?style=flat-square&logo=npm&label=npm)](https://www.npmjs.com/package/deepseek-harness-background)
6
+ [![npm downloads](https://img.shields.io/npm/dt/deepseek-harness-background?style=flat-square&logo=npm)](https://www.npmjs.com/package/deepseek-harness-background)
5
7
  [![CI](https://img.shields.io/github/actions/workflow/status/HaoyueQin/deepseek-harness-background/ci.yml?style=flat-square&logo=githubactions&logoColor=white)](https://github.com/HaoyueQin/deepseek-harness-background/actions)
6
8
  [![GitHub issues](https://img.shields.io/github/issues/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/issues)
7
9
  [![GitHub last commit](https://img.shields.io/github/last-commit/HaoyueQin/deepseek-harness-background?style=flat-square&logo=github)](https://github.com/HaoyueQin/deepseek-harness-background/commits)
@@ -91,7 +93,7 @@ dsh --profile web
91
93
  - **设置行**位于官方「通用」设置分区的 `settings.general.item` 槽中,紧挨「外观」行。控件样式全部使用 `--dsw-alias-*` 设计 token(按钮 / 胶囊 / 分段控件 / 滑块轨道与官方 chrome 一致),滑块为原生 `input[type=range]` 的 5% / 1–2px 步进 + 松手提交。
92
94
  - 插件自有的 host 路由(`/api/bg-wallpaper/*`:`settings`、`upload`、`image/<id>`)负责读写设置与提供上传图片,带同源校验、大小上限、MIME/签名校验与路径穿越防护。使用自定义路由族,是因为 api-proxy 的 settings 白名单不向第三方命名空间开放 settings RPC。
93
95
  - 背景以 `body` 上一张固定的 `z-index:-2` 壁纸层 + `z-index:-1` 遮罩绘制,由 `data-dsh-bg` 属性开关;遮罩在注入样式表里按 `data-ds-dark-theme` 切换白/黑纱帘;毛玻璃效果通过覆盖外壳的 surface 设计 token 实现。
94
- - 上传文件存放在 `$DSH_HOME/deepseek-harness-background/`(内容寻址 id)。切换新图片或清除背景时,被替换的旧上传文件会被自动回收,目录不会堆积死图片。关闭 / 卸载后不留残留。
96
+ - 上传文件存放在 `$DSH_HOME/deepseek-harness-background/`(内容寻址 id)。切换新图片或清除背景时,被替换的旧上传文件会被自动回收,正常使用下目录不会堆积死图片。(例外:上传后从未保存进设置——例如上传后立刻关闭标签页——会留下一个孤儿文件。)关闭 / 卸载后不留残留。
95
97
 
96
98
  ## 开发
97
99
 
package/lib/client.js CHANGED
@@ -92,8 +92,6 @@ window.__ModuleLoader__.load({
92
92
  //#region src/settings.ts
93
93
  /** Browser-facing base path of the background API (shared with the host routes). */
94
94
  const BACKGROUND_API_PREFIX = "/api/bg-wallpaper";
95
- /** Maximum scrim opacity (a nearly opaque veil keeps text legible over any art). */
96
- const SCRIM_MAX = .95;
97
95
  /** Default scrim strength applied over user art (a light readability veil). */
98
96
  const DEFAULT_SCRIM = .25;
99
97
  /** Default panel opacity (0..1); 1 = fully opaque official surfaces, 0 = fully transparent glass. */
@@ -124,6 +122,20 @@ window.__ModuleLoader__.load({
124
122
  /** Fraction of the official white surface alpha kept at the most transparent panel setting. */
125
123
  const GLASS_MIN_ALPHA = .05;
126
124
  /**
125
+ * The theme-aware glass surface alphas for one panelOpacity: the input-surface
126
+ * token and the bubble token share one curve, dimmed per scheme. Shared by the
127
+ * live painter and the settings-row preview card so the two cannot drift.
128
+ */
129
+ function glassSurfaceAlphas(panelOpacity, inDark) {
130
+ const alpha = GLASS_MIN_ALPHA + panelOpacity * .85;
131
+ const clamped = Math.max(0, Math.min(.9, alpha));
132
+ const factor = inDark ? .4 : .8;
133
+ return {
134
+ major: `rgba(255, 255, 255, ${(clamped * factor).toFixed(3)})`,
135
+ bubble: `rgba(255, 255, 255, ${(clamped * factor * .8).toFixed(3)})`
136
+ };
137
+ }
138
+ /**
127
139
  * Resolve the effective background image url for a section.
128
140
  * @param settings - resolved background section.
129
141
  * @returns the image url, or '' when no source is set.
@@ -169,6 +181,8 @@ window.__ModuleLoader__.load({
169
181
  document.body.style.setProperty("--dsw-specific-sidebar-fill", "transparent");
170
182
  const url = backgroundImageUrl(settings, uploadBase);
171
183
  if (url === "") {
184
+ this.observer?.disconnect();
185
+ this.observer = void 0;
172
186
  this.applySurfaceGlass(settings, true);
173
187
  this.active = false;
174
188
  this.removeLayers();
@@ -181,10 +195,11 @@ window.__ModuleLoader__.load({
181
195
  this.img = document.createElement("img");
182
196
  this.img.className = IMAGE_CLASS;
183
197
  this.img.alt = "";
198
+ this.img.referrerPolicy = "no-referrer";
184
199
  this.layer.appendChild(this.img);
185
200
  document.body.appendChild(this.layer);
186
201
  }
187
- if (this.img && this.img.src !== url) this.img.src = url;
202
+ if (this.img && this.img.getAttribute("src") !== url) this.img.src = url;
188
203
  if (!this.scrim) {
189
204
  this.scrim = document.createElement("div");
190
205
  this.scrim.className = SCRIM_CLASS;
@@ -264,11 +279,9 @@ window.__ModuleLoader__.load({
264
279
  this.setVar("--bg-glass-blur", "0px");
265
280
  return;
266
281
  }
267
- const alpha = GLASS_MIN_ALPHA + settings.panelOpacity * .85;
268
- const clamped = Math.max(0, Math.min(.9, alpha));
269
- const factor = inDark ? .4 : .8;
270
- s.setProperty("--dsw-specific-input-major", `rgba(255, 255, 255, ${(clamped * factor).toFixed(3)})`);
271
- s.setProperty("--dsw-specific-bubble", `rgba(255, 255, 255, ${(clamped * factor * .8).toFixed(3)})`);
282
+ const { major, bubble } = glassSurfaceAlphas(settings.panelOpacity, inDark);
283
+ s.setProperty("--dsw-specific-input-major", major);
284
+ s.setProperty("--dsw-specific-bubble", bubble);
272
285
  }
273
286
  /** Remove the wallpaper + scrim layers and clear the active attribute. */
274
287
  removeLayers() {
@@ -354,10 +367,7 @@ window.__ModuleLoader__.load({
354
367
  }
355
368
  s.setProperty("--bg-glass-blur", `${settings.blur}px`);
356
369
  s.setProperty("--bg-glass-saturate", String(1.15 + settings.blur * .03));
357
- const alpha = GLASS_MIN_ALPHA + settings.panelOpacity * .85;
358
- const clamped = Math.max(0, Math.min(.9, alpha));
359
- const factor = inDark ? .4 : .8;
360
- s.setProperty("--bg-preview-glass", `rgba(255, 255, 255, ${(clamped * factor * .8).toFixed(3)})`);
370
+ s.setProperty("--bg-preview-glass", glassSurfaceAlphas(settings.panelOpacity, inDark).bubble);
361
371
  }
362
372
  /** Remove every preview variable from an element (restores CSS defaults). */
363
373
  function clearPreviewSurface(el) {
@@ -371,8 +381,9 @@ window.__ModuleLoader__.load({
371
381
  "background.description": "为整个应用界面设置背景图片,可调不透明度、遮罩、面板透明与毛玻璃模糊",
372
382
  "background.upload": "上传图片",
373
383
  "background.uploading": "上传中…",
374
- "background.uploadMinetypes": "支持 JPG / PNG / WebP / GIF",
384
+ "background.uploadMediaTypes": "支持 JPG / PNG / WebP / GIF",
375
385
  "background.urlPlaceholder": "或粘贴图片 http(s) 链接后回车",
386
+ "background.invalidUrl": "链接无效,请粘贴 http(s) 开头的图片链接",
376
387
  "background.applyUrl": "应用链接",
377
388
  "background.enabled": "背景已启用",
378
389
  "background.source": "图片",
@@ -397,8 +408,9 @@ window.__ModuleLoader__.load({
397
408
  "background.description": "Set a background image behind the whole app surface with adjustable opacity, scrim, panel transparency and glass blur",
398
409
  "background.upload": "Upload image",
399
410
  "background.uploading": "Uploading…",
400
- "background.uploadMinetypes": "JPG / PNG / WebP / GIF",
411
+ "background.uploadMediaTypes": "JPG / PNG / WebP / GIF",
401
412
  "background.urlPlaceholder": "Or paste an image http(s) link and press Enter",
413
+ "background.invalidUrl": "Invalid link — paste an image link starting with http(s)",
402
414
  "background.applyUrl": "Apply URL",
403
415
  "background.enabled": "Background on",
404
416
  "background.source": "Image",
@@ -481,9 +493,10 @@ window.__ModuleLoader__.load({
481
493
  /**
482
494
  * Persist the section through the host route. Concurrent calls are
483
495
  * serialized by a request sequence: an older save that resolves after a
484
- * newer one must not clobber the newer snapshot with its stale value.
496
+ * newer one answers 'superseded' instead of 'ok', so the caller can never
497
+ * mistake a stale write for success and adopt its outdated section.
485
498
  * @param section - the complete next section.
486
- * @returns whether the host accepted the write.
499
+ * @returns whether the write committed, was displaced by a newer save, or failed.
487
500
  */
488
501
  async save(section) {
489
502
  const seq = ++this.saveSeq;
@@ -495,16 +508,16 @@ window.__ModuleLoader__.load({
495
508
  body: JSON.stringify(section)
496
509
  });
497
510
  const body = await response.json();
498
- if (!response.ok || !body.ok || body.value === void 0) return false;
499
- if (seq !== this.saveSeq) return true;
511
+ if (!response.ok || !body.ok || body.value === void 0) return "failed";
512
+ if (seq !== this.saveSeq) return "superseded";
500
513
  this.snapshot = {
501
514
  status: "ready",
502
515
  value: body.value
503
516
  };
504
517
  this.notify();
505
- return true;
518
+ return "ok";
506
519
  } catch {
507
- return false;
520
+ return "failed";
508
521
  }
509
522
  };
510
523
  const promise = run();
@@ -546,39 +559,39 @@ window.__ModuleLoader__.load({
546
559
  document.head.appendChild(tag);
547
560
  }
548
561
  var SettingsRow_module_css_default = {
549
- "titleRow": "-ozqdq_titleRow",
550
- "clearBtn": "-ozqdq_clearBtn",
551
- "preview": "-ozqdq_preview",
552
- "previewGlass": "-ozqdq_previewGlass",
553
- "header": "-ozqdq_header",
562
+ "segActive": "-ozqdq_segActive",
563
+ "error": "-ozqdq_error",
564
+ "sliderLabel": "-ozqdq_sliderLabel",
554
565
  "previewEmpty": "-ozqdq_previewEmpty",
555
- "sectionLabel": "-ozqdq_sectionLabel",
556
- "btnDanger": "-ozqdq_btnDanger",
557
- "sliderRow": "-ozqdq_sliderRow",
558
- "segGroup": "-ozqdq_segGroup",
559
- "clearRow": "-ozqdq_clearRow",
566
+ "row": "-ozqdq_row",
567
+ "urlRow": "-ozqdq_urlRow",
560
568
  "slider": "-ozqdq_slider",
561
569
  "sliderValue": "-ozqdq_sliderValue",
562
- "hint": "-ozqdq_hint",
570
+ "clearRow": "-ozqdq_clearRow",
571
+ "preview": "-ozqdq_preview",
572
+ "clearBtn": "-ozqdq_clearBtn",
573
+ "btn": "-ozqdq_btn",
563
574
  "segLabel": "-ozqdq_segLabel",
564
- "segBtn": "-ozqdq_segBtn",
565
- "sliderLabel": "-ozqdq_sliderLabel",
566
- "segRow": "-ozqdq_segRow",
575
+ "previewGlassText": "-ozqdq_previewGlassText",
576
+ "btnPrimary": "-ozqdq_btnPrimary",
577
+ "header": "-ozqdq_header",
578
+ "title": "-ozqdq_title",
579
+ "previewScrim": "-ozqdq_previewScrim",
580
+ "sectionLabel": "-ozqdq_sectionLabel",
581
+ "enabledBadge": "-ozqdq_enabledBadge",
582
+ "titleRow": "-ozqdq_titleRow",
583
+ "previewGlass": "-ozqdq_previewGlass",
567
584
  "desc": "-ozqdq_desc",
568
- "urlRow": "-ozqdq_urlRow",
585
+ "sliderRow": "-ozqdq_sliderRow",
569
586
  "previewImg": "-ozqdq_previewImg",
570
- "segActive": "-ozqdq_segActive",
571
- "error": "-ozqdq_error",
572
- "title": "-ozqdq_title",
587
+ "hint": "-ozqdq_hint",
588
+ "segBtn": "-ozqdq_segBtn",
589
+ "segGroup": "-ozqdq_segGroup",
573
590
  "urlInput": "-ozqdq_urlInput",
574
- "btn": "-ozqdq_btn",
575
- "btnPrimary": "-ozqdq_btnPrimary",
576
- "previewGlassText": "-ozqdq_previewGlassText",
577
- "enabledBadge": "-ozqdq_enabledBadge",
578
- "row": "-ozqdq_row",
579
- "previewScrim": "-ozqdq_previewScrim",
580
- "section": "-ozqdq_section",
581
- "sourceRow": "-ozqdq_sourceRow"
591
+ "btnDanger": "-ozqdq_btnDanger",
592
+ "segRow": "-ozqdq_segRow",
593
+ "sourceRow": "-ozqdq_sourceRow",
594
+ "section": "-ozqdq_section"
582
595
  };
583
596
  //#endregion
584
597
  //#region src/client/SettingsRow.tsx
@@ -624,7 +637,7 @@ window.__ModuleLoader__.load({
624
637
  },
625
638
  scrim: {
626
639
  min: 0,
627
- max: SCRIM_MAX,
640
+ max: 1,
628
641
  step: .05,
629
642
  unit: "percent"
630
643
  },
@@ -783,23 +796,29 @@ window.__ModuleLoader__.load({
783
796
  /** Persist one section through the host route and adopt it on success. */
784
797
  const saveNow = (0, react.useCallback)(async (next) => {
785
798
  dirtyRef.current = true;
786
- const ok = await settingsClient.save(next);
787
- if (ok) dirtyRef.current = false;
788
- setError(ok ? "" : t("background.saveFailed"));
789
- if (ok) {
790
- lastSavedRef.current = next;
791
- draftRef.current = next;
792
- setDraft(next);
793
- if (!next.uploadId) setUrlText(next.url);
799
+ const result = await settingsClient.save(next);
800
+ if (result === "failed") {
801
+ setError(t("background.saveFailed"));
802
+ return;
794
803
  }
804
+ if (result === "superseded") return;
805
+ dirtyRef.current = false;
806
+ setError("");
807
+ lastSavedRef.current = next;
808
+ draftRef.current = next;
809
+ setDraft(next);
810
+ if (!next.uploadId) setUrlText(next.url);
795
811
  }, [t]);
812
+ const saveNowRef = (0, react.useRef)(saveNow);
813
+ saveNowRef.current = saveNow;
796
814
  /** Debounced commit: one POST per drag gesture, from the latest draft. */
797
815
  const onCommit = (0, react.useCallback)(() => {
798
816
  clearTimeout(commitTimer.current);
799
817
  commitTimer.current = window.setTimeout(() => {
800
- saveNow(draftRef.current);
818
+ commitTimer.current = void 0;
819
+ saveNowRef.current(draftRef.current);
801
820
  }, 120);
802
- }, [saveNow]);
821
+ }, []);
803
822
  (0, react.useEffect)(() => {
804
823
  if (snapshot.status !== "ready" || snapshot.value === void 0) return;
805
824
  const saved = lastSavedRef.current;
@@ -812,8 +831,13 @@ window.__ModuleLoader__.load({
812
831
  }
813
832
  }, [snapshot]);
814
833
  (0, react.useEffect)(() => () => {
815
- clearTimeout(commitTimer.current);
816
834
  clearTimeout(editingTimer.current);
835
+ editingRef.current = false;
836
+ if (commitTimer.current !== void 0) {
837
+ clearTimeout(commitTimer.current);
838
+ commitTimer.current = void 0;
839
+ saveNowRef.current(draftRef.current);
840
+ }
817
841
  if (dirtyRef.current) settingsClient.flush();
818
842
  }, []);
819
843
  const handleFile = async (file) => {
@@ -845,7 +869,7 @@ window.__ModuleLoader__.load({
845
869
  const handleUrlSubmit = () => {
846
870
  const url = urlText.trim();
847
871
  if (!/^https?:\/\//i.test(url)) {
848
- setError(t("background.saveFailed"));
872
+ setError(t("background.invalidUrl"));
849
873
  return;
850
874
  }
851
875
  markEditing();
@@ -916,7 +940,8 @@ window.__ModuleLoader__.load({
916
940
  className: SettingsRow_module_css_default.previewImg,
917
941
  src: sourceUrl,
918
942
  alt: "",
919
- draggable: false
943
+ draggable: false,
944
+ referrerPolicy: "no-referrer"
920
945
  }),
921
946
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: SettingsRow_module_css_default.previewScrim }),
922
947
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -960,7 +985,7 @@ window.__ModuleLoader__.load({
960
985
  }),
961
986
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
962
987
  className: SettingsRow_module_css_default.hint,
963
- children: t("background.uploadMinetypes")
988
+ children: t("background.uploadMediaTypes")
964
989
  }),
965
990
  draft.uploadId && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
966
991
  className: SettingsRow_module_css_default.enabledBadge,
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useState","useRef","css","useSyncExternalStore","useCallback"],"sources":["../src/client/background-css.ts","../src/settings.ts","../src/client/backdrop.ts","../src/client/locales.ts","../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../src/client/settings-client.ts","../src/client/SettingsRow.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Background CSS — the stylesheet injected once into the document when the\n * plugin first runs. It defines the behind-body wallpaper layer, the scrim,\n * and the frosted-glass panels, all driven by CSS variables the painter writes\n * onto `document.body`. Kept in a single idempotent `<style data-plugin-css>`\n * tag (mirrors the reference `dsh-wallpaper-engine` bundle).\n */\n\n/** Unique id stamped on the injected style element (dedup key). */\nexport const BACKGROUND_CSS_TAG = 'deepseek-harness-background/styles'\n\n/** Body attribute the painter sets while a wallpaper is active. */\nexport const ACTIVE_ATTR = 'data-dsh-bg'\n\n/**\n * The backdrop CSS. The wallpaper is a fixed `z-index:-2` element (below the\n * scrim at `-1`, below the UI). While active, the official surface tokens are\n * made transparent (so the wallpaper shows through every column) and the\n * opaque input / bubble surfaces turn into translucent frosted glass.\n * Selectors target authored attributes (`data-dsh-bg`, `data-composer-card`)\n * and CSS-module SUFFIX conventions (`_bubble`) — the same stable anchors the\n * reference engine uses, since hashed prefixes change across shell rebuilds.\n */\nexport const BACKGROUND_CSS = `\n .dsh-bg-layer {\n position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none;\n }\n .dsh-bg-layer .dsh-bg-image {\n width: 100%; height: 100%; display: block; border: 0; background: transparent;\n object-fit: var(--bg-object-fit, cover);\n opacity: var(--bg-opacity, 1);\n filter: blur(var(--bg-wallpaper-blur, 0px));\n /* Blur thins the edges; scale the image up to hide the fringe. */\n transform: scale(var(--bg-wallpaper-scale, 1));\n transform-origin: center;\n }\n /* Scrim is theme-aware: a WHITE veil in the light scheme (it lifts the art\n toward the near-white surface so dark text keeps contrast) and a BLACK veil\n in the dark scheme (it dims the art so light text keeps contrast). */\n .dsh-bg-scrim {\n position: fixed; inset: 0; z-index: -1; pointer-events: none;\n background: rgba(255, 255, 255, var(--bg-scrim, 0.25));\n }\n body[data-ds-dark-theme] .dsh-bg-scrim {\n background: rgba(0, 0, 0, var(--bg-scrim, 0.25));\n }\n\n /* Active: make the app frame + sidebar background transparent so all columns\n share the same wallpaper + scrim backdrop. */\n body[data-dsh-bg] {\n --dsw-alias-bg-base: transparent;\n --dsw-specific-sidebar-fill: transparent;\n }\n\n /* Frosted glass over the opaque conversation surfaces (composer + bubbles).\n The surface translucency is driven by --dsw-specific-input-major /\n --dsw-specific-bubble, which the painter writes (theme-aware) so that a\n fully-opaque panel restores the official surfaces. The blur here comes\n from --bg-glass-blur; a faint top-weighted specular sheen makes the\n translucent surfaces read as wet glass. */\n body[data-dsh-bg] [data-composer-card],\n body[data-dsh-bg] [class*=\"_bubble\"] {\n background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.05) 38%, rgba(255, 255, 255, 0.02));\n -webkit-backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.6)) brightness(var(--bg-glass-brightness, 1.04)) contrast(1.01);\n backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.6)) brightness(var(--bg-glass-brightness, 1.04)) contrast(1.01);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, var(--bg-glass-highlight, 0.32)),\n inset 0 -1px 0 rgba(255, 255, 255, 0.08),\n inset 0 0 0 0.5px rgba(255, 255, 255, 0.08),\n 0 12px 40px rgba(0, 0, 0, var(--bg-glass-shadow, 0.12));\n }\n`\n\n/**\n * Inject the backdrop stylesheet once. Safe to call repeatedly; a second call\n * is a no-op (dedup key on the style tag).\n */\nexport function injectBackgroundCss(): void {\n if (document.querySelector(`style[data-plugin-css=\"${BACKGROUND_CSS_TAG}\"]`)) return\n const tag = document.createElement('style')\n tag.dataset.plugin = 'deepseek-harness-background'\n tag.dataset.pluginCss = BACKGROUND_CSS_TAG\n tag.textContent = BACKGROUND_CSS\n document.head.appendChild(tag)\n}\n","/** Durable background settings — pure constants/types shared by both halves. */\r\n\r\n/** Settings namespace owned by the background plugin. */\r\nexport const BACKGROUND_SETTINGS_NAMESPACE = 'ui-background'\r\n\r\n/**\r\n * The schema's field names, in schema order. Used as a whitelist when a\r\n * section write commits: the settings layer keeps unknown keys in the user\r\n * document (a legacy schema field would linger forever under a merge-only\r\n * write), so the write path strips everything outside this list.\r\n */\r\nexport const BACKGROUND_SETTINGS_FIELDS = [\r\n 'enabled', 'uploadId', 'url', 'opacity', 'scrim',\r\n 'panelOpacity', 'blur', 'wallpaperBlur', 'fit',\r\n] as const\r\n\r\n/** Browser-facing base path of the background API (shared with the host routes). */\r\nexport const BACKGROUND_API_PREFIX = '/api/bg-wallpaper'\r\n\r\n/** Minimum scrim opacity (0 = no veil at all). */\r\nexport const SCRIM_MIN = 0\r\n\r\n/** Maximum scrim opacity (a nearly opaque veil keeps text legible over any art). */\r\nexport const SCRIM_MAX = 0.95\r\n\r\n/** Default scrim strength applied over user art (a light readability veil). */\r\nexport const DEFAULT_SCRIM = 0.25\r\n\r\n/** Default background image opacity (1 = fully opaque). */\r\nexport const DEFAULT_OPACITY = 1\r\n\r\n/** Minimum background image opacity (0 = fully invisible). */\r\nexport const OPACITY_MIN = 0\r\n\r\n/** Maximum background image opacity (1 = fully opaque). */\r\nexport const OPACITY_MAX = 1\r\n\r\n/** Default panel opacity (0..1); 1 = fully opaque official surfaces, 0 = fully transparent glass. */\r\nexport const DEFAULT_PANEL_OPACITY = 0.15\r\n\r\n/** Minimum panel opacity. */\r\nexport const PANEL_OPACITY_MIN = 0\r\n\r\n/** Maximum panel opacity (opaque — effectively disables the glass). */\r\nexport const PANEL_OPACITY_MAX = 1\r\n\r\n/** Default frosted-glass blur radius in px (0 disables backdrop-filter). */\r\nexport const DEFAULT_BLUR = 16\r\n\r\n/** Minimum glass blur radius. */\r\nexport const BLUR_MIN = 0\r\n\r\n/** Maximum glass blur radius. */\r\nexport const BLUR_MAX = 40\r\n\r\n/** Default wallpaper blur radius in px (blurs the image itself). */\r\nexport const DEFAULT_WALLPAPER_BLUR = 0\r\n\r\n/** Maximum wallpaper blur radius. */\r\nexport const WALLPAPER_BLUR_MAX = 60\r\n\r\n/** Default background rendering mode. */\r\nexport const DEFAULT_FIT = 'cover'\r\n\r\n/** Image positioning modes. `cover` fills the frame (cropping as needed); `contain` fits the whole image inside the frame (letterboxing). */\r\nexport const FIT_MODES = ['cover', 'contain'] as const\r\n\r\n/** Background rendering mode. */\r\nexport type BackgroundFit = typeof FIT_MODES[number]\r\n\r\n/**\r\n * User-owned background section, resolved from schema defaults + user layer.\r\n * The active source is either a local upload id (host serves it via\r\n * `/api/bg-wallpaper/image/<id>`) or a raw HTTP(S) image URL the browser\r\n * loads; exactly one of `uploadId` / `url` is set — when both are empty no\r\n * background renders.\r\n */\r\nexport interface BackgroundSettings {\r\n /** Whether the custom background renders at all. */\r\n enabled: boolean\r\n /** Local upload id when the active source is an uploaded file. */\r\n uploadId: string\r\n /** Image URL when the active source is a remote URL. */\r\n url: string\r\n /** Background image opacity (0..1); the image fades toward the surface color. */\r\n opacity: number\r\n /** Readability scrim strength (0..1) layered between the art and the UI. */\r\n scrim: number\r\n /** Panel transparency (0..1): 0 = fully transparent surfaces, 1 = unaffected. */\r\n panelOpacity: number\r\n /** Frosted-glass blur radius on opaque surfaces (px; 0 disables). */\r\n blur: number\r\n /** Blur of the wallpaper image itself (px; 0 disables). */\r\n wallpaperBlur: number\r\n /** How the image fits the frame. */\r\n fit: BackgroundFit\r\n}\r\n","/**\n * Background painter — renders the user background behind the whole app frame:\n * a fixed `z-index:-2` wallpaper element plus a `z-index:-1` scrim on\n * `document.body`, driven by the `data-dsh-bg` body attribute and a set of\n * CSS variables. It also applies the frosted-glass surface tokens so the\n * wallpaper shows through translucent input / bubble panels.\n *\n * Retraction discipline mirrors the reference skin plugins: every property /\n * DOM node this painter owns is restored to its prior state on dispose, and\n * the `data-ds-dark-theme` observer is disconnected.\n */\n\nimport { ACTIVE_ATTR, injectBackgroundCss } from './background-css.ts'\nimport { BACKGROUND_API_PREFIX, type BackgroundSettings } from '../settings.ts'\n\n/** Class names the layer and scrim elements carry. */\nconst LAYER_CLASS = 'dsh-bg-layer'\nconst IMAGE_CLASS = 'dsh-bg-image'\nconst SCRIM_CLASS = 'dsh-bg-scrim'\n\n/** Surface tokens this painter overwrites (restored on dispose). */\nconst SURFACE_TOKENS = [\n '--dsw-specific-input-major',\n '--dsw-specific-bubble',\n] as const\n\n/** Fraction of the official white surface alpha kept at the most transparent panel setting. */\nconst GLASS_MIN_ALPHA = 0.05\n\n/**\n * Resolve the effective background image url for a section.\n * @param settings - resolved background section.\n * @returns the image url, or '' when no source is set.\n */\nexport function backgroundImageUrl(settings: BackgroundSettings, uploadBase: string): string {\n if (settings.uploadId) return `${uploadBase}/image/${settings.uploadId}`\n return settings.url\n}\n\n/**\n * Manage the behind-body wallpaper + scrim layers and the glass surface.\n */\nexport class BackgroundPainter {\n private layer: HTMLDivElement | null = null\n private img: HTMLImageElement | null = null\n private scrim: HTMLDivElement | null = null\n private observer: MutationObserver | undefined\n private active = false\n private settings: BackgroundSettings | undefined\n private readonly savedTokens = new Map<string, string>()\n private readonly savedVars = new Map<string, string>()\n\n /** Remember + hijack a property only once so dispose restores the original. */\n private rememberOnce(prop: string): void {\n if (this.savedTokens.has(prop) || this.savedVars.has(prop)) return\n const value = document.body.style.getPropertyValue(prop)\n if (SURFACE_TOKENS.includes(prop as (typeof SURFACE_TOKENS)[number])) {\n this.savedTokens.set(prop, value)\n } else {\n this.savedVars.set(prop, value)\n }\n }\n\n /**\n * Apply the latest settings. Idempotent.\n * @param settings - resolved background section.\n * @param uploadBase - the API base for resolving uploads.\n */\n apply(settings: BackgroundSettings, uploadBase: string): void {\n this.settings = settings\n if (!settings.enabled) {\n this.dispose()\n return\n }\n injectBackgroundCss()\n this.rememberOnce('--dsw-alias-bg-base')\n this.rememberOnce('--dsw-specific-sidebar-fill')\n document.body.style.setProperty('--dsw-alias-bg-base', 'transparent')\n document.body.style.setProperty('--dsw-specific-sidebar-fill', 'transparent')\n\n const url = backgroundImageUrl(settings, uploadBase)\n if (url === '') {\n // No source: leave the frame alone, restore the removed layers and any\n // glass-token overrides we may have written on a previous apply.\n this.applySurfaceGlass(settings, true)\n this.active = false\n this.removeLayers()\n document.body.removeAttribute(ACTIVE_ATTR)\n return\n }\n\n // Wallpaper layer + scrim.\n if (!this.layer) {\n this.layer = document.createElement('div')\n this.layer.className = LAYER_CLASS\n this.img = document.createElement('img')\n this.img.className = IMAGE_CLASS\n this.img.alt = ''\n this.layer.appendChild(this.img)\n document.body.appendChild(this.layer)\n }\n if (this.img && this.img.src !== url) this.img.src = url\n\n if (!this.scrim) {\n this.scrim = document.createElement('div')\n this.scrim.className = SCRIM_CLASS\n document.body.appendChild(this.scrim)\n }\n document.body.setAttribute(ACTIVE_ATTR, 'on')\n this.active = true\n\n // Push the knobs into CSS variables.\n const s = document.body.style\n this.setVar('--bg-object-fit', settings.fit)\n this.setVar('--bg-opacity', String(settings.opacity))\n this.setVar('--bg-wallpaper-blur', `${settings.wallpaperBlur}px`)\n this.setVar('--bg-wallpaper-scale', (1 + settings.wallpaperBlur * 0.006).toFixed(4))\n this.setVar('--bg-scrim', String(settings.scrim))\n this.setVar('--bg-glass-blur', `${settings.blur}px`)\n this.setVar('--bg-glass-saturate', String(1.15 + settings.blur * 0.03))\n\n // Glass surface: theme-aware translucent token (panelOpacity === 1 keeps\n // the official opaque surfaces and disables the blur).\n this.applySurfaceGlass(settings, false)\n\n // Theme observer: repaint the glass tokens when the light/dark scheme\n // flips (the surface translucency differs between schemes). Disconnects\n // on dispose.\n if (!this.observer) {\n this.observer = new MutationObserver(() => this.applySurfaceGlass(this.settings, false))\n this.observer.observe(document.body, { attributes: true, attributeFilter: ['data-ds-dark-theme'] })\n }\n }\n\n/** Write one owned CSS variable onto body. */\nprivate setVar(name: string, value: string): void {\n this.rememberOnce(name)\n document.body.style.setProperty(name, value)\n}\n\n/**\n * Update one effect knob without re-running the full apply. This is the hot\n * path for slider drags (many events per second): only the single CSS\n * variable changes, the DOM layers are untouched.\n * @param key - the knob to update.\n * @param value - its new value in its canonical unit.\n */\nsetKnob(key: 'opacity' | 'scrim' | 'blur' | 'wallpaperBlur' | 'fit', value: number | BackgroundSettings['fit']): void {\n if (this.settings === undefined || !this.settings.enabled) return\n switch (key) {\n case 'opacity':\n this.setVar('--bg-opacity', String(value))\n break\n case 'scrim':\n this.setVar('--bg-scrim', String(value))\n break\n case 'blur': {\n this.setVar('--bg-glass-blur', `${value}px`)\n this.setVar('--bg-glass-saturate', String(1.15 + Number(value) * 0.03))\n break\n }\n case 'wallpaperBlur': {\n this.setVar('--bg-wallpaper-blur', `${value}px`)\n this.setVar('--bg-wallpaper-scale', (1 + Number(value) * 0.006).toFixed(4))\n break\n }\n case 'fit':\n this.setVar('--bg-object-fit', String(value))\n break\n }\n}\n\n /**\n * Apply the translucent-glass surface tokens (or restore the official ones\n * when the panel is fully opaque / there is no background).\n * @param settings - resolved section (for panelOpacity).\n * @param forceRestore - true to restore official surfaces regardless (used\n * when no source is set, so leftover glass tokens never linger).\n */\n private applySurfaceGlass(settings: BackgroundSettings | undefined, forceRestore: boolean): void {\n const s = document.body.style\n const inDark = document.body.dataset.dsDarkTheme !== undefined\n // Always remember the original surface values so a restore / dispose\n // returns them cleanly.\n for (const token of SURFACE_TOKENS) this.rememberOnce(token)\n\n const restore = () => {\n for (const token of SURFACE_TOKENS) {\n const original = this.savedTokens.get(token)\n if (original !== undefined && original !== '') s.setProperty(token, original)\n else s.removeProperty(token)\n }\n }\n\n if (forceRestore || settings === undefined || settings.panelOpacity >= 1) {\n restore()\n // No blur when glass is off.\n this.setVar('--bg-glass-blur', '0px')\n return\n }\n\n // glassAlpha brightens from GLASS_MIN_ALPHA at opacity 0 toward 0.9 at\n // opacity 1. Dark scheme uses a lower white alpha (the wallpaper stays\n // visible without washing the surface), mirroring the reference engine.\n const alpha = GLASS_MIN_ALPHA + settings.panelOpacity * (0.9 - GLASS_MIN_ALPHA)\n const clamped = Math.max(0, Math.min(0.9, alpha))\n const factor = inDark ? 0.4 : 0.8\n s.setProperty('--dsw-specific-input-major', `rgba(255, 255, 255, ${(clamped * factor).toFixed(3)})`)\n s.setProperty('--dsw-specific-bubble', `rgba(255, 255, 255, ${(clamped * factor * 0.8).toFixed(3)})`)\n }\n\n /** Remove the wallpaper + scrim layers and clear the active attribute. */\n private removeLayers(): void {\n if (this.layer) { this.layer.remove(); this.layer = null; this.img = null }\n if (this.scrim) { this.scrim.remove(); this.scrim = null }\n }\n\n /** Restore every owned property and remove the layers. */\n dispose(): void {\n this.observer?.disconnect()\n this.observer = undefined\n this.removeLayers()\n const s = document.body.style\n document.body.removeAttribute(ACTIVE_ATTR)\n for (const [prop, value] of this.savedTokens) s.setProperty(prop, value)\n for (const [prop, value] of this.savedVars) s.setProperty(prop, value)\n this.savedTokens.clear()\n this.savedVars.clear()\n this.active = false\n this.settings = undefined\n }\n\n /** @returns whether a wallpaper is currently rendered. */\n isActive(): boolean {\n return this.active\n }\n}\n\n/** Module-level painter singleton shared by the settings row preview and the live apply. */\nexport const backgroundPainter = new BackgroundPainter()\n\n/**\n * Apply the given settings through the shared painter (idempotent). Convenience\n * used by the settings row for live previews and by the plugin apply to render\n * the persisted section.\n * @param settings - settings to apply (may be an unsaved draft for preview).\n */\nexport function paintBackground(settings: BackgroundSettings): void {\n backgroundPainter.apply(settings, BACKGROUND_API_PREFIX)\n}\n\n/** Numeric effect knobs the slider drag path repaints. */\nexport type BackgroundKnob = 'opacity' | 'scrim' | 'blur' | 'wallpaperBlur' | 'fit'\n\n/**\n * Update one effect knob on the live backdrop without re-running the full\n * apply — the hot path for slider drags (see BackgroundPainter.setKnob).\n * @param key - the knob to update.\n * @param value - its new value.\n */\nexport function paintBackgroundKnob(key: BackgroundKnob, value: number | BackgroundSettings['fit']): void {\n backgroundPainter.setKnob(key, value)\n}\n\n/** Preview variables the settings-surface preview card consumes. */\nconst PREVIEW_VARS = [\n '--bg-opacity', '--bg-scrim', '--bg-object-fit',\n '--bg-wallpaper-blur', '--bg-wallpaper-scale',\n '--bg-glass-blur', '--bg-glass-saturate', '--bg-preview-glass',\n] as const\n\n/**\n * Push the effect knobs onto one element (the settings-surface preview card)\n * so dragging a slider updates the card without touching the live backdrop.\n * The card's CSS reads the same variable names as the painter writes on body.\n * @param el - the preview element.\n * @param settings - the draft settings to render (may be unsaved).\n */\nexport function paintPreviewSurface(el: HTMLElement, settings: BackgroundSettings): void {\n const s = el.style\n s.setProperty('--bg-object-fit', settings.fit)\n s.setProperty('--bg-opacity', String(settings.opacity))\n s.setProperty('--bg-scrim', String(settings.scrim))\n s.setProperty('--bg-wallpaper-blur', `${settings.wallpaperBlur}px`)\n s.setProperty('--bg-wallpaper-scale', (1 + settings.wallpaperBlur * 0.006).toFixed(4))\n // The glass bubble mirrors the live panel surface: below panelOpacity 1 its\n // alpha follows the same theme-aware curve as --dsw-specific-bubble (so the\n // panel-opacity slider is visible in the preview); at 1 the official opaque\n // surface returns — the bubble turns opaque and the blur switches off, just\n // like applySurfaceGlass does on the real backdrop.\n const inDark = document.body.dataset.dsDarkTheme !== undefined\n if (settings.panelOpacity >= 1) {\n s.setProperty('--bg-glass-blur', '0px')\n s.setProperty('--bg-preview-glass', 'var(--dsw-alias-bg-layer-1)')\n return\n }\n s.setProperty('--bg-glass-blur', `${settings.blur}px`)\n s.setProperty('--bg-glass-saturate', String(1.15 + settings.blur * 0.03))\n const alpha = GLASS_MIN_ALPHA + settings.panelOpacity * (0.9 - GLASS_MIN_ALPHA)\n const clamped = Math.max(0, Math.min(0.9, alpha))\n const factor = inDark ? 0.4 : 0.8\n s.setProperty('--bg-preview-glass', `rgba(255, 255, 255, ${(clamped * factor * 0.8).toFixed(3)})`)\n}\n\n/** Remove every preview variable from an element (restores CSS defaults). */\nexport function clearPreviewSurface(el: HTMLElement): void {\n for (const name of PREVIEW_VARS) el.style.removeProperty(name)\n}\n","/** Locale dictionaries for the background plugin. */\n\nimport type { BackgroundCardLocaleKey } from './types.ts'\n\n/** Dictionary of the namespace this plugin owns. */\nexport type BackgroundCardDict = Record<BackgroundCardLocaleKey, string>\n\n/** Chinese copy. */\nexport const zh: BackgroundCardDict = {\n 'background.title': '自定义背景',\n 'background.description': '为整个应用界面设置背景图片,可调不透明度、遮罩、面板透明与毛玻璃模糊',\n 'background.upload': '上传图片',\n 'background.uploading': '上传中…',\n 'background.uploadMinetypes': '支持 JPG / PNG / WebP / GIF',\n 'background.urlPlaceholder': '或粘贴图片 http(s) 链接后回车',\n 'background.applyUrl': '应用链接',\n 'background.enabled': '背景已启用',\n 'background.source': '图片',\n 'background.effects': '效果',\n 'background.noPreview': '上传图片或粘贴链接后,此处实时预览效果',\n 'background.previewGlass': '毛玻璃',\n 'background.opacity': '不透明度',\n 'background.scrim': '遮罩',\n 'background.panelOpacity': '面板不透明度',\n 'background.blur': '毛玻璃模糊',\n 'background.wallpaperBlur': '壁纸模糊',\n 'background.fit': '填充方式',\n 'background.cover': '铺满',\n 'background.contain': '完整',\n 'background.clear': '清除背景',\n 'background.uploadFailed': '上传失败,请重试',\n 'background.saveFailed': '保存失败,请重试',\n}\n\n/** English copy. */\nexport const en: BackgroundCardDict = {\n 'background.title': 'Custom Background',\n 'background.description': 'Set a background image behind the whole app surface with adjustable opacity, scrim, panel transparency and glass blur',\n 'background.upload': 'Upload image',\n 'background.uploading': 'Uploading…',\n 'background.uploadMinetypes': 'JPG / PNG / WebP / GIF',\n 'background.urlPlaceholder': 'Or paste an image http(s) link and press Enter',\n 'background.applyUrl': 'Apply URL',\n 'background.enabled': 'Background on',\n 'background.source': 'Image',\n 'background.effects': 'Effects',\n 'background.noPreview': 'Upload an image or paste a link to preview live here',\n 'background.previewGlass': 'Glass',\n 'background.opacity': 'Opacity',\n 'background.scrim': 'Scrim',\n 'background.panelOpacity': 'Panel opacity',\n 'background.blur': 'Glass blur',\n 'background.wallpaperBlur': 'Wallpaper blur',\n 'background.fit': 'Fit',\n 'background.cover': 'Cover',\n 'background.contain': 'Contain',\n 'background.clear': 'Clear background',\n 'background.uploadFailed': 'Upload failed, try again',\n 'background.saveFailed': 'Save failed, try again',\n}\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * Background settings transport — reads and writes the `ui-background`\n * section, and uploads local images, through the plugin's own same-origin\n * HTTP routes (see src/routes.ts). A custom route family keeps the section\n * usable even though the api-proxy settings allowlist does not expose\n * third-party namespaces over the settings RPC. One module-level client\n * serves the painter and the settings row.\n */\n\nimport type { BackgroundSettings } from '../settings.ts'\nimport { BACKGROUND_API_PREFIX } from '../settings.ts'\n\n/** One snapshot of the section as last seen by the transport. */\nexport interface SettingsSnapshot {\n status: 'loading' | 'ready' | 'error'\n value?: BackgroundSettings\n}\n\ntype Listener = () => void\n\n/**\n * Observable settings client (getSnapshot/subscribe — the uSES currency).\n * Methods are arrow properties so React's useSyncExternalStore can call them\n * without an owning `this`.\n */\nexport class SettingsClient {\n private snapshot: SettingsSnapshot = { status: 'loading' }\n private readonly listeners = new Set<Listener>()\n /** Monotonic request id; the latest save wins even when POSTs interleave. */\n private saveSeq = 0\n /** Pending save promise (used by the row's flush-on-unmount). */\n private pending: Promise<boolean> | undefined\n\n /** @returns the current sync snapshot (stable reference until the next change). */\n getSnapshot = (): SettingsSnapshot => this.snapshot\n\n /** Observe snapshot replacements. @returns the disposer removing this listener. */\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n private notify(): void {\n for (const listener of this.listeners) listener()\n }\n\n /** Await any in-flight save (used before teardown so edits are not dropped). */\n async flush(): Promise<void> {\n await this.pending\n }\n\n /** Fetch the section from the host. */\n async load(): Promise<void> {\n try {\n const response = await fetch(`${BACKGROUND_API_PREFIX}/settings`)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const body = await response.json() as { ok: boolean; value?: BackgroundSettings }\n if (!body.ok || body.value === undefined) throw new Error('unexpected payload')\n this.snapshot = { status: 'ready', value: body.value }\n } catch {\n this.snapshot = { status: 'error' }\n }\n this.notify()\n }\n\n /**\n * Persist the section through the host route. Concurrent calls are\n * serialized by a request sequence: an older save that resolves after a\n * newer one must not clobber the newer snapshot with its stale value.\n * @param section - the complete next section.\n * @returns whether the host accepted the write.\n */\n async save(section: BackgroundSettings): Promise<boolean> {\n const seq = ++this.saveSeq\n const run = async (): Promise<boolean> => {\n try {\n const response = await fetch(`${BACKGROUND_API_PREFIX}/settings`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(section),\n })\n const body = await response.json() as { ok: boolean; value?: BackgroundSettings }\n if (!response.ok || !body.ok || body.value === undefined) return false\n // Discard the response of a superseded save — it reflects an older\n // document that must not overwrite the latest one.\n if (seq !== this.saveSeq) return true\n this.snapshot = { status: 'ready', value: body.value }\n this.notify()\n return true\n } catch {\n return false\n }\n }\n const promise = run()\n this.pending = promise\n return promise\n }\n\n /**\n * Upload a local image file; on success stores its id into the snapshot and\n * returns the resolved background url (or null on failure).\n * @param file - the chosen image file.\n * @returns the resolved image url, or null.\n */\n async upload(file: File): Promise<string | null> {\n try {\n const response = await fetch(`${BACKGROUND_API_PREFIX}/upload`, {\n method: 'POST',\n headers: { 'content-type': file.type },\n body: file,\n })\n const body = await response.json() as { ok: boolean; id?: string; url?: string }\n if (!response.ok || !body.ok || body.id === undefined || body.url === undefined) return null\n return body.url ?? null\n } catch {\n return null\n }\n }\n}\n\n/** Shared client for the painter and the settings row. */\nexport const settingsClient = new SettingsClient()\n","/**\n * Background settings row — the editable form rendered inside the General\n * settings section (`settings.general.item` slot), in the same visual area as\n * the official Appearance row. Layout mirrors the reference wallpaper engine\n * picker (preview surface + grouped sections), styled with the harness alias\n * tokens so it reads as part of the settings chrome.\n *\n * Interaction model (fixes the previous jank):\n * - Sliders STEP in 5% (or 2px/5px) increments, so dragging snaps between\n * discrete values instead of firing dozens of sub-percent updates.\n * - While dragging, only the CSS variables change: the slider holds its own\n * state (no parent re-render) and the preview card repaints in isolation.\n * React's onChange maps to the native `input` event and would fire on every\n * step, so a commit is deferred to release (pointerup / keyup / blur) and\n * debounced — one POST per gesture, never per tick.\n * - A `lastSavedRef` distinguishes our own saves from external changes, so\n * the snapshot sync can never clobber a draft the user is still editing.\n */\n\nimport { useCallback, useEffect, useRef, useState, type ChangeEvent } from 'react'\nimport { useSyncExternalStore } from 'react'\nimport type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport clsx from 'clsx'\nimport {\n BACKGROUND_API_PREFIX, BLUR_MAX, BLUR_MIN, DEFAULT_BLUR, DEFAULT_FIT,\n DEFAULT_OPACITY, DEFAULT_PANEL_OPACITY, DEFAULT_SCRIM, DEFAULT_WALLPAPER_BLUR,\n FIT_MODES, OPACITY_MAX, OPACITY_MIN, PANEL_OPACITY_MAX, PANEL_OPACITY_MIN,\n SCRIM_MAX, SCRIM_MIN, WALLPAPER_BLUR_MAX, type BackgroundFit,\n type BackgroundSettings,\n} from '../settings.ts'\nimport { clearPreviewSurface, paintBackground, paintBackgroundKnob, paintPreviewSurface } from './backdrop.ts'\nimport { settingsClient, type SettingsSnapshot } from './settings-client.ts'\nimport css from './SettingsRow.module.css'\n\n/** Full component props: runtime share + locale seat. */\nexport type BackgroundRowProps =\n PropsRuntime<'settings.general.item'> & PropsLocale<'ui-background'>\n\n/** Defaults mirror the host schema (also the pre-load draft fallback). */\nconst DEFAULTS: BackgroundSettings = {\n enabled: false,\n uploadId: '',\n url: '',\n opacity: DEFAULT_OPACITY,\n scrim: DEFAULT_SCRIM,\n panelOpacity: DEFAULT_PANEL_OPACITY,\n blur: DEFAULT_BLUR,\n wallpaperBlur: DEFAULT_WALLPAPER_BLUR,\n fit: DEFAULT_FIT,\n}\n\n/** The numeric field keys a slider edits. */\ntype NumField = 'opacity' | 'scrim' | 'panelOpacity' | 'blur' | 'wallpaperBlur'\n\n/** Per-field slider geometry: 5% steps for ratios, fine px steps for radii so\n * the two blur sliders feel smooth near small values. Bounds come from the\n * shared settings constants so the schema boundary and the UI cannot drift. */\nconst SLIDER_SPEC: Record<NumField, { min: number; max: number; step: number; unit: 'percent' | 'px' }> = {\n opacity: { min: OPACITY_MIN, max: OPACITY_MAX, step: 0.05, unit: 'percent' },\n scrim: { min: SCRIM_MIN, max: SCRIM_MAX, step: 0.05, unit: 'percent' },\n panelOpacity: { min: PANEL_OPACITY_MIN, max: PANEL_OPACITY_MAX, step: 0.05, unit: 'percent' },\n blur: { min: BLUR_MIN, max: BLUR_MAX, step: 1, unit: 'px' },\n wallpaperBlur: { min: BLUR_MIN, max: WALLPAPER_BLUR_MAX, step: 2, unit: 'px' },\n}\n\n/** Derive the bare upload id from a resolve url like /api/bg-wallpaper/image/<id>. */\nfunction stripId(url: string): string {\n const prefix = `${BACKGROUND_API_PREFIX}/image/`\n return url.startsWith(prefix) ? url.slice(prefix.length) : ''\n}\n\n/** Read the settings store (uSES-friendly). */\nfunction readSnapshot(): SettingsSnapshot {\n return settingsClient.getSnapshot()\n}\n\n/** Field-wise equality for distinguishing our own saves from external changes. */\nfunction sameSettings(a: BackgroundSettings, b: BackgroundSettings): boolean {\n return a.enabled === b.enabled && a.uploadId === b.uploadId && a.url === b.url\n && a.opacity === b.opacity && a.scrim === b.scrim && a.panelOpacity === b.panelOpacity\n && a.blur === b.blur && a.wallpaperBlur === b.wallpaperBlur && a.fit === b.fit\n}\n\n/**\n * A stepped range control. While dragging it re-renders only itself: the value\n * is local state, `onLive(name, value)` repaints (CSS variables, no React\n * state), and `onCommit()` fires once on release through a debounced save.\n */\nfunction ControlSlider(props: {\n name: NumField\n label: string\n value: number\n onLive: (name: NumField, value: number) => void\n onCommit: () => void\n}) {\n const { name, label, value, onLive, onCommit } = props\n const spec = SLIDER_SPEC[name]\n const [local, setLocal] = useState(value)\n const localRef = useRef(value)\n const dragging = useRef(false)\n const onLiveRef = useRef(onLive)\n const onCommitRef = useRef(onCommit)\n onLiveRef.current = onLive\n onCommitRef.current = onCommit\n\n // Adopt an external value (load / save) when not mid-drag.\n useEffect(() => {\n if (!dragging.current && localRef.current !== value) {\n localRef.current = value\n setLocal(value)\n }\n }, [value])\n\n const handleLive = (e: ChangeEvent<HTMLInputElement>): void => {\n dragging.current = true\n const v = Number(e.currentTarget.value)\n localRef.current = v\n setLocal(v)\n syncTrackFill(e.currentTarget)\n onLiveRef.current(name, v)\n }\n\n const handleCommit = (): void => {\n if (!dragging.current) return\n dragging.current = false\n onCommitRef.current()\n }\n\n const text = spec.unit === 'percent'\n ? `${Math.round(local * 100)}%`\n : `${local}px`\n\n return (\n <div className={css.sliderRow}>\n <span className={css.sliderLabel}>{label}</span>\n <input\n type=\"range\"\n className={css.slider}\n ref={(el) => {\n // Sync the \"filled\" track fraction on mount and on every external\n // value adoption (the webkit track reads --bg-fill; moz draws its\n // own ::-moz-range-progress).\n if (el) syncTrackFill(el)\n }}\n min={spec.min} max={spec.max} step={spec.step}\n value={String(local)}\n aria-label={label}\n onInput={handleLive}\n onPointerUp={handleCommit}\n onKeyUp={(e) => {\n if (e.key.startsWith('Arrow') || e.key === 'Home' || e.key === 'End'\n || e.key === 'PageUp' || e.key === 'PageDown') handleCommit()\n }}\n onBlur={handleCommit}\n />\n <span className={css.sliderValue}>{text}</span>\n </div>\n )\n}\n\n/** Write the filled-track fraction (%) of a range input into its --bg-fill\n * variable (webkit has no range-progress pseudo-element; moz draws its own). */\nfunction syncTrackFill(el: HTMLInputElement): void {\n const min = Number(el.min)\n const max = Number(el.max)\n const value = Number(el.value)\n const pct = max === min ? 0 : ((value - min) / (max - min)) * 100\n el.style.setProperty('--bg-fill', `${pct}%`)\n}\n\n/**\n * The background settings row. A draft mirrors the persisted section; dragging\n * repaints the preview card and the live backdrop through CSS variables, and a\n * release commits once through the plugin's own host route.\n */\nexport function BackgroundSettingsRow({ t }: BackgroundRowProps) {\n const snapshot = useSyncExternalStore(settingsClient.subscribe, readSnapshot)\n const [draft, setDraft] = useState<BackgroundSettings>(DEFAULTS)\n const [uploading, setUploading] = useState(false)\n const [urlText, setUrlText] = useState('')\n const [error, setError] = useState('')\n const fileInput = useRef<HTMLInputElement>(null)\n const previewRef = useRef<HTMLDivElement>(null)\n // The latest draft, read by every handler (never a stale closure).\n const draftRef = useRef<BackgroundSettings>(DEFAULTS)\n // True while a drag/gesture is in flight: hold off snapshot sync.\n const editingRef = useRef(false)\n // The last section this row saved; external changes are snapshots that differ.\n const lastSavedRef = useRef<BackgroundSettings | null>(null)\n // True while an edit has not yet reached the host (drives flush-on-unmount).\n const dirtyRef = useRef(false)\n const commitTimer = useRef<number | undefined>(undefined)\n const editingTimer = useRef<number | undefined>(undefined)\n\n /** Repaint the live backdrop + preview card from one draft (no React state). */\n const paintLive = useCallback((next: BackgroundSettings): void => {\n paintBackground(next)\n if (previewRef.current) paintPreviewSurface(previewRef.current, next)\n }, [])\n\n /** Mark the row as being edited; clears after a quiet 600ms window. */\n const markEditing = useCallback((): void => {\n editingRef.current = true\n clearTimeout(editingTimer.current)\n editingTimer.current = window.setTimeout(() => { editingRef.current = false }, 600)\n }, [])\n\n /** Slider live handler (stable identity for the memoized sliders). */\n const onLive = useCallback((key: NumField, value: number): void => {\n const next = { ...draftRef.current, [key]: value }\n draftRef.current = next\n markEditing()\n // Hot path: most knobs repaint through the painter's single-variable\n // writer (no DOM layer churn). panelOpacity drives the glass surface\n // tokens, which need the full apply.\n if (key === 'panelOpacity') {\n paintBackground(next)\n } else {\n paintBackgroundKnob(key, value)\n }\n if (previewRef.current) paintPreviewSurface(previewRef.current, next)\n }, [markEditing])\n\n /** Persist one section through the host route and adopt it on success. */\n const saveNow = useCallback(async (next: BackgroundSettings): Promise<void> => {\n dirtyRef.current = true\n const ok = await settingsClient.save(next)\n if (ok) dirtyRef.current = false\n setError(ok ? '' : t('background.saveFailed'))\n if (ok) {\n lastSavedRef.current = next\n draftRef.current = next\n setDraft(next)\n if (!next.uploadId) setUrlText(next.url)\n }\n }, [t])\n\n /** Debounced commit: one POST per drag gesture, from the latest draft. */\n const onCommit = useCallback((): void => {\n clearTimeout(commitTimer.current)\n commitTimer.current = window.setTimeout(() => { void saveNow(draftRef.current) }, 120)\n }, [saveNow])\n\n // Sync the draft from the persisted value when it changes externally, but\n // never overwrite a draft the user is mid-editing.\n useEffect(() => {\n if (snapshot.status !== 'ready' || snapshot.value === undefined) return\n const saved = lastSavedRef.current\n const external = saved === null || !sameSettings(saved, snapshot.value)\n if (external && !editingRef.current) {\n lastSavedRef.current = snapshot.value\n draftRef.current = snapshot.value\n setDraft(snapshot.value)\n setUrlText(snapshot.value.uploadId ? '' : snapshot.value.url)\n if (previewRef.current) paintPreviewSurface(previewRef.current, snapshot.value)\n }\n }, [snapshot])\n\n // Teardown: flush any pending debounced save before the row unmounts so a\n // quick panel close after a release cannot drop the edit.\n useEffect(() => () => {\n clearTimeout(commitTimer.current)\n clearTimeout(editingTimer.current)\n if (dirtyRef.current) void settingsClient.flush()\n }, [])\n\n const handleFile = async (file?: File): Promise<void> => {\n if (!file) return\n markEditing()\n setUploading(true)\n setError('')\n try {\n const url = await settingsClient.upload(file)\n if (url === null) {\n setError(t('background.uploadFailed'))\n return\n }\n const next: BackgroundSettings = { ...draftRef.current, uploadId: stripId(url), url: '', enabled: true }\n draftRef.current = next\n setDraft(next)\n setUrlText('')\n paintLive(next)\n await saveNow(next)\n } finally {\n setUploading(false)\n }\n }\n\n const handleUrlSubmit = (): void => {\n const url = urlText.trim()\n if (!/^https?:\\/\\//i.test(url)) {\n setError(t('background.saveFailed'))\n return\n }\n markEditing()\n const next: BackgroundSettings = { ...draftRef.current, uploadId: '', url, enabled: true }\n draftRef.current = next\n setDraft(next)\n paintLive(next)\n void saveNow(next)\n }\n\n const handleClear = (): void => {\n const next: BackgroundSettings = { ...draftRef.current, uploadId: '', url: '', enabled: false }\n draftRef.current = next\n setDraft(next)\n setUrlText('')\n if (previewRef.current) clearPreviewSurface(previewRef.current)\n paintBackground(next)\n void saveNow(next)\n }\n\n const handleFit = (mode: BackgroundFit): void => {\n markEditing()\n const next: BackgroundSettings = { ...draftRef.current, fit: mode }\n draftRef.current = next\n setDraft(next)\n paintLive(next)\n void saveNow(next)\n }\n\n const sourceUrl = draft.uploadId ? `${BACKGROUND_API_PREFIX}/image/${draft.uploadId}` : draft.url\n const hasImage = sourceUrl !== ''\n\n return (\n <div className={css.row}>\n <div className={css.header}>\n <div className={css.titleRow}>\n <div className={css.title}>{t('background.title')}</div>\n {hasImage && (\n <button type=\"button\" className={clsx(css.btn, css.btnDanger, css.clearBtn)} onClick={handleClear}>\n {t('background.clear')}\n </button>\n )}\n </div>\n <div className={css.desc}>{t('background.description')}</div>\n </div>\n\n {/* Live preview surface: image + theme-aware scrim + frosted glass bubble. */}\n <div className={css.preview} ref={previewRef}>\n {hasImage\n ? (\n <>\n <img className={css.previewImg} src={sourceUrl} alt=\"\" draggable={false} />\n <div className={css.previewScrim} />\n <div className={css.previewGlass}>\n <span className={css.previewGlassText}>{t('background.previewGlass')}</span>\n </div>\n </>\n )\n : (\n <div className={css.previewEmpty}>{t('background.noPreview')}</div>\n )}\n </div>\n\n {/* Image source: upload or paste an http(s) link. */}\n <div className={css.section}>\n <div className={css.sectionLabel}>{t('background.source')}</div>\n <div className={css.sourceRow}>\n <input\n type=\"file\"\n ref={fileInput}\n hidden\n accept=\"image/jpeg,image/png,image/webp,image/gif\"\n onChange={(e) => {\n void handleFile(e.currentTarget.files?.[0])\n e.currentTarget.value = ''\n }}\n />\n <button\n type=\"button\"\n className={clsx(css.btn, css.btnPrimary)}\n disabled={uploading}\n onClick={() => fileInput.current?.click()}\n >\n {uploading ? t('background.uploading') : t('background.upload')}\n </button>\n <span className={css.hint}>{t('background.uploadMinetypes')}</span>\n {draft.uploadId && <span className={css.enabledBadge}>✓ {t('background.enabled')}</span>}\n </div>\n <div className={css.urlRow}>\n <input\n className={css.urlInput}\n value={urlText}\n placeholder={t('background.urlPlaceholder')}\n onKeyDown={(e) => { if (e.key === 'Enter') handleUrlSubmit() }}\n onChange={(e) => setUrlText(e.currentTarget.value)}\n />\n <button\n type=\"button\"\n className={css.btn}\n disabled={!urlText.trim()}\n onClick={handleUrlSubmit}\n >\n {t('background.applyUrl')}\n </button>\n </div>\n </div>\n\n {/* Effects: stepped sliders + fit segmented control. */}\n <div className={css.section}>\n <div className={css.sectionLabel}>{t('background.effects')}</div>\n <ControlSlider name=\"opacity\" label={t('background.opacity')} value={draft.opacity} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"scrim\" label={t('background.scrim')} value={draft.scrim} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"panelOpacity\" label={t('background.panelOpacity')} value={draft.panelOpacity} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"blur\" label={t('background.blur')} value={draft.blur} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"wallpaperBlur\" label={t('background.wallpaperBlur')} value={draft.wallpaperBlur} onLive={onLive} onCommit={onCommit} />\n <div className={css.segRow}>\n <span className={css.segLabel}>{t('background.fit')}</span>\n <div className={css.segGroup} role=\"group\" aria-label={t('background.fit')}>\n {FIT_MODES.map((mode) => (\n <button\n key={mode}\n type=\"button\"\n className={clsx(css.segBtn, draft.fit === mode && css.segActive)}\n onClick={() => handleFit(mode)}\n >\n {t(mode === 'cover' ? 'background.cover' : 'background.contain')}\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {error && <div className={css.error} role=\"alert\">{error}</div>}\n\n {hasImage && (\n <div className={css.clearRow}>\n <button type=\"button\" className={clsx(css.btn, css.btnDanger)} onClick={handleClear}>\n {t('background.clear')}\n </button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Browser half of the background plugin: paints the background behind the\n * whole app frame and provides the settings surface — a live row inside the\n * official General settings section (next to the Appearance row). Data flows\n * through the plugin's own host route (`/api/bg-wallpaper/*`), not the\n * settings RPC, because the api-proxy settings allowlist does not expose\n * third-party namespaces. Retraction discipline mirrors the skin plugins:\n * every property and DOM node the painter owns is restored on dispose.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { backgroundPainter, paintBackground } from './backdrop.ts'\nimport { en, zh } from './locales.ts'\nimport { BackgroundSettingsRow } from './SettingsRow.tsx'\nimport { settingsClient } from './settings-client.ts'\nimport type {} from './types.ts'\n\n/**\n * Service injections this entry declares: the `slots` (runtime) and `locale`\n * services only — the settings surface talks to the plugin's own host route\n * over plain same-origin fetch, so no settings/connection service is needed.\n */\nexport const inject = ['slots', 'locale']\n\n/**\n * Client plugin body: paint the persisted background live and register the\n * General-settings row.\n * @param ctx - client cordis context (slots/locale injected).\n */\nexport function apply(ctx: Context): void {\n // Paint from the plugin's own transport; repaint on every snapshot change.\n const paint = (): void => {\n const snapshot = settingsClient.getSnapshot()\n const value = snapshot.status === 'ready' ? snapshot.value : undefined\n if (value === undefined || !value.enabled) {\n backgroundPainter.dispose()\n return\n }\n paintBackground(value)\n }\n const unsubscribe = settingsClient.subscribe(paint)\n void settingsClient.load()\n\n ctx.effect(() => ctx.locale.register('ui-background', { zh, en }), 'deepseek-harness-background: dictionaries')\n\n // Live settings row in the General section (same slot as the Appearance row).\n ctx.slots.inject('settings.general.item', () => ctx.slots.register({\n name: 'settings.general.item',\n id: 'deepseek-harness-background',\n order: 600,\n locale: 'ui-background',\n }, BackgroundSettingsRow))\n\n ctx.effect(() => () => {\n unsubscribe()\n backgroundPainter.dispose()\n }, 'deepseek-harness-background: background surface')\n}\n"],"x_google_ignoreList":[4],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useState","useRef","css","useSyncExternalStore","useCallback"],"sources":["../src/client/background-css.ts","../src/settings.ts","../src/client/backdrop.ts","../src/client/locales.ts","../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../src/client/settings-client.ts","../src/client/SettingsRow.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Background CSS — the stylesheet injected once into the document when the\n * plugin first runs. It defines the behind-body wallpaper layer, the scrim,\n * and the frosted-glass panels, all driven by CSS variables the painter writes\n * onto `document.body`. Kept in a single idempotent `<style data-plugin-css>`\n * tag (mirrors the reference `dsh-wallpaper-engine` bundle).\n */\n\n/** Unique id stamped on the injected style element (dedup key). */\nexport const BACKGROUND_CSS_TAG = 'deepseek-harness-background/styles'\n\n/** Body attribute the painter sets while a wallpaper is active. */\nexport const ACTIVE_ATTR = 'data-dsh-bg'\n\n/**\n * The backdrop CSS. The wallpaper is a fixed `z-index:-2` element (below the\n * scrim at `-1`, below the UI). While active, the official surface tokens are\n * made transparent (so the wallpaper shows through every column) and the\n * opaque input / bubble surfaces turn into translucent frosted glass.\n * Selectors target authored attributes (`data-dsh-bg`, `data-composer-card`)\n * and CSS-module SUFFIX conventions (`_bubble`) — the same stable anchors the\n * reference engine uses, since hashed prefixes change across shell rebuilds.\n */\nexport const BACKGROUND_CSS = `\n .dsh-bg-layer {\n position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none;\n }\n .dsh-bg-layer .dsh-bg-image {\n width: 100%; height: 100%; display: block; border: 0; background: transparent;\n object-fit: var(--bg-object-fit, cover);\n opacity: var(--bg-opacity, 1);\n filter: blur(var(--bg-wallpaper-blur, 0px));\n /* Blur thins the edges; scale the image up to hide the fringe. */\n transform: scale(var(--bg-wallpaper-scale, 1));\n transform-origin: center;\n }\n /* Scrim is theme-aware: a WHITE veil in the light scheme (it lifts the art\n toward the near-white surface so dark text keeps contrast) and a BLACK veil\n in the dark scheme (it dims the art so light text keeps contrast). */\n .dsh-bg-scrim {\n position: fixed; inset: 0; z-index: -1; pointer-events: none;\n background: rgba(255, 255, 255, var(--bg-scrim, 0.25));\n }\n body[data-ds-dark-theme] .dsh-bg-scrim {\n background: rgba(0, 0, 0, var(--bg-scrim, 0.25));\n }\n\n /* Active: make the app frame + sidebar background transparent so all columns\n share the same wallpaper + scrim backdrop. */\n body[data-dsh-bg] {\n --dsw-alias-bg-base: transparent;\n --dsw-specific-sidebar-fill: transparent;\n }\n\n /* Frosted glass over the opaque conversation surfaces (composer + bubbles).\n The surface translucency is driven by --dsw-specific-input-major /\n --dsw-specific-bubble, which the painter writes (theme-aware) so that a\n fully-opaque panel restores the official surfaces. The blur here comes\n from --bg-glass-blur; a faint top-weighted specular sheen makes the\n translucent surfaces read as wet glass. */\n body[data-dsh-bg] [data-composer-card],\n body[data-dsh-bg] [class*=\"_bubble\"] {\n background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.05) 38%, rgba(255, 255, 255, 0.02));\n -webkit-backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.6)) brightness(var(--bg-glass-brightness, 1.04)) contrast(1.01);\n backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.6)) brightness(var(--bg-glass-brightness, 1.04)) contrast(1.01);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, var(--bg-glass-highlight, 0.32)),\n inset 0 -1px 0 rgba(255, 255, 255, 0.08),\n inset 0 0 0 0.5px rgba(255, 255, 255, 0.08),\n 0 12px 40px rgba(0, 0, 0, var(--bg-glass-shadow, 0.12));\n }\n`\n\n/**\n * Inject the backdrop stylesheet once. Safe to call repeatedly; a second call\n * is a no-op (dedup key on the style tag).\n */\nexport function injectBackgroundCss(): void {\n if (document.querySelector(`style[data-plugin-css=\"${BACKGROUND_CSS_TAG}\"]`)) return\n const tag = document.createElement('style')\n tag.dataset.plugin = 'deepseek-harness-background'\n tag.dataset.pluginCss = BACKGROUND_CSS_TAG\n tag.textContent = BACKGROUND_CSS\n document.head.appendChild(tag)\n}\n","/** Durable background settings — pure constants/types shared by both halves. */\r\n\r\n/** Settings namespace owned by the background plugin. */\r\nexport const BACKGROUND_SETTINGS_NAMESPACE = 'ui-background'\r\n\r\n/**\r\n * The schema's field names, in schema order. Used as a whitelist when a\r\n * section write commits: the settings layer keeps unknown keys in the user\r\n * document (a legacy schema field would linger forever under a merge-only\r\n * write), so the write path strips everything outside this list.\r\n */\r\nexport const BACKGROUND_SETTINGS_FIELDS = [\r\n 'enabled', 'uploadId', 'url', 'opacity', 'scrim',\r\n 'panelOpacity', 'blur', 'wallpaperBlur', 'fit',\r\n] as const\r\n\r\n/** Browser-facing base path of the background API (shared with the host routes). */\r\nexport const BACKGROUND_API_PREFIX = '/api/bg-wallpaper'\r\n\r\n/** Minimum scrim opacity (0 = no veil at all). */\r\nexport const SCRIM_MIN = 0\r\n\r\n/** Maximum scrim opacity (1 = fully opaque veil — hides the wallpaper entirely).\r\n * Kept at 1 so every percent-unit slider shares one 0..1 domain: the same\r\n * displayed percentage puts every thumb at the same fraction of its track. */\r\nexport const SCRIM_MAX = 1\r\n\r\n/** Default scrim strength applied over user art (a light readability veil). */\r\nexport const DEFAULT_SCRIM = 0.25\r\n\r\n/** Default background image opacity (1 = fully opaque). */\r\nexport const DEFAULT_OPACITY = 1\r\n\r\n/** Minimum background image opacity (0 = fully invisible). */\r\nexport const OPACITY_MIN = 0\r\n\r\n/** Maximum background image opacity (1 = fully opaque). */\r\nexport const OPACITY_MAX = 1\r\n\r\n/** Default panel opacity (0..1); 1 = fully opaque official surfaces, 0 = fully transparent glass. */\r\nexport const DEFAULT_PANEL_OPACITY = 0.15\r\n\r\n/** Minimum panel opacity. */\r\nexport const PANEL_OPACITY_MIN = 0\r\n\r\n/** Maximum panel opacity (opaque — effectively disables the glass). */\r\nexport const PANEL_OPACITY_MAX = 1\r\n\r\n/** Default frosted-glass blur radius in px (0 disables backdrop-filter). */\r\nexport const DEFAULT_BLUR = 16\r\n\r\n/** Minimum glass blur radius. */\r\nexport const BLUR_MIN = 0\r\n\r\n/** Maximum glass blur radius. */\r\nexport const BLUR_MAX = 40\r\n\r\n/** Default wallpaper blur radius in px (blurs the image itself). */\r\nexport const DEFAULT_WALLPAPER_BLUR = 0\r\n\r\n/** Maximum wallpaper blur radius. */\r\nexport const WALLPAPER_BLUR_MAX = 60\r\n\r\n/** Default background rendering mode. */\r\nexport const DEFAULT_FIT = 'cover'\r\n\r\n/** Image positioning modes. `cover` fills the frame (cropping as needed); `contain` fits the whole image inside the frame (letterboxing). */\r\nexport const FIT_MODES = ['cover', 'contain'] as const\r\n\r\n/** Background rendering mode. */\r\nexport type BackgroundFit = typeof FIT_MODES[number]\r\n\r\n/**\r\n * User-owned background section, resolved from schema defaults + user layer.\r\n * The active source is either a local upload id (host serves it via\r\n * `/api/bg-wallpaper/image/<id>`) or a raw HTTP(S) image URL the browser\r\n * loads; exactly one of `uploadId` / `url` is set — when both are empty no\r\n * background renders.\r\n */\r\nexport interface BackgroundSettings {\r\n /** Whether the custom background renders at all. */\r\n enabled: boolean\r\n /** Local upload id when the active source is an uploaded file. */\r\n uploadId: string\r\n /** Image URL when the active source is a remote URL. */\r\n url: string\r\n /** Background image opacity (0..1); the image fades toward the surface color. */\r\n opacity: number\r\n /** Readability scrim strength (0..1) layered between the art and the UI. */\r\n scrim: number\r\n /** Panel transparency (0..1): 0 = fully transparent surfaces, 1 = unaffected. */\r\n panelOpacity: number\r\n /** Frosted-glass blur radius on opaque surfaces (px; 0 disables). */\r\n blur: number\r\n /** Blur of the wallpaper image itself (px; 0 disables). */\r\n wallpaperBlur: number\r\n /** How the image fits the frame. */\r\n fit: BackgroundFit\r\n}\r\n","/**\n * Background painter — renders the user background behind the whole app frame:\n * a fixed `z-index:-2` wallpaper element plus a `z-index:-1` scrim on\n * `document.body`, driven by the `data-dsh-bg` body attribute and a set of\n * CSS variables. It also applies the frosted-glass surface tokens so the\n * wallpaper shows through translucent input / bubble panels.\n *\n * Retraction discipline mirrors the reference skin plugins: every property /\n * DOM node this painter owns is restored to its prior state on dispose, and\n * the `data-ds-dark-theme` observer is disconnected.\n */\n\nimport { ACTIVE_ATTR, injectBackgroundCss } from './background-css.ts'\nimport { BACKGROUND_API_PREFIX, type BackgroundSettings } from '../settings.ts'\n\n/** Class names the layer and scrim elements carry. */\nconst LAYER_CLASS = 'dsh-bg-layer'\nconst IMAGE_CLASS = 'dsh-bg-image'\nconst SCRIM_CLASS = 'dsh-bg-scrim'\n\n/** Surface tokens this painter overwrites (restored on dispose). */\nconst SURFACE_TOKENS = [\n '--dsw-specific-input-major',\n '--dsw-specific-bubble',\n] as const\n\n/** Fraction of the official white surface alpha kept at the most transparent panel setting. */\nconst GLASS_MIN_ALPHA = 0.05\n\n/**\n * The theme-aware glass surface alphas for one panelOpacity: the input-surface\n * token and the bubble token share one curve, dimmed per scheme. Shared by the\n * live painter and the settings-row preview card so the two cannot drift.\n */\nfunction glassSurfaceAlphas(panelOpacity: number, inDark: boolean): { major: string; bubble: string } {\n const alpha = GLASS_MIN_ALPHA + panelOpacity * (0.9 - GLASS_MIN_ALPHA)\n const clamped = Math.max(0, Math.min(0.9, alpha))\n const factor = inDark ? 0.4 : 0.8\n return {\n major: `rgba(255, 255, 255, ${(clamped * factor).toFixed(3)})`,\n bubble: `rgba(255, 255, 255, ${(clamped * factor * 0.8).toFixed(3)})`,\n }\n}\n\n/**\n * Resolve the effective background image url for a section.\n * @param settings - resolved background section.\n * @returns the image url, or '' when no source is set.\n */\nexport function backgroundImageUrl(settings: BackgroundSettings, uploadBase: string): string {\n if (settings.uploadId) return `${uploadBase}/image/${settings.uploadId}`\n return settings.url\n}\n\n/**\n * Manage the behind-body wallpaper + scrim layers and the glass surface.\n */\nexport class BackgroundPainter {\n private layer: HTMLDivElement | null = null\n private img: HTMLImageElement | null = null\n private scrim: HTMLDivElement | null = null\n private observer: MutationObserver | undefined\n private active = false\n private settings: BackgroundSettings | undefined\n private readonly savedTokens = new Map<string, string>()\n private readonly savedVars = new Map<string, string>()\n\n /** Remember + hijack a property only once so dispose restores the original. */\n private rememberOnce(prop: string): void {\n if (this.savedTokens.has(prop) || this.savedVars.has(prop)) return\n const value = document.body.style.getPropertyValue(prop)\n if (SURFACE_TOKENS.includes(prop as (typeof SURFACE_TOKENS)[number])) {\n this.savedTokens.set(prop, value)\n } else {\n this.savedVars.set(prop, value)\n }\n }\n\n /**\n * Apply the latest settings. Idempotent.\n * @param settings - resolved background section.\n * @param uploadBase - the API base for resolving uploads.\n */\n apply(settings: BackgroundSettings, uploadBase: string): void {\n this.settings = settings\n if (!settings.enabled) {\n this.dispose()\n return\n }\n injectBackgroundCss()\n this.rememberOnce('--dsw-alias-bg-base')\n this.rememberOnce('--dsw-specific-sidebar-fill')\n document.body.style.setProperty('--dsw-alias-bg-base', 'transparent')\n document.body.style.setProperty('--dsw-specific-sidebar-fill', 'transparent')\n\n const url = backgroundImageUrl(settings, uploadBase)\n if (url === '') {\n // No source: leave the frame alone, restore the removed layers and any\n // glass-token overrides we may have written on a previous apply. The\n // theme observer must go too — a later light/dark flip would re-run\n // applySurfaceGlass with forceRestore=false and write the translucent\n // tokens back onto panels that no longer sit over a wallpaper.\n this.observer?.disconnect()\n this.observer = undefined\n this.applySurfaceGlass(settings, true)\n this.active = false\n this.removeLayers()\n document.body.removeAttribute(ACTIVE_ATTR)\n return\n }\n\n // Wallpaper layer + scrim.\n if (!this.layer) {\n this.layer = document.createElement('div')\n this.layer.className = LAYER_CLASS\n this.img = document.createElement('img')\n this.img.className = IMAGE_CLASS\n this.img.alt = ''\n // Remote-url sources must not leak the local origin via Referer.\n this.img.referrerPolicy = 'no-referrer'\n this.layer.appendChild(this.img)\n document.body.appendChild(this.layer)\n }\n // Compare the *attribute* (the raw value we set) rather than the .src\n // property, which resolves to an absolute URL and would make this check\n // always-true for relative urls. Same-value writes are no-ops in the DOM,\n // so this is a clarity fix, not a behavior fix.\n if (this.img && this.img.getAttribute('src') !== url) this.img.src = url\n\n if (!this.scrim) {\n this.scrim = document.createElement('div')\n this.scrim.className = SCRIM_CLASS\n document.body.appendChild(this.scrim)\n }\n document.body.setAttribute(ACTIVE_ATTR, 'on')\n this.active = true\n\n // Push the knobs into CSS variables.\n const s = document.body.style\n this.setVar('--bg-object-fit', settings.fit)\n this.setVar('--bg-opacity', String(settings.opacity))\n this.setVar('--bg-wallpaper-blur', `${settings.wallpaperBlur}px`)\n this.setVar('--bg-wallpaper-scale', (1 + settings.wallpaperBlur * 0.006).toFixed(4))\n this.setVar('--bg-scrim', String(settings.scrim))\n this.setVar('--bg-glass-blur', `${settings.blur}px`)\n this.setVar('--bg-glass-saturate', String(1.15 + settings.blur * 0.03))\n\n // Glass surface: theme-aware translucent token (panelOpacity === 1 keeps\n // the official opaque surfaces and disables the blur).\n this.applySurfaceGlass(settings, false)\n\n // Theme observer: repaint the glass tokens when the light/dark scheme\n // flips (the surface translucency differs between schemes). Disconnects\n // on dispose.\n if (!this.observer) {\n this.observer = new MutationObserver(() => this.applySurfaceGlass(this.settings, false))\n this.observer.observe(document.body, { attributes: true, attributeFilter: ['data-ds-dark-theme'] })\n }\n }\n\n/** Write one owned CSS variable onto body. */\nprivate setVar(name: string, value: string): void {\n this.rememberOnce(name)\n document.body.style.setProperty(name, value)\n}\n\n/**\n * Update one effect knob without re-running the full apply. This is the hot\n * path for slider drags (many events per second): only the single CSS\n * variable changes, the DOM layers are untouched.\n * @param key - the knob to update.\n * @param value - its new value in its canonical unit.\n */\nsetKnob(key: 'opacity' | 'scrim' | 'blur' | 'wallpaperBlur' | 'fit', value: number | BackgroundSettings['fit']): void {\n if (this.settings === undefined || !this.settings.enabled) return\n switch (key) {\n case 'opacity':\n this.setVar('--bg-opacity', String(value))\n break\n case 'scrim':\n this.setVar('--bg-scrim', String(value))\n break\n case 'blur': {\n this.setVar('--bg-glass-blur', `${value}px`)\n this.setVar('--bg-glass-saturate', String(1.15 + Number(value) * 0.03))\n break\n }\n case 'wallpaperBlur': {\n this.setVar('--bg-wallpaper-blur', `${value}px`)\n this.setVar('--bg-wallpaper-scale', (1 + Number(value) * 0.006).toFixed(4))\n break\n }\n case 'fit':\n this.setVar('--bg-object-fit', String(value))\n break\n }\n}\n\n /**\n * Apply the translucent-glass surface tokens (or restore the official ones\n * when the panel is fully opaque / there is no background).\n * @param settings - resolved section (for panelOpacity).\n * @param forceRestore - true to restore official surfaces regardless (used\n * when no source is set, so leftover glass tokens never linger).\n */\n private applySurfaceGlass(settings: BackgroundSettings | undefined, forceRestore: boolean): void {\n const s = document.body.style\n const inDark = document.body.dataset.dsDarkTheme !== undefined\n // Always remember the original surface values so a restore / dispose\n // returns them cleanly.\n for (const token of SURFACE_TOKENS) this.rememberOnce(token)\n\n const restore = () => {\n for (const token of SURFACE_TOKENS) {\n const original = this.savedTokens.get(token)\n if (original !== undefined && original !== '') s.setProperty(token, original)\n else s.removeProperty(token)\n }\n }\n\n if (forceRestore || settings === undefined || settings.panelOpacity >= 1) {\n restore()\n // No blur when glass is off.\n this.setVar('--bg-glass-blur', '0px')\n return\n }\n\n // Dark scheme uses a lower white alpha (the wallpaper stays visible\n // without washing the surface), mirroring the reference engine.\n const { major, bubble } = glassSurfaceAlphas(settings.panelOpacity, inDark)\n s.setProperty('--dsw-specific-input-major', major)\n s.setProperty('--dsw-specific-bubble', bubble)\n }\n\n /** Remove the wallpaper + scrim layers and clear the active attribute. */\n private removeLayers(): void {\n if (this.layer) { this.layer.remove(); this.layer = null; this.img = null }\n if (this.scrim) { this.scrim.remove(); this.scrim = null }\n }\n\n /** Restore every owned property and remove the layers. */\n dispose(): void {\n this.observer?.disconnect()\n this.observer = undefined\n this.removeLayers()\n const s = document.body.style\n document.body.removeAttribute(ACTIVE_ATTR)\n for (const [prop, value] of this.savedTokens) s.setProperty(prop, value)\n for (const [prop, value] of this.savedVars) s.setProperty(prop, value)\n this.savedTokens.clear()\n this.savedVars.clear()\n this.active = false\n this.settings = undefined\n }\n\n /** @returns whether a wallpaper is currently rendered. */\n isActive(): boolean {\n return this.active\n }\n}\n\n/** Module-level painter singleton shared by the settings row preview and the live apply. */\nexport const backgroundPainter = new BackgroundPainter()\n\n/**\n * Apply the given settings through the shared painter (idempotent). Convenience\n * used by the settings row for live previews and by the plugin apply to render\n * the persisted section.\n * @param settings - settings to apply (may be an unsaved draft for preview).\n */\nexport function paintBackground(settings: BackgroundSettings): void {\n backgroundPainter.apply(settings, BACKGROUND_API_PREFIX)\n}\n\n/** Numeric effect knobs the slider drag path repaints. */\nexport type BackgroundKnob = 'opacity' | 'scrim' | 'blur' | 'wallpaperBlur' | 'fit'\n\n/**\n * Update one effect knob on the live backdrop without re-running the full\n * apply — the hot path for slider drags (see BackgroundPainter.setKnob).\n * @param key - the knob to update.\n * @param value - its new value.\n */\nexport function paintBackgroundKnob(key: BackgroundKnob, value: number | BackgroundSettings['fit']): void {\n backgroundPainter.setKnob(key, value)\n}\n\n/** Preview variables the settings-surface preview card consumes. */\nconst PREVIEW_VARS = [\n '--bg-opacity', '--bg-scrim', '--bg-object-fit',\n '--bg-wallpaper-blur', '--bg-wallpaper-scale',\n '--bg-glass-blur', '--bg-glass-saturate', '--bg-preview-glass',\n] as const\n\n/**\n * Push the effect knobs onto one element (the settings-surface preview card)\n * so dragging a slider updates the card without touching the live backdrop.\n * The card's CSS reads the same variable names as the painter writes on body.\n * @param el - the preview element.\n * @param settings - the draft settings to render (may be unsaved).\n */\nexport function paintPreviewSurface(el: HTMLElement, settings: BackgroundSettings): void {\n const s = el.style\n s.setProperty('--bg-object-fit', settings.fit)\n s.setProperty('--bg-opacity', String(settings.opacity))\n s.setProperty('--bg-scrim', String(settings.scrim))\n s.setProperty('--bg-wallpaper-blur', `${settings.wallpaperBlur}px`)\n s.setProperty('--bg-wallpaper-scale', (1 + settings.wallpaperBlur * 0.006).toFixed(4))\n // The glass bubble mirrors the live panel surface: below panelOpacity 1 its\n // alpha follows the same theme-aware curve as --dsw-specific-bubble (so the\n // panel-opacity slider is visible in the preview); at 1 the official opaque\n // surface returns — the bubble turns opaque and the blur switches off, just\n // like applySurfaceGlass does on the real backdrop.\n const inDark = document.body.dataset.dsDarkTheme !== undefined\n if (settings.panelOpacity >= 1) {\n s.setProperty('--bg-glass-blur', '0px')\n s.setProperty('--bg-preview-glass', 'var(--dsw-alias-bg-layer-1)')\n return\n }\n s.setProperty('--bg-glass-blur', `${settings.blur}px`)\n s.setProperty('--bg-glass-saturate', String(1.15 + settings.blur * 0.03))\n // The preview bubble mirrors the live --dsw-specific-bubble token.\n s.setProperty('--bg-preview-glass', glassSurfaceAlphas(settings.panelOpacity, inDark).bubble)\n}\n\n/** Remove every preview variable from an element (restores CSS defaults). */\nexport function clearPreviewSurface(el: HTMLElement): void {\n for (const name of PREVIEW_VARS) el.style.removeProperty(name)\n}\n","/** Locale dictionaries for the background plugin. */\n\nimport type { BackgroundCardLocaleKey } from './types.ts'\n\n/** Dictionary of the namespace this plugin owns. */\nexport type BackgroundCardDict = Record<BackgroundCardLocaleKey, string>\n\n/** Chinese copy. */\nexport const zh: BackgroundCardDict = {\n 'background.title': '自定义背景',\n 'background.description': '为整个应用界面设置背景图片,可调不透明度、遮罩、面板透明与毛玻璃模糊',\n 'background.upload': '上传图片',\n 'background.uploading': '上传中…',\n 'background.uploadMediaTypes': '支持 JPG / PNG / WebP / GIF',\n 'background.urlPlaceholder': '或粘贴图片 http(s) 链接后回车',\n 'background.invalidUrl': '链接无效,请粘贴 http(s) 开头的图片链接',\n 'background.applyUrl': '应用链接',\n 'background.enabled': '背景已启用',\n 'background.source': '图片',\n 'background.effects': '效果',\n 'background.noPreview': '上传图片或粘贴链接后,此处实时预览效果',\n 'background.previewGlass': '毛玻璃',\n 'background.opacity': '不透明度',\n 'background.scrim': '遮罩',\n 'background.panelOpacity': '面板不透明度',\n 'background.blur': '毛玻璃模糊',\n 'background.wallpaperBlur': '壁纸模糊',\n 'background.fit': '填充方式',\n 'background.cover': '铺满',\n 'background.contain': '完整',\n 'background.clear': '清除背景',\n 'background.uploadFailed': '上传失败,请重试',\n 'background.saveFailed': '保存失败,请重试',\n}\n\n/** English copy. */\nexport const en: BackgroundCardDict = {\n 'background.title': 'Custom Background',\n 'background.description': 'Set a background image behind the whole app surface with adjustable opacity, scrim, panel transparency and glass blur',\n 'background.upload': 'Upload image',\n 'background.uploading': 'Uploading…',\n 'background.uploadMediaTypes': 'JPG / PNG / WebP / GIF',\n 'background.urlPlaceholder': 'Or paste an image http(s) link and press Enter',\n 'background.invalidUrl': 'Invalid link — paste an image link starting with http(s)',\n 'background.applyUrl': 'Apply URL',\n 'background.enabled': 'Background on',\n 'background.source': 'Image',\n 'background.effects': 'Effects',\n 'background.noPreview': 'Upload an image or paste a link to preview live here',\n 'background.previewGlass': 'Glass',\n 'background.opacity': 'Opacity',\n 'background.scrim': 'Scrim',\n 'background.panelOpacity': 'Panel opacity',\n 'background.blur': 'Glass blur',\n 'background.wallpaperBlur': 'Wallpaper blur',\n 'background.fit': 'Fit',\n 'background.cover': 'Cover',\n 'background.contain': 'Contain',\n 'background.clear': 'Clear background',\n 'background.uploadFailed': 'Upload failed, try again',\n 'background.saveFailed': 'Save failed, try again',\n}\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * Background settings transport — reads and writes the `ui-background`\n * section, and uploads local images, through the plugin's own same-origin\n * HTTP routes (see src/routes.ts). A custom route family keeps the section\n * usable even though the api-proxy settings allowlist does not expose\n * third-party namespaces over the settings RPC. One module-level client\n * serves the painter and the settings row.\n */\n\nimport type { BackgroundSettings } from '../settings.ts'\nimport { BACKGROUND_API_PREFIX } from '../settings.ts'\n\n/** One snapshot of the section as last seen by the transport. */\nexport interface SettingsSnapshot {\n status: 'loading' | 'ready' | 'error'\n value?: BackgroundSettings\n}\n\ntype Listener = () => void\n\n/** Outcome of one save attempt: committed, displaced by a newer save (the\n * caller must not adopt its own section back into the draft), or rejected. */\nexport type SaveResult = 'ok' | 'superseded' | 'failed'\n\n/**\n * Observable settings client (getSnapshot/subscribe — the uSES currency).\n * Methods are arrow properties so React's useSyncExternalStore can call them\n * without an owning `this`.\n */\nexport class SettingsClient {\n private snapshot: SettingsSnapshot = { status: 'loading' }\n private readonly listeners = new Set<Listener>()\n /** Monotonic request id; the latest save wins even when POSTs interleave. */\n private saveSeq = 0\n /** Pending save promise (used by the row's flush-on-unmount). */\n private pending: Promise<SaveResult> | undefined\n\n /** @returns the current sync snapshot (stable reference until the next change). */\n getSnapshot = (): SettingsSnapshot => this.snapshot\n\n /** Observe snapshot replacements. @returns the disposer removing this listener. */\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n private notify(): void {\n for (const listener of this.listeners) listener()\n }\n\n /** Await any in-flight save (used before teardown so edits are not dropped). */\n async flush(): Promise<void> {\n await this.pending\n }\n\n /** Fetch the section from the host. */\n async load(): Promise<void> {\n try {\n const response = await fetch(`${BACKGROUND_API_PREFIX}/settings`)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n const body = await response.json() as { ok: boolean; value?: BackgroundSettings }\n if (!body.ok || body.value === undefined) throw new Error('unexpected payload')\n this.snapshot = { status: 'ready', value: body.value }\n } catch {\n this.snapshot = { status: 'error' }\n }\n this.notify()\n }\n\n /**\n * Persist the section through the host route. Concurrent calls are\n * serialized by a request sequence: an older save that resolves after a\n * newer one answers 'superseded' instead of 'ok', so the caller can never\n * mistake a stale write for success and adopt its outdated section.\n * @param section - the complete next section.\n * @returns whether the write committed, was displaced by a newer save, or failed.\n */\n async save(section: BackgroundSettings): Promise<SaveResult> {\n const seq = ++this.saveSeq\n const run = async (): Promise<SaveResult> => {\n try {\n const response = await fetch(`${BACKGROUND_API_PREFIX}/settings`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(section),\n })\n const body = await response.json() as { ok: boolean; value?: BackgroundSettings }\n if (!response.ok || !body.ok || body.value === undefined) return 'failed'\n // Discard the response of a superseded save — it reflects an older\n // document that must not overwrite the latest one.\n if (seq !== this.saveSeq) return 'superseded'\n this.snapshot = { status: 'ready', value: body.value }\n this.notify()\n return 'ok'\n } catch {\n return 'failed'\n }\n }\n const promise = run()\n this.pending = promise\n return promise\n }\n\n /**\n * Upload a local image file; on success stores its id into the snapshot and\n * returns the resolved background url (or null on failure).\n * @param file - the chosen image file.\n * @returns the resolved image url, or null.\n */\n async upload(file: File): Promise<string | null> {\n try {\n const response = await fetch(`${BACKGROUND_API_PREFIX}/upload`, {\n method: 'POST',\n headers: { 'content-type': file.type },\n body: file,\n })\n const body = await response.json() as { ok: boolean; id?: string; url?: string }\n if (!response.ok || !body.ok || body.id === undefined || body.url === undefined) return null\n return body.url ?? null\n } catch {\n return null\n }\n }\n}\n\n/** Shared client for the painter and the settings row. */\nexport const settingsClient = new SettingsClient()\n","/**\n * Background settings row — the editable form rendered inside the General\n * settings section (`settings.general.item` slot), in the same visual area as\n * the official Appearance row. Layout mirrors the reference wallpaper engine\n * picker (preview surface + grouped sections), styled with the harness alias\n * tokens so it reads as part of the settings chrome.\n *\n * Interaction model (fixes the previous jank):\n * - Sliders STEP in 5% (or 2px/5px) increments, so dragging snaps between\n * discrete values instead of firing dozens of sub-percent updates.\n * - While dragging, only the CSS variables change: the slider holds its own\n * state (no parent re-render) and the preview card repaints in isolation.\n * React's onChange maps to the native `input` event and would fire on every\n * step, so a commit is deferred to release (pointerup / keyup / blur) and\n * debounced — one POST per gesture, never per tick.\n * - A `lastSavedRef` distinguishes our own saves from external changes, so\n * the snapshot sync can never clobber a draft the user is still editing.\n */\n\nimport { useCallback, useEffect, useRef, useState, type ChangeEvent } from 'react'\nimport { useSyncExternalStore } from 'react'\nimport type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport clsx from 'clsx'\nimport {\n BACKGROUND_API_PREFIX, BLUR_MAX, BLUR_MIN, DEFAULT_BLUR, DEFAULT_FIT,\n DEFAULT_OPACITY, DEFAULT_PANEL_OPACITY, DEFAULT_SCRIM, DEFAULT_WALLPAPER_BLUR,\n FIT_MODES, OPACITY_MAX, OPACITY_MIN, PANEL_OPACITY_MAX, PANEL_OPACITY_MIN,\n SCRIM_MAX, SCRIM_MIN, WALLPAPER_BLUR_MAX, type BackgroundFit,\n type BackgroundSettings,\n} from '../settings.ts'\nimport { clearPreviewSurface, paintBackground, paintBackgroundKnob, paintPreviewSurface } from './backdrop.ts'\nimport { settingsClient, type SettingsSnapshot } from './settings-client.ts'\nimport css from './SettingsRow.module.css'\n\n/** Full component props: runtime share + locale seat. */\nexport type BackgroundRowProps =\n PropsRuntime<'settings.general.item'> & PropsLocale<'ui-background'>\n\n/** Defaults mirror the host schema (also the pre-load draft fallback). */\nconst DEFAULTS: BackgroundSettings = {\n enabled: false,\n uploadId: '',\n url: '',\n opacity: DEFAULT_OPACITY,\n scrim: DEFAULT_SCRIM,\n panelOpacity: DEFAULT_PANEL_OPACITY,\n blur: DEFAULT_BLUR,\n wallpaperBlur: DEFAULT_WALLPAPER_BLUR,\n fit: DEFAULT_FIT,\n}\n\n/** The numeric field keys a slider edits. */\ntype NumField = 'opacity' | 'scrim' | 'panelOpacity' | 'blur' | 'wallpaperBlur'\n\n/** Per-field slider geometry: 5% steps for ratios, fine px steps for radii so\n * the two blur sliders feel smooth near small values. Bounds come from the\n * shared settings constants so the schema boundary and the UI cannot drift. */\nconst SLIDER_SPEC: Record<NumField, { min: number; max: number; step: number; unit: 'percent' | 'px' }> = {\n opacity: { min: OPACITY_MIN, max: OPACITY_MAX, step: 0.05, unit: 'percent' },\n scrim: { min: SCRIM_MIN, max: SCRIM_MAX, step: 0.05, unit: 'percent' },\n panelOpacity: { min: PANEL_OPACITY_MIN, max: PANEL_OPACITY_MAX, step: 0.05, unit: 'percent' },\n blur: { min: BLUR_MIN, max: BLUR_MAX, step: 1, unit: 'px' },\n wallpaperBlur: { min: BLUR_MIN, max: WALLPAPER_BLUR_MAX, step: 2, unit: 'px' },\n}\n\n/** Derive the bare upload id from a resolve url like /api/bg-wallpaper/image/<id>. */\nfunction stripId(url: string): string {\n const prefix = `${BACKGROUND_API_PREFIX}/image/`\n return url.startsWith(prefix) ? url.slice(prefix.length) : ''\n}\n\n/** Read the settings store (uSES-friendly). */\nfunction readSnapshot(): SettingsSnapshot {\n return settingsClient.getSnapshot()\n}\n\n/** Field-wise equality for distinguishing our own saves from external changes. */\nfunction sameSettings(a: BackgroundSettings, b: BackgroundSettings): boolean {\n return a.enabled === b.enabled && a.uploadId === b.uploadId && a.url === b.url\n && a.opacity === b.opacity && a.scrim === b.scrim && a.panelOpacity === b.panelOpacity\n && a.blur === b.blur && a.wallpaperBlur === b.wallpaperBlur && a.fit === b.fit\n}\n\n/**\n * A stepped range control. While dragging it re-renders only itself: the value\n * is local state, `onLive(name, value)` repaints (CSS variables, no React\n * state), and `onCommit()` fires once on release through a debounced save.\n */\nfunction ControlSlider(props: {\n name: NumField\n label: string\n value: number\n onLive: (name: NumField, value: number) => void\n onCommit: () => void\n}) {\n const { name, label, value, onLive, onCommit } = props\n const spec = SLIDER_SPEC[name]\n const [local, setLocal] = useState(value)\n const localRef = useRef(value)\n const dragging = useRef(false)\n const onLiveRef = useRef(onLive)\n const onCommitRef = useRef(onCommit)\n onLiveRef.current = onLive\n onCommitRef.current = onCommit\n\n // Adopt an external value (load / save) when not mid-drag.\n useEffect(() => {\n if (!dragging.current && localRef.current !== value) {\n localRef.current = value\n setLocal(value)\n }\n }, [value])\n\n const handleLive = (e: ChangeEvent<HTMLInputElement>): void => {\n dragging.current = true\n const v = Number(e.currentTarget.value)\n localRef.current = v\n setLocal(v)\n syncTrackFill(e.currentTarget)\n onLiveRef.current(name, v)\n }\n\n const handleCommit = (): void => {\n if (!dragging.current) return\n dragging.current = false\n onCommitRef.current()\n }\n\n const text = spec.unit === 'percent'\n ? `${Math.round(local * 100)}%`\n : `${local}px`\n\n return (\n <div className={css.sliderRow}>\n <span className={css.sliderLabel}>{label}</span>\n <input\n type=\"range\"\n className={css.slider}\n ref={(el) => {\n // Sync the \"filled\" track fraction on mount and on every external\n // value adoption (the webkit track reads --bg-fill; moz draws its\n // own ::-moz-range-progress).\n if (el) syncTrackFill(el)\n }}\n min={spec.min} max={spec.max} step={spec.step}\n value={String(local)}\n aria-label={label}\n onInput={handleLive}\n onPointerUp={handleCommit}\n onKeyUp={(e) => {\n if (e.key.startsWith('Arrow') || e.key === 'Home' || e.key === 'End'\n || e.key === 'PageUp' || e.key === 'PageDown') handleCommit()\n }}\n onBlur={handleCommit}\n />\n <span className={css.sliderValue}>{text}</span>\n </div>\n )\n}\n\n/** Write the filled-track fraction (%) of a range input into its --bg-fill\n * variable (webkit has no range-progress pseudo-element; moz draws its own). */\nfunction syncTrackFill(el: HTMLInputElement): void {\n const min = Number(el.min)\n const max = Number(el.max)\n const value = Number(el.value)\n const pct = max === min ? 0 : ((value - min) / (max - min)) * 100\n el.style.setProperty('--bg-fill', `${pct}%`)\n}\n\n/**\n * The background settings row. A draft mirrors the persisted section; dragging\n * repaints the preview card and the live backdrop through CSS variables, and a\n * release commits once through the plugin's own host route.\n */\nexport function BackgroundSettingsRow({ t }: BackgroundRowProps) {\n const snapshot = useSyncExternalStore(settingsClient.subscribe, readSnapshot)\n const [draft, setDraft] = useState<BackgroundSettings>(DEFAULTS)\n const [uploading, setUploading] = useState(false)\n const [urlText, setUrlText] = useState('')\n const [error, setError] = useState('')\n const fileInput = useRef<HTMLInputElement>(null)\n const previewRef = useRef<HTMLDivElement>(null)\n // The latest draft, read by every handler (never a stale closure).\n const draftRef = useRef<BackgroundSettings>(DEFAULTS)\n // True while a drag/gesture is in flight: hold off snapshot sync.\n const editingRef = useRef(false)\n // The last section this row saved; external changes are snapshots that differ.\n const lastSavedRef = useRef<BackgroundSettings | null>(null)\n // True while an edit has not yet reached the host (drives flush-on-unmount).\n const dirtyRef = useRef(false)\n const commitTimer = useRef<number | undefined>(undefined)\n const editingTimer = useRef<number | undefined>(undefined)\n\n /** Repaint the live backdrop + preview card from one draft (no React state). */\n const paintLive = useCallback((next: BackgroundSettings): void => {\n paintBackground(next)\n if (previewRef.current) paintPreviewSurface(previewRef.current, next)\n }, [])\n\n /** Mark the row as being edited; clears after a quiet 600ms window. */\n const markEditing = useCallback((): void => {\n editingRef.current = true\n clearTimeout(editingTimer.current)\n editingTimer.current = window.setTimeout(() => { editingRef.current = false }, 600)\n }, [])\n\n /** Slider live handler (stable identity for the memoized sliders). */\n const onLive = useCallback((key: NumField, value: number): void => {\n const next = { ...draftRef.current, [key]: value }\n draftRef.current = next\n markEditing()\n // Hot path: most knobs repaint through the painter's single-variable\n // writer (no DOM layer churn). panelOpacity drives the glass surface\n // tokens, which need the full apply.\n if (key === 'panelOpacity') {\n paintBackground(next)\n } else {\n paintBackgroundKnob(key, value)\n }\n if (previewRef.current) paintPreviewSurface(previewRef.current, next)\n }, [markEditing])\n\n /** Persist one section through the host route and adopt it on success. */\n const saveNow = useCallback(async (next: BackgroundSettings): Promise<void> => {\n dirtyRef.current = true\n const result = await settingsClient.save(next)\n if (result === 'failed') {\n setError(t('background.saveFailed'))\n return\n }\n // A superseded save lost to a newer one: that newer save owns adoption\n // and the dirty flag — adopting here would resurrect an outdated section.\n if (result === 'superseded') return\n dirtyRef.current = false\n setError('')\n lastSavedRef.current = next\n draftRef.current = next\n setDraft(next)\n if (!next.uploadId) setUrlText(next.url)\n }, [t])\n\n // The latest saveNow, readable from stable callbacks and the unmount\n // cleanup (never a stale closure).\n const saveNowRef = useRef(saveNow)\n saveNowRef.current = saveNow\n\n /** Debounced commit: one POST per drag gesture, from the latest draft. */\n const onCommit = useCallback((): void => {\n clearTimeout(commitTimer.current)\n commitTimer.current = window.setTimeout(() => {\n // Reset before saving: a fired timer must never look like an armed one,\n // or the unmount flush would re-save an already-committed gesture.\n commitTimer.current = undefined\n void saveNowRef.current(draftRef.current)\n }, 120)\n }, [])\n\n // Sync the draft from the persisted value when it changes externally, but\n // never overwrite a draft the user is mid-editing.\n useEffect(() => {\n if (snapshot.status !== 'ready' || snapshot.value === undefined) return\n const saved = lastSavedRef.current\n const external = saved === null || !sameSettings(saved, snapshot.value)\n if (external && !editingRef.current) {\n lastSavedRef.current = snapshot.value\n draftRef.current = snapshot.value\n setDraft(snapshot.value)\n setUrlText(snapshot.value.uploadId ? '' : snapshot.value.url)\n if (previewRef.current) paintPreviewSurface(previewRef.current, snapshot.value)\n }\n }, [snapshot])\n\n // Teardown: a pending debounced commit must not die with the row. If the\n // timer is still armed, fire the save NOW (the timer callback would never\n // run after unmount, and dirtyRef is only set inside saveNow — clearing\n // the timer alone would silently drop the last slider adjustment). Then\n // wait out any save already in flight. The effect runs once per mount:\n // saveNow is reached through its ref, so an identity change mid-session\n // cannot re-trigger this teardown, and editingRef is reset so a cleared\n // editing window can never block snapshot sync forever.\n useEffect(() => () => {\n clearTimeout(editingTimer.current)\n editingRef.current = false\n if (commitTimer.current !== undefined) {\n clearTimeout(commitTimer.current)\n commitTimer.current = undefined\n void saveNowRef.current(draftRef.current)\n }\n if (dirtyRef.current) void settingsClient.flush()\n }, [])\n\n const handleFile = async (file?: File): Promise<void> => {\n if (!file) return\n markEditing()\n setUploading(true)\n setError('')\n try {\n const url = await settingsClient.upload(file)\n if (url === null) {\n setError(t('background.uploadFailed'))\n return\n }\n const next: BackgroundSettings = { ...draftRef.current, uploadId: stripId(url), url: '', enabled: true }\n draftRef.current = next\n setDraft(next)\n setUrlText('')\n paintLive(next)\n await saveNow(next)\n } finally {\n setUploading(false)\n }\n }\n\n const handleUrlSubmit = (): void => {\n const url = urlText.trim()\n if (!/^https?:\\/\\//i.test(url)) {\n setError(t('background.invalidUrl'))\n return\n }\n markEditing()\n const next: BackgroundSettings = { ...draftRef.current, uploadId: '', url, enabled: true }\n draftRef.current = next\n setDraft(next)\n paintLive(next)\n void saveNow(next)\n }\n\n const handleClear = (): void => {\n const next: BackgroundSettings = { ...draftRef.current, uploadId: '', url: '', enabled: false }\n draftRef.current = next\n setDraft(next)\n setUrlText('')\n if (previewRef.current) clearPreviewSurface(previewRef.current)\n paintBackground(next)\n void saveNow(next)\n }\n\n const handleFit = (mode: BackgroundFit): void => {\n markEditing()\n const next: BackgroundSettings = { ...draftRef.current, fit: mode }\n draftRef.current = next\n setDraft(next)\n paintLive(next)\n void saveNow(next)\n }\n\n const sourceUrl = draft.uploadId ? `${BACKGROUND_API_PREFIX}/image/${draft.uploadId}` : draft.url\n const hasImage = sourceUrl !== ''\n\n return (\n <div className={css.row}>\n <div className={css.header}>\n <div className={css.titleRow}>\n <div className={css.title}>{t('background.title')}</div>\n {hasImage && (\n <button type=\"button\" className={clsx(css.btn, css.btnDanger, css.clearBtn)} onClick={handleClear}>\n {t('background.clear')}\n </button>\n )}\n </div>\n <div className={css.desc}>{t('background.description')}</div>\n </div>\n\n {/* Live preview surface: image + theme-aware scrim + frosted glass bubble. */}\n <div className={css.preview} ref={previewRef}>\n {hasImage\n ? (\n <>\n <img className={css.previewImg} src={sourceUrl} alt=\"\" draggable={false} referrerPolicy=\"no-referrer\" />\n <div className={css.previewScrim} />\n <div className={css.previewGlass}>\n <span className={css.previewGlassText}>{t('background.previewGlass')}</span>\n </div>\n </>\n )\n : (\n <div className={css.previewEmpty}>{t('background.noPreview')}</div>\n )}\n </div>\n\n {/* Image source: upload or paste an http(s) link. */}\n <div className={css.section}>\n <div className={css.sectionLabel}>{t('background.source')}</div>\n <div className={css.sourceRow}>\n <input\n type=\"file\"\n ref={fileInput}\n hidden\n accept=\"image/jpeg,image/png,image/webp,image/gif\"\n onChange={(e) => {\n void handleFile(e.currentTarget.files?.[0])\n e.currentTarget.value = ''\n }}\n />\n <button\n type=\"button\"\n className={clsx(css.btn, css.btnPrimary)}\n disabled={uploading}\n onClick={() => fileInput.current?.click()}\n >\n {uploading ? t('background.uploading') : t('background.upload')}\n </button>\n <span className={css.hint}>{t('background.uploadMediaTypes')}</span>\n {draft.uploadId && <span className={css.enabledBadge}>✓ {t('background.enabled')}</span>}\n </div>\n <div className={css.urlRow}>\n <input\n className={css.urlInput}\n value={urlText}\n placeholder={t('background.urlPlaceholder')}\n onKeyDown={(e) => { if (e.key === 'Enter') handleUrlSubmit() }}\n onChange={(e) => setUrlText(e.currentTarget.value)}\n />\n <button\n type=\"button\"\n className={css.btn}\n disabled={!urlText.trim()}\n onClick={handleUrlSubmit}\n >\n {t('background.applyUrl')}\n </button>\n </div>\n </div>\n\n {/* Effects: stepped sliders + fit segmented control. */}\n <div className={css.section}>\n <div className={css.sectionLabel}>{t('background.effects')}</div>\n <ControlSlider name=\"opacity\" label={t('background.opacity')} value={draft.opacity} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"scrim\" label={t('background.scrim')} value={draft.scrim} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"panelOpacity\" label={t('background.panelOpacity')} value={draft.panelOpacity} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"blur\" label={t('background.blur')} value={draft.blur} onLive={onLive} onCommit={onCommit} />\n <ControlSlider name=\"wallpaperBlur\" label={t('background.wallpaperBlur')} value={draft.wallpaperBlur} onLive={onLive} onCommit={onCommit} />\n <div className={css.segRow}>\n <span className={css.segLabel}>{t('background.fit')}</span>\n <div className={css.segGroup} role=\"group\" aria-label={t('background.fit')}>\n {FIT_MODES.map((mode) => (\n <button\n key={mode}\n type=\"button\"\n className={clsx(css.segBtn, draft.fit === mode && css.segActive)}\n onClick={() => handleFit(mode)}\n >\n {t(mode === 'cover' ? 'background.cover' : 'background.contain')}\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {error && <div className={css.error} role=\"alert\">{error}</div>}\n\n {hasImage && (\n <div className={css.clearRow}>\n <button type=\"button\" className={clsx(css.btn, css.btnDanger)} onClick={handleClear}>\n {t('background.clear')}\n </button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Browser half of the background plugin: paints the background behind the\n * whole app frame and provides the settings surface — a live row inside the\n * official General settings section (next to the Appearance row). Data flows\n * through the plugin's own host route (`/api/bg-wallpaper/*`), not the\n * settings RPC, because the api-proxy settings allowlist does not expose\n * third-party namespaces. Retraction discipline mirrors the skin plugins:\n * every property and DOM node the painter owns is restored on dispose.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { backgroundPainter, paintBackground } from './backdrop.ts'\nimport { en, zh } from './locales.ts'\nimport { BackgroundSettingsRow } from './SettingsRow.tsx'\nimport { settingsClient } from './settings-client.ts'\nimport type {} from './types.ts'\n\n/**\n * Service injections this entry declares: the `slots` (runtime) and `locale`\n * services only — the settings surface talks to the plugin's own host route\n * over plain same-origin fetch, so no settings/connection service is needed.\n */\nexport const inject = ['slots', 'locale']\n\n/**\n * Client plugin body: paint the persisted background live and register the\n * General-settings row.\n * @param ctx - client cordis context (slots/locale injected).\n */\nexport function apply(ctx: Context): void {\n // Paint from the plugin's own transport; repaint on every snapshot change.\n const paint = (): void => {\n const snapshot = settingsClient.getSnapshot()\n const value = snapshot.status === 'ready' ? snapshot.value : undefined\n if (value === undefined || !value.enabled) {\n backgroundPainter.dispose()\n return\n }\n paintBackground(value)\n }\n const unsubscribe = settingsClient.subscribe(paint)\n void settingsClient.load()\n\n ctx.effect(() => ctx.locale.register('ui-background', { zh, en }), 'deepseek-harness-background: dictionaries')\n\n // Live settings row in the General section (same slot as the Appearance row).\n ctx.slots.inject('settings.general.item', () => ctx.slots.register({\n name: 'settings.general.item',\n id: 'deepseek-harness-background',\n order: 600,\n locale: 'ui-background',\n }, BackgroundSettingsRow))\n\n ctx.effect(() => () => {\n unsubscribe()\n backgroundPainter.dispose()\n }, 'deepseek-harness-background: background surface')\n}\n"],"x_google_ignoreList":[4],"mappings":"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"}
package/lib/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { settingsNamespace } from "@deepseek-ai/dsh-settings";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { randomBytes } from "node:crypto";
4
- import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { extname, join } from "node:path";
4
+ import { existsSync, mkdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
+ import { readFile } from "node:fs/promises";
6
+ import { extname, join, resolve } from "node:path";
6
7
  import { homedir } from "node:os";
7
8
  //#region src/settings.ts
8
9
  /** Durable background settings — pure constants/types shared by both halves. */
@@ -29,8 +30,10 @@ const BACKGROUND_SETTINGS_FIELDS = [
29
30
  const BACKGROUND_API_PREFIX = "/api/bg-wallpaper";
30
31
  /** Minimum scrim opacity (0 = no veil at all). */
31
32
  const SCRIM_MIN = 0;
32
- /** Maximum scrim opacity (a nearly opaque veil keeps text legible over any art). */
33
- const SCRIM_MAX = .95;
33
+ /** Maximum scrim opacity (1 = fully opaque veil — hides the wallpaper entirely).
34
+ * Kept at 1 so every percent-unit slider shares one 0..1 domain: the same
35
+ * displayed percentage puts every thumb at the same fraction of its track. */
36
+ const SCRIM_MAX = 1;
34
37
  /** Default scrim strength applied over user art (a light readability veil). */
35
38
  const DEFAULT_SCRIM = .25;
36
39
  /** Default background image opacity (1 = fully opaque). */
@@ -61,7 +64,7 @@ const BackgroundSettingsSchema = z.object({
61
64
  /** Image URL or empty (mutually exclusive with uploadId at runtime). */
62
65
  url: z.string().default(""),
63
66
  opacity: z.number().min(0).max(1).default(1),
64
- scrim: z.number().min(0).max(SCRIM_MAX).default(DEFAULT_SCRIM),
67
+ scrim: z.number().min(0).max(1).default(DEFAULT_SCRIM),
65
68
  panelOpacity: z.number().min(0).max(1).default(DEFAULT_PANEL_OPACITY),
66
69
  blur: z.number().min(0).max(40).default(16),
67
70
  wallpaperBlur: z.number().min(0).max(60).default(0),
@@ -72,24 +75,34 @@ const BackgroundSettingsSchema = z.object({
72
75
  /**
73
76
  * Harness-home resolution — where the uploaded wallpaper images live.
74
77
  *
75
- * Mirrors the dsh launcher's precedence (see apps/cli reference and the
76
- * skin-center harness-home.ts in dsh-web-ui): an explicit `--home`-formatted
77
- * override, else `$DSH_HOME`, else `<homedir>/.dsh`. Kept as a tiny pure
78
+ * Mirrors the dsh launcher's precedence (see @deepseek-ai/dsh-home-paths'
79
+ * resolveDshHome): `$DSH_HOME`, else `<homedir>/.dsh`. Kept as a tiny pure
78
80
  * function so the host half needs no out-of-tree dependency on the internal
79
- * home-paths package.
81
+ * home-paths package. The semantics match the official resolver: an empty or
82
+ * whitespace-only `$DSH_HOME` counts as unset, a leading `~` / `~/` / `~\`
83
+ * expands against the OS home, and the result is normalized with
84
+ * path.resolve().
80
85
  */
81
86
  /** First non-blank string in a candidate list, else undefined. */
82
87
  function firstNonBlank(...values) {
83
88
  for (const value of values) if (typeof value === "string" && value.trim() !== "") return value.trim();
84
89
  }
90
+ /** Expand supported tilde prefixes against the OS home (mirrors expandHomePath). */
91
+ function expandHomePath(path) {
92
+ if (path === "~") return homedir();
93
+ if (path.startsWith("~/") || path.startsWith("~\\")) return join(homedir(), path.slice(2));
94
+ return path;
95
+ }
85
96
  /**
86
- * Resolve the harness home (the directory containing `profiles/`): explicit
87
- * override wins, then `$DSH_HOME`, then `~/.dsh`.
97
+ * Resolve the harness home (the directory containing `profiles/`): `$DSH_HOME`
98
+ * first, then `~/.dsh`. An empty/whitespace `$DSH_HOME` counts as unset; a
99
+ * leading tilde expands against the OS home; the result is absolute.
88
100
  * @param env - environment (defaults to process.env).
89
- * @returns the absolute harness home directory.
101
+ * @returns the normalized absolute harness home directory.
90
102
  */
91
103
  function resolveHarnessHome(env = process.env) {
92
- return firstNonBlank(env.DSH_HOME) ?? join(homedir(), ".dsh");
104
+ const selected = firstNonBlank(env.DSH_HOME) ?? join(homedir(), ".dsh");
105
+ return resolve(expandHomePath(selected));
93
106
  }
94
107
  /** Subdirectory under the harness home that this plugin owns. */
95
108
  const PLUGIN_HOME_REL = join("deepseek-harness-background");
@@ -149,7 +162,7 @@ const NUM_BOUNDS = {
149
162
  },
150
163
  scrim: {
151
164
  min: 0,
152
- max: SCRIM_MAX
165
+ max: 1
153
166
  },
154
167
  panelOpacity: {
155
168
  min: 0,
@@ -198,19 +211,42 @@ function requireMethod(req, res, method) {
198
211
  * the request `Host` is rejected. Requests without either header (curl, node
199
212
  * http) pass — this is a local single-user tool, and the fence only targets
200
213
  * the cross-site browser vector.
214
+ *
215
+ * The `Host` itself must also be a loopback name: under a DNS-rebinding
216
+ * attack the attacker's page resolves their own domain to 127.0.0.1, so both
217
+ * `Origin` and `Host` carry the attacker's hostname and the self-consistency
218
+ * check above alone would admit them. The web server binds loopback (or is
219
+ * deliberately exposed on 0.0.0.0 by its operator), so accepting only
220
+ * 127.0.0.1 / localhost / [::1] keeps every legitimate same-origin client
221
+ * working while refusing rebounded ones.
201
222
  */
223
+ const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
224
+ "127.0.0.1",
225
+ "localhost",
226
+ "[::1]",
227
+ "::1"
228
+ ]);
229
+ function isLoopbackHost(host) {
230
+ let bare = host;
231
+ if (host.startsWith("[")) {
232
+ const end = host.indexOf("]");
233
+ if (end !== -1) bare = host.slice(0, end + 1);
234
+ } else {
235
+ const colon = host.indexOf(":");
236
+ if (colon !== -1) bare = host.slice(0, colon);
237
+ }
238
+ return LOOPBACK_HOSTS.has(bare) || LOOPBACK_HOSTS.has(host);
239
+ }
202
240
  function isSameOriginRequest(req) {
203
241
  const site = req.headers["sec-fetch-site"];
204
242
  if (typeof site === "string" && site === "cross-site") return false;
243
+ const host = req.headers.host;
244
+ if (typeof host !== "string" || !isLoopbackHost(host)) return false;
205
245
  const origin = req.headers.origin;
206
- if (typeof origin === "string" && origin !== "" && origin !== "null") {
207
- const host = req.headers.host;
208
- if (typeof host !== "string" || host === "") return false;
209
- try {
210
- return new URL(origin).host === host;
211
- } catch {
212
- return false;
213
- }
246
+ if (typeof origin === "string" && origin !== "" && origin !== "null") try {
247
+ return new URL(origin).host === host;
248
+ } catch {
249
+ return false;
214
250
  }
215
251
  return true;
216
252
  }
@@ -292,6 +328,10 @@ function validateSectionBody(body, home) {
292
328
  if (raw === void 0) continue;
293
329
  if (typeof raw !== "number" || !Number.isFinite(raw) || raw < min || raw > max) return "invalid-range";
294
330
  }
331
+ const enabled = body.enabled;
332
+ if (enabled !== void 0 && typeof enabled !== "boolean") return "invalid-enabled";
333
+ const fit = body.fit;
334
+ if (fit !== void 0 && fit !== "" && !FIT_MODES.includes(fit)) return "invalid-fit";
295
335
  return null;
296
336
  }
297
337
  /**
@@ -324,13 +364,14 @@ function storeUpload(bytes, mime, homeOverride) {
324
364
  url: `${BACKGROUND_API_PREFIX}/image/${id}`
325
365
  };
326
366
  }
327
- /** Resolve an upload id to its on-disk path (or "" if invalid / missing). */
367
+ /** Resolve an upload id to its on-disk path (or "" if invalid / missing).
368
+ * Read-only: unlike the write path this never creates the plugin home, so a
369
+ * GET cannot pay for (or race) a mkdirSync on every request. */
328
370
  function uploadPath(id, homeOverride) {
329
371
  if (!isUploadId(id)) return "";
330
- const dir = ensurePluginHome(homeOverride);
372
+ const dir = pluginHome(homeOverride);
331
373
  for (const ext of [
332
374
  "jpg",
333
- "jpeg",
334
375
  "png",
335
376
  "webp",
336
377
  "gif"
@@ -478,7 +519,7 @@ function makeBackgroundRoutes(settings, opts = {}) {
478
519
  {
479
520
  kind: "prefix",
480
521
  path: `${BACKGROUND_API_PREFIX}/image`,
481
- handler: (req, res) => {
522
+ handler: async (req, res) => {
482
523
  if (!isSameOriginRequest(req)) {
483
524
  json(res, 403, {
484
525
  ok: false,
@@ -497,11 +538,12 @@ function makeBackgroundRoutes(settings, opts = {}) {
497
538
  return;
498
539
  }
499
540
  try {
500
- const data = readFileSync(abs);
541
+ const data = await readFile(abs);
501
542
  const mime = mimeForPath(abs) ?? "application/octet-stream";
502
543
  const stat = statSync(abs);
503
544
  res.writeHead(200, {
504
545
  "content-type": mime,
546
+ "x-content-type-options": "nosniff",
505
547
  "content-length": String(data.length),
506
548
  "last-modified": stat.mtime.toUTCString(),
507
549
  "cache-control": "public, max-age=31536000, immutable"
@@ -528,7 +570,7 @@ const BACKGROUND_NAMESPACE = settingsNamespace(BACKGROUND_SETTINGS_NAMESPACE);
528
570
  */
529
571
  function apply(ctx) {
530
572
  ctx.inject(["settings", "webServer"], (hostCtx) => {
531
- hostCtx.settings.register(BACKGROUND_NAMESPACE, BackgroundSettingsSchema, { applies: "live" });
573
+ if (!hostCtx.settings.describe().some((descriptor) => descriptor.ns === "ui-background")) hostCtx.settings.register(BACKGROUND_NAMESPACE, BackgroundSettingsSchema, { applies: "live" });
532
574
  try {
533
575
  for (const route of makeBackgroundRoutes(hostCtx.settings)) hostCtx.effect(() => hostCtx.webServer.register(route), "deepseek-harness-background: settings route");
534
576
  } catch (error) {
package/package.json CHANGED
@@ -1,88 +1,86 @@
1
- {
2
- "name": "deepseek-harness-background",
3
- "description": "Custom background image plugin for the DeepSeek Harness web GUI: upload a local picture or paste an image URL, and render it behind the whole app surface with adjustable opacity, scrim, panel transparency and frosted-glass blur.",
4
- "version": "0.2.0",
5
- "keywords": [
6
- "deepseek-harness",
7
- "dsh",
8
- "plugin",
9
- "dsh-plugin",
10
- "theme",
11
- "wallpaper",
12
- "background",
13
- "web-ui"
14
- ],
15
- "author": "HaoyueQin",
16
- "license": "MIT",
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/HaoyueQin/deepseek-harness-background.git"
20
- },
21
- "homepage": "https://github.com/HaoyueQin/deepseek-harness-background",
22
- "packageManager": "pnpm@11.9.0",
23
- "type": "module",
24
- "main": "lib/index.js",
25
- "exports": {
26
- ".": "./lib/index.js",
27
- "./client": "./lib/client.js",
28
- "./src/*": "./src/*",
29
- "./package.json": "./package.json"
30
- },
31
- "dsh": {
32
- "bundle": {
33
- "patch": "./cordis.patch.yml"
34
- },
35
- "client": {
36
- "inject": [
37
- "@deepseek-ai/dsh-client-runtime",
38
- "@deepseek-ai/dsh-client-locale"
39
- ],
40
- "platform": "web"
41
- }
42
- },
43
- "scripts": {
44
- "build": "tsdown",
45
- "prepare": "tsdown",
46
- "watch": "tsdown --watch",
47
- "test": "vitest run",
48
- "typecheck": "tsc --noEmit -p tsconfig.json"
49
- },
50
- "license": "MIT",
51
- "files": [
52
- "lib",
53
- "cordis.patch.yml",
54
- "README.md",
55
- "README.zh.md"
56
- ],
57
- "peerDependencies": {
58
- "@deepseek-ai/cordis": "^4.0.1",
59
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
60
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
61
- "react": "^18.2.0"
62
- },
63
- "dependencies": {
64
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
65
- "@deepseek-ai/schemastery": "^3.18.1"
66
- },
67
- "devDependencies": {
68
- "@deepseek-ai/cordis": "^4.0.1",
69
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
70
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
71
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
72
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
73
- "@deepseek-ai/schemastery": "^3.18.1",
74
- "@testing-library/dom": "^10.4.1",
75
- "@testing-library/react": "^16.3.0",
76
- "@types/node": "^22.0.0",
77
- "@types/react": "~18.3.1",
78
- "@types/react-dom": "~18.3.0",
79
- "clsx": "^2.1.1",
80
- "jsdom": "^29.1.1",
81
- "lightningcss": "^1.33.0",
82
- "react": "^18.2.0",
83
- "react-dom": "^18.2.0",
84
- "tsdown": "^0.22.14",
85
- "typescript": "^5.8.0",
86
- "vitest": "^4.1.8"
87
- }
88
- }
1
+ {
2
+ "name": "deepseek-harness-background",
3
+ "description": "Custom background image plugin for the DeepSeek Harness web GUI: upload a local picture or paste an image URL, and render it behind the whole app surface with adjustable opacity, scrim, panel transparency and frosted-glass blur.",
4
+ "version": "0.2.1",
5
+ "keywords": [
6
+ "deepseek-harness",
7
+ "dsh",
8
+ "plugin",
9
+ "dsh-plugin",
10
+ "theme",
11
+ "wallpaper",
12
+ "background",
13
+ "web-ui"
14
+ ],
15
+ "author": "HaoyueQin",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/HaoyueQin/deepseek-harness-background.git"
20
+ },
21
+ "homepage": "https://github.com/HaoyueQin/deepseek-harness-background",
22
+ "packageManager": "pnpm@11.9.0",
23
+ "type": "module",
24
+ "main": "lib/index.js",
25
+ "exports": {
26
+ ".": "./lib/index.js",
27
+ "./client": "./lib/client.js",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "dsh": {
31
+ "bundle": {
32
+ "patch": "./cordis.patch.yml"
33
+ },
34
+ "client": {
35
+ "inject": [
36
+ "@deepseek-ai/dsh-client-runtime",
37
+ "@deepseek-ai/dsh-client-locale"
38
+ ],
39
+ "platform": "web"
40
+ }
41
+ },
42
+ "scripts": {
43
+ "build": "tsdown",
44
+ "prepare": "tsdown",
45
+ "watch": "tsdown --watch",
46
+ "test": "vitest run",
47
+ "typecheck": "tsc --noEmit -p tsconfig.json"
48
+ },
49
+ "files": [
50
+ "lib",
51
+ "cordis.patch.yml",
52
+ "README.md",
53
+ "README.zh.md"
54
+ ],
55
+ "peerDependencies": {
56
+ "@deepseek-ai/cordis": "^4.0.1",
57
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.1",
58
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.1",
59
+ "react": "^18.2.0"
60
+ },
61
+ "dependencies": {
62
+ "@deepseek-ai/dsh-settings": "^0.1.1-rc.1",
63
+ "@deepseek-ai/schemastery": "^3.18.1"
64
+ },
65
+ "devDependencies": {
66
+ "@deepseek-ai/cordis": "^4.0.1",
67
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.1",
68
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.1",
69
+ "@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.1",
70
+ "@deepseek-ai/dsh-settings": "^0.1.1-rc.1",
71
+ "@deepseek-ai/schemastery": "^3.18.1",
72
+ "@testing-library/dom": "^10.4.1",
73
+ "@testing-library/react": "^16.3.0",
74
+ "@types/node": "^22.0.0",
75
+ "@types/react": "~18.3.1",
76
+ "@types/react-dom": "~18.3.0",
77
+ "clsx": "^2.1.1",
78
+ "jsdom": "^29.1.1",
79
+ "lightningcss": "^1.33.0",
80
+ "react": "^18.2.0",
81
+ "react-dom": "^18.2.0",
82
+ "tsdown": "^0.22.14",
83
+ "typescript": "^5.8.0",
84
+ "vitest": "^4.1.8"
85
+ }
86
+ }