dsh-any-background 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -150
- package/README.zh.md +163 -150
- package/lib/client.js +1095 -417
- package/lib/client.js.map +1 -1
- package/lib/index.js +357 -24
- package/package.json +126 -121
package/lib/client.js
CHANGED
|
@@ -44,11 +44,13 @@ window.__ModuleLoader__.load({
|
|
|
44
44
|
uiSop: "设置界面透明度",
|
|
45
45
|
uiChatRegion: "对话文本框",
|
|
46
46
|
uiTrajectory: "轨迹页",
|
|
47
|
+
uiPanelRegion: "better-sidebar 工作台",
|
|
48
|
+
uiProduced: "产出物/高亮内容",
|
|
47
49
|
bgTitle: "背景",
|
|
48
50
|
bgChoose: "选择图片或视频",
|
|
49
51
|
bgRemove: "移除背景",
|
|
50
52
|
bgFromUrl: "从网址",
|
|
51
|
-
bgUrlPlaceholder: "
|
|
53
|
+
bgUrlPlaceholder: "粘贴图片或视频网址 https://…",
|
|
52
54
|
bgUrlApply: "应用",
|
|
53
55
|
bgUrlCancel: "取消",
|
|
54
56
|
bgUrlApplying: "加载中…",
|
|
@@ -122,6 +124,13 @@ window.__ModuleLoader__.load({
|
|
|
122
124
|
presetDots: "点阵",
|
|
123
125
|
presetWaves: "波浪",
|
|
124
126
|
presetPoly: "低多边形",
|
|
127
|
+
presetStarfield: "星空",
|
|
128
|
+
presetRain: "雨滴",
|
|
129
|
+
presetContour: "等高线",
|
|
130
|
+
presetMeta: "流体光斑",
|
|
131
|
+
bgPause: "暂停",
|
|
132
|
+
bgResume: "播放",
|
|
133
|
+
bgUrlVideoFail: "获取视频失败,请确认链接可直接下载视频文件",
|
|
125
134
|
schemeTitle: "界面明暗",
|
|
126
135
|
schemeLight: "亮色",
|
|
127
136
|
schemeDark: "暗色",
|
|
@@ -200,11 +209,13 @@ window.__ModuleLoader__.load({
|
|
|
200
209
|
uiSop: "Settings interface opacity",
|
|
201
210
|
uiChatRegion: "Conversation text frame",
|
|
202
211
|
uiTrajectory: "Trajectory view",
|
|
212
|
+
uiPanelRegion: "better-sidebar workbench",
|
|
213
|
+
uiProduced: "Produced / highlights",
|
|
203
214
|
bgTitle: "Background",
|
|
204
215
|
bgChoose: "Choose image or video",
|
|
205
216
|
bgRemove: "Remove background",
|
|
206
217
|
bgFromUrl: "From URL",
|
|
207
|
-
bgUrlPlaceholder: "Paste an image URL https://…",
|
|
218
|
+
bgUrlPlaceholder: "Paste an image or video URL https://…",
|
|
208
219
|
bgUrlApply: "Apply",
|
|
209
220
|
bgUrlCancel: "Cancel",
|
|
210
221
|
bgUrlApplying: "Loading…",
|
|
@@ -278,6 +289,13 @@ window.__ModuleLoader__.load({
|
|
|
278
289
|
presetDots: "Dots",
|
|
279
290
|
presetWaves: "Waves",
|
|
280
291
|
presetPoly: "Low poly",
|
|
292
|
+
presetStarfield: "Starfield",
|
|
293
|
+
presetRain: "Rain",
|
|
294
|
+
presetContour: "Contours",
|
|
295
|
+
presetMeta: "Fluid orbs",
|
|
296
|
+
bgPause: "Pause",
|
|
297
|
+
bgResume: "Play",
|
|
298
|
+
bgUrlVideoFail: "Could not fetch the video — make sure the link downloads a video file directly",
|
|
281
299
|
schemeTitle: "Interface scheme",
|
|
282
300
|
schemeLight: "Light",
|
|
283
301
|
schemeDark: "Dark",
|
|
@@ -347,7 +365,9 @@ window.__ModuleLoader__.load({
|
|
|
347
365
|
settings: 0,
|
|
348
366
|
chat: 0,
|
|
349
367
|
trajectory: 0,
|
|
350
|
-
input: 0
|
|
368
|
+
input: 0,
|
|
369
|
+
panel: 0,
|
|
370
|
+
produced: 0
|
|
351
371
|
},
|
|
352
372
|
settingsOpacity: 1,
|
|
353
373
|
wallpaperOpacity: 1,
|
|
@@ -373,6 +393,8 @@ window.__ModuleLoader__.load({
|
|
|
373
393
|
regenerateOnReload: false,
|
|
374
394
|
chatTextOpacity: 0,
|
|
375
395
|
trajectoryOpacity: 1,
|
|
396
|
+
panelOpacity: 1,
|
|
397
|
+
producedOpacity: 1,
|
|
376
398
|
profiles: [],
|
|
377
399
|
rotation: {
|
|
378
400
|
enabled: false,
|
|
@@ -408,12 +430,19 @@ window.__ModuleLoader__.load({
|
|
|
408
430
|
let wpVideoSnapshot = null;
|
|
409
431
|
/** Local blob URL backing an in-session video; revoked when replaced or cleared. */
|
|
410
432
|
let wpVideoObjectUrl = null;
|
|
411
|
-
function setWpImageUrl(url) {
|
|
412
|
-
wpImageUrl = url;
|
|
413
|
-
}
|
|
414
433
|
function setWpUrl(url) {
|
|
415
434
|
wpUrl = url;
|
|
416
435
|
}
|
|
436
|
+
let imageRev = 0;
|
|
437
|
+
function bumpImageRev(url) {
|
|
438
|
+
if (url === null) return url;
|
|
439
|
+
if (!/^https?:\/\//i.test(url) && !url.startsWith("/")) return url;
|
|
440
|
+
imageRev++;
|
|
441
|
+
return `${url}${url.includes("?") ? "&" : "?"}r=${imageRev}`;
|
|
442
|
+
}
|
|
443
|
+
function setWpImageUrl(url) {
|
|
444
|
+
wpImageUrl = bumpImageRev(url);
|
|
445
|
+
}
|
|
417
446
|
let videoRev = 0;
|
|
418
447
|
function setWpVideoUrl(url, mime) {
|
|
419
448
|
if (url === null) {
|
|
@@ -502,7 +531,9 @@ window.__ModuleLoader__.load({
|
|
|
502
531
|
"settings",
|
|
503
532
|
"chat",
|
|
504
533
|
"trajectory",
|
|
505
|
-
"input"
|
|
534
|
+
"input",
|
|
535
|
+
"panel",
|
|
536
|
+
"produced"
|
|
506
537
|
]) {
|
|
507
538
|
const v = b[k];
|
|
508
539
|
out[k] = typeof v === "number" ? Math.min(60, Math.max(0, v)) : DEFAULT_CONFIG.blurs[k];
|
|
@@ -518,6 +549,12 @@ window.__ModuleLoader__.load({
|
|
|
518
549
|
function rSop() {
|
|
519
550
|
return clamp01(cfg.settingsOpacity, DEFAULT_CONFIG.settingsOpacity);
|
|
520
551
|
}
|
|
552
|
+
function rPanelOpacity() {
|
|
553
|
+
return clamp01(cfg.panelOpacity, DEFAULT_CONFIG.panelOpacity);
|
|
554
|
+
}
|
|
555
|
+
function rProducedOpacity() {
|
|
556
|
+
return clamp01(cfg.producedOpacity, DEFAULT_CONFIG.producedOpacity);
|
|
557
|
+
}
|
|
521
558
|
function rBgState() {
|
|
522
559
|
return cfg.bgState;
|
|
523
560
|
}
|
|
@@ -578,7 +615,9 @@ window.__ModuleLoader__.load({
|
|
|
578
615
|
wallpaperOpacity: rWop(),
|
|
579
616
|
blur: rBl(),
|
|
580
617
|
chatTextOpacity: rChatTextOpacity(),
|
|
581
|
-
trajectoryOpacity: rTrajectoryOpacity()
|
|
618
|
+
trajectoryOpacity: rTrajectoryOpacity(),
|
|
619
|
+
panelOpacity: rPanelOpacity(),
|
|
620
|
+
producedOpacity: rProducedOpacity()
|
|
582
621
|
};
|
|
583
622
|
}
|
|
584
623
|
/** Apply an appearance snapshot onto cfg (meta fields untouched). */
|
|
@@ -597,6 +636,8 @@ window.__ModuleLoader__.load({
|
|
|
597
636
|
cfg.blur = typeof ap.blur === "number" ? Math.min(60, Math.max(0, ap.blur)) : DEFAULT_CONFIG.blur;
|
|
598
637
|
cfg.chatTextOpacity = clamp01(ap.chatTextOpacity, DEFAULT_CONFIG.chatTextOpacity);
|
|
599
638
|
cfg.trajectoryOpacity = clamp01(ap.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity);
|
|
639
|
+
cfg.panelOpacity = clamp01(ap.panelOpacity, DEFAULT_CONFIG.panelOpacity);
|
|
640
|
+
cfg.producedOpacity = clamp01(ap.producedOpacity, DEFAULT_CONFIG.producedOpacity);
|
|
600
641
|
}
|
|
601
642
|
const num = (n, def) => typeof n === "number" ? n : def;
|
|
602
643
|
const cl = (n, lo, hi, def) => typeof n === "number" ? Math.min(hi, Math.max(lo, n)) : def;
|
|
@@ -627,7 +668,9 @@ window.__ModuleLoader__.load({
|
|
|
627
668
|
wallpaperOpacity: clamp01(ac.wallpaperOpacity, DEFAULT_CONFIG.wallpaperOpacity),
|
|
628
669
|
blur: num(ac.blur, DEFAULT_CONFIG.blur),
|
|
629
670
|
chatTextOpacity: clamp01(ac.chatTextOpacity, DEFAULT_CONFIG.chatTextOpacity),
|
|
630
|
-
trajectoryOpacity: clamp01(ac.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity)
|
|
671
|
+
trajectoryOpacity: clamp01(ac.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity),
|
|
672
|
+
panelOpacity: clamp01(ac.panelOpacity, DEFAULT_CONFIG.panelOpacity),
|
|
673
|
+
producedOpacity: clamp01(ac.producedOpacity, DEFAULT_CONFIG.producedOpacity)
|
|
631
674
|
}
|
|
632
675
|
});
|
|
633
676
|
}
|
|
@@ -690,7 +733,9 @@ window.__ModuleLoader__.load({
|
|
|
690
733
|
"settings",
|
|
691
734
|
"chat",
|
|
692
735
|
"trajectory",
|
|
693
|
-
"input"
|
|
736
|
+
"input",
|
|
737
|
+
"panel",
|
|
738
|
+
"produced"
|
|
694
739
|
]) blurs[k] = num(bl[k], DEFAULT_CONFIG.blurs[k]);
|
|
695
740
|
const bgType = [
|
|
696
741
|
"video",
|
|
@@ -728,6 +773,8 @@ window.__ModuleLoader__.load({
|
|
|
728
773
|
regenerateOnReload: typeof c.regenerateOnReload === "boolean" ? c.regenerateOnReload : DEFAULT_CONFIG.regenerateOnReload,
|
|
729
774
|
chatTextOpacity: clamp01(c.chatTextOpacity, DEFAULT_CONFIG.chatTextOpacity),
|
|
730
775
|
trajectoryOpacity: clamp01(c.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity),
|
|
776
|
+
panelOpacity: clamp01(c.panelOpacity, DEFAULT_CONFIG.panelOpacity),
|
|
777
|
+
producedOpacity: clamp01(c.producedOpacity, DEFAULT_CONFIG.producedOpacity),
|
|
731
778
|
profiles: adoptProfiles(c.profiles),
|
|
732
779
|
rotation: adoptRotation(c.rotation),
|
|
733
780
|
schedule: adoptSchedule(c.schedule),
|
|
@@ -748,7 +795,8 @@ window.__ModuleLoader__.load({
|
|
|
748
795
|
preset: [
|
|
749
796
|
"aurora",
|
|
750
797
|
"nebula",
|
|
751
|
-
"noise"
|
|
798
|
+
"noise",
|
|
799
|
+
"starfield"
|
|
752
800
|
].includes(p.preset) ? p.preset : "aurora",
|
|
753
801
|
speed: cl(p.speed, 0, 2, .3),
|
|
754
802
|
scale: cl(p.scale, .3, 3, 1),
|
|
@@ -759,7 +807,10 @@ window.__ModuleLoader__.load({
|
|
|
759
807
|
preset: [
|
|
760
808
|
"dots",
|
|
761
809
|
"waves",
|
|
762
|
-
"poly"
|
|
810
|
+
"poly",
|
|
811
|
+
"rain",
|
|
812
|
+
"contour",
|
|
813
|
+
"meta"
|
|
763
814
|
].includes(p.preset) ? p.preset : "dots",
|
|
764
815
|
density: cl(p.density, 0, 1, .5),
|
|
765
816
|
scale: cl(p.scale, .3, 3, 1),
|
|
@@ -771,6 +822,10 @@ window.__ModuleLoader__.load({
|
|
|
771
822
|
const RPC_CHANNEL = "/dsh-any-background";
|
|
772
823
|
/** Same-origin serve URL of the persisted video (enough for <video src>/fetch). */
|
|
773
824
|
const VIDEO_SERVE_URL = "/dsh-any-background/video";
|
|
825
|
+
/** Same-origin serve URL of the persisted wallpaper (native <img> loading). */
|
|
826
|
+
const WALLPAPER_SERVE_URL = "/dsh-any-background/wallpaper";
|
|
827
|
+
/** Raw-bytes upload endpoint for the wallpaper slot (no base64 inflation). */
|
|
828
|
+
const WALLPAPER_UPLOAD_URL = "/dsh-any-background/wallpaper/upload";
|
|
774
829
|
/** HTTP route new videos are POSTed to as raw bytes (see uploadVideo). */
|
|
775
830
|
const VIDEO_UPLOAD_URL = "/dsh-any-background/video/upload";
|
|
776
831
|
const RPC_NS = "dshAnyBackground";
|
|
@@ -810,19 +865,20 @@ window.__ModuleLoader__.load({
|
|
|
810
865
|
function persistConfig() {
|
|
811
866
|
rpcCall("writeConfig", { config: cfg });
|
|
812
867
|
}
|
|
813
|
-
/** Load the persisted theme (config + wallpaper + video URL) from the node
|
|
814
|
-
*
|
|
815
|
-
*
|
|
868
|
+
/** Load the persisted theme (config + wallpaper URL + video URL) from the node
|
|
869
|
+
* half. Image and video both travel as serve URLs — never bytes — so this RPC
|
|
870
|
+
* stays tiny. Resolves true when the server advanced a due wallpaper rotation
|
|
871
|
+
* during the read — the restored wallpaper is then already the new pick. */
|
|
816
872
|
async function loadPersisted() {
|
|
817
873
|
const data = await rpcCall("read", {});
|
|
818
874
|
if (data && typeof data === "object") {
|
|
819
875
|
const d = data;
|
|
820
876
|
if (d.config) adoptConfig(d.config);
|
|
821
|
-
if (typeof d.
|
|
822
|
-
else if (d.
|
|
877
|
+
if (typeof d.wallpaperUrl === "string") setWpImageUrl(d.wallpaperUrl);
|
|
878
|
+
else if (d.wallpaperUrl === null) setWpImageUrl(null);
|
|
823
879
|
if (typeof d.videoUrl === "string") setWpVideoUrl(d.videoUrl, cfg.videoMime);
|
|
824
880
|
else if (d.videoUrl === null) setWpVideoUrl(null, null);
|
|
825
|
-
if (cfg.backgroundType === "image") setWpUrl(
|
|
881
|
+
if (cfg.backgroundType === "image") setWpUrl(rWpImage());
|
|
826
882
|
return d.rotated === true;
|
|
827
883
|
}
|
|
828
884
|
return false;
|
|
@@ -837,7 +893,7 @@ window.__ModuleLoader__.load({
|
|
|
837
893
|
return await rpcCall("setVideo", { dataUrl }) === true;
|
|
838
894
|
}
|
|
839
895
|
/** Download a wallpaper from a network URL and persist it into the local slot
|
|
840
|
-
* (the host replaces wallpaper.jpg). Returns the freshly stored
|
|
896
|
+
* (the host replaces wallpaper.jpg). Returns the freshly stored serve URL on
|
|
841
897
|
* success, or the host's failure message. */
|
|
842
898
|
async function setWallpaperFromUrl(url) {
|
|
843
899
|
if (!rpcCallFn) return {
|
|
@@ -864,7 +920,40 @@ window.__ModuleLoader__.load({
|
|
|
864
920
|
const v = res.value;
|
|
865
921
|
return v?.ok === true ? {
|
|
866
922
|
ok: true,
|
|
867
|
-
|
|
923
|
+
wallpaperUrl: v.wallpaperUrl ?? null
|
|
924
|
+
} : {
|
|
925
|
+
ok: false,
|
|
926
|
+
error: v?.error ?? "failed"
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
/** Download a background video from a network URL. The server streams it into
|
|
930
|
+
* the video slot and returns the resolved MIME for playback via the serve URL. */
|
|
931
|
+
async function setVideoFromUrl(url) {
|
|
932
|
+
if (!rpcCallFn) return {
|
|
933
|
+
ok: false,
|
|
934
|
+
error: "rpc not ready"
|
|
935
|
+
};
|
|
936
|
+
let res;
|
|
937
|
+
try {
|
|
938
|
+
res = await rpcCallFn(rpcEndpoint("setVideoUrl"), { url });
|
|
939
|
+
} catch (e) {
|
|
940
|
+
return {
|
|
941
|
+
ok: false,
|
|
942
|
+
error: e instanceof Error ? e.message : String(e)
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
if (!res) return {
|
|
946
|
+
ok: false,
|
|
947
|
+
error: "no response"
|
|
948
|
+
};
|
|
949
|
+
if (res.ok !== true) return {
|
|
950
|
+
ok: false,
|
|
951
|
+
error: res.error?.message ?? "request failed"
|
|
952
|
+
};
|
|
953
|
+
const v = res.value;
|
|
954
|
+
return v?.ok === true ? {
|
|
955
|
+
ok: true,
|
|
956
|
+
mime: v.mime ?? "video/mp4"
|
|
868
957
|
} : {
|
|
869
958
|
ok: false,
|
|
870
959
|
error: v?.error ?? "failed"
|
|
@@ -884,6 +973,20 @@ window.__ModuleLoader__.load({
|
|
|
884
973
|
return false;
|
|
885
974
|
}
|
|
886
975
|
}
|
|
976
|
+
/** Upload a wallpaper's raw bytes over HTTP — same streaming model as videos,
|
|
977
|
+
* original pixels preserved, zero base64 round-trips. */
|
|
978
|
+
async function uploadWallpaper(blob) {
|
|
979
|
+
try {
|
|
980
|
+
return (await fetch(WALLPAPER_UPLOAD_URL, {
|
|
981
|
+
method: "POST",
|
|
982
|
+
headers: { "Content-Type": blob.type || "image/jpeg" },
|
|
983
|
+
body: blob
|
|
984
|
+
})).ok;
|
|
985
|
+
} catch (e) {
|
|
986
|
+
console.warn("dsh-any-background: wallpaper upload failed", e);
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
887
990
|
/** Add an image (data URL + small thumbnail) to the server-side rotation pool. */
|
|
888
991
|
async function rotationAdd(dataUrl, thumb) {
|
|
889
992
|
if (!rpcCallFn) return {
|
|
@@ -934,7 +1037,7 @@ window.__ModuleLoader__.load({
|
|
|
934
1037
|
}
|
|
935
1038
|
}
|
|
936
1039
|
/** Activate a rotation item: the server copies its bytes into the wallpaper
|
|
937
|
-
* slot and returns the
|
|
1040
|
+
* slot and returns the serve URL for immediate display. */
|
|
938
1041
|
async function rotationActivate(index) {
|
|
939
1042
|
if (!rpcCallFn) return {
|
|
940
1043
|
ok: false,
|
|
@@ -958,6 +1061,88 @@ window.__ModuleLoader__.load({
|
|
|
958
1061
|
}
|
|
959
1062
|
}
|
|
960
1063
|
//#endregion
|
|
1064
|
+
//#region src/client/utils/image.ts
|
|
1065
|
+
/**
|
|
1066
|
+
* Read a chosen image file as a data URL WITHOUT re-encoding: the original
|
|
1067
|
+
* pixels are kept as-is (no canvas downscale / JPEG re-compression), so the
|
|
1068
|
+
* wallpaper is stored and displayed at full fidelity. The tradeoff is a larger
|
|
1069
|
+
* payload over the RPC channel and on disk for big images.
|
|
1070
|
+
*/
|
|
1071
|
+
function readImg(file, cb) {
|
|
1072
|
+
const r = new FileReader();
|
|
1073
|
+
r.onerror = () => cb(null);
|
|
1074
|
+
r.onload = () => cb(r.result);
|
|
1075
|
+
r.readAsDataURL(file);
|
|
1076
|
+
}
|
|
1077
|
+
/** Promised readImg variant for async flows. */
|
|
1078
|
+
function readImgAsync(file) {
|
|
1079
|
+
return new Promise((resolve) => readImg(file, resolve));
|
|
1080
|
+
}
|
|
1081
|
+
/** Blob → data URL (used when a local asset must be embedded, e.g. theme export). */
|
|
1082
|
+
function blobToDataUrl(blob) {
|
|
1083
|
+
return new Promise((resolve, reject) => {
|
|
1084
|
+
const fr = new FileReader();
|
|
1085
|
+
fr.onload = () => resolve(fr.result);
|
|
1086
|
+
fr.onerror = () => reject(fr.error);
|
|
1087
|
+
fr.readAsDataURL(blob);
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
const IMG_CACHE_MAX = 6;
|
|
1091
|
+
const imgCache = /* @__PURE__ */ new Map();
|
|
1092
|
+
/** Decode an image URL once and share the element across callers (LRU-capped). */
|
|
1093
|
+
function loadImage(url) {
|
|
1094
|
+
const hit = imgCache.get(url);
|
|
1095
|
+
if (hit) {
|
|
1096
|
+
imgCache.delete(url);
|
|
1097
|
+
imgCache.set(url, hit);
|
|
1098
|
+
return hit;
|
|
1099
|
+
}
|
|
1100
|
+
const p = new Promise((resolve) => {
|
|
1101
|
+
const img = new Image();
|
|
1102
|
+
img.onerror = () => resolve(null);
|
|
1103
|
+
img.onload = () => resolve(img);
|
|
1104
|
+
img.src = url;
|
|
1105
|
+
});
|
|
1106
|
+
imgCache.set(url, p);
|
|
1107
|
+
while (imgCache.size > IMG_CACHE_MAX) {
|
|
1108
|
+
const oldest = imgCache.keys().next().value;
|
|
1109
|
+
if (oldest !== void 0) imgCache.delete(oldest);
|
|
1110
|
+
}
|
|
1111
|
+
return p;
|
|
1112
|
+
}
|
|
1113
|
+
/** Build a small JPEG thumbnail (rotation-pool picker preview). Resolves null
|
|
1114
|
+
* when the image cannot be decoded. */
|
|
1115
|
+
function makeThumb(dataUrl, maxSide = 96) {
|
|
1116
|
+
return new Promise((resolve) => {
|
|
1117
|
+
const img = new Image();
|
|
1118
|
+
img.onerror = () => resolve(null);
|
|
1119
|
+
img.onload = () => {
|
|
1120
|
+
try {
|
|
1121
|
+
const iw = img.naturalWidth || img.width;
|
|
1122
|
+
const ih = img.naturalHeight || img.height;
|
|
1123
|
+
if (iw <= 0 || ih <= 0) {
|
|
1124
|
+
resolve(null);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
const k = Math.min(1, maxSide / Math.max(iw, ih));
|
|
1128
|
+
const c = document.createElement("canvas");
|
|
1129
|
+
c.width = Math.max(1, Math.round(iw * k));
|
|
1130
|
+
c.height = Math.max(1, Math.round(ih * k));
|
|
1131
|
+
const g = c.getContext("2d");
|
|
1132
|
+
if (!g) {
|
|
1133
|
+
resolve(null);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
g.drawImage(img, 0, 0, c.width, c.height);
|
|
1137
|
+
resolve(c.toDataURL("image/jpeg", .72));
|
|
1138
|
+
} catch {
|
|
1139
|
+
resolve(null);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
img.src = dataUrl;
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
//#endregion
|
|
961
1146
|
//#region src/client/utils/color.ts
|
|
962
1147
|
function hsvToHsl(h, s, v) {
|
|
963
1148
|
const l = v * (1 - s / 2);
|
|
@@ -1164,124 +1349,119 @@ window.__ModuleLoader__.load({
|
|
|
1164
1349
|
return rgbToHsl(b.r / b.count, b.g / b.count, b.b / b.count);
|
|
1165
1350
|
}
|
|
1166
1351
|
function extractWallpaperPalette(dataUrl, bgState) {
|
|
1167
|
-
return
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
const c = document.createElement("canvas");
|
|
1192
|
-
c.width = EXTRACT_SIDE;
|
|
1193
|
-
c.height = EXTRACT_SIDE;
|
|
1194
|
-
const g = c.getContext("2d", { willReadFrequently: true });
|
|
1195
|
-
g.drawImage(img, sx, sy, sw, sh, 0, 0, EXTRACT_SIDE, EXTRACT_SIDE);
|
|
1196
|
-
const px = g.getImageData(0, 0, EXTRACT_SIDE, EXTRACT_SIDE).data;
|
|
1197
|
-
const counts = /* @__PURE__ */ new Uint32Array(4096);
|
|
1198
|
-
const sums = /* @__PURE__ */ new Float64Array(12288);
|
|
1199
|
-
let totalLum = 0;
|
|
1200
|
-
let sampled = 0;
|
|
1201
|
-
for (let i = 0; i < px.length; i += 4) {
|
|
1202
|
-
const r = px[i], gg = px[i + 1], b = px[i + 2];
|
|
1203
|
-
const max = Math.max(r, gg, b), min = Math.min(r, gg, b);
|
|
1204
|
-
const v = max / 255;
|
|
1205
|
-
const s = max === 0 ? 0 : (max - min) / max;
|
|
1206
|
-
totalLum += (.2126 * r + .7152 * gg + .0722 * b) / 255;
|
|
1207
|
-
sampled++;
|
|
1208
|
-
if (s < .08 || v < .12 || v > .97) continue;
|
|
1209
|
-
const key = r >> 4 << 8 | gg >> 4 << 4 | b >> 4;
|
|
1210
|
-
counts[key]++;
|
|
1211
|
-
sums[key * 3] += r;
|
|
1212
|
-
sums[key * 3 + 1] += gg;
|
|
1213
|
-
sums[key * 3 + 2] += b;
|
|
1214
|
-
}
|
|
1215
|
-
const buckets = [];
|
|
1216
|
-
for (let k = 0; k < 4096; k++) {
|
|
1217
|
-
if (counts[k] === 0) continue;
|
|
1218
|
-
buckets.push({
|
|
1219
|
-
count: counts[k],
|
|
1220
|
-
r: sums[k * 3],
|
|
1221
|
-
g: sums[k * 3 + 1],
|
|
1222
|
-
b: sums[k * 3 + 2],
|
|
1223
|
-
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
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
if (buckets.length === 0) {
|
|
1227
|
-
resolve(null);
|
|
1228
|
-
return;
|
|
1352
|
+
return loadImage(dataUrl).then((img) => {
|
|
1353
|
+
if (!img) return null;
|
|
1354
|
+
try {
|
|
1355
|
+
const iw = img.naturalWidth || img.width;
|
|
1356
|
+
const ih = img.naturalHeight || img.height;
|
|
1357
|
+
const bg = bgState;
|
|
1358
|
+
let sx = 0, sy = 0, sw = iw, sh = ih;
|
|
1359
|
+
if (bg.iw === iw && bg.ih === ih && bg.iw > 0) {
|
|
1360
|
+
const fit = Math.min(window.innerWidth / iw, window.innerHeight / ih);
|
|
1361
|
+
const w = iw * fit * bg.zoom;
|
|
1362
|
+
const h = ih * fit * bg.zoom;
|
|
1363
|
+
const px = bg.x * window.innerWidth - w / 2;
|
|
1364
|
+
const py = bg.y * window.innerHeight - h / 2;
|
|
1365
|
+
sx = Math.max(0, -px);
|
|
1366
|
+
sy = Math.max(0, -py);
|
|
1367
|
+
sw = Math.min(iw - sx, window.innerWidth - px - sx);
|
|
1368
|
+
sh = Math.min(ih - sy, window.innerHeight - py - sy);
|
|
1369
|
+
if (sw <= 0 || sh <= 0) {
|
|
1370
|
+
sx = 0;
|
|
1371
|
+
sy = 0;
|
|
1372
|
+
sw = iw;
|
|
1373
|
+
sh = ih;
|
|
1229
1374
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1375
|
+
}
|
|
1376
|
+
const c = document.createElement("canvas");
|
|
1377
|
+
c.width = EXTRACT_SIDE;
|
|
1378
|
+
c.height = EXTRACT_SIDE;
|
|
1379
|
+
const g = c.getContext("2d", { willReadFrequently: true });
|
|
1380
|
+
g.drawImage(img, sx, sy, sw, sh, 0, 0, EXTRACT_SIDE, EXTRACT_SIDE);
|
|
1381
|
+
const px = g.getImageData(0, 0, EXTRACT_SIDE, EXTRACT_SIDE).data;
|
|
1382
|
+
const counts = /* @__PURE__ */ new Uint32Array(4096);
|
|
1383
|
+
const sums = /* @__PURE__ */ new Float64Array(12288);
|
|
1384
|
+
let totalLum = 0;
|
|
1385
|
+
let sampled = 0;
|
|
1386
|
+
for (let i = 0; i < px.length; i += 4) {
|
|
1387
|
+
const r = px[i], gg = px[i + 1], b = px[i + 2];
|
|
1388
|
+
const max = Math.max(r, gg, b), min = Math.min(r, gg, b);
|
|
1389
|
+
const v = max / 255;
|
|
1390
|
+
const s = max === 0 ? 0 : (max - min) / max;
|
|
1391
|
+
totalLum += (.2126 * r + .7152 * gg + .0722 * b) / 255;
|
|
1392
|
+
sampled++;
|
|
1393
|
+
if (s < .08 || v < .12 || v > .97) continue;
|
|
1394
|
+
const key = r >> 4 << 8 | gg >> 4 << 4 | b >> 4;
|
|
1395
|
+
counts[key]++;
|
|
1396
|
+
sums[key * 3] += r;
|
|
1397
|
+
sums[key * 3 + 1] += gg;
|
|
1398
|
+
sums[key * 3 + 2] += b;
|
|
1399
|
+
}
|
|
1400
|
+
const buckets = [];
|
|
1401
|
+
for (let k = 0; k < 4096; k++) {
|
|
1402
|
+
if (counts[k] === 0) continue;
|
|
1403
|
+
buckets.push({
|
|
1404
|
+
count: counts[k],
|
|
1405
|
+
r: sums[k * 3],
|
|
1406
|
+
g: sums[k * 3 + 1],
|
|
1407
|
+
b: sums[k * 3 + 2],
|
|
1408
|
+
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
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
if (buckets.length === 0) return null;
|
|
1412
|
+
buckets.sort((a, b) => b.count * (.5 + b.s) - a.count * (.5 + a.s));
|
|
1413
|
+
const primary = bucketsToHsl(buckets[0]);
|
|
1414
|
+
const primaryHue = primary[0];
|
|
1415
|
+
let secondary = primary;
|
|
1416
|
+
for (const b of buckets.slice(1)) {
|
|
1417
|
+
const [h] = bucketsToHsl(b);
|
|
1418
|
+
if (Math.abs((h - primaryHue + 540) % 360 - 180) > 30) {
|
|
1419
|
+
secondary = bucketsToHsl(b);
|
|
1420
|
+
break;
|
|
1240
1421
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
]
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1422
|
+
}
|
|
1423
|
+
let tertiary = [
|
|
1424
|
+
(primaryHue + 180) % 360,
|
|
1425
|
+
Math.min(.7, primary[1]),
|
|
1426
|
+
Math.min(.7, primary[2])
|
|
1427
|
+
];
|
|
1428
|
+
for (const b of buckets.slice(1)) {
|
|
1429
|
+
const [h] = bucketsToHsl(b);
|
|
1430
|
+
const sep = Math.abs((h - primaryHue + 540) % 360 - 180);
|
|
1431
|
+
if (sep > 90 && sep < 150) {
|
|
1432
|
+
tertiary = bucketsToHsl(b);
|
|
1433
|
+
break;
|
|
1253
1434
|
}
|
|
1254
|
-
const surface = [
|
|
1255
|
-
primaryHue,
|
|
1256
|
-
Math.min(.06, primary[1] * .3),
|
|
1257
|
-
primary[2]
|
|
1258
|
-
];
|
|
1259
|
-
const luminance = sampled > 0 ? totalLum / sampled : primary[2];
|
|
1260
|
-
primary[2] = luminance < .5 ? Math.min(.44, Math.max(.2, primary[2])) : Math.max(.6, Math.min(.82, primary[2]));
|
|
1261
|
-
resolve({
|
|
1262
|
-
primary: [
|
|
1263
|
-
primary[0],
|
|
1264
|
-
Math.min(.9, Math.max(.15, primary[1])),
|
|
1265
|
-
primary[2]
|
|
1266
|
-
],
|
|
1267
|
-
secondary: [
|
|
1268
|
-
secondary[0],
|
|
1269
|
-
Math.min(.85, Math.max(.2, secondary[1])),
|
|
1270
|
-
Math.min(.75, Math.max(.35, secondary[2]))
|
|
1271
|
-
],
|
|
1272
|
-
tertiary: [
|
|
1273
|
-
tertiary[0],
|
|
1274
|
-
Math.min(.8, Math.max(.2, tertiary[1])),
|
|
1275
|
-
Math.min(.7, Math.max(.35, tertiary[2]))
|
|
1276
|
-
],
|
|
1277
|
-
surface,
|
|
1278
|
-
luminance
|
|
1279
|
-
});
|
|
1280
|
-
} catch {
|
|
1281
|
-
resolve(null);
|
|
1282
1435
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1436
|
+
const surface = [
|
|
1437
|
+
primaryHue,
|
|
1438
|
+
Math.min(.06, primary[1] * .3),
|
|
1439
|
+
primary[2]
|
|
1440
|
+
];
|
|
1441
|
+
const luminance = sampled > 0 ? totalLum / sampled : primary[2];
|
|
1442
|
+
primary[2] = luminance < .5 ? Math.min(.44, Math.max(.2, primary[2])) : Math.max(.6, Math.min(.82, primary[2]));
|
|
1443
|
+
return {
|
|
1444
|
+
primary: [
|
|
1445
|
+
primary[0],
|
|
1446
|
+
Math.min(.9, Math.max(.15, primary[1])),
|
|
1447
|
+
primary[2]
|
|
1448
|
+
],
|
|
1449
|
+
secondary: [
|
|
1450
|
+
secondary[0],
|
|
1451
|
+
Math.min(.85, Math.max(.2, secondary[1])),
|
|
1452
|
+
Math.min(.75, Math.max(.35, secondary[2]))
|
|
1453
|
+
],
|
|
1454
|
+
tertiary: [
|
|
1455
|
+
tertiary[0],
|
|
1456
|
+
Math.min(.8, Math.max(.2, tertiary[1])),
|
|
1457
|
+
Math.min(.7, Math.max(.35, tertiary[2]))
|
|
1458
|
+
],
|
|
1459
|
+
surface,
|
|
1460
|
+
luminance
|
|
1461
|
+
};
|
|
1462
|
+
} catch {
|
|
1463
|
+
return null;
|
|
1464
|
+
}
|
|
1285
1465
|
});
|
|
1286
1466
|
}
|
|
1287
1467
|
/** Backward-compatible single-color extraction: returns the primary HSL. */
|
|
@@ -1294,26 +1474,22 @@ window.__ModuleLoader__.load({
|
|
|
1294
1474
|
* reads dark (use white fonts), false when light (use black fonts), or null
|
|
1295
1475
|
* when the frame cannot be decoded. */
|
|
1296
1476
|
function analyzeFrameDark(dataUrl) {
|
|
1297
|
-
return
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
resolve(null);
|
|
1314
|
-
}
|
|
1315
|
-
};
|
|
1316
|
-
img.src = dataUrl;
|
|
1477
|
+
return loadImage(dataUrl).then((img) => {
|
|
1478
|
+
if (!img) return null;
|
|
1479
|
+
try {
|
|
1480
|
+
const c = document.createElement("canvas");
|
|
1481
|
+
c.width = ANALYZE_SIDE;
|
|
1482
|
+
c.height = ANALYZE_SIDE;
|
|
1483
|
+
const g = c.getContext("2d", { willReadFrequently: true });
|
|
1484
|
+
g.drawImage(img, 0, 0, ANALYZE_SIDE, ANALYZE_SIDE);
|
|
1485
|
+
const px = g.getImageData(0, 0, ANALYZE_SIDE, ANALYZE_SIDE).data;
|
|
1486
|
+
let lum = 0;
|
|
1487
|
+
const count = px.length / 4;
|
|
1488
|
+
for (let i = 0; i < px.length; i += 4) lum += .2126 * px[i] + .7152 * px[i + 1] + .0722 * px[i + 2];
|
|
1489
|
+
return lum / count / 255 < .5;
|
|
1490
|
+
} catch {
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1317
1493
|
});
|
|
1318
1494
|
}
|
|
1319
1495
|
/** HSL (h 0-360, s/l 0-1) → RGB (0-255 integers). */
|
|
@@ -1355,20 +1531,42 @@ window.__ModuleLoader__.load({
|
|
|
1355
1531
|
function newRaf(canvas, draw) {
|
|
1356
1532
|
canvas.dataset.dshAnyCanvas = "1";
|
|
1357
1533
|
let running = true;
|
|
1534
|
+
let paused = false;
|
|
1535
|
+
let rafId = 0;
|
|
1358
1536
|
let last = 0;
|
|
1359
1537
|
draw();
|
|
1538
|
+
if (typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return {
|
|
1539
|
+
stop: () => {
|
|
1540
|
+
running = false;
|
|
1541
|
+
},
|
|
1542
|
+
pause: () => void 0,
|
|
1543
|
+
resume: () => void 0
|
|
1544
|
+
};
|
|
1360
1545
|
const loop = (ts) => {
|
|
1361
|
-
if (!running) return;
|
|
1546
|
+
if (!running || paused) return;
|
|
1362
1547
|
if (ts - last >= FRAME_MS) {
|
|
1363
1548
|
last = ts;
|
|
1364
1549
|
draw();
|
|
1365
1550
|
}
|
|
1366
|
-
requestAnimationFrame(loop);
|
|
1551
|
+
rafId = requestAnimationFrame(loop);
|
|
1552
|
+
};
|
|
1553
|
+
rafId = requestAnimationFrame(loop);
|
|
1554
|
+
return {
|
|
1555
|
+
stop: () => {
|
|
1556
|
+
running = false;
|
|
1557
|
+
cancelAnimationFrame(rafId);
|
|
1558
|
+
},
|
|
1559
|
+
pause: () => {
|
|
1560
|
+
paused = true;
|
|
1561
|
+
cancelAnimationFrame(rafId);
|
|
1562
|
+
},
|
|
1563
|
+
resume: () => {
|
|
1564
|
+
if (!running || !paused) return;
|
|
1565
|
+
paused = false;
|
|
1566
|
+
last = 0;
|
|
1567
|
+
rafId = requestAnimationFrame(loop);
|
|
1568
|
+
}
|
|
1367
1569
|
};
|
|
1368
|
-
requestAnimationFrame(loop);
|
|
1369
|
-
return { stop: () => {
|
|
1370
|
-
running = false;
|
|
1371
|
-
} };
|
|
1372
1570
|
}
|
|
1373
1571
|
function createCanvas() {
|
|
1374
1572
|
const c = document.createElement("canvas");
|
|
@@ -1383,7 +1581,6 @@ window.__ModuleLoader__.load({
|
|
|
1383
1581
|
};
|
|
1384
1582
|
}
|
|
1385
1583
|
function fitLiveCanvas(c) {
|
|
1386
|
-
if (c.dataset.dshAnyStatic === "1") return;
|
|
1387
1584
|
const { w, h } = liveSize();
|
|
1388
1585
|
if (c.width !== w || c.height !== h) {
|
|
1389
1586
|
c.width = w;
|
|
@@ -1680,6 +1877,43 @@ window.__ModuleLoader__.load({
|
|
|
1680
1877
|
col = hueRotate(col, u_seed * 6.28318530718);
|
|
1681
1878
|
gl_FragColor = vec4(col, 1.0);
|
|
1682
1879
|
}
|
|
1880
|
+
`;
|
|
1881
|
+
if (preset === "starfield") return common + `
|
|
1882
|
+
float hash21(vec2 p) {
|
|
1883
|
+
p = fract(p * vec2(234.34, 435.345));
|
|
1884
|
+
p += dot(p, p + 34.23);
|
|
1885
|
+
return fract(p.x * p.y);
|
|
1886
|
+
}
|
|
1887
|
+
vec3 starLayer(vec2 uv, float t, float density) {
|
|
1888
|
+
vec3 col = vec3(0.0);
|
|
1889
|
+
vec2 grid = uv * density;
|
|
1890
|
+
vec2 cell = floor(grid);
|
|
1891
|
+
vec2 f = fract(grid) - 0.5;
|
|
1892
|
+
float h = hash21(cell);
|
|
1893
|
+
if (h > 0.8) {
|
|
1894
|
+
vec2 offs = (vec2(hash21(cell + 1.3), hash21(cell + 2.7)) - 0.5) * 0.7;
|
|
1895
|
+
float d = length(f - offs);
|
|
1896
|
+
float tw = 0.35 + 0.65 * sin(t * (1.0 + h * 2.5) + h * 40.0);
|
|
1897
|
+
col += vec3(0.85, 0.92, 1.0) * smoothstep(0.07, 0.0, d) * max(0.0, tw);
|
|
1898
|
+
}
|
|
1899
|
+
return col;
|
|
1900
|
+
}
|
|
1901
|
+
void main() {
|
|
1902
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
1903
|
+
vec2 p = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
|
|
1904
|
+
float t = u_time * 0.5;
|
|
1905
|
+
vec3 col = mix(vec3(0.012, 0.014, 0.035), vec3(0.035, 0.035, 0.08), uv.y);
|
|
1906
|
+
// Faint drifting nebula veil behind the stars.
|
|
1907
|
+
float n = fbm(vec3(p * 2.2 * u_scale, t * 0.05));
|
|
1908
|
+
col += vec3(0.05, 0.045, 0.11) * smoothstep(0.05, 0.85, n);
|
|
1909
|
+
vec3 stars = vec3(0.0);
|
|
1910
|
+
stars += starLayer(p + vec2(t * 0.006, 0.0), t, 13.0 * u_scale);
|
|
1911
|
+
stars += starLayer(p * 1.8 + vec2(t * 0.013, 3.0), t * 1.25, 27.0 * u_scale);
|
|
1912
|
+
stars += starLayer(p * 3.1 + vec2(t * 0.021, 7.0), t * 0.85, 46.0 * u_scale);
|
|
1913
|
+
col += stars;
|
|
1914
|
+
col = hueRotate(col, u_seed * 6.28318530718);
|
|
1915
|
+
gl_FragColor = vec4(col, 1.0);
|
|
1916
|
+
}
|
|
1683
1917
|
`;
|
|
1684
1918
|
return common + `
|
|
1685
1919
|
void main() {
|
|
@@ -1703,8 +1937,154 @@ window.__ModuleLoader__.load({
|
|
|
1703
1937
|
function createPatternBg(params, canvas) {
|
|
1704
1938
|
if (params.preset === "waves") return createWaves(params, canvas);
|
|
1705
1939
|
if (params.preset === "poly") return createLowPoly(params, canvas);
|
|
1940
|
+
if (params.preset === "rain") return createRain(params, canvas);
|
|
1941
|
+
if (params.preset === "contour") return createContour(params, canvas);
|
|
1942
|
+
if (params.preset === "meta") return createMeta(params, canvas);
|
|
1706
1943
|
return createDots(params, canvas);
|
|
1707
1944
|
}
|
|
1945
|
+
function createRain(params, canvas) {
|
|
1946
|
+
const c = canvas ?? createCanvas();
|
|
1947
|
+
fitLiveCanvas(c);
|
|
1948
|
+
const g = c.getContext("2d", { alpha: false });
|
|
1949
|
+
const rng = createRng(params.seed);
|
|
1950
|
+
const dark = rng() < .5;
|
|
1951
|
+
const hue = Math.round(rng() * 360);
|
|
1952
|
+
const count = Math.round(30 + params.density * 150);
|
|
1953
|
+
const drops = [];
|
|
1954
|
+
for (let i = 0; i < count; i++) drops.push({
|
|
1955
|
+
x: rng(),
|
|
1956
|
+
y: rng(),
|
|
1957
|
+
len: (.035 + rng() * .075) * (.7 + params.scale * .5),
|
|
1958
|
+
speed: (.25 + rng() * .55) * (.6 + params.scale * .5),
|
|
1959
|
+
sat: Math.round(30 + rng() * 40),
|
|
1960
|
+
lit: dark ? Math.round(55 + rng() * 25) : Math.round(40 + rng() * 25),
|
|
1961
|
+
alpha: .12 + rng() * .28,
|
|
1962
|
+
width: .8 + rng() * 1.4
|
|
1963
|
+
});
|
|
1964
|
+
let t = 0;
|
|
1965
|
+
const draw = () => {
|
|
1966
|
+
fitLiveCanvas(c);
|
|
1967
|
+
const w = c.width, h = c.height;
|
|
1968
|
+
g.fillStyle = dark ? "#07080c" : "#f4f6f9";
|
|
1969
|
+
g.fillRect(0, 0, w, h);
|
|
1970
|
+
for (const d of drops) {
|
|
1971
|
+
const yy = ((d.y + t * d.speed) % 1 + 1) % 1;
|
|
1972
|
+
const x = d.x * w;
|
|
1973
|
+
const y0 = yy * h;
|
|
1974
|
+
const y1 = y0 - d.len * h;
|
|
1975
|
+
const grad = g.createLinearGradient(x, y0, x, y1);
|
|
1976
|
+
grad.addColorStop(0, `hsla(${hue},${d.sat}%,${d.lit}%,${d.alpha.toFixed(2)})`);
|
|
1977
|
+
grad.addColorStop(1, "hsla(0,0%,0%,0)");
|
|
1978
|
+
g.strokeStyle = grad;
|
|
1979
|
+
g.lineWidth = d.width;
|
|
1980
|
+
g.beginPath();
|
|
1981
|
+
g.moveTo(x, y0);
|
|
1982
|
+
g.lineTo(x, y1);
|
|
1983
|
+
g.stroke();
|
|
1984
|
+
}
|
|
1985
|
+
t += .016;
|
|
1986
|
+
};
|
|
1987
|
+
return {
|
|
1988
|
+
canvas: c,
|
|
1989
|
+
...newRaf(c, draw),
|
|
1990
|
+
snapshot: () => c.toDataURL("image/jpeg", .94)
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
function createContour(params, canvas) {
|
|
1994
|
+
const c = canvas ?? createCanvas();
|
|
1995
|
+
fitLiveCanvas(c);
|
|
1996
|
+
const g = c.getContext("2d", { alpha: false });
|
|
1997
|
+
const noise = createNoise(params.seed);
|
|
1998
|
+
const rng = createRng(params.seed + 1013);
|
|
1999
|
+
const dark = rng() < .5;
|
|
2000
|
+
const hue = Math.round(rng() * 360);
|
|
2001
|
+
const layers = Math.round(16 + params.density * 46);
|
|
2002
|
+
const freq = .0016 / params.scale;
|
|
2003
|
+
const amp = .055 * params.scale;
|
|
2004
|
+
let t = 0;
|
|
2005
|
+
const draw = () => {
|
|
2006
|
+
fitLiveCanvas(c);
|
|
2007
|
+
const w = c.width, h = c.height;
|
|
2008
|
+
g.fillStyle = dark ? "#0a0b0e" : "#f6f7fa";
|
|
2009
|
+
g.fillRect(0, 0, w, h);
|
|
2010
|
+
const samples = Math.max(60, Math.floor(w / 8));
|
|
2011
|
+
for (let i = 0; i <= layers; i++) {
|
|
2012
|
+
const base = i / layers;
|
|
2013
|
+
const shift = t * .05;
|
|
2014
|
+
g.beginPath();
|
|
2015
|
+
for (let s = 0; s <= samples; s++) {
|
|
2016
|
+
const fx = s / samples;
|
|
2017
|
+
const y = (base + (noise(fx * 60 * freq * 400, i * .22 + shift) - .5) * amp) * h;
|
|
2018
|
+
if (s === 0) g.moveTo(0, y);
|
|
2019
|
+
else g.lineTo(fx * w, y);
|
|
2020
|
+
}
|
|
2021
|
+
const lit = dark ? 20 + i / layers * 22 : 62 - i / layers * 18;
|
|
2022
|
+
g.strokeStyle = `hsla(${(hue + i * 2) % 360},30%,${lit}%,${(.25 + .2 * (i / layers)).toFixed(2)})`;
|
|
2023
|
+
g.lineWidth = 1;
|
|
2024
|
+
g.stroke();
|
|
2025
|
+
}
|
|
2026
|
+
t += .032;
|
|
2027
|
+
};
|
|
2028
|
+
return {
|
|
2029
|
+
canvas: c,
|
|
2030
|
+
...newRaf(c, draw),
|
|
2031
|
+
snapshot: () => c.toDataURL("image/jpeg", .94)
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
function createMeta(params, canvas) {
|
|
2035
|
+
const c = canvas ?? createCanvas();
|
|
2036
|
+
fitLiveCanvas(c);
|
|
2037
|
+
const g = c.getContext("2d", { alpha: false });
|
|
2038
|
+
const rng = createRng(params.seed);
|
|
2039
|
+
const dark = rng() < .5;
|
|
2040
|
+
const baseHue = Math.round(rng() * 360);
|
|
2041
|
+
const count = Math.round(5 + params.density * 11);
|
|
2042
|
+
const blobs = [];
|
|
2043
|
+
for (let i = 0; i < count; i++) blobs.push({
|
|
2044
|
+
x: .15 + rng() * .7,
|
|
2045
|
+
y: .15 + rng() * .7,
|
|
2046
|
+
r: (.09 + rng() * .14) * params.scale,
|
|
2047
|
+
ax: .08 + rng() * .22,
|
|
2048
|
+
ay: .08 + rng() * .22,
|
|
2049
|
+
speed: .15 + rng() * .3,
|
|
2050
|
+
phase: rng() * Math.PI * 2,
|
|
2051
|
+
hue: (baseHue + rng() * 70) % 360
|
|
2052
|
+
});
|
|
2053
|
+
let t = 0;
|
|
2054
|
+
const draw = () => {
|
|
2055
|
+
fitLiveCanvas(c);
|
|
2056
|
+
const w = c.width, h = c.height;
|
|
2057
|
+
g.fillStyle = dark ? "#08090d" : "#f5f6f9";
|
|
2058
|
+
g.fillRect(0, 0, w, h);
|
|
2059
|
+
g.globalCompositeOperation = dark ? "lighter" : "multiply";
|
|
2060
|
+
for (const b of blobs) {
|
|
2061
|
+
const cx = (b.x + Math.sin(t * b.speed + b.phase) * b.ax) * w;
|
|
2062
|
+
const cy = (b.y + Math.cos(t * b.speed * .83 + b.phase * 1.7) * b.ay) * h;
|
|
2063
|
+
const r = b.r * Math.min(w, h) * (.9 + .2 * Math.sin(t * b.speed + b.phase));
|
|
2064
|
+
const rad = g.createRadialGradient(cx, cy, 0, cx, cy, r);
|
|
2065
|
+
if (dark) {
|
|
2066
|
+
rad.addColorStop(0, `hsla(${b.hue},65%,58%,0.55)`);
|
|
2067
|
+
rad.addColorStop(.7, `hsla(${b.hue},60%,45%,0.18)`);
|
|
2068
|
+
rad.addColorStop(1, "hsla(0,0%,0%,0)");
|
|
2069
|
+
} else {
|
|
2070
|
+
rad.addColorStop(0, `hsla(${b.hue},55%,70%,0.5)`);
|
|
2071
|
+
rad.addColorStop(.7, `hsla(${b.hue},50%,80%,0.2)`);
|
|
2072
|
+
rad.addColorStop(1, "hsla(0,0%,100%,0)");
|
|
2073
|
+
}
|
|
2074
|
+
g.fillStyle = rad;
|
|
2075
|
+
g.beginPath();
|
|
2076
|
+
g.arc(cx, cy, r, 0, Math.PI * 2);
|
|
2077
|
+
g.fill();
|
|
2078
|
+
}
|
|
2079
|
+
g.globalCompositeOperation = "source-over";
|
|
2080
|
+
t += .03;
|
|
2081
|
+
};
|
|
2082
|
+
return {
|
|
2083
|
+
canvas: c,
|
|
2084
|
+
...newRaf(c, draw),
|
|
2085
|
+
snapshot: () => c.toDataURL("image/jpeg", .94)
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
1708
2088
|
function createDots(params, canvas) {
|
|
1709
2089
|
const c = canvas ?? createCanvas();
|
|
1710
2090
|
fitLiveCanvas(c);
|
|
@@ -1909,6 +2289,14 @@ window.__ModuleLoader__.load({
|
|
|
1909
2289
|
let wpController = null;
|
|
1910
2290
|
let snapshotListener = null;
|
|
1911
2291
|
let tokenStyleEl = null;
|
|
2292
|
+
/** Pause the live generated background's animation loop (session-only). */
|
|
2293
|
+
function pauseGeneratedBg() {
|
|
2294
|
+
wpController?.pause?.();
|
|
2295
|
+
}
|
|
2296
|
+
/** Resume the live generated background's animation loop. */
|
|
2297
|
+
function resumeGeneratedBg() {
|
|
2298
|
+
wpController?.resume?.();
|
|
2299
|
+
}
|
|
1912
2300
|
function clearDynamicBg() {
|
|
1913
2301
|
wpController?.stop();
|
|
1914
2302
|
wpController?.canvas.remove();
|
|
@@ -2056,11 +2444,77 @@ window.__ModuleLoader__.load({
|
|
|
2056
2444
|
}
|
|
2057
2445
|
const SETTINGS_PANEL_SEL = "[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]";
|
|
2058
2446
|
const SETTINGS_STYLE_RULE = `${SETTINGS_PANEL_SEL}{background:var(--dsh-any-bg-settings-surface,var(--dsw-alias-bg-layer-2));backdrop-filter:var(--dsh-any-blur-settings,none);--dsw-alias-bg-layer-1:var(--dsh-any-bg-settings-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-bg-settings-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-bg-settings-layer-3)}${SETTINGS_PANEL_SEL} .dab-card{backdrop-filter:var(--dsh-any-blur-card-panels,none);-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,none)}`;
|
|
2059
|
-
const
|
|
2447
|
+
const POPOVER_BLUR_RULE = "[role=\"menu\"]:not([data-dockkit-tab-menu]),[role=\"listbox\"],body > [role=\"tree\"],body > [role=\"dialog\"]:not([aria-modal=\"true\"]){backdrop-filter:var(--dsh-any-blur-card-panels,none);-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,none)}";
|
|
2448
|
+
const INPUT_BLUR_RULE = "[data-composer-card]{position:relative;isolation:isolate}[data-composer-card]::before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;-webkit-backdrop-filter:var(--dsh-any-input-blur,none);backdrop-filter:var(--dsh-any-input-blur,none)}[data-cordis-panel]{-webkit-backdrop-filter:var(--dsh-any-input-blur,none);backdrop-filter:var(--dsh-any-input-blur,none)}[data-cordis-panel]{--dsw-specific-menu:var(--dsh-any-op-menu-cordis)!important}";
|
|
2060
2449
|
function applyInputBlur(px) {
|
|
2061
2450
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-input-blur", `blur(${px}px)`);
|
|
2062
2451
|
else document.documentElement.style.removeProperty("--dsh-any-input-blur");
|
|
2063
2452
|
}
|
|
2453
|
+
const MOBILE_HEADER_RULE = ".dsh-mobile-app-header{background:var(--dsh-any-op-bg,transparent)!important;backdrop-filter:var(--dsh-any-part-blur-global,none);-webkit-backdrop-filter:var(--dsh-any-part-blur-global,none)}";
|
|
2454
|
+
/** Mirror of the bg-part blur on :root. --dsh-any-part-blur is element-scoped
|
|
2455
|
+
* to the columns, so surfaces outside their subtree (the mobile header, or
|
|
2456
|
+
* third-party styles) can never inherit it. */
|
|
2457
|
+
function applyBgBlurGlobal(px) {
|
|
2458
|
+
if (px > 0) document.documentElement.style.setProperty("--dsh-any-part-blur-global", `blur(${px}px)`);
|
|
2459
|
+
else document.documentElement.style.removeProperty("--dsh-any-part-blur-global");
|
|
2460
|
+
}
|
|
2461
|
+
const PANEL_TOKEN_RULE = "[data-dsh-bottom-panel],[data-sidebar-right-panel]{--dsw-alias-bg-base:var(--dsh-any-panel-bg-base);--dsw-alias-bg-layer-1:var(--dsh-any-panel-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-panel-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-panel-layer-3);position:fixed!important;z-index:26!important}[data-sidebar-right-panel][data-sidebar-right-panel=\"fullscreen\"]{z-index:40!important}";
|
|
2462
|
+
const PANEL_BLUR_RULE = "[data-dsh-bottom-panel],[data-sidebar-right-panel]{-webkit-backdrop-filter:var(--dsh-any-blur-panel,none);backdrop-filter:var(--dsh-any-blur-panel,none)}";
|
|
2463
|
+
const PRODUCED_RULE = "[data-code-block-content] pre,[data-code-block-banner],[data-composer-chip],:not(pre)>code{-webkit-backdrop-filter:var(--dsh-any-blur-prod,none);backdrop-filter:var(--dsh-any-blur-prod,none)}@supports (background:color-mix(in srgb,red 50%,transparent)){.md-code-block{background:transparent!important}.md-code-block>div{background-color:transparent!important}.md-code-block [data-code-block-content] pre{background-color:color-mix(in srgb,var(--dsl-code-block-background,var(--dsw-alias-markdown-code-block,transparent)) var(--dsh-any-prod-pct,100%),transparent)!important}.md-code-block [data-code-block-banner]{background-color:color-mix(in srgb,var(--dsl-code-block-banner-background-color,var(--dsw-alias-markdown-code-block-banner,transparent)) var(--dsh-any-prod-pct,100%),transparent)!important}[data-code-block-content]{--shiki-background:color-mix(in srgb,var(--dsw-alias-markdown-code-block,var(--dsl-code-block-background,transparent)) var(--dsh-any-prod-pct,100%),transparent)}:not(pre)>code{background-color:color-mix(in srgb,var(--dsw-alias-markdown-inline-code,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-composer-chip]>*{background-color:color-mix(in srgb,var(--dsw-alias-interactive-bg-hover,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}}";
|
|
2464
|
+
/** The host's own default colors for the panel layer tokens, used when the
|
|
2465
|
+
* plugin has no palette (no picked color, no wallpaper verdict, no forced
|
|
2466
|
+
* scheme). Without these the panel's re-scope would resolve to invalid vars
|
|
2467
|
+
* and the panel would have no background — reading the host's resolved value
|
|
2468
|
+
* keeps the panel opaque by default and lets the slider retint it like every
|
|
2469
|
+
* other homepage part. Reads from the live `:root` so a custom host skin or
|
|
2470
|
+
* theme override wins. */
|
|
2471
|
+
function readHostLayerTokens() {
|
|
2472
|
+
if (typeof getComputedStyle === "undefined") return null;
|
|
2473
|
+
const root = document.documentElement;
|
|
2474
|
+
const cs = getComputedStyle(root);
|
|
2475
|
+
const base = cs.getPropertyValue("--dsw-alias-bg-base").trim();
|
|
2476
|
+
const layer1 = cs.getPropertyValue("--dsw-alias-bg-layer-1").trim();
|
|
2477
|
+
const layer2 = cs.getPropertyValue("--dsw-alias-bg-layer-2").trim();
|
|
2478
|
+
const layer3 = cs.getPropertyValue("--dsw-alias-bg-layer-3").trim();
|
|
2479
|
+
if (!base && !layer1 && !layer2 && !layer3) return null;
|
|
2480
|
+
return {
|
|
2481
|
+
base,
|
|
2482
|
+
layer1,
|
|
2483
|
+
layer2,
|
|
2484
|
+
layer3
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
function applyPanelOverrides(op) {
|
|
2488
|
+
const tokens = paletteTokens();
|
|
2489
|
+
const root = document.documentElement;
|
|
2490
|
+
if (tokens !== null) {
|
|
2491
|
+
const base = tokens["--dsw-alias-bg-base"];
|
|
2492
|
+
const layer1 = tokens["--dsw-alias-bg-layer-1"];
|
|
2493
|
+
const layer2 = tokens["--dsw-alias-bg-layer-2"];
|
|
2494
|
+
const layer3 = tokens["--dsw-alias-bg-layer-3"];
|
|
2495
|
+
if (base !== void 0) root.style.setProperty("--dsh-any-panel-bg-base", toRgba(base, op));
|
|
2496
|
+
if (layer1 !== void 0) root.style.setProperty("--dsh-any-panel-layer-1", toRgba(layer1, op));
|
|
2497
|
+
if (layer2 !== void 0) root.style.setProperty("--dsh-any-panel-layer-2", toRgba(layer2, op));
|
|
2498
|
+
if (layer3 !== void 0) root.style.setProperty("--dsh-any-panel-layer-3", toRgba(layer3, op));
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
const host = readHostLayerTokens();
|
|
2502
|
+
if (host === null) {
|
|
2503
|
+
root.style.setProperty("--dsh-any-panel-bg-base", "transparent");
|
|
2504
|
+
root.style.setProperty("--dsh-any-panel-layer-1", "transparent");
|
|
2505
|
+
root.style.setProperty("--dsh-any-panel-layer-2", "transparent");
|
|
2506
|
+
root.style.setProperty("--dsh-any-panel-layer-3", "transparent");
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
if (host.base) root.style.setProperty("--dsh-any-panel-bg-base", toRgba(host.base, op));
|
|
2510
|
+
if (host.layer1) root.style.setProperty("--dsh-any-panel-layer-1", toRgba(host.layer1, op));
|
|
2511
|
+
if (host.layer2) root.style.setProperty("--dsh-any-panel-layer-2", toRgba(host.layer2, op));
|
|
2512
|
+
if (host.layer3) root.style.setProperty("--dsh-any-panel-layer-3", toRgba(host.layer3, op));
|
|
2513
|
+
}
|
|
2514
|
+
function applyPanelBlur(px) {
|
|
2515
|
+
if (px > 0) document.documentElement.style.setProperty("--dsh-any-blur-panel", `blur(${px}px)`);
|
|
2516
|
+
else document.documentElement.style.removeProperty("--dsh-any-blur-panel");
|
|
2517
|
+
}
|
|
2064
2518
|
function applySettingsOverrides(op) {
|
|
2065
2519
|
const [h, s, l] = rColor();
|
|
2066
2520
|
const tokens = genTokens(h, s, l, rColorScheme()).tokens;
|
|
@@ -2099,14 +2553,16 @@ window.__ModuleLoader__.load({
|
|
|
2099
2553
|
return;
|
|
2100
2554
|
}
|
|
2101
2555
|
const url = rWp();
|
|
2102
|
-
if (url)
|
|
2103
|
-
if (hsl) {
|
|
2104
|
-
cfg.color = hsl;
|
|
2105
|
-
colorAdoptedListener?.(hsl);
|
|
2106
|
-
}
|
|
2556
|
+
if (url) {
|
|
2107
2557
|
applyWp();
|
|
2108
|
-
|
|
2109
|
-
|
|
2558
|
+
extractWallpaperColor(url, cfg.backgroundType === "video" ? rVideoBgState() : rBgState()).then((hsl) => {
|
|
2559
|
+
if (hsl && rWp() === url) {
|
|
2560
|
+
cfg.color = hsl;
|
|
2561
|
+
colorAdoptedListener?.(hsl);
|
|
2562
|
+
}
|
|
2563
|
+
applyWp();
|
|
2564
|
+
});
|
|
2565
|
+
} else applyWp();
|
|
2110
2566
|
}
|
|
2111
2567
|
/** Switch the background source type. For generated types a new live canvas is
|
|
2112
2568
|
* attached to the wallpaper layer and a snapshot is kept for the store/preview. */
|
|
@@ -2176,7 +2632,7 @@ window.__ModuleLoader__.load({
|
|
|
2176
2632
|
let frameEl = null;
|
|
2177
2633
|
let sidebarEl = null;
|
|
2178
2634
|
let centerEl = null;
|
|
2179
|
-
let
|
|
2635
|
+
let rightEl = null;
|
|
2180
2636
|
const PART_BLUR_CLASS = "dab-part-blur";
|
|
2181
2637
|
const PART_UNDERLAY_CLASS = "dab-part-underlay";
|
|
2182
2638
|
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)}`;
|
|
@@ -2188,16 +2644,32 @@ window.__ModuleLoader__.load({
|
|
|
2188
2644
|
partBlurStyleEl.textContent = PART_BLUR_RULE;
|
|
2189
2645
|
document.head.appendChild(partBlurStyleEl);
|
|
2190
2646
|
}
|
|
2647
|
+
/** Find one direct child of `frame` whose `[data-rightbar-col]` marker matches.
|
|
2648
|
+
* The rightbar is its own grid track, NOT a "details" twin of the center
|
|
2649
|
+
* column, so it must never receive the main-bg tint or blur underlay. */
|
|
2650
|
+
function findRightbarInFrame(frame) {
|
|
2651
|
+
for (let i = 0; i < frame.children.length; i++) {
|
|
2652
|
+
const child = frame.children[i];
|
|
2653
|
+
if (child instanceof HTMLElement && child.dataset.rightbarCol !== void 0) return child;
|
|
2654
|
+
}
|
|
2655
|
+
return null;
|
|
2656
|
+
}
|
|
2191
2657
|
function discoverParts() {
|
|
2192
2658
|
const overlay = document.querySelector("[data-shell-overlay]");
|
|
2193
2659
|
if (overlay === null) return;
|
|
2194
2660
|
const frame = overlay.parentElement;
|
|
2195
2661
|
if (frame === null) return;
|
|
2196
2662
|
frameEl = frame;
|
|
2663
|
+
rightEl = findRightbarInFrame(frame);
|
|
2197
2664
|
const idx = Array.from(frame.children).indexOf(overlay);
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2665
|
+
if (rightEl !== null) {
|
|
2666
|
+
const rightIdx = Array.from(frame.children).indexOf(rightEl);
|
|
2667
|
+
sidebarEl = frame.children[rightIdx - 2] ?? null;
|
|
2668
|
+
centerEl = frame.children[rightIdx - 1] ?? null;
|
|
2669
|
+
} else {
|
|
2670
|
+
sidebarEl = frame.children[idx - 3] ?? null;
|
|
2671
|
+
centerEl = frame.children[idx - 2] ?? null;
|
|
2672
|
+
}
|
|
2201
2673
|
}
|
|
2202
2674
|
function setBlur(el, px) {
|
|
2203
2675
|
if (el === null) return;
|
|
@@ -2229,10 +2701,14 @@ window.__ModuleLoader__.load({
|
|
|
2229
2701
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-blur-settings", `blur(${px}px)`);
|
|
2230
2702
|
else document.documentElement.style.removeProperty("--dsh-any-blur-settings");
|
|
2231
2703
|
}
|
|
2232
|
-
/** Apply the main-background opacity to the center
|
|
2704
|
+
/** Apply the main-background opacity to the center column instead of
|
|
2233
2705
|
* the frame. The frame's translucent bg-base sits UNDER the sidebar, so
|
|
2234
2706
|
* reducing the main-bg opacity stacked a second alpha onto the sidebar; moving
|
|
2235
|
-
* the alpha onto the
|
|
2707
|
+
* the alpha onto the column keeps the sidebar owned by its own slider. The
|
|
2708
|
+
* rightbar (DSH 0.1.5-rc.1+) is intentionally NOT tinted here — it sits on
|
|
2709
|
+
* its own grid track, paints its own surfaces from its own tokens, and
|
|
2710
|
+
* inheriting the main-bg tint would blend it into the page and make the
|
|
2711
|
+
* right panel disappear. */
|
|
2236
2712
|
function applyPartOpacities(ops) {
|
|
2237
2713
|
const tokens = paletteTokens();
|
|
2238
2714
|
if (tokens === null) return;
|
|
@@ -2241,12 +2717,12 @@ window.__ModuleLoader__.load({
|
|
|
2241
2717
|
const base = tokens["--dsw-alias-bg-base"];
|
|
2242
2718
|
frameEl.style.background = "transparent";
|
|
2243
2719
|
if (centerEl !== null) centerEl.style.background = base !== void 0 ? toRgba(base, ops.bg) : "transparent";
|
|
2244
|
-
if (
|
|
2720
|
+
if (rightEl !== null) rightEl.style.background = "";
|
|
2245
2721
|
}
|
|
2246
2722
|
/** Blur of the option panels inside the settings dialog (.dab-card), owned by
|
|
2247
2723
|
* the "dialog option panel" (card) blur slider. Written as a plugin-owned
|
|
2248
2724
|
* variable consumed by SETTINGS_STYLE_RULE — deliberately NOT applied to the
|
|
2249
|
-
* homepage center
|
|
2725
|
+
* homepage center column, which this slider must never touch. */
|
|
2250
2726
|
function applyCardPanelsBlur(px) {
|
|
2251
2727
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-blur-card-panels", `blur(${px}px)`);
|
|
2252
2728
|
else document.documentElement.style.removeProperty("--dsh-any-blur-card-panels");
|
|
@@ -2257,12 +2733,31 @@ window.__ModuleLoader__.load({
|
|
|
2257
2733
|
setBlur(frameEl, 0);
|
|
2258
2734
|
setBlur(sidebarEl, blurs.sidebar);
|
|
2259
2735
|
setBlur(centerEl, blurs.bg);
|
|
2260
|
-
setBlur(
|
|
2736
|
+
setBlur(rightEl, 0);
|
|
2737
|
+
applyBgBlurGlobal(blurs.bg);
|
|
2261
2738
|
applyCardPanelsBlur(blurs.card);
|
|
2262
2739
|
applySettingsBlur(blurs.settings);
|
|
2263
2740
|
applyInputBlur(blurs.input);
|
|
2741
|
+
applyPanelBlur(blurs.panel);
|
|
2742
|
+
applyProduced();
|
|
2264
2743
|
applyViewCards();
|
|
2265
2744
|
}
|
|
2745
|
+
/** Produced/artifact surfaces (conversation code blocks + their banner +
|
|
2746
|
+
* composer chips): re-write the root blur and the alpha percentage consumed by
|
|
2747
|
+
* PRODUCED_RULE. The percentage is an alpha for the surface's OWN host color —
|
|
2748
|
+
* 1 (100%) reproduces the untouched host look and lowering it fades exactly
|
|
2749
|
+
* that color out — so no palette sampling is involved and the surfaces keep
|
|
2750
|
+
* following the active theme/wallpaper color on their own. */
|
|
2751
|
+
function applyProduced() {
|
|
2752
|
+
const px = rBlurs().produced;
|
|
2753
|
+
const root = document.documentElement;
|
|
2754
|
+
if (px > 0) root.style.setProperty("--dsh-any-blur-prod", `blur(${px}px)`);
|
|
2755
|
+
else root.style.removeProperty("--dsh-any-blur-prod");
|
|
2756
|
+
let opacity = rProducedOpacity();
|
|
2757
|
+
if (opacity < 0) opacity = 0;
|
|
2758
|
+
if (opacity > 1) opacity = 1;
|
|
2759
|
+
root.style.setProperty("--dsh-any-prod-pct", `${Math.round(opacity * 100)}%`);
|
|
2760
|
+
}
|
|
2266
2761
|
/** Live per-part blur update during slider drag (no full re-apply). */
|
|
2267
2762
|
function setPartBlur(part, v) {
|
|
2268
2763
|
if (part === "settings") {
|
|
@@ -2277,6 +2772,14 @@ window.__ModuleLoader__.load({
|
|
|
2277
2772
|
applyInputBlur(v);
|
|
2278
2773
|
return;
|
|
2279
2774
|
}
|
|
2775
|
+
if (part === "panel") {
|
|
2776
|
+
applyPanelBlur(v);
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2779
|
+
if (part === "produced") {
|
|
2780
|
+
applyProduced();
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2280
2783
|
if (part === "chat" || part === "trajectory") {
|
|
2281
2784
|
applyViewCards();
|
|
2282
2785
|
return;
|
|
@@ -2284,7 +2787,7 @@ window.__ModuleLoader__.load({
|
|
|
2284
2787
|
discoverParts();
|
|
2285
2788
|
if (part === "bg") {
|
|
2286
2789
|
setBlur(centerEl, v);
|
|
2287
|
-
|
|
2790
|
+
applyBgBlurGlobal(v);
|
|
2288
2791
|
} else setBlur(sidebarEl, v);
|
|
2289
2792
|
}
|
|
2290
2793
|
const VIEW_CARDS = [{
|
|
@@ -2484,7 +2987,7 @@ window.__ModuleLoader__.load({
|
|
|
2484
2987
|
function watchParts() {
|
|
2485
2988
|
if (partsObserver !== null || typeof MutationObserver === "undefined") return;
|
|
2486
2989
|
partsObserver = new MutationObserver(() => {
|
|
2487
|
-
if (frameEl !== null && sidebarEl !== null && centerEl !== null &&
|
|
2990
|
+
if (frameEl !== null && sidebarEl !== null && centerEl !== null && rightEl !== null && document.body.contains(frameEl) && viewTargets.every((el) => el !== null && document.body.contains(el))) return;
|
|
2488
2991
|
applyPartBlurs(rBlurs());
|
|
2489
2992
|
applyPartOpacities(rOps());
|
|
2490
2993
|
});
|
|
@@ -2554,17 +3057,18 @@ window.__ModuleLoader__.load({
|
|
|
2554
3057
|
cb(imgNat.w, imgNat.h);
|
|
2555
3058
|
return;
|
|
2556
3059
|
}
|
|
2557
|
-
|
|
2558
|
-
|
|
3060
|
+
loadImage(url).then((img) => {
|
|
3061
|
+
if (!img) {
|
|
3062
|
+
cb(0, 0);
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
2559
3065
|
imgNat = {
|
|
2560
3066
|
url,
|
|
2561
3067
|
w: img.naturalWidth,
|
|
2562
3068
|
h: img.naturalHeight
|
|
2563
3069
|
};
|
|
2564
3070
|
cb(img.naturalWidth, img.naturalHeight);
|
|
2565
|
-
};
|
|
2566
|
-
img.onerror = () => cb(0, 0);
|
|
2567
|
-
img.src = url;
|
|
3071
|
+
});
|
|
2568
3072
|
}
|
|
2569
3073
|
const DRAG_MAX_SIDE = 720;
|
|
2570
3074
|
let lowResUrl = null;
|
|
@@ -2575,8 +3079,11 @@ window.__ModuleLoader__.load({
|
|
|
2575
3079
|
cb(lowResUrl);
|
|
2576
3080
|
return;
|
|
2577
3081
|
}
|
|
2578
|
-
|
|
2579
|
-
|
|
3082
|
+
loadImage(url).then((img) => {
|
|
3083
|
+
if (!img) {
|
|
3084
|
+
cb(null);
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
2580
3087
|
const k = Math.min(1, DRAG_MAX_SIDE / Math.max(img.naturalWidth, img.naturalHeight));
|
|
2581
3088
|
if (k >= 1) {
|
|
2582
3089
|
lowResFor = url;
|
|
@@ -2599,9 +3106,7 @@ window.__ModuleLoader__.load({
|
|
|
2599
3106
|
lowResFor = url;
|
|
2600
3107
|
lowResUrl = low;
|
|
2601
3108
|
cb(low);
|
|
2602
|
-
};
|
|
2603
|
-
img.onerror = () => cb(null);
|
|
2604
|
-
img.src = url;
|
|
3109
|
+
});
|
|
2605
3110
|
}
|
|
2606
3111
|
function setDragLow(on) {
|
|
2607
3112
|
if (cfg.backgroundType !== "image" || on === dragLow || !wpEl) return;
|
|
@@ -2640,6 +3145,7 @@ window.__ModuleLoader__.load({
|
|
|
2640
3145
|
}
|
|
2641
3146
|
let wpVerdict = null;
|
|
2642
3147
|
let verdictListener = null;
|
|
3148
|
+
let verdictGen = 0;
|
|
2643
3149
|
/** Register a callback fired when the background brightness verdict CHANGES
|
|
2644
3150
|
* (a new wallpaper was analyzed, a generated bg regenerated), so the skin can
|
|
2645
3151
|
* be re-registered through the host theme service. */
|
|
@@ -2652,6 +3158,7 @@ window.__ModuleLoader__.load({
|
|
|
2652
3158
|
if (dark !== null) verdictListener?.();
|
|
2653
3159
|
}
|
|
2654
3160
|
function updateWpVerdict(url) {
|
|
3161
|
+
const gen = ++verdictGen;
|
|
2655
3162
|
if (url === null) {
|
|
2656
3163
|
applyVerdict(null);
|
|
2657
3164
|
return;
|
|
@@ -2661,7 +3168,7 @@ window.__ModuleLoader__.load({
|
|
|
2661
3168
|
return;
|
|
2662
3169
|
}
|
|
2663
3170
|
analyzeFrameDark(url).then((dark) => {
|
|
2664
|
-
if (dark === null) return;
|
|
3171
|
+
if (dark === null || gen !== verdictGen) return;
|
|
2665
3172
|
wpVerdict = {
|
|
2666
3173
|
url,
|
|
2667
3174
|
dark
|
|
@@ -2791,10 +3298,12 @@ window.__ModuleLoader__.load({
|
|
|
2791
3298
|
setBgDark(null);
|
|
2792
3299
|
}
|
|
2793
3300
|
if (rHasColor() || rBgDark() !== null || rSchemeOverride() !== "auto") applyCustomTokens(rOps());
|
|
3301
|
+
else if (!url) clearThemeTokens();
|
|
2794
3302
|
if (rHasColor()) {
|
|
2795
3303
|
applySettingsOverrides(rSop());
|
|
2796
3304
|
applyTrajectoryOverrides(rTrajectoryOpacity());
|
|
2797
3305
|
}
|
|
3306
|
+
applyPanelOverrides(rPanelOpacity());
|
|
2798
3307
|
applyPartBlurs(rBlurs());
|
|
2799
3308
|
}
|
|
2800
3309
|
function teardownWp() {
|
|
@@ -2822,6 +3331,14 @@ window.__ModuleLoader__.load({
|
|
|
2822
3331
|
document.documentElement.style.removeProperty("--dsh-any-blur-settings");
|
|
2823
3332
|
document.documentElement.style.removeProperty("--dsh-any-blur-card-panels");
|
|
2824
3333
|
document.documentElement.style.removeProperty("--dsh-any-input-blur");
|
|
3334
|
+
document.documentElement.style.removeProperty("--dsh-any-part-blur-global");
|
|
3335
|
+
document.documentElement.style.removeProperty("--dsh-any-panel-bg-base");
|
|
3336
|
+
document.documentElement.style.removeProperty("--dsh-any-panel-layer-1");
|
|
3337
|
+
document.documentElement.style.removeProperty("--dsh-any-panel-layer-2");
|
|
3338
|
+
document.documentElement.style.removeProperty("--dsh-any-panel-layer-3");
|
|
3339
|
+
document.documentElement.style.removeProperty("--dsh-any-blur-panel");
|
|
3340
|
+
document.documentElement.style.removeProperty("--dsh-any-blur-prod");
|
|
3341
|
+
document.documentElement.style.removeProperty("--dsh-any-prod-pct");
|
|
2825
3342
|
for (const v of Object.values(OPACITY_VARS)) document.documentElement.style.removeProperty(v);
|
|
2826
3343
|
baseTokenKey = "";
|
|
2827
3344
|
lastBgKey = "";
|
|
@@ -2835,10 +3352,10 @@ window.__ModuleLoader__.load({
|
|
|
2835
3352
|
setBlur(frameEl, 0);
|
|
2836
3353
|
setBlur(sidebarEl, 0);
|
|
2837
3354
|
setBlur(centerEl, 0);
|
|
2838
|
-
setBlur(
|
|
3355
|
+
setBlur(rightEl, 0);
|
|
2839
3356
|
if (frameEl !== null) frameEl.style.removeProperty("background");
|
|
2840
3357
|
if (centerEl !== null) centerEl.style.removeProperty("background");
|
|
2841
|
-
if (
|
|
3358
|
+
if (rightEl !== null) rightEl.style.removeProperty("background");
|
|
2842
3359
|
stopWatchingParts();
|
|
2843
3360
|
}
|
|
2844
3361
|
/** Live wallpaper-opacity updates during slider drag (no full re-apply). */
|
|
@@ -2912,56 +3429,6 @@ window.__ModuleLoader__.load({
|
|
|
2912
3429
|
v.src = url;
|
|
2913
3430
|
});
|
|
2914
3431
|
}
|
|
2915
|
-
//#endregion
|
|
2916
|
-
//#region src/client/utils/image.ts
|
|
2917
|
-
/**
|
|
2918
|
-
* Read a chosen image file as a data URL WITHOUT re-encoding: the original
|
|
2919
|
-
* pixels are kept as-is (no canvas downscale / JPEG re-compression), so the
|
|
2920
|
-
* wallpaper is stored and displayed at full fidelity. The tradeoff is a larger
|
|
2921
|
-
* payload over the RPC channel and on disk for big images.
|
|
2922
|
-
*/
|
|
2923
|
-
function readImg(file, cb) {
|
|
2924
|
-
const r = new FileReader();
|
|
2925
|
-
r.onerror = () => cb(null);
|
|
2926
|
-
r.onload = () => cb(r.result);
|
|
2927
|
-
r.readAsDataURL(file);
|
|
2928
|
-
}
|
|
2929
|
-
/** Promised readImg variant for async flows. */
|
|
2930
|
-
function readImgAsync(file) {
|
|
2931
|
-
return new Promise((resolve) => readImg(file, resolve));
|
|
2932
|
-
}
|
|
2933
|
-
/** Build a small JPEG thumbnail (rotation-pool picker preview). Resolves null
|
|
2934
|
-
* when the image cannot be decoded. */
|
|
2935
|
-
function makeThumb(dataUrl, maxSide = 96) {
|
|
2936
|
-
return new Promise((resolve) => {
|
|
2937
|
-
const img = new Image();
|
|
2938
|
-
img.onerror = () => resolve(null);
|
|
2939
|
-
img.onload = () => {
|
|
2940
|
-
try {
|
|
2941
|
-
const iw = img.naturalWidth || img.width;
|
|
2942
|
-
const ih = img.naturalHeight || img.height;
|
|
2943
|
-
if (iw <= 0 || ih <= 0) {
|
|
2944
|
-
resolve(null);
|
|
2945
|
-
return;
|
|
2946
|
-
}
|
|
2947
|
-
const k = Math.min(1, maxSide / Math.max(iw, ih));
|
|
2948
|
-
const c = document.createElement("canvas");
|
|
2949
|
-
c.width = Math.max(1, Math.round(iw * k));
|
|
2950
|
-
c.height = Math.max(1, Math.round(ih * k));
|
|
2951
|
-
const g = c.getContext("2d");
|
|
2952
|
-
if (!g) {
|
|
2953
|
-
resolve(null);
|
|
2954
|
-
return;
|
|
2955
|
-
}
|
|
2956
|
-
g.drawImage(img, 0, 0, c.width, c.height);
|
|
2957
|
-
resolve(c.toDataURL("image/jpeg", .72));
|
|
2958
|
-
} catch {
|
|
2959
|
-
resolve(null);
|
|
2960
|
-
}
|
|
2961
|
-
};
|
|
2962
|
-
img.src = dataUrl;
|
|
2963
|
-
});
|
|
2964
|
-
}
|
|
2965
3432
|
const UI_CSS = `
|
|
2966
3433
|
/* The section is rendered INLINE inside the host settings dialog's content
|
|
2967
3434
|
* column: the host provides the modal chrome (backdrop, centering, closing).
|
|
@@ -3031,6 +3498,7 @@ window.__ModuleLoader__.load({
|
|
|
3031
3498
|
.dab-urlinput{flex:1;min-width:180px;height:34px;padding:0 12px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:12.5px;outline:none;transition:border-color .2s}
|
|
3032
3499
|
.dab-urlinput::placeholder{color:var(--dsw-alias-label-quaternary)}
|
|
3033
3500
|
.dab-urlinput: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)}
|
|
3501
|
+
.dab-urlrow{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:12px;animation:dab-list-in .3s cubic-bezier(.22,1,.36,1) both}
|
|
3034
3502
|
.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}
|
|
3035
3503
|
.dab-swatch-lg:hover{transform:scale(1.02)}
|
|
3036
3504
|
|
|
@@ -3041,11 +3509,19 @@ window.__ModuleLoader__.load({
|
|
|
3041
3509
|
.dab-btn:disabled{opacity:.5;cursor:not-allowed}
|
|
3042
3510
|
.dab-btn-primary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);border-color:transparent}
|
|
3043
3511
|
.dab-btn-danger{color:var(--dsw-alias-state-error-primary)}
|
|
3512
|
+
.dab-btn-danger-solid{background:var(--dsw-alias-state-error-primary);color:#fff;border-color:transparent}
|
|
3513
|
+
.dab-btn-danger-solid:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 85%,#000)}
|
|
3044
3514
|
.dab-btn-ghost{background:transparent;border-color:transparent;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
3045
3515
|
.dab-btn-ghost:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2);box-shadow:none}
|
|
3516
|
+
/* Soft solid button: subtle layer background instead of transparent ghost. */
|
|
3517
|
+
.dab-btn-soft{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
3518
|
+
.dab-btn-soft:hover:not(:disabled){background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary)}
|
|
3519
|
+
/* White background with error-red text (inverse of danger-solid). */
|
|
3520
|
+
.dab-btn-danger-inverted{background:#fff;color:var(--dsw-alias-state-error-primary);border-color:transparent}
|
|
3521
|
+
.dab-btn-danger-inverted:hover:not(:disabled){background:#f3f4f6}
|
|
3046
3522
|
.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}
|
|
3047
3523
|
.dab-chip-row{display:flex;flex-wrap:wrap;gap:8px}
|
|
3048
|
-
.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:
|
|
3524
|
+
.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:background-color .22s ease,border-color .22s ease,color .22s ease}
|
|
3049
3525
|
.dab-chip:hover{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary)}
|
|
3050
3526
|
.dab-chip.is-active{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);border-color:transparent}
|
|
3051
3527
|
|
|
@@ -3095,7 +3571,7 @@ window.__ModuleLoader__.load({
|
|
|
3095
3571
|
|
|
3096
3572
|
/* ── generated background type cards ─────────────────────────────────────── */
|
|
3097
3573
|
.dab-types{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
|
|
3098
|
-
.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}
|
|
3574
|
+
.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;animation:dab-type-in .42s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--i,0) * 60ms)}
|
|
3099
3575
|
.dab-type:hover{transform:translateY(-2px)}
|
|
3100
3576
|
.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)}
|
|
3101
3577
|
.dab-type-thumb{height:62px;border-radius:9px;overflow:hidden;position:relative}
|
|
@@ -3126,14 +3602,15 @@ window.__ModuleLoader__.load({
|
|
|
3126
3602
|
/* ── presets / profiles / schedule / rotation ────────────────────────────── */
|
|
3127
3603
|
.dab-row-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
3128
3604
|
.dab-preset-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
|
|
3129
|
-
.dab-preset{display:flex;flex-direction:column;align-items:flex-start;gap:3px;padding:12px;border:1.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:13px;cursor:pointer;font:inherit;text-align:left;transition:border-color .25s,transform .25s,box-shadow .25s}
|
|
3605
|
+
.dab-preset{display:flex;flex-direction:column;align-items:flex-start;gap:3px;padding:12px;border:1.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:13px;cursor:pointer;font:inherit;text-align:left;transition:border-color .25s,transform .25s,box-shadow .25s;animation:dab-row-in .34s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--i,0) * 45ms)}
|
|
3130
3606
|
.dab-preset:hover{transform:translateY(-2px);border-color:var(--dsw-alias-brand-primary);box-shadow:0 8px 22px -12px rgba(0,0,0,.3)}
|
|
3131
3607
|
.dab-preset-dot{width:22px;height:22px;border-radius:50%;margin-bottom:4px;box-shadow:inset 0 0 0 1px rgba(0,0,0,.12),0 4px 10px -4px rgba(0,0,0,.35);background:var(--dsw-alias-bg-layer-2);display:grid;place-items:center;color:var(--dsw-alias-label-tertiary)}
|
|
3132
3608
|
.dab-preset-name{font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
3133
3609
|
.dab-preset-desc{font-size:10.5px;line-height:1.45;color:var(--dsw-alias-label-tertiary)}
|
|
3134
|
-
.dab-profile-list{display:flex;flex-direction:column;gap:8px;margin-top:12px}
|
|
3135
|
-
.dab-profile-row{display:flex;align-items:center;gap:11px;padding:9px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2);transition:border-color .22s ease}
|
|
3136
|
-
.dab-profile-row
|
|
3610
|
+
.dab-profile-list{display:flex;flex-direction:column;gap:8px;margin-top:12px;animation:dab-list-in .38s cubic-bezier(.22,1,.36,1) both}
|
|
3611
|
+
.dab-profile-row{display:flex;align-items:center;gap:11px;padding:9px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2);animation:dab-row-in .34s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--i,0) * 45ms);transition:border-color .22s ease,background .22s ease}
|
|
3612
|
+
.dab-profile-row:hover{border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 28%,transparent);background:color-mix(in srgb,var(--dsw-alias-label-primary) 4%,var(--dsw-alias-bg-layer-2))}
|
|
3613
|
+
.dab-profile-row.is-active{border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 45%,transparent);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 7%,var(--dsw-alias-bg-layer-2))}
|
|
3137
3614
|
.dab-profile-dot{width:22px;height:22px;flex:none;border-radius:50%;background:var(--dsw-alias-bg-layer-1);box-shadow:inset 0 0 0 1px rgba(0,0,0,.12);display:grid;place-items:center;color:var(--dsw-alias-label-tertiary)}
|
|
3138
3615
|
.dab-profile-meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
|
|
3139
3616
|
.dab-profile-name{font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
@@ -3143,14 +3620,22 @@ window.__ModuleLoader__.load({
|
|
|
3143
3620
|
.dab-select:focus{border-color:var(--dsw-alias-brand-primary)}
|
|
3144
3621
|
.dab-timeinput{height:32px;padding:0 8px;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}
|
|
3145
3622
|
.dab-timeinput:focus{border-color:var(--dsw-alias-brand-primary)}
|
|
3146
|
-
|
|
3147
|
-
.
|
|
3623
|
+
/* Collapsible panel: animates grid-template-rows 0fr→1fr so the schedule
|
|
3624
|
+
block unfolds to its content height instead of popping in. visibility
|
|
3625
|
+
flips after the fold finishes (and instantly on open) so hidden inputs
|
|
3626
|
+
are never focusable. */
|
|
3627
|
+
.dab-schedule-wrap{display:grid;grid-template-rows:0fr;visibility:hidden;transition:grid-template-rows .42s cubic-bezier(.22,1,.36,1),visibility 0s linear .42s}
|
|
3628
|
+
.dab-schedule-wrap.is-open{grid-template-rows:1fr;visibility:visible;transition:grid-template-rows .42s cubic-bezier(.22,1,.36,1)}
|
|
3629
|
+
.dab-schedule-clip{overflow:hidden;min-height:0;opacity:0;transition:opacity .28s ease}
|
|
3630
|
+
.dab-schedule-wrap.is-open .dab-schedule-clip{opacity:1;transition:opacity .32s ease .1s}
|
|
3631
|
+
.dab-schedule-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;padding-top:12px}
|
|
3632
|
+
.dab-schedule-cell{min-width:0;animation:dab-row-in .34s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--i,0) * 60ms)}
|
|
3148
3633
|
.dab-time-row{display:flex;flex-direction:column;gap:9px}
|
|
3149
3634
|
.dab-time-label{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
3150
3635
|
.dab-time-label svg{color:var(--dsw-alias-brand-primary);flex:none}
|
|
3151
3636
|
.dab-time-label .dab-select,.dab-time-label .dab-timeinput{flex:1;min-width:0}
|
|
3152
3637
|
.dab-thumbstrip{display:flex;flex-wrap:wrap;gap:9px;margin-top:13px}
|
|
3153
|
-
.dab-thumb{position:relative;width:64px;height:44px;border-radius:9px;overflow:hidden;border:1.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);display:grid;place-items:center;color:var(--dsw-alias-label-tertiary)}
|
|
3638
|
+
.dab-thumb{position:relative;width:64px;height:44px;border-radius:9px;overflow:hidden;border:1.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);display:grid;place-items:center;color:var(--dsw-alias-label-tertiary);animation:dab-list-in .3s cubic-bezier(.22,1,.36,1) both}
|
|
3154
3639
|
.dab-thumb.is-current{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsw-alias-brand-primary) 25%,transparent)}
|
|
3155
3640
|
.dab-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
|
|
3156
3641
|
.dab-thumb-x{position:absolute;top:2px;right:2px;width:16px;height:16px;border:0;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;cursor:pointer;display:grid;place-items:center;opacity:0;transition:opacity .18s ease,background .18s ease;padding:0}
|
|
@@ -3184,6 +3669,9 @@ window.__ModuleLoader__.load({
|
|
|
3184
3669
|
@keyframes dab-fade-in{from{opacity:0}to{opacity:1}}
|
|
3185
3670
|
@keyframes dab-page-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
|
|
3186
3671
|
@keyframes dab-rise-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
|
|
3672
|
+
@keyframes dab-list-in{from{opacity:0;transform:translateY(-7px)}to{opacity:1;transform:none}}
|
|
3673
|
+
@keyframes dab-row-in{from{opacity:0;transform:translateY(6px) scale(.985)}to{opacity:1;transform:none}}
|
|
3674
|
+
@keyframes dab-type-in{from{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}
|
|
3187
3675
|
@keyframes dab-orb-in{from{opacity:0;transform:scale(.82)}to{opacity:1;transform:none}}
|
|
3188
3676
|
@keyframes dab-spin{to{transform:rotate(360deg)}}
|
|
3189
3677
|
@keyframes dab-rotate{to{transform:rotate(360deg)}}
|
|
@@ -3322,6 +3810,12 @@ window.__ModuleLoader__.load({
|
|
|
3322
3810
|
})
|
|
3323
3811
|
]
|
|
3324
3812
|
});
|
|
3813
|
+
/** Bottom workbench panel (dsh-better-sidebar): a window with a docked strip. */
|
|
3814
|
+
const PanelIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
3815
|
+
size,
|
|
3816
|
+
className,
|
|
3817
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.4 2.7h11.2v10.6H2.4z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.4 10.1h11.2" })]
|
|
3818
|
+
});
|
|
3325
3819
|
const SlidersIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
3326
3820
|
size,
|
|
3327
3821
|
className,
|
|
@@ -3491,6 +3985,16 @@ window.__ModuleLoader__.load({
|
|
|
3491
3985
|
className,
|
|
3492
3986
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" })
|
|
3493
3987
|
});
|
|
3988
|
+
const PlayIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
3989
|
+
size,
|
|
3990
|
+
className,
|
|
3991
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.2 3.4l7.4 4.6-7.4 4.6V3.4z" })
|
|
3992
|
+
});
|
|
3993
|
+
const PauseIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
3994
|
+
size,
|
|
3995
|
+
className,
|
|
3996
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.4 3.4v9.2M10.6 3.4v9.2" })
|
|
3997
|
+
});
|
|
3494
3998
|
//#endregion
|
|
3495
3999
|
//#region src/client/components/ErrorBoundary.tsx
|
|
3496
4000
|
/**
|
|
@@ -4134,12 +4638,12 @@ window.__ModuleLoader__.load({
|
|
|
4134
4638
|
}
|
|
4135
4639
|
function ColorPage({ p, notify }) {
|
|
4136
4640
|
const { t, hue, sat, lit, setColor, extractColor, setSchemeOverride, useStore } = p;
|
|
4137
|
-
const
|
|
4138
|
-
const
|
|
4139
|
-
const
|
|
4641
|
+
const storeUrl = useStore((s) => s.url);
|
|
4642
|
+
const schemeOverride = useStore((s) => s.schemeOverride);
|
|
4643
|
+
const color = useStore((s) => s.color);
|
|
4140
4644
|
const [pickerOpen, setPickerOpen] = (0, react.useState)(false);
|
|
4141
4645
|
const [extracting, setExtracting] = (0, react.useState)(false);
|
|
4142
|
-
const wheel =
|
|
4646
|
+
const wheel = color ?? [
|
|
4143
4647
|
hue,
|
|
4144
4648
|
sat,
|
|
4145
4649
|
lit
|
|
@@ -4411,6 +4915,65 @@ window.__ModuleLoader__.load({
|
|
|
4411
4915
|
});
|
|
4412
4916
|
}
|
|
4413
4917
|
//#endregion
|
|
4918
|
+
//#region src/client/env.ts
|
|
4919
|
+
/**
|
|
4920
|
+
* Host capability detection — which third-party plugins are actually present.
|
|
4921
|
+
*
|
|
4922
|
+
* The host exposes no plugin registry on the client context, so presence is
|
|
4923
|
+
* inferred from the stable DOM markers those plugins mount. Detection runs
|
|
4924
|
+
* once (plugin boot) and is re-armed by a cheap body-level MutationObserver so
|
|
4925
|
+
* a plugin that mounts after this one still flips the cached verdict. The
|
|
4926
|
+
* verdict is sticky-true for the session: whether a detected plugin's surface
|
|
4927
|
+
* is currently collapsed must not hide the settings that govern it.
|
|
4928
|
+
*/
|
|
4929
|
+
/** Persistent container + surfaces dsh-better-sidebar mounts when loaded.
|
|
4930
|
+
* `[data-dsh-better-sidebar]` is the plugin's host wrapper — always appended
|
|
4931
|
+
* to document.body on mount — so it is the authoritative "plugin is loaded"
|
|
4932
|
+
* signal and the body childList observer fires exactly when it appears. The
|
|
4933
|
+
* panel surfaces we actually style are listed only as reinforcement: they are
|
|
4934
|
+
* conditionally rendered (collapsed panels drop them), so they must never be
|
|
4935
|
+
* the sole signal. */
|
|
4936
|
+
const BETTER_SIDEBAR_MARKERS = [
|
|
4937
|
+
"[data-dsh-better-sidebar]",
|
|
4938
|
+
"[data-dsh-panel-host]",
|
|
4939
|
+
"[data-dsh-bottom-panel]",
|
|
4940
|
+
"[data-sidebar-right-panel]"
|
|
4941
|
+
];
|
|
4942
|
+
let betterSidebar = false;
|
|
4943
|
+
const subs = /* @__PURE__ */ new Set();
|
|
4944
|
+
/** Read snapshot of whether the better-sidebar plugin is present. */
|
|
4945
|
+
function rBetterSidebar() {
|
|
4946
|
+
return betterSidebar;
|
|
4947
|
+
}
|
|
4948
|
+
function emit() {
|
|
4949
|
+
subs.forEach((cb) => cb());
|
|
4950
|
+
}
|
|
4951
|
+
function sync() {
|
|
4952
|
+
if (document.querySelector(BETTER_SIDEBAR_MARKERS.join(",")) !== null && !betterSidebar) {
|
|
4953
|
+
betterSidebar = true;
|
|
4954
|
+
emit();
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
/** React hook: re-renders the caller when better-sidebar presence changes. */
|
|
4958
|
+
function useBetterSidebar() {
|
|
4959
|
+
return (0, react.useSyncExternalStore)(subscribe, rBetterSidebar);
|
|
4960
|
+
}
|
|
4961
|
+
/** Subscribe to verdict changes. */
|
|
4962
|
+
function subscribe(cb) {
|
|
4963
|
+
subs.add(cb);
|
|
4964
|
+
return () => {
|
|
4965
|
+
subs.delete(cb);
|
|
4966
|
+
};
|
|
4967
|
+
}
|
|
4968
|
+
/** Start detection (probe once, then watch body mounts). Returns an
|
|
4969
|
+
* unsubscribe for plugin teardown. */
|
|
4970
|
+
function startBetterSidebarWatch() {
|
|
4971
|
+
sync();
|
|
4972
|
+
const observer = new MutationObserver(sync);
|
|
4973
|
+
observer.observe(document.body, { childList: true });
|
|
4974
|
+
return () => observer.disconnect();
|
|
4975
|
+
}
|
|
4976
|
+
//#endregion
|
|
4414
4977
|
//#region src/client/components/pages/InterfacePage.tsx
|
|
4415
4978
|
const PARTS = [
|
|
4416
4979
|
{
|
|
@@ -4447,10 +5010,22 @@ window.__ModuleLoader__.load({
|
|
|
4447
5010
|
isTrajectory: true,
|
|
4448
5011
|
labelKey: "uiTrajectory",
|
|
4449
5012
|
Icon: TrajectoryIcon
|
|
5013
|
+
},
|
|
5014
|
+
{
|
|
5015
|
+
isProduced: true,
|
|
5016
|
+
labelKey: "uiProduced",
|
|
5017
|
+
Icon: TextIcon
|
|
5018
|
+
},
|
|
5019
|
+
{
|
|
5020
|
+
isPanel: true,
|
|
5021
|
+
labelKey: "uiPanelRegion",
|
|
5022
|
+
Icon: PanelIcon
|
|
4450
5023
|
}
|
|
4451
5024
|
];
|
|
4452
5025
|
function InterfacePage({ p }) {
|
|
4453
|
-
const { t, setOps, setBlurs, setSop } = p;
|
|
5026
|
+
const { t, setOps, setBlurs, setSop, setPanelOp } = p;
|
|
5027
|
+
const hasBetterSidebar = useBetterSidebar();
|
|
5028
|
+
const parts = PARTS.filter((part) => !(part.isPanel && !hasBetterSidebar));
|
|
4454
5029
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
4455
5030
|
className: "dab-head dab-rise",
|
|
4456
5031
|
style: { "--d": 0 },
|
|
@@ -4470,11 +5045,11 @@ window.__ModuleLoader__.load({
|
|
|
4470
5045
|
]
|
|
4471
5046
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4472
5047
|
className: "dab-grid-parts",
|
|
4473
|
-
children:
|
|
4474
|
-
const { labelKey, Icon, isSettings, isChat, isTrajectory } = part;
|
|
5048
|
+
children: parts.map((part, i) => {
|
|
5049
|
+
const { labelKey, Icon, isSettings, isChat, isTrajectory, isPanel, isProduced } = part;
|
|
4475
5050
|
const opKey = part.opKey;
|
|
4476
|
-
const blurKey = isChat ? "chat" : isTrajectory ? "trajectory" : isSettings ? "settings" : opKey;
|
|
4477
|
-
const opacity = isChat ? rChatTextOpacity() : isTrajectory ? rTrajectoryOpacity() : isSettings ? rSop() : rOps()[opKey];
|
|
5051
|
+
const blurKey = isChat ? "chat" : isTrajectory ? "trajectory" : isSettings ? "settings" : isPanel ? "panel" : isProduced ? "produced" : opKey;
|
|
5052
|
+
const opacity = isChat ? rChatTextOpacity() : isTrajectory ? rTrajectoryOpacity() : isSettings ? rSop() : isPanel ? rPanelOpacity() : isProduced ? rProducedOpacity() : rOps()[opKey];
|
|
4478
5053
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4479
5054
|
className: "dab-card dab-card-hover dab-rise",
|
|
4480
5055
|
style: { "--d": i + 1 },
|
|
@@ -4514,6 +5089,12 @@ window.__ModuleLoader__.load({
|
|
|
4514
5089
|
} else if (isSettings) {
|
|
4515
5090
|
cfg.settingsOpacity = op;
|
|
4516
5091
|
applySettingsOverrides(op);
|
|
5092
|
+
} else if (isPanel) {
|
|
5093
|
+
cfg.panelOpacity = op;
|
|
5094
|
+
applyPanelOverrides(op);
|
|
5095
|
+
} else if (isProduced) {
|
|
5096
|
+
cfg.producedOpacity = op;
|
|
5097
|
+
applyProduced();
|
|
4517
5098
|
} else {
|
|
4518
5099
|
const ops = { ...rOps() };
|
|
4519
5100
|
ops[opKey] = op;
|
|
@@ -4533,7 +5114,12 @@ window.__ModuleLoader__.load({
|
|
|
4533
5114
|
applyTrajectoryOverrides(op);
|
|
4534
5115
|
saveConfig();
|
|
4535
5116
|
} else if (isSettings) setSop(op);
|
|
4536
|
-
else
|
|
5117
|
+
else if (isPanel) setPanelOp(op);
|
|
5118
|
+
else if (isProduced) {
|
|
5119
|
+
cfg.producedOpacity = op;
|
|
5120
|
+
applyProduced();
|
|
5121
|
+
saveConfig();
|
|
5122
|
+
} else {
|
|
4537
5123
|
const ops = { ...rOps() };
|
|
4538
5124
|
ops[opKey] = op;
|
|
4539
5125
|
setOps(ops);
|
|
@@ -4781,13 +5367,12 @@ window.__ModuleLoader__.load({
|
|
|
4781
5367
|
}
|
|
4782
5368
|
];
|
|
4783
5369
|
function BackgroundPage({ p }) {
|
|
4784
|
-
const { t,
|
|
4785
|
-
const
|
|
4786
|
-
const
|
|
4787
|
-
const
|
|
4788
|
-
const
|
|
4789
|
-
const
|
|
4790
|
-
const rotation = store.rotation;
|
|
5370
|
+
const { t, setWpFromServer, setVideo, setWop, setBl, setBgType, setGeneratedBg, regenerateBg, setRegenerateOnReload, setRotation, addRotationItems, removeRotationItem, rotateNow, setVideoFromUrl, useStore } = p;
|
|
5371
|
+
const storeUrl = useStore((s) => s.url);
|
|
5372
|
+
const backgroundType = useStore((s) => s.backgroundType);
|
|
5373
|
+
const generatedBg = useStore((s) => s.generatedBg);
|
|
5374
|
+
const regenerateOnReload = useStore((s) => s.regenerateOnReload);
|
|
5375
|
+
const rotation = useStore((s) => s.rotation);
|
|
4791
5376
|
const fileRef = (0, react.useRef)(null);
|
|
4792
5377
|
const rotFileRef = (0, react.useRef)(null);
|
|
4793
5378
|
const [editorOpen, setEditorOpen] = (0, react.useState)(false);
|
|
@@ -4803,13 +5388,22 @@ window.__ModuleLoader__.load({
|
|
|
4803
5388
|
const isStatic = backgroundType === "image" || isVideo;
|
|
4804
5389
|
const isGenerated = !isStatic;
|
|
4805
5390
|
const activeGenType = isGenerated && generatedBg ? generatedBg.type : "mesh";
|
|
5391
|
+
const [paused, setPaused] = (0, react.useState)(false);
|
|
5392
|
+
const genPreset = generatedBg !== null && generatedBg.type !== "mesh" ? generatedBg.preset : void 0;
|
|
5393
|
+
(0, react.useEffect)(() => {
|
|
5394
|
+
setPaused(false);
|
|
5395
|
+
}, [
|
|
5396
|
+
activeGenType,
|
|
5397
|
+
genPreset,
|
|
5398
|
+
generatedBg?.seed
|
|
5399
|
+
]);
|
|
4806
5400
|
const onFileSelect = (f) => {
|
|
4807
5401
|
if (f.type.startsWith("video/")) {
|
|
4808
5402
|
setVideo(f, f.type);
|
|
4809
5403
|
return;
|
|
4810
5404
|
}
|
|
4811
|
-
|
|
4812
|
-
if (
|
|
5405
|
+
uploadWallpaper(f).then((ok) => {
|
|
5406
|
+
if (ok) setWpFromServer(WALLPAPER_SERVE_URL);
|
|
4813
5407
|
});
|
|
4814
5408
|
};
|
|
4815
5409
|
const onDrop = (e) => {
|
|
@@ -4827,12 +5421,17 @@ window.__ModuleLoader__.load({
|
|
|
4827
5421
|
setUrlBusy(true);
|
|
4828
5422
|
setUrlErr(null);
|
|
4829
5423
|
const r = await setWallpaperFromUrl(u);
|
|
4830
|
-
setUrlBusy(false);
|
|
4831
5424
|
if (r.ok) {
|
|
4832
|
-
|
|
5425
|
+
setWpFromServer(r.wallpaperUrl ?? null);
|
|
4833
5426
|
setUrlOpen(false);
|
|
4834
5427
|
setUrlVal("");
|
|
5428
|
+
} else if (r.error === "not an image") {
|
|
5429
|
+
if (await setVideoFromUrl(u)) {
|
|
5430
|
+
setUrlOpen(false);
|
|
5431
|
+
setUrlVal("");
|
|
5432
|
+
} else setUrlErr(t("bgUrlVideoFail"));
|
|
4835
5433
|
} else setUrlErr(r.error === "invalid url" || r.error === "unsupported scheme" ? t("bgUrlBadHttp") : r.error ?? t("bgUrlFail"));
|
|
5434
|
+
setUrlBusy(false);
|
|
4836
5435
|
};
|
|
4837
5436
|
const switchToStatic = () => {
|
|
4838
5437
|
if (isStatic) return;
|
|
@@ -4847,10 +5446,12 @@ window.__ModuleLoader__.load({
|
|
|
4847
5446
|
};
|
|
4848
5447
|
const setGenType = (type) => {
|
|
4849
5448
|
if (type === activeGenType) return;
|
|
5449
|
+
setPaused(false);
|
|
4850
5450
|
setBgType(type);
|
|
4851
5451
|
};
|
|
4852
5452
|
const ensureGenParams = () => generatedBg ?? defaultParamsFor(activeGenType);
|
|
4853
5453
|
const updateGenerated = (patch) => {
|
|
5454
|
+
setPaused(false);
|
|
4854
5455
|
setGeneratedBg({
|
|
4855
5456
|
...ensureGenParams(),
|
|
4856
5457
|
...patch
|
|
@@ -4881,9 +5482,13 @@ window.__ModuleLoader__.load({
|
|
|
4881
5482
|
case "aurora": return t("presetAurora");
|
|
4882
5483
|
case "nebula": return t("presetNebula");
|
|
4883
5484
|
case "noise": return t("presetNoise");
|
|
5485
|
+
case "starfield": return t("presetStarfield");
|
|
4884
5486
|
case "dots": return t("presetDots");
|
|
4885
5487
|
case "waves": return t("presetWaves");
|
|
4886
5488
|
case "poly": return t("presetPoly");
|
|
5489
|
+
case "rain": return t("presetRain");
|
|
5490
|
+
case "contour": return t("presetContour");
|
|
5491
|
+
case "meta": return t("presetMeta");
|
|
4887
5492
|
default: return key;
|
|
4888
5493
|
}
|
|
4889
5494
|
};
|
|
@@ -4938,6 +5543,7 @@ window.__ModuleLoader__.load({
|
|
|
4938
5543
|
type: "button",
|
|
4939
5544
|
className: "dab-btn",
|
|
4940
5545
|
onClick: () => {
|
|
5546
|
+
setPaused(false);
|
|
4941
5547
|
regenerateBg();
|
|
4942
5548
|
setSpinTick((x) => x + 1);
|
|
4943
5549
|
},
|
|
@@ -4945,7 +5551,7 @@ window.__ModuleLoader__.load({
|
|
|
4945
5551
|
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4946
5552
|
type: "button",
|
|
4947
5553
|
className: "dab-btn dab-btn-danger",
|
|
4948
|
-
onClick: () =>
|
|
5554
|
+
onClick: () => setWpFromServer(null),
|
|
4949
5555
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 13 }), t("bgRemove")]
|
|
4950
5556
|
})]
|
|
4951
5557
|
})
|
|
@@ -5006,7 +5612,7 @@ window.__ModuleLoader__.load({
|
|
|
5006
5612
|
}),
|
|
5007
5613
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5008
5614
|
type: "button",
|
|
5009
|
-
className: "dab-btn dab-btn-
|
|
5615
|
+
className: "dab-btn dab-btn-soft",
|
|
5010
5616
|
onClick: () => setUrlOpen((o) => !o),
|
|
5011
5617
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkIcon, { size: 14 }), t("bgFromUrl")]
|
|
5012
5618
|
}),
|
|
@@ -5019,21 +5625,14 @@ window.__ModuleLoader__.load({
|
|
|
5019
5625
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditIcon, { size: 14 }), t("bgEdit")]
|
|
5020
5626
|
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5021
5627
|
type: "button",
|
|
5022
|
-
className: "dab-btn dab-btn-
|
|
5023
|
-
onClick: () =>
|
|
5628
|
+
className: "dab-btn dab-btn-danger-inverted",
|
|
5629
|
+
onClick: () => setWpFromServer(null),
|
|
5024
5630
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 14 }), t("bgRemove")]
|
|
5025
5631
|
})] }) : null
|
|
5026
5632
|
]
|
|
5027
5633
|
}),
|
|
5028
5634
|
urlOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5029
5635
|
className: "dab-urlrow",
|
|
5030
|
-
style: {
|
|
5031
|
-
marginTop: 12,
|
|
5032
|
-
display: "flex",
|
|
5033
|
-
flexWrap: "wrap",
|
|
5034
|
-
gap: 8,
|
|
5035
|
-
alignItems: "center"
|
|
5036
|
-
},
|
|
5037
5636
|
children: [
|
|
5038
5637
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5039
5638
|
type: "text",
|
|
@@ -5213,9 +5812,10 @@ window.__ModuleLoader__.load({
|
|
|
5213
5812
|
children: [
|
|
5214
5813
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5215
5814
|
className: "dab-types",
|
|
5216
|
-
children: typeMeta.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5815
|
+
children: typeMeta.map((m, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5217
5816
|
type: "button",
|
|
5218
5817
|
className: `dab-type${activeGenType === m.type ? " is-active" : ""}`,
|
|
5818
|
+
style: { "--i": i },
|
|
5219
5819
|
onClick: () => setGenType(m.type),
|
|
5220
5820
|
children: [
|
|
5221
5821
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: `dab-type-thumb ${m.thumb}` }),
|
|
@@ -5244,7 +5844,8 @@ window.__ModuleLoader__.load({
|
|
|
5244
5844
|
children: [
|
|
5245
5845
|
"aurora",
|
|
5246
5846
|
"nebula",
|
|
5247
|
-
"noise"
|
|
5847
|
+
"noise",
|
|
5848
|
+
"starfield"
|
|
5248
5849
|
].map((pr) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5249
5850
|
type: "button",
|
|
5250
5851
|
className: `dab-chip${generatedBg.preset === pr ? " is-active" : ""}`,
|
|
@@ -5263,7 +5864,10 @@ window.__ModuleLoader__.load({
|
|
|
5263
5864
|
children: [
|
|
5264
5865
|
"dots",
|
|
5265
5866
|
"waves",
|
|
5266
|
-
"poly"
|
|
5867
|
+
"poly",
|
|
5868
|
+
"rain",
|
|
5869
|
+
"contour",
|
|
5870
|
+
"meta"
|
|
5267
5871
|
].map((pr) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5268
5872
|
type: "button",
|
|
5269
5873
|
className: `dab-chip${generatedBg.preset === pr ? " is-active" : ""}`,
|
|
@@ -5353,11 +5957,26 @@ window.__ModuleLoader__.load({
|
|
|
5353
5957
|
onClick: () => setRegenerateOnReload(!regenerateOnReload),
|
|
5354
5958
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dab-toggle-knob" })
|
|
5355
5959
|
}),
|
|
5960
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5961
|
+
type: "button",
|
|
5962
|
+
className: "dab-btn",
|
|
5963
|
+
onClick: () => {
|
|
5964
|
+
if (paused) {
|
|
5965
|
+
resumeGeneratedBg();
|
|
5966
|
+
setPaused(false);
|
|
5967
|
+
} else {
|
|
5968
|
+
pauseGeneratedBg();
|
|
5969
|
+
setPaused(true);
|
|
5970
|
+
}
|
|
5971
|
+
},
|
|
5972
|
+
children: [paused ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlayIcon, { size: 13 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PauseIcon, { size: 13 }), paused ? t("bgResume") : t("bgPause")]
|
|
5973
|
+
}),
|
|
5356
5974
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5357
5975
|
type: "button",
|
|
5358
5976
|
className: "dab-btn dab-btn-primary",
|
|
5359
5977
|
onClick: () => {
|
|
5360
5978
|
regenerateBg();
|
|
5979
|
+
setPaused(false);
|
|
5361
5980
|
setSpinTick((x) => x + 1);
|
|
5362
5981
|
},
|
|
5363
5982
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -5452,7 +6071,9 @@ window.__ModuleLoader__.load({
|
|
|
5452
6071
|
settings: 0,
|
|
5453
6072
|
chat: 0,
|
|
5454
6073
|
trajectory: 0,
|
|
5455
|
-
input: 0
|
|
6074
|
+
input: 0,
|
|
6075
|
+
panel: 0,
|
|
6076
|
+
produced: 0
|
|
5456
6077
|
};
|
|
5457
6078
|
const BUILTIN_PRESETS = [
|
|
5458
6079
|
{
|
|
@@ -5470,7 +6091,9 @@ window.__ModuleLoader__.load({
|
|
|
5470
6091
|
wallpaperOpacity: 1,
|
|
5471
6092
|
blur: 0,
|
|
5472
6093
|
chatTextOpacity: 0,
|
|
5473
|
-
trajectoryOpacity: 1
|
|
6094
|
+
trajectoryOpacity: 1,
|
|
6095
|
+
panelOpacity: 1,
|
|
6096
|
+
producedOpacity: 1
|
|
5474
6097
|
}
|
|
5475
6098
|
},
|
|
5476
6099
|
{
|
|
@@ -5494,13 +6117,16 @@ window.__ModuleLoader__.load({
|
|
|
5494
6117
|
card: 12,
|
|
5495
6118
|
settings: 20,
|
|
5496
6119
|
trajectory: 8,
|
|
5497
|
-
input: 14
|
|
6120
|
+
input: 14,
|
|
6121
|
+
panel: 12
|
|
5498
6122
|
},
|
|
5499
6123
|
settingsOpacity: .88,
|
|
5500
6124
|
wallpaperOpacity: 1,
|
|
5501
6125
|
blur: 0,
|
|
5502
6126
|
chatTextOpacity: 0,
|
|
5503
|
-
trajectoryOpacity: .85
|
|
6127
|
+
trajectoryOpacity: .85,
|
|
6128
|
+
panelOpacity: .85,
|
|
6129
|
+
producedOpacity: 1
|
|
5504
6130
|
}
|
|
5505
6131
|
},
|
|
5506
6132
|
{
|
|
@@ -5518,7 +6144,9 @@ window.__ModuleLoader__.load({
|
|
|
5518
6144
|
wallpaperOpacity: 1,
|
|
5519
6145
|
blur: 0,
|
|
5520
6146
|
chatTextOpacity: 0,
|
|
5521
|
-
trajectoryOpacity: 1
|
|
6147
|
+
trajectoryOpacity: 1,
|
|
6148
|
+
panelOpacity: 1,
|
|
6149
|
+
producedOpacity: 1
|
|
5522
6150
|
}
|
|
5523
6151
|
},
|
|
5524
6152
|
{
|
|
@@ -5542,13 +6170,16 @@ window.__ModuleLoader__.load({
|
|
|
5542
6170
|
card: 14,
|
|
5543
6171
|
settings: 22,
|
|
5544
6172
|
trajectory: 10,
|
|
5545
|
-
input: 16
|
|
6173
|
+
input: 16,
|
|
6174
|
+
panel: 16
|
|
5546
6175
|
},
|
|
5547
6176
|
settingsOpacity: .85,
|
|
5548
6177
|
wallpaperOpacity: .92,
|
|
5549
6178
|
blur: 2,
|
|
5550
6179
|
chatTextOpacity: 0,
|
|
5551
|
-
trajectoryOpacity: .8
|
|
6180
|
+
trajectoryOpacity: .8,
|
|
6181
|
+
panelOpacity: .8,
|
|
6182
|
+
producedOpacity: 1
|
|
5552
6183
|
}
|
|
5553
6184
|
},
|
|
5554
6185
|
{
|
|
@@ -5572,13 +6203,16 @@ window.__ModuleLoader__.load({
|
|
|
5572
6203
|
card: 10,
|
|
5573
6204
|
settings: 18,
|
|
5574
6205
|
trajectory: 6,
|
|
5575
|
-
input: 20
|
|
6206
|
+
input: 20,
|
|
6207
|
+
panel: 18
|
|
5576
6208
|
},
|
|
5577
6209
|
settingsOpacity: .8,
|
|
5578
6210
|
wallpaperOpacity: 1,
|
|
5579
6211
|
blur: 0,
|
|
5580
6212
|
chatTextOpacity: 0,
|
|
5581
|
-
trajectoryOpacity: .78
|
|
6213
|
+
trajectoryOpacity: .78,
|
|
6214
|
+
panelOpacity: .75,
|
|
6215
|
+
producedOpacity: 1
|
|
5582
6216
|
}
|
|
5583
6217
|
},
|
|
5584
6218
|
{
|
|
@@ -5600,7 +6234,9 @@ window.__ModuleLoader__.load({
|
|
|
5600
6234
|
wallpaperOpacity: 1,
|
|
5601
6235
|
blur: 0,
|
|
5602
6236
|
chatTextOpacity: 0,
|
|
5603
|
-
trajectoryOpacity: 1
|
|
6237
|
+
trajectoryOpacity: 1,
|
|
6238
|
+
panelOpacity: 1,
|
|
6239
|
+
producedOpacity: 1
|
|
5604
6240
|
}
|
|
5605
6241
|
}
|
|
5606
6242
|
];
|
|
@@ -5618,10 +6254,9 @@ window.__ModuleLoader__.load({
|
|
|
5618
6254
|
}
|
|
5619
6255
|
function ProfilePage({ p, notify }) {
|
|
5620
6256
|
const { t, exportTheme, importTheme, saveProfile, applyProfile, deleteProfile, applyPreset, setSchedule, useStore } = p;
|
|
5621
|
-
const
|
|
5622
|
-
const
|
|
5623
|
-
const
|
|
5624
|
-
const activeProfile = store.activeProfile;
|
|
6257
|
+
const profiles = useStore((s) => s.profiles);
|
|
6258
|
+
const schedule = useStore((s) => s.schedule);
|
|
6259
|
+
const activeProfile = useStore((s) => s.activeProfile);
|
|
5625
6260
|
const importRef = (0, react.useRef)(null);
|
|
5626
6261
|
const [nameOpen, setNameOpen] = (0, react.useState)(false);
|
|
5627
6262
|
const [nameVal, setNameVal] = (0, react.useState)("");
|
|
@@ -5694,11 +6329,12 @@ window.__ModuleLoader__.load({
|
|
|
5694
6329
|
children: t("presetGalleryTitle")
|
|
5695
6330
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5696
6331
|
className: "dab-preset-grid",
|
|
5697
|
-
children: BUILTIN_PRESETS.map((ps) => {
|
|
6332
|
+
children: BUILTIN_PRESETS.map((ps, i) => {
|
|
5698
6333
|
const hex = hexOf(ps.appearance.color);
|
|
5699
6334
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5700
6335
|
type: "button",
|
|
5701
6336
|
className: "dab-preset",
|
|
6337
|
+
style: { "--i": i },
|
|
5702
6338
|
onClick: () => {
|
|
5703
6339
|
applyPreset(ps.appearance);
|
|
5704
6340
|
notify(t("presetApplied"));
|
|
@@ -5741,13 +6377,6 @@ window.__ModuleLoader__.load({
|
|
|
5741
6377
|
}),
|
|
5742
6378
|
nameOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5743
6379
|
className: "dab-urlrow",
|
|
5744
|
-
style: {
|
|
5745
|
-
marginTop: 12,
|
|
5746
|
-
display: "flex",
|
|
5747
|
-
flexWrap: "wrap",
|
|
5748
|
-
gap: 8,
|
|
5749
|
-
alignItems: "center"
|
|
5750
|
-
},
|
|
5751
6380
|
children: [
|
|
5752
6381
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5753
6382
|
type: "text",
|
|
@@ -5788,8 +6417,9 @@ window.__ModuleLoader__.load({
|
|
|
5788
6417
|
children: t("profilesEmpty")
|
|
5789
6418
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5790
6419
|
className: "dab-profile-list",
|
|
5791
|
-
children: profiles.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6420
|
+
children: profiles.map((entry, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5792
6421
|
className: `dab-profile-row${activeProfile === entry.id ? " is-active" : ""}`,
|
|
6422
|
+
style: { "--i": i },
|
|
5793
6423
|
children: [
|
|
5794
6424
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProfileDot, { entry }),
|
|
5795
6425
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5810,10 +6440,10 @@ window.__ModuleLoader__.load({
|
|
|
5810
6440
|
}),
|
|
5811
6441
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5812
6442
|
type: "button",
|
|
5813
|
-
className: `dab-btn dab-btn-
|
|
6443
|
+
className: `dab-btn dab-btn-danger-solid${confirmId === entry.id ? " dab-btn-confirm" : ""}`,
|
|
5814
6444
|
title: t("profileDelete"),
|
|
5815
6445
|
onClick: () => onDelete(entry),
|
|
5816
|
-
children: confirmId === entry.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, { size: 13 }), t("profileDeleteConfirm")] }) :
|
|
6446
|
+
children: confirmId === entry.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, { size: 13 }), t("profileDeleteConfirm")] }) : t("profileDelete")
|
|
5817
6447
|
})
|
|
5818
6448
|
]
|
|
5819
6449
|
}, entry.id))
|
|
@@ -5844,93 +6474,103 @@ window.__ModuleLoader__.load({
|
|
|
5844
6474
|
style: { marginTop: 8 },
|
|
5845
6475
|
children: t("scheduleHint")
|
|
5846
6476
|
}),
|
|
5847
|
-
|
|
5848
|
-
className:
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
children: [
|
|
5880
|
-
|
|
5881
|
-
t("
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
className: "dab-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
6477
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6478
|
+
className: `dab-schedule-wrap${schedule.enabled ? " is-open" : ""}`,
|
|
6479
|
+
"aria-hidden": !schedule.enabled,
|
|
6480
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6481
|
+
className: "dab-schedule-clip",
|
|
6482
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6483
|
+
className: "dab-schedule-grid",
|
|
6484
|
+
children: [
|
|
6485
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6486
|
+
className: "dab-schedule-cell",
|
|
6487
|
+
style: { "--i": 0 },
|
|
6488
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6489
|
+
className: "dab-swatch-title",
|
|
6490
|
+
children: t("scheduleMode")
|
|
6491
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6492
|
+
className: "dab-chip-row",
|
|
6493
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
6494
|
+
type: "button",
|
|
6495
|
+
className: `dab-chip${schedule.mode === "time" ? " is-active" : ""}`,
|
|
6496
|
+
onClick: () => patchSchedule({ mode: "time" }),
|
|
6497
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClockIcon, { size: 13 }), t("scheduleModeTime")]
|
|
6498
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6499
|
+
type: "button",
|
|
6500
|
+
className: `dab-chip${schedule.mode === "system" ? " is-active" : ""}`,
|
|
6501
|
+
onClick: () => patchSchedule({ mode: "system" }),
|
|
6502
|
+
children: t("scheduleModeSystem")
|
|
6503
|
+
})]
|
|
6504
|
+
})]
|
|
6505
|
+
}),
|
|
6506
|
+
schedule.mode === "time" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6507
|
+
className: "dab-schedule-cell",
|
|
6508
|
+
style: { "--i": 1 },
|
|
6509
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6510
|
+
className: "dab-swatch-title",
|
|
6511
|
+
children: t("scheduleTimes")
|
|
6512
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6513
|
+
className: "dab-time-row",
|
|
6514
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6515
|
+
className: "dab-time-label",
|
|
6516
|
+
children: [
|
|
6517
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SunIcon, { size: 13 }),
|
|
6518
|
+
t("scheduleDayStart"),
|
|
6519
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6520
|
+
type: "time",
|
|
6521
|
+
className: "dab-timeinput",
|
|
6522
|
+
value: schedule.dayStart,
|
|
6523
|
+
onChange: (e) => {
|
|
6524
|
+
if (/^([01]\d|2[0-3]):[0-5]\d$/.test(e.target.value)) patchSchedule({ dayStart: e.target.value });
|
|
6525
|
+
}
|
|
6526
|
+
})
|
|
6527
|
+
]
|
|
6528
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6529
|
+
className: "dab-time-label",
|
|
6530
|
+
children: [
|
|
6531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoonIcon, { size: 13 }),
|
|
6532
|
+
t("scheduleNightStart"),
|
|
6533
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6534
|
+
type: "time",
|
|
6535
|
+
className: "dab-timeinput",
|
|
6536
|
+
value: schedule.nightStart,
|
|
6537
|
+
onChange: (e) => {
|
|
6538
|
+
if (/^([01]\d|2[0-3]):[0-5]\d$/.test(e.target.value)) patchSchedule({ nightStart: e.target.value });
|
|
6539
|
+
}
|
|
6540
|
+
})
|
|
6541
|
+
]
|
|
6542
|
+
})]
|
|
6543
|
+
})]
|
|
6544
|
+
}) : null,
|
|
6545
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6546
|
+
className: "dab-schedule-cell",
|
|
6547
|
+
style: { "--i": 2 },
|
|
6548
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6549
|
+
className: "dab-swatch-title",
|
|
6550
|
+
children: t("scheduleProfiles")
|
|
6551
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6552
|
+
className: "dab-time-row",
|
|
6553
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6554
|
+
className: "dab-time-label",
|
|
6555
|
+
children: [
|
|
6556
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SunIcon, { size: 13 }),
|
|
6557
|
+
t("scheduleDayProfile"),
|
|
6558
|
+
profileOptions(schedule.dayProfile, (id) => patchSchedule({ dayProfile: id }))
|
|
6559
|
+
]
|
|
6560
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6561
|
+
className: "dab-time-label",
|
|
6562
|
+
children: [
|
|
6563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoonIcon, { size: 13 }),
|
|
6564
|
+
t("scheduleNightProfile"),
|
|
6565
|
+
profileOptions(schedule.nightProfile, (id) => patchSchedule({ nightProfile: id }))
|
|
6566
|
+
]
|
|
6567
|
+
})]
|
|
6568
|
+
})]
|
|
6569
|
+
})
|
|
6570
|
+
]
|
|
5931
6571
|
})
|
|
5932
|
-
|
|
5933
|
-
})
|
|
6572
|
+
})
|
|
6573
|
+
})
|
|
5934
6574
|
]
|
|
5935
6575
|
}),
|
|
5936
6576
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -6186,7 +6826,7 @@ window.__ModuleLoader__.load({
|
|
|
6186
6826
|
}, "dsh-any-background: skin dispose");
|
|
6187
6827
|
const styleEl = document.createElement("style");
|
|
6188
6828
|
styleEl.dataset.plugin = "dsh-any-background";
|
|
6189
|
-
styleEl.textContent = `body[data-ds-dark-theme="dsh-any-background"]::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(255,255,255,0.03) 0%,transparent 60%)}${SETTINGS_STYLE_RULE}${TRAJECTORY_STYLE_RULE}${INPUT_BLUR_RULE}[data-composer-card] textarea::placeholder,[data-composer-card] input::placeholder,[data-composer-card] [contenteditable]::placeholder,[data-cordis-panel] input::placeholder,[data-cordis-panel] textarea::placeholder,.dab-input::placeholder,.dab-input textarea::placeholder,.dab-input input::placeholder{color:var(--dsh-any-placeholder,var(--dsw-alias-label-caption,#8a8f98))!important;font-style:italic;opacity:.85}`;
|
|
6829
|
+
styleEl.textContent = `body[data-ds-dark-theme="dsh-any-background"]::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(255,255,255,0.03) 0%,transparent 60%)}${SETTINGS_STYLE_RULE}${POPOVER_BLUR_RULE}${TRAJECTORY_STYLE_RULE}${INPUT_BLUR_RULE}${MOBILE_HEADER_RULE}${PANEL_TOKEN_RULE}${PANEL_BLUR_RULE}${PRODUCED_RULE}[data-composer-card] textarea::placeholder,[data-composer-card] input::placeholder,[data-composer-card] [contenteditable]::placeholder,[data-cordis-panel] input::placeholder,[data-cordis-panel] textarea::placeholder,.dab-input::placeholder,.dab-input textarea::placeholder,.dab-input input::placeholder{color:var(--dsh-any-placeholder,var(--dsw-alias-label-caption,#8a8f98))!important;font-style:italic;opacity:.85}`;
|
|
6190
6830
|
document.head.appendChild(styleEl);
|
|
6191
6831
|
ctx.effect(() => () => {
|
|
6192
6832
|
styleEl?.parentNode?.removeChild(styleEl);
|
|
@@ -6291,32 +6931,35 @@ window.__ModuleLoader__.load({
|
|
|
6291
6931
|
* the full adaptation (host skin + editor wheel sync); the caller persists. */
|
|
6292
6932
|
const adoptWallpaperColor = async (dataUrl) => {
|
|
6293
6933
|
const hsl = await extractWallpaperColor(dataUrl, rBgState());
|
|
6294
|
-
if (!hsl) return;
|
|
6934
|
+
if (!hsl || rWp() !== dataUrl) return;
|
|
6295
6935
|
cfg.color = hsl;
|
|
6296
6936
|
registerCustom(hsl[0], hsl[1], hsl[2]);
|
|
6297
6937
|
colorRev++;
|
|
6298
6938
|
bound?.syncColor(hslToHsv(hsl[0], hsl[1], hsl[2]), colorRev);
|
|
6299
6939
|
};
|
|
6300
6940
|
/** Activate a rotation item: the server copies its bytes into the wallpaper
|
|
6301
|
-
* slot; the client applies the returned
|
|
6941
|
+
* slot; the client applies the returned serve URL through the normal image
|
|
6302
6942
|
* path, then re-extracts the theme color from the new picture so the
|
|
6303
6943
|
* palette follows the rotation. */
|
|
6304
6944
|
const applyRotationIndex = async (idx, auto) => {
|
|
6305
6945
|
const rot = rRotation();
|
|
6306
6946
|
if (idx < 0 || idx >= rot.items.length) return false;
|
|
6307
6947
|
const r = await rotationActivate(idx);
|
|
6308
|
-
if (!r.ok || !r.
|
|
6948
|
+
if (!r.ok || !r.wallpaperUrl) {
|
|
6309
6949
|
console.warn("dsh-any-background: rotation activate failed", r.error);
|
|
6310
6950
|
return false;
|
|
6311
6951
|
}
|
|
6312
|
-
|
|
6313
|
-
|
|
6952
|
+
cfg.backgroundType = "image";
|
|
6953
|
+
setBgDark(null);
|
|
6954
|
+
setWpImageUrl(r.wallpaperUrl);
|
|
6955
|
+
setWpUrl(rWpImage());
|
|
6956
|
+
setBgState({ ...DEFAULT_CONFIG.bgState });
|
|
6314
6957
|
cfg.rotation = {
|
|
6315
6958
|
...rot,
|
|
6316
6959
|
current: idx,
|
|
6317
6960
|
lastRotate: auto || rot.lastRotate === null ? (/* @__PURE__ */ new Date()).toISOString() : rot.lastRotate
|
|
6318
6961
|
};
|
|
6319
|
-
if (cfg.backgroundType === "image") await adoptWallpaperColor(
|
|
6962
|
+
if (cfg.backgroundType === "image") await adoptWallpaperColor(rWp());
|
|
6320
6963
|
applyThemeColor();
|
|
6321
6964
|
syncBg();
|
|
6322
6965
|
saveConfig();
|
|
@@ -6428,6 +7071,7 @@ window.__ModuleLoader__.load({
|
|
|
6428
7071
|
ctx.effect(() => () => {
|
|
6429
7072
|
teardownWp();
|
|
6430
7073
|
}, "dsh-any-background: wp cleanup");
|
|
7074
|
+
ctx.effect(() => startBetterSidebarWatch(), "dsh-any-background: better-sidebar watch");
|
|
6431
7075
|
ctx.effect(() => ctx.on("theme/change", () => {
|
|
6432
7076
|
if (rHasColor() || rBgDark() !== null || rSchemeOverride() !== "auto") {
|
|
6433
7077
|
const snapshot = ctx.theme.getTheme();
|
|
@@ -6510,16 +7154,16 @@ window.__ModuleLoader__.load({
|
|
|
6510
7154
|
nl
|
|
6511
7155
|
], colorRev);
|
|
6512
7156
|
},
|
|
6513
|
-
|
|
7157
|
+
setWpFromServer: (u) => {
|
|
6514
7158
|
cfg.backgroundType = "image";
|
|
6515
7159
|
setBgDark(null);
|
|
6516
7160
|
setWpImageUrl(u);
|
|
6517
|
-
setWpUrl(
|
|
7161
|
+
setWpUrl(rWpImage());
|
|
6518
7162
|
setBgState({ ...DEFAULT_CONFIG.bgState });
|
|
6519
|
-
persistWallpaper(u);
|
|
6520
7163
|
if (u === null) {
|
|
6521
7164
|
setWpVideoUrl(null, null);
|
|
6522
7165
|
persistVideo(null);
|
|
7166
|
+
persistWallpaper(null);
|
|
6523
7167
|
}
|
|
6524
7168
|
applyThemeColor();
|
|
6525
7169
|
syncBg();
|
|
@@ -6605,6 +7249,11 @@ window.__ModuleLoader__.load({
|
|
|
6605
7249
|
applySettingsOverrides(v);
|
|
6606
7250
|
saveConfig();
|
|
6607
7251
|
},
|
|
7252
|
+
setPanelOp: (v) => {
|
|
7253
|
+
cfg.panelOpacity = v;
|
|
7254
|
+
applyPanelOverrides(v);
|
|
7255
|
+
saveConfig();
|
|
7256
|
+
},
|
|
6608
7257
|
extractColor: async () => {
|
|
6609
7258
|
const url = rWp();
|
|
6610
7259
|
if (!url) return false;
|
|
@@ -6624,23 +7273,29 @@ window.__ModuleLoader__.load({
|
|
|
6624
7273
|
if (cfg.backgroundType === "video") {
|
|
6625
7274
|
const vurl = rWpVideo();
|
|
6626
7275
|
if (vurl) try {
|
|
6627
|
-
|
|
6628
|
-
videoPayload = await new Promise((resolve, reject) => {
|
|
6629
|
-
const fr = new FileReader();
|
|
6630
|
-
fr.onload = () => resolve(fr.result);
|
|
6631
|
-
fr.onerror = () => reject(fr.error);
|
|
6632
|
-
fr.readAsDataURL(blob);
|
|
6633
|
-
});
|
|
7276
|
+
videoPayload = await blobToDataUrl(await fetch(vurl).then((r) => r.blob()));
|
|
6634
7277
|
if (videoPayload && !/^data:video\//.test(videoPayload)) videoPayload = videoPayload.replace(/^data:[^;,]*/, `data:${cfg.videoMime ?? "video/mp4"}`);
|
|
6635
7278
|
} catch {
|
|
6636
7279
|
videoPayload = null;
|
|
6637
7280
|
}
|
|
6638
7281
|
}
|
|
7282
|
+
let wallpaperPayload = null;
|
|
7283
|
+
if (cfg.backgroundType === "image") {
|
|
7284
|
+
const wurl = rWp();
|
|
7285
|
+
if (wurl) {
|
|
7286
|
+
if (wurl.startsWith("data:")) wallpaperPayload = wurl;
|
|
7287
|
+
else try {
|
|
7288
|
+
wallpaperPayload = await blobToDataUrl(await fetch(wurl).then((r) => r.blob()));
|
|
7289
|
+
} catch {
|
|
7290
|
+
wallpaperPayload = null;
|
|
7291
|
+
}
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
6639
7294
|
const payload = {
|
|
6640
7295
|
version: 2,
|
|
6641
7296
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6642
7297
|
config: cfg,
|
|
6643
|
-
wallpaper:
|
|
7298
|
+
wallpaper: wallpaperPayload,
|
|
6644
7299
|
video: videoPayload
|
|
6645
7300
|
};
|
|
6646
7301
|
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
|
@@ -6821,6 +7476,29 @@ window.__ModuleLoader__.load({
|
|
|
6821
7476
|
rotateNow: async () => {
|
|
6822
7477
|
if (rRotation().items.length === 0) return false;
|
|
6823
7478
|
return applyRotationIndex(pickNextRotationIndex(), true);
|
|
7479
|
+
},
|
|
7480
|
+
setVideoFromUrl: async (url) => {
|
|
7481
|
+
const r = await setVideoFromUrl(url);
|
|
7482
|
+
if (!r.ok || !r.mime) {
|
|
7483
|
+
console.warn("dsh-any-background: video url fetch failed", r.error);
|
|
7484
|
+
return false;
|
|
7485
|
+
}
|
|
7486
|
+
setBgDark(null);
|
|
7487
|
+
cfg.backgroundType = "video";
|
|
7488
|
+
setWpUrl(null);
|
|
7489
|
+
cfg.videoBgState = { ...DEFAULT_CONFIG.bgState };
|
|
7490
|
+
setWpVideoUrl(VIDEO_SERVE_URL, r.mime);
|
|
7491
|
+
applyWp();
|
|
7492
|
+
saveConfig();
|
|
7493
|
+
syncBg();
|
|
7494
|
+
const applied = rWpVideo();
|
|
7495
|
+
captureVideoSnapshot(applied).then((snap) => {
|
|
7496
|
+
if (rWpVideo() !== applied) return;
|
|
7497
|
+
setWpVideoSnapshot(snap);
|
|
7498
|
+
applyThemeColor();
|
|
7499
|
+
syncBg();
|
|
7500
|
+
});
|
|
7501
|
+
return true;
|
|
6824
7502
|
}
|
|
6825
7503
|
};
|
|
6826
7504
|
};
|