@aceshooting/lyra-ui 21.0.0 → 21.1.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 (272) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +6 -3
  3. package/custom-elements.json +1 -1
  4. package/dist/ai/runtime.js +1 -1
  5. package/dist/ai/types.d.ts +13 -5
  6. package/dist/cli/lyra-rename-ledger.mjs +1 -0
  7. package/dist/cli/migrate-wa.mjs +33 -13
  8. package/dist/cli/migration-contract.json +1 -1
  9. package/dist/components/agent-tools/agent-trace/agent-trace.class.d.ts +5 -3
  10. package/dist/components/agent-tools/agent-trace/agent-trace.class.js +3 -2
  11. package/dist/components/agent-tools/tool-call-block/tool-call-block.class.d.ts +15 -3
  12. package/dist/components/agent-tools/tool-call-block/tool-call-block.class.js +3 -3
  13. package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.d.ts +24 -8
  14. package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.js +3 -3
  15. package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +4 -0
  16. package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.d.ts +9 -5
  17. package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.js +2 -2
  18. package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.styles.js +1 -1
  19. package/dist/components/agent-tools/tool-status.js +2 -2
  20. package/dist/components/agent-tools/tool-timeline/tool-timeline.class.d.ts +8 -0
  21. package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +3 -2
  22. package/dist/components/agent-tools/tool-timeline/tool-timeline.styles.js +1 -1
  23. package/dist/components/agent-tools/trace-tree/span.d.ts +2 -2
  24. package/dist/components/charts/chart/box-plot.class.d.ts +3 -1
  25. package/dist/components/charts/chart/box-plot.class.js +9 -9
  26. package/dist/components/charts/chart/chart-bidi.d.ts +109 -0
  27. package/dist/components/charts/chart/chart-bidi.js +1 -0
  28. package/dist/components/charts/chart/chart.class.d.ts +17 -2
  29. package/dist/components/charts/chart/chart.class.js +10 -8
  30. package/dist/components/charts/chart/histogram.class.js +1 -1
  31. package/dist/components/charts/chart/lite-chart.class.d.ts +24 -5
  32. package/dist/components/charts/chart/lite-chart.class.js +9 -9
  33. package/dist/components/charts/chart/lite-chart.styles.js +1 -1
  34. package/dist/components/conversation/code-block/shiki-dark-theme.d.ts +2 -2
  35. package/dist/components/conversation/code-block/shiki-dark-theme.js +1 -1
  36. package/dist/components/conversation/conversation-item/conversation-item.class.d.ts +18 -2
  37. package/dist/components/conversation/conversation-item/conversation-item.class.js +3 -3
  38. package/dist/components/conversation/conversation-item/conversation-item.styles.js +1 -1
  39. package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
  40. package/dist/components/conversation/markdown/markdown-core.class.d.ts +3 -1
  41. package/dist/components/conversation/markdown/markdown.class.d.ts +3 -1
  42. package/dist/components/conversation/message-actions/message-actions.class.d.ts +7 -2
  43. package/dist/components/conversation/message-actions/message-actions.class.js +2 -2
  44. package/dist/components/conversation/message-parts/message-parts.class.d.ts +6 -2
  45. package/dist/components/conversation/message-parts/message-parts.class.js +5 -3
  46. package/dist/components/conversation/selection-toolbar/selection-toolbar.class.d.ts +3 -2
  47. package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
  48. package/dist/components/conversation/streaming-text/streaming-text-base.class.d.ts +1 -5
  49. package/dist/components/conversation/streaming-text/streaming-text-base.class.js +2 -2
  50. package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +7 -1
  51. package/dist/components/conversation/streaming-text/streaming-text-core.class.js +3 -4
  52. package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +7 -1
  53. package/dist/components/conversation/streaming-text/streaming-text.class.js +3 -4
  54. package/dist/components/conversation/thread-list/thread-list.class.d.ts +13 -2
  55. package/dist/components/conversation/thread-list/thread-list.class.js +3 -3
  56. package/dist/components/data/data-grid/data-grid.class.js +1 -1
  57. package/dist/components/data/document-library/document-library.class.js +2 -0
  58. package/dist/components/data/flow-canvas/flow-canvas.class.js +1 -1
  59. package/dist/components/data/flow-minimap/flow-minimap.class.js +1 -1
  60. package/dist/components/data/flow-node/flow-node.class.d.ts +6 -2
  61. package/dist/components/data/flow-node/flow-node.class.js +6 -2
  62. package/dist/components/data/flow-node/flow-node.styles.js +1 -1
  63. package/dist/components/data/gauge/gauge.styles.js +1 -1
  64. package/dist/components/data/heatmap/heatmap.class.d.ts +3 -0
  65. package/dist/components/data/heatmap/heatmap.class.js +2 -2
  66. package/dist/components/data/heatmap/heatmap.styles.js +1 -1
  67. package/dist/components/data/sparkline/sparkline.class.d.ts +5 -3
  68. package/dist/components/data/stat/stat.class.d.ts +4 -3
  69. package/dist/components/data/table/table.class.d.ts +32 -5
  70. package/dist/components/data/table/table.class.js +3 -2
  71. package/dist/components/forms/button/button.class.d.ts +7 -1
  72. package/dist/components/forms/button/button.styles.js +1 -1
  73. package/dist/components/forms/date-picker/date-input.styles.js +1 -1
  74. package/dist/components/forms/input/input.styles.js +1 -1
  75. package/dist/components/forms/rubric-form/rubric-form.class.js +1 -0
  76. package/dist/components/layout/app-rail/app-rail.class.d.ts +50 -11
  77. package/dist/components/layout/app-rail/app-rail.class.js +2 -2
  78. package/dist/components/layout/command-palette/command-palette.class.js +1 -1
  79. package/dist/components/layout/menu/menu.class.d.ts +6 -5
  80. package/dist/components/layout/split-panel/split-panel.class.d.ts +2 -1
  81. package/dist/components/media/flag/language-map.d.ts +8 -0
  82. package/dist/components/media/flag/language-map.js +1 -1
  83. package/dist/components/media/map/map-loader.d.ts +7 -1
  84. package/dist/components/media/map/map.class.d.ts +111 -5
  85. package/dist/components/media/map/map.class.js +3 -3
  86. package/dist/components/media/media-card/media-card.class.d.ts +18 -5
  87. package/dist/components/media/media-card/media-card.class.js +1 -1
  88. package/dist/components/overlays/overlay/popover.class.d.ts +20 -0
  89. package/dist/components/overlays/overlay/popover.class.js +2 -2
  90. package/dist/components/retrieval/graph/graph.class.d.ts +3 -3
  91. package/dist/components/retrieval/graph/graph.class.js +1 -1
  92. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +5 -3
  93. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js +3 -2
  94. package/dist/components/retrieval/mind-map/mind-map.class.d.ts +3 -3
  95. package/dist/components/retrieval/mind-map/mind-map.class.js +1 -1
  96. package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts +4 -1
  97. package/dist/components/retrieval/retrieval-results/retrieval-results.class.js +5 -1
  98. package/dist/components/retrieval/source-picker/source-picker.class.d.ts +4 -1
  99. package/dist/components/retrieval/source-picker/source-picker.class.js +5 -1
  100. package/dist/components/utility/icon/icon.class.d.ts +9 -3
  101. package/dist/components/utility/icon/icon.class.js +1 -1
  102. package/dist/components/utility/mutation-observer/mutation-observer.class.d.ts +14 -8
  103. package/dist/components/utility/mutation-observer/mutation-observer.class.js +1 -1
  104. package/dist/components/viewers/document-viewer/document-viewer.class.d.ts +6 -1
  105. package/dist/components/viewers/document-viewer/document-viewer.class.js +7 -1
  106. package/dist/components/viewers/document-viewer/registry.d.ts +12 -3
  107. package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +13 -5
  108. package/dist/components/viewers/geojson-view/geojson-view.class.js +1 -1
  109. package/dist/components/viewers/geojson-view/geojson-viewer.class.d.ts +3 -2
  110. package/dist/custom-elements-jsx.d.ts +1 -1
  111. package/dist/events.d.ts +644 -1242
  112. package/dist/internal/anchored-overlay-runtime.d.ts +2 -2
  113. package/dist/internal/cell-range.d.ts +0 -6
  114. package/dist/internal/cell-range.js +1 -1
  115. package/dist/internal/css-token-length.d.ts +7 -0
  116. package/dist/internal/css-token-length.js +1 -0
  117. package/dist/internal/default-strings.generated.d.ts +1 -1
  118. package/dist/internal/default-strings.generated.js +1 -1
  119. package/dist/internal/deferred-focus-return.d.ts +7 -4
  120. package/dist/internal/deferred-focus-return.js +1 -1
  121. package/dist/internal/dev-mode-attribute-warning.d.ts +35 -0
  122. package/dist/internal/dev-mode-attribute-warning.js +1 -1
  123. package/dist/internal/localization-runtime.js +1 -1
  124. package/dist/internal/localization-types.d.ts +1 -1
  125. package/dist/internal/localization.js +2 -2
  126. package/dist/internal/package-metadata.d.ts +1 -1
  127. package/dist/internal/package-metadata.js +1 -1
  128. package/dist/internal/papaparse-loader.d.ts +1 -1
  129. package/dist/internal/papaparse-loader.js +1 -1
  130. package/dist/internal/positioner.d.ts +10 -0
  131. package/dist/internal/positioner.js +1 -1
  132. package/dist/internal/shared-unions.d.ts +2 -1
  133. package/dist/internal/slotted-overlay-controller.d.ts +20 -0
  134. package/dist/internal/slotted-overlay-controller.js +1 -0
  135. package/dist/internal/text-quote.d.ts +1 -1
  136. package/dist/internal/text-quote.js +1 -1
  137. package/dist/internal/theme-observation.d.ts +4 -0
  138. package/dist/internal/theme-observation.js +1 -0
  139. package/dist/internal/theme-watcher.js +1 -1
  140. package/dist/localization.d.ts +6 -1
  141. package/dist/localization.js +1 -1
  142. package/dist/lyra.d.ts +1 -1
  143. package/dist/lyra.js +1 -1
  144. package/dist/svelte.d.ts +1 -1
  145. package/dist/testing/lyra-tag-event-map.js +1 -1
  146. package/dist/translations/ar/agent-tools.js +1 -1
  147. package/dist/translations/cs/agent-tools.js +1 -1
  148. package/dist/translations/da/agent-tools.js +1 -1
  149. package/dist/translations/de/agent-tools.js +1 -1
  150. package/dist/translations/de-CH/agent-tools.js +1 -1
  151. package/dist/translations/es/agent-tools.js +1 -1
  152. package/dist/translations/fa/agent-tools.js +1 -1
  153. package/dist/translations/fi/agent-tools.js +1 -1
  154. package/dist/translations/fr/agent-tools.js +1 -1
  155. package/dist/translations/he/agent-tools.js +1 -1
  156. package/dist/translations/hi/agent-tools.js +1 -1
  157. package/dist/translations/hr/agent-tools.js +1 -1
  158. package/dist/translations/hu/agent-tools.js +1 -1
  159. package/dist/translations/id/agent-tools.js +1 -1
  160. package/dist/translations/it/agent-tools.js +1 -1
  161. package/dist/translations/ja/agent-tools.js +1 -1
  162. package/dist/translations/kk/agent-tools.js +1 -1
  163. package/dist/translations/ko/agent-tools.js +1 -1
  164. package/dist/translations/nb/agent-tools.js +1 -1
  165. package/dist/translations/nl/agent-tools.js +1 -1
  166. package/dist/translations/nn/agent-tools.js +1 -1
  167. package/dist/translations/pl/agent-tools.js +1 -1
  168. package/dist/translations/pt-BR/agent-tools.js +1 -1
  169. package/dist/translations/pt-PT/agent-tools.js +1 -1
  170. package/dist/translations/pt-PT/charts.js +1 -1
  171. package/dist/translations/pt-PT/conversation.js +1 -1
  172. package/dist/translations/pt-PT/data.js +1 -1
  173. package/dist/translations/pt-PT/forms.js +1 -1
  174. package/dist/translations/pt-PT/layout.js +1 -1
  175. package/dist/translations/pt-PT/media.js +1 -1
  176. package/dist/translations/pt-PT/retrieval.js +1 -1
  177. package/dist/translations/pt-PT/shared.js +1 -1
  178. package/dist/translations/pt-PT/utility.js +1 -1
  179. package/dist/translations/pt-PT/viewers.js +2 -2
  180. package/dist/translations/ro/agent-tools.js +1 -1
  181. package/dist/translations/ru/agent-tools.js +1 -1
  182. package/dist/translations/sl/agent-tools.js +1 -1
  183. package/dist/translations/sv/agent-tools.js +1 -1
  184. package/dist/translations/tr/agent-tools.js +1 -1
  185. package/dist/translations/uk/agent-tools.js +1 -1
  186. package/dist/translations/zh-CN/agent-tools.js +1 -1
  187. package/dist/translations/zh-TW/agent-tools.js +1 -1
  188. package/dist/utilities/index.d.ts +1 -1
  189. package/dist/utilities/index.js +1 -1
  190. package/dist/utilities/localization.d.ts +28 -10
  191. package/dist/vue.d.ts +1 -1
  192. package/llms/components/lr-accordion-item.md +2 -344
  193. package/llms/components/lr-accordion.md +2 -344
  194. package/llms/components/lr-app-rail-group.md +2 -721
  195. package/llms/components/lr-app-rail-item.md +2 -721
  196. package/llms/components/lr-app-rail.md +29 -4
  197. package/llms/components/lr-bar-chart.md +2 -87
  198. package/llms/components/lr-box-plot.md +1 -1
  199. package/llms/components/lr-breadcrumb-item.md +2 -67
  200. package/llms/components/lr-bubble-chart.md +2 -87
  201. package/llms/components/lr-button.md +7 -0
  202. package/llms/components/lr-chart.md +4 -5
  203. package/llms/components/lr-chip-group.md +2 -272
  204. package/llms/components/lr-conversation-item.md +20 -2
  205. package/llms/components/lr-date-input.md +2 -457
  206. package/llms/components/lr-date-picker.md +6 -2
  207. package/llms/components/lr-document-library.md +3 -3
  208. package/llms/components/lr-document-viewer.md +14 -5
  209. package/llms/components/lr-doughnut-chart.md +2 -87
  210. package/llms/components/lr-dropdown-item.md +2 -306
  211. package/llms/components/lr-dropdown.md +6 -0
  212. package/llms/components/lr-flag.md +9 -1
  213. package/llms/components/lr-flow-node.md +4 -1
  214. package/llms/components/lr-geojson-view.md +3 -90
  215. package/llms/components/lr-geojson-viewer.md +13 -5
  216. package/llms/components/lr-icon-button.md +4 -1
  217. package/llms/components/lr-icon.md +14 -6
  218. package/llms/components/lr-input.md +9 -2
  219. package/llms/components/lr-lite-chart.md +17 -9
  220. package/llms/components/lr-map.md +87 -2
  221. package/llms/components/lr-markdown-core.md +5 -4
  222. package/llms/components/lr-markdown.md +4 -2
  223. package/llms/components/lr-media-card.md +10 -5
  224. package/llms/components/lr-menu-item.md +2 -306
  225. package/llms/components/lr-menu.md +9 -6
  226. package/llms/components/lr-menubar-item.md +2 -125
  227. package/llms/components/lr-message-actions.md +4 -1
  228. package/llms/components/lr-message-parts.md +14 -5
  229. package/llms/components/lr-mutation-observer.md +12 -6
  230. package/llms/components/lr-navigation-menu-item.md +2 -237
  231. package/llms/components/lr-number-input.md +4 -0
  232. package/llms/components/lr-option.md +2 -712
  233. package/llms/components/lr-pie-chart.md +2 -87
  234. package/llms/components/lr-polar-area-chart.md +2 -87
  235. package/llms/components/lr-popover.md +10 -0
  236. package/llms/components/lr-prompt-input.md +5 -1
  237. package/llms/components/lr-radar-chart.md +2 -87
  238. package/llms/components/lr-reorder-item.md +2 -157
  239. package/llms/components/lr-result-field.md +2 -117
  240. package/llms/components/lr-retrieval-results.md +3 -1
  241. package/llms/components/lr-rubric-form.md +3 -2
  242. package/llms/components/lr-scatter-chart.md +2 -87
  243. package/llms/components/lr-selection-toolbar.md +4 -2
  244. package/llms/components/lr-source-card.md +2 -186
  245. package/llms/components/lr-source-picker.md +3 -1
  246. package/llms/components/lr-sparkline.md +9 -4
  247. package/llms/components/lr-split-panel.md +5 -3
  248. package/llms/components/lr-stat.md +4 -3
  249. package/llms/components/lr-streaming-text-core.md +7 -5
  250. package/llms/components/lr-streaming-text.md +9 -6
  251. package/llms/components/lr-swatch-picker.md +130 -0
  252. package/llms/components/lr-tag.md +2 -177
  253. package/llms/components/lr-thread-list.md +27 -4
  254. package/llms/components/lr-timeline-item.md +2 -120
  255. package/llms/components/lr-toast-item.md +2 -223
  256. package/llms/components/lr-tool-call-block.md +12 -3
  257. package/llms/components/lr-tool-call-chip.md +12 -6
  258. package/llms/components/lr-tool-param-form.md +3 -2
  259. package/llms/components/lr-tool-result-dialog.md +7 -3
  260. package/llms/components/lr-tool-result-view.md +4 -3
  261. package/llms/components/lr-tool-timeline.md +12 -5
  262. package/llms/components/lr-tree-item.md +2 -354
  263. package/llms/index.md +22 -4
  264. package/llms/migration.md +75 -1
  265. package/llms/peers.md +1 -1
  266. package/llms/shared.md +86 -39
  267. package/llms-full.txt +739 -163
  268. package/package.json +6 -6
  269. package/registrations.json +1 -1
  270. package/vscode-css-data.json +1 -1
  271. package/vscode-html-data.json +1 -1
  272. package/web-types.json +1 -1
package/llms-full.txt CHANGED
@@ -12,7 +12,7 @@ Read it end to end only if you genuinely need everything. Otherwise:
12
12
 
13
13
  | To… | Read |
