@twick/timeline 0.14.2 → 0.14.3

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/README.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @twick/timeline
2
2
 
3
+ Timeline management and editing capabilities for video projects.
4
+
5
+ ## Overview
6
+
7
+ This package provides a comprehensive timeline editor with CRUD operations for managing video tracks and elements. It uses the visitor pattern to handle different element types consistently and offers a fluent interface for timeline manipulation.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @twick/timeline
13
+ # or
14
+ pnpm add @twick/timeline
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { TimelineEditor, TimelineOperationContext } from '@twick/timeline';
21
+
22
+ // Create editor with context
23
+ const context: TimelineOperationContext = {
24
+ contextId: 'my-editor',
25
+ setTotalDuration: (duration) => console.log('Duration:', duration),
26
+ setPresent: (data) => console.log('Present:', data),
27
+ handleUndo: () => console.log('Undo'),
28
+ handleRedo: () => console.log('Redo'),
29
+ handleResetHistory: () => console.log('Reset History'),
30
+ setLatestProjectVersion: (version) => console.log('Version:', version),
31
+ setTimelineAction: (action, payload) => console.log('Action:', action, payload),
32
+ };
33
+
34
+ const editor = new TimelineEditor(context);
35
+ const track = editor.addTrack('My Video Track');
36
+ ```
37
+
3
38
  ## Timeline Editor CRUD Operations
4
39
 
5
40
  The TimelineEditor provides a clean interface for managing tracks and elements using the visitor pattern.
@@ -172,4 +207,55 @@ function MyComponent() {
172
207
  }
173
208
  ```
174
209
 
210
+ ## API Reference
211
+
212
+ ### Core Classes
213
+
214
+ - `TimelineEditor`: Main timeline editor class
215
+ - `TextElement`: Text element implementation
216
+ - `VideoElement`: Video element implementation
217
+ - `ImageElement`: Image element implementation
218
+ - `AudioElement`: Audio element implementation
219
+
220
+ ### Hooks
221
+
222
+ - `useTimelineContext`: React hook for timeline context
223
+
224
+ ### Utility Functions
225
+
226
+ - `getCurrentElements`: Get elements at specific time
227
+ - `getTotalDuration`: Calculate total timeline duration
228
+ - `generateShortUuid`: Generate unique IDs
229
+ - `isElementId`: Check if ID is element type
230
+ - `isTrackId`: Check if ID is track type
231
+
232
+ ### Types
233
+
234
+ - `TimelineOperationContext`: Context interface for timeline operations
235
+ - `TrackElement`: Base track element interface
236
+
237
+ For complete API documentation, refer to the generated documentation.
238
+
239
+ ## Browser Support
240
+
241
+ This package requires a browser environment with support for:
242
+ - Modern JavaScript features (ES2020+)
243
+ - Promise and async/await support
244
+
245
+ ## Documentation
246
+
247
+ For complete documentation, refer to the project documentation site.
248
+
249
+ ## License
250
+
251
+ This package is licensed under the **Sustainable Use License (SUL) Version 1.0**.
252
+
253
+ - Free for use in commercial and non-commercial apps
254
+ - Can be modified and self-hosted
255
+ - Cannot be sold, rebranded, or distributed as a standalone SDK
256
+
257
+ For commercial licensing inquiries, contact: contact@kifferai.com
258
+
259
+ For full license terms, see the main LICENSE.md file in the project root.
260
+
175
261
 
@@ -3,31 +3,134 @@ import { TrackElement } from '../core/elements/base.element';
3
3
  import { ProjectJSON, TrackJSON } from '../types';
4
4
  import { TimelineEditor } from '../core/editor/timeline.editor';
5
5
 
