@bendyline/squisq 2.13.4 → 2.13.6

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 (34) hide show
  1. package/dist/{Doc-DnRFE0EI.d.ts → Doc-BQUVDooL.d.ts} +9 -0
  2. package/dist/{ImageEditDoc-CadNz387.d.ts → ImageEditDoc-glA5UUau.d.ts} +1 -1
  3. package/dist/{chunk-DRHJD6U4.js → chunk-4O26IMVA.js} +151 -11
  4. package/dist/{chunk-FIPRGGXD.js → chunk-B2MOGSWY.js} +331 -1
  5. package/dist/{chunk-D3JQMZUT.js → chunk-CPD3MFX7.js} +1 -1
  6. package/dist/{chunk-POZCKPCO.js → chunk-CSR6VCSJ.js} +2 -1
  7. package/dist/{chunk-BITSNRJH.js → chunk-FGN4OZVK.js} +5 -5
  8. package/dist/{chunk-JFJZ4NS6.js → chunk-FJNTG3VZ.js} +2 -2
  9. package/dist/{chunk-6NWVLLMI.js → chunk-KVEOD57Z.js} +5 -0
  10. package/dist/{chunk-ISVJ42I3.js → chunk-SG3HXPMF.js} +3 -0
  11. package/dist/{chunk-J2NFT7GH.js → chunk-VQXOX3YN.js} +174 -39
  12. package/dist/doc/index.d.ts +10 -6
  13. package/dist/doc/index.js +6 -6
  14. package/dist/fence/index.d.ts +1 -1
  15. package/dist/generate/index.d.ts +1 -1
  16. package/dist/imageEdit/index.d.ts +3 -3
  17. package/dist/index.d.ts +7 -7
  18. package/dist/index.js +17 -9
  19. package/dist/jsonForm/index.d.ts +1 -1
  20. package/dist/jsonForm/index.js +1 -1
  21. package/dist/markdown/index.d.ts +6 -1
  22. package/dist/markdown/index.js +5 -3
  23. package/dist/{materializePageSection-D84uA6Vk.d.ts → materializePageSection-_ftXlwvB.d.ts} +1 -1
  24. package/dist/mediaEdit/index.d.ts +2 -2
  25. package/dist/narration/index.d.ts +60 -3
  26. package/dist/narration/index.js +8 -4
  27. package/dist/recommend/index.js +2 -2
  28. package/dist/schemas/index.d.ts +4 -4
  29. package/dist/schemas/index.js +1 -1
  30. package/dist/{themeLibrary-in0fafp4.d.ts → themeLibrary-D6GK29l3.d.ts} +1 -1
  31. package/dist/timing/index.d.ts +1 -1
  32. package/dist/transform/index.d.ts +25 -4
  33. package/dist/transform/index.js +7 -5
  34. package/package.json +1 -1
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-FTN6SGW5.js";
4
4
  import {
5
5
  applySavedPresentation
6
- } from "./chunk-DRHJD6U4.js";
6
+ } from "./chunk-4O26IMVA.js";
7
7
  import {
8
8
  SHAPE_NAMES,
9
9
  buildChartData,
@@ -31,7 +31,7 @@ import {
31
31
  templateRegistry,
32
32
  writeCustomTemplatesToFrontmatter,
33
33
  writeCustomThemesToFrontmatter
34
- } from "./chunk-J2NFT7GH.js";
34
+ } from "./chunk-VQXOX3YN.js";
35
35
  import {
36
36
  ASCII_TREE_VOCAB,
37
37
  ASCII_VOCAB,
@@ -54,7 +54,7 @@ import {
54
54
  toCells,
55
55
  toGrid,
56
56
  traceBoxes
57
- } from "./chunk-POZCKPCO.js";
57
+ } from "./chunk-CSR6VCSJ.js";
58
58
  import {
59
59
  iconMarker
60
60
  } from "./chunk-7ZAAICW4.js";
@@ -87,7 +87,7 @@ import {
87
87
  } from "./chunk-L7QZORLB.js";
88
88
  import {
89
89
  parseMarkdown
90
- } from "./chunk-D3JQMZUT.js";
90
+ } from "./chunk-CPD3MFX7.js";
91
91
  import {
92
92
  TABLE_FED_TEMPLATES,
93
93
  TEMPLATE_ALIASES,
@@ -105,7 +105,7 @@ import {
105
105
  parseVideoEmbedUrl,
106
106
  readFrontmatterThemeId,
107
107
  serializeAnnotation
108
- } from "./chunk-6NWVLLMI.js";
108
+ } from "./chunk-KVEOD57Z.js";
109
109
  import {
110
110
  BUILD_PARAM_KEYS,
111
111
  buildSpecParams
@@ -3,14 +3,14 @@ import {
3
3
  parseMarkdown,
4
4
  remarkWikiLinks,
5
5
  toMdast
6
- } from "./chunk-D3JQMZUT.js";
6
+ } from "./chunk-CPD3MFX7.js";
7
7
  import {
8
8
  formatFrontmatterYaml,
9
9
  getChildren,
10
10
  splitFrontmatterBlock,
11
11
  stringifyHtmlNodes,
12
12
  walkMarkdownTree
13
- } from "./chunk-6NWVLLMI.js";
13
+ } from "./chunk-KVEOD57Z.js";
14
14
 