14
14
  |---|---|
15
- | use one component | `llms/components/<tag>.md` (path derived from the tag; self-contained) |
15
+ | use one component | `llms/components/<tag>.md` (path derived from the tag; a sibling-documented tag points to its primary file) |
16
16
  | find the right component | `llms/index.md` (every tag, its import path, one-line purpose) |
17
17
  | get library-wide behavior right | `llms/shared.md` (status, imports/autoloading, events, forms, theming/styles, i18n/RTL, TS/frameworks, SSR, utilities, AI types) |
18
18
  | theme it | `llms/tokens.md` |
@@ -28,9 +28,11 @@ a sibling `.class.js` exports a class without registering it.
28
28
  ## Contents
29
29
 
30
30
  This file is the concatenation of the per-family sources in `llms/`. When only one component is
31
- in question, read `llms/components/<tag>.md` instead — it is the same text plus its import path,
32
- and costs a few hundred tokens instead of the whole catalog. `llms/index.md` maps every tag to its
33
- family and entry point.
31
+ in question, read `llms/components/<tag>.md` instead — for a tag documented on its own, that file
32
+ holds the same text plus its import path; for a tag documented together with siblings, it holds
33
+ the generated facts (import path, status, peers, and so on) plus a pointer to the primary
34
+ sibling's file for the shared prose. Either way it costs a few hundred tokens instead of the
35
+ whole catalog. `llms/index.md` maps every tag to its family and entry point.
34
36
 
35
37
  - **Foundation** — base class, form association, events, i18n, theming, TypeScript, frameworks, SSR, packaging (`llms/shared.md`)
36
38
  - **Form controls & inputs** — 31 tags in `components/forms/`
@@ -82,15 +84,40 @@ customary M+1 warning period — `lr-usage-badge`'s `compact`, `lr-chart`'s `hor
82
84
  a mechanical migration listed in the 9.0.0 changelog entry and in `migration.md`. From 9.0.0 onward
83
85
  the M+2 rule applies as written; treat the above as a documented exception, not a precedent.
84
86
 
87
+ Deprecation records also cover three things that are not a single member. Deprecated **slot
88
+ content** is a kind of content inside a slot that stays supported (for example, content other than
89
+ items in a menu's default slot); the slot's own description says which content is deprecated.
90
+ Deprecated **package entry points** (an import path) and deprecated **exported types** are listed
91
+ under "Deprecated package exports" at the end of `llms/index.md`; a deprecated type also carries
92
+ `@deprecated` in its declaration, so editors strike it through. Each component's own deprecations
93
+ are listed in its `llms/components/<tag>.md` header and in `custom-elements.json` (the declaration's
94
+ `deprecations`, plus a `deprecation` on each deprecated member). These records are the complete
95
+ list of deprecated APIs; audit an upgrade against them, not against console output.
96
+
97
+ **Development warnings.** Some deprecations also log a development-mode `console.warn` naming the
98
+ element, the deprecated API, and its replacement. A warning only ever fires for usage a component
99
+ can observe cheaply and exactly — setting a deprecated property or attribute, connecting a
100
+ deprecated tag, or vetoing through a deprecated alias event — and **not every deprecated API
101
+ warns**: many deprecations have no runtime warning, among them `lr-icon`'s `auto-width`,
102
+ `lr-stat`'s default-slot icon, `lr-menu`'s non-item default-slot content, the `lr-before-*` alias
103
+ events of `lr-chart`, `lr-box-plot`, `lr-graph-legend` and `lr-graph-query-builder`, and
104
+ `lr-command-palette`'s `lr-open` alias event, and deprecated CSS parts, custom properties, and
105
+ custom states are styling hooks a component cannot observe, so they never warn. A silent console
106
+ is therefore not evidence that an application uses no deprecated API. Where a warning exists, it
107
+ fires once per page for each element name and deprecated API, and only when Lit runs its
108
+ development build; production builds never warn. The deprecated form keeps working either
109
+ way, and moving to the replacement removes the warning. A test suite that fails on console output
110
+ while deliberately exercising a deprecated form can pre-seed Lit's development-mode
111
+ `globalThis.litIssuedWarnings` set, before the element renders, with the key
112
+ `lyra-deprecated:<tag>:<kind>:<name>` — `<kind>` and `<name>` as in the deprecation record.
113
+
85
114
  ### Release history and upgrade notes
86
115
 
87
- `since` records when a tag first appeared; it is not a history of later additions, fixes, or
88
- breaking changes. For every release after 9.0.0 — including minor and patch releases — read the
89
- package's shipped [CHANGELOG.md](./CHANGELOG.md) before upgrading. Family-wide breaking-change
90
- summaries sit at the start of the applicable authored `llms/<family>.md` file; each generated
91
- `llms/components/<tag>.md` header links that family summary when one exists. Component-specific
92
- version notes remain in the component's own section. `llms/migration.md` is narrower: it covers
93
- `wa-*`/`sl-*` renames and compatibility decisions, not Lyra release history.
116
+ `since` records when a tag first appeared, not later changes. Before upgrading, read the
117
+ package's [CHANGELOG.md](./CHANGELOG.md) (every release, including minor and patch).
118
+ Family-wide breaking-change summaries open each authored `llms/<family>.md`, and each generated
119
+ `llms/components/<tag>.md` header links its family summary. `llms/migration.md` covers only
120
+ `wa-*`/`sl-*` renames, not Lyra release history.
94
121
 
95
122
  The major-version landmarks after 9.0.0 are:
96
123
 
@@ -2153,17 +2180,21 @@ override takes precedence over either page-level choice.
2153
2180
 
2154
2181
  To keep `<html lang>`/`dir` in step with `setLyraLocale()` — which everything _outside_ this library
2155
2182
  reads, from `:lang()` rules to spellcheck to a screen reader's pronunciation — use
2156
- `bridgeLyraLocale()` from `@aceshooting/lyra-ui/utilities/localization.js` (see
2183
+ `bridgeLyraLocale()` from `@aceshooting/lyra-ui/localization.js` (see
2157
2184
  [Shared helpers](#shared-helpers-utilities)).
2158
2185
 
2159
2186
  The side-effect-free `@aceshooting/lyra-ui/localization.js` entry exports
2160
2187
  `registerLyraLocale`, `setLyraLocale`, `getLyraLocale`, `getLyraLocaleDirection`,
2161
2188
  `getRegisteredLyraLocales`, `getRegisteredLyraLocaleKeys`, `subscribeLyraLocaleRegistry`,
2162
- `resolveLyraLocale`, `resolveLyraDirection`, `resolveLyraString`, `LYRA_DEFAULT_STRINGS`, and the types
2189
+ `subscribeLyraLocale`, `bridgeLyraLocale`, `resolveLyraLocale`, `resolveLyraDirection`,
2190
+ `resolveLyraString`, `resolveLyraScopedString`, `LYRA_DEFAULT_STRINGS`, and the types
2163
2191
  `LyraLocaleStrings`, `LyraLocaleMeta`, `LyraLocaleDirection`, `LyraMessageKey`, `LyraMessage`,
2164
- `LyraPluralMessage` and `LyraPluralCategory`. The package root continues to re-export the same
2165
- surface for compatibility and remains registration-free; use the dedicated entry when the
2166
- application only needs locale setup and the narrower import graph.
2192
+ `LyraPluralMessage`, `LyraPluralCategory`, `LyraLocaleBridgeOptions` and `LyraLocaleBridgeCleanup`.
2193
+ The package root continues to re-export the runtime for compatibility and remains registration-free;
2194
+ use the dedicated entry when the application only needs locale setup and the narrower import graph.
2195
+ The `@aceshooting/lyra-ui/utilities/localization.js` entry point is deprecated, with removal not
2196
+ before 23.0.0: every name it exports is exported here as the identical binding, so change the import
2197
+ specifier to `@aceshooting/lyra-ui/localization.js` and nothing else.
2167
2198
  **`LYRA_DEFAULT_STRINGS` is the authoritative key list** (matching the `LyraMessageKey` union) —
2168
2199
  read it to find the key to override rather than guessing one. Date, number, byte, relative-time and
2169
2200
  calendar output goes through `Intl`.
@@ -2186,14 +2217,20 @@ category selection walks the same chain, so the two can never disagree:
2186
2217
  ceiling retains its exact tag, base language and English fallback without constructing an
2187
2218
  unbounded prefix ladder; malformed over-complex inherited input goes directly to English.
2188
2219
  2. **Then any registered, well-formed BCP-47 catalog sharing the base language**, which is how a
2189
- _regional-only_ catalog is reached from a less specific tag: `lang="zh"` and `lang="zh-Hans"`
2190
- both find the shipped `zh-CN` catalog, while `lang="pt"` reaches `pt-BR` and an exact `lang="pt-PT"`
2191
- selects the European Portuguese catalog. Order within this
2192
- step is deterministic and independent of import order — most shared subtags first
2193
- (`zh-Hant-TW` prefers a registered `zh-TW` over `zh-CN`), then alphabetically as the tie-break
2194
- (bare `zh` with both registered picks `zh-CN`). Register the regional tag you actually mean if
2195
- the tie-break isn't the answer you want. Legacy/custom tags are exact/truncation-addressable
2196
- only and do not become regional siblings accidentally.
2220
+ _regional-only_ catalog is reached from a less specific tag (`lang="zh"` finds `zh-CN`,
2221
+ `lang="pt"` finds `pt-BR`). Order is deterministic and independent of import order:
2222
+ 1. **Same likely script** — the explicit script subtag, else CLDR likely subtags via
2223
+ `Intl.Locale#maximize()`. `zh-Hant`, `zh-HK`, `zh-MO` and `zh-Hant-CN` reach Traditional
2224
+ `zh-TW`; `zh`, `zh-Hans`, `zh-SG` and `zh-Hans-HK` reach Simplified `zh-CN`. An unknown
2225
+ script (none, `Zzzz`, or no `Intl.Locale`) never matches.
2226
+ 2. **Same likely region** — a region-less tag reaches its CLDR default region: bare `pt` picks
2227
+ `pt-BR` even with `pt-PT` imported.
2228
+ 3. **Most shared subtags**, counting each occurrence (`pt-AO` and `pt-MZ` pick `pt-PT`;
2229
+ `qaa-Hant-TW` picks `qaa-TW` over `qaa-CN`).
2230
+ 4. **Alphabetical**, as a stable tie-break.
2231
+
2232
+ Register the regional tag you mean if the ordering isn't what you want. Legacy/custom tags are
2233
+ exact/truncation-addressable only and never become regional siblings.
2197
2234
  3. **Then `en`**, always available through the built-in English defaults.
2198
2235
 
2199
2236
  Step 1 always beats step 2: with both `zh` and `zh-CN` registered, `zh-Hans-CN` resolves to `zh`.
@@ -2236,7 +2273,7 @@ through to the next valid tier.
2236
2273
  sorted, deduped and canonically spelled. `subscribeLyraLocaleRegistry(listener: () => void): () =>
2237
2274
  void` fires when registry membership grows, including for a newly registered locale that is not
2238
2275
  active; extending an existing catalog does not change membership and does not fire it.
2239
- `subscribeLyraLocale()` (on `@aceshooting/lyra-ui/utilities/localization.js`) instead fires when the
2276
+ `subscribeLyraLocale()` (also on `@aceshooting/lyra-ui/localization.js`) instead fires when the
2240
2277
  active selection changes or a registration can alter the active locale's messages/direction, and
2241
2278
  filters unrelated registrations. Both return an idempotent unsubscribe. Delivery uses the eligible
2242
2279
  starting listener snapshots: one callback failure cannot prevent later active, component or
@@ -2279,11 +2316,11 @@ import "@aceshooting/lyra-ui/translations/ar.js"; // declares dir: 'rtl'; direct
2279
2316
  import "@aceshooting/lyra-ui/translations/fa.js"; // fa-IR falls back to this base catalog
2280
2317
  import "@aceshooting/lyra-ui/translations/he.js"; // he-IL falls back to this base catalog
2281
2318
  import "@aceshooting/lyra-ui/translations/it.js"; // Italian
2282
- import "@aceshooting/lyra-ui/translations/pt-BR.js"; // serves generic pt and pt-BR
2283
- import "@aceshooting/lyra-ui/translations/pt-PT.js"; // European Portuguese
2319
+ import "@aceshooting/lyra-ui/translations/pt-BR.js"; // Brazilian: serves pt and pt-BR
2320
+ import "@aceshooting/lyra-ui/translations/pt-PT.js"; // European: serves pt-PT, pt-AO, pt-MZ
2284
2321
  import "@aceshooting/lyra-ui/translations/ro.js"; // Romanian
2285
- import "@aceshooting/lyra-ui/translations/zh-CN.js"; // serves zh, zh-Hans and zh-Hans-CN
2286
- import "@aceshooting/lyra-ui/translations/zh-TW.js"; // Traditional Chinese
2322
+ import "@aceshooting/lyra-ui/translations/zh-CN.js"; // Simplified: serves zh, zh-Hans, zh-SG and zh-Hans-CN
2323
+ import "@aceshooting/lyra-ui/translations/zh-TW.js"; // Traditional: serves zh-Hant, zh-HK and zh-MO
2287
2324
  ```
2288
2325
 
2289
2326
  Persian and Hebrew use CLDR plural categories (`fa`: `one`/`other`; `he`:
@@ -2295,10 +2332,11 @@ and `he` declare `dir: 'rtl'`, so `getLyraLocaleDirection()`
2295
2332
  answers for them (and for `ar-EG`, `fa-IR`, `he-IL`) — but locale selection still does not _force_
2296
2333
  writing direction: set `dir="rtl"` on the page or an ancestor yourself.
2297
2334
 
2298
- `de-CH`, `pt-BR`, `pt-PT`, `zh-CN`, and `zh-TW` are regional catalogs. Step 2 of the lookup order above
2299
- still makes each reachable from a less-specific language tag, while an exact `lang="de-CH"`,
2300
- `lang="pt-PT"` or `lang="zh-TW"` selects its dedicated regional catalog. They are listed under their real tags in
2301
- `getRegisteredLyraLocales()`.
2335
+ `de-CH`, `pt-BR`, `pt-PT`, `zh-CN`, and `zh-TW` are regional catalogs: an exact tag always selects
2336
+ its own catalog, and a less specific tag reaches one through step 2 only when nothing on its
2337
+ truncation walk answers first (with `de` imported, `lang="de-AT"` resolves to `de`, never `de-CH`).
2338
+ The same routing drives plural selection and `getLyraLocaleDirection()`. They are listed under
2339
+ their real tags in `getRegisteredLyraLocales()`.
2302
2340
 
2303
2341
  Import only the locales the application can actually offer — each is a separate module, so unimported
2304
2342
  ones cost nothing. A catalog registered this way is merged like any other, so a later
@@ -2434,6 +2472,14 @@ import {
2434
2472
  validates them on `tool-upsert` events and `tool-call` parts: a non-finite or non-number time, or
2435
2473
  a `redactedFields` that is not an array of at most 100 strings of at most 4,096 characters, fails
2436
2474
  the event as `invalid_stream_event`.
2475
+ - **Tool display name and incomplete calls** (21.1.0) — `ToolInvocation.displayName` is the
2476
+ application's own, already-translated tool label; `<lr-message-parts>` (both `tool-display`
2477
+ modes) and `<lr-tool-timeline>` show it in place of `name`, which still selects the result
2478
+ renderer. `status: 'incomplete'` marks a call that ended without a result (an interrupted stream,
2479
+ a cancelled run); set it yourself on a `tool-upsert` or `tool-call` part, since the adapters never
2480
+ infer it. The runtime accepts both on `tool-upsert` events, `tool-call` parts and message
2481
+ snapshots; a `displayName` that is not a string of at most `maxIdentifierCharacters` fails the
2482
+ event as `invalid_stream_event`, and a later upsert that omits it keeps the retained one.
2437
2483
  - **Resource limits** — pass a partial `AgentStreamLimits` to `createAgentStreamState()`, or adapter
2438
2484
  limit options to the relevant adapter. The exported `DEFAULT_AGENT_STREAM_LIMITS`,
2439
2485
  `DEFAULT_AI_SDK_ADAPTER_LIMITS`, `DEFAULT_AG_UI_ADAPTER_LIMITS`, and
@@ -3274,9 +3320,12 @@ readonly politeness: AnnouncementPoliteness; messageTtlMs: number; announce(text
3274
3320
  release(): void }`, and `AnnouncerTimerHost { setTimeout(handler: () => void, timeout: number):
3275
3321
  number; clearTimeout(handle: number): void }`.
3276
3322
  - **`localization` → `subscribeLyraLocale(listener): () => void` and
3277
- `bridgeLyraLocale(options?): () => void`** — the _active-locale_ half of the locale runtime,
3278
- which the side-effect-free `@aceshooting/lyra-ui/localization.js` entry does not carry (that one
3279
- has `subscribeLyraLocaleRegistry()`, which answers a different question — see "Localization").
3323
+ `bridgeLyraLocale(options?): () => void`** — the _active-locale_ half of the locale runtime.
3324
+ **This `utilities/localization.js` entry point is deprecated**, with removal not before 23.0.0:
3325
+ import every name below from the side-effect-free `@aceshooting/lyra-ui/localization.js` entry,
3326
+ which exports the identical bindings and types. The extensionless `@aceshooting/lyra-ui/utilities`
3327
+ barrel keeps exporting them. `subscribeLyraLocale()` is distinct from
3328
+ `subscribeLyraLocaleRegistry()`, which answers a different question — see "Localization".
3280
3329
  `subscribeLyraLocale()` fires whenever the active selection changes, and when a newly registered
3281
3330
  or extended catalog can alter that selection's messages or direction. Registrations unrelated to
3282
3331
  the active lookup chain are filtered. An application can therefore re-render its **own**
@@ -3299,8 +3348,7 @@ number; clearTimeout(handle: number): void }`.
3299
3348
  Its exact options record is `LyraLocaleBridgeOptions { target?: Element; direction?: boolean }`.
3300
3349
 
3301
3350
  ```ts
