@designcombo/video 0.1.8 → 0.1.10

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,49 @@
1
+ import { Application, Container, Graphics } from 'pixi.js';
2
+ import { IClip } from '../clips/iclip';
3
+ import { Transformer } from '../transfomer/transformer';
4
+ import { Studio } from '../studio';
5
+ export declare class SelectionManager {
6
+ private studio;
7
+ selectedClips: Set<IClip>;
8
+ activeTransformer: Transformer | null;
9
+ interactiveClips: Set<IClip>;
10
+ selectionGraphics: Graphics | null;
11
+ isDragSelecting: boolean;
12
+ private dragSelectionStart;
13
+ private isUpdatingTextClipRealtime;
14
+ private textClipResizedWidth;
15
+ private textClipResizeHandle;
16
+ private textClipResizedSx;
17
+ private textClipResizedSy;
18
+ constructor(studio: Studio);
19
+ init(app: Application, artboard: Container): void;
20
+ private onStagePointerDown;
21
+ private onStagePointerMove;
22
+ private onStagePointerUp;
23
+ /**
24
+ * Setup sprite interactivity for click selection
25
+ */
26
+ setupSpriteInteractivity(clip: IClip): void;
27
+ /**
28
+ * Find the topmost clip (highest zIndex) at a given point
29
+ */
30
+ getTopmostClipAtPoint(globalPoint: {
31
+ x: number;
32
+ y: number;
33
+ }): IClip | null;
34
+ /**
35
+ * Select a clip and show transform controls
36
+ */
37
+ selectClip(clip: IClip, addToSelection?: boolean): void;
38
+ selectClipsByIds(ids: string[]): void;
39
+ setSelection(clips: IClip[]): void;
40
+ deselectClip(): void;
41
+ clear(): void;
42
+ private recreateTransformer;
43
+ private destroyTransformer;
44
+ private createTransformer;
45
+ private syncSelectedClipsTransformsRealtime;
46
+ private syncSelectedClipsTransforms;
47
+ private syncSpriteToClipProperties;
48
+ deleteSelected(): Promise<void>;
49
+ }
@@ -0,0 +1,77 @@
1
+ import { Studio, StudioTrack } from '../studio';
2
+ import { IClip } from '../clips/iclip';
3
+ import { VideoClip } from '../clips/video-clip';
4
+ import { ImageClip } from '../clips/image-clip';
5
+ import { ProjectJSON } from '../json-serialization';
6
+ export declare class TimelineModel {
7
+ private studio;
8
+ tracks: StudioTrack[];
9
+ clips: IClip[];
10
+ constructor(studio: Studio);
11
+ getTrackById(trackId: string): StudioTrack | undefined;
12
+ getClipById(clipId: string): IClip | undefined;
13
+ findTrackIdByClipId(clipId: string): string | undefined;
14
+ getTrackIndex(trackId: string): number;
15
+ /**
16
+ * Add a new track to the studio
17
+ */
18
+ addTrack(track: {
19
+ name: string;
20
+ type: string;
21
+ id?: string;
22
+ }): StudioTrack;
23
+ /**
24
+ * Remove a track and all its clips
25
+ */
26
+ removeTrack(trackId: string): Promise<void>;
27
+ /**
28
+ * Add a Media clip (Video/Image) to the main track with ripple effect
29
+ */
30
+ addMedia(clip: VideoClip | ImageClip): Promise<void>;
31
+ /**
32
+ * Add a Transition clip at the join where the selected clip starts.
33
+ */
34
+ addTransition(transitionKey: string, duration?: number, fromClipId?: string | null, toClipId?: string | null): Promise<void>;
35
+ /**
36
+ * Add a clip to the studio
37
+ */
38
+ addClip(clip: IClip, options?: {
39
+ trackId?: string;
40
+ audioSource?: string | File | Blob;
41
+ } | string | File | Blob): Promise<void>;
42
+ removeClip(clip: IClip): Promise<void>;
43
+ removeClipById(clipId: string): Promise<void>;
44
+ updateClip(clipId: string, updates: Partial<IClip>): Promise<void>;
45
+ /**
46
+ * Export current project state to JSON
47
+ */
48
+ exportToJSON(): ProjectJSON;
49
+ /**
50
+ * Load clips from JSON
51
+ */
52
+ loadFromJSON(json: ProjectJSON): Promise<void>;
53
+ private loadClipIntoTrack;
54
+ /**
55
+ * Delete all currently selected clips
56
+ */
57
+ deleteSelected(): Promise<void>;
58
+ /**
59
+ * Duplicate all currently selected clips
60
+ */
61
+ duplicateSelected(): Promise<void>;
62
+ /**
63
+ * Split the selected clip at the given time or current time
64
+ */
65
+ splitSelected(splitTime?: number): Promise<void>;
66
+ /**
67
+ * Trim the selected clip from a specified time
68
+ * @param trimFromSeconds - Number of seconds to trim from the start of the clip
69
+ */
70
+ trimSelected(trimFromSeconds: number): Promise<void>;
71
+ updateSelected(updates: Partial<IClip>): Promise<void>;
72
+ setTracks(tracks: StudioTrack[]): Promise<void>;
73
+ recalculateMaxDuration(): Promise<void>;
74
+ private setupPlaybackForClip;
75
+ private isPlaybackCapable;
76
+ clear(): Promise<void>;
77
+ }
@@ -0,0 +1,36 @@
1
+ import { Studio } from '../studio';
2
+ import { IClip, IPlaybackCapable } from '../clips/iclip';
3
+ export interface PlaybackElementInfo {
4
+ element: HTMLVideoElement | HTMLAudioElement;
5
+ objectUrl?: string;
6
+ }
7
+ export declare class Transport {
8
+ private studio;
9
+ isPlaying: boolean;
10
+ currentTime: number;
11
+ maxDuration: number;
12
+ private playStartTime;
13
+ private playStartTimestamp;
14
+ private rafId;
15
+ playbackElements: Map<IClip, PlaybackElementInfo>;
16
+ constructor(studio: Studio);
17
+ setMaxDuration(duration: number): void;
18
+ /**
19
+ * Start playback
20
+ */
21
+ play(): Promise<void>;
22
+ /**
23
+ * Pause playback
24
+ */
25
+ pause(): void;
26
+ /**
27
+ * Stop playback and reset to start
28
+ */
29
+ stop(): Promise<void>;
30
+ /**
31
+ * Seek to a specific time (in microseconds)
32
+ */
33
+ seek(time: number): Promise<void>;
34
+ private renderLoop;
35
+ isPlaybackCapable(clip: any): clip is IPlaybackCapable;
36
+ }
package/dist/studio.d.ts CHANGED
@@ -1,10 +1,15 @@
1
- import { Texture } from 'pixi.js';
1
+ import { Application, Sprite, Texture, Container, Graphics, RenderTexture } from 'pixi.js';
2
2
  import { ImageClip } from './clips/image-clip';
