@aceshooting/lyra-ui 3.8.0 → 3.9.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 (51) hide show
  1. package/README.md +3 -2
  2. package/custom-elements.json +1264 -97
  3. package/dist/components/activity-feed/activity-feed.class.d.ts +13 -1
  4. package/dist/components/activity-feed/activity-feed.class.d.ts.map +1 -1
  5. package/dist/components/activity-feed/activity-feed.class.js +10 -2
  6. package/dist/components/activity-feed/activity-feed.class.js.map +1 -1
  7. package/dist/components/graph/graph-canvas.d.ts +3 -0
  8. package/dist/components/graph/graph-canvas.d.ts.map +1 -1
  9. package/dist/components/graph/graph-canvas.js +5 -0
  10. package/dist/components/graph/graph-canvas.js.map +1 -1
  11. package/dist/components/graph/graph.class.d.ts +14 -0
  12. package/dist/components/graph/graph.class.d.ts.map +1 -1
  13. package/dist/components/graph/graph.class.js +28 -0
  14. package/dist/components/graph/graph.class.js.map +1 -1
  15. package/dist/components/graph/graph.styles.d.ts.map +1 -1
  16. package/dist/components/graph/graph.styles.js +6 -0
  17. package/dist/components/graph/graph.styles.js.map +1 -1
  18. package/dist/components/input/input.class.d.ts +3 -1
  19. package/dist/components/input/input.class.d.ts.map +1 -1
  20. package/dist/components/input/input.class.js +2 -0
  21. package/dist/components/input/input.class.js.map +1 -1
  22. package/dist/components/markdown/markdown-core.class.d.ts +334 -0
  23. package/dist/components/markdown/markdown-core.class.d.ts.map +1 -0
  24. package/dist/components/markdown/markdown-core.class.js +999 -0
  25. package/dist/components/markdown/markdown-core.class.js.map +1 -0
  26. package/dist/components/markdown/markdown-core.d.ts +2 -0
  27. package/dist/components/markdown/markdown-core.d.ts.map +1 -0
  28. package/dist/components/markdown/markdown-core.js +5 -0
  29. package/dist/components/markdown/markdown-core.js.map +1 -0
  30. package/dist/components/thread-list/thread-list.class.d.ts +10 -0
  31. package/dist/components/thread-list/thread-list.class.d.ts.map +1 -1
  32. package/dist/components/thread-list/thread-list.class.js +9 -1
  33. package/dist/components/thread-list/thread-list.class.js.map +1 -1
  34. package/dist/components/usage-badge/usage-badge.class.d.ts +10 -1
  35. package/dist/components/usage-badge/usage-badge.class.d.ts.map +1 -1
  36. package/dist/components/usage-badge/usage-badge.class.js +9 -0
  37. package/dist/components/usage-badge/usage-badge.class.js.map +1 -1
  38. package/dist/internal/root-registration-allowlist.d.ts +1 -1
  39. package/dist/internal/root-registration-allowlist.d.ts.map +1 -1
  40. package/dist/internal/root-registration-allowlist.js +1 -0
  41. package/dist/internal/root-registration-allowlist.js.map +1 -1
  42. package/dist/lyra.d.ts +3 -0
  43. package/dist/lyra.d.ts.map +1 -1
  44. package/dist/lyra.js +2 -0
  45. package/dist/lyra.js.map +1 -1
  46. package/llms-full.txt +83 -9
  47. package/llms.txt +2 -1
  48. package/package.json +31 -31
  49. package/vscode-css-data.json +7 -0
  50. package/vscode-html-data.json +106 -6
  51. package/web-types.json +129 -7
@@ -8,7 +8,7 @@
8
8
  "declarations": [
9
9
  {
10
10
  "kind": "class",
11
- "description": "`<lyra-activity-feed>` — an append-only streaming log of granular agent actions (\"Searching the\nweb…\", \"Read src/index.ts\"), collapsing to a localized \"Completed N steps\" summary once the run\nis over. Entries never change state once added (a step whose status mutates in place belongs to\n`<lyra-task-list>` instead). Implements the shared follow (stick-to-bottom) contract: `follow`\nis a component-managed, host-assignable property, released on user scroll-up and re-engaged at\nthe bottom, firing `lyra-follow-change` on every transition (mount excluded). At/above\n`virtualizeThreshold` entries, the body renders through an internal `<lyra-virtual-list>`\ninstead of a plain keyed list — same list semantics either way, keyed by `id`.",
11
+ "description": "`<lyra-activity-feed>` — an append-only streaming log of granular agent actions (\"Searching the\nweb…\", \"Read src/index.ts\"), collapsing to a localized \"Completed N steps\" summary once the run\nis over. Entries never change state once added (a step whose status mutates in place belongs to\n`<lyra-task-list>` instead). Implements the shared follow (stick-to-bottom) contract: `follow`\nis a component-managed, host-assignable property, released on user scroll-up and re-engaged at\nthe bottom, firing `lyra-follow-change` on every transition (mount excluded). At/above\n`virtualizeThreshold` entries, the body renders through an internal `<lyra-virtual-list>`\ninstead of a plain keyed list — same list semantics either way, keyed by `id`.\n\nEach entry's `text` renders as plain text by default; a host needing richer per-entry content\n(rendered markdown, a trailing tool-call chip list, etc.) sets `renderText` to fully replace it,\nidentically whether or not the feed is currently virtualized.",
12
12
  "name": "LyraActivityFeed",
13
13
  "cssProperties": [
14
14
  {
@@ -35,7 +35,7 @@
35
35
  "name": "entry-icon"
36
36
  },
37
37
  {
38
- "description": "The entry's `text`.",
38
+ "description": "The entry's `text`. Not rendered while `renderText` is set — its returned content replaces this part entirely.",
39
39
  "name": "entry-text"
40
40
  },
41
41
  {
@@ -259,6 +259,14 @@
259
259
  "module": "src/internal/lyra-element.ts"
260
260
  }
261
261
  },
262
+ {
263
+ "kind": "field",
264
+ "name": "renderText",
265
+ "type": {
266
+ "text": "(entry: ActivityEntry) => TemplateResult | undefined"
267
+ },
268
+ "description": "Overrides the default plain-text `[part=\"entry-text\"]` rendering of every entry with an\narbitrary `TemplateResult` (e.g. rendered markdown, or markdown plus a trailing list of\n`<lyra-tool-call-chip>`s) — the returned content fully replaces `[part=\"entry-text\"]` rather\nthan augmenting it, the same way `formatTimestamp` fully replaces the default timestamp\nformatting. Applies identically whether or not the feed is currently virtualized, since both\npaths render every entry through the same internal template."
269
+ },
262
270
  {
263
271
  "kind": "field",
264
272
  "name": "scrollRafId",
@@ -81636,6 +81644,11 @@
81636
81644
  "name": "--lyra-graph-selected-color",
81637
81645
  "default": "var(--lyra-color-success)"
81638
81646
  },
81647
+ {
81648
+ "description": "Opacity applied to a node/link when `dimmedNodeIds`/`dimmedLinkIds` includes its id (both SVG and canvas renderers). `1` (the default) is a no-op -- an unset host sees no visual change.",
81649
+ "name": "--lyra-graph-dimmed-opacity",
81650
+ "default": "1"
81651
+ },
81639
81652
  {
81640
81653
  "description": "Hull fill/stroke color.",
81641
81654
  "name": "--lyra-graph-hull-fill",
@@ -81957,6 +81970,24 @@
81957
81970
  "module": "src/internal/lyra-element.ts"
81958
81971
  }
81959
81972
  },
81973
+ {
81974
+ "kind": "field",
81975
+ "name": "dimmedLinkIds",
81976
+ "type": {
81977
+ "text": "string[]"
81978
+ },
81979
+ "default": "[]",
81980
+ "description": "Same contract as `dimmedNodeIds`, for links. A link's dimming key is the same `linkKey()`\nvalue (`GraphLink.id`, else `` `${source}->${target}` ``) `selectedLinkIds` already uses."
81981
+ },
81982
+ {
81983
+ "kind": "field",
81984
+ "name": "dimmedNodeIds",
81985
+ "type": {
81986
+ "text": "string[]"
81987
+ },
81988
+ "default": "[]",
81989
+ "description": "Node ids to render dimmed (`data-dimmed` on the matching `[part=\"node\"]`, themeable via\n`--lyra-graph-dimmed-opacity`). Controlled, mirroring `selectedNodeIds`/`selectedLinkIds`: the\ncomponent never assigns this itself, only renders it -- a host typically computes it from a\n`lyra-node-enter`/`lyra-link-enter` hover (the complement of the hovered id's neighbor set,\ncomputed from the host's own `links` array) and assigns the result back. Empty (the default)\nrenders every node at full opacity, unchanged from today."
81990
+ },
81960
81991
  {
81961
81992
  "kind": "field",
81962
81993
  "name": "edgeLabelMinZoom",
@@ -83117,6 +83148,30 @@
83117
83148
  }
83118
83149
  ]
83119
83150
  },