6
+ /**
7
+ * Type definition for the Timeline context.
8
+ * Contains all the state and functions needed to manage a timeline instance.
9
+ * Provides access to the timeline editor, selected items, undo/redo functionality,
10
+ * and timeline actions.
11
+ *
12
+ * @example
13
+ * ```js
14
+ * const {
15
+ * editor,
16
+ * selectedItem,
17
+ * totalDuration,
18
+ * canUndo,
19
+ * canRedo,
20
+ * setSelectedItem,
21
+ * setTimelineAction
22
+ * } = useTimelineContext();
23
+ * ```
24
+ */
6
25
  export type TimelineContextType = {
26
+ /** Unique identifier for this timeline context */
7
27
  contextId: string;
28
+ /** The timeline editor instance for this context */
8
29
  editor: TimelineEditor;
30
+ /** Currently selected track or element */
9
31
  selectedItem: Track | TrackElement | null;
32
+ /** Change counter for tracking modifications */
10
33
  changeLog: number;
34
+ /** Current timeline action being performed */
11
35
  timelineAction: {
12
36
  type: string;
13
37
  payload: any;
14
38
  };
39
+ /** Total duration of the timeline in seconds */
15
40
  totalDuration: number;
41
+ /** Current project state */
16
42
  present: ProjectJSON | null;
43
+ /** Whether undo operation is available */
17
44
  canUndo: boolean;
45
+ /** Whether redo operation is available */
18
46
  canRedo: boolean;
47
+ /** Function to set the selected item */
19
48
  setSelectedItem: (item: Track | TrackElement | null) => void;
49
+ /** Function to set timeline actions */
20
50
  setTimelineAction: (type: string, payload: any) => void;
21
51
  };
52
+ /**
53
+ * Props for the TimelineProvider component.
54
+ * Defines the configuration options for timeline context initialization.
55
+ *
56
+ * @example
57
+ * ```jsx
58
+ * <TimelineProvider
59
+ * contextId="my-timeline"
60
+ * initialData={{ tracks: [], version: 1 }}
61
+ * undoRedoPersistenceKey="timeline-state"
62
+ * maxHistorySize={50}
63
+ * >
64
+ * <YourApp />
65
+ * </TimelineProvider>
66
+ * ```
67
+ */
22
68
  export interface TimelineProviderProps {
69
+ /** React children to wrap with timeline context */
23
70
  children: React.ReactNode;
71
+ /** Unique identifier for this timeline context */
24
72
  contextId: string;
73
+ /** Initial timeline data to load */
25
74
  initialData?: {
26
75
  tracks: TrackJSON[];
27
76
  version: number;
28
77
  };
78
+ /** Key for persisting undo/redo state */
29
79
  undoRedoPersistenceKey?: string;
80
+ /** Maximum number of history states to keep */
30
81
  maxHistorySize?: number;
31
82
  }
83
+ /**
84
+ * Provider component for the Timeline context.
85
+ * Wraps the timeline functionality with PostHog analytics and undo/redo support.
86
+ * Manages the global state for timeline instances including tracks, elements,
87
+ * playback state, and history management.
88
+ *
89
+ * @param props - Timeline provider configuration
90
+ * @returns Context provider with timeline state management
91
+ *
92
+ * @example
93
+ * ```jsx
94
+ * <TimelineProvider
95
+ * contextId="my-timeline"
96
+ * initialData={{ tracks: [], version: 1 }}
97
+ * undoRedoPersistenceKey="timeline-state"
98
+ * >
99
+ * <YourApp />
100
+ * </TimelineProvider>
101
+ * ```
102
+ */
32
103
  export declare const TimelineProvider: ({ contextId, children, initialData, undoRedoPersistenceKey, maxHistorySize, }: TimelineProviderProps) => import("react/jsx-runtime").JSX.Element;
104
+ /**
105
+ * Hook to access the Timeline context.
106
+ * Provides access to timeline state, editor instance, and timeline management functions.
107
+ * Must be used within a TimelineProvider component.
108
+ *
109
+ * @returns TimelineContextType object with all timeline state and controls
110
+ * @throws Error if used outside of TimelineProvider
111
+ *
112
+ * @example
113
+ * ```js
114
+ * const {
115
+ * editor,
116
+ * selectedItem,
117
+ * totalDuration,
118
+ * canUndo,
119
+ * canRedo,
120
+ * setSelectedItem,
121
+ * setTimelineAction
122
+ * } = useTimelineContext();
123
+ *
124
+ * // Access the timeline editor
125
+ * const tracks = editor.getTracks();
126
+ *
127
+ * // Check if undo is available
128
+ * if (canUndo) {
129
+ * editor.undo();
130
+ * }
131
+ *
132
+ * // Set timeline action
133
+ * setTimelineAction(TIMELINE_ACTION.SET_PLAYER_STATE, { playing: true });
134
+ * ```
135
+ */
33
136
  export declare const useTimelineContext: () => TimelineContextType;
@@ -2,66 +2,318 @@ import { TrackJSON } from '../../types';
2
2
  import { TrackElement } from '../elements/base.element';
3
3
  import { TrackFriend } from './track.friend';
4
4
 
