wallpaper-engine-dsh 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +136 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ A DSH bundle that turns your local **Wallpaper Engine** library into the live ba
|
|
|
10
10
|
|
|
11
11
|
**Legibility on any wallpaper.** Text stays readable by construction, not by luck. The veil always opposes the text colour — black in the dark theme, white in the light theme — so neither a pitch-black nor a pure-white wallpaper can swallow the UI. On top of that, a Smart veil samples the wallpaper's brightness every few seconds and automatically strengthens the veil when a scene drifts too bright (or too dark) for the current theme; it also strengthens the border contrast and the glass frosting on the same evidence. Dropdowns, menus, and dialogs sit on DSH's opaque surfaces and are never affected.
|
|
12
12
|
|
|
13
|
-
**Manual framing.** Pick a base layout — Cover, Contain, Stretch, Original, or Free — then frame the shot yourself. **Adjust** opens the live wallpaper fullscreen: scroll to zoom, drag to pan. In the first four layouts the crop is clamped so the image always fills the screen with no exposed edges; **Free** places the wallpaper at natural size on a black canvas where it can be dragged anywhere
|
|
13
|
+
**Manual framing.** Pick a base layout — Cover, Contain, Stretch, Original, or Free — then frame the shot yourself. **Adjust** opens the live wallpaper fullscreen: scroll to zoom, drag to pan. In the first four layouts the crop is clamped so the image always fills the screen with no exposed edges; **Free** places the wallpaper at natural size on a black canvas where it can be dragged anywhere, zoomed from 0.1× to 4×, and rotated to any angle (±90° buttons or Alt+drag), with just enough kept on screen that it can never be dragged out of sight. **Mirror** flips the wallpaper horizontally and works in every layout — a flip never exposes an edge. Esc or Done finishes, the framing persists, and **Reset crop** restores the default in one click. Web wallpapers take the same select and the same zoom/pan/rotate framing — the transform applies to the iframe directly.
|
|
14
14
|
|
|
15
15
|
**Four live sliders.** Wallpaper blur, Scrim, Borders, and Glass — every change lands instantly and persists. Blur renders in place: composition, layout, and crop stay identical at every blur level, and within one blur radius of the screen edge the picture fades softly into the veil beneath the UI chrome. A whisper of dither noise keeps smooth gradients band-free; at zero blur the media bypasses the filter pipeline entirely for bit-exact pixels.
|
|
16
16
|
|
package/README.zh.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
**任何壁纸上文字都可读。** 遮罩颜色始终与文字颜色相反:深色主题用黑色遮罩,浅色主题用白色遮罩,因此过亮或过暗的壁纸都不会盖住文字。在此基础上,「智能可视」每隔几秒采样一次壁纸亮度,亮度过高或过低时自动加强遮罩。下拉菜单、弹窗等浮层显示在 DSH 原生的不透明表面上,不受影响。
|
|
12
12
|
|
|
13
|
-
**手动取景。** 先选基础排布(填充、适应、拉伸、原始、自由),再手动调整画面:点「调整画面」将壁纸全屏展开,滚轮缩放,拖动平移。前四种排布下裁剪范围经过限制,画面始终铺满屏幕,不露出边缘;「自由」把壁纸按原始大小放在黑色画布上,可拖动到任意位置,缩放范围 0.1–4
|
|
13
|
+
**手动取景。** 先选基础排布(填充、适应、拉伸、原始、自由),再手动调整画面:点「调整画面」将壁纸全屏展开,滚轮缩放,拖动平移。前四种排布下裁剪范围经过限制,画面始终铺满屏幕,不露出边缘;「自由」把壁纸按原始大小放在黑色画布上,可拖动到任意位置,缩放范围 0.1–4×,并可旋转任意角度(±90° 按钮或 Alt+拖动),同时始终保留一小块在屏幕内,不会拖丢。「镜像」水平翻转壁纸,所有排布下都可用——翻转不会破坏铺满。按 Esc 或点「完成」退出,取景设置会保存,「重置裁剪」可一键恢复默认。网页壁纸使用相同的排布下拉与缩放/平移/旋转取景(变换直接作用于 iframe)。
|
|
14
14
|
|
|
15
15
|
**四个实时滑杆。** 壁纸模糊、暗化、边框、玻璃,每一步调节立即生效并保存。模糊在原位渲染:任意模糊度下,画面的比例、排布与取景完全一致;屏幕边缘一个模糊半径内的画面向遮罩过渡,被界面边饰覆盖,通常不可见。模糊时叠加轻微噪声,消除渐变色带;模糊为零时媒体完全绕过滤镜管线,像素保持不变。
|
|
16
16
|
|
package/lib/client.js
CHANGED
|
@@ -95,6 +95,12 @@ window.__ModuleLoader__.load({
|
|
|
95
95
|
adjust: "调整画面",
|
|
96
96
|
adjustDone: "完成",
|
|
97
97
|
adjustHint: "拖动平移 · 滚轮缩放 · Esc 或点「完成」退出",
|
|
98
|
+
adjustHintFree: "拖动平移 · 滚轮缩放 · Alt+拖动旋转 · Esc 或点「完成」退出",
|
|
99
|
+
mirror: "镜像",
|
|
100
|
+
rotateCcw: "⟲",
|
|
101
|
+
rotateCw: "⟳",
|
|
102
|
+
rotateCcwTitle: "逆时针旋转 90°",
|
|
103
|
+
rotateCwTitle: "顺时针旋转 90°",
|
|
98
104
|
resetCrop: "重置裁剪",
|
|
99
105
|
statusGroup: (name, total, usable, interval, order) =>
|
|
100
106
|
"列表「" + name + "」:" + total + " 项 · " + usable + " 可用 · 每 " + interval + " 分钟 · " + order,
|
|
@@ -173,6 +179,12 @@ window.__ModuleLoader__.load({
|
|
|
173
179
|
adjust: "Adjust",
|
|
174
180
|
adjustDone: "Done",
|
|
175
181
|
adjustHint: "Drag to pan · wheel to zoom · Esc or Done to exit",
|
|
182
|
+
adjustHintFree: "Drag to pan · wheel to zoom · Alt+drag to rotate · Esc or Done to exit",
|
|
183
|
+
mirror: "Mirror",
|
|
184
|
+
rotateCcw: "⟲",
|
|
185
|
+
rotateCw: "⟳",
|
|
186
|
+
rotateCcwTitle: "Rotate 90° counterclockwise",
|
|
187
|
+
rotateCwTitle: "Rotate 90° clockwise",
|
|
176
188
|
resetCrop: "Reset crop",
|
|
177
189
|
statusGroup: (name, total, usable, interval, order) =>
|
|
178
190
|
"List \"" + name + "\": " + total + " items · " + usable + " usable · every " + interval + " min · " + order,
|
|
@@ -226,6 +238,8 @@ window.__ModuleLoader__.load({
|
|
|
226
238
|
zoom: 1,
|
|
227
239
|
offsetX: 0,
|
|
228
240
|
offsetY: 0,
|
|
241
|
+
rotation: 0, // free-fit only; degrees, positive = clockwise
|
|
242
|
+
mirrored: false, // horizontal flip, available in every fit mode
|
|
229
243
|
};
|
|
230
244
|
|
|
231
245
|
const FIT_MODES = ["cover", "contain", "fill", "none", "free"];
|
|
@@ -281,6 +295,8 @@ window.__ModuleLoader__.load({
|
|
|
281
295
|
zoom: clampNum(o.zoom, 0.1, 4, DEFAULTS.zoom),
|
|
282
296
|
offsetX: clampNum(o.offsetX, -300, 300, DEFAULTS.offsetX),
|
|
283
297
|
offsetY: clampNum(o.offsetY, -300, 300, DEFAULTS.offsetY),
|
|
298
|
+
rotation: clampNum(o.rotation, -1080, 1080, DEFAULTS.rotation),
|
|
299
|
+
mirrored: o.mirrored === true,
|
|
284
300
|
};
|
|
285
301
|
} catch {
|
|
286
302
|
return { id: "", ...DEFAULTS };
|
|
@@ -339,6 +355,8 @@ window.__ModuleLoader__.load({
|
|
|
339
355
|
zoom: selection.zoom,
|
|
340
356
|
offsetX: selection.offsetX,
|
|
341
357
|
offsetY: selection.offsetY,
|
|
358
|
+
rotation: selection.rotation,
|
|
359
|
+
mirrored: selection.mirrored,
|
|
342
360
|
}));
|
|
343
361
|
} catch { /* storage full / blocked → session-only state, not fatal */ }
|
|
344
362
|
}
|
|
@@ -734,18 +752,32 @@ window.__ModuleLoader__.load({
|
|
|
734
752
|
//
|
|
735
753
|
// Crop geometry is chosen by the fit mode. The four object-fit modes keep a
|
|
736
754
|
// GAP-FREE invariant: the media box covers the viewport exactly at zoom 1,
|
|
737
|
-
// zoom floors at 1,
|
|
738
|
-
//
|
|
739
|
-
//
|
|
740
|
-
//
|
|
741
|
-
//
|
|
755
|
+
// zoom floors at 1, pan is clamped to ±(zoom−1)/2·100 %, and rotation is
|
|
756
|
+
// forced to 0 — a rotated rectangle cannot cover the screen, so turning
|
|
757
|
+
// belongs to free mode only. Mirroring is allowed everywhere: a flip is
|
|
758
|
+
// symmetric and never breaks coverage. The "free" mode drops the gap-free
|
|
759
|
+
// invariant on purpose: the media sits at natural size on a black canvas,
|
|
760
|
+
// zoom runs 0.1–4×, rotation is arbitrary, and pan is only clamped to keep
|
|
761
|
+
// a sliver (≥10 % of the box) on screen so the wallpaper can never be
|
|
762
|
+
// dragged entirely out of sight.
|
|
742
763
|
function clampCrop() {
|
|
764
|
+
if (!isFreeFit()) selection.rotation = 0;
|
|
765
|
+
else {
|
|
766
|
+
// Keep the stored angle canonical (−180, 180]: Alt+drag can accumulate
|
|
767
|
+
// several full turns, and persisting raw degrees would come back from
|
|
768
|
+
// storage truncated at the ±1080 read clamp — a DIFFERENT visual angle.
|
|
769
|
+
// Modulo keeps every equivalent representation identical on disk.
|
|
770
|
+
selection.rotation = ((selection.rotation + 180) % 360 + 360) % 360 - 180;
|
|
771
|
+
}
|
|
743
772
|
selection.zoom = Math.round(Math.min(4, Math.max(isFreeFit() ? 0.1 : 1, selection.zoom)) * 100) / 100;
|
|
744
773
|
if (isFreeFit()) {
|
|
745
774
|
// Content spans ±zoom·50 % of the box around its centre; the pan offset
|
|
746
|
-
// shifts it by its own % of the box.
|
|
747
|
-
//
|
|
748
|
-
|
|
775
|
+
// shifts it by its own % of the box. Rotation expands the projected
|
|
776
|
+
// extent by k = |cos θ| + |sin θ| (up to √2 at 45°). |offset| ≤
|
|
777
|
+
// (zoom·k+1)·50 − 10 keeps ≥10 % of the media visible in both axes.
|
|
778
|
+
const rad = selection.rotation * Math.PI / 180;
|
|
779
|
+
const k = Math.abs(Math.cos(rad)) + Math.abs(Math.sin(rad));
|
|
780
|
+
const reach = (selection.zoom * k + 1) * 50 - 10;
|
|
749
781
|
selection.offsetX = Math.min(reach, Math.max(-reach, selection.offsetX));
|
|
750
782
|
selection.offsetY = Math.min(reach, Math.max(-reach, selection.offsetY));
|
|
751
783
|
return;
|
|
@@ -786,12 +818,31 @@ window.__ModuleLoader__.load({
|
|
|
786
818
|
bar.className = "webg-adjust-bar";
|
|
787
819
|
const hint = document.createElement("span");
|
|
788
820
|
hint.className = "webg-adjust-hint";
|
|
789
|
-
hint.textContent = S().adjustHint;
|
|
821
|
+
hint.textContent = isFreeFit() ? S().adjustHintFree : S().adjustHint;
|
|
790
822
|
const done = document.createElement("button");
|
|
791
823
|
done.type = "button";
|
|
792
824
|
done.className = "webg-btn webg-btn--primary";
|
|
793
825
|
done.textContent = S().adjustDone;
|
|
794
826
|
bar.appendChild(hint);
|
|
827
|
+
// Free fit only: a rotated rectangle cannot cover the screen, so turning
|
|
828
|
+
// exists solely where exposed edges are allowed. ±90° buttons cover the
|
|
829
|
+
// common cases; arbitrary angles come from Alt+drag below.
|
|
830
|
+
if (isFreeFit()) {
|
|
831
|
+
for (const dir of [-90, 90]) {
|
|
832
|
+
const rot = document.createElement("button");
|
|
833
|
+
rot.type = "button";
|
|
834
|
+
rot.className = "webg-btn";
|
|
835
|
+
rot.textContent = dir < 0 ? S().rotateCcw : S().rotateCw;
|
|
836
|
+
rot.title = dir < 0 ? S().rotateCcwTitle : S().rotateCwTitle;
|
|
837
|
+
rot.addEventListener("click", () => {
|
|
838
|
+
selection.rotation += dir;
|
|
839
|
+
clampCrop();
|
|
840
|
+
applyEffects();
|
|
841
|
+
persistSelection();
|
|
842
|
+
});
|
|
843
|
+
bar.appendChild(rot);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
795
846
|
bar.appendChild(done);
|
|
796
847
|
overlay.appendChild(bar);
|
|
797
848
|
document.body.appendChild(overlay);
|
|
@@ -801,14 +852,34 @@ window.__ModuleLoader__.load({
|
|
|
801
852
|
// translate() in the transform list is applied AFTER scale() (right-to-
|
|
802
853
|
// left), so pan deltas are independent of zoom: dx px = dx/width %.
|
|
803
854
|
let drag = null;
|
|
855
|
+
// Alt+drag (free fit): swing the media around the overlay's centre —
|
|
856
|
+
// the angle delta between pointer positions becomes the rotation delta.
|
|
857
|
+
let rot = null; // { a0: starting pointer angle, base: starting rotation }
|
|
858
|
+
const centerAngle = (e) => {
|
|
859
|
+
const cx = (overlay.clientWidth || 1) / 2;
|
|
860
|
+
const cy = (overlay.clientHeight || 1) / 2;
|
|
861
|
+
return Math.atan2(e.clientY - cy, e.clientX - cx);
|
|
862
|
+
};
|
|
804
863
|
const onPointerDown = (e) => {
|
|
805
864
|
if (e.target === done || bar.contains(e.target)) return;
|
|
806
|
-
|
|
865
|
+
if (e.altKey && isFreeFit()) {
|
|
866
|
+
rot = { a0: centerAngle(e), base: selection.rotation };
|
|
867
|
+
} else {
|
|
868
|
+
drag = { x: e.clientX, y: e.clientY };
|
|
869
|
+
}
|
|
870
|
+
// Capture for EITHER gesture: pointer moves must keep arriving even
|
|
871
|
+
// when the cursor leaves the window mid-swing.
|
|
807
872
|
if (overlay.setPointerCapture && e.pointerId !== undefined) {
|
|
808
873
|
try { overlay.setPointerCapture(e.pointerId); } catch {}
|
|
809
874
|
}
|
|
810
875
|
};
|
|
811
876
|
const onPointerMove = (e) => {
|
|
877
|
+
if (rot) {
|
|
878
|
+
selection.rotation = rot.base + (centerAngle(e) - rot.a0) * 180 / Math.PI;
|
|
879
|
+
clampCrop();
|
|
880
|
+
applyEffects();
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
812
883
|
if (!drag) return;
|
|
813
884
|
// translate % refers to the media box, which is the viewport exactly —
|
|
814
885
|
// divide the pixel delta by it so the content tracks the cursor 1:1.
|
|
@@ -820,18 +891,16 @@ window.__ModuleLoader__.load({
|
|
|
820
891
|
drag = { x: e.clientX, y: e.clientY };
|
|
821
892
|
applyEffects();
|
|
822
893
|
};
|
|
823
|
-
const
|
|
894
|
+
const endPointer = () => {
|
|
895
|
+
if (rot) { rot = null; persistSelection(); return; }
|
|
824
896
|
if (!drag) return;
|
|
825
897
|
drag = null;
|
|
826
898
|
persistSelection();
|
|
827
899
|
};
|
|
900
|
+
const onPointerUp = endPointer;
|
|
828
901
|
// pointercancel (touch gesture takeover): end the drag without losing the
|
|
829
902
|
// current offsets — otherwise drag stays "stuck" until the next pointerup.
|
|
830
|
-
const onPointerCancel =
|
|
831
|
-
if (!drag) return;
|
|
832
|
-
drag = null;
|
|
833
|
-
persistSelection();
|
|
834
|
-
};
|
|
903
|
+
const onPointerCancel = endPointer;
|
|
835
904
|
const onWheel = (e) => {
|
|
836
905
|
if (typeof e.preventDefault === "function") e.preventDefault();
|
|
837
906
|
// clampCrop enforces the mode's zoom floor: the gap-free modes never
|
|
@@ -870,6 +939,7 @@ window.__ModuleLoader__.load({
|
|
|
870
939
|
selection.zoom = 1;
|
|
871
940
|
selection.offsetX = 0;
|
|
872
941
|
selection.offsetY = 0;
|
|
942
|
+
selection.rotation = 0; // framing geometry home; mirroring keeps its own toggle
|
|
873
943
|
persistSelection();
|
|
874
944
|
applyEffects();
|
|
875
945
|
emit();
|
|
@@ -1028,7 +1098,7 @@ window.__ModuleLoader__.load({
|
|
|
1028
1098
|
const key = [
|
|
1029
1099
|
dark, selection.scrim, selection.border, selection.blur, selection.wallpaperBlur,
|
|
1030
1100
|
selection.autoScrim, sampledLuminance, selection.fit, selection.zoom,
|
|
1031
|
-
selection.offsetX, selection.offsetY,
|
|
1101
|
+
selection.offsetX, selection.offsetY, selection.rotation, selection.mirrored,
|
|
1032
1102
|
].join("\u0000");
|
|
1033
1103
|
if (key === lastEffectKey) return; // nothing it reads changed → no writes, no reflow
|
|
1034
1104
|
lastEffectKey = key;
|
|
@@ -1071,6 +1141,8 @@ window.__ModuleLoader__.load({
|
|
|
1071
1141
|
s.setProperty("--webg-zoom", String(selection.zoom));
|
|
1072
1142
|
s.setProperty("--webg-offset-x", selection.offsetX + "%");
|
|
1073
1143
|
s.setProperty("--webg-offset-y", selection.offsetY + "%");
|
|
1144
|
+
s.setProperty("--webg-rotate", selection.rotation + "deg");
|
|
1145
|
+
s.setProperty("--webg-mirror", selection.mirrored ? "-1" : "1");
|
|
1074
1146
|
|
|
1075
1147
|
// Write the scrim colour directly too, then force a synchronous layout, so
|
|
1076
1148
|
// slider feedback lands on this frame even on stalled compositors.
|
|
@@ -1097,6 +1169,8 @@ window.__ModuleLoader__.load({
|
|
|
1097
1169
|
s.removeProperty("--webg-zoom");
|
|
1098
1170
|
s.removeProperty("--webg-offset-x");
|
|
1099
1171
|
s.removeProperty("--webg-offset-y");
|
|
1172
|
+
s.removeProperty("--webg-rotate");
|
|
1173
|
+
s.removeProperty("--webg-mirror");
|
|
1100
1174
|
const scrim = document.getElementById(SCRIM_ID);
|
|
1101
1175
|
if (scrim) scrim.style.background = "";
|
|
1102
1176
|
}
|
|
@@ -1292,14 +1366,23 @@ window.__ModuleLoader__.load({
|
|
|
1292
1366
|
function onFit(e) {
|
|
1293
1367
|
selection.fit = FIT_MODES.indexOf(e.target.value) >= 0 ? e.target.value : "cover";
|
|
1294
1368
|
// Re-seat the crop into the new mode's envelope: leaving free for a
|
|
1295
|
-
// gap-free mode pulls zoom back up to 1
|
|
1296
|
-
// direction only ever relaxes bounds, so the framing
|
|
1369
|
+
// gap-free mode pulls zoom back up to 1, reins the pan in and zeroes the
|
|
1370
|
+
// rotation; the other direction only ever relaxes bounds, so the framing
|
|
1371
|
+
// carries over. Mirroring survives every switch — a flip never breaks
|
|
1372
|
+
// coverage.
|
|
1297
1373
|
clampCrop();
|
|
1298
1374
|
persistSelection();
|
|
1299
1375
|
applyEffects();
|
|
1300
1376
|
emit();
|
|
1301
1377
|
}
|
|
1302
1378
|
|
|
1379
|
+
function onToggleMirror() {
|
|
1380
|
+
selection.mirrored = !selection.mirrored;
|
|
1381
|
+
persistSelection();
|
|
1382
|
+
applyEffects();
|
|
1383
|
+
emit();
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1303
1386
|
// ── Picker sections (presentational; every input arrives as an argument) ────
|
|
1304
1387
|
// WallpaperPicker composes these in a fixed order. Each one owns exactly one
|
|
1305
1388
|
// visual row/block of the settings panel and produces the same DOM the old
|
|
@@ -1539,6 +1622,13 @@ window.__ModuleLoader__.load({
|
|
|
1539
1622
|
...[t.fitCover, t.fitContain, t.fitFill, t.fitNone, t.fitFree].map((label, i) =>
|
|
1540
1623
|
React.createElement("option", { key: FIT_MODES[i], value: FIT_MODES[i] }, label),
|
|
1541
1624
|
)),
|
|
1625
|
+
// Mirror is a coverage-preserving flip: valid in EVERY fit mode and
|
|
1626
|
+
// for every wallpaper kind (the transform applies to iframes too).
|
|
1627
|
+
React.createElement("button", {
|
|
1628
|
+
className: "webg-btn" + (sel.mirrored ? " webg-btn--active" : ""),
|
|
1629
|
+
type: "button",
|
|
1630
|
+
onClick: onToggleMirror,
|
|
1631
|
+
}, t.mirror),
|
|
1542
1632
|
React.createElement("button", {
|
|
1543
1633
|
className: "webg-btn", type: "button", onClick: enterAdjust,
|
|
1544
1634
|
disabled: sel.adjusting,
|
|
@@ -1680,9 +1770,13 @@ window.__ModuleLoader__.load({
|
|
|
1680
1770
|
display: block;
|
|
1681
1771
|
background: transparent; border: 0;
|
|
1682
1772
|
filter: var(--webg-media-filter, none);
|
|
1683
|
-
/* Manual crop:
|
|
1684
|
-
|
|
1685
|
-
|
|
1773
|
+
/* Manual crop: the transform list is ordered pan → rotate → zoom →
|
|
1774
|
+
mirror. translate comes first so pan offsets stay screen-aligned no
|
|
1775
|
+
matter the rotation; mirror is innermost so the flip happens in the
|
|
1776
|
+
media's own axes. */
|
|
1777
|
+
transform: translate(var(--webg-offset-x, 0%), var(--webg-offset-y, 0%))
|
|
1778
|
+
rotate(var(--webg-rotate, 0deg)) scale(var(--webg-zoom, 1))
|
|
1779
|
+
scaleX(var(--webg-mirror, 1));
|
|
1686
1780
|
transform-origin: center center;
|
|
1687
1781
|
}
|
|
1688
1782
|
|
|
@@ -1803,17 +1897,32 @@ window.__ModuleLoader__.load({
|
|
|
1803
1897
|
border-color: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.5));
|
|
1804
1898
|
}
|
|
1805
1899
|
.webg-btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
1806
|
-
/* brand-primary is monochrome (near-black light / near-white dark)
|
|
1807
|
-
|
|
1900
|
+
/* brand-primary is monochrome (near-black light / near-white dark). The
|
|
1901
|
+
shell's brand-primary-INVERT token aliases the same shade as brand-
|
|
1902
|
+
primary itself in both themes (no contrast), so anything sitting ON a
|
|
1903
|
+
brand-primary fill takes its contrast colour from the inverted label
|
|
1904
|
+
ramp instead — near-white on the dark light-theme fill, dark bluish-800
|
|
1905
|
+
on the near-white dark-theme fill. */
|
|
1906
|
+
body {
|
|
1907
|
+
--webg-on-brand: var(--dsw-alias-label-primary-inverted,
|
|
1908
|
+
var(--dsw-alias-label-primary-foreground, rgb(249, 250, 251)));
|
|
1909
|
+
}
|
|
1808
1910
|
.webg-btn--primary {
|
|
1809
1911
|
background: var(--dsw-alias-brand-primary, rgb(15, 17, 21));
|
|
1810
1912
|
border-color: transparent;
|
|
1811
|
-
color: var(--
|
|
1913
|
+
color: var(--webg-on-brand);
|
|
1812
1914
|
}
|
|
1813
1915
|
.webg-btn--primary:hover:not(:disabled) {
|
|
1814
1916
|
background: var(--dsw-alias-brand-primary, rgb(15, 17, 21));
|
|
1815
1917
|
opacity: 0.85;
|
|
1816
1918
|
}
|
|
1919
|
+
/* Toggle-style button showing an engaged state (e.g. 镜像): brand fill
|
|
1920
|
+
with the contrasting on-brand label. */
|
|
1921
|
+
.webg-btn--active {
|
|
1922
|
+
background: var(--dsw-alias-brand-primary, rgb(15, 17, 21));
|
|
1923
|
+
border-color: transparent;
|
|
1924
|
+
color: var(--webg-on-brand);
|
|
1925
|
+
}
|
|
1817
1926
|
|
|
1818
1927
|
.webg-select, .webg-text {
|
|
1819
1928
|
padding: 5px 10px;
|
|
@@ -1873,7 +1982,7 @@ window.__ModuleLoader__.load({
|
|
|
1873
1982
|
-webkit-appearance: none;
|
|
1874
1983
|
width: 14px; height: 14px; border-radius: 50%;
|
|
1875
1984
|
margin-top: -5px; /* centers the 14px thumb on the 4px track */
|
|
1876
|
-
background: var(--
|
|
1985
|
+
background: var(--webg-on-brand);
|
|
1877
1986
|
border: 2.5px solid var(--dsw-alias-brand-primary, rgb(15, 17, 21));
|
|
1878
1987
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
|
|
1879
1988
|
transition: transform 120ms ease;
|
|
@@ -1971,7 +2080,7 @@ window.__ModuleLoader__.load({
|
|
|
1971
2080
|
position: absolute; top: 4px; left: 4px; width: 18px; height: 18px;
|
|
1972
2081
|
border-radius: 4px;
|
|
1973
2082
|
background: var(--dsw-alias-brand-primary, rgb(15, 17, 21));
|
|
1974
|
-
color: var(--
|
|
2083
|
+
color: var(--webg-on-brand);
|
|
1975
2084
|
font-size: 12px; line-height: 18px; text-align: center;
|
|
1976
2085
|
}
|
|
1977
2086
|
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallpaper-engine-dsh",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Wallpaper Engine library as the DSH web GUI background — video/web/still wallpapers, rotation lists, search, resource monitor, bilingual UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|