3302
- import { bridgeLyraLocale } from "@aceshooting/lyra-ui/utilities/localization.js";
3303
- import { setLyraLocale } from "@aceshooting/lyra-ui/localization.js";
3351
+ import { bridgeLyraLocale, setLyraLocale } from "@aceshooting/lyra-ui/localization.js";
3304
3352
  import "@aceshooting/lyra-ui/translations/ar.js";
3305
3353
 
3306
3354
  const stop = bridgeLyraLocale(); // mirrors onto <html>
@@ -3311,13 +3359,13 @@ number; clearTimeout(handle: number): void }`.
3311
3359
  `resolveLyraScopedString(/* public names: host, key, defaults, overrides, fallback, values */):
3312
3360
  unknown` is a scoped variant of `resolveLyraString()` (see "Localization"): the same
3313
3361
  override → fallback → registered-locale-catalog resolution chain, but against a caller-supplied
3314
- `defaults` record instead of the complete `LYRA_DEFAULT_STRINGS` catalog. It lives on this
3315
- tree-shakable entry rather than the side-effect-free `@aceshooting/lyra-ui/localization.js` one so
3316
- that resolving a handful of an application's own messages never pulls in the full compatibility
3317
- catalog to do it — pass a small `defaults` object of just the keys used.
3362
+ `defaults` record instead of the complete `LYRA_DEFAULT_STRINGS` catalog, so resolving a handful
3363
+ of an application's own messages never needs the full compatibility catalog — pass a small
3364
+ `defaults` object of just the keys used. `@aceshooting/lyra-ui/localization.js` is side-effect-free,
3365
+ so a bundler drops the unused catalog; an unbundled graph that imports it still loads the catalog.
3318
3366
 
3319
3367
  ```ts
3320
- import { resolveLyraScopedString } from "@aceshooting/lyra-ui/utilities/localization.js";
3368
+ import { resolveLyraScopedString } from "@aceshooting/lyra-ui/localization.js";
3321
3369
 
3322
3370
  const label = resolveLyraScopedString(host, "save", { save: "Save" });