83151
+ {
83152
+ "kind": "method",
83153
+ "name": "isDimmed",
83154
+ "privacy": "private",
83155
+ "return": {
83156
+ "type": {
83157
+ "text": "boolean"
83158
+ }
83159
+ },
83160
+ "parameters": [
83161
+ {
83162
+ "name": "kind",
83163
+ "type": {
83164
+ "text": "'node' | 'link'"
83165
+ }
83166
+ },
83167
+ {
83168
+ "name": "id",
83169
+ "type": {
83170
+ "text": "string"
83171
+ }
83172
+ }
83173
+ ]
83174
+ },
83120
83175
  {
83121
83176
  "kind": "method",
83122
83177
  "name": "isSelected",
@@ -90800,7 +90855,7 @@
90800
90855
  "declarations": [
90801
90856
  {
90802
90857
  "kind": "class",
90803
- "description": "`<lyra-input>` — a single-line plain-text input primitive, the `lyra-*` equivalent of a plain\n`wa-input`, form-associated via the `FormAssociated` mixin (same shape as `<lyra-textarea>`).\n\nShips the same opt-in `label`/`hint`/`errorText` form-control chrome as `<lyra-textarea>`/\n`<lyra-select>` (props + matching named slots + `form-control`/`form-control-label`/`hint`/`error`\nparts) — left unset, the chrome stays hidden. `size` uses the same `xs`–`xl` scale as\n`<lyra-select>`/`<lyra-combobox>`. `type=\"password\"` always renders a\n`password-toggle` eye-icon button that flips the internal native input between\n`type=\"password\"`/`type=\"text\"` and tracks `passwordVisible`. `type=\"email\"`/`type=\"number\"`\n(with `min`/`max`/`step`) delegate constraint validation to the internal native `<input>`'s own\nbrowser-computed `validity`, bridged into this element's `ElementInternals` by `updateValidity()`.\n\nA host `aria-label` is forwarded to the internal textbox via the typed `accessibleLabel` property;\nexternal `aria-labelledby`/`aria-describedby` idrefs are not copied across the shadow boundary.",
90858
+ "description": "`<lyra-input>` — a single-line plain-text input primitive, the `lyra-*` equivalent of a plain\n`wa-input`, form-associated via the `FormAssociated` mixin (same shape as `<lyra-textarea>`).\n\nShips the same opt-in `label`/`hint`/`errorText` form-control chrome as `<lyra-textarea>`/\n`<lyra-select>` (props + matching named slots + `form-control`/`form-control-label`/`hint`/`error`\nparts) — left unset, the chrome stays hidden. `size` uses the same `xs`–`xl` scale as\n`<lyra-select>`/`<lyra-combobox>`. `type=\"password\"` always renders a\n`password-toggle` eye-icon button that flips the internal native input between\n`type=\"password\"`/`type=\"text\"` and tracks `passwordVisible`. `type=\"email\"`/`type=\"number\"`\n(with `min`/`max`/`step`) delegate constraint validation to the internal native `<input>`'s own\nbrowser-computed `validity`, bridged into this element's `ElementInternals` by `updateValidity()`.\n`type=\"search\"`/`type=\"time\"` forward straight through to the native input with no additional\nchrome or validation, the same as `type=\"text\"`.\n\nA host `aria-label` is forwarded to the internal textbox via the typed `accessibleLabel` property;\nexternal `aria-labelledby`/`aria-describedby` idrefs are not copied across the shadow boundary.",
90804
90859
  "name": "LyraInput",
90805
90860
  "cssParts": [
90806
90861
  {
@@ -98721,43 +98776,1176 @@
98721
98776
  },
98722
98777
  {
98723
98778
  "kind": "method",
98724
- "name": "tryConstructMap",
98779
+ "name": "tryConstructMap",
98780
+ "privacy": "private",
98781
+ "return": {
98782
+ "type": {
98783
+ "text": "void"
98784
+ }
98785
+ },
98786
+ "description": "Constructs the underlying `maplibregl.Map` — called once both the\nlazy-loaded `maplibre-gl` module has resolved and this element has been\nobserved intersecting the viewport, whichever settles last. Idempotent\n(a no-op once `_map` already exists), so it's safe to call from both the\n`connectedCallback()` load path and the visibility path in `updated()`\nbelow without risking a double construction."
98787
+ }
98788
+ ],
98789
+ "events": [
98790
+ {
98791
+ "description": "`detail: { lngLat, feature? }`.",
98792
+ "name": "lyra-map-click"
98793
+ },
98794
+ {
98795
+ "description": "Fired once the underlying maplibregl.Map loads.",
98796
+ "name": "lyra-map-load"
98797
+ }
98798
+ ],
98799
+ "attributes": [
98800
+ {
98801
+ "name": "center",
98802
+ "type": {
98803
+ "text": "[number, number]"
98804
+ },
98805
+ "default": "[0, 0]",
98806
+ "fieldName": "center"
98807
+ },
98808
+ {
98809
+ "name": "label",
98810
+ "type": {
98811
+ "text": "string"
98812
+ },
98813
+ "default": "''",
98814
+ "description": "Accessible name fallback for the map group. A host `aria-label` takes\nprecedence when both are set; when neither is present, the group uses\nthe localized `map` message.",
98815
+ "fieldName": "label"
98816
+ },
98817
+ {
98818
+ "name": "locale",
98819
+ "type": {
98820
+ "text": "string"
98821
+ },
98822
+ "default": "''",
98823
+ "description": "Optional locale override. Otherwise the nearest `locale`/`lang` ancestor is used.",
98824
+ "fieldName": "locale",
98825
+ "inheritedFrom": {
98826
+ "name": "LyraElement",
98827
+ "module": "src/internal/lyra-element.ts"
98828
+ }
98829
+ },
98830
+ {
98831
+ "name": "zoom",
98832
+ "type": {
98833
+ "text": "number"
98834
+ },
98835
+ "default": "2",
98836
+ "fieldName": "zoom"
98837
+ }
98838
+ ],
98839
+ "superclass": {
98840
+ "name": "LyraElement",
98841
+ "module": "/src/internal/lyra-element.js"
98842
+ },
98843
+ "tagName": "lyra-map",
98844
+ "customElement": true
98845
+ }
98846
+ ],
98847
+ "exports": [
98848
+ {
98849
+ "kind": "js",
98850
+ "name": "LyraMap",
98851
+ "declaration": {
98852
+ "name": "LyraMap",
98853
+ "module": "src/components/map/map.class.ts"
98854
+ }
98855
+ }
98856
+ ]
98857
+ },
98858
+ {
98859
+ "kind": "javascript-module",
98860
+ "path": "src/components/map/map.ts",
98861
+ "declarations": [],
98862
+ "exports": [
98863
+ {
98864
+ "kind": "custom-element-definition",
98865
+ "name": "lyra-map",
98866
+ "declaration": {
98867
+ "name": "LyraMap",
98868
+ "module": "src/components/map/map.class.ts"
98869
+ }
98870
+ },
98871
+ {
98872
+ "kind": "js",
98873
+ "name": "*",
98874
+ "declaration": {
98875
+ "name": "*",
98876
+ "module": "src/components/map/map.class.js"
98877
+ }
98878
+ }
98879
+ ]
98880
+ },
98881
+ {
98882
+ "kind": "javascript-module",
98883
+ "path": "src/components/markdown/katex-loader.ts",
98884
+ "declarations": [
98885
+ {
98886
+ "kind": "function",
98887
+ "name": "getKatex",
98888
+ "return": {
98889
+ "type": {
98890
+ "text": "Promise<KatexApi | null>"
98891
+ }
98892
+ },
98893
+ "parameters": [
98894
+ {
98895
+ "name": "importKatex",
98896
+ "optional": true,
98897
+ "type": {
98898
+ "text": "() => Promise<{ default: KatexApi } | KatexApi>"
98899
+ }
98900
+ }
98901
+ ],
98902
+ "description": "Lazily loads `katex` once per page, caching the resolved module (or `null`) across every\n`<lyra-markdown>` instance -- mirrors `markdown-loader.ts`'s `loadMarkdownDeps()`/\n`getMarkdownDepsIfLoaded()` cached-promise shape for a single optional peer."
98903
+ },
98904
+ {
98905
+ "kind": "function",
98906
+ "name": "loadKatex",
98907
+ "return": {
98908
+ "type": {
98909
+ "text": "Promise<KatexApi | null>"
98910
+ }
98911
+ },
98912
+ "parameters": [
98913
+ {
98914
+ "name": "importKatex",
98915
+ "default": "() =>\n import('katex') as Promise<{ default: KatexApi }>",
98916
+ "type": {
98917
+ "text": "() => Promise<{ default: KatexApi } | KatexApi>"
98918
+ }
98919
+ }
98920
+ ],
98921
+ "description": "Loads the optional peer dependency `katex`, used by `<lyra-markdown>`'s `math` property to\nrender `$...$`/`$$...$$` TeX as MathML. Resolves `null` (with a one-time `console.warn`) if the\npeer isn't installed -- rendering falls back to the literal, unparsed TeX source in that case,\na fully supported default rather than a degraded mode. Mirrors `dompurify-loader.ts`'s single-\noptional-peer shape."
98922
+ }
98923
+ ],
98924
+ "exports": [
98925
+ {
98926
+ "kind": "js",
98927
+ "name": "getKatex",
98928
+ "declaration": {
98929
+ "name": "getKatex",
98930
+ "module": "src/components/markdown/katex-loader.ts"
98931
+ }
98932
+ },
98933
+ {
98934
+ "kind": "js",
98935
+ "name": "loadKatex",
98936
+ "declaration": {
98937
+ "name": "loadKatex",
98938
+ "module": "src/components/markdown/katex-loader.ts"
98939
+ }
98940
+ }
98941
+ ]
98942
+ },
98943
+ {
98944
+ "kind": "javascript-module",
98945
+ "path": "src/components/markdown/markdown-core.class.ts",
98946
+ "declarations": [
98947
+ {
98948
+ "kind": "class",
98949
+ "description": "`<lyra-markdown-core>` — a build-lean variant of `<lyra-markdown>` for a consumer whose\n`languages` map already covers every language it will ever render. Every other capability (GFM\ntables, fenced code blocks, links, blockquotes, heading anchors, text-quote highlights, math) is\nidentical to `<lyra-markdown>` -- only fenced-code-block highlighting differs: this component's\nown module never textually contains a call to (or import of) `loadShikiHighlighter` (the\n~200-language default dynamic-import table `<lyra-markdown>` calls unconditionally unless its\n*runtime* `languagesOnly` flag is `true` -- a flag on that same module a bundler can't prove\nalways-true, so the unconditional call stays in the build output regardless). A consumer\nimporting this entry point instead of `markdown.js` gets a genuinely shiki-full-table-free\nbuild. A fenced block whose language isn't a key in `languages` always renders the plain-text\nfallback -- there is no default/full-table highlighter here to fall back to, mirroring\n`<lyra-code-block-core>`'s identical contract for the sibling component.\n\nBuilt on the optional peer dependencies `marked` (parsing) and `dompurify` (sanitizing), both\nlazy-loaded via `markdown-loader.ts` on first connect.\n\nRendering never ships unsanitized or broken markup silently:\n- If `marked` fails to load, or throws while parsing malformed input, the\n component falls back to plain text (`white-space: pre-wrap`, no HTML\n parsing at all) and fires `lyra-render-error`.\n- If `sanitize` is `true` (the default) and `dompurify` fails to load, the\n component *also* falls back to plain text + `lyra-render-error` — it\n never renders marked's raw HTML output when sanitization was requested\n (or defaulted to) but is unavailable, even though `marked` itself loaded\n fine.\n- If `sanitize` is explicitly `false`, marked's raw output renders as-is\n regardless of whether `dompurify` is installed — the consumer opted out\n of sanitization, so `dompurify`'s absence is irrelevant to that path.\n\nThat same plain-text fallback rendering (`data-fallback` on the `content`\npart) is also, unconditionally and by default, a brief *transient* state on\nevery connect, not just a failure path: `connectedCallback()`'s dynamic\n`import()` of `marked`/`dompurify` (see `markdown-loader.ts`) is\nasynchronous, so the very first paint of any `<lyra-markdown>` on a page\nshows plain text for at least one microtask — even when both peers are\nalready installed and load without error — until that import resolves and\na second render replaces it with the real Markdown output. Set\n`eager-load` to skip that window once the shared dependency cache is\nalready warm; see that property's doc for exactly what \"warm\" requires.\n\n`heading`/`code`/`blockquote`/`table`/`link`/`image` tokens are rendered\nthrough a `marked` renderer override that injects `part=\"...\"` attributes\ndirectly into the produced HTML — a single pass, not a second DOM walk\nafter insertion.\n\nFenced code blocks are syntax-highlighted via the same fine-grained `shiki/core` recipe\n`<lyra-code-block-core>` uses (`highlightCode`, default `true` — gated by whether a fenced\nblock's language is a key in `languages`, since there is no default highlighter here to gate on\n\"is shiki installed at all\"). The very first render of any content is always plain (identical to\n`<lyra-markdown>`'s own output); highlighting arrives as an asynchronous upgrade one render\nlater, once the fine-grained highlighter resolves. No highlighting is attempted while\n`streaming` is `true` — it applies once a stream settles, so there is no added per-chunk cost\nwhile content is still arriving.\n\nWhen `heading-anchors` is set, every rendered heading's slug (computed via the shared\nGitHub-slugger-style `Slugger`) is stamped as its `id`; `getHeadingTree()` computes that same\noutline on every parse regardless of `heading-anchors`, so a host can build a table of contents\neven while ids aren't in the DOM yet. `scrollToAnchor()` (from the adopted `DocumentAnchorTarget`\nmixin) resolves `fragment` anchors against that outline and `text-quote` anchors via\n`internal/text-quote.ts`'s shared scope/resolve helpers; `highlights` re-resolve by quote after\nevery render (never by node identity), so a highlight painted before a `streaming` update\nfinishes still finds its quote once the matching text arrives. Highlight painting uses\n`internal/text-highlights.ts`'s `acquireHighlightHandle()` -- the CSS Custom Highlight API where\nthe browser supports it (no DOM mutation at all), a `<mark>`-wrap fallback otherwise.\n\n`math` renders `$...$`/`$$...$$` TeX as MathML via the optional `katex` peer's\n`renderToString(tex, { output: 'mathml' })` -- MathML Core renders natively and accessibly in\nevergreen browsers with no extra stylesheet or webfont needing to cross the shadow boundary. A\nmissing `katex` peer renders the literal, unparsed TeX source (delimiters included) and fires one\n`lyra-render-error`.",
98950
+ "name": "LyraMarkdownCore",
98951
+ "cssParts": [
98952
+ {
98953
+ "description": "Every rendered `<blockquote>`.",
98954
+ "name": "blockquote"
98955
+ },
98956
+ {
98957
+ "description": "Every rendered fenced/indented `<pre>`.",
98958
+ "name": "code-block"
98959
+ },
98960
+ {
98961
+ "description": "The wrapper around the rendered (or plain-text fallback) output.",
98962
+ "name": "content"
98963
+ },
98964
+ {
98965
+ "description": "Every rendered `<h1>`–`<h6>` (shifted by `heading-offset`).",
98966
+ "name": "heading"
98967
+ },
98968
+ {
98969
+ "description": "Every rendered `<img>`.",
98970
+ "name": "img"
98971
+ },
98972
+ {
98973
+ "description": "Every rendered inline `<code>` span (backtick spans, not fenced blocks).",
98974
+ "name": "inline-code"
98975
+ },
98976
+ {
98977
+ "description": "Every rendered `<a>`.",
98978
+ "name": "link"
98979
+ },
98980
+ {
98981
+ "description": "Every rendered `<ul>`/`<ol>`.",
98982
+ "name": "list"
98983
+ },
98984
+ {
98985
+ "description": "A rendered inline or block math span (`data-display=\"inline\"|\"block\"`).",
98986
+ "name": "math"
98987
+ },
98988
+ {
98989
+ "description": "Every rendered `<p>`.",
98990
+ "name": "paragraph"
98991
+ },
98992
+ {
98993
+ "description": "Every rendered `<table>`.",
98994
+ "name": "table"
98995
+ }
98996
+ ],
98997
+ "members": [
98998
+ {
98999
+ "kind": "field",
99000
+ "name": "anchorKinds",
99001
+ "type": {
99002
+ "text": "readonly LyraAnchorKind[]"
99003
+ },
99004
+ "readonly": true,
99005
+ "default": "['fragment', 'text-quote']",
99006
+ "description": "Anchor kinds this component resolves via `scrollToAnchor()`. Readonly."
99007
+ },
99008
+ {
99009
+ "kind": "field",
99010
+ "name": "content",
99011
+ "type": {
99012
+ "text": "string"
99013
+ },
99014
+ "default": "''",
99015
+ "description": "The Markdown source to render.",
99016
+ "attribute": "content"
99017
+ },
99018
+ {
99019
+ "kind": "field",
99020
+ "name": "deferredLoad",
99021
+ "type": {
99022
+ "text": "() => void | undefined"
99023
+ },
99024
+ "privacy": "private",
99025
+ "inheritedFrom": {
99026
+ "name": "LyraElement",
99027
+ "module": "src/internal/lyra-element.ts"
99028
+ }
99029
+ },
99030
+ {
99031
+ "kind": "field",
99032
+ "name": "deps",
99033
+ "type": {
99034
+ "text": "MarkdownDeps | undefined"
99035
+ },
99036
+ "privacy": "private"
99037
+ },
99038
+ {
99039
+ "kind": "field",
99040
+ "name": "eagerLoad",
99041
+ "type": {
99042
+ "text": "boolean"
99043
+ },
99044
+ "default": "false",
99045
+ "description": "When `true`, `connectedCallback()` skips awaiting `loadMarkdownDeps()`'s\ndynamic `import()` if the shared `marked`/`dompurify` module cache (see\n`markdown-loader.ts`'s `getMarkdownDepsIfLoaded()`) has *already*\nresolved — e.g. because an earlier `<lyra-markdown>` instance on the\npage already finished loading, or the consumer primed the cache\ndirectly by calling `loadMarkdownDeps()` themselves at startup — and\nrenders synchronously instead. When the cache isn't warm yet (most\nnotably: the very first `<lyra-markdown>` ever connected on a page,\nsince nothing has called `loadMarkdownDeps()` before it), this still\nfalls back to the normal async path — a dynamic `import()` can't be\nmade synchronous, so this is a fast path for the common \"already warm\"\ncase, not a hard guarantee. `false` (the default) is byte-identical to\ntoday: always the async `import()`, fallback-text window included.",
99046
+ "attribute": "eager-load"
99047
+ },
99048
+ {
99049
+ "kind": "field",
99050
+ "name": "effectiveDirection",
99051
+ "type": {
99052
+ "text": "'ltr' | 'rtl'"
99053
+ },
99054
+ "privacy": "protected",
99055
+ "description": "The effective text direction, including inherited CSS direction.",
99056
+ "readonly": true,
99057
+ "inheritedFrom": {
99058
+ "name": "LyraElement",
99059
+ "module": "src/internal/lyra-element.ts"
99060
+ }
99061
+ },
99062
+ {
99063
+ "kind": "field",
99064
+ "name": "effectiveLocale",
99065
+ "type": {
99066
+ "text": "string"
99067
+ },
99068
+ "privacy": "protected",
99069
+ "description": "The effective locale used by this component.",
99070
+ "readonly": true,
99071
+ "inheritedFrom": {
99072
+ "name": "LyraElement",
99073
+ "module": "src/internal/lyra-element.ts"
99074
+ }
99075
+ },
99076
+ {
99077
+ "kind": "field",
99078
+ "name": "escapeHtml",
99079
+ "type": {
99080
+ "text": "boolean"
99081
+ },
99082
+ "default": "false",
99083
+ "description": "When `true`, overrides marked's `html` renderer hook to emit the HTML-escaped source text\ninstead of passing raw/sanitized markup through -- for a consumer rendering arbitrary\nalready-written content (e.g. a historical chat/agent transcript full of code/XML/HTML\nsnippets) where a stray angle bracket should render as visible text, not a real DOM element.\nStill lets GFM tables/lists/etc. render normally -- only raw embedded HTML is affected.\n`false` (the default) reproduces today's exact `marked`-default (sanitized-when-`sanitize`)\npassthrough behavior.",
99084
+ "attribute": "escape-html"
99085
+ },
99086
+ {
99087
+ "kind": "field",
99088
+ "name": "failedHighlightKeys",
99089
+ "privacy": "private",
99090
+ "default": "new Set<string>()",
99091
+ "description": "Keys from `PendingHighlight` that failed to highlight -- peer missing, language unrecognized,\nor tokenization threw. Once a key lands here, `code()` stops re-discovering it as pending on\nevery future render. Without this, a permanently-unhighlightable block (e.g. an unrecognized\nlanguage) would never get cached, so every `renderMarkdown()` pass -- including the one\n`highlightPending()` itself triggers on completion -- would rediscover it as pending and retry\nit again, forever. Mirrors `code-loader.ts`'s own `unsupportedLanguages` Set, which exists for\nthe identical reason one level down (a single unrecognized `language` value on\n`<lyra-code-block>`)."
99092
+ },
99093
+ {
99094
+ "kind": "field",
99095
+ "name": "gfm",
99096
+ "type": {
99097
+ "text": "boolean"
99098
+ },
99099
+ "default": "true",
99100
+ "description": "Enable GitHub-flavored Markdown (tables, strikethrough, autolinks, task lists).",
99101
+ "attribute": "gfm"
99102
+ },
99103
+ {
99104
+ "kind": "field",
99105
+ "name": "headingAnchors",
99106
+ "type": {
99107
+ "text": "boolean"
99108
+ },
99109
+ "default": "false",
99110
+ "description": "Stamps a computed slug as `id` on every rendered heading. `getHeadingTree()` computes the\nsame slugs regardless of this property -- it only controls whether the `id` attribute is\nemitted into the rendered DOM. `false` (the default) preserves today's exact output.\n\nWhen `sanitize` is also on (the default), a slug whose *value* collides with a real\n`document` property name (e.g. a heading literally titled \"Title\", \"Location\", or \"Forms\"\nslugs to `title`/`location`/`forms`) has its `id` silently stripped by DOMPurify's DOM-\nclobbering protection (`SANITIZE_DOM`) -- `getHeadingTree()` still reports that heading's slug\neither way, but `scrollToAnchor({ kind: 'fragment', id })` still resolves it correctly even\nwithout a DOM `id` present, via its own position-based fallback lookup.",
99111
+ "attribute": "heading-anchors"
99112
+ },
99113
+ {
99114
+ "kind": "field",
99115
+ "name": "headingOffset",
99116
+ "type": {
99117
+ "text": "number"
99118
+ },
99119
+ "default": "0",
99120
+ "description": "Added to every rendered heading's source `token.depth` before emitting\n`<h${depth}>` — e.g. `heading-offset=\"2\"` renders a source `#` as\n`<h3>` and a source `##` as `<h4>`. The result is clamped to `[1, 6]`\n(a source `######` with a positive offset stays at `<h6>` rather than\noverflowing past the HTML heading levels; the floor at `1` is\ndefensive, since this property is meant to be additive-only). `0`\n(the default) preserves today's exact `<h${token.depth}>` output.",
99121
+ "attribute": "heading-offset"
99122
+ },
99123
+ {
99124
+ "kind": "field",
99125
+ "name": "headingTree",
99126
+ "type": {
99127
+ "text": "MarkdownHeadingItem[]"
99128
+ },
99129
+ "privacy": "private",
99130
+ "default": "[]",
99131
+ "description": "Document-ordered heading outline computed on every parse (see `getHeadingTree()`), regardless\nof `headingAnchors`."
99132
+ },
99133
+ {
99134
+ "kind": "field",
99135
+ "name": "highlightCache",
99136
+ "privacy": "private",
99137
+ "default": "new Map<string, string>()",
99138
+ "description": "`(lang, code)` -> already-highlighted HTML, content-addressed (see `PendingHighlight`'s doc).\nPersists across renders of this instance; populated asynchronously by `highlightPending()`.\nNever consulted while `streaming` is `true` or `highlightCode` is `false` -- both gates live\nin the `code()` renderer inside `parseMarkdown()`. Bounded to HIGHLIGHT_CACHE_MAX\nentries, least-recently-used first out, via `getCachedHighlight()`/`setCachedHighlight()` --\nalways go through those instead of the map directly so hits refresh recency."
99139
+ },
99140
+ {
99141
+ "kind": "field",
99142
+ "name": "highlightCode",
99143
+ "type": {
99144
+ "text": "boolean"
99145
+ },
99146
+ "default": "true",
99147
+ "description": "Syntax-highlights fenced code blocks via the same optional `shiki` peer `<lyra-code-block>`\nuses. `true` (the default) upgrades every fenced block from plain `<pre><code>` once the peer\nis available -- a pure upgrade, not a behavior change gated on opt-in, since it's itself gated\ntransparently by whether `shiki` is installed at all (an app that never installs it sees\nbyte-identical output to today). Set `false` to keep plain output even when `shiki` is\ninstalled. No effect while `streaming` is `true` -- see that property's own doc.",
99148
+ "attribute": "highlight-code"
99149
+ },
99150
+ {
99151
+ "kind": "field",
99152
+ "name": "highlightHandle",
99153
+ "type": {
99154
+ "text": "HighlightHandle | undefined"
99155
+ },
99156
+ "privacy": "private",
99157
+ "description": "Lazily acquired the first time a highlight needs painting; released on disconnect."
99158
+ },
99159
+ {
99160
+ "kind": "field",
99161
+ "name": "highlightToken",
99162
+ "type": {
99163
+ "text": "number"
99164
+ },
99165
+ "privacy": "private",
99166
+ "default": "0",
99167
+ "description": "Bumped on every `highlightPending()` call, including ones that end up not actually loading\nanything -- guards against a newer `content`/`streaming` change superseding an older in-flight\nhighlight, exactly mirroring `<lyra-code-block>`'s own `highlightToken` field for the identical\nrace (an async grammar load resolving after a newer call already produced correct output)."
99168
+ },
99169
+ {
99170
+ "kind": "field",
99171
+ "name": "internalLinkPrefix",
99172
+ "type": {
99173
+ "text": "string"
99174
+ },
99175
+ "default": "''",
99176
+ "description": "When set, a rendered link whose `href` starts with this prefix is\ntreated as internal — its click is intercepted and reported via\n`lyra-link-click` instead of navigating. Empty (the default) means\nevery link is treated as external.",
99177
+ "attribute": "internal-link-prefix"
99178
+ },
99179
+ {
99180
+ "kind": "field",
99181
+ "name": "languages",
99182
+ "type": {
99183
+ "text": "Record<string, ShikiLanguageInput>"
99184
+ },
99185
+ "default": "{}",
99186
+ "description": "Grammar definitions this instance can highlight, e.g. `{ json: jsonGrammar }` (import from\n`shiki/langs/<name>.mjs`), forwarded verbatim to `loadShikiHighlighterCore()` -- same shape as\n`<lyra-code-block-core>`'s own `languages`. This component has no default/full-table\nhighlighter to fall back to -- a fenced block whose language isn't a key here always renders\nthe plain-text fallback. Empty (the default) never highlights anything."
99187
+ },
99188
+ {
99189
+ "kind": "field",
99190
+ "name": "linkTarget",
99191
+ "type": {
99192
+ "text": "string | null"
99193
+ },
99194
+ "default": "'_blank'",
99195
+ "description": "`target` applied to every rendered `<a>`, with `rel=\"noopener\nnoreferrer\"` always added alongside it whenever a `target` is emitted.\n`'_blank'` (the default) preserves today's exact output. Set to `null`\n(or the empty string, e.g. via the `link-target=\"\"` attribute) to omit\n`target`/`rel` entirely, so rendered links open in the same tab.",
99196
+ "attribute": "link-target"
99197
+ },
99198
+ {
99199
+ "kind": "field",
99200
+ "name": "loadSchedulePending",
99201
+ "type": {
99202
+ "text": "boolean"
99203
+ },
99204
+ "privacy": "private",
99205
+ "default": "false",
99206
+ "inheritedFrom": {
99207
+ "name": "LyraElement",
99208
+ "module": "src/internal/lyra-element.ts"
99209
+ }
99210
+ },
99211
+ {
99212
+ "kind": "field",
99213
+ "name": "locale",
99214
+ "type": {
99215
+ "text": "string"
99216
+ },
99217
+ "default": "''",
99218
+ "description": "Optional locale override. Otherwise the nearest `locale`/`lang` ancestor is used.",
99219
+ "attribute": "locale",
99220
+ "reflects": true,
99221
+ "inheritedFrom": {
99222
+ "name": "LyraElement",
99223
+ "module": "src/internal/lyra-element.ts"
99224
+ }
99225
+ },
99226
+ {
99227
+ "kind": "field",
99228
+ "name": "math",
99229
+ "type": {
99230
+ "text": "boolean"
99231
+ },
99232
+ "default": "false",
99233
+ "description": "Renders `$...$`/`$$...$$` TeX via the optional `katex` peer, as MathML. `false` (the\ndefault) renders `$...$` literally, unparsed -- today's exact output.",
99234
+ "attribute": "math"
99235
+ },
99236
+ {
99237
+ "kind": "field",
99238
+ "name": "mathFailureReported",
99239
+ "type": {
99240
+ "text": "boolean"
99241
+ },
99242
+ "privacy": "private",
99243
+ "default": "false",
99244
+ "description": "Guards `lyra-render-error` so a permanently-missing `katex` peer reports once per instance,\nnot on every subsequent re-render while `math` stays on. Reset whenever `math` toggles."
99245
+ },
99246
+ {
99247
+ "kind": "field",
99248
+ "name": "onContentClick",
99249
+ "privacy": "private"
99250
+ },
99251
+ {
99252
+ "kind": "field",
99253
+ "name": "pendingLoadController",
99254
+ "type": {
99255
+ "text": "AbortController | undefined"
99256
+ },
99257
+ "privacy": "private",
99258
+ "inheritedFrom": {
99259
+ "name": "LyraElement",
99260
+ "module": "src/internal/lyra-element.ts"
99261
+ }
99262
+ },
99263
+ {
99264
+ "kind": "field",
99265
+ "name": "renderedHtml",
99266
+ "type": {
99267
+ "text": "string | null"
99268
+ },
99269
+ "privacy": "private",
99270
+ "default": "null"
99271
+ },
99272
+ {
99273
+ "kind": "field",
99274
+ "name": "resolvedHighlightRanges",
99275
+ "type": {
99276
+ "text": "{ id: string; range: Range }[]"
99277
+ },
99278
+ "privacy": "private",
99279
+ "default": "[]",
99280
+ "description": "The most recently resolved `text-quote` highlight ranges, kept for `onContentClick()`'s\ncoordinate hit-test -- the CSS Custom Highlight API paints ranges without creating any DOM\nelement to attach a click listener to, so activation is resolved by comparing the click point\nagainst each range's own `getClientRects()` instead, uniformly across both paint paths."
99281
+ },
99282
+ {
99283
+ "kind": "field",
99284
+ "name": "sanitize",
99285
+ "type": {
99286
+ "text": "boolean"
99287
+ },
99288
+ "default": "true",
99289
+ "description": "Sanitize marked's HTML output with DOMPurify before rendering. See the\nclass doc for what happens when this is `true` (the default) but the\n`dompurify` peer isn't installed.",
99290
+ "attribute": "sanitize"
99291
+ },
99292
+ {
99293
+ "kind": "field",
99294
+ "name": "stopLocaleSubscription",
99295
+ "type": {
99296
+ "text": "() => void | undefined"
99297
+ },
99298
+ "privacy": "private",
99299
+ "inheritedFrom": {
99300
+ "name": "LyraElement",
99301
+ "module": "src/internal/lyra-element.ts"
99302
+ }
99303
+ },
99304
+ {
99305
+ "kind": "field",
99306
+ "name": "streaming",
99307
+ "type": {
99308
+ "text": "boolean"
99309
+ },
99310
+ "default": "false",
99311
+ "description": "Signals that `content` is still arriving incrementally. Content changes\ncontinue to render immediately; while this is `true`, the host remains\n`aria-busy=\"true\"` so assistive technology knows the rendered document\nis not final. Set it back to `false` with the final content update.\nReflects so a consumer can also target `lyra-markdown[streaming]`.",
99312
+ "attribute": "streaming",
99313
+ "reflects": true
99314
+ },
99315
+ {
99316
+ "kind": "field",
99317
+ "name": "strings",
99318
+ "type": {
99319
+ "text": "LyraLocaleStrings"
99320
+ },
99321
+ "default": "{}",
99322
+ "description": "Per-instance message overrides, useful for application-specific wording.",
99323
+ "inheritedFrom": {
99324
+ "name": "LyraElement",
99325
+ "module": "src/internal/lyra-element.ts"
99326
+ }
99327
+ },
99328
+ {
99329
+ "kind": "method",
99330
+ "name": "addEventListener",
99331
+ "return": {
99332
+ "type": {
99333
+ "text": "void"
99334
+ }
99335
+ },
99336
+ "parameters": [
99337
+ {
99338
+ "name": "type",
99339
+ "type": {
99340
+ "text": "K"
99341
+ }
99342
+ },
99343
+ {
99344
+ "name": "listener",
99345
+ "type": {
99346
+ "text": "(this: this, event: Events[K]) => unknown"
99347
+ }
99348
+ },
99349
+ {
99350
+ "name": "options",
99351
+ "optional": true,
99352
+ "type": {
99353
+ "text": "boolean | AddEventListenerOptions"
99354
+ }
99355
+ }
99356
+ ],
99357
+ "inheritedFrom": {
99358
+ "name": "LyraElement",
99359
+ "module": "src/internal/lyra-element.ts"
99360
+ }
99361
+ },
99362
+ {
99363
+ "kind": "method",
99364
+ "name": "applyAnchor",
99365
+ "privacy": "protected",
99366
+ "return": {
99367
+ "type": {
99368
+ "text": "Promise<boolean>"
99369
+ }
99370
+ },
99371
+ "parameters": [
99372
+ {
99373
+ "name": "anchor",
99374
+ "type": {
99375
+ "text": "LyraAnchor"
99376
+ }
99377
+ }
99378
+ ]
99379
+ },
99380
+ {
99381
+ "kind": "method",
99382
+ "name": "applyFallback",
99383
+ "privacy": "private",
99384
+ "return": {
99385
+ "type": {
99386
+ "text": "void"
99387
+ }
99388
+ },
99389
+ "parameters": [
99390
+ {
99391
+ "name": "error",
99392
+ "type": {
99393
+ "text": "unknown"
99394
+ }
99395
+ }
99396
+ ]
99397
+ },
99398
+ {
99399
+ "kind": "method",
99400
+ "name": "applyFragmentAnchor",
99401
+ "privacy": "private",
99402
+ "return": {
99403
+ "type": {
99404
+ "text": "boolean"
99405
+ }
99406
+ },
99407
+ "parameters": [
99408
+ {
99409
+ "name": "root",
99410
+ "type": {
99411
+ "text": "Element"
99412
+ }
99413
+ },
99414
+ {
99415
+ "name": "anchor",
99416
+ "type": {
99417
+ "text": "Extract<LyraAnchor, { kind: 'fragment' }>"
99418
+ }
99419
+ }
99420
+ ]
99421
+ },
99422
+ {
99423
+ "kind": "method",
99424
+ "name": "applyTextQuoteAnchor",
99425
+ "privacy": "private",
99426
+ "return": {
99427
+ "type": {
99428
+ "text": "boolean"
99429
+ }
99430
+ },
99431
+ "parameters": [
99432
+ {
99433
+ "name": "root",
99434
+ "type": {
99435
+ "text": "Element"
99436
+ }
99437
+ },
99438
+ {
99439
+ "name": "anchor",
99440
+ "type": {
99441
+ "text": "Extract<LyraAnchor, { kind: 'text-quote' }>"
99442
+ }
99443
+ }
99444
+ ]
99445
+ },
99446
+ {
99447
+ "kind": "method",
99448
+ "name": "beginAbortableLoad",
99449
+ "privacy": "protected",
99450
+ "return": {
99451
+ "type": {
99452
+ "text": "AbortSignal | undefined"
99453
+ }
99454
+ },
99455
+ "description": "Starts a component-owned cancellable load and aborts the previous one.",
99456
+ "inheritedFrom": {
99457
+ "name": "LyraElement",
99458
+ "module": "src/internal/lyra-element.ts"
99459
+ }
99460
+ },
99461
+ {
99462
+ "kind": "method",
99463
+ "name": "computeSelectionAnchor",
99464
+ "privacy": "protected",
99465
+ "return": {
99466
+ "type": {
99467
+ "text": "LyraAnchor | null"
99468
+ }
99469
+ },
99470
+ "parameters": [
99471
+ {
99472
+ "name": "range",
99473
+ "type": {
99474
+ "text": "Range"
99475
+ }
99476
+ }
99477
+ ],
99478
+ "description": "Overrides `DocumentAnchorTarget`'s default (whole render-root) selection scope, matching\n`heading`'s slug computation and `applyTextQuoteAnchor()` above: only `[part=\"content\"]` is a\nmeaningful text-quote scope, so a selection that somehow reaches outside it (there is no other\ntext in this component's shadow tree today, but the live region could grow one) never leaks\ninto a captured anchor."
99479
+ },
99480
+ {
99481
+ "kind": "method",
99482
+ "name": "contentRoot",
99483
+ "privacy": "private",
99484
+ "return": {
99485
+ "type": {
99486
+ "text": "Element | null"
99487
+ }
99488
+ }
99489
+ },
99490
+ {
99491
+ "kind": "method",
99492
+ "name": "emit",
99493
+ "privacy": "protected",
99494
+ "return": {
99495
+ "type": {
99496
+ "text": "CustomEvent<T>"
99497
+ }
99498
+ },
99499
+ "parameters": [
99500
+ {
99501
+ "name": "name",
99502
+ "type": {
99503
+ "text": "string"
99504
+ }
99505
+ },
99506
+ {
99507
+ "name": "detail",
99508
+ "optional": true,
99509
+ "type": {
99510
+ "text": "T"
99511
+ }
99512
+ },
99513
+ {
99514
+ "name": "options",
99515
+ "optional": true,
99516
+ "type": {
99517
+ "text": "LyraEmitOptions"
99518
+ }
99519
+ }
99520
+ ],
99521
+ "description": "Dispatch a composed, bubbling custom event; notifications are not veto points by default.",
99522
+ "inheritedFrom": {
99523
+ "name": "LyraElement",
99524
+ "module": "src/internal/lyra-element.ts"
99525
+ }
99526
+ },
99527
+ {
99528
+ "kind": "method",
99529
+ "name": "ensureHighlightHandle",
99530
+ "privacy": "private",
99531
+ "return": {
99532
+ "type": {
99533
+ "text": "HighlightHandle"
99534
+ }
99535
+ }
99536
+ },
99537
+ {
99538
+ "kind": "method",
99539
+ "name": "findHeadingByComputedId",
99540
+ "privacy": "private",
99541
+ "return": {
99542
+ "type": {
99543
+ "text": "Element | null"
99544
+ }
99545
+ },
99546
+ "parameters": [
99547
+ {
99548
+ "name": "root",
99549
+ "type": {
99550
+ "text": "Element"
99551
+ }
99552
+ },
99553
+ {
99554
+ "name": "id",
99555
+ "type": {
99556
+ "text": "string"
99557
+ }
99558
+ }
99559
+ ],
99560
+ "description": "`headingAnchors` may be off, so a target heading might carry no `id` attribute in the DOM --\nre-derives the same slug order `getHeadingTree()` was built in and matches by position\ninstead of by attribute."
99561
+ },
99562
+ {
99563
+ "kind": "method",
99564
+ "name": "getCachedHighlight",
99565
+ "privacy": "private",
99566
+ "return": {
99567
+ "type": {
99568
+ "text": "string | undefined"
99569
+ }
99570
+ },
99571
+ "parameters": [
99572
+ {
99573
+ "name": "key",
99574
+ "type": {
99575
+ "text": "string"
99576
+ }
99577
+ }
99578
+ ],
99579
+ "description": "LRU read: a hit is re-inserted so Map iteration order (insertion order) keeps the first key\nthe least recently used one -- the entry `setCachedHighlight()` evicts when full."
99580
+ },
99581
+ {
99582
+ "kind": "method",
99583
+ "name": "getHeadingTree",
99584
+ "return": {
99585
+ "type": {
99586
+ "text": "MarkdownHeadingItem[]"
99587
+ }
99588
+ },
99589
+ "description": "A document-ordered, flattened heading outline -- computed on every parse regardless of\n`headingAnchors` (see that property's own doc). A caller building a table of contents can rely\non this even while `heading-anchors` is off."
99590
+ },
99591
+ {
99592
+ "kind": "method",
99593
+ "name": "highlightPending",
99594
+ "privacy": "private",
99595
+ "return": {
99596
+ "type": {
99597
+ "text": "Promise<void>"
99598
+ }
99599
+ },
99600
+ "parameters": [
99601
+ {
99602
+ "name": "pendingKeys",
99603
+ "type": {
99604
+ "text": "PendingHighlight[]"
99605
+ }
99606
+ }
99607
+ ],
99608
+ "description": "Loads whatever shiki grammars `pendingKeys` need, tokenizes each pending block concurrently,\npopulates `highlightCache` with the results, then triggers one more `renderMarkdown()` pass so\nthe newly-cached entries actually reach the screen. A pending key whose language isn't a key in\n`languages`, or otherwise fails to tokenize, is recorded in `failedHighlightKeys` -- it stays\nuncached, so `code()` keeps emitting its plain fallback for it on every future render, and\n`code()` also stops re-discovering it as pending (see `failedHighlightKeys`'s own doc for why\nthat matters: without it, the `renderMarkdown()` call at the end of this method would\nrediscover the same permanently-uncacheable key as pending on every pass, forever). Does not\nblock or delay any other pending key -- each is tried independently via `Promise.all`."
99609
+ },
99610
+ {
99611
+ "kind": "method",
99612
+ "name": "hitTestHighlightAt",
99613
+ "privacy": "private",
99614
+ "return": {
99615
+ "type": {
99616
+ "text": "string | null"
99617
+ }
99618
+ },
99619
+ "parameters": [
99620
+ {
99621
+ "name": "x",
99622
+ "type": {
99623
+ "text": "number"
99624
+ }
99625
+ },
99626
+ {
99627
+ "name": "y",
99628
+ "type": {
99629
+ "text": "number"
99630
+ }
99631
+ }
99632
+ ],
99633
+ "description": "Hit-tests a click point against every currently-resolved highlight's `getClientRects()`,\ntopmost (last-resolved) first. The CSS Custom Highlight API paints ranges without creating any\nDOM element to attach a click listener to, so this is the only activation path that works\nidentically on both paint paths -- mirrors `<lyra-pdf-viewer>`'s own coordinate-based\n`onPageClick()` hit-test for the same reason (its own painted highlights sit under a text\nlayer that intercepts most pointer events)."
99634
+ },
99635
+ {
99636
+ "kind": "method",
99637
+ "name": "localize",
99638
+ "privacy": "protected",
99639
+ "return": {
99640
+ "type": {
99641
+ "text": "string"
99642
+ }
99643
+ },
99644
+ "parameters": [
99645
+ {
99646
+ "name": "key",
99647
+ "type": {
99648
+ "text": "string"
99649
+ }
99650
+ },
99651
+ {
99652
+ "name": "fallback",
99653
+ "optional": true,
99654
+ "type": {
99655
+ "text": "string"
99656
+ }
99657
+ },
99658
+ {
99659
+ "name": "values",
99660
+ "optional": true,
99661
+ "type": {
99662
+ "text": "Record<string, string | number>"
99663
+ }
99664
+ }
99665
+ ],
99666
+ "description": "Resolve a localized message using this component's overrides and locale.",
99667
+ "inheritedFrom": {
99668
+ "name": "LyraElement",
99669
+ "module": "src/internal/lyra-element.ts"
99670
+ }
99671
+ },
99672
+ {
99673
+ "kind": "method",
99674
+ "name": "maybeHighlightPending",
99675
+ "privacy": "private",
99676
+ "return": {
99677
+ "type": {
99678
+ "text": "void"
99679
+ }
99680
+ },
99681
+ "parameters": [
99682
+ {
99683
+ "name": "pendingKeys",
99684
+ "type": {
99685
+ "text": "PendingHighlight[]"
99686
+ }
99687
+ }
99688
+ ],
99689
+ "description": "Kicks off async highlighting for `pendingKeys` (see `highlightPending()`) unless there's\nnothing to do, `highlightCode` is off, or `streaming` is on -- called from both of\n`renderMarkdown()`'s exit points (the `sanitize=false` early return and the normal sanitized\npath), since highlighting is independent of that decision."
99690
+ },
99691
+ {
99692
+ "kind": "method",
99693
+ "name": "maybeLoadKatex",
99694
+ "privacy": "private",
99695
+ "return": {
99696
+ "type": {
99697
+ "text": "void"
99698
+ }
99699
+ },
99700
+ "description": "Kicks off the shared `katex` load the first time `math` needs it and no attempt is already\nin flight (module-scoped, so every `<lyra-markdown>` instance on the page shares one load --\nmirrors `markdown-loader.ts`'s own warm-cache shape). Skipped entirely under\n`__setKatexForTesting()` -- that seam controls math-rendering behavior directly and must never\nrace a real, unmocked `import('katex')` settling underneath it."
99701
+ },
99702
+ {
99703
+ "kind": "method",
99704
+ "name": "parseMarkdown",
99705
+ "privacy": "private",
99706
+ "return": {
99707
+ "type": {
99708
+ "text": "{ html: string; hadMathFallback: boolean }"
99709
+ }
99710
+ },
99711
+ "parameters": [
99712
+ {
99713
+ "name": "marked",
99714
+ "type": {
99715
+ "text": "OptionalPeerApi"
99716
+ }
99717
+ },
99718
+ {
99719
+ "name": "pendingKeys",
99720
+ "type": {
99721
+ "text": "PendingHighlight[]"
99722
+ }
99723
+ },
99724
+ {
99725
+ "name": "headingTreeOut",
99726
+ "type": {
99727
+ "text": "MarkdownHeadingItem[]"
99728
+ }
99729
+ }
99730
+ ]
99731
+ },
99732
+ {
99733
+ "kind": "method",
99734
+ "name": "removeEventListener",
99735
+ "return": {
99736
+ "type": {
99737
+ "text": "void"
99738
+ }
99739
+ },
99740
+ "parameters": [
99741
+ {
99742
+ "name": "type",
99743
+ "type": {
99744
+ "text": "K"
99745
+ }
99746
+ },
99747
+ {
99748
+ "name": "listener",
99749
+ "type": {
99750
+ "text": "(this: this, event: Events[K]) => unknown"
99751
+ }
99752
+ },
99753
+ {
99754
+ "name": "options",
99755
+ "optional": true,
99756
+ "type": {
99757
+ "text": "boolean | EventListenerOptions"
99758
+ }
99759
+ }
99760
+ ],
99761
+ "inheritedFrom": {
99762
+ "name": "LyraElement",
99763
+ "module": "src/internal/lyra-element.ts"
99764
+ }
99765
+ },
99766
+ {
99767
+ "kind": "method",
99768
+ "name": "renderMarkdown",
99769
+ "privacy": "private",
99770
+ "return": {
99771
+ "type": {
99772
+ "text": "void"
99773
+ }
99774
+ }
99775
+ },
99776
+ {
99777
+ "kind": "method",
99778
+ "name": "repaintHighlights",
99779
+ "privacy": "private",
99780
+ "return": {
99781
+ "type": {
99782
+ "text": "void"
99783
+ }
99784
+ },
99785
+ "description": "Re-resolves every `text-quote` highlight against the current rendered content and repaints\nvia `acquireHighlightHandle()` -- resolution is always by quote text, never by node identity,\nso a highlight set before its quote exists in `content` yet (e.g. mid-`streaming`) simply\npaints nothing until a later render's text actually contains it. `fragment` highlights aren't\npainted (there is no literal span of text to wrap/underline for a whole section)."
99786
+ },
99787
+ {
99788
+ "kind": "method",
99789
+ "name": "reportMathFailure",
99790
+ "privacy": "private",
99791
+ "return": {
99792
+ "type": {
99793
+ "text": "void"
99794
+ }
99795
+ },
99796
+ "description": "Fires `lyra-render-error` once per instance for a permanently-missing `katex` peer. Called\nonly once `renderMarkdown()` has confirmed the peer is actually missing (`katexOverride` or\nthe resolved module is `null`) -- a math token rendering its literal fallback while the load\nis merely still in flight (the same one-microtask transient window every other optional peer\nin this component has) never reports an error on its own."
99797
+ },
99798
+ {
99799
+ "kind": "method",
99800
+ "name": "scheduleAfterUpdate",
99801
+ "privacy": "protected",
99802
+ "return": {
99803
+ "type": {
99804
+ "text": "void"
99805
+ }
99806
+ },
99807
+ "parameters": [
99808
+ {
99809
+ "name": "callback",
99810
+ "type": {
99811
+ "text": "() => void"
99812
+ }
99813
+ }
99814
+ ],
99815
+ "description": "Runs one coalesced load callback after the current update completes.\nLit still runs the update cycle while detached, so a load scheduled then is\nheld and replayed on reconnect rather than dropped.",
99816
+ "inheritedFrom": {
99817
+ "name": "LyraElement",
99818
+ "module": "src/internal/lyra-element.ts"
99819
+ }
99820
+ },
99821
+ {
99822
+ "kind": "method",
99823
+ "name": "setCachedHighlight",
98725
99824
  "privacy": "private",
98726
99825
  "return": {
98727
99826
  "type": {
98728
99827
  "text": "void"
98729
99828
  }
98730
99829
  },
98731
- "description": "Constructs the underlying `maplibregl.Map` — called once both the\nlazy-loaded `maplibre-gl` module has resolved and this element has been\nobserved intersecting the viewport, whichever settles last. Idempotent\n(a no-op once `_map` already exists), so it's safe to call from both the\n`connectedCallback()` load path and the visibility path in `updated()`\nbelow without risking a double construction."
99830
+ "parameters": [
99831
+ {
99832
+ "name": "key",
99833
+ "type": {
99834
+ "text": "string"
99835
+ }
99836
+ },
99837
+ {
99838
+ "name": "html",
99839
+ "type": {
99840
+ "text": "string"
99841
+ }
99842
+ }
99843
+ ]
98732
99844
  }
98733
99845
  ],
98734
99846
  "events": [
98735
99847
  {
98736
- "description": "`detail: { lngLat, feature? }`.",
98737
- "name": "lyra-map-click"
99848
+ "description": "Fired after an `anchor` property assignment or a `scrollToAnchor()` call is applied. `detail: { found }`.",
99849
+ "name": "lyra-anchor-result"
98738
99850
  },
98739
99851
  {
98740
- "description": "Fired once the underlying maplibregl.Map loads.",
98741
- "name": "lyra-map-load"
99852
+ "description": "A painted `text-quote` highlight was clicked. `detail: { id }`.",
99853
+ "name": "lyra-highlight-activate"
99854
+ },
99855
+ {
99856
+ "description": "Fired (and the click prevented) when a rendered link's `href` starts with `internal-link-prefix`. `detail: { href: string, internal: true }`. Ordinary external links navigate normally (in `link-target`) and never fire this event.",
99857
+ "name": "lyra-link-click"
99858
+ },
99859
+ {
99860
+ "description": "Fired whenever rendering falls back to plain text, or `math` is set but the `katex` peer isn't installed. `detail: { error: unknown }`.",
99861
+ "name": "lyra-render-error"
99862
+ },
99863
+ {
99864
+ "description": "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.",
99865
+ "name": "lyra-text-select"
98742
99866
  }
98743
99867
  ],
98744
99868
  "attributes": [
98745
99869
  {
98746
- "name": "center",
99870
+ "name": "content",
98747
99871
  "type": {
98748
- "text": "[number, number]"
99872
+ "text": "string"
98749
99873
  },
98750
- "default": "[0, 0]",
98751
- "fieldName": "center"
99874
+ "default": "''",
99875
+ "description": "The Markdown source to render.",
99876
+ "fieldName": "content"
98752
99877
  },
98753
99878
  {
98754
- "name": "label",
99879
+ "name": "eager-load",
99880
+ "type": {
99881
+ "text": "boolean"
99882
+ },
99883
+ "default": "false",
99884
+ "description": "When `true`, `connectedCallback()` skips awaiting `loadMarkdownDeps()`'s\ndynamic `import()` if the shared `marked`/`dompurify` module cache (see\n`markdown-loader.ts`'s `getMarkdownDepsIfLoaded()`) has *already*\nresolved — e.g. because an earlier `<lyra-markdown>` instance on the\npage already finished loading, or the consumer primed the cache\ndirectly by calling `loadMarkdownDeps()` themselves at startup — and\nrenders synchronously instead. When the cache isn't warm yet (most\nnotably: the very first `<lyra-markdown>` ever connected on a page,\nsince nothing has called `loadMarkdownDeps()` before it), this still\nfalls back to the normal async path — a dynamic `import()` can't be\nmade synchronous, so this is a fast path for the common \"already warm\"\ncase, not a hard guarantee. `false` (the default) is byte-identical to\ntoday: always the async `import()`, fallback-text window included.",
99885
+ "fieldName": "eagerLoad"
99886
+ },
99887
+ {
99888
+ "name": "escape-html",
99889
+ "type": {
99890
+ "text": "boolean"
99891
+ },
99892
+ "default": "false",
99893
+ "description": "When `true`, overrides marked's `html` renderer hook to emit the HTML-escaped source text\ninstead of passing raw/sanitized markup through -- for a consumer rendering arbitrary\nalready-written content (e.g. a historical chat/agent transcript full of code/XML/HTML\nsnippets) where a stray angle bracket should render as visible text, not a real DOM element.\nStill lets GFM tables/lists/etc. render normally -- only raw embedded HTML is affected.\n`false` (the default) reproduces today's exact `marked`-default (sanitized-when-`sanitize`)\npassthrough behavior.",
99894
+ "fieldName": "escapeHtml"
99895
+ },
99896
+ {
99897
+ "name": "gfm",
99898
+ "type": {
99899
+ "text": "boolean"
99900
+ },
99901
+ "default": "true",
99902
+ "description": "Enable GitHub-flavored Markdown (tables, strikethrough, autolinks, task lists).",
99903
+ "fieldName": "gfm"
99904
+ },
99905
+ {
99906
+ "name": "heading-anchors",
99907
+ "type": {
99908
+ "text": "boolean"
99909
+ },
99910
+ "default": "false",
99911
+ "description": "Stamps a computed slug as `id` on every rendered heading. `getHeadingTree()` computes the\nsame slugs regardless of this property -- it only controls whether the `id` attribute is\nemitted into the rendered DOM. `false` (the default) preserves today's exact output.\n\nWhen `sanitize` is also on (the default), a slug whose *value* collides with a real\n`document` property name (e.g. a heading literally titled \"Title\", \"Location\", or \"Forms\"\nslugs to `title`/`location`/`forms`) has its `id` silently stripped by DOMPurify's DOM-\nclobbering protection (`SANITIZE_DOM`) -- `getHeadingTree()` still reports that heading's slug\neither way, but `scrollToAnchor({ kind: 'fragment', id })` still resolves it correctly even\nwithout a DOM `id` present, via its own position-based fallback lookup.",
99912
+ "fieldName": "headingAnchors"
99913
+ },
99914
+ {
99915
+ "name": "heading-offset",
99916
+ "type": {
99917
+ "text": "number"
99918
+ },
99919
+ "default": "0",
99920
+ "description": "Added to every rendered heading's source `token.depth` before emitting\n`<h${depth}>` — e.g. `heading-offset=\"2\"` renders a source `#` as\n`<h3>` and a source `##` as `<h4>`. The result is clamped to `[1, 6]`\n(a source `######` with a positive offset stays at `<h6>` rather than\noverflowing past the HTML heading levels; the floor at `1` is\ndefensive, since this property is meant to be additive-only). `0`\n(the default) preserves today's exact `<h${token.depth}>` output.",
99921
+ "fieldName": "headingOffset"
99922
+ },
99923
+ {
99924
+ "name": "highlight-code",
99925
+ "type": {
99926
+ "text": "boolean"
99927
+ },
99928
+ "default": "true",
99929
+ "description": "Syntax-highlights fenced code blocks via the same optional `shiki` peer `<lyra-code-block>`\nuses. `true` (the default) upgrades every fenced block from plain `<pre><code>` once the peer\nis available -- a pure upgrade, not a behavior change gated on opt-in, since it's itself gated\ntransparently by whether `shiki` is installed at all (an app that never installs it sees\nbyte-identical output to today). Set `false` to keep plain output even when `shiki` is\ninstalled. No effect while `streaming` is `true` -- see that property's own doc.",
99930
+ "fieldName": "highlightCode"
99931
+ },
99932
+ {
99933
+ "name": "internal-link-prefix",
98755
99934
  "type": {
98756
99935
  "text": "string"
98757
99936
  },
98758
99937
  "default": "''",
98759
- "description": "Accessible name fallback for the map group. A host `aria-label` takes\nprecedence when both are set; when neither is present, the group uses\nthe localized `map` message.",
98760
- "fieldName": "label"
99938
+ "description": "When set, a rendered link whose `href` starts with this prefix is\ntreated as internal its click is intercepted and reported via\n`lyra-link-click` instead of navigating. Empty (the default) means\nevery link is treated as external.",
99939
+ "fieldName": "internalLinkPrefix"
99940
+ },
99941
+ {
99942
+ "name": "link-target",
99943
+ "type": {
99944
+ "text": "string | null"
99945
+ },
99946
+ "default": "'_blank'",
99947
+ "description": "`target` applied to every rendered `<a>`, with `rel=\"noopener\nnoreferrer\"` always added alongside it whenever a `target` is emitted.\n`'_blank'` (the default) preserves today's exact output. Set to `null`\n(or the empty string, e.g. via the `link-target=\"\"` attribute) to omit\n`target`/`rel` entirely, so rendered links open in the same tab.",
99948
+ "fieldName": "linkTarget"
98761
99949
  },
98762
99950
  {
98763
99951
  "name": "locale",
@@ -98773,44 +99961,69 @@
98773
99961
  }
98774
99962
  },
98775
99963
  {
98776
- "name": "zoom",
99964
+ "name": "math",
98777
99965
  "type": {
98778
- "text": "number"
99966
+ "text": "boolean"
98779
99967
  },
98780
- "default": "2",
98781
- "fieldName": "zoom"
99968
+ "default": "false",
99969
+ "description": "Renders `$...$`/`$$...$$` TeX via the optional `katex` peer, as MathML. `false` (the\ndefault) renders `$...$` literally, unparsed -- today's exact output.",
99970
+ "fieldName": "math"
99971
+ },
99972
+ {
99973
+ "name": "sanitize",
99974
+ "type": {
99975
+ "text": "boolean"
99976
+ },
99977
+ "default": "true",
99978
+ "description": "Sanitize marked's HTML output with DOMPurify before rendering. See the\nclass doc for what happens when this is `true` (the default) but the\n`dompurify` peer isn't installed.",
99979
+ "fieldName": "sanitize"
99980
+ },
99981
+ {
99982
+ "name": "streaming",
99983
+ "type": {
99984
+ "text": "boolean"
99985
+ },
99986
+ "default": "false",
99987
+ "description": "Signals that `content` is still arriving incrementally. Content changes\ncontinue to render immediately; while this is `true`, the host remains\n`aria-busy=\"true\"` so assistive technology knows the rendered document\nis not final. Set it back to `false` with the final content update.\nReflects so a consumer can also target `lyra-markdown[streaming]`.",
99988
+ "fieldName": "streaming"
99989
+ }
99990
+ ],
99991
+ "mixins": [
99992
+ {
99993
+ "name": "DocumentAnchorTarget",
99994
+ "module": "/src/internal/anchor-target.js"
98782
99995
  }
98783
99996
  ],
98784
99997
  "superclass": {
98785
- "name": "LyraElement",
98786
- "module": "/src/internal/lyra-element.js"
99998
+ "name": "LyraMarkdownCoreBase",
99999
+ "module": "src/components/markdown/markdown-core.class.ts"
98787
100000
  },
98788
- "tagName": "lyra-map",
100001
+ "tagName": "lyra-markdown-core",
98789
100002
  "customElement": true
98790
100003
  }
98791
100004
  ],
