wallpaper-engine-dsh 1.0.8 → 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.
Files changed (2) hide show
  1. package/lib/client.js +61 -11
  2. package/package.json +12 -7
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";
@@ -1093,6 +1098,9 @@ window.__ModuleLoader__.load({
1093
1098
  // tick would rewrite all CSS variables AND force a synchronous layout for
1094
1099
  // nothing. clearEffects() resets it so the next apply always writes.
1095
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;
1096
1104
 
1097
1105
  /** Average Rec.601 luminance (0..1) of a drawable media frame, or null. */
1098
1106
  function luminanceOf(media) {
@@ -1163,7 +1171,16 @@ window.__ModuleLoader__.load({
1163
1171
  // The veil OPPOSES the text colour: dark theme (light text) darkens bright
1164
1172
  // wallpapers, light theme (dark text) brightens dark ones — either way the
1165
1173
  // text side wins. This is what makes ANY wallpaper survivable.
1166
- const dark = Boolean(document.body.hasAttribute && document.body.hasAttribute("data-ds-dark-theme"));
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
+ }
1167
1184
  const roundedLum = sampledLuminance !== null ? Math.round(sampledLuminance * 100) / 100 : null;
1168
1185
  const key = [
1169
1186
  dark, selection.scrim, selection.border, selection.blur, selection.wallpaperBlur,
@@ -1911,7 +1928,7 @@ window.__ModuleLoader__.load({
1911
1928
 
1912
1929
  /* Light-scheme text ramp: grays were tuned against a near-white page and
1913
1930
  lose contrast over busy wallpapers — push them darker while active. */
1914
- body[${ACTIVE_ATTR}]:not([data-ds-dark-theme]) {
1931
+ body[${ACTIVE_ATTR}]:not([${DARK_ATTR}]) {
1915
1932
  --dsw-alias-label-primary: rgb(0, 0, 0);
1916
1933
  --dsw-alias-label-primary-dimmed: rgb(10, 10, 12);
1917
1934
  --dsw-alias-label-secondary: rgb(40, 42, 46);
@@ -1929,7 +1946,7 @@ window.__ModuleLoader__.load({
1929
1946
  --dsw-specific-input-major: rgba(255, 255, 255, 0.15);
1930
1947
  --dsw-specific-bubble: rgba(255, 255, 255, 0.12);
1931
1948
  }
1932
- body[data-ds-dark-theme][${ACTIVE_ATTR}] {
1949
+ body[${DARK_ATTR}][${ACTIVE_ATTR}] {
1933
1950
  --dsw-specific-input-major: rgba(255, 255, 255, 0.06);
1934
1951
  --dsw-specific-bubble: rgba(255, 255, 255, 0.05);
1935
1952
  }
@@ -2170,7 +2187,10 @@ window.__ModuleLoader__.load({
2170
2187
  `;
2171
2188
 
2172
2189
  // ── Plugin exports ──────────────────────────────────────────────────────────
2173
- const inject = ["slots"];
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"];
2174
2194
 
2175
2195
  function apply(ctx) {
2176
2196
  // A plugin UPDATE re-applies this module WITHOUT re-evaluating it: the old
@@ -2198,6 +2218,42 @@ window.__ModuleLoader__.load({
2198
2218
  } catch { /* event bus unavailable → static language */ }
2199
2219
  }
2200
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
+
2201
2257
  // 1. Styles: owned by the fiber — injected on apply, REMOVED on dispose.
2202
2258
  if (ctx.effect && typeof document !== "undefined") {
2203
2259
  ctx.effect(() => {
@@ -2230,12 +2286,6 @@ window.__ModuleLoader__.load({
2230
2286
  applyEffects();
2231
2287
  }, 2500);
2232
2288
  }
2233
- // A theme switch flips the veil colour immediately (black ↔ white).
2234
- let observer = null;
2235
- if (typeof MutationObserver === "function") {
2236
- observer = new MutationObserver(() => applyEffects());
2237
- observer.observe(document.body, { attributes: true, attributeFilter: ["data-ds-dark-theme"] });
2238
- }
2239
2289
  return () => {
2240
2290
  // Stop any in-flight inventory fetch from committing state (and
2241
2291
  // re-arming the rotation timer) after teardown began.
@@ -2246,7 +2296,6 @@ window.__ModuleLoader__.load({
2246
2296
  // removes the bar and its listeners) before tearing layers down.
2247
2297
  exitAdjust();
2248
2298
  if (samplerId !== null) window.clearInterval(samplerId);
2249
- if (observer) observer.disconnect();
2250
2299
  sampledLuminance = null;
2251
2300
  if (previewProbe) { previewProbe.remove(); previewProbe = null; }
2252
2301
  clearRotationTimer();
@@ -2265,6 +2314,7 @@ window.__ModuleLoader__.load({
2265
2314
  if (scrim) scrim.remove();
2266
2315
  clearEffects();
2267
2316
  document.body.removeAttribute(ACTIVE_ATTR);
2317
+ document.body.removeAttribute(DARK_ATTR);
2268
2318
  };
2269
2319
  });
2270
2320
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallpaper-engine-dsh",
3
- "version": "1.0.8",
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-runtime"
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-runtime": ">=0.1.0-rc.6",
79
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.0-rc.6",
80
- "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.6",
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-runtime": {
89
+ "@deepseek-ai/dsh-client-locale": {
88
90
  "optional": true
89
91
  },
90
- "@deepseek-ai/dsh-client-ui-slots": {
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": {