3323
3371
  ```
@@ -3714,6 +3762,7 @@ These named interfaces and helper signatures are available to typed integrations
3714
3762
  `ToolInvocation {
3715
3763
  id: unknown;
3716
3764
  name: unknown;
3765
+ displayName: unknown;
3717
3766
  args: unknown;
3718
3767
  status: unknown;
3719
3768
  result: unknown;
@@ -6757,8 +6806,12 @@ control, without making the control valid — see "The validity alias is cancela
6757
6806
  `label`, `next-icon`, `previous-icon`, and `start`. Lyra additionally retains `error`, which
6758
6807
  overrides `errorText`.
6759
6808
 
6760
- The editable input shrinks first in a constrained row; `start` and `end` adornments are each
6761
- capped at 40% and ellipsize unbroken content. Clear and calendar actions retain their fixed target.
6809
+ The editable input shrinks first in a constrained row. While a `start` or `end` adornment is shown
6810
+ it keeps a four-character floor, so a short adornment keeps its content width; without one it
6811
+ yields entirely to the clear and calendar actions. Adornments are sized to their content, each
6812
+ capped at 40%, and ellipsize unbroken content. Clear and calendar actions retain their fixed
6813
+ target, so a row with an adornment that is narrower than those actions plus the floor overflows
6814
+ rather than collapsing the field.
6762
6815
 
6763
6816
  **Custom states:** `blank`, `disabled`, `open`, and `range`; the shared form-associated mixin also
6764
6817
  exposes its validity states.
@@ -7358,6 +7411,13 @@ plus Shoelace aliases `prefix` → `start` and `suffix` → `end`.
7358
7411
  In a constrained button the default label ellipsizes and each adornment wrapper is capped at 40%
7359
7412
  of the control. Fixed icons remain visible while unbroken labels or metadata cannot widen the row.
7360
7413
 
7414
+ A detected icon-only label (the `icon-button` state) has no text to ellipsize, so it does not clip
7415
+ paint. A glow drawn around the glyph extends past the label box instead of being cut to a square,
7416
+ for example `gemstoneSelectedGlyphStyles`' drop-shadow halo on a `data-lr-gemstone-selected`
7417
+ wrapper. A text label keeps the truncation clip, even beside a glowing glyph. The `start`/`end`
7418
+ wrappers always clip, so put a glowing glyph in the default slot on its own. Hit area, focus ring,
7419
+ and label box are unchanged.
7420
+
7361
7421
  **CSS parts:** `base` (compatibility name for the internal control; use `button`),
7362
7422
  `button` (the internal native `<button>`, or an `<a>` when `href` resolves to a safe link; it is
7363
7423
  the same node as `base`), `label`, `start`/`prefix` (the same wrapper), `end`/`suffix` (the same
@@ -7656,7 +7716,10 @@ sibling of it, not piped through `<lr-icon>`: the internal `<lr-icon>` mounts on
7656
7716
  set, so with `icon` left empty your content is the button's only child. That is what lets a
7657
7717
  complete element — an `<svg>`, an `<img>`, an `<lr-flag>` — render at its own natural aspect ratio
7658
7718
  instead of being forced into a 1:1 box. Setting both `icon` and slotted content renders both, side
7659
- by side; that is a valid composition, not a fallback.
7719
+ by side; that is a valid composition, not a fallback. No wrapper sits between the slot and the
7720
+ native control, and that control declares no overflow clip. A glow painted around slotted content
7721
+ therefore keeps its full silhouette instead of being cut to the glyph's box. The
7722
+ `gemstoneAccentPicker` trigger's `gemstoneSelectedGlyphStyles` halo relies on this.
7660
7723
 
7661
7724
  **Bare SVG geometry fallback:** slotted bare SVG _geometry_ (`path`, `circle`, `rect`, `line`,
7662
7725
  `polygon`, `polyline`, `ellipse`, `g`, `use`) with no `icon` set and no enclosing `<svg>` of its
@@ -7948,8 +8011,15 @@ and cancellation rules. Never fires while `debounce` is unset, `0`, or non-finit
7948
8011
  `prefix` (alias of `start`), `suffix` (alias of `end`),
7949
8012
  `clear-button` (non-empty clearable `text`/`search` inputs only),
7950
8013
  `hint`/`form-control-help-text` (compatibility names on the same hint node), and `error`.
7951
- Long `start`/`end` adornments shrink and ellipsize inside their flex allocation rather than
7952
- widening a narrow field; label, hint, and error text wrap at unbroken boundaries. The
8014
+ In a constrained row the native field gives up its spare width first, so a short adornment such
8015
+ as a measurement unit (`kWh`) stays whole beside a narrow value. While a `start` or `end` adornment
8016
+ is shown the field keeps a four-character floor; only then do the adornments shrink: each is
8017
+ capped at half the row, and genuinely long adornments ellipsize inside that allocation rather than
8018
+ widening a narrow field. The floor holds against the fixed clear, password-toggle and stepper
8019
+ actions too, so a row with an adornment that is narrower than those actions plus the floor
8020
+ overflows rather than hiding the value. A row without adornments has no floor: its field still
8021
+ yields entirely to those actions. A shrink-to-fit container still sizes the field to its native
8022
+ intrinsic width. Label, hint, and error text wrap at unbroken boundaries. The
7953
8023
  `Narrow RTL (320px)` story exercises both adornments with a clear action and localized long copy.
7954
8024
 
7955
8025
  **The required marker.** `required` with a non-empty `label` paints the library's shared marker on
@@ -8229,6 +8299,10 @@ disabled state.
8229
8299
 
8230
8300
  The inherited `--lr-input-*` theme inputs keep `lr-input`'s ancestor-theme precedence; the number
8231
8301
  subclass does not redeclare them on its host.
8302
+ Beside the stepper pair it keeps `lr-input`'s constrained-row allocation: the number field gives up
8303
+ its spare width first, down to a four-character floor while a unit is shown, so a short `end` unit
8304
+ stays whole (the `Narrow Measurement Unit` story). A stepper row with a unit therefore needs room
8305
+ for the steppers plus that floor; without a unit the field still yields entirely to the steppers.
8232
8306
  It also installs the shared six-tier size sheet: stepper-bearing rows follow the same rendered
8233
8307
  action-height ladder as `lr-input` instead of remaining at the default tier for every `size`.
8234
8308
 
@@ -9305,6 +9379,136 @@ class AccentTrigger extends LitElement {
9305
9379
  tuning the gap and hit size small enough to avoid wrapping is a guess that breaks at the next
9306
9380
  swatch added.
9307
9381
 
9382
+ ### gemstoneAccentPicker — the signature accent selector
9383
+
9384
+ `gemstoneAccentPicker` names the canonical composition of `lr-icon-button`, `lr-popover`, and
9385
+ `lr-swatch-picker mode="gemstone"`. It is a **pattern name, not an exported component or tag**.
9386
+ Use it for application accent selection: a glowing current-gem trigger, one compact
9387
+ “Gemstone: selected name” caption, and all nine canonical gemstones, in `GEMSTONE_KEYS` order, in
9388
+ one row. Localize the caption, trigger, dialog, radiogroup, and item names. Omit introductory copy
9389
+ and a second selected-name paragraph.
9390
+
9391
+ The consuming Lit component imports these granular entries and lists
9392
+ `gemstoneSelectedGlyphStyles` in its `static styles` ahead of the CSS that follows.
9393
+ `components/forms/icon-button/icon-button-register.js` can replace `icon-button.js` when no icon
9394
+ button in the application uses `icon` or `src`:
9395
+
9396
+ ```ts
9397
+ import '@aceshooting/lyra-ui/components/forms/icon-button/icon-button.js';
9398
+ import '@aceshooting/lyra-ui/components/overlays/overlay/popover.js';
9399
+ import '@aceshooting/lyra-ui/components/forms/swatch-picker/swatch-picker.js';
9400
+ import {
9401
+ GEMSTONE_KEYS,
9402
+ GEMSTONES,
9403
+ gemstoneGlyph,
9404
+ gemstoneSelectedGlyphStyles,
9405
+ type GemstoneKey,
9406
+ } from '@aceshooting/lyra-ui/theme/gemstones.js';
9407
+ ```
9408
+
9409
+ `--accent-color`, `--caption-color`, and `--text-color` are application-owned theme tokens.
9410
+ `--lr-overlay-max-inline-size` lifts the popup's default 20rem cap so the 20rem palette plus the
9411
+ popover's own padding fits. Both `--lr-gemstone-selected-*` values reach the trigger glyph and the
9412
+ picker's checked glyph alike: a 0.42rem halo at 92% of the accent. Leave
9413
+ `--lr-gemstone-selected-shine-duration` unset to keep the default 1.8s shine. The
9414
+ `--lr-icon-button-*` values keep the trigger free of a rectangular fill at rest, on hover, and
9415
+ while pressed:
9416
+
9417
+ ```css
9418
+ .gemstone-accent-picker {
9419
+ --lr-overlay-max-inline-size: 22rem;
9420
+ --lr-gemstone-selected-color: color-mix(in srgb, var(--accent-color) 92%, transparent);
9421
+ --lr-gemstone-selected-blur: .42rem;
9422
+ }
9423
+ .gemstone-accent-picker lr-icon-button {
9424
+ --lr-icon-button-background: transparent;
9425
+ --lr-icon-button-background-hover: transparent;
9426
+ --lr-icon-button-background-active: transparent;
9427
+ --lr-icon-button-border: none;
9428
+ }
9429
+ .gem { display: inline-flex; inline-size: 1.15rem; block-size: 1.15rem; }
9430
+ .gem svg { inline-size: 100%; block-size: 100%; }
9431
+ .palette {
9432
+ box-sizing: border-box;
9433
+ display: flex;
9434
+ flex-direction: column;
9435
+ gap: .15rem;
9436
+ inline-size: 20rem;
9437
+ padding: .3rem;
9438
+ }
9439
+ .heading {
9440
+ margin: .2rem .3rem .5rem;
9441
+ color: var(--caption-color);
9442
+ font-size: .75rem;
9443
+ font-weight: 600;
9444
+ }
9445
+ .name { color: var(--text-color); }
9446
+ .palette lr-swatch-picker {
9447
+ padding: 0 .3rem .3rem;
9448
+ --lr-swatch-picker-hit-size: 1.75rem;
9449
+ --lr-swatch-picker-wrap: nowrap;
9450
+ --lr-swatch-picker-gap: .25rem;
9451
+ }
9452
+ @media (max-width: 30rem) {
9453
+ .palette { inline-size: auto; }
9454
+ .palette lr-swatch-picker {
9455
+ --lr-swatch-picker-hit-size: 1.5rem;
9456
+ --lr-swatch-picker-gap: .125rem;
9457
+ }
9458
+ }
9459
+ ```
9460
+
9461
+ At the default 16px root font size, `1.75rem`/`.25rem` give 28px targets with 4px gaps, and
9462
+ `1.5rem`/`.125rem` give 24px targets with 2px gaps. Below 30rem the palette shrinks to its row, so
9463
+ the popup fits a 320px viewport without scrolling. A `calc(100dvw - 2.5rem)` palette overflows the
9464
+ popup by 2px there. Apply `data-lr-gemstone-selected` to the transparent glyph wrapper, not the
9465
+ whole trigger, so the halo follows the gem's silhouette. The focus ring belongs to keyboard focus;
9466
+ do not add a permanent circular selection border. Leave the popover's default content padding and
9467
+ arrow intact.
9468
+
9469
+ This Lit template assumes `selected` is a `GemstoneKey`. `accentLabel` names the dialog and the
9470
+ radiogroup. `triggerLabel(name)` (“Accent color: Ruby”), `captionLabel` (“Gemstone:”, with the
9471
+ locale's own separator), and `translateGemstone()` come from the application's catalog.
9472
+ `onAccentChange()` validates the string and updates application state:
9473
+
9474
+ ```ts
9475
+ // Build once per locale. A new array on every render re-creates every swatch and briefly blurs
9476
+ // the focused one.
9477
+ const items = GEMSTONE_KEYS.map((key) => ({
9478
+ value: key,
9479
+ color: GEMSTONES[key].fill,
9480
+ label: translateGemstone(key),
9481
+ gemstone: key,
9482
+ }));
9483
+
9484
+ const name = translateGemstone(selected);
9485
+ html`
9486
+ <lr-popover class="gemstone-accent-picker" placement="bottom-end"
9487
+ popup-role="dialog" aria-label=${accentLabel}>
9488
+ <lr-icon-button slot="trigger" label=${triggerLabel(name)}>
9489
+ <span class="gem" data-lr-gemstone-selected aria-hidden="true">${gemstoneGlyph(GEMSTONES[selected].fill)}</span>
9490
+ </lr-icon-button>
9491
+ <div class="palette">
9492
+ <p class="heading">${captionLabel} <span class="name">${name}</span></p>
9493
+ <lr-swatch-picker mode="gemstone" aria-label=${accentLabel}
9494
+ .items=${items} .value=${selected}
9495
+ @lr-change=${(event: CustomEvent<{ value: string }>) => onAccentChange(event.detail.value)}>
9496
+ </lr-swatch-picker>
9497
+ </div>
9498
+ </lr-popover>
9499
+ `;
9500
+ ```
9501
+
9502
+ The parts already supply the behavior, so do not re-implement it. The picker is a roving-tabindex
9503
+ `radiogroup` with `aria-checked` and a `:focus-visible` ring per swatch, and its Arrow keys swap
9504
+ under RTL. The popover keeps `bottom-end` on the logical end under RTL and returns focus to the
9505
+ trigger on Escape. `gemstoneSelectedGlyphStyles` stops the shine under
9506
+ `prefers-reduced-motion: reduce`. Verify 320px and desktop layouts, enlarged text, and both color
9507
+ modes. In a sidebar or settings panel, the caption and row may appear inline with the same glow
9508
+ and spacing; use the compact tier when the container is narrow and allow wrapping if even that
9509
+ tier cannot fit. Never shrink targets below 24px. Application theme application, default accent,
9510
+ validation, and persistence remain caller-owned.
9511
+
9308
9512
  ---
9309
9513
 
9310
9514
  ## `lr-checkbox`
@@ -11487,8 +11691,9 @@ errors and own/fieldset validation barring), `lr-submit` (`detail: { value, item
11487
11691
  (`detail: { itemId }`, `skippable` only). `lr-invalid` (no detail) is the one bubbling/composed,
11488
11692
  cancelable alias emitted when the complete rubric fails a native validity check; preventing it also
11489
11693
  suppresses the native event's default validation UI.
11490
- Native and prefixed input/change events from the composed child controls are contained; consumers
11491
- above the rubric receive only the rubric-owned aggregate event shape.
11694
+ Native and prefixed input/change events from the composed child controls are contained, as is a
11695
+ multiple-category checkbox group's `lr-checkbox-group-toggle-request` proposal; consumers above the
11696
+ rubric receive only the rubric-owned aggregate event shape.
11492
11697
 
11493
11698
  **Methods:** `getForm()` returns the owning form. `setCustomValidity(message)` sets or clears a
11494
11699
  form-level error no per-key rule can
@@ -12319,14 +12524,17 @@ space-separated samples restores the attribute-driven path.
12319
12524
  **Slots:** none.
12320
12525
 
12321
12526
  **CSS parts:** `sparkline` and deprecated `base` are aliases on the same outer SVG, `fill` is the
12322
- area path for solid/gradient appearance, `line` is the stroke path. Additive aliases are `area` on
12323
- the same fill path and `bar` on each extension-mode rectangle.
12527
+ area path for solid/gradient appearance, `line` is the stroke path, and the additive `bar` is each
12528
+ extension-mode rectangle. `area` is a deprecated alias of `fill` on the same path (removal not
12529
+ before 23.0.0); use `::part(fill)`.
12324
12530
 
12325
12531
  **Themeable custom properties:** `--fill-color` (area/gradient stop color), `--line-color` (stroke
12326
12532
  color), and `--line-width` (stroke width). Each reads through the live CSS cascade; line/fill
12327
12533
  default to the selected `trend`'s semantic Lyra tokens and `--line-width` falls back through the
12328
- compatibility `--lr-sparkline-stroke-width` to `--lr-border-width-medium`. No canvas bridge or
12329
- manual refresh is needed.
12534
+ deprecated `--lr-sparkline-stroke-width` (removal not before 23.0.0) to `--lr-border-width-medium`.
12535
+ Replace that alias with `--line-width` scoped to the element (`lr-sparkline { --line-width: 2px; }`),
12536
+ since the unprefixed name also reaches any other element that reads it. No canvas bridge or manual
12537
+ refresh is needed. Styling hooks cannot be observed, so neither deprecated alias logs a warning.
12330
12538
 
12331
12539
  **Optional peer deps:** none.
12332
12540
 
@@ -12446,8 +12654,9 @@ value: string; readonly exactValue?: string }`; at most the first 10,000 rows ar
12446
12654
 
12447
12655
  **Events:** none.
12448
12656
 
12449
- **Slots:** `start` (canonical leading icon), default (legacy leading-icon alias, retained as the
12450
- fallback; `start` takes precedence when both are filled), `caption` (rich caption content —
12657
+ **Slots:** `start` (canonical leading icon), default (deprecated leading-icon alias, removal not
12658
+ before 23.0.0: it still renders while `start` is empty and `start` takes precedence when both are
12659
+ filled; move the icon to `slot="start"`), `caption` (rich caption content —
12451
12660
  overrides the `caption` attribute when slotted content is provided), `spark` (a sparkline, e.g. `<lr-sparkline
12452
12661
  slot="spark">`, or other compact trend visual — `lr-stat` only reserves the slot and doesn't
12453
12662
  render one itself), `sub` (rich sub-line content — overrides the `sub` attribute when slotted content
@@ -15623,7 +15832,10 @@ owns none of that.
15623
15832
 
15624
15833
  **Slots:** default (body content), `icon` (leading header glyph), `header` (replaces the built-in
15625
15834
  heading row entirely), `toolbar` (action row at the block-end edge; revealed by hover/focus on
15626
- hover-capable devices and always visible with a coarse pointer or no hover).
15835
+ hover-capable devices and always visible with a coarse pointer or no hover; it also stays revealed
15836
+ while an `lr-dropdown`, `lr-popover` or `lr-context-menu` opened from it is open, because that menu
15837
+ sits in the browser top layer, where Chromium and WebKit stop matching `:hover`/`:focus-within` on
15838
+ the node).
15627
15839
 
15628
15840
  **CSS parts:** `base` (the row wrapping the input handles, the card and the output handles — it
15629
15841
  carries no card chrome of its own), `card` (the bordered, filled node card), `header`, `icon`,
@@ -16398,9 +16610,9 @@ button, while `error` is set.
16398
16610
  selection stays silent; every post-mount `selectedDocumentIds` change appends the localized selected count
16399
16611
  to the document's shared light-DOM polite sink, including zero and repeated equal counts.
16400
16612
  Internal search, tag-filter, and checkbox native `input`/`change` plus prefixed `lr-input`/
16401
- `lr-change` aliases, the tag combobox's lifecycle/filter/clear/invalid events, table pagination and
16402
- priority-column visibility events, and the table's click-anywhere selection event stop at the
16403
- component's translation boundary. The table is still in multiple-selection semantics so
16613
+ `lr-change` aliases, the checkboxes' `lr-checkbox-toggle-request` proposals, the tag combobox's
16614
+ lifecycle/filter/clear/invalid events, table pagination and priority-column visibility events, and
16615
+ the table's click-anywhere selection event stop at the component's translation boundary. The table is still in multiple-selection semantics so
16404
16616
  `selectedDocumentIds` reaches row `aria-selected`; document selection itself remains checkbox-owned, while
16405
16617
  row activation opens the document. Listen for the document-library events above; one interaction
16406
16618
  emits one documented host contract without also leaking a composed child event.
@@ -17725,9 +17937,11 @@ custom-handle content inside the separator; its assigned subtree is inert, so th
17725
17937
  the sole pointer/keyboard resize control). Under RTL, logical start/end and horizontal pointer/arrow
17726
17938
  behavior mirror together; vertical behavior does not invert.
17727
17939
 
17728
- **CSS parts:** `base split-panel` (both tokens are on the same outer wrapper), `start panel` and
17940
+ **CSS parts:** `base split-panel` (both tokens are on the same outer wrapper; `split-panel` is a
17941
+ deprecated alias of `base`, removal not before 23.0.0, so style `::part(base)`), `start panel` and
17729
17942
  `end panel` (each pane exposes its individual token plus the shared `panel` token), `divider`
17730
- (focusable `role="separator"`, with value/min/max and disabled ARIA state).
17943
+ (focusable `role="separator"`, with value/min/max and disabled ARIA state). The deprecated part
17944
+ still matches until its removal; being a stylesheet selector, it logs no runtime warning.
17731
17945
 
17732
17946
  **Themeable custom properties:** `--divider-width` (default `4px`), `--divider-hit-area` (requested
17733
17947
  default `12px`, with Lyra's `--lr-icon-button-size` minimum target remaining the floor), `--min`
@@ -19683,9 +19897,10 @@ letting a consumer that syncs app chrome to the rail's mode pick up the restored
19683
19897
  the host's re-render has applied. That second pass only acts while focus is still inside the rail
19684
19898
  or has fallen to `<body>` — focus moved elsewhere in the meantime is never taken back — and
19685
19899
  focuses the first candidate that can actually hold focus: the trigger, then the element that held
19686
- focus when the overlay opened, then the built-in `[part='toggle']` (unavailable under
19687
- `hideToggle`), and finally the rail host; when even the host cannot take focus (hidden, or inert —
19688
- under an inert ancestor or behind a stacked modal), focus is left where it is. The host
19900
+ focus when the overlay opened (none when focus was on `<body>`), then the built-in
19901
+ `[part='toggle']` (unavailable under `hideToggle`), then the rail host, and finally
19902
+ `focusFallback` when set; when none of them can take focus (the host hidden, or inert — under an
19903
+ inert ancestor or behind a stacked modal), focus is left where it is. The host
19689
19904
  temporarily receives `tabindex="-1"` only when it has no authored tabindex; the temporary
19690
19905
  attribute is removed once focus moves off the host or the rail disconnects, while a blur caused
19691
19906
  only by the window or tab losing system focus (the host stays the focused element) leaves it in
@@ -19698,7 +19913,8 @@ letting a consumer that syncs app chrome to the rail's mode pick up the restored
19698
19913
  `null`), no external element is a return candidate: a close still returns focus to the built-in
19699
19914
  toggle when its own click opened the overlay, otherwise to the element that held focus at open,
19700
19915
  and the deferred pass still runs over its remaining candidates (that element, the built-in toggle,
19701
- then the rail host) — only the trigger candidate depends on the association. The resolved trigger
19916
+ the rail host, then `focusFallback`) — only the trigger candidate depends on the association. The
19917
+ resolved trigger
19702
19918
  also receives `aria-expanded` (rendered in both states) and `aria-controls` pointing at the rail's
19703
19919
  own panel, so an external control announces the overlay's state across the shadow boundary.
19704
19920
  Because a light-DOM element cannot hold a raw reference into another element's shadow tree,
@@ -19710,6 +19926,29 @@ letting a consumer that syncs app chrome to the rail's mode pick up the restored
19710
19926
  label/`htmlFor`-style alternative to assigning `trigger` directly (mirrors `<lr-page-rail>`'s
19711
19927
  `for`). Resolved against this element's own root (shadow root or document) when the overlay opens
19712
19928
  and again when it closes. Ignored once `trigger` is itself set.
19929
+ - `focusFallback: HTMLElement | string | null = null` (attribute `focus-fallback`) — opt-in
19930
+ last-resort target for the mobile overlay's deferred focus return, tried only after every built-in
19931
+ target (the `trigger`/`for` association, the element that held focus at open, `[part='toggle']`,
19932
+ the rail host) is unavailable — typically the application's main region, for a host that takes the
19933
+ navigation, or the region around it, out of the layout as the overlay closes. Accepts an element or
19934
+ an id, the same element-or-id shape as `<lr-intersection-observer>`'s `root`; an id (the attribute
19935
+ always holds one — an id, not a selector) is resolved like `for`, against the rail's own root
19936
+ (shadow root or document), and `''` means unset. Resolved when the deferred pass runs, so
19937
+ reassigning it after opening, or re-creating the element under the same id before the pass runs,
19938
+ is honored; property and attribute writes set the same value and the last write wins. It never
19939
+ outranks a target that can take focus, so it changes no close that already lands, and it is not
19940
+ used by the synchronous attempt, by a close caused by leaving `'mobile'` (a breakpoint or
19941
+ `forceMode` change), or in the full/icon-only presentations. A target that is missing,
19942
+ disconnected, in another document, hidden, inert or under an inert ancestor, `aria-hidden`,
19943
+ disabled, or not focusable leaves focus where it is, and so does anything inside this rail
19944
+ (unavailable whenever the rail host is). The target needs `tabindex="-1"` (or another way to take
19945
+ focus): unlike the host, the rail never adds a tabindex to it. Unset (`null`) reproduces the
19946
+ existing behavior exactly.
19947
+
19948
+ ```html
19949
+ <main id="content" tabindex="-1">…</main>
19950
+ <lr-app-rail hide-toggle for="menu-button" focus-fallback="content">…</lr-app-rail>
19951
+ ```
19713
19952
  - `resizable: boolean = false` (reflected) — opts a continuously draggable width in for the `'full'`
19714
19953
  state, exposing a `[part='resizer']` handle clamped to `[minRailWidthPx, maxRailWidthPx]`. `false`
19715
19954
  (the default) renders no resizer and leaves the fixed-width `--lr-app-rail-width` CSS token
@@ -20516,11 +20755,14 @@ is neutral. This is also the composition used for direct `<lr-dropdown-item>` ch
20516
20755
  Preventing it anywhere keeps the current menu/submenu chain open. There are no
20517
20756
  `lr-menu-select`, `lr-menu-item-select`, or nested-selection aliases.
20518
20757
 
20519
- **Slots:** default (`<lr-menu-item>`/`<lr-dropdown-item>` plus semantic separators), `header`, and
20520
- `footer`. Header and footer are composed controls or explanatory regions outside the
20521
- `role="menu"` list, so filters, counts, or footer actions do not violate the menu required-child
20522
- contract. Arbitrary non-item content in the default slot still renders, but is not enrolled as a
20523
- menu item.
20758
+ **Slots:** default (`<lr-menu-item>`/`<lr-dropdown-item>` rows, `<lr-menu-label>` captions, and
20759
+ `<hr>`/`<lr-divider>` separators), `header`, and `footer`. Header and footer are composed controls
20760
+ or explanatory regions outside the `role="menu"` list, so filters, counts, or footer actions do not
20761
+ violate the menu required-child contract. Any other default-slot content is deprecated (removal not
20762
+ before 23.0.0): it still renders, but inside `role="menu"` without a menu-item role and skipped by
20763
+ keyboard navigation, so move it to `slot="header"` or `slot="footer"`. Content that `lr-dropdown`
20764
+ forwards into its own menu is not covered: its default slot mirrors `sl-dropdown`'s free-form
20765
+ content.
20524
20766
 
20525
20767
  **CSS parts:** `header`, `list`, and `footer`. Root `trigger` and `popup` parts do not exist;
20526
20768
  style those on `<lr-dropdown>` when using the overlay composition.
@@ -23521,8 +23763,8 @@ that override no longer decides anything for their modal instances and can be dr
23521
23763
  still resolves the `z-index` in their stylesheet, but only as the fallback for a user agent without
23522
23764
  popover support. A `contained` drawer is deliberately nonmodal and is not promoted. The token keeps
23523
23765
  doing real work everywhere else it is used: `lr-popover`, `lr-dropdown` and `lr-tooltip` are not
23524
- promoted unless trapped (below), and otherwise go on stacking at `--lr-overlay-stack-index`,
23525
- falling back to `--lr-layer-popover`.
23766
+ promoted unless trapped (below) or, for `lr-popover` and `lr-dropdown`, opted in with `top-layer`,
23767
+ and otherwise go on stacking at `--lr-overlay-stack-index`, falling back to `--lr-layer-popover`.
23526
23768
 
23527
23769
  **Anchored overlays and the top layer.** An anchored overlay that resolves the `fixed` strategy
23528
23770
  (dropdowns, popovers, tooltips, selects, comboboxes, date/time inputs, colour pickers, submenus,
@@ -23546,6 +23788,29 @@ at UI scale with either strategy. The full-viewport modal surfaces that are not
23546
23788
  `lr-widget`, the `lr-responsive-panel` overlay and `lr-tour`'s spotlight) still stack with
23547
23789
  `z-index`: do not place them under a transformed, filtered or contained ancestor.
23548
23790
 
23791
+ An ancestor that is only a **stacking context** does not trap an overlay, so it is not promoted: a
23792
+ popover inside a `position: fixed` (or `sticky`) header with `z-index: 1000` still paints beneath a
23793
+ sibling surface at `z-index: 1100`, and no `z-index` on the popup can lift it out of the header's
23794
+ context. For that case `lr-popover` and `lr-dropdown` take an explicit opt-in, `top-layer`: the open
23795
+ popup is always shown in the browser top layer, placed `fixed`, with anchoring, the arrow, RTL
23796
+ placement, focus, Escape, light dismiss and the transitions unchanged, and no DOM node moved.
23797
+ Stacking contexts are deliberately not detected automatically: every `fixed` or `sticky` ancestor
23798
+ creates one, and so does almost every `z-index`ed, translucent (`opacity`) or `isolation: isolate`d
23799
+ one; most of them never cover the overlay, and promoting through each would lift overlays above
23800
+ layers that pages order on purpose (including their own toasts and sticky chrome). Whether a
23801
+ stacking context actually hides an overlay depends on sibling layering elsewhere on the page, which
23802
+ only the page knows, so the page opts in per instance.
23803
+
23804
+ ```html
23805
+ <header style="position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 1000">
23806
+ <lr-popover top-layer placement="bottom-start">
23807
+ <button slot="trigger">Account</button>
23808
+ <nav aria-label="Account"><a href="/profile">Profile</a></nav>
23809
+ </lr-popover>
23810
+ </header>
23811
+ <div class="search-panel" style="position: fixed; z-index: 1100">…</div>
23812
+ ```
23813
+
23549
23814
  **Initial focus.** An `[autofocus]` element anywhere in the slotted content takes focus when the
23550
23815
  overlay opens — including one inside a slotted custom element's own open shadow root, so
23551
23816
  `<lr-input autofocus>` behaves like `<input autofocus>`. With nothing marked, a modal (`lr-dialog`,
@@ -25012,6 +25277,16 @@ If the import fails, leave the native disclosure visible and usable.
25012
25277
  escapes transformed, filtered or contained ancestors by promoting the popup into the browser top
25013
25278
  layer where the native Popover API exists; otherwise, as before, such an ancestor contains and
25014
25279
  clips it (see **Anchored overlays and the top layer**).
25280
+ - `topLayer: boolean = false` (attribute `top-layer`, reflected) — always shows the open popup in
25281
+ the browser top layer where the native Popover API exists, so it paints above every page layer
25282
+ whatever the stacking contexts around it: the fix for a popover in a `z-index`ed fixed or sticky
25283
+ header, toolbar or rail that a sibling surface stacked higher would otherwise cover. While set the
25284
+ popup is placed with the `fixed` strategy whatever `positioning-strategy` resolves to (the
25285
+ property still reads back its own value). Anchoring, the arrow, RTL placement, focus, Escape,
25286
+ light dismiss, the hover bridge and the show/hide transition are unchanged, because no DOM node
25287
+ moves; the popup stays promoted through its hide transition and leaves the top layer once it
25288
+ settles closed. Changes apply live while open. Without native Popover API support the popup keeps
25289
+ its ordinary `z-index` stacking. Unset, nothing changes: only a trapping ancestor promotes it.
25015
25290
  - `trigger: string = 'click'` — a _space-separated_ list of `click` (the shipped behaviour),
25016
25291
  `hover`, `focus` and `manual`, spelled exactly the way `<lr-tooltip>`'s `trigger` is, so
25017
25292
  `trigger="hover focus"` means the same thing on both. `LyraPopoverTrigger` is the type of one
@@ -25541,6 +25816,12 @@ its surface.
25541
25816
  inherits `<lr-popover>`'s `trigger`/`showDelay`/`hideDelay`/`hoverBridge`, and honors the
25542
25817
  cascading `--lr-positioning-strategy` custom property ahead of this mirrored `absolute` default
25543
25818
  when neither spelling is authored on the instance.
25819
+ - `topLayer: boolean = false` (attribute `top-layer`, reflected) — inherited from `<lr-popover>`:
25820
+ always shows the open menu in the browser top layer, placed `fixed` whatever
25821
+ `positioning-strategy`/`hoist` say, so a dropdown in a `z-index`ed fixed header opens above a
25822
+ sibling surface stacked higher. Submenus of a promoted menu stay above the sibling surface too.
25823
+ Unset, a dropdown is promoted only when a trapping ancestor forces it (see **Anchored overlays
25824
+ and the top layer**).
25544
25825
  - `containingElement?: HTMLElement` (property only) — an external element that counts as inside for
25545
25826
  light-dismiss handling.
25546
25827
  - `arrow`, `withoutArrow` (`without-arrow`), `arrowPlacement`, `arrowPadding`, and `accessibleLabel`
@@ -27229,11 +27510,16 @@ adds no layout.
27229
27510
  needed; reflected), `attr: string | null = null` (reflected; `*` observes every attribute; otherwise a
27230
27511
  space-separated filter), `attrOldValue: boolean = false` (`attr-old-value`), `charData: boolean =
27231
27512
  false` (`char-data`), and `charDataOldValue: boolean = false` (`char-data-old-value`); all four
27232
- mapped attributes reflect. Lyra's
27233
- existing aliases remain and also reflect: `observeAttributes` (`attributes`; unfiltered boolean
27234
- form of `attr`, equivalent to `attr: '*'`) and `characterData` (`character-data`, equivalent to
27235
- `charData`). Plus `subtree: boolean = true`, and programmatic `attributeFilter: string[] = []`
27236
- (neither reflects).
27513
+ mapped attributes reflect. Plus `subtree: boolean = true`, and programmatic
27514
+ `attributeFilter: string[] = []` (neither reflects).
27515
+
27516
+ **Deprecated aliases** (still functional and still reflecting until their removal, no earlier than
27517
+ 23.0.0; setting either logs a one-time development warning): `observeAttributes` (`attributes`)
27518
+ turns on attribute observation, and `characterData` (`character-data`) is identical to `charData`.
27519
+ Replace `character-data` with `char-data`. Replace `attributes` with `attr="*"` only when `attr`,
27520
+ `attr-old-value` and `attributeFilter` are all unset; otherwise just remove it. Those already turn
27521
+ attribute observation on, and a non-null `attr` ignores `attributeFilter`, so adding `attr="*"` next
27522
+ to a filter would widen the observer to every attribute.
27237
27523
 
27238
27524
  The browser capability is optional. A missing or throwing owner-window lookup, or an unavailable or
27239
27525
  throwing constructor, leaves that rebuild inert rather than leaking an exception. Individual
@@ -28091,9 +28377,14 @@ Pairs with `lr-icon-button` (see `llms/components/lr-icon-button.md`).
28091
28377
  `flip`, `flip-360`, `shake`, `spin`, `spin-pulse`, `spin-reverse`, `spin-snap`, `spin-snap-4`,
28092
28378
  `spin-snap-8`, `buzz`, `wag`, `float`, `swing`, or `jello`. Every treatment stops under
28093
28379
  `prefers-reduced-motion: reduce`.
28094
- - `fixedWidth: boolean = false` (attribute `fixed-width`, reflected) — widens the icon _box_ to
28095
- `--lr-icon-fixed-width` while the glyph keeps `--lr-icon-size` and centres inside it, so a column
28096
- of differently-shaped icons lines its labels up.
28380
+ - `fixedWidth: boolean = false` (attribute `fixed-width`, reflected, deprecated; removal not before
28381
+ 23.0.0) — widens the icon _box_ to `--lr-icon-fixed-width` while the glyph's svg is pinned to
28382
+ `--lr-icon-size` (1em by default) and centres inside it, so a column of differently-shaped icons
28383
+ lines its labels up. It keeps working unchanged, and setting it logs a one-time development
28384
+ warning. Omit it: the default canvas already gives every icon the same 1.25em box. For the wider
28385
+ 1.5em box set `lr-icon { inline-size: var(--lr-size-1-5em); }` on the host instead. One geometry
28386
+ difference: a glyph wider than 1em keeps its intrinsic width (up to the box) instead of being
28387
+ squeezed to 1em.
28097
28388
 
28098
28389
  **Events:**
28099
28390
 
@@ -28128,8 +28419,9 @@ source tree.
28128
28419
  - `--lr-icon-size` (unset by default) — when supplied, overrides both canvas dimensions. Without
28129
28420
  it, the selected `canvas` uses the font-relative sizes above. Stroke color is `currentColor` and
28130
28421
  the host is `color: inherit`, so color comes from surrounding text with no configuration.
28131
- - `--lr-icon-fixed-width` (default `--lr-size-1-5em`) — inline size of the box while `fixed-width`
28132
- is set.
28422
+ - `--lr-icon-fixed-width` (default `--lr-size-1-5em`; deprecated together with `fixed-width`,
28423
+ removal not before 23.0.0) — inline size of the box while `fixed-width` is set. Set `inline-size`
28424
+ on the host instead.
28133
28425
  - `--lr-icon-rotate` (default `0deg`), `--lr-icon-flip-x` and `--lr-icon-flip-y` (default `1` each) —
28134
28426
  the transform inputs. `--lr-icon-rotate` is written inline from the `rotate` property and the flip
28135
28427
  factors are set to `-1` by `flip`, so set those properties rather than these tokens.
@@ -28197,7 +28489,7 @@ unregisterIconLibrary("material"); // icons using it revert to the built-in glyp
28197
28489
  name="star"
28198
28490
  label="Favourite"
28199
28491
  ></lr-icon>
28200
- <lr-icon library="material" name="delete" fixed-width></lr-icon>
28492
+ <lr-icon library="material" name="delete"></lr-icon>
28201
28493
  <lr-icon name="search" canvas="square" animation="beat"></lr-icon>
28202
28494
  ```
28203
28495
 
@@ -29071,7 +29363,15 @@ Also exported from the package root:
29071
29363
  `languageToCountry(language: string): string | undefined` and the `LANGUAGE_TO_COUNTRY` lookup
29072
29364
  table (region subtag wins, e.g. `en-US` → `us`; plain `en` → `gb`; override the table per-app if you
29073
29365
  need different defaults). Only the table's own entries are eligible for base-language fallback;
29074
- inherited object members such as `constructor` resolve to `undefined`. Also exported:
29366
+ inherited object members such as `constructor` resolve to `undefined`. A region-less tag whose
29367
+ script subtag implies a different likely region than its bare language takes that region instead of
29368
+ the table default, so Traditional and Simplified Chinese never share a flag: `zh-Hant` → `tw` while
29369
+ `zh` and `zh-Hans` → `cn`. The rule is not specific to Chinese: `zh-Hanb` and `zh-Bopo` → `tw`, and
29370
+ `kk-Arab` (Kazakh in Arabic script, as written in China) → `cn` rather than the `kk` default of `kz`;
29371
+ pass `country` when a different flag is wanted. A script that implies the language's own likely
29372
+ region keeps the table's convention (`en-Latn` → `gb`), a language with no table entry stays
29373
+ unresolved, and an engine without `Intl.Locale` likely-subtags data uses the table default. Also
29374
+ exported:
29075
29375
  `localeNativeName(tag: string): string`.
29076
29376
 
29077
29377
  `localeNativeName()` returns a BCP-47 tag's **endonym** — the locale's name written in that locale
@@ -29345,7 +29645,16 @@ modes use the same expression semantics at initial render and after a theme chan
29345
29645
  **Properties:**
29346
29646
 
29347
29647
  - `center: [number, number] = [0, 0]`
29348
- - `zoom: number = 2`
29648
+ - `zoom: number = 2` — `center` and `zoom` are the declarative camera. Assigning either moves the
29649
+ map without emitting anything. When the camera settles after a user gesture or `fitBounds()`, the
29650
+ element writes that camera into both and reports it through `lr-map-view-change`, so its next
29651
+ update never resets it, and a later, different assignment still moves the map. Assigning a value
29652
+ the map already shows moves nothing, so echoing a reported camera back, however late, never stops
29653
+ an animation or gesture. An assignment that does interrupt one (a fit still animating, a
29654
+ gesture's inertia) applies the whole declared camera, `center` and `zoom` alike, so the axis you
29655
+ did not assign is not left wherever the interrupted movement stopped; with nothing in flight,
29656
+ only the assigned axis moves. Because both track the settled camera, a map rebuilt after a
29657
+ disconnect/reconnect opens at the last settled camera rather than the originally declared one.
29349
29658
  - `renderWorldCopies?: boolean` (attribute: false) — forwarded to MapLibre when its map is
29350
29659
  constructed. Leave it unset to preserve MapLibre's own current default; set `false` before
29351
29660
  construction to stop repeating the world horizontally. This is a construction-time option, so a
@@ -29760,6 +30069,73 @@ shared `maplibre-gl` import without constructing a map or allocating a WebGL con
29760
30069
  `false` when the peer is unavailable, allowing an application to choose a fallback before connecting
29761
30070
  an element.
29762
30071
 
30072
+ `fitBounds(bounds: LyraMapBounds, options?: LyraMapFitBoundsOptions): boolean` frames a box
30073
+ `[[west, south], [east, north]]` in the viewport: the way to show every current marker, or a
30074
+ selected region, whenever the data set changes. Use it instead of calling the peer's own
30075
+ `fitBounds()` through `map`, which the element neither knows about nor reports. Once the camera
30076
+ settles, the element writes it into `center`/`zoom` and emits `lr-map-view-change` with
30077
+ `source: 'fit'`, so declarative state stays in sync and the element never resets the fitted camera.
30078
+
30079
+ - `options.padding` — screen pixels kept clear around the box: one number for every side, or a
30080
+ `LyraMapPadding { top?, right?, bottom?, left? }`. The sides are the viewport's physical sides in
30081
+ both text directions, because the map's projected space is physical. A negative or non-finite
30082
+ side counts as `0`. Defaults to `0`.
30083
+ - `options.maxZoom` — the highest zoom the fit may reach, so one marker or a tight cluster does not
30084
+ zoom to the street. Clamped into `[0, 22]`; a non-finite value sets no ceiling.
30085
+ - `options.animate` — defaults to `true`. The camera never animates under
30086
+ `prefers-reduced-motion: reduce`.
30087
+ - Latitudes lie in `[-90, 90]`, with `south <= north`, and longitudes in `[-180, 180]`. A box
30088
+ crossing the antimeridian takes either of MapLibre's forms: `west > east`, or `east` unwrapped
30089
+ past 180, up to one full turn east of `west` (`[[170, -10], [190, 10]]`, the form MapLibre's
30090
+ `LngLatBounds` produces). A single point is a valid zero-size box; pair it with `maxZoom`.
30091
+ Anything else (non-finite or non-number values, out-of-range coordinates, an inverted latitude
30092
+ span, the wrong shape) returns `false` without moving or queueing anything, with a
30093
+ development-mode warning.
30094
+ - Callable before the map exists, whether the peer is still loading or an off-screen map has not
30095
+ been constructed yet. The most recent request is queued and applied without animation right
30096
+ after construction, unless `center` or `zoom` has been given a different value since the call.
30097
+ Re-assigning the value already held (a framework re-render building an equal array, say) keeps
30098
+ the queued fit.
30099
+ - A `center`/`zoom` assignment made while a fit is still animating wins: the interrupted fit is
30100
+ neither written back nor reported, and the camera lands on the whole declared `center`/`zoom`.
30101
+ - Returns `true` once the fit is running or queued, and `false` when the loaded peer cannot fit a
30102
+ box. When the padded box cannot fit the map's current size (padding wider or taller than the
30103
+ map), MapLibre leaves the camera where it is and logs its own warning: the call still returns
30104
+ `true`, and no `lr-map-view-change` fires.
30105
+
30106
+ There is deliberately no declarative `bounds` property. `center` and `zoom` already describe the
30107
+ camera; a third camera input would need a precedence rule against them, and a framework that
30108
+ rebuilds its arrays on every render would refit on every render, pulling the camera away from a user
30109
+ who has just panned. A one-shot command whose result lands in `center`/`zoom` keeps one camera input.
30110
+
30111
+ ```ts
30112
+ import type {
30113
+ LyraMap,
30114
+ LyraMapBounds,
30115
+ LyraMapMarker,
30116
+ LyraMapViewChangeDetail,
30117
+ } from '@aceshooting/lyra-ui/components/media/map/map.class.js';
30118
+
30119
+ let camera: LyraMapViewChangeDetail | undefined;
30120
+ const mapEl = document.querySelector('lr-map') as LyraMap;
30121
+ mapEl.addEventListener('lr-map-view-change', (event) => {
30122
+ // Keep the host's own camera state in step: { center, zoom, source }.
30123
+ camera = event.detail;
30124
+ });
30125
+
30126
+ function showMarkers(markers: LyraMapMarker[]): void {
30127
+ mapEl.markers = markers;
30128
+ if (markers.length === 0) return;
30129
+ const lngs = markers.map((marker) => marker.lngLat[0]);
30130
+ const lats = markers.map((marker) => marker.lngLat[1]);
30131
+ const box: LyraMapBounds = [
30132
+ [Math.min(...lngs), Math.min(...lats)],
30133
+ [Math.max(...lngs), Math.max(...lats)],
30134
+ ];
30135
+ mapEl.fitBounds(box, { padding: 48, maxZoom: 14 });
30136
+ }
30137
+ ```
30138
+
29763
30139
  **Events:** `lr-map-load` (fired once, after the underlying map's own `'load'`),
29764
30140
  `lr-map-legend-toggle` (**cancelable**; frozen `LyraMapLegendToggleDetail { value, visible,
29765
30141
  hiddenCategories }` — the activated row's category key, its proposed visibility, and the complete
@@ -29792,7 +30168,16 @@ separately because it is a synthetic aggregate rather than one of your features:
29792
30168
  is MapLibre's `point_count`/`point_count_abbreviated`/`cluster_id` properties, which is what a
29793
30169
  zoom-to-cluster handler reads. The count label is deliberately not hit-tested (it sits exactly on
29794
30170
  the circle already queried and would only make the label the topmost hit), and a `kind: 'heatmap'`
29795
- layer is never queried at all — MapLibre returns no features for a rendered density surface
30171
+ layer is never queried at all — MapLibre returns no features for a rendered density surface.
30172
+ Also `lr-map-view-change` (non-cancelable; frozen `LyraMapViewChangeDetail { center, zoom, source }`,
30173
+ `source` being `LyraMapViewChangeSource = 'user' | 'fit'`), fired once each time the camera settles
30174
+ after a user gesture (drag, wheel, touch, keyboard, Shift+drag box zoom, or a standard peer
30175
+ navigation button) or a `fitBounds()` call. `center` is `[longitude, latitude]` with the longitude
30176
+ wrapped into `[-180, 180]`. The element has already written the camera into `center` and `zoom`
30177
+ when it fires, so a host that mirrors camera state assigns the detail back, at any later time,
30178
+ without moving the map. Assigning `center`/`zoom` reconciles without emitting it, so a controlled
30179
+ host cannot loop, and camera moves made directly through `map` are not reported — which is why
30180
+ `'user'` and `'fit'` are the only sources.
29796
30181
 
29797
30182
  The outer marker-activation detail is frozen, while an opaque marker `unsafeHtml` value remains the
29798
30183
  original supplied value at the MapLibre popup and marker-activation boundary. Treat it as trusted
@@ -30999,7 +31384,8 @@ and later MIME values restore detection.
30999
31384
  button and the `kind="video"` `open-button` render `disabled`; a safe file chip's anchor loses
31000
31385
  its `href` and `download`, so it genuinely cannot fetch, and gains an explicit `role="link"` so
31001
31386
  its accessible name and `aria-current` stay valid on an element that no longer has an implicit
31002
- role. `lr-media-open` and `lr-before-media-download` stop firing from every path, `click()`
31387
+ role. `lr-media-open`, `lr-media-download-request` and its deprecated `lr-before-media-download`
31388
+ alias stop firing from every path, `click()`
31003
31389
  included, the action leaves the tab order, and it paints at `--lr-opacity-disabled` with a
31004
31390
  `not-allowed` cursor. Deliberately does NOT reach into the `kind="video"` player: `<video
31005
31391
  controls>` is media content with its own native transport, not this card's action. An
@@ -31031,9 +31417,13 @@ not exports from the component entry.
31031
31417
  **Events:** `lr-media-open` (`detail: LyraMediaCardOpenDetail { src: string; filename: string }`,
31032
31418
  noncancelable) notifies
31033
31419
  after image-card or video open-button activation; those kinds have no component-owned navigation,
31034
- so a host decides what "open" means. `lr-before-media-download` carries the same detail and is
31420
+ so a host decides what "open" means. `lr-media-download-request` carries the same detail and is
31035
31421
  cancelable only for a safe file anchor immediately before its native download/open default; calling
31036
- `preventDefault()` there suppresses that exact default. `detail.src` is whichever internally
31422
+ `preventDefault()` there suppresses that exact default. `lr-before-media-download` is its deprecated
31423
+ alias (removal not before 23.0.0): it still fires right after the canonical event from the same
31424
+ activation, with an equal but separate `detail`, and either event's `preventDefault()` vetoes the
31425
+ download/open. A veto through the alias logs a one-time development warning, so move listeners to
31426
+ `lr-media-download-request`. `detail.src` is whichever internally
31037
31427
  validated safe-URL sink actually rendered, not necessarily the raw `src` property verbatim — a
31038
31428
  whitespace-padded value is trimmed, so it matches the rendered sink.
31039
31429
  The former generic `lr-open` event is removed in v9: notification and veto phases now have distinct,
@@ -31089,7 +31479,7 @@ sizing), `--lr-focus-ring-*`, `--lr-transition-fast`.
31089
31479
  .addEventListener("lr-media-open", (e) => openLightbox(e.detail.src));
31090
31480
  document
31091
31481
  .getElementById("file-card")
31092
- .addEventListener("lr-before-media-download", (e) => shouldUseNativeDownload || e.preventDefault());
31482
+ .addEventListener("lr-media-download-request", (e) => shouldUseNativeDownload || e.preventDefault());
31093
31483
  </script>
31094
31484
  ```
31095
31485
 
@@ -32527,11 +32917,15 @@ These named interfaces and helper signatures are available to typed integrations
32527
32917
  setZoom(zoom: number): unknown;
32528
32918
  resize(): unknown;
32529
32919
  setMaxBounds?(bounds: readonly [readonly [number, number], readonly [number, number]] | null): unknown;
32920
+ fitBounds?(bounds: [[number, number], [number, number]], options: Record<string, unknown>, eventData: Record<string, unknown>): unknown;
32921
+ isMoving?(): boolean;
32530
32922
  remove(): void;
32531
32923
  on(type: 'error', listener: (event: {
32532
32924
  error?: unknown;
32533
32925
  }) => void): this;
32534
32926
  on(type: 'load', listener: () => void): this;
32927
+ on(type: 'moveend', listener: (event: Readonly<Record<string, unknown>> | undefined) => void): this;
32928
+ on(type: 'boxzoomend', listener: () => void): this;
32535
32929
  on(type: 'click', listener: (event: {
32536
32930
  point: unknown;
32537
32931
  lngLat: {
@@ -32721,6 +33115,26 @@ These named interfaces and helper signatures are available to typed integrations
32721
33115
  readonly open: boolean;
32722
33116
  }`
32723
33117
  Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
33118
+ `LyraMapPadding {
33119
+ readonly top?: number;
33120
+ readonly right?: number;
33121
+ readonly bottom?: number;
33122
+ readonly left?: number;
33123
+ }`
33124
+ Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
33125
+ `LyraMapFitBoundsOptions {
33126
+ readonly padding?: number | LyraMapPadding;
33127
+ readonly maxZoom?: number;
33128
+ readonly animate?: boolean;
33129
+ }`
33130
+ Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
33131
+ `LyraMapViewChangeDetail {
33132
+ readonly center: readonly [number, number];
33133
+ readonly zoom: number;
33134
+ readonly source: LyraMapViewChangeSource;
33135
+ }`
33136
+ `LyraMapViewChangeSource = 'user' | 'fit'`.
33137
+ Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
32724
33138
  `LyraMapStyleSpecification {
32725
33139
  readonly version: 8;
32726
33140
  readonly sources: Readonly<Record<string, unknown>>;
@@ -32996,11 +33410,10 @@ structured points retain their y-value formatting.
32996
33410
  axes, `'r'` for a radar/polar-area ring, and `undefined` where there is no axis at all (a
32997
33411
  pie/doughnut slice). Two value axes usually exist precisely because they carry different units,
32998
33412
  so without it one formatter cannot render a secondary axis correctly. `datasetIndex`, `index`,
32999
- `label` and `seriesLabel` now reach the `tick`, `tooltip`, `legend` and `visual` surfaces as well
33000
- as the `table`, `export` and `spoken` ones they always reached, and indexes are reported in
33001
- source space — the same space the data table, the CSV export and `lr-point-click` use. Every one
33002
- of these fields was `undefined` before, so no existing formatter changes behaviour. A
33003
- `stackTotals`/`tableTotals` stack total is the exception in the other direction: it is a sum
33413
+ `label` and `seriesLabel` reach the `tooltip`, `legend`, `visual`, `table`, `export` and `spoken`
33414
+ surfaces, with indexes in source space — the same space the data table, the CSV export and
33415
+ `lr-point-click` use. A `tick` call carries `axis` but none of those four. A
33416
+ `stackTotals`/`tableTotals` stack total is also an exception: it is a sum
33004
33417
  *across* the stack's datasets, so it carries `statistic: 'total'`, the category `index`/`label`
33005
33418
  and the stack's own `axis`, but no `datasetIndex` and no `seriesLabel` — naming the topmost
33006
33419
  series would make a unit-switching formatter render that one series' unit for a cross-series
@@ -33468,7 +33881,9 @@ is no "every item in the tooltip" surface to hook a title or footer formatter on
33468
33881
  them side by side; ignored for `type="line"`
33469
33882
  - `tickFormat?: (value: number) => string` (attribute: false) — formats a y-axis tick value for
33470
33883
  display (e.g. `(v) => \`$${v.toFixed(2)}\`` for currency, or a duration formatter for `"42s"`).
33471
- Falls back to the built-in "nice numbers" formatter when unset.
33884
+ Falls back to the built-in "nice numbers" formatter when unset. The returned string is painted
33885
+ bidi-isolated, so `2.4 MiB/s` keeps its number-unit order under `dir="rtl"` (see
33886
+ [Formatted labels in right-to-left layouts](#formatted-labels-in-right-to-left-layouts)).
33472
33887
  - `formatter?: LyraChartFormatter` (attribute: false) — family-wide context-object formatter used
33473
33888
  by visual/tooltips, spoken text, legends, tables, and CSV export. It takes precedence over the
33474
33889
  older surface-specific hooks, which remain available as compatibility fallbacks.
@@ -33526,14 +33941,20 @@ is no "every item in the tooltip" surface to hook a title or footer formatter on
33526
33941
  actually render *text*: after the global record sampler runs, it selects from those retained
33527
33942
  categories, always shows the first and last sampled label, and roughly evenly distributes the
33528
33943
  rest between them. A number is authoritative up to the number of sampled categories. `'auto'`
33529
- derives the cap after each resize from the resolved plot width and widest rendered caller label,
33530
- using the same deterministic 7px-per-
33531
- character estimate as label ellipsis plus 10px of lane breathing room. It therefore responds to
33532
- either `layout` mode without DOM text measurement or browser-specific font metrics. Unset (the
33533
- default) renders every label, unchanged. Each rendered category label is allocation-aware:
33534
- narrow/long text is ellipsized before paint, with the complete caller label retained as its
33535
- accessible name. Independently, the global 1,000-record safety sampler may bound both marks and
33536
- labels for very large category×series input.
33944
+ re-selects after each resize from the resolved tick positions and the widest rendered caller
33945
+ label, using the same deterministic 7px-per-character estimate as label ellipsis. Whenever the
33946
+ first and last labels fit side by side, it keeps at least 10px of breathing room between every
33947
+ adjacent pair of labels; when even those two do not fit, only they remain and label ellipsis
33948
+ prevents overlap. Each label is measured where it paints: centered on its tick, except the first
33949
+ and last category, which anchor toward the plot interior (mirrored under RTL) and so occupy their
33950
+ whole width on the inner side of their tick. Label centers are spaced evenly between those two
33951
+ boundary labels, so neither boundary pair is crowded. A `barX` that reorders the ticks away from
33952
+ source order falls back to the width-only cap (plot width divided by the widest label plus 10px).
33953
+ It therefore responds to either `layout` mode without DOM text measurement or browser-specific
33954
+ font metrics. Unset (the default) renders every label, unchanged. Each rendered category label
33955
+ is allocation-aware: narrow/long text is ellipsized before paint, with the complete caller label
33956
+ retained as its accessible name. Independently, the global 1,000-record safety sampler may bound
33957
+ both marks and labels for very large category×series input.
33537
33958
  - `barX?: (index: number) => number` (attribute: false, bar type only) — overrides the internal
33538
33959
  per-category x-origin formula (`plotX + i * slot`) used by both bars and their axis labels, so a
33539
33960
  consumer can pixel-align this chart's bars with a sibling `<lr-heatmap>` calendar's week columns
@@ -34050,7 +34471,7 @@ bounded-alternative sampling notice)
34050
34471
  `--lr-chart-tooltip-text` — same public host-level precedence, token names, and defaults as `lr-chart`
34051
34472
  (also `getComputedStyle`-resolved and CSS-color-validated on every draw; invalid expressions use
34052
34473
  concrete semantic fallbacks rather than retaining a prior canvas paint), but declared in its own stylesheet, not a
34053
- re-export: `lr-box-plot` has no `zoom`, so no `reset-zoom-button` chrome exists here. A `BoxPlotSeries`
34474
+ re-export: `lr-box-plot` has no `zoom`, so no `reset-zoom-button` chrome exists here. A `LyraBoxPlotSeries`
34054
34475
  that sets no `color` is assigned an entry from the same `--lr-color-chart-1..8` ramp `lr-chart` uses,
34055
34476
  so `--lr-theme-color-chart-*` retheming reaches box plots too. That resolved color then layers two
34056
34477
  further per-series override tokens for the canvas paint, each wrapping modulo 8 like the underlying
@@ -34117,6 +34538,44 @@ loads are memoized per page.
34117
34538
 
34118
34539
  ---
34119
34540
 
34541
+ ## Formatted labels in right-to-left layouts
34542
+
34543
+ Every formatted chart label is bidi-isolated: it paints in the direction of its own first strong
34544
+ character, like `<bdi>` or a first-strong isolate, rather than in the chart's inherited direction.
34545
+ A number-first left-to-right string from `tickFormat`, `formatter`, `valueFormatter`,
34546
+ `tableCellFormatter`, `legendText` or an authored category label (`2.4 MiB/s`, `9:00 AM`, `-3`)
34547
+ therefore keeps its number-unit order inside `dir="rtl"`, and an Arabic- or Hebrew-script label
34548
+ keeps its own right-to-left order instead of being forced left-to-right. Axis placement, category
34549
+ order, boundary anchoring and label fitting are unchanged, and there is nothing to opt into — no
34550
+ text-direction hook is needed, and a formatter should return plain text without its own isolation
34551
+ controls.
34552
+
34553
+ - `lr-lite-chart`: value-axis ticks, category ticks and axis titles (SVG `unicode-bidi: plaintext`);
34554
+ the `legendText`/`formatter` legend value; the generated table's series headers, row headers and
34555
+ cells; and, while visible, each value interpolated into a single-series data-list sentence (each
34556
+ in its own `<bdi>`).
34557
+ - `lr-chart` (every typed subclass and `lr-histogram` included) and `lr-box-plot`: Chart.js tick
34558
+ labels on every generated scale (radar/polar point labels included), axis titles, every tooltip
34559
+ line, annotation labels and the `data-labels`/`stack-totals` labels are drawn inside a Unicode
34560
+ directional embedding only where their first-strong direction differs from the canvas's (a
34561
+ Chart.js `textDirection` set through `config` counts as the canvas's). A formatted value inside a
34562
+ tooltip line (`Series: 2.4 MiB/s`) is also embedded on its own wherever it reads differently from
34563
+ the line, so it keeps its order after an Arabic- or Hebrew-script series name. A left-to-right
34564
+ chart with left-to-right labels therefore draws the formatter's strings exactly. A DOM legend
34565
+ entry that shows a value isolates its label and value each in a `<bdi>`, and so do the generated
34566
+ table's headers and cells.
34567
+ - Labels that a raw `config` passthrough draws on its own — scales, annotations or plugins it adds
34568
+ beyond the generated ones — are drawn exactly as given; isolate those strings yourself if they
34569
+ need it.
34570
+ - Isolation adds no bidi control characters to accessible names, live announcements, the visually
34571
+ hidden data alternatives or CSV export. `lr-lite-chart`'s per-mark native SVG `<title>` hover
34572
+ text stays the plain `pointText`/built-in string: the browser also exposes it as the mark's
34573
+ accessible description, where control characters would reach speech and braille output.
34574
+ - The same rule covers `lr-gauge`'s value and label captions and `lr-heatmap`'s hover tooltip and
34575
+ legend scale labels.
34576
+
34577
+ ---
34578
+
34120
34579
  ## Chart streaming and export
34121
34580
 
34122
34581
  `lr-lite-chart` and `lr-chart` expose imperative helpers for live dashboards:
@@ -34724,7 +35183,11 @@ value, without suppressing the visible `name` heading.
34724
35183
  (`<lr-dialog>`'s close event was spelled `lr-dialog-close` before 10.0.0.) The name itself is not
34725
35184
  dialog-scoped in this library: nesting this viewer inside a consumer's own `<lr-dialog>` means
34726
35185
  that dialog's `lr-close` listener also observes this event — see `<lr-dialog>`'s own `lr-close`
34727
- section (in `overlays.md`) for the full list of emitters and the guard pattern.
35186
+ section (in `overlays.md`) for the full list of emitters and the guard pattern. This `lr-close`
35187
+ is not cancelable, and it is the viewer's only dismissal report: the shell dialog's own
35188
+ `lr-show`, `lr-after-show`, `lr-initial-focus`, `lr-request-close`, `lr-hide`, and
35189
+ `lr-after-hide` stay inside the viewer. A registered renderer's descendant dialog keeps those
35190
+ events on their normal composed path.
34728
35191
  - `lr-download` — `detail: { src, filename }`, emitted when the native safe download action is
34729
35192
  activated. The browser download itself is handled by the link.
34730
35193
  - `lr-anchor-result` — `detail: { found }`. Emitted by this shell as `{ found: false }` once per
@@ -34761,11 +35224,15 @@ for the native download action's hover and pressed backgrounds.
34761
35224
 
34762
35225
  **Renderer registry exports:**
34763
35226
 
34764
- - `DocumentFile` — the compatible mutable lookup input passed to `matches(file)` callbacks:
35227
+ - `DocumentFile` (deprecated, removal not before 23.0.0; use the structurally identical
35228
+ `LyraDocumentFile`) — the compatible mutable lookup input passed to `matches(file)` callbacks:
34765
35229
  `{ name, mimeType, src, anchor?, highlights?, alt? }`. Adapter and legacy-render boundaries
34766
35230
  receive an immutable file snapshot whose highlights use the shared trimmed, nonempty,
34767
- first-wins identity projection.
34768
- - `LyraDocumentFile` — the readonly file snapshot wrapped by every discriminated payload.
35231
+ first-wins identity projection. Exported signatures, listed below, keep this name until its
35232
+ removal; the two names are assignable to each other, so annotate your own code with
35233
+ `LyraDocumentFile`.
35234
+ - `LyraDocumentFile` — the readonly file snapshot wrapped by every discriminated payload, and the
35235
+ supported name for the file type.
34769
35236
  - `LyraDocumentRendererPayload` — readonly `kind: 'document' | 'av'` discriminated input wrapping an
34770
35237
  immutable `file`. The AV branch adds readonly `cues` and `tracks`; snapshots retain at most 10,000
34771
35238
  cues and 64 tracks, clone and freeze every retained record, and bound every retained string.
@@ -34775,7 +35242,8 @@ for the native download action's hover and pressed backgrounds.
34775
35242
  - `LyraDocumentRendererAdapterDefinition<K>` / `LyraDocumentRendererAdapter` — strongly typed
34776
35243
  authoring input and its factory-created, type-erased registry form. The callbacks adapt the legacy
34777
35244
  file to one payload kind, derive capabilities from that retained payload, and render it.
34778
- - `DocumentRendererDefinition` (also `LyraDocumentRendererDefinition`) — a validated direct
35245
+ - `LyraDocumentRendererDefinition` (the deprecated `DocumentRendererDefinition` names the same
35246
+ union until its removal, no earlier than 23.0.0) — a validated direct
34779
35247
  `{ render, matches?, capabilities? }`, adapted `{ adapter, matches? }`, or lazy
34780
35248
  `{ load, matches?, capabilities? }` definition; exactly one of `render`, `adapter`, and `load` is
34781
35249
  required. Static capabilities are rejected on adapted definitions so their adapter remains the
@@ -36588,9 +37056,13 @@ clipboard failure bubbles `lr-error` plus `lr-copy-error`
36588
37056
  ## `lr-geojson-viewer` / `lr-geojson-view`
36589
37057
 
36590
37058
  Document-registry bridge that fetches, validates, and renders a GeoJSON file through `<lr-map>`'s
36591
- `dataLayers`. The canonical class is `LyraGeoJsonViewer`; the pre-v9 `lr-geojson-view` tag and
36592
- `LyraGeojsonView` class name remain compatibility aliases. The document registry renders the
36593
- canonical tag and matches `application/geo+json` and `.geojson` filenames.
37059
+ `dataLayers`. The canonical class is `LyraGeoJsonViewer`. The pre-v9 `lr-geojson-view` tag, its
37060
+ `LyraGeojsonView` class and `LyraGeojsonViewEventMap` type are deprecated compatibility aliases
37061
+ (removal not before 23.0.0; this replaces the earlier promise to keep them permanently): they keep
37062
+ working unchanged until then, and connecting `lr-geojson-view` logs a one-time development warning.
37063
+ Rename them to `lr-geojson-viewer`, `LyraGeoJsonViewer` and `LyraGeoJsonViewerEventMap`. The
37064
+ document registry renders the canonical tag and matches `application/geo+json` and `.geojson`
37065
+ filenames.
36594
37066
 
36595
37067
  Validates the parsed JSON is a `Feature`/`FeatureCollection`/bare geometry (one of `Point`,
36596
37068
  `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`, `GeometryCollection`) before
@@ -36661,8 +37133,12 @@ but working state, since the `lr-json-viewer` fallback below it still renders th
36661
37133
  size of `[part="base"]`, matching every other viewer in this family. It is also settable via the
36662
37134
  `maxHeight` property, which writes this token inline on `[part="base"]`.
36663
37135
 
36664
- The canonical tag is registered by importing `geojson-viewer/geojson-viewer.js`; the permanent
36665
- compatibility tag and old deep route remain available through `geojson-view/geojson-view.js`.
37136
+ The canonical tag is registered by importing `components/lr-geojson-viewer.js` (deep route
37137
+ `components/viewers/geojson-view/geojson-viewer.js`). The deprecated `components/lr-geojson-view.js`,
37138
+ `components/viewers/geojson-view/geojson-view.js` and `geojson-view.class.js` routes register or
37139
+ export the alias (the registration routes also register `lr-geojson-viewer`) and keep working until
37140
+ their removal, no earlier than 23.0.0; an application importing one of them only to register the
37141
+ canonical tag switches to `components/lr-geojson-viewer.js`.
36666
37142
  Remote
36667
37143
  resources are capped at 25 MB; exceeding it surfaces the localized `documentPreviewResourceTooLarge`
36668
37144
  message instead of the map. Lyra supports MapLibre v5 and v6; consumers must import its CSS.
@@ -37344,7 +37820,9 @@ uses for its own `[part="body"]`.
37344
37820
  indented. It copies source text without the renderer's terminal newline, preserving leading tabs
37345
37821
  whenever they match the displayed code after expansion. Custom code renderers and pre-escaped
37346
37822
  code bypass it. `codeBlockChrome: boolean = false` (attribute `code-block-chrome`) is a
37347
- compatibility spelling; either property enables the header. Plain streaming defers headers until
37823
+ deprecated compatibility spelling (removal not before 23.0.0; setting it logs a one-time
37824
+ development warning): either property enables the header, so use `code-block-header`. Plain
37825
+ streaming defers headers until
37348
37826
  settle; progressive streaming adds them to committed blocks. The button has a localized accessible
37349
37827
  name and native pointer title, with no custom tooltip on keyboard focus. In sanitize mode enabling
37350
37828
  the header also removes authored style elements and their CSS text to prevent visual copy
@@ -37635,8 +38113,9 @@ instance's isolated peer-neutral configurable parser; `htmlMode: 'sanitize' | 'e
37635
38113
  `link-target`), `internalLinkPrefix: string = ''` (attribute `internal-link-prefix`),
37636
38114
  `headingOffset: number = 0` (attribute `heading-offset`), `streaming: boolean = false` (reflected),
37637
38115
  `streamingRender: MarkdownStreamingRender = 'plain'` (attribute `streaming-render`, not reflected),
37638
- `codeBlockHeader: boolean = false` (attribute `code-block-header`) with its compatibility alias
37639
- `codeBlockChrome: boolean = false` (attribute `code-block-chrome`),
38116
+ `codeBlockHeader: boolean = false` (attribute `code-block-header`) with its deprecated
38117
+ compatibility alias `codeBlockChrome: boolean = false` (attribute `code-block-chrome`; removal not
38118
+ before 23.0.0),
37640
38119
  `highlightCode: boolean = true` (attribute
37641
38120
  `highlight-code`), `languages: Record<string, ShikiLanguageSource> = {}` (attribute: false) —
37642
38121
  required, unlike `<lr-markdown>`'s optional `languages?:`; empty (the default) means every fenced
@@ -37726,7 +38205,7 @@ The Markdown part set also includes `task-list`, `task-item`, `task-item-checked
37726
38205
  `task-checkbox`, `table-wrapper`, `code-block-frame`, `code-block-copy-success`,
37727
38206
  `code-block-copy-error` and `streaming-tail`. `codeBlockHeader: boolean = false`
37728
38207
  (attribute `code-block-header`) enables the code-block header; `codeBlockChrome` (attribute
37729
- `code-block-chrome`) is its compatibility alias. Successful and
38208
+ `code-block-chrome`) is its deprecated compatibility alias (removal not before 23.0.0). Successful and
37730
38209
  failed writes pass through as `lr-copy` and `lr-copy-error`, carrying the immutable clipboard
37731
38210
  outcome, bubbling and composed.
37732
38211
 
@@ -38408,10 +38887,28 @@ plus bubbling/composed `blur` and `focus` with `null` detail relayed from the re
38408
38887
 
38409
38888
  `start`/`content`/`excerpt`/`meta` must all stay non-focusable — see the `role="button"` note below.
38410
38889
 
38411
- **CSS parts:** `base`, `active-indicator` (decorative, rendered only while `active`),
38890
+ **CSS parts:** `base`, `base-menu-open` (state alias on `base` while a menu opened from the
38891
+ `actions` slot is open), `active-indicator` (decorative, rendered only while `active`),
38412
38892
  `select-button`, `start`, `content`, `label`, `label-input`, `rename-button`, `excerpt`, `meta`, `timestamp`,
38413
38893
  `actions`
38414
38894
 
38895
+ **Custom state:** `menu-open` — the host matches `:state(menu-open)` while an `lr-dropdown`,
38896
+ `lr-popover` or `lr-context-menu` opened from the `actions` slot is open (including one composed
38897
+ inside another component's shadow root), however it was opened; `[part="base"]` then also carries
38898
+ `base-menu-open`, and the row keeps its hover tint (an `active` row keeps its active tint). The menu
38899
+ sits in the browser top layer, where Chromium and WebKit stop matching `:hover` and `:focus-within`
38900
+ on the row while the pointer or focus is inside it, so a hover- or focus-revealed actions trigger
38901
+ must key on this state as well:
38902
+
38903
+ ```css
38904
+ lr-conversation-item .row-menu {
38905
+ opacity: 0;
38906
+ }
38907
+ lr-conversation-item:is(:hover, :focus-within, :state(menu-open)) .row-menu {
38908
+ opacity: 1;
38909
+ }
38910
+ ```
38911
+
38415
38912
  **Themeable custom properties:** `--lr-conversation-item-active-bg` (default
38416
38913
  `var(--lr-color-brand-quiet)`) — the row's background while `active`. `--lr-conversation-item-active-color`
38417
38914
  (default `var(--lr-color-text)`) — the text color of `[part='excerpt']` and `[part='timestamp']`
@@ -38846,9 +39343,11 @@ properties existed:
38846
39343
  - `headingOffset: number = 0` (attribute `heading-offset`) — forwarded to the composed
38847
39344
  `<lr-markdown>`'s own `headingOffset`.
38848
39345
  - `streamingRender: MarkdownStreamingRender = 'plain'` (attribute `streaming-render`, not reflected)
38849
- and `codeBlockHeader: boolean = false` (attribute `code-block-header`), with its compatibility
38850
- alias `codeBlockChrome: boolean = false` (attribute `code-block-chrome`) — forwarded to the
38851
- composed Markdown element. Their settled-block streaming behavior, code-copy behavior, and defaults match
39346
+ and `codeBlockHeader: boolean = false` (attribute `code-block-header`), with its deprecated
39347
+ compatibility alias `codeBlockChrome: boolean = false` (attribute `code-block-chrome`; removal not
39348
+ before 23.0.0; setting it logs a one-time development warning) — forwarded to the composed
39349
+ Markdown element, which receives either header option as its own `codeBlockHeader`. Their
39350
+ settled-block streaming behavior, code-copy behavior, and defaults match
38852
39351
  `<lr-markdown>`; see its **Properties** section above.
38853
39352
  Use `streaming-render="progressive"` with `content-mode="markdown"`: in `auto` mode the element
38854
39353
  switches from plain text to Markdown when detection first succeeds, which replaces the displayed
@@ -38944,7 +39443,8 @@ showing the previous stream's stale final content for the length of the window.
38944
39443
 
38945
39444
  Rendering itself is never reimplemented here: Markdown mode composes `<lr-markdown>` directly,
38946
39445
  forwarding this component's own `streaming` through as that component's `streaming` hint prop,
38947
- `streamingRender`, `codeBlockHeader`, `codeBlockChrome`, `languages` verbatim, and the rest of `<lr-markdown>`'s
39446
+ `streamingRender`, `codeBlockHeader` (on when either this element's `codeBlockHeader` or its
39447
+ deprecated `codeBlockChrome` is), `languages` verbatim, and the rest of `<lr-markdown>`'s
38948
39448
  configuration surface verbatim too (`tabSize`, `htmlMode`, `gfm`, `linkTarget`,
38949
39449
  `internalLinkPrefix`, `headingOffset`, `highlightCode`, `headingAnchors`, `math`, `maxHeight` —
38950
39450
  see **Properties** above); plain-text mode
@@ -38975,7 +39475,7 @@ The Markdown part set also includes `task-list`, `task-item`, `task-item-checked
38975
39475
  `task-checkbox`, `table-wrapper`, `code-block-frame`, `code-block-copy-success`,
38976
39476
  `code-block-copy-error` and `streaming-tail`. `codeBlockHeader: boolean = false`
38977
39477
  (attribute `code-block-header`) enables the code-block header; `codeBlockChrome` (attribute
38978
- `code-block-chrome`) is its compatibility alias. Successful and
39478
+ `code-block-chrome`) is its deprecated compatibility alias (removal not before 23.0.0). Successful and
38979
39479
  failed writes pass through as `lr-copy` and `lr-copy-error`, carrying the immutable clipboard
38980
39480
  outcome, bubbling and composed.
38981
39481
 
@@ -39013,12 +39513,14 @@ attribute names, and defaults described under `<lr-streaming-text>`'s own **Prop
39013
39513
  `rel="noopener noreferrer"` whenever a `target` is emitted); `internalLinkPrefix: string = ''`
39014
39514
  (attribute `internal-link-prefix`); `headingOffset: number = 0` (attribute `heading-offset`);
39015
39515
  `streamingRender: MarkdownStreamingRender = 'plain'` (attribute `streaming-render`, not reflected);
39016
- `codeBlockHeader: boolean = false` (attribute `code-block-header`) and its compatibility alias
39017
- `codeBlockChrome: boolean = false` (attribute `code-block-chrome`); `highlightCode: boolean = true`
39516
+ `codeBlockHeader: boolean = false` (attribute `code-block-header`) and its deprecated
39517
+ compatibility alias `codeBlockChrome: boolean = false` (attribute `code-block-chrome`; removal not
39518
+ before 23.0.0); `highlightCode: boolean = true`
39018
39519
  (attribute `highlight-code`); `headingAnchors: boolean = false` (attribute `heading-anchors`);
39019
39520
  `math: boolean = false`; `maxHeight: string = ''` (attribute `max-height`). All are forwarded to
39020
39521
  the composed `<lr-markdown-core>`, with matching behavior and defaults. `streamingRender` controls
39021
- progressive output, and `codeBlockHeader` (or its `codeBlockChrome` alias) enables the localized
39522
+ progressive output, and `codeBlockHeader` (or its deprecated `codeBlockChrome` alias, which reaches
39523
+ the composed element as `codeBlockHeader`) enables the localized
39022
39524
  code label and source-copy action.
39023
39525
 
39024
39526
  **Exported helper:** `looksLikeMarkdown(text: string): boolean` — the same standalone heuristic
@@ -39069,7 +39571,7 @@ The Markdown part set also includes `task-list`, `task-item`, `task-item-checked
39069
39571
  `task-checkbox`, `table-wrapper`, `code-block-frame`, `code-block-copy-success`,
39070
39572
  `code-block-copy-error` and `streaming-tail`. `codeBlockHeader: boolean = false`
39071
39573
  (attribute `code-block-header`) enables the code-block header; `codeBlockChrome` (attribute
39072
- `code-block-chrome`) is its compatibility alias. Successful and
39574
+ `code-block-chrome`) is its deprecated compatibility alias (removal not before 23.0.0). Successful and
39073
39575
  failed writes pass through as `lr-copy` and `lr-copy-error`, carrying the immutable clipboard
39074
39576
  outcome, bubbling and composed.
39075
39577
 
@@ -39781,7 +40283,10 @@ MessageFeedbackValue = null` (attribute `feedback-rating`) — forwarded to the
39781
40283
  opens). `feedbackPending: boolean` (read-only, nonreflecting) — true only while the current built-in
39782
40284
  feedback control awaits settlement; it has no `feedback-pending` attribute or change event.
39783
40285
  `revealOnInteraction: boolean = false` (reflected, attribute `reveal-on-interaction`) — hides
39784
- the bar until the closest `lr-chat-message` ancestor is hovered, or the toolbar contains focus.
40286
+ the bar until the closest `lr-chat-message` ancestor is hovered, or the toolbar contains focus. It
40287
+ also stays revealed while an `lr-dropdown`, `lr-popover` or `lr-context-menu` opened from a slotted
40288
+ control is open, since that menu sits in the browser top layer, where focus inside it does not
40289
+ count as focus within the toolbar in Chromium and WebKit.
39785
40290
  `label?: string` — accessible name override for the toolbar. Omitting it localizes the default
39786
40291
  `messageActionsLabel` message; an explicit empty string suppresses that default and renders no
39787
40292
  label. `accessibleLabel: string | null =
@@ -40338,8 +40843,11 @@ ancestor `--lr-positioning-strategy` still wins), and a trapped `fixed` menu ope
40338
40843
  outside the list, in the browser top layer where the native Popover API exists, in left-to-right
40339
40844
  and right-to-left documents, with no consumer CSS. A promoted menu whose trigger scrolls out of the
40340
40845
  list is hidden until the trigger returns. Where the Popover API is absent, the row holding an open
40341
- `lr-dropdown` stops transforming while it is open, so its menu is still not clipped. Unset renders
40342
- only the built-in
40846
+ `lr-dropdown` stops transforming while it is open, so its menu is still not clipped. While such a
40847
+ menu is open its row carries `row-item-base-menu-open` (see the parts below) and keeps its built-in
40848
+ hover tint. Arrow, Home and End keys a returned control already handled (`lr-menu` moving between
40849
+ its own items, a dropdown trigger opening its menu) stay with that control rather than also moving
40850
+ focus to another row. Unset renders only the built-in
40343
40851
  `rowActions`.
40344
40852
  `renderStart?: (thread: LyraChatThread) => TemplateResult` (attribute: false) — renders non-interactive
40345
40853
  start-side content in each virtualized row. `renderExcerpt?: (thread: LyraChatThread) => TemplateResult`
@@ -40422,10 +40930,30 @@ reach callback-rendered descendants through these parts). `row-excerpt` wraps `r
40422
40930
  output, which is slotted into the row item's own `excerpt` slot.
40423
40931
 
40424
40932
  Data mode additionally forwards each row `<lr-conversation-item>`'s own parts under a `row-item-`
40425
- prefix: `row-item-base`, `row-item-active-indicator`, `row-item-select-button`, `row-item-start`, `row-item-content`,
40933
+ prefix: `row-item-base`, `row-item-base-menu-open`, `row-item-active-indicator`, `row-item-select-button`, `row-item-start`, `row-item-content`,
40426
40934
  `row-item-label`, `row-item-label-input`, `row-item-rename-button`, `row-item-excerpt`,
40427
40935
  `row-item-meta`, `row-item-timestamp`, `row-item-actions`.
40428
40936
 
40937
+ **Row menu-open state:** `row-item-base-menu-open` is carried by `row-item-base` while a menu that
40938
+ `renderActions` opened from that row (`lr-dropdown`, `lr-popover` or `lr-context-menu`) is open,
40939
+ however it was opened. That menu sits in the browser top layer, where Chromium and WebKit stop
40940
+ matching `:hover` and `:focus-within` on the row while the pointer or keyboard focus is inside it.
40941
+ A menu trigger revealed on row hover or focus must key on this state too, or it hides while its own
40942
+ menu is open — and a `display`-based reveal hides the menu with it, because an open menu is
40943
+ concealed once its trigger stops being measurable. `::part()` cannot be followed by a descendant,
40944
+ so carry the reveal to `row-actions` through an inherited custom property:
40945
+
40946
+ ```css
40947
+ lr-thread-list::part(row-item-base):hover,
40948
+ lr-thread-list::part(row-item-base):focus-within,
40949
+ lr-thread-list::part(row-item-base-menu-open) {
40950
+ --app-row-actions-opacity: 1;
40951
+ }
40952
+ lr-thread-list::part(row-actions) {
40953
+ opacity: var(--app-row-actions-opacity, 0);
40954
+ }
40955
+ ```
40956
+
40429
40957
  **Themeable excerpt highlights:** `<mark>` descendants returned by `renderExcerpt` use
40430
40958
  `--lr-thread-list-excerpt-highlight-background` (default `var(--lr-color-warning-quiet)`),
40431
40959
  `--lr-thread-list-excerpt-highlight-foreground` (default `inherit`),
@@ -41199,7 +41727,9 @@ and `codeBlockHeader: boolean = false` (attribute `code-block-header`) — forwa
41199
41727
  and reasoning Markdown parts while `contentMode="markdown"`. Progressive mode renders settled
41200
41728
  blocks during a streaming part and preserves the `plain` fallback by default; the header adds the
41201
41729
  source language label and localized copy action to built-in code blocks.
41202
- `codeBlockChrome: boolean = false` (attribute `code-block-chrome`) is an equivalent enabling alias. These settings do not replace
41730
+ `codeBlockChrome: boolean = false` (attribute `code-block-chrome`) is a deprecated equivalent
41731
+ enabling alias (removal not before 23.0.0; setting it logs a one-time development warning), and the
41732
+ parts' Markdown elements receive either option as `codeBlockHeader`. These settings do not replace
41203
41733
  the `MessagePart.state` lifecycle or affect `contentMode="plain"`.
41204
41734
  `maxRenderedParts: number = 0` (attribute `max-rendered-parts`) — `0` (the default) renders every
41205
41735
  part, unbounded, matching every prior release; a positive value windows rendering to the newest N
@@ -41237,17 +41767,24 @@ duration; expanding shows arguments, error and result in place) and pairs it wit
41237
41767
  - Everything else renders as in chip display: unpaired results, a result whose call is outside the
41238
41768
  rendered window, duplicate results after the first, and custom-rendered calls or results. A
41239
41769
  second call part with the same invocation id renders its own unpaired block.
41240
- - The block binds `name`, `callId` and `args` from the invocation, and `redactedFields` from the
41770
+ - The block binds `name`, `displayName`, `callId` and `args` from the invocation, and `redactedFields` from the
41241
41771
  invocation-level paths described under redaction below; `result` from
41242
41772
  the paired part (else `invocation.result`); `error` from the paired part when it carries one (else
41243
41773
  `invocation.error`); and `durationMs = endedAt - startedAt` when both are set (else no duration).
41244
41774
  `expanded` is never bound, so a user-expanded block stays open across `parts` updates.
41245
- - Block status, first match wins: invocation `denied` → `denied`; invocation `error` or a paired
41246
- error result → `error`; a paired result still `streaming` → `running`; any paired result →
41775
+ - Block status, first match wins: invocation `denied` → `denied`; invocation `incomplete` (a call
41776
+ that ended without a result, 21.1.0) → `incomplete`, whatever result part is paired; invocation
41777
+ `error` or a paired error result → `error`; a paired result still `streaming` → `running`; any paired result →
41247
41778
  `success`; otherwise the invocation's own status (an unknown value → `pending`), so a call still
41248
41779
  waiting on its result keeps showing `pending`/`running`.
41249
41780
  - The tool-call wrapper stretches to the message width in block display.
41250
41781
 
41782
+ **Display name in both displays** (21.1.0). An invocation's `displayName` (the application's own,
41783
+ already-translated tool label) is bound to the chip's or the block's `displayName`, so the visible
41784
+ and announced tool name is the translated one while `name` still selects the result renderer and
41785
+ is still what `lr-tool-call-chip-select` and `lr-render-error` report. A non-string value is not
41786
+ forwarded.
41787
+
41251
41788
  **Redaction in both displays.** An invocation's `redactedFields` (dotted paths rooted at
41252
41789
  `args`/`result`/`error`, see `<lr-tool-call-block>`) masks every built-in rendering of its payload
41253
41790
  with the localized `Value hidden`: in block display the block's details; in chip display, and for
@@ -41407,7 +41944,11 @@ textarea, paired with `lr-input` and `lr-change`; `lr-submit` (`{ value }`),
41407
41944
  `lr-audio-request`, `lr-attachment-retry` (`{ attachmentId }`), and cancelable
41408
41945
  `lr-attachment-preview-request` (`{ attachmentId, name, mimeType, src }`). Child events are stopped
41409
41946
  and re-emitted from `lr-prompt-input`; all composed interactions are suppressed while `disabled`,
41410
- including a child event dispatched in the same turn that disables the host.
41947
+ including a child event dispatched in the same turn that disables the host. The one deliberate
41948
+ exception is the built-in voice picker's preview pair: its cancelable `lr-preview-request`
41949
+ (`{ voiceId, previewUrl? }`) and `lr-preview-change` (`{ voiceId }`) reach the host unchanged, so a
41950
+ host can play its own TTS preview (call `preventDefault()`) for catalog entries without a
41951
+ `previewUrl`, including plain-string catalogs.
41411
41952
 
41412
41953
  **Slots:** `controls`; `start` (attachment-control content before the textarea); `chips`; `end`
41413
41954
  (custom send/stop action); and `footer`. `start` replaces the default attachment trigger and `end`
@@ -41523,8 +42064,10 @@ movement starts from the action that received the event rather than stale contro
41523
42064
  private so controlled rect updates cannot be silently overridden by stale authored CSS.
41524
42065
  **Themeable custom properties:** `--lr-selection-toolbar-placement-gap` (default
41525
42066
  `var(--lr-space-s)`) is the non-negative distance from the selection and from viewport edges while
41526
- the toolbar avoids collisions. It accepts unitless pixel values and `px`, `rem`, and `em` values; unsupported
41527
- values fall back to the default and negative values clamp to `0`. Collision math uses the active
42067
+ the toolbar avoids collisions. It accepts unitless pixel values, `px`, `rem`, and `em` values, and
42068
+ CSS length math (`calc()`, `min()`, `max()`, `clamp()`) resolved in its live theme scope. Percentages
42069
+ have no placement-gap basis. Invalid values fall back to the default and negative values clamp to
42070
+ `0`. Collision math uses the active
41528
42071
  `visualViewport` bounds and offsets when available, including after visual-viewport changes.
41529
42072
 
41530
42073
  The toolbar is a floating surface and paints from the **shared overlay-surface family** (16.0.0):
@@ -42073,11 +42616,17 @@ its content.
42073
42616
  **Properties:**
42074
42617
 
42075
42618
  - `name: string = ''` — the tool/function name, e.g. `web_search`
42619
+ - `displayName?: string` (attribute `display-name`, 21.1.0) — the application's own,
42620
+ already-translated label for the tool (e.g. `Lecture d’un fichier`). It replaces `name` in the
42621
+ visible `name` part and the chip's accessible name only; `lr-tool-call-chip-select` still reports
42622
+ the raw `name`. Unset or empty shows `name`
42076
42623
  - `category: string = ''` — optional grouping label, e.g. `research`
42077
- - `status: 'pending'|'running'|'success'|'error'|'denied' = 'pending'` (reflected) — drives the
42078
- glyph, accent color, and `status-text`; same status vocabulary as `<lr-tool-result-dialog>` so a
42079
- call's chip and its detail dialog always agree; unknown runtime values render the pending icon,
42080
- text, and accessible label instead of failing the update
42624
+ - `status: 'pending'|'running'|'success'|'error'|'denied'|'incomplete' = 'pending'` (reflected) —
42625
+ drives the glyph, accent color, and `status-text`; same status vocabulary as
42626
+ `<lr-tool-result-dialog>` so a call's chip and its detail dialog always agree; `incomplete`
42627
+ (21.1.0) is a call that ended without a result (an interrupted stream, a cancelled run) and reads
42628
+ `Incomplete` (`statusIncomplete`) with its own static glyph in the neutral `pending` tone; unknown
42629
+ runtime values render the pending icon, text, and accessible label instead of failing the update
42081
42630
  - `summary: string = ''` — short human-readable status text, e.g. `Searching web…`
42082
42631
  - `durationMs?: number` (attribute `duration-ms`) — how long the call took, in milliseconds; the
42083
42632
  `duration` part is omitted entirely when unset
@@ -42102,7 +42651,7 @@ content — assigned content wins; otherwise the `icon` prop is rendered as a li
42102
42651
  the built-in glyph for the current `status` is used)
42103
42652
 
42104
42653
  **CSS parts:** `base` (the clickable `<button>`), `icon`, `label` (wrapper around `category`, `name`,
42105
- `summary`), `category`, `name`, `summary`, `meta` (wrapper around `status-text` and `duration`),
42654
+ `summary`), `category`, `name` (the tool name, or `display-name` when set), `summary`, `meta` (wrapper around `status-text` and `duration`),
42106
42655
  `status-text`, `duration`, `tooltip` (the floating detail popup, only meaningful while open)
