@bendyline/squisq 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +14 -1
  2. package/dist/{ContentContainer-DNx460y8.d.ts → ContentContainer-BXUlIf18.d.ts} +6 -2
  3. package/dist/{Doc-BsMkmzH8.d.ts → Doc-BBVGq1_9.d.ts} +94 -15
  4. package/dist/{ImageEditDoc-DvoeHMP9.d.ts → ImageEditDoc-FuyGtt6o.d.ts} +1 -1
  5. package/dist/{chunk-4ZFRZBU2.js → chunk-5TYS5V3G.js} +866 -192
  6. package/dist/chunk-5TYS5V3G.js.map +1 -0
  7. package/dist/{chunk-7MQGNR76.js → chunk-A4O7GIWE.js} +2 -27
  8. package/dist/chunk-A4O7GIWE.js.map +1 -0
  9. package/dist/{chunk-OH43RPTP.js → chunk-BEQODJWV.js} +20 -3
  10. package/dist/chunk-BEQODJWV.js.map +1 -0
  11. package/dist/chunk-ET53IIEP.js +1 -0
  12. package/dist/chunk-ET53IIEP.js.map +1 -0
  13. package/dist/{chunk-KZNXZ5SM.js → chunk-GPLTCSXO.js} +18 -7
  14. package/dist/chunk-GPLTCSXO.js.map +1 -0
  15. package/dist/{chunk-OKJOQXK4.js → chunk-HWVFJAAH.js} +38 -2
  16. package/dist/chunk-HWVFJAAH.js.map +1 -0
  17. package/dist/{chunk-2TMAWB4Y.js → chunk-IMSCRLLV.js} +1 -1
  18. package/dist/chunk-IMSCRLLV.js.map +1 -0
  19. package/dist/{chunk-4AKUQPEE.js → chunk-KKNUBQ6Y.js} +1 -1
  20. package/dist/chunk-KKNUBQ6Y.js.map +1 -0
  21. package/dist/{chunk-TVSGLUID.js → chunk-LDQ2HJIX.js} +3 -3
  22. package/dist/{chunk-RUDYOTA6.js → chunk-LTN6I7QW.js} +56 -1
  23. package/dist/chunk-LTN6I7QW.js.map +1 -0
  24. package/dist/{chunk-2OIBZYKP.js → chunk-P3K7NLHF.js} +2 -2
  25. package/dist/{chunk-K32VJONL.js → chunk-T5UK6YOB.js} +41 -2
  26. package/dist/chunk-T5UK6YOB.js.map +1 -0
  27. package/dist/{chunk-FR2RBTKO.js → chunk-TQWLI6S2.js} +93 -27
  28. package/dist/chunk-TQWLI6S2.js.map +1 -0
  29. package/dist/{chunk-LRCV7N7F.js → chunk-UY7KGQ5R.js} +2 -2
  30. package/dist/{chunk-3E5F2XMR.js → chunk-VWUFZ6ZG.js} +29 -2
  31. package/dist/chunk-VWUFZ6ZG.js.map +1 -0
  32. package/dist/doc/index.d.ts +4 -4
  33. package/dist/doc/index.js +24 -9
  34. package/dist/generate/index.d.ts +2 -80
  35. package/dist/generate/index.js +2 -7
  36. package/dist/imageEdit/index.d.ts +4 -4
  37. package/dist/imageEdit/index.js +2 -2
  38. package/dist/index.d.ts +8 -8
  39. package/dist/index.js +41 -26
  40. package/dist/jsonForm/index.d.ts +42 -1
  41. package/dist/jsonForm/index.js +9 -1
  42. package/dist/markdown/index.d.ts +18 -2
  43. package/dist/markdown/index.js +5 -3
  44. package/dist/recommend/index.js +2 -2
  45. package/dist/schemas/index.d.ts +4 -4
  46. package/dist/schemas/index.js +3 -5
  47. package/dist/storage/index.d.ts +2 -2
  48. package/dist/storage/index.js +1 -1
  49. package/dist/story/index.d.ts +151 -13
  50. package/dist/story/index.js +24 -9
  51. package/dist/{themeLibrary-DQQKuRMx.d.ts → themeLibrary-CehcJhzz.d.ts} +1 -1
  52. package/dist/timing/index.js +3 -5
  53. package/dist/transform/index.d.ts +2 -2
  54. package/dist/transform/index.js +3 -3
  55. package/dist/versions/index.d.ts +1 -1
  56. package/package.json +1 -1
  57. package/src/__tests__/contentExtractor.test.ts +60 -0
  58. package/src/__tests__/coverBlock.test.ts +79 -3
  59. package/src/__tests__/customTemplateSample.test.ts +63 -2
  60. package/src/__tests__/customTemplatesFrontmatter.test.ts +31 -0
  61. package/src/__tests__/customThemesFrontmatter.test.ts +27 -0
  62. package/src/__tests__/inputDescriptors.test.ts +162 -0
  63. package/src/__tests__/jsonForm.tokens.test.ts +84 -0
  64. package/src/__tests__/markdown.test.ts +64 -0
  65. package/src/__tests__/markdownSanitize.test.ts +15 -0
  66. package/src/__tests__/markdownToDoc.test.ts +173 -2
  67. package/src/__tests__/mediaAnnotations.test.ts +27 -0
  68. package/src/__tests__/shapeGeometry.test.ts +37 -0
  69. package/src/__tests__/structuredData.test.ts +73 -3
  70. package/src/__tests__/templateMetadata.test.ts +11 -1
  71. package/src/__tests__/templates.test.ts +112 -0
  72. package/src/__tests__/validateDoc.test.ts +160 -0
  73. package/src/doc/annotationBlocks.ts +117 -0
  74. package/src/doc/customTemplatesFrontmatter.ts +7 -1
  75. package/src/doc/customThemesFrontmatter.ts +6 -1
  76. package/src/doc/docToMarkdown.ts +78 -29
  77. package/src/doc/getLayers.ts +11 -2
  78. package/src/doc/index.ts +12 -0
  79. package/src/doc/markdownToDoc.ts +80 -3
  80. package/src/doc/mediaAnnotations.ts +15 -33
  81. package/src/doc/standaloneAnnotation.ts +58 -0
  82. package/src/doc/structuredData.ts +92 -21
  83. package/src/doc/templates/__tests__/customTemplate.test.ts +21 -2
  84. package/src/doc/templates/comparisonBar.ts +8 -1
  85. package/src/doc/templates/coverBlock.ts +78 -15
  86. package/src/doc/templates/customTemplate.ts +10 -4
  87. package/src/doc/templates/diagramBlock.ts +2 -2
  88. package/src/doc/templates/drawingBlock.ts +2 -2
  89. package/src/doc/templates/factCard.ts +4 -2
  90. package/src/doc/templates/imageWithCaption.ts +10 -8
  91. package/src/doc/templates/index.ts +12 -2
  92. package/src/doc/templates/inputDescriptors.ts +432 -0
  93. package/src/doc/templates/photoGrid.ts +16 -39
  94. package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +267 -1
  95. package/src/doc/templates/tokens/resolveTokens.ts +0 -0
  96. package/src/doc/templates/videoWithCaption.ts +15 -14
  97. package/src/doc/utils/nearest.ts +59 -0
  98. package/src/doc/utils/shapeGeometry.ts +90 -2
  99. package/src/doc/validate.ts +153 -54
  100. package/src/generate/contentExtractor.ts +148 -35
  101. package/src/generate/index.ts +0 -3
  102. package/src/jsonForm/index.ts +2 -0
  103. package/src/jsonForm/tokens.ts +78 -0
  104. package/src/markdown/annotationCoercion.ts +1 -1
  105. package/src/markdown/attrTokens.ts +19 -0
  106. package/src/markdown/convert.ts +11 -1
  107. package/src/markdown/index.ts +2 -1
  108. package/src/markdown/sanitize.ts +22 -2
  109. package/src/markdown/stringify.ts +12 -3
  110. package/src/markdown/utils.ts +62 -1
  111. package/src/schemas/CustomTemplates.ts +56 -4
  112. package/src/schemas/Doc.ts +39 -41
  113. package/src/schemas/themeCompile.ts +1 -1
  114. package/src/storage/ContentContainer.ts +6 -2
  115. package/src/transform/types.ts +1 -1
  116. package/dist/chunk-2TMAWB4Y.js.map +0 -1
  117. package/dist/chunk-3E5F2XMR.js.map +0 -1
  118. package/dist/chunk-4AKUQPEE.js.map +0 -1
  119. package/dist/chunk-4X3JQXNM.js +0 -35
  120. package/dist/chunk-4X3JQXNM.js.map +0 -1
  121. package/dist/chunk-4ZFRZBU2.js.map +0 -1
  122. package/dist/chunk-7MQGNR76.js.map +0 -1
  123. package/dist/chunk-FBKP5CAO.js +0 -145
  124. package/dist/chunk-FBKP5CAO.js.map +0 -1
  125. package/dist/chunk-FR2RBTKO.js.map +0 -1
  126. package/dist/chunk-K32VJONL.js.map +0 -1
  127. package/dist/chunk-KZNXZ5SM.js.map +0 -1
  128. package/dist/chunk-OH43RPTP.js.map +0 -1
  129. package/dist/chunk-OKJOQXK4.js.map +0 -1
  130. package/dist/chunk-RUDYOTA6.js.map +0 -1
  131. package/src/__tests__/slideshowGenerator.test.ts +0 -128
  132. package/src/generate/slideshowGenerator.ts +0 -280
  133. /package/dist/{chunk-TVSGLUID.js.map → chunk-LDQ2HJIX.js.map} +0 -0
  134. /package/dist/{chunk-2OIBZYKP.js.map → chunk-P3K7NLHF.js.map} +0 -0
  135. /package/dist/{chunk-LRCV7N7F.js.map → chunk-UY7KGQ5R.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markdown/stringify.ts","../src/markdown/sanitize.ts"],"sourcesContent":["/**\n * JSON → Markdown Serializer\n *\n * Converts a MarkdownDocument JSON structure back to a markdown string\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkStringify from 'remark-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport type { MarkdownDocument, StringifyOptions } from './types.js';\nimport { toMdast } from './convert.js';\nimport { formatBlockScalar } from './utils.js';\n\n// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n// ============================================\n// Annotation-span unescaping\n// ============================================\n\n// Quoted runs as they appear in remark-stringify output. Backslashes inside\n// the span are doubled by remark (a literal `\\` before punctuation becomes\n// `\\\\`), so `\\\\.` covers both authored escapes and remark's own.\nconst DQ_RUN = `\"(?:[^\"\\\\\\\\]|\\\\\\\\.)*\"`;\nconst SQ_RUN = `'(?:[^'\\\\\\\\]|\\\\\\\\.)*'`;\n\n/**\n * A `{[…]}` template-annotation (or inline-icon) span as it appears in\n * remark output: `{\\[inner]}`, where `inner` may contain backslash escapes\n * and quoted runs (which may themselves contain `]`).\n */\nconst ESCAPED_TEMPLATE_SPAN_RE = new RegExp(\n `\\\\{\\\\\\\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\\\\\.|[^\\\\]\\\\\\\\])+)\\\\]\\\\}`,\n 'g',\n);\n\n/** A heading line (annotations are only emitted trailing on headings). */\nconst HEADING_LINE_RE = /^#{1,6} .*$/gm;\n\n/**\n * The trailing Pandoc `{…}` span on a heading line, possibly followed by an\n * already-unescaped `{[…]}` template span (canonical emit order).\n */\nconst TRAILING_ESCAPED_PANDOC_SPAN_RE = new RegExp(\n `\\\\{(?!\\\\\\\\?\\\\[)((?:${DQ_RUN}|${SQ_RUN}|\\\\\\\\.|[^}\\\\\\\\])*)\\\\}(?=\\\\s*(?:\\\\{\\\\[.*)?$)`,\n);\n\n/**\n * Backslash+punctuation pairs that are safe to unescape inside an\n * annotation span: the bare character re-parses as literal text in\n * trailing-heading position, so removing the backslash is purely\n * cosmetic and round-trips.\n *\n * Deliberately EXCLUDED (left escaped in the emitted markdown): emphasis\n * and other inline-construct openers (`*`, `_`, `` ` ``, `~`, `<`, `>`,\n * `&`, `!`, `|`, `^`). Unescaping those could make remark-parse split the\n * heading's text run into inline nodes on reparse, which would hide the\n * annotation from the extractor. remark-parse resolves the surviving\n * `\\X` escapes back to bare characters, so values still round-trip —\n * the escapes are a cosmetic cost only.\n *\n * Pairs are consumed left to right, so an authored `\\\"` that remark\n * doubled to `\\\\\"` correctly comes back as `\\\"`.\n */\nconst UNESCAPE_PUNCT_RE = /\\\\([\\\\[\\]:#.,+=/?;@%$(){}'\"-])/g;\n\nfunction unescapeMarkdownPunct(text: string): string {\n return text.replace(UNESCAPE_PUNCT_RE, '$1');\n}\n\n/**\n * Serialize a MarkdownDocument back to a markdown string.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to control formatting and disable extensions.\n *\n * @param doc - The MarkdownDocument to serialize\n * @param options - Serialization options (formatting, extensions)\n * @returns A markdown string\n *\n * @example\n * ```ts\n * const doc: MarkdownDocument = {\n * type: 'document',\n * children: [\n * { type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] },\n * { type: 'paragraph', children: [{ type: 'text', value: 'World' }] },\n * ],\n * };\n * const md = stringifyMarkdown(doc);\n * // '# Hello\\n\\nWorld\\n'\n * ```\n */\nexport function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOptions): string {\n // Convert MarkdownDocument → mdast tree\n const mdastTree = toMdast(doc);\n\n // Use cached default processor when all extensions and default formatting are used.\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n !options.bullet &&\n !options.bulletOrdered &&\n !options.emphasis &&\n !options.strong &&\n !options.rule &&\n !options.fence &&\n options.setext == null);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkStringify, {\n bullet: '-',\n bulletOrdered: '.',\n emphasis: '*',\n strong: '*',\n rule: '-',\n fence: '`',\n setext: false,\n });\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested options.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified();\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n\n processor = processor.use(remarkStringify, {\n bullet: options?.bullet ?? '-',\n bulletOrdered: options?.bulletOrdered ?? '.',\n emphasis: options?.emphasis ?? '*',\n strong: options?.strong ?? '*',\n rule: options?.rule ?? '-',\n fence: options?.fence ?? '`',\n setext: options?.setext ?? false,\n });\n }\n\n // Stringify mdast → markdown string\n const result = processor.stringify(mdastTree) as string;\n\n // remark-stringify backslash-escapes markdown-significant characters in\n // text nodes (`[` to prevent link syntax, `:` for directives, `*`/`_` for\n // emphasis, a literal `\\` before punctuation, …). Our annotations are\n // injected as plain text, so their interiors come back escaped. Undo that\n // inside `{[…]}` template-annotation spans: removing one backslash from\n // each backslash+punctuation pair (left to right) exactly inverts\n // remark's escaping — including restoring an authored `\\\"` that remark\n // doubled to `\\\\\"`. The span regex understands quoted values, so a\n // quoted `]` doesn't terminate the span.\n let cleaned = result.replace(\n ESCAPED_TEMPLATE_SPAN_RE,\n (_m, inner: string) => `{[${unescapeMarkdownPunct(inner)}]}`,\n );\n\n // Same treatment for the trailing Pandoc `{…}` attribute block on heading\n // lines (canonical emit order is Pandoc block then template annotation,\n // so the span sits at end-of-line or just before a `{[…]}` span).\n cleaned = cleaned.replace(HEADING_LINE_RE, (line) =>\n line.replace(\n TRAILING_ESCAPED_PANDOC_SPAN_RE,\n (_m, inner: string) => `{${unescapeMarkdownPunct(inner)}}`,\n ),\n );\n\n // remark-directive escapes `:` in text (since `:name` would round-trip as\n // a text directive). Inside any other Pandoc-style `{…}` brace span, `:`\n // is purely a value separator (e.g. `connectsTo=foo:flow`), so unescape\n // it to keep round-trips lossless. The negative lookahead skips `{[…]}`\n // template annotations — those are handled above.\n cleaned = cleaned.replace(/\\{(?!\\\\?\\[)[^}]*\\}/g, (match) => match.replace(/\\\\:/g, ':'));\n\n // Prepend YAML frontmatter if present\n if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {\n const yamlLines = Object.entries(doc.frontmatter).map(([k, v]) => {\n if (typeof v === 'string') {\n // Multi-line strings emit as a YAML literal block scalar, matching\n // `formatFrontmatterValue` / `setFrontmatterValues` so both paths\n // agree; single-line strings stay verbatim as before.\n return /[\\r\\n]/.test(v)\n ? `${k}: ${formatBlockScalar(v.replace(/\\r\\n?/g, '\\n'))}`\n : `${k}: ${v}`;\n }\n return `${k}: ${JSON.stringify(v)}`;\n });\n return `---\\n${yamlLines.join('\\n')}\\n---\\n\\n${cleaned}`;\n }\n\n return cleaned;\n}\n","import type { HtmlElement, HtmlNode } from './types.js';\n\nexport type HtmlPolicy = 'strip' | 'sanitize' | 'trusted';\n\nexport type UrlKind = 'link' | 'media';\n\nconst SAFE_LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);\nconst SAFE_MEDIA_SCHEMES = new Set(['http', 'https', 'blob']);\n\n/** Schemes that can execute script — never allowed, even via extraLinkSchemes. */\nconst NEVER_ALLOWED_SCHEMES = new Set(['javascript', 'vbscript', 'data']);\n\nexport interface SanitizeUrlOptions {\n /**\n * Extra lowercase schemes allowed for `kind: 'link'` — the host-app escape\n * hatch for custom link protocols it intercepts itself (e.g. an app-internal\n * navigation scheme). Executable schemes are refused regardless.\n */\n extraLinkSchemes?: readonly string[];\n}\n\nconst SAFE_DATA_MEDIA_RE =\n /^data:(?:image\\/(?!svg\\+xml)[a-z0-9.+-]+|audio\\/[a-z0-9.+-]+|video\\/[a-z0-9.+-]+);/i;\n\nconst SAFE_TAGS = new Set([\n 'a',\n 'abbr',\n 'b',\n 'blockquote',\n 'br',\n 'caption',\n 'cite',\n 'code',\n 'col',\n 'colgroup',\n 'data',\n 'dd',\n 'del',\n 'details',\n 'dfn',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'figcaption',\n 'figure',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'hr',\n 'i',\n 'img',\n 'kbd',\n 'li',\n 'mark',\n 'ol',\n 'p',\n 'pre',\n 'q',\n 's',\n 'samp',\n 'small',\n 'source',\n 'span',\n 'strong',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'track',\n 'u',\n 'ul',\n 'var',\n 'video',\n 'audio',\n]);\n\nconst DROP_WITH_CONTENT_TAGS = new Set([\n 'base',\n 'button',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'link',\n 'meta',\n 'object',\n 'option',\n 'script',\n 'select',\n 'style',\n 'svg',\n 'template',\n 'textarea',\n]);\n\nconst GLOBAL_ATTRS = new Set(['class', 'id', 'title', 'role']);\n\nconst TAG_ATTRS: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel']),\n audio: new Set(['src', 'controls', 'preload', 'muted', 'loop']),\n col: new Set(['span', 'width']),\n colgroup: new Set(['span']),\n data: new Set(['value']),\n img: new Set(['src', 'alt', 'width', 'height', 'loading', 'decoding']),\n li: new Set(['value']),\n ol: new Set(['start', 'type']),\n q: new Set(['cite']),\n source: new Set(['src', 'type', 'media']),\n td: new Set(['colspan', 'rowspan', 'align']),\n th: new Set(['colspan', 'rowspan', 'align', 'scope']),\n time: new Set(['datetime']),\n track: new Set(['src', 'kind', 'label', 'srclang', 'default']),\n video: new Set([\n 'src',\n 'poster',\n 'width',\n 'height',\n 'controls',\n 'preload',\n 'muted',\n 'loop',\n 'playsinline',\n ]),\n};\n\nconst SAFE_ATTR_NAME_RE = /^[a-z][a-z0-9:_.-]*$/;\nconst SAFE_OL_TYPES = new Set(['1', 'a', 'A', 'i', 'I']);\nconst SAFE_PRELOAD_VALUES = new Set(['none', 'metadata', 'auto']);\nconst SAFE_TRACK_KINDS = new Set(['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']);\n\n/**\n * Sanitize a URL for a rendered link or media attribute.\n *\n * Relative URLs are allowed. Absolute URLs are restricted to schemes that\n * cannot execute script in a document context. Media URLs additionally allow\n * `blob:` and image/audio/video `data:` URLs because Squisq uses both for\n * browser-local media previews and self-contained exports.\n */\nexport function sanitizeUrl(\n url: string | null | undefined,\n kind: UrlKind = 'link',\n options?: SanitizeUrlOptions,\n): string | null {\n if (typeof url !== 'string') return null;\n\n const trimmed = url.trim();\n if (!trimmed) return null;\n\n const compact = stripUrlSchemeNoise(trimmed);\n const colon = compact.indexOf(':');\n const firstPathChar = firstIndexOfAny(compact, ['/', '?', '#']);\n const hasScheme = colon >= 0 && (firstPathChar < 0 || colon < firstPathChar);\n\n if (!hasScheme) return trimmed;\n\n const scheme = compact.slice(0, colon).toLowerCase();\n if (kind === 'media') {\n if (scheme === 'data') return SAFE_DATA_MEDIA_RE.test(compact) ? trimmed : null;\n return SAFE_MEDIA_SCHEMES.has(scheme) ? trimmed : null;\n }\n\n if (SAFE_LINK_SCHEMES.has(scheme)) return trimmed;\n if (options?.extraLinkSchemes?.includes(scheme) && !NEVER_ALLOWED_SCHEMES.has(scheme)) {\n return trimmed;\n }\n return null;\n}\n\n/**\n * Return a sanitized copy of an HtmlNode tree. The original parsed tree is left\n * untouched so markdown stringify can still preserve source fidelity.\n */\nexport function sanitizeHtmlNodes(nodes: HtmlNode[]): HtmlNode[] {\n const out: HtmlNode[] = [];\n for (const node of nodes) {\n out.push(...sanitizeHtmlNode(node));\n }\n return out;\n}\n\nfunction sanitizeHtmlNode(node: HtmlNode): HtmlNode[] {\n switch (node.type) {\n case 'htmlText':\n return [node];\n case 'htmlComment':\n return [];\n case 'htmlElement':\n return sanitizeHtmlElement(node);\n }\n}\n\nfunction sanitizeHtmlElement(node: HtmlElement): HtmlNode[] {\n const tag = node.tagName.toLowerCase();\n\n if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];\n\n const children = sanitizeHtmlNodes(node.children);\n if (!SAFE_TAGS.has(tag)) return children;\n\n return [\n {\n type: 'htmlElement',\n tagName: tag,\n attributes: sanitizeAttrs(tag, node.attributes),\n children,\n selfClosing: node.selfClosing,\n },\n ];\n}\n\nfunction sanitizeAttrs(tag: string, attrs: Record<string, string>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [rawName, rawValue] of Object.entries(attrs)) {\n const name = rawName.toLowerCase();\n if (!isAllowedAttr(tag, name)) continue;\n\n const value = String(rawValue);\n const safeValue = sanitizeAttrValue(tag, name, value);\n if (safeValue === null) continue;\n out[name] = safeValue;\n }\n if (tag === 'a' && out.target === '_blank') {\n out.rel = ensureRelTokens(out.rel, ['noopener', 'noreferrer']);\n }\n return out;\n}\n\nfunction isAllowedAttr(tag: string, name: string): boolean {\n if (!SAFE_ATTR_NAME_RE.test(name)) return false;\n if (name.startsWith('on')) return false;\n if (name.startsWith('aria-') || name.startsWith('data-')) return true;\n if (GLOBAL_ATTRS.has(name)) return true;\n return TAG_ATTRS[tag]?.has(name) ?? false;\n}\n\nfunction sanitizeAttrValue(tag: string, name: string, value: string): string | null {\n if (name === 'href') return sanitizeUrl(value, 'link');\n if (name === 'src' || name === 'poster') return sanitizeUrl(value, 'media');\n if (\n (name === 'width' ||\n name === 'height' ||\n name === 'span' ||\n name === 'colspan' ||\n name === 'rowspan' ||\n name === 'value') &&\n !isNonNegativeInteger(value)\n ) {\n return null;\n }\n if (name === 'start' && !isInteger(value)) return null;\n if (name === 'type' && tag === 'ol' && !SAFE_OL_TYPES.has(value)) return null;\n if (name === 'preload' && !SAFE_PRELOAD_VALUES.has(value)) return null;\n if (name === 'kind' && tag === 'track' && !SAFE_TRACK_KINDS.has(value)) return null;\n if (name === 'align' && value !== 'left' && value !== 'right' && value !== 'center') return null;\n if (name === 'target')\n return value === '_blank' || value === '_self' || value === '_parent' || value === '_top'\n ? value\n : null;\n if (name === 'rel') return sanitizeRel(value);\n return value;\n}\n\nfunction sanitizeRel(value: string): string {\n return relTokens(value).join(' ');\n}\n\nfunction ensureRelTokens(value: string | undefined, required: string[]): string {\n const tokens = new Set(value ? relTokens(value) : []);\n for (const token of required) tokens.add(token);\n return Array.from(tokens).join(' ');\n}\n\nfunction relTokens(value: string): string[] {\n const tokens = value\n .split(/\\s+/)\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean)\n .filter((token) => /^[a-z0-9_-]+$/.test(token));\n return Array.from(new Set(tokens));\n}\n\nfunction isInteger(value: string): boolean {\n return /^-?\\d+$/.test(value.trim());\n}\n\nfunction isNonNegativeInteger(value: string): boolean {\n return /^\\d+$/.test(value.trim());\n}\n\nfunction firstIndexOfAny(value: string, needles: string[]): number {\n let result = -1;\n for (const needle of needles) {\n const index = value.indexOf(needle);\n if (index >= 0 && (result < 0 || index < result)) result = index;\n }\n return result;\n}\n\nfunction stripUrlSchemeNoise(value: string): string {\n let out = '';\n for (const char of value) {\n const code = char.charCodeAt(0);\n if (code <= 0x20 || code === 0x7f || char.trim() === '') continue;\n out += char;\n }\n return out;\n}\n"],"mappings":"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","names":[]}
@@ -159,13 +159,42 @@ function countNodes(node) {
159
159
  function parseFrontmatter(yaml) {
160
160
  if (!yaml || !yaml.trim()) return null;
161
161
  const result = {};
162
- for (const line of yaml.split("\n")) {
162
+ const lines = yaml.split("\n");
163
+ for (let i = 0; i < lines.length; i++) {
164
+ const line = lines[i];
163
165
  const trimmed = line.trim();
164
166
  if (!trimmed || trimmed.startsWith("#")) continue;
165
167
  const colonIdx = trimmed.indexOf(":");
166
168
  if (colonIdx < 1) continue;
167
169
  const key = trimmed.slice(0, colonIdx).trim();
168
170
  let value = trimmed.slice(colonIdx + 1).trim();
171
+ if (value === "|" || value === "|-") {
172
+ const keyIndent = line.length - line.trimStart().length;
173
+ const blockLines = [];
174
+ let j = i + 1;
175
+ for (; j < lines.length; j++) {
176
+ const bl = lines[j];
177
+ if (bl.trim() === "") {
178
+ blockLines.push("");
179
+ continue;
180
+ }
181
+ const indent = bl.length - bl.trimStart().length;
182
+ if (indent <= keyIndent) break;
183
+ blockLines.push(bl);
184
+ }
185
+ i = j - 1;
186
+ while (blockLines.length > 0 && blockLines[blockLines.length - 1] === "") {
187
+ blockLines.pop();
188
+ }
189
+ let commonIndent = Infinity;
190
+ for (const bl of blockLines) {
191
+ if (bl === "") continue;
192
+ commonIndent = Math.min(commonIndent, bl.length - bl.trimStart().length);
193
+ }
194
+ if (!isFinite(commonIndent)) commonIndent = 0;
195
+ result[key] = blockLines.map((bl) => bl === "" ? "" : bl.slice(commonIndent)).join("\n");
196
+ continue;
197
+ }
169
198
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
170
199
  value = value.slice(1, -1);
171
200
  }
@@ -181,9 +210,15 @@ function parseFrontmatter(yaml) {
181
210
  }
182
211
  return Object.keys(result).length > 0 ? result : null;
183
212
  }
213
+ function formatBlockScalar(value) {
214
+ const body = value.split("\n").map((line) => line === "" ? "" : ` ${line}`).join("\n");
215
+ return `|-
216
+ ${body}`;
217
+ }
184
218
  var FRONTMATTER_BLOCK_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n)?/;
185
219
  function formatFrontmatterValue(value) {
186
220
  if (typeof value === "boolean" || typeof value === "number") return String(value);
221
+ if (/[\r\n]/.test(value)) return formatBlockScalar(value.replace(/\r\n?/g, "\n"));
187
222
  if (/^[{[]/.test(value) && !/[\r\n]/.test(value)) return value;
188
223
  const needsQuote = /^\s|\s$/.test(value) || /^[!&*?|>%@`]/.test(value) || /[:#]/.test(value) || value === "" || value === "true" || value === "false" || /^-?\d+(\.\d+)?$/.test(value);
189
224
  if (!needsQuote) return value;
@@ -317,10 +352,11 @@ export {
317
352
  extractPlainText,
318
353
  countNodes,
319
354
  parseFrontmatter,
355
+ formatBlockScalar,
320
356
  setFrontmatterValues,
321
357
  createDocument,
322
358
  inferDocumentTitle,
323
359
  readFrontmatterThemeId,
324
360
  plainTextFromInlineHtml
325
361
  };
326
- //# sourceMappingURL=chunk-OKJOQXK4.js.map
362
+ //# sourceMappingURL=chunk-HWVFJAAH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markdown/htmlParse.ts","../src/markdown/utils.ts"],"sourcesContent":["/**\n * HTML ↔ HtmlNode Conversion\n *\n * Parses raw HTML strings into the squisq HtmlNode tree representation\n * using hast-util-from-html (parse5-backed), and serializes HtmlNode trees\n * back to HTML strings with a lightweight custom serializer.\n */\n\nimport { fromHtml } from 'hast-util-from-html';\nimport type { HtmlNode } from './types.js';\n\n// ============================================\n// Hast → HtmlNode conversion\n// ============================================\n\n/**\n * Mapping from hast DOM-style property names to HTML attribute names.\n * Only includes names where the property and attribute differ.\n */\nconst HAST_PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n httpEquiv: 'http-equiv',\n acceptCharset: 'accept-charset',\n};\n\n/** HTML void elements that are self-closing. */\nconst VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n]);\n\n/**\n * Convert a hast property name to an HTML attribute name.\n */\nfunction propertyToAttribute(prop: string): string {\n if (HAST_PROP_TO_ATTR[prop]) return HAST_PROP_TO_ATTR[prop];\n\n // data-* attributes: dataFooBar → data-foo-bar\n if (prop.length > 4 && prop.startsWith('data') && prop[4] >= 'A' && prop[4] <= 'Z') {\n return (\n 'data-' +\n prop\n .slice(4)\n .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())\n .replace(/^-/, '')\n );\n }\n\n // aria-* attributes: ariaLabel → aria-label\n if (prop.length > 4 && prop.startsWith('aria') && prop[4] >= 'A' && prop[4] <= 'Z') {\n return (\n 'aria-' +\n prop\n .slice(4)\n .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())\n .replace(/^-/, '')\n );\n }\n\n // Most HTML attributes are lowercase (tabIndex → tabindex, readOnly → readonly, etc.)\n return prop.toLowerCase();\n}\n\n/**\n * Convert hast element properties to a flat HTML attribute map.\n */\nfunction convertHastProperties(properties: Record<string, unknown>): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (value === false || value === undefined || value === null) continue;\n\n const attrName = propertyToAttribute(key);\n\n if (value === true) {\n attrs[attrName] = '';\n } else if (Array.isArray(value)) {\n attrs[attrName] = value.join(' ');\n } else {\n attrs[attrName] = String(value);\n }\n }\n return attrs;\n}\n\n/**\n * Recursively convert hast child nodes to HtmlNode[].\n */\nfunction hastChildrenToNodes(children: unknown[]): HtmlNode[] {\n const result: HtmlNode[] = [];\n for (const child of children) {\n const node = hastNodeToHtmlNode(child as Record<string, unknown>);\n if (node) result.push(node);\n }\n return result;\n}\n\n/**\n * Convert a single hast node to an HtmlNode.\n */\nfunction hastNodeToHtmlNode(node: Record<string, unknown>): HtmlNode | null {\n switch (node.type) {\n case 'element': {\n const tagName = node.tagName as string;\n const properties = (node.properties ?? {}) as Record<string, unknown>;\n const children = (node.children ?? []) as unknown[];\n return {\n type: 'htmlElement',\n tagName,\n attributes: convertHastProperties(properties),\n children: hastChildrenToNodes(children),\n selfClosing: VOID_ELEMENTS.has(tagName) && children.length === 0,\n };\n }\n case 'text':\n return {\n type: 'htmlText',\n value: node.value as string,\n };\n case 'comment':\n return {\n type: 'htmlComment',\n value: node.value as string,\n };\n case 'doctype':\n // Doctype nodes are rare in markdown HTML; skip them\n return null;\n default:\n return null;\n }\n}\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Parse a raw HTML string into an HtmlNode[] tree.\n *\n * Uses the HTML5 parsing algorithm (via parse5) for correct handling of\n * self-closing tags, optional end tags, entity decoding, etc.\n *\n * @param html - Raw HTML string to parse\n * @returns Array of top-level HtmlNode elements\n */\nexport function parseHtmlToNodes(html: string): HtmlNode[] {\n if (!html || !html.trim()) return [];\n\n try {\n const tree = fromHtml(html, { fragment: true });\n return hastChildrenToNodes(tree.children as unknown[]);\n } catch {\n // If parsing fails, return the raw text as a single text node\n return [{ type: 'htmlText', value: html }];\n }\n}\n\n/**\n * Escape special characters in HTML attribute values.\n */\nfunction escapeHtmlAttr(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n\n/**\n * Serialize an HtmlNode back to an HTML string.\n */\nfunction stringifyNode(node: HtmlNode): string {\n switch (node.type) {\n case 'htmlElement': {\n const attrs = Object.entries(node.attributes)\n .map(([k, v]) => (v === '' ? k : `${k}=\"${escapeHtmlAttr(v)}\"`))\n .join(' ');\n const open = attrs ? `<${node.tagName} ${attrs}>` : `<${node.tagName}>`;\n\n if (node.selfClosing) return open;\n\n const inner = node.children.map(stringifyNode).join('');\n return `${open}${inner}</${node.tagName}>`;\n }\n case 'htmlText':\n return node.value;\n case 'htmlComment':\n return `<!--${node.value}-->`;\n }\n}\n\n/**\n * Serialize an HtmlNode[] tree back to an HTML string.\n *\n * @param nodes - Array of HtmlNode elements to serialize\n * @returns HTML string\n */\nexport function stringifyHtmlNodes(nodes: HtmlNode[]): string {\n return nodes.map(stringifyNode).join('');\n}\n","/**\n * Markdown Tree Utilities\n *\n * Helper functions for traversing and querying the MarkdownDocument tree.\n * All functions are pure and operate on the JSON node interfaces.\n */\n\nimport type { MarkdownNode, MarkdownDocument, HtmlNode } from './types.js';\nimport { parseHtmlToNodes } from './htmlParse.js';\n\n/**\n * Get the children of any markdown node, if it has children.\n * Returns an empty array for leaf nodes (text, code, break, etc.).\n *\n * This is useful for generic tree walking where you don't want to\n * check the specific node type.\n */\nexport function getChildren(node: MarkdownNode): MarkdownNode[] {\n if ('children' in node && Array.isArray(node.children)) {\n return node.children as MarkdownNode[];\n }\n return [];\n}\n\n/**\n * Walk the markdown tree depth-first, calling the visitor for each node.\n *\n * The visitor receives the current node and its parent. Return `true`\n * from the visitor to skip the node's children (prune).\n *\n * @param node - The root node to start walking from\n * @param visitor - Called for each node; return true to skip children\n * @param parent - (internal) Parent node\n */\nexport function walkMarkdownTree(\n node: MarkdownNode,\n visitor: (node: MarkdownNode, parent?: MarkdownNode) => void | boolean,\n parent?: MarkdownNode,\n): void {\n const skip = visitor(node, parent);\n if (skip === true) return;\n\n const children = getChildren(node);\n for (const child of children) {\n walkMarkdownTree(child, visitor, node);\n }\n}\n\n/**\n * Find all nodes of a specific type in the tree.\n *\n * @param root - The document or node to search within\n * @param type - The node type to find (e.g., 'heading', 'link', 'text')\n * @returns Array of matching nodes\n *\n * @example\n * ```ts\n * const headings = findNodesByType(doc, 'heading');\n * const links = findNodesByType(doc, 'link');\n * ```\n */\nexport function findNodesByType<T extends MarkdownNode>(root: MarkdownNode, type: T['type']): T[] {\n const results: T[] = [];\n walkMarkdownTree(root, (node) => {\n if (node.type === type) {\n results.push(node as T);\n }\n });\n return results;\n}\n\n/**\n * Extract all plain text content from a node and its descendants.\n * Concatenates text values, ignoring formatting, links, etc.\n *\n * @param node - The node to extract text from\n * @returns Plain text content\n *\n * @example\n * ```ts\n * const heading = doc.children[0]; // { type: 'heading', children: [{ type: 'text', value: 'Hello' }] }\n * extractPlainText(heading); // 'Hello'\n * ```\n */\nexport function extractPlainText(node: MarkdownNode): string {\n if ('value' in node && typeof node.value === 'string') {\n return node.value;\n }\n\n const children = getChildren(node);\n // Preserve boundaries between block-level elements (list items, paragraphs\n // inside list items, blockquotes) so downstream consumers like caption\n // splitting can treat each item as a separate phrase.\n const separator = node.type === 'list' || node.type === 'listItem' ? '\\n' : '';\n return children.map(extractPlainText).join(separator);\n}\n\n/**\n * Count the total number of nodes in the tree.\n *\n * @param node - The root node\n * @returns Total node count (including the root)\n */\nexport function countNodes(node: MarkdownNode): number {\n let count = 1;\n const children = getChildren(node);\n for (const child of children) {\n count += countNodes(child);\n }\n return count;\n}\n\n/**\n * Parse a YAML frontmatter string into a key-value record.\n *\n * Handles simple `key: value` pairs common in markdown frontmatter.\n * Values are trimmed; quoted strings have their quotes removed.\n * Returns `null` if parsing fails or the input is empty.\n *\n * @param yaml - The raw YAML string (without the `---` delimiters)\n * @returns A record of string keys to parsed values, or null\n */\nexport function parseFrontmatter(yaml: string): Record<string, unknown> | null {\n if (!yaml || !yaml.trim()) return null;\n\n const result: Record<string, unknown> = {};\n const lines = yaml.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx < 1) continue;\n\n const key = trimmed.slice(0, colonIdx).trim();\n let value: string | boolean | number = trimmed.slice(colonIdx + 1).trim();\n\n // YAML literal block scalar: `key: |` or `key: |-`. Subsequent lines\n // that are more-indented than the key belong to the scalar; they are\n // dedented by their common leading indent and joined with `\\n`. A line\n // at the key's indent (or less) ends the scalar. Both `|` and `|-`\n // yield the joined string here (`|-` without a trailing newline); we do\n // not model `|`'s trailing-newline / `+`/`-` chomping nuances beyond\n // stripping trailing blank lines.\n if (value === '|' || value === '|-') {\n const keyIndent = line.length - line.trimStart().length;\n const blockLines: string[] = [];\n let j = i + 1;\n for (; j < lines.length; j++) {\n const bl = lines[j];\n if (bl.trim() === '') {\n blockLines.push('');\n continue;\n }\n const indent = bl.length - bl.trimStart().length;\n if (indent <= keyIndent) break; // dedent ends the scalar\n blockLines.push(bl);\n }\n i = j - 1; // resume the outer walk at the terminating line\n\n // Drop trailing blank lines captured inside the scalar.\n while (blockLines.length > 0 && blockLines[blockLines.length - 1] === '') {\n blockLines.pop();\n }\n\n // Dedent by the common leading whitespace of the non-blank lines.\n let commonIndent = Infinity;\n for (const bl of blockLines) {\n if (bl === '') continue;\n commonIndent = Math.min(commonIndent, bl.length - bl.trimStart().length);\n }\n if (!isFinite(commonIndent)) commonIndent = 0;\n result[key] = blockLines.map((bl) => (bl === '' ? '' : bl.slice(commonIndent))).join('\\n');\n continue;\n }\n\n // Remove surrounding quotes\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n // Parse booleans and numbers\n if (value === 'true') {\n result[key] = true;\n } else if (value === 'false') {\n result[key] = false;\n } else if (value !== '' && !isNaN(Number(value))) {\n result[key] = Number(value);\n } else {\n result[key] = value;\n }\n }\n\n return Object.keys(result).length > 0 ? result : null;\n}\n\n/**\n * Format a multi-line string as a YAML literal block scalar body, i.e.\n * `|-` followed by each source line indented two spaces. Paired with\n * {@link parseFrontmatter}'s block-scalar reader so multi-line frontmatter\n * values (e.g. pretty-printed JSON payloads) round-trip. Blank lines stay\n * blank (no trailing whitespace) so the output diffs cleanly.\n */\nexport function formatBlockScalar(value: string): string {\n const body = value\n .split('\\n')\n .map((line) => (line === '' ? '' : ` ${line}`))\n .join('\\n');\n return `|-\\n${body}`;\n}\n\nconst FRONTMATTER_BLOCK_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(\\r?\\n)?/;\n\n/** Quote a frontmatter scalar so it round-trips cleanly through `parseFrontmatter`. */\nfunction formatFrontmatterValue(value: string | number | boolean): string {\n if (typeof value === 'boolean' || typeof value === 'number') return String(value);\n // Multi-line strings can't live on one line — emit a YAML literal block\n // scalar (`|-`) that `parseFrontmatter` reads back. This carries pretty-\n // printed JSON payloads (opt-in custom templates/themes). Single-line\n // values keep their exact historical output below.\n if (/[\\r\\n]/.test(value)) return formatBlockScalar(value.replace(/\\r\\n?/g, '\\n'));\n // A single-line JSON object/array literal round-trips verbatim through\n // the line-based `parseFrontmatter` (no leading quote to strip, no\n // newline to split on), so write it unquoted to keep it human-readable\n // — matching how `stringifyMarkdown` emits string values. Used by the\n // compact custom-layouts payload.\n if (/^[{[]/.test(value) && !/[\\r\\n]/.test(value)) return value;\n // Quote when needed: leading/trailing whitespace, leading punctuation that\n // could trigger YAML modes, or values that look like reserved literals.\n const needsQuote =\n /^\\s|\\s$/.test(value) ||\n /^[!&*?|>%@`]/.test(value) ||\n /[:#]/.test(value) ||\n value === '' ||\n value === 'true' ||\n value === 'false' ||\n /^-?\\d+(\\.\\d+)?$/.test(value);\n if (!needsQuote) return value;\n // Prefer double quotes; escape any embedded double-quote / backslash.\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `\"${escaped}\"`;\n}\n\n/**\n * Update a markdown source string's YAML frontmatter block, applying the\n * given key/value updates. A `null` or `undefined` value removes the key.\n * If no frontmatter block exists and any non-null update is supplied, a\n * new block is prepended; if every update is a removal and no block\n * exists, the source is returned unchanged.\n *\n * Existing key order is preserved; new keys are appended in the order\n * they appear in `updates`. The simple parser used here handles plain\n * `key: value` pairs only — comments, multi-line scalars, and nested\n * structures are left untouched (we only rewrite lines whose key matches\n * one in `updates`).\n *\n * @param source - The markdown source string.\n * @param updates - Map of key → new value (or null/undefined to remove).\n * @returns The updated markdown source string.\n */\nexport function setFrontmatterValues(\n source: string,\n updates: Record<string, string | number | boolean | null | undefined>,\n): string {\n const updateKeys = Object.keys(updates);\n if (updateKeys.length === 0) return source;\n\n const match = source.match(FRONTMATTER_BLOCK_RE);\n const handled = new Set<string>();\n\n // Existing frontmatter block — rewrite line-by-line, preserving order.\n if (match) {\n const inner = match[1];\n const lines = inner.split(/\\r?\\n/);\n const newLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) {\n newLines.push(line);\n continue;\n }\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx < 1) {\n newLines.push(line);\n continue;\n }\n const key = trimmed.slice(0, colonIdx).trim();\n if (!Object.prototype.hasOwnProperty.call(updates, key)) {\n newLines.push(line);\n continue;\n }\n handled.add(key);\n const next = updates[key];\n if (next === null || next === undefined) continue; // remove\n newLines.push(`${key}: ${formatFrontmatterValue(next)}`);\n }\n\n // Append any keys that weren't already present.\n for (const key of updateKeys) {\n if (handled.has(key)) continue;\n const next = updates[key];\n if (next === null || next === undefined) continue;\n newLines.push(`${key}: ${formatFrontmatterValue(next)}`);\n }\n\n // If every line got removed, drop the block entirely.\n const nonEmpty = newLines.filter((l) => l.trim().length > 0);\n const rest = source.slice(match[0].length);\n if (nonEmpty.length === 0) return rest;\n return `---\\n${newLines.join('\\n')}\\n---\\n${rest}`;\n }\n\n // No existing block — only create one if there's at least one non-null\n // value to write.\n const fresh: string[] = [];\n for (const key of updateKeys) {\n const next = updates[key];\n if (next === null || next === undefined) continue;\n fresh.push(`${key}: ${formatFrontmatterValue(next)}`);\n }\n if (fresh.length === 0) return source;\n return `---\\n${fresh.join('\\n')}\\n---\\n${source}`;\n}\n\n/**\n * Create a minimal MarkdownDocument from a list of block nodes.\n * Convenience function for programmatic document construction.\n */\nexport function createDocument(...children: MarkdownDocument['children']): MarkdownDocument {\n return {\n type: 'document',\n children,\n };\n}\n\n/**\n * Infer a human-readable title for a markdown document.\n *\n * Resolution order:\n * 1. `title:` value in YAML frontmatter (when present and non-empty)\n * 2. Text of the first heading found, walking top-level depth (H1, then\n * H2, etc.) — the shallowest heading wins regardless of source order\n *\n * Returns `undefined` when no usable title is found.\n */\nexport function inferDocumentTitle(doc: MarkdownDocument): string | undefined {\n const fmTitle = doc.frontmatter?.title;\n if (typeof fmTitle === 'string') {\n const trimmed = fmTitle.trim();\n if (trimmed) return trimmed;\n }\n\n let best: { depth: number; text: string } | null = null;\n for (const node of doc.children) {\n if (node.type !== 'heading') continue;\n const text = extractPlainText(node).trim();\n if (!text) continue;\n if (best === null || node.depth < best.depth) {\n best = { depth: node.depth, text };\n if (best.depth === 1) break;\n }\n }\n\n return best?.text;\n}\n\n/**\n * Read a theme id from a markdown document's frontmatter.\n *\n * Checks the editor's canonical `squisq-theme` key first, then the\n * shorter legacy aliases (`themeId`, `theme`). Returns `undefined`\n * when none of them carry a non-empty string. Centralizing the lookup\n * keeps the export pipelines (DOCX, PPTX, HTML, plain-HTML bundle) in\n * sync with whatever the editor writes — adding a new frontmatter\n * spelling later is a one-file change.\n */\nexport function readFrontmatterThemeId(\n frontmatter: Record<string, unknown> | undefined,\n): string | undefined {\n if (!frontmatter) return undefined;\n const keys = ['squisq-theme', 'themeId', 'theme'] as const;\n for (const key of keys) {\n const raw = frontmatter[key];\n if (typeof raw === 'string') {\n const trimmed = raw.trim();\n if (trimmed) return trimmed;\n }\n }\n return undefined;\n}\n\n/**\n * Project an inline-HTML string (as stored on `TextLayer.content.html`) down\n * to plain text — the value mirrored into `content.text` for plain consumers\n * (PDF/markdown export, search, accessibility) and the SVG `<text>` fallback.\n *\n * `<br>` and block-level tags (`p`, `div`, `h1`–`h6`, `li`, `blockquote`,\n * `pre`) become line breaks so multi-line rich content reads sensibly. Entity\n * decoding is handled by {@link parseHtmlToNodes} (parse5-backed), so this is\n * more correct than a regex tag-stripper.\n */\nexport function plainTextFromInlineHtml(html: string): string {\n const BLOCK = new Set([\n 'p',\n 'div',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'li',\n 'blockquote',\n 'pre',\n ]);\n const walk = (nodes: HtmlNode[]): string =>\n nodes\n .map((n) => {\n if (n.type === 'htmlText') return n.value;\n if (n.type !== 'htmlElement') return '';\n const tag = n.tagName.toLowerCase();\n if (tag === 'br') return '\\n';\n const inner = walk(n.children);\n return BLOCK.has(tag) ? `${inner}\\n` : inner;\n })\n .join('');\n return walk(parseHtmlToNodes(html))\n .replace(/\\u00A0/g, ' ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n"],"mappings":"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","names":[]}
@@ -367,4 +367,4 @@ export {
367
367
  scopeContainer,
368
368
  createMediaProviderFromContainer
369
369
  };
370
- //# sourceMappingURL=chunk-2TMAWB4Y.js.map
370
+ //# sourceMappingURL=chunk-IMSCRLLV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage/MemoryStorageAdapter.ts","../src/storage/LocalStorageAdapter.ts","../src/storage/LocalForageAdapter.ts","../src/storage/ContentContainer.ts","../src/storage/ScopedContentContainer.ts","../src/storage/MediaProviderFromContainer.ts"],"sourcesContent":["/**\n * In-memory storage adapter for testing and SSR environments.\n * Data does not persist across page reloads.\n */\n\nimport type { StorageAdapter } from './Storage.js';\n\nexport class MemoryStorageAdapter implements StorageAdapter {\n readonly supportsEnumeration = true;\n private store = new Map<string, string>();\n\n async get<T>(key: string): Promise<T | null> {\n const val = this.store.get(key);\n if (val === undefined) return null;\n try {\n return JSON.parse(val) as T;\n } catch {\n return null;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n this.store.set(key, JSON.stringify(value));\n }\n\n async remove(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n async keys(): Promise<string[]> {\n return Array.from(this.store.keys());\n }\n}\n","/**\n * localStorage adapter for web browsers.\n * Supports a configurable key prefix to namespace stored data.\n */\n\nimport type { StorageAdapter } from './Storage.js';\n\nexport class LocalStorageAdapter implements StorageAdapter {\n readonly supportsEnumeration = true;\n private prefix: string;\n\n constructor(prefix = '') {\n this.prefix = prefix;\n }\n\n async get<T>(key: string): Promise<T | null> {\n try {\n const raw = localStorage.getItem(this.prefix + key);\n if (raw === null) return null;\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n localStorage.setItem(this.prefix + key, JSON.stringify(value));\n }\n\n async remove(key: string): Promise<void> {\n localStorage.removeItem(this.prefix + key);\n }\n\n async clear(): Promise<void> {\n const keysToRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(this.prefix)) {\n keysToRemove.push(k);\n }\n }\n for (const k of keysToRemove) {\n localStorage.removeItem(k);\n }\n }\n\n async keys(): Promise<string[]> {\n const result: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(this.prefix)) {\n result.push(k.slice(this.prefix.length));\n }\n }\n return result;\n }\n}\n","/**\n * LocalForage adapter - IndexedDB storage with automatic fallbacks.\n *\n * Uses localforage which provides IndexedDB storage with automatic\n * fallbacks to WebSQL and localStorage. This allows storing much more\n * data than raw localStorage (which is limited to ~5MB).\n *\n * Unlike LocalStorageAdapter, this adapter can store binary data\n * (ArrayBuffer, Blob, Uint8Array) natively without JSON serialization.\n *\n * Ported from qualla-internal/shared/services/StorageAdapters.ts with\n * configurable store name and key prefix.\n */\n\nimport localforage from 'localforage';\nimport type { StorageAdapter } from './Storage.js';\n\nexport interface LocalForageAdapterOptions {\n /** IndexedDB database name (default: 'squisq') */\n name?: string;\n /** IndexedDB object store name (default: 'data') */\n storeName?: string;\n /** Key prefix for namespacing (default: '') */\n prefix?: string;\n /** Human-readable description (default: 'Squisq storage') */\n description?: string;\n}\n\nexport class LocalForageAdapter implements StorageAdapter {\n readonly supportsEnumeration = true;\n private store: LocalForage;\n private prefix: string;\n\n constructor(options: LocalForageAdapterOptions = {}) {\n const {\n name = 'squisq',\n storeName = 'data',\n prefix = '',\n description = 'Squisq storage',\n } = options;\n\n this.prefix = prefix;\n this.store = localforage.createInstance({ name, storeName, description });\n }\n\n async get<T>(key: string): Promise<T | null> {\n try {\n const value = await this.store.getItem<T>(this.prefix + key);\n return value;\n } catch (e: unknown) {\n console.warn('[Storage] get (IndexedDB) failed:', key, e);\n return null;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n try {\n await this.store.setItem(this.prefix + key, value);\n } catch (e: unknown) {\n console.error('[Storage] set (IndexedDB) failed:', key, e);\n throw e;\n }\n }\n\n async remove(key: string): Promise<void> {\n try {\n await this.store.removeItem(this.prefix + key);\n } catch (e: unknown) {\n console.warn('[Storage] remove (IndexedDB) failed:', key, e);\n }\n }\n\n async clear(): Promise<void> {\n try {\n if (this.prefix) {\n // Only remove keys with our prefix\n const keys = await this.store.keys();\n const keysToRemove = keys.filter((k) => k.startsWith(this.prefix));\n await Promise.all(keysToRemove.map((k) => this.store.removeItem(k)));\n } else {\n // No prefix — clear the entire store\n await this.store.clear();\n }\n } catch (e: unknown) {\n console.error('[Storage] clear (IndexedDB) failed:', e);\n }\n }\n\n async keys(): Promise<string[]> {\n try {\n const allKeys = await this.store.keys();\n if (this.prefix) {\n return allKeys\n .filter((k) => k.startsWith(this.prefix))\n .map((k) => k.slice(this.prefix.length));\n }\n return allKeys;\n } catch (e: unknown) {\n console.warn('[Storage] keys (IndexedDB) failed:', e);\n return [];\n }\n }\n}\n","/**\n * ContentContainer — virtual file system for document containers.\n *\n * A Squisq document is more than just markdown: it's a container of files\n * including the primary markdown document, images, audio, timing data, and\n * other media. ContentContainer provides an abstract async file system\n * interface for reading, writing, and listing these files.\n *\n * Paths are forward-slash separated strings relative to the container root\n * (e.g., 'images/hero.jpg', 'index.md'). No leading slash.\n *\n * Implementations (in core):\n * - MemoryContentContainer — in-memory Map (for zip import, tests, transient use)\n * - ScopedContentContainer — a prefix-scoped view onto a parent container\n */\n\n/**\n * Metadata about a file in a ContentContainer.\n */\nexport interface ContentEntry {\n /** Relative path within the container (e.g., 'images/hero.jpg') */\n path: string;\n /** MIME type (e.g., 'image/jpeg') */\n mimeType: string;\n /** File size in bytes */\n size: number;\n}\n\n/**\n * Abstract async file system for document containers.\n *\n * All paths are forward-slash separated, relative to the container root,\n * with no leading slash. Example: 'images/hero.jpg', 'index.md'.\n */\nexport interface ContentContainer {\n /** Read a file's binary content. Returns null if the file does not exist. */\n readFile(path: string): Promise<ArrayBuffer | null>;\n\n /** Write a file. Creates or overwrites. */\n writeFile(path: string, data: ArrayBuffer | Uint8Array, mimeType?: string): Promise<void>;\n\n /** Remove a file. No-op if the file does not exist. */\n removeFile(path: string): Promise<void>;\n\n /**\n * List files in the container.\n *\n * Returns file **entries** (`ContentEntry[]` with `.path`/`.mimeType`/`.size`),\n * NOT bare path strings — despite the name. Read `entry.path` for the path;\n * calling string methods (e.g. `.startsWith`) directly on an entry will throw.\n * @param prefix — Optional path prefix to filter by (e.g., 'images/')\n */\n listFiles(prefix?: string): Promise<ContentEntry[]>;\n\n /** Check whether a file exists. */\n exists(path: string): Promise<boolean>;\n\n /**\n * Find the primary markdown document path.\n *\n * Discovery order: index.md → doc.md → document.md → first *.md at root.\n * Returns null if no markdown file is found at the root level.\n */\n getDocumentPath(): Promise<string | null>;\n\n /**\n * Convenience: read the primary markdown document as a UTF-8 string.\n * Returns null if no markdown file is found.\n */\n readDocument(): Promise<string | null>;\n\n /**\n * Convenience: write a markdown document.\n * @param markdown — The markdown content\n * @param filename — Filename to use (defaults to 'index.md')\n */\n writeDocument(markdown: string, filename?: string): Promise<void>;\n}\n\n// ============================================\n// Well-known markdown filenames in priority order\n// ============================================\n\nconst MARKDOWN_PRIORITY = ['index.md', 'doc.md', 'document.md'];\n\n/**\n * Find the primary markdown path from a list of file entries.\n * Exported for reuse by other ContentContainer implementations.\n */\nexport function findDocumentPath(entries: ContentEntry[]): string | null {\n // Only consider root-level files (no '/' in path)\n const rootFiles = entries.filter((e) => !e.path.includes('/'));\n\n for (const name of MARKDOWN_PRIORITY) {\n if (rootFiles.some((e) => e.path.toLowerCase() === name)) {\n return name;\n }\n }\n\n // Fallback: first .md file at root\n const firstMd = rootFiles.find((e) => e.path.toLowerCase().endsWith('.md'));\n return firstMd?.path ?? null;\n}\n\n// ============================================\n// MemoryContentContainer\n// ============================================\n\ninterface MemoryFile {\n data: ArrayBuffer;\n mimeType: string;\n}\n\n/**\n * In-memory ContentContainer backed by a Map.\n *\n * Used for zip import (deserialize into memory), tests, and transient operations.\n */\nexport class MemoryContentContainer implements ContentContainer {\n private files = new Map<string, MemoryFile>();\n\n async readFile(path: string): Promise<ArrayBuffer | null> {\n return this.files.get(path)?.data ?? null;\n }\n\n async writeFile(path: string, data: ArrayBuffer | Uint8Array, mimeType?: string): Promise<void> {\n // Copy to a standalone ArrayBuffer. Uint8Array/Buffer may share a larger\n // backing buffer (e.g., Node's buffer pool), so we must slice to the\n // exact byte range to avoid storing stale pool data.\n let buffer: ArrayBuffer;\n if (data instanceof ArrayBuffer) {\n buffer = data;\n } else {\n buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer;\n }\n this.files.set(path, {\n data: buffer,\n mimeType: mimeType ?? guessMimeType(path),\n });\n }\n\n async removeFile(path: string): Promise<void> {\n this.files.delete(path);\n }\n\n async listFiles(prefix?: string): Promise<ContentEntry[]> {\n const entries: ContentEntry[] = [];\n for (const [path, file] of this.files) {\n if (prefix && !path.startsWith(prefix)) continue;\n entries.push({\n path,\n mimeType: file.mimeType,\n size: file.data.byteLength,\n });\n }\n return entries;\n }\n\n async exists(path: string): Promise<boolean> {\n return this.files.has(path);\n }\n\n async getDocumentPath(): Promise<string | null> {\n return findDocumentPath(await this.listFiles());\n }\n\n async readDocument(): Promise<string | null> {\n const docPath = await this.getDocumentPath();\n if (!docPath) return null;\n const data = await this.readFile(docPath);\n if (!data) return null;\n return new TextDecoder().decode(data);\n }\n\n async writeDocument(markdown: string, filename?: string): Promise<void> {\n const name = filename ?? 'index.md';\n const data = new TextEncoder().encode(markdown);\n await this.writeFile(name, data, 'text/markdown');\n }\n}\n\n// ============================================\n// MIME type guessing\n// ============================================\n\nconst EXTENSION_MIME_MAP: Record<string, string> = {\n '.md': 'text/markdown',\n '.txt': 'text/plain',\n '.json': 'application/json',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png': 'image/png',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.ogg': 'audio/ogg',\n '.css': 'text/css',\n '.html': 'text/html',\n '.js': 'application/javascript',\n};\n\nfunction guessMimeType(path: string): string {\n const dot = path.lastIndexOf('.');\n if (dot === -1) return 'application/octet-stream';\n const ext = path.slice(dot).toLowerCase();\n return EXTENSION_MIME_MAP[ext] ?? 'application/octet-stream';\n}\n","/**\n * ScopedContentContainer\n *\n * Wraps a parent {@link ContentContainer} and prefixes every path with a\n * fixed subfolder, presenting that subfolder as the root. Useful for\n * sidecar folders (e.g. `<imagebasename>_files/`) where a component\n * should be able to read/write/list files relative to its own root\n * without knowing where it lives in the parent tree.\n *\n * Scopes nest: `scopeContainer(scopeContainer(parent, 'a'), 'b')` is\n * equivalent to `scopeContainer(parent, 'a/b')`.\n *\n * `getDocumentPath()` always returns `null` for scoped sidecars — they\n * are not document roots.\n */\n\nimport type { ContentContainer, ContentEntry } from './ContentContainer.js';\n\n/** Normalize a prefix: strip leading/trailing slashes, ensure no `..` segments. */\nfunction normalizePrefix(prefix: string): string {\n let p = prefix.trim();\n while (p.startsWith('/')) p = p.slice(1);\n while (p.endsWith('/')) p = p.slice(0, -1);\n if (p.length === 0) {\n throw new Error('ScopedContentContainer: prefix must not be empty');\n }\n if (p.split('/').some((seg) => seg === '..' || seg === '.')) {\n throw new Error(\n `ScopedContentContainer: prefix must not contain '.' or '..' segments: ${prefix}`,\n );\n }\n return p;\n}\n\n/**\n * A ContentContainer view rooted at a subfolder of a parent container.\n * All paths passed to / returned from this container are relative to\n * the subfolder; the parent never sees an unprefixed path.\n */\nexport class ScopedContentContainer implements ContentContainer {\n private readonly parent: ContentContainer;\n /** Prefix without trailing slash. */\n readonly prefix: string;\n /** Prefix with trailing slash. */\n private readonly prefixSlash: string;\n\n constructor(parent: ContentContainer, prefix: string) {\n this.parent = parent;\n this.prefix = normalizePrefix(prefix);\n this.prefixSlash = `${this.prefix}/`;\n }\n\n private toParent(path: string): string {\n let p = path;\n while (p.startsWith('/')) p = p.slice(1);\n return this.prefixSlash + p;\n }\n\n /** Strip the prefix from a parent-space path. Returns null if the path is outside the scope. */\n private fromParent(parentPath: string): string | null {\n if (!parentPath.startsWith(this.prefixSlash)) return null;\n return parentPath.slice(this.prefixSlash.length);\n }\n\n readFile(path: string): Promise<ArrayBuffer | null> {\n return this.parent.readFile(this.toParent(path));\n }\n\n writeFile(path: string, data: ArrayBuffer | Uint8Array, mimeType?: string): Promise<void> {\n return this.parent.writeFile(this.toParent(path), data, mimeType);\n }\n\n removeFile(path: string): Promise<void> {\n return this.parent.removeFile(this.toParent(path));\n }\n\n async listFiles(prefix?: string): Promise<ContentEntry[]> {\n const parentPrefix = prefix ? this.toParent(prefix) : this.prefixSlash;\n const entries = await this.parent.listFiles(parentPrefix);\n const out: ContentEntry[] = [];\n for (const e of entries) {\n const local = this.fromParent(e.path);\n if (local === null) continue;\n out.push({ path: local, mimeType: e.mimeType, size: e.size });\n }\n return out;\n }\n\n exists(path: string): Promise<boolean> {\n return this.parent.exists(this.toParent(path));\n }\n\n /** Sidecars are not document roots. */\n async getDocumentPath(): Promise<string | null> {\n return null;\n }\n\n async readDocument(): Promise<string | null> {\n return null;\n }\n\n async writeDocument(): Promise<void> {\n throw new Error(\n 'ScopedContentContainer is a sidecar view and does not support writeDocument(). ' +\n 'Write to a specific path with writeFile() instead.',\n );\n }\n}\n\n/** Factory: convenience wrapper for `new ScopedContentContainer(parent, prefix)`. */\nexport function scopeContainer(parent: ContentContainer, prefix: string): ScopedContentContainer {\n return new ScopedContentContainer(parent, prefix);\n}\n","/**\n * MediaProviderFromContainer — bridges ContentContainer to MediaProvider.\n *\n * Creates a MediaProvider that resolves relative paths by reading binary data\n * from a ContentContainer and generating blob URLs. Blob URLs are cached and\n * revoked on dispose().\n *\n * This allows any ContentContainer (memory, slot-backed, zip-loaded) to be\n * used with existing rendering components (DocPlayer, ImageLayer, VideoLayer)\n * that consume MediaProvider.\n */\n\nimport type { MediaProvider, MediaEntry } from '../schemas/MediaProvider.js';\nimport type { ContentContainer } from './ContentContainer.js';\n\n/**\n * Create a MediaProvider backed by a ContentContainer.\n *\n * @param container — The ContentContainer to read/write media from\n * @returns A MediaProvider that resolves paths to blob URLs\n */\nexport function createMediaProviderFromContainer(container: ContentContainer): MediaProvider {\n const blobUrlCache = new Map<string, string>();\n\n return {\n async resolveUrl(relativePath: string): Promise<string> {\n const cached = blobUrlCache.get(relativePath);\n if (cached) return cached;\n\n const data = await container.readFile(relativePath);\n if (!data) return relativePath;\n\n const entries = await container.listFiles();\n const entry = entries.find((e) => e.path === relativePath);\n const mimeType = entry?.mimeType ?? 'application/octet-stream';\n\n const blob = new Blob([data], { type: mimeType });\n const url = URL.createObjectURL(blob);\n blobUrlCache.set(relativePath, url);\n return url;\n },\n\n async listMedia(): Promise<MediaEntry[]> {\n const entries = await container.listFiles();\n return entries\n .filter((e) => !e.path.toLowerCase().endsWith('.md'))\n .map((e) => ({\n name: e.path,\n mimeType: e.mimeType,\n size: e.size,\n }));\n },\n\n async addMedia(\n name: string,\n data: ArrayBuffer | Blob | Uint8Array,\n mimeType: string,\n ): Promise<string> {\n // Invalidate any cached blob URL for this path before overwriting\n const cached = blobUrlCache.get(name);\n if (cached) {\n URL.revokeObjectURL(cached);\n blobUrlCache.delete(name);\n }\n\n let buffer: ArrayBuffer | Uint8Array;\n if (data instanceof Blob) {\n buffer = await data.arrayBuffer();\n } else {\n buffer = data;\n }\n await container.writeFile(name, buffer, mimeType);\n return name;\n },\n\n async removeMedia(relativePath: string): Promise<void> {\n const cached = blobUrlCache.get(relativePath);\n if (cached) {\n URL.revokeObjectURL(cached);\n blobUrlCache.delete(relativePath);\n }\n await container.removeFile(relativePath);\n },\n\n dispose(): void {\n for (const url of blobUrlCache.values()) {\n URL.revokeObjectURL(url);\n }\n blobUrlCache.clear();\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1747,4 +1747,4 @@ export {
1747
1747
  buildGoogleFontsUrl,
1748
1748
  FONT_FALLBACKS
1749
1749
  };
1750
- //# sourceMappingURL=chunk-4AKUQPEE.js.map
1750
+ //# sourceMappingURL=chunk-KKNUBQ6Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/CustomTemplates.ts","../src/schemas/Theme.ts","../src/schemas/colorUtils.ts","../src/schemas/themeValidator.ts","../src/schemas/themes/standard.json","../src/schemas/themes/standard-dark.json","../src/schemas/themes/documentary.json","../src/schemas/themes/minimalist.json","../src/schemas/themes/bold.json","../src/schemas/themes/morning-light.json","../src/schemas/themes/tech-dark.json","../src/schemas/themes/magazine.json","../src/schemas/themes/cinematic.json","../src/schemas/themes/warm-earth.json","../src/schemas/themes/gezellig.json","../src/schemas/themes/index.ts","../src/schemas/themeLibrary.ts","../src/schemas/fontStacks.ts"],"sourcesContent":["/**\n * Custom Templates Schema\n *\n * User-defined block layout templates. The author designs a visual\n * layout once (in the editor's TemplateDesigner) and saves it as a\n * `CustomTemplateDefinition`. The definition lives either in the\n * document's frontmatter (per-doc, portable, ships with the markdown)\n * or in the user's local library (cross-doc reuse via localStorage).\n *\n * A custom template's content is just a `Layer[]` — the same shape that\n * built-in templates emit at expansion time. The novel bit is\n * *placeholder tokens*: TextLayer / ImageLayer content fields may carry\n * strings like `{title}`, `{content}`, `{children}`, or `{image:N}`\n * that the resolver (see `doc/templates/tokens/resolveTokens.ts`)\n * substitutes against the source Block at render time.\n *\n * Responsiveness model: every numeric position field in `layers` is\n * stored as a `%`-string relative to `viewport`, so the same template\n * renders correctly at any of Squisq's viewport presets (landscape,\n * portrait, square). The `viewport` field itself is purely an\n * authoring-time reference — it tells the designer canvas which aspect\n * ratio to mount with, and the thumbnail renderer which viewBox to use.\n */\n\nimport type { Layer } from './Doc.js';\n\n/**\n * Iteration directive for a custom-template layer.\n *\n * A layer that carries a `repeat` is cloned once per item of a source\n * collection at render time (see `resolveTokens`). The clones are laid\n * out in a row or column, offset from the base position by the layer's\n * own width/height plus `gap`, and each clone resolves per-item tokens\n * (`{item}`, `{item:src}`, `{item:label}`, `{index}`) against its item.\n */\nexport interface LayerRepeat {\n /**\n * Which of the source Block's collections to iterate:\n * - `images` — every image in the block body (alt + url)\n * - `children` — child block titles\n * - `listItems` — top-level list-item text in the block body\n */\n source: 'images' | 'children' | 'listItems';\n\n /**\n * Layout axis for the clones. `column` (default) stacks them\n * vertically (offsets `position.y`); `row` lays them out horizontally\n * (offsets `position.x`).\n */\n direction?: 'row' | 'column';\n\n /**\n * Extra spacing added between consecutive clones, in the same unit as\n * the layer's `width`/`height`. Defaults to 24.\n */\n gap?: number;\n\n /** Cap on the number of clones rendered (omit for \"all items\"). */\n max?: number;\n}\n\n/**\n * A custom-template layer: a normal render `Layer` that may additionally\n * carry a `repeat` directive. This is a strict superset of the core\n * `Layer` type used only inside `CustomTemplateDefinition.layers`; the\n * resolver strips `repeat` before emitting the final `Layer[]`, so the\n * core `Layer` type is never widened.\n */\nexport type CustomTemplateLayer = Layer & { repeat?: LayerRepeat };\n\n/**\n * A single user-defined template. Stored in `Doc.customTemplates`\n * (frontmatter-backed) or in the user's local library.\n */\nexport interface CustomTemplateDefinition {\n /**\n * Stable id used in heading annotations (`### Foo {[name]}`). Must be\n * a slug — lowercase alphanumerics, hyphens. The picker enforces\n * uniqueness within a (doc + library) merged namespace.\n */\n name: string;\n\n /** Human-readable label shown in the template picker. */\n label: string;\n\n /** Optional one-sentence description for the picker card. */\n description?: string;\n\n /**\n * Authoring viewport — the canvas size the designer drew against.\n * Stored so the designer and thumbnail renderer can reproduce the\n * same canvas. Layer positions are stored as `%`-strings, so the\n * actual render-time viewport can differ from this without breaking\n * the layout. Defaults to 1920×1080 landscape.\n */\n viewport: {\n width: number;\n height: number;\n };\n\n /**\n * The visual content. Every `position.x` / `position.y` /\n * `position.width` / `position.height` should be a `%`-string at\n * save time (the designer's normalizePositions step enforces this).\n *\n * Tokens supported (grammar v2):\n * - TextLayer.content.text — `{title}`, `{content}`, `{children}`,\n * `{image:N}` (Nth image's alt text), `{attr:key}` (block\n * attribute), plus a pipe default on any token\n * (`{attr:subtitle|Untitled}`).\n * - ImageLayer.content.src — `{image:N}` (the URL) and `{attr:key}`,\n * with pipe defaults (`{image:1|fallback.jpg}`).\n * - Inside a layer with `repeat`, per-item tokens: `{item}`,\n * `{item:src}`, `{item:label}`, `{index}` (1-based).\n *\n * A layer may carry a `repeat` directive (see {@link LayerRepeat}) to\n * be cloned once per item of a source collection.\n */\n layers: CustomTemplateLayer[];\n}\n\n/**\n * Tag value for the YAML frontmatter key that carries inlined custom\n * template definitions. Exported so the markdown bridge stays in sync.\n */\nexport const FRONTMATTER_CUSTOM_TEMPLATES_KEY = 'squisq-custom-templates';\n","/**\n * Theme System\n *\n * A Theme bundles color palette, typography, visual style, render-style\n * algorithm, and per-block color schemes into one JSON-serializable object.\n *\n * Built-in themes and customizer-authored themes share this exact schema.\n * Customizer-authored themes additionally specify `seedColors` so the few\n * fields the user picked can be re-edited later; built-ins typically omit\n * `seedColors` and ship with explicit `colors` already filled in.\n *\n * Design principles:\n * - Fully JSON-serializable. No functions; no raw CSS font strings.\n * - Font families are structured `FontFamily` references resolved by\n * `resolveFontFamily()` at render time.\n * - Doc carries an optional `themeId`; resolution happens at render time\n * via `resolveTheme(id)`.\n * - `createTheme` deep-merges a base theme with partial overrides;\n * `compileTheme` fills in defaults and derives missing color slots from\n * `seedColors`; both produce a complete `Theme`.\n */\n\nimport type { LayoutHints } from './LayoutStrategy.js';\nimport type { AnimationType, ImageTreatment, TransitionType } from './Doc.js';\nimport type { PersistentLayerConfig } from './BlockTemplates.js';\n\n// ============================================\n// Schema version\n// ============================================\n\n/** Current Theme schema version. Bump on breaking changes; loader migrates. */\nexport const THEME_SCHEMA_VERSION = '1' as const;\nexport type ThemeSchemaVersion = typeof THEME_SCHEMA_VERSION;\n\n/**\n * Frontmatter key under which a document inlines its custom Theme\n * definitions. The theme analog of `FRONTMATTER_CUSTOM_TEMPLATES_KEY`\n * (custom block templates). See `doc/customThemesFrontmatter.ts` for the\n * codec and `doc/resolveDocTheme.ts` for doc-scoped resolution.\n */\nexport const FRONTMATTER_CUSTOM_THEMES_KEY = 'squisq-custom-themes';\n\n// ============================================\n// Color Palette\n// ============================================\n\n/**\n * Core color palette for a theme. Every color is a `#rrggbb` hex string.\n */\nexport interface ThemeColorPalette {\n /** Primary accent color */\n primary: string;\n /** Secondary accent color */\n secondary: string;\n /** Background color (typically dark) */\n background: string;\n /** Lighter background for contrast panels */\n backgroundLight: string;\n /** Main text color */\n text: string;\n /** Muted/secondary text color */\n textMuted: string;\n /** Highlight/emphasis color */\n highlight: string;\n /** Warning/alert color */\n warning: string;\n}\n\n/**\n * A named color scheme used by templates for per-block color variation\n * (e.g., statHighlight or sectionHeader).\n */\nexport interface ThemeColorScheme {\n /** Background fill */\n bg: string;\n /** Primary text tint */\n text: string;\n /** Accent / secondary tint */\n accent: string;\n}\n\n/**\n * Optional authoring metadata. When present and `colors` is partial,\n * `compileTheme` derives missing color slots from these seeds via OKLCh\n * lightening / darkening. Round-trips a customizer-authored theme back\n * into editable form.\n */\nexport interface ThemeSeedColors {\n primary: string;\n secondary?: string;\n accent?: string;\n background?: string;\n text?: string;\n}\n\n// ============================================\n// Typography\n// ============================================\n\n/** Categorical bucket for a font family — drives default fallback. */\nexport type FontFamilyKind = 'sans' | 'serif' | 'mono' | 'display';\n\n/**\n * Structured reference to a font family. Either a curated stack id (looked\n * up in `AVAILABLE_FONT_STACKS`) or a custom user-supplied name with a\n * structured fallback bucket. Themes never carry raw CSS family strings.\n */\nexport type FontFamily =\n | { stackId: string }\n | {\n custom: {\n name: string;\n fallback: 'serif' | 'sans-serif' | 'monospace' | 'system-ui';\n };\n };\n\n/**\n * Typography settings for a theme.\n */\nexport interface ThemeTypography {\n /** Font family for body / description text */\n bodyFont: FontFamily;\n /** Font family for titles and headings */\n titleFont: FontFamily;\n /** Font family for code / monospaced text (optional) */\n monoFont?: FontFamily;\n /** Multiplier applied to LayoutHints.titleScale (default 1.0) */\n titleScale?: number;\n /** Multiplier applied to LayoutHints.bodyScale (default 1.0) */\n bodyScale?: number;\n /** Default body line height (default 1.4) */\n lineHeight?: number;\n /** Default title line height */\n titleLineHeight?: number;\n /** Default title font weight */\n titleWeight?: 'normal' | 'bold';\n}\n\n// ============================================\n// Visual Style\n// ============================================\n\n/**\n * Global visual-style knobs that templates consult.\n */\nexport interface ThemeStyle {\n /** Default border radius for cards / shapes (px) */\n borderRadius?: number;\n /** Whether templates should default to text shadows */\n textShadow?: boolean;\n /** Darkness of overlay on image-backed blocks (0–1) */\n overlayOpacity?: number;\n /** Multiplier on all animation durations (1.0 = normal, <1 faster, >1 slower) */\n animationSpeed?: number;\n /** Default horizontal padding for text (percentage string, e.g. \"5%\") */\n blockPadding?: string;\n /**\n * Photographic grade applied to template imagery (full-bleed photos,\n * feature halves, accent strips) so photos look native to the theme.\n * Duotone tints default to the theme primary. Blocks opt out per\n * template via `imageTreatment: 'none'`.\n */\n imageTreatment?: ImageTreatment;\n}\n\n// ============================================\n// Render Style\n// ============================================\n\n/**\n * Render-style algorithm preset. Controls layout tweaks, default animations,\n * transitions, and per-template behavioral hints.\n */\nexport interface RenderStyle {\n /** Identifier for this algorithmic approach (e.g. \"documentary\", \"magazine\") */\n name: string;\n /** Partial overrides merged onto the orientation-based LayoutHints */\n layoutOverrides?: Partial<LayoutHints>;\n /** Default entrance animation for text layers */\n defaultTextAnimation?: AnimationType;\n /** Default animation for background / image layers */\n defaultImageAnimation?: AnimationType;\n /** Whether to apply Ken Burns ambient motion to images by default */\n ambientMotion?: boolean;\n /** Default block-to-block transition */\n defaultTransition?: { type: TransitionType; duration?: number };\n /**\n * Per-template behavioral hints. Keys are template names, values are\n * string/number/boolean maps that templates can read to vary their output.\n * Templates that consume hints publish a `hintSchema` export documenting\n * the keys they recognise.\n */\n templateHints?: Record<string, Record<string, string | number | boolean>>;\n}\n\n// ============================================\n// Theme\n// ============================================\n\n/**\n * A complete, JSON-serializable theme definition. Built-in and customizer-\n * authored themes share this exact shape.\n */\nexport interface Theme {\n /** Schema version. Always '1' for now. */\n schemaVersion: ThemeSchemaVersion;\n /** Unique identifier (e.g. \"documentary\") */\n id: string;\n /** Human-readable display name */\n name: string;\n /** Short description for theme pickers */\n description?: string;\n /**\n * Id of the theme this custom theme was derived from, if any. Recorded\n * by `compileTheme({ base })` so a customizer can re-open the theme and\n * re-inherit the base's render style / color schemes / typography.\n * Absent on built-ins.\n */\n basedOn?: string;\n /** Optional authoring seeds (customizer fills these; built-ins typically omit) */\n seedColors?: ThemeSeedColors;\n /** Color palette */\n colors: ThemeColorPalette;\n /** Typography settings */\n typography: ThemeTypography;\n /** Global visual-style knobs */\n style: ThemeStyle;\n /** Algorithmic render-style preset */\n renderStyle: RenderStyle;\n /**\n * Named color schemes for per-block color variation.\n * Templates reference these by name (e.g. \"blue\", \"warm\").\n */\n colorSchemes: Record<string, ThemeColorScheme>;\n /** Optional persistent layers baked into the theme */\n persistentLayers?: PersistentLayerConfig;\n}\n\n// ============================================\n// Deep-Partial helper type\n// ============================================\n\n/**\n * Recursively makes every property optional.\n */\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Deep-merge `source` into `target`, returning a new object.\n * Arrays are replaced wholesale (not concatenated).\n */\nfunction deepMerge<T extends Record<string, unknown>>(target: T, source: DeepPartial<T>): T {\n const result = { ...target } as Record<string, unknown>;\n for (const key of Object.keys(source)) {\n const srcVal = (source as Record<string, unknown>)[key];\n const tgtVal = (target as Record<string, unknown>)[key];\n if (\n srcVal !== null &&\n srcVal !== undefined &&\n typeof srcVal === 'object' &&\n !Array.isArray(srcVal) &&\n tgtVal !== null &&\n tgtVal !== undefined &&\n typeof tgtVal === 'object' &&\n !Array.isArray(tgtVal)\n ) {\n result[key] = deepMerge(\n tgtVal as Record<string, unknown>,\n srcVal as DeepPartial<Record<string, unknown>>,\n );\n } else if (srcVal !== undefined) {\n result[key] = srcVal;\n }\n }\n return result as T;\n}\n\n/**\n * Create a new theme by deep-merging overrides onto a base theme.\n * The returned theme gets its own `id` if one is provided in overrides,\n * otherwise keeps the base theme's `id` suffixed with \"-custom\".\n */\nexport function createTheme(base: Theme, overrides: DeepPartial<Theme>): Theme {\n const merged = deepMerge(\n base as unknown as Record<string, unknown>,\n overrides as DeepPartial<Record<string, unknown>>,\n ) as unknown as Theme;\n if (!overrides.id && merged.id === base.id) {\n merged.id = `${base.id}-custom`;\n }\n if (!merged.schemaVersion) merged.schemaVersion = THEME_SCHEMA_VERSION;\n return merged;\n}\n\n// ============================================\n// Surface Schemes — orthogonal to Theme\n// ============================================\n\n/**\n * A Surface Scheme is the light/dark axis, orthogonal to the editorial\n * identity of a Theme. A Theme chooses voice (serif vs sans, muted vs\n * bold, documentary vs magazine); a SurfaceScheme chooses what the paper\n * looks like. Any theme can render on either surface.\n */\nexport interface SurfaceScheme {\n /** Identifier — 'light', 'dark', or a custom id. */\n id: string;\n background: string;\n backgroundLight: string;\n text: string;\n textMuted: string;\n}\n\n/** Near-white paper with dark text — standard light mode. */\nexport const LIGHT_SURFACE: SurfaceScheme = {\n id: 'light',\n background: '#ffffff',\n backgroundLight: '#f5f5f5',\n text: '#1a1a1a',\n textMuted: '#666666',\n};\n\n/** Dark charcoal paper with light text — standard dark mode. */\nexport const DARK_SURFACE: SurfaceScheme = {\n id: 'dark',\n background: '#1a202c',\n backgroundLight: '#2d3748',\n text: '#f0f0f0',\n textMuted: '#a0aec0',\n};\n\n/**\n * Overlay a SurfaceScheme's surface colors onto a Theme's palette,\n * leaving everything else (primary, highlight, warning, typography,\n * style, renderStyle, colorSchemes) untouched.\n */\nexport function applySurface(theme: Theme, surface: SurfaceScheme): Theme {\n return {\n ...theme,\n colors: {\n ...theme.colors,\n background: surface.background,\n backgroundLight: surface.backgroundLight,\n text: surface.text,\n textMuted: surface.textMuted,\n },\n };\n}\n\n// ============================================\n// Runtime registry — for custom themes\n// ============================================\n\nconst CUSTOM_THEME_REGISTRY = new Map<string, Theme>();\n\n/**\n * Register a Theme so it can be looked up by id via `resolveTheme(id)`.\n * Lets `Doc.themeId` round-trip through Doc serialization for custom themes.\n *\n * Registered themes take precedence over built-ins with the same id.\n */\nexport function registerTheme(theme: Theme): void {\n CUSTOM_THEME_REGISTRY.set(theme.id, theme);\n}\n\n/** Remove a previously registered theme. */\nexport function unregisterTheme(id: string): void {\n CUSTOM_THEME_REGISTRY.delete(id);\n}\n\n/** Snapshot of all currently registered (non-built-in) themes. */\nexport function getRegisteredThemes(): Theme[] {\n return Array.from(CUSTOM_THEME_REGISTRY.values());\n}\n\n/** @internal — used by themeLibrary's `resolveTheme` to check the registry first. */\nexport function lookupRegisteredTheme(id: string | undefined): Theme | undefined {\n if (!id) return undefined;\n return CUSTOM_THEME_REGISTRY.get(id);\n}\n","/**\n * Color Utilities\n *\n * Pure-JS color math used by theme compilation and contrast checks.\n * No dependencies. Operates on `#rrggbb` hex strings.\n *\n * Internally uses OKLCh — perceptually uniform, so lightening by 0.1\n * looks like the same change at any starting point. Out-of-gamut\n * results are clamped to sRGB.\n */\n\nconst HEX_RE = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;\n\n/** Test whether a string is a valid `#rgb` or `#rrggbb` hex color. */\nexport function isHex(value: string): boolean {\n return HEX_RE.test(value);\n}\n\n/** Parse `#rgb` or `#rrggbb` into 0..1 sRGB triplet. Returns null on failure. */\nfunction parseHex(hex: string): [number, number, number] | null {\n if (!HEX_RE.test(hex)) return null;\n let body = hex.slice(1);\n if (body.length === 3)\n body = body\n .split('')\n .map((c) => c + c)\n .join('');\n return [\n parseInt(body.slice(0, 2), 16) / 255,\n parseInt(body.slice(2, 4), 16) / 255,\n parseInt(body.slice(4, 6), 16) / 255,\n ];\n}\n\nfunction toHex(rgb: [number, number, number]): string {\n const ch = (v: number) => {\n const c = Math.max(0, Math.min(255, Math.round(v * 255)));\n return c.toString(16).padStart(2, '0');\n };\n return '#' + ch(rgb[0]) + ch(rgb[1]) + ch(rgb[2]);\n}\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n}\n\ninterface OKLCh {\n L: number;\n C: number;\n h: number;\n}\n\nfunction rgbToOklch(rgb: [number, number, number]): OKLCh {\n const [sr, sg, sb] = rgb;\n const r = srgbToLinear(sr);\n const g = srgbToLinear(sg);\n const b = srgbToLinear(sb);\n\n const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;\n const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;\n const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;\n\n const l_ = Math.cbrt(l);\n const m_ = Math.cbrt(m);\n const s_ = Math.cbrt(s);\n\n const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;\n const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;\n const bb = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;\n\n const C = Math.sqrt(a * a + bb * bb);\n let h = (Math.atan2(bb, a) * 180) / Math.PI;\n if (h < 0) h += 360;\n return { L, C, h };\n}\n\nfunction oklchToRgb({ L, C, h }: OKLCh): [number, number, number] {\n const hr = (h * Math.PI) / 180;\n const a = C * Math.cos(hr);\n const bb = C * Math.sin(hr);\n\n const l_ = L + 0.3963377774 * a + 0.2158037573 * bb;\n const m_ = L - 0.1055613458 * a - 0.0638541728 * bb;\n const s_ = L - 0.0894841775 * a - 1.291485548 * bb;\n\n const l = l_ * l_ * l_;\n const m = m_ * m_ * m_;\n const s = s_ * s_ * s_;\n\n const r = +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;\n const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;\n const b = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;\n\n return [\n Math.max(0, Math.min(1, linearToSrgb(r))),\n Math.max(0, Math.min(1, linearToSrgb(g))),\n Math.max(0, Math.min(1, linearToSrgb(b))),\n ];\n}\n\n/**\n * Lighten a color by `amount` (0..1) in OKLCh L space.\n * Falls back to the input on parse failure.\n */\nexport function oklchLighten(hex: string, amount: number): string {\n const rgb = parseHex(hex);\n if (!rgb) return hex;\n const lch = rgbToOklch(rgb);\n lch.L = Math.max(0, Math.min(1, lch.L + amount));\n return toHex(oklchToRgb(lch));\n}\n\n/**\n * Darken a color by `amount` (0..1) in OKLCh L space.\n * Falls back to the input on parse failure.\n */\nexport function oklchDarken(hex: string, amount: number): string {\n return oklchLighten(hex, -amount);\n}\n\n/**\n * Adjust the OKLCh chroma of a color by a multiplier (e.g. 0.7 to mute).\n */\nexport function oklchSetChroma(hex: string, multiplier: number): string {\n const rgb = parseHex(hex);\n if (!rgb) return hex;\n const lch = rgbToOklch(rgb);\n lch.C = Math.max(0, lch.C * multiplier);\n return toHex(oklchToRgb(lch));\n}\n\n/**\n * WCAG relative luminance of a hex color, returned as 0..1.\n */\nexport function relativeLuminance(hex: string): number {\n const rgb = parseHex(hex);\n if (!rgb) return 0;\n const [r, g, b] = rgb.map(srgbToLinear) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/**\n * Hue of a hex color in HSL degrees (0..360). Returns 0 for pure grays\n * and on parse failure. Used by the duotone image treatment to aim its\n * `hue-rotate()` at the theme's tint color.\n */\nexport function hexHueDegrees(hex: string): number {\n const rgb = parseHex(hex);\n if (!rgb) return 0;\n const [r, g, b] = rgb;\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n if (max === min) return 0;\n const d = max - min;\n let h: number;\n if (max === r) h = ((g - b) / d) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n return Math.round((h * 60 + 360) % 360);\n}\n\n/**\n * Convert a hex color + alpha (0..1) to an `rgba()` string.\n * Falls back to the input on parse failure (e.g. a value that is\n * already an rgba()/gradient string).\n */\nexport function withAlpha(hex: string, alpha: number): string {\n const rgb = parseHex(hex);\n if (!rgb) return hex;\n const a = Math.max(0, Math.min(1, alpha));\n const ch = (v: number) => Math.round(v * 255);\n return `rgba(${ch(rgb[0])}, ${ch(rgb[1])}, ${ch(rgb[2])}, ${a})`;\n}\n\n/**\n * WCAG contrast ratio between two hex colors. Returns 1..21.\n */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick a near-white or near-black text color that contrasts well against `bg`.\n */\nexport function pickContrastingText(bg: string, light = '#f7fafc', dark = '#1a202c'): string {\n return contrastRatio(bg, dark) >= contrastRatio(bg, light) ? dark : light;\n}\n\n/**\n * Derive a 5-step lighter→darker scale from a seed color.\n * Stops are evenly spaced in OKLCh L space around the seed.\n * `spread` controls amplitude (0.05 = subtle, 0.15 = balanced, 0.22 = high).\n */\nexport function deriveScale(\n seedHex: string,\n spread = 0.15,\n): {\n lighter2: string;\n lighter1: string;\n base: string;\n darker1: string;\n darker2: string;\n} {\n return {\n lighter2: oklchLighten(seedHex, spread),\n lighter1: oklchLighten(seedHex, spread / 2),\n base: seedHex,\n darker1: oklchDarken(seedHex, spread / 2),\n darker2: oklchDarken(seedHex, spread),\n };\n}\n","/**\n * Theme Validator\n *\n * Hand-rolled validator for `Theme` JSON. No dependencies. Produces a\n * flat list of typed errors with JSON paths, suitable for surfacing in\n * the customizer UI or throwing at module init for built-in themes.\n *\n * Design: walk the candidate object, push errors for each violation.\n * Coverage is exhaustive for required fields and shapes; permissive on\n * forward-compatible fields (unknown keys are ignored, not rejected).\n */\n\nimport type { Theme, ThemeColorPalette } from './Theme.js';\nimport { THEME_SCHEMA_VERSION } from './Theme.js';\nimport { isHex } from './colorUtils.js';\nimport { isTransitionType } from './Transitions.js';\n\nexport interface ValidationError {\n path: string;\n message: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n /** Present only when `valid === true`. The same input typed as `Theme`. */\n theme?: Theme;\n}\n\nconst VALID_FALLBACK = new Set(['serif', 'sans-serif', 'monospace', 'system-ui']);\nconst VALID_TITLE_WEIGHT = new Set(['normal', 'bold']);\nconst VALID_GRADIENT_PRESETS = new Set([\n 'dark-vignette',\n 'radial-dark',\n 'warm-sunset',\n 'cool-blue',\n 'earth-tones',\n]);\n\nclass V {\n errors: ValidationError[] = [];\n err(path: string, message: string): void {\n this.errors.push({ path, message });\n }\n isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n }\n isString(v: unknown): v is string {\n return typeof v === 'string';\n }\n isNumber(v: unknown): v is number {\n return typeof v === 'number' && Number.isFinite(v);\n }\n isBoolean(v: unknown): v is boolean {\n return typeof v === 'boolean';\n }\n hex(path: string, v: unknown): void {\n if (!this.isString(v)) {\n this.err(path, `expected hex color string, got ${typeof v}`);\n } else if (!isHex(v)) {\n this.err(path, `expected #rgb or #rrggbb hex color, got \"${v}\"`);\n }\n }\n\n palette(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n const required: (keyof ThemeColorPalette)[] = [\n 'primary',\n 'secondary',\n 'background',\n 'backgroundLight',\n 'text',\n 'textMuted',\n 'highlight',\n 'warning',\n ];\n for (const k of required) this.hex(`${path}.${k}`, v[k]);\n }\n\n colorScheme(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n this.hex(`${path}.bg`, v.bg);\n this.hex(`${path}.text`, v.text);\n this.hex(`${path}.accent`, v.accent);\n }\n\n colorSchemes(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object map');\n return;\n }\n for (const key of Object.keys(v)) {\n this.colorScheme(`${path}.${key}`, v[key]);\n }\n }\n\n fontFamily(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected FontFamily object ({ stackId } or { custom })');\n return;\n }\n if ('stackId' in v) {\n if (!this.isString(v.stackId) || v.stackId.length === 0) {\n this.err(`${path}.stackId`, 'expected non-empty string');\n }\n return;\n }\n if ('custom' in v) {\n const c = v.custom;\n if (!this.isObject(c)) {\n this.err(`${path}.custom`, 'expected { name, fallback } object');\n return;\n }\n if (!this.isString(c.name) || c.name.length === 0) {\n this.err(`${path}.custom.name`, 'expected non-empty string');\n }\n if (!this.isString(c.fallback) || !VALID_FALLBACK.has(c.fallback)) {\n this.err(\n `${path}.custom.fallback`,\n `expected one of: serif, sans-serif, monospace, system-ui`,\n );\n }\n return;\n }\n this.err(path, 'expected { stackId } or { custom: { name, fallback } }');\n }\n\n typography(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n this.fontFamily(`${path}.bodyFont`, v.bodyFont);\n this.fontFamily(`${path}.titleFont`, v.titleFont);\n if (v.monoFont !== undefined) this.fontFamily(`${path}.monoFont`, v.monoFont);\n for (const k of ['titleScale', 'bodyScale', 'lineHeight', 'titleLineHeight']) {\n if (v[k] !== undefined && !this.isNumber(v[k])) {\n this.err(`${path}.${k}`, `expected number, got ${typeof v[k]}`);\n }\n }\n if (v.titleWeight !== undefined) {\n if (!this.isString(v.titleWeight) || !VALID_TITLE_WEIGHT.has(v.titleWeight)) {\n this.err(`${path}.titleWeight`, 'expected \"normal\" or \"bold\"');\n }\n }\n }\n\n style(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n if (v.borderRadius !== undefined && !this.isNumber(v.borderRadius)) {\n this.err(`${path}.borderRadius`, 'expected number');\n }\n if (v.textShadow !== undefined && !this.isBoolean(v.textShadow)) {\n this.err(`${path}.textShadow`, 'expected boolean');\n }\n for (const k of ['overlayOpacity', 'animationSpeed']) {\n if (v[k] !== undefined && !this.isNumber(v[k])) {\n this.err(`${path}.${k}`, 'expected number');\n }\n }\n if (v.blockPadding !== undefined && !this.isString(v.blockPadding)) {\n this.err(`${path}.blockPadding`, 'expected string (percentage e.g. \"5%\")');\n }\n if (v.imageTreatment !== undefined) {\n this.imageTreatment(`${path}.imageTreatment`, v.imageTreatment);\n }\n }\n\n imageTreatment(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n const kinds = ['none', 'mono', 'duotone', 'warm', 'cool'];\n if (!this.isString(v.type) || !kinds.includes(v.type)) {\n this.err(`${path}.type`, `expected one of ${kinds.join('|')}`);\n }\n if (v.strength !== undefined && !this.isNumber(v.strength)) {\n this.err(`${path}.strength`, 'expected number (0..1)');\n }\n if (v.color !== undefined) this.hex(`${path}.color`, v.color);\n }\n\n renderStyle(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n if (!this.isString(v.name) || v.name.length === 0) {\n this.err(`${path}.name`, 'expected non-empty string');\n }\n if (v.ambientMotion !== undefined && !this.isBoolean(v.ambientMotion)) {\n this.err(`${path}.ambientMotion`, 'expected boolean');\n }\n for (const k of ['defaultTextAnimation', 'defaultImageAnimation']) {\n if (v[k] !== undefined && !this.isString(v[k])) {\n this.err(`${path}.${k}`, 'expected animation type string');\n }\n }\n if (v.defaultTransition !== undefined) {\n const t = v.defaultTransition;\n if (!this.isObject(t)) {\n this.err(`${path}.defaultTransition`, 'expected object');\n } else {\n if (!this.isString(t.type) || !isTransitionType(t.type)) {\n this.err(`${path}.defaultTransition.type`, 'expected valid transition type');\n }\n if (t.duration !== undefined && !this.isNumber(t.duration)) {\n this.err(`${path}.defaultTransition.duration`, 'expected number');\n }\n }\n }\n if (v.layoutOverrides !== undefined && !this.isObject(v.layoutOverrides)) {\n this.err(`${path}.layoutOverrides`, 'expected object');\n }\n if (v.templateHints !== undefined) {\n if (!this.isObject(v.templateHints)) {\n this.err(`${path}.templateHints`, 'expected object map');\n } else {\n for (const tplName of Object.keys(v.templateHints)) {\n const hints = v.templateHints[tplName];\n if (!this.isObject(hints)) {\n this.err(`${path}.templateHints.${tplName}`, 'expected object');\n continue;\n }\n for (const key of Object.keys(hints)) {\n const val = hints[key];\n const t = typeof val;\n if (t !== 'string' && t !== 'number' && t !== 'boolean') {\n this.err(\n `${path}.templateHints.${tplName}.${key}`,\n `expected string|number|boolean, got ${t}`,\n );\n }\n }\n }\n }\n }\n }\n\n seedColors(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n if (v.primary === undefined) {\n this.err(`${path}.primary`, 'required when seedColors is present');\n } else {\n this.hex(`${path}.primary`, v.primary);\n }\n for (const k of ['secondary', 'accent', 'background', 'text']) {\n if (v[k] !== undefined) this.hex(`${path}.${k}`, v[k]);\n }\n }\n\n persistentLayer(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n // Either { template, config } or a raw Layer — only validate the template form here\n if ('template' in v) {\n if (!this.isString(v.template)) {\n this.err(`${path}.template`, 'expected string');\n return;\n }\n const cfg = v.config;\n if (!this.isObject(cfg)) {\n this.err(`${path}.config`, 'expected object');\n return;\n }\n if (cfg.type !== v.template) {\n this.err(`${path}.config.type`, `expected \"${v.template}\", got \"${String(cfg.type)}\"`);\n }\n if (v.template === 'gradientBackground' && cfg.preset !== undefined) {\n if (!this.isString(cfg.preset) || !VALID_GRADIENT_PRESETS.has(cfg.preset)) {\n this.err(\n `${path}.config.preset`,\n `expected one of: ${Array.from(VALID_GRADIENT_PRESETS).join(', ')}`,\n );\n }\n }\n if (v.template === 'gradientBackground' && 'gradient' in cfg) {\n this.err(\n `${path}.config.gradient`,\n 'raw CSS gradient strings are not allowed in Theme JSON; use a `preset`',\n );\n }\n if (v.template === 'patternBackground') {\n const patterns = ['dots', 'grid', 'diagonal', 'noise'];\n if (!this.isString(cfg.pattern) || !patterns.includes(cfg.pattern)) {\n this.err(`${path}.config.pattern`, `expected one of ${patterns.join('|')}`);\n }\n for (const k of ['opacity', 'scale']) {\n if (cfg[k] !== undefined && !this.isNumber(cfg[k])) {\n this.err(`${path}.config.${k}`, 'expected number');\n }\n }\n if (cfg.color !== undefined) this.hex(`${path}.config.color`, cfg.color);\n }\n if (v.template === 'vignette') {\n if (cfg.strength !== undefined && !this.isNumber(cfg.strength)) {\n this.err(`${path}.config.strength`, 'expected number (0..1)');\n }\n if (cfg.color !== undefined) this.hex(`${path}.config.color`, cfg.color);\n }\n if (v.template === 'ambientGradient') {\n if (cfg.duration !== undefined && !this.isNumber(cfg.duration)) {\n this.err(`${path}.config.duration`, 'expected number (seconds)');\n }\n for (const k of ['from', 'to']) {\n if (cfg[k] !== undefined) this.hex(`${path}.config.${k}`, cfg[k]);\n }\n }\n }\n // Raw Layer form is permissive — no schema check here\n }\n\n persistentLayers(path: string, v: unknown): void {\n if (!this.isObject(v)) {\n this.err(path, 'expected object');\n return;\n }\n for (const side of ['bottomLayers', 'topLayers']) {\n const arr = v[side];\n if (arr === undefined) continue;\n if (!Array.isArray(arr)) {\n this.err(`${path}.${side}`, 'expected array');\n continue;\n }\n arr.forEach((entry, i) => this.persistentLayer(`${path}.${side}[${i}]`, entry));\n }\n }\n}\n\nexport function validateTheme(input: unknown): ValidationResult {\n const v = new V();\n if (!v.isObject(input)) {\n return { valid: false, errors: [{ path: '', message: 'expected object' }] };\n }\n\n if (input.schemaVersion !== THEME_SCHEMA_VERSION) {\n v.err(\n 'schemaVersion',\n `expected \"${THEME_SCHEMA_VERSION}\", got \"${String(input.schemaVersion)}\"`,\n );\n }\n if (!v.isString(input.id) || input.id.length === 0) {\n v.err('id', 'expected non-empty string');\n }\n if (!v.isString(input.name) || input.name.length === 0) {\n v.err('name', 'expected non-empty string');\n }\n if (input.description !== undefined && !v.isString(input.description)) {\n v.err('description', 'expected string');\n }\n if (input.basedOn !== undefined && !v.isString(input.basedOn)) {\n v.err('basedOn', 'expected string');\n }\n\n if (input.seedColors !== undefined) v.seedColors('seedColors', input.seedColors);\n\n if (input.colors === undefined) {\n v.err('colors', 'required');\n } else {\n v.palette('colors', input.colors);\n }\n\n if (input.typography === undefined) {\n v.err('typography', 'required');\n } else {\n v.typography('typography', input.typography);\n }\n\n if (input.style === undefined) {\n v.err('style', 'required');\n } else {\n v.style('style', input.style);\n }\n\n if (input.renderStyle === undefined) {\n v.err('renderStyle', 'required');\n } else {\n v.renderStyle('renderStyle', input.renderStyle);\n }\n\n if (input.colorSchemes === undefined) {\n v.err('colorSchemes', 'required');\n } else {\n v.colorSchemes('colorSchemes', input.colorSchemes);\n }\n\n if (input.persistentLayers !== undefined) {\n v.persistentLayers('persistentLayers', input.persistentLayers);\n }\n\n if (v.errors.length === 0) {\n return { valid: true, errors: [], theme: input as unknown as Theme };\n }\n return { valid: false, errors: v.errors };\n}\n\n/** Throws on invalid input. Useful at module init for built-in themes. */\nexport function assertTheme(input: unknown, source = 'theme'): Theme {\n const result = validateTheme(input);\n if (!result.valid || !result.theme) {\n const summary = result.errors\n .slice(0, 5)\n .map((e) => ` ${e.path || '<root>'}: ${e.message}`)\n .join('\\n');\n const more = result.errors.length > 5 ? `\\n ... and ${result.errors.length - 5} more` : '';\n throw new Error(`Invalid ${source}:\\n${summary}${more}`);\n }\n return result.theme;\n}\n\n// (Theme types are re-exported from `./Theme.js` via the schemas barrel.)\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"standard\",\n \"name\": \"Standard Light\",\n \"description\": \"Clean and safe — black-on-white, system fonts only, no external font dependencies. Default theme.\",\n \"colors\": {\n \"primary\": \"#2563eb\",\n \"secondary\": \"#475569\",\n \"background\": \"#ffffff\",\n \"backgroundLight\": \"#f1f5f9\",\n \"text\": \"#0f172a\",\n \"textMuted\": \"#475569\",\n \"highlight\": \"#1d4ed8\",\n \"warning\": \"#dc2626\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"system-sans\"\n },\n \"titleFont\": {\n \"stackId\": \"system-serif\"\n },\n \"monoFont\": {\n \"stackId\": \"system-mono\"\n },\n \"titleWeight\": \"bold\"\n },\n \"style\": {\n \"textShadow\": false,\n \"overlayOpacity\": 0.35,\n \"animationSpeed\": 1.0\n },\n \"renderStyle\": {\n \"name\": \"standard\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"ambientMotion\": false,\n \"defaultTransition\": {\n \"type\": \"fade\",\n \"duration\": 0.7\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#dbeafe\",\n \"text\": \"#1d4ed8\",\n \"accent\": \"#2563eb\"\n },\n \"green\": {\n \"bg\": \"#dcfce7\",\n \"text\": \"#166534\",\n \"accent\": \"#16a34a\"\n },\n \"purple\": {\n \"bg\": \"#ede9fe\",\n \"text\": \"#5b21b6\",\n \"accent\": \"#7c3aed\"\n },\n \"red\": {\n \"bg\": \"#fee2e2\",\n \"text\": \"#991b1b\",\n \"accent\": \"#dc2626\"\n },\n \"orange\": {\n \"bg\": \"#ffedd5\",\n \"text\": \"#9a3412\",\n \"accent\": \"#ea580c\"\n },\n \"teal\": {\n \"bg\": \"#ccfbf1\",\n \"text\": \"#115e59\",\n \"accent\": \"#0d9488\"\n }\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"standard-dark\",\n \"name\": \"Standard Dark\",\n \"description\": \"Standard, dark variant. System fonts, dark navy background, light text.\",\n \"colors\": {\n \"primary\": \"#3182ce\",\n \"secondary\": \"#4a5568\",\n \"background\": \"#1a202c\",\n \"backgroundLight\": \"#2d3748\",\n \"text\": \"#f7fafc\",\n \"textMuted\": \"#a0aec0\",\n \"highlight\": \"#4299e1\",\n \"warning\": \"#fc8181\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"system-sans\"\n },\n \"titleFont\": {\n \"stackId\": \"system-serif\"\n },\n \"monoFont\": {\n \"stackId\": \"system-mono\"\n },\n \"titleWeight\": \"bold\"\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.45,\n \"animationSpeed\": 1.0\n },\n \"renderStyle\": {\n \"name\": \"standard\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"ambientMotion\": false,\n \"defaultTransition\": {\n \"type\": \"fade\",\n \"duration\": 0.7\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#1a365d\",\n \"text\": \"#63b3ed\",\n \"accent\": \"#90cdf4\"\n },\n \"green\": {\n \"bg\": \"#22543d\",\n \"text\": \"#9ae6b4\",\n \"accent\": \"#68d391\"\n },\n \"purple\": {\n \"bg\": \"#44337a\",\n \"text\": \"#d6bcfa\",\n \"accent\": \"#b794f4\"\n },\n \"red\": {\n \"bg\": \"#742a2a\",\n \"text\": \"#fc8181\",\n \"accent\": \"#feb2b2\"\n },\n \"orange\": {\n \"bg\": \"#744210\",\n \"text\": \"#fbd38d\",\n \"accent\": \"#f6ad55\"\n },\n \"teal\": {\n \"bg\": \"#234e52\",\n \"text\": \"#81e6d9\",\n \"accent\": \"#4fd1c5\"\n }\n },\n \"persistentLayers\": {\n \"bottomLayers\": [\n {\n \"template\": \"gradientBackground\",\n \"config\": {\n \"type\": \"gradientBackground\",\n \"preset\": \"dark-vignette\"\n }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"documentary\",\n \"name\": \"Documentary\",\n \"description\": \"Classic dark cinematic look with elegant serif typography.\",\n \"colors\": {\n \"primary\": \"#3d5a80\",\n \"secondary\": \"#63b3ed\",\n \"background\": \"#1a202c\",\n \"backgroundLight\": \"#2d3748\",\n \"text\": \"#ffffff\",\n \"textMuted\": \"#a0aec0\",\n \"highlight\": \"#63b3ed\",\n \"warning\": \"#fc8181\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"source-serif\"\n },\n \"titleFont\": {\n \"stackId\": \"playfair\"\n },\n \"titleWeight\": \"bold\"\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.5,\n \"animationSpeed\": 1.0,\n \"imageTreatment\": {\n \"type\": \"mono\",\n \"strength\": 0.35\n }\n },\n \"renderStyle\": {\n \"name\": \"documentary\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"defaultImageAnimation\": \"slowZoom\",\n \"ambientMotion\": true,\n \"defaultTransition\": {\n \"type\": \"fade\",\n \"duration\": 0.8\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#22354d\",\n \"text\": \"#a8c3e0\",\n \"accent\": \"#7fa3c9\"\n },\n \"green\": {\n \"bg\": \"#26382e\",\n \"text\": \"#a9c6ae\",\n \"accent\": \"#82a88a\"\n },\n \"purple\": {\n \"bg\": \"#35304a\",\n \"text\": \"#beb3d6\",\n \"accent\": \"#9d8fc2\"\n },\n \"red\": {\n \"bg\": \"#45262a\",\n \"text\": \"#d9a2a2\",\n \"accent\": \"#c37f7c\"\n },\n \"orange\": {\n \"bg\": \"#453522\",\n \"text\": \"#d9bb90\",\n \"accent\": \"#c29a64\"\n },\n \"teal\": {\n \"bg\": \"#1f3a3d\",\n \"text\": \"#9cc4c4\",\n \"accent\": \"#6fa5a5\"\n }\n },\n \"persistentLayers\": {\n \"bottomLayers\": [\n {\n \"template\": \"gradientBackground\",\n \"config\": {\n \"type\": \"gradientBackground\",\n \"preset\": \"dark-vignette\"\n }\n }\n ],\n \"topLayers\": [\n {\n \"template\": \"vignette\",\n \"config\": {\n \"type\": \"vignette\",\n \"strength\": 0.25\n }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"minimalist\",\n \"name\": \"Minimalist\",\n \"description\": \"Clean near-white background with sans-serif type and minimal animation.\",\n \"colors\": {\n \"primary\": \"#2d3748\",\n \"secondary\": \"#4a5568\",\n \"background\": \"#f7fafc\",\n \"backgroundLight\": \"#edf2f7\",\n \"text\": \"#1a202c\",\n \"textMuted\": \"#718096\",\n \"highlight\": \"#3182ce\",\n \"warning\": \"#e53e3e\"\n },\n \"typography\": {\n \"bodyFont\": { \"stackId\": \"inter\" },\n \"titleFont\": { \"stackId\": \"inter\" },\n \"titleWeight\": \"bold\",\n \"lineHeight\": 1.6\n },\n \"style\": {\n \"textShadow\": false,\n \"overlayOpacity\": 0.3,\n \"animationSpeed\": 1.2,\n \"borderRadius\": 8\n },\n \"renderStyle\": {\n \"name\": \"minimalist\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"ambientMotion\": false,\n \"defaultTransition\": { \"type\": \"fade\", \"duration\": 0.6 },\n \"templateHints\": {\n \"title\": { \"showAccentLine\": false }\n }\n },\n \"colorSchemes\": {\n \"blue\": { \"bg\": \"#ebf8ff\", \"text\": \"#2b6cb0\", \"accent\": \"#3182ce\" },\n \"green\": { \"bg\": \"#f0fff4\", \"text\": \"#276749\", \"accent\": \"#38a169\" },\n \"purple\": { \"bg\": \"#faf5ff\", \"text\": \"#553c9a\", \"accent\": \"#805ad5\" },\n \"red\": { \"bg\": \"#fff5f5\", \"text\": \"#c53030\", \"accent\": \"#e53e3e\" },\n \"orange\": { \"bg\": \"#fffaf0\", \"text\": \"#c05621\", \"accent\": \"#dd6b20\" },\n \"teal\": { \"bg\": \"#e6fffa\", \"text\": \"#285e61\", \"accent\": \"#319795\" }\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"bold\",\n \"name\": \"Bold\",\n \"description\": \"High-contrast black with vivid accents and dramatic animations.\",\n \"colors\": {\n \"primary\": \"#e53e3e\",\n \"secondary\": \"#ed8936\",\n \"background\": \"#000000\",\n \"backgroundLight\": \"#1a1a1a\",\n \"text\": \"#ffffff\",\n \"textMuted\": \"#cbd5e0\",\n \"highlight\": \"#f6e05e\",\n \"warning\": \"#fc8181\"\n },\n \"typography\": {\n \"bodyFont\": { \"stackId\": \"roboto\" },\n \"titleFont\": { \"stackId\": \"oswald\" },\n \"titleWeight\": \"bold\",\n \"titleScale\": 1.15,\n \"bodyScale\": 1.0,\n \"titleLineHeight\": 1.1\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.6,\n \"animationSpeed\": 0.8\n },\n \"renderStyle\": {\n \"name\": \"bold\",\n \"defaultTextAnimation\": \"zoomIn\",\n \"defaultImageAnimation\": \"slowZoom\",\n \"ambientMotion\": true,\n \"defaultTransition\": { \"type\": \"dissolve\", \"duration\": 0.5 },\n \"templateHints\": {\n \"statHighlight\": { \"entrance\": \"dramatic\" },\n \"fullBleedQuote\": { \"entrance\": \"dramatic\" }\n }\n },\n \"colorSchemes\": {\n \"blue\": { \"bg\": \"#1a202c\", \"text\": \"#63b3ed\", \"accent\": \"#90cdf4\" },\n \"green\": { \"bg\": \"#1a202c\", \"text\": \"#48bb78\", \"accent\": \"#68d391\" },\n \"purple\": { \"bg\": \"#1a202c\", \"text\": \"#b794f4\", \"accent\": \"#d6bcfa\" },\n \"red\": { \"bg\": \"#1a202c\", \"text\": \"#fc8181\", \"accent\": \"#feb2b2\" },\n \"orange\": { \"bg\": \"#1a202c\", \"text\": \"#f6ad55\", \"accent\": \"#fbd38d\" },\n \"teal\": { \"bg\": \"#1a202c\", \"text\": \"#4fd1c5\", \"accent\": \"#81e6d9\" }\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"morning-light\",\n \"name\": \"Morning Light\",\n \"description\": \"Warm light background with dark text and gentle serif typography.\",\n \"colors\": {\n \"primary\": \"#744210\",\n \"secondary\": \"#975a16\",\n \"background\": \"#faf8f5\",\n \"backgroundLight\": \"#f5f0eb\",\n \"text\": \"#2d3748\",\n \"textMuted\": \"#718096\",\n \"highlight\": \"#dd6b20\",\n \"warning\": \"#c53030\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"lora\"\n },\n \"titleFont\": {\n \"stackId\": \"merriweather\"\n },\n \"titleWeight\": \"bold\",\n \"lineHeight\": 1.7\n },\n \"style\": {\n \"textShadow\": false,\n \"overlayOpacity\": 0.35,\n \"animationSpeed\": 1.1,\n \"borderRadius\": 4\n },\n \"renderStyle\": {\n \"name\": \"morning-light\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"ambientMotion\": false,\n \"defaultTransition\": {\n \"type\": \"fade\",\n \"duration\": 1.0\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#ebf8ff\",\n \"text\": \"#2c5282\",\n \"accent\": \"#4299e1\"\n },\n \"green\": {\n \"bg\": \"#f0fff4\",\n \"text\": \"#22543d\",\n \"accent\": \"#48bb78\"\n },\n \"purple\": {\n \"bg\": \"#faf5ff\",\n \"text\": \"#44337a\",\n \"accent\": \"#9f7aea\"\n },\n \"red\": {\n \"bg\": \"#fff5f5\",\n \"text\": \"#9b2c2c\",\n \"accent\": \"#fc8181\"\n },\n \"orange\": {\n \"bg\": \"#fffaf0\",\n \"text\": \"#744210\",\n \"accent\": \"#ed8936\"\n },\n \"teal\": {\n \"bg\": \"#e6fffa\",\n \"text\": \"#234e52\",\n \"accent\": \"#38b2ac\"\n }\n },\n \"persistentLayers\": {\n \"topLayers\": [\n {\n \"template\": \"patternBackground\",\n \"config\": {\n \"type\": \"patternBackground\",\n \"pattern\": \"noise\",\n \"opacity\": 0.03\n }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"tech-dark\",\n \"name\": \"Tech Dark\",\n \"description\": \"True black with neon cyan/green accents and monospace titles.\",\n \"colors\": {\n \"primary\": \"#00e5ff\",\n \"secondary\": \"#00e676\",\n \"background\": \"#0a0a0a\",\n \"backgroundLight\": \"#1a1a2e\",\n \"text\": \"#e0e0e0\",\n \"textMuted\": \"#9e9e9e\",\n \"highlight\": \"#00e5ff\",\n \"warning\": \"#ff5252\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"ibm-plex-sans\"\n },\n \"titleFont\": {\n \"stackId\": \"jetbrains-mono\"\n },\n \"monoFont\": {\n \"stackId\": \"jetbrains-mono\"\n },\n \"titleWeight\": \"bold\",\n \"titleScale\": 0.9,\n \"lineHeight\": 1.5\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.55,\n \"animationSpeed\": 0.9,\n \"borderRadius\": 2,\n \"imageTreatment\": {\n \"type\": \"cool\",\n \"strength\": 0.45\n }\n },\n \"renderStyle\": {\n \"name\": \"tech-dark\",\n \"defaultTextAnimation\": \"typewriter\",\n \"defaultImageAnimation\": \"slowZoom\",\n \"ambientMotion\": true,\n \"defaultTransition\": {\n \"type\": \"dissolve\",\n \"duration\": 0.4\n },\n \"templateHints\": {\n \"statHighlight\": {\n \"entrance\": \"dramatic\"\n }\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#0d1b2a\",\n \"text\": \"#00b0ff\",\n \"accent\": \"#40c4ff\"\n },\n \"green\": {\n \"bg\": \"#0d1b0d\",\n \"text\": \"#00e676\",\n \"accent\": \"#69f0ae\"\n },\n \"purple\": {\n \"bg\": \"#1a0d2e\",\n \"text\": \"#d500f9\",\n \"accent\": \"#ea80fc\"\n },\n \"red\": {\n \"bg\": \"#2e0d0d\",\n \"text\": \"#ff5252\",\n \"accent\": \"#ff8a80\"\n },\n \"orange\": {\n \"bg\": \"#2e1a0d\",\n \"text\": \"#ff9100\",\n \"accent\": \"#ffab40\"\n },\n \"teal\": {\n \"bg\": \"#0d2e2e\",\n \"text\": \"#1de9b6\",\n \"accent\": \"#64ffda\"\n }\n },\n \"persistentLayers\": {\n \"topLayers\": [\n {\n \"template\": \"patternBackground\",\n \"config\": {\n \"type\": \"patternBackground\",\n \"pattern\": \"grid\",\n \"color\": \"#00e5ff\",\n \"opacity\": 0.04,\n \"scale\": 1.5\n }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"magazine\",\n \"name\": \"Magazine\",\n \"description\": \"Rich editorial palette with mixed serif/sans typography and slide transitions.\",\n \"colors\": {\n \"primary\": \"#c53030\",\n \"secondary\": \"#2b6cb0\",\n \"background\": \"#1a202c\",\n \"backgroundLight\": \"#2d3748\",\n \"text\": \"#f7fafc\",\n \"textMuted\": \"#cbd5e0\",\n \"highlight\": \"#ed8936\",\n \"warning\": \"#fc8181\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"dm-sans\"\n },\n \"titleFont\": {\n \"stackId\": \"dm-serif\"\n },\n \"titleWeight\": \"bold\",\n \"titleScale\": 1.05,\n \"lineHeight\": 1.5\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.45,\n \"animationSpeed\": 1.0,\n \"borderRadius\": 6\n },\n \"renderStyle\": {\n \"name\": \"magazine\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"defaultImageAnimation\": \"slowZoom\",\n \"ambientMotion\": true,\n \"defaultTransition\": {\n \"type\": \"slideLeft\",\n \"duration\": 0.7\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#1d3a6e\",\n \"text\": \"#a3c9f5\",\n \"accent\": \"#4d9de0\"\n },\n \"green\": {\n \"bg\": \"#14532d\",\n \"text\": \"#86efac\",\n \"accent\": \"#22c55e\"\n },\n \"purple\": {\n \"bg\": \"#3b2a70\",\n \"text\": \"#c4b5fd\",\n \"accent\": \"#a78bfa\"\n },\n \"red\": {\n \"bg\": \"#7f1d1d\",\n \"text\": \"#fca5a5\",\n \"accent\": \"#f87171\"\n },\n \"orange\": {\n \"bg\": \"#7c2d12\",\n \"text\": \"#fdba74\",\n \"accent\": \"#f97316\"\n },\n \"teal\": {\n \"bg\": \"#134e4a\",\n \"text\": \"#5eead4\",\n \"accent\": \"#14b8a6\"\n }\n },\n \"persistentLayers\": {\n \"topLayers\": [\n {\n \"template\": \"patternBackground\",\n \"config\": {\n \"type\": \"patternBackground\",\n \"pattern\": \"diagonal\",\n \"color\": \"#f7fafc\",\n \"opacity\": 0.03,\n \"scale\": 1.2\n }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"cinematic\",\n \"name\": \"Cinematic\",\n \"description\": \"Ultra-dark with moody gradients, gold accents, and slow dissolve transitions.\",\n \"colors\": {\n \"primary\": \"#d69e2e\",\n \"secondary\": \"#b7791f\",\n \"background\": \"#0a0f1a\",\n \"backgroundLight\": \"#1a202c\",\n \"text\": \"#f7fafc\",\n \"textMuted\": \"#a0aec0\",\n \"highlight\": \"#ecc94b\",\n \"warning\": \"#fc8181\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"crimson\"\n },\n \"titleFont\": {\n \"stackId\": \"cormorant\"\n },\n \"titleWeight\": \"bold\",\n \"lineHeight\": 1.5,\n \"titleLineHeight\": 1.2\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.55,\n \"animationSpeed\": 1.2,\n \"imageTreatment\": {\n \"type\": \"warm\",\n \"strength\": 0.5\n }\n },\n \"renderStyle\": {\n \"name\": \"cinematic\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"defaultImageAnimation\": \"slowZoom\",\n \"ambientMotion\": true,\n \"defaultTransition\": {\n \"type\": \"dissolve\",\n \"duration\": 1.2\n },\n \"templateHints\": {\n \"title\": {\n \"showAccentLine\": true\n }\n }\n },\n \"colorSchemes\": {\n \"blue\": {\n \"bg\": \"#0f1d33\",\n \"text\": \"#9db8dc\",\n \"accent\": \"#5d84b8\"\n },\n \"green\": {\n \"bg\": \"#10251c\",\n \"text\": \"#9ecfae\",\n \"accent\": \"#55a878\"\n },\n \"purple\": {\n \"bg\": \"#1f1433\",\n \"text\": \"#c3aede\",\n \"accent\": \"#8f74c0\"\n },\n \"red\": {\n \"bg\": \"#2e1114\",\n \"text\": \"#e0959a\",\n \"accent\": \"#c05f66\"\n },\n \"orange\": {\n \"bg\": \"#2e1d0b\",\n \"text\": \"#e3b978\",\n \"accent\": \"#d69e2e\"\n },\n \"teal\": {\n \"bg\": \"#0d2626\",\n \"text\": \"#93cfc7\",\n \"accent\": \"#4d9e94\"\n }\n },\n \"persistentLayers\": {\n \"bottomLayers\": [\n {\n \"template\": \"gradientBackground\",\n \"config\": {\n \"type\": \"gradientBackground\",\n \"preset\": \"radial-dark\"\n }\n }\n ],\n \"topLayers\": [\n {\n \"template\": \"vignette\",\n \"config\": {\n \"type\": \"vignette\",\n \"strength\": 0.35\n }\n },\n {\n \"template\": \"patternBackground\",\n \"config\": {\n \"type\": \"patternBackground\",\n \"pattern\": \"noise\",\n \"opacity\": 0.05\n }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"warm-earth\",\n \"name\": \"Warm Earth\",\n \"description\": \"Natural warm tones with earthy browns and terracotta accents.\",\n \"colors\": {\n \"primary\": \"#9c4221\",\n \"secondary\": \"#c05621\",\n \"background\": \"#1c1410\",\n \"backgroundLight\": \"#2d241e\",\n \"text\": \"#faf5f0\",\n \"textMuted\": \"#c4a882\",\n \"highlight\": \"#dd6b20\",\n \"warning\": \"#e53e3e\"\n },\n \"typography\": {\n \"bodyFont\": { \"stackId\": \"pt-serif\" },\n \"titleFont\": { \"stackId\": \"pt-serif\" },\n \"titleWeight\": \"bold\",\n \"lineHeight\": 1.6\n },\n \"style\": {\n \"textShadow\": true,\n \"overlayOpacity\": 0.5,\n \"animationSpeed\": 1.1\n },\n \"renderStyle\": {\n \"name\": \"warm-earth\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"defaultImageAnimation\": \"panLeft\",\n \"ambientMotion\": true,\n \"defaultTransition\": { \"type\": \"fade\", \"duration\": 0.9 }\n },\n \"colorSchemes\": {\n \"blue\": { \"bg\": \"#2a1f14\", \"text\": \"#90cdf4\", \"accent\": \"#63b3ed\" },\n \"green\": { \"bg\": \"#1e2a14\", \"text\": \"#9ae6b4\", \"accent\": \"#68d391\" },\n \"purple\": { \"bg\": \"#2a142a\", \"text\": \"#d6bcfa\", \"accent\": \"#b794f4\" },\n \"red\": { \"bg\": \"#2a1414\", \"text\": \"#fc8181\", \"accent\": \"#feb2b2\" },\n \"orange\": { \"bg\": \"#2a1e0a\", \"text\": \"#fbd38d\", \"accent\": \"#f6ad55\" },\n \"teal\": { \"bg\": \"#142a28\", \"text\": \"#81e6d9\", \"accent\": \"#4fd1c5\" }\n },\n \"persistentLayers\": {\n \"bottomLayers\": [\n {\n \"template\": \"gradientBackground\",\n \"config\": { \"type\": \"gradientBackground\", \"preset\": \"earth-tones\" }\n }\n ]\n }\n}\n","{\n \"schemaVersion\": \"1\",\n \"id\": \"gezellig\",\n \"name\": \"Gezellig\",\n \"description\": \"Warm sage + terracotta palette inspired by the Gezel desktop app. Subtle orange-tinted dark background, cream text, sage and terracotta accents.\",\n \"colors\": {\n \"primary\": \"#b0724c\",\n \"secondary\": \"#667f62\",\n \"background\": \"#1f1a17\",\n \"backgroundLight\": \"#2b2420\",\n \"text\": \"#f3ede0\",\n \"textMuted\": \"#a89b85\",\n \"highlight\": \"#c0875d\",\n \"warning\": \"#e07a6b\"\n },\n \"typography\": {\n \"bodyFont\": {\n \"stackId\": \"system-sans-emoji\"\n },\n \"titleFont\": {\n \"stackId\": \"system-serif-emoji\"\n },\n \"monoFont\": {\n \"stackId\": \"system-mono\"\n },\n \"titleWeight\": \"bold\",\n \"lineHeight\": 1.55\n },\n \"style\": {\n \"textShadow\": false,\n \"overlayOpacity\": 0.4,\n \"animationSpeed\": 1.0,\n \"borderRadius\": 6,\n \"imageTreatment\": {\n \"type\": \"warm\",\n \"strength\": 0.3\n }\n },\n \"renderStyle\": {\n \"name\": \"gezellig\",\n \"defaultTextAnimation\": \"fadeIn\",\n \"defaultImageAnimation\": \"slowZoom\",\n \"ambientMotion\": false,\n \"defaultTransition\": {\n \"type\": \"fade\",\n \"duration\": 0.6\n }\n },\n \"colorSchemes\": {\n \"terracotta\": {\n \"bg\": \"#3d2620\",\n \"text\": \"#f3ede0\",\n \"accent\": \"#c0875d\"\n },\n \"sage\": {\n \"bg\": \"#28332a\",\n \"text\": \"#e3ead9\",\n \"accent\": \"#8aa589\"\n },\n \"amber\": {\n \"bg\": \"#3d2e17\",\n \"text\": \"#fbe3a8\",\n \"accent\": \"#d4a149\"\n },\n \"plum\": {\n \"bg\": \"#2e1f2a\",\n \"text\": \"#e8d4e0\",\n \"accent\": \"#b088a5\"\n },\n \"ink\": {\n \"bg\": \"#1a1f26\",\n \"text\": \"#d5dde8\",\n \"accent\": \"#7b8fa8\"\n },\n \"cream\": {\n \"bg\": \"#f3ede0\",\n \"text\": \"#2b2420\",\n \"accent\": \"#b0724c\"\n }\n },\n \"persistentLayers\": {\n \"topLayers\": [\n {\n \"template\": \"patternBackground\",\n \"config\": {\n \"type\": \"patternBackground\",\n \"pattern\": \"noise\",\n \"opacity\": 0.04\n }\n }\n ]\n }\n}\n","/**\n * Built-in Theme Library\n *\n * Loads each built-in theme from its JSON sidecar, validates it, and\n * exports the typed `Theme` constants. The JSON files in this directory\n * are syntactically identical to customizer-authored themes — they just\n * use more of the schema.\n *\n * Validation runs at module init: if a built-in JSON drifts from the\n * schema, the import fails loudly during build / startup.\n */\n\nimport type { Theme } from '../Theme.js';\nimport { assertTheme } from '../themeValidator.js';\n\nimport standardJson from './standard.json' with { type: 'json' };\nimport standardDarkJson from './standard-dark.json' with { type: 'json' };\nimport documentaryJson from './documentary.json' with { type: 'json' };\nimport minimalistJson from './minimalist.json' with { type: 'json' };\nimport boldJson from './bold.json' with { type: 'json' };\nimport morningLightJson from './morning-light.json' with { type: 'json' };\nimport techDarkJson from './tech-dark.json' with { type: 'json' };\nimport magazineJson from './magazine.json' with { type: 'json' };\nimport cinematicJson from './cinematic.json' with { type: 'json' };\nimport warmEarthJson from './warm-earth.json' with { type: 'json' };\nimport gezelligJson from './gezellig.json' with { type: 'json' };\n\nexport const standard: Theme = assertTheme(standardJson, 'standard.json');\nexport const standardDark: Theme = assertTheme(standardDarkJson, 'standard-dark.json');\nexport const documentary: Theme = assertTheme(documentaryJson, 'documentary.json');\nexport const minimalist: Theme = assertTheme(minimalistJson, 'minimalist.json');\nexport const bold: Theme = assertTheme(boldJson, 'bold.json');\nexport const morningLight: Theme = assertTheme(morningLightJson, 'morning-light.json');\nexport const techDark: Theme = assertTheme(techDarkJson, 'tech-dark.json');\nexport const magazine: Theme = assertTheme(magazineJson, 'magazine.json');\nexport const cinematic: Theme = assertTheme(cinematicJson, 'cinematic.json');\nexport const warmEarth: Theme = assertTheme(warmEarthJson, 'warm-earth.json');\nexport const gezellig: Theme = assertTheme(gezelligJson, 'gezellig.json');\n\n/** All built-in themes, keyed by id. */\nexport const BUILTIN_THEMES: Record<string, Theme> = {\n standard,\n 'standard-dark': standardDark,\n documentary,\n minimalist,\n bold,\n 'morning-light': morningLight,\n 'tech-dark': techDark,\n magazine,\n cinematic,\n 'warm-earth': warmEarth,\n gezellig,\n};\n","/**\n * Built-in Theme Library\n *\n * Public entry point for theme lookup. Built-in themes live as JSON\n * sidecars in [./themes/](./themes/) — they share the exact same shape\n * as customizer-authored themes (no two-tier distinction).\n *\n * Usage:\n * ```ts\n * import { THEMES, resolveTheme } from '@bendyline/squisq/schemas';\n *\n * const theme = resolveTheme('minimalist'); // by id, with registry override\n * const theme = THEMES.documentary; // direct access to a built-in\n * ```\n */\n\nimport type { Theme } from './Theme.js';\nimport { lookupRegisteredTheme } from './Theme.js';\nimport { BUILTIN_THEMES } from './themes/index.js';\n\n/** All built-in themes, keyed by id. */\nexport const THEMES: Record<string, Theme> = BUILTIN_THEMES;\n\n/** The default theme id. */\nexport const DEFAULT_THEME_ID = 'standard';\n\n/** The default theme (standard — safe system fonts, no external dependencies). */\nexport const DEFAULT_THEME: Theme = THEMES[DEFAULT_THEME_ID];\n\n/**\n * Resolve a theme by id. Custom themes registered via `registerTheme()`\n * take precedence over built-ins; otherwise built-ins are searched and\n * the default theme is returned for unknown ids.\n */\nexport function resolveTheme(id: string | undefined): Theme {\n const registered = lookupRegisteredTheme(id);\n if (registered) return registered;\n if (id && id in THEMES) return THEMES[id];\n return DEFAULT_THEME;\n}\n\n/** Get all built-in theme ids. */\nexport function getAvailableThemes(): string[] {\n return Object.keys(THEMES);\n}\n\n/** Get a summary of all built-in themes (id + name + description) for theme pickers. */\nexport function getThemeSummaries(): Array<{ id: string; name: string; description?: string }> {\n return Object.values(THEMES).map((t) => ({\n id: t.id,\n name: t.name,\n description: t.description,\n }));\n}\n","/**\n * Font Stacks\n *\n * Curated registry of font families used by built-in themes and offered in\n * the theme customizer. Each stack has a stable `id` (referenced by JSON\n * theme files via `{ stackId }`) and a CSS `family` string that resolves\n * to the actual font when rendered.\n *\n * Themes never carry raw CSS font strings — they reference a `stackId` or\n * specify a `{ custom: { name, fallback } }` form, both of which resolve\n * here at render time.\n */\n\nimport type { FontFamily, FontFamilyKind } from './Theme.js';\n\nexport interface FontStack {\n /** Stable identifier referenced by Theme JSON. */\n id: string;\n /** Display label for pickers. */\n label: string;\n /** Full CSS font-family string with safe fallbacks. */\n family: string;\n /** Categorical bucket for the picker UI. */\n kind: FontFamilyKind;\n /**\n * Google Fonts family name, when this stack is served by Google Fonts.\n * Used by exports that bundle a `<link>` to fonts.googleapis.com so the\n * face renders correctly even when the host page doesn't preload it.\n * Omit (or set to undefined) for purely system stacks.\n */\n googleFontFamily?: string;\n}\n\n/** All curated stacks, in the order they should appear in pickers. */\nexport const AVAILABLE_FONT_STACKS: FontStack[] = [\n // System defaults\n {\n id: 'system-sans',\n label: 'System Sans',\n family: 'system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif',\n kind: 'sans',\n },\n {\n id: 'system-serif',\n label: 'System Serif',\n family: 'Georgia, \"Times New Roman\", serif',\n kind: 'serif',\n },\n {\n id: 'system-mono',\n label: 'System Mono',\n family: 'Consolas, \"Courier New\", monospace',\n kind: 'mono',\n },\n // Curated Google Fonts. `googleFontFamily` is the canonical family\n // name Google serves the face under (used by exports that emit a\n // `<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/...\">`\n // so the face renders even when the host doesn't preload it).\n {\n id: 'inter',\n label: 'Inter',\n family: '\"Inter\", \"Segoe UI\", Roboto, sans-serif',\n kind: 'sans',\n googleFontFamily: 'Inter',\n },\n {\n id: 'ibm-plex-sans',\n label: 'IBM Plex Sans',\n family: '\"IBM Plex Sans\", \"Segoe UI\", Roboto, sans-serif',\n kind: 'sans',\n googleFontFamily: 'IBM Plex Sans',\n },\n {\n id: 'roboto',\n label: 'Roboto',\n family: '\"Roboto\", \"Segoe UI\", Arial, sans-serif',\n kind: 'sans',\n googleFontFamily: 'Roboto',\n },\n {\n id: 'dm-sans',\n label: 'DM Sans',\n family: '\"DM Sans\", \"Segoe UI\", Roboto, sans-serif',\n kind: 'sans',\n googleFontFamily: 'DM Sans',\n },\n {\n id: 'oswald',\n label: 'Oswald',\n // Condensed grotesques make a far better stand-in for Oswald than\n // Impact/Arial Black, whose extreme weight distorts every layout\n // tuned for the real face.\n family: '\"Oswald\", \"Arial Narrow\", \"Helvetica Neue Condensed\", \"Roboto Condensed\", sans-serif',\n kind: 'display',\n googleFontFamily: 'Oswald',\n },\n {\n id: 'playfair',\n label: 'Playfair Display',\n family: '\"Playfair Display\", Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'Playfair Display',\n },\n {\n id: 'source-serif',\n label: 'Source Serif',\n family: '\"Source Serif 4\", \"PT Serif\", Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'Source Serif 4',\n },\n {\n id: 'merriweather',\n label: 'Merriweather',\n family: '\"Merriweather\", Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'Merriweather',\n },\n {\n id: 'lora',\n label: 'Lora',\n family: '\"Lora\", \"Merriweather\", Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'Lora',\n },\n {\n id: 'pt-serif',\n label: 'PT Serif',\n family: '\"PT Serif\", Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'PT Serif',\n },\n {\n id: 'crimson',\n label: 'Crimson Text',\n family: '\"Crimson Text\", Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'Crimson Text',\n },\n {\n id: 'cormorant',\n label: 'Cormorant Garamond',\n family: '\"Cormorant Garamond\", Garamond, Georgia, serif',\n kind: 'serif',\n googleFontFamily: 'Cormorant Garamond',\n },\n {\n id: 'dm-serif',\n label: 'DM Serif Display',\n family: '\"DM Serif Display\", Georgia, serif',\n kind: 'display',\n googleFontFamily: 'DM Serif Display',\n },\n {\n id: 'jetbrains-mono',\n label: 'JetBrains Mono',\n family: '\"JetBrains Mono\", \"Fira Code\", Consolas, monospace',\n kind: 'mono',\n googleFontFamily: 'JetBrains Mono',\n },\n // Themed system stacks with emoji fallbacks (used by gezellig)\n {\n id: 'system-sans-emoji',\n label: 'System Sans + Emoji',\n family:\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, \"OpenMoji Color\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Noto Color Emoji\", sans-serif',\n kind: 'sans',\n },\n {\n id: 'system-serif-emoji',\n label: 'System Serif + Emoji',\n family:\n 'Georgia, \"Times New Roman\", \"OpenMoji Color\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Noto Color Emoji\", serif',\n kind: 'serif',\n },\n];\n\nconst STACKS_BY_ID: Record<string, FontStack> = Object.fromEntries(\n AVAILABLE_FONT_STACKS.map((s) => [s.id, s]),\n);\n\n/** Look up a stack by id. Returns undefined if not registered. */\nexport function getFontStack(id: string): FontStack | undefined {\n return STACKS_BY_ID[id];\n}\n\n/** Default fallback CSS family per kind, used when a custom font has no fallback specified. */\nconst FALLBACK_BY_KIND: Record<FontFamilyKind, string> = {\n sans: 'system-ui, sans-serif',\n serif: 'Georgia, serif',\n mono: 'Consolas, monospace',\n display: '\"Arial Narrow\", \"Helvetica Neue\", Arial, sans-serif',\n};\n\n/**\n * Resolve a `FontFamily` reference (or legacy raw string) to a CSS family string.\n * Used by `getThemeFont` at render time. Never returns an empty string —\n * unrecognized stackIds fall back to the requested role's default.\n */\nexport function resolveFontFamily(\n family: FontFamily | string | undefined,\n roleFallback: string,\n): string {\n if (!family) return roleFallback;\n if (typeof family === 'string') return family;\n if ('stackId' in family) {\n const stack = STACKS_BY_ID[family.stackId];\n return stack ? stack.family : roleFallback;\n }\n if ('custom' in family) {\n const { name, fallback } = family.custom;\n const fb =\n fallback === 'system-ui'\n ? 'system-ui, sans-serif'\n : fallback === 'serif'\n ? 'Georgia, serif'\n : fallback === 'monospace'\n ? 'Consolas, monospace'\n : 'system-ui, sans-serif';\n const safeName = name.includes(' ') || name.includes(',') ? `\"${name}\"` : name;\n return `${safeName}, ${fb}`;\n }\n return roleFallback;\n}\n\n/** Convenience: quick `{ stackId }` factory for code that constructs themes. */\nexport function fontStack(id: string): FontFamily {\n return { stackId: id };\n}\n\n/**\n * Build a `https://fonts.googleapis.com/css2?...` URL that loads the\n * subset of supplied `FontFamily` references that are served by Google\n * Fonts. Returns `null` when nothing in the list needs Google hosting\n * (system stacks, custom fonts, or unknown stack ids) — callers can use\n * that to skip emitting a `<link>` entirely.\n *\n * The URL requests regular + bold (400/700) for each family, which is\n * what the templates use. `display=swap` keeps the fallback visible\n * while the web font streams in.\n */\nexport function buildGoogleFontsUrl(families: Array<FontFamily | undefined>): string | null {\n const names = new Set<string>();\n for (const f of families) {\n if (!f || typeof f === 'string') continue;\n if ('stackId' in f) {\n const stack = STACKS_BY_ID[f.stackId];\n if (stack?.googleFontFamily) names.add(stack.googleFontFamily);\n }\n }\n if (names.size === 0) return null;\n // css2 syntax: ?family=Name+With+Spaces:wght@400;700&family=Other:wght@400;700&display=swap\n const params = Array.from(names)\n .sort()\n .map((name) => `family=${encodeURIComponent(name).replace(/%20/g, '+')}:wght@400;700`)\n .join('&');\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n\n/** Default fallback families exposed for tools that need them. */\nexport const FONT_FALLBACKS = FALLBACK_BY_KIND;\n"],"mappings":"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","names":[]}