@linker-design-plus/timeline-track 2.1.3 → 2.1.4

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.
@@ -35,6 +35,7 @@ export declare class Track {
35
35
  private onClipSelect;
36
36
  private onTimeJump;
37
37
  private onClipOverlap?;
38
+ private onPrimaryTrackMagnetMove?;
38
39
  private hasBoundGlobalPointerListenersForDrag;
39
40
  private hasLockedGlobalCursor;
40
41
  private edgeAutoScrollAnimationFrameId;
@@ -56,7 +57,7 @@ export declare class Track {
56
57
  private handleGlobalPointerMove;
57
58
  private handleGlobalPointerEnd;
58
59
  private handleWindowBlur;
59
- constructor(layer: Konva.Layer, config: TrackConfig, trackType: TrackType, zoom: number, trackY: number, trackHeight: number, theme: Theme, onClipUpdate: (clip: ClipType, originalClip?: ClipType, clipUpdates?: ClipStateUpdate[]) => void, onClipAdd: (clip: ClipType) => void, onClipRemove: (clipId: string) => void, onClipSplit: (clip1: ClipType, clip2: ClipType) => void, onClipSelect: (clip: ClipType) => void, onTimeJump: (time: TimeMs) => void, onHorizontalDragAutoScroll?: (nextScrollLeft: number) => number, onClipOverlap?: (clip: ClipType, currentTrackId: string, originalClip?: ClipType | null) => void, onClipCrossTrackPreview?: (clip: ClipType, targetTrackY: number, currentTrackId: string) => 'self' | 'external' | 'clear', onClipCrossTrack?: (clip: ClipType, originalClip: ClipType | null, targetTrackY: number, currentTrackId: string) => boolean | void, onClearDropPreview?: () => void, onClearSelection?: () => void, onSnapGuideChange?: (guideTime: TimeMs | null) => void, onClipToggleSelection?: (clipId: string) => void, onSetSingleSelection?: (clipId: string) => void, getMultiDragClipIds?: (clipId: string) => string[] | null, onMultiDragMove?: (request: MultiDragMoveRequest) => boolean | void, onMultiDragInteractionEnd?: () => void, dragActivationThreshold?: number, enableClipSnap?: boolean, clipSnapThreshold?: number);
60
+ constructor(layer: Konva.Layer, config: TrackConfig, trackType: TrackType, zoom: number, trackY: number, trackHeight: number, theme: Theme, onClipUpdate: (clip: ClipType, originalClip?: ClipType, clipUpdates?: ClipStateUpdate[]) => void, onClipAdd: (clip: ClipType) => void, onClipRemove: (clipId: string) => void, onClipSplit: (clip1: ClipType, clip2: ClipType) => void, onClipSelect: (clip: ClipType) => void, onTimeJump: (time: TimeMs) => void, onHorizontalDragAutoScroll?: (nextScrollLeft: number) => number, onClipOverlap?: (clip: ClipType, currentTrackId: string, originalClip?: ClipType | null) => void, onPrimaryTrackMagnetMove?: (clip: ClipType, originalClip: ClipType | null, peerClips: ClipType[], currentTrackId: string) => boolean | void, onClipCrossTrackPreview?: (clip: ClipType, targetTrackY: number, currentTrackId: string) => 'self' | 'external' | 'clear', onClipCrossTrack?: (clip: ClipType, originalClip: ClipType | null, targetTrackY: number, currentTrackId: string) => boolean | void, onClearDropPreview?: () => void, onClearSelection?: () => void, onSnapGuideChange?: (guideTime: TimeMs | null) => void, onClipToggleSelection?: (clipId: string) => void, onSetSingleSelection?: (clipId: string) => void, getMultiDragClipIds?: (clipId: string) => string[] | null, onMultiDragMove?: (request: MultiDragMoveRequest) => boolean | void, onMultiDragInteractionEnd?: () => void, dragActivationThreshold?: number, enableClipSnap?: boolean, clipSnapThreshold?: number);
60
61
  private initClips;
61
62
  private ensureDropPreviewGroup;
62
63
  private createDropPreviewRect;
@@ -83,6 +84,7 @@ export declare class Track {
83
84
  private resolveDraggedClipForInteraction;
84
85
  private applyInteractionTransition;
85
86
  private handleClipMoveEnd;
87
+ private compactPrimaryTrackClips;
86
88
  private updateAllClips;
87
89
  private buildAndSendUpdates;
88
90
  private updateHoverCursor;
@@ -0,0 +1,16 @@
1
+ import type { TimeMs } from '../models/types';
2
+ export declare const PLAYBACK_VIEWPORT_FOLLOW_MARGIN_PX = 48;
3
+ export declare function getPlayheadScreenX(time: TimeMs, zoom: number, scrollLeft: number): number;
4
+ export interface PlaybackViewportFollowInput {
5
+ time: TimeMs;
6
+ zoom: number;
7
+ scrollLeft: number;
8
+ viewportWidth: number;
9
+ contentWidth: number;
10
+ margin?: number;
11
+ }
12
+ export declare function clampScrollLeft(scrollLeft: number, viewportWidth: number, contentWidth: number): number;
13
+ /**
14
+ * 播放边距跟随:播放头超出左右安全边距时返回目标 scrollLeft,否则返回 null。
15
+ */
16
+ export declare function resolveScrollLeftForPlaybackFollow(input: PlaybackViewportFollowInput): number | null;
@@ -20,6 +20,7 @@ interface TimelineStructureSessionOptions {
20
20
  trackCollection: TimelineTrackCollection;
21
21
  ensureTrackFromHistorySnapshot: (trackSnapshot: Track, restoreAnchor?: TrackRestoreAnchor | null) => string | null;
22
22
  removeClipGaps: () => void;
23
+ setPrimaryTrackMagnetEnabled: (enabled: boolean) => void;
23
24
  loadClipThumbnails?: (clip: Clip) => Promise<boolean> | void;
24
25
  addClipToTrack?: (trackId: string, clip: Clip) => boolean;
25
26
  removeClip?: (clipId: string) => void;
@@ -89,6 +89,8 @@ export declare class TimelineManager {
89
89
  private activeStructureSession;
90
90
  private structureSessionDepth;
91
91
  private preserveEmptyTracksDepth;
92
+ private playbackViewportFollowSuspended;
93
+ private isProgrammaticScrollChange;
92
94
  constructor(config?: Partial<TimelineConfig>);
93
95
  private createPlaybackAttemptId;
94
96
  private refreshPlaybackAttempt;
@@ -134,6 +136,10 @@ export declare class TimelineManager {
134
136
  private get selectedClipId();
135
137
  private set selectedClipId(value);
136
138
  private ensureConfigState;
139
+ private isPlaybackViewportFollowEnabled;
140
+ private suspendPlaybackViewportFollowIfUserScrolling;
141
+ private runProgrammaticScrollChange;
142
+ private followPlaybackViewportIfNeeded;
137
143
  private getCurrentTimeState;
138
144
  private getPlayStateState;
139
145
  private setPlayStateState;
@@ -247,6 +253,17 @@ export declare class TimelineManager {
247
253
  getCurrentTime(): TimeMs;
248
254
  setEnableClipSnap(enabled: boolean): void;
249
255
  getEnableClipSnap(): boolean;
256
+ setEnablePrimaryTrackMagnet(enabled: boolean): void;
257
+ getEnablePrimaryTrackMagnet(): boolean;
258
+ private setPrimaryTrackMagnetEnabledFromHistory;
259
+ private setPrimaryTrackMagnetEnabledState;
260
+ private getPrimaryTrackId;
261
+ private isPrimaryTrackId;
262
+ private shouldNormalizePrimaryTrackForTrackIds;
263
+ private getStableStartOrderedClips;
264
+ private buildPrimaryTrackContinuousClips;
265
+ private buildPrimaryTrackMultiDragOrderedClips;
266
+ private normalizePrimaryTrackInSession;
250
267
  setZoom(zoom: number): void;
251
268
  /**
252
269
  * 以游标(playhead)为中心设置缩放,用于外置 slider 等非幕布上的缩放操作
@@ -415,6 +432,7 @@ export declare class TimelineManager {
415
432
  private emitSelectedClipChangeIfNeeded;
416
433
  private beginSourceLoading;
417
434
  private endSourceLoading;
435
+ private handlePrimaryTrackMagnetMove;
418
436
  private handleClipOverlap;
419
437
  private handleClipCrossTrackPreview;
420
438
  private handleClipCrossTrack;
@@ -586,6 +604,7 @@ export declare class TimelineManager {
586
604
  private resolveMultiDragOverlapTargetTrackId;
587
605
  private commitMultiDragPlacements;
588
606
  private finalizeMultiDragSameTrack;
607
+ private compactPrimaryTrackAfterMultiDrag;
589
608
  private previewMultiDragCrossTrack;
590
609
  private finalizeMultiDragCrossTrack;
591
610
  private resolveTrackIndexDelta;
@@ -1,4 +1,4 @@
1
- import { Action, Clip, ClipStateUpdate, CompoundAction, HistoryState, MoveClipBetweenTracksAction, MoveClipAction, MultiClipUpdateAction, RemoveClipAction, RemoveGapsAction, RestoreClipAudioAction, ResizeClipAction, SeparateClipAudioAction, SetTimeAction, SplitClipAction, Track, TrackRestoreAnchor, TrackHistoryReference, UpdateClipAction, AddClipAction } from '../models/types';
1
+ import { Action, Clip, ClipStateUpdate, CompoundAction, HistoryState, MoveClipBetweenTracksAction, MoveClipAction, MultiClipUpdateAction, RemoveClipAction, RemoveGapsAction, RestoreClipAudioAction, ResizeClipAction, SeparateClipAudioAction, SetPrimaryTrackMagnetAction, SetTimeAction, SplitClipAction, Track, TrackRestoreAnchor, TrackHistoryReference, UpdateClipAction, AddClipAction } from '../models/types';
2
2
  export declare class HistoryManager {
3
3
  private state;
4
4
  private maxHistorySize;
@@ -77,5 +77,6 @@ export declare class HistoryManager {
77
77
  createMoveClipBetweenTracksAction(clipId: string, sourceTrackId: string, targetTrackId: string, clipBefore: Clip, clipAfter: Clip, sourceTrackSnapshot: Track | null, targetTrackSnapshot: Track | null, sourceTrackRestoreAnchor?: TrackRestoreAnchor | null, targetTrackRestoreAnchor?: TrackRestoreAnchor | null): MoveClipBetweenTracksAction;
78
78
  createSeparateClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): SeparateClipAudioAction;
79
79
  createRestoreClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): RestoreClipAudioAction;
80
+ createSetPrimaryTrackMagnetAction(previousEnabled: boolean, nextEnabled: boolean): SetPrimaryTrackMagnetAction;
80
81
  createCompoundAction(actions: Action[], label?: string): CompoundAction;
81
82
  }
@@ -7,6 +7,7 @@ export interface TimelineHistoryExecutionTarget {
7
7
  addClipToTrack(trackId: string, clip: Clip): boolean;
8
8
  updateClip(clipId: string, updates: Partial<Clip>): void;
9
9
  moveClipToTrack(clipId: string, targetTrackId: string): boolean;
10
+ setPrimaryTrackMagnetEnabled?(enabled: boolean): void;
10
11
  ensureTrackFromHistorySnapshot(trackSnapshot: Track, restoreAnchor?: TrackRestoreAnchor | null): string | null;
11
12
  removeClipGaps(): void;
12
13
  getClips(): Clip[];
@@ -16,6 +16,7 @@ export declare class TimelineHistoryRecorder {
16
16
  recordRestoreClipAudio(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
17
17
  createSeparateClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
18
18
  createRestoreClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
19
+ recordSetPrimaryTrackMagnet(previousEnabled: boolean, nextEnabled: boolean): Action;
19
20
  recordCompoundAction(actions: Action[], label?: string): Action | null;
20
21
  createClipUpdateAction(clip: Clip, originalClip?: Clip, clipUpdates?: ClipStateUpdate[]): Action | null;
21
22
  withTransaction<T>(label: string | undefined, callback: () => T): T;
@@ -223,6 +223,7 @@ export interface TimelineConfig {
223
223
  speed?: number;
224
224
  dragActivationThreshold?: number;
225
225
  enableClipSnap?: boolean;
226
+ enablePrimaryTrackMagnet?: boolean;
226
227
  clipSnapThreshold?: number;
227
228
  thumbnailProvider?: ThumbnailProvider;
228
229
  previewBackend?: PreviewBackendType;
@@ -231,6 +232,8 @@ export interface TimelineConfig {
231
232
  textPreviewFont?: TextPreviewFontConfig | null;
232
233
  draftData?: TimelineExportData;
233
234
  keyboardShortcuts?: false | TimelineKeyboardShortcutsConfig;
235
+ /** 播放时自动横向滚动以保持播放头在可视安全区内;默认开启 */
236
+ playbackViewportFollow?: boolean;
234
237
  }
235
238
  export type PreviewAspectRatioMode = 'auto-first-added-video' | 'auto-first-video' | 'manual';
236
239
  export interface PreviewAspectRatio {
@@ -405,6 +408,14 @@ export interface RestoreClipAudioAction {
405
408
  };
406
409
  timestamp: number;
407
410
  }
411
+ export interface SetPrimaryTrackMagnetAction {
412
+ type: 'set_primary_track_magnet';
413
+ data: {
414
+ previousEnabled: boolean;
415
+ nextEnabled: boolean;
416
+ };
417
+ timestamp: number;
418
+ }
408
419
  export interface CompoundAction {
409
420
  type: 'compound';
410
421
  data: {
@@ -413,7 +424,7 @@ export interface CompoundAction {
413
424
  };
414
425
  timestamp: number;
415
426
  }
416
- export type Action = AddClipAction | RemoveClipAction | UpdateClipAction | SplitClipAction | MoveClipAction | ResizeClipAction | SetTimeAction | RemoveGapsAction | MultiClipUpdateAction | MoveClipBetweenTracksAction | SeparateClipAudioAction | RestoreClipAudioAction | CompoundAction;
427
+ export type Action = AddClipAction | RemoveClipAction | UpdateClipAction | SplitClipAction | MoveClipAction | ResizeClipAction | SetTimeAction | RemoveGapsAction | MultiClipUpdateAction | MoveClipBetweenTracksAction | SeparateClipAudioAction | RestoreClipAudioAction | SetPrimaryTrackMagnetAction | CompoundAction;
417
428
  export type ActionType = Action['type'];
418
429
  export interface Position {
419
430
  x: number;
@@ -18,6 +18,7 @@ export interface TimelineTrackBridgeHandlers {
18
18
  onTimeJump: (time: TimeMs) => void;
19
19
  onHorizontalDragAutoScroll?: (nextScrollLeft: number) => number;
20
20
  onClipOverlap?: (clip: Clip, currentTrackId: string, originalClip?: Clip | null) => void;
21
+ onPrimaryTrackMagnetMove?: (clip: Clip, originalClip: Clip | null, peerClips: Clip[], currentTrackId: string) => boolean | void;
21
22
  onClipCrossTrackPreview?: (clip: Clip, targetTrackY: number, currentTrackId: string) => 'self' | 'external' | 'clear';
22
23
  onClipCrossTrack?: (clip: Clip, originalClip: Clip | null, targetTrackY: number, currentTrackId: string) => boolean | void;
23
24
  onClearDropPreview?: () => void;
@@ -41,7 +42,7 @@ export interface TimelineTrackBridgeCreateOptions {
41
42
  enableClipSnap?: boolean;
42
43
  clipSnapThreshold?: number;
43
44
  }
44
- export type TimelineTrackConstructor<TTrack = Track> = new (layer: Konva.Layer, config: TrackConfig, trackType: TrackType, zoom: number, trackY: number, trackHeight: number, theme: Theme, onClipUpdate: (clip: Clip, originalClip?: Clip, clipUpdates?: ClipStateUpdate[]) => void, onClipAdd: (clip: Clip) => void, onClipRemove: (clipId: string) => void, onClipSplit: (clip1: Clip, clip2: Clip) => void, onClipSelect: (clip: Clip) => void, onTimeJump: (time: TimeMs) => void, onHorizontalDragAutoScroll?: (nextScrollLeft: number) => number, onClipOverlap?: (clip: Clip, currentTrackId: string, originalClip?: Clip | null) => void, onClipCrossTrackPreview?: (clip: Clip, targetTrackY: number, currentTrackId: string) => 'self' | 'external' | 'clear', onClipCrossTrack?: (clip: Clip, originalClip: Clip | null, targetTrackY: number, currentTrackId: string) => boolean | void, onClearDropPreview?: () => void, onClearSelection?: () => void, onSnapGuideChange?: (guideTime: TimeMs | null) => void, onClipToggleSelection?: (clipId: string) => void, onSetSingleSelection?: (clipId: string) => void, getMultiDragClipIds?: (clipId: string) => string[] | null, onMultiDragMove?: (request: MultiDragMoveRequest) => boolean | void, onMultiDragInteractionEnd?: () => void, dragActivationThreshold?: number, enableClipSnap?: boolean, clipSnapThreshold?: number) => TTrack;
45
+ export type TimelineTrackConstructor<TTrack = Track> = new (layer: Konva.Layer, config: TrackConfig, trackType: TrackType, zoom: number, trackY: number, trackHeight: number, theme: Theme, onClipUpdate: (clip: Clip, originalClip?: Clip, clipUpdates?: ClipStateUpdate[]) => void, onClipAdd: (clip: Clip) => void, onClipRemove: (clipId: string) => void, onClipSplit: (clip1: Clip, clip2: Clip) => void, onClipSelect: (clip: Clip) => void, onTimeJump: (time: TimeMs) => void, onHorizontalDragAutoScroll?: (nextScrollLeft: number) => number, onClipOverlap?: (clip: Clip, currentTrackId: string, originalClip?: Clip | null) => void, onPrimaryTrackMagnetMove?: (clip: Clip, originalClip: Clip | null, peerClips: Clip[], currentTrackId: string) => boolean | void, onClipCrossTrackPreview?: (clip: Clip, targetTrackY: number, currentTrackId: string) => 'self' | 'external' | 'clear', onClipCrossTrack?: (clip: Clip, originalClip: Clip | null, targetTrackY: number, currentTrackId: string) => boolean | void, onClearDropPreview?: () => void, onClearSelection?: () => void, onSnapGuideChange?: (guideTime: TimeMs | null) => void, onClipToggleSelection?: (clipId: string) => void, onSetSingleSelection?: (clipId: string) => void, getMultiDragClipIds?: (clipId: string) => string[] | null, onMultiDragMove?: (request: MultiDragMoveRequest) => boolean | void, onMultiDragInteractionEnd?: () => void, dragActivationThreshold?: number, enableClipSnap?: boolean, clipSnapThreshold?: number) => TTrack;
45
46
  export declare class TimelineTrackBridge<TTrack = Track> {
46
47
  private readonly handlers;
47
48
  private readonly TrackCtor;