@bendyline/squisq 2.11.12 → 2.11.14

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.
package/README.md CHANGED
@@ -67,6 +67,26 @@ console.log(doc.children); // AST nodes
67
67
  const md = stringifyMarkdown(doc);
68
68
  ```
69
69
 
70
+ ### Condense imported Markdown
71
+
72
+ `condenseMarkdownSource(source)` removes table alignment padding, shortens
73
+ GFM delimiter rows, and reduces decorative thematic breaks to three markers.
74
+ It preserves table cells and alignment, code, math, HTML, links, footnotes,
75
+ prose, and frontmatter. The output is reparsed and structurally compared with
76
+ the original before returning; the API throws if equivalence cannot be proven.
77
+ It is idempotent and does not summarize articles or remove citations.
78
+
79
+ Run condensation after link/image edits, which can re-serialize padded tables.
80
+
81
+ ```ts
82
+ import { condenseMarkdownSource } from '@bendyline/squisq/markdown';
83
+
84
+ const compactMarkdown = condenseMarkdownSource(importedMarkdown);
85
+ ```
86
+
87
+ The shared source-transform registry also exposes `condense`, available from
88
+ the editor's Transform menu and `squisq transform file.md --ops condense`.
89
+
70
90
  ### Author an ASCII Timeline
71
91
 
72
92
  Timeline fences are source-of-truth markdown. Unicode marker rails are
@@ -739,6 +739,10 @@ interface BaseTemplateBlock {
739
739
  * Provenance only — NOT a time (see sourceStartTime for seconds).
740
740
  */
741
741
  sourceCharOffset?: number;
742
+ /** Projection-only summary policy; keeps visual features on their owning slide. */
743
+ summaryMode?: 'brief' | 'headings-and-features';
744
+ /** Automatic title-and-feature composition; omitted for authored templates. */
745
+ summaryLayout?: 'feature';
742
746
  /**
743
747
  * Per-block override for the theme's photographic image grade
744
748
  * (`theme.style.imageTreatment`). `'none'` opts this block's imagery out;
@@ -2482,6 +2486,10 @@ interface Block {
2482
2486
  sourceBlockIds?: string[];
2483
2487
  /** Char offset of the extraction inside the source block's plain text. */
2484
2488
  sourceCharOffset?: number;
2489
+ /** Projection-only summary policy; keeps visual features on their owning slide. */
2490
+ summaryMode?: 'brief' | 'headings-and-features';
2491
+ /** Automatic title-and-feature composition; omitted for authored templates. */
2492
+ summaryLayout?: 'feature';
2485
2493
  /**
2486
2494
  * Pre-computed visual layers, rendered back-to-front.
2487
2495
  *
@@ -1,4 +1,4 @@
1
- import { a2 as ImageLayer, bu as TextLayer, bh as ShapeLayer, aX as PathLayer } from './Doc-BWtfp7dT.js';
1
+ import { a2 as ImageLayer, bu as TextLayer, bh as ShapeLayer, aX as PathLayer } from './Doc-C-cv1g4G.js';
2
2
 
3
3
  /**
4
4
  * ImageEditDoc — JSON document describing a layered, editable image.
@@ -4,8 +4,16 @@ import {
4
4
  stripMarkdown
5
5
  } from "./chunk-SVN7T6BK.js";
6
6
  import {
7
+ coerceTemplateParams,
8
+ deriveTemplateInputs,
9
+ flattenRenderableBlocks,
7
10
  markdownToDoc
8
- } from "./chunk-42IU2SJY.js";
11
+ } from "./chunk-YPI2VJSB.js";
12
+ import {
13
+ isAsciiDiagramFence,
14
+ isAsciiTimelineFence,
15
+ isTreeFence
16
+ } from "./chunk-C3NAY5YP.js";
9
17
  import {
10
18
  cloneAndFreezeData,
11
19
  isTemplateBlock
@@ -13,9 +21,13 @@ import {
13
21
  import {
14
22
  parseMarkdown
15
23
  } from "./chunk-HUXK7EOT.js";
24
+ import {
25
+ isContainerTemplate
26
+ } from "./chunk-TMOHAFYE.js";
16
27
  import {
17
28
  extractPlainText,
18
- getChildren
29
+ getChildren,
30
+ stringifyHtmlNodes
19
31
  } from "./chunk-X7BNDUNI.js";
20
32
  import {
21
33
  estimateTimeFromText
@@ -99,26 +111,45 @@ var narrativeStyle = {
99
111
  var minimalStyle = {
100
112
  id: "minimal",
101
113
  name: "Minimal",
102
- description: "Light touch \u2014 only the strongest highlights, mostly text",
114
+ description: "Short highlights and a single brief line per section",
115
+ contentMode: "brief",
103
116
  minConfidence: 0.6,
104
117
  transformRatio: 0.2,
105
118
  preferredTypes: ["stat", "quote"],
106
119
  colorSchemes: ["blue", "green"],
107
120
  insertSectionHeaders: false,
108
121
  interleaveImages: false,
109
- blocksPerSection: { max: 2 },
122
+ blocksPerSection: { max: 1 },
110
123
  transitionStyle: "fade",
111
124
  suggestedThemeId: "minimalist",
112
125
  page: { spacing: "generous", emphasisCurve: "even" }
113
126
  };
114
127
 
128
+ // src/transform/styles/headingsAndFeatures.ts
129
+ var headingsAndFeaturesStyle = {
130
+ id: "headings-and-features",
131
+ name: "Headings and features",
132
+ description: "Headings with their images, videos, and diagrams; no body prose",
133
+ contentMode: "headings-and-features",
134
+ minConfidence: 1,
135
+ transformRatio: 0,
136
+ preferredTypes: [],
137
+ colorSchemes: ["blue", "green"],
138
+ insertSectionHeaders: false,
139
+ interleaveImages: false,
140
+ blocksPerSection: { max: 1 },
141
+ transitionStyle: "fade",
142
+ page: { spacing: "generous", emphasisCurve: "even" }
143
+ };
144
+
115
145
  // src/transform/registry.ts
116
146
  var TRANSFORM_STYLES = Object.freeze({
117
147
  [documentaryStyle.id]: cloneAndFreezeData(documentaryStyle),
118
148
  [magazineStyle.id]: cloneAndFreezeData(magazineStyle),
119
149
  [dataDrivenStyle.id]: cloneAndFreezeData(dataDrivenStyle),
120
150
  [narrativeStyle.id]: cloneAndFreezeData(narrativeStyle),
121
- [minimalStyle.id]: cloneAndFreezeData(minimalStyle)
151
+ [minimalStyle.id]: cloneAndFreezeData(minimalStyle),
152
+ [headingsAndFeaturesStyle.id]: cloneAndFreezeData(headingsAndFeaturesStyle)
122
153
  });
123
154
  var DEFAULT_TRANSFORM_STYLE_ID = "documentary";
124
155
  var EXTRACTION_TYPES = /* @__PURE__ */ new Set([
@@ -148,6 +179,9 @@ function validateTransformStyle(style) {
148
179
  fail(`${key} must be a finite number between 0 and 1`);
149
180
  }
150
181
  }
182
+ if (candidate.contentMode !== void 0 && candidate.contentMode !== "brief" && candidate.contentMode !== "headings-and-features") {
183
+ fail("contentMode must be brief or headings-and-features");
184
+ }
151
185
  if (!Array.isArray(candidate.preferredTypes) || candidate.preferredTypes.some((type) => !EXTRACTION_TYPES.has(type))) {
152
186
  fail("preferredTypes contains an unknown extraction type");
153
187
  }
@@ -328,7 +362,9 @@ function collectLeafBlocks(blocks) {
328
362
  return result;
329
363
  }
330
364
  function analyzeBlocks(blocks, options) {
331
- const flat = collectLeafBlocks(blocks);
365
+ return analyzeFlatBlocks(collectLeafBlocks(blocks), options);
366
+ }
367
+ function analyzeFlatBlocks(flat, options) {
332
368
  const results = [];
333
369
  for (const block of flat) {
334
370
  let plainText = "";
@@ -353,6 +389,223 @@ function analyzeBlocks(blocks, options) {
353
389
  return results;
354
390
  }
355
391
 
392
+ // src/transform/contentSummary.ts
393
+ var NATIVE_FEATURE_TEMPLATES = /* @__PURE__ */ new Set([
394
+ "diagram",
395
+ "tree",
396
+ "timeline",
397
+ "map",
398
+ "drawing",
399
+ "layout",
400
+ "dataTable",
401
+ "barChart",
402
+ "columnChart",
403
+ "pieChart",
404
+ "donutChart",
405
+ "lineChart",
406
+ "areaChart",
407
+ "scatterChart"
408
+ ]);
409
+ var VIDEO_FILE_RE = /\.(?:webm|mp4|mov|m4v|ogv)(?:[?#].*)?$/i;
410
+ var PROSE_KEYS = /* @__PURE__ */ new Set([
411
+ "body",
412
+ "subtitle",
413
+ "description",
414
+ "explanation",
415
+ "definition",
416
+ "detail",
417
+ "caption",
418
+ "quote",
419
+ "text"
420
+ ]);
421
+ function briefText(text) {
422
+ const normalized = text.replace(/\s+/g, " ").trim();
423
+ const sentence = normalized.match(/^.*?[.!?](?:["'”’])?(?=\s|$)/)?.[0] ?? normalized;
424
+ const words = sentence.split(/\s+/).filter(Boolean);
425
+ let brief = words.slice(0, 24).join(" ");
426
+ const truncated = words.length > 24 || brief.length > 180;
427
+ if (brief.length > 179 && truncated) {
428
+ brief = brief.slice(0, 179).replace(/\s+\S*$/, "").trimEnd();
429
+ }
430
+ return truncated ? `${brief.replace(/[.,;:!?]+$/, "")}\u2026` : brief;
431
+ }
432
+ function htmlFeatures(nodes) {
433
+ return nodes.flatMap((node) => {
434
+ if (node.type !== "htmlElement") return [];
435
+ const tag = node.tagName.toLowerCase();
436
+ if (tag === "img") return [node];
437
+ if (tag === "video") {
438
+ return [
439
+ {
440
+ ...node,
441
+ children: node.children.filter(
442
+ (child) => child.type === "htmlElement" && ["source", "track"].includes(child.tagName.toLowerCase())
443
+ )
444
+ }
445
+ ];
446
+ }
447
+ return htmlFeatures(node.children);
448
+ });
449
+ }
450
+ function extractFeatureContents(contents, keepData = false) {
451
+ const features = [];
452
+ const visit = (node) => {
453
+ if (node.type === "code") {
454
+ if (node.lang?.trim().toLowerCase() === "mermaid" || isAsciiDiagramFence(node) || isTreeFence(node) || isAsciiTimelineFence(node) || keepData && /^(json|ya?ml)$/i.test(node.lang ?? "") && /\bdata\b/i.test(node.meta ?? ""))
455
+ features.push(node);
456
+ return;
457
+ }
458
+ if (node.type === "table") {
459
+ if (keepData) features.push(node);
460
+ else for (const child of getChildren(node)) visit(child);
461
+ return;
462
+ }
463
+ if (node.type === "image" || node.type === "link" && VIDEO_FILE_RE.test(node.url)) {
464
+ features.push({ type: "paragraph", children: [node] });
465
+ return;
466
+ }
467
+ if (node.type === "htmlBlock" || node.type === "htmlInline") {
468
+ const htmlChildren = htmlFeatures(node.htmlChildren);
469
+ if (htmlChildren.length) {
470
+ features.push({
471
+ type: "htmlBlock",
472
+ htmlChildren,
473
+ rawHtml: stringifyHtmlNodes(htmlChildren)
474
+ });
475
+ }
476
+ return;
477
+ }
478
+ for (const child of getChildren(node)) visit(child);
479
+ };
480
+ for (const node of contents ?? []) visit(node);
481
+ return features;
482
+ }
483
+ function withoutProse(record) {
484
+ return record && Object.fromEntries(Object.entries(record).filter(([key]) => !PROSE_KEYS.has(key)));
485
+ }
486
+ function typedMediaContents(block) {
487
+ const record = {
488
+ ...block,
489
+ ...block.templateData,
490
+ ...coerceTemplateParams(block.template ?? "", block.templateOverrides ?? {}).input
491
+ };
492
+ const contents = [];
493
+ const addImage = (src, alt) => {
494
+ if (typeof src === "string" && src) {
495
+ contents.push({
496
+ type: "paragraph",
497
+ children: [{ type: "image", url: src, alt: typeof alt === "string" ? alt : "" }]
498
+ });
499
+ }
500
+ };
501
+ addImage(record.imageSrc, record.imageAlt);
502
+ for (const key of ["accentImage", "backgroundImage"]) {
503
+ const image = record[key];
504
+ if (image && typeof image === "object") {
505
+ const data = image;
506
+ addImage(data.src, data.alt);
507
+ }
508
+ }
509
+ if (Array.isArray(record.images)) {
510
+ for (const image of record.images) {
511
+ if (image && typeof image === "object") {
512
+ const data = image;
513
+ addImage(data.src, data.alt);
514
+ }
515
+ }
516
+ }
517
+ if (typeof record.videoSrc === "string" && record.videoSrc) {
518
+ const htmlChildren = [
519
+ {
520
+ type: "htmlElement",
521
+ tagName: "video",
522
+ selfClosing: false,
523
+ children: [],
524
+ attributes: {
525
+ src: record.videoSrc,
526
+ ...typeof record.posterSrc === "string" ? { poster: record.posterSrc } : {}
527
+ }
528
+ }
529
+ ];
530
+ contents.push({ type: "htmlBlock", htmlChildren, rawHtml: stringifyHtmlNodes(htmlChildren) });
531
+ }
532
+ return contents;
533
+ }
534
+ function summarizeBlock(block, mode) {
535
+ const title = block.sourceHeading ? extractPlainText(block.sourceHeading) : block.title ?? "";
536
+ const nativeFeature = NATIVE_FEATURE_TEMPLATES.has(block.template ?? "");
537
+ const authoredTemplate = mode === "headings-and-features" && !!block.template && !block.autoTemplate && (!block.sourceHeading || !!block.sourceHeading.templateAnnotation || !!block.promotedBodyAnnotation);
538
+ const keepTemplate = nativeFeature || authoredTemplate;
539
+ const tableInputs = keepTemplate ? null : deriveTemplateInputs("dataTable", title, block.contents);
540
+ const featureContents = extractFeatureContents(block.contents, keepTemplate || !!tableInputs);
541
+ const brief = mode === "brief" ? briefText(
542
+ (block.contents ?? []).filter((node) => ["paragraph", "list", "blockquote"].includes(node.type)).map(extractPlainText).join(" ")
543
+ ) : "";
544
+ const template = keepTemplate ? block.template : tableInputs ? "dataTable" : brief ? "content" : "sectionHeader";
545
+ const contents = [
546
+ ...brief && !nativeFeature && !tableInputs ? [{ type: "paragraph", children: [{ type: "text", value: brief }] }] : [],
547
+ ...featureContents,
548
+ ...typedMediaContents(block)
549
+ ];
550
+ const base = {
551
+ id: block.id,
552
+ startTime: block.startTime,
553
+ duration: block.duration,
554
+ audioSegment: block.audioSegment,
555
+ title,
556
+ template,
557
+ contents,
558
+ summaryMode: mode,
559
+ ...mode === "headings-and-features" && !keepTemplate && !tableInputs && contents.length ? { summaryLayout: "feature" } : {},
560
+ sourceBlockId: block.id,
561
+ sourceCharOffset: 0,
562
+ sourceStartTime: block.startTime,
563
+ sourceDuration: block.duration,
564
+ transition: block.transition,
565
+ media: block.media,
566
+ ...block.sourceHeading ? { sourceHeading: { ...block.sourceHeading, templateAnnotation: { template } } } : {}
567
+ };
568
+ if (!keepTemplate && !tableInputs) return base;
569
+ const headingInputs = authoredTemplate ? {
570
+ body: "",
571
+ subtitle: "",
572
+ description: template === "statHighlight" ? title : "",
573
+ explanation: "",
574
+ definition: "",
575
+ detail: "",
576
+ ...template === "list" ? { items: [] } : {},
577
+ ...template === "imageWithCaption" || template === "videoWithCaption" ? { caption: title } : {},
578
+ ...template === "quote" ? { quote: title } : {},
579
+ ...template === "pullQuote" || template === "fullBleedQuote" ? { text: title } : {}
580
+ } : {};
581
+ return {
582
+ ...keepTemplate ? withoutProse(block) : {},
583
+ ...base,
584
+ ...headingInputs,
585
+ children: keepTemplate && isContainerTemplate(template) ? block.children : void 0,
586
+ templateData: tableInputs ?? { ...withoutProse(block.templateData), ...headingInputs },
587
+ templateOverrides: keepTemplate ? withoutProse(
588
+ template === "list" ? Object.fromEntries(
589
+ Object.entries(block.templateOverrides ?? {}).filter(([key]) => key !== "items")
590
+ ) : block.templateOverrides
591
+ ) : void 0
592
+ };
593
+ }
594
+ function summarizeHighlight(block, source) {
595
+ const summary = {
596
+ ...block,
597
+ summaryMode: "brief",
598
+ contents: [...extractFeatureContents(source.contents), ...typedMediaContents(source)],
599
+ media: source.media
600
+ };
601
+ if (summary.template === "quote") return { ...summary, quote: briefText(summary.quote) };
602
+ if (summary.template === "statHighlight")
603
+ return { ...summary, description: briefText(summary.description), detail: void 0 };
604
+ if (summary.template === "fullBleedQuote" || summary.template === "pullQuote")
605
+ return { ...summary, text: briefText(summary.text) };
606
+ return summary;
607
+ }
608
+
356
609
  // src/transform/templateSelector.ts
357
610
  var ACCENT_POSITIONS = [
358
611
  "left-strip",
@@ -446,7 +699,8 @@ function buildBlockSequence(analyzed, selected, config, images, rng) {
446
699
  const ab = analyzed[i];
447
700
  const extractions = blockExtractions.get(i);
448
701
  if (!extractions || extractions.length === 0) {
449
- blocks.push(ab.block);
702
+ blocks.push(config.contentMode === "brief" ? summarizeBlock(ab.block, "brief") : ab.block);
703
+ if (config.contentMode === "brief") transformedCount++;
450
704
  continue;
451
705
  }
452
706
  if (config.insertSectionHeaders && ab.block.title) {
@@ -495,6 +749,9 @@ function buildBlockSequence(analyzed, selected, config, images, rng) {
495
749
  const translated = translateTemplateBlock(templateBlock, remapTarget);
496
750
  if (translated) templateBlock = translated;
497
751
  }
752
+ if (config.contentMode === "brief") {
753
+ templateBlock = summarizeHighlight(templateBlock, ab.block);
754
+ }
498
755
  if (config.transitionStyle !== "cut") {
499
756
  const transitionType = config.transitionStyle === "mixed" ? rng.pick(["fade", "dissolve"]) ?? "fade" : config.transitionStyle;
500
757
  templateBlock.transition = {
@@ -780,12 +1037,15 @@ function applyTransform(doc, style, options) {
780
1037
  const config = options?.overrides ? { ...baseConfig, ...options.overrides, id: baseConfig.id } : baseConfig;
781
1038
  const seed = options?.seed ?? hashString(doc.articleId || "transform");
782
1039
  const images = options?.images ?? extractDocImages(doc.blocks);
783
- const analyzed = analyzeBlocks(doc.blocks, {
784
- minConfidence: config.minConfidence,
785
- types: config.preferredTypes
786
- });
1040
+ const analyzed = (config.contentMode ? analyzeFlatBlocks : analyzeBlocks)(
1041
+ config.contentMode ? flattenRenderableBlocks(doc.blocks) : doc.blocks,
1042
+ {
1043
+ minConfidence: config.minConfidence,
1044
+ types: config.preferredTypes
1045
+ }
1046
+ );
787
1047
  const hasContent = analyzed.some((ab) => ab.bodyWordCount >= 5);
788
- if (!hasContent) {
1048
+ if (!hasContent && !config.contentMode) {
789
1049
  return {
790
1050
  doc: { ...doc },
791
1051
  stats: {
@@ -795,7 +1055,11 @@ function applyTransform(doc, style, options) {
795
1055
  }
796
1056
  };
797
1057
  }
798
- const selection = selectAndBuild(analyzed, config, images, seed);
1058
+ const selection = config.contentMode === "headings-and-features" ? {
1059
+ blocks: analyzed.filter((ab) => ab.block.sourceHeading || ab.block.title).map((ab) => summarizeBlock(ab.block, "headings-and-features")),
1060
+ transformedCount: analyzed.filter((ab) => ab.block.sourceHeading || ab.block.title).length,
1061
+ insertedCount: 0
1062
+ } : selectAndBuild(analyzed, config, images, seed);
799
1063
  let paced = selection.blocks;
800
1064
  let pacingInserted = 0;
801
1065
  const docTitle = firstTitle(doc);
@@ -826,7 +1090,13 @@ function applyTransform(doc, style, options) {
826
1090
  const transformedDoc = {
827
1091
  ...doc,
828
1092
  blocks: timedBlocks,
829
- themeId: options?.themeId ?? doc.themeId ?? config.suggestedThemeId
1093
+ themeId: options?.themeId ?? doc.themeId ?? config.suggestedThemeId,
1094
+ ...config.contentMode && doc.startBlock ? {
1095
+ startBlock: {
1096
+ ...doc.startBlock,
1097
+ subtitle: config.contentMode === "brief" ? briefText(doc.startBlock.subtitle ?? "") : void 0
1098
+ }
1099
+ } : {}
830
1100
  };
831
1101
  return {
832
1102
  doc: transformedDoc,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  flattenRenderableBlocks,
3
3
  getBlockBodyText
4
- } from "./chunk-42IU2SJY.js";
4
+ } from "./chunk-YPI2VJSB.js";
5
5
  import {
6
6
  estimateSpokenWordCount
7
7
  } from "./chunk-GHOPHGOK.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildNarrationScript,
3
3
  parseNarrationTimingJson
4
- } from "./chunk-BR7ZMDWW.js";
4
+ } from "./chunk-6H2HBBFC.js";
5
5
  import {
6
6
  SHAPE_NAMES,
7
7
  buildChartData,
@@ -32,7 +32,7 @@ import {
32
32
  templateRegistry,
33
33
  writeCustomTemplatesToFrontmatter,
34
34
  writeCustomThemesToFrontmatter
35
- } from "./chunk-42IU2SJY.js";
35
+ } from "./chunk-YPI2VJSB.js";
36
36
  import {
37
37
  ASCII_TREE_VOCAB,
38
38
  ASCII_VOCAB,
@@ -954,8 +954,10 @@ function blockToSlide(block, index, knownTemplates, documentTitle) {
954
954
  // authored rich elements (Mermaid fences today; other media can follow)
955
955
  // independently of the selected visual template.
956
956
  ...block.contents ? { contents: block.contents } : {},
957
- // Custom templates additionally consume child blocks through tokens.
958
- ...isCustomTemplate && block.children ? { children: block.children } : {},
957
+ // Custom templates consume child blocks through tokens. Compact summaries
958
+ // also retain the children owned by a diagram/drawing feature.
959
+ ...block.children && (isCustomTemplate || block.summaryMode && isContainerTemplate(template)) ? { children: block.children } : {},
960
+ ...block.summaryMode && block.layers ? { layers: block.layers } : {},
959
961
  ...defaults,
960
962
  ...extraFields,
961
963
  // Structured body data (```json data fences, GFM tables for dataTable)
