@codellyson/framely 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.
Files changed (209) hide show
  1. package/dist/AbsoluteFill.d.ts +18 -0
  2. package/dist/AbsoluteFill.d.ts.map +1 -0
  3. package/dist/AbsoluteFill.js +25 -0
  4. package/dist/AbsoluteFill.js.map +1 -0
  5. package/dist/Audio.d.ts +80 -0
  6. package/dist/Audio.d.ts.map +1 -0
  7. package/dist/Audio.js +221 -0
  8. package/dist/Audio.js.map +1 -0
  9. package/dist/Composition.d.ts +208 -0
  10. package/dist/Composition.d.ts.map +1 -0
  11. package/dist/Composition.js +210 -0
  12. package/dist/Composition.js.map +1 -0
  13. package/dist/Easing.d.ts +88 -0
  14. package/dist/Easing.d.ts.map +1 -0
  15. package/dist/Easing.js +266 -0
  16. package/dist/Easing.js.map +1 -0
  17. package/dist/ErrorBoundary.d.ts +35 -0
  18. package/dist/ErrorBoundary.d.ts.map +1 -0
  19. package/dist/ErrorBoundary.js +74 -0
  20. package/dist/ErrorBoundary.js.map +1 -0
  21. package/dist/Folder.d.ts +46 -0
  22. package/dist/Folder.d.ts.map +1 -0
  23. package/dist/Folder.js +44 -0
  24. package/dist/Folder.js.map +1 -0
  25. package/dist/Freeze.d.ts +35 -0
  26. package/dist/Freeze.d.ts.map +1 -0
  27. package/dist/Freeze.js +40 -0
  28. package/dist/Freeze.js.map +1 -0
  29. package/dist/IFrame.d.ts +28 -0
  30. package/dist/IFrame.d.ts.map +1 -0
  31. package/dist/IFrame.js +57 -0
  32. package/dist/IFrame.js.map +1 -0
  33. package/dist/Img.d.ts +36 -0
  34. package/dist/Img.d.ts.map +1 -0
  35. package/dist/Img.js +91 -0
  36. package/dist/Img.js.map +1 -0
  37. package/dist/Loop.d.ts +66 -0
  38. package/dist/Loop.d.ts.map +1 -0
  39. package/dist/Loop.js +79 -0
  40. package/dist/Loop.js.map +1 -0
  41. package/dist/Player.d.ts +118 -0
  42. package/dist/Player.d.ts.map +1 -0
  43. package/dist/Player.js +311 -0
  44. package/dist/Player.js.map +1 -0
  45. package/dist/Sequence.d.ts +25 -0
  46. package/dist/Sequence.d.ts.map +1 -0
  47. package/dist/Sequence.js +37 -0
  48. package/dist/Sequence.js.map +1 -0
  49. package/dist/Series.d.ts +52 -0
  50. package/dist/Series.d.ts.map +1 -0
  51. package/dist/Series.js +86 -0
  52. package/dist/Series.js.map +1 -0
  53. package/dist/Text.d.ts +129 -0
  54. package/dist/Text.d.ts.map +1 -0
  55. package/dist/Text.js +211 -0
  56. package/dist/Text.js.map +1 -0
  57. package/dist/Video.d.ts +75 -0
  58. package/dist/Video.d.ts.map +1 -0
  59. package/dist/Video.js +136 -0
  60. package/dist/Video.js.map +1 -0
  61. package/dist/config.d.ts +128 -0
  62. package/dist/config.d.ts.map +1 -0
  63. package/dist/config.js +243 -0
  64. package/dist/config.js.map +1 -0
  65. package/dist/context.d.ts +84 -0
  66. package/dist/context.d.ts.map +1 -0
  67. package/dist/context.js +131 -0
  68. package/dist/context.js.map +1 -0
  69. package/dist/delayRender.d.ts +130 -0
  70. package/dist/delayRender.d.ts.map +1 -0
  71. package/dist/delayRender.js +197 -0
  72. package/dist/delayRender.js.map +1 -0
  73. package/dist/getInputProps.d.ts +118 -0
  74. package/dist/getInputProps.d.ts.map +1 -0
  75. package/dist/getInputProps.js +181 -0
  76. package/dist/getInputProps.js.map +1 -0
  77. package/dist/hooks/useDelayRender.d.ts +52 -0
  78. package/dist/hooks/useDelayRender.d.ts.map +1 -0
  79. package/dist/hooks/useDelayRender.js +92 -0
  80. package/dist/hooks/useDelayRender.js.map +1 -0
  81. package/dist/hooks.d.ts +19 -0
  82. package/dist/hooks.d.ts.map +1 -0
  83. package/dist/hooks.js +17 -0
  84. package/dist/hooks.js.map +1 -0
  85. package/dist/index.d.ts +71 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +65 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/interpolate.d.ts +80 -0
  90. package/dist/interpolate.d.ts.map +1 -0
  91. package/dist/interpolate.js +108 -0
  92. package/dist/interpolate.js.map +1 -0
  93. package/dist/interpolateColors.d.ts +50 -0
  94. package/dist/interpolateColors.d.ts.map +1 -0
  95. package/dist/interpolateColors.js +300 -0
  96. package/dist/interpolateColors.js.map +1 -0
  97. package/dist/makeTransform.d.ts +98 -0
  98. package/dist/makeTransform.d.ts.map +1 -0
  99. package/dist/makeTransform.js +287 -0
  100. package/dist/makeTransform.js.map +1 -0
  101. package/dist/measureSpring.d.ts +75 -0
  102. package/dist/measureSpring.d.ts.map +1 -0
  103. package/dist/measureSpring.js +108 -0
  104. package/dist/measureSpring.js.map +1 -0
  105. package/dist/noise.d.ts +110 -0
  106. package/dist/noise.d.ts.map +1 -0
  107. package/dist/noise.js +228 -0
  108. package/dist/noise.js.map +1 -0
  109. package/dist/preload.d.ts +145 -0
  110. package/dist/preload.d.ts.map +1 -0
  111. package/dist/preload.js +225 -0
  112. package/dist/preload.js.map +1 -0
  113. package/dist/registerRoot.d.ts +140 -0
  114. package/dist/registerRoot.d.ts.map +1 -0
  115. package/dist/registerRoot.js +238 -0
  116. package/dist/registerRoot.js.map +1 -0
  117. package/dist/shapes/Circle.d.ts +15 -0
  118. package/dist/shapes/Circle.d.ts.map +1 -0
  119. package/dist/shapes/Circle.js +11 -0
  120. package/dist/shapes/Circle.js.map +1 -0
  121. package/dist/shapes/Ellipse.d.ts +16 -0
  122. package/dist/shapes/Ellipse.d.ts.map +1 -0
  123. package/dist/shapes/Ellipse.js +11 -0
  124. package/dist/shapes/Ellipse.js.map +1 -0
  125. package/dist/shapes/Line.d.ts +15 -0
  126. package/dist/shapes/Line.d.ts.map +1 -0
  127. package/dist/shapes/Line.js +11 -0
  128. package/dist/shapes/Line.js.map +1 -0
  129. package/dist/shapes/Path.d.ts +20 -0
  130. package/dist/shapes/Path.d.ts.map +1 -0
  131. package/dist/shapes/Path.js +14 -0
  132. package/dist/shapes/Path.js.map +1 -0
  133. package/dist/shapes/Polygon.d.ts +15 -0
  134. package/dist/shapes/Polygon.d.ts.map +1 -0
  135. package/dist/shapes/Polygon.js +16 -0
  136. package/dist/shapes/Polygon.js.map +1 -0
  137. package/dist/shapes/Rect.d.ts +18 -0
  138. package/dist/shapes/Rect.d.ts.map +1 -0
  139. package/dist/shapes/Rect.js +11 -0
  140. package/dist/shapes/Rect.js.map +1 -0
  141. package/dist/shapes/Svg.d.ts +16 -0
  142. package/dist/shapes/Svg.d.ts.map +1 -0
  143. package/dist/shapes/Svg.js +15 -0
  144. package/dist/shapes/Svg.js.map +1 -0
  145. package/dist/shapes/index.d.ts +16 -0
  146. package/dist/shapes/index.d.ts.map +1 -0
  147. package/dist/shapes/index.js +9 -0
  148. package/dist/shapes/index.js.map +1 -0
  149. package/dist/shapes/usePathLength.d.ts +24 -0
  150. package/dist/shapes/usePathLength.d.ts.map +1 -0
  151. package/dist/shapes/usePathLength.js +32 -0
  152. package/dist/shapes/usePathLength.js.map +1 -0
  153. package/dist/staticFile.d.ts +47 -0
  154. package/dist/staticFile.d.ts.map +1 -0
  155. package/dist/staticFile.js +105 -0
  156. package/dist/staticFile.js.map +1 -0
  157. package/dist/templates/api.d.ts +26 -0
  158. package/dist/templates/api.d.ts.map +1 -0
  159. package/dist/templates/api.js +142 -0
  160. package/dist/templates/api.js.map +1 -0
  161. package/dist/templates/index.d.ts +7 -0
  162. package/dist/templates/index.d.ts.map +1 -0
  163. package/dist/templates/index.js +7 -0
  164. package/dist/templates/index.js.map +1 -0
  165. package/dist/templates/mockData.d.ts +7 -0
  166. package/dist/templates/mockData.d.ts.map +1 -0
  167. package/dist/templates/mockData.js +262 -0
  168. package/dist/templates/mockData.js.map +1 -0
  169. package/dist/templates/types.d.ts +104 -0
  170. package/dist/templates/types.d.ts.map +1 -0
  171. package/dist/templates/types.js +16 -0
  172. package/dist/templates/types.js.map +1 -0
  173. package/dist/transitions/TransitionSeries.d.ts +127 -0
  174. package/dist/transitions/TransitionSeries.d.ts.map +1 -0
  175. package/dist/transitions/TransitionSeries.js +190 -0
  176. package/dist/transitions/TransitionSeries.js.map +1 -0
  177. package/dist/transitions/index.d.ts +52 -0
  178. package/dist/transitions/index.d.ts.map +1 -0
  179. package/dist/transitions/index.js +31 -0
  180. package/dist/transitions/index.js.map +1 -0
  181. package/dist/transitions/presets/fade.d.ts +45 -0
  182. package/dist/transitions/presets/fade.d.ts.map +1 -0
  183. package/dist/transitions/presets/fade.js +56 -0
  184. package/dist/transitions/presets/fade.js.map +1 -0
  185. package/dist/transitions/presets/flip.d.ts +99 -0
  186. package/dist/transitions/presets/flip.d.ts.map +1 -0
  187. package/dist/transitions/presets/flip.js +153 -0
  188. package/dist/transitions/presets/flip.js.map +1 -0
  189. package/dist/transitions/presets/slide.d.ts +61 -0
  190. package/dist/transitions/presets/slide.d.ts.map +1 -0
  191. package/dist/transitions/presets/slide.js +116 -0
  192. package/dist/transitions/presets/slide.js.map +1 -0
  193. package/dist/transitions/presets/wipe.d.ts +69 -0
  194. package/dist/transitions/presets/wipe.d.ts.map +1 -0
  195. package/dist/transitions/presets/wipe.js +136 -0
  196. package/dist/transitions/presets/wipe.js.map +1 -0
  197. package/dist/transitions/presets/zoom.d.ts +76 -0
  198. package/dist/transitions/presets/zoom.d.ts.map +1 -0
  199. package/dist/transitions/presets/zoom.js +110 -0
  200. package/dist/transitions/presets/zoom.js.map +1 -0
  201. package/dist/useAudioData.d.ts +112 -0
  202. package/dist/useAudioData.d.ts.map +1 -0
  203. package/dist/useAudioData.js +183 -0
  204. package/dist/useAudioData.js.map +1 -0
  205. package/dist/useSpring.d.ts +79 -0
  206. package/dist/useSpring.d.ts.map +1 -0
  207. package/dist/useSpring.js +140 -0
  208. package/dist/useSpring.js.map +1 -0
  209. package/package.json +51 -0