5
+ /**
6
+ * Track class represents a timeline track that contains multiple elements.
7
+ * A track is a container for timeline elements (video, audio, text, etc.) that
8
+ * can be arranged sequentially or in parallel. Tracks provide validation,
9
+ * serialization, and element management capabilities.
10
+ *
11
+ * @example
12
+ * ```js
13
+ * import { Track, VideoElement, TextElement } from '@twick/timeline';
14
+ *
15
+ * // Create a new track
16
+ * const videoTrack = new Track("Video Track");
17
+ *
18
+ * // Add elements to the track
19
+ * const videoElement = new VideoElement({
20
+ * src: "video.mp4",
21
+ * start: 0,
22
+ * end: 10
23
+ * });
24
+ *
25
+ * videoTrack.createFriend().addElement(videoElement);
26
+ *
27
+ * // Serialize the track
28
+ * const trackData = videoTrack.serialize();
29
+ * ```
30
+ */
5
31
  export declare class Track {
6
32
  private id;
7
33
  private name;
8
34
  private type;
9
35
  private elements;
10
36
  private validator;
37
+ /**
38
+ * Creates a new Track instance.
39
+ *
40
+ * @param name - The display name for the track
41
+ * @param id - Optional unique identifier (auto-generated if not provided)
42
+ *
43
+ * @example
44
+ * ```js
45
+ * const track = new Track("My Video Track");
46
+ * const trackWithId = new Track("Audio Track", "audio-track-1");
47
+ * ```
48
+ */
11
49
  constructor(name: string, id?: string);
12
50
  /**
13
- * Create a friend instance for explicit access to protected methods
14
- * This implements the Friend Class Pattern
15
- * @returns TrackFriend instance
51
+ * Creates a friend instance for explicit access to protected methods.
52
+ * This implements the Friend Class Pattern to allow controlled access
53
+ * to protected methods while maintaining encapsulation.
54
+ *
55
+ * @returns TrackFriend instance that can access protected methods
56
+ *
57
+ * @example
58
+ * ```js
59
+ * const track = new Track("My Track");
60
+ * const friend = track.createFriend();
61
+ *
62
+ * // Use friend to add elements
63
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
64
+ * friend.addElement(element);
65
+ * ```
16
66
  */
17
67
  createFriend(): TrackFriend;
18
68
  /**
19
- * Friend method to add element (called by TrackFriend)
20
- * @param element The element to add
21
- * @param skipValidation If true, skips validation
69
+ * Friend method to add element (called by TrackFriend).
70
+ * Provides controlled access to the protected addElement method.
71
+ *
72
+ * @param element - The element to add to the track
73
+ * @param skipValidation - If true, skips validation (use with caution)
22
74
  * @returns true if element was added successfully
75
+ *
76
+ * @example
77
+ * ```js
78
+ * const track = new Track("My Track");
79
+ * const friend = track.createFriend();
80
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
81
+ *
82
+ * const success = track.addElementViaFriend(element);
83
+ * // success = true if element was added successfully
84
+ * ```
23
85
  */
24
86
  addElementViaFriend(element: TrackElement, skipValidation?: boolean): boolean;
25
87
  /**
26
- * Friend method to remove element (called by TrackFriend)
27
- * @param element The element to remove
88
+ * Friend method to remove element (called by TrackFriend).
89
+ * Provides controlled access to the protected removeElement method.
90
+ *
91
+ * @param element - The element to remove from the track
92
+ *
93
+ * @example
94
+ * ```js
95
+ * const track = new Track("My Track");
96
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
97
+ *
98
+ * track.removeElementViaFriend(element);
99
+ * // Element is removed from the track
100
+ * ```
28
101
  */
29
102
  removeElementViaFriend(element: TrackElement): void;
30
103
  /**
31
- * Friend method to update element (called by TrackFriend)
32
- * @param element The element to update
104
+ * Friend method to update element (called by TrackFriend).
105
+ * Provides controlled access to the protected updateElement method.
106
+ *
107
+ * @param element - The updated element to replace the existing one
33
108
  * @returns true if element was updated successfully
109
+ *
110
+ * @example
111
+ * ```js
112
+ * const track = new Track("My Track");
113
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
114
+ *
115
+ * // Update the element
116
+ * element.setEnd(15);
117
+ * const success = track.updateElementViaFriend(element);
118
+ * // success = true if element was updated successfully
119
+ * ```
34
120
  */
35
121
  updateElementViaFriend(element: TrackElement): boolean;
122
+ /**
123
+ * Gets the unique identifier of the track.
124
+ *
125
+ * @returns The track's unique ID string
126
+ *
127
+ * @example
128
+ * ```js
129
+ * const track = new Track("My Track", "track-123");
130
+ * const id = track.getId(); // "track-123"
131
+ * ```
132
+ */
36
133
  getId(): string;
134
+ /**
135
+ * Gets the display name of the track.
136
+ *
137
+ * @returns The track's display name
138
+ *
139
+ * @example
140
+ * ```js
141
+ * const track = new Track("Video Track");
142
+ * const name = track.getName(); // "Video Track"
143
+ * ```
144
+ */
37
145
  getName(): string;
146
+ /**
147
+ * Gets the type of the track.
148
+ *
149
+ * @returns The track's type string
150
+ *
151
+ * @example
152
+ * ```js
153
+ * const track = new Track("My Track");
154
+ * const type = track.getType(); // "element"
155
+ * ```
156
+ */
38
157
  getType(): string;
158
+ /**
159
+ * Gets a read-only array of all elements in the track.
160
+ * Returns a copy of the elements array to prevent external modification.
161
+ *
162
+ * @returns Read-only array of track elements
163
+ *
164
+ * @example
165
+ * ```js
166
+ * const track = new Track("My Track");
167
+ * const elements = track.getElements();
168
+ * // elements is a read-only array of TrackElement instances
169
+ *
170
+ * elements.forEach(element => {
171
+ * console.log(`Element: ${element.getId()}, Duration: ${element.getEnd() - element.getStart()}`);
172
+ * });
173
+ * ```
174
+ */
39
175
  getElements(): ReadonlyArray<TrackElement>;
40
176
  /**
41
- * Validates an element
42
- * @param element The element to validate
177
+ * Validates a single element using the track's validator.
178
+ * Checks if the element meets all validation requirements.
179
+ *
180
+ * @param element - The element to validate
43
181
  * @returns true if valid, throws ValidationError if invalid
182
+ *
183
+ * @example
184
+ * ```js
185
+ * const track = new Track("My Track");
186
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
187
+ *
188
+ * try {
189
+ * const isValid = track.validateElement(element);
190
+ * console.log('Element is valid:', isValid);
191
+ * } catch (error) {
192
+ * if (error instanceof ValidationError) {
193
+ * console.log('Validation failed:', error.errors);
194
+ * }
195
+ * }
196
+ * ```
44
197
  */
45
198
  validateElement(element: TrackElement): boolean;
199
+ /**
200
+ * Gets the total duration of the track.
201
+ * Calculates the duration based on the end time of the last element.
202
+ *
203
+ * @returns The total duration of the track in seconds
204
+ *
205
+ * @example
206
+ * ```js
207
+ * const track = new Track("My Track");
208
+ * const duration = track.getTrackDuration(); // 0 if no elements
209
+ *
210
+ * // After adding elements
211
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 30 });
212
+ * track.createFriend().addElement(element);
213
+ * const newDuration = track.getTrackDuration(); // 30
214
+ * ```
215
+ */
46
216
  getTrackDuration(): number;
47
217
  /**
48
- * Adds an element to the track with validation
49
- * @param element The element to add
50
- * @param skipValidation If true, skips validation (use with caution)
218
+ * Adds an element to the track with validation.
219
+ * Protected method that should be accessed through TrackFriend.
220
+ *
221
+ * @param element - The element to add to the track
222
+ * @param skipValidation - If true, skips validation (use with caution)
51
223
  * @returns true if element was added successfully, throws ValidationError if validation fails
224
+ *
225
+ * @example
226
+ * ```js
227
+ * const track = new Track("My Track");
228
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
229
+ *
230
+ * // Use friend to access this protected method
231
+ * const friend = track.createFriend();
232
+ * const success = friend.addElement(element);
233
+ * ```
52
234
  */
53
235
  protected addElement(element: TrackElement, skipValidation?: boolean): boolean;
236
+ /**
237
+ * Removes an element from the track.
238
+ * Protected method that should be accessed through TrackFriend.
239
+ *
240
+ * @param element - The element to remove from the track
241
+ *
242
+ * @example
243
+ * ```js
244
+ * const track = new Track("My Track");
245
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
246
+ *
247
+ * // Use friend to access this protected method
248
+ * const friend = track.createFriend();
249
+ * friend.removeElement(element);
250
+ * ```
251
+ */
54
252
  protected removeElement(element: TrackElement): void;
55
253
  /**
56
- * Updates an element in the track with validation
57
- * @param element The element to update
254
+ * Updates an element in the track with validation.
255
+ * Protected method that should be accessed through TrackFriend.
256
+ *
257
+ * @param element - The updated element to replace the existing one
58
258
  * @returns true if element was updated successfully, throws ValidationError if validation fails
259
+ *
260
+ * @example
261
+ * ```js
262
+ * const track = new Track("My Track");
263
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
264
+ *
265
+ * // Use friend to access this protected method
266
+ * const friend = track.createFriend();
267
+ * element.setEnd(15);
268
+ * const success = friend.updateElement(element);
269
+ * ```
59
270
  */
60
271
  protected updateElement(element: TrackElement): boolean;
272
+ /**
273
+ * Finds an element in the track by its ID.
274
+ *
275
+ * @param id - The unique identifier of the element to find
276
+ * @returns The found element or undefined if not found
277
+ *
278
+ * @example
279
+ * ```js
280
+ * const track = new Track("My Track");
281
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
282
+ * track.createFriend().addElement(element);
283
+ *
284
+ * const foundElement = track.getElementById(element.getId());
285
+ * // foundElement is the same element instance
286
+ * ```
287
+ */
61
288
  getElementById(id: string): Readonly<TrackElement> | undefined;
62
289
  /**
63
- * Validates all elements in the track and returns combined result and per-element status
290
+ * Validates all elements in the track and returns combined result and per-element status.
291
+ * Provides detailed validation information for each element including errors and warnings.
292
+ *
64
293
  * @returns Object with overall isValid and array of per-element validation results
294
+ *
295
+ * @example
296
+ * ```js
297
+ * const track = new Track("My Track");
298
+ * const element1 = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
299
+ * const element2 = new TextElement({ text: "Hello", start: 5, end: 15 });
300
+ *
301
+ * track.createFriend().addElement(element1);
302
+ * track.createFriend().addElement(element2);
303
+ *
304
+ * const validation = track.validateAllElements();
305
+ * console.log('Overall valid:', validation.isValid);
306
+ *
307
+ * validation.results.forEach(result => {
308
+ * console.log(`Element ${result.element.getId()}: ${result.isValid ? 'Valid' : 'Invalid'}`);
309
+ * if (result.errors) {
310
+ * console.log('Errors:', result.errors);
311
+ * }
312
+ * if (result.warnings) {
313
+ * console.log('Warnings:', result.warnings);
314
+ * }
315
+ * });
316
+ * ```
65
317
  */
66
318
  validateAllElements(): {
67
319
  isValid: boolean;
@@ -72,6 +324,59 @@ export declare class Track {
72
324
  warnings?: string[];
73
325
  }>;
74
326
  };
327
+ /**
328
+ * Serializes the track and all its elements to JSON format.
329
+ * Converts the track structure to a format that can be stored or transmitted.
330
+ *
331
+ * @returns TrackJSON object representing the track and its elements
332
+ *
333
+ * @example
334
+ * ```js
335
+ * const track = new Track("My Track");
336
+ * const element = new VideoElement({ src: "video.mp4", start: 0, end: 10 });
337
+ * track.createFriend().addElement(element);
338
+ *
339
+ * const trackData = track.serialize();
340
+ * // trackData = {
341
+ * // id: "t-abc123",
342
+ * // name: "My Track",
343
+ * // type: "element",
344
+ * // elements: [{ ... }]
345
+ * // }
346
+ *
347
+ * // Save to localStorage
348
+ * localStorage.setItem('track-data', JSON.stringify(trackData));
349
+ * ```
350
+ */
75
351
  serialize(): TrackJSON;
352
+ /**
353
+ * Creates a Track instance from JSON data.
354
+ * Static factory method for deserializing track data.
355
+ *
356
+ * @param json - JSON object containing track data
357
+ * @returns New Track instance with loaded data
358
+ *
359
+ * @example
360
+ * ```js
361
+ * const trackData = {
362
+ * id: "t-abc123",
363
+ * name: "My Track",
364
+ * type: "element",
365
+ * elements: [
366
+ * {
367
+ * id: "e-def456",
368
+ * type: "video",
369
+ * src: "video.mp4",
370
+ * start: 0,
371
+ * end: 10
372
+ * }
373
+ * ]
374
+ * };
375
+ *
376
+ * const track = Track.fromJSON(trackData);
377
+ * console.log(track.getName()); // "My Track"
378
+ * console.log(track.getElements().length); // 1
379
+ * ```
380
+ */
76
381
  static fromJSON(json: any): Track;
77
382
  }