wallpaper-engine-dsh 1.0.7 → 2.0.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/README.md +1 -0
- package/README.zh.md +1 -0
- package/lib/client.js +98 -24
- package/lib/core.js +7 -1
- package/lib/index.js +1 -1
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ Media serving is defensive by design: tokens are unguessable 72-bit values, stab
|
|
|
64
64
|
|
|
65
65
|
- Scene and application wallpapers are not listed: they are Wallpaper Engine's private `.pkg` scene packages, and their live animation can only be rendered by Wallpaper Engine itself on the desktop.
|
|
66
66
|
- Web wallpapers run isolated from the page's storage, so ones that persist state across loads won't keep it.
|
|
67
|
+
- Web wallpapers with sound may stay silent until you interact with the page — browsers gate audio in sandboxed documents on a user gesture, and wallpapers that rely on Wallpaper Engine's system-audio feed (visualizers) have no signal to draw from.
|
|
67
68
|
- The glass effect reads DSH design tokens; if a future shell redesign renames them, frosting degrades to plain transparency.
|
|
68
69
|
|
|
69
70
|
## Development
|
package/README.zh.md
CHANGED
|
@@ -64,6 +64,7 @@ bundle 分为两半:
|
|
|
64
64
|
|
|
65
65
|
- 场景与应用壁纸不出现在列表中:它们是 Wallpaper Engine 私有的 `.pkg` 场景包,动态渲染只能由桌面端的 Wallpaper Engine 完成。
|
|
66
66
|
- 网页壁纸与页面存储隔离,需要跨加载保存状态的壁纸无法保留状态。
|
|
67
|
+
- 带声音的网页壁纸可能在你与页面交互前保持静默——浏览器对沙箱文档的音频有自动播放限制;依赖 Wallpaper Engine 系统音频输入的壁纸(如音频可视化)在浏览器中没有信号可画。
|
|
67
68
|
- 玻璃效果读 DSH 设计令牌;若未来外壳重构重命名令牌,磨砂会退化为普通半透明。
|
|
68
69
|
|
|
69
70
|
## 开发
|
package/lib/client.js
CHANGED
|
@@ -10,6 +10,11 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
const SETTINGS_KEY = "wallpaper-engine-dsh:selection";
|
|
11
11
|
const INVENTORY_URL = "/we-background/inventory";
|
|
12
12
|
const ACTIVE_ATTR = "data-webg-wallpaper";
|
|
13
|
+
// Plugin-owned dark-theme signal for CSS. The shell's old dark-mode body
|
|
14
|
+
// attribute is gone in DSH >= 0.2.1-rc.5; the client now derives dark from
|
|
15
|
+
// the shell's theme service and mirrors it here, so the stylesheet never
|
|
16
|
+
// keys off host DOM internals.
|
|
17
|
+
const DARK_ATTR = "data-webg-dark";
|
|
13
18
|
const LAYER_ID = "wallpaper-engine-dsh-layer";
|
|
14
19
|
const SCRIM_ID = "wallpaper-engine-dsh-scrim";
|
|
15
20
|
const STYLE_TAG_ID = "wallpaper-engine-dsh/styles";
|
|
@@ -688,6 +693,7 @@ window.__ModuleLoader__.load({
|
|
|
688
693
|
media = document.createElement("iframe");
|
|
689
694
|
media.src = sel.url;
|
|
690
695
|
media.setAttribute("sandbox", "allow-scripts");
|
|
696
|
+
media.setAttribute("allow", "autoplay");
|
|
691
697
|
media.setAttribute("referrerpolicy", "no-referrer");
|
|
692
698
|
media.setAttribute("frameborder", "0");
|
|
693
699
|
media.setAttribute("scrolling", "no");
|
|
@@ -1019,7 +1025,13 @@ window.__ModuleLoader__.load({
|
|
|
1019
1025
|
if (existing) {
|
|
1020
1026
|
existing.classList.add("webg-layer--leave");
|
|
1021
1027
|
const stale = existing;
|
|
1022
|
-
schedule(() =>
|
|
1028
|
+
schedule(() => {
|
|
1029
|
+
const v = stale.querySelector("video");
|
|
1030
|
+
if (v) { v.pause(); v.removeAttribute("src"); if (typeof v.load === "function") v.load(); }
|
|
1031
|
+
const f = stale.querySelector("iframe");
|
|
1032
|
+
if (f) { f.src = "about:blank"; }
|
|
1033
|
+
stale.remove();
|
|
1034
|
+
}, LEAVE_MS);
|
|
1023
1035
|
}
|
|
1024
1036
|
}
|
|
1025
1037
|
const node = document.getElementById(LAYER_ID);
|
|
@@ -1035,11 +1047,18 @@ window.__ModuleLoader__.load({
|
|
|
1035
1047
|
existing.classList.add("webg-layer--leave");
|
|
1036
1048
|
const stale = existing;
|
|
1037
1049
|
schedule(() => {
|
|
1050
|
+
const v = stale.querySelector("video");
|
|
1051
|
+
if (v) { v.pause(); v.removeAttribute("src"); if (typeof v.load === "function") v.load(); }
|
|
1052
|
+
const f = stale.querySelector("iframe");
|
|
1053
|
+
if (f) { f.src = "about:blank"; }
|
|
1038
1054
|
stale.remove();
|
|
1039
1055
|
if (!document.getElementById(LAYER_ID)) {
|
|
1040
1056
|
const s = document.getElementById(SCRIM_ID);
|
|
1041
1057
|
if (s) s.remove();
|
|
1042
1058
|
document.body.removeAttribute(ACTIVE_ATTR);
|
|
1059
|
+
if (previewProbe) { previewProbe.remove(); previewProbe = null; }
|
|
1060
|
+
sampledLuminance = null;
|
|
1061
|
+
clearEffects();
|
|
1043
1062
|
}
|
|
1044
1063
|
}, LEAVE_MS);
|
|
1045
1064
|
}
|
|
@@ -1060,6 +1079,9 @@ window.__ModuleLoader__.load({
|
|
|
1060
1079
|
if (selection.type === "web") {
|
|
1061
1080
|
const w = selection.inventory.wallpapers.find((x) => x.id === selection.id);
|
|
1062
1081
|
if (w && w.preview) probePreviewLuminance(w.preview);
|
|
1082
|
+
} else if (previewProbe) {
|
|
1083
|
+
previewProbe.remove();
|
|
1084
|
+
previewProbe = null;
|
|
1063
1085
|
}
|
|
1064
1086
|
}
|
|
1065
1087
|
}
|
|
@@ -1076,6 +1098,9 @@ window.__ModuleLoader__.load({
|
|
|
1076
1098
|
// tick would rewrite all CSS variables AND force a synchronous layout for
|
|
1077
1099
|
// nothing. clearEffects() resets it so the next apply always writes.
|
|
1078
1100
|
let lastEffectKey = null;
|
|
1101
|
+
// Resolved dark-theme state, driven by the shell theme service (or the OS
|
|
1102
|
+
// prefers-color-scheme fallback). Mirrored onto body[DARK_ATTR] for CSS.
|
|
1103
|
+
let themeDark = false;
|
|
1079
1104
|
|
|
1080
1105
|
/** Average Rec.601 luminance (0..1) of a drawable media frame, or null. */
|
|
1081
1106
|
function luminanceOf(media) {
|
|
@@ -1146,10 +1171,20 @@ window.__ModuleLoader__.load({
|
|
|
1146
1171
|
// The veil OPPOSES the text colour: dark theme (light text) darkens bright
|
|
1147
1172
|
// wallpapers, light theme (dark text) brightens dark ones — either way the
|
|
1148
1173
|
// text side wins. This is what makes ANY wallpaper survivable.
|
|
1149
|
-
const dark =
|
|
1174
|
+
const dark = themeDark;
|
|
1175
|
+
if (typeof document !== "undefined" && document.body) {
|
|
1176
|
+
if (dark) {
|
|
1177
|
+
if (!(document.body.hasAttribute && document.body.hasAttribute(DARK_ATTR))) {
|
|
1178
|
+
document.body.setAttribute(DARK_ATTR, "");
|
|
1179
|
+
}
|
|
1180
|
+
} else if (document.body.hasAttribute && document.body.hasAttribute(DARK_ATTR)) {
|
|
1181
|
+
document.body.removeAttribute(DARK_ATTR);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
const roundedLum = sampledLuminance !== null ? Math.round(sampledLuminance * 100) / 100 : null;
|
|
1150
1185
|
const key = [
|
|
1151
1186
|
dark, selection.scrim, selection.border, selection.blur, selection.wallpaperBlur,
|
|
1152
|
-
selection.autoScrim,
|
|
1187
|
+
selection.autoScrim, roundedLum, selection.fit, selection.zoom,
|
|
1153
1188
|
selection.offsetX, selection.offsetY, selection.rotation, selection.mirrored,
|
|
1154
1189
|
].join("\u0000");
|
|
1155
1190
|
if (key === lastEffectKey) return; // nothing it reads changed → no writes, no reflow
|
|
@@ -1200,9 +1235,6 @@ window.__ModuleLoader__.load({
|
|
|
1200
1235
|
// slider feedback lands on this frame even on stalled compositors.
|
|
1201
1236
|
const scrim = document.getElementById(SCRIM_ID);
|
|
1202
1237
|
if (scrim) scrim.style.background = veilColor;
|
|
1203
|
-
if (document.body && document.body.offsetHeight !== undefined) {
|
|
1204
|
-
void document.body.offsetHeight;
|
|
1205
|
-
}
|
|
1206
1238
|
}
|
|
1207
1239
|
|
|
1208
1240
|
function clearEffects() {
|
|
@@ -1242,7 +1274,7 @@ window.__ModuleLoader__.load({
|
|
|
1242
1274
|
}
|
|
1243
1275
|
onInput(v);
|
|
1244
1276
|
};
|
|
1245
|
-
return React.createElement(React.Fragment,
|
|
1277
|
+
return React.createElement(React.Fragment, { key: label },
|
|
1246
1278
|
React.createElement("span", { className: "webg-hint webg-label" }, label),
|
|
1247
1279
|
React.createElement("input", {
|
|
1248
1280
|
className: "webg-slider", type: "range",
|
|
@@ -1446,6 +1478,7 @@ window.__ModuleLoader__.load({
|
|
|
1446
1478
|
className: "webg-text webg-search", type: "search",
|
|
1447
1479
|
placeholder: t.searchPlaceholder,
|
|
1448
1480
|
value: query,
|
|
1481
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1449
1482
|
onInput: (e) => setQuery(e.target.value),
|
|
1450
1483
|
}),
|
|
1451
1484
|
query.trim() && React.createElement("span", { className: "webg-hint" },
|
|
@@ -1476,14 +1509,14 @@ window.__ModuleLoader__.load({
|
|
|
1476
1509
|
React.createElement("span", { className: "webg-card-close" }, t.closeCard),
|
|
1477
1510
|
),
|
|
1478
1511
|
usableList.length === 0
|
|
1479
|
-
? React.createElement("span", { className: "webg-hint" },
|
|
1512
|
+
? React.createElement("span", { className: "webg-hint webg-grid-empty" },
|
|
1480
1513
|
sel.inventory.installDir
|
|
1481
1514
|
? (sel.inventory.total > 0
|
|
1482
1515
|
? t.emptyNoUsable(sel.inventory.total)
|
|
1483
1516
|
: t.emptyNoProjects)
|
|
1484
1517
|
: t.emptyNoInstall)
|
|
1485
1518
|
: filteredList.length === 0
|
|
1486
|
-
? React.createElement("span", { className: "webg-hint" }, t.noMatch(q))
|
|
1519
|
+
? React.createElement("span", { className: "webg-hint webg-grid-empty" }, t.noMatch(q))
|
|
1487
1520
|
: filteredList.map((w) => ThumbCard(w, w.id === sel.id, () => applySelection(w.id), t)),
|
|
1488
1521
|
);
|
|
1489
1522
|
}
|
|
@@ -1563,7 +1596,9 @@ window.__ModuleLoader__.load({
|
|
|
1563
1596
|
React.createElement("input", {
|
|
1564
1597
|
className: "webg-text", type: "text",
|
|
1565
1598
|
value: editing.name,
|
|
1566
|
-
|
|
1599
|
+
onChange: (e) => { editing.name = e.target.value; if (!e.nativeEvent || !e.nativeEvent.isComposing) emit(); },
|
|
1600
|
+
onInput: (e) => { editing.name = e.target.value; },
|
|
1601
|
+
onCompositionEnd: (e) => { editing.name = e.target.value; emit(); },
|
|
1567
1602
|
}),
|
|
1568
1603
|
),
|
|
1569
1604
|
React.createElement("div", { className: "webg-row" },
|
|
@@ -1588,6 +1623,7 @@ window.__ModuleLoader__.load({
|
|
|
1588
1623
|
className: "webg-text", type: "search",
|
|
1589
1624
|
placeholder: t.filterPlaceholder,
|
|
1590
1625
|
value: editorQuery,
|
|
1626
|
+
onChange: (e) => setEditorQuery(e.target.value),
|
|
1591
1627
|
onInput: (e) => setEditorQuery(e.target.value),
|
|
1592
1628
|
}),
|
|
1593
1629
|
React.createElement("div", { className: "webg-editor-grid" },
|
|
@@ -1595,10 +1631,10 @@ window.__ModuleLoader__.load({
|
|
|
1595
1631
|
const eq = editorQuery.trim();
|
|
1596
1632
|
const pool = usableList.filter((w) => matchesQuery(w, eq));
|
|
1597
1633
|
if (usableList.length === 0) {
|
|
1598
|
-
return React.createElement("span", { className: "webg-hint" }, t.noUsable);
|
|
1634
|
+
return React.createElement("span", { className: "webg-hint webg-grid-empty" }, t.noUsable);
|
|
1599
1635
|
}
|
|
1600
1636
|
if (pool.length === 0) {
|
|
1601
|
-
return React.createElement("span", { className: "webg-hint" }, t.noMatch(eq));
|
|
1637
|
+
return React.createElement("span", { className: "webg-hint webg-grid-empty" }, t.noMatch(eq));
|
|
1602
1638
|
}
|
|
1603
1639
|
return pool.map((w) => EditorCard(w, editing, t));
|
|
1604
1640
|
})(),
|
|
@@ -1685,7 +1721,7 @@ window.__ModuleLoader__.load({
|
|
|
1685
1721
|
className: "webg-btn", type: "button", onClick: enterAdjust,
|
|
1686
1722
|
disabled: sel.adjusting,
|
|
1687
1723
|
}, t.adjust),
|
|
1688
|
-
(sel.zoom !== 1 || sel.offsetX !== 0 || sel.offsetY !== 0) &&
|
|
1724
|
+
(sel.zoom !== 1 || sel.offsetX !== 0 || sel.offsetY !== 0 || sel.rotation !== 0) &&
|
|
1689
1725
|
React.createElement("button", {
|
|
1690
1726
|
className: "webg-btn", type: "button", onClick: resetCrop,
|
|
1691
1727
|
}, t.resetCrop),
|
|
@@ -1892,7 +1928,7 @@ window.__ModuleLoader__.load({
|
|
|
1892
1928
|
|
|
1893
1929
|
/* Light-scheme text ramp: grays were tuned against a near-white page and
|
|
1894
1930
|
lose contrast over busy wallpapers — push them darker while active. */
|
|
1895
|
-
body[${ACTIVE_ATTR}]:not([
|
|
1931
|
+
body[${ACTIVE_ATTR}]:not([${DARK_ATTR}]) {
|
|
1896
1932
|
--dsw-alias-label-primary: rgb(0, 0, 0);
|
|
1897
1933
|
--dsw-alias-label-primary-dimmed: rgb(10, 10, 12);
|
|
1898
1934
|
--dsw-alias-label-secondary: rgb(40, 42, 46);
|
|
@@ -1910,7 +1946,7 @@ window.__ModuleLoader__.load({
|
|
|
1910
1946
|
--dsw-specific-input-major: rgba(255, 255, 255, 0.15);
|
|
1911
1947
|
--dsw-specific-bubble: rgba(255, 255, 255, 0.12);
|
|
1912
1948
|
}
|
|
1913
|
-
body[
|
|
1949
|
+
body[${DARK_ATTR}][${ACTIVE_ATTR}] {
|
|
1914
1950
|
--dsw-specific-input-major: rgba(255, 255, 255, 0.06);
|
|
1915
1951
|
--dsw-specific-bubble: rgba(255, 255, 255, 0.05);
|
|
1916
1952
|
}
|
|
@@ -2070,6 +2106,11 @@ window.__ModuleLoader__.load({
|
|
|
2070
2106
|
display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2071
2107
|
gap: 8px; max-height: 300px; overflow-y: auto; padding: 2px;
|
|
2072
2108
|
}
|
|
2109
|
+
.webg-grid-empty {
|
|
2110
|
+
grid-column: 1 / -1;
|
|
2111
|
+
display: flex; align-items: center; justify-content: center;
|
|
2112
|
+
padding: 24px 12px; text-align: center;
|
|
2113
|
+
}
|
|
2073
2114
|
.webg-card {
|
|
2074
2115
|
position: relative; width: 100%; padding: 0; cursor: pointer;
|
|
2075
2116
|
aspect-ratio: 16 / 9; display: block; overflow: hidden;
|
|
@@ -2146,7 +2187,10 @@ window.__ModuleLoader__.load({
|
|
|
2146
2187
|
`;
|
|
2147
2188
|
|
|
2148
2189
|
// ── Plugin exports ──────────────────────────────────────────────────────────
|
|
2149
|
-
|
|
2190
|
+
// The client runner (dsh-cordis-client-runner) rejects ctx.get() of any
|
|
2191
|
+
// service this plugin does not declare. slots registers the settings row,
|
|
2192
|
+
// locale drives the bilingual UI, theme drives the veil/scheme reactions.
|
|
2193
|
+
const inject = ["slots", "locale", "theme"];
|
|
2150
2194
|
|
|
2151
2195
|
function apply(ctx) {
|
|
2152
2196
|
// A plugin UPDATE re-applies this module WITHOUT re-evaluating it: the old
|
|
@@ -2174,6 +2218,42 @@ window.__ModuleLoader__.load({
|
|
|
2174
2218
|
} catch { /* event bus unavailable → static language */ }
|
|
2175
2219
|
}
|
|
2176
2220
|
|
|
2221
|
+
// 0b. Theme: follow the shell's theme service (snapshot.active.colorScheme
|
|
2222
|
+
// is "light"|"dark"; "theme/change" emits the new snapshot). Without
|
|
2223
|
+
// the service, fall back to the OS prefers-color-scheme media query.
|
|
2224
|
+
// A flip re-runs applyEffects() so the veil colour (black ↔ white) and
|
|
2225
|
+
// the DARK_ATTR mirror follow immediately.
|
|
2226
|
+
const themeSvc = typeof ctx.get === "function" ? ctx.get("theme") : undefined;
|
|
2227
|
+
if (themeSvc && typeof themeSvc.getTheme === "function") {
|
|
2228
|
+
try {
|
|
2229
|
+
const snap = themeSvc.getTheme();
|
|
2230
|
+
themeDark = Boolean(snap && snap.active && snap.active.colorScheme === "dark");
|
|
2231
|
+
} catch { /* keep previous state */ }
|
|
2232
|
+
if (typeof ctx.on === "function") {
|
|
2233
|
+
try {
|
|
2234
|
+
ctx.on("theme/change", (snap) => {
|
|
2235
|
+
const next = Boolean(snap && snap.active && snap.active.colorScheme === "dark");
|
|
2236
|
+
if (next !== themeDark) { themeDark = next; applyEffects(); }
|
|
2237
|
+
});
|
|
2238
|
+
} catch { /* event bus unavailable → static theme */ }
|
|
2239
|
+
}
|
|
2240
|
+
} else if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
2241
|
+
try {
|
|
2242
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
2243
|
+
themeDark = Boolean(mq && mq.matches);
|
|
2244
|
+
if (typeof mq.addEventListener === "function") {
|
|
2245
|
+
const onScheme = (e) => {
|
|
2246
|
+
const next = Boolean(e && e.matches);
|
|
2247
|
+
if (next !== themeDark) { themeDark = next; applyEffects(); }
|
|
2248
|
+
};
|
|
2249
|
+
mq.addEventListener("change", onScheme);
|
|
2250
|
+
if (typeof ctx.effect === "function") {
|
|
2251
|
+
ctx.effect(() => () => mq.removeEventListener("change", onScheme));
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
} catch { /* no matchMedia → light default */ }
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2177
2257
|
// 1. Styles: owned by the fiber — injected on apply, REMOVED on dispose.
|
|
2178
2258
|
if (ctx.effect && typeof document !== "undefined") {
|
|
2179
2259
|
ctx.effect(() => {
|
|
@@ -2206,12 +2286,6 @@ window.__ModuleLoader__.load({
|
|
|
2206
2286
|
applyEffects();
|
|
2207
2287
|
}, 2500);
|
|
2208
2288
|
}
|
|
2209
|
-
// A theme switch flips the veil colour immediately (black ↔ white).
|
|
2210
|
-
let observer = null;
|
|
2211
|
-
if (typeof MutationObserver === "function") {
|
|
2212
|
-
observer = new MutationObserver(() => applyEffects());
|
|
2213
|
-
observer.observe(document.body, { attributes: true, attributeFilter: ["data-ds-dark-theme"] });
|
|
2214
|
-
}
|
|
2215
2289
|
return () => {
|
|
2216
2290
|
// Stop any in-flight inventory fetch from committing state (and
|
|
2217
2291
|
// re-arming the rotation timer) after teardown began.
|
|
@@ -2222,7 +2296,6 @@ window.__ModuleLoader__.load({
|
|
|
2222
2296
|
// removes the bar and its listeners) before tearing layers down.
|
|
2223
2297
|
exitAdjust();
|
|
2224
2298
|
if (samplerId !== null) window.clearInterval(samplerId);
|
|
2225
|
-
if (observer) observer.disconnect();
|
|
2226
2299
|
sampledLuminance = null;
|
|
2227
2300
|
if (previewProbe) { previewProbe.remove(); previewProbe = null; }
|
|
2228
2301
|
clearRotationTimer();
|
|
@@ -2241,6 +2314,7 @@ window.__ModuleLoader__.load({
|
|
|
2241
2314
|
if (scrim) scrim.remove();
|
|
2242
2315
|
clearEffects();
|
|
2243
2316
|
document.body.removeAttribute(ACTIVE_ATTR);
|
|
2317
|
+
document.body.removeAttribute(DARK_ATTR);
|
|
2244
2318
|
};
|
|
2245
2319
|
});
|
|
2246
2320
|
}
|
|
@@ -2323,7 +2397,7 @@ window.__ModuleLoader__.load({
|
|
|
2323
2397
|
if (ctx.slots) {
|
|
2324
2398
|
ctx.slots.inject("settings.general.item", () =>
|
|
2325
2399
|
ctx.slots.register(
|
|
2326
|
-
{ name: "settings.general.item", id: "we-background", order: 500, label: S().settingsLabel },
|
|
2400
|
+
{ name: "settings.general.item", id: "we-background", order: 500, label: () => S().settingsLabel },
|
|
2327
2401
|
() => React.createElement(WallpaperPicker),
|
|
2328
2402
|
),
|
|
2329
2403
|
);
|
package/lib/core.js
CHANGED
|
@@ -174,8 +174,10 @@ export function steamProbeDirs(platform = process.platform, home = homedir) {
|
|
|
174
174
|
return [
|
|
175
175
|
'C:\\Program Files (x86)\\Steam',
|
|
176
176
|
'C:\\Program Files\\Steam',
|
|
177
|
+
'C:\\SteamLibrary',
|
|
177
178
|
'D:\\Steam',
|
|
178
179
|
'D:\\SteamLibrary',
|
|
180
|
+
'E:\\Steam',
|
|
179
181
|
'E:\\SteamLibrary',
|
|
180
182
|
];
|
|
181
183
|
case 'darwin':
|
|
@@ -536,9 +538,13 @@ export function mimeFor(absPath) {
|
|
|
536
538
|
js: 'text/javascript; charset=utf-8', mjs: 'text/javascript; charset=utf-8',
|
|
537
539
|
css: 'text/css; charset=utf-8', json: 'application/json; charset=utf-8',
|
|
538
540
|
jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif',
|
|
539
|
-
png: 'image/png', webp: 'image/webp', svg: 'image/svg+xml',
|
|
541
|
+
png: 'image/png', webp: 'image/webp', svg: 'image/svg+xml', ico: 'image/x-icon',
|
|
540
542
|
mp3: 'audio/mpeg', ogg: 'audio/ogg', wav: 'audio/wav',
|
|
543
|
+
flac: 'audio/flac', aac: 'audio/aac', m4a: 'audio/mp4',
|
|
541
544
|
woff: 'font/woff', woff2: 'font/woff2', ttf: 'font/ttf',
|
|
545
|
+
otf: 'font/otf', eot: 'application/vnd.ms-fontobject',
|
|
546
|
+
wasm: 'application/wasm', txt: 'text/plain; charset=utf-8',
|
|
547
|
+
xml: 'application/xml; charset=utf-8',
|
|
542
548
|
}[ext] || 'application/octet-stream';
|
|
543
549
|
}
|
|
544
550
|
|
package/lib/index.js
CHANGED
|
@@ -265,7 +265,7 @@ export function createRouteRegistrar(webServer, opts = {}) {
|
|
|
265
265
|
// unguessable token remains the only capability — wildcard origin
|
|
266
266
|
// grants nothing beyond what a known URL already grants.
|
|
267
267
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
268
|
-
if (mime
|
|
268
|
+
if (mime.startsWith('text/html') || mime === 'image/svg+xml') {
|
|
269
269
|
// Script-capable documents (web wallpapers, SVG previews) get a
|
|
270
270
|
// sandbox CSP even on direct top-level navigation, so they can
|
|
271
271
|
// never execute against the DSH origin. `allow-scripts` keeps the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallpaper-engine-dsh",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
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",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"client": {
|
|
55
55
|
"inject": [
|
|
56
|
-
"@deepseek-ai/dsh-client-
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-settings-general"
|
|
57
57
|
],
|
|
58
58
|
"platform": "web",
|
|
59
59
|
"immediately": true
|
|
@@ -71,23 +71,28 @@
|
|
|
71
71
|
"release:major": "npm version major -m \"chore: release v%s\" && git push --follow-tags"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
74
|
+
"dsh": ">=0.2.1-rc.5",
|
|
74
75
|
"node": ">=18.17"
|
|
75
76
|
},
|
|
76
77
|
"peerDependencies": {
|
|
77
78
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
78
|
-
"@deepseek-ai/dsh-client-
|
|
79
|
-
"@deepseek-ai/dsh-client-ui-
|
|
80
|
-
"@deepseek-ai/dsh-
|
|
79
|
+
"@deepseek-ai/dsh-client-locale": ">=0.2.1-rc.5",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-settings-general": ">=0.2.1-rc.5",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-theme": ">=0.2.1-rc.5",
|
|
82
|
+
"@deepseek-ai/dsh-host-webserver": ">=0.2.1-rc.5",
|
|
81
83
|
"react": "^18.2.0"
|
|
82
84
|
},
|
|
83
85
|
"peerDependenciesMeta": {
|
|
84
86
|
"@deepseek-ai/cordis": {
|
|
85
87
|
"optional": true
|
|
86
88
|
},
|
|
87
|
-
"@deepseek-ai/dsh-client-
|
|
89
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
88
90
|
"optional": true
|
|
89
91
|
},
|
|
90
|
-
"@deepseek-ai/dsh-client-ui-
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-settings-general": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-theme": {
|
|
91
96
|
"optional": true
|
|
92
97
|
},
|
|
93
98
|
"@deepseek-ai/dsh-host-webserver": {
|