@linker-design-plus/timeline-track 2.1.6 → 2.2.1
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/core/controllers/domPreviewBackend.d.ts +2 -2
- package/dist/core/controllers/index.d.ts +2 -1
- package/dist/core/controllers/previewBackend.d.ts +5 -0
- package/dist/core/controllers/previewTransformMath.d.ts +6 -0
- package/dist/core/controllers/previewTransformOverlay.d.ts +21 -0
- package/dist/core/facade/timelineManager.d.ts +3 -0
- package/dist/core/models/types.d.ts +30 -0
- package/dist/core/preview/index.d.ts +2 -0
- package/dist/core/preview/mediaHelpers.d.ts +24 -0
- package/dist/core/preview/previewClockAggregator.d.ts +19 -0
- package/dist/core/preview/previewDomCompositor.d.ts +50 -0
- package/dist/core/preview/previewEngine.d.ts +87 -0
- package/dist/core/preview/previewPlaybackSlot.d.ts +59 -0
- package/dist/core/preview/previewPrefetchPool.d.ts +11 -0
- package/dist/core/preview/previewRuntimePublisher.d.ts +20 -0
- package/dist/core/preview/previewSourceService.d.ts +25 -0
- package/dist/core/preview/previewSyncCoordinator.d.ts +34 -0
- package/dist/core/preview/previewTextLayer.d.ts +53 -0
- package/dist/core/preview/textClipExportLayout.d.ts +5 -0
- package/dist/core/preview/textPreviewLayout.d.ts +79 -0
- package/dist/core/preview/types.d.ts +65 -0
- package/dist/index.cjs.js +113 -112
- package/dist/index.es.js +6550 -6770
- package/package.json +1 -1
- package/dist/core/controllers/previewRecoveryExecution.d.ts +0 -25
- package/dist/core/controllers/previewSlotLifecycle.d.ts +0 -42
- package/dist/core/controllers/previewSlotPolicy.d.ts +0 -59
- package/dist/core/controllers/timelinePreviewSession.d.ts +0 -220
package/README.md
CHANGED
|
@@ -146,5 +146,5 @@ pnpm exec tsc -p tsconfig.json --noEmit
|
|
|
146
146
|
- [docs/implementation-notes.md](./docs/implementation-notes.md): selection、preview、resource cache、draft 的实现笔记
|
|
147
147
|
- [docs/resource-cache.md](./docs/resource-cache.md): 预览资源缓存的能力边界与配置
|
|
148
148
|
- [docs/interaction-model.md](./docs/interaction-model.md): 指针交互分层和拖拽约束
|
|
149
|
-
- [docs/preview-
|
|
149
|
+
- [docs/preview-clock-flow.md](./docs/preview-clock-flow.md): 预览媒体时钟驱动播放与 buffering 阻塞流程
|
|
150
150
|
- [docs/testing-harness-roadmap.md](./docs/testing-harness-roadmap.md): Harness Engineering 导向的测试与诊断重构方案
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TimelinePreviewBackend, TimelinePreviewBackendOptions } from './previewBackend';
|
|
2
|
-
import {
|
|
3
|
-
export declare class DomPreviewBackend extends
|
|
2
|
+
import { PreviewEngine } from '../preview/previewEngine';
|
|
3
|
+
export declare class DomPreviewBackend extends PreviewEngine implements TimelinePreviewBackend {
|
|
4
4
|
constructor(options?: TimelinePreviewBackendOptions);
|
|
5
5
|
destroy(): void;
|
|
6
6
|
}
|
|
@@ -7,7 +7,8 @@ export { PreviewClockController } from './previewClockController';
|
|
|
7
7
|
export * from './timelineDurationController';
|
|
8
8
|
export { TimelineEventDispatcher } from './timelineEventDispatcher';
|
|
9
9
|
export { TimelinePlaybackResolver } from './timelinePlaybackResolver';
|
|
10
|
-
export {
|
|
10
|
+
export { PreviewEngine } from '../preview/previewEngine';
|
|
11
|
+
export { PreviewEngine as TimelinePreviewSession } from '../preview/previewEngine';
|
|
11
12
|
export { TimelinePreviewRuntimeController } from './timelinePreviewRuntimeController';
|
|
12
13
|
export { TimelinePreviewStateController, type TimelinePendingPreviewState } from './timelinePreviewStateController';
|
|
13
14
|
export { TimelineKeyboardShortcutsController, type TimelineKeyboardShortcutsControllerCallbacks } from './timelineKeyboardShortcutsController';
|
|
@@ -23,6 +23,11 @@ export interface TimelinePreviewBackendCallbacks {
|
|
|
23
23
|
onVisualTransformCommit?: (clipId: string, visualTransform: ClipVisualTransform) => void;
|
|
24
24
|
onTextFontSizeCommit?: (clipId: string, fontSize: number) => void;
|
|
25
25
|
onTextRotationCommit?: (clipId: string, rotation: number) => void;
|
|
26
|
+
onTextLayoutCommit?: (clipId: string, layout: {
|
|
27
|
+
layoutWidth: number;
|
|
28
|
+
layoutHeight: number;
|
|
29
|
+
}) => void;
|
|
30
|
+
onTextLayoutResolved?: (clipId: string, layout: import('../models/types').TextClipResolvedLayout) => void;
|
|
26
31
|
onPendingPreviewRetry?: () => void;
|
|
27
32
|
onRuntimeError?: (error: unknown) => void;
|
|
28
33
|
/** 预览层文字交互(点击字幕或文字变换控件)时若时间线处于播放中,用于请求暂停 */
|
|
@@ -16,10 +16,16 @@ export interface PreviewMediaSize {
|
|
|
16
16
|
height: number;
|
|
17
17
|
}
|
|
18
18
|
export declare const DEFAULT_CLIP_VISUAL_TRANSFORM: ClipVisualTransform;
|
|
19
|
+
/** 文本预览底边与画幅底边的默认间距(相对画幅高度)。 */
|
|
20
|
+
export declare const TEXT_CLIP_BOTTOM_MARGIN_RATIO = 0.06;
|
|
21
|
+
/** 新建文本时的中心锚点默认位(基于 720 高 baseline 与估算框高)。 */
|
|
22
|
+
export declare const DEFAULT_TEXT_CLIP_VISUAL_TRANSFORM: ClipVisualTransform;
|
|
19
23
|
export declare const MIN_CLIP_VISUAL_SCALE = 0.1;
|
|
20
24
|
export declare const MAX_CLIP_VISUAL_SCALE = 8;
|
|
21
25
|
export declare function clampClipVisualScale(scale: number): number;
|
|
22
26
|
export declare function resolveClipVisualTransform(transform?: Partial<ClipVisualTransform> | null): ClipVisualTransform;
|
|
27
|
+
/** 文本位移使用框中心锚点;预览布局不使用 scale(固定为 1)。 */
|
|
28
|
+
export declare function resolveTextClipVisualTransform(transform?: Partial<ClipVisualTransform> | null): ClipVisualTransform;
|
|
23
29
|
export declare function areClipVisualTransformsEqual(left?: Partial<ClipVisualTransform> | null, right?: Partial<ClipVisualTransform> | null, epsilon?: number): boolean;
|
|
24
30
|
export declare function calculateContainRect(frame: PreviewFrameSize, media: PreviewMediaSize): PreviewRect;
|
|
25
31
|
export declare function calculatePreviewDisplayRect(frame: PreviewFrameSize, media: PreviewMediaSize, transform?: Partial<ClipVisualTransform> | null): PreviewRect;
|
|
@@ -9,6 +9,11 @@ export interface PreviewTransformOverlayState {
|
|
|
9
9
|
clipType?: 'text' | 'video';
|
|
10
10
|
currentFontSize?: number;
|
|
11
11
|
currentRotation?: number;
|
|
12
|
+
currentLayoutWidth?: number;
|
|
13
|
+
currentLayoutHeight?: number;
|
|
14
|
+
/** 文本选中时把手柄挂到此元素,与文案共用一个框 */
|
|
15
|
+
textHostElement?: HTMLElement | null;
|
|
16
|
+
baselineScale?: number;
|
|
12
17
|
}
|
|
13
18
|
interface PreviewTransformOverlayCallbacks {
|
|
14
19
|
onPreviewTransformChange?: (clipId: string, transform: ClipVisualTransform | null) => void;
|
|
@@ -17,6 +22,14 @@ interface PreviewTransformOverlayCallbacks {
|
|
|
17
22
|
onFontSizeCommit?: (clipId: string, fontSize: number) => void;
|
|
18
23
|
onRotationChange?: (clipId: string, rotation: number | null) => void;
|
|
19
24
|
onRotationCommit?: (clipId: string, rotation: number) => void;
|
|
25
|
+
onTextLayoutChange?: (clipId: string, layout: {
|
|
26
|
+
layoutWidth: number;
|
|
27
|
+
layoutHeight: number;
|
|
28
|
+
} | null) => void;
|
|
29
|
+
onTextLayoutCommit?: (clipId: string, layout: {
|
|
30
|
+
layoutWidth: number;
|
|
31
|
+
layoutHeight: number;
|
|
32
|
+
}) => void;
|
|
20
33
|
/** 文字片段预览变换框上开始拖拽/缩放/旋转(播放中时应暂停) */
|
|
21
34
|
onTextTransformInteractionStart?: () => void;
|
|
22
35
|
}
|
|
@@ -30,6 +43,7 @@ export declare class PreviewTransformOverlay {
|
|
|
30
43
|
private readonly handlePointerDownHandlers;
|
|
31
44
|
private state;
|
|
32
45
|
private interaction;
|
|
46
|
+
private textHostElement;
|
|
33
47
|
private readonly callbacks;
|
|
34
48
|
private readonly boundPointerMove;
|
|
35
49
|
private readonly boundPointerUp;
|
|
@@ -39,6 +53,8 @@ export declare class PreviewTransformOverlay {
|
|
|
39
53
|
attach(frameElement: HTMLElement): void;
|
|
40
54
|
detach(): void;
|
|
41
55
|
sync(state: PreviewTransformOverlayState | null): void;
|
|
56
|
+
/** 在已选中的文本框上按下时启动移动(与手柄同属一个框) */
|
|
57
|
+
handleTextHostPointerDown(event: Event): void;
|
|
42
58
|
private createRotateHandle;
|
|
43
59
|
private getRotateHandlePointerDown;
|
|
44
60
|
private createHandle;
|
|
@@ -49,6 +65,11 @@ export declare class PreviewTransformOverlay {
|
|
|
49
65
|
private handleDocumentPointerMove;
|
|
50
66
|
private finishInteraction;
|
|
51
67
|
private render;
|
|
68
|
+
private renderTextHostHandles;
|
|
69
|
+
private renderOverlayBox;
|
|
70
|
+
private renderRotateHandle;
|
|
71
|
+
private setTextHost;
|
|
72
|
+
private mountHandlesOn;
|
|
52
73
|
private positionHandle;
|
|
53
74
|
private toLocalPoint;
|
|
54
75
|
private getFrameBounds;
|
|
@@ -349,6 +349,7 @@ export declare class TimelineManager {
|
|
|
349
349
|
private clearTimelineClipMediaStatus;
|
|
350
350
|
private getFailedPreviewClipIds;
|
|
351
351
|
private getExportComposition;
|
|
352
|
+
private resolveTextClipExportLayout;
|
|
352
353
|
private getExportCoverUrl;
|
|
353
354
|
exportTimeline(): TimelineExportData;
|
|
354
355
|
importTimeline(data: TimelineExportData): Promise<void>;
|
|
@@ -640,6 +641,8 @@ export declare class TimelineManager {
|
|
|
640
641
|
private resolveSelectedClipSnapshot;
|
|
641
642
|
private resolveSelectionChangeData;
|
|
642
643
|
private commitPreviewVisualTransform;
|
|
644
|
+
private commitPreviewTextLayout;
|
|
645
|
+
private commitPreviewTextResolvedLayout;
|
|
643
646
|
private commitPreviewTextFontSize;
|
|
644
647
|
private commitPreviewTextRotation;
|
|
645
648
|
clearAllTracksAndClips(): void;
|
|
@@ -23,9 +23,35 @@ export interface TextClipStyle {
|
|
|
23
23
|
strokeColor: string;
|
|
24
24
|
strokeWidth: number;
|
|
25
25
|
paddingX: number;
|
|
26
|
+
/** 文本区域宽度(baseline 画布像素)。 */
|
|
27
|
+
layoutWidth?: number;
|
|
28
|
+
/** 文本区域高度(baseline 画布像素);未设时由内容估算。 */
|
|
29
|
+
layoutHeight?: number;
|
|
30
|
+
/** 1 = visualTransform 为框中心锚点。 */
|
|
31
|
+
anchorVersion?: number;
|
|
26
32
|
}
|
|
27
33
|
export declare const DEFAULT_TEXT_CLIP_STYLE: TextClipStyle;
|
|
28
34
|
export declare function resolveTextClipStyle(style?: Partial<TextClipStyle> | null): TextClipStyle;
|
|
35
|
+
export type TextClipLayoutMode = 'auto' | 'manual';
|
|
36
|
+
/** 预览层布局完成后的归一化快照,供导出与后端渲染。 */
|
|
37
|
+
export interface TextClipResolvedLayout {
|
|
38
|
+
/** 与 export composition 一致的 baseline 画布尺寸(短边 720)。 */
|
|
39
|
+
compositionWidth: number;
|
|
40
|
+
compositionHeight: number;
|
|
41
|
+
/** 文本框宽(baseline 参考画布 px,非最终成片 px)。 */
|
|
42
|
+
layoutWidth: number;
|
|
43
|
+
/** 文本框高(baseline 参考画布 px,非最终成片 px)。 */
|
|
44
|
+
layoutHeight: number;
|
|
45
|
+
/** layoutWidth / compositionWidth */
|
|
46
|
+
layoutWidthRatio: number;
|
|
47
|
+
/** layoutHeight / compositionHeight */
|
|
48
|
+
layoutHeightRatio: number;
|
|
49
|
+
/** 框中心锚点,画幅归一化 0–1(已从 legacy 底锚迁移)。 */
|
|
50
|
+
centerX: number;
|
|
51
|
+
centerY: number;
|
|
52
|
+
layoutMode: TextClipLayoutMode;
|
|
53
|
+
anchorVersion: 1;
|
|
54
|
+
}
|
|
29
55
|
export type PreviewBackendType = 'dom' | 'canvas' | 'auto';
|
|
30
56
|
export interface Mp4PreviewMediaSource {
|
|
31
57
|
url: string;
|
|
@@ -102,6 +128,7 @@ export interface ClipConfig {
|
|
|
102
128
|
volume?: number;
|
|
103
129
|
textContent?: string;
|
|
104
130
|
textStyle?: Partial<TextClipStyle>;
|
|
131
|
+
textResolvedLayout?: TextClipResolvedLayout;
|
|
105
132
|
ttsSourceTextClipId?: string;
|
|
106
133
|
ttsVoiceId?: string;
|
|
107
134
|
ttsVoiceName?: string;
|
|
@@ -130,6 +157,7 @@ export interface ClipEntity {
|
|
|
130
157
|
volume?: number;
|
|
131
158
|
textContent?: string;
|
|
132
159
|
textStyle?: TextClipStyle;
|
|
160
|
+
textResolvedLayout?: TextClipResolvedLayout;
|
|
133
161
|
ttsSourceTextClipId?: string;
|
|
134
162
|
ttsVoiceId?: string;
|
|
135
163
|
ttsVoiceName?: string;
|
|
@@ -512,6 +540,7 @@ export interface TimelineCompositionExportData {
|
|
|
512
540
|
height: number;
|
|
513
541
|
aspectRatio: string;
|
|
514
542
|
mode: PreviewAspectRatioMode;
|
|
543
|
+
textPreviewFont?: TextPreviewFontConfig;
|
|
515
544
|
}
|
|
516
545
|
export interface TrackExportData {
|
|
517
546
|
id: string;
|
|
@@ -542,6 +571,7 @@ export interface ClipExportData {
|
|
|
542
571
|
volume?: number;
|
|
543
572
|
textContent?: string;
|
|
544
573
|
textStyle?: TextClipStyle;
|
|
574
|
+
textResolvedLayout?: TextClipResolvedLayout;
|
|
545
575
|
ttsSourceTextClipId?: string;
|
|
546
576
|
ttsVoiceId?: string;
|
|
547
577
|
ttsVoiceName?: string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo } from '../models/types';
|
|
2
|
+
import type { DiagnosticMediaState } from '../../utils/diagnostics';
|
|
3
|
+
export declare const HAVE_CURRENT_DATA_STATE = 2;
|
|
4
|
+
export declare const HAVE_FUTURE_DATA_STATE = 3;
|
|
5
|
+
export declare const STEADY_PLAYBACK_CURRENT_SLOT_SEEK_THRESHOLD_SECONDS = 0.5;
|
|
6
|
+
export declare const CLOCK_SOURCE_TARGET_ALIGNMENT_THRESHOLD_MS = 750;
|
|
7
|
+
export declare const SOURCE_RESOLVE_TIMEOUT_MS = 15000;
|
|
8
|
+
export declare const PREVIEW_SLOT_FAILURE_MESSAGE = "\u5A92\u4F53\u4E22\u5931";
|
|
9
|
+
export declare function isPromiseLike<T>(value: T | Promise<T>): value is Promise<T>;
|
|
10
|
+
export declare function getAssignedSrc(element: HTMLMediaElement): string;
|
|
11
|
+
export declare function getConfiguredSrc(element: HTMLMediaElement): string;
|
|
12
|
+
export declare function getMediaSourceDebugState(element: HTMLMediaElement, desiredSource?: string | null): Record<string, unknown>;
|
|
13
|
+
export declare function isVideoMediaElement(element: HTMLMediaElement): element is HTMLVideoElement;
|
|
14
|
+
export declare function safeLoad(element: HTMLMediaElement): void;
|
|
15
|
+
export declare function safePause(element: HTMLMediaElement): void;
|
|
16
|
+
export declare function safePlay(element: HTMLMediaElement): void;
|
|
17
|
+
export declare function safeSetCurrentTime(element: HTMLMediaElement, nextTimeSeconds: number): void;
|
|
18
|
+
export declare function getClipStyleDimension(entry: ActiveClipPlaybackInfo | null, widthKey: 'sys_width' | 'sys_height'): number | null;
|
|
19
|
+
export declare function parsePxValue(value?: string): number;
|
|
20
|
+
export declare function getBufferedEnd(element: HTMLMediaElement): number | null;
|
|
21
|
+
export declare function getMediaDebugState(element: HTMLMediaElement): DiagnosticMediaState;
|
|
22
|
+
export declare function doesSlotSourceMatchDesired(configuredSource: string | null, assignedSource: string | null, desiredSource: string | null): boolean;
|
|
23
|
+
export declare function getSlotSeekThresholdSeconds(role: 'current' | 'preload', playbackRate: number): number;
|
|
24
|
+
export declare function buildPreviewSourceCacheKey(clip: ActiveClipPlaybackInfo['clip']): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TimeMs } from '../models/types';
|
|
2
|
+
import type { PreviewClockState, PreviewLoadingState } from '../controllers/previewBackend';
|
|
3
|
+
import type { PlaybackSlotState } from './types';
|
|
4
|
+
export declare class PreviewClockAggregator {
|
|
5
|
+
private pendingClockAlignmentTargetTime;
|
|
6
|
+
updateAlignmentTarget(hasActiveVisualVideo: boolean, interactionMode: 'steady' | 'seek' | 'scrub' | undefined, currentTime: TimeMs): void;
|
|
7
|
+
buildClockState(loading: PreviewLoadingState, slots: PlaybackSlotState[], playbackGroupSuspended: boolean): PreviewClockState;
|
|
8
|
+
private getClockSourceSlot;
|
|
9
|
+
private buildCandidate;
|
|
10
|
+
private compareCandidates;
|
|
11
|
+
private isCandidateAligned;
|
|
12
|
+
private getAlignmentThresholdMs;
|
|
13
|
+
private isMediaTimeAligned;
|
|
14
|
+
private isTimelineAligned;
|
|
15
|
+
private getMediaTimeMs;
|
|
16
|
+
private getTimelineTime;
|
|
17
|
+
private buildFromSlot;
|
|
18
|
+
private resolveBlockedReason;
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, PreviewAspectRatio } from '../models/types';
|
|
2
|
+
import { resolveClipVisualTransform, type PreviewFrameSize } from '../controllers/previewTransformMath';
|
|
3
|
+
import { PreviewTransformOverlay, type PreviewTransformOverlayState } from '../controllers/previewTransformOverlay';
|
|
4
|
+
import { type TextLayoutStyleOverride, type TextPreviewBoxLayout } from './textPreviewLayout';
|
|
5
|
+
import type { PreviewPlaybackSlot } from './previewPlaybackSlot';
|
|
6
|
+
export declare class PreviewDomCompositor {
|
|
7
|
+
private container;
|
|
8
|
+
private rootElement;
|
|
9
|
+
private frameElement;
|
|
10
|
+
private resizeObserver;
|
|
11
|
+
private onFrameLayoutChange;
|
|
12
|
+
private requestedAspectRatio;
|
|
13
|
+
private resolvedAutoAspectRatio;
|
|
14
|
+
readonly transformOverlay: PreviewTransformOverlay;
|
|
15
|
+
constructor(transformOverlay: PreviewTransformOverlay);
|
|
16
|
+
attach(container: HTMLElement, options: {
|
|
17
|
+
rootClassName?: string;
|
|
18
|
+
frameClassName?: string;
|
|
19
|
+
onFrameLayoutChange?: () => void;
|
|
20
|
+
}): void;
|
|
21
|
+
detach(): void;
|
|
22
|
+
getFrameElement(): HTMLDivElement | null;
|
|
23
|
+
getRootElement(): HTMLDivElement | null;
|
|
24
|
+
applyAspectRatio(aspectRatio: PreviewAspectRatio): void;
|
|
25
|
+
getAspectRatio(): PreviewAspectRatio;
|
|
26
|
+
resolveAutoAspectRatio(width: number, height: number, onChange?: (ratio: PreviewAspectRatio) => void): void;
|
|
27
|
+
tryResolveAutoAspectRatioFromSlot(slot: PreviewPlaybackSlot, onChange?: (ratio: PreviewAspectRatio) => void): void;
|
|
28
|
+
refreshSlotVisual(slot: PreviewPlaybackSlot): void;
|
|
29
|
+
buildSelectedOverlayState(primarySelectedClipId: string | null, slots: PreviewPlaybackSlot[], textOverlay: {
|
|
30
|
+
getEntry: (clipId: string) => {
|
|
31
|
+
entry: ActiveClipPlaybackInfo;
|
|
32
|
+
element: HTMLElement;
|
|
33
|
+
} | undefined;
|
|
34
|
+
getLayout?: (clipId: string) => TextPreviewBoxLayout | null;
|
|
35
|
+
getTransientFontSize: (clipId: string) => number | null;
|
|
36
|
+
getTransientRotation: (clipId: string) => number | null;
|
|
37
|
+
getTextLayoutOverride?: (clipId: string) => TextLayoutStyleOverride | null;
|
|
38
|
+
}, getEffectiveVisualTransform: (entry: ActiveClipPlaybackInfo) => ReturnType<typeof resolveClipVisualTransform>): PreviewTransformOverlayState | null;
|
|
39
|
+
getFrameSize(): PreviewFrameSize;
|
|
40
|
+
getTextBaselineMetrics(frameSize: PreviewFrameSize): {
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
scale: number;
|
|
44
|
+
};
|
|
45
|
+
moveTransform(startTransform: ReturnType<typeof resolveClipVisualTransform>, deltaX: number, deltaY: number): ReturnType<typeof resolveClipVisualTransform>;
|
|
46
|
+
transformsEqual(left: ReturnType<typeof resolveClipVisualTransform>, right: ReturnType<typeof resolveClipVisualTransform>): boolean;
|
|
47
|
+
private updateFrameLayout;
|
|
48
|
+
private getMediaSize;
|
|
49
|
+
private syncFailureVisual;
|
|
50
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, PreviewAspectRatio, PreviewSourceResolver, TextPreviewFontConfig, TrackType } from '../models/types';
|
|
2
|
+
import type { PreviewPendingState, PreviewRuntimeState, TimelinePreviewSyncPayload } from '../controllers/previewBackend';
|
|
3
|
+
import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
|
|
4
|
+
import type { ResourceCacheManager } from '../resources/resourceCache';
|
|
5
|
+
import { type DiagnosticEmitInput, type DiagnosticsCenter } from '../../utils/diagnostics';
|
|
6
|
+
import { PreviewPlaybackSlot } from './previewPlaybackSlot';
|
|
7
|
+
import type { PreviewEngineCallbacks } from './types';
|
|
8
|
+
export interface PreviewEngineDependencies {
|
|
9
|
+
createMediaElement?: (kind: TrackType, role: 'current' | 'preload') => HTMLMediaElement;
|
|
10
|
+
createAspectRatioProbe?: () => HTMLVideoElement;
|
|
11
|
+
createAudioContext?: () => AudioContext | null;
|
|
12
|
+
diagnostics?: DiagnosticsCenter;
|
|
13
|
+
getDiagnosticsContext?: () => Partial<DiagnosticEmitInput>;
|
|
14
|
+
rootClassName?: string;
|
|
15
|
+
frameClassName?: string;
|
|
16
|
+
slotClassNamePrefix?: string;
|
|
17
|
+
previewSourceResolver?: PreviewSourceResolver;
|
|
18
|
+
textPreviewFont?: TextPreviewFontConfig | null;
|
|
19
|
+
sourceMediaRegistry?: SourceMediaRegistry;
|
|
20
|
+
resourceCacheManager?: ResourceCacheManager | null;
|
|
21
|
+
}
|
|
22
|
+
export declare class PreviewEngine {
|
|
23
|
+
private container;
|
|
24
|
+
private pendingOverlayElement;
|
|
25
|
+
private readonly pendingOverlayRenderer;
|
|
26
|
+
private readonly compositor;
|
|
27
|
+
private readonly textLayer;
|
|
28
|
+
private readonly sourceService;
|
|
29
|
+
private readonly syncCoordinator;
|
|
30
|
+
private readonly clockAggregator;
|
|
31
|
+
private readonly runtimePublisher;
|
|
32
|
+
private readonly trackSlots;
|
|
33
|
+
private readonly callbacks;
|
|
34
|
+
private readonly dependencies;
|
|
35
|
+
private audioContext;
|
|
36
|
+
private masterGainNode;
|
|
37
|
+
private loadingProbeTimeoutId;
|
|
38
|
+
private clockProbeTimeoutId;
|
|
39
|
+
private playbackGroupSuspended;
|
|
40
|
+
private primarySelectedClipId;
|
|
41
|
+
private transientVisualTransform;
|
|
42
|
+
private transientFontSize;
|
|
43
|
+
private transientTextLayout;
|
|
44
|
+
private transientRotation;
|
|
45
|
+
private pendingState;
|
|
46
|
+
private activeSyncRequestId;
|
|
47
|
+
private isSyncProjecting;
|
|
48
|
+
private lastSyncedPlayState;
|
|
49
|
+
private aspectRatioProbe;
|
|
50
|
+
private aspectRatioProbeSrc;
|
|
51
|
+
constructor(callbacks?: PreviewEngineCallbacks, dependencies?: PreviewEngineDependencies);
|
|
52
|
+
hasPreview(): boolean;
|
|
53
|
+
attach(container: HTMLElement): void;
|
|
54
|
+
detach(): void;
|
|
55
|
+
sync(payload: TimelinePreviewSyncPayload): void;
|
|
56
|
+
applyAspectRatio(aspectRatio: PreviewAspectRatio): void;
|
|
57
|
+
getAspectRatio(): PreviewAspectRatio;
|
|
58
|
+
setPendingState(state: PreviewPendingState | null): void;
|
|
59
|
+
failSlot(slot: PreviewPlaybackSlot, _entry: ActiveClipPlaybackInfo, message: string): void;
|
|
60
|
+
get lastRuntimeState(): PreviewRuntimeState;
|
|
61
|
+
refreshRuntimeState(): void;
|
|
62
|
+
private applyTrackPlan;
|
|
63
|
+
private getTrackSlots;
|
|
64
|
+
private collectSlots;
|
|
65
|
+
private buildLoadingState;
|
|
66
|
+
private buildTrackedSummary;
|
|
67
|
+
private syncPlaybackGroupSuspension;
|
|
68
|
+
private configureAudioRouting;
|
|
69
|
+
private getAudioContext;
|
|
70
|
+
private getMasterGainNode;
|
|
71
|
+
private bootstrapLayoutAspectRatioFromClipStyle;
|
|
72
|
+
private maybeResolveAutoAspectRatio;
|
|
73
|
+
private getAspectRatioProbe;
|
|
74
|
+
private getEffectiveVisualTransform;
|
|
75
|
+
private getTextLayoutOverride;
|
|
76
|
+
private getTextLayoutOptions;
|
|
77
|
+
private handlePreviewTransformChange;
|
|
78
|
+
refreshVisualLayout(): void;
|
|
79
|
+
private refreshTextSelectionOverlay;
|
|
80
|
+
private refreshPendingOverlay;
|
|
81
|
+
private syncLoadingProbe;
|
|
82
|
+
private syncClockProbe;
|
|
83
|
+
private clearProbes;
|
|
84
|
+
private clearLoadingProbe;
|
|
85
|
+
private clearClockProbe;
|
|
86
|
+
private emitDiagnostic;
|
|
87
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, PlayState, TrackType } from '../models/types';
|
|
2
|
+
import type { PreviewSlotPhase } from '../controllers/previewBackend';
|
|
3
|
+
import type { PlaybackSlotState, ResolvedSlotSource, TrackedVideoSlotStatus } from './types';
|
|
4
|
+
export interface PreviewPlaybackSlotOptions {
|
|
5
|
+
createMediaElement?: (kind: TrackType, role: 'current' | 'preload') => HTMLMediaElement;
|
|
6
|
+
slotClassNamePrefix?: string;
|
|
7
|
+
onStateChange?: () => void;
|
|
8
|
+
onMediaError?: (slot: PreviewPlaybackSlot, message: string) => void;
|
|
9
|
+
onDiagnostic?: (eventName: string, slot: PreviewPlaybackSlot, extra?: Record<string, unknown>) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare class PreviewPlaybackSlot implements PlaybackSlotState {
|
|
12
|
+
trackId: string;
|
|
13
|
+
kind: TrackType;
|
|
14
|
+
role: 'current' | 'preload';
|
|
15
|
+
element: HTMLMediaElement;
|
|
16
|
+
wrapper: HTMLDivElement | null;
|
|
17
|
+
failureOverlay: HTMLDivElement | null;
|
|
18
|
+
failureLabel: HTMLDivElement | null;
|
|
19
|
+
entry: ActiveClipPlaybackInfo | null;
|
|
20
|
+
stableSourceUrl: string | null;
|
|
21
|
+
boundStableKey: string | null;
|
|
22
|
+
desiredSource: string | null;
|
|
23
|
+
objectUrl: string | null;
|
|
24
|
+
requestedPlayState: PlayState;
|
|
25
|
+
isActive: boolean;
|
|
26
|
+
isLoading: boolean;
|
|
27
|
+
isResolvingSource: boolean;
|
|
28
|
+
isBuffering: boolean;
|
|
29
|
+
isSeeking: boolean;
|
|
30
|
+
phase: PreviewSlotPhase;
|
|
31
|
+
errorMessage: string | null;
|
|
32
|
+
hasReadyEventSinceSourceChange: boolean;
|
|
33
|
+
pendingClockAlignmentMediaTime: number | null;
|
|
34
|
+
sourceGen: number;
|
|
35
|
+
sourceNode: MediaElementAudioSourceNode | null;
|
|
36
|
+
gainNode: GainNode | null;
|
|
37
|
+
audioRoutingFailed: boolean;
|
|
38
|
+
cleanup: Array<() => void>;
|
|
39
|
+
private lastPlaybackResumeAttemptAtMs;
|
|
40
|
+
private readonly options;
|
|
41
|
+
constructor(trackId: string, kind: TrackType, role: 'current' | 'preload', options?: PreviewPlaybackSlotOptions);
|
|
42
|
+
mountVideoTo(frame: HTMLElement): void;
|
|
43
|
+
mountAudioTo(root: HTMLElement): void;
|
|
44
|
+
clear(): void;
|
|
45
|
+
fail(message: string): void;
|
|
46
|
+
clearFailureIfClipChanged(): void;
|
|
47
|
+
assignSource(resolved: ResolvedSlotSource, stableKey: string): boolean;
|
|
48
|
+
applyPlayback(entry: ActiveClipPlaybackInfo, playState: PlayState, speed: number, visible: boolean, zIndex: number): void;
|
|
49
|
+
sourceMatchesDesired(): boolean;
|
|
50
|
+
maybeResumePlayback(reason: 'canplay' | 'seeked' | 'runtime-check'): void;
|
|
51
|
+
destroy(releaseObjectUrl: (url: string | null) => void): void;
|
|
52
|
+
private syncCurrentPlayback;
|
|
53
|
+
private preparePreload;
|
|
54
|
+
private setVisible;
|
|
55
|
+
private createMediaElement;
|
|
56
|
+
private setupWrapper;
|
|
57
|
+
private bindMediaEvents;
|
|
58
|
+
}
|
|
59
|
+
export declare function evaluateTrackedVideoSlot(slot: PlaybackSlotState, isTracked: boolean): TrackedVideoSlotStatus;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo } from '../models/types';
|
|
2
|
+
import type { PreviewSlotTarget } from '../controllers/previewTrackPlanner';
|
|
3
|
+
import type { PreviewPlaybackSlot } from './previewPlaybackSlot';
|
|
4
|
+
/**
|
|
5
|
+
* Prefetch pool schedules warm loading on preload slots without affecting
|
|
6
|
+
* playback failure state or preview clock aggregation.
|
|
7
|
+
*/
|
|
8
|
+
export declare class PreviewPrefetchPool {
|
|
9
|
+
shouldSchedule(target: PreviewSlotTarget): boolean;
|
|
10
|
+
onPrefetchFailure(slot: PreviewPlaybackSlot, _entry: ActiveClipPlaybackInfo, reason: string): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PreviewRuntimeState } from '../controllers/previewBackend';
|
|
2
|
+
import type { PreviewPlaybackSlot } from './previewPlaybackSlot';
|
|
3
|
+
import type { TrackedVideoSlotStatus } from './types';
|
|
4
|
+
export interface RuntimePublisherInput {
|
|
5
|
+
activeSyncRequestId?: number;
|
|
6
|
+
isSyncProjecting: boolean;
|
|
7
|
+
slots: PreviewPlaybackSlot[];
|
|
8
|
+
evaluateTracked: (slot: PreviewPlaybackSlot) => TrackedVideoSlotStatus;
|
|
9
|
+
isTrackedVideo: (slot: PreviewPlaybackSlot) => boolean;
|
|
10
|
+
clock: PreviewRuntimeState['clock'];
|
|
11
|
+
}
|
|
12
|
+
export declare class PreviewRuntimePublisher {
|
|
13
|
+
private lastSignature;
|
|
14
|
+
private lastState;
|
|
15
|
+
private lastSettledSyncRequestId;
|
|
16
|
+
getLastState(): PreviewRuntimeState;
|
|
17
|
+
publish(input: RuntimePublisherInput, onChange: (state: PreviewRuntimeState) => void, onSyncProcessed?: (syncRequestId?: number) => void): PreviewRuntimeState;
|
|
18
|
+
reset(): void;
|
|
19
|
+
private buildLoadingState;
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, PreviewSourceResolver } from '../models/types';
|
|
2
|
+
import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
|
|
3
|
+
import type { ResourceCacheManager } from '../resources/resourceCache';
|
|
4
|
+
import type { ResolvedSlotSource } from './types';
|
|
5
|
+
export interface PreviewSourceServiceDependencies {
|
|
6
|
+
previewSourceResolver?: PreviewSourceResolver;
|
|
7
|
+
sourceMediaRegistry?: SourceMediaRegistry;
|
|
8
|
+
resourceCacheManager?: ResourceCacheManager | null;
|
|
9
|
+
onDiagnostic?: (eventName: string, data: Record<string, unknown>) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare class PreviewSourceService {
|
|
12
|
+
private readonly dependencies;
|
|
13
|
+
private readonly pendingRuntime;
|
|
14
|
+
constructor(dependencies?: PreviewSourceServiceDependencies);
|
|
15
|
+
resolve(entry: ActiveClipPlaybackInfo, options: {
|
|
16
|
+
sourceGen: number;
|
|
17
|
+
getCurrentSourceGen: () => number;
|
|
18
|
+
reuse?: ResolvedSlotSource | null;
|
|
19
|
+
}): Promise<ResolvedSlotSource | null> | ResolvedSlotSource | null;
|
|
20
|
+
getReusableSource(boundStableKey: string | null, stableSourceUrl: string | null, desiredSource: string | null, objectUrl: string | null, entry: ActiveClipPlaybackInfo): ResolvedSlotSource | null;
|
|
21
|
+
revokeObjectUrl(objectUrl: string | null | undefined): void;
|
|
22
|
+
private resolvePlayable;
|
|
23
|
+
private normalizeCachedResult;
|
|
24
|
+
private emit;
|
|
25
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, TrackType } from '../models/types';
|
|
2
|
+
import type { PreviewSlotTarget, PreviewTrackPlan } from '../controllers/previewTrackPlanner';
|
|
3
|
+
import { evaluateTrackedVideoSlot, PreviewPlaybackSlot } from './previewPlaybackSlot';
|
|
4
|
+
import type { PreviewSourceService } from './previewSourceService';
|
|
5
|
+
export interface TrackSlotPair {
|
|
6
|
+
current: PreviewPlaybackSlot;
|
|
7
|
+
preload: PreviewPlaybackSlot;
|
|
8
|
+
}
|
|
9
|
+
export interface PreviewSyncCoordinatorOptions {
|
|
10
|
+
sourceService: PreviewSourceService;
|
|
11
|
+
getTrackSlots: (trackId: string, kind: TrackType) => TrackSlotPair;
|
|
12
|
+
onAsyncWorkStarted: () => void;
|
|
13
|
+
onAsyncWorkFinished: () => void;
|
|
14
|
+
onStateChange: () => void;
|
|
15
|
+
releaseObjectUrl: (url: string | null) => void;
|
|
16
|
+
rememberObjectUrl: (slot: PreviewPlaybackSlot, url: string | null) => void;
|
|
17
|
+
configureAudio: (slot: PreviewPlaybackSlot, entry: ActiveClipPlaybackInfo) => void;
|
|
18
|
+
syncClockAlignment: (slot: PreviewPlaybackSlot, target: PreviewSlotTarget, clipChanged: boolean) => void;
|
|
19
|
+
isCurrentTarget: (slot: PreviewPlaybackSlot, entry: ActiveClipPlaybackInfo, stableKey: string) => boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class PreviewSyncCoordinator {
|
|
22
|
+
private syncGen;
|
|
23
|
+
private pendingAsyncCount;
|
|
24
|
+
private readonly options;
|
|
25
|
+
constructor(options: PreviewSyncCoordinatorOptions);
|
|
26
|
+
get isProjecting(): boolean;
|
|
27
|
+
applyTrackPlan(plan: PreviewTrackPlan): void | Promise<void>;
|
|
28
|
+
bumpSyncGeneration(): number;
|
|
29
|
+
private applySlotTarget;
|
|
30
|
+
private beginAsyncWork;
|
|
31
|
+
private endAsyncWork;
|
|
32
|
+
}
|
|
33
|
+
export declare function isTrackedActiveCurrentVideoSlot(slot: PreviewPlaybackSlot): boolean;
|
|
34
|
+
export declare function evaluateSlotForRuntime(slot: PreviewPlaybackSlot): ReturnType<typeof evaluateTrackedVideoSlot>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, ClipVisualTransform, TextPreviewFontConfig } from '../models/types';
|
|
2
|
+
import type { PreviewDomCompositor } from './previewDomCompositor';
|
|
3
|
+
import { type TextLayoutStyleOverride, type TextPreviewBoxLayout } from './textPreviewLayout';
|
|
4
|
+
export type { TextLayoutStyleOverride } from './textPreviewLayout';
|
|
5
|
+
export { resolveTextEntryMaxWidthPx } from './textPreviewLayout';
|
|
6
|
+
export interface RefreshTextLayoutOptions {
|
|
7
|
+
getStyleOverride?: (entry: ActiveClipPlaybackInfo) => TextLayoutStyleOverride | null;
|
|
8
|
+
resolveVisualTransform?: (entry: ActiveClipPlaybackInfo) => ClipVisualTransform;
|
|
9
|
+
}
|
|
10
|
+
/** 统一文本框 DOM 层:布局仅来自 computeTextPreviewBox。 */
|
|
11
|
+
export declare class PreviewTextLayer {
|
|
12
|
+
private overlayRoot;
|
|
13
|
+
private readonly entries;
|
|
14
|
+
private fontStyleElement;
|
|
15
|
+
private fontSignature;
|
|
16
|
+
private fontLoadState;
|
|
17
|
+
private fontLoadToken;
|
|
18
|
+
private primarySelectedClipId;
|
|
19
|
+
private textPreviewFont;
|
|
20
|
+
private readonly compositor;
|
|
21
|
+
private readonly callbacks;
|
|
22
|
+
private autoRemeasureScheduled;
|
|
23
|
+
constructor(compositor: PreviewDomCompositor, callbacks: PreviewTextLayer['callbacks']);
|
|
24
|
+
mount(frameElement: HTMLDivElement): HTMLDivElement;
|
|
25
|
+
destroy(): void;
|
|
26
|
+
setPrimarySelectedClipId(clipId: string | null): void;
|
|
27
|
+
setTextPreviewFont(config: TextPreviewFontConfig | null): void;
|
|
28
|
+
syncEntries(activeEntries: ActiveClipPlaybackInfo[], resolveVisualTransform: (entry: ActiveClipPlaybackInfo) => ClipVisualTransform, layoutOptions?: RefreshTextLayoutOptions): void;
|
|
29
|
+
refreshLayout(resolveVisualTransform: (entry: ActiveClipPlaybackInfo) => ClipVisualTransform, options?: RefreshTextLayoutOptions): void;
|
|
30
|
+
getEntry(clipId: string): {
|
|
31
|
+
entry: ActiveClipPlaybackInfo;
|
|
32
|
+
element: HTMLDivElement;
|
|
33
|
+
layout: TextPreviewBoxLayout | null;
|
|
34
|
+
} | undefined;
|
|
35
|
+
getLayout(clipId: string): TextPreviewBoxLayout | null;
|
|
36
|
+
private resolveBaselineScale;
|
|
37
|
+
private reapplyAllContent;
|
|
38
|
+
private applyBoxLayout;
|
|
39
|
+
private applyContent;
|
|
40
|
+
private usesManualTextLayout;
|
|
41
|
+
private emitResolvedLayout;
|
|
42
|
+
private finalizeTextBoxLayout;
|
|
43
|
+
private applyManualTextBoxLayout;
|
|
44
|
+
private applyAutoTextBoxLayout;
|
|
45
|
+
private scheduleAutoRemeasure;
|
|
46
|
+
private getFontFamily;
|
|
47
|
+
private ensureFontReady;
|
|
48
|
+
private resetFontState;
|
|
49
|
+
private clearEntries;
|
|
50
|
+
private removeEntry;
|
|
51
|
+
private createPointerDownHandler;
|
|
52
|
+
private refreshInteractionState;
|
|
53
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Clip, TextClipResolvedLayout, TextPreviewFontConfig } from '../models/types';
|
|
2
|
+
export declare function measureTextClipResolvedLayoutForExport(clip: Pick<Clip, 'textContent' | 'textStyle' | 'visualTransform' | 'name'>, composition: {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}, textPreviewFont?: TextPreviewFontConfig | null): TextClipResolvedLayout | null;
|