@bendyline/squisq 2.11.7 → 2.11.8

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 (38) hide show
  1. package/dist/{Doc-BkxT7G80.d.ts → Doc-CynsGqL4.d.ts} +208 -1
  2. package/dist/{ImageEditDoc-C8ZB54Vv.d.ts → ImageEditDoc-BVZe_5y4.d.ts} +1 -1
  3. package/dist/{chunk-SKKBRLLM.js → chunk-5FBI3ZW4.js} +7 -42
  4. package/dist/{chunk-YGGYYBJS.js → chunk-JJO6JFNZ.js} +1 -1
  5. package/dist/{chunk-SOIV7KDO.js → chunk-JM2QARY2.js} +42 -10
  6. package/dist/chunk-JSGIWW35.js +70 -0
  7. package/dist/{chunk-IYZTF4VO.js → chunk-KFZOCWPK.js} +2 -2
  8. package/dist/chunk-L7QZORLB.js +230 -0
  9. package/dist/chunk-NEWNNLWN.js +64 -0
  10. package/dist/{chunk-6ECMNUIP.js → chunk-OU5FYBOB.js} +170 -4
  11. package/dist/{chunk-3YNFMFNK.js → chunk-RMCUZDW5.js} +17 -8
  12. package/dist/{chunk-XJXBI3N5.js → chunk-XZVBR5BP.js} +1 -1
  13. package/dist/{chunk-GAZKTT4R.js → chunk-YNO6JV36.js} +96 -33
  14. package/dist/{chunk-EF353KGH.js → chunk-ZEONJCP3.js} +1 -1
  15. package/dist/doc/index.d.ts +5 -5
  16. package/dist/doc/index.js +22 -20
  17. package/dist/fence/index.d.ts +1 -1
  18. package/dist/generate/index.d.ts +1 -1
  19. package/dist/imageEdit/index.d.ts +47 -4
  20. package/dist/imageEdit/index.js +5 -1
  21. package/dist/index.d.ts +5 -5
  22. package/dist/index.js +48 -39
  23. package/dist/jsonForm/index.d.ts +1 -1
  24. package/dist/jsonForm/index.js +4 -2
  25. package/dist/markdown/index.js +3 -3
  26. package/dist/{materializePageSection-Dp2aHFbc.d.ts → materializePageSection-CAJoa8fh.d.ts} +1 -1
  27. package/dist/mediaEdit/index.d.ts +467 -0
  28. package/dist/mediaEdit/index.js +990 -0
  29. package/dist/narration/index.d.ts +20 -12
  30. package/dist/narration/index.js +6 -4
  31. package/dist/schemas/index.d.ts +4 -4
  32. package/dist/schemas/index.js +7 -3
  33. package/dist/{themeLibrary-BbSjnGmd.d.ts → themeLibrary-z7t3x5CU.d.ts} +1 -1
  34. package/dist/timing/index.d.ts +1 -1
  35. package/dist/transform/index.d.ts +2 -2
  36. package/dist/transform/index.js +6 -5
  37. package/package.json +6 -1
  38. package/dist/{chunk-CW24CH7I.js → chunk-HUXK7EOT.js} +3 -3
@@ -1,5 +1,158 @@
1
1
  import { a3 as Transition, a5 as TransitionType, M as MarkdownBlockNode, r as MarkdownHeading } from './types-Be6sBUBx.js';
2
2
 