98792
100005
  "exports": [
98793
100006
  {
98794
100007
  "kind": "js",
98795
- "name": "LyraMap",
100008
+ "name": "LyraMarkdownCore",
98796
100009
  "declaration": {
98797
- "name": "LyraMap",
98798
- "module": "src/components/map/map.class.ts"
100010
+ "name": "LyraMarkdownCore",
100011
+ "module": "src/components/markdown/markdown-core.class.ts"
98799
100012
  }
98800
100013
  }
98801
100014
  ]
98802
100015
  },
98803
100016
  {
98804
100017
  "kind": "javascript-module",
98805
- "path": "src/components/map/map.ts",
100018
+ "path": "src/components/markdown/markdown-core.ts",
98806
100019
  "declarations": [],
98807
100020
  "exports": [
98808
100021
  {
98809
100022
  "kind": "custom-element-definition",
98810
- "name": "lyra-map",
100023
+ "name": "lyra-markdown-core",
98811
100024
  "declaration": {
98812
- "name": "LyraMap",
98813
- "module": "src/components/map/map.class.ts"
100025
+ "name": "LyraMarkdownCore",
100026
+ "module": "src/components/markdown/markdown-core.class.ts"
98814
100027
  }
98815
100028
  },
98816
100029
  {
@@ -98818,69 +100031,7 @@
98818
100031
  "name": "*",
98819
100032
  "declaration": {
98820
100033
  "name": "*",
98821
- "module": "src/components/map/map.class.js"
98822
- }
98823
- }
98824
- ]
98825
- },
98826
- {
98827
- "kind": "javascript-module",
98828
- "path": "src/components/markdown/katex-loader.ts",
98829
- "declarations": [
98830
- {
98831
- "kind": "function",
98832
- "name": "getKatex",
98833
- "return": {
98834
- "type": {
98835
- "text": "Promise<KatexApi | null>"
98836
- }
98837
- },
98838
- "parameters": [
98839
- {
98840
- "name": "importKatex",
98841
- "optional": true,
98842
- "type": {
98843
- "text": "() => Promise<{ default: KatexApi } | KatexApi>"
98844
- }
98845
- }
98846
- ],
98847
- "description": "Lazily loads `katex` once per page, caching the resolved module (or `null`) across every\n`<lyra-markdown>` instance -- mirrors `markdown-loader.ts`'s `loadMarkdownDeps()`/\n`getMarkdownDepsIfLoaded()` cached-promise shape for a single optional peer."
98848
- },
98849
- {
98850
- "kind": "function",
98851
- "name": "loadKatex",
98852
- "return": {
98853
- "type": {
98854
- "text": "Promise<KatexApi | null>"
98855
- }
98856
- },
98857
- "parameters": [
98858
- {
98859
- "name": "importKatex",
98860
- "default": "() =>\n import('katex') as Promise<{ default: KatexApi }>",
98861
- "type": {
98862
- "text": "() => Promise<{ default: KatexApi } | KatexApi>"
98863
- }
98864
- }
98865
- ],
98866
- "description": "Loads the optional peer dependency `katex`, used by `<lyra-markdown>`'s `math` property to\nrender `$...$`/`$$...$$` TeX as MathML. Resolves `null` (with a one-time `console.warn`) if the\npeer isn't installed -- rendering falls back to the literal, unparsed TeX source in that case,\na fully supported default rather than a degraded mode. Mirrors `dompurify-loader.ts`'s single-\noptional-peer shape."
98867
- }
98868
- ],
98869
- "exports": [
98870
- {
98871
- "kind": "js",
98872
- "name": "getKatex",
98873
- "declaration": {
98874
- "name": "getKatex",
98875
- "module": "src/components/markdown/katex-loader.ts"
98876
- }
98877
- },
98878
- {
98879
- "kind": "js",
98880
- "name": "loadKatex",
98881
- "declaration": {
98882
- "name": "loadKatex",
98883
- "module": "src/components/markdown/katex-loader.ts"
100034
+ "module": "src/components/markdown/markdown-core.class.js"
98884
100035
  }
98885
100036
  }
98886
100037
  ]
@@ -148768,7 +149919,7 @@
148768
149919
  "declarations": [
148769
149920
  {
148770
149921
  "kind": "class",
148771
- "description": "`<lyra-thread-list>` — the conversation sidebar: a grouped, searchable list of chat sessions with\npin/archive/delete/rename affordances. *Data mode* (non-empty `threads`, or empty `threads` with\nnothing slotted) renders every row as a `<lyra-conversation-item>` inside an internal\n`<lyra-virtual-list>` — virtualized by construction, scroll position and per-row state survive a\n`threads` replacement; zero rows renders the built-in empty state. *Slotted mode* (empty `threads`\n*and* real slotted content) renders host-supplied `<lyra-conversation-item>`s from the default slot\nas-is: no grouping, virtualization, or row actions in that mode — those are data-mode-only by design\n(shadow DOM cannot inject group headers between slotted children).\n\nNo thread CRUD or persistence: every mutation (`lyra-thread-pin`/`-archive`/`-delete`/`-rename`) is\na controlled event carrying the *requested* new state — the host mutates `threads`.",
149922
+ "description": "`<lyra-thread-list>` — the conversation sidebar: a grouped, searchable list of chat sessions with\npin/archive/delete/rename affordances. *Data mode* (non-empty `threads`, or empty `threads` with\nnothing slotted) renders every row as a `<lyra-conversation-item>` inside an internal\n`<lyra-virtual-list>` — virtualized by construction, scroll position and per-row state survive a\n`threads` replacement; zero rows renders the built-in empty state. *Slotted mode* (empty `threads`\n*and* real slotted content) renders host-supplied `<lyra-conversation-item>`s from the default slot\nas-is: no grouping, virtualization, or row actions in that mode — those are data-mode-only by design\n(shadow DOM cannot inject group headers between slotted children).\n\nNo thread CRUD or persistence: every mutation (`lyra-thread-pin`/`-archive`/`-delete`/`-rename`) is\na controlled event carrying the *requested* new state — the host mutates `threads`.\n\nData mode: a host needing content with no home in `lyra-conversation-item`'s own\n`title`/`excerpt`/`meta`/`actions` surface (e.g. a leading purpose icon — the item has no default\nslot to receive one) sets `wrapRow` to wrap the already-built row.",
148772
149923
  "name": "LyraThreadList",
148773
149924
  "cssParts": [
148774
149925
  {
@@ -149110,6 +150261,14 @@
149110
150261
  "privacy": "private",
149111
150262
  "readonly": true
149112
150263
  },
150264
+ {
150265
+ "kind": "field",
150266
+ "name": "wrapRow",
150267
+ "type": {
150268
+ "text": "(thread: ChatThread, row: TemplateResult) => TemplateResult | undefined"
150269
+ },
150270
+ "description": "Data mode only: wraps each row's built-in `<lyra-conversation-item>` with host-supplied\ncontent that has no home in the item's own `title`/`excerpt`/`meta`/`actions` surface — e.g. a\nleading purpose icon (`lyra-conversation-item` has no default slot to receive one) or trailing\ntag chips. Receives the thread and the already-built row `TemplateResult`; returns the final\nrow content. Unset renders the built-in row unwrapped."
150271
+ },
149113
150272
  {
149114
150273
  "kind": "method",
149115
150274
  "name": "addEventListener",
@@ -162082,7 +163241,7 @@
162082
163241
  "declarations": [
162083
163242
  {
162084
163243
  "kind": "class",
162085
- "description": "`<lyra-usage-badge>` — a compact, static resource strip for one message or run: tokens in/out,\ncost, latency, with a hover/focus tooltip breakdown. Purely formatting — this component computes\nno counts, rates, or prices; every segment is independently optional, and with nothing set,\nnothing renders at all (not even a focusable/interactive shell).\n\nThe tooltip reuses `<lyra-tool-call-chip>`'s hover/focus/Escape/`aria-describedby` contract\nwholesale: hover and focus are tracked as independent \"keep it open\" reasons, so releasing one\nmodality while the other still holds doesn't close it.",
163244
+ "description": "`<lyra-usage-badge>` — a compact, static resource strip for one message or run: tokens in/out,\ncost, latency, with a hover/focus tooltip breakdown. Purely formatting — this component computes\nno counts, rates, or prices; every segment is independently optional, and with nothing set,\nnothing renders at all (not even a focusable/interactive shell).\n\nThe tooltip reuses `<lyra-tool-call-chip>`'s hover/focus/Escape/`aria-describedby` contract\nwholesale: hover and focus are tracked as independent \"keep it open\" reasons, so releasing one\nmodality while the other still holds doesn't close it.\n\nThe built-in latency formatting has no minutes/hours tier (`'{ms}ms'`, or one-decimal seconds\nabove 1000ms) — a host whose latencies commonly exceed a minute sets `formatLatency` to render\nits own scale instead, in both the visible strip and the tooltip row.",
162086
163245
  "name": "LyraUsageBadge",
162087
163246
  "cssParts": [
162088
163247
  {
@@ -162194,6 +163353,14 @@
162194
163353
  "privacy": "private",
162195
163354
  "default": "false"
162196
163355
  },
163356
+ {
163357
+ "kind": "field",
163358
+ "name": "formatLatency",
163359
+ "type": {
163360
+ "text": "(ms: number) => string | undefined"
163361
+ },
163362
+ "description": "Overrides the default `formatDuration()` rendering of `latencyMs` (`'{ms}ms'`, or one-decimal\nseconds above 1000ms — no minutes/hours tier) in both the visible strip and the tooltip row.\nMirrors `<lyra-activity-feed>`'s `formatTimestamp` convention."
163363
+ },
162197
163364
  {
162198
163365
  "kind": "field",
162199
163366
  "name": "hasCost",
@@ -162272,7 +163439,7 @@
162272
163439
  "type": {
162273
163440
  "text": "number | undefined"
162274
163441
  },
162275
- "description": "Latency in milliseconds, formatted with the shared duration algorithm.",
163442
+ "description": "Latency in milliseconds, formatted with the shared duration algorithm (or `formatLatency`,\nwhen set).",
162276
163443
  "attribute": "latency-ms"
162277
163444
  },
162278
163445
  {
@@ -162692,7 +163859,7 @@
162692
163859
  "type": {
162693
163860
  "text": "number | undefined"
162694
163861
  },
162695
- "description": "Latency in milliseconds, formatted with the shared duration algorithm.",
163862
+ "description": "Latency in milliseconds, formatted with the shared duration algorithm (or `formatLatency`,\nwhen set).",
162696
163863
  "fieldName": "latencyMs"
162697
163864
  },
162698
163865
  {