@bendyline/squisq 2.3.0 → 2.3.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.
@@ -14,6 +14,7 @@ import {
14
14
  isEligibleAsciiTimelineFenceLang,
15
15
  isEligibleTreeFenceLang,
16
16
  isExplicitDiagramLang,
17
+ isExplicitTimelineLang,
17
18
  isExplicitTreeLang,
18
19
  matchNumberHighlight,
19
20
  parseAsciiDiagram,
@@ -943,6 +944,65 @@ function sectionHeader(input, context) {
943
944
  return layers;
944
945
  }
945
946
 
947
+ // src/doc/templates/contentBlock.ts
948
+ function completeBodyText(context) {
949
+ return (context.block?.contents ?? []).map((node) => extractPlainText(node)).join("\n").trim();
950
+ }
951
+ function bodyFontSize(body, context) {
952
+ const base = body.length > 1800 ? 18 : body.length > 1100 ? 21 : body.length > 650 ? 24 : 28;
953
+ return themedFontSize(base, context, false);
954
+ }
955
+ function contentBlock(input, context) {
956
+ const { theme } = context;
957
+ const title = input.title ?? context.block?.title ?? "";
958
+ const body = completeBodyText(context);
959
+ const layers = [createBackgroundLayer("bg", theme.colors.background)];
960
+ if (title) {
961
+ layers.push({
962
+ type: "text",
963
+ id: "title",
964
+ content: {
965
+ text: title,
966
+ style: {
967
+ fontSize: themedFontSize(46, context, true),
968
+ fontFamily: getThemeFont(context, "title"),
969
+ fontWeight: "bold",
970
+ color: theme.colors.text,
971
+ textAlign: "left",
972
+ lineHeight: 1.15,
973
+ shadow: shouldUseShadow(context)
974
+ }
975
+ },
976
+ position: { x: "8%", y: "9%", width: "84%", height: "15%", anchor: "top-left" }
977
+ });
978
+ }
979
+ if (body) {
980
+ layers.push({
981
+ type: "text",
982
+ id: "body",
983
+ content: {
984
+ text: body,
985
+ style: {
986
+ fontSize: bodyFontSize(body, context),
987
+ fontFamily: getThemeFont(context, "body"),
988
+ color: theme.colors.text,
989
+ textAlign: "left",
990
+ lineHeight: 1.35,
991
+ shadow: shouldUseShadow(context)
992
+ }
993
+ },
994
+ position: {
995
+ x: "8%",
996
+ y: title ? "28%" : "10%",
997
+ width: "84%",
998
+ height: title ? "64%" : "82%",
999
+ anchor: "top-left"
1000
+ }
1001
+ });
1002
+ }
1003
+ return layers;
1004
+ }
1005
+
946
1006
  // src/doc/templates/statHighlight.ts
947
1007
  function statHighlight(input, context) {
948
1008
  const { stat, description, detail, colorScheme = "blue", accentImage } = input;
@@ -1703,7 +1763,7 @@ function buildFeatureLayers(input, context, side) {
1703
1763
  const sized = (imageWidth ?? 0) > 0 || (imageHeight ?? 0) > 0;
1704
1764
  const stack = layout.stackColumns;
1705
1765
  const titleFontSize = themedFontSize(48, context, true);
1706
- const bodyFontSize = themedFontSize(24, context, false);
1766
+ const bodyFontSize2 = themedFontSize(24, context, false);
1707
1767
  let imgX;
1708
1768
  let imgY;
1709
1769
  let imgW;
@@ -1788,7 +1848,7 @@ function buildFeatureLayers(input, context, side) {
1788
1848
  content: {
1789
1849
  text: body,
1790
1850
  style: {
1791
- fontSize: bodyFontSize,
1851
+ fontSize: bodyFontSize2,
1792
1852
  fontFamily: getThemeFont(context, "body"),
1793
1853
  color: theme.colors.textMuted,
1794
1854
  textAlign,
@@ -4811,6 +4871,7 @@ function makeCustomTemplateFn(def) {
4811
4871
  var templateRegistry = {
4812
4872
  title: titleBlock,
4813
4873
  sectionHeader,
4874
+ content: contentBlock,
4814
4875
  statHighlight,
4815
4876
  quote: quoteBlock,
4816
4877
  factCard,
@@ -4963,6 +5024,18 @@ var TEMPLATE_INPUT_DESCRIPTORS = {
4963
5024
  { key: "subtitle", description: "Subtitle or tagline" },
4964
5025
  { key: "backgroundColor", description: "Background color", valueHint: "CSS color" }
4965
5026
  ],
5027
+ sectionHeader: [
5028
+ { key: "title", description: "Section title" },
5029
+ {
5030
+ key: "colorScheme",
5031
+ description: "Fallback background color scheme",
5032
+ valueHint: COLOR_SCHEME_HINT
5033
+ },
5034
+ { key: "imageSrc", description: "Optional background image path" },
5035
+ { key: "imageAlt", description: "Alt text for the background image" },
5036
+ { key: "ambientMotion", description: "Slow zoom/pan effect", values: AMBIENT_MOTION_VALUES }
5037
+ ],
5038
+ content: [{ key: "title", description: "Heading shown above the complete body" }],
4966
5039
  statHighlight: [
4967
5040
  { key: "stat", description: 'The statistic (e.g. "89%", "2x")' },
4968
5041
  { key: "description", description: "What the stat means" },
@@ -5980,6 +6053,13 @@ var BLOCK_MEDIA_LAYOUT_POLICIES = {
5980
6053
  unconsumedMedia: "reserve-when-no-native-media",
5981
6054
  variants: TITLE_VARIANTS
5982
6055
  },
6056
+ content: {
6057
+ summary: "Preserves the complete heading and body; supplemental media receives a companion field.",
6058
+ noMedia: "template-default",
6059
+ ownership: "supplemental",
6060
+ unconsumedMedia: "reserved-slot",
6061
+ variants: TEXT_VARIANTS
6062
+ },
5983
6063
  statHighlight: {
5984
6064
  summary: "Uses its optional accent image natively; otherwise supplemental media occupies a companion field.",
5985
6065
  noMedia: "template-without-optional-media",
@@ -6320,6 +6400,95 @@ function mapPosition(position, contentRect, viewport) {
6320
6400
  }
6321
6401
  return mapped;
6322
6402
  }
6403
+ function placeLayersInRect(layers, sourceViewport, target, idPrefix) {
6404
+ const scaleX = target.width / sourceViewport.width;
6405
+ const scaleY = target.height / sourceViewport.height;
6406
+ const fontScale = Math.min(scaleX, scaleY);
6407
+ return layers.map((layer) => {
6408
+ const position = {
6409
+ ...layer.position,
6410
+ x: target.x + resolvePositionValue(layer.position.x, sourceViewport.width) * scaleX,
6411
+ y: target.y + resolvePositionValue(layer.position.y, sourceViewport.height) * scaleY,
6412
+ ...layer.position.width !== void 0 ? {
6413
+ width: resolvePositionValue(layer.position.width, sourceViewport.width) * scaleX
6414
+ } : {},
6415
+ ...layer.position.height !== void 0 ? {
6416
+ height: resolvePositionValue(layer.position.height, sourceViewport.height) * scaleY
6417
+ } : {}
6418
+ };
6419
+ const base = { ...layer, id: `${idPrefix}-${layer.id}`, position };
6420
+ if (layer.type === "path") {
6421
+ return {
6422
+ ...base,
6423
+ type: "path",
6424
+ content: {
6425
+ ...layer.content,
6426
+ d: transformAbsoluteSvgPath(layer.content.d, scaleX, scaleY, target.x, target.y),
6427
+ ...layer.content.strokeWidth !== void 0 ? { strokeWidth: layer.content.strokeWidth * fontScale } : {}
6428
+ }
6429
+ };
6430
+ }
6431
+ if (layer.type === "text" && fontScale !== 1) {
6432
+ return {
6433
+ ...base,
6434
+ type: "text",
6435
+ content: {
6436
+ ...layer.content,
6437
+ style: {
6438
+ ...layer.content.style,
6439
+ fontSize: Math.max(10, layer.content.style.fontSize * fontScale)
6440
+ }
6441
+ }
6442
+ };
6443
+ }
6444
+ return base;
6445
+ });
6446
+ }
6447
+ function transformAbsoluteSvgPath(d, scaleX, scaleY, translateX, translateY) {
6448
+ const tokens = d.match(/[a-zA-Z]|[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi);
6449
+ if (!tokens) return d;
6450
+ let command = "";
6451
+ let parameterIndex = 0;
6452
+ return tokens.map((token) => {
6453
+ if (/^[a-zA-Z]$/.test(token)) {
6454
+ command = token;
6455
+ parameterIndex = 0;
6456
+ return token;
6457
+ }
6458
+ const value = Number(token);
6459
+ if (!Number.isFinite(value) || command === command.toLowerCase()) {
6460
+ parameterIndex++;
6461
+ return token;
6462
+ }
6463
+ const index = parameterIndex++;
6464
+ let transformed = value;
6465
+ switch (command) {
6466
+ case "M":
6467
+ case "L":
6468
+ case "T":
6469
+ case "C":
6470
+ case "S":
6471
+ case "Q":
6472
+ transformed = index % 2 === 0 ? value * scaleX + translateX : value * scaleY + translateY;
6473
+ break;
6474
+ case "H":
6475
+ transformed = value * scaleX + translateX;
6476
+ break;
6477
+ case "V":
6478
+ transformed = value * scaleY + translateY;
6479
+ break;
6480
+ case "A": {
6481
+ const arcIndex = index % 7;
6482
+ if (arcIndex === 0) transformed = value * scaleX;
6483
+ else if (arcIndex === 1) transformed = value * scaleY;
6484
+ else if (arcIndex === 5) transformed = value * scaleX + translateX;
6485
+ else if (arcIndex === 6) transformed = value * scaleY + translateY;
6486
+ break;
6487
+ }
6488
+ }
6489
+ return String(Math.round(transformed * 1e3) / 1e3);
6490
+ }).join(" ");
6491
+ }
6323
6492
  function mapForegroundLayers(layers, contentRect, viewport, fontScale, template) {
6324
6493
  const mapped = layers.map((layer) => {
6325
6494
  if (isFullViewportBackground(layer, viewport)) return layer;
@@ -6631,6 +6800,55 @@ function mermaidSources(block) {
6631
6800
  (node) => node.type === "code" && node.lang?.trim().toLowerCase() === "mermaid"
6632
6801
  ).map((node) => node.value).filter((source) => source.trim().length > 0);
6633
6802
  }
6803
+ function embeddedSpatialMedia(block) {
6804
+ const canonicalTemplate = block.template ? resolveTemplateName(block.template) : void 0;
6805
+ const fences = (block.contents ?? []).filter(
6806
+ (node) => node.type === "code"
6807
+ );
6808
+ const items = [];
6809
+ for (const fence of fences) {
6810
+ if (canonicalTemplate !== "diagram" && isEligibleAsciiFenceLang(fence.lang)) {
6811
+ const detection = detectAsciiDiagram(fence.value, {
6812
+ explicit: isExplicitDiagramLang(fence.lang)
6813
+ });
6814
+ if (detection.isDiagram && detection.diagram) {
6815
+ items.push({
6816
+ kind: "spatial",
6817
+ template: "diagram",
6818
+ data: asciiDiagramToTemplateData(detection.diagram),
6819
+ aspectRatio: 16 / 9
6820
+ });
6821
+ continue;
6822
+ }
6823
+ }
6824
+ if (canonicalTemplate !== "tree" && isEligibleTreeFenceLang(fence.lang)) {
6825
+ const detection = detectTree(fence.value, { explicit: isExplicitTreeLang(fence.lang) });
6826
+ if (detection.isTree && detection.tree) {
6827
+ items.push({
6828
+ kind: "spatial",
6829
+ template: "tree",
6830
+ data: treeToTemplateData(detection.tree),
6831
+ aspectRatio: 3 / 4
6832
+ });
6833
+ continue;
6834
+ }
6835
+ }
6836
+ if (canonicalTemplate !== "timeline" && isEligibleAsciiTimelineFenceLang(fence.lang)) {
6837
+ const detection = detectAsciiTimeline(fence.value, {
6838
+ explicit: isExplicitTimelineLang(fence.lang)
6839
+ });
6840
+ if (detection.isTimeline && detection.timeline) {
6841
+ items.push({
6842
+ kind: "spatial",
6843
+ template: "timeline",
6844
+ data: asciiTimelineToTemplateData(detection.timeline),
6845
+ aspectRatio: 16 / 9
6846
+ });
6847
+ }
6848
+ }
6849
+ }
6850
+ return items;
6851
+ }
6634
6852
  function collectRichMediaItems(layers, block) {
6635
6853
  const existingImages = new Set(
6636
6854
  layers.filter((layer) => layer.type === "image").map((layer) => layer.content.src)
@@ -6669,7 +6887,8 @@ function collectRichMediaItems(layers, block) {
6669
6887
  aspectRatio: 16 / 9
6670
6888
  })
6671
6889
  ),
6672
- ...sources.map((source) => ({ kind: "mermaid", source, aspectRatio: 16 / 9 }))
6890
+ ...sources.map((source) => ({ kind: "mermaid", source, aspectRatio: 16 / 9 })),
6891
+ ...embeddedSpatialMedia(block)
6673
6892
  ];
6674
6893
  }
6675
6894
  function gridCells(region, count, viewport, inset) {
@@ -6703,47 +6922,75 @@ function effectiveImageTreatment(theme, block) {
6703
6922
  if (!base || base.type === "none") return void 0;
6704
6923
  return base.type === "duotone" && !base.color ? { ...base, color: theme.colors.primary } : base;
6705
6924
  }
6706
- function richMediaLayer(item, position, block, theme, kindIndex) {
6925
+ function richMediaLayers(item, position, block, theme, kindIndex) {
6707
6926
  if (item.kind === "image") {
6708
6927
  const treatment = effectiveImageTreatment(theme, block);
6709
- return {
6710
- id: `${block.id}-embedded-image-${kindIndex}`,
6711
- type: "image",
6712
- position,
6713
- content: {
6714
- src: item.src,
6715
- alt: item.alt || block.title || "Embedded image",
6716
- fit: "contain",
6717
- ...treatment ? { treatment } : {}
6928
+ return [
6929
+ {
6930
+ id: `${block.id}-embedded-image-${kindIndex}`,
6931
+ type: "image",
6932
+ position,
6933
+ content: {
6934
+ src: item.src,
6935
+ alt: item.alt || block.title || "Embedded image",
6936
+ fit: "contain",
6937
+ ...treatment ? { treatment } : {}
6938
+ }
6718
6939
  }
6719
- };
6940
+ ];
6720
6941
  }
6721
6942
  if (item.kind === "video") {
6722
- return {
6723
- id: `${block.id}-embedded-video-${kindIndex}`,
6724
- type: "video",
6725
- position,
6726
- content: {
6727
- src: item.src,
6728
- ...item.posterSrc ? { posterSrc: item.posterSrc } : {},
6729
- alt: item.alt || block.title || "Embedded video",
6730
- fit: "contain",
6731
- clipStart: 0,
6732
- clipEnd: Math.max(0, block.duration)
6943
+ return [
6944
+ {
6945
+ id: `${block.id}-embedded-video-${kindIndex}`,
6946
+ type: "video",
6947
+ position,
6948
+ content: {
6949
+ src: item.src,
6950
+ ...item.posterSrc ? { posterSrc: item.posterSrc } : {},
6951
+ alt: item.alt || block.title || "Embedded video",
6952
+ fit: "contain",
6953
+ clipStart: 0,
6954
+ clipEnd: Math.max(0, block.duration)
6955
+ }
6733
6956
  }
6734
- };
6957
+ ];
6735
6958
  }
6736
- return {
6737
- id: `${block.id}-mermaid-${kindIndex}`,
6738
- type: "mermaid",
6739
- position,
6740
- content: {
6741
- source: item.source,
6742
- background: theme.colors.backgroundLight,
6743
- foreground: theme.colors.text,
6744
- padding: Math.max(12, Math.round(Math.min(position.width, position.height) * 0.025))
6745
- }
6959
+ if (item.kind === "mermaid") {
6960
+ return [
6961
+ {
6962
+ id: `${block.id}-mermaid-${kindIndex}`,
6963
+ type: "mermaid",
6964
+ position,
6965
+ content: {
6966
+ source: item.source,
6967
+ background: theme.colors.backgroundLight,
6968
+ foreground: theme.colors.text,
6969
+ padding: Math.max(12, Math.round(Math.min(position.width, position.height) * 0.025))
6970
+ }
6971
+ }
6972
+ ];
6973
+ }
6974
+ const localViewport = {
6975
+ width: position.width,
6976
+ height: position.height,
6977
+ name: `Embedded ${item.template}`
6746
6978
  };
6979
+ const context = createTemplateContext(theme, 0, 1, localViewport);
6980
+ context.block = cloneData(block);
6981
+ const base = {
6982
+ id: `${block.id}-embedded-${item.template}-${kindIndex}`,
6983
+ duration: block.duration,
6984
+ audioSegment: block.audioSegment,
6985
+ ...item.data
6986
+ };
6987
+ const localLayers = item.template === "timeline" ? timelineBlock({ ...base, template: "timeline" }, context) : item.template === "tree" ? treeBlock({ ...base, template: "tree" }, context) : diagramBlock({ ...base, template: "diagram" }, context);
6988
+ return placeLayersInRect(
6989
+ localLayers,
6990
+ localViewport,
6991
+ position,
6992
+ `${block.id}-embedded-${item.template}-${kindIndex}`
6993
+ );
6747
6994
  }
6748
6995
  function appendRichContentLayers(layers, block, theme, viewport) {
6749
6996
  const items = collectRichMediaItems(layers, block);
@@ -6756,10 +7003,15 @@ function appendRichContentLayers(layers, block, theme, viewport) {
6756
7003
  items.map((item) => item.aspectRatio)
6757
7004
  );
6758
7005
  const cells = gridCells(layout.mediaRect, items.length, viewport, layout.framed);
6759
- const counters = { image: 0, video: 0, mermaid: 0 };
6760
- const mediaLayers = items.map((item, index) => {
7006
+ const counters = {
7007
+ image: 0,
7008
+ video: 0,
7009
+ mermaid: 0,
7010
+ spatial: 0
7011
+ };
7012
+ const mediaLayers = items.flatMap((item, index) => {
6761
7013
  counters[item.kind] += 1;
6762
- return richMediaLayer(item, cells[index], block, theme, counters[item.kind]);
7014
+ return richMediaLayers(item, cells[index], block, theme, counters[item.kind]);
6763
7015
  });
6764
7016
  const frame = layout.framed ? {
6765
7017
  id: `${block.id}-rich-media-frame`,
@@ -6861,7 +7113,11 @@ var TEMPLATE_METADATA = {
6861
7113
  },
6862
7114
  sectionHeader: {
6863
7115
  label: "Section Header",
6864
- description: "A clean section break with a prominent title and optional subtitle."
7116
+ description: "A clean section break with a prominent title and optional background image."
7117
+ },
7118
+ content: {
7119
+ label: "Content",
7120
+ description: "Shows a heading and the complete body in a loss-averse content-first layout."
6865
7121
  },
6866
7122
  statHighlight: {
6867
7123
  label: "Stat Highlight",
@@ -6957,6 +7213,161 @@ var TEMPLATE_METADATA = {
6957
7213
  }
6958
7214
  };
6959
7215
 
7216
+ // src/doc/templates/authoringMetadata.ts
7217
+ var TEMPLATE_AUTHORING_METADATA = {
7218
+ title: {
7219
+ role: "opener",
7220
+ bodyPolicy: "ignored",
7221
+ safeForContentFirst: false,
7222
+ placement: "heading"
7223
+ },
7224
+ sectionHeader: {
7225
+ role: "divider",
7226
+ bodyPolicy: "ignored",
7227
+ safeForContentFirst: false,
7228
+ placement: "heading"
7229
+ },
7230
+ content: {
7231
+ role: "content",
7232
+ bodyPolicy: "complete",
7233
+ safeForContentFirst: true,
7234
+ placement: "heading"
7235
+ },
7236
+ statHighlight: {
7237
+ role: "highlight",
7238
+ bodyPolicy: "derived",
7239
+ safeForContentFirst: false,
7240
+ placement: "heading"
7241
+ },
7242
+ quote: { role: "quote", bodyPolicy: "derived", safeForContentFirst: false, placement: "heading" },
7243
+ factCard: {
7244
+ role: "highlight",
7245
+ bodyPolicy: "derived",
7246
+ safeForContentFirst: false,
7247
+ placement: "heading"
7248
+ },
7249
+ twoColumn: {
7250
+ role: "comparison",
7251
+ bodyPolicy: "structured",
7252
+ safeForContentFirst: false,
7253
+ placement: "heading"
7254
+ },
7255
+ dateEvent: {
7256
+ role: "event",
7257
+ bodyPolicy: "derived",
7258
+ safeForContentFirst: false,
7259
+ placement: "heading"
7260
+ },
7261
+ imageWithCaption: {
7262
+ role: "media",
7263
+ bodyPolicy: "derived",
7264
+ safeForContentFirst: false,
7265
+ placement: "heading"
7266
+ },
7267
+ leftFeature: {
7268
+ role: "media",
7269
+ bodyPolicy: "derived",
7270
+ safeForContentFirst: false,
7271
+ placement: "heading"
7272
+ },
7273
+ rightFeature: {
7274
+ role: "media",
7275
+ bodyPolicy: "derived",
7276
+ safeForContentFirst: false,
7277
+ placement: "heading"
7278
+ },
7279
+ map: {
7280
+ role: "spatial",
7281
+ bodyPolicy: "structured",
7282
+ safeForContentFirst: false,
7283
+ placement: "heading"
7284
+ },
7285
+ fullBleedQuote: {
7286
+ role: "quote",
7287
+ bodyPolicy: "derived",
7288
+ safeForContentFirst: false,
7289
+ placement: "heading"
7290
+ },
7291
+ list: {
7292
+ role: "content",
7293
+ bodyPolicy: "derived",
7294
+ safeForContentFirst: false,
7295
+ placement: "heading"
7296
+ },
7297
+ photoGrid: {
7298
+ role: "media",
7299
+ bodyPolicy: "structured",
7300
+ safeForContentFirst: false,
7301
+ placement: "heading"
7302
+ },
7303
+ definitionCard: {
7304
+ role: "content",
7305
+ bodyPolicy: "derived",
7306
+ safeForContentFirst: false,
7307
+ placement: "heading"
7308
+ },
7309
+ comparisonBar: {
7310
+ role: "comparison",
7311
+ bodyPolicy: "structured",
7312
+ safeForContentFirst: false,
7313
+ placement: "heading"
7314
+ },
7315
+ pullQuote: {
7316
+ role: "quote",
7317
+ bodyPolicy: "derived",
7318
+ safeForContentFirst: false,
7319
+ placement: "heading"
7320
+ },
7321
+ videoWithCaption: {
7322
+ role: "media",
7323
+ bodyPolicy: "derived",
7324
+ safeForContentFirst: false,
7325
+ placement: "heading"
7326
+ },
7327
+ videoPullQuote: {
7328
+ role: "media",
7329
+ bodyPolicy: "derived",
7330
+ safeForContentFirst: false,
7331
+ placement: "heading"
7332
+ },
7333
+ dataTable: {
7334
+ role: "data",
7335
+ bodyPolicy: "structured",
7336
+ safeForContentFirst: false,
7337
+ placement: "heading"
7338
+ },
7339
+ diagram: {
7340
+ role: "spatial",
7341
+ bodyPolicy: "structured",
7342
+ safeForContentFirst: false,
7343
+ placement: "heading"
7344
+ },
7345
+ tree: {
7346
+ role: "spatial",
7347
+ bodyPolicy: "structured",
7348
+ safeForContentFirst: false,
7349
+ placement: "heading"
7350
+ },
7351
+ timeline: {
7352
+ role: "spatial",
7353
+ bodyPolicy: "structured",
7354
+ safeForContentFirst: false,
7355
+ placement: "heading"
7356
+ },
7357
+ drawing: {
7358
+ role: "spatial",
7359
+ bodyPolicy: "structured",
7360
+ safeForContentFirst: false,
7361
+ placement: "heading"
7362
+ },
7363
+ layout: {
7364
+ role: "spatial",
7365
+ bodyPolicy: "structured",
7366
+ safeForContentFirst: false,
7367
+ placement: "heading"
7368
+ }
7369
+ };
7370
+
6960
7371
  // src/doc/templates/coverBlock.ts
6961
7372
  var COVER_TITLE_BASE_PX = 120;
6962
7373
  var COVER_TITLE_COMFORTABLE_CHARS = 26;
@@ -8796,6 +9207,7 @@ export {
8796
9207
  createAccentLayers,
8797
9208
  adjustY,
8798
9209
  sectionHeader,
9210
+ contentBlock,
8799
9211
  statHighlight,
8800
9212
  quoteBlock,
8801
9213
  factCard,
@@ -8866,6 +9278,7 @@ export {
8866
9278
  getBlockMediaLayoutPolicy,
8867
9279
  materializeBlockLayers,
8868
9280
  TEMPLATE_METADATA,
9281
+ TEMPLATE_AUTHORING_METADATA,
8869
9282
  coverBlock,
8870
9283
  expandCoverBlock,
8871
9284
  expandDocBlocks,
@@ -24,7 +24,7 @@ import {
24
24
  templateRegistry,
25
25
  writeCustomTemplatesToFrontmatter,
26
26
  writeCustomThemesToFrontmatter
27
- } from "./chunk-GIIOY6XR.js";
27
+ } from "./chunk-CKZY6K5R.js";
28
28
  import {
29
29
  ASCII_TREE_VOCAB,
30
30
  ASCII_VOCAB,
@@ -633,6 +633,13 @@ var sectionHeader = (input) => {
633
633
  mediaBackground: !!s.imageSrc
634
634
  };
635
635
  };
636
+ var content = (input) => {
637
+ const c = input;
638
+ return {
639
+ kind: "prose",
640
+ slots: { title: c.title, body: bodyRichText(input) }
641
+ };
642
+ };
636
643
  var statHighlight = (input) => {
637
644
  const s = input;
638
645
  return {
@@ -899,6 +906,7 @@ var layout = (input, ctx) => canvasDraft("layout", ctx, input);
899
906
  var sectionExtractors = {
900
907
  title,
901
908
  sectionHeader,
909
+ content,
902
910
  statHighlight,
903
911
  quote,
904
912
  factCard,
@@ -1802,9 +1810,10 @@ var PAGE_BASE_CSS = `
1802
1810
  .squisq-page-prose p + p { margin-top: 1.25em; }
1803
1811
  .squisq-page-prose > * + * { margin-top: 1.1em; }
1804
1812
  .squisq-page-prose a { color: var(--squisq-page-primary); text-decoration-color: color-mix(in srgb, var(--squisq-page-primary) 40%, transparent); }
1805
- .squisq-page-prose code { font-family: var(--squisq-page-mono-font); font-size: 0.9em; background: color-mix(in srgb, var(--squisq-page-text) 8%, transparent); border-radius: 4px; padding: 0.12em 0.35em; }
1813
+ .squisq-page-prose code { font-family: var(--squisq-page-mono-font); font-size: 0.9em; }
1814
+ .squisq-page-prose :not(pre) > code { background: color-mix(in srgb, var(--squisq-page-text) 8%, transparent); border-radius: 4px; padding: 0.12em 0.35em; }
1806
1815
  .squisq-page-prose pre { background: color-mix(in srgb, var(--squisq-page-text) 6%, transparent); border-radius: var(--squisq-page-radius); padding: 1em 1.25em; overflow-x: auto; }
1807
- .squisq-page-prose pre code { background: none; padding: 0; }
1816
+ .squisq-page-prose pre code { background: none; border-radius: 0; padding: 0; }
1808
1817
  .squisq-page-prose blockquote { margin: 0; border-left: 4px solid var(--squisq-page-accent); padding-left: 1.25em; color: var(--squisq-page-text-muted); }
1809
1818
  .squisq-page-prose ul, .squisq-page-prose ol { padding-left: 1.5em; margin: 0; }
1810
1819
  .squisq-page-prose li + li { margin-top: 0.4em; }
@@ -2499,33 +2508,45 @@ function checkAssets(markdownDoc, blocks, hasAsset, diagnostics) {
2499
2508
  ...line != null ? { line } : {}
2500
2509
  });
2501
2510
  };
2511
+ const markdownUrls = /* @__PURE__ */ new Set();
2502
2512
  const walk = (node) => {
2503
2513
  if (node.type === "image") {
2514
+ markdownUrls.add(node.url);
2504
2515
  report(node.url, node.position?.start.line);
2505
2516
  }
2506
2517
  if (node.type === "htmlBlock" || node.type === "htmlInline") {
2507
- walkHtmlImages(node.htmlChildren, (src) => report(src, node.position?.start.line));
2518
+ walkHtmlImages(node.htmlChildren, (src) => {
2519
+ markdownUrls.add(src);
2520
+ report(src, node.position?.start.line);
2521
+ });
2508
2522
  }
2509
2523
  for (const child of getChildren(node)) walk(child);
2510
2524
  };
2511
2525
  for (const child of markdownDoc.children) walk(child);
2512
2526
  for (const block of blocks) {
2527
+ const authoredUrls = /* @__PURE__ */ new Set();
2513
2528
  const sources = [
2514
- block.templateOverrides,
2515
- block.templateData
2529
+ { value: block.templateOverrides, derived: false },
2530
+ { value: block.templateData, derived: true }
2516
2531
  ];
2517
- for (const source of sources) {
2532
+ for (const { value: source, derived } of sources) {
2518
2533
  if (!source) continue;
2519
2534
  for (const key of ASSET_PARAM_KEYS) {
2520
2535
  const value = source[key];
2521
2536
  if (typeof value === "string") {
2522
- report(value, block.sourceHeading?.position?.start.line, block.id);
2537
+ if (!derived) authoredUrls.add(value);
2538
+ if (!derived || !markdownUrls.has(value) && !authoredUrls.has(value)) {
2539
+ report(value, block.sourceHeading?.position?.start.line, block.id);
2540
+ }
2523
2541
  }
2524
2542
  }
2525
2543
  const images = source["images"];
2526
2544
  const list2 = typeof images === "string" ? images.split(",").map((s) => s.trim()) : Array.isArray(images) ? images.filter((i) => typeof i === "string") : [];
2527
2545
  for (const img of list2) {
2528
- report(img, block.sourceHeading?.position?.start.line, block.id);
2546
+ if (!derived) authoredUrls.add(img);
2547
+ if (!derived || !markdownUrls.has(img) && !authoredUrls.has(img)) {
2548
+ report(img, block.sourceHeading?.position?.start.line, block.id);
2549
+ }
2529
2550
  }
2530
2551
  }
2531
2552
  }