3
+ /**
4
+ * Media-edit recipes — the non-destructive edit list a media reference carries.
5
+ *
6
+ * A recipe lives in the markdown, as parameters on the `{[audio|video …]}`
7
+ * annotation or `data-squisq-{audio|video}-*` attributes on a standalone
8
+ * `<audio>` / `<video>` tag. The source file is never modified: timeline ops
9
+ * (`cuts`, `gain`, `fadeIn`, `fadeOut`, `crop`) are applied at playback and
10
+ * export, and the signal chain (`fx`) is rendered into a derived, time-aligned
11
+ * audio file (see `renderStore.ts`). Undo is therefore the editor's ordinary
12
+ * undo, and a reference with no recipe behaves exactly as before.
13
+ *
14
+ * Pure and dependency-free: runs in the browser and in Node.
15
+ */
16
+ /** Signal operations, in the FIXED order the chain applies them. */
17
+ declare const MEDIA_FX_ORDER: readonly ["highpass", "denoise", "debreath", "loudness"];
18
+ type MediaFxOpId = (typeof MEDIA_FX_ORDER)[number];
19
+ interface MediaFxOpSpec {
20
+ /** Value used when the token carries none (`denoise` ≡ `denoise:0.8`). */
21
+ defaultValue: number;
22
+ min: number;
23
+ max: number;
24
+ }
25
+ /**
26
+ * Parameter contract per op. Values are clamped into range on parse, so a
27
+ * recipe always describes something the engine can render.
28
+ *
29
+ * - `highpass` — corner frequency in Hz.
30
+ * - `denoise` — wet/dry strength, 0…1.
31
+ * - `debreath` — attenuation applied to detected breaths, in dB (≤ 0).
32
+ * - `loudness` — integrated loudness target in LUFS.
33
+ */
34
+ declare const MEDIA_FX_SPECS: Readonly<Record<MediaFxOpId, MediaFxOpSpec>>;
35
+ interface MediaFxOp {
36
+ id: MediaFxOpId;
37
+ value: number;
38
+ }
39
+ interface MediaFxChain {
40
+ /** Known ops, de-duplicated (last wins) and in {@link MEDIA_FX_ORDER}. */
41
+ ops: MediaFxOp[];
42
+ /**
43
+ * Tokens this engine does not understand, preserved verbatim so a recipe
44
+ * written by a newer engine survives a round-trip through an older one.
45
+ * They are never rendered.
46
+ */
47
+ unknown: string[];
48
+ }
49
+ /** A removed range of the SOURCE file, in seconds (`start < end`). */
50
+ interface MediaCut {
51
+ start: number;
52
+ end: number;
53
+ }
54
+ /** A crop rectangle, normalized to the source frame (0…1 on each axis). */
55
+ interface MediaCrop {
56
+ x: number;
57
+ y: number;
58
+ w: number;
59
+ h: number;
60
+ }
61
+ /** The complete recipe a media reference can carry. Every field is optional. */
62
+ interface MediaEdits {
63
+ fx?: MediaFxChain;
64
+ /** Normalized: sorted, merged, non-overlapping. */
65
+ cuts?: MediaCut[];
66
+ /** Clip gain in dB. */
67
+ gain?: number;
68
+ /** Fade-in length in seconds, at the start of the played clip. */
69
+ fadeIn?: number;
70
+ /** Fade-out length in seconds, at the end of the played clip. */
71
+ fadeOut?: number;
72
+ crop?: MediaCrop;
73
+ /**
74
+ * Links companion clips (a screen + camera capture, a narration audio +
75
+ * video pair). Editors propagate cuts, trims and `fx` across a group.
76
+ */
77
+ group?: string;
78
+ }
79
+ /** Raw string values of a recipe, keyed by {@link MediaEditParamKey}. */
80
+ type MediaEditParamValues = Partial<Record<MediaEditParamKey, string>>;
81
+ type MediaEditParamKey = 'fx' | 'cuts' | 'gain' | 'fadeIn' | 'fadeOut' | 'crop' | 'group';
82
+ /**
83
+ * Every recipe parameter with its two spellings: the annotation key
84
+ * (`fadeIn=0.3`) and the HTML attribute suffix, appended to the per-kind
85
+ * prefix (`data-squisq-video-fade-in="0.3"`). The order is the canonical
86
+ * serialization order.
87
+ */
88
+ declare const MEDIA_EDIT_PARAMS: ReadonlyArray<{
89
+ key: MediaEditParamKey;
90
+ htmlSuffix: string;
91
+ }>;
92
+ /** `data-squisq-video-fade-in` for (`video`, `fadeIn`). */
93
+ declare function mediaEditHtmlAttribute(kind: 'audio' | 'video', key: MediaEditParamKey): string;
94
+ /** Compact, stable decimal: at most `digits` fraction digits, no `-0`. */
95
+ declare function formatMediaNumber(value: number, digits?: number): string;
96
+ /**
97
+ * Parse an `fx` value (`"highpass:80 denoise debreath:-15"`). Tokens split on
98
+ * whitespace or commas. A known op with a missing value gets its default; a
99
+ * known op with an unparseable value is kept verbatim in `unknown` rather
100
+ * than guessed at. `debreath:15` is read as −15 dB (attenuation is the only
101
+ * meaningful direction). Returns null when the value holds no tokens.
102
+ */
103
+ declare function parseMediaFx(raw: string | undefined): MediaFxChain | null;
104
+ /**
105
+ * Canonical `fx` string: known ops in chain order with explicit values, then
106
+ * unknown tokens in their authored order. Explicit values keep a recipe's
107
+ * meaning stable even if a later engine changes a default.
108
+ */
109
+ declare function serializeMediaFx(chain: MediaFxChain): string;
110
+ /** True when the chain would render anything (it has at least one known op). */
111
+ declare function hasRenderableFx(chain: MediaFxChain | undefined): chain is MediaFxChain;
112
+ /**
113
+ * Sort, merge overlapping or touching ranges, and drop empty ones. When a
114
+ * `[windowStart, windowEnd]` source window is given (the clip's trim), cuts
115
+ * are clamped into it and anything outside is dropped.
116
+ */
117
+ declare function normalizeMediaCuts(cuts: readonly MediaCut[], windowStart?: number, windowEnd?: number): MediaCut[];
118
+ /**
119
+ * Parse `"12.4-13.1 40.2-41"` (also `1:02-1:04.5`, comma separated). Invalid
120
+ * ranges are dropped. Returns null when nothing valid remains.
121
+ */
122
+ declare function parseMediaCuts(raw: string | undefined): MediaCut[] | null;
123
+ declare function serializeMediaCuts(cuts: readonly MediaCut[]): string;
124
+ /**
125
+ * Parse `"x y w h"` (fractions of the source frame). The rectangle is clamped
126
+ * inside the frame. A full-frame rectangle means "no crop" and returns null.
127
+ */
128
+ declare function parseMediaCrop(raw: string | undefined): MediaCrop | null;
129
+ /** Clamp a crop rectangle into the frame; null when it covers the whole frame. */
130
+ declare function normalizeMediaCrop(crop: MediaCrop): MediaCrop | null;
131
+ declare function serializeMediaCrop(crop: MediaCrop): string;
132
+ /**
133
+ * Parse a recipe from raw parameter values. Invalid fields are dropped
134
+ * (never guessed); neutral values (`gain=0`, `fadeIn=0`, full-frame crop)
135
+ * are dropped too. Returns undefined when nothing remains, so callers can
136
+ * leave `MediaClip.edits` unset for an unedited reference.
137
+ */
138
+ declare function parseMediaEdits(values: MediaEditParamValues): MediaEdits | undefined;
139
+ /**
140
+ * Serialize a recipe to raw parameter values. A key maps to `null` when its
141
+ * field is absent, so a writer can remove a stale attribute with the same
142
+ * call that sets the others.
143
+ */
144
+ declare function serializeMediaEdits(edits: MediaEdits | undefined): Record<MediaEditParamKey, string | null>;
145
+ /** Read recipe values from annotation params (`fx=…`, `fadeIn=…`). */
146
+ declare function mediaEditValuesFromParams(params: Record<string, string>): MediaEditParamValues;
147
+ /** Read recipe values from HTML attributes (`data-squisq-video-fade-in=…`). */
148
+ declare function mediaEditValuesFromAttributes(kind: 'audio' | 'video', attributes: Record<string, string>): MediaEditParamValues;
149
+ /**
150
+ * Stable key naming the render of `src` under `fx`. Only the known, canonical
151
+ * ops take part — unknown tokens are not rendered, so they must not force a
152
+ * re-render (the render manifest records them instead). 12 hex digits.
153
+ */
154
+ declare function mediaRenderKey(src: string, fx: MediaFxChain): string;
155
+
3
156
  /**
4
157
  * Media-clip timing model.
5
158
  *
@@ -76,6 +229,11 @@ interface MediaClip {
76
229
  * timed relative to the document start and able to span every block.
77
230
  */