3
3
  import { IClip } from './clips/iclip';
4
4
  import { VideoClip } from './clips/video-clip';
5
+ import { PixiSpriteRenderer } from './sprite/pixi-sprite-renderer';
5
6
  import { ProjectJSON } from './json-serialization';
7
+ import { Transformer } from './transfomer/transformer';
6
8
  import { EffectKey } from './effect/glsl/gl-effect';
7
9
  import { default as EventEmitter } from './event-emitter';
10
+ import { SelectionManager } from './studio/selection-manager';
11
+ import { Transport } from './studio/transport';
12
+ import { TimelineModel } from './studio/timeline-model';
8
13
  export interface IStudioOpts {
9
14
  width: number;
10
15
  height: number;
@@ -13,6 +18,19 @@ export interface IStudioOpts {
13
18
  canvas?: HTMLCanvasElement;
14
19
  interactivity?: boolean;
15
20
  }
21
+ interface ActiveGlobalEffect {
22
+ id: string;
23
+ key: EffectKey;
24
+ startTime: number;
25
+ duration: number;
26
+ trackIndex?: number;
27
+ }
28
+ interface GlobalEffectInfo {
29
+ id: string;
30
+ key: EffectKey;
31
+ startTime: number;
32
+ duration: number;
33
+ }
16
34
  export interface StudioEvents {
17
35
  'selection:created': {
18
36
  selected: IClip[];
@@ -57,63 +75,57 @@ export interface StudioTrack {
57
75
  type: string;
58
76
  clipIds: string[];
59
77
  }
60
- /**
61
- * Interactive preview studio for clips with playback controls
62
- * Useful for previewing clips before rendering with Compositor
63
- *
64
- * @example
65
- * const studio = new Studio({
66
- * width: 1280,
67
- * height: 720,
68
- * fps: 30,
69
- * bgColor: '#000'
70
- * });
71
- *
72
- * await studio.addClip(spr1);
73
- * await studio.addClip(spr2);
74
- * studio.play();
75
- *
76
- * studio.on('selection:created', ({ selected }) => {
77
- * console.log('Selection created', selected);
78
- * });
79
- */
80
78
  export declare class Studio extends EventEmitter<StudioEvents> {
81
- private pixiApp;
82
- private tracks;
83
- private clips;
84
- private spriteRenderers;
85
- private artboard;
86
- private clipContainer;
87
- private artboardMask;
88
- private artboardBg;
89
- private activeTransformer;
90
- private selectedClips;
91
- private interactiveClips;
92
- private playbackElements;
93
- private videoSprites;
94
- private clipListeners;
95
- private isPlaying;
96
- private currentTime;
97
- private playStartTime;
98
- private playStartTimestamp;
99
- private rafId;
100
- private maxDuration;
101
- private opts;
102
- private destroyed;
103
- private globalEffects;
104
- private activeGlobalEffect;
105
- private currentGlobalEffectSprite;
106
- private effectFilters;
107
- private transitionRenderers;
108
- private transitionSprites;
109
- private transFromTexture;
110
- private transToTexture;
111
- private transBgGraphics;
112
- private clipsNormalContainer;
113
- private clipsEffectContainer;
114
- private videoTextureCache;
115
- private lastFromFrame;
116
- private lastToFrame;
79
+ selection: SelectionManager;
80
+ transport: Transport;
81
+ timeline: TimelineModel;
82
+ pixiApp: Application | null;
83
+ get tracks(): StudioTrack[];
84
+ get clips(): IClip[];
85
+ spriteRenderers: Map<IClip, PixiSpriteRenderer>;
86
+ artboard: Container | null;
87
+ clipContainer: Container | null;
88
+ artboardMask: Graphics | null;
89
+ artboardBg: Graphics | null;
90
+ get activeTransformer(): Transformer | null;
91
+ set activeTransformer(val: Transformer | null);
92
+ get selectedClips(): Set<IClip>;
93
+ set selectedClips(val: Set<IClip>);
94
+ get interactiveClips(): Set<IClip>;
95
+ set interactiveClips(val: Set<IClip>);
96
+ get playbackElements(): Map<IClip, import('./studio/transport').PlaybackElementInfo>;
97
+ videoSprites: Map<IClip, Sprite>;
98
+ clipListeners: Map<IClip, () => void>;
99
+ get isPlaying(): boolean;
100
+ set isPlaying(val: boolean);
101
+ get currentTime(): number;
102
+ set currentTime(val: number);
103
+ get maxDuration(): number;
104
+ set maxDuration(val: number);
105
+ opts: Required<Omit<IStudioOpts, 'canvas'>> & {
106
+ canvas?: HTMLCanvasElement;
107
+ };
108
+ destroyed: boolean;
109
+ globalEffects: Map<string, GlobalEffectInfo>;
110
+ activeGlobalEffect: ActiveGlobalEffect | null;
111
+ currentGlobalEffectSprite: Sprite | null;
112
+ effectFilters: Map<string, {
113
+ filter: import('pixi.js').Filter;
114
+ render({ width, height, canvasTexture, progress }: import('./effect/types').EffectRendererOptions): RenderTexture;
115
+ }>;
116
+ transitionRenderers: Map<string, {
117
+ render({ width, height, from, to, progress }: import('./transition/types').TransitionRendererOptions): RenderTexture;
118
+ destroy(): void;
119
+ }>;
120
+ transitionSprites: Map<string, Sprite>;
121
+ transFromTexture: RenderTexture | null;
122
+ transToTexture: RenderTexture | null;
123
+ transBgGraphics: Graphics | null;
124
+ clipsNormalContainer: Container | null;
125
+ clipsEffectContainer: Container | null;
126
+ videoTextureCache: WeakMap<HTMLVideoElement, Texture<import('pixi.js').TextureSource<any>>>;
127
+ lastFromFrame: Texture | ImageBitmap | null;
128
+ lastToFrame: Texture | ImageBitmap | null;
117
129
  /**
118
130
  * Convert hex color string to number
119
131
  */
@@ -123,6 +135,8 @@ export declare class Studio extends EventEmitter<StudioEvents> {
123
135
  * Create a new Studio instance
124
136
  */
125
137
  constructor(opts: IStudioOpts);
138
+ private handleTimelineChange;
139
+ private handleClipRemoved;
126
140
  private initPixiApp;
127
141
  /**
128
142
  * Get studio options
@@ -141,10 +155,10 @@ export declare class Studio extends EventEmitter<StudioEvents> {
141
155
  /**
142
156
  * Add a Media clip (Video/Image) to the main track with ripple effect
143
157
  */
144
- addMedia(clip: VideoClip | ImageClip): Promise<void>;
145
158
  /**
146
- * Add a Transition clip at the join where the selected clip starts.
159
+ * Add a Media clip (Video/Image) to the main track with ripple effect
147
160
  */
161
+ addMedia(clip: VideoClip | ImageClip): Promise<void>;
148
162
  addTransition(transitionKey: string, duration?: number, fromClipId?: string | null, toClipId?: string | null): Promise<void>;
149
163
  findTrackIdByClipId(clipId: string): string | undefined;
150
164
  /**
@@ -164,73 +178,40 @@ export declare class Studio extends EventEmitter<StudioEvents> {
164
178
  type: string;
165
179
  id?: string;
166
180
  }): StudioTrack;
167
- /**
168
- * Set tracks (and implicitly clips if they are new)
169
- */
170
181
  setTracks(tracks: StudioTrack[]): Promise<void>;
171
- /**
172
- * Remove a track and all its clips
173
- */
174
182
  removeTrack(trackId: string): Promise<void>;
175
183
  /**
176
184
  * Get a clip by its ID
177
185
  */
178
186
  getClipById(id: string): IClip | undefined;
179
- /**
180
- * Update a clip's properties
181
- * Handles consistency between display (from/to) and duration
182
- */
183
187
  updateClip(id: string, updates: Partial<IClip>): Promise<void>;
184
- /**
185
- * Get all tracks
186
- */
187
188
  getTracks(): StudioTrack[];
188
- /**
189
- * Get a clip by ID
190
- */
191
189
  getClip(id: string): IClip | undefined;
192
190
  /**
193
191
  * Setup sprite interactivity for click selection
192
+ * Delegated to SelectionManager
194
193
  */
195
- private setupSpriteInteractivity;
194
+ setupSpriteInteractivity(clip: IClip): void;
196
195
  /**
197
196
  * Setup playback element for a clip (if it supports playback)
198
197
  */
199
- private setupPlaybackForClip;
200
- private isPlaybackCapable;
201
198
  /**
202
199
  * Remove a clip from the studio
203
200
  */
204
201
  removeClip(clip: IClip): Promise<void>;
205
- /**
206
- * Remove a clip by its ID
207
- */
208
202
  removeClipById(clipId: string): Promise<void>;
209
- /**
210
- * Delete all currently selected clips
211
- */
212
203
  deleteSelected(): Promise<void>;
213
204
  /**
214
205
  * Duplicate all currently selected clips
215
206
  */
216
207
  duplicateSelected(): Promise<void>;
217
- /**
218
- * Split the selected clip at the given time or current time
219
- */
220
208
  splitSelected(splitTime?: number): Promise<void>;
221
- /**
222
- * Trim the selected clip from a specified time
223
- * @param trimFromSeconds - Number of seconds to trim from the start of the clip
224
- */
225
209
  trimSelected(trimFromSeconds: number): Promise<void>;
226
- /**
227
- * Update properties for all currently selected clips
228
- */
229
210
  updateSelected(updates: Partial<IClip>): Promise<void>;
230
211
  /**
231
212
  * Clear all clips from the studio
232
213
  */
233
- clear(): void;
214
+ clear(): Promise<void>;
234
215
  /**
235
216
  * Start playback
236
217
  */
@@ -253,7 +234,6 @@ export declare class Studio extends EventEmitter<StudioEvents> {
253
234
  getCurrentTime(): number;
254
235
  /**
255
236
  * Get maximum duration (in microseconds)
256
- * Returns 0 if duration is invalid (Infinity, NaN, or <= 0)
257
237
  */
258
238
  getMaxDuration(): number;
259
239
  /**
@@ -264,10 +244,9 @@ export declare class Studio extends EventEmitter<StudioEvents> {
264
244
  * Get currently selected clips
265
245
  */
266
246
  getSelectedClips(): IClip[];
267
- private renderLoop;
268
247
  private getVideoTexture;
269
- private updateFrame;
270
- private recalculateMaxDuration;
248
+ private isPlaybackCapable;
249
+ updateFrame(timestamp: number): Promise<void>;
271
250
  /**
272
251
  * Apply global effect to the current scene
273
252
  */
@@ -297,32 +276,9 @@ export declare class Studio extends EventEmitter<StudioEvents> {
297
276
  destroy(): void;
298
277
  /**
299
278
  * Select a clip and show transform controls
279
+ * Delegated to InteractionManager
300
280
  */
301
281
  selectClip(clip: IClip, addToSelection?: boolean): void;
302
- /**
303
- * Create transformer for currently selected clips
304
- */
305
- private createTransformer;
306
- private isUpdatingTextClipRealtime;
307
- private textClipResizedWidth;
308
- private textClipResizeHandle;
309
- private textClipResizedSx;
310
- private textClipResizedSy;
311
- /**
312
- * Sync sprite transforms in real-time during drag (for TextClip reflow)
313
- * This is called during the drag operation to prevent visual scaling
314
- * Only works for mid-right handle (mr)
315
- */
316
- private syncSelectedClipsTransformsRealtime;
317
- /**
318
- * Sync sprite transforms back to clip properties for all selected clips
319
- * This ensures clip properties are always up-to-date during transformations
320
- */
321
- private syncSelectedClipsTransforms;
322
- /**
323
- * Sync sprite transforms back to clip properties
324
- */
325
- private syncSpriteToClipProperties;
326
282
  /**
327
283
  * Set the selection to a specific list of clips
328
284
  */
@@ -340,10 +296,6 @@ export declare class Studio extends EventEmitter<StudioEvents> {
340
296
  * @param sourceUrlMap Optional map of clips to their source URLs (required for proper serialization)
341
297
  */
342
298
  exportToJSON(): ProjectJSON;
343
- /**
344
- * Load clips from JSON
345
- * @param json The JSON project data
346
- */
347
299
  loadFromJSON(json: ProjectJSON): Promise<void>;
348
- private loadClipIntoTrack;
349
300
  }
301
+ export {};
@@ -1,10 +1,11 @@
1
- import { Container, Point } from 'pixi.js';
1
+ import { Container, Point, Graphics } from 'pixi.js';
2
2
  import { Wireframe } from './parts/wireframe';
3
3
  import { HandleKind } from './parts/handle';
4
4
  export declare class Transformer extends Container {
5
5
  #private;
6
6
  group: Container[];
7
7
  wireframe: Wireframe;
8
+ selectionOutlines: Graphics;
8
9
  isDragging: boolean;
9
10
  lastPointer: Point;
10
11
  activeHandle: HandleKind | null;
@@ -1,4 +1,4 @@
1
- import { E as p, U as Ne, T as ee, F as je, G as he, v as fe, M as P, l as C, d as pe, I as v, t as w, a8 as $, R as N, w as L, H as me, a5 as G, a6 as ge, c as F, B as T, D as j, S as M, y as D, af as qe, ag as q, L as Y, ah as U, s as Q, a0 as Qe, $ as X, n as xe, q as _e, aa as be, ad as ye, o as Je, p as Ze, ab as et, ac as tt, ae as rt, ai as nt, aj as st, ak as it, al as H, am as at, an as ot, m as ve, ao as te, ap as k, e as b, aq as ut } from "./index-BB-WOZJr.js";
1
+ import { E as p, U as Ne, T as ee, F as je, G as he, v as fe, M as P, l as C, d as pe, I as v, t as w, a8 as $, R as N, w as L, H as me, a5 as G, a6 as ge, c as F, B as T, D as j, S as M, y as D, af as qe, ag as q, L as Y, ah as U, s as Q, a0 as Qe, $ as X, n as xe, q as _e, aa as be, ad as ye, o as Je, p as Ze, ab as et, ac as tt, ae as rt, ai as nt, aj as st, ak as it, al as H, am as at, an as ot, m as ve, ao as te, ap as k, e as b, aq as ut } from "./index-DBEPaJbQ.js";
2
2
  import { c as z, a as lt, b as ct, B as Te } from "./colorToUniform-C2jGzNe1.js";
3
3
  class Pe {
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designcombo/video",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Video rendering and processing library",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/designcombo/combo-new.git",
11
+ "url": "https://github.com/designcombo/combo-video.git",
12
12
  "directory": "packages/video"
13
13
  },
14
14
  "keywords": [
@@ -22,7 +22,7 @@
22
22
  "files": [
23
23
  "dist"
24
24
  ],
25
- "main": "dist/index.umd.js",
25
+ "main": "dist/index.umd.js",
26
26
  "module": "dist/index.es.js",
27
27
  "types": "dist/index.d.ts",
28
28
  "exports": {