@linker-design-plus/timeline-track 2.0.13 → 2.0.15

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
@@ -89,6 +89,7 @@ timeline.on('history_change', (_event, data) => {
89
89
  - `updateClip(clipId, updates)` / `removeClip(clipId)`
90
90
  - `selectClip(clipId)` / `clearSelection()` / `getSelectedClip()`
91
91
  - `splitCurrentClip()` / `removeClipGaps()`
92
+ - `getRenderedHeight()`
92
93
  - `undo()` / `redo()` / `clearHistory()`
93
94
  - `attachPreview(containerOrConfig)` / `detachPreview()`
94
95
  - `exportTimeline()` / `loadDraft(data)`
@@ -103,6 +104,9 @@ timeline.on('history_change', (_event, data) => {
103
104
 
104
105
  - 播放 / 暂停:`Space`
105
106
  - 删除片段:`Delete` / `Backspace`
107
+ - 复制片段:`Mod+C`
108
+ - 剪切片段:`Mod+X`
109
+ - 粘贴片段:`Mod+V`
106
110
  - 分离 / 还原音频:`Mod+Alt+A`
107
111
  - 分割片段:`Mod+B`
108
112
  - 撤销:`Mod+Z`
@@ -94,8 +94,6 @@ export declare class ClipConfigPanel {
94
94
  private syncVoiceStateForClip;
95
95
  private syncActiveTab;
96
96
  private supportsVoicePanel;
97
- private resolveDefaultVoiceFilterTag;
98
- private getVisibleVoiceOptions;
99
97
  private ensureVoiceFilters;
100
98
  private handleVoiceFilterChange;
101
99
  private handleVoiceSelect;
@@ -79,6 +79,7 @@ export declare class ClipConfigPanelRenderer extends LitDomRenderer<ClipConfigPa
79
79
  private renderVoiceLoadingOverlay;
80
80
  private renderEmptyState;
81
81
  private renderPanel;
82
+ private renderTabPanel;
82
83
  private renderTabBar;
83
84
  private renderActionBar;
84
85
  private renderVoiceActionBar;
@@ -1,5 +1,6 @@
1
1
  import type { ActiveClipPlaybackInfo, Clip, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewMediaSource, PreviewSourceResolver, TextPreviewFontConfig, TimeMs } from '../models/types';
2
2
  import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
3
+ import type { DiagnosticsCenter, DiagnosticEmitInput } from '../../utils/diagnostics';
3
4
  export interface TimelinePreviewSyncPayload {
4
5
  activeClips: ActiveClipPlaybackInfo[];
5
6
  nextClips: ActiveClipPlaybackInfo[];
@@ -8,6 +9,7 @@ export interface TimelinePreviewSyncPayload {
8
9
  currentTime: TimeMs;
9
10
  playState: PlayState;
10
11
  speed: number;
12
+ interactionMode?: 'steady' | 'seek' | 'scrub';
11
13
  primarySelectedClipId?: string | null;
12
14
  syncRequestId?: number;
13
15
  }
@@ -17,6 +19,7 @@ export interface TimelinePreviewBackendCallbacks {
17
19
  onSyncProcessed?: (syncRequestId?: number) => void;
18
20
  onRuntimeStateChange?: (state: PreviewRuntimeState) => void;
19
21
  onAspectRatioChange?: (aspectRatio: PreviewAspectRatio) => void;
22
+ onPreviewClipSelect?: (clipId: string) => void;
20
23
  onVisualTransformCommit?: (clipId: string, visualTransform: ClipVisualTransform) => void;
21
24
  onPendingPreviewRetry?: () => void;
22
25
  onRuntimeError?: (error: unknown) => void;
@@ -69,6 +72,8 @@ export interface TimelinePreviewBackendOptions {
69
72
  previewSourceResolver?: PreviewSourceResolver;
70
73
  textPreviewFont?: TextPreviewFontConfig | null;
71
74
  sourceMediaRegistry?: SourceMediaRegistry;
75
+ diagnostics?: DiagnosticsCenter;
76
+ getDiagnosticsContext?: () => Partial<DiagnosticEmitInput>;
72
77
  rootClassName?: string;
73
78
  frameClassName?: string;
74
79
  slotClassNamePrefix?: string;
@@ -3,6 +3,7 @@ import type { DomRenderer } from '../renderers/domRenderer';
3
3
  export interface PreviewPendingOverlayViewModel {
4
4
  pendingState: PreviewPendingState | null;
5
5
  runtimeState: PreviewRuntimeState;
6
+ isBuffering: boolean;
6
7
  }
7
8
  export declare class PreviewPendingOverlayRenderer implements DomRenderer<PreviewPendingOverlayViewModel> {
8
9
  private container;
@@ -17,6 +17,7 @@ export interface ClipSelectPlan {
17
17
  export declare function applyClipMutationEffects(handlers: ClipMutationEffectHandlers): void;
18
18
  export declare function applyClipUpdateEffects(handlers: ClipUpdateMutationEffectHandlers): void;
19
19
  export declare function shouldReloadClipThumbnails(clip: Clip, originalClip?: Clip, hasThumbnailProvider?: boolean): boolean;
20
+ export declare function calculatePlaybackEndTime(clips: Pick<Clip, 'endTime'>[]): number;
20
21
  export declare function calculateCanPlayState(clips: Pick<Clip, 'endTime'>[], currentTime: number): boolean;
21
22
  interface SyncCanPlayStateOptions {
22
23
  currentCanPlay: boolean;
@@ -2,11 +2,17 @@ import type { TimelineKeyboardShortcutsConfig } from '../models';
2
2
  export interface TimelineKeyboardShortcutsControllerCallbacks {
3
3
  togglePlay(): void;
4
4
  deleteSelectedClips(): void;
5
+ copySelectedClips(): void;
6
+ cutSelectedClips(): void;
7
+ pasteClipboardClips(): Promise<boolean> | boolean;
5
8
  toggleSelectedClipsAudio(): Promise<boolean>;
6
9
  splitCurrentClip(): void;
7
10
  undo(): boolean;
8
11
  redo(): boolean;
9
12
  canDeleteSelectedClips(): boolean;
13
+ canCopySelectedClips(): boolean;
14
+ canCutSelectedClips(): boolean;
15
+ canPasteClipboardClips(): boolean;
10
16
  canSplitSelectedClip(): boolean;
11
17
  canToggleSelectedClipsAudio(): boolean;
12
18
  }
@@ -1,12 +1,14 @@
1
1
  import type { ActiveClipPlaybackInfo, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewSourceResolver, TextPreviewFontConfig, TimeMs, TrackType } from '../models/types';
2
2
  import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
3
3
  import type { PreviewPendingState, PreviewRuntimeState } from './previewBackend';
4
+ import { type DiagnosticEmitInput, type DiagnosticsCenter } from '../../utils/diagnostics';
4
5
  interface TimelinePreviewSessionCallbacks {
5
6
  onBufferingStateChange?: (isBuffering: boolean) => void;
6
7
  onSourceLoadingChange?: (pending: number) => void;
7
8
  onSyncProcessed?: (syncRequestId?: number) => void;
8
9
  onRuntimeStateChange?: (state: PreviewRuntimeState) => void;
9
10
  onAspectRatioChange?: (aspectRatio: PreviewAspectRatio) => void;
11
+ onPreviewClipSelect?: (clipId: string) => void;
10
12
  onVisualTransformCommit?: (clipId: string, visualTransform: ClipVisualTransform) => void;
11
13
  onPendingPreviewRetry?: () => void;
12
14
  }
@@ -14,6 +16,8 @@ interface TimelinePreviewSessionDependencies {
14
16
  createMediaElement?: (kind: TrackType, role: 'current' | 'preload') => HTMLMediaElement;
15
17
  createAspectRatioProbe?: () => HTMLVideoElement;
16
18
  createAudioContext?: () => AudioContext | null;
19
+ diagnostics?: DiagnosticsCenter;
20
+ getDiagnosticsContext?: () => Partial<DiagnosticEmitInput>;
17
21
  rootClassName?: string;
18
22
  frameClassName?: string;
19
23
  slotClassNamePrefix?: string;
@@ -29,6 +33,7 @@ export interface TimelinePreviewSyncPayload {
29
33
  currentTime: TimeMs;
30
34
  playState: PlayState;
31
35
  speed: number;
36
+ interactionMode?: 'steady' | 'seek' | 'scrub';
32
37
  primarySelectedClipId?: string | null;
33
38
  syncRequestId?: number;
34
39
  }
@@ -73,8 +78,10 @@ export declare class TimelinePreviewSession {
73
78
  private pendingState;
74
79
  private activeSyncRequestId;
75
80
  private isSyncProjecting;
81
+ private readonly deferredPreloadSlotKeys;
82
+ private deferredPreloadFlushScheduled;
76
83
  constructor(callbacks?: TimelinePreviewSessionCallbacks, dependencies?: TimelinePreviewSessionDependencies);
77
- private logLoadTrace;
84
+ private emitDiagnostic;
78
85
  private buildSlotTraceData;
79
86
  hasPreview(): boolean;
80
87
  attach(container: HTMLElement): void;
@@ -85,6 +92,7 @@ export declare class TimelinePreviewSession {
85
92
  getAspectRatio(): PreviewAspectRatio;
86
93
  private createMediaElement;
87
94
  private createSlot;
95
+ private resetSlotRecoveryTracking;
88
96
  private destroySlot;
89
97
  private getTrackSlots;
90
98
  private swapTrackSlots;
@@ -95,8 +103,24 @@ export declare class TimelinePreviewSession {
95
103
  private slotNeedsRecovery;
96
104
  private recoverSlot;
97
105
  private finishSlotRecovery;
106
+ private getSlotKey;
107
+ private isActiveCurrentSlot;
108
+ private hasBlockingActiveCurrentSlot;
109
+ private shouldDeferPreloadTarget;
110
+ private shouldDeferPreloadRecovery;
111
+ private buildDeferredPreloadTarget;
112
+ private deferPreloadTarget;
113
+ private deferPreloadRecovery;
114
+ private scheduleDeferredPreloadFlush;
115
+ private flushDeferredPreloads;
98
116
  private applyResolvedSlotState;
99
117
  private isCurrentRequest;
118
+ private shouldIgnoreExpectedEmptied;
119
+ private shouldIgnoreExpectedAbort;
120
+ private shouldIgnoreClearedSlotRecoverableEvent;
121
+ private shouldSkipImmediatePreloadRecovery;
122
+ private isBackgroundPreloadSlot;
123
+ private parkPreloadSlot;
100
124
  private failSlot;
101
125
  private configureAudioRouting;
102
126
  private syncCurrentSlot;
@@ -2,6 +2,7 @@ import { type TimelinePreviewBackend } from '../controllers';
2
2
  import { TimelineConfig, Clip, ClipConfig, TimeMs, PlayState, Action, TimelineEvent, EventListener as TimelineEventListener, TimelineExportData, Track as TrackEntity, ThumbnailProvider, TrackInsertionPlacement, TrackType, ActiveClipPlaybackInfo, PreviewAspectRatio, PreviewMountConfig, PreviewBackendType, SelectedClipAudioAction, ClipConfigVoicePanelOptions } from '../models';
3
3
  import type { ResolvedPlaybackPlan } from '../controllers/timelinePlaybackResolver';
4
4
  import { TimelineClipConfigController } from '../controllers/timelineClipConfigController';
5
+ import { type DiagnosticExportPackage, type DiagnosticLiveSnapshot, type DiagnosticsListener, type DiagnosticsLiveListener, type DiagnosticSessionSnapshot } from '../../utils/diagnostics';
5
6
  export declare class TimelineManager {
6
7
  private timeline;
7
8
  private tracks;
@@ -63,13 +64,27 @@ export declare class TimelineManager {
63
64
  private readonly bodyCanvasHostClickListener;
64
65
  private readonly rootWheelListener;
65
66
  private keyboardShortcutsController;
67
+ private clipClipboard;
66
68
  private mountManager;
67
69
  private pendingDraftData;
68
70
  private selectionStore;
69
71
  private multiDragSession;
70
72
  private clipRemovalBatchDepth;
71
73
  private splitOperationDepth;
74
+ private readonly diagnostics;
75
+ private diagnosticsPanel;
76
+ private previewRuntimeState;
77
+ private previewActiveClipIds;
78
+ private playbackAttemptId;
79
+ private lastPreviewSyncedPlayState;
80
+ private previewSyncInteractionMode;
72
81
  constructor(config?: Partial<TimelineConfig>);
82
+ private createPlaybackAttemptId;
83
+ private refreshPlaybackAttempt;
84
+ private emitDiagnostic;
85
+ private buildDiagnosticsRuntimeState;
86
+ private buildPreviewDiagnosticsContext;
87
+ private mountDiagnosticsPanel;
73
88
  private getTimelineStore;
74
89
  private getTimelineCommands;
75
90
  private getTrackCollection;
@@ -136,14 +151,17 @@ export declare class TimelineManager {
136
151
  private getTracksSortedByOrder;
137
152
  private getPlaybackTracksSnapshot;
138
153
  private buildPlaybackPlan;
154
+ private hasTimelineVideoClip;
139
155
  private getPreviewAutoAspectRatioClipOrderMap;
140
156
  private getNextPreviewAutoAspectRatioOrder;
141
157
  private getAutoAspectRatioClip;
142
158
  private registerPreviewAutoAspectRatioClip;
143
159
  private syncPreviewSession;
160
+ private syncPreviewPlaybackStateIfNeeded;
144
161
  private beginPendingPreview;
145
162
  updatePendingPreviewState(): void;
146
163
  private buildPreviewPendingState;
164
+ private buildPreviewIndicatorState;
147
165
  private clearPendingPreviewState;
148
166
  private resetPreviewRuntimeState;
149
167
  private destroyPreviewSession;
@@ -321,9 +339,20 @@ export declare class TimelineManager {
321
339
  private applyGeneratedAudioClipResult;
322
340
  private syncTextClipDurationToAudio;
323
341
  private doesClipOverlapOnTrack;
342
+ private doesClipGroupOverlapOnTrack;
324
343
  private findNearestAvailableTrackForClip;
325
- private relocateGeneratedAudioClipIfNeeded;
344
+ private resolveOverlapRelocationTargetTrackId;
345
+ private relocateClipToAvoidOverlap;
346
+ private buildClipboardPasteGroups;
347
+ private createTracksForTypeBoundary;
348
+ private buildClipboardPasteGroupPlacementClips;
349
+ private ensureClipboardPasteSourceTracks;
350
+ private resolveClipboardPasteTypeGroupAssignments;
351
+ private resolvePreferredTrackIndexForPasteGroup;
352
+ private resolveClipboardPasteTrackAssignments;
353
+ private relocateClipIfNeeded;
326
354
  private regenerateVoiceLinkedAudioClips;
355
+ private getSelectedTextClipsForVoiceGeneration;
327
356
  private handleVoiceGenerateAction;
328
357
  private primeOrLoadClipThumbnails;
329
358
  private markClipThumbnailLoadCompleted;
@@ -348,6 +377,13 @@ export declare class TimelineManager {
348
377
  getPlayState(): PlayState;
349
378
  setDuration(duration: TimeMs): void;
350
379
  getDuration(): TimeMs;
380
+ /**
381
+ * 获取当前时间轴内容完整渲染高度。
382
+ *
383
+ * 该高度包含顶部时间刻度、所有轨道内容和底部横向滚动条,可供外部容器
384
+ * 在需要贴合内容时作为高度参考。
385
+ */
386
+ getRenderedHeight(): number;
351
387
  /**
352
388
  * 调整时间轴大小以适配容器
353
389
  * @param width 新的宽度
@@ -383,6 +419,15 @@ export declare class TimelineManager {
383
419
  toggleSelection(clipId: string): void;
384
420
  isClipSelected(clipId: string): boolean;
385
421
  deleteSelectedClips(): void;
422
+ copySelectedClips(): boolean;
423
+ cutSelectedClips(): boolean;
424
+ pasteClipboardClips(): Promise<boolean>;
425
+ private deleteSelectedClipsWithHistoryLabel;
426
+ private captureSelectedClipsToClipboard;
427
+ private cloneClipForClipboard;
428
+ private buildClipboardPasteClipConfig;
429
+ private resolveClipboardPasteTrackId;
430
+ private generateClipId;
386
431
  separateSelectedClipsAudio(): void;
387
432
  selectAllClips(): void;
388
433
  private emitSelectionChangeEvent;
@@ -406,6 +451,9 @@ export declare class TimelineManager {
406
451
  getSelectedClip(): Clip | null;
407
452
  getPrimarySelectedClip(): Clip | null;
408
453
  getSelectedClips(): Clip[];
454
+ canCopySelectedClips(): boolean;
455
+ canCutSelectedClips(): boolean;
456
+ canPasteClipboardClips(): boolean;
409
457
  canDeleteSelectedClips(): boolean;
410
458
  canSplitSelectedClip(): boolean;
411
459
  getSelectedClipAudioAction(): SelectedClipAudioAction;
@@ -421,6 +469,12 @@ export declare class TimelineManager {
421
469
  getPreviewBackendType(): Exclude<PreviewBackendType, 'auto'>;
422
470
  hasAttachedPreview(): boolean;
423
471
  getPreviewAspectRatio(): PreviewAspectRatio;
472
+ getDiagnosticsSnapshot(): DiagnosticSessionSnapshot;
473
+ subscribeDiagnostics(listener: DiagnosticsListener): () => void;
474
+ getDiagnosticsLiveSnapshot(recentEventLimit?: number): DiagnosticLiveSnapshot;
475
+ subscribeDiagnosticsLive(listener: DiagnosticsLiveListener, recentEventLimit?: number): () => void;
476
+ exportDiagnosticsPackage(): DiagnosticExportPackage;
477
+ clearDiagnostics(sessionOnly?: boolean): void;
424
478
  setPreviewAspectRatio(aspectRatio: {
425
479
  width: number;
426
480
  height: number;
@@ -434,6 +488,7 @@ export declare class TimelineManager {
434
488
  removeSelectedClips(): boolean;
435
489
  canSeparateClipAudio(clipId: string): boolean;
436
490
  canRestoreClipAudio(clipId: string): boolean;
491
+ private pausePlaybackForAudioSeparation;
437
492
  private resolvePreferredSeparatedAudioTrackId;
438
493
  separateClipAudio(clipId: string): Promise<string | null>;
439
494
  restoreClipAudio(clipId: string): boolean;
@@ -498,6 +553,7 @@ export declare class TimelineManager {
498
553
  private handleBoxSelectEnd;
499
554
  private isPointOnClip;
500
555
  private getClipsIntersectingBox;
556
+ private resolveTrackRenderHeight;
501
557
  private addClipToTrack;
502
558
  private cloneTrackSnapshot;
503
559
  private ensureTrackFromHistorySnapshot;
@@ -3,6 +3,7 @@ import type { Action, Clip, ClipStateUpdate, Track, TrackRestoreAnchor } from '.
3
3
  export declare class TimelineHistoryRecorder {
4
4
  private readonly history;
5
5
  constructor(history: HistoryManager);
6
+ createAddClipAction(clip: Clip): Action;
6
7
  recordAddClip(clip: Clip): Action;
7
8
  createRemoveClipAction(clip: Clip, sourceTrackId?: string | null, sourceTrackSnapshot?: Track | null, sourceTrackRestoreAnchor?: TrackRestoreAnchor | null): Action;
8
9
  recordRemoveClip(clip: Clip, sourceTrackId?: string | null, sourceTrackSnapshot?: Track | null, sourceTrackRestoreAnchor?: TrackRestoreAnchor | null): Action;
@@ -1,12 +1,8 @@
1
+ export type { DiagnosticEvent, DiagnosticExportPackage, DiagnosticLiveEvent, DiagnosticLiveSnapshot, DiagnosticsConfig, DiagnosticsLiveListener, DiagnosticSessionSnapshot } from '../../utils/diagnostics/types';
2
+ import type { DiagnosticsConfig } from '../../utils/diagnostics/types';
1
3
  export type TrackType = 'video' | 'audio' | 'text';
2
4
  export type PlayState = 'playing' | 'paused';
3
5
  export type TimeMs = number;
4
- export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
5
- export interface LogConfig {
6
- enabled?: boolean;
7
- level?: LogLevel;
8
- moduleLevels?: Record<string, LogLevel>;
9
- }
10
6
  export interface ThumbnailProvider {
11
7
  /**
12
8
  * 获取片段的封面图片
@@ -26,7 +22,6 @@ export interface TextClipStyle {
26
22
  rotation: number;
27
23
  strokeColor: string;
28
24
  strokeWidth: number;
29
- lineHeight: number;
30
25
  paddingX: number;
31
26
  }
32
27
  export declare const DEFAULT_TEXT_CLIP_STYLE: TextClipStyle;
@@ -187,7 +182,7 @@ export type ThemeConfig = Partial<Theme>;
187
182
  export type ResolvedTimelineConfig = Omit<TimelineConfig, 'theme'> & {
188
183
  theme: Theme;
189
184
  };
190
- export type TimelineKeyboardShortcutAction = 'togglePlay' | 'deleteSelectedClips' | 'toggleSelectedClipsAudio' | 'splitCurrentClip' | 'undo' | 'redo';
185
+ export type TimelineKeyboardShortcutAction = 'togglePlay' | 'deleteSelectedClips' | 'copySelectedClips' | 'cutSelectedClips' | 'pasteClipboardClips' | 'toggleSelectedClipsAudio' | 'splitCurrentClip' | 'undo' | 'redo';
191
186
  export type TimelineKeyboardShortcutBindings = Partial<Record<TimelineKeyboardShortcutAction, string | string[]>>;
192
187
  export interface TimelineKeyboardShortcutsConfig {
193
188
  enabled?: boolean;
@@ -203,7 +198,7 @@ export interface TimelineConfig {
203
198
  container?: HTMLElement;
204
199
  theme?: ThemeConfig;
205
200
  timeScaleHeight?: number;
206
- logConfig?: LogConfig;
201
+ diagnostics?: DiagnosticsConfig;
207
202
  speed?: number;
208
203
  dragActivationThreshold?: number;
209
204
  enableClipSnap?: boolean;
@@ -453,7 +448,7 @@ export interface EventListener {
453
448
  export interface TimelineManagerConfig {
454
449
  initialDuration?: TimeMs;
455
450
  initialZoom?: number;
456
- logConfig?: LogConfig;
451
+ diagnostics?: DiagnosticsConfig;
457
452
  thumbnailProvider?: ThumbnailProvider;
458
453
  }
459
454
  export interface TimelineExportData {