78
231
  anchor: 'block' | 'document';
232
+ /**
233
+ * Non-destructive edit recipe (`fx`, `cuts`, `gain`, fades, `crop`,
234
+ * `group`). Absent for an unedited reference. See `@bendyline/squisq/mediaEdit`.
235
+ */
236
+ edits?: MediaEdits;
79
237
  /**
80
238
  * 1-based source line of the authoring annotation, when derived from
81
239
  * markdown. Enables round-tripping edits (e.g. the timeline editor rewriting
@@ -128,6 +286,36 @@ interface ScheduledClip {
128
286
  blockId?: string;
129
287
  /** 1-based source line of the authoring annotation, when known. */
130
288
  sourceLine?: number;
289
+ /** Clip gain in dB (from the recipe). */
290
+ gain?: number;
291
+ /** Fade-in seconds at this entry's start (first segment of a cut clip only). */
292
+ fadeIn?: number;
293
+ /** Fade-out seconds at this entry's end (last segment of a cut clip only). */
294
+ fadeOut?: number;
295
+ /** Normalized crop rectangle for a video clip. */
296
+ crop?: MediaCrop;
297
+ /**
298
+ * True when a video's own audio track must stay silent because a processed
299
+ * companion audio entry (see {@link ScheduledClip.derivedFrom}) plays in its place.
300
+ */
301
+ audioMuted?: boolean;
302
+ /**
303
+ * Set on the processed companion audio entry emitted for an edited video:
304
+ * the id of the clip whose audio it replaces. Timeline UIs hide these.
305
+ */
306
+ derivedFrom?: string;
307
+ /** The source reference before processed-audio substitution. */
308
+ originalSrc?: string;
309
+ /**
310
+ * Present when a clip with cuts was expanded into contiguous entries, one
311
+ * per kept source range. `groupId` is the authored clip's id; entries of
312
+ * one group play the same source back to back.
313
+ */
314
+ segment?: {
315
+ groupId: string;
316
+ index: number;
317
+ count: number;
318
+ };
131
319
  }
132
320
  /** Options for {@link resolveMediaSchedule} / {@link getDocPlaybackDuration}. */
133
321
  interface MediaScheduleOptions {
@@ -146,7 +334,26 @@ interface MediaScheduleOptions {
146
334
  * block by design.
147
335
  */
148
336
  intrinsicDuration?: (clip: MediaClip) => number | undefined;
337
+ /**
338
+ * Optional lookup of a clip's processed (rendered `fx`) audio. When it
339
+ * returns a path, an audio clip plays that path instead of its source, and a
340
+ * video clip keeps its picture but mutes its own audio while a companion
341
+ * audio entry plays the processed track with identical timing. Renders are
342
+ * sample-aligned with the source, so no timing changes.
343
+ *
344
+ * Return undefined when no current render exists (the default when no
345
+ * lookup is wired): the clip then plays its original audio.
346
+ */
347
+ processedAudio?: (clip: MediaClip) => string | undefined;
149
348
  }
349
+ /**
350
+ * Schedule one clip that occupies `[start, end)` on the doc timeline, honoring
351
+ * its recipe (cuts → contiguous segment entries, gain/fades/crop, processed
352
+ * audio). {@link resolveMediaSchedule} uses this for every annotated clip;
353
+ * hosts that schedule media from other sources (body-embedded `<audio>` /
354
+ * `<video>` tags) call it so edits behave identically there.
355
+ */
356
+ declare function scheduleMediaClip(clip: MediaClip, start: number, end: number, anchor: 'block' | 'document', blockId?: string, opts?: MediaScheduleOptions): ScheduledClip[];
150
357
  /**
151
358
  * Flatten every block clip and document clip into absolute-timed
152
359
  * `ScheduledClip`s. Pure — depends only on `doc` (plus the optional intrinsic
@@ -2847,4 +3054,4 @@ declare function getBlockAtTime(blocks: Block[], time: number): Block | null;
2847
3054
  */
2848
3055
  declare function getCaptionAtTime(captions: CaptionTrack | undefined, time: number): CaptionPhrase | null;
2849
3056
 
2850
- export { type ImageLayer as $, type AccentImage as A, type BarChartInput as B, type CaptionPhrase as C, DARK_SURFACE as D, DEFAULT_DOC_FONT as E, DEFAULT_TITLE_FONT as F, type DataTableInput as G, type DateEventInput as H, type DeepPartial as I, type DefinitionCardInput as J, type DiagramBlockInput as K, type DiagramEdgeAnchor as L, type DiagramTemplateEdge as M, type DiagramTemplateNode as N, type Doc as O, type DocBlock as P, type DocDiagnostic as Q, type DonutChartInput as R, type DrawingBlockInput as S, FRONTMATTER_CUSTOM_TEMPLATES_KEY as T, FRONTMATTER_CUSTOM_THEMES_KEY as U, type FactCardInput as V, type FontFamily as W, type FontFamilyKind as X, type FullBleedQuoteInput as Y, type GradientBackgroundConfig as Z, type ImageBackgroundConfig as _, type AccentPosition as a, type ScheduledClip as a$, type ImageTreatment as a0, type ImageWithCaptionInput as a1, LIGHT_SURFACE as a2, type Layer as a3, type LayerRepeat as a4, type LayoutHints as a5, type LeftFeatureInput as a6, type LineChartInput as a7, type LinearGradient as a8, type ListBlockInput as a9, type PageAccentRotation as aA, type PageBackground as aB, type PageDesignFamily as aC, type PageEmphasis as aD, type PageHeadingTreatment as aE, type PageSectionKind as aF, type PageSectionOverride as aG, type PageTokens as aH, type PathLayer as aI, type PatternBackgroundConfig as aJ, type PersistentLayer as aK, type PersistentLayerConfig as aL, type PersistentLayerTemplate as aM, type PersistentLayerTemplateConfig as aN, type PersistentLayerTemplateType as aO, type PhotoGridInput as aP, type PieChartInput as aQ, type PipStyle as aR, type Position as aS, type ProgressIndicatorConfig as aT, type PromotedBodyAnnotation as aU, type PullQuoteInput as aV, type QuoteBlockInput as aW, type RawLayersInput as aX, type RenderStyle as aY, type RightFeatureInput as aZ, type ScatterChartInput as a_, type MapBlockInput as aa, type MapLayer as ab, type MapMarker as ac, type MapTileStyle as ad, type MarkerStyle as ae, type MediaClip as af, type MediaScheduleOptions as ag, type MermaidLayer as ah, PAGE_ACCENT_STRATEGIES as ai, PAGE_BACKGROUNDS as aj, PAGE_BACKGROUND_RHYTHMS as ak, PAGE_DESIGN_FAMILIES as al, PAGE_DIVIDERS as am, PAGE_EMPHASES as an, PAGE_EYEBROWS as ao, PAGE_HEADING_CASES as ap, PAGE_HEADING_SCALES as aq, PAGE_HEADING_UNDERLINES as ar, PAGE_HERO_STYLES as as, PAGE_IMAGE_FRAMINGS as at, PAGE_NUMERAL_STYLES as au, PAGE_PATTERNS as av, PAGE_QUOTE_MARKS as aw, PAGE_SECTION_KINDS as ax, PAGE_SECTION_SPACINGS as ay, PAGE_SHADOWS as az, type AmbientGradientConfig as b, getSafeTextBounds as b$, type SectionHeaderInput as b0, type ShapeFilter as b1, type ShapeLayer as b2, type ShapePattern as b3, type SolidBackgroundConfig as b4, type StartBlockConfig as b5, type StatHighlightInput as b6, type SurfaceScheme as b7, THEME_SCHEMA_VERSION as b8, type TableLayer as b9, type TreeLayerStyle as bA, type TreeTemplateItem as bB, type TwoColumnInput as bC, VIEWPORT_PRESETS as bD, type VideoLayer as bE, type VideoPipPosition as bF, type VideoPipShape as bG, type VideoPipSize as bH, type VideoPlacement as bI, type VideoPresentation as bJ, type VideoPullQuoteInput as bK, type VideoWithCaptionInput as bL, type ViewportConfig as bM, type ViewportOrientation as bN, type ViewportPreset as bO, type VignetteConfig as bP, applySurface as bQ, calculateDuration as bR, calculateFontScale as bS, createTemplateContext as bT, createTheme as bU, createThemeRegistry as bV, getAspectRatioString as bW, getBlockAtTime as bX, getCaptionAtTime as bY, getDocPlaybackDuration as bZ, getLayoutHints as b_, type TableLayerStyle as ba, type TemplateBlock as bb, type TemplateContext as bc, type TemplateFunction as bd, type TemplateRegistry as be, type TextLayer as bf, type TextStyle as bg, type Theme as bh, type ThemeColorPalette as bi, type ThemeColorScheme as bj, type ThemePageStyle as bk, type ThemeRegistry as bl, type ThemeSchemaVersion as bm, type ThemeSeedColors as bn, type ThemeStyle as bo, type ThemeTypography as bp, type TimelineBlockInput as bq, type TimelineTemplateEvent as br, type TimelineTemplateLink as bs, type TimelineTemplateTrack as bt, type TitleBlockInput as bu, type TitleCaptionConfig as bv, type TranscriptBlockInput as bw, type TreeBlockInput as bx, type TreeLayer as by, type TreeLayerItem as bz, type Animation as c, getSegmentAtTime as c0, getTwoColumnPositions as c1, getViewport as c2, getViewportOrientation as c3, isCustomTemplateDefinition as c4, isPersistentLayerTemplate as c5, isTemplateBlock as c6, scaledFontSize$1 as c7, resolveMediaSchedule as c8, scaledFontSize as c9, validateCustomTemplateDefinition as ca, type AnimationType as d, type AreaChartInput as e, type AudioBookmark as f, type AudioSegment as g, type AudioTimingData as h, type AudioTrack as i, type BigTextInput as j, type Block as k, type BlockConnection as l, type BorderStyle as m, type CaptionTrack as n, type CaptionWord as o, type ChartBaseFields as p, type ChartTemplateInput as q, type ColorScheme as r, type ColumnChartInput as s, type ComparisonBarInput as t, type ContentBlockInput as u, type CornerBrandingConfig as v, type CustomTemplateDefinition as w, type CustomTemplateLayer as x, type CustomTemplateValidationError as y, type CustomTemplateValidationResult as z };
3057
+ export { type ImageLayer as $, type AccentImage as A, type BarChartInput as B, type CaptionPhrase as C, DARK_SURFACE as D, DEFAULT_DOC_FONT as E, DEFAULT_TITLE_FONT as F, type DataTableInput as G, type DateEventInput as H, type DeepPartial as I, type DefinitionCardInput as J, type DiagramBlockInput as K, type DiagramEdgeAnchor as L, type DiagramTemplateEdge as M, type DiagramTemplateNode as N, type Doc as O, type DocBlock as P, type DocDiagnostic as Q, type DonutChartInput as R, type DrawingBlockInput as S, FRONTMATTER_CUSTOM_TEMPLATES_KEY as T, FRONTMATTER_CUSTOM_THEMES_KEY as U, type FactCardInput as V, type FontFamily as W, type FontFamilyKind as X, type FullBleedQuoteInput as Y, type GradientBackgroundConfig as Z, type ImageBackgroundConfig as _, type AccentPosition as a, type RenderStyle as a$, type ImageTreatment as a0, type ImageWithCaptionInput as a1, LIGHT_SURFACE as a2, type Layer as a3, type LayerRepeat as a4, type LayoutHints as a5, type LeftFeatureInput as a6, type LineChartInput as a7, type LinearGradient as a8, type ListBlockInput as a9, PAGE_SECTION_KINDS as aA, PAGE_SECTION_SPACINGS as aB, PAGE_SHADOWS as aC, type PageAccentRotation as aD, type PageBackground as aE, type PageDesignFamily as aF, type PageEmphasis as aG, type PageHeadingTreatment as aH, type PageSectionKind as aI, type PageSectionOverride as aJ, type PageTokens as aK, type PathLayer as aL, type PatternBackgroundConfig as aM, type PersistentLayer as aN, type PersistentLayerConfig as aO, type PersistentLayerTemplate as aP, type PersistentLayerTemplateConfig as aQ, type PersistentLayerTemplateType as aR, type PhotoGridInput as aS, type PieChartInput as aT, type PipStyle as aU, type Position as aV, type ProgressIndicatorConfig as aW, type PromotedBodyAnnotation as aX, type PullQuoteInput as aY, type QuoteBlockInput as aZ, type RawLayersInput as a_, type MapBlockInput as aa, type MapLayer as ab, type MapMarker as ac, type MapTileStyle as ad, type MarkerStyle as ae, type MediaClip as af, type MediaCrop as ag, type MediaCut as ah, type MediaEdits as ai, type MediaScheduleOptions as aj, type MermaidLayer as ak, PAGE_ACCENT_STRATEGIES as al, PAGE_BACKGROUNDS as am, PAGE_BACKGROUND_RHYTHMS as an, PAGE_DESIGN_FAMILIES as ao, PAGE_DIVIDERS as ap, PAGE_EMPHASES as aq, PAGE_EYEBROWS as ar, PAGE_HEADING_CASES as as, PAGE_HEADING_SCALES as at, PAGE_HEADING_UNDERLINES as au, PAGE_HERO_STYLES as av, PAGE_IMAGE_FRAMINGS as aw, PAGE_NUMERAL_STYLES as ax, PAGE_PATTERNS as ay, PAGE_QUOTE_MARKS as az, type AmbientGradientConfig as b, getCaptionAtTime as b$, type RightFeatureInput as b0, type ScatterChartInput as b1, type ScheduledClip as b2, type SectionHeaderInput as b3, type ShapeFilter as b4, type ShapeLayer as b5, type ShapePattern as b6, type SolidBackgroundConfig as b7, type StartBlockConfig as b8, type StatHighlightInput as b9, type TreeBlockInput as bA, type TreeLayer as bB, type TreeLayerItem as bC, type TreeLayerStyle as bD, type TreeTemplateItem as bE, type TwoColumnInput as bF, VIEWPORT_PRESETS as bG, type VideoLayer as bH, type VideoPipPosition as bI, type VideoPipShape as bJ, type VideoPipSize as bK, type VideoPlacement as bL, type VideoPresentation as bM, type VideoPullQuoteInput as bN, type VideoWithCaptionInput as bO, type ViewportConfig as bP, type ViewportOrientation as bQ, type ViewportPreset as bR, type VignetteConfig as bS, applySurface as bT, calculateDuration as bU, calculateFontScale as bV, createTemplateContext as bW, createTheme as bX, createThemeRegistry as bY, getAspectRatioString as bZ, getBlockAtTime as b_, type SurfaceScheme as ba, THEME_SCHEMA_VERSION as bb, type TableLayer as bc, type TableLayerStyle as bd, type TemplateBlock as be, type TemplateContext as bf, type TemplateFunction as bg, type TemplateRegistry as bh, type TextLayer as bi, type TextStyle as bj, type Theme as bk, type ThemeColorPalette as bl, type ThemeColorScheme as bm, type ThemePageStyle as bn, type ThemeRegistry as bo, type ThemeSchemaVersion as bp, type ThemeSeedColors as bq, type ThemeStyle as br, type ThemeTypography as bs, type TimelineBlockInput as bt, type TimelineTemplateEvent as bu, type TimelineTemplateLink as bv, type TimelineTemplateTrack as bw, type TitleBlockInput as bx, type TitleCaptionConfig as by, type TranscriptBlockInput as bz, type Animation as c, getDocPlaybackDuration as c0, getLayoutHints as c1, getSafeTextBounds as c2, getSegmentAtTime as c3, getTwoColumnPositions as c4, getViewport as c5, getViewportOrientation as c6, isCustomTemplateDefinition as c7, isPersistentLayerTemplate as c8, isTemplateBlock as c9, serializeMediaCuts as cA, serializeMediaEdits as cB, serializeMediaFx as cC, scaledFontSize$1 as ca, resolveMediaSchedule as cb, scaledFontSize as cc, scheduleMediaClip as cd, validateCustomTemplateDefinition as ce, type MediaFxOpId as cf, type MediaFxChain as cg, MEDIA_EDIT_PARAMS as ch, MEDIA_FX_ORDER as ci, MEDIA_FX_SPECS as cj, type MediaEditParamKey as ck, type MediaEditParamValues as cl, type MediaFxOp as cm, formatMediaNumber as cn, hasRenderableFx as co, mediaEditHtmlAttribute as cp, mediaEditValuesFromAttributes as cq, mediaEditValuesFromParams as cr, mediaRenderKey as cs, normalizeMediaCrop as ct, normalizeMediaCuts as cu, parseMediaCrop as cv, parseMediaCuts as cw, parseMediaEdits as cx, parseMediaFx as cy, serializeMediaCrop as cz, type AnimationType as d, type AreaChartInput as e, type AudioBookmark as f, type AudioSegment as g, type AudioTimingData as h, type AudioTrack as i, type BigTextInput as j, type Block as k, type BlockConnection as l, type BorderStyle as m, type CaptionTrack as n, type CaptionWord as o, type ChartBaseFields as p, type ChartTemplateInput as q, type ColorScheme as r, type ColumnChartInput as s, type ComparisonBarInput as t, type ContentBlockInput as u, type CornerBrandingConfig as v, type CustomTemplateDefinition as w, type CustomTemplateLayer as x, type CustomTemplateValidationError as y, type CustomTemplateValidationResult as z };
@@ -1,4 +1,4 @@
1
- import { $ as ImageLayer, bf as TextLayer, b2 as ShapeLayer, aI as PathLayer } from './Doc-BkxT7G80.js';
1
+ import { $ as ImageLayer, bi as TextLayer, b5 as ShapeLayer, aL as PathLayer } from './Doc-CynsGqL4.js';
2
2
 
3
3
  /**
4
4
  * ImageEditDoc — JSON document describing a layered, editable image.
@@ -1,11 +1,16 @@
1
+ import {
2
+ biquadRun,
3
+ highpassCoeffs,
4
+ lowpassCoeffs
5
+ } from "./chunk-NEWNNLWN.js";
1
6
  import {
2
7
  buildNarrationScript,
3
8
  expectedSyllablesAt,
4
9
  wordPosAtExpectedSyllables
5
- } from "./chunk-EF353KGH.js";
10
+ } from "./chunk-ZEONJCP3.js";
6
11
  import {
7
12
  flattenRenderableBlocks
8
- } from "./chunk-3YNFMFNK.js";
13
+ } from "./chunk-RMCUZDW5.js";
9
14
 
10
15
  // src/narration/types.ts
11
16
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -71,32 +76,6 @@ var DEFAULT_ALIGN_CONFIG = Object.freeze({
71
76
  });
72
77
 
73
78
  // src/narration/features.ts
74
- function highpassCoeffs(sampleRate, freqHz) {
75
- const w0 = 2 * Math.PI * freqHz / sampleRate;
76
- const cosW0 = Math.cos(w0);
77
- const alpha = Math.sin(w0) / (2 * Math.SQRT1_2);
78
- const a0 = 1 + alpha;
79
- return {
80
- b0: (1 + cosW0) / 2 / a0,
81
- b1: -(1 + cosW0) / a0,
82
- b2: (1 + cosW0) / 2 / a0,
83
- a1: -2 * cosW0 / a0,
84
- a2: (1 - alpha) / a0
85
- };
86
- }
87
- function lowpassCoeffs(sampleRate, freqHz) {
88
- const w0 = 2 * Math.PI * freqHz / sampleRate;
89
- const cosW0 = Math.cos(w0);
90
- const alpha = Math.sin(w0) / (2 * Math.SQRT1_2);
91
- const a0 = 1 + alpha;
92
- return {
93
- b0: (1 - cosW0) / 2 / a0,
94
- b1: (1 - cosW0) / a0,
95
- b2: (1 - cosW0) / 2 / a0,
96
- a1: -2 * cosW0 / a0,
97
- a2: (1 - alpha) / a0
98
- };
99
- }
100
79
  function createBandpass(sampleRate, lowHz = DEFAULT_FEATURE_CONFIG.bandLowHz, highHz = DEFAULT_FEATURE_CONFIG.bandHighHz) {
101
80
  return {
102
81
  coeffs: { hp: highpassCoeffs(sampleRate, lowHz), lp: lowpassCoeffs(sampleRate, highHz) },
@@ -104,20 +83,6 @@ function createBandpass(sampleRate, lowHz = DEFAULT_FEATURE_CONFIG.bandLowHz, hi
104
83
  lp: { x1: 0, x2: 0, y1: 0, y2: 0 }
105
84
  };
106
85
  }
107
- function biquadRun(coeffs, state, input, output) {
108
- let { x1, x2, y1, y2 } = state;
109
- const { b0, b1, b2, a1, a2 } = coeffs;
110
- for (let i = 0; i < input.length; i++) {
111
- const x0 = input[i];
112
- const y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
113
- output[i] = y0;
114
- x2 = x1;
115
- x1 = x0;
116
- y2 = y1;
117
- y1 = y0;
118
- }
119
- return { x1, x2, y1, y2 };
120
- }
121
86
  function bandpassRun(state, input, output) {
122
87
  const hp = biquadRun(state.coeffs.hp, state.hp, input, output);
123
88
  const lp = biquadRun(state.coeffs.lp, state.lp, output, output);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defaultPageStyle
3
- } from "./chunk-GAZKTT4R.js";
3
+ } from "./chunk-YNO6JV36.js";
4
4
  import {
5
5
  FONT_FALLBACKS,
6
6
  THEME_SCHEMA_VERSION,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildNarrationScript,
3
3
  parseNarrationTimingJson
4
- } from "./chunk-EF353KGH.js";
4
+ } from "./chunk-ZEONJCP3.js";
5
5
  import {
6
6
  SHAPE_NAMES,
7
7
  buildChartData,
@@ -32,10 +32,7 @@ import {
32
32
  templateRegistry,
33
33
  writeCustomTemplatesToFrontmatter,
34
34
  writeCustomThemesToFrontmatter
35
- } from "./chunk-3YNFMFNK.js";
36
- import {
37
- iconMarker
38
- } from "./chunk-7ZAAICW4.js";
35
+ } from "./chunk-RMCUZDW5.js";
39
36
  import {
40
37
  ASCII_TREE_VOCAB,
41
38
  ASCII_VOCAB,
@@ -53,10 +50,13 @@ import {
53
50
  toGrid,
54
51
  traceBoxes
55
52
  } from "./chunk-C3NAY5YP.js";
53
+ import {
54
+ iconMarker
55
+ } from "./chunk-7ZAAICW4.js";
56
56
  import {
57
57
  defaultPageStyle,
58
58
  resolveMediaSchedule
59
- } from "./chunk-GAZKTT4R.js";
59
+ } from "./chunk-YNO6JV36.js";
60
60
  import {
61
61
  DEFAULT_THEME,
62
62
  FRONTMATTER_CUSTOM_TEMPLATES_KEY,
@@ -74,9 +74,15 @@ import {
74
74
  resolveTheme,
75
75
  withAlpha
76
76
  } from "./chunk-Y4D6TDZT.js";
77
+ import {
78
+ createMediaTimeMap
79
+ } from "./chunk-JSGIWW35.js";
80
+ import {
81
+ serializeMediaEdits
82
+ } from "./chunk-L7QZORLB.js";
77
83
  import {
78
84
  parseMarkdown
79
- } from "./chunk-CW24CH7I.js";
85
+ } from "./chunk-HUXK7EOT.js";
80
86
  import {
81
87
  KNOWN_BLOCK_META_KEYS,
82
88
  TABLE_FED_TEMPLATES,
@@ -596,6 +602,9 @@ function synthesizeMediaParagraph(clip) {
596
602
  if (clip.clipStart != null) params.clipStart = String(clip.clipStart);
597
603
  if (clip.clipEnd != null) params.clipEnd = String(clip.clipEnd);
598
604
  if (clip.spillover) params.spillover = "true";
605
+ for (const [key, value] of Object.entries(serializeMediaEdits(clip.edits))) {
606
+ if (value != null) params[key] = value;
607
+ }
599
608
  return {
600
609
  type: "paragraph",
601
610
  children: [{ type: "text", value: serializeAnnotation(clip.kind, params) }]
@@ -3976,6 +3985,26 @@ function proportionalRanges(doc, timing) {
3976
3985
  });
3977
3986
  return ranges;
3978
3987
  }
3988
+ function narrationTimeMap(clip, takeDuration) {
3989
+ const trimmed = clip.clipStart != null || clip.clipEnd != null;
3990
+ if (!trimmed && !clip.edits?.cuts?.length) return null;
3991
+ return createMediaTimeMap({
3992
+ clipStart: clip.clipStart,
3993
+ clipEnd: clip.clipEnd,
3994
+ cuts: clip.edits?.cuts,
3995
+ sourceDuration: takeDuration
3996
+ });
3997
+ }
3998
+ function mapRangesToPlayed(ranges, map2) {
3999
+ const mapped = /* @__PURE__ */ new Map();
4000
+ for (const [blockId, range] of ranges) {
4001
+ mapped.set(blockId, {
4002
+ startSec: map2.snapSourceToPlayed(range.startSec),
4003
+ endSec: map2.snapSourceToPlayed(range.endSec)
4004
+ });
4005
+ }
4006
+ return mapped;
4007
+ }
3979
4008
  function retimeBlocks(blocks, ctx) {
3980
4009
  return blocks.map((block) => {
3981
4010
  const range = ctx.ranges.get(block.id);
@@ -4018,8 +4047,11 @@ async function applyNarrationTiming(doc, container) {
4018
4047
  const timing = parseNarrationTimingJson(sidecarData);
4019
4048
  if (!timing) continue;
4020
4049
  const flat = flattenRenderableBlocks(doc.blocks);
4021
- const ranges = timing.blocks.length > 0 ? matchSidecarBlocks(flat, timing) : proportionalRanges(doc, timing);
4022
- if (!ranges || ranges.size === 0) continue;
4050
+ const takeRanges = timing.blocks.length > 0 ? matchSidecarBlocks(flat, timing) : proportionalRanges(doc, timing);
4051
+ if (!takeRanges || takeRanges.size === 0) continue;
4052
+ const timeMap = narrationTimeMap(clip, timing.duration);
4053
+ const ranges = timeMap ? mapRangesToPlayed(takeRanges, timeMap) : takeRanges;
4054
+ const playedDuration = timeMap ? timeMap.playedDuration : timing.duration;
4023
4055
  const ctx = {
4024
4056
  clipStart: clip.startAt,
4025
4057
  ranges,
@@ -4028,7 +4060,7 @@ async function applyNarrationTiming(doc, container) {
4028
4060
  shift: 0
4029
4061
  };
4030
4062
  const blocks = retimeBlocks(doc.blocks, ctx);
4031
- const duration = Math.max(clip.startAt + timing.duration, ctx.cursor);
4063
+ const duration = Math.max(clip.startAt + playedDuration, ctx.cursor);
4032
4064
  const retimed = {
4033
4065
  ...doc,
4034
4066
  blocks,
@@ -0,0 +1,70 @@
1
+ import {
2
+ normalizeMediaCuts
3
+ } from "./chunk-L7QZORLB.js";
4
+
5
+ // src/mediaEdit/timeMap.ts
6
+ function createMediaTimeMap(input) {
7
+ const windowStart = Math.max(0, input.clipStart ?? 0);
8
+ let windowEnd = input.clipEnd ?? Number.POSITIVE_INFINITY;
9
+ if (input.sourceDuration != null && Number.isFinite(input.sourceDuration)) {
10
+ windowEnd = Math.min(windowEnd, input.sourceDuration);
11
+ }
12
+ windowEnd = Math.max(windowStart, windowEnd);
13
+ const cuts = normalizeMediaCuts(input.cuts ?? [], windowStart, windowEnd);
14
+ const kept = [];
15
+ let cursor = windowStart;
16
+ for (const cut of cuts) {
17
+ if (cut.start > cursor) kept.push({ start: cursor, end: cut.start });
18
+ cursor = Math.max(cursor, cut.end);
19
+ }
20
+ if (windowEnd > cursor) kept.push({ start: cursor, end: windowEnd });
21
+ const offsets = [];
22
+ let total = 0;
23
+ for (const range of kept) {
24
+ offsets.push(total);
25
+ total += range.end - range.start;
26
+ }
27
+ const playedDuration = total;
28
+ const sourceToPlayed = (t) => {
29
+ for (let i = 0; i < kept.length; i++) {
30
+ const range = kept[i];
31
+ if (t >= range.start && (t < range.end || i === kept.length - 1 && t === range.end)) {
32
+ return offsets[i] + (t - range.start);
33
+ }
34
+ }
35
+ return null;
36
+ };
37
+ const snapSourceToPlayed = (t) => {
38
+ if (kept.length === 0 || t <= windowStart) return 0;
39
+ for (let i = 0; i < kept.length; i++) {
40
+ const range = kept[i];
41
+ if (t < range.start) return offsets[i];
42
+ if (t < range.end) return offsets[i] + (t - range.start);
43
+ }
44
+ return playedDuration;
45
+ };
46
+ const playedToSource = (p) => {
47
+ if (kept.length === 0) return windowStart;
48
+ if (p <= 0) return kept[0].start;
49
+ for (let i = 0; i < kept.length; i++) {
50
+ const length = kept[i].end - kept[i].start;
51
+ if (p < offsets[i] + length) return kept[i].start + (p - offsets[i]);
52
+ }
53
+ return kept[kept.length - 1].end;
54
+ };
55
+ return {
56
+ windowStart,
57
+ windowEnd,
58
+ cuts,
59
+ hasCuts: cuts.length > 0,
60
+ playedDuration,
61
+ keptRanges: () => kept.map((r) => ({ ...r })),
62
+ sourceToPlayed,
63
+ snapSourceToPlayed,
64
+ playedToSource
65
+ };
66
+ }
67
+
68
+ export {
69
+ createMediaTimeMap
70
+ };
@@ -5,14 +5,14 @@ import {
5
5
  } from "./chunk-SVN7T6BK.js";
6
6
  import {
7
7
  markdownToDoc
8
- } from "./chunk-3YNFMFNK.js";
8
+ } from "./chunk-RMCUZDW5.js";
9
9
  import {
10
10
  cloneAndFreezeData,
11
11
  isTemplateBlock
12
12
  } from "./chunk-Y4D6TDZT.js";
13
13
  import {
14
14
  parseMarkdown
15
- } from "./chunk-CW24CH7I.js";
15
+ } from "./chunk-HUXK7EOT.js";
16
16
  import {
17
17
  extractPlainText,
18
18
  getChildren