@bendyline/squisq 1.5.1 → 2.0.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.
- package/README.md +63 -10
- package/dist/{Doc-BBVGq1_9.d.ts → Doc-ylqWKDc_.d.ts} +248 -37
- package/dist/{ImageEditDoc-FuyGtt6o.d.ts → ImageEditDoc-D828IXJ1.d.ts} +1 -1
- package/dist/annotationCoercion-Dr_DY9Hx.d.ts +86 -0
- package/dist/{chunk-VWUFZ6ZG.js → chunk-3E5F2XMR.js} +2 -29
- package/dist/chunk-3E5F2XMR.js.map +1 -0
- package/dist/chunk-4X3JQXNM.js +35 -0
- package/dist/chunk-4X3JQXNM.js.map +1 -0
- package/dist/{chunk-2OIBZYKP.js → chunk-5DKBFXPY.js} +31 -121
- package/dist/chunk-5DKBFXPY.js.map +1 -0
- package/dist/{chunk-DLXZMS5K.js → chunk-5J4HKRJF.js} +51 -3
- package/dist/chunk-5J4HKRJF.js.map +1 -0
- package/dist/chunk-5UYI3RJB.js +2503 -0
- package/dist/chunk-5UYI3RJB.js.map +1 -0
- package/dist/{chunk-TEEEILMP.js → chunk-66QO7XFB.js} +19 -292
- package/dist/chunk-66QO7XFB.js.map +1 -0
- package/dist/{chunk-KKNUBQ6Y.js → chunk-6PJRB2CF.js} +156 -91
- package/dist/chunk-6PJRB2CF.js.map +1 -0
- package/dist/{chunk-HWVFJAAH.js → chunk-AMIR72JP.js} +28 -10
- package/dist/chunk-AMIR72JP.js.map +1 -0
- package/dist/chunk-FVJUUTEM.js +74 -0
- package/dist/chunk-FVJUUTEM.js.map +1 -0
- package/dist/{chunk-LH7I6SH7.js → chunk-GF3BVNLU.js} +6360 -5723
- package/dist/chunk-GF3BVNLU.js.map +1 -0
- package/dist/{chunk-Q3ROPT5H.js → chunk-GZY66SDN.js} +1 -1
- package/dist/{chunk-Q3ROPT5H.js.map → chunk-GZY66SDN.js.map} +1 -1
- package/dist/chunk-JOQYPRDZ.js +127 -0
- package/dist/chunk-JOQYPRDZ.js.map +1 -0
- package/dist/{chunk-A4O7GIWE.js → chunk-LYNJEOAC.js} +10 -73
- package/dist/chunk-LYNJEOAC.js.map +1 -0
- package/dist/{chunk-LDQ2HJIX.js → chunk-PXLVXTLW.js} +252 -54
- package/dist/chunk-PXLVXTLW.js.map +1 -0
- package/dist/{chunk-TQWLI6S2.js → chunk-Q7LROV2H.js} +4 -2
- package/dist/{chunk-TQWLI6S2.js.map → chunk-Q7LROV2H.js.map} +1 -1
- package/dist/{chunk-T5UK6YOB.js → chunk-QTSJOY54.js} +60 -17
- package/dist/chunk-QTSJOY54.js.map +1 -0
- package/dist/chunk-QVANWDPH.js +2358 -0
- package/dist/chunk-QVANWDPH.js.map +1 -0
- package/dist/{chunk-6ATE2PSM.js → chunk-R2GC6RLD.js} +7 -7
- package/dist/chunk-R2GC6RLD.js.map +1 -0
- package/dist/chunk-R2YZTCLO.js +292 -0
- package/dist/chunk-R2YZTCLO.js.map +1 -0
- package/dist/chunk-S7A5D7ZJ.js +174 -0
- package/dist/chunk-S7A5D7ZJ.js.map +1 -0
- package/dist/chunk-T3TI7GSZ.js +884 -0
- package/dist/chunk-T3TI7GSZ.js.map +1 -0
- package/dist/{chunk-RUDYOTA6.js → chunk-TUWMK62B.js} +110 -4
- package/dist/chunk-TUWMK62B.js.map +1 -0
- package/dist/{chunk-IMSCRLLV.js → chunk-VR7UPJ7C.js} +35 -25
- package/dist/chunk-VR7UPJ7C.js.map +1 -0
- package/dist/doc/index.d.ts +2334 -5
- package/dist/doc/index.js +134 -34
- package/dist/generate/index.d.ts +7 -2
- package/dist/generate/index.js +1 -1
- package/dist/imageEdit/index.d.ts +9 -6
- package/dist/imageEdit/index.js +3 -3
- package/dist/index.d.ts +12 -10
- package/dist/index.js +253 -81
- package/dist/jsonForm/index.d.ts +7 -3
- package/dist/jsonForm/index.js +9 -4
- package/dist/markdown/index.d.ts +6 -70
- package/dist/markdown/index.js +13 -9
- package/dist/narration/index.d.ts +618 -0
- package/dist/narration/index.js +82 -0
- package/dist/recommend/index.d.ts +15 -1
- package/dist/recommend/index.js +2 -2
- package/dist/schemas/index.d.ts +28 -6
- package/dist/schemas/index.js +15 -13
- package/dist/spatial/index.d.ts +0 -1
- package/dist/spatial/index.js +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/{themeLibrary-CehcJhzz.d.ts → themeLibrary-RjVgLpUi.d.ts} +10 -11
- package/dist/timing/index.js +5 -3
- package/dist/transform/index.d.ts +35 -24
- package/dist/transform/index.js +9 -10
- package/dist/{types-DlAZ7MW4.d.ts → types-BwMPl2CB.d.ts} +1 -1
- package/dist/{types-D98li1gV.d.ts → types-C4kiRn_k.d.ts} +3 -1
- package/dist/versions/index.d.ts +2 -1
- package/dist/versions/index.js +2 -2
- package/package.json +6 -6
- package/src/__tests__/applyNarrationTiming.test.ts +190 -0
- package/src/__tests__/applyRenderStyle.test.ts +3 -3
- package/src/__tests__/asciiDiagramBattle.test.ts +246 -0
- package/src/__tests__/asciiDiagramDetect.test.ts +162 -0
- package/src/__tests__/asciiDiagramMapping.test.ts +38 -0
- package/src/__tests__/asciiDiagramParse.test.ts +233 -0
- package/src/__tests__/asciiDiagramPipeline.test.ts +273 -0
- package/src/__tests__/asciiDiagramRender.test.ts +162 -0
- package/src/__tests__/asciiDiagramRepair.test.ts +84 -0
- package/src/__tests__/asciiTimelineDetect.test.ts +157 -0
- package/src/__tests__/asciiTimelineParse.test.ts +205 -0
- package/src/__tests__/asciiTimelinePipeline.test.ts +349 -0
- package/src/__tests__/asciiTimelineRender.test.ts +376 -0
- package/src/__tests__/attrTokens.test.ts +5 -0
- package/src/__tests__/contentContainer.test.ts +43 -0
- package/src/__tests__/coverBlock.test.ts +33 -2
- package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -15
- package/src/__tests__/dataTable.test.ts +25 -15
- package/src/__tests__/diagramBlock.test.ts +3 -3
- package/src/__tests__/diagramText.test.ts +26 -0
- package/src/__tests__/docToMarkdownMedia.test.ts +83 -0
- package/src/__tests__/drawingBlock.test.ts +3 -3
- package/src/__tests__/fixtures/asciiDiagramBattle.ts +512 -0
- package/src/__tests__/fixtures/asciiDiagrams.ts +287 -0
- package/src/__tests__/fixtures/asciiTimelines.ts +35 -0
- package/src/__tests__/fixtures/treeviewFixtures.ts +243 -0
- package/src/__tests__/getLayers.test.ts +32 -18
- package/src/__tests__/getLayersFallback.test.ts +8 -8
- package/src/__tests__/imageEditVersions.test.ts +2 -0
- package/src/__tests__/jsonForm.pointer.test.ts +47 -1
- package/src/__tests__/layoutBlock.test.ts +3 -3
- package/src/__tests__/majorApiTypes.test.ts +18 -0
- package/src/__tests__/markdown.test.ts +41 -0
- package/src/__tests__/markdownToDoc.test.ts +5 -2
- package/src/__tests__/matchFontFamily.test.ts +114 -0
- package/src/__tests__/materializeBlockLayers.test.ts +135 -0
- package/src/__tests__/materializeTestUtils.ts +17 -0
- package/src/__tests__/narrationAlign.test.ts +150 -0
- package/src/__tests__/narrationFeatures.test.ts +71 -0
- package/src/__tests__/narrationNuclei.test.ts +45 -0
- package/src/__tests__/narrationPacing.test.ts +189 -0
- package/src/__tests__/narrationScript.test.ts +149 -0
- package/src/__tests__/narrationSessionRepro.test.ts +62 -0
- package/src/__tests__/narrationSidecar.test.ts +88 -0
- package/src/__tests__/narrationSyllables.test.ts +65 -0
- package/src/__tests__/narrationTestSignals.ts +167 -0
- package/src/__tests__/narrationVad.test.ts +66 -0
- package/src/__tests__/resolveDocTheme.test.ts +16 -2
- package/src/__tests__/rootExports.test.ts +18 -0
- package/src/__tests__/scopedContentContainer.test.ts +20 -0
- package/src/__tests__/shapeGeometry.test.ts +49 -0
- package/src/__tests__/templateAnnotationParse.test.ts +10 -0
- package/src/__tests__/templateInputs.test.ts +69 -0
- package/src/__tests__/templateMetadata.test.ts +2 -1
- package/src/__tests__/templates.test.ts +143 -17
- package/src/__tests__/themeCompile.test.ts +49 -22
- package/src/__tests__/transformProvenance.test.ts +160 -0
- package/src/__tests__/transformV2.test.ts +40 -14
- package/src/__tests__/treeviewBattle.test.ts +76 -0
- package/src/__tests__/treeviewDetect.test.ts +168 -0
- package/src/__tests__/treeviewParse.test.ts +118 -0
- package/src/__tests__/treeviewPipeline.test.ts +183 -0
- package/src/__tests__/treeviewRender.test.ts +91 -0
- package/src/__tests__/versions.test.ts +44 -0
- package/src/doc/applyNarrationTiming.ts +244 -0
- package/src/doc/asciiDiagram/chars.ts +210 -0
- package/src/doc/asciiDiagram/detect.ts +175 -0
- package/src/doc/asciiDiagram/index.ts +38 -0
- package/src/doc/asciiDiagram/mapping.ts +225 -0
- package/src/doc/asciiDiagram/parse.ts +1095 -0
- package/src/doc/asciiDiagram/render.ts +963 -0
- package/src/doc/asciiDiagram/repair.ts +400 -0
- package/src/doc/asciiDiagram/types.ts +65 -0
- package/src/doc/asciiTimeline/detect.ts +138 -0
- package/src/doc/asciiTimeline/index.ts +26 -0
- package/src/doc/asciiTimeline/mapping.ts +43 -0
- package/src/doc/asciiTimeline/parse.ts +870 -0
- package/src/doc/asciiTimeline/render.ts +427 -0
- package/src/doc/asciiTimeline/types.ts +73 -0
- package/src/doc/asciiTimeline/wrappedFlow.ts +427 -0
- package/src/doc/audioMapping.ts +60 -46
- package/src/doc/customTemplatesFrontmatter.ts +0 -41
- package/src/doc/docToMarkdown.ts +126 -39
- package/src/doc/index.ts +76 -4
- package/src/doc/markdownToDoc.ts +135 -1
- package/src/doc/materializeBlockLayers.ts +304 -0
- package/src/doc/mediaAnnotations.ts +20 -0
- package/src/doc/resolveDocTheme.ts +8 -7
- package/src/doc/templateInputs.ts +185 -2
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +22 -8
- package/src/doc/templates/coverBlock.ts +49 -13
- package/src/doc/templates/diagramBlock.ts +237 -38
- package/src/doc/templates/drawingBlock.ts +2 -2
- package/src/doc/templates/fallbackBlock.ts +1 -1
- package/src/doc/templates/index.ts +134 -279
- package/src/doc/templates/inputDescriptors.ts +20 -1
- package/src/doc/templates/metadata.ts +10 -0
- package/src/doc/templates/persistentLayers.ts +12 -126
- package/src/doc/templates/registry.ts +88 -0
- package/src/doc/templates/templateNames.ts +22 -0
- package/src/doc/templates/timelineBlock.ts +613 -0
- package/src/doc/templates/treeBlock.ts +131 -0
- package/src/doc/treeview/chars.ts +72 -0
- package/src/doc/treeview/detect.ts +145 -0
- package/src/doc/treeview/index.ts +30 -0
- package/src/doc/treeview/mapping.ts +95 -0
- package/src/doc/treeview/parse.ts +201 -0
- package/src/doc/treeview/render.ts +41 -0
- package/src/doc/treeview/types.ts +43 -0
- package/src/doc/utils/applyRenderStyle.ts +2 -9
- package/src/doc/utils/diagramText.ts +78 -0
- package/src/doc/utils/shapeGeometry.ts +157 -7
- package/src/doc/validate.ts +1 -1
- package/src/generate/templateMapper.ts +7 -0
- package/src/imageEdit/export.ts +6 -5
- package/src/imageEdit/versionPaths.ts +13 -26
- package/src/imageEdit/versions.ts +21 -100
- package/src/index.ts +1 -0
- package/src/internal/immutable.ts +58 -0
- package/src/jsonForm/index.ts +9 -1
- package/src/jsonForm/pointer.ts +68 -23
- package/src/markdown/attrTokens.ts +7 -5
- package/src/markdown/convert.ts +11 -1
- package/src/markdown/index.ts +1 -0
- package/src/markdown/stringify.ts +4 -9
- package/src/markdown/utils.ts +36 -14
- package/src/narration/align.ts +514 -0
- package/src/narration/features.ts +231 -0
- package/src/narration/index.ts +86 -0
- package/src/narration/nuclei.ts +118 -0
- package/src/narration/pacing.ts +211 -0
- package/src/narration/script.ts +239 -0
- package/src/narration/session.ts +100 -0
- package/src/narration/sidecar.ts +187 -0
- package/src/narration/syllables.ts +85 -0
- package/src/narration/trace.ts +50 -0
- package/src/narration/types.ts +299 -0
- package/src/narration/vad.ts +131 -0
- package/src/recommend/numberHighlight.ts +25 -0
- package/src/recommend/templates.ts +108 -12
- package/src/schemas/BlockTemplates.ts +156 -7
- package/src/schemas/Doc.ts +72 -9
- package/src/schemas/Media.ts +12 -2
- package/src/schemas/Theme.ts +45 -25
- package/src/schemas/Viewport.ts +2 -2
- package/src/schemas/fontStacks.ts +79 -0
- package/src/schemas/themeCompile.ts +15 -0
- package/src/schemas/themeConstants.ts +2 -0
- package/src/schemas/themeLibrary.ts +25 -19
- package/src/schemas/themeValidator.ts +1 -1
- package/src/spatial/Geohash.ts +0 -3
- package/src/storage/ContentContainer.ts +17 -9
- package/src/storage/ScopedContentContainer.ts +45 -15
- package/src/transform/applyTransform.ts +5 -5
- package/src/transform/index.ts +3 -1
- package/src/transform/registry.ts +169 -49
- package/src/transform/templateSelector.ts +21 -2
- package/src/transform/timingAllocator.ts +152 -21
- package/src/transform/types.ts +14 -1
- package/src/versions/gitignore.ts +34 -0
- package/src/versions/index.ts +2 -0
- package/src/versions/operations.ts +18 -104
- package/src/versions/paths.ts +13 -28
- package/src/versions/snapshotStore.ts +202 -0
- package/src/versions/types.ts +3 -1
- package/dist/chunk-2OIBZYKP.js.map +0 -1
- package/dist/chunk-6ATE2PSM.js.map +0 -1
- package/dist/chunk-A4O7GIWE.js.map +0 -1
- package/dist/chunk-DLXZMS5K.js.map +0 -1
- package/dist/chunk-HWVFJAAH.js.map +0 -1
- package/dist/chunk-IMSCRLLV.js.map +0 -1
- package/dist/chunk-JELIRVDP.js +0 -24
- package/dist/chunk-JELIRVDP.js.map +0 -1
- package/dist/chunk-KKNUBQ6Y.js.map +0 -1
- package/dist/chunk-LDQ2HJIX.js.map +0 -1
- package/dist/chunk-LH7I6SH7.js.map +0 -1
- package/dist/chunk-RUDYOTA6.js.map +0 -1
- package/dist/chunk-T5UK6YOB.js.map +0 -1
- package/dist/chunk-TEEEILMP.js.map +0 -1
- package/dist/chunk-UY7KGQ5R.js +0 -192
- package/dist/chunk-UY7KGQ5R.js.map +0 -1
- package/dist/chunk-VLJJHDAC.js +0 -218
- package/dist/chunk-VLJJHDAC.js.map +0 -1
- package/dist/chunk-VWUFZ6ZG.js.map +0 -1
- package/dist/story/index.d.ts +0 -1777
- package/dist/story/index.js +0 -273
- package/src/doc/getLayers.ts +0 -195
- /package/dist/{story → narration}/index.js.map +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generate/contentExtractor.ts","../src/generate/templateMapper.ts"],"sourcesContent":["/**\n * Content Extractor\n *\n * Pattern-based extraction of compelling content from plain text.\n * Identifies statistics, dates, quotes, comparisons, facts, impact lines,\n * lists, and definitions that can be turned into slideshow slides.\n *\n * Extraction is purely regex-based — no AI calls, no runtime costs,\n * deterministic and reproducible.\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\n/** Kinds of content the extractor recognises. */\nexport type ExtractionType =\n | 'stat'\n | 'date'\n | 'quote'\n | 'comparison'\n | 'fact'\n | 'impactLine'\n | 'list'\n | 'definition';\n\n/** An extracted content element with source position. */\nexport interface ExtractedElement {\n type: ExtractionType;\n /** Full extracted text (usually the surrounding sentence). */\n text: string;\n /** Confidence score 0–1. */\n confidence: number;\n /** Character offset in the source text. */\n sourcePosition: number;\n /** Character offset where element ends. */\n endPosition: number;\n /** Type-specific structured payload. */\n data:\n | StatData\n | DateData\n | QuoteData\n | ComparisonData\n | FactData\n | ImpactLineData\n | ListData\n | DefinitionData;\n}\n\nexport interface StatData {\n type: 'stat';\n value: string;\n unit?: string;\n description: string;\n}\n\nexport interface DateData {\n type: 'date';\n date: string;\n description: string;\n mood?: 'neutral' | 'somber' | 'celebratory';\n}\n\nexport interface QuoteData {\n type: 'quote';\n quote: string;\n attribution?: string;\n}\n\nexport interface ComparisonData {\n type: 'comparison';\n left: { label: string; sublabel?: string };\n right: { label: string; sublabel?: string };\n header?: string;\n}\n\nexport interface FactData {\n type: 'fact';\n fact: string;\n explanation?: string;\n}\n\nexport interface ImpactLineData {\n type: 'impactLine';\n text: string;\n}\n\nexport interface ListData {\n type: 'list';\n items: string[];\n title?: string;\n}\n\nexport interface DefinitionData {\n type: 'definition';\n term: string;\n definition: string;\n origin?: string;\n}\n\n/** Options for `extractContent`. */\nexport interface ExtractionOptions {\n /** Minimum confidence threshold (default: 0.3). */\n minConfidence?: number;\n /** Which types to extract (default: all). */\n types?: ExtractionType[];\n /** Maximum extractions per type (default: unlimited). */\n maxPerType?: number;\n}\n\n/** Result of `extractContent`. */\nexport interface ExtractionResult {\n elements: ExtractedElement[];\n sourceLength: number;\n stats: {\n statCount: number;\n dateCount: number;\n quoteCount: number;\n comparisonCount: number;\n factCount: number;\n impactLineCount: number;\n listCount: number;\n definitionCount: number;\n totalCount: number;\n };\n}\n\n// ── Patterns ───────────────────────────────────────────────────────\n\nconst STAT_PATTERNS = [\n /\\b(roughly |about |approximately |nearly |over |under |more than |less than )?([\\d,]+(?:\\.\\d+)?)\\s*%/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(million|billion|trillion|thousand|hundred)\\b/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(miles?|feet|ft|meters?|m|kilometers?|km|acres?|square miles?|sq mi|hectares?)\\b/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(x|times)\\b/gi,\n /\\b([$€£¥])\\s*([\\d,]+(?:\\.\\d+)?)\\s*(million|billion|thousand)?\\b/gi,\n /\\b(\\d+)(st|nd|rd|th)\\s+(largest|smallest|oldest|newest|most|least)/gi,\n];\n\nconst DATE_PATTERNS = [\n /\\b(January|February|March|April|May|June|July|August|September|October|November|December)\\s+(\\d{1,2},?\\s+)?(\\d{4})\\b/gi,\n /\\b(\\d{4})s\\b|\\b(\\d{1,2})(st|nd|rd|th)\\s+century\\b/gi,\n /\\b(from\\s+)?(\\d{4})\\s*(to|–|-|through)\\s*(\\d{4})\\b/gi,\n /\\b(in|by|since|after|before|during)\\s+(\\d{4})\\b/gi,\n];\n\nconst QUOTE_PATTERNS = [\n /\"([^\"]{15,200})\"/g,\n /\\u201c([^\\u201d]{15,200})\\u201d/g,\n /(\\w+(?:\\s+\\w+)?)\\s+(?:said|stated|wrote|noted|observed|commented),?\\s*\"([^\"]+)\"/gi,\n];\n\n// A single side of a comparison (\"X\" in \"from X to Y\"). Deliberately excludes\n// sentence/clause terminators (. ! ? ;) and newlines so a capture group can\n// never swallow text from an adjacent sentence. Without this, greedy `.{2,30}`\n// matches across a period — e.g. \"…contradicted from witness to witness.\n// Robertson said…\" yields right=\"witness. Robertson\", producing a nonsensical\n// \"witness → witness. Robertson\" slide. Commas and colons are allowed (ranges\n// like \"9:00 to 17:00\", ratios); downstream label truncation trims commas.\nconst COMPARISON_SIDE = '[^.!?;\\\\r\\\\n]{2,30}';\n\nconst COMPARISON_PATTERNS = [\n new RegExp(`from\\\\s+(${COMPARISON_SIDE})\\\\s+to\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n new RegExp(`(${COMPARISON_SIDE})\\\\s+(?:vs\\\\.?|versus)\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n new RegExp(`(${COMPARISON_SIDE})\\\\s+compared\\\\s+to\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n];\n\n// Words whose trailing period is an abbreviation, not a sentence end. Used by\n// isSentenceBoundary() so names like \"George W. Tibbetts\" or \"Quong Chong & Co.\"\n// don't get split into fragments. Matched only when the entire preceding word\n// equals one of these (case-insensitive), so \"canal.\" / \"taco.\" are unaffected.\nconst SENTENCE_ABBREVIATIONS = new Set([\n 'mr',\n 'mrs',\n 'ms',\n 'dr',\n 'prof',\n 'st',\n 'mt',\n 'jr',\n 'sr',\n 'gen',\n 'col',\n 'capt',\n 'lt',\n 'sgt',\n 'rev',\n 'hon',\n 'sen',\n 'rep',\n 'gov',\n 'pres',\n 'vs',\n 'etc',\n 'inc',\n 'co',\n 'ltd',\n 'corp',\n 'al',\n 'fig',\n 'eg',\n 'ie',\n 'approx',\n 'dept',\n 'univ',\n 'assn',\n 'bros',\n 'ave',\n 'blvd',\n 'rd',\n]);\n\nconst FACT_INDICATORS = [\n 'largest',\n 'smallest',\n 'oldest',\n 'newest',\n 'first',\n 'last',\n 'most',\n 'least',\n 'only',\n 'unique',\n 'rare',\n 'famous',\n 'important',\n 'significant',\n 'notable',\n 'remarkable',\n 'extraordinary',\n 'crucial',\n 'essential',\n 'critical',\n 'major',\n 'key',\n 'discovered',\n 'founded',\n 'established',\n 'created',\n 'built',\n 'invented',\n 'developed',\n 'introduced',\n 'pioneered',\n 'transformed',\n 'revolutionized',\n 'changed',\n 'shaped',\n];\n\nconst SOMBER_INDICATORS = [\n 'disaster',\n 'tragedy',\n 'death',\n 'died',\n 'killed',\n 'destroyed',\n 'devastated',\n 'collapsed',\n 'lost',\n 'abandoned',\n 'declined',\n 'war',\n 'battle',\n 'conflict',\n 'earthquake',\n 'fire',\n 'flood',\n];\n\nconst CELEBRATORY_INDICATORS = [\n 'celebration',\n 'victory',\n 'success',\n 'achievement',\n 'triumph',\n 'opened',\n 'completed',\n 'inaugurated',\n 'launched',\n 'won',\n 'record',\n 'breakthrough',\n 'milestone',\n 'golden',\n 'grand',\n];\n\nconst IMPACT_VERBS = [\n 'check',\n 'watch',\n 'stop',\n 'prepare',\n 'imagine',\n 'consider',\n 'look',\n 'listen',\n 'notice',\n 'remember',\n 'discover',\n 'explore',\n 'visit',\n 'try',\n 'experience',\n 'avoid',\n 'beware',\n 'enjoy',\n];\n\nconst LIST_PATTERNS = [\n /(?:including|such as|namely|like|features?|offers?)\\s+([^.]{10,150})/gi,\n /(?:three|four|five|six|several|many|various)\\s+(?:main|key|primary|notable|popular|important)?\\s*\\w+(?:\\s+\\w+)?:\\s*([^.]{10,150})/gi,\n];\n\nconst DEFINITION_PATTERNS = [\n /\\b([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3})\\s+is\\s+(?:a|an|the)\\s+([^.]{10,120})/g,\n /known\\s+as\\s+([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3}),\\s+([^.]{10,120})/gi,\n /\\b([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3})\\s+refers?\\s+to\\s+([^.]{10,120})/g,\n /called\\s+([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3}),?\\s+(?:which|meaning|a)\\s+([^.]{10,120})/gi,\n];\n\n// ── Main ───────────────────────────────────────────────────────────\n\n/**\n * Extract compelling content from plain text.\n *\n * Call `stripMarkdown(text)` first if the input contains markdown.\n */\nexport function extractContent(text: string, options: ExtractionOptions = {}): ExtractionResult {\n const {\n minConfidence = 0.3,\n types = ['stat', 'date', 'quote', 'comparison', 'fact', 'impactLine', 'list', 'definition'],\n maxPerType,\n } = options;\n\n const elements: ExtractedElement[] = [];\n\n if (types.includes('stat')) elements.push(...extractStats(text));\n if (types.includes('date')) elements.push(...extractDates(text));\n if (types.includes('quote')) elements.push(...extractQuotes(text));\n if (types.includes('comparison')) elements.push(...extractComparisons(text));\n if (types.includes('fact')) elements.push(...extractFacts(text));\n if (types.includes('impactLine')) elements.push(...extractImpactLines(text));\n if (types.includes('list')) elements.push(...extractLists(text));\n if (types.includes('definition')) elements.push(...extractDefinitions(text));\n\n let filtered = elements.filter((e) => e.confidence >= minConfidence);\n\n if (maxPerType !== undefined) {\n const byType = new Map<ExtractionType, ExtractedElement[]>();\n for (const elem of filtered) {\n const list = byType.get(elem.type) || [];\n list.push(elem);\n byType.set(elem.type, list);\n }\n filtered = [];\n for (const [, list] of byType) {\n list.sort((a, b) => b.confidence - a.confidence);\n filtered.push(...list.slice(0, maxPerType));\n }\n }\n\n filtered.sort((a, b) => a.sourcePosition - b.sourcePosition);\n filtered = deduplicateOverlapping(filtered);\n\n return {\n elements: filtered,\n sourceLength: text.length,\n stats: {\n statCount: filtered.filter((e) => e.type === 'stat').length,\n dateCount: filtered.filter((e) => e.type === 'date').length,\n quoteCount: filtered.filter((e) => e.type === 'quote').length,\n comparisonCount: filtered.filter((e) => e.type === 'comparison').length,\n factCount: filtered.filter((e) => e.type === 'fact').length,\n impactLineCount: filtered.filter((e) => e.type === 'impactLine').length,\n listCount: filtered.filter((e) => e.type === 'list').length,\n definitionCount: filtered.filter((e) => e.type === 'definition').length,\n totalCount: filtered.length,\n },\n };\n}\n\n// ── Per-type extractors ────────────────────────────────────────────\n\nfunction extractStats(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of STAT_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const context = getSentenceContext(text, position, match[0].length);\n const value = match[0].trim();\n\n elements.push({\n type: 'stat',\n text: context.sentence,\n confidence: calculateStatConfidence(value, context.sentence),\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'stat', value, description: cleanDescription(context.sentence, value) },\n });\n }\n }\n return elements;\n}\n\nfunction extractDates(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of DATE_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const context = getSentenceContext(text, position, match[0].length);\n const dateStr = match[0].trim();\n\n elements.push({\n type: 'date',\n text: context.sentence,\n confidence: calculateDateConfidence(dateStr, context.sentence),\n sourcePosition: context.start,\n endPosition: context.end,\n data: {\n type: 'date',\n date: dateStr,\n description: cleanDescription(context.sentence, dateStr),\n mood: detectMood(context.sentence),\n },\n });\n }\n }\n return elements;\n}\n\nfunction extractQuotes(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of QUOTE_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const quote = match.length > 2 ? match[2] : match[1];\n const attribution = match.length > 2 ? match[1] : undefined;\n if (quote.length < 15 || quote.length > 200) continue;\n\n elements.push({\n type: 'quote',\n text: match[0],\n confidence: calculateQuoteConfidence(quote),\n sourcePosition: position,\n endPosition: position + match[0].length,\n data: { type: 'quote', quote: quote.trim(), attribution: attribution?.trim() },\n });\n }\n }\n return elements;\n}\n\nfunction extractComparisons(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of COMPARISON_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n // Trim a trailing clause introduced by a comma + space (\"retired\n // airliners, arranged in rows\" → \"retired airliners\"). A greedy side\n // match often swallows the words after the compared thing; the comma is a\n // clause boundary, not part of the value. Numeric grouping commas\n // (\"4,000\") survive because those are comma+digit, not comma+space.\n const trimClause = (s: string) => s.replace(/,\\s+.*$/, '').trim();\n const left = trimClause(match[1].trim());\n const right = trimClause(match[2].trim());\n if (left.length < 2 || left.length > 30) continue;\n if (right.length < 2 || right.length > 30) continue;\n // Reject \"from X to X\"-style idioms — \"from witness to witness\",\n // \"from day to day\", \"from generation to generation\". These mean \"it\n // varied\", not \"A versus B\", and render as a pointless \"X → X\" card.\n // The signature is a word repeated across the connector: the last word\n // of the left side equals the first word of the right side. (Greedy\n // matching may append trailing context to the right side, so a whole-\n // string equality check is not enough.)\n const wordsOf = (s: string) =>\n s\n .toLowerCase()\n .split(/\\s+/)\n .map((w) => w.replace(/[^a-z0-9]+/g, ''))\n .filter(Boolean);\n const leftWords = wordsOf(left);\n const rightWords = wordsOf(right);\n if (\n leftWords.length > 0 &&\n rightWords.length > 0 &&\n leftWords[leftWords.length - 1] === rightWords[0]\n ) {\n continue;\n }\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'comparison',\n text: context.sentence,\n confidence: 0.6,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'comparison', left: { label: left }, right: { label: right } },\n });\n }\n }\n return elements;\n}\n\n/**\n * Whether the `. ! ?` at `index` is a real sentence terminator rather than a\n * period inside an abbreviation (\"George W. Tibbetts\", \"M. DeWitt\", \"Mr.\",\n * \"U.S.\"), a single-letter initial, a decimal (\"3.5\"), or an ellipsis. Naive\n * splitting on every period produces broken fragments — e.g. the standalone\n * slide \"They were met at George W\" from \"…at George W. Tibbetts' store…\".\n * Returns false for any non-terminator character so callers can scan freely.\n */\nfunction isSentenceBoundary(text: string, index: number): boolean {\n const ch = text[index];\n if (ch === '!' || ch === '?') return true;\n if (ch !== '.') return false;\n\n // Ellipsis: defer the boundary to the final dot in a run of dots.\n if (text[index + 1] === '.') return false;\n // Decimal number: digit \".\" digit → \"3.5\".\n if (/[0-9]/.test(text[index - 1] ?? '') && /[0-9]/.test(text[index + 1] ?? '')) return false;\n\n // The run of letters immediately preceding the period.\n let w = index - 1;\n while (w >= 0 && /[A-Za-z]/.test(text[w])) w--;\n const word = text.slice(w + 1, index);\n if (word.length === 1) return false; // single-letter initial: \"George W.\"\n if (word && SENTENCE_ABBREVIATIONS.has(word.toLowerCase())) return false;\n\n // A genuine sentence starts with an uppercase letter, digit, or opening\n // quote/bracket. A lowercase continuation means the period was mid-sentence\n // (an abbreviation we didn't enumerate).\n let j = index + 1;\n while (j < text.length && /\\s/.test(text[j])) j++;\n if (j >= text.length) return true; // end of text\n return !/[a-z]/.test(text[j]);\n}\n\n/**\n * Iterate over sentence-like spans in the text, yielding trimmed text and\n * accurate start/end indices within the original string.\n *\n * Boundaries are detected by isSentenceBoundary() so abbreviations, initials,\n * decimals, and ellipses do not split a sentence. Leading whitespace and\n * trailing whitespace/terminators are excluded from each span.\n */\nfunction forEachSentenceSpan(\n text: string,\n callback: (trimmed: string, start: number, end: number) => void,\n): void {\n const emit = (rawStart: number, rawEnd: number) => {\n let start = rawStart;\n while (start < rawEnd && /\\s/.test(text[start])) start++;\n let end = rawEnd;\n while (end > start && /[\\s.!?]/.test(text[end - 1])) end--;\n if (end <= start) return;\n const trimmed = text.slice(start, end);\n if (trimmed) callback(trimmed, start, end);\n };\n\n let spanStart = 0;\n let i = 0;\n while (i < text.length) {\n if (isSentenceBoundary(text, i)) {\n // Consume any consecutive terminators (\"?!\", \"…\") as one boundary.\n let end = i + 1;\n while (end < text.length && /[.!?]/.test(text[end])) end++;\n emit(spanStart, end);\n spanStart = end;\n i = end;\n } else {\n i++;\n }\n }\n if (spanStart < text.length) emit(spanStart, text.length);\n}\n\nfunction extractFacts(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n\n forEachSentenceSpan(text, (trimmed, start, end) => {\n if (trimmed.length <= 20) return;\n\n const score = scoreSentenceAsFact(trimmed);\n if (score < 0.4) return;\n\n elements.push({\n type: 'fact',\n text: trimmed,\n confidence: score,\n sourcePosition: start,\n endPosition: end,\n data: { type: 'fact', fact: trimmed },\n });\n });\n\n return elements;\n}\n\nfunction extractImpactLines(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n\n forEachSentenceSpan(text, (trimmed, start, end) => {\n if (trimmed.length < 8 || trimmed.length > 60) return;\n\n let score = 0.3;\n const lower = trimmed.toLowerCase();\n\n for (const verb of IMPACT_VERBS) {\n if (lower.startsWith(verb)) {\n score += 0.25;\n break;\n }\n }\n\n if (end < text.length && text[end] === '!') score += 0.15;\n if (trimmed.length < 30) score += 0.1;\n if (trimmed.includes(',')) score -= 0.1;\n if (/^(and|but|or|the|a|an)\\b/i.test(trimmed)) score -= 0.15;\n if (score < 0.5) return;\n\n elements.push({\n type: 'impactLine',\n text: trimmed,\n confidence: Math.min(1, score),\n sourcePosition: start,\n endPosition: end,\n data: { type: 'impactLine', text: trimmed },\n });\n });\n\n return elements;\n}\n\nfunction extractLists(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of LIST_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const listContent = match[1].trim();\n const items = listContent\n .split(/,\\s*(?:and|or)\\s+|,\\s+|\\s+and\\s+|\\s+or\\s+/)\n .map((item) => item.trim())\n .filter((item) => item.length >= 2 && item.length <= 60);\n\n if (items.length < 3 || items.length > 5) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'list',\n text: context.sentence,\n confidence: 0.65,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'list', items },\n });\n }\n }\n return elements;\n}\n\nfunction extractDefinitions(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of DEFINITION_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const term = match[1].trim();\n const definition = match[2].trim();\n if (term.length < 3 || term.length > 30) continue;\n if (definition.length < 10 || definition.length > 120) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'definition',\n text: context.sentence,\n confidence: 0.6,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'definition', term, definition },\n });\n }\n }\n return elements;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\nfunction getSentenceContext(\n text: string,\n matchStart: number,\n matchLength: number,\n): { sentence: string; start: number; end: number } {\n let start = matchStart;\n while (start > 0 && !isSentenceBoundary(text, start - 1)) start--;\n while (start < matchStart && /\\s/.test(text[start])) start++;\n\n let end = matchStart + matchLength;\n while (end < text.length && !isSentenceBoundary(text, end)) end++;\n if (end < text.length) end++;\n\n return { sentence: text.slice(start, end).trim(), start, end };\n}\n\nfunction calculateStatConfidence(value: string, context: string): number {\n let score = 0.5;\n if (value.includes('%')) score += 0.2;\n if (/million|billion|trillion/i.test(value)) score += 0.15;\n if (/increased|decreased|grew|dropped|rose|fell/i.test(context)) score += 0.15;\n if (context.length < 30) score -= 0.2;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction calculateDateConfidence(date: string, context: string): number {\n let score = 0.5;\n if (\n /January|February|March|April|May|June|July|August|September|October|November|December/i.test(\n date,\n )\n )\n score += 0.2;\n if (/founded|established|built|opened|discovered|created/i.test(context)) score += 0.2;\n if (/happened|occurred|took place|began|ended/i.test(context)) score += 0.15;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction calculateQuoteConfidence(quote: string): number {\n let score = 0.5;\n if (quote.length >= 30 && quote.length <= 150) score += 0.2;\n if (/\\b(I|we|our|my)\\b/i.test(quote)) score += 0.1;\n if (!/[.!?]$/.test(quote)) score -= 0.1;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction scoreSentenceAsFact(sentence: string): number {\n let score = 0.3;\n const lower = sentence.toLowerCase();\n for (const indicator of FACT_INDICATORS) {\n if (lower.includes(indicator)) {\n score += 0.1;\n break;\n }\n }\n const properNouns = sentence.match(/\\b[A-Z][a-z]+\\b/g);\n if (properNouns && properNouns.length >= 2) score += 0.15;\n if (/\\d/.test(sentence)) score += 0.1;\n if (sentence.endsWith('?')) score -= 0.3;\n if (sentence.length > 200) score -= 0.15;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction detectMood(sentence: string): 'neutral' | 'somber' | 'celebratory' {\n const lower = sentence.toLowerCase();\n for (const word of SOMBER_INDICATORS) {\n if (lower.includes(word)) return 'somber';\n }\n for (const word of CELEBRATORY_INDICATORS) {\n if (lower.includes(word)) return 'celebratory';\n }\n return 'neutral';\n}\n\nfunction cleanDescription(sentence: string, value: string): string {\n const prepositionPatterns = [\n new RegExp(`\\\\b[Ii]n\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Oo]n\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Dd]uring\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Bb]y\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Ss]ince\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Ff]rom\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n ];\n\n let desc = sentence;\n for (const pattern of prepositionPatterns) {\n desc = desc.replace(pattern, '');\n }\n if (desc === sentence) {\n desc = sentence.replace(value, '').trim();\n }\n\n desc = desc.replace(/^[,:\\-–—]+\\s*/, '').replace(/\\s*[,:\\-–—]+$/, '');\n desc = desc.replace(\n /\\s+(?:of|on|in|at|by|to|for|from|with|about|over|under|between|than|into|onto|upon)\\s*([,.])/gi,\n '$1',\n );\n desc = desc.replace(\n /\\s+(?:for|of|to|in|at|by|with|from|about|over|under|between|than|into|onto|upon)\\s*[.!?]?\\s*$/i,\n '.',\n );\n desc = desc.replace(/\\s{2,}/g, ' ').replace(/\\s+([,.])/g, '$1');\n desc = desc.charAt(0).toUpperCase() + desc.slice(1);\n return desc.trim();\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nconst TYPE_SPECIFICITY: Record<ExtractionType, number> = {\n stat: 5,\n date: 4,\n quote: 3,\n definition: 3,\n comparison: 2,\n list: 2,\n impactLine: 2,\n fact: 1,\n};\n\nfunction deduplicateOverlapping(elements: ExtractedElement[]): ExtractedElement[] {\n if (elements.length <= 1) return elements;\n\n const toRemove = new Set<number>();\n\n for (let i = 0; i < elements.length; i++) {\n if (toRemove.has(i)) continue;\n const a = elements[i];\n\n for (let j = i + 1; j < elements.length; j++) {\n if (toRemove.has(j)) continue;\n const b = elements[j];\n\n if (elementsOverlap(a, b)) {\n const aSpec = TYPE_SPECIFICITY[a.type];\n const bSpec = TYPE_SPECIFICITY[b.type];\n if (aSpec >= bSpec) {\n toRemove.add(j);\n } else {\n toRemove.add(i);\n break;\n }\n }\n }\n }\n\n return elements.filter((_, index) => !toRemove.has(index));\n}\n\nfunction elementsOverlap(a: ExtractedElement, b: ExtractedElement): boolean {\n if (a.sourcePosition === b.sourcePosition) return true;\n if (a.sourcePosition <= b.sourcePosition && a.endPosition >= b.endPosition) return true;\n if (b.sourcePosition <= a.sourcePosition && b.endPosition >= a.endPosition) return true;\n\n const overlapStart = Math.max(a.sourcePosition, b.sourcePosition);\n const overlapEnd = Math.min(a.endPosition, b.endPosition);\n if (overlapEnd > overlapStart) {\n const overlapLength = overlapEnd - overlapStart;\n const minLength = Math.min(a.endPosition - a.sourcePosition, b.endPosition - b.sourcePosition);\n if (overlapLength > minLength * 0.5) return true;\n }\n\n return false;\n}\n\n/**\n * Strip markdown formatting from text, returning plain prose.\n * Call this before `extractContent` if the input is markdown.\n */\nexport function stripMarkdown(markdown: string): string {\n return (\n markdown\n // Remove code blocks (before inline code)\n .replace(/```[\\s\\S]*?```/g, '')\n // Remove images (before links — `![` prefix distinguishes them)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '')\n // Remove links, keeping text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '$1')\n .replace(/\\*([^*]+)\\*/g, '$1')\n .replace(/__([^_]+)__/g, '$1')\n .replace(/_([^_]+)_/g, '$1')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/^>\\s+/gm, '')\n .replace(/^[-*_]{3,}$/gm, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n );\n}\n","/**\n * Template Mapper\n *\n * Converts extracted content elements into squisq TemplateBlock inputs.\n * Each ExtractionType maps to a specific template from BlockTemplates.\n */\n\nimport type {\n ExtractedElement,\n StatData,\n DateData,\n QuoteData,\n ComparisonData,\n FactData,\n ImpactLineData,\n ListData,\n DefinitionData,\n} from './contentExtractor.js';\n\nimport type {\n StatHighlightInput,\n QuoteBlockInput,\n FactCardInput,\n TwoColumnInput,\n DateEventInput,\n FullBleedQuoteInput,\n ListBlockInput,\n DefinitionCardInput,\n TemplateBlock,\n ColorScheme,\n AccentImage,\n} from '../schemas/BlockTemplates.js';\n\n/** Options for mapping an element to a template block. */\nexport interface MapOptions {\n id: string;\n duration: number;\n audioSegment: number;\n colorScheme?: ColorScheme;\n accentImage?: AccentImage;\n sourceStartTime?: number;\n sourceDuration?: number;\n}\n\n/**\n * Convert an extracted element into the matching squisq TemplateBlock.\n */\nexport function mapElementToBlock(element: ExtractedElement, options: MapOptions): TemplateBlock {\n const base = {\n id: options.id,\n duration: options.duration,\n audioSegment: options.audioSegment,\n sourceStartTime: options.sourceStartTime,\n sourceDuration: options.sourceDuration,\n };\n\n switch (element.data.type) {\n case 'stat':\n return mapStat(element.data, base, options);\n case 'date':\n return mapDate(element.data, base, options);\n case 'quote':\n return mapQuote(element.data, base, options);\n case 'comparison':\n return mapComparison(element.data, base, options);\n case 'fact':\n return mapFact(element.data, base, options);\n case 'impactLine':\n return mapImpactLine(element.data, base, options);\n case 'list':\n return mapList(element.data, base, options);\n case 'definition':\n return mapDefinition(element.data, base, options);\n }\n}\n\n// ── Per-type mappers ───────────────────────────────────────────────\n\ntype Base = {\n id: string;\n duration: number;\n audioSegment: number;\n sourceStartTime?: number;\n sourceDuration?: number;\n};\n\nfunction mapStat(data: StatData, base: Base, opts: MapOptions): StatHighlightInput {\n return {\n ...base,\n template: 'statHighlight',\n stat: data.value,\n description: data.description,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapDate(data: DateData, base: Base, opts: MapOptions): DateEventInput {\n return {\n ...base,\n template: 'dateEvent',\n date: data.date,\n description: data.description,\n mood: data.mood,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapQuote(data: QuoteData, base: Base, opts: MapOptions): QuoteBlockInput {\n return {\n ...base,\n template: 'quote',\n quote: data.quote,\n attribution: data.attribution,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapComparison(data: ComparisonData, base: Base, opts: MapOptions): TwoColumnInput {\n return {\n ...base,\n template: 'twoColumn',\n left: data.left,\n right: data.right,\n header: data.header,\n leftColor: opts.colorScheme,\n };\n}\n\nfunction mapFact(data: FactData, base: Base, opts: MapOptions): FactCardInput {\n return {\n ...base,\n template: 'factCard',\n fact: data.fact,\n explanation: data.explanation ?? '',\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapImpactLine(data: ImpactLineData, base: Base, opts: MapOptions): FullBleedQuoteInput {\n return {\n ...base,\n template: 'fullBleedQuote',\n text: data.text,\n colorScheme: opts.colorScheme,\n };\n}\n\nfunction mapList(data: ListData, base: Base, opts: MapOptions): ListBlockInput {\n return {\n ...base,\n template: 'list',\n items: data.items,\n title: data.title,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapDefinition(data: DefinitionData, base: Base, opts: MapOptions): DefinitionCardInput {\n return {\n ...base,\n template: 'definitionCard',\n term: data.term,\n definition: data.definition,\n origin: data.origin,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/generate/contentExtractor.ts","../src/generate/templateMapper.ts"],"sourcesContent":["/**\n * Content Extractor\n *\n * Pattern-based extraction of compelling content from plain text.\n * Identifies statistics, dates, quotes, comparisons, facts, impact lines,\n * lists, and definitions that can be turned into slideshow slides.\n *\n * Extraction is purely regex-based — no AI calls, no runtime costs,\n * deterministic and reproducible.\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\n/** Kinds of content the extractor recognises. */\nexport type ExtractionType =\n | 'stat'\n | 'date'\n | 'quote'\n | 'comparison'\n | 'fact'\n | 'impactLine'\n | 'list'\n | 'definition';\n\n/** An extracted content element with source position. */\nexport interface ExtractedElement {\n type: ExtractionType;\n /** Full extracted text (usually the surrounding sentence). */\n text: string;\n /** Confidence score 0–1. */\n confidence: number;\n /** Character offset in the source text. */\n sourcePosition: number;\n /** Character offset where element ends. */\n endPosition: number;\n /** Type-specific structured payload. */\n data:\n | StatData\n | DateData\n | QuoteData\n | ComparisonData\n | FactData\n | ImpactLineData\n | ListData\n | DefinitionData;\n}\n\nexport interface StatData {\n type: 'stat';\n value: string;\n unit?: string;\n description: string;\n}\n\nexport interface DateData {\n type: 'date';\n date: string;\n description: string;\n mood?: 'neutral' | 'somber' | 'celebratory';\n}\n\nexport interface QuoteData {\n type: 'quote';\n quote: string;\n attribution?: string;\n}\n\nexport interface ComparisonData {\n type: 'comparison';\n left: { label: string; sublabel?: string };\n right: { label: string; sublabel?: string };\n header?: string;\n}\n\nexport interface FactData {\n type: 'fact';\n fact: string;\n explanation?: string;\n}\n\nexport interface ImpactLineData {\n type: 'impactLine';\n text: string;\n}\n\nexport interface ListData {\n type: 'list';\n items: string[];\n title?: string;\n}\n\nexport interface DefinitionData {\n type: 'definition';\n term: string;\n definition: string;\n origin?: string;\n}\n\n/** Options for `extractContent`. */\nexport interface ExtractionOptions {\n /** Minimum confidence threshold (default: 0.3). */\n minConfidence?: number;\n /** Which types to extract (default: all). */\n types?: ExtractionType[];\n /** Maximum extractions per type (default: unlimited). */\n maxPerType?: number;\n}\n\n/** Result of `extractContent`. */\nexport interface ExtractionResult {\n elements: ExtractedElement[];\n sourceLength: number;\n stats: {\n statCount: number;\n dateCount: number;\n quoteCount: number;\n comparisonCount: number;\n factCount: number;\n impactLineCount: number;\n listCount: number;\n definitionCount: number;\n totalCount: number;\n };\n}\n\n// ── Patterns ───────────────────────────────────────────────────────\n\nconst STAT_PATTERNS = [\n /\\b(roughly |about |approximately |nearly |over |under |more than |less than )?([\\d,]+(?:\\.\\d+)?)\\s*%/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(million|billion|trillion|thousand|hundred)\\b/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(miles?|feet|ft|meters?|m|kilometers?|km|acres?|square miles?|sq mi|hectares?)\\b/gi,\n /\\b([\\d,]+(?:\\.\\d+)?)\\s*(x|times)\\b/gi,\n /\\b([$€£¥])\\s*([\\d,]+(?:\\.\\d+)?)\\s*(million|billion|thousand)?\\b/gi,\n /\\b(\\d+)(st|nd|rd|th)\\s+(largest|smallest|oldest|newest|most|least)/gi,\n];\n\nconst DATE_PATTERNS = [\n /\\b(January|February|March|April|May|June|July|August|September|October|November|December)\\s+(\\d{1,2},?\\s+)?(\\d{4})\\b/gi,\n /\\b(\\d{4})s\\b|\\b(\\d{1,2})(st|nd|rd|th)\\s+century\\b/gi,\n /\\b(from\\s+)?(\\d{4})\\s*(to|–|-|through)\\s*(\\d{4})\\b/gi,\n /\\b(in|by|since|after|before|during)\\s+(\\d{4})\\b/gi,\n];\n\nconst QUOTE_PATTERNS = [\n /\"([^\"]{15,200})\"/g,\n /\\u201c([^\\u201d]{15,200})\\u201d/g,\n /(\\w+(?:\\s+\\w+)?)\\s+(?:said|stated|wrote|noted|observed|commented),?\\s*\"([^\"]+)\"/gi,\n];\n\n// A single side of a comparison (\"X\" in \"from X to Y\"). Deliberately excludes\n// sentence/clause terminators (. ! ? ;) and newlines so a capture group can\n// never swallow text from an adjacent sentence. Without this, greedy `.{2,30}`\n// matches across a period — e.g. \"…contradicted from witness to witness.\n// Robertson said…\" yields right=\"witness. Robertson\", producing a nonsensical\n// \"witness → witness. Robertson\" slide. Commas and colons are allowed (ranges\n// like \"9:00 to 17:00\", ratios); downstream label truncation trims commas.\nconst COMPARISON_SIDE = '[^.!?;\\\\r\\\\n]{2,30}';\n\nconst COMPARISON_PATTERNS = [\n new RegExp(`from\\\\s+(${COMPARISON_SIDE})\\\\s+to\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n new RegExp(`(${COMPARISON_SIDE})\\\\s+(?:vs\\\\.?|versus)\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n new RegExp(`(${COMPARISON_SIDE})\\\\s+compared\\\\s+to\\\\s+(${COMPARISON_SIDE})`, 'gi'),\n];\n\n// Words whose trailing period is an abbreviation, not a sentence end. Used by\n// isSentenceBoundary() so names like \"George W. Tibbetts\" or \"Quong Chong & Co.\"\n// don't get split into fragments. Matched only when the entire preceding word\n// equals one of these (case-insensitive), so \"canal.\" / \"taco.\" are unaffected.\nconst SENTENCE_ABBREVIATIONS = new Set([\n 'mr',\n 'mrs',\n 'ms',\n 'dr',\n 'prof',\n 'st',\n 'mt',\n 'jr',\n 'sr',\n 'gen',\n 'col',\n 'capt',\n 'lt',\n 'sgt',\n 'rev',\n 'hon',\n 'sen',\n 'rep',\n 'gov',\n 'pres',\n 'vs',\n 'etc',\n 'inc',\n 'co',\n 'ltd',\n 'corp',\n 'al',\n 'fig',\n 'eg',\n 'ie',\n 'approx',\n 'dept',\n 'univ',\n 'assn',\n 'bros',\n 'ave',\n 'blvd',\n 'rd',\n]);\n\nconst FACT_INDICATORS = [\n 'largest',\n 'smallest',\n 'oldest',\n 'newest',\n 'first',\n 'last',\n 'most',\n 'least',\n 'only',\n 'unique',\n 'rare',\n 'famous',\n 'important',\n 'significant',\n 'notable',\n 'remarkable',\n 'extraordinary',\n 'crucial',\n 'essential',\n 'critical',\n 'major',\n 'key',\n 'discovered',\n 'founded',\n 'established',\n 'created',\n 'built',\n 'invented',\n 'developed',\n 'introduced',\n 'pioneered',\n 'transformed',\n 'revolutionized',\n 'changed',\n 'shaped',\n];\n\nconst SOMBER_INDICATORS = [\n 'disaster',\n 'tragedy',\n 'death',\n 'died',\n 'killed',\n 'destroyed',\n 'devastated',\n 'collapsed',\n 'lost',\n 'abandoned',\n 'declined',\n 'war',\n 'battle',\n 'conflict',\n 'earthquake',\n 'fire',\n 'flood',\n];\n\nconst CELEBRATORY_INDICATORS = [\n 'celebration',\n 'victory',\n 'success',\n 'achievement',\n 'triumph',\n 'opened',\n 'completed',\n 'inaugurated',\n 'launched',\n 'won',\n 'record',\n 'breakthrough',\n 'milestone',\n 'golden',\n 'grand',\n];\n\nconst IMPACT_VERBS = [\n 'check',\n 'watch',\n 'stop',\n 'prepare',\n 'imagine',\n 'consider',\n 'look',\n 'listen',\n 'notice',\n 'remember',\n 'discover',\n 'explore',\n 'visit',\n 'try',\n 'experience',\n 'avoid',\n 'beware',\n 'enjoy',\n];\n\nconst LIST_PATTERNS = [\n /(?:including|such as|namely|like|features?|offers?)\\s+([^.]{10,150})/gi,\n /(?:three|four|five|six|several|many|various)\\s+(?:main|key|primary|notable|popular|important)?\\s*\\w+(?:\\s+\\w+)?:\\s*([^.]{10,150})/gi,\n];\n\nconst DEFINITION_PATTERNS = [\n /\\b([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3})\\s+is\\s+(?:a|an|the)\\s+([^.]{10,120})/g,\n /known\\s+as\\s+([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3}),\\s+([^.]{10,120})/gi,\n /\\b([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3})\\s+refers?\\s+to\\s+([^.]{10,120})/g,\n /called\\s+([A-Z][a-z]+(?:\\s+[A-Z]?[a-z]+){0,3}),?\\s+(?:which|meaning|a)\\s+([^.]{10,120})/gi,\n];\n\n// ── Main ───────────────────────────────────────────────────────────\n\n/**\n * Extract compelling content from plain text.\n *\n * Call `stripMarkdown(text)` first if the input contains markdown.\n */\nexport function extractContent(text: string, options: ExtractionOptions = {}): ExtractionResult {\n const {\n minConfidence = 0.3,\n types = ['stat', 'date', 'quote', 'comparison', 'fact', 'impactLine', 'list', 'definition'],\n maxPerType,\n } = options;\n\n const elements: ExtractedElement[] = [];\n\n if (types.includes('stat')) elements.push(...extractStats(text));\n if (types.includes('date')) elements.push(...extractDates(text));\n if (types.includes('quote')) elements.push(...extractQuotes(text));\n if (types.includes('comparison')) elements.push(...extractComparisons(text));\n if (types.includes('fact')) elements.push(...extractFacts(text));\n if (types.includes('impactLine')) elements.push(...extractImpactLines(text));\n if (types.includes('list')) elements.push(...extractLists(text));\n if (types.includes('definition')) elements.push(...extractDefinitions(text));\n\n let filtered = elements.filter((e) => e.confidence >= minConfidence);\n\n if (maxPerType !== undefined) {\n const byType = new Map<ExtractionType, ExtractedElement[]>();\n for (const elem of filtered) {\n const list = byType.get(elem.type) || [];\n list.push(elem);\n byType.set(elem.type, list);\n }\n filtered = [];\n for (const [, list] of byType) {\n list.sort((a, b) => b.confidence - a.confidence);\n filtered.push(...list.slice(0, maxPerType));\n }\n }\n\n filtered.sort((a, b) => a.sourcePosition - b.sourcePosition);\n filtered = deduplicateOverlapping(filtered);\n\n return {\n elements: filtered,\n sourceLength: text.length,\n stats: {\n statCount: filtered.filter((e) => e.type === 'stat').length,\n dateCount: filtered.filter((e) => e.type === 'date').length,\n quoteCount: filtered.filter((e) => e.type === 'quote').length,\n comparisonCount: filtered.filter((e) => e.type === 'comparison').length,\n factCount: filtered.filter((e) => e.type === 'fact').length,\n impactLineCount: filtered.filter((e) => e.type === 'impactLine').length,\n listCount: filtered.filter((e) => e.type === 'list').length,\n definitionCount: filtered.filter((e) => e.type === 'definition').length,\n totalCount: filtered.length,\n },\n };\n}\n\n// ── Per-type extractors ────────────────────────────────────────────\n\nfunction extractStats(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of STAT_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const context = getSentenceContext(text, position, match[0].length);\n const value = match[0].trim();\n\n elements.push({\n type: 'stat',\n text: context.sentence,\n confidence: calculateStatConfidence(value, context.sentence),\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'stat', value, description: cleanDescription(context.sentence, value) },\n });\n }\n }\n return elements;\n}\n\nfunction extractDates(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of DATE_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const context = getSentenceContext(text, position, match[0].length);\n const dateStr = match[0].trim();\n\n elements.push({\n type: 'date',\n text: context.sentence,\n confidence: calculateDateConfidence(dateStr, context.sentence),\n sourcePosition: context.start,\n endPosition: context.end,\n data: {\n type: 'date',\n date: dateStr,\n description: cleanDescription(context.sentence, dateStr),\n mood: detectMood(context.sentence),\n },\n });\n }\n }\n return elements;\n}\n\nfunction extractQuotes(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of QUOTE_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const quote = match.length > 2 ? match[2] : match[1];\n const attribution = match.length > 2 ? match[1] : undefined;\n if (quote.length < 15 || quote.length > 200) continue;\n\n elements.push({\n type: 'quote',\n text: match[0],\n confidence: calculateQuoteConfidence(quote),\n sourcePosition: position,\n endPosition: position + match[0].length,\n data: { type: 'quote', quote: quote.trim(), attribution: attribution?.trim() },\n });\n }\n }\n return elements;\n}\n\nfunction extractComparisons(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of COMPARISON_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n // Trim a trailing clause introduced by a comma + space (\"retired\n // airliners, arranged in rows\" → \"retired airliners\"). A greedy side\n // match often swallows the words after the compared thing; the comma is a\n // clause boundary, not part of the value. Numeric grouping commas\n // (\"4,000\") survive because those are comma+digit, not comma+space.\n const trimClause = (s: string) => s.replace(/,\\s+.*$/, '').trim();\n const left = trimClause(match[1].trim());\n const right = trimClause(match[2].trim());\n if (left.length < 2 || left.length > 30) continue;\n if (right.length < 2 || right.length > 30) continue;\n // Reject \"from X to X\"-style idioms — \"from witness to witness\",\n // \"from day to day\", \"from generation to generation\". These mean \"it\n // varied\", not \"A versus B\", and render as a pointless \"X → X\" card.\n // The signature is a word repeated across the connector: the last word\n // of the left side equals the first word of the right side. (Greedy\n // matching may append trailing context to the right side, so a whole-\n // string equality check is not enough.)\n const wordsOf = (s: string) =>\n s\n .toLowerCase()\n .split(/\\s+/)\n .map((w) => w.replace(/[^a-z0-9]+/g, ''))\n .filter(Boolean);\n const leftWords = wordsOf(left);\n const rightWords = wordsOf(right);\n if (\n leftWords.length > 0 &&\n rightWords.length > 0 &&\n leftWords[leftWords.length - 1] === rightWords[0]\n ) {\n continue;\n }\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'comparison',\n text: context.sentence,\n confidence: 0.6,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'comparison', left: { label: left }, right: { label: right } },\n });\n }\n }\n return elements;\n}\n\n/**\n * Whether the `. ! ?` at `index` is a real sentence terminator rather than a\n * period inside an abbreviation (\"George W. Tibbetts\", \"M. DeWitt\", \"Mr.\",\n * \"U.S.\"), a single-letter initial, a decimal (\"3.5\"), or an ellipsis. Naive\n * splitting on every period produces broken fragments — e.g. the standalone\n * slide \"They were met at George W\" from \"…at George W. Tibbetts' store…\".\n * Returns false for any non-terminator character so callers can scan freely.\n */\nfunction isSentenceBoundary(text: string, index: number): boolean {\n const ch = text[index];\n if (ch === '!' || ch === '?') return true;\n if (ch !== '.') return false;\n\n // Ellipsis: defer the boundary to the final dot in a run of dots.\n if (text[index + 1] === '.') return false;\n // Decimal number: digit \".\" digit → \"3.5\".\n if (/[0-9]/.test(text[index - 1] ?? '') && /[0-9]/.test(text[index + 1] ?? '')) return false;\n\n // The run of letters immediately preceding the period.\n let w = index - 1;\n while (w >= 0 && /[A-Za-z]/.test(text[w])) w--;\n const word = text.slice(w + 1, index);\n if (word.length === 1) return false; // single-letter initial: \"George W.\"\n if (word && SENTENCE_ABBREVIATIONS.has(word.toLowerCase())) return false;\n\n // A genuine sentence starts with an uppercase letter, digit, or opening\n // quote/bracket. A lowercase continuation means the period was mid-sentence\n // (an abbreviation we didn't enumerate).\n let j = index + 1;\n while (j < text.length && /\\s/.test(text[j])) j++;\n if (j >= text.length) return true; // end of text\n return !/[a-z]/.test(text[j]);\n}\n\n/**\n * Iterate over sentence-like spans in the text, yielding trimmed text and\n * accurate start/end indices within the original string.\n *\n * Boundaries are detected by isSentenceBoundary() so abbreviations, initials,\n * decimals, and ellipses do not split a sentence. Leading whitespace and\n * trailing whitespace/terminators are excluded from each span.\n */\nfunction forEachSentenceSpan(\n text: string,\n callback: (trimmed: string, start: number, end: number) => void,\n): void {\n const emit = (rawStart: number, rawEnd: number) => {\n let start = rawStart;\n while (start < rawEnd && /\\s/.test(text[start])) start++;\n let end = rawEnd;\n while (end > start && /[\\s.!?]/.test(text[end - 1])) end--;\n if (end <= start) return;\n const trimmed = text.slice(start, end);\n if (trimmed) callback(trimmed, start, end);\n };\n\n let spanStart = 0;\n let i = 0;\n while (i < text.length) {\n if (isSentenceBoundary(text, i)) {\n // Consume any consecutive terminators (\"?!\", \"…\") as one boundary.\n let end = i + 1;\n while (end < text.length && /[.!?]/.test(text[end])) end++;\n emit(spanStart, end);\n spanStart = end;\n i = end;\n } else {\n i++;\n }\n }\n if (spanStart < text.length) emit(spanStart, text.length);\n}\n\nfunction extractFacts(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n\n forEachSentenceSpan(text, (trimmed, start, end) => {\n if (trimmed.length <= 20) return;\n\n const score = scoreSentenceAsFact(trimmed);\n if (score < 0.4) return;\n\n elements.push({\n type: 'fact',\n text: trimmed,\n confidence: score,\n sourcePosition: start,\n endPosition: end,\n data: { type: 'fact', fact: trimmed },\n });\n });\n\n return elements;\n}\n\nfunction extractImpactLines(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n\n forEachSentenceSpan(text, (trimmed, start, end) => {\n if (trimmed.length < 8 || trimmed.length > 60) return;\n\n let score = 0.3;\n const lower = trimmed.toLowerCase();\n\n for (const verb of IMPACT_VERBS) {\n if (lower.startsWith(verb)) {\n score += 0.25;\n break;\n }\n }\n\n if (end < text.length && text[end] === '!') score += 0.15;\n if (trimmed.length < 30) score += 0.1;\n if (trimmed.includes(',')) score -= 0.1;\n if (/^(and|but|or|the|a|an)\\b/i.test(trimmed)) score -= 0.15;\n if (score < 0.5) return;\n\n elements.push({\n type: 'impactLine',\n text: trimmed,\n confidence: Math.min(1, score),\n sourcePosition: start,\n endPosition: end,\n data: { type: 'impactLine', text: trimmed },\n });\n });\n\n return elements;\n}\n\nfunction extractLists(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of LIST_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const listContent = match[1].trim();\n const items = listContent\n .split(/,\\s*(?:and|or)\\s+|,\\s+|\\s+and\\s+|\\s+or\\s+/)\n .map((item) => item.trim())\n .filter((item) => item.length >= 2 && item.length <= 60);\n\n if (items.length < 3 || items.length > 5) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'list',\n text: context.sentence,\n confidence: 0.65,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'list', items },\n });\n }\n }\n return elements;\n}\n\nfunction extractDefinitions(text: string): ExtractedElement[] {\n const elements: ExtractedElement[] = [];\n const seen = new Set<number>();\n\n for (const pattern of DEFINITION_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const position = match.index;\n if (seen.has(position)) continue;\n seen.add(position);\n\n const term = match[1].trim();\n const definition = match[2].trim();\n if (term.length < 3 || term.length > 30) continue;\n if (definition.length < 10 || definition.length > 120) continue;\n\n const context = getSentenceContext(text, position, match[0].length);\n\n elements.push({\n type: 'definition',\n text: context.sentence,\n confidence: 0.6,\n sourcePosition: context.start,\n endPosition: context.end,\n data: { type: 'definition', term, definition },\n });\n }\n }\n return elements;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\nfunction getSentenceContext(\n text: string,\n matchStart: number,\n matchLength: number,\n): { sentence: string; start: number; end: number } {\n let start = matchStart;\n while (start > 0 && !isSentenceBoundary(text, start - 1)) start--;\n while (start < matchStart && /\\s/.test(text[start])) start++;\n\n let end = matchStart + matchLength;\n while (end < text.length && !isSentenceBoundary(text, end)) end++;\n if (end < text.length) end++;\n\n return { sentence: text.slice(start, end).trim(), start, end };\n}\n\nfunction calculateStatConfidence(value: string, context: string): number {\n let score = 0.5;\n if (value.includes('%')) score += 0.2;\n if (/million|billion|trillion/i.test(value)) score += 0.15;\n if (/increased|decreased|grew|dropped|rose|fell/i.test(context)) score += 0.15;\n if (context.length < 30) score -= 0.2;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction calculateDateConfidence(date: string, context: string): number {\n let score = 0.5;\n if (\n /January|February|March|April|May|June|July|August|September|October|November|December/i.test(\n date,\n )\n )\n score += 0.2;\n if (/founded|established|built|opened|discovered|created/i.test(context)) score += 0.2;\n if (/happened|occurred|took place|began|ended/i.test(context)) score += 0.15;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction calculateQuoteConfidence(quote: string): number {\n let score = 0.5;\n if (quote.length >= 30 && quote.length <= 150) score += 0.2;\n if (/\\b(I|we|our|my)\\b/i.test(quote)) score += 0.1;\n if (!/[.!?]$/.test(quote)) score -= 0.1;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction scoreSentenceAsFact(sentence: string): number {\n let score = 0.3;\n const lower = sentence.toLowerCase();\n for (const indicator of FACT_INDICATORS) {\n if (lower.includes(indicator)) {\n score += 0.1;\n break;\n }\n }\n const properNouns = sentence.match(/\\b[A-Z][a-z]+\\b/g);\n if (properNouns && properNouns.length >= 2) score += 0.15;\n if (/\\d/.test(sentence)) score += 0.1;\n if (sentence.endsWith('?')) score -= 0.3;\n if (sentence.length > 200) score -= 0.15;\n return Math.min(1, Math.max(0, score));\n}\n\nfunction detectMood(sentence: string): 'neutral' | 'somber' | 'celebratory' {\n const lower = sentence.toLowerCase();\n for (const word of SOMBER_INDICATORS) {\n if (lower.includes(word)) return 'somber';\n }\n for (const word of CELEBRATORY_INDICATORS) {\n if (lower.includes(word)) return 'celebratory';\n }\n return 'neutral';\n}\n\nfunction cleanDescription(sentence: string, value: string): string {\n const prepositionPatterns = [\n new RegExp(`\\\\b[Ii]n\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Oo]n\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Dd]uring\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Bb]y\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Ss]ince\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n new RegExp(`\\\\b[Ff]rom\\\\s+${escapeRegex(value)}[,.]?\\\\s*`, 'g'),\n ];\n\n let desc = sentence;\n for (const pattern of prepositionPatterns) {\n desc = desc.replace(pattern, '');\n }\n if (desc === sentence) {\n desc = sentence.replace(value, '').trim();\n }\n\n desc = desc.replace(/^[,:\\-–—]+\\s*/, '').replace(/\\s*[,:\\-–—]+$/, '');\n desc = desc.replace(\n /\\s+(?:of|on|in|at|by|to|for|from|with|about|over|under|between|than|into|onto|upon)\\s*([,.])/gi,\n '$1',\n );\n desc = desc.replace(\n /\\s+(?:for|of|to|in|at|by|with|from|about|over|under|between|than|into|onto|upon)\\s*[.!?]?\\s*$/i,\n '.',\n );\n desc = desc.replace(/\\s{2,}/g, ' ').replace(/\\s+([,.])/g, '$1');\n desc = desc.charAt(0).toUpperCase() + desc.slice(1);\n return desc.trim();\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nconst TYPE_SPECIFICITY: Record<ExtractionType, number> = {\n stat: 5,\n date: 4,\n quote: 3,\n definition: 3,\n comparison: 2,\n list: 2,\n impactLine: 2,\n fact: 1,\n};\n\nfunction deduplicateOverlapping(elements: ExtractedElement[]): ExtractedElement[] {\n if (elements.length <= 1) return elements;\n\n const toRemove = new Set<number>();\n\n for (let i = 0; i < elements.length; i++) {\n if (toRemove.has(i)) continue;\n const a = elements[i];\n\n for (let j = i + 1; j < elements.length; j++) {\n if (toRemove.has(j)) continue;\n const b = elements[j];\n\n if (elementsOverlap(a, b)) {\n const aSpec = TYPE_SPECIFICITY[a.type];\n const bSpec = TYPE_SPECIFICITY[b.type];\n if (aSpec >= bSpec) {\n toRemove.add(j);\n } else {\n toRemove.add(i);\n break;\n }\n }\n }\n }\n\n return elements.filter((_, index) => !toRemove.has(index));\n}\n\nfunction elementsOverlap(a: ExtractedElement, b: ExtractedElement): boolean {\n if (a.sourcePosition === b.sourcePosition) return true;\n if (a.sourcePosition <= b.sourcePosition && a.endPosition >= b.endPosition) return true;\n if (b.sourcePosition <= a.sourcePosition && b.endPosition >= a.endPosition) return true;\n\n const overlapStart = Math.max(a.sourcePosition, b.sourcePosition);\n const overlapEnd = Math.min(a.endPosition, b.endPosition);\n if (overlapEnd > overlapStart) {\n const overlapLength = overlapEnd - overlapStart;\n const minLength = Math.min(a.endPosition - a.sourcePosition, b.endPosition - b.sourcePosition);\n if (overlapLength > minLength * 0.5) return true;\n }\n\n return false;\n}\n\n/**\n * Strip markdown formatting from text, returning plain prose.\n * Call this before `extractContent` if the input is markdown.\n */\nexport function stripMarkdown(markdown: string): string {\n return (\n markdown\n // Remove code blocks (before inline code)\n .replace(/```[\\s\\S]*?```/g, '')\n // Remove images (before links — `![` prefix distinguishes them)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '')\n // Remove links, keeping text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '$1')\n .replace(/\\*([^*]+)\\*/g, '$1')\n .replace(/__([^_]+)__/g, '$1')\n .replace(/_([^_]+)_/g, '$1')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/^>\\s+/gm, '')\n .replace(/^[-*_]{3,}$/gm, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n );\n}\n","/**\n * Template Mapper\n *\n * Converts extracted content elements into squisq TemplateBlock inputs.\n * Each ExtractionType maps to a specific template from BlockTemplates.\n */\n\nimport type {\n ExtractedElement,\n StatData,\n DateData,\n QuoteData,\n ComparisonData,\n FactData,\n ImpactLineData,\n ListData,\n DefinitionData,\n} from './contentExtractor.js';\n\nimport type {\n StatHighlightInput,\n QuoteBlockInput,\n FactCardInput,\n TwoColumnInput,\n DateEventInput,\n FullBleedQuoteInput,\n ListBlockInput,\n DefinitionCardInput,\n TemplateBlock,\n ColorScheme,\n AccentImage,\n} from '../schemas/BlockTemplates.js';\n\n/** Options for mapping an element to a template block. */\nexport interface MapOptions {\n id: string;\n duration: number;\n audioSegment: number;\n colorScheme?: ColorScheme;\n accentImage?: AccentImage;\n /** Doc-timeline SECONDS when the extracted content is spoken. */\n sourceStartTime?: number;\n sourceDuration?: number;\n /** Provenance: the source block the element was extracted from. */\n sourceBlockId?: string;\n /** Provenance: char offset of the extraction in the source plain text. */\n sourceCharOffset?: number;\n}\n\n/**\n * Convert an extracted element into the matching squisq TemplateBlock.\n */\nexport function mapElementToBlock(element: ExtractedElement, options: MapOptions): TemplateBlock {\n const base = {\n id: options.id,\n duration: options.duration,\n audioSegment: options.audioSegment,\n sourceStartTime: options.sourceStartTime,\n sourceDuration: options.sourceDuration,\n sourceBlockId: options.sourceBlockId,\n sourceCharOffset: options.sourceCharOffset,\n };\n\n switch (element.data.type) {\n case 'stat':\n return mapStat(element.data, base, options);\n case 'date':\n return mapDate(element.data, base, options);\n case 'quote':\n return mapQuote(element.data, base, options);\n case 'comparison':\n return mapComparison(element.data, base, options);\n case 'fact':\n return mapFact(element.data, base, options);\n case 'impactLine':\n return mapImpactLine(element.data, base, options);\n case 'list':\n return mapList(element.data, base, options);\n case 'definition':\n return mapDefinition(element.data, base, options);\n }\n}\n\n// ── Per-type mappers ───────────────────────────────────────────────\n\ntype Base = {\n id: string;\n duration: number;\n audioSegment: number;\n sourceStartTime?: number;\n sourceDuration?: number;\n};\n\nfunction mapStat(data: StatData, base: Base, opts: MapOptions): StatHighlightInput {\n return {\n ...base,\n template: 'statHighlight',\n stat: data.value,\n description: data.description,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapDate(data: DateData, base: Base, opts: MapOptions): DateEventInput {\n return {\n ...base,\n template: 'dateEvent',\n date: data.date,\n description: data.description,\n mood: data.mood,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapQuote(data: QuoteData, base: Base, opts: MapOptions): QuoteBlockInput {\n return {\n ...base,\n template: 'quote',\n quote: data.quote,\n attribution: data.attribution,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapComparison(data: ComparisonData, base: Base, opts: MapOptions): TwoColumnInput {\n return {\n ...base,\n template: 'twoColumn',\n left: data.left,\n right: data.right,\n header: data.header,\n leftColor: opts.colorScheme,\n };\n}\n\nfunction mapFact(data: FactData, base: Base, opts: MapOptions): FactCardInput {\n return {\n ...base,\n template: 'factCard',\n fact: data.fact,\n explanation: data.explanation ?? '',\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapImpactLine(data: ImpactLineData, base: Base, opts: MapOptions): FullBleedQuoteInput {\n return {\n ...base,\n template: 'fullBleedQuote',\n text: data.text,\n colorScheme: opts.colorScheme,\n };\n}\n\nfunction mapList(data: ListData, base: Base, opts: MapOptions): ListBlockInput {\n return {\n ...base,\n template: 'list',\n items: data.items,\n title: data.title,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n\nfunction mapDefinition(data: DefinitionData, base: Base, opts: MapOptions): DefinitionCardInput {\n return {\n ...base,\n template: 'definitionCard',\n term: data.term,\n definition: data.definition,\n origin: data.origin,\n colorScheme: opts.colorScheme,\n accentImage: opts.accentImage,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
DEFAULT_THEME,
|
|
3
3
|
applySurface,
|
|
4
4
|
resolveFontFamily
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6PJRB2CF.js";
|
|
6
6
|
|
|
7
7
|
// src/jsonForm/chooseControl.ts
|
|
8
8
|
var ENUM_SEGMENTED_LIMIT = 4;
|
|
@@ -68,14 +68,48 @@ function hasFiniteRange(schema) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// src/jsonForm/pointer.ts
|
|
71
|
+
function escapePointerSegment(segment) {
|
|
72
|
+
return segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
73
|
+
}
|
|
71
74
|
function toPointer(path) {
|
|
72
|
-
if (path === ""
|
|
75
|
+
if (path === "") return "";
|
|
73
76
|
if (path.startsWith("/")) return path;
|
|
74
|
-
return "/" + path.split(".").map(
|
|
77
|
+
return "/" + path.split(".").map(escapePointerSegment).join("/");
|
|
78
|
+
}
|
|
79
|
+
function appendPointer(base, segment) {
|
|
80
|
+
const pointer = toPointer(base);
|
|
81
|
+
pointerSegments(pointer);
|
|
82
|
+
return `${pointer}/${escapePointerSegment(segment)}`;
|
|
75
83
|
}
|
|
76
84
|
function pointerSegments(pointer) {
|
|
77
85
|
if (pointer === "") return [];
|
|
78
|
-
|
|
86
|
+
if (!pointer.startsWith("/")) {
|
|
87
|
+
throw new TypeError(`Invalid JSON Pointer ${JSON.stringify(pointer)}: expected "" or "/..."`);
|
|
88
|
+
}
|
|
89
|
+
return pointer.slice(1).split("/").map((seg) => {
|
|
90
|
+
if (/~(?:[^01]|$)/.test(seg)) {
|
|
91
|
+
throw new TypeError(
|
|
92
|
+
`Invalid JSON Pointer ${JSON.stringify(pointer)}: "~" must be escaped as "~0"`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return seg.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function arrayIndex(segment) {
|
|
99
|
+
if (!/^(?:0|[1-9]\d*)$/.test(segment)) return void 0;
|
|
100
|
+
const index = Number(segment);
|
|
101
|
+
return Number.isSafeInteger(index) && index <= 4294967294 ? index : void 0;
|
|
102
|
+
}
|
|
103
|
+
function ownValue(object, key) {
|
|
104
|
+
return Object.prototype.hasOwnProperty.call(object, key) ? object[key] : void 0;
|
|
105
|
+
}
|
|
106
|
+
function setOwn(object, key, value) {
|
|
107
|
+
Object.defineProperty(object, key, {
|
|
108
|
+
value,
|
|
109
|
+
enumerable: true,
|
|
110
|
+
configurable: true,
|
|
111
|
+
writable: true
|
|
112
|
+
});
|
|
79
113
|
}
|
|
80
114
|
function getByPointer(data, path) {
|
|
81
115
|
const segments = pointerSegments(toPointer(path));
|
|
@@ -83,11 +117,11 @@ function getByPointer(data, path) {
|
|
|
83
117
|
for (const seg of segments) {
|
|
84
118
|
if (cur === null || cur === void 0) return void 0;
|
|
85
119
|
if (Array.isArray(cur)) {
|
|
86
|
-
const idx =
|
|
87
|
-
if (
|
|
120
|
+
const idx = arrayIndex(seg);
|
|
121
|
+
if (idx === void 0 || idx >= cur.length) return void 0;
|
|
88
122
|
cur = cur[idx];
|
|
89
123
|
} else if (typeof cur === "object") {
|
|
90
|
-
cur = cur
|
|
124
|
+
cur = ownValue(cur, seg);
|
|
91
125
|
} else {
|
|
92
126
|
return void 0;
|
|
93
127
|
}
|
|
@@ -103,28 +137,35 @@ function setRec(node, segments, i, value) {
|
|
|
103
137
|
const seg = segments[i];
|
|
104
138
|
const isLast = i === segments.length - 1;
|
|
105
139
|
const nextSeg = isLast ? void 0 : segments[i + 1];
|
|
106
|
-
const writingArrayChild = nextSeg !== void 0 &&
|
|
140
|
+
const writingArrayChild = nextSeg !== void 0 && arrayIndex(nextSeg) !== void 0;
|
|
107
141
|
if (Array.isArray(node)) {
|
|
108
|
-
const idx =
|
|
142
|
+
const idx = arrayIndex(seg);
|
|
143
|
+
if (idx === void 0) {
|
|
144
|
+
throw new TypeError(`Invalid array index ${JSON.stringify(seg)} in JSON Pointer`);
|
|
145
|
+
}
|
|
109
146
|
const next = node.slice();
|
|
110
147
|
next[idx] = isLast ? value : setRec(node[idx] ?? (writingArrayChild ? [] : {}), segments, i + 1, value);
|
|
111
148
|
return next;
|
|
112
149
|
}
|
|
113
150
|
const obj = node && typeof node === "object" ? { ...node } : {};
|
|
114
|
-
|
|
151
|
+
const previous = ownValue(obj, seg);
|
|
152
|
+
setOwn(
|
|
153
|
+
obj,
|
|
154
|
+
seg,
|
|
155
|
+
isLast ? value : setRec(previous ?? (writingArrayChild ? [] : {}), segments, i + 1, value)
|
|
156
|
+
);
|
|
115
157
|
return obj;
|
|
116
158
|
}
|
|
117
159
|
function resolveRef(schema, root) {
|
|
118
160
|
if (!schema.$ref) return schema;
|
|
119
161
|
const ref = schema.$ref;
|
|
162
|
+
if (ref === "#") return root;
|
|
120
163
|
if (!ref.startsWith("#/")) return void 0;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
cur = cur[seg];
|
|
164
|
+
try {
|
|
165
|
+
return getByPointer(root, ref.slice(1));
|
|
166
|
+
} catch {
|
|
167
|
+
return void 0;
|
|
126
168
|
}
|
|
127
|
-
return cur ?? void 0;
|
|
128
169
|
}
|
|
129
170
|
|
|
130
171
|
// src/jsonForm/evaluateWhen.ts
|
|
@@ -218,7 +259,9 @@ export {
|
|
|
218
259
|
chooseControl,
|
|
219
260
|
primaryType,
|
|
220
261
|
arrayItemKind,
|
|
262
|
+
escapePointerSegment,
|
|
221
263
|
toPointer,
|
|
264
|
+
appendPointer,
|
|
222
265
|
pointerSegments,
|
|
223
266
|
getByPointer,
|
|
224
267
|
setByPointer,
|
|
@@ -229,4 +272,4 @@ export {
|
|
|
229
272
|
buildJsonFormTokens,
|
|
230
273
|
resolveJsonFormTheme
|
|
231
274
|
};
|
|
232
|
-
//# sourceMappingURL=chunk-
|
|
275
|
+
//# sourceMappingURL=chunk-QTSJOY54.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/jsonForm/chooseControl.ts","../src/jsonForm/pointer.ts","../src/jsonForm/evaluateWhen.ts","../src/jsonForm/inferSchema.ts","../src/jsonForm/tokens.ts"],"sourcesContent":["/**\n * Pick a `ControlKind` for a schema node. The single dispatcher used by\n * both `<JsonView>` and `<JsonEditor>` so view and edit modes always\n * agree on what each field _is_, even when their UIs differ.\n *\n * Order of precedence:\n * 1. Explicit `squisq.control` hint\n * 2. `format` keyword (color/date/time/datetime/markdown/textarea)\n * 3. Enum cardinality (segmented vs. combobox)\n * 4. Numeric range (slider when both min and max present)\n * 5. String length (multiline when maxLength > 200)\n * 6. Type-default (object/array/boolean/string/number)\n */\n\nimport type { ControlKind, SquisqAnnotatedSchema } from './types.js';\n\nconst ENUM_SEGMENTED_LIMIT = 4;\nconst MULTILINE_LENGTH_THRESHOLD = 200;\n\nexport function chooseControl(schema: SquisqAnnotatedSchema): ControlKind {\n const hint = schema.squisq?.control;\n if (hint) return hint;\n\n const type = primaryType(schema);\n\n if (schema.format && type === 'string') {\n switch (schema.format) {\n case 'color':\n return 'color';\n case 'date':\n return 'date';\n case 'time':\n return 'time';\n case 'date-time':\n return 'datetime';\n case 'markdown':\n return 'richtext';\n case 'textarea':\n return 'multiline';\n }\n }\n\n if (schema.enum && schema.enum.length > 0) {\n return schema.enum.length <= ENUM_SEGMENTED_LIMIT ? 'segmented' : 'combobox';\n }\n\n if (schema.oneOf || schema.anyOf) return 'tabs';\n\n switch (type) {\n case 'array':\n return arrayItemKind(schema) === 'object' ? 'card-stack' : 'chip-bin';\n case 'object':\n return 'group';\n case 'boolean':\n return 'toggle';\n case 'integer':\n case 'number':\n return hasFiniteRange(schema) ? 'slider' : 'stepper';\n case 'string':\n if (typeof schema.maxLength === 'number' && schema.maxLength > MULTILINE_LENGTH_THRESHOLD) {\n return 'multiline';\n }\n return 'text';\n default:\n return 'text';\n }\n}\n\n/** Return the first concrete type of a schema, ignoring `null` for nullable shorthand. */\nexport function primaryType(schema: SquisqAnnotatedSchema): string | undefined {\n const t = schema.type;\n if (typeof t === 'string') return t;\n if (Array.isArray(t)) return t.find((x) => x !== 'null');\n return undefined;\n}\n\n/** Inspect `items` to decide whether an array holds objects or primitives. */\nexport function arrayItemKind(schema: SquisqAnnotatedSchema): 'object' | 'primitive' {\n const items = Array.isArray(schema.items) ? schema.items[0] : schema.items;\n if (!items) return 'primitive';\n return primaryType(items) === 'object' ? 'object' : 'primitive';\n}\n\nfunction hasFiniteRange(schema: SquisqAnnotatedSchema): boolean {\n const min = schema.minimum ?? schema.exclusiveMinimum;\n const max = schema.maximum ?? schema.exclusiveMaximum;\n return typeof min === 'number' && typeof max === 'number';\n}\n","/**\n * JSON Pointer helpers (RFC 6901) plus immutable get/set, plus a tiny\n * dotted-path normalizer so hosts can write `user.role` instead of\n * `/user/role`. `$ref` resolution is local-only (`#/$defs/Foo`).\n */\n\nimport type { SquisqAnnotatedSchema } from './types.js';\n\n/** Escape one object-member name for use as an RFC 6901 pointer segment. */\nexport function escapePointerSegment(segment: string): string {\n return segment.replace(/~/g, '~0').replace(/\\//g, '~1');\n}\n\n/** Normalize a path to a JSON Pointer string. Accepts both forms. */\nexport function toPointer(path: string): string {\n if (path === '') return '';\n if (path.startsWith('/')) return path;\n // Dotted form: split on `.` and escape per RFC 6901.\n return '/' + path.split('.').map(escapePointerSegment).join('/');\n}\n\n/** Append one member name to a dotted path or validated JSON Pointer. */\nexport function appendPointer(base: string, segment: string): string {\n const pointer = toPointer(base);\n pointerSegments(pointer); // validate explicit pointer syntax before composing\n return `${pointer}/${escapePointerSegment(segment)}`;\n}\n\n/** Split a JSON Pointer into decoded segments. Empty string → []. */\nexport function pointerSegments(pointer: string): string[] {\n if (pointer === '') return [];\n if (!pointer.startsWith('/')) {\n throw new TypeError(`Invalid JSON Pointer ${JSON.stringify(pointer)}: expected \"\" or \"/...\"`);\n }\n return pointer\n .slice(1)\n .split('/')\n .map((seg) => {\n if (/~(?:[^01]|$)/.test(seg)) {\n throw new TypeError(\n `Invalid JSON Pointer ${JSON.stringify(pointer)}: \"~\" must be escaped as \"~0\"`,\n );\n }\n return seg.replace(/~1/g, '/').replace(/~0/g, '~');\n });\n}\n\n/** Parse the canonical array-index spelling used by JSON Pointer consumers. */\nfunction arrayIndex(segment: string): number | undefined {\n if (!/^(?:0|[1-9]\\d*)$/.test(segment)) return undefined;\n const index = Number(segment);\n // 2^32 - 1 is the Array length sentinel, not an element index.\n return Number.isSafeInteger(index) && index <= 0xffff_fffe ? index : undefined;\n}\n\nfunction ownValue(object: object, key: string): unknown {\n return Object.prototype.hasOwnProperty.call(object, key)\n ? (object as Record<string, unknown>)[key]\n : undefined;\n}\n\n/** Define a data member without invoking Object.prototype.__proto__'s setter. */\nfunction setOwn(object: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(object, key, {\n value,\n enumerable: true,\n configurable: true,\n writable: true,\n });\n}\n\n/** Read a value from data by path (dotted or pointer). Returns undefined if missing. */\nexport function getByPointer(data: unknown, path: string): unknown {\n const segments = pointerSegments(toPointer(path));\n let cur: unknown = data;\n for (const seg of segments) {\n if (cur === null || cur === undefined) return undefined;\n if (Array.isArray(cur)) {\n const idx = arrayIndex(seg);\n if (idx === undefined || idx >= cur.length) return undefined;\n cur = cur[idx];\n } else if (typeof cur === 'object') {\n cur = ownValue(cur, seg);\n } else {\n return undefined;\n }\n }\n return cur;\n}\n\n/**\n * Return a structurally-shared copy of `data` with the value at `path`\n * replaced. Intermediate objects/arrays are cloned along the path; the\n * rest is shared by reference. Creates intermediate objects if missing.\n */\nexport function setByPointer<T>(data: T, path: string, value: unknown): T {\n const segments = pointerSegments(toPointer(path));\n if (segments.length === 0) return value as T;\n return setRec(data, segments, 0, value) as T;\n}\n\nfunction setRec(node: unknown, segments: string[], i: number, value: unknown): unknown {\n const seg = segments[i];\n const isLast = i === segments.length - 1;\n\n // Decide whether the slot we're about to write into is an array index.\n const nextSeg = isLast ? undefined : segments[i + 1];\n const writingArrayChild = nextSeg !== undefined && arrayIndex(nextSeg) !== undefined;\n\n if (Array.isArray(node)) {\n const idx = arrayIndex(seg);\n if (idx === undefined) {\n throw new TypeError(`Invalid array index ${JSON.stringify(seg)} in JSON Pointer`);\n }\n const next = node.slice();\n next[idx] = isLast\n ? value\n : setRec(node[idx] ?? (writingArrayChild ? [] : {}), segments, i + 1, value);\n return next;\n }\n\n const obj = (node && typeof node === 'object' ? { ...(node as object) } : {}) as Record<\n string,\n unknown\n >;\n const previous = ownValue(obj, seg);\n setOwn(\n obj,\n seg,\n isLast ? value : setRec(previous ?? (writingArrayChild ? [] : {}), segments, i + 1, value),\n );\n return obj;\n}\n\n/**\n * Resolve a local `$ref` (e.g. `#/$defs/Foo`) against the root schema.\n * Returns the original schema if no `$ref` is present, or `undefined`\n * if the reference cannot be resolved. Cross-document refs are not\n * supported.\n */\nexport function resolveRef(\n schema: SquisqAnnotatedSchema,\n root: SquisqAnnotatedSchema,\n): SquisqAnnotatedSchema | undefined {\n if (!schema.$ref) return schema;\n const ref = schema.$ref;\n if (ref === '#') return root;\n if (!ref.startsWith('#/')) return undefined;\n try {\n return getByPointer(root, ref.slice(1)) as SquisqAnnotatedSchema | undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Evaluate a `SquisqWhen` rule against the current root data. Pure;\n * renderers call this on every render with fresh data.\n */\n\nimport type { SquisqWhen } from './types.js';\nimport { getByPointer } from './pointer.js';\n\nexport function evaluateWhen(when: SquisqWhen, rootData: unknown): boolean {\n const actual = getByPointer(rootData, when.field);\n\n if (when.equals !== undefined) {\n return deepEqual(actual, when.equals);\n }\n if (when.oneOf !== undefined) {\n return when.oneOf.some((v) => deepEqual(actual, v));\n }\n if (when.matches !== undefined) {\n if (typeof actual !== 'string') return false;\n try {\n return new RegExp(when.matches).test(actual);\n } catch {\n return false;\n }\n }\n if (when.truthy !== undefined) {\n return Boolean(actual) === when.truthy;\n }\n // No operator specified — defaults to \"field has any defined value\".\n return actual !== undefined && actual !== null;\n}\n\n/**\n * Resolve a `boolean | SquisqWhen` flag (used by `hidden`/`disabled`)\n * to a final boolean. Literal booleans pass through.\n */\nexport function resolveFlag(flag: boolean | SquisqWhen | undefined, rootData: unknown): boolean {\n if (flag === undefined) return false;\n if (typeof flag === 'boolean') return flag;\n return evaluateWhen(flag, rootData);\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n if (Array.isArray(a)) {\n if (!Array.isArray(b) || a.length !== b.length) return false;\n return a.every((v, i) => deepEqual(v, b[i]));\n }\n if (Array.isArray(b)) return false;\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const ak = Object.keys(ao);\n const bk = Object.keys(bo);\n if (ak.length !== bk.length) return false;\n return ak.every((k) => deepEqual(ao[k], bo[k]));\n}\n","/**\n * Infer a JSON Schema from an example JSON value. Thin facade over\n * `genson-js` so consumers don't depend on it directly and we can swap\n * the implementation later.\n */\n\nimport { createSchema, mergeSchemas } from 'genson-js';\nimport type { SquisqAnnotatedSchema } from './types.js';\n\nexport interface InferSchemaOptions {\n /** Additional samples merged into the inferred schema for broader coverage. */\n additionalSamples?: readonly unknown[];\n}\n\nexport function inferSchema(\n sample: unknown,\n options: InferSchemaOptions = {},\n): SquisqAnnotatedSchema {\n const base = createSchema(sample) as SquisqAnnotatedSchema;\n if (!options.additionalSamples || options.additionalSamples.length === 0) {\n return base;\n }\n const extras = options.additionalSamples.map((s) => createSchema(s) as SquisqAnnotatedSchema);\n return mergeSchemas([base, ...extras] as never[]) as SquisqAnnotatedSchema;\n}\n","/**\n * Pure CSS-custom-property builder shared by `<JsonView>` (read-only, in\n * `@bendyline/squisq-react`) and `<JsonEditor>` (editable, in\n * `@bendyline/squisq-editor-react`).\n *\n * Both components derive an identical token bag from a Theme + resolved\n * Surface; they differ only in the CSS-var prefix (`--squisq-json` vs\n * `--squisq-jsonform`) so the viewer and editor can coexist on one page.\n * This function is the single source of that derivation. It is pure and\n * framework-free: it performs no `window` / `matchMedia` access — the\n * surface must already be resolved to a concrete `SurfaceScheme` (or left\n * `undefined` to skip surface application) by the calling React hook.\n */\n\nimport { applySurface, DEFAULT_THEME, resolveFontFamily } from '../schemas/index.js';\nimport type { SurfaceScheme, Theme } from '../schemas/index.js';\n\nexport interface BuildJsonFormTokensOptions {\n /**\n * CSS-var prefix WITHOUT the trailing dash, e.g. `--squisq-json` or\n * `--squisq-jsonform`. Each token key is `${prefix}-${name}`.\n */\n prefix: string;\n}\n\n/**\n * Build the CSS custom-property map for a JSON form/view surface.\n *\n * @param theme The theme to render with (falls back to `DEFAULT_THEME`).\n * @param surface Already-resolved concrete surface, or `undefined` to leave\n * the theme un-surfaced. (The `'auto'` → concrete resolution\n * is the React hook's job, so this stays pure.)\n * @param options `{ prefix }` — the CSS-var namespace.\n */\nexport function buildJsonFormTokens(\n theme: Theme | undefined,\n surface: SurfaceScheme | undefined,\n options: BuildJsonFormTokensOptions,\n): Record<string, string> {\n const { prefix } = options;\n const baseTheme = theme ?? DEFAULT_THEME;\n const finalTheme = surface ? applySurface(baseTheme, surface) : baseTheme;\n\n const titleFont = resolveFontFamily(finalTheme.typography.titleFont, 'system-ui, sans-serif');\n const bodyFont = resolveFontFamily(finalTheme.typography.bodyFont, 'system-ui, sans-serif');\n const monoFont = resolveFontFamily(\n finalTheme.typography.monoFont,\n 'ui-monospace, Consolas, monospace',\n );\n\n return {\n [`${prefix}-bg`]: finalTheme.colors.background,\n [`${prefix}-text`]: finalTheme.colors.text,\n [`${prefix}-muted`]: finalTheme.colors.textMuted,\n [`${prefix}-primary`]: finalTheme.colors.primary,\n [`${prefix}-accent`]: finalTheme.colors.secondary,\n [`${prefix}-warning`]: finalTheme.colors.warning,\n [`${prefix}-border`]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,\n [`${prefix}-input-bg`]: finalTheme.colors.backgroundLight,\n [`${prefix}-title-font`]: titleFont,\n [`${prefix}-body-font`]: bodyFont,\n [`${prefix}-mono-font`]: monoFont,\n [`${prefix}-radius`]: `${finalTheme.style.borderRadius ?? 8}px`,\n };\n}\n\n/**\n * Resolve the effective theme after surface application — the companion to\n * `buildJsonFormTokens` for callers that also need the `Theme` object (both\n * hooks return it alongside the style bag).\n */\nexport function resolveJsonFormTheme(\n theme: Theme | undefined,\n surface: SurfaceScheme | undefined,\n): Theme {\n const baseTheme = theme ?? DEFAULT_THEME;\n return surface ? applySurface(baseTheme, surface) : baseTheme;\n}\n"],"mappings":"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","names":[]}
|