42107
42656
 
42108
42657
  **Themeable custom properties:** `--lr-tool-call-chip-spin` (default `var(--lr-transition-ambient)`,
@@ -42110,7 +42659,7 @@ i.e. `1.8s ease-in-out` at the shipped token value and `0.001ms linear` under
42110
42659
  `prefers-reduced-motion` — running-icon animation duration/timing) and `--lr-transition-ambient`
42111
42660
  (default `1.8s ease-in-out` — pending-icon pulse duration/timing).
42112
42661
  `--lr-tool-call-chip-accent`, `--lr-tool-call-chip-bg`, and `--lr-tool-call-chip-border` are public
42113
- component hooks whose private defaults follow `status` (e.g. `pending` →
42662
+ component hooks whose private defaults follow `status` (e.g. `pending` and `incomplete` →
42114
42663
  `--lr-color-text-quiet`/`--lr-color-surface`/`--lr-color-border`; `running` → brand; `success` →
42115
42664
  success; `error` → danger; `denied` → warning). Set them on an ancestor to retheme a subtree or
42116
42665
  directly on one chip; either public value remains authoritative in every status. Shared tokens
@@ -42217,7 +42766,7 @@ Direction: a text fallback (`::part(fallback-text)`) uses `unicode-bidi: plainte
42217
42766
  - `copyable: boolean = false` (reflected) — shows a copy-to-clipboard affordance alongside the
42218
42767
  fallback view, for either `fallback` kind: forwarded to `<lr-json-viewer>`'s own `copyable` for
42219
42768
  `"json"`, or a `<lr-copy-button>` rendered next to the text for `"text"`.
42220
- - `status: 'pending'|'running'|'success'|'error'|'denied' = 'success'` (reflected) — the outcome of
42769
+ - `status: 'pending'|'running'|'success'|'error'|'denied'|'incomplete' = 'success'` (reflected) — the outcome of
42221
42770
  the currently-rendered result, as reported by the matched renderer's own `context.reportStatus()`
42222
42771
  (see below). Reset to `'success'` immediately before every `render()` call, so a renderer that
42223
42772
  never calls `reportStatus` — including every pre-existing 2-arg renderer written before this
@@ -42299,8 +42848,9 @@ DirectToolRendererDefinition }>` and `render?: never` — lazy loader
42299
42848
  **`ToolRenderContext`** — the shape of `render()`'s 3rd argument:
42300
42849
 
42301
42850
  - `reportStatus: (status: ToolResultStatus) => void` — reports this render's outcome without
42302
- throwing. `ToolResultStatus` is `'pending' | 'running' | 'success' | 'error' | 'denied'`, the same
42303
- union `<lr-tool-result-dialog>`/`<lr-tool-call-chip>` use, re-exported from this module. Calling
42851
+ throwing. `ToolResultStatus` is `'pending' | 'running' | 'success' | 'error' | 'denied' |
42852
+ 'incomplete'`, the same union `<lr-tool-result-dialog>`/`<lr-tool-call-chip>` use, re-exported
42853
+ from this module. Calling
42304
42854
  it is entirely optional: a renderer that never calls it leaves `<lr-tool-result-view>`'s `status`
42305
42855
  property at its default, `'success'`. This threads through the lazy `load()` path exactly the
42306
42856
  same way — a `render()` resolved via `load()` receives the same 3rd `context` argument as one
@@ -42432,8 +42982,10 @@ title.
42432
42982
  cloning that name. A direct property assignment made without the attribute can name the panel
42433
42983
  - `toolName: string = ''` (attribute `tool-name`) — the tool's name, rendered prominently in the
42434
42984
  header
42435
- - `status: 'pending'|'running'|'success'|'error'|'denied' = 'pending'` (reflected) — drives the
42436
- header's status badge; same status vocabulary as `<lr-tool-call-chip>`
42985
+ - `status: 'pending'|'running'|'success'|'error'|'denied'|'incomplete' = 'pending'` (reflected) —
42986
+ drives the header's status badge; same status vocabulary as `<lr-tool-call-chip>`. `incomplete`
42987
+ (21.1.0) is a call that ended without a result; its badge reads `Incomplete` in the neutral
42988
+ pending look, through its own `--lr-tool-result-dialog-incomplete-*` pair
42437
42989
  - `durationMs?: number` (attribute `duration-ms`) — how long the call took, in milliseconds; omitted
42438
42990
  from the header entirely when unset
42439
42991
  - `maximized: boolean = false` (reflected) — near-fullscreen presentation of the same open dialog
@@ -42536,6 +43088,8 @@ Backdrop clicks leave the dialog open by default; add `light-dismiss` to opt in,
42536
43088
  - `--lr-tool-result-dialog-error-bg` — Error status background. Default: `var(--lr-color-danger-quiet)`.
42537
43089
  - `--lr-tool-result-dialog-denied-color` — Denied status foreground. Default: `var(--lr-color-warning)`.
42538
43090
  - `--lr-tool-result-dialog-denied-bg` — Denied status background. Default: `var(--lr-color-warning-quiet)`.
43091
+ - `--lr-tool-result-dialog-incomplete-color` — Incomplete status foreground (21.1.0). Default: `var(--lr-color-text-quiet)`.
43092
+ - `--lr-tool-result-dialog-incomplete-bg` — Incomplete status background (21.1.0). Default: `transparent`.
42539
43093
 
42540
43094
  ---
42541
43095
 
@@ -43294,8 +43848,9 @@ fired once up front at connect time and after every effective change; serializat
43294
43848
  publish their root message as `errors.base` and `formError`), and no-detail `focus`/`blur` events for
