@meframe/core 0.4.7 → 0.5.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 (50) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/model/CompositionModel.d.ts +6 -0
  6. package/dist/model/CompositionModel.d.ts.map +1 -1
  7. package/dist/model/CompositionModel.js +49 -1
  8. package/dist/model/CompositionModel.js.map +1 -1
  9. package/dist/model/types.d.ts +8 -2
  10. package/dist/model/types.d.ts.map +1 -1
  11. package/dist/model/types.js +4 -0
  12. package/dist/model/types.js.map +1 -1
  13. package/dist/orchestrator/CompositionPlanner.d.ts +1 -0
  14. package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
  15. package/dist/orchestrator/CompositionPlanner.js +22 -1
  16. package/dist/orchestrator/CompositionPlanner.js.map +1 -1
  17. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  18. package/dist/orchestrator/Orchestrator.js +8 -1
  19. package/dist/orchestrator/Orchestrator.js.map +1 -1
  20. package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
  21. package/dist/stages/compose/LayerRenderer.js +20 -9
  22. package/dist/stages/compose/LayerRenderer.js.map +1 -1
  23. package/dist/stages/compose/font-system/types.d.ts +1 -1
  24. package/dist/stages/compose/font-system/types.d.ts.map +1 -1
  25. package/dist/stages/compose/instructions.d.ts +1 -1
  26. package/dist/stages/compose/instructions.d.ts.map +1 -1
  27. package/dist/stages/compose/text-layer-animation-time.d.ts +8 -0
  28. package/dist/stages/compose/text-layer-animation-time.d.ts.map +1 -0
  29. package/dist/stages/compose/text-layer-animation-time.js +22 -0
  30. package/dist/stages/compose/text-layer-animation-time.js.map +1 -0
  31. package/dist/stages/compose/text-renderers/basic-text-renderer.js +1 -21
  32. package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -1
  33. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.d.ts +9 -0
  34. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.d.ts.map +1 -0
  35. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.js +188 -0
  36. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.js.map +1 -0
  37. package/dist/stages/compose/text-renderers/word-fancy-renderer.js +1 -1
  38. package/dist/stages/compose/text-utils/caption-layout.d.ts +63 -0
  39. package/dist/stages/compose/text-utils/caption-layout.d.ts.map +1 -0
  40. package/dist/stages/compose/text-utils/caption-layout.js +129 -0
  41. package/dist/stages/compose/text-utils/caption-layout.js.map +1 -0
  42. package/dist/stages/compose/text-utils/index.d.ts +1 -0
  43. package/dist/stages/compose/text-utils/index.d.ts.map +1 -1
  44. package/dist/stages/compose/types.d.ts +3 -1
  45. package/dist/stages/compose/types.d.ts.map +1 -1
  46. package/dist/workers/stages/export/{export.worker.Cw9iPvkh.js → export.worker.Dztm6GuN.js} +218 -28
  47. package/dist/workers/stages/export/export.worker.Dztm6GuN.js.map +1 -0
  48. package/dist/workers/worker-manifest.json +1 -1
  49. package/package.json +1 -1
  50. package/dist/workers/stages/export/export.worker.Cw9iPvkh.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ export { CompositionModel } from './model/CompositionModel';
9
9
  export type { CompositionModelData, CompositionPatch, DirtyRange, TimeUs, Track, Clip, VideoClip, AudioClip, CaptionClip, Resource, Effect, Transition, Attachment, AnimationEffect, AnimationKeyframe, Transform2D, } from './model/types';
10
10
  export type { CacheConfig, CacheStats } from './cache/types';
11
11
  export type { Plugin, PluginHook } from './plugins/types';
12
+ export { computeCaptionLayout } from './stages/compose/text-utils/caption-layout';
13
+ export type { CaptionLayoutInput, CaptionLayout, CaptionLine, CaptionWord, CaptionStyle, CaptionContainer, } from './stages/compose/text-utils/caption-layout';
12
14
  export { setupCanvasDPI, createHiDPICanvas, checkCanvasDPI } from './utils/canvas-utils';
13
15
  export { BrowserCompatibilityError } from './utils/errors';
14
16
  export declare const VERSION = "0.3.3";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,MAAM,EACN,KAAK,EACL,IAAI,EACJ,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,MAAM,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG7D,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEzF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAG3D,eAAO,MAAM,OAAO,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,MAAM,EACN,KAAK,EACL,IAAI,EACJ,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,MAAM,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG7D,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,WAAW,EACX,YAAY,EACZ,gBAAgB,GACjB,MAAM,4CAA4C,CAAC;AAGpD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEzF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAG3D,eAAO,MAAM,OAAO,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Meframe } from "./Meframe.js";
2
2
  import { MeframeEvent } from "./event/events.js";
3
3
  import { CompositionModel } from "./model/CompositionModel.js";
4
+ import { computeCaptionLayout } from "./stages/compose/text-utils/caption-layout.js";
4
5
  import { checkCanvasDPI, createHiDPICanvas, setupCanvasDPI } from "./utils/canvas-utils.js";
5
6
  import { BrowserCompatibilityError } from "./utils/errors.js";
6
7
  const VERSION = "0.3.3";
