@mengine/medeo-client 1.3.1-alpha.0 → 1.3.1-alpha.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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as index_d_exports } from "./index-CMa6ZCtX.js";
2
- import { $ as VoiceDescriptor, A as generatePartId, At as Attachment, B as ResolvedEntityClipPlacement, C as PlannedSemanticOpKind, Ct as VideoDocumentSchemaVersion, D as SnapshotReadable, Dt as VideoDraftPartUnion, E as SchemaValidator, Et as VideoDraft, F as videoDocumentMirrorSchema, Ft as Timeline$1, G as EntityTimelineProjectionError, H as ResolvedEntityTrack, I as VideoDocumentValidationError, It as Track$1, J as JsonObject, K as EntityRelationRows, L as assertValidVideoDocument, Lt as TrackItem$1, M as TrackItemDraft, Mt as CaptionStyle, N as VideoDocumentDraft, Nt as PartAggregation, O as ValidationError, Ot as effectiveVideoClipDurationMs, P as VideoDocumentMirrorSchema, Pt as SpeedShift, Q as SequenceRange, R as validateVideoDocument, S as ImplementedSemanticOpKind, St as VideoDocument, T as isImplementedSemanticOpKind, Tt as VideoDocumentValidationIssueCode, U as resolveEntityTimelineLayout, V as ResolvedEntityTimelineLayout, W as projectEntityTimeline, X as CaptionStyleFields, Y as JsonValue, Z as SequenceDuration, _ as SemanticEditor, _t as Track, a as PlainMemoryAdapter, at as buildSpeechHostMap, b as TransactAudit, bt as VIDEO_DOCUMENT_SCHEMA_VERSION, c as MirrorVideoDocumentAdapter, ct as toVideoDocument, d as createMirrorVideoDocument, dt as DEFAULT_UNIT_TIME_MS, et as EntityId, f as createMirrorVideoDocumentAdapter, ft as ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, g as SemanticDocumentAdapter, gt as Timeline, h as OpActor, ht as SpeechPart, i as JournalEntry, it as SpeechHostMap, j as TrackDraft, jt as CaptionPart$1, k as PartIdFactory, kt as speedOf, l as MirrorVideoDocumentOptions, lt as BgmPart, m as CommitOptions, mt as PartUnion, n as videoDocumentSchema, nt as buildInitialVideoDocument, o as PlainMemoryAdapterOptions, ot as derivePositionFromAbs, p as writeVideoDocumentToDraft, pt as PartKind, q as EntityRow, r as readVideoDocumentFromDraft, rt as DerivedItemPosition, s as createPlainMemoryAdapter, st as fromVideoDocument, t as partUnionSchema, tt as InitialDocumentFacts, u as applyEntityTimelineProjection, ut as CaptionPart, v as SemanticOpInput, vt as TrackItem, w as SemanticOpKind, wt as VideoDocumentValidationIssue, x as IMPLEMENTED_SEMANTIC_OP_KINDS, xt as VideoClipPart, y as SemanticOpName, yt as TrackItemTimePosition, z as EntityTimelineTrackRole } from "./index-WxMNUbOa.js";
2
+ import { $ as VoiceDescriptor, A as generatePartId, At as effectiveVideoClipDurationMs, B as ResolvedEntityClipPlacement, C as PlannedSemanticOpKind, Ct as VideoClipPart, D as SnapshotReadable, Dt as VideoDocumentValidationIssueCode, E as SchemaValidator, Et as VideoDocumentValidationIssue, F as videoDocumentMirrorSchema, Ft as PartAggregation, G as EntityTimelineProjectionError, H as ResolvedEntityTrack, I as VideoDocumentValidationError, It as SpeedShift, J as CaptionSegmentSelection, K as EntityRelationRows, L as assertValidVideoDocument, Lt as Timeline$1, M as TrackItemDraft, Mt as Attachment, N as VideoDocumentDraft, Nt as CaptionPart$1, O as ValidationError, Ot as VideoDraft, P as VideoDocumentMirrorSchema, Pt as CaptionStyle, Q as SequenceRange, R as validateVideoDocument, Rt as Track$1, S as ImplementedSemanticOpKind, St as VIDEO_DOCUMENT_SCHEMA_VERSION, T as isImplementedSemanticOpKind, Tt as VideoDocumentSchemaVersion, U as resolveEntityTimelineLayout, V as ResolvedEntityTimelineLayout, W as projectEntityTimeline, X as ScriptTextSegment, Y as CaptionStyleFields, Z as SequenceDuration, _ as SemanticEditor, _t as SpeechPart, a as PlainMemoryAdapter, at as DerivedItemPosition, b as TransactAudit, bt as TrackItem, c as MirrorVideoDocumentAdapter, ct as derivePositionFromAbs, d as createMirrorVideoDocument, dt as BgmPart, et as JsonObject, f as createMirrorVideoDocumentAdapter, ft as CaptionPart, g as SemanticDocumentAdapter, gt as PartUnion, h as OpActor, ht as PartKind, i as JournalEntry, it as buildInitialVideoDocument, j as TrackDraft, jt as speedOf, k as PartIdFactory, kt as VideoDraftPartUnion, l as MirrorVideoDocumentOptions, lt as fromVideoDocument, m as CommitOptions, mt as ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, n as videoDocumentSchema, nt as EntityId, o as PlainMemoryAdapterOptions, ot as SpeechHostMap, p as writeVideoDocumentToDraft, pt as DEFAULT_UNIT_TIME_MS, q as EntityRow, r as readVideoDocumentFromDraft, rt as InitialDocumentFacts, s as createPlainMemoryAdapter, st as buildSpeechHostMap, t as partUnionSchema, tt as JsonValue, u as applyEntityTimelineProjection, ut as toVideoDocument, v as SemanticOpInput, vt as Timeline, w as SemanticOpKind, wt as VideoDocument, x as IMPLEMENTED_SEMANTIC_OP_KINDS, xt as TrackItemTimePosition, y as SemanticOpName, yt as Track, z as EntityTimelineTrackRole, zt as TrackItem$1 } from "./index-PYtGeXNk.js";
3
3
  import { PeerID } from "loro-crdt";
4
4
  import { DocState } from "@mengine/sync";
5
5
  import { BaseDocStorage, Connection, DocDiff, DocPushReceipt, DocSnapshotRecord, DocStorage, DocStorageOptions, DocUpdate, DocUpdateRecord } from "@mengine/storage";
@@ -178,6 +178,57 @@ interface MengineEventStreamOptions {
178
178
  }
179
179
  declare function readMengineEventStream(options: MengineEventStreamOptions): Promise<void>;
180
180
  //#endregion
