@bendyline/squisq 2.4.4 → 2.6.0

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.
@@ -456,6 +456,25 @@ interface SectionHeaderInput extends BaseTemplateBlock {
456
456
  /** Ambient motion for background image */
457
457
  ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';
458
458
  }
459
+ /**
460
+ * Big text - thumbnail-style display card with the title in gigantic
461
+ * uppercase type, sized to fill the frame minus a small padding margin.
462
+ * With an image, the title sits over a full-bleed photo with a theme-tinted
463
+ * contrast bloom behind the text; without one, it sits on a clean theme
464
+ * surface. Shows nothing but the title — body content feeds narration only.
465
+ * Built for YouTube covers and social thumbnails.
466
+ */
467
+ interface BigTextInput extends BaseTemplateBlock {
468
+ template: 'bigText';
469
+ /** The display text (rendered uppercase). */
470
+ title: string;
471
+ /** Optional full-bleed background image path. */
472
+ imageSrc?: string;
473
+ /** Alt text for the background image. */
474
+ imageAlt?: string;
475
+ /** Ambient motion for the background image. */
476
+ ambientMotion?: 'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight';
477
+ }
459
478
  /**
460
479
  * Content block - loss-averse heading and body layout.
461
480
  *
@@ -1066,7 +1085,7 @@ type ChartTemplateInput = BarChartInput | ColumnChartInput | PieChartInput | Don
1066
1085
  /**
1067
1086
  * Union of all template block types.
1068
1087
  */
1069
- type TemplateBlock = TitleBlockInput | SectionHeaderInput | ContentBlockInput | StatHighlightInput | QuoteBlockInput | FactCardInput | TwoColumnInput | DateEventInput | ImageWithCaptionInput | LeftFeatureInput | RightFeatureInput | MapBlockInput | FullBleedQuoteInput | ListBlockInput | PhotoGridInput | DefinitionCardInput | ComparisonBarInput | PullQuoteInput | VideoWithCaptionInput | VideoPullQuoteInput | DataTableInput | BarChartInput | ColumnChartInput | PieChartInput | DonutChartInput | LineChartInput | AreaChartInput | ScatterChartInput | DiagramBlockInput | TreeBlockInput | TimelineBlockInput | DrawingBlockInput | RawLayersInput;
1088
+ type TemplateBlock = TitleBlockInput | SectionHeaderInput | BigTextInput | ContentBlockInput | StatHighlightInput | QuoteBlockInput | FactCardInput | TwoColumnInput | DateEventInput | ImageWithCaptionInput | LeftFeatureInput | RightFeatureInput | MapBlockInput | FullBleedQuoteInput | ListBlockInput | PhotoGridInput | DefinitionCardInput | ComparisonBarInput | PullQuoteInput | VideoWithCaptionInput | VideoPullQuoteInput | DataTableInput | BarChartInput | ColumnChartInput | PieChartInput | DonutChartInput | LineChartInput | AreaChartInput | ScatterChartInput | DiagramBlockInput | TreeBlockInput | TimelineBlockInput | DrawingBlockInput | RawLayersInput;
1070
1089
  /**
1071
1090
  * A block can be either a raw Block or a TemplateBlock.
1072
1091
  */
@@ -2638,6 +2657,15 @@ interface TextStyle {
2638
2657
  padding?: number;
2639
2658
  /** Maximum number of lines before truncation (adds "..." to last line) */
2640
2659
  maxLines?: number;
2660
+ /**
2661
+ * Fit the rendered text to the layer's box: the renderer measures the
2662
+ * real glyphs and scales the text down (never up) to stay inside the box,
2663
+ * and — when vertically centered — centers the whole wrapped block on the
2664
+ * anchor instead of hanging extra lines below it. For display type sized
2665
+ * from estimates (e.g. the bigText template), where the actual font can
2666
+ * run wider than any pure width model.
2667
+ */
2668
+ shrinkToFit?: boolean;
2641
2669
  }
2642
2670
  /**
2643
2671
  * Animation to apply to a layer.
@@ -2770,4 +2798,4 @@ declare function getBlockAtTime(blocks: Block[], time: number): Block | null;
2770
2798
  */
2771
2799
  declare function getCaptionAtTime(captions: CaptionTrack | undefined, time: number): CaptionPhrase | null;
2772
2800
 
