wallpaper-engine-dsh 0.1.2 → 0.1.3

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 CHANGED
@@ -10,6 +10,7 @@ A DSH bundle that turns your local **Wallpaper Engine** library into the live ba
10
10
  - **Search** — filter the grid instantly by title or workshop id.
11
11
  - **Rotation lists** — any number of carousel lists, each with its own wallpapers, interval (1–120 min) and order (sequence/random). Your first playable Wallpaper Engine playlist is imported automatically; others can be imported into any list.
12
12
  - **Four live sliders** — wallpaper blur, scrim, border contrast, and glass frosting, all instant and persisted.
13
+ - **Canvas fit** — cover/contain/stretch/original sizing plus five-way alignment for video and still wallpapers.
13
14
  - **Resource monitor** — FPS readout with a low-framerate hint, plus auto-pause when the tab is hidden or the battery runs low (both optional).
14
15
  - **Bilingual** — the settings UI follows the shell's Language preference (Chinese/English), with a manual override in the picker.
15
16
  - **Crossfade** — wallpaper switches and clearing fade smoothly instead of snapping.
package/README.zh.md CHANGED
@@ -10,6 +10,7 @@
10
10
  - **搜索** —— 按标题或创意工坊 ID 即时筛选网格。
11
11
  - **轮播列表** —— 可建任意多个列表,各自拥有壁纸集合、切换间隔(1–120 分钟)和顺序(顺序/随机)。首次运行自动导入第一个可播放的 Wallpaper Engine 播放列表,其余列表可随时导入。
12
12
  - **四个实时滑杆** —— 壁纸模糊、暗化、边框对比、玻璃磨砂,即时生效并持久保存。
13
+ - **画布排布** —— 视频与静态壁纸支持填充/适应/拉伸/原始四种排布和五向对齐。
13
14
  - **资源监控** —— 帧率读数与低帧率提示,页面隐藏或电量不足时自动暂停(均可开关)。
14
15
  - **中英双语** —— 设置界面跟随 DSH 的"语言"偏好自动切换,也可在面板内手动指定。
15
16
  - **交叉淡入** —— 切换与关闭都平滑过渡,不生硬跳变。
