@bendyline/squisq 2.13.0 → 2.13.2

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 (37) hide show
  1. package/dist/{Doc-czvrR7Oy.d.ts → Doc-CYTLtFEu.d.ts} +32 -0
  2. package/dist/{ImageEditDoc-BDkpwCMN.d.ts → ImageEditDoc-DZ4k_O10.d.ts} +1 -1
  3. package/dist/{chunk-Y3HL26X3.js → chunk-2GN67Q2T.js} +1 -1
  4. package/dist/{chunk-PVFDR3XT.js → chunk-3A6YM375.js} +1 -1
  5. package/dist/{chunk-ECPR5JKY.js → chunk-56TW6HE6.js} +7 -7
  6. package/dist/{chunk-4OQFLZ6G.js → chunk-5HWYSUXS.js} +20 -4
  7. package/dist/{chunk-ILLLGZCE.js → chunk-7ZXCS3UU.js} +2 -2
  8. package/dist/{chunk-2DXLKKTS.js → chunk-FUQYQWQR.js} +5 -5
  9. package/dist/{chunk-GX3VSKYS.js → chunk-GLWQTZ4C.js} +1 -1
  10. package/dist/{chunk-MWSGNEH6.js → chunk-HE3HQZWU.js} +1 -0
  11. package/dist/{chunk-JDYWKGG7.js → chunk-HGCIIXHP.js} +1 -1
  12. package/dist/{chunk-E2NFHYCB.js → chunk-ISVJ42I3.js} +7 -1
  13. package/dist/{chunk-HYJ63N6V.js → chunk-WNEH3BZP.js} +14 -0
  14. package/dist/{chunk-FVFTJ64H.js → chunk-YY3OQ3MI.js} +296 -53
  15. package/dist/doc/index.d.ts +11 -6
  16. package/dist/doc/index.js +8 -8
  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 +3 -3
  20. package/dist/index.d.ts +4 -4
  21. package/dist/index.js +12 -12
  22. package/dist/jsonForm/index.d.ts +1 -1
  23. package/dist/jsonForm/index.js +3 -3
  24. package/dist/markdown/index.js +3 -3
  25. package/dist/{materializePageSection-mTAAHNlA.d.ts → materializePageSection-CDps_xUd.d.ts} +1 -1
  26. package/dist/mediaEdit/index.d.ts +2 -2
  27. package/dist/narration/index.d.ts +1 -1
  28. package/dist/narration/index.js +5 -5
  29. package/dist/recommend/index.js +2 -2
  30. package/dist/schemas/index.d.ts +4 -4
  31. package/dist/schemas/index.js +2 -2
  32. package/dist/{themeLibrary-Bsn4YNuq.d.ts → themeLibrary-CMm_jh3a.d.ts} +1 -1
  33. package/dist/timing/index.d.ts +7 -1
  34. package/dist/timing/index.js +1 -1
  35. package/dist/transform/index.d.ts +2 -2
  36. package/dist/transform/index.js +6 -6
  37. package/package.json +1 -1
@@ -27,7 +27,7 @@ import {
27
27
  profileBlockContents,
28
28
  renderMarkdownBlocksHtml,
29
29
  treeToTemplateData
30
- } from "./chunk-JDYWKGG7.js";
30
+ } from "./chunk-HGCIIXHP.js";
31
31
  import {
32
32
  anchorPoint,
33
33
  base64ToUtf8,
@@ -57,7 +57,7 @@ import {
57
57
  validateCustomTemplateDefinition,
58
58
  validateTheme,
59
59
  withAlpha
60
- } from "./chunk-HYJ63N6V.js";
60
+ } from "./chunk-WNEH3BZP.js";
61
61
  import {
62
62
  mediaEditValuesFromAttributes,
63
63
  mediaEditValuesFromParams,
@@ -84,7 +84,7 @@ import {
84
84
  parseVideoEmbedUrl,
85
85
  readFrontmatterThemeId,
86
86
  walkMarkdownTree
87
- } from "./chunk-MWSGNEH6.js";
87
+ } from "./chunk-HE3HQZWU.js";
88
88
  import {
89
89
  resolveBlockTransition
90
90
  } from "./chunk-P53Z4RYC.js";
