dsh-any-background 0.1.4 → 0.1.5

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