181
+ //#region ../medeo-dsl/src/composition.d.ts
182
+ type ScriptCompositionIssueCode = 'composition_missing' | 'composition_ambiguous' | 'composition_dangling' | 'unknown_segment' | 'empty_selection' | 'invalid_script' | 'invalid_selection';
183
+ /**
184
+ * Raised when a variant's complete content cannot be assembled from its
185
+ * required composition Relation. Assembly never falls back to variant-local
186
+ * text: a missing, ambiguous, or dangling source is an error, not an empty
187
+ * read.
188
+ */
189
+ declare class ScriptCompositionError extends Error {
190
+ readonly code: ScriptCompositionIssueCode;
191
+ readonly entityId: EntityId;
192
+ constructor(code: ScriptCompositionIssueCode, entityId: EntityId, message: string);
193
+ }
194
+ interface AssembledCaptionContent {
195
+ readonly caption: EntityRow<'caption'>;
196
+ readonly audioScript: EntityRow<'audio-script'>;
197
+ /** Selected AudioScript segments in Caption selection order. */
198
+ readonly segments: readonly ScriptTextSegment[];
199
+ /** Complete display text: the selected segments' text joined in order. */
200
+ readonly text: string;
201
+ }
202
+ interface AssembledPhoneticScriptContent {
203
+ readonly phoneticScript: EntityRow<'phonetic-script'>;
204
+ readonly audioScript: EntityRow<'audio-script'>;
205
+ /** All AudioScript segments in script order; the pronunciation variant never reorders text. */
206
+ readonly segments: readonly ScriptTextSegment[];
207
+ /** Complete base text owned by the AudioScript. */
208
+ readonly text: string;
209
+ }
210
+ /**
211
+ * Resolve the one AudioScript a variant composes. Relations are queried from
212
+ * both endpoints, so persisted endpoint positions do not matter.
213
+ */
214
+ declare function findComposedAudioScript(rows: EntityRelationRows, variantEntityId: EntityId, variantKind: 'caption' | 'phonetic-script'): EntityRow<'audio-script'>;
215
+ /** Join the whole AudioScript text in segment order. */
216
+ declare function assembleScriptText(script: EntityRow<'audio-script'>): string;
217
+ /**
218
+ * Assemble the complete Caption content from its AudioScript: the selected
219
+ * segments in selection order, joined as display text. The Caption payload
220
+ * never carries text, so a broken composition fails here instead of silently
221
+ * rendering an empty caption.
222
+ */
223
+ declare function assembleCaptionContent(rows: EntityRelationRows, captionEntityId: EntityId): AssembledCaptionContent;
224
+ /**
225
+ * Assemble the complete Phonetic Script content for TTS: the composed
226
+ * AudioScript's base text plus the variant's own phoneme and prosody fields.
227
+ */
228
+ declare function assemblePhoneticScriptContent(rows: EntityRelationRows, phoneticScriptEntityId: EntityId): AssembledPhoneticScriptContent;
229
+ /** Select source text without creating another authoritative text field. */
230
+ declare function selectAudioScriptSegments(script: EntityRow<'audio-script'>, selections: readonly CaptionSegmentSelection[]): readonly ScriptTextSegment[];
231
+ //#endregion
181
232
  //#region src/entity-editor/entity-graph-http-client.d.ts
182
233
  /** One authenticated revision of the authoritative Medeo Entity graph. */