43295
43849
  generated native text/number inputs. The composed
43296
43850
  `<lr-select>` controls already bubble their own focus/blur bridges through the host.
43297
- Nested control events (`input`, `change`, `lr-change`, select show/hide, and option mutation) are
43298
- contained at the form boundary; consumers receive the single form-level `lr-input` contract.
43851
+ Nested control events (`input`, `change`, `lr-change`, the selects' `lr-show`/`lr-after-show`/
43852
+ `lr-hide`/`lr-after-hide`, and option mutation) are contained at the form boundary; consumers receive
43853
+ the single form-level `lr-input` contract.
43299
43854
  `lr-invalid` (no detail) is the bubbling/composed, cancelable alias emitted when the complete
43300
43855
  parameter form fails a native validity check; preventing it also prevents the native `invalid`
43301
43856
  event's default validation UI.
@@ -44969,9 +45524,9 @@ primitives, with retry counts and sensitive-field redaction.
44969
45524
  **Properties:**
44970
45525
 
44971
45526
  - `entries: readonly ToolTimelineEntry[] = []` (attribute: false) — `ToolTimelineEntry` **extends
44972
- `ToolInvocation` from `@aceshooting/lyra-ui/ai`** (`{ id: string; name: string; args:
44973
- Record<string, unknown>; status: ToolCallStatus; result?: unknown; error?: string }`, where
44974
- `ToolCallStatus = 'pending' | 'running' | 'success' | 'error' | 'denied'`) with `{ startedAt?:
45527
+ `ToolInvocation` from `@aceshooting/lyra-ui/ai`** (`{ id: string; name: string; displayName?:
45528
+ string; args: Record<string, unknown>; status: ToolCallStatus; result?: unknown; error?: string }`, where
45529
+ `ToolCallStatus = 'pending' | 'running' | 'success' | 'error' | 'denied' | 'incomplete'`) with `{ startedAt?:
44975
45530
  number; endedAt?: number; retryCount?: number; redactedFields?: readonly string[]; needsApproval?: boolean;
44976
45531
  approved?: boolean; sourceKey?: string; icon?: string }`. `sourceKey` identifies the owning run or
44977
45532
  source generation when invocation ids can be reused; every expansion, activation, renderer error,
@@ -44979,7 +45534,12 @@ approved?: boolean; sourceKey?: string; icon?: string }`. `sourceKey` identifies
44979
45534
  are omitted; a blank optional `sourceKey` is treated as absent. Duplicate occurrences of the same