@@ -1031,7 +1033,9 @@ function buildPreviewDoc(doc, options) {
1031
1033
  const block = flat[i];
1032
1034
  const blockImages = extractBlockImages(block.contents);
1033
1035
  const slide = blockToSlide(block, i, knownTemplates, documentTitle);
1034
- if (blockImages.length > 0 && slide.template === "sectionHeader") {
1036
+ if (block.summaryMode) {
1037
+ for (const img of blockImages) usedImageSrcs.add(img.src);
1038
+ } else if (blockImages.length > 0 && slide.template === "sectionHeader") {
1035
1039
  const img = blockImages[0];
1036
1040
  usedImageSrcs.add(img.src);
1037
1041
  slide.template = "imageWithCaption";
@@ -7,10 +7,10 @@ import {
7
7
  buildNarrationScript,
8
8
  expectedSyllablesAt,
9
9
  wordPosAtExpectedSyllables
10
- } from "./chunk-BR7ZMDWW.js";
10
+ } from "./chunk-6H2HBBFC.js";
11
11
  import {
12
12
  flattenRenderableBlocks
13
- } from "./chunk-42IU2SJY.js";
13
+ } from "./chunk-YPI2VJSB.js";
14
14
 
15
15
  // src/narration/types.ts
16
16
  var DEFAULT_FEATURE_CONFIG = Object.freeze({