dsh-any-background 0.1.0

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/lib/client.js ADDED
@@ -0,0 +1,1248 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-any-background",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/client/index.tsx
11
+ const name = "dsh-any-background";
12
+ const inject = [
13
+ "slots",
14
+ "locale",
15
+ "theme"
16
+ ];
17
+ const NS = "settings.anyBg";
18
+ const LS_COLOR = "dsh-any-background:color";
19
+ const LS_WP = "dsh-any-background:wallpaper";
20
+ const LS_OP = "dsh-any-background:opacity";
21
+ const LS_WOP = "dsh-any-background:wallpaper-opacity";
22
+ const LS_BL = "dsh-any-background:blur";
23
+ const LS_BG = "dsh-any-background:bgState";
24
+ const LS_SOP = "dsh-any-background:settings-opacity";
25
+ const DEF_OP = .85;
26
+ const DEF_BL = 0;
27
+ const DEF_SOP = 1;
28
+ const CUSTOM_ID = "custom-color";
29
+ const zh = {
30
+ nav: "主题",
31
+ subtitle: "自定义界面外观",
32
+ colorTitle: "主题色",
33
+ colorHint: "在色轮上选择色相,在方形中调整饱和度和明度",
34
+ uiTitle: "界面",
35
+ uiOpacity: "主界面透明度",
36
+ uiOpacityHint: "拖动滑块调整主页界面背景的透明度",
37
+ uiSop: "设置界面透明度",
38
+ uiSopHint: "拖动滑块调整设置页界面背景的透明度",
39
+ bgTitle: "背景图片",
40
+ bgChoose: "选择图片",
41
+ bgRemove: "移除图片",
42
+ bgEdit: "编辑位置",
43
+ wpOpacity: "壁纸透明度",
44
+ bgBlur: "壁纸模糊",
45
+ bgHint: "拖动滑块实时调整。点击背景图可打开编辑器调整位置和大小",
46
+ editorTitle: "背景编辑器",
47
+ editorHint: "拖动移动图片,滚轮缩放大小",
48
+ editorCommit: "确认",
49
+ editorCancel: "取消",
50
+ editorReset: "重置"
51
+ };
52
+ const en = {
53
+ nav: "Theme",
54
+ subtitle: "Customize appearance",
55
+ colorTitle: "Theme color",
56
+ colorHint: "Pick hue on the ring, adjust saturation & lightness in the square",
57
+ uiTitle: "Interface",
58
+ uiOpacity: "Main interface opacity",
59
+ uiOpacityHint: "Drag to adjust homepage interface background opacity",
60
+ uiSop: "Settings interface opacity",
61
+ uiSopHint: "Drag to adjust the settings page background opacity",
62
+ bgTitle: "Wallpaper",
63
+ bgChoose: "Choose image",
64
+ bgRemove: "Remove image",
65
+ bgEdit: "Edit position",
66
+ wpOpacity: "Wallpaper opacity",
67
+ bgBlur: "Wallpaper blur",
68
+ bgHint: "Drag sliders for real-time adjustment. Click the image to open the editor",
69
+ editorTitle: "Background editor",
70
+ editorHint: "Drag to move, scroll to zoom",
71
+ editorCommit: "Confirm",
72
+ editorCancel: "Cancel",
73
+ editorReset: "Reset"
74
+ };
75
+ function rLS(k) {
76
+ try {
77
+ const v = localStorage.getItem(k);
78
+ return typeof v === "string" ? v : null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ function wLS(k, v) {
84
+ try {
85
+ v === null ? localStorage.removeItem(k) : localStorage.setItem(k, v);
86
+ } catch (e) {
87
+ console.warn(`dsh-any-background: localStorage write failed for "${k}"`, e);
88
+ }
89
+ }
90
+ function rColor() {
91
+ try {
92
+ const v = JSON.parse(rLS(LS_COLOR) || "");
93
+ if (Array.isArray(v) && v.length === 3) return v;
94
+ } catch {}
95
+ return [
96
+ 220,
97
+ .55,
98
+ .25
99
+ ];
100
+ }
101
+ function rWp() {
102
+ const v = rLS(LS_WP);
103
+ return v?.length ? v : null;
104
+ }
105
+ function rOp() {
106
+ const v = rLS(LS_OP);
107
+ if (!v) return DEF_OP;
108
+ const n = +v;
109
+ return isFinite(n) ? Math.min(1, Math.max(0, n)) : DEF_OP;
110
+ }
111
+ function rWop() {
112
+ const v = rLS(LS_WOP);
113
+ if (!v) return 1;
114
+ const n = +v;
115
+ return isFinite(n) ? Math.min(1, Math.max(0, n)) : 1;
116
+ }
117
+ function rBl() {
118
+ const v = rLS(LS_BL);
119
+ if (!v) return DEF_BL;
120
+ const n = +v;
121
+ return isFinite(n) ? Math.min(60, Math.max(0, n)) : DEF_BL;
122
+ }
123
+ function rSop() {
124
+ const v = rLS(LS_SOP);
125
+ if (!v) return DEF_SOP;
126
+ const n = +v;
127
+ return isFinite(n) ? Math.min(1, Math.max(0, n)) : DEF_SOP;
128
+ }
129
+ function rBgState() {
130
+ try {
131
+ const v = JSON.parse(rLS(LS_BG) || "");
132
+ if (v && typeof v.zoom === "number" && typeof v.iw === "number" && v.iw > 0 && v.ih > 0) return v;
133
+ } catch {}
134
+ return {
135
+ zoom: 1,
136
+ x: 0,
137
+ y: 0,
138
+ iw: 0,
139
+ ih: 0
140
+ };
141
+ }
142
+ function hsvToHsl(h, s, v) {
143
+ const l = v * (1 - s / 2);
144
+ return [
145
+ h,
146
+ l === 0 || l === 1 ? 0 : (v - l) / Math.min(l, 1 - l),
147
+ l
148
+ ];
149
+ }
150
+ function hslToHsv(h, s, l) {
151
+ const v = l + s * Math.min(l, 1 - l);
152
+ return [
153
+ h,
154
+ v === 0 ? 0 : 2 * (1 - l / v),
155
+ v
156
+ ];
157
+ }
158
+ function genTokens(hue, sat, lit) {
159
+ const dark = lit < .55;
160
+ const h = (d) => ((hue + d) % 360 + 360) % 360;
161
+ const s = (d) => Math.max(0, Math.min(1, sat + d));
162
+ const l = (d) => Math.max(0, Math.min(1, lit + d));
163
+ const hsl = (hh, ss, ll) => `hsl(${Math.round(hh)},${Math.round(ss * 100)}%,${Math.round(ll * 100)}%)`;
164
+ const rgba = (hh, ss, ll, a) => {
165
+ `${Math.round(hh)}${Math.round(ss * 100)}${Math.round(ll * 100)}`;
166
+ return `hsla(${Math.round(hh)},${Math.round(ss * 100)}%,${Math.round(ll * 100)}%,${a})`;
167
+ };
168
+ if (dark) return {
169
+ colorScheme: "dark",
170
+ tokens: {
171
+ "--dsw-alias-bg-base": hsl(h(0), s(0), l(-.04)),
172
+ "--dsw-alias-bg-layer-1": hsl(h(0), s(0), l(.02)),
173
+ "--dsw-alias-bg-layer-2": hsl(h(0), s(0), l(.07)),
174
+ "--dsw-alias-bg-layer-3": hsl(h(0), s(-.05), l(.12)),
175
+ "--dsw-alias-bg-overlay": hsl(h(0), s(-.05), l(.12)),
176
+ "--dsw-alias-border-l1": rgba(h(0), s(-.1), l(.18), .12),
177
+ "--dsw-alias-border-l2": rgba(h(0), s(-.1), l(.22), .22),
178
+ "--dsw-alias-label-primary": hsl(0, 0, 1),
179
+ "--dsw-alias-label-secondary": hsl(0, 0, 1),
180
+ "--dsw-alias-label-tertiary": hsl(0, 0, 1),
181
+ "--dsw-alias-brand-primary": hsl(h(0), s(.1), Math.max(l(.2), .5)),
182
+ "--dsw-alias-brand-text": l(.2) > .6 ? "#000" : "#fff",
183
+ "--dsw-alias-button-primary-hover": hsl(h(0), s(.1), Math.max(l(.28), .58)),
184
+ "--dsw-alias-button-primary-dimmed": hsl(h(0), s(0), l(.07)),
185
+ "--dsw-alias-button-elevated-fill": hsl(h(0), s(0), l(.04)),
186
+ "--dsw-alias-interactive-bg-hover": rgba(h(0), s(0), Math.max(l(.15), .4), .12),
187
+ "--dsw-alias-interactive-bg-active": rgba(h(0), s(0), Math.max(l(.15), .4), .2),
188
+ "--dsw-alias-markdown-code-block": hsl(h(0), s(0), l(-.06)),
189
+ "--dsw-alias-markdown-inline-code": hsl(h(0), s(0), l(.04)),
190
+ "--dsw-alias-state-error-primary": "#ff5c72",
191
+ "--dsw-alias-state-success-primary": "#3ddc84",
192
+ "--dsw-alias-state-warn-primary": "#ffb347",
193
+ "--dsw-specific-sidebar-fill": hsl(h(0), s(0), l(-.06)),
194
+ "--dsw-specific-sidebar-nav-item-active": hsl(h(0), s(0), l(.04)),
195
+ "--dsw-specific-sidebar-nav-item-hover": hsl(h(0), s(0), l(0)),
196
+ "--dsw-specific-input-major": hsl(h(0), s(0), l(.02)),
197
+ "--dsw-alias-scrollbar-bg-l1": hsl(h(0), s(-.05), l(.12)),
198
+ "--dsw-alias-scrollbar-bg-l2": hsl(h(0), s(-.05), l(.16)),
199
+ "--dsw-alias-scrollbar-hover-l1": hsl(h(0), s(-.05), l(.22)),
200
+ "--dsw-alias-scrollbar-hover-l2": hsl(h(0), s(-.05), l(.22))
201
+ }
202
+ };
203
+ return {
204
+ colorScheme: "light",
205
+ tokens: {
206
+ "--dsw-alias-bg-base": hsl(h(0), s(-.08), l(.03)),
207
+ "--dsw-alias-bg-layer-1": hsl(h(0), s(-.12), l(.07)),
208
+ "--dsw-alias-bg-layer-2": hsl(h(0), s(-.1), l(-.03)),
209
+ "--dsw-alias-bg-layer-3": hsl(h(0), s(-.08), l(-.09)),
210
+ "--dsw-alias-bg-overlay": hsl(h(0), s(-.12), l(.08)),
211
+ "--dsw-alias-border-l1": rgba(h(0), s(-.15), l(-.35), .18),
212
+ "--dsw-alias-border-l2": rgba(h(0), s(-.15), l(-.35), .3),
213
+ "--dsw-alias-label-primary": hsl(0, 0, 0),
214
+ "--dsw-alias-label-secondary": hsl(0, 0, 0),
215
+ "--dsw-alias-label-tertiary": hsl(0, 0, 0),
216
+ "--dsw-alias-brand-primary": hsl(h(0), s(.05), Math.min(l(-.18), .45)),
217
+ "--dsw-alias-brand-text": "#fff",
218
+ "--dsw-alias-button-primary-hover": hsl(h(0), s(.05), Math.min(l(-.12), .5)),
219
+ "--dsw-alias-button-primary-dimmed": hsl(h(0), s(-.1), l(-.03)),
220
+ "--dsw-alias-button-elevated-fill": hsl(h(0), s(-.1), l(.1)),
221
+ "--dsw-alias-interactive-bg-hover": rgba(h(0), s(0), l(-.3), .08),
222
+ "--dsw-alias-interactive-bg-active": rgba(h(0), s(0), l(-.3), .14),
223
+ "--dsw-alias-markdown-code-block": hsl(h(0), s(-.1), l(-.03)),
224
+ "--dsw-alias-markdown-inline-code": hsl(h(0), s(-.08), l(.04)),
225
+ "--dsw-specific-sidebar-fill": hsl(h(0), s(-.1), l(-.03)),
226
+ "--dsw-specific-sidebar-nav-item-active": hsl(h(0), s(-.08), l(.05)),
227
+ "--dsw-specific-sidebar-nav-item-hover": hsl(h(0), s(-.12), l(0)),
228
+ "--dsw-specific-input-major": hsl(h(0), s(-.12), l(.1)),
229
+ "--dsw-alias-scrollbar-bg-l1": hsl(h(0), s(-.1), l(-.08)),
230
+ "--dsw-alias-scrollbar-bg-l2": hsl(h(0), s(-.08), l(-.12)),
231
+ "--dsw-alias-scrollbar-hover-l1": hsl(h(0), s(-.08), l(-.16)),
232
+ "--dsw-alias-scrollbar-hover-l2": hsl(h(0), s(-.08), l(-.16))
233
+ }
234
+ };
235
+ }
236
+ let wpEl = null;
237
+ let ctxRef = null;
238
+ function toRgba(c, a) {
239
+ const hx = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(c.trim());
240
+ if (hx) {
241
+ let d = hx[1];
242
+ if (d.length === 3) d = d.split("").map((x) => x + x).join("");
243
+ const n = parseInt(d, 16);
244
+ return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${a})`;
245
+ }
246
+ const rgb = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)$/i.exec(c.trim());
247
+ if (rgb) return `rgba(${rgb[1]},${rgb[2]},${rgb[3]},${a})`;
248
+ const hsl = /^hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%/i.exec(c.trim());
249
+ if (hsl) return `hsla(${hsl[1]},${hsl[2]}%,${hsl[3]}%,${a})`;
250
+ return c.trim();
251
+ }
252
+ let appliedTokenNames = [];
253
+ /** Remove every inline token this plugin wrote (teardown symmetry). */
254
+ function clearCustomTokens() {
255
+ for (const name of appliedTokenNames) document.body.style.removeProperty(name);
256
+ appliedTokenNames = [];
257
+ }
258
+ /**
259
+ * Write the saved color's full token set as inline variables on body — the
260
+ * same write surface the theme presenter owns, but derived DIRECTLY from the
261
+ * saved pick, so the theme color never depends on the theme service's active
262
+ * state or the presenter's timing. The bg-base and sidebar tokens are
263
+ * re-emitted at the requested alpha; every other token (layers, labels,
264
+ * borders, brand) is written verbatim. No reads: nothing can observe a stale
265
+ * or reset theme value and leave the homepage on the system color.
266
+ */
267
+ function applyCustomTokens(op) {
268
+ const [h, s, l] = rColor();
269
+ const { tokens } = genTokens(h, s, l);
270
+ const sideOp = Math.min(1, op + .08);
271
+ clearCustomTokens();
272
+ if (l < .55) document.body.setAttribute("data-ds-dark-theme", "");
273
+ else document.body.removeAttribute("data-ds-dark-theme");
274
+ for (const [name, value] of Object.entries(tokens)) {
275
+ let v = value;
276
+ if (name === "--dsw-alias-bg-base") v = toRgba(value, op);
277
+ else if (name === "--dsw-specific-sidebar-fill") v = toRgba(value, sideOp);
278
+ document.body.style.setProperty(name, v);
279
+ appliedTokenNames.push(name);
280
+ }
281
+ }
282
+ const SETTINGS_STYLE_RULE = `[role="dialog"][aria-modal="true"][aria-labelledby]{background:var(--dsh-any-bg-settings-surface,var(--dsw-alias-bg-layer-2))}`;
283
+ function applySettingsOverrides(op) {
284
+ if (op >= 1) {
285
+ document.documentElement.style.removeProperty("--dsh-any-bg-settings-surface");
286
+ return;
287
+ }
288
+ const [h, s, l] = rColor();
289
+ const layer2 = genTokens(h, s, l).tokens["--dsw-alias-bg-layer-2"];
290
+ if (layer2 !== void 0) document.documentElement.style.setProperty("--dsh-any-bg-settings-surface", toRgba(layer2, op));
291
+ }
292
+ function applyWp(ctx) {
293
+ const url = rWp();
294
+ if (!url) {
295
+ wpEl?.remove();
296
+ wpEl = null;
297
+ } else {
298
+ if (!wpEl || !document.body.contains(wpEl)) {
299
+ wpEl = document.createElement("div");
300
+ wpEl.style.cssText = "position:fixed;inset:0;z-index:-1;pointer-events:none;background-repeat:no-repeat;";
301
+ document.body.prepend(wpEl);
302
+ }
303
+ const bg = rBgState();
304
+ wpEl.style.backgroundImage = `url("${url}")`;
305
+ if (bg.iw > 0) {
306
+ const fit = Math.min(window.innerWidth / bg.iw, window.innerHeight / bg.ih);
307
+ const w = bg.iw * fit * bg.zoom;
308
+ const h = bg.ih * fit * bg.zoom;
309
+ wpEl.style.backgroundSize = `${w}px ${h}px`;
310
+ wpEl.style.backgroundPosition = `${bg.x * window.innerWidth - w / 2}px ${bg.y * window.innerHeight - h / 2}px`;
311
+ } else {
312
+ wpEl.style.backgroundSize = "contain";
313
+ wpEl.style.backgroundPosition = "center";
314
+ }
315
+ const blur = rBl();
316
+ wpEl.style.filter = blur > 0 ? `blur(${blur}px)` : "none";
317
+ wpEl.style.opacity = String(rWop());
318
+ }
319
+ applyCustomTokens(rOp());
320
+ applySettingsOverrides(rSop());
321
+ }
322
+ function teardownWp() {
323
+ wpEl?.remove();
324
+ wpEl = null;
325
+ clearCustomTokens();
326
+ document.documentElement.style.removeProperty("--dsh-any-bg-settings-surface");
327
+ }
328
+ const WHEEL_SIZE = 220;
329
+ const CX = WHEEL_SIZE / 2;
330
+ const RING_OUTER = 106;
331
+ const RING_INNER = 82;
332
+ const SQ_HALF = Math.round(RING_INNER / Math.SQRT2);
333
+ function drawWheel(cvs, hue, sat, lit) {
334
+ const c = cvs.getContext("2d");
335
+ c.clearRect(0, 0, WHEEL_SIZE, WHEEL_SIZE);
336
+ for (let a = 0; a < 360; a++) {
337
+ const r1 = (a - 90) * Math.PI / 180;
338
+ const r2 = (a + 1.5 - 90) * Math.PI / 180;
339
+ c.beginPath();
340
+ c.arc(CX, CX, RING_OUTER, r1, r2);
341
+ c.arc(CX, CX, RING_INNER, r2, r1, true);
342
+ c.closePath();
343
+ c.fillStyle = `hsl(${a},100%,50%)`;
344
+ c.fill();
345
+ }
346
+ const gx = CX - SQ_HALF, gy = CX - SQ_HALF, sz = SQ_HALF * 2;
347
+ c.fillStyle = "#fff";
348
+ c.fillRect(gx, gy, sz, sz);
349
+ const gh = c.createLinearGradient(gx, 0, gx + sz, 0);
350
+ gh.addColorStop(0, "rgba(255,255,255,1)");
351
+ gh.addColorStop(1, `hsl(${hue},100%,50%)`);
352
+ c.fillStyle = gh;
353
+ c.fillRect(gx, gy, sz, sz);
354
+ const gv = c.createLinearGradient(0, gy, 0, gy + sz);
355
+ gv.addColorStop(0, "rgba(0,0,0,0)");
356
+ gv.addColorStop(1, "rgba(0,0,0,1)");
357
+ c.fillStyle = gv;
358
+ c.fillRect(gx, gy, sz, sz);
359
+ const hRad = (hue - 90) * Math.PI / 180;
360
+ const hR = 188 / 2;
361
+ const hmx = CX + Math.cos(hRad) * hR, hmy = CX + Math.sin(hRad) * hR;
362
+ c.beginPath();
363
+ c.arc(hmx, hmy, 8, 0, Math.PI * 2);
364
+ c.fillStyle = "rgba(0,0,0,0.25)";
365
+ c.fill();
366
+ c.beginPath();
367
+ c.arc(hmx, hmy, 6.5, 0, Math.PI * 2);
368
+ c.strokeStyle = "#fff";
369
+ c.lineWidth = 2;
370
+ c.stroke();
371
+ const smx = gx + sat * sz, smy = gy + (1 - lit) * sz;
372
+ c.beginPath();
373
+ c.arc(smx, smy, 7, 0, Math.PI * 2);
374
+ c.fillStyle = "rgba(0,0,0,0.25)";
375
+ c.fill();
376
+ c.beginPath();
377
+ c.arc(smx, smy, 5.5, 0, Math.PI * 2);
378
+ c.strokeStyle = "#fff";
379
+ c.lineWidth = 2;
380
+ c.stroke();
381
+ c.beginPath();
382
+ c.arc(smx, smy, 3.5, 0, Math.PI * 2);
383
+ c.strokeStyle = "#000";
384
+ c.lineWidth = 1;
385
+ c.stroke();
386
+ }
387
+ function hitTest(x, y) {
388
+ if (Math.abs(x - CX) <= SQ_HALF && Math.abs(y - CX) <= SQ_HALF) return "square";
389
+ const dx = x - CX, dy = y - CX;
390
+ const dist = Math.sqrt(dx * dx + dy * dy);
391
+ if (dist >= RING_INNER - 4 && dist <= 110) return "ring";
392
+ return null;
393
+ }
394
+ function pickHue(x, y) {
395
+ let angle = Math.atan2(y - CX, x - CX) * 180 / Math.PI + 90;
396
+ if (angle < 0) angle += 360;
397
+ return angle;
398
+ }
399
+ function pickSL(x, y) {
400
+ const gx = CX - SQ_HALF, gy = CX - SQ_HALF, sz = SQ_HALF * 2;
401
+ return [Math.max(0, Math.min(1, (x - gx) / sz)), Math.max(.02, Math.min(.98, 1 - (y - gy) / sz))];
402
+ }
403
+ function ColorWheel({ hue, sat, lit, onChange }) {
404
+ const cvsRef = (0, react.useRef)(null);
405
+ const [col, setCol] = (0, react.useState)({
406
+ hue,
407
+ sat,
408
+ lit
409
+ });
410
+ const colRef = (0, react.useRef)(col);
411
+ colRef.current = col;
412
+ (0, react.useEffect)(() => {
413
+ setCol((c) => c.hue === hue && c.sat === sat && c.lit === lit ? c : {
414
+ hue,
415
+ sat,
416
+ lit
417
+ });
418
+ }, [
419
+ hue,
420
+ sat,
421
+ lit
422
+ ]);
423
+ (0, react.useEffect)(() => {
424
+ if (cvsRef.current) drawWheel(cvsRef.current, col.hue, col.sat, col.lit);
425
+ }, [col]);
426
+ const apply = (0, react.useCallback)((nh, ns, nl) => {
427
+ setCol({
428
+ hue: nh,
429
+ sat: ns,
430
+ lit: nl
431
+ });
432
+ onChange(nh, ns, nl);
433
+ }, [onChange]);
434
+ const onDown = (0, react.useCallback)((e) => {
435
+ const r = cvsRef.current.getBoundingClientRect();
436
+ const x = e.clientX - r.left, y = e.clientY - r.top;
437
+ const region = hitTest(x, y);
438
+ if (!region) return;
439
+ if (region === "ring") apply(pickHue(x, y), colRef.current.sat, colRef.current.lit);
440
+ else {
441
+ const [s, l] = pickSL(x, y);
442
+ apply(colRef.current.hue, s, l);
443
+ }
444
+ const onMove = (ev) => {
445
+ const rr = cvsRef.current.getBoundingClientRect();
446
+ const mx = ev.clientX - rr.left, my = ev.clientY - rr.top;
447
+ if (region === "ring") {
448
+ const d = Math.sqrt((mx - CX) ** 2 + (my - CX) ** 2);
449
+ if (d >= RING_INNER - 10 && d <= 116) apply(pickHue(mx, my), colRef.current.sat, colRef.current.lit);
450
+ } else {
451
+ const [s, l] = pickSL(mx, my);
452
+ apply(colRef.current.hue, s, l);
453
+ }
454
+ };
455
+ const onUp = () => {
456
+ document.removeEventListener("mousemove", onMove);
457
+ document.removeEventListener("mouseup", onUp);
458
+ };
459
+ document.addEventListener("mousemove", onMove);
460
+ document.addEventListener("mouseup", onUp);
461
+ }, [apply]);
462
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
463
+ ref: cvsRef,
464
+ width: WHEEL_SIZE,
465
+ height: WHEEL_SIZE,
466
+ style: ST.wheelCanvas,
467
+ onMouseDown: onDown
468
+ });
469
+ }
470
+ function compress(img, side, q) {
471
+ const s = Math.min(1, side / Math.max(img.width, img.height));
472
+ const c = document.createElement("canvas");
473
+ c.width = Math.max(1, Math.round(img.width * s));
474
+ c.height = Math.max(1, Math.round(img.height * s));
475
+ c.getContext("2d").drawImage(img, 0, 0, c.width, c.height);
476
+ return c.toDataURL("image/jpeg", q);
477
+ }
478
+ function readImg(file, cb) {
479
+ const r = new FileReader();
480
+ r.onerror = () => cb(null);
481
+ r.onload = () => {
482
+ const img = new Image();
483
+ img.onerror = () => cb(null);
484
+ img.onload = () => {
485
+ try {
486
+ let u = compress(img, 1600, .75);
487
+ if (u.length > 2e6) u = compress(img, 1e3, .6);
488
+ if (u.length > 2e6) u = compress(img, 800, .5);
489
+ cb(u);
490
+ } catch {
491
+ cb(null);
492
+ }
493
+ };
494
+ img.src = r.result;
495
+ };
496
+ r.readAsDataURL(file);
497
+ }
498
+ const ST = {
499
+ root: {
500
+ display: "flex",
501
+ flexDirection: "column",
502
+ gap: "20px",
503
+ padding: "4px 0",
504
+ maxWidth: "640px"
505
+ },
506
+ h2: {
507
+ color: "var(--dsw-alias-label-primary)",
508
+ fontSize: "18px",
509
+ fontWeight: 600,
510
+ margin: 0
511
+ },
512
+ sub: {
513
+ color: "var(--dsw-alias-label-tertiary)",
514
+ fontSize: "13px"
515
+ },
516
+ hr: {
517
+ height: "1px",
518
+ background: "var(--dsw-alias-border-l2)",
519
+ border: "none",
520
+ margin: "4px 0"
521
+ },
522
+ label: {
523
+ color: "var(--dsw-alias-label-primary)",
524
+ fontSize: "16px",
525
+ fontWeight: 600,
526
+ marginBottom: "8px"
527
+ },
528
+ hint: {
529
+ color: "var(--dsw-alias-label-tertiary)",
530
+ fontSize: "12px",
531
+ lineHeight: "18px",
532
+ marginTop: "4px"
533
+ },
534
+ colorHint: {
535
+ color: "var(--dsw-alias-label-tertiary)",
536
+ fontSize: "12px",
537
+ lineHeight: "18px",
538
+ marginTop: "7px",
539
+ textAlign: "center"
540
+ },
541
+ wheelCanvas: {
542
+ cursor: "crosshair",
543
+ borderRadius: "50%"
544
+ },
545
+ center: {
546
+ display: "flex",
547
+ justifyContent: "center"
548
+ },
549
+ btnGroup: {
550
+ display: "flex",
551
+ justifyContent: "center",
552
+ marginTop: "10px"
553
+ },
554
+ row: {
555
+ display: "flex",
556
+ alignItems: "center",
557
+ gap: "10px",
558
+ flexWrap: "wrap"
559
+ },
560
+ sliderBlock: {
561
+ display: "flex",
562
+ flexDirection: "column",
563
+ gap: "4px"
564
+ },
565
+ sliderRow: {
566
+ display: "flex",
567
+ alignItems: "center",
568
+ gap: "10px"
569
+ },
570
+ sliderLabel: {
571
+ color: "var(--dsw-alias-label-secondary)",
572
+ fontSize: "13px"
573
+ },
574
+ smallHint: {
575
+ color: "var(--dsw-alias-label-tertiary)",
576
+ fontSize: "11px",
577
+ lineHeight: "16px"
578
+ },
579
+ slider: {
580
+ flex: 1,
581
+ accentColor: "var(--dsw-alias-brand-primary)",
582
+ minWidth: "160px"
583
+ },
584
+ sliderVal: {
585
+ color: "var(--dsw-alias-label-secondary)",
586
+ fontSize: "12px",
587
+ whiteSpace: "nowrap",
588
+ width: "44px",
589
+ textAlign: "right"
590
+ },
591
+ btn: {
592
+ height: "32px",
593
+ padding: "0 14px",
594
+ borderRadius: "8px",
595
+ border: "1px solid var(--dsw-alias-border-l2)",
596
+ background: "var(--dsw-alias-button-elevated-fill)",
597
+ color: "var(--dsw-alias-label-primary)",
598
+ cursor: "pointer",
599
+ fontSize: "13px",
600
+ font: "inherit",
601
+ boxSizing: "border-box"
602
+ },
603
+ btnDanger: { color: "var(--dsw-alias-state-error-primary)" },
604
+ btnPrimary: {
605
+ background: "var(--dsw-alias-brand-primary)",
606
+ color: "var(--dsw-alias-brand-text)",
607
+ border: "none"
608
+ },
609
+ preview: {
610
+ width: "368px",
611
+ height: "225px",
612
+ objectFit: "cover",
613
+ borderRadius: "6px",
614
+ border: "1px solid var(--dsw-alias-border-l2)",
615
+ cursor: "pointer"
616
+ },
617
+ sliders: {
618
+ display: "flex",
619
+ flexDirection: "column",
620
+ gap: "12px",
621
+ marginTop: "8px"
622
+ },
623
+ overlay: {
624
+ position: "fixed",
625
+ inset: 0,
626
+ zIndex: 9999,
627
+ background: "rgba(0,0,0,0.7)",
628
+ display: "flex",
629
+ flexDirection: "column",
630
+ alignItems: "center",
631
+ justifyContent: "center",
632
+ gap: "12px"
633
+ },
634
+ modalTitle: {
635
+ color: "#fff",
636
+ fontSize: "16px",
637
+ fontWeight: 500
638
+ },
639
+ modalHint: {
640
+ color: "rgba(255,255,255,0.6)",
641
+ fontSize: "12px"
642
+ },
643
+ previewRect: {
644
+ position: "relative",
645
+ overflow: "hidden",
646
+ border: "2px solid rgba(255,255,255,0.3)",
647
+ borderRadius: "8px",
648
+ background: "#000",
649
+ cursor: "grab"
650
+ },
651
+ previewImg: {
652
+ position: "absolute",
653
+ transformOrigin: "0 0",
654
+ pointerEvents: "none"
655
+ },
656
+ modalBtns: {
657
+ display: "flex",
658
+ gap: "10px"
659
+ }
660
+ };
661
+ function BgEditor({ url, t, onClose, onCommit }) {
662
+ const pw = Math.min(window.innerWidth * .75, 860);
663
+ const ph = Math.round(pw * window.innerHeight / window.innerWidth);
664
+ const saved = rBgState();
665
+ const [zoom, setZoom] = (0, react.useState)(saved.iw > 0 ? saved.zoom : 1);
666
+ const [pos, setPos] = (0, react.useState)(saved.iw > 0 ? {
667
+ x: saved.x * pw,
668
+ y: saved.y * ph
669
+ } : {
670
+ x: 0,
671
+ y: 0
672
+ });
673
+ const [imgSize, setImgSize] = (0, react.useState)({
674
+ w: 0,
675
+ h: 0
676
+ });
677
+ const containerRef = (0, react.useRef)(null);
678
+ const imgRef = (0, react.useRef)(null);
679
+ const dragRef = (0, react.useRef)({
680
+ active: false,
681
+ sx: 0,
682
+ sy: 0,
683
+ spx: 0,
684
+ spy: 0
685
+ });
686
+ (0, react.useEffect)(() => {
687
+ const img = new Image();
688
+ img.onload = () => {
689
+ const scale = Math.min(pw / img.width, ph / img.height);
690
+ const w = img.width * scale, h = img.height * scale;
691
+ setImgSize({
692
+ w,
693
+ h
694
+ });
695
+ const s = rBgState();
696
+ if (s.iw > 0 && s.iw === img.width && s.ih === img.height) {
697
+ setZoom(s.zoom);
698
+ setPos({
699
+ x: s.x * pw - w * s.zoom / 2,
700
+ y: s.y * ph - h * s.zoom / 2
701
+ });
702
+ } else {
703
+ setZoom(1);
704
+ setPos({
705
+ x: (pw - w) / 2,
706
+ y: (ph - h) / 2
707
+ });
708
+ }
709
+ };
710
+ img.src = url;
711
+ }, [
712
+ url,
713
+ pw,
714
+ ph
715
+ ]);
716
+ const onDown = (0, react.useCallback)((e) => {
717
+ e.preventDefault();
718
+ dragRef.current = {
719
+ active: true,
720
+ sx: e.clientX,
721
+ sy: e.clientY,
722
+ spx: pos.x,
723
+ spy: pos.y
724
+ };
725
+ const onMove = (ev) => {
726
+ if (!dragRef.current.active) return;
727
+ setPos({
728
+ x: dragRef.current.spx + ev.clientX - dragRef.current.sx,
729
+ y: dragRef.current.spy + ev.clientY - dragRef.current.sy
730
+ });
731
+ };
732
+ const onUp = () => {
733
+ dragRef.current.active = false;
734
+ document.removeEventListener("mousemove", onMove);
735
+ document.removeEventListener("mouseup", onUp);
736
+ };
737
+ document.addEventListener("mousemove", onMove);
738
+ document.addEventListener("mouseup", onUp);
739
+ }, [pos]);
740
+ const onWheelCb = (0, react.useCallback)((e) => {
741
+ e.preventDefault();
742
+ const rect = containerRef.current.getBoundingClientRect();
743
+ const mx = e.clientX - rect.left, my = e.clientY - rect.top;
744
+ const factor = e.deltaY > 0 ? .92 : 1.08;
745
+ const nz = Math.max(.1, Math.min(10, zoom * factor));
746
+ const nx = mx - (mx - pos.x) * (nz / zoom);
747
+ const ny = my - (my - pos.y) * (nz / zoom);
748
+ setZoom(nz);
749
+ setPos({
750
+ x: nx,
751
+ y: ny
752
+ });
753
+ }, [zoom, pos]);
754
+ (0, react.useEffect)(() => {
755
+ const el = containerRef.current;
756
+ if (!el) return;
757
+ el.addEventListener("wheel", onWheelCb, { passive: false });
758
+ return () => el.removeEventListener("wheel", onWheelCb);
759
+ }, [onWheelCb]);
760
+ const resetView = (0, react.useCallback)(() => {
761
+ if (imgSize.w === 0) return;
762
+ setZoom(1);
763
+ setPos({
764
+ x: (pw - imgSize.w) / 2,
765
+ y: (ph - imgSize.h) / 2
766
+ });
767
+ }, [
768
+ pw,
769
+ ph,
770
+ imgSize
771
+ ]);
772
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
773
+ style: ST.overlay,
774
+ onClick: (e) => {
775
+ if (e.target === e.currentTarget) onClose();
776
+ },
777
+ children: [
778
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
779
+ style: ST.modalTitle,
780
+ children: t("editorTitle")
781
+ }),
782
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
783
+ ref: containerRef,
784
+ style: {
785
+ ...ST.previewRect,
786
+ width: pw,
787
+ height: ph
788
+ },
789
+ onMouseDown: onDown,
790
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
791
+ ref: imgRef,
792
+ src: url,
793
+ alt: "",
794
+ draggable: false,
795
+ style: {
796
+ ...ST.previewImg,
797
+ width: imgSize.w,
798
+ height: imgSize.h,
799
+ transform: `translate(${pos.x}px,${pos.y}px) scale(${zoom})`
800
+ }
801
+ })
802
+ }),
803
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
804
+ style: ST.modalHint,
805
+ children: t("editorHint")
806
+ }),
807
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
808
+ style: ST.modalBtns,
809
+ children: [
810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
811
+ style: ST.btn,
812
+ onClick: resetView,
813
+ children: t("editorReset")
814
+ }),
815
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
816
+ style: ST.btn,
817
+ onClick: onClose,
818
+ children: t("editorCancel")
819
+ }),
820
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
821
+ style: {
822
+ ...ST.btn,
823
+ ...ST.btnPrimary
824
+ },
825
+ onClick: () => onCommit(zoom, (pos.x + imgSize.w * zoom / 2) / pw, (pos.y + imgSize.h * zoom / 2) / ph, imgRef.current?.naturalWidth ?? 0, imgRef.current?.naturalHeight ?? 0),
826
+ children: t("editorCommit")
827
+ })
828
+ ]
829
+ })
830
+ ]
831
+ });
832
+ }
833
+ function LiveSlider({ min, max, step, def, fmt, onInput, onChange }) {
834
+ const valRef = (0, react.useRef)(null);
835
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
836
+ style: ST.sliderRow,
837
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
838
+ type: "range",
839
+ min,
840
+ max,
841
+ step,
842
+ defaultValue: def,
843
+ style: ST.slider,
844
+ onInput: (e) => {
845
+ const v = Number(e.target.value);
846
+ onInput(v);
847
+ if (valRef.current) valRef.current.textContent = fmt(v);
848
+ },
849
+ onChange: (e) => onChange(Number(e.target.value))
850
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
851
+ ref: valRef,
852
+ style: ST.sliderVal,
853
+ children: fmt(def)
854
+ })]
855
+ });
856
+ }
857
+ function ThemeSection(props) {
858
+ const { t, hue, sat, lit, setColor, url, setWp, setOp, setWop, setBl, setSop, useStore } = props;
859
+ const storeUrl = useStore((s) => s.url);
860
+ const fileRef = (0, react.useRef)(null);
861
+ const [editorOpen, setEditorOpen] = (0, react.useState)(false);
862
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
863
+ style: ST.root,
864
+ children: [
865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
866
+ style: ST.h2,
867
+ children: t("nav")
868
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
869
+ style: ST.sub,
870
+ children: t("subtitle")
871
+ })] }),
872
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
873
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
874
+ style: ST.label,
875
+ children: t("colorTitle")
876
+ }),
877
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
878
+ style: ST.center,
879
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorWheel, {
880
+ hue,
881
+ sat,
882
+ lit,
883
+ onChange: setColor
884
+ })
885
+ }),
886
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
887
+ style: ST.colorHint,
888
+ children: t("colorHint")
889
+ })
890
+ ] }),
891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { style: ST.hr }),
892
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
893
+ style: ST.label,
894
+ children: t("uiTitle")
895
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
896
+ style: ST.sliders,
897
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
898
+ style: ST.sliderBlock,
899
+ children: [
900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
901
+ style: ST.sliderLabel,
902
+ children: t("uiOpacity")
903
+ }),
904
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
905
+ style: ST.smallHint,
906
+ children: t("uiOpacityHint")
907
+ }),
908
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
909
+ min: 0,
910
+ max: 100,
911
+ step: 1,
912
+ def: Math.round(rOp() * 100),
913
+ fmt: (v) => `${v}%`,
914
+ onInput: (v) => {
915
+ const op = v / 100;
916
+ wLS(LS_OP, String(op));
917
+ applyCustomTokens(op);
918
+ },
919
+ onChange: (v) => setOp(v / 100)
920
+ })
921
+ ]
922
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
923
+ style: ST.sliderBlock,
924
+ children: [
925
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
926
+ style: ST.sliderLabel,
927
+ children: t("uiSop")
928
+ }),
929
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
930
+ style: ST.smallHint,
931
+ children: t("uiSopHint")
932
+ }),
933
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
934
+ min: 0,
935
+ max: 100,
936
+ step: 1,
937
+ def: Math.round(rSop() * 100),
938
+ fmt: (v) => `${v}%`,
939
+ onInput: (v) => {
940
+ const op = v / 100;
941
+ wLS(LS_SOP, String(op));
942
+ applySettingsOverrides(op);
943
+ },
944
+ onChange: (v) => setSop(v / 100)
945
+ })
946
+ ]
947
+ })]
948
+ })] }),
949
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { style: ST.hr }),
950
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
951
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
952
+ style: ST.label,
953
+ children: t("bgTitle")
954
+ }),
955
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
956
+ style: ST.center,
957
+ children: storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
958
+ src: storeUrl,
959
+ alt: "",
960
+ style: ST.preview,
961
+ onClick: () => setEditorOpen(true)
962
+ }) : null
963
+ }),
964
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
965
+ style: ST.btnGroup,
966
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
967
+ style: ST.row,
968
+ children: [
969
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
970
+ type: "button",
971
+ style: ST.btn,
972
+ onClick: () => fileRef.current?.click(),
973
+ children: t("bgChoose")
974
+ }),
975
+ storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
976
+ type: "button",
977
+ style: ST.btn,
978
+ onClick: () => setEditorOpen(true),
979
+ children: t("bgEdit")
980
+ }) : null,
981
+ storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
982
+ type: "button",
983
+ style: {
984
+ ...ST.btn,
985
+ ...ST.btnDanger
986
+ },
987
+ onClick: () => setWp(null),
988
+ children: t("bgRemove")
989
+ }) : null,
990
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
991
+ ref: fileRef,
992
+ type: "file",
993
+ accept: "image/*",
994
+ style: { display: "none" },
995
+ onChange: (e) => {
996
+ const f = e.target.files?.[0];
997
+ if (!f) return;
998
+ readImg(f, (d) => {
999
+ if (d) setWp(d);
1000
+ e.target.value = "";
1001
+ });
1002
+ }
1003
+ })
1004
+ ]
1005
+ })
1006
+ }),
1007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1008
+ style: ST.sliders,
1009
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1010
+ style: ST.sliderBlock,
1011
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1012
+ style: ST.sliderLabel,
1013
+ children: t("wpOpacity")
1014
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
1015
+ min: 0,
1016
+ max: 100,
1017
+ step: 1,
1018
+ def: Math.round(rWop() * 100),
1019
+ fmt: (v) => `${v}%`,
1020
+ onInput: (v) => {
1021
+ const op = v / 100;
1022
+ wLS(LS_WOP, String(op));
1023
+ if (wpEl) wpEl.style.opacity = String(op);
1024
+ },
1025
+ onChange: (v) => setWop(v / 100)
1026
+ })]
1027
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1028
+ style: ST.sliderBlock,
1029
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1030
+ style: ST.sliderLabel,
1031
+ children: t("bgBlur")
1032
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
1033
+ min: 0,
1034
+ max: 60,
1035
+ step: 1,
1036
+ def: rBl(),
1037
+ fmt: (v) => `${v}px`,
1038
+ onInput: (v) => {
1039
+ wLS(LS_BL, String(v));
1040
+ if (wpEl) wpEl.style.filter = v > 0 ? `blur(${v}px)` : "none";
1041
+ },
1042
+ onChange: (v) => setBl(v)
1043
+ })]
1044
+ })]
1045
+ }),
1046
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1047
+ style: ST.hint,
1048
+ children: t("bgHint")
1049
+ })
1050
+ ] }),
1051
+ editorOpen && storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BgEditor, {
1052
+ url: storeUrl,
1053
+ t,
1054
+ onClose: () => setEditorOpen(false),
1055
+ onCommit: (z, x, y, iw, ih) => {
1056
+ wLS(LS_BG, JSON.stringify({
1057
+ zoom: z,
1058
+ x,
1059
+ y,
1060
+ iw,
1061
+ ih
1062
+ }));
1063
+ applyWp(ctxRef);
1064
+ setEditorOpen(false);
1065
+ }
1066
+ }) : null
1067
+ ]
1068
+ });
1069
+ }
1070
+ function apply(ctx) {
1071
+ ctxRef = ctx;
1072
+ const [initH, initS, initL] = rColor();
1073
+ let customDispose = null;
1074
+ const registerCustom = (h, s, l) => {
1075
+ customDispose?.();
1076
+ try {
1077
+ const { colorScheme, tokens } = genTokens(h, s, l);
1078
+ customDispose = ctx.theme.register({
1079
+ id: CUSTOM_ID,
1080
+ colorScheme,
1081
+ tokens
1082
+ });
1083
+ } catch {
1084
+ customDispose = null;
1085
+ }
1086
+ ctx.theme.setTheme(CUSTOM_ID);
1087
+ };
1088
+ if (rLS(LS_COLOR)) registerCustom(initH, initS, initL);
1089
+ ctx.effect(() => () => {
1090
+ customDispose?.();
1091
+ }, "dsh-any-background: skin dispose");
1092
+ let styleEl;
1093
+ if (typeof document !== "undefined") {
1094
+ styleEl = document.createElement("style");
1095
+ styleEl.dataset.plugin = "dsh-any-background";
1096
+ styleEl.textContent = `body[data-ds-dark-theme="${CUSTOM_ID}"]::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(255,255,255,0.03) 0%,transparent 60%)}${SETTINGS_STYLE_RULE}`;
1097
+ document.head.appendChild(styleEl);
1098
+ }
1099
+ ctx.effect(() => () => {
1100
+ styleEl?.parentNode?.removeChild(styleEl);
1101
+ }, "dsh-any-background: gradient");
1102
+ let rev = 0;
1103
+ const store = (0, _deepseek_ai_dsh_client_runtime_client.defineStore)({
1104
+ init: () => ({
1105
+ url: null,
1106
+ rev: -1
1107
+ }),
1108
+ actions: { syncBg: (d, url, r) => {
1109
+ if (r <= d.rev) return;
1110
+ d.url = url;
1111
+ d.rev = r;
1112
+ } }
1113
+ });
1114
+ let bound;
1115
+ const syncBg = () => {
1116
+ rev++;
1117
+ bound?.syncBg(rWp(), rev);
1118
+ };
1119
+ applyWp(ctx);
1120
+ syncBg();
1121
+ ctx.effect(() => () => {
1122
+ teardownWp();
1123
+ }, "dsh-any-background: wp cleanup");
1124
+ ctx.effect(() => ctx.on("theme/change", () => {
1125
+ if (rLS(LS_COLOR)) {
1126
+ const snapshot = ctx.theme.getTheme();
1127
+ if (snapshot.preference !== CUSTOM_ID && snapshot.themes.some((t) => t.id === CUSTOM_ID)) ctx.theme.setTheme(CUSTOM_ID);
1128
+ }
1129
+ applyWp(ctx);
1130
+ }), "dsh-any-background: theme change");
1131
+ let frame = 0;
1132
+ const applySoon = () => {
1133
+ if (frame !== 0) return;
1134
+ frame = requestAnimationFrame(() => {
1135
+ frame = 0;
1136
+ applyWp(ctx);
1137
+ });
1138
+ };
1139
+ const sentinel = document.createElement("div");
1140
+ sentinel.style.cssText = "position:fixed;inset:0;pointer-events:none;visibility:hidden";
1141
+ document.body.append(sentinel);
1142
+ const viewportObserver = new ResizeObserver(applySoon);
1143
+ viewportObserver.observe(sentinel);
1144
+ const dprQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
1145
+ dprQuery.addEventListener("change", applySoon);
1146
+ ctx.effect(() => () => {
1147
+ viewportObserver.disconnect();
1148
+ dprQuery.removeEventListener("change", applySoon);
1149
+ sentinel.remove();
1150
+ }, "dsh-any-background: viewport watch");
1151
+ ctx.effect(() => ctx.locale.register(NS, {
1152
+ zh,
1153
+ en
1154
+ }), "dsh-any-background: i18n");
1155
+ const sectionInject = (actions) => {
1156
+ bound = actions;
1157
+ syncBg();
1158
+ const [wh, ws, wl] = rColor();
1159
+ const [dh, ds, dv] = hslToHsv(wh, ws, wl);
1160
+ return {
1161
+ t: ctx.locale.bind(NS),
1162
+ hue: dh,
1163
+ sat: ds,
1164
+ lit: dv,
1165
+ setColor: (nh, ns, nl) => {
1166
+ const [sh, ss, sl] = hsvToHsl(nh, ns, nl);
1167
+ wLS(LS_COLOR, JSON.stringify([
1168
+ sh,
1169
+ ss,
1170
+ sl
1171
+ ]));
1172
+ registerCustom(sh, ss, sl);
1173
+ applyWp(ctx);
1174
+ },
1175
+ setWp: (u) => {
1176
+ wLS(LS_WP, u);
1177
+ wLS(LS_BG, null);
1178
+ applyWp(ctx);
1179
+ syncBg();
1180
+ },
1181
+ setOp: (v) => {
1182
+ wLS(LS_OP, String(v));
1183
+ applyWp(ctx);
1184
+ syncBg();
1185
+ },
1186
+ setWop: (v) => {
1187
+ wLS(LS_WOP, String(v));
1188
+ applyWp(ctx);
1189
+ syncBg();
1190
+ },
1191
+ setBl: (v) => {
1192
+ wLS(LS_BL, String(v));
1193
+ applyWp(ctx);
1194
+ syncBg();
1195
+ },
1196
+ setSop: (v) => {
1197
+ wLS(LS_SOP, String(v));
1198
+ applySettingsOverrides(v);
1199
+ }
1200
+ };
1201
+ };
1202
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
1203
+ name: "settings.section",
1204
+ id: "dsh-any-background",
1205
+ order: 35,
1206
+ label: () => ctx.locale.bind(NS)("nav"),
1207
+ locale: NS,
1208
+ store,
1209
+ inject: sectionInject
1210
+ }, ThemeSection));
1211
+ const restoreSaved = () => {
1212
+ if (rLS(LS_COLOR)) {
1213
+ const [h, s, l] = rColor();
1214
+ registerCustom(h, s, l);
1215
+ }
1216
+ applyWp(ctx);
1217
+ };
1218
+ const restoreTimers = [300, 1500].map((delay) => window.setTimeout(restoreSaved, delay));
1219
+ ctx.effect(() => () => {
1220
+ restoreTimers.forEach((id) => window.clearTimeout(id));
1221
+ }, "dsh-any-background: boot restore");
1222
+ const watchdogId = window.setInterval(() => {
1223
+ if (!rLS(LS_COLOR)) return;
1224
+ const snapshot = ctx.theme.getTheme();
1225
+ let changed = false;
1226
+ if (!snapshot.themes.some((t) => t.id === CUSTOM_ID)) {
1227
+ const [h, s, l] = rColor();
1228
+ registerCustom(h, s, l);
1229
+ changed = true;
1230
+ } else if (snapshot.preference !== CUSTOM_ID) {
1231
+ ctx.theme.setTheme(CUSTOM_ID);
1232
+ changed = true;
1233
+ }
1234
+ if (changed) applyWp(ctx);
1235
+ }, 1e3);
1236
+ ctx.effect(() => () => {
1237
+ window.clearInterval(watchdogId);
1238
+ }, "dsh-any-background: theme watchdog");
1239
+ }
1240
+ //#endregion
1241
+ exports.apply = apply;
1242
+ exports.inject = inject;
1243
+ exports.name = name;
1244
+ return module.exports;
1245
+ }
1246
+ });
1247
+
1248
+ //# sourceMappingURL=client.js.map