@damiro/stylo 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +5 -3
  2. package/dist/{InPlaceView-CRb5AkfX.js → InPlaceView-Z8c9cqW8.js} +4 -4
  3. package/dist/{InPlaceView-CRb5AkfX.js.map → InPlaceView-Z8c9cqW8.js.map} +1 -1
  4. package/dist/{Preview-5lm2uUVj.js → Preview-BRA_R_Gp.js} +3 -3
  5. package/dist/{Preview-5lm2uUVj.js.map → Preview-BRA_R_Gp.js.map} +1 -1
  6. package/dist/Stylo.d.ts.map +1 -1
  7. package/dist/{embed-DKKeCE1Y.js → embed-DJAydn-v.js} +2 -2
  8. package/dist/{embed-DKKeCE1Y.js.map → embed-DJAydn-v.js.map} +1 -1
  9. package/dist/{embed-cache-CcBfVrc9.js → embed-cache-BU9Smn-h.js} +24 -17
  10. package/dist/embed-cache-BU9Smn-h.js.map +1 -0
  11. package/dist/styles.css +1 -1
  12. package/dist/stylo.js +391 -166
  13. package/dist/stylo.js.map +1 -1
  14. package/dist/toolbar/OverflowBar.d.ts +19 -0
  15. package/dist/toolbar/OverflowBar.d.ts.map +1 -0
  16. package/dist/toolbar/OverflowMenu.d.ts +24 -0
  17. package/dist/toolbar/OverflowMenu.d.ts.map +1 -0
  18. package/dist/toolbar/Toolbar.d.ts +5 -1
  19. package/dist/toolbar/Toolbar.d.ts.map +1 -1
  20. package/dist/toolbar/overflow-fit.d.ts +27 -0
  21. package/dist/toolbar/overflow-fit.d.ts.map +1 -0
  22. package/dist/toolbar/toolbar-model.d.ts +31 -0
  23. package/dist/toolbar/toolbar-model.d.ts.map +1 -0
  24. package/dist/toolbar/use-toolbar-overflow.d.ts +17 -0
  25. package/dist/toolbar/use-toolbar-overflow.d.ts.map +1 -0
  26. package/dist/types/toolbar.d.ts +16 -0
  27. package/dist/types/toolbar.d.ts.map +1 -1
  28. package/dist/{useCodeMirror-B07wRexo.js → useCodeMirror-Fn6qEfMx.js} +2 -2
  29. package/dist/{useCodeMirror-B07wRexo.js.map → useCodeMirror-Fn6qEfMx.js.map} +1 -1
  30. package/package.json +1 -1
  31. package/dist/embed-cache-CcBfVrc9.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./markdown-DcS6jMel.js";
2
- import { a as c, c as l, l as u, u as d } from "./embed-cache-CcBfVrc9.js";
3
- import { a as f, i as p, n as m, o as h, t as g } from "./embed-DKKeCE1Y.js";
2
+ import { a as c, c as l, l as u, u as d } from "./embed-cache-BU9Smn-h.js";
3
+ import { a as f, i as p, n as m, o as h, t as g } from "./embed-DJAydn-v.js";
4
4
  import { Children as _, cloneElement as v, isValidElement as y, useEffect as b, useMemo as x, useRef as S, useState as C } from "react";
5
5
  import { LanguageDescription as w } from "@codemirror/language";
6
6
  import { highlightTree as T, tagHighlighter as E } from "@lezer/highlight";
