wallpaper-engine-dsh 1.0.8 → 2.1.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 +10 -2
- package/README.zh.md +10 -2
- package/lib/client.js +162 -20
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -8,11 +8,11 @@ A DSH bundle that turns your local **Wallpaper Engine** library into the live ba
|
|
|
8
8
|
|
|
9
9
|
**The library, rendered live.** The grid lists every wallpaper the browser can genuinely render — video and web wallpapers. Scene and application wallpapers are Wallpaper Engine's private `.pkg` scene packages; since no browser can execute them, they are filtered out rather than offered as degraded stills. Everything listed plays for real, badged Video or Web, and a search box narrows large libraries by title or workshop id instantly.
|
|
10
10
|
|
|
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 (web wallpapers' pixels are unreadable inside the sandbox, so their preview still stands in as the sample)
|
|
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 (web wallpapers' pixels are unreadable inside the sandbox, so their preview still stands in as the sample). While a wallpaper is active, dropdown menus, the slash-command popup, the queue-dock panel, the settings dialog, and the image lightbox all render as frosted glass: the shell's own menu primitive carries an isolation property that silently kills its internal blur layer, so the plugin applies the blur on the surface element itself and deepens the menu fill, keeping overlay text readable over any wallpaper. Everything reverts to the host look when the wallpaper is off.
|
|
12
12
|
|
|
13
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
|
-
**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.
|
|
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. The Glass slider's colour melt is anchored to the shell's own menu material: at the 40px ceiling it reproduces the host recipe blur(40px) saturate(150%) exactly, so full glass reads as one system with the shell's native frost.
|
|
16
16
|
|
|
17
17
|
**Rotation lists.** Build any number of carousel lists, each with its own wallpapers, interval (1–120 min), and order (sequence or random). Auto-rotate arms once the active list holds at least two usable wallpapers; the status row always reports whether rotation is actually running. Your first playable Wallpaper Engine playlist is imported automatically on first run; any other playlist can be imported into any list.
|
|
18
18
|
|
|
@@ -48,6 +48,14 @@ Restart `dsh web`, then open **Settings → General → Wallpaper Engine**.
|
|
|
48
48
|
|
|
49
49
|
Your selection and every knob persist in the browser's `localStorage`.
|
|
50
50
|
|
|
51
|
+
## Desktop
|
|
52
|
+
|
|
53
|
+
The desktop shell and the browser share the same frontend and the same injection pipeline; the `platform: "web"` declaration works as-is, with no separate desktop package.
|
|
54
|
+
|
|
55
|
+
- **Windows**: the main window is opaque and the wallpaper renders at the DOM layer. The window-controls strip at the top is an opaque host-owned overlay; the plugin blends the wallpaper into it with a short fade in the overlay's own colour, and the fade switches off in fullscreen.
|
|
56
|
+
- **macOS**: the shell sidebar is the system's native vibrant material, so what shows behind the wallpaper there is the system material rather than the wallpaper itself — host behaviour.
|
|
57
|
+
- **With system transparency effects off** (`prefers-reduced-transparency`), all glass and blur falls back to solid surfaces; the wallpaper stays, and legibility is carried by the veil and the solids.
|
|
58
|
+
|
|
51
59
|
## How it works
|
|
52
60
|
|
|
53
61
|
The bundle has two halves:
|
package/README.zh.md
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
**只列出能真实播放的壁纸。** 网格中列出浏览器可以真实渲染的全部类型——视频与网页壁纸。场景/应用壁纸是 Wallpaper Engine 私有的 `.pkg` 场景包,浏览器无法执行,因此直接过滤,不以降级的静态图代替。列出的每张壁纸都真实播放,并按 视频/网页 标注类型;搜索框可按标题或创意工坊 ID 筛选较大的壁纸库。
|
|
10
10
|
|
|
11
|
-
**任何壁纸上文字都可读。** 遮罩颜色始终与文字颜色相反:深色主题用黑色遮罩,浅色主题用白色遮罩,因此过亮或过暗的壁纸都不会盖住文字。在此基础上,「智能可视」每隔几秒采样一次壁纸亮度,亮度过高或过低时自动加强遮罩(网页壁纸的像素在沙箱中读不到,改用其预览图作为采样依据)
|
|
11
|
+
**任何壁纸上文字都可读。** 遮罩颜色始终与文字颜色相反:深色主题用黑色遮罩,浅色主题用白色遮罩,因此过亮或过暗的壁纸都不会盖住文字。在此基础上,「智能可视」每隔几秒采样一次壁纸亮度,亮度过高或过低时自动加强遮罩(网页壁纸的像素在沙箱中读不到,改用其预览图作为采样依据)。壁纸激活时,下拉菜单、斜杠命令提示框、队列面板、设置弹窗与图片灯箱统一为磨砂玻璃:外壳的菜单原语自身带隔离属性,其内部模糊层在宿主原样下并不生效,插件改为在 surface 元素上施加模糊,并把菜单底色加深,保证浮层文字在任意壁纸上可读;关闭壁纸后恢复宿主原样。
|
|
12
12
|
|
|
13
13
|
**手动取景。** 先选基础排布(填充、适应、拉伸、原始、自由),再手动调整画面:点「调整画面」将壁纸全屏展开,滚轮缩放,拖动平移。前四种排布下裁剪范围经过限制,画面始终铺满屏幕,不露出边缘;「自由」把壁纸按原始大小放在黑色画布上,可拖动到任意位置,缩放范围 0.1–4×,并可旋转任意角度(±90° 按钮或 Alt+拖动),同时始终保留一小块在屏幕内,不会拖丢。「镜像」水平翻转壁纸,所有排布下都可用——翻转不会破坏铺满。按 Esc 或点「完成」退出,取景设置会保存,「重置裁剪」可一键恢复默认。网页壁纸使用相同的排布下拉与缩放/平移/旋转取景(变换直接作用于 iframe)。
|
|
14
14
|
|
|
15
|
-
**四个实时滑杆。**
|
|
15
|
+
**四个实时滑杆。** 壁纸模糊、暗化、边框、玻璃,每一步调节立即生效并保存。模糊在原位渲染:任意模糊度下,画面的比例、排布与取景完全一致;屏幕边缘一个模糊半径内的画面向遮罩过渡,被界面边饰覆盖,通常不可见。模糊时叠加轻微噪声,消除渐变色带;模糊为零时媒体完全绕过滤镜管线,像素保持不变。玻璃滑杆的「色彩熔融」在 40px 满档时正好等于外壳官方菜单材质的配方(blur(40px) saturate(150%)),满档玻璃与外壳原生磨砂观感一致。
|
|
16
16
|
|
|
17
17
|
**轮播列表。** 可以创建任意多个列表,各自指定壁纸集合、切换间隔(1–120 分钟)与顺序(顺序/随机)。当前列表至少有两张可用壁纸时才会自动轮转;状态行始终显示轮转是否正在运行。首次运行会自动导入第一个可播放的 Wallpaper Engine 播放列表,其他播放列表也可随时导入到任意列表。
|
|
18
18
|
|
|
@@ -48,6 +48,14 @@ dsh plugin --profile web add link:<克隆文件夹的绝对路径>
|
|
|
48
48
|
|
|
49
49
|
你的选择与每一项调节都持久化在浏览器的 `localStorage` 中。
|
|
50
50
|
|
|
51
|
+
## 桌面端
|
|
52
|
+
|
|
53
|
+
桌面端与浏览器共用同一前端和同一注入管道,`platform: "web"` 声明即可生效,无需单独的桌面包。
|
|
54
|
+
|
|
55
|
+
- **Windows**:主窗口不透明,壁纸渲染在 DOM 层。顶部窗口控制按钮条是宿主维护的实色 overlay,插件用一条与 overlay 同色的短渐变把壁纸衔接过去,弱化接缝;进入全屏时自动关闭。
|
|
56
|
+
- **macOS**:外壳侧栏为系统原生 vibrant 材质,壁纸在其后播放时透出的是系统材质而非壁纸本身,为外壳既定行为。
|
|
57
|
+
- **系统关闭透明效果**(`prefers-reduced-transparency`)时,玻璃与模糊全部退为实色表面,壁纸保留,可读性由遮罩与实色表面保证。
|
|
58
|
+
|
|
51
59
|
## 工作原理
|
|
52
60
|
|
|
53
61
|
bundle 分为两半:
|
package/lib/client.js
CHANGED
|
@@ -10,6 +10,12 @@ 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 own dark-mode body
|
|
14
|
+
// attribute still exists in 0.2.1-rc.5 (its writer moved to the layout
|
|
15
|
+
// theme presenter), but the stylesheet never keys off host DOM internals:
|
|
16
|
+
// the client derives dark from the shell's theme service and mirrors it
|
|
17
|
+
// here, so a host-side restructure can never silently break the glass.
|
|
18
|
+
const DARK_ATTR = "data-webg-dark";
|
|
13
19
|
const LAYER_ID = "wallpaper-engine-dsh-layer";
|
|
14
20
|
const SCRIM_ID = "wallpaper-engine-dsh-scrim";
|
|
15
21
|
const STYLE_TAG_ID = "wallpaper-engine-dsh/styles";
|
|
@@ -1093,6 +1099,9 @@ window.__ModuleLoader__.load({
|
|
|
1093
1099
|
// tick would rewrite all CSS variables AND force a synchronous layout for
|
|
1094
1100
|
// nothing. clearEffects() resets it so the next apply always writes.
|
|
1095
1101
|
let lastEffectKey = null;
|
|
1102
|
+
// Resolved dark-theme state, driven by the shell theme service (or the OS
|
|
1103
|
+
// prefers-color-scheme fallback). Mirrored onto body[DARK_ATTR] for CSS.
|
|
1104
|
+
let themeDark = false;
|
|
1096
1105
|
|
|
1097
1106
|
/** Average Rec.601 luminance (0..1) of a drawable media frame, or null. */
|
|
1098
1107
|
function luminanceOf(media) {
|
|
@@ -1163,7 +1172,16 @@ window.__ModuleLoader__.load({
|
|
|
1163
1172
|
// The veil OPPOSES the text colour: dark theme (light text) darkens bright
|
|
1164
1173
|
// wallpapers, light theme (dark text) brightens dark ones — either way the
|
|
1165
1174
|
// text side wins. This is what makes ANY wallpaper survivable.
|
|
1166
|
-
const dark =
|
|
1175
|
+
const dark = themeDark;
|
|
1176
|
+
if (typeof document !== "undefined" && document.body) {
|
|
1177
|
+
if (dark) {
|
|
1178
|
+
if (!(document.body.hasAttribute && document.body.hasAttribute(DARK_ATTR))) {
|
|
1179
|
+
document.body.setAttribute(DARK_ATTR, "");
|
|
1180
|
+
}
|
|
1181
|
+
} else if (document.body.hasAttribute && document.body.hasAttribute(DARK_ATTR)) {
|
|
1182
|
+
document.body.removeAttribute(DARK_ATTR);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1167
1185
|
const roundedLum = sampledLuminance !== null ? Math.round(sampledLuminance * 100) / 100 : null;
|
|
1168
1186
|
const key = [
|
|
1169
1187
|
dark, selection.scrim, selection.border, selection.blur, selection.wallpaperBlur,
|
|
@@ -1186,9 +1204,22 @@ window.__ModuleLoader__.load({
|
|
|
1186
1204
|
s.setProperty("--webg-scrim-color", veilColor);
|
|
1187
1205
|
s.setProperty("--webg-border-alpha", String(selection.border));
|
|
1188
1206
|
s.setProperty("--webg-blur", selection.blur + "px");
|
|
1189
|
-
// The glass "colour melt"
|
|
1190
|
-
s
|
|
1207
|
+
// The glass "colour melt" anchors at the shell's own menu material: the
|
|
1208
|
+
// slider's 40px ceiling reproduces the host recipe blur(40px) saturate(150%)
|
|
1209
|
+
// exactly, so full glass and the host's frosted chrome read as one system.
|
|
1210
|
+
s.setProperty("--webg-saturate", String(1 + selection.blur * 0.0125));
|
|
1191
1211
|
s.setProperty("--webg-glass-brightness", "1.04");
|
|
1212
|
+
// Modal masks (settings dialog, image lightbox) read the shell's mask-blur
|
|
1213
|
+
// token — wire it to the glass slider so modals frost the app behind them
|
|
1214
|
+
// instead of showing the wallpaper sharply through a translucent mask.
|
|
1215
|
+
// Capped: a 40px blur on a full-viewport backdrop costs more GPU than the
|
|
1216
|
+
// effect is worth.
|
|
1217
|
+
// Gated on an active selection: this is a HOST token, so masks must not
|
|
1218
|
+
// blur the app while no wallpaper runs. At 0 blur: none - no layer, no cost.
|
|
1219
|
+
s.setProperty("--dsw-mask-blur",
|
|
1220
|
+
selection.id && selection.blur > 0
|
|
1221
|
+
? "blur(" + Math.min(selection.blur, 20) + "px)"
|
|
1222
|
+
: "none");
|
|
1192
1223
|
// At 0 blur, keep the media OUT of the filter pipeline entirely: even
|
|
1193
1224
|
// blur(0px) forces an extra raster/compositing pass, and the noise overlay
|
|
1194
1225
|
// would still occupy a layer at opacity 0. filter:none + display:none keep
|
|
@@ -1226,6 +1257,7 @@ window.__ModuleLoader__.load({
|
|
|
1226
1257
|
s.removeProperty("--webg-scrim-color");
|
|
1227
1258
|
s.removeProperty("--webg-border-alpha");
|
|
1228
1259
|
s.removeProperty("--webg-blur");
|
|
1260
|
+
s.removeProperty("--dsw-mask-blur"); // restore the host's own mask token
|
|
1229
1261
|
s.removeProperty("--webg-saturate");
|
|
1230
1262
|
s.removeProperty("--webg-glass-brightness");
|
|
1231
1263
|
s.removeProperty("--webg-media-filter");
|
|
@@ -1911,7 +1943,7 @@ window.__ModuleLoader__.load({
|
|
|
1911
1943
|
|
|
1912
1944
|
/* Light-scheme text ramp: grays were tuned against a near-white page and
|
|
1913
1945
|
lose contrast over busy wallpapers — push them darker while active. */
|
|
1914
|
-
body[${ACTIVE_ATTR}]:not([
|
|
1946
|
+
body[${ACTIVE_ATTR}]:not([${DARK_ATTR}]) {
|
|
1915
1947
|
--dsw-alias-label-primary: rgb(0, 0, 0);
|
|
1916
1948
|
--dsw-alias-label-primary-dimmed: rgb(10, 10, 12);
|
|
1917
1949
|
--dsw-alias-label-secondary: rgb(40, 42, 46);
|
|
@@ -1929,20 +1961,97 @@ window.__ModuleLoader__.load({
|
|
|
1929
1961
|
--dsw-specific-input-major: rgba(255, 255, 255, 0.15);
|
|
1930
1962
|
--dsw-specific-bubble: rgba(255, 255, 255, 0.12);
|
|
1931
1963
|
}
|
|
1932
|
-
body[
|
|
1964
|
+
body[${DARK_ATTR}][${ACTIVE_ATTR}] {
|
|
1933
1965
|
--dsw-specific-input-major: rgba(255, 255, 255, 0.06);
|
|
1934
1966
|
--dsw-specific-bubble: rgba(255, 255, 255, 0.05);
|
|
1935
1967
|
}
|
|
1936
1968
|
body[${ACTIVE_ATTR}] [data-composer-card],
|
|
1937
1969
|
body[${ACTIVE_ATTR}] [class*="_bubble"] {
|
|
1938
|
-
background-image: linear-gradient(
|
|
1939
|
-
-webkit-backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.
|
|
1940
|
-
backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.
|
|
1970
|
+
background-image: linear-gradient(165deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.06) 32%, rgba(255, 255, 255, 0.02));
|
|
1971
|
+
-webkit-backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.2)) brightness(var(--webg-glass-brightness, 1.04)) contrast(1.01);
|
|
1972
|
+
backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.2)) brightness(var(--webg-glass-brightness, 1.04)) contrast(1.01);
|
|
1941
1973
|
box-shadow:
|
|
1942
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.
|
|
1943
|
-
inset 0 -1px 0 rgba(255, 255, 255, 0.
|
|
1944
|
-
inset 0 0 0 0.5px rgba(255, 255, 255, 0.
|
|
1945
|
-
0 12px 40px rgba(0, 0, 0, 0.
|
|
1974
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.36),
|
|
1975
|
+
inset 0 -1px 0 rgba(255, 255, 255, 0.10),
|
|
1976
|
+
inset 0 0 0 0.5px rgba(255, 255, 255, 0.10),
|
|
1977
|
+
0 12px 40px rgba(0, 0, 0, 0.14);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
/* Menu + queue-dock surface repair (wallpaper active). The shell's
|
|
1981
|
+
menu primitive parks fill and blur on a child layer while the
|
|
1982
|
+
surface element itself is isolated - an isolated ancestor forms
|
|
1983
|
+
a backdrop root, so the blur only samples the panel interior.
|
|
1984
|
+
Same on the queue dock panel (:before carries the blur). With a
|
|
1985
|
+
wallpaper active, menus showed sharp content through a 45% fill.
|
|
1986
|
+
An element's OWN backdrop-filter ignores its own isolation, so
|
|
1987
|
+
the filter moves onto the surface element; fill deepened 72%. */
|
|
1988
|
+
body[${ACTIVE_ATTR}] { --dsw-menu-surface-fill: rgba(67, 69, 74, 0.72); }
|
|
1989
|
+
body[${ACTIVE_ATTR}]:not([${DARK_ATTR}]) { --dsw-menu-surface-fill: rgba(248, 249, 250, 0.72); }
|
|
1990
|
+
body[${ACTIVE_ATTR}] [data-menu-material],
|
|
1991
|
+
body[${ACTIVE_ATTR}] [data-composer-dock] [class*="_panel"] {
|
|
1992
|
+
-webkit-backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.2)) brightness(var(--webg-glass-brightness, 1.04)) contrast(1.01);
|
|
1993
|
+
backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.2)) brightness(var(--webg-glass-brightness, 1.04)) contrast(1.01);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
/* Settings modal and sibling dialogs: the shell paints dialogs on
|
|
1997
|
+
an opaque layer-2 panel. While a wallpaper is active, dialogs
|
|
1998
|
+
join the glass system - the mask behind them already frosts the
|
|
1999
|
+
app (the mask blur token is wired to the glass slider in
|
|
2000
|
+
applyEffects). color-mix keeps the host hue at 80% opacity.
|
|
2001
|
+
[class*="_dialog_"] matches only the module-suffix panel class;
|
|
2002
|
+
compounds like _dialogHeader lack the underscore segment. */
|
|
2003
|
+
body[${ACTIVE_ATTR}] [class*="_dialog_"] {
|
|
2004
|
+
background-color: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 80%, transparent);
|
|
2005
|
+
-webkit-backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.2)) brightness(var(--webg-glass-brightness, 1.04)) contrast(1.01);
|
|
2006
|
+
backdrop-filter: blur(var(--webg-blur, 16px)) saturate(var(--webg-saturate, 1.2)) brightness(var(--webg-glass-brightness, 1.04)) contrast(1.01);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
/* Windows desktop: the window-controls strip is an opaque host
|
|
2010
|
+
overlay (titleBarOverlay colour, fed from the renderer-measured
|
|
2011
|
+
palette). Blend the wallpaper up into it with a short fade from
|
|
2012
|
+
the overlay's own colour (dark #1b1b1c / light #f9fafb), scoped
|
|
2013
|
+
to the desktop marker html[data-windows-titlebar] (absent in
|
|
2014
|
+
browsers) and off in fullscreen. Pure CSS: the fill var flips
|
|
2015
|
+
with the plugin dark mirror attribute, no JS involved. */
|
|
2016
|
+
body[${DARK_ATTR}] { --webg-titlebar-fill: #1b1b1c; }
|
|
2017
|
+
body:not([${DARK_ATTR}]) { --webg-titlebar-fill: #f9fafb; }
|
|
2018
|
+
html[data-windows-titlebar]:not([data-fullscreen]) body[${ACTIVE_ATTR}] .webg-scrim::before {
|
|
2019
|
+
content: ""; position: absolute; top: 0; left: 0; right: 0;
|
|
2020
|
+
height: var(--dsh-windows-titlebar-height, 40px);
|
|
2021
|
+
background: linear-gradient(var(--webg-titlebar-fill, #1b1b1c), transparent);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/* Accessibility: prefers-reduced-transparency (Windows transparency
|
|
2025
|
+
effects off, macOS Reduce transparency). Glass off, surfaces back
|
|
2026
|
+
to solid fills; the wallpaper stays, legibility carried by the
|
|
2027
|
+
veil and the solids. Selectors mirror the glass rules above so
|
|
2028
|
+
this block wins by document order. */
|
|
2029
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
2030
|
+
body[${ACTIVE_ATTR}] {
|
|
2031
|
+
--dsw-specific-input-major: #26272c;
|
|
2032
|
+
--dsw-specific-bubble: #26272c;
|
|
2033
|
+
--dsw-menu-surface-fill: #43454a;
|
|
2034
|
+
--dsw-mask-blur: none !important;
|
|
2035
|
+
}
|
|
2036
|
+
body[${ACTIVE_ATTR}]:not([${DARK_ATTR}]) {
|
|
2037
|
+
--dsw-specific-input-major: #f8f9fa;
|
|
2038
|
+
--dsw-specific-bubble: #f4f5f6;
|
|
2039
|
+
--dsw-menu-surface-fill: #f8f9fa;
|
|
2040
|
+
--dsw-mask-blur: none !important;
|
|
2041
|
+
}
|
|
2042
|
+
body[${ACTIVE_ATTR}] [data-composer-card],
|
|
2043
|
+
body[${ACTIVE_ATTR}] [class*="_bubble"],
|
|
2044
|
+
body[${ACTIVE_ATTR}] [data-menu-material],
|
|
2045
|
+
body[${ACTIVE_ATTR}] [data-composer-dock] [class*="_panel"],
|
|
2046
|
+
body[${ACTIVE_ATTR}] [class*="_dialog_"] {
|
|
2047
|
+
-webkit-backdrop-filter: none;
|
|
2048
|
+
backdrop-filter: none;
|
|
2049
|
+
background-image: none;
|
|
2050
|
+
box-shadow: var(--dsw-elevation-soft, 0 3px 8px rgba(0, 0, 0, 0.08));
|
|
2051
|
+
}
|
|
2052
|
+
body[${ACTIVE_ATTR}] [class*="_dialog_"] {
|
|
2053
|
+
background-color: var(--dsw-alias-bg-layer-2);
|
|
2054
|
+
}
|
|
1946
2055
|
}
|
|
1947
2056
|
|
|
1948
2057
|
/* ── Picker chrome (token-driven, theme-aware) ── */
|
|
@@ -2170,7 +2279,10 @@ window.__ModuleLoader__.load({
|
|
|
2170
2279
|
`;
|
|
2171
2280
|
|
|
2172
2281
|
// ── Plugin exports ──────────────────────────────────────────────────────────
|
|
2173
|
-
|
|
2282
|
+
// The client runner (dsh-cordis-client-runner) rejects ctx.get() of any
|
|
2283
|
+
// service this plugin does not declare. slots registers the settings row,
|
|
2284
|
+
// locale drives the bilingual UI, theme drives the veil/scheme reactions.
|
|
2285
|
+
const inject = ["slots", "locale", "theme"];
|
|
2174
2286
|
|
|
2175
2287
|
function apply(ctx) {
|
|
2176
2288
|
// A plugin UPDATE re-applies this module WITHOUT re-evaluating it: the old
|
|
@@ -2198,6 +2310,42 @@ window.__ModuleLoader__.load({
|
|
|
2198
2310
|
} catch { /* event bus unavailable → static language */ }
|
|
2199
2311
|
}
|
|
2200
2312
|
|
|
2313
|
+
// 0b. Theme: follow the shell's theme service (snapshot.active.colorScheme
|
|
2314
|
+
// is "light"|"dark"; "theme/change" emits the new snapshot). Without
|
|
2315
|
+
// the service, fall back to the OS prefers-color-scheme media query.
|
|
2316
|
+
// A flip re-runs applyEffects() so the veil colour (black ↔ white) and
|
|
2317
|
+
// the DARK_ATTR mirror follow immediately.
|
|
2318
|
+
const themeSvc = typeof ctx.get === "function" ? ctx.get("theme") : undefined;
|
|
2319
|
+
if (themeSvc && typeof themeSvc.getTheme === "function") {
|
|
2320
|
+
try {
|
|
2321
|
+
const snap = themeSvc.getTheme();
|
|
2322
|
+
themeDark = Boolean(snap && snap.active && snap.active.colorScheme === "dark");
|
|
2323
|
+
} catch { /* keep previous state */ }
|
|
2324
|
+
if (typeof ctx.on === "function") {
|
|
2325
|
+
try {
|
|
2326
|
+
ctx.on("theme/change", (snap) => {
|
|
2327
|
+
const next = Boolean(snap && snap.active && snap.active.colorScheme === "dark");
|
|
2328
|
+
if (next !== themeDark) { themeDark = next; applyEffects(); }
|
|
2329
|
+
});
|
|
2330
|
+
} catch { /* event bus unavailable → static theme */ }
|
|
2331
|
+
}
|
|
2332
|
+
} else if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
2333
|
+
try {
|
|
2334
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
2335
|
+
themeDark = Boolean(mq && mq.matches);
|
|
2336
|
+
if (typeof mq.addEventListener === "function") {
|
|
2337
|
+
const onScheme = (e) => {
|
|
2338
|
+
const next = Boolean(e && e.matches);
|
|
2339
|
+
if (next !== themeDark) { themeDark = next; applyEffects(); }
|
|
2340
|
+
};
|
|
2341
|
+
mq.addEventListener("change", onScheme);
|
|
2342
|
+
if (typeof ctx.effect === "function") {
|
|
2343
|
+
ctx.effect(() => () => mq.removeEventListener("change", onScheme));
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
} catch { /* no matchMedia → light default */ }
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2201
2349
|
// 1. Styles: owned by the fiber — injected on apply, REMOVED on dispose.
|
|
2202
2350
|
if (ctx.effect && typeof document !== "undefined") {
|
|
2203
2351
|
ctx.effect(() => {
|
|
@@ -2230,12 +2378,6 @@ window.__ModuleLoader__.load({
|
|
|
2230
2378
|
applyEffects();
|
|
2231
2379
|
}, 2500);
|
|
2232
2380
|
}
|
|
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
2381
|
return () => {
|
|
2240
2382
|
// Stop any in-flight inventory fetch from committing state (and
|
|
2241
2383
|
// re-arming the rotation timer) after teardown began.
|
|
@@ -2246,7 +2388,6 @@ window.__ModuleLoader__.load({
|
|
|
2246
2388
|
// removes the bar and its listeners) before tearing layers down.
|
|
2247
2389
|
exitAdjust();
|
|
2248
2390
|
if (samplerId !== null) window.clearInterval(samplerId);
|
|
2249
|
-
if (observer) observer.disconnect();
|
|
2250
2391
|
sampledLuminance = null;
|
|
2251
2392
|
if (previewProbe) { previewProbe.remove(); previewProbe = null; }
|
|
2252
2393
|
clearRotationTimer();
|
|
@@ -2265,6 +2406,7 @@ window.__ModuleLoader__.load({
|
|
|
2265
2406
|
if (scrim) scrim.remove();
|
|
2266
2407
|
clearEffects();
|
|
2267
2408
|
document.body.removeAttribute(ACTIVE_ATTR);
|
|
2409
|
+
document.body.removeAttribute(DARK_ATTR);
|
|
2268
2410
|
};
|
|
2269
2411
|
});
|
|
2270
2412
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallpaper-engine-dsh",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.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": {
|