2773
- export { type ImageTreatment as $, type AccentImage as A, type BarChartInput as B, type CaptionPhrase as C, DARK_SURFACE as D, DEFAULT_TITLE_FONT as E, type DataTableInput as F, type DateEventInput as G, type DeepPartial as H, type DefinitionCardInput as I, type DiagramBlockInput as J, type DiagramEdgeAnchor as K, type DiagramTemplateEdge as L, type DiagramTemplateNode as M, type Doc as N, type DocBlock as O, type DocDiagnostic as P, type DonutChartInput as Q, type DrawingBlockInput as R, FRONTMATTER_CUSTOM_TEMPLATES_KEY as S, FRONTMATTER_CUSTOM_THEMES_KEY as T, type FactCardInput as U, type FontFamily as V, type FontFamilyKind as W, type FullBleedQuoteInput as X, type GradientBackgroundConfig as Y, type ImageBackgroundConfig as Z, type ImageLayer as _, type AccentPosition as a, type SectionHeaderInput as a$, type ImageWithCaptionInput as a0, LIGHT_SURFACE as a1, type Layer as a2, type LayerRepeat as a3, type LayoutHints as a4, type LeftFeatureInput as a5, type LineChartInput as a6, type LinearGradient as a7, type ListBlockInput as a8, type MapBlockInput as a9, type PageBackground as aA, type PageDesignFamily as aB, type PageEmphasis as aC, type PageHeadingTreatment as aD, type PageSectionKind as aE, type PageSectionOverride as aF, type PageTokens as aG, type PathLayer as aH, type PatternBackgroundConfig as aI, type PersistentLayer as aJ, type PersistentLayerConfig as aK, type PersistentLayerTemplate as aL, type PersistentLayerTemplateConfig as aM, type PersistentLayerTemplateType as aN, type PhotoGridInput as aO, type PieChartInput as aP, type PipStyle as aQ, type Position as aR, type ProgressIndicatorConfig as aS, type PromotedBodyAnnotation as aT, type PullQuoteInput as aU, type QuoteBlockInput as aV, type RawLayersInput as aW, type RenderStyle as aX, type RightFeatureInput as aY, type ScatterChartInput as aZ, type ScheduledClip as a_, type MapLayer as aa, type MapMarker as ab, type MapTileStyle as ac, type MarkerStyle as ad, type MediaClip as ae, type MediaScheduleOptions as af, type MermaidLayer as ag, PAGE_ACCENT_STRATEGIES as ah, PAGE_BACKGROUNDS as ai, PAGE_BACKGROUND_RHYTHMS as aj, PAGE_DESIGN_FAMILIES as ak, PAGE_DIVIDERS as al, PAGE_EMPHASES as am, PAGE_EYEBROWS as an, PAGE_HEADING_CASES as ao, PAGE_HEADING_SCALES as ap, PAGE_HEADING_UNDERLINES as aq, PAGE_HERO_STYLES as ar, PAGE_IMAGE_FRAMINGS as as, PAGE_NUMERAL_STYLES as at, PAGE_PATTERNS as au, PAGE_QUOTE_MARKS as av, PAGE_SECTION_KINDS as aw, PAGE_SECTION_SPACINGS as ax, PAGE_SHADOWS as ay, type PageAccentRotation as az, type AmbientGradientConfig as b, getTwoColumnPositions as b$, type ShapeFilter as b0, type ShapeLayer as b1, type ShapePattern as b2, type SolidBackgroundConfig as b3, type StartBlockConfig as b4, type StatHighlightInput as b5, type SurfaceScheme as b6, THEME_SCHEMA_VERSION as b7, type TableLayer as b8, type TableLayerStyle as b9, type TwoColumnInput as bA, VIEWPORT_PRESETS as bB, type VideoLayer as bC, type VideoPipPosition as bD, type VideoPipShape as bE, type VideoPipSize as bF, type VideoPlacement as bG, type VideoPresentation as bH, type VideoPullQuoteInput as bI, type VideoWithCaptionInput as bJ, type ViewportConfig as bK, type ViewportOrientation as bL, type ViewportPreset as bM, type VignetteConfig as bN, applySurface as bO, calculateDuration as bP, calculateFontScale as bQ, createTemplateContext as bR, createTheme as bS, createThemeRegistry as bT, getAspectRatioString as bU, getBlockAtTime as bV, getCaptionAtTime as bW, getDocPlaybackDuration as bX, getLayoutHints as bY, getSafeTextBounds as bZ, getSegmentAtTime as b_, type TemplateBlock as ba, type TemplateContext as bb, type TemplateFunction as bc, type TemplateRegistry as bd, type TextLayer as be, type TextStyle as bf, type Theme as bg, type ThemeColorPalette as bh, type ThemeColorScheme as bi, type ThemePageStyle as bj, type ThemeRegistry as bk, type ThemeSchemaVersion as bl, type ThemeSeedColors as bm, type ThemeStyle as bn, type ThemeTypography as bo, type TimelineBlockInput as bp, type TimelineTemplateEvent as bq, type TimelineTemplateLink as br, type TimelineTemplateTrack as bs, type TitleBlockInput as bt, type TitleCaptionConfig as bu, type TreeBlockInput as bv, type TreeLayer as bw, type TreeLayerItem as bx, type TreeLayerStyle as by, type TreeTemplateItem as bz, type Animation as c, getViewport as c0, getViewportOrientation as c1, isCustomTemplateDefinition as c2, isPersistentLayerTemplate as c3, isTemplateBlock as c4, scaledFontSize$1 as c5, resolveMediaSchedule as c6, scaledFontSize as c7, validateCustomTemplateDefinition as c8, type AnimationType as d, type AreaChartInput as e, type AudioBookmark as f, type AudioSegment as g, type AudioTimingData as h, type AudioTrack as i, type Block as j, type BlockConnection as k, type BorderStyle as l, type CaptionTrack as m, type CaptionWord as n, type ChartBaseFields as o, type ChartTemplateInput as p, type ColorScheme as q, type ColumnChartInput as r, type ComparisonBarInput as s, type ContentBlockInput as t, type CornerBrandingConfig as u, type CustomTemplateDefinition as v, type CustomTemplateLayer as w, type CustomTemplateValidationError as x, type CustomTemplateValidationResult as y, DEFAULT_DOC_FONT as z };
2801
+ export { type ImageLayer as $, type AccentImage as A, type BarChartInput as B, type CaptionPhrase as C, DARK_SURFACE as D, DEFAULT_DOC_FONT as E, DEFAULT_TITLE_FONT as F, type DataTableInput as G, type DateEventInput as H, type DeepPartial as I, type DefinitionCardInput as J, type DiagramBlockInput as K, type DiagramEdgeAnchor as L, type DiagramTemplateEdge as M, type DiagramTemplateNode as N, type Doc as O, type DocBlock as P, type DocDiagnostic as Q, type DonutChartInput as R, type DrawingBlockInput as S, FRONTMATTER_CUSTOM_TEMPLATES_KEY as T, FRONTMATTER_CUSTOM_THEMES_KEY as U, type FactCardInput as V, type FontFamily as W, type FontFamilyKind as X, type FullBleedQuoteInput as Y, type GradientBackgroundConfig as Z, type ImageBackgroundConfig as _, type AccentPosition as a, type ScheduledClip as a$, type ImageTreatment as a0, type ImageWithCaptionInput as a1, LIGHT_SURFACE as a2, type Layer as a3, type LayerRepeat as a4, type LayoutHints as a5, type LeftFeatureInput as a6, type LineChartInput as a7, type LinearGradient as a8, type ListBlockInput as a9, type PageAccentRotation as aA, type PageBackground as aB, type PageDesignFamily as aC, type PageEmphasis as aD, type PageHeadingTreatment as aE, type PageSectionKind as aF, type PageSectionOverride as aG, type PageTokens as aH, type PathLayer as aI, type PatternBackgroundConfig as aJ, type PersistentLayer as aK, type PersistentLayerConfig as aL, type PersistentLayerTemplate as aM, type PersistentLayerTemplateConfig as aN, type PersistentLayerTemplateType as aO, type PhotoGridInput as aP, type PieChartInput as aQ, type PipStyle as aR, type Position as aS, type ProgressIndicatorConfig as aT, type PromotedBodyAnnotation as aU, type PullQuoteInput as aV, type QuoteBlockInput as aW, type RawLayersInput as aX, type RenderStyle as aY, type RightFeatureInput as aZ, type ScatterChartInput as a_, type MapBlockInput as aa, type MapLayer as ab, type MapMarker as ac, type MapTileStyle as ad, type MarkerStyle as ae, type MediaClip as af, type MediaScheduleOptions as ag, type MermaidLayer as ah, PAGE_ACCENT_STRATEGIES as ai, PAGE_BACKGROUNDS as aj, PAGE_BACKGROUND_RHYTHMS as ak, PAGE_DESIGN_FAMILIES as al, PAGE_DIVIDERS as am, PAGE_EMPHASES as an, PAGE_EYEBROWS as ao, PAGE_HEADING_CASES as ap, PAGE_HEADING_SCALES as aq, PAGE_HEADING_UNDERLINES as ar, PAGE_HERO_STYLES as as, PAGE_IMAGE_FRAMINGS as at, PAGE_NUMERAL_STYLES as au, PAGE_PATTERNS as av, PAGE_QUOTE_MARKS as aw, PAGE_SECTION_KINDS as ax, PAGE_SECTION_SPACINGS as ay, PAGE_SHADOWS as az, type AmbientGradientConfig as b, getSegmentAtTime as b$, type SectionHeaderInput as b0, type ShapeFilter as b1, type ShapeLayer as b2, type ShapePattern as b3, type SolidBackgroundConfig as b4, type StartBlockConfig as b5, type StatHighlightInput as b6, type SurfaceScheme as b7, THEME_SCHEMA_VERSION as b8, type TableLayer as b9, type TreeTemplateItem as bA, type TwoColumnInput as bB, VIEWPORT_PRESETS as bC, type VideoLayer as bD, type VideoPipPosition as bE, type VideoPipShape as bF, type VideoPipSize as bG, type VideoPlacement as bH, type VideoPresentation as bI, type VideoPullQuoteInput as bJ, type VideoWithCaptionInput as bK, type ViewportConfig as bL, type ViewportOrientation as bM, type ViewportPreset as bN, type VignetteConfig as bO, applySurface as bP, calculateDuration as bQ, calculateFontScale as bR, createTemplateContext as bS, createTheme as bT, createThemeRegistry as bU, getAspectRatioString as bV, getBlockAtTime as bW, getCaptionAtTime as bX, getDocPlaybackDuration as bY, getLayoutHints as bZ, getSafeTextBounds as b_, type TableLayerStyle as ba, type TemplateBlock as bb, type TemplateContext as bc, type TemplateFunction as bd, type TemplateRegistry as be, type TextLayer as bf, type TextStyle as bg, type Theme as bh, type ThemeColorPalette as bi, type ThemeColorScheme as bj, type ThemePageStyle as bk, type ThemeRegistry as bl, type ThemeSchemaVersion as bm, type ThemeSeedColors as bn, type ThemeStyle as bo, type ThemeTypography as bp, type TimelineBlockInput as bq, type TimelineTemplateEvent as br, type TimelineTemplateLink as bs, type TimelineTemplateTrack as bt, type TitleBlockInput as bu, type TitleCaptionConfig as bv, type TreeBlockInput as bw, type TreeLayer as bx, type TreeLayerItem as by, type TreeLayerStyle as bz, type Animation as c, getTwoColumnPositions as c0, getViewport as c1, getViewportOrientation as c2, isCustomTemplateDefinition as c3, isPersistentLayerTemplate as c4, isTemplateBlock as c5, scaledFontSize$1 as c6, resolveMediaSchedule as c7, scaledFontSize as c8, validateCustomTemplateDefinition as c9, type AnimationType as d, type AreaChartInput as e, type AudioBookmark as f, type AudioSegment as g, type AudioTimingData as h, type AudioTrack as i, type BigTextInput as j, type Block as k, type BlockConnection as l, type BorderStyle as m, type CaptionTrack as n, type CaptionWord as o, type ChartBaseFields as p, type ChartTemplateInput as q, type ColorScheme as r, type ColumnChartInput as s, type ComparisonBarInput as t, type ContentBlockInput as u, type CornerBrandingConfig as v, type CustomTemplateDefinition as w, type CustomTemplateLayer as x, type CustomTemplateValidationError as y, type CustomTemplateValidationResult as z };
@@ -1,4 +1,4 @@
1
- import { _ as ImageLayer, be as TextLayer, b1 as ShapeLayer, aH as PathLayer } from './Doc-BKKcPjfe.js';
1
+ import { $ as ImageLayer, bf as TextLayer, b2 as ShapeLayer, aI as PathLayer } from './Doc-DBadkoP4.js';
2
2
 