44980
45535
  pair are normalized before any lookup with a deterministic first-occurrence-wins policy. `icon` is a
44981
45536
  literal hint forwarded to the composed tool-call chip. A foreign runtime `status` normalizes once
44982
- to `pending` before both the timeline row and its composed chip render.
45537
+ to `pending` before both the timeline row and its composed chip render. `displayName` (21.1.0),
45538
+ the application's own translated tool label, is forwarded to the chip's `displayName` and names
45539
+ the entry's details disclosure (`Details for Lecture d’un fichier`); an empty, omitted or
45540
+ non-string value shows `name`. `name` still selects the result renderer, is still the `toolName`
45541
+ in `lr-tool-render-error`, and still names the tool in the approval dialog, so the reviewer
45542
+ authorizes the exact tool.
44983
45543
  Timestamps are epoch milliseconds; entries sort ascending by `startedAt`,
44984
45544
  and an entry with none sorts after every timed entry (keeping its relative position among other
44985
45545
  untimed ones) and renders no visible timestamp. `startedAt`+`endedAt` derive the `durationMs`
@@ -45060,6 +45620,8 @@ per-entry timeline marker dot's size, which also sets the entry grid's leading c
45060
45620
  `status="denied"` entry, decoupled from the pending-approval border color below so either can be
