webwallgl 1.0.0-beta1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 oneincase <462534624@qq.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,173 @@
1
+ # WebWallGL — Wallpaper Engine scene renderer for the browser
2
+
3
+ **[简体中文](README.md) | [English](README.en.md)**
4
+
5
+ ## Introduction
6
+
7
+ WebWallGL is a browser-side renderer for Wallpaper Engine "scene" wallpapers: it faithfully replays workshop scene packages (scene.pkg) in WebGL, with layer effect chains, particles, 3D puppet bones, text widgets, script sandboxes, audio response and live user-property updates.
8
+
9
+ - Zero runtime dependencies, single-file delivery (ESM ~400KB / gzip ~126KB)
10
+ - Installable via npm or a &lt;script> CDN tag; multiple isolated instances per page
11
+ - This bench is the library's first consumer — everything you see here is public API
12
+
13
+ ## What can you build
14
+
15
+ WebWallGL is a 'wallpaper-compatible rendering core' — anywhere a "dynamic background" is needed, drop in a WebWallGL instance:
16
+
17
+ - Wallpaper apps: the core renderer for desktop wallpaper engines (Tauri / Electron / WebView shells), fully replaying WE workshop scenes
18
+ - Websites: animated backgrounds or full-screen hero sections for homepages, landing pages and product sites
19
+ - Dev tools: background layers for dashboards, monitoring walls, terminals and GUI launchers
20
+ - Background plugins: embedded render source for music visualizers, OBS backdrops, digital signage and widget engines
21
+
22
+ In one line: everything can be a wall. Your app provides a canvas and a source for the scene package — parsing, assembly, the render loop, script sandboxes, pointer and audio are all WebWallGL's job.
23
+
24
+ ## Installation
25
+
26
+ Three ways to include it — pick one:
27
+
28
+ ```
29
+ // 1) npm + bundler (recommended)
30
+ npm i webwallgl
31
+ import { mount, httpSource } from "webwallgl";
32
+ ```
33
+
34
+ ```
35
+ // 2) ESM CDN
36
+ import { mount, httpSource } from "https://cdn.example.com/webwallgl/1.0.0-beta1/webwallgl.min.mjs";
37
+ ```
38
+
39
+ ```
40
+ <!-- 3) UMD <script>: exposes the global WebWallGL -->
41
+ <script src="https://cdn.example.com/webwallgl/1.0.0-beta1/webwallgl.global.min.js"></script>
42
+ <script>
43
+ const { mount, httpSource } = WebWallGL;
44
+ </script>
45
+ ```
46
+
47
+ ## Quick start
48
+
49
+ One canvas plus one source is everything. mount() resolves after the first frame is actually drawn:
50
+
51
+ ```
52
+ <canvas id="wp" style="width:100%;height:400px"></canvas>
53
+
54
+ import { mount, httpSource } from "webwallgl";
55
+
56
+ const wp = await mount(document.querySelector("#wp"), {
57
+ source: httpSource("https://cdn.example.com/wallpapers/3122339805"),
58
+ fps: 60,
59
+ });
60
+
61
+ // The instance is ready to use after the first frame:
62
+ wp.pause();
63
+ wp.resume();
64
+ wp.setProperties({ schemecolor: "0.5 0.2 0.8" });
65
+ console.log("live fps:", wp.stats.fps);
66
+ ```
67
+
68
+ The canvas CSS size is the render size: the library aligns the backing store to clientWidth/clientHeight, and the aspect follows container resizes automatically — no manual resize handling.
69
+
70
+ ## Loading scenes: Source
71
+
72
+ The library makes only two network requests (scene.pkg and optional project.json), so the resource abstraction is one interface with three built-in implementations:
73
+
74
+ | Factory | Use case |
75
+ | --- | --- |
76
+ | `httpSource(baseUrl, init?)` | HTTP base URL; falls back through the three real layouts: scene.pkg → scenes/scene.pkg → gifscene.pkg |
77
+ | `fileSource(file, project?)` | A local .pkg from &lt;input type=file> or drag & drop |
78
+ | `bytesSource(pkg, project?, key?)` | Bytes already in hand (bundled, IndexedDB cache, custom transport) |
79
+
80
+ ```
81
+ // Local file: drop a scene.pkg into the page to preview it
82
+ input.addEventListener("change", () => {
83
+ const src = fileSource(input.files[0]);
84
+ mount(canvas, { source: src });
85
+ });
86
+ ```
87
+
88
+ - httpSource tries the root scene.pkg first and tolerates each fetch separately — WKWebView/custom protocols throw Failed to fetch instead of a 404; the wrong order breaks every wallpaper
89
+ - source.key feeds the library's parse cache: packages with the same key are parsed once (pause/property changes cost zero network)
90
+ - A missing project.json is normal: without a property table, fields fall back to the scene.json snapshot values
91
+
92
+ ## Mount options
93
+
94
+ | Option | Default | Description |
95
+ | --- | --- | --- |
96
+ | `source (required)` | `—` | See Source above |
97
+ | `fit` | `"cover"` | cover crops to fill / contain letterboxes / stretch distorts |
98
+ | `renderDpr` | `1` | Render DPR cap (effective = min(devicePixelRatio, renderDpr)); lower it to save VRAM |
99
+ | `fps` | `60` | FPS cap; skipped frames don't count into stats.fps, so drops are visible |
100
+ | `volume` | `0` | 0..1. Starts muted (autoplay policy); set a non-zero volume after ready |
101
+ | `autoplay` | `true` | When false, stays paused after mount |
102
+ | `properties` | `{}` | Initial user property overrides (keys are property names) |
103
+ | `pointer / audio / media` | `built-in` | Pointer follows the canvas; audio/media are deterministic sims; pass null to disable |
104
+ | `features` | `all on` | Debug switches: models / text / particles / effects / components |
105
+ | `onReady / onError / onDiagnostic` | `—` | Callback surface; can also subscribe later via instance.on() |
106
+
107
+ ## The SceneInstance API
108
+
109
+ | Member | Description |
110
+ | --- | --- |
111
+ | `pause() / resume() / paused` | Pause/resume. Never remounts the package: video/audio resume from where they were |
112
+ | `setFit(fit) / setFps(n) / setVolume(v)` | Live updates, no remount (the render loop reads them per frame) |
113
+ | `setRenderDpr(dpr)` | Changing DPR rebuilds the canvas; remounts internally (pkg cache hit, no re-download) |
114
+ | `setProperties(props)` | Live property updates: patches the property table / effect constants / script sandboxes in place, no re-fetch |
115
+ | `getProperties()` | The current flattened property value map |
116
+ | `load(source)` | Switch scenes reusing the same canvas and WebGL context; resolves after the first frame |
117
+ | `release() / restore()` | Free GL resources keeping the config (display sleep) / rebuild from the kept config |
118
+ | `destroy()` | Terminal: frees resources, unbinds listeners; the instance is dead afterwards |
119
+ | `stats / info` | Measured FPS ({fps, running}, zeroes out instead of freezing) / scene info (logical size, layer count, has models/particles/text) |
120
+ | `on(ev, fn)` | Subscribe to ready / error / diagnostic; returns an unsubscribe function |
121
+
122
+ ## Events & diagnostics
123
+
124
+ ```
125
+ const off = wp.on("diagnostic", (msg, level) => {
126
+ // level: "info" | "warn" | "error"
127
+ console[level === "error" ? "error" : "log"]("[wp]", msg);
128
+ });
129
+ wp.on("error", (err) => showError(err));
130
+ wp.on("ready", (info) => {
131
+ // info: { width, height, layerCount, hasModels, hasParticles, hasText }
132
+ });
133
+ ```
134
+
135
+ The library ships no fallback page and phones home nowhere: diagnostics and errors arrive via callbacks only. What to do on failure (show a hint, swap the wallpaper, destroy the instance) is your call.
136
+
137
+ ## Multiple instances per page
138
+
139
+ ```
140
+ const a = await mount(c1, { source: httpSource(urlA) });
141
+ const b = await mount(c2, { source: httpSource(urlB) });
142
+ b.pause(); // does not affect a
143
+ ```
144
+
145
+ - Each instance owns a Runtime: config, FPS meter, WebGL context and property table are fully isolated
146
+ - Pointer events bind to each instance's canvas; normalized coordinates are canvas-relative and never capture the page
147
+ - Mind WebGL context limits: browsers allow ~8–16 per page and drop the oldest beyond that
148
+
149
+ ## Lifecycle & caching
150
+
151
+ - Parsed scene.pkg entries are cached by source.key (up to 2): pause/resume, property changes and setRenderDpr remounts never re-download
152
+ - After release(), stats.running goes false and the FPS reading zeroes out — a stopped meter must not freeze on its last value
153
+ - After destroy() the canvas is yours again; mount() a fresh instance any time
154
+
155
+ ## Troubleshooting
156
+
157
+ - Black screen with WEBGL2_UNAVAILABLE: no WebGL2 in this environment; there is no software fallback
158
+ - HTTP 404: make sure the httpSource directory really contains a scene.pkg (all three layouts are tried before failing)
159
+ - Failed to fetch with no status: custom-protocol/WKWebView behavior for missing paths — by design; just read the final error
160
+ - stats.fps is 0 while the picture moves: the meter counts committed frames only; browsers suspend rAF for occluded tabs — expected
161
+ - Audio starts late: autoplay policy requires user interaction before sound; that's why volume defaults to 0
162
+
163
+ ## Copyright & compliance
164
+
165
+ The library code is MIT. Wallpaper Engine workshop assets (scene.pkg, textures, audio/video) remain copyrighted by their authors: point the library only at content you own or are licensed to use, and don't redistribute other people's work through your product.
166
+
167
+ ## Sponsor the author
168
+
169
+ If this rendering core helped your project, buying the author a coffee is always appreciated. Scan the QR code to sponsor — any amount counts.
170
+
171
+ | WeChat Pay | Alipay |
172
+ | --- | --- |
173
+ | ![WeChat Pay QR code](imgs/wechat.png) | ![Alipay QR code](imgs/alipay.png) |
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # WebWallGL —— 浏览器端 WE 场景壁纸渲染库
2
+
3
+ **[简体中文](README.md) | [English](README.en.md)**
4
+
5
+ ## 简介
6
+
7
+ WebWallGL 是一个浏览器端的 Wallpaper Engine「scene」场景壁纸渲染库:把创意工坊场景包(scene.pkg)在 WebGL 里实时还原,支持图层效果链、粒子、3D 木偶骨骼、文字挂件、脚本沙箱、音频响应与用户自定义属性热更新。
8
+
9
+ - 零运行时依赖,单文件引入(ESM 约 400KB / gzip 约 126KB)
10
+ - 可 npm 安装,也可 &lt;script> CDN 引入;一页可开多个互不干扰的实例
11
+ - 本测试台本身就是库的第一个使用者 —— 你在这里看到的能力都是公共 API
12
+
13
+ ## 能做什么
14
+
15
+ WebWallGL 是一个「壁纸兼容渲染核心」——凡是需要「动态背景」的地方,都可以塞一个 WebWallGL 实例进去:
16
+
17
+ - 壁纸软件:桌面壁纸引擎(Tauri / Electron / WebView 壳)的核心渲染器,完整还原 WE 创意工坊场景
18
+ - 网页:个人主页、落地页、产品官网的动态背景或全屏 hero 区块
19
+ - 各类代码工具:仪表盘、监控大屏、终端(如 GUI 启动器/开发工具)的背景层
20
+ - 背景插件:音乐播放器可视化、直播 OBS 背景板、数字标牌、kye/小部件引擎的嵌入渲染源
21
+
22
+ 一句话:万物皆可 Wall。你的应用只负责提供一块 canvas 和场景包的来源,剩下的解析、装配、渲染循环、脚本沙箱、指针与音频,全部交给 WebWallGL。
23
+
24
+ ## 安装
25
+
26
+ 三种引入方式,任选其一:
27
+
28
+ ```
29
+ // 1) npm + 打包器(推荐)
30
+ npm i webwallgl
31
+ import { mount, httpSource } from "webwallgl";
32
+ ```
33
+
34
+ ```
35
+ // 2) ESM CDN(vite/webpack 之外的直引方式)
36
+ import { mount, httpSource } from "https://cdn.example.com/webwallgl/1.0.0-beta1/webwallgl.min.mjs";
37
+ ```
38
+
39
+ ```
40
+ <!-- 3) UMD <script>:暴露全局 WebWallGL -->
41
+ <script src="https://cdn.example.com/webwallgl/1.0.0-beta1/webwallgl.global.min.js"></script>
42
+ <script>
43
+ const { mount, httpSource } = WebWallGL;
44
+ </script>
45
+ ```
46
+
47
+ ## 快速开始
48
+
49
+ 一个 canvas + 一个资源来源就是全部。mount() 在首帧真正画出来之后 resolve:
50
+
51
+ ```
52
+ <canvas id="wp" style="width:100%;height:400px"></canvas>
53
+
54
+ import { mount, httpSource } from "webwallgl";
55
+
56
+ const wp = await mount(document.querySelector("#wp"), {
57
+ source: httpSource("https://cdn.example.com/wallpapers/3122339805"),
58
+ fps: 60,
59
+ });
60
+
61
+ // 首帧之后实例即可用:
62
+ wp.pause();
63
+ wp.resume();
64
+ wp.setProperties({ schemecolor: "0.5 0.2 0.8" });
65
+ console.log("实测帧率", wp.stats.fps);
66
+ ```
67
+
68
+ 画布的 CSS 尺寸就是渲染尺寸:库把 backing store 对齐 clientWidth/clientHeight,容器改大小后画面宽高比自动跟随,不需要手动 resize。
69
+
70
+ ## 资源来源 Source
71
+
72
+ 库对网络只发两个请求(scene.pkg 与可选的 project.json),所以资源抽象只有一个接口、三个内置实现:
73
+
74
+ | 工厂 | 用途 |
75
+ | --- | --- |
76
+ | `httpSource(baseUrl, init?)` | HTTP 基址;自动按 scene.pkg → scenes/scene.pkg → gifscene.pkg 三种真实布局回退 |
77
+ | `fileSource(file, project?)` | &lt;input type=file> 或拖拽进来的 .pkg 本地文件 |
78
+ | `bytesSource(pkg, project?, key?)` | 已经拿到字节(bundle 内嵌、IndexedDB 缓存、自定义通道) |
79
+
80
+ ```
81
+ // 本地文件:拖一个 scene.pkg 进页面即可预览
82
+ input.addEventListener("change", () => {
83
+ const src = fileSource(input.files[0]);
84
+ mount(canvas, { source: src });
85
+ });
86
+ ```
87
+
88
+ - httpSource 必须先试根目录 scene.pkg,且每个 fetch 单独容错 —— WKWebView/自定义协议对缺失路径抛 Failed to fetch 而不是 404,顺序错了会「一片壁纸全坏」
89
+ - source.key 参与库内解析缓存:相同 key 的包不会重复解析(暂停/改属性零网络)
90
+ - project 缺失是常态:没有属性表时场景字段用 scene.json 内的快照值
91
+
92
+ ## 挂载选项 MountOptions
93
+
94
+ | 选项 | 默认 | 说明 |
95
+ | --- | --- | --- |
96
+ | `source(必填)` | `—` | 见上节 Source |
97
+ | `fit` | `"cover"` | cover 等比裁切铺满 / contain 等比留边 / stretch 拉伸 |
98
+ | `renderDpr` | `1` | 渲染 DPR 上限(实际取 min(devicePixelRatio, renderDpr)),调低省显存 |
99
+ | `fps` | `60` | 帧率上限;被上限跳过的帧不计入 stats.fps,掉帧一眼可见 |
100
+ | `volume` | `0` | 0..1。默认静音起步(浏览器自动播放策略);就绪后再设非零音量 |
101
+ | `autoplay` | `true` | false 时挂载后保持暂停 |
102
+ | `properties` | `{}` | 初始用户属性覆盖值(键为属性名) |
103
+ | `pointer / audio / media` | `内置` | 指针跟随 canvas、音频/系统媒体为确定性模拟;传 null 禁用 |
104
+ | `features` | `全开` | 调试开关:models / text / particles / effects / components |
105
+ | `onReady / onError / onDiagnostic` | `—` | 回调面;也可之后用 instance.on() 订阅 |
106
+
107
+ ## 实例 API SceneInstance
108
+
109
+ | 成员 | 说明 |
110
+ | --- | --- |
111
+ | `pause() / resume() / paused` | 暂停恢复。禁止整包重挂:恢复时视频/音频从暂停点继续 |
112
+ | `setFit(fit) / setFps(n) / setVolume(v)` | 热更新,无需重挂载(渲染循环每帧读取) |
113
+ | `setRenderDpr(dpr)` | 改 DPR 需重建画布,内部自动重挂(pkg 缓存命中,不重新下载) |
114
+ | `setProperties(props)` | 属性热更新:就地改属性表/效果常量/脚本沙箱,不重新拉包 |
115
+ | `getProperties()` | 当前生效的扁平化属性值表 |
116
+ | `load(source)` | 换场景,复用同一 canvas 与 WebGL 上下文;首帧后 resolve |
117
+ | `release() / restore()` | 释放显存但保留配置(显示器睡眠)/ 用保留的配置重建 |
118
+ | `destroy()` | 终态:释放资源、解绑监听,之后实例不可再用 |
119
+ | `stats / info` | 实测帧率({fps, running},停了会归零而不是冻住)/ 场景基本信息(逻辑分辨率、图层数、是否含模型/粒子/文字) |
120
+ | `on(ev, fn)` | 订阅 ready / error / diagnostic,返回取消函数 |
121
+
122
+ ## 事件与诊断
123
+
124
+ ```
125
+ const off = wp.on("diagnostic", (msg, level) => {
126
+ // level: "info" | "warn" | "error"
127
+ console[level === "error" ? "error" : "log"]("[wp]", msg);
128
+ });
129
+ wp.on("error", (err) => showError(err));
130
+ wp.on("ready", (info) => {
131
+ // info: { width, height, layerCount, hasModels, hasParticles, hasText }
132
+ });
133
+ ```
134
+
135
+ 库不自带降级页,也不向任何服务器上报:诊断与错误全部经回调交给你,渲染失败的兜底(提示、换壁纸、卸载实例)由调用方决定。
136
+
137
+ ## 一页多实例
138
+
139
+ ```
140
+ const a = await mount(c1, { source: httpSource(urlA) });
141
+ const b = await mount(c2, { source: httpSource(urlB) });
142
+ b.pause(); // 不影响 a
143
+ ```
144
+
145
+ - 每个实例持独立的 Runtime:配置、帧率计、WebGL 上下文、属性表互不可见
146
+ - 指针事件挂在各自的 canvas 上,归一化坐标相对画布,不会截获整页输入
147
+ - 注意 WebGL 上下文数量:浏览器一般允许同页 8~16 个,超出会丢最旧的上下文
148
+
149
+ ## 生命周期与缓存
150
+
151
+ - 解析后的 scene.pkg 按 source.key 缓存(最多 2 份):暂停恢复、改属性、setRenderDpr 重挂都不重新下载
152
+ - release() 后 stats.running 变 false、读数归零 —— 停住的读数不该冻在最后一个值上
153
+ - destroy() 之后 canvas 归还给你,库不再碰它;可以再 mount() 一个新实例
154
+
155
+ ## 故障排查
156
+
157
+ - 黑屏且 onError 报 WEBGL2_UNAVAILABLE:环境没有 WebGL2,库不做软件回退
158
+ - HTTP 404 加载失败:确认 httpSource 指向的目录里真的有 scene.pkg(三种布局会依次尝试,全部失败才报错)
159
+ - Failed to fetch 且无状态码:自定义协议/WKWebView 对缺失路径的行为,属正常容错路径,看最后一条错误即可
160
+ - stats.fps 为 0 但画面在动:读数是「真正提交渲染」的帧,标签页被遮挡时浏览器会暂停 rAF,属预期
161
+ - 有声音但延迟起播:自动播放策略要求用户交互后才允许出声,volume 默认 0 正是为此
162
+
163
+ ## 版权与合规
164
+
165
+ 库代码 MIT。Wallpaper Engine 创意工坊素材(scene.pkg、贴图、音视频)版权归各自作者所有:请仅指向你自己拥有或已获授权的素材,不要把他人作品打包进你的产品或公网分发。
166
+
167
+ ## 赞赏作者
168
+
169
+ 如果这个渲染核心帮到了你的项目,欢迎请作者喝杯咖啡。扫描二维码即可赞赏,金额随意。
170
+
171
+ | 微信支付 | 支付宝 |
172
+ | --- | --- |
173
+ | ![微信支付赞赏码](imgs/wechat.png) | ![支付宝赞赏码](imgs/alipay.png) |
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "webwallgl",
3
+ "version": "1.0.0-beta1",
4
+ "description": "Wallpaper Engine scene wallpaper renderer for the browser (npm / CDN)",
5
+ "license": "MIT",
6
+ "author": "oneincase <462534624@qq.com>",
7
+ "type": "module",
8
+ "keywords": [
9
+ "wallpaper-engine",
10
+ "wallpaper",
11
+ "webgl",
12
+ "renderer",
13
+ "scene",
14
+ "canvas",
15
+ "animated-background",
16
+ "particles",
17
+ "browser"
18
+ ],
19
+ "files": [
20
+ "webwallgl.mjs",
21
+ "webwallgl.min.mjs",
22
+ "webwallgl.global.js",
23
+ "webwallgl.global.min.js",
24
+ "*.map",
25
+ "webwallgl.d.ts",
26
+ "types.d.ts",
27
+ "imgs"
28
+ ],
29
+ "main": "./webwallgl.global.min.js",
30
+ "module": "./webwallgl.mjs",
31
+ "types": "./webwallgl.d.ts",
32
+ "unpkg": "./webwallgl.global.min.js",
33
+ "jsdelivr": "./webwallgl.global.min.js",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./webwallgl.d.ts",
37
+ "import": "./webwallgl.mjs",
38
+ "default": "./webwallgl.global.min.js"
39
+ }
40
+ },
41
+ "sideEffects": false
42
+ }
package/types.d.ts ADDED
@@ -0,0 +1,170 @@
1
+ /** 场景在 canvas 上的适配方式 */
2
+ export type Fit = "cover" | "contain" | "stretch";
3
+ /**
4
+ * WE 用户属性的取值。project.json 的 general.properties 里只出现这几种标量:
5
+ * bool → boolean,slider → number,combo/textinput/file/directory → string,
6
+ * color → "r g b"(0..1 浮点三元组,仍是 string)。
7
+ */
8
+ export type PropertyValue = boolean | number | string;
9
+ /** 场景资源来源。库对外只发两个请求,所以接口只有两个方法(见 LIBRARY-PLAN §4) */
10
+ export type Source = {
11
+ /**
12
+ * 场景容器(scene.pkg)字节。实现方只管给字节,解析由库负责。
13
+ * 抛错即视为该场景不可用,会走 onError。
14
+ */
15
+ scenePkg(signal?: AbortSignal): Promise<ArrayBuffer | Uint8Array>;
16
+ /**
17
+ * 属性表(project.json)。可选,且返回 null 合法 —— 真实壁纸库里大量场景
18
+ * 没有 project.json,此时场景字段一律用 scene.json 内的快照值。
19
+ */
20
+ project?(signal?: AbortSignal): Promise<unknown | null>;
21
+ /**
22
+ * 缓存键。相同键的 scene.pkg 命中库内缓存,避免重复解析上百 MB 的包
23
+ * (暂停恢复、改属性都不该重新走一遍解析)。省略则不参与缓存。
24
+ */
25
+ readonly key?: string;
26
+ };
27
+ /**
28
+ * 指针状态提供者。默认实现监听传入 canvas 自身的 pointer 事件;
29
+ * 传 null 则禁用指针交互(壁纸脚本读到的指针恒为静止居中)。
30
+ */
31
+ export type PointerSource = {
32
+ /** 归一化坐标 0..1,相对 canvas 左上角;y 向下 */
33
+ readonly x: number;
34
+ readonly y: number;
35
+ readonly leftDown: boolean;
36
+ readonly rightDown: boolean;
37
+ /** 指针是否在 canvas 内 */
38
+ readonly inside: boolean;
39
+ };
40
+ /**
41
+ * 音频频谱提供者(音频响应壁纸用)。返回当前快照,不推进状态 ——
42
+ * 推进由库的渲染循环按场景时间驱动,保证同一时刻取到同一份数据。
43
+ * 默认是内置的确定性模拟源(无需麦克风权限,离线可复现)。
44
+ */
45
+ export type AudioSource = {
46
+ /** 左右声道各 64 段频谱,值域 0..1 */
47
+ snapshot(): {
48
+ left: Float32Array | number[];
49
+ right: Float32Array | number[];
50
+ };
51
+ };
52
+ /**
53
+ * 系统媒体信息提供者("正在播放"类壁纸用)。默认是内置模拟源。
54
+ * 接真实数据时替换本接口即可,字段名与 WE 的 media* 回调一致。
55
+ */
56
+ export type MediaSource = {
57
+ snapshot(): {
58
+ playing: boolean;
59
+ title?: string;
60
+ artist?: string;
61
+ album?: string;
62
+ /** 0..1 播放进度 */
63
+ position?: number;
64
+ durationSeconds?: number;
65
+ };
66
+ };
67
+ /** 渲染开关。调试用,默认全开;对应旧 types.ts 的 SKIP_* 常量取反 */
68
+ export type FeatureFlags = {
69
+ /** puppet 骨骼网格(人物模型) */
70
+ models: boolean;
71
+ /** 文字对象(时钟/日期等挂件) */
72
+ text: boolean;
73
+ /** 粒子(雪/雨/火花/雾/光轴) */
74
+ particles: boolean;
75
+ /** 图层效果链 */
76
+ effects: boolean;
77
+ /** WE 内置组件对象(本机库 0 个,真出现时按开关处理) */
78
+ components: boolean;
79
+ };
80
+ /** 场景装配完成后的基本信息 */
81
+ export type SceneInfo = {
82
+ /** 场景逻辑分辨率(scene.json 的 general.orthogonalprojection) */
83
+ width: number;
84
+ height: number;
85
+ /** 图层数(已按 features 过滤后) */
86
+ layerCount: number;
87
+ /** 是否含 3D 模型 / 粒子 / 文字(便于调用方决定要不要降画质) */
88
+ hasModels: boolean;
89
+ hasParticles: boolean;
90
+ hasText: boolean;
91
+ };
92
+ /** 诊断级别。库内部所有 reportDiag 都归到这三档 */
93
+ export type DiagnosticLevel = "info" | "warn" | "error";
94
+ export type SceneEvents = {
95
+ ready: (info: SceneInfo) => void;
96
+ error: (err: Error) => void;
97
+ diagnostic: (msg: string, level: DiagnosticLevel) => void;
98
+ };
99
+ export type MountOptions = {
100
+ /** 资源来源(必填) */
101
+ source: Source;
102
+ /** 适配模式。默认 "cover" */
103
+ fit?: Fit;
104
+ /**
105
+ * 渲染分辨率上限(有效 devicePixelRatio 封顶),越低越省显存。默认 1。
106
+ * 实际使用 min(devicePixelRatio, renderDpr)。
107
+ */
108
+ renderDpr?: number;
109
+ /** 帧率上限。默认 60。渲染循环跳过比目标更快的帧,降低 GPU 占用 */
110
+ fps?: number;
111
+ /**
112
+ * 音量 0..1。默认 0(静音起步)—— 浏览器自动播放策略要求先有用户交互,
113
+ * 非静音起步会让整个场景的音频节点被挂起。
114
+ */
115
+ volume?: number;
116
+ /** 是否自动开始渲染循环。默认 true */
117
+ autoplay?: boolean;
118
+ /** 用户属性覆盖值(键为 project.json 里的属性名) */
119
+ properties?: Record<string, PropertyValue>;
120
+ /** 指针源。默认跟随 canvas 自身 pointer 事件;null = 禁用 */
121
+ pointer?: PointerSource | null;
122
+ /** 音频源。默认内置确定性模拟;null = 禁用(频谱恒为 0) */
123
+ audio?: AudioSource | null;
124
+ /** 系统媒体源。默认内置模拟;null = 禁用 */
125
+ media?: MediaSource | null;
126
+ /** 渲染开关(调试用) */
127
+ features?: Partial<FeatureFlags>;
128
+ /** 诊断回调。替代旧的 GET /diag 上报 */
129
+ onDiagnostic?: SceneEvents["diagnostic"];
130
+ /** 装配或渲染失败。库不自带降级页,由调用方决定怎么兜 */
131
+ onError?: SceneEvents["error"];
132
+ /** 首帧渲染完成 */
133
+ onReady?: SceneEvents["ready"];
134
+ };
135
+ /** 帧率观测。running=false 表示已暂停或没在出帧(读数会归零而不是冻住) */
136
+ export type FrameStats = {
137
+ fps: number;
138
+ running: boolean;
139
+ };
140
+ export type SceneInstance = {
141
+ /** 挂载目标(构造时传入的那个 canvas) */
142
+ readonly canvas: HTMLCanvasElement;
143
+ pause(): void;
144
+ resume(): void;
145
+ readonly paused: boolean;
146
+ setFit(fit: Fit): void;
147
+ setFps(fps: number): void;
148
+ setVolume(volume: number): void;
149
+ /** 改 DPR 需重建画布尺寸,内部自动重挂当前场景 */
150
+ setRenderDpr(dpr: number): void;
151
+ /**
152
+ * 用户属性热更新。就地改属性表 / 效果常量 / 脚本沙箱,
153
+ * **不重新拉取与解析 scene.pkg**(百 MB 包重挂是可感知的卡顿)。
154
+ */
155
+ setProperties(props: Record<string, PropertyValue>): void;
156
+ /** 当前生效的属性值(扁平化后的 name → value) */
157
+ getProperties(): Record<string, PropertyValue>;
158
+ /** 换场景,复用同一 canvas 与 WebGL 上下文 */
159
+ load(source: Source): Promise<void>;
160
+ /** 释放 GL/视频/音频资源,保留配置(显示器睡眠等场景) */
161
+ release(): void;
162
+ /** 用保留的配置重建 */
163
+ restore(): void;
164
+ /** 彻底销毁:解绑事件监听、释放全部资源,之后不可再用 */
165
+ destroy(): void;
166
+ readonly stats: FrameStats;
167
+ readonly info: SceneInfo | null;
168
+ /** 事件订阅,返回取消函数 */
169
+ on<K extends keyof SceneEvents>(ev: K, fn: SceneEvents[K]): () => void;
170
+ };
package/webwallgl.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ // WebWallGL 公共类型入口(随 dist 发布为 dist/lib/webwallgl.d.ts)。
2
+ //
3
+ // ⚠️ 维护约束:函数签名是**手写的契约面**,全部类型来自 api/types.ts
4
+ // (tsc 自动生成到 dist/lib/types,构建时由 build:lib 拷贝拼接)。
5
+ // 改 api/mount.ts / api/source.ts 的**函数签名**时必须同步本文件 ——
6
+ // 类型本体(MountOptions/SceneInstance/Source 等)改 api/types.ts 即可自动带出。
7
+
8
+ export {
9
+ type Fit,
10
+ type Source,
11
+ type MountOptions,
12
+ type SceneInstance,
13
+ type SceneInfo,
14
+ type SceneEvents,
15
+ type FrameStats,
16
+ type PropertyValue,
17
+ type FeatureFlags,
18
+ type PointerSource,
19
+ type AudioSource,
20
+ type MediaSource,
21
+ type DiagnosticLevel,
22
+ } from "./types";
23
+
24
+ export declare function mount(
25
+ canvas: HTMLCanvasElement,
26
+ options: MountOptions,
27
+ ): Promise<SceneInstance>;
28
+
29
+ export declare function createScene(
30
+ canvas: HTMLCanvasElement,
31
+ options?: Partial<Omit<MountOptions, "source">>,
32
+ ): SceneInstance;
33
+
34
+ export declare function httpSource(baseUrl: string, init?: RequestInit): Source;
35
+
36
+ export declare function fileSource(file: File | Blob, project?: unknown): Source;
37
+
38
+ export declare function bytesSource(
39
+ pkg: ArrayBuffer | Uint8Array,
40
+ project?: unknown,
41
+ key?: string,
42
+ ): Source;