3
3
  /**
4
4
  * ImageEditDoc — JSON document describing a layered, editable image.
@@ -6,7 +6,7 @@ import {
6
6
  quoteAttrValue,
7
7
  splitKeyValueToken,
8
8
  tokenizeAttrTokens
9
- } from "./chunk-O3LLD7HG.js";
9
+ } from "./chunk-NBKAXPSX.js";
10
10
  import {
11
11
  resolveIcon
12
12
  } from "./chunk-7N4G32LG.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  expectedSyllablesAt,
3
3
  wordPosAtExpectedSyllables
4
- } from "./chunk-OIIIHI3Y.js";
4
+ } from "./chunk-GSJEGMKF.js";
5
5
 
6
6
  // src/narration/types.ts
7
7
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -56,7 +56,7 @@ import {
56
56
  sanitizeUrl,
57
57
  splitKeyValueToken,
58
58
  tokenizeAttrTokens
59
- } from "./chunk-O3LLD7HG.js";
59
+ } from "./chunk-NBKAXPSX.js";
60
60
  import {
61
61
  extractPlainText,
62
62
  readFrontmatterThemeId,
@@ -934,6 +934,138 @@ function sectionHeader(input, context) {
934
934
  return layers;
935
935
  }
936
936
 
937
+ // src/doc/templates/bigText.ts
938
+ var BIG_TEXT_LINE_HEIGHT = 1.05;
939
+ var BIG_TEXT_WIDTH_FRACTION = 0.92;
940
+ var BIG_TEXT_HEIGHT_FRACTION = 0.86;
941
+ var BIG_TEXT_CHAR_WIDTH = 0.62;
942
+ var BIG_TEXT_MIN_PX = 48;
943
+ function wrappedLineCount(wordLengths, charsPerLine) {
944
+ let lines = 1;
945
+ let current = 0;
946
+ for (const length of wordLengths) {
947
+ const withWord = current === 0 ? length : current + 1 + length;
948
+ if (withWord <= charsPerLine) {
949
+ current = withWord;
950
+ } else {
951
+ lines += 1;
952
+ current = length;
953
+ }
954
+ }
955
+ return lines;
956
+ }
957
+ function fitBigTextSize(title, viewport) {
958
+ const availWidth = viewport.width * BIG_TEXT_WIDTH_FRACTION;
959
+ const availHeight = viewport.height * BIG_TEXT_HEIGHT_FRACTION;
960
+ const wordLengths = title.trim().split(/\s+/).filter(Boolean).map((word) => word.length);
961
+ if (wordLengths.length === 0) return Math.floor(availHeight / BIG_TEXT_LINE_HEIGHT);
962
+ const longestWord = Math.max(...wordLengths);
963
+ const widthCap = availWidth / (BIG_TEXT_CHAR_WIDTH * longestWord);
964
+ const singleLineCap = availHeight / BIG_TEXT_LINE_HEIGHT;
965
+ const fits = (size) => {
966
+ const charsPerLine = Math.max(1, Math.floor(availWidth / (size * BIG_TEXT_CHAR_WIDTH)));
967
+ const lines = wrappedLineCount(wordLengths, charsPerLine);
968
+ return lines * size * BIG_TEXT_LINE_HEIGHT <= availHeight;
969
+ };
970
+ let low = BIG_TEXT_MIN_PX;
971
+ let high = Math.min(widthCap, singleLineCap);
972
+ if (high <= low) return Math.max(BIG_TEXT_MIN_PX, Math.floor(high));
973
+ if (!fits(low)) return BIG_TEXT_MIN_PX;
974
+ for (let i = 0; i < 24; i += 1) {
975
+ const mid = (low + high) / 2;
976
+ if (fits(mid)) low = mid;
977
+ else high = mid;
978
+ }
979
+ return Math.floor(low);
980
+ }
981
+ function bigTextBloom(background) {
982
+ return `radial-gradient(ellipse at 50% 50%, ${withAlpha(background, 0.92)} 0%, ${withAlpha(background, 0.78)} 38%, ${withAlpha(background, 0.32)} 66%, ${withAlpha(background, 0)} 88%)`;
983
+ }
984
+ function bigText(input, context) {
985
+ const { title = "", imageSrc, imageAlt, ambientMotion } = input;
986
+ const { theme, viewport } = context;
987
+ const treatment = themedImageTreatment(context, input.imageTreatment);
988
+ const displayTitle = title.toUpperCase();
989
+ const titleFontSize = fitBigTextSize(displayTitle, viewport);
990
+ const layers = [];
991
+ if (imageSrc) {
992
+ layers.push(
993
+ {
994
+ type: "image",
995
+ id: "bigtext-image",
996
+ content: {
997
+ src: imageSrc,
998
+ alt: imageAlt || title,
999
+ fit: "cover",
1000
+ ...treatment ? { treatment } : {}
1001
+ },
1002
+ position: { x: 0, y: 0, width: "100%", height: "100%" },
1003
+ animation: mapAmbientMotion(ambientMotion, 8) ?? {
1004
+ type: "slowZoom",
1005
+ duration: 8,
1006
+ direction: "in"
1007
+ }
1008
+ },
1009
+ {
1010
+ type: "shape",
1011
+ id: "bigtext-bloom",
1012
+ content: {
1013
+ shape: "rect",
1014
+ fill: bigTextBloom(theme.colors.background)
1015
+ },
1016
+ position: { x: 0, y: 0, width: "100%", height: "100%" }
1017
+ }
1018
+ );
1019
+ } else {
1020
+ layers.push(
1021
+ {
1022
+ type: "shape",
1023
+ id: "bigtext-bg",
1024
+ content: {
1025
+ shape: "rect",
1026
+ fill: theme.colors.background
1027
+ },
1028
+ position: { x: 0, y: 0, width: "100%", height: "100%" }
1029
+ },
1030
+ {
1031
+ type: "shape",
1032
+ id: "bigtext-tint",
1033
+ content: {
1034
+ shape: "rect",
1035
+ fill: `radial-gradient(ellipse at 50% 50%, ${withAlpha(theme.colors.primary, 0.38)} 0%, ${withAlpha(theme.colors.primary, 0.14)} 48%, ${withAlpha(theme.colors.primary, 0)} 78%)`
1036
+ },
1037
+ position: { x: 0, y: 0, width: "100%", height: "100%" }
1038
+ }
1039
+ );
1040
+ }
1041
+ layers.push({
1042
+ type: "text",
1043
+ id: "bigtext-title",
1044
+ content: {
1045
+ text: displayTitle,
1046
+ style: {
1047
+ fontSize: titleFontSize,
1048
+ fontFamily: getThemeFont(context, "title"),
1049
+ fontWeight: "bold",
1050
+ color: theme.colors.text,
1051
+ textAlign: "center",
1052
+ lineHeight: BIG_TEXT_LINE_HEIGHT,
1053
+ shadow: !!imageSrc,
1054
+ shrinkToFit: true
1055
+ }
1056
+ },
1057
+ position: {
1058
+ x: "50%",
1059
+ y: "50%",
1060
+ anchor: "center",
1061
+ width: `${BIG_TEXT_WIDTH_FRACTION * 100}%`,
1062
+ height: `${BIG_TEXT_HEIGHT_FRACTION * 100}%`
1063
+ },
1064
+ animation: themedEntrance(context, "text", { type: "fadeIn", duration: 1 })
1065
+ });
1066
+ return layers;
1067
+ }
1068
+
937
1069
  // src/doc/templates/contentBlock.ts
938
1070
  function completeBodyText(context) {
939
1071
  return (context.block?.contents ?? []).filter((node) => !(node.type === "code" && node.lang?.trim().toLowerCase() === "mermaid")).map((node) => extractPlainText(node)).join("\n").trim();
@@ -2763,6 +2895,13 @@ function deriveTemplateInputs(templateName, headingText, contents, options = {})
2763
2895
  if (!img) return placeholders ? { caption: headingText } : null;
2764
2896
  return { imageSrc: img.src, imageAlt: img.alt || headingText, caption: headingText };
2765
2897
  }
2898
+ case "bigText": {
2899
+ const img = extractFirstImage(contents);
2900
+ return {
2901
+ title: headingText,
2902
+ ...img ? { imageSrc: img.src, imageAlt: img.alt || headingText } : {}
2903
+ };
2904
+ }
2766
2905
  case "photoGrid": {
2767
2906
  const images = extractImages(contents, 4);
2768
2907
  if (images.length < 2) return placeholders ? {} : null;
@@ -3773,9 +3912,9 @@ function fitDiagramLabel(text, boxWidth, boxHeight, preferredFontSize) {
3773
3912
  };
3774
3913
  }
3775
3914
  function countWrappedLines(text, fontSize, maxWidth) {
3776
- return text.split("\n").reduce((count, line) => count + wrappedLineCount(line, fontSize, maxWidth), 0);
3915
+ return text.split("\n").reduce((count, line) => count + wrappedLineCount2(line, fontSize, maxWidth), 0);
3777
3916
  }
3778
- function wrappedLineCount(text, fontSize, maxWidth) {
3917
+ function wrappedLineCount2(text, fontSize, maxWidth) {
3779
3918
  if (!text.trim()) return 1;
3780
3919
  const charsPerLine = Math.floor(maxWidth / (fontSize * 0.5));
3781
3920
  if (charsPerLine <= 0) return 1;
@@ -6621,6 +6760,7 @@ function scatterChart(input, context) {
6621
6760
  var templateRegistry = {
6622
6761
  title: titleBlock,
6623
6762
  sectionHeader,
6763
+ bigText,
6624
6764
  content: contentBlock,
6625
6765
  statHighlight,
6626
6766
  quote: quoteBlock,
@@ -6835,6 +6975,12 @@ var TEMPLATE_INPUT_DESCRIPTORS = {
6835
6975
  { key: "imageAlt", description: "Alt text for the background image" },
6836
6976
  { key: "ambientMotion", description: "Slow zoom/pan effect", values: AMBIENT_MOTION_VALUES }
6837
6977
  ],
6978
+ bigText: [
6979
+ { key: "title", description: "The display text (rendered uppercase)" },
6980
+ { key: "imageSrc", description: "Optional full-bleed background image path" },
6981
+ { key: "imageAlt", description: "Alt text for the background image" },
6982
+ { key: "ambientMotion", description: "Slow zoom/pan effect", values: AMBIENT_MOTION_VALUES }
6983
+ ],
6838
6984
  content: [{ key: "title", description: "Heading shown above the complete body" }],
6839
6985
  statHighlight: [
6840
6986
  { key: "stat", description: 'The statistic (e.g. "89%", "2x")' },
@@ -7251,6 +7397,15 @@ var BLOCK_MEDIA_LAYOUT_POLICIES = {
7251
7397
  unconsumedMedia: "reserve-when-no-native-media",
7252
7398
  variants: TITLE_VARIANTS
7253
7399
  },
7400
+ bigText: {
7401
+ summary: "Uses its authored background image (behind a contrast bloom) when present; otherwise renders gigantic text on the theme surface.",
7402
+ noMedia: "template-without-optional-media",
7403
+ ownership: "optional-native",
7404
+ nativeLayout: "background-image",
7405
+ additionalMediaLayout: "overlay-inset",
7406
+ unconsumedMedia: "reserve-when-no-native-media",
7407
+ variants: TITLE_VARIANTS
7408
+ },
7254
7409
  content: {
7255
7410
  summary: "Preserves the complete heading and body; supplemental media receives a companion field.",
7256
7411
  noMedia: "template-default",
@@ -8392,6 +8547,10 @@ var TEMPLATE_METADATA = {
8392
8547
  label: "Section Header",
8393
8548
  description: "A clean section break with a prominent title and optional background image."
8394
8549
  },
8550
+ bigText: {
8551
+ label: "Big Text",
8552
+ description: "Gigantic uppercase display text \u2014 thumbnail-style \u2014 on a theme surface or over an image with a contrast bloom."
8553
+ },
8395
8554
  content: {
8396
8555
  label: "Content",
8397
8556
  description: "Shows a heading and the complete body in a loss-averse content-first layout."
@@ -8532,6 +8691,12 @@ var TEMPLATE_AUTHORING_METADATA = {
8532
8691
  safeForContentFirst: false,
8533
8692
  placement: "heading"
8534
8693
  },
8694
+ bigText: {
8695
+ role: "opener",
8696
+ bodyPolicy: "ignored",
8697
+ safeForContentFirst: false,
8698
+ placement: "heading"
8699
+ },
8535
8700
  content: {
8536
8701
  role: "content",
8537
8702
  bodyPolicy: "complete",
@@ -8883,8 +9048,81 @@ function startBlockToCoverInput(config) {
8883
9048
  heroLicense: config.heroLicense
8884
9049
  };
8885
9050
  }
8886
- function expandCoverBlock(config, context) {
9051
+ function expandCoverBlock(config, context, template = "cover") {
8887
9052
  const input = startBlockToCoverInput(config);
9053
+ const base = {
9054
+ id: "cover-block",
9055
+ duration: 0,
9056
+ audioSegment: -1
9057
+ };
9058
+ if (template === "title") {
9059
+ return titleBlock(
9060
+ {
9061
+ ...base,
9062
+ template: "title",
9063
+ title: input.title,
9064
+ subtitle: input.subtitle
9065
+ },
9066
+ context
9067
+ );
9068
+ }
9069
+ if (template === "sectionHeader") {
9070
+ return sectionHeader(
9071
+ {
9072
+ ...base,
9073
+ template: "sectionHeader",
9074
+ title: input.title,
9075
+ imageSrc: input.heroSrc,
9076
+ imageAlt: input.heroAlt,
9077
+ ambientMotion: input.ambientMotion
9078
+ },
9079
+ context
9080
+ );
9081
+ }
9082
+ if (template === "bigText" || template === "bigTextImage") {
9083
+ const useHero = template === "bigTextImage" && !!input.heroSrc;
9084
+ return bigText(
9085
+ {
9086
+ ...base,
9087
+ template: "bigText",
9088
+ title: input.title,
9089
+ ...useHero ? {
9090
+ imageSrc: input.heroSrc,
9091
+ imageAlt: input.heroAlt ?? input.title,
9092
+ ambientMotion: input.ambientMotion
9093
+ } : {}
9094
+ },
9095
+ context
9096
+ );
9097
+ }
9098
+ if (template === "imageWithCaption") {
9099
+ if (!input.heroSrc) {
9100
+ return titleBlock(
9101
+ {
9102
+ ...base,
9103
+ template: "title",
9104
+ title: input.title,
9105
+ subtitle: input.subtitle
9106
+ },
9107
+ context
9108
+ );
9109
+ }
9110
+ return imageWithCaption(
9111
+ {
9112
+ ...base,
9113
+ template: "imageWithCaption",
9114
+ imageSrc: input.heroSrc,
9115
+ imageAlt: input.heroAlt ?? input.title,
9116
+ caption: input.title,
9117
+ subtitle: input.subtitle,
9118
+ ambientMotion: input.ambientMotion,
9119
+ imageCredit: input.heroCredit,
9120
+ imageLicense: input.heroLicense,
9121
+ isTitle: true
9122
+ },
9123
+ context
9124
+ );
9125
+ }
8888
9126
  return coverBlock(input, context);
8889
9127
  }
8890
9128
 
@@ -10141,7 +10379,7 @@ function markdownToDoc(markdownDoc, options) {
10141
10379
  ...documentMedia.length > 0 ? { documentMedia } : {}
10142
10380
  };
10143
10381
  if (options?.generateCoverBlock ?? true) {
10144
- const coverConfig = buildStartBlock(markdownDoc, rootBlocks);
10382
+ const coverConfig = buildStartBlock(markdownDoc, rootBlocks, options?.fileName);
10145
10383
  if (coverConfig) {
10146
10384
  doc.startBlock = coverConfig;
10147
10385
  }
@@ -10391,12 +10629,35 @@ function findFirstImage(node) {
10391
10629
  }
10392
10630
  return void 0;
10393
10631
  }
10394
- function buildStartBlock(markdownDoc, rootBlocks) {
10395
- const firstH1 = rootBlocks.find((b) => b.sourceHeading?.depth === 1);
10396
- if (!firstH1) return void 0;
10397
- const title = firstH1.title ?? extractPlainText(firstH1.sourceHeading);
10632
+ function shallowestHeadingBlock(rootBlocks) {
10633
+ let best;
10634
+ for (const block of rootBlocks) {
10635
+ const depth = block.sourceHeading?.depth;
10636
+ if (depth === void 0) continue;
10637
+ const title = block.title ?? extractPlainText(block.sourceHeading);
10638
+ if (!title) continue;
10639
+ if (!best || depth < best.sourceHeading.depth) {
10640
+ best = block;
10641
+ if (depth === 1) break;
10642
+ }
10643
+ }
10644
+ return best;
10645
+ }
10646
+ function fileNameCoverTitle(fileName) {
10647
+ if (!fileName) return void 0;
10648
+ const base = fileName.split(/[\\/]/).pop() ?? "";
10649
+ const stem = base.replace(/\.[^.]+$/, "").replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim();
10650
+ if (!stem) return void 0;
10651
+ return stem.charAt(0).toUpperCase() + stem.slice(1);
10652
+ }
10653
+ function buildStartBlock(markdownDoc, rootBlocks, fileName) {
10654
+ const frontmatterTitle = typeof markdownDoc.frontmatter?.title === "string" ? markdownDoc.frontmatter.title.trim() : void 0;
10655
+ const headingBlock = shallowestHeadingBlock(rootBlocks);
10656
+ const headingTitle = headingBlock ? headingBlock.title ?? extractPlainText(headingBlock.sourceHeading) : void 0;
10657
+ const title = frontmatterTitle || headingTitle || fileNameCoverTitle(fileName);
10398
10658
  if (!title) return void 0;
10399
- const subtitle = firstH1.contents?.[0]?.type === "paragraph" ? extractPlainText(firstH1.contents[0]) : void 0;
10659
+ const subtitleBlock = headingBlock ?? rootBlocks[0];
10660
+ const subtitle = subtitleBlock?.contents?.[0]?.type === "paragraph" ? extractPlainText(subtitleBlock.contents[0]) : void 0;
10400
10661
  const firstImage = findFirstImage(markdownDoc);
10401
10662
  const config = {
10402
10663
  title,
@@ -10744,6 +11005,8 @@ export {
10744
11005
  createAccentLayers,
10745
11006
  adjustY,
10746
11007
  sectionHeader,
11008
+ fitBigTextSize,
11009
+ bigText,
10747
11010
  contentBlock,
10748
11011
  statHighlight,
10749
11012
  quoteBlock,
@@ -2,7 +2,7 @@ import {
2
2
  assertMarkdownDocumentWithinLimits,
3
3
  parseMarkdown,
4
4
  toMdast
5
- } from "./chunk-YQW6AQBV.js";
5
+ } from "./chunk-6MVWWZHL.js";
6
6
  import {
7
7
  formatFrontmatterYaml,
8
8
  getChildren,