package/dist/Series.js ADDED
@@ -0,0 +1,86 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo, Children, createContext, useContext, } from 'react';
3
+ import TimelineContext, { useTimeline } from './context';
4
+ /**
5
+ * Context for Series.Sequence to access its calculated timing.
6
+ */
7
+ const SeriesContext = createContext(null);
8
+ /**
9
+ * Series renders children sequentially, auto-calculating `from` for each.
10
+ *
11
+ * Unlike Sequence (which requires explicit `from` values), Series automatically
12
+ * places each child one after another in the timeline.
13
+ *
14
+ * Usage:
15
+ * <Series>
16
+ * <Series.Sequence durationInFrames={60}>
17
+ * <Scene1 />
18
+ * </Series.Sequence>
19
+ * <Series.Sequence durationInFrames={90}>
20
+ * <Scene2 />
21
+ * </Series.Sequence>
22
+ * </Series>
23
+ */
24
+ const Series = function Series({ children }) {
25
+ const parent = useTimeline();
26
+ const childArray = Children.toArray(children);
27
+ // Calculate cumulative offsets for each child
28
+ const timings = useMemo(() => {
29
+ let currentOffset = 0;
30
+ return childArray.map((child) => {
31
+ const duration = child.props?.durationInFrames ?? 0;
32
+ const offset = child.props?.offset ?? 0;
33
+ const from = currentOffset + offset;
34
+ currentOffset = from + duration;
35
+ return { from, duration };
36
+ });
37
+ }, [childArray]);
38
+ return (_jsx(_Fragment, { children: childArray.map((child, index) => {
39
+ const { from, duration } = timings[index];
40
+ return (_jsx(SeriesContext.Provider, { value: { from, duration, index }, children: child }, index));
41
+ }) }));
42
+ };
43
+ /**
44
+ * Series.Sequence represents a scene within a Series.
45
+ *
46
+ * Props:
47
+ * durationInFrames - How many frames this sequence lasts (required unless last)
48
+ * offset - Adjust timing: positive = delay, negative = overlap with previous
49
+ * layout - 'absolute-fill' (default) or 'none'
50
+ * style - Additional CSS styles
51
+ * name - Optional label for debugging
52
+ */
53
+ Series.Sequence = function SeriesSequence({ durationInFrames, offset = 0, layout = 'absolute-fill', style, name, children, }) {
54
+ const parent = useTimeline();
55
+ const seriesContext = useContext(SeriesContext);
56
+ if (!seriesContext) {
57
+ throw new Error('Series.Sequence must be used within a Series');
58
+ }
59
+ const { from, duration } = seriesContext;
60
+ const actualDuration = durationInFrames ?? duration;
61
+ // Calculate the child's local frame
62
+ const localFrame = parent.frame - from;
63
+ const contextValue = useMemo(() => ({
64
+ ...parent,
65
+ frame: localFrame,
66
+ durationInFrames: actualDuration,
67
+ }), [parent, localFrame, actualDuration]);
68
+ const containerStyle = layout === 'absolute-fill'
69
+ ? {
70
+ position: 'absolute',
71
+ top: 0,
72
+ left: 0,
73
+ right: 0,
74
+ bottom: 0,
75
+ ...style,
76
+ }
77
+ : style;
78
+ // Don't render if we're outside this sequence's range
79
+ if (localFrame < 0 || localFrame >= actualDuration) {
80
+ return null;
81
+ }
82
+ return (_jsx(TimelineContext.Provider, { value: contextValue, children: _jsx("div", { style: containerStyle, "data-series-sequence-name": name, children: children }) }));
83
+ };
84
+ export { Series };
85
+ export default Series;
86
+ //# sourceMappingURL=Series.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Series.js","sourceRoot":"","sources":["../src/Series.tsx"],"names":[],"mappings":"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"}
package/dist/Text.d.ts ADDED
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Text Component
3
+ *
4
+ * Motion-graphics-grade text with character-level animation support.
5
+ * Automatically waits for font loading via delayRender.
6
+ */
7
+ import React from 'react';
8
+ /** Props accepted by the per-character `<Span>` component. */
9
+ export interface CharSpanProps {
10
+ style?: React.CSSProperties;
11
+ className?: string;
12
+ }
13
+ /** Metadata for a single character produced by `splitText`. */
14
+ export interface CharData {
15
+ char: string;
16
+ index: number;
17
+ wordIndex: number;
18
+ lineIndex: number;
19
+ isWhitespace: boolean;
20
+ isNewline: boolean;
21
+ /** A component that renders this character inside a styleable `<span>`. */
22
+ Span: React.ComponentType<CharSpanProps>;
23
+ }
24
+ /** Text stroke configuration. */
25
+ export interface StrokeConfig {
26
+ color: string;
27
+ width: number;
28
+ }
29
+ /** Text shadow configuration. */
30
+ export interface ShadowConfig {
31
+ color: string;
32
+ blur?: number;
33
+ x?: number;
34
+ y?: number;
35
+ }
36
+ /** Word boundary descriptor returned by `getWords`. */
37
+ export interface WordData {
38
+ word: string;
39
+ startIndex: number;
40
+ endIndex: number;
41
+ wordIndex: number;
42
+ }
43
+ /** Props for the `<Text>` component. */
44
+ export interface TextProps {
45
+ /** The text to render. */
46
+ text: string;
47
+ /** Font size in pixels. @default 40 */
48
+ fontSize?: number;
49
+ /** Font family. @default 'sans-serif' */
50
+ fontFamily?: string;
51
+ /** Font weight. @default 400 */
52
+ fontWeight?: number | string;
53
+ /** Text color. @default 'white' */
54
+ color?: string;
55
+ /** Letter spacing in pixels. @default 0 */
56
+ letterSpacing?: number;
57
+ /** Line height multiplier. @default 1.2 */
58
+ lineHeight?: number;
59
+ /** Text alignment. @default 'left' */
60
+ textAlign?: 'left' | 'center' | 'right';
61
+ /** CSS text-transform value. @default 'none' */
62
+ textTransform?: React.CSSProperties['textTransform'];
63
+ /** CSS white-space value. @default 'pre-wrap' */
64
+ whiteSpace?: React.CSSProperties['whiteSpace'];
65
+ /** Text stroke configuration. */
66
+ stroke?: StrokeConfig;
67
+ /** Text shadow configuration. */
68
+ shadow?: ShadowConfig;
69
+ /** Additional container styles. */
70
+ style?: React.CSSProperties;
71
+ /** Container class name. */
72
+ className?: string;
73
+ /**
74
+ * Render function receiving character data.
75
+ *
76
+ * When provided as a function the component splits the text into
77
+ * individually styleable `<Span>` components, enabling per-character
78
+ * animation.
79
+ */
80
+ children?: (chars: CharData[]) => React.ReactNode;
81
+ }
82
+ interface RawCharData {
83
+ char: string;
84
+ index: number;
85
+ wordIndex: number;
86
+ lineIndex: number;
87
+ isWhitespace: boolean;
88
+ isNewline: boolean;
89
+ }
90
+ /**
91
+ * Split text into character, word, and line metadata.
92
+ *
93
+ * @param text - Text to split
94
+ * @returns Array of per-character metadata objects
95
+ */
96
+ export declare function splitText(text: string): RawCharData[];
97
+ /**
98
+ * Get word boundaries from split text.
99
+ *
100
+ * @param chars - Output of `splitText()`
101
+ * @returns Array of word boundary descriptors
102
+ */
103
+ export declare function getWords(chars: RawCharData[]): WordData[];
104
+ /**
105
+ * Text component with character-level animation.
106
+ *
107
+ * Renders text with each character wrapped in an individually styleable span.
108
+ * Waits for fonts to load before rendering (via delayRender).
109
+ *
110
+ * @example
111
+ * // Simple text
112
+ * <Text text="Hello World" fontSize={80} color="white" />
113
+ *
114
+ * @example
115
+ * // Character-by-character animation
116
+ * <Text text="Animate Me" fontSize={60} fontFamily="Inter">
117
+ * {(chars) => chars.map((c, i) => (
118
+ * <c.Span key={i} style={{
119
+ * opacity: interpolate(frame, [i * 2, i * 2 + 10], [0, 1], { extrapolateRight: 'clamp' }),
120
+ * }} />
121
+ * ))}
122
+ * </Text>
123
+ */
124
+ export declare function Text({ text, fontSize, fontFamily, fontWeight, color, letterSpacing, lineHeight, textAlign, textTransform, whiteSpace, stroke, shadow, style, className, children, }: TextProps): React.ReactElement;
125
+ export declare namespace Text {
126
+ var displayName: string;
127
+ }
128
+ export default Text;
129
+ //# sourceMappingURL=Text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Text.d.ts","sourceRoot":"","sources":["../src/Text.tsx"],"names":[],"mappings":"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"}
package/dist/Text.js ADDED
@@ -0,0 +1,211 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Text Component
4
+ *
5
+ * Motion-graphics-grade text with character-level animation support.
6
+ * Automatically waits for font loading via delayRender.
7
+ */
8
+ import { useEffect, useRef, useMemo } from 'react';
9
+ import { delayRender, continueRender } from './delayRender';
10
+ // ---------------------------------------------------------------------------
11
+ // Utility functions
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Split text into character, word, and line metadata.
15
+ *
16
+ * @param text - Text to split
17
+ * @returns Array of per-character metadata objects
18
+ */
19
+ export function splitText(text) {
20
+ const chars = [];
21
+ let wordIndex = 0;
22
+ let lineIndex = 0;
23
+ for (let i = 0; i < text.length; i++) {
24
+ const char = text[i];
25
+ const isNewline = char === '\n';
26
+ const isWhitespace = /\s/.test(char);
27
+ chars.push({
28
+ char,
29
+ index: i,
30
+ wordIndex,
31
+ lineIndex,
32
+ isWhitespace,
33
+ isNewline,
34
+ });
35
+ if (isNewline) {
36
+ lineIndex++;
37
+ wordIndex++;
38
+ }
39
+ else if (isWhitespace) {
40
+ wordIndex++;
41
+ }
42
+ }
43
+ return chars;
44
+ }
45
+ /**
46
+ * Get word boundaries from split text.
47
+ *
48
+ * @param chars - Output of `splitText()`
49
+ * @returns Array of word boundary descriptors
50
+ */
51
+ export function getWords(chars) {
52
+ const words = [];
53
+ let currentWord = '';
54
+ let startIndex = 0;
55
+ let currentWordIndex = -1;
56
+ for (const c of chars) {
57
+ if (c.isWhitespace || c.isNewline) {
58
+ if (currentWord.length > 0) {
59
+ words.push({
60
+ word: currentWord,
61
+ startIndex,
62
+ endIndex: c.index - 1,
63
+ wordIndex: currentWordIndex,
64
+ });
65
+ currentWord = '';
66
+ }
67
+ }
68
+ else {
69
+ if (currentWord.length === 0) {
70
+ startIndex = c.index;
71
+ currentWordIndex = c.wordIndex;
72
+ }
73
+ currentWord += c.char;
74
+ }
75
+ }
76
+ if (currentWord.length > 0) {
77
+ words.push({
78
+ word: currentWord,
79
+ startIndex,
80
+ endIndex: chars[chars.length - 1].index,
81
+ wordIndex: currentWordIndex,
82
+ });
83
+ }
84
+ return words;
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // Internal helpers
88
+ // ---------------------------------------------------------------------------
89
+ /** Generic font families that never need to be loaded. */
90
+ const GENERIC_FONTS = new Set([
91
+ 'sans-serif',
92
+ 'serif',
93
+ 'monospace',
94
+ ]);
95
+ /**
96
+ * Create a character span component for per-character styling.
97
+ */
98
+ function createCharSpan(charInfo, _baseStyle) {
99
+ const CharSpan = ({ style: charStyle, className: charClassName, }) => {
100
+ if (charInfo.isNewline) {
101
+ return _jsx("br", {});
102
+ }
103
+ return (_jsx("span", { className: charClassName, style: {
104
+ display: 'inline-block',
105
+ whiteSpace: charInfo.isWhitespace ? 'pre' : undefined,
106
+ ...charStyle,
107
+ }, children: charInfo.char }));
108
+ };
109
+ CharSpan.displayName = `Char(${charInfo.char === ' ' ? 'space' : charInfo.char})`;
110
+ return CharSpan;
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Text component
114
+ // ---------------------------------------------------------------------------
115
+ /**
116
+ * Text component with character-level animation.
117
+ *
118
+ * Renders text with each character wrapped in an individually styleable span.
119
+ * Waits for fonts to load before rendering (via delayRender).
120
+ *
121
+ * @example
122
+ * // Simple text
123
+ * <Text text="Hello World" fontSize={80} color="white" />
124
+ *
125
+ * @example
126
+ * // Character-by-character animation
127
+ * <Text text="Animate Me" fontSize={60} fontFamily="Inter">
128
+ * {(chars) => chars.map((c, i) => (
129
+ * <c.Span key={i} style={{
130
+ * opacity: interpolate(frame, [i * 2, i * 2 + 10], [0, 1], { extrapolateRight: 'clamp' }),
131
+ * }} />
132
+ * ))}
133
+ * </Text>
134
+ */
135
+ export function Text({ text, fontSize = 40, fontFamily = 'sans-serif', fontWeight = 400, color = 'white', letterSpacing = 0, lineHeight = 1.2, textAlign = 'left', textTransform = 'none', whiteSpace = 'pre-wrap', stroke, shadow, style, className, children, }) {
136
+ const delayHandle = useRef(null);
137
+ // Wait for font to load
138
+ useEffect(() => {
139
+ if (GENERIC_FONTS.has(fontFamily)) {
140
+ return;
141
+ }
142
+ const handle = delayRender(`Loading font: ${fontFamily}`);
143
+ delayHandle.current = handle;
144
+ document.fonts.ready
145
+ .then(() => {
146
+ continueRender(handle);
147
+ delayHandle.current = null;
148
+ })
149
+ .catch(() => {
150
+ // Continue even if font check fails
151
+ continueRender(handle);
152
+ delayHandle.current = null;
153
+ });
154
+ return () => {
155
+ if (delayHandle.current !== null) {
156
+ continueRender(delayHandle.current);
157
+ delayHandle.current = null;
158
+ }
159
+ };
160
+ }, [fontFamily]);
161
+ // Build base styles
162
+ const baseStyle = useMemo(() => {
163
+ const s = {
164
+ fontSize,
165
+ fontFamily,
166
+ fontWeight,
167
+ color,
168
+ letterSpacing: letterSpacing !== 0 ? `${letterSpacing}px` : undefined,
169
+ lineHeight,
170
+ textAlign,
171
+ textTransform,
172
+ whiteSpace,
173
+ margin: 0,
174
+ padding: 0,
175
+ ...style,
176
+ };
177
+ if (stroke) {
178
+ s.WebkitTextStroke = `${stroke.width}px ${stroke.color}`;
179
+ }
180
+ if (shadow) {
181
+ s.textShadow = `${shadow.x || 0}px ${shadow.y || 0}px ${shadow.blur || 0}px ${shadow.color}`;
182
+ }
183
+ return s;
184
+ }, [
185
+ fontSize, fontFamily, fontWeight, color, letterSpacing,
186
+ lineHeight, textAlign, textTransform, whiteSpace,
187
+ stroke?.color, stroke?.width,
188
+ shadow?.color, shadow?.blur, shadow?.x, shadow?.y,
189
+ style,
190
+ ]);
191
+ // Split text and create character data with Span components
192
+ const charData = useMemo(() => {
193
+ const chars = splitText(text);
194
+ return chars.map((c) => ({
195
+ ...c,
196
+ Span: createCharSpan(c, baseStyle),
197
+ }));
198
+ }, [text, baseStyle]);
199
+ // If no children function, render simple text
200
+ if (typeof children !== 'function') {
201
+ return (_jsx("div", { className: className, style: baseStyle, children: text }));
202
+ }
203
+ return (_jsx("div", { className: className, style: {
204
+ ...baseStyle,
205
+ // Reset text properties on container — chars handle their own
206
+ display: 'inline',
207
+ }, children: children(charData) }));
208
+ }
209
+ Text.displayName = 'Text';
210
+ export default Text;
211
+ //# sourceMappingURL=Text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Text.js","sourceRoot":"","sources":["../src/Text.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { type CSSProperties, type SyntheticEvent } from 'react';
2
+ /**
3
+ * A volume value can be a static number (0-1) or a callback that receives the
4
+ * current frame and returns a number (0-1). The callback form is useful for
5
+ * animating volume over time (e.g. fade-in/fade-out).
6
+ */
7
+ export type VolumeCallback = (frame: number) => number;
8
+ /**
9
+ * Props accepted by the Video component.
10
+ *
11
+ * Extends the standard HTML `<video>` attributes so that any native prop
12
+ * (e.g. `crossOrigin`, `poster`, `preload`) can be forwarded directly.
13
+ */
14
+ export interface VideoProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'volume' | 'playbackRate' | 'muted' | 'loop' | 'onError'> {
15
+ /** Video source URL. Use `staticFile()` for local assets. */
16
+ src: string;
17
+ /**
18
+ * Volume level (0-1), or a callback `(frame) => number` for dynamic volume.
19
+ * Clamped to the 0-1 range at runtime.
20
+ * @default 1
21
+ */
22
+ volume?: number | VolumeCallback;
23
+ /**
24
+ * Playback speed multiplier.
25
+ * @default 1
26
+ */
27
+ playbackRate?: number;
28
+ /**
29
+ * Whether to mute audio.
30
+ * @default false
31
+ */
32
+ muted?: boolean;
33
+ /**
34
+ * Whether the video should loop.
35
+ * @default false
36
+ */
37
+ loop?: boolean;
38
+ /**
39
+ * Frame index from which the video should start playing (0-based).
40
+ * @default 0
41
+ */
42
+ startFrom?: number;
43
+ /**
44
+ * Frame index at which the video should stop (exclusive).
45
+ * If omitted the video plays until its natural end.
46
+ */
47
+ endAt?: number;
48
+ /** Optional CSS styles applied to the `<video>` element. */
49
+ style?: CSSProperties;
50
+ /** Optional CSS class name applied to the `<video>` element. */
51
+ className?: string;
52
+ /** Called when the video element encounters an error. */
53
+ onError?: (event: SyntheticEvent<HTMLVideoElement, Event>) => void;
54
+ }
55
+ /**
56
+ * Video component that syncs with the Framely timeline.
57
+ *
58
+ * The video's playback position is controlled by the current frame,
59
+ * ensuring perfect sync between the video and your composition.
60
+ *
61
+ * Usage:
62
+ * import { Video, staticFile } from './lib';
63
+ *
64
+ * <Video src={staticFile('videos/intro.mp4')} />
65
+ * <Video src={staticFile('videos/bg.mp4')} volume={0.5} muted />
66
+ * <Video
67
+ * src={staticFile('videos/clip.mp4')}
68
+ * startFrom={30}
69
+ * endAt={90}
70
+ * volume={(frame) => interpolate(frame, [0, 30], [0, 1])}
71
+ * />
72
+ */
73
+ export declare function Video({ src, volume, playbackRate, muted, loop, startFrom, endAt, style, className, onError, ...rest }: VideoProps): JSX.Element | null;
74
+ export default Video;
75
+ //# sourceMappingURL=Video.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Video.d.ts","sourceRoot":"","sources":["../src/Video.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAMf;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,UACf,SAAQ,IAAI,CACV,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,QAAQ,GAAG,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CACzD;IACD,6DAA6D;IAC7D,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IAEjC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,4DAA4D;IAC5D,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yDAAyD;IACzD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;CACpE;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,KAAK,CAAC,EACpB,GAAG,EACH,MAAU,EACV,YAAgB,EAChB,KAAa,EACb,IAAY,EACZ,SAAa,EACb,KAAK,EACL,KAAK,EACL,SAAS,EACT,OAAO,EACP,GAAG,IAAI,EACR,EAAE,UAAU,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAmIjC;AAED,eAAe,KAAK,CAAC"}
package/dist/Video.js ADDED
@@ -0,0 +1,136 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState, useMemo, } from 'react';
3
+ import { useTimeline } from './context';
4
+ import { delayRender, continueRender, cancelRender } from './delayRender';
5
+ // ─── Component ────────────────────────────────────────────────────────────────
6
+ /**
7
+ * Video component that syncs with the Framely timeline.
8
+ *
9
+ * The video's playback position is controlled by the current frame,
10
+ * ensuring perfect sync between the video and your composition.
11
+ *
12
+ * Usage:
13
+ * import { Video, staticFile } from './lib';
14
+ *
15
+ * <Video src={staticFile('videos/intro.mp4')} />
16
+ * <Video src={staticFile('videos/bg.mp4')} volume={0.5} muted />
17
+ * <Video
18
+ * src={staticFile('videos/clip.mp4')}
19
+ * startFrom={30}
20
+ * endAt={90}
21
+ * volume={(frame) => interpolate(frame, [0, 30], [0, 1])}
22
+ * />
23
+ */
24
+ export function Video({ src, volume = 1, playbackRate = 1, muted = false, loop = false, startFrom = 0, endAt, style, className, onError, ...rest }) {
25
+ const { frame, fps, playing } = useTimeline();
26
+ const videoRef = useRef(null);
27
+ const handleRef = useRef(null);
28
+ const [ready, setReady] = useState(false);
29
+ // Calculate the actual volume (support callback)
30
+ const actualVolume = useMemo(() => {
31
+ if (typeof volume === 'function') {
32
+ return Math.max(0, Math.min(1, volume(frame)));
33
+ }
34
+ return Math.max(0, Math.min(1, volume));
35
+ }, [volume, frame]);
36
+ // Calculate target time based on frame
37
+ const targetTime = useMemo(() => {
38
+ const relativeFrame = frame - startFrom;
39
+ if (relativeFrame < 0)
40
+ return 0;
41
+ return relativeFrame / fps;
42
+ }, [frame, startFrom, fps]);
43
+ // Check if we're within the video's active range
44
+ const isActive = useMemo(() => {
45
+ if (frame < startFrom)
46
+ return false;
47
+ if (endAt !== undefined && frame >= endAt)
48
+ return false;
49
+ return true;
50
+ }, [frame, startFrom, endAt]);
51
+ // Delay render until video is ready
52
+ useEffect(() => {
53
+ handleRef.current = delayRender(`Loading video: ${src}`, {
54
+ timeoutInMilliseconds: 30000,
55
+ });
56
+ return () => {
57
+ if (handleRef.current !== null) {
58
+ continueRender(handleRef.current);
59
+ handleRef.current = null;
60
+ }
61
+ };
62
+ }, [src]);
63
+ // Handle video ready state
64
+ const handleCanPlay = () => {
65
+ setReady(true);
66
+ if (handleRef.current !== null) {
67
+ continueRender(handleRef.current);
68
+ handleRef.current = null;
69
+ }
70
+ };
71
+ // Handle video error
72
+ const handleError = (event) => {
73
+ const err = new Error(`Failed to load video: ${src}`);
74
+ onError?.(event);
75
+ if (handleRef.current !== null) {
76
+ cancelRender(err);
77
+ handleRef.current = null;
78
+ }
79
+ };
80
+ // Sync video time with frame
81
+ useEffect(() => {
82
+ const video = videoRef.current;
83
+ if (!video || !ready)
84
+ return;
85
+ // Only seek if we're more than half a frame off
86
+ const tolerance = 0.5 / fps;
87
+ if (Math.abs(video.currentTime - targetTime) > tolerance) {
88
+ video.currentTime = targetTime;
89
+ }
90
+ }, [targetTime, ready, fps]);
91
+ // Sync volume
92
+ useEffect(() => {
93
+ const video = videoRef.current;
94
+ if (!video)
95
+ return;
96
+ video.volume = actualVolume;
97
+ }, [actualVolume]);
98
+ // Sync playback rate
99
+ useEffect(() => {
100
+ const video = videoRef.current;
101
+ if (!video)
102
+ return;
103
+ video.playbackRate = playbackRate;
104
+ }, [playbackRate]);
105
+ // Sync muted state
106
+ useEffect(() => {
107
+ const video = videoRef.current;
108
+ if (!video)
109
+ return;
110
+ video.muted = muted;
111
+ }, [muted]);
112
+ // Handle play/pause (for preview mode)
113
+ useEffect(() => {
114
+ const video = videoRef.current;
115
+ if (!video || !ready)
116
+ return;
117
+ if (playing && isActive) {
118
+ video.play().catch(() => {
119
+ // Autoplay might be blocked, that's okay
120
+ });
121
+ }
122
+ else {
123
+ video.pause();
124
+ }
125
+ }, [playing, ready, isActive]);
126
+ // Don't render if outside active range
127
+ if (!isActive) {
128
+ return null;
129
+ }
130
+ return (_jsx("video", { ref: videoRef, src: src, style: {
131
+ ...style,
132
+ opacity: ready ? (style?.opacity ?? 1) : 0,
133
+ }, className: className, onCanPlayThrough: handleCanPlay, onError: handleError, playsInline: true, preload: "auto", loop: loop, ...rest }));
134
+ }
135
+ export default Video;
136
+ //# sourceMappingURL=Video.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Video.js","sourceRoot":"","sources":["../src/Video.tsx"],"names":[],"mappings":"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"}