183
234
  interface EntityGraphState {
@@ -288,7 +339,8 @@ interface DeleteClipTreeInput {
288
339
  interface VoiceoverCaptionFact {
289
340
  /** Stable placed caption identity supplied by the materialized side effect. */
290
341
  readonly captionClipEntityId: string;
291
- readonly text: string;
342
+ /** Ordered selection of AudioScript segments; caption text is never passed inline. */
343
+ readonly selections: readonly CaptionSegmentSelection[];
292
344
  readonly startMs: number;
293
345
  readonly durationMs: number;
294
346
  readonly style?: CaptionStyleFields;
@@ -296,8 +348,8 @@ interface VoiceoverCaptionFact {
296
348
  type VoiceoverTakeInput = {
297
349
  readonly timelineEntityId: string; /** Stable placed speech identity, distinct from media.assetId. */
298
350
  readonly voiceoverClipEntityId: string;
299
- readonly media: VoiceMediaAssetFact; /** Complete spoken text; the editor owns the deterministic local script segment identity. */
300
- readonly scriptText: string;
351
+ readonly media: VoiceMediaAssetFact; /** Existing pronunciation variant; its composed AudioScript stays the text owner. */
352
+ readonly phoneticScriptEntityId: string;
301
353
  readonly volume: number;
302
354
  readonly captions: readonly VoiceoverCaptionFact[];
303
355
  } & ({
@@ -373,6 +425,9 @@ interface TrimClipInput {
373
425
  interface VoiceoverTakeResult {
374
426
  readonly voiceoverClipEntityId: string;
375
427
  readonly voiceEntityId: string;
428
+ /** The pronunciation variant the Voice was rendered from. */
429
+ readonly phoneticScriptEntityId: string;
430
+ /** The base-text owner resolved along the phonetic composition Relation. */
376
431
  readonly audioScriptEntityId: string;
377
432
  readonly captionClipEntityIds: readonly string[];
378
433
  }
@@ -405,7 +460,10 @@ interface InsertCaptionClipInput {
405
460
  readonly timelineEntityId: string;
406
461
  /** Stable placed caption identity, distinct from the Caption content identity. */
407
462
  readonly captionClipEntityId?: string;
408
- readonly text: string;
463
+ /** Existing base-text owner; the Caption composes it instead of storing text. */
464
+ readonly audioScriptEntityId: string;
465
+ /** Ordered selection of the AudioScript segments this Caption displays. */
466
+ readonly selections: readonly CaptionSegmentSelection[];
409
467
  /** Intrinsic cue length of the Caption entity itself; display comes from the placement. */
410
468
  readonly durationMs: number;
411
469
  readonly style?: CaptionStyleFields;
@@ -490,18 +548,26 @@ declare class EntityTimelineEditor {
490
548
  setCaptionVisibility(input: SetCaptionVisibilityInput): void;
491
549
  patchCaptionStyle(input: PatchCaptionStyleInput): void;
492
550
  /**
493
- * Place an independent Caption: its own content entity, inline text Asset,
494
- * and Clip with a display Marker. No Voice, provenance, or alignment is
495
- * required — the placement describes only this use. Moving or stretching
496
- * the returned Clip later changes the Clip/Marker, never the Caption entity.
551
+ * Place a Caption as a composed variant of an existing AudioScript: the
552
+ * entity stores only its selection, style, and inherent cue-bounded
553
+ * Sequence — never text. The Clip and its display Marker describe this use;
554
+ * moving or stretching the returned Clip later changes the Clip/Marker,
555
+ * never the Caption entity or the AudioScript.
497
556
  */
498
557
  insertCaptionClip(input: InsertCaptionClipInput): ClipEntityId;
499
558
  private insertPlacedClipIntoDraft;
500
559
  private ensureRoleTrack;
501
- private ensureAudioScript;
560
+ /** The existing pronunciation variant a Voice consumes; composition is required. */
561
+ private requireComposedPhoneticScript;
562
+ /** Preserve the factual synthesis input, regardless of persisted endpoint positions. */
563
+ private ensurePhoneticRender;
502
564
  private reconcileVoiceoverCaptions;
503
565
  private ensureRoleTrackForClip;
504
566
  private replaceCaptionSemanticRelations;
567
+ /**
568
+ * Remove one placed Caption use. The composed AudioScript outlives the
569
+ * caption: it may anchor further captions and remains the text owner.
570
+ */
505
571
  private removeCaptionClipGraph;
506
572
  private replaceAnchorHost;
507
573
  /** Rebuild the legacy edit baseline from current visual order before a high-level cascade. */
@@ -538,8 +604,7 @@ interface EditorFoundation {
538
604
  * Image/Video/Audio/Voice Entity must carry its own external locator, every
539
605
  * external asset id has exactly one claimant, and no media variant may be
540
606
  * bound through a legacy `physical-asset` Relation. Dual-entity media graphs
541
- * are rejected, not migrated. Caption inline text keeps its inline Asset and
542
- * `physical-asset` binding.
607
+ * are rejected, not migrated. Caption composes AudioScript text and has no physical text Asset.
543
608
  */
544
609
  declare function assertCanonicalEditorResources(rows: EntityRelationRows): void;
545
610
  /**
@@ -550,8 +615,7 @@ declare function assertCanonicalEditorResources(rows: EntityRelationRows): void;
550
615
  *
551
616
  * 1. every Image/Video/Audio/Voice Entity carries its own Asset `external`
552
617
  * identity;
553
- * 2. no `physical-asset` Relation may bind a media variant (Caption inline
554
- * text keeps its inline Asset and binding);
618
+ * 2. no `physical-asset` Relation may bind a media variant or Caption;
555
619
  * 3. a self-carried media identity is immutable — it cannot be rewritten to
556
620
  * another asset id.
557
621
  *
@@ -1491,4 +1555,4 @@ declare function hostForAbsMs(ranges: MainClipRange[], absMs: number): MainClipR
1491
1555
  */
1492
1556
  declare function relativePositionForAbs(ranges: MainClipRange[], absMs: number): TrackItemTimePosition;
1493
1557
  //#endregion
1494
- export { type Aggregation, type Attachment, type AudioMediaAssetFact, type BgmPart, type CaptionPart, type CaptionStyle, type ClipEntityId, type ClipPlacement, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DeleteBgmInput, type DeleteClipInput, type DeleteClipTreeInput, type DeleteVoiceoverInput, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, type EditorFoundation, type EntityGraphCommitOptions, EntityGraphHttpClient, type EntityGraphState, EntityTimelineEditor, type EntityTimelineIdFactory, EntityTimelineProjectionError, type EntityTimelineTrackRole, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImageMediaAssetFact, type ImplementedSemanticOpKind, type ImportedMediaAsset, type InitialDocumentFacts, type InsertCaptionClipInput, type InsertClipInput, type InsertMediaClipInput, type InsertMediaClipsInput, type InsertPlacedClipInput, type JournalEntry, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type LinearClipSpeed, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, type MediaAssetFact, type MediaClipInsertion, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type MoveClipInput, type MoveClipsToStartsInput, type MoveSequentialClipsInput, type MoveVoiceoverInput, type OpActor, type PartAggregation, type PartIdFactory, type PartKind, type PartUnion, type PatchCaptionStyleInput, PlainMemoryAdapter, type PlainMemoryAdapterOptions, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, type ReplaceClipContentInput, type ReplaceMediaClipInput, type ReplaceSequentialClipsInput, type ReplacementMediaClipInput, type ResolvedEntityClipPlacement, type ResolvedEntityTimelineLayout, type ResolvedEntityTrack, SchemaValidator, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SequentialClipAnchor, type SetBgmInput, type SetCaptionVisibilityInput, type SetClipPlacementInput, type SetClipSpeedInput, type SetClipVolumeInput, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, type TrimClipInput, type UpdateClipInput, type UpdateClipMarkerInput, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, type VideoDocumentSchemaVersion, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type VideoMediaAssetFact, type VisualMediaAssetFact, type VoiceMediaAssetFact, type VoiceoverCaptionFact, type VoiceoverTakeInput, type VoiceoverTakeResult, type WaitForServerAckOptions, applyEntityTimelineProjection, arrangeMainTrackSeamlessly, assertCanonicalEditorResources, assertEntityProjectionPreservesLegacy, assertMediaAssetWritePolicy, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createEditSandbox, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, createPlainMemoryAdapter, decodeDocVersionMark, derivePositionFromAbs, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, importMediaAsset, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, migrateLegacyTimelineToEntities, partDurationMs, partUnionSchema, projectEntityTimeline, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, replayJournal, resolveAllSpeechOverlaps, resolveEntityTimelineLayout, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, snapshotToPlain, solveVideoDocument, speedOf, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema, writeVideoDocumentToDraft };
1558
+ export { type Aggregation, type AssembledCaptionContent, type AssembledPhoneticScriptContent, type Attachment, type AudioMediaAssetFact, type BgmPart, type CaptionPart, type CaptionSegmentSelection, type CaptionStyle, type ClipEntityId, type ClipPlacement, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DeleteBgmInput, type DeleteClipInput, type DeleteClipTreeInput, type DeleteVoiceoverInput, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, type EditorFoundation, type EntityGraphCommitOptions, EntityGraphHttpClient, type EntityGraphState, EntityTimelineEditor, type EntityTimelineIdFactory, EntityTimelineProjectionError, type EntityTimelineTrackRole, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImageMediaAssetFact, type ImplementedSemanticOpKind, type ImportedMediaAsset, type InitialDocumentFacts, type InsertCaptionClipInput, type InsertClipInput, type InsertMediaClipInput, type InsertMediaClipsInput, type InsertPlacedClipInput, type JournalEntry, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type LinearClipSpeed, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, type MediaAssetFact, type MediaClipInsertion, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type MoveClipInput, type MoveClipsToStartsInput, type MoveSequentialClipsInput, type MoveVoiceoverInput, type OpActor, type PartAggregation, type PartIdFactory, type PartKind, type PartUnion, type PatchCaptionStyleInput, PlainMemoryAdapter, type PlainMemoryAdapterOptions, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, type ReplaceClipContentInput, type ReplaceMediaClipInput, type ReplaceSequentialClipsInput, type ReplacementMediaClipInput, type ResolvedEntityClipPlacement, type ResolvedEntityTimelineLayout, type ResolvedEntityTrack, SchemaValidator, ScriptCompositionError, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SequentialClipAnchor, type SetBgmInput, type SetCaptionVisibilityInput, type SetClipPlacementInput, type SetClipSpeedInput, type SetClipVolumeInput, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, type TrimClipInput, type UpdateClipInput, type UpdateClipMarkerInput, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, type VideoDocumentSchemaVersion, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type VideoMediaAssetFact, type VisualMediaAssetFact, type VoiceMediaAssetFact, type VoiceoverCaptionFact, type VoiceoverTakeInput, type VoiceoverTakeResult, type WaitForServerAckOptions, applyEntityTimelineProjection, arrangeMainTrackSeamlessly, assembleCaptionContent, assemblePhoneticScriptContent, assembleScriptText, assertCanonicalEditorResources, assertEntityProjectionPreservesLegacy, assertMediaAssetWritePolicy, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createEditSandbox, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, createPlainMemoryAdapter, decodeDocVersionMark, derivePositionFromAbs, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, ensureLaneTrack, fillMainTrackTimeGaps, findComposedAudioScript, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, importMediaAsset, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, migrateLegacyTimelineToEntities, partDurationMs, partUnionSchema, projectEntityTimeline, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, replayJournal, resolveAllSpeechOverlaps, resolveEntityTimelineLayout, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, selectAudioScriptSegments, snapshotToPlain, solveVideoDocument, speedOf, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema, writeVideoDocumentToDraft };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as videoDocumentMirrorSchema, A as ensureLaneTrack, B as resolveSpeechOverlapByShiftingVideos, C as toVideoDocument, D as partUnionSchema, E as validateVideoDocument, F as arrangeMainTrackSeamlessly, G as safeDurationMs, H as TIMELINE_SKELETON_DURATION_MS, I as fillMainTrackTimeGaps, J as VIDEO_DOCUMENT_SCHEMA_VERSION, K as DEFAULT_UNIT_TIME_MS, L as reassignSpeechesToVideoClipsByTime, M as laneTrackId, N as solveVideoDocument, O as videoDocumentSchema, P as cascadeAfterVideoClipChanges, Q as recordEntries, R as recalculateTimelineDuration, S as fromVideoDocument, T as assertValidVideoDocument, U as isEmptyVideoClip, V as syncAggregatedClipsTimePosition, W as partDurationMs, X as speedOf, Y as effectiveVideoClipDurationMs, Z as partUnionToDraft, _ as hasSequence, a as applyEntityTimelineProjection, b as buildSpeechHostMap, c as writeVideoDocumentToDraft, d as resolveEntityTimelineLayout, et as base64ToBytes, f as EntityTimelineProjectionError, g as createRelationId, h as createEntityId, i as MirrorVideoDocumentAdapter, j as findLaneTrack, k as LANE_KINDS_IN_STACK_ORDER, l as readVideoDocumentFromDraft, m as isJsonObject, n as createPlainMemoryAdapter, o as createMirrorVideoDocument, p as decodeEntityRelationRows, q as ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, r as generatePartId, s as createMirrorVideoDocumentAdapter, t as PlainMemoryAdapter, tt as bytesToBase64, u as projectEntityTimeline, v as isMediaAssetVariantKind, w as VideoDocumentValidationError, x as derivePositionFromAbs, y as buildInitialVideoDocument, z as resolveAllSpeechOverlaps } from "./document-Caidonf8.js";
1
+ import { $ as ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, A as VideoDocumentValidationError, B as cascadeAfterVideoClipChanges, C as hasSequence, D as derivePositionFromAbs, E as buildSpeechHostMap, F as LANE_KINDS_IN_STACK_ORDER, G as resolveAllSpeechOverlaps, H as fillMainTrackTimeGaps, I as ensureLaneTrack, J as TIMELINE_SKELETON_DURATION_MS, K as resolveSpeechOverlapByShiftingVideos, L as findLaneTrack, M as validateVideoDocument, N as partUnionSchema, O as fromVideoDocument, P as videoDocumentSchema, Q as DEFAULT_UNIT_TIME_MS, R as laneTrackId, S as selectAudioScriptSegments, T as buildInitialVideoDocument, U as reassignSpeechesToVideoClipsByTime, V as arrangeMainTrackSeamlessly, W as recalculateTimelineDuration, X as partDurationMs, Y as isEmptyVideoClip, Z as safeDurationMs, _ as ScriptCompositionError, a as applyEntityTimelineProjection, at as videoDocumentMirrorSchema, b as assembleScriptText, c as writeVideoDocumentToDraft, d as resolveEntityTimelineLayout, et as VIDEO_DOCUMENT_SCHEMA_VERSION, f as EntityTimelineProjectionError, g as createRelationId, h as createEntityId, i as MirrorVideoDocumentAdapter, it as recordEntries, j as assertValidVideoDocument, k as toVideoDocument, l as readVideoDocumentFromDraft, m as isJsonObject, n as createPlainMemoryAdapter, nt as speedOf, o as createMirrorVideoDocument, ot as base64ToBytes, p as decodeEntityRelationRows, q as syncAggregatedClipsTimePosition, r as generatePartId, rt as partUnionToDraft, s as createMirrorVideoDocumentAdapter, st as bytesToBase64, t as PlainMemoryAdapter, tt as effectiveVideoClipDurationMs, u as projectEntityTimeline, v as assembleCaptionContent, w as isMediaAssetVariantKind, x as findComposedAudioScript, y as assemblePhoneticScriptContent, z as solveVideoDocument } from "./document-Bf200CmD.js";
2
2
  import { addSpeechesInputSchema, addVideoClipsInputSchema, adjustBgmVolumeInputSchema, adjustSpeechVolumeInputSchema, adjustVideoClipDurationInputSchema, adjustVideoClipVolumeInputSchema, changeSpeechScriptInputSchema, changeSpeechVoiceInputSchema, deleteBgmInputSchema, deleteSpeechesInputSchema, deleteVideoClipsInputSchema, moveSpeechesInputSchema, moveVideoClipsByAnchorInputSchema, moveVideoClipsInputSchema, replaceVideoClipContentInputSchema, replaceVideoClipSequenceInputSchema, setBgmInputSchema, setCaptionStyleInputSchema, setCaptionVisibilityInputSchema, setVideoClipSpeedShiftInputSchema, t as schemas_exports } from "./schemas.js";
3
3
  import { LoroDoc, VersionVector } from "loro-crdt";
4
4
  import { ClientServerSynchronizer, DocManager } from "@mengine/sync";
@@ -231,8 +231,7 @@ const roleOrder$1 = {
231
231
  * Image/Video/Audio/Voice Entity must carry its own external locator, every
232
232
  * external asset id has exactly one claimant, and no media variant may be
233
233
  * bound through a legacy `physical-asset` Relation. Dual-entity media graphs
234
- * are rejected, not migrated. Caption inline text keeps its inline Asset and
235
- * `physical-asset` binding.
234
+ * are rejected, not migrated. Caption composes AudioScript text and has no physical text Asset.
236
235
  */
237
236
  function assertCanonicalEditorResources(rows) {
238
237
  if (rows.entities.filter((row) => row.entityKind === "timeline").length > 1) throw new EntityTimelineProjectionError("Editor requires exactly one Timeline");
@@ -263,8 +262,7 @@ function assertCanonicalEditorResources(rows) {
263
262
  *
264
263
  * 1. every Image/Video/Audio/Voice Entity carries its own Asset `external`
265
264
  * identity;
266
- * 2. no `physical-asset` Relation may bind a media variant (Caption inline
267
- * text keeps its inline Asset and binding);
265
+ * 2. no `physical-asset` Relation may bind a media variant or Caption;
268
266
  * 3. a self-carried media identity is immutable — it cannot be rewritten to
269
267
  * another asset id.
270
268
  *
@@ -608,6 +606,19 @@ function sameVoice(left, right) {
608
606
  }
609
607
  //#endregion
610
608
  //#region src/entity-editor/entity-timeline-editor.ts
609
+ /**
610
+ * Resolve the AudioScript a PhoneticScript composes. The composition is the
611
+ * required text source: a variant without it has no content and fails here.
612
+ */
613
+ function requireComposedAudioScript(rows, phoneticScriptEntityId) {
614
+ return assemblePhoneticScriptContent(rows, phoneticScriptEntityId).audioScript.entityId;
615
+ }
616
+ function captionSelections(selections) {
617
+ return structuredClone(selections);
618
+ }
619
+ function assertSelectionNamesScriptSegments(rows, audioScriptEntityId, selections) {
620
+ selectAudioScriptSegments(requireEntityKind(rows, audioScriptEntityId, "audio-script"), selections);
621
+ }
611
622
  const roleOrder = {
612
623
  caption: 0,
613
624
  video_clip: 1,
@@ -1129,7 +1140,8 @@ var EntityTimelineEditor = class {
1129
1140
  const imported = importMediaAsset(draft, input.media, this.idFactory);
1130
1141
  replaceRows(draft, imported.rows);
1131
1142
  const voice = requireEntityKind(draft, imported.contentEntityId, "voice");
1132
- const script = this.ensureAudioScript(draft, voice.entityId, input.scriptText);
1143
+ const phonetic = this.requireComposedPhoneticScript(draft, input.phoneticScriptEntityId);
1144
+ this.ensurePhoneticRender(draft, voice.entityId, phonetic.entityId);
1133
1145
  const speechTrack = this.ensureRoleTrack(draft, input.timelineEntityId, "speech");
1134
1146
  const existing = draft.entities.find((row) => row.entityId === input.voiceoverClipEntityId);
1135
1147
  let voiceoverClipEntityId;
@@ -1179,12 +1191,13 @@ var EntityTimelineEditor = class {
1179
1191
  }
1180
1192
  });
1181
1193
  }
1182
- this.reconcileVoiceoverCaptions(draft, voiceoverClipEntityId, voice.entityId, script.entityId, input.captions);
1194
+ this.reconcileVoiceoverCaptions(draft, voiceoverClipEntityId, voice.entityId, phonetic.entityId, input.captions);
1183
1195
  this.resolveOneVoiceoverOverlap(draft);
1184
1196
  return {
1185
1197
  voiceoverClipEntityId,
1186
1198
  voiceEntityId: voice.entityId,
1187
- audioScriptEntityId: script.entityId,
1199
+ phoneticScriptEntityId: phonetic.entityId,
1200
+ audioScriptEntityId: requireComposedAudioScript(draft, phonetic.entityId),
1188
1201
  captionClipEntityIds: input.captions.map((caption) => caption.captionClipEntityId)
1189
1202
  };
1190
1203
  });
@@ -1287,14 +1300,18 @@ var EntityTimelineEditor = class {
1287
1300
  });
1288
1301
  }
1289
1302
  /**
1290
- * Place an independent Caption: its own content entity, inline text Asset,
1291
- * and Clip with a display Marker. No Voice, provenance, or alignment is
1292
- * required — the placement describes only this use. Moving or stretching
1293
- * the returned Clip later changes the Clip/Marker, never the Caption entity.
1303
+ * Place a Caption as a composed variant of an existing AudioScript: the
1304
+ * entity stores only its selection, style, and inherent cue-bounded
1305
+ * Sequence — never text. The Clip and its display Marker describe this use;
1306
+ * moving or stretching the returned Clip later changes the Clip/Marker,
1307
+ * never the Caption entity or the AudioScript.
1294
1308
  */
1295
1309
  insertCaptionClip(input) {
1296
1310
  return this.transact((draft) => {
1297
1311
  const durationMs = finitePositive(input.durationMs, "Caption durationMs");
1312
+ const selections = captionSelections(input.selections);
1313
+ const script = requireEntityKind(draft, input.audioScriptEntityId, "audio-script");
1314
+ assertSelectionNamesScriptSegments(draft, script.entityId, selections);
1298
1315
  const track = this.ensureRoleTrack(draft, input.timelineEntityId, "caption");
1299
1316
  const content = {
1300
1317
  entityId: this.mintEntityId(draft),
@@ -1307,20 +1324,12 @@ var EntityTimelineEditor = class {
1307
1324
  },
1308
1325
  sampling: "native",
1309
1326
  coordinateSpace: "milliseconds",
1310
- text: input.text,
1327
+ selections,
1311
1328
  ...input.style === void 0 ? {} : { style: cloneJsonObject(input.style, "Caption style") }
1312
1329
  }
1313
1330
  };
1314
- const asset = {
1315
- entityId: this.mintEntityId(draft),
1316
- entityKind: "asset",
1317
- payload: { inline: {
1318
- mediaType: "text/plain",
1319
- text: input.text
1320
- } }
1321
- };
1322
- draft.entities.push(content, asset);
1323
- draft.relations.push(this.createRelation(draft, "physical-asset", content.entityId, asset.entityId));
1331
+ draft.entities.push(content);
1332
+ draft.relations.push(this.createRelation(draft, "caption-composition", content.entityId, script.entityId, { segmentAlignment: [] }));
1324
1333
  return this.insertPlacedClipIntoDraft(draft, {
1325
1334
  trackEntityId: track.entityId,
1326
1335
  contentEntityId: content.entityId,
@@ -1398,34 +1407,24 @@ var EntityTimelineEditor = class {
1398
1407
  draft.relations.push(this.createRelation(draft, "timeline-track", timeline.entityId, track.entityId));
1399
1408
  return track;
1400
1409
  }
1401
- ensureAudioScript(draft, voiceEntityId, text) {
1402
- const renders = draft.relations.filter((relation) => relation.relationKind === "audio-script-render" && relation.endpoint0EntityId === voiceEntityId);
1403
- if (renders.length > 1) throw new Error(`Voice "${voiceEntityId}" has multiple AudioScript relations`);
1410
+ /** The existing pronunciation variant a Voice consumes; composition is required. */
1411
+ requireComposedPhoneticScript(draft, phoneticScriptEntityId) {
1412
+ const phonetic = requireEntityKind(draft, phoneticScriptEntityId, "phonetic-script");
1413
+ requireComposedAudioScript(draft, phonetic.entityId);
1414
+ return phonetic;
1415
+ }
1416
+ /** Preserve the factual synthesis input, regardless of persisted endpoint positions. */
1417
+ ensurePhoneticRender(draft, voiceEntityId, phoneticScriptEntityId) {
1418
+ const renders = draft.relations.filter((relation) => relation.relationKind === "phonetic-script-render" && (relation.endpoint0EntityId === voiceEntityId || relation.endpoint1EntityId === voiceEntityId));
1419
+ if (renders.length > 1) throw new Error(`Voice "${voiceEntityId}" has multiple PhoneticScript render relations`);
1404
1420
  if (renders[0] !== void 0) {
1405
- const script = requireEntityKind(draft, renders[0].endpoint1EntityId, "audio-script");
1406
- const replacement = {
1407
- ...script,
1408
- payload: { segments: [{
1409
- segmentId: "segment-0",
1410
- text
1411
- }] }
1412
- };
1413
- replaceEntity(draft, script.entityId, replacement);
1414
- return replacement;
1421
+ if (otherEndpoint(renders[0], voiceEntityId) === phoneticScriptEntityId) return;
1422
+ throw new Error(`Voice "${voiceEntityId}" already has a different PhoneticScript synthesis input`);
1415
1423
  }
1416
- const script = {
1417
- entityId: this.mintEntityId(draft),
1418
- entityKind: "audio-script",
1419
- payload: { segments: [{
1420
- segmentId: "segment-0",
1421
- text
1422
- }] }
1423
- };
1424
- draft.entities.push(script);
1425
- draft.relations.push(this.createRelation(draft, "audio-script-render", voiceEntityId, script.entityId));
1426
- return script;
1424
+ draft.relations.push(this.createRelation(draft, "phonetic-script-render", voiceEntityId, phoneticScriptEntityId));
1427
1425
  }
1428
- reconcileVoiceoverCaptions(draft, voiceoverClipEntityId, voiceEntityId, scriptEntityId, captions) {
1426
+ reconcileVoiceoverCaptions(draft, voiceoverClipEntityId, voiceEntityId, phoneticScriptEntityId, captions) {
1427
+ const audioScriptEntityId = requireComposedAudioScript(draft, phoneticScriptEntityId);
1429
1428
  const requested = new Set(captions.map((caption) => caption.captionClipEntityId));
1430
1429
  if (requested.size !== captions.length) throw new Error("Caption Clip ids must be unique within a voiceover take");
1431
1430
  const existing = directAnchorChildren(draft, voiceoverClipEntityId).filter((clipId) => clipRole(draft, clipId) === "caption");
@@ -1434,6 +1433,8 @@ var EntityTimelineEditor = class {
1434
1433
  for (const caption of captions) {
1435
1434
  const startMs = finiteNonNegative(caption.startMs, "Caption startMs");
1436
1435
  const durationMs = finitePositive(caption.durationMs, "Caption durationMs");
1436
+ const selections = captionSelections(caption.selections);
1437
+ assertSelectionNamesScriptSegments(draft, audioScriptEntityId, selections);
1437
1438
  if (draft.entities.find((entity) => entity.entityId === caption.captionClipEntityId) === void 0) {
1438
1439
  const content = {
1439
1440
  entityId: this.mintEntityId(draft),
@@ -1446,23 +1447,15 @@ var EntityTimelineEditor = class {
1446
1447
  },
1447
1448
  sampling: "native",
1448
1449
  coordinateSpace: "milliseconds",
1449
- text: caption.text,
1450
+ selections,
1450
1451
  ...caption.style === void 0 ? {} : { style: cloneJsonObject(caption.style, "Caption style") }
1451
1452
  }
1452
1453
  };
1453
- const asset = {
1454
- entityId: this.mintEntityId(draft),
1455
- entityKind: "asset",
1456
- payload: { inline: {
1457
- mediaType: "text/plain",
1458
- text: caption.text
1459
- } }
1460
- };
1461
- draft.entities.push(content, asset);
1462
- draft.relations.push(this.createRelation(draft, "physical-asset", content.entityId, asset.entityId), this.createRelation(draft, "caption-alignment", content.entityId, voiceEntityId, { alignment: {
1454
+ draft.entities.push(content);
1455
+ draft.relations.push(this.createRelation(draft, "caption-composition", content.entityId, audioScriptEntityId, { segmentAlignment: [] }), this.createRelation(draft, "caption-alignment", content.entityId, voiceEntityId, { alignment: {
1463
1456
  start: startMs,
1464
1457
  end: startMs + durationMs
1465
- } }), this.createRelation(draft, "caption-provenance", content.entityId, scriptEntityId, { segmentAlignment: [] }));
1458
+ } }));
1466
1459
  this.insertPlacedClipIntoDraft(draft, {
1467
1460
  clipEntityId: caption.captionClipEntityId,
1468
1461
  trackEntityId: captionTrack.entityId,
@@ -1494,27 +1487,10 @@ var EntityTimelineEditor = class {
1494
1487
  ...content,
1495
1488
  payload: {
1496
1489
  ...content.payload,
1497
- extent: {
1498
- kind: "bounded",
1499
- start: 0,
1500
- end: durationMs
1501
- },
1502
- text: caption.text,
1490
+ selections,
1503
1491
  ...Object.keys(style).length === 0 ? {} : { style }
1504
1492
  }
1505
1493
  });
1506
- const asset = requireEntityKind(draft, otherEndpoint(requireOneRelation(draft, content.entityId, "physical-asset"), content.entityId), "asset");
1507
- if (asset.payload.inline === void 0) throw new Error(`Caption "${caption.captionClipEntityId}" is not inline`);
1508
- replaceEntity(draft, asset.entityId, {
1509
- ...asset,
1510
- payload: {
1511
- ...asset.payload,
1512
- inline: {
1513
- mediaType: "text/plain",
1514
- text: caption.text
1515
- }
1516
- }
1517
- });
1518
1494
  this.applyClipPlacement(draft, topology.clip.entityId, {
1519
1495
  kind: "anchored",
1520
1496
  hostClipEntityId: voiceoverClipEntityId,
@@ -1525,17 +1501,13 @@ var EntityTimelineEditor = class {
1525
1501
  ...placed.marker,
1526
1502
  payload: {
1527
1503
  ...placed.marker.payload,
1528
- sourceRange: {
1529
- start: 0,
1530
- end: durationMs
1531
- },
1532
1504
  duration: {
1533
1505
  mode: "fixed",
1534
1506
  value: durationMs
1535
1507
  }
1536
1508
  }
1537
1509
  });
1538
- this.replaceCaptionSemanticRelations(draft, content.entityId, voiceEntityId, scriptEntityId, startMs, durationMs);
1510
+ this.replaceCaptionSemanticRelations(draft, content.entityId, voiceEntityId, audioScriptEntityId, startMs, durationMs);
1539
1511
  }
1540
1512
  }
1541
1513
  ensureRoleTrackForClip(draft, clipEntityId, role) {
@@ -1545,22 +1517,24 @@ var EntityTimelineEditor = class {
1545
1517
  return this.ensureRoleTrack(draft, timeline.entityId, role);
1546
1518
  }
1547
1519
  replaceCaptionSemanticRelations(draft, captionEntityId, voiceEntityId, scriptEntityId, startMs, durationMs) {
1548
- const removed = new Set(incidentRelations(draft, captionEntityId).filter((relation) => relation.relationKind === "caption-alignment" || relation.relationKind === "caption-provenance").map((relation) => relation.relationId));
1520
+ const removed = new Set(incidentRelations(draft, captionEntityId).filter((relation) => relation.relationKind === "caption-alignment" || relation.relationKind === "caption-composition").map((relation) => relation.relationId));
1549
1521
  draft.relations = draft.relations.filter((relation) => !removed.has(relation.relationId));
1550
1522
  draft.relations.push(this.createRelation(draft, "caption-alignment", captionEntityId, voiceEntityId, { alignment: {
1551
1523
  start: startMs,
1552
1524
  end: startMs + durationMs
1553
- } }), this.createRelation(draft, "caption-provenance", captionEntityId, scriptEntityId, { segmentAlignment: [] }));
1525
+ } }), this.createRelation(draft, "caption-composition", captionEntityId, scriptEntityId, { segmentAlignment: [] }));
1554
1526
  }
1527
+ /**
1528
+ * Remove one placed Caption use. The composed AudioScript outlives the
1529
+ * caption: it may anchor further captions and remains the text owner.
1530
+ */
1555
1531
  removeCaptionClipGraph(draft, clipEntityId) {
1556
1532
  const topology = requireClipOnRole(draft, clipEntityId, "caption");
1557
1533
  const caption = requireEntityKind(draft, otherEndpoint(topology.markerContentRelation, topology.marker.entityId), "caption");
1558
- const asset = requireEntityKind(draft, otherEndpoint(requireOneRelation(draft, caption.entityId, "physical-asset"), caption.entityId), "asset");
1559
1534
  const contentRelations = incidentRelations(draft, caption.entityId);
1560
1535
  const allowed = new Set([
1561
- "physical-asset",
1536
+ "caption-composition",
1562
1537
  "caption-alignment",
1563
- "caption-provenance",
1564
1538
  "marker-content"
1565
1539
  ]);
1566
1540
  const unsupported = contentRelations.filter((relation) => !allowed.has(relation.relationKind));
@@ -1568,10 +1542,8 @@ var EntityTimelineEditor = class {
1568
1542
  this.removeClipGraph(draft, clipEntityId);
1569
1543
  if (incidentRelations(draft, caption.entityId).some((relation) => relation.relationKind === "marker-content")) return;
1570
1544
  const removed = new Set(incidentRelations(draft, caption.entityId).map((relation) => relation.relationId));
1571
- const assetIncidents = incidentRelations(draft, asset.entityId);
1572
- const deleteAsset = asset.payload.inline !== void 0 && assetIncidents.every((relation) => removed.has(relation.relationId));
1573
1545
  draft.relations = draft.relations.filter((relation) => !removed.has(relation.relationId));
1574
- draft.entities = draft.entities.filter((entity) => entity.entityId !== caption.entityId && (!deleteAsset || entity.entityId !== asset.entityId));
1546
+ draft.entities = draft.entities.filter((entity) => entity.entityId !== caption.entityId);
1575
1547
  }
1576
1548
  replaceAnchorHost(draft, childClipEntityId, hostClipEntityId) {
1577
1549
  const anchors = draft.relations.filter((relation) => relation.relationKind === "clip-anchor" && relation.endpoint0EntityId === childClipEntityId);
@@ -1992,7 +1964,6 @@ function migrateLegacyTimelineToEntities(legacy, assetFacts, initial = {
1992
1964
  facts.set(fact.assetId, fact);
1993
1965
  }
1994
1966
  const contentByPartId = /* @__PURE__ */ new Map();
1995
- const scriptBySpeechClipId = /* @__PURE__ */ new Map();
1996
1967
  for (const track of legacy.tracks ?? []) for (const item of track.items ?? []) {
1997
1968
  const part = requirePart(legacy, item.part_id);
1998
1969
  if (part.video_clip !== void 0) {
@@ -2026,7 +1997,8 @@ function migrateLegacyTimelineToEntities(legacy, assetFacts, initial = {
2026
1997
  entities = [...imported.rows.entities];
2027
1998
  relations = [...imported.rows.relations];
2028
1999
  contentByPartId.set(item.part_id, imported.contentEntityId);
2029
- scriptBySpeechClipId.set(item.part_id, ensureAudioScript(imported.contentEntityId, speech.audio_script, entities, relations, addEntity, link));
2000
+ const phonemeScript = speech.phoneme_script;
2001
+ ensureSpeechPhoneticChain(imported.contentEntityId, speech.audio_script, typeof phonemeScript === "string" ? phonemeScript : void 0, entities, relations, addEntity, link);
2030
2002
  } else if (part.bgm !== void 0) {
2031
2003
  const bgm = part.bgm;
2032
2004
  const fact = facts.get(bgm.origin_media_id);
@@ -2101,6 +2073,22 @@ function migrateLegacyTimelineToEntities(legacy, assetFacts, initial = {
2101
2073
  } else if (part.caption !== void 0) {
2102
2074
  const caption = part.caption;
2103
2075
  duration = caption.initial_duration_ms;
2076
+ const voice = contentByPartId.get(caption.speech_part_id);
2077
+ const render = relations.find((relation) => relation.relationKind === "phonetic-script-render" && relation.endpoint0EntityId === voice);
2078
+ const phonetic = entities.find((entity) => entity.entityId === render?.endpoint1EntityId);
2079
+ const script = phonetic === void 0 ? void 0 : composedAudioScript(phonetic, entities, relations);
2080
+ if (script === void 0) fail("Legacy Caption has no composed AudioScript");
2081
+ const sourceText = scriptText(script);
2082
+ const captionText = caption.text;
2083
+ const offset = sourceText.indexOf(captionText);
2084
+ if (!captionText.trim() || offset < 0 || sourceText.indexOf(captionText, offset + 1) >= 0) fail("Legacy Caption needs explicit unambiguous AudioScript selections before cutover");
2085
+ const selections = [{
2086
+ segmentId: "segment-0",
2087
+ ...sourceText === captionText ? {} : { textRange: {
2088
+ start: Array.from(sourceText.slice(0, offset)).length,
2089
+ end: Array.from(sourceText.slice(0, offset + captionText.length)).length
2090
+ } }
2091
+ }];
2104
2092
  content = addEntity("caption", {
2105
2093
  extent: {
2106
2094
  kind: "bounded",
@@ -2109,21 +2097,14 @@ function migrateLegacyTimelineToEntities(legacy, assetFacts, initial = {
2109
2097
  },
2110
2098
  sampling: "native",
2111
2099
  coordinateSpace: "milliseconds",
2112
- text: caption.text,
2100
+ selections,
2113
2101
  ...caption.style === void 0 ? {} : { style: toEntityCaptionStyle(caption.style) }
2114
2102
  });
2115
- const asset = addEntity("asset", { inline: {
2116
- mediaType: "text/plain",
2117
- text: caption.text
2118
- } });
2119
- link("physical-asset", content, asset);
2120
- const voice = contentByPartId.get(caption.speech_part_id);
2121
- const script = scriptBySpeechClipId.get(caption.speech_part_id);
2103
+ link("caption-composition", content, script.entityId, { segmentAlignment: [] });
2122
2104
  link("caption-alignment", content, voice, { alignment: {
2123
2105
  start: caption.start_ms,
2124
2106
  end: caption.start_ms + duration
2125
2107
  } });
2126
- link("caption-provenance", content, script, { segmentAlignment: [] });
2127
2108
  markerPayload = {
2128
2109
  sourceRange: {
2129
2110
  start: 0,
@@ -2339,7 +2320,7 @@ function assertPartSupported(id, part, role) {
2339
2320
  if (speech.voice?.name !== void 0 && typeof speech.voice.name !== "string") fail("Migration requires a string voice name when present");
2340
2321
  if (!Array.isArray(speech.caption_ids) || speech.caption_ids.some((captionId) => !trimmed(captionId))) fail("Migration requires explicit Speech caption identities");
2341
2322
  if (new Set(speech.caption_ids).size !== speech.caption_ids.length) fail("Migration requires unique Speech caption identities");
2342
- if (phonemeScript !== void 0 && phonemeScript !== null) fail("Non-null phoneme_script requires a phonetic-script migration adapter");
2323
+ if (phonemeScript !== void 0 && phonemeScript !== null && typeof phonemeScript !== "string") fail("Migration requires a string phoneme_script when present");
2343
2324
  assertKnownFields(speech.voice, ["id", "name"], "voice");
2344
2325
  assertKnownFields(speech, [
2345
2326
  "id",
@@ -2463,20 +2444,31 @@ function requirePart(document, partId) {
2463
2444
  if (part === void 0) fail(`Placed Clip ${partId} has no part payload`);
2464
2445
  return part;
2465
2446
  }
2466
- function ensureAudioScript(voiceEntityId, text, entities, relations, addEntity, link) {
2467
- const renders = relations.filter((relation) => relation.relationKind === "audio-script-render" && relation.endpoint0EntityId === voiceEntityId);
2468
- if (renders.length > 1) fail(`Voice ${voiceEntityId} has ambiguous AudioScript provenance`);
2447
+ function ensureSpeechPhoneticChain(voiceEntityId, audioScriptText, phonemeScript, entities, relations, addEntity, link) {
2448
+ const renders = relations.filter((relation) => relation.relationKind === "phonetic-script-render" && relation.endpoint0EntityId === voiceEntityId);
2449
+ if (renders.length > 1) fail(`Voice ${voiceEntityId} has ambiguous PhoneticScript provenance`);
2469
2450
  if (renders.length === 1) {
2470
- const script = entities.find((entity) => entity.entityId === renders[0].endpoint1EntityId && entity.entityKind === "audio-script");
2471
- if (script === void 0 || scriptText(script) !== text) fail(`Voice ${voiceEntityId} conflicts with the legacy Speech script`);
2472
- return script.entityId;
2451
+ const phonetic = entities.find((entity) => entity.entityId === renders[0].endpoint1EntityId && entity.entityKind === "phonetic-script");
2452
+ if (phonetic === void 0) fail(`Voice ${voiceEntityId} has a dangling PhoneticScript render`);
2453
+ const script = composedAudioScript(phonetic, entities, relations);
2454
+ if (script === void 0 || scriptText(script) !== audioScriptText) fail(`Voice ${voiceEntityId} conflicts with the legacy Speech script`);
2455
+ return phonetic.entityId;
2473
2456
  }
2474
2457
  const script = addEntity("audio-script", { segments: [{
2475
2458
  segmentId: "segment-0",
2476
- text
2459
+ text: audioScriptText
2477
2460
  }] });
2478
- link("audio-script-render", voiceEntityId, script);
2479
- return script;
2461
+ const phonetic = addEntity("phonetic-script", phonemeScript === void 0 ? {} : { phonemeScript });
2462
+ link("phonetic-script-composition", phonetic, script, { segmentAlignment: [] });
2463
+ link("phonetic-script-render", voiceEntityId, phonetic);
2464
+ return phonetic;
2465
+ }
2466
+ function composedAudioScript(phonetic, entities, relations) {
2467
+ const edges = relations.filter((relation) => relation.relationKind === "phonetic-script-composition" && (relation.endpoint0EntityId === phonetic.entityId || relation.endpoint1EntityId === phonetic.entityId));
2468
+ if (edges.length !== 1) return void 0;
2469
+ const edge = edges[0];
2470
+ const otherId = edge.endpoint0EntityId === phonetic.entityId ? edge.endpoint1EntityId : edge.endpoint0EntityId;
2471
+ return entities.find((entity) => entity.entityId === otherId);
2480
2472
  }
2481
2473
  function scriptText(script) {
2482
2474
  const segments = script.payload.segments;
@@ -4704,4 +4696,4 @@ function serverCovers(serverVV, target) {
4704
4696
  return covers(server, target);
4705
4697
  }
4706
4698
  //#endregion
4707
- export { DEFAULT_UNIT_TIME_MS, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, EntityGraphHttpClient, EntityTimelineEditor, EntityTimelineProjectionError, IMPLEMENTED_SEMANTIC_OP_KINDS, LANE_KINDS_IN_STACK_ORDER, ManualSyncDoc, MedeoHttpDocStorage, MemoryDocStorage, MengineAckFailedError, MengineDocSession, MengineHttpClient, MengineHttpRequestError, MenginePushRejectedError, MirrorVideoDocumentAdapter, PlainMemoryAdapter, SchemaValidator, SemanticEditor, TIMELINE_SKELETON_DURATION_MS, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, VideoDocumentValidationError, applyEntityTimelineProjection, arrangeMainTrackSeamlessly, assertCanonicalEditorResources, assertEntityProjectionPreservesLegacy, assertMediaAssetWritePolicy, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createEditSandbox, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, createPlainMemoryAdapter, decodeDocVersionMark, derivePositionFromAbs, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, importMediaAsset, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, migrateLegacyTimelineToEntities, partDurationMs, partUnionSchema, projectEntityTimeline, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, replayJournal, resolveAllSpeechOverlaps, resolveEntityTimelineLayout, resolveSpeechOverlapByShiftingVideos, safeDurationMs, schemas_exports as schemas, snapshotToPlain, solveVideoDocument, speedOf, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema, writeVideoDocumentToDraft };
4699
+ export { DEFAULT_UNIT_TIME_MS, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION, EntityGraphHttpClient, EntityTimelineEditor, EntityTimelineProjectionError, IMPLEMENTED_SEMANTIC_OP_KINDS, LANE_KINDS_IN_STACK_ORDER, ManualSyncDoc, MedeoHttpDocStorage, MemoryDocStorage, MengineAckFailedError, MengineDocSession, MengineHttpClient, MengineHttpRequestError, MenginePushRejectedError, MirrorVideoDocumentAdapter, PlainMemoryAdapter, SchemaValidator, ScriptCompositionError, SemanticEditor, TIMELINE_SKELETON_DURATION_MS, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, VideoDocumentValidationError, applyEntityTimelineProjection, arrangeMainTrackSeamlessly, assembleCaptionContent, assemblePhoneticScriptContent, assembleScriptText, assertCanonicalEditorResources, assertEntityProjectionPreservesLegacy, assertMediaAssetWritePolicy, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createEditSandbox, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, createPlainMemoryAdapter, decodeDocVersionMark, derivePositionFromAbs, effectiveVideoClipDurationMs, encodeDocVersionMark, ensureEditorFoundation, ensureLaneTrack, fillMainTrackTimeGaps, findComposedAudioScript, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, importMediaAsset, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, migrateLegacyTimelineToEntities, partDurationMs, partUnionSchema, projectEntityTimeline, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, replayJournal, resolveAllSpeechOverlaps, resolveEntityTimelineLayout, resolveSpeechOverlapByShiftingVideos, safeDurationMs, schemas_exports as schemas, selectAudioScriptSegments, snapshotToPlain, solveVideoDocument, speedOf, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema, writeVideoDocumentToDraft };
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Et as VideoDraft, c as MirrorVideoDocumentAdapter } from "./index-WxMNUbOa.js";
1
+ import { Ot as VideoDraft, c as MirrorVideoDocumentAdapter } from "./index-PYtGeXNk.js";
2
2
 
3
3
  //#region src/testing/in-memory-mengine-server.d.ts
4
4
  /**
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { J as VIDEO_DOCUMENT_SCHEMA_VERSION, et as base64ToBytes, s as createMirrorVideoDocumentAdapter, tt as bytesToBase64 } from "./document-Caidonf8.js";
1
+ import { et as VIDEO_DOCUMENT_SCHEMA_VERSION, ot as base64ToBytes, s as createMirrorVideoDocumentAdapter, st as bytesToBase64 } from "./document-Bf200CmD.js";
2
2
  import { t as classifyUpdate } from "./loro-relay-doc-ssdYpuef.js";
3
3
  import { LoroDoc, VersionVector, encodeFrontiers } from "loro-crdt";
4
4
  //#region src/testing/in-memory-mengine-server.ts