import type{LyraAnchorTargetEventMap}from'../../../internal/anchor-target.js';import{type LyraMarkedParser}from'./markdown-loader.js';import{type ShikiLanguageSource}from'../code-block/shiki-types.js';import{type PendingHighlight,type MarkdownHeadingItem as SharedMarkdownHeadingItem,type MarkdownHtmlMode}from'./markdown-shared.js';import{MarkdownRuntimeBase,type MarkdownHighlightAttempt,type MarkdownVariantContext}from'./markdown-base.class.js'; /** Re-exported so `markdown-core.ts`'s `export *` keeps exposing this from the same public path as * before this type moved into the pair's shared module -- see `markdown-shared.ts`'s class doc. */ export type MarkdownHeadingItem=SharedMarkdownHeadingItem; /** Peer-neutral public alias matching the full variant's `getMarked()` signature. */ export type Marked=LyraMarkedParser; /** `true`-defaulting boolean attribute converter -- Lit's default presence-based `type: Boolean` * can never be set back to `false` from a plain-HTML attribute once the property's own default is * `true` (removing an attribute that was never present fires no `attributeChangedCallback`), so * `fromAttribute` checks the literal string instead. Shared by `gfm` and `highlightCode`. */ export interface LyraMarkdownCoreEventMap extends LyraAnchorTargetEventMap{'lr-render-error':CustomEvent<{error:unknown;}>;'lr-link-click':CustomEvent<{href:string;}>;'lr-content-settled':CustomEvent;} /** * `` — a build-lean variant of `` for a consumer whose * `languages` map already covers every language it will ever render. Every other capability (GFM * tables, fenced code blocks, links, blockquotes, heading anchors, text-quote highlights, math) is * identical to `` -- only fenced-code-block highlighting differs: this component's * own module never textually contains a call to (or import of) `loadShikiHighlighter` (the * ~200-language default dynamic-import table `` can call). A consumer * importing this entry point instead of `markdown.js` gets a genuinely shiki-full-table-free * build. A fenced block whose language isn't a key in `languages` always renders the plain-text * fallback -- there is no default/full-table highlighter here to fall back to, mirroring * ``'s identical contract for the sibling component. * * Built on the optional peer dependencies `marked` (parsing) and `dompurify` (sanitizing), both * lazy-loaded via `markdown-loader.ts` on first connect. * * Rendering never ships unsanitized or broken markup silently: * - If `marked` fails to load, or throws while parsing malformed input, the * component falls back to plain text (`white-space: pre-wrap`, no HTML * parsing at all) and fires `lr-render-error`. * - If `htmlMode` is `sanitize` (the default) and `dompurify` fails to load, the * component *also* falls back to plain text + `lr-render-error` — it * never renders marked's raw HTML output when sanitization was requested * (or defaulted to) but is unavailable, even though `marked` itself loaded * fine. * - If `htmlMode` is explicitly `trusted`, marked's raw output renders as-is * regardless of whether `dompurify` is installed — the consumer opted out * of sanitization, so `dompurify`'s absence is irrelevant to that path. * * That same plain-text fallback rendering (`data-fallback` on the `content` * part) is also, unconditionally and by default, a brief *transient* state on * every connect, not just a failure path: `connectedCallback()`'s dynamic * `import()` of `marked`/`dompurify` (see `markdown-loader.ts`) is * asynchronous, so the very first paint of any `` on a page * shows plain text for at least one microtask — even when both peers are * already installed and load without error — until that import resolves and * a second render replaces it with the real Markdown output. Call `preloadMarkdown()` before * mounting the first instance to avoid that window; later instances always adopt its settled * shared dependency cache synchronously. * Disconnecting and reconnecting while the shared load is pending invalidates the earlier * connection's settlement callback, so the current connection parses only once. * * `heading`/`code`/`blockquote`/`table`/`link`/`image` tokens are rendered * through a `marked` renderer override that injects `part="..."` attributes * directly into the produced HTML — a single pass, not a second DOM walk * after insertion. * * Fenced code blocks are syntax-highlighted via the same fine-grained `shiki/core` recipe * `` uses (`highlightCode`, default `true` — gated by whether a fenced * block's language is a key in `languages`, since there is no default highlighter here to gate on * "is shiki installed at all"). The very first render of any content is always plain (identical to * ``'s own output); highlighting arrives as an asynchronous upgrade one render * later, once the fine-grained highlighter resolves. No highlighting is attempted while * `streaming` is `true` — it applies once a stream settles, so there is no added per-chunk cost * while content is still arriving. * * Highlighted blocks follow the page's resolved theme. Shiki emits both palettes at once, so * `[part="content"]` carries `data-dark-theme="true"` whenever the component's own resolved * `--lr-color-text` is lighter than its `--lr-color-surface`, and the stylesheet then paints each * token from `--shiki-dark`/`--shiki-dark-bg` instead of the light inline color. It keys off the * resolved tokens rather than `prefers-color-scheme`, so a consumer theming with * `--lr-theme-color-*` independently of the OS setting gets the dark palette too -- the same * mechanism `` uses for its own `[part="body"]`. * * When `heading-anchors` is set, every rendered heading's slug (computed via the shared * GitHub-slugger-style `Slugger`) is stamped as its `id`; `getHeadingTree()` computes that same * outline on every parse regardless of `heading-anchors`, so a host can build a table of contents * even while ids aren't in the DOM yet. `scrollToAnchor()` (from the adopted `DocumentAnchorTarget` * mixin) resolves `fragment` anchors against that outline and `text-quote` anchors via * `internal/text-quote.ts`'s shared scope/resolve helpers; `highlights` re-resolve by quote after * every render (never by node identity), so a highlight painted before a `streaming` update * finishes still finds its quote once the matching text arrives. Highlight painting uses * `internal/text-highlights.ts`'s `acquireHighlightHandle()` -- the CSS Custom Highlight API where * the browser supports it (no DOM mutation at all), a ``-wrap fallback otherwise. Quote * resolution indexes at most 1,000,000 code units/20,000 text nodes per content generation, bounds * each quote/context field to 4,096 code units and each pass to 4,000,000 scanned code units, and * paints at most 100 host highlights from a 1,000-entry candidate window while preserving the * active entry from the bounded host snapshot. * * `math` renders `$...$`/`$$...$$` TeX as MathML via the optional `katex` peer's * `renderToString(tex, { output: 'mathml' })` -- MathML Core renders natively and accessibly in * evergreen browsers with no extra stylesheet or webfont needing to cross the shadow boundary. A * missing `katex` peer renders the literal, unparsed TeX source (delimiters included) and fires one * `lr-render-error`. * * @customElement lr-markdown-core * @event lr-link-click - Fired (and the click prevented) when a rendered * link's `href` starts with `internal-link-prefix`. `detail: { href: string }`. * Ordinary external links navigate normally * (in `link-target`) and never fire this event. * @event lr-render-error - Fired whenever rendering falls back to plain * text, or `math` is set but the `katex` peer isn't installed. `detail: { error: unknown }`. * @event lr-highlight-activate - A painted `text-quote` highlight was clicked. * `detail: { highlightId }`. * @event lr-text-select - Fired on selection end inside the rendered content. `detail: { text, * anchor, rects }`; `anchor` is a `text-quote` `LyraAnchor` scoped to the rendered content, or * `null` if the selection couldn't be anchored. * @event lr-anchor-result - Fired after an `anchor` property assignment or a `scrollToAnchor()` * call is applied. `detail: { found }`. * @event lr-content-settled - Fired whenever newly-rendered content actually reaches * `[part="content"]` -- including a transient plain-text fallback frame and a later async * syntax-highlight upgrade, not only a final parsed render. `detail: null`. Composed and * bubbling, so a host composing this element inside a free-form container (e.g. * ``'s default slot) can listen for it to drive auto-scroll; see that * component's own docs. * @csspart content - The wrapper around the rendered (or plain-text * fallback) output; respects `max-height`. * @csspart heading - Every rendered `

`–`

` (shifted by * `heading-offset`). * @csspart paragraph - Every rendered `

`. * @csspart list - Every rendered `