dsh-any-background 0.1.7 → 0.1.8
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/LICENSE +21 -21
- package/README.md +40 -209
- package/README.zh.md +43 -211
- package/cordis.patch.yml +8 -8
- package/lib/client.js +2866 -963
- package/lib/client.js.map +1 -1
- package/lib/index.js +47 -2
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -7,13 +7,25 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
let react_dom = require("react-dom");
|
|
10
11
|
//#region src/client/i18n.ts
|
|
11
12
|
const NS = "settings.anyBg";
|
|
12
13
|
const zh = {
|
|
13
14
|
nav: "主题",
|
|
14
|
-
|
|
15
|
+
brandTag: "外观定制",
|
|
16
|
+
close: "关闭",
|
|
17
|
+
pageColor: "色彩",
|
|
18
|
+
pageInterface: "界面",
|
|
19
|
+
pageBackground: "背景",
|
|
20
|
+
pageProfile: "配置",
|
|
21
|
+
descColor: "拖动色轮或输入精确数值,整个界面的配色将实时跟随变化",
|
|
22
|
+
descInterface: "为主界面的各个区域单独调节透明度与模糊,营造空间层次感",
|
|
23
|
+
descBackground: "上传一张图片作为壁纸,或让算法为你实时生成动态背景",
|
|
24
|
+
descProfile: "将当前主题导出为文件备份,或从文件一键恢复完整配置",
|
|
15
25
|
colorTitle: "主题色",
|
|
16
|
-
colorHint: "
|
|
26
|
+
colorHint: "在色轮外圈选择色相,在内部方形中调整饱和度和明度;双击滑块可恢复默认值",
|
|
27
|
+
swatchTitle: "灵感色板",
|
|
28
|
+
hexCaption: "当前主题色",
|
|
17
29
|
uiTitle: "主界面",
|
|
18
30
|
uiOpacity: "透明度",
|
|
19
31
|
uiBlur: "模糊度",
|
|
@@ -21,75 +33,156 @@ window.__ModuleLoader__.load({
|
|
|
21
33
|
uiOpacitySide: "侧边栏",
|
|
22
34
|
uiOpacityCard: "对话框中选项面板",
|
|
23
35
|
uiSop: "设置界面透明度",
|
|
24
|
-
bgTitle: "
|
|
36
|
+
bgTitle: "背景",
|
|
25
37
|
bgChoose: "选择图片",
|
|
26
|
-
bgRemove: "
|
|
38
|
+
bgRemove: "移除背景",
|
|
27
39
|
bgEdit: "编辑位置",
|
|
28
|
-
wpOpacity: "
|
|
29
|
-
bgBlur: "
|
|
30
|
-
|
|
40
|
+
wpOpacity: "背景透明度",
|
|
41
|
+
bgBlur: "背景模糊",
|
|
42
|
+
bgSourceImage: "图片",
|
|
43
|
+
bgSourceGenerated: "动态生成",
|
|
44
|
+
dropHint: "点击选择图片,或将图片拖到这里",
|
|
45
|
+
liveBadge: "动态壁纸",
|
|
46
|
+
bgHint: "图片模式下悬停预览可编辑位置;动态生成模式下可选择类型、预设与参数。绑定种子后刷新页面壁纸保持不变,取消绑定则每次刷新随机换新",
|
|
31
47
|
editorTitle: "背景编辑器",
|
|
32
48
|
editorHint: "拖动移动图片,滚轮缩放大小",
|
|
33
49
|
editorCommit: "确认",
|
|
34
50
|
editorCancel: "取消",
|
|
35
51
|
editorReset: "重置",
|
|
36
|
-
extractColor: "
|
|
52
|
+
extractColor: "从背景提取主题色",
|
|
37
53
|
extracting: "取色中…",
|
|
38
54
|
extractNoWp: "请先设置背景图片",
|
|
39
|
-
extractDone: "
|
|
40
|
-
extractFail: "
|
|
55
|
+
extractDone: "已应用背景主色调",
|
|
56
|
+
extractFail: "未在背景中找到鲜明的颜色,请换一张背景",
|
|
41
57
|
crashTitle: "界面渲染出错",
|
|
42
58
|
crashDesc: "主题设置面板遇到问题,点击下方按钮重置后重试",
|
|
43
59
|
crashReset: "重置面板",
|
|
44
60
|
exportTheme: "导出配置",
|
|
45
61
|
importTheme: "导入配置",
|
|
62
|
+
exportCardTitle: "导出配置",
|
|
63
|
+
exportCardDesc: "将主题色、透明度、模糊与壁纸打包为 dsh-any-theme.json 文件",
|
|
64
|
+
importCardTitle: "导入配置",
|
|
65
|
+
importCardDesc: "从之前导出的 JSON 文件一键恢复完整主题",
|
|
66
|
+
toastExportDone: "配置文件已开始下载",
|
|
46
67
|
importDone: "已导入主题配置",
|
|
47
68
|
importFail: "导入失败,文件格式不正确",
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
footerTag: "外观插件",
|
|
70
|
+
eyedropper: "从背景取色",
|
|
71
|
+
pickerTitle: "从背景取色",
|
|
72
|
+
pickerHint: "移动鼠标预览颜色,点击背景选取为主题色",
|
|
73
|
+
pickerClose: "关闭",
|
|
74
|
+
bgTypeImage: "图片",
|
|
75
|
+
bgTypeMesh: "网格渐变",
|
|
76
|
+
bgTypeShader: "Shader",
|
|
77
|
+
bgTypePattern: "几何图案",
|
|
78
|
+
bgMeshDesc: "柔和的多点渐变色块",
|
|
79
|
+
bgShaderDesc: "持续流动的光影动画",
|
|
80
|
+
bgPatternDesc: "规律的几何纹理",
|
|
81
|
+
bgRegenerate: "重新生成",
|
|
82
|
+
bgSeedLocked: "已绑定 · 刷新不变",
|
|
83
|
+
bgSeedUnlocked: "未绑定 · 刷新换新",
|
|
84
|
+
seedLock: "锁定种子",
|
|
85
|
+
bgMeshScale: "扩散范围",
|
|
86
|
+
bgMeshIntensity: "色彩强度",
|
|
87
|
+
bgShaderPreset: "预设",
|
|
88
|
+
bgShaderSpeed: "流动速度",
|
|
89
|
+
bgShaderScale: "纹理尺度",
|
|
90
|
+
bgPatternPreset: "图案",
|
|
91
|
+
bgPatternDensity: "密度",
|
|
92
|
+
bgPatternScale: "尺度",
|
|
93
|
+
presetAurora: "极光",
|
|
94
|
+
presetNebula: "星云",
|
|
95
|
+
presetNoise: "流动噪声",
|
|
96
|
+
presetDots: "点阵",
|
|
97
|
+
presetWaves: "波浪",
|
|
98
|
+
presetPoly: "低多边形"
|
|
52
99
|
};
|
|
53
100
|
const en = {
|
|
54
101
|
nav: "Theme",
|
|
55
|
-
|
|
102
|
+
brandTag: "Appearance",
|
|
103
|
+
close: "Close",
|
|
104
|
+
pageColor: "Color",
|
|
105
|
+
pageInterface: "Interface",
|
|
106
|
+
pageBackground: "Background",
|
|
107
|
+
pageProfile: "Profile",
|
|
108
|
+
descColor: "Drag the wheel or type exact values — the whole UI follows live",
|
|
109
|
+
descInterface: "Tune opacity and blur per surface to build depth",
|
|
110
|
+
descBackground: "Upload an image as wallpaper, or let the algorithm paint a live background",
|
|
111
|
+
descProfile: "Export the current theme as a file, or restore it from one",
|
|
56
112
|
colorTitle: "Theme color",
|
|
57
|
-
colorHint: "Pick hue on the ring, adjust saturation & lightness in the square",
|
|
58
|
-
|
|
113
|
+
colorHint: "Pick hue on the outer ring, adjust saturation & lightness in the square; double-click a slider to reset",
|
|
114
|
+
swatchTitle: "Quick swatches",
|
|
115
|
+
hexCaption: "Current accent",
|
|
116
|
+
uiTitle: "Interface",
|
|
59
117
|
uiOpacity: "Opacity",
|
|
60
118
|
uiBlur: "Blur",
|
|
61
119
|
uiOpacityBg: "Main background",
|
|
62
120
|
uiOpacitySide: "Sidebar",
|
|
63
121
|
uiOpacityCard: "Cards & panels",
|
|
64
122
|
uiSop: "Settings interface opacity",
|
|
65
|
-
bgTitle: "
|
|
123
|
+
bgTitle: "Background",
|
|
66
124
|
bgChoose: "Choose image",
|
|
67
|
-
bgRemove: "Remove
|
|
125
|
+
bgRemove: "Remove background",
|
|
68
126
|
bgEdit: "Edit position",
|
|
69
|
-
wpOpacity: "
|
|
70
|
-
bgBlur: "
|
|
71
|
-
|
|
127
|
+
wpOpacity: "Background opacity",
|
|
128
|
+
bgBlur: "Background blur",
|
|
129
|
+
bgSourceImage: "Image",
|
|
130
|
+
bgSourceGenerated: "Generated",
|
|
131
|
+
dropHint: "Click to choose an image, or drop one here",
|
|
132
|
+
liveBadge: "Live wallpaper",
|
|
133
|
+
bgHint: "In image mode hover the preview to edit position; in generated mode pick a type, preset and parameters. With the seed locked the wallpaper stays the same across refreshes; unlocked, every refresh randomizes it",
|
|
72
134
|
editorTitle: "Background editor",
|
|
73
135
|
editorHint: "Drag to move, scroll to zoom",
|
|
74
136
|
editorCommit: "Confirm",
|
|
75
137
|
editorCancel: "Cancel",
|
|
76
138
|
editorReset: "Reset",
|
|
77
|
-
extractColor: "Extract from
|
|
139
|
+
extractColor: "Extract from background",
|
|
78
140
|
extracting: "Extracting…",
|
|
79
|
-
extractNoWp: "Set a
|
|
80
|
-
extractDone: "
|
|
81
|
-
extractFail: "No vivid color found in this
|
|
141
|
+
extractNoWp: "Set a background first",
|
|
142
|
+
extractDone: "Background color applied",
|
|
143
|
+
extractFail: "No vivid color found in this background, try another",
|
|
82
144
|
crashTitle: "Section crashed",
|
|
83
145
|
crashDesc: "The theme panel hit an error. Reset below to recover.",
|
|
84
146
|
crashReset: "Reset panel",
|
|
85
147
|
exportTheme: "Export",
|
|
86
148
|
importTheme: "Import",
|
|
149
|
+
exportCardTitle: "Export profile",
|
|
150
|
+
exportCardDesc: "Bundle color, opacity, blur and wallpaper into dsh-any-theme.json",
|
|
151
|
+
importCardTitle: "Import profile",
|
|
152
|
+
importCardDesc: "Restore a full theme from a previously exported JSON file",
|
|
153
|
+
toastExportDone: "Export started",
|
|
87
154
|
importDone: "Theme imported",
|
|
88
155
|
importFail: "Import failed — invalid file",
|
|
156
|
+
footerTag: "Appearance plugin",
|
|
89
157
|
eyedropper: "Eyedropper",
|
|
90
|
-
pickerTitle: "Pick from
|
|
158
|
+
pickerTitle: "Pick from background",
|
|
91
159
|
pickerHint: "Hover to preview, click to pick as theme color",
|
|
92
|
-
pickerClose: "Close"
|
|
160
|
+
pickerClose: "Close",
|
|
161
|
+
bgTypeImage: "Image",
|
|
162
|
+
bgTypeMesh: "Mesh gradient",
|
|
163
|
+
bgTypeShader: "Shader",
|
|
164
|
+
bgTypePattern: "Pattern",
|
|
165
|
+
bgMeshDesc: "Soft multi-blob gradients",
|
|
166
|
+
bgShaderDesc: "Flowing light animation",
|
|
167
|
+
bgPatternDesc: "Regular geometric texture",
|
|
168
|
+
bgRegenerate: "Regenerate",
|
|
169
|
+
bgSeedLocked: "Seeded · stable on refresh",
|
|
170
|
+
bgSeedUnlocked: "Unseeded · new on refresh",
|
|
171
|
+
seedLock: "Lock seed",
|
|
172
|
+
bgMeshScale: "Spread",
|
|
173
|
+
bgMeshIntensity: "Intensity",
|
|
174
|
+
bgShaderPreset: "Preset",
|
|
175
|
+
bgShaderSpeed: "Flow speed",
|
|
176
|
+
bgShaderScale: "Texture scale",
|
|
177
|
+
bgPatternPreset: "Pattern",
|
|
178
|
+
bgPatternDensity: "Density",
|
|
179
|
+
bgPatternScale: "Scale",
|
|
180
|
+
presetAurora: "Aurora",
|
|
181
|
+
presetNebula: "Nebula",
|
|
182
|
+
presetNoise: "Flowing noise",
|
|
183
|
+
presetDots: "Dots",
|
|
184
|
+
presetWaves: "Waves",
|
|
185
|
+
presetPoly: "Low poly"
|
|
93
186
|
};
|
|
94
187
|
//#endregion
|
|
95
188
|
//#region src/client/state.ts
|
|
@@ -115,7 +208,10 @@ window.__ModuleLoader__.load({
|
|
|
115
208
|
y: 0,
|
|
116
209
|
iw: 0,
|
|
117
210
|
ih: 0
|
|
118
|
-
}
|
|
211
|
+
},
|
|
212
|
+
backgroundType: "image",
|
|
213
|
+
generatedBg: null,
|
|
214
|
+
regenerateOnReload: false
|
|
119
215
|
};
|
|
120
216
|
let cfg = {
|
|
121
217
|
...DEFAULT_CONFIG,
|
|
@@ -123,13 +219,24 @@ window.__ModuleLoader__.load({
|
|
|
123
219
|
blurs: { ...DEFAULT_CONFIG.blurs },
|
|
124
220
|
bgState: { ...DEFAULT_CONFIG.bgState }
|
|
125
221
|
};
|
|
222
|
+
let wpImageUrl = null;
|
|
126
223
|
let wpUrl = null;
|
|
224
|
+
function setWpImageUrl(url) {
|
|
225
|
+
wpImageUrl = url;
|
|
226
|
+
}
|
|
127
227
|
function setWpUrl(url) {
|
|
128
228
|
wpUrl = url;
|
|
129
229
|
}
|
|
130
230
|
function setBgState(s) {
|
|
131
231
|
cfg.bgState = s;
|
|
132
232
|
}
|
|
233
|
+
let bgDark = null;
|
|
234
|
+
function setBgDark(v) {
|
|
235
|
+
bgDark = v;
|
|
236
|
+
}
|
|
237
|
+
function rBgDark() {
|
|
238
|
+
return bgDark;
|
|
239
|
+
}
|
|
133
240
|
function rHasColor() {
|
|
134
241
|
return cfg.color !== null;
|
|
135
242
|
}
|
|
@@ -140,8 +247,12 @@ window.__ModuleLoader__.load({
|
|
|
140
247
|
.25
|
|
141
248
|
];
|
|
142
249
|
}
|
|
250
|
+
function rWpImage() {
|
|
251
|
+
return wpImageUrl;
|
|
252
|
+
}
|
|
253
|
+
/** Display URL: the uploaded image when in image mode, else the generated snapshot. */
|
|
143
254
|
function rWp() {
|
|
144
|
-
return wpUrl;
|
|
255
|
+
return cfg.backgroundType === "image" ? wpImageUrl : wpUrl;
|
|
145
256
|
}
|
|
146
257
|
function rOps() {
|
|
147
258
|
const o = cfg.opacities ?? {};
|
|
@@ -184,6 +295,9 @@ window.__ModuleLoader__.load({
|
|
|
184
295
|
const legacy = typeof c.opacity === "number" ? c.opacity : null;
|
|
185
296
|
const ops = c.opacities ?? {};
|
|
186
297
|
const bl = c.blurs ?? {};
|
|
298
|
+
const bgType = c.backgroundType === "mesh" || c.backgroundType === "shader" || c.backgroundType === "pattern" ? c.backgroundType : DEFAULT_CONFIG.backgroundType;
|
|
299
|
+
const gen = c.generatedBg && typeof c.generatedBg === "object" ? c.generatedBg : null;
|
|
300
|
+
const generatedBg = gen && gen.type === bgType ? c.generatedBg : null;
|
|
187
301
|
cfg = {
|
|
188
302
|
color,
|
|
189
303
|
opacities: {
|
|
@@ -206,7 +320,41 @@ window.__ModuleLoader__.load({
|
|
|
206
320
|
y: typeof bg.y === "number" ? bg.y : 0,
|
|
207
321
|
iw: typeof bg.iw === "number" && bg.iw > 0 ? bg.iw : 0,
|
|
208
322
|
ih: typeof bg.ih === "number" && bg.ih > 0 ? bg.ih : 0
|
|
209
|
-
}
|
|
323
|
+
},
|
|
324
|
+
backgroundType: bgType,
|
|
325
|
+
generatedBg: generatedBg ? normalizeGeneratedBg(generatedBg) : null,
|
|
326
|
+
regenerateOnReload: typeof c.regenerateOnReload === "boolean" ? c.regenerateOnReload : DEFAULT_CONFIG.regenerateOnReload
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function normalizeGeneratedBg(p) {
|
|
330
|
+
if (!p) return null;
|
|
331
|
+
if (p.type === "mesh") return {
|
|
332
|
+
type: "mesh",
|
|
333
|
+
seed: typeof p.seed === "number" ? p.seed : 0,
|
|
334
|
+
scale: typeof p.scale === "number" ? Math.min(3, Math.max(.3, p.scale)) : 1,
|
|
335
|
+
intensity: typeof p.intensity === "number" ? Math.min(1, Math.max(0, p.intensity)) : .6
|
|
336
|
+
};
|
|
337
|
+
if (p.type === "shader") return {
|
|
338
|
+
type: "shader",
|
|
339
|
+
preset: [
|
|
340
|
+
"aurora",
|
|
341
|
+
"nebula",
|
|
342
|
+
"noise"
|
|
343
|
+
].includes(p.preset) ? p.preset : "aurora",
|
|
344
|
+
speed: typeof p.speed === "number" ? Math.min(2, Math.max(0, p.speed)) : .3,
|
|
345
|
+
scale: typeof p.scale === "number" ? Math.min(3, Math.max(.3, p.scale)) : 1,
|
|
346
|
+
seed: typeof p.seed === "number" ? Math.floor(p.seed) : 0
|
|
347
|
+
};
|
|
348
|
+
return {
|
|
349
|
+
type: "pattern",
|
|
350
|
+
preset: [
|
|
351
|
+
"dots",
|
|
352
|
+
"waves",
|
|
353
|
+
"poly"
|
|
354
|
+
].includes(p.preset) ? p.preset : "dots",
|
|
355
|
+
density: typeof p.density === "number" ? Math.min(1, Math.max(0, p.density)) : .5,
|
|
356
|
+
scale: typeof p.scale === "number" ? Math.min(3, Math.max(.3, p.scale)) : 1,
|
|
357
|
+
seed: typeof p.seed === "number" ? Math.floor(p.seed) : 0
|
|
210
358
|
};
|
|
211
359
|
}
|
|
212
360
|
//#endregion
|
|
@@ -255,8 +403,9 @@ window.__ModuleLoader__.load({
|
|
|
255
403
|
if (data && typeof data === "object") {
|
|
256
404
|
const d = data;
|
|
257
405
|
if (d.config) adoptConfig(d.config);
|
|
258
|
-
if (typeof d.wallpaper === "string")
|
|
259
|
-
else if (d.wallpaper === null)
|
|
406
|
+
if (typeof d.wallpaper === "string") setWpImageUrl(d.wallpaper);
|
|
407
|
+
else if (d.wallpaper === null) setWpImageUrl(null);
|
|
408
|
+
if (cfg.backgroundType === "image") setWpUrl(d.wallpaper === null ? null : d.wallpaper);
|
|
260
409
|
}
|
|
261
410
|
}
|
|
262
411
|
/** Persist a wallpaper (null removes it). One-shot large payload, no debounce. */
|
|
@@ -288,8 +437,13 @@ window.__ModuleLoader__.load({
|
|
|
288
437
|
* same token set, and applyWp / applyCustomTokens / applySettingsOverrides call
|
|
289
438
|
* this repeatedly (slider drags, viewport re-applies), so cache the last result
|
|
290
439
|
* and skip the 30+ hsl() string builds when nothing changed.
|
|
440
|
+
*
|
|
441
|
+
* This is the simple, pre-Material-You version: dark/light is decided only by
|
|
442
|
+
* lit < 0.55, and all tokens are derived directly from the picked hue/sat/lit.
|
|
443
|
+
* The optional palette argument is kept for API compatibility but ignored here,
|
|
444
|
+
* so saved picks never get rewritten by a wallpaper-derived palette.
|
|
291
445
|
*/
|
|
292
|
-
function genTokens(hue, sat, lit) {
|
|
446
|
+
function genTokens(hue, sat, lit, _palette) {
|
|
293
447
|
const key = `${hue}|${sat}|${lit}`;
|
|
294
448
|
if (tokensCacheKey === key && tokensCache) return tokensCache;
|
|
295
449
|
tokensCacheKey = key;
|
|
@@ -415,7 +569,10 @@ window.__ModuleLoader__.load({
|
|
|
415
569
|
];
|
|
416
570
|
}
|
|
417
571
|
const EXTRACT_SIDE = 64;
|
|
418
|
-
function
|
|
572
|
+
function bucketsToHsl(b) {
|
|
573
|
+
return rgbToHsl(b.r / b.count, b.g / b.count, b.b / b.count);
|
|
574
|
+
}
|
|
575
|
+
function extractWallpaperPalette(dataUrl, bgState) {
|
|
419
576
|
return new Promise((resolve) => {
|
|
420
577
|
const img = new Image();
|
|
421
578
|
img.onerror = () => resolve(null);
|
|
@@ -448,41 +605,119 @@ window.__ModuleLoader__.load({
|
|
|
448
605
|
const px = g.getImageData(0, 0, EXTRACT_SIDE, EXTRACT_SIDE).data;
|
|
449
606
|
const counts = /* @__PURE__ */ new Uint32Array(4096);
|
|
450
607
|
const sums = /* @__PURE__ */ new Float64Array(12288);
|
|
608
|
+
let totalLum = 0;
|
|
609
|
+
let sampled = 0;
|
|
451
610
|
for (let i = 0; i < px.length; i += 4) {
|
|
452
611
|
const r = px[i], gg = px[i + 1], b = px[i + 2];
|
|
453
612
|
const max = Math.max(r, gg, b), min = Math.min(r, gg, b);
|
|
454
613
|
const v = max / 255;
|
|
455
|
-
|
|
614
|
+
const s = max === 0 ? 0 : (max - min) / max;
|
|
615
|
+
totalLum += v;
|
|
616
|
+
sampled++;
|
|
617
|
+
if (s < .08 || v < .12 || v > .97) continue;
|
|
456
618
|
const key = r >> 4 << 8 | gg >> 4 << 4 | b >> 4;
|
|
457
619
|
counts[key]++;
|
|
458
620
|
sums[key * 3] += r;
|
|
459
621
|
sums[key * 3 + 1] += gg;
|
|
460
622
|
sums[key * 3 + 2] += b;
|
|
461
623
|
}
|
|
462
|
-
|
|
624
|
+
const buckets = [];
|
|
463
625
|
for (let k = 0; k < 4096; k++) {
|
|
464
626
|
if (counts[k] === 0) continue;
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
bestW = w;
|
|
473
|
-
best = k;
|
|
474
|
-
}
|
|
627
|
+
buckets.push({
|
|
628
|
+
count: counts[k],
|
|
629
|
+
r: sums[k * 3],
|
|
630
|
+
g: sums[k * 3 + 1],
|
|
631
|
+
b: sums[k * 3 + 2],
|
|
632
|
+
s: (Math.max(sums[k * 3], sums[k * 3 + 1], sums[k * 3 + 2]) - Math.min(sums[k * 3], sums[k * 3 + 1], sums[k * 3 + 2])) / Math.max(sums[k * 3], sums[k * 3 + 1], sums[k * 3 + 2]) || 0
|
|
633
|
+
});
|
|
475
634
|
}
|
|
476
|
-
if (
|
|
635
|
+
if (buckets.length === 0) {
|
|
477
636
|
resolve(null);
|
|
478
637
|
return;
|
|
479
638
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
639
|
+
buckets.sort((a, b) => b.count * (.5 + b.s) - a.count * (.5 + a.s));
|
|
640
|
+
const primary = bucketsToHsl(buckets[0]);
|
|
641
|
+
const primaryHue = primary[0];
|
|
642
|
+
let secondary = primary;
|
|
643
|
+
for (const b of buckets.slice(1)) {
|
|
644
|
+
const [h] = bucketsToHsl(b);
|
|
645
|
+
if (Math.abs((h - primaryHue + 540) % 360 - 180) > 30) {
|
|
646
|
+
secondary = bucketsToHsl(b);
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
let tertiary = [
|
|
651
|
+
(primaryHue + 180) % 360,
|
|
652
|
+
Math.min(.7, primary[1]),
|
|
653
|
+
Math.min(.7, primary[2])
|
|
654
|
+
];
|
|
655
|
+
for (const b of buckets.slice(1)) {
|
|
656
|
+
const [h] = bucketsToHsl(b);
|
|
657
|
+
const sep = Math.abs((h - primaryHue + 540) % 360 - 180);
|
|
658
|
+
if (sep > 90 && sep < 150) {
|
|
659
|
+
tertiary = bucketsToHsl(b);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const surface = [
|
|
664
|
+
primaryHue,
|
|
665
|
+
Math.min(.06, primary[1] * .3),
|
|
666
|
+
primary[2]
|
|
667
|
+
];
|
|
668
|
+
const luminance = sampled > 0 ? totalLum / sampled : primary[2];
|
|
669
|
+
primary[2] = luminance < .5 ? Math.min(.44, Math.max(.2, primary[2])) : Math.max(.6, Math.min(.82, primary[2]));
|
|
670
|
+
resolve({
|
|
671
|
+
primary: [
|
|
672
|
+
primary[0],
|
|
673
|
+
Math.min(.9, Math.max(.15, primary[1])),
|
|
674
|
+
primary[2]
|
|
675
|
+
],
|
|
676
|
+
secondary: [
|
|
677
|
+
secondary[0],
|
|
678
|
+
Math.min(.85, Math.max(.2, secondary[1])),
|
|
679
|
+
Math.min(.75, Math.max(.35, secondary[2]))
|
|
680
|
+
],
|
|
681
|
+
tertiary: [
|
|
682
|
+
tertiary[0],
|
|
683
|
+
Math.min(.8, Math.max(.2, tertiary[1])),
|
|
684
|
+
Math.min(.7, Math.max(.35, tertiary[2]))
|
|
685
|
+
],
|
|
686
|
+
surface,
|
|
687
|
+
luminance
|
|
688
|
+
});
|
|
689
|
+
} catch {
|
|
690
|
+
resolve(null);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
img.src = dataUrl;
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
/** Backward-compatible single-color extraction: returns the primary HSL. */
|
|
697
|
+
async function extractWallpaperColor(dataUrl, bgState) {
|
|
698
|
+
const palette = await extractWallpaperPalette(dataUrl, bgState);
|
|
699
|
+
return palette ? palette.primary : null;
|
|
700
|
+
}
|
|
701
|
+
const ANALYZE_SIDE = 32;
|
|
702
|
+
/** Analyze a captured frame's average luminance. Resolves true when the frame
|
|
703
|
+
* reads dark (use white fonts), false when light (use black fonts), or null
|
|
704
|
+
* when the frame cannot be decoded. */
|
|
705
|
+
function analyzeFrameDark(dataUrl) {
|
|
706
|
+
return new Promise((resolve) => {
|
|
707
|
+
const img = new Image();
|
|
708
|
+
img.onerror = () => resolve(null);
|
|
709
|
+
img.onload = () => {
|
|
710
|
+
try {
|
|
711
|
+
const c = document.createElement("canvas");
|
|
712
|
+
c.width = ANALYZE_SIDE;
|
|
713
|
+
c.height = ANALYZE_SIDE;
|
|
714
|
+
const g = c.getContext("2d", { willReadFrequently: true });
|
|
715
|
+
g.drawImage(img, 0, 0, ANALYZE_SIDE, ANALYZE_SIDE);
|
|
716
|
+
const px = g.getImageData(0, 0, ANALYZE_SIDE, ANALYZE_SIDE).data;
|
|
717
|
+
let lum = 0;
|
|
718
|
+
const count = px.length / 4;
|
|
719
|
+
for (let i = 0; i < px.length; i += 4) lum += .2126 * px[i] + .7152 * px[i + 1] + .0722 * px[i + 2];
|
|
720
|
+
resolve(lum / count / 255 < .5);
|
|
486
721
|
} catch {
|
|
487
722
|
resolve(null);
|
|
488
723
|
}
|
|
@@ -523,10 +758,584 @@ window.__ModuleLoader__.load({
|
|
|
523
758
|
];
|
|
524
759
|
}
|
|
525
760
|
//#endregion
|
|
761
|
+
//#region src/client/utils/bg-generators.ts
|
|
762
|
+
const RENDER_SCALE = .55;
|
|
763
|
+
const FRAME_MS = 1e3 / 30;
|
|
764
|
+
function newRaf(canvas, draw) {
|
|
765
|
+
canvas.dataset.dshAnyCanvas = "1";
|
|
766
|
+
let running = true;
|
|
767
|
+
let last = 0;
|
|
768
|
+
draw();
|
|
769
|
+
const loop = (ts) => {
|
|
770
|
+
if (!running) return;
|
|
771
|
+
if (ts - last >= FRAME_MS) {
|
|
772
|
+
last = ts;
|
|
773
|
+
draw();
|
|
774
|
+
}
|
|
775
|
+
requestAnimationFrame(loop);
|
|
776
|
+
};
|
|
777
|
+
requestAnimationFrame(loop);
|
|
778
|
+
return { stop: () => {
|
|
779
|
+
running = false;
|
|
780
|
+
} };
|
|
781
|
+
}
|
|
782
|
+
function createCanvas() {
|
|
783
|
+
const c = document.createElement("canvas");
|
|
784
|
+
c.style.cssText = "position:absolute;inset:0;width:100%;height:100%;object-fit:cover;";
|
|
785
|
+
return c;
|
|
786
|
+
}
|
|
787
|
+
function liveSize() {
|
|
788
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 1.5);
|
|
789
|
+
return {
|
|
790
|
+
w: Math.max(320, Math.ceil(window.innerWidth * dpr * RENDER_SCALE)),
|
|
791
|
+
h: Math.max(180, Math.ceil(window.innerHeight * dpr * RENDER_SCALE))
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function fitLiveCanvas(c) {
|
|
795
|
+
if (c.dataset.dshAnyStatic === "1") return;
|
|
796
|
+
const { w, h } = liveSize();
|
|
797
|
+
if (c.width !== w || c.height !== h) {
|
|
798
|
+
c.width = w;
|
|
799
|
+
c.height = h;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
function createRng(seed) {
|
|
803
|
+
let s = seed > 0 ? seed : 1;
|
|
804
|
+
return () => {
|
|
805
|
+
s ^= s << 13;
|
|
806
|
+
s ^= s >>> 17;
|
|
807
|
+
s ^= s << 5;
|
|
808
|
+
return (s >>> 0) / 4294967295;
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function createNoise(seed) {
|
|
812
|
+
const rng = createRng(seed);
|
|
813
|
+
const perm = [];
|
|
814
|
+
for (let i = 0; i < 256; i++) perm[i] = i;
|
|
815
|
+
for (let i = 255; i > 0; i--) {
|
|
816
|
+
const j = Math.floor(rng() * (i + 1));
|
|
817
|
+
const t = perm[i];
|
|
818
|
+
perm[i] = perm[j];
|
|
819
|
+
perm[j] = t;
|
|
820
|
+
}
|
|
821
|
+
for (let i = 0; i < 256; i++) perm[i + 256] = perm[i];
|
|
822
|
+
function fade(t) {
|
|
823
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
824
|
+
}
|
|
825
|
+
function lerp(a, b, t) {
|
|
826
|
+
return a + (b - a) * t;
|
|
827
|
+
}
|
|
828
|
+
function grad(hash, x, y) {
|
|
829
|
+
const h = hash & 15;
|
|
830
|
+
const u = h < 8 ? x : y;
|
|
831
|
+
const v = h < 4 ? y : h === 12 || h === 14 ? x : 0;
|
|
832
|
+
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
|
|
833
|
+
}
|
|
834
|
+
return (x, y) => {
|
|
835
|
+
const X = Math.floor(x) & 255;
|
|
836
|
+
const Y = Math.floor(y) & 255;
|
|
837
|
+
x -= Math.floor(x);
|
|
838
|
+
y -= Math.floor(y);
|
|
839
|
+
const u = fade(x);
|
|
840
|
+
const v = fade(y);
|
|
841
|
+
const A = perm[X] + Y;
|
|
842
|
+
return lerp(lerp(grad(perm[A], x, y), grad(perm[A + 1], x - 1, y), u), lerp(grad(perm[A + 256], x, y - 1), grad(perm[A + 257], x - 1, y - 1), u), v);
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
function createMeshGradient(params, canvas) {
|
|
846
|
+
const c = canvas ?? createCanvas();
|
|
847
|
+
fitLiveCanvas(c);
|
|
848
|
+
const g = c.getContext("2d", { alpha: false });
|
|
849
|
+
const rng = createRng(params.seed);
|
|
850
|
+
const noise = createNoise(params.seed);
|
|
851
|
+
const dark = rng() < .5;
|
|
852
|
+
const baseHue = Math.round(rng() * 360);
|
|
853
|
+
const count = Math.round(7 + 9 * params.scale * params.intensity);
|
|
854
|
+
const blobs = [];
|
|
855
|
+
for (let i = 0; i < count; i++) blobs.push({
|
|
856
|
+
x: rng(),
|
|
857
|
+
y: rng(),
|
|
858
|
+
r: (.22 + rng() * .6) * params.scale,
|
|
859
|
+
hx: rng() * 4 - 2,
|
|
860
|
+
hy: rng() * 4 - 2,
|
|
861
|
+
speed: .05 + rng() * .1,
|
|
862
|
+
hue: (baseHue + (rng() < .5 ? 30 : 180) + rng() * 60) % 360,
|
|
863
|
+
sat: Math.round(45 + rng() * 50 * params.intensity),
|
|
864
|
+
lit: dark ? Math.round(18 + rng() * 35 * params.intensity) : Math.round(60 + rng() * 25 * params.intensity)
|
|
865
|
+
});
|
|
866
|
+
let t = 0;
|
|
867
|
+
const alpha = (.22 + .3 * params.intensity).toFixed(3);
|
|
868
|
+
const innerAlpha = (+alpha * .35).toFixed(3);
|
|
869
|
+
const draw = () => {
|
|
870
|
+
fitLiveCanvas(c);
|
|
871
|
+
const w = c.width, h = c.height;
|
|
872
|
+
g.fillStyle = dark ? "#0a0b0e" : "#f5f7fa";
|
|
873
|
+
g.fillRect(0, 0, w, h);
|
|
874
|
+
for (const b of blobs) {
|
|
875
|
+
const phase = t * b.speed;
|
|
876
|
+
const cx = ((b.x + noise(b.hx + phase * .3, b.hy) * .25 + phase * .03) % 1 + 1) % 1 * w;
|
|
877
|
+
const cy = ((b.y + noise(b.hx, b.hy + phase * .3) * .25 + phase * .012) % 1 + 1) % 1 * h;
|
|
878
|
+
const r = b.r * Math.min(w, h) * (.8 + .4 * Math.sin(phase + b.hx));
|
|
879
|
+
const rad = g.createRadialGradient(cx, cy, 0, cx, cy, r);
|
|
880
|
+
rad.addColorStop(0, `hsla(${b.hue},${b.sat}%,${b.lit}%,${alpha})`);
|
|
881
|
+
rad.addColorStop(.55, `hsla(${b.hue},${Math.round(b.sat * .6)}%,${b.lit}%,${innerAlpha})`);
|
|
882
|
+
rad.addColorStop(1, "hsla(0,0%,0%,0)");
|
|
883
|
+
g.fillStyle = rad;
|
|
884
|
+
g.fillRect(0, 0, w, h);
|
|
885
|
+
}
|
|
886
|
+
t += .028;
|
|
887
|
+
};
|
|
888
|
+
return {
|
|
889
|
+
canvas: c,
|
|
890
|
+
...newRaf(c, draw),
|
|
891
|
+
snapshot: () => c.toDataURL("image/jpeg", .92)
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
function createShaderBg(params, canvas) {
|
|
895
|
+
const c = canvas ?? createCanvas();
|
|
896
|
+
fitLiveCanvas(c);
|
|
897
|
+
const gl = c.getContext("webgl", { alpha: false }) || c.getContext("experimental-webgl", { alpha: false });
|
|
898
|
+
if (!gl) return createMeshGradient({
|
|
899
|
+
type: "mesh",
|
|
900
|
+
seed: params.speed * 1e3,
|
|
901
|
+
scale: params.scale,
|
|
902
|
+
intensity: .6
|
|
903
|
+
}, c);
|
|
904
|
+
const program = createProgram(gl, `
|
|
905
|
+
attribute vec2 a_position;
|
|
906
|
+
void main() { gl_Position = vec4(a_position, 0.0, 1.0); }
|
|
907
|
+
`, shaderFragment(params.preset));
|
|
908
|
+
if (!program) return createMeshGradient({
|
|
909
|
+
type: "mesh",
|
|
910
|
+
seed: params.speed * 1e3,
|
|
911
|
+
scale: params.scale,
|
|
912
|
+
intensity: .6
|
|
913
|
+
}, c);
|
|
914
|
+
const posLoc = gl.getAttribLocation(program, "a_position");
|
|
915
|
+
const buf = gl.createBuffer();
|
|
916
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
|
917
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
918
|
+
-1,
|
|
919
|
+
-1,
|
|
920
|
+
1,
|
|
921
|
+
-1,
|
|
922
|
+
-1,
|
|
923
|
+
1,
|
|
924
|
+
-1,
|
|
925
|
+
1,
|
|
926
|
+
1,
|
|
927
|
+
-1,
|
|
928
|
+
1,
|
|
929
|
+
1
|
|
930
|
+
]), gl.STATIC_DRAW);
|
|
931
|
+
gl.enableVertexAttribArray(posLoc);
|
|
932
|
+
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
933
|
+
gl.useProgram(program);
|
|
934
|
+
const uRes = gl.getUniformLocation(program, "u_resolution");
|
|
935
|
+
const uTime = gl.getUniformLocation(program, "u_time");
|
|
936
|
+
const uScale = gl.getUniformLocation(program, "u_scale");
|
|
937
|
+
const uSeed = gl.getUniformLocation(program, "u_seed");
|
|
938
|
+
const seed01 = (params.seed >>> 0) / 4294967295;
|
|
939
|
+
let t = 0;
|
|
940
|
+
const draw = () => {
|
|
941
|
+
fitLiveCanvas(c);
|
|
942
|
+
gl.viewport(0, 0, c.width, c.height);
|
|
943
|
+
gl.uniform2f(uRes, c.width, c.height);
|
|
944
|
+
gl.uniform1f(uTime, t);
|
|
945
|
+
gl.uniform1f(uScale, params.scale);
|
|
946
|
+
gl.uniform1f(uSeed, seed01);
|
|
947
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
948
|
+
t += .32 * params.speed;
|
|
949
|
+
};
|
|
950
|
+
return {
|
|
951
|
+
canvas: c,
|
|
952
|
+
...newRaf(c, draw),
|
|
953
|
+
snapshot: () => c.toDataURL("image/jpeg", .95)
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
function createProgram(gl, vs, fs) {
|
|
957
|
+
const v = gl.createShader(gl.VERTEX_SHADER);
|
|
958
|
+
const f = gl.createShader(gl.FRAGMENT_SHADER);
|
|
959
|
+
if (!v || !f) return null;
|
|
960
|
+
gl.shaderSource(v, vs);
|
|
961
|
+
gl.compileShader(v);
|
|
962
|
+
gl.shaderSource(f, fs);
|
|
963
|
+
gl.compileShader(f);
|
|
964
|
+
if (!gl.getShaderParameter(v, gl.COMPILE_STATUS) || !gl.getShaderParameter(f, gl.COMPILE_STATUS)) {
|
|
965
|
+
gl.deleteShader(v);
|
|
966
|
+
gl.deleteShader(f);
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
const p = gl.createProgram();
|
|
970
|
+
if (!p) return null;
|
|
971
|
+
gl.attachShader(p, v);
|
|
972
|
+
gl.attachShader(p, f);
|
|
973
|
+
gl.linkProgram(p);
|
|
974
|
+
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
|
|
975
|
+
gl.deleteProgram(p);
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
return p;
|
|
979
|
+
}
|
|
980
|
+
function shaderFragment(preset) {
|
|
981
|
+
const common = `
|
|
982
|
+
precision mediump float;
|
|
983
|
+
uniform vec2 u_resolution;
|
|
984
|
+
uniform float u_time;
|
|
985
|
+
uniform float u_scale;
|
|
986
|
+
uniform float u_seed;
|
|
987
|
+
|
|
988
|
+
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
989
|
+
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
990
|
+
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
|
|
991
|
+
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
|
|
992
|
+
vec3 hueRotate(vec3 rgb, float angle) {
|
|
993
|
+
float c = cos(angle), s = sin(angle);
|
|
994
|
+
mat3 m = mat3(
|
|
995
|
+
0.299 + 0.701*c + 0.168*s, 0.587 - 0.587*c + 0.330*s, 0.114 - 0.114*c - 0.497*s,
|
|
996
|
+
0.299 - 0.299*c - 0.328*s, 0.587 + 0.413*c + 0.035*s, 0.114 - 0.114*c + 0.292*s,
|
|
997
|
+
0.299 - 0.300*c + 1.250*s, 0.587 - 0.588*c - 1.050*s, 0.114 + 0.886*c - 0.203*s
|
|
998
|
+
);
|
|
999
|
+
return rgb * m;
|
|
1000
|
+
}
|
|
1001
|
+
float snoise(vec3 v) {
|
|
1002
|
+
const vec2 C = vec2(1.0/6.0, 1.0/3.0);
|
|
1003
|
+
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
|
1004
|
+
vec3 i = floor(v + dot(v, C.yyy));
|
|
1005
|
+
vec3 x0 = v - i + dot(i, C.xxx);
|
|
1006
|
+
vec3 g = step(x0.yzx, x0.xyz);
|
|
1007
|
+
vec3 l = 1.0 - g;
|
|
1008
|
+
vec3 i1 = min(g.xyz, l.zxy);
|
|
1009
|
+
vec3 i2 = max(g.xyz, l.zxy);
|
|
1010
|
+
vec3 x1 = x0 - i1 + C.xxx;
|
|
1011
|
+
vec3 x2 = x0 - i2 + C.yyy;
|
|
1012
|
+
vec3 x3 = x0 - D.yyy;
|
|
1013
|
+
i = mod289(i);
|
|
1014
|
+
vec4 p = permute(permute(permute(
|
|
1015
|
+
i.z + vec4(0.0, i1.z, i2.z, 1.0))
|
|
1016
|
+
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
|
|
1017
|
+
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
|
|
1018
|
+
float n_ = 0.142857142857;
|
|
1019
|
+
vec3 ns = n_ * D.wyz - D.xzx;
|
|
1020
|
+
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
|
|
1021
|
+
vec4 x_ = floor(j * ns.z);
|
|
1022
|
+
vec4 y_ = floor(j - 7.0 * x_);
|
|
1023
|
+
vec4 x = x_ * ns.x + ns.yyyy;
|
|
1024
|
+
vec4 y = y_ * ns.x + ns.yyyy;
|
|
1025
|
+
vec4 h = 1.0 - abs(x) - abs(y);
|
|
1026
|
+
vec4 b0 = vec4(x.xy, y.xy);
|
|
1027
|
+
vec4 b1 = vec4(x.zw, y.zw);
|
|
1028
|
+
vec4 s0 = floor(b0)*2.0 + 1.0;
|
|
1029
|
+
vec4 s1 = floor(b1)*2.0 + 1.0;
|
|
1030
|
+
vec4 sh = -step(h, vec4(0.0));
|
|
1031
|
+
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
|
|
1032
|
+
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
|
|
1033
|
+
vec3 p0 = vec3(a0.xy, h.x);
|
|
1034
|
+
vec3 p1 = vec3(a0.zw, h.y);
|
|
1035
|
+
vec3 p2 = vec3(a1.xy, h.z);
|
|
1036
|
+
vec3 p3 = vec3(a1.zw, h.w);
|
|
1037
|
+
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
|
|
1038
|
+
p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;
|
|
1039
|
+
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
|
|
1040
|
+
m = m * m;
|
|
1041
|
+
return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
|
|
1042
|
+
}
|
|
1043
|
+
float fbm(vec3 p) {
|
|
1044
|
+
float v = 0.0, a = 0.5;
|
|
1045
|
+
for (int i = 0; i < 5; i++) {
|
|
1046
|
+
v += a * snoise(p);
|
|
1047
|
+
p *= 2.0; a *= 0.5;
|
|
1048
|
+
}
|
|
1049
|
+
return v;
|
|
1050
|
+
}
|
|
1051
|
+
`;
|
|
1052
|
+
if (preset === "aurora") return common + `
|
|
1053
|
+
void main() {
|
|
1054
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
1055
|
+
float t = u_time;
|
|
1056
|
+
float n1 = fbm(vec3(uv * 2.5 * u_scale, t));
|
|
1057
|
+
float n2 = fbm(vec3(uv * 4.0 * u_scale + 7.0, t * 1.4));
|
|
1058
|
+
float n3 = fbm(vec3(uv * 1.2 * u_scale - 3.0, t * 0.7));
|
|
1059
|
+
float bands = smoothstep(0.15, 0.85, 0.5 + 0.5 * sin((uv.y + n3 * 0.08) * 8.0 + n1 * 1.5));
|
|
1060
|
+
vec3 c1 = vec3(0.03, 0.08, 0.14);
|
|
1061
|
+
vec3 c2 = vec3(0.05, 0.28, 0.32);
|
|
1062
|
+
vec3 c3 = vec3(0.18, 0.62, 0.42);
|
|
1063
|
+
vec3 c4 = vec3(0.55, 0.22, 0.52);
|
|
1064
|
+
vec3 c5 = vec3(0.85, 0.35, 0.25);
|
|
1065
|
+
vec3 col = mix(c1, c2, bands);
|
|
1066
|
+
col = mix(col, c3, smoothstep(0.25, 0.75, n1));
|
|
1067
|
+
col = mix(col, c4, smoothstep(0.45, 0.85, n2) * 0.75);
|
|
1068
|
+
col = mix(col, c5, smoothstep(0.7, 0.95, n2 + n1 * 0.3) * 0.45);
|
|
1069
|
+
col = hueRotate(col, u_seed * 6.28318530718);
|
|
1070
|
+
gl_FragColor = vec4(col, 1.0);
|
|
1071
|
+
}
|
|
1072
|
+
`;
|
|
1073
|
+
if (preset === "nebula") return common + `
|
|
1074
|
+
void main() {
|
|
1075
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
1076
|
+
float t = u_time * 0.8;
|
|
1077
|
+
float n = fbm(vec3(uv * 2.2 * u_scale, t));
|
|
1078
|
+
float n2 = fbm(vec3(uv * 5.0 * u_scale - 4.0, t * 0.65));
|
|
1079
|
+
float n3 = fbm(vec3(uv * 0.9 * u_scale + 2.0, t * 0.4));
|
|
1080
|
+
vec3 c1 = vec3(0.02, 0.02, 0.08);
|
|
1081
|
+
vec3 c2 = vec3(0.12, 0.04, 0.22);
|
|
1082
|
+
vec3 c3 = vec3(0.32, 0.10, 0.35);
|
|
1083
|
+
vec3 c4 = vec3(0.10, 0.18, 0.42);
|
|
1084
|
+
vec3 c5 = vec3(0.55, 0.30, 0.55);
|
|
1085
|
+
vec3 col = mix(c1, c2, smoothstep(-0.5, 0.6, n));
|
|
1086
|
+
col = mix(col, c3, smoothstep(0.15, 0.8, n2) * 0.75);
|
|
1087
|
+
col = mix(col, c4, smoothstep(0.35, 0.85, n + n3 * 0.3) * 0.55);
|
|
1088
|
+
col = mix(col, c5, smoothstep(0.6, 0.95, n2) * 0.35);
|
|
1089
|
+
col = hueRotate(col, u_seed * 6.28318530718);
|
|
1090
|
+
gl_FragColor = vec4(col, 1.0);
|
|
1091
|
+
}
|
|
1092
|
+
`;
|
|
1093
|
+
return common + `
|
|
1094
|
+
void main() {
|
|
1095
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
1096
|
+
float t = u_time;
|
|
1097
|
+
float n = fbm(vec3(uv * 3.5 * u_scale, t));
|
|
1098
|
+
float n2 = fbm(vec3(uv * 9.0 * u_scale + 15.0, t * 1.6));
|
|
1099
|
+
float n3 = fbm(vec3(uv * 1.5 * u_scale - 5.0, t * 0.5));
|
|
1100
|
+
vec3 c1 = vec3(0.06, 0.06, 0.08);
|
|
1101
|
+
vec3 c2 = vec3(0.16, 0.18, 0.22);
|
|
1102
|
+
vec3 c3 = vec3(0.30, 0.32, 0.36);
|
|
1103
|
+
vec3 c4 = vec3(0.46, 0.48, 0.52);
|
|
1104
|
+
vec3 col = mix(c1, c2, 0.5 + 0.5 * n + n3 * 0.15);
|
|
1105
|
+
col = mix(col, c3, smoothstep(0.3, 0.8, n2) * 0.45);
|
|
1106
|
+
col = mix(col, c4, smoothstep(0.6, 0.95, n2) * 0.25);
|
|
1107
|
+
col = hueRotate(col, u_seed * 6.28318530718);
|
|
1108
|
+
gl_FragColor = vec4(col, 1.0);
|
|
1109
|
+
}
|
|
1110
|
+
`;
|
|
1111
|
+
}
|
|
1112
|
+
function createPatternBg(params, canvas) {
|
|
1113
|
+
if (params.preset === "waves") return createWaves(params, canvas);
|
|
1114
|
+
if (params.preset === "poly") return createLowPoly(params, canvas);
|
|
1115
|
+
return createDots(params, canvas);
|
|
1116
|
+
}
|
|
1117
|
+
function createDots(params, canvas) {
|
|
1118
|
+
const c = canvas ?? createCanvas();
|
|
1119
|
+
fitLiveCanvas(c);
|
|
1120
|
+
const g = c.getContext("2d", { alpha: false });
|
|
1121
|
+
const rng = createRng(params.seed);
|
|
1122
|
+
const dark = rng() < .5;
|
|
1123
|
+
const baseHue = Math.round(rng() * 360);
|
|
1124
|
+
const spacing = Math.max(26, 150 * params.scale / (.25 + params.density));
|
|
1125
|
+
const dots = [];
|
|
1126
|
+
for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) dots.push({
|
|
1127
|
+
cx: x / 7,
|
|
1128
|
+
cy: y / 7,
|
|
1129
|
+
r: spacing * .16 * params.scale * (.6 + rng() * .7),
|
|
1130
|
+
hue: (baseHue + rng() * 80) % 360,
|
|
1131
|
+
sat: Math.round(35 + rng() * 55),
|
|
1132
|
+
lit: dark ? Math.round(30 + rng() * 35) : Math.round(55 + rng() * 30),
|
|
1133
|
+
phase: rng() * Math.PI * 2,
|
|
1134
|
+
speed: .5 + rng() * 1.2
|
|
1135
|
+
});
|
|
1136
|
+
let t = 0;
|
|
1137
|
+
const draw = () => {
|
|
1138
|
+
fitLiveCanvas(c);
|
|
1139
|
+
const w = c.width, h = c.height;
|
|
1140
|
+
g.fillStyle = dark ? "#0a0b0d" : "#f6f7f9";
|
|
1141
|
+
g.fillRect(0, 0, w, h);
|
|
1142
|
+
for (const d of dots) {
|
|
1143
|
+
const pulse = .75 + .35 * Math.sin(t * d.speed + d.phase);
|
|
1144
|
+
const r = Math.max(1, d.r * pulse);
|
|
1145
|
+
const x = d.cx * w + d.r * .3 * Math.sin(t * d.speed * .5 + d.phase);
|
|
1146
|
+
const y = d.cy * h + d.r * .3 * Math.cos(t * d.speed * .7 + d.phase);
|
|
1147
|
+
g.beginPath();
|
|
1148
|
+
g.arc(x, y, r, 0, Math.PI * 2);
|
|
1149
|
+
g.fillStyle = `hsla(${d.hue},${d.sat}%,${d.lit}%,${(.18 + .25 * pulse).toFixed(2)})`;
|
|
1150
|
+
g.fill();
|
|
1151
|
+
}
|
|
1152
|
+
t += .032;
|
|
1153
|
+
};
|
|
1154
|
+
return {
|
|
1155
|
+
canvas: c,
|
|
1156
|
+
...newRaf(c, draw),
|
|
1157
|
+
snapshot: () => c.toDataURL("image/jpeg", .94)
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function createWaves(params, canvas) {
|
|
1161
|
+
const c = canvas ?? createCanvas();
|
|
1162
|
+
fitLiveCanvas(c);
|
|
1163
|
+
const g = c.getContext("2d", { alpha: false });
|
|
1164
|
+
const rng = createRng(params.seed);
|
|
1165
|
+
const dark = rng() < .5;
|
|
1166
|
+
const hue = Math.round(rng() * 360);
|
|
1167
|
+
const layers = Math.round(5 + params.density * 8);
|
|
1168
|
+
const waves = [];
|
|
1169
|
+
for (let i = 0; i < layers; i++) waves.push({
|
|
1170
|
+
yBase: .25 + i / layers * .55,
|
|
1171
|
+
amp: (25 + rng() * 45) * params.scale,
|
|
1172
|
+
freq: (.006 + rng() * .01) / params.scale,
|
|
1173
|
+
phase: rng() * Math.PI * 2,
|
|
1174
|
+
speed: (.3 + rng() * .7) * (rng() < .5 ? 1 : -1),
|
|
1175
|
+
hue: (hue + i * 12) % 360,
|
|
1176
|
+
sat: Math.round(40 + rng() * 45),
|
|
1177
|
+
lit: dark ? Math.round(14 + i / layers * 32) : Math.round(72 - i / layers * 28),
|
|
1178
|
+
alpha: .22 + rng() * .32
|
|
1179
|
+
});
|
|
1180
|
+
let t = 0;
|
|
1181
|
+
const draw = () => {
|
|
1182
|
+
fitLiveCanvas(c);
|
|
1183
|
+
const w = c.width, h = c.height;
|
|
1184
|
+
g.fillStyle = dark ? "#07080a" : "#f8f9fb";
|
|
1185
|
+
g.fillRect(0, 0, w, h);
|
|
1186
|
+
for (const wave of waves) {
|
|
1187
|
+
g.beginPath();
|
|
1188
|
+
g.moveTo(0, h);
|
|
1189
|
+
for (let x = 0; x <= w; x += Math.max(6, Math.floor(w / 180))) {
|
|
1190
|
+
const y = h * wave.yBase + Math.sin(x * wave.freq + wave.phase + t * wave.speed) * wave.amp + Math.sin(x * wave.freq * 2.1 + wave.phase * 1.3 - t * wave.speed * 1.5) * wave.amp * .5;
|
|
1191
|
+
g.lineTo(x, y);
|
|
1192
|
+
}
|
|
1193
|
+
g.lineTo(w, h);
|
|
1194
|
+
g.closePath();
|
|
1195
|
+
g.fillStyle = `hsla(${wave.hue},${wave.sat}%,${wave.lit}%,${wave.alpha.toFixed(2)})`;
|
|
1196
|
+
g.fill();
|
|
1197
|
+
}
|
|
1198
|
+
t += .032;
|
|
1199
|
+
};
|
|
1200
|
+
return {
|
|
1201
|
+
canvas: c,
|
|
1202
|
+
...newRaf(c, draw),
|
|
1203
|
+
snapshot: () => c.toDataURL("image/jpeg", .94)
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
function createLowPoly(params, canvas) {
|
|
1207
|
+
const c = canvas ?? createCanvas();
|
|
1208
|
+
fitLiveCanvas(c);
|
|
1209
|
+
const g = c.getContext("2d", { alpha: false });
|
|
1210
|
+
const rng = createRng(params.seed);
|
|
1211
|
+
const dark = rng() < .5;
|
|
1212
|
+
const hue = Math.round(rng() * 360);
|
|
1213
|
+
const cols = Math.round(10 + params.density * 20);
|
|
1214
|
+
const rows = Math.max(6, Math.round(cols * .65));
|
|
1215
|
+
const points = [];
|
|
1216
|
+
for (let y = 0; y <= rows; y++) {
|
|
1217
|
+
const row = [];
|
|
1218
|
+
for (let x = 0; x <= cols; x++) row.push({
|
|
1219
|
+
x: x / cols,
|
|
1220
|
+
y: y / rows,
|
|
1221
|
+
dx: (rng() - .5) * .02,
|
|
1222
|
+
dy: (rng() - .5) * .02,
|
|
1223
|
+
speed: .3 + rng() * .5
|
|
1224
|
+
});
|
|
1225
|
+
points.push(row);
|
|
1226
|
+
}
|
|
1227
|
+
const cells = [];
|
|
1228
|
+
for (let y = 0; y < rows; y++) for (let x = 0; x < cols; x++) cells.push({
|
|
1229
|
+
y,
|
|
1230
|
+
x,
|
|
1231
|
+
hue: (hue + rng() * 60) % 360,
|
|
1232
|
+
sat: Math.round(35 + rng() * 40),
|
|
1233
|
+
lit: dark ? Math.round(12 + rng() * 35) : Math.round(65 - rng() * 10),
|
|
1234
|
+
phase: rng() * Math.PI * 2
|
|
1235
|
+
});
|
|
1236
|
+
let t = 0;
|
|
1237
|
+
const px = points.map((row) => row.map(() => ({
|
|
1238
|
+
x: 0,
|
|
1239
|
+
y: 0
|
|
1240
|
+
})));
|
|
1241
|
+
const draw = () => {
|
|
1242
|
+
fitLiveCanvas(c);
|
|
1243
|
+
const w = c.width, h = c.height;
|
|
1244
|
+
g.fillStyle = dark ? "#08090c" : "#f5f6f8";
|
|
1245
|
+
g.fillRect(0, 0, w, h);
|
|
1246
|
+
for (let y = 0; y <= rows; y++) {
|
|
1247
|
+
const row = points[y];
|
|
1248
|
+
const out = px[y];
|
|
1249
|
+
for (let x = 0; x <= cols; x++) {
|
|
1250
|
+
const p = row[x];
|
|
1251
|
+
out[x].x = (p.x + Math.sin(t * p.speed + p.dx * 100) * p.dx) * w;
|
|
1252
|
+
out[x].y = (p.y + Math.cos(t * p.speed + p.dy * 100) * p.dy) * h;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
for (const cell of cells) {
|
|
1256
|
+
const p1 = px[cell.y][cell.x], p2 = px[cell.y][cell.x + 1], p3 = px[cell.y + 1][cell.x], p4 = px[cell.y + 1][cell.x + 1];
|
|
1257
|
+
const cx = (p1.x + p2.x + p3.x) / 3 / w;
|
|
1258
|
+
const lit = Math.max(0, Math.min(100, cell.lit + Math.sin(t * .6 + cell.phase) * 6));
|
|
1259
|
+
g.beginPath();
|
|
1260
|
+
g.moveTo(p1.x, p1.y);
|
|
1261
|
+
g.lineTo(p2.x, p2.y);
|
|
1262
|
+
g.lineTo(p3.x, p3.y);
|
|
1263
|
+
g.closePath();
|
|
1264
|
+
g.fillStyle = `hsla(${cell.hue + cx * 40},${cell.sat}%,${lit}%,0.92)`;
|
|
1265
|
+
g.fill();
|
|
1266
|
+
g.beginPath();
|
|
1267
|
+
g.moveTo(p2.x, p2.y);
|
|
1268
|
+
g.lineTo(p4.x, p4.y);
|
|
1269
|
+
g.lineTo(p3.x, p3.y);
|
|
1270
|
+
g.closePath();
|
|
1271
|
+
g.fillStyle = `hsla(${(cell.hue + cx * 40 + 12) % 360},${cell.sat}%,${Math.max(0, lit - 4)}%,0.92)`;
|
|
1272
|
+
g.fill();
|
|
1273
|
+
}
|
|
1274
|
+
t += .03;
|
|
1275
|
+
};
|
|
1276
|
+
return {
|
|
1277
|
+
canvas: c,
|
|
1278
|
+
...newRaf(c, draw),
|
|
1279
|
+
snapshot: () => c.toDataURL("image/jpeg", .94)
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
function createDynamicBackground(params, canvas) {
|
|
1283
|
+
if (params.type === "mesh") return createMeshGradient(params, canvas);
|
|
1284
|
+
if (params.type === "shader") return createShaderBg(params, canvas);
|
|
1285
|
+
return createPatternBg(params, canvas);
|
|
1286
|
+
}
|
|
1287
|
+
function randomSeed$1() {
|
|
1288
|
+
return Math.floor(Math.random() * 2147483647);
|
|
1289
|
+
}
|
|
1290
|
+
/** Build default params for a newly selected background type. */
|
|
1291
|
+
function defaultParamsFor(type) {
|
|
1292
|
+
if (type === "mesh") return {
|
|
1293
|
+
type: "mesh",
|
|
1294
|
+
seed: randomSeed$1(),
|
|
1295
|
+
scale: 1.1,
|
|
1296
|
+
intensity: .65
|
|
1297
|
+
};
|
|
1298
|
+
if (type === "shader") return {
|
|
1299
|
+
type: "shader",
|
|
1300
|
+
preset: "aurora",
|
|
1301
|
+
speed: .35,
|
|
1302
|
+
scale: 1,
|
|
1303
|
+
seed: randomSeed$1()
|
|
1304
|
+
};
|
|
1305
|
+
return {
|
|
1306
|
+
type: "pattern",
|
|
1307
|
+
preset: "dots",
|
|
1308
|
+
density: .5,
|
|
1309
|
+
scale: 1,
|
|
1310
|
+
seed: randomSeed$1()
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
//#endregion
|
|
526
1314
|
//#region src/client/wallpaper.ts
|
|
527
1315
|
let wpEl = null;
|
|
528
1316
|
let appliedTokenNames = [];
|
|
1317
|
+
let wpController = null;
|
|
1318
|
+
let snapshotListener = null;
|
|
1319
|
+
let tokenStyleEl = null;
|
|
1320
|
+
function clearDynamicBg() {
|
|
1321
|
+
wpController?.stop();
|
|
1322
|
+
wpController?.canvas.remove();
|
|
1323
|
+
wpController = null;
|
|
1324
|
+
}
|
|
1325
|
+
/** Register a callback fired once a generated snapshot is ready (so the caller
|
|
1326
|
+
* can re-sync the settings preview / store). */
|
|
1327
|
+
function onGeneratedSnapshot(cb) {
|
|
1328
|
+
snapshotListener = cb;
|
|
1329
|
+
}
|
|
1330
|
+
function ensureTokenStyle() {
|
|
1331
|
+
if (tokenStyleEl?.isConnected) return tokenStyleEl;
|
|
1332
|
+
tokenStyleEl = document.createElement("style");
|
|
1333
|
+
tokenStyleEl.dataset.plugin = "dsh-any-background-tokens";
|
|
1334
|
+
document.head.appendChild(tokenStyleEl);
|
|
1335
|
+
return tokenStyleEl;
|
|
1336
|
+
}
|
|
529
1337
|
function clearCustomTokens() {
|
|
1338
|
+
if (tokenStyleEl) tokenStyleEl.textContent = "";
|
|
530
1339
|
for (const name of appliedTokenNames) document.body.style.removeProperty(name);
|
|
531
1340
|
appliedTokenNames = [];
|
|
532
1341
|
}
|
|
@@ -539,20 +1348,39 @@ window.__ModuleLoader__.load({
|
|
|
539
1348
|
* brand) is written verbatim. No reads: nothing can observe a stale or reset
|
|
540
1349
|
* theme value and leave the homepage on the system color.
|
|
541
1350
|
*/
|
|
1351
|
+
/** Label tokens flipped by the generated-background brightness verdict. */
|
|
1352
|
+
const LABEL_TOKENS = [
|
|
1353
|
+
"--dsw-alias-label-primary",
|
|
1354
|
+
"--dsw-alias-label-secondary",
|
|
1355
|
+
"--dsw-alias-label-tertiary",
|
|
1356
|
+
"--dsw-alias-label-caption",
|
|
1357
|
+
"--dsw-alias-label-dimmed",
|
|
1358
|
+
"--dsw-alias-label-quaternary"
|
|
1359
|
+
];
|
|
542
1360
|
function applyCustomTokens(ops) {
|
|
543
1361
|
const [h, s, l] = rColor();
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
document.body.
|
|
554
|
-
|
|
555
|
-
|
|
1362
|
+
let { tokens } = genTokens(h, s, l);
|
|
1363
|
+
try {
|
|
1364
|
+
const dark = rBgDark();
|
|
1365
|
+
if (dark !== null) {
|
|
1366
|
+
tokens = { ...tokens };
|
|
1367
|
+
const font = dark ? "#fff" : "#000";
|
|
1368
|
+
for (const name of LABEL_TOKENS) tokens[name] = font;
|
|
1369
|
+
}
|
|
1370
|
+
if (dark ?? l < .55) document.body.setAttribute("data-ds-dark-theme", "dsh-any-background");
|
|
1371
|
+
else document.body.removeAttribute("data-ds-dark-theme");
|
|
1372
|
+
const decls = [];
|
|
1373
|
+
for (const [name, value] of Object.entries(tokens)) {
|
|
1374
|
+
let v = value;
|
|
1375
|
+
if (name === "--dsw-alias-bg-base") v = toRgba(value, ops.bg);
|
|
1376
|
+
else if (name === "--dsw-specific-sidebar-fill") v = toRgba(value, ops.sidebar);
|
|
1377
|
+
else if (name === "--dsw-alias-bg-layer-1" || name === "--dsw-alias-bg-layer-2" || name === "--dsw-alias-bg-layer-3") v = toRgba(value, ops.card);
|
|
1378
|
+
decls.push(`${name}:${v}!important`);
|
|
1379
|
+
}
|
|
1380
|
+
ensureTokenStyle().textContent = `body{${decls.join(";")}}`;
|
|
1381
|
+
for (const name of appliedTokenNames) document.body.style.removeProperty(name);
|
|
1382
|
+
appliedTokenNames = Object.keys(tokens);
|
|
1383
|
+
} catch (e) {}
|
|
556
1384
|
}
|
|
557
1385
|
const SETTINGS_STYLE_RULE = `[role="dialog"][aria-modal="true"][aria-labelledby]{background:var(--dsh-any-bg-settings-surface,var(--dsw-alias-bg-layer-2));backdrop-filter:var(--dsh-any-blur-settings,none)}`;
|
|
558
1386
|
function applySettingsOverrides(op) {
|
|
@@ -560,14 +1388,109 @@ window.__ModuleLoader__.load({
|
|
|
560
1388
|
document.documentElement.style.removeProperty("--dsh-any-bg-settings-surface");
|
|
561
1389
|
return;
|
|
562
1390
|
}
|
|
1391
|
+
if (!rHasColor()) return;
|
|
563
1392
|
const [h, s, l] = rColor();
|
|
564
1393
|
const layer2 = genTokens(h, s, l).tokens["--dsw-alias-bg-layer-2"];
|
|
565
1394
|
if (layer2 !== void 0) document.documentElement.style.setProperty("--dsh-any-bg-settings-surface", toRgba(layer2, op));
|
|
566
1395
|
}
|
|
1396
|
+
/** Apply only the wallpaper layer (position, scale, opacity, blur) without
|
|
1397
|
+
* touching the theme color palette. Used when generated background parameters
|
|
1398
|
+
* change so the visual keeps updating without shifting the picked theme color. */
|
|
1399
|
+
function applyWallpaper() {
|
|
1400
|
+
applyWp();
|
|
1401
|
+
}
|
|
1402
|
+
/** Apply the theme color. If the user has an explicit saved pick we use it
|
|
1403
|
+
* directly; otherwise we fall back to extracting a dominant color from the
|
|
1404
|
+
* current wallpaper (one HSL sample, no palette), so new uploads still get a
|
|
1405
|
+
* matching theme automatically. */
|
|
1406
|
+
function applyThemeColor() {
|
|
1407
|
+
if (rHasColor()) {
|
|
1408
|
+
applyWp();
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
const url = rWp();
|
|
1412
|
+
if (url) extractWallpaperColor(url, rBgState()).then((hsl) => {
|
|
1413
|
+
if (hsl) cfg.color = hsl;
|
|
1414
|
+
applyWp();
|
|
1415
|
+
});
|
|
1416
|
+
else applyWp();
|
|
1417
|
+
}
|
|
1418
|
+
/** Switch the background source type. For generated types a new live canvas is
|
|
1419
|
+
* attached to the wallpaper layer and a snapshot is kept for the store/preview. */
|
|
1420
|
+
function setBackgroundType(type) {
|
|
1421
|
+
cfg.backgroundType = type;
|
|
1422
|
+
if (type === "image") {
|
|
1423
|
+
clearDynamicBg();
|
|
1424
|
+
setBgDark(null);
|
|
1425
|
+
setWpUrl(rWpImage());
|
|
1426
|
+
applyThemeColor();
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (!cfg.generatedBg || cfg.generatedBg.type !== type) cfg.generatedBg = defaultParamsFor(type);
|
|
1430
|
+
applyGeneratedBg(cfg.generatedBg);
|
|
1431
|
+
}
|
|
1432
|
+
function randomSeed() {
|
|
1433
|
+
return Math.floor(Math.random() * 2147483647);
|
|
1434
|
+
}
|
|
1435
|
+
/** Regenerate the current generated background with a new visual seed while
|
|
1436
|
+
* preserving the user's scale/intensity/speed/density/preset choices. */
|
|
1437
|
+
function regenerateGeneratedBg() {
|
|
1438
|
+
const params = cfg.generatedBg;
|
|
1439
|
+
if (!params || cfg.backgroundType === "image") return;
|
|
1440
|
+
const next = params.type === "mesh" ? {
|
|
1441
|
+
...params,
|
|
1442
|
+
seed: randomSeed()
|
|
1443
|
+
} : {
|
|
1444
|
+
...params,
|
|
1445
|
+
seed: randomSeed()
|
|
1446
|
+
};
|
|
1447
|
+
cfg.generatedBg = next;
|
|
1448
|
+
applyGeneratedBg(next);
|
|
1449
|
+
}
|
|
1450
|
+
/** Update a generated background's parameters and re-render. */
|
|
1451
|
+
function updateGeneratedBg(params) {
|
|
1452
|
+
cfg.backgroundType = params.type;
|
|
1453
|
+
cfg.generatedBg = params;
|
|
1454
|
+
applyGeneratedBg(params);
|
|
1455
|
+
}
|
|
1456
|
+
function applyGeneratedBg(params) {
|
|
1457
|
+
clearDynamicBg();
|
|
1458
|
+
ensureWpContainer();
|
|
1459
|
+
wpController = createDynamicBackground(params);
|
|
1460
|
+
if (wpEl) {
|
|
1461
|
+
wpEl.style.backgroundImage = "none";
|
|
1462
|
+
wpEl.appendChild(wpController.canvas);
|
|
1463
|
+
}
|
|
1464
|
+
requestAnimationFrame(() => {
|
|
1465
|
+
if (!wpController) return;
|
|
1466
|
+
const controller = wpController;
|
|
1467
|
+
const frame = controller.snapshot();
|
|
1468
|
+
setWpUrl(frame);
|
|
1469
|
+
applyWallpaper();
|
|
1470
|
+
snapshotListener?.();
|
|
1471
|
+
setBgDark(null);
|
|
1472
|
+
analyzeFrameDark(frame).then((dark) => {
|
|
1473
|
+
if (dark === null || wpController !== controller) return;
|
|
1474
|
+
setBgDark(dark);
|
|
1475
|
+
applyCustomTokens(rOps());
|
|
1476
|
+
});
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
567
1479
|
let frameEl = null;
|
|
568
1480
|
let sidebarEl = null;
|
|
569
1481
|
let centerEl = null;
|
|
570
1482
|
let detailsEl = null;
|
|
1483
|
+
const PART_BLUR_CLASS = "dab-part-blur";
|
|
1484
|
+
const PART_UNDERLAY_CLASS = "dab-part-underlay";
|
|
1485
|
+
const PART_BLUR_RULE = `${PART_BLUR_CLASS}{isolation:isolate}.${PART_UNDERLAY_CLASS}{position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;backdrop-filter:var(--dsh-any-part-blur,none);-webkit-backdrop-filter:var(--dsh-any-part-blur,none)}`;
|
|
1486
|
+
let partBlurStyleEl = null;
|
|
1487
|
+
function ensurePartBlurStyle() {
|
|
1488
|
+
if (partBlurStyleEl?.isConnected) return;
|
|
1489
|
+
partBlurStyleEl = document.createElement("style");
|
|
1490
|
+
partBlurStyleEl.dataset.plugin = "dsh-any-background-parts";
|
|
1491
|
+
partBlurStyleEl.textContent = PART_BLUR_RULE;
|
|
1492
|
+
document.head.appendChild(partBlurStyleEl);
|
|
1493
|
+
}
|
|
571
1494
|
function discoverParts() {
|
|
572
1495
|
const overlay = document.querySelector("[data-shell-overlay]");
|
|
573
1496
|
if (overlay === null) return;
|
|
@@ -581,8 +1504,29 @@ window.__ModuleLoader__.load({
|
|
|
581
1504
|
}
|
|
582
1505
|
function setBlur(el, px) {
|
|
583
1506
|
if (el === null) return;
|
|
584
|
-
|
|
585
|
-
|
|
1507
|
+
const underlay = el.querySelector(`:scope > .${PART_UNDERLAY_CLASS}`);
|
|
1508
|
+
if (px > 0) {
|
|
1509
|
+
ensurePartBlurStyle();
|
|
1510
|
+
if (!el.classList.contains(PART_BLUR_CLASS) && getComputedStyle(el).position === "static") {
|
|
1511
|
+
el.style.position = "relative";
|
|
1512
|
+
el.setAttribute("data-dab-pos-patched", "1");
|
|
1513
|
+
}
|
|
1514
|
+
el.classList.add(PART_BLUR_CLASS);
|
|
1515
|
+
if (underlay === null) {
|
|
1516
|
+
const node = document.createElement("div");
|
|
1517
|
+
node.className = PART_UNDERLAY_CLASS;
|
|
1518
|
+
el.prepend(node);
|
|
1519
|
+
}
|
|
1520
|
+
el.style.setProperty("--dsh-any-part-blur", `blur(${px}px)`);
|
|
1521
|
+
} else {
|
|
1522
|
+
el.classList.remove(PART_BLUR_CLASS);
|
|
1523
|
+
el.style.removeProperty("--dsh-any-part-blur");
|
|
1524
|
+
underlay?.remove();
|
|
1525
|
+
if (el.getAttribute("data-dab-pos-patched") === "1") {
|
|
1526
|
+
el.style.removeProperty("position");
|
|
1527
|
+
el.removeAttribute("data-dab-pos-patched");
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
586
1530
|
}
|
|
587
1531
|
function applySettingsBlur(px) {
|
|
588
1532
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-blur-settings", `blur(${px}px)`);
|
|
@@ -630,41 +1574,68 @@ window.__ModuleLoader__.load({
|
|
|
630
1574
|
partsObserver?.disconnect();
|
|
631
1575
|
partsObserver = null;
|
|
632
1576
|
}
|
|
1577
|
+
function ensureWpContainer() {
|
|
1578
|
+
if (!wpEl || !document.body.contains(wpEl)) {
|
|
1579
|
+
wpEl = document.createElement("div");
|
|
1580
|
+
wpEl.style.cssText = "position:fixed;inset:0;z-index:-1;pointer-events:none;overflow:hidden;";
|
|
1581
|
+
document.body.prepend(wpEl);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
function applyImageWp(url) {
|
|
1585
|
+
clearDynamicBg();
|
|
1586
|
+
ensureWpContainer();
|
|
1587
|
+
const bg = rBgState();
|
|
1588
|
+
const next = `url("${url}")`;
|
|
1589
|
+
if (wpEl.style.backgroundImage !== next) {
|
|
1590
|
+
wpEl.style.backgroundImage = next;
|
|
1591
|
+
wpEl.style.backgroundRepeat = "no-repeat";
|
|
1592
|
+
}
|
|
1593
|
+
if (bg.iw > 0) {
|
|
1594
|
+
const fit = Math.min(window.innerWidth / bg.iw, window.innerHeight / bg.ih);
|
|
1595
|
+
const w = bg.iw * fit * bg.zoom;
|
|
1596
|
+
const h = bg.ih * fit * bg.zoom;
|
|
1597
|
+
wpEl.style.backgroundSize = `${w}px ${h}px`;
|
|
1598
|
+
wpEl.style.backgroundPosition = `${bg.x * window.innerWidth - w / 2}px ${bg.y * window.innerHeight - h / 2}px`;
|
|
1599
|
+
} else {
|
|
1600
|
+
wpEl.style.backgroundSize = "contain";
|
|
1601
|
+
wpEl.style.backgroundPosition = "center";
|
|
1602
|
+
}
|
|
1603
|
+
applyWpEffects();
|
|
1604
|
+
}
|
|
1605
|
+
function applyWpEffects() {
|
|
1606
|
+
if (!wpEl) return;
|
|
1607
|
+
const blur = rBl();
|
|
1608
|
+
wpEl.style.filter = blur > 0 ? `blur(${blur}px)` : "none";
|
|
1609
|
+
wpEl.style.opacity = String(rWop());
|
|
1610
|
+
}
|
|
633
1611
|
function applyWp() {
|
|
634
1612
|
const url = rWp();
|
|
635
|
-
if (
|
|
1613
|
+
if (cfg.backgroundType !== "image" && cfg.generatedBg) {
|
|
1614
|
+
if (!wpController) {
|
|
1615
|
+
applyGeneratedBg(cfg.generatedBg);
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
ensureWpContainer();
|
|
1619
|
+
if (wpController.canvas.parentElement !== wpEl) wpEl.appendChild(wpController.canvas);
|
|
1620
|
+
applyWpEffects();
|
|
1621
|
+
} else if (url) applyImageWp(url);
|
|
1622
|
+
else {
|
|
1623
|
+
clearDynamicBg();
|
|
636
1624
|
wpEl?.remove();
|
|
637
1625
|
wpEl = null;
|
|
638
|
-
} else {
|
|
639
|
-
if (!wpEl || !document.body.contains(wpEl)) {
|
|
640
|
-
wpEl = document.createElement("div");
|
|
641
|
-
wpEl.style.cssText = "position:fixed;inset:0;z-index:-1;pointer-events:none;background-repeat:no-repeat;";
|
|
642
|
-
document.body.prepend(wpEl);
|
|
643
|
-
}
|
|
644
|
-
const bg = rBgState();
|
|
645
|
-
wpEl.style.backgroundImage = `url("${url}")`;
|
|
646
|
-
if (bg.iw > 0) {
|
|
647
|
-
const fit = Math.min(window.innerWidth / bg.iw, window.innerHeight / bg.ih);
|
|
648
|
-
const w = bg.iw * fit * bg.zoom;
|
|
649
|
-
const h = bg.ih * fit * bg.zoom;
|
|
650
|
-
wpEl.style.backgroundSize = `${w}px ${h}px`;
|
|
651
|
-
wpEl.style.backgroundPosition = `${bg.x * window.innerWidth - w / 2}px ${bg.y * window.innerHeight - h / 2}px`;
|
|
652
|
-
} else {
|
|
653
|
-
wpEl.style.backgroundSize = "contain";
|
|
654
|
-
wpEl.style.backgroundPosition = "center";
|
|
655
|
-
}
|
|
656
|
-
const blur = rBl();
|
|
657
|
-
wpEl.style.filter = blur > 0 ? `blur(${blur}px)` : "none";
|
|
658
|
-
wpEl.style.opacity = String(rWop());
|
|
659
1626
|
}
|
|
660
|
-
applyCustomTokens(rOps());
|
|
661
|
-
applySettingsOverrides(rSop());
|
|
1627
|
+
if (rHasColor() || rBgDark() !== null) applyCustomTokens(rOps());
|
|
1628
|
+
if (rHasColor()) applySettingsOverrides(rSop());
|
|
662
1629
|
applyPartBlurs(rBlurs());
|
|
663
1630
|
}
|
|
664
1631
|
function teardownWp() {
|
|
1632
|
+
clearDynamicBg();
|
|
1633
|
+
setBgDark(null);
|
|
665
1634
|
wpEl?.remove();
|
|
666
1635
|
wpEl = null;
|
|
667
1636
|
clearCustomTokens();
|
|
1637
|
+
tokenStyleEl?.remove();
|
|
1638
|
+
tokenStyleEl = null;
|
|
668
1639
|
document.documentElement.style.removeProperty("--dsh-any-bg-settings-surface");
|
|
669
1640
|
document.documentElement.style.removeProperty("--dsh-any-blur-settings");
|
|
670
1641
|
setBlur(frameEl, 0);
|
|
@@ -681,378 +1652,554 @@ window.__ModuleLoader__.load({
|
|
|
681
1652
|
function setWpBlur(v) {
|
|
682
1653
|
if (wpEl) wpEl.style.filter = v > 0 ? `blur(${v}px)` : "none";
|
|
683
1654
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
position: "fixed",
|
|
901
|
-
inset: 0,
|
|
902
|
-
zIndex: 9999,
|
|
903
|
-
background: "rgba(0,0,0,0.7)",
|
|
904
|
-
display: "flex",
|
|
905
|
-
flexDirection: "column",
|
|
906
|
-
alignItems: "center",
|
|
907
|
-
justifyContent: "center",
|
|
908
|
-
gap: "12px"
|
|
909
|
-
},
|
|
910
|
-
modalTitle: {
|
|
911
|
-
color: "#fff",
|
|
912
|
-
fontSize: "16px",
|
|
913
|
-
fontWeight: 500
|
|
914
|
-
},
|
|
915
|
-
modalHint: {
|
|
916
|
-
color: "rgba(255,255,255,0.6)",
|
|
917
|
-
fontSize: "12px"
|
|
918
|
-
},
|
|
919
|
-
previewRect: {
|
|
920
|
-
position: "relative",
|
|
921
|
-
overflow: "hidden",
|
|
922
|
-
border: "2px solid rgba(255,255,255,0.3)",
|
|
923
|
-
borderRadius: "8px",
|
|
924
|
-
background: "#000",
|
|
925
|
-
cursor: "grab"
|
|
926
|
-
},
|
|
927
|
-
previewImg: {
|
|
928
|
-
position: "absolute",
|
|
929
|
-
transformOrigin: "0 0",
|
|
930
|
-
pointerEvents: "none"
|
|
931
|
-
},
|
|
932
|
-
modalBtns: {
|
|
933
|
-
display: "flex",
|
|
934
|
-
gap: "10px"
|
|
935
|
-
},
|
|
936
|
-
pickerBox: {
|
|
937
|
-
maxWidth: "min(90vw, 720px)",
|
|
938
|
-
maxHeight: "60vh",
|
|
939
|
-
overflow: "hidden",
|
|
940
|
-
borderRadius: "8px",
|
|
941
|
-
border: "2px solid rgba(255,255,255,0.3)",
|
|
942
|
-
background: "#000",
|
|
943
|
-
cursor: "crosshair"
|
|
944
|
-
},
|
|
945
|
-
pickerCanvas: {
|
|
946
|
-
display: "block",
|
|
947
|
-
maxWidth: "100%",
|
|
948
|
-
maxHeight: "60vh",
|
|
949
|
-
objectFit: "contain"
|
|
950
|
-
},
|
|
951
|
-
pickerBar: {
|
|
952
|
-
display: "flex",
|
|
953
|
-
alignItems: "center",
|
|
954
|
-
gap: "10px",
|
|
955
|
-
minWidth: "260px"
|
|
956
|
-
},
|
|
957
|
-
pickerSwatch: {
|
|
958
|
-
width: "28px",
|
|
959
|
-
height: "28px",
|
|
960
|
-
borderRadius: "6px",
|
|
961
|
-
border: "1px solid rgba(255,255,255,0.4)",
|
|
962
|
-
flexShrink: 0
|
|
963
|
-
},
|
|
964
|
-
pickerValue: {
|
|
965
|
-
color: "#fff",
|
|
966
|
-
fontSize: "13px",
|
|
967
|
-
fontFamily: "monospace"
|
|
968
|
-
},
|
|
969
|
-
magnifier: {
|
|
970
|
-
position: "fixed",
|
|
971
|
-
width: "96px",
|
|
972
|
-
height: "96px",
|
|
973
|
-
borderRadius: "50%",
|
|
974
|
-
border: "2px solid rgba(255,255,255,0.7)",
|
|
975
|
-
boxShadow: "0 4px 16px rgba(0,0,0,0.5)",
|
|
976
|
-
zIndex: 1e4,
|
|
977
|
-
background: "#000",
|
|
978
|
-
pointerEvents: "none",
|
|
979
|
-
transition: "opacity 0.08s"
|
|
1655
|
+
const UI_CSS = `
|
|
1656
|
+
/* The section is rendered INLINE inside the host settings dialog's content
|
|
1657
|
+
* column: the host provides the modal chrome (backdrop, centering, closing).
|
|
1658
|
+
* These classes style only the embedded shell; transient fixed layers (toast,
|
|
1659
|
+
* color picker, background editor) escape through Portals on <html>. */
|
|
1660
|
+
.dab-root{position:relative;color:var(--dsw-alias-label-primary);animation:dab-fade-in .35s ease both;container-type:inline-size;display:flex;flex-direction:column;align-items:center;width:100%;min-width:0;--dab-mono:ui-monospace,"Cascadia Mono","SF Mono",Consolas,"Courier New",monospace}
|
|
1661
|
+
.dab-root *,.dab-root *::before,.dab-root *::after{box-sizing:border-box}
|
|
1662
|
+
.dab-root button{font-family:inherit}
|
|
1663
|
+
|
|
1664
|
+
/* ── shell: nav rail + page body ─────────────────────────────────────────── */
|
|
1665
|
+
.dab-shell{display:grid;grid-template-columns:158px minmax(0,1fr);gap:26px;align-items:start;padding-bottom:8px;width:100%;max-width:980px;margin:0 auto}
|
|
1666
|
+
.dab-nav{position:sticky;top:0;display:flex;flex-direction:column;gap:18px}
|
|
1667
|
+
.dab-brand{display:flex;align-items:center;gap:10px;padding:2px 6px}
|
|
1668
|
+
.dab-brand-tile{width:30px;height:30px;flex:none;border-radius:9px;display:grid;place-items:center;color:var(--dsw-alias-brand-text);background:var(--dsw-alias-brand-primary);box-shadow:0 4px 14px -4px var(--dsw-alias-brand-primary)}
|
|
1669
|
+
.dab-brand-name{font-size:13px;font-weight:650;letter-spacing:.01em;line-height:1.25}
|
|
1670
|
+
.dab-brand-tag{font-size:9px;letter-spacing:.16em;font-weight:600;color:var(--dsw-alias-label-quaternary,var(--dsw-alias-label-tertiary));text-transform:uppercase}
|
|
1671
|
+
.dab-nav-list{position:relative;display:flex;flex-direction:column;gap:4px}
|
|
1672
|
+
.dab-nav-ind{position:absolute;left:0;right:0;top:0;height:38px;border-radius:11px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 13%,transparent);border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 25%,transparent);transition:transform .38s cubic-bezier(.22,1,.36,1)}
|
|
1673
|
+
.dab-nav-item{position:relative;z-index:1;display:flex;align-items:center;gap:10px;height:38px;padding:0 12px;border:0;background:none;border-radius:11px;color:var(--dsw-alias-label-tertiary);font-size:13px;cursor:pointer;text-align:left;transition:color .22s ease}
|
|
1674
|
+
.dab-nav-item:hover{color:var(--dsw-alias-label-primary)}
|
|
1675
|
+
.dab-nav-item.is-active{color:var(--dsw-alias-brand-primary);font-weight:600}
|
|
1676
|
+
.dab-nav-item svg{flex:none;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}
|
|
1677
|
+
.dab-nav-item:hover svg{transform:scale(1.14) rotate(-5deg)}
|
|
1678
|
+
|
|
1679
|
+
/* ── page chrome ─────────────────────────────────────────────────────────── */
|
|
1680
|
+
.dab-page{animation:dab-page-in .4s cubic-bezier(.22,1,.36,1) both;min-width:0;display:flex;flex-direction:column;gap:13px}
|
|
1681
|
+
.dab-head{margin:2px 0 5px}
|
|
1682
|
+
.dab-overline{font-size:10.5px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--dsw-alias-brand-primary);opacity:.9}
|
|
1683
|
+
.dab-h1{margin:4px 0 0;font-size:21px;font-weight:700;letter-spacing:-.01em}
|
|
1684
|
+
.dab-desc{margin:6px 0 0;font-size:12.5px;line-height:1.55;color:var(--dsw-alias-label-tertiary);max-width:56ch}
|
|
1685
|
+
.dab-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;padding:18px}
|
|
1686
|
+
.dab-card-hover{transition:transform .28s ease,box-shadow .28s ease,border-color .28s ease}
|
|
1687
|
+
.dab-card-hover:hover{transform:translateY(-2px);box-shadow:0 10px 28px -12px rgba(0,0,0,.28)}
|
|
1688
|
+
.dab-rise{animation:dab-rise-in .55s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--d,0) * 62ms)}
|
|
1689
|
+
|
|
1690
|
+
/* ── accent hero (color orb) ─────────────────────────────────────────────── */
|
|
1691
|
+
.dab-hero-accent{display:flex;align-items:center;gap:24px;flex-wrap:wrap}
|
|
1692
|
+
.dab-orb-wrap{position:relative;width:118px;height:118px;flex:none}
|
|
1693
|
+
.dab-orb{position:absolute;inset:11px;border-radius:50%;background:radial-gradient(circle at 32% 28%,rgba(255,255,255,.5),rgba(255,255,255,0) 44%),var(--c,#888);box-shadow:0 16px 36px -10px var(--c-soft,transparent),inset 0 -10px 20px rgba(0,0,0,.16);animation:dab-orb-in .7s cubic-bezier(.22,1,.36,1) both}
|
|
1694
|
+
.dab-orb-ring{position:absolute;inset:0;border-radius:50%;background:conic-gradient(from 0deg,transparent 0 30%,var(--c,#888) 46%,transparent 62%,transparent 76%,var(--c,#888) 90%,transparent 100%);-webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 3.5px),#000 calc(100% - 2.5px));mask:radial-gradient(farthest-side,transparent calc(100% - 3.5px),#000 calc(100% - 2.5px));animation:dab-spin 7s linear infinite;opacity:.9}
|
|
1695
|
+
.dab-hex-caption{font-size:11px;color:var(--dsw-alias-label-tertiary);letter-spacing:.04em}
|
|
1696
|
+
.dab-hex{font-family:var(--dab-mono);font-size:24px;font-weight:600;letter-spacing:.02em;line-height:1.2;margin-top:2px}
|
|
1697
|
+
.dab-hsl-row{display:flex;gap:16px;margin-top:7px;font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
1698
|
+
.dab-hsl-row b{font-weight:600;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1699
|
+
|
|
1700
|
+
/* ── swatches ────────────────────────────────────────────────────────────── */
|
|
1701
|
+
.dab-swatch-title{font-size:12px;font-weight:600;margin-bottom:10px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1702
|
+
.dab-swatches{display:flex;flex-wrap:wrap;gap:9px}
|
|
1703
|
+
.dab-swatch{width:25px;height:25px;border-radius:50%;border:0;padding:0;cursor:pointer;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);transition:transform .22s cubic-bezier(.34,1.56,.64,1),box-shadow .22s ease}
|
|
1704
|
+
.dab-swatch:hover{transform:scale(1.2)}
|
|
1705
|
+
.dab-swatch.is-on{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-1),0 0 0 4px var(--dsw-alias-brand-primary)}
|
|
1706
|
+
|
|
1707
|
+
/* ── wheel card ──────────────────────────────────────────────────────────── */
|
|
1708
|
+
.dab-wheel-card{position:relative;display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap;padding:24px 18px}
|
|
1709
|
+
.dab-wheel-glow{position:absolute;width:230px;height:230px;border-radius:50%;filter:blur(48px);opacity:.2;background:var(--c,#888);pointer-events:none;transition:background .4s ease}
|
|
1710
|
+
.dab-wheel{position:relative;cursor:crosshair;border-radius:50%;box-shadow:0 12px 32px -14px rgba(0,0,0,.4)}
|
|
1711
|
+
.dab-hint{font-size:11.5px;line-height:1.55;color:var(--dsw-alias-label-tertiary);padding:0 4px}
|
|
1712
|
+
|
|
1713
|
+
/* ── precise color inputs ────────────────────────────────────────────────── */
|
|
1714
|
+
.dab-inputs{display:flex;flex-direction:column;gap:10px;min-width:172px}
|
|
1715
|
+
.dab-field{display:flex;align-items:center;gap:8px}
|
|
1716
|
+
.dab-field-label{width:14px;text-align:center;font-family:var(--dab-mono);font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary)}
|
|
1717
|
+
.dab-num{flex:1;min-width:0;height:30px;padding:0 10px;border-radius:9px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-family:var(--dab-mono);font-size:12px;outline:none;transition:border-color .2s,box-shadow .2s}
|
|
1718
|
+
.dab-num:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
|
|
1719
|
+
.dab-num::-webkit-outer-spin-button,.dab-num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
|
|
1720
|
+
.dab-num{-moz-appearance:textfield;appearance:textfield}
|
|
1721
|
+
.dab-swatch-lg{height:38px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);box-shadow:inset 0 0 14px rgba(0,0,0,.1);transition:transform .3s ease}
|
|
1722
|
+
.dab-swatch-lg:hover{transform:scale(1.02)}
|
|
1723
|
+
|
|
1724
|
+
/* ── buttons & chips ─────────────────────────────────────────────────────── */
|
|
1725
|
+
.dab-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:34px;padding:0 14px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);color:var(--dsw-alias-label-primary);font-size:12.5px;font-weight:550;cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease,opacity .18s ease,background .18s ease}
|
|
1726
|
+
.dab-btn:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 5px 14px -6px rgba(0,0,0,.32)}
|
|
1727
|
+
.dab-btn:active:not(:disabled){transform:translateY(0) scale(.97);box-shadow:none}
|
|
1728
|
+
.dab-btn:disabled{opacity:.5;cursor:not-allowed}
|
|
1729
|
+
.dab-btn-primary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);border-color:transparent}
|
|
1730
|
+
.dab-btn-danger{color:var(--dsw-alias-state-error-primary)}
|
|
1731
|
+
.dab-btn-ghost{background:transparent;border-color:transparent;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1732
|
+
.dab-btn-ghost:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2);box-shadow:none}
|
|
1733
|
+
.dab-btn:focus-visible,.dab-nav-item:focus-visible,.dab-seg-item:focus-visible,.dab-swatch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
1734
|
+
.dab-chip-row{display:flex;flex-wrap:wrap;gap:8px}
|
|
1735
|
+
.dab-chip{height:30px;padding:0 14px;border-radius:99px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary));font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all .22s ease}
|
|
1736
|
+
.dab-chip:hover{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary)}
|
|
1737
|
+
.dab-chip.is-active{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);border-color:transparent}
|
|
1738
|
+
|
|
1739
|
+
/* ── sliders ─────────────────────────────────────────────────────────────── */
|
|
1740
|
+
.dab-slider-block{display:flex;flex-direction:column;gap:6px}
|
|
1741
|
+
.dab-slider-block + .dab-slider-block{margin-top:13px}
|
|
1742
|
+
.dab-slider-label{font-size:12px;font-weight:550;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1743
|
+
.dab-slider-val{font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary);min-width:44px;text-align:right}
|
|
1744
|
+
.dab-slider{-webkit-appearance:none;appearance:none;flex:1;min-width:0;height:4px;border-radius:99px;outline:none;cursor:pointer;margin:5px 0;background:linear-gradient(to right,var(--dsw-alias-brand-primary) calc(var(--pct,50) * 1%),var(--dsw-alias-border-l2) calc(var(--pct,50) * 1%));transition:height .15s ease}
|
|
1745
|
+
.dab-slider:hover{height:5px}
|
|
1746
|
+
.dab-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;background:var(--dsw-alias-button-elevated-fill,#fff);border:2px solid var(--dsw-alias-brand-primary);box-shadow:0 1px 5px rgba(0,0,0,.28);transition:transform .16s cubic-bezier(.34,1.56,.64,1)}
|
|
1747
|
+
.dab-slider:hover::-webkit-slider-thumb,.dab-slider:focus-visible::-webkit-slider-thumb{transform:scale(1.22)}
|
|
1748
|
+
.dab-slider:active::-webkit-slider-thumb{transform:scale(1.34)}
|
|
1749
|
+
.dab-slider::-moz-range-thumb{width:13px;height:13px;border-radius:50%;background:var(--dsw-alias-button-elevated-fill,#fff);border:2px solid var(--dsw-alias-brand-primary)}
|
|
1750
|
+
|
|
1751
|
+
/* ── interface part cards ────────────────────────────────────────────────── */
|
|
1752
|
+
.dab-grid-parts{display:grid;grid-template-columns:repeat(auto-fill,minmax(256px,1fr));gap:13px}
|
|
1753
|
+
.dab-part-head{display:flex;align-items:center;gap:11px;margin-bottom:14px}
|
|
1754
|
+
.dab-part-ico{width:32px;height:32px;flex:none;border-radius:10px;display:grid;place-items:center;color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}
|
|
1755
|
+
.dab-part-name{font-size:13.5px;font-weight:600}
|
|
1756
|
+
.dab-part-badge{margin-left:auto;font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);border-radius:99px;padding:3px 9px}
|
|
1757
|
+
|
|
1758
|
+
/* ── segmented control ───────────────────────────────────────────────────── */
|
|
1759
|
+
.dab-seg{position:relative;display:inline-flex;padding:3px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:11px}
|
|
1760
|
+
.dab-seg-thumb{position:absolute;top:3px;bottom:3px;left:3px;width:var(--w,96px);border-radius:8px;background:var(--dsw-alias-button-elevated-fill);box-shadow:0 2px 8px -2px rgba(0,0,0,.28);transition:transform .32s cubic-bezier(.22,1,.36,1)}
|
|
1761
|
+
.dab-seg-item{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;width:var(--w,96px);height:30px;border:0;background:none;border-radius:8px;color:var(--dsw-alias-label-tertiary);font-size:12.5px;cursor:pointer;transition:color .25s ease}
|
|
1762
|
+
.dab-seg-item.is-active{color:var(--dsw-alias-label-primary);font-weight:600}
|
|
1763
|
+
|
|
1764
|
+
/* ── toggle switch ───────────────────────────────────────────────────────── */
|
|
1765
|
+
.dab-toggle{position:relative;width:42px;height:24px;flex:none;border-radius:99px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;padding:0;transition:background .28s ease,border-color .28s ease}
|
|
1766
|
+
.dab-toggle-knob{position:absolute;top:2.5px;left:2.5px;width:17px;height:17px;border-radius:50%;background:var(--dsw-alias-label-secondary,#999);box-shadow:0 1px 3px rgba(0,0,0,.3);transition:transform .28s cubic-bezier(.22,1,.36,1),background .28s ease}
|
|
1767
|
+
.dab-toggle.is-on{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
1768
|
+
.dab-toggle.is-on .dab-toggle-knob{transform:translateX(18px);background:#fff}
|
|
1769
|
+
|
|
1770
|
+
/* ── background preview hero ─────────────────────────────────────────────── */
|
|
1771
|
+
.dab-hero{position:relative;border-radius:16px;overflow:hidden;aspect-ratio:16/9;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2)}
|
|
1772
|
+
.dab-hero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .5s cubic-bezier(.22,1,.36,1)}
|
|
1773
|
+
.dab-hero:hover .dab-hero-img{transform:scale(1.03)}
|
|
1774
|
+
.dab-hero-empty{position:absolute;inset:6px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:var(--dsw-alias-label-tertiary);font-size:12.5px;border:1.5px dashed var(--dsw-alias-border-l2);border-radius:12px;cursor:pointer;background:transparent;transition:border-color .25s,color .25s,background .25s;width:auto;height:auto}
|
|
1775
|
+
.dab-hero-empty:hover{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}
|
|
1776
|
+
.dab-hero-empty.is-over{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 8%,transparent)}
|
|
1777
|
+
.dab-hero-badge{position:absolute;top:10px;left:10px;display:inline-flex;align-items:center;gap:5px;height:24px;padding:0 11px;border-radius:99px;background:rgba(0,0,0,.45);color:#fff;font-size:11px;backdrop-filter:blur(6px);pointer-events:none}
|
|
1778
|
+
.dab-hero-veil{position:absolute;left:0;right:0;bottom:0;padding:34px 12px 12px;display:flex;align-items:flex-end;justify-content:flex-end;gap:8px;background:linear-gradient(to top,rgba(0,0,0,.55),rgba(0,0,0,0));opacity:0;transform:translateY(6px);transition:opacity .3s ease,transform .3s ease}
|
|
1779
|
+
.dab-hero:hover .dab-hero-veil{opacity:1;transform:none}
|
|
1780
|
+
.dab-hero-veil .dab-btn{background:rgba(255,255,255,.94);color:#14161a;border-color:transparent;height:30px;font-size:12px}
|
|
1781
|
+
.dab-hero-veil .dab-btn-danger{color:#dc2626}
|
|
1782
|
+
|
|
1783
|
+
/* ── generated background type cards ─────────────────────────────────────── */
|
|
1784
|
+
.dab-types{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
|
|
1785
|
+
.dab-type{position:relative;border:1.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:14px;padding:10px;cursor:pointer;text-align:left;font:inherit;transition:border-color .25s,transform .25s,box-shadow .25s}
|
|
1786
|
+
.dab-type:hover{transform:translateY(-2px)}
|
|
1787
|
+
.dab-type.is-active{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
|
|
1788
|
+
.dab-type-thumb{height:62px;border-radius:9px;overflow:hidden;position:relative}
|
|
1789
|
+
.dab-type-name{margin-top:9px;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
1790
|
+
.dab-type-desc{margin-top:2px;font-size:11px;color:var(--dsw-alias-label-tertiary);line-height:1.4}
|
|
1791
|
+
.dab-type-check{position:absolute;top:16px;right:16px;width:20px;height:20px;border-radius:50%;background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);display:grid;place-items:center;opacity:0;transform:scale(.4);transition:opacity .25s,transform .25s cubic-bezier(.34,1.56,.64,1)}
|
|
1792
|
+
.dab-type.is-active .dab-type-check{opacity:1;transform:none}
|
|
1793
|
+
.dab-thumb-mesh{background:radial-gradient(circle at 22% 30%,#f472b6,transparent 42%),radial-gradient(circle at 80% 22%,#38bdf8,transparent 46%),radial-gradient(circle at 52% 84%,#fbbf24,transparent 52%),#1e293b}
|
|
1794
|
+
.dab-thumb-shader{background:linear-gradient(120deg,#0ea5e9,#8b5cf6,#d946ef,#0ea5e9);background-size:300% 300%;animation:dab-flow 6s linear infinite}
|
|
1795
|
+
.dab-thumb-pattern{background-image:radial-gradient(circle,rgba(255,255,255,.92) 1.2px,transparent 1.4px);background-size:10px 10px;background-color:#334155}
|
|
1796
|
+
|
|
1797
|
+
/* ── seed row ────────────────────────────────────────────────────────────── */
|
|
1798
|
+
.dab-seed{display:flex;align-items:center;gap:13px;padding:12px 14px;border-radius:12px;background:var(--dsw-alias-bg-layer-2);margin-top:14px}
|
|
1799
|
+
.dab-seed-ico{color:var(--dsw-alias-brand-primary);display:grid;place-items:center}
|
|
1800
|
+
.dab-seed-txt{flex:1;min-width:0}
|
|
1801
|
+
.dab-seed-title{font-size:12.5px;font-weight:600;display:flex;align-items:center;gap:6px}
|
|
1802
|
+
.dab-seed-desc{font-size:11px;color:var(--dsw-alias-label-tertiary);margin-top:2px}
|
|
1803
|
+
.dab-spin{animation:dab-rotate .7s cubic-bezier(.3,.7,.3,1) 1}
|
|
1804
|
+
|
|
1805
|
+
/* ── profile page ────────────────────────────────────────────────────────── */
|
|
1806
|
+
.dab-profile-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(238px,1fr));gap:13px}
|
|
1807
|
+
.dab-profile-ico{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;margin-bottom:13px;color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}
|
|
1808
|
+
.dab-profile-title{font-size:14px;font-weight:650}
|
|
1809
|
+
.dab-profile-desc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.55;margin:5px 0 15px}
|
|
1810
|
+
.dab-footer{margin-top:6px;padding:14px 4px 0;border-top:1px solid var(--dsw-alias-border-l2);display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
1811
|
+
.dab-footer-mono{font-family:var(--dab-mono);letter-spacing:.02em}
|
|
1812
|
+
|
|
1813
|
+
/* ── toast ───────────────────────────────────────────────────────────────── */
|
|
1814
|
+
.dab-toast{position:fixed;left:50%;bottom:30px;transform:translateX(-50%);display:flex;align-items:center;gap:8px;height:38px;padding:0 16px;border-radius:99px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 10px 30px -8px rgba(0,0,0,.38);font-size:12.5px;z-index:10001;animation:dab-toast-in .32s cubic-bezier(.22,1,.36,1) both}
|
|
1815
|
+
.dab-toast-ok{color:var(--dsw-alias-brand-primary);display:grid;place-items:center}
|
|
1816
|
+
.dab-toast-err{color:var(--dsw-alias-state-error-primary);display:grid;place-items:center}
|
|
1817
|
+
|
|
1818
|
+
/* ── modals (editor / eyedropper / crash) ────────────────────────────────── */
|
|
1819
|
+
/* Pin to the viewport explicitly with vw/vh so ancestor padding/margins on
|
|
1820
|
+
* body cannot shift or clip the overlay; keep it above host sidebar chrome.
|
|
1821
|
+
* pointer-events:auto re-enables interaction: these overlays render inside a
|
|
1822
|
+
* Portal root that is pointer-events:none so it never blocks the page alone. */
|
|
1823
|
+
.dab-overlay{position:fixed;left:0;top:0;width:100vw;height:100vh;z-index:999999;background:rgba(8,10,14,.62);backdrop-filter:blur(8px);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;animation:dab-fade-in .25s ease both;pointer-events:auto}
|
|
1824
|
+
.dab-overlay-title{color:#fff;font-size:15px;font-weight:600}
|
|
1825
|
+
.dab-overlay-hint{color:rgba(255,255,255,.62);font-size:12px}
|
|
1826
|
+
.dab-modal-card{animation:dab-zoom-in .3s cubic-bezier(.22,1,.36,1) both;max-width:calc(100vw - 40px);max-height:calc(100vh - 120px);overflow:auto}
|
|
1827
|
+
.dab-overlay .dab-btn{background:rgba(255,255,255,.94);color:#14161a;border-color:transparent}
|
|
1828
|
+
.dab-overlay .dab-btn-primary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text)}
|
|
1829
|
+
.dab-crash{display:flex;flex-direction:column;gap:10px;align-items:center;padding:28px 18px;border:1px solid var(--dsw-alias-border-l2);border-radius:16px}
|
|
1830
|
+
.dab-crash-title{font-size:15px;font-weight:650}
|
|
1831
|
+
.dab-crash-desc{font-size:12px;color:var(--dsw-alias-label-tertiary);text-align:center;line-height:1.5}
|
|
1832
|
+
|
|
1833
|
+
/* ── keyframes ───────────────────────────────────────────────────────────── */
|
|
1834
|
+
@keyframes dab-fade-in{from{opacity:0}to{opacity:1}}
|
|
1835
|
+
@keyframes dab-page-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
|
|
1836
|
+
@keyframes dab-rise-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
|
|
1837
|
+
@keyframes dab-orb-in{from{opacity:0;transform:scale(.82)}to{opacity:1;transform:none}}
|
|
1838
|
+
@keyframes dab-spin{to{transform:rotate(360deg)}}
|
|
1839
|
+
@keyframes dab-rotate{to{transform:rotate(360deg)}}
|
|
1840
|
+
@keyframes dab-toast-in{from{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%,0)}}
|
|
1841
|
+
@keyframes dab-zoom-in{from{opacity:0;transform:scale(.94) translateY(8px)}to{opacity:1;transform:none}}
|
|
1842
|
+
@keyframes dab-flow{to{background-position:300% 50%}}
|
|
1843
|
+
|
|
1844
|
+
/* ── responsive & motion preferences ─────────────────────────────────────── */
|
|
1845
|
+
@container (max-width:620px){
|
|
1846
|
+
.dab-shell{grid-template-columns:1fr;gap:14px}
|
|
1847
|
+
.dab-nav{position:static;flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
|
|
1848
|
+
.dab-nav-list{flex-direction:row;overflow-x:auto;scrollbar-width:none}
|
|
1849
|
+
.dab-nav-list::-webkit-scrollbar{display:none}
|
|
1850
|
+
.dab-nav-ind{display:none}
|
|
1851
|
+
.dab-nav-item{flex:none;padding:0 10px}
|
|
1852
|
+
.dab-nav-item.is-active{background:var(--dsw-alias-bg-layer-2)}
|
|
1853
|
+
.dab-types{grid-template-columns:1fr}
|
|
1854
|
+
}
|
|
1855
|
+
@container (min-width:621px){
|
|
1856
|
+
.dab-shell{grid-template-columns:158px minmax(0,1fr);gap:26px}
|
|
1857
|
+
}
|
|
1858
|
+
@media (prefers-reduced-motion:reduce){
|
|
1859
|
+
.dab-root *,.dab-root *::before,.dab-root *::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
|
|
1860
|
+
}
|
|
1861
|
+
`;
|
|
1862
|
+
const CSS_ID = "dab-ui-css";
|
|
1863
|
+
/** Inject the design-system stylesheet once per document (HMR-safe). */
|
|
1864
|
+
function ensureUiCss() {
|
|
1865
|
+
if (typeof document === "undefined") return;
|
|
1866
|
+
let el = document.getElementById(CSS_ID);
|
|
1867
|
+
if (!el) {
|
|
1868
|
+
el = document.createElement("style");
|
|
1869
|
+
el.id = CSS_ID;
|
|
1870
|
+
document.head.appendChild(el);
|
|
980
1871
|
}
|
|
981
|
-
|
|
1872
|
+
if (el.textContent !== UI_CSS) el.textContent = UI_CSS;
|
|
1873
|
+
}
|
|
982
1874
|
//#endregion
|
|
983
|
-
//#region src/client/
|
|
984
|
-
/**
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1875
|
+
//#region src/client/components/icons.tsx
|
|
1876
|
+
/** Sun glyph, migrated from @deepseek-ai/dsh-client-ui-primitives IconLightOutline16. */
|
|
1877
|
+
const SUN_PATHS = "<path d=\"M11.3496 8C11.3496 6.14985 9.85015 4.65039 8 4.65039C6.14985 4.65039 4.65039 6.14985 4.65039 8C4.65039 9.85015 6.14985 11.3496 8 11.3496C9.85015 11.3496 11.3496 9.85015 11.3496 8ZM12.6504 8C12.6504 10.5681 10.5681 12.6504 8 12.6504C5.43188 12.6504 3.34961 10.5681 3.34961 8C3.34961 5.43188 5.43188 3.34961 8 3.34961C10.5681 3.34961 12.6504 5.43188 12.6504 8Z\" fill=\"currentColor\"/><path d=\"M8.65039 0.5V2.5H7.34961V0.5H8.65039Z\" fill=\"currentColor\"/><path d=\"M8.65039 13.5V15.5H7.34961V13.5H8.65039Z\" fill=\"currentColor\"/><path d=\"M3.15808 2.24035L4.57229 3.65456L3.6525 4.57435L2.23829 3.16014L3.15808 2.24035Z\" fill=\"currentColor\"/><path d=\"M12.3505 11.4327L13.7647 12.8469L12.8449 13.7667L11.4307 12.3525L12.3505 11.4327Z\" fill=\"currentColor\"/><path d=\"M2.24537 12.8469L3.65958 11.4327L4.57937 12.3525L3.16516 13.7667L2.24537 12.8469Z\" fill=\"currentColor\"/><path d=\"M11.4377 3.65455L12.852 2.24033L13.7718 3.16012L12.3575 4.57434L11.4377 3.65455Z\" fill=\"currentColor\"/><path d=\"M0.5 7.35461H2.5V8.6554H0.5L0.5 7.35461Z\" fill=\"currentColor\"/><path d=\"M13.5 7.35461H15.5V8.6554H13.5V8.6554Z\" fill=\"currentColor\"/>";
|
|
1878
|
+
function SunIcon({ size = 16, className }) {
|
|
1879
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1880
|
+
width: size,
|
|
1881
|
+
height: size,
|
|
1882
|
+
className,
|
|
1883
|
+
viewBox: "0 0 16 16",
|
|
1884
|
+
fill: "none",
|
|
1885
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1886
|
+
dangerouslySetInnerHTML: { __html: SUN_PATHS }
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
function Glyph({ children, size = 16, className }) {
|
|
1890
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1891
|
+
width: size,
|
|
1892
|
+
height: size,
|
|
1893
|
+
className,
|
|
1894
|
+
viewBox: "0 0 16 16",
|
|
1895
|
+
fill: "none",
|
|
1896
|
+
stroke: "currentColor",
|
|
1897
|
+
strokeWidth: 1.5,
|
|
1898
|
+
strokeLinecap: "round",
|
|
1899
|
+
strokeLinejoin: "round",
|
|
1900
|
+
"aria-hidden": "true",
|
|
1901
|
+
children
|
|
1902
|
+
});
|
|
995
1903
|
}
|
|
1904
|
+
const DropletIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
1905
|
+
size,
|
|
1906
|
+
className,
|
|
1907
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 1.9c2.4 2.8 4.3 5 4.3 7.1a4.3 4.3 0 1 1-8.6 0C3.7 6.9 5.6 4.7 8 1.9z" })
|
|
1908
|
+
});
|
|
1909
|
+
const LayersIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1910
|
+
size,
|
|
1911
|
+
className,
|
|
1912
|
+
children: [
|
|
1913
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 2.2 13.2 5 8 7.8 2.8 5 8 2.2z" }),
|
|
1914
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.8 8.2 8 11l5.2-2.8" }),
|
|
1915
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.8 11.2 8 14l5.2-2.8" })
|
|
1916
|
+
]
|
|
1917
|
+
});
|
|
1918
|
+
const PhotoIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1919
|
+
size,
|
|
1920
|
+
className,
|
|
1921
|
+
children: [
|
|
1922
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1923
|
+
x: "2",
|
|
1924
|
+
y: "3.2",
|
|
1925
|
+
width: "12",
|
|
1926
|
+
height: "9.6",
|
|
1927
|
+
rx: "2"
|
|
1928
|
+
}),
|
|
1929
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1930
|
+
cx: "5.7",
|
|
1931
|
+
cy: "6.3",
|
|
1932
|
+
r: "0.9"
|
|
1933
|
+
}),
|
|
1934
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M14 10.4l-2.8-2.8-4.8 4.8" })
|
|
1935
|
+
]
|
|
1936
|
+
});
|
|
1937
|
+
const SlidersIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1938
|
+
size,
|
|
1939
|
+
className,
|
|
1940
|
+
children: [
|
|
1941
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.5 4.5h4.9M11.6 4.5h1.9M2.5 11.5h1.9M8.6 11.5h4.9" }),
|
|
1942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1943
|
+
cx: "9.5",
|
|
1944
|
+
cy: "4.5",
|
|
1945
|
+
r: "1.7"
|
|
1946
|
+
}),
|
|
1947
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1948
|
+
cx: "5.5",
|
|
1949
|
+
cy: "11.5",
|
|
1950
|
+
r: "1.7"
|
|
1951
|
+
})
|
|
1952
|
+
]
|
|
1953
|
+
});
|
|
1954
|
+
const SparkleIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1955
|
+
size,
|
|
1956
|
+
className,
|
|
1957
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M7.2 1.8l1.2 3.1 3.1 1.2-3.1 1.2-1.2 3.1-1.2-3.1L2.9 6.1 6 4.9l1.2-3.1z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 10.5l.5 1.2 1.2.5-1.2.5-.5 1.2-.5-1.2-1.2-.5 1.2-.5.5-1.2z" })]
|
|
1958
|
+
});
|
|
1959
|
+
const RefreshIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1960
|
+
size,
|
|
1961
|
+
className,
|
|
1962
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M13.2 8A5.2 5.2 0 1 1 11.5 4.2" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M13.4 1.8v2.9h-2.9" })]
|
|
1963
|
+
});
|
|
1964
|
+
const DownloadIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1965
|
+
size,
|
|
1966
|
+
className,
|
|
1967
|
+
children: [
|
|
1968
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 2.2v8" }),
|
|
1969
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4.6 7l3.4 3.4L11.4 7" }),
|
|
1970
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.8 13.8h10.4" })
|
|
1971
|
+
]
|
|
1972
|
+
});
|
|
1973
|
+
const UploadIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1974
|
+
size,
|
|
1975
|
+
className,
|
|
1976
|
+
children: [
|
|
1977
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 10.4V2.6" }),
|
|
1978
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4.6 5.8L8 2.4l3.4 3.4" }),
|
|
1979
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.8 13.8h10.4" })
|
|
1980
|
+
]
|
|
1981
|
+
});
|
|
1982
|
+
const LockIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1983
|
+
size,
|
|
1984
|
+
className,
|
|
1985
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1986
|
+
x: "3.6",
|
|
1987
|
+
y: "7.2",
|
|
1988
|
+
width: "8.8",
|
|
1989
|
+
height: "6",
|
|
1990
|
+
rx: "1.4"
|
|
1991
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.6 7.2V5.4a2.4 2.4 0 0 1 4.8 0v1.8" })]
|
|
1992
|
+
});
|
|
1993
|
+
const TrashIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
1994
|
+
size,
|
|
1995
|
+
className,
|
|
1996
|
+
children: [
|
|
1997
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.8 4.4h10.4" }),
|
|
1998
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M6.4 4.4V2.9h3.2v1.5" }),
|
|
1999
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4.4 4.4l.5 8.6h6.2l.5-8.6" })
|
|
2000
|
+
]
|
|
2001
|
+
});
|
|
2002
|
+
const EditIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
2003
|
+
size,
|
|
2004
|
+
className,
|
|
2005
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M9.5 4l2.5 2.5" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 13l.8-3L10 3.8 12.2 6 6 12.2 3 13z" })]
|
|
2006
|
+
});
|
|
2007
|
+
const PipetteIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
2008
|
+
size,
|
|
2009
|
+
className,
|
|
2010
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
2011
|
+
cx: "8",
|
|
2012
|
+
cy: "8",
|
|
2013
|
+
r: "4.2"
|
|
2014
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 1.6v2.6M8 11.8v2.6M1.6 8h2.6M11.8 8h2.6" })]
|
|
2015
|
+
});
|
|
2016
|
+
const CheckIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
2017
|
+
size,
|
|
2018
|
+
className,
|
|
2019
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3.2 8.6l3 3L12.8 5" })
|
|
2020
|
+
});
|
|
2021
|
+
const AlertIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
2022
|
+
size,
|
|
2023
|
+
className,
|
|
2024
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 2.6l5.4 9.8H2.6L8 2.6z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 6.8v2.4M8 11.4v.01" })]
|
|
2025
|
+
});
|
|
2026
|
+
const CanvasIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
2027
|
+
size,
|
|
2028
|
+
className,
|
|
2029
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
2030
|
+
x: "2.2",
|
|
2031
|
+
y: "2.2",
|
|
2032
|
+
width: "11.6",
|
|
2033
|
+
height: "11.6",
|
|
2034
|
+
rx: "2"
|
|
2035
|
+
})
|
|
2036
|
+
});
|
|
2037
|
+
const SidebarIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
2038
|
+
size,
|
|
2039
|
+
className,
|
|
2040
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
2041
|
+
x: "2.2",
|
|
2042
|
+
y: "2.2",
|
|
2043
|
+
width: "11.6",
|
|
2044
|
+
height: "11.6",
|
|
2045
|
+
rx: "2"
|
|
2046
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M6.6 2.2v11.6" })]
|
|
2047
|
+
});
|
|
2048
|
+
const ChatIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
2049
|
+
size,
|
|
2050
|
+
className,
|
|
2051
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M13.4 4.4v4.4a2 2 0 0 1-2 2H6.2l-3.6 3V4.4a2 2 0 0 1 2-2h6.8a2 2 0 0 1 2 2z" })
|
|
2052
|
+
});
|
|
2053
|
+
const GearIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
2054
|
+
size,
|
|
2055
|
+
className,
|
|
2056
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
2057
|
+
cx: "8",
|
|
2058
|
+
cy: "8",
|
|
2059
|
+
r: "2.1"
|
|
2060
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 1.7v1.8M8 12.5v1.8M1.7 8h1.8M12.5 8h1.8M3.6 3.6l1.3 1.3M11.1 11.1l1.3 1.3M12.4 3.6l-1.3 1.3M4.9 11.1l-1.3 1.3" })]
|
|
2061
|
+
});
|
|
996
2062
|
//#endregion
|
|
997
|
-
//#region src/client/components/
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
2063
|
+
//#region src/client/components/ErrorBoundary.tsx
|
|
2064
|
+
/**
|
|
2065
|
+
* Catches render errors from the theme section subtree (wheel, sliders, editor)
|
|
2066
|
+
* so a single bad state can never take down the whole settings panel. Shows a
|
|
2067
|
+
* compact fallback with a reset button; the reset re-renders the section with
|
|
2068
|
+
* the current saved config, which is enough to recover from most transient
|
|
2069
|
+
* failures (corrupt transient UI state, stale image decode, etc.).
|
|
2070
|
+
*/
|
|
2071
|
+
var ErrorBoundary = class extends react.Component {
|
|
2072
|
+
state = { error: null };
|
|
2073
|
+
static getDerivedStateFromError(error) {
|
|
2074
|
+
return { error };
|
|
2075
|
+
}
|
|
2076
|
+
componentDidCatch(error, info) {
|
|
2077
|
+
console.error("dsh-any-background: section render crashed", error, info.componentStack);
|
|
2078
|
+
}
|
|
2079
|
+
reset = () => {
|
|
2080
|
+
this.setState({ error: null });
|
|
2081
|
+
this.props.onReset?.();
|
|
2082
|
+
};
|
|
2083
|
+
render() {
|
|
2084
|
+
if (this.state.error) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2085
|
+
className: "dab-crash",
|
|
2086
|
+
children: [
|
|
2087
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2088
|
+
className: "dab-crash-title",
|
|
2089
|
+
children: this.props.t("crashTitle")
|
|
2090
|
+
}),
|
|
2091
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2092
|
+
className: "dab-crash-desc",
|
|
2093
|
+
children: this.props.t("crashDesc")
|
|
2094
|
+
}),
|
|
2095
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2096
|
+
type: "button",
|
|
2097
|
+
className: "dab-btn",
|
|
2098
|
+
onClick: this.reset,
|
|
2099
|
+
children: this.props.t("crashReset")
|
|
2100
|
+
})
|
|
2101
|
+
]
|
|
2102
|
+
});
|
|
2103
|
+
return this.props.children;
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
//#endregion
|
|
2107
|
+
//#region src/client/components/Portal.tsx
|
|
2108
|
+
/**
|
|
2109
|
+
* Render children into a fixed root attached to document.documentElement.
|
|
2110
|
+
*
|
|
2111
|
+
* The host's sidebar is often implemented by translating the body or a wrapper
|
|
2112
|
+
* (margin-left / transform). A fixed element portaled to body would still be
|
|
2113
|
+
* captured by that transformed ancestor and shift with the sidebar. Attaching
|
|
2114
|
+
* the portal root directly to <html> escapes body-level transforms so the
|
|
2115
|
+
* overlay is always painted relative to the viewport and centered correctly.
|
|
2116
|
+
*/
|
|
2117
|
+
function Portal({ children }) {
|
|
2118
|
+
const [target, setTarget] = (0, react.useState)(null);
|
|
2119
|
+
(0, react.useEffect)(() => {
|
|
2120
|
+
const el = document.createElement("div");
|
|
2121
|
+
el.dataset.dabPortal = "1";
|
|
2122
|
+
el.style.cssText = "position:fixed;inset:0;pointer-events:none;z-index:999999";
|
|
2123
|
+
document.documentElement.appendChild(el);
|
|
2124
|
+
setTarget(el);
|
|
2125
|
+
return () => {
|
|
2126
|
+
el.remove();
|
|
2127
|
+
};
|
|
2128
|
+
}, []);
|
|
2129
|
+
return target ? (0, react_dom.createPortal)(children, target) : null;
|
|
2130
|
+
}
|
|
2131
|
+
//#endregion
|
|
2132
|
+
//#region src/client/components/ColorWheel.tsx
|
|
2133
|
+
const WHEEL_SIZE = 220;
|
|
2134
|
+
const CX = WHEEL_SIZE / 2;
|
|
2135
|
+
const RING_OUTER = 106;
|
|
2136
|
+
const RING_INNER = 82;
|
|
2137
|
+
const SQ_HALF = RING_INNER / Math.SQRT2;
|
|
2138
|
+
/** Static hue ring cached once across all wheels. */
|
|
2139
|
+
let ringCache = null;
|
|
2140
|
+
function getRingCache() {
|
|
2141
|
+
if (ringCache) return ringCache;
|
|
2142
|
+
const cvs = document.createElement("canvas");
|
|
2143
|
+
cvs.width = WHEEL_SIZE;
|
|
2144
|
+
cvs.height = WHEEL_SIZE;
|
|
2145
|
+
const c = cvs.getContext("2d");
|
|
2146
|
+
const g = c.createConicGradient(0, CX, CX);
|
|
2147
|
+
for (let i = 0; i <= 360; i++) g.addColorStop(i / 360, `hsl(${i},100%,50%)`);
|
|
2148
|
+
c.beginPath();
|
|
2149
|
+
c.arc(CX, CX, RING_OUTER, 0, Math.PI * 2);
|
|
2150
|
+
c.arc(CX, CX, RING_INNER, 0, Math.PI * 2, true);
|
|
2151
|
+
c.fillStyle = g;
|
|
2152
|
+
c.fill();
|
|
2153
|
+
ringCache = cvs;
|
|
2154
|
+
return ringCache;
|
|
2155
|
+
}
|
|
2156
|
+
function drawMarkers(ctx, hue, sat, lit) {
|
|
2157
|
+
const hRad = hue * Math.PI / 180;
|
|
2158
|
+
const hR = 94;
|
|
2159
|
+
const hmx = CX + Math.cos(hRad) * hR;
|
|
2160
|
+
const hmy = CX + Math.sin(hRad) * hR;
|
|
2161
|
+
ctx.beginPath();
|
|
2162
|
+
ctx.arc(hmx, hmy, 8, 0, Math.PI * 2);
|
|
2163
|
+
ctx.fillStyle = "rgba(0,0,0,0.25)";
|
|
2164
|
+
ctx.fill();
|
|
2165
|
+
ctx.beginPath();
|
|
2166
|
+
ctx.arc(hmx, hmy, 6.5, 0, Math.PI * 2);
|
|
2167
|
+
ctx.strokeStyle = "#fff";
|
|
2168
|
+
ctx.lineWidth = 2;
|
|
2169
|
+
ctx.stroke();
|
|
2170
|
+
const gx = CX - SQ_HALF, gy = CX - SQ_HALF, sz = SQ_HALF * 2;
|
|
2171
|
+
const smx = gx + sat * sz;
|
|
2172
|
+
const smy = gy + (1 - lit) * sz;
|
|
2173
|
+
ctx.beginPath();
|
|
2174
|
+
ctx.arc(smx, smy, 7, 0, Math.PI * 2);
|
|
2175
|
+
ctx.fillStyle = "rgba(0,0,0,0.25)";
|
|
2176
|
+
ctx.fill();
|
|
2177
|
+
ctx.beginPath();
|
|
2178
|
+
ctx.arc(smx, smy, 5.5, 0, Math.PI * 2);
|
|
2179
|
+
ctx.strokeStyle = "#fff";
|
|
2180
|
+
ctx.lineWidth = 2;
|
|
2181
|
+
ctx.stroke();
|
|
2182
|
+
ctx.beginPath();
|
|
2183
|
+
ctx.arc(smx, smy, 3.5, 0, Math.PI * 2);
|
|
2184
|
+
ctx.strokeStyle = "#000";
|
|
2185
|
+
ctx.lineWidth = 1;
|
|
2186
|
+
ctx.stroke();
|
|
2187
|
+
}
|
|
2188
|
+
function drawSquare(c, hue) {
|
|
2189
|
+
const gx = CX - SQ_HALF, gy = CX - SQ_HALF, sz = SQ_HALF * 2;
|
|
2190
|
+
c.clearRect(gx - 1, gy - 1, sz + 2, sz + 2);
|
|
2191
|
+
c.fillStyle = "#fff";
|
|
2192
|
+
c.fillRect(gx, gy, sz, sz);
|
|
2193
|
+
const gh = c.createLinearGradient(gx, 0, gx + sz, 0);
|
|
2194
|
+
gh.addColorStop(0, "rgba(255,255,255,1)");
|
|
2195
|
+
gh.addColorStop(1, `hsl(${hue},100%,50%)`);
|
|
2196
|
+
c.fillStyle = gh;
|
|
2197
|
+
c.fillRect(gx, gy, sz, sz);
|
|
1024
2198
|
const gv = c.createLinearGradient(0, gy, 0, gy + sz);
|
|
1025
2199
|
gv.addColorStop(0, "rgba(0,0,0,0)");
|
|
1026
2200
|
gv.addColorStop(1, "rgba(0,0,0,1)");
|
|
1027
2201
|
c.fillStyle = gv;
|
|
1028
2202
|
c.fillRect(gx, gy, sz, sz);
|
|
1029
|
-
const hRad = (hue - 90) * Math.PI / 180;
|
|
1030
|
-
const hR = 94;
|
|
1031
|
-
const hmx = CX + Math.cos(hRad) * hR, hmy = CX + Math.sin(hRad) * hR;
|
|
1032
|
-
c.beginPath();
|
|
1033
|
-
c.arc(hmx, hmy, 8, 0, Math.PI * 2);
|
|
1034
|
-
c.fillStyle = "rgba(0,0,0,0.25)";
|
|
1035
|
-
c.fill();
|
|
1036
|
-
c.beginPath();
|
|
1037
|
-
c.arc(hmx, hmy, 6.5, 0, Math.PI * 2);
|
|
1038
|
-
c.strokeStyle = "#fff";
|
|
1039
|
-
c.lineWidth = 2;
|
|
1040
|
-
c.stroke();
|
|
1041
|
-
const smx = gx + sat * sz, smy = gy + (1 - lit) * sz;
|
|
1042
|
-
c.beginPath();
|
|
1043
|
-
c.arc(smx, smy, 7, 0, Math.PI * 2);
|
|
1044
|
-
c.fillStyle = "rgba(0,0,0,0.25)";
|
|
1045
|
-
c.fill();
|
|
1046
|
-
c.beginPath();
|
|
1047
|
-
c.arc(smx, smy, 5.5, 0, Math.PI * 2);
|
|
1048
|
-
c.strokeStyle = "#fff";
|
|
1049
|
-
c.lineWidth = 2;
|
|
1050
|
-
c.stroke();
|
|
1051
|
-
c.beginPath();
|
|
1052
|
-
c.arc(smx, smy, 3.5, 0, Math.PI * 2);
|
|
1053
|
-
c.strokeStyle = "#000";
|
|
1054
|
-
c.lineWidth = 1;
|
|
1055
|
-
c.stroke();
|
|
1056
2203
|
}
|
|
1057
2204
|
function hitTest(x, y) {
|
|
1058
2205
|
if (Math.abs(x - CX) <= SQ_HALF && Math.abs(y - CX) <= SQ_HALF) return "square";
|
|
@@ -1062,7 +2209,7 @@ window.__ModuleLoader__.load({
|
|
|
1062
2209
|
return null;
|
|
1063
2210
|
}
|
|
1064
2211
|
function pickHue(x, y) {
|
|
1065
|
-
let angle = Math.atan2(y - CX, x - CX) * 180 / Math.PI
|
|
2212
|
+
let angle = Math.atan2(y - CX, x - CX) * 180 / Math.PI;
|
|
1066
2213
|
if (angle < 0) angle += 360;
|
|
1067
2214
|
return angle;
|
|
1068
2215
|
}
|
|
@@ -1070,7 +2217,7 @@ window.__ModuleLoader__.load({
|
|
|
1070
2217
|
const gx = CX - SQ_HALF, gy = CX - SQ_HALF, sz = SQ_HALF * 2;
|
|
1071
2218
|
return [Math.max(0, Math.min(1, (x - gx) / sz)), Math.max(.02, Math.min(.98, 1 - (y - gy) / sz))];
|
|
1072
2219
|
}
|
|
1073
|
-
function ColorWheel({ hue, sat, lit, onChange }) {
|
|
2220
|
+
const ColorWheel = (0, react.memo)(function ColorWheel({ hue, sat, lit, onChange }) {
|
|
1074
2221
|
const cvsRef = (0, react.useRef)(null);
|
|
1075
2222
|
const [col, setCol] = (0, react.useState)({
|
|
1076
2223
|
hue,
|
|
@@ -1091,35 +2238,55 @@ window.__ModuleLoader__.load({
|
|
|
1091
2238
|
lit
|
|
1092
2239
|
]);
|
|
1093
2240
|
(0, react.useEffect)(() => {
|
|
1094
|
-
|
|
2241
|
+
const cvs = cvsRef.current;
|
|
2242
|
+
if (!cvs) return;
|
|
2243
|
+
const ctx = cvs.getContext("2d");
|
|
2244
|
+
ctx.clearRect(0, 0, WHEEL_SIZE, WHEEL_SIZE);
|
|
2245
|
+
drawSquare(ctx, col.hue);
|
|
2246
|
+
ctx.drawImage(getRingCache(), 0, 0);
|
|
2247
|
+
drawMarkers(ctx, col.hue, col.sat, col.lit);
|
|
1095
2248
|
}, [col]);
|
|
1096
|
-
const
|
|
2249
|
+
const pendingRef = (0, react.useRef)(null);
|
|
2250
|
+
const rafRef = (0, react.useRef)(null);
|
|
2251
|
+
const flushPending = (0, react.useCallback)(() => {
|
|
2252
|
+
rafRef.current = null;
|
|
2253
|
+
const p = pendingRef.current;
|
|
2254
|
+
if (!p) return;
|
|
2255
|
+
pendingRef.current = null;
|
|
1097
2256
|
setCol({
|
|
1098
|
-
hue:
|
|
1099
|
-
sat:
|
|
1100
|
-
lit:
|
|
2257
|
+
hue: p.h,
|
|
2258
|
+
sat: p.s,
|
|
2259
|
+
lit: p.l
|
|
1101
2260
|
});
|
|
1102
|
-
onChange(
|
|
2261
|
+
onChange(p.h, p.s, p.l);
|
|
1103
2262
|
}, [onChange]);
|
|
2263
|
+
const schedule = (0, react.useCallback)((h, s, l) => {
|
|
2264
|
+
pendingRef.current = {
|
|
2265
|
+
h,
|
|
2266
|
+
s,
|
|
2267
|
+
l
|
|
2268
|
+
};
|
|
2269
|
+
if (rafRef.current === null) rafRef.current = requestAnimationFrame(flushPending);
|
|
2270
|
+
}, [flushPending]);
|
|
1104
2271
|
const onDown = (0, react.useCallback)((e) => {
|
|
1105
2272
|
const r = cvsRef.current.getBoundingClientRect();
|
|
1106
2273
|
const x = e.clientX - r.left, y = e.clientY - r.top;
|
|
1107
2274
|
const region = hitTest(x, y);
|
|
1108
2275
|
if (!region) return;
|
|
1109
|
-
if (region === "ring")
|
|
2276
|
+
if (region === "ring") schedule(pickHue(x, y), colRef.current.sat, colRef.current.lit);
|
|
1110
2277
|
else {
|
|
1111
2278
|
const [s, l] = pickSL(x, y);
|
|
1112
|
-
|
|
2279
|
+
schedule(colRef.current.hue, s, l);
|
|
1113
2280
|
}
|
|
1114
2281
|
const onMove = (ev) => {
|
|
1115
2282
|
const rr = cvsRef.current.getBoundingClientRect();
|
|
1116
2283
|
const mx = ev.clientX - rr.left, my = ev.clientY - rr.top;
|
|
1117
2284
|
if (region === "ring") {
|
|
1118
2285
|
const d = Math.sqrt((mx - CX) ** 2 + (my - CX) ** 2);
|
|
1119
|
-
if (d >= 72 && d <= 116)
|
|
2286
|
+
if (d >= 72 && d <= 116) schedule(pickHue(mx, my), colRef.current.sat, colRef.current.lit);
|
|
1120
2287
|
} else {
|
|
1121
2288
|
const [s, l] = pickSL(mx, my);
|
|
1122
|
-
|
|
2289
|
+
schedule(colRef.current.hue, s, l);
|
|
1123
2290
|
}
|
|
1124
2291
|
};
|
|
1125
2292
|
const onUp = () => {
|
|
@@ -1128,17 +2295,18 @@ window.__ModuleLoader__.load({
|
|
|
1128
2295
|
};
|
|
1129
2296
|
document.addEventListener("mousemove", onMove);
|
|
1130
2297
|
document.addEventListener("mouseup", onUp);
|
|
1131
|
-
}, [
|
|
2298
|
+
}, [schedule]);
|
|
1132
2299
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
|
|
1133
2300
|
ref: cvsRef,
|
|
1134
2301
|
width: WHEEL_SIZE,
|
|
1135
2302
|
height: WHEEL_SIZE,
|
|
1136
|
-
|
|
2303
|
+
className: "dab-wheel",
|
|
1137
2304
|
onMouseDown: onDown
|
|
1138
2305
|
});
|
|
1139
|
-
}
|
|
2306
|
+
});
|
|
1140
2307
|
//#endregion
|
|
1141
2308
|
//#region src/client/components/ColorInputs.tsx
|
|
2309
|
+
const SEG_W$1 = 66;
|
|
1142
2310
|
function clamp(v, min, max) {
|
|
1143
2311
|
return Math.min(max, Math.max(min, v));
|
|
1144
2312
|
}
|
|
@@ -1156,9 +2324,9 @@ window.__ModuleLoader__.load({
|
|
|
1156
2324
|
if (!focused.current) setText(String(value));
|
|
1157
2325
|
}, [value]);
|
|
1158
2326
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1159
|
-
|
|
2327
|
+
className: "dab-field",
|
|
1160
2328
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1161
|
-
|
|
2329
|
+
className: "dab-field-label",
|
|
1162
2330
|
children: label
|
|
1163
2331
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1164
2332
|
type: "number",
|
|
@@ -1166,7 +2334,7 @@ window.__ModuleLoader__.load({
|
|
|
1166
2334
|
max,
|
|
1167
2335
|
step,
|
|
1168
2336
|
value: text,
|
|
1169
|
-
|
|
2337
|
+
className: "dab-num",
|
|
1170
2338
|
onFocus: () => {
|
|
1171
2339
|
focused.current = true;
|
|
1172
2340
|
},
|
|
@@ -1183,7 +2351,7 @@ window.__ModuleLoader__.load({
|
|
|
1183
2351
|
});
|
|
1184
2352
|
}
|
|
1185
2353
|
/**
|
|
1186
|
-
* Precise color entry next to the wheel: a HSL/RGB
|
|
2354
|
+
* Precise color entry next to the wheel: a HSL/RGB segmented toggle plus three
|
|
1187
2355
|
* numeric channel fields and a live swatch. The wheel is HSV end-to-end, so
|
|
1188
2356
|
* this panel converts at the boundary — HSL fields map straight onto the
|
|
1189
2357
|
* stored HSL, RGB fields round-trip through rgbToHsl — and both emit HSV via
|
|
@@ -1199,30 +2367,36 @@ window.__ModuleLoader__.load({
|
|
|
1199
2367
|
onChange(...hslToHsv(nh, ns, nl));
|
|
1200
2368
|
};
|
|
1201
2369
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1202
|
-
|
|
2370
|
+
className: "dab-inputs",
|
|
1203
2371
|
children: [
|
|
1204
2372
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
},
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
2373
|
+
className: "dab-seg",
|
|
2374
|
+
style: { "--w": `${SEG_W$1}px` },
|
|
2375
|
+
children: [
|
|
2376
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2377
|
+
className: "dab-seg-thumb",
|
|
2378
|
+
style: { transform: `translateX(${mode === "hsl" ? 0 : SEG_W$1}px)` }
|
|
2379
|
+
}),
|
|
2380
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2381
|
+
type: "button",
|
|
2382
|
+
className: `dab-seg-item${mode === "hsl" ? " is-active" : ""}`,
|
|
2383
|
+
onClick: () => setMode("hsl"),
|
|
2384
|
+
children: "HSL"
|
|
2385
|
+
}),
|
|
2386
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2387
|
+
type: "button",
|
|
2388
|
+
className: `dab-seg-item${mode === "rgb" ? " is-active" : ""}`,
|
|
2389
|
+
onClick: () => setMode("rgb"),
|
|
2390
|
+
children: "RGB"
|
|
2391
|
+
})
|
|
2392
|
+
]
|
|
1223
2393
|
}),
|
|
1224
2394
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1225
|
-
style:
|
|
2395
|
+
style: {
|
|
2396
|
+
display: "flex",
|
|
2397
|
+
flexDirection: "column",
|
|
2398
|
+
gap: 7
|
|
2399
|
+
},
|
|
1226
2400
|
children: mode === "hsl" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1227
2401
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumField, {
|
|
1228
2402
|
label: "H",
|
|
@@ -1275,10 +2449,10 @@ window.__ModuleLoader__.load({
|
|
|
1275
2449
|
})
|
|
1276
2450
|
] })
|
|
1277
2451
|
}),
|
|
1278
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1279
|
-
|
|
1280
|
-
background: `hsl(${Math.round(h)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%)`
|
|
1281
|
-
}
|
|
2452
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2453
|
+
className: "dab-swatch-lg",
|
|
2454
|
+
style: { background: `hsl(${Math.round(h)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%)` }
|
|
2455
|
+
})
|
|
1282
2456
|
]
|
|
1283
2457
|
});
|
|
1284
2458
|
}
|
|
@@ -1286,7 +2460,7 @@ window.__ModuleLoader__.load({
|
|
|
1286
2460
|
//#region src/client/components/ColorPicker.tsx
|
|
1287
2461
|
const MAG_SIZE = 96;
|
|
1288
2462
|
const MAG_ZOOM = 8;
|
|
1289
|
-
function toHex(rgb) {
|
|
2463
|
+
function toHex$1(rgb) {
|
|
1290
2464
|
return "#" + rgb.map((v) => v.toString(16).padStart(2, "0")).join("");
|
|
1291
2465
|
}
|
|
1292
2466
|
/**
|
|
@@ -1383,22 +2557,36 @@ window.__ModuleLoader__.load({
|
|
|
1383
2557
|
const [h, sl, l] = rgbToHsl(s.rgb[0], s.rgb[1], s.rgb[2]);
|
|
1384
2558
|
onPick(hslToHsv(h, sl, l));
|
|
1385
2559
|
};
|
|
1386
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1387
|
-
|
|
2560
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2561
|
+
className: "dab-overlay",
|
|
1388
2562
|
children: [
|
|
1389
2563
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1390
|
-
|
|
2564
|
+
className: "dab-overlay-title",
|
|
1391
2565
|
children: t("pickerTitle")
|
|
1392
2566
|
}),
|
|
1393
2567
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1394
|
-
|
|
2568
|
+
className: "dab-overlay-hint",
|
|
1395
2569
|
children: t("pickerHint")
|
|
1396
2570
|
}),
|
|
1397
2571
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1398
|
-
|
|
2572
|
+
className: "dab-modal-card",
|
|
2573
|
+
style: {
|
|
2574
|
+
maxWidth: "min(90vw, 720px)",
|
|
2575
|
+
maxHeight: "60vh",
|
|
2576
|
+
overflow: "hidden",
|
|
2577
|
+
borderRadius: 12,
|
|
2578
|
+
border: "2px solid rgba(255,255,255,0.3)",
|
|
2579
|
+
background: "#000",
|
|
2580
|
+
cursor: "crosshair"
|
|
2581
|
+
},
|
|
1399
2582
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
|
|
1400
2583
|
ref: canvasRef,
|
|
1401
|
-
style:
|
|
2584
|
+
style: {
|
|
2585
|
+
display: "block",
|
|
2586
|
+
maxWidth: "100%",
|
|
2587
|
+
maxHeight: "60vh",
|
|
2588
|
+
objectFit: "contain"
|
|
2589
|
+
},
|
|
1402
2590
|
onMouseMove: onMove,
|
|
1403
2591
|
onMouseLeave: () => {
|
|
1404
2592
|
setHover(null);
|
|
@@ -1408,20 +2596,35 @@ window.__ModuleLoader__.load({
|
|
|
1408
2596
|
})
|
|
1409
2597
|
}),
|
|
1410
2598
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1411
|
-
style:
|
|
2599
|
+
style: {
|
|
2600
|
+
display: "flex",
|
|
2601
|
+
alignItems: "center",
|
|
2602
|
+
gap: 10,
|
|
2603
|
+
minWidth: 260
|
|
2604
|
+
},
|
|
1412
2605
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
1413
|
-
|
|
1414
|
-
|
|
2606
|
+
width: 28,
|
|
2607
|
+
height: 28,
|
|
2608
|
+
borderRadius: 8,
|
|
2609
|
+
border: "1px solid rgba(255,255,255,0.4)",
|
|
2610
|
+
background: hover ? toHex$1(hover.rgb) : "transparent"
|
|
1415
2611
|
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1416
|
-
style:
|
|
1417
|
-
|
|
2612
|
+
style: {
|
|
2613
|
+
color: "#fff",
|
|
2614
|
+
fontSize: 13,
|
|
2615
|
+
fontFamily: "var(--dab-mono, monospace)"
|
|
2616
|
+
},
|
|
2617
|
+
children: hover ? `${toHex$1(hover.rgb)} · rgb(${hover.rgb.join(", ")})` : "—"
|
|
1418
2618
|
})]
|
|
1419
2619
|
}),
|
|
1420
2620
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1421
|
-
style:
|
|
2621
|
+
style: {
|
|
2622
|
+
display: "flex",
|
|
2623
|
+
gap: 10
|
|
2624
|
+
},
|
|
1422
2625
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1423
2626
|
type: "button",
|
|
1424
|
-
|
|
2627
|
+
className: "dab-btn",
|
|
1425
2628
|
onClick: onClose,
|
|
1426
2629
|
children: t("pickerClose")
|
|
1427
2630
|
})
|
|
@@ -1431,16 +2634,447 @@ window.__ModuleLoader__.load({
|
|
|
1431
2634
|
width: MAG_SIZE,
|
|
1432
2635
|
height: MAG_SIZE,
|
|
1433
2636
|
style: {
|
|
1434
|
-
|
|
2637
|
+
position: "fixed",
|
|
2638
|
+
width: MAG_SIZE,
|
|
2639
|
+
height: MAG_SIZE,
|
|
2640
|
+
borderRadius: "50%",
|
|
2641
|
+
border: "2px solid rgba(255,255,255,0.7)",
|
|
2642
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.5)",
|
|
2643
|
+
zIndex: 1e4,
|
|
2644
|
+
background: "#000",
|
|
2645
|
+
pointerEvents: "none",
|
|
2646
|
+
transition: "opacity 0.08s",
|
|
1435
2647
|
left: mag?.x ?? 0,
|
|
1436
2648
|
top: mag?.y ?? 0,
|
|
1437
2649
|
opacity: mag ? 1 : 0
|
|
1438
2650
|
}
|
|
1439
2651
|
})
|
|
1440
2652
|
]
|
|
2653
|
+
}) });
|
|
2654
|
+
}
|
|
2655
|
+
//#endregion
|
|
2656
|
+
//#region src/client/components/pages/ColorPage.tsx
|
|
2657
|
+
/** Curated quick-pick hues (HSL, s .72 l .55). */
|
|
2658
|
+
const SWATCHES = [
|
|
2659
|
+
[
|
|
2660
|
+
356,
|
|
2661
|
+
.72,
|
|
2662
|
+
.55
|
|
2663
|
+
],
|
|
2664
|
+
[
|
|
2665
|
+
24,
|
|
2666
|
+
.78,
|
|
2667
|
+
.55
|
|
2668
|
+
],
|
|
2669
|
+
[
|
|
2670
|
+
44,
|
|
2671
|
+
.8,
|
|
2672
|
+
.55
|
|
2673
|
+
],
|
|
2674
|
+
[
|
|
2675
|
+
152,
|
|
2676
|
+
.62,
|
|
2677
|
+
.5
|
|
2678
|
+
],
|
|
2679
|
+
[
|
|
2680
|
+
174,
|
|
2681
|
+
.68,
|
|
2682
|
+
.48
|
|
2683
|
+
],
|
|
2684
|
+
[
|
|
2685
|
+
208,
|
|
2686
|
+
.72,
|
|
2687
|
+
.55
|
|
2688
|
+
],
|
|
2689
|
+
[
|
|
2690
|
+
252,
|
|
2691
|
+
.68,
|
|
2692
|
+
.6
|
|
2693
|
+
],
|
|
2694
|
+
[
|
|
2695
|
+
300,
|
|
2696
|
+
.64,
|
|
2697
|
+
.58
|
|
2698
|
+
]
|
|
2699
|
+
];
|
|
2700
|
+
function toHex(rgb) {
|
|
2701
|
+
return "#" + rgb.map((v) => Math.round(v).toString(16).padStart(2, "0")).join("");
|
|
2702
|
+
}
|
|
2703
|
+
function ColorPage({ p, notify }) {
|
|
2704
|
+
const { t, hue, sat, lit, setColor, extractColor, useStore } = p;
|
|
2705
|
+
const store = useStore((s) => s);
|
|
2706
|
+
const storeUrl = store.url;
|
|
2707
|
+
const [pickerOpen, setPickerOpen] = (0, react.useState)(false);
|
|
2708
|
+
const [extracting, setExtracting] = (0, react.useState)(false);
|
|
2709
|
+
const wheel = store.color ?? [
|
|
2710
|
+
hue,
|
|
2711
|
+
sat,
|
|
2712
|
+
lit
|
|
2713
|
+
];
|
|
2714
|
+
const [h, s, l] = hsvToHsl(wheel[0], wheel[1], wheel[2]);
|
|
2715
|
+
const [r, g, b] = hslToRgb(h, s, l);
|
|
2716
|
+
const hex = toHex([
|
|
2717
|
+
r,
|
|
2718
|
+
g,
|
|
2719
|
+
b
|
|
2720
|
+
]);
|
|
2721
|
+
const orbVars = {
|
|
2722
|
+
"--c": hex,
|
|
2723
|
+
"--c-soft": `rgba(${Math.round(r)},${Math.round(g)},${Math.round(b)},0.5)`
|
|
2724
|
+
};
|
|
2725
|
+
const onExtract = async () => {
|
|
2726
|
+
if (!storeUrl || extracting) return;
|
|
2727
|
+
setExtracting(true);
|
|
2728
|
+
try {
|
|
2729
|
+
const ok = await extractColor();
|
|
2730
|
+
notify(ok ? t("extractDone") : t("extractFail"), ok);
|
|
2731
|
+
} catch {
|
|
2732
|
+
notify(t("extractFail"), false);
|
|
2733
|
+
} finally {
|
|
2734
|
+
setExtracting(false);
|
|
2735
|
+
}
|
|
2736
|
+
};
|
|
2737
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2738
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
2739
|
+
className: "dab-head dab-rise",
|
|
2740
|
+
style: { "--d": 0 },
|
|
2741
|
+
children: [
|
|
2742
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2743
|
+
className: "dab-overline",
|
|
2744
|
+
children: "Accent"
|
|
2745
|
+
}),
|
|
2746
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2747
|
+
className: "dab-h1",
|
|
2748
|
+
children: t("colorTitle")
|
|
2749
|
+
}),
|
|
2750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2751
|
+
className: "dab-desc",
|
|
2752
|
+
children: t("descColor")
|
|
2753
|
+
})
|
|
2754
|
+
]
|
|
2755
|
+
}),
|
|
2756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2757
|
+
className: "dab-card dab-card-hover dab-hero-accent dab-rise",
|
|
2758
|
+
style: { "--d": 1 },
|
|
2759
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2760
|
+
className: "dab-orb-wrap",
|
|
2761
|
+
style: orbVars,
|
|
2762
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "dab-orb-ring" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "dab-orb" })]
|
|
2763
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2764
|
+
style: {
|
|
2765
|
+
flex: 1,
|
|
2766
|
+
minWidth: 200
|
|
2767
|
+
},
|
|
2768
|
+
children: [
|
|
2769
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2770
|
+
className: "dab-hex-caption",
|
|
2771
|
+
children: t("hexCaption")
|
|
2772
|
+
}),
|
|
2773
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2774
|
+
className: "dab-hex",
|
|
2775
|
+
children: hex.toUpperCase()
|
|
2776
|
+
}),
|
|
2777
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2778
|
+
className: "dab-hsl-row",
|
|
2779
|
+
children: [
|
|
2780
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["H ", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [Math.round(h), "°"] })] }),
|
|
2781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["S ", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [Math.round(s * 100), "%"] })] }),
|
|
2782
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["L ", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [Math.round(l * 100), "%"] })] })
|
|
2783
|
+
]
|
|
2784
|
+
}),
|
|
2785
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2786
|
+
className: "dab-chip-row",
|
|
2787
|
+
style: { marginTop: 14 },
|
|
2788
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2789
|
+
type: "button",
|
|
2790
|
+
className: "dab-btn",
|
|
2791
|
+
disabled: !storeUrl || extracting,
|
|
2792
|
+
onClick: onExtract,
|
|
2793
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SparkleIcon, { size: 14 }), extracting ? t("extracting") : t("extractColor")]
|
|
2794
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2795
|
+
type: "button",
|
|
2796
|
+
className: "dab-btn",
|
|
2797
|
+
disabled: !storeUrl,
|
|
2798
|
+
onClick: () => setPickerOpen(true),
|
|
2799
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PipetteIcon, { size: 14 }), t("eyedropper")]
|
|
2800
|
+
})]
|
|
2801
|
+
})
|
|
2802
|
+
]
|
|
2803
|
+
})]
|
|
2804
|
+
}),
|
|
2805
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2806
|
+
className: "dab-card dab-wheel-card dab-rise",
|
|
2807
|
+
style: { "--d": 2 },
|
|
2808
|
+
children: [
|
|
2809
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2810
|
+
className: "dab-wheel-glow",
|
|
2811
|
+
style: { background: hex }
|
|
2812
|
+
}),
|
|
2813
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorWheel, {
|
|
2814
|
+
hue: wheel[0],
|
|
2815
|
+
sat: wheel[1],
|
|
2816
|
+
lit: wheel[2],
|
|
2817
|
+
onChange: setColor
|
|
2818
|
+
}),
|
|
2819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorInputs, {
|
|
2820
|
+
hue: wheel[0],
|
|
2821
|
+
sat: wheel[1],
|
|
2822
|
+
lit: wheel[2],
|
|
2823
|
+
onChange: setColor
|
|
2824
|
+
})
|
|
2825
|
+
]
|
|
2826
|
+
}),
|
|
2827
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2828
|
+
className: "dab-hint dab-rise",
|
|
2829
|
+
style: { "--d": 3 },
|
|
2830
|
+
children: t("colorHint")
|
|
2831
|
+
}),
|
|
2832
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2833
|
+
className: "dab-card dab-card-hover dab-rise",
|
|
2834
|
+
style: { "--d": 3 },
|
|
2835
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2836
|
+
className: "dab-swatch-title",
|
|
2837
|
+
children: t("swatchTitle")
|
|
2838
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2839
|
+
className: "dab-swatches",
|
|
2840
|
+
children: SWATCHES.map(([sh, ss, sl], i) => {
|
|
2841
|
+
const on = Math.abs((wheel[0] - sh + 540) % 360 - 180) < 3 && Math.abs(s - ss) < .05 && Math.abs(l - sl) < .05;
|
|
2842
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2843
|
+
type: "button",
|
|
2844
|
+
className: `dab-swatch${on ? " is-on" : ""}`,
|
|
2845
|
+
style: { background: `hsl(${sh} ${Math.round(ss * 100)}% ${Math.round(sl * 100)}%)` },
|
|
2846
|
+
title: toHex(hslToRgb(sh, ss, sl)).toUpperCase(),
|
|
2847
|
+
onClick: () => setColor(...hslToHsv(sh, ss, sl))
|
|
2848
|
+
}, i);
|
|
2849
|
+
})
|
|
2850
|
+
})]
|
|
2851
|
+
}),
|
|
2852
|
+
pickerOpen && storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorPicker, {
|
|
2853
|
+
url: storeUrl,
|
|
2854
|
+
t,
|
|
2855
|
+
onClose: () => setPickerOpen(false),
|
|
2856
|
+
onPick: (hsv) => {
|
|
2857
|
+
setColor(hsv[0], hsv[1], hsv[2]);
|
|
2858
|
+
setPickerOpen(false);
|
|
2859
|
+
}
|
|
2860
|
+
}) : null
|
|
2861
|
+
] });
|
|
2862
|
+
}
|
|
2863
|
+
//#endregion
|
|
2864
|
+
//#region src/client/components/LiveSlider.tsx
|
|
2865
|
+
/**
|
|
2866
|
+
* Zero-lag slider: the thumb and value label update through DOM refs while
|
|
2867
|
+
* dragging (onInput) so the caller can mutate the live UI directly without a
|
|
2868
|
+
* React re-render; onChange commits the settled value. Double-click resets to
|
|
2869
|
+
* the canonical default. The track fill is a gradient driven by the --pct
|
|
2870
|
+
* custom property, updated imperatively alongside the thumb.
|
|
2871
|
+
*/
|
|
2872
|
+
function LiveSlider({ min, max, step, def, fmt, label, onInput, onChange }) {
|
|
2873
|
+
const inputRef = (0, react.useRef)(null);
|
|
2874
|
+
const valRef = (0, react.useRef)(null);
|
|
2875
|
+
const paint = (el, v) => {
|
|
2876
|
+
el.style.setProperty("--pct", String((v - min) / (max - min) * 100));
|
|
2877
|
+
};
|
|
2878
|
+
(0, react.useEffect)(() => {
|
|
2879
|
+
if (inputRef.current) {
|
|
2880
|
+
inputRef.current.value = String(def);
|
|
2881
|
+
paint(inputRef.current, def);
|
|
2882
|
+
}
|
|
2883
|
+
if (valRef.current) valRef.current.textContent = fmt(def);
|
|
2884
|
+
}, [
|
|
2885
|
+
def,
|
|
2886
|
+
fmt,
|
|
2887
|
+
min,
|
|
2888
|
+
max
|
|
2889
|
+
]);
|
|
2890
|
+
const apply = (v) => {
|
|
2891
|
+
if (inputRef.current) {
|
|
2892
|
+
inputRef.current.value = String(v);
|
|
2893
|
+
paint(inputRef.current, v);
|
|
2894
|
+
}
|
|
2895
|
+
if (valRef.current) valRef.current.textContent = fmt(v);
|
|
2896
|
+
onInput(v);
|
|
2897
|
+
onChange(v);
|
|
2898
|
+
};
|
|
2899
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2900
|
+
className: "dab-slider-block",
|
|
2901
|
+
children: [label ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2902
|
+
className: "dab-slider-label",
|
|
2903
|
+
children: label
|
|
2904
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2905
|
+
style: {
|
|
2906
|
+
display: "flex",
|
|
2907
|
+
alignItems: "center",
|
|
2908
|
+
gap: 10
|
|
2909
|
+
},
|
|
2910
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2911
|
+
ref: inputRef,
|
|
2912
|
+
type: "range",
|
|
2913
|
+
className: "dab-slider",
|
|
2914
|
+
style: { "--pct": (def - min) / (max - min) * 100 },
|
|
2915
|
+
min,
|
|
2916
|
+
max,
|
|
2917
|
+
step,
|
|
2918
|
+
defaultValue: def,
|
|
2919
|
+
title: label ? `${label} · ${fmt(def)}` : fmt(def),
|
|
2920
|
+
onDoubleClick: () => apply(def),
|
|
2921
|
+
onInput: (e) => {
|
|
2922
|
+
const el = e.target;
|
|
2923
|
+
const v = Number(el.value);
|
|
2924
|
+
paint(el, v);
|
|
2925
|
+
onInput(v);
|
|
2926
|
+
if (valRef.current) valRef.current.textContent = fmt(v);
|
|
2927
|
+
},
|
|
2928
|
+
onChange: (e) => onChange(Number(e.target.value))
|
|
2929
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2930
|
+
ref: valRef,
|
|
2931
|
+
className: "dab-slider-val",
|
|
2932
|
+
children: fmt(def)
|
|
2933
|
+
})]
|
|
2934
|
+
})]
|
|
1441
2935
|
});
|
|
1442
2936
|
}
|
|
1443
2937
|
//#endregion
|
|
2938
|
+
//#region src/client/components/pages/InterfacePage.tsx
|
|
2939
|
+
const PARTS = [
|
|
2940
|
+
{
|
|
2941
|
+
opKey: "bg",
|
|
2942
|
+
labelKey: "uiOpacityBg",
|
|
2943
|
+
Icon: CanvasIcon
|
|
2944
|
+
},
|
|
2945
|
+
{
|
|
2946
|
+
opKey: "sidebar",
|
|
2947
|
+
labelKey: "uiOpacitySide",
|
|
2948
|
+
Icon: SidebarIcon
|
|
2949
|
+
},
|
|
2950
|
+
{
|
|
2951
|
+
opKey: "card",
|
|
2952
|
+
labelKey: "uiOpacityCard",
|
|
2953
|
+
Icon: ChatIcon
|
|
2954
|
+
},
|
|
2955
|
+
{
|
|
2956
|
+
isSettings: true,
|
|
2957
|
+
labelKey: "uiSop",
|
|
2958
|
+
Icon: GearIcon
|
|
2959
|
+
}
|
|
2960
|
+
];
|
|
2961
|
+
function InterfacePage({ p }) {
|
|
2962
|
+
const { t, setOps, setBlurs, setSop } = p;
|
|
2963
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
2964
|
+
className: "dab-head dab-rise",
|
|
2965
|
+
style: { "--d": 0 },
|
|
2966
|
+
children: [
|
|
2967
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2968
|
+
className: "dab-overline",
|
|
2969
|
+
children: "Surfaces"
|
|
2970
|
+
}),
|
|
2971
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2972
|
+
className: "dab-h1",
|
|
2973
|
+
children: t("uiTitle")
|
|
2974
|
+
}),
|
|
2975
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2976
|
+
className: "dab-desc",
|
|
2977
|
+
children: t("descInterface")
|
|
2978
|
+
})
|
|
2979
|
+
]
|
|
2980
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2981
|
+
className: "dab-grid-parts",
|
|
2982
|
+
children: PARTS.map((part, i) => {
|
|
2983
|
+
const { labelKey, Icon, isSettings } = part;
|
|
2984
|
+
const opKey = part.opKey;
|
|
2985
|
+
const blurKey = opKey ?? "settings";
|
|
2986
|
+
const opacity = isSettings ? rSop() : rOps()[opKey];
|
|
2987
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2988
|
+
className: "dab-card dab-card-hover dab-rise",
|
|
2989
|
+
style: { "--d": i + 1 },
|
|
2990
|
+
children: [
|
|
2991
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2992
|
+
className: "dab-part-head",
|
|
2993
|
+
children: [
|
|
2994
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2995
|
+
className: "dab-part-ico",
|
|
2996
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 16 })
|
|
2997
|
+
}),
|
|
2998
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2999
|
+
className: "dab-part-name",
|
|
3000
|
+
children: t(labelKey)
|
|
3001
|
+
}),
|
|
3002
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3003
|
+
className: "dab-part-badge",
|
|
3004
|
+
children: [Math.round(opacity * 100), "%"]
|
|
3005
|
+
})
|
|
3006
|
+
]
|
|
3007
|
+
}),
|
|
3008
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3009
|
+
label: t("uiOpacity"),
|
|
3010
|
+
min: 0,
|
|
3011
|
+
max: 100,
|
|
3012
|
+
step: 1,
|
|
3013
|
+
def: Math.round(opacity * 100),
|
|
3014
|
+
fmt: (v) => `${v}%`,
|
|
3015
|
+
onInput: (v) => {
|
|
3016
|
+
const op = v / 100;
|
|
3017
|
+
if (isSettings) {
|
|
3018
|
+
cfg.settingsOpacity = op;
|
|
3019
|
+
applySettingsOverrides(op);
|
|
3020
|
+
} else {
|
|
3021
|
+
const ops = { ...rOps() };
|
|
3022
|
+
ops[opKey] = op;
|
|
3023
|
+
cfg.opacities = ops;
|
|
3024
|
+
applyCustomTokens(ops);
|
|
3025
|
+
}
|
|
3026
|
+
saveConfig();
|
|
3027
|
+
},
|
|
3028
|
+
onChange: (v) => {
|
|
3029
|
+
const op = v / 100;
|
|
3030
|
+
if (isSettings) setSop(op);
|
|
3031
|
+
else {
|
|
3032
|
+
const ops = { ...rOps() };
|
|
3033
|
+
ops[opKey] = op;
|
|
3034
|
+
setOps(ops);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
}),
|
|
3038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3039
|
+
label: t("uiBlur"),
|
|
3040
|
+
min: 0,
|
|
3041
|
+
max: 60,
|
|
3042
|
+
step: 1,
|
|
3043
|
+
def: rBlurs()[blurKey],
|
|
3044
|
+
fmt: (v) => `${v}px`,
|
|
3045
|
+
onInput: (v) => {
|
|
3046
|
+
const blurs = { ...rBlurs() };
|
|
3047
|
+
blurs[blurKey] = v;
|
|
3048
|
+
cfg.blurs = blurs;
|
|
3049
|
+
setPartBlur(blurKey, v);
|
|
3050
|
+
saveConfig();
|
|
3051
|
+
},
|
|
3052
|
+
onChange: (v) => {
|
|
3053
|
+
const blurs = { ...rBlurs() };
|
|
3054
|
+
blurs[blurKey] = v;
|
|
3055
|
+
setBlurs(blurs);
|
|
3056
|
+
}
|
|
3057
|
+
})
|
|
3058
|
+
]
|
|
3059
|
+
}, blurKey);
|
|
3060
|
+
})
|
|
3061
|
+
})] });
|
|
3062
|
+
}
|
|
3063
|
+
//#endregion
|
|
3064
|
+
//#region src/client/utils/image.ts
|
|
3065
|
+
/**
|
|
3066
|
+
* Read a chosen image file as a data URL WITHOUT re-encoding: the original
|
|
3067
|
+
* pixels are kept as-is (no canvas downscale / JPEG re-compression), so the
|
|
3068
|
+
* wallpaper is stored and displayed at full fidelity. The tradeoff is a larger
|
|
3069
|
+
* payload over the RPC channel and on disk for big images.
|
|
3070
|
+
*/
|
|
3071
|
+
function readImg(file, cb) {
|
|
3072
|
+
const r = new FileReader();
|
|
3073
|
+
r.onerror = () => cb(null);
|
|
3074
|
+
r.onload = () => cb(r.result);
|
|
3075
|
+
r.readAsDataURL(file);
|
|
3076
|
+
}
|
|
3077
|
+
//#endregion
|
|
1444
3078
|
//#region src/client/components/BgEditor.tsx
|
|
1445
3079
|
function BgEditor({ url, t, onClose, onCommit }) {
|
|
1446
3080
|
const pw = Math.min(window.innerWidth * .75, 860);
|
|
@@ -1527,7 +3161,7 @@ window.__ModuleLoader__.load({
|
|
|
1527
3161
|
if (!el) return;
|
|
1528
3162
|
const rect = el.getBoundingClientRect();
|
|
1529
3163
|
const mx = rect.width / 2, my = rect.height / 2;
|
|
1530
|
-
const factor = e.deltaY > 0 ? .
|
|
3164
|
+
const factor = e.deltaY > 0 ? .97 : 1.03;
|
|
1531
3165
|
const nz = Math.max(.1, Math.min(10, zoom * factor));
|
|
1532
3166
|
const nx = mx - (mx - pos.x) * (nz / zoom);
|
|
1533
3167
|
const ny = my - (my - pos.y) * (nz / zoom);
|
|
@@ -1555,20 +3189,26 @@ window.__ModuleLoader__.load({
|
|
|
1555
3189
|
ph,
|
|
1556
3190
|
imgSize
|
|
1557
3191
|
]);
|
|
1558
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1559
|
-
|
|
3192
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3193
|
+
className: "dab-overlay",
|
|
1560
3194
|
onClick: (e) => {
|
|
1561
3195
|
if (e.target === e.currentTarget) onClose();
|
|
1562
3196
|
},
|
|
1563
3197
|
children: [
|
|
1564
3198
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1565
|
-
|
|
3199
|
+
className: "dab-overlay-title",
|
|
1566
3200
|
children: t("editorTitle")
|
|
1567
3201
|
}),
|
|
1568
3202
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1569
3203
|
ref: containerRef,
|
|
3204
|
+
className: "dab-modal-card",
|
|
1570
3205
|
style: {
|
|
1571
|
-
|
|
3206
|
+
position: "relative",
|
|
3207
|
+
overflow: "hidden",
|
|
3208
|
+
border: "2px solid rgba(255,255,255,0.3)",
|
|
3209
|
+
borderRadius: 12,
|
|
3210
|
+
background: "#000",
|
|
3211
|
+
cursor: "grab",
|
|
1572
3212
|
width: pw,
|
|
1573
3213
|
height: ph
|
|
1574
3214
|
},
|
|
@@ -1579,7 +3219,9 @@ window.__ModuleLoader__.load({
|
|
|
1579
3219
|
alt: "",
|
|
1580
3220
|
draggable: false,
|
|
1581
3221
|
style: {
|
|
1582
|
-
|
|
3222
|
+
position: "absolute",
|
|
3223
|
+
transformOrigin: "0 0",
|
|
3224
|
+
pointerEvents: "none",
|
|
1583
3225
|
width: imgSize.w,
|
|
1584
3226
|
height: imgSize.h,
|
|
1585
3227
|
transform: `translate(${pos.x}px,${pos.y}px) scale(${zoom})`
|
|
@@ -1587,479 +3229,680 @@ window.__ModuleLoader__.load({
|
|
|
1587
3229
|
})
|
|
1588
3230
|
}),
|
|
1589
3231
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1590
|
-
|
|
3232
|
+
className: "dab-overlay-hint",
|
|
1591
3233
|
children: t("editorHint")
|
|
1592
3234
|
}),
|
|
1593
3235
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1594
|
-
style:
|
|
3236
|
+
style: {
|
|
3237
|
+
display: "flex",
|
|
3238
|
+
gap: 10
|
|
3239
|
+
},
|
|
1595
3240
|
children: [
|
|
1596
3241
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1597
|
-
|
|
3242
|
+
type: "button",
|
|
3243
|
+
className: "dab-btn",
|
|
1598
3244
|
onClick: resetView,
|
|
1599
3245
|
children: t("editorReset")
|
|
1600
3246
|
}),
|
|
1601
3247
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1602
|
-
|
|
3248
|
+
type: "button",
|
|
3249
|
+
className: "dab-btn",
|
|
1603
3250
|
onClick: onClose,
|
|
1604
3251
|
children: t("editorCancel")
|
|
1605
3252
|
}),
|
|
1606
3253
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
...ST.btnPrimary
|
|
1610
|
-
},
|
|
3254
|
+
type: "button",
|
|
3255
|
+
className: "dab-btn dab-btn-primary",
|
|
1611
3256
|
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),
|
|
1612
3257
|
children: t("editorCommit")
|
|
1613
3258
|
})
|
|
1614
3259
|
]
|
|
1615
3260
|
})
|
|
1616
3261
|
]
|
|
1617
|
-
});
|
|
3262
|
+
}) });
|
|
1618
3263
|
}
|
|
1619
3264
|
//#endregion
|
|
1620
|
-
//#region src/client/components/
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
onInput(v);
|
|
1640
|
-
if (valRef.current) valRef.current.textContent = fmt(v);
|
|
1641
|
-
},
|
|
1642
|
-
onChange: (e) => onChange(Number(e.target.value))
|
|
1643
|
-
}),
|
|
1644
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1645
|
-
ref: valRef,
|
|
1646
|
-
style: ST.sliderVal,
|
|
1647
|
-
children: fmt(def)
|
|
1648
|
-
})
|
|
1649
|
-
]
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
//#endregion
|
|
1653
|
-
//#region src/client/components/ErrorBoundary.tsx
|
|
1654
|
-
/**
|
|
1655
|
-
* Catches render errors from the theme section subtree (wheel, sliders, editor)
|
|
1656
|
-
* so a single bad state can never take down the whole settings panel. Shows a
|
|
1657
|
-
* compact fallback with a reset button; the reset re-renders the section with
|
|
1658
|
-
* the current saved config, which is enough to recover from most transient
|
|
1659
|
-
* failures (corrupt transient UI state, stale image decode, etc.).
|
|
1660
|
-
*/
|
|
1661
|
-
var ErrorBoundary = class extends react.Component {
|
|
1662
|
-
state = { error: null };
|
|
1663
|
-
static getDerivedStateFromError(error) {
|
|
1664
|
-
return { error };
|
|
1665
|
-
}
|
|
1666
|
-
componentDidCatch(error, info) {
|
|
1667
|
-
console.error("dsh-any-background: section render crashed", error, info.componentStack);
|
|
1668
|
-
}
|
|
1669
|
-
reset = () => {
|
|
1670
|
-
this.setState({ error: null });
|
|
1671
|
-
this.props.onReset?.();
|
|
3265
|
+
//#region src/client/components/pages/BackgroundPage.tsx
|
|
3266
|
+
const SEG_W = 108;
|
|
3267
|
+
function BackgroundPage({ p }) {
|
|
3268
|
+
const { t, setWp, setWop, setBl, setBgType, setGeneratedBg, regenerateBg, setRegenerateOnReload, useStore } = p;
|
|
3269
|
+
const store = useStore((s) => s);
|
|
3270
|
+
const storeUrl = store.url;
|
|
3271
|
+
const backgroundType = store.backgroundType;
|
|
3272
|
+
const generatedBg = store.generatedBg;
|
|
3273
|
+
const regenerateOnReload = store.regenerateOnReload;
|
|
3274
|
+
const fileRef = (0, react.useRef)(null);
|
|
3275
|
+
const [editorOpen, setEditorOpen] = (0, react.useState)(false);
|
|
3276
|
+
const [dragOver, setDragOver] = (0, react.useState)(false);
|
|
3277
|
+
const [spinTick, setSpinTick] = (0, react.useState)(0);
|
|
3278
|
+
const isGenerated = backgroundType !== "image";
|
|
3279
|
+
const activeGenType = isGenerated && generatedBg ? generatedBg.type : "mesh";
|
|
3280
|
+
const onFileSelect = (f) => {
|
|
3281
|
+
readImg(f, (d) => {
|
|
3282
|
+
if (d) setWp(d);
|
|
3283
|
+
});
|
|
1672
3284
|
};
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
3285
|
+
const onDrop = (e) => {
|
|
3286
|
+
e.preventDefault();
|
|
3287
|
+
setDragOver(false);
|
|
3288
|
+
const f = e.dataTransfer.files?.[0];
|
|
3289
|
+
if (f && f.type.startsWith("image/")) onFileSelect(f);
|
|
3290
|
+
};
|
|
3291
|
+
const switchToImage = () => {
|
|
3292
|
+
if (backgroundType !== "image") setBgType("image");
|
|
3293
|
+
};
|
|
3294
|
+
const setGenType = (type) => {
|
|
3295
|
+
if (type === activeGenType) return;
|
|
3296
|
+
setBgType(type);
|
|
3297
|
+
};
|
|
3298
|
+
const ensureGenParams = () => generatedBg ?? defaultParamsFor(activeGenType);
|
|
3299
|
+
const updateGenerated = (patch) => {
|
|
3300
|
+
setGeneratedBg({
|
|
3301
|
+
...ensureGenParams(),
|
|
3302
|
+
...patch
|
|
3303
|
+
});
|
|
3304
|
+
};
|
|
3305
|
+
const typeMeta = [
|
|
3306
|
+
{
|
|
3307
|
+
type: "mesh",
|
|
3308
|
+
labelKey: "bgTypeMesh",
|
|
3309
|
+
descKey: "bgMeshDesc",
|
|
3310
|
+
thumb: "dab-thumb-mesh"
|
|
3311
|
+
},
|
|
3312
|
+
{
|
|
3313
|
+
type: "shader",
|
|
3314
|
+
labelKey: "bgTypeShader",
|
|
3315
|
+
descKey: "bgShaderDesc",
|
|
3316
|
+
thumb: "dab-thumb-shader"
|
|
3317
|
+
},
|
|
3318
|
+
{
|
|
3319
|
+
type: "pattern",
|
|
3320
|
+
labelKey: "bgTypePattern",
|
|
3321
|
+
descKey: "bgPatternDesc",
|
|
3322
|
+
thumb: "dab-thumb-pattern"
|
|
3323
|
+
}
|
|
3324
|
+
];
|
|
3325
|
+
const presetLabel = (key) => {
|
|
3326
|
+
switch (key) {
|
|
3327
|
+
case "aurora": return t("presetAurora");
|
|
3328
|
+
case "nebula": return t("presetNebula");
|
|
3329
|
+
case "noise": return t("presetNoise");
|
|
3330
|
+
case "dots": return t("presetDots");
|
|
3331
|
+
case "waves": return t("presetWaves");
|
|
3332
|
+
case "poly": return t("presetPoly");
|
|
3333
|
+
default: return key;
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3337
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
3338
|
+
className: "dab-head dab-rise",
|
|
3339
|
+
style: { "--d": 0 },
|
|
1676
3340
|
children: [
|
|
1677
3341
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1678
|
-
|
|
1679
|
-
children:
|
|
3342
|
+
className: "dab-overline",
|
|
3343
|
+
children: "Canvas"
|
|
1680
3344
|
}),
|
|
1681
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1682
|
-
|
|
1683
|
-
children:
|
|
3345
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
3346
|
+
className: "dab-h1",
|
|
3347
|
+
children: t("bgTitle")
|
|
1684
3348
|
}),
|
|
1685
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
3349
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3350
|
+
className: "dab-desc",
|
|
3351
|
+
children: t("descBackground")
|
|
3352
|
+
})
|
|
3353
|
+
]
|
|
3354
|
+
}),
|
|
3355
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
3356
|
+
className: "dab-rise",
|
|
3357
|
+
style: { "--d": 1 },
|
|
3358
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3359
|
+
className: "dab-hero",
|
|
3360
|
+
children: storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3361
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
3362
|
+
className: "dab-hero-img",
|
|
3363
|
+
src: storeUrl,
|
|
3364
|
+
alt: "",
|
|
3365
|
+
draggable: false
|
|
3366
|
+
}),
|
|
3367
|
+
isGenerated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3368
|
+
className: "dab-hero-badge",
|
|
3369
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SparkleIcon, { size: 11 }), t("liveBadge")]
|
|
3370
|
+
}) : null,
|
|
3371
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3372
|
+
className: "dab-hero-veil",
|
|
3373
|
+
children: [backgroundType === "image" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3374
|
+
type: "button",
|
|
3375
|
+
className: "dab-btn",
|
|
3376
|
+
onClick: () => setEditorOpen(true),
|
|
3377
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditIcon, { size: 13 }), t("bgEdit")]
|
|
3378
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3379
|
+
type: "button",
|
|
3380
|
+
className: "dab-btn",
|
|
3381
|
+
onClick: () => {
|
|
3382
|
+
regenerateBg();
|
|
3383
|
+
setSpinTick((x) => x + 1);
|
|
3384
|
+
},
|
|
3385
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RefreshIcon, { size: 13 }), t("bgRegenerate")]
|
|
3386
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3387
|
+
type: "button",
|
|
3388
|
+
className: "dab-btn dab-btn-danger",
|
|
3389
|
+
onClick: () => setWp(null),
|
|
3390
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 13 }), t("bgRemove")]
|
|
3391
|
+
})]
|
|
3392
|
+
})
|
|
3393
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1686
3394
|
type: "button",
|
|
1687
|
-
|
|
1688
|
-
onClick:
|
|
1689
|
-
|
|
3395
|
+
className: `dab-hero-empty${dragOver ? " is-over" : ""}`,
|
|
3396
|
+
onClick: () => fileRef.current?.click(),
|
|
3397
|
+
onDragOver: (e) => {
|
|
3398
|
+
e.preventDefault();
|
|
3399
|
+
setDragOver(true);
|
|
3400
|
+
},
|
|
3401
|
+
onDragLeave: () => setDragOver(false),
|
|
3402
|
+
onDrop,
|
|
3403
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadIcon, { size: 20 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("dropHint") })]
|
|
3404
|
+
})
|
|
3405
|
+
})
|
|
3406
|
+
}),
|
|
3407
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
3408
|
+
className: "dab-rise",
|
|
3409
|
+
style: { "--d": 2 },
|
|
3410
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3411
|
+
className: "dab-seg",
|
|
3412
|
+
style: { "--w": `${SEG_W}px` },
|
|
3413
|
+
children: [
|
|
3414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3415
|
+
className: "dab-seg-thumb",
|
|
3416
|
+
style: { transform: `translateX(${isGenerated ? SEG_W : 0}px)` }
|
|
3417
|
+
}),
|
|
3418
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3419
|
+
type: "button",
|
|
3420
|
+
className: `dab-seg-item${!isGenerated ? " is-active" : ""}`,
|
|
3421
|
+
onClick: switchToImage,
|
|
3422
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PhotoIcon, { size: 14 }), t("bgSourceImage")]
|
|
3423
|
+
}),
|
|
3424
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3425
|
+
type: "button",
|
|
3426
|
+
className: `dab-seg-item${isGenerated ? " is-active" : ""}`,
|
|
3427
|
+
onClick: () => {
|
|
3428
|
+
if (!isGenerated) setBgType(generatedBg?.type ?? "mesh");
|
|
3429
|
+
},
|
|
3430
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SparkleIcon, { size: 14 }), t("bgSourceGenerated")]
|
|
3431
|
+
})
|
|
3432
|
+
]
|
|
3433
|
+
})
|
|
3434
|
+
}),
|
|
3435
|
+
!isGenerated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
3436
|
+
className: "dab-card dab-card-hover dab-rise",
|
|
3437
|
+
style: { "--d": 3 },
|
|
3438
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3439
|
+
className: "dab-chip-row",
|
|
3440
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3441
|
+
type: "button",
|
|
3442
|
+
className: "dab-btn dab-btn-primary",
|
|
3443
|
+
onClick: () => fileRef.current?.click(),
|
|
3444
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadIcon, { size: 14 }), t("bgChoose")]
|
|
3445
|
+
}), storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3446
|
+
type: "button",
|
|
3447
|
+
className: "dab-btn",
|
|
3448
|
+
onClick: () => setEditorOpen(true),
|
|
3449
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditIcon, { size: 14 }), t("bgEdit")]
|
|
3450
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3451
|
+
type: "button",
|
|
3452
|
+
className: "dab-btn dab-btn-ghost dab-btn-danger",
|
|
3453
|
+
onClick: () => setWp(null),
|
|
3454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 14 }), t("bgRemove")]
|
|
3455
|
+
})] }) : null]
|
|
3456
|
+
})
|
|
3457
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3458
|
+
className: "dab-card dab-rise",
|
|
3459
|
+
style: { "--d": 3 },
|
|
3460
|
+
children: [
|
|
3461
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3462
|
+
className: "dab-types",
|
|
3463
|
+
children: typeMeta.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3464
|
+
type: "button",
|
|
3465
|
+
className: `dab-type${activeGenType === m.type ? " is-active" : ""}`,
|
|
3466
|
+
onClick: () => setGenType(m.type),
|
|
3467
|
+
children: [
|
|
3468
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: `dab-type-thumb ${m.thumb}` }),
|
|
3469
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3470
|
+
className: "dab-type-name",
|
|
3471
|
+
children: t(m.labelKey)
|
|
3472
|
+
}),
|
|
3473
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3474
|
+
className: "dab-type-desc",
|
|
3475
|
+
children: t(m.descKey)
|
|
3476
|
+
}),
|
|
3477
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3478
|
+
className: "dab-type-check",
|
|
3479
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, { size: 11 })
|
|
3480
|
+
})
|
|
3481
|
+
]
|
|
3482
|
+
}, m.type))
|
|
3483
|
+
}),
|
|
3484
|
+
activeGenType === "shader" && generatedBg?.type === "shader" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3485
|
+
style: { marginTop: 14 },
|
|
3486
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3487
|
+
className: "dab-swatch-title",
|
|
3488
|
+
children: t("bgShaderPreset")
|
|
3489
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3490
|
+
className: "dab-chip-row",
|
|
3491
|
+
children: [
|
|
3492
|
+
"aurora",
|
|
3493
|
+
"nebula",
|
|
3494
|
+
"noise"
|
|
3495
|
+
].map((pr) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3496
|
+
type: "button",
|
|
3497
|
+
className: `dab-chip${generatedBg.preset === pr ? " is-active" : ""}`,
|
|
3498
|
+
onClick: () => updateGenerated({ preset: pr }),
|
|
3499
|
+
children: presetLabel(pr)
|
|
3500
|
+
}, pr))
|
|
3501
|
+
})]
|
|
3502
|
+
}) : null,
|
|
3503
|
+
activeGenType === "pattern" && generatedBg?.type === "pattern" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3504
|
+
style: { marginTop: 14 },
|
|
3505
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3506
|
+
className: "dab-swatch-title",
|
|
3507
|
+
children: t("bgPatternPreset")
|
|
3508
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3509
|
+
className: "dab-chip-row",
|
|
3510
|
+
children: [
|
|
3511
|
+
"dots",
|
|
3512
|
+
"waves",
|
|
3513
|
+
"poly"
|
|
3514
|
+
].map((pr) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3515
|
+
type: "button",
|
|
3516
|
+
className: `dab-chip${generatedBg.preset === pr ? " is-active" : ""}`,
|
|
3517
|
+
onClick: () => updateGenerated({ preset: pr }),
|
|
3518
|
+
children: presetLabel(pr)
|
|
3519
|
+
}, pr))
|
|
3520
|
+
})]
|
|
3521
|
+
}) : null,
|
|
3522
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3523
|
+
style: { marginTop: 16 },
|
|
3524
|
+
children: [
|
|
3525
|
+
activeGenType === "mesh" && generatedBg?.type === "mesh" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3526
|
+
label: t("bgMeshScale"),
|
|
3527
|
+
min: 30,
|
|
3528
|
+
max: 300,
|
|
3529
|
+
step: 1,
|
|
3530
|
+
def: Math.round(generatedBg.scale * 100),
|
|
3531
|
+
fmt: (v) => `${v}%`,
|
|
3532
|
+
onInput: () => {},
|
|
3533
|
+
onChange: (v) => updateGenerated({ scale: v / 100 })
|
|
3534
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3535
|
+
label: t("bgMeshIntensity"),
|
|
3536
|
+
min: 0,
|
|
3537
|
+
max: 100,
|
|
3538
|
+
step: 1,
|
|
3539
|
+
def: Math.round(generatedBg.intensity * 100),
|
|
3540
|
+
fmt: (v) => `${v}%`,
|
|
3541
|
+
onInput: () => {},
|
|
3542
|
+
onChange: (v) => updateGenerated({ intensity: v / 100 })
|
|
3543
|
+
})] }) : null,
|
|
3544
|
+
activeGenType === "shader" && generatedBg?.type === "shader" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3545
|
+
label: t("bgShaderSpeed"),
|
|
3546
|
+
min: 0,
|
|
3547
|
+
max: 200,
|
|
3548
|
+
step: 1,
|
|
3549
|
+
def: Math.round(generatedBg.speed * 100),
|
|
3550
|
+
fmt: (v) => `${v}%`,
|
|
3551
|
+
onInput: () => {},
|
|
3552
|
+
onChange: (v) => updateGenerated({ speed: v / 100 })
|
|
3553
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3554
|
+
label: t("bgShaderScale"),
|
|
3555
|
+
min: 30,
|
|
3556
|
+
max: 300,
|
|
3557
|
+
step: 1,
|
|
3558
|
+
def: Math.round(generatedBg.scale * 100),
|
|
3559
|
+
fmt: (v) => `${v}%`,
|
|
3560
|
+
onInput: () => {},
|
|
3561
|
+
onChange: (v) => updateGenerated({ scale: v / 100 })
|
|
3562
|
+
})] }) : null,
|
|
3563
|
+
activeGenType === "pattern" && generatedBg?.type === "pattern" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3564
|
+
label: t("bgPatternDensity"),
|
|
3565
|
+
min: 0,
|
|
3566
|
+
max: 100,
|
|
3567
|
+
step: 1,
|
|
3568
|
+
def: Math.round(generatedBg.density * 100),
|
|
3569
|
+
fmt: (v) => `${v}%`,
|
|
3570
|
+
onInput: () => {},
|
|
3571
|
+
onChange: (v) => updateGenerated({ density: v / 100 })
|
|
3572
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3573
|
+
label: t("bgPatternScale"),
|
|
3574
|
+
min: 30,
|
|
3575
|
+
max: 300,
|
|
3576
|
+
step: 1,
|
|
3577
|
+
def: Math.round(generatedBg.scale * 100),
|
|
3578
|
+
fmt: (v) => `${v}%`,
|
|
3579
|
+
onInput: () => {},
|
|
3580
|
+
onChange: (v) => updateGenerated({ scale: v / 100 })
|
|
3581
|
+
})] }) : null
|
|
3582
|
+
]
|
|
3583
|
+
}),
|
|
3584
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3585
|
+
className: "dab-seed",
|
|
3586
|
+
children: [
|
|
3587
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3588
|
+
className: "dab-seed-ico",
|
|
3589
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LockIcon, { size: 15 })
|
|
3590
|
+
}),
|
|
3591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3592
|
+
className: "dab-seed-txt",
|
|
3593
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3594
|
+
className: "dab-seed-title",
|
|
3595
|
+
children: t("seedLock")
|
|
3596
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3597
|
+
className: "dab-seed-desc",
|
|
3598
|
+
children: !regenerateOnReload ? t("bgSeedLocked") : t("bgSeedUnlocked")
|
|
3599
|
+
})]
|
|
3600
|
+
}),
|
|
3601
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3602
|
+
type: "button",
|
|
3603
|
+
className: `dab-toggle${!regenerateOnReload ? " is-on" : ""}`,
|
|
3604
|
+
role: "switch",
|
|
3605
|
+
"aria-checked": !regenerateOnReload,
|
|
3606
|
+
onClick: () => setRegenerateOnReload(!regenerateOnReload),
|
|
3607
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dab-toggle-knob" })
|
|
3608
|
+
}),
|
|
3609
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3610
|
+
type: "button",
|
|
3611
|
+
className: "dab-btn dab-btn-primary",
|
|
3612
|
+
onClick: () => {
|
|
3613
|
+
regenerateBg();
|
|
3614
|
+
setSpinTick((x) => x + 1);
|
|
3615
|
+
},
|
|
3616
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3617
|
+
className: "dab-spin",
|
|
3618
|
+
style: { display: "grid" },
|
|
3619
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RefreshIcon, { size: 13 })
|
|
3620
|
+
}, spinTick), t("bgRegenerate")]
|
|
3621
|
+
})
|
|
3622
|
+
]
|
|
1690
3623
|
})
|
|
1691
3624
|
]
|
|
1692
|
-
})
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
3625
|
+
}),
|
|
3626
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3627
|
+
className: "dab-card dab-card-hover dab-rise",
|
|
3628
|
+
style: { "--d": 4 },
|
|
3629
|
+
children: [
|
|
3630
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3631
|
+
label: t("wpOpacity"),
|
|
3632
|
+
min: 0,
|
|
3633
|
+
max: 100,
|
|
3634
|
+
step: 1,
|
|
3635
|
+
def: Math.round(rWop() * 100),
|
|
3636
|
+
fmt: (v) => `${v}%`,
|
|
3637
|
+
onInput: (v) => {
|
|
3638
|
+
const op = v / 100;
|
|
3639
|
+
cfg.wallpaperOpacity = op;
|
|
3640
|
+
setWpOpacity(op);
|
|
3641
|
+
saveConfig();
|
|
3642
|
+
},
|
|
3643
|
+
onChange: (v) => setWop(v / 100)
|
|
3644
|
+
}),
|
|
3645
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
3646
|
+
label: t("bgBlur"),
|
|
3647
|
+
min: 0,
|
|
3648
|
+
max: 60,
|
|
3649
|
+
step: 1,
|
|
3650
|
+
def: rBl(),
|
|
3651
|
+
fmt: (v) => `${v}px`,
|
|
3652
|
+
onInput: (v) => {
|
|
3653
|
+
cfg.blur = v;
|
|
3654
|
+
setWpBlur(v);
|
|
3655
|
+
saveConfig();
|
|
3656
|
+
},
|
|
3657
|
+
onChange: (v) => setBl(v)
|
|
3658
|
+
}),
|
|
3659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3660
|
+
className: "dab-hint",
|
|
3661
|
+
style: { marginTop: 12 },
|
|
3662
|
+
children: t("bgHint")
|
|
3663
|
+
})
|
|
3664
|
+
]
|
|
3665
|
+
}),
|
|
3666
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3667
|
+
ref: fileRef,
|
|
3668
|
+
type: "file",
|
|
3669
|
+
accept: "image/*",
|
|
3670
|
+
style: { display: "none" },
|
|
3671
|
+
onChange: (e) => {
|
|
3672
|
+
const f = e.target.files?.[0];
|
|
3673
|
+
if (!f) return;
|
|
3674
|
+
onFileSelect(f);
|
|
3675
|
+
e.target.value = "";
|
|
3676
|
+
}
|
|
3677
|
+
}),
|
|
3678
|
+
editorOpen && storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BgEditor, {
|
|
3679
|
+
url: storeUrl,
|
|
3680
|
+
t,
|
|
3681
|
+
onClose: () => setEditorOpen(false),
|
|
3682
|
+
onCommit: (z, x, y, iw, ih) => {
|
|
3683
|
+
cfg.bgState = {
|
|
3684
|
+
zoom: z,
|
|
3685
|
+
x,
|
|
3686
|
+
y,
|
|
3687
|
+
iw,
|
|
3688
|
+
ih
|
|
3689
|
+
};
|
|
3690
|
+
applyWp();
|
|
3691
|
+
saveConfig();
|
|
3692
|
+
setEditorOpen(false);
|
|
3693
|
+
}
|
|
3694
|
+
}) : null
|
|
3695
|
+
] });
|
|
3696
|
+
}
|
|
1696
3697
|
//#endregion
|
|
1697
|
-
//#region src/client/components/
|
|
1698
|
-
function
|
|
1699
|
-
const { t,
|
|
1700
|
-
const storeUrl = useStore((s) => s.url);
|
|
1701
|
-
const storeColor = useStore((s) => s.color);
|
|
1702
|
-
const fileRef = (0, react.useRef)(null);
|
|
3698
|
+
//#region src/client/components/pages/ProfilePage.tsx
|
|
3699
|
+
function ProfilePage({ p, notify }) {
|
|
3700
|
+
const { t, exportTheme, importTheme } = p;
|
|
1703
3701
|
const importRef = (0, react.useRef)(null);
|
|
1704
|
-
const [editorOpen, setEditorOpen] = (0, react.useState)(false);
|
|
1705
|
-
const [pickerOpen, setPickerOpen] = (0, react.useState)(false);
|
|
1706
|
-
const [extracting, setExtracting] = (0, react.useState)(false);
|
|
1707
|
-
const [extractMsg, setExtractMsg] = (0, react.useState)(null);
|
|
1708
|
-
const [importMsg, setImportMsg] = (0, react.useState)(null);
|
|
1709
|
-
const msgTimer = (0, react.useRef)(void 0);
|
|
1710
|
-
(0, react.useEffect)(() => () => window.clearTimeout(msgTimer.current), []);
|
|
1711
|
-
const showMsg = (setter, key) => {
|
|
1712
|
-
setter(t(key));
|
|
1713
|
-
window.clearTimeout(msgTimer.current);
|
|
1714
|
-
msgTimer.current = window.setTimeout(() => setter(null), 2500);
|
|
1715
|
-
};
|
|
1716
|
-
const onExtract = async () => {
|
|
1717
|
-
if (!storeUrl || extracting) return;
|
|
1718
|
-
setExtracting(true);
|
|
1719
|
-
try {
|
|
1720
|
-
showMsg(setExtractMsg, await extractColor() ? "extractDone" : "extractFail");
|
|
1721
|
-
} catch {
|
|
1722
|
-
showMsg(setExtractMsg, "extractFail");
|
|
1723
|
-
} finally {
|
|
1724
|
-
setExtracting(false);
|
|
1725
|
-
}
|
|
1726
|
-
};
|
|
1727
3702
|
const onImport = async (file) => {
|
|
1728
3703
|
try {
|
|
1729
|
-
|
|
3704
|
+
const ok = await importTheme(file);
|
|
3705
|
+
notify(ok ? t("importDone") : t("importFail"), ok);
|
|
1730
3706
|
} catch {
|
|
1731
|
-
|
|
3707
|
+
notify(t("importFail"), false);
|
|
1732
3708
|
}
|
|
1733
3709
|
};
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
];
|
|
1739
|
-
const blurSlider = (part) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
1740
|
-
label: t("uiBlur"),
|
|
1741
|
-
min: 0,
|
|
1742
|
-
max: 60,
|
|
1743
|
-
step: 1,
|
|
1744
|
-
def: rBlurs()[part],
|
|
1745
|
-
fmt: (v) => `${v}px`,
|
|
1746
|
-
onInput: (v) => {
|
|
1747
|
-
const blurs = { ...rBlurs() };
|
|
1748
|
-
blurs[part] = v;
|
|
1749
|
-
cfg.blurs = blurs;
|
|
1750
|
-
setPartBlur(part, v);
|
|
1751
|
-
saveConfig();
|
|
1752
|
-
},
|
|
1753
|
-
onChange: (v) => {
|
|
1754
|
-
const blurs = { ...rBlurs() };
|
|
1755
|
-
blurs[part] = v;
|
|
1756
|
-
setBlurs(blurs);
|
|
1757
|
-
}
|
|
1758
|
-
});
|
|
1759
|
-
const partSlider = (labelKey, part) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1760
|
-
style: ST.sliderBlock,
|
|
1761
|
-
children: [
|
|
1762
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1763
|
-
style: ST.sliderLabel,
|
|
1764
|
-
children: t(labelKey)
|
|
1765
|
-
}),
|
|
1766
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
1767
|
-
label: t("uiOpacity"),
|
|
1768
|
-
min: 0,
|
|
1769
|
-
max: 100,
|
|
1770
|
-
step: 1,
|
|
1771
|
-
def: Math.round(rOps()[part] * 100),
|
|
1772
|
-
fmt: (v) => `${v}%`,
|
|
1773
|
-
onInput: (v) => {
|
|
1774
|
-
const ops = { ...rOps() };
|
|
1775
|
-
ops[part] = v / 100;
|
|
1776
|
-
cfg.opacities = ops;
|
|
1777
|
-
applyCustomTokens(ops);
|
|
1778
|
-
saveConfig();
|
|
1779
|
-
},
|
|
1780
|
-
onChange: (v) => {
|
|
1781
|
-
const ops = { ...rOps() };
|
|
1782
|
-
ops[part] = v / 100;
|
|
1783
|
-
setOps(ops);
|
|
1784
|
-
}
|
|
1785
|
-
}),
|
|
1786
|
-
blurSlider(part)
|
|
1787
|
-
]
|
|
1788
|
-
});
|
|
1789
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary, {
|
|
1790
|
-
t,
|
|
1791
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1792
|
-
style: ST.root,
|
|
3710
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3711
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
3712
|
+
className: "dab-head dab-rise",
|
|
3713
|
+
style: { "--d": 0 },
|
|
1793
3714
|
children: [
|
|
1794
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1795
|
-
|
|
1796
|
-
children:
|
|
1797
|
-
style: ST.h2,
|
|
1798
|
-
children: t("nav")
|
|
1799
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1800
|
-
style: ST.sub,
|
|
1801
|
-
children: t("subtitle")
|
|
1802
|
-
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1803
|
-
style: ST.row,
|
|
1804
|
-
children: [
|
|
1805
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1806
|
-
type: "button",
|
|
1807
|
-
style: ST.btn,
|
|
1808
|
-
onClick: exportTheme,
|
|
1809
|
-
children: t("exportTheme")
|
|
1810
|
-
}),
|
|
1811
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1812
|
-
type: "button",
|
|
1813
|
-
style: ST.btn,
|
|
1814
|
-
onClick: () => importRef.current?.click(),
|
|
1815
|
-
children: t("importTheme")
|
|
1816
|
-
}),
|
|
1817
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1818
|
-
ref: importRef,
|
|
1819
|
-
type: "file",
|
|
1820
|
-
accept: "application/json,.json",
|
|
1821
|
-
style: { display: "none" },
|
|
1822
|
-
onChange: (e) => {
|
|
1823
|
-
const f = e.target.files?.[0];
|
|
1824
|
-
if (!f) return;
|
|
1825
|
-
onImport(f);
|
|
1826
|
-
e.target.value = "";
|
|
1827
|
-
}
|
|
1828
|
-
})
|
|
1829
|
-
]
|
|
1830
|
-
})]
|
|
3715
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3716
|
+
className: "dab-overline",
|
|
3717
|
+
children: "Profile"
|
|
1831
3718
|
}),
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
children:
|
|
1835
|
-
})
|
|
1836
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3719
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
3720
|
+
className: "dab-h1",
|
|
3721
|
+
children: t("pageProfile")
|
|
3722
|
+
}),
|
|
3723
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3724
|
+
className: "dab-desc",
|
|
3725
|
+
children: t("descProfile")
|
|
3726
|
+
})
|
|
3727
|
+
]
|
|
3728
|
+
}),
|
|
3729
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3730
|
+
className: "dab-profile-grid",
|
|
3731
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3732
|
+
className: "dab-card dab-card-hover dab-rise",
|
|
3733
|
+
style: { "--d": 1 },
|
|
3734
|
+
children: [
|
|
1837
3735
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1838
|
-
|
|
1839
|
-
children:
|
|
1840
|
-
}),
|
|
1841
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1842
|
-
style: ST.wheelRow,
|
|
1843
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorWheel, {
|
|
1844
|
-
hue: wheel[0],
|
|
1845
|
-
sat: wheel[1],
|
|
1846
|
-
lit: wheel[2],
|
|
1847
|
-
onChange: setColor
|
|
1848
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorInputs, {
|
|
1849
|
-
hue: wheel[0],
|
|
1850
|
-
sat: wheel[1],
|
|
1851
|
-
lit: wheel[2],
|
|
1852
|
-
onChange: setColor
|
|
1853
|
-
})]
|
|
3736
|
+
className: "dab-profile-ico",
|
|
3737
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadIcon, { size: 17 })
|
|
1854
3738
|
}),
|
|
1855
3739
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1856
|
-
|
|
1857
|
-
children: t("
|
|
3740
|
+
className: "dab-profile-title",
|
|
3741
|
+
children: t("exportCardTitle")
|
|
1858
3742
|
}),
|
|
1859
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1860
|
-
|
|
1861
|
-
children:
|
|
1862
|
-
type: "button",
|
|
1863
|
-
style: {
|
|
1864
|
-
...ST.btn,
|
|
1865
|
-
opacity: !storeUrl || extracting ? .55 : 1
|
|
1866
|
-
},
|
|
1867
|
-
title: !storeUrl ? t("extractNoWp") : void 0,
|
|
1868
|
-
disabled: !storeUrl || extracting,
|
|
1869
|
-
onClick: onExtract,
|
|
1870
|
-
children: extracting ? t("extracting") : t("extractColor")
|
|
1871
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1872
|
-
type: "button",
|
|
1873
|
-
style: {
|
|
1874
|
-
...ST.btn,
|
|
1875
|
-
opacity: !storeUrl ? .55 : 1
|
|
1876
|
-
},
|
|
1877
|
-
title: !storeUrl ? t("extractNoWp") : void 0,
|
|
1878
|
-
disabled: !storeUrl,
|
|
1879
|
-
onClick: () => setPickerOpen(true),
|
|
1880
|
-
children: t("eyedropper")
|
|
1881
|
-
})]
|
|
3743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3744
|
+
className: "dab-profile-desc",
|
|
3745
|
+
children: t("exportCardDesc")
|
|
1882
3746
|
}),
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
partSlider("uiOpacityCard", "card"),
|
|
1898
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1899
|
-
style: ST.sliderBlock,
|
|
1900
|
-
children: [
|
|
1901
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1902
|
-
style: ST.sliderLabel,
|
|
1903
|
-
children: t("uiSop")
|
|
1904
|
-
}),
|
|
1905
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
1906
|
-
label: t("uiOpacity"),
|
|
1907
|
-
min: 0,
|
|
1908
|
-
max: 100,
|
|
1909
|
-
step: 1,
|
|
1910
|
-
def: Math.round(rSop() * 100),
|
|
1911
|
-
fmt: (v) => `${v}%`,
|
|
1912
|
-
onInput: (v) => {
|
|
1913
|
-
const op = v / 100;
|
|
1914
|
-
cfg.settingsOpacity = op;
|
|
1915
|
-
applySettingsOverrides(op);
|
|
1916
|
-
saveConfig();
|
|
1917
|
-
},
|
|
1918
|
-
onChange: (v) => setSop(v / 100)
|
|
1919
|
-
}),
|
|
1920
|
-
blurSlider("settings")
|
|
1921
|
-
]
|
|
1922
|
-
})
|
|
1923
|
-
]
|
|
1924
|
-
})] }),
|
|
1925
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { style: ST.hr }),
|
|
1926
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
3747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3748
|
+
type: "button",
|
|
3749
|
+
className: "dab-btn dab-btn-primary",
|
|
3750
|
+
onClick: () => {
|
|
3751
|
+
exportTheme();
|
|
3752
|
+
notify(t("toastExportDone"));
|
|
3753
|
+
},
|
|
3754
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadIcon, { size: 14 }), t("exportTheme")]
|
|
3755
|
+
})
|
|
3756
|
+
]
|
|
3757
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3758
|
+
className: "dab-card dab-card-hover dab-rise",
|
|
3759
|
+
style: { "--d": 2 },
|
|
3760
|
+
children: [
|
|
1927
3761
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1928
|
-
|
|
1929
|
-
children:
|
|
3762
|
+
className: "dab-profile-ico",
|
|
3763
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadIcon, { size: 17 })
|
|
1930
3764
|
}),
|
|
1931
3765
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1932
|
-
|
|
1933
|
-
children:
|
|
1934
|
-
src: storeUrl,
|
|
1935
|
-
alt: "",
|
|
1936
|
-
style: ST.preview,
|
|
1937
|
-
onClick: () => setEditorOpen(true)
|
|
1938
|
-
}) : null
|
|
3766
|
+
className: "dab-profile-title",
|
|
3767
|
+
children: t("importCardTitle")
|
|
1939
3768
|
}),
|
|
1940
3769
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1941
|
-
|
|
1942
|
-
children:
|
|
1943
|
-
style: ST.row,
|
|
1944
|
-
children: [
|
|
1945
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1946
|
-
type: "button",
|
|
1947
|
-
style: ST.btn,
|
|
1948
|
-
onClick: () => fileRef.current?.click(),
|
|
1949
|
-
children: t("bgChoose")
|
|
1950
|
-
}),
|
|
1951
|
-
storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1952
|
-
type: "button",
|
|
1953
|
-
style: ST.btn,
|
|
1954
|
-
onClick: () => setEditorOpen(true),
|
|
1955
|
-
children: t("bgEdit")
|
|
1956
|
-
}) : null,
|
|
1957
|
-
storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1958
|
-
type: "button",
|
|
1959
|
-
style: {
|
|
1960
|
-
...ST.btn,
|
|
1961
|
-
...ST.btnDanger
|
|
1962
|
-
},
|
|
1963
|
-
onClick: () => setWp(null),
|
|
1964
|
-
children: t("bgRemove")
|
|
1965
|
-
}) : null,
|
|
1966
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1967
|
-
ref: fileRef,
|
|
1968
|
-
type: "file",
|
|
1969
|
-
accept: "image/*",
|
|
1970
|
-
style: { display: "none" },
|
|
1971
|
-
onChange: (e) => {
|
|
1972
|
-
const f = e.target.files?.[0];
|
|
1973
|
-
if (!f) return;
|
|
1974
|
-
readImg(f, (d) => {
|
|
1975
|
-
if (d) setWp(d);
|
|
1976
|
-
e.target.value = "";
|
|
1977
|
-
});
|
|
1978
|
-
}
|
|
1979
|
-
})
|
|
1980
|
-
]
|
|
1981
|
-
})
|
|
3770
|
+
className: "dab-profile-desc",
|
|
3771
|
+
children: t("importCardDesc")
|
|
1982
3772
|
}),
|
|
1983
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
style: ST.sliderLabel,
|
|
1989
|
-
children: t("wpOpacity")
|
|
1990
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
1991
|
-
min: 0,
|
|
1992
|
-
max: 100,
|
|
1993
|
-
step: 1,
|
|
1994
|
-
def: Math.round(rWop() * 100),
|
|
1995
|
-
fmt: (v) => `${v}%`,
|
|
1996
|
-
onInput: (v) => {
|
|
1997
|
-
const op = v / 100;
|
|
1998
|
-
cfg.wallpaperOpacity = op;
|
|
1999
|
-
setWpOpacity(op);
|
|
2000
|
-
saveConfig();
|
|
2001
|
-
},
|
|
2002
|
-
onChange: (v) => setWop(v / 100)
|
|
2003
|
-
})]
|
|
2004
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2005
|
-
style: ST.sliderBlock,
|
|
2006
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2007
|
-
style: ST.sliderLabel,
|
|
2008
|
-
children: t("bgBlur")
|
|
2009
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
2010
|
-
min: 0,
|
|
2011
|
-
max: 60,
|
|
2012
|
-
step: 1,
|
|
2013
|
-
def: rBl(),
|
|
2014
|
-
fmt: (v) => `${v}px`,
|
|
2015
|
-
onInput: (v) => {
|
|
2016
|
-
cfg.blur = v;
|
|
2017
|
-
setWpBlur(v);
|
|
2018
|
-
saveConfig();
|
|
2019
|
-
},
|
|
2020
|
-
onChange: (v) => setBl(v)
|
|
2021
|
-
})]
|
|
2022
|
-
})]
|
|
3773
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3774
|
+
type: "button",
|
|
3775
|
+
className: "dab-btn",
|
|
3776
|
+
onClick: () => importRef.current?.click(),
|
|
3777
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadIcon, { size: 14 }), t("importTheme")]
|
|
2023
3778
|
}),
|
|
2024
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
2025
|
-
|
|
2026
|
-
|
|
3779
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3780
|
+
ref: importRef,
|
|
3781
|
+
type: "file",
|
|
3782
|
+
accept: "application/json,.json",
|
|
3783
|
+
style: { display: "none" },
|
|
3784
|
+
onChange: (e) => {
|
|
3785
|
+
const f = e.target.files?.[0];
|
|
3786
|
+
if (!f) return;
|
|
3787
|
+
onImport(f);
|
|
3788
|
+
e.target.value = "";
|
|
3789
|
+
}
|
|
2027
3790
|
})
|
|
2028
|
-
]
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
editorOpen && storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BgEditor, {
|
|
2039
|
-
url: storeUrl,
|
|
2040
|
-
t,
|
|
2041
|
-
onClose: () => setEditorOpen(false),
|
|
2042
|
-
onCommit: (z, x, y, iw, ih) => {
|
|
2043
|
-
cfg.bgState = {
|
|
2044
|
-
zoom: z,
|
|
2045
|
-
x,
|
|
2046
|
-
y,
|
|
2047
|
-
iw,
|
|
2048
|
-
ih
|
|
2049
|
-
};
|
|
2050
|
-
applyWp();
|
|
2051
|
-
saveConfig();
|
|
2052
|
-
setEditorOpen(false);
|
|
2053
|
-
}
|
|
2054
|
-
}) : null
|
|
2055
|
-
]
|
|
3791
|
+
]
|
|
3792
|
+
})]
|
|
3793
|
+
}),
|
|
3794
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
3795
|
+
className: "dab-footer dab-rise",
|
|
3796
|
+
style: { "--d": 3 },
|
|
3797
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3798
|
+
className: "dab-footer-mono",
|
|
3799
|
+
children: "dsh-any-background"
|
|
3800
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("footerTag") })]
|
|
2056
3801
|
})
|
|
2057
|
-
});
|
|
3802
|
+
] });
|
|
2058
3803
|
}
|
|
2059
3804
|
//#endregion
|
|
2060
|
-
//#region src/client/components/
|
|
2061
|
-
/**
|
|
2062
|
-
|
|
3805
|
+
//#region src/client/components/ThemeSection.tsx
|
|
3806
|
+
/**
|
|
3807
|
+
* dsh-any-background — settings section shell.
|
|
3808
|
+
*
|
|
3809
|
+
* The host renders this section only while the settings dialog is open and the
|
|
3810
|
+
* section is the active nav entry, so mounting IS being visible. The section is
|
|
3811
|
+
* rendered INLINE inside the host settings dialog's content column (the host
|
|
3812
|
+
* provides the modal chrome); the shell is a left nav rail + page body. Only
|
|
3813
|
+
* the transient toast escapes through a Portal (the shell's container-type
|
|
3814
|
+
* containment would otherwise capture its fixed positioning).
|
|
3815
|
+
*/
|
|
3816
|
+
function ThemeSection(props) {
|
|
3817
|
+
ensureUiCss();
|
|
3818
|
+
const { t } = props;
|
|
3819
|
+
const [page, setPage] = (0, react.useState)(0);
|
|
3820
|
+
const [toast, setToast] = (0, react.useState)(null);
|
|
3821
|
+
const toastTimer = (0, react.useRef)(void 0);
|
|
3822
|
+
(0, react.useEffect)(() => () => window.clearTimeout(toastTimer.current), []);
|
|
3823
|
+
const notify = (msg, ok = true) => {
|
|
3824
|
+
setToast({
|
|
3825
|
+
msg,
|
|
3826
|
+
ok
|
|
3827
|
+
});
|
|
3828
|
+
window.clearTimeout(toastTimer.current);
|
|
3829
|
+
toastTimer.current = window.setTimeout(() => setToast(null), 2600);
|
|
3830
|
+
};
|
|
3831
|
+
const pages = [
|
|
3832
|
+
{
|
|
3833
|
+
label: t("pageColor"),
|
|
3834
|
+
Icon: DropletIcon,
|
|
3835
|
+
node: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColorPage, {
|
|
3836
|
+
p: props,
|
|
3837
|
+
notify
|
|
3838
|
+
})
|
|
3839
|
+
},
|
|
3840
|
+
{
|
|
3841
|
+
label: t("pageInterface"),
|
|
3842
|
+
Icon: LayersIcon,
|
|
3843
|
+
node: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InterfacePage, { p: props })
|
|
3844
|
+
},
|
|
3845
|
+
{
|
|
3846
|
+
label: t("pageBackground"),
|
|
3847
|
+
Icon: PhotoIcon,
|
|
3848
|
+
node: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BackgroundPage, { p: props })
|
|
3849
|
+
},
|
|
3850
|
+
{
|
|
3851
|
+
label: t("pageProfile"),
|
|
3852
|
+
Icon: SlidersIcon,
|
|
3853
|
+
node: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProfilePage, {
|
|
3854
|
+
p: props,
|
|
3855
|
+
notify
|
|
3856
|
+
})
|
|
3857
|
+
}
|
|
3858
|
+
];
|
|
3859
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ErrorBoundary, {
|
|
3860
|
+
t,
|
|
3861
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3862
|
+
className: "dab-root",
|
|
3863
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3864
|
+
className: "dab-shell",
|
|
3865
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
|
|
3866
|
+
className: "dab-nav",
|
|
3867
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3868
|
+
className: "dab-brand",
|
|
3869
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3870
|
+
className: "dab-brand-tile",
|
|
3871
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SunIcon, { size: 15 })
|
|
3872
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3873
|
+
className: "dab-brand-name",
|
|
3874
|
+
children: t("nav")
|
|
3875
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3876
|
+
className: "dab-brand-tag",
|
|
3877
|
+
children: t("brandTag")
|
|
3878
|
+
})] })]
|
|
3879
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3880
|
+
className: "dab-nav-list",
|
|
3881
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3882
|
+
className: "dab-nav-ind",
|
|
3883
|
+
style: { transform: `translateY(${page * 42}px)` }
|
|
3884
|
+
}), pages.map((pg, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3885
|
+
type: "button",
|
|
3886
|
+
className: `dab-nav-item${i === page ? " is-active" : ""}`,
|
|
3887
|
+
onClick: () => setPage(i),
|
|
3888
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(pg.Icon, { size: 16 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: pg.label })]
|
|
3889
|
+
}, pg.label))]
|
|
3890
|
+
})]
|
|
3891
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3892
|
+
className: "dab-page",
|
|
3893
|
+
children: pages[page].node
|
|
3894
|
+
}, page)]
|
|
3895
|
+
})
|
|
3896
|
+
}), toast ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3897
|
+
className: "dab-toast",
|
|
3898
|
+
role: "status",
|
|
3899
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3900
|
+
className: toast.ok ? "dab-toast-ok" : "dab-toast-err",
|
|
3901
|
+
children: toast.ok ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertIcon, { size: 14 })
|
|
3902
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: toast.msg })]
|
|
3903
|
+
}) }) : null]
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
2063
3906
|
//#endregion
|
|
2064
3907
|
//#region src/client/index.tsx
|
|
2065
3908
|
/**
|
|
@@ -2081,9 +3924,9 @@ window.__ModuleLoader__.load({
|
|
|
2081
3924
|
* rpc.ts file-backed persistence over the /dsh-any-background channel
|
|
2082
3925
|
* wallpaper.ts wallpaper DOM layer + inline token writes
|
|
2083
3926
|
* utils/ color math, token generation, image compression
|
|
2084
|
-
* components/ ColorWheel, BgEditor, LiveSlider
|
|
3927
|
+
* components/ ThemeSection shell + pages/, ColorWheel, BgEditor, LiveSlider
|
|
2085
3928
|
* i18n.ts zh/en dictionaries
|
|
2086
|
-
*
|
|
3929
|
+
* ui.css.ts scoped design-system stylesheet (classes + keyframes)
|
|
2087
3930
|
*/
|
|
2088
3931
|
const name = "dsh-any-background";
|
|
2089
3932
|
const inject = [
|
|
@@ -2114,6 +3957,7 @@ window.__ModuleLoader__.load({
|
|
|
2114
3957
|
if (rHasColor()) registerCustom(initH, initS, initL);
|
|
2115
3958
|
ctx.effect(() => () => {
|
|
2116
3959
|
customDispose?.();
|
|
3960
|
+
if (colorTimerRef.current !== null) window.clearTimeout(colorTimerRef.current);
|
|
2117
3961
|
}, "dsh-any-background: skin dispose");
|
|
2118
3962
|
let styleEl;
|
|
2119
3963
|
if (typeof document !== "undefined") {
|
|
@@ -2127,31 +3971,47 @@ window.__ModuleLoader__.load({
|
|
|
2127
3971
|
}, "dsh-any-background: gradient");
|
|
2128
3972
|
let rev = 0;
|
|
2129
3973
|
let colorRev = 0;
|
|
3974
|
+
let bgRev = 0;
|
|
3975
|
+
const colorTimerRef = { current: null };
|
|
2130
3976
|
const store = (0, _deepseek_ai_dsh_client_runtime_client.defineStore)({
|
|
2131
3977
|
init: () => ({
|
|
2132
3978
|
url: null,
|
|
2133
3979
|
rev: -1,
|
|
2134
3980
|
colorRev: -1,
|
|
2135
|
-
color: null
|
|
3981
|
+
color: null,
|
|
3982
|
+
backgroundType: cfg.backgroundType,
|
|
3983
|
+
generatedBg: cfg.generatedBg,
|
|
3984
|
+
bgRev: -1,
|
|
3985
|
+
regenerateOnReload: cfg.regenerateOnReload
|
|
2136
3986
|
}),
|
|
2137
3987
|
actions: {
|
|
2138
|
-
syncBg: (d, url, r) => {
|
|
2139
|
-
if (r
|
|
2140
|
-
|
|
2141
|
-
|
|
3988
|
+
syncBg: (d, url, r, bgType, genBg, bgr, reload) => {
|
|
3989
|
+
if (r > d.rev) {
|
|
3990
|
+
d.url = url;
|
|
3991
|
+
d.rev = r;
|
|
3992
|
+
}
|
|
3993
|
+
if (bgr !== void 0 && bgr > d.bgRev) {
|
|
3994
|
+
d.backgroundType = bgType;
|
|
3995
|
+
d.generatedBg = genBg ?? null;
|
|
3996
|
+
d.bgRev = bgr;
|
|
3997
|
+
}
|
|
3998
|
+
if (reload !== void 0) d.regenerateOnReload = reload;
|
|
2142
3999
|
},
|
|
2143
4000
|
syncColor: (d, hsv, r) => {
|
|
2144
|
-
if (r
|
|
2145
|
-
|
|
2146
|
-
|
|
4001
|
+
if (r > d.colorRev) {
|
|
4002
|
+
d.color = hsv;
|
|
4003
|
+
d.colorRev = r;
|
|
4004
|
+
}
|
|
2147
4005
|
}
|
|
2148
4006
|
}
|
|
2149
4007
|
});
|
|
2150
4008
|
let bound = null;
|
|
2151
4009
|
const syncBg = () => {
|
|
2152
4010
|
rev++;
|
|
2153
|
-
|
|
4011
|
+
bgRev++;
|
|
4012
|
+
bound?.syncBg(rWp(), rev, cfg.backgroundType, cfg.generatedBg, bgRev, cfg.regenerateOnReload);
|
|
2154
4013
|
};
|
|
4014
|
+
onGeneratedSnapshot(syncBg);
|
|
2155
4015
|
applyWp();
|
|
2156
4016
|
syncBg();
|
|
2157
4017
|
watchParts();
|
|
@@ -2160,7 +4020,11 @@ window.__ModuleLoader__.load({
|
|
|
2160
4020
|
const [h, s, l] = rColor();
|
|
2161
4021
|
registerCustom(h, s, l);
|
|
2162
4022
|
}
|
|
2163
|
-
|
|
4023
|
+
if (cfg.backgroundType !== "image") {
|
|
4024
|
+
if (cfg.regenerateOnReload) regenerateGeneratedBg();
|
|
4025
|
+
else if (cfg.generatedBg) updateGeneratedBg(cfg.generatedBg);
|
|
4026
|
+
persistConfig();
|
|
4027
|
+
} else applyThemeColor();
|
|
2164
4028
|
syncBg();
|
|
2165
4029
|
if (rHasColor()) {
|
|
2166
4030
|
colorRev++;
|
|
@@ -2218,9 +4082,13 @@ window.__ModuleLoader__.load({
|
|
|
2218
4082
|
ss,
|
|
2219
4083
|
sl
|
|
2220
4084
|
];
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
4085
|
+
if (colorTimerRef.current !== null) window.clearTimeout(colorTimerRef.current);
|
|
4086
|
+
colorTimerRef.current = window.setTimeout(() => {
|
|
4087
|
+
colorTimerRef.current = null;
|
|
4088
|
+
registerCustom(sh, ss, sl);
|
|
4089
|
+
applyWp();
|
|
4090
|
+
saveConfig();
|
|
4091
|
+
}, 80);
|
|
2224
4092
|
colorRev++;
|
|
2225
4093
|
bound?.syncColor([
|
|
2226
4094
|
nh,
|
|
@@ -2229,11 +4097,34 @@ window.__ModuleLoader__.load({
|
|
|
2229
4097
|
], colorRev);
|
|
2230
4098
|
},
|
|
2231
4099
|
setWp: (u) => {
|
|
4100
|
+
cfg.backgroundType = "image";
|
|
4101
|
+
setBgDark(null);
|
|
4102
|
+
setWpImageUrl(u);
|
|
2232
4103
|
setWpUrl(u);
|
|
2233
4104
|
setBgState({ ...DEFAULT_CONFIG.bgState });
|
|
2234
|
-
applyWp();
|
|
2235
|
-
syncBg();
|
|
2236
4105
|
persistWallpaper(u);
|
|
4106
|
+
applyThemeColor();
|
|
4107
|
+
syncBg();
|
|
4108
|
+
},
|
|
4109
|
+
setBgType: (type) => {
|
|
4110
|
+
setBackgroundType(type);
|
|
4111
|
+
saveConfig();
|
|
4112
|
+
syncBg();
|
|
4113
|
+
},
|
|
4114
|
+
setGeneratedBg: (params) => {
|
|
4115
|
+
updateGeneratedBg(params);
|
|
4116
|
+
saveConfig();
|
|
4117
|
+
syncBg();
|
|
4118
|
+
},
|
|
4119
|
+
regenerateBg: () => {
|
|
4120
|
+
regenerateGeneratedBg();
|
|
4121
|
+
persistConfig();
|
|
4122
|
+
syncBg();
|
|
4123
|
+
},
|
|
4124
|
+
setRegenerateOnReload: (v) => {
|
|
4125
|
+
cfg.regenerateOnReload = v;
|
|
4126
|
+
persistConfig();
|
|
4127
|
+
syncBg();
|
|
2237
4128
|
},
|
|
2238
4129
|
setOps: (ops) => {
|
|
2239
4130
|
cfg.opacities = ops;
|
|
@@ -2280,10 +4171,10 @@ window.__ModuleLoader__.load({
|
|
|
2280
4171
|
},
|
|
2281
4172
|
exportTheme: () => {
|
|
2282
4173
|
const payload = {
|
|
2283
|
-
version:
|
|
4174
|
+
version: 2,
|
|
2284
4175
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2285
4176
|
config: cfg,
|
|
2286
|
-
wallpaper: rWp()
|
|
4177
|
+
wallpaper: cfg.backgroundType === "image" ? rWp() : null
|
|
2287
4178
|
};
|
|
2288
4179
|
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
|
2289
4180
|
const url = URL.createObjectURL(blob);
|
|
@@ -2300,15 +4191,24 @@ window.__ModuleLoader__.load({
|
|
|
2300
4191
|
const d = data;
|
|
2301
4192
|
if (typeof d.config !== "object" || d.config === null) return false;
|
|
2302
4193
|
adoptConfig(d.config);
|
|
2303
|
-
|
|
2304
|
-
|
|
4194
|
+
if (cfg.backgroundType === "image") {
|
|
4195
|
+
const wallpaper = typeof d.wallpaper === "string" && /^data:image\//.test(d.wallpaper) ? d.wallpaper : null;
|
|
4196
|
+
setWpImageUrl(wallpaper);
|
|
4197
|
+
setWpUrl(wallpaper);
|
|
4198
|
+
persistWallpaper(wallpaper);
|
|
4199
|
+
applyThemeColor();
|
|
4200
|
+
} else {
|
|
4201
|
+
setWpImageUrl(null);
|
|
4202
|
+
setWpUrl(null);
|
|
4203
|
+
persistWallpaper(null);
|
|
4204
|
+
if (cfg.regenerateOnReload) regenerateGeneratedBg();
|
|
4205
|
+
else if (cfg.generatedBg) updateGeneratedBg(cfg.generatedBg);
|
|
4206
|
+
}
|
|
2305
4207
|
persistConfig();
|
|
2306
|
-
persistWallpaper(wallpaper);
|
|
2307
4208
|
if (rHasColor()) {
|
|
2308
4209
|
const [h, s, l] = rColor();
|
|
2309
4210
|
registerCustom(h, s, l);
|
|
2310
4211
|
}
|
|
2311
|
-
applyWp();
|
|
2312
4212
|
syncBg();
|
|
2313
4213
|
if (rHasColor()) {
|
|
2314
4214
|
colorRev++;
|
|
@@ -2380,8 +4280,11 @@ window.__ModuleLoader__.load({
|
|
|
2380
4280
|
}, "dsh-any-background: nav icon watch");
|
|
2381
4281
|
const restoreSaved = () => {
|
|
2382
4282
|
if (rHasColor()) {
|
|
2383
|
-
const
|
|
2384
|
-
|
|
4283
|
+
const snapshot = ctx.theme.getTheme();
|
|
4284
|
+
if (!snapshot.themes.some((t) => t.id === CUSTOM_ID)) {
|
|
4285
|
+
const [h, s, l] = rColor();
|
|
4286
|
+
registerCustom(h, s, l);
|
|
4287
|
+
} else if (snapshot.preference !== CUSTOM_ID) ctx.theme.setTheme(CUSTOM_ID);
|
|
2385
4288
|
}
|
|
2386
4289
|
applyWp();
|
|
2387
4290
|
};
|