@linker-design-plus/timeline-track 2.0.1 → 2.0.3
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 +1 -1
- package/dist/components/panel/ClipConfigPanel.d.ts +21 -3
- package/dist/components/timeline/ManagedPlayhead.d.ts +28 -3
- package/dist/components/timeline/TimelineHeaderView.d.ts +1 -1
- package/dist/components/track/Track.d.ts +25 -1
- package/dist/components/track/trackInteractionState.d.ts +37 -1
- package/dist/core/controllers/timelineTrackMutationController.d.ts +1 -0
- package/dist/core/facade/timelineManager.d.ts +71 -0
- package/dist/core/history/history.d.ts +3 -1
- package/dist/core/history/timelineHistoryExecutor.d.ts +7 -2
- package/dist/core/history/timelineHistoryRecorder.d.ts +6 -2
- package/dist/core/models/types.d.ts +27 -3
- package/dist/core/presentation/timelinePresentationAdapter.d.ts +1 -1
- package/dist/core/stores/selectionStore.d.ts +8 -1
- package/dist/core/tracks/index.d.ts +1 -0
- package/dist/core/tracks/timelineTrackBridge.d.ts +14 -1
- package/dist/core/tracks/timelineTrackCollection.d.ts +8 -0
- package/dist/index.cjs.js +4 -4
- package/dist/index.es.js +5277 -4007
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,7 +120,7 @@ await timeline.refreshAllClipThumbnails();
|
|
|
120
120
|
- `setZoom(zoom)` / `getZoom()`
|
|
121
121
|
- `setSpeed(speed)` / `getSpeed()`
|
|
122
122
|
- `addClip(config)` / `addClips(configs)`
|
|
123
|
-
- `updateClip(clipId, updates)` / `removeClip(clipId)`
|
|
123
|
+
- `updateClip(clipId, updates)` / `removeClip(clipId)` / `removeClipsByExternalId(externalId)`
|
|
124
124
|
- `selectClip(clipId)` / `clearSelection()` / `getSelectedClip()`
|
|
125
125
|
- `canSeparateClipAudio(clipId)` / `separateClipAudio(clipId)` / `canRestoreClipAudio(clipId)` / `restoreClipAudio(clipId)`
|
|
126
126
|
- `splitCurrentClip()` / `removeClipGaps()` / `fitZoom()`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Clip } from '../../core/models';
|
|
1
|
+
import type { Clip } from '../../core/models';
|
|
2
2
|
export interface ClipConfigPanelTheme {
|
|
3
3
|
backgroundColor: string;
|
|
4
4
|
borderColor: string;
|
|
@@ -25,26 +25,44 @@ export declare class ClipConfigPanel {
|
|
|
25
25
|
private theme;
|
|
26
26
|
private onClipUpdate?;
|
|
27
27
|
private currentClip;
|
|
28
|
+
private activeTab;
|
|
29
|
+
private tabBar;
|
|
30
|
+
private contentArea;
|
|
31
|
+
private visualPanel;
|
|
32
|
+
private audioPanel;
|
|
28
33
|
private xSlider;
|
|
29
34
|
private xInput;
|
|
30
35
|
private ySlider;
|
|
31
36
|
private yInput;
|
|
32
37
|
private scaleSlider;
|
|
33
38
|
private scaleInput;
|
|
39
|
+
private volumeSlider;
|
|
40
|
+
private volumeInput;
|
|
34
41
|
constructor(config: ClipConfigPanelConfig);
|
|
35
42
|
setClip(clip: Clip | null): void;
|
|
36
43
|
destroy(): void;
|
|
44
|
+
private getAvailableTabs;
|
|
37
45
|
private render;
|
|
46
|
+
private applyBaseTheme;
|
|
38
47
|
private renderEmptyState;
|
|
39
|
-
private
|
|
48
|
+
private renderPanel;
|
|
49
|
+
private renderTabBar;
|
|
50
|
+
private updateTabBarState;
|
|
51
|
+
private renderContentArea;
|
|
52
|
+
private updateContentVisibility;
|
|
53
|
+
private renderVisualPanel;
|
|
54
|
+
private renderAudioPanel;
|
|
55
|
+
private renderActionBar;
|
|
40
56
|
private renderPositionControl;
|
|
41
57
|
private renderPresetGrid;
|
|
42
58
|
private calculatePresetPosition;
|
|
43
59
|
private renderScaleControl;
|
|
60
|
+
private renderVolumeControl;
|
|
61
|
+
private handleVolumeChange;
|
|
62
|
+
private updateVolumeControls;
|
|
44
63
|
private clampValue;
|
|
45
64
|
private updateUIControls;
|
|
46
65
|
private updateSliderAndInput;
|
|
47
66
|
private handleTransformChange;
|
|
48
67
|
private handleTransformChanges;
|
|
49
|
-
private applyTheme;
|
|
50
68
|
}
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
import type { TimeMs, Theme } from '../../core/models/types';
|
|
2
2
|
export declare class ManagedPlayhead {
|
|
3
|
+
private static readonly SVG_NS;
|
|
4
|
+
private static readonly VISUAL_WIDTH;
|
|
5
|
+
private static readonly HANDLE_HEIGHT;
|
|
6
|
+
private static readonly HANDLE_LINE_START;
|
|
7
|
+
private static readonly LINE_WIDTH;
|
|
8
|
+
private static readonly LINE_HIT_WIDTH;
|
|
9
|
+
private static readonly END_CAP_WIDTH;
|
|
10
|
+
private static readonly END_CAP_HEIGHT;
|
|
11
|
+
private static readonly AUTO_SCROLL_EDGE_THRESHOLD;
|
|
12
|
+
private static readonly AUTO_SCROLL_MAX_SPEED;
|
|
3
13
|
private readonly container;
|
|
4
|
-
private readonly
|
|
5
|
-
private readonly
|
|
14
|
+
private readonly visualElement;
|
|
15
|
+
private readonly lineHitAreaElement;
|
|
6
16
|
private readonly theme;
|
|
7
17
|
private readonly onTimeChange;
|
|
18
|
+
private readonly onHorizontalAutoScroll?;
|
|
8
19
|
private currentTime;
|
|
9
20
|
private zoom;
|
|
10
21
|
private scrollLeft;
|
|
11
22
|
private height;
|
|
12
23
|
private isDragging;
|
|
13
24
|
private hasBoundGlobalPointerListeners;
|
|
25
|
+
private edgeAutoScrollAnimationFrameId;
|
|
26
|
+
private lastPointerClientX;
|
|
14
27
|
private handleGlobalPointerMove;
|
|
15
28
|
private handleGlobalPointerEnd;
|
|
16
29
|
private handleVisibilityChange;
|
|
17
|
-
constructor(container: HTMLElement, initialTime: TimeMs, zoom: number, height: number, theme: Theme, onTimeChange: (time: TimeMs) => void);
|
|
30
|
+
constructor(container: HTMLElement, initialTime: TimeMs, zoom: number, height: number, theme: Theme, onTimeChange: (time: TimeMs, mode?: 'seek' | 'scrub') => void, onHorizontalAutoScroll?: (nextScrollLeft: number) => number);
|
|
18
31
|
setCurrentTime(time: TimeMs): void;
|
|
19
32
|
setTime(time: TimeMs): void;
|
|
20
33
|
setZoom(zoom: number): void;
|
|
@@ -22,8 +35,20 @@ export declare class ManagedPlayhead {
|
|
|
22
35
|
setHeight(height: number): void;
|
|
23
36
|
destroy(): void;
|
|
24
37
|
private handleMouseDown;
|
|
38
|
+
private handleHandleClick;
|
|
25
39
|
private bindGlobalPointerListeners;
|
|
26
40
|
private unbindGlobalPointerListeners;
|
|
27
41
|
private updateTimeFromClientX;
|
|
42
|
+
private updateEdgeAutoScrollState;
|
|
43
|
+
private calculateHorizontalAutoScrollVelocity;
|
|
44
|
+
private startEdgeAutoScroll;
|
|
45
|
+
private stopEdgeAutoScroll;
|
|
46
|
+
private readonly handleEdgeAutoScrollFrame;
|
|
28
47
|
private render;
|
|
48
|
+
private renderVisual;
|
|
49
|
+
private createHandleShadowPath;
|
|
50
|
+
private createHandleBadgePath;
|
|
51
|
+
private createGripLines;
|
|
52
|
+
private createMainLine;
|
|
53
|
+
private createEndCap;
|
|
29
54
|
}
|
|
@@ -17,7 +17,7 @@ export declare class TimelineHeaderView {
|
|
|
17
17
|
private handleGlobalPointerMove;
|
|
18
18
|
private handleGlobalPointerEnd;
|
|
19
19
|
private handleVisibilityChange;
|
|
20
|
-
constructor(stage: Konva.Stage, layer: Konva.Layer, config: Partial<TimelineConfig>, onTimeChange: (time: TimeMs) => void, onScrollChange: (scrollLeft: number) => void);
|
|
20
|
+
constructor(stage: Konva.Stage, layer: Konva.Layer, config: Partial<TimelineConfig>, onTimeChange: (time: TimeMs, mode?: 'seek' | 'scrub') => void, onScrollChange: (scrollLeft: number) => void);
|
|
21
21
|
setCurrentTime(_time: TimeMs): void;
|
|
22
22
|
setZoom(zoom: number): void;
|
|
23
23
|
setDuration(duration: TimeMs): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Konva from 'konva';
|
|
2
2
|
import { Clip as ClipType, ClipStateUpdate, TrackConfig, TimeMs, Theme, TrackType } from '../../core/models';
|
|
3
|
+
import type { MultiDragMoveRequest } from '../../core/tracks/timelineTrackBridge';
|
|
3
4
|
export declare class Track {
|
|
4
5
|
private static readonly DEFAULT_DRAG_ACTIVATION_THRESHOLD;
|
|
5
6
|
private static readonly DEFAULT_CLIP_SNAP_THRESHOLD;
|
|
@@ -7,7 +8,9 @@ export declare class Track {
|
|
|
7
8
|
private static readonly AUTO_SCROLL_MAX_SPEED;
|
|
8
9
|
private layer;
|
|
9
10
|
private trackGroup;
|
|
11
|
+
private dragOverlayLayer;
|
|
10
12
|
private dropPreviewGroup;
|
|
13
|
+
private dropPreviewClips;
|
|
11
14
|
private config;
|
|
12
15
|
private theme;
|
|
13
16
|
private trackType;
|
|
@@ -19,6 +22,9 @@ export declare class Track {
|
|
|
19
22
|
private trackHeight;
|
|
20
23
|
private selectedClip;
|
|
21
24
|
private hasSelectedClip;
|
|
25
|
+
private selectedClipIds;
|
|
26
|
+
private multiDragOriginalPositions;
|
|
27
|
+
private promotedClipParents;
|
|
22
28
|
private interactionState;
|
|
23
29
|
private isVisualUpdate;
|
|
24
30
|
private onClipUpdate;
|
|
@@ -37,6 +43,11 @@ export declare class Track {
|
|
|
37
43
|
private onClearDropPreview?;
|
|
38
44
|
private onClearSelection?;
|
|
39
45
|
private onSnapGuideChange?;
|
|
46
|
+
private onClipToggleSelection?;
|
|
47
|
+
private onSetSingleSelection?;
|
|
48
|
+
private getMultiDragClipIds?;
|
|
49
|
+
private onMultiDragMove?;
|
|
50
|
+
private onMultiDragInteractionEnd?;
|
|
40
51
|
private resolveSnapTargetClips?;
|
|
41
52
|
private readonly dragActivationThreshold;
|
|
42
53
|
private enableClipSnap;
|
|
@@ -44,9 +55,10 @@ export declare class Track {
|
|
|
44
55
|
private handleGlobalPointerMove;
|
|
45
56
|
private handleGlobalPointerEnd;
|
|
46
57
|
private handleWindowBlur;
|
|
47
|
-
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) => 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, dragActivationThreshold?: number, enableClipSnap?: boolean, clipSnapThreshold?: number);
|
|
58
|
+
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) => 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);
|
|
48
59
|
private initClips;
|
|
49
60
|
private ensureDropPreviewGroup;
|
|
61
|
+
private createDropPreviewRect;
|
|
50
62
|
private showDropPreview;
|
|
51
63
|
private hideDropPreview;
|
|
52
64
|
private initEventListeners;
|
|
@@ -55,6 +67,8 @@ export declare class Track {
|
|
|
55
67
|
private handleVisibilityChange;
|
|
56
68
|
private readonly handleEdgeAutoScrollFrame;
|
|
57
69
|
private handleTrackBackgroundClick;
|
|
70
|
+
private handleTrackBackgroundMouseDown;
|
|
71
|
+
updateClipSelection(clipId: string, isSelected: boolean): void;
|
|
58
72
|
private createClipGroup;
|
|
59
73
|
private handleClipClick;
|
|
60
74
|
private handleClipMouseDown;
|
|
@@ -85,6 +99,10 @@ export declare class Track {
|
|
|
85
99
|
setScrollLeft(scrollLeft: number): void;
|
|
86
100
|
private updateClipVisibility;
|
|
87
101
|
setTrackY(trackY: number): void;
|
|
102
|
+
setTrackStackOrder(order: number): void;
|
|
103
|
+
setDragOverlayLayer(layer: Konva.Layer | null): void;
|
|
104
|
+
setClipsDragOverlayActive(clipIds: string[], isActive: boolean): void;
|
|
105
|
+
clearClipDragOverlay(): void;
|
|
88
106
|
getTrackType(): TrackType;
|
|
89
107
|
render(shouldBatchDraw?: boolean): void;
|
|
90
108
|
private getTrackBackgroundFill;
|
|
@@ -98,8 +116,11 @@ export declare class Track {
|
|
|
98
116
|
removeClipGaps(): void;
|
|
99
117
|
getTrackGroup(): Konva.Group;
|
|
100
118
|
getId(): string;
|
|
119
|
+
updateClipPosition(clipId: string, newStartTime: TimeMs, isFinal: boolean, previewOffsetY?: number): void;
|
|
120
|
+
updateClipPositionFinal(clipId: string, newStartTime: TimeMs): void;
|
|
101
121
|
setTrackHeight(height: number): void;
|
|
102
122
|
showClipDropPreview(clip: ClipType): void;
|
|
123
|
+
showClipDropPreviews(clips: ClipType[]): void;
|
|
103
124
|
clearClipDropPreview(): void;
|
|
104
125
|
getRole(): 'primary' | 'normal';
|
|
105
126
|
setClipSnapEnabled(enabled: boolean): void;
|
|
@@ -113,6 +134,9 @@ export declare class Track {
|
|
|
113
134
|
private buildResizeRightPreviewClip;
|
|
114
135
|
private applyClipSnap;
|
|
115
136
|
private applyPreviewClipState;
|
|
137
|
+
private setClipDragOverlayActive;
|
|
138
|
+
private resolveClipRenderY;
|
|
139
|
+
private getPromotedClipParents;
|
|
116
140
|
private updateSnapGuideLine;
|
|
117
141
|
private updateEdgeAutoScrollState;
|
|
118
142
|
private calculateHorizontalAutoScrollVelocity;
|
|
@@ -35,7 +35,21 @@ export interface DraggingResizeRightTrackInteractionState {
|
|
|
35
35
|
kind: 'draggingResizeRight';
|
|
36
36
|
context: TrackInteractionContext;
|
|
37
37
|
}
|
|
38
|
-
export
|
|
38
|
+
export interface BoxSelectingTrackInteractionState {
|
|
39
|
+
kind: 'boxSelecting';
|
|
40
|
+
context: {
|
|
41
|
+
startClientX: number;
|
|
42
|
+
startClientY: number;
|
|
43
|
+
startLocalX: number;
|
|
44
|
+
startLocalY: number;
|
|
45
|
+
currentClientX: number;
|
|
46
|
+
currentClientY: number;
|
|
47
|
+
currentLocalX: number;
|
|
48
|
+
currentLocalY: number;
|
|
49
|
+
additive: boolean;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export type TrackInteractionState = IdleTrackInteractionState | PressedTrackInteractionState | DraggingMoveTrackInteractionState | DraggingResizeLeftTrackInteractionState | DraggingResizeRightTrackInteractionState | BoxSelectingTrackInteractionState;
|
|
39
53
|
export type TrackInteractionEvent = {
|
|
40
54
|
type: 'POINTER_DOWN';
|
|
41
55
|
operation: TrackPointerOperation;
|
|
@@ -58,6 +72,21 @@ export type TrackInteractionEvent = {
|
|
|
58
72
|
type: 'SCROLL_LEFT_CHANGED';
|
|
59
73
|
} | {
|
|
60
74
|
type: 'POINTER_UP' | 'VISIBILITY_HIDDEN' | 'WINDOW_BLUR' | 'CANCEL';
|
|
75
|
+
} | {
|
|
76
|
+
type: 'BOX_SELECT_START';
|
|
77
|
+
clientX: number;
|
|
78
|
+
clientY: number;
|
|
79
|
+
localX: number;
|
|
80
|
+
localY: number;
|
|
81
|
+
additive: boolean;
|
|
82
|
+
} | {
|
|
83
|
+
type: 'BOX_SELECT_MOVE';
|
|
84
|
+
clientX: number;
|
|
85
|
+
clientY: number;
|
|
86
|
+
localX: number;
|
|
87
|
+
localY: number;
|
|
88
|
+
} | {
|
|
89
|
+
type: 'BOX_SELECT_END';
|
|
61
90
|
};
|
|
62
91
|
export interface TrackInteractionTransitionEffects {
|
|
63
92
|
bindGlobalListeners?: boolean;
|
|
@@ -77,3 +106,10 @@ export declare function getTrackInteractionContext(state: TrackInteractionState
|
|
|
77
106
|
export declare function getTrackInteractionOperation(state: TrackInteractionState | null | undefined): TrackPointerOperation | null;
|
|
78
107
|
export declare function getTrackInteractionCursor(state: TrackInteractionState | null | undefined): 'ew-resize' | 'grabbing' | null;
|
|
79
108
|
export declare function transitionTrackInteraction(state: TrackInteractionState, event: TrackInteractionEvent): TrackInteractionTransitionResult;
|
|
109
|
+
export declare function isBoxSelecting(state: TrackInteractionState | null | undefined): boolean;
|
|
110
|
+
export declare function getBoxSelectRect(state: TrackInteractionState | null | undefined): {
|
|
111
|
+
x1: number;
|
|
112
|
+
y1: number;
|
|
113
|
+
x2: number;
|
|
114
|
+
y2: number;
|
|
115
|
+
} | null;
|
|
@@ -29,6 +29,7 @@ interface ClipMutationSideEffectHandlers {
|
|
|
29
29
|
updateCanPlayState?: () => void;
|
|
30
30
|
updateTrackInfoPanel?: () => void;
|
|
31
31
|
handleClipChange?: () => void;
|
|
32
|
+
cleanupEmptyTracks?: () => void;
|
|
32
33
|
}
|
|
33
34
|
interface ClipMoveSideEffectHandlers {
|
|
34
35
|
notifySelectionChange: () => void;
|
|
@@ -18,7 +18,13 @@ export declare class TimelineManager {
|
|
|
18
18
|
private headerLayer;
|
|
19
19
|
private backgroundLayer;
|
|
20
20
|
private trackLayer;
|
|
21
|
+
private dragOverlayLayer;
|
|
21
22
|
private snapGuideLayer;
|
|
23
|
+
private selectionBoxLayer;
|
|
24
|
+
private selectionBoxRect;
|
|
25
|
+
private isBoxSelecting;
|
|
26
|
+
private boxSelectStartX;
|
|
27
|
+
private boxSelectStartY;
|
|
22
28
|
private resizeObserver;
|
|
23
29
|
private rootContainer;
|
|
24
30
|
private layout;
|
|
@@ -57,6 +63,9 @@ export declare class TimelineManager {
|
|
|
57
63
|
private readonly rootWheelListener;
|
|
58
64
|
private mountManager;
|
|
59
65
|
private pendingDraftData;
|
|
66
|
+
private selectionStore;
|
|
67
|
+
private multiDragSession;
|
|
68
|
+
private clipRemovalBatchDepth;
|
|
60
69
|
constructor(config?: Partial<TimelineConfig>);
|
|
61
70
|
private getTimelineStore;
|
|
62
71
|
private getTimelineCommands;
|
|
@@ -136,6 +145,7 @@ export declare class TimelineManager {
|
|
|
136
145
|
private cleanupEmptyTracks;
|
|
137
146
|
private clearAllTrackDropPreviews;
|
|
138
147
|
private showClipDropPreview;
|
|
148
|
+
private showClipDropPreviews;
|
|
139
149
|
private ensureTrackInsertionPreviewLine;
|
|
140
150
|
private refreshTrackInsertionPreview;
|
|
141
151
|
private showTrackInsertionPreview;
|
|
@@ -237,6 +247,7 @@ export declare class TimelineManager {
|
|
|
237
247
|
addClips(clipConfigs: ClipConfig[]): Promise<string[]>;
|
|
238
248
|
private addClipInternal;
|
|
239
249
|
removeClip(clipId: string): void;
|
|
250
|
+
removeClipsByExternalId(externalId: string): boolean;
|
|
240
251
|
updateClip(clipId: string, updates: Partial<Clip>): void;
|
|
241
252
|
splitClip(clipId: string, time: TimeMs): void;
|
|
242
253
|
splitCurrentClip(): void;
|
|
@@ -312,6 +323,16 @@ export declare class TimelineManager {
|
|
|
312
323
|
* 清空所有轨道的选中状态
|
|
313
324
|
*/
|
|
314
325
|
clearSelection(): void;
|
|
326
|
+
getSelectedClipIds(): string[];
|
|
327
|
+
addToSelection(clipId: string): void;
|
|
328
|
+
removeFromSelection(clipId: string): void;
|
|
329
|
+
toggleSelection(clipId: string): void;
|
|
330
|
+
isClipSelected(clipId: string): boolean;
|
|
331
|
+
deleteSelectedClips(): void;
|
|
332
|
+
separateSelectedClipsAudio(): void;
|
|
333
|
+
selectAllClips(): void;
|
|
334
|
+
private emitSelectionChangeEvent;
|
|
335
|
+
private updateAllTracksSelectionVisual;
|
|
315
336
|
/**
|
|
316
337
|
* 滚动到指定片段
|
|
317
338
|
* @param clipId 目标片段的 ID
|
|
@@ -330,6 +351,11 @@ export declare class TimelineManager {
|
|
|
330
351
|
* @returns 当前选中的 clip,如果没有则返回 null
|
|
331
352
|
*/
|
|
332
353
|
getSelectedClip(): Clip | null;
|
|
354
|
+
getSelectedClips(): Clip[];
|
|
355
|
+
canDeleteSelectedClips(): boolean;
|
|
356
|
+
getSelectedClipAudioAction(): 'separate' | 'restore' | null;
|
|
357
|
+
canToggleSelectedClipsAudio(): boolean;
|
|
358
|
+
toggleSelectedClipsAudio(): Promise<boolean>;
|
|
333
359
|
getCurrentActiveClips(): ActiveClipPlaybackInfo[];
|
|
334
360
|
getCurrentPlaybackPlan(): ResolvedPlaybackPlan;
|
|
335
361
|
getPlaybackPlanAtTime(time: TimeMs): ResolvedPlaybackPlan;
|
|
@@ -349,10 +375,12 @@ export declare class TimelineManager {
|
|
|
349
375
|
* @returns 是否成功删除了选中的 clip
|
|
350
376
|
*/
|
|
351
377
|
removeSelectedClip(): boolean;
|
|
378
|
+
removeSelectedClips(): boolean;
|
|
352
379
|
canSeparateClipAudio(clipId: string): boolean;
|
|
353
380
|
canRestoreClipAudio(clipId: string): boolean;
|
|
354
381
|
separateClipAudio(clipId: string): Promise<string | null>;
|
|
355
382
|
restoreClipAudio(clipId: string): boolean;
|
|
383
|
+
private applySelectedClipAudioAction;
|
|
356
384
|
/**
|
|
357
385
|
* 将片段从一个轨道移动到另一个轨道
|
|
358
386
|
* @param clipId 要移动的片段 ID
|
|
@@ -370,9 +398,52 @@ export declare class TimelineManager {
|
|
|
370
398
|
/** 清除历史堆栈 */
|
|
371
399
|
clearHistory(): void;
|
|
372
400
|
private handleClipChange;
|
|
401
|
+
private withClipRemovalBatch;
|
|
402
|
+
private resolveClipIdsForExternalIdRemoval;
|
|
403
|
+
private shouldClearSelectionForClipIds;
|
|
404
|
+
private resolveSelectedClipAtTime;
|
|
405
|
+
private handleClipToggleSelection;
|
|
406
|
+
private handleSetSingleSelection;
|
|
407
|
+
private getMultiDragClipIds;
|
|
408
|
+
private handleMultiDragMove;
|
|
409
|
+
private normalizeTrackGroupStackOrder;
|
|
410
|
+
private elevateMultiDragTrackGroups;
|
|
411
|
+
private resolveMultiDragSessionTrackIds;
|
|
412
|
+
private resetMultiDragSession;
|
|
413
|
+
private handleMultiDragInteractionEnd;
|
|
414
|
+
private syncMultiDragOverlayState;
|
|
415
|
+
private clearMultiDragOverlayState;
|
|
416
|
+
private ensureMultiDragSession;
|
|
417
|
+
private haveSameClipIds;
|
|
418
|
+
private restoreDraggedClipSnapshot;
|
|
419
|
+
private syncMultiDragPeerClips;
|
|
420
|
+
private previewMultiDragCrossTrack;
|
|
421
|
+
private finalizeMultiDragCrossTrack;
|
|
422
|
+
private resolveTrackIndexDelta;
|
|
423
|
+
private resolveMultiDragTargetTrackId;
|
|
424
|
+
private resolveMultiDragPreviewTargetTrackId;
|
|
425
|
+
private buildMovedClipSnapshot;
|
|
426
|
+
private appendPreviewClip;
|
|
427
|
+
private resolveCrossTrackMoveDestination;
|
|
428
|
+
private resolveCrossTrackPreviewDestination;
|
|
429
|
+
private ensureTrackIdByRelativeIndex;
|
|
430
|
+
private findTrackIdByRelativeIndex;
|
|
431
|
+
private getTrackIdsByType;
|
|
432
|
+
private updateClipPositionWithHistory;
|
|
433
|
+
private updateClipPositionSilently;
|
|
434
|
+
private ensureSelectionBoxRect;
|
|
435
|
+
private showSelectionBoxRect;
|
|
436
|
+
private hideSelectionBoxRect;
|
|
437
|
+
private handleStageMouseDown;
|
|
438
|
+
private handleBoxSelectMove;
|
|
439
|
+
private handleBoxSelectEnd;
|
|
440
|
+
private isPointOnClip;
|
|
441
|
+
private getClipsIntersectingBox;
|
|
373
442
|
private addClipToTrack;
|
|
374
443
|
private cloneTrackSnapshot;
|
|
375
444
|
private ensureTrackFromHistorySnapshot;
|
|
445
|
+
private getTrackRestoreAnchor;
|
|
446
|
+
private resolveTrackInsertionFromRestoreAnchor;
|
|
376
447
|
private resolveTrackIdByClipId;
|
|
377
448
|
private findTrackByClipId;
|
|
378
449
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Action, Clip, ClipStateUpdate, HistoryState, MoveClipBetweenTracksAction, MoveClipAction, MultiClipUpdateAction, RemoveClipAction, RemoveGapsAction, RestoreClipAudioAction, ResizeClipAction, SeparateClipAudioAction, SetTimeAction, SplitClipAction, Track, UpdateClipAction, AddClipAction } from '../models/types';
|
|
1
|
+
import { Action, Clip, ClipStateUpdate, CompoundAction, HistoryState, MoveClipBetweenTracksAction, MoveClipAction, MultiClipUpdateAction, RemoveClipAction, RemoveGapsAction, RestoreClipAudioAction, ResizeClipAction, SeparateClipAudioAction, SetTimeAction, SplitClipAction, Track, TrackRestoreAnchor, UpdateClipAction, AddClipAction } from '../models/types';
|
|
2
2
|
export declare class HistoryManager {
|
|
3
3
|
private state;
|
|
4
4
|
private maxHistorySize;
|
|
@@ -42,6 +42,7 @@ export declare class HistoryManager {
|
|
|
42
42
|
* 创建移除片段操作
|
|
43
43
|
*/
|
|
44
44
|
createRemoveClipAction(clip: Clip): RemoveClipAction;
|
|
45
|
+
createRemoveClipActionWithTrackSnapshot(clip: Clip, sourceTrackId: string | null, sourceTrackSnapshot: Track | null, sourceTrackRestoreAnchor?: TrackRestoreAnchor | null): RemoveClipAction;
|
|
45
46
|
/**
|
|
46
47
|
* 创建更新片段操作
|
|
47
48
|
*/
|
|
@@ -76,4 +77,5 @@ export declare class HistoryManager {
|
|
|
76
77
|
createMoveClipBetweenTracksAction(clipId: string, sourceTrackId: string, targetTrackId: string, clipBefore: Clip, clipAfter: Clip, sourceTrackSnapshot: Track | null, targetTrackSnapshot: Track | null): MoveClipBetweenTracksAction;
|
|
77
78
|
createSeparateClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): SeparateClipAudioAction;
|
|
78
79
|
createRestoreClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): RestoreClipAudioAction;
|
|
80
|
+
createCompoundAction(actions: Action[], label?: string): CompoundAction;
|
|
79
81
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Action, Clip, Track } from '../models/types';
|
|
1
|
+
import type { Action, Clip, Track, TrackRestoreAnchor } from '../models/types';
|
|
2
2
|
export interface TimelineHistoryExecutionTrack {
|
|
3
3
|
addClip(clip: Clip): void;
|
|
4
4
|
}
|
|
@@ -7,7 +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
|
-
ensureTrackFromHistorySnapshot(trackSnapshot: Track): string | null;
|
|
10
|
+
ensureTrackFromHistorySnapshot(trackSnapshot: Track, restoreAnchor?: TrackRestoreAnchor | null): string | null;
|
|
11
11
|
removeClipGaps(): void;
|
|
12
12
|
getClips(): Clip[];
|
|
13
13
|
findTrackByClipId(clipId: string): TimelineHistoryExecutionTrack | null;
|
|
@@ -19,5 +19,10 @@ export declare class TimelineHistoryExecutor {
|
|
|
19
19
|
constructor(target: TimelineHistoryExecutionTarget);
|
|
20
20
|
executeUndo(action: Action): void;
|
|
21
21
|
executeRedo(action: Action): void;
|
|
22
|
+
private executeUndoInternal;
|
|
23
|
+
private executeRedoInternal;
|
|
22
24
|
private restoreMovedClip;
|
|
25
|
+
private normalizeRemoveClipActionData;
|
|
26
|
+
private resolveTrackIdForHistorySnapshot;
|
|
27
|
+
private resolveRestoreAnchor;
|
|
23
28
|
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { HistoryManager } from '../history/history';
|
|
2
|
-
import type { Action, Clip, ClipStateUpdate, Track } from '../models/types';
|
|
2
|
+
import type { Action, Clip, ClipStateUpdate, Track, TrackRestoreAnchor } from '../models/types';
|
|
3
3
|
export declare class TimelineHistoryRecorder {
|
|
4
4
|
private readonly history;
|
|
5
5
|
constructor(history: HistoryManager);
|
|
6
6
|
recordAddClip(clip: Clip): Action;
|
|
7
|
-
|
|
7
|
+
createRemoveClipAction(clip: Clip, sourceTrackId?: string | null, sourceTrackSnapshot?: Track | null, sourceTrackRestoreAnchor?: TrackRestoreAnchor | null): Action;
|
|
8
|
+
recordRemoveClip(clip: Clip, sourceTrackId?: string | null, sourceTrackSnapshot?: Track | null, sourceTrackRestoreAnchor?: TrackRestoreAnchor | null): Action;
|
|
8
9
|
recordClipUpdate(clip: Clip, originalClip?: Clip, clipUpdates?: ClipStateUpdate[]): Action | null;
|
|
9
10
|
recordSplitClip(clip1: Clip, clip2: Clip): Action;
|
|
10
11
|
recordRemoveGaps(clipsBefore: Clip[]): Action | null;
|
|
11
12
|
recordMoveClipBetweenTracks(clipId: string, sourceTrackId: string, targetTrackId: string, clipBefore: Clip, clipAfter: Clip, sourceTrackSnapshot: Track | null, targetTrackSnapshot: Track | null): Action;
|
|
12
13
|
recordSeparateClipAudio(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
|
|
13
14
|
recordRestoreClipAudio(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
|
|
15
|
+
createSeparateClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
|
|
16
|
+
createRestoreClipAudioAction(videoClipBefore: Clip, videoClipAfter: Clip, audioClip: Clip | null, audioTrackId: string | null): Action;
|
|
17
|
+
recordCompoundAction(actions: Action[], label?: string): Action | null;
|
|
14
18
|
private createClipUpdateAction;
|
|
15
19
|
}
|
|
@@ -51,6 +51,7 @@ export interface ClipConfig {
|
|
|
51
51
|
separatedAudioClipId?: string;
|
|
52
52
|
separatedFromVideoClipId?: string;
|
|
53
53
|
trackId?: string;
|
|
54
|
+
volume?: number;
|
|
54
55
|
}
|
|
55
56
|
export interface ClipEntity {
|
|
56
57
|
id: string;
|
|
@@ -70,6 +71,7 @@ export interface ClipEntity {
|
|
|
70
71
|
visualTransform?: ClipVisualTransform;
|
|
71
72
|
separatedAudioClipId?: string;
|
|
72
73
|
separatedFromVideoClipId?: string;
|
|
74
|
+
volume?: number;
|
|
73
75
|
}
|
|
74
76
|
export interface ClipViewState {
|
|
75
77
|
isDragging: boolean;
|
|
@@ -201,9 +203,18 @@ export interface AddClipAction {
|
|
|
201
203
|
}
|
|
202
204
|
export interface RemoveClipAction {
|
|
203
205
|
type: 'remove_clip';
|
|
204
|
-
data:
|
|
206
|
+
data: {
|
|
207
|
+
clip: Clip;
|
|
208
|
+
sourceTrackId: string | null;
|
|
209
|
+
sourceTrackSnapshot: Track | null;
|
|
210
|
+
sourceTrackRestoreAnchor: TrackRestoreAnchor | null;
|
|
211
|
+
};
|
|
205
212
|
timestamp: number;
|
|
206
213
|
}
|
|
214
|
+
export interface TrackRestoreAnchor {
|
|
215
|
+
previousTrackId: string | null;
|
|
216
|
+
nextTrackId: string | null;
|
|
217
|
+
}
|
|
207
218
|
export interface UpdateClipAction {
|
|
208
219
|
type: 'update_clip';
|
|
209
220
|
data: {
|
|
@@ -297,7 +308,15 @@ export interface RestoreClipAudioAction {
|
|
|
297
308
|
};
|
|
298
309
|
timestamp: number;
|
|
299
310
|
}
|
|
300
|
-
export
|
|
311
|
+
export interface CompoundAction {
|
|
312
|
+
type: 'compound';
|
|
313
|
+
data: {
|
|
314
|
+
actions: Action[];
|
|
315
|
+
label?: string;
|
|
316
|
+
};
|
|
317
|
+
timestamp: number;
|
|
318
|
+
}
|
|
319
|
+
export type Action = AddClipAction | RemoveClipAction | UpdateClipAction | SplitClipAction | MoveClipAction | ResizeClipAction | SetTimeAction | RemoveGapsAction | MultiClipUpdateAction | MoveClipBetweenTracksAction | SeparateClipAudioAction | RestoreClipAudioAction | CompoundAction;
|
|
301
320
|
export type ActionType = Action['type'];
|
|
302
321
|
export interface Position {
|
|
303
322
|
x: number;
|
|
@@ -311,7 +330,7 @@ export interface HistoryState {
|
|
|
311
330
|
past: Action[];
|
|
312
331
|
future: Action[];
|
|
313
332
|
}
|
|
314
|
-
export type TimelineEvent = 'time_change' | 'play_state_change' | 'clip_added' | 'clip_removed' | 'clip_updated' | 'zoom_change' | 'history_change' | 'track_duration_change' | 'clip_selected' | 'selected_clip_change' | 'speed_change' | 'can_play_change' | 'buffering_state_change' | 'source_loading_change' | 'preview_aspect_ratio_change' | 'draft_loaded';
|
|
333
|
+
export type TimelineEvent = 'time_change' | 'play_state_change' | 'clip_added' | 'clip_removed' | 'clip_updated' | 'zoom_change' | 'history_change' | 'track_duration_change' | 'clip_selected' | 'selected_clip_change' | 'speed_change' | 'can_play_change' | 'buffering_state_change' | 'source_loading_change' | 'preview_aspect_ratio_change' | 'draft_loaded' | 'selection_change';
|
|
315
334
|
export interface TimeChangeData {
|
|
316
335
|
time: TimeMs;
|
|
317
336
|
}
|
|
@@ -350,6 +369,10 @@ export interface DraftLoadedData {
|
|
|
350
369
|
error: unknown;
|
|
351
370
|
}>;
|
|
352
371
|
}
|
|
372
|
+
export interface MultiSelectionChangeData {
|
|
373
|
+
selectedClipIds: string[];
|
|
374
|
+
previousSelectedClipIds: string[];
|
|
375
|
+
}
|
|
353
376
|
export interface EventListener {
|
|
354
377
|
(event: TimelineEvent, data?: any): void;
|
|
355
378
|
}
|
|
@@ -397,4 +420,5 @@ export interface ClipExportData {
|
|
|
397
420
|
visualTransform?: ClipVisualTransform;
|
|
398
421
|
separatedAudioClipId?: string;
|
|
399
422
|
separatedFromVideoClipId?: string;
|
|
423
|
+
volume?: number;
|
|
400
424
|
}
|
|
@@ -17,6 +17,6 @@ export declare class TimelinePresentationAdapter {
|
|
|
17
17
|
syncZoom(timeline: TimelineZoomView | null, playhead: TimelineZoomView | null, tracks: TimelineTrackCollection, zoom: number): void;
|
|
18
18
|
syncScrollLeft(playhead: TimelineScrollView | null, tracks: TimelineTrackCollection, scrollLeft: number): void;
|
|
19
19
|
syncScrollTop(panel: TimelineVerticalScrollView | null, scrollbar: TimelineVerticalScrollView | null, scrollTop: number): void;
|
|
20
|
-
syncSelection(tracks: TimelineTrackCollection, selectedClipId: string | null): void;
|
|
20
|
+
syncSelection(tracks: TimelineTrackCollection, selectedClipId: string | null, selectedClipIds?: string[]): void;
|
|
21
21
|
findSelectedClip(tracks: TimelineTrackCollection): Clip | null;
|
|
22
22
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
export declare class SelectionStore {
|
|
2
|
-
private
|
|
2
|
+
private selectedClipIds;
|
|
3
3
|
getSelectedClipId(): string | null;
|
|
4
|
+
getSelectedClipIds(): string[];
|
|
5
|
+
addToSelection(clipId: string): void;
|
|
6
|
+
removeFromSelection(clipId: string): void;
|
|
7
|
+
toggleSelection(clipId: string): void;
|
|
8
|
+
setSelection(clipIds: string[]): void;
|
|
4
9
|
setSelectedClipId(clipId: string | null): void;
|
|
5
10
|
clear(): void;
|
|
6
11
|
hasSelection(): boolean;
|
|
12
|
+
isSelected(clipId: string): boolean;
|
|
13
|
+
getSelectionCount(): number;
|
|
7
14
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import Konva from 'konva';
|
|
2
2
|
import { Track } from '../../components/track/Track';
|
|
3
3
|
import type { Clip, ClipStateUpdate, Theme, TimeMs, TrackConfig, TrackType } from '../models/types';
|
|
4
|
+
export interface MultiDragMoveRequest {
|
|
5
|
+
draggedClipId: string;
|
|
6
|
+
deltaTime: TimeMs;
|
|
7
|
+
targetTrackY: number;
|
|
8
|
+
currentTrackId: string;
|
|
9
|
+
crossTrackOffsetY: number;
|
|
10
|
+
isFinal: boolean;
|
|
11
|
+
}
|
|
4
12
|
export interface TimelineTrackBridgeHandlers {
|
|
5
13
|
onClipUpdate: (clip: Clip, originalClip?: Clip, clipUpdates?: ClipStateUpdate[]) => void;
|
|
6
14
|
onClipAdd: (clip: Clip) => void;
|
|
@@ -15,6 +23,11 @@ export interface TimelineTrackBridgeHandlers {
|
|
|
15
23
|
onClearDropPreview?: () => void;
|
|
16
24
|
onClearSelection?: () => void;
|
|
17
25
|
onSnapGuideChange?: (guideTime: TimeMs | null) => void;
|
|
26
|
+
onClipToggleSelection?: (clipId: string) => void;
|
|
27
|
+
onSetSingleSelection?: (clipId: string) => void;
|
|
28
|
+
getMultiDragClipIds?: (clipId: string) => string[] | null;
|
|
29
|
+
onMultiDragMove?: (request: MultiDragMoveRequest) => boolean | void;
|
|
30
|
+
onMultiDragInteractionEnd?: () => void;
|
|
18
31
|
}
|
|
19
32
|
export interface TimelineTrackBridgeCreateOptions {
|
|
20
33
|
layer: Konva.Layer;
|
|
@@ -28,7 +41,7 @@ export interface TimelineTrackBridgeCreateOptions {
|
|
|
28
41
|
enableClipSnap?: boolean;
|
|
29
42
|
clipSnapThreshold?: number;
|
|
30
43
|
}
|
|
31
|
-
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) => 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, dragActivationThreshold?: number, enableClipSnap?: boolean, clipSnapThreshold?: number) => TTrack;
|
|
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) => 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;
|
|
32
45
|
export declare class TimelineTrackBridge<TTrack = Track> {
|
|
33
46
|
private readonly handlers;
|
|
34
47
|
private readonly TrackCtor;
|