45061
45621
  retinted independently; `--lr-tool-timeline-pending-marker-color` (default
45062
45622
  `var(--lr-color-text-quiet)`) — rail-dot color for a `status="pending"` entry;
45623
+ `--lr-tool-timeline-incomplete-marker-color` (21.1.0, default `var(--lr-color-text-quiet)`) —
45624
+ rail-dot color for a `status="incomplete"` entry, independent of the pending one;
45063
45625
  `--lr-tool-timeline-pending-approval-border-color` (default
45064
45626
  `var(--lr-color-warning)`) — color of the entry body's leading border while that entry's
45065
45627
  `data-pending-approval` is `"true"`.
@@ -45970,6 +46532,7 @@ These named interfaces and helper signatures are available to typed integrations
45970
46532
  // Inherited from ToolInvocation.
45971
46533
  id: string;
45972
46534
  name: string;
46535
+ displayName?: string;
45973
46536
  args: Record<string, unknown>;
45974
46537
  status: ToolCallStatus;
45975
46538
  result?: unknown;
@@ -46030,11 +46593,19 @@ progress, and `No data` only for a terminal status.
46030
46593
  **Properties:**
46031
46594
 
46032
46595
  - `name: string = ''` — tool name; an empty name renders the localized generic `Tool call` label
46596
+ - `displayName?: string` (attribute `display-name`, 21.1.0) — the application's own,
46597
+ already-translated label for the tool. It replaces `name` inside the localized header verb only
46598
+ (`Used Lecture d’un fichier`), so the status wording stays localized; `name` still selects the
46599
+ result renderer and is still reported in `lr-render-error`. Unset or empty shows `name`; a
46600
+ `label` override replaces the whole header, this included
46033
46601
  - `callId: string = ''` (attribute `call-id`) — invocation id echoed in `lr-toggle` and
46034
46602
  `lr-render-error` details
46035
46603
  - `status: ToolCallStatus = 'pending'` (reflected, including the default) —
46036
- `'pending'|'running'|'success'|'error'|'denied'`; selects the header verb, glyph and accent.
46037
- Values outside the set normalize and reflect as `pending`
46604
+ `'pending'|'running'|'success'|'error'|'denied'|'incomplete'`; selects the header verb, glyph
46605
+ and accent. `incomplete` (21.1.0) is a call that ended without a result (an interrupted stream, a
46606
+ cancelled run): it reads `Did not finish using {name}` (`toolCallBlockHeaderIncomplete`), or
46607
+ `Incomplete` (`statusIncomplete`) in `status-text` beside a `label`, with a static glyph and the
46608
+ neutral accent. Values outside the set normalize and reflect as `pending`
46038
46609
  - `expanded: boolean = false` (reflected) — whether the details are shown
46039
46610
  - `label?: string` — header override, used verbatim (including `''`); when set, the localized
46040
46611
  status text renders beside it in `status-text`. Unset renders the localized status verb
@@ -46085,7 +46656,8 @@ set), `duration` (only while finite), `body` (the disclosed region), `args`, `ar
46085
46656
  header/body divider
46086
46657
  - `--lr-tool-call-block-radius` (default `var(--lr-radius)`) — card radius
46087
46658
  - `--lr-tool-call-block-accent` — status glyph colour; its private default follows `status`
46088
- (`--lr-color-text-quiet`, then brand while running, success, danger on error, warning when denied)
46659
+ (`--lr-color-text-quiet` while pending or incomplete, then brand while running, success, danger
46660
+ on error, warning when denied)
46089
46661
  - `--lr-tool-call-block-error-color` (default `var(--lr-color-danger)`) — error section text
46090
46662
 
46091
46663
  The running glyph spins and the pending glyph pulses at `--lr-transition-ambient`; both stop under
@@ -47161,7 +47733,9 @@ string; mimeType?: string; name?: string; children?: LyraSourceEntry[] }`; flat
47161
47733
  **Events:** `lr-sources-change` (`detail: { selectedSourceIds }`, the complete updated leaf-id array,
47162
47734
  fired after every toggle including select-all. Also fired when a `sources` reassignment prunes a
47163
47735
  previously-selected id that is no longer a valid leaf. Not fired when a consumer sets
47164
- `selectedSourceIds` directly; that assignment is normalized silently).
47736
+ `selectedSourceIds` directly; that assignment is normalized silently). The select-all checkbox's
47737
+ own `lr-checkbox-toggle-request`, native `input`/`change`, and `lr-input`/`lr-change` stay inside
47738
+ the component; `lr-sources-change` is the host-level report of that toggle.
47165
47739
 
47166
47740
  **Slots:** none.
47167
47741
 
@@ -48336,7 +48910,9 @@ region. Initial empty content, loading intermediates, and reconnects are not rep
48336
48910
  - `lr-select` (`detail: RetrievalResultsSelectDetail` = `{ chunkIds: string[]; chunks: RetrievalChunk[] }`)
48337
48911
  — the _complete_ updated selection, both as ids and as exactly one canonical record per id, so a
48338
48912
  host needn't re-look-up ids against its own copy on every toggle. This derived detail is always
48339
- canonicalized nonblank/first-wins regardless of the legacy `dedupe` switch.
48913
+ canonicalized nonblank/first-wins regardless of the legacy `dedupe` switch. It is the only
48914
+ host-level report of a row toggle: the row checkbox's `lr-checkbox-toggle-request`, native
48915
+ `input`/`change`, and `lr-input`/`lr-change` stay inside the component.
48340
48916
  - `lr-load-more` (`detail: null`) — from the virtual list's scroll-near-bottom detection while
48341
48917
  virtualized, or the `[part="load-more"]` button otherwise. Only fires while `hasMore` is true and
48342
48918
  `loading` is false.