@@ -316,4 +316,4 @@ function K({ value: e, onWikiLinkClick: t, embedSource: o, onResolveError: c, fr
316
316
  //#endregion
317
317
  export { K as Preview };
318
318
 
319
- //# sourceMappingURL=Preview-5lm2uUVj.js.map
319
+ //# sourceMappingURL=Preview-BRA_R_Gp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Preview-5lm2uUVj.js","names":[],"sources":["../src/render/highlightCode.ts","../src/render/CodeBlock.tsx","../src/render/taskCheckbox.ts","../src/render/preview-components.tsx","../src/render/remark-callout.ts","../src/render/remark-embed.ts","../src/render/remark-wikilink.ts","../src/render/Preview.tsx"],"sourcesContent":["import { LanguageDescription, type Language } from \"@codemirror/language\"\nimport { highlightTree, tagHighlighter } from \"@lezer/highlight\"\nimport { SYNTAX_TAG_GROUPS } from \"../editor/highlight\"\nimport type { CodeLanguages } from \"../types\"\n\n/**\n * `preview`'s counterpart to `src/editor/highlight.ts`'s `styloHighlightStyle`\n * — same `SYNTAX_TAG_GROUPS`, same tags, but emitting plain `stylo-tok-<token>`\n * classes (styled in `stylo.module.css`) instead of a CodeMirror `StyleModule`,\n * since `highlightTree` here runs outside of any `EditorView`.\n */\nconst previewHighlighter = tagHighlighter(\n SYNTAX_TAG_GROUPS.map(({ tags, token }) => ({ tag: tags, class: `stylo-tok-${token}` })),\n)\n\nconst ESCAPES: Record<string, string> = { \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\" }\nconst escapeHtml = (text: string) => text.replace(/[&<>]/g, (c) => ESCAPES[c] ?? c)\n\n/**\n * Resolves a fence's language the same way `@codemirror/lang-markdown`\n * resolves `codeLanguages` for the CodeMirror surfaces, so a name that matches\n * on one matches on the other: the function form is called with the name\n * directly, an array is matched fuzzily by `LanguageDescription.matchLanguageName`.\n */\nfunction resolveLanguage(\n name: string,\n codeLanguages: CodeLanguages,\n): Language | LanguageDescription | null {\n if (typeof codeLanguages === \"function\") return codeLanguages(name)\n return LanguageDescription.matchLanguageName(codeLanguages, name, true)\n}\n\n/**\n * Tokenizes one fenced code block for `preview`, reusing the exact grammars\n * and colour mapping the in-place canvas's CodeMirror highlighter uses, so a\n * block looks identical whether the note is being read or edited. Returns\n * `null` — the block stays plain, un-highlighted text — when `codeLanguages`\n * has no match for `info`, or the match fails to load.\n */\nexport async function highlightToHtml(\n code: string,\n info: string,\n codeLanguages: CodeLanguages,\n): Promise<string | null> {\n const name = /\\S*/.exec(info)?.[0] ?? \"\"\n if (!name) return null\n\n const match = resolveLanguage(name, codeLanguages)\n if (!match) return null\n\n let language: Language\n if (match instanceof LanguageDescription) {\n try {\n language = (await match.load()).language\n } catch {\n return null\n }\n } else {\n language = match\n }\n\n const tree = language.parser.parse(code)\n let html = \"\"\n let pos = 0\n highlightTree(tree, previewHighlighter, (from, to, classes) => {\n if (from > pos) html += escapeHtml(code.slice(pos, from))\n html += `<span class=\"${classes}\">${escapeHtml(code.slice(from, to))}</span>`\n pos = to\n })\n if (pos < code.length) html += escapeHtml(code.slice(pos))\n return html\n}\n","import { useEffect, useState } from \"react\"\nimport type { CodeLanguages } from \"../types\"\nimport { highlightToHtml } from \"./highlightCode\"\n\ninterface CodeBlockProps {\n code: string\n language: string\n codeLanguages: CodeLanguages\n className?: string\n}\n\n/**\n * One fenced code block in `preview`. Renders plain text immediately — so\n * there is never a loading flash for something this small — then swaps in\n * `--stylo-syntax-*`-coloured spans once `codeLanguages` resolves and the\n * block's grammar parses. Per-language grammars stay exactly as lazy as they\n * already are for the CodeMirror surfaces (`codeLanguages` is host-supplied),\n * so a `preview`-only consumer never pays for one it never shows.\n */\nexport function CodeBlock({ code, language, codeLanguages, className }: CodeBlockProps) {\n const [html, setHtml] = useState<string | null>(null)\n\n useEffect(() => {\n let live = true\n setHtml(null)\n highlightToHtml(code, language, codeLanguages).then((result) => {\n if (live) setHtml(result)\n })\n return () => {\n live = false\n }\n }, [code, language, codeLanguages])\n\n if (html == null) return <code className={className}>{code}</code>\n // `html` is built in highlightCode.ts from the parsed source text (HTML-escaped)\n // and fixed `stylo-tok-*` class names only — no host or document input reaches it.\n return <code className={className} dangerouslySetInnerHTML={{ __html: html }} />\n}\n","import {\n Children,\n cloneElement,\n isValidElement,\n type ChangeEvent,\n type ReactElement,\n type ReactNode,\n} from \"react\"\nimport type { TaskToggleInfo } from \"../types\"\n\n/**\n * A task-list item's own `[ ]` / `[x]` marker sits right after its bullet or\n * ordinal and indentation — nothing else can appear before it on the line.\n * Anchoring the match to the very start of the item's raw text (from mdast's\n * `position.start.offset`) makes this safe even when the item's own text\n * could otherwise read as another marker further into the line.\n */\nconst CHECKBOX_PREFIX = /^[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+\\[([ xX])\\]/\n\n/**\n * Locates the exact `[ ]` / `[x]` substring of a task-list item within\n * `value`, given the raw offset where the item itself starts (a GFM\n * `listItem`'s `position.start.offset`, as parsed by `remark-gfm`).\n * Returns `null` if the prefix doesn't match a checkbox — it always should\n * for a node `remark-gfm` marked `checked`, but a raw markdown edit racing\n * the render is not impossible.\n */\nexport function findCheckboxOffsets(\n value: string,\n itemStart: number,\n): { start: number; end: number } | null {\n const lineEnd = value.indexOf(\"\\n\", itemStart)\n const prefix = value.slice(itemStart, lineEnd === -1 ? value.length : lineEnd)\n const match = CHECKBOX_PREFIX.exec(prefix)\n if (!match) return null\n const start = itemStart + match[0].length - 3\n return { start, end: start + 3 }\n}\n\n/**\n * Builds the checkbox's `onChange` handler for a rendered `<li>`, or `null`\n * when it isn't an eligible task item — not a task list item at all,\n * `onTaskToggle` unset, no position data, or (a raw-edit race, effectively\n * never) a marker `findCheckboxOffsets` can't locate.\n */\nexport function makeToggleHandler(\n node: { position?: { start: { offset?: number } } } | undefined,\n className: string | undefined,\n value: string,\n onTaskToggle: ((info: TaskToggleInfo) => void) | undefined,\n): ((event: ChangeEvent<HTMLInputElement>) => void) | null {\n if (!onTaskToggle || typeof className !== \"string\" || !className.includes(\"task-list-item\")) {\n return null\n }\n const itemStart = node?.position?.start.offset\n if (itemStart === undefined) return null\n const offsets = findCheckboxOffsets(value, itemStart)\n if (!offsets) return null\n return (event) => onTaskToggle({ ...offsets, checked: event.target.checked })\n}\n\n/**\n * A task item's checkbox `<input>` is always the leftmost leaf of its `<li>`\n * — `mdast-util-to-hast` unshifts it onto the first paragraph, itself the\n * first result, whether or not GFM kept that paragraph wrapped (a \"loose\"\n * list, blank lines between items) or unwrapped it (a \"tight\" one). Walking\n * only the first-child spine finds it either way without ever wandering into\n * a nested sub-list's own checkbox, which lives as a later sibling, not a\n * descendant of the first child.\n */\nexport function enableLeadingCheckbox(\n node: ReactNode,\n onChange: (event: ChangeEvent<HTMLInputElement>) => void,\n): { done: true; node: ReactNode } | { done: false } {\n if (!isValidElement(node)) return { done: false }\n if (node.type === \"input\") {\n return {\n done: true,\n node: cloneElement(node as ReactElement<Record<string, unknown>>, {\n disabled: false,\n onChange,\n }),\n }\n }\n const kids = Children.toArray((node.props as { children?: ReactNode }).children)\n if (kids.length === 0) return { done: false }\n const result = enableLeadingCheckbox(kids[0], onChange)\n if (!result.done) return { done: false }\n return {\n done: true,\n node: cloneElement(node as ReactElement<{ children?: ReactNode }>, {}, [\n result.node,\n ...kids.slice(1),\n ]),\n }\n}\n","import { Children, isValidElement, type ReactNode } from \"react\"\nimport type { Components } from \"react-markdown\"\nimport type { CodeLanguages, EmbedSource, ResolveErrorInfo, TaskToggleInfo } from \"../types\"\nimport { CodeBlock } from \"./CodeBlock\"\nimport { Embed } from \"./Embed\"\nimport { enableLeadingCheckbox, makeToggleHandler } from \"./taskCheckbox\"\n\nexport interface PreviewComponentsOptions {\n /** Read at click time, not baked into the closure — kept fresh across\n * re-renders without forcing `buildPreviewComponents` itself to depend on\n * `value` (which changes every keystroke and would defeat memoizing it). */\n valueRef: { current: string }\n onWikiLinkClick?: (target: string) => void\n embedSource?: EmbedSource\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n codeLanguages?: CodeLanguages\n onTaskToggle?: (info: TaskToggleInfo) => void\n}\n\n/** One `data-*` attribute off a component's spread props, as a string. */\nfunction dataAttr(rest: object, key: string): string | undefined {\n const v = (rest as Record<string, unknown>)[key]\n return typeof v === \"string\" ? v : undefined\n}\n\n/** The `![[ref]]` embed for a `data-stylo-embed(-inline)` node, or `children` unchanged. */\nfunction embedOrChildren(\n reference: string | undefined,\n embedSource: EmbedSource | undefined,\n onResolveError: PreviewComponentsOptions[\"onResolveError\"],\n inline: boolean,\n children: ReactNode,\n): ReactNode {\n if (reference === undefined || !embedSource) return children\n return (\n <Embed reference={reference} source={embedSource} onError={onResolveError} inline={inline} />\n )\n}\n\n/**\n * Builds `react-markdown`'s `components` map for `Preview`: the wikilink\n * click handler on `a`, the embed slots on `div` / `span`, the task-checkbox\n * wiring on `li`, the table wrapper, and fenced-code highlighting.\n */\nexport function buildPreviewComponents({\n valueRef,\n onWikiLinkClick,\n embedSource,\n onResolveError,\n codeLanguages,\n onTaskToggle,\n}: PreviewComponentsOptions): Components {\n return {\n a({ node: _node, children, ...rest }) {\n const target = dataAttr(rest, \"data-wikilink\")\n if (target !== undefined) {\n return (\n <a\n {...rest}\n href=\"#\"\n onClick={(event) => {\n event.preventDefault()\n onWikiLinkClick?.(target)\n }}\n >\n {children}\n </a>\n )\n }\n return (\n <a {...rest} rel=\"noreferrer\">\n {children}\n </a>\n )\n },\n div({ node: _node, children, ...rest }) {\n return (\n <div {...rest}>\n {embedOrChildren(\n dataAttr(rest, \"data-stylo-embed\"),\n embedSource,\n onResolveError,\n false,\n children,\n )}\n </div>\n )\n },\n span({ node: _node, children, ...rest }) {\n return (\n <span {...rest}>\n {embedOrChildren(\n dataAttr(rest, \"data-stylo-embed-inline\"),\n embedSource,\n onResolveError,\n true,\n children,\n )}\n </span>\n )\n },\n li({ node, children, ...rest }) {\n const handleToggle = makeToggleHandler(node, rest.className, valueRef.current, onTaskToggle)\n if (handleToggle) {\n // A loose list (blank line between items) keeps the `<li>`'s content\n // wrapped in a `<p>`, itself preceded by a formatting `\"\\n\"` text\n // node — skip past that to the first real element.\n const kids = Children.toArray(children)\n const firstElementIndex = kids.findIndex((kid) => isValidElement(kid))\n const enabled =\n firstElementIndex === -1\n ? null\n : enableLeadingCheckbox(kids[firstElementIndex], handleToggle)\n if (enabled?.done) {\n const nextKids: ReactNode[] = kids.map((kid, i) =>\n i === firstElementIndex ? enabled.node : kid,\n )\n return <li {...rest}>{nextKids}</li>\n }\n }\n return <li {...rest}>{children}</li>\n },\n table({ node: _node, children, ...rest }) {\n return (\n <div className=\"stylo-table-wrap\">\n <table {...rest}>{children}</table>\n </div>\n )\n },\n code({ node: _node, className, children, ...rest }) {\n const language = /language-(\\w+)/.exec(className || \"\")?.[1]\n if (language && codeLanguages) {\n return (\n <CodeBlock\n className={className}\n language={language}\n code={String(children).replace(/\\n$/, \"\")}\n codeLanguages={codeLanguages}\n />\n )\n }\n return (\n <code {...rest} className={className}>\n {children}\n </code>\n )\n },\n }\n}\n","import type { Blockquote, Root } from \"mdast\"\nimport { visit } from \"unist-util-visit\"\nimport { CALLOUT_TOKEN, calloutBucket } from \"../callout\"\n\n/**\n * Turns an Obsidian callout blockquote (`> [!note] Title`) into a\n * `<blockquote class=\"stylo-callout stylo-callout-<bucket>\" data-callout=\"note\">`.\n * The `[!type]` token is stripped from the first line; a `::before` label keyed\n * off `data-callout` stands in for it (matching the in-place canvas). The\n * `-` / `+` fold marker is accepted and ignored — the box always renders open.\n */\nexport function remarkCallout() {\n return (tree: Root) => {\n visit(tree, \"blockquote\", (node: Blockquote) => {\n const para = node.children[0]\n if (para?.type !== \"paragraph\") return\n const first = para.children[0]\n if (first?.type !== \"text\") return\n\n const m = CALLOUT_TOKEN.exec(first.value)\n if (!m) return\n\n first.value = first.value.slice(m[0].length)\n const type = m[1]!.toLowerCase()\n node.data ??= {}\n const props = (node.data.hProperties ??= {}) as Record<string, unknown>\n props.className = [\"stylo-callout\", `stylo-callout-${calloutBucket(type)}`]\n props[\"data-callout\"] = type\n })\n }\n}\n","import type { Paragraph, PhrasingContent, Root, Text } from \"mdast\"\nimport { SKIP, visit } from \"unist-util-visit\"\nimport { EMBED_PATTERN, isLoneEmbed } from \"../embed\"\n\n/**\n * Turns `![[ref]]` transclusion syntax into empty carrier elements the `Embed`\n * component (wired in `Preview`) fills from the host's `embedSource`.\n *\n * - A **lone** `![[ref]]` — the whole paragraph, trimmed — becomes a block\n * `<div class=\"stylo-embed\" data-stylo-embed=\"ref\">`.\n * - A `![[ref]]` **inside other text** becomes an inline\n * `<span class=\"stylo-embed\" data-stylo-embed-inline=\"ref\">`, and the\n * surrounding text is preserved around it. The host node should be phrasing\n * content in this case.\n *\n * `![[…]]` inside inline or fenced code is left literal (it is not a `text`\n * node). Must run before `remarkWikilink`, or the inner `[[ref]]` is rewritten\n * to a link first. Off unless the consumer passes `embedSource` — `Preview` only\n * adds this plugin then.\n */\nexport function remarkEmbed() {\n return (tree: Root) => {\n visit(tree, \"paragraph\", (node: Paragraph) => {\n const only = node.children.length === 1 ? node.children[0] : undefined\n if (only?.type !== \"text\" || !isLoneEmbed(only.value)) return\n\n EMBED_PATTERN.lastIndex = 0\n const ref = (EMBED_PATTERN.exec(only.value.trim())?.[1] ?? \"\").trim()\n if (!ref) return\n\n node.children = []\n node.data ??= {}\n node.data.hName = \"div\"\n node.data.hProperties = { className: [\"stylo-embed\"], \"data-stylo-embed\": ref }\n })\n\n visit(tree, \"text\", (node: Text, index, parent) => {\n if (index == null || parent == null || !node.value.includes(\"![[\")) return\n\n const parts: PhrasingContent[] = []\n let cursor = 0\n // `matchAll` seeds its internal copy from the shared regex's `lastIndex`;\n // reset it so the scan starts at 0 (the lone-paragraph pass above left it\n // mid-string).\n EMBED_PATTERN.lastIndex = 0\n for (const match of node.value.matchAll(EMBED_PATTERN)) {\n const ref = (match[1] ?? \"\").trim()\n if (!ref) continue\n const start = match.index ?? 0\n if (start > cursor) parts.push({ type: \"text\", value: node.value.slice(cursor, start) })\n parts.push({\n type: \"text\",\n value: \"\",\n data: {\n hName: \"span\",\n hProperties: { className: [\"stylo-embed\"], \"data-stylo-embed-inline\": ref },\n hChildren: [],\n },\n })\n cursor = start + match[0].length\n }\n\n if (!parts.length) return\n if (cursor < node.value.length) parts.push({ type: \"text\", value: node.value.slice(cursor) })\n parent.children.splice(index, 1, ...parts)\n return [SKIP, index + parts.length]\n })\n }\n}\n","import type { Link, Root, Text } from \"mdast\"\nimport { visit } from \"unist-util-visit\"\nimport { WIKILINK_PATTERN } from \"../wikilink\"\n\n/**\n * Rewrites `[[target]]` and `[[target|label]]` inside text into link nodes that\n * carry a `data-wikilink` attribute holding the target. The rendered href is\n * inert (`#`); navigation is the host's job via `onWikiLinkClick`.\n */\nexport function remarkWikilink() {\n return (tree: Root) => {\n visit(tree, \"text\", (node: Text, index, parent) => {\n if (parent == null || index == null || !node.value.includes(\"[[\")) return\n\n const out: Array<Text | Link> = []\n let cursor = 0\n\n for (const match of node.value.matchAll(WIKILINK_PATTERN)) {\n const [raw = \"\", rawTarget = \"\", rawLabel] = match\n const start = match.index ?? 0\n // A `![[ref]]` embed either was already consumed by `remarkEmbed`\n // earlier in the pipeline (when `embedSource` is set), or wasn't —\n // either way its inner `[[ref]]` must stay untouched here rather than\n // becoming a dead link, per `EmbedSource`'s documented fallback (a\n // bare `![[ref]]` renders unchanged with no `embedSource`).\n if (start > 0 && node.value[start - 1] === \"!\") continue\n if (start > cursor) {\n out.push({ type: \"text\", value: node.value.slice(cursor, start) })\n }\n out.push({\n type: \"link\",\n url: \"#\",\n data: { hProperties: { \"data-wikilink\": rawTarget.trim() } },\n children: [{ type: \"text\", value: (rawLabel ?? rawTarget).trim() }],\n })\n cursor = start + raw.length\n }\n\n if (cursor === 0) return\n if (cursor < node.value.length) {\n out.push({ type: \"text\", value: node.value.slice(cursor) })\n }\n\n parent.children.splice(index, 1, ...out)\n return index + out.length\n })\n }\n}\n","import { useMemo, useRef } from \"react\"\nimport Markdown from \"react-markdown\"\nimport rehypeKatex from \"rehype-katex\"\nimport remarkBreaks from \"remark-breaks\"\nimport remarkFrontmatter from \"remark-frontmatter\"\nimport remarkGfm from \"remark-gfm\"\nimport remarkMath from \"remark-math\"\nimport { splitFrontmatter } from \"../frontmatter\"\nimport styles from \"../styles/stylo.module.css\"\nimport type {\n CodeLanguages,\n EmbedSource,\n FrontmatterDisplay,\n ResolveErrorInfo,\n TaskToggleInfo,\n} from \"../types\"\nimport { buildPreviewComponents } from \"./preview-components\"\nimport { remarkCallout } from \"./remark-callout\"\nimport { remarkEmbed } from \"./remark-embed\"\nimport { remarkWikilink } from \"./remark-wikilink\"\n\nconst REHYPE_PLUGINS = [rehypeKatex]\n\nexport interface PreviewProps {\n value: string\n onWikiLinkClick?: (target: string) => void\n /** Resolves `![[ref]]` embeds. Omit and `![[…]]` stays literal. */\n embedSource?: EmbedSource\n /** Notified when `embedSource` rejects; the literal fallback still renders. */\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n /** `\"code\"` renders the `---` block as a styled `<pre>`; `\"hidden\"` (default) drops it. */\n frontmatter?: FrontmatterDisplay\n /**\n * Grammars for fenced-code syntax highlighting, matching the CodeMirror\n * surfaces' `codeLanguages` prop exactly (same resolution rules, same\n * `--stylo-syntax-*` colours). Omit and fenced code renders as plain,\n * un-highlighted text — today's behaviour.\n */\n codeLanguages?: CodeLanguages\n /**\n * Turn a single line ending into a real line break (`<br>`) instead of\n * CommonMark's default — a blank line required to start a new paragraph,\n * otherwise consecutive lines join into one run. Obsidian's Live Preview\n * reads this way. Off by default: every existing `preview` render keeps\n * today's paragraph-joining behaviour unless a host opts in. Has no effect\n * on `in-place` / `source` — CodeMirror already decorates each source line\n * independently there.\n */\n softBreaks?: boolean\n /**\n * Makes task-list checkboxes clickable instead of `disabled`. Fired with\n * raw `value` offsets bracketing the clicked marker and its new state; the\n * host owns splicing `value` and calling its own `onChange`. Off by\n * default — every checkbox stays `disabled`, today's behaviour, until a\n * host opts in.\n */\n onTaskToggle?: (info: TaskToggleInfo) => void\n}\n\n/** Rendered Markdown + KaTeX view. A pure function of the string. */\nexport function Preview({\n value,\n onWikiLinkClick,\n embedSource,\n onResolveError,\n frontmatter = \"hidden\",\n codeLanguages,\n softBreaks,\n onTaskToggle,\n}: PreviewProps) {\n const fm = frontmatter === \"code\" ? splitFrontmatter(value) : null\n\n // `remarkEmbed` must precede `remarkWikilink` (it consumes the `![[…]]` before\n // the inner `[[…]]` is rewritten) and is only in the pipeline when the host\n // opts in, so a bare `![[x]]` renders unchanged otherwise.\n const remarkPlugins = useMemo(\n () => [\n remarkFrontmatter,\n remarkGfm,\n remarkMath,\n ...(softBreaks ? [remarkBreaks] : []),\n ...(embedSource ? [remarkEmbed] : []),\n remarkWikilink,\n remarkCallout,\n ],\n [embedSource, softBreaks],\n )\n\n // `value` changes on every keystroke, so it's read through a ref rather than\n // closed over directly — otherwise memoizing `components` on it would be\n // pointless, defeating the point of memoizing at all (a changed component\n // identity at the same tree position makes react-markdown remount it, which\n // is exactly what stable identities here are meant to avoid for `CodeBlock`\n // and `Embed`).\n const valueRef = useRef(value)\n valueRef.current = value\n\n const components = useMemo(\n () =>\n buildPreviewComponents({\n valueRef,\n onWikiLinkClick,\n embedSource,\n onResolveError,\n codeLanguages,\n onTaskToggle,\n }),\n [onWikiLinkClick, embedSource, onResolveError, codeLanguages, onTaskToggle],\n )\n\n return (\n <div className={styles.preview}>\n {fm && <div className=\"stylo-frontmatter\">{fm.frontmatter}</div>}\n <Markdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={REHYPE_PLUGINS}\n components={components}\n >\n {value}\n </Markdown>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Preview-BRA_R_Gp.js","names":[],"sources":["../src/render/highlightCode.ts","../src/render/CodeBlock.tsx","../src/render/taskCheckbox.ts","../src/render/preview-components.tsx","../src/render/remark-callout.ts","../src/render/remark-embed.ts","../src/render/remark-wikilink.ts","../src/render/Preview.tsx"],"sourcesContent":["import { LanguageDescription, type Language } from \"@codemirror/language\"\nimport { highlightTree, tagHighlighter } from \"@lezer/highlight\"\nimport { SYNTAX_TAG_GROUPS } from \"../editor/highlight\"\nimport type { CodeLanguages } from \"../types\"\n\n/**\n * `preview`'s counterpart to `src/editor/highlight.ts`'s `styloHighlightStyle`\n * — same `SYNTAX_TAG_GROUPS`, same tags, but emitting plain `stylo-tok-<token>`\n * classes (styled in `stylo.module.css`) instead of a CodeMirror `StyleModule`,\n * since `highlightTree` here runs outside of any `EditorView`.\n */\nconst previewHighlighter = tagHighlighter(\n SYNTAX_TAG_GROUPS.map(({ tags, token }) => ({ tag: tags, class: `stylo-tok-${token}` })),\n)\n\nconst ESCAPES: Record<string, string> = { \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\" }\nconst escapeHtml = (text: string) => text.replace(/[&<>]/g, (c) => ESCAPES[c] ?? c)\n\n/**\n * Resolves a fence's language the same way `@codemirror/lang-markdown`\n * resolves `codeLanguages` for the CodeMirror surfaces, so a name that matches\n * on one matches on the other: the function form is called with the name\n * directly, an array is matched fuzzily by `LanguageDescription.matchLanguageName`.\n */\nfunction resolveLanguage(\n name: string,\n codeLanguages: CodeLanguages,\n): Language | LanguageDescription | null {\n if (typeof codeLanguages === \"function\") return codeLanguages(name)\n return LanguageDescription.matchLanguageName(codeLanguages, name, true)\n}\n\n/**\n * Tokenizes one fenced code block for `preview`, reusing the exact grammars\n * and colour mapping the in-place canvas's CodeMirror highlighter uses, so a\n * block looks identical whether the note is being read or edited. Returns\n * `null` — the block stays plain, un-highlighted text — when `codeLanguages`\n * has no match for `info`, or the match fails to load.\n */\nexport async function highlightToHtml(\n code: string,\n info: string,\n codeLanguages: CodeLanguages,\n): Promise<string | null> {\n const name = /\\S*/.exec(info)?.[0] ?? \"\"\n if (!name) return null\n\n const match = resolveLanguage(name, codeLanguages)\n if (!match) return null\n\n let language: Language\n if (match instanceof LanguageDescription) {\n try {\n language = (await match.load()).language\n } catch {\n return null\n }\n } else {\n language = match\n }\n\n const tree = language.parser.parse(code)\n let html = \"\"\n let pos = 0\n highlightTree(tree, previewHighlighter, (from, to, classes) => {\n if (from > pos) html += escapeHtml(code.slice(pos, from))\n html += `<span class=\"${classes}\">${escapeHtml(code.slice(from, to))}</span>`\n pos = to\n })\n if (pos < code.length) html += escapeHtml(code.slice(pos))\n return html\n}\n","import { useEffect, useState } from \"react\"\nimport type { CodeLanguages } from \"../types\"\nimport { highlightToHtml } from \"./highlightCode\"\n\ninterface CodeBlockProps {\n code: string\n language: string\n codeLanguages: CodeLanguages\n className?: string\n}\n\n/**\n * One fenced code block in `preview`. Renders plain text immediately — so\n * there is never a loading flash for something this small — then swaps in\n * `--stylo-syntax-*`-coloured spans once `codeLanguages` resolves and the\n * block's grammar parses. Per-language grammars stay exactly as lazy as they\n * already are for the CodeMirror surfaces (`codeLanguages` is host-supplied),\n * so a `preview`-only consumer never pays for one it never shows.\n */\nexport function CodeBlock({ code, language, codeLanguages, className }: CodeBlockProps) {\n const [html, setHtml] = useState<string | null>(null)\n\n useEffect(() => {\n let live = true\n setHtml(null)\n highlightToHtml(code, language, codeLanguages).then((result) => {\n if (live) setHtml(result)\n })\n return () => {\n live = false\n }\n }, [code, language, codeLanguages])\n\n if (html == null) return <code className={className}>{code}</code>\n // `html` is built in highlightCode.ts from the parsed source text (HTML-escaped)\n // and fixed `stylo-tok-*` class names only — no host or document input reaches it.\n return <code className={className} dangerouslySetInnerHTML={{ __html: html }} />\n}\n","import {\n Children,\n cloneElement,\n isValidElement,\n type ChangeEvent,\n type ReactElement,\n type ReactNode,\n} from \"react\"\nimport type { TaskToggleInfo } from \"../types\"\n\n/**\n * A task-list item's own `[ ]` / `[x]` marker sits right after its bullet or\n * ordinal and indentation — nothing else can appear before it on the line.\n * Anchoring the match to the very start of the item's raw text (from mdast's\n * `position.start.offset`) makes this safe even when the item's own text\n * could otherwise read as another marker further into the line.\n */\nconst CHECKBOX_PREFIX = /^[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+\\[([ xX])\\]/\n\n/**\n * Locates the exact `[ ]` / `[x]` substring of a task-list item within\n * `value`, given the raw offset where the item itself starts (a GFM\n * `listItem`'s `position.start.offset`, as parsed by `remark-gfm`).\n * Returns `null` if the prefix doesn't match a checkbox — it always should\n * for a node `remark-gfm` marked `checked`, but a raw markdown edit racing\n * the render is not impossible.\n */\nexport function findCheckboxOffsets(\n value: string,\n itemStart: number,\n): { start: number; end: number } | null {\n const lineEnd = value.indexOf(\"\\n\", itemStart)\n const prefix = value.slice(itemStart, lineEnd === -1 ? value.length : lineEnd)\n const match = CHECKBOX_PREFIX.exec(prefix)\n if (!match) return null\n const start = itemStart + match[0].length - 3\n return { start, end: start + 3 }\n}\n\n/**\n * Builds the checkbox's `onChange` handler for a rendered `<li>`, or `null`\n * when it isn't an eligible task item — not a task list item at all,\n * `onTaskToggle` unset, no position data, or (a raw-edit race, effectively\n * never) a marker `findCheckboxOffsets` can't locate.\n */\nexport function makeToggleHandler(\n node: { position?: { start: { offset?: number } } } | undefined,\n className: string | undefined,\n value: string,\n onTaskToggle: ((info: TaskToggleInfo) => void) | undefined,\n): ((event: ChangeEvent<HTMLInputElement>) => void) | null {\n if (!onTaskToggle || typeof className !== \"string\" || !className.includes(\"task-list-item\")) {\n return null\n }\n const itemStart = node?.position?.start.offset\n if (itemStart === undefined) return null\n const offsets = findCheckboxOffsets(value, itemStart)\n if (!offsets) return null\n return (event) => onTaskToggle({ ...offsets, checked: event.target.checked })\n}\n\n/**\n * A task item's checkbox `<input>` is always the leftmost leaf of its `<li>`\n * — `mdast-util-to-hast` unshifts it onto the first paragraph, itself the\n * first result, whether or not GFM kept that paragraph wrapped (a \"loose\"\n * list, blank lines between items) or unwrapped it (a \"tight\" one). Walking\n * only the first-child spine finds it either way without ever wandering into\n * a nested sub-list's own checkbox, which lives as a later sibling, not a\n * descendant of the first child.\n */\nexport function enableLeadingCheckbox(\n node: ReactNode,\n onChange: (event: ChangeEvent<HTMLInputElement>) => void,\n): { done: true; node: ReactNode } | { done: false } {\n if (!isValidElement(node)) return { done: false }\n if (node.type === \"input\") {\n return {\n done: true,\n node: cloneElement(node as ReactElement<Record<string, unknown>>, {\n disabled: false,\n onChange,\n }),\n }\n }\n const kids = Children.toArray((node.props as { children?: ReactNode }).children)\n if (kids.length === 0) return { done: false }\n const result = enableLeadingCheckbox(kids[0], onChange)\n if (!result.done) return { done: false }\n return {\n done: true,\n node: cloneElement(node as ReactElement<{ children?: ReactNode }>, {}, [\n result.node,\n ...kids.slice(1),\n ]),\n }\n}\n","import { Children, isValidElement, type ReactNode } from \"react\"\nimport type { Components } from \"react-markdown\"\nimport type { CodeLanguages, EmbedSource, ResolveErrorInfo, TaskToggleInfo } from \"../types\"\nimport { CodeBlock } from \"./CodeBlock\"\nimport { Embed } from \"./Embed\"\nimport { enableLeadingCheckbox, makeToggleHandler } from \"./taskCheckbox\"\n\nexport interface PreviewComponentsOptions {\n /** Read at click time, not baked into the closure — kept fresh across\n * re-renders without forcing `buildPreviewComponents` itself to depend on\n * `value` (which changes every keystroke and would defeat memoizing it). */\n valueRef: { current: string }\n onWikiLinkClick?: (target: string) => void\n embedSource?: EmbedSource\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n codeLanguages?: CodeLanguages\n onTaskToggle?: (info: TaskToggleInfo) => void\n}\n\n/** One `data-*` attribute off a component's spread props, as a string. */\nfunction dataAttr(rest: object, key: string): string | undefined {\n const v = (rest as Record<string, unknown>)[key]\n return typeof v === \"string\" ? v : undefined\n}\n\n/** The `![[ref]]` embed for a `data-stylo-embed(-inline)` node, or `children` unchanged. */\nfunction embedOrChildren(\n reference: string | undefined,\n embedSource: EmbedSource | undefined,\n onResolveError: PreviewComponentsOptions[\"onResolveError\"],\n inline: boolean,\n children: ReactNode,\n): ReactNode {\n if (reference === undefined || !embedSource) return children\n return (\n <Embed reference={reference} source={embedSource} onError={onResolveError} inline={inline} />\n )\n}\n\n/**\n * Builds `react-markdown`'s `components` map for `Preview`: the wikilink\n * click handler on `a`, the embed slots on `div` / `span`, the task-checkbox\n * wiring on `li`, the table wrapper, and fenced-code highlighting.\n */\nexport function buildPreviewComponents({\n valueRef,\n onWikiLinkClick,\n embedSource,\n onResolveError,\n codeLanguages,\n onTaskToggle,\n}: PreviewComponentsOptions): Components {\n return {\n a({ node: _node, children, ...rest }) {\n const target = dataAttr(rest, \"data-wikilink\")\n if (target !== undefined) {\n return (\n <a\n {...rest}\n href=\"#\"\n onClick={(event) => {\n event.preventDefault()\n onWikiLinkClick?.(target)\n }}\n >\n {children}\n </a>\n )\n }\n return (\n <a {...rest} rel=\"noreferrer\">\n {children}\n </a>\n )\n },\n div({ node: _node, children, ...rest }) {\n return (\n <div {...rest}>\n {embedOrChildren(\n dataAttr(rest, \"data-stylo-embed\"),\n embedSource,\n onResolveError,\n false,\n children,\n )}\n </div>\n )\n },\n span({ node: _node, children, ...rest }) {\n return (\n <span {...rest}>\n {embedOrChildren(\n dataAttr(rest, \"data-stylo-embed-inline\"),\n embedSource,\n onResolveError,\n true,\n children,\n )}\n </span>\n )\n },\n li({ node, children, ...rest }) {\n const handleToggle = makeToggleHandler(node, rest.className, valueRef.current, onTaskToggle)\n if (handleToggle) {\n // A loose list (blank line between items) keeps the `<li>`'s content\n // wrapped in a `<p>`, itself preceded by a formatting `\"\\n\"` text\n // node — skip past that to the first real element.\n const kids = Children.toArray(children)\n const firstElementIndex = kids.findIndex((kid) => isValidElement(kid))\n const enabled =\n firstElementIndex === -1\n ? null\n : enableLeadingCheckbox(kids[firstElementIndex], handleToggle)\n if (enabled?.done) {\n const nextKids: ReactNode[] = kids.map((kid, i) =>\n i === firstElementIndex ? enabled.node : kid,\n )\n return <li {...rest}>{nextKids}</li>\n }\n }\n return <li {...rest}>{children}</li>\n },\n table({ node: _node, children, ...rest }) {\n return (\n <div className=\"stylo-table-wrap\">\n <table {...rest}>{children}</table>\n </div>\n )\n },\n code({ node: _node, className, children, ...rest }) {\n const language = /language-(\\w+)/.exec(className || \"\")?.[1]\n if (language && codeLanguages) {\n return (\n <CodeBlock\n className={className}\n language={language}\n code={String(children).replace(/\\n$/, \"\")}\n codeLanguages={codeLanguages}\n />\n )\n }\n return (\n <code {...rest} className={className}>\n {children}\n </code>\n )\n },\n }\n}\n","import type { Blockquote, Root } from \"mdast\"\nimport { visit } from \"unist-util-visit\"\nimport { CALLOUT_TOKEN, calloutBucket } from \"../callout\"\n\n/**\n * Turns an Obsidian callout blockquote (`> [!note] Title`) into a\n * `<blockquote class=\"stylo-callout stylo-callout-<bucket>\" data-callout=\"note\">`.\n * The `[!type]` token is stripped from the first line; a `::before` label keyed\n * off `data-callout` stands in for it (matching the in-place canvas). The\n * `-` / `+` fold marker is accepted and ignored — the box always renders open.\n */\nexport function remarkCallout() {\n return (tree: Root) => {\n visit(tree, \"blockquote\", (node: Blockquote) => {\n const para = node.children[0]\n if (para?.type !== \"paragraph\") return\n const first = para.children[0]\n if (first?.type !== \"text\") return\n\n const m = CALLOUT_TOKEN.exec(first.value)\n if (!m) return\n\n first.value = first.value.slice(m[0].length)\n const type = m[1]!.toLowerCase()\n node.data ??= {}\n const props = (node.data.hProperties ??= {}) as Record<string, unknown>\n props.className = [\"stylo-callout\", `stylo-callout-${calloutBucket(type)}`]\n props[\"data-callout\"] = type\n })\n }\n}\n","import type { Paragraph, PhrasingContent, Root, Text } from \"mdast\"\nimport { SKIP, visit } from \"unist-util-visit\"\nimport { EMBED_PATTERN, isLoneEmbed } from \"../embed\"\n\n/**\n * Turns `![[ref]]` transclusion syntax into empty carrier elements the `Embed`\n * component (wired in `Preview`) fills from the host's `embedSource`.\n *\n * - A **lone** `![[ref]]` — the whole paragraph, trimmed — becomes a block\n * `<div class=\"stylo-embed\" data-stylo-embed=\"ref\">`.\n * - A `![[ref]]` **inside other text** becomes an inline\n * `<span class=\"stylo-embed\" data-stylo-embed-inline=\"ref\">`, and the\n * surrounding text is preserved around it. The host node should be phrasing\n * content in this case.\n *\n * `![[…]]` inside inline or fenced code is left literal (it is not a `text`\n * node). Must run before `remarkWikilink`, or the inner `[[ref]]` is rewritten\n * to a link first. Off unless the consumer passes `embedSource` — `Preview` only\n * adds this plugin then.\n */\nexport function remarkEmbed() {\n return (tree: Root) => {\n visit(tree, \"paragraph\", (node: Paragraph) => {\n const only = node.children.length === 1 ? node.children[0] : undefined\n if (only?.type !== \"text\" || !isLoneEmbed(only.value)) return\n\n EMBED_PATTERN.lastIndex = 0\n const ref = (EMBED_PATTERN.exec(only.value.trim())?.[1] ?? \"\").trim()\n if (!ref) return\n\n node.children = []\n node.data ??= {}\n node.data.hName = \"div\"\n node.data.hProperties = { className: [\"stylo-embed\"], \"data-stylo-embed\": ref }\n })\n\n visit(tree, \"text\", (node: Text, index, parent) => {\n if (index == null || parent == null || !node.value.includes(\"![[\")) return\n\n const parts: PhrasingContent[] = []\n let cursor = 0\n // `matchAll` seeds its internal copy from the shared regex's `lastIndex`;\n // reset it so the scan starts at 0 (the lone-paragraph pass above left it\n // mid-string).\n EMBED_PATTERN.lastIndex = 0\n for (const match of node.value.matchAll(EMBED_PATTERN)) {\n const ref = (match[1] ?? \"\").trim()\n if (!ref) continue\n const start = match.index ?? 0\n if (start > cursor) parts.push({ type: \"text\", value: node.value.slice(cursor, start) })\n parts.push({\n type: \"text\",\n value: \"\",\n data: {\n hName: \"span\",\n hProperties: { className: [\"stylo-embed\"], \"data-stylo-embed-inline\": ref },\n hChildren: [],\n },\n })\n cursor = start + match[0].length\n }\n\n if (!parts.length) return\n if (cursor < node.value.length) parts.push({ type: \"text\", value: node.value.slice(cursor) })\n parent.children.splice(index, 1, ...parts)\n return [SKIP, index + parts.length]\n })\n }\n}\n","import type { Link, Root, Text } from \"mdast\"\nimport { visit } from \"unist-util-visit\"\nimport { WIKILINK_PATTERN } from \"../wikilink\"\n\n/**\n * Rewrites `[[target]]` and `[[target|label]]` inside text into link nodes that\n * carry a `data-wikilink` attribute holding the target. The rendered href is\n * inert (`#`); navigation is the host's job via `onWikiLinkClick`.\n */\nexport function remarkWikilink() {\n return (tree: Root) => {\n visit(tree, \"text\", (node: Text, index, parent) => {\n if (parent == null || index == null || !node.value.includes(\"[[\")) return\n\n const out: Array<Text | Link> = []\n let cursor = 0\n\n for (const match of node.value.matchAll(WIKILINK_PATTERN)) {\n const [raw = \"\", rawTarget = \"\", rawLabel] = match\n const start = match.index ?? 0\n // A `![[ref]]` embed either was already consumed by `remarkEmbed`\n // earlier in the pipeline (when `embedSource` is set), or wasn't —\n // either way its inner `[[ref]]` must stay untouched here rather than\n // becoming a dead link, per `EmbedSource`'s documented fallback (a\n // bare `![[ref]]` renders unchanged with no `embedSource`).\n if (start > 0 && node.value[start - 1] === \"!\") continue\n if (start > cursor) {\n out.push({ type: \"text\", value: node.value.slice(cursor, start) })\n }\n out.push({\n type: \"link\",\n url: \"#\",\n data: { hProperties: { \"data-wikilink\": rawTarget.trim() } },\n children: [{ type: \"text\", value: (rawLabel ?? rawTarget).trim() }],\n })\n cursor = start + raw.length\n }\n\n if (cursor === 0) return\n if (cursor < node.value.length) {\n out.push({ type: \"text\", value: node.value.slice(cursor) })\n }\n\n parent.children.splice(index, 1, ...out)\n return index + out.length\n })\n }\n}\n","import { useMemo, useRef } from \"react\"\nimport Markdown from \"react-markdown\"\nimport rehypeKatex from \"rehype-katex\"\nimport remarkBreaks from \"remark-breaks\"\nimport remarkFrontmatter from \"remark-frontmatter\"\nimport remarkGfm from \"remark-gfm\"\nimport remarkMath from \"remark-math\"\nimport { splitFrontmatter } from \"../frontmatter\"\nimport styles from \"../styles/stylo.module.css\"\nimport type {\n CodeLanguages,\n EmbedSource,\n FrontmatterDisplay,\n ResolveErrorInfo,\n TaskToggleInfo,\n} from \"../types\"\nimport { buildPreviewComponents } from \"./preview-components\"\nimport { remarkCallout } from \"./remark-callout\"\nimport { remarkEmbed } from \"./remark-embed\"\nimport { remarkWikilink } from \"./remark-wikilink\"\n\nconst REHYPE_PLUGINS = [rehypeKatex]\n\nexport interface PreviewProps {\n value: string\n onWikiLinkClick?: (target: string) => void\n /** Resolves `![[ref]]` embeds. Omit and `![[…]]` stays literal. */\n embedSource?: EmbedSource\n /** Notified when `embedSource` rejects; the literal fallback still renders. */\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n /** `\"code\"` renders the `---` block as a styled `<pre>`; `\"hidden\"` (default) drops it. */\n frontmatter?: FrontmatterDisplay\n /**\n * Grammars for fenced-code syntax highlighting, matching the CodeMirror\n * surfaces' `codeLanguages` prop exactly (same resolution rules, same\n * `--stylo-syntax-*` colours). Omit and fenced code renders as plain,\n * un-highlighted text — today's behaviour.\n */\n codeLanguages?: CodeLanguages\n /**\n * Turn a single line ending into a real line break (`<br>`) instead of\n * CommonMark's default — a blank line required to start a new paragraph,\n * otherwise consecutive lines join into one run. Obsidian's Live Preview\n * reads this way. Off by default: every existing `preview` render keeps\n * today's paragraph-joining behaviour unless a host opts in. Has no effect\n * on `in-place` / `source` — CodeMirror already decorates each source line\n * independently there.\n */\n softBreaks?: boolean\n /**\n * Makes task-list checkboxes clickable instead of `disabled`. Fired with\n * raw `value` offsets bracketing the clicked marker and its new state; the\n * host owns splicing `value` and calling its own `onChange`. Off by\n * default — every checkbox stays `disabled`, today's behaviour, until a\n * host opts in.\n */\n onTaskToggle?: (info: TaskToggleInfo) => void\n}\n\n/** Rendered Markdown + KaTeX view. A pure function of the string. */\nexport function Preview({\n value,\n onWikiLinkClick,\n embedSource,\n onResolveError,\n frontmatter = \"hidden\",\n codeLanguages,\n softBreaks,\n onTaskToggle,\n}: PreviewProps) {\n const fm = frontmatter === \"code\" ? splitFrontmatter(value) : null\n\n // `remarkEmbed` must precede `remarkWikilink` (it consumes the `![[…]]` before\n // the inner `[[…]]` is rewritten) and is only in the pipeline when the host\n // opts in, so a bare `![[x]]` renders unchanged otherwise.\n const remarkPlugins = useMemo(\n () => [\n remarkFrontmatter,\n remarkGfm,\n remarkMath,\n ...(softBreaks ? [remarkBreaks] : []),\n ...(embedSource ? [remarkEmbed] : []),\n remarkWikilink,\n remarkCallout,\n ],\n [embedSource, softBreaks],\n )\n\n // `value` changes on every keystroke, so it's read through a ref rather than\n // closed over directly — otherwise memoizing `components` on it would be\n // pointless, defeating the point of memoizing at all (a changed component\n // identity at the same tree position makes react-markdown remount it, which\n // is exactly what stable identities here are meant to avoid for `CodeBlock`\n // and `Embed`).\n const valueRef = useRef(value)\n valueRef.current = value\n\n const components = useMemo(\n () =>\n buildPreviewComponents({\n valueRef,\n onWikiLinkClick,\n embedSource,\n onResolveError,\n codeLanguages,\n onTaskToggle,\n }),\n [onWikiLinkClick, embedSource, onResolveError, codeLanguages, onTaskToggle],\n )\n\n return (\n <div className={styles.preview}>\n {fm && <div className=\"stylo-frontmatter\">{fm.frontmatter}</div>}\n <Markdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={REHYPE_PLUGINS}\n components={components}\n >\n {value}\n </Markdown>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Stylo.d.ts","sourceRoot":"","sources":["../src/Stylo.tsx"],"names":[],"mappings":"AAWA,OAAO,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAoB,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAExE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,oGA4MhB,CAAA"}
1
+ {"version":3,"file":"Stylo.d.ts","sourceRoot":"","sources":["../src/Stylo.tsx"],"names":[],"mappings":"AAWA,OAAO,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAoB,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAExE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,oGA8MhB,CAAA"}
@@ -1,4 +1,4 @@
1
- import { i as e, n as t, r as n } from "./embed-cache-CcBfVrc9.js";
1
+ import { i as e, n as t, r as n } from "./embed-cache-BU9Smn-h.js";
2
2
  import { useEffect as r, useState as i } from "react";
3
3
  import { jsx as a } from "react/jsx-runtime";
4
4
  //#region src/render/Embed.tsx
@@ -106,4 +106,4 @@ function f(e) {
106
106
  //#endregion
107
107
  export { u as a, s as i, f as n, o, c as r, d as t };
108
108
 
109
- //# sourceMappingURL=embed-DKKeCE1Y.js.map
109
+ //# sourceMappingURL=embed-DJAydn-v.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"embed-DKKeCE1Y.js","names":[],"sources":["../src/render/Embed.tsx","../src/callout.ts","../src/embed.ts"],"sourcesContent":["import { type ReactNode, useEffect, useState } from \"react\"\nimport type { EmbedSource, ResolveErrorInfo } from \"../types\"\nimport { onEmbedInvalidated, peekEmbed, resolveEmbed } from \"./embed-cache\"\n\ninterface EmbedProps {\n /** The raw `![[ref]]` reference, trimmed. */\n reference: string\n source: EmbedSource\n /** Notified if `source` rejects; the literal fallback still renders. */\n onError?: (error: unknown, info: ResolveErrorInfo) => void\n /**\n * Render as phrasing content in a `<span>` (a `![[…]]` mid-sentence) rather\n * than the default block `<div>`. The host node should be inline in this mode.\n */\n inline?: boolean\n}\n\ntype State = { status: \"loading\" | \"error\" } | { status: \"ready\"; node: ReactNode }\n\n/**\n * Renders one `![[ref]]` embed. Resolves the reference through `embedSource` —\n * via a shared cache (`embed-cache.ts`), so an embed re-mounted by a scroll or a\n * `preview` re-render is not re-fetched — and drops the result into\n * `<div class=\"stylo-embed-content\">`, or a `<span>` when `inline`. While it\n * resolves — and if it rejects, or resolves to `null` — the literal `![[ref]]`\n * text stands in, so a reference is never silently lost.\n */\nexport function Embed({ reference, source, onError, inline }: EmbedProps) {\n const [state, setState] = useState<State>(() => {\n const hit = peekEmbed(source, reference)\n return hit ? { status: \"ready\", node: hit.node } : { status: \"loading\" }\n })\n\n useEffect(() => {\n let live = true\n function load() {\n const hit = peekEmbed(source, reference)\n if (hit) {\n setState({ status: \"ready\", node: hit.node })\n return\n }\n setState({ status: \"loading\" })\n resolveEmbed(source, reference).then(\n (node) => live && setState({ status: \"ready\", node }),\n (error) => {\n if (!live) return\n onError?.(error, { source: \"embedSource\", input: reference })\n setState({ status: \"error\" })\n },\n )\n }\n load()\n // Reruns this same load when a host calls `StyloHandle.invalidateEmbed`\n // for this ref, so a mounted embed updates immediately rather than only\n // on its next mount.\n const unsubscribe = onEmbedInvalidated(source, reference, load)\n return () => {\n live = false\n unsubscribe()\n }\n }, [reference, source, onError])\n\n const literal = `![[${reference}]]`\n if (state.status === \"loading\") return <span aria-busy=\"true\">{literal}</span>\n if (state.status === \"ready\" && state.node != null) {\n // Keep the mousedown away from the in-place canvas's own delegated\n // handler — same reason and technique as the editable table widget\n // (`table-widget.ts`): CodeMirror's built-in click-to-place-caret runs\n // for *any* mousedown that reaches `contentDOM`'s listener regardless of\n // what a custom `domEventHandlers` hook decided, so returning `false`\n // from one is not enough to keep an atomic widget's interactive content\n // clickable — the event has to be stopped before it gets there. Not\n // `preventDefault` — the button (or whatever the host renders) still\n // gets the click natively. Inert outside the canvas (`preview` / `split`\n // have no such listener to stop).\n const stop = (e: { stopPropagation: () => void }) => e.stopPropagation()\n return inline ? (\n <span className=\"stylo-embed-content stylo-embed-inline\" onMouseDown={stop}>\n {state.node}\n </span>\n ) : (\n <div className=\"stylo-embed-content\" onMouseDown={stop}>\n {state.node}\n </div>\n )\n }\n return <span>{literal}</span>\n}\n","/**\n * Obsidian-style callouts: a blockquote whose first line is `> [!type]`,\n * optionally `> [!type] Title` or `> [!type]- Title` (the `-` / `+` fold marker\n * is parsed but Stylo always renders expanded).\n *\n * Single source of truth for the in-place decoration scanner (`nodes.ts`) and\n * the preview remark plugin (`remark-callout.ts`), so the two never drift. The\n * many Obsidian type names collapse to five colour buckets; the raw type is kept\n * on a `data-callout` attribute for a `::before` label and consumer styling.\n */\n\n/** `[!type]` with an optional fold marker, anchored after any `>` prefix. */\nexport const CALLOUT_TOKEN = /^\\[!(\\w+)\\][+-]?[ \\t]*/\n\n/** A blockquote head line in the in-place canvas: `> ` prefix, then the token. */\nexport const CALLOUT_HEAD_LINE = /^([\\s>]*)(\\[!(\\w+)\\][+-]?[ \\t]*)/\n\nexport type CalloutBucket = \"note\" | \"tip\" | \"warn\" | \"danger\" | \"example\"\n\nconst BUCKET: Record<string, CalloutBucket> = {\n note: \"note\",\n info: \"note\",\n abstract: \"note\",\n summary: \"note\",\n tldr: \"note\",\n tip: \"tip\",\n hint: \"tip\",\n important: \"tip\",\n success: \"tip\",\n check: \"tip\",\n done: \"tip\",\n question: \"warn\",\n help: \"warn\",\n faq: \"warn\",\n warning: \"warn\",\n caution: \"warn\",\n attention: \"warn\",\n todo: \"warn\",\n failure: \"danger\",\n fail: \"danger\",\n missing: \"danger\",\n danger: \"danger\",\n error: \"danger\",\n bug: \"danger\",\n example: \"example\",\n quote: \"example\",\n cite: \"example\",\n}\n\n/** The colour bucket for a callout type; unknown types read as `note`. */\nexport function calloutBucket(type: string): CalloutBucket {\n return BUCKET[type.toLowerCase()] ?? \"note\"\n}\n","/**\n * `![[ref]]` transclusion. Capture group 1 is the raw reference — everything\n * between `![[` and `]]`, passed to the host verbatim (any `#heading`,\n * `#^blockid`, or `|size` suffix included). The `g` flag is set for\n * `matchAll` / `exec` loops.\n *\n * Deliberately not built on `WIKILINK_PATTERN`: in an embed the `|` is a size\n * hint (`![[image.png|300]]`), not a display label, so Stylo does not split on\n * it — the whole reference goes to `embedSource`.\n */\nexport const EMBED_PATTERN = /!\\[\\[([^\\]\\n]+?)\\]\\]/g\n\n/** True when `text` (trimmed) is exactly one `![[ref]]` and nothing else. */\nexport function isLoneEmbed(text: string): boolean {\n const trimmed = text.trim()\n EMBED_PATTERN.lastIndex = 0\n const m = EMBED_PATTERN.exec(trimmed)\n return m != null && m[0].length === trimmed.length\n}\n"],"mappings":";;;;AA2BA,SAAgB,EAAM,EAAE,cAAW,WAAQ,YAAS,aAAsB;CACxE,IAAM,CAAC,GAAO,KAAY,QAAsB;EAC9C,IAAM,IAAM,EAAU,GAAQ,CAAS;EACvC,OAAO,IAAM;GAAE,QAAQ;GAAS,MAAM,EAAI;EAAK,IAAI,EAAE,QAAQ,UAAU;CACzE,CAAC;CAED,QAAgB;EACd,IAAI,IAAO;EACX,SAAS,IAAO;GACd,IAAM,IAAM,EAAU,GAAQ,CAAS;GACvC,IAAI,GAAK;IACP,EAAS;KAAE,QAAQ;KAAS,MAAM,EAAI;IAAK,CAAC;IAC5C;GACF;GAEA,AADA,EAAS,EAAE,QAAQ,UAAU,CAAC,GAC9B,EAAa,GAAQ,CAAS,CAAC,CAAC,MAC7B,MAAS,KAAQ,EAAS;IAAE,QAAQ;IAAS;GAAK,CAAC,IACnD,MAAU;IACJ,MACL,IAAU,GAAO;KAAE,QAAQ;KAAe,OAAO;IAAU,CAAC,GAC5D,EAAS,EAAE,QAAQ,QAAQ,CAAC;GAC9B,CACF;EACF;EACA,EAAK;EAIL,IAAM,IAAc,EAAmB,GAAQ,GAAW,CAAI;EAC9D,aAAa;GAEX,AADA,IAAO,IACP,EAAY;EACd;CACF,GAAG;EAAC;EAAW;EAAQ;CAAO,CAAC;CAE/B,IAAM,IAAU,MAAM,EAAU;CAChC,IAAI,EAAM,WAAW,WAAW,OAAO,kBAAC,QAAD;EAAM,aAAU;EAAQ,UAAA;CAAc,CAAA;CAC7E,IAAI,EAAM,WAAW,WAAW,EAAM,QAAQ,MAAM;EAWlD,IAAM,KAAQ,MAAuC,EAAE,gBAAgB;EACvE,OAAO,IACL,kBAAC,QAAD;GAAM,WAAU;GAAyC,aAAa;GACnE,UAAA,EAAM;EACH,CAAA,IAEN,kBAAC,OAAD;GAAK,WAAU;GAAsB,aAAa;GAC/C,UAAA,EAAM;EACJ,CAAA;CAET;CACA,OAAO,kBAAC,QAAD,EAAA,UAAO,EAAc,CAAA;AAC9B;;;AC3EA,IAAa,IAAgB,0BAGhB,IAAoB,oCAI3B,IAAwC;CAC5C,MAAM;CACN,MAAM;CACN,UAAU;CACV,SAAS;CACT,MAAM;CACN,KAAK;CACL,MAAM;CACN,WAAW;CACX,SAAS;CACT,OAAO;CACP,MAAM;CACN,UAAU;CACV,MAAM;CACN,KAAK;CACL,SAAS;CACT,SAAS;CACT,WAAW;CACX,MAAM;CACN,SAAS;CACT,MAAM;CACN,SAAS;CACT,QAAQ;CACR,OAAO;CACP,KAAK;CACL,SAAS;CACT,OAAO;CACP,MAAM;AACR;AAGA,SAAgB,EAAc,GAA6B;CACzD,OAAO,EAAO,EAAK,YAAY,MAAM;AACvC;;;AC1CA,IAAa,IAAgB;AAG7B,SAAgB,EAAY,GAAuB;CACjD,IAAM,IAAU,EAAK,KAAK;CAC1B,EAAc,YAAY;CAC1B,IAAM,IAAI,EAAc,KAAK,CAAO;CACpC,OAAO,KAAK,QAAQ,EAAE,EAAE,CAAC,WAAW,EAAQ;AAC9C"}
1
+ {"version":3,"file":"embed-DJAydn-v.js","names":[],"sources":["../src/render/Embed.tsx","../src/callout.ts","../src/embed.ts"],"sourcesContent":["import { type ReactNode, useEffect, useState } from \"react\"\nimport type { EmbedSource, ResolveErrorInfo } from \"../types\"\nimport { onEmbedInvalidated, peekEmbed, resolveEmbed } from \"./embed-cache\"\n\ninterface EmbedProps {\n /** The raw `![[ref]]` reference, trimmed. */\n reference: string\n source: EmbedSource\n /** Notified if `source` rejects; the literal fallback still renders. */\n onError?: (error: unknown, info: ResolveErrorInfo) => void\n /**\n * Render as phrasing content in a `<span>` (a `![[…]]` mid-sentence) rather\n * than the default block `<div>`. The host node should be inline in this mode.\n */\n inline?: boolean\n}\n\ntype State = { status: \"loading\" | \"error\" } | { status: \"ready\"; node: ReactNode }\n\n/**\n * Renders one `![[ref]]` embed. Resolves the reference through `embedSource` —\n * via a shared cache (`embed-cache.ts`), so an embed re-mounted by a scroll or a\n * `preview` re-render is not re-fetched — and drops the result into\n * `<div class=\"stylo-embed-content\">`, or a `<span>` when `inline`. While it\n * resolves — and if it rejects, or resolves to `null` — the literal `![[ref]]`\n * text stands in, so a reference is never silently lost.\n */\nexport function Embed({ reference, source, onError, inline }: EmbedProps) {\n const [state, setState] = useState<State>(() => {\n const hit = peekEmbed(source, reference)\n return hit ? { status: \"ready\", node: hit.node } : { status: \"loading\" }\n })\n\n useEffect(() => {\n let live = true\n function load() {\n const hit = peekEmbed(source, reference)\n if (hit) {\n setState({ status: \"ready\", node: hit.node })\n return\n }\n setState({ status: \"loading\" })\n resolveEmbed(source, reference).then(\n (node) => live && setState({ status: \"ready\", node }),\n (error) => {\n if (!live) return\n onError?.(error, { source: \"embedSource\", input: reference })\n setState({ status: \"error\" })\n },\n )\n }\n load()\n // Reruns this same load when a host calls `StyloHandle.invalidateEmbed`\n // for this ref, so a mounted embed updates immediately rather than only\n // on its next mount.\n const unsubscribe = onEmbedInvalidated(source, reference, load)\n return () => {\n live = false\n unsubscribe()\n }\n }, [reference, source, onError])\n\n const literal = `![[${reference}]]`\n if (state.status === \"loading\") return <span aria-busy=\"true\">{literal}</span>\n if (state.status === \"ready\" && state.node != null) {\n // Keep the mousedown away from the in-place canvas's own delegated\n // handler — same reason and technique as the editable table widget\n // (`table-widget.ts`): CodeMirror's built-in click-to-place-caret runs\n // for *any* mousedown that reaches `contentDOM`'s listener regardless of\n // what a custom `domEventHandlers` hook decided, so returning `false`\n // from one is not enough to keep an atomic widget's interactive content\n // clickable — the event has to be stopped before it gets there. Not\n // `preventDefault` — the button (or whatever the host renders) still\n // gets the click natively. Inert outside the canvas (`preview` / `split`\n // have no such listener to stop).\n const stop = (e: { stopPropagation: () => void }) => e.stopPropagation()\n return inline ? (\n <span className=\"stylo-embed-content stylo-embed-inline\" onMouseDown={stop}>\n {state.node}\n </span>\n ) : (\n <div className=\"stylo-embed-content\" onMouseDown={stop}>\n {state.node}\n </div>\n )\n }\n return <span>{literal}</span>\n}\n","/**\n * Obsidian-style callouts: a blockquote whose first line is `> [!type]`,\n * optionally `> [!type] Title` or `> [!type]- Title` (the `-` / `+` fold marker\n * is parsed but Stylo always renders expanded).\n *\n * Single source of truth for the in-place decoration scanner (`nodes.ts`) and\n * the preview remark plugin (`remark-callout.ts`), so the two never drift. The\n * many Obsidian type names collapse to five colour buckets; the raw type is kept\n * on a `data-callout` attribute for a `::before` label and consumer styling.\n */\n\n/** `[!type]` with an optional fold marker, anchored after any `>` prefix. */\nexport const CALLOUT_TOKEN = /^\\[!(\\w+)\\][+-]?[ \\t]*/\n\n/** A blockquote head line in the in-place canvas: `> ` prefix, then the token. */\nexport const CALLOUT_HEAD_LINE = /^([\\s>]*)(\\[!(\\w+)\\][+-]?[ \\t]*)/\n\nexport type CalloutBucket = \"note\" | \"tip\" | \"warn\" | \"danger\" | \"example\"\n\nconst BUCKET: Record<string, CalloutBucket> = {\n note: \"note\",\n info: \"note\",\n abstract: \"note\",\n summary: \"note\",\n tldr: \"note\",\n tip: \"tip\",\n hint: \"tip\",\n important: \"tip\",\n success: \"tip\",\n check: \"tip\",\n done: \"tip\",\n question: \"warn\",\n help: \"warn\",\n faq: \"warn\",\n warning: \"warn\",\n caution: \"warn\",\n attention: \"warn\",\n todo: \"warn\",\n failure: \"danger\",\n fail: \"danger\",\n missing: \"danger\",\n danger: \"danger\",\n error: \"danger\",\n bug: \"danger\",\n example: \"example\",\n quote: \"example\",\n cite: \"example\",\n}\n\n/** The colour bucket for a callout type; unknown types read as `note`. */\nexport function calloutBucket(type: string): CalloutBucket {\n return BUCKET[type.toLowerCase()] ?? \"note\"\n}\n","/**\n * `![[ref]]` transclusion. Capture group 1 is the raw reference — everything\n * between `![[` and `]]`, passed to the host verbatim (any `#heading`,\n * `#^blockid`, or `|size` suffix included). The `g` flag is set for\n * `matchAll` / `exec` loops.\n *\n * Deliberately not built on `WIKILINK_PATTERN`: in an embed the `|` is a size\n * hint (`![[image.png|300]]`), not a display label, so Stylo does not split on\n * it — the whole reference goes to `embedSource`.\n */\nexport const EMBED_PATTERN = /!\\[\\[([^\\]\\n]+?)\\]\\]/g\n\n/** True when `text` (trimmed) is exactly one `![[ref]]` and nothing else. */\nexport function isLoneEmbed(text: string): boolean {\n const trimmed = text.trim()\n EMBED_PATTERN.lastIndex = 0\n const m = EMBED_PATTERN.exec(trimmed)\n return m != null && m[0].length === trimmed.length\n}\n"],"mappings":";;;;AA2BA,SAAgB,EAAM,EAAE,cAAW,WAAQ,YAAS,aAAsB;CACxE,IAAM,CAAC,GAAO,KAAY,QAAsB;EAC9C,IAAM,IAAM,EAAU,GAAQ,CAAS;EACvC,OAAO,IAAM;GAAE,QAAQ;GAAS,MAAM,EAAI;EAAK,IAAI,EAAE,QAAQ,UAAU;CACzE,CAAC;CAED,QAAgB;EACd,IAAI,IAAO;EACX,SAAS,IAAO;GACd,IAAM,IAAM,EAAU,GAAQ,CAAS;GACvC,IAAI,GAAK;IACP,EAAS;KAAE,QAAQ;KAAS,MAAM,EAAI;IAAK,CAAC;IAC5C;GACF;GAEA,AADA,EAAS,EAAE,QAAQ,UAAU,CAAC,GAC9B,EAAa,GAAQ,CAAS,CAAC,CAAC,MAC7B,MAAS,KAAQ,EAAS;IAAE,QAAQ;IAAS;GAAK,CAAC,IACnD,MAAU;IACJ,MACL,IAAU,GAAO;KAAE,QAAQ;KAAe,OAAO;IAAU,CAAC,GAC5D,EAAS,EAAE,QAAQ,QAAQ,CAAC;GAC9B,CACF;EACF;EACA,EAAK;EAIL,IAAM,IAAc,EAAmB,GAAQ,GAAW,CAAI;EAC9D,aAAa;GAEX,AADA,IAAO,IACP,EAAY;EACd;CACF,GAAG;EAAC;EAAW;EAAQ;CAAO,CAAC;CAE/B,IAAM,IAAU,MAAM,EAAU;CAChC,IAAI,EAAM,WAAW,WAAW,OAAO,kBAAC,QAAD;EAAM,aAAU;EAAQ,UAAA;CAAc,CAAA;CAC7E,IAAI,EAAM,WAAW,WAAW,EAAM,QAAQ,MAAM;EAWlD,IAAM,KAAQ,MAAuC,EAAE,gBAAgB;EACvE,OAAO,IACL,kBAAC,QAAD;GAAM,WAAU;GAAyC,aAAa;GACnE,UAAA,EAAM;EACH,CAAA,IAEN,kBAAC,OAAD;GAAK,WAAU;GAAsB,aAAa;GAC/C,UAAA,EAAM;EACJ,CAAA;CAET;CACA,OAAO,kBAAC,QAAD,EAAA,UAAO,EAAc,CAAA;AAC9B;;;AC3EA,IAAa,IAAgB,0BAGhB,IAAoB,oCAI3B,IAAwC;CAC5C,MAAM;CACN,MAAM;CACN,UAAU;CACV,SAAS;CACT,MAAM;CACN,KAAK;CACL,MAAM;CACN,WAAW;CACX,SAAS;CACT,OAAO;CACP,MAAM;CACN,UAAU;CACV,MAAM;CACN,KAAK;CACL,SAAS;CACT,SAAS;CACT,WAAW;CACX,MAAM;CACN,SAAS;CACT,MAAM;CACN,SAAS;CACT,QAAQ;CACR,OAAO;CACP,KAAK;CACL,SAAS;CACT,OAAO;CACP,MAAM;AACR;AAGA,SAAgB,EAAc,GAA6B;CACzD,OAAO,EAAO,EAAK,YAAY,MAAM;AACvC;;;AC1CA,IAAa,IAAgB;AAG7B,SAAgB,EAAY,GAAuB;CACjD,IAAM,IAAU,EAAK,KAAK;CAC1B,EAAc,YAAY;CAC1B,IAAM,IAAI,EAAc,KAAK,CAAO;CACpC,OAAO,KAAK,QAAQ,EAAE,EAAE,CAAC,WAAW,EAAQ;AAC9C"}
@@ -1,22 +1,29 @@
1
1
  import { HighlightStyle as e, syntaxHighlighting as t } from "@codemirror/language";
2
2
  import { tags as n } from "@lezer/highlight";
3
3
  var r = {
4
- root: "_root_11vle_1",
5
- source: "_source_11vle_42",
6
- inplace: "_inplace_11vle_57",
7
- toolbar: "_toolbar_11vle_63",
8
- toolbarGroup: "_toolbarGroup_11vle_75",
9
- toolbarButton: "_toolbarButton_11vle_81",
10
- toolbarSep: "_toolbarSep_11vle_140",
11
- toolbarSticky: "_toolbarSticky_11vle_150",
12
- toolbarStickyBottom: "_toolbarStickyBottom_11vle_150",
13
- toolbarStickyTop: "_toolbarStickyTop_11vle_150",
14
- toolbarStickyHidden: "_toolbarStickyHidden_11vle_219",
15
- stickyToolbarRootBottom: "_stickyToolbarRootBottom_11vle_230",
16
- stickyToolbarRootTop: "_stickyToolbarRootTop_11vle_235",
17
- split: "_split_11vle_249",
18
- splitPane: "_splitPane_11vle_256",
19
- preview: "_preview_11vle_282"
4
+ root: "_root_qpl96_1",
5
+ source: "_source_qpl96_42",
6
+ inplace: "_inplace_qpl96_57",
7
+ toolbar: "_toolbar_qpl96_63",
8
+ toolbarGroup: "_toolbarGroup_qpl96_75",
9
+ toolbarButton: "_toolbarButton_qpl96_81",
10
+ toolbarSep: "_toolbarSep_qpl96_140",
11
+ toolbarSticky: "_toolbarSticky_qpl96_150",
12
+ toolbarStickyBottom: "_toolbarStickyBottom_qpl96_150",
13
+ toolbarStickyTop: "_toolbarStickyTop_qpl96_150",
14
+ toolbarOverflow: "_toolbarOverflow_qpl96_221",
15
+ toolbarMore: "_toolbarMore_qpl96_219",
16
+ toolbarMeasureClip: "_toolbarMeasureClip_qpl96_241",
17
+ toolbarMeasure: "_toolbarMeasure_qpl96_241",
18
+ toolbarMenu: "_toolbarMenu_qpl96_259",
19
+ toolbarMenuItem: "_toolbarMenuItem_qpl96_280",
20
+ toolbarMenuDivider: "_toolbarMenuDivider_qpl96_317",
21
+ toolbarStickyHidden: "_toolbarStickyHidden_qpl96_324",
22
+ stickyToolbarRootBottom: "_stickyToolbarRootBottom_qpl96_335",
23
+ stickyToolbarRootTop: "_stickyToolbarRootTop_qpl96_340",
24
+ split: "_split_qpl96_354",
25
+ splitPane: "_splitPane_qpl96_361",
26
+ preview: "_preview_qpl96_387"
20
27
  }, i = /\[\[([^\]|\n]+?)(?:\|([^\]\n]+?))?\]\]/g;
21
28
  //#endregion
22
29
  //#region src/frontmatter.ts
@@ -173,4 +180,4 @@ function y(e, t) {
173
180
  //#endregion
174
181
  export { c as a, s as c, y as i, i as l, g as n, u as o, v as r, a as s, _ as t, r as u };
175
182
 
176
- //# sourceMappingURL=embed-cache-CcBfVrc9.js.map
183
+ //# sourceMappingURL=embed-cache-BU9Smn-h.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed-cache-BU9Smn-h.js","names":[],"sources":["../src/styles/stylo.module.css","../src/wikilink.ts","../src/frontmatter.ts","../src/editor/highlight.ts","../src/render/embed-cache.ts"],"sourcesContent":[".root {\n display: flex;\n flex-direction: column;\n min-height: 0;\n color: var(--stylo-text);\n background: var(--stylo-bg);\n border: 1px solid var(--stylo-border);\n border-radius: var(--stylo-radius);\n overflow: hidden;\n}\n\n/* The editing surface's own scroll container (source, in-place, and the source\n pane of split all resolve to `.cm-scroller`). Thin, token-tinted bars so the\n editor doesn't fall back to a heavier native scrollbar than the app chrome\n around it. A host's own `.cm-scroller` rules load after and win. */\n.root :global(.cm-scroller) {\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--stylo-border) 90%, transparent) transparent;\n}\n\n.root :global(.cm-scroller)::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n}\n\n.root :global(.cm-scroller)::-webkit-scrollbar-thumb {\n border: 3px solid transparent;\n border-radius: 6px;\n background: color-mix(in srgb, var(--stylo-border) 90%, transparent);\n background-clip: padding-box;\n}\n\n.root :global(.cm-scroller)::-webkit-scrollbar-thumb:hover {\n background: color-mix(in srgb, var(--stylo-text-muted) 55%, transparent);\n background-clip: padding-box;\n}\n\n.root :global(.cm-scroller)::-webkit-scrollbar-corner {\n background: transparent;\n}\n\n.source {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n\n.source :global(.cm-editor) {\n height: 100%;\n}\n\n.source :global(.cm-editor.cm-focused) {\n outline: 2px solid var(--stylo-ring);\n outline-offset: -2px;\n}\n\n.inplace {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n\n.toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 2px;\n padding: 4px 6px;\n background: var(--stylo-bg);\n border-bottom: 1px solid var(--stylo-border);\n}\n\n/* One flex child per `\"|\"`-delimited run of items, so `.toolbar`'s wrap\n breaks between groups instead of inside one. */\n.toolbarGroup {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.toolbarButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n min-width: 28px;\n height: 28px;\n padding: 0 5px;\n border: 0;\n border-radius: calc(var(--stylo-radius) - 3px);\n background: none;\n color: var(--stylo-text-muted);\n font: inherit;\n font-size: 0.75rem;\n font-weight: 600;\n line-height: 1;\n cursor: pointer;\n}\n\n.toolbarButton:hover:not(:disabled) {\n background: color-mix(in srgb, var(--stylo-border) 45%, transparent);\n color: var(--stylo-text);\n}\n\n.toolbarButton[data-active] {\n background: color-mix(in srgb, var(--stylo-accent) 12%, transparent);\n color: var(--stylo-text);\n}\n\n.toolbarButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.toolbarButton:focus-visible {\n outline: 2px solid var(--stylo-ring);\n outline-offset: -2px;\n}\n\n.toolbarButton svg {\n display: block;\n width: 16px;\n height: 16px;\n}\n\n/* Text glyphs that stand in for an icon (e.g. `fm`) — monospace to read as syntax. */\n.toolbarButton code {\n font-family: var(\n --stylo-font-family-mono,\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace\n );\n font-size: 0.8125rem;\n font-weight: 500;\n}\n\n.toolbarSep {\n align-self: stretch;\n width: 1px;\n margin: 4px 3px;\n background: var(--stylo-border);\n}\n\n/* `toolbar={{ sticky: \"top\" | \"bottom\" | true }}` — pins the bar to an edge\n instead of wherever `<Stylo>` sits on the page. One row, no wrapping: it\n scrolls horizontally instead, and its buttons grow to a comfortable touch\n size. Shared by both edges; `.toolbarStickyTop` / `.toolbarStickyBottom`\n below pick the positioning mechanism and the edge itself. */\n.toolbarSticky {\n z-index: 50;\n flex-wrap: nowrap;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n transition: opacity 0.15s ease;\n /* The base `.toolbar` deliberately leaves font-family to inherit from\n wherever the host mounts `<Stylo>`. Both sticky edges are\n `position: fixed` against the window, so neither can safely lean on\n that — the prose-font token (or its default stack) instead of the\n browser's serif default. */\n font-family: var(\n --stylo-font-family,\n -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif\n );\n}\n\n.toolbarSticky::-webkit-scrollbar {\n display: none;\n}\n\n.toolbarSticky .toolbarButton {\n min-width: 40px;\n height: 40px;\n flex: 0 0 auto;\n}\n\n/* `\"bottom\"` (or `true`) — has to be `position: fixed`: nothing about a\n mobile keyboard opening is a \"scroll\", so `position: sticky` has nothing to\n react to. Toolbar.tsx nudges it up from `bottom: 0` with\n `transform: translateY()`, tracking `useKeyboardInset`. */\n.toolbarStickyBottom {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n border-bottom: none;\n border-top: 1px solid var(--stylo-border);\n box-shadow: 0 -2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);\n}\n\n/* `\"top\"` — `position: fixed`, always pinned rather than waiting for a scroll\n threshold like `\"bottom\"`. Nothing ever eats into the top of the screen, so\n there's no keyboard to react to and no accessory bar to collide with — the\n two problems `\"bottom\"` has. `Toolbar.tsx`'s `useFloatingWatchdog` (a\n `requestAnimationFrame` loop, not CSS) is what actually keeps it visible on\n iOS Safari: earlier rounds tried a static `translateZ(0)`, then real\n `position: sticky`, and the disappear-on-scroll bug outlived both — a value\n that's set once and never re-touched gives the browser's compositor nothing\n to recompute if it drifts, so the watchdog re-asserts a `transform` every\n frame instead of trusting a one-time fix to hold. */\n.toolbarStickyTop {\n position: fixed;\n left: 0;\n right: 0;\n top: 0;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);\n}\n\n/* `toolbar={{ overflow: \"menu\" }}` — one row that never wraps or scrolls;\n what does not fit folds into `.toolbarMore`. Declared after `.toolbarSticky`\n so it wins over its horizontal scroll (the menu must not be clipped). */\n.toolbarOverflow {\n flex-wrap: nowrap;\n overflow: visible;\n min-width: 0;\n}\n\n/* The menu and measuring row anchor to the bar; a sticky bar is already\n `position: fixed`, which anchors them too. */\n.toolbarOverflow:not(.toolbarSticky) {\n position: relative;\n}\n\n.toolbarMore {\n margin-inline-start: auto;\n flex: 0 0 auto;\n}\n\n/* Hidden copy of every slot, read by `useToolbarOverflow` for real widths.\n The zero-size clip keeps the full-width row from adding scroll to the bar's\n ancestors. */\n.toolbarMeasureClip {\n position: absolute;\n top: 0;\n inset-inline-start: 0;\n width: 0;\n height: 0;\n overflow: hidden;\n visibility: hidden;\n pointer-events: none;\n}\n\n.toolbarMeasure {\n display: flex;\n align-items: center;\n gap: 2px;\n width: max-content;\n}\n\n.toolbarMenu {\n position: absolute;\n top: 100%;\n inset-inline-end: 0;\n z-index: 60;\n display: flex;\n flex-direction: column;\n min-width: 180px;\n padding: 4px;\n border: 1px solid var(--stylo-border);\n border-radius: var(--stylo-radius);\n background: var(--stylo-bg);\n box-shadow: 0 4px 14px color-mix(in srgb, var(--stylo-text) 14%, transparent);\n}\n\n/* A bar fixed to the bottom edge opens its menu upward. */\n.toolbarStickyBottom .toolbarMenu {\n top: auto;\n bottom: 100%;\n}\n\n.toolbarMenuItem {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border: 0;\n border-radius: calc(var(--stylo-radius) - 3px);\n background: none;\n color: var(--stylo-text);\n font: inherit;\n font-size: 0.8125rem;\n text-align: start;\n cursor: pointer;\n}\n\n.toolbarMenuItem:hover:not(:disabled),\n.toolbarMenuItem:focus-visible {\n background: color-mix(in srgb, var(--stylo-border) 45%, transparent);\n outline: none;\n}\n\n.toolbarMenuItem[data-active] {\n background: color-mix(in srgb, var(--stylo-accent) 12%, transparent);\n}\n\n.toolbarMenuItem:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.toolbarMenuItem svg {\n display: block;\n flex: none;\n width: 16px;\n height: 16px;\n}\n\n.toolbarMenuDivider {\n height: 1px;\n margin: 4px 2px;\n background: var(--stylo-border);\n}\n\n/* `stickyVisibility: \"dynamic\"` — faded out while nothing is focused. */\n.toolbarStickyHidden {\n opacity: 0;\n pointer-events: none;\n}\n\n/* `\"bottom\"` is `position: fixed`, out of flow entirely — reserve its resting\n height so it doesn't sit over the document's last line. Static, not\n keyboard-aware: while the keyboard is open the bar rides up to meet it (see\n `useKeyboardInset`), but the browser's own scroll-into-view around the\n caret is what keeps typing visible above both — this padding only covers\n the keyboard-closed resting state. */\n.stickyToolbarRootBottom .source,\n.stickyToolbarRootBottom .inplace {\n padding-bottom: 52px;\n}\n\n.stickyToolbarRootTop .source,\n.stickyToolbarRootTop .inplace {\n padding-top: 52px;\n}\n\n.inplace :global(.cm-editor) {\n height: 100%;\n}\n\n.inplace :global(.cm-editor.cm-focused) {\n outline: 2px solid var(--stylo-ring);\n outline-offset: -2px;\n}\n\n.split {\n display: grid;\n grid-template-columns: 1fr 1fr;\n flex: 1;\n min-height: 0;\n}\n\n.splitPane {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: auto;\n box-sizing: border-box;\n}\n\n.splitPane + .splitPane {\n border-left: 1px solid var(--stylo-border);\n}\n\n.splitPane > .source {\n flex: 1;\n min-height: 0;\n}\n\n/* Vertical rhythm follows Tailwind's `prose` (@tailwindcss/typography) as a\n visual reference — the em-based scale only, no plugin bundled. Consumer CSS\n wins: `.stylo p`, `.stylo h2`, … load after and match our specificity.\n `font-size` is the one base every descendant rule below sizes itself off of\n in `em`, so deriving it from `--stylo-font-size` (the same token the\n in-place canvas uses, ADR-002 §3) cascades the whole scale — no other rule\n needs touching. `padding` is `em`, not a fixed `rem`, for the same reason:\n it tracks the base font size instead of going visually oversized against a\n host-set smaller one. */\n.preview {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n padding: 0.8em 1.0666667em;\n font-size: var(--stylo-font-size, 0.9375rem);\n line-height: 1.75;\n overflow-wrap: break-word;\n}\n\n.preview > :first-child {\n margin-top: 0;\n}\n\n.preview > :last-child {\n margin-bottom: 0;\n}\n\n.preview :is(h1, h2, h3, h4, h5, h6) {\n font-weight: 600;\n}\n\n.preview h1 {\n margin: 0 0 0.8888889em;\n font-size: var(--stylo-rhythm-h1-size, 2.25em);\n line-height: var(--stylo-rhythm-h1-line, 1.1111111);\n}\n\n.preview h2 {\n margin: 2em 0 1em;\n font-size: var(--stylo-rhythm-h2-size, 1.5em);\n line-height: var(--stylo-rhythm-h2-line, 1.3333333);\n}\n\n.preview h3 {\n margin: 1.6em 0 0.6em;\n font-size: var(--stylo-rhythm-h3-size, 1.25em);\n line-height: var(--stylo-rhythm-h3-line, 1.6);\n}\n\n/* h4/h5/h6 share a margin and line-height, but each needs its own font-size —\n previously unset here, so h5/h6 silently fell back to the browser's UA\n default instead of matching the in-place canvas (2026-09-13). */\n.preview :is(h4, h5, h6) {\n margin: 1.5em 0 0.5em;\n line-height: var(--stylo-rhythm-h4-line, 1.5);\n}\n\n.preview h4 {\n font-size: var(--stylo-rhythm-h4-size, 1em);\n}\n\n.preview h5 {\n font-size: var(--stylo-rhythm-h5-size, 0.9em);\n}\n\n.preview h6 {\n font-size: var(--stylo-rhythm-h6-size, 0.9em);\n}\n\n.preview p {\n margin: 1.25em 0;\n}\n\n.preview :is(ul, ol) {\n margin: 1.25em 0;\n padding-left: 1.625em;\n}\n\n.preview li {\n margin: var(--stylo-rhythm-list-item-gap, 0.5em) 0;\n}\n\n.preview li > :is(ul, ol) {\n margin: var(--stylo-rhythm-list-item-gap-nested, 0.75em) 0;\n}\n\n.preview a {\n color: var(--stylo-link);\n text-decoration: none;\n}\n\n.preview code {\n font-family: var(\n --stylo-font-family-mono,\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace\n );\n font-size: 0.9em;\n padding: 0.1em 0.35em;\n border-radius: calc(var(--stylo-radius) - 3px);\n background: color-mix(in srgb, var(--stylo-border) 45%, transparent);\n}\n\n.preview pre {\n margin: 1.7142857em 0;\n padding: 0.75rem 0.9rem;\n border-radius: var(--stylo-radius);\n background: color-mix(in srgb, var(--stylo-border) 35%, transparent);\n overflow-x: auto;\n white-space: var(--stylo-preview-code-white-space, pre-wrap);\n overflow-wrap: break-word;\n}\n\n.preview pre code {\n padding: 0;\n font-size: 0.85em;\n background: none;\n}\n\n/* Fenced-code token colours — the `preview` counterpart to `src/editor/highlight.ts`'s\n `styloHighlightStyle`. Both read the same `SYNTAX_TAG_GROUPS` list and the same\n `--stylo-syntax-*` tokens; these are plain global classes (rather than a mounted\n CodeMirror `StyleModule`) because `highlightCode.ts` walks a Lezer tree outside of\n any `EditorView`. Keep this list in sync with `SYNTAX_TAG_GROUPS` in `highlight.ts`. */\n:global(.stylo-tok-keyword) {\n color: var(--stylo-syntax-keyword);\n}\n:global(.stylo-tok-string) {\n color: var(--stylo-syntax-string);\n}\n:global(.stylo-tok-escape) {\n color: var(--stylo-syntax-escape);\n}\n:global(.stylo-tok-comment) {\n color: var(--stylo-syntax-comment);\n font-style: italic;\n}\n:global(.stylo-tok-number) {\n color: var(--stylo-syntax-number);\n}\n:global(.stylo-tok-constant) {\n color: var(--stylo-syntax-constant);\n}\n:global(.stylo-tok-function) {\n color: var(--stylo-syntax-function);\n}\n:global(.stylo-tok-type) {\n color: var(--stylo-syntax-type);\n}\n:global(.stylo-tok-property) {\n color: var(--stylo-syntax-property);\n}\n:global(.stylo-tok-tag) {\n color: var(--stylo-syntax-tag);\n}\n:global(.stylo-tok-meta) {\n color: var(--stylo-syntax-comment);\n}\n:global(.stylo-tok-invalid) {\n color: var(--stylo-syntax-invalid);\n}\n\n/* frontmatter=\"code\": the raw `---` block, recessed. `.stylo-frontmatter` is a\n plain (unscoped) class so a consumer's own CSS overrides it at equal\n specificity — restyle the block, or rename / remove the label:\n .stylo-frontmatter::before { content: \"Meta\" }\n .stylo-frontmatter::before { content: none } */\n:global(.stylo-frontmatter) {\n margin: 0 0 1.2em;\n padding: 0.7rem 0.9rem;\n border-left: 3px solid var(--stylo-border);\n border-radius: calc(var(--stylo-radius) - 3px);\n background: color-mix(in srgb, var(--stylo-border) 22%, transparent);\n color: var(--stylo-text-muted);\n font-family: var(\n --stylo-font-family-mono,\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace\n );\n font-size: 0.8125rem;\n line-height: 1.5;\n white-space: pre-wrap;\n overflow-x: auto;\n}\n\n:global(.stylo-frontmatter)::before {\n content: \"Frontmatter\";\n display: block;\n margin-bottom: 0.5em;\n font-family: var(\n --stylo-font-family,\n -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Helvetica,\n Arial,\n sans-serif\n );\n font-size: 0.7rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.preview blockquote {\n margin: 1.6em 0;\n padding-left: 1em;\n border-left: 0.25rem solid var(--stylo-border);\n color: var(--stylo-text-muted);\n}\n\n/* Obsidian callouts (`> [!note]`) — mirrors the in-place canvas. `remark-callout`\n adds `stylo-callout stylo-callout-<bucket>` + `data-callout`. Override\n `--stylo-callout-<bucket>` (or `--stylo-callout-accent` per type) to reskin. */\n.preview :global(.stylo-callout) {\n margin: 1.6em 0;\n padding: var(--stylo-rhythm-callout-padding-y, 0.7em) var(--stylo-rhythm-callout-padding-x, 1rem);\n border-left: 0.25rem solid var(--stylo-callout-accent, var(--stylo-border));\n /* Right corners only, matching the in-place canvas: its callout is built\n from several stacked lines carrying their own border-left, so its left\n edge can't be rounded either — round preview's the same way rather than\n the two surfaces drawing a different shape (2026-09-13). */\n border-top-right-radius: var(--stylo-radius);\n border-bottom-right-radius: var(--stylo-radius);\n background: color-mix(in srgb, var(--stylo-callout-accent, var(--stylo-border)) 10%, transparent);\n color: var(--stylo-text);\n}\n.preview :global(.stylo-callout)::before {\n content: attr(data-callout);\n display: block;\n margin-bottom: 0.2em;\n font-size: 0.8em;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--stylo-callout-accent, var(--stylo-text-muted));\n}\n.preview :global(.stylo-callout-note) {\n --stylo-callout-accent: var(--stylo-callout-note, #3b82f6);\n}\n.preview :global(.stylo-callout-tip) {\n --stylo-callout-accent: var(--stylo-callout-tip, #22c55e);\n}\n.preview :global(.stylo-callout-warn) {\n --stylo-callout-accent: var(--stylo-callout-warn, #f59e0b);\n}\n.preview :global(.stylo-callout-danger) {\n --stylo-callout-accent: var(--stylo-callout-danger, #ef4444);\n}\n.preview :global(.stylo-callout-example) {\n --stylo-callout-accent: var(--stylo-callout-example, #a855f7);\n}\n\n/* `![[embed]]` transclusion. `remark-embed` swaps a lone-`![[ref]]` paragraph\n for `<div class=\"stylo-embed\">`, and `Embed` nests the host-rendered content\n in `.stylo-embed-content`. Both classes are stable override points; set\n `--stylo-embed-accent` to reskin the rail, or zero the padding / border to\n opt out of the frame entirely. */\n.preview :global(.stylo-embed) {\n margin: 1.6em 0;\n padding-left: 1em;\n border-left: 0.25rem solid var(--stylo-embed-accent, var(--stylo-border));\n}\n.preview :global(.stylo-embed-content) > :first-child {\n margin-top: 0;\n}\n.preview :global(.stylo-embed-content) > :last-child {\n margin-bottom: 0;\n}\n\n/* A GFM table renders wrapped in `<div class=\"stylo-table-wrap\">`, which\n carries the block/scroll behaviour a table wider than its column needs —\n `display: block` directly on `<table>` disables its own table-layout\n algorithm entirely (the rendered grid then sizes to its content,\n independent of the outer box's width), so a host's `width: 100%` on the\n table itself would only ever stretch a box disconnected from the grid it's\n trying to resize. `.stylo-table-wrap` is a stable override point, matching\n `.cm-inplace-table-wrap` on the in-place canvas. */\n.preview :global(.stylo-table-wrap) {\n display: block;\n margin: 2em 0;\n overflow-x: auto;\n}\n\n.preview table {\n border-collapse: collapse;\n}\n\n.preview :is(th, td) {\n padding: 0.35em 0.6em;\n border: 1px solid var(--stylo-table-border);\n text-align: left;\n}\n\n.preview thead th {\n background: var(--stylo-table-header-bg);\n font-weight: 600;\n}\n\n/* Opt-in zebra striping — no-op until the host sets `--stylo-table-stripe-bg`. */\n.preview tbody tr:nth-child(even) td {\n background: var(--stylo-table-stripe-bg);\n}\n\n/* prose sets `hr { margin: 3em 0 }`, which reads as a page break, not a rule.\n GitHub / VS Code render it tight; `1em` each side matches that. */\n.preview hr {\n margin: 1em 0;\n border: none;\n border-top: 1px solid var(--stylo-border);\n}\n\n.preview :global(.katex-display) {\n padding: 0.25em 0;\n overflow-x: auto;\n overflow-y: hidden;\n}\n","/**\n * `[[target]]` or `[[target|label]]`. Capture group 1 is the target, group 2 the\n * optional display label. The `g` flag is set for `matchAll` / `exec` loops;\n * callers that keep the regex across calls must reset `lastIndex` (or use\n * `matchAll`, which is stateless).\n *\n * Single source of truth for both the render-side plugin (`remark-wikilink`) and\n * the in-place decoration scanner, so the two never drift.\n */\nexport const WIKILINK_PATTERN = /\\[\\[([^\\]|\\n]+?)(?:\\|([^\\]\\n]+?))?\\]\\]/g\n","import type { Text } from \"@codemirror/state\"\n\n/**\n * The leading `---` … `---` YAML block, if present. The CodeMirror Markdown\n * grammar has no frontmatter node — it parses the fences as two thematic breaks\n * — so the region is located by hand. Shared by the in-place decoration layer\n * and the toolbar's frontmatter command.\n */\nexport function frontmatterRange(doc: Text): { from: number; to: number } | null {\n if (doc.lines < 2 || doc.line(1).text.trim() !== \"---\") return null\n for (let n = 2; n <= doc.lines; n++) {\n if (doc.line(n).text.trim() === \"---\") return { from: 0, to: doc.line(n).to }\n }\n return null\n}\n\nconst FRONTMATTER_BLOCK = /^[ \\t]*---[ \\t]*\\r?\\n(?:([\\s\\S]*?)\\r?\\n)?[ \\t]*---[ \\t]*(?:\\r?\\n|$)/\n\n/**\n * Split a leading `---` … `---` block off a Markdown string. `frontmatter` is\n * the inner text (`\"\"` for an empty block); `body` is everything after the\n * closing fence. Used by `preview` to render the block instead of dropping it.\n */\nexport function splitFrontmatter(md: string): { frontmatter: string; body: string } | null {\n const m = FRONTMATTER_BLOCK.exec(md)\n return m ? { frontmatter: m[1] ?? \"\", body: md.slice(m[0].length) } : null\n}\n","import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\"\nimport { tags as t, type Tag } from \"@lezer/highlight\"\n\n/**\n * One row per `--stylo-syntax-*` token: which Lezer tags map to it, and\n * whether it reads italic. This is the single source of truth for fenced-code\n * colour — `styloHighlightStyle` below builds the live CodeMirror extension\n * from it, and `src/render/highlightCode.ts` builds `preview`'s tree-walking\n * highlighter (a `tagHighlighter` emitting `stylo-tok-<token>` classes,\n * styled in `stylo.module.css`) from the same list, so both surfaces agree by\n * construction rather than by two hand-kept mappings staying in sync.\n *\n * `token` doubles as the `--stylo-syntax-<token>` suffix and the\n * `stylo-tok-<token>` class suffix. `meta` shares its colour with `comment`\n * but isn't italic, so it gets its own row rather than merging into it.\n */\nexport interface SyntaxTagGroup {\n token: string\n tags: Tag[]\n italic?: boolean\n}\n\nexport const SYNTAX_TAG_GROUPS: SyntaxTagGroup[] = [\n {\n token: \"keyword\",\n tags: [\n t.keyword,\n t.modifier,\n t.controlKeyword,\n t.operatorKeyword,\n t.definitionKeyword,\n t.moduleKeyword,\n ],\n },\n { token: \"string\", tags: [t.string, t.special(t.string), t.regexp, t.attributeValue] },\n { token: \"escape\", tags: [t.escape, t.character] },\n {\n token: \"comment\",\n tags: [t.comment, t.lineComment, t.blockComment, t.docComment],\n italic: true,\n },\n { token: \"number\", tags: [t.number, t.integer, t.float] },\n { token: \"constant\", tags: [t.bool, t.null, t.atom, t.constant(t.name)] },\n {\n token: \"function\",\n tags: [t.function(t.variableName), t.function(t.propertyName), t.macroName],\n },\n { token: \"type\", tags: [t.typeName, t.className, t.namespace] },\n { token: \"property\", tags: [t.propertyName, t.attributeName] },\n { token: \"tag\", tags: [t.tagName] },\n { token: \"meta\", tags: [t.meta, t.annotation] },\n { token: \"invalid\", tags: [t.invalid] },\n]\n\n/**\n * Token colours for fenced code and any other embedded-language grammar.\n *\n * Every colour resolves to a `--stylo-syntax-*` custom property, so the host\n * restyles code the same way as the rest of the `--stylo-*` palette; the values\n * in `tokens.css` are the defaults. Both `@codemirror/language` and\n * `@lezer/highlight` are already transitive dependencies of\n * `@codemirror/lang-markdown`, so this adds no package weight.\n *\n * Markdown's own structural tokens (heading marks, emphasis markers, link\n * brackets) are deliberately left undefined: the in-place canvas styles those\n * with decorations, and `source` mode keeps its plain, un-tinted look. Only\n * tokens that come from a real programming-language parser get a colour.\n */\nexport const styloHighlightStyle = HighlightStyle.define(\n SYNTAX_TAG_GROUPS.map(({ tags, token, italic }) => ({\n tag: tags,\n color: `var(--stylo-syntax-${token === \"meta\" ? \"comment\" : token})`,\n ...(italic ? { fontStyle: \"italic\" } : {}),\n })),\n)\n\n/** The highlight style wrapped as an editor extension. */\nexport const styloHighlighting = syntaxHighlighting(styloHighlightStyle)\n","import type { ReactNode } from \"react\"\nimport type { EmbedSource } from \"../types\"\n\n/**\n * A process-wide resolution cache for `![[ref]]` embeds, shared by `preview` and\n * the in-place canvas (both render through `Embed`). Without it, an embed\n * scrolled out of the canvas viewport and back — or a `preview` re-render that\n * remounts the subtree — re-invokes `embedSource`, which for a network-backed\n * source is a refetch each time.\n *\n * Keyed first by the `embedSource` identity (a `WeakMap`, so two editors with\n * different resolvers never collide and a changed `embedSource` drops its cache\n * for free — the type doc already asks consumers to keep the reference stable),\n * then by the raw `ref` string.\n *\n * In-flight promises are shared, so two embeds of the same `ref` mounting\n * together call `embedSource` once. A rejection is **not** cached — the next\n * mount retries. `null` (the host chose to keep the reference literal) is a\n * valid cached value; `settled` tells it apart from an absent entry.\n *\n * The cache has no TTL and never re-checks a settled entry on its own — see\n * {@link invalidateEmbed} for the host-triggered way to drop one when the\n * content behind a reference actually changes, exposed publicly as\n * `StyloHandle.invalidateEmbed`.\n */\n\ninterface Entry {\n promise: Promise<ReactNode>\n node?: ReactNode\n settled: boolean\n}\n\n/** Distinct `ref`s kept per resolver. Eviction only costs a re-resolve. */\nconst MAX_ENTRIES = 64\n\nconst caches = new WeakMap<EmbedSource, Map<string, Entry>>()\n\nfunction bucket(source: EmbedSource): Map<string, Entry> {\n let m = caches.get(source)\n if (!m) {\n m = new Map()\n caches.set(source, m)\n }\n return m\n}\n\n/** Mounted `Embed`s waiting to hear that their own `(source, ref)` was\n * invalidated, so they can re-resolve without waiting for a remount. */\nconst listeners = new WeakMap<EmbedSource, Map<string, Set<() => void>>>()\n\nfunction listenerSet(source: EmbedSource, ref: string): Set<() => void> {\n let bySource = listeners.get(source)\n if (!bySource) {\n bySource = new Map()\n listeners.set(source, bySource)\n }\n let set = bySource.get(ref)\n if (!set) {\n set = new Set()\n bySource.set(ref, set)\n }\n return set\n}\n\n/** Notified whenever `ref` is invalidated for `source` (see {@link invalidateEmbed}).\n * Returns an unsubscribe function. */\nexport function onEmbedInvalidated(source: EmbedSource, ref: string, listener: () => void) {\n const set = listenerSet(source, ref)\n set.add(listener)\n return () => set.delete(listener)\n}\n\n/**\n * Drops the cached result for `ref` — or, with no `ref`, every entry for\n * `source` — so the next resolution re-invokes `embedSource` instead of\n * serving the stale answer. This is the escape hatch for the one case the\n * cache can't know about on its own: the content behind a reference changing\n * while the editor is open. Any `Embed` currently mounted for a dropped ref\n * re-resolves immediately via {@link onEmbedInvalidated}, rather than only on\n * its next mount.\n */\nexport function invalidateEmbed(source: EmbedSource, ref?: string): void {\n const m = caches.get(source)\n if (!m) return\n const refs = ref !== undefined ? [ref] : [...m.keys()]\n const bySource = listeners.get(source)\n for (const r of refs) {\n m.delete(r)\n bySource?.get(r)?.forEach((fn) => fn())\n }\n}\n\n/** A settled result for `ref`, or `undefined` while it is unresolved or absent. */\nexport function peekEmbed(source: EmbedSource, ref: string): { node: ReactNode } | undefined {\n const entry = bucket(source).get(ref)\n return entry?.settled ? { node: entry.node } : undefined\n}\n\n/** Resolve `ref` through the cache — reusing an in-flight or settled entry, or\n * calling `source` on a miss. */\nexport function resolveEmbed(source: EmbedSource, ref: string): Promise<ReactNode> {\n const m = bucket(source)\n const existing = m.get(ref)\n if (existing) return existing.promise\n\n const promise = Promise.resolve(source(ref)).then(\n (node) => {\n const entry = m.get(ref)\n if (entry) {\n entry.node = node\n entry.settled = true\n }\n return node\n },\n (err) => {\n m.delete(ref) // never cache a failure\n throw err\n },\n )\n m.set(ref, { promise, settled: false })\n\n // Insertion-order cap: drop the oldest distinct ref once over the limit.\n if (m.size > MAX_ENTRIES) {\n const oldest = m.keys().next().value\n if (oldest !== undefined && oldest !== ref) m.delete(oldest)\n }\n return promise\n}\n"],"mappings":"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"}
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- ._root_11vle_1{min-height:0;color:var(--stylo-text);background:var(--stylo-bg);border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);flex-direction:column;display:flex;overflow:hidden}._root_11vle_1 .cm-scroller{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--stylo-border) 90%, transparent) transparent}._root_11vle_1 .cm-scroller::-webkit-scrollbar{width:10px;height:10px}._root_11vle_1 .cm-scroller::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--stylo-border) 90%, transparent);background-clip:padding-box;border:3px solid #0000;border-radius:6px}._root_11vle_1 .cm-scroller::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--stylo-text-muted) 55%, transparent);background-clip:padding-box}._root_11vle_1 .cm-scroller::-webkit-scrollbar-corner{background:0 0}._source_11vle_42{flex:auto;min-height:0;overflow:auto}._source_11vle_42 .cm-editor{height:100%}._source_11vle_42 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._inplace_11vle_57{flex:auto;min-height:0;overflow:auto}._toolbar_11vle_63{background:var(--stylo-bg);border-bottom:1px solid var(--stylo-border);flex-wrap:wrap;align-items:center;gap:2px;padding:4px 6px;display:flex}._toolbarGroup_11vle_75{align-items:center;gap:2px;display:flex}._toolbarButton_11vle_81{box-sizing:border-box;border-radius:calc(var(--stylo-radius) - 3px);min-width:28px;height:28px;color:var(--stylo-text-muted);font:inherit;cursor:pointer;background:0 0;border:0;justify-content:center;align-items:center;padding:0 5px;font-size:.75rem;font-weight:600;line-height:1;display:inline-flex}._toolbarButton_11vle_81:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._toolbarButton_11vle_81[data-active]{background:color-mix(in srgb, var(--stylo-accent) 12%, transparent);color:var(--stylo-text)}._toolbarButton_11vle_81:disabled{opacity:.4;cursor:default}._toolbarButton_11vle_81:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._toolbarButton_11vle_81 svg{width:16px;height:16px;display:block}._toolbarButton_11vle_81 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);font-size:.8125rem;font-weight:500}._toolbarSep_11vle_140{background:var(--stylo-border);align-self:stretch;width:1px;margin:4px 3px}._toolbarSticky_11vle_150{z-index:50;-webkit-overflow-scrolling:touch;scrollbar-width:none;font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);flex-wrap:nowrap;transition:opacity .15s;overflow-x:auto}._toolbarSticky_11vle_150::-webkit-scrollbar{display:none}._toolbarSticky_11vle_150 ._toolbarButton_11vle_81{flex:none;min-width:40px;height:40px}._toolbarStickyBottom_11vle_150{border-bottom:none;border-top:1px solid var(--stylo-border);box-shadow:0 -2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;bottom:0;left:0;right:0}._toolbarStickyTop_11vle_150{box-shadow:0 2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;top:0;left:0;right:0}._toolbarStickyHidden_11vle_219{opacity:0;pointer-events:none}._stickyToolbarRootBottom_11vle_230 ._source_11vle_42,._stickyToolbarRootBottom_11vle_230 ._inplace_11vle_57{padding-bottom:52px}._stickyToolbarRootTop_11vle_235 ._source_11vle_42,._stickyToolbarRootTop_11vle_235 ._inplace_11vle_57{padding-top:52px}._inplace_11vle_57 .cm-editor{height:100%}._inplace_11vle_57 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._split_11vle_249{flex:1;grid-template-columns:1fr 1fr;min-height:0;display:grid}._splitPane_11vle_256{box-sizing:border-box;flex-direction:column;min-height:0;display:flex;overflow:auto}._splitPane_11vle_256+._splitPane_11vle_256{border-left:1px solid var(--stylo-border)}._splitPane_11vle_256>._source_11vle_42{flex:1;min-height:0}._preview_11vle_282{min-height:0;font-size:var(--stylo-font-size,.9375rem);overflow-wrap:break-word;flex:auto;padding:.8em 1.06667em;line-height:1.75;overflow:auto}._preview_11vle_282>:first-child{margin-top:0}._preview_11vle_282>:last-child{margin-bottom:0}._preview_11vle_282 :is(h1,h2,h3,h4,h5,h6){font-weight:600}._preview_11vle_282 h1{font-size:var(--stylo-rhythm-h1-size,2.25em);line-height:var(--stylo-rhythm-h1-line,1.11111);margin:0 0 .888889em}._preview_11vle_282 h2{font-size:var(--stylo-rhythm-h2-size,1.5em);line-height:var(--stylo-rhythm-h2-line,1.33333);margin:2em 0 1em}._preview_11vle_282 h3{font-size:var(--stylo-rhythm-h3-size,1.25em);line-height:var(--stylo-rhythm-h3-line,1.6);margin:1.6em 0 .6em}._preview_11vle_282 :is(h4,h5,h6){line-height:var(--stylo-rhythm-h4-line,1.5);margin:1.5em 0 .5em}._preview_11vle_282 h4{font-size:var(--stylo-rhythm-h4-size,1em)}._preview_11vle_282 h5{font-size:var(--stylo-rhythm-h5-size,.9em)}._preview_11vle_282 h6{font-size:var(--stylo-rhythm-h6-size,.9em)}._preview_11vle_282 p{margin:1.25em 0}._preview_11vle_282 :is(ul,ol){margin:1.25em 0;padding-left:1.625em}._preview_11vle_282 li{margin:var(--stylo-rhythm-list-item-gap,.5em) 0}._preview_11vle_282 li>:is(ul,ol){margin:var(--stylo-rhythm-list-item-gap-nested,.75em) 0}._preview_11vle_282 a{color:var(--stylo-link);text-decoration:none}._preview_11vle_282 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 45%, transparent);padding:.1em .35em;font-size:.9em}._preview_11vle_282 pre{border-radius:var(--stylo-radius);background:color-mix(in srgb, var(--stylo-border) 35%, transparent);white-space:var(--stylo-preview-code-white-space,pre-wrap);overflow-wrap:break-word;margin:1.71429em 0;padding:.75rem .9rem;overflow-x:auto}._preview_11vle_282 pre code{background:0 0;padding:0;font-size:.85em}.stylo-tok-keyword{color:var(--stylo-syntax-keyword)}.stylo-tok-string{color:var(--stylo-syntax-string)}.stylo-tok-escape{color:var(--stylo-syntax-escape)}.stylo-tok-comment{color:var(--stylo-syntax-comment);font-style:italic}.stylo-tok-number{color:var(--stylo-syntax-number)}.stylo-tok-constant{color:var(--stylo-syntax-constant)}.stylo-tok-function{color:var(--stylo-syntax-function)}.stylo-tok-type{color:var(--stylo-syntax-type)}.stylo-tok-property{color:var(--stylo-syntax-property)}.stylo-tok-tag{color:var(--stylo-syntax-tag)}.stylo-tok-meta{color:var(--stylo-syntax-comment)}.stylo-tok-invalid{color:var(--stylo-syntax-invalid)}.stylo-frontmatter{border-left:3px solid var(--stylo-border);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 22%, transparent);color:var(--stylo-text-muted);font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);white-space:pre-wrap;margin:0 0 1.2em;padding:.7rem .9rem;font-size:.8125rem;line-height:1.5;overflow-x:auto}.stylo-frontmatter:before{content:"Frontmatter";font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);letter-spacing:.04em;text-transform:uppercase;margin-bottom:.5em;font-size:.7rem;font-weight:600;display:block}._preview_11vle_282 blockquote{border-left:.25rem solid var(--stylo-border);color:var(--stylo-text-muted);margin:1.6em 0;padding-left:1em}._preview_11vle_282 .stylo-callout{padding:var(--stylo-rhythm-callout-padding-y,.7em) var(--stylo-rhythm-callout-padding-x,1rem);border-left:.25rem solid var(--stylo-callout-accent,var(--stylo-border));border-top-right-radius:var(--stylo-radius);border-bottom-right-radius:var(--stylo-radius);background:color-mix(in srgb, var(--stylo-callout-accent,var(--stylo-border)) 10%, transparent);color:var(--stylo-text);margin:1.6em 0}._preview_11vle_282 .stylo-callout:before{content:attr(data-callout);letter-spacing:.04em;text-transform:uppercase;color:var(--stylo-callout-accent,var(--stylo-text-muted));margin-bottom:.2em;font-size:.8em;font-weight:700;display:block}._preview_11vle_282 .stylo-callout-note{--stylo-callout-accent:var(--stylo-callout-note,#3b82f6)}._preview_11vle_282 .stylo-callout-tip{--stylo-callout-accent:var(--stylo-callout-tip,#22c55e)}._preview_11vle_282 .stylo-callout-warn{--stylo-callout-accent:var(--stylo-callout-warn,#f59e0b)}._preview_11vle_282 .stylo-callout-danger{--stylo-callout-accent:var(--stylo-callout-danger,#ef4444)}._preview_11vle_282 .stylo-callout-example{--stylo-callout-accent:var(--stylo-callout-example,#a855f7)}._preview_11vle_282 .stylo-embed{border-left:.25rem solid var(--stylo-embed-accent,var(--stylo-border));margin:1.6em 0;padding-left:1em}._preview_11vle_282 .stylo-embed-content>:first-child{margin-top:0}._preview_11vle_282 .stylo-embed-content>:last-child{margin-bottom:0}._preview_11vle_282 .stylo-table-wrap{margin:2em 0;display:block;overflow-x:auto}._preview_11vle_282 table{border-collapse:collapse}._preview_11vle_282 :is(th,td){border:1px solid var(--stylo-table-border);text-align:left;padding:.35em .6em}._preview_11vle_282 thead th{background:var(--stylo-table-header-bg);font-weight:600}._preview_11vle_282 tbody tr:nth-child(2n) td{background:var(--stylo-table-stripe-bg)}._preview_11vle_282 hr{border:none;border-top:1px solid var(--stylo-border);margin:1em 0}._preview_11vle_282 .katex-display{padding:.25em 0;overflow:auto hidden}.stylo{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--stylo-bg:#fff;--stylo-surface-floating:#fff;--stylo-text:#09090b;--stylo-text-muted:#71717a;--stylo-border:#e4e4e7;--stylo-accent:#18181b;--stylo-link:#2563eb;--stylo-ring:#a1a1aa;--stylo-radius:.5rem;--stylo-font-size:.9375rem;--stylo-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--stylo-font-family-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--stylo-table-border:var(--stylo-border);--stylo-table-header-bg:color-mix(in srgb, var(--stylo-border) 30%, transparent);--stylo-table-stripe-bg:transparent;--stylo-guide:var(--stylo-border);--stylo-syntax-keyword:#7c3aed;--stylo-syntax-string:#0a7c2f;--stylo-syntax-escape:#b45309;--stylo-syntax-comment:#8a8f98;--stylo-syntax-number:#b45309;--stylo-syntax-constant:#b45309;--stylo-syntax-function:#2563eb;--stylo-syntax-type:#a16207;--stylo-syntax-property:#0f766e;--stylo-syntax-tag:#cf222e;--stylo-syntax-invalid:#dc2626;--stylo-rhythm-h1-size:2.25em;--stylo-rhythm-h1-line:1.11111;--stylo-rhythm-h2-size:1.5em;--stylo-rhythm-h2-line:1.33333;--stylo-rhythm-h3-size:1.25em;--stylo-rhythm-h3-line:1.6;--stylo-rhythm-h4-size:1em;--stylo-rhythm-h4-line:1.5;--stylo-rhythm-h5-size:.9em;--stylo-rhythm-h5-line:1.5;--stylo-rhythm-h6-size:.9em;--stylo-rhythm-h6-line:1.5;--stylo-rhythm-list-item-gap:.5em;--stylo-rhythm-list-item-gap-nested:.75em;--stylo-rhythm-callout-padding-x:1rem;--stylo-rhythm-callout-padding-y:.7em}:where(.dark,[data-theme=dark]) .stylo,.stylo:where(.dark,[data-theme=dark]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--stylo-bg:#09090b;--stylo-surface-floating:#09090b;--stylo-text:#fafafa;--stylo-text-muted:#a1a1aa;--stylo-border:#27272a;--stylo-accent:#fafafa;--stylo-link:#60a5fa;--stylo-ring:#52525b;--stylo-syntax-keyword:#c678dd;--stylo-syntax-string:#98c379;--stylo-syntax-escape:#d19a66;--stylo-syntax-comment:#7f848e;--stylo-syntax-number:#d19a66;--stylo-syntax-constant:#d19a66;--stylo-syntax-function:#61afef;--stylo-syntax-type:#e5c07b;--stylo-syntax-property:#56b6c2;--stylo-syntax-tag:#e06c75;--stylo-syntax-invalid:#e06c75}._root_18hcd_3{color:var(--stylo-text);font:inherit;grid-template-columns:1fr 1fr;gap:12px;display:grid}@media (width<=460px){._root_18hcd_3{grid-template-columns:1fr}}._col_18hcd_17{border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);background:var(--stylo-bg);flex-direction:column;min-width:0;display:flex}._colHead_18hcd_26{border-bottom:1px solid var(--stylo-border);letter-spacing:.04em;text-transform:uppercase;color:var(--stylo-text-muted);margin:0;padding:8px 12px;font-size:.75rem;font-weight:600}._list_18hcd_37{flex-direction:column;flex:1;gap:2px;margin:0;padding:4px;list-style:none;display:flex}._row_18hcd_47{border-radius:calc(var(--stylo-radius) - 3px);background:var(--stylo-bg);align-items:center;gap:8px;padding:5px 6px;display:flex}._row_18hcd_47:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._row_18hcd_47:hover{background:color-mix(in srgb, var(--stylo-border) 30%, transparent)}._row_18hcd_47[data-dragging]{background:var(--stylo-surface-floating,#fff);opacity:.9;box-shadow:0 4px 16px #00000024}._handle_18hcd_73{border-radius:calc(var(--stylo-radius) - 4px);width:20px;height:22px;color:var(--stylo-text-muted);font:inherit;cursor:grab;touch-action:none;background:0 0;border:1px solid #0000;flex:none;justify-content:center;align-items:center;padding:0;line-height:1;display:inline-flex}._handle_18hcd_73:hover{background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._handle_18hcd_73:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-1px}._handle_18hcd_73:active{cursor:grabbing}._glyph_18hcd_105{width:18px;height:18px;color:var(--stylo-text-muted);flex:none;justify-content:center;align-items:center;display:inline-flex}._glyph_18hcd_105 svg{width:16px;height:16px;display:block}._label_18hcd_121{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:.8125rem;overflow:hidden}._rowActions_18hcd_130{flex:none;gap:2px;display:flex}._iconButton_18hcd_136,._textButton_18hcd_137{box-sizing:border-box;border-radius:calc(var(--stylo-radius) - 4px);color:var(--stylo-text-muted);font:inherit;cursor:pointer;background:0 0;border:1px solid #0000}._iconButton_18hcd_136{width:22px;height:22px;font-size:.8125rem;line-height:1}._textButton_18hcd_137{padding:3px 8px;font-size:.75rem}._iconButton_18hcd_136:hover:not(:disabled),._textButton_18hcd_137:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._iconButton_18hcd_136:focus-visible,._textButton_18hcd_137:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-1px}._iconButton_18hcd_136:disabled{opacity:.35;cursor:default}._footer_18hcd_176{border-top:1px solid var(--stylo-border);flex-wrap:wrap;gap:6px;padding:6px;display:flex}._empty_18hcd_184{color:var(--stylo-text-muted);padding:8px 6px;font-size:.8125rem}._srOnly_18hcd_190{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
1
+ ._root_qpl96_1{min-height:0;color:var(--stylo-text);background:var(--stylo-bg);border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);flex-direction:column;display:flex;overflow:hidden}._root_qpl96_1 .cm-scroller{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--stylo-border) 90%, transparent) transparent}._root_qpl96_1 .cm-scroller::-webkit-scrollbar{width:10px;height:10px}._root_qpl96_1 .cm-scroller::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--stylo-border) 90%, transparent);background-clip:padding-box;border:3px solid #0000;border-radius:6px}._root_qpl96_1 .cm-scroller::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--stylo-text-muted) 55%, transparent);background-clip:padding-box}._root_qpl96_1 .cm-scroller::-webkit-scrollbar-corner{background:0 0}._source_qpl96_42{flex:auto;min-height:0;overflow:auto}._source_qpl96_42 .cm-editor{height:100%}._source_qpl96_42 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._inplace_qpl96_57{flex:auto;min-height:0;overflow:auto}._toolbar_qpl96_63{background:var(--stylo-bg);border-bottom:1px solid var(--stylo-border);flex-wrap:wrap;align-items:center;gap:2px;padding:4px 6px;display:flex}._toolbarGroup_qpl96_75{align-items:center;gap:2px;display:flex}._toolbarButton_qpl96_81{box-sizing:border-box;border-radius:calc(var(--stylo-radius) - 3px);min-width:28px;height:28px;color:var(--stylo-text-muted);font:inherit;cursor:pointer;background:0 0;border:0;justify-content:center;align-items:center;padding:0 5px;font-size:.75rem;font-weight:600;line-height:1;display:inline-flex}._toolbarButton_qpl96_81:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._toolbarButton_qpl96_81[data-active]{background:color-mix(in srgb, var(--stylo-accent) 12%, transparent);color:var(--stylo-text)}._toolbarButton_qpl96_81:disabled{opacity:.4;cursor:default}._toolbarButton_qpl96_81:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._toolbarButton_qpl96_81 svg{width:16px;height:16px;display:block}._toolbarButton_qpl96_81 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);font-size:.8125rem;font-weight:500}._toolbarSep_qpl96_140{background:var(--stylo-border);align-self:stretch;width:1px;margin:4px 3px}._toolbarSticky_qpl96_150{z-index:50;-webkit-overflow-scrolling:touch;scrollbar-width:none;font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);flex-wrap:nowrap;transition:opacity .15s;overflow-x:auto}._toolbarSticky_qpl96_150::-webkit-scrollbar{display:none}._toolbarSticky_qpl96_150 ._toolbarButton_qpl96_81{flex:none;min-width:40px;height:40px}._toolbarStickyBottom_qpl96_150{border-bottom:none;border-top:1px solid var(--stylo-border);box-shadow:0 -2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;bottom:0;left:0;right:0}._toolbarStickyTop_qpl96_150{box-shadow:0 2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;top:0;left:0;right:0}._toolbarOverflow_qpl96_221{flex-wrap:nowrap;min-width:0;overflow:visible}._toolbarOverflow_qpl96_221:not(._toolbarSticky_qpl96_150){position:relative}._toolbarMore_qpl96_219{flex:none;margin-inline-start:auto}._toolbarMeasureClip_qpl96_241{top:0;visibility:hidden;pointer-events:none;width:0;height:0;position:absolute;inset-inline-start:0;overflow:hidden}._toolbarMeasure_qpl96_241{align-items:center;gap:2px;width:max-content;display:flex}._toolbarMenu_qpl96_259{top:100%;z-index:60;border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);background:var(--stylo-bg);min-width:180px;box-shadow:0 4px 14px color-mix(in srgb, var(--stylo-text) 14%, transparent);flex-direction:column;padding:4px;display:flex;position:absolute;inset-inline-end:0}._toolbarStickyBottom_qpl96_150 ._toolbarMenu_qpl96_259{top:auto;bottom:100%}._toolbarMenuItem_qpl96_280{border-radius:calc(var(--stylo-radius) - 3px);color:var(--stylo-text);font:inherit;text-align:start;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:6px 8px;font-size:.8125rem;display:flex}._toolbarMenuItem_qpl96_280:hover:not(:disabled),._toolbarMenuItem_qpl96_280:focus-visible{background:color-mix(in srgb, var(--stylo-border) 45%, transparent);outline:none}._toolbarMenuItem_qpl96_280[data-active]{background:color-mix(in srgb, var(--stylo-accent) 12%, transparent)}._toolbarMenuItem_qpl96_280:disabled{opacity:.4;cursor:default}._toolbarMenuItem_qpl96_280 svg{flex:none;width:16px;height:16px;display:block}._toolbarMenuDivider_qpl96_317{background:var(--stylo-border);height:1px;margin:4px 2px}._toolbarStickyHidden_qpl96_324{opacity:0;pointer-events:none}._stickyToolbarRootBottom_qpl96_335 ._source_qpl96_42,._stickyToolbarRootBottom_qpl96_335 ._inplace_qpl96_57{padding-bottom:52px}._stickyToolbarRootTop_qpl96_340 ._source_qpl96_42,._stickyToolbarRootTop_qpl96_340 ._inplace_qpl96_57{padding-top:52px}._inplace_qpl96_57 .cm-editor{height:100%}._inplace_qpl96_57 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._split_qpl96_354{flex:1;grid-template-columns:1fr 1fr;min-height:0;display:grid}._splitPane_qpl96_361{box-sizing:border-box;flex-direction:column;min-height:0;display:flex;overflow:auto}._splitPane_qpl96_361+._splitPane_qpl96_361{border-left:1px solid var(--stylo-border)}._splitPane_qpl96_361>._source_qpl96_42{flex:1;min-height:0}._preview_qpl96_387{min-height:0;font-size:var(--stylo-font-size,.9375rem);overflow-wrap:break-word;flex:auto;padding:.8em 1.06667em;line-height:1.75;overflow:auto}._preview_qpl96_387>:first-child{margin-top:0}._preview_qpl96_387>:last-child{margin-bottom:0}._preview_qpl96_387 :is(h1,h2,h3,h4,h5,h6){font-weight:600}._preview_qpl96_387 h1{font-size:var(--stylo-rhythm-h1-size,2.25em);line-height:var(--stylo-rhythm-h1-line,1.11111);margin:0 0 .888889em}._preview_qpl96_387 h2{font-size:var(--stylo-rhythm-h2-size,1.5em);line-height:var(--stylo-rhythm-h2-line,1.33333);margin:2em 0 1em}._preview_qpl96_387 h3{font-size:var(--stylo-rhythm-h3-size,1.25em);line-height:var(--stylo-rhythm-h3-line,1.6);margin:1.6em 0 .6em}._preview_qpl96_387 :is(h4,h5,h6){line-height:var(--stylo-rhythm-h4-line,1.5);margin:1.5em 0 .5em}._preview_qpl96_387 h4{font-size:var(--stylo-rhythm-h4-size,1em)}._preview_qpl96_387 h5{font-size:var(--stylo-rhythm-h5-size,.9em)}._preview_qpl96_387 h6{font-size:var(--stylo-rhythm-h6-size,.9em)}._preview_qpl96_387 p{margin:1.25em 0}._preview_qpl96_387 :is(ul,ol){margin:1.25em 0;padding-left:1.625em}._preview_qpl96_387 li{margin:var(--stylo-rhythm-list-item-gap,.5em) 0}._preview_qpl96_387 li>:is(ul,ol){margin:var(--stylo-rhythm-list-item-gap-nested,.75em) 0}._preview_qpl96_387 a{color:var(--stylo-link);text-decoration:none}._preview_qpl96_387 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 45%, transparent);padding:.1em .35em;font-size:.9em}._preview_qpl96_387 pre{border-radius:var(--stylo-radius);background:color-mix(in srgb, var(--stylo-border) 35%, transparent);white-space:var(--stylo-preview-code-white-space,pre-wrap);overflow-wrap:break-word;margin:1.71429em 0;padding:.75rem .9rem;overflow-x:auto}._preview_qpl96_387 pre code{background:0 0;padding:0;font-size:.85em}.stylo-tok-keyword{color:var(--stylo-syntax-keyword)}.stylo-tok-string{color:var(--stylo-syntax-string)}.stylo-tok-escape{color:var(--stylo-syntax-escape)}.stylo-tok-comment{color:var(--stylo-syntax-comment);font-style:italic}.stylo-tok-number{color:var(--stylo-syntax-number)}.stylo-tok-constant{color:var(--stylo-syntax-constant)}.stylo-tok-function{color:var(--stylo-syntax-function)}.stylo-tok-type{color:var(--stylo-syntax-type)}.stylo-tok-property{color:var(--stylo-syntax-property)}.stylo-tok-tag{color:var(--stylo-syntax-tag)}.stylo-tok-meta{color:var(--stylo-syntax-comment)}.stylo-tok-invalid{color:var(--stylo-syntax-invalid)}.stylo-frontmatter{border-left:3px solid var(--stylo-border);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 22%, transparent);color:var(--stylo-text-muted);font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);white-space:pre-wrap;margin:0 0 1.2em;padding:.7rem .9rem;font-size:.8125rem;line-height:1.5;overflow-x:auto}.stylo-frontmatter:before{content:"Frontmatter";font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);letter-spacing:.04em;text-transform:uppercase;margin-bottom:.5em;font-size:.7rem;font-weight:600;display:block}._preview_qpl96_387 blockquote{border-left:.25rem solid var(--stylo-border);color:var(--stylo-text-muted);margin:1.6em 0;padding-left:1em}._preview_qpl96_387 .stylo-callout{padding:var(--stylo-rhythm-callout-padding-y,.7em) var(--stylo-rhythm-callout-padding-x,1rem);border-left:.25rem solid var(--stylo-callout-accent,var(--stylo-border));border-top-right-radius:var(--stylo-radius);border-bottom-right-radius:var(--stylo-radius);background:color-mix(in srgb, var(--stylo-callout-accent,var(--stylo-border)) 10%, transparent);color:var(--stylo-text);margin:1.6em 0}._preview_qpl96_387 .stylo-callout:before{content:attr(data-callout);letter-spacing:.04em;text-transform:uppercase;color:var(--stylo-callout-accent,var(--stylo-text-muted));margin-bottom:.2em;font-size:.8em;font-weight:700;display:block}._preview_qpl96_387 .stylo-callout-note{--stylo-callout-accent:var(--stylo-callout-note,#3b82f6)}._preview_qpl96_387 .stylo-callout-tip{--stylo-callout-accent:var(--stylo-callout-tip,#22c55e)}._preview_qpl96_387 .stylo-callout-warn{--stylo-callout-accent:var(--stylo-callout-warn,#f59e0b)}._preview_qpl96_387 .stylo-callout-danger{--stylo-callout-accent:var(--stylo-callout-danger,#ef4444)}._preview_qpl96_387 .stylo-callout-example{--stylo-callout-accent:var(--stylo-callout-example,#a855f7)}._preview_qpl96_387 .stylo-embed{border-left:.25rem solid var(--stylo-embed-accent,var(--stylo-border));margin:1.6em 0;padding-left:1em}._preview_qpl96_387 .stylo-embed-content>:first-child{margin-top:0}._preview_qpl96_387 .stylo-embed-content>:last-child{margin-bottom:0}._preview_qpl96_387 .stylo-table-wrap{margin:2em 0;display:block;overflow-x:auto}._preview_qpl96_387 table{border-collapse:collapse}._preview_qpl96_387 :is(th,td){border:1px solid var(--stylo-table-border);text-align:left;padding:.35em .6em}._preview_qpl96_387 thead th{background:var(--stylo-table-header-bg);font-weight:600}._preview_qpl96_387 tbody tr:nth-child(2n) td{background:var(--stylo-table-stripe-bg)}._preview_qpl96_387 hr{border:none;border-top:1px solid var(--stylo-border);margin:1em 0}._preview_qpl96_387 .katex-display{padding:.25em 0;overflow:auto hidden}.stylo{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--stylo-bg:#fff;--stylo-surface-floating:#fff;--stylo-text:#09090b;--stylo-text-muted:#71717a;--stylo-border:#e4e4e7;--stylo-accent:#18181b;--stylo-link:#2563eb;--stylo-ring:#a1a1aa;--stylo-radius:.5rem;--stylo-font-size:.9375rem;--stylo-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--stylo-font-family-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--stylo-table-border:var(--stylo-border);--stylo-table-header-bg:color-mix(in srgb, var(--stylo-border) 30%, transparent);--stylo-table-stripe-bg:transparent;--stylo-guide:var(--stylo-border);--stylo-syntax-keyword:#7c3aed;--stylo-syntax-string:#0a7c2f;--stylo-syntax-escape:#b45309;--stylo-syntax-comment:#8a8f98;--stylo-syntax-number:#b45309;--stylo-syntax-constant:#b45309;--stylo-syntax-function:#2563eb;--stylo-syntax-type:#a16207;--stylo-syntax-property:#0f766e;--stylo-syntax-tag:#cf222e;--stylo-syntax-invalid:#dc2626;--stylo-rhythm-h1-size:2.25em;--stylo-rhythm-h1-line:1.11111;--stylo-rhythm-h2-size:1.5em;--stylo-rhythm-h2-line:1.33333;--stylo-rhythm-h3-size:1.25em;--stylo-rhythm-h3-line:1.6;--stylo-rhythm-h4-size:1em;--stylo-rhythm-h4-line:1.5;--stylo-rhythm-h5-size:.9em;--stylo-rhythm-h5-line:1.5;--stylo-rhythm-h6-size:.9em;--stylo-rhythm-h6-line:1.5;--stylo-rhythm-list-item-gap:.5em;--stylo-rhythm-list-item-gap-nested:.75em;--stylo-rhythm-callout-padding-x:1rem;--stylo-rhythm-callout-padding-y:.7em}:where(.dark,[data-theme=dark]) .stylo,.stylo:where(.dark,[data-theme=dark]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--stylo-bg:#09090b;--stylo-surface-floating:#09090b;--stylo-text:#fafafa;--stylo-text-muted:#a1a1aa;--stylo-border:#27272a;--stylo-accent:#fafafa;--stylo-link:#60a5fa;--stylo-ring:#52525b;--stylo-syntax-keyword:#c678dd;--stylo-syntax-string:#98c379;--stylo-syntax-escape:#d19a66;--stylo-syntax-comment:#7f848e;--stylo-syntax-number:#d19a66;--stylo-syntax-constant:#d19a66;--stylo-syntax-function:#61afef;--stylo-syntax-type:#e5c07b;--stylo-syntax-property:#56b6c2;--stylo-syntax-tag:#e06c75;--stylo-syntax-invalid:#e06c75}._root_18hcd_3{color:var(--stylo-text);font:inherit;grid-template-columns:1fr 1fr;gap:12px;display:grid}@media (width<=460px){._root_18hcd_3{grid-template-columns:1fr}}._col_18hcd_17{border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);background:var(--stylo-bg);flex-direction:column;min-width:0;display:flex}._colHead_18hcd_26{border-bottom:1px solid var(--stylo-border);letter-spacing:.04em;text-transform:uppercase;color:var(--stylo-text-muted);margin:0;padding:8px 12px;font-size:.75rem;font-weight:600}._list_18hcd_37{flex-direction:column;flex:1;gap:2px;margin:0;padding:4px;list-style:none;display:flex}._row_18hcd_47{border-radius:calc(var(--stylo-radius) - 3px);background:var(--stylo-bg);align-items:center;gap:8px;padding:5px 6px;display:flex}._row_18hcd_47:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._row_18hcd_47:hover{background:color-mix(in srgb, var(--stylo-border) 30%, transparent)}._row_18hcd_47[data-dragging]{background:var(--stylo-surface-floating,#fff);opacity:.9;box-shadow:0 4px 16px #00000024}._handle_18hcd_73{border-radius:calc(var(--stylo-radius) - 4px);width:20px;height:22px;color:var(--stylo-text-muted);font:inherit;cursor:grab;touch-action:none;background:0 0;border:1px solid #0000;flex:none;justify-content:center;align-items:center;padding:0;line-height:1;display:inline-flex}._handle_18hcd_73:hover{background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._handle_18hcd_73:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-1px}._handle_18hcd_73:active{cursor:grabbing}._glyph_18hcd_105{width:18px;height:18px;color:var(--stylo-text-muted);flex:none;justify-content:center;align-items:center;display:inline-flex}._glyph_18hcd_105 svg{width:16px;height:16px;display:block}._label_18hcd_121{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:.8125rem;overflow:hidden}._rowActions_18hcd_130{flex:none;gap:2px;display:flex}._iconButton_18hcd_136,._textButton_18hcd_137{box-sizing:border-box;border-radius:calc(var(--stylo-radius) - 4px);color:var(--stylo-text-muted);font:inherit;cursor:pointer;background:0 0;border:1px solid #0000}._iconButton_18hcd_136{width:22px;height:22px;font-size:.8125rem;line-height:1}._textButton_18hcd_137{padding:3px 8px;font-size:.75rem}._iconButton_18hcd_136:hover:not(:disabled),._textButton_18hcd_137:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._iconButton_18hcd_136:focus-visible,._textButton_18hcd_137:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-1px}._iconButton_18hcd_136:disabled{opacity:.35;cursor:default}._footer_18hcd_176{border-top:1px solid var(--stylo-border);flex-wrap:wrap;gap:6px;padding:6px;display:flex}._empty_18hcd_184{color:var(--stylo-text-muted);padding:8px 6px;font-size:.8125rem}._srOnly_18hcd_190{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
2
2
  /*$vite$:1*/