@aicut/core 0.4.3 → 0.6.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.
- package/README.md +178 -1
- package/dist/chunk-H6AY6NW4.js +123 -0
- package/dist/chunk-H6AY6NW4.js.map +1 -0
- package/dist/chunk-WTCK3XQ6.js +93 -0
- package/dist/chunk-WTCK3XQ6.js.map +1 -0
- package/dist/i18n-B24k4XVG.d.cts +84 -0
- package/dist/i18n-B24k4XVG.d.ts +84 -0
- package/dist/index.cjs +2383 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +523 -6
- package/dist/index.d.ts +523 -6
- package/dist/index.js +2187 -140
- package/dist/index.js.map +1 -1
- package/dist/lighting/index.cjs +24 -6
- package/dist/lighting/index.cjs.map +1 -1
- package/dist/lighting/index.d.cts +2 -1
- package/dist/lighting/index.d.ts +2 -1
- package/dist/lighting/index.js +1 -1
- package/dist/playback/webcodecs/index.cjs +10679 -0
- package/dist/playback/webcodecs/index.cjs.map +1 -0
- package/dist/playback/webcodecs/index.d.cts +125 -0
- package/dist/playback/webcodecs/index.d.ts +125 -0
- package/dist/playback/webcodecs/index.js +10608 -0
- package/dist/playback/webcodecs/index.js.map +1 -0
- package/dist/types-BbZjOQLz.d.ts +108 -0
- package/dist/types-CjvRUPtZ.d.cts +108 -0
- package/dist/types-CmS-UIEr.d.cts +137 -0
- package/dist/types-CmS-UIEr.d.ts +137 -0
- package/package.json +15 -4
- package/styles/theme.css +358 -2
- package/dist/chunk-CCDON7CU.js +0 -87
- package/dist/chunk-CCDON7CU.js.map +0 -1
- package/dist/types-C95koNwJ.d.cts +0 -120
- package/dist/types-C95koNwJ.d.ts +0 -120
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,180 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { P as PlaybackEngine, a as PlaybackEngineOptions, b as PlaybackEngineFactory } from './types-BbZjOQLz.js';
|
|
2
|
+
import { M as Ms, P as Project, T as Track, C as Clip, a as MediaSource, b as Theme, K as KeyframeProp, E as EasingKind } from './types-CmS-UIEr.js';
|
|
3
|
+
export { c as Keyframe } from './types-CmS-UIEr.js';
|
|
4
|
+
import { L as Locale } from './i18n-B24k4XVG.js';
|
|
5
|
+
export { f as formatLabel, l as localeEn, a as localeZh, m as mergeLocale } from './i18n-B24k4XVG.js';
|
|
6
|
+
|
|
7
|
+
declare class HtmlVideoEngine implements PlaybackEngine {
|
|
8
|
+
private host;
|
|
9
|
+
private mount;
|
|
10
|
+
private sources;
|
|
11
|
+
private project;
|
|
12
|
+
private currentClipId;
|
|
13
|
+
private playing;
|
|
14
|
+
private timeMs;
|
|
15
|
+
private rafHandle;
|
|
16
|
+
private lastFrameTs;
|
|
17
|
+
/** Permanent rAF that positions the active wrapper at the output
|
|
18
|
+
* rect + pushes keyframe transform onto the inner video via CSS. */
|
|
19
|
+
private transformRaf;
|
|
20
|
+
/** Public event hooks — set by Editor. */
|
|
21
|
+
onTimeUpdate?: (ms: Ms) => void;
|
|
22
|
+
onEnded?: () => void;
|
|
23
|
+
onError?: (err: Error) => void;
|
|
24
|
+
onReady?: () => void;
|
|
25
|
+
onSourceMetadata?: (sourceId: string, durationMs: Ms) => void;
|
|
26
|
+
constructor(opts: PlaybackEngineOptions);
|
|
27
|
+
setProject(next: Project): void;
|
|
28
|
+
play(): void;
|
|
29
|
+
pause(): void;
|
|
30
|
+
isPlaying(): boolean;
|
|
31
|
+
getTime(): Ms;
|
|
32
|
+
seek(timeMs: Ms): void;
|
|
33
|
+
/**
|
|
34
|
+
* The OUTPUT frame — the fixed stage the rendered video is clipped
|
|
35
|
+
* to. Independent of the keyframe transform. Used by the overlay to
|
|
36
|
+
* draw the dashed border at a stable position.
|
|
37
|
+
*/
|
|
38
|
+
getOutputFrameRect(): {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
w: number;
|
|
42
|
+
h: number;
|
|
43
|
+
} | null;
|
|
44
|
+
/**
|
|
45
|
+
* The CONTENT frame — where the transformed video pixels actually
|
|
46
|
+
* land. Equal to the output frame when transform is identity; may
|
|
47
|
+
* extend outside (zoom in) or fit inside (zoom out) when not.
|
|
48
|
+
*/
|
|
49
|
+
getFrameRect(): {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
w: number;
|
|
53
|
+
h: number;
|
|
54
|
+
} | null;
|
|
55
|
+
/** Untransformed contain-letterbox rect — the OUTPUT frame. */
|
|
56
|
+
private baseFrameRect;
|
|
57
|
+
/**
|
|
58
|
+
* Permanent rAF that (a) sizes + positions the active wrapper to
|
|
59
|
+
* the output frame, and (b) writes the keyframe transform onto the
|
|
60
|
+
* inner video. Negligible cost — three style writes per frame max.
|
|
61
|
+
*/
|
|
62
|
+
private startTransformLoop;
|
|
63
|
+
private applyTransforms;
|
|
64
|
+
destroy(): void;
|
|
65
|
+
private syncSources;
|
|
66
|
+
private activate;
|
|
67
|
+
private seekVideoToClipOffset;
|
|
68
|
+
private clipById;
|
|
69
|
+
/**
|
|
70
|
+
* Find the clip whose timeline range contains `timeMs`, searching
|
|
71
|
+
* across ALL video tracks. If multiple tracks have a clip at this
|
|
72
|
+
* moment, the lowest-index track wins.
|
|
73
|
+
*/
|
|
74
|
+
private clipAtTime;
|
|
75
|
+
/** Earliest clip starting at-or-after `timeMs` across all video tracks. */
|
|
76
|
+
private nextClipAfterTime;
|
|
77
|
+
/** Max clip end across all video tracks. */
|
|
78
|
+
private totalDuration;
|
|
79
|
+
private startTickLoop;
|
|
80
|
+
private stopTickLoop;
|
|
81
|
+
private advance;
|
|
82
|
+
}
|
|
83
|
+
/** Factory shorthand for `Editor.create({ playbackEngine })`. */
|
|
84
|
+
declare const htmlVideoEngineFactory: PlaybackEngineFactory;
|
|
85
|
+
|
|
86
|
+
interface CanvasCompositorEngineOptions extends PlaybackEngineOptions {
|
|
87
|
+
/**
|
|
88
|
+
* Show the corner HUD ("engine: canvas compositor • t=… • frames
|
|
89
|
+
* painted: …"). Off by default — production hosts get a clean canvas
|
|
90
|
+
* with no chrome painted on top. Turn on in development / demos to
|
|
91
|
+
* see who's drawing and what the current state is.
|
|
92
|
+
*/
|
|
93
|
+
debug?: boolean;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Reference second engine — demonstrates that the `PlaybackEngine`
|
|
97
|
+
* surface really is engine-agnostic. Same `<video>`-based decode as
|
|
98
|
+
* `HtmlVideoEngine` (so it works in every browser, no WebCodecs gate),
|
|
99
|
+
* but rendering happens via `ctx.drawImage(video, …)` on a single
|
|
100
|
+
* canvas instead of the browser painting the video element itself.
|
|
101
|
+
*
|
|
102
|
+
* Why ship it: it's tangible proof that a host can swap the rendering
|
|
103
|
+
* surface for compositing / shaders / overlays / capture-to-canvas
|
|
104
|
+
* without touching Editor internals. It's also the natural stepping
|
|
105
|
+
* stone toward a WebGL or WebCodecs path (those replace the decoder
|
|
106
|
+
* but keep the canvas-blit pattern).
|
|
107
|
+
*
|
|
108
|
+
* Limits: same as `HtmlVideoEngine` (one source visible at a time,
|
|
109
|
+
* seek snaps to the browser's keyframe pipeline). The point is the
|
|
110
|
+
* surface, not new capability.
|
|
111
|
+
*/
|
|
112
|
+
declare class CanvasCompositorEngine implements PlaybackEngine {
|
|
113
|
+
private host;
|
|
114
|
+
private mount;
|
|
115
|
+
private canvas;
|
|
116
|
+
private ctx;
|
|
117
|
+
/** Only created when constructed with `debug: true`. */
|
|
118
|
+
private badge;
|
|
119
|
+
private videos;
|
|
120
|
+
private project;
|
|
121
|
+
private currentClipId;
|
|
122
|
+
private playing;
|
|
123
|
+
private timeMs;
|
|
124
|
+
private rafHandle;
|
|
125
|
+
private lastFrameTs;
|
|
126
|
+
private paintedFrames;
|
|
127
|
+
/** Output frame rect (no transform) — fixed bounds. */
|
|
128
|
+
private lastOutputRect;
|
|
129
|
+
/** Post-transform content rect. */
|
|
130
|
+
private lastFrameRect;
|
|
131
|
+
onTimeUpdate?: (ms: Ms) => void;
|
|
132
|
+
onEnded?: () => void;
|
|
133
|
+
onError?: (err: Error) => void;
|
|
134
|
+
onReady?: () => void;
|
|
135
|
+
onSourceMetadata?: (sourceId: string, durationMs: Ms) => void;
|
|
136
|
+
constructor(opts: CanvasCompositorEngineOptions);
|
|
137
|
+
setProject(next: Project): void;
|
|
138
|
+
play(): void;
|
|
139
|
+
pause(): void;
|
|
140
|
+
isPlaying(): boolean;
|
|
141
|
+
getTime(): Ms;
|
|
142
|
+
seek(timeMs: Ms): void;
|
|
143
|
+
destroy(): void;
|
|
144
|
+
private syncSources;
|
|
145
|
+
private activate;
|
|
146
|
+
private seekVideoToClipOffset;
|
|
147
|
+
private clipById;
|
|
148
|
+
private clipAtTime;
|
|
149
|
+
private nextClipAfterTime;
|
|
150
|
+
private totalDuration;
|
|
151
|
+
private resizeCanvas;
|
|
152
|
+
private startTickLoop;
|
|
153
|
+
private stopTickLoop;
|
|
154
|
+
private advance;
|
|
155
|
+
/**
|
|
156
|
+
* One paint per rAF — clears the canvas, draws the current active
|
|
157
|
+
* video frame letterboxed to fit, then refreshes the HUD. Done
|
|
158
|
+
* unconditionally (not just on `playing`) so the HUD frame counter
|
|
159
|
+
* and the seek preview both update when paused.
|
|
160
|
+
*/
|
|
161
|
+
private paint;
|
|
162
|
+
getOutputFrameRect(): {
|
|
163
|
+
x: number;
|
|
164
|
+
y: number;
|
|
165
|
+
w: number;
|
|
166
|
+
h: number;
|
|
167
|
+
} | null;
|
|
168
|
+
getFrameRect(): {
|
|
169
|
+
x: number;
|
|
170
|
+
y: number;
|
|
171
|
+
w: number;
|
|
172
|
+
h: number;
|
|
173
|
+
} | null;
|
|
174
|
+
private updateBadge;
|
|
175
|
+
}
|
|
176
|
+
/** Factory shorthand for `Editor.create({ playbackEngine })`. */
|
|
177
|
+
declare const canvasCompositorEngineFactory: PlaybackEngineFactory;
|
|
3
178
|
|
|
4
179
|
interface EditorOptions {
|
|
5
180
|
/** Host element to mount the editor into. Will be wiped on init. */
|
|
@@ -20,6 +195,63 @@ interface EditorOptions {
|
|
|
20
195
|
* Call `editor.setLocale(...)` to switch at runtime.
|
|
21
196
|
*/
|
|
22
197
|
locale?: Partial<Locale>;
|
|
198
|
+
/**
|
|
199
|
+
* Optional factory for a custom playback engine. Receives the
|
|
200
|
+
* editor's preview host element + the initial project, returns
|
|
201
|
+
* anything satisfying `PlaybackEngine`. Defaults to the built-in
|
|
202
|
+
* `HtmlVideoEngine` (one hidden `<video>` per source, swap on
|
|
203
|
+
* boundaries). Hosts that need frame-accurate editing, multi-track
|
|
204
|
+
* compositing, transitions, etc. pass a `WebCodecsEngine` factory
|
|
205
|
+
* (v0.6+) or their own.
|
|
206
|
+
*/
|
|
207
|
+
playbackEngine?: PlaybackEngineFactory;
|
|
208
|
+
/**
|
|
209
|
+
* Pixel height of each track row in the timeline (default 56). Lower
|
|
210
|
+
* values (~32–40) shrink the timeline footprint for small viewports
|
|
211
|
+
* where the default crowds out the preview. Reasonable range:
|
|
212
|
+
* [28, 96]. Applied process-wide via `setTimelineMetrics` — multi-
|
|
213
|
+
* editor mounts share the value.
|
|
214
|
+
*/
|
|
215
|
+
trackHeight?: number;
|
|
216
|
+
/**
|
|
217
|
+
* Pixel height of the timeline ruler / time-label strip (default 24).
|
|
218
|
+
* Pair with `trackHeight` to compact the whole timeline. Reasonable
|
|
219
|
+
* range: [18, 36].
|
|
220
|
+
*/
|
|
221
|
+
rulerHeight?: number;
|
|
222
|
+
/**
|
|
223
|
+
* Pixel height of the whole bottom timeline area (default 240). The
|
|
224
|
+
* canvas inside fills 100% of this and shows a vertical scrollbar
|
|
225
|
+
* when there are more tracks than fit. Lower this (~120–180) on
|
|
226
|
+
* small viewports so the preview takes more of the editor's height.
|
|
227
|
+
* Reasonable range: [120, 480].
|
|
228
|
+
*/
|
|
229
|
+
timelineHeight?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Per-clip keyframe animation (X / Y / Scale). Off by default; flip
|
|
232
|
+
* `enabled: true` to surface keyframe markers on the timeline and
|
|
233
|
+
* route the canvas / WebCodecs engines through `getEffectiveTransform`
|
|
234
|
+
* when painting. Data is preserved either way — disabling just hides
|
|
235
|
+
* the editing UI and renders identity transforms.
|
|
236
|
+
*
|
|
237
|
+
* HtmlVideoEngine cannot apply per-frame transforms (it shows a raw
|
|
238
|
+
* `<video>`), so keyframes are silently ignored on that engine
|
|
239
|
+
* regardless of this flag. Swap to `CanvasCompositorEngine` or
|
|
240
|
+
* `WebCodecsEngine` for live preview.
|
|
241
|
+
*/
|
|
242
|
+
keyframes?: {
|
|
243
|
+
enabled?: boolean;
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Show the |◀ / ▶| "jump to clip start / end" toolbar buttons and
|
|
247
|
+
* bind the I / O keyboard shortcuts. Off by default — hosts opt in
|
|
248
|
+
* the same way they do for keyframes. When off the buttons are
|
|
249
|
+
* completely hidden (display: none) so they don't take up toolbar
|
|
250
|
+
* space, and the I / O keys fall through to the page.
|
|
251
|
+
*/
|
|
252
|
+
clipEdgeNav?: {
|
|
253
|
+
enabled?: boolean;
|
|
254
|
+
};
|
|
23
255
|
}
|
|
24
256
|
interface EditorEventMap {
|
|
25
257
|
/** Emitted whenever the project mutates. */
|
|
@@ -47,6 +279,23 @@ interface EditorEventMap {
|
|
|
47
279
|
selectionChange: {
|
|
48
280
|
clipId: string | null;
|
|
49
281
|
};
|
|
282
|
+
/** Currently selected keyframe (parent clip + keyframe id), or null.
|
|
283
|
+
* Selecting a keyframe also selects its parent clip — listeners
|
|
284
|
+
* watching `selectionChange` get notified independently. */
|
|
285
|
+
keyframeSelectionChange: {
|
|
286
|
+
target: {
|
|
287
|
+
clipId: string;
|
|
288
|
+
keyframeId: string;
|
|
289
|
+
} | null;
|
|
290
|
+
};
|
|
291
|
+
/** Keyframe-mode toggle changed (Editor.setKeyframesEnabled). */
|
|
292
|
+
keyframesEnabledChange: {
|
|
293
|
+
enabled: boolean;
|
|
294
|
+
};
|
|
295
|
+
/** Jump-to-clip-edge nav toggle changed (Editor.setClipEdgeNavEnabled). */
|
|
296
|
+
clipEdgeNavEnabledChange: {
|
|
297
|
+
enabled: boolean;
|
|
298
|
+
};
|
|
50
299
|
/** Zoom (px/sec) changed. */
|
|
51
300
|
scaleChange: {
|
|
52
301
|
pxPerSec: number;
|
|
@@ -123,10 +372,131 @@ interface EditorApi {
|
|
|
123
372
|
setSnap(snap: boolean): void;
|
|
124
373
|
getSelection(): string | null;
|
|
125
374
|
setSelection(clipId: string | null): void;
|
|
375
|
+
isKeyframesEnabled(): boolean;
|
|
376
|
+
setKeyframesEnabled(enabled: boolean): void;
|
|
377
|
+
isClipEdgeNavEnabled(): boolean;
|
|
378
|
+
setClipEdgeNavEnabled(enabled: boolean): void;
|
|
379
|
+
/** Screen-space CSS-pixel rect of the active rendered frame, post
|
|
380
|
+
* transform, relative to the editor preview. Null when none. */
|
|
381
|
+
getActiveFrameRect(): {
|
|
382
|
+
x: number;
|
|
383
|
+
y: number;
|
|
384
|
+
w: number;
|
|
385
|
+
h: number;
|
|
386
|
+
} | null;
|
|
387
|
+
/** Output frame rect (fixed bounds, no transform). The overlay
|
|
388
|
+
* draws the dashed border here. */
|
|
389
|
+
getActiveOutputFrameRect(): {
|
|
390
|
+
x: number;
|
|
391
|
+
y: number;
|
|
392
|
+
w: number;
|
|
393
|
+
h: number;
|
|
394
|
+
} | null;
|
|
395
|
+
/**
|
|
396
|
+
* Upsert a per-property keyframe at the given clip-local time. If a
|
|
397
|
+
* keyframe for the same `prop` already exists within ~1 frame of
|
|
398
|
+
* `time` it gets its value updated; else a new one is appended.
|
|
399
|
+
* Returns the keyframe's id, or null when the clip can't be found.
|
|
400
|
+
*
|
|
401
|
+
* Defaults: `time` = playhead in clip-local coords; `value` = the
|
|
402
|
+
* currently interpolated value for that prop (so adding doesn't
|
|
403
|
+
* cause a visible jump).
|
|
404
|
+
*/
|
|
405
|
+
addKeyframe(clipId: string, prop: KeyframeProp, opts?: {
|
|
406
|
+
time?: Ms;
|
|
407
|
+
value?: number;
|
|
408
|
+
}): string | null;
|
|
409
|
+
removeKeyframe(clipId: string, keyframeId: string): boolean;
|
|
410
|
+
moveKeyframe(clipId: string, keyframeId: string, timeMs: Ms): boolean;
|
|
411
|
+
/** Change one keyframe's value (single number, since each kf is
|
|
412
|
+
* per-property). */
|
|
413
|
+
setKeyframeValue(clipId: string, keyframeId: string, value: number): boolean;
|
|
414
|
+
/**
|
|
415
|
+
* Change one keyframe's outgoing easing curve. Shapes only the
|
|
416
|
+
* segment from this kf to the NEXT kf in time on the same prop.
|
|
417
|
+
* The kf's value is untouched.
|
|
418
|
+
*/
|
|
419
|
+
setKeyframeEasing(clipId: string, keyframeId: string, easing: EasingKind): boolean;
|
|
420
|
+
/**
|
|
421
|
+
* Batch-set the outgoing easing on every kf at one moment in time
|
|
422
|
+
* (within the 16ms tolerance that the rest of the API uses) on a
|
|
423
|
+
* single clip. Mirrors the panel's "one dropdown for the moment"
|
|
424
|
+
* UX so all three props (panX / panY / scale) at the selected
|
|
425
|
+
* moment animate with the same curve. Single history entry.
|
|
426
|
+
*/
|
|
427
|
+
setKeyframesEasingAtTime(clipId: string, timeMs: Ms, easing: EasingKind): boolean;
|
|
428
|
+
/**
|
|
429
|
+
* CapCut-style auto-record: write `value` for `prop` at the playhead.
|
|
430
|
+
* - If the prop already has keyframes → upsert a keyframe at the
|
|
431
|
+
* playhead with this value.
|
|
432
|
+
* - If the prop has no keyframes → just update the static base
|
|
433
|
+
* (panX / panY / scale on the clip) so the visual changes
|
|
434
|
+
* without committing the user to an animation track yet.
|
|
435
|
+
* Returns true if the project changed.
|
|
436
|
+
*/
|
|
437
|
+
setValueAtPlayhead(clipId: string, prop: KeyframeProp, value: number): boolean;
|
|
438
|
+
getSelectedKeyframe(): {
|
|
439
|
+
clipId: string;
|
|
440
|
+
keyframeId: string;
|
|
441
|
+
} | null;
|
|
442
|
+
setSelectedKeyframe(target: {
|
|
443
|
+
clipId: string;
|
|
444
|
+
keyframeId: string;
|
|
445
|
+
} | null): void;
|
|
446
|
+
/**
|
|
447
|
+
* Toolbar-style toggle. If ANY keyframe exists at the playhead time
|
|
448
|
+
* on the selected clip, remove every keyframe at that time (all
|
|
449
|
+
* props). Otherwise, capture one keyframe per prop (panX, panY,
|
|
450
|
+
* scale) at the playhead with the currently interpolated values.
|
|
451
|
+
* Returns true when the project changed.
|
|
452
|
+
*/
|
|
453
|
+
toggleKeyframeAtPlayhead(): boolean;
|
|
454
|
+
/**
|
|
455
|
+
* Clear every keyframe AND every static transform value on a clip,
|
|
456
|
+
* restoring the identity pose (panX=0, panY=0, scale=1). Single
|
|
457
|
+
* history entry. */
|
|
458
|
+
resetClipTransform(clipId: string): boolean;
|
|
459
|
+
/**
|
|
460
|
+
* Pin all three transform props (panX, panY, scale) to identity
|
|
461
|
+
* (0, 0, 1) at one specific clip-local time. Upserts on each prop —
|
|
462
|
+
* keyframes that already exist at that time get their values
|
|
463
|
+
* overwritten; props with no kf there get one added. Single history
|
|
464
|
+
* entry. Used by the panel's Reset button when a keyframe is selected.
|
|
465
|
+
*/
|
|
466
|
+
resetKeyframesAtTime(clipId: string, timeMs: Ms): boolean;
|
|
467
|
+
/**
|
|
468
|
+
* Move the playhead to a clip edge. "end" intentionally lands 1ms
|
|
469
|
+
* INSIDE the clip (clipEnd - 1) so the playhead remains inside the
|
|
470
|
+
* clip — that lets the user immediately press the keyframe button
|
|
471
|
+
* (or the I/O shortcut) and have it find the right clip + drop a
|
|
472
|
+
* keyframe at clip-local time `duration - 1ms`. Without the -1ms
|
|
473
|
+
* offset the playhead lands on the seam and `toggleKeyframeAtPlayhead`
|
|
474
|
+
* picks the next clip (or none at all).
|
|
475
|
+
* Returns true when the seek actually moved.
|
|
476
|
+
*/
|
|
477
|
+
seekToClipEdge(clipId: string, edge: "start" | "end"): boolean;
|
|
478
|
+
/** Convenience for the toolbar: act on the currently-selected clip. */
|
|
479
|
+
seekToSelectedClipEdge(edge: "start" | "end"): boolean;
|
|
126
480
|
canUndo(): boolean;
|
|
127
481
|
canRedo(): boolean;
|
|
128
482
|
undo(): boolean;
|
|
129
483
|
redo(): boolean;
|
|
484
|
+
/**
|
|
485
|
+
* Open a "drag session". While open, every internal `pushHistory`
|
|
486
|
+
* call captures the pre-session snapshot ONCE — subsequent calls
|
|
487
|
+
* during the same session are no-ops. The session commits a single
|
|
488
|
+
* history entry on `endInteraction()` (or is dropped entirely if
|
|
489
|
+
* the project ended up unchanged). Nestable: nested begin/end pairs
|
|
490
|
+
* count by depth and only the outermost commits.
|
|
491
|
+
*
|
|
492
|
+
* Hosts call this around continuous gestures (drag the preview
|
|
493
|
+
* overlay, scrub a numeric slider, wheel-zoom) so a single user
|
|
494
|
+
* gesture becomes ONE undo entry instead of 30-100. Without this,
|
|
495
|
+
* each pointermove of an overlay drag pushes its own history entry
|
|
496
|
+
* and the user has to mash Cmd+Z that many times to fully undo.
|
|
497
|
+
*/
|
|
498
|
+
beginInteraction(): void;
|
|
499
|
+
endInteraction(): void;
|
|
130
500
|
/**
|
|
131
501
|
* Bookend slot at the very left of the top toolbar — host appends
|
|
132
502
|
* its own controls (e.g. an aspect-ratio dropdown). Empty by default
|
|
@@ -165,6 +535,13 @@ declare class Editor implements EditorApi {
|
|
|
165
535
|
private bus;
|
|
166
536
|
private history;
|
|
167
537
|
private selectedClipId;
|
|
538
|
+
private selectedKeyframe;
|
|
539
|
+
private keyframesEnabled;
|
|
540
|
+
private clipEdgeNavEnabled;
|
|
541
|
+
/** Drag-session bookkeeping for ripple-merge undo. See
|
|
542
|
+
* beginInteraction / endInteraction docs on EditorApi. */
|
|
543
|
+
private interactionDepth;
|
|
544
|
+
private interactionStartSnapshot;
|
|
168
545
|
private pxPerSec;
|
|
169
546
|
private snap;
|
|
170
547
|
private locale;
|
|
@@ -252,10 +629,74 @@ declare class Editor implements EditorApi {
|
|
|
252
629
|
snapMs(timeMs: Ms, ignoreClipId?: string | null): Ms;
|
|
253
630
|
getSelection(): string | null;
|
|
254
631
|
setSelection(clipId: string | null): void;
|
|
632
|
+
isKeyframesEnabled(): boolean;
|
|
633
|
+
/**
|
|
634
|
+
* Screen-space CSS-pixel rect of the actively painted frame
|
|
635
|
+
* (post-transform), relative to the editor's preview element.
|
|
636
|
+
* Null when no clip is active, the engine doesn't expose
|
|
637
|
+
* `getFrameRect`, or the rect isn't computed yet. Used by the
|
|
638
|
+
* library's keyframe-editing overlay.
|
|
639
|
+
*/
|
|
640
|
+
getActiveFrameRect(): {
|
|
641
|
+
x: number;
|
|
642
|
+
y: number;
|
|
643
|
+
w: number;
|
|
644
|
+
h: number;
|
|
645
|
+
} | null;
|
|
646
|
+
/**
|
|
647
|
+
* Screen-space CSS-pixel rect of the OUTPUT FRAME (the fixed
|
|
648
|
+
* stage that clips the rendered video). Different from
|
|
649
|
+
* `getActiveFrameRect` which includes the keyframe transform —
|
|
650
|
+
* this one stays put as the user drags / scales the content.
|
|
651
|
+
* Used by the overlay to anchor the dashed border + drag body.
|
|
652
|
+
*/
|
|
653
|
+
getActiveOutputFrameRect(): {
|
|
654
|
+
x: number;
|
|
655
|
+
y: number;
|
|
656
|
+
w: number;
|
|
657
|
+
h: number;
|
|
658
|
+
} | null;
|
|
659
|
+
setKeyframesEnabled(enabled: boolean): void;
|
|
660
|
+
isClipEdgeNavEnabled(): boolean;
|
|
661
|
+
setClipEdgeNavEnabled(enabled: boolean): void;
|
|
662
|
+
addKeyframe(clipId: string, prop: KeyframeProp, opts?: {
|
|
663
|
+
time?: Ms;
|
|
664
|
+
value?: number;
|
|
665
|
+
}): string | null;
|
|
666
|
+
removeKeyframe(clipId: string, keyframeId: string): boolean;
|
|
667
|
+
moveKeyframe(clipId: string, keyframeId: string, timeMs: Ms): boolean;
|
|
668
|
+
setKeyframeValue(clipId: string, keyframeId: string, value: number): boolean;
|
|
669
|
+
setKeyframeEasing(clipId: string, keyframeId: string, easing: EasingKind): boolean;
|
|
670
|
+
setKeyframesEasingAtTime(clipId: string, timeMs: Ms, easing: EasingKind): boolean;
|
|
671
|
+
setValueAtPlayhead(clipId: string, prop: KeyframeProp, value: number): boolean;
|
|
672
|
+
getSelectedKeyframe(): {
|
|
673
|
+
clipId: string;
|
|
674
|
+
keyframeId: string;
|
|
675
|
+
} | null;
|
|
676
|
+
resetClipTransform(clipId: string): boolean;
|
|
677
|
+
resetKeyframesAtTime(clipId: string, timeMs: Ms): boolean;
|
|
678
|
+
seekToClipEdge(clipId: string, edge: "start" | "end"): boolean;
|
|
679
|
+
seekToSelectedClipEdge(edge: "start" | "end"): boolean;
|
|
680
|
+
toggleKeyframeAtPlayhead(): boolean;
|
|
681
|
+
setSelectedKeyframe(target: {
|
|
682
|
+
clipId: string;
|
|
683
|
+
keyframeId: string;
|
|
684
|
+
} | null): void;
|
|
255
685
|
canUndo(): boolean;
|
|
256
686
|
canRedo(): boolean;
|
|
257
687
|
undo(): boolean;
|
|
258
688
|
redo(): boolean;
|
|
689
|
+
beginInteraction(): void;
|
|
690
|
+
endInteraction(): void;
|
|
691
|
+
/**
|
|
692
|
+
* Selections (clipId + selectedKeyframe) live OUTSIDE the project
|
|
693
|
+
* snapshot, so undo / redo can leave them pointing at ids that no
|
|
694
|
+
* longer exist. Defend against dangling refs by clearing anything
|
|
695
|
+
* the restored project doesn't actually contain — and emit the
|
|
696
|
+
* paired change events so panels / overlays hide cleanly instead
|
|
697
|
+
* of holding zombie references.
|
|
698
|
+
*/
|
|
699
|
+
private reconcileSelectionsWithProject;
|
|
259
700
|
on<K extends EditorEventName>(event: K, handler: (payload: EditorEventMap[K]) => void): () => void;
|
|
260
701
|
off<K extends EditorEventName>(event: K, handler: (payload: EditorEventMap[K]) => void): void;
|
|
261
702
|
destroy(): void;
|
|
@@ -267,6 +708,32 @@ declare class Editor implements EditorApi {
|
|
|
267
708
|
private handleSourceMetadata;
|
|
268
709
|
}
|
|
269
710
|
|
|
711
|
+
/**
|
|
712
|
+
* The transform a clip's content is rendered with at a given moment.
|
|
713
|
+
* Engines apply this INSIDE a fixed output frame: `panX` / `panY`
|
|
714
|
+
* translate the content (in CSS px), `scale` resizes it around the
|
|
715
|
+
* output frame's center. Anything pushed outside the frame is
|
|
716
|
+
* clipped. Identity = `{ panX: 0, panY: 0, scale: 1 }`.
|
|
717
|
+
*/
|
|
718
|
+
interface EffectiveTransform {
|
|
719
|
+
panX: number;
|
|
720
|
+
panY: number;
|
|
721
|
+
scale: number;
|
|
722
|
+
}
|
|
723
|
+
/** Identity transform — no pan, no scaling (content fills the output frame). */
|
|
724
|
+
declare const IDENTITY_TRANSFORM: EffectiveTransform;
|
|
725
|
+
/** True when a transform is effectively identity (within FP slop). */
|
|
726
|
+
declare function isIdentityTransform(t: EffectiveTransform): boolean;
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Effective transform = all three properties evaluated together. The
|
|
730
|
+
* engine applies this to the content inside the fixed output frame:
|
|
731
|
+
* scale around frame center, then translate by (panX, panY).
|
|
732
|
+
*/
|
|
733
|
+
declare function getEffectiveTransform(clip: Clip, localMs: Ms): EffectiveTransform;
|
|
734
|
+
/** Same as `getEffectiveTransform` but takes timeline-absolute time. */
|
|
735
|
+
declare function getTransformAtTimelineTime(clip: Clip, timelineMs: Ms): EffectiveTransform;
|
|
736
|
+
|
|
270
737
|
declare function createEmptyProject(): Project;
|
|
271
738
|
/**
|
|
272
739
|
* Defensive normalization — ensures clips on each track are sorted by
|
|
@@ -282,10 +749,30 @@ declare function normalizeProject(project: Project): Project;
|
|
|
282
749
|
*/
|
|
283
750
|
declare function createId(prefix?: string): string;
|
|
284
751
|
|
|
285
|
-
/** Visual constants — kept here so draw + hit-test share one source of truth.
|
|
286
|
-
|
|
287
|
-
|
|
752
|
+
/** Visual constants — kept here so draw + hit-test share one source of truth.
|
|
753
|
+
* The two row-height values are `let` rather than `const` so hosts can
|
|
754
|
+
* shrink the timeline footprint for small screens via
|
|
755
|
+
* `setTimelineMetrics(...)`. ES module live bindings mean every importer
|
|
756
|
+
* sees the updated value automatically. */
|
|
757
|
+
declare let TRACK_HEIGHT: number;
|
|
758
|
+
declare let RULER_HEIGHT: number;
|
|
288
759
|
declare const HEADER_WIDTH = 96;
|
|
760
|
+
/**
|
|
761
|
+
* Override the default timeline row + ruler heights. Process-wide — call
|
|
762
|
+
* before / during editor construction. Useful when the editor is mounted
|
|
763
|
+
* in a small viewport (e.g. side-by-side panel on a laptop) and the
|
|
764
|
+
* default 56px tracks crowd out the preview.
|
|
765
|
+
*
|
|
766
|
+
* Reasonable ranges: trackHeight ∈ [28, 96], rulerHeight ∈ [18, 36].
|
|
767
|
+
* Anything smaller leaves no room for the clip label or the time ticks.
|
|
768
|
+
*
|
|
769
|
+
* Multi-editor mounts share these values. If you have two editors with
|
|
770
|
+
* different needs, agree on the smaller one or remount on switch.
|
|
771
|
+
*/
|
|
772
|
+
declare function setTimelineMetrics(opts: {
|
|
773
|
+
trackHeight?: number;
|
|
774
|
+
rulerHeight?: number;
|
|
775
|
+
}): void;
|
|
289
776
|
|
|
290
777
|
/**
|
|
291
778
|
* Public options for the standalone `Timeline` component. The class
|
|
@@ -334,6 +821,23 @@ interface TimelineOptions {
|
|
|
334
821
|
}) => void;
|
|
335
822
|
onResizeClip?: (clipId: string, edits: Partial<Pick<Clip, "in" | "out" | "start">>) => void;
|
|
336
823
|
onChange?: (project: Project) => void;
|
|
824
|
+
/**
|
|
825
|
+
* Hosts wire these to forward keyframe edits to the Editor. The
|
|
826
|
+
* Timeline only paints + hit-tests; mutation goes through these
|
|
827
|
+
* callbacks so the Editor can push history + emit events.
|
|
828
|
+
*/
|
|
829
|
+
onSelectKeyframe?: (target: {
|
|
830
|
+
clipId: string;
|
|
831
|
+
keyframeId: string;
|
|
832
|
+
} | null) => void;
|
|
833
|
+
onMoveKeyframe?: (clipId: string, keyframeId: string, timeMs: Ms) => void;
|
|
834
|
+
/** Host-driven state mirror — Editor passes these on every render
|
|
835
|
+
* via `Timeline.setKeyframeState`. */
|
|
836
|
+
keyframesEnabled?: boolean;
|
|
837
|
+
selectedKeyframe?: {
|
|
838
|
+
clipId: string;
|
|
839
|
+
keyframeId: string;
|
|
840
|
+
} | null;
|
|
337
841
|
/**
|
|
338
842
|
* Lets the host predict where a drop will actually land — used to
|
|
339
843
|
* keep the drag-ghost visual honest. The Editor wires this to its
|
|
@@ -382,6 +886,8 @@ declare class Timeline {
|
|
|
382
886
|
private readOnly;
|
|
383
887
|
private autoFitEnabled;
|
|
384
888
|
private locale;
|
|
889
|
+
private keyframesEnabled;
|
|
890
|
+
private selectedKeyframe;
|
|
385
891
|
private scrollLeft;
|
|
386
892
|
private scrollTop;
|
|
387
893
|
private viewportWidth;
|
|
@@ -400,6 +906,7 @@ declare class Timeline {
|
|
|
400
906
|
private scrollbarDrag;
|
|
401
907
|
private hoveredClipId;
|
|
402
908
|
private hoveredTrackIndex;
|
|
909
|
+
private hoveredKeyframe;
|
|
403
910
|
private hoverCursor;
|
|
404
911
|
private dropTargetTrackIndex;
|
|
405
912
|
private snapX;
|
|
@@ -496,6 +1003,15 @@ declare class Timeline {
|
|
|
496
1003
|
private maybeContinueFade;
|
|
497
1004
|
private maybeAutoFit;
|
|
498
1005
|
private buildDrawState;
|
|
1006
|
+
/** Host-pushed state — Editor calls this when its keyframe mode
|
|
1007
|
+
* changes or when a keyframe is selected/deselected externally. */
|
|
1008
|
+
setKeyframeState(state: {
|
|
1009
|
+
enabled?: boolean;
|
|
1010
|
+
selected?: {
|
|
1011
|
+
clipId: string;
|
|
1012
|
+
keyframeId: string;
|
|
1013
|
+
} | null;
|
|
1014
|
+
}): void;
|
|
499
1015
|
private readStyle;
|
|
500
1016
|
private attachPointer;
|
|
501
1017
|
private onPointerDown;
|
|
@@ -524,6 +1040,7 @@ declare class Timeline {
|
|
|
524
1040
|
*/
|
|
525
1041
|
private maybeStartDragAutoScroll;
|
|
526
1042
|
private onPointerUp;
|
|
1043
|
+
private attachKeyboard;
|
|
527
1044
|
private attachWheel;
|
|
528
1045
|
private attachResize;
|
|
529
1046
|
private localCoords;
|
|
@@ -532,4 +1049,4 @@ declare class Timeline {
|
|
|
532
1049
|
private applySnap;
|
|
533
1050
|
}
|
|
534
1051
|
|
|
535
|
-
export { Clip, Editor, type EditorApi, type EditorEventMap, type EditorEventName, type EditorOptions, HEADER_WIDTH, Locale, MediaSource, Ms, Project, RULER_HEIGHT, TRACK_HEIGHT, Theme, Timeline, type TimelineOptions, Track, createEmptyProject, createId, normalizeProject };
|
|
1052
|
+
export { CanvasCompositorEngine, type CanvasCompositorEngineOptions, Clip, EasingKind, Editor, type EditorApi, type EditorEventMap, type EditorEventName, type EditorOptions, type EffectiveTransform, HEADER_WIDTH, HtmlVideoEngine, IDENTITY_TRANSFORM, KeyframeProp, Locale, MediaSource, Ms, PlaybackEngine, PlaybackEngineFactory, PlaybackEngineOptions, Project, RULER_HEIGHT, TRACK_HEIGHT, Theme, Timeline, type TimelineOptions, Track, canvasCompositorEngineFactory, createEmptyProject, createId, getEffectiveTransform, getTransformAtTimelineTime, htmlVideoEngineFactory, isIdentityTransform, normalizeProject, setTimelineMetrics };
|