@linker-design-plus/timeline-track 2.0.11 → 2.0.12
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 +21 -1
- package/dist/assets/AlibabaPuHuiTi-3-55-Regular.woff2 +0 -0
- package/dist/components/panel/ClipConfigPanel.d.ts +52 -1
- package/dist/components/panel/ClipConfigPanelRenderer.d.ts +48 -2
- package/dist/components/panel/TrackInfoPanelRenderer.d.ts +3 -0
- package/dist/components/track/Track.d.ts +4 -0
- package/dist/core/controllers/index.d.ts +1 -0
- package/dist/core/controllers/previewBackend.d.ts +2 -1
- package/dist/core/controllers/timelineClipConfigController.d.ts +5 -1
- package/dist/core/controllers/timelineKeyboardShortcutsController.d.ts +29 -0
- package/dist/core/controllers/timelinePreviewSession.d.ts +32 -1
- package/dist/core/facade/timelineManager.d.ts +16 -2
- package/dist/core/models/constants.d.ts +1 -0
- package/dist/core/models/types.d.ts +80 -10
- package/dist/core/testing/konva-test-stub.d.ts +2 -0
- package/dist/core/theme/colorTokens.d.ts +6 -9
- package/dist/index.cjs.js +264 -68
- package/dist/index.es.js +3912 -2575
- package/dist/utils/rendering/KonvaUtils.d.ts +2 -2
- package/dist/utils/rendering/clipCoverRenderer.d.ts +1 -0
- package/dist/utils/rendering/clipVisualRenderer.d.ts +2 -2
- package/dist/utils/svgIcon.d.ts +1 -0
- package/package.json +1 -1
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`
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
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,72 @@ 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 voiceCheckbox: "clip-config-voice-checkbox";
|
|
56
|
+
readonly primaryButton: "clip-config-primary-button";
|
|
57
|
+
readonly errorText: "clip-config-error-text";
|
|
58
|
+
readonly voiceLoadingOverlay: "clip-config-voice-loading-overlay";
|
|
59
|
+
readonly voiceLoadingCard: "clip-config-voice-loading-card";
|
|
60
|
+
readonly voiceLoadingIcon: "clip-config-voice-loading-icon";
|
|
61
|
+
readonly voiceLoadingText: "clip-config-voice-loading-text";
|
|
40
62
|
};
|
|
41
63
|
export declare class ClipConfigPanel {
|
|
42
64
|
private readonly container;
|
|
43
65
|
private readonly theme;
|
|
44
66
|
private readonly renderer;
|
|
45
67
|
private readonly onClipUpdate?;
|
|
68
|
+
private readonly onGenerateVoice?;
|
|
69
|
+
private readonly voiceCatalog;
|
|
46
70
|
private currentClip;
|
|
47
71
|
private activeTab;
|
|
48
72
|
private readonly iconCache;
|
|
73
|
+
private pendingPreferredTab;
|
|
74
|
+
private selectedVoiceId;
|
|
75
|
+
private activeVoiceFilterTag;
|
|
76
|
+
private textDraftContent;
|
|
77
|
+
private followTextUpdates;
|
|
78
|
+
private voiceLoadingBusyCount;
|
|
79
|
+
private isVoiceLoadingOverlayRendered;
|
|
80
|
+
private isVoiceLoadingOverlayExiting;
|
|
81
|
+
private voiceLoadingOverlayHideTimer;
|
|
82
|
+
private voiceErrorMessage;
|
|
49
83
|
constructor(config: ClipConfigPanelConfig);
|
|
50
84
|
setClip(clip: Clip | null): void;
|
|
85
|
+
setPreferredTab(tab: 'voice' | null): void;
|
|
86
|
+
setVoiceGenerationBusy(isBusy: boolean): void;
|
|
51
87
|
destroy(): void;
|
|
52
88
|
private render;
|
|
89
|
+
private getAvailableTabs;
|
|
90
|
+
private resetVoiceState;
|
|
91
|
+
private syncVoiceStateForClip;
|
|
92
|
+
private syncActiveTab;
|
|
93
|
+
private supportsVoicePanel;
|
|
94
|
+
private resolveDefaultVoiceFilterTag;
|
|
95
|
+
private getVisibleVoiceOptions;
|
|
96
|
+
private ensureVoiceFilters;
|
|
97
|
+
private handleVoiceFilterChange;
|
|
98
|
+
private handleVoiceSelect;
|
|
99
|
+
private handleVoiceFollowTextUpdatesChange;
|
|
100
|
+
private handleGenerateVoice;
|
|
53
101
|
private handleVolumeChange;
|
|
54
102
|
private clampValue;
|
|
55
103
|
private handleTransformChange;
|
|
56
104
|
private handleTransformChanges;
|
|
105
|
+
private handleTextContentInput;
|
|
106
|
+
private handleTextContentCommit;
|
|
107
|
+
private handleTextStyleChange;
|
|
57
108
|
}
|
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
import { type TemplateResult } from 'lit';
|
|
2
|
-
import type
|
|
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
|
+
activeVoiceFilterTag: string | null;
|
|
30
|
+
followTextUpdates: boolean;
|
|
31
|
+
isVoiceLoadingOverlayVisible: boolean;
|
|
32
|
+
isVoiceLoadingOverlayExiting: boolean;
|
|
33
|
+
voiceErrorMessage: string | null;
|
|
17
34
|
callbacks: ClipConfigPanelCallbacks;
|
|
18
35
|
}
|
|
19
36
|
export declare const CLIP_CONFIG_CSS_CLASSES: {
|
|
@@ -23,6 +40,8 @@ export declare const CLIP_CONFIG_CSS_CLASSES: {
|
|
|
23
40
|
readonly content: "clip-config-content";
|
|
24
41
|
readonly visualPanel: "clip-config-visual-panel";
|
|
25
42
|
readonly audioPanel: "clip-config-audio-panel";
|
|
43
|
+
readonly textPanel: "clip-config-text-panel";
|
|
44
|
+
readonly voicePanel: "clip-config-voice-panel";
|
|
26
45
|
readonly actionBar: "clip-config-action-bar";
|
|
27
46
|
readonly section: "clip-config-section";
|
|
28
47
|
readonly sectionTitle: "clip-config-section-title";
|
|
@@ -30,28 +49,55 @@ export declare const CLIP_CONFIG_CSS_CLASSES: {
|
|
|
30
49
|
readonly label: "clip-config-label";
|
|
31
50
|
readonly slider: "clip-config-slider";
|
|
32
51
|
readonly input: "clip-config-input";
|
|
52
|
+
readonly textarea: "clip-config-textarea";
|
|
53
|
+
readonly colorInput: "clip-config-color-input";
|
|
33
54
|
readonly presetGrid: "clip-config-preset-grid";
|
|
34
55
|
readonly presetButton: "clip-config-preset-button";
|
|
35
56
|
readonly resetButton: "clip-config-reset-button";
|
|
36
57
|
readonly emptyState: "clip-config-empty-state";
|
|
58
|
+
readonly filterGroup: "clip-config-filter-group";
|
|
59
|
+
readonly filterButton: "clip-config-filter-button";
|
|
60
|
+
readonly voiceCardGrid: "clip-config-voice-grid";
|
|
61
|
+
readonly voiceCard: "clip-config-voice-card";
|
|
62
|
+
readonly voiceCardAvatar: "clip-config-voice-avatar";
|
|
63
|
+
readonly voiceCardMeta: "clip-config-voice-meta";
|
|
64
|
+
readonly voiceCardTitle: "clip-config-voice-title";
|
|
65
|
+
readonly voiceCardSubtitle: "clip-config-voice-subtitle";
|
|
66
|
+
readonly voiceCardCheck: "clip-config-voice-check";
|
|
67
|
+
readonly voiceCheckbox: "clip-config-voice-checkbox";
|
|
68
|
+
readonly primaryButton: "clip-config-primary-button";
|
|
69
|
+
readonly errorText: "clip-config-error-text";
|
|
70
|
+
readonly voiceLoadingOverlay: "clip-config-voice-loading-overlay";
|
|
71
|
+
readonly voiceLoadingCard: "clip-config-voice-loading-card";
|
|
72
|
+
readonly voiceLoadingIcon: "clip-config-voice-loading-icon";
|
|
73
|
+
readonly voiceLoadingText: "clip-config-voice-loading-text";
|
|
37
74
|
};
|
|
38
75
|
export declare class ClipConfigPanelRenderer extends LitDomRenderer<ClipConfigPanelViewModel> {
|
|
39
76
|
protected renderTemplate(viewModel: ClipConfigPanelViewModel): TemplateResult;
|
|
77
|
+
private renderVoiceLoadingOverlay;
|
|
40
78
|
private renderEmptyState;
|
|
41
79
|
private renderPanel;
|
|
42
80
|
private renderTabBar;
|
|
81
|
+
private renderActionBar;
|
|
82
|
+
private renderVoiceActionBar;
|
|
43
83
|
private renderVisualPanel;
|
|
44
84
|
private renderPositionSection;
|
|
45
85
|
private renderScaleSection;
|
|
46
86
|
private renderAudioPanel;
|
|
87
|
+
private renderTextPanel;
|
|
88
|
+
private renderVoicePanel;
|
|
89
|
+
private renderFilterButton;
|
|
90
|
+
private renderVoiceCard;
|
|
47
91
|
private renderPositionControl;
|
|
48
92
|
private renderPresetGrid;
|
|
49
93
|
private renderSectionTitle;
|
|
50
94
|
private renderRangeInput;
|
|
51
95
|
private renderNumberInput;
|
|
96
|
+
private renderLabeledNumberInput;
|
|
52
97
|
private getContainerStyle;
|
|
53
98
|
private getSliderStyle;
|
|
54
99
|
private getAvailableTabs;
|
|
100
|
+
private supportsVoicePanel;
|
|
55
101
|
private getTransform;
|
|
56
102
|
private calculatePresetPosition;
|
|
57
103
|
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;
|
|
@@ -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
|
|
41
|
-
name
|
|
85
|
+
src?: string;
|
|
86
|
+
name?: string;
|
|
42
87
|
isMuted?: boolean;
|
|
43
88
|
startTime?: TimeMs;
|
|
44
|
-
duration
|
|
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;
|