@@ -11,6 +12,7 @@ export {
11
12
  MeframeEvent,
12
13
  VERSION,
13
14
  checkCanvasDPI,
15
+ computeCaptionLayout,
14
16
  createHiDPICanvas,
15
17
  setupCanvasDPI
16
18
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @meframe/core - Next generation media processing framework\n * Based on WebCodecs API for high-performance video/audio processing\n */\n\n// Core exports\nexport { Meframe } from './Meframe';\nexport type { MeframeConfig, MeframeState } from './types';\nexport { MeframeEvent } from './event/events';\n\n// Model exports\nexport { CompositionModel } from './model/CompositionModel';\nexport type {\n CompositionModelData,\n CompositionPatch,\n DirtyRange,\n TimeUs,\n Track,\n Clip,\n VideoClip,\n AudioClip,\n CaptionClip,\n Resource,\n Effect,\n Transition,\n Attachment,\n AnimationEffect,\n AnimationKeyframe,\n Transform2D,\n} from './model/types';\n\n// Cache exports\nexport type { CacheConfig, CacheStats } from './cache/types';\n\n// Plugin exports\nexport type { Plugin, PluginHook } from './plugins/types';\n\n// Utility exports\nexport { setupCanvasDPI, createHiDPICanvas, checkCanvasDPI } from './utils/canvas-utils';\n\nexport { BrowserCompatibilityError } from './utils/errors';\n\n// Re-export version\nexport const VERSION = '0.3.3';\n"],"names":[],"mappings":";;;;;AA2CO,MAAM,UAAU;"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @meframe/core - Next generation media processing framework\n * Based on WebCodecs API for high-performance video/audio processing\n */\n\n// Core exports\nexport { Meframe } from './Meframe';\nexport type { MeframeConfig, MeframeState } from './types';\nexport { MeframeEvent } from './event/events';\n\n// Model exports\nexport { CompositionModel } from './model/CompositionModel';\nexport type {\n CompositionModelData,\n CompositionPatch,\n DirtyRange,\n TimeUs,\n Track,\n Clip,\n VideoClip,\n AudioClip,\n CaptionClip,\n Resource,\n Effect,\n Transition,\n Attachment,\n AnimationEffect,\n AnimationKeyframe,\n Transform2D,\n} from './model/types';\n\n// Cache exports\nexport type { CacheConfig, CacheStats } from './cache/types';\n\n// Plugin exports\nexport type { Plugin, PluginHook } from './plugins/types';\n\n// Caption layout exports\nexport { computeCaptionLayout } from './stages/compose/text-utils/caption-layout';\nexport type {\n CaptionLayoutInput,\n CaptionLayout,\n CaptionLine,\n CaptionWord,\n CaptionStyle,\n CaptionContainer,\n} from './stages/compose/text-utils/caption-layout';\n\n// Utility exports\nexport { setupCanvasDPI, createHiDPICanvas, checkCanvasDPI } from './utils/canvas-utils';\n\nexport { BrowserCompatibilityError } from './utils/errors';\n\n// Re-export version\nexport const VERSION = '0.3.3';\n"],"names":[],"mappings":";;;;;;AAsDO,MAAM,UAAU;"}
@@ -72,6 +72,12 @@ export declare class CompositionModel {
72
72
  * Recalculate total duration based on main track
73
73
  */
74
74
  recalculateDuration(): void;
75
+ /**
76
+ * One entrance preset for the whole caption track: first caption clip on the
77
+ * timeline (by startUs) that defines `animation` wins; all sunk caption
78
+ * attachments reuse it so compose matches a global track style.
79
+ */
80
+ private getSharedCaptionTrackAnimation;
75
81
  private sinkAttachmentTracks;
76
82
  private getTimeOverlap;
77
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CompositionModel.d.ts","sourceRoot":"","sources":["../../src/model/CompositionModel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CompositionModel.d.ts","sourceRoot":"","sources":["../../src/model/CompositionModel.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { hasResourceId } from "./types.js";
1
+ import { hasResourceId, isCaptionClip } from "./types.js";
2
2
  import { binarySearchRange, binarySearchOverlapping } from "../utils/binary-search.js";
3
3
  import { validateCompositionStructure } from "./validation.js";
4
4
  import { filterRenderConfig } from "../utils/object-utils.js";
@@ -324,6 +324,20 @@ ${errors.map((e) => `${e.path}: ${e.message}`).join("\n")}`
324
324
  const lastClip = mainTrack.clips[mainTrack.clips.length - 1];
325
325
  this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);
326
326
  }
327
+ /**
328
+ * One entrance preset for the whole caption track: first caption clip on the
329
+ * timeline (by startUs) that defines `animation` wins; all sunk caption
330
+ * attachments reuse it so compose matches a global track style.
331
+ */
332
+ getSharedCaptionTrackAnimation(track) {
333
+ if (track.kind !== "caption") return void 0;
334
+ for (const clip of track.clips) {
335
+ if (isCaptionClip(clip) && clip.animation != null) {
336
+ return clip.animation;
337
+ }
338
+ }
339
+ return void 0;
340
+ }
327
341
  sinkAttachmentTracks() {
328
342
  let mainTrack;
329
343
  const attachmentTracks = [];
@@ -340,10 +354,14 @@ ${errors.map((e) => `${e.path}: ${e.message}`).join("\n")}`
340
354
  if (!mainTrack) {
341
355
  throw new Error("Main track not found");
342
356
  }
357
+ if (attachmentTracks.length === 0) {
358
+ return;
359
+ }
343
360
  for (const clip of mainTrack.clips) {
344
361
  clip.attachments = [];
345
362
  }
346
363
  for (const attachmentTrack of attachmentTracks) {
364
+ const sharedCaptionAnimation = attachmentTrack.kind === "caption" ? this.getSharedCaptionTrackAnimation(attachmentTrack) : void 0;
347
365
  for (const attachmentClip of attachmentTrack.clips) {
348
366
  const attachmentKind = attachmentTrack.kind;
349
367
  for (const mainClip of mainTrack.clips) {
@@ -373,6 +391,36 @@ ${errors.map((e) => `${e.path}: ${e.message}`).join("\n")}`
373
391
  if ("text" in attachmentClip && attachmentClip.text) {
374
392
  attachmentData.text = attachmentClip.text;
375
393
  }
394
+ if (isCaptionClip(attachmentClip)) {
395
+ if (attachmentClip.fontFamily) {
396
+ attachmentData.fontFamily = attachmentClip.fontFamily;
397
+ }
398
+ if (attachmentClip.localeCode) {
399
+ attachmentData.localeCode = attachmentClip.localeCode;
400
+ }
401
+ if (attachmentClip.fontTemplate) {
402
+ attachmentData.fontTemplate = attachmentClip.fontTemplate;
403
+ }
404
+ if (attachmentClip.fontSize != null) {
405
+ attachmentData.fontSize = attachmentClip.fontSize;
406
+ }
407
+ if (attachmentClip.fontWeight != null) {
408
+ attachmentData.fontWeight = attachmentClip.fontWeight;
409
+ }
410
+ if (attachmentClip.fill != null) {
411
+ attachmentData.fill = attachmentClip.fill;
412
+ }
413
+ if (attachmentClip.wordTimings) {
414
+ attachmentData.wordTimings = attachmentClip.wordTimings;
415
+ }
416
+ const captionAnimation = sharedCaptionAnimation ?? attachmentClip.animation;
417
+ if (captionAnimation) {
418
+ attachmentData.animation = captionAnimation;
419
+ }
420
+ if (attachmentClip.letterCase) {
421
+ attachmentData.letterCase = attachmentClip.letterCase;
422
+ }
423
+ }
376
424
  const newAttachment = {
377
425
  id: `${attachmentKind}-${attachmentClip.id}-${mainClip.id}`,
378
426
  kind: attachmentKind,
@@ -1 +1 @@
1
- {"version":3,"file":"CompositionModel.js","sources":["../../src/model/CompositionModel.ts"],"sourcesContent":["import {\n CompositionModelData,\n Track,\n Clip,\n Resource,\n TimeUs,\n AnimationEffect,\n hasResourceId,\n} from './types';\nimport { binarySearchRange, binarySearchOverlapping } from '../utils/binary-search';\nimport { validateCompositionStructure } from './validation';\nimport { filterRenderConfig } from '../utils/object-utils';\n\nexport class CompositionModel {\n public readonly version = '1.0' as const;\n public readonly fps: 24 | 25 | 30 | 60;\n public durationUs!: TimeUs; // Assigned in buildIndexes()\n public readonly mainTrackId: string;\n public tracks: Track[];\n public readonly resources: Map<string, Resource>;\n\n private readonly trackMap: Map<string, Track>;\n private readonly clipMap: Map<string, Clip>;\n private readonly resourceRefCount: Map<string, number>;\n\n public readonly renderConfig?: {\n width: number;\n height: number;\n backgroundColor?: string;\n };\n\n public readonly ext?: Record<string, unknown>;\n\n constructor(data: CompositionModelData) {\n const errors = validateCompositionStructure(data);\n if (errors.length > 0) {\n throw new Error(\n `Validation failed:\\n${errors.map((e) => `${e.path}: ${e.message}`).join('\\n')}`\n );\n }\n\n this.fps = data.fps;\n this.mainTrackId = data.mainTrackId ?? 'main';\n this.tracks = data.tracks;\n this.resources = new Map(Object.entries(data.resources));\n this.renderConfig = data.renderConfig;\n this.ext = data.ext;\n\n // Build indexes\n this.trackMap = new Map();\n this.clipMap = new Map();\n this.resourceRefCount = new Map();\n\n this.buildIndexes();\n }\n\n // Track operations\n findTrack(id: string): Track | null {\n return this.trackMap.get(id) || null;\n }\n\n getTracksByKind(kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx'): Track[] {\n return this.tracks.filter((track) => track.kind === kind);\n }\n\n // Clip operations with binary search optimization\n findClip(id: string): Clip | null {\n return this.clipMap.get(id) || null;\n }\n\n getClipsAtTime(timeUs: TimeUs, trackId?: string): Clip[] {\n const tracks = trackId ? [this.findTrack(trackId)] : this.tracks;\n const clips: Clip[] = [];\n\n for (const track of tracks) {\n if (!track) continue;\n // Use binary search for single point lookup\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n if (clip) {\n clips.push(clip);\n }\n }\n\n return clips;\n }\n\n getActiveClips(startUs: TimeUs, endUs: TimeUs): Clip[] {\n const clips: Clip[] = [];\n\n for (const track of this.tracks) {\n // Use binary search for range overlap\n const overlappingClips = binarySearchOverlapping(\n track.clips,\n startUs,\n endUs,\n (clip, _index) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n })\n );\n\n clips.push(...overlappingClips);\n }\n\n return clips;\n }\n\n /**\n * Get all clips in a specific track that overlap with the given time range\n * Uses binary search for O(log n + k) performance\n * @param startUs - Range start time (inclusive)\n * @param endUs - Range end time (exclusive)\n * @param trackId - Optional track ID to filter (defaults to main track)\n */\n getClipsInRange(startUs: TimeUs, endUs: TimeUs, trackId?: string): Clip[] {\n const targetTrackId = trackId ?? this.mainTrackId;\n const track = this.findTrack(targetTrackId);\n\n if (!track) {\n return [];\n }\n\n return binarySearchOverlapping(track.clips, startUs, endUs, (clip) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n }));\n }\n\n getClipIdsByResourceId(resourceId: string): string[] {\n const resource = this.resources.get(resourceId);\n return resource?.clipIds || [];\n }\n\n getClipIdAtTime(trackId: string, timeUs: TimeUs): string | undefined {\n const track = this.findTrack(trackId);\n if (!track) {\n return undefined;\n }\n\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n return clip?.id;\n }\n\n /**\n * Get neighboring clips (Prev/Current/Next) at a specific time for video tracks\n * Returns prev, current, and next clip IDs\n */\n getNeighboringClips(timeUs: TimeUs): { prev?: string; current?: string; next?: string } {\n const videoTracks = this.getTracksByKind('video');\n const result: { prev?: string; current?: string; next?: string } = {};\n\n for (const track of videoTracks) {\n const clips = track.clips;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n if (!clip) continue;\n\n const clipEndUs = clip.startUs + clip.durationUs;\n\n if (clip.startUs <= timeUs && timeUs < clipEndUs) {\n if (!result.current) {\n result.current = clip.id;\n }\n\n if (i > 0 && !result.prev) {\n const prevClip = clips[i - 1];\n if (prevClip) {\n result.prev = prevClip.id;\n }\n }\n\n if (i < clips.length - 1 && !result.next) {\n const nextClip = clips[i + 1];\n if (nextClip) {\n result.next = nextClip.id;\n }\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get all clip IDs that should be cached using adaptive strategy\n * - Short clips (≤ maxDuration): cache Current + Next (smooth transitions)\n * - Long clips (> maxDuration): cache Current only (memory control)\n * @param timeUs - Current playback time\n * @param maxDuration - Max duration for 2-clip strategy (default 5s)\n */\n getClipsToCacheAtTime(timeUs: TimeUs, maxDuration = 5_000_000): Set<string> {\n const { current, next } = this.getNeighboringClips(timeUs);\n const clipIds = new Set<string>();\n\n if (!current) return clipIds;\n clipIds.add(current);\n\n // Only cache next clip if current clip is short enough\n const currentClip = this.findClip(current);\n if (currentClip && currentClip.durationUs <= maxDuration && next) {\n clipIds.add(next);\n }\n\n return clipIds;\n }\n\n // Resource operations\n getResource(id: string): Resource | null {\n return this.resources.get(id) || null;\n }\n\n updateResourceState(id: string, state: 'pending' | 'loading' | 'ready' | 'error'): void {\n const resource = this.resources.get(id);\n if (resource) {\n resource.state = state;\n }\n }\n\n getUnusedResources(): Resource[] {\n const unused: Resource[] = [];\n\n for (const [id, resource] of this.resources) {\n if (!this.resourceRefCount.has(id) || this.resourceRefCount.get(id) === 0) {\n unused.push(resource);\n }\n }\n\n return unused;\n }\n\n // Time operations\n getDuration(): TimeUs {\n return this.durationUs;\n }\n\n getTrackDuration(trackId: string): TimeUs {\n const track = this.findTrack(trackId);\n if (!track || track.clips.length === 0) return 0;\n\n // Since clips are sorted, last clip determines duration\n const lastClip = track.clips[track.clips.length - 1];\n return (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n buildIndexes(options?: {\n incremental?: boolean;\n trackId?: string;\n clipId?: string;\n clip?: Clip;\n operation?: 'add' | 'update' | 'remove';\n }): void {\n const track = options?.trackId ? this.findTrack(options.trackId) : undefined;\n const isAttachmentTrack = track && track.kind !== 'video' && track.kind !== 'audio';\n // Incremental update for video/audio track clip operations\n if (options?.incremental && !isAttachmentTrack && options.clipId && options.operation) {\n const clip = options.clip ?? this.clipMap.get(options.clipId);\n\n if (options.operation === 'add' && clip) {\n this.addClipToIndexes(clip);\n } else if (options.operation === 'remove' && clip) {\n this.removeClipFromIndexes(clip);\n } else if (options.operation === 'update' && clip) {\n // Handle resource change during update\n if (clip.oldResourceId) {\n this.updateClipResourceIndex(\n options.clipId,\n clip.oldResourceId,\n hasResourceId(clip) ? clip.resourceId : undefined\n );\n }\n }\n\n // Recalculate duration only if affected main track\n if (track?.id === this.mainTrackId) {\n this.recalculateDuration();\n }\n return;\n }\n\n // Full rebuild: needed for attachment tracks or initial load\n this.fullRebuildIndexes();\n }\n\n private fullRebuildIndexes(): void {\n // Clear existing indexes\n this.trackMap.clear();\n this.clipMap.clear();\n this.resourceRefCount.clear();\n\n // Step 1: Sink attachment tracks to main track (preserves original tracks)\n this.sinkAttachmentTracks();\n\n let maxEndUs = 0;\n\n // Step 2: Build all indexes in one pass (track, clip, resource)\n for (const track of this.tracks) {\n this.trackMap.set(track.id, track);\n\n for (const clip of track.clips) {\n (clip as Clip).trackId = track.id;\n (clip as Clip).trackKind = track.kind;\n this.clipMap.set(clip.id, clip);\n\n // Main track resource index (only for clips with resourceId)\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n resource.clipIds = [...(resource.clipIds || []), clip.id];\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n\n // Attachment resource indexes (attachments are already sunk)\n const attachments = clip.attachments ?? [];\n for (const attachment of attachments) {\n const attachmentResourceId = attachment.data?.resourceId;\n if (attachmentResourceId && typeof attachmentResourceId === 'string') {\n const attachmentResource = this.resources.get(attachmentResourceId);\n if (attachmentResource) {\n const clipIds = attachmentResource.clipIds || [];\n if (!clipIds.includes(clip.id)) {\n attachmentResource.clipIds = [...clipIds, clip.id];\n }\n }\n const attachmentCount = this.resourceRefCount.get(attachmentResourceId) || 0;\n this.resourceRefCount.set(attachmentResourceId, attachmentCount + 1);\n }\n }\n\n // Calculate max end time\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clipEndUs > maxEndUs) {\n maxEndUs = clipEndUs;\n }\n }\n }\n\n this.durationUs = maxEndUs;\n }\n\n private addClipToIndexes(clip: Clip): void {\n this.clipMap.set(clip.id, clip);\n\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n if (!resource.clipIds) {\n resource.clipIds = [];\n }\n if (!resource.clipIds.includes(clip.id)) {\n resource.clipIds.push(clip.id);\n }\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n }\n\n private removeClipFromIndexes(clip: Clip): void {\n this.clipMap.delete(clip.id);\n\n const resourceId = clip.oldResourceId || (hasResourceId(clip) ? clip.resourceId : undefined);\n if (resourceId) {\n const resource = this.resources.get(resourceId);\n if (resource?.clipIds) {\n resource.clipIds = resource.clipIds.filter((id) => id !== clip.id);\n }\n const count = this.resourceRefCount.get(resourceId) || 0;\n this.resourceRefCount.set(resourceId, Math.max(0, count - 1));\n }\n }\n\n /**\n * Incrementally update resource index when clip's resourceId changes\n */\n updateClipResourceIndex(\n clipId: string,\n oldResourceId: string | undefined,\n newResourceId: string | undefined\n ): void {\n // Remove from old resource\n if (oldResourceId) {\n const oldResource = this.resources.get(oldResourceId);\n if (oldResource?.clipIds) {\n oldResource.clipIds = oldResource.clipIds.filter((id) => id !== clipId);\n }\n const oldCount = this.resourceRefCount.get(oldResourceId) || 0;\n this.resourceRefCount.set(oldResourceId, Math.max(0, oldCount - 1));\n }\n\n // Add to new resource\n if (newResourceId) {\n const newResource = this.resources.get(newResourceId);\n if (newResource) {\n if (!newResource.clipIds) {\n newResource.clipIds = [];\n }\n if (!newResource.clipIds.includes(clipId)) {\n newResource.clipIds.push(clipId);\n }\n }\n const newCount = this.resourceRefCount.get(newResourceId) || 0;\n this.resourceRefCount.set(newResourceId, newCount + 1);\n }\n }\n\n /**\n * Recalculate total duration based on main track\n */\n recalculateDuration(): void {\n const mainTrack = this.findTrack(this.mainTrackId);\n if (!mainTrack || mainTrack.clips.length === 0) {\n this.durationUs = 0;\n return;\n }\n\n // Since clips are sorted, last clip determines duration\n const lastClip = mainTrack.clips[mainTrack.clips.length - 1];\n this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n private sinkAttachmentTracks(): void {\n let mainTrack: Track | undefined;\n const attachmentTracks = [];\n\n // Sort all tracks\n for (const track of this.tracks) {\n track.clips.sort((a, b) => a.startUs - b.startUs);\n\n if (track.id === this.mainTrackId) {\n mainTrack = track;\n continue;\n }\n // Collect attachment tracks for sinking (caption, overlay, fx)\n // Video and audio tracks are not sunk\n if (track.kind === 'caption' || track.kind === 'overlay' || track.kind === 'fx') {\n attachmentTracks.push(track);\n }\n }\n\n if (!mainTrack) {\n throw new Error('Main track not found');\n }\n\n // Clear existing attachments before sinking (in case of rebuild)\n for (const clip of mainTrack.clips) {\n clip.attachments = [];\n }\n\n for (const attachmentTrack of attachmentTracks) {\n for (const attachmentClip of attachmentTrack.clips) {\n // Use track.kind directly as attachment kind\n const attachmentKind = attachmentTrack.kind;\n\n for (const mainClip of mainTrack.clips) {\n const overlap = this.getTimeOverlap(attachmentClip, mainClip);\n if (!overlap) continue;\n\n if (!mainClip.attachments) {\n mainClip.attachments = [];\n }\n\n // Extract animation effect\n const animationEffect = attachmentClip.effects?.find(\n (e) => e.effectType === 'animation'\n ) as AnimationEffect | undefined;\n\n const attachmentData: Record<string, unknown> = {\n animation: animationEffect?.params,\n overlayClipStartUs: attachmentClip.startUs,\n mainClipStartUs: mainClip.startUs,\n };\n\n // Add renderConfig if valid\n const filteredRenderConfig = filterRenderConfig(\n 'renderConfig' in attachmentClip ? attachmentClip.renderConfig : undefined,\n `attachment clip ${attachmentClip.id}`\n );\n if (filteredRenderConfig) {\n attachmentData.renderConfig = filteredRenderConfig;\n }\n\n // Add resourceId if exists (trackKind not yet set, check field directly)\n if ('resourceId' in attachmentClip && attachmentClip.resourceId) {\n attachmentData.resourceId = attachmentClip.resourceId;\n }\n\n // Add text if exists (trackKind not yet set, check field directly)\n if ('text' in attachmentClip && attachmentClip.text) {\n attachmentData.text = attachmentClip.text;\n }\n\n const newAttachment = {\n id: `${attachmentKind}-${attachmentClip.id}-${mainClip.id}`,\n kind: attachmentKind,\n startUs: overlap.clipRelativeStart,\n durationUs: overlap.duration,\n data: attachmentData,\n };\n\n mainClip.attachments.push(newAttachment);\n }\n }\n }\n }\n\n private getTimeOverlap(\n attachmentClip: Clip,\n mainClip: Clip\n ): { clipRelativeStart: number; duration: number } | null {\n const attachmentStart = attachmentClip.startUs;\n const attachmentEnd = attachmentClip.startUs + attachmentClip.durationUs;\n const mainStart = mainClip.startUs;\n const mainEnd = mainClip.startUs + mainClip.durationUs;\n\n if (attachmentEnd <= mainStart || attachmentStart >= mainEnd) {\n return null;\n }\n\n const overlapStart = Math.max(attachmentStart, mainStart);\n const overlapEnd = Math.min(attachmentEnd, mainEnd);\n const duration = overlapEnd - overlapStart;\n const clipRelativeStart = overlapStart - mainStart;\n\n return { clipRelativeStart, duration };\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CompositionModel.js","sources":["../../src/model/CompositionModel.ts"],"sourcesContent":["import {\n type CaptionClip,\n CompositionModelData,\n Track,\n Clip,\n Resource,\n TimeUs,\n AnimationEffect,\n hasResourceId,\n isCaptionClip,\n} from './types';\nimport { binarySearchRange, binarySearchOverlapping } from '../utils/binary-search';\nimport { validateCompositionStructure } from './validation';\nimport { filterRenderConfig } from '../utils/object-utils';\n\nexport class CompositionModel {\n public readonly version = '1.0' as const;\n public readonly fps: 24 | 25 | 30 | 60;\n public durationUs!: TimeUs; // Assigned in buildIndexes()\n public readonly mainTrackId: string;\n public tracks: Track[];\n public readonly resources: Map<string, Resource>;\n\n private readonly trackMap: Map<string, Track>;\n private readonly clipMap: Map<string, Clip>;\n private readonly resourceRefCount: Map<string, number>;\n\n public readonly renderConfig?: {\n width: number;\n height: number;\n backgroundColor?: string;\n };\n\n public readonly ext?: Record<string, unknown>;\n\n constructor(data: CompositionModelData) {\n const errors = validateCompositionStructure(data);\n if (errors.length > 0) {\n throw new Error(\n `Validation failed:\\n${errors.map((e) => `${e.path}: ${e.message}`).join('\\n')}`\n );\n }\n\n this.fps = data.fps;\n this.mainTrackId = data.mainTrackId ?? 'main';\n this.tracks = data.tracks;\n this.resources = new Map(Object.entries(data.resources));\n this.renderConfig = data.renderConfig;\n this.ext = data.ext;\n\n // Build indexes\n this.trackMap = new Map();\n this.clipMap = new Map();\n this.resourceRefCount = new Map();\n\n this.buildIndexes();\n }\n\n // Track operations\n findTrack(id: string): Track | null {\n return this.trackMap.get(id) || null;\n }\n\n getTracksByKind(kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx'): Track[] {\n return this.tracks.filter((track) => track.kind === kind);\n }\n\n // Clip operations with binary search optimization\n findClip(id: string): Clip | null {\n return this.clipMap.get(id) || null;\n }\n\n getClipsAtTime(timeUs: TimeUs, trackId?: string): Clip[] {\n const tracks = trackId ? [this.findTrack(trackId)] : this.tracks;\n const clips: Clip[] = [];\n\n for (const track of tracks) {\n if (!track) continue;\n // Use binary search for single point lookup\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n if (clip) {\n clips.push(clip);\n }\n }\n\n return clips;\n }\n\n getActiveClips(startUs: TimeUs, endUs: TimeUs): Clip[] {\n const clips: Clip[] = [];\n\n for (const track of this.tracks) {\n // Use binary search for range overlap\n const overlappingClips = binarySearchOverlapping(\n track.clips,\n startUs,\n endUs,\n (clip, _index) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n })\n );\n\n clips.push(...overlappingClips);\n }\n\n return clips;\n }\n\n /**\n * Get all clips in a specific track that overlap with the given time range\n * Uses binary search for O(log n + k) performance\n * @param startUs - Range start time (inclusive)\n * @param endUs - Range end time (exclusive)\n * @param trackId - Optional track ID to filter (defaults to main track)\n */\n getClipsInRange(startUs: TimeUs, endUs: TimeUs, trackId?: string): Clip[] {\n const targetTrackId = trackId ?? this.mainTrackId;\n const track = this.findTrack(targetTrackId);\n\n if (!track) {\n return [];\n }\n\n return binarySearchOverlapping(track.clips, startUs, endUs, (clip) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n }));\n }\n\n getClipIdsByResourceId(resourceId: string): string[] {\n const resource = this.resources.get(resourceId);\n return resource?.clipIds || [];\n }\n\n getClipIdAtTime(trackId: string, timeUs: TimeUs): string | undefined {\n const track = this.findTrack(trackId);\n if (!track) {\n return undefined;\n }\n\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n return clip?.id;\n }\n\n /**\n * Get neighboring clips (Prev/Current/Next) at a specific time for video tracks\n * Returns prev, current, and next clip IDs\n */\n getNeighboringClips(timeUs: TimeUs): { prev?: string; current?: string; next?: string } {\n const videoTracks = this.getTracksByKind('video');\n const result: { prev?: string; current?: string; next?: string } = {};\n\n for (const track of videoTracks) {\n const clips = track.clips;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n if (!clip) continue;\n\n const clipEndUs = clip.startUs + clip.durationUs;\n\n if (clip.startUs <= timeUs && timeUs < clipEndUs) {\n if (!result.current) {\n result.current = clip.id;\n }\n\n if (i > 0 && !result.prev) {\n const prevClip = clips[i - 1];\n if (prevClip) {\n result.prev = prevClip.id;\n }\n }\n\n if (i < clips.length - 1 && !result.next) {\n const nextClip = clips[i + 1];\n if (nextClip) {\n result.next = nextClip.id;\n }\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get all clip IDs that should be cached using adaptive strategy\n * - Short clips (≤ maxDuration): cache Current + Next (smooth transitions)\n * - Long clips (> maxDuration): cache Current only (memory control)\n * @param timeUs - Current playback time\n * @param maxDuration - Max duration for 2-clip strategy (default 5s)\n */\n getClipsToCacheAtTime(timeUs: TimeUs, maxDuration = 5_000_000): Set<string> {\n const { current, next } = this.getNeighboringClips(timeUs);\n const clipIds = new Set<string>();\n\n if (!current) return clipIds;\n clipIds.add(current);\n\n // Only cache next clip if current clip is short enough\n const currentClip = this.findClip(current);\n if (currentClip && currentClip.durationUs <= maxDuration && next) {\n clipIds.add(next);\n }\n\n return clipIds;\n }\n\n // Resource operations\n getResource(id: string): Resource | null {\n return this.resources.get(id) || null;\n }\n\n updateResourceState(id: string, state: 'pending' | 'loading' | 'ready' | 'error'): void {\n const resource = this.resources.get(id);\n if (resource) {\n resource.state = state;\n }\n }\n\n getUnusedResources(): Resource[] {\n const unused: Resource[] = [];\n\n for (const [id, resource] of this.resources) {\n if (!this.resourceRefCount.has(id) || this.resourceRefCount.get(id) === 0) {\n unused.push(resource);\n }\n }\n\n return unused;\n }\n\n // Time operations\n getDuration(): TimeUs {\n return this.durationUs;\n }\n\n getTrackDuration(trackId: string): TimeUs {\n const track = this.findTrack(trackId);\n if (!track || track.clips.length === 0) return 0;\n\n // Since clips are sorted, last clip determines duration\n const lastClip = track.clips[track.clips.length - 1];\n return (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n buildIndexes(options?: {\n incremental?: boolean;\n trackId?: string;\n clipId?: string;\n clip?: Clip;\n operation?: 'add' | 'update' | 'remove';\n }): void {\n const track = options?.trackId ? this.findTrack(options.trackId) : undefined;\n const isAttachmentTrack = track && track.kind !== 'video' && track.kind !== 'audio';\n // Incremental update for video/audio track clip operations\n if (options?.incremental && !isAttachmentTrack && options.clipId && options.operation) {\n const clip = options.clip ?? this.clipMap.get(options.clipId);\n\n if (options.operation === 'add' && clip) {\n this.addClipToIndexes(clip);\n } else if (options.operation === 'remove' && clip) {\n this.removeClipFromIndexes(clip);\n } else if (options.operation === 'update' && clip) {\n // Handle resource change during update\n if (clip.oldResourceId) {\n this.updateClipResourceIndex(\n options.clipId,\n clip.oldResourceId,\n hasResourceId(clip) ? clip.resourceId : undefined\n );\n }\n }\n\n // Recalculate duration only if affected main track\n if (track?.id === this.mainTrackId) {\n this.recalculateDuration();\n }\n return;\n }\n\n // Full rebuild: needed for attachment tracks or initial load\n this.fullRebuildIndexes();\n }\n\n private fullRebuildIndexes(): void {\n // Clear existing indexes\n this.trackMap.clear();\n this.clipMap.clear();\n this.resourceRefCount.clear();\n\n // Step 1: Sink attachment tracks to main track (preserves original tracks)\n this.sinkAttachmentTracks();\n\n let maxEndUs = 0;\n\n // Step 2: Build all indexes in one pass (track, clip, resource)\n for (const track of this.tracks) {\n this.trackMap.set(track.id, track);\n\n for (const clip of track.clips) {\n (clip as Clip).trackId = track.id;\n (clip as Clip).trackKind = track.kind;\n this.clipMap.set(clip.id, clip);\n\n // Main track resource index (only for clips with resourceId)\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n resource.clipIds = [...(resource.clipIds || []), clip.id];\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n\n // Attachment resource indexes (attachments are already sunk)\n const attachments = clip.attachments ?? [];\n for (const attachment of attachments) {\n const attachmentResourceId = attachment.data?.resourceId;\n if (attachmentResourceId && typeof attachmentResourceId === 'string') {\n const attachmentResource = this.resources.get(attachmentResourceId);\n if (attachmentResource) {\n const clipIds = attachmentResource.clipIds || [];\n if (!clipIds.includes(clip.id)) {\n attachmentResource.clipIds = [...clipIds, clip.id];\n }\n }\n const attachmentCount = this.resourceRefCount.get(attachmentResourceId) || 0;\n this.resourceRefCount.set(attachmentResourceId, attachmentCount + 1);\n }\n }\n\n // Calculate max end time\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clipEndUs > maxEndUs) {\n maxEndUs = clipEndUs;\n }\n }\n }\n\n this.durationUs = maxEndUs;\n }\n\n private addClipToIndexes(clip: Clip): void {\n this.clipMap.set(clip.id, clip);\n\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n if (!resource.clipIds) {\n resource.clipIds = [];\n }\n if (!resource.clipIds.includes(clip.id)) {\n resource.clipIds.push(clip.id);\n }\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n }\n\n private removeClipFromIndexes(clip: Clip): void {\n this.clipMap.delete(clip.id);\n\n const resourceId = clip.oldResourceId || (hasResourceId(clip) ? clip.resourceId : undefined);\n if (resourceId) {\n const resource = this.resources.get(resourceId);\n if (resource?.clipIds) {\n resource.clipIds = resource.clipIds.filter((id) => id !== clip.id);\n }\n const count = this.resourceRefCount.get(resourceId) || 0;\n this.resourceRefCount.set(resourceId, Math.max(0, count - 1));\n }\n }\n\n /**\n * Incrementally update resource index when clip's resourceId changes\n */\n updateClipResourceIndex(\n clipId: string,\n oldResourceId: string | undefined,\n newResourceId: string | undefined\n ): void {\n // Remove from old resource\n if (oldResourceId) {\n const oldResource = this.resources.get(oldResourceId);\n if (oldResource?.clipIds) {\n oldResource.clipIds = oldResource.clipIds.filter((id) => id !== clipId);\n }\n const oldCount = this.resourceRefCount.get(oldResourceId) || 0;\n this.resourceRefCount.set(oldResourceId, Math.max(0, oldCount - 1));\n }\n\n // Add to new resource\n if (newResourceId) {\n const newResource = this.resources.get(newResourceId);\n if (newResource) {\n if (!newResource.clipIds) {\n newResource.clipIds = [];\n }\n if (!newResource.clipIds.includes(clipId)) {\n newResource.clipIds.push(clipId);\n }\n }\n const newCount = this.resourceRefCount.get(newResourceId) || 0;\n this.resourceRefCount.set(newResourceId, newCount + 1);\n }\n }\n\n /**\n * Recalculate total duration based on main track\n */\n recalculateDuration(): void {\n const mainTrack = this.findTrack(this.mainTrackId);\n if (!mainTrack || mainTrack.clips.length === 0) {\n this.durationUs = 0;\n return;\n }\n\n // Since clips are sorted, last clip determines duration\n const lastClip = mainTrack.clips[mainTrack.clips.length - 1];\n this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n /**\n * One entrance preset for the whole caption track: first caption clip on the\n * timeline (by startUs) that defines `animation` wins; all sunk caption\n * attachments reuse it so compose matches a global track style.\n */\n private getSharedCaptionTrackAnimation(track: Track): CaptionClip['animation'] | undefined {\n if (track.kind !== 'caption') return undefined;\n for (const clip of track.clips) {\n if (isCaptionClip(clip) && clip.animation != null) {\n return clip.animation;\n }\n }\n return undefined;\n }\n\n private sinkAttachmentTracks(): void {\n let mainTrack: Track | undefined;\n const attachmentTracks = [];\n\n // Sort all tracks\n for (const track of this.tracks) {\n track.clips.sort((a, b) => a.startUs - b.startUs);\n\n if (track.id === this.mainTrackId) {\n mainTrack = track;\n continue;\n }\n // Collect attachment tracks for sinking (caption, overlay, fx)\n // Video and audio tracks are not sunk\n if (track.kind === 'caption' || track.kind === 'overlay' || track.kind === 'fx') {\n attachmentTracks.push(track);\n }\n }\n\n if (!mainTrack) {\n throw new Error('Main track not found');\n }\n\n if (attachmentTracks.length === 0) {\n return;\n }\n\n // Clear existing attachments before sinking (in case of rebuild)\n for (const clip of mainTrack.clips) {\n clip.attachments = [];\n }\n\n for (const attachmentTrack of attachmentTracks) {\n const sharedCaptionAnimation =\n attachmentTrack.kind === 'caption'\n ? this.getSharedCaptionTrackAnimation(attachmentTrack)\n : undefined;\n\n for (const attachmentClip of attachmentTrack.clips) {\n // Use track.kind directly as attachment kind\n const attachmentKind = attachmentTrack.kind;\n\n for (const mainClip of mainTrack.clips) {\n const overlap = this.getTimeOverlap(attachmentClip, mainClip);\n if (!overlap) continue;\n\n if (!mainClip.attachments) {\n mainClip.attachments = [];\n }\n\n // Extract animation effect\n const animationEffect = attachmentClip.effects?.find(\n (e) => e.effectType === 'animation'\n ) as AnimationEffect | undefined;\n\n const attachmentData: Record<string, unknown> = {\n animation: animationEffect?.params,\n overlayClipStartUs: attachmentClip.startUs,\n mainClipStartUs: mainClip.startUs,\n };\n\n // Add renderConfig if valid\n const filteredRenderConfig = filterRenderConfig(\n 'renderConfig' in attachmentClip ? attachmentClip.renderConfig : undefined,\n `attachment clip ${attachmentClip.id}`\n );\n if (filteredRenderConfig) {\n attachmentData.renderConfig = filteredRenderConfig;\n }\n\n // Add resourceId if exists (trackKind not yet set, check field directly)\n if ('resourceId' in attachmentClip && attachmentClip.resourceId) {\n attachmentData.resourceId = attachmentClip.resourceId;\n }\n\n // Add text if exists (trackKind not yet set, check field directly)\n if ('text' in attachmentClip && attachmentClip.text) {\n attachmentData.text = attachmentClip.text;\n }\n\n if (isCaptionClip(attachmentClip)) {\n if (attachmentClip.fontFamily) {\n attachmentData.fontFamily = attachmentClip.fontFamily;\n }\n if (attachmentClip.localeCode) {\n attachmentData.localeCode = attachmentClip.localeCode;\n }\n if (attachmentClip.fontTemplate) {\n attachmentData.fontTemplate = attachmentClip.fontTemplate;\n }\n if (attachmentClip.fontSize != null) {\n attachmentData.fontSize = attachmentClip.fontSize;\n }\n if (attachmentClip.fontWeight != null) {\n attachmentData.fontWeight = attachmentClip.fontWeight;\n }\n if (attachmentClip.fill != null) {\n attachmentData.fill = attachmentClip.fill;\n }\n if (attachmentClip.wordTimings) {\n attachmentData.wordTimings = attachmentClip.wordTimings;\n }\n const captionAnimation = sharedCaptionAnimation ?? attachmentClip.animation;\n if (captionAnimation) {\n attachmentData.animation = captionAnimation;\n }\n if (attachmentClip.letterCase) {\n attachmentData.letterCase = attachmentClip.letterCase;\n }\n }\n\n const newAttachment = {\n id: `${attachmentKind}-${attachmentClip.id}-${mainClip.id}`,\n kind: attachmentKind,\n startUs: overlap.clipRelativeStart,\n durationUs: overlap.duration,\n data: attachmentData,\n };\n\n mainClip.attachments.push(newAttachment);\n }\n }\n }\n }\n\n private getTimeOverlap(\n attachmentClip: Clip,\n mainClip: Clip\n ): { clipRelativeStart: number; duration: number } | null {\n const attachmentStart = attachmentClip.startUs;\n const attachmentEnd = attachmentClip.startUs + attachmentClip.durationUs;\n const mainStart = mainClip.startUs;\n const mainEnd = mainClip.startUs + mainClip.durationUs;\n\n if (attachmentEnd <= mainStart || attachmentStart >= mainEnd) {\n return null;\n }\n\n const overlapStart = Math.max(attachmentStart, mainStart);\n const overlapEnd = Math.min(attachmentEnd, mainEnd);\n const duration = overlapEnd - overlapStart;\n const clipRelativeStart = overlapStart - mainStart;\n\n return { clipRelativeStart, duration };\n }\n}\n"],"names":[],"mappings":"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"}
@@ -78,13 +78,16 @@ export interface CaptionClip extends BaseClip {
78
78
  localeCode?: string;
79
79
  fontTemplate?: string;
80
80
  fontFamily?: string;
81
+ fontSize?: number;
82
+ fontWeight?: string | number;
83
+ fill?: string;
81
84
  wordTimings?: Array<{
82
85
  text: string;
83
86
  startUs: TimeUs;
84
87
  endUs: TimeUs;
85
88
  }>;
86
89
  animation?: {
87
- type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';
90
+ type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp' | 'slideUp' | 'scale' | 'rotateScale' | 'blur' | 'flip3d' | 'typewriter' | 'letterSpread';
88
91
  [key: string]: unknown;
89
92
  };
90
93
  letterCase?: 'upper' | 'lower' | 'none';
@@ -161,13 +164,16 @@ export interface CaptionAttachmentData {
161
164
  localeCode?: string;
162
165
  fontTemplate?: string;
163
166
  fontFamily?: string;
167
+ fontSize?: number;
168
+ fontWeight?: string | number;
169
+ fill?: string;
164
170
  wordTimings?: Array<{
165
171
  text: string;
166
172
  startUs: TimeUs;
167
173
  endUs: TimeUs;
168
174
  }>;
169
175
  animation?: {
170
- type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';
176
+ type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp' | 'slideUp' | 'scale' | 'rotateScale' | 'blur' | 'flip3d' | 'typewriter' | 'letterSpread';
171
177
  [key: string]: unknown;
172
178
  };
173
179
  letterCase?: 'upper' | 'lower' | 'none';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/model/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/model/types.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,9 @@ function isVideoClip(clip) {
4
4
  function isAudioClip(clip) {
5
5
  return clip.trackKind === "audio";
6
6
  }
7
+ function isCaptionClip(clip) {
8
+ return clip.trackKind === "caption";
9
+ }
7
10
  function hasResourceId(clip) {
8
11
  return isVideoClip(clip) || isAudioClip(clip);
9
12
  }
@@ -26,6 +29,7 @@ export {
26
29
  hasAudioConfig,
27
30
  hasResourceId,
28
31
  isAudioClip,
32
+ isCaptionClip,
29
33
  isVideoClip,
30
34
  videoClipPlaybackRate
31
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/model/types.ts"],"sourcesContent":["// All time values in microseconds (µs)\nexport type TimeUs = number; // 1 second = 1_000_000 µs\n\n// Helper constants\nexport const MICROSECONDS_PER_SECOND = 1_000_000;\nexport const MICROSECONDS_PER_MILLISECOND = 1_000;\n\n// ────── Root Object ──────\nexport interface CompositionModelData {\n version: '1.0';\n fps: 24 | 25 | 30 | 60;\n durationUs: TimeUs;\n tracks: Track[];\n resources: Record<string, Resource>;\n\n mainTrackId?: string;\n renderConfig?: RenderConfig;\n\n ext?: Record<string, unknown>;\n}\n\nexport interface RenderConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n}\n\n// ────── Track ──────\nexport interface Track {\n id: string;\n kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx';\n clips: Clip[];\n\n effects?: Effect[];\n duckingRules?: DuckingRule[];\n}\n\n// ────── Clip ──────\n\n// Clip-level render configuration for video/image resources\nexport interface ClipRenderConfig {\n width?: number | string; // Pixels or percentage (e.g., \"10%\")\n height?: number | string;\n}\n\ninterface BaseClip {\n id: string;\n startUs: TimeUs;\n durationUs: TimeUs;\n trackId?: string;\n\n trimStartUs?: TimeUs;\n /**\n * Loop the underlying media content to fill this clip's duration.\n *\n * - AudioClip: loop audio samples\n * - VideoClip: (future) loop video frames (and embedded audio) together\n */\n loop?: boolean;\n\n effects?: Effect[];\n attachments?: Attachment[];\n\n transitionIn?: Transition;\n transitionOut?: Transition;\n\n metadata?: {\n purpose?: 'caption' | 'overlay' | 'mask';\n [key: string]: unknown;\n };\n\n // Internal: temporary field for tracking old resourceId during patch operations\n oldResourceId?: string;\n}\n\nexport interface VideoClip extends BaseClip {\n trackKind: 'video';\n resourceId: string;\n\n /**\n * Timeline speed: source microseconds consumed per timeline microsecond (1 = normal).\n * Effective source span for the clip is durationUs * playbackRate from trimStartUs.\n */\n playbackRate?: number;\n\n // Render configuration (size, positioning strategy)\n renderConfig?: ClipRenderConfig;\n\n // Audio configuration for video's original sound\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface AudioClip extends BaseClip {\n trackKind: 'audio';\n resourceId: string;\n\n // Audio configuration\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface CaptionClip extends BaseClip {\n trackKind: 'caption';\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n}\n\nexport interface FxClip extends BaseClip {\n trackKind: 'fx';\n}\n\nexport interface OverlayClip extends BaseClip {\n trackKind: 'overlay';\n resourceId: string;\n\n // Render configuration (size, positioning strategy)\n renderConfig?: ClipRenderConfig;\n\n // Optional overlay-specific config\n opacity?: number; // 0.0-1.0, default 1.0\n}\n\nexport type Clip = VideoClip | AudioClip | CaptionClip | OverlayClip | FxClip;\n\n// ────── Resource ──────\nexport interface Resource {\n id: string;\n type: 'video' | 'image' | 'audio' | 'json' | string;\n uri: string;\n metadata?: Record<string, unknown>;\n clipIds?: string[];\n // Runtime state maintained by engine\n state?: 'pending' | 'loading' | 'ready' | 'error';\n // Runtime error info maintained by engine (best-effort, non-persistent)\n error?: {\n code: string;\n message: string;\n terminal?: boolean;\n };\n}\n\n// ────── Common Structures ──────\nexport interface Effect {\n id: string;\n effectType: 'filter' | 'lut' | 'animation' | string;\n params?: Record<string, unknown>;\n}\n\n// Animation effect (effectType: 'animation')\nexport interface AnimationEffect extends Effect {\n effectType: 'animation';\n params: {\n position: { x: number; y: number };\n keyframes: AnimationKeyframe[];\n };\n}\n\nexport interface AnimationKeyframe {\n time: number; // Relative to clip.startUs (microseconds)\n transform?: Transform2D;\n opacity?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n}\n\nexport interface Transform2D {\n x?: number; // Relative offset\n y?: number;\n scaleX?: number;\n scaleY?: number;\n rotation?: number; // Degrees\n anchorX?: number; // Rotation center x (0-1)\n anchorY?: number; // Rotation center y (0-1)\n}\n\nexport interface Transition {\n id: string;\n transitionType: 'fade' | 'wipe' | 'slide' | string;\n durationUs: TimeUs;\n curve?: 'linear' | 'ease-in' | 'ease-out' | string;\n params?: Record<string, unknown>;\n}\n\nexport interface Attachment {\n id: string;\n kind: 'caption' | 'overlay' | 'mask' | string;\n startUs: TimeUs;\n durationUs: TimeUs;\n data: Record<string, unknown>;\n}\n\nexport interface CaptionAttachmentData {\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n overlayClipStartUs?: TimeUs;\n mainClipStartUs?: TimeUs;\n}\n\nexport interface DuckingRule {\n targetTrackKind: 'voice' | 'audio' | string;\n ratio: number;\n attackMs: number;\n releaseMs: number;\n}\n\n// ────── Patch System ──────\nexport interface CompositionPatch {\n operations: PatchOperation[];\n metadata?: {\n timestamp: number;\n source?: string;\n version?: string;\n };\n}\n\nexport type PatchOperation =\n | TrackOperation\n | ClipOperation\n | ResourceOperation\n | AttachmentOperation\n | TransitionOperation\n | EffectOperation\n | RenderConfigOperation;\n\n// Track operations\nexport interface TrackOperation {\n type: 'addTrack' | 'updateTrack' | 'removeTrack';\n trackId?: string;\n track?: Partial<Track>;\n}\n\n// Clip operations\nexport interface ClipOperation {\n type: 'addClip' | 'updateClip' | 'removeClip' | 'moveClip';\n trackId: string;\n clipId?: string;\n clip?: Partial<Clip>;\n targetTrackId?: string;\n targetStartUs?: TimeUs;\n}\n\n// Resource operations\nexport interface ResourceOperation {\n type: 'addResource' | 'updateResource' | 'removeResource';\n resourceId: string;\n resource?: Partial<Resource>;\n}\n\n// Attachment operations\nexport interface AttachmentOperation {\n type: 'addAttachment' | 'updateAttachment' | 'removeAttachment';\n trackId: string;\n clipId: string;\n attachmentId?: string;\n attachment?: Partial<Attachment>;\n}\n\n// Transition operations\nexport interface TransitionOperation {\n type: 'addTransition' | 'updateTransition' | 'removeTransition';\n trackId: string;\n clipId: string;\n position: 'in' | 'out';\n transition?: Partial<Transition>;\n}\n\n// Render config operations\nexport interface RenderConfigOperation {\n type: 'updateRenderConfig';\n renderConfig?: Partial<RenderConfig>;\n}\n\n// Effect operations\nexport interface EffectOperation {\n type: 'addEffect' | 'updateEffect' | 'removeEffect';\n targetType: 'track' | 'clip';\n targetId: string;\n effectId?: string;\n effect?: Partial<Effect>;\n}\n\n// ────── Dirty Range ──────\nexport interface DirtyRange {\n trackId: string;\n startUs: TimeUs;\n endUs: TimeUs;\n reason: string;\n}\n\n// ────── Validation ──────\nexport interface ValidationError {\n path: string;\n message: string;\n value: any;\n}\n\n// ────── Type Guards ──────\nexport function isVideoClip(clip: Clip): clip is VideoClip {\n return clip.trackKind === 'video';\n}\n\nexport function isAudioClip(clip: Clip): clip is AudioClip {\n return clip.trackKind === 'audio';\n}\n\nexport function isCaptionClip(clip: Clip): clip is CaptionClip {\n return clip.trackKind === 'caption';\n}\n\nexport function isFxClip(clip: Clip): clip is FxClip {\n return clip.trackKind === 'fx';\n}\n\nexport function hasResourceId(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n\nexport function hasAudioConfig(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n\nconst PLAYBACK_RATE_MIN = 0.05;\nconst PLAYBACK_RATE_MAX = 32;\n\nexport function videoClipPlaybackRate(clip: Clip): number {\n if (!isVideoClip(clip)) {\n return 1;\n }\n const r = clip.playbackRate;\n if (typeof r !== 'number' || !Number.isFinite(r) || r <= 0) {\n return 1;\n }\n return Math.min(Math.max(r, PLAYBACK_RATE_MIN), PLAYBACK_RATE_MAX);\n}\n"],"names":[],"mappings":"AAqUO,SAAS,YAAY,MAA+B;AACzD,SAAO,KAAK,cAAc;AAC5B;AAEO,SAAS,YAAY,MAA+B;AACzD,SAAO,KAAK,cAAc;AAC5B;AAUO,SAAS,cAAc,MAA2C;AACvE,SAAO,YAAY,IAAI,KAAK,YAAY,IAAI;AAC9C;AAEO,SAAS,eAAe,MAA2C;AACxE,SAAO,YAAY,IAAI,KAAK,YAAY,IAAI;AAC9C;AAEA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAEnB,SAAS,sBAAsB,MAAoB;AACxD,MAAI,CAAC,YAAY,IAAI,GAAG;AACtB,WAAO;AAAA,EACT;AACA,QAAM,IAAI,KAAK;AACf,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,GAAG;AAC1D,WAAO;AAAA,EACT;AACA,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,iBAAiB,GAAG,iBAAiB;AACnE;"}
1
+ {"version":3,"file":"types.js","sources":["../../src/model/types.ts"],"sourcesContent":["// All time values in microseconds (µs)\nexport type TimeUs = number; // 1 second = 1_000_000 µs\n\n// Helper constants\nexport const MICROSECONDS_PER_SECOND = 1_000_000;\nexport const MICROSECONDS_PER_MILLISECOND = 1_000;\n\n// ────── Root Object ──────\nexport interface CompositionModelData {\n version: '1.0';\n fps: 24 | 25 | 30 | 60;\n durationUs: TimeUs;\n tracks: Track[];\n resources: Record<string, Resource>;\n\n mainTrackId?: string;\n renderConfig?: RenderConfig;\n\n ext?: Record<string, unknown>;\n}\n\nexport interface RenderConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n}\n\n// ────── Track ──────\nexport interface Track {\n id: string;\n kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx';\n clips: Clip[];\n\n effects?: Effect[];\n duckingRules?: DuckingRule[];\n}\n\n// ────── Clip ──────\n\n// Clip-level render configuration for video/image resources\nexport interface ClipRenderConfig {\n width?: number | string; // Pixels or percentage (e.g., \"10%\")\n height?: number | string;\n}\n\ninterface BaseClip {\n id: string;\n startUs: TimeUs;\n durationUs: TimeUs;\n trackId?: string;\n\n trimStartUs?: TimeUs;\n /**\n * Loop the underlying media content to fill this clip's duration.\n *\n * - AudioClip: loop audio samples\n * - VideoClip: (future) loop video frames (and embedded audio) together\n */\n loop?: boolean;\n\n effects?: Effect[];\n attachments?: Attachment[];\n\n transitionIn?: Transition;\n transitionOut?: Transition;\n\n metadata?: {\n purpose?: 'caption' | 'overlay' | 'mask';\n [key: string]: unknown;\n };\n\n // Internal: temporary field for tracking old resourceId during patch operations\n oldResourceId?: string;\n}\n\nexport interface VideoClip extends BaseClip {\n trackKind: 'video';\n resourceId: string;\n\n /**\n * Timeline speed: source microseconds consumed per timeline microsecond (1 = normal).\n * Effective source span for the clip is durationUs * playbackRate from trimStartUs.\n */\n playbackRate?: number;\n\n // Render configuration (size, positioning strategy)\n renderConfig?: ClipRenderConfig;\n\n // Audio configuration for video's original sound\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface AudioClip extends BaseClip {\n trackKind: 'audio';\n resourceId: string;\n\n // Audio configuration\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface CaptionClip extends BaseClip {\n trackKind: 'caption';\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n fontSize?: number;\n fontWeight?: string | number;\n fill?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type:\n | 'none'\n | 'fade'\n | 'wordByWord'\n | 'characterKTV'\n | 'wordByWordFancy'\n | 'wordByWordSlideUp'\n | 'slideUp'\n | 'scale'\n | 'rotateScale'\n | 'blur'\n | 'flip3d'\n | 'typewriter'\n | 'letterSpread';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n}\n\nexport interface FxClip extends BaseClip {\n trackKind: 'fx';\n}\n\nexport interface OverlayClip extends BaseClip {\n trackKind: 'overlay';\n resourceId: string;\n\n // Render configuration (size, positioning strategy)\n renderConfig?: ClipRenderConfig;\n\n // Optional overlay-specific config\n opacity?: number; // 0.0-1.0, default 1.0\n}\n\nexport type Clip = VideoClip | AudioClip | CaptionClip | OverlayClip | FxClip;\n\n// ────── Resource ──────\nexport interface Resource {\n id: string;\n type: 'video' | 'image' | 'audio' | 'json' | string;\n uri: string;\n metadata?: Record<string, unknown>;\n clipIds?: string[];\n // Runtime state maintained by engine\n state?: 'pending' | 'loading' | 'ready' | 'error';\n // Runtime error info maintained by engine (best-effort, non-persistent)\n error?: {\n code: string;\n message: string;\n terminal?: boolean;\n };\n}\n\n// ────── Common Structures ──────\nexport interface Effect {\n id: string;\n effectType: 'filter' | 'lut' | 'animation' | string;\n params?: Record<string, unknown>;\n}\n\n// Animation effect (effectType: 'animation')\nexport interface AnimationEffect extends Effect {\n effectType: 'animation';\n params: {\n position: { x: number; y: number };\n keyframes: AnimationKeyframe[];\n };\n}\n\nexport interface AnimationKeyframe {\n time: number; // Relative to clip.startUs (microseconds)\n transform?: Transform2D;\n opacity?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n}\n\nexport interface Transform2D {\n x?: number; // Relative offset\n y?: number;\n scaleX?: number;\n scaleY?: number;\n rotation?: number; // Degrees\n anchorX?: number; // Rotation center x (0-1)\n anchorY?: number; // Rotation center y (0-1)\n}\n\nexport interface Transition {\n id: string;\n transitionType: 'fade' | 'wipe' | 'slide' | string;\n durationUs: TimeUs;\n curve?: 'linear' | 'ease-in' | 'ease-out' | string;\n params?: Record<string, unknown>;\n}\n\nexport interface Attachment {\n id: string;\n kind: 'caption' | 'overlay' | 'mask' | string;\n startUs: TimeUs;\n durationUs: TimeUs;\n data: Record<string, unknown>;\n}\n\nexport interface CaptionAttachmentData {\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n fontSize?: number;\n fontWeight?: string | number;\n fill?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type:\n | 'none'\n | 'fade'\n | 'wordByWord'\n | 'characterKTV'\n | 'wordByWordFancy'\n | 'wordByWordSlideUp'\n | 'slideUp'\n | 'scale'\n | 'rotateScale'\n | 'blur'\n | 'flip3d'\n | 'typewriter'\n | 'letterSpread';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n overlayClipStartUs?: TimeUs;\n mainClipStartUs?: TimeUs;\n}\n\nexport interface DuckingRule {\n targetTrackKind: 'voice' | 'audio' | string;\n ratio: number;\n attackMs: number;\n releaseMs: number;\n}\n\n// ────── Patch System ──────\nexport interface CompositionPatch {\n operations: PatchOperation[];\n metadata?: {\n timestamp: number;\n source?: string;\n version?: string;\n };\n}\n\nexport type PatchOperation =\n | TrackOperation\n | ClipOperation\n | ResourceOperation\n | AttachmentOperation\n | TransitionOperation\n | EffectOperation\n | RenderConfigOperation;\n\n// Track operations\nexport interface TrackOperation {\n type: 'addTrack' | 'updateTrack' | 'removeTrack';\n trackId?: string;\n track?: Partial<Track>;\n}\n\n// Clip operations\nexport interface ClipOperation {\n type: 'addClip' | 'updateClip' | 'removeClip' | 'moveClip';\n trackId: string;\n clipId?: string;\n clip?: Partial<Clip>;\n targetTrackId?: string;\n targetStartUs?: TimeUs;\n}\n\n// Resource operations\nexport interface ResourceOperation {\n type: 'addResource' | 'updateResource' | 'removeResource';\n resourceId: string;\n resource?: Partial<Resource>;\n}\n\n// Attachment operations\nexport interface AttachmentOperation {\n type: 'addAttachment' | 'updateAttachment' | 'removeAttachment';\n trackId: string;\n clipId: string;\n attachmentId?: string;\n attachment?: Partial<Attachment>;\n}\n\n// Transition operations\nexport interface TransitionOperation {\n type: 'addTransition' | 'updateTransition' | 'removeTransition';\n trackId: string;\n clipId: string;\n position: 'in' | 'out';\n transition?: Partial<Transition>;\n}\n\n// Render config operations\nexport interface RenderConfigOperation {\n type: 'updateRenderConfig';\n renderConfig?: Partial<RenderConfig>;\n}\n\n// Effect operations\nexport interface EffectOperation {\n type: 'addEffect' | 'updateEffect' | 'removeEffect';\n targetType: 'track' | 'clip';\n targetId: string;\n effectId?: string;\n effect?: Partial<Effect>;\n}\n\n// ────── Dirty Range ──────\nexport interface DirtyRange {\n trackId: string;\n startUs: TimeUs;\n endUs: TimeUs;\n reason: string;\n}\n\n// ────── Validation ──────\nexport interface ValidationError {\n path: string;\n message: string;\n value: any;\n}\n\n// ────── Type Guards ──────\nexport function isVideoClip(clip: Clip): clip is VideoClip {\n return clip.trackKind === 'video';\n}\n\nexport function isAudioClip(clip: Clip): clip is AudioClip {\n return clip.trackKind === 'audio';\n}\n\nexport function isCaptionClip(clip: Clip): clip is CaptionClip {\n return clip.trackKind === 'caption';\n}\n\nexport function isFxClip(clip: Clip): clip is FxClip {\n return clip.trackKind === 'fx';\n}\n\nexport function hasResourceId(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n\nexport function hasAudioConfig(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n\nconst PLAYBACK_RATE_MIN = 0.05;\nconst PLAYBACK_RATE_MAX = 32;\n\nexport function videoClipPlaybackRate(clip: Clip): number {\n if (!isVideoClip(clip)) {\n return 1;\n }\n const r = clip.playbackRate;\n if (typeof r !== 'number' || !Number.isFinite(r) || r <= 0) {\n return 1;\n }\n return Math.min(Math.max(r, PLAYBACK_RATE_MIN), PLAYBACK_RATE_MAX);\n}\n"],"names":[],"mappings":"AAqWO,SAAS,YAAY,MAA+B;AACzD,SAAO,KAAK,cAAc;AAC5B;AAEO,SAAS,YAAY,MAA+B;AACzD,SAAO,KAAK,cAAc;AAC5B;AAEO,SAAS,cAAc,MAAiC;AAC7D,SAAO,KAAK,cAAc;AAC5B;AAMO,SAAS,cAAc,MAA2C;AACvE,SAAO,YAAY,IAAI,KAAK,YAAY,IAAI;AAC9C;AAEO,SAAS,eAAe,MAA2C;AACxE,SAAO,YAAY,IAAI,KAAK,YAAY,IAAI;AAC9C;AAEA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAEnB,SAAS,sBAAsB,MAAoB;AACxD,MAAI,CAAC,YAAY,IAAI,GAAG;AACtB,WAAO;AAAA,EACT;AACA,QAAM,IAAI,KAAK;AACf,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,GAAG;AAC1D,WAAO;AAAA,EACT;AACA,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,iBAAiB,GAAG,iBAAiB;AACnE;"}
@@ -47,6 +47,7 @@ export declare class CompositionPlanner {
47
47
  private buildTransitionPlans;
48
48
  private transitionToPlan;
49
49
  private getStringField;
50
+ private getNumberField;
50
51
  private needsPlanRefresh;
51
52
  }
52
53
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"CompositionPlanner.d.ts","sourceRoot":"","sources":["../../src/orchestrator/CompositionPlanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,IAAI,EAKL,MAAM,UAAU,CAAC;AAGlB,OAAO,KAAK,EACV,kBAAkB,EAQnB,MAAM,gCAAgC,CAAC;AAIxC,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC;IACrB,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAcD,UAAU,oBAAoB;IAC5B,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,UAAU,QAAQ;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IAEzD,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAKvC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAwB1D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIjC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAsBpD;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,gBAAgB,EAAE;IAoEtF,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,QAAQ;IAqBlE,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,oBAAoB;IAoC5B,OAAO,CAAC,qBAAqB;IAiC7B,OAAO,CAAC,0BAA0B;IAiBlC,OAAO,CAAC,sBAAsB;IA4F9B,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,gBAAgB;CAQzB"}
1
+ {"version":3,"file":"CompositionPlanner.d.ts","sourceRoot":"","sources":["../../src/orchestrator/CompositionPlanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,IAAI,EAKL,MAAM,UAAU,CAAC;AAGlB,OAAO,KAAK,EACV,kBAAkB,EAQnB,MAAM,gCAAgC,CAAC;AAIxC,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC;IACrB,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAcD,UAAU,oBAAoB;IAC5B,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,UAAU,QAAQ;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IAEzD,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAKvC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAwB1D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIjC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAsBpD;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,gBAAgB,EAAE;IAoEtF,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,QAAQ;IAqBlE,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,oBAAoB;IAoC5B,OAAO,CAAC,qBAAqB;IAiC7B,OAAO,CAAC,0BAA0B;IAiBlC,OAAO,CAAC,sBAAsB;IA0G9B,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,gBAAgB;CAQzB"}
@@ -283,7 +283,21 @@ class CompositionPlanner {
283
283
  if (!fontTemplate) {
284
284
  fontTemplate = this.getDefaultFontTemplate(localeCode);
285
285
  }
286
- const fontConfig = getFontConfig(localeCode, fontTemplate, fontFamily);
286
+ const baseFontConfig = getFontConfig(localeCode, fontTemplate, fontFamily);
287
+ const sizeOverride = this.getNumberField(attachment.data, "fontSize");
288
+ const fillOverride = this.getStringField(attachment.data, "fill");
289
+ const wRaw = attachment.data.fontWeight;
290
+ const textStyle = { ...baseFontConfig.textStyle };
291
+ if (sizeOverride != null) {
292
+ textStyle.fontSize = sizeOverride;
293
+ }
294
+ if (wRaw != null && (typeof wRaw === "string" || typeof wRaw === "number")) {
295
+ textStyle.fontWeight = wRaw;
296
+ }
297
+ if (fillOverride) {
298
+ textStyle.fill = fillOverride;
299
+ }
300
+ const fontConfig = { ...baseFontConfig, textStyle };
287
301
  const payload = {
288
302
  text,
289
303
  localeCode,
@@ -400,6 +414,13 @@ class CompositionPlanner {
400
414
  const value = data[key];
401
415
  return typeof value === "string" ? value : void 0;
402
416
  }
417
+ getNumberField(data, key) {
418
+ const value = data[key];
419
+ if (typeof value === "number" && !Number.isNaN(value)) {
420
+ return value;
421
+ }
422
+ return void 0;
423
+ }
403
424
  needsPlanRefresh(clip, plan) {
404
425
  const currentAttachmentCount = (clip.attachments ?? []).length;
405
426
  const cachedAttachmentCount = plan.instructions.layers.filter(