dsh-any-background 0.1.5 → 0.1.6

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