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

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
@@ -39,7 +39,12 @@ const timeline = new TimelineManager({
39
39
  container,
40
40
  zoom: 100,
41
41
  currentTime: 0,
42
- previewBackend: 'dom'
42
+ previewBackend: 'dom',
43
+ keyboardShortcuts: {
44
+ bindings: {
45
+ togglePlay: 'Mod+Alt+P'
46
+ }
47
+ }
43
48
  });
44
49
 
45
50
  timeline.attachPreview(preview);
@@ -88,6 +93,21 @@ timeline.on('history_change', (_event, data) => {
88
93
  - `attachPreview(containerOrConfig)` / `detachPreview()`
89
94
  - `exportTimeline()` / `loadDraft(data)`
90
95
 
96
+ 快捷键配置:
97
+
98
+ - `keyboardShortcuts: false` 可彻底关闭快捷键
99
+ - `keyboardShortcuts.bindings` 可按动作覆盖默认键位
100
+ - 时间轴挂载后默认在当前页面生效
101
+
102
+ 默认快捷键:
103
+
104
+ - 播放 / 暂停:`Space`
105
+ - 删除片段:`Delete` / `Backspace`
106
+ - 分离 / 还原音频:`Mod+Alt+A`
107
+ - 分割片段:`Mod+B`
108
+ - 撤销:`Mod+Z`
109
+ - 还原:`Mod+Shift+Z`,Windows 另支持 `Ctrl+Y`
110
+
91
111
  常用事件:
92
112
 
93
113
  - `time_change`
@@ -1,4 +1,4 @@
1
- import type { Clip } from '../../core/models';
1
+ import { type Clip, type VoiceOption } from '../../core/models';
2
2
  export interface ClipConfigPanelTheme {
3
3
  backgroundColor: string;
4
4
  borderColor: string;
@@ -17,7 +17,9 @@ export declare const defaultDarkTheme: ClipConfigPanelTheme;
17
17
  export interface ClipConfigPanelConfig {
18
18
  container: HTMLElement;
19
19
  theme?: ClipConfigPanelTheme;
20
+ voiceCatalog?: VoiceOption[];
20
21
  onClipUpdate?: (clipId: string, updates: Partial<Clip>) => void;
22
+ onGenerateVoice?: (clip: Clip, voice: VoiceOption, followTextUpdates: boolean) => Promise<void>;
21
23
  }
22
24
  export declare const CSS_CLASSES: {
23
25
  readonly container: "clip-config-panel";
@@ -26,6 +28,8 @@ export declare const CSS_CLASSES: {
26
28
  readonly content: "clip-config-content";
27
29
  readonly visualPanel: "clip-config-visual-panel";
28
30
  readonly audioPanel: "clip-config-audio-panel";
31
+ readonly textPanel: "clip-config-text-panel";
32
+ readonly voicePanel: "clip-config-voice-panel";
29
33
  readonly actionBar: "clip-config-action-bar";
30
34
  readonly section: "clip-config-section";
31
35
  readonly sectionTitle: "clip-config-section-title";
@@ -33,25 +37,78 @@ export declare const CSS_CLASSES: {
33
37
  readonly label: "clip-config-label";
34
38
  readonly slider: "clip-config-slider";
35
39
  readonly input: "clip-config-input";
40
+ readonly textarea: "clip-config-textarea";
41
+ readonly colorInput: "clip-config-color-input";
36
42
  readonly presetGrid: "clip-config-preset-grid";
37
43
  readonly presetButton: "clip-config-preset-button";
38
44
  readonly resetButton: "clip-config-reset-button";
39
45
  readonly emptyState: "clip-config-empty-state";
46
+ readonly filterGroup: "clip-config-filter-group";
47
+ readonly filterButton: "clip-config-filter-button";
48
+ readonly voiceCardGrid: "clip-config-voice-grid";
49
+ readonly voiceCard: "clip-config-voice-card";
50
+ readonly voiceCardAvatar: "clip-config-voice-avatar";
51
+ readonly voiceCardMeta: "clip-config-voice-meta";
52
+ readonly voiceCardTitle: "clip-config-voice-title";
53
+ readonly voiceCardSubtitle: "clip-config-voice-subtitle";
54
+ readonly voiceCardCheck: "clip-config-voice-check";
55
+ readonly voiceCardPreviewBadge: "clip-config-voice-preview-badge";
56
+ readonly voiceCheckbox: "clip-config-voice-checkbox";
57
+ readonly primaryButton: "clip-config-primary-button";
58
+ readonly errorText: "clip-config-error-text";
59
+ readonly voiceLoadingOverlay: "clip-config-voice-loading-overlay";
60
+ readonly voiceLoadingCard: "clip-config-voice-loading-card";
61
+ readonly voiceLoadingIcon: "clip-config-voice-loading-icon";
62
+ readonly voiceLoadingText: "clip-config-voice-loading-text";
40
63
  };
41
64
  export declare class ClipConfigPanel {
42
65
  private readonly container;
43
66
  private readonly theme;
44
67
  private readonly renderer;
45
68
  private readonly onClipUpdate?;
69
+ private readonly onGenerateVoice?;
70
+ private readonly voiceCatalog;
46
71
  private currentClip;
47
72
  private activeTab;
48
73
  private readonly iconCache;
74
+ private pendingPreferredTab;
75
+ private selectedVoiceId;
76
+ private activeVoiceFilterTag;
77
+ private textDraftContent;
78
+ private followTextUpdates;
79
+ private voiceLoadingBusyCount;
80
+ private isVoiceLoadingOverlayRendered;
81
+ private isVoiceLoadingOverlayExiting;
82
+ private voiceLoadingOverlayHideTimer;
83
+ private voiceErrorMessage;
84
+ private voicePreviewAudio;
85
+ private previewingVoiceId;
49
86
  constructor(config: ClipConfigPanelConfig);
50
87
  setClip(clip: Clip | null): void;
88
+ setPreferredTab(tab: 'voice' | null): void;
89
+ setVoiceGenerationBusy(isBusy: boolean): void;
51
90
  destroy(): void;
52
91
  private render;
92
+ private getAvailableTabs;
93
+ private resetVoiceState;
94
+ private syncVoiceStateForClip;
95
+ private syncActiveTab;
96
+ private supportsVoicePanel;
97
+ private resolveDefaultVoiceFilterTag;
98
+ private getVisibleVoiceOptions;
99
+ private ensureVoiceFilters;
100
+ private handleVoiceFilterChange;
101
+ private handleVoiceSelect;
102
+ private handleVoiceFollowTextUpdatesChange;
103
+ private handleGenerateVoice;
53
104
  private handleVolumeChange;
54
105
  private clampValue;
55
106
  private handleTransformChange;
56
107
  private handleTransformChanges;
108
+ private stopVoicePreview;
109
+ private readonly handleVoicePreviewEnded;
110
+ private playVoicePreview;
111
+ private handleTextContentInput;
112
+ private handleTextContentCommit;
113
+ private handleTextStyleChange;
57
114
  }
@@ -1,19 +1,37 @@
1
1
  import { type TemplateResult } from 'lit';
2
- import type { Clip } from '../../core/models';
2
+ import { type Clip, type VoiceOption } from '../../core/models';
3
3
  import { LitDomRenderer } from '../../core/renderers/domRenderer';
4
4
  import type { ClipConfigPanelTheme } from './ClipConfigPanel';
5
- export type ClipConfigPanelTabKey = 'visual' | 'audio';
5
+ export type ClipConfigPanelTabKey = 'visual' | 'audio' | 'text' | 'voice';
6
6
  export interface ClipConfigPanelCallbacks {
7
7
  onTabChange: (tab: ClipConfigPanelTabKey) => void;
8
8
  onTransformChange: (property: 'x' | 'y' | 'scale', value: number) => void;
9
9
  onPresetSelect: (x: number, y: number) => void;
10
10
  onVolumeChange: (value: number) => void;
11
+ onTextContentInput: (value: string) => void;
12
+ onTextContentCommit: () => void;
13
+ onTextStyleChange: (property: 'fontSize' | 'color' | 'rotation' | 'x' | 'y', value: number | string) => void;
14
+ onVoiceFilterChange: (tag: string) => void;
15
+ onVoiceSelect: (voiceId: string) => void;
16
+ onVoiceFollowTextUpdatesChange: (value: boolean) => void;
17
+ onGenerateVoice: () => Promise<void>;
11
18
  }
12
19
  export interface ClipConfigPanelViewModel {
13
20
  clip: Clip | null;
14
21
  activeTab: ClipConfigPanelTabKey;
15
22
  theme: ClipConfigPanelTheme;
16
23
  emptyStateIconSvg: string;
24
+ loadingIconSvg: string;
25
+ selectedIconSvg: string;
26
+ textDraftContent: string;
27
+ voiceCatalog: VoiceOption[];
28
+ selectedVoiceId: string | null;
29
+ previewingVoiceId: string | null;
30
+ activeVoiceFilterTag: string | null;
31
+ followTextUpdates: boolean;
32
+ isVoiceLoadingOverlayVisible: boolean;
33
+ isVoiceLoadingOverlayExiting: boolean;
34
+ voiceErrorMessage: string | null;
17
35
  callbacks: ClipConfigPanelCallbacks;
18
36
  }
19
37
  export declare const CLIP_CONFIG_CSS_CLASSES: {
@@ -23,6 +41,8 @@ export declare const CLIP_CONFIG_CSS_CLASSES: {
23
41
  readonly content: "clip-config-content";
24
42
  readonly visualPanel: "clip-config-visual-panel";
25
43
  readonly audioPanel: "clip-config-audio-panel";
44
+ readonly textPanel: "clip-config-text-panel";
45
+ readonly voicePanel: "clip-config-voice-panel";
26
46
  readonly actionBar: "clip-config-action-bar";
27
47
  readonly section: "clip-config-section";
28
48
  readonly sectionTitle: "clip-config-section-title";
@@ -30,28 +50,56 @@ export declare const CLIP_CONFIG_CSS_CLASSES: {
30
50
  readonly label: "clip-config-label";
31
51
  readonly slider: "clip-config-slider";
32
52
  readonly input: "clip-config-input";
53
+ readonly textarea: "clip-config-textarea";
54
+ readonly colorInput: "clip-config-color-input";
33
55
  readonly presetGrid: "clip-config-preset-grid";
34
56
  readonly presetButton: "clip-config-preset-button";
35
57
  readonly resetButton: "clip-config-reset-button";
36
58
  readonly emptyState: "clip-config-empty-state";
59
+ readonly filterGroup: "clip-config-filter-group";
60
+ readonly filterButton: "clip-config-filter-button";
61
+ readonly voiceCardGrid: "clip-config-voice-grid";
62
+ readonly voiceCard: "clip-config-voice-card";
63
+ readonly voiceCardAvatar: "clip-config-voice-avatar";
64
+ readonly voiceCardMeta: "clip-config-voice-meta";
65
+ readonly voiceCardTitle: "clip-config-voice-title";
66
+ readonly voiceCardSubtitle: "clip-config-voice-subtitle";
67
+ readonly voiceCardCheck: "clip-config-voice-check";
68
+ readonly voiceCardPreviewBadge: "clip-config-voice-preview-badge";
69
+ readonly voiceCheckbox: "clip-config-voice-checkbox";
70
+ readonly primaryButton: "clip-config-primary-button";
71
+ readonly errorText: "clip-config-error-text";
72
+ readonly voiceLoadingOverlay: "clip-config-voice-loading-overlay";
73
+ readonly voiceLoadingCard: "clip-config-voice-loading-card";
74
+ readonly voiceLoadingIcon: "clip-config-voice-loading-icon";
75
+ readonly voiceLoadingText: "clip-config-voice-loading-text";
37
76
  };
38
77
  export declare class ClipConfigPanelRenderer extends LitDomRenderer<ClipConfigPanelViewModel> {
39
78
  protected renderTemplate(viewModel: ClipConfigPanelViewModel): TemplateResult;
79
+ private renderVoiceLoadingOverlay;
40
80
  private renderEmptyState;
41
81
  private renderPanel;
42
82
  private renderTabBar;
83
+ private renderActionBar;
84
+ private renderVoiceActionBar;
43
85
  private renderVisualPanel;
44
86
  private renderPositionSection;
45
87
  private renderScaleSection;
46
88
  private renderAudioPanel;
89
+ private renderTextPanel;
90
+ private renderVoicePanel;
91
+ private renderFilterButton;
92
+ private renderVoiceCard;
47
93
  private renderPositionControl;
48
94
  private renderPresetGrid;
49
95
  private renderSectionTitle;
50
96
  private renderRangeInput;
51
97
  private renderNumberInput;
98
+ private renderLabeledNumberInput;
52
99
  private getContainerStyle;
53
100
  private getSliderStyle;
54
101
  private getAvailableTabs;
102
+ private supportsVoicePanel;
55
103
  private getTransform;
56
104
  private calculatePresetPosition;
57
105
  private resizeSvg;
@@ -17,8 +17,11 @@ export interface TrackInfoPanelViewModel {
17
17
  includeTimeScaleSpacer: boolean;
18
18
  onMuteTrack?: (trackId: string, isMuted: boolean) => void;
19
19
  icons: {
20
+ textTrack: string;
20
21
  videoTrack: string;
21
22
  audioTrack: string;
23
+ display: string;
24
+ hide: string;
22
25
  volume: string;
23
26
  volumeMuted: string;
24
27
  };
@@ -99,6 +99,9 @@ export declare class Track {
99
99
  private readonly handleEdgeAutoScrollFrame;
100
100
  private handleTrackBackgroundClick;
101
101
  private handleTrackBackgroundMouseDown;
102
+ private getClipDisplayLabel;
103
+ private getClipAudioBadgeText;
104
+ private isSourceBoundClip;
102
105
  updateClipSelection(clipId: string, isSelected: boolean): void;
103
106
  private createClipGroup;
104
107
  private handleClipClick;
@@ -130,6 +133,7 @@ export declare class Track {
130
133
  setCurrentTime(_time: TimeMs): void;
131
134
  setScrollLeft(scrollLeft: number): void;
132
135
  private updateClipVisibility;
136
+ private updateClipViewportState;
133
137
  setTrackY(trackY: number): void;
134
138
  setTrackStackOrder(order: number): void;
135
139
  setDragOverlayLayer(layer: Konva.Layer | null): void;
@@ -10,6 +10,7 @@ export { TimelinePlaybackResolver } from './timelinePlaybackResolver';
10
10
  export { TimelinePreviewSession } from './timelinePreviewSession';
11
11
  export { TimelinePreviewRuntimeController } from './timelinePreviewRuntimeController';
12
12
  export { TimelinePreviewStateController, type TimelinePendingPreviewState } from './timelinePreviewStateController';
13
+ export { TimelineKeyboardShortcutsController, type TimelineKeyboardShortcutsControllerCallbacks } from './timelineKeyboardShortcutsController';
13
14
  export * from './timelineSelectionController';
14
15
  export * from './timelineTrackMutationController';
15
16
  export { TimelineTrackInfoPanelController } from './timelineTrackInfoPanelController';
@@ -1,4 +1,4 @@
1
- import type { ActiveClipPlaybackInfo, Clip, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewMediaSource, PreviewSourceResolver, TimeMs } from '../models/types';
1
+ import type { ActiveClipPlaybackInfo, Clip, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewMediaSource, PreviewSourceResolver, TextPreviewFontConfig, TimeMs } from '../models/types';
2
2
  import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
3
3
  export interface TimelinePreviewSyncPayload {
4
4
  activeClips: ActiveClipPlaybackInfo[];
@@ -67,6 +67,7 @@ export interface PreviewBackendClockState {
67
67
  export interface TimelinePreviewBackendOptions {
68
68
  callbacks?: TimelinePreviewBackendCallbacks;
69
69
  previewSourceResolver?: PreviewSourceResolver;
70
+ textPreviewFont?: TextPreviewFontConfig | null;
70
71
  sourceMediaRegistry?: SourceMediaRegistry;
71
72
  rootClassName?: string;
72
73
  frameClassName?: string;
@@ -1,9 +1,11 @@
1
- import { Theme, Clip } from '../models';
1
+ import { Theme, Clip, type VoiceOption } from '../models';
2
2
  export interface TimelineClipConfigControllerConfig {
3
3
  container: HTMLElement;
4
4
  theme: Theme;
5
5
  getPrimarySelectedClip: () => Clip | null;
6
+ voiceCatalog: VoiceOption[];
6
7
  updateClip: (clipId: string, updates: Partial<Clip>) => void;
8
+ onGenerateVoice?: (clip: Clip, voice: VoiceOption, followTextUpdates: boolean) => Promise<void>;
7
9
  }
8
10
  export declare class TimelineClipConfigController {
9
11
  private panel;
@@ -13,5 +15,7 @@ export declare class TimelineClipConfigController {
13
15
  update(): void;
14
16
  updateFromExternal(): void;
15
17
  destroy(): void;
18
+ setPreferredTab(tab: 'voice' | null): void;
19
+ setVoiceGenerationBusy(isBusy: boolean): void;
16
20
  private convertTheme;
17
21
  }
@@ -0,0 +1,29 @@
1
+ import type { TimelineKeyboardShortcutsConfig } from '../models';
2
+ export interface TimelineKeyboardShortcutsControllerCallbacks {
3
+ togglePlay(): void;
4
+ deleteSelectedClips(): void;
5
+ toggleSelectedClipsAudio(): Promise<boolean>;
6
+ splitCurrentClip(): void;
7
+ undo(): boolean;
8
+ redo(): boolean;
9
+ canDeleteSelectedClips(): boolean;
10
+ canSplitSelectedClip(): boolean;
11
+ canToggleSelectedClipsAudio(): boolean;
12
+ }
13
+ export interface TimelineKeyboardShortcutsControllerOptions {
14
+ root: HTMLElement;
15
+ config?: false | TimelineKeyboardShortcutsConfig;
16
+ callbacks: TimelineKeyboardShortcutsControllerCallbacks;
17
+ isMacLike?: boolean;
18
+ }
19
+ export declare class TimelineKeyboardShortcutsController {
20
+ private readonly callbacks;
21
+ private readonly bindings;
22
+ private readonly enabled;
23
+ private readonly keydownListener;
24
+ constructor(options: TimelineKeyboardShortcutsControllerOptions);
25
+ init(): void;
26
+ destroy(): void;
27
+ private handleKeydown;
28
+ private dispatchAction;
29
+ }
@@ -1,4 +1,4 @@
1
- import type { ActiveClipPlaybackInfo, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewSourceResolver, TimeMs, TrackType } from '../models/types';
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
4
  interface TimelinePreviewSessionCallbacks {
@@ -18,6 +18,7 @@ interface TimelinePreviewSessionDependencies {
18
18
  frameClassName?: string;
19
19
  slotClassNamePrefix?: string;
20
20
  previewSourceResolver?: PreviewSourceResolver;
21
+ textPreviewFont?: TextPreviewFontConfig | null;
21
22
  sourceMediaRegistry?: SourceMediaRegistry;
22
23
  }
23
24
  export interface TimelinePreviewSyncPayload {
@@ -35,10 +36,12 @@ export declare class TimelinePreviewSession {
35
36
  private container;
36
37
  private rootElement;
37
38
  private frameElement;
39
+ private textOverlayRoot;
38
40
  private pendingOverlayElement;
39
41
  private readonly pendingOverlayRenderer;
40
42
  private resizeObserver;
41
43
  private readonly trackSlots;
44
+ private readonly textPreviewEntries;
42
45
  private audioContext;
43
46
  private masterGainNode;
44
47
  private loadingCount;
@@ -49,9 +52,18 @@ export declare class TimelinePreviewSession {
49
52
  private lastSettledSyncRequestId;
50
53
  private primarySelectedClipId;
51
54
  private transientVisualTransform;
55
+ private textPreviewDragState;
56
+ private textPreviewFontStyleElement;
57
+ private textPreviewFontSignature;
58
+ private textPreviewFontLoadState;
59
+ private textPreviewFontLoadToken;
52
60
  private readonly callbacks;
53
61
  private readonly dependencies;
54
62
  private readonly transformOverlay;
63
+ private readonly boundTextPreviewPointerMove;
64
+ private readonly boundTextPreviewPointerUp;
65
+ private readonly boundTextPreviewVisibilityChange;
66
+ private readonly boundTextPreviewWindowBlur;
55
67
  private requestedAspectRatio;
56
68
  private resolvedAutoAspectRatio;
57
69
  private aspectRatioProbe;
@@ -106,9 +118,28 @@ export declare class TimelinePreviewSession {
106
118
  private refreshPendingOverlay;
107
119
  private handlePreviewTransformChange;
108
120
  private refreshVisualLayout;
121
+ private syncTextPreviewEntries;
122
+ private clearTextPreviewEntries;
123
+ private removeTextPreviewEntry;
124
+ private applyTextPreviewEntry;
125
+ private refreshTextPreviewLayout;
126
+ private refreshTextPreviewEntries;
127
+ private createTextPreviewPointerDownHandler;
128
+ private bindTextPreviewDocumentListeners;
129
+ private unbindTextPreviewDocumentListeners;
130
+ private handleTextPreviewPointerMove;
131
+ private finishTextPreviewDrag;
132
+ private refreshTextPreviewInteractionState;
133
+ private getTextPreviewFontConfig;
134
+ private getTextPreviewFontFamily;
135
+ private ensureTextPreviewFontReady;
136
+ private resetTextPreviewFontState;
109
137
  private refreshSlotVisualLayout;
110
138
  private buildSelectedOverlayState;
111
139
  private getFrameSize;
140
+ private getDocument;
141
+ private toFrameLocalPoint;
142
+ private getTextPreviewBaselineMetrics;
112
143
  private getMediaSize;
113
144
  private getEffectiveVisualTransform;
114
145
  }
@@ -1,5 +1,5 @@
1
1
  import { type TimelinePreviewBackend } from '../controllers';
2
- import { TimelineConfig, Clip, ClipConfig, TimeMs, PlayState, Action, TimelineEvent, EventListener as TimelineEventListener, TimelineExportData, Track as TrackEntity, ThumbnailProvider, TrackInsertionPlacement, TrackType, ActiveClipPlaybackInfo, PreviewAspectRatio, PreviewMountConfig, PreviewBackendType, SelectedClipAudioAction } from '../models';
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
5
  export declare class TimelineManager {
@@ -40,6 +40,7 @@ export declare class TimelineManager {
40
40
  private lastTrackDuration;
41
41
  private lastReportedTrackDuration;
42
42
  private thumbnailProvider;
43
+ private thumbnailProviderVersion;
43
44
  private clipThumbnailLoadStates;
44
45
  private sourceMediaRegistry;
45
46
  private canPlay;
@@ -61,6 +62,7 @@ export declare class TimelineManager {
61
62
  private readonly bodyViewportScrollListener;
62
63
  private readonly bodyCanvasHostClickListener;
63
64
  private readonly rootWheelListener;
65
+ private keyboardShortcutsController;
64
66
  private mountManager;
65
67
  private pendingDraftData;
66
68
  private selectionStore;
@@ -172,6 +174,7 @@ export declare class TimelineManager {
172
174
  private renderBodyBackground;
173
175
  private handleBodyBackgroundClick;
174
176
  private handleBodyCanvasHostClick;
177
+ private getSnapGuideAccentColor;
175
178
  private ensureSnapGuideLine;
176
179
  private refreshSnapGuideLine;
177
180
  private updateSnapGuideLine;
@@ -312,6 +315,16 @@ export declare class TimelineManager {
312
315
  */
313
316
  private updateCanPlayState;
314
317
  private findClipById;
318
+ private buildGeneratedAudioClipName;
319
+ private normalizeGeneratedAudioDuration;
320
+ private getVoiceLinkedAudioClips;
321
+ private applyGeneratedAudioClipResult;
322
+ private syncTextClipDurationToAudio;
323
+ private doesClipOverlapOnTrack;
324
+ private findNearestAvailableTrackForClip;
325
+ private relocateGeneratedAudioClipIfNeeded;
326
+ private regenerateVoiceLinkedAudioClips;
327
+ private handleVoiceGenerateAction;
315
328
  private primeOrLoadClipThumbnails;
316
329
  private markClipThumbnailLoadCompleted;
317
330
  private buildClipThumbnailLoadSignature;
@@ -404,7 +417,7 @@ export declare class TimelineManager {
404
417
  getActiveClipsAtTime(time: TimeMs): ActiveClipPlaybackInfo[];
405
418
  attachPreview(containerOrConfig: HTMLElement | PreviewMountConfig): void;
406
419
  detachPreview(): void;
407
- attachClipConfig(container: HTMLElement): TimelineClipConfigController;
420
+ attachClipConfig(container: HTMLElement, options?: ClipConfigVoicePanelOptions): TimelineClipConfigController;
408
421
  getPreviewBackendType(): Exclude<PreviewBackendType, 'auto'>;
409
422
  hasAttachedPreview(): boolean;
410
423
  getPreviewAspectRatio(): PreviewAspectRatio;
@@ -439,6 +452,7 @@ export declare class TimelineManager {
439
452
  */
440
453
  getTrackTotalDuration(): TimeMs;
441
454
  destroy(): void;
455
+ private initKeyboardShortcuts;
442
456
  /** 清除历史堆栈 */
443
457
  clearHistory(): void;
444
458
  private handleClipChange;
@@ -21,6 +21,7 @@ export declare const TIME_SCALE: {
21
21
  export declare const TIMELINE_LEFT_PADDING = 15;
22
22
  export declare const MIN_CLIP_LINE_WIDTH = 6;
23
23
  export declare const TRACK_HEIGHT: {
24
+ TEXT: number;
24
25
  VIDEO: number;
25
26
  AUDIO: number;
26
27
  };
@@ -1,4 +1,4 @@
1
- export type TrackType = 'video' | 'audio';
1
+ export type TrackType = 'video' | 'audio' | 'text';
2
2
  export type PlayState = 'playing' | 'paused';
3
3
  export type TimeMs = number;
4
4
  export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
@@ -20,6 +20,17 @@ export interface ClipVisualTransform {
20
20
  y: number;
21
21
  scale: number;
22
22
  }
23
+ export interface TextClipStyle {
24
+ fontSize: number;
25
+ color: string;
26
+ rotation: number;
27
+ strokeColor: string;
28
+ strokeWidth: number;
29
+ lineHeight: number;
30
+ paddingX: number;
31
+ }
32
+ export declare const DEFAULT_TEXT_CLIP_STYLE: TextClipStyle;
33
+ export declare function resolveTextClipStyle(style?: Partial<TextClipStyle> | null): TextClipStyle;
23
34
  export type PreviewBackendType = 'dom' | 'canvas' | 'auto';
24
35
  export interface Mp4PreviewMediaSource {
25
36
  url: string;
@@ -33,15 +44,49 @@ export interface HlsFmp4PreviewMediaSource {
33
44
  }
34
45
  export type PreviewMediaSource = Mp4PreviewMediaSource | HlsFmp4PreviewMediaSource;
35
46
  export type PreviewSourceResolver = (clip: Clip) => PreviewMediaSource | null | Promise<PreviewMediaSource | null>;
47
+ export interface TextPreviewFontConfig {
48
+ fontName: string;
49
+ fontSourceUrl: string;
50
+ }
51
+ export interface VoiceOption {
52
+ id: string;
53
+ name: string;
54
+ lang: string;
55
+ emotion: string;
56
+ tags: string[];
57
+ avatar?: string;
58
+ audiofile?: string;
59
+ source?: Record<string, unknown>;
60
+ }
61
+ export interface GenerateVoiceRequest {
62
+ requestId: string;
63
+ sourceTextClipId: string;
64
+ targetAudioClipId?: string;
65
+ text: string;
66
+ voiceId: string;
67
+ followTextUpdates: boolean;
68
+ mode: 'create' | 'replace';
69
+ }
70
+ export interface GenerateVoiceResult {
71
+ requestId: string;
72
+ audioSrc: string;
73
+ duration: number;
74
+ voiceId: string;
75
+ voiceName: string;
76
+ }
77
+ export interface ClipConfigVoicePanelOptions {
78
+ voiceCatalog?: VoiceOption[];
79
+ generateVoiceBatch?: (requests: GenerateVoiceRequest[]) => Promise<GenerateVoiceResult[]>;
80
+ }
36
81
  export interface ClipConfig {
37
82
  id?: string;
38
83
  type?: TrackType;
39
84
  externalId?: string;
40
- src: string;
41
- name: string;
85
+ src?: string;
86
+ name?: string;
42
87
  isMuted?: boolean;
43
88
  startTime?: TimeMs;
44
- duration: TimeMs;
89
+ duration?: TimeMs;
45
90
  startTimeAtSource?: TimeMs;
46
91
  endTimeAtSource?: TimeMs;
47
92
  sourceDuration?: TimeMs;
@@ -52,6 +97,12 @@ export interface ClipConfig {
52
97
  separatedFromVideoClipId?: string;
53
98
  trackId?: string;
54
99
  volume?: number;
100
+ textContent?: string;
101
+ textStyle?: Partial<TextClipStyle>;
102
+ ttsSourceTextClipId?: string;
103
+ ttsVoiceId?: string;
104
+ ttsVoiceName?: string;
105
+ ttsFollowTextUpdates?: boolean;
55
106
  }
56
107
  export interface ClipEntity {
57
108
  id: string;
@@ -72,6 +123,12 @@ export interface ClipEntity {
72
123
  separatedAudioClipId?: string;
73
124
  separatedFromVideoClipId?: string;
74
125
  volume?: number;
126
+ textContent?: string;
127
+ textStyle?: TextClipStyle;
128
+ ttsSourceTextClipId?: string;
129
+ ttsVoiceId?: string;
130
+ ttsVoiceName?: string;
131
+ ttsFollowTextUpdates?: boolean;
75
132
  }
76
133
  export interface ClipViewState {
77
134
  isDragging: boolean;
@@ -114,15 +171,9 @@ export interface Theme {
114
171
  clipDuration: string;
115
172
  clipHandle: string;
116
173
  clipCoverBackground: string;
117
- audioClipBackground?: string;
118
174
  audioClipText?: string;
119
175
  audioClipAccent?: string;
120
- clipSelectedBackground: string;
121
176
  clipSelectedBorder: string;
122
- clipSelectedName: string;
123
- clipSelectedDuration: string;
124
- clipSelectedHandle: string;
125
- clipSelectedCoverBackground: string;
126
177
  playhead: string;
127
178
  grid: string;
128
179
  snapGuideLineColor?: string;
@@ -136,6 +187,12 @@ export type ThemeConfig = Partial<Theme>;
136
187
  export type ResolvedTimelineConfig = Omit<TimelineConfig, 'theme'> & {
137
188
  theme: Theme;
138
189
  };
190
+ export type TimelineKeyboardShortcutAction = 'togglePlay' | 'deleteSelectedClips' | 'toggleSelectedClipsAudio' | 'splitCurrentClip' | 'undo' | 'redo';
191
+ export type TimelineKeyboardShortcutBindings = Partial<Record<TimelineKeyboardShortcutAction, string | string[]>>;
192
+ export interface TimelineKeyboardShortcutsConfig {
193
+ enabled?: boolean;
194
+ bindings?: TimelineKeyboardShortcutBindings;
195
+ }
139
196
  export declare const defaultDarkTheme: Theme;
140
197
  export declare function resolveTheme(theme?: ThemeConfig): Theme;
141
198
  export interface TimelineConfig {
@@ -154,7 +211,9 @@ export interface TimelineConfig {
154
211
  thumbnailProvider?: ThumbnailProvider;
155
212
  previewBackend?: PreviewBackendType;
156
213
  previewSourceResolver?: PreviewSourceResolver;
214
+ textPreviewFont?: TextPreviewFontConfig | null;
157
215
  draftData?: TimelineExportData;
216
+ keyboardShortcuts?: false | TimelineKeyboardShortcutsConfig;
158
217
  }
159
218
  export type PreviewAspectRatioMode = 'auto-first-added-video' | 'auto-first-video' | 'manual';
160
219
  export interface PreviewAspectRatio {
@@ -167,11 +226,16 @@ export interface PreviewAspectRatioPreset {
167
226
  label: string;
168
227
  aspectRatio: PreviewAspectRatio;
169
228
  }
229
+ export declare const ASPECT_RATIO_BASELINE_SHORT_EDGE_PX = 720;
170
230
  export declare const DEFAULT_PREVIEW_ASPECT_RATIO: PreviewAspectRatio;
171
231
  export declare const PREVIEW_ASPECT_RATIO_PRESETS: PreviewAspectRatioPreset[];
172
232
  export declare function normalizePreviewAspectRatioMode(mode?: PreviewAspectRatioMode | null): PreviewAspectRatioMode;
173
233
  export declare function isAutoPreviewAspectRatioMode(mode?: PreviewAspectRatioMode | null): boolean;
174
234
  export declare function normalizePreviewAspectRatio(aspectRatio?: Partial<PreviewAspectRatio> | null): PreviewAspectRatio;
235
+ export declare function resolveAspectRatioBaselineDimensions(aspectRatio: Pick<PreviewAspectRatio, 'width' | 'height'>, shortEdge?: number): {
236
+ width: number;
237
+ height: number;
238
+ };
175
239
  export interface PreviewMountConfig {
176
240
  container: HTMLElement;
177
241
  aspectRatio?: {
@@ -432,4 +496,10 @@ export interface ClipExportData {
432
496
  separatedAudioClipId?: string;
433
497
  separatedFromVideoClipId?: string;
434
498
  volume?: number;
499
+ textContent?: string;
500
+ textStyle?: TextClipStyle;
501
+ ttsSourceTextClipId?: string;
502
+ ttsVoiceId?: string;
503
+ ttsVoiceName?: string;
504
+ ttsFollowTextUpdates?: boolean;
435
505
  }
@@ -18,6 +18,8 @@ declare class MockNode {
18
18
  cornerRadius(value?: number): any;
19
19
  text(value?: string): any;
20
20
  ellipsis(value?: boolean): any;
21
+ lineHeight(value?: number): any;
22
+ wrap(value?: string): any;
21
23
  points(value?: number[]): any;
22
24
  setAttr(key: string, value: any): void;
23
25
  getAttr(key: string): any;