@sansavision/vidra-web-capture 0.1.5

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.
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shape of the `window.__vidra` bridge object injected by the
3
+ * capture harness (`capture.js`).
4
+ */
5
+ export interface VidraBridge {
6
+ /** Whether the page is currently being captured by Vidra. */
7
+ capturing: boolean;
8
+ /** Current logical frame number (0-based). */
9
+ frame: number;
10
+ /** Current logical time in seconds. */
11
+ time: number;
12
+ /** Timeline frames-per-second. */
13
+ fps: number;
14
+ /** Arbitrary key/value variables passed from the project IR. */
15
+ vars: Record<string, number>;
16
+ /**
17
+ * Emit a value back to the Vidra capture engine.
18
+ * Useful for communicating computed values (e.g. layout metrics)
19
+ * from the web content back to the video project.
20
+ */
21
+ emit: (key: string, value: unknown) => void;
22
+ }
23
+ declare global {
24
+ interface Window {
25
+ __vidra?: VidraBridge;
26
+ __vidra_advance_frame?: () => void;
27
+ }
28
+ }
29
+ /**
30
+ * Return the current Vidra bridge state, or sensible defaults when
31
+ * not running inside a capture harness.
32
+ */
33
+ export interface VidraState {
34
+ /** Whether we are inside the Vidra capture harness. */
35
+ capturing: boolean;
36
+ /** Current frame number (0 when standalone). */
37
+ frame: number;
38
+ /** Current time in seconds (real clock when standalone). */
39
+ time: number;
40
+ /** Timeline FPS (60 when standalone). */
41
+ fps: number;
42
+ /** Project variables (empty object when standalone). */
43
+ vars: Record<string, number>;
44
+ /**
45
+ * Emit a value back to the engine. No-op when standalone.
46
+ */
47
+ emit: (key: string, value: unknown) => void;
48
+ }
49
+ /**
50
+ * A lightweight, framework-agnostic class for reading the Vidra capture
51
+ * bridge and emitting values back.
52
+ *
53
+ * @example
54
+ * ```js
55
+ * const capture = new VidraCapture();
56
+ * const { frame, time, fps, vars, capturing } = capture.getState();
57
+ * console.log(capturing ? `Capturing frame ${frame}` : 'Standalone mode');
58
+ * capture.emit('layout_height', document.body.scrollHeight);
59
+ * ```
60
+ */
61
+ export declare class VidraCapture {
62
+ private startTime;
63
+ constructor();
64
+ /**
65
+ * Return the current bridge state. If `window.__vidra` exists,
66
+ * values come from the harness. Otherwise, real-time defaults are
67
+ * returned so the page works normally in a browser.
68
+ */
69
+ getState(): VidraState;
70
+ /**
71
+ * Convenience: emit a value to the capture engine.
72
+ * Safe to call even when not in a capture harness (no-op).
73
+ */
74
+ emit(key: string, value: unknown): void;
75
+ /**
76
+ * Returns `true` if the page is currently being captured by Vidra.
77
+ */
78
+ isCapturing(): boolean;
79
+ }
80
+ export default VidraCapture;
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ // ─── @sansavision/vidra-web-capture ─────────────────────────────────
2
+ //
3
+ // This module provides the bridge between user-authored web content
4
+ // (React components, vanilla HTML pages) and the Vidra capture harness.
5
+ //
6
+ // When running inside Vidra's capture harness (Playwright or CDP), the
7
+ // global `window.__vidra` object is injected. This class reads from it
8
+ // and provides typed access. When running standalone (normal browser),
9
+ // it gracefully degrades to real-time defaults.
10
+ // ─── VidraCapture (vanilla JS) ──────────────────────────────────────
11
+ /**
12
+ * A lightweight, framework-agnostic class for reading the Vidra capture
13
+ * bridge and emitting values back.
14
+ *
15
+ * @example
16
+ * ```js
17
+ * const capture = new VidraCapture();
18
+ * const { frame, time, fps, vars, capturing } = capture.getState();
19
+ * console.log(capturing ? `Capturing frame ${frame}` : 'Standalone mode');
20
+ * capture.emit('layout_height', document.body.scrollHeight);
21
+ * ```
22
+ */
23
+ export class VidraCapture {
24
+ constructor() {
25
+ this.startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
26
+ }
27
+ /**
28
+ * Return the current bridge state. If `window.__vidra` exists,
29
+ * values come from the harness. Otherwise, real-time defaults are
30
+ * returned so the page works normally in a browser.
31
+ */
32
+ getState() {
33
+ const bridge = typeof window !== 'undefined' ? window.__vidra : undefined;
34
+ if (bridge && bridge.capturing) {
35
+ return {
36
+ capturing: true,
37
+ frame: bridge.frame,
38
+ time: bridge.time,
39
+ fps: bridge.fps,
40
+ vars: bridge.vars ?? {},
41
+ emit: bridge.emit ?? (() => { }),
42
+ };
43
+ }
44
+ // Graceful degradation — standalone mode
45
+ const elapsed = ((typeof performance !== 'undefined' ? performance.now() : Date.now()) - this.startTime) / 1000;
46
+ return {
47
+ capturing: false,
48
+ frame: 0,
49
+ time: elapsed,
50
+ fps: 60,
51
+ vars: {},
52
+ emit: () => { },
53
+ };
54
+ }
55
+ /**
56
+ * Convenience: emit a value to the capture engine.
57
+ * Safe to call even when not in a capture harness (no-op).
58
+ */
59
+ emit(key, value) {
60
+ this.getState().emit(key, value);
61
+ }
62
+ /**
63
+ * Returns `true` if the page is currently being captured by Vidra.
64
+ */
65
+ isCapturing() {
66
+ return typeof window !== 'undefined' && !!window.__vidra?.capturing;
67
+ }
68
+ }
69
+ export default VidraCapture;
@@ -0,0 +1,34 @@
1
+ import { type VidraState } from './index.js';
2
+ export interface UseVidraSceneOptions {
3
+ /**
4
+ * How often (in ms) to poll the bridge for updates when in
5
+ * standalone mode. Defaults to 16ms (~60fps).
6
+ * In capture mode the harness drives updates via postMessage,
7
+ * so this value is only used for the real-time fallback.
8
+ */
9
+ pollInterval?: number;
10
+ }
11
+ /**
12
+ * React hook for making a component Vidra-capturable.
13
+ *
14
+ * When running inside the Vidra capture harness, it returns the
15
+ * current frame/time/fps/vars from the injected `window.__vidra`
16
+ * bridge. When running standalone in a normal browser, it returns
17
+ * sensible real-time defaults so the component renders normally.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { useVidraScene } from '@sansavision/vidra-web-capture/react';
22
+ *
23
+ * export default function MyScene() {
24
+ * const { frame, time, fps, vars, capturing, emit } = useVidraScene();
25
+ * return (
26
+ * <div style={{ opacity: vars.opacity ?? 1 }}>
27
+ * {capturing ? `Frame ${frame}` : 'Live preview'}
28
+ * </div>
29
+ * );
30
+ * }
31
+ * ```
32
+ */
33
+ export declare function useVidraScene(opts?: UseVidraSceneOptions): VidraState;
34
+ export { VidraCapture, type VidraState, type VidraBridge } from './index.js';
package/dist/react.js ADDED
@@ -0,0 +1,63 @@
1
+ // ─── @sansavision/vidra-web-capture/react ────────────────────────────
2
+ //
3
+ // React hook that wraps VidraCapture and provides reactive state.
4
+ import { useState, useEffect, useRef, useCallback } from 'react';
5
+ import { VidraCapture } from './index.js';
6
+ /**
7
+ * React hook for making a component Vidra-capturable.
8
+ *
9
+ * When running inside the Vidra capture harness, it returns the
10
+ * current frame/time/fps/vars from the injected `window.__vidra`
11
+ * bridge. When running standalone in a normal browser, it returns
12
+ * sensible real-time defaults so the component renders normally.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * import { useVidraScene } from '@sansavision/vidra-web-capture/react';
17
+ *
18
+ * export default function MyScene() {
19
+ * const { frame, time, fps, vars, capturing, emit } = useVidraScene();
20
+ * return (
21
+ * <div style={{ opacity: vars.opacity ?? 1 }}>
22
+ * {capturing ? `Frame ${frame}` : 'Live preview'}
23
+ * </div>
24
+ * );
25
+ * }
26
+ * ```
27
+ */
28
+ export function useVidraScene(opts = {}) {
29
+ const { pollInterval = 16 } = opts;
30
+ const captureRef = useRef(null);
31
+ if (!captureRef.current) {
32
+ captureRef.current = new VidraCapture();
33
+ }
34
+ const [state, setState] = useState(() => captureRef.current.getState());
35
+ // Listen for postMessage-based updates from the capture harness
36
+ useEffect(() => {
37
+ const onMessage = (ev) => {
38
+ if (ev.data?.type === 'vidra_frame') {
39
+ // The harness has advanced a frame — re-read bridge.
40
+ setState(captureRef.current.getState());
41
+ }
42
+ };
43
+ window.addEventListener('message', onMessage);
44
+ // In standalone mode, poll so that `time` updates for animations.
45
+ let timerId;
46
+ if (!captureRef.current.isCapturing()) {
47
+ timerId = setInterval(() => {
48
+ setState(captureRef.current.getState());
49
+ }, pollInterval);
50
+ }
51
+ return () => {
52
+ window.removeEventListener('message', onMessage);
53
+ if (timerId !== undefined)
54
+ clearInterval(timerId);
55
+ };
56
+ }, [pollInterval]);
57
+ // Stable emit callback
58
+ const emit = useCallback((key, value) => {
59
+ captureRef.current?.emit(key, value);
60
+ }, []);
61
+ return { ...state, emit };
62
+ }
63
+ export { VidraCapture } from './index.js';
package/dist/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,20 @@
1
+ // Standalone test for VidraCapture graceful degradation.
2
+ // Run with: node dist/test.js
3
+ import { VidraCapture } from './index.js';
4
+ // ── Test 1: VidraCapture outside harness returns defaults ───────────
5
+ const capture = new VidraCapture();
6
+ const state = capture.getState();
7
+ console.assert(state.capturing === false, 'Expected capturing=false in standalone');
8
+ console.assert(state.frame === 0, 'Expected frame=0 in standalone');
9
+ console.assert(typeof state.time === 'number' && state.time >= 0, 'Expected time>=0');
10
+ console.assert(state.fps === 60, 'Expected fps=60 default');
11
+ console.assert(typeof state.vars === 'object', 'Expected vars to be an object');
12
+ console.assert(Object.keys(state.vars).length === 0, 'Expected empty vars');
13
+ console.assert(typeof state.emit === 'function', 'Expected emit to be a function');
14
+ // emit() should not throw when not capturing
15
+ state.emit('test_key', 42);
16
+ // ── Test 2: isCapturing() returns false ─────────────────────────────
17
+ console.assert(capture.isCapturing() === false, 'isCapturing should be false');
18
+ // ── Test 3: emit() convenience method works without error ───────────
19
+ capture.emit('layout_height', 1080);
20
+ console.log('✅ All VidraCapture standalone tests passed');
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@sansavision/vidra-web-capture",
3
+ "version": "0.1.5",
4
+ "description": "Client-side SDK for making React/JS apps Vidra-capturable as web() layers",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./react": {
14
+ "import": "./dist/react.js",
15
+ "types": "./dist/react.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "test": "node --experimental-vm-modules dist/test.js"
25
+ },
26
+ "keywords": [
27
+ "vidra",
28
+ "video",
29
+ "web-capture",
30
+ "react",
31
+ "scene"
32
+ ],
33
+ "author": "Sansa Vision",
34
+ "license": "MIT",
35
+ "peerDependencies": {
36
+ "react": ">=17.0.0"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "react": {
40
+ "optional": true
41
+ }
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.0.0",
45
+ "@types/react": "^18.0.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "restricted"
49
+ }
50
+ }