@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.
Files changed (29) hide show
  1. package/README.md +1 -1
  2. package/dist/core/controllers/domPreviewBackend.d.ts +2 -2
  3. package/dist/core/controllers/index.d.ts +2 -1
  4. package/dist/core/controllers/previewBackend.d.ts +5 -0
  5. package/dist/core/controllers/previewTransformMath.d.ts +6 -0
  6. package/dist/core/controllers/previewTransformOverlay.d.ts +21 -0
  7. package/dist/core/facade/timelineManager.d.ts +3 -0
  8. package/dist/core/models/types.d.ts +30 -0
  9. package/dist/core/preview/index.d.ts +2 -0
  10. package/dist/core/preview/mediaHelpers.d.ts +24 -0
  11. package/dist/core/preview/previewClockAggregator.d.ts +19 -0
  12. package/dist/core/preview/previewDomCompositor.d.ts +50 -0
  13. package/dist/core/preview/previewEngine.d.ts +87 -0
  14. package/dist/core/preview/previewPlaybackSlot.d.ts +59 -0
  15. package/dist/core/preview/previewPrefetchPool.d.ts +11 -0
  16. package/dist/core/preview/previewRuntimePublisher.d.ts +20 -0
  17. package/dist/core/preview/previewSourceService.d.ts +25 -0
  18. package/dist/core/preview/previewSyncCoordinator.d.ts +34 -0
  19. package/dist/core/preview/previewTextLayer.d.ts +53 -0
  20. package/dist/core/preview/textClipExportLayout.d.ts +5 -0
  21. package/dist/core/preview/textPreviewLayout.d.ts +79 -0
  22. package/dist/core/preview/types.d.ts +65 -0
  23. package/dist/index.cjs.js +113 -112
  24. package/dist/index.es.js +6550 -6770
  25. package/package.json +1 -1
  26. package/dist/core/controllers/previewRecoveryExecution.d.ts +0 -25
  27. package/dist/core/controllers/previewSlotLifecycle.d.ts +0 -42
  28. package/dist/core/controllers/previewSlotPolicy.d.ts +0 -59
  29. 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-playback-recovery-flow.md](./docs/preview-playback-recovery-flow.md): 预览媒体时钟驱动播放、buffering 阻塞与恢复续播流程
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 { TimelinePreviewSession } from './timelinePreviewSession';
3
- export declare class DomPreviewBackend extends TimelinePreviewSession implements TimelinePreviewBackend {
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 { TimelinePreviewSession } from './timelinePreviewSession';
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,2 @@
1
+ export { PreviewEngine, type PreviewEngineDependencies } from './previewEngine';
2
+ export { PreviewSourceService } from './previewSourceService';
@@ -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;