@bendyline/squisq 2.11.7 → 2.11.9

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 (40) hide show
  1. package/dist/{Doc-BkxT7G80.d.ts → Doc-DmElSKmg.d.ts} +219 -1
  2. package/dist/{ImageEditDoc-C8ZB54Vv.d.ts → ImageEditDoc-Dm_BETxC.d.ts} +1 -1
  3. package/dist/{chunk-EF353KGH.js → chunk-2PGC4JDT.js} +1 -1
  4. package/dist/{chunk-YGGYYBJS.js → chunk-B5STNW6C.js} +2 -2
  5. package/dist/{chunk-IYZTF4VO.js → chunk-CCRBHFNO.js} +3 -3
  6. package/dist/{chunk-VU4T74LI.js → chunk-DTM4FG7S.js} +1 -1
  7. package/dist/chunk-JSGIWW35.js +70 -0
  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-Y4D6TDZT.js → chunk-PCCQLD3G.js} +2 -0
  12. package/dist/{chunk-SKKBRLLM.js → chunk-QDA7NXDO.js} +7 -42
  13. package/dist/{chunk-3YNFMFNK.js → chunk-QOVINAB2.js} +18 -9
  14. package/dist/{chunk-SOIV7KDO.js → chunk-VRTXPGP7.js} +256 -26
  15. package/dist/{chunk-XJXBI3N5.js → chunk-XZVBR5BP.js} +1 -1
  16. package/dist/{chunk-GAZKTT4R.js → chunk-YNO6JV36.js} +96 -33
  17. package/dist/doc/index.d.ts +10 -6
  18. package/dist/doc/index.js +23 -21
  19. package/dist/fence/index.d.ts +1 -1
  20. package/dist/generate/index.d.ts +1 -1
  21. package/dist/imageEdit/index.d.ts +47 -4
  22. package/dist/imageEdit/index.js +5 -1
  23. package/dist/index.d.ts +5 -5
  24. package/dist/index.js +52 -41
  25. package/dist/jsonForm/index.d.ts +1 -1
  26. package/dist/jsonForm/index.js +6 -4
  27. package/dist/markdown/index.js +3 -3
  28. package/dist/{materializePageSection-Dp2aHFbc.d.ts → materializePageSection-BfttC9Zj.d.ts} +6 -3
  29. package/dist/mediaEdit/index.d.ts +467 -0
  30. package/dist/mediaEdit/index.js +990 -0
  31. package/dist/narration/index.d.ts +20 -12
  32. package/dist/narration/index.js +7 -5
  33. package/dist/schemas/index.d.ts +4 -4
  34. package/dist/schemas/index.js +10 -4
  35. package/dist/{themeLibrary-BbSjnGmd.d.ts → themeLibrary-ChnTImby.d.ts} +1 -1
  36. package/dist/timing/index.d.ts +1 -1
  37. package/dist/transform/index.d.ts +2 -2
  38. package/dist/transform/index.js +7 -6
  39. package/package.json +6 -1
  40. package/dist/{chunk-CW24CH7I.js → chunk-HUXK7EOT.js} +3 -3
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildNarrationScript,
3
3
  parseNarrationTimingJson
4
- } from "./chunk-EF353KGH.js";
4
+ } from "./chunk-2PGC4JDT.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-QOVINAB2.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,
@@ -73,10 +73,16 @@ import {
73
73
  resolveFontFamily,
74
74
  resolveTheme,
75
75
  withAlpha
76
- } from "./chunk-Y4D6TDZT.js";
76
+ } from "./chunk-PCCQLD3G.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) }]
@@ -1463,10 +1472,42 @@ var sectionExtractors = {
1463
1472
  };
1464
1473
 
1465
1474
  // src/doc/page/materializePageSection.ts