package/lib/client.js CHANGED
@@ -83,6 +83,17 @@ window.__ModuleLoader__.load({
83
83
  lowFpsHint: "帧率偏低,可尝试暂停视频、增大壁纸模糊或换用静态壁纸",
84
84
  language: "语言",
85
85
  langAuto: "自动",
86
+ fit: "排布",
87
+ fitCover: "填充",
88
+ fitContain: "适应",
89
+ fitFill: "拉伸",
90
+ fitNone: "原始",
91
+ position: "对齐",
92
+ posCenter: "居中",
93
+ posTop: "顶部",
94
+ posBottom: "底部",
95
+ posLeft: "居左",
96
+ posRight: "居右",
86
97
  statusGroup: (name, total, usable, interval, order) =>
87
98
  "列表「" + name + "」:" + total + " 项 · " + usable + " 可用 · 每 " + interval + " 分钟 · " + order,
88
99
  statusUsable: (n) => n + " 张可用壁纸",
@@ -148,6 +159,17 @@ window.__ModuleLoader__.load({
148
159
  lowFpsHint: "Low frame rate — try pausing the video, raising wallpaper blur, or using a still wallpaper",
149
160
  language: "Language",
150
161
  langAuto: "Auto",
162
+ fit: "Fit",
163
+ fitCover: "Cover",
164
+ fitContain: "Contain",
165
+ fitFill: "Stretch",
166
+ fitNone: "Original",
167
+ position: "Align",
168
+ posCenter: "Center",
169
+ posTop: "Top",
170
+ posBottom: "Bottom",
171
+ posLeft: "Left",
172
+ posRight: "Right",
151
173
  statusGroup: (name, total, usable, interval, order) =>
152
174
  "List \"" + name + "\": " + total + " items · " + usable + " usable · every " + interval + " min · " + order,
153
175
  statusUsable: (n) => n + " usable wallpapers",
@@ -193,8 +215,13 @@ window.__ModuleLoader__.load({
193
215
  pauseOnHidden: true,
194
216
  pauseOnBattery: true,
195
217
  lang: "auto",
218
+ fit: "cover",
219
+ position: "center",
196
220
  };
197
221
 
222
+ const FIT_MODES = ["cover", "contain", "fill", "none"];
223
+ const POSITIONS = ["center", "top", "bottom", "left", "right"];
224
+
198
225
  // ── Persisted selection ─────────────────────────────────────────────────────
199
226
  function clampNum(v, lo, hi, fallback) {
200
227
  return typeof v === "number" && isFinite(v) && v >= lo && v <= hi ? v : fallback;
@@ -238,6 +265,8 @@ window.__ModuleLoader__.load({
238
265
  pauseOnHidden: o.pauseOnHidden !== false,
239
266
  pauseOnBattery: o.pauseOnBattery !== false,
240
267
  lang: o.lang === "zh" || o.lang === "en" ? o.lang : "auto",
268
+ fit: FIT_MODES.indexOf(o.fit) >= 0 ? o.fit : DEFAULTS.fit,
269
+ position: POSITIONS.indexOf(o.position) >= 0 ? o.position : DEFAULTS.position,
241
270
  };
242
271
  } catch {
243
272
  return { id: "", ...DEFAULTS };
@@ -287,6 +316,8 @@ window.__ModuleLoader__.load({
287
316
  pauseOnHidden: selection.pauseOnHidden,
288
317
  pauseOnBattery: selection.pauseOnBattery,
289
318
  lang: selection.lang,
319
+ fit: selection.fit,
320
+ position: selection.position,
290
321
  }));
291
322
  } catch { /* storage full / blocked → session-only state, not fatal */ }
292
323
  }
@@ -693,6 +724,10 @@ window.__ModuleLoader__.load({
693
724
  s.setProperty("--webg-wallpaper-blur", selection.wallpaperBlur + "px");
694
725
  // Scaling compensates the transparent fringe CSS blur reveals at edges.
695
726
  s.setProperty("--webg-wallpaper-scale", (1 + selection.wallpaperBlur * 0.006).toFixed(4));
727
+ // Canvas fit: object-fit/position for video and still wallpapers (iframes
728
+ // ignore object-fit — web wallpapers always fill their layer).
729
+ s.setProperty("--webg-fit", selection.fit);
730
+ s.setProperty("--webg-position", selection.position);
696
731
 
697
732
  // Write the scrim colour directly too, then force a synchronous layout, so
698
733
  // slider feedback lands on this frame even on stalled compositors.
@@ -712,6 +747,8 @@ window.__ModuleLoader__.load({
712
747
  s.removeProperty("--webg-glass-brightness");
713
748
  s.removeProperty("--webg-wallpaper-blur");
714
749
  s.removeProperty("--webg-wallpaper-scale");
750
+ s.removeProperty("--webg-fit");
751
+ s.removeProperty("--webg-position");
715
752
  const scrim = document.getElementById(SCRIM_ID);
716
753
  if (scrim) scrim.style.background = "";
717
754
  }
@@ -838,6 +875,18 @@ window.__ModuleLoader__.load({
838
875
  const onBorder = (pct) => { selection.border = pct / 100; persistSelection(); applyEffects(); emit(); };
839
876
  const onBlur = (px) => { selection.blur = px; persistSelection(); applyEffects(); emit(); };
840
877
  const onWallpaperBlur = (px) => { selection.wallpaperBlur = px; persistSelection(); applyEffects(); emit(); };
878
+ const onFit = (e) => {
879
+ selection.fit = FIT_MODES.indexOf(e.target.value) >= 0 ? e.target.value : "cover";
880
+ persistSelection();
881
+ applyEffects();
882
+ emit();
883
+ };
884
+ const onPosition = (e) => {
885
+ selection.position = POSITIONS.indexOf(e.target.value) >= 0 ? e.target.value : "center";
886
+ persistSelection();
887
+ applyEffects();
888
+ emit();
889
+ };
841
890
 
842
891
  if (!sel.loaded) {
843
892
  return React.createElement("div", { className: "webg-picker" },
@@ -1073,6 +1122,28 @@ window.__ModuleLoader__.load({
1073
1122
  ),
1074
1123
  // ── Effects ──
1075
1124
  sel.id && React.createElement(React.Fragment, null,
1125
+ // Canvas fit only matters for video/still wallpapers — an iframe
1126
+ // ignores object-fit and always fills its layer.
1127
+ sel.type !== "web" && React.createElement("div", { className: "webg-row webg-fit-row" },
1128
+ React.createElement("span", { className: "webg-hint webg-label" }, t.fit),
1129
+ React.createElement("select", {
1130
+ className: "webg-select webg-fit-select",
1131
+ value: sel.fit,
1132
+ onChange: onFit,
1133
+ },
1134
+ ...[t.fitCover, t.fitContain, t.fitFill, t.fitNone].map((label, i) =>
1135
+ React.createElement("option", { key: FIT_MODES[i], value: FIT_MODES[i] }, label),
1136
+ )),
1137
+ React.createElement("span", { className: "webg-hint webg-label" }, t.position),
1138
+ React.createElement("select", {
1139
+ className: "webg-select webg-position-select",
1140
+ value: sel.position,
1141
+ onChange: onPosition,
1142
+ },
1143
+ ...[t.posCenter, t.posTop, t.posBottom, t.posLeft, t.posRight].map((label, i) =>
1144
+ React.createElement("option", { key: POSITIONS[i], value: POSITIONS[i] }, label),
1145
+ )),
1146
+ ),
1076
1147
  SliderRow(t.wallpaperBlur, 0, 60, 1, sel.wallpaperBlur, onWallpaperBlur, sel.wallpaperBlur + "px"),
1077
1148
  SliderRow(t.scrim, 0, 90, 5, Math.round(sel.scrim * 100), onScrim, Math.round(sel.scrim * 100) + "%"),
1078
1149
  SliderRow(t.border, 0, 90, 5, Math.round(sel.border * 100), onBorder, Math.round(sel.border * 100) + "%"),
@@ -1128,7 +1199,9 @@ window.__ModuleLoader__.load({
1128
1199
  .webg-layer--enter { opacity: 0; }
1129
1200
  .webg-layer--leave { opacity: 0; }
1130
1201
  .webg-layer .webg-media {
1131
- width: 100%; height: 100%; object-fit: cover; display: block;
1202
+ width: 100%; height: 100%; object-fit: var(--webg-fit, cover);
1203
+ object-position: var(--webg-position, center);
1204
+ display: block;
1132
1205
  background: transparent; border: 0;
1133
1206
  filter: blur(var(--webg-wallpaper-blur, 0px));
1134
1207
  transform: scale(var(--webg-wallpaper-scale, 1));
@@ -1189,7 +1262,9 @@ window.__ModuleLoader__.load({
1189
1262
  }
1190
1263
 
1191
1264
  /* ── Picker chrome (token-driven, theme-aware) ── */
1192
- .webg-picker { display: flex; flex-direction: column; gap: 10px; }
1265
+ /* padding-top lifts the first row off the settings-section divider so the
1266
+ search/language row breathes; gap keeps every row evenly spaced. */
1267
+ .webg-picker { display: flex; flex-direction: column; gap: 12px; padding-top: 10px; }
1193
1268
  .webg-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
1194
1269
  .webg-hint { font-size: 0.8em; opacity: 0.7; color: var(--dsw-alias-label-tertiary, inherit); }
1195
1270
  .webg-error {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallpaper-engine-dsh",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Wallpaper Engine Video/Web wallpapers as the DSH web GUI background — discovery, sandboxed rendering, rotation lists, liquid-glass tuning.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",