@@ -1791,12 +1791,12 @@ function statHighlight(input, context) {
1791
1791
  const statTop = groupTop;
1792
1792
  const descTop = statTop + statBoxH + gap;
1793
1793
  const detailTop = descTop + descBoxH + detailGap;
1794
- const pct = (px) => `${Number((px / frameH * 100).toFixed(3))}%`;
1794
+ const pct2 = (px) => `${Number((px / frameH * 100).toFixed(3))}%`;
1795
1795
  const box = (top, height) => ({
1796
1796
  x: accentLayout.textCenterX,
1797
- y: pct(top + height / 2),
1797
+ y: pct2(top + height / 2),
1798
1798
  width: accentLayout.textWidth,
1799
- height: pct(height),
1799
+ height: pct2(height),
1800
1800
  anchor: "center"
1801
1801
  });
1802
1802
  layers.push({
@@ -1880,7 +1880,7 @@ function quoteBlock(input, context) {
1880
1880
  const accentImage = accentForViewport(input.accentImage, viewport);
1881
1881
  const H = viewport.height;
1882
1882
  const W = viewport.width;
1883
- const pct = (px) => `${Number((px / H * 100).toFixed(3))}%`;
1883
+ const pct2 = (px) => `${Number((px / H * 100).toFixed(3))}%`;
1884
1884
  const accentLayout = accentImage ? getAccentLayout(accentImage.position) : DEFAULT_LAYOUT;
1885
1885
  const yShiftPx = accentLayout.adjustY ? accentLayout.textYAdjust / 100 * H : 0;
1886
1886
  const colWidthPct = parseFloat(accentLayout.textWidth);
@@ -1938,9 +1938,9 @@ function quoteBlock(input, context) {
1938
1938
  },
1939
1939
  position: {
1940
1940
  x: colLeft,
1941
- y: pct(bandTopPx),
1941
+ y: pct2(bandTopPx),
1942
1942
  width: accentLayout.textWidth,
1943
- height: pct(titleFit.heightPx),
1943
+ height: pct2(titleFit.heightPx),
1944
1944
  anchor: "top-left"
1945
1945
  },
1946
1946
  animation: { type: "fadeIn", duration: 0.8 }
@@ -1994,7 +1994,7 @@ function quoteBlock(input, context) {
1994
1994
  },
1995
1995
  position: {
1996
1996
  x: accentLayout.textCenterX,
1997
- y: pct(quoteTopPx - decoMarginPx - decoFontSize * 0.6),
1997
+ y: pct2(quoteTopPx - decoMarginPx - decoFontSize * 0.6),
1998
1998
  anchor: "center"
1999
1999
  }
2000
2000
  });
@@ -2020,9 +2020,9 @@ function quoteBlock(input, context) {
2020
2020
  },
2021
2021
  position: {
2022
2022
  x: colLeft,
2023
- y: pct(quoteTopPx),
2023
+ y: pct2(quoteTopPx),
2024
2024
  width: accentLayout.textWidth,
2025
- height: pct(quoteFit.heightPx),
2025
+ height: pct2(quoteFit.heightPx),
2026
2026
  anchor: "top-left"
2027
2027
  },
2028
2028
  animation: motionEntrance(context, { type: "fadeIn", duration: 2 })
@@ -2047,9 +2047,9 @@ function quoteBlock(input, context) {
2047
2047
  },
2048
2048
  position: {
2049
2049
  x: colLeft,
2050
- y: pct(quoteBottomPx + attrGapPx),
2050
+ y: pct2(quoteBottomPx + attrGapPx),
2051
2051
  width: accentLayout.textWidth,
2052
- height: pct(attrFit.heightPx),
2052
+ height: pct2(attrFit.heightPx),
2053
2053
  anchor: "top-left"
2054
2054
  },
2055
2055
  animation: { type: "fadeIn", duration: 1, delay: 1.5 }
@@ -2133,12 +2133,12 @@ function factCard(input, context) {
2133
2133
  const factTop = groupTop;
2134
2134
  const explainTop = factTop + factBoxH + gap;
2135
2135
  const sourceTop = explainTop + explainBoxH + sourceGap;
2136
- const pct = (px) => `${Number((px / frameH * 100).toFixed(3))}%`;
2136
+ const pct2 = (px) => `${Number((px / frameH * 100).toFixed(3))}%`;
2137
2137
  const box = (top, height) => ({
2138
2138
  x: accentLayout.textCenterX,
2139
- y: pct(top + height / 2),
2139
+ y: pct2(top + height / 2),
2140
2140
  width: accentLayout.textWidth,
2141
- height: pct(height),
2141
+ height: pct2(height),
2142
2142
  anchor: "center"
2143
2143
  });
2144
2144
  layers.push({
@@ -3090,7 +3090,7 @@ function fullBleedQuote(input, context) {
3090
3090
  const colors = resolveColorScheme(context, colorScheme);
3091
3091
  const H = viewport.height;
3092
3092
  const W = viewport.width;
3093
- const pct = (px) => `${Number((px / H * 100).toFixed(3))}%`;
3093
+ const pct2 = (px) => `${Number((px / H * 100).toFixed(3))}%`;
3094
3094
  const impactFontSize = themedFontSize(120, context, true);
3095
3095
  const baseFontSize = Math.round(impactFontSize * lengthFactor(text));
3096
3096
  const bandHeightPx = (BAND_BOTTOM3 - BAND_TOP3) * H;
@@ -3141,9 +3141,9 @@ function fullBleedQuote(input, context) {
3141
3141
  },
3142
3142
  position: {
3143
3143
  x: `${COLUMN_LEFT * 100}%`,
3144
- y: pct(topPx),
3144
+ y: pct2(topPx),
3145
3145
  width: `${COLUMN_WIDTH * 100}%`,
3146
- height: pct(fit.heightPx),
3146
+ height: pct2(fit.heightPx),
3147
3147
  anchor: "top-left"
3148
3148
  },
3149
3149
  animation: motionEntrance(
@@ -3605,12 +3605,12 @@ function definitionCard(input, context) {
3605
3605
  const ruleTop = termTop + termH + ruleGap;
3606
3606
  const defTop = ruleTop + RULE_HEIGHT_PX + defGap;
3607
3607
  const originTop = defTop + defH + originGap;
3608
- const pct = (px) => `${(px / viewport.height * 100).toFixed(3)}%`;
3608
+ const pct2 = (px) => `${(px / viewport.height * 100).toFixed(3)}%`;
3609
3609
  const box = (topPx, heightPx) => {
3610
3610
  const height = Math.round(heightPx);
3611
3611
  return {
3612
3612
  x: accentLayout.textCenterX,
3613
- y: pct(topPx + height / 2),
3613
+ y: pct2(topPx + height / 2),
3614
3614
  width: accentLayout.textWidth,
3615
3615
  height,
3616
3616
  anchor: "center"
@@ -3657,7 +3657,7 @@ function definitionCard(input, context) {
3657
3657
  },
3658
3658
  position: {
3659
3659
  x: columnLeft,
3660
- y: pct(ruleTop),
3660
+ y: pct2(ruleTop),
3661
3661
  width: "30%",
3662
3662
  height: `${RULE_HEIGHT_PX}px`,
3663
3663
  anchor: "top-left"
@@ -3883,7 +3883,7 @@ var ATTR_LINE_HEIGHT = 1.4;
3883
3883
  function pullQuoteLockup(options, context) {
3884
3884
  const { text, attribution, overMedia } = options;
3885
3885
  const { theme, viewport } = context;
3886
- const pct = (px) => px / viewport.height * 100;
3886
+ const pct2 = (px) => px / viewport.height * 100;
3887
3887
  const quoteBaseSize = themedFontSize(52, context, true);
3888
3888
  const quoteMinSize = Math.max(18, themedFontSize(24, context, true));
3889
3889
  const attrFontSize = themedFontSize(26, context, false);
@@ -3910,12 +3910,12 @@ function pullQuoteLockup(options, context) {
3910
3910
  const gapPx = attribution ? Math.max(16, fit.fontSize * 0.6) : 0;
3911
3911
  const stackHeightPx = fit.heightPx + gapPx + attrHeightPx;
3912
3912
  const stackTopPx = Math.max(frameTopPx, viewport.height / 2 - stackHeightPx / 2);
3913
- const quoteTopPct = pct(stackTopPx);
3914
- const attrTopPct = pct(stackTopPx + fit.heightPx + gapPx);
3913
+ const quoteTopPct = pct2(stackTopPx);
3914
+ const attrTopPct = pct2(stackTopPx + fit.heightPx + gapPx);
3915
3915
  const decoHeadroomPx = stackTopPx - 0.03 * viewport.height;
3916
3916
  const decoFontSize = Math.min(decoBaseSize, Math.floor(decoHeadroomPx / 0.6));
3917
3917
  const showDeco = decoFontSize >= decoMinSize;
3918
- const decoYPct = quoteTopPct - pct(decoFontSize * 0.55);
3918
+ const decoYPct = quoteTopPct - pct2(decoFontSize * 0.55);
3919
3919
  const shadow = overMedia ? true : shouldUseShadow(context);
3920
3920
  const quoteColor = overMedia ? "#ffffff" : theme.colors.text;
3921
3921
  const attrColor = overMedia ? "rgba(255, 255, 255, 0.85)" : theme.colors.textMuted;
@@ -6884,6 +6884,35 @@ var BOLD_CHAR_WIDTH_EM = 0.66;
6884
6884
  function estimateTextWidth(text, fontSize, charWidthEm = AVERAGE_CHAR_WIDTH_EM) {
6885
6885
  return text.length * fontSize * charWidthEm;
6886
6886
  }
6887
+ var DISPLAY_CHAR_EM = {
6888
+ space: 0.27,
6889
+ narrowLower: 0.36,
6890
+ wideLower: 0.86,
6891
+ lower: 0.57,
6892
+ narrowUpper: 0.4,
6893
+ wideUpper: 0.95,
6894
+ upper: 0.7,
6895
+ digit: 0.57,
6896
+ punct: 0.34,
6897
+ other: 0.62
6898
+ };
6899
+ function displayCharEm(ch) {
6900
+ if (ch === " ") return DISPLAY_CHAR_EM.space;
6901
+ if ("fijlrt".includes(ch)) return DISPLAY_CHAR_EM.narrowLower;
6902
+ if (ch === "m" || ch === "w") return DISPLAY_CHAR_EM.wideLower;
6903
+ if (ch >= "a" && ch <= "z") return DISPLAY_CHAR_EM.lower;
6904
+ if (ch === "I" || ch === "J") return DISPLAY_CHAR_EM.narrowUpper;
6905
+ if (ch === "M" || ch === "W") return DISPLAY_CHAR_EM.wideUpper;
6906
+ if (ch >= "A" && ch <= "Z") return DISPLAY_CHAR_EM.upper;
6907
+ if (ch >= "0" && ch <= "9") return DISPLAY_CHAR_EM.digit;
6908
+ if (".,:;-/'\u2019!?()".includes(ch)) return DISPLAY_CHAR_EM.punct;
6909
+ return DISPLAY_CHAR_EM.other;
6910
+ }
6911
+ function estimateDisplayTextWidth(text, fontSize) {
6912
+ let em = 0;
6913
+ for (const ch of text.normalize("NFD").replace(/\p{M}/gu, "")) em += displayCharEm(ch);
6914
+ return em * fontSize;
6915
+ }
6887
6916
 
6888
6917
  // src/doc/templates/timelineBlock.ts
6889
6918
  var clamp01 = (value) => Math.max(0, Math.min(1, value));
@@ -9921,13 +9950,227 @@ function coverScrimGradient(background) {
9921
9950
  const stops = COVER_SCRIM_STOPS.map((s) => `${withAlpha(background, s.alpha)} ${s.pos}%`);
9922
9951
  return `linear-gradient(0deg, ${stops.join(", ")})`;
9923
9952
  }
9953
+ var FROSTED_LINE_HEIGHT = 1.12;
9954
+ var FROSTED_MAX_LINES = 3;
9955
+ var FROSTED_SQUARE_SHARE = 0.9;
9956
+ var FROSTED_PAD_X_EM = 0.45;
9957
+ var FROSTED_PAD_Y_EM = 0.3;
9958
+ var FROSTED_RADIUS_EM = 0.3;
9959
+ var FROSTED_BLUR_SHARE = 0.0167;
9960
+ var FROSTED_BOTTOM_MARGIN_PCT = { wide: 7, tall: 17 };
9961
+ var FROSTED_TINT_UNDER_LIGHT_TEXT = "rgba(12, 12, 14, 0.26)";
9962
+ var FROSTED_TINT_UNDER_DARK_TEXT = "rgba(250, 250, 250, 0.5)";
9963
+ var FROSTED_BACKDROP_CONTRAST = 0.65;
9964
+ var COVER_SUBTITLE_BASE_PX = 40;
9965
+ var INK_EM = { xHeight: 0.45, ascent: 0.71, accentedCapital: 0.8, descent: 0.165 };
9966
+ function opticalCentreShiftEm(lines) {
9967
+ const first = (lines[0] ?? "").normalize("NFD");
9968
+ const last = lines[lines.length - 1] ?? "";
9969
+ const top = /[A-Z]\p{M}/u.test(first) ? INK_EM.accentedCapital : /[A-Zbdfhklt0-9]|[a-z]\p{M}/u.test(first) ? INK_EM.ascent : INK_EM.xHeight;
9970
+ const bottom = /[gjpqyQ,;]/.test(last) ? INK_EM.descent : 0;
9971
+ return Math.round(((top - bottom) / 2 - INK_EM.xHeight / 2) * 1e3) / 1e3;
9972
+ }
9973
+ function wrapDisplayLine(line, fontSize, maxWidth) {
9974
+ const lines = [];
9975
+ let current = "";
9976
+ for (const word of line.trim().split(/\s+/).filter(Boolean)) {
9977
+ const candidate = current ? `${current} ${word}` : word;
9978
+ if (!current || estimateDisplayTextWidth(candidate, fontSize) <= maxWidth) {
9979
+ current = candidate;
9980
+ } else {
9981
+ lines.push(current);
9982
+ current = word;
9983
+ }
9984
+ }
9985
+ if (current) lines.push(current);
9986
+ return lines.length > 0 ? lines : [""];
9987
+ }
9988
+ function balancedDisplayLines(line, fontSize, maxWidth) {
9989
+ const greedy = wrapDisplayLine(line, fontSize, maxWidth);
9990
+ if (greedy.length < 2) return greedy;
9991
+ let lo = Math.max(
9992
+ ...line.split(/\s+/).filter(Boolean).map((word) => estimateDisplayTextWidth(word, fontSize))
9993
+ );
9994
+ let hi = maxWidth;
9995
+ if (lo >= hi) return greedy;
9996
+ while (hi - lo > 0.5) {
9997
+ const mid = (lo + hi) / 2;
9998
+ if (wrapDisplayLine(line, fontSize, mid).length <= greedy.length) hi = mid;
9999
+ else lo = mid;
10000
+ }
10001
+ return wrapDisplayLine(line, fontSize, hi);
10002
+ }
10003
+ function frostedCoverLayout(title, subtitle, context) {
10004
+ const { width: frameW, height: frameH } = context.viewport;
10005
+ const shortSide = Math.min(frameW, frameH);
10006
+ const maxPlateWidth = FROSTED_SQUARE_SHARE * shortSide;
10007
+ const minSize = themedFontSize(COVER_TITLE_MIN_BASE_PX, context, true);
10008
+ const wrapAt = (size) => {
10009
+ const column2 = maxPlateWidth - 2 * FROSTED_PAD_X_EM * size;
10010
+ const lines2 = title.split("\n").flatMap((line) => balancedDisplayLines(line, size, column2));
10011
+ const width = Math.max(...lines2.map((line) => estimateDisplayTextWidth(line, size)));
10012
+ return { lines: lines2, width, column: column2 };
10013
+ };
10014
+ let fontSize = themedFontSize(fitCoverTitleSize(title), context, true);
10015
+ let wrapped = wrapAt(fontSize);
10016
+ while ((wrapped.lines.length > FROSTED_MAX_LINES || wrapped.width > wrapped.column) && fontSize > minSize) {
10017
+ fontSize = Math.max(minSize, Math.floor(fontSize * 0.95));
10018
+ wrapped = wrapAt(fontSize);
10019
+ }
10020
+ const { lines, column } = wrapped;
10021
+ const textWidth = Math.min(wrapped.width, column);
10022
+ const textHeight = lines.length * fontSize * FROSTED_LINE_HEIGHT;
10023
+ const padX = FROSTED_PAD_X_EM * fontSize;
10024
+ const padY = FROSTED_PAD_Y_EM * fontSize;
10025
+ let sub;
10026
+ if (subtitle?.trim()) {
10027
+ const subSize = themedFontSize(COVER_SUBTITLE_BASE_PX, context, false);
10028
+ const subLines = subtitle.split("\n").flatMap((line) => wrapDisplayLine(line, subSize, column));
10029
+ sub = {
10030
+ lines: subLines,
10031
+ width: Math.min(
10032
+ column,
10033
+ Math.max(...subLines.map((l) => estimateDisplayTextWidth(l, subSize)))
10034
+ ),
10035
+ height: subLines.length * subSize * 1.3,
10036
+ fontSize: subSize,
10037
+ gap: 0.3 * fontSize
10038
+ };
10039
+ }
10040
+ const plateWidth = Math.max(textWidth, sub?.width ?? 0) + 2 * padX;
10041
+ const plateHeight = textHeight + (sub ? sub.gap + sub.height : 0) + 2 * padY;
10042
+ const marginPct = frameH > frameW ? FROSTED_BOTTOM_MARGIN_PCT.tall : FROSTED_BOTTOM_MARGIN_PCT.wide;
10043
+ const plateBottom = frameH * (1 - marginPct / 100);
10044
+ const plateY = plateBottom - plateHeight;
10045
+ const cx = frameW / 2;
10046
+ const longestChars = Math.max(...lines.map((line) => line.length));
10047
+ const titleBoxWidth = Math.max(textWidth, longestChars * fontSize * 0.5 + 1);
10048
+ return {
10049
+ fontSize,
10050
+ lines,
10051
+ plate: {
10052
+ x: cx - plateWidth / 2,
10053
+ y: plateY,
10054
+ width: plateWidth,
10055
+ height: plateHeight,
10056
+ radius: FROSTED_RADIUS_EM * fontSize
10057
+ },
10058
+ // The box spans the plate's padding too: shrink-to-fit measures the font's whole
10059
+ // ascent and descent (about 1.3 em), which overflows a box of 1.12 em lines and
10060
+ // would scale every title down by up to 14%.
10061
+ title: {
10062
+ cx,
10063
+ cy: plateY + padY + textHeight / 2,
10064
+ width: titleBoxWidth,
10065
+ height: textHeight + 2 * padY
10066
+ },
10067
+ ...sub ? {
10068
+ subtitle: {
10069
+ cx,
10070
+ cy: plateY + padY + textHeight + sub.gap + sub.height / 2,
10071
+ width: Math.max(
10072
+ sub.width,
10073
+ Math.max(...sub.lines.map((l) => l.length)) * sub.fontSize * 0.5 + 1
10074
+ ),
10075
+ height: sub.height,
10076
+ fontSize: sub.fontSize,
10077
+ lines: sub.lines
10078
+ }
10079
+ } : {},
10080
+ baselineShift: opticalCentreShiftEm(lines),
10081
+ blur: Math.round(FROSTED_BLUR_SHARE * shortSide)
10082
+ };
10083
+ }
10084
+ function pct(value, total) {
10085
+ return `${Math.round(value / total * 1e4) / 100}%`;
10086
+ }
10087
+ function frostedCoverLayers(input, context) {
10088
+ const { theme, viewport } = context;
10089
+ const layout = frostedCoverLayout(input.title, input.subtitle, context);
10090
+ const lightText = relativeLuminance(theme.colors.text) > 0.5;
10091
+ const { plate } = layout;
10092
+ const layers = [
10093
+ {
10094
+ type: "shape",
10095
+ id: "cover-plate",
10096
+ content: {
10097
+ shape: "rect",
10098
+ fill: lightText ? FROSTED_TINT_UNDER_LIGHT_TEXT : FROSTED_TINT_UNDER_DARK_TEXT,
10099
+ borderRadius: Math.round(plate.radius),
10100
+ backdropBlur: layout.blur,
10101
+ ...lightText ? { backdropContrast: FROSTED_BACKDROP_CONTRAST } : {}
10102
+ },
10103
+ position: {
10104
+ x: pct(plate.x, viewport.width),
10105
+ y: pct(plate.y, viewport.height),
10106
+ width: pct(plate.width, viewport.width),
10107
+ height: pct(plate.height, viewport.height)
10108
+ }
10109
+ },
10110
+ {
10111
+ type: "text",
10112
+ id: "cover-title",
10113
+ content: {
10114
+ text: layout.lines.join("\n"),
10115
+ style: {
10116
+ fontSize: layout.fontSize,
10117
+ fontFamily: getThemeFont(context, "title"),
10118
+ fontWeight: "bold",
10119
+ color: theme.colors.text,
10120
+ textAlign: "center",
10121
+ verticalAlign: "middle",
10122
+ lineHeight: FROSTED_LINE_HEIGHT,
10123
+ baselineShift: layout.baselineShift,
10124
+ shadow: lightText,
10125
+ shrinkToFit: true
10126
+ }
10127
+ },
10128
+ position: {
10129
+ x: "50%",
10130
+ y: pct(layout.title.cy, viewport.height),
10131
+ anchor: "center",
10132
+ width: pct(layout.title.width, viewport.width),
10133
+ height: pct(layout.title.height, viewport.height)
10134
+ },
10135
+ animation: getMotionProfile(context).coverWordReveal ? { type: "wordReveal", duration: 0.5, delay: 0.1, stagger: 0.12 } : { type: "fadeIn", duration: 0.8 }
10136
+ }
10137
+ ];
10138
+ if (layout.subtitle) {
10139
+ layers.push({
10140
+ type: "text",
10141
+ id: "cover-subtitle",
10142
+ content: {
10143
+ text: layout.subtitle.lines.join("\n"),
10144
+ style: {
10145
+ fontSize: layout.subtitle.fontSize,
10146
+ fontFamily: getThemeFont(context, "body"),
10147
+ fontWeight: "normal",
10148
+ color: lightText ? withAlpha(theme.colors.text, 0.86) : theme.colors.textMuted,
10149
+ textAlign: "center",
10150
+ verticalAlign: "middle",
10151
+ lineHeight: 1.3,
10152
+ shrinkToFit: true
10153
+ }
10154
+ },
10155
+ position: {
10156
+ x: "50%",
10157
+ y: pct(layout.subtitle.cy, viewport.height),
10158
+ anchor: "center",
10159
+ width: pct(layout.subtitle.width, viewport.width),
10160
+ height: pct(layout.subtitle.height, viewport.height)
10161
+ },
10162
+ animation: motionEntrance(context, { type: "fadeIn", duration: 0.8, delay: 0.2 })
10163
+ });
10164
+ }
10165
+ return layers;
10166
+ }
9924
10167
  function coverBlock(input, context) {
9925
10168
  const treatment = themedImageTreatment(context, input.imageTreatment);
9926
10169
  const { heroSrc, heroAlt, title, subtitle, ambientMotion, heroCredit, heroLicense } = input;
9927
10170
  const { theme, layout, viewport } = context;
9928
10171
  const standardLightCover = !heroSrc && isStandardLightCover(context);
9929
10172
  const titleFontSize = themedFontSize(fitCoverTitleSize(title), context, true);
9930
- const subtitleFontSize = themedFontSize(40, context, false);
10173
+ const subtitleFontSize = themedFontSize(COVER_SUBTITLE_BASE_PX, context, false);
9931
10174
  const titleYPct = subtitle ? parseFloat(layout.primaryY) : 50;
9932
10175
  const maxWidthPx = parseFloat(layout.maxTextWidth) / 100 * viewport.width;
9933
10176
  const titleH = Math.max(
@@ -9951,31 +10194,30 @@ function coverBlock(input, context) {
9951
10194
  const layers = [];
9952
10195
  if (heroSrc) {
9953
10196
  const imageAnimation = mapAmbientMotion(ambientMotion);
9954
- layers.push(
9955
- {
9956
- type: "image",
9957
- id: "cover-hero",
9958
- content: {
9959
- src: heroSrc,
9960
- alt: heroAlt || title,
9961
- fit: "cover",
9962
- credit: heroCredit,
9963
- license: heroLicense,
9964
- ...treatment ? { treatment } : {}
9965
- },
9966
- position: { x: 0, y: 0, width: "100%", height: "100%" },
9967
- animation: imageAnimation
10197
+ layers.push({
10198
+ type: "image",
10199
+ id: "cover-hero",
10200
+ content: {
10201
+ src: heroSrc,
10202
+ alt: heroAlt || title,
10203
+ fit: "cover",
10204
+ credit: heroCredit,
10205
+ license: heroLicense,
10206
+ ...treatment ? { treatment } : {}
9968
10207
  },
9969
- {
9970
- type: "shape",
9971
- id: "cover-gradient",
9972
- content: {
9973
- shape: "rect",
9974
- fill: coverScrimGradient(theme.colors.background)
9975
- },
9976
- position: { x: 0, y: 0, width: "100%", height: "100%" }
9977
- }
9978
- );
10208
+ position: { x: 0, y: 0, width: "100%", height: "100%" },
10209
+ animation: imageAnimation
10210
+ });
10211
+ if (input.coverStyle === "frosted") return [...layers, ...frostedCoverLayers(input, context)];
10212
+ layers.push({
10213
+ type: "shape",
10214
+ id: "cover-gradient",
10215
+ content: {
10216
+ shape: "rect",
10217
+ fill: coverScrimGradient(theme.colors.background)
10218
+ },
10219
+ position: { x: 0, y: 0, width: "100%", height: "100%" }
10220
+ });
9979
10221
  } else {
9980
10222
  if (standardLightCover) {
9981
10223
  layers.push(
@@ -10084,7 +10326,8 @@ function startBlockToCoverInput(config) {
10084
10326
  subtitle: config.subtitle,
10085
10327
  ambientMotion: config.ambientMotion,
10086
10328
  heroCredit: config.heroCredit,
10087
- heroLicense: config.heroLicense
10329
+ heroLicense: config.heroLicense,
10330
+ ...config.coverStyle ? { coverStyle: config.coverStyle } : {}
10088
10331
  };
10089
10332
  }
10090
10333
  function expandCoverBlock(config, context, template = "cover") {
@@ -1,12 +1,12 @@
1
- import { a6 as Layer, k as Block, Q as Doc, br as TemplateBlock, bs as TemplateContext, y as CustomTemplateDefinition, bu as TemplateRegistry, bA as Theme, c4 as ViewportConfig, a_ as PersistentLayerConfig, at as MotionSpec, R as DocBlock, c5 as ViewportOrientation, aZ as PersistentLayer, bN as TitleBlockInput, bf as SectionHeaderInput, j as BigTextInput, u as ContentBlockInput, bl as StatHighlightInput, b9 as QuoteBlockInput, Y as FactCardInput, bV as TwoColumnInput, J as DateEventInput, a4 as ImageWithCaptionInput, a9 as LeftFeatureInput, bc as RightFeatureInput, ae as MapBlockInput, bk as StartBlockConfig, $ as FullBleedQuoteInput, ac as ListBlockInput, b2 as PhotoGridInput, L as DefinitionCardInput, t as ComparisonBarInput, b8 as PullQuoteInput, c3 as VideoWithCaptionInput, c2 as VideoPullQuoteInput, I as DataTableInput, M as DiagramBlockInput, bQ as TreeBlockInput, bJ as TimelineBlockInput, U as DrawingBlockInput, ai as MarkerStyle, A as AccentImage, a3 as ImageTreatment, a as AccentPosition, c as Animation, d as AnimationType, au as MotionState, ar as MotionProfile, bC as ThemeColorScheme, w as CountUpSpec, bE as ThemeRegistry, aR as PageEmphasis, bD as ThemePageStyle, S as DocDiagnostic, P as DiagramTemplateNode, O as DiagramTemplateEdge, bM as TimelineTemplateTrack, bL as TimelineTemplateLink } from '../Doc-czvrR7Oy.js';
2
- export { V as FRONTMATTER_CUSTOM_TEMPLATES_KEY, W as FRONTMATTER_CUSTOM_THEMES_KEY, a8 as LayoutHints, bb as RenderStyle, bB as ThemeColorPalette, bH as ThemeStyle, bI as ThemeTypography, bW as VIEWPORT_PRESETS, c6 as ViewportPreset, cb as createTemplateContext, ci as getLayoutHints, cl as getTwoColumnPositions, cm as getViewport, cn as getViewportOrientation, cr as isTemplateBlock, cx as scaledFontSize } from '../Doc-czvrR7Oy.js';
1
+ import { a6 as Layer, k as Block, Q as Doc, br as TemplateBlock, bs as TemplateContext, y as CustomTemplateDefinition, bu as TemplateRegistry, bA as Theme, c4 as ViewportConfig, a_ as PersistentLayerConfig, at as MotionSpec, R as DocBlock, c5 as ViewportOrientation, aZ as PersistentLayer, bN as TitleBlockInput, bf as SectionHeaderInput, j as BigTextInput, u as ContentBlockInput, bl as StatHighlightInput, b9 as QuoteBlockInput, Y as FactCardInput, bV as TwoColumnInput, J as DateEventInput, a4 as ImageWithCaptionInput, a9 as LeftFeatureInput, bc as RightFeatureInput, ae as MapBlockInput, bk as StartBlockConfig, $ as FullBleedQuoteInput, ac as ListBlockInput, b2 as PhotoGridInput, L as DefinitionCardInput, t as ComparisonBarInput, b8 as PullQuoteInput, c3 as VideoWithCaptionInput, c2 as VideoPullQuoteInput, I as DataTableInput, M as DiagramBlockInput, bQ as TreeBlockInput, bJ as TimelineBlockInput, U as DrawingBlockInput, ai as MarkerStyle, A as AccentImage, a3 as ImageTreatment, a as AccentPosition, c as Animation, d as AnimationType, au as MotionState, ar as MotionProfile, bC as ThemeColorScheme, w as CountUpSpec, bE as ThemeRegistry, aR as PageEmphasis, bD as ThemePageStyle, S as DocDiagnostic, P as DiagramTemplateNode, O as DiagramTemplateEdge, bM as TimelineTemplateTrack, bL as TimelineTemplateLink } from '../Doc-CYTLtFEu.js';
2
+ export { V as FRONTMATTER_CUSTOM_TEMPLATES_KEY, W as FRONTMATTER_CUSTOM_THEMES_KEY, a8 as LayoutHints, bb as RenderStyle, bB as ThemeColorPalette, bH as ThemeStyle, bI as ThemeTypography, bW as VIEWPORT_PRESETS, c6 as ViewportPreset, cb as createTemplateContext, ci as getLayoutHints, cl as getTwoColumnPositions, cm as getViewport, cn as getViewportOrientation, cr as isTemplateBlock, cx as scaledFontSize } from '../Doc-CYTLtFEu.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-mTAAHNlA.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-mTAAHNlA.js';
5
+ import { c as PageSection } from '../materializePageSection-CDps_xUd.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-CDps_xUd.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-Bsn4YNuq.js';
9
+ export { D as DEFAULT_THEME, g as getAvailableThemes, b as getThemeSummaries, r as resolveTheme } from '../themeLibrary-CMm_jh3a.js';
10
10
 
11
11
  /** Pure build planning and evaluation, shared by interactive slides and frame export. */
12
12
 
@@ -1075,7 +1075,10 @@ declare function resolveCoverSlideSettings(frontmatter: Record<string, unknown>
1075
1075
  *
1076
1076
  * Features:
1077
1077
  * - Full-screen hero image with Ken Burns ambient motion
1078
- * - Gradient overlay for text readability
1078
+ * - Gradient overlay for text readability (default `coverStyle: 'scrim'`), or a clear
1079
+ * photo with the title on a frosted-glass plate (`'frosted'`): the plate rests near
1080
+ * the bottom, grows upward for longer titles, and stays inside the frame's centre
1081
+ * square so a square crop (a social link card) still shows the whole title
1079
1082
  * - Large centered title with optional subtitle
1080
1083
  * - No animation delays (shown at rest, not during playback)
1081
1084
  *
@@ -1102,6 +1105,8 @@ interface CoverBlockInput {
1102
1105
  heroLicense?: string;
1103
1106
  /** Per-block override for the theme's photographic image grade. */
1104
1107
  imageTreatment?: 'none' | 'mono' | 'duotone' | 'warm' | 'cool';
1108
+ /** How a hero cover keeps its title legible (see StartBlockConfig.coverStyle). */
1109
+ coverStyle?: 'scrim' | 'frosted';
1105
1110
  }
1106
1111
  /**
1107
1112
  * 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-ECPR5JKY.js";
78
+ } from "../chunk-56TW6HE6.js";
79
79
  import "../chunk-FTN6SGW5.js";
80
- import "../chunk-2DXLKKTS.js";
80
+ import "../chunk-FUQYQWQR.js";
81
81
  import {
82
82
  BASE_INPUT_DESCRIPTORS,
83
83
  BLOCK_MEDIA_LAYOUT_POLICIES,
@@ -191,7 +191,7 @@ import {
191
191
  wrapWithPersistentLayers,
192
192
  writeCustomTemplatesToFrontmatter,
193
193
  writeCustomThemesToFrontmatter
194
- } from "../chunk-FVFTJ64H.js";
194
+ } from "../chunk-YY3OQ3MI.js";
195
195
  import {
196
196
  ASCII_CHAR_H,
197
197
  ASCII_CHAR_W,
@@ -243,7 +243,7 @@ import {
243
243
  treeFromMarkdownList,
244
244
  treeFromTemplateData,
245
245
  treeToTemplateData
246
- } from "../chunk-JDYWKGG7.js";
246
+ } from "../chunk-HGCIIXHP.js";
247
247
  import "../chunk-SVN7T6BK.js";
248
248
  import {
249
249
  PATH_SHAPE_KINDS,
@@ -275,20 +275,20 @@ import {
275
275
  isTemplateBlock,
276
276
  resolveTheme,
277
277
  scaledFontSize2 as scaledFontSize
278
- } from "../chunk-HYJ63N6V.js";
278
+ } from "../chunk-WNEH3BZP.js";
279
279
  import "../chunk-JSGIWW35.js";
280
280
  import "../chunk-L7QZORLB.js";
281
- import "../chunk-PVFDR3XT.js";
281
+ import "../chunk-3A6YM375.js";
282
282
  import {
283
283
  CONTAINER_TEMPLATES,
284
284
  TABLE_FED_TEMPLATES,
285
285
  isContainerTemplate,
286
286
  resolveTemplateName
287
287
  } from "../chunk-Y25EJEZN.js";
288
- import "../chunk-MWSGNEH6.js";
288
+ import "../chunk-HE3HQZWU.js";
289
289
  import "../chunk-CZ7AUSLI.js";
290
290
  import "../chunk-P53Z4RYC.js";
291
- import "../chunk-4OQFLZ6G.js";
291
+ import "../chunk-5HWYSUXS.js";
292
292
  import "../chunk-GHOPHGOK.js";
293
293
  import "../chunk-XRW6P7IF.js";
294
294
  export {
@@ -1,4 +1,4 @@
1
- import { bA as Theme } from '../Doc-czvrR7Oy.js';
1
+ import { bA as Theme } from '../Doc-CYTLtFEu.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 { r as ColorScheme, A as AccentImage, br as TemplateBlock } from '../Doc-czvrR7Oy.js';
3
+ import { r as ColorScheme, A as AccentImage, br as TemplateBlock } from '../Doc-CYTLtFEu.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-BDkpwCMN.js';
2
- export { E as EditorLayerMeta, I as ImageEditCanvas, c as ImageEditLayerKind, d as ImageEditMeta } from '../ImageEditDoc-BDkpwCMN.js';
1
+ import { a as ImageEditDoc, b as ImageEditLayer } from '../ImageEditDoc-DZ4k_O10.js';
2
+ export { E as EditorLayerMeta, I as ImageEditCanvas, c as ImageEditLayerKind, d as ImageEditMeta } from '../ImageEditDoc-DZ4k_O10.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-czvrR7Oy.js';
5
+ import '../Doc-CYTLtFEu.js';
6
6
  import '../types-Zk8qYuaT.js';
7
7
 
8
8
  /**