dsh-background-by-model 0.9.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/CHANGELOG.zh.md +17 -1
- package/README.md +22 -19
- package/README.zh.md +23 -20
- package/lib/client.js +405 -131
- package/lib/index.js +271 -92
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -330,6 +330,28 @@ window.__ModuleLoader__.load({
|
|
|
330
330
|
function defaultTransition() {
|
|
331
331
|
return { ...DEFAULT_TRANSITION };
|
|
332
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* One image entry for a place that CREATES a picture rather than reads one back
|
|
335
|
+
* (a fresh holiday, and every upload in the panel).
|
|
336
|
+
*
|
|
337
|
+
* The two effects whose neutral value is also the shipped default are written
|
|
338
|
+
* explicitly (`opacity`, `blur`), even though `normalizeImage` would default them:
|
|
339
|
+
* the read-time lifts key off the ABSENCE of a key, so an entry created without one
|
|
340
|
+
* would come back as "written before this field existed" and inherit whatever the
|
|
341
|
+
* rule happened to carry. `brightness` is the one field whose absence is meaningful
|
|
342
|
+
* (see `BgImage.brightness`), so it is left out — "untouched" is exactly what a new
|
|
343
|
+
* picture is.
|
|
344
|
+
*/
|
|
345
|
+
function defaultImage(slot, color = null, bgMode = "fit") {
|
|
346
|
+
return {
|
|
347
|
+
slot,
|
|
348
|
+
bgMode,
|
|
349
|
+
bgState: { ...DEFAULT_BG_STATE },
|
|
350
|
+
color,
|
|
351
|
+
opacity: 1,
|
|
352
|
+
blur: 0
|
|
353
|
+
};
|
|
354
|
+
}
|
|
333
355
|
/** Every slot a rule owns, in order. The one thing the render and store layers
|
|
334
356
|
* ask of the image list, so a rule's slots are never enumerated ad hoc. */
|
|
335
357
|
function ruleSlots(rule) {
|
|
@@ -399,18 +421,10 @@ window.__ModuleLoader__.load({
|
|
|
399
421
|
const color = hexToHsl(def.color);
|
|
400
422
|
return {
|
|
401
423
|
id: def.id,
|
|
402
|
-
images: [
|
|
403
|
-
slot: def.slot,
|
|
404
|
-
bgMode: "fill",
|
|
405
|
-
bgState: { ...DEFAULT_BG_STATE },
|
|
406
|
-
color
|
|
407
|
-
}],
|
|
424
|
+
images: [defaultImage(def.slot, color, "fill")],
|
|
408
425
|
match: "",
|
|
409
426
|
enabled: true,
|
|
410
427
|
color,
|
|
411
|
-
wallpaperOpacity: 1,
|
|
412
|
-
brightness: 100,
|
|
413
|
-
blur: 0,
|
|
414
428
|
rotate: defaultRotation()
|
|
415
429
|
};
|
|
416
430
|
}
|
|
@@ -434,9 +448,6 @@ window.__ModuleLoader__.load({
|
|
|
434
448
|
match: "",
|
|
435
449
|
enabled: true,
|
|
436
450
|
color: null,
|
|
437
|
-
wallpaperOpacity: 1,
|
|
438
|
-
brightness: 100,
|
|
439
|
-
blur: 0,
|
|
440
451
|
rotate: defaultRotation()
|
|
441
452
|
};
|
|
442
453
|
}
|
|
@@ -547,28 +558,41 @@ window.__ModuleLoader__.load({
|
|
|
547
558
|
durationMs: clamp$1(t.durationMs, 0, TRANSITION_MAX_MS, fallback)
|
|
548
559
|
};
|
|
549
560
|
}
|
|
561
|
+
/** The inherit block for a caller that has no rule-level values at all. */
|
|
562
|
+
const DEFAULT_IMAGE_INHERIT = {
|
|
563
|
+
color: null,
|
|
564
|
+
opacity: 1,
|
|
565
|
+
brightness: void 0,
|
|
566
|
+
blur: 0
|
|
567
|
+
};
|
|
550
568
|
/**
|
|
551
569
|
* Coerce one persisted image entry, or null when it lacks a usable slot.
|
|
552
570
|
*
|
|
553
|
-
* `inherit`
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
571
|
+
* `inherit` is the rule-level block this entry may take its values from, and each
|
|
572
|
+
* field is lifted ONLY when the entry has no key of its own — that absence is the
|
|
573
|
+
* whole signal. `color: null` is a deliberate "follow the system theme" and must
|
|
574
|
+
* not be re-inherited on every load; `brightness` keeps its key absent when neither
|
|
575
|
+
* side ever named one, so an upgrade never writes a default nobody chose.
|
|
557
576
|
*
|
|
558
577
|
* `inheritMode` is the same one-shot lift for the layout mode. It is the caller's to
|
|
559
578
|
* resolve because the callers answer differently: a user rule passes the mode its
|
|
560
579
|
* config carried at the RULE level (schema 5), a holiday passes its own `fill`. An
|
|
561
580
|
* entry that HAS a `bgMode` key keeps it, even when it equals the default.
|
|
562
581
|
*/
|
|
563
|
-
function normalizeImage(raw, inherit =
|
|
582
|
+
function normalizeImage(raw, inherit = DEFAULT_IMAGE_INHERIT, inheritMode = "fit") {
|
|
564
583
|
const i = raw ?? {};
|
|
565
584
|
if (typeof i.slot !== "string" || !SLOT_RE$1.test(i.slot)) return null;
|
|
566
|
-
|
|
585
|
+
const image = {
|
|
567
586
|
slot: i.slot,
|
|
568
587
|
bgMode: BG_MODES$1.includes(i.bgMode) ? i.bgMode : inheritMode,
|
|
569
588
|
bgState: normalizeBgState(i.bgState),
|
|
570
|
-
color: hasOwn(i, "color") ? normalizeHsl(i.color) : inherit
|
|
589
|
+
color: hasOwn(i, "color") ? normalizeHsl(i.color) : inherit.color,
|
|
590
|
+
opacity: clamp01(i.opacity, inherit.opacity),
|
|
591
|
+
blur: clamp$1(i.blur, 0, 60, inherit.blur)
|
|
571
592
|
};
|
|
593
|
+
const brightness = i.brightness === void 0 ? inherit.brightness : clamp$1(i.brightness, 0, 200, 100);
|
|
594
|
+
if (brightness !== void 0) image.brightness = brightness;
|
|
595
|
+
return image;
|
|
572
596
|
}
|
|
573
597
|
/**
|
|
574
598
|
* Coerce one persisted rule, or null when it names no image ANYWHERE.
|
|
@@ -593,38 +617,45 @@ window.__ModuleLoader__.load({
|
|
|
593
617
|
if (id === null) return null;
|
|
594
618
|
const color = normalizeHsl(r.color);
|
|
595
619
|
const mode = BG_MODES$1.includes(r.bgMode) ? r.bgMode : "fit";
|
|
620
|
+
const inherit = {
|
|
621
|
+
color,
|
|
622
|
+
opacity: clamp01(r.wallpaperOpacity, 1),
|
|
623
|
+
brightness: r.brightness === void 0 ? void 0 : clamp$1(r.brightness, 0, 200, 100),
|
|
624
|
+
blur: clamp$1(r.blur, 0, 60, 0)
|
|
625
|
+
};
|
|
596
626
|
const images = [];
|
|
597
627
|
const seen = /* @__PURE__ */ new Set();
|
|
598
628
|
const hadList = Array.isArray(r.images);
|
|
599
629
|
if (hadList) for (const entry of r.images) {
|
|
600
|
-
const image = normalizeImage(entry,
|
|
630
|
+
const image = normalizeImage(entry, inherit, mode);
|
|
601
631
|
if (image === null || seen.has(image.slot)) continue;
|
|
602
632
|
seen.add(image.slot);
|
|
603
633
|
images.push(image);
|
|
604
634
|
}
|
|
605
635
|
const legacySlot = typeof r.slot === "string" && SLOT_RE$1.test(r.slot) ? r.slot : null;
|
|
606
|
-
if (images.length === 0 && legacySlot !== null)
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
636
|
+
if (images.length === 0 && legacySlot !== null) {
|
|
637
|
+
const image = {
|
|
638
|
+
slot: legacySlot,
|
|
639
|
+
bgMode: mode,
|
|
640
|
+
bgState: normalizeBgState(r.bgState),
|
|
641
|
+
color,
|
|
642
|
+
opacity: inherit.opacity,
|
|
643
|
+
blur: inherit.blur
|
|
644
|
+
};
|
|
645
|
+
if (inherit.brightness !== void 0) image.brightness = inherit.brightness;
|
|
646
|
+
images.push(image);
|
|
647
|
+
}
|
|
612
648
|
if (images.length === 0 && !hadList) return null;
|
|
613
649
|
const rotate = normalizeRotation(r.rotate);
|
|
614
650
|
if (images.length < 2) rotate.enabled = false;
|
|
615
|
-
|
|
616
|
-
const rule = {
|
|
651
|
+
return {
|
|
617
652
|
id,
|
|
618
653
|
images,
|
|
619
654
|
match: typeof r.match === "string" ? r.match : "",
|
|
620
655
|
enabled: r.enabled !== false,
|
|
621
656
|
color,
|
|
622
|
-
wallpaperOpacity: clamp01(r.wallpaperOpacity, 1),
|
|
623
|
-
blur: clamp$1(r.blur, 0, 60, 0),
|
|
624
657
|
rotate
|
|
625
658
|
};
|
|
626
|
-
if (brightness !== void 0) rule.brightness = brightness;
|
|
627
|
-
return rule;
|
|
628
659
|
}
|
|
629
660
|
/**
|
|
630
661
|
* Coerce one persisted holiday entry.
|
|
@@ -641,17 +672,29 @@ window.__ModuleLoader__.load({
|
|
|
641
672
|
return {
|
|
642
673
|
...base,
|
|
643
674
|
enabled: r.enabled !== false,
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
675
|
+
images: base.images.map((image) => {
|
|
676
|
+
const stored = r.images?.[0];
|
|
677
|
+
const merged = {
|
|
678
|
+
slot: image.slot,
|
|
679
|
+
bgMode: image.bgMode,
|
|
680
|
+
bgState: normalizeBgState(stored?.bgState ?? r.bgState),
|
|
681
|
+
color: image.color,
|
|
682
|
+
opacity: clamp01(stored?.opacity ?? legacyWallpaperOpacity(r.wallpaperOpacity), image.opacity),
|
|
683
|
+
blur: clamp$1(stored?.blur ?? r.blur, 0, 60, image.blur)
|
|
684
|
+
};
|
|
685
|
+
const brightness = stored?.brightness === void 0 ? r.brightness === void 0 ? void 0 : clamp$1(r.brightness, 0, 200, 100) : clamp$1(stored.brightness, 0, 200, 100);
|
|
686
|
+
if (brightness !== void 0) merged.brightness = brightness;
|
|
687
|
+
return merged;
|
|
688
|
+
})
|
|
653
689
|
};
|
|
654
690
|
}
|
|
691
|
+
/**
|
|
692
|
+
* The rule-level opacity a schema-9 holiday entry carried, or `undefined` so the
|
|
693
|
+
* caller can tell "absent" from "set to 1" and leave `clamp01` its default.
|
|
694
|
+
*/
|
|
695
|
+
function legacyWallpaperOpacity(raw) {
|
|
696
|
+
return typeof raw === "number" && isFinite(raw) ? raw : void 0;
|
|
697
|
+
}
|
|
655
698
|
/** Coerce the whole holiday block; unknown ids are dropped, missing ones defaulted. */
|
|
656
699
|
function normalizeHolidays(raw) {
|
|
657
700
|
const r = raw ?? {};
|
|
@@ -892,24 +935,34 @@ window.__ModuleLoader__.load({
|
|
|
892
935
|
function rBgMode() {
|
|
893
936
|
return activeImage()?.bgMode ?? "fit";
|
|
894
937
|
}
|
|
938
|
+
/**
|
|
939
|
+
* Opacity of the wallpaper layer, 0..1 (per image, see BgImage).
|
|
940
|
+
*
|
|
941
|
+
* Read from the image being painted, not from the rule: one rule's rotation can go
|
|
942
|
+
* through a full-bleed photo and a faint decorative plate, and a rule-level answer
|
|
943
|
+
* would be wrong for one of them. A rule with no image paints nothing at all, so
|
|
944
|
+
* there is no layer for the value to describe.
|
|
945
|
+
*/
|
|
895
946
|
function rWop() {
|
|
896
|
-
return clamp01(
|
|
947
|
+
return clamp01(activeImage()?.opacity, 1);
|
|
897
948
|
}
|
|
898
949
|
/**
|
|
899
950
|
* Brightness of the wallpaper layer, in percent (100 = the picture exactly as the
|
|
900
951
|
* file has it).
|
|
901
952
|
*
|
|
902
|
-
* Read from the
|
|
903
|
-
* same way:
|
|
904
|
-
* the setting existed
|
|
905
|
-
*
|
|
906
|
-
* numbers of its own (see
|
|
953
|
+
* Read from the CURRENT image, like the opacity and blur beside it, and defaulted
|
|
954
|
+
* the same way: an image whose `brightness` key is absent — every config written
|
|
955
|
+
* before the setting existed, and every picture whose rule never named one — is
|
|
956
|
+
* left alone, so an upgrade changes nothing on screen. The default and the ceiling
|
|
957
|
+
* live in ../schema, so the render layer holds no numbers of its own (see
|
|
958
|
+
* ./wallpaper's `wpFilter`).
|
|
907
959
|
*/
|
|
908
960
|
function rBrightness() {
|
|
909
|
-
return clamp$1(
|
|
961
|
+
return clamp$1(activeImage()?.brightness, 0, 200, 100);
|
|
910
962
|
}
|
|
963
|
+
/** Blur of the CURRENT image, in px (0 = the picture as its file has it). */
|
|
911
964
|
function rBl() {
|
|
912
|
-
return clamp$1(
|
|
965
|
+
return clamp$1(activeImage()?.blur, 0, 60, 0);
|
|
913
966
|
}
|
|
914
967
|
/** Framing of the CURRENT image (per image, see BgImage). */
|
|
915
968
|
function rBgState() {
|
|
@@ -1064,7 +1117,7 @@ window.__ModuleLoader__.load({
|
|
|
1064
1117
|
const d = data;
|
|
1065
1118
|
const slots = Array.isArray(d.slots) ? d.slots.filter((s) => typeof s === "string") : [];
|
|
1066
1119
|
const schema = typeof d.schema === "number" && isFinite(d.schema) ? d.schema : null;
|
|
1067
|
-
hostShape = typeof schema === "number" && schema >=
|
|
1120
|
+
hostShape = typeof schema === "number" && schema >= 10 ? "current" : "legacy";
|
|
1068
1121
|
if (hostShape === "legacy") holdWrite();
|
|
1069
1122
|
return {
|
|
1070
1123
|
config: d.config,
|
|
@@ -3197,13 +3250,25 @@ window.__ModuleLoader__.load({
|
|
|
3197
3250
|
/** Index of the layer fading in right now, if any. */
|
|
3198
3251
|
let pending = null;
|
|
3199
3252
|
let fadeTimer = null;
|
|
3253
|
+
/** The wallpaper's steady opacity — the ACTIVE picture's, as last committed.
|
|
3254
|
+
*
|
|
3255
|
+
* Mirrored rather than read back off the style string, because a repaint that
|
|
3256
|
+
* runs in the middle of a fade (a theme re-registration, a panel re-render) has
|
|
3257
|
+
* to re-write the value the fade is HEADING for; reading `rWop()` there would
|
|
3258
|
+
* snap the whole layer to the incoming picture's alpha and cut the fade short.
|
|
3259
|
+
*/
|
|
3260
|
+
let wpAlpha = 1;
|
|
3200
3261
|
function createLayer() {
|
|
3201
3262
|
const el = document.createElement("div");
|
|
3202
3263
|
el.dataset.dabWpLayer = "";
|
|
3203
3264
|
el.style.cssText = "position:absolute;inset:0;opacity:0;background-repeat:no-repeat;background-position:center;";
|
|
3204
3265
|
return {
|
|
3205
3266
|
el,
|
|
3206
|
-
url: null
|
|
3267
|
+
url: null,
|
|
3268
|
+
posX: null,
|
|
3269
|
+
posY: null,
|
|
3270
|
+
brightness: 100,
|
|
3271
|
+
blur: 0
|
|
3207
3272
|
};
|
|
3208
3273
|
}
|
|
3209
3274
|
function ensureWpContainer() {
|
|
@@ -3221,6 +3286,7 @@ window.__ModuleLoader__.load({
|
|
|
3221
3286
|
layers = [a, b];
|
|
3222
3287
|
front = 0;
|
|
3223
3288
|
pending = null;
|
|
3289
|
+
wpAlpha = 1;
|
|
3224
3290
|
document.body.prepend(wpEl);
|
|
3225
3291
|
}
|
|
3226
3292
|
/** Drop the whole wallpaper layer (no rule carries an image). */
|
|
@@ -3232,6 +3298,7 @@ window.__ModuleLoader__.load({
|
|
|
3232
3298
|
pending = null;
|
|
3233
3299
|
front = 0;
|
|
3234
3300
|
layers = null;
|
|
3301
|
+
wpAlpha = 1;
|
|
3235
3302
|
wpEl?.remove();
|
|
3236
3303
|
wpEl = null;
|
|
3237
3304
|
}
|
|
@@ -3239,9 +3306,14 @@ window.__ModuleLoader__.load({
|
|
|
3239
3306
|
function clearLayer(l) {
|
|
3240
3307
|
if (l.url === null) return;
|
|
3241
3308
|
l.url = null;
|
|
3309
|
+
l.posX = null;
|
|
3310
|
+
l.posY = null;
|
|
3311
|
+
l.brightness = 100;
|
|
3312
|
+
l.blur = 0;
|
|
3242
3313
|
l.el.style.backgroundImage = "";
|
|
3243
3314
|
l.el.style.backgroundSize = "";
|
|
3244
3315
|
l.el.style.backgroundPosition = "";
|
|
3316
|
+
l.el.style.inset = "0px";
|
|
3245
3317
|
l.el.style.transform = "none";
|
|
3246
3318
|
}
|
|
3247
3319
|
/** Write the two properties a switch animates. Both are always written, so a
|
|
@@ -3269,6 +3341,34 @@ window.__ModuleLoader__.load({
|
|
|
3269
3341
|
img.onerror = () => cb(0, 0);
|
|
3270
3342
|
img.src = url;
|
|
3271
3343
|
}
|
|
3344
|
+
/** How far a layer's box is grown on every side to hide a blurred edge.
|
|
3345
|
+
*
|
|
3346
|
+
* `filter: blur(σ)` convolves the layer's own rendering with a Gaussian of
|
|
3347
|
+
* standard deviation σ and treats everything outside the box as transparent, so
|
|
3348
|
+
* the outermost pixels come out half opaque and the image fades inward over
|
|
3349
|
+
* ~2σ — composited over the page behind it, that half-transparent rim is the
|
|
3350
|
+
* black border the blur slider paints, and it grows thicker with the slider.
|
|
3351
|
+
* Growing the box by 2σ puts every visible pixel at least 2σ inside the edge,
|
|
3352
|
+
* where ~2% of the page still shows through the outermost row: gone to the eye.
|
|
3353
|
+
* Two sigma rather than three is the trade for the `cover` modes, which pay for
|
|
3354
|
+
* the growth by framing slightly tighter — a fifth of the image at most, at the
|
|
3355
|
+
* slider's maximum, where the detail a wider frame would reveal is blurred out.
|
|
3356
|
+
*/
|
|
3357
|
+
function wpBlurPad(blur) {
|
|
3358
|
+
return blur > 0 ? Math.ceil(blur * 2) + 2 : 0;
|
|
3359
|
+
}
|
|
3360
|
+
/** Grow a layer's box by `pad` on every side while keeping its image on the same
|
|
3361
|
+
* pixels. The background positioning area starts at the border box, so a box
|
|
3362
|
+
* whose origin moved by -pad needs a numeric placement moved by +pad; `center`
|
|
3363
|
+
* (and the box-relative `cover` / `100% 100%` sizes) sit in the middle of a
|
|
3364
|
+
* symmetrically grown box and need no compensation. The container clips the
|
|
3365
|
+
* growth, so nothing about the visible layout changes: it exists only to give
|
|
3366
|
+
* the blur pixels to sample beyond the viewport. */
|
|
3367
|
+
function writeLayerBox(l, pad) {
|
|
3368
|
+
const { posX, posY } = l;
|
|
3369
|
+
l.el.style.inset = `${-pad}px`;
|
|
3370
|
+
l.el.style.backgroundPosition = posX === null || posY === null ? "center" : `${posX + pad}px ${posY + pad}px`;
|
|
3371
|
+
}
|
|
3272
3372
|
/** Paint one layer's image and its placement. The placement always follows the
|
|
3273
3373
|
* ACTIVE rule: the incoming layer is painted right after a switch, and a
|
|
3274
3374
|
* repaint of the same URL (framing edit, viewport resize) must use the rule's
|
|
@@ -3280,6 +3380,8 @@ window.__ModuleLoader__.load({
|
|
|
3280
3380
|
const mode = rBgMode();
|
|
3281
3381
|
const next = `url("${url}")`;
|
|
3282
3382
|
if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
|
|
3383
|
+
l.posX = null;
|
|
3384
|
+
l.posY = null;
|
|
3283
3385
|
if (mode === "fit") {
|
|
3284
3386
|
el.style.backgroundRepeat = "no-repeat";
|
|
3285
3387
|
if (bg.iw > 0) {
|
|
@@ -3287,35 +3389,31 @@ window.__ModuleLoader__.load({
|
|
|
3287
3389
|
const w = bg.iw * fit * bg.zoom;
|
|
3288
3390
|
const h = bg.ih * fit * bg.zoom;
|
|
3289
3391
|
el.style.backgroundSize = `${w}px ${h}px`;
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
el.style.backgroundPosition = "center";
|
|
3294
|
-
}
|
|
3392
|
+
l.posX = bg.x * window.innerWidth - w / 2;
|
|
3393
|
+
l.posY = bg.y * window.innerHeight - h / 2;
|
|
3394
|
+
} else el.style.backgroundSize = "contain";
|
|
3295
3395
|
} else if (mode === "fill") {
|
|
3296
3396
|
el.style.backgroundRepeat = "no-repeat";
|
|
3297
3397
|
el.style.backgroundSize = "cover";
|
|
3298
|
-
el.style.backgroundPosition = "center";
|
|
3299
3398
|
} else if (mode === "stretch") {
|
|
3300
3399
|
el.style.backgroundRepeat = "no-repeat";
|
|
3301
3400
|
el.style.backgroundSize = "100% 100%";
|
|
3302
|
-
el.style.backgroundPosition = "center";
|
|
3303
3401
|
} else if (mode === "tile") {
|
|
3304
3402
|
el.style.backgroundRepeat = "repeat";
|
|
3305
3403
|
el.style.backgroundSize = "auto";
|
|
3306
|
-
|
|
3404
|
+
l.posX = 0;
|
|
3405
|
+
l.posY = 0;
|
|
3307
3406
|
} else {
|
|
3308
3407
|
el.style.backgroundRepeat = "no-repeat";
|
|
3309
3408
|
el.style.backgroundSize = "contain";
|
|
3310
|
-
el.style.backgroundPosition = "center";
|
|
3311
3409
|
imageNatSize(url, (w, h) => {
|
|
3312
3410
|
if (l.url !== url || el.style.backgroundImage !== next || rBgMode() !== "center") return;
|
|
3313
|
-
if (w > 0 && h > 0) {
|
|
3314
|
-
el.style.backgroundSize = `${w}px ${h}px`;
|
|
3315
|
-
el.style.backgroundPosition = "center";
|
|
3316
|
-
}
|
|
3411
|
+
if (w > 0 && h > 0) el.style.backgroundSize = `${w}px ${h}px`;
|
|
3317
3412
|
});
|
|
3318
3413
|
}
|
|
3414
|
+
l.brightness = rBrightness();
|
|
3415
|
+
l.blur = rBl();
|
|
3416
|
+
applyLayerEffects(l);
|
|
3319
3417
|
}
|
|
3320
3418
|
/** Whether there is anything to animate FROM: a wallpaper on screen that is
|
|
3321
3419
|
* actually visible. The duration question belongs to `rFadeMs`, and the
|
|
@@ -3339,6 +3437,10 @@ window.__ModuleLoader__.load({
|
|
|
3339
3437
|
}
|
|
3340
3438
|
pending = null;
|
|
3341
3439
|
front = idx;
|
|
3440
|
+
if (wpEl !== null) {
|
|
3441
|
+
wpEl.style.transition = "none";
|
|
3442
|
+
wpEl.style.opacity = String(wpAlpha);
|
|
3443
|
+
}
|
|
3342
3444
|
const el = layers[idx].el;
|
|
3343
3445
|
el.style.opacity = "1";
|
|
3344
3446
|
el.style.transform = "none";
|
|
@@ -3385,12 +3487,14 @@ window.__ModuleLoader__.load({
|
|
|
3385
3487
|
if (pending !== null) {
|
|
3386
3488
|
if (ls[pending].url === url) {
|
|
3387
3489
|
paintLayer(ls[pending], url);
|
|
3490
|
+
wpAlpha = rWop();
|
|
3388
3491
|
applyWpEffects();
|
|
3389
3492
|
return;
|
|
3390
3493
|
}
|
|
3391
3494
|
if (ls[front].url === url) {
|
|
3392
3495
|
cancelFade();
|
|
3393
3496
|
paintLayer(ls[front], url);
|
|
3497
|
+
wpAlpha = rWop();
|
|
3394
3498
|
applyWpEffects();
|
|
3395
3499
|
return;
|
|
3396
3500
|
}
|
|
@@ -3398,6 +3502,7 @@ window.__ModuleLoader__.load({
|
|
|
3398
3502
|
}
|
|
3399
3503
|
if (ls[front].url === url) {
|
|
3400
3504
|
paintLayer(ls[front], url);
|
|
3505
|
+
wpAlpha = rWop();
|
|
3401
3506
|
applyWpEffects();
|
|
3402
3507
|
return;
|
|
3403
3508
|
}
|
|
@@ -3418,7 +3523,11 @@ window.__ModuleLoader__.load({
|
|
|
3418
3523
|
applyLayerStyle(el, plan.from);
|
|
3419
3524
|
el.style.zIndex = "1";
|
|
3420
3525
|
from.el.style.zIndex = "0";
|
|
3526
|
+
const toAlpha = rWop();
|
|
3527
|
+
const fadeAlpha = plan.animate && Math.abs(toAlpha - wpAlpha) > .001;
|
|
3528
|
+
if (fadeAlpha) wpEl.style.transition = "none";
|
|
3421
3529
|
paintLayer(target, url);
|
|
3530
|
+
if (!fadeAlpha) wpAlpha = toAlpha;
|
|
3422
3531
|
applyWpEffects();
|
|
3423
3532
|
if (!plan.animate) {
|
|
3424
3533
|
applyLayerStyle(el, plan.to);
|
|
@@ -3431,15 +3540,24 @@ window.__ModuleLoader__.load({
|
|
|
3431
3540
|
if (pending !== idx || layers === null) return;
|
|
3432
3541
|
el.style.transition = plan.transition;
|
|
3433
3542
|
applyLayerStyle(el, plan.to);
|
|
3543
|
+
if (fadeAlpha) {
|
|
3544
|
+
wpEl.style.transition = plan.transition;
|
|
3545
|
+
wpEl.style.opacity = String(toAlpha);
|
|
3546
|
+
wpAlpha = toAlpha;
|
|
3547
|
+
}
|
|
3434
3548
|
});
|
|
3435
3549
|
fadeTimer = window.setTimeout(() => finishFade(idx), plan.durationMs + 160);
|
|
3436
3550
|
}
|
|
3437
3551
|
/**
|
|
3438
|
-
* The `filter`
|
|
3439
|
-
*
|
|
3552
|
+
* The `filter` ONE LAYER carries, from the brightness and blur of the picture
|
|
3553
|
+
* painted on IT.
|
|
3554
|
+
*
|
|
3555
|
+
* Both are per image (see BgImage): one rule's rotation can run a daylight photo
|
|
3556
|
+
* and a night shot, and a rule-level answer forced the second one to live at the
|
|
3557
|
+
* first one's exposure.
|
|
3440
3558
|
*
|
|
3441
3559
|
* ONE declaration, because the two are one CSS property: assigning `filter` twice
|
|
3442
|
-
* would leave whichever ran last, so
|
|
3560
|
+
* would leave whichever ran last, so an image that is both brightened and blurred
|
|
3443
3561
|
* would silently lose one of them. Brightness is written first and blur second for
|
|
3444
3562
|
* a reason that is visible: blurring a dimmed picture softens the highlights the
|
|
3445
3563
|
* dimming just brought down, while dimming an already-blurred one simply darkens
|
|
@@ -3449,21 +3567,34 @@ window.__ModuleLoader__.load({
|
|
|
3449
3567
|
* The neutral brightness is LEFT OUT rather than written as `brightness(100%)`.
|
|
3450
3568
|
* A layer with neither effect gets `''`, which is what an untouched wallpaper has
|
|
3451
3569
|
* always carried, and keeps the layer off the compositor's filter path entirely.
|
|
3570
|
+
*
|
|
3571
|
+
* The numbers are the LAYER's, not live reads: during a switch the two layers
|
|
3572
|
+
* hold two different photos, and a live read made the outgoing one jump to the
|
|
3573
|
+
* incoming one's exposure at the first frame of every fade.
|
|
3452
3574
|
*/
|
|
3453
|
-
function wpFilter() {
|
|
3575
|
+
function wpFilter(brightness, blur) {
|
|
3454
3576
|
const parts = [];
|
|
3455
|
-
const brightness = rBrightness();
|
|
3456
3577
|
if (brightness !== 100) parts.push(`brightness(${brightness}%)`);
|
|
3457
|
-
const blur = rBl();
|
|
3458
3578
|
if (blur > 0) parts.push(`blur(${blur}px)`);
|
|
3459
3579
|
return parts.join(" ");
|
|
3460
3580
|
}
|
|
3461
3581
|
function applyWpEffects() {
|
|
3462
3582
|
if (wpEl === null || layers === null) return;
|
|
3463
|
-
wpEl.style.opacity = String(
|
|
3464
|
-
|
|
3465
|
-
layers[
|
|
3466
|
-
|
|
3583
|
+
wpEl.style.opacity = String(wpAlpha);
|
|
3584
|
+
applyLayerEffects(layers[0]);
|
|
3585
|
+
applyLayerEffects(layers[1]);
|
|
3586
|
+
}
|
|
3587
|
+
/** Write what a layer takes from ITS picture: the filter, and the box growth that
|
|
3588
|
+
* keeps a blurred edge from drawing a dark rim.
|
|
3589
|
+
*
|
|
3590
|
+
* ONE function for both, because the two are one visible decision — the same
|
|
3591
|
+
* blur that makes `filter` non-empty is what makes the pad non-zero — and
|
|
3592
|
+
* because BOTH update paths (a full repaint and a drag frame) write it, so a
|
|
3593
|
+
* blur dragged to its top cannot leave the rim behind until the slider is
|
|
3594
|
+
* released. */
|
|
3595
|
+
function applyLayerEffects(l) {
|
|
3596
|
+
l.el.style.filter = wpFilter(l.brightness, l.blur);
|
|
3597
|
+
writeLayerBox(l, wpBlurPad(l.blur));
|
|
3467
3598
|
}
|
|
3468
3599
|
/** Repaint the wallpaper layer, the token palette and every interface part from
|
|
3469
3600
|
* the ACTIVE rule. This is the single entry point a rule switch goes through. */
|
|
@@ -3512,6 +3643,38 @@ window.__ModuleLoader__.load({
|
|
|
3512
3643
|
tableFixStyleEl = null;
|
|
3513
3644
|
stopWatchingParts();
|
|
3514
3645
|
}
|
|
3646
|
+
/** Live wallpaper-opacity updates during slider drag (no full re-apply).
|
|
3647
|
+
*
|
|
3648
|
+
* The container is the one holder of this value (see `applyWpEffects`), so the
|
|
3649
|
+
* mirror and the style are written together — a later repaint then re-asserts
|
|
3650
|
+
* what the drag ended on rather than the value it started from. */
|
|
3651
|
+
function setWpOpacity(v) {
|
|
3652
|
+
wpAlpha = v;
|
|
3653
|
+
if (wpEl) wpEl.style.opacity = String(v);
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Live wallpaper-filter updates during a slide, with no full re-apply.
|
|
3657
|
+
*
|
|
3658
|
+
* Deliberately parameterless: the filter composes TWO settings (brightness and
|
|
3659
|
+
* blur), so a function trusting one caller's value would write half of it and
|
|
3660
|
+
* drop the other. Both are read back from the store instead (./state), which is
|
|
3661
|
+
* the same store the drag has already written to before calling this.
|
|
3662
|
+
*
|
|
3663
|
+
* Written onto the layer PAINTING that picture rather than onto both: during a
|
|
3664
|
+
* switch the other layer is still showing the previous photo, and giving it this
|
|
3665
|
+
* one's brightness and blur is exactly the snap this snapshot exists to prevent.
|
|
3666
|
+
*/
|
|
3667
|
+
function setWpFilter() {
|
|
3668
|
+
if (layers === null) return;
|
|
3669
|
+
const url = rWp();
|
|
3670
|
+
if (url === null) return;
|
|
3671
|
+
for (const l of layers) {
|
|
3672
|
+
if (l.url !== url) continue;
|
|
3673
|
+
l.brightness = rBrightness();
|
|
3674
|
+
l.blur = rBl();
|
|
3675
|
+
applyLayerEffects(l);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3515
3678
|
//#endregion
|
|
3516
3679
|
//#region src/client/cadence.ts
|
|
3517
3680
|
/**
|
|
@@ -4526,7 +4689,7 @@ window.__ModuleLoader__.load({
|
|
|
4526
4689
|
if (url !== null) images[slot] = url;
|
|
4527
4690
|
}
|
|
4528
4691
|
return {
|
|
4529
|
-
version:
|
|
4692
|
+
version: 10,
|
|
4530
4693
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4531
4694
|
config: cfg,
|
|
4532
4695
|
images
|
|
@@ -4565,7 +4728,7 @@ window.__ModuleLoader__.load({
|
|
|
4565
4728
|
* "replace everything with this" literally true.
|
|
4566
4729
|
*/
|
|
4567
4730
|
async function applyThemeFile(host, data, wipeFirst) {
|
|
4568
|
-
if (typeof data.version === "number" && data.version >
|
|
4731
|
+
if (typeof data.version === "number" && data.version > 10) return false;
|
|
4569
4732
|
if (typeof data.config !== "object" || data.config === null) return false;
|
|
4570
4733
|
if (wipeFirst) {
|
|
4571
4734
|
await resetStore();
|
|
@@ -4753,6 +4916,7 @@ window.__ModuleLoader__.load({
|
|
|
4753
4916
|
ruleBrightness: "图片亮度",
|
|
4754
4917
|
ruleBlur: "图片模糊",
|
|
4755
4918
|
ruleEffectPick: "调节项",
|
|
4919
|
+
ruleEffectNoImage: "添加图片后可分别设置每张图的透明度、亮度与模糊",
|
|
4756
4920
|
rotTitle: "多图轮换",
|
|
4757
4921
|
rotEnable: "轮换这组图",
|
|
4758
4922
|
rotNeedTwo: "至少两张图才能轮换",
|
|
@@ -4936,6 +5100,7 @@ window.__ModuleLoader__.load({
|
|
|
4936
5100
|
ruleBrightness: "Image brightness",
|
|
4937
5101
|
ruleBlur: "Image blur",
|
|
4938
5102
|
ruleEffectPick: "Effect",
|
|
5103
|
+
ruleEffectNoImage: "Add an image to give each one its own opacity, brightness and blur",
|
|
4939
5104
|
rotTitle: "Image rotation",
|
|
4940
5105
|
rotEnable: "Rotate these images",
|
|
4941
5106
|
rotNeedTwo: "At least two images are needed to rotate",
|
|
@@ -5222,37 +5387,80 @@ window.__ModuleLoader__.load({
|
|
|
5222
5387
|
//#region src/client/components/LiveSlider.tsx
|
|
5223
5388
|
/**
|
|
5224
5389
|
* Zero-lag slider: the thumb and value label update through DOM refs while
|
|
5225
|
-
* dragging
|
|
5226
|
-
*
|
|
5227
|
-
*
|
|
5228
|
-
*
|
|
5390
|
+
* dragging, so the caller can preview the change without a React re-render; the
|
|
5391
|
+
* settled value is committed ONCE, when the gesture ends.
|
|
5392
|
+
*
|
|
5393
|
+
* The split is the whole point and it is not an optimisation detail. A range
|
|
5394
|
+
* input fires `input` on every frame of a drag, and React's `change` handler for
|
|
5395
|
+
* one is wired to that same event — so a slider that commits from `onChange`
|
|
5396
|
+
* commits dozens of times a second, and every commit is a full repaint (skin,
|
|
5397
|
+
* palette, wallpaper layer). That is why dragging a wallpaper effect cost a whole
|
|
5398
|
+
* `applyActive()` per frame while the cheap live writers beside it were never
|
|
5399
|
+
* reached.
|
|
5400
|
+
*
|
|
5401
|
+
* The gesture's end is therefore read from the events that actually end one: the
|
|
5402
|
+
* native `change` event, a pointer released over the track, and a blur. No timer
|
|
5403
|
+
* is armed — nothing here may compete with the plugin's one scheduler.
|
|
5229
5404
|
*/
|
|
5230
5405
|
function LiveSlider({ min, max, step, def, fmt, label, ariaLabel, onInput, onChange }) {
|
|
5231
5406
|
const inputRef = (0, react.useRef)(null);
|
|
5232
5407
|
const valRef = (0, react.useRef)(null);
|
|
5408
|
+
/**
|
|
5409
|
+
* The value the current gesture has reached but has not committed yet.
|
|
5410
|
+
*
|
|
5411
|
+
* Non-null is also the answer to "is a gesture in flight?", which the `def` sync
|
|
5412
|
+
* below has to ask: the model watch re-emits on its own beat, and the render that
|
|
5413
|
+
* causes would otherwise snap the knob back to the committed value mid-drag.
|
|
5414
|
+
*/
|
|
5415
|
+
const pending = (0, react.useRef)(null);
|
|
5416
|
+
/** `onChange` is read through a ref so the native listener cannot go stale. */
|
|
5417
|
+
const commitRef = (0, react.useRef)(onChange);
|
|
5418
|
+
commitRef.current = onChange;
|
|
5233
5419
|
const paint = (el, v) => {
|
|
5234
5420
|
el.style.setProperty("--pct", String((v - min) / (max - min) * 100));
|
|
5235
5421
|
};
|
|
5236
|
-
|
|
5422
|
+
/** Move the knob and the label without calling anybody back. */
|
|
5423
|
+
const show = (v) => {
|
|
5237
5424
|
if (inputRef.current) {
|
|
5238
|
-
inputRef.current.value = String(
|
|
5239
|
-
paint(inputRef.current,
|
|
5425
|
+
inputRef.current.value = String(v);
|
|
5426
|
+
paint(inputRef.current, v);
|
|
5240
5427
|
}
|
|
5241
|
-
if (valRef.current) valRef.current.textContent = fmt(
|
|
5428
|
+
if (valRef.current) valRef.current.textContent = fmt(v);
|
|
5429
|
+
};
|
|
5430
|
+
const commit = () => {
|
|
5431
|
+
const v = pending.current;
|
|
5432
|
+
if (v === null) return;
|
|
5433
|
+
pending.current = null;
|
|
5434
|
+
commitRef.current(v);
|
|
5435
|
+
};
|
|
5436
|
+
/** One frame of a gesture: the caller's cheap path, then remember the value. */
|
|
5437
|
+
const live = (v) => {
|
|
5438
|
+
show(v);
|
|
5439
|
+
pending.current = v;
|
|
5440
|
+
onInput?.(v);
|
|
5441
|
+
};
|
|
5442
|
+
(0, react.useEffect)(() => {
|
|
5443
|
+
if (pending.current !== null) return;
|
|
5444
|
+
show(def);
|
|
5242
5445
|
}, [
|
|
5243
5446
|
def,
|
|
5244
5447
|
fmt,
|
|
5245
5448
|
min,
|
|
5246
5449
|
max
|
|
5247
5450
|
]);
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5451
|
+
(0, react.useEffect)(() => {
|
|
5452
|
+
const el = inputRef.current;
|
|
5453
|
+
if (el === null) return;
|
|
5454
|
+
const onEnd = () => commit();
|
|
5455
|
+
el.addEventListener("change", onEnd);
|
|
5456
|
+
return () => {
|
|
5457
|
+
el.removeEventListener("change", onEnd);
|
|
5458
|
+
commit();
|
|
5459
|
+
};
|
|
5460
|
+
}, []);
|
|
5461
|
+
const reset = () => {
|
|
5462
|
+
live(def);
|
|
5463
|
+
commit();
|
|
5256
5464
|
};
|
|
5257
5465
|
const name = label ?? ariaLabel;
|
|
5258
5466
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5277,15 +5485,10 @@ window.__ModuleLoader__.load({
|
|
|
5277
5485
|
defaultValue: def,
|
|
5278
5486
|
"aria-label": name,
|
|
5279
5487
|
title: name !== void 0 ? `${name} · ${fmt(def)}` : fmt(def),
|
|
5280
|
-
onDoubleClick:
|
|
5281
|
-
onInput: (e) =>
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
paint(el, v);
|
|
5285
|
-
onInput?.(v);
|
|
5286
|
-
if (valRef.current) valRef.current.textContent = fmt(v);
|
|
5287
|
-
},
|
|
5288
|
-
onChange: (e) => onChange(Number(e.target.value))
|
|
5488
|
+
onDoubleClick: reset,
|
|
5489
|
+
onInput: (e) => live(Number(e.target.value)),
|
|
5490
|
+
onPointerUp: commit,
|
|
5491
|
+
onBlur: commit
|
|
5289
5492
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5290
5493
|
ref: valRef,
|
|
5291
5494
|
className: "dab-slider-val",
|
|
@@ -6547,49 +6750,92 @@ window.__ModuleLoader__.load({
|
|
|
6547
6750
|
});
|
|
6548
6751
|
}
|
|
6549
6752
|
/**
|
|
6550
|
-
* The
|
|
6551
|
-
* single track edits it.
|
|
6753
|
+
* The selected image's three paint effects on ONE row: the menu names the target
|
|
6754
|
+
* and a single track edits it.
|
|
6755
|
+
*
|
|
6756
|
+
* Per IMAGE, like the layout mode, the framing and the colour above it. The move
|
|
6757
|
+
* is the same argument: a rotation through a daylight photo and a night shot does
|
|
6758
|
+
* not want one exposure, and a rule-level value forced one of them to live at the
|
|
6759
|
+
* other's compromise. A rule with no picture has nothing to expose, so the row is
|
|
6760
|
+
* DISABLED (not hidden) there — the same way the layout chips behave.
|
|
6552
6761
|
*
|
|
6553
6762
|
* The three ranges are not interchangeable — opacity and brightness are
|
|
6554
6763
|
* percentages (brightness ceilings at the shared `WALLPAPER_BRIGHTNESS_MAX`),
|
|
6555
6764
|
* blur is pixels — so changing the menu swaps the whole track: limits, formatter
|
|
6556
6765
|
* and writer together. It never reinterprets one track under another name. The
|
|
6557
|
-
* `key` remounts the input so `defaultValue` starts at the new
|
|
6558
|
-
*
|
|
6559
|
-
*
|
|
6766
|
+
* `key` remounts the input so `defaultValue` starts at the new target's value.
|
|
6767
|
+
*
|
|
6768
|
+
* A drag has TWO writers, and which one runs is the whole reason this row is not
|
|
6769
|
+
* a one-liner. `onInput` runs on every frame and must cost a wallpaper-layer
|
|
6770
|
+
* property write (./wallpaper's `setWpOpacity` / `setWpFilter`) — the same cheap
|
|
6771
|
+
* path the Interface page uses. `onChange` runs once, when the gesture ends, and
|
|
6772
|
+
* is the one that goes through `setImageEffects`: that is a full repaint (skin,
|
|
6773
|
+
* palette, wallpaper), and paying it per frame made a drag stall on a big image.
|
|
6560
6774
|
*/
|
|
6561
|
-
function WallpaperEffectRow({ p, rule, field, onField }) {
|
|
6775
|
+
function WallpaperEffectRow({ p, rule, image, painted, field, onField, active }) {
|
|
6562
6776
|
const { t } = p;
|
|
6777
|
+
/**
|
|
6778
|
+
* One frame of a drag: write the image in place, then touch only the layer
|
|
6779
|
+
* property that field owns.
|
|
6780
|
+
*
|
|
6781
|
+
* Gated on `active && painted`: the wallpaper layer paints the ACTIVE rule's
|
|
6782
|
+
* CURRENT image, so previewing any other one would write this card's numbers onto
|
|
6783
|
+
* a different picture's pixels — the visible lie the per-image split makes
|
|
6784
|
+
* possible and this gate prevents. The commit still lands, addressed by slot.
|
|
6785
|
+
*
|
|
6786
|
+
* Writing in place rather than through `setImageEffects` is deliberate and narrow:
|
|
6787
|
+
* that one bumps `rulesRev` and closes with a repaint, which is exactly the
|
|
6788
|
+
* per-frame cost being removed. Nothing is lost — the gesture's `onChange` hands
|
|
6789
|
+
* the settled value over, and that path clamps and saves.
|
|
6790
|
+
*/
|
|
6791
|
+
const preview = (patch, paint) => {
|
|
6792
|
+
if (!active || !painted || image === void 0) return;
|
|
6793
|
+
Object.assign(image, patch);
|
|
6794
|
+
paint();
|
|
6795
|
+
};
|
|
6796
|
+
const slot = image?.slot;
|
|
6563
6797
|
const spec = field === "opacity" ? {
|
|
6564
6798
|
label: t("ruleOpacity"),
|
|
6565
6799
|
min: 0,
|
|
6566
6800
|
max: 100,
|
|
6567
6801
|
step: 1,
|
|
6568
|
-
def: Math.round(
|
|
6802
|
+
def: Math.round((image?.opacity ?? 1) * 100),
|
|
6569
6803
|
fmt: (v) => `${v}%`,
|
|
6570
|
-
|
|
6804
|
+
preview: (v) => preview({ opacity: v / 100 }, () => setWpOpacity(v / 100)),
|
|
6805
|
+
onChange: (v) => {
|
|
6806
|
+
if (slot !== void 0) p.setImageEffects(rule.id, slot, { opacity: v / 100 });
|
|
6807
|
+
}
|
|
6571
6808
|
} : field === "brightness" ? {
|
|
6572
6809
|
label: t("ruleBrightness"),
|
|
6573
6810
|
min: 0,
|
|
6574
6811
|
max: 200,
|
|
6575
6812
|
step: 1,
|
|
6576
|
-
def:
|
|
6813
|
+
def: image?.brightness ?? 100,
|
|
6577
6814
|
fmt: (v) => `${v}%`,
|
|
6578
|
-
|
|
6815
|
+
preview: (v) => preview({ brightness: v }, setWpFilter),
|
|
6816
|
+
onChange: (v) => {
|
|
6817
|
+
if (slot !== void 0) p.setImageEffects(rule.id, slot, { brightness: v });
|
|
6818
|
+
}
|
|
6579
6819
|
} : {
|
|
6580
6820
|
label: t("ruleBlur"),
|
|
6581
6821
|
min: 0,
|
|
6582
6822
|
max: 60,
|
|
6583
6823
|
step: 1,
|
|
6584
|
-
def:
|
|
6824
|
+
def: image?.blur ?? 0,
|
|
6585
6825
|
fmt: (v) => `${v}px`,
|
|
6586
|
-
|
|
6826
|
+
preview: (v) => preview({ blur: v }, setWpFilter),
|
|
6827
|
+
onChange: (v) => {
|
|
6828
|
+
if (slot !== void 0) p.setImageEffects(rule.id, slot, { blur: v });
|
|
6829
|
+
}
|
|
6587
6830
|
};
|
|
6831
|
+
const disabled = image === void 0;
|
|
6588
6832
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6589
6833
|
className: "dab-wp-row",
|
|
6590
6834
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
6591
6835
|
className: "dab-select",
|
|
6592
6836
|
value: field,
|
|
6837
|
+
disabled,
|
|
6838
|
+
title: disabled ? t("ruleEffectNoImage") : void 0,
|
|
6593
6839
|
"aria-label": t("ruleEffectPick"),
|
|
6594
6840
|
onChange: (e) => onField(e.target.value),
|
|
6595
6841
|
children: [
|
|
@@ -6613,8 +6859,9 @@ window.__ModuleLoader__.load({
|
|
|
6613
6859
|
step: spec.step,
|
|
6614
6860
|
def: spec.def,
|
|
6615
6861
|
fmt: spec.fmt,
|
|
6862
|
+
onInput: spec.preview,
|
|
6616
6863
|
onChange: spec.onChange
|
|
6617
|
-
}, field)]
|
|
6864
|
+
}, `${field}:${slot ?? ""}`)]
|
|
6618
6865
|
});
|
|
6619
6866
|
}
|
|
6620
6867
|
function RuleCard({ p, rule, index, total, active, liveIndex, notify, sel, onSel, field, onField, onRemove }) {
|
|
@@ -6642,6 +6889,15 @@ window.__ModuleLoader__.load({
|
|
|
6642
6889
|
const selIdx = rule.images.length === 0 ? 0 : Math.min(sel, rule.images.length - 1);
|
|
6643
6890
|
const image = rule.images[selIdx];
|
|
6644
6891
|
const url = image === void 0 ? null : p.imageOf(image.slot);
|
|
6892
|
+
/**
|
|
6893
|
+
* Whether the image being EDITED is also the one on screen.
|
|
6894
|
+
*
|
|
6895
|
+
* The wallpaper layer paints the active rule's CURRENT image, so a drag preview
|
|
6896
|
+
* may only write a layer property when the two coincide — otherwise the card's
|
|
6897
|
+
* numbers would land on another picture's pixels. This is the per-image split's
|
|
6898
|
+
* own version of the `active` gate beside it.
|
|
6899
|
+
*/
|
|
6900
|
+
const painted = active && selIdx === liveIndex;
|
|
6645
6901
|
const ownColor = image === void 0 ? rule.color : image.color;
|
|
6646
6902
|
const setColor = (color) => {
|
|
6647
6903
|
if (image === void 0) p.setRule(rule.id, { color });
|
|
@@ -7215,14 +7471,21 @@ window.__ModuleLoader__.load({
|
|
|
7215
7471
|
})
|
|
7216
7472
|
]
|
|
7217
7473
|
})] }),
|
|
7218
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7474
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7219
7475
|
style: { marginTop: 16 },
|
|
7220
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WallpaperEffectRow, {
|
|
7476
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WallpaperEffectRow, {
|
|
7221
7477
|
p,
|
|
7222
7478
|
rule,
|
|
7479
|
+
image,
|
|
7480
|
+
painted,
|
|
7223
7481
|
field,
|
|
7224
|
-
onField
|
|
7225
|
-
|
|
7482
|
+
onField,
|
|
7483
|
+
active
|
|
7484
|
+
}), image === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7485
|
+
className: "dab-hint",
|
|
7486
|
+
style: { marginTop: 8 },
|
|
7487
|
+
children: t("ruleEffectNoImage")
|
|
7488
|
+
}) : null]
|
|
7226
7489
|
}),
|
|
7227
7490
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7228
7491
|
style: { marginTop: 14 },
|
|
@@ -8550,13 +8813,7 @@ window.__ModuleLoader__.load({
|
|
|
8550
8813
|
const added = [];
|
|
8551
8814
|
for (const dataUrl of dataUrls) {
|
|
8552
8815
|
const slot = nextSlot();
|
|
8553
|
-
|
|
8554
|
-
slot,
|
|
8555
|
-
bgMode: "fit",
|
|
8556
|
-
bgState: { ...DEFAULT_BG_STATE },
|
|
8557
|
-
color: null
|
|
8558
|
-
};
|
|
8559
|
-
rule.images.push(image);
|
|
8816
|
+
rule.images.push(defaultImage(slot));
|
|
8560
8817
|
added.push(slot);
|
|
8561
8818
|
setImage(slot, dataUrl);
|
|
8562
8819
|
writeImage(slot, dataUrl);
|
|
@@ -8632,6 +8889,28 @@ window.__ModuleLoader__.load({
|
|
|
8632
8889
|
saveConfig();
|
|
8633
8890
|
repaintIfMoved(id, winner);
|
|
8634
8891
|
},
|
|
8892
|
+
/**
|
|
8893
|
+
* Store ONE image's three paint effects. Addressed by slot like the mode and
|
|
8894
|
+
* the framing: how a picture is exposed belongs to that picture, and a rule's
|
|
8895
|
+
* rotation can run a daylight photo next to a night shot.
|
|
8896
|
+
*
|
|
8897
|
+
* `normalizeRuleInPlace` is the clamp, so a drag can never write a value the
|
|
8898
|
+
* next load would trim; the layer's own preview path (`setWpOpacity` /
|
|
8899
|
+
* `setWpFilter`) has already painted the value for the frames of the gesture.
|
|
8900
|
+
*/
|
|
8901
|
+
setImageEffects: (id, slot, patch) => {
|
|
8902
|
+
const rule = ruleById(id);
|
|
8903
|
+
const image = rule?.images.find((i) => i.slot === slot);
|
|
8904
|
+
if (rule === null || image === void 0) return;
|
|
8905
|
+
const winner = winnerId();
|
|
8906
|
+
if (patch.opacity !== void 0) image.opacity = patch.opacity;
|
|
8907
|
+
if (patch.brightness !== void 0) image.brightness = patch.brightness;
|
|
8908
|
+
if (patch.blur !== void 0) image.blur = patch.blur;
|
|
8909
|
+
normalizeRuleInPlace(rule);
|
|
8910
|
+
rulesRev++;
|
|
8911
|
+
saveConfig();
|
|
8912
|
+
repaintIfMoved(id, winner);
|
|
8913
|
+
},
|
|
8635
8914
|
setRuleRotation: (id, patch) => {
|
|
8636
8915
|
const rule = ruleById(id);
|
|
8637
8916
|
if (rule === null) return;
|
|
@@ -8684,12 +8963,7 @@ window.__ModuleLoader__.load({
|
|
|
8684
8963
|
const res = await fetchImageUrl(slot, url);
|
|
8685
8964
|
if (!res.ok) return res;
|
|
8686
8965
|
const winner = winnerId();
|
|
8687
|
-
rule.images.push(
|
|
8688
|
-
slot,
|
|
8689
|
-
bgMode: "fit",
|
|
8690
|
-
bgState: { ...DEFAULT_BG_STATE },
|
|
8691
|
-
color: null
|
|
8692
|
-
});
|
|
8966
|
+
rule.images.push(defaultImage(slot));
|
|
8693
8967
|
setImage(slot, res.dataUrl ?? null);
|
|
8694
8968
|
rulesRev++;
|
|
8695
8969
|
persistConfig();
|