@wangyaoshen/locus-player 0.1.17-dev → 0.1.27

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/dist/main.js CHANGED
@@ -1,291 +1,478 @@
1
- var v = Object.defineProperty;
2
- var m = (s, r, t) => r in s ? v(s, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[r] = t;
3
- var i = (s, r, t) => (m(s, typeof r != "symbol" ? r + "" : r, t), t);
4
- import { Stage as b, Player as f, Vector2 as g } from "@wangyaoshen/locus-core";
5
- const I = `.initial{display:none}.state-initial .initial{display:block}.loading{display:none}.state-loading .loading{display:block}.ready{display:none}.state-ready .ready{display:block}.error{display:none}.state-error .error{display:block}:host{position:relative;display:block}.overlay{position:absolute;left:0;right:0;top:0;bottom:0;display:flex;align-items:center;justify-content:center;opacity:0;background-color:#0000008a;transition:opacity .1s}.overlay.state-ready:not(.auto){cursor:pointer}.overlay.playing:not(.hover):hover{cursor:none}.overlay.hover,.overlay.state-ready:focus-within,.overlay.state-ready:not(.playing){opacity:1}.overlay.hover .button,.overlay.state-ready:focus-within .button,.overlay.state-ready:not(.playing) .button{scale:1;transition:scale .1s ease-out}.overlay.state-loading,.overlay.state-error{opacity:1;transition:opacity 1s}.overlay.state-ready.auto{opacity:0}.overlay.interactive-mode{display:none}.button{width:50%;max-width:96px;aspect-ratio:1;scale:.5;transition:scale .1s ease-in,opacity .1s;background-color:transparent;border:none;background-size:100% 100%;background-repeat:no-repeat;opacity:.54;cursor:inherit;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iI2ZmZmZmZiI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bS0yIDE0LjV2LTlsNiA0LjUtNiA0LjV6Ii8+PC9zdmc+)}.playing .button{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0cHgiIGZpbGw9IiNmZmZmZmYiPjxnPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjwvZz48Zz48Zy8+PHBhdGggZD0iTTEyLDJDNi40OCwyLDIsNi40OCwyLDEyczQuNDgsMTAsMTAsMTBzMTAtNC40OCwxMC0xMFMxNy41MiwyLDEyLDJ6IE0xMSwxNkg5VjhoMlYxNnogTTE1LDE2aC0yVjhoMlYxNnoiLz48L2c+PC9zdmc+)}.button:focus,.overlay:hover .button{opacity:.87}.auto .button{display:none}.canvas{width:100%;display:block;opacity:0;transition:opacity .1s}.canvas.state-ready{opacity:1}.message{font-family:JetBrains Mono,sans-serif;text-align:center;font-size:20px;padding:8px 16px;margin:16px;border-radius:4px;color:#fff9;background-color:#000000de}.loader{width:50%;max-width:96px;display:none;rotate:-90deg;animation:stroke 2s cubic-bezier(.5,0,.5,1) infinite,rotate 2s linear infinite}@keyframes stroke{0%{stroke-dasharray:5.6548667765px 50.8938009883px;stroke-dashoffset:2.8274333882px}50%{stroke-dasharray:50.8938009883px 5.6548667765px;stroke-dashoffset:-2.8274333882px}to{stroke-dasharray:5.6548667765px 50.8938009883px;stroke-dashoffset:-53.7212343766px}}@keyframes rotate{0%{rotate:-110deg}to{rotate:250deg}}
6
- `, w = `<div class="overlay" part="overlay">
7
- <button
8
- part="play-button"
9
- title="Play / Pause"
10
- class="button ready"
11
- tabindex="0"
12
- ></button>
13
- <div part="message" class="message error">
14
- An error occurred while loading the animation.
15
- </div>
16
- <svg
17
- part="loader"
18
- class="loader loading"
19
- viewBox="0 0 24 24"
20
- stroke="#ffffff"
21
- stroke-width="2"
22
- fill="transparent"
23
- >
24
- <circle cx="12" cy="12" r="9" />
25
- </svg>
26
- </div>
27
- `, M = `<style>${I}</style>${w}`, y = "motion-canvas-player";
28
- function p(s) {
29
- return s !== null && "transformMousePosition" in s && "getView" in s;
1
+ import { DefaultPlugin, Logger, Player, ProjectMetadata, Stage, ValueDispatcher, Vector2 } from "@wangyaoshen/locus-core";
2
+ var SwcModule = null, InitPromise = null, IMPORT_ALIASES = [
3
+ [/(['"])@motion-canvas\/core(['"])/g, "$1@locus/core$2"],
4
+ [/(['"])@motion-canvas\/2d(['"])/g, "$1@locus/2d$2"],
5
+ [/(['"])@motion-canvas\/2d\/lib(['"])/g, "$1@locus/2d$2"]
6
+ ];
7
+ function rewriteImportAliases(e) {
8
+ let S = e;
9
+ for (let [e, C] of IMPORT_ALIASES) S = S.replace(e, C);
10
+ return S;
30
11
  }
31
- function P(s) {
32
- return s !== null && typeof s == "object" && "containsPoint" in s && "startDrag" in s && "drag" in s && "endDrag" in s && "isDragging" in s && "interactive" in s;
12
+ async function initSwc() {
13
+ SwcModule || (InitPromise ||= (async () => {
14
+ try {
15
+ let e = await import("./wasm-CkwQS38J-CNLZprHF.js");
16
+ await e.default(), SwcModule = e;
17
+ } catch (e) {
18
+ throw InitPromise = null, Error(`Failed to initialize SWC: ${e}`);
19
+ }
20
+ })(), await InitPromise);
33
21
  }
34
- class k extends HTMLElement {
35
- // 标记是否刚完成交互操作
36
- constructor() {
37
- super();
38
- i(this, "root");
39
- i(this, "canvas");
40
- i(this, "overlay");
41
- i(this, "button");
42
- i(this, "state", "initial");
43
- i(this, "project", null);
44
- i(this, "player", null);
45
- i(this, "defaultSettings");
46
- i(this, "abortController", null);
47
- i(this, "mouseMoveId", null);
48
- i(this, "finished", !1);
49
- i(this, "playing", !1);
50
- i(this, "connected", !1);
51
- i(this, "stage", new b());
52
- // 交互状态
53
- i(this, "draggingElement", null);
54
- i(this, "interactiveElements", []);
55
- i(this, "wasInteracting", !1);
56
- i(this, "handleMouseMove", () => {
57
- this.mouseMoveId && clearTimeout(this.mouseMoveId), this.hover && !this.playing && this.setPlaying(!0), this.mouseMoveId = window.setTimeout(() => {
58
- this.mouseMoveId = null, this.updateClass();
59
- }, 2e3), this.updateClass();
60
- });
61
- i(this, "handleMouseLeave", () => {
62
- this.hover && this.setPlaying(!1), this.mouseMoveId && (clearTimeout(this.mouseMoveId), this.mouseMoveId = null, this.updateClass());
63
- });
64
- i(this, "handleMouseDown", (t) => {
65
- t.preventDefault();
66
- });
67
- i(this, "handleClick", () => {
68
- if (this.wasInteracting) {
69
- this.wasInteracting = !1;
70
- return;
71
- }
72
- this.auto || (this.handleMouseMove(), this.setPlaying(!this.playing), this.button.animate(
73
- [
74
- { scale: "0.9" },
75
- {
76
- scale: "1",
77
- easing: "ease-out"
78
- }
79
- ],
80
- { duration: 200 }
81
- ));
82
- });
83
- i(this, "render", async () => {
84
- this.player && await this.stage.render(
85
- this.player.playback.currentScene,
86
- this.player.playback.previousScene
87
- );
88
- });
89
- /**
90
- * 处理指针按下
91
- */
92
- i(this, "handlePointerDown", (t) => {
93
- if (!this.interactive || this.state !== "ready")
94
- return;
95
- const e = this.getScenePosition(t);
96
- if (!e)
97
- return;
98
- const a = this.findInteractiveElementAt(e);
99
- a && (t.preventDefault(), t.stopPropagation(), this.draggingElement = a, this.wasInteracting = !0, a.startDrag(), this.setPointerCapture(t.pointerId), this.style.cursor = "grabbing");
100
- });
101
- /**
102
- * 处理指针移动
103
- */
104
- i(this, "handlePointerMove", (t) => {
105
- if (!this.interactive || this.state !== "ready")
106
- return;
107
- const e = this.getScenePosition(t);
108
- if (e)
109
- if (this.draggingElement)
110
- t.preventDefault(), this.draggingElement.drag(e), this.render();
111
- else {
112
- const a = this.findInteractiveElementAt(e);
113
- this.style.cursor = a ? "grab" : "default";
114
- }
115
- });
116
- /**
117
- * 处理指针释放
118
- */
119
- i(this, "handlePointerUp", (t) => {
120
- this.interactive && this.draggingElement && (this.draggingElement.endDrag(), this.draggingElement = null, this.releasePointerCapture(t.pointerId), this.style.cursor = "default", this.render());
121
- });
122
- this.root = this.attachShadow({ mode: "open" }), this.root.innerHTML = M, this.overlay = this.root.querySelector(".overlay"), this.button = this.root.querySelector(".button"), this.canvas = this.stage.finalBuffer, this.canvas.classList.add("canvas"), this.root.prepend(this.canvas), this.overlay.addEventListener("click", this.handleClick), this.overlay.addEventListener("mousemove", this.handleMouseMove), this.overlay.addEventListener("mouseleave", this.handleMouseLeave), this.button.addEventListener("mousedown", this.handleMouseDown), this.addEventListener("pointerdown", this.handlePointerDown), this.addEventListener("pointermove", this.handlePointerMove), this.addEventListener("pointerup", this.handlePointerUp), this.addEventListener("pointercancel", this.handlePointerUp), this.setState(
123
- "initial"
124
- /* Initial */
125
- );
126
- }
127
- static get observedAttributes() {
128
- return [
129
- "src",
130
- "quality",
131
- "width",
132
- "height",
133
- "auto",
134
- "variables",
135
- "interactive"
136
- ];
137
- }
138
- get auto() {
139
- return !!this.getAttribute("auto");
140
- }
141
- get hover() {
142
- return this.getAttribute("auto") === "hover";
143
- }
144
- get interactive() {
145
- return this.hasAttribute("interactive");
146
- }
147
- get quality() {
148
- const t = this.getAttribute("quality");
149
- return t ? parseFloat(t) : this.defaultSettings.resolutionScale;
150
- }
151
- get width() {
152
- const t = this.getAttribute("width");
153
- return t ? parseFloat(t) : this.defaultSettings.size.width;
154
- }
155
- get height() {
156
- const t = this.getAttribute("height");
157
- return t ? parseFloat(t) : this.defaultSettings.size.height;
158
- }
159
- get variables() {
160
- try {
161
- const t = this.getAttribute("variables");
162
- return t ? JSON.parse(t) : {};
163
- } catch {
164
- return this.project.logger.warn("Project variables could not be parsed."), {};
165
- }
166
- }
167
- setState(t) {
168
- this.state = t, this.setPlaying(this.playing);
169
- }
170
- setPlaying(t) {
171
- var e, a;
172
- this.state === "ready" && (t || this.auto && !this.hover) ? ((e = this.player) == null || e.togglePlayback(!0), this.playing = !0) : ((a = this.player) == null || a.togglePlayback(!1), this.playing = !1), this.updateClass();
173
- }
174
- updateClass() {
175
- this.overlay.className = `overlay state-${this.state}`, this.canvas.className = `canvas state-${this.state}`, this.overlay.classList.toggle("playing", this.playing), this.overlay.classList.toggle("auto", this.auto), this.overlay.classList.toggle("hover", this.mouseMoveId !== null), this.overlay.classList.toggle("interactive-mode", this.interactive), this.connected && (this.mouseMoveId !== null || !this.playing ? this.dataset.overlay = "" : delete this.dataset.overlay);
176
- }
177
- async updateSource(t) {
178
- var n, o, h, c;
179
- this.setState(
180
- "initial"
181
- /* Initial */
182
- ), (n = this.abortController) == null || n.abort(), this.abortController = new AbortController();
183
- let e;
184
- try {
185
- const l = import(
186
- /* webpackIgnore: true */
187
- /* @vite-ignore */
188
- t
189
- ), u = new Promise((d) => setTimeout(d, 200));
190
- await Promise.any([u, l]), this.setState(
191
- "loading"
192
- /* Loading */
193
- ), e = (await l).default;
194
- } catch (l) {
195
- console.error(l), this.setState(
196
- "error"
197
- /* Error */
198
- );
199
- return;
200
- }
201
- this.defaultSettings = e.meta.getFullRenderingSettings();
202
- const a = new f(e);
203
- a.setVariables(this.variables), this.finished = !1, (o = this.player) == null || o.onRender.unsubscribe(this.render), (h = this.player) == null || h.togglePlayback(!1), (c = this.player) == null || c.deactivate(), this.project = e, this.player = a, this.updateSettings(), this.player.onRender.subscribe(this.render), this.player.togglePlayback(this.playing), this.setState(
204
- "ready"
205
- /* Ready */
206
- );
207
- }
208
- attributeChangedCallback(t, e, a) {
209
- var n;
210
- switch (t) {
211
- case "auto":
212
- this.setPlaying(this.playing);
213
- break;
214
- case "src":
215
- this.updateSource(a);
216
- break;
217
- case "quality":
218
- case "width":
219
- case "height":
220
- this.updateSettings();
221
- break;
222
- case "variables":
223
- (n = this.player) == null || n.setVariables(this.variables);
224
- break;
225
- case "interactive":
226
- this.updateInteractiveState();
227
- break;
228
- }
229
- }
230
- disconnectedCallback() {
231
- var t, e;
232
- this.connected = !1, (t = this.player) == null || t.deactivate(), (e = this.player) == null || e.onRender.unsubscribe(this.render);
233
- }
234
- connectedCallback() {
235
- var t, e;
236
- this.connected = !0, (t = this.player) == null || t.activate(), (e = this.player) == null || e.onRender.subscribe(this.render), this.updateInteractiveState();
237
- }
238
- updateSettings() {
239
- const t = {
240
- ...this.defaultSettings,
241
- size: new g(this.width, this.height),
242
- resolutionScale: this.quality
243
- };
244
- this.stage.configure(t), this.player.configure(t);
245
- }
246
- // ============ 交互系统 ============
247
- /**
248
- * 更新交互状态
249
- */
250
- updateInteractiveState() {
251
- this.interactive ? (this.overlay.classList.add("interactive-mode"), this.style.cursor = "default", this.refreshInteractiveElements()) : (this.overlay.classList.remove("interactive-mode"), this.style.cursor = "", this.interactiveElements = [], this.draggingElement = null);
252
- }
253
- /**
254
- * 刷新可交互元素列表
255
- */
256
- refreshInteractiveElements() {
257
- var e;
258
- this.interactiveElements = [];
259
- const t = (e = this.player) == null ? void 0 : e.playback.currentScene;
260
- if (p(t))
261
- try {
262
- const n = t.getView().findAll(P);
263
- this.interactiveElements = n.filter((o) => o.interactive());
264
- } catch {
265
- }
266
- }
267
- /**
268
- * 获取事件的场景坐标
269
- */
270
- getScenePosition(t) {
271
- var d;
272
- const e = (d = this.player) == null ? void 0 : d.playback.currentScene;
273
- if (!p(e))
274
- return null;
275
- const a = this.canvas.getBoundingClientRect(), n = this.width, o = this.height, h = (t.clientX - a.left) / a.width, c = (t.clientY - a.top) / a.height, l = (h - 0.5) * n, u = (c - 0.5) * o;
276
- return new g(l, u);
277
- }
278
- /**
279
- * 在指定位置查找可交互元素
280
- */
281
- findInteractiveElementAt(t) {
282
- this.refreshInteractiveElements();
283
- for (let e = this.interactiveElements.length - 1; e >= 0; e--) {
284
- const a = this.interactiveElements[e];
285
- if (a.containsPoint(t))
286
- return a;
287
- }
288
- return null;
289
- }
22
+ var initOxc = initSwc;
23
+ async function transform(e, S = {}) {
24
+ if (await initSwc(), !SwcModule) throw Error("SWC module not initialized");
25
+ let { jsxImportSource: C = "@locus/2d", filename: w = "input.tsx" } = S;
26
+ try {
27
+ return rewriteImportAliases(SwcModule.transformSync(e, {
28
+ filename: w,
29
+ jsc: {
30
+ parser: {
31
+ syntax: "typescript",
32
+ tsx: !0,
33
+ decorators: !0
34
+ },
35
+ transform: { react: {
36
+ runtime: "automatic",
37
+ importSource: C
38
+ } },
39
+ target: "es2022"
40
+ },
41
+ module: { type: "es6" },
42
+ sourceMaps: !1
43
+ }).code);
44
+ } catch (e) {
45
+ throw e instanceof Error ? Error(`Transform failed for ${w}: ${e.message}`) : Error(`Transform failed: ${e}`);
46
+ }
290
47
  }
291
- customElements.get(y) || customElements.define(y, k);
48
+ var BlobURLManager = class {
49
+ urls = /* @__PURE__ */ new Map();
50
+ maxAge;
51
+ maxCount;
52
+ cleanupTimer = null;
53
+ constructor(e = {}) {
54
+ this.maxAge = e.maxAge ?? 300 * 1e3, this.maxCount = e.maxCount ?? 50, this.startAutoCleanup();
55
+ }
56
+ create(e, S = "text/javascript") {
57
+ this.cleanup();
58
+ let C = new Blob([e], { type: S }), w = URL.createObjectURL(C);
59
+ return this.urls.set(w, {
60
+ url: w,
61
+ timestamp: Date.now(),
62
+ size: C.size
63
+ }), w;
64
+ }
65
+ release(e) {
66
+ return this.urls.has(e) ? (URL.revokeObjectURL(e), this.urls.delete(e), !0) : !1;
67
+ }
68
+ cleanup() {
69
+ let e = Date.now();
70
+ for (let [S, C] of this.urls) e - C.timestamp > this.maxAge && (URL.revokeObjectURL(S), this.urls.delete(S));
71
+ if (this.urls.size >= this.maxCount) {
72
+ let e = Array.from(this.urls.entries()).sort((e, S) => e[1].timestamp - S[1].timestamp).slice(0, this.urls.size - this.maxCount + 1);
73
+ for (let [S] of e) URL.revokeObjectURL(S), this.urls.delete(S);
74
+ }
75
+ }
76
+ get size() {
77
+ return this.urls.size;
78
+ }
79
+ get totalSize() {
80
+ let e = 0;
81
+ for (let S of this.urls.values()) e += S.size;
82
+ return e;
83
+ }
84
+ getStats() {
85
+ let e = Date.now(), S = e;
86
+ for (let e of this.urls.values()) e.timestamp < S && (S = e.timestamp);
87
+ return {
88
+ count: this.urls.size,
89
+ totalSize: this.totalSize,
90
+ oldestAge: e - S
91
+ };
92
+ }
93
+ startAutoCleanup() {
94
+ this.cleanupTimer ||= setInterval(() => {
95
+ this.cleanup();
96
+ }, 60 * 1e3);
97
+ }
98
+ stopAutoCleanup() {
99
+ this.cleanupTimer &&= (clearInterval(this.cleanupTimer), null);
100
+ }
101
+ dispose() {
102
+ this.stopAutoCleanup();
103
+ for (let [e] of this.urls) URL.revokeObjectURL(e);
104
+ this.urls.clear();
105
+ }
106
+ }, LocusCompiler = class {
107
+ worker = null;
108
+ messageId = 0;
109
+ pendingRequests = /* @__PURE__ */ new Map();
110
+ blobManager;
111
+ options;
112
+ initialized = !1;
113
+ constructor(e = {}) {
114
+ this.options = {
115
+ useWorker: e.useWorker ?? !0,
116
+ workerUrl: e.workerUrl ?? "",
117
+ jsxImportSource: e.jsxImportSource ?? "@locus/2d",
118
+ gc: e.gc ?? {}
119
+ }, this.blobManager = new BlobURLManager(this.options.gc);
120
+ }
121
+ async init() {
122
+ this.initialized ||= (this.options.useWorker ? await this.initWorker() : await initOxc(), !0);
123
+ }
124
+ async initWorker() {
125
+ return new Promise((e, S) => {
126
+ try {
127
+ if (this.options.workerUrl) this.worker = new Worker(this.options.workerUrl, { type: "module" });
128
+ else {
129
+ let e = new Blob(["\n import { initOxc, transformSync } from '@wangyaoshen/locus-compiler/transform';\n\n let initialized = false;\n\n self.onmessage = async (e) => {\n const { type, id, payload } = e.data;\n\n if (type === 'init') {\n try {\n await initOxc();\n initialized = true;\n self.postMessage({ type: 'ready', id, payload: { ready: true } });\n } catch (error) {\n self.postMessage({\n type: 'error',\n id,\n payload: { error: error.message }\n });\n }\n return;\n }\n\n if (type === 'compile') {\n const startTime = performance.now();\n try {\n if (!initialized) {\n await initOxc();\n initialized = true;\n }\n\n const { code } = payload;\n const result = transformSync(code);\n const duration = performance.now() - startTime;\n\n self.postMessage({\n type: 'result',\n id,\n payload: { success: true, code: result, duration }\n });\n } catch (error) {\n self.postMessage({\n type: 'result',\n id,\n payload: { success: false, error: error.message }\n });\n }\n }\n };\n "], { type: "text/javascript" }), S = URL.createObjectURL(e);
130
+ this.worker = new Worker(S, { type: "module" });
131
+ }
132
+ this.worker.onmessage = (S) => {
133
+ let { type: C, id: w, payload: T } = S.data;
134
+ if (C === "ready") {
135
+ e();
136
+ return;
137
+ }
138
+ let E = this.pendingRequests.get(w);
139
+ E && (this.pendingRequests.delete(w), C === "error" ? E.reject(Error(T.error)) : C === "result" && E.resolve(T));
140
+ }, this.worker.onerror = (e) => {
141
+ S(/* @__PURE__ */ Error(`Worker error: ${e.message}`));
142
+ }, this.sendMessage({
143
+ type: "init",
144
+ id: this.messageId++,
145
+ payload: void 0
146
+ });
147
+ } catch (e) {
148
+ S(e);
149
+ }
150
+ });
151
+ }
152
+ sendMessage(e) {
153
+ if (!this.worker) throw Error("Worker not initialized");
154
+ this.worker.postMessage(e);
155
+ }
156
+ async compile(e) {
157
+ this.initialized || await this.init();
158
+ let S = e.code || "";
159
+ return this.options.useWorker && this.worker ? this.compileWithWorker(S) : this.compileSync(S);
160
+ }
161
+ compileWithWorker(e) {
162
+ return new Promise((S, C) => {
163
+ let w = this.messageId++;
164
+ this.pendingRequests.set(w, {
165
+ resolve: S,
166
+ reject: C
167
+ }), this.sendMessage({
168
+ type: "compile",
169
+ id: w,
170
+ payload: { code: e }
171
+ });
172
+ });
173
+ }
174
+ async compileSync(e) {
175
+ let S = performance.now();
176
+ try {
177
+ return {
178
+ success: !0,
179
+ code: await transform(e, { jsxImportSource: this.options.jsxImportSource }),
180
+ duration: performance.now() - S
181
+ };
182
+ } catch (e) {
183
+ return {
184
+ success: !1,
185
+ error: e instanceof Error ? e.message : String(e),
186
+ duration: performance.now() - S
187
+ };
188
+ }
189
+ }
190
+ async compileToUrl(e) {
191
+ let S = await this.compile(e);
192
+ if (!S.success || !S.code) throw Error(S.error || "Compilation failed");
193
+ return this.blobManager.create(S.code);
194
+ }
195
+ releaseUrl(e) {
196
+ return this.blobManager.release(e);
197
+ }
198
+ getStats() {
199
+ return this.blobManager.getStats();
200
+ }
201
+ dispose() {
202
+ this.worker &&= (this.worker.terminate(), null), this.blobManager.dispose(), this.pendingRequests.clear(), this.initialized = !1;
203
+ }
204
+ }, DEFAULT_CDN = "https://esm.sh", NPM_SCOPE = "@wangyaoshen";
205
+ function generateImportMap(e = {}) {
206
+ let { version: S = "latest", cdn: C = DEFAULT_CDN, extra: w = {} } = e;
207
+ return { imports: {
208
+ "@locus/core": `${C}/${NPM_SCOPE}/locus-core@${S}`,
209
+ "@locus/core/": `${C}/${NPM_SCOPE}/locus-core@${S}/`,
210
+ "@locus/2d": `${C}/${NPM_SCOPE}/locus-2d@${S}`,
211
+ "@locus/2d/": `${C}/${NPM_SCOPE}/locus-2d@${S}/`,
212
+ "@locus/interaction": `${C}/${NPM_SCOPE}/locus-interaction@${S}`,
213
+ "@locus/interaction/": `${C}/${NPM_SCOPE}/locus-interaction@${S}/`,
214
+ ...w
215
+ } };
216
+ }
217
+ function injectImportMap(e) {
218
+ if (typeof document > "u") {
219
+ console.warn("injectImportMap: Not in browser environment");
220
+ return;
221
+ }
222
+ let S = document.querySelector("script[type=\"importmap\"]");
223
+ S && S.remove();
224
+ let C = generateImportMap(e), w = document.createElement("script");
225
+ w.type = "importmap", w.textContent = JSON.stringify(C, null, 2), document.head.prepend(w);
226
+ }
227
+ var TEMPLATE = "<style>.initial{display:none}.state-initial .initial{display:block}.loading{display:none}.state-loading .loading{display:block}.ready{display:none}.state-ready .ready{display:block}.error{display:none}.state-error .error{display:block}:host{display:block;position:relative}.overlay{opacity:0;background-color:#0000008a;justify-content:center;align-items:center;transition:opacity .1s;display:flex;position:absolute;inset:0}.overlay.state-ready:not(.auto){cursor:pointer}.overlay.playing:not(.hover):hover{cursor:none}.overlay.hover,.overlay.state-ready:focus-within,.overlay.state-ready:not(.playing){opacity:1}.overlay.hover .button,.overlay.state-ready:focus-within .button,.overlay.state-ready:not(.playing) .button{transition:scale .1s ease-out;scale:1}.overlay.state-loading,.overlay.state-error{opacity:1;transition:opacity 1s}.overlay.state-ready.auto{opacity:0}.overlay.interactive-mode{display:none}.button{aspect-ratio:1;opacity:.54;width:50%;max-width:96px;cursor:inherit;background-color:#0000;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iI2ZmZmZmZiI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bS0yIDE0LjV2LTlsNiA0LjUtNiA0LjV6Ii8+PC9zdmc+);background-repeat:no-repeat;background-size:100% 100%;border:none;transition:scale .1s ease-in,opacity .1s;scale:.5}.playing .button{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0cHgiIGZpbGw9IiNmZmZmZmYiPjxnPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjwvZz48Zz48Zy8+PHBhdGggZD0iTTEyLDJDNi40OCwyLDIsNi40OCwyLDEyczQuNDgsMTAsMTAsMTBzMTAtNC40OCwxMC0xMFMxNy41MiwyLDEyLDJ6IE0xMSwxNkg5VjhoMlYxNnogTTE1LDE2aC0yVjhoMlYxNnoiLz48L2c+PC9zdmc+)}.button:focus,.overlay:hover .button{opacity:.87}.auto .button{display:none}.canvas{opacity:0;width:100%;transition:opacity .1s;display:block}.canvas.state-ready{opacity:1}.message{text-align:center;color:#fff9;background-color:#000000de;border-radius:4px;margin:16px;padding:8px 16px;font-family:JetBrains Mono,sans-serif;font-size:20px}.loader{width:50%;max-width:96px;animation:2s cubic-bezier(.5,0,.5,1) infinite stroke,2s linear infinite rotate;display:none;rotate:-90deg}@keyframes stroke{0%{stroke-dasharray:5.65487 50.8938;stroke-dashoffset:2.82743px}50%{stroke-dasharray:50.8938 5.65487;stroke-dashoffset:-2.82743px}to{stroke-dasharray:5.65487 50.8938;stroke-dashoffset:-53.7212px}}@keyframes rotate{0%{rotate:-110deg}to{rotate:250deg}}</style><div class=\"overlay\" part=\"overlay\">\n <button\n part=\"play-button\"\n title=\"Play / Pause\"\n class=\"button ready\"\n tabindex=\"0\"\n ></button>\n <div part=\"message\" class=\"message error\">\n An error occurred while loading the animation.\n </div>\n <svg\n part=\"loader\"\n class=\"loader loading\"\n viewBox=\"0 0 24 24\"\n stroke=\"#ffffff\"\n stroke-width=\"2\"\n fill=\"transparent\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n </svg>\n</div>\n", ID = "locus-player", State = /* @__PURE__ */ function(e) {
228
+ return e.Initial = "initial", e.Loading = "loading", e.Ready = "ready", e.Error = "error", e;
229
+ }(State || {});
230
+ function isSceneDescription(e) {
231
+ return typeof e == "object" && !!e && "klass" in e && "config" in e && "stack" in e;
232
+ }
233
+ function isProject(e) {
234
+ return typeof e == "object" && !!e && "scenes" in e && Array.isArray(e.scenes);
235
+ }
236
+ function wrapSceneAsProject(C, T = "player-scene") {
237
+ C.onReplaced = new ValueDispatcher(C);
238
+ let D = {
239
+ name: T,
240
+ logger: new Logger(),
241
+ plugins: [DefaultPlugin()],
242
+ scenes: [C],
243
+ experimentalFeatures: !0
244
+ };
245
+ return D.meta = new ProjectMetadata(D), D.meta.shared.size.set([1920, 1080]), D;
246
+ }
247
+ function isScene2D(e) {
248
+ return e != null && "transformMousePosition" in e && "getView" in e;
249
+ }
250
+ function isInteractiveElement(e) {
251
+ return typeof e == "object" && !!e && "containsPoint" in e && "startDrag" in e && "drag" in e && "endDrag" in e && "isDragging" in e && "interactive" in e;
252
+ }
253
+ var LocusPlayer = class extends HTMLElement {
254
+ static get observedAttributes() {
255
+ return [
256
+ "src",
257
+ "code",
258
+ "quality",
259
+ "width",
260
+ "height",
261
+ "auto",
262
+ "variables",
263
+ "interactive"
264
+ ];
265
+ }
266
+ get auto() {
267
+ return !!this.getAttribute("auto");
268
+ }
269
+ get hover() {
270
+ return this.getAttribute("auto") === "hover";
271
+ }
272
+ get interactive() {
273
+ return this.hasAttribute("interactive");
274
+ }
275
+ get quality() {
276
+ let e = this.getAttribute("quality");
277
+ return e ? parseFloat(e) : this.defaultSettings?.resolutionScale ?? 1;
278
+ }
279
+ get width() {
280
+ let e = this.getAttribute("width");
281
+ return e ? parseFloat(e) : this.defaultSettings?.size?.width ?? 1920;
282
+ }
283
+ get height() {
284
+ let e = this.getAttribute("height");
285
+ return e ? parseFloat(e) : this.defaultSettings?.size?.height ?? 1080;
286
+ }
287
+ get variables() {
288
+ try {
289
+ let e = this.getAttribute("variables");
290
+ return e ? JSON.parse(e) : {};
291
+ } catch {
292
+ return this.project?.logger.warn("Project variables could not be parsed."), {};
293
+ }
294
+ }
295
+ root;
296
+ canvas;
297
+ overlay;
298
+ button;
299
+ state = State.Initial;
300
+ project = null;
301
+ player = null;
302
+ defaultSettings = null;
303
+ abortController = null;
304
+ mouseMoveId = null;
305
+ finished = !1;
306
+ playing = !1;
307
+ connected = !1;
308
+ stage = new Stage();
309
+ draggingElement = null;
310
+ interactiveElements = [];
311
+ wasInteracting = !1;
312
+ compiler = null;
313
+ currentBlobUrl = null;
314
+ importMapInjected = !1;
315
+ constructor() {
316
+ super(), this.root = this.attachShadow({ mode: "open" }), this.root.innerHTML = TEMPLATE, this.overlay = this.root.querySelector(".overlay"), this.button = this.root.querySelector(".button"), this.canvas = this.stage.finalBuffer, this.canvas.classList.add("canvas"), this.root.prepend(this.canvas), this.overlay.addEventListener("click", this.handleClick), this.overlay.addEventListener("mousemove", this.handleMouseMove), this.overlay.addEventListener("mouseleave", this.handleMouseLeave), this.button.addEventListener("mousedown", this.handleMouseDown), this.addEventListener("pointerdown", this.handlePointerDown), this.addEventListener("pointermove", this.handlePointerMove), this.addEventListener("pointerup", this.handlePointerUp), this.addEventListener("pointercancel", this.handlePointerUp), this.setState(State.Initial);
317
+ }
318
+ handleMouseMove = () => {
319
+ this.mouseMoveId && clearTimeout(this.mouseMoveId), this.hover && !this.playing && this.setPlaying(!0), this.mouseMoveId = window.setTimeout(() => {
320
+ this.mouseMoveId = null, this.updateClass();
321
+ }, 2e3), this.updateClass();
322
+ };
323
+ handleMouseLeave = () => {
324
+ this.hover && this.setPlaying(!1), this.mouseMoveId && (clearTimeout(this.mouseMoveId), this.mouseMoveId = null, this.updateClass());
325
+ };
326
+ handleMouseDown = (e) => {
327
+ e.preventDefault();
328
+ };
329
+ handleClick = () => {
330
+ if (this.wasInteracting) {
331
+ this.wasInteracting = !1;
332
+ return;
333
+ }
334
+ this.auto || (this.handleMouseMove(), this.setPlaying(!this.playing), this.button.animate([{ scale: "0.9" }, {
335
+ scale: "1",
336
+ easing: "ease-out"
337
+ }], { duration: 200 }));
338
+ };
339
+ setState(e) {
340
+ this.state = e, this.setPlaying(this.playing);
341
+ }
342
+ setPlaying(e) {
343
+ this.state === State.Ready && (e || this.auto && !this.hover) ? (this.player?.togglePlayback(!0), this.playing = !0) : (this.player?.togglePlayback(!1), this.playing = !1), this.updateClass();
344
+ }
345
+ updateClass() {
346
+ this.overlay.className = `overlay state-${this.state}`, this.canvas.className = `canvas state-${this.state}`, this.overlay.classList.toggle("playing", this.playing), this.overlay.classList.toggle("auto", this.auto), this.overlay.classList.toggle("hover", this.mouseMoveId !== null), this.overlay.classList.toggle("interactive-mode", this.interactive), this.connected && (this.mouseMoveId !== null || !this.playing ? this.dataset.overlay = "" : delete this.dataset.overlay);
347
+ }
348
+ async updateSource(e) {
349
+ this.setState(State.Initial), this.abortController?.abort(), this.abortController = new AbortController();
350
+ let S;
351
+ try {
352
+ let C = import(
353
+ /* webpackIgnore: true */
354
+ /* @vite-ignore */
355
+ e
356
+ ), w = new Promise((e) => setTimeout(e, 200));
357
+ await Promise.any([w, C]), this.setState(State.Loading), S = (await C).default;
358
+ } catch (e) {
359
+ console.error(e), this.setState(State.Error);
360
+ return;
361
+ }
362
+ this.defaultSettings = S.meta.getFullRenderingSettings();
363
+ let w = new Player(S);
364
+ w.setVariables(this.variables), this.finished = !1, this.player?.onRender.unsubscribe(this.render), this.player?.togglePlayback(!1), this.player?.deactivate(), this.project = S, this.player = w, this.updateSettings(), this.player.onRender.subscribe(this.render), this.player.togglePlayback(this.playing), this.setState(State.Ready);
365
+ }
366
+ async updateCode(e) {
367
+ if (e) {
368
+ this.setState(State.Initial), this.abortController?.abort(), this.abortController = new AbortController();
369
+ try {
370
+ this.importMapInjected ||= (document.querySelector("script[type=\"importmap\"]") || injectImportMap(), !0), this.compiler || (this.compiler = new LocusCompiler({
371
+ useWorker: !1,
372
+ jsxImportSource: "@locus/2d"
373
+ }), await this.compiler.init()), this.setState(State.Loading), this.currentBlobUrl &&= (this.compiler.releaseUrl(this.currentBlobUrl), null);
374
+ let S = await this.compiler.compileToUrl({ code: e });
375
+ this.currentBlobUrl = S;
376
+ let w = (await import(
377
+ /* webpackIgnore: true */
378
+ /* @vite-ignore */
379
+ S
380
+ )).default, T;
381
+ if (isProject(w)) T = w;
382
+ else if (isSceneDescription(w)) console.log("[LocusPlayer] Auto-wrapping Scene as Project"), T = wrapSceneAsProject(w);
383
+ else throw Error("Invalid export: expected a Project (from makeProject) or Scene (from makeScene2D)");
384
+ this.defaultSettings = T.meta.getFullRenderingSettings();
385
+ let E = new Player(T);
386
+ E.setVariables(this.variables), this.finished = !1, this.player?.onRender.unsubscribe(this.render), this.player?.togglePlayback(!1), this.player?.deactivate(), this.project = T, this.player = E, this.updateSettings(), this.player.onRender.subscribe(this.render), this.player.togglePlayback(this.playing), this.setState(State.Ready);
387
+ } catch (e) {
388
+ e instanceof Error ? (console.error("[LocusPlayer] Compilation error:", e.message), console.error("[LocusPlayer] Stack:", e.stack)) : console.error("[LocusPlayer] Unknown error:", e), this.setState(State.Error);
389
+ }
390
+ }
391
+ }
392
+ attributeChangedCallback(e, S, C) {
393
+ switch (e) {
394
+ case "auto":
395
+ this.setPlaying(this.playing);
396
+ break;
397
+ case "src":
398
+ this.updateSource(C);
399
+ break;
400
+ case "code":
401
+ this.updateCode(C);
402
+ break;
403
+ case "quality":
404
+ case "width":
405
+ case "height":
406
+ this.updateSettings();
407
+ break;
408
+ case "variables":
409
+ this.player?.setVariables(this.variables);
410
+ break;
411
+ case "interactive":
412
+ this.updateInteractiveState();
413
+ break;
414
+ }
415
+ }
416
+ disconnectedCallback() {
417
+ this.connected = !1, this.player?.deactivate(), this.player?.onRender.unsubscribe(this.render), this.currentBlobUrl && this.compiler && (this.compiler.releaseUrl(this.currentBlobUrl), this.currentBlobUrl = null), this.compiler &&= (this.compiler.dispose(), null);
418
+ }
419
+ connectedCallback() {
420
+ this.connected = !0, this.player?.activate(), this.player?.onRender.subscribe(this.render), this.updateInteractiveState();
421
+ }
422
+ render = async () => {
423
+ this.player && await this.stage.render(this.player.playback.currentScene, this.player.playback.previousScene);
424
+ };
425
+ updateSettings() {
426
+ if (!this.player || !this.defaultSettings) return;
427
+ let e = {
428
+ ...this.defaultSettings,
429
+ size: new Vector2(this.width, this.height),
430
+ resolutionScale: this.quality
431
+ };
432
+ this.project?.meta.shared.size.set([this.width, this.height]), this.stage.configure(e), this.player.configure(e);
433
+ }
434
+ updateInteractiveState() {
435
+ this.interactive ? (this.overlay.classList.add("interactive-mode"), this.style.cursor = "default", this.refreshInteractiveElements()) : (this.overlay.classList.remove("interactive-mode"), this.style.cursor = "", this.interactiveElements = [], this.draggingElement = null);
436
+ }
437
+ refreshInteractiveElements() {
438
+ this.interactiveElements = [];
439
+ let e = this.player?.playback.currentScene;
440
+ if (isScene2D(e)) try {
441
+ this.interactiveElements = e.getView().findAll(isInteractiveElement).filter((e) => e.interactive());
442
+ } catch {}
443
+ }
444
+ getScenePosition(e) {
445
+ let S = this.player?.playback.currentScene;
446
+ if (!isScene2D(S)) return null;
447
+ let C = this.canvas.getBoundingClientRect(), w = this.width, T = this.height, E = (e.clientX - C.left) / C.width, O = (e.clientY - C.top) / C.height;
448
+ return new Vector2((E - .5) * w, (O - .5) * T);
449
+ }
450
+ findInteractiveElementAt(e) {
451
+ this.refreshInteractiveElements();
452
+ for (let S = this.interactiveElements.length - 1; S >= 0; S--) {
453
+ let C = this.interactiveElements[S];
454
+ if (C.containsPoint(e)) return C;
455
+ }
456
+ return null;
457
+ }
458
+ handlePointerDown = (e) => {
459
+ if (!this.interactive || this.state !== State.Ready) return;
460
+ let S = this.getScenePosition(e);
461
+ if (!S) return;
462
+ let C = this.findInteractiveElementAt(S);
463
+ C && (e.preventDefault(), e.stopPropagation(), this.draggingElement = C, this.wasInteracting = !0, C.startDrag(), this.setPointerCapture(e.pointerId), this.style.cursor = "grabbing");
464
+ };
465
+ handlePointerMove = (e) => {
466
+ if (!this.interactive || this.state !== State.Ready) return;
467
+ let S = this.getScenePosition(e);
468
+ if (S) if (this.draggingElement) e.preventDefault(), this.draggingElement.drag(S), this.render();
469
+ else {
470
+ let e = this.findInteractiveElementAt(S);
471
+ this.style.cursor = e ? "grab" : "default";
472
+ }
473
+ };
474
+ handlePointerUp = (e) => {
475
+ this.interactive && this.draggingElement && (this.draggingElement.endDrag(), this.draggingElement = null, this.releasePointerCapture(e.pointerId), this.style.cursor = "default", this.render());
476
+ };
477
+ };
478
+ customElements.get(ID) || customElements.define(ID, LocusPlayer);