@thewhateverapp/tile-sdk 0.13.31 → 0.13.33

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.
Files changed (102) hide show
  1. package/dist/react/index.d.ts.map +1 -1
  2. package/dist/react/index.js +1 -0
  3. package/dist/spec/host/OverlayHost.d.ts +68 -0
  4. package/dist/spec/host/OverlayHost.d.ts.map +1 -0
  5. package/dist/spec/host/OverlayHost.js +143 -0
  6. package/dist/spec/host/index.d.ts +7 -0
  7. package/dist/spec/host/index.d.ts.map +1 -0
  8. package/dist/spec/host/index.js +6 -0
  9. package/dist/spec/index.d.ts +29 -0
  10. package/dist/spec/index.d.ts.map +1 -0
  11. package/dist/spec/index.js +81 -0
  12. package/dist/spec/registry/ComponentRegistry.d.ts +208 -0
  13. package/dist/spec/registry/ComponentRegistry.d.ts.map +1 -0
  14. package/dist/spec/registry/ComponentRegistry.js +227 -0
  15. package/dist/spec/registry/composites/BottomSheet.d.ts +33 -0
  16. package/dist/spec/registry/composites/BottomSheet.d.ts.map +1 -0
  17. package/dist/spec/registry/composites/BottomSheet.js +98 -0
  18. package/dist/spec/registry/composites/CountdownCTA.d.ts +35 -0
  19. package/dist/spec/registry/composites/CountdownCTA.d.ts.map +1 -0
  20. package/dist/spec/registry/composites/CountdownCTA.js +91 -0
  21. package/dist/spec/registry/composites/Poll.d.ts +39 -0
  22. package/dist/spec/registry/composites/Poll.d.ts.map +1 -0
  23. package/dist/spec/registry/composites/Poll.js +76 -0
  24. package/dist/spec/registry/composites/Prediction.d.ts +37 -0
  25. package/dist/spec/registry/composites/Prediction.d.ts.map +1 -0
  26. package/dist/spec/registry/composites/Prediction.js +116 -0
  27. package/dist/spec/registry/composites/index.d.ts +33 -0
  28. package/dist/spec/registry/composites/index.d.ts.map +1 -0
  29. package/dist/spec/registry/composites/index.js +36 -0
  30. package/dist/spec/registry/index.d.ts +15 -0
  31. package/dist/spec/registry/index.d.ts.map +1 -0
  32. package/dist/spec/registry/index.js +24 -0
  33. package/dist/spec/registry/primitives/Button.d.ts +30 -0
  34. package/dist/spec/registry/primitives/Button.d.ts.map +1 -0
  35. package/dist/spec/registry/primitives/Button.js +62 -0
  36. package/dist/spec/registry/primitives/Divider.d.ts +22 -0
  37. package/dist/spec/registry/primitives/Divider.d.ts.map +1 -0
  38. package/dist/spec/registry/primitives/Divider.js +56 -0
  39. package/dist/spec/registry/primitives/Image.d.ts +27 -0
  40. package/dist/spec/registry/primitives/Image.d.ts.map +1 -0
  41. package/dist/spec/registry/primitives/Image.js +36 -0
  42. package/dist/spec/registry/primitives/ProgressBar.d.ts +28 -0
  43. package/dist/spec/registry/primitives/ProgressBar.d.ts.map +1 -0
  44. package/dist/spec/registry/primitives/ProgressBar.js +50 -0
  45. package/dist/spec/registry/primitives/Row.d.ts +26 -0
  46. package/dist/spec/registry/primitives/Row.d.ts.map +1 -0
  47. package/dist/spec/registry/primitives/Row.js +50 -0
  48. package/dist/spec/registry/primitives/Spacer.d.ts +18 -0
  49. package/dist/spec/registry/primitives/Spacer.d.ts.map +1 -0
  50. package/dist/spec/registry/primitives/Spacer.js +25 -0
  51. package/dist/spec/registry/primitives/Stack.d.ts +22 -0
  52. package/dist/spec/registry/primitives/Stack.d.ts.map +1 -0
  53. package/dist/spec/registry/primitives/Stack.js +41 -0
  54. package/dist/spec/registry/primitives/Text.d.ts +26 -0
  55. package/dist/spec/registry/primitives/Text.d.ts.map +1 -0
  56. package/dist/spec/registry/primitives/Text.js +33 -0
  57. package/dist/spec/registry/primitives/index.d.ts +45 -0
  58. package/dist/spec/registry/primitives/index.d.ts.map +1 -0
  59. package/dist/spec/registry/primitives/index.js +55 -0
  60. package/dist/spec/renderer/BindingResolver.d.ts +35 -0
  61. package/dist/spec/renderer/BindingResolver.d.ts.map +1 -0
  62. package/dist/spec/renderer/BindingResolver.js +131 -0
  63. package/dist/spec/renderer/CaptionTrack.d.ts +22 -0
  64. package/dist/spec/renderer/CaptionTrack.d.ts.map +1 -0
  65. package/dist/spec/renderer/CaptionTrack.js +83 -0
  66. package/dist/spec/renderer/LayoutRenderer.d.ts +24 -0
  67. package/dist/spec/renderer/LayoutRenderer.d.ts.map +1 -0
  68. package/dist/spec/renderer/LayoutRenderer.js +66 -0
  69. package/dist/spec/renderer/OverlayCue.d.ts +20 -0
  70. package/dist/spec/renderer/OverlayCue.d.ts.map +1 -0
  71. package/dist/spec/renderer/OverlayCue.js +161 -0
  72. package/dist/spec/renderer/index.d.ts +10 -0
  73. package/dist/spec/renderer/index.d.ts.map +1 -0
  74. package/dist/spec/renderer/index.js +13 -0
  75. package/dist/spec/runtime/ActionRouter.d.ts +33 -0
  76. package/dist/spec/runtime/ActionRouter.d.ts.map +1 -0
  77. package/dist/spec/runtime/ActionRouter.js +84 -0
  78. package/dist/spec/runtime/OverlayRuntime.d.ts +84 -0
  79. package/dist/spec/runtime/OverlayRuntime.d.ts.map +1 -0
  80. package/dist/spec/runtime/OverlayRuntime.js +216 -0
  81. package/dist/spec/runtime/StateManager.d.ts +31 -0
  82. package/dist/spec/runtime/StateManager.d.ts.map +1 -0
  83. package/dist/spec/runtime/StateManager.js +60 -0
  84. package/dist/spec/runtime/TimeSync.d.ts +47 -0
  85. package/dist/spec/runtime/TimeSync.d.ts.map +1 -0
  86. package/dist/spec/runtime/TimeSync.js +140 -0
  87. package/dist/spec/runtime/index.d.ts +10 -0
  88. package/dist/spec/runtime/index.d.ts.map +1 -0
  89. package/dist/spec/runtime/index.js +13 -0
  90. package/dist/spec/schema.d.ts +889 -0
  91. package/dist/spec/schema.d.ts.map +1 -0
  92. package/dist/spec/schema.js +284 -0
  93. package/dist/spec/theme/ThemeProvider.d.ts +151 -0
  94. package/dist/spec/theme/ThemeProvider.d.ts.map +1 -0
  95. package/dist/spec/theme/ThemeProvider.js +227 -0
  96. package/dist/spec/theme/index.d.ts +7 -0
  97. package/dist/spec/theme/index.d.ts.map +1 -0
  98. package/dist/spec/theme/index.js +12 -0
  99. package/dist/spec/types.d.ts +322 -0
  100. package/dist/spec/types.d.ts.map +1 -0
  101. package/dist/spec/types.js +36 -0
  102. package/package.json +8 -1
