dsh-mpkg-wallpaper 3.7.2 → 3.7.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.en.md +50 -11
- package/README.md +38 -8
- package/THIRD-PARTY.md +20 -4
- package/lib/client.js +4 -8
- package/package.json +3 -1
- package/lib/liquid-glass/geometry.js +0 -229
- package/lib/liquid-glass/index.js +0 -483
- package/lib/liquid-glass/material.js +0 -130
- package/lib/liquid-glass/renderer.js +0 -640
- package/lib/liquid-glass/shaders.js +0 -787
- package/lib/liquid-glass/v2-geometry.js +0 -65
- package/lib/liquid-glass/v2-material.js +0 -71
- package/lib/liquid-glass/v2-shaders.js +0 -269
- package/lib/liquid-glass/v2.js +0 -537
- package/lib/liquid-glass-bundle.js +0 -2611
package/README.en.md
CHANGED
|
@@ -69,7 +69,7 @@ A plugin for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harn
|
|
|
69
69
|
- Based on CSS `backdrop-filter`: semi-transparent + blur + edge highlight (**no longer the WebGL refraction version** — WebGL was removed in v3.6.0, see below). Four toggles:
|
|
70
70
|
- **lgTest (test mode)**: keeps only wallpaper + floating + layout, and overrides no DSH token (otherwise a translucent base makes the chat box transparent without blur)
|
|
71
71
|
- **lgComposer / lgSidebar / lgHeader**: add a liquid-glass overlay to the message-bubble area / sidebar / title bar respectively (the sidebar can only be semi-transparent + edge highlight because of the settings dialog's render hierarchy — it **cannot** use backdrop-filter, or it would squash the settings dialog into the sidebar — historical pitfall)
|
|
72
|
-
- **History**: early versions used real WebGL refraction (`lib/liquid-glass/` library + `liquid-glass-bundle.js` 107KB); v3.6.0 removed the WebGL runtime (unstable + large) in favor of pure CSS. `lib/liquid-glass/*.js` and `liquid-glass-bundle.js`
|
|
72
|
+
- **History**: early versions used real WebGL refraction (`lib/liquid-glass/` library + `liquid-glass-bundle.js` 107KB); v3.6.0 removed the WebGL runtime (unstable + large) in favor of pure CSS. `lib/liquid-glass/*.js` and `liquid-glass-bundle.js` **stay in the repo but have been moved off the publish surface** (negative `files` patterns `!lib/liquid-glass/**` + `!lib/liquid-glass-bundle.js` ⇒ **not shipped in the npm package**, P-127) and are **no longer referenced by the client** (`lgModule` is declared but never assigned). The host-side static route is still a **live path** (`/api/mpkg-wallpaper/lg/<file>.js` really `readFileSync`s `lib/liquid-glass/<file>`; in-repo development/demos are unaffected), and the `tools/liquid-demo/` demo page uses that same mount (P-122). `tools/liquid-demo/` stays in the repo only and is **not shipped** (`files` excludes `tools/`). So they are **not dead files**; **being off the publish surface means those 10 files are no longer distributed ⇒ the MIT attribution obligation surface disappears**, while the repo keeps all 10 and still records their 10 sha256 sums (`THIRD-PARTY.md` §1.3; numbers and gates: `docs/PUBLISH-SURFACE-LIQUID-GLASS.md` P-127).
|
|
73
73
|
|
|
74
74
|
**🎬 Lens & picture**
|
|
75
75
|
- Lens zoom (10–2000%) & pan, brightness (50–150%), light sharpen, Deep diving background box
|
|
@@ -230,9 +230,9 @@ without anyone noticing locally. Gate step 12 turns that into a machine-checkabl
|
|
|
230
230
|
(`node tools/style-scope-guard.mjs`):
|
|
231
231
|
|
|
232
232
|
* **It uses the real artifact**: `buildCss` is never re-implemented. `tools/_stub.mjs` loads `lib/client.js`
|
|
233
|
-
in Node and calls the plugin's own `__mpwBuildCss(patch)` over **600+ setting combinations** (
|
|
234
|
-
defaults / each boolean alone / all 512 combinations of the 9 core switches /
|
|
235
|
-
numeric 0 and 100 / no wallpaper / lgTest), then parses every generated rule, including `@media` / `@supports` nesting.
|
|
233
|
+
in Node and calls the plugin's own `__mpwBuildCss(patch)` over **600+ setting combinations** (the run prints
|
|
234
|
+
the exact count; 615 this round: defaults / each boolean alone / all 512 combinations of the 9 core switches /
|
|
235
|
+
the `bsCompat` family / numeric 0 and 100 / no wallpaper / lgTest), then parses every generated rule, including `@media` / `@supports` nesting.
|
|
236
236
|
* **Verdict**: every selector must hit our own markers (`.mpw*` / `[data-mpw*]` / `#mpw-*`) or a **registered**
|
|
237
237
|
host/third-party scope. The `bsCompat` block that deliberately targets third-party DOM
|
|
238
238
|
(`[data-dsh-better-sidebar] …`) is allowed **only because it is declared** in the allow-list with a reason and a
|
|
@@ -283,7 +283,10 @@ check: `node tools/switch-wiring-test.mjs` (gate step 2):
|
|
|
283
283
|
* non-boolean features (`accent` / `aquaTextEnhance` must change the output); `themeColor` is
|
|
284
284
|
"always-emitted CSS + runtime attribute gate", so the check asserts the gate rules exist instead;
|
|
285
285
|
* switches **proven dead** go into `KNOWN_DEAD` and are listed on every run (two-way assertion: fixing one
|
|
286
|
-
requires deleting its entry).
|
|
286
|
+
requires deleting its entry). **The table is now empty** (`lgCss`/`sessionFollow` fixed, `glassWindow` retired
|
|
287
|
+
and deleted); **retired** switches are instead guarded by section A0 ("0 dangling references in the source +
|
|
288
|
+
0 orphan key in either dictionary"), each with a permanent mutation that must go red.
|
|
289
|
+
That list drove the first fix: **`lgCss` (pure CSS/SVG liquid glass) never ran at
|
|
287
290
|
all** — the block referenced `bdSupported` while the `const` was declared after it, i.e. a same-scope TDZ
|
|
288
291
|
`ReferenceError` swallowed by the outer `catch { /* liquid glass failure must not affect other styles */ }`
|
|
289
292
|
(the catch is kept; it now only fires on real failures). The criteria are two-way: `lgCss:true` must emit the
|
|
@@ -292,8 +295,17 @@ check: `node tools/switch-wiring-test.mjs` (gate step 2):
|
|
|
292
295
|
toggle and copy but nothing read `section.sessionFollow`; implemented per the **user-visible copy** (on = follow
|
|
293
296
|
that opacity, off = **back to the host's original colour**), default unchanged, asserted in
|
|
294
297
|
`tools/switch-wiring-test.mjs` section A4 (three two-way assertions in both default and unified-blur contexts;
|
|
295
|
-
the "remove the read" mutation must go red). The third, `glassWindow`,
|
|
296
|
-
|
|
298
|
+
the "remove the read" mutation must go red). The third, `glassWindow`, was handled under the user's policy of
|
|
299
|
+
**"no copy that is visible but unclickable"**: it had **no toggle row (nobody could see it) and no read point
|
|
300
|
+
(nothing to click)**, while the feature its copy promised is already delivered by `settingsBlur` (settings-panel
|
|
301
|
+
blur) plus `dialogBlur`/`popoverBlur` ⇒ its copy and field were **deleted** rather than wired (wiring would add
|
|
302
|
+
a second switch for the same element and make the settings panel a backdrop root). Six deletion sites
|
|
303
|
+
(2 i18n lines each in zh/en + the `lgTest` preset default + the reset defaults + `BACKUP_FIELDS` + the import
|
|
304
|
+
`boolFields`; the export/sanitise lists are deleted **in lockstep** so an imported backup cannot fall through to
|
|
305
|
+
the "unregistered type" passthrough) — asserted in section **A0** of `tools/switch-wiring-test.mjs`
|
|
306
|
+
(0 hits in the source + 0 hits in both dictionaries) with the permanent mutation
|
|
307
|
+
`retired-glasswindow-copy-restored` required to go red; the default-context CSS is **sha256-identical**
|
|
308
|
+
before and after. See [`docs/TOKEN-NAMESPACE.md`](docs/TOKEN-NAMESPACE.md) §3b. **`bsCompat` (the better-sidebar adaptation master switch) now defaults to on** (ruled 2026-09-18): the
|
|
297
309
|
bottom-panel float adaptation is settled on real devices, so a default of off meant nobody ever saw it.
|
|
298
310
|
Existing users are migrated **only if they never set it explicitly**; anyone who turned it off by hand is
|
|
299
311
|
**never overridden** (the write path stamps a `bsCompatUserSet` marker; the migration itself does not).
|
|
@@ -311,6 +323,31 @@ check: `node tools/switch-wiring-test.mjs` (gate step 2):
|
|
|
311
323
|
Neither assertion was relaxed.
|
|
312
324
|
* discrimination proof: reverting either gate back under `aquaOn` must turn the audit red.
|
|
313
325
|
|
|
326
|
+
## Pre-commit gate (a few seconds, 2026-09-19 / P-129)
|
|
327
|
+
|
|
328
|
+
The audit above (plus the panel-rendering regression) is now available as a **few-second pre-commit** — four
|
|
329
|
+
seconds before a commit is enough to stop the two failure classes that **have really happened here several
|
|
330
|
+
times**: "the switch isn't wired" and "the panel no longer renders".
|
|
331
|
+
|
|
332
|
+
```sh
|
|
333
|
+
cd dsh-mpkg-wallpaper
|
|
334
|
+
git config core.hooksPath .githooks # install (this clone only; git config --unset core.hooksPath to remove)
|
|
335
|
+
git commit --no-verify # bypass once
|
|
336
|
+
MPW_SKIP_PRECOMMIT=1 git commit -m … # bypass once (the script's own explicit escape hatch, always exit 0)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
* **What runs**: `tools/panel-smoke.mjs` (panel rendering + language dictionaries, ~0.3 s) and
|
|
340
|
+
`tools/switch-wiring-test.mjs` (every switch must really change the output + retired switches with 0 dangling
|
|
341
|
+
references + 7 discrimination mutations, ~2.5 s), **serially**, **2.8–3.9 s** total across repeated runs.
|
|
342
|
+
* **What does not run**: the 12 steps of `tools/check.sh` (step 3 is the 1115-combination CSS matrix, step 9
|
|
343
|
+
onwards needs headless Firefox) stay **out** of pre-commit — a minute-long gate in pre-commit only makes people
|
|
344
|
+
afraid to commit. The full gate is still `bash tools/check.sh` (or CI).
|
|
345
|
+
* **It does not block development**: installing is explicit (not installing changes nothing); if the staged
|
|
346
|
+
changes touch no artifact path (no `lib/`, `tools/`, `package.json`) it **skips**; a missing `node` or script
|
|
347
|
+
prints one line and exits 0; on failure it prints the red lines verbatim and tells you how to bypass — it never
|
|
348
|
+
edits files and never auto-fixes.
|
|
349
|
+
* **Details, every measured output and the unverified items**: [`docs/PRE-COMMIT.md`](docs/PRE-COMMIT.md).
|
|
350
|
+
|
|
314
351
|
## Video-wallpaper transcoding: a **misjudgement** + resource caps (2026-09-17, item 1)
|
|
315
352
|
|
|
316
353
|
> User report: "I'm not using transcoding, my wallpaper is a **video-class mpkg**, the
|
|
@@ -545,8 +582,8 @@ dsh-mpkg-wallpaper/
|
|
|
545
582
|
│ ├── web-wallpaper.js # web wallpapers: content-first type detection + WE API shim source + entry-HTML injection + CORS policy (MIT, self-written)
|
|
546
583
|
│ ├── client.js # browser: mpkg parsing + settings page + bg DOM + blur suite + library + time-variation/web options + playback control/power saving
|
|
547
584
|
│ ├── pkg-extract.js# scene.pkg static-frame/layer extraction (PKG+LZ4+TEX, MIT, from elysia395)
|
|
548
|
-
│ ├── liquid-glass/ # WebGL liquid-glass library (**leftover, no runtime ref
|
|
549
|
-
│ └── liquid-glass-bundle.js # liquid-glass bundle (107KB, **not referenced by the client
|
|
585
|
+
│ ├── liquid-glass/ # WebGL liquid-glass library (**leftover, not shipped**; no runtime ref; CSS version since v3.6.0)
|
|
586
|
+
│ └── liquid-glass-bundle.js # liquid-glass bundle (107KB, **not shipped**; not referenced by the client; byte-rebuildable from the kept sources, sha256 db50361c…)
|
|
550
587
|
├── tools/ # gates/tests/benches + lg build/inline scripts + liquid-demo page (for developers)
|
|
551
588
|
│ # audio scan: audio-scan-bench.mjs (timing table) / audio-scan-test.mjs (spec assertions · no full inflate · cache)
|
|
552
589
|
│ # scene-audio-route-test.mjs (/raw Range + probe route + security)
|
|
@@ -555,6 +592,8 @@ dsh-mpkg-wallpaper/
|
|
|
555
592
|
│ # bundle-equivalence-test.mjs (gate step 11: same route assertions on source and bundle + mutation controls)
|
|
556
593
|
│ # style scope: style-scope-guard.mjs (gate step 12: every injected CSS rule must hit .mpw*/[data-mpw*];
|
|
557
594
|
│ # host/third-party scopes must be registered in the allow-list with a reason + docs pointer)
|
|
595
|
+
│ # pre-commit: pre-commit.sh (a few seconds: panel-smoke + switch-wiring, with path filtering and
|
|
596
|
+
│ # bypass hatches; paired with ../.githooks/pre-commit — see README / docs/PRE-COMMIT.md)
|
|
558
597
|
│ # note: the research-era Python tools (unmpkg/tex2png/mdl_explorer/xref) were
|
|
559
598
|
│ # **deleted (GPL lineage unresolved, 2026-09-16)** — see `../docs/COPYING-RULES.md` §6
|
|
560
599
|
├── dist/ # build output (**not committed**, gitignored): dsh-mpkg-wallpaper.bundle.mjs (Option 4, generated on demand)
|
|
@@ -564,8 +603,8 @@ dsh-mpkg-wallpaper/
|
|
|
564
603
|
├── README.en.md # English
|
|
565
604
|
└── THIRD-PARTY.md # third-party provenance / clean-room record (shipped, MIT-side attribution)
|
|
566
605
|
```
|
|
567
|
-
> Note: `lib/liquid-glass/` and `lib/liquid-glass-bundle.js`
|
|
568
|
-
> Why `dist/` is **not committed**: it is a pure derivative of `lib/*.js` (an inlined artifact) that duplicates tracked sources byte for byte, and two builds are byte-identical (machine-asserted in `tools/bundle-equivalence-test.mjs` §②); committing it would only create drift ("edited lib, forgot to rebuild") . By contrast `lib/liquid-glass-bundle.js` **is** committed because it *used to be* a **runtime input** (base64 written into `lib/client.js` by `tools/inline-lg-bundle.mjs`) — ⚠ **measured 2026-09-18: that link is dead**: neither `LG_BUNDLE_B64` nor `LG_BUNDLE_SRC` exists in `lib/client.js` (the only `B64` hit is a comment), and both inline tools now fail with "missing placeholder". It is therefore currently **neither referenced by the client nor a runtime input**; it
|
|
606
|
+
> Note: `lib/liquid-glass/` and `lib/liquid-glass-bundle.js` **have been moved off the publish surface** (negative `files` patterns `!lib/liquid-glass/**` + `!lib/liquid-glass-bundle.js`, P-127 ⇒ **not in the npm package**; measured 22 files/1 626 235 B → 12 files/1 389 781 B), yet **all of them stay in the repo**, and the **client no longer references them** (WebGL was removed in v3.6.0 in favor of the CSS version); the host still keeps the `/api/mpkg-wallpaper/lg` route (live path, no callers; in-repo development/demos unaffected, an npm-installed copy returns 404 by design). Local backups such as `lib/client.js.bak-*` are likewise excluded by negative `files` patterns (`!lib/**/*.bak*`) and are **not shipped**; both are guarded by **two-way** machine assertions in section ⑨ of `tools/integrity-check.mjs` ("not on the publish surface" **and** "none of them may disappear from the repo").
|
|
607
|
+
> Why `dist/` is **not committed**: it is a pure derivative of `lib/*.js` (an inlined artifact) that duplicates tracked sources byte for byte, and two builds are byte-identical (machine-asserted in `tools/bundle-equivalence-test.mjs` §②); committing it would only create drift ("edited lib, forgot to rebuild") . By contrast `lib/liquid-glass-bundle.js` **is** committed because it *used to be* a **runtime input** (base64 written into `lib/client.js` by `tools/inline-lg-bundle.mjs`) — ⚠ **measured 2026-09-18: that link is dead**: neither `LG_BUNDLE_B64` nor `LG_BUNDLE_SRC` exists in `lib/client.js` (the only `B64` hit is a comment), and both inline tools now fail with "missing placeholder". It is therefore currently **neither referenced by the client nor a runtime input**; it **stays in the repo** (off the npm package since P-127) and can be rebuilt **byte-identically** from the kept sources. `dist/` is also outside the `files` whitelist, so the npm package never carries it.
|
|
569
608
|
|
|
570
609
|
## Acknowledgements
|
|
571
610
|
|
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
- 基于 CSS `backdrop-filter` 的半透明 + 模糊 + 边缘高光(**不再是 WebGL 折射版**——WebGL 液态玻璃已在 v3.6.0 移除,见下)。分四个开关:
|
|
72
72
|
- **lgTest(测试模式)**:只保留壁纸 + 悬浮 + 布局,不覆盖任何 DSH token(否则背景读半透明会让聊天框透明无模糊)
|
|
73
73
|
- **lgComposer / lgSidebar / lgHeader**:分别给聊天气泡区、侧边栏、标题栏加液态玻璃叠加层(侧边栏因设置弹窗渲染层级限制,只能做半透明 + 边缘高光,**不能加 backdrop-filter**,否则会把设置弹窗压缩进侧边栏——历史踩坑)
|
|
74
|
-
- **历史说明**:早期版本用的是 WebGL 真折射(`lib/liquid-glass/` 库 + `liquid-glass-bundle.js` 107KB);因不稳定且体积大,v3.6.0 已删掉 WebGL 运行时,改用纯 CSS 版。`lib/liquid-glass/*.js`、`liquid-glass-bundle.js`
|
|
74
|
+
- **历史说明**:早期版本用的是 WebGL 真折射(`lib/liquid-glass/` 库 + `liquid-glass-bundle.js` 107KB);因不稳定且体积大,v3.6.0 已删掉 WebGL 运行时,改用纯 CSS 版。`lib/liquid-glass/*.js`、`liquid-glass-bundle.js` **仍在仓库、但已移出发布面**(`files` 负向模式 `!lib/liquid-glass/**` + `!lib/liquid-glass-bundle.js` ⇒ **不进 npm 包**,P-127):**客户端已无任何引用**(`lgModule` 只声明、从未赋值),但**宿主端那条静态托管路由仍是活路径**(`/api/mpkg-wallpaper/lg/<file>.js` 命中时真的 `readFileSync` 读 `lib/liquid-glass/<file>`;仓库内开发/演示照旧),`tools/liquid-demo/` 的演示页也走同一条挂载(P-122)。`tools/liquid-demo/` 只留在仓库、**不进发布包**(`files` 白名单不含 `tools/`)。⇒ 它们**不是死文件**;**移出发布面 ⇒ 不再分发这 10 个文件 ⇒ MIT 署名义务面随之消失**,但仓库内**一个不少**、仍如实登记 10 个 sha256(`THIRD-PARTY.md` §1.3;发布面数字与判据见 `docs/PUBLISH-SURFACE-LIQUID-GLASS.md` P-127)。
|
|
75
75
|
|
|
76
76
|
**🎬 镜头与画面**
|
|
77
77
|
- 镜头缩放(10–2000%)与平移、画面亮度(50–150%)、轻度锐化、Deep diving 背景框
|
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
门禁第 12 步把它变成会变红的机器判据(`node tools/style-scope-guard.mjs`):
|
|
250
250
|
|
|
251
251
|
* **拿真实产物**:不重写 `buildCss`,用 `tools/_stub.mjs` 在 Node 里跑 `lib/client.js`,调用插件自己暴露的
|
|
252
|
-
`__mpwBuildCss(patch)`,按源码里的 `boolFields`/`numFields` 自动枚举 **600+
|
|
253
|
-
每个布尔开关单独开 / 核心 9 开关全 512 组合 / bsCompat 家族 / 数值 0 与 100 / 无壁纸 / lgTest),
|
|
252
|
+
`__mpwBuildCss(patch)`,按源码里的 `boolFields`/`numFields` 自动枚举 **600+ 组设置**(实跑会打印条数;
|
|
253
|
+
本轮 615:默认段 / 每个布尔开关单独开 / 核心 9 开关全 512 组合 / bsCompat 家族 / 数值 0 与 100 / 无壁纸 / lgTest),
|
|
254
254
|
再对生成出来的 CSS 逐条解析(含 `@media`/`@supports`)。
|
|
255
255
|
* **判据**:每条规则的选择器必须命中我们自己的标记(`.mpw*` / `[data-mpw*]` / `#mpw-*`),或命中**已登记**的
|
|
256
256
|
宿主/第三方作用域——`bsCompat` 那句故意打第三方 DOM 的 `[data-dsh-better-sidebar] …` 也在其列,
|
|
@@ -292,6 +292,8 @@
|
|
|
292
292
|
* 非布尔功能(`accent`/`aquaTextEnhance` 设值必须改变产物);`themeColor` 属"CSS 常驻 + 运行时属性门控",
|
|
293
293
|
判据改为断言门控规则确实在产物里。
|
|
294
294
|
* **已证实失效**的开关进 `KNOWN_DEAD` 并在每次运行时显式列出(双向断言:修好了必须从表里删)。
|
|
295
|
+
**该表现已清空**(`lgCss`/`sessionFollow` 已修好、`glassWindow` 已退役删除);**退役**(删掉)的开关改由
|
|
296
|
+
A0 段看住"源码 0 悬空引用 + zh/en 两套字典 0 孤儿文案",各配一条常驻变异(加回去必须变红)。
|
|
295
297
|
本轮据此修掉了第 1 条:**`lgCss`(纯 CSS/SVG 液态玻璃)整块从未执行** —— 块内引用 `bdSupported`,
|
|
296
298
|
而该 const 声明在它之后 ⇒ 同一函数作用域 TDZ `ReferenceError`,被外层
|
|
297
299
|
`catch { /* 液态玻璃失败不得影响其它样式 */ }` 吞掉(catch 原样保留,现在只在真的失败时才起作用)。
|
|
@@ -300,7 +302,13 @@
|
|
|
300
302
|
第 2 条也修了:**`sessionFollow`(新会话按钮跟随面板不透明度)**——设置页有开关 + 文案,但全仓无人读
|
|
301
303
|
`section.sessionFollow`;按**用户可见文案**实现(开 = 跟随那条透明度,关 = **回到宿主原色**),默认档不变,
|
|
302
304
|
判据在 `tools/switch-wiring-test.mjs` 的 A4 段(默认档 + 统一虚化档各 3 条双向断言;变异「把读取删掉」必红)。
|
|
303
|
-
第 3 条 `glassWindow`
|
|
305
|
+
第 3 条 `glassWindow` 已按**用户政策「不留看得见却点不动」**处理:它**既无 toggleRow(没人看得见)、
|
|
306
|
+
也无读取点(点不动)**,而文案承诺的功能已由 `settingsBlur`(设置面板虚化)+ `dialogBlur`/`popoverBlur`
|
|
307
|
+
覆盖 ⇒ **删文案 + 删字段**(不接线,避免多出第二个管同一元素的开关、也避免把设置面板变成 backdrop root)。
|
|
308
|
+
删除点 6 处(zh/en i18n 各 2 行 + `lgTest` 预设默认值 + 重置默认值 + `BACKUP_FIELDS` + 导入 `boolFields`,
|
|
309
|
+
导出/净化**同步删**以免导入备份时落进"未登记类型"兜底);判据在 `tools/switch-wiring-test.mjs` 的 **A0 段**
|
|
310
|
+
(源码 0 命中 + zh/en 两套字典 0 命中)+ 常驻变异 `retired-glasswindow-copy-restored` 必红;
|
|
311
|
+
删前删后**默认档产物 sha256 逐字节相同**。详见
|
|
304
312
|
[`docs/TOKEN-NAMESPACE.md`](docs/TOKEN-NAMESPACE.md) §3b。
|
|
305
313
|
**`bsCompat`(better-sidebar 适配总开关)默认已改为开**(2026-09-18 用户裁定):底部面板悬浮适配已真机定案,
|
|
306
314
|
默认关 = 没人看得见;存量用户**只在"从没显式设过"时**被迁移,**手动关过的绝不覆盖**(写入口打
|
|
@@ -315,6 +323,26 @@
|
|
|
315
323
|
(`(headerBlur || unifyTint) && headerBg`,与 `css-matrix` 断言 7 同口径)—— 两条断言都没放宽。
|
|
316
324
|
* 分辨力自证:把 `accent` / `aquaTextEnhance` 的门控改回被 `aquaOn` 包住 ⇒ 必须变红(两条变异)。
|
|
317
325
|
|
|
326
|
+
## 提交前门禁(秒级 pre-commit,2026-09-19 / P-129)
|
|
327
|
+
|
|
328
|
+
上面这条审计(以及面板渲染回归)已经抽成**秒级 pre-commit**,**提交前 4 秒**就能拦住"开关没接线""面板渲染坏了"这两类**已经真实发生过多次**的事故:
|
|
329
|
+
|
|
330
|
+
```sh
|
|
331
|
+
cd dsh-mpkg-wallpaper
|
|
332
|
+
git config core.hooksPath .githooks # 装载(只影响本机 clone;git config --unset core.hooksPath 卸载)
|
|
333
|
+
git commit --no-verify # 单次绕过
|
|
334
|
+
MPW_SKIP_PRECOMMIT=1 git commit -m … # 单次绕过(走脚本内显式口,恒 exit 0)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
* **跑什么**:`tools/panel-smoke.mjs`(面板渲染 + 语言字典,~0.3 s)+ `tools/switch-wiring-test.mjs`
|
|
338
|
+
(每个开关必须真的改变产物 + 退役开关 0 悬空引用 + 7 条分辨力变异自证,~2.5 s),**串行**,多次实测合计
|
|
339
|
+
**2.8–3.9 s**。
|
|
340
|
+
* **不跑什么**:`tools/check.sh` 的 12 步(第 3 步 1115 组 CSS 矩阵、第 9 步起无头 Firefox)**不进** pre-commit
|
|
341
|
+
—— 分钟级门禁塞进 pre-commit 只会让人"改一行不敢提交"。完整门禁仍是 `bash tools/check.sh`(或 CI)。
|
|
342
|
+
* **不阻塞开发**:装载是显式的(不装零影响);暂存区只动了非产物路径(无 `lib/`·`tools/`·`package.json`)⇒ **直接跳过**;
|
|
343
|
+
缺 `node`/缺脚本 ⇒ 打印一句就 `exit 0`;失败会**原样打印红行**并提示绕过方式,不改文件、不自动 fix。
|
|
344
|
+
* **细节、全部实测输出与未证实项**:[`docs/PRE-COMMIT.md`](docs/PRE-COMMIT.md)。
|
|
345
|
+
|
|
318
346
|
## 视频壁纸转码:判定是"误判" + 资源上限(2026-09-17,第 1 条)
|
|
319
347
|
|
|
320
348
|
> 用户原话:「我现在并没有使用视频转码,我用的是 **video 类的 mpkg**,然后**解码帧率无上限**,
|
|
@@ -541,8 +569,8 @@ dsh-mpkg-wallpaper/
|
|
|
541
569
|
│ ├── web-interaction.js # 网页壁纸交互(第 11 条):坐标换算/事件整形/交互模式状态机/舞台契约(MIT,自写;语义参照 webwallgl)
|
|
542
570
|
│ ├── client.js # 浏览器端:mpkg 解析 + 设置页 + 背景 DOM + 虚化体系 + 壁纸库 + 时间变化/网页设置 + 播放控制/省电
|
|
543
571
|
│ ├── pkg-extract.js# scene.pkg 静态帧/图层提取(PKG+LZ4+TEX,MIT,来自 elysia395)
|
|
544
|
-
│ ├── liquid-glass/ # 液态玻璃 WebGL
|
|
545
|
-
│ ├── liquid-glass-bundle.js # 液态玻璃打包产物(107KB
|
|
572
|
+
│ ├── liquid-glass/ # 液态玻璃 WebGL 库(**遗留 · 不进发布包**:客户端无引用,宿主仍托管 `/lg` 路由,v3.6.0 已改 CSS 版)
|
|
573
|
+
│ ├── liquid-glass-bundle.js # 液态玻璃打包产物(107KB,**不进发布包**;客户端无引用,可由保留源逐字节重建,sha256 db50361c…)
|
|
546
574
|
│ └── THIRD-PARTY.md # 第三方来历/洁净室记录与许可归属(**随包发布**,MIT 侧署名)
|
|
547
575
|
├── tools/ # 门禁/测试/基准脚本 + lg 构建/内联脚本 + liquid-demo 演示页(供开发者)
|
|
548
576
|
│ # 音频扫描:audio-scan-bench.mjs(耗时表)/ audio-scan-test.mjs(规格断言 · 不整包解压 · 缓存)
|
|
@@ -553,6 +581,8 @@ dsh-mpkg-wallpaper/
|
|
|
553
581
|
│ # bundle-equivalence-test.mjs(门禁第 11 步:同一套路由断言打源码与 bundle + 变异对照)
|
|
554
582
|
│ # 样式作用域:style-scope-guard.mjs(门禁第 12 步:注入的每条 CSS 规则都必须命中 .mpw*/[data-mpw*],
|
|
555
583
|
│ # 宿主/第三方作用域必须登记在允许清单里(带 reason + docs 指针);裸元素/:root 覆盖判红)
|
|
584
|
+
│ # 提交前门禁:pre-commit.sh(秒级:panel-smoke + switch-wiring,含路径过滤与绕过口;
|
|
585
|
+
│ # 配 ../.githooks/pre-commit,装载见 README「提交前门禁」/ docs/PRE-COMMIT.md)
|
|
556
586
|
│ # 注:研究期的 Python 工具(unmpkg/tex2png/mdl_explorer/xref)
|
|
557
587
|
│ # **已删除(GPL 血缘存疑,2026-09-16)**,见 `../docs/COPYING-RULES.md` §6
|
|
558
588
|
├── dist/ # 构建产物(**不入库**,.gitignore 忽略):dsh-mpkg-wallpaper.bundle.mjs(方式四用,现生成)
|
|
@@ -562,8 +592,8 @@ dsh-mpkg-wallpaper/
|
|
|
562
592
|
├── README.md # 本文件(中文)
|
|
563
593
|
└── README.en.md # 英文说明
|
|
564
594
|
```
|
|
565
|
-
> 注:`lib/liquid-glass/`、`lib/liquid-glass-bundle.js`
|
|
566
|
-
> `dist/` 为什么**不入库**:它是 `lib/*.js` 的纯派生物(内联产物),与被跟踪源码逐字节重复;两次构建 sha256 逐字节相同(`tools/bundle-equivalence-test.mjs` 第②节机器断言),入库只会制造"改了 lib 忘了重跑 bundle"的漂移。对照:`lib/liquid-glass-bundle.js` **入库**是因为它历史上曾是**运行期输入**(由 `tools/inline-lg-bundle.mjs` 把 base64 写进 `lib/client.js` 的模板常量)——⚠ **2026-09-18 实测该链路已失效**:`lib/client.js` 里 `LG_BUNDLE_B64`/`LG_BUNDLE_SRC` 常量**都不存在**(全文 `B64` 仅 1 处,是注释),两个 inline
|
|
595
|
+
> 注:`lib/liquid-glass/`、`lib/liquid-glass-bundle.js` **已移出发布面**(`files` 负向模式 `!lib/liquid-glass/**` + `!lib/liquid-glass-bundle.js`,P-127 ⇒ **不进 npm 包**,实测发布面 22 文件/1 626 235 B → 12 文件/1 389 781 B),但**仓库内一个不少**、**客户端不再引用**(WebGL 已在 v3.6.0 移除,改用 CSS 版);宿主仍保留 `/api/mpkg-wallpaper/lg` 托管路由(活路径、无调用方;本机开发/演示照旧,从 npm 安装的副本上按设计返回 404)。本地备份 `lib/client.js.bak-*` 同样由 `files` 负向模式(`!lib/**/*.bak*`)排除,**不进发布包**;两者都由 `tools/integrity-check.mjs` 第 ⑨ 节**双向**机器断言把关("不在发布面" + "仓库内不能少")。
|
|
596
|
+
> `dist/` 为什么**不入库**:它是 `lib/*.js` 的纯派生物(内联产物),与被跟踪源码逐字节重复;两次构建 sha256 逐字节相同(`tools/bundle-equivalence-test.mjs` 第②节机器断言),入库只会制造"改了 lib 忘了重跑 bundle"的漂移。对照:`lib/liquid-glass-bundle.js` **入库**是因为它历史上曾是**运行期输入**(由 `tools/inline-lg-bundle.mjs` 把 base64 写进 `lib/client.js` 的模板常量)——⚠ **2026-09-18 实测该链路已失效**:`lib/client.js` 里 `LG_BUNDLE_B64`/`LG_BUNDLE_SRC` 常量**都不存在**(全文 `B64` 仅 1 处,是注释),两个 inline 工具现在跑会报「缺少占位区」;因此它当前**既不被客户端引用、也不再是运行期输入**,只是**留在仓库**(P-127 起**不再进包**),且可由保留源**逐字节重建**(`node tools/build-lg-bundle.mjs` ⇒ sha256 与入库那份相同)。`dist/` 也不在 `files` 白名单 ⇒ npm 包不夹带。
|
|
567
597
|
|
|
568
598
|
## 致谢
|
|
569
599
|
|
package/THIRD-PARTY.md
CHANGED
|
@@ -42,13 +42,16 @@
|
|
|
42
42
|
|
|
43
43
|
> ①(2026-09-18,P-122) 本节按逐副本复核结果重写;复核全过程(引用面/身份/判据/反向自证)见
|
|
44
44
|
> `docs/LIQUID-GLASS-DEDUP.md`。
|
|
45
|
+
> ①(2026-09-19,**P-127**) **发布面变更**:这 10 个文件已用 `files` 负向模式移出发布面 ⇒
|
|
46
|
+
> **不再随包分发**(详见 `docs/PUBLISH-SURFACE-LIQUID-GLASS.md`)。下方"发布面/署名"两处口径已同步;
|
|
47
|
+
> **仓库内 10 个文件一个不少**,sha256 登记照旧。
|
|
45
48
|
|
|
46
49
|
| 项 | 内容 |
|
|
47
50
|
|---|---|
|
|
48
51
|
| 涉及文件 | `lib/liquid-glass/*.js`(9 文件,129 097 B,5830 行)+ 其派生产物 `lib/liquid-glass-bundle.js`(107 420 B,2611 行) |
|
|
49
52
|
| 出处 | 外部 MIT 项目 **`apple-liquid-glass-webgl`**。**唯一记录是提交 `d13019d`**(2026-08-24)的说明:"复用 apple-liquid-glass-webgl(MIT,零依赖)源码 → lib/liquid-glass/ + vendor/" |
|
|
50
|
-
| 许可 | **MIT**(MIT
|
|
51
|
-
| 发布面 |
|
|
53
|
+
| 许可 | **MIT**(MIT 要求**随副本**携带版权声明与许可正文 —— 见下方"署名义务面") |
|
|
54
|
+
| 发布面 | **不随包发布**(①P-127:`files` 负向模式 `!lib/liquid-glass/**` + `!lib/liquid-glass-bundle.js`;实测发布面 22 文件/1 626 235 B → 12 文件/1 389 781 B,`tools/integrity-check.mjs` 第 ⑨ 节双向断言把关)。**文件仍在仓库内**(旧的"随包发布"登记见 `docs/LIQUID-GLASS-DEDUP.md` §1②、§4 第 6 项,那是 P-127 之前的口径) |
|
|
52
55
|
| 运行期引用 | 宿主路由 `GET /api/mpkg-wallpaper/lg/<file>.js` 在命中时 `readFileSync` 读取 `lib/liquid-glass/<file>`(`lib/index.js:3299-3314`)。客户端**无**调用方(`lib/client.js` 无 `liquid-glass` 引用、无动态 import) |
|
|
53
56
|
| 副本数 | **1 份**(原 `tools/liquid-demo/vendor/` 的 9 文件与 `lib/liquid-glass/` sha256 两两相同 ⇒ 2026-09-18 已删,演示页改为加载唯一一份) |
|
|
54
57
|
|
|
@@ -56,10 +59,22 @@
|
|
|
56
59
|
对这 9 个文件 + bundle 逐个 grep,`MIT License` / `Copyright (c)` / `Copyright ©` /
|
|
57
60
|
`SPDX-License-Identifier` / `Permission is hereby granted` / `The above copyright`
|
|
58
61
|
命中数**全部为 0**,也没有任何上游 URL(`github`/`http`/`apple-liquid` 命中 0)。
|
|
59
|
-
⇒
|
|
62
|
+
⇒ 保留副本**不带上游署名**;署名义务**只**由本文件 + 提交记录承担。
|
|
60
63
|
补法(未做,见 `docs/LIQUID-GLASS-DEDUP.md` §9 第 1 项):找到上游后把其 LICENSE 正文与版权行
|
|
61
64
|
落到本文件或随附文件。
|
|
62
65
|
|
|
66
|
+
**署名义务面(①2026-09-19,P-127 后的准确口径)**:MIT 的署名要求是"**随副本**携带版权与许可声明"。
|
|
67
|
+
①P-127 把这 10 个文件**移出发布面** ⇒ **本包不再分发这些副本** ⇒ **该义务面随之消失**
|
|
68
|
+
(`tools/integrity-check.mjs` 第 ⑨ 节逐条断言这 10 个路径**不在** npm 包里)。
|
|
69
|
+
**但这不等于可以不管**:
|
|
70
|
+
|
|
71
|
+
1. 仓库里**仍保留**这 10 个文件(宿主 `/lg` 路由 + 演示页运行期要读)⇒ 本节继续**如实登记**
|
|
72
|
+
出处/许可/sha256,并且**如实写明文件内没有版权行**(不粉饰);
|
|
73
|
+
2. 一旦有人**再分发**它们(fork 后单独打包、再次 vendor 进别的项目、把 `lib/liquid-glass/`
|
|
74
|
+
加回 `files`),**必须先把上游 LICENSE 正文与版权行补到本文件或随附文件**——
|
|
75
|
+
上游 URL/commit/版本号至今**未被证实**(`docs/LIQUID-GLASS-DEDUP.md` §9 第 1 项),
|
|
76
|
+
所以"再分发"这条路目前**走不通**,这也是把版权风险留在仓库内、不随包外发的原因之一。
|
|
77
|
+
|
|
63
78
|
**sha256 登记(`sha256sum` 实测,2026-09-18;删除副本前对真树取,跑完复核 10/10 未变)**:
|
|
64
79
|
|
|
65
80
|
```
|
|
@@ -77,7 +92,8 @@ db50361cfd3d860602ddc2ff8bd1b6567ff01f471df1eccb60b3bcbfbbb0cd08 lib/liquid-gla
|
|
|
77
92
|
|
|
78
93
|
- `lib/liquid-glass-bundle.js` 是**派生产物**(`tools/build-lg-bundle.mjs` 拼接上面 7 个 V2 文件,
|
|
79
94
|
去 `import`/改写 `export`):在副本上重跑该工具,产物 sha256 与上表**逐字节相同** ⇒ 无独立信息,
|
|
80
|
-
|
|
95
|
+
但仍**留在仓库**(P-127 起**不进发布面**),保留理由见 `docs/LIQUID-GLASS-DEDUP.md` §6 与
|
|
96
|
+
`docs/PUBLISH-SURFACE-LIQUID-GLASS.md` §3。
|
|
81
97
|
- 若将来引入任何**其它**第三方代码(含 GPL-3.0 的渲染器侧代码),**不得**进入本 MIT 包,
|
|
82
98
|
只允许进渲染器并按 `../docs/COPYING-RULES.md` 登记台账。
|
|
83
99
|
|
package/lib/client.js
CHANGED
|
@@ -5961,7 +5961,7 @@ ${selDark} {
|
|
|
5961
5961
|
chatFollow: false, sessionFollow: false, sidebarBlur: false, sidebarBlurAmount: 0,
|
|
5962
5962
|
headerBlur: false, headerBlurAmount: 0, headerBg: false, aquaMask: false, aquaTint: false,
|
|
5963
5963
|
aquaInk: false, aquaTextEnhance: false, todoBlur: false, clock: false, clock24h: false,
|
|
5964
|
-
clockSec: false, clockDate: false, themeColor: "", accent: "",
|
|
5964
|
+
clockSec: false, clockDate: false, themeColor: "", accent: "",
|
|
5965
5965
|
// ①(修正) lgTest 测试模式下侧边栏**不透明**(sidebar:false):用户实测
|
|
5966
5966
|
// 测试模式侧边栏透壁纸,界面花、看不清内容。测试模式要的是干净对比界面
|
|
5967
5967
|
// (壁纸在右侧聊天区可测玻璃),侧边栏不透明便于看清。
|
|
@@ -10014,7 +10014,7 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
10014
10014
|
hybrid: DEFAULT_HYBRID, roundCompat: DEFAULT_ROUND_COMPAT,
|
|
10015
10015
|
rotate: DEFAULT_ROTATE, rotateMin: 5, customDirPath: "", brightness: DEFAULT_BRIGHTNESS, float: DEFAULT_FLOAT,
|
|
10016
10016
|
playbackRate: DEFAULT_PLAYBACK_RATE, rotGroups: [], rotGroupId: "", rotSeeded: false,
|
|
10017
|
-
glassColor: "", glassAlpha: 12,
|
|
10017
|
+
glassColor: "", glassAlpha: 12, accent: "", newStyle: false, mute: true,
|
|
10018
10018
|
thinkBg: DEFAULT_THINK_BG,
|
|
10019
10019
|
bsCompat: DEFAULT_BS_COMPAT, bsFloat: false, bsFont: false, bsReveal: false, bsAlpha: false, bsAqua: false, bsBottomAvoid: false,
|
|
10020
10020
|
enabled: DEFAULT_ENABLED
|
|
@@ -10032,7 +10032,7 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
10032
10032
|
"dialogBlur", "dialogAmount", "popoverBlur", "popoverAmount", "maskBlur", "maskAmount",
|
|
10033
10033
|
"unifyTint", "unifyAmount", "sidebarAlpha", "chatFollow", "sessionFollow",
|
|
10034
10034
|
"aquaMask", "aquaTint", "aquaInk", "aquaColor", "aquaInkColor", "aquaTextEnhance", "todoBlur",
|
|
10035
|
-
"aquaMaskAlpha", "aquaTintStrength", "themeColor", "accent", "glassColor", "glassAlpha",
|
|
10035
|
+
"aquaMaskAlpha", "aquaTintStrength", "themeColor", "accent", "glassColor", "glassAlpha",
|
|
10036
10036
|
"hybrid", "roundCompat", "rotate", "rotateMin", "rotGroups", "rotGroupId", "rotSeeded",
|
|
10037
10037
|
"brightness", "float", "playbackRate", "newStyle", "mute", "thinkBg", "enabled",
|
|
10038
10038
|
"forceEnabled", "propEdits", "clock", "clock24h", "clockSec", "clockDate", "clockPos", "clockSize",
|
|
@@ -10074,7 +10074,7 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
10074
10074
|
// ①(修正) 类型校验(评审指出:原实现直接透传,`opacity:"abc"` 会污染设置):
|
|
10075
10075
|
// 数值类必须为有限数、布尔类必须为 boolean、字符串类必须为 string。
|
|
10076
10076
|
const numFields = ["opacity", "blur", "zoom", "headerBlurAmount", "headerFrostAmount", "dialogAmount", "popoverAmount", "popoverAlpha", "maskAmount", "unifyAmount", "sidebarAlpha", "aquaMaskAlpha", "aquaTintStrength", "glassAlpha", "lgCssAmount", "rotateMin", "brightness", "playbackRate", "clockSize", "fpsCap", "resMax", "bsRevealAlpha", "sceneWatchdogSecs"]; // ①(批次15) 四处接线③:导入数值净化
|
|
10077
|
-
const boolFields = ["sidebar", "sharp", "headerBlur", "headerBg", "rightSidebarBlur", "dialogBlur", "popoverBlur", "maskBlur", "unifyTint", "chatFollow", "sessionFollow", "aquaMask", "aquaTint", "aquaInk", "aquaTextEnhance", "todoBlur", "hybrid", "roundCompat", "rotate", "float", "newStyle", "mute", "thinkBg", "enabled", "forceEnabled", "clock", "clock24h", "clockSec", "clockDate", "
|
|
10077
|
+
const boolFields = ["sidebar", "sharp", "headerBlur", "headerBg", "rightSidebarBlur", "dialogBlur", "popoverBlur", "maskBlur", "unifyTint", "chatFollow", "sessionFollow", "aquaMask", "aquaTint", "aquaInk", "aquaTextEnhance", "todoBlur", "hybrid", "roundCompat", "rotate", "float", "newStyle", "mute", "thinkBg", "enabled", "forceEnabled", "clock", "clock24h", "clockSec", "clockDate", "bsCompat", "bsFloat", "bsFont", "bsReveal", "bsAlpha", "bsAqua", "bsBottomAvoid", "lgTest", "lgCss", "lgComposer", "lgSidebar", "lgHeader", "powPauseHidden", "powPauseBlur", "powPauseBattery", "sceneWatchdog"]; // ①(批次15) 四处接线④:导入布尔净化
|
|
10078
10078
|
const strFields = ["aquaColor", "aquaInkColor", "themeColor", "accent", "glassColor", "clockPos", "fontColorGrayColor", "sceneDebugParams"]; // ①(批次15) sceneDebugParams 走字符串净化(非法键/值在写入端丢弃)
|
|
10079
10079
|
for (const k of BACKUP_FIELDS) {
|
|
10080
10080
|
const v = settings[k];
|
|
@@ -12364,8 +12364,6 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
12364
12364
|
"todoBlur": "任务列表磨砂",
|
|
12365
12365
|
"glass.title": "液态玻璃(elysia395 方案)",
|
|
12366
12366
|
"glass.desc": "设置卡片/弹窗玻璃化:玻璃底色 + 白叠层强度 + 总开关(与 Aqua 其他功能独立,默认关)",
|
|
12367
|
-
"glassWindow": "设置窗口液态玻璃",
|
|
12368
|
-
"glassWindow.desc": "整个设置卡片/弹窗玻璃化(半透明 + 模糊 + 底色 tint)",
|
|
12369
12367
|
"glass.accent": "配色(accent)",
|
|
12370
12368
|
"glass.accent.hint": "驱动按钮/滑条/选中/链接/发送键等品牌交互元素(与主题颜色分工:它管整体底色)",
|
|
12371
12369
|
"glass.color": "玻璃颜色",
|
|
@@ -13032,8 +13030,6 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
13032
13030
|
// ①(P-66 真 bug 修复) 补齐 zh 有、en 缺的 18 个键(缺键 → 英文界面直接显示 key 原文)
|
|
13033
13031
|
"glass.title": "Liquid glass (elysia395 approach)",
|
|
13034
13032
|
"glass.desc": "Glassmorphism for the settings card/dialog: glass tint + white overlay strength + master switch (independent of the other Aqua features; off by default)",
|
|
13035
|
-
"glassWindow": "Liquid glass on the settings window",
|
|
13036
|
-
"glassWindow.desc": "Turn the whole settings card/dialog into glass (translucent + blur + background tint)",
|
|
13037
13033
|
"glass.accent": "Accent color",
|
|
13038
13034
|
"glass.accent.hint": "Drives brand interactive elements (buttons/sliders/selection/links/send button); the theme color owns the overall background tint",
|
|
13039
13035
|
"glass.color": "Glass color",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mpkg-wallpaper",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.3",
|
|
4
4
|
"description": "DSH Web 壁纸引擎 Wallpaper Engine mpkg 背景插件:浏览器内直接解析 .mpkg(preview.gif 动态背景/内嵌 mp4 视频/多时段切换),整屏统一虚化/对话框/弹层/遮罩独立虚化、镜头缩放平移、时钟、冲突检测。",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"!lib/**/*.bak*",
|
|
11
11
|
"!lib/**/*.tmp*",
|
|
12
12
|
"!lib/**/*.orig",
|
|
13
|
+
"!lib/liquid-glass/**",
|
|
14
|
+
"!lib/liquid-glass-bundle.js",
|
|
13
15
|
"icon.svg",
|
|
14
16
|
"cordis.patch.yml",
|
|
15
17
|
"README.md",
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
// CPU mirror of the shape maths in FS_GLASS.
|
|
2
|
-
//
|
|
3
|
-
// The shader is the source of truth for what the glass looks like, but callers
|
|
4
|
-
// also need the same geometry on the CPU: to know which component the pointer
|
|
5
|
-
// is over, and to split a large element list into groups that cannot influence
|
|
6
|
-
// each other. Keeping both in this module - free of any WebGL or DOM
|
|
7
|
-
// dependency - is what makes those rules unit-testable.
|
|
8
|
-
//
|
|
9
|
-
// Every length here is in the same unit as the element coordinates (CSS pixels
|
|
10
|
-
// for the public API). The renderer applies `dpr` separately.
|
|
11
|
-
|
|
12
|
-
// The glass shader carries the group in uniform arrays of this length.
|
|
13
|
-
export const MAX_GLASS_SHAPES = 16;
|
|
14
|
-
|
|
15
|
-
// `sdGroup` weights each shape by exp(-(d - nearest) / scale). Past this many
|
|
16
|
-
// multiples of `scale` the contribution is below 1/3000 of a pixel of distance,
|
|
17
|
-
// which is far under the quantisation of the RGBA8 output. Shapes separated by
|
|
18
|
-
// more than that can be shaded in different draw calls without a visible seam.
|
|
19
|
-
const MERGE_INFLUENCE_SCALES = 8;
|
|
20
|
-
|
|
21
|
-
// Matches the shader: scale = max(mergeRadius * 0.36, 0.01).
|
|
22
|
-
const MERGE_SCALE_RATIO = 0.36;
|
|
23
|
-
|
|
24
|
-
// Apple's folder corner is capped at 23.5% of the short side.
|
|
25
|
-
const MAX_CORNER_RATIO = 0.235;
|
|
26
|
-
|
|
27
|
-
export const SHAPE_TYPES = Object.freeze({ rect: 0, folder: 0, pill: 1, circle: 2 });
|
|
28
|
-
|
|
29
|
-
export function shapeTypeOf(shape) {
|
|
30
|
-
return SHAPE_TYPES[shape] ?? 0;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Corner radius the renderer will use for an element, before `dpr`. */
|
|
34
|
-
export function cornerRadiusOf(element, materialRadius = 0) {
|
|
35
|
-
const short = Math.min(element.w ?? element.width ?? 0, element.h ?? element.height ?? 0);
|
|
36
|
-
return Math.min(element.radius ?? materialRadius, short * MAX_CORNER_RATIO);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Superellipse rounded box, mirroring `sdSquircle` in the glass shader. */
|
|
40
|
-
export function sdSquircle(px, py, halfX, halfY, radius, exponent) {
|
|
41
|
-
const qx = Math.abs(px) - halfX + radius;
|
|
42
|
-
const qy = Math.abs(py) - halfY + radius;
|
|
43
|
-
const mx = Math.max(qx, 0) + 1e-5;
|
|
44
|
-
const my = Math.max(qy, 0) + 1e-5;
|
|
45
|
-
const e = (mx ** exponent + my ** exponent) ** (1 / exponent);
|
|
46
|
-
return Math.min(Math.max(qx, qy), 0) + e - radius;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Mirrors `sdPrimitive`: exact circle, exact capsule, or squircle folder. */
|
|
50
|
-
export function sdPrimitive(px, py, halfX, halfY, shapeType, radius, squircle = 2) {
|
|
51
|
-
if (shapeType === 2) return Math.hypot(px, py) - Math.min(halfX, halfY);
|
|
52
|
-
if (shapeType === 1) return sdSquircle(px, py, halfX, halfY, Math.min(halfX, halfY), 2);
|
|
53
|
-
return sdSquircle(px, py, halfX, halfY, radius, Math.max(squircle, 2));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function toShape(element, material) {
|
|
57
|
-
const w = element.w ?? element.width ?? element.size ?? 0;
|
|
58
|
-
const h = element.h ?? element.height ?? element.size ?? w;
|
|
59
|
-
return {
|
|
60
|
-
cx: (element.x ?? 0) + w / 2,
|
|
61
|
-
cy: (element.y ?? 0) + h / 2,
|
|
62
|
-
halfX: w / 2,
|
|
63
|
-
halfY: h / 2,
|
|
64
|
-
type: shapeTypeOf(element.shape),
|
|
65
|
-
radius: cornerRadiusOf({ ...element, w, h }, material.radius ?? 0),
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Signed distance to the fused silhouette of `elements`, in element
|
|
71
|
-
* coordinates. Mirrors `sdAppleShape`, including the global exponential
|
|
72
|
-
* smooth-min, so a hit test agrees with the pixels the shader produced.
|
|
73
|
-
*
|
|
74
|
-
* The smooth-min pulls the surface inward by at most `scale * ln(2)`, which is
|
|
75
|
-
* a quarter of `mergeRadius`. A gap between two components therefore only
|
|
76
|
-
* closes into a bridge while it is narrower than about `mergeRadius / 2`;
|
|
77
|
-
* beyond that the fusion distance only softens the approach.
|
|
78
|
-
*/
|
|
79
|
-
export function sdGroup(x, y, elements, material = {}, mergeRadius = material.mergeRadius ?? 0) {
|
|
80
|
-
if (!elements.length) return Infinity;
|
|
81
|
-
const shapes = elements.map((element) => toShape(element, material));
|
|
82
|
-
const squircle = material.squircle ?? 2;
|
|
83
|
-
|
|
84
|
-
let nearest = Infinity;
|
|
85
|
-
const distances = shapes.map((shape) => {
|
|
86
|
-
const d = sdPrimitive(x - shape.cx, y - shape.cy, shape.halfX, shape.halfY,
|
|
87
|
-
shape.type, shape.radius, squircle);
|
|
88
|
-
if (d < nearest) nearest = d;
|
|
89
|
-
return d;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
if (!(mergeRadius >= 0.01) || shapes.length === 1) return nearest;
|
|
93
|
-
|
|
94
|
-
const scale = Math.max(mergeRadius * MERGE_SCALE_RATIO, 0.01);
|
|
95
|
-
let sum = 0;
|
|
96
|
-
for (const d of distances) sum += Math.exp(-(d - nearest) / scale);
|
|
97
|
-
return nearest - scale * Math.log(Math.max(sum, 1e-6));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* The element whose own primitive is nearest to the point, or `null` when the
|
|
102
|
-
* point is outside the fused surface. `tolerance` grows the hit area, which is
|
|
103
|
-
* what a coarse pointer (touch) wants.
|
|
104
|
-
*/
|
|
105
|
-
export function hitTestElements(x, y, elements, material = {}, options = {}) {
|
|
106
|
-
const mergeRadius = options.fusion === false
|
|
107
|
-
? 0
|
|
108
|
-
: (options.mergeRadius ?? material.mergeRadius ?? 0);
|
|
109
|
-
const tolerance = options.tolerance ?? 0;
|
|
110
|
-
const squircle = material.squircle ?? 2;
|
|
111
|
-
|
|
112
|
-
// Separate elements are separate draw calls. The last one painted owns every
|
|
113
|
-
// overlapping pixel, regardless of how deeply the point lies inside an older
|
|
114
|
-
// element. Picking the most-negative distance here would make a large card
|
|
115
|
-
// steal clicks from a smaller button drawn on top of it.
|
|
116
|
-
if (options.fusion === false) {
|
|
117
|
-
for (let i = elements.length - 1; i >= 0; i--) {
|
|
118
|
-
const shape = toShape(elements[i], material);
|
|
119
|
-
const d = sdPrimitive(x - shape.cx, y - shape.cy, shape.halfX, shape.halfY,
|
|
120
|
-
shape.type, shape.radius, squircle);
|
|
121
|
-
if (d <= tolerance) return elements[i];
|
|
122
|
-
}
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Mirror the renderer's connected-component splitting and 16-shape chunks.
|
|
127
|
-
// In particular, do not invent a smooth-union bridge across a chunk boundary
|
|
128
|
-
// that the GPU cannot draw. Iterate backwards because later passes composite
|
|
129
|
-
// over earlier ones when separately rendered groups overlap.
|
|
130
|
-
const groups = groupElements(elements, mergeRadius, MAX_GLASS_SHAPES).groups;
|
|
131
|
-
for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex--) {
|
|
132
|
-
const group = groups[groupIndex];
|
|
133
|
-
if (sdGroup(x, y, group, material, mergeRadius) > tolerance) continue;
|
|
134
|
-
|
|
135
|
-
let best = null;
|
|
136
|
-
let bestDistance = Infinity;
|
|
137
|
-
for (let i = group.length - 1; i >= 0; i--) {
|
|
138
|
-
const shape = toShape(group[i], material);
|
|
139
|
-
const d = sdPrimitive(x - shape.cx, y - shape.cy, shape.halfX, shape.halfY,
|
|
140
|
-
shape.type, shape.radius, squircle);
|
|
141
|
-
if (d < bestDistance) {
|
|
142
|
-
bestDistance = d;
|
|
143
|
-
best = group[i];
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return best;
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Signed distance to the exact silhouette produced by the renderer's passes. */
|
|
152
|
-
export function sdRenderedGroups(
|
|
153
|
-
x, y, elements, material = {}, mergeRadius = material.mergeRadius ?? 0,
|
|
154
|
-
) {
|
|
155
|
-
const groups = groupElements(elements, mergeRadius, MAX_GLASS_SHAPES).groups;
|
|
156
|
-
if (!groups.length) return Infinity;
|
|
157
|
-
let bestDistance = Infinity;
|
|
158
|
-
for (const group of groups) {
|
|
159
|
-
const d = sdGroup(x, y, group, material, mergeRadius);
|
|
160
|
-
if (d < bestDistance) bestDistance = d;
|
|
161
|
-
}
|
|
162
|
-
return bestDistance;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Axis-aligned gap between two element boxes; 0 when they overlap. */
|
|
166
|
-
function boxGap(a, b) {
|
|
167
|
-
const box = (element) => {
|
|
168
|
-
const w = Number(element.w ?? element.width ?? element.size ?? 0);
|
|
169
|
-
const h = Number(element.h ?? element.height ?? element.size ?? w);
|
|
170
|
-
return { x: Number(element.x ?? 0), y: Number(element.y ?? 0), w, h };
|
|
171
|
-
};
|
|
172
|
-
const aa = box(a);
|
|
173
|
-
const bb = box(b);
|
|
174
|
-
const dx = Math.max(0, Math.max(aa.x - (bb.x + bb.w), bb.x - (aa.x + aa.w)));
|
|
175
|
-
const dy = Math.max(0, Math.max(aa.y - (bb.y + bb.h), bb.y - (aa.y + aa.h)));
|
|
176
|
-
return Math.hypot(dx, dy);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Split elements into sets that can be shaded independently.
|
|
181
|
-
*
|
|
182
|
-
* Two elements land in the same set when their boxes are close enough for the
|
|
183
|
-
* smooth-min to bridge them. Elements further apart than the influence radius
|
|
184
|
-
* contribute nothing measurable to each other's distance field, so drawing
|
|
185
|
-
* them in separate passes is visually identical to one fused pass.
|
|
186
|
-
*/
|
|
187
|
-
export function connectedElementGroups(elements, mergeRadius = 0) {
|
|
188
|
-
if (elements.length <= 1) return elements.length ? [elements.slice()] : [];
|
|
189
|
-
const reach = Math.max(0, mergeRadius) * MERGE_SCALE_RATIO * MERGE_INFLUENCE_SCALES;
|
|
190
|
-
|
|
191
|
-
const parent = elements.map((_, i) => i);
|
|
192
|
-
const find = (i) => {
|
|
193
|
-
while (parent[i] !== i) { parent[i] = parent[parent[i]]; i = parent[i]; }
|
|
194
|
-
return i;
|
|
195
|
-
};
|
|
196
|
-
for (let i = 0; i < elements.length; i++) {
|
|
197
|
-
for (let j = i + 1; j < elements.length; j++) {
|
|
198
|
-
if (boxGap(elements[i], elements[j]) <= reach) parent[find(i)] = find(j);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const byRoot = new Map();
|
|
203
|
-
elements.forEach((element, i) => {
|
|
204
|
-
const root = find(i);
|
|
205
|
-
if (!byRoot.has(root)) byRoot.set(root, []);
|
|
206
|
-
byRoot.get(root).push(element);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return [...byRoot.values()];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Connected sets, further chunked so no group exceeds the shader's uniform
|
|
214
|
-
* arrays. A chunked set is the only lossy case: shapes that really do influence
|
|
215
|
-
* each other end up in different passes, so callers should surface a warning
|
|
216
|
-
* when `groupElements` reports it.
|
|
217
|
-
*/
|
|
218
|
-
export function groupElements(elements, mergeRadius = 0, maxPerGroup = MAX_GLASS_SHAPES) {
|
|
219
|
-
const connected = connectedElementGroups(elements, mergeRadius);
|
|
220
|
-
const groups = [];
|
|
221
|
-
let truncated = false;
|
|
222
|
-
for (const group of connected) {
|
|
223
|
-
if (group.length > maxPerGroup) truncated = true;
|
|
224
|
-
for (let i = 0; i < group.length; i += maxPerGroup) {
|
|
225
|
-
groups.push(group.slice(i, i + maxPerGroup));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return { groups, truncated };
|
|
229
|
-
}
|