15
15
  // src/markdown/stringify.ts
16
16
  import { unified } from "unified";
@@ -172,11 +172,15 @@ function findNodesByType(root, type) {
172
172
  });
173
173
  return results;
174
174
  }
175
+ function isHtmlLineBreak(rawHtml) {
176
+ return /^<br\s*\/?>$/i.test(rawHtml.trim());
177
+ }
175
178
  function extractPlainText(node) {
176
179
  if ("value" in node && typeof node.value === "string") {
177
180
  return node.value;
178
181
  }
179
182
  if (node.type === "break") return "\n";
183
+ if (node.type === "htmlInline" && isHtmlLineBreak(node.rawHtml)) return "\n";
180
184
  const children = getChildren(node);
181
185
  const separator = node.type === "list" || node.type === "listItem" || node.type === "table" ? "\n" : node.type === "tableRow" ? " " : "";
182
186
  return children.map(extractPlainText).join(separator);
@@ -2433,6 +2437,7 @@ export {
2433
2437
  getChildren,
2434
2438
  walkMarkdownTree,
2435
2439
  findNodesByType,
2440
+ isHtmlLineBreak,
2436
2441
  extractPlainText,
2437
2442
  countNodes,
2438
2443
  parseFrontmatter,
@@ -309,6 +309,9 @@ function validateStartBlock(value, issues) {
309
309
  if (value.coverStyle !== void 0 && value.coverStyle !== "scrim" && value.coverStyle !== "frosted") {
310
310
  add(issues, "startBlock.coverStyle", 'must be "scrim" or "frosted"', value.coverStyle);
311
311
  }
312
+ if (value.coverLayout !== void 0 && value.coverLayout !== "stacked" && value.coverLayout !== "split") {
313
+ add(issues, "startBlock.coverLayout", 'must be "stacked" or "split"', value.coverLayout);
314
+ }
312
315
  }
313
316
  function validateCaptions(value, issues) {
314
317
  if (!isRecord(value)) {
@@ -32,7 +32,7 @@ import {
32
32
  profileBlockContents,
33
33
  renderMarkdownBlocksHtml,
34
34
  treeToTemplateData
35
- } from "./chunk-POZCKPCO.js";
35
+ } from "./chunk-CSR6VCSJ.js";
36
36
  import {
37
37
  anchorPoint,
38
38
  base64ToUtf8,
@@ -81,7 +81,7 @@ import {
81
81
  parseVideoEmbedUrl,
82
82
  readFrontmatterThemeId,
83
83
  walkMarkdownTree
84
- } from "./chunk-6NWVLLMI.js";
84
+ } from "./chunk-KVEOD57Z.js";
85
85
  import {
86
86
  resolveBlockTransition
87
87
  } from "./chunk-P53Z4RYC.js";
@@ -4508,6 +4508,17 @@ function dataTable(input, context) {
4508
4508
 
4509
4509
  // src/doc/templates/chart/parse.ts
4510
4510
  var NUMERIC_COLUMN_THRESHOLD = 0.6;
4511
+ function cellText(cell) {
4512
+ if (typeof cell === "string") return cell;
4513
+ if (cell === null || cell === void 0) return "";
4514
+ if (typeof cell === "number") return Number.isFinite(cell) ? String(cell) : "";
4515
+ return typeof cell === "object" ? "" : String(cell);
4516
+ }
4517
+ function normalizeChartTable(table) {
4518
+ const headers = Array.isArray(table.headers) ? table.headers.map(cellText) : [];
4519
+ const rows = Array.isArray(table.rows) ? table.rows.filter(Array.isArray).map((row) => row.map(cellText)) : [];
4520
+ return { headers, rows };
4521
+ }
4511
4522
  function parseCellNumber(raw) {
4512
4523
  if (typeof raw !== "string") return null;
4513
4524
  let text = raw.trim();
@@ -4553,8 +4564,7 @@ function resolveColumnRef(ref, headers) {
4553
4564
  return null;
4554
4565
  }
4555
4566
  function buildChartData(table, options = {}) {
4556
- const headers = Array.isArray(table.headers) ? table.headers : [];
4557
- const rows = Array.isArray(table.rows) ? table.rows : [];
4567
+ const { headers, rows } = normalizeChartTable(table);
4558
4568
  if (headers.length === 0 || rows.length === 0) return null;
4559
4569
  const warnings = [];
4560
4570
  let labelIndex = 0;
@@ -4972,7 +4982,7 @@ function renderChart(kind, input, context) {
4972
4982
  }
4973
4983
  function resolveTable(input, context) {
4974
4984
  if (Array.isArray(input.headers) && input.headers.length > 0 && Array.isArray(input.rows) && input.rows.length > 0) {
4975
- return { headers: input.headers, rows: input.rows };
4985
+ return normalizeChartTable(input);
4976
4986
  }
4977
4987
  const contents = input.contents ?? context.block?.contents;
4978
4988
  return extractTableFromContents(contents);
@@ -6276,14 +6286,15 @@ function mapForegroundLayers(layers, contentRect, viewport, fontScale, template)
6276
6286
  if (isFullViewportBackground(layer, viewport)) return layer;
6277
6287
  const position = mapPosition(layer.position, contentRect, viewport);
6278
6288
  if (layer.type === "text") {
6289
+ const fontSize = Math.max(12, Math.round(layer.content.style.fontSize * fontScale));
6279
6290
  return {
6280
6291
  ...layer,
6281
- position,
6292
+ position: layer.content.flow?.role === "marker" ? markerPosition(layer, position, fontSize / layer.content.style.fontSize, viewport) : position,
6282
6293
  content: {
6283
6294
  ...layer.content,
6284
6295
  style: {
6285
6296
  ...layer.content.style,
6286
- fontSize: Math.max(12, Math.round(layer.content.style.fontSize * fontScale)),
6297
+ fontSize,
6287
6298
  ...layer.content.style.padding !== void 0 ? { padding: Math.round(layer.content.style.padding * fontScale) } : {}
6288
6299
  }
6289
6300
  }
@@ -6293,6 +6304,20 @@ function mapForegroundLayers(layers, contentRect, viewport, fontScale, template)
6293
6304
  });
6294
6305
  return arrangeTitleFamily(mapped, template, contentRect, viewport);
6295
6306
  }
6307
+ function markerPosition(layer, mapped, textScale, viewport) {
6308
+ if (mapped.width === void 0 || layer.position.width === void 0) return mapped;
6309
+ const width = resolvePositionValue(mapped.width, viewport.width);
6310
+ const needed = Math.ceil(resolvePositionValue(layer.position.width, viewport.width) * textScale);
6311
+ if (needed <= width) return mapped;
6312
+ const grow = needed - width;
6313
+ const anchor = mapped.anchor ?? "top-left";
6314
+ const shift = layer.content.style.textAlign !== "right" || anchor.includes("right") ? 0 : anchor === "center" ? grow / 2 : grow;
6315
+ return {
6316
+ ...mapped,
6317
+ x: resolvePositionValue(mapped.x, viewport.width) - shift,
6318
+ width: needed
6319
+ };
6320
+ }
6296
6321
  function arrangeTitleFamily(layers, template, contentRect, viewport) {
6297
6322
  const centerX = contentRect.x + contentRect.width / 2;
6298
6323
  const centerY = contentRect.y + contentRect.height / 2;
@@ -10334,33 +10359,125 @@ function coverScrimGradient(background) {
10334
10359
  const stops = COVER_SCRIM_STOPS.map((s) => `${withAlpha(background, s.alpha)} ${s.pos}%`);
10335
10360
  return `linear-gradient(0deg, ${stops.join(", ")})`;
10336
10361
  }
10362
+ function fitFrostedText(text, size, minSize, maxPlateWidth, maxLines) {
10363
+ const wrapAt = (s) => {
10364
+ const column = maxPlateWidth - 2 * FROSTED_PAD_X_EM * s;
10365
+ const lines = text.split("\n").flatMap((line) => balancedDisplayLines(line, s, column));
10366
+ const width = Math.max(...lines.map((line) => estimateDisplayTextWidth(line, s)));
10367
+ return { lines, width, column };
10368
+ };
10369
+ let fontSize = size;
10370
+ let wrapped = wrapAt(fontSize);
10371
+ while ((wrapped.lines.length > maxLines || wrapped.width > wrapped.column) && fontSize > minSize) {
10372
+ fontSize = Math.max(minSize, Math.floor(fontSize * 0.95));
10373
+ wrapped = wrapAt(fontSize);
10374
+ }
10375
+ return {
10376
+ fontSize,
10377
+ lines: wrapped.lines,
10378
+ column: wrapped.column,
10379
+ textWidth: Math.min(wrapped.width, wrapped.column)
10380
+ };
10381
+ }
10337
10382
  var FROSTED_LINE_HEIGHT = 1.12;
10338
10383
  var FROSTED_MAX_LINES = 3;
10339
10384
  var FROSTED_SQUARE_SHARE = 0.9;
10340
10385
  var FROSTED_BOTTOM_MARGIN_PCT = { wide: 7, tall: 17 };
10386
+ var FROSTED_TOP_MARGIN_PCT = FROSTED_BOTTOM_MARGIN_PCT;
10341
10387
  var COVER_SUBTITLE_BASE_PX = 40;
10342
- function frostedCoverLayout(title, subtitle, context) {
10388
+ var COVER_DECK_BASE_PX = 114;
10389
+ var FROSTED_SPLIT_TITLE_SCALE = 1.5;
10390
+ var FROSTED_DECK_MAX_LINES = 3;
10391
+ var FROSTED_SPLIT_MIN_GAP_EM = 0.5;
10392
+ function frostedCoverLayout(title, subtitle, context, coverLayout = "stacked") {
10343
10393
  const { width: frameW, height: frameH } = context.viewport;
10344
10394
  const shortSide = Math.min(frameW, frameH);
10345
10395
  const maxPlateWidth = FROSTED_SQUARE_SHARE * shortSide;
10346
10396
  const minSize = themedFontSize(COVER_TITLE_MIN_BASE_PX, context, true);
10347
- const wrapAt = (size) => {
10348
- const column2 = maxPlateWidth - 2 * FROSTED_PAD_X_EM * size;
10349
- const lines2 = title.split("\n").flatMap((line) => balancedDisplayLines(line, size, column2));
10350
- const width = Math.max(...lines2.map((line) => estimateDisplayTextWidth(line, size)));
10351
- return { lines: lines2, width, column: column2 };
10397
+ const tall = frameH > frameW;
10398
+ const marginPct = tall ? FROSTED_BOTTOM_MARGIN_PCT.tall : FROSTED_BOTTOM_MARGIN_PCT.wide;
10399
+ const plateBottom = frameH * (1 - marginPct / 100);
10400
+ const cx = frameW / 2;
10401
+ const fitTitle = (scale) => {
10402
+ const fit = fitFrostedText(
10403
+ title,
10404
+ Math.round(scale * themedFontSize(fitCoverTitleSize(title), context, true)),
10405
+ minSize,
10406
+ maxPlateWidth,
10407
+ FROSTED_MAX_LINES
10408
+ );
10409
+ const textHeight2 = fit.lines.length * fit.fontSize * FROSTED_LINE_HEIGHT;
10410
+ const padX2 = FROSTED_PAD_X_EM * fit.fontSize;
10411
+ const padY2 = FROSTED_PAD_Y_EM * fit.fontSize;
10412
+ return {
10413
+ ...fit,
10414
+ textHeight: textHeight2,
10415
+ padX: padX2,
10416
+ padY: padY2,
10417
+ radius: FROSTED_RADIUS_EM * fit.fontSize,
10418
+ // The box spans the plate's padding too: shrink-to-fit measures the font's whole
10419
+ // ascent and descent (about 1.3 em), which overflows a box of 1.12 em lines and
10420
+ // would scale every title down by up to 14%.
10421
+ box: (plateY2) => ({
10422
+ cx,
10423
+ cy: plateY2 + padY2 + textHeight2 / 2,
10424
+ width: frostedTitleBoxWidth(fit.lines, fit.fontSize, fit.textWidth),
10425
+ height: textHeight2 + 2 * padY2
10426
+ })
10427
+ };
10352
10428
  };
10353
- let fontSize = themedFontSize(fitCoverTitleSize(title), context, true);
10354
- let wrapped = wrapAt(fontSize);
10355
- while ((wrapped.lines.length > FROSTED_MAX_LINES || wrapped.width > wrapped.column) && fontSize > minSize) {
10356
- fontSize = Math.max(minSize, Math.floor(fontSize * 0.95));
10357
- wrapped = wrapAt(fontSize);
10429
+ if (coverLayout === "split" && subtitle?.trim()) {
10430
+ const { fontSize: fontSize2, lines: lines2, textWidth: textWidth2, textHeight: textHeight2, padX: padX2, padY: padY2, radius: radius2, box: box2 } = fitTitle(FROSTED_SPLIT_TITLE_SCALE);
10431
+ const deckMin = themedFontSize(COVER_SUBTITLE_BASE_PX, context, false);
10432
+ const deck = fitFrostedText(
10433
+ subtitle.trim(),
10434
+ Math.max(deckMin, themedFontSize(COVER_DECK_BASE_PX, context, true)),
10435
+ deckMin,
10436
+ maxPlateWidth,
10437
+ FROSTED_DECK_MAX_LINES
10438
+ );
10439
+ const deckPadX = FROSTED_PAD_X_EM * deck.fontSize;
10440
+ const deckHeight = deck.lines.length * deck.fontSize * FROSTED_LINE_HEIGHT + 2 * FROSTED_PAD_Y_EM * deck.fontSize;
10441
+ const deckWidth = deck.textWidth + 2 * deckPadX;
10442
+ const topPct = tall ? FROSTED_TOP_MARGIN_PCT.tall : FROSTED_TOP_MARGIN_PCT.wide;
10443
+ const titlePlate = {
10444
+ x: cx - (textWidth2 + 2 * padX2) / 2,
10445
+ y: frameH * topPct / 100,
10446
+ width: textWidth2 + 2 * padX2,
10447
+ height: textHeight2 + 2 * padY2,
10448
+ radius: radius2
10449
+ };
10450
+ const deckPlate = {
10451
+ x: cx - deckWidth / 2,
10452
+ y: plateBottom - deckHeight,
10453
+ width: deckWidth,
10454
+ height: deckHeight,
10455
+ // The title's corners, so the two plates read as a pair.
10456
+ radius: Math.min(radius2, deckHeight / 2)
10457
+ };
10458
+ const gap = deckPlate.y - (titlePlate.y + titlePlate.height);
10459
+ if (gap >= FROSTED_SPLIT_MIN_GAP_EM * fontSize2) {
10460
+ return {
10461
+ fontSize: fontSize2,
10462
+ lines: lines2,
10463
+ plate: titlePlate,
10464
+ title: box2(titlePlate.y),
10465
+ subtitle: {
10466
+ cx,
10467
+ cy: deckPlate.y + deckHeight / 2,
10468
+ width: frostedTitleBoxWidth(deck.lines, deck.fontSize, deck.textWidth),
10469
+ height: deckHeight,
10470
+ fontSize: deck.fontSize,
10471
+ lines: deck.lines,
10472
+ baselineShift: opticalCentreShiftEm(deck.lines)
10473
+ },
10474
+ subtitlePlate: deckPlate,
10475
+ baselineShift: opticalCentreShiftEm(lines2),
10476
+ blur: frostedBlurPx(context)
10477
+ };
10478
+ }
10358
10479
  }
10359
- const { lines, column } = wrapped;
10360
- const textWidth = Math.min(wrapped.width, column);
10361
- const textHeight = lines.length * fontSize * FROSTED_LINE_HEIGHT;
10362
- const padX = FROSTED_PAD_X_EM * fontSize;
10363
- const padY = FROSTED_PAD_Y_EM * fontSize;
10480
+ const { fontSize, lines, column, textWidth, textHeight, padX, padY, radius, box } = fitTitle(1);
10364
10481
  let sub;
10365
10482
  if (subtitle?.trim()) {
10366
10483
  const subSize = themedFontSize(COVER_SUBTITLE_BASE_PX, context, false);
@@ -10378,10 +10495,7 @@ function frostedCoverLayout(title, subtitle, context) {
10378
10495
  }
10379
10496
  const plateWidth = Math.max(textWidth, sub?.width ?? 0) + 2 * padX;
10380
10497
  const plateHeight = textHeight + (sub ? sub.gap + sub.height : 0) + 2 * padY;
10381
- const marginPct = frameH > frameW ? FROSTED_BOTTOM_MARGIN_PCT.tall : FROSTED_BOTTOM_MARGIN_PCT.wide;
10382
- const plateBottom = frameH * (1 - marginPct / 100);
10383
10498
  const plateY = plateBottom - plateHeight;
10384
- const cx = frameW / 2;
10385
10499
  return {
10386
10500
  fontSize,
10387
10501
  lines,
@@ -10390,17 +10504,9 @@ function frostedCoverLayout(title, subtitle, context) {
10390
10504
  y: plateY,
10391
10505
  width: plateWidth,
10392
10506
  height: plateHeight,
10393
- radius: FROSTED_RADIUS_EM * fontSize
10394
- },
10395
- // The box spans the plate's padding too: shrink-to-fit measures the font's whole
10396
- // ascent and descent (about 1.3 em), which overflows a box of 1.12 em lines and
10397
- // would scale every title down by up to 14%.
10398
- title: {
10399
- cx,
10400
- cy: plateY + padY + textHeight / 2,
10401
- width: frostedTitleBoxWidth(lines, fontSize, textWidth),
10402
- height: textHeight + 2 * padY
10507
+ radius
10403
10508
  },
10509
+ title: box(plateY),
10404
10510
  ...sub ? {
10405
10511
  subtitle: {
10406
10512
  cx,
@@ -10420,7 +10526,7 @@ function frostedCoverLayout(title, subtitle, context) {
10420
10526
  }
10421
10527
  function frostedCoverLayers(input, context) {
10422
10528
  const { theme, viewport } = context;
10423
- const layout = frostedCoverLayout(input.title, input.subtitle, context);
10529
+ const layout = frostedCoverLayout(input.title, input.subtitle, context, input.coverLayout);
10424
10530
  const lightText = frostedUnderLightText(context);
10425
10531
  const layers = [
10426
10532
  frostedPlateLayer("cover-plate", layout.plate, context),
@@ -10452,7 +10558,35 @@ function frostedCoverLayers(input, context) {
10452
10558
  animation: getMotionProfile(context).coverWordReveal ? { type: "wordReveal", duration: 0.5, delay: 0.1, stagger: 0.12 } : { type: "fadeIn", duration: 0.8 }
10453
10559
  }
10454
10560
  ];
10455
- if (layout.subtitle) {
10561
+ if (layout.subtitle && layout.subtitlePlate) {
10562
+ layers.push(frostedPlateLayer("cover-subtitle-plate", layout.subtitlePlate, context), {
10563
+ type: "text",
10564
+ id: "cover-subtitle",
10565
+ content: {
10566
+ text: layout.subtitle.lines.join("\n"),
10567
+ style: {
10568
+ fontSize: layout.subtitle.fontSize,
10569
+ fontFamily: getThemeFont(context, "title"),
10570
+ fontWeight: "bold",
10571
+ color: theme.colors.text,
10572
+ textAlign: "center",
10573
+ verticalAlign: "middle",
10574
+ lineHeight: FROSTED_LINE_HEIGHT,
10575
+ baselineShift: layout.subtitle.baselineShift,
10576
+ shadow: lightText,
10577
+ shrinkToFit: true
10578
+ }
10579
+ },
10580
+ position: {
10581
+ x: "50%",
10582
+ y: pct(layout.subtitle.cy, viewport.height),
10583
+ anchor: "center",
10584
+ width: pct(layout.subtitle.width, viewport.width),
10585
+ height: pct(layout.subtitle.height, viewport.height)
10586
+ },
10587
+ animation: motionEntrance(context, { type: "fadeIn", duration: 0.8, delay: 0.2 })
10588
+ });
10589
+ } else if (layout.subtitle) {
10456
10590
  layers.push({
10457
10591
  type: "text",
10458
10592
  id: "cover-subtitle",
@@ -10644,7 +10778,8 @@ function startBlockToCoverInput(config) {
10644
10778
  ambientMotion: config.ambientMotion,
10645
10779
  heroCredit: config.heroCredit,
10646
10780
  heroLicense: config.heroLicense,
10647
- ...config.coverStyle ? { coverStyle: config.coverStyle } : {}
10781
+ ...config.coverStyle ? { coverStyle: config.coverStyle } : {},
10782
+ ...config.coverLayout ? { coverLayout: config.coverLayout } : {}
10648
10783
  };
10649
10784
  }
10650
10785
  function expandCoverBlock(config, context, template = "cover") {
@@ -1,12 +1,12 @@
1
- import { a7 as Layer, k as Block, R as Doc, bt as TemplateBlock, bu as TemplateContext, z as CustomTemplateDefinition, bw as TemplateRegistry, bC as Theme, c7 as ViewportConfig, b0 as PersistentLayerConfig, av as MotionSpec, S as DocBlock, c8 as ViewportOrientation, a$ as PersistentLayer, bP as TitleBlockInput, bh as SectionHeaderInput, j as BigTextInput, v as ContentBlockInput, bn as StatHighlightInput, bb as QuoteBlockInput, Z as FactCardInput, bY as TwoColumnInput, K as DateEventInput, a5 as ImageWithCaptionInput, aa as LeftFeatureInput, be as RightFeatureInput, n as BottomFeatureInput, ao as MediaOnlyInput, bR as TopFeatureInput, af as MapBlockInput, bm as StartBlockConfig, a0 as FullBleedQuoteInput, ad as ListBlockInput, b4 as PhotoGridInput, M as DefinitionCardInput, u as ComparisonBarInput, ba as PullQuoteInput, c6 as VideoWithCaptionInput, c5 as VideoPullQuoteInput, J as DataTableInput, N as DiagramBlockInput, bT as TreeBlockInput, bL as TimelineBlockInput, V as DrawingBlockInput, aj as MarkerStyle, A as AccentImage, a4 as ImageTreatment, a as AccentPosition, c as Animation, d as AnimationType, aw as MotionState, at as MotionProfile, bE as ThemeColorScheme, x as CountUpSpec, bG as ThemeRegistry, aT as PageEmphasis, bF as ThemePageStyle, T as DocDiagnostic, Q as DiagramTemplateNode, P as DiagramTemplateEdge, bO as TimelineTemplateTrack, bN as TimelineTemplateLink } from '../Doc-DnRFE0EI.js';
2
- export { W as FRONTMATTER_CUSTOM_TEMPLATES_KEY, X as FRONTMATTER_CUSTOM_THEMES_KEY, a9 as LayoutHints, bd as RenderStyle, bD as ThemeColorPalette, bJ as ThemeStyle, bK as ThemeTypography, bZ as VIEWPORT_PRESETS, c9 as ViewportPreset, ce as createTemplateContext, cl as getLayoutHints, co as getTwoColumnPositions, cp as getViewport, cq as getViewportOrientation, cu as isTemplateBlock, cA as scaledFontSize } from '../Doc-DnRFE0EI.js';
1
+ import { a7 as Layer, k as Block, R as Doc, bt as TemplateBlock, bu as TemplateContext, z as CustomTemplateDefinition, bw as TemplateRegistry, bC as Theme, c7 as ViewportConfig, b0 as PersistentLayerConfig, av as MotionSpec, S as DocBlock, c8 as ViewportOrientation, a$ as PersistentLayer, bP as TitleBlockInput, bh as SectionHeaderInput, j as BigTextInput, v as ContentBlockInput, bn as StatHighlightInput, bb as QuoteBlockInput, Z as FactCardInput, bY as TwoColumnInput, K as DateEventInput, a5 as ImageWithCaptionInput, aa as LeftFeatureInput, be as RightFeatureInput, n as BottomFeatureInput, ao as MediaOnlyInput, bR as TopFeatureInput, af as MapBlockInput, bm as StartBlockConfig, a0 as FullBleedQuoteInput, ad as ListBlockInput, b4 as PhotoGridInput, M as DefinitionCardInput, u as ComparisonBarInput, ba as PullQuoteInput, c6 as VideoWithCaptionInput, c5 as VideoPullQuoteInput, J as DataTableInput, N as DiagramBlockInput, bT as TreeBlockInput, bL as TimelineBlockInput, V as DrawingBlockInput, aj as MarkerStyle, A as AccentImage, a4 as ImageTreatment, a as AccentPosition, c as Animation, d as AnimationType, aw as MotionState, at as MotionProfile, bE as ThemeColorScheme, x as CountUpSpec, bG as ThemeRegistry, aT as PageEmphasis, bF as ThemePageStyle, T as DocDiagnostic, Q as DiagramTemplateNode, P as DiagramTemplateEdge, bO as TimelineTemplateTrack, bN as TimelineTemplateLink } from '../Doc-BQUVDooL.js';
2
+ export { W as FRONTMATTER_CUSTOM_TEMPLATES_KEY, X as FRONTMATTER_CUSTOM_THEMES_KEY, a9 as LayoutHints, bd as RenderStyle, bD as ThemeColorPalette, bJ as ThemeStyle, bK as ThemeTypography, bZ as VIEWPORT_PRESETS, c9 as ViewportPreset, ce as createTemplateContext, cl as getLayoutHints, co as getTwoColumnPositions, cp as getViewport, cq as getViewportOrientation, cu as isTemplateBlock, cA as scaledFontSize } from '../Doc-BQUVDooL.js';
3
3
  import { g as BuildStep, M as MarkdownBlockNode, S as MarkdownListItem, f as BuildSpec, e as BuildCursor, h as BuildTiming, V as MarkdownNode, ae as TransitionType, ad as TransitionDirection, z as MarkdownHeading, v as MarkdownDocument, a2 as MarkdownTable, q as MarkdownCodeBlock, R as MarkdownList } from '../types-Zk8qYuaT.js';
4
4
  import { C as CoercedBlockMeta } from '../annotationCoercion-hTvELpIN.js';
5
- import { c as PageSection } from '../materializePageSection-D84uA6Vk.js';
6
- export { M as MaterializePageSectionOptions, a as MaterializePageSectionsOptions, P as PageMedia, b as PageRichText, d as PageSectionDiagnostic, e as PageSectionItem, f as PageSectionMaterialization, g as PageSectionSource, h as PageSpatialKind, i as PageTransformHints, m as materializePageSection, j as materializePageSections, r as resolvePageStyle } from '../materializePageSection-D84uA6Vk.js';
5
+ import { c as PageSection } from '../materializePageSection-_ftXlwvB.js';
6
+ export { M as MaterializePageSectionOptions, a as MaterializePageSectionsOptions, P as PageMedia, b as PageRichText, d as PageSectionDiagnostic, e as PageSectionItem, f as PageSectionMaterialization, g as PageSectionSource, h as PageSpatialKind, i as PageTransformHints, m as materializePageSection, j as materializePageSections, r as resolvePageStyle } from '../materializePageSection-_ftXlwvB.js';
7
7
  import { C as ContentContainer } from '../ContentContainer-B2w9sUoL.js';
8
8
  import { V as ViewIssue } from '../viewState-lTZS-hP_.js';
9
- export { D as DEFAULT_THEME, g as getAvailableThemes, b as getThemeSummaries, r as resolveTheme } from '../themeLibrary-in0fafp4.js';
9
+ export { D as DEFAULT_THEME, g as getAvailableThemes, b as getThemeSummaries, r as resolveTheme } from '../themeLibrary-D6GK29l3.js';
10
10
 
11
11
  /** Pure build planning and evaluation, shared by interactive slides and frame export. */
12
12
 
@@ -1192,7 +1192,9 @@ declare function resolveCoverSlideSettings(frontmatter: Record<string, unknown>
1192
1192
  * photo with the title on a frosted-glass plate (`'frosted'`): the plate rests near
1193
1193
  * the bottom, grows upward for longer titles, and stays inside the frame's centre
1194
1194
  * square so a square crop (a social link card) still shows the whole title
1195
- * - Large centered title with optional subtitle
1195
+ * - Large centered title with optional subtitle; a frosted cover can split them
1196
+ * (`coverLayout: 'split'`), the title on a plate up top and the subtitle as a deck
1197
+ * on its own plate below, for a vertical cover's name and hook
1196
1198
  * - No animation delays (shown at rest, not during playback)
1197
1199
  *
1198
1200
  * This is shared code used by both site and efb-app doc renderers.
@@ -1220,6 +1222,8 @@ interface CoverBlockInput {
1220
1222
  imageTreatment?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool';
1221
1223
  /** How a hero cover keeps its title legible (see StartBlockConfig.coverStyle). */
1222
1224
  coverStyle?: 'scrim' | 'frosted';
1225
+ /** Where a frosted cover puts its subtitle (see StartBlockConfig.coverLayout). */
1226
+ coverLayout?: 'stacked' | 'split';
1223
1227
  }
1224
1228
  /**
1225
1229
  * Generate cover block layers from StartBlockConfig.
package/dist/doc/index.js CHANGED
@@ -75,9 +75,9 @@ import {
75
75
  validateMarkdownSource,
76
76
  wordRevealDelay,
77
77
  writeDashboardLayoutsToFrontmatter
78
- } from "../chunk-BITSNRJH.js";
78
+ } from "../chunk-FGN4OZVK.js";
79
79
  import "../chunk-FTN6SGW5.js";
80
- import "../chunk-DRHJD6U4.js";
80
+ import "../chunk-4O26IMVA.js";
81
81
  import {
82
82
  BASE_INPUT_DESCRIPTORS,
83
83
  BLOCK_MEDIA_LAYOUT_POLICIES,
@@ -195,7 +195,7 @@ import {
195
195
  wrapWithPersistentLayers,
196
196
  writeCustomTemplatesToFrontmatter,
197
197
  writeCustomThemesToFrontmatter
198
- } from "../chunk-J2NFT7GH.js";
198
+ } from "../chunk-VQXOX3YN.js";
199
199
  import {
200
200
  ASCII_CHAR_H,
201
201
  ASCII_CHAR_W,
@@ -248,7 +248,7 @@ import {
248
248
  treeFromMarkdownList,
249
249
  treeFromTemplateData,
250
250
  treeToTemplateData
251
- } from "../chunk-POZCKPCO.js";
251
+ } from "../chunk-CSR6VCSJ.js";
252
252
  import "../chunk-SVN7T6BK.js";
253
253
  import {
254
254
  PATH_SHAPE_KINDS,
@@ -283,14 +283,14 @@ import {
283
283
  } from "../chunk-WNEH3BZP.js";
284
284
  import "../chunk-JSGIWW35.js";
285
285
  import "../chunk-L7QZORLB.js";
286
- import "../chunk-D3JQMZUT.js";
286
+ import "../chunk-CPD3MFX7.js";
287
287
  import {
288
288
  CONTAINER_TEMPLATES,
289
289
  TABLE_FED_TEMPLATES,
290
290
  isContainerTemplate,
291
291
  resolveTemplateName
292
292
  } from "../chunk-DOOUVVOM.js";
293
- import "../chunk-6NWVLLMI.js";
293
+ import "../chunk-KVEOD57Z.js";
294
294
  import "../chunk-CZ7AUSLI.js";
295
295
  import "../chunk-P53Z4RYC.js";
296
296
  import "../chunk-5HWYSUXS.js";
@@ -1,4 +1,4 @@
1
- import { bC as Theme } from '../Doc-DnRFE0EI.js';
1
+ import { bC as Theme } from '../Doc-BQUVDooL.js';
2
2
  import '../types-Zk8qYuaT.js';
3
3
 
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  import { E as ExtractedElement } from '../contentExtractor-BNfVJV2U.js';
2
2
  export { C as ComparisonData, D as DateData, a as DefinitionData, b as ExtractionOptions, c as ExtractionResult, d as ExtractionType, F as FactData, I as ImpactLineData, L as ListData, Q as QuoteData, S as StatData, e as extractContent, s as stripMarkdown } from '../contentExtractor-BNfVJV2U.js';
3
- import { s as ColorScheme, A as AccentImage, bt as TemplateBlock } from '../Doc-DnRFE0EI.js';
3
+ import { s as ColorScheme, A as AccentImage, bt as TemplateBlock } from '../Doc-BQUVDooL.js';
4
4
  import '../types-Zk8qYuaT.js';
5
5
 
6
6
  /**
@@ -1,8 +1,8 @@
1
- import { a as ImageEditDoc, b as ImageEditLayer } from '../ImageEditDoc-CadNz387.js';
2
- export { E as EditorLayerMeta, I as ImageEditCanvas, c as ImageEditLayerKind, d as ImageEditMeta } from '../ImageEditDoc-CadNz387.js';
1
+ import { a as ImageEditDoc, b as ImageEditLayer } from '../ImageEditDoc-glA5UUau.js';
2
+ export { E as EditorLayerMeta, I as ImageEditCanvas, c as ImageEditLayerKind, d as ImageEditMeta } from '../ImageEditDoc-glA5UUau.js';
3
3
  import { C as ContentContainer } from '../ContentContainer-B2w9sUoL.js';
4
4
  import { V as Version, C as CoalesceOptions, P as PrunePolicy } from '../types-8QjefM9J.js';
5
- import '../Doc-DnRFE0EI.js';
5
+ import '../Doc-BQUVDooL.js';
6
6
  import '../types-Zk8qYuaT.js';
7
7
 
8
8
  /**