@@ -0,0 +1,20 @@
1
+ /**
2
+ * OverlayCue
3
+ *
4
+ * Renders a single overlay cue with placement and dismiss handling.
5
+ */
6
+ import type { OverlayCue as OverlayCueType } from '../types';
7
+ export interface OverlayCueRendererProps {
8
+ /** The cue to render */
9
+ cue: OverlayCueType;
10
+ /** Animation state */
11
+ isEntering?: boolean;
12
+ isExiting?: boolean;
13
+ }
14
+ export declare function OverlayCueRenderer({ cue, isEntering, isExiting, }: OverlayCueRendererProps): JSX.Element;
15
+ /**
16
+ * Renders all visible cues from the runtime.
17
+ */
18
+ export declare function OverlayCueContainer(): JSX.Element;
19
+ export default OverlayCueRenderer;
20
+ //# sourceMappingURL=OverlayCue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverlayCue.d.ts","sourceRoot":"","sources":["../../../src/spec/renderer/OverlayCue.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAA+C,MAAM,UAAU,CAAC;AAI1G,MAAM,WAAW,uBAAuB;IACtC,wBAAwB;IACxB,GAAG,EAAE,cAAc,CAAC;IAEpB,sBAAsB;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA6GD,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,UAAkB,EAClB,SAAiB,GAClB,EAAE,uBAAuB,GAAG,GAAG,CAAC,OAAO,CAgEvC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,GAAG,CAAC,OAAO,CAYjD;AAED,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * OverlayCue
3
+ *
4
+ * Renders a single overlay cue with placement and dismiss handling.
5
+ */
6
+ import React, { useEffect, useCallback, useState } from 'react';
7
+ import { useOverlayRuntime } from '../runtime/OverlayRuntime';
8
+ import { LayoutRenderer } from './LayoutRenderer';
9
+ // =============================================================================
10
+ // Placement Styles
11
+ // =============================================================================
12
+ function getTilePlacementStyles(placement) {
13
+ const styles = {
14
+ position: 'absolute',
15
+ };
16
+ // Lane positioning
17
+ switch (placement.lane) {
18
+ case 'top':
19
+ styles.top = placement.inset ?? 16;
20
+ styles.left = 0;
21
+ styles.right = 0;
22
+ break;
23
+ case 'center':
24
+ styles.top = '50%';
25
+ styles.left = '50%';
26
+ styles.transform = 'translate(-50%, -50%)';
27
+ break;
28
+ case 'bottom':
29
+ styles.bottom = placement.inset ?? 16;
30
+ styles.left = 0;
31
+ styles.right = 0;
32
+ break;
33
+ }
34
+ // Alignment
35
+ if (placement.lane !== 'center') {
36
+ switch (placement.align) {
37
+ case 'left':
38
+ styles.left = placement.inset ?? 16;
39
+ styles.right = 'auto';
40
+ break;
41
+ case 'right':
42
+ styles.left = 'auto';
43
+ styles.right = placement.inset ?? 16;
44
+ break;
45
+ case 'center':
46
+ default:
47
+ styles.display = 'flex';
48
+ styles.justifyContent = 'center';
49
+ break;
50
+ }
51
+ }
52
+ // Z-index
53
+ if (placement.zIndex !== undefined) {
54
+ styles.zIndex = placement.zIndex;
55
+ }
56
+ return styles;
57
+ }
58
+ function getFullPlacementStyles(placement) {
59
+ const styles = {
60
+ position: 'absolute',
61
+ };
62
+ // Position
63
+ if (placement.top !== undefined)
64
+ styles.top = placement.top;
65
+ if (placement.bottom !== undefined)
66
+ styles.bottom = placement.bottom;
67
+ if (placement.left !== undefined)
68
+ styles.left = placement.left;
69
+ if (placement.right !== undefined)
70
+ styles.right = placement.right;
71
+ // Dimensions
72
+ if (placement.width !== undefined) {
73
+ styles.width = typeof placement.width === 'number' ? placement.width : placement.width;
74
+ }
75
+ if (placement.height !== undefined) {
76
+ styles.height = typeof placement.height === 'number' ? placement.height : placement.height;
77
+ }
78
+ // Z-index
79
+ if (placement.zIndex !== undefined) {
80
+ styles.zIndex = placement.zIndex;
81
+ }
82
+ return styles;
83
+ }
84
+ function getPlacementStyles(placement) {
85
+ if ('lane' in placement) {
86
+ return getTilePlacementStyles(placement);
87
+ }
88
+ return getFullPlacementStyles(placement);
89
+ }
90
+ // =============================================================================
91
+ // Animation Classes
92
+ // =============================================================================
93
+ function getAnimationClasses(isEntering, isExiting) {
94
+ if (isEntering) {
95
+ return 'animate-in fade-in slide-in-from-bottom-4 duration-300';
96
+ }
97
+ if (isExiting) {
98
+ return 'animate-out fade-out slide-out-to-bottom-4 duration-200';
99
+ }
100
+ return '';
101
+ }
102
+ // =============================================================================
103
+ // Main Component
104
+ // =============================================================================
105
+ export function OverlayCueRenderer({ cue, isEntering = false, isExiting = false, }) {
106
+ const { dismissOverlay, time, spec } = useOverlayRuntime();
107
+ const [isDismissing, setIsDismissing] = useState(false);
108
+ const placement = cue.placement;
109
+ const placementStyles = getPlacementStyles(placement);
110
+ const animationClasses = getAnimationClasses(isEntering, isExiting || isDismissing);
111
+ // Handle dismiss
112
+ const handleDismiss = useCallback(() => {
113
+ setIsDismissing(true);
114
+ // Wait for exit animation
115
+ setTimeout(() => {
116
+ dismissOverlay(cue.id);
117
+ }, 200);
118
+ }, [cue.id, dismissOverlay]);
119
+ // Auto-dismiss based on policy
120
+ useEffect(() => {
121
+ if (!cue.dismiss)
122
+ return;
123
+ const policy = cue.dismiss;
124
+ // Check triggers
125
+ for (const trigger of policy.triggers) {
126
+ switch (trigger.type) {
127
+ case 'timeout': {
128
+ const timeoutMs = trigger.delayMs ?? 5000;
129
+ const timer = setTimeout(handleDismiss, timeoutMs);
130
+ return () => clearTimeout(timer);
131
+ }
132
+ case 'tap':
133
+ // Handled via onClick
134
+ break;
135
+ case 'swipe':
136
+ // Would need gesture handling - simplified for now
137
+ break;
138
+ case 'action':
139
+ // Dismiss on action is handled in ActionRouter
140
+ break;
141
+ }
142
+ }
143
+ }, [cue.dismiss, handleDismiss]);
144
+ // Handle tap dismiss
145
+ const handleClick = useCallback(() => {
146
+ if (cue.dismiss?.triggers.some((t) => t.type === 'tap')) {
147
+ handleDismiss();
148
+ }
149
+ }, [cue.dismiss, handleDismiss]);
150
+ return (React.createElement("div", { "data-cue-id": cue.id, className: `overlay-cue ${animationClasses}`, style: placementStyles, onClick: handleClick },
151
+ React.createElement(LayoutRenderer, { node: cue.layout, nodeKey: cue.id })));
152
+ }
153
+ /**
154
+ * Renders all visible cues from the runtime.
155
+ */
156
+ export function OverlayCueContainer() {
157
+ const { visibleCues } = useOverlayRuntime();
158
+ return (React.createElement("div", { className: "overlay-cue-container absolute inset-0 pointer-events-none" }, visibleCues.map((cue) => (React.createElement("div", { key: cue.id, className: "pointer-events-auto" },
159
+ React.createElement(OverlayCueRenderer, { cue: cue, isEntering: true }))))));
160
+ }
161
+ export default OverlayCueRenderer;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Renderer Module Index
3
+ *
4
+ * Exports all renderer components and utilities.
5
+ */
6
+ export { LayoutRenderer, LayoutTree, type LayoutRendererProps, } from './LayoutRenderer';
7
+ export { resolveBindingPath, resolveBinding, resolveProps, resolvePropValue, createBindingContext, type BindingContext, } from './BindingResolver';
8
+ export { CaptionTrackRenderer, useActiveCaption, type CaptionTrackProps, } from './CaptionTrack';
9
+ export { OverlayCueRenderer, OverlayCueContainer, type OverlayCueRendererProps, } from './OverlayCue';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/spec/renderer/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,cAAc,EACd,UAAU,EACV,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,uBAAuB,GAC7B,MAAM,cAAc,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Renderer Module Index
3
+ *
4
+ * Exports all renderer components and utilities.
5
+ */
6
+ // Layout rendering
7
+ export { LayoutRenderer, LayoutTree, } from './LayoutRenderer';
8
+ // Binding resolution
9
+ export { resolveBindingPath, resolveBinding, resolveProps, resolvePropValue, createBindingContext, } from './BindingResolver';
10
+ // Caption rendering
11
+ export { CaptionTrackRenderer, useActiveCaption, } from './CaptionTrack';
12
+ // Overlay cue rendering
13
+ export { OverlayCueRenderer, OverlayCueContainer, } from './OverlayCue';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * ActionRouter
3
+ *
4
+ * Routes spec actions to appropriate handlers.
5
+ */
6
+ import type { Action, SpecEvent, SpecCommand } from '../types';
7
+ import type { StateManager } from './StateManager';
8
+ export interface ActionContext {
9
+ /** State manager for local state */
10
+ stateManager: StateManager;
11
+ /** Emit event to host */
12
+ emitEvent: (event: SpecEvent) => void;
13
+ /** Send command (internal) */
14
+ sendCommand?: (command: SpecCommand) => void;
15
+ /** Navigate to full page */
16
+ navigateToPage?: () => void;
17
+ /** Open a tile */
18
+ openTile?: (tileId: string) => void;
19
+ /** Dismiss current overlay */
20
+ dismissOverlay?: (overlayId: string) => void;
21
+ }
22
+ export interface ActionRouter {
23
+ execute(action: Action): Promise<void>;
24
+ }
25
+ /**
26
+ * Creates an action router for handling spec actions.
27
+ */
28
+ export declare function createActionRouter(context: ActionContext): ActionRouter;
29
+ /**
30
+ * Helper to check if a value is an action.
31
+ */
32
+ export declare function isActionValue(value: unknown): value is Action;
33
+ //# sourceMappingURL=ActionRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionRouter.d.ts","sourceRoot":"","sources":["../../../src/spec/runtime/ActionRouter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAc,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,YAAY,EAAE,YAAY,CAAC;IAE3B,yBAAyB;IACzB,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAEtC,8BAA8B;IAC9B,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAE7C,4BAA4B;IAC5B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAE5B,kBAAkB;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpC,8BAA8B;IAC9B,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CA+EvE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAO7D"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * ActionRouter
3
+ *
4
+ * Routes spec actions to appropriate handlers.
5
+ */
6
+ /**
7
+ * Creates an action router for handling spec actions.
8
+ */
9
+ export function createActionRouter(context) {
10
+ const { stateManager, emitEvent, navigateToPage, openTile, dismissOverlay } = context;
11
+ const handlers = {
12
+ async emit(action) {
13
+ const payload = action.payload ?? {};
14
+ const event = payload.event;
15
+ const data = payload.data;
16
+ if (event) {
17
+ emitEvent({ type: event, payload: data });
18
+ }
19
+ },
20
+ async setState(action) {
21
+ const payload = action.payload ?? {};
22
+ const key = payload.key;
23
+ const value = payload.value;
24
+ if (key !== undefined) {
25
+ stateManager.setState(key, value);
26
+ }
27
+ },
28
+ async sequence(action) {
29
+ const actions = (action.payload?.actions ?? []);
30
+ for (const subAction of actions) {
31
+ await execute(subAction);
32
+ }
33
+ },
34
+ async openTile(action) {
35
+ const tileId = action.payload?.tileId;
36
+ if (tileId && openTile) {
37
+ openTile(tileId);
38
+ }
39
+ },
40
+ async openFull() {
41
+ if (navigateToPage) {
42
+ navigateToPage();
43
+ }
44
+ },
45
+ async navigate(action) {
46
+ const url = action.payload?.url;
47
+ const target = action.payload?.target ?? '_blank';
48
+ if (url) {
49
+ window.open(url, target);
50
+ }
51
+ },
52
+ async dismiss(action) {
53
+ const overlayId = action.payload?.overlayId;
54
+ if (overlayId && dismissOverlay) {
55
+ dismissOverlay(overlayId);
56
+ }
57
+ },
58
+ async fx(action) {
59
+ // Effects like confetti, sound, haptics
60
+ const effect = action.payload?.effect;
61
+ console.log('FX effect:', effect, action.payload);
62
+ // TODO: Implement actual effects
63
+ },
64
+ };
65
+ const execute = async (action) => {
66
+ const handler = handlers[action.$action];
67
+ if (handler) {
68
+ await handler(action);
69
+ }
70
+ else {
71
+ console.warn(`Unknown action type: ${action.$action}`);
72
+ }
73
+ };
74
+ return { execute };
75
+ }
76
+ /**
77
+ * Helper to check if a value is an action.
78
+ */
79
+ export function isActionValue(value) {
80
+ return (typeof value === 'object' &&
81
+ value !== null &&
82
+ '$action' in value &&
83
+ typeof value.$action === 'string');
84
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * OverlayRuntime
3
+ *
4
+ * Main runtime provider for the overlay spec system.
5
+ * Manages state, time sync, and action routing.
6
+ */
7
+ import { type ReactNode } from 'react';
8
+ import type { OverlaySpec, SpecEvent, Action, OverlayCue } from '../types';
9
+ import { type StateSnapshot } from './StateManager';
10
+ import { type TimeState, type VideoTimeSource } from './TimeSync';
11
+ export interface OverlayRuntimeContextValue {
12
+ /** The overlay spec */
13
+ spec: OverlaySpec;
14
+ /** Current time state */
15
+ time: TimeState;
16
+ /** Local state snapshot */
17
+ state: StateSnapshot;
18
+ /** Host-provided data */
19
+ data: Record<string, unknown>;
20
+ /** User context */
21
+ ctx: Record<string, unknown>;
22
+ /** Execute an action */
23
+ executeAction: (action: Action) => Promise<void>;
24
+ /** Get current state value */
25
+ getStateValue: (key: string) => unknown;
26
+ /** Set state value */
27
+ setStateValue: (key: string, value: unknown) => void;
28
+ /** Get visible overlay cues */
29
+ visibleCues: OverlayCue[];
30
+ /** Dismissed overlay IDs */
31
+ dismissedIds: Set<string>;
32
+ /** Dismiss an overlay */
33
+ dismissOverlay: (id: string) => void;
34
+ }
35
+ export interface OverlayRuntimeProps {
36
+ /** The overlay spec to render */
37
+ spec: OverlaySpec;
38
+ /** Video time source for sync */
39
+ timeSource?: VideoTimeSource | null;
40
+ /** Host-provided data bindings */
41
+ data?: Record<string, unknown>;
42
+ /** User context (userId, etc.) */
43
+ ctx?: Record<string, unknown>;
44
+ /** Event callback for host */
45
+ onEvent?: (event: SpecEvent) => void;
46
+ /** Navigate to page callback */
47
+ onNavigateToPage?: () => void;
48
+ /** Open tile callback */
49
+ onOpenTile?: (tileId: string) => void;
50
+ /** Children to render */
51
+ children?: ReactNode;
52
+ }
53
+ export declare function OverlayRuntime({ spec, timeSource, data, ctx, onEvent, onNavigateToPage, onOpenTile, children, }: OverlayRuntimeProps): JSX.Element;
54
+ /**
55
+ * Hook to access the overlay runtime context.
56
+ */
57
+ export declare function useOverlayRuntime(): OverlayRuntimeContextValue;
58
+ /**
59
+ * Hook to get current time state.
60
+ */
61
+ export declare function useRuntimeTime(): TimeState;
62
+ /**
63
+ * Hook to get local state.
64
+ */
65
+ export declare function useRuntimeState(): StateSnapshot;
66
+ /**
67
+ * Hook to get host data.
68
+ */
69
+ export declare function useRuntimeData<T = unknown>(key: string): T | undefined;
70
+ /**
71
+ * Hook to execute actions.
72
+ */
73
+ export declare function useRuntimeAction(): (action: Action) => Promise<void>;
74
+ interface EvaluationContext {
75
+ state: StateSnapshot;
76
+ data: Record<string, unknown>;
77
+ ctx: Record<string, unknown>;
78
+ }
79
+ declare function resolveBinding(path: string, context: EvaluationContext): unknown;
80
+ declare function getNestedValue(obj: Record<string, unknown>, path: string): unknown;
81
+ declare function evaluateCondition(condition: unknown, context: EvaluationContext): boolean;
82
+ export { evaluateCondition, resolveBinding, getNestedValue };
83
+ export type { EvaluationContext };
84
+ //# sourceMappingURL=OverlayRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverlayRuntime.d.ts","sourceRoot":"","sources":["../../../src/spec/runtime/OverlayRuntime.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * OverlayRuntime
3
+ *
4
+ * Main runtime provider for the overlay spec system.
5
+ * Manages state, time sync, and action routing.
6
+ */
7
+ import React, { createContext, useContext, useEffect, useMemo, useCallback, } from 'react';
8
+ import { createStateManager, useSpecState } from './StateManager';
9
+ import { createActionRouter } from './ActionRouter';
10
+ import { useTimeSync, isTimeInWindow } from './TimeSync';
11
+ const OverlayRuntimeContext = createContext(null);
12
+ // =============================================================================
13
+ // Provider Component
14
+ // =============================================================================
15
+ export function OverlayRuntime({ spec, timeSource = null, data = {}, ctx = {}, onEvent, onNavigateToPage, onOpenTile, children, }) {
16
+ // Create state manager
17
+ const stateManager = useMemo(() => createStateManager(spec.state?.local ?? {}), [spec.id] // Reset when spec ID changes
18
+ );
19
+ // Track dismissed overlays
20
+ const [dismissedIds, setDismissedIds] = React.useState(new Set());
21
+ const dismissOverlay = useCallback((id) => {
22
+ setDismissedIds((prev) => new Set([...prev, id]));
23
+ }, []);
24
+ // Get current state
25
+ const state = useSpecState(stateManager);
26
+ // Time sync
27
+ const time = useTimeSync(timeSource, spec.sync);
28
+ // Event emitter
29
+ const emitEvent = useCallback((event) => {
30
+ onEvent?.(event);
31
+ }, [onEvent]);
32
+ // Create action router
33
+ const actionRouter = useMemo(() => {
34
+ const actionContext = {
35
+ stateManager,
36
+ emitEvent,
37
+ navigateToPage: onNavigateToPage,
38
+ openTile: onOpenTile,
39
+ dismissOverlay,
40
+ };
41
+ return createActionRouter(actionContext);
42
+ }, [stateManager, emitEvent, onNavigateToPage, onOpenTile, dismissOverlay]);
43
+ // Execute action helper
44
+ const executeAction = useCallback(async (action) => {
45
+ await actionRouter.execute(action);
46
+ }, [actionRouter]);
47
+ // State accessors
48
+ const getStateValue = useCallback((key) => stateManager.getState(key), [stateManager]);
49
+ const setStateValue = useCallback((key, value) => stateManager.setState(key, value), [stateManager]);
50
+ // Calculate visible cues
51
+ const visibleCues = useMemo(() => {
52
+ const cues = spec.tracks?.overlays ?? [];
53
+ return cues.filter((cue) => {
54
+ // Check if dismissed
55
+ if (dismissedIds.has(cue.id)) {
56
+ return false;
57
+ }
58
+ // Check time window
59
+ const inWindow = isTimeInWindow(time.timeMs, cue.window?.startMs, cue.window?.endMs, spec.sync?.toleranceMs ?? 100);
60
+ if (!inWindow) {
61
+ return false;
62
+ }
63
+ // Check visibility condition
64
+ if (cue.visibleWhen) {
65
+ const visible = evaluateCondition(cue.visibleWhen, { state, data, ctx });
66
+ if (!visible) {
67
+ return false;
68
+ }
69
+ }
70
+ return true;
71
+ });
72
+ }, [spec.tracks?.overlays, time.timeMs, spec.sync?.toleranceMs, state, data, ctx, dismissedIds]);
73
+ // Reset dismissed IDs when spec changes
74
+ useEffect(() => {
75
+ setDismissedIds(new Set());
76
+ }, [spec.id]);
77
+ const value = {
78
+ spec,
79
+ time,
80
+ state,
81
+ data,
82
+ ctx,
83
+ executeAction,
84
+ getStateValue,
85
+ setStateValue,
86
+ visibleCues,
87
+ dismissedIds,
88
+ dismissOverlay,
89
+ };
90
+ return (React.createElement(OverlayRuntimeContext.Provider, { value: value }, children));
91
+ }
92
+ // =============================================================================
93
+ // Hooks
94
+ // =============================================================================
95
+ /**
96
+ * Hook to access the overlay runtime context.
97
+ */
98
+ export function useOverlayRuntime() {
99
+ const context = useContext(OverlayRuntimeContext);
100
+ if (!context) {
101
+ throw new Error('useOverlayRuntime must be used within an OverlayRuntime provider');
102
+ }
103
+ return context;
104
+ }
105
+ /**
106
+ * Hook to get current time state.
107
+ */
108
+ export function useRuntimeTime() {
109
+ const { time } = useOverlayRuntime();
110
+ return time;
111
+ }
112
+ /**
113
+ * Hook to get local state.
114
+ */
115
+ export function useRuntimeState() {
116
+ const { state } = useOverlayRuntime();
117
+ return state;
118
+ }
119
+ /**
120
+ * Hook to get host data.
121
+ */
122
+ export function useRuntimeData(key) {
123
+ const { data } = useOverlayRuntime();
124
+ return data[key];
125
+ }
126
+ /**
127
+ * Hook to execute actions.
128
+ */
129
+ export function useRuntimeAction() {
130
+ const { executeAction } = useOverlayRuntime();
131
+ return executeAction;
132
+ }
133
+ function resolveBinding(path, context) {
134
+ const parts = path.split('.');
135
+ const scope = parts[0];
136
+ const key = parts.slice(1).join('.');
137
+ switch (scope) {
138
+ case 'local':
139
+ return context.state[key];
140
+ case 'data':
141
+ return getNestedValue(context.data, key);
142
+ case 'ctx':
143
+ return getNestedValue(context.ctx, key);
144
+ default:
145
+ return undefined;
146
+ }
147
+ }
148
+ function getNestedValue(obj, path) {
149
+ return path.split('.').reduce((current, key) => {
150
+ if (current && typeof current === 'object') {
151
+ return current[key];
152
+ }
153
+ return undefined;
154
+ }, obj);
155
+ }
156
+ function evaluateCondition(condition, context) {
157
+ if (typeof condition !== 'object' || condition === null) {
158
+ return Boolean(condition);
159
+ }
160
+ const cond = condition;
161
+ // Binding condition - check truthiness
162
+ if ('$bind' in cond) {
163
+ const value = resolveBinding(cond.$bind, context);
164
+ return Boolean(value);
165
+ }
166
+ // Comparison condition
167
+ if ('op' in cond && 'left' in cond && 'right' in cond) {
168
+ const left = resolveBindingValue(cond.left, context);
169
+ const right = resolveBindingValue(cond.right, context);
170
+ switch (cond.op) {
171
+ case 'eq':
172
+ return left === right;
173
+ case 'neq':
174
+ return left !== right;
175
+ case 'gt':
176
+ return left > right;
177
+ case 'gte':
178
+ return left >= right;
179
+ case 'lt':
180
+ return left < right;
181
+ case 'lte':
182
+ return left <= right;
183
+ case 'contains':
184
+ if (Array.isArray(left)) {
185
+ return left.includes(right);
186
+ }
187
+ if (typeof left === 'string') {
188
+ return left.includes(String(right));
189
+ }
190
+ return false;
191
+ default:
192
+ return false;
193
+ }
194
+ }
195
+ // Logical conditions
196
+ if ('and' in cond) {
197
+ const conditions = cond.and;
198
+ return conditions.every((c) => evaluateCondition(c, context));
199
+ }
200
+ if ('or' in cond) {
201
+ const conditions = cond.or;
202
+ return conditions.some((c) => evaluateCondition(c, context));
203
+ }
204
+ if ('not' in cond) {
205
+ return !evaluateCondition(cond.not, context);
206
+ }
207
+ return false;
208
+ }
209
+ function resolveBindingValue(value, context) {
210
+ if (typeof value === 'object' && value !== null && '$bind' in value) {
211
+ return resolveBinding(value.$bind, context);
212
+ }
213
+ return value;
214
+ }
215
+ // Export evaluation helpers for use in renderer
216
+ export { evaluateCondition, resolveBinding, getNestedValue };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * StateManager
3
+ *
4
+ * Manages local spec state with React integration.
5
+ */
6
+ import type { StateConfig } from '../types';
7
+ export type StateSnapshot = Record<string, unknown>;
8
+ export interface StateManager {
9
+ getSnapshot(): StateSnapshot;
10
+ subscribe(listener: () => void): () => void;
11
+ setState(key: string, value: unknown): void;
12
+ getState(key: string): unknown;
13
+ reset(): void;
14
+ }
15
+ /**
16
+ * Creates a state manager for spec local state.
17
+ */
18
+ export declare function createStateManager(initial?: StateConfig['local']): StateManager;
19
+ /**
20
+ * Hook to use spec state in components.
21
+ */
22
+ export declare function useSpecState(manager: StateManager): StateSnapshot;
23
+ /**
24
+ * Hook to get a specific state value.
25
+ */
26
+ export declare function useSpecStateValue<T = unknown>(manager: StateManager, key: string): T;
27
+ /**
28
+ * Hook to get a state setter.
29
+ */
30
+ export declare function useSpecStateSetter(manager: StateManager): (key: string, value: unknown) => void;
31
+ //# sourceMappingURL=StateManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateManager.d.ts","sourceRoot":"","sources":["../../../src/spec/runtime/StateManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,WAAW,IAAI,aAAa,CAAC;IAC7B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,WAAW,CAAC,OAAO,CAAM,GAAG,YAAY,CAkCnF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAMjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CAGpF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAO/F"}