@viji-dev/core 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artist-dts-p5.js +1 -1
- package/dist/artist-dts.js +1 -1
- package/dist/artist-global-p5.d.ts +106 -48
- package/dist/artist-global.d.ts +106 -48
- package/dist/artist-jsdoc.d.ts +2 -6
- package/dist/assets/{viji.worker-BbzNOVhB.js → viji.worker-CWKkFyOs.js} +533 -168
- package/dist/assets/viji.worker-CWKkFyOs.js.map +1 -0
- package/dist/docs-api.js +9759 -9704
- package/dist/{essentia-wasm.web-qCUJ8zjr.js → essentia-wasm.web-CmPC-AKu.js} +2 -2
- package/dist/{essentia-wasm.web-qCUJ8zjr.js.map → essentia-wasm.web-CmPC-AKu.js.map} +1 -1
- package/dist/{index-Oj_9v8r4.js → index-Cp9G0z4E.js} +100 -8
- package/dist/index-Cp9G0z4E.js.map +1 -0
- package/dist/index.d.ts +106 -48
- package/dist/index.js +1 -1
- package/dist/shader-uniforms.js +10 -0
- package/package.json +1 -1
- package/dist/assets/viji.worker-BbzNOVhB.js.map +0 -1
- package/dist/index-Oj_9v8r4.js.map +0 -1
|
@@ -6,7 +6,7 @@ class VijiCoreError extends Error {
|
|
|
6
6
|
this.name = "VijiCoreError";
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
const inlineScriptCode = '"use strict";\n(() => {\n // src/iframe/protocol.ts\n var PROTOCOL_VERSION = 1;\n function collectTransferables(msg) {\n switch (msg.type) {\n case "init": {\n const m = msg;\n const out = [];\n if (m.data?.canvas) out.push(m.data.canvas);\n const wasm = m.data?.cvWasmFiles;\n if (wasm) {\n out.push(\n wasm.simdLoaderJs,\n wasm.simdBinary,\n wasm.nosimdLoaderJs,\n wasm.nosimdBinary\n );\n }\n return out;\n }\n case "video-canvas-setup": {\n const m = msg;\n return m.data?.offscreenCanvas ? [m.data.offscreenCanvas] : [];\n }\n case "video-frame-update": {\n const m = msg;\n return m.data?.imageBitmap ? [m.data.imageBitmap] : [];\n }\n case "video-frame-direct": {\n const m = msg;\n return m.data?.imageBitmap ? [m.data.imageBitmap] : [];\n }\n case "parameter-update": {\n const m = msg;\n return m.data?.transferList ?? [];\n }\n case "parameter-batch-update": {\n const m = msg;\n return m.data?.transferList ?? [];\n }\n case "audio-analysis-update": {\n const m = msg;\n const out = [];\n if (m.data?.frequencyData?.buffer) out.push(m.data.frequencyData.buffer);\n if (m.data?.waveformData?.buffer) out.push(m.data.waveformData.buffer);\n return out;\n }\n case "capture-frame-result":\n case "auto-capture-result": {\n const data = msg.data;\n return data?.bitmap ? [data.bitmap] : [];\n }\n default:\n return [];\n }\n }\n\n // src/iframe/inline-script.ts\n var hostOrigin = null;\n var hostWindow = null;\n var worker = null;\n var workerBlobUrl = null;\n var canvas = null;\n var domHandlersAttached = false;\n var sensorHandlersAttached = false;\n function whenDomReady() {\n if (document.readyState === "loading") {\n return new Promise((resolve) => {\n document.addEventListener("DOMContentLoaded", () => resolve(), {\n once: true\n });\n });\n }\n return Promise.resolve();\n }\n async function announceReady() {\n await whenDomReady();\n const msg = {\n type: "iframe-ready",\n protocolVersion: PROTOCOL_VERSION,\n environment: {\n crossOriginIsolated: window.crossOriginIsolated,\n isSecureContext: window.isSecureContext,\n origin: self.origin\n }\n };\n window.parent.postMessage(msg, "*");\n }\n function installHostListener() {\n window.addEventListener("message", (event) => {\n if (event.source !== window.parent) return;\n if (hostOrigin === null) {\n hostOrigin = event.origin;\n hostWindow = window.parent;\n } else if (event.origin !== hostOrigin) {\n return;\n }\n handleHostMessage(event.data).catch((err) => {\n reportError(err);\n });\n });\n }\n function postToHost(msg, transfer = []) {\n if (hostOrigin === null || hostWindow === null) {\n return;\n }\n const targetOrigin = hostOrigin === "null" ? "*" : hostOrigin;\n hostWindow.postMessage(msg, targetOrigin, transfer);\n }\n function reportError(err) {\n const e = err;\n const msg = e?.stack ? {\n type: "iframe-error",\n message: e.message ?? String(err),\n stack: e.stack\n } : {\n type: "iframe-error",\n message: e?.message ?? String(err)\n };\n emitToHost(msg);\n }\n function reportWorkerOnError(e) {\n const msg = {\n type: "iframe-error",\n message: e.message ? `Worker error: ${e.message}` : "Worker error: (no detail \\u2014 likely cross-origin opacity)",\n ...e.error && e.error.stack ? { stack: e.error.stack } : {},\n ...e.filename ? { filename: e.filename } : {},\n ...e.lineno ? { lineno: e.lineno } : {},\n ...e.colno ? { colno: e.colno } : {}\n };\n emitToHost(msg);\n }\n function emitToHost(msg) {\n if (hostOrigin && hostWindow) {\n const targetOrigin = hostOrigin === "null" ? "*" : hostOrigin;\n hostWindow.postMessage(msg, targetOrigin);\n } else {\n window.parent.postMessage(msg, "*");\n }\n }\n async function handleHostMessage(msg) {\n switch (msg.type) {\n case "viji-bootstrap":\n await handleBootstrap(msg);\n return;\n case "viji-host-message":\n relayHostToWorker(msg.payload);\n return;\n case "viji-resize":\n handleResize(msg.width, msg.height);\n return;\n case "viji-enable-sensors":\n setSensorsEnabled(msg.enabled);\n return;\n case "viji-enable-interaction":\n setInteractionEnabled(msg.enabled);\n return;\n case "viji-terminate":\n handleTerminate();\n return;\n }\n }\n async function handleBootstrap(msg) {\n if (worker) {\n return;\n }\n canvas = document.createElement("canvas");\n canvas.id = "viji-canvas";\n canvas.width = msg.init.canvasWidth;\n canvas.height = msg.init.canvasHeight;\n canvas.style.width = "100%";\n canvas.style.height = "100%";\n canvas.style.display = "block";\n canvas.tabIndex = 0;\n canvas.style.outline = "none";\n document.body.appendChild(canvas);\n const offscreen = canvas.transferControlToOffscreen();\n workerBlobUrl = URL.createObjectURL(\n new Blob([msg.workerCode], { type: "application/javascript" })\n );\n worker = new Worker(workerBlobUrl, { type: "classic" });\n worker.onerror = (e) => {\n reportWorkerOnError(e);\n };\n worker.onmessage = (event) => {\n relayWorkerToHost(event.data);\n };\n const initId = `init_${Date.now()}`;\n const initMsg = {\n type: "init",\n id: initId,\n timestamp: Date.now(),\n data: {\n canvas: offscreen,\n isHeadless: msg.init.isHeadless,\n sc: msg.init.sc,\n rh: msg.init.rh,\n cvWasmFiles: msg.wasmFiles\n }\n };\n const initResponse = waitForWorkerMessage((m) => {\n return m && m.type === "init-response" && m.id === initId;\n });\n worker.postMessage(initMsg, [\n offscreen,\n msg.wasmFiles.simdLoaderJs,\n msg.wasmFiles.simdBinary,\n msg.wasmFiles.nosimdLoaderJs,\n msg.wasmFiles.nosimdBinary\n ]);\n await initResponse;\n if (msg.init.allowInteraction && !msg.init.isHeadless) {\n setInteractionEnabled(true);\n }\n if (msg.init.allowSensors && !msg.init.isHeadless) {\n setSensorsEnabled(true);\n }\n if (canvas) {\n canvas.addEventListener("contextmenu", (e) => e.preventDefault());\n }\n postToHost({ type: "viji-ready" });\n }\n function waitForWorkerMessage(predicate) {\n return new Promise((resolve, reject) => {\n if (!worker) {\n reject(new Error("worker not spawned"));\n return;\n }\n const w = worker;\n const onceListener = (event) => {\n if (predicate(event.data)) {\n w.removeEventListener("message", onceListener);\n resolve(event.data);\n }\n };\n w.addEventListener("message", onceListener);\n });\n }\n function relayHostToWorker(payload) {\n if (!worker || !payload) return;\n try {\n const transfer = collectTransferables(payload);\n worker.postMessage(payload, transfer);\n } catch (err) {\n reportError(err);\n }\n }\n function relayWorkerToHost(payload) {\n if (!payload) return;\n try {\n const transfer = collectTransferables(payload);\n postToHost(\n { type: "worker-message", payload },\n transfer\n );\n } catch (err) {\n reportError(err);\n }\n }\n function handleResize(width, height) {\n if (!worker) return;\n worker.postMessage({\n type: "resolution-update",\n id: `resize_${Date.now()}`,\n timestamp: Date.now(),\n data: {\n effectiveWidth: width,\n effectiveHeight: height,\n displayScale: 1\n }\n });\n }\n var isMouseInCanvas = false;\n function setInteractionEnabled(enabled) {\n if (enabled === domHandlersAttached) return;\n if (!canvas) return;\n if (enabled) {\n canvas.addEventListener("mousedown", handleMouseEvent, { passive: false });\n canvas.addEventListener("mousemove", handleMouseEvent, { passive: false });\n canvas.addEventListener("mouseup", handleMouseEvent, { passive: false });\n canvas.addEventListener("mouseenter", handleMouseEnter, { passive: false });\n canvas.addEventListener("mouseleave", handleMouseLeave, { passive: false });\n canvas.addEventListener("wheel", handleWheelEvent, { passive: false });\n canvas.addEventListener("touchstart", handleTouchEvent, { passive: false });\n canvas.addEventListener("touchmove", handleTouchEvent, { passive: false });\n canvas.addEventListener("touchend", handleTouchEvent, { passive: false });\n canvas.addEventListener("touchcancel", handleTouchEvent, { passive: false });\n document.addEventListener("keydown", handleKeyboardEvent, { passive: false });\n document.addEventListener("keyup", handleKeyboardEvent, { passive: false });\n canvas.addEventListener("mousedown", focusCanvas);\n canvas.addEventListener("touchstart", focusCanvas);\n domHandlersAttached = true;\n } else {\n canvas.removeEventListener("mousedown", handleMouseEvent);\n canvas.removeEventListener("mousemove", handleMouseEvent);\n canvas.removeEventListener("mouseup", handleMouseEvent);\n canvas.removeEventListener("mouseenter", handleMouseEnter);\n canvas.removeEventListener("mouseleave", handleMouseLeave);\n canvas.removeEventListener("wheel", handleWheelEvent);\n canvas.removeEventListener("touchstart", handleTouchEvent);\n canvas.removeEventListener("touchmove", handleTouchEvent);\n canvas.removeEventListener("touchend", handleTouchEvent);\n canvas.removeEventListener("touchcancel", handleTouchEvent);\n document.removeEventListener("keydown", handleKeyboardEvent);\n document.removeEventListener("keyup", handleKeyboardEvent);\n canvas.removeEventListener("mousedown", focusCanvas);\n canvas.removeEventListener("touchstart", focusCanvas);\n domHandlersAttached = false;\n }\n }\n function focusCanvas() {\n if (canvas) canvas.focus();\n }\n function dualRoute(workerType, data) {\n if (worker) {\n worker.postMessage({\n type: workerType,\n id: `${workerType}_${Date.now()}`,\n timestamp: Date.now(),\n data\n });\n }\n postToHost({\n type: "interaction-event",\n kind: workerType,\n data\n });\n }\n function handleMouseEvent(event) {\n if (!canvas) return;\n event.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const x = (event.clientX - rect.left) * (canvas.width / rect.width);\n const y = (event.clientY - rect.top) * (canvas.height / rect.height);\n dualRoute("mouse-update", {\n x,\n y,\n buttons: event.buttons,\n deltaX: event.movementX || 0,\n deltaY: event.movementY || 0,\n wheelDeltaX: 0,\n wheelDeltaY: 0,\n isInCanvas: isMouseInCanvas,\n timestamp: performance.now()\n });\n }\n function handleMouseEnter(event) {\n isMouseInCanvas = true;\n handleMouseEvent(event);\n }\n function handleMouseLeave(event) {\n isMouseInCanvas = false;\n handleMouseEvent(event);\n }\n function handleWheelEvent(event) {\n if (!canvas) return;\n event.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const x = (event.clientX - rect.left) * (canvas.width / rect.width);\n const y = (event.clientY - rect.top) * (canvas.height / rect.height);\n dualRoute("mouse-update", {\n x,\n y,\n buttons: event.buttons,\n deltaX: 0,\n deltaY: 0,\n wheelDeltaX: event.deltaX,\n wheelDeltaY: event.deltaY,\n isInCanvas: isMouseInCanvas,\n timestamp: performance.now()\n });\n }\n var KEY_PASSTHROUGH = /* @__PURE__ */ new Set([\n "Tab",\n "F1",\n "F2",\n "F3",\n "F4",\n "F5",\n "F11",\n "F12"\n ]);\n function handleKeyboardEvent(event) {\n if (!KEY_PASSTHROUGH.has(event.key)) {\n event.preventDefault();\n }\n dualRoute("keyboard-update", {\n type: event.type,\n key: event.key,\n code: event.code,\n keyCode: event.keyCode,\n shiftKey: event.shiftKey,\n ctrlKey: event.ctrlKey,\n altKey: event.altKey,\n metaKey: event.metaKey,\n timestamp: performance.now()\n });\n }\n function handleTouchEvent(event) {\n if (!canvas) return;\n event.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const scaleX = canvas.width / rect.width;\n const scaleY = canvas.height / rect.height;\n const canvasW = canvas.width;\n const canvasH = canvas.height;\n const mapTouch = (t) => {\n const cx = (t.clientX - rect.left) * scaleX;\n const cy = (t.clientY - rect.top) * scaleY;\n return {\n identifier: t.identifier,\n clientX: cx,\n clientY: cy,\n radiusX: t.radiusX ?? 0,\n radiusY: t.radiusY ?? 0,\n rotationAngle: t.rotationAngle ?? 0,\n force: t.force ?? 0,\n isInCanvas: cx >= 0 && cx <= canvasW && cy >= 0 && cy <= canvasH\n };\n };\n const changedMap = /* @__PURE__ */ new Map();\n for (let i = 0; i < event.changedTouches.length; i++) {\n const t = event.changedTouches[i];\n changedMap.set(t.identifier, mapTouch(t));\n }\n const touches = Array.from(event.touches).map((t) => {\n return changedMap.get(t.identifier) ?? mapTouch(t);\n });\n if (event.type === "touchend" || event.type === "touchcancel") {\n const activeIds = new Set(touches.map((t) => t.identifier));\n for (const [id, mapped] of changedMap) {\n if (!activeIds.has(id)) {\n touches.push({ ...mapped, ended: true });\n }\n }\n }\n dualRoute("touch-update", {\n type: event.type,\n touches,\n timestamp: performance.now()\n });\n }\n function setSensorsEnabled(enabled) {\n if (enabled === sensorHandlersAttached) return;\n if (enabled) {\n window.addEventListener("devicemotion", handleDeviceMotion);\n window.addEventListener("deviceorientation", handleDeviceOrientation);\n sensorHandlersAttached = true;\n } else {\n window.removeEventListener("devicemotion", handleDeviceMotion);\n window.removeEventListener("deviceorientation", handleDeviceOrientation);\n sensorHandlersAttached = false;\n }\n }\n function handleDeviceMotion(event) {\n if (!event.acceleration && !event.accelerationIncludingGravity && !event.rotationRate) {\n return;\n }\n postToHost({\n type: "sensor-event",\n kind: "motion",\n data: {\n acceleration: event.acceleration ? {\n x: event.acceleration.x,\n y: event.acceleration.y,\n z: event.acceleration.z\n } : null,\n accelerationIncludingGravity: event.accelerationIncludingGravity ? {\n x: event.accelerationIncludingGravity.x,\n y: event.accelerationIncludingGravity.y,\n z: event.accelerationIncludingGravity.z\n } : null,\n rotationRate: event.rotationRate ? {\n alpha: event.rotationRate.alpha,\n beta: event.rotationRate.beta,\n gamma: event.rotationRate.gamma\n } : null,\n interval: event.interval || 16\n }\n });\n }\n function handleDeviceOrientation(event) {\n postToHost({\n type: "sensor-event",\n kind: "orientation",\n data: {\n alpha: event.alpha,\n beta: event.beta,\n gamma: event.gamma,\n absolute: event.absolute || false\n }\n });\n }\n function handleTerminate() {\n setInteractionEnabled(false);\n setSensorsEnabled(false);\n if (worker) {\n worker.terminate();\n worker = null;\n }\n if (workerBlobUrl) {\n URL.revokeObjectURL(workerBlobUrl);\n workerBlobUrl = null;\n }\n }\n installHostListener();\n announceReady().catch((err) => reportError(err));\n window.addEventListener("error", (e) => {\n reportError(e.error ?? new Error(e.message));\n });\n window.addEventListener("unhandledrejection", (e) => {\n reportError(e.reason ?? new Error("Unhandled promise rejection"));\n });\n})();\n';
|
|
9
|
+
const inlineScriptCode = '"use strict";\n(() => {\n // src/iframe/protocol.ts\n var PROTOCOL_VERSION = 1;\n function collectTransferables(msg) {\n switch (msg.type) {\n case "init": {\n const m = msg;\n const out = [];\n if (m.data?.canvas) out.push(m.data.canvas);\n const wasm = m.data?.cvWasmFiles;\n if (wasm) {\n out.push(\n wasm.simdLoaderJs,\n wasm.simdBinary,\n wasm.nosimdLoaderJs,\n wasm.nosimdBinary\n );\n }\n if (m.data?.controlPort) out.push(m.data.controlPort);\n return out;\n }\n case "video-canvas-setup": {\n const m = msg;\n return m.data?.offscreenCanvas ? [m.data.offscreenCanvas] : [];\n }\n case "video-frame-update": {\n const m = msg;\n return m.data?.imageBitmap ? [m.data.imageBitmap] : [];\n }\n case "video-frame-direct": {\n const m = msg;\n return m.data?.imageBitmap ? [m.data.imageBitmap] : [];\n }\n case "parameter-update": {\n const m = msg;\n return m.data?.transferList ?? [];\n }\n case "parameter-batch-update": {\n const m = msg;\n return m.data?.transferList ?? [];\n }\n case "audio-analysis-update": {\n const m = msg;\n const out = [];\n if (m.data?.frequencyData?.buffer) out.push(m.data.frequencyData.buffer);\n if (m.data?.waveformData?.buffer) out.push(m.data.waveformData.buffer);\n return out;\n }\n case "capture-frame-result":\n case "auto-capture-result": {\n const data = msg.data;\n return data?.bitmap ? [data.bitmap] : [];\n }\n default:\n return [];\n }\n }\n\n // src/iframe/inline-script.ts\n var hostOrigin = null;\n var hostWindow = null;\n var worker = null;\n var workerBlobUrl = null;\n var canvas = null;\n var domHandlersAttached = false;\n var sensorHandlersAttached = false;\n function whenDomReady() {\n if (document.readyState === "loading") {\n return new Promise((resolve) => {\n document.addEventListener("DOMContentLoaded", () => resolve(), {\n once: true\n });\n });\n }\n return Promise.resolve();\n }\n async function announceReady() {\n await whenDomReady();\n const msg = {\n type: "iframe-ready",\n protocolVersion: PROTOCOL_VERSION,\n environment: {\n crossOriginIsolated: window.crossOriginIsolated,\n isSecureContext: window.isSecureContext,\n origin: self.origin\n }\n };\n window.parent.postMessage(msg, "*");\n }\n function installHostListener() {\n window.addEventListener("message", (event) => {\n if (event.source !== window.parent) return;\n if (hostOrigin === null) {\n hostOrigin = event.origin;\n hostWindow = window.parent;\n } else if (event.origin !== hostOrigin) {\n return;\n }\n handleHostMessage(event.data).catch((err) => {\n reportError(err);\n });\n });\n }\n function postToHost(msg, transfer = []) {\n if (hostOrigin === null || hostWindow === null) {\n return;\n }\n const targetOrigin = hostOrigin === "null" ? "*" : hostOrigin;\n hostWindow.postMessage(msg, targetOrigin, transfer);\n }\n function reportError(err) {\n const e = err;\n const msg = e?.stack ? {\n type: "iframe-error",\n message: e.message ?? String(err),\n stack: e.stack\n } : {\n type: "iframe-error",\n message: e?.message ?? String(err)\n };\n emitToHost(msg);\n }\n function reportWorkerOnError(e) {\n const msg = {\n type: "iframe-error",\n message: e.message ? `Worker error: ${e.message}` : "Worker error: (no detail \\u2014 likely cross-origin opacity)",\n ...e.error && e.error.stack ? { stack: e.error.stack } : {},\n ...e.filename ? { filename: e.filename } : {},\n ...e.lineno ? { lineno: e.lineno } : {},\n ...e.colno ? { colno: e.colno } : {}\n };\n emitToHost(msg);\n }\n function emitToHost(msg) {\n if (hostOrigin && hostWindow) {\n const targetOrigin = hostOrigin === "null" ? "*" : hostOrigin;\n hostWindow.postMessage(msg, targetOrigin);\n } else {\n window.parent.postMessage(msg, "*");\n }\n }\n async function handleHostMessage(msg) {\n switch (msg.type) {\n case "viji-bootstrap":\n await handleBootstrap(msg);\n return;\n case "viji-host-message":\n relayHostToWorker(msg.payload);\n return;\n case "viji-resize":\n handleResize(msg.width, msg.height);\n return;\n case "viji-enable-sensors":\n setSensorsEnabled(msg.enabled);\n return;\n case "viji-enable-interaction":\n setInteractionEnabled(msg.enabled);\n return;\n case "viji-terminate":\n handleTerminate();\n return;\n }\n }\n async function handleBootstrap(msg) {\n if (worker) {\n return;\n }\n canvas = document.createElement("canvas");\n canvas.id = "viji-canvas";\n canvas.width = msg.init.canvasWidth;\n canvas.height = msg.init.canvasHeight;\n canvas.style.width = "100%";\n canvas.style.height = "100%";\n canvas.style.display = "block";\n canvas.tabIndex = 0;\n canvas.style.outline = "none";\n document.body.appendChild(canvas);\n const offscreen = canvas.transferControlToOffscreen();\n workerBlobUrl = URL.createObjectURL(\n new Blob([msg.workerCode], { type: "application/javascript" })\n );\n worker = new Worker(workerBlobUrl, { type: "classic" });\n worker.onerror = (e) => {\n reportWorkerOnError(e);\n };\n worker.onmessage = (event) => {\n relayWorkerToHost(event.data);\n };\n const initId = `init_${Date.now()}`;\n const initMsg = {\n type: "init",\n id: initId,\n timestamp: Date.now(),\n data: {\n canvas: offscreen,\n isHeadless: msg.init.isHeadless,\n sc: msg.init.sc,\n rh: msg.init.rh,\n cvWasmFiles: msg.wasmFiles,\n controlPort: msg.controlPort\n }\n };\n const initResponse = waitForWorkerMessage((m) => {\n return m && m.type === "init-response" && m.id === initId;\n });\n worker.postMessage(initMsg, [\n offscreen,\n msg.wasmFiles.simdLoaderJs,\n msg.wasmFiles.simdBinary,\n msg.wasmFiles.nosimdLoaderJs,\n msg.wasmFiles.nosimdBinary,\n msg.controlPort\n ]);\n await initResponse;\n if (msg.init.allowInteraction && !msg.init.isHeadless) {\n setInteractionEnabled(true);\n }\n if (msg.init.allowSensors && !msg.init.isHeadless) {\n setSensorsEnabled(true);\n }\n if (canvas) {\n canvas.addEventListener("contextmenu", (e) => e.preventDefault());\n }\n postToHost({ type: "viji-ready" });\n }\n function waitForWorkerMessage(predicate) {\n return new Promise((resolve, reject) => {\n if (!worker) {\n reject(new Error("worker not spawned"));\n return;\n }\n const w = worker;\n const onceListener = (event) => {\n if (predicate(event.data)) {\n w.removeEventListener("message", onceListener);\n resolve(event.data);\n }\n };\n w.addEventListener("message", onceListener);\n });\n }\n function relayHostToWorker(payload) {\n if (!worker || !payload) return;\n try {\n const transfer = collectTransferables(payload);\n worker.postMessage(payload, transfer);\n } catch (err) {\n reportError(err);\n }\n }\n function relayWorkerToHost(payload) {\n if (!payload) return;\n try {\n const transfer = collectTransferables(payload);\n postToHost(\n { type: "worker-message", payload },\n transfer\n );\n } catch (err) {\n reportError(err);\n }\n }\n function handleResize(width, height) {\n if (!worker) return;\n worker.postMessage({\n type: "resolution-update",\n id: `resize_${Date.now()}`,\n timestamp: Date.now(),\n data: {\n effectiveWidth: width,\n effectiveHeight: height,\n displayScale: 1\n }\n });\n }\n var isMouseInCanvas = false;\n function setInteractionEnabled(enabled) {\n if (enabled === domHandlersAttached) return;\n if (!canvas) return;\n if (enabled) {\n canvas.addEventListener("mousedown", handleMouseEvent, { passive: false });\n canvas.addEventListener("mousemove", handleMouseEvent, { passive: false });\n canvas.addEventListener("mouseup", handleMouseEvent, { passive: false });\n canvas.addEventListener("mouseenter", handleMouseEnter, { passive: false });\n canvas.addEventListener("mouseleave", handleMouseLeave, { passive: false });\n canvas.addEventListener("wheel", handleWheelEvent, { passive: false });\n canvas.addEventListener("touchstart", handleTouchEvent, { passive: false });\n canvas.addEventListener("touchmove", handleTouchEvent, { passive: false });\n canvas.addEventListener("touchend", handleTouchEvent, { passive: false });\n canvas.addEventListener("touchcancel", handleTouchEvent, { passive: false });\n document.addEventListener("keydown", handleKeyboardEvent, { passive: false });\n document.addEventListener("keyup", handleKeyboardEvent, { passive: false });\n canvas.addEventListener("mousedown", focusCanvas);\n canvas.addEventListener("touchstart", focusCanvas);\n domHandlersAttached = true;\n } else {\n canvas.removeEventListener("mousedown", handleMouseEvent);\n canvas.removeEventListener("mousemove", handleMouseEvent);\n canvas.removeEventListener("mouseup", handleMouseEvent);\n canvas.removeEventListener("mouseenter", handleMouseEnter);\n canvas.removeEventListener("mouseleave", handleMouseLeave);\n canvas.removeEventListener("wheel", handleWheelEvent);\n canvas.removeEventListener("touchstart", handleTouchEvent);\n canvas.removeEventListener("touchmove", handleTouchEvent);\n canvas.removeEventListener("touchend", handleTouchEvent);\n canvas.removeEventListener("touchcancel", handleTouchEvent);\n document.removeEventListener("keydown", handleKeyboardEvent);\n document.removeEventListener("keyup", handleKeyboardEvent);\n canvas.removeEventListener("mousedown", focusCanvas);\n canvas.removeEventListener("touchstart", focusCanvas);\n domHandlersAttached = false;\n }\n }\n function focusCanvas() {\n if (canvas) canvas.focus();\n }\n function dualRoute(workerType, data) {\n if (worker) {\n worker.postMessage({\n type: workerType,\n id: `${workerType}_${Date.now()}`,\n timestamp: Date.now(),\n data\n });\n }\n postToHost({\n type: "interaction-event",\n kind: workerType,\n data\n });\n }\n function handleMouseEvent(event) {\n if (!canvas) return;\n event.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const x = (event.clientX - rect.left) * (canvas.width / rect.width);\n const y = (event.clientY - rect.top) * (canvas.height / rect.height);\n dualRoute("mouse-update", {\n x,\n y,\n buttons: event.buttons,\n deltaX: event.movementX || 0,\n deltaY: event.movementY || 0,\n wheelDeltaX: 0,\n wheelDeltaY: 0,\n isInCanvas: isMouseInCanvas,\n timestamp: performance.now()\n });\n }\n function handleMouseEnter(event) {\n isMouseInCanvas = true;\n handleMouseEvent(event);\n }\n function handleMouseLeave(event) {\n isMouseInCanvas = false;\n handleMouseEvent(event);\n }\n function handleWheelEvent(event) {\n if (!canvas) return;\n event.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const x = (event.clientX - rect.left) * (canvas.width / rect.width);\n const y = (event.clientY - rect.top) * (canvas.height / rect.height);\n dualRoute("mouse-update", {\n x,\n y,\n buttons: event.buttons,\n deltaX: 0,\n deltaY: 0,\n wheelDeltaX: event.deltaX,\n wheelDeltaY: event.deltaY,\n isInCanvas: isMouseInCanvas,\n timestamp: performance.now()\n });\n }\n var KEY_PASSTHROUGH = /* @__PURE__ */ new Set([\n "Tab",\n "F1",\n "F2",\n "F3",\n "F4",\n "F5",\n "F11",\n "F12"\n ]);\n function handleKeyboardEvent(event) {\n if (!KEY_PASSTHROUGH.has(event.key)) {\n event.preventDefault();\n }\n dualRoute("keyboard-update", {\n type: event.type,\n key: event.key,\n code: event.code,\n keyCode: event.keyCode,\n shiftKey: event.shiftKey,\n ctrlKey: event.ctrlKey,\n altKey: event.altKey,\n metaKey: event.metaKey,\n timestamp: performance.now()\n });\n }\n function handleTouchEvent(event) {\n if (!canvas) return;\n event.preventDefault();\n const rect = canvas.getBoundingClientRect();\n const scaleX = canvas.width / rect.width;\n const scaleY = canvas.height / rect.height;\n const canvasW = canvas.width;\n const canvasH = canvas.height;\n const mapTouch = (t) => {\n const cx = (t.clientX - rect.left) * scaleX;\n const cy = (t.clientY - rect.top) * scaleY;\n return {\n identifier: t.identifier,\n clientX: cx,\n clientY: cy,\n radiusX: t.radiusX ?? 0,\n radiusY: t.radiusY ?? 0,\n rotationAngle: t.rotationAngle ?? 0,\n force: t.force ?? 0,\n isInCanvas: cx >= 0 && cx <= canvasW && cy >= 0 && cy <= canvasH\n };\n };\n const changedMap = /* @__PURE__ */ new Map();\n for (let i = 0; i < event.changedTouches.length; i++) {\n const t = event.changedTouches[i];\n changedMap.set(t.identifier, mapTouch(t));\n }\n const touches = Array.from(event.touches).map((t) => {\n return changedMap.get(t.identifier) ?? mapTouch(t);\n });\n if (event.type === "touchend" || event.type === "touchcancel") {\n const activeIds = new Set(touches.map((t) => t.identifier));\n for (const [id, mapped] of changedMap) {\n if (!activeIds.has(id)) {\n touches.push({ ...mapped, ended: true });\n }\n }\n }\n dualRoute("touch-update", {\n type: event.type,\n touches,\n timestamp: performance.now()\n });\n }\n function setSensorsEnabled(enabled) {\n if (enabled === sensorHandlersAttached) return;\n if (enabled) {\n window.addEventListener("devicemotion", handleDeviceMotion);\n window.addEventListener("deviceorientation", handleDeviceOrientation);\n sensorHandlersAttached = true;\n } else {\n window.removeEventListener("devicemotion", handleDeviceMotion);\n window.removeEventListener("deviceorientation", handleDeviceOrientation);\n sensorHandlersAttached = false;\n }\n }\n function handleDeviceMotion(event) {\n if (!event.acceleration && !event.accelerationIncludingGravity && !event.rotationRate) {\n return;\n }\n postToHost({\n type: "sensor-event",\n kind: "motion",\n data: {\n acceleration: event.acceleration ? {\n x: event.acceleration.x,\n y: event.acceleration.y,\n z: event.acceleration.z\n } : null,\n accelerationIncludingGravity: event.accelerationIncludingGravity ? {\n x: event.accelerationIncludingGravity.x,\n y: event.accelerationIncludingGravity.y,\n z: event.accelerationIncludingGravity.z\n } : null,\n rotationRate: event.rotationRate ? {\n alpha: event.rotationRate.alpha,\n beta: event.rotationRate.beta,\n gamma: event.rotationRate.gamma\n } : null,\n interval: event.interval || 16\n }\n });\n }\n function handleDeviceOrientation(event) {\n postToHost({\n type: "sensor-event",\n kind: "orientation",\n data: {\n alpha: event.alpha,\n beta: event.beta,\n gamma: event.gamma,\n absolute: event.absolute || false\n }\n });\n }\n function handleTerminate() {\n setInteractionEnabled(false);\n setSensorsEnabled(false);\n if (worker) {\n worker.terminate();\n worker = null;\n }\n if (workerBlobUrl) {\n URL.revokeObjectURL(workerBlobUrl);\n workerBlobUrl = null;\n }\n }\n installHostListener();\n announceReady().catch((err) => reportError(err));\n window.addEventListener("error", (e) => {\n reportError(e.error ?? new Error(e.message));\n });\n window.addEventListener("unhandledrejection", (e) => {\n reportError(e.reason ?? new Error("Unhandled promise rejection"));\n });\n})();\n';
|
|
10
10
|
const IFRAME_ALLOW = "accelerometer; gyroscope; magnetometer; camera; microphone";
|
|
11
11
|
const OPAQUE_ORIGIN = "null";
|
|
12
12
|
function collectTransferables(msg) {
|
|
@@ -24,6 +24,7 @@ function collectTransferables(msg) {
|
|
|
24
24
|
wasm.nosimdBinary
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
|
+
if (m.data?.controlPort) out.push(m.data.controlPort);
|
|
27
28
|
return out;
|
|
28
29
|
}
|
|
29
30
|
case "video-canvas-setup": {
|
|
@@ -68,7 +69,8 @@ function collectBootstrapTransferables(msg) {
|
|
|
68
69
|
msg.wasmFiles.simdLoaderJs,
|
|
69
70
|
msg.wasmFiles.simdBinary,
|
|
70
71
|
msg.wasmFiles.nosimdLoaderJs,
|
|
71
|
-
msg.wasmFiles.nosimdBinary
|
|
72
|
+
msg.wasmFiles.nosimdBinary,
|
|
73
|
+
msg.controlPort
|
|
72
74
|
];
|
|
73
75
|
}
|
|
74
76
|
class IFrameManager {
|
|
@@ -103,6 +105,19 @@ class IFrameManager {
|
|
|
103
105
|
hostMessageListener = null;
|
|
104
106
|
workerMessageHandlers = [];
|
|
105
107
|
vijiReadyHandlers = [];
|
|
108
|
+
/**
|
|
109
|
+
* Direct host↔worker control channel. `port1` stays on the host side and is
|
|
110
|
+
* handed to `WorkerManager` via `getControlPort1()` after `viji-ready`;
|
|
111
|
+
* `port2` is shipped to the worker through the bootstrap envelope so all
|
|
112
|
+
* steady-state host↔worker traffic can bypass the per-frame iframe relay
|
|
113
|
+
* tax. The trust boundary is unchanged — the worker still inherits the
|
|
114
|
+
* iframe's opaque origin; the port is just a faster transport.
|
|
115
|
+
*
|
|
116
|
+
* Single-owner discipline: IFrameManager owns the channel lifecycle and
|
|
117
|
+
* closes `port1` on destroy. WorkerManager only borrows the port reference
|
|
118
|
+
* for its own listener (which it detaches without closing).
|
|
119
|
+
*/
|
|
120
|
+
controlChannel = null;
|
|
106
121
|
initiallyVisible;
|
|
107
122
|
/**
|
|
108
123
|
* Enable or disable debug logging
|
|
@@ -260,6 +275,35 @@ class IFrameManager {
|
|
|
260
275
|
onVijiReady(handler) {
|
|
261
276
|
this.vijiReadyHandlers.push(handler);
|
|
262
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Create the host↔worker control channel and return the iframe-side end
|
|
280
|
+
* (`port2`) for inclusion in the bootstrap envelope. Idempotent: a second
|
|
281
|
+
* call returns the same `port2` (the underlying channel is created once
|
|
282
|
+
* per IFrameManager lifetime).
|
|
283
|
+
*
|
|
284
|
+
* Called by `WorkerManager.createWorker` while assembling the
|
|
285
|
+
* `HostBootstrapMessage` so the port hops host → iframe → worker as a
|
|
286
|
+
* transferable. The host-side `port1` is consumed via `getControlPort1()`
|
|
287
|
+
* after `viji-ready`.
|
|
288
|
+
*/
|
|
289
|
+
createControlChannel() {
|
|
290
|
+
if (!this.controlChannel) {
|
|
291
|
+
this.controlChannel = new MessageChannel();
|
|
292
|
+
}
|
|
293
|
+
return this.controlChannel.port2;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Returns the host-side end (`port1`) of the control channel created by
|
|
297
|
+
* `createControlChannel()`. WorkerManager grabs this after `viji-ready` to
|
|
298
|
+
* route per-frame traffic directly to the worker.
|
|
299
|
+
*
|
|
300
|
+
* Returns `null` if the channel was never created (e.g. legacy bootstrap
|
|
301
|
+
* shape) or has been torn down by `destroy()`. Callers should treat null
|
|
302
|
+
* as "stay on the iframe relay path" rather than as an error.
|
|
303
|
+
*/
|
|
304
|
+
getControlPort1() {
|
|
305
|
+
return this.controlChannel?.port1 ?? null;
|
|
306
|
+
}
|
|
263
307
|
// ========================================
|
|
264
308
|
// Host-side message dispatch
|
|
265
309
|
// ========================================
|
|
@@ -425,6 +469,13 @@ class IFrameManager {
|
|
|
425
469
|
} catch {
|
|
426
470
|
}
|
|
427
471
|
}
|
|
472
|
+
if (this.controlChannel) {
|
|
473
|
+
try {
|
|
474
|
+
this.controlChannel.port1.close();
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
this.controlChannel = null;
|
|
478
|
+
}
|
|
428
479
|
if (this.hostMessageListener) {
|
|
429
480
|
window.removeEventListener("message", this.hostMessageListener);
|
|
430
481
|
this.hostMessageListener = null;
|
|
@@ -589,7 +640,7 @@ class IFrameManager {
|
|
|
589
640
|
}
|
|
590
641
|
}
|
|
591
642
|
}
|
|
592
|
-
const workerUrl = "" + new URL("assets/viji.worker-
|
|
643
|
+
const workerUrl = "" + new URL("assets/viji.worker-CWKkFyOs.js", import.meta.url).href;
|
|
593
644
|
const simdLoaderJs = new URL("assets/wasm/vision_wasm_internal.js", import.meta.url).href;
|
|
594
645
|
const simdBinary = new URL("assets/wasm/vision_wasm_internal.wasm", import.meta.url).href;
|
|
595
646
|
const nosimdLoaderJs = new URL("assets/wasm/vision_wasm_nosimd_internal.js", import.meta.url).href;
|
|
@@ -606,6 +657,19 @@ class WorkerManager {
|
|
|
606
657
|
isInitialized = false;
|
|
607
658
|
/** Bound worker-message handler so we can detach on destroy. */
|
|
608
659
|
onWorkerMessageBound = null;
|
|
660
|
+
/**
|
|
661
|
+
* Host-side end of the direct host↔worker MessageChannel (created by
|
|
662
|
+
* `IFrameManager.createControlChannel()` at bootstrap time, handed to us
|
|
663
|
+
* after `viji-ready`). Once non-null, `relayHostToWorker` posts on this
|
|
664
|
+
* port instead of through the iframe relay — one cross-process hop
|
|
665
|
+
* instead of two for every per-frame message.
|
|
666
|
+
*
|
|
667
|
+
* Single-owner discipline (docs/14, rule 5): IFrameManager owns the
|
|
668
|
+
* channel lifecycle and closes `port1` on destroy. WorkerManager only
|
|
669
|
+
* detaches its `onmessage` listener.
|
|
670
|
+
*/
|
|
671
|
+
controlPort = null;
|
|
672
|
+
onControlPortMessageBound = null;
|
|
609
673
|
/**
|
|
610
674
|
* Bootstraps the worker inside the iframe.
|
|
611
675
|
*
|
|
@@ -636,6 +700,7 @@ class WorkerManager {
|
|
|
636
700
|
]);
|
|
637
701
|
this.setupMessageHandling();
|
|
638
702
|
const res = this.iframeManager.getEffectiveResolution();
|
|
703
|
+
const controlPort2 = this.iframeManager.createControlChannel();
|
|
639
704
|
const bootstrap = {
|
|
640
705
|
type: "viji-bootstrap",
|
|
641
706
|
workerCode,
|
|
@@ -645,6 +710,7 @@ class WorkerManager {
|
|
|
645
710
|
nosimdLoaderJs: nosimdLoaderJs$1,
|
|
646
711
|
nosimdBinary: nosimdBinary$1
|
|
647
712
|
},
|
|
713
|
+
controlPort: controlPort2,
|
|
648
714
|
init: {
|
|
649
715
|
canvasWidth: res.width,
|
|
650
716
|
canvasHeight: res.height,
|
|
@@ -675,6 +741,14 @@ class WorkerManager {
|
|
|
675
741
|
collectBootstrapTransferables(bootstrap)
|
|
676
742
|
);
|
|
677
743
|
await vijiReady;
|
|
744
|
+
this.controlPort = this.iframeManager.getControlPort1();
|
|
745
|
+
if (this.controlPort && this.onWorkerMessageBound) {
|
|
746
|
+
const dispatcher = this.onWorkerMessageBound;
|
|
747
|
+
this.onControlPortMessageBound = (evt) => {
|
|
748
|
+
dispatcher(evt.data);
|
|
749
|
+
};
|
|
750
|
+
this.controlPort.onmessage = this.onControlPortMessageBound;
|
|
751
|
+
}
|
|
678
752
|
this.postMessage("set-scene-code", { sceneCode: this.sceneCode });
|
|
679
753
|
this.isInitialized = true;
|
|
680
754
|
} catch (error) {
|
|
@@ -726,8 +800,17 @@ class WorkerManager {
|
|
|
726
800
|
this.relayHostToWorker(message);
|
|
727
801
|
}
|
|
728
802
|
/**
|
|
729
|
-
* Sends a host-to-worker message
|
|
730
|
-
*
|
|
803
|
+
* Sends a host-to-worker message. Once the direct control channel is
|
|
804
|
+
* wired (after `viji-ready`), per-message traffic flows on `port1`
|
|
805
|
+
* directly to the worker — bypassing the iframe relay's per-frame cost.
|
|
806
|
+
* During the bootstrap window (before `viji-ready` fires) and any
|
|
807
|
+
* post-teardown call we fall back to the iframe relay path so init-time
|
|
808
|
+
* messages still reach the worker.
|
|
809
|
+
*
|
|
810
|
+
* Transferables are harvested via the centralized `collectTransferables`
|
|
811
|
+
* helper either way, and the `transferList` shim added by parameter
|
|
812
|
+
* messages is stripped before send (the worker has no use for it once
|
|
813
|
+
* the actual handles have been transferred).
|
|
731
814
|
*/
|
|
732
815
|
relayHostToWorker(message) {
|
|
733
816
|
if (!this.iframeManager.ready && !this.isInitialized) {
|
|
@@ -739,6 +822,10 @@ class WorkerManager {
|
|
|
739
822
|
try {
|
|
740
823
|
const transfer = collectTransferables(message);
|
|
741
824
|
const sanitized = stripTransferList(message);
|
|
825
|
+
if (this.controlPort) {
|
|
826
|
+
this.controlPort.postMessage(sanitized, transfer);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
742
829
|
this.iframeManager.postEnvelope(
|
|
743
830
|
{ type: "viji-host-message", payload: sanitized },
|
|
744
831
|
transfer
|
|
@@ -779,6 +866,11 @@ class WorkerManager {
|
|
|
779
866
|
this.iframeManager.offWorkerMessage(this.onWorkerMessageBound);
|
|
780
867
|
this.onWorkerMessageBound = null;
|
|
781
868
|
}
|
|
869
|
+
if (this.controlPort && this.onControlPortMessageBound) {
|
|
870
|
+
this.controlPort.onmessage = null;
|
|
871
|
+
this.onControlPortMessageBound = null;
|
|
872
|
+
}
|
|
873
|
+
this.controlPort = null;
|
|
782
874
|
this.isInitialized = false;
|
|
783
875
|
} catch (error) {
|
|
784
876
|
console.warn("Error during worker cleanup:", error);
|
|
@@ -1805,7 +1897,7 @@ class EssentiaOnsetDetection {
|
|
|
1805
1897
|
this.initPromise = (async () => {
|
|
1806
1898
|
try {
|
|
1807
1899
|
const essentiaModule = await import("./essentia.js-core.es-DnrJE0uR.js");
|
|
1808
|
-
const wasmModule = await import("./essentia-wasm.web-
|
|
1900
|
+
const wasmModule = await import("./essentia-wasm.web-CmPC-AKu.js").then((n) => n.e);
|
|
1809
1901
|
const EssentiaClass = essentiaModule.Essentia || essentiaModule.default?.Essentia || essentiaModule.default;
|
|
1810
1902
|
let WASMModule = wasmModule.default || wasmModule.EssentiaWASM || wasmModule.default?.EssentiaWASM;
|
|
1811
1903
|
if (!WASMModule) {
|
|
@@ -10601,7 +10693,7 @@ class VijiCore {
|
|
|
10601
10693
|
}
|
|
10602
10694
|
}
|
|
10603
10695
|
}
|
|
10604
|
-
const VERSION = "0.
|
|
10696
|
+
const VERSION = "0.5.0";
|
|
10605
10697
|
export {
|
|
10606
10698
|
AudioSystem as A,
|
|
10607
10699
|
VERSION as V,
|
|
@@ -10609,4 +10701,4 @@ export {
|
|
|
10609
10701
|
VijiCoreError as b,
|
|
10610
10702
|
getDefaultExportFromCjs as g
|
|
10611
10703
|
};
|
|
10612
|
-
//# sourceMappingURL=index-
|
|
10704
|
+
//# sourceMappingURL=index-Cp9G0z4E.js.map
|