1466
- function resolvePageStyle(theme, hints) {
1475
+ function resolvePageStyle(theme, hints, variant = "page") {
1467
1476
  const base = theme?.pageStyle ?? defaultPageStyle(theme ?? DEFAULT_THEME);
1468
- if (!hints?.spacing || hints.spacing === base.tokens.sectionSpacing) return base;
1469
- return { ...base, tokens: { ...base.tokens, sectionSpacing: hints.spacing } };
1477
+ const spaced = !hints?.spacing || hints.spacing === base.tokens.sectionSpacing ? base : { ...base, tokens: { ...base.tokens, sectionSpacing: hints.spacing } };
1478
+ return variant === "document" ? documentPageStyle(spaced) : spaced;
1479
+ }
1480
+ function withoutBackgrounds(overrides) {
1481
+ const out = {};
1482
+ for (const [key, override] of Object.entries(overrides)) {
1483
+ if (!override) continue;
1484
+ const { background: _background, ...rest } = override;
1485
+ out[key] = rest;
1486
+ }
1487
+ return out;
1488
+ }
1489
+ function documentPageStyle(style) {
1490
+ return {
1491
+ family: style.family,
1492
+ tokens: {
1493
+ ...style.tokens,
1494
+ cornerRadius: Math.min(style.tokens.cornerRadius, 6),
1495
+ sectionSpacing: "compact",
1496
+ divider: "none",
1497
+ backgroundRhythm: "flat",
1498
+ headingTreatment: { eyebrow: "kicker", scale: "regular", case: "none", underline: "none" },
1499
+ shadow: "none",
1500
+ quoteMark: "accent-bar",
1501
+ numeralStyle: "plain",
1502
+ pattern: "none"
1503
+ },
1504
+ ...style.sections ? { sections: withoutBackgrounds(style.sections) } : {},
1505
+ ...style.templates ? { templates: withoutBackgrounds(style.templates) } : {},
1506
+ accentRotation: { strategy: "primary-only" }
1507
+ };
1508
+ }
1509
+ function isAutoPickedOnly(block) {
1510
+ return block.autoTemplate === true && !block.sourceHeading?.templateAnnotation?.template && !block.promotedBodyAnnotation;
1470
1511
  }
1471
1512
  var ACCENT_KINDS = /* @__PURE__ */ new Set([
1472
1513
  "banner",
@@ -1538,8 +1579,9 @@ function preserveRichContent(block, draft, widgetFenceLangs) {
1538
1579
  const richContent = unconsumedRichContent(block, draft, widgetFenceLangs);
1539
1580
  return richContent ? { ...draft, slots: { ...draft.slots, richContent } } : draft;
1540
1581
  }
1541
- function draftForBlock(block, viewport, customTemplates, widgetFenceLangs) {
1542
- if (isTemplatedPageBlock(block)) {
1582
+ function draftForBlock(block, viewport, customTemplates, widgetFenceLangs, variant = "page") {
1583
+ const autoPickedInDocument = variant === "document" && isAutoPickedOnly(block);
1584
+ if (isTemplatedPageBlock(block) && !autoPickedInDocument) {
1543
1585
  const resolved = resolvePageBlock(block);
1544
1586
  const templateName = resolved.templateName;
1545
1587
  const extractor = sectionExtractors[templateName];
@@ -1675,8 +1717,14 @@ function rotationSchemeNames(pageStyle, theme) {
1675
1717
  function materializePageSection(block, options = {}) {
1676
1718
  const theme = options.theme ?? DEFAULT_THEME;
1677
1719
  const viewport = options.viewport ?? VIEWPORT_PRESETS.landscape;
1678
- const pageStyle = resolvePageStyle(theme);
1679
- const result = draftForBlock(block, viewport, options.customTemplates, options.widgetFenceLangs);
1720
+ const pageStyle = resolvePageStyle(theme, void 0, options.variant);
1721
+ const result = draftForBlock(
1722
+ block,
1723
+ viewport,
1724
+ options.customTemplates,
1725
+ options.widgetFenceLangs,
1726
+ options.variant
1727
+ );
1680
1728
  const override = overrideFor(pageStyle, result.draft.kind, result.templateName);
1681
1729
  const background = override?.background ?? (result.draft.mediaBackground ? "media" : "base");
1682
1730
  const emphasis = override?.emphasis ?? result.draft.emphasis ?? "standard";
@@ -1700,14 +1748,39 @@ function materializePageSection(block, options = {}) {
1700
1748
  diagnostic: result.diagnostic
1701
1749
  };
1702
1750
  }
1751
+ function dedupeDocumentMasthead(seeds) {
1752
+ const cover = seeds[0];
1753
+ const first = seeds[1];
1754
+ const slots = { ...first.draft.slots };
1755
+ if ((slots.headingLevel ?? 1) > 1) {
1756
+ seeds.splice(0, 1);
1757
+ return;
1758
+ }
1759
+ const markdown = slots.body?.markdown;
1760
+ const lead = markdown?.[0];
1761
+ const subtitle = cover.draft.slots.subtitle;
1762
+ if (subtitle && lead?.type === "paragraph" && extractPlainText(lead) === subtitle) {
1763
+ const { subtitle: _mirrored, ...coverSlots } = cover.draft.slots;
1764
+ cover.draft = { ...cover.draft, slots: coverSlots };
1765
+ }
1766
+ delete slots.title;
1767
+ delete slots.headingLevel;
1768
+ if (markdown && markdown.length > 0) {
1769
+ first.draft = { ...first.draft, slots };
1770
+ } else {
1771
+ seeds.splice(1, 1);
1772
+ }
1773
+ }
1703
1774
  function materializePageSections(doc, options = {}) {
1704
1775
  const theme = options.theme ?? DEFAULT_THEME;
1705
1776
  const viewport = options.viewport ?? VIEWPORT_PRESETS.landscape;
1706
- const pageStyle = resolvePageStyle(theme, options.transformPage);
1777
+ const variant = options.variant ?? "page";
1778
+ const pageStyle = resolvePageStyle(theme, options.transformPage, variant);
1707
1779
  const customTemplates = options.customTemplates ?? doc.customTemplates;
1708
1780
  const seeds = [];
1709
1781
  if (options.cover) {
1710
1782
  const cover = options.cover;
1783
+ const heroSrc = variant === "document" ? void 0 : cover.heroSrc;
1711
1784
  seeds.push({
1712
1785
  draft: {
1713
1786
  kind: "hero",
@@ -1715,16 +1788,16 @@ function materializePageSections(doc, options = {}) {
1715
1788
  slots: {
1716
1789
  title: cover.title,
1717
1790
  subtitle: cover.subtitle,
1718
- media: cover.heroSrc ? {
1791
+ media: heroSrc ? {
1719
1792
  type: "image",
1720
- src: cover.heroSrc,
1793
+ src: heroSrc,
1721
1794
  alt: cover.heroAlt ?? "",
1722
1795
  credit: cover.heroCredit,
1723
1796
  license: cover.heroLicense
1724
1797
  } : void 0
1725
1798
  },
1726
1799
  emphasis: "lead",
1727
- mediaBackground: !!cover.heroSrc
1800
+ mediaBackground: !!heroSrc
1728
1801
  },
1729
1802
  source: "cover",
1730
1803
  depth: 0
@@ -1732,7 +1805,13 @@ function materializePageSections(doc, options = {}) {
1732
1805
  }
1733
1806
  const walk = (blocks, depth) => {
1734
1807
  for (const block of blocks) {
1735
- const result = draftForBlock(block, viewport, customTemplates, options.widgetFenceLangs);
1808
+ const result = draftForBlock(
1809
+ block,
1810
+ viewport,
1811
+ customTemplates,
1812
+ options.widgetFenceLangs,
1813
+ variant
1814
+ );
1736
1815
  seeds.push({ ...result, block, depth });
1737
1816
  const consumed = result.draft.kind === "canvas-embed" && isContainerTemplate(result.templateName);
1738
1817
  if (!consumed && block.children && block.children.length > 0) {
@@ -1755,6 +1834,8 @@ function materializePageSections(doc, options = {}) {
1755
1834
  };
1756
1835
  }
1757
1836
  seeds.splice(0, 1);
1837
+ } else if (variant === "document" && first.draft.kind === "prose" && first.draft.slots.title === cover.title) {
1838
+ dedupeDocumentMasthead(seeds);
1758
1839
  } else if (first.draft.kind === "prose" && first.draft.slots.title === cover.title) {
1759
1840
  const slots = { ...first.draft.slots };
1760
1841
  delete slots.title;
@@ -1912,6 +1993,7 @@ var NARROW_RULES = `
1912
1993
  .squisq-page-feature { grid-template-columns: 1fr; }
1913
1994
  .squisq-page-feature--media-right .squisq-page-feature-media { order: 0; }
1914
1995
  .squisq-page-stats { flex-direction: column; gap: 1.75rem; }
1996
+ .squisq-page-stats > .squisq-page-stat { flex: 0 0 auto; max-width: none; }
1915
1997
  .squisq-page-cards { grid-template-columns: 1fr; }
1916
1998
  .squisq-page-gallery { grid-template-columns: repeat(2, 1fr); }
1917
1999
  .squisq-page-milestone { grid-template-columns: 1fr; gap: 0.5rem; }
@@ -2279,10 +2361,14 @@ var PAGE_BASE_CSS = `
2279
2361
  font-size: 1.15em;
2280
2362
  }
2281
2363
  /* Item bodies are rendered markdown: neutralize UA paragraph margins (which
2282
- would otherwise push the first line below the marker) and space blocks. */
2364
+ would otherwise push the first line below the marker) and space blocks.
2365
+ MarkdownRenderer wraps its blocks in a .squisq-md div, so the paragraphs
2366
+ are grandchildren. */
2283
2367
  .squisq-page-item-body { min-width: 0; }
2284
- .squisq-page-item-body > * { margin: 0; }
2285
- .squisq-page-item-body > * + * { margin-top: 0.6em; }
2368
+ .squisq-page-item-body > *,
2369
+ .squisq-page-item-body > .squisq-md > * { margin: 0; }
2370
+ .squisq-page-item-body > * + *,
2371
+ .squisq-page-item-body > .squisq-md > * + * { margin-top: 0.6em; }
2286
2372
  .squisq-page[data-numerals='mono'] .squisq-page-items li::before { font-family: var(--squisq-page-mono-font); }
2287
2373
  .squisq-page-items-title { font-size: 1.9rem; }
2288
2374
 
@@ -2430,6 +2516,127 @@ var PAGE_BASE_CSS = `
2430
2516
  .squisq-page--thumbnail-images .squisq-page-prose img,
2431
2517
  .squisq-page--thumbnail-images .squisq-page-figure img { max-width: 100px; max-height: 100px; object-fit: cover; }
2432
2518
 
2519
+ /* \u2500\u2500 Document variant \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2520
+ /* The working-document register (reports, notes, previews). Sizes are in em
2521
+ * so one host font-size scales the whole page, and a section's title is
2522
+ * sized by its markdown heading level (data-heading-level), never by which
2523
+ * section kind happens to carry it. Rules that must beat a page rule keyed
2524
+ * on a token attribute repeat .squisq-page for specificity. */
2525
+ .squisq-page--document { --squisq-page-divider-color: color-mix(in srgb, var(--squisq-page-text) 14%, transparent); }
2526
+ .squisq-page--document .squisq-page-section { padding-block: 0.4em; }
2527
+ .squisq-page--document .squisq-page-section .squisq-page-section-inner { max-width: var(--squisq-page-content-max); }
2528
+ .squisq-page--document > .squisq-page-section:is([data-heading-level='3'], [data-heading-level='4'], [data-heading-level='5'], [data-heading-level='6']) { padding-top: 1em; }
2529
+ /* A top-level heading opens with a hairline set in the text column, like the
2530
+ * masthead's, rather than a full-bleed section border. */
2531
+ .squisq-page.squisq-page--document > .squisq-page-section:is([data-heading-level='1'], [data-heading-level='2']) { margin-top: 0.8em; }
2532
+ .squisq-page.squisq-page--document > .squisq-page-section:is([data-heading-level='1'], [data-heading-level='2']) > .squisq-page-section-inner::before {
2533
+ content: '';
2534
+ display: block;
2535
+ margin-bottom: 1.1em;
2536
+ border-top: 1px solid var(--squisq-page-divider-color);
2537
+ }
2538
+ .squisq-page.squisq-page--document > .squisq-page-section:first-of-type,
2539
+ .squisq-page.squisq-page--document > .squisq-page-section--hero + .squisq-page-section { margin-top: 0; }
2540
+ .squisq-page.squisq-page--document > .squisq-page-section:first-of-type > .squisq-page-section-inner::before,
2541
+ .squisq-page.squisq-page--document > .squisq-page-section--hero + .squisq-page-section > .squisq-page-section-inner::before { display: none; }
2542
+ .squisq-page--document > .squisq-page-section:first-of-type { padding-top: 1em; }
2543
+ .squisq-page--document > .squisq-page-section:last-of-type { padding-bottom: 1.25em; }
2544
+ /* Thin margins hug the host's container, which supplies the outer inset. */
2545
+ .squisq-page--document.squisq-page--thin > .squisq-page-section:first-of-type { padding-top: 0; }
2546
+ .squisq-page--document.squisq-page--thin > .squisq-page-section:last-of-type { padding-bottom: 0; }
2547
+
2548
+ /* Masthead: the cover as a left-aligned document title over a hairline. */
2549
+ .squisq-page.squisq-page--document .squisq-page-section--hero { padding-block: 1em 0.6em; }
2550
+ .squisq-page.squisq-page--document .squisq-page-hero-body {
2551
+ text-align: left;
2552
+ padding-bottom: 0.55em;
2553
+ border-bottom: 1px solid var(--squisq-page-divider-color);
2554
+ }
2555
+ .squisq-page.squisq-page--document .squisq-page-hero-title { font-size: 1.6em; line-height: 1.2; letter-spacing: normal; text-transform: none; }
2556
+ .squisq-page.squisq-page--document .squisq-page-hero-subtitle { margin: 0.35em 0 0; font-size: 1em; max-width: none; }
2557
+ .squisq-page.squisq-page--document .squisq-page-section--hero-media { min-height: 12em; }
2558
+ .squisq-page.squisq-page--document .squisq-page-section--hero-media .squisq-page-section-inner { padding-block: 1.25em; }
2559
+ .squisq-page.squisq-page--document .squisq-page-section--hero-media .squisq-page-hero-body { border-bottom: none; }
2560
+
2561
+ /* One heading scale for prose headings and typed-section titles alike. */
2562
+ .squisq-page--document .squisq-page-section-title { font-size: 1.3em; line-height: 1.25; margin: 0 0 0.45em; text-align: left; }
2563
+ .squisq-page--document [data-heading-level='1'] .squisq-page-section-title { font-size: 1.6em; }
2564
+ .squisq-page--document [data-heading-level='3'] .squisq-page-section-title { font-size: 1.1em; }
2565
+ .squisq-page--document :is([data-heading-level='4'], [data-heading-level='5'], [data-heading-level='6']) .squisq-page-section-title { font-size: 1em; }
2566
+ .squisq-page--document .squisq-page-prose :is(h1, h2, h3, h4, h5, h6) { line-height: 1.25; margin: 0; }
2567
+ .squisq-page--document .squisq-page-prose h1 { font-size: 1.6em; }
2568
+ .squisq-page--document .squisq-page-prose h2 { font-size: 1.3em; }
2569
+ .squisq-page--document .squisq-page-prose h3 { font-size: 1.1em; }
2570
+ .squisq-page--document .squisq-page-prose :is(h4, h5, h6) { font-size: 1em; }
2571
+ .squisq-page--document .squisq-page-eyebrow { font-size: 0.72em; letter-spacing: 0.1em; margin-bottom: 0.5em; }
2572
+
2573
+ /* Prose */
2574
+ .squisq-page--document .squisq-page-prose { font-size: 1em; }
2575
+ /* A prose section is a heading wrapper then a body wrapper (.squisq-md each). */
2576
+ .squisq-page--document .squisq-page-prose > .squisq-md + .squisq-md { margin-top: 0.4em; }
2577
+ .squisq-page--document .squisq-page-prose > .squisq-md > * + * { margin-top: 0.75em; }
2578
+ .squisq-page--document .squisq-page-prose > .squisq-md > :last-child { margin-bottom: 0; }
2579
+ .squisq-page--document .squisq-page-prose p + p { margin-top: 0.75em; }
2580
+ .squisq-page--document .squisq-page-prose li + li { margin-top: 0.25em; }
2581
+ .squisq-page--document .squisq-page-prose :is(ul, ol) :is(ul, ol) { margin-top: 0.25em; }
2582
+ .squisq-page--document .squisq-page-prose code { font-size: 0.88em; }
2583
+ .squisq-page--document .squisq-page-prose pre { padding: 0.7em 0.9em; }
2584
+ .squisq-page--document .squisq-md-code-frame { margin: 0.75em 0; }
2585
+ .squisq-page--document .squisq-page-prose blockquote { border-left-width: 3px; padding-left: 1em; }
2586
+ .squisq-page--document .squisq-page-prose hr { margin-block: 1.25em; }
2587
+ .squisq-page--document :is(.squisq-page-prose, .squisq-page-table-section) table { font-size: 0.93em; }
2588
+ .squisq-page--document .squisq-page-prose th,
2589
+ .squisq-page--document .squisq-page-table th {
2590
+ font-family: var(--squisq-page-body-font);
2591
+ font-weight: 600;
2592
+ padding: 0.45em 0.7em;
2593
+ }
2594
+ .squisq-page--document .squisq-page-prose td,
2595
+ .squisq-page--document .squisq-page-table td { padding: 0.4em 0.7em; }
2596
+ .squisq-page--document .squisq-page-rich-content { margin-top: 1em; padding-top: 0.75em; }
2597
+
2598
+ /* Typed sections an author asked for, at document scale. */
2599
+ .squisq-page--document .squisq-page-section-inner > :first-child { margin-top: 0; }
2600
+ .squisq-page--document .squisq-page-stats { justify-content: flex-start; text-align: left; gap: 1.5em; }
2601
+ .squisq-page--document .squisq-page-stat { flex: 1 1 10em; }
2602
+ .squisq-page.squisq-page--document .squisq-page-stat-value { font-size: 1.9em; }
2603
+ .squisq-page--document .squisq-page-stat-title { margin-top: 0.35em; font-size: 1em; }
2604
+ .squisq-page--document .squisq-page-stat-body { margin-top: 0.2em; font-size: 0.9em; }
2605
+ .squisq-page--document .squisq-page-stat-meta { margin-top: 0.75em; text-align: left; font-size: 0.85em; }
2606
+ .squisq-page.squisq-page--document .squisq-page-quote { font-size: 1.15em; line-height: 1.45; }
2607
+ .squisq-page.squisq-page--document .squisq-page-quote-figure {
2608
+ border: none;
2609
+ border-left: 3px solid var(--squisq-page-accent);
2610
+ padding: 0 0 0 1em;
2611
+ text-align: left;
2612
+ }
2613
+ .squisq-page--document .squisq-page-quote-attribution { margin-top: 0.6em; font-size: 0.9em; }
2614
+ .squisq-page.squisq-page--document .squisq-page-section--quote-band.squisq-page-section--bg-media { min-height: 10em; }
2615
+ .squisq-page.squisq-page--document .squisq-page-section--banner.squisq-page-section--bg-media { min-height: 9em; }
2616
+ .squisq-page--document .squisq-page-feature { gap: 1.5em; }
2617
+ .squisq-page--document .squisq-page-feature-body { font-size: 1em; }
2618
+ .squisq-page--document .squisq-page-figure figcaption,
2619
+ .squisq-page--document .squisq-page-gallery-caption { margin-top: 0.6em; font-size: 0.85em; }
2620
+ .squisq-page--document .squisq-page-gallery { gap: 0.75em; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
2621
+ .squisq-page--document .squisq-page-callout { padding: 0.85em 1.1em; gap: 1em; border-left-width: 3px; }
2622
+ .squisq-page--document .squisq-page-callout-title { font-size: 1.05em; margin-bottom: 0.35em; }
2623
+ .squisq-page--document .squisq-page-callout-meta { margin-top: 0.6em; font-size: 0.82em; }
2624
+ .squisq-page--document .squisq-page-callout .squisq-page-media-frame { flex-basis: 5.5em; }
2625
+ .squisq-page--document .squisq-page-callout .squisq-page-media-frame img { width: 5.5em; height: 5.5em; }
2626
+ .squisq-page--document .squisq-page-cards { gap: 0.9em; grid-template-columns: repeat(auto-fit, minmax(12em, 1fr)); }
2627
+ .squisq-page--document .squisq-page-card { padding: 0.9em 1em 1em; border-top-width: 3px; }
2628
+ .squisq-page--document .squisq-page-card-title { font-size: 1.05em; }
2629
+ .squisq-page--document .squisq-page-cards-title { margin-bottom: 0.6em; }
2630
+ .squisq-page--document .squisq-page-items { max-width: none; }
2631
+ .squisq-page--document .squisq-page-items li { grid-template-columns: 2.2em 1fr; padding: 0.45em 0; font-size: 1em; }
2632
+ .squisq-page--document .squisq-page-items li::before { font-size: 1em; }
2633
+ .squisq-page--document .squisq-page-section--timeline-rail { padding-block: 0.5em; }
2634
+ .squisq-page--document .squisq-page-milestone { grid-template-columns: 7em 1fr; gap: 1.25em; }
2635
+ .squisq-page--document .squisq-page-milestone-date { font-size: 1em; }
2636
+ .squisq-page--document .squisq-page-milestone-rail { left: calc(7em + 0.62em); top: -0.5em; bottom: -0.5em; }
2637
+ .squisq-page--document .squisq-page-milestone-body { padding-left: 1.25em; }
2638
+ .squisq-page--document .squisq-page-milestone-footer { font-size: 0.85em; }
2639
+
2433
2640
  /* \u2500\u2500 Reveal animation (progressive enhancement) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2434
2641
  @media (prefers-reduced-motion: no-preference) {
2435
2642
  .squisq-page-reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.55s ease, transform 0.55s ease; }
@@ -3976,6 +4183,26 @@ function proportionalRanges(doc, timing) {
3976
4183
  });
3977
4184
  return ranges;
3978
4185
  }
4186
+ function narrationTimeMap(clip, takeDuration) {
4187
+ const trimmed = clip.clipStart != null || clip.clipEnd != null;
4188
+ if (!trimmed && !clip.edits?.cuts?.length) return null;
4189
+ return createMediaTimeMap({
4190
+ clipStart: clip.clipStart,
4191
+ clipEnd: clip.clipEnd,
4192
+ cuts: clip.edits?.cuts,
4193
+ sourceDuration: takeDuration
4194
+ });
4195
+ }
4196
+ function mapRangesToPlayed(ranges, map2) {
4197
+ const mapped = /* @__PURE__ */ new Map();
4198
+ for (const [blockId, range] of ranges) {
4199
+ mapped.set(blockId, {
4200
+ startSec: map2.snapSourceToPlayed(range.startSec),
4201
+ endSec: map2.snapSourceToPlayed(range.endSec)
4202
+ });
4203
+ }
4204
+ return mapped;
4205
+ }
3979
4206
  function retimeBlocks(blocks, ctx) {
3980
4207
  return blocks.map((block) => {
3981
4208
  const range = ctx.ranges.get(block.id);
@@ -4018,8 +4245,11 @@ async function applyNarrationTiming(doc, container) {
4018
4245
  const timing = parseNarrationTimingJson(sidecarData);
4019
4246
  if (!timing) continue;
4020
4247
  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;
4248
+ const takeRanges = timing.blocks.length > 0 ? matchSidecarBlocks(flat, timing) : proportionalRanges(doc, timing);
4249
+ if (!takeRanges || takeRanges.size === 0) continue;
4250
+ const timeMap = narrationTimeMap(clip, timing.duration);
4251
+ const ranges = timeMap ? mapRangesToPlayed(takeRanges, timeMap) : takeRanges;
4252
+ const playedDuration = timeMap ? timeMap.playedDuration : timing.duration;
4023
4253
  const ctx = {
4024
4254
  clipStart: clip.startAt,
4025
4255
  ranges,
@@ -4028,7 +4258,7 @@ async function applyNarrationTiming(doc, container) {
4028
4258
  shift: 0
4029
4259
  };
4030
4260
  const blocks = retimeBlocks(doc.blocks, ctx);
4031
- const duration = Math.max(clip.startAt + timing.duration, ctx.cursor);
4261
+ const duration = Math.max(clip.startAt + playedDuration, ctx.cursor);
4032
4262
  const retimed = {
4033
4263
  ...doc,
4034
4264
  blocks,
@@ -2,7 +2,7 @@ import {
2
2
  assertMarkdownDocumentWithinLimits,
3
3
  parseMarkdown,
4
4
  toMdast
5
- } from "./chunk-CW24CH7I.js";
5
+ } from "./chunk-HUXK7EOT.js";
6
6
  import {
7
7
  formatFrontmatterYaml,
8
8
  getChildren,
@@ -1,3 +1,7 @@
1
+ import {
2
+ createMediaTimeMap
3
+ } from "./chunk-JSGIWW35.js";
4
+
1
5
  // src/schemas/Media.ts
2
6
  function flatten(blocks, out = []) {
3
7
  for (const b of blocks) {
@@ -6,17 +10,104 @@ function flatten(blocks, out = []) {
6
10
  }
7
11
  return out;
8
12
  }
13
+ function clipTimeMap(clip, sourceDuration) {
14
+ return createMediaTimeMap({
15
+ clipStart: clip.clipStart,
16
+ clipEnd: clip.clipEnd,
17
+ cuts: clip.edits?.cuts,
18
+ sourceDuration
19
+ });
20
+ }
9
21
  function clipLength(clip) {
10
22
  if (clip.clipEnd == null) return null;
11
- return Math.max(0, clip.clipEnd - (clip.clipStart ?? 0));
23
+ if (!clip.edits?.cuts?.length) return Math.max(0, clip.clipEnd - (clip.clipStart ?? 0));
24
+ return clipTimeMap(clip).playedDuration;
12
25
  }
13
26
  function intrinsicPlayedLength(clip, opts) {
14
27
  if (clip.clipEnd != null || clip.lockToBlock === true) return null;
15
28
  const intrinsic = opts?.intrinsicDuration?.(clip);
16
29
  if (intrinsic == null || !Number.isFinite(intrinsic) || intrinsic <= 0) return null;
17
- const played = intrinsic - (clip.clipStart ?? 0);
30
+ const played = clip.edits?.cuts?.length ? clipTimeMap(clip, intrinsic).playedDuration : intrinsic - (clip.clipStart ?? 0);
18
31
  return played > 0 ? played : null;
19
32
  }
33
+ function emitClip(out, clip, start, end, anchor, blockId, opts) {
34
+ const edits = clip.edits;
35
+ const processed = edits?.fx ? opts?.processedAudio?.(clip) : void 0;
36
+ const base = {
37
+ src: clip.src,
38
+ kind: clip.kind,
39
+ ...clip.placement ? { placement: clip.placement } : {},
40
+ ...clip.pipSize ? { pipSize: clip.pipSize } : {},
41
+ ...clip.pipShape ? { pipShape: clip.pipShape } : {},
42
+ ...clip.pipPosition ? { pipPosition: clip.pipPosition } : {},
43
+ ...clip.lockToBlock != null ? { lockToBlock: clip.lockToBlock } : {},
44
+ anchor,
45
+ ...blockId !== void 0 ? { blockId } : {},
46
+ ...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {},
47
+ ...edits?.gain != null ? { gain: edits.gain } : {},
48
+ ...clip.kind === "video" && edits?.crop ? { crop: edits.crop } : {}
49
+ };
50
+ if (processed && clip.kind === "audio") {
51
+ base.src = processed;
52
+ base.originalSrc = clip.src;
53
+ }
54
+ if (processed && clip.kind === "video") base.audioMuted = true;
55
+ const safeEnd = Math.max(start, end);
56
+ const pieces = [];
57
+ if (!edits?.cuts?.length) {
58
+ pieces.push({ absoluteStart: start, absoluteEnd: safeEnd, sourceIn: clip.clipStart ?? 0 });
59
+ } else {
60
+ let offset = 0;
61
+ for (const range of clipTimeMap(clip).keptRanges()) {
62
+ const segStart = start + offset;
63
+ if (segStart >= safeEnd) break;
64
+ const segEnd = Math.min(safeEnd, segStart + (range.end - range.start));
65
+ pieces.push({ absoluteStart: segStart, absoluteEnd: segEnd, sourceIn: range.start });
66
+ offset += range.end - range.start;
67
+ }
68
+ if (pieces.length === 0) {
69
+ pieces.push({ absoluteStart: start, absoluteEnd: start, sourceIn: clip.clipStart ?? 0 });
70
+ }
71
+ }
72
+ const count = pieces.length;
73
+ pieces.forEach((piece, index) => {
74
+ const entry = {
75
+ ...base,
76
+ id: count > 1 ? `${clip.id}#${index}` : clip.id,
77
+ ...piece,
78
+ ...index === 0 && edits?.fadeIn ? { fadeIn: edits.fadeIn } : {},
79
+ ...index === count - 1 && edits?.fadeOut ? { fadeOut: edits.fadeOut } : {},
80
+ ...count > 1 ? { segment: { groupId: clip.id, index, count } } : {}
81
+ };
82
+ out.push(entry);
83
+ if (processed && clip.kind === "video") out.push(companionAudio(entry, clip, processed));
84
+ });
85
+ }
86
+ function scheduleMediaClip(clip, start, end, anchor, blockId, opts) {
87
+ const out = [];
88
+ emitClip(out, clip, start, end, anchor, blockId, opts);
89
+ return out;
90
+ }
91
+ function companionAudio(entry, clip, processed) {
92
+ const companion = {
93
+ ...entry,
94
+ id: `${entry.id}~audio`,
95
+ kind: "audio",
96
+ src: processed,
97
+ originalSrc: clip.src,
98
+ derivedFrom: clip.id
99
+ };
100
+ delete companion.crop;
101
+ delete companion.audioMuted;
102
+ delete companion.placement;
103
+ delete companion.pipSize;
104
+ delete companion.pipShape;
105
+ delete companion.pipPosition;
106
+ if (entry.segment) {
107
+ companion.segment = { ...entry.segment, groupId: `${clip.id}~audio` };
108
+ }
109
+ return companion;
110
+ }
20
111
  function baseTimelineEnd(doc) {
21
112
  const blockEnd = flatten(doc.blocks).reduce(
22
113
  (max, b) => Math.max(max, b.startTime + b.duration),
@@ -40,22 +131,7 @@ function resolveMediaSchedule(doc, opts) {
40
131
  }
41
132
  const natLen = intrinsicPlayedLength(clip, opts);
42
133
  if (natLen != null) end = Math.min(end, start + natLen);
43
- out.push({
44
- id: clip.id,
45
- src: clip.src,
46
- kind: clip.kind,
47
- ...clip.placement ? { placement: clip.placement } : {},
48
- ...clip.pipSize ? { pipSize: clip.pipSize } : {},
49
- ...clip.pipShape ? { pipShape: clip.pipShape } : {},
50
- ...clip.pipPosition ? { pipPosition: clip.pipPosition } : {},
51
- ...clip.lockToBlock != null ? { lockToBlock: clip.lockToBlock } : {},
52
- absoluteStart: start,
53
- absoluteEnd: Math.max(start, end),
54
- sourceIn: clip.clipStart ?? 0,
55
- anchor: "block",
56
- blockId: block.id,
57
- ...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}
58
- });
134
+ emitClip(out, clip, start, end, "block", block.id, opts);
59
135
  }
60
136
  }
61
137
  for (const clip of doc.documentMedia ?? []) {
@@ -64,21 +140,7 @@ function resolveMediaSchedule(doc, opts) {
64
140
  let end = len != null ? start + len : docEnd;
65
141
  const natLen = intrinsicPlayedLength(clip, opts);
66
142
  if (natLen != null) end = Math.min(end, start + natLen);
67
- out.push({
68
- id: clip.id,
69
- src: clip.src,
70
- kind: clip.kind,
71
- ...clip.placement ? { placement: clip.placement } : {},
72
- ...clip.pipSize ? { pipSize: clip.pipSize } : {},
73
- ...clip.pipShape ? { pipShape: clip.pipShape } : {},
74
- ...clip.pipPosition ? { pipPosition: clip.pipPosition } : {},
75
- ...clip.lockToBlock != null ? { lockToBlock: clip.lockToBlock } : {},
76
- absoluteStart: start,
77
- absoluteEnd: Math.max(start, end),
78
- sourceIn: clip.clipStart ?? 0,
79
- anchor: "document",
80
- ...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}
81
- });
143
+ emitClip(out, clip, start, end, "document", void 0, opts);
82
144
  }
83
145
  return out;
84
146
  }
@@ -232,6 +294,7 @@ function defaultPageStyle(theme) {
232
294
  }
233
295
 
234
296
  export {
297
+ scheduleMediaClip,
235
298
  resolveMediaSchedule,
236
299
  getDocPlaybackDuration,
237
300
  defaultPageStyle