@hypertools/sdk 0.1.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/LICENSE +21 -0
- package/README.md +443 -0
- package/dist/capture/CaptureManager.d.ts +55 -0
- package/dist/capture/CaptureManager.d.ts.map +1 -0
- package/dist/capture/index.d.ts +27 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +11 -0
- package/dist/capture/index.js.map +10 -0
- package/dist/capture/types.d.ts +76 -0
- package/dist/capture/types.d.ts.map +1 -0
- package/dist/codegen/index.d.ts +6 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +800 -0
- package/dist/codegen/index.js.map +13 -0
- package/dist/controls/HypertoolControls.d.ts +84 -0
- package/dist/controls/HypertoolControls.d.ts.map +1 -0
- package/dist/controls/index.d.ts +11 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +28 -0
- package/dist/controls/index.js.map +12 -0
- package/dist/controls/simple-api.d.ts +43 -0
- package/dist/controls/simple-api.d.ts.map +1 -0
- package/dist/controls/theme.d.ts +80 -0
- package/dist/controls/theme.d.ts.map +1 -0
- package/dist/controls/types.d.ts +178 -0
- package/dist/controls/types.d.ts.map +1 -0
- package/dist/core/EventEmitter.d.ts +76 -0
- package/dist/core/EventEmitter.d.ts.map +1 -0
- package/dist/core/Experience.d.ts +128 -0
- package/dist/core/Experience.d.ts.map +1 -0
- package/dist/core/ObjectRegistry.d.ts +76 -0
- package/dist/core/ObjectRegistry.d.ts.map +1 -0
- package/dist/core/ParamStore.d.ts +66 -0
- package/dist/core/ParamStore.d.ts.map +1 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +13 -0
- package/dist/export/bundler.d.ts +55 -0
- package/dist/export/bundler.d.ts.map +1 -0
- package/dist/export/generators/index.d.ts +6 -0
- package/dist/export/generators/index.d.ts.map +1 -0
- package/dist/export/generators/webComponent.d.ts +29 -0
- package/dist/export/generators/webComponent.d.ts.map +1 -0
- package/dist/export/index.d.ts +19 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +800 -0
- package/dist/export/index.js.map +13 -0
- package/dist/export/runtime.d.ts +46 -0
- package/dist/export/runtime.d.ts.map +1 -0
- package/dist/frame/cssBridge.d.ts +34 -0
- package/dist/frame/cssBridge.d.ts.map +1 -0
- package/dist/frame/index.d.ts +9 -0
- package/dist/frame/index.d.ts.map +1 -0
- package/dist/frame/index.js +3 -0
- package/dist/frame/index.js.map +24 -0
- package/dist/frame/runtime.d.ts +39 -0
- package/dist/frame/runtime.d.ts.map +1 -0
- package/dist/frame/types.d.ts +119 -0
- package/dist/frame/types.d.ts.map +1 -0
- package/dist/frame/utils/dom.d.ts +11 -0
- package/dist/frame/utils/dom.d.ts.map +1 -0
- package/dist/frame/wrapper-app/WrapperApp.d.ts +16 -0
- package/dist/frame/wrapper-app/WrapperApp.d.ts.map +1 -0
- package/dist/frame/wrapper-app/components/CanvasSizeWidget.d.ts +17 -0
- package/dist/frame/wrapper-app/components/CanvasSizeWidget.d.ts.map +1 -0
- package/dist/frame/wrapper-app/components/ControlsPanel.d.ts +11 -0
- package/dist/frame/wrapper-app/components/ControlsPanel.d.ts.map +1 -0
- package/dist/frame/wrapper-app/components/ExportWidget.d.ts +16 -0
- package/dist/frame/wrapper-app/components/ExportWidget.d.ts.map +1 -0
- package/dist/frame/wrapper-app/components/ResizeHandles.d.ts +19 -0
- package/dist/frame/wrapper-app/components/ResizeHandles.d.ts.map +1 -0
- package/dist/frame/wrapper-app/components/SandboxContainer.d.ts +16 -0
- package/dist/frame/wrapper-app/components/SandboxContainer.d.ts.map +1 -0
- package/dist/frame/wrapper-app/components/index.d.ts +5 -0
- package/dist/frame/wrapper-app/components/index.d.ts.map +1 -0
- package/dist/frame/wrapper-app/context/CanvasContext.d.ts +37 -0
- package/dist/frame/wrapper-app/context/CanvasContext.d.ts.map +1 -0
- package/dist/frame/wrapper-app/context/index.d.ts +2 -0
- package/dist/frame/wrapper-app/context/index.d.ts.map +1 -0
- package/dist/frame/wrapper-app/index.d.ts +9 -0
- package/dist/frame/wrapper-app/index.d.ts.map +1 -0
- package/dist/frame/wrapper-app/types.d.ts +38 -0
- package/dist/frame/wrapper-app/types.d.ts.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +189 -0
- package/dist/index.js.map +35 -0
- package/dist/react/ExperienceView.d.ts +53 -0
- package/dist/react/ExperienceView.d.ts.map +1 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +15 -0
- package/dist/react/useExperience.d.ts +55 -0
- package/dist/react/useExperience.d.ts.map +1 -0
- package/dist/recording/ImageCapture.d.ts +46 -0
- package/dist/recording/ImageCapture.d.ts.map +1 -0
- package/dist/recording/Timeline.d.ts +105 -0
- package/dist/recording/Timeline.d.ts.map +1 -0
- package/dist/recording/VideoRecorder.d.ts +64 -0
- package/dist/recording/VideoRecorder.d.ts.map +1 -0
- package/dist/recording/index.d.ts +10 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +3 -0
- package/dist/recording/index.js.map +12 -0
- package/package.json +141 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React component for Experience
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { type UseExperienceOptions } from './useExperience';
|
|
6
|
+
import type { Experience } from '../core/Experience';
|
|
7
|
+
import type { ParamDefinitions } from '../core/ParamStore';
|
|
8
|
+
export interface ExperienceViewProps extends UseExperienceOptions {
|
|
9
|
+
/** CSS class name */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Inline styles */
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
/** Called when experience is ready */
|
|
14
|
+
onReady?: (experience: Experience) => void;
|
|
15
|
+
/** Called on error */
|
|
16
|
+
onError?: (error: Error) => void;
|
|
17
|
+
/** Called on param change */
|
|
18
|
+
onParamChange?: (key: string, value: unknown, previousValue: unknown) => void;
|
|
19
|
+
/** Called on play */
|
|
20
|
+
onPlay?: () => void;
|
|
21
|
+
/** Called on pause */
|
|
22
|
+
onPause?: () => void;
|
|
23
|
+
/** Children (rendered as overlay) */
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
export interface ExperienceViewRef {
|
|
27
|
+
/** Experience instance */
|
|
28
|
+
experience: Experience | null;
|
|
29
|
+
/** Whether experience is ready */
|
|
30
|
+
isReady: boolean;
|
|
31
|
+
/** Whether experience is playing */
|
|
32
|
+
isPlaying: boolean;
|
|
33
|
+
/** Current params */
|
|
34
|
+
params: Record<string, unknown>;
|
|
35
|
+
/** Param definitions */
|
|
36
|
+
paramDefs: ParamDefinitions;
|
|
37
|
+
/** Set a single parameter */
|
|
38
|
+
setParam: (key: string, value: unknown) => void;
|
|
39
|
+
/** Set multiple parameters */
|
|
40
|
+
setParams: (params: Record<string, unknown>) => void;
|
|
41
|
+
/** Play animation */
|
|
42
|
+
play: () => void;
|
|
43
|
+
/** Pause animation */
|
|
44
|
+
pause: () => void;
|
|
45
|
+
/** Toggle play/pause */
|
|
46
|
+
toggle: () => void;
|
|
47
|
+
/** Capture image */
|
|
48
|
+
captureImage: (format?: 'png' | 'jpeg' | 'webp') => Promise<Blob | null>;
|
|
49
|
+
/** Find object by name */
|
|
50
|
+
findObjectByName: <T = unknown>(name: string) => T | undefined;
|
|
51
|
+
}
|
|
52
|
+
export declare const ExperienceView: React.ForwardRefExoticComponent<ExperienceViewProps & React.RefAttributes<ExperienceViewRef>>;
|
|
53
|
+
//# sourceMappingURL=ExperienceView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExperienceView.d.ts","sourceRoot":"","sources":["../../src/react/ExperienceView.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAqD,MAAM,OAAO,CAAC;AAC1E,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAE3C,sBAAsB;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC,6BAA6B;IAC7B,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IAE9E,qBAAqB;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAE9B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IAEjB,oCAAoC;IACpC,SAAS,EAAE,OAAO,CAAC;IAEnB,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,wBAAwB;IACxB,SAAS,EAAE,gBAAgB,CAAC;IAE5B,6BAA6B;IAC7B,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEhD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAErD,qBAAqB;IACrB,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,wBAAwB;IACxB,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,oBAAoB;IACpB,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAEzE,0BAA0B;IAC1B,gBAAgB,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC;CAChE;AAED,eAAO,MAAM,cAAc,+FAmH1B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React wrapper exports
|
|
3
|
+
*/
|
|
4
|
+
export { useExperience } from './useExperience';
|
|
5
|
+
export type { UseExperienceOptions, UseExperienceResult, } from './useExperience';
|
|
6
|
+
export { ExperienceView } from './ExperienceView';
|
|
7
|
+
export type { ExperienceViewProps, ExperienceViewRef, } from './ExperienceView';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EACV,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{useEffect as v,useRef as b,useState as z,useCallback as _}from"react";class P{_handlers=new Map;on(U,V){if(!this._handlers.has(U))this._handlers.set(U,new Set);return this._handlers.get(U).add(V),()=>this.off(U,V)}once(U,V){let J=(K)=>{this.off(U,J),V(K)};return this.on(U,J)}off(U,V){let J=this._handlers.get(U);if(J)J.delete(V)}emit(U){let V=this._handlers.get(U.type);if(!V)return;for(let J of V)try{J(U)}catch(K){console.error(`[EventEmitter] Handler error for ${U.type}:`,K)}}removeAllListeners(U){if(U)this._handlers.delete(U);else this._handlers.clear()}listenerCount(U){return this._handlers.get(U)?.size??0}}class h{_definitions;_values;_listeners;_proxy;constructor(U,V){this._definitions=U,this._listeners=new Set,this._values={};for(let[J,K]of Object.entries(U))this._values[J]=K.value;if(V){for(let[J,K]of Object.entries(V))if(J in this._definitions)this._values[J]=this._validate(J,K)}this._proxy=this._createProxy()}_createProxy(){let U=this;return new Proxy(this._values,{get(V,J){return V[J]},set(V,J,K){if(!(J in U._definitions))return console.warn(`[ParamStore] Unknown parameter: ${J}`),!1;let Q=U._validate(J,K),Y=V[J];if(Q!==Y)V[J]=Q,U._notify(J,Q,Y);return!0},has(V,J){return J in V},ownKeys(V){return Object.keys(V)},getOwnPropertyDescriptor(V,J){if(J in V)return{enumerable:!0,configurable:!0,value:V[J]};return}})}_validate(U,V){let J=this._definitions[U];if(!J)return V;switch(J.type){case"number":{let K=typeof V==="number"?V:parseFloat(String(V));if(isNaN(K))K=J.value;if(J.min!==void 0)K=Math.max(J.min,K);if(J.max!==void 0)K=Math.min(J.max,K);if(J.step!==void 0)K=Math.round(K/J.step)*J.step;return K}case"color":{let K=String(V);if(/^#[0-9A-Fa-f]{6}$/.test(K))return K;if(/^#[0-9A-Fa-f]{3}$/.test(K))return K;if(/^#[0-9A-Fa-f]{8}$/.test(K))return K;if(/^rgb\(/.test(K))return K;if(/^rgba\(/.test(K))return K;if(/^hsl\(/.test(K))return K;if(/^hsla\(/.test(K))return K;return J.value}case"boolean":if(typeof V==="boolean")return V;if(V==="true"||V==="1")return!0;if(V==="false"||V==="0")return!1;return Boolean(V);case"string":return String(V);case"select":{let K=String(V);return(J.options||[]).some((Z)=>typeof Z==="object"?Z.value===K:Z===K)?K:J.value}case"point2d":{if(typeof V==="object"&&V!==null&&"x"in V&&"y"in V)return{x:Number(V.x),y:Number(V.y)};return J.value}case"point3d":{if(typeof V==="object"&&V!==null&&"x"in V&&"y"in V&&"z"in V)return{x:Number(V.x),y:Number(V.y),z:Number(V.z)};return J.value}default:return V}}_notify(U,V,J){for(let K of this._listeners)try{K(U,V,J)}catch(Q){console.error("[ParamStore] Listener error:",Q)}}getProxy(){return this._proxy}getSnapshot(){return{...this._values}}getDefinitions(){return{...this._definitions}}set(U,V){this._proxy[U]=V}setMultiple(U){for(let[V,J]of Object.entries(U))this._proxy[V]=J}reset(){for(let[U,V]of Object.entries(this._definitions))this._proxy[U]=V.value}subscribe(U){return this._listeners.add(U),()=>this._listeners.delete(U)}addDefinition(U,V){this._definitions[U]=V,this._values[U]=V.value}}class k{_byName=new Map;_byId=new Map;_onRegister=new Set;_onUnregister=new Set;register(U,V,J){if(this._byName.has(U))this.unregister(U);let K=this._generateId(),Q={name:U,id:K,object:V,metadata:J};this._byName.set(U,Q),this._byId.set(K,Q);for(let Y of this._onRegister)try{Y(U,K,V)}catch(Z){console.error("[ObjectRegistry] onRegister callback error:",Z)}return K}unregister(U){let V=this._byName.get(U);if(!V)return!1;this._byName.delete(U),this._byId.delete(V.id);for(let J of this._onUnregister)try{J(U)}catch(K){console.error("[ObjectRegistry] onUnregister callback error:",K)}return!0}findByName(U){return this._byName.get(U)?.object}findById(U){return this._byId.get(U)?.object}getInfo(U){return this._byName.get(U)}has(U){return this._byName.has(U)}getNames(){return Array.from(this._byName.keys())}getAll(){let U=new Map;for(let[V,J]of this._byName)U.set(V,J.object);return U}query(U){let V=[];for(let J of this._byName.values())if(U(J))V.push(J);return V}findByType(U){return this.query((V)=>V.metadata?.type===U).map((V)=>V.object)}clear(){let U=Array.from(this._byName.keys());for(let V of U)this.unregister(V)}onRegister(U){return this._onRegister.add(U),()=>this._onRegister.delete(U)}onUnregister(U){return this._onUnregister.add(U),()=>this._onUnregister.delete(U)}get size(){return this._byName.size}_generateId(){return`obj_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}}class g{_isReady=!1;_isPlaying=!1;_isDestroyed=!1;_currentFrame=0;_paramStore;_events;_objects;_mount;_frameRate;_filename="capture";_cleanups=[];_userCleanup;_animationFrameId;_lastFrameTime=0;_resizeObserver;_captureHandler;constructor(U){if(this._mount=U.mount,this._frameRate=U.frameRate??60,U.background)this._mount.style.background=U.background;this._paramStore=new h(U.paramDefs??{},U.initialParams),this._events=new P,this._objects=new k,this._paramStore.subscribe((V,J,K)=>{this._events.emit({type:"paramChange",timestamp:Date.now(),key:V,value:J,previousValue:K})}),this._setupResizeObserver(),this._runSetup(U.setup,U.autoplay??!0)}get isReady(){return this._isReady}get isPlaying(){return this._isPlaying}get isDestroyed(){return this._isDestroyed}get currentFrame(){return this._currentFrame}get mount(){return this._mount}get params(){return this._paramStore.getProxy()}get events(){return this._events}get objects(){return this._objects}setParam(U,V){this._paramStore.set(U,V)}setParams(U){this._paramStore.setMultiple(U)}getParams(){return this._paramStore.getSnapshot()}getParamDefs(){return this._paramStore.getDefinitions()}resetParams(){this._paramStore.reset()}registerObject(U,V,J){return this._objects.register(U,V,J)}findObjectByName(U){return this._objects.findByName(U)}findObjectById(U){return this._objects.findById(U)}getAllObjects(){return this._objects.getAll()}play(){if(this._isPlaying||this._isDestroyed)return;this._isPlaying=!0,this._lastFrameTime=performance.now(),this._tick(),this._events.emit({type:"play",timestamp:Date.now()})}pause(){if(!this._isPlaying)return;if(this._isPlaying=!1,this._animationFrameId)cancelAnimationFrame(this._animationFrameId),this._animationFrameId=void 0;this._events.emit({type:"pause",timestamp:Date.now()})}toggle(){if(this._isPlaying)this.pause();else this.play()}on(U,V){return this._events.on(U,V)}once(U,V){return this._events.once(U,V)}off(U,V){this._events.off(U,V)}async captureImage(U="png"){if(this._captureHandler)return this._captureHandler(U);let V=this._mount.querySelector("canvas");if(!V)return console.warn("[Experience] No canvas found for capture"),null;return new Promise((J)=>{let K=`image/${U}`,Q=U==="jpeg"?0.92:void 0;V.toBlob((Y)=>J(Y),K,Q)})}getFilename(){return this._filename}addCleanup(U){if(typeof U==="function")this._cleanups.push(U)}destroy(){if(this._isDestroyed)return;if(this._isDestroyed=!0,this.pause(),this._userCleanup)try{this._userCleanup()}catch(U){console.error("[Experience] User cleanup error:",U)}while(this._cleanups.length>0){let U=this._cleanups.pop();if(U)try{U()}catch(V){console.error("[Experience] Cleanup error:",V)}}this._resizeObserver?.disconnect(),this._objects.clear(),this._events.removeAllListeners(),this._events.emit({type:"destroyed",timestamp:Date.now()})}async _runSetup(U,V){try{let J=this._createContext(),K=await U(J);if(typeof K==="function")this._userCleanup=K;if(this._isReady=!0,this._events.emit({type:"ready",timestamp:Date.now()}),V)this.play()}catch(J){console.error("[Experience] Setup error:",J),this._events.emit({type:"error",timestamp:Date.now(),error:J instanceof Error?J:Error(String(J))})}}_createContext(){return{mount:this._mount,params:this._paramStore.getProxy(),exports:this._createExportsApi(),environment:this._createEnvironmentApi(),registerObject:(U,V,J)=>this._objects.register(U,V,J),findObjectByName:(U)=>this._objects.findByName(U),experience:this}}_createExportsApi(){return{captureImage:(U)=>this.captureImage(U),setFilename:(U)=>{this._filename=U},registerCaptureHandler:(U)=>{this._captureHandler=U}}}_createEnvironmentApi(){return{window,document,onResize:(U)=>{let V=()=>{U(this._mount.clientWidth,this._mount.clientHeight)};V();let J=new ResizeObserver(V);J.observe(this._mount);let K=()=>J.disconnect();return this._cleanups.push(K),K},addCleanup:(U)=>this.addCleanup(U)}}_setupResizeObserver(){this._resizeObserver=new ResizeObserver((U)=>{for(let V of U){let{width:J,height:K}=V.contentRect;this._events.emit({type:"resize",timestamp:Date.now(),width:J,height:K})}}),this._resizeObserver.observe(this._mount)}_tick(){if(!this._isPlaying)return;let U=performance.now(),V=U-this._lastFrameTime,J=1000/this._frameRate;if(V>=J)this._currentFrame++,this._lastFrameTime=U-V%J,this._events.emit({type:"frame",timestamp:Date.now(),frame:this._currentFrame,deltaTime:V});this._animationFrameId=requestAnimationFrame(()=>this._tick())}}function y(U){let V=b(null),J=b(null),K=b(U.setup);K.current=U.setup;let[Q,Y]=z(!1),[Z,$]=z(!1),[G,M]=z(null),[C,B]=z({}),[X,A]=z({});v(()=>{if(!V.current)return;Y(!1),M(null);let L=new g({mount:V.current,paramDefs:U.paramDefs,initialParams:U.initialParams,setup:(R)=>K.current(R),autoplay:U.autoplay,frameRate:U.frameRate,background:U.background});J.current=L;let F=L.on("ready",()=>{Y(!0),$(L.isPlaying),B(L.getParams()),A(L.getParamDefs())}),I=L.on("error",(R)=>{M(R.error)}),q=L.on("play",()=>$(!0)),x=L.on("pause",()=>$(!1)),f=L.on("paramChange",()=>{B(L.getParams())});return()=>{F(),I(),q(),x(),f(),L.destroy(),J.current=null}},[U.paramDefs,U.initialParams,U.autoplay,U.frameRate,U.background]);let N=_((L,F)=>{J.current?.setParam(L,F)},[]),W=_((L)=>{J.current?.setParams(L)},[]),j=_(()=>{J.current?.resetParams()},[]),D=_(()=>{J.current?.play()},[]),H=_(()=>{J.current?.pause()},[]),T=_(()=>{J.current?.toggle()},[]),w=_(async(L)=>{return J.current?.captureImage(L)??null},[]),O=_((L)=>{return J.current?.findObjectByName(L)},[]),S=_(()=>{J.current?.destroy(),J.current=null},[]);return{experience:J.current,isReady:Q,isPlaying:Z,error:G,containerRef:V,params:C,paramDefs:X,setParam:N,setParams:W,resetParams:j,play:D,pause:H,toggle:T,captureImage:w,findObjectByName:O,destroy:S}}import{forwardRef as d,useImperativeHandle as m,useEffect as E}from"react";import{jsxDEV as u}from"react/jsx-dev-runtime";var c=d(function(V,J){let{className:K,style:Q,onReady:Y,onError:Z,onParamChange:$,onPlay:G,onPause:M,children:C,...B}=V,{experience:X,isReady:A,isPlaying:N,error:W,containerRef:j,params:D,paramDefs:H,setParam:T,setParams:w,play:O,pause:S,toggle:L,captureImage:F,findObjectByName:I}=y(B);return E(()=>{if(A&&X)Y?.(X)},[A,X,Y]),E(()=>{if(W)Z?.(W)},[W,Z]),E(()=>{if(!X||!$)return;return X.on("paramChange",(q)=>{$(q.key,q.value,q.previousValue)})},[X,$]),E(()=>{if(!X)return;let q=X.on("play",()=>G?.()),x=X.on("pause",()=>M?.());return()=>{q(),x()}},[X,G,M]),m(J,()=>({experience:X,isReady:A,isPlaying:N,params:D,paramDefs:H,setParam:T,setParams:w,play:O,pause:S,toggle:L,captureImage:F,findObjectByName:I}),[X,A,N,D,H,T,w,O,S,L,F,I]),u("div",{ref:j,className:K,style:{width:"100%",height:"100%",position:"relative",...Q},children:C},void 0,!1,void 0,this)});export{y as useExperience,c as ExperienceView};
|
|
2
|
+
|
|
3
|
+
//# debugId=B458719DB131808B64756E2164756E21
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/react/useExperience.ts", "../../src/core/EventEmitter.ts", "../../src/core/ParamStore.ts", "../../src/core/ObjectRegistry.ts", "../../src/core/Experience.ts", "../../src/react/ExperienceView.tsx"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * React hook for Experience\n */\n\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport { Experience, type ExperienceConfig, type SetupFunction } from '../core/Experience';\nimport type { ParamDefinitions } from '../core/ParamStore';\nimport type { ErrorEvent } from '../core/EventEmitter';\n\nexport interface UseExperienceOptions {\n /** Parameter definitions */\n paramDefs?: ParamDefinitions;\n\n /** Initial parameter values */\n initialParams?: Record<string, unknown>;\n\n /** Setup function */\n setup: SetupFunction;\n\n /** Auto-start on init (default: true) */\n autoplay?: boolean;\n\n /** Target frame rate (default: 60) */\n frameRate?: number;\n\n /** Background color */\n background?: string;\n}\n\nexport interface UseExperienceResult {\n /** Experience instance (null until container is available) */\n experience: Experience | null;\n\n /** Whether experience is ready */\n isReady: boolean;\n\n /** Whether experience is playing */\n isPlaying: boolean;\n\n /** Error if loading failed */\n error: Error | null;\n\n /** Ref to attach to container element */\n containerRef: React.RefObject<HTMLElement>;\n\n /** Current params (reactive snapshot) */\n params: Record<string, unknown>;\n\n /** Param definitions */\n paramDefs: ParamDefinitions;\n\n /** Set a single parameter */\n setParam: (key: string, value: unknown) => void;\n\n /** Set multiple parameters */\n setParams: (params: Record<string, unknown>) => void;\n\n /** Reset params to defaults */\n resetParams: () => void;\n\n /** Play animation */\n play: () => void;\n\n /** Pause animation */\n pause: () => void;\n\n /** Toggle play/pause */\n toggle: () => void;\n\n /** Capture image */\n captureImage: (format?: 'png' | 'jpeg' | 'webp') => Promise<Blob | null>;\n\n /** Find object by name */\n findObjectByName: <T = unknown>(name: string) => T | undefined;\n\n /** Destroy experience */\n destroy: () => void;\n}\n\nexport function useExperience(options: UseExperienceOptions): UseExperienceResult {\n const containerRef = useRef<HTMLElement>(null);\n const experienceRef = useRef<Experience | null>(null);\n const setupRef = useRef(options.setup);\n\n // Keep setup ref updated\n setupRef.current = options.setup;\n\n const [isReady, setIsReady] = useState(false);\n const [isPlaying, setIsPlaying] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [params, setParams] = useState<Record<string, unknown>>({});\n const [paramDefs, setParamDefs] = useState<ParamDefinitions>({});\n\n // Create experience when container is available\n useEffect(() => {\n if (!containerRef.current) return;\n\n // Reset state\n setIsReady(false);\n setError(null);\n\n const exp = new Experience({\n mount: containerRef.current,\n paramDefs: options.paramDefs,\n initialParams: options.initialParams,\n setup: (context) => setupRef.current(context),\n autoplay: options.autoplay,\n frameRate: options.frameRate,\n background: options.background,\n });\n\n experienceRef.current = exp;\n\n // Ready handler\n const unsubReady = exp.on('ready', () => {\n setIsReady(true);\n setIsPlaying(exp.isPlaying);\n setParams(exp.getParams());\n setParamDefs(exp.getParamDefs());\n });\n\n // Error handler\n const unsubError = exp.on<ErrorEvent>('error', (event) => {\n setError(event.error);\n });\n\n // Play/pause handlers\n const unsubPlay = exp.on('play', () => setIsPlaying(true));\n const unsubPause = exp.on('pause', () => setIsPlaying(false));\n\n // Param change handler\n const unsubParamChange = exp.on('paramChange', () => {\n setParams(exp.getParams());\n });\n\n return () => {\n unsubReady();\n unsubError();\n unsubPlay();\n unsubPause();\n unsubParamChange();\n exp.destroy();\n experienceRef.current = null;\n };\n }, [options.paramDefs, options.initialParams, options.autoplay, options.frameRate, options.background]);\n\n // Memoized callbacks\n const setParam = useCallback((key: string, value: unknown) => {\n experienceRef.current?.setParam(key, value);\n }, []);\n\n const setParamsCallback = useCallback((newParams: Record<string, unknown>) => {\n experienceRef.current?.setParams(newParams);\n }, []);\n\n const resetParams = useCallback(() => {\n experienceRef.current?.resetParams();\n }, []);\n\n const play = useCallback(() => {\n experienceRef.current?.play();\n }, []);\n\n const pause = useCallback(() => {\n experienceRef.current?.pause();\n }, []);\n\n const toggle = useCallback(() => {\n experienceRef.current?.toggle();\n }, []);\n\n const captureImage = useCallback(async (format?: 'png' | 'jpeg' | 'webp') => {\n return experienceRef.current?.captureImage(format) ?? null;\n }, []);\n\n const findObjectByName = useCallback(<T = unknown>(name: string): T | undefined => {\n return experienceRef.current?.findObjectByName<T>(name);\n }, []);\n\n const destroy = useCallback(() => {\n experienceRef.current?.destroy();\n experienceRef.current = null;\n }, []);\n\n return {\n experience: experienceRef.current,\n isReady,\n isPlaying,\n error,\n containerRef: containerRef as React.RefObject<HTMLElement>,\n params,\n paramDefs,\n setParam,\n setParams: setParamsCallback,\n resetParams,\n play,\n pause,\n toggle,\n captureImage,\n findObjectByName,\n destroy,\n };\n}\n",
|
|
6
|
+
"/**\n * Typed event system for Experience\n */\n\nexport type ExperienceEventType =\n | 'ready'\n | 'error'\n | 'frame'\n | 'resize'\n | 'paramChange'\n | 'play'\n | 'pause'\n | 'objectRegistered'\n | 'objectUnregistered'\n | string; // Allow custom events\n\nexport interface ExperienceEvent {\n type: ExperienceEventType;\n timestamp: number;\n}\n\nexport interface ReadyEvent extends ExperienceEvent {\n type: 'ready';\n}\n\nexport interface ErrorEvent extends ExperienceEvent {\n type: 'error';\n error: Error;\n}\n\nexport interface FrameEvent extends ExperienceEvent {\n type: 'frame';\n frame: number;\n deltaTime: number;\n}\n\nexport interface ResizeEvent extends ExperienceEvent {\n type: 'resize';\n width: number;\n height: number;\n}\n\nexport interface ParamChangeEvent extends ExperienceEvent {\n type: 'paramChange';\n key: string;\n value: unknown;\n previousValue: unknown;\n}\n\nexport interface PlayEvent extends ExperienceEvent {\n type: 'play';\n}\n\nexport interface PauseEvent extends ExperienceEvent {\n type: 'pause';\n}\n\nexport interface ObjectRegisteredEvent extends ExperienceEvent {\n type: 'objectRegistered';\n name: string;\n id: string;\n}\n\nexport interface ObjectUnregisteredEvent extends ExperienceEvent {\n type: 'objectUnregistered';\n name: string;\n}\n\nexport type EventHandler<T extends ExperienceEvent = ExperienceEvent> = (\n event: T\n) => void;\n\nexport class EventEmitter {\n private _handlers = new Map<ExperienceEventType, Set<EventHandler>>();\n\n /**\n * Subscribe to event\n * @returns Unsubscribe function\n */\n on<T extends ExperienceEvent>(\n type: T['type'],\n handler: EventHandler<T>\n ): () => void {\n if (!this._handlers.has(type)) {\n this._handlers.set(type, new Set());\n }\n this._handlers.get(type)!.add(handler as EventHandler);\n\n // Return unsubscribe function\n return () => this.off(type, handler);\n }\n\n /**\n * Subscribe once - auto-unsubscribes after first call\n */\n once<T extends ExperienceEvent>(\n type: T['type'],\n handler: EventHandler<T>\n ): () => void {\n const wrapper: EventHandler<T> = (event) => {\n this.off(type, wrapper);\n handler(event);\n };\n return this.on(type, wrapper);\n }\n\n /**\n * Unsubscribe from event\n */\n off<T extends ExperienceEvent>(\n type: T['type'],\n handler: EventHandler<T>\n ): void {\n const handlers = this._handlers.get(type);\n if (handlers) {\n handlers.delete(handler as EventHandler);\n }\n }\n\n /**\n * Emit event to all handlers\n */\n emit<T extends ExperienceEvent>(event: T): void {\n const handlers = this._handlers.get(event.type);\n if (!handlers) return;\n\n for (const handler of handlers) {\n try {\n handler(event);\n } catch (error) {\n console.error(`[EventEmitter] Handler error for ${event.type}:`, error);\n }\n }\n }\n\n /**\n * Remove all listeners for a type (or all if no type specified)\n */\n removeAllListeners(type?: ExperienceEventType): void {\n if (type) {\n this._handlers.delete(type);\n } else {\n this._handlers.clear();\n }\n }\n\n /**\n * Get listener count for a type\n */\n listenerCount(type: ExperienceEventType): number {\n return this._handlers.get(type)?.size ?? 0;\n }\n}\n",
|
|
7
|
+
"/**\n * Reactive parameter store with Proxy-based reactivity\n */\n\nexport type ParamType =\n | 'number'\n | 'color'\n | 'boolean'\n | 'string'\n | 'select'\n | 'file'\n | 'point2d'\n | 'point3d';\n\nexport interface SelectOption {\n label: string;\n value: string;\n}\n\nexport interface ParamDefinition {\n type: ParamType;\n value: unknown;\n label?: string;\n\n // Number constraints\n min?: number;\n max?: number;\n step?: number;\n\n // Select options\n options?: (string | SelectOption)[];\n\n // File constraints\n accept?: string;\n maxSize?: number;\n}\n\nexport type ParamDefinitions = Record<string, ParamDefinition>;\nexport type ParamValues = Record<string, unknown>;\n\nexport type ParamChangeCallback = (\n key: string,\n value: unknown,\n previousValue: unknown\n) => void;\n\nexport class ParamStore {\n private _definitions: ParamDefinitions;\n private _values: ParamValues;\n private _listeners: Set<ParamChangeCallback>;\n private _proxy: ParamValues;\n\n constructor(definitions: ParamDefinitions, initialOverrides?: ParamValues) {\n this._definitions = definitions;\n this._listeners = new Set();\n\n // Initialize from definitions\n this._values = {};\n for (const [key, def] of Object.entries(definitions)) {\n this._values[key] = def.value;\n }\n\n // Apply overrides\n if (initialOverrides) {\n for (const [key, value] of Object.entries(initialOverrides)) {\n if (key in this._definitions) {\n this._values[key] = this._validate(key, value);\n }\n }\n }\n\n // Create reactive proxy\n this._proxy = this._createProxy();\n }\n\n private _createProxy(): ParamValues {\n const self = this;\n return new Proxy(this._values, {\n get(target, prop: string) {\n return target[prop];\n },\n set(target, prop: string, value: unknown) {\n if (!(prop in self._definitions)) {\n console.warn(`[ParamStore] Unknown parameter: ${prop}`);\n return false;\n }\n\n const validated = self._validate(prop, value);\n const previous = target[prop];\n\n if (validated !== previous) {\n target[prop] = validated;\n self._notify(prop, validated, previous);\n }\n\n return true;\n },\n has(target, prop: string) {\n return prop in target;\n },\n ownKeys(target) {\n return Object.keys(target);\n },\n getOwnPropertyDescriptor(target, prop: string) {\n if (prop in target) {\n return {\n enumerable: true,\n configurable: true,\n value: target[prop],\n };\n }\n return undefined;\n },\n });\n }\n\n private _validate(key: string, value: unknown): unknown {\n const def = this._definitions[key];\n if (!def) return value;\n\n switch (def.type) {\n case 'number': {\n let num = typeof value === 'number' ? value : parseFloat(String(value));\n if (isNaN(num)) num = def.value as number;\n if (def.min !== undefined) num = Math.max(def.min, num);\n if (def.max !== undefined) num = Math.min(def.max, num);\n if (def.step !== undefined) {\n num = Math.round(num / def.step) * def.step;\n }\n return num;\n }\n\n case 'color': {\n const str = String(value);\n // Accept various color formats\n if (/^#[0-9A-Fa-f]{6}$/.test(str)) return str;\n if (/^#[0-9A-Fa-f]{3}$/.test(str)) return str;\n if (/^#[0-9A-Fa-f]{8}$/.test(str)) return str; // RGBA\n if (/^rgb\\(/.test(str)) return str;\n if (/^rgba\\(/.test(str)) return str;\n if (/^hsl\\(/.test(str)) return str;\n if (/^hsla\\(/.test(str)) return str;\n return def.value;\n }\n\n case 'boolean':\n if (typeof value === 'boolean') return value;\n if (value === 'true' || value === '1') return true;\n if (value === 'false' || value === '0') return false;\n return Boolean(value);\n\n case 'string':\n return String(value);\n\n case 'select': {\n const str = String(value);\n const options = def.options || [];\n const valid = options.some((opt) =>\n typeof opt === 'object' ? opt.value === str : opt === str\n );\n return valid ? str : def.value;\n }\n\n case 'point2d': {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'x' in value &&\n 'y' in value\n ) {\n return { x: Number((value as any).x), y: Number((value as any).y) };\n }\n return def.value;\n }\n\n case 'point3d': {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'x' in value &&\n 'y' in value &&\n 'z' in value\n ) {\n return {\n x: Number((value as any).x),\n y: Number((value as any).y),\n z: Number((value as any).z),\n };\n }\n return def.value;\n }\n\n default:\n return value;\n }\n }\n\n private _notify(key: string, value: unknown, previous: unknown): void {\n for (const listener of this._listeners) {\n try {\n listener(key, value, previous);\n } catch (error) {\n console.error('[ParamStore] Listener error:', error);\n }\n }\n }\n\n /**\n * Get reactive params proxy\n */\n getProxy(): ParamValues {\n return this._proxy;\n }\n\n /**\n * Get snapshot of current values (non-reactive copy)\n */\n getSnapshot(): ParamValues {\n return { ...this._values };\n }\n\n /**\n * Get definitions\n */\n getDefinitions(): ParamDefinitions {\n return { ...this._definitions };\n }\n\n /**\n * Set single value\n */\n set(key: string, value: unknown): void {\n this._proxy[key] = value;\n }\n\n /**\n * Set multiple values\n */\n setMultiple(params: ParamValues): void {\n for (const [key, value] of Object.entries(params)) {\n this._proxy[key] = value;\n }\n }\n\n /**\n * Reset to defaults\n */\n reset(): void {\n for (const [key, def] of Object.entries(this._definitions)) {\n this._proxy[key] = def.value;\n }\n }\n\n /**\n * Subscribe to changes\n * @returns Unsubscribe function\n */\n subscribe(callback: ParamChangeCallback): () => void {\n this._listeners.add(callback);\n return () => this._listeners.delete(callback);\n }\n\n /**\n * Add a new param definition dynamically\n */\n addDefinition(key: string, definition: ParamDefinition): void {\n this._definitions[key] = definition;\n this._values[key] = definition.value;\n }\n}\n",
|
|
8
|
+
"/**\n * Spline-like object registry for querying objects by name/id\n */\n\nexport interface RegisteredObject<T = unknown> {\n name: string;\n id: string;\n object: T;\n metadata?: Record<string, unknown>;\n}\n\nexport type ObjectRegisteredCallback = (name: string, id: string, object: unknown) => void;\nexport type ObjectUnregisteredCallback = (name: string) => void;\n\nexport class ObjectRegistry {\n private _byName = new Map<string, RegisteredObject>();\n private _byId = new Map<string, RegisteredObject>();\n private _onRegister: Set<ObjectRegisteredCallback> = new Set();\n private _onUnregister: Set<ObjectUnregisteredCallback> = new Set();\n\n /**\n * Register an object for external access\n * @returns Generated ID for the object\n */\n register<T>(\n name: string,\n object: T,\n metadata?: Record<string, unknown>\n ): string {\n // If name already exists, unregister first\n if (this._byName.has(name)) {\n this.unregister(name);\n }\n\n const id = this._generateId();\n\n const registered: RegisteredObject<T> = {\n name,\n id,\n object,\n metadata,\n };\n\n this._byName.set(name, registered);\n this._byId.set(id, registered);\n\n // Notify listeners\n for (const callback of this._onRegister) {\n try {\n callback(name, id, object);\n } catch (e) {\n console.error('[ObjectRegistry] onRegister callback error:', e);\n }\n }\n\n return id;\n }\n\n /**\n * Unregister an object by name\n */\n unregister(name: string): boolean {\n const obj = this._byName.get(name);\n if (!obj) return false;\n\n this._byName.delete(name);\n this._byId.delete(obj.id);\n\n // Notify listeners\n for (const callback of this._onUnregister) {\n try {\n callback(name);\n } catch (e) {\n console.error('[ObjectRegistry] onUnregister callback error:', e);\n }\n }\n\n return true;\n }\n\n /**\n * Find object by name (Spline-like API)\n */\n findByName<T = unknown>(name: string): T | undefined {\n return this._byName.get(name)?.object as T | undefined;\n }\n\n /**\n * Find object by ID\n */\n findById<T = unknown>(id: string): T | undefined {\n return this._byId.get(id)?.object as T | undefined;\n }\n\n /**\n * Get registered object info by name\n */\n getInfo(name: string): RegisteredObject | undefined {\n return this._byName.get(name);\n }\n\n /**\n * Check if an object exists\n */\n has(name: string): boolean {\n return this._byName.has(name);\n }\n\n /**\n * Get all registered object names\n */\n getNames(): string[] {\n return Array.from(this._byName.keys());\n }\n\n /**\n * Get all registered objects as a Map\n */\n getAll(): Map<string, unknown> {\n const result = new Map<string, unknown>();\n for (const [name, registered] of this._byName) {\n result.set(name, registered.object);\n }\n return result;\n }\n\n /**\n * Query objects by metadata\n */\n query(predicate: (obj: RegisteredObject) => boolean): RegisteredObject[] {\n const results: RegisteredObject[] = [];\n for (const obj of this._byName.values()) {\n if (predicate(obj)) {\n results.push(obj);\n }\n }\n return results;\n }\n\n /**\n * Query objects by type (requires metadata.type to be set)\n */\n findByType<T = unknown>(type: string): T[] {\n return this.query((obj) => obj.metadata?.type === type).map(\n (obj) => obj.object as T\n );\n }\n\n /**\n * Clear all registered objects\n */\n clear(): void {\n const names = Array.from(this._byName.keys());\n for (const name of names) {\n this.unregister(name);\n }\n }\n\n /**\n * Subscribe to object registration events\n */\n onRegister(callback: ObjectRegisteredCallback): () => void {\n this._onRegister.add(callback);\n return () => this._onRegister.delete(callback);\n }\n\n /**\n * Subscribe to object unregistration events\n */\n onUnregister(callback: ObjectUnregisteredCallback): () => void {\n this._onUnregister.add(callback);\n return () => this._onUnregister.delete(callback);\n }\n\n /**\n * Get the number of registered objects\n */\n get size(): number {\n return this._byName.size;\n }\n\n private _generateId(): string {\n return `obj_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n }\n}\n",
|
|
9
|
+
"/**\n * Core Experience class - THE shared logic for all runtimes\n *\n * This is the brain. All adapters (Frame, Export, HostedPreview) use this.\n * Adapters are thin wiring layers - Experience contains ALL the logic.\n */\n\nimport {\n EventEmitter,\n type ExperienceEvent,\n type EventHandler,\n type ParamChangeEvent,\n type FrameEvent,\n type ResizeEvent,\n type ErrorEvent,\n} from './EventEmitter';\nimport { ParamStore, type ParamDefinitions, type ParamValues } from './ParamStore';\nimport { ObjectRegistry } from './ObjectRegistry';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ExperienceConfig {\n /** Mount element where content is rendered */\n mount: HTMLElement;\n\n /** Parameter definitions schema */\n paramDefs?: ParamDefinitions;\n\n /** Initial parameter values (overrides defaults from paramDefs) */\n initialParams?: Record<string, unknown>;\n\n /** User's setup function */\n setup: SetupFunction;\n\n /** Auto-start on init (default: true) */\n autoplay?: boolean;\n\n /** Target frame rate for frame events (default: 60) */\n frameRate?: number;\n\n /** Background color/style */\n background?: string;\n}\n\n/**\n * Context passed to user's setup function\n * This is the API users interact with in their code\n */\nexport interface ExperienceContext {\n /** DOM element to render into */\n mount: HTMLElement;\n\n /** Reactive params object (Proxy) - changes trigger events */\n params: ParamValues;\n\n /** Export/capture utilities */\n exports: ExportsApi;\n\n /** Environment utilities (window, document, resize) */\n environment: EnvironmentApi;\n\n /** Register an object for Spline-like queries */\n registerObject: <T = unknown>(\n name: string,\n object: T,\n metadata?: Record<string, unknown>\n ) => string;\n\n /** Find registered object by name */\n findObjectByName: <T = unknown>(name: string) => T | undefined;\n\n /** Experience instance (for advanced use) */\n experience: Experience;\n}\n\nexport interface ExportsApi {\n /** Capture canvas as image */\n captureImage: (format?: 'png' | 'jpeg' | 'webp') => Promise<Blob | null>;\n\n /** Set filename for exports */\n setFilename: (filename: string) => void;\n\n /** Register custom capture handler */\n registerCaptureHandler: (handler: CaptureHandler) => void;\n}\n\nexport type CaptureHandler = (format: string) => Promise<Blob | null>;\n\nexport interface EnvironmentApi {\n /** Window reference */\n window: Window;\n\n /** Document reference */\n document: Document;\n\n /** Subscribe to resize events, returns cleanup */\n onResize: (callback: (width: number, height: number) => void) => () => void;\n\n /** Add cleanup function to be called on destroy */\n addCleanup: (cleanup: () => void) => void;\n}\n\nexport type SetupFunction = (\n context: ExperienceContext\n) => CleanupFunction | void | Promise<CleanupFunction | void>;\n\nexport type CleanupFunction = () => void;\n\n// ============================================================================\n// Experience Class\n// ============================================================================\n\nexport class Experience {\n // State\n private _isReady = false;\n private _isPlaying = false;\n private _isDestroyed = false;\n private _currentFrame = 0;\n\n // Core systems\n private _paramStore: ParamStore;\n private _events: EventEmitter;\n private _objects: ObjectRegistry;\n\n // Config\n private _mount: HTMLElement;\n private _frameRate: number;\n private _filename = 'capture';\n\n // Lifecycle\n private _cleanups: CleanupFunction[] = [];\n private _userCleanup?: CleanupFunction;\n private _animationFrameId?: number;\n private _lastFrameTime = 0;\n private _resizeObserver?: ResizeObserver;\n private _captureHandler?: CaptureHandler;\n\n constructor(config: ExperienceConfig) {\n this._mount = config.mount;\n this._frameRate = config.frameRate ?? 60;\n\n // Apply background if provided\n if (config.background) {\n this._mount.style.background = config.background;\n }\n\n // Initialize core systems\n this._paramStore = new ParamStore(\n config.paramDefs ?? {},\n config.initialParams\n );\n this._events = new EventEmitter();\n this._objects = new ObjectRegistry();\n\n // Subscribe to param changes -> emit events\n this._paramStore.subscribe((key, value, previousValue) => {\n this._events.emit<ParamChangeEvent>({\n type: 'paramChange',\n timestamp: Date.now(),\n key,\n value,\n previousValue,\n });\n });\n\n // Setup resize observer\n this._setupResizeObserver();\n\n // Run user setup\n this._runSetup(config.setup, config.autoplay ?? true);\n }\n\n // ===== Public Getters =====\n\n get isReady(): boolean {\n return this._isReady;\n }\n\n get isPlaying(): boolean {\n return this._isPlaying;\n }\n\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n get currentFrame(): number {\n return this._currentFrame;\n }\n\n get mount(): HTMLElement {\n return this._mount;\n }\n\n /** Reactive params proxy */\n get params(): ParamValues {\n return this._paramStore.getProxy();\n }\n\n /** EventEmitter for external subscriptions */\n get events(): EventEmitter {\n return this._events;\n }\n\n /** ObjectRegistry for external access */\n get objects(): ObjectRegistry {\n return this._objects;\n }\n\n // ===== Parameter Methods =====\n\n setParam(key: string, value: unknown): void {\n this._paramStore.set(key, value);\n }\n\n setParams(params: Record<string, unknown>): void {\n this._paramStore.setMultiple(params);\n }\n\n getParams(): Record<string, unknown> {\n return this._paramStore.getSnapshot();\n }\n\n getParamDefs(): ParamDefinitions {\n return this._paramStore.getDefinitions();\n }\n\n resetParams(): void {\n this._paramStore.reset();\n }\n\n // ===== Object Registry (Spline-like) =====\n\n registerObject<T = unknown>(\n name: string,\n object: T,\n metadata?: Record<string, unknown>\n ): string {\n return this._objects.register(name, object, metadata);\n }\n\n findObjectByName<T = unknown>(name: string): T | undefined {\n return this._objects.findByName<T>(name);\n }\n\n findObjectById<T = unknown>(id: string): T | undefined {\n return this._objects.findById<T>(id);\n }\n\n getAllObjects(): Map<string, unknown> {\n return this._objects.getAll();\n }\n\n // ===== Playback =====\n\n play(): void {\n if (this._isPlaying || this._isDestroyed) return;\n this._isPlaying = true;\n this._lastFrameTime = performance.now();\n this._tick();\n this._events.emit({ type: 'play', timestamp: Date.now() });\n }\n\n pause(): void {\n if (!this._isPlaying) return;\n this._isPlaying = false;\n if (this._animationFrameId) {\n cancelAnimationFrame(this._animationFrameId);\n this._animationFrameId = undefined;\n }\n this._events.emit({ type: 'pause', timestamp: Date.now() });\n }\n\n toggle(): void {\n if (this._isPlaying) {\n this.pause();\n } else {\n this.play();\n }\n }\n\n // ===== Events =====\n\n on<T extends ExperienceEvent>(\n type: T['type'],\n handler: EventHandler<T>\n ): () => void {\n return this._events.on(type, handler);\n }\n\n once<T extends ExperienceEvent>(\n type: T['type'],\n handler: EventHandler<T>\n ): () => void {\n return this._events.once(type, handler);\n }\n\n off<T extends ExperienceEvent>(\n type: T['type'],\n handler: EventHandler<T>\n ): void {\n this._events.off(type, handler);\n }\n\n // ===== Capture =====\n\n async captureImage(format: 'png' | 'jpeg' | 'webp' = 'png'): Promise<Blob | null> {\n // Use custom handler if registered\n if (this._captureHandler) {\n return this._captureHandler(format);\n }\n\n // Default: find canvas and capture\n const canvas = this._mount.querySelector('canvas');\n if (!canvas) {\n console.warn('[Experience] No canvas found for capture');\n return null;\n }\n\n return new Promise((resolve) => {\n const mimeType = `image/${format}`;\n const quality = format === 'jpeg' ? 0.92 : undefined;\n canvas.toBlob((blob) => resolve(blob), mimeType, quality);\n });\n }\n\n getFilename(): string {\n return this._filename;\n }\n\n // ===== Lifecycle =====\n\n /**\n * Add a cleanup function to be called on destroy\n */\n addCleanup(cleanup: CleanupFunction): void {\n if (typeof cleanup === 'function') {\n this._cleanups.push(cleanup);\n }\n }\n\n /**\n * Destroy the experience and run all cleanups\n */\n destroy(): void {\n if (this._isDestroyed) return;\n this._isDestroyed = true;\n\n // Stop animation\n this.pause();\n\n // Run user cleanup first\n if (this._userCleanup) {\n try {\n this._userCleanup();\n } catch (e) {\n console.error('[Experience] User cleanup error:', e);\n }\n }\n\n // Run registered cleanups (reverse order)\n while (this._cleanups.length > 0) {\n const cleanup = this._cleanups.pop();\n if (cleanup) {\n try {\n cleanup();\n } catch (e) {\n console.error('[Experience] Cleanup error:', e);\n }\n }\n }\n\n // Disconnect observers\n this._resizeObserver?.disconnect();\n\n // Clear registry\n this._objects.clear();\n\n // Remove all event listeners\n this._events.removeAllListeners();\n\n // Emit destroyed event before clearing\n this._events.emit({ type: 'destroyed', timestamp: Date.now() });\n }\n\n // ===== Private Methods =====\n\n private async _runSetup(setup: SetupFunction, autoplay: boolean): Promise<void> {\n try {\n // Create context for user's setup\n const context = this._createContext();\n\n // Run user's setup\n const cleanup = await setup(context);\n if (typeof cleanup === 'function') {\n this._userCleanup = cleanup;\n }\n\n this._isReady = true;\n this._events.emit({ type: 'ready', timestamp: Date.now() });\n\n // Autoplay if enabled\n if (autoplay) {\n this.play();\n }\n } catch (error) {\n console.error('[Experience] Setup error:', error);\n this._events.emit<ErrorEvent>({\n type: 'error',\n timestamp: Date.now(),\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n private _createContext(): ExperienceContext {\n return {\n mount: this._mount,\n params: this._paramStore.getProxy(),\n exports: this._createExportsApi(),\n environment: this._createEnvironmentApi(),\n registerObject: (name, object, metadata) =>\n this._objects.register(name, object, metadata),\n findObjectByName: (name) => this._objects.findByName(name),\n experience: this,\n };\n }\n\n private _createExportsApi(): ExportsApi {\n return {\n captureImage: (format) => this.captureImage(format),\n setFilename: (filename) => {\n this._filename = filename;\n },\n registerCaptureHandler: (handler) => {\n this._captureHandler = handler;\n },\n };\n }\n\n private _createEnvironmentApi(): EnvironmentApi {\n return {\n window,\n document,\n onResize: (callback) => {\n const handler = () => {\n callback(this._mount.clientWidth, this._mount.clientHeight);\n };\n\n // Call immediately with current size\n handler();\n\n // Setup observer\n const ro = new ResizeObserver(handler);\n ro.observe(this._mount);\n\n const cleanup = () => ro.disconnect();\n this._cleanups.push(cleanup);\n return cleanup;\n },\n addCleanup: (cleanup) => this.addCleanup(cleanup),\n };\n }\n\n private _setupResizeObserver(): void {\n this._resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n this._events.emit<ResizeEvent>({\n type: 'resize',\n timestamp: Date.now(),\n width,\n height,\n });\n }\n });\n this._resizeObserver.observe(this._mount);\n }\n\n private _tick(): void {\n if (!this._isPlaying) return;\n\n const now = performance.now();\n const deltaTime = now - this._lastFrameTime;\n const targetInterval = 1000 / this._frameRate;\n\n if (deltaTime >= targetInterval) {\n this._currentFrame++;\n this._lastFrameTime = now - (deltaTime % targetInterval);\n\n this._events.emit<FrameEvent>({\n type: 'frame',\n timestamp: Date.now(),\n frame: this._currentFrame,\n deltaTime,\n });\n }\n\n this._animationFrameId = requestAnimationFrame(() => this._tick());\n }\n}\n",
|
|
10
|
+
"/**\n * React component for Experience\n */\n\nimport React, { forwardRef, useImperativeHandle, useEffect } from 'react';\nimport { useExperience, type UseExperienceOptions } from './useExperience';\nimport type { Experience } from '../core/Experience';\nimport type { ParamDefinitions } from '../core/ParamStore';\nimport type { ParamChangeEvent } from '../core/EventEmitter';\n\nexport interface ExperienceViewProps extends UseExperienceOptions {\n /** CSS class name */\n className?: string;\n\n /** Inline styles */\n style?: React.CSSProperties;\n\n /** Called when experience is ready */\n onReady?: (experience: Experience) => void;\n\n /** Called on error */\n onError?: (error: Error) => void;\n\n /** Called on param change */\n onParamChange?: (key: string, value: unknown, previousValue: unknown) => void;\n\n /** Called on play */\n onPlay?: () => void;\n\n /** Called on pause */\n onPause?: () => void;\n\n /** Children (rendered as overlay) */\n children?: React.ReactNode;\n}\n\nexport interface ExperienceViewRef {\n /** Experience instance */\n experience: Experience | null;\n\n /** Whether experience is ready */\n isReady: boolean;\n\n /** Whether experience is playing */\n isPlaying: boolean;\n\n /** Current params */\n params: Record<string, unknown>;\n\n /** Param definitions */\n paramDefs: ParamDefinitions;\n\n /** Set a single parameter */\n setParam: (key: string, value: unknown) => void;\n\n /** Set multiple parameters */\n setParams: (params: Record<string, unknown>) => void;\n\n /** Play animation */\n play: () => void;\n\n /** Pause animation */\n pause: () => void;\n\n /** Toggle play/pause */\n toggle: () => void;\n\n /** Capture image */\n captureImage: (format?: 'png' | 'jpeg' | 'webp') => Promise<Blob | null>;\n\n /** Find object by name */\n findObjectByName: <T = unknown>(name: string) => T | undefined;\n}\n\nexport const ExperienceView = forwardRef<ExperienceViewRef, ExperienceViewProps>(\n function ExperienceView(props, ref) {\n const {\n className,\n style,\n onReady,\n onError,\n onParamChange,\n onPlay,\n onPause,\n children,\n ...options\n } = props;\n\n const {\n experience,\n isReady,\n isPlaying,\n error,\n containerRef,\n params,\n paramDefs,\n setParam,\n setParams,\n play,\n pause,\n toggle,\n captureImage,\n findObjectByName,\n } = useExperience(options);\n\n // Handle onReady callback\n useEffect(() => {\n if (isReady && experience) {\n onReady?.(experience);\n }\n }, [isReady, experience, onReady]);\n\n // Handle onError callback\n useEffect(() => {\n if (error) {\n onError?.(error);\n }\n }, [error, onError]);\n\n // Handle param changes\n useEffect(() => {\n if (!experience || !onParamChange) return;\n\n return experience.on<ParamChangeEvent>('paramChange', (event) => {\n onParamChange(event.key, event.value, event.previousValue);\n });\n }, [experience, onParamChange]);\n\n // Handle play/pause callbacks\n useEffect(() => {\n if (!experience) return;\n\n const unsubPlay = experience.on('play', () => onPlay?.());\n const unsubPause = experience.on('pause', () => onPause?.());\n\n return () => {\n unsubPlay();\n unsubPause();\n };\n }, [experience, onPlay, onPause]);\n\n // Expose methods via ref\n useImperativeHandle(\n ref,\n () => ({\n experience,\n isReady,\n isPlaying,\n params,\n paramDefs,\n setParam,\n setParams,\n play,\n pause,\n toggle,\n captureImage,\n findObjectByName,\n }),\n [\n experience,\n isReady,\n isPlaying,\n params,\n paramDefs,\n setParam,\n setParams,\n play,\n pause,\n toggle,\n captureImage,\n findObjectByName,\n ]\n );\n\n return (\n <div\n ref={containerRef as React.RefObject<HTMLDivElement>}\n className={className}\n style={{\n width: '100%',\n height: '100%',\n position: 'relative',\n ...style,\n }}\n >\n {children}\n </div>\n );\n }\n);\n"
|
|
11
|
+
],
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "B458719DB131808B64756E2164756E21",
|
|
14
|
+
"names": []
|
|
15
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for Experience
|
|
3
|
+
*/
|
|
4
|
+
import { Experience, type SetupFunction } from '../core/Experience';
|
|
5
|
+
import type { ParamDefinitions } from '../core/ParamStore';
|
|
6
|
+
export interface UseExperienceOptions {
|
|
7
|
+
/** Parameter definitions */
|
|
8
|
+
paramDefs?: ParamDefinitions;
|
|
9
|
+
/** Initial parameter values */
|
|
10
|
+
initialParams?: Record<string, unknown>;
|
|
11
|
+
/** Setup function */
|
|
12
|
+
setup: SetupFunction;
|
|
13
|
+
/** Auto-start on init (default: true) */
|
|
14
|
+
autoplay?: boolean;
|
|
15
|
+
/** Target frame rate (default: 60) */
|
|
16
|
+
frameRate?: number;
|
|
17
|
+
/** Background color */
|
|
18
|
+
background?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface UseExperienceResult {
|
|
21
|
+
/** Experience instance (null until container is available) */
|
|
22
|
+
experience: Experience | null;
|
|
23
|
+
/** Whether experience is ready */
|
|
24
|
+
isReady: boolean;
|
|
25
|
+
/** Whether experience is playing */
|
|
26
|
+
isPlaying: boolean;
|
|
27
|
+
/** Error if loading failed */
|
|
28
|
+
error: Error | null;
|
|
29
|
+
/** Ref to attach to container element */
|
|
30
|
+
containerRef: React.RefObject<HTMLElement>;
|
|
31
|
+
/** Current params (reactive snapshot) */
|
|
32
|
+
params: Record<string, unknown>;
|
|
33
|
+
/** Param definitions */
|
|
34
|
+
paramDefs: ParamDefinitions;
|
|
35
|
+
/** Set a single parameter */
|
|
36
|
+
setParam: (key: string, value: unknown) => void;
|
|
37
|
+
/** Set multiple parameters */
|
|
38
|
+
setParams: (params: Record<string, unknown>) => void;
|
|
39
|
+
/** Reset params to defaults */
|
|
40
|
+
resetParams: () => void;
|
|
41
|
+
/** Play animation */
|
|
42
|
+
play: () => void;
|
|
43
|
+
/** Pause animation */
|
|
44
|
+
pause: () => void;
|
|
45
|
+
/** Toggle play/pause */
|
|
46
|
+
toggle: () => void;
|
|
47
|
+
/** Capture image */
|
|
48
|
+
captureImage: (format?: 'png' | 'jpeg' | 'webp') => Promise<Blob | null>;
|
|
49
|
+
/** Find object by name */
|
|
50
|
+
findObjectByName: <T = unknown>(name: string) => T | undefined;
|
|
51
|
+
/** Destroy experience */
|
|
52
|
+
destroy: () => void;
|
|
53
|
+
}
|
|
54
|
+
export declare function useExperience(options: UseExperienceOptions): UseExperienceResult;
|
|
55
|
+
//# sourceMappingURL=useExperience.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useExperience.d.ts","sourceRoot":"","sources":["../../src/react/useExperience.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAyB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,MAAM,WAAW,oBAAoB;IACnC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAE7B,+BAA+B;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAExC,qBAAqB;IACrB,KAAK,EAAE,aAAa,CAAC;IAErB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAE9B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IAEjB,oCAAoC;IACpC,SAAS,EAAE,OAAO,CAAC;IAEnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB,yCAAyC;IACzC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAE3C,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,wBAAwB;IACxB,SAAS,EAAE,gBAAgB,CAAC;IAE5B,6BAA6B;IAC7B,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEhD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAErD,+BAA+B;IAC/B,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,qBAAqB;IACrB,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,wBAAwB;IACxB,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,oBAAoB;IACpB,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAEzE,0BAA0B;IAC1B,gBAAgB,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC;IAE/D,yBAAyB;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CA2HhF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image capture utilities for canvas
|
|
3
|
+
*/
|
|
4
|
+
export interface CaptureOptions {
|
|
5
|
+
/** Image format (default: 'png') */
|
|
6
|
+
format?: 'png' | 'jpeg' | 'webp';
|
|
7
|
+
/** Quality for jpeg/webp (0-1, default: 0.92) */
|
|
8
|
+
quality?: number;
|
|
9
|
+
/** Optional filename for download */
|
|
10
|
+
filename?: string;
|
|
11
|
+
/** Scale factor (default: 1) */
|
|
12
|
+
scale?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CaptureResult {
|
|
15
|
+
blob: Blob;
|
|
16
|
+
url: string;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
format: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class ImageCapture {
|
|
22
|
+
private _canvas;
|
|
23
|
+
constructor(canvas: HTMLCanvasElement);
|
|
24
|
+
/**
|
|
25
|
+
* Capture current canvas state as blob
|
|
26
|
+
*/
|
|
27
|
+
capture(options?: CaptureOptions): Promise<CaptureResult | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Capture and download immediately
|
|
30
|
+
*/
|
|
31
|
+
download(options?: CaptureOptions): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Capture as data URL
|
|
34
|
+
*/
|
|
35
|
+
toDataURL(options?: CaptureOptions): string;
|
|
36
|
+
/**
|
|
37
|
+
* Copy to clipboard (if supported)
|
|
38
|
+
*/
|
|
39
|
+
copyToClipboard(options?: CaptureOptions): Promise<boolean>;
|
|
40
|
+
private _createScaledCanvas;
|
|
41
|
+
/**
|
|
42
|
+
* Dispose the capture instance
|
|
43
|
+
*/
|
|
44
|
+
dispose(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=ImageCapture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageCapture.d.ts","sourceRoot":"","sources":["../../src/recording/ImageCapture.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAEjC,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAkC;gBAErC,MAAM,EAAE,iBAAiB;IAIrC;;OAEG;IACG,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IA0C1E;;OAEG;IACG,QAAQ,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3D;;OAEG;IACH,SAAS,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM;IAY/C;;OAEG;IACG,eAAe,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBrE,OAAO,CAAC,mBAAmB;IAkB3B;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyframe-based timeline animation system
|
|
3
|
+
*/
|
|
4
|
+
export type EasingFunction = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'ease-in-quad' | 'ease-out-quad' | 'ease-in-cubic' | 'ease-out-cubic' | ((t: number) => number);
|
|
5
|
+
export interface TimelineKeyframe {
|
|
6
|
+
time: number;
|
|
7
|
+
params: Record<string, unknown>;
|
|
8
|
+
easing?: EasingFunction;
|
|
9
|
+
}
|
|
10
|
+
export interface TimelineOptions {
|
|
11
|
+
/** Loop the timeline (default: false) */
|
|
12
|
+
loop?: boolean;
|
|
13
|
+
/** Auto-start (default: false) */
|
|
14
|
+
autoplay?: boolean;
|
|
15
|
+
/** Playback speed multiplier (default: 1) */
|
|
16
|
+
speed?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface TimelineState {
|
|
19
|
+
currentTime: number;
|
|
20
|
+
duration: number;
|
|
21
|
+
isPlaying: boolean;
|
|
22
|
+
isPaused: boolean;
|
|
23
|
+
progress: number;
|
|
24
|
+
}
|
|
25
|
+
type ParamSetter = (params: Record<string, unknown>) => void;
|
|
26
|
+
export declare class Timeline {
|
|
27
|
+
private _keyframes;
|
|
28
|
+
private _duration;
|
|
29
|
+
private _currentTime;
|
|
30
|
+
private _isPlaying;
|
|
31
|
+
private _isPaused;
|
|
32
|
+
private _options;
|
|
33
|
+
private _animationFrame?;
|
|
34
|
+
private _lastTickTime;
|
|
35
|
+
private _paramSetter;
|
|
36
|
+
private _onComplete?;
|
|
37
|
+
private _onUpdate?;
|
|
38
|
+
constructor(paramSetter: ParamSetter, options?: TimelineOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Add a keyframe at specified time
|
|
41
|
+
*/
|
|
42
|
+
addKeyframe(time: number, params: Record<string, unknown>, easing?: EasingFunction): this;
|
|
43
|
+
/**
|
|
44
|
+
* Remove keyframe at specified time
|
|
45
|
+
*/
|
|
46
|
+
removeKeyframe(time: number): this;
|
|
47
|
+
/**
|
|
48
|
+
* Remove all keyframes
|
|
49
|
+
*/
|
|
50
|
+
clearKeyframes(): this;
|
|
51
|
+
/**
|
|
52
|
+
* Get all keyframes
|
|
53
|
+
*/
|
|
54
|
+
getKeyframes(): TimelineKeyframe[];
|
|
55
|
+
/**
|
|
56
|
+
* Start playing timeline
|
|
57
|
+
*/
|
|
58
|
+
play(): this;
|
|
59
|
+
/**
|
|
60
|
+
* Pause timeline
|
|
61
|
+
*/
|
|
62
|
+
pause(): this;
|
|
63
|
+
/**
|
|
64
|
+
* Stop and reset to start
|
|
65
|
+
*/
|
|
66
|
+
stop(): this;
|
|
67
|
+
/**
|
|
68
|
+
* Seek to specific time
|
|
69
|
+
*/
|
|
70
|
+
seek(time: number): this;
|
|
71
|
+
/**
|
|
72
|
+
* Seek to progress (0-1)
|
|
73
|
+
*/
|
|
74
|
+
seekProgress(progress: number): this;
|
|
75
|
+
/**
|
|
76
|
+
* Set playback speed
|
|
77
|
+
*/
|
|
78
|
+
setSpeed(speed: number): this;
|
|
79
|
+
get duration(): number;
|
|
80
|
+
get currentTime(): number;
|
|
81
|
+
get isPlaying(): boolean;
|
|
82
|
+
get isPaused(): boolean;
|
|
83
|
+
get progress(): number;
|
|
84
|
+
getState(): TimelineState;
|
|
85
|
+
/**
|
|
86
|
+
* Called when timeline completes (or loops)
|
|
87
|
+
*/
|
|
88
|
+
onComplete(callback: () => void): this;
|
|
89
|
+
/**
|
|
90
|
+
* Called on each update
|
|
91
|
+
*/
|
|
92
|
+
onUpdate(callback: (state: TimelineState) => void): this;
|
|
93
|
+
private _tick;
|
|
94
|
+
private _applyParamsAtTime;
|
|
95
|
+
private _interpolateValue;
|
|
96
|
+
private _interpolateColor;
|
|
97
|
+
private _hexToRgb;
|
|
98
|
+
private _recalculateDuration;
|
|
99
|
+
/**
|
|
100
|
+
* Dispose the timeline
|
|
101
|
+
*/
|
|
102
|
+
dispose(): void;
|
|
103
|
+
}
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=Timeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Timeline.d.ts","sourceRoot":"","sources":["../../src/recording/Timeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video recording using MediaRecorder API
|
|
3
|
+
*/
|
|
4
|
+
export interface RecordingOptions {
|
|
5
|
+
/** Output format (default: 'webm') */
|
|
6
|
+
format?: 'webm' | 'mp4';
|
|
7
|
+
/** Video quality (0-1, default: 0.9) */
|
|
8
|
+
quality?: number;
|
|
9
|
+
/** Frame rate (default: 60) */
|
|
10
|
+
frameRate?: number;
|
|
11
|
+
/** Maximum recording duration in seconds */
|
|
12
|
+
maxDuration?: number;
|
|
13
|
+
/** Video bitrate in bits per second */
|
|
14
|
+
videoBitsPerSecond?: number;
|
|
15
|
+
/** Audio bitrate (if audio track present) */
|
|
16
|
+
audioBitsPerSecond?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RecorderHandle {
|
|
19
|
+
/** Whether currently recording */
|
|
20
|
+
readonly isRecording: boolean;
|
|
21
|
+
/** Current recording duration in seconds */
|
|
22
|
+
readonly duration: number;
|
|
23
|
+
/** Progress (0-1) if maxDuration set */
|
|
24
|
+
readonly progress: number;
|
|
25
|
+
/** Stop recording and get the blob */
|
|
26
|
+
stop(): Promise<Blob>;
|
|
27
|
+
/** Pause recording */
|
|
28
|
+
pause(): void;
|
|
29
|
+
/** Resume recording */
|
|
30
|
+
resume(): void;
|
|
31
|
+
/** Cancel recording without getting result */
|
|
32
|
+
cancel(): void;
|
|
33
|
+
}
|
|
34
|
+
export declare class VideoRecorder {
|
|
35
|
+
private _canvas;
|
|
36
|
+
private _recorder;
|
|
37
|
+
private _chunks;
|
|
38
|
+
private _startTime;
|
|
39
|
+
private _isRecording;
|
|
40
|
+
private _isPaused;
|
|
41
|
+
private _maxDurationTimer?;
|
|
42
|
+
constructor(canvas: HTMLCanvasElement);
|
|
43
|
+
/**
|
|
44
|
+
* Check if MediaRecorder is supported
|
|
45
|
+
*/
|
|
46
|
+
static isSupported(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Get supported MIME types
|
|
49
|
+
*/
|
|
50
|
+
static getSupportedMimeTypes(): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Start recording
|
|
53
|
+
*/
|
|
54
|
+
start(options?: RecordingOptions): RecorderHandle;
|
|
55
|
+
private _stop;
|
|
56
|
+
private _pause;
|
|
57
|
+
private _resume;
|
|
58
|
+
private _cancel;
|
|
59
|
+
/**
|
|
60
|
+
* Dispose the recorder
|
|
61
|
+
*/
|
|
62
|
+
dispose(): void;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=VideoRecorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/recording/VideoRecorder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAExB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,sCAAsC;IACtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB,sBAAsB;IACtB,KAAK,IAAI,IAAI,CAAC;IAEd,uBAAuB;IACvB,MAAM,IAAI,IAAI,CAAC;IAEf,8CAA8C;IAC9C,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,iBAAiB,CAAC,CAAgC;gBAE9C,MAAM,EAAE,iBAAiB;IAIrC;;OAEG;IACH,MAAM,CAAC,WAAW,IAAI,OAAO;IAK7B;;OAEG;IACH,MAAM,CAAC,qBAAqB,IAAI,MAAM,EAAE;IAWxC;;OAEG;IACH,KAAK,CAAC,OAAO,GAAE,gBAAqB,GAAG,cAAc;IA4ErD,OAAO,CAAC,KAAK;IAiCb,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,OAAO;IAgBf;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recording module exports
|
|
3
|
+
*/
|
|
4
|
+
export { VideoRecorder } from './VideoRecorder';
|
|
5
|
+
export type { RecordingOptions, RecorderHandle, } from './VideoRecorder';
|
|
6
|
+
export { ImageCapture } from './ImageCapture';
|
|
7
|
+
export type { CaptureOptions, CaptureResult, } from './ImageCapture';
|
|
8
|
+
export { Timeline } from './Timeline';
|
|
9
|
+
export type { EasingFunction, TimelineKeyframe, TimelineOptions, TimelineState, } from './Timeline';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recording/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACV,cAAc,EACd,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
class ${_canvas=null;_recorder=null;_chunks=[];_startTime=0;_isRecording=!1;_isPaused=!1;_maxDurationTimer;constructor(C){this._canvas=C}static isSupported(){return typeof MediaRecorder<"u"&&typeof HTMLCanvasElement.prototype.captureStream==="function"}static getSupportedMimeTypes(){return["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm","video/mp4;codecs=h264","video/mp4"].filter((H)=>MediaRecorder.isTypeSupported(H))}start(C={}){if(!this._canvas)throw Error("No canvas provided");if(this._isRecording)throw Error("Already recording");let H=C.frameRate??60,O=this._canvas.captureStream(H),z;if(C.format==="mp4")z=MediaRecorder.isTypeSupported("video/mp4;codecs=h264")?"video/mp4;codecs=h264":"video/mp4";else z=MediaRecorder.isTypeSupported("video/webm;codecs=vp9")?"video/webm;codecs=vp9":"video/webm";if(this._recorder=new MediaRecorder(O,{mimeType:z,videoBitsPerSecond:C.videoBitsPerSecond??5000000,audioBitsPerSecond:C.audioBitsPerSecond}),this._chunks=[],this._startTime=Date.now(),this._isRecording=!0,this._isPaused=!1,this._recorder.ondataavailable=(J)=>{if(J.data.size>0)this._chunks.push(J.data)},this._recorder.start(100),C.maxDuration)this._maxDurationTimer=setTimeout(()=>{if(this._isRecording)this._recorder?.stop()},C.maxDuration*1000);let B=this;return{get isRecording(){return B._isRecording},get duration(){if(!B._isRecording)return 0;return(Date.now()-B._startTime)/1000},get progress(){if(!C.maxDuration)return 0;return Math.min(1,this.duration/C.maxDuration)},stop:()=>B._stop(),pause:()=>B._pause(),resume:()=>B._resume(),cancel:()=>B._cancel()}}_stop(){return new Promise((C,H)=>{if(!this._recorder){H(Error("No active recording"));return}if(this._maxDurationTimer)clearTimeout(this._maxDurationTimer);this._recorder.onstop=()=>{this._isRecording=!1,this._isPaused=!1;let O=this._recorder?.mimeType??"video/webm",z=new Blob(this._chunks,{type:O});this._recorder=null,this._chunks=[],C(z)},this._recorder.onerror=(O)=>{this._isRecording=!1,H(Error("Recording failed"))},this._recorder.stop()})}_pause(){if(this._recorder&&this._isRecording&&!this._isPaused)this._recorder.pause(),this._isPaused=!0}_resume(){if(this._recorder&&this._isRecording&&this._isPaused)this._recorder.resume(),this._isPaused=!1}_cancel(){if(this._maxDurationTimer)clearTimeout(this._maxDurationTimer);if(this._recorder&&this._isRecording)this._recorder.onstop=null,this._recorder.stop();this._isRecording=!1,this._isPaused=!1,this._recorder=null,this._chunks=[]}dispose(){this._cancel(),this._canvas=null}}class E{_canvas=null;constructor(C){this._canvas=C}async capture(C={}){if(!this._canvas)return console.warn("[ImageCapture] No canvas provided"),null;let H=C.format??"png",O=C.quality??0.92,z=C.scale??1,B=this._canvas;if(z!==1)B=this._createScaledCanvas(z);let J=`image/${H}`;return new Promise((L)=>{B.toBlob((M)=>{if(!M){L(null);return}let X=URL.createObjectURL(M);L({blob:M,url:X,width:B.width,height:B.height,format:H})},J,H==="png"?void 0:O)})}async download(C={}){let H=await this.capture(C);if(!H){console.warn("[ImageCapture] Capture failed");return}let O=C.filename??`capture-${Date.now()}.${C.format??"png"}`,z=document.createElement("a");z.href=H.url,z.download=O,z.click(),setTimeout(()=>URL.revokeObjectURL(H.url),1000)}toDataURL(C={}){if(!this._canvas)throw Error("No canvas provided");let H=C.format??"png",O=C.quality??0.92,z=`image/${H}`;return this._canvas.toDataURL(z,H==="png"?void 0:O)}async copyToClipboard(C={}){if(!navigator.clipboard?.write)return console.warn("[ImageCapture] Clipboard API not supported"),!1;let H=await this.capture({...C,format:"png"});if(!H)return!1;try{return await navigator.clipboard.write([new ClipboardItem({"image/png":H.blob})]),URL.revokeObjectURL(H.url),!0}catch(O){return console.error("[ImageCapture] Clipboard write failed:",O),URL.revokeObjectURL(H.url),!1}}_createScaledCanvas(C){if(!this._canvas)throw Error("No canvas provided");let H=document.createElement("canvas");H.width=Math.floor(this._canvas.width*C),H.height=Math.floor(this._canvas.height*C);let O=H.getContext("2d");if(O)O.scale(C,C),O.drawImage(this._canvas,0,0);return H}dispose(){this._canvas=null}}var F={linear:(C)=>C,"ease-in":(C)=>C*C,"ease-out":(C)=>C*(2-C),"ease-in-out":(C)=>C<0.5?2*C*C:-1+(4-2*C)*C,"ease-in-quad":(C)=>C*C,"ease-out-quad":(C)=>C*(2-C),"ease-in-cubic":(C)=>C*C*C,"ease-out-cubic":(C)=>--C*C*C+1};class P{_keyframes=[];_duration=0;_currentTime=0;_isPlaying=!1;_isPaused=!1;_options;_animationFrame;_lastTickTime=0;_paramSetter;_onComplete;_onUpdate;constructor(C,H={}){if(this._paramSetter=C,this._options={loop:H.loop??!1,autoplay:H.autoplay??!1,speed:H.speed??1},this._options.autoplay)this.play()}addKeyframe(C,H,O){return this._keyframes.push({time:C,params:H,easing:O}),this._keyframes.sort((z,B)=>z.time-B.time),this._recalculateDuration(),this}removeKeyframe(C){return this._keyframes=this._keyframes.filter((H)=>H.time!==C),this._recalculateDuration(),this}clearKeyframes(){return this._keyframes=[],this._duration=0,this}getKeyframes(){return[...this._keyframes]}play(){if(this._isPlaying&&!this._isPaused)return this;return this._isPlaying=!0,this._isPaused=!1,this._lastTickTime=performance.now(),this._tick(),this}pause(){if(this._isPaused=!0,this._animationFrame)cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0;return this}stop(){if(this._isPlaying=!1,this._isPaused=!1,this._currentTime=0,this._animationFrame)cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0;return this._applyParamsAtTime(0),this}seek(C){return this._currentTime=Math.max(0,Math.min(C,this._duration)),this._applyParamsAtTime(this._currentTime),this}seekProgress(C){return this.seek(C*this._duration)}setSpeed(C){return this._options.speed=C,this}get duration(){return this._duration}get currentTime(){return this._currentTime}get isPlaying(){return this._isPlaying&&!this._isPaused}get isPaused(){return this._isPaused}get progress(){return this._duration>0?this._currentTime/this._duration:0}getState(){return{currentTime:this._currentTime,duration:this._duration,isPlaying:this._isPlaying&&!this._isPaused,isPaused:this._isPaused,progress:this.progress}}onComplete(C){return this._onComplete=C,this}onUpdate(C){return this._onUpdate=C,this}_tick(){if(!this._isPlaying||this._isPaused)return;let C=performance.now(),H=(C-this._lastTickTime)*this._options.speed;if(this._lastTickTime=C,this._currentTime+=H/1000,this._currentTime>=this._duration)if(this._options.loop)this._currentTime=this._currentTime%this._duration;else{this._currentTime=this._duration,this._isPlaying=!1,this._applyParamsAtTime(this._currentTime),this._onComplete?.(),this._onUpdate?.(this.getState());return}this._applyParamsAtTime(this._currentTime),this._onUpdate?.(this.getState()),this._animationFrame=requestAnimationFrame(()=>this._tick())}_applyParamsAtTime(C){if(this._keyframes.length===0)return;let H=null,O=null;for(let z=0;z<this._keyframes.length;z++){if(this._keyframes[z].time<=C)H=this._keyframes[z];if(this._keyframes[z].time>=C&&!O)O=this._keyframes[z]}if(!H&&O){this._paramSetter(O.params);return}if(H&&!O){this._paramSetter(H.params);return}if(H&&O&&H.time===O.time){this._paramSetter(H.params);return}if(H&&O){let z=O.time-H.time,B=C-H.time,J=z>0?B/z:1,L=O.easing??"linear",X=(typeof L==="function"?L:F[L]??F.linear)(J),W={},q=new Set([...Object.keys(H.params),...Object.keys(O.params)]);for(let Q of q){let Y=H.params[Q],Z=O.params[Q];if(Y===void 0)W[Q]=Z;else if(Z===void 0)W[Q]=Y;else W[Q]=this._interpolateValue(Y,Z,X)}this._paramSetter(W)}}_interpolateValue(C,H,O){if(typeof C==="number"&&typeof H==="number")return C+(H-C)*O;if(typeof C==="string"&&typeof H==="string"&&C.startsWith("#")&&H.startsWith("#"))return this._interpolateColor(C,H,O);if(typeof C==="boolean"&&typeof H==="boolean")return O<0.5?C:H;if(typeof C==="string"&&typeof H==="string")return O<0.5?C:H;return O<0.5?C:H}_interpolateColor(C,H,O){let z=this._hexToRgb(C),B=this._hexToRgb(H);if(!z||!B)return C;let J=Math.round(z.r+(B.r-z.r)*O),L=Math.round(z.g+(B.g-z.g)*O),M=Math.round(z.b+(B.b-z.b)*O);return`#${J.toString(16).padStart(2,"0")}${L.toString(16).padStart(2,"0")}${M.toString(16).padStart(2,"0")}`}_hexToRgb(C){let H=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(C);return H?{r:parseInt(H[1],16),g:parseInt(H[2],16),b:parseInt(H[3],16)}:null}_recalculateDuration(){this._duration=this._keyframes.length>0?Math.max(...this._keyframes.map((C)=>C.time)):0}dispose(){this.stop(),this._keyframes=[],this._onComplete=void 0,this._onUpdate=void 0}}export{$ as VideoRecorder,P as Timeline,E as ImageCapture};
|
|
2
|
+
|
|
3
|
+
//# debugId=937A1AEA73EE788364756E2164756E21
|