@aceshooting/lyra-ui 5.2.0 → 6.0.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 (327) hide show
  1. package/CHANGELOG.md +3979 -0
  2. package/README.md +6 -5
  3. package/custom-elements.json +3580 -1047
  4. package/dist/components/agent-tools/artifact-panel/artifact-panel.class.d.ts +4 -2
  5. package/dist/components/agent-tools/artifact-panel/artifact-panel.class.d.ts.map +1 -1
  6. package/dist/components/agent-tools/artifact-panel/artifact-panel.class.js.map +1 -1
  7. package/dist/components/agent-tools/browser-frame/browser-frame.class.d.ts +7 -3
  8. package/dist/components/agent-tools/browser-frame/browser-frame.class.d.ts.map +1 -1
  9. package/dist/components/agent-tools/browser-frame/browser-frame.class.js.map +1 -1
  10. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.d.ts +36 -4
  11. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.d.ts.map +1 -1
  12. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.js +68 -19
  13. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.js.map +1 -1
  14. package/dist/components/agent-tools/confirm-bar/confirm-bar.d.ts +1 -0
  15. package/dist/components/agent-tools/confirm-bar/confirm-bar.d.ts.map +1 -1
  16. package/dist/components/agent-tools/confirm-bar/confirm-bar.js +1 -0
  17. package/dist/components/agent-tools/confirm-bar/confirm-bar.js.map +1 -1
  18. package/dist/components/agent-tools/confirm-bar/confirm-bar.styles.d.ts.map +1 -1
  19. package/dist/components/agent-tools/confirm-bar/confirm-bar.styles.js +10 -29
  20. package/dist/components/agent-tools/confirm-bar/confirm-bar.styles.js.map +1 -1
  21. package/dist/components/agent-tools/eval-dataset/eval-dataset.class.d.ts +7 -7
  22. package/dist/components/agent-tools/eval-dataset/eval-dataset.class.d.ts.map +1 -1
  23. package/dist/components/agent-tools/eval-dataset/eval-dataset.class.js +14 -13
  24. package/dist/components/agent-tools/eval-dataset/eval-dataset.class.js.map +1 -1
  25. package/dist/components/agent-tools/eval-dataset/eval-dataset.d.ts +1 -1
  26. package/dist/components/agent-tools/eval-dataset/eval-dataset.d.ts.map +1 -1
  27. package/dist/components/agent-tools/eval-dataset/eval-dataset.js +1 -1
  28. package/dist/components/agent-tools/eval-dataset/eval-dataset.js.map +1 -1
  29. package/dist/components/agent-tools/eval-result/eval-result.class.d.ts +11 -9
  30. package/dist/components/agent-tools/eval-result/eval-result.class.d.ts.map +1 -1
  31. package/dist/components/agent-tools/eval-result/eval-result.class.js +9 -7
  32. package/dist/components/agent-tools/eval-result/eval-result.class.js.map +1 -1
  33. package/dist/components/agent-tools/eval-result/eval-result.d.ts +1 -1
  34. package/dist/components/agent-tools/eval-result/eval-result.d.ts.map +1 -1
  35. package/dist/components/agent-tools/eval-result/eval-result.js +1 -1
  36. package/dist/components/agent-tools/eval-result/eval-result.js.map +1 -1
  37. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.d.ts +47 -5
  38. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.d.ts.map +1 -1
  39. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.js +90 -15
  40. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.js.map +1 -1
  41. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.d.ts +2 -1
  42. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.d.ts.map +1 -1
  43. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.js +2 -1
  44. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.js.map +1 -1
  45. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.styles.d.ts.map +1 -1
  46. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.styles.js +6 -26
  47. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.styles.js.map +1 -1
  48. package/dist/components/charts/chart/chart.class.d.ts.map +1 -1
  49. package/dist/components/charts/chart/chart.class.js.map +1 -1
  50. package/dist/components/charts/chart/lite-chart.class.d.ts +5 -1
  51. package/dist/components/charts/chart/lite-chart.class.d.ts.map +1 -1
  52. package/dist/components/charts/chart/lite-chart.class.js.map +1 -1
  53. package/dist/components/conversation/code-block/greycat-language.d.ts +5 -0
  54. package/dist/components/conversation/code-block/greycat-language.d.ts.map +1 -1
  55. package/dist/components/conversation/code-block/greycat-language.js +48 -6
  56. package/dist/components/conversation/code-block/greycat-language.js.map +1 -1
  57. package/dist/components/conversation/message-feedback/message-feedback.class.d.ts +5 -3
  58. package/dist/components/conversation/message-feedback/message-feedback.class.d.ts.map +1 -1
  59. package/dist/components/conversation/message-feedback/message-feedback.class.js.map +1 -1
  60. package/dist/components/conversation/thread-list/thread-list.class.d.ts +16 -3
  61. package/dist/components/conversation/thread-list/thread-list.class.d.ts.map +1 -1
  62. package/dist/components/conversation/thread-list/thread-list.class.js +13 -3
  63. package/dist/components/conversation/thread-list/thread-list.class.js.map +1 -1
  64. package/dist/components/data/calendar/calendar.class.d.ts +3 -1
  65. package/dist/components/data/calendar/calendar.class.d.ts.map +1 -1
  66. package/dist/components/data/calendar/calendar.class.js.map +1 -1
  67. package/dist/components/data/calendar/calendar.styles.js +3 -3
  68. package/dist/components/data/document-library/document-library.class.d.ts +6 -5
  69. package/dist/components/data/document-library/document-library.class.d.ts.map +1 -1
  70. package/dist/components/data/document-library/document-library.class.js +2 -3
  71. package/dist/components/data/document-library/document-library.class.js.map +1 -1
  72. package/dist/components/data/flow-canvas/flow-canvas.class.d.ts +8 -1
  73. package/dist/components/data/flow-canvas/flow-canvas.class.d.ts.map +1 -1
  74. package/dist/components/data/flow-canvas/flow-canvas.class.js +5 -0
  75. package/dist/components/data/flow-canvas/flow-canvas.class.js.map +1 -1
  76. package/dist/components/data/flow-canvas/flow-canvas.styles.d.ts.map +1 -1
  77. package/dist/components/data/flow-canvas/flow-canvas.styles.js +2 -1
  78. package/dist/components/data/flow-canvas/flow-canvas.styles.js.map +1 -1
  79. package/dist/components/data/heatmap/heatmap.class.d.ts +6 -2
  80. package/dist/components/data/heatmap/heatmap.class.d.ts.map +1 -1
  81. package/dist/components/data/heatmap/heatmap.class.js.map +1 -1
  82. package/dist/components/data/table/table.class.d.ts +16 -4
  83. package/dist/components/data/table/table.class.d.ts.map +1 -1
  84. package/dist/components/data/table/table.class.js.map +1 -1
  85. package/dist/components/data/tree/tree.class.d.ts +3 -1
  86. package/dist/components/data/tree/tree.class.d.ts.map +1 -1
  87. package/dist/components/data/tree/tree.class.js.map +1 -1
  88. package/dist/components/data/word-cloud/word-cloud-layout.d.ts +7 -2
  89. package/dist/components/data/word-cloud/word-cloud-layout.d.ts.map +1 -1
  90. package/dist/components/data/word-cloud/word-cloud-layout.js.map +1 -1
  91. package/dist/components/data/word-cloud/word-cloud.class.d.ts +3 -3
  92. package/dist/components/data/word-cloud/word-cloud.class.d.ts.map +1 -1
  93. package/dist/components/data/word-cloud/word-cloud.class.js.map +1 -1
  94. package/dist/components/forms/button/button.class.d.ts +5 -0
  95. package/dist/components/forms/button/button.class.d.ts.map +1 -1
  96. package/dist/components/forms/button/button.class.js +5 -0
  97. package/dist/components/forms/button/button.class.js.map +1 -1
  98. package/dist/components/forms/button/button.styles.d.ts.map +1 -1
  99. package/dist/components/forms/button/button.styles.js +6 -2
  100. package/dist/components/forms/button/button.styles.js.map +1 -1
  101. package/dist/components/forms/color-picker/color-picker.class.d.ts +4 -0
  102. package/dist/components/forms/color-picker/color-picker.class.d.ts.map +1 -1
  103. package/dist/components/forms/color-picker/color-picker.class.js +6 -0
  104. package/dist/components/forms/color-picker/color-picker.class.js.map +1 -1
  105. package/dist/components/forms/color-picker/color-picker.styles.d.ts.map +1 -1
  106. package/dist/components/forms/color-picker/color-picker.styles.js +20 -2
  107. package/dist/components/forms/color-picker/color-picker.styles.js.map +1 -1
  108. package/dist/components/forms/combobox/combobox.class.d.ts +2 -2
  109. package/dist/components/forms/combobox/combobox.class.d.ts.map +1 -1
  110. package/dist/components/forms/combobox/combobox.class.js +1 -1
  111. package/dist/components/forms/combobox/combobox.class.js.map +1 -1
  112. package/dist/components/forms/combobox/combobox.styles.d.ts.map +1 -1
  113. package/dist/components/forms/combobox/combobox.styles.js +8 -0
  114. package/dist/components/forms/combobox/combobox.styles.js.map +1 -1
  115. package/dist/components/forms/date-picker/date-picker.class.d.ts +4 -0
  116. package/dist/components/forms/date-picker/date-picker.class.d.ts.map +1 -1
  117. package/dist/components/forms/date-picker/date-picker.class.js +6 -0
  118. package/dist/components/forms/date-picker/date-picker.class.js.map +1 -1
  119. package/dist/components/forms/date-picker/date-picker.styles.d.ts.map +1 -1
  120. package/dist/components/forms/date-picker/date-picker.styles.js +15 -0
  121. package/dist/components/forms/date-picker/date-picker.styles.js.map +1 -1
  122. package/dist/components/forms/emoji-picker/emoji-picker.class.d.ts +9 -2
  123. package/dist/components/forms/emoji-picker/emoji-picker.class.d.ts.map +1 -1
  124. package/dist/components/forms/emoji-picker/emoji-picker.class.js +11 -2
  125. package/dist/components/forms/emoji-picker/emoji-picker.class.js.map +1 -1
  126. package/dist/components/forms/emoji-picker/emoji-picker.styles.d.ts.map +1 -1
  127. package/dist/components/forms/emoji-picker/emoji-picker.styles.js +30 -7
  128. package/dist/components/forms/emoji-picker/emoji-picker.styles.js.map +1 -1
  129. package/dist/components/forms/icon-button/icon-button.class.d.ts +20 -3
  130. package/dist/components/forms/icon-button/icon-button.class.d.ts.map +1 -1
  131. package/dist/components/forms/icon-button/icon-button.class.js +84 -4
  132. package/dist/components/forms/icon-button/icon-button.class.js.map +1 -1
  133. package/dist/components/forms/icon-button/icon-button.styles.d.ts.map +1 -1
  134. package/dist/components/forms/icon-button/icon-button.styles.js +4 -0
  135. package/dist/components/forms/icon-button/icon-button.styles.js.map +1 -1
  136. package/dist/components/forms/locale-picker/locale-picker.class.d.ts +221 -0
  137. package/dist/components/forms/locale-picker/locale-picker.class.d.ts.map +1 -0
  138. package/dist/components/forms/locale-picker/locale-picker.class.js +630 -0
  139. package/dist/components/forms/locale-picker/locale-picker.class.js.map +1 -0
  140. package/dist/components/forms/locale-picker/locale-picker.d.ts +3 -0
  141. package/dist/components/forms/locale-picker/locale-picker.d.ts.map +1 -0
  142. package/dist/components/forms/locale-picker/locale-picker.js +6 -0
  143. package/dist/components/forms/locale-picker/locale-picker.js.map +1 -0
  144. package/dist/components/forms/locale-picker/locale-picker.styles.d.ts +2 -0
  145. package/dist/components/forms/locale-picker/locale-picker.styles.d.ts.map +1 -0
  146. package/dist/components/forms/locale-picker/locale-picker.styles.js +213 -0
  147. package/dist/components/forms/locale-picker/locale-picker.styles.js.map +1 -0
  148. package/dist/components/forms/phone-input/phone-input.class.d.ts +11 -0
  149. package/dist/components/forms/phone-input/phone-input.class.d.ts.map +1 -1
  150. package/dist/components/forms/phone-input/phone-input.class.js +13 -0
  151. package/dist/components/forms/phone-input/phone-input.class.js.map +1 -1
  152. package/dist/components/forms/phone-input/phone-input.styles.d.ts.map +1 -1
  153. package/dist/components/forms/phone-input/phone-input.styles.js +39 -3
  154. package/dist/components/forms/phone-input/phone-input.styles.js.map +1 -1
  155. package/dist/components/forms/select/select.class.d.ts +2 -2
  156. package/dist/components/forms/select/select.class.d.ts.map +1 -1
  157. package/dist/components/forms/select/select.class.js +1 -1
  158. package/dist/components/forms/select/select.class.js.map +1 -1
  159. package/dist/components/forms/select/select.styles.d.ts.map +1 -1
  160. package/dist/components/forms/select/select.styles.js +6 -0
  161. package/dist/components/forms/select/select.styles.js.map +1 -1
  162. package/dist/components/forms/swatch-picker/swatch-picker.class.d.ts +12 -3
  163. package/dist/components/forms/swatch-picker/swatch-picker.class.d.ts.map +1 -1
  164. package/dist/components/forms/swatch-picker/swatch-picker.class.js +14 -3
  165. package/dist/components/forms/swatch-picker/swatch-picker.class.js.map +1 -1
  166. package/dist/components/forms/swatch-picker/swatch-picker.styles.d.ts.map +1 -1
  167. package/dist/components/forms/swatch-picker/swatch-picker.styles.js +32 -9
  168. package/dist/components/forms/swatch-picker/swatch-picker.styles.js.map +1 -1
  169. package/dist/components/forms/time-range/time-range.class.d.ts +8 -0
  170. package/dist/components/forms/time-range/time-range.class.d.ts.map +1 -1
  171. package/dist/components/forms/time-range/time-range.class.js +10 -0
  172. package/dist/components/forms/time-range/time-range.class.js.map +1 -1
  173. package/dist/components/forms/time-range/time-range.styles.d.ts.map +1 -1
  174. package/dist/components/forms/time-range/time-range.styles.js +42 -13
  175. package/dist/components/forms/time-range/time-range.styles.js.map +1 -1
  176. package/dist/components/forms/token-input/token-input.class.d.ts +11 -0
  177. package/dist/components/forms/token-input/token-input.class.d.ts.map +1 -1
  178. package/dist/components/forms/token-input/token-input.class.js +13 -0
  179. package/dist/components/forms/token-input/token-input.class.js.map +1 -1
  180. package/dist/components/forms/token-input/token-input.styles.d.ts.map +1 -1
  181. package/dist/components/forms/token-input/token-input.styles.js +36 -2
  182. package/dist/components/forms/token-input/token-input.styles.js.map +1 -1
  183. package/dist/components/layout/app-rail/app-rail.class.d.ts +6 -2
  184. package/dist/components/layout/app-rail/app-rail.class.d.ts.map +1 -1
  185. package/dist/components/layout/app-rail/app-rail.class.js.map +1 -1
  186. package/dist/components/layout/control-group/control-group.class.d.ts +16 -0
  187. package/dist/components/layout/control-group/control-group.class.d.ts.map +1 -1
  188. package/dist/components/layout/control-group/control-group.class.js +19 -0
  189. package/dist/components/layout/control-group/control-group.class.js.map +1 -1
  190. package/dist/components/layout/control-group/control-group.styles.d.ts.map +1 -1
  191. package/dist/components/layout/control-group/control-group.styles.js +8 -3
  192. package/dist/components/layout/control-group/control-group.styles.js.map +1 -1
  193. package/dist/components/layout/dashboard-grid/dashboard-grid.class.d.ts +3 -0
  194. package/dist/components/layout/dashboard-grid/dashboard-grid.class.d.ts.map +1 -1
  195. package/dist/components/layout/dashboard-grid/dashboard-grid.class.js +3 -0
  196. package/dist/components/layout/dashboard-grid/dashboard-grid.class.js.map +1 -1
  197. package/dist/components/layout/dashboard-grid/dashboard-grid.styles.d.ts.map +1 -1
  198. package/dist/components/layout/dashboard-grid/dashboard-grid.styles.js +2 -1
  199. package/dist/components/layout/dashboard-grid/dashboard-grid.styles.js.map +1 -1
  200. package/dist/components/layout/reorder-list/reorder-item.class.d.ts +59 -0
  201. package/dist/components/layout/reorder-list/reorder-item.class.d.ts.map +1 -0
  202. package/dist/components/layout/reorder-list/reorder-item.class.js +110 -0
  203. package/dist/components/layout/reorder-list/reorder-item.class.js.map +1 -0
  204. package/dist/components/layout/reorder-list/reorder-item.d.ts +2 -0
  205. package/dist/components/layout/reorder-list/reorder-item.d.ts.map +1 -0
  206. package/dist/components/layout/reorder-list/reorder-item.js +5 -0
  207. package/dist/components/layout/reorder-list/reorder-item.js.map +1 -0
  208. package/dist/components/{data/data-grid/data-grid.styles.d.ts → layout/reorder-list/reorder-item.styles.d.ts} +1 -1
  209. package/dist/components/layout/reorder-list/reorder-item.styles.d.ts.map +1 -0
  210. package/dist/components/layout/reorder-list/reorder-item.styles.js +62 -0
  211. package/dist/components/layout/reorder-list/reorder-item.styles.js.map +1 -0
  212. package/dist/components/layout/reorder-list/reorder-list.class.d.ts +77 -0
  213. package/dist/components/layout/reorder-list/reorder-list.class.d.ts.map +1 -0
  214. package/dist/components/layout/reorder-list/reorder-list.class.js +197 -0
  215. package/dist/components/layout/reorder-list/reorder-list.class.js.map +1 -0
  216. package/dist/components/layout/reorder-list/reorder-list.d.ts +3 -0
  217. package/dist/components/layout/reorder-list/reorder-list.d.ts.map +1 -0
  218. package/dist/components/layout/reorder-list/reorder-list.js +6 -0
  219. package/dist/components/layout/reorder-list/reorder-list.js.map +1 -0
  220. package/dist/components/layout/reorder-list/reorder-list.styles.d.ts +2 -0
  221. package/dist/components/layout/reorder-list/reorder-list.styles.d.ts.map +1 -0
  222. package/dist/components/layout/reorder-list/reorder-list.styles.js +12 -0
  223. package/dist/components/layout/reorder-list/reorder-list.styles.js.map +1 -0
  224. package/dist/components/layout/virtual-list/virtual-list.class.d.ts +7 -1
  225. package/dist/components/layout/virtual-list/virtual-list.class.d.ts.map +1 -1
  226. package/dist/components/layout/virtual-list/virtual-list.class.js +3 -0
  227. package/dist/components/layout/virtual-list/virtual-list.class.js.map +1 -1
  228. package/dist/components/layout/virtual-list/virtual-list.styles.d.ts.map +1 -1
  229. package/dist/components/layout/virtual-list/virtual-list.styles.js +2 -1
  230. package/dist/components/layout/virtual-list/virtual-list.styles.js.map +1 -1
  231. package/dist/components/overlays/chip/chip.class.d.ts +2 -2
  232. package/dist/components/overlays/chip/chip.class.d.ts.map +1 -1
  233. package/dist/components/overlays/chip/chip.class.js +1 -1
  234. package/dist/components/overlays/chip/chip.styles.d.ts.map +1 -1
  235. package/dist/components/overlays/chip/chip.styles.js +10 -0
  236. package/dist/components/overlays/chip/chip.styles.js.map +1 -1
  237. package/dist/components/overlays/overlay/popover.class.d.ts +3 -1
  238. package/dist/components/overlays/overlay/popover.class.d.ts.map +1 -1
  239. package/dist/components/overlays/overlay/popover.class.js.map +1 -1
  240. package/dist/components/retrieval/graph/graph.class.d.ts +7 -3
  241. package/dist/components/retrieval/graph/graph.class.d.ts.map +1 -1
  242. package/dist/components/retrieval/graph/graph.class.js.map +1 -1
  243. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +6 -3
  244. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts.map +1 -1
  245. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js.map +1 -1
  246. package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts +4 -2
  247. package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts.map +1 -1
  248. package/dist/components/retrieval/retrieval-results/retrieval-results.class.js.map +1 -1
  249. package/dist/components/utility/diff-view/diff-view.class.d.ts +4 -1
  250. package/dist/components/utility/diff-view/diff-view.class.d.ts.map +1 -1
  251. package/dist/components/utility/diff-view/diff-view.class.js.map +1 -1
  252. package/dist/components/utility/format/relative-time.class.d.ts.map +1 -1
  253. package/dist/components/utility/format/relative-time.class.js +4 -1
  254. package/dist/components/utility/format/relative-time.class.js.map +1 -1
  255. package/dist/components/viewers/document-compare/document-compare.class.d.ts +9 -2
  256. package/dist/components/viewers/document-compare/document-compare.class.d.ts.map +1 -1
  257. package/dist/components/viewers/document-compare/document-compare.class.js.map +1 -1
  258. package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +6 -0
  259. package/dist/components/viewers/geojson-view/geojson-view.class.d.ts.map +1 -1
  260. package/dist/components/viewers/geojson-view/geojson-view.class.js.map +1 -1
  261. package/dist/components/viewers/notebook-viewer/notebook-viewer.class.d.ts.map +1 -1
  262. package/dist/components/viewers/notebook-viewer/notebook-viewer.class.js.map +1 -1
  263. package/dist/internal/localization.d.ts +11 -1
  264. package/dist/internal/localization.d.ts.map +1 -1
  265. package/dist/internal/localization.js +24 -1
  266. package/dist/internal/localization.js.map +1 -1
  267. package/dist/internal/root-registration-allowlist.d.ts +1 -1
  268. package/dist/internal/root-registration-allowlist.d.ts.map +1 -1
  269. package/dist/internal/root-registration-allowlist.js +3 -1
  270. package/dist/internal/root-registration-allowlist.js.map +1 -1
  271. package/dist/internal/tokens.styles.d.ts.map +1 -1
  272. package/dist/internal/tokens.styles.js +1 -0
  273. package/dist/internal/tokens.styles.js.map +1 -1
  274. package/dist/lyra.d.ts +11 -5
  275. package/dist/lyra.d.ts.map +1 -1
  276. package/dist/lyra.js +7 -3
  277. package/dist/lyra.js.map +1 -1
  278. package/dist/theme.css +1 -0
  279. package/llms/components/lr-button.md +4 -1
  280. package/llms/components/lr-chip-group.md +1 -1
  281. package/llms/components/lr-chip.md +1 -1
  282. package/llms/components/lr-color-picker.md +7 -1
  283. package/llms/components/lr-combobox.md +1 -1
  284. package/llms/components/lr-confirm-bar.md +31 -4
  285. package/llms/components/lr-control-group.md +12 -4
  286. package/llms/components/lr-dashboard-grid.md +4 -1
  287. package/llms/components/lr-date-input.md +5 -1
  288. package/llms/components/lr-date-picker.md +5 -1
  289. package/llms/components/lr-emoji-picker.md +11 -6
  290. package/llms/components/lr-eval-dataset.md +1 -1
  291. package/llms/components/lr-eval-result.md +4 -4
  292. package/llms/components/lr-flag.md +9 -5
  293. package/llms/components/lr-flow-canvas.md +7 -1
  294. package/llms/components/lr-icon-button.md +10 -5
  295. package/llms/components/lr-locale-picker.md +86 -0
  296. package/llms/components/lr-option.md +1 -1
  297. package/llms/components/lr-phone-input.md +10 -3
  298. package/llms/components/lr-reorder-item.md +81 -0
  299. package/llms/components/lr-reorder-list.md +81 -0
  300. package/llms/components/lr-select.md +2 -2
  301. package/llms/components/lr-swatch-picker.md +15 -9
  302. package/llms/components/lr-thread-list.md +11 -6
  303. package/llms/components/lr-time-range.md +8 -0
  304. package/llms/components/lr-token-input.md +14 -6
  305. package/llms/components/lr-tool-approval-dialog.md +30 -6
  306. package/llms/components/lr-virtual-list.md +5 -1
  307. package/llms/index.md +7 -5
  308. package/llms/shared.md +13 -4
  309. package/llms/tokens.md +4 -2
  310. package/llms-full.txt +362 -120
  311. package/llms.txt +4 -4
  312. package/package.json +8 -3
  313. package/vscode-css-data.json +183 -8
  314. package/vscode-html-data.json +521 -366
  315. package/web-types.json +810 -96
  316. package/dist/components/data/data-grid/data-grid.class.d.ts +0 -69
  317. package/dist/components/data/data-grid/data-grid.class.d.ts.map +0 -1
  318. package/dist/components/data/data-grid/data-grid.class.js +0 -123
  319. package/dist/components/data/data-grid/data-grid.class.js.map +0 -1
  320. package/dist/components/data/data-grid/data-grid.d.ts +0 -2
  321. package/dist/components/data/data-grid/data-grid.d.ts.map +0 -1
  322. package/dist/components/data/data-grid/data-grid.js +0 -5
  323. package/dist/components/data/data-grid/data-grid.js.map +0 -1
  324. package/dist/components/data/data-grid/data-grid.styles.d.ts.map +0 -1
  325. package/dist/components/data/data-grid/data-grid.styles.js +0 -15
  326. package/dist/components/data/data-grid/data-grid.styles.js.map +0 -1
  327. package/llms/components/lr-data-grid.md +0 -55
package/CHANGELOG.md ADDED
@@ -0,0 +1,3979 @@
1
+ # Changelog
2
+
3
+ ## 6.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 5c93d1b: Remove `<lr-data-grid>`. It was a strict functional subset of `<lr-table>` (same `role="grid"` +
8
+ roving-tabindex + sort/select/loading pattern, with none of `<lr-table>`'s filtering, pagination,
9
+ inline editing, resize, grouping, expansion, heat-tint, sticky columns, or footers), implemented
10
+ independently with no shared code. Use `<lr-table>` instead:
11
+
12
+ - `DataGridColumn<T>`'s optional `value(row)` becomes `TableColumn<T>`'s required `cell(row)`.
13
+ - `<lr-data-grid>`'s `emptyText` string becomes `<lr-table>`'s `emptyHeading`/`emptyDescription`
14
+ pair (rendered via an internal `<lr-empty>`, not a plain text cell).
15
+ - `<lr-data-grid>` always mutated `selectedKey` and emitted `lr-selection-change` on row
16
+ click/activation; `<lr-table>` only does that when `selection-mode` is `"single"` or
17
+ `"multiple"` (default `"none"`, presentational) — listen on `lr-row-click` (`detail: { row }`)
18
+ instead if you don't need `<lr-table>`'s own selection bookkeeping.
19
+ - `accessibleLabel`/`aria-label` — unchanged; `<lr-table>` reads a plain `aria-label` attribute the
20
+ same way.
21
+
22
+ `<lr-eval-dataset>` and `<lr-eval-result>` composed `<lr-data-grid>` internally and now compose
23
+ `<lr-table>` instead. `<lr-eval-result>`'s public `columns` property changes type accordingly from
24
+ `DataGridColumn<EvalRunResult>[]` to `TableColumn<EvalRunResult>[]` — update any `value(row)`
25
+ column definitions you pass in to `cell(row)`.
26
+
27
+ ### Minor Changes
28
+
29
+ - 5c93d1b: Add a `3xs` size tier to `<lr-chip>`, one step below `2xs`, for dense inline count pills.
30
+ - 8e6e045: `lr-confirm-bar`: swap the hand-rolled `deny-button`/`approve-button` native `<button>`s for
31
+ `<lr-button>`, so `--lr-button-*` theming and a consumer's existing `lr-button` style fragments
32
+ reach them like every other button in an app. Adds a host-writable
33
+ `pending: 'approved' | 'denied' | null` property and makes `lr-approve`/`lr-deny` cancelable: a
34
+ listener calling `preventDefault()` sets `pending` to the decision being made (showing `loading`
35
+ on that button, `disabled` on the other) instead of resolving synchronously, so a host whose
36
+ approval hits a network call can keep the UI honest about being in flight. Finalize by setting
37
+ `.decision`, or bounce back by clearing `.pending` to `null`.
38
+
39
+ **Breaking (CSS only):** `::part(deny-button)`/`::part(approve-button)` now select an `<lr-button>`
40
+ host, not a native `<button>`.
41
+
42
+ Before:
43
+ lr-confirm-bar::part(deny-button) { padding: 4px 8px; border: ...; }
44
+ After (use the re-exported sub-parts):
45
+ lr-confirm-bar::part(deny-button-base) { padding: 4px 8px; border: ...; }
46
+
47
+ Runtime API (events, `tone`, `compact`, slots, the new `pending` property) is unchanged.
48
+
49
+ - 050c43c: Fix `<lr-control-group>` collapsing to 0 inline size when placed as an ordinary flex-basis:auto
50
+ child of a shrink-to-fit flex row (its own stated primary use case). The `@container`
51
+ narrow-allocation breakpoint is now opt-in via a new `responsive` property instead of always-on.
52
+ - 18e7b10: Add `--lr-dashboard-grid-cell-hover-outline-color` to `<lr-dashboard-grid>`, theming the mouse-hover preview outline on `[part="cell"]` independently of the shared `--lr-color-border-strong` token. Set it to `transparent` to opt out of the hover treatment entirely.
53
+ - bd2e594: Add `--lr-flow-canvas-node-hover-outline-color` to `<lr-flow-canvas>`, theming the mouse-hover preview outline on `[part="node"]` independently of the shared `--lr-color-border-strong` token. Set it to `transparent` to opt out of the hover treatment entirely.
54
+ - 76690c7: Add `--lr-button-gap` and `--lr-button-radius` custom properties to `<lr-button>`, so the
55
+ icon/label gap and corner radius are retunable without a `::part(base)` rule — matching the
56
+ retunable-without-`::part()` treatment `--lr-button-padding-block/-inline` and
57
+ `--lr-button-font-size` already have.
58
+ - f8810d7: Add `<lr-locale-picker>`: a closed-list locale switcher over the locale registry
59
+ (`getRegisteredLyraLocales()`) or an explicit `locales` catalog, form-associated and mirroring
60
+ `<lr-select>`'s hand-rolled listbox. Selecting a row emits a cancelable `lr-change` and, unless
61
+ vetoed, applies the pick via `setLyraLocale()`.
62
+ - 77377ed: Add `getRegisteredLyraLocales()` and `subscribeLyraLocaleRegistry()` so a consumer can enumerate
63
+ and live-track every locale registered via `registerLyraLocale()` (plus `'en'`) — the piece that
64
+ unblocks a locale-picker component built on top of the existing locale runtime.
65
+ - 0771a83: Add a `renderExcerpt` hook to `<lr-thread-list>`, rendering rich per-row excerpt content into the
66
+ row `<lr-conversation-item>`'s own `excerpt` slot — where it wins over the plain-string `excerpt`
67
+ property — for cases like a server-highlighted search-match snippet, without giving up the built-in
68
+ title layout and inline-rename affordance the way `renderRowContent` requires.
69
+ - 02cd69d: Add `<lr-reorder-item>`, one row of the new `<lr-reorder-list>` flat-list reorder primitive.
70
+ - 2cf4206: Add `<lr-reorder-list>`, a generic flat-list reorder primitive with move-up/move-down buttons and
71
+ a Ctrl/Cmd+ArrowUp/ArrowDown keyboard shortcut, emitting the full new order on every move.
72
+ - c6af1b7: `lr-tool-approval-dialog`: swap the hand-rolled `deny-button`/`approve-button` native `<button>`s
73
+ for `<lr-button>` (`variant="neutral"`/`"brand"`), so `--lr-button-*` theming and a consumer's
74
+ existing `lr-button` style fragments reach them like every other button in an app. Adds a
75
+ host-writable `pending: 'approve' | 'deny' | null` property and makes `lr-approve`/`lr-deny`
76
+ cancelable: a listener calling `preventDefault()` sets `pending` to the decision being made
77
+ (showing `loading` on that button, `disabled` on the other) instead of closing immediately, so a
78
+ host whose approval hits a network call can keep the dialog honest about being in flight.
79
+ Finalize by calling `close('approve'|'deny')`, or bounce back by clearing `.pending` to `null`.
80
+ While `pending` is set, Escape and backdrop dismissal are suppressed; `pending` itself resets to
81
+ `null` every time the dialog re-opens. The `edit-button` is unaffected.
82
+
83
+ **Breaking (CSS only):** `::part(deny-button)`/`::part(approve-button)` now select an `<lr-button>`
84
+ host, not a native `<button>`.
85
+
86
+ Before:
87
+ lr-tool-approval-dialog::part(deny-button) { padding: 4px 8px; border: ...; }
88
+ After (use the re-exported sub-parts):
89
+ lr-tool-approval-dialog::part(deny-button-base) { padding: 4px 8px; border: ...; }
90
+
91
+ Runtime API (events, `editable` and its editing behavior, slots, the new `pending` property) is
92
+ unchanged.
93
+
94
+ - 65d6a2b: Add `--lr-virtual-list-hover-outline-color` to `<lr-virtual-list>`, theming the mouse-hover preview outline on `[part="base"]` independently of the shared `--lr-color-border-strong` token. Set it to `transparent` to opt out of the hover treatment entirely.
95
+
96
+ ### Patch Changes
97
+
98
+ - 3fd9bbd: Regenerate the agent-tools reference docs and custom-elements manifest for the `lr-confirm-bar`/
99
+ `lr-tool-approval-dialog` `lr-button` swap and their new `pending` properties (see the sibling
100
+ changesets for the runtime changes themselves).
101
+ - 9ebb38c: `lr-icon-button`: restore rendering for slotted bare SVG geometry (`<path>`, `<circle>`, etc. with
102
+ no enclosing `<svg>`) when `icon` is unset. 5.2.0's natural-aspect-ratio change made the default
103
+ slot a sibling of the internal glyph instead of nesting it inside an SVG, which silently stopped
104
+ this narrow case from painting (no console error, no type error). A small whitelist of raw SVG
105
+ geometry tag names is now cloned into a real SVG-namespaced element the same way `<lr-icon>`'s own
106
+ custom-content slot already does — every other case (complete `<svg>`, `<img>`, custom elements) is
107
+ untouched, so the `createElementNS`-on-custom-elements bug 5.2.0 fixed for `<lr-flag>` cannot
108
+ regress.
109
+
110
+ ## 5.2.0
111
+
112
+ ### Minor Changes
113
+
114
+ - 602177a: `lr-button`: expose its per-size geometry and its outlined fill as custom properties, so a consumer
115
+ no longer needs a `::part(base)` rule to fit a button into a dense toolbar or to tint an outline.
116
+
117
+ - `--lr-button-padding-block`, `--lr-button-padding-inline` and `--lr-button-font-size` now carry
118
+ each `size` tier's padding/font-size (the `:host` defaults are the `m` tier). Every tier is now
119
+ pure custom-property re-assignment — matching `lr-input`, `lr-select`, `lr-combobox`,
120
+ `lr-segmented` and `lr-date-input` — so overriding one knob retunes the tier instead of fighting
121
+ the stylesheet.
122
+ - `--lr-button-min-height` carries the active tier's `min-block-size` floor (it resolves to that
123
+ tier's existing `--lr-button-size-*` token), and the new `--lr-button-height` pins an exact
124
+ height — flooring _and_ capping the button, e.g. to match a fixed toolbar row. It is deliberately
125
+ left undeclared by default so each tier's floor still applies when it is unset.
126
+ - `--lr-button-outlined-fill` (default `transparent`) tints `appearance="outlined"`. Like
127
+ `--lr-button-quiet-*` it is not swapped per `variant`. Note that the existing hover
128
+ `filter: brightness()` visibly brightens a tinted fill, where a transparent one showed no change.
129
+
130
+ `appearance="link"` continues to ignore all of these and render as zero-chrome inline text. With
131
+ every property unset, all six tiers render byte-identical to before.
132
+
133
+ - fe06b7d: `lr-card`: `interactive` now grants real activation semantics when `href` is not also set.
134
+ `[part='base']` becomes focusable (`tabindex="0"`), responds to Enter and Space (Space calls
135
+ `preventDefault()` so the page does not scroll under the focused card), and emits a new
136
+ `lr-card-activate` event (no detail) — so a clickable tile no longer needs a consumer-supplied
137
+ wrapper or a `::part(base)` hack to be keyboard-operable.
138
+
139
+ - The card deliberately carries **no** `role="button"`. A card is a container that routinely holds
140
+ slotted buttons and links, and `role="button"` around focusable descendants is the axe-core
141
+ `nested-interactive` violation this library's own a11y gate enforces (unlike `lr-chip`'s
142
+ `toggleable` mode, which can forbid focusable children and therefore can carry the role).
143
+ - Because of that, "did the user aim at the card or at a control inside it?" is answered at event
144
+ time: the handler walks `composedPath()` from the original target up to `[part='base']` and bails
145
+ out if anything on the way is itself a control (a link, button, form control, `[tabindex]`, or an
146
+ interactive `role`). A click on a slotted `lr-button` or `<a>` therefore never activates the card.
147
+ - With `href` set, the root is still a real `<a>`: native navigation remains the activation, no
148
+ extra `tabindex` is added, and `lr-card-activate` is never fired.
149
+ - Without `interactive`, the rendered output is unchanged — no `tabindex`, no listeners, no events.
150
+
151
+ - 76b4ef7: `lr-chat-composer` gains `appearance="plain"` (reflected, `'card' | 'plain'`, default `'card'`), so a
152
+ composer docked inside a chat panel, dialog footer or toolbar that already draws its own border
153
+ doesn't double the frame. `plain` drops `[part="base"]`'s border, background, padding and corner
154
+ radius; the row layout, disabled treatment and the send/stop button's own chrome are unaffected.
155
+
156
+ Focus stays visible either way. The card's only focus affordance is a border-color shift, and there
157
+ is no border left to recolor under `plain` (the internal textarea sets `outline: none`), so `plain`
158
+ swaps in an underline across the input row instead — drawn as an inset box-shadow from
159
+ `--lr-focus-ring-width`/`--lr-focus-ring-color`, so it costs no layout.
160
+
161
+ An unset composer renders byte-identically to before.
162
+
163
+ - 89dc89a: Add density and chrome-less escape hatches to six card-chrome components so an embedded card no
164
+ longer forces its own frame on a host that already draws one:
165
+
166
+ - `lr-agent-run`, `lr-entity-card`, `lr-source-card` each gain both a reflected `compact` boolean
167
+ (tighter padding/gap, tunable via `--lr-<component>-compact-padding` / `-gap`) and
168
+ `appearance="plain"` (drops border, background, padding and radius). `plain` wins over `compact`
169
+ when both are set.
170
+ - `lr-stack-trace` and `lr-flow-run-overlay` gain `appearance="plain"` — for nesting inside an
171
+ `lr-result-card` / `lr-agent-run` or a host toolbar that already draws a border, without doubling
172
+ the frame. `lr-flow-run-overlay`'s `plain` also drops its floating-surface shadow.
173
+ - `lr-file-input` gains a reflected `compact` boolean (tighter dropzone padding, gap and label
174
+ font, tunable via `--lr-file-input-compact-padding` / `-gap` / `-font-size`) so the dropzone fits
175
+ a toolbar or table cell.
176
+
177
+ All escapes default off; an unset component renders byte-identically to before. Interactive
178
+ affordances that live on child controls (agent-run's Cancel/Retry, stack-trace's copy/frame
179
+ buttons, source-card's title/toggle) keep their own chrome under `plain`.
180
+
181
+ - bca1353: `lr-flow-controls` gains `appearance="plain"` (reflected, `'card' | 'plain'`, default `'card'`), for
182
+ clusters placed in a host toolbar or panel that already draws its own surface. `plain` drops
183
+ `[part="base"]`'s border, background, padding, corner radius **and** its floating-surface
184
+ `box-shadow` — a lift shadow with no surface under it reads as a stray smudge — matching what
185
+ `lr-flow-run-overlay`'s `plain` already does.
186
+
187
+ The cluster keeps its layout, its `orientation` axis, every button's shared minimum hit area
188
+ (`--lr-icon-button-size`) and their hover/focus rings. No `compact` is offered: the padding is
189
+ already the smallest spacing step, and the only remaining room is that hit-area floor.
190
+
191
+ The existing `for`, `orientation` and `hideLock` properties are now documented too. An unset cluster
192
+ renders byte-identically to before.
193
+
194
+ - ddf52ba: `lr-flow-node` now exposes its card as a CSS part and gains a density escape:
195
+
196
+ - The bordered, filled card is reachable as `::part(card)` (it keeps its `.card` class, so nothing
197
+ that already targeted it changes).
198
+ - New reflected `compact` boolean tightens the card padding for dense canvases and palette previews,
199
+ tunable via `--lr-flow-node-compact-padding` (default `var(--lr-space-xs)`) and
200
+ `--lr-flow-node-compact-gap` (default `var(--lr-space-2xs)`). The border, background, shadow and
201
+ the `selected`/`status="running"` treatments all stay.
202
+
203
+ Two documentation/CSS bugs are fixed in passing: the `base` part is documented as what it actually
204
+ is (the row wrapping the handles and the card, carrying no chrome of its own), and a duplicated
205
+ `min-inline-size: 0` that overrode the card's own minimum width is removed — the documented
206
+ `--lr-flow-node-min-inline-size` custom property was dead until now and once again sets the card's
207
+ minimum inline size (default `11rem`).
208
+
209
+ An unset node renders as before apart from that restored minimum width.
210
+
211
+ - 81f615b: `lr-checkbox` / `lr-radio`: publish the label indent, and stop hard-sizing the radio's circle.
212
+
213
+ - **New `--lr-checkbox-label-indent` and `--lr-radio-label-indent`** carry the distance from the
214
+ control's start edge to the start of the label — the box/circle's own floor
215
+ (`min(--lr-icon-button-size, 1.75rem)`) plus the label gap (`--lr-space-s`), i.e. `2.25rem` at the
216
+ default tokens. Consumers composing per-option hint text under a checkbox previously had to
217
+ hardcode that `2.25rem` after reading it out of the shadow styles, where neither term was a public
218
+ contract, so the hint silently de-aligned on any retheme. `[part='base']`'s `gap` is now _derived
219
+ from_ the published property rather than repeating `--lr-space-s`, so the advertised value and the
220
+ rendered geometry cannot drift: setting the property moves the label. Rendering is byte-identical
221
+ when it is left unset.
222
+
223
+ **Read this before assuming it closes the filed case.** The property is declared on the
224
+ component's `:host`, so it is readable by the element itself and overridable from your own
225
+ stylesheet (`lr-checkbox { --lr-checkbox-label-indent: … }` beats a `:host` rule), but custom
226
+ properties inherit _down_, not sideways — a **sibling** `<p>` in your own tree can never read it
227
+ off the checkbox. What actually solves that case is the `--lr-theme-icon-button-size` bridge that
228
+ landed alongside this release: compute `calc(min(var(--lr-theme-icon-button-size, 2.5rem), 1.75rem)
229
+
230
+ - var(--lr-theme-space-s, 0.5rem))` on your own wrapper from tokens you control, and both the
231
+ control and your hint text stay aligned through a retheme. The new "Aligning per-option hint text"
232
+ stories show both halves. This is not an unfixed gap; please do not re-file it as one.
233
+
234
+ - **Bug fix — `lr-radio`'s `[part='circle']` was hard-sized**, with `inline-size`/`block-size` where
235
+ `lr-checkbox`'s `[part='box']` correctly uses `min-inline-size`/`min-block-size`. Since
236
+ `[part='base']` carries no box of its own, that circle _is_ the entire tap target for a label-less
237
+ radio, and a hard size can be smaller than its own content — an enlarged indicator overflowed it
238
+ instead of growing it. It is now a floor, matching `lr-checkbox` exactly. Default rendering is
239
+ unchanged (28×28 at the default tokens, above the WCAG 2.2 SC 2.5.8 24×24 minimum).
240
+
241
+ Note the residual, unchanged in this release: neither control guarantees the 24×24 minimum once
242
+ `--lr-icon-button-size` is themed below it — `min()` still tracks the token down 1:1. Both
243
+ controls behave identically here; a hard floor would need its own decision, since it would also
244
+ block a deliberately dense checkbox.
245
+
246
+ `lr-checkbox` deliberately still has no `hint`/`errorText` chrome of its own (see its class docs);
247
+ that omission is intentional and adding it would require a `form-control` wrapper that changes the
248
+ part structure for existing consumers.
249
+
250
+ - 6bf969f: Themeable code tab width, chat bubble geometry, and the code-block active-line outline color.
251
+
252
+ - `--lr-code-block-tab-size` (default `2`) sets the tab width of rendered code. It is honoured by
253
+ `lr-code-block`, `lr-code-block-core`, `lr-markdown`, and `lr-markdown-core`, and shares the
254
+ default of the existing `--lr-code-editor-tab-size`, so the editable and read-only code surfaces
255
+ agree. The markdown viewers declare it themselves because they are sibling elements of
256
+ `lr-code-block`, not descendants — one declaration could not have reached them. `lr-code-block`
257
+ reads the token rather than writing `tab-size` inline, so the override survives shiki's own
258
+ inline `style` on the highlighted `<pre>`. Note that a markdown code block wraps
259
+ (`white-space: pre-wrap`) while `lr-code-block` does not, so the same value can render
260
+ differently on a wrapped line, where tab stops restart.
261
+ - `--lr-chat-message-bubble-padding` (default `var(--lr-space-m)`) and
262
+ `--lr-chat-message-bubble-radius` (default `var(--lr-radius)`) reshape `lr-chat-message`'s
263
+ bubble. Use these instead of a `::part(bubble)` padding/radius override: an outer-tree `::part`
264
+ declaration outranks every rule inside the component's shadow tree, which silently suppressed
265
+ the per-`status` (`failed`, `streaming`) and per-role bubble treatments. The radius prop is
266
+ bubble-only — `collapse-button` and `retry-button` keep reading the shared `--lr-radius`.
267
+ - `--lr-code-block-active-line-outline-color` (default `var(--lr-color-brand)`) retints only the
268
+ outline of the line marked active by `active-highlight-id`, leaving the language pill, hover,
269
+ and focus surfaces on `--lr-color-brand`.
270
+
271
+ All three default to exactly today's rendering, so a consumer who overrides none of them sees no
272
+ visual change.
273
+
274
+ - fe06b7d: `lr-confirm-bar`: new reflected `compact` property that collapses the bar from a full card
275
+ (bordered, padded, `display: block` surface) into a chrome-less inline row, for a confirmation that
276
+ has to live inside an existing container — a table cell, a card's action row, a toolbar.
277
+
278
+ - The **host** flips to `inline-flex` under `[compact]`, not just `[part='base']`: restyling the
279
+ part alone still leaves a `display: block` host that breaks the row it was dropped into.
280
+ - The narrow-allocation container query is switched off with it (`container-type: normal`). A
281
+ compact bar is _expected_ to be narrow, so leaving the query live would fire it essentially
282
+ always and stretch the Deny/Approve buttons to fill — the opposite of the intent.
283
+ - Re-chrome it through `--lr-confirm-bar-compact-padding` (default `0`),
284
+ `--lr-confirm-bar-compact-gap` (default `var(--lr-space-s)`), `--lr-confirm-bar-compact-border`
285
+ (default `none`), `--lr-confirm-bar-compact-radius` (default `0`) and
286
+ `--lr-confirm-bar-compact-background` (default `transparent`).
287
+ - Everything else is unchanged: `lr-approve`/`lr-deny` shapes, `role="group"` and its heading
288
+ label, and the contract that focus moves synchronously to `[part='status']` _before_ the
289
+ Deny/Approve buttons unmount. Leaving `compact` unset renders exactly as before.
290
+
291
+ - 49e0738: `lr-conversation-item` gains a `compact` density flag
292
+
293
+ A reflected boolean `compact` (default `false`, matching `lr-empty`'s convention) tightens
294
+ `[part='base']`'s padding from `var(--lr-space-s) var(--lr-space-m)` to
295
+ `var(--lr-space-xs) var(--lr-space-s)`, its gap from `var(--lr-space-xs)` to `var(--lr-space-2xs)`,
296
+ and collapses `[part='content']`'s inter-line gap to `0`. Both tuned values sit behind the new
297
+ `--lr-conversation-item-compact-padding` / `--lr-conversation-item-compact-gap` custom properties —
298
+ declared as inline `var()` fallbacks at the point of use, never on `:host`, so a surrounding list can
299
+ retune every row at once from an ancestor. Unset, a row renders exactly as before.
300
+
301
+ Nothing else changes. In particular `[part='rename-button']` keeps its
302
+ `min-inline-size`/`min-block-size: var(--lr-icon-button-size)` floor under `compact`, so a density
303
+ flag can never silently drop a row's icon target below the shared minimum; the excerpt stays visible
304
+ (it is already single-line ellipsised and `hidden`-bindable per row) and the excerpt/timestamp font
305
+ sizes stay at their existing steps. `:host([compact]) [part='base']` is ordered before
306
+ `:host([active]) [part='base']`, which is equal specificity, so an active row keeps its background
307
+ and its promoted excerpt/timestamp contrast when both are set.
308
+
309
+ - 3737d4c: Add consumer-settable CSS custom properties for state-styled surfaces in the data and agent-tools
310
+ families that previously took their color straight from a library-wide `--lr-color-*` token with no
311
+ component-scoped indirection. Because CSS Shadow Parts forbids an attribute selector after `::part()`
312
+ (`::part(row)[aria-selected]` is invalid), these states could only be restyled by hijacking the
313
+ shared token, which repaints everything else that reads it. Each new property uses an inline
314
+ `var()` fallback to its old token value, so an unset consumer renders byte-identically to before:
315
+
316
+ - `lr-data-grid`: `--lr-data-grid-row-selected-bg` (selected row background).
317
+ - `lr-env-list`: `--lr-env-list-reveal-active-bg`, `--lr-env-list-reveal-active-border` (pressed
318
+ reveal toggle background/border).
319
+ - `lr-flow-node`: `--lr-flow-node-selected-border` (selected card border color).
320
+ - `lr-flow-canvas`: `--lr-flow-canvas-node-current-outline-color` (current node outline color).
321
+ - `lr-artifact-panel`: `--lr-artifact-panel-view-active-bg`, `--lr-artifact-panel-view-active-color`
322
+ (pressed preview/code toggle background/text).
323
+ - `lr-test-results`: `--lr-test-results-filter-active-bg`, `--lr-test-results-filter-active-border`,
324
+ `--lr-test-results-filter-active-color` (pressed status filter toggle).
325
+ - `lr-span-waterfall`: `--lr-span-waterfall-row-active-bg` (active row background).
326
+ - `lr-trace-tree`: `--lr-trace-tree-row-active-bg` (active row background).
327
+ - `lr-agent-trace`: `--lr-agent-trace-handoff-active-bg` (active handoff quick-jump entry background).
328
+ - `lr-policy-summary`: `--lr-policy-summary-count-allow-color`,
329
+ `--lr-policy-summary-count-deny-color`, `--lr-policy-summary-count-needs-review-color` (per-state
330
+ count text colors).
331
+
332
+ - 8e4e5cc: `<lr-filter-bar>` gains a `'text'` filter type, composing `<lr-input>` for an open-ended query, plus
333
+ an optional per-filter `debounce` (ms). A dashboard whose toolbar is a search box next to a few
334
+ dropdowns can now be a single filter bar — the search box participates in the same `value` object,
335
+ the same removable active-filter chips (shown verbatim, so a query containing a slash is no longer
336
+ mangled), the same reset button and `loading` state — and can delete its own hand-rolled debounce
337
+ timer. A pending debounce is flushed by the field's own change/blur and cancelled by `reset()`, a
338
+ chip removal, and disconnection, and the text field stays uncontrolled-with-sync so a re-render
339
+ mid-typing never disturbs the caret.
340
+ - f8bc916: Form controls: exact-height escape hatches and `start`/`end` adornment slots.
341
+
342
+ - `--lr-combobox-trigger-height` and `--lr-input-control-height` are new custom properties that pin
343
+ an exact control height — flooring _and_ capping the row — so `lr-select`, `lr-combobox` and
344
+ `lr-input` can be pixel-matched in one toolbar without a `::part()` rule. Both are deliberately
345
+ left undeclared by default, so each tier's existing `*-min-height` floor still applies when they
346
+ are unset. Because the component never declares them, they can also be set from an ancestor or an
347
+ outer-tree rule, not only inline on the element. On `lr-combobox` the hatch is a single-row
348
+ affordance: in `multiple` mode a tag row long enough to wrap overflows the pinned box visibly
349
+ (nothing is clipped), so leave it unset there.
350
+ - **Behaviour change:** `lr-select` declared `--lr-select-trigger-height: auto` on `:host`, which
351
+ made the `var()` fallback to `--lr-select-trigger-min-height` unreachable and left that property
352
+ dead at the default `m` tier (four extra specificity rules patched the floor back for
353
+ `xs`/`s`/`l`/`xl` only). The sentinel is now genuinely undeclared and the patch rules are gone, so
354
+ `--lr-select-trigger-min-height` is live at every tier. The visible consequence is that a
355
+ default-size `lr-select` trigger now honours the `2.5rem` floor it already declared — byte
356
+ identical to `lr-input`'s and `lr-combobox`'s own `m` floor, so the three controls line up.
357
+ `getComputedStyle(el).getPropertyValue('--lr-select-trigger-height')` now returns `''` rather than
358
+ `'auto'`; assert the rendered `min-block-size`/`block-size` instead.
359
+ - `lr-combobox` and `lr-date-input` gain `start`/`end` adornment slots with matching `start`/`end`
360
+ CSS parts, mirroring `lr-input`'s existing implementation: the wrappers are `hidden` while nothing
361
+ is slotted, and they inherit the control's own padding so no consumer spacing is needed. `end`
362
+ renders before the dropdown chevron (`lr-combobox`) and before the calendar toggle
363
+ (`lr-date-input`), so consumer content never sits outboard of the built-in trigger. Slotted
364
+ adornments are never collected as `lr-combobox` options.
365
+ - `lr-select` is deliberately excluded from `start`/`end`: its `[part='trigger']` is a native
366
+ `<button>`, whose content model forbids interactive descendants, and its `justify-content:
367
+ space-between` would push the label to the middle. `lr-date-input` is deliberately excluded from
368
+ the exact-height hatch: its row has no `min-block-size`, and its height is pinned transitively by
369
+ `--lr-icon-button-size` on the calendar button — capping it would crush the 24x24 target.
370
+
371
+ - 4a43cc0: `<lr-heatmap>`: `CalendarCellPos` now carries the resolved ISO `yyyy-mm-dd` `date` alongside
372
+ `week`/`weekday`. Every calendar-mode position handed to `cellText`, `cellColor` and
373
+ `cellInteractive` is populated — **including grid positions with no matching entry in `days`**
374
+ (a gap in a sparse calendar still sits on a real calendar day) — so a callback can key off the date
375
+ directly instead of re-deriving the grid's `firstWeekStart + week * 7 + weekday` anchor arithmetic,
376
+ which was the only way to answer "is this cell in the future?" before.
377
+
378
+ The date comes from a per-grid cache built once whenever the calendar grid is rebuilt, so it costs
379
+ an array read rather than a `Date` allocation per cell per repaint, and it is deliberately excluded
380
+ from the internal hover/focus position-equality check so repaint diffs are unchanged. Matrix mode's
381
+ `MatrixCellPos` is untouched, and `lr-cell-click`'s detail shape is unchanged.
382
+
383
+ `date` is a **required** field of `CalendarCellPos`. No API on this component accepts a
384
+ `CalendarCellPos` as input — it is purely a callback parameter type — so this is additive for every
385
+ supported use; the only way to notice it is hand-constructing a `CalendarCellPos` literal in
386
+ TypeScript, which now needs a `date`.
387
+
388
+ - 4a43cc0: `<lr-heatmap>`: `HeatmapLegendStop.color` is now optional, so a `legendStops` entry can be a
389
+ **caption-only** stop. A stop with no `color` (or an empty-string `color`) renders its
390
+ `[part="legend-stop-label"]` with **no `[part="legend-swatch"]` element in the DOM at all**, rather
391
+ than an empty 0.6rem swatch box — the shape a GitHub-style "Less ▢▢▢▢ More" key needs for the bare
392
+ captions bracketing its colored ramp. Colored stops are unchanged, and an all-colored `legendStops`
393
+ array renders exactly as before.
394
+
395
+ The trailing `valueLabel` caption that closes the legend row also gained
396
+ `part="legend-value-label"` (it was the one unaddressable node in `[part='legend']`), in both the
397
+ gradient and the `legendStops` branch. Nothing else in the legend markup changed.
398
+
399
+ - 4a43cc0: `<lr-heatmap>` gained `maxCellSize` (`max-cell-size`) and `minCellSize` (`min-cell-size`), bounding
400
+ the cell size `fit-to-width` derives from the host's measured width in **both** calendar and matrix
401
+ mode. Without a ceiling, a 5-week calendar or a 3-column matrix in a wide pane inflates into a few
402
+ giant blocks; without a raisable floor, a year-long calendar in a narrow pane collapses onto the
403
+ built-in 4px minimum.
404
+
405
+ Both are ignored while `fit-to-width` is unset — an explicit `cell-size` is an exact request and is
406
+ never clamped — and both default to unset, so an untouched consumer's geometry is byte-identical.
407
+ `min-cell-size` can only raise the built-in 4px floor, never lower it; when both are set and
408
+ `max-cell-size < min-cell-size` the ceiling wins. A non-finite or empty attribute means unset rather
409
+ than `0`.
410
+
411
+ Note that the canvas is sized from the _clamped_ cell size, so a capped grid leaves the host's
412
+ remaining width unfilled instead of stretching to it — align it with normal CSS on the host.
413
+
414
+ - 068cb85: `lr-icon-button` hosts natural-aspect-ratio content
415
+
416
+ The default slot is now rendered as a **sibling** of the built-in glyph instead of being piped
417
+ through `<lr-icon>`, and `<lr-icon>` is mounted only when `icon` is set. The button box is also
418
+ floored with `min-inline-size`/`min-block-size: var(--lr-icon-button-size)` instead of being pinned
419
+ to it, matching that token's documented contract (a minimum tappable box, not a fixed size).
420
+
421
+ Slotted content previously went through `lr-icon`'s node-cloning path, which rebuilds every
422
+ assigned node with `document.createElementNS('http://www.w3.org/2000/svg', localName)` — a slotted
423
+ custom element such as `<lr-flag>` became an SVG-namespaced element that never upgraded and never
424
+ painted. It now renders normally, at its own aspect ratio.
425
+
426
+ **Migration.** Slotted **bare SVG geometry** (`<path>`, `<circle>`, …) with no `icon` attribute
427
+ relied on the removed `<lr-icon>` wrapper to supply an SVG parent, and must now be wrapped
428
+ explicitly:
429
+
430
+ ```html
431
+ <!-- before -->
432
+ <lr-icon-button aria-label="Star"><path d="…"></path></lr-icon-button>
433
+ <!-- after -->
434
+ <lr-icon-button aria-label="Star"
435
+ ><lr-icon path="…"></lr-icon
436
+ ></lr-icon-button>
437
+ ```
438
+
439
+ A complete element — an `<svg>`, an `<img>`, an `<lr-flag>` — keeps working, renders more reliably,
440
+ and is no longer constrained to a 1:1 box: content larger than `--lr-icon-button-size` now grows the
441
+ button and keeps its own aspect ratio, while a small glyph still pads out to the full tappable
442
+ target on both axes.
443
+
444
+ - 9ed6aa8: Add component-scoped state-styling cssprops to eight layout/forms components, so a selected/active/current state can be restyled from outside without hijacking a library-wide `--lr-color-*` token (which repaints everything else reading it). `::part(x)[state]` is invalid CSS — an attribute selector cannot follow `::part()` — so hijacking the shared token used to be the only lever. Each new prop is an inline `var()` fallback (never declared on `:host`, which would re-stamp per instance and shadow any ancestor value), and every default is the exact token the rule used before, so an unset consumer renders byte-identically.
445
+
446
+ - `lr-app-rail-item`: `--lr-app-rail-item-current-bg`, `--lr-app-rail-item-current-color` for the `active`/`aria-current="page"` item.
447
+ - `lr-stepper`: `--lr-stepper-current-color`, `--lr-stepper-error-color`, `--lr-stepper-current-index-bg`, `--lr-stepper-current-index-color`.
448
+ - `lr-widget`: `--lr-widget-view-toggle-active-bg`, `--lr-widget-view-toggle-active-color` for the pressed view toggle.
449
+ - `lr-carousel`: `--lr-carousel-indicator-current-bg`, `--lr-carousel-indicator-current-border-color` for the current slide's indicator dot.
450
+ - `lr-breadcrumb-item`: `--lr-breadcrumb-current-color` for the current-page item.
451
+ - `lr-command-palette`: `--lr-command-palette-active-bg` for the active command row.
452
+ - `lr-time-range`: `--lr-time-range-preset-active-bg`, `--lr-time-range-preset-active-border-color`, `--lr-time-range-preset-active-color` for the active preset button.
453
+ - `lr-emoji-picker`: `--lr-emoji-picker-active-bg` for the keyboard-active and hovered emoji (both share one rule, so one hook retints both).
454
+
455
+ - cea6d8e: New `localeNativeName(tag)` helper next to `languageToCountry()` / `LANGUAGE_TO_COUNTRY`: it returns
456
+ a locale's endonym — its name written in that locale itself (`'fr'` → `français`, `'pt-BR'` →
457
+ `português (Brasil)`) — which is what a language switcher should list. It reads through the shared
458
+ memoized `Intl.DisplayNames` cache, so no name table ships and repeat lookups are free, and it
459
+ degrades to the tag itself for an unknown or structurally invalid tag instead of throwing. Paired
460
+ with `languageToCountry()` and `lr-popover`, it composes the locale-picker recipe shown in the new
461
+ Flag story.
462
+ - 184bfff: `lr-menu`: Escape from `header`/`footer` content closes the menu and refocuses the trigger
463
+ unconditionally, with no opt-in required.
464
+
465
+ That matches `<lr-popover>`, which already dismisses on Escape from arbitrary popup content, and it
466
+ is the only sensible contract for a region the component now positively invites you to fill: a
467
+ filter field you can Tab into but not Escape out of is a trap.
468
+
469
+ - `closeOnEscapeAnywhere` is **unchanged** — not deprecated, still `false` by default, and still
470
+ governing exactly one thing: Escape from non-item content slotted into the **default** slot.
471
+ Escape bubbling up from inside `[part='list']` is left entirely to the list's own handler.
472
+ - Arrow/ArrowUp/Home/End/Enter/Space from header/footer content keep their full native behavior;
473
+ the item-target gate that guarantees that is untouched, and nothing in the new region handler
474
+ calls `preventDefault()` for those keys.
475
+
476
+ - 184bfff: `lr-menu`: new `header` and `footer` slots for composed, non-menu-item content, rendered inside
477
+ `[part='popup']` but **outside** the `role="menu"` list — with matching `header`/`footer` CSS parts.
478
+
479
+ A filter field, a section title, an "Apply"/"Done" button and friends have always been a real use
480
+ case for this component (`closeOnEscapeAnywhere` exists for exactly that), but the only place to put
481
+ them was the default slot — i.e. inside `role="menu"`, where ARIA permits only
482
+ `menuitem`/`menuitemradio`/`menuitemcheckbox`/`group`/`separator` children. Anything else there is an
483
+ `aria-required-children` violation. The new slots give that content a valid home.
484
+
485
+ - Nothing about the default slot changes: item discovery, roving tabindex, type-ahead,
486
+ `closeOnEscapeAnywhere` and its `false` default all behave exactly as before, and `items` still
487
+ only ever contains `<lr-menu-item>`s no matter what the new slots hold.
488
+ - With neither slot filled the rendered result is unchanged — both wrappers collapse to no box at
489
+ all, `[part='list']` keeps its exact position and size inside the popup, and the host gains no
490
+ attribute of any kind.
491
+ - Emptiness is tracked from each slot's own `slotchange` (reflected as `data-has-header` /
492
+ `data-has-footer` / `data-list-empty` on the host) rather than with `:empty`, which can never match
493
+ a part that contains a slot: Chromium counts the whitespace-only text nodes Lit leaves there.
494
+ - Non-item content in the **default** slot keeps working exactly as it did, with no runtime warning,
495
+ but the new slots are now the supported place for it.
496
+
497
+ - 184bfff: `lr-menu`: Tab now moves focus into the `header`/`footer` regions instead of closing the menu, and
498
+ tabbing out of the popup's last focusable finally closes it.
499
+
500
+ Two halves of the same defect. `onListKeyDown` gated every key except Escape behind "is the event
501
+ target a real `<lr-menu-item>`?", so (a) Tab from an item always closed the menu — you could never
502
+ Tab _into_ composed content, in either direction, since Shift+Tab is `key === 'Tab'` too — and
503
+ (b) Tab from composed content did nothing at all: focus walked out of the popup while the menu
504
+ stayed open, an untested dismissal hole.
505
+
506
+ Tab handling therefore moves from `[part='list']` to `[part='popup']`, which also sees keydowns from
507
+ the new regions, and the menu now closes only when Tab would leave the popup entirely:
508
+
509
+ - Tab from an item with a focusable `footer` (or Shift+Tab with a focusable `header`) keeps the menu
510
+ open and lets the browser's own Tab advance carry focus into the region.
511
+ - Tab out of the last focusable in the popup — in either direction, from an item or from composed
512
+ content — closes the menu.
513
+ - **With no header/footer content, Tab closes exactly as before**, and non-item content in the
514
+ default slot stays deliberately Tab-unreachable from an item.
515
+ - `preventDefault()` is still never called for Tab, in any branch: native focus navigation proceeds
516
+ untouched, only the now-stale open state is cleared.
517
+
518
+ - fe06b7d: `lr-menu`: `show(focus?)` and `hide(options?)` are now public.
519
+
520
+ - `hide({ focusTrigger: true })` closes the menu **and** returns DOM focus to the `trigger`-slotted
521
+ element — the case the trigger alone cannot express, e.g. a slotted "Apply"/"Done" button inside
522
+ the menu, or a consumer-owned keyboard shortcut. `hide()` on its own closes without moving focus,
523
+ for dismissals where the interaction has already put focus somewhere the user chose.
524
+ - `show()` is promoted alongside it (rather than shipping an asymmetric API) and still accepts the
525
+ `'first' | 'last'` initial focus target.
526
+ - The roving-tabindex reset moved from `hide()` into `updated()`, so a bare `el.open = false` from
527
+ outside now resets `activeIndex` too. Previously that path left a stale `tabindex="0"` tab stop on
528
+ whichever item was last active, so Tab could land inside a closed menu. `hide()` stays thin and
529
+ `updated()` remains the single owner of positioning, listeners and the `lr-show`/`lr-hide` events;
530
+ focus restoration deliberately stays in `hide()` so `disconnectedCallback()`'s own `open = false`
531
+ teardown reset can never steal focus.
532
+
533
+ - 09bdfde: `lr-activity-feed`: make the virtualized entry rows actually styleable, by this component and by a
534
+ consumer.
535
+
536
+ At/above `virtualizeThreshold` the entries are produced by this component's `renderItem` but
537
+ committed into the embedded `<lr-virtual-list>`'s own shadow root, one boundary deeper than a
538
+ `[part='entry']` selector can reach — so every entry, icon, text and timestamp rule was silently
539
+ inert and a long feed rendered as unstyled rows. Each rule now pairs its plain selector (still
540
+ correct below the threshold, where the same template renders into this component's own shadow root)
541
+ with an `lr-virtual-list::part(…)` twin, and an `exportparts` forwarding declaration makes the same
542
+ parts reachable as `lr-activity-feed::part(entry)` etc. from a consuming stylesheet.
543
+
544
+ The tone dot is promoted from an internal class to a named `tone-dot` part, since a class selector
545
+ cannot cross a shadow boundary either. `::part()` cannot be followed by an attribute selector, so
546
+ the tone carries a second name in the dot's part list rather than being matched through
547
+ `[data-tone]` (`::part()` matches with `part~=` semantics, so both names select the same element).
548
+ New parts: `tone-dot`, plus `tone-dot-neutral`/`tone-dot-brand`/`tone-dot-success`/
549
+ `tone-dot-warning`/`tone-dot-danger`. The `data-tone` attributes are unchanged, and a consumer can
550
+ now retint a single tone instead of overriding a library-wide color token.
551
+
552
+ - 9150bb1: `lr-archive-viewer`: make the virtualized entry rows actually styleable, by this component and by a
553
+ consumer.
554
+
555
+ Entry rows are produced by this component's `renderItem` but committed into the embedded
556
+ `<lr-virtual-list>`'s own shadow root, one boundary deeper than a `[part='entry']` selector can
557
+ reach — so all five row-level rules were silently inert and the listing rendered as unstyled stacked
558
+ text with no row layout, no icon sizing, no truncation and no size column treatment. They now reach
559
+ through `lr-virtual-list::part(…)`, and an `exportparts` forwarding declaration makes the same parts
560
+ reachable as `lr-archive-viewer::part(entry)` etc. from a consuming stylesheet.
561
+
562
+ New part `entry-name-dir`: `::part()` cannot be followed by a descendant combinator, so the
563
+ directory-row emphasis that used to be written as a descendant selector now targets a second part
564
+ name on the name element itself. A directory row's name is `part="entry-name entry-name-dir"`, and
565
+ `::part()` matches with `part~=` semantics, so both names select it.
566
+
567
+ - 3e171e6: Fix `lr-av-player`'s transcript cue styling never applying, and make every cue-level part reachable
568
+ from a consumer stylesheet.
569
+
570
+ Cues are composed through `lr-virtual-list`, whose `renderItem` result is committed inside that
571
+ element's **own** shadow root — one boundary below the player's. A bare `[part='cue']` selector in
572
+ the player's stylesheet cannot cross that boundary, so every cue rule was silently inert and each
573
+ transcript row fell back to the raw browser button appearance: a grey background, a visible border,
574
+ `1px 6px` padding and centered text, with no timestamp or speaker treatment and no visual state for
575
+ the playing cue or the search matches. Every one of those rules now goes through
576
+ `lr-virtual-list::part(…)`.
577
+
578
+ `::part()` cannot be followed by an attribute selector, so the three cue states get their own part
579
+ names, added alongside `cue` as a part list (`::part()` carries `part~=` semantics, so both names
580
+ match the same element):
581
+
582
+ - **New:** `cue-current` — the row the playhead is inside.
583
+ - **New:** `cue-match` — a row matching the current search query.
584
+ - **New:** `cue-active-match` — the row holding the current search match.
585
+
586
+ The `aria-current`, `data-match` and `data-active-match` attributes are unchanged and still describe
587
+ each row's state.
588
+
589
+ This also makes two documented custom properties live for the first time:
590
+ `--lr-av-player-cue-current-bg` now retints the playing cue, and
591
+ `--lr-av-player-cue-active-match-color` now recolors the active search match's outline. Both
592
+ previously resolved against a rule that never matched anything.
593
+
594
+ The player forwards `cue`, `cue-current`, `cue-match`, `cue-active-match`, `cue-time`, `cue-speaker`
595
+ and `cue-text` through `exportparts`, so `lr-av-player::part(cue)` and friends work from a consumer
596
+ stylesheet for the first time.
597
+
598
+ - c0f00ac: `lr-csv-viewer` and `lr-spreadsheet-viewer`: make the documented `cell-highlight` part actually
599
+ visible, and reachable from a consumer stylesheet.
600
+
601
+ Both viewers already emitted `part="cell cell-highlight"` for a cell covered by a `highlights`
602
+ entry, but neither had a single CSS rule for it anywhere — a highlighted cell rendered
603
+ indistinguishably from a plain one. Highlighted cells render inside the internal
604
+ `<lr-virtual-list>`'s own shadow root (they are `renderItem`'s output), so the styling is applied
605
+ through `lr-virtual-list::part(cell-highlight)`, using the same outline tokens `lr-dataset-viewer`
606
+ gives its own `cell-highlight` so a highlight reads identically across the table viewers.
607
+
608
+ - New `--lr-csv-viewer-highlight-color` / `--lr-spreadsheet-viewer-highlight-color` custom
609
+ properties (default `var(--lr-color-brand)`) set the outline color; the active highlight sets it
610
+ inline to `var(--lr-color-warning, var(--lr-color-brand))`, so the active match is now
611
+ distinguishable from the other highlighted cells.
612
+ - A paired `:focus-visible` rule restores the shared focus ring, which the unconditional highlight
613
+ outline would otherwise swallow on this focusable cell.
614
+ - Both viewers now forward `exportparts` for `data-row`, `cell` and `cell-highlight` from the
615
+ internal `<lr-virtual-list>`, so `lr-csv-viewer::part(cell)` and friends reach the real rendered
616
+ rows instead of matching nothing.
617
+
618
+ - 99d5500: Fix `lr-chunk-inspector`'s entire chunk-row styling never applying above `virtualize-at`, and make
619
+ every row-level part reachable from a consumer stylesheet.
620
+
621
+ Past the threshold the row template becomes `lr-virtual-list`'s `renderItem`, whose result is
622
+ committed inside that element's **own** shadow root — one boundary below this component's. A bare
623
+ `[part='chunk']` selector cannot cross that boundary, so a long chunk list lost its row layout and
624
+ separators, the score line's size/color/tabular figures, the score bar and its tone-mapped fill, the
625
+ line clamp on the collapsed text preview, and the borderless brand styling on the open and
626
+ show-more buttons, which fell back to the raw browser button appearance. Both documented custom
627
+ properties (`--lr-chunk-inspector-current-bg`, `--lr-chunk-inspector-current-color`) were dead
628
+ there too. Every rule now pairs its original selector with an `lr-virtual-list::part(…)` arm, so
629
+ both rendering paths present identically — below the threshold the rows are still rendered into this
630
+ component's own shadow root, where the bare selector is the one that matches.
631
+
632
+ `::part()` cannot be followed by an attribute selector, and it cannot be followed into the matched
633
+ element's subtree either, so row state is now carried by an additional part name (added alongside
634
+ the base name as a part list — `::part()` carries `part~=` semantics, so both names match the same
635
+ element):
636
+
637
+ - **New:** `chunk-current` — the row matching `activeId`.
638
+ - **New:** `score-current` — that row's score line, previously reached through a descendant
639
+ selector no `::part()` can express.
640
+ - **New:** `score-fill-success`, `score-fill-warning`, `score-fill-danger` — the score bar fill in
641
+ each scoring tier.
642
+ - **New:** `text-clamped` — the text preview while still collapsed.
643
+
644
+ The `aria-current`, `data-tone` and `data-clamped` attributes are unchanged and still describe each
645
+ element's state.
646
+
647
+ While virtualized, the chunk row no longer carries its own `role="listitem"`: `lr-virtual-list`
648
+ already wraps every row it renders in one, and the nested duplicate left the inner list item with a
649
+ list-item rather than list parent — an invalid ARIA containment that axe flags.
650
+
651
+ The internal `lr-virtual-list` now forwards every row part through `exportparts`, so
652
+ `lr-chunk-inspector::part(chunk)` and friends work from a consumer stylesheet in both paths.
653
+
654
+ - 5bdb6d7: Fix `lr-notebook-viewer`'s cell and output styling never applying, and make every cell-level part
655
+ reachable from a consumer stylesheet.
656
+
657
+ Cells are composed through `lr-virtual-list`, whose `renderItem` result is committed inside that
658
+ element's **own** shadow root — one boundary below the viewer's. A bare `[part='cell']` selector in
659
+ the viewer's stylesheet cannot cross that boundary, so the rules for `cell`, `cell-gutter`,
660
+ `outputs`, `output` and `output-toggle` were all silently inert: cells rendered without their
661
+ two-column grid, padding and separator, the execution-count gutter without its monospace/quiet
662
+ treatment, stderr and error outputs untinted, and the show-all-output control as a raw browser
663
+ button. Every one of those rules now goes through `lr-virtual-list::part(…)`, including the
664
+ narrow-allocation `@container` block — container queries resolve through the flat tree, so they
665
+ still evaluate against the viewer's own `:host` container across the shadow boundary.
666
+
667
+ `::part()` cannot be followed by an attribute selector or a descendant combinator, so three states
668
+ and one descendant get their own part names, added alongside the existing ones as a part list
669
+ (`::part()` carries `part~=` semantics, so both names match the same element):
670
+
671
+ - **New:** `cell-active` — the cell an anchor currently targets. This is what
672
+ `--lr-notebook-viewer-active-bg` retints; that custom property had no effect until now.
673
+ - **New:** `output-error` — a stderr stream or an error output, carrying the danger tint.
674
+ - **New:** `error-output-label` — the label introducing an error output's traceback.
675
+
676
+ The `data-active`, `data-stream` and `data-output-type` attributes are unchanged and still describe
677
+ each element for scripting.
678
+
679
+ The viewer forwards `cell`, `cell-active`, `cell-gutter`, `cell-source`, `outputs`, `output`,
680
+ `output-error`, `error-output-label` and `output-toggle` through `exportparts`, so
681
+ `lr-notebook-viewer::part(cell)` and friends work from a consumer stylesheet for the first time.
682
+
683
+ - 2e3be2e: `lr-page-rail`: make the virtualized page rows actually styleable, by this component and by a
684
+ consumer.
685
+
686
+ Page rows are produced by this component's `renderItem` but committed into the embedded
687
+ `<lr-virtual-list>`'s own shadow root, one boundary deeper than a `[part='page']` selector can
688
+ reach — so all 13 row-level rules were silently inert and every page button rendered as a raw
689
+ browser `<button>` (UA background, UA border, UA padding) instead of the intended rail row. They now
690
+ reach through `lr-virtual-list::part(…)`, and an `exportparts` forwarding declaration makes the same
691
+ parts reachable as `lr-page-rail::part(page)` etc. from a consuming stylesheet.
692
+
693
+ `--lr-page-rail-current-bg` becomes live with this fix: it previously documented a background that
694
+ nothing applied. It now tints the current page row, and keeps it tinted while the row is hovered so
695
+ the current page stays identifiable under the pointer.
696
+
697
+ `::part()` cannot be followed by an attribute selector, so state variants carry a second part name
698
+ in the element's part list instead (`::part()` matches with `part~=` semantics, so both names select
699
+ the same element). New parts: `page-current` on the current page button (alongside `page`), and
700
+ `heat-dot-accent`/`heat-dot-success`/`heat-dot-warning`/`heat-dot-danger`/`heat-dot-neutral`/
701
+ `heat-dot-overflow` on the heat markers (alongside `heat-dot`). The `data-tone`/`data-overflow`
702
+ attributes are unchanged.
703
+
704
+ - 3217988: Fix `lr-pdf-viewer`'s page styling never applying, and make every page-level part reachable from a
705
+ consumer stylesheet.
706
+
707
+ Pages are composed through `lr-virtual-list`, whose `renderItem` result is committed inside that
708
+ element's **own** shadow root — one boundary below the viewer's. A bare `[part='page']` selector in
709
+ the viewer's stylesheet cannot cross that boundary, so the rules for `page`, `text-layer`, the page
710
+ canvas, the generated text runs, the selection tint, and both search-match states were all silently
711
+ inert: pages rendered without their centering/padding wrapper, the canvas without its border,
712
+ the text layer unpositioned, and search matches unhighlighted. Every one of those rules now goes
713
+ through `lr-virtual-list::part(…)`, including the RTL text-layer mirror.
714
+
715
+ Because `::part()` cannot be followed by a descendant combinator, two elements that were previously
716
+ addressed as descendants get their own names:
717
+
718
+ - **New:** `page-canvas` — the canvas a page's content is painted onto.
719
+ - **New:** `text-span` — one generated text run inside a page's text layer. The selection tint hangs
720
+ off this part (`::part(text-span)::selection`), since a highlight pseudo is matched against the
721
+ element the selected text originates in.
722
+
723
+ `search-match` / `search-match-active` are now matched directly by name (`::part()` already carries
724
+ `part~=` semantics), and the viewer forwards `page`, `page-canvas`, `text-layer`, `text-span`,
725
+ `search-match` and `search-match-active` through `exportparts`, so `lr-pdf-viewer::part(page)` and
726
+ friends work from a consumer stylesheet for the first time.
727
+
728
+ - 6f3db46: Fix `lr-retrieval-results`' row, selection and metadata styling never applying while virtualized,
729
+ and make every row-level part reachable from a consumer stylesheet.
730
+
731
+ Rows are composed through `lr-virtual-list`, whose `renderItem` result is committed inside that
732
+ element's **own** shadow root — one boundary below this component's. A bare `[part='row-body']`
733
+ selector in this component's stylesheet cannot cross that boundary, so the checkbox offset, the
734
+ row-body layout, the selected-row indicator and the whole metadata list were silently inert
735
+ whenever the list virtualized. `grouping="source"` always virtualizes, so every grouped consumer
736
+ saw an unstyled result set, and the documented `--lr-retrieval-results-selected-border` custom
737
+ property had nothing to recolor there. Each of those rules now pairs its original selector with an
738
+ `lr-virtual-list::part(…)` arm, so both rendering paths present identically — the flat path below
739
+ `virtualize-at` still renders these parts into this component's own shadow root, where the bare
740
+ selector is the one that matches.
741
+
742
+ `::part()` cannot be followed by an attribute selector, nor by a descendant combinator, so two
743
+ kinds of rule needed new part names:
744
+
745
+ - **New:** `row-body-selected` — added alongside `row-body` as a part list (`::part()` carries
746
+ `part~=` semantics, so both names match the same element) on the selected row. The `data-selected`
747
+ attribute is unchanged and still describes the row's state.
748
+ - **New:** `metadata-term` and `metadata-value` — the `<dt>`/`<dd>` inside a `metadata-entry`,
749
+ previously styled through a descendant selector that `::part()` cannot express. The trailing colon
750
+ after a metadata key is now `::part(metadata-term)::after`.
751
+
752
+ The group header in grouped mode also gains a separator matching the one this component's rows use;
753
+ `lr-virtual-list` supplies the rest of its appearance.
754
+
755
+ `exportparts` now forwards `select`, `row-body`, `row-body-selected`, `metadata`, `metadata-entry`,
756
+ `metadata-term` and `metadata-value` alongside the existing `row`/`group-header`, and forwards each
757
+ per-row `lr-chunk-inspector`'s own parts onward under a `chunk-` prefix (`chunk`, `chunk-current`,
758
+ `chunk-score`, `chunk-score-current`, `chunk-score-bar`, `chunk-score-fill`,
759
+ `chunk-score-fill-success`/`-warning`/`-danger`, `chunk-open-button`, `chunk-title`, `chunk-text`,
760
+ `chunk-text-clamped`, `chunk-toggle`) — those live two shadow hops deep and were unreachable from
761
+ outside the component entirely.
762
+
763
+ - 583f359: `lr-phone-input`: rebuild the country selector's closed state and add an opt-in `flags` API.
764
+
765
+ The old closed control was the bare native `<select>` showing each option's full
766
+ `"Country name (+code)"` text: long localized names clipped under the UA chevron (the trigger was
767
+ capped at 45% of the field), the calling code appeared twice (inside the option text and again in
768
+ `calling-code`), and the popup fell back to UA colors (a white panel in dark themes). The native
769
+ `<select>` is kept — its popup, localized full country names, keyboard type-ahead, and native
770
+ mobile pickers are irreplaceable and fully accessible — but it is now stretched invisibly over a
771
+ compact decorative trigger:
772
+
773
+ - New closed state: selected alpha-2 code (localized "Select" placeholder when no countries exist)
774
+ plus the shared design-system chevron, with a pointer cursor, a hover tint, and an inner
775
+ focus-visible ring so keyboard focus on the selector is distinguishable from focus on the
776
+ telephone input. No more clipping and no duplicated calling code.
777
+ - Popup options now pin `--lr-color-surface`/`--lr-color-text` so the open list follows the theme
778
+ in dark mode.
779
+ - New `flags` boolean attribute renders the selected country's flag in the trigger as
780
+ `<lr-flag variant="compact" aria-label="">` (decorative — the select already announces the
781
+ country). The `<lr-flag>` definition is registered lazily on first use, so nothing flag-related
782
+ is bundled while `flags` stays off; flag artwork keeps the standalone `<lr-flag>` contract
783
+ (install optional `@aceshooting/lyra-flags` + import
784
+ `components/media/flag/flag-peer.js` once). Without it the trigger simply omits the image.
785
+ - New CSS parts: `country` (selector region), `country-trigger`, `flag`, `country-code`
786
+ (`data-placeholder` when empty), `expand-icon`. Existing parts are unchanged in name, but
787
+ `country-select` is now the invisible overlay — a consumer rule that painted its text/background
788
+ should target `country-trigger`/`country-code` instead.
789
+
790
+ - e83deb1: Selected-state styling hooks for `lr-segmented` and `lr-tabs`, an exact-height hatch for the
791
+ `lr-segmented` track, and a marker legend row for `lr-sequence-strip`.
792
+
793
+ - `lr-segmented` gains `--lr-segmented-selected-bg`, `--lr-segmented-selected-color`,
794
+ `--lr-segmented-selected-font-weight`, `--lr-segmented-selected-shadow` and
795
+ `--lr-segmented-hover-color`. Recoloring the checked pill previously required hijacking
796
+ library-wide `--lr-color-surface`/`--lr-color-text`, which necessarily repainted hovered
797
+ _unselected_ segments too (they read the same tokens); `::part(segment)[aria-checked='true']` is
798
+ not valid CSS, so there was no other route. The hover color is now its own hook, so the two states
799
+ are independent.
800
+ - `lr-segmented` also gains `--lr-segmented-track-height`, pinning the track to an exact height at
801
+ every `size` tier for a row that must line up with a hard-sized toolbar control. It is genuinely
802
+ unset by default, so each tier keeps its `--lr-segmented-track-min-height` floor until you set it.
803
+ - `lr-tabs` gains `--lr-tabs-selected-color`, `--lr-tabs-indicator-color` and
804
+ `--lr-tabs-hover-color` for the same reason: the selected tab's text/underline and the hovered
805
+ tab's text no longer share `--lr-color-brand`/`--lr-color-text` with the rest of the library.
806
+ - `lr-sequence-strip` gains `markerLabel` (`marker-label`). When set alongside `show-legend` it adds
807
+ one trailing legend row — `[part="legend-marker-swatch"]`, a neutral chip (themeable via the new
808
+ `--lr-sequence-strip-legend-marker-bg`) carrying the cell's own bottom bar in
809
+ `--lr-sequence-strip-marker-color` — and the marker's count joins the strip's auto-generated
810
+ `aria-label` summary, so the visual legend keeps no entry without a spoken counterpart.
811
+
812
+ Every new custom property is an inline `var()` fallback resolving to the token the rule already
813
+ used, so an unset consumer renders exactly as before.
814
+
815
+ - 36dce60: Fill the sized-control cssprop gaps for `lr-date-input`, `lr-pagination`, `lr-known-date`,
816
+ `lr-chip`, `lr-avatar`, and `lr-avatar-group`, matching the per-tier theming surface
817
+ `lr-input`/`lr-select`/`lr-combobox` already expose.
818
+
819
+ - **`lr-avatar` / `lr-avatar-group` (visible bug fix):** the initials fallback and the "+N"
820
+ overflow badge were painted at a fixed `--lr-font-size-sm` at every `size`, so initials did not
821
+ scale with the avatar circle. They now scale via new per-tier `--lr-avatar-font-size` and
822
+ `--lr-avatar-group-badge-font-size` knobs (`sm`/`md`/`lg`). The `md` default is unchanged, so
823
+ existing avatars render identically.
824
+ - **`lr-date-input`:** adds a per-tier `--lr-date-input-control-min-height` floor and an exact-height
825
+ `--lr-date-input-control-height` hatch on the input row (it previously had neither). The calendar
826
+ toggle keeps its own 24x24 touch target even when the height hatch pins a shorter row.
827
+ - **`lr-known-date`:** adds a per-tier `--lr-known-date-field-min-height` floor and an exact-height
828
+ `--lr-known-date-field-height` hatch on each field input.
829
+ - **`lr-chip`:** the interactive tap-target floor is now the per-tier `--lr-chip-min-height` (was a
830
+ single hardcoded `1.5rem` shared by every tier), and a new `--lr-chip-height` hatch pins an exact
831
+ height. Interactive chips keep the 24px WCAG 2.2 SC 2.5.8 minimum at every tier; a `--lr-chip-height`
832
+ below that is for non-interactive chips only.
833
+ - **`lr-pagination`:** the nav buttons' and page input's inner padding is now the
834
+ `--lr-pagination-control-padding` knob (was a hardcoded `var(--lr-space-xs)`), kept uniform across
835
+ tiers so current rendering is unchanged.
836
+
837
+ All new knobs default to today's exact values, so unset consumers render byte-identical at every
838
+ tier (the `lr-avatar` `sm`/`lg` font-size fix is the sole deliberate exception).
839
+
840
+ - 6ab596d: `<lr-split>`: `rail-breakpoint` and `float-breakpoint` now accept a CSS length (`'640px'`,
841
+ `'68.75rem'`, `'3em'`) as well as the original bare pixel number, and a new
842
+ `collapse-breakpoint-basis="viewport"` measures both against the viewport via `matchMedia` instead
843
+ of the split's own `[part="base"]` allocation — for collapsing in step with a page-level `@media`
844
+ layout. Both thresholds are classified together on every change, so a fast resize crossing both at
845
+ once still lands on one correct state and fires `lr-split-collapse-change` once; under viewport
846
+ basis the first paint already carries the right `data-collapse-state` with no `ResizeObserver`
847
+ round-trip, and that initial state is not announced as a transition. Note `(max-width:)` is
848
+ inclusive while container basis compares strictly `<`, so switching basis shifts each crossing
849
+ point by 1px. An unparseable length (`'80vw'`, `'calc(…)'`, garbage) falls back to the documented
850
+ `640`/`400` defaults rather than switching collapse off, and the "rail must sit above float"
851
+ invariant is still enforced, in pixel space, under both bases.
852
+
853
+ Because both properties now accept a string, they use Lit's default string converter: reading
854
+ `el.railBreakpoint` after `rail-breakpoint="640"` returns `'640'` rather than `640` (matching how
855
+ `orientationBreakpoint` already behaves). Authored values and crossing behavior are unchanged.
856
+
857
+ - e1d4af8: `lr-stat` gains two layout axes and stops reserving space for an absent label.
858
+
859
+ - `appearance="card" | "plain"` (default `card`, reflected). `plain` removes the border,
860
+ background, padding, corner radius and the `block-size: 100%` stretch, so a stat can sit inline
861
+ in prose, a toolbar or a table cell instead of only as a card. A `plain` stat with a safe `href`
862
+ underlines its `[part="value"]` on hover/focus, since the card's border-color-shift affordance is
863
+ invisible with no border; the focus ring is unchanged. `plain` also wins over `compact` when both
864
+ are set, and drops `emphasis`'s accent edge (card chrome) while keeping its brand value tint.
865
+ - `orientation="vertical" | "horizontal"` (default `vertical`, reflected). `horizontal` lays label,
866
+ value + unit, trend, sub and caption out on a single wrapping baseline row; `[part="spark"]` and
867
+ `[part="rows"]` stay stacked on their own full-width line beneath it.
868
+ - `[part="label"]` is now `hidden` whenever `label` is empty, so a label-less stat no longer leaves
869
+ a blank gap above its value. A non-empty label is never hidden and its `aria-labelledby` pairing
870
+ with `[part="value"]` is unchanged.
871
+
872
+ - 3312708: Add component-scoped CSS custom properties for state styling across thirteen conversation, retrieval, viewer and media components. Each of these components previously painted a selected/active/current state straight from a library-wide `--lr-color-*` token, which left the state unrestylable from outside: `::part(x)[data-active]` is invalid CSS, so the only lever was hijacking the shared token — repainting every other surface on the page that read it.
873
+
874
+ Every new property uses the inline `var()` fallback form and is deliberately **not** declared on `:host`, so a value set on the element or any ancestor is honoured rather than shadowed. With none of them set, rendering is byte-identical to before.
875
+
876
+ - `lr-conversation-item` — `--lr-conversation-item-active-bg`, `--lr-conversation-item-active-color`
877
+ - `lr-push-to-talk` — `--lr-push-to-talk-recording-color`
878
+ - `lr-chunk-inspector` — `--lr-chunk-inspector-current-bg`, `--lr-chunk-inspector-current-color`
879
+ - `lr-retrieval-results` — `--lr-retrieval-results-selected-border`
880
+ - `lr-retrieval-trace` — `--lr-retrieval-trace-active-border`
881
+ - `lr-source-picker` — `--lr-source-picker-checked-bg`, `--lr-source-picker-checked-border`, `--lr-source-picker-mixed-bg`
882
+ - `lr-page-rail` — `--lr-page-rail-current-bg`
883
+ - `lr-notebook-viewer` — `--lr-notebook-viewer-active-bg`
884
+ - `lr-svg-viewer` — `--lr-svg-viewer-active-border`
885
+ - `lr-document-preview` — `--lr-document-preview-active-border`
886
+ - `lr-xml-viewer` — `--lr-xml-viewer-active-match-color`
887
+ - `lr-av-player` — `--lr-av-player-marker-active-color`, `--lr-av-player-cue-current-bg`, `--lr-av-player-cue-active-match-color`
888
+ - `lr-image-viewer` — `--lr-image-viewer-annotate-active-bg`, `--lr-image-viewer-annotate-active-border`, `--lr-image-viewer-highlight-active-color`
889
+
890
+ `--lr-conversation-item-active-*` and `--lr-chunk-inspector-current-*` are documented as contrast-sensitive pairs: each background is half of a WCAG-AA dependency with the text color rendered on it.
891
+
892
+ Also fixes a WCAG-AA contrast failure in `lr-chunk-inspector`: the current (`active-id`) chunk's score line rendered in `--lr-color-text-quiet`, which reaches only ~4.24:1 against the `--lr-color-brand-quiet` current-row background — under the 4.5:1 floor for normal-size text. It now uses full-strength text while current, matching the identical fix already carried by `lr-attachment-chip`, `lr-chat-message` and `lr-conversation-item`. Non-current rows keep the quiet treatment.
893
+
894
+ - 5e9a18e: `lr-table`: keep focus inside a persistent (`editable: 'always'`) cell editor when the rows are
895
+ re-sorted underneath it. Row rendering is keyed by row key, so a re-sort _moves_ the editor's
896
+ `<input>` node — the typed value rides along, but a DOM move drops focus on its own — so the table
897
+ now records the focused editor's cell and restores focus to it after the move. A row that has left
898
+ the rendered set entirely (paginated away, filtered out) only clears the record: focus is not yanked
899
+ to whichever unrelated row now occupies that position.
900
+ - 5e9a18e: `lr-table`: give a persistent (`editable: 'always'`) cell editor its own Enter/Escape semantics.
901
+ Enter commits and keeps focus in the field rather than closing an editor that has no closed state,
902
+ and Escape — which has nothing to cancel back to — is no longer cancelled, so an ancestor
903
+ dialog/popover still acts on it. A double-click editor's Enter-commits-and-closes and
904
+ Escape-cancels behavior is unchanged. Adds the accompanying `AlwaysOnEditors` story.
905
+ - 5e9a18e: `lr-table`: widen `TableColumn.editable` to `boolean | 'always'`. `true` keeps today's
906
+ double-click-to-open editor unchanged; the new `'always'` renders a persistent editor in every body
907
+ cell of that column from first paint, for settings/rate-style grids where double-clicking each cell
908
+ to change a value is the wrong interaction. Persistent editors are plain tab stops (no `tabindex` of
909
+ their own), exactly like the existing row-expand toggle, so the roving header/row tabindex model is
910
+ untouched; each one keeps its individually interpolated `tableEditCell` accessible name, and
911
+ double-clicking an `'always'` cell no longer opens a second, competing editor inside it.
912
+ - 5e9a18e: `lr-table`: a persistent (`editable: 'always'`) cell editor binds its `value` as a content attribute
913
+ rather than as the `.value` property, so native dirty-value-flag semantics apply — an out-of-band
914
+ `rows` update to a cell the user has already typed into no longer replaces the draft they are still
915
+ editing, while an untouched editor still picks up a new `rows` value normally. Double-click editors
916
+ (`editable: true`) keep the property binding and its deliberate re-assert, unchanged. `lr-cell-edit`
917
+ remains the only mutation channel; the table still never mutates `row`.
918
+ - 43ee7d0: `lr-table`: the empty state is now addressable, cells can carry a native tooltip, `table-layout` is
919
+ settable, and the selected row has its own background custom property.
920
+
921
+ - Every built-in `<lr-empty>` the table renders carries `part="empty"` and re-exports its own inner
922
+ parts as `empty-base`/`empty-icon`/`empty-heading`/`empty-description`/`empty-actions`, so the
923
+ empty state can be restyled from outside without replacing it. Note that the no-columns and
924
+ no-rows states return the empty element as the shadow root's own root, so `::part(base)` does not
925
+ apply in those two states — only in the filtered-to-zero one.
926
+ - A new `empty` slot replaces the built-in empty state wholesale on the two _data_-empty branches
927
+ (no rows at all, and filtered/paginated down to zero). The no-columns branch keeps its own
928
+ `noColumnsHeading` copy and is deliberately not slot-replaceable — it reports a configuration
929
+ problem, not an empty result set.
930
+ - New `emptyCompact` property (`empty-compact` attribute) overrides the built-in empty state's
931
+ `compact` density. Left unset it preserves today's per-branch behaviour exactly: spacious for the
932
+ whole-table states, compact for the in-table filtered-to-zero one.
933
+ - New `columns[].cellTitle(row)` renders the generated `<td>`'s native `title`, symmetrical with
934
+ `cellStyle`. Returning `undefined` or an empty string omits the attribute entirely rather than
935
+ rendering `title=""`, which would suppress an ancestor's own tooltip, and the attribute is
936
+ suppressed while that cell is in inline-edit mode so the tooltip cannot shadow the editor. Some
937
+ screen readers announce a `<td title>` as the cell's accessible name, so use it only for a longer
938
+ form of what the cell already shows.
939
+ - New `layout: 'auto' | 'fixed' = 'auto'` property (reflected) sets a floor for the table's
940
+ `table-layout`. `fixed` applies the fixed algorithm even with no column widths declared; the
941
+ default `auto` still resolves to fixed whenever a column declares a `width` or a drag-resize is in
942
+ flight, since resizing does not work under `table-layout: auto`. Under `fixed` with no declared
943
+ widths the first row determines every column's width — so revealing a `priority`-hidden column
944
+ re-measures all of them — and `columns[].minWidth`/`maxWidth` are ignored by the fixed algorithm.
945
+ - New `--lr-table-row-selected-bg` custom property (default `var(--lr-color-brand-quiet)`) recolors
946
+ the `aria-selected` row. Shadow Parts forbids an attribute selector after `::part()`, so
947
+ `::part(row)[aria-selected]` is invalid CSS and the selected row could previously only be
948
+ restyled by overriding the library-wide brand-quiet token. Unset, rendering is unchanged.
949
+
950
+ - 437bef5: `lr-table`: add a skeleton loading mode. A new `loadingAppearance: 'spinner' | 'skeleton'`
951
+ property (attribute `loading-appearance`, default `'spinner'` — unchanged output) controls how
952
+ `loading` renders. `'skeleton'` keeps the real `<colgroup>`, `<thead>`, filter field and
953
+ pagination footer in place and fills the table body with placeholder `<lr-skeleton>` rows, so a
954
+ cold load sketches the grid's shape and holds its column geometry instead of collapsing to a
955
+ spinner and reflowing when the rows arrive. The placeholder row count comes from the new
956
+ `skeletonRows` property (attribute `skeleton-rows`, default `0` = derive from the normalized
957
+ `pageSize`, capped at 20, else 3). Exactly one `role="status"` live region announces the load —
958
+ each placeholder opts out of its own announcement, so there is no per-cell live-region storm. A
959
+ `priority`-hidden column is given no visible placeholder cell. New `skeleton` CSS part targets the
960
+ placeholders.
961
+ - bc8cb8b: Make the focus-ring and icon-button-size tokens themeable from an ancestor, and fill out
962
+ `theme.css` with the inputs it was missing.
963
+
964
+ `--lr-focus-ring-width`, `--lr-focus-ring-offset` and `--lr-icon-button-size` were the only
965
+ three tokens declared as bare literals instead of chaining through a `--lr-theme-*` input.
966
+ That made them the only tokens genuinely unreachable for subtree theming: a `--lr-*` token is
967
+ re-declared on **every** `LyraElement`'s `:host`, so a value set on an ancestor is shadowed at
968
+ the first intervening lyra host and never reaches anything nested inside it. `--lr-theme-*`
969
+ inputs are declared only at `:root` (in `theme.css`) and never in component shadow styles, so
970
+ they _do_ inherit through nested shadow roots — which is why the bridge is the supported route.
971
+ The three tokens now read `--lr-theme-focus-ring-width`, `--lr-theme-focus-ring-offset` and
972
+ `--lr-theme-icon-button-size`, with their existing values as fallbacks, so nothing renders
973
+ differently by default.
974
+
975
+ Keep a resolved `--lr-theme-icon-button-size` at or above 24px: it backs the hit area of
976
+ `lr-date-input`, `lr-combobox`, `lr-input` and `lr-select`, and anything smaller fails
977
+ WCAG 2.2 SC 2.5.8 (Target Size (Minimum)).
978
+
979
+ `src/theme.css` also gains the type scale, spacing scale, stacking layers, chart palette,
980
+ the 16 ANSI terminal slots, the raised surface and both overlay scrims as real inputs — every
981
+ one set to the exact value it already fell back to, so importing the sheet changes no computed
982
+ value. Two fixes came with that:
983
+
984
+ - `.lr-dark` never set `--lr-theme-color-surface-raised`, so a `.lr-dark` page rendered raised
985
+ surfaces at the light `#f6f8fa` while `prefers-color-scheme: dark` rendered them at `#22272e`.
986
+ The dark block now mirrors the raised surface and the eight chart colors.
987
+ - `--lr-color-overlay` and `--lr-color-overlay-strong` both read a single
988
+ `--lr-theme-color-overlay` input, so defining that input flattened the strong scrim's `0.92`
989
+ onto the plain scrim's value. `--lr-color-overlay-strong` now has its own
990
+ `--lr-theme-color-overlay-strong` input, chained through the old one so a theme that sets only
991
+ `--lr-theme-color-overlay` still tints both exactly as before.
992
+
993
+ - e9c4f22: `lr-thread-list` forwards a `compact` row density
994
+
995
+ A reflected boolean `compact` (default `false`) that sets `compact` on every data-mode row
996
+ `<lr-conversation-item>`, mirroring how `editable` is already forwarded — the one-attribute way to
997
+ tighten a whole sidebar, where previously the only lever was styling `::part(row-item-base)` and
998
+ `::part(row-item-title)` by hand. The density itself lives on the row item; this property only
999
+ forwards it, so both components stay in sync from one implementation.
1000
+
1001
+ Slotted mode (empty `threads` _with_ real slotted content) is a documented no-op: that mode renders
1002
+ host-supplied `<lr-conversation-item>`s as-is, so the host sets `compact` on its own items there —
1003
+ the same division of responsibility slotted mode already has for every other row property.
1004
+
1005
+ - b9d78b7: `lr-thread-list` now forwards the row `<lr-conversation-item>`'s own CSS parts out of data mode under
1006
+ a `row-item-*` namespace: `row-item-base`, `row-item-option`, `row-item-leading`, `row-item-content`,
1007
+ `row-item-title`, `row-item-title-input`, `row-item-rename-button`, `row-item-excerpt`,
1008
+ `row-item-meta`, `row-item-timestamp` and `row-item-actions`.
1009
+
1010
+ Data mode builds each row itself, two shadow roots down, so until now none of those eleven parts were
1011
+ reachable from outside — including the two declarations that set row height. Row density could only
1012
+ be changed with `lr-thread-list::part(row) { --lr-theme-space-s: … }`, a whole-subtree retheme that
1013
+ also shrank everything nested in the row (a `renderActions` menu's items dropped below the
1014
+ touch-target floor and had to be un-retheme'd inline). `lr-thread-list::part(row-item-base)
1015
+ { padding-block: … }` now sets row density with no token override and no collateral damage.
1016
+
1017
+ The existing `row-leading`/`row-content`/`row-meta`/`row-actions`/`row-wrapper` parts are unchanged;
1018
+ they wrap this component's render-callback output, which is a different surface from the item's own
1019
+ internals. Purely additive: an unstyled thread list renders identically.
1020
+
1021
+ - 9010a89: `lr-thread-list` exposes a `row-wrapper` CSS part around `wrapRow` output.
1022
+
1023
+ `wrapRow` was the one row hook with no library-added part -- `renderLeading`, `renderRowContent`,
1024
+ `renderMeta` and `renderActions` each get a `row-*` wrapper, so a host wrapping a whole row had to
1025
+ thread its own class through the callback to lay it out. Its return value is now placed inside a
1026
+ `part="row-wrapper"` block `<div>`, reachable from outside as `lr-thread-list::part(row-wrapper)`.
1027
+
1028
+ The wrapper is deliberately unstyled and block-level, and is added only when `wrapRow` is set: the
1029
+ box the internal `lr-virtual-list` measures for windowing is its own `[part="row"]` one level up,
1030
+ and an unstyled block box contributes exactly its child's height to it, so measured row heights are
1031
+ unchanged. The part is row-only -- group headers never pass through `wrapRow` and never carry it.
1032
+
1033
+ - 81af4b0: Add `sticky-groups` to `lr-thread-list`: the current date/custom group's header stays pinned to the
1034
+ top of the scroll viewport while its rows are in view, and is pushed off as the next group's header
1035
+ arrives. Group headers are ordinary virtualized rows, so this renders an `aria-hidden` copy into
1036
+ `lr-virtual-list`'s sticky layer — the real row keeps the `role="heading"` semantics and the tab
1037
+ order, while the pinned copy stays clickable and requests the same `lr-group-toggle` collapse. The
1038
+ band is exported as `::part(group-sticky)`, and the copy renders the same
1039
+ `group-header`/`group-toggle`/`group-label`/`group-icon` parts as the real header, so existing
1040
+ header styling applies to both. Default `false` renders exactly as before.
1041
+ - 81af4b0: Remove `lr-thread-list`'s reach into the internal `lr-virtual-list`'s shadow root. Arrowing past the
1042
+ rendered window now scrolls through the child's public `scrollContainer` and waits for its
1043
+ `lr-scroll` notification before moving focus, instead of mutating the scroll position of an element
1044
+ found by querying the child's render root and then dispatching a fabricated `scroll` event at it —
1045
+ which also raced the child's re-render rather than following it. Row lookup goes through a new
1046
+ `lr-virtual-list.renderedRows` accessor (the currently-windowed `[part="row"]` wrappers, in item
1047
+ order), added because a windowed list gives a host no other way to reach a row that may not have
1048
+ existed a frame earlier; `exportparts` forwards styling, not element references.
1049
+ - cea6d8e: `lr-token-input` can now edit a token in place. Set `editable` and each token becomes a roving tab
1050
+ stop that opens an inline editor on click, Enter, or F2: Enter commits and emits
1051
+ `lr-token-edit` with `{ value, previousValue, index }`, Escape reverts silently, and a blur commits
1052
+ without stealing focus back. New `token-label` and `token-editor` CSS parts (rendered only while
1053
+ `editable` is set) and a `--lr-token-input-editor-inline-size` custom property style the two states;
1054
+ with `editable` unset the token row renders exactly as before and stays non-focusable.
1055
+
1056
+ `delimiter` now accepts `null` — as a property, or via `delimiter="none"` / `delimiter=""` — so a
1057
+ token may contain commas verbatim (`Bash(git status:*)`): nothing is split and no keystroke is
1058
+ treated as a commit key. Removing the attribute restores the `,` default, and an empty delimiter no
1059
+ longer explodes a draft into one token per character.
1060
+
1061
+ - 0a5666d: `<lr-tree>` gains a `reorderable` opt-in for keyboard reordering. With it set, Ctrl/Cmd+ArrowUp /
1062
+ Ctrl/Cmd+ArrowDown on the focused row emits `lr-reorder` with
1063
+ `detail: { id, parentId, fromIndex, toIndex }` — sibling-scoped indices within the node's own
1064
+ parent's child list (`parentId` is `null` for a top-level item), so a reorder can never turn into
1065
+ a reparent at a subtree boundary. The keybinding matches `<lr-dashboard-grid>`'s existing
1066
+ `cells-draggable` keyboard move; Alt+Arrow was avoided because it is browser back/forward on
1067
+ Windows and Linux. `data` stays host-owned — the event is a request, and the move is announced
1068
+ through an internal `<lr-live-region>` (new `treeNodeMoved` message key).
1069
+
1070
+ Also fixes a pre-existing focus bug this surfaced: reassigning `data` in a way that merely
1071
+ _re-indexes_ the focused node (rather than removing it) dropped real DOM focus to `<body>`.
1072
+ Focus now follows the node, including for nested rows several shadow roots down.
1073
+
1074
+ `reorderable` is `false` by default — unset, markup and keyboard behaviour are unchanged and no
1075
+ `lr-reorder` is ever emitted. `<lr-file-tree>` deliberately does not forward it: its tree items are
1076
+ derived from `nodes` and keyed by filesystem path, an order it does not own.
1077
+
1078
+ - 8774f0d: Add `lr-virtual-list` position queries: `offsetForIndex(index)` returns the pixel top row `index`
1079
+ renders at (clamped to `0…items.length`, so `offsetForIndex(items.length)` is the total content
1080
+ height), and `indexAtOffset(px)` returns the row whose box contains that offset (`-1` for an empty
1081
+ list). Both work in the same coordinate space as the scroll container's `scrollTop`, so a host can
1082
+ do scroll-linked layout without duplicating the windowing math; in `row-height="auto"` mode an
1083
+ unmeasured row's offset stays estimate-based until its `ResizeObserver` measurement lands.
1084
+ - 8774f0d: Add `lr-virtual-list`'s sticky group header layer. Setting `renderStickyGroup` renders a
1085
+ `[part="sticky-group"]` overlay pinned to the top of the scroll viewport showing whichever `groups`
1086
+ entry the viewport is currently inside, pushed out by the overlap as the next group's header arrives
1087
+ rather than swapped abruptly. Native `position: sticky` on the rows themselves is structurally inert
1088
+ here, since every row is absolutely positioned and transform-offset by the windowing math.
1089
+
1090
+ The overlay is a visual copy of content that already exists in the list, so it is `aria-hidden`, its
1091
+ ordinary focusable content is forced to `tabindex="-1"` (the real row keeps sole ownership of the
1092
+ heading semantics and of the tab order), and it is `pointer-events: none` until a consumer opts in
1093
+ with `lr-virtual-list::part(sticky-group) { pointer-events: auto; }`. It is measured by its own
1094
+ `ResizeObserver` and never by the row observer, so a group header that is also a real row is not
1095
+ double-counted in `row-height="auto"` mode. A `groups` entry whose `label` is the empty string now
1096
+ renders no `[part="group"]` marker — it is a pure position anchor, for a host that renders its own
1097
+ group headers as rows. With `renderStickyGroup` unset, nothing about the rendered output changes.
1098
+
1099
+ - 8774f0d: Add `lr-virtual-list`'s public `scrollContainer` getter (the `[part="base"]` scroll box, `undefined`
1100
+ before the first render) and an `lr-scroll` event (`detail: { scrollTop, viewportHeight }`). The
1101
+ event is emitted from the animation frame that already coalesces native `scroll` events, so a burst
1102
+ of them produces at most one `lr-scroll` per frame and none at all when the position did not change.
1103
+ Together they let a host follow _sub-row_ scroll movement — which `lr-visible-range-changed`, firing
1104
+ only on index-range changes, cannot report — without reaching into the component's shadow root or
1105
+ dispatching synthetic `scroll` events at it.
1106
+
1107
+ ### Patch Changes
1108
+
1109
+ - 2e16fad: Fix `lr-artifact-panel`'s restore/copy/download header buttons rendering fully raw browser chrome
1110
+ (zero CSS at all) while the adjacent header buttons in the same row are fully themed, and give
1111
+ view-button its own hover/focus-visible to match its version-previous/version-next siblings.
1112
+ - c2ddee5: Fix `lr-av-player`'s playback-rate `<select>` rendering raw browser chrome with an unthemed
1113
+ (typically white) option popup regardless of theme -- it now resets native appearance, themes its
1114
+ option list, and gains hover/focus-visible states and a decorative chevron in place of the removed
1115
+ native one.
1116
+ - db4e0a5: Fix `lr-calendar`'s previous-month nav button never matching its own styling rule (it rendered with
1117
+ raw browser button chrome next to a fully themed next button) and add missing `:hover`/`:focus-visible`
1118
+ treatment to the nav buttons, day-grid cells, and agenda-event buttons.
1119
+ - bfaf7f9: `lr-checkbox-group`: document `value` as a read-out of child state, and warn on the two ways it is
1120
+ misused.
1121
+
1122
+ `value` shipped with no documentation at all while the generated docs listed it among settable
1123
+ properties, so it read as an input. It never was one: `sync()` recomputes it from the
1124
+ `<lr-checkbox>` children and assigns it on every child toggle, `slotchange`, `name`/`required`
1125
+ change, blur and `form.reset()` — and `connectedCallback()` syncs _before the first render_, so even
1126
+ a constructor-time or template-time `.value=` binding is discarded before it is ever observed. It
1127
+ now carries that contract in its JSDoc, and:
1128
+
1129
+ - assigning `value` from outside logs a `console.warn` naming the property and pointing at `checked`
1130
+ on the children (once per element — a repeat assignment is the same mistake, not new information);
1131
+ - a group with two or more children sharing a `value` logs a `console.warn` too. This is the _easy_
1132
+ mistake, not an exotic one: `<lr-checkbox>`'s `value` defaults to `'on'`, so five undifferentiated
1133
+ children yield `['on','on','on','on','on']` and a `FormData` that cannot say which was checked.
1134
+
1135
+ Both warnings follow the same plain-`console.warn` shape as the library's other authoring-mistake
1136
+ warnings (`lr-task-list` over-nesting, `lr-dashboard-grid` unmatched `cell-id`, `lr-flow-canvas`
1137
+ unrecognized child). No behavior changed for the normal children-drive-value flow, which warns not
1138
+ at all.
1139
+
1140
+ `value` was deliberately **not** made authoritative. Push-down is unimplementable without surprise
1141
+ while children default to `value = 'on'` (a host assigning `['on']` would check every
1142
+ undifferentiated child), and it would additionally need a re-entrancy guard and a pending-value
1143
+ retention path for children that have not upgraded yet. Recorded here so a later release can add a
1144
+ distinct `defaultValue` API without reversing anything documented now.
1145
+
1146
+ - 2a45da4: Fix four components (`lr-chunk-inspector`, `lr-community-card`, `lr-provenance-panel`,
1147
+ `lr-notebook-viewer`) whose real `<button>`s get UA-chrome reset (`border:none; background:
1148
+ transparent; cursor:pointer;`) but no hover or focus-visible of their own -- `lr-provenance-panel`'s
1149
+ disclosure header (`aria-expanded`/`aria-controls`) had zero visible keyboard focus indicator at all.
1150
+ - 1d121a9: Fix `lr-code-block`/`lr-code-block-core`'s shiki dark-theme override only activating on the OS-level
1151
+ `prefers-color-scheme` media query -- a consumer who sets `--lr-theme-color-*` explicitly, without the
1152
+ OS itself being in dark mode, now correctly gets the dark shiki syntax theme too, matching every other
1153
+ `--lr-color-*` token's consumer-overrides-first resolution.
1154
+ - 1372546: Fix `lr-color-picker`'s native color swatch -- the directly visible, directly focusable control --
1155
+ having no hover or focus-visible treatment, so tabbing to it fell through to the browser's raw
1156
+ default color-input focus ring.
1157
+ - f8bc916: `lr-combobox`: the `clearable` button now covers the filter axis as well as the selection.
1158
+
1159
+ Typing a query that matches nothing left the user with no affordance to clear it — the button was
1160
+ gated on a committed selection alone, and `clear()` early-returned on an empty selection. It now
1161
+ renders whenever there is something to clear on either axis, and each axis announces only its own
1162
+ change: clearing a selection still emits `input`/`change`/`lr-clear`, while clearing filter text
1163
+ emits `lr-filter` with an empty `value` and no spurious selection events.
1164
+
1165
+ The query half of the gate is scoped to states where the query is actually visible — the open
1166
+ listbox in single-select, or any time in `multiple` mode. A closed single-select shows the selected
1167
+ label rather than the query, so a stale query alone never surfaces a button offering to clear text
1168
+ the user cannot see.
1169
+
1170
+ - 77bfb28: Fix `lr-data-grid`'s sort-header focus ring targeting `<th>`, which can never itself receive
1171
+ keyboard focus (only its nested sort button can) -- tabbing to a sortable column header now shows
1172
+ the library's focus ring instead of the browser's raw default, and the sort button gains a
1173
+ matching hover state.
1174
+ - dfd6199: Fix `lr-date-picker`'s previous/next month-nav buttons having a hover state but no focus-visible ring
1175
+ -- the file's only focus-visible coverage was on day cells, leaving keyboard users with no visible
1176
+ indicator on the nav buttons.
1177
+ - 7c99e80: Route several stray hardcoded style values through design tokens so visually-identical states stay
1178
+ in sync across components:
1179
+
1180
+ - **Disabled controls** in `lr-node-palette`, `lr-flow-controls`, `lr-compare-panel`,
1181
+ `lr-graph-query-builder`, and `lr-rubric-form` now dim through the shared `--lr-opacity-disabled`
1182
+ token instead of one-off `0.4`/`0.5`/`0.6` literals, so every disabled control fades by the same
1183
+ amount (and rethemes with one property).
1184
+ - **Anchored popovers/menus/tooltips** (`lr-menu`, `lr-select`, `lr-combobox`, `lr-date-input`,
1185
+ `lr-model-select`, `lr-voice-picker`, `lr-mention-popover`, `lr-export-button`, `lr-tour`,
1186
+ `lr-tool-call-chip`, `lr-usage-badge`, `lr-citation-badge`, `lr-entity-chip`,
1187
+ `lr-knowledge-graph-explorer`) share a new `--lr-popover-viewport-clamp` token (default `92vw`,
1188
+ themeable via `--lr-theme-popover-viewport-clamp`). Previously these split between `92vw` and
1189
+ `90vw`, so two popovers side by side could clamp to different widths; they now clamp consistently.
1190
+ - **Solid-fill hover lift** on `lr-chat-composer`, `lr-tool-approval-dialog`, `lr-message-feedback`,
1191
+ `lr-tour`, and `lr-retrieval-search` now shares a new `--lr-hover-brightness` token (default
1192
+ `1.08`, themeable via `--lr-theme-hover-brightness`), replacing per-component `filter: brightness()`
1193
+ magic numbers. Note `lr-retrieval-search`'s submit button now _brightens_ on hover like every other
1194
+ brand button, where it previously darkened (`0.92`).
1195
+ - `lr-calendar`'s narrow-container day-cell floor now references the existing `--lr-size-4rem` token
1196
+ instead of a raw `4rem`, matching its wide-container sibling.
1197
+
1198
+ Also adds a new consumer override hook: `--lr-responsive-panel-sheet-max-block-size` (default `85dvh`,
1199
+ falling back to `85vh` where `dvh` is unsupported) lets you set the maximum height of an
1200
+ `lr-responsive-panel` `variant="bottom-sheet"` overlay, which previously had no override at all.
1201
+
1202
+ - ac5936a: Fix `lr-details`' summary -- the component's real, natively-focusable/clickable surface -- having no
1203
+ hover or focus-visible treatment at all. `lr-accordion-item` (which extends `lr-details` with no
1204
+ style override) is fixed by the same change.
1205
+ - 188335c: Sync the consumer-facing agent reference (`llms/`) with the part-reachability, density and composed
1206
+ -content work that just landed across the viewers, media, retrieval, agent-tools, layout,
1207
+ conversation and data families.
1208
+
1209
+ - Document the newly forwarded and newly named CSS parts on `lr-pdf-viewer`, `lr-archive-viewer`,
1210
+ `lr-page-rail`, `lr-notebook-viewer`, `lr-csv-viewer`, `lr-spreadsheet-viewer`,
1211
+ `lr-dataset-viewer`, `lr-av-player`, `lr-terminal`, `lr-ingestion-queue`, `lr-neighbor-list`,
1212
+ `lr-chunk-inspector`, `lr-retrieval-results` and `lr-activity-feed`, including why row state is
1213
+ published as an extra part name rather than an attribute on the part.
1214
+ - Replace the paragraphs that described `--lr-page-rail-current-bg`,
1215
+ `--lr-notebook-viewer-active-bg`, `--lr-av-player-cue-current-bg` and
1216
+ `--lr-av-player-cue-active-match-color` as declared-but-inert; all four now take effect.
1217
+ - Document `--lr-csv-viewer-highlight-color` and `--lr-spreadsheet-viewer-highlight-color`, and
1218
+ `--lr-trace-tree-row-active-color` (plus the pairing rule it forms with
1219
+ `--lr-trace-tree-row-active-bg`, and the knock-on note under `lr-agent-trace`).
1220
+ - Document `lr-menu`'s `header`/`footer` slots and parts, the revised Escape/Tab keyboard contract,
1221
+ and the narrowed scope of `closeOnEscapeAnywhere`.
1222
+ - Document `lr-table`'s `columns[].editable: 'always'` persistent editors, `lr-flow-node`'s
1223
+ `compact` and `card` part, `lr-flow-controls`' and `lr-chat-composer`'s `appearance`, and
1224
+ `lr-conversation-item`/`lr-thread-list`'s `compact`.
1225
+
1226
+ - 2be1ad5: Sync the consumer-facing agent reference (`llms/`) with the sticky group-header work on
1227
+ `lr-virtual-list` and `lr-thread-list`.
1228
+
1229
+ - Document `lr-virtual-list`'s `renderStickyGroup`, the `sticky-group` CSS part, and the four
1230
+ behaviors a consumer would otherwise get wrong: the band is `aria-hidden` with its focusable
1231
+ descendants forced to `tabindex="-1"` (so it is never a second tab stop or a second heading, and a
1232
+ focus-delegating custom element inside it must set its own), it is `pointer-events: none` until
1233
+ opted back in through `lr-virtual-list::part(sticky-group)`, it is never measured as a row, and it
1234
+ stays mounted but hidden above the first group so its scroll inset is measurable before the first
1235
+ jump.
1236
+ - Document that a `groups` entry with an **empty** `label` renders no marker and acts as a pure
1237
+ position anchor, and drop the stale claim that `groups` had no visible effect and that its marker
1238
+ carried `role="heading"`.
1239
+ - Document `offsetForIndex()`/`indexAtOffset()`, the `scrollContainer`/`renderedRows` getters, the
1240
+ `lr-scroll` event and its `VirtualListScroll` detail type, and add a sticky-group usage example.
1241
+ - Document `lr-thread-list`'s `stickyGroups` property (attribute `sticky-groups`) and the
1242
+ `group-sticky` exported part, including that the real header row keeps the
1243
+ `role="heading"`/`aria-level` semantics and the tab order while the pinned copy stays clickable.
1244
+
1245
+ - ed762ff: `lr-xml-viewer` treats `--lr-icon-button-size` as a floor
1246
+
1247
+ `lr-xml-viewer`'s node `[part='toggle']` is an interactive button that pinned the shared
1248
+ minimum-target token as a fixed `inline-size`/`block-size` with `padding: 0` and no floor — the
1249
+ opposite of what the token's own definition documents ("components pad out to this via
1250
+ `min-inline-size`/`min-block-size`, not by growing the glyph itself"). It now sizes its glyph box at
1251
+ `--lr-size-1-25rem` with `min-inline-size`/`min-block-size: var(--lr-icon-button-size)`, mirroring
1252
+ `lr-code-block`'s equivalent toggle, so lowering the token shrinks the hit area but never squashes
1253
+ the chevron.
1254
+
1255
+ - 4c59cc2: Fix `lr-image-viewer`'s fit-mode `<select>` rendering raw browser chrome with an unthemed option
1256
+ popup, and add missing hover/focus-visible to all three toolbar controls (fit-control, rotate-button,
1257
+ annotate-toggle) -- previously none of the three had either state.
1258
+ - 10c8b91: Fix `lr-input` (and `lr-time-input`, which renders through the same template/stylesheet) keeping
1259
+ native browser chrome in three cases: the search-cancel glyph only reset while `clearable` was set
1260
+ (the common non-clearable case kept it), `type="number"` never resetting the spin-button, and
1261
+ `type="time"` never touching its calendar-picker-indicator at all -- now restyled, not suppressed,
1262
+ since it's the only mouse/touch affordance to open the native time picker.
1263
+ - 0410eb7: Fix two factual errors in the shipped agent-facing reference (`llms/shared.md`, and the
1264
+ `llms.txt`/`llms-full.txt`/`llms/` artifacts generated from it): the internals section stated
1265
+ `LYRA_PREFIX = 'lyra'` when the constant is `'lr'` — on the same line that correctly showed
1266
+ `tag(name)` producing `` `lr-${name}` `` — and claimed a hardcoded count of 127 `Lyra*EventMap`
1267
+ types when there are now 181. The count is no longer stated as a number, so it cannot drift again.
1268
+ - 184bfff: `lr-menu`: axe coverage for a composed popup, stories moved onto the new `header`/`footer` slots,
1269
+ and the three shipped descriptions of what this component accepts finally agree.
1270
+
1271
+ - New axe assertion for a menu with a `header` `<input>` and a `footer` `<button>` — the exact shape
1272
+ that was an `aria-required-children` violation while the only place for it was inside
1273
+ `role="menu"`, and which no test covered.
1274
+ - `show() / hide({ focusTrigger: true })`'s Apply button moves to `slot="footer"`, and the filter
1275
+ field gets a new `header`-slot story. The old default-slot filter story stays, relabelled as the
1276
+ legacy shape it now is, so its `closeOnEscapeAnywhere` behavior remains covered.
1277
+ - The class doc's `@slot` tag said "menu items and `<hr>` only" while the interaction contract two
1278
+ paragraphs above it promised slotted controls "keep their own full default keyboard behavior" and
1279
+ `show()`/`hide()` named a slotted Apply button as a supported case. All three now describe the
1280
+ same component.
1281
+
1282
+ - 3c8a299: Add missing `:hover` to six agent-tools components (`lr-browser-frame`, `lr-commit-card`,
1283
+ `lr-terminal`, `lr-test-results`, `lr-compare-panel`, `lr-confirm-bar`) whose interactive buttons
1284
+ already had `cursor: pointer` and a correct focus-visible ring but no hover affordance for mouse
1285
+ users.
1286
+ - 4ac6c31: Add missing `:hover` to six components (`lr-stack-trace`, `lr-span-waterfall`, `lr-chat-viewport`,
1287
+ `lr-checkpoint`, `lr-push-to-talk`, `lr-transcript-feed`) whose interactive controls already had
1288
+ `cursor: pointer` and a correct focus-visible ring but no hover affordance for mouse users.
1289
+ - 696cc7f: Add missing `:hover` to six components (`lr-env-list`, `lr-graph-query-builder`, `lr-rubric-form`,
1290
+ `lr-chart`, `lr-scroller`, `lr-widget`) whose interactive controls already had `cursor: pointer` and a
1291
+ correct focus-visible ring but no hover affordance for mouse users; `lr-chart`'s reset-zoom-button also
1292
+ gains `font: inherit`, which it was missing entirely.
1293
+ - e1b9c22: Add missing `:hover` to six components (`lr-carousel`, `lr-dashboard-grid`, `lr-callout`,
1294
+ `lr-memory-panel`, `lr-neighbor-list`, `lr-path-strip`) whose interactive controls already had
1295
+ `cursor: pointer` and a correct focus-visible ring (where applicable) but no hover affordance for
1296
+ mouse users.
1297
+ - e73a243: Add missing `:hover` to six components (`lr-retrieval-results`, `lr-pdf-viewer`, `lr-ebook-viewer`,
1298
+ `lr-pptx-viewer`, `lr-email-viewer`, `lr-dataset-viewer`) whose interactive controls already had
1299
+ `cursor: pointer` and a correct focus-visible ring but no hover affordance for mouse users.
1300
+ - ae8e04e: Fix nine components (`lr-combobox`, `lr-eval-dataset`, `lr-command-palette`, `lr-table`,
1301
+ `lr-tool-select-dialog`, `lr-code-editor`, `lr-message-feedback`, `lr-model-select`, `lr-voice-picker`)
1302
+ whose native `<input>`/`<textarea>` themed background/color/border correctly but left `::placeholder`
1303
+ at the browser's fixed light-tuned default -- each field's placeholder text now uses
1304
+ `--lr-color-text-quiet`, with Firefox's reduced default `::placeholder` opacity undone on the
1305
+ `type="search"` fields.
1306
+ - e649e77: Fix `lr-node-palette`'s search field being the only `type="search"` field in its family with zero
1307
+ focus-ring styling (its siblings `lr-thread-list`/`lr-emoji-picker` already wire this), and reset the
1308
+ native search-cancel glyph to match.
1309
+ - e879ff6: Fix `<lr-split>` and `<lr-stepper>` reporting a stale `effectiveOrientation` (and
1310
+ `data-effective-orientation`) when `orientation-breakpoint-basis="viewport"` and the viewport
1311
+ crossed the breakpoint while the element was detached from the DOM. The media-query listener is
1312
+ torn down on disconnect and a plain reconnect schedules no Lit update, so the missed crossing was
1313
+ never noticed; reconnecting now re-reads the query and announces the crossing (including
1314
+ `lr-split-orientation-change` / `lr-stepper-orientation-change`) only when the matched state
1315
+ actually differs. A plain mount, and a reconnect that crossed nothing, stay silent as before.
1316
+ - c2ea153: Fix `lr-pagination`'s page-input and `lr-tool-param-form`'s numeric JSON-schema fields rendering the
1317
+ native spin-button inside a fixed-size control box -- the adjacent prev/next buttons (pagination) and
1318
+ form validation (tool-param-form) already provide stepping, so removing the spinner loses no
1319
+ functionality.
1320
+ - 94fa823: `lr-terminal`, `lr-ingestion-queue` and `lr-dataset-viewer`: forward the CSS parts rendered through
1321
+ their internal `<lr-virtual-list>` so a consumer can actually reach them.
1322
+
1323
+ All three already styled those parts correctly from their own stylesheets, but none forwarded
1324
+ `exportparts` from the `<lr-virtual-list>` element. Because the rows are `renderItem`'s output and
1325
+ therefore live inside that element's own shadow root, a consumer rule like
1326
+ `lr-terminal::part(line)` matched nothing at all — the documented parts were unreachable from
1327
+ outside the component.
1328
+
1329
+ - `lr-terminal` now exports `line`.
1330
+ - `lr-ingestion-queue` now exports `item`, `item-header`, `item-name`, `item-progress`,
1331
+ `item-meta`, `item-error`, `item-actions`, `retry-button` and `cancel-button`.
1332
+ - `lr-dataset-viewer` now exports `data-row`, `cell`, `cell-highlight` and `cell-highlight-action`.
1333
+
1334
+ No styling changed and no new parts were added.
1335
+
1336
+ - 56f7b65: Add a build-time guard against `::part()` CSS that parses but never matches.
1337
+
1338
+ Two classes of silently-inert rule are now caught by `pnpm lint` (a new
1339
+ `scripts/check-part-reachability.mjs` in the contract-policy chain), neither of which any existing
1340
+ check — TypeScript, the style policy, or a test that inspects stylesheet text — could see:
1341
+
1342
+ - **`cross-root-part`** — a component that mounts `<lr-virtual-list>` and hands it a
1343
+ `renderItem`/`renderGroup` callback renders those rows into _that element's_ shadow root, so a
1344
+ bare `[part='x']` selector in the composing component's own stylesheet can never match them. The
1345
+ checker cross-references the literal part names emitted from the callback (following the class
1346
+ members it reaches) against the bare `[part]` selectors in the sibling `*.styles.ts`, and reports
1347
+ any name that has no `lr-virtual-list::part(x)` rule anywhere in that file. Components that
1348
+ legitimately render the same part into both roots — below/above a virtualization threshold, or a
1349
+ directly-rendered header row — carry both selectors and are not flagged; a
1350
+ `policy-allow(cross-root-part):` comment covers anything else.
1351
+ - **`part-compound`** — per Selectors L4 a pseudo-element may only be followed by pseudo-classes, so
1352
+ `::part(a)[attr]`, `::part(a).cls`, `::part(a) .descendant` and `::part(a) > .child` parse and
1353
+ then match nothing. Every `*.styles.ts` is scanned for those shapes; `::part(a):hover`,
1354
+ `::part(a)::selection` and the part-list form `::part(a b)` remain valid and pass.
1355
+
1356
+ No component behavior changes; the library is clean under both rules today.
1357
+
1358
+ - cce32a2: `lr-neighbor-list`: make the virtualized relationship rows and group headers actually styleable, by
1359
+ this component and by a consumer.
1360
+
1361
+ Above `virtualizeAt` the rows are produced by this component's `renderItem` but committed into the
1362
+ embedded `<lr-virtual-list>`'s own shadow root, one boundary deeper than a `[part='row']` selector
1363
+ can reach — so every row, node-label, direction, relation, meta and expand-button rule was silently
1364
+ inert and a large neighborhood rendered as raw browser `<button>`s with no dividers. Each rule now
1365
+ pairs its plain selector (still correct at/below the threshold) with an `lr-virtual-list::part(…)`
1366
+ twin, and an `exportparts` forwarding declaration makes the same parts reachable as
1367
+ `lr-neighbor-list::part(node-label)` etc. from a consuming stylesheet.
1368
+
1369
+ Group headers were unstyled whenever the list virtualized: in that path the header is the internal
1370
+ virtual-list's own `group` part, which this component neither styled nor exported. It is now styled
1371
+ to match `group-header` and exported under that same name, so grouped rows present identically
1372
+ either side of the threshold.
1373
+
1374
+ The virtualized rows no longer nest a second `role="listitem"`/`part="row"` element inside the
1375
+ virtual-list's own row wrapper. `renderItem` returns just the row's content, exactly as the
1376
+ non-virtualized path's own wrapper receives it: the duplicate nesting both reported a `listitem`
1377
+ inside a `listitem` and made the row's padding and divider border apply twice, since `::part()`
1378
+ matches at any depth of the target shadow tree.
1379
+
1380
+ - 1e518e6: Fix `lr-playback`'s range slider only getting a pointer cursor in its disabled state, unlike the
1381
+ adjacent play button, and add a matching hover affordance.
1382
+ - 5e9a18e: `lr-table`: focus the cell editor that was actually just opened by a double-click. The autofocus
1383
+ looked up `[part="cell-editor"]` across the whole grid and focused whichever one came first in the
1384
+ DOM — indistinguishable from correct while only one editor could ever exist at a time, but wrong as
1385
+ soon as a column renders persistent (`editable: 'always'`) editors of its own. It is now scoped to
1386
+ the opening cell's own row and column.
1387
+ - 326973c: Fix `lr-thread-list` and `lr-emoji-picker`'s otherwise fully-themed search fields showing a raw
1388
+ gray browser "x" glyph (with its own hit target and hover behavior, ignoring every token applied to
1389
+ the field) once non-empty.
1390
+ - 9010a89: `lr-thread-list` and `lr-chat-viewport` now size their virtual list to their own height.
1391
+
1392
+ Both composed an `lr-virtual-list` without ever setting `--lr-virtual-list-height`, so the list
1393
+ scrolled inside that token's 24rem default no matter how tall the surrounding pane was -- a
1394
+ `<lr-thread-list>` in a 700px sidebar showed a 384px scroller with dead space underneath, and every
1395
+ consumer had to hand-set `--lr-virtual-list-height` to work around it. Both now fill the height they
1396
+ are given with no consumer CSS. `lr-thread-list` degrades safely: in a container with no resolvable
1397
+ height the internal viewport still renders at exactly the 24rem it does today (the shipped default
1398
+ becomes the list's flex-basis rather than a percentage that would collapse to zero or grow to the
1399
+ full un-virtualized content height). `lr-chat-viewport`'s virtual mode uses a percentage -- the
1400
+ slotted list lives in the consumer's light DOM, out of reach of `::part()` -- so it, like slotted
1401
+ mode's own scroll container, needs a height-bounded parent. A consumer rule or inline style setting
1402
+ `--lr-virtual-list-height` on the list still wins in both components.
1403
+
1404
+ Also fixes `lr-chat-viewport`'s virtual-mode layout rules, which were written as
1405
+ `:host(:has(> lr-virtual-list))`. `:has()` is invalid inside `:host()`, so those rules were silently
1406
+ dropped: in virtual mode `[part="scroll"]` kept the padding and `overflow-y: auto` it is documented
1407
+ to give up, and `[part="content"]` never got the height the slotted list sizes against.
1408
+
1409
+ - 3e1d4f8: Fix `lr-token-input`'s draft-input and inline token-editor leaving `::placeholder` at the browser's
1410
+ default color, and add missing `:hover`/`:focus-visible` to the per-token remove button.
1411
+ - 67a7881: Cover `lr-trace-tree`'s active row with an axe assertion. The active-row test group previously
1412
+ carried a comment explaining why no accessibility assertion could be made there — the default tint
1413
+ put the row's own secondary text below the WCAG AA contrast floor, so any axe run against a
1414
+ populated active row would have failed. With that fixed, the assertion now runs for real: a
1415
+ populated tree is asserted accessible with each status tone in turn made active, after first
1416
+ proving the fixture actually reached the `[data-active]` state so the check cannot pass vacuously.
1417
+ It was verified to bite by reverting the fix and confirming axe reports the exact contrast
1418
+ violations it is meant to catch. The active-row Storybook story now sets
1419
+ `--lr-trace-tree-row-active-bg` and `--lr-trace-tree-row-active-color` together and documents why
1420
+ they are a pair.
1421
+ - 67a7881: Fix `lr-trace-tree`'s active-row secondary text falling below the WCAG AA 4.5:1 contrast floor. The
1422
+ active (`activeSpanId`) row paints `--lr-color-brand-quiet`, against which `--lr-color-text-quiet`
1423
+ lands at ~4.25:1 — so `detail`, `duration`, `tokens-in`, `tokens-out`, `cost` and the `pending`
1424
+ status label were all failing while the row was active, even though every one of them passes
1425
+ comfortably against the plain row background. Those parts now render at full-strength
1426
+ `--lr-color-text` once the row is active (15.3:1 in light mode, 11.2:1 in dark), the same fix
1427
+ `lr-conversation-item` already carries for the identical bug. Darkening the active tint instead
1428
+ would have made it worse: every failing foreground is dark text.
1429
+
1430
+ This changes default rendering on the active row, which is intended — the previous default was a
1431
+ real accessibility failure. The new `--lr-trace-tree-row-active-color` custom property retunes it;
1432
+ it pairs with `--lr-trace-tree-row-active-bg`, and a consumer setting that to a dark tint in light
1433
+ mode should set both, because the defaults assume the active background stays on the same side of
1434
+ the lightness midpoint as the ambient surface.
1435
+
1436
+ - 67a7881: Raise `lr-trace-tree`'s active-row status labels to clear WCAG AA without flattening their hue.
1437
+ `[part='status-text']` on the active row now renders
1438
+ `color-mix(in srgb, var(--lr-color-<tone>) 75%, var(--lr-color-text))` for each semantic tone —
1439
+ success moves from 4.46:1 to 6.18:1 and `denied` from 4.28:1 to 5.96:1 against the default active
1440
+ tint, while `error` and `running` (which only barely cleared the floor) gain headroom too. Keeping
1441
+ the hue matters: an error row that stops being red once selected loses the fastest scan signal in a
1442
+ trace list.
1443
+
1444
+ The mix is applied to every semantic tone rather than only the two that fail at the shipped
1445
+ defaults, because a per-status carve-out is theme-fragile — a consumer retheming one `--lr-color-*`
1446
+ moves that ratio and would silently re-break. It is also theme-symmetric by construction:
1447
+ `--lr-color-text` flips with the color scheme, so the same declaration darkens the label in light
1448
+ mode and lightens it in dark mode. `[part='bar']` is deliberately untouched — it is a non-text
1449
+ graphic on a 3:1 floor it already passes, and scoping the mix to `[part='status-text']` avoids
1450
+ re-pointing a consumer's own `--lr-color-*` override inside one row.
1451
+
1452
+ - 4df6ca1: `lr-virtual-list` no longer traps a popup opened from inside a row underneath the rows that follow
1453
+ it. `[part="row"]` sets `will-change: transform`, which makes every row its own stacking context, and
1454
+ rows carried no `z-index` — so they painted in DOM order and each row painted over the previous one.
1455
+ A `lr-menu` dropdown rendered in a row (for example through `lr-thread-list`'s `renderActions`) was
1456
+ positioned, visible and hit-testable, yet painted _under_ the next rows: its own `z-index: 900` only
1457
+ orders siblings inside its row's context. The last row always looked correct, so small fixtures never
1458
+ caught it.
1459
+
1460
+ `[part="row"]:focus-within` now lifts the row to `var(--lr-layer-content)` — the same layer
1461
+ `[part="group"]` already uses — for exactly as long as something inside it holds focus. This also
1462
+ stops outward focus rings on a row being clipped by later rows. Nothing changes when no row holds
1463
+ focus.
1464
+
1465
+ - 8774f0d: Keep `lr-virtual-list`'s scroll-into-view clear of the sticky group band. With `renderStickyGroup`
1466
+ set, the band's measured height is applied as `scroll-padding-block-start` on the scroll container —
1467
+ so native keyboard and anchor scrolling get the same treatment — and subtracted from the
1468
+ top-aligned targets `active-id` and `scrollToIndex({ align: 'start' })` compute, which otherwise
1469
+ parked the target row underneath the band. `align: 'end'` is unaffected, since the band never
1470
+ covers the viewport's bottom edge. With `renderStickyGroup` unset the inset is zero and both scroll
1471
+ paths behave exactly as before, with no inline style on the container at all.
1472
+
1473
+ ## 5.1.0
1474
+
1475
+ ### Minor Changes
1476
+
1477
+ - 5f82bf7: Add role-scoped bubble cssprops to `lr-chat-message` — `--lr-chat-message-bubble-bg`,
1478
+ `--lr-chat-message-bubble-color`, `--lr-chat-message-user-bubble-bg`, and
1479
+ `--lr-chat-message-user-bubble-color` — so a consumer can retint one role's bubble fill/text
1480
+ without overriding the shared `--lr-color-brand-quiet`/`--lr-color-surface`/`--lr-color-text`
1481
+ tokens, which also drive unrelated parts of the component (e.g. `[part="collapse-button"]:hover`).
1482
+ All four default to exactly the values the bubble already used, so nothing changes for existing
1483
+ consumers who set none of them.
1484
+ - abd60dd: `lr-stepper`'s `orientation-breakpoint` now accepts a CSS length, not only a bare pixel number:
1485
+ `500`, `'500'`, `'500px'`, `'31.25rem'` and `'3em'` are all valid, and equal computed values behave
1486
+ identically.
1487
+
1488
+ `rem` resolves against the **document root**'s computed font size — exactly as a `rem` in a CSS
1489
+ `@media` query does, and deliberately _not_ against the stepper itself — so a breakpoint authored in
1490
+ `rem` stays numerically in step with the sibling `@media (max-width: …rem)` rule it has to agree
1491
+ with, instead of silently drifting from it when the root font size changes (browser zoom, a user
1492
+ font-size preference, an app base-size token). `em` resolves against the stepper's own computed font
1493
+ size. The length is re-resolved on every measurement and never cached, so those changes are picked
1494
+ up without any invalidation step on the consumer's side.
1495
+
1496
+ A value that isn't a usable length — `%`, `vw`, `calc()`, `'auto'`, an unparseable string — now
1497
+ behaves exactly as unset: no `ResizeObserver` is armed and no `data-effective-orientation` attribute
1498
+ appears, rather than arming a breakpoint that can never be crossed. For a viewport-relative
1499
+ breakpoint, leave `orientationBreakpoint` unset and drive `orientation` from your own `matchMedia()`
1500
+ controller; `orientationBreakpoint` measures the stepper's own allocated inline size, not the
1501
+ viewport.
1502
+
1503
+ The property's TypeScript type widens from `number | undefined` to `number | string | undefined`,
1504
+ and the `orientation-breakpoint` attribute is no longer coerced through Lit's `Number` converter.
1505
+ Every existing numeric usage — attribute or property — is unaffected. This mirrors the identical
1506
+ change to `lr-split`, whose `orientationBreakpoint`/`narrowOrientation` contract `lr-stepper`
1507
+ deliberately shares.
1508
+
1509
+ - 22cb935: `lr-heatmap` gains a `legendStops` property so the built-in legend can describe a custom
1510
+ `cellColor` domain. Because `cellColor` overrides a cell's color entirely, the legend's
1511
+ `--lr-heatmap-scale-lo`/`-hi` gradient bar could describe a ramp the grid no longer used, leaving a
1512
+ consumer to hide `::part(legend)` and hand-roll swatches.
1513
+
1514
+ `legendStops: HeatmapLegendStop[]` (`{ value, color, label? }`, `attribute: false`) renders a
1515
+ discrete key **instead of** that gradient bar — one `[part="legend-stop"]` per entry in array order,
1516
+ each a `[part="legend-swatch"]` in the entry's color plus a `[part="legend-stop-label"]`. Labels
1517
+ default to the component's own locale-aware numeric formatting of `value`, so an explicit `label` is
1518
+ only needed when the number isn't the right caption. `[part="legend-lo"]`/`[part="legend-hi"]` and
1519
+ the bar are omitted while stops are supplied; labeled `annotations` still render their
1520
+ `[part="legend-annotation"]` entries alongside them.
1521
+
1522
+ The stops are presentation only — they never feed back into the color ramp, the bucket math, the
1523
+ tooltip or the accessible name. Left unset (or empty), the legend renders exactly as before.
1524
+
1525
+ - ce2a423: `lr-combobox` now emits `lr-filter` (`detail: { value: string }`) on every user-driven change to its
1526
+ in-progress filter text, so consumers that need the live as-you-typed string — a "no matches for
1527
+ “x”" empty state, a debounced side effect — no longer have to reach into the component's shadow DOM
1528
+ for `[part="combobox-input"]`.
1529
+
1530
+ The name is deliberately not `lr-input`: on `lr-combobox` the host's `value` is the _committed
1531
+ selection_, so reusing `lr-input`'s event name would make one event name carry a different string on
1532
+ different components. `lr-filter` fires for user input only — picking a row, the clear button,
1533
+ `form.reset()`, dismissing the listbox, a programmatic `value` write and `setRangeText()` all blank
1534
+ the filter silently, mirroring how `<lr-input>`'s `lr-input` only reports user edits.
1535
+
1536
+ The `ComboboxFilterDetail` detail type is exported and `LyraComboboxEventMap` carries the new entry,
1537
+ so `addEventListener('lr-filter', …)` is typed.
1538
+
1539
+ - 7c46ced: `<lr-split>`'s `orientationBreakpoint` now accepts a CSS length string as well as a bare pixel
1540
+ number, so it can be authored in the same unit as the sibling CSS `@media` rule it has to agree
1541
+ with.
1542
+
1543
+ Accepted forms: `900` / `orientation-breakpoint="900"` (unchanged), `'900px'`, `'56.25rem'`, and
1544
+ `'3em'`. `rem` resolves against the **document root**'s font size — exactly as a `rem` in a CSS
1545
+ `@media` query does, not against the element — so a breakpoint written to match
1546
+ `@media (max-width: 56.25rem)` stays in sync with it across browser zoom, a user font-size
1547
+ preference, or an app-level base-size change. `em` resolves against the split's own computed font
1548
+ size. The length is re-resolved on **every** measurement rather than cached at first render, so a
1549
+ root font-size change moves the crossing width with no invalidation step.
1550
+
1551
+ Anything that isn't a resolvable length now behaves exactly as unset — no `ResizeObserver`, no
1552
+ `data-effective-orientation` marker — where before, a non-numeric attribute became `NaN` and armed
1553
+ observation for a threshold that could never be crossed. That deliberately includes `%`, `vw`/`vh`
1554
+ and `calc()`, which would mix a viewport-relative threshold into a measurement of the element's own
1555
+ allocation; drive `orientation` from your own `matchMedia()` controller for a viewport-relative
1556
+ breakpoint instead.
1557
+
1558
+ One visible consequence of dropping the `Number` attribute converter: reading `.orientationBreakpoint`
1559
+ back after setting the attribute now returns the authored string (`'900'`), not the number `900`.
1560
+ The resulting layout behavior is identical, and the property type is now `number | string`.
1561
+
1562
+ - 2be0a50: `<lr-split>` gains `orientationBreakpointBasis` (`"container"` by default, `"viewport"`
1563
+ opt-in), selecting whether `orientationBreakpoint` is compared against the component's own
1564
+ measured inline size or a `matchMedia('(max-width: …)')` query. Viewport basis lets sibling
1565
+ components in one row flip orientation together at a single shared breakpoint — impossible
1566
+ to express with a self-measured threshold when the row stacks via a CSS `@media` rule — and
1567
+ lets the browser resolve a `rem` breakpoint with real media-query semantics. Left unset,
1568
+ behavior is unchanged.
1569
+ - 96ea325: `<lr-stepper>` gains `orientationBreakpointBasis` (`"container"` by default, `"viewport"`
1570
+ opt-in), selecting whether `orientationBreakpoint` is compared against the stepper's own
1571
+ measured inline size or a `matchMedia('(max-width: …)')` query. Viewport basis is the only
1572
+ way a stepper with a fixed width in a row layout can react to that row stacking at a shared
1573
+ breakpoint. Left unset, behavior is unchanged.
1574
+ - b1ce3f6: `lr-sequence-strip`: add `showLegend` for a persistent category key.
1575
+
1576
+ The strip colors each cell by category, but the only way to read that mapping was to hover every
1577
+ cell one at a time — consumers were hand-rolling a swatch key underneath instead. `showLegend`
1578
+ (attribute `show-legend`, reflected, default `false`) now renders that key from the `categories`
1579
+ array the component already receives, as `legend` / `legend-item` / `legend-swatch` /
1580
+ `legend-label` CSS parts, with `--lr-sequence-strip-legend-swatch-size` to resize the chips.
1581
+
1582
+ The legend is deliberately static — it lists every `categories` entry whether or not any item uses
1583
+ it, and toggles nothing (`lr-graph-legend` remains the interactive, filtering legend). Because it
1584
+ only repeats the category names the strip already announces through `[part="base"]`'s `role="img"`
1585
+ summary, the whole legend is `aria-hidden`: visible on screen, announced exactly once. Left unset,
1586
+ rendering is unchanged.
1587
+
1588
+ - 3127d5e: Restructure the AI-agent-facing reference so a component lookup costs a few hundred tokens instead
1589
+ of the whole catalog, and close the gaps that made it unreliable.
1590
+
1591
+ **New published layout.** `llms/index.md` maps every tag to its import path and one-line purpose;
1592
+ `llms/components/<tag>.md` is a self-contained per-component reference addressed directly from the
1593
+ tag name; `llms/shared.md`, `llms/tokens.md`, `llms/peers.md` and `llms/migration.md` carry the
1594
+ library-wide contracts. `llms.txt` is now the entry index over all of it, and `llms-full.txt` keeps
1595
+ its role as the single-file concatenation. Everything is generated from per-family authored sources
1596
+ by `pnpm run llms` and diffed in CI, so the docs cannot drift from `custom-elements.json`.
1597
+
1598
+ **Corrected documentation that was wrong, not merely missing:**
1599
+
1600
+ - Import paths in the docs had not been updated for the family directory layout —
1601
+ `components/combobox/combobox.js` does not resolve; it is
1602
+ `components/forms/combobox/combobox.js`. CI now fails on any documented path that has no source
1603
+ module.
1604
+ - 26 components were documented twice with divergent content; the freshness check validated the
1605
+ weaker copy.
1606
+ - `lr-include` was documented with the wrong purpose, property semantics, event name and CSS parts.
1607
+ - Wrong or non-existent CSS parts on `lr-timeline`/`lr-timeline-item`, `lr-tour`, `lr-known-date`,
1608
+ `lr-random-content`, `lr-avatar-group`, `lr-breadcrumb`, `lr-swatch-picker`.
1609
+ - `lr-button` was missing the `quiet` appearance and the `2xs` size; `lr-attachment-trigger` was
1610
+ missing the `audio` capability; `lr-avatar` was documented as having no slots.
1611
+ - `lr-widget` event details are objects, not scalars; three overlay-color tokens do resolve to
1612
+ `var(--lr-color-overlay)`; `lr-histogram`'s `label` default is localized, not `'Frequency'`.
1613
+ - The root barrel skips 15 peer-gated tags, not 13 — `lr-knowledge-graph-explorer` and
1614
+ `lr-geojson-view` were undocumented omissions.
1615
+
1616
+ **Newly documented:** the `@aceshooting/lyra-ui/ai` provider-neutral data types, the `locale` and
1617
+ `strings` properties present on every element, the localization API surface
1618
+ (`setLyraLocale`/`getLyraLocale`/`resolveLyraString`/`LYRA_DEFAULT_STRINGS` and its 996 message
1619
+ keys), the full design-token catalog, framework integration (React/Vue/Angular/Svelte property and
1620
+ event binding), TypeScript usage (the 127 `Lyra*EventMap` types, the typed `addEventListener`,
1621
+ `HTMLElementTagNameMap`), SSR/declarative-shadow-DOM status, the component-to-peer-dependency table,
1622
+ editor tooling metadata, and `<lr-map>`'s OpenStreetMap demo-tile-server production hazard.
1623
+
1624
+ The freshness check now covers events, slots, CSS parts and themeable custom properties in addition
1625
+ to properties — it previously only checked properties, which is how 87 public names came to be
1626
+ documented nowhere.
1627
+
1628
+ ### Patch Changes
1629
+
1630
+ - 7bdefd2: `lr-time-input` now accepts `min`/`max` as attributes. It inherits both from `lr-input`, where they
1631
+ are declared `type: Number` for the `type="number"` contract, so a `min="09:00"` attribute parsed to
1632
+ `NaN` and reached the native `<input type="time">` as the literal string `"NaN"` — which the browser
1633
+ discards, silently dropping the bound. Only a direct property assignment worked, and it needed a
1634
+ TypeScript widening cast to do so.
1635
+
1636
+ `LyraTimeInput` now redeclares `min`/`max` with a converter that forwards the attribute verbatim, so
1637
+ `<lr-time-input min="09:00" max="17:00">` reaches the native input intact and its own constraint
1638
+ validation reports `rangeUnderflow`/`rangeOverflow` as it should. Seconds-precision bounds
1639
+ (`min="09:00:30"` alongside `step="1"`) work the same way, removing the attribute clears the bound,
1640
+ and both are typed `string | number | undefined` so an assignment no longer needs a cast.
1641
+
1642
+ `<lr-input type="number">` is unchanged: its `min`/`max` attributes still parse to numbers.
1643
+
1644
+ - 2724dec: Add an internal `resolveCssLength()` helper that resolves a CSS length (a bare/`px` number, `rem`,
1645
+ or `em`) to pixels, reading the document root font size at call time so a `rem`-authored threshold
1646
+ tracks browser zoom, a user font-size preference, or an app changing its base size. Units that only
1647
+ make sense against a different reference box (`%`, `vw`/`vh`, `ch`), absolute units, and
1648
+ `calc()`/`var()` expressions resolve to `undefined`, which callers treat as "unset".
1649
+
1650
+ No public API change yet — this is the shared groundwork for letting `lr-split` and `lr-stepper`
1651
+ accept `orientation-breakpoint` as a CSS length.
1652
+
1653
+ - 356f5fb: `lr-emoji-picker` now resolves its three geometry custom properties to real pixels before using
1654
+ them for the windowed layout. `--lr-emoji-picker-item-size`, `--lr-emoji-picker-gap`, and
1655
+ `--lr-emoji-picker-row-height` were read with `parseFloat(getComputedStyle(host).getPropertyValue(
1656
+ token))`, which hands back the property's computed _token stream_ rather than a length: the shipped
1657
+ `2.5rem` item size was used as `2.5px`, the `0.125rem` gap as `0.125px`, and the `calc()`-based
1658
+ default row height was unparseable and always fell back to a hardcoded `64`. The windowed grid
1659
+ therefore packed its column cap of 20 emoji into a row that could only paint five, and scrolled at a
1660
+ row pitch that did not match the painted rows.
1661
+
1662
+ Each token is now assigned to an off-flow probe box in the shadow root and read back as that box's
1663
+ used inline size, so the browser performs the unit math — `rem`, `em`, `ch`, `%`, `calc()`, any CSS
1664
+ length resolves correctly, and the item-size probe carries the same `--lr-icon-button-size` minimum
1665
+ the emoji buttons do, so the measured item size is the painted one. The result is cached and
1666
+ re-derived only when it can actually change: the probe boxes are themselves observed, so a token
1667
+ override applied after the first render, a theme swap, or a root/host font-size change updates the
1668
+ geometry without any per-frame measurement. Numeric fallbacks still cover the case where no box has
1669
+ been laid out yet.
1670
+
1671
+ Consumers no longer have to express these tokens in `px` for the windowed geometry to line up with
1672
+ what is painted.
1673
+
1674
+ - 4ddf1fb: Document `orientationBreakpointBasis` on `<lr-split>` and `<lr-stepper>`, and the four
1675
+ role-scoped bubble custom properties on `<lr-chat-message>`. Also corrects a claim that
1676
+ a `rem` inside a CSS `@media` query resolves against the document root's computed font
1677
+ size — it resolves against the browser's _initial_ font size, which is exactly why the
1678
+ `'viewport'` basis, and not `'container'`, is the one that matches a CSS `@media` rule.
1679
+ - 2a0cb74: Add an internal `OrientationBreakpointController` that owns orientation-breakpoint
1680
+ resolution, basis selection, and media-query lifecycle for the layout components. No
1681
+ consumer-visible change on its own.
1682
+ - 8057596: Fix `<lr-pdf-viewer>`'s `search()` throwing an uncaught `IndexSizeError` when a search term occurs
1683
+ more than once inside a single PDF.js text-layer node (e.g. a repeated substring within one text
1684
+ item's `<span>`). `paintSearchMatches()` computed every match's DOM range against a pristine,
1685
+ pre-painting snapshot of each text node, but wrapping the first match with `Range.surroundContents()`
1686
+ splits/shrinks that node out from under the second match's precomputed offset, so `setStart()`/
1687
+ `setEnd()` threw before the existing `surroundContents()` try/catch ever ran. Offsets for a node are
1688
+ now tracked against the node as it actually stands after each prior match is painted, so every
1689
+ repeated occurrence within one text-layer node is now correctly highlighted instead of crashing
1690
+ `search()`.
1691
+ - 415e61f: `<lr-code-editor>`: make `--lr-code-editor-tab-size` actually themeable.
1692
+
1693
+ The stylesheet read the token on the `textarea` part, but `render()` also wrote an inline
1694
+ `tab-size:${tabSize}` on that same element on every update, and an inline declaration always beats a
1695
+ rule — so the documented token was inert and a host-level override was silently ignored.
1696
+
1697
+ `render()` now writes the token itself, and only when `tabSize` was explicitly assigned. The
1698
+ resulting precedence, highest first: an explicitly set `tabSize` (property or `tab-size` attribute)
1699
+
1700
+ > a host-level `--lr-code-editor-tab-size` > the `:host` default of `2`. `tabSize` therefore remains
1701
+ > the primary knob and still wins wherever it is used; it just stops shadowing the token while it sits
1702
+ > at its default. The Tab key follows the same order, so the indent unit and the rendered tab stops
1703
+ > cannot disagree — except for a length-valued token (`40px`, `2ch`, …), which stays a purely visual
1704
+ > tab-stop metric and leaves the inserted-space count at `tabSize`.
1705
+
1706
+ ## 5.0.0
1707
+
1708
+ ### Major Changes
1709
+
1710
+ - 3abb16e: Reorganized `packages/lyra-ui/src/components/` into 11 named family subfolders (Conversation &
1711
+ Chat, Agent Tools & Observability, Retrieval & Knowledge Graphs, Forms & Inputs, Data Display,
1712
+ Charts, Layout & Navigation, Overlays & Feedback, Document & File Viewers, Media & Files, Utility)
1713
+ instead of a flat 212-directory list.
1714
+
1715
+ **Breaking:** any consumer importing an individual component's granular subpath (e.g.
1716
+ `@aceshooting/lyra-ui/components/combobox/combobox.js`) must add that component's family segment
1717
+ (`@aceshooting/lyra-ui/components/forms/combobox/combobox.js`). The root entry
1718
+ (`@aceshooting/lyra-ui`) and the `@aceshooting/lyra-ui/components/*` wildcard export are
1719
+ unaffected for consumers who only import the root barrel. See
1720
+ `packages/lyra-ui/scripts/component-families.json` for the full directory-to-family mapping.
1721
+
1722
+ ### Minor Changes
1723
+
1724
+ - 0ed6e71: Added a frame-coalesced `lr-viewport-change` event to `lr-graph`, firing at most once per
1725
+ animation frame for every source that can move a rendered node's screen position (pan/zoom, a
1726
+ `focusNode()`/`fit()` tween, or a simulation tick) so a consumer anchoring its own UI to a node's
1727
+ `getBoundingClientRect()` no longer needs to poll its own `requestAnimationFrame` loop.
1728
+ `--lr-graph-dimmed-opacity` now defaults to `0.35` (previously the inert `1`), so `dimmedNodeIds`/
1729
+ `dimmedLinkIds` are visible out of the box with no host styling required.
1730
+
1731
+ `lr-knowledge-graph-explorer` now computes and forwards `dimmedLinkIds` alongside
1732
+ `dimmedNodeIds`, switched its details-popover pan/zoom tracking from RAF polling to the new
1733
+ `lr-viewport-change` event, and added a `highlight: 'selection' | 'hover' | 'none' = 'selection'`
1734
+ property: `'hover'` also dims by the currently pointer-hovered node's neighborhood, `'none'` opts
1735
+ a host out of this component's own dimming entirely.
1736
+
1737
+ - bd501b7: Added `defaultSizes` to `lr-split` for an initialization-only fallback (a valid restored
1738
+ `storageKey` layout still wins, then `defaultSizes`, then equal distribution) that's never
1739
+ overwritten by a later reactive parent render. Added an opt-in `orientationBreakpoint`/
1740
+ `narrowOrientation` responsive-axis contract (mirrored below by `lr-stepper`): below the
1741
+ component's own measured inline size, `narrowOrientation` becomes the effective resize axis
1742
+ instead of the authored `orientation`, exposed via `effectiveOrientation`, a
1743
+ `data-effective-orientation` attribute, and `lr-split-orientation-change`. Extended
1744
+ `panelConstraints` with `minPercent`/`maxPercent`, combining with `minPx`/`maxPx` on the same side
1745
+ via the stricter bound.
1746
+ - 5319ed6: Added an opt-in `orientationBreakpoint`/`narrowOrientation` responsive-axis contract to
1747
+ `lr-stepper`, mirroring `lr-split`'s identically-named properties: below the stepper's own
1748
+ measured inline size, `narrowOrientation` becomes the effective layout/navigation axis instead of
1749
+ the authored `orientation`, exposed via `effectiveOrientation`, a `data-effective-orientation`
1750
+ attribute, and `lr-stepper-orientation-change`. Unset (the default), behavior is unchanged.
1751
+
1752
+ ## 4.2.0
1753
+
1754
+ ### Minor Changes
1755
+
1756
+ - f3ae130: Adds an `@aceshooting/lyra-ui/ai` entrypoint re-exporting the provider-neutral AI/agent data
1757
+ contracts from `src/ai/types.ts` (also re-exported as types from the root `lyra.ts` barrel), so
1758
+ consumers importing these shared types don't have to reach into `./ai/types` directly.
1759
+ - 46eb4d2: `<lr-diff-view>` gains a `contextLines` property: collapses a run of unchanged lines longer than
1760
+ `2 * contextLines` behind a single localized fold marker reporting how many lines it hides, keeping
1761
+ only `contextLines` lines of context immediately before/after each change (leading/trailing runs
1762
+ keep only their nearest `contextLines` lines) — the same context-window convention `git diff -U<n>`
1763
+ uses. Default `undefined` renders every line unconditionally, exactly as before this property
1764
+ existed. Works identically in both `unified` and `split` layout.
1765
+
1766
+ ### Patch Changes
1767
+
1768
+ - fffd101: `<lr-chart>` no longer tracks its resolved Chart.js draw-time chart-area geometry as a reactive
1769
+ `@state()` field — recording it during Chart.js's own draw pass could trigger a second synchronous
1770
+ Lit update mid-draw. It's now a plain private field with a microtask-coalesced `requestUpdate()`,
1771
+ so repeated geometry updates within the same draw pass collapse into a single re-render.
1772
+ - 273d5da: Fixed `lr-csv-viewer` and `lr-spreadsheet-viewer`: data rows rendered as unstyled stacked text
1773
+ instead of a proper grid, because their styling lived in a `[part='data-row']`/`[part='cell']`
1774
+ CSS selector scoped to the wrong shadow root (data rows render inside the nested
1775
+ `<lr-virtual-list>`'s own shadow tree via its `renderItem` callback, not the viewer's own). Only
1776
+ the header row, rendered directly by the viewer, was ever actually styled. Fixed with
1777
+ `lr-virtual-list::part(data-row)`/`::part(cell)` rules that correctly reach across that shadow
1778
+ boundary.
1779
+ - a15cb97: `<lr-notebook-viewer>` now interprets ANSI SGR color/style escape codes embedded in stream and error
1780
+ outputs (common in colorized Python tracebacks and console output), rendering them as styled spans
1781
+ via the same shared `internal/ansi.ts` parser `<lr-terminal>` uses, instead of showing the raw
1782
+ escape sequences as literal text.
1783
+ - ef988d8: `<lr-trace-tree>` now syncs `focusedId` from `activeSpanId` in `willUpdate()` instead of `updated()`,
1784
+ so the roving-tabindex target updates before render rather than one tick after it.
1785
+
1786
+ ## 4.1.0
1787
+
1788
+ ### Minor Changes
1789
+
1790
+ - b28758d: New `<lr-agent-run>` component: the top-level shell for one `AgentRun` (from `@aceshooting/lyra-ui/ai/types`) -- lifecycle-status badge, elapsed time, current step, model/cost summary, and built-in Cancel/Retry controls in a header, plus four named composition slots (`tasks`/`tools`/`reasoning`/`output`) for the run's actual content. Composes `lr-generation-status` for the live elapsed-time ticker while a run is in progress, `lr-usage-badge` for the cost summary, `lr-task-list` for the `tasks` slot's default content (mapped from `run.steps`), and `lr-badge`/`lr-empty` for the status pill and empty state -- no new step-rendering logic. Emits `lr-cancel`/`lr-retry` (`CancelEventDetail`/`RetryEventDetail`) rather than cancelling or retrying anything itself.
1791
+ - f33364d: New `<lr-agent-trace>` component: a provider-neutral agent/LLM trace view combining a span-kind
1792
+ filter row, a handoff quick-jump list, and the full trace hierarchy, all driven by one shared
1793
+ `LyraSpan[]` array. All trace rendering -- hierarchy, expand/collapse, keyboard navigation,
1794
+ duration bars, empty state -- is entirely `<lr-trace-tree>`'s own; this component only ever hands
1795
+ it a (possibly filtered) `spans` array plus pass-through properties, never building its own row
1796
+ markup. The filter row composes `<lr-graph-legend>` (the same type/visibility-toggle legend
1797
+ pattern already established for `<lr-graph>` node types, reused here for `LyraSpan.kind`
1798
+ visibility) and the handoff list composes `<lr-handoff-divider>` for each visible `'agent'`-kind
1799
+ span. Selection (`activeSpanId`) is controlled end-to-end for deep-linking: both a tree-row click
1800
+ and a handoff quick-jump activation update it and fire the identical `lr-span-select` `{ id }`
1801
+ shape.
1802
+ - eb3e833: New provider-neutral shared type surface at `@aceshooting/lyra-ui/ai/types`: `AgentStatus`,
1803
+ `AgentRun`, `AgentStep`, `ChatMessage`, `ToolInvocation`, `RetrievalQuery`, `RetrievalChunk`,
1804
+ `Citation`, `DocumentRef`, `GroundingAssessment`, and shared run-lifecycle/retrieval-progress/
1805
+ citation-select/tool-approval/cancel/retry/export event-detail types. A foundational types-only
1806
+ module (no runtime code, no new custom elements) for the upcoming retrieval, agent-run,
1807
+ knowledge-graph, dashboard, and evaluation component families -- structurally compatible with the
1808
+ prop shapes `lr-chat-message`, `lr-citation-badge`, `lr-tool-call-chip`, `lr-tool-result-view`,
1809
+ `lr-source-card`, `lr-attachment-chip`, and `lr-document-preview` already expose, so these types
1810
+ assign directly onto those components' own properties with no adapters. `ToolInvocation.status`
1811
+ reuses `lr-tool-call-chip`'s own `ToolCallStatus` union rather than the broader `AgentStatus`
1812
+ shape, since a single tool call's terminal state is exactly what that existing vocabulary already
1813
+ covers.
1814
+ - c2d8f05: `lyra-chat-message` gains a `failure` slot, only ever rendered while `status="failed"`. Left empty
1815
+ (the default), today's built-in failed-state UI is unchanged: the `[part="status-text"]` message,
1816
+ the `[part="retry-button"]`, and the `chatFailedAnnounce` live-region announcement all keep working
1817
+ exactly as before. Once the slot has assigned content, that built-in status text and retry button
1818
+ are suppressed -- the host is now fully responsible for its own failure presentation (e.g. a
1819
+ prominent, translated `role="alert"` banner with its own retry control), and the built-in
1820
+ live-region announcement is suppressed too, so a host's own alert content doesn't get double
1821
+ announced alongside a generic built-in one. The `failure` slot itself contributes no box
1822
+ (`display: contents`), so the host's content lays out exactly as authored without needing any
1823
+ `::part(failure)` override. Content assigned to it should carry `role="alert"` itself when it
1824
+ represents an actionable send failure -- this component has no way to add that role on the host's
1825
+ behalf. Programmatic focus is rescued to `[part="bubble"]` (mirroring the existing built-in retry
1826
+ button's own focus rescue) whenever the failure slot's content held focus and `status` changes away
1827
+ from `"failed"`, so a host's own retry control clearing the failed state never silently drops focus
1828
+ to `document.body`. The existing `lr-retry` event contract is untouched; a host's own retry control
1829
+ can dispatch it manually to stay consistent with listeners elsewhere in a conversation surface, but
1830
+ nothing requires it to.
1831
+ - 3f35f20: New `<lr-context-inspector>` component: an inspection view of the exact context assembled for a
1832
+ model call. Renders per-segment token estimates through an embedded `<lr-context-meter>`, source
1833
+ attribution through `<lr-citation-badge>`, and copy/export affordances through
1834
+ `<lr-copy-button>`/`<lr-export-button>` -- composing all four rather than re-implementing any of
1835
+ their rendering. Adds two small, purpose-built presentational features no existing primitive
1836
+ covers: a truncation-boundary marker for a segment cut short of its original content
1837
+ (`ContextInspectorSegment.truncated`/`omittedTokens`), and titled `<mark part="redaction">`
1838
+ highlighting for character ranges a segment's `text` already carries a redaction placeholder in
1839
+ (`ContextInspectorSegment.redactions`) -- this component never receives or renders unredacted
1840
+ content, only marks where a host-side redaction already happened. Pure projection: never fetches,
1841
+ estimates tokens, or performs redaction itself.
1842
+ - 607b832: Add `<lr-dashboard-grid>`, a responsive, keyboard-accessible widget-layout shell: a controlled
1843
+ `layout: DashboardCell[]` (grid-unit `x`/`y`/`w`/`h` + a widget descriptor) drives a CSS Grid,
1844
+ composing `<lr-widget>`/`<lr-widget-renderer>` for each cell's default content unless a
1845
+ light-DOM `[cell-id]` child is authored instead. Pointer drag/resize and Ctrl/Cmd+Arrow (move) /
1846
+ Ctrl/Cmd+Shift+Arrow (resize) keyboard equivalents both route through the same `collision`-policy
1847
+ resolution (`'reject'` the default, `'push'`, or `'overlap'`), emitting `lr-cell-move`/
1848
+ `lr-cell-resize`/`lr-collision`/`lr-layout-change` -- the component never mutates `layout` itself
1849
+ nor touches `localStorage`/network; the host applies (or ignores) every event and owns persistence
1850
+ entirely, matching `lr-flow-canvas`/`lr-table`'s own controlled-component convention. Below a
1851
+ ~40rem container allocation (`@container`, not the viewport), cells stack into a single flowing
1852
+ column instead of shrinking columns unreadably.
1853
+ - 21de4b4: `lyra-date-input` gains a `size: '2xs' | 'xs' | 's' | 'm' | 'l' | 'xl' = 'm'` property, matching
1854
+ `lyra-input`/`lyra-select`/`lyra-combobox`'s shared control-size scale. `size="s"` now renders the
1855
+ field at the same height/density as `lyra-input size="s"`, so a date field can sit flush beside a
1856
+ compact input or select in the same form row or toolbar. The calendar-toggle and clear buttons
1857
+ keep their existing minimum touch target at every tier instead of shrinking below it. The default
1858
+ `m` tier is pixel-identical to this component's previous, only rendering.
1859
+ - d44e979: New `<lr-document-compare>` component: side-by-side or inline comparison of two document versions,
1860
+ composed entirely from existing primitives -- `<lr-diff-view>` (the real two-string line diff,
1861
+ `view="diff"`, the default) and `<lr-document-preview>` (each version's own actual rendered
1862
+ content, `view="side-by-side"`). The side-by-side panes are independently scrollable, so this
1863
+ component adds two minimal, purpose-built coordination mechanisms scoped narrowly to that: a
1864
+ proportional scroll-position sync (`syncScroll`, default `true`, mirroring `<lr-compare-panel>`'s
1865
+ own proven algorithm) and highlight-anchor sync (activating a region highlight shared by `id`
1866
+ across both versions' `highlights` scrolls the other pane to its own matching highlight). A shared
1867
+ `anchor` property (matching `<lr-document-viewer>`'s own) drives both panes to the same target at
1868
+ once. New `DocumentCompareVersion` type extends the shared `DocumentRef` (from `@aceshooting/lyra-ui/ai/types`)
1869
+ with `text` (diffed directly, no fetch) and per-version `highlights`.
1870
+ - 7dfbed7: New `<lr-document-library>`: a searchable, filterable document inventory with versions, tags,
1871
+ owners, freshness, and bulk selection. Consumes the shared `DocumentRef` type from
1872
+ `@aceshooting/lyra-ui/ai/types` (`id`/`name`/`mimeType`/`uri`/`version`) as its base row shape,
1873
+ extended locally (`LibraryDocument`) with `tags`/`owner`/`updatedAt`/`freshness` -- the fields an
1874
+ inventory view needs that a provider-neutral document reference deliberately doesn't carry.
1875
+ Composes `<lr-table>` for the grid itself (`<lr-data-grid>` was evaluated and ruled out: it only
1876
+ supports a single `selectedKey` and stringifies every cell value, so it cannot host the checkbox/
1877
+ chip/icon content bulk selection, tags, and per-row type icons need; `<lr-table>` supports
1878
+ arbitrary cell content and `priority`-driven responsive column hiding), `<lr-chip>`/`<lr-chip-
1879
+ group>` for tags and the freshness badge, `<lr-file-icon>` for per-document type icons, `<lr-
1880
+ input type="search">` for free-text search, and `<lr-combobox multiple>` for a tag facet filter
1881
+ (AND semantics -- a document must carry every selected tag). Bulk selection renders a `<lr-
1882
+ checkbox>` per row plus a header select-all checkbox (indeterminate when some but not all visible
1883
+ rows are selected) independently of `<lr-table>`'s own built-in `selectionMode`, since that mode's
1884
+ click-anywhere-on-the-row toggle would conflict with the row's own name button opening the
1885
+ document. A controlled data view like this package's other orchestration-level list surfaces: no
1886
+ upload/sync/mutation logic of its own, only `lr-filter-change` / `lr-sort` / `lr-selection-change`
1887
+ / `lr-open` request-and-notification events. `selectedIds` referencing a document no longer present
1888
+ in `documents` is silently pruned (no event fires for that pruning, mirroring `<lr-chip-group>`'s
1889
+ identical silent-resync convention) rather than left dangling.
1890
+ - a012673: New `<lr-drilldown-panel>` component: controlled navigation from a chart/table datum to its related
1891
+ evidence, documents, entities, or agent runs. A navigation shell only -- an `lr-breadcrumb` trail
1892
+ over a host-owned `path`, plus, for whichever categories the current node actually has content for,
1893
+ the one existing primitive that already renders that content type (`lr-source-card` for evidence,
1894
+ `lr-document-preview` for documents, `lr-entity-card` for entities), wrapped in an `lr-tabs` strip
1895
+ only when more than one category has content. Agent-run content has no dedicated rendering primitive
1896
+ yet in this library, so it composes via a `runs` slot instead of inventing bespoke rendering.
1897
+ Activating a non-current breadcrumb step fires `lr-drilldown-navigate` (`detail: { id, index }`) --
1898
+ the component never mutates `path` itself.
1899
+ - a0bb717: New `<lr-entity-dossier>` component: a full knowledge-graph entity detail surface combining
1900
+ properties, relationships, supporting chunks, confidence, and provenance into one composed
1901
+ layout. A persistent header renders `<lr-entity-card>` (the entity's summary/properties) next to
1902
+ an optional confidence `<lr-stat>`, above an `<lr-tabs>` strip for Relationships
1903
+ (`<lr-neighbor-list>`), Supporting chunks (`<lr-chunk-inspector>`), and Provenance
1904
+ (`<lr-provenance-panel>`). Pure layout -- it never fetches, ranks, or mutates graph/document state,
1905
+ and never re-renders what any of those five composed components already render themselves; every
1906
+ one of their own events bubbles through unmodified rather than being re-declared as this
1907
+ component's own. Tab labels reuse the exact `localize()` keys the composed child underneath
1908
+ already uses for its own accessible name (`neighborListLabel`, `chunkInspectorLabel`,
1909
+ `provenancePanelLabel`), so no new localization keys were needed and a translated locale only has
1910
+ to cover each string once.
1911
+ - 5f2927f: New `<lr-eval-dataset>` component: dataset management for an evaluation suite -- a filterable,
1912
+ taggable list of `EvalExample` rows (`id`, `input`, `expectedOutput?`, `tags?`, `metadata?`) with
1913
+ add/remove/import/export affordances. Fully controlled, matching this library's established
1914
+ convention for the rest of the agentic-AI orchestration layer: `examples` is the host's own data,
1915
+ and the component never mutates it or performs any I/O itself -- every action fires an
1916
+ `lr-example-select` / `lr-example-add-request` / `lr-example-remove-request` /
1917
+ `lr-import-request` / `lr-export-request` event and the host decides how to act on it.
1918
+
1919
+ Composes `<lr-data-grid>` for the row list, `<lr-chip>`/`<lr-chip-group>` as a toggleable,
1920
+ OR-matched tag-browse filter, `<lr-file-input>` for the import affordance, and
1921
+ `<lr-export-button>` for the export affordance (its own built-in client-side download is
1922
+ suppressed so every configured format routes through `lr-export-request` uniformly). Ships with a
1923
+ searchable free-text filter, RTL and 320px-allocation coverage, and localized strings with
1924
+ `.strings` override support.
1925
+
1926
+ - f5870ef: New `<lr-eval-result>`: rubric scoring, human review, and comparison across a single evaluation
1927
+ example's runs (one per model or prompt version), LangSmith/Arize-eval-result style. Composes
1928
+ three existing primitives directly rather than re-deriving their behavior: `<lr-data-grid>` renders
1929
+ the `runs` comparison table (`columns` is a plain pass-through to its own `DataGridColumn[]`
1930
+ shape); `<lr-rubric-form>` is the human-review scoring surface for whichever run is selected,
1931
+ reading/writing that run's own `review` value and re-emitting its
1932
+ `lr-input`/`lr-validity-change`/`lr-submit`/`lr-skip` events as
1933
+ `lr-review-input`/`lr-review-validity-change`/`lr-review-submit`/`lr-review-skip` with the run id
1934
+ attached; `<lr-diff-view>` compares the selected run's output against `baselineRunId`'s output --
1935
+ `layout="split"` once they resolve to two distinct runs, `layout="unified"` (an all-equal diff,
1936
+ i.e. a plain read of the one run's output) once they resolve to the same run or no baseline
1937
+ resolves at all. `selectedRunId`/`baselineRunId` are both fully controlled (never mutated
1938
+ internally) and fall back to `runs[0]?.id` purely for rendering when unset, so the component
1939
+ renders something useful with zero configuration beyond `runs`; a `selectedRunId`/`baselineRunId`
1940
+ that matches no entry in `runs` degrades gracefully (the comparison grid still renders, the
1941
+ review/diff sections simply don't).
1942
+ - 3827a19: New `<lr-evaluation-run>` component: an evaluation batch's live progress. An overall
1943
+ `<lr-progress-bar>` counts terminal (done/error/cancelled) examples against the batch's `total`
1944
+ (or `examples.length` when unset), with running/failed count badges alongside it. Each example
1945
+ renders as its own `<lr-details>` disclosure showing input/output via `<lr-markdown>` or
1946
+ `<lr-code-block>` (per `inputFormat`/`outputFormat`), a composed `<lr-grounding-summary>` when the
1947
+ example carries a `GroundingAssessment` (plus optional evidence `citations`), and a composed
1948
+ `<lr-tool-timeline>` when it carries `toolTrace` entries -- this component defines no grounding-
1949
+ scoring or tool-call rendering of its own. `status` reuses the shared `AgentStatus` contract from
1950
+ `@aceshooting/lyra-ui/ai/types`, the same run-lifecycle vocabulary an agent step already uses.
1951
+ Nested `<lr-grounding-summary>`/`<lr-tool-timeline>` selection and approval events are intercepted
1952
+ and re-emitted as this component's own `lr-example-citation-select`/
1953
+ `lr-example-tool-approval-decide`, correlated with the originating example's `id` so a host never
1954
+ needs to walk the DOM to find out which example a nested interaction came from. Per-example
1955
+ disclosure toggling fires `lr-example-toggle`. A live region announces per-example status
1956
+ transitions (started/completed/failed/cancelled/needs input/needs approval), gated so a freshly-
1957
+ mounted run never announces its initial statuses.
1958
+ - 0669f01: New `<lr-filter-bar>` component: a row of composable dashboard filters, each declared by the host
1959
+ (`filters: FilterBarFilterDefinition[]`) rather than invented by this component -- every filter
1960
+ renders an existing Lyra input (`<lr-select>`/`<lr-combobox>` for closed choice sets,
1961
+ `<lr-date-input>` in single or `mode="range"` for dates), plus a `<lr-chip-group>` of removable
1962
+ `<lr-chip>`s summarizing the active filters, an `<lr-button>` reset action, and (while `loading`)
1963
+ an `<lr-spinner>` status indicator. Controlled, like every other Lyra data component: `value` is a
1964
+ plain, JSON-serializable `FilterBarValue` object the host reads/writes directly -- this component
1965
+ never touches `location`/`history`/storage itself, so turning `value` into (and back out of) a URL
1966
+ querystring or app-state store is entirely the host's own concern. `required`-flagged filters get
1967
+ live `invalidFilterIds`/`checkValidity()`/`reportValidity()` and a `lr-validity-change` event, with
1968
+ each filter's own inline error rendered by its already-chromed composed control rather than a
1969
+ second, duplicate label/hint/error frame. `reset()` restores every filter to its own
1970
+ `defaultValue` (or unset) and emits both the standard `lr-input` and a dedicated `lr-reset`,
1971
+ mirroring `<lr-combobox>`'s own `clear()`/`lr-clear` pattern.
1972
+ - c5a4786: New `<lr-graph-query-builder>` component: an editor for a single typed relationship/path filter
1973
+ (`GraphQuery`) over a knowledge graph -- start/end entity anchors, relationship-type and
1974
+ node-type "add" pickers (`<lr-select>`) with a removable active-filter chip display
1975
+ (`<lr-chip>`/`<lr-chip-group>`), a traversal direction, a min/max hop range, validation
1976
+ (`value`/`checkValidity()`/`reportValidity()`/`lr-validity-change`, form-associated via
1977
+ `ElementInternals` the same way `<lr-rubric-form>`/`<lr-tool-param-form>` are), and a
1978
+ host-persisted saved-query list (`savedQueries` + `lr-query-save`/`lr-query-load`/
1979
+ `lr-query-delete`). `GraphQuery` is a serializable, provider-neutral query model suitable for
1980
+ handing straight to a GraphRAG retrieval/traversal backend via `value` or the `lr-query-run`
1981
+ event's payload.
1982
+ - a5723c3: New `<lr-grounding-summary>` component: the claim-level scorecard for one generated answer,
1983
+ consuming `GroundingAssessment` from `@aceshooting/lyra-ui`'s `src/ai/types.ts` directly as its
1984
+ `assessment` property. Composes `lr-stat` for the supported/unsupported claim counts, citation
1985
+ coverage, and optional confidence numeric displays (tone-mapped via a `thresholds` property), and
1986
+ `lr-citation-badge` for an optional `citations` list linking each evidence entry back to its exact
1987
+ `span`. Activating a citation badge emits `lr-citation-select` (detail: `{ citation }`, the
1988
+ `CitationSelectEventDetail` shape from `src/ai/types.ts`) carrying the full citation record, in
1989
+ addition to the badge's own `lr-citation-activate` still bubbling through unmodified. Warnings
1990
+ render verbatim as caller-supplied data; every other label is localized via `this.localize()`.
1991
+ - 497c8d3: New `<lr-ingestion-queue>` component: a controlled list of documents moving through an ingestion
1992
+ pipeline (`queued` → `uploading` → `extracting` → `chunking` → `embedding` → `indexing`, plus the
1993
+ terminal `done`/`failed`/`cancelled` stages), each row composing `lr-badge` for its stage label,
1994
+ `lr-progress-bar` for in-flight progress, and chunk-count/embedding-status/attempt-count text.
1995
+ `lr-empty` renders the zero-items state. Presentation only -- this component runs no ingestion
1996
+ itself and never mutates `items`; retrying a `failed` row or cancelling any non-terminal row fires
1997
+ a controlled `lr-retry`/`lr-cancel` request event (`detail` extends the shared `RetryEventDetail`/
1998
+ `CancelEventDetail` from `src/ai/types.ts` with the `itemId` identifying which row) and waits for
1999
+ the host to supply an updated `items` array, the same request/response convention
2000
+ `<lr-thread-list>`'s row-action events already establish. At or above `virtualizeThreshold` items
2001
+ the list renders through an internal `<lr-virtual-list>` instead of a plain keyed list, matching
2002
+ `<lr-thread-list>`'s data mode and `<lr-activity-feed>`'s own `virtualizeThreshold` precedent.
2003
+ - 593e879: New `<lr-knowledge-base>` component: a knowledge-base source list showing sync status, indexing
2004
+ health, and permissions per source, plus an aggregate summary row. A controlled data view -- it
2005
+ never syncs or indexes anything itself, only presents `sources: KnowledgeSource[]` and emits
2006
+ request-only `lr-kb-create`/`lr-kb-sync`/`lr-kb-pause`/`lr-kb-delete` events for the host to act on
2007
+ and reflect back into a new `sources` value, mirroring `lr-thread-list`'s `lr-thread-pin`/
2008
+ `-archive`/`-delete` convention. Composes `lr-table` for the source list (its own interactive-cell
2009
+ click guarding keeps the per-row `lr-menu` from misfiring row activation), `lr-badge` for the
2010
+ sync-status/indexing-health/permission indicators, `lr-stat` for the aggregate summary, and
2011
+ `lr-menu` for the per-row Sync now/Pause sync/Delete source actions.
2012
+ - f04b670: Add `<lr-knowledge-graph-explorer>`, an orchestration-level knowledge-graph surface composing the
2013
+ existing `lr-graph` canvas with entity search, type filters (via `lr-graph-legend`), neighborhood
2014
+ expansion, pinned nodes, path finding between pins (via `lr-path-strip`), node selection, and a
2015
+ node-details popover (via `lr-popover.showAt()` and `lr-entity-card`/`lr-neighbor-list`). Composes
2016
+ existing primitives rather than re-implementing graph rendering. New events `lr-path-request` and
2017
+ `lr-pin-change`; every composed primitive's own event (`lr-node-click`, `lr-node-expand`,
2018
+ `lr-selection-change`, `lr-community-click`, `lr-relation-activate`, etc.) bubbles straight through
2019
+ unmodified.
2020
+ - 593e879: New `<lr-memory-panel>` component: an agent's working memory surface -- short-term context and
2021
+ long-term memories, each item's confidence and optional grounding provenance, and add/remove/forget
2022
+ actions gated behind an explicit confirmation step. Composes `<lr-provenance-panel>` for a per-item
2023
+ provenance breakdown (behind a disclosure toggle, only rendered when an item defines one) and
2024
+ `<lr-confirm-bar>` for every add/remove/forget confirmation, reusing this repo's existing inline
2025
+ confirmation pattern rather than inventing a new one. A memory item's confidence reuses
2026
+ `<lr-citation-badge>`'s own confidence vocabulary, tiered against `thresholds` the same way
2027
+ `<lr-chunk-inspector>` tiers a chunk's relevance score. `shortTerm`/`longTerm` are controlled and
2028
+ never mutated by the component -- approving a pending action only fires the matching `lr-add` /
2029
+ `lr-remove` / `lr-forget` event; the host applies the resulting state change.
2030
+ - ac4857d: Add `showAt(rect, options?)` to `<lr-popover>` and `<lr-tooltip>`, a virtual-anchor API that opens
2031
+ the overlay positioned against an arbitrary rectangle (`{ x, y, width?, height? }`, defaulting to a
2032
+ zero-size point) instead of the slotted `trigger`. This lets a canvas/SVG surface -- a `<lr-graph>`
2033
+ node, a chart datum, a text-selection range -- get flip/shift/RTL-aware positioning, Escape,
2034
+ light-dismiss, and (optional, via `options.returnFocusTo`) focus-return for free, without a
2035
+ consumer hand-rolling absolute positioning and dismissal logic around it. Both components remain
2036
+ fully backward compatible: a component that never calls `showAt()` behaves byte-identical to
2037
+ before. `place()` (`src/internal/positioner.ts`) is widened from `HTMLElement` to `Element |
2038
+ VirtualAnchor` to support this, with no behavior change for existing real-element anchors.
2039
+ - 823f19b: New `<lr-policy-summary>` component: a read-only list of guardrail, permission, privacy, and
2040
+ tool-policy decisions, each carrying an `allow` / `deny` / `needs-review` state and an
2041
+ always-visible, accessible explanation of why that decision was made -- never conveyed by color
2042
+ alone. Composes `<lr-badge>` for the compact per-decision state indicator and `<lr-callout
2043
+ inline>` for the explanation text, whose own `role="alert"`/`role="status"` semantics already
2044
+ carry the right urgency per state, plus `<lr-details>` for a decision's optional richer `detail`
2045
+ (matched rule text, policy id, cited evidence) behind progressive disclosure. `decisions` is
2046
+ controlled and never mutated by the component -- this is a summary surface, not an approval gate
2047
+ (see `<lr-tool-approval-dialog>`/`<lr-confirm-bar>` for that).
2048
+ - c6dd883: New `<lr-query-builder>` component: a composable structured-query builder for tabular/dashboard
2049
+ data queries -- a flat list of field/operator/value condition rows combined with one AND/OR
2050
+ combinator. Distinct from `<lr-graph-query-builder>`, which builds typed relationship/path
2051
+ queries over a knowledge graph -- a genuinely different data model that never shares a file or a
2052
+ value type with this one.
2053
+
2054
+ Fully controlled: a host supplies `fields` (available columns, each carrying a
2055
+ `QueryBuilderFieldType` of `string` / `number` / `boolean` / `date` / `enum` that determines its
2056
+ offered operators and value control) and a plain, serializable `value: { combinator, conditions }`
2057
+ object, the same controlled-plain-object-`value` convention as `<lr-rubric-form>`. Each row
2058
+ composes `<lr-select>` for the field and operator pickers and a value control chosen from the
2059
+ selected field's type: `<lr-input type="text">`, `<lr-input type="number">`, `<lr-select>` with
2060
+ True/False options, `<lr-date-input>`, or `<lr-select>`/a multi-select `<lr-combobox>` for `enum`
2061
+ fields (`eq`/`neq` vs. `in`/`notIn`). A unary operator (`isEmpty`/`isNotEmpty`) renders no value
2062
+ control. `<lr-icon-button icon="trash">` removes a row and `<lr-button>` appends one, both
2063
+ surfaced through public `addCondition()`/`removeCondition(id)` methods and `lr-add-condition`/
2064
+ `lr-remove-condition`/`lr-input` events -- the component never mutates `fields`/`value` in place
2065
+ or touches storage/network itself.
2066
+
2067
+ - b443be6: New `<lr-retrieval-results>` component: the orchestration-level ranked-chunk-list surface for
2068
+ retrieval/grounding workflows, consuming `RetrievalChunk[]` from `@aceshooting/lyra-ui/ai/types`.
2069
+ Composes an internal `<lr-chunk-inspector>` per row (reusing its score bar, tier coloring, title/
2070
+ page rendering, expandable text, and `compact` mode verbatim -- no hand-rolled chunk-card markup)
2071
+ and an internal `<lr-virtual-list>` for windowing once the result count is large or `grouping` is
2072
+ active. Adds deduplication by `id` (keeping the higher-scoring duplicate), optional grouping by
2073
+ `source.id` (bucketed, best-scoring group first, same convention `<lr-thread-list>`'s date grouping
2074
+ already uses), multi-selection via a per-row `<lr-checkbox>` (`selectedIds` controlled, `lr-select`
2075
+ emits the updated ids and matching chunks), pagination/infinite loading (`has-more`/`loading`
2076
+ forwarded to the internal `<lr-virtual-list>` while virtualized, or a `[part="load-more"]` button
2077
+ otherwise -- both paths emit `lr-load-more`), and a `compact`/`expanded` `presentation` switch.
2078
+ `metadata` (arbitrary `Record<string, unknown>`, not rendered by any existing primitive) shows as a
2079
+ plain key/value list in `expanded` presentation. A row's `lr-chunk-open` is forwarded verbatim for
2080
+ routing into `<lr-document-viewer>`.
2081
+ - 5f2927f: New `<lr-retrieval-search>` component: the query bar for a retrieval/RAG surface, composing
2082
+ `lr-input` (query text), `lr-segmented` (vector/keyword/hybrid mode), `lr-chip`/`lr-chip-group`
2083
+ (removable active-filter/scope chips), `lr-spinner` (loading), and `lr-empty` (empty results).
2084
+ Fully controlled and network-free -- `query`/`mode`/`filters`/`scope` are host-owned properties,
2085
+ and the component only emits `lr-search` (detail: a `RetrievalQuery` from `@aceshooting/lyra-ui`'s
2086
+ `src/ai/types.ts`) on Enter or the submit button; the host performs the actual retrieval and
2087
+ toggles `loading` around it. Because this component has no way to know when a request resolves,
2088
+ submitting again while already `loading` is treated as superseding the in-flight request:
2089
+ `lr-cancel` fires immediately before the new `lr-search`, and the submit button itself doubles as
2090
+ an explicit Cancel affordance while `loading`. Filter/scope chip removal updates this component's
2091
+ own copy first, then emits `lr-filters-change` with the complete next `{ filters, scope }` state,
2092
+ mirroring `lr-source-picker`'s existing round-trip convention.
2093
+ - 3d6479f: New `<lr-retrieval-trace>` component: a retrieval pipeline's stage timeline (query rewriting,
2094
+ embedding, retrieval, reranking, filtering), rendered through `<lr-span-waterfall>`'s existing
2095
+ time-scaled bar rendering rather than a new timeline widget -- each `RetrievalStage` projects to
2096
+ one `LyraSpan`, with `kind` mapped onto whichever existing `LyraSpan['kind']` fits best (`embed`
2097
+ -> `'embedding'`, `retrieve` -> `'retriever'`, `query-rewrite` -> `'llm'`, `rerank`/`filter` ->
2098
+ `'tool'`). Below the timeline, a disclosure list exposes each stage's expandable evidence panel:
2099
+ free-form text, retrieved/reranked/filtered chunks via a compact `<lr-chunk-inspector>` (`chunks`
2100
+ accepts `RetrievalChunk` from `@aceshooting/lyra-ui/ai/types` directly), and/or arbitrary stage
2101
+ metadata as a key/value list. Controlled `stages`/`activeStageId` properties; emits `lr-stage-select`
2102
+ and `lr-stage-toggle`. Never fetches, ranks, or computes retrieval results itself.
2103
+ - 5597050: `lyra-thread-list` gains a `renderActions?: (thread: ChatThread) => TemplateResult` data-mode
2104
+ property, an escape hatch for a fully custom per-row action surface (e.g. a `<lr-menu>` with
2105
+ Rename/Delete, a rename dialog, delete-confirmation state) that the existing `rowActions`'s closed
2106
+ `pin | archive | delete` set can't express. Its content is appended after any built-in `rowActions`
2107
+ buttons in the same row's `actions` slot -- additive, not a replacement, the same composition
2108
+ direction `wrapRow` already takes elsewhere on the row. Set `rowActions` to `[]` (its default) to
2109
+ use only the callback's content. `renderActions` is re-invoked per row on every render with the
2110
+ current thread (never memoized/stale) and its content sits as a DOM sibling of the row's own
2111
+ selectable region, so activating a custom action never also fires `lr-select` -- the same
2112
+ structural mechanism the built-in row-action buttons already rely on. Leaving `renderActions`
2113
+ unset leaves `rowActions`' rendered output byte-for-byte unchanged, and `wrapRow` continues to
2114
+ compose independently around the result either way.
2115
+ - c67e88b: New `<lr-tool-timeline>` component: a chronological list of an agent run's tool calls, rendering
2116
+ each entry through `<lr-tool-call-chip>` (name/status/duration) and `<lr-tool-result-view>`
2117
+ (args/result) -- both already built for exactly this -- plus one shared `<lr-tool-approval-dialog>`
2118
+ for entries gated behind a human decision. Its own job is strictly ordering and layout on top of
2119
+ those existing primitives: `entries` (a new `ToolTimelineEntry[]`, extending `ToolInvocation` from
2120
+ `@aceshooting/lyra-ui/ai/types` with `startedAt`/`endedAt`, `retryCount`, `redactedFields`,
2121
+ `needsApproval`, and `approved`) sorts ascending by `startedAt`, with untimed entries trailing in
2122
+ their original relative order; duration is derived from `startedAt`/`endedAt` and handed to the
2123
+ chip's own `durationMs`; a retry badge renders only while `retryCount > 0`; and per-entry
2124
+ `redactedFields` (dotted paths, or a bare `"args"`/`"result"`/`"error"` for a whole branch) mask
2125
+ sensitive values in the read-only detail view with a "Value hidden" placeholder -- the copy of
2126
+ `args` handed to the approval dialog is always the real, unredacted value, since approving a call
2127
+ requires seeing what will actually run. Activating a pending entry's chip opens the shared dialog;
2128
+ approving or denying it emits this component's own `lr-tool-approval-decide`
2129
+ (`{ invocationId, approved, args? }`, extending the shared `ToolApprovalEventDetail`) and never
2130
+ mutates `entries` itself -- a host applies the decision and re-assigns `entries`, and the dialog
2131
+ closes on its own if the entry under review disappears or resolves out from under it in the
2132
+ meantime.
2133
+
2134
+ ## 4.0.0
2135
+
2136
+ ### Major Changes
2137
+
2138
+ - cf2cbbb: Release 4.0.0 renames the public custom-element, event, and design-token prefixes from
2139
+ `lyra-*`/`--lyra-*` to `lr-*`/`--lr-*`. The package name, JavaScript `Lyra*` class names,
2140
+ and `lyra-ui` repository/package paths remain unchanged. This is a breaking migration:
2141
+ update element tags, library-specific event names, and CSS custom-property overrides.
2142
+
2143
+ ### Minor Changes
2144
+
2145
+ - cf2cbbb: New `lr-control-group` primitive: a responsive layout wrapper (`role="group"`, `flex-wrap: wrap`,
2146
+ `align-items: center`) for a row of mixed form controls and action buttons — a segmented switcher
2147
+ beside a select and an export button, for example. Distinct from `lr-button-group` (which
2148
+ stretches uniform-height buttons to a shared row height): `lr-control-group` centers children of
2149
+ differing intrinsic heights instead, since it makes no assumption about child type. Gap is
2150
+ themeable via `--lr-control-group-gap`.
2151
+ - aa1fb49: `lr-segmented` gains a `size: '2xs' | 'xs' | 's' | 'm' | 'l' | 'xl' = 'm'` property, matching
2152
+ `lr-select`/`lr-combobox`'s compact-form-control scale (`xs`-`xl`) plus `lr-input`'s `2xs`
2153
+ tier. `size="s"` now renders at the same control height as `lr-select size="s"`/
2154
+ `lr-combobox size="s"`, so a segmented metric switcher can sit flush beside a compact select or
2155
+ combobox in the same toolbar without consumer CSS reaching into `::part(base)`. The default `m`
2156
+ tier is pixel-identical to this component's previous, only rendering.
2157
+
2158
+ ### Patch Changes
2159
+
2160
+ - 5266832: Fixed `lr-select`'s `size="xs"`/`"s"`/`"l"`/`"xl"` to actually enforce their documented
2161
+ per-size minimum trigger height. A `var()` fallback bug meant `--lr-select-trigger-min-height`
2162
+ was silently dead code at every size — only padding and font-size ever varied, height did not.
2163
+ The default (`m`) tier's rendering is unchanged; a consumer-set `--lr-select-trigger-height`
2164
+ override still wins over the per-size floor, as before.
2165
+
2166
+ ## 3.9.0
2167
+
2168
+ ### Minor Changes
2169
+
2170
+ - abdd967: `lr-activity-feed` gains `renderText?: (entry: ActivityEntry) => TemplateResult`, overriding the
2171
+ default plain-text `[part="entry-text"]` rendering with arbitrary rich content — rendered markdown,
2172
+ or markdown plus a trailing tool-call chip list — identically whether or not the feed is currently
2173
+ virtualized, since both the plain and virtualized paths render every entry through the same
2174
+ internal template. Previously `ActivityEntry.text` could only ever render as plain escaped text,
2175
+ with no way to attach richer per-entry content.
2176
+ - b64d4d2: `lr-graph` gains `dimmedNodeIds`/`dimmedLinkIds` (controlled, mirroring
2177
+ `selectedNodeIds`/`selectedLinkIds`): a host can now apply a themeable low-opacity treatment to
2178
+ arbitrary nodes/links -- e.g. dimming every non-neighbor of a hovered node -- via a new
2179
+ `--lr-graph-dimmed-opacity` custom property, in both the `svg` (default) and `canvas` renderers.
2180
+ Previously the only way to express this was reaching into the shadow DOM; `1` (no-op) by default,
2181
+ so existing usage is unaffected.
2182
+ - 1d1935e: `lr-input` gains `'search'` as a documented `LyraInputType` member. It already worked at runtime
2183
+ via unchecked passthrough to the internal native `<input type="search">` (`type` has no runtime
2184
+ validation), but the exported type union didn't include it, so a consumer setting `type="search"`
2185
+ got no compile-time confirmation it was supported and no protection against a future stricter-typed
2186
+ release silently dropping it.
2187
+ - f6b4957: New `<lr-markdown-core>` entry point: a build-lean variant of `<lr-markdown>` for a consumer
2188
+ whose `languages` map already covers every language it renders, mirroring the existing
2189
+ `<lr-code-block>`/`<lr-code-block-core>` split. Its own module never imports shiki's ~200-
2190
+ language default dynamic-import table -- `<lr-markdown>`'s existing `languagesOnly` flag can't
2191
+ give a bundler that guarantee, since it's checked at runtime, not statically provable. Every other
2192
+ capability (GFM, heading anchors, text-quote highlights, math) is unchanged from `<lr-markdown>`;
2193
+ a fenced block whose language isn't in `languages` always renders the plain-text fallback.
2194
+ - 0a5227e: `lr-thread-list` gains `wrapRow?: (thread: ChatThread, row: TemplateResult) => TemplateResult`
2195
+ (data mode only): wraps each row's built-in `lr-conversation-item` with host-supplied content
2196
+ that has no home in the item's own `title`/`excerpt`/`meta`/`actions` surface — most notably a
2197
+ leading purpose icon, since `lr-conversation-item` has no default slot to receive one at all.
2198
+ Previously data mode forced an all-or-nothing choice between its built-in grouping/virtualization
2199
+ and a host's need for row content outside that surface, which only slotted mode (no grouping, no
2200
+ virtualization) could accommodate.
2201
+ - d3f2e13: `lr-usage-badge` gains `formatLatency?: (ms: number) => string`, overriding the built-in duration
2202
+ algorithm (which has no minutes/hours tier — `'{ms}ms'`, or one-decimal seconds above 1000ms) in
2203
+ both the visible strip and the tooltip row. Mirrors `lr-activity-feed`'s `formatTimestamp`
2204
+ convention. Previously a consumer whose latencies commonly exceed a minute (e.g. a long-running
2205
+ agent run) had no way to render its own duration scale instead of a bare seconds count.
2206
+
2207
+ ## 3.8.0
2208
+
2209
+ ### Minor Changes
2210
+
2211
+ - c4cb188: Adds `<lr-activity-feed>`: an append-only streaming log of granular agent actions, collapsing to
2212
+ a localized "Completed N steps" summary once the run is over. Implements the shared follow
2213
+ (stick-to-bottom) contract (`follow` property, `lr-follow-change` event) and virtualizes its body
2214
+ through an internal `<lr-virtual-list>` at/above `virtualizeThreshold` entries, using that
2215
+ component's `scrollToIndex()` method to drive its stick-to-bottom follow. `<lr-virtual-list>`
2216
+ also gains `aria-label` forwarding from the host element onto its internal `role="list"`
2217
+ container, usable independently of `<lr-activity-feed>`.
2218
+ - 5a0276e: Adds an internal, dependency-free ANSI/SGR parser (`src/internal/ansi.ts`, not a public export) —
2219
+ shared groundwork for `lr-terminal`'s streamed console-output rendering. No public API surface
2220
+ change on its own; ships alongside the `lr-terminal` component in the same release.
2221
+ - b92b5d4: Adds `<lr-artifact-panel>`: a shell around one generated artifact — title/kind header, a
2222
+ preview↔code toggle (rendered only once the `code` slot has content), version navigation with a
2223
+ "Restore this version" affordance (`lr-version-change`/`lr-restore`, versions are host state),
2224
+ `streaming`/`aria-busy` state, and built-in copy/download actions. Renders none of the artifact
2225
+ itself — content is slotted.
2226
+ - cf005b9: `lr-attachment-trigger` gains an `'audio'` capability, following the existing `camera` capability's
2227
+ request-only pattern exactly: activating it fires `lr-audio-request` (no embedded recorder), and the
2228
+ host opens its own capture UI — typically `<lr-push-to-talk>` in a `<lr-overlay>`/popover — then
2229
+ hands the resulting blob to its attachment tray. Purely additive: the default `capabilities` stays
2230
+ `['files']`, and every existing `files`/`image`/`camera` behavior is unchanged.
2231
+ - b85934b: Adds `<lr-audio-visualizer>`: a presentational, canvas-drawn voice-activity visualization (bars or
2232
+ waveform), driven by a `MediaStream` (lazily wired to a WebAudio analyser), a numeric `level`, or a
2233
+ `state` (`idle`/`listening`/`thinking`/`speaking`) alone for an ambient animation. Pairs with
2234
+ `lr-push-to-talk`'s `stream`/`lr-level` output. Zero dependencies — native Web Audio only,
2235
+ reduced-motion-aware.
2236
+ - 3310f16: Adds `<lr-av-player>`: an audio/video player built on a native media element with a cue transcript
2237
+ synced to playback, `time-range` anchor/highlight support, an optional dependency-free waveform
2238
+ (peaks-in, no in-component decoding), playback-rate control, and imperative transcript search.
2239
+ Self-registers into the document-viewer registry for the common audio/video MIME types. Owns
2240
+ recorded-media transcript sync — distinct from `lr-transcript-feed` (live voice-session captions)
2241
+ and from `lr-playback` (an index stepper, no media).
2242
+ - 0fe240b: Adds `<lr-branch-picker>`: a controlled "‹ 2 / 5 ›" navigator across regenerated/edited variants of
2243
+ one chat message, mirroring `lr-pagination`'s "never mutates its own state" contract. Fires
2244
+ `lr-branch-change` with the requested (always in-bounds) index; the host swaps the displayed branch
2245
+ content and applies the new index back. Designed to slot into `lr-message-actions`' default slot or
2246
+ directly into `lr-chat-message`'s `actions`/`badges` slots.
2247
+ - bc75a1f: Adds `<lr-browser-frame>`: a presentational "agent computer" viewport — a safe-URL-gated
2248
+ screenshot/frame stream `<img>` (or slotted live media), read-only address bar, visible (never
2249
+ color-only) connection status, kind-distinct action-ping overlays, and take-over/stop affordances
2250
+ (`lr-take-over`, `lr-stop`). No automation transport and no input relay — take-over is an event;
2251
+ the host swaps in its own interactive element.
2252
+ - e29f575: `lr-button` and `lr-input` gain `size="2xs"`, a sub-`xs` tier for dense, toolbar-embedded controls
2253
+ (e.g. a search input and text buttons inside a compact dialog header). Composes with `appearance`/
2254
+ `variant` the same way the existing five sizes already do.
2255
+ - e4762fd: `lr-button` gains `appearance="quiet"`: a bordered, transparent-until-hover tier for a toolbar-style
2256
+ icon+label action whose border/text read fixed `--lr-color-border`/`--lr-color-text-quiet` tokens
2257
+ regardless of `variant`, unlike `appearance="outlined"`'s variant-tinted text — for a call site that
2258
+ needs a genuinely muted resting state rather than a bold bordered button. New
2259
+ `--lr-button-quiet-border`/`--lr-button-quiet-text` custom properties back the two tokens.
2260
+ - 4ac983b: `lr-chat-message` gains `actionsOutsideBubble` (reflects to `actions-outside-bubble`): renders the
2261
+ `actions` slot's content as a sibling immediately after the message bubble instead of nested inside its
2262
+ footer's own padding/background box. Previously a consumer whose action row (e.g. a hover-reveal copy
2263
+ button) had to sit visually outside the bubble's chrome could not adopt this component at all, since
2264
+ `::part(footer)` styling alone cannot detach it from the bubble's box.
2265
+ - 65a1f8c: Adds `<lr-chat-viewport>`: the transcript scroll container for a chat/agent conversation surface —
2266
+ owns the stick-to-bottom `follow` state machine (`follow` property, `lr-follow-change` event,
2267
+ matching the same shared follow contract `<lr-activity-feed>` already implements) while an answer
2268
+ streams, a built-in "jump to latest" pill with a pluralized unread count, and an unread divider. Two
2269
+ content shapes are auto-detected: ordinary element children (slotted mode) or exactly one
2270
+ `lr-virtual-list` (virtual mode, built on that component's `scrollToIndex()` method). Renders no
2271
+ messages and computes no unread state itself — the host supplies `unreadStartIndex` and slots its
2272
+ own message elements or a virtual list.
2273
+ - bf601c8: Adds `<lr-checkpoint>`: an inline conversation restore point — a labeled marker between messages
2274
+ whose Restore affordance confirms inline (an accessible-name-carrying button swap, focus-managed,
2275
+ Escape/focus-out-aware) before firing a `lr-restore { checkpointId, label }` event. Persists and
2276
+ restores nothing itself — host state in, events out. `confirmRestore="false"` skips the inline
2277
+ confirm step entirely; `restorable="false"` renders a plain, non-interactive marker for read-only
2278
+ views or the currently-restored point.
2279
+ - 22c1006: Adds `<lr-chunk-inspector>`: a ranked retrieved-chunks "why this answer" panel — relevance score
2280
+ bars with tier-mapped tones, expandable chunk text (state keyed by chunk id, survives streaming
2281
+ reassignment), and `lr-chunk-open` for landing a chunk in `lr-document-viewer` with its anchor.
2282
+ Virtualizes automatically above `virtualizeAt` rows via the existing `lr-virtual-list`.
2283
+ - c274bd6: `lr-code-block` and `lr-code-block-core` gain `highlight-lines` (declarative `"3-5,7"`-style
2284
+ line emphasis), `interactive-lines` (turns the line-number gutter into a keyboard-navigable,
2285
+ clickable roving-tabindex group emitting `lr-line-click`), and `line-range` anchor-target support
2286
+ (`highlights`, `activeHighlightId`, `scrollToAnchor()`, event `lr-text-select`) — identical on
2287
+ both components since they share the new line-addressing logic. Previously there was no way to
2288
+ emphasize or deep-link to a specific line/range of lines in a rendered code block.
2289
+ - f71fcac: Adds `<lr-commit-card>`: a compact commit summary card — abbreviated/copyable hash, subject/body
2290
+ message split, author/time meta, a non-color-only aggregate `+N -M` diffstat, and a collapsible
2291
+ per-file change list (`lr-file-select` on activation) reusing `lr-file-tree`'s `GitStatus`
2292
+ vocabulary and shared `gitStatus*` labels.
2293
+ - 22c1006: Adds `<lr-community-card>`: a GraphRAG community-report card — label, summary excerpt, member
2294
+ count, member chips with a "+N" overflow chip, and a drill-in action (`lr-drill`) surfaced from
2295
+ the header, an explicit drill button, and the overflow chip alike. A `compact` mode renders just
2296
+ title + member count + drill button for dense listings (e.g. inside `lr-provenance-panel`).
2297
+ - 1432601: Add `lr-compare-panel`: side-by-side A/B output comparison with a winner vote (LMSYS-arena /
2298
+ LangSmith-pairwise style) — two slotted panes (`a`/`b`), an optional shared `prompt` header, a
2299
+ `role="group"` vote bar (better-A / better-B / tie / both-bad, the last two individually
2300
+ hideable), and optional proportional `syncScroll` between panes. No hotkeys (slotted content may
2301
+ contain inputs); casting a vote announces through an internal live region.
2302
+ - bc75a1f: Adds `<lr-confirm-bar>`: an inline, non-modal approve/deny block for one proposed action — the
2303
+ in-flow sibling of `lr-tool-approval-dialog` for confirmations that belong in the transcript instead
2304
+ of an overlay. Same `lr-approve`/`lr-deny` event shapes and the same heading/args-label/deny/approve
2305
+ localization keys as the dialog, so the two stay in lockstep. No focus trap, scroll lock, or
2306
+ Escape/backdrop handling; on activation, focus moves synchronously to the always-present decided-state
2307
+ text before the Deny/Approve buttons unmount, and an internal live region announces the outcome.
2308
+ - 23bfb7b: `lr-conversation-item` gains a `meta` slot (small, non-focusable structured fields below the
2309
+ title/excerpt — e.g. a day label, project name, cost) and an `excerpt` slot that wins over the
2310
+ existing `excerpt` property whenever it has assigned content, mirroring `lr-timeline-item`'s own
2311
+ `timestamp` slot-wins-over-property pattern. Previously a consumer needing a rich excerpt (e.g. a
2312
+ search-hit snippet with `<mark>` highlighting) or a multi-field meta line had to flatten that
2313
+ structure into the plain-text `excerpt` property or hand-roll the row entirely.
2314
+ - 2ad038b: `lr-dataset-viewer` now virtualizes through `lr-virtual-list` (a new `item-role="row"` mode,
2315
+ mapping to a proper `role="table"`/`role="row"`/`role="rowgroup"` accessibility tree) instead of a
2316
+ single synchronous `<table>`, lifting its row cap from 1,000 to the shared 10,000-row default every
2317
+ other tabular viewer already uses. It also gains `cell-range` anchor-target support (`highlights`,
2318
+ `activeHighlightId`, `scrollToAnchor()`, event `lr-highlight-activate`) and an imperative
2319
+ in-document search API (`search()`, `searchNext()`, `searchPrevious()`, `clearSearch()`, event
2320
+ `lr-search-change`), sharing the same raw-grid cell addressing as `lr-csv-viewer`, with the
2321
+ header row always included since this viewer always parses with PapaParse's `header: true`. The
2322
+ `lyra:dataset` document-viewer registration now declares `capabilities: { anchors: ['cell-range'],
2323
+ search: true, textSelect: false }`. `lr-virtual-list` itself gains the underlying
2324
+ `item-role`/`row-index-offset` properties this required, additive and defaulting to today's exact
2325
+ `listitem` behavior for every other consumer. Previously a 1,001+ row dataset file failed to load at
2326
+ all, and there was no way to highlight or search a cell.
2327
+ - 2ad038b: `lr-diff-view` gains `layout="split"` (two side-by-side columns derived from the same line-diff
2328
+ alignment as the default unified view — unbalanced replace hunks pad the shorter side with empty
2329
+ placeholder rows) and optional syntax highlighting via `language`/`languages` (same fine-grained
2330
+ shiki-core-only shape as `lr-code-block-core`, so the peer-free default stays truly peer-free).
2331
+ Previously diff-view only rendered a single interleaved unified view with no highlighting option.
2332
+ - dc168c7: `lr-docx-viewer` gains `getHeadingTree()` (a document-ordered heading outline stamped with
2333
+ GitHub-slugger-style ids, using the same slugging algorithm as `lr-markdown`), `fragment`/
2334
+ `text-quote` anchor-target support (`highlights`, `activeHighlightId`, `scrollToAnchor()`, events
2335
+ `lr-highlight-activate`/`lr-text-select`/`lr-anchor-result`), and an imperative in-document
2336
+ search API (`search()`, `searchNext()`, `searchPrevious()`, `clearSearch()`, event
2337
+ `lr-search-change`). Previously there was no way to deep-link into a section, highlight a quoted
2338
+ passage, or search inside a rendered Word document.
2339
+ - d3edf31: `lr-ebook-viewer` gains `getToc()` (a flat, nested table of contents from the EPUB's own
2340
+ navigation document), a `location` property (get/set the current CFI or spine href, with
2341
+ `lr-location-change` on user navigation), an imperative in-book search API (`search()`,
2342
+ `searchNext()`, `searchPrevious()`, `clearSearch()`, event `lr-search-change`), and `cfi`/
2343
+ `text-quote` anchor-target support (`highlights`, `activeHighlightId`, `scrollToAnchor()`, events
2344
+ `lr-highlight-activate`/`lr-text-select`). Previously there was no way to read an EPUB's table
2345
+ of contents, deep-link into a specific location, or search inside a rendered book.
2346
+ - 2ad038b: `lr-email-viewer` attachments become interactive: each row is now a real button emitting
2347
+ `lr-attachment-open { attachment: { filename, mimeType, content } }` with the attachment's decoded
2348
+ bytes attached (the component itself never opens/downloads anything — host-owned routing, e.g. into
2349
+ `lr-document-viewer`). A new `fold-quotes` property collapses trailing quoted-reply text/HTML
2350
+ (`>`-prefixed text runs, `gmail_quote`/`yahoo_quoted`/Outlook-style HTML blocks) behind a localized
2351
+ toggle. Previously attachments were inert metadata with no way to retrieve their content, and quoted
2352
+ reply chains always rendered in full.
2353
+ - ba094cb: Adds `<lr-emoji-picker>`: a searchable, keyboard-navigable, form-associated emoji picker
2354
+ (`value`/`lr-change`, matching this library's other form-control conventions). `groups` is fully
2355
+ consumer-suppliable — this component ships no emoji data of its own — with an optional convenience
2356
+ auto-loader for a default set via the `emoji-picker-element-data` peer when `groups` is left unset.
2357
+ Lets a consumer currently wrapping the third-party `emoji-picker-element` custom element (plus its
2358
+ locale-data package) as a direct dependency replace it with a first-party `lr-*` component instead.
2359
+ - 22c1006: Adds `<lr-entity-card>`: a dossier card for one knowledge-graph entity (`LyraEntity`) — type
2360
+ badge, description, key/value property rows, relationship-degree and community rows, and a
2361
+ built-in "focus in graph" action that emits `lr-entity-activate` for a host to route into
2362
+ `lr-graph`'s `focusNode()`.
2363
+ - 22c1006: Adds `<lr-entity-chip>`: an inline `@entity` mention for agent prose with a hover/focus preview
2364
+ popover, reusing `lr-citation-badge`'s interaction contract wholesale (200ms hover-leave grace,
2365
+ independent hover/focus hold-open state, Escape dismissal, Space opens/Enter activates). The
2366
+ knowledge-graph sibling of `lr-citation-badge` — renders its `label` text rather than a `[n]`
2367
+ index, and reflects `type` for host-level per-type theming.
2368
+ - 2ab49e6: Adds `<lr-env-list>`: a masked key/value list for environment variables and secrets
2369
+ (`<dl>`/`<dt>`/`<dd>` semantics), defaulting every entry to masked (a fixed eight-bullet run,
2370
+ length-independent so value length is never leaked) with per-row reveal (`lr-reveal-change`, state
2371
+ keyed by name and position, and reset for a row whose name shifts position) and copy (`lr-copy`,
2372
+ always copies the real value). `revealable=false` for screen-share-safe hosts. Masking is
2373
+ presentational, not a security boundary.
2374
+ - 892c9d3: Adds `<lr-file-tree>`: a file-explorer preset over `lr-tree` + `lr-file-icon` with path-keyed
2375
+ nodes, per-file git-status badges and `+N -M` diffstat, lazy directory loading (`setChildren()`,
2376
+ `lr-load-children`), `revealPath()`, and `lr-file-select`/`lr-file-open` events (matching the
2377
+ "Enter/click on an already-selected file opens it" keyboard parity rule).
2378
+ - 22c1006: Adds `<lr-flow-canvas>`: a dependency-free, pannable/zoomable DAG workflow canvas — HTML card
2379
+ nodes with typed connection handles, SVG Bézier edges with arrowheads and labels, a shared layered
2380
+ auto-layout for unpositioned nodes, and controlled selection/drag/connect gestures behind three
2381
+ independent opt-in flags (`nodes-draggable`, `connectable`, `droppable`). Readonly viewer by default;
2382
+ never mutates `nodes`/`edges` itself. Ships a `registerCompanion()` hook so `lr-flow-minimap`,
2383
+ `lr-flow-controls`, and `lr-flow-run-overlay` (following in subsequent releases) can attach
2384
+ without reaching into its shadow DOM.
2385
+ - 22c1006: Adds `<lr-flow-controls>`: the zoom in/out, fit, and interaction-lock button cluster for
2386
+ `lr-flow-canvas`, so every flow surface ships the same affordances without hosts rebuilding them.
2387
+ Zoom buttons disable at the resolved canvas's `minZoom`/`maxZoom` bounds; the lock toggle stays in
2388
+ sync with the canvas's `locked` attribute regardless of what changed it.
2389
+ - 22c1006: Adds `<lr-flow-minimap>`: a corner overview map for `lr-flow-canvas` — scaled node rectangles
2390
+ (status-tinted) plus a draggable, keyboard-operable viewport rectangle for orientation and fast
2391
+ navigation on canvases larger than the screen. Attaches via `registerCompanion()`, either slotted
2392
+ into one of the canvas's corner slots or externally via `for="canvas-id"`.
2393
+ - 22c1006: Adds `<lr-flow-node>`: the workflow node card — header/body/toolbar chrome, tool-lifecycle status
2394
+ tones with a visible (never color-only) status chip, a determinate progress bar, and named
2395
+ connection-handle elements. Used automatically by `lr-flow-canvas` as the default card for any
2396
+ node without a slotted override, and usable standalone for palette previews or docs.
2397
+ - 22c1006: Adds `<lr-flow-run-overlay>`: execution-state presentation for `lr-flow-canvas` — mirrors a
2398
+ `FlowRunDecorations` map into the resolved canvas (which owns the actual node/edge paint) and
2399
+ renders a compact "{done} of {total} steps complete" summary strip with per-status counts.
2400
+ Status transitions announce through a throttled live region. Pure pushed state — no execution,
2401
+ polling, or internal clock.
2402
+ - 2ad038b: Adds an internal `application/geo+json` document-viewer registry bridge (`<lr-geojson-view>`,
2403
+ `.geojson` filename matching included): fetches and validates a GeoJSON `Feature`/`FeatureCollection`/
2404
+ bare-geometry payload, computes a bounding-box fit, and renders it through `lr-map`'s new
2405
+ `dataLayers` property with a feature-count status line. Falls back to `lr-json-viewer` with a
2406
+ missing-library callout when the optional `maplibre-gl` peer isn't installed. Not a documented public
2407
+ tag this round — importing `geojson-view/geojson-view.js` opts a host into the bridge, matching how
2408
+ `lr-map`/`lr-graph`/the chart family already stay out of the root barrel import.
2409
+ - ca9258f: `lr-graph` gains `renderer: 'svg' | 'canvas'` (default `'svg'`, unchanged). `'canvas'` swaps the
2410
+ per-node/per-link SVG DOM for a single DPR-aware `<canvas>` (reusing `lr-heatmap`'s proven backing-
2411
+ store/resize/DPR-watch machinery), targeting roughly 5,000 nodes / 10,000 links versus SVG's ~500/
2412
+ ~1,500 ceiling. Hit-testing uses an offscreen color-picking canvas (exact hits for all three node
2413
+ shapes, stroked/dashed links, and hull blobs, one code path, zero new dependencies); pointer drag,
2414
+ click, double-click-to-expand, and hover tooltips all work via that same hit-test. Keyboard/screen-
2415
+ reader parity is preserved through an offscreen virtual-cursor button list driving the identical
2416
+ roving/announcement logic as SVG mode — the honest v1 trade-off is no `::part(node)`/`::part(link)`
2417
+ styling (pixels, not elements) and a drawn focus ring instead of a CSS one, both documented. Fully
2418
+ additive — the default `renderer: 'svg'` reproduces today's DOM exactly.
2419
+ - c6ab7c8: `lr-graph` gains `GraphNode.communityId` and a `communities` property, rendering one translucent
2420
+ convex-hull blob per entry (membership = union of `memberIds` and matching `communityId`) behind
2421
+ links/nodes. Hulls are keyboard/click-activatable (`lr-community-click`), join the roving focus
2422
+ ring after nodes and links, and are included in `fit()`'s bounding-box calculation. Fully additive
2423
+ — an empty `communities` array (the default) renders no hulls and leaves the roving ring/`fit()`
2424
+ behavior unchanged.
2425
+ - c996af0: `lr-graph` gains `showEdgeLabels` (default `false`) to draw each link's `label` as visible SVG
2426
+ text at the segment midpoint, and `edgeLabelMinZoom` (default `0.6`) to hide all edge labels below
2427
+ that zoom scale. A per-label length gate also hides a label whose measured text width exceeds 85%
2428
+ of its edge's current on-screen length. Labels are `aria-hidden` (the accessible name already
2429
+ carries `label` via the existing link announcement) and fully opt-in — a graph that never sets
2430
+ `showEdgeLabels` renders no edge-label DOM at all.
2431
+ - 7f7511a: `lr-graph` gains a double-activate expand gesture: double-clicking a node, or activating the same
2432
+ focused node twice via Enter/Space within 500ms, emits `lr-node-expand { id }`. A new
2433
+ `GraphNode.expandable` flag renders a "+" badge and adds "expandable" to the node's spoken text. A
2434
+ node newly linked to an already-positioned neighbor (e.g. appended after an expand) now spawns near
2435
+ that neighbor instead of a random position. Fully additive — no existing click/keyboard behavior
2436
+ changes, and a graph that never sets `expandable` never renders the badge (though the
2437
+ `lr-node-expand` event itself fires for any double-activated node, matching native
2438
+ dblclick semantics).
2439
+ - 5d77b48: `lr-graph` gains a programmatic camera (`focusNode(id, { zoom? })`, `fit({ padding? })`, both
2440
+ reduced-motion-aware rAF tweens that keep d3-zoom's own state consistent), a declarative
2441
+ `focusId` twin (centers once, renders a persistent `focus-halo` ring), and a controlled selection
2442
+ model (`selectionMode: 'none' | 'single' | 'multiple'`, `selectedNodeIds`/`selectedLinkIds`,
2443
+ `lr-selection-change`) mirroring `lr-heatmap.selectedCell`'s controlled contract — the
2444
+ component only ever emits intent, never assigns the selection props itself. Fully additive: default
2445
+ `selectionMode: 'none'` and unset `focusId` reproduce today's behavior exactly.
2446
+ - 844fe95: `lr-graph` gains `lr-node-enter`/`lr-node-leave`/`lr-link-enter`/`lr-link-leave` hover
2447
+ events (mirroring the existing `lr-node-click`/`lr-link-click` detail shapes) plus a `data-hovered`
2448
+ attribute toggled on the hovered node/link element for pure-CSS theming. Both are suppressed while a
2449
+ drag or pan gesture is in progress, so a drag crossing over other nodes/links doesn't spam
2450
+ enter/leave pairs. Previously a consumer computing an adjacency-based neighbor highlight on hover
2451
+ (e.g. dimming every unconnected node/link) had no way to observe which node/link was currently
2452
+ hovered from outside the component.
2453
+ - f8d6b9e: `lr-graph` gains `layout: 'force' | 'layered'` (default `'force'`, unchanged). `'layered'`
2454
+ computes a deterministic Sugiyama-lite layout instead of running d3-force — longest-path layering,
2455
+ barycenter crossing reduction, cycle-safe (back edges reversed internally, the caller's data is
2456
+ never mutated). The algorithm itself lives in a new shared, dependency-free
2457
+ `src/internal/layered-layout.ts`, a standalone util suitable for any future layered-diagram
2458
+ consumer. Node drag is disabled in layered mode; pan/zoom, keyboard, focus/fit, hulls, edge labels,
2459
+ and type filtering all work identically to force mode. Fully additive — the default `layout:
2460
+ 'force'` reproduces today's simulation-driven layout exactly.
2461
+ - 22c1006: Adds `<lr-graph-legend>`: a node-type legend for a paired `lr-graph`, rendering one swatch +
2462
+ label + count row per §3.4 node type and doubling as a visibility filter. Event-decoupled from any
2463
+ graph instance — a host forwards `graph.nodeTypes` in as `types` and forwards
2464
+ `lr-visibility-change`'s `hiddenTypes` back out to `graph.hiddenTypes`.
2465
+ - 942798e: `lr-graph` gains `GraphNode.type` and a new `nodeTypes` property declaring each type's legend
2466
+ label, fill color, and shape (`circle`/`square`/`diamond`). Fill resolution precedence is
2467
+ `node.color` > the type's own color > an ordered categorical fallback palette
2468
+ (`--lr-graph-cat-1`…`--lr-graph-cat-8`, new tokens) by the type's index in `nodeTypes` > the
2469
+ existing untyped default. Typed nodes also gain richer spoken text ("{label} ({type})"). Fully
2470
+ additive — a graph with no `type`/`nodeTypes` set renders identical circles, unchanged.
2471
+ - 32f7b12: `lr-graph` gains `hiddenTypes: string[]`, hiding every node whose `type` is listed (plus incident
2472
+ links) from rendering, the simulation, the keyboard roving ring, and the accessible data list/
2473
+ counts. Positions round-trip via a new remembered-position cache, so toggling a type off and back
2474
+ on restores each node where it was instead of re-randomizing. Fully additive — an empty
2475
+ `hiddenTypes` (the default) renders every node/link exactly as before.
2476
+ - e022166: Adds `<lr-handoff-divider>`: a labeled semantic separator marking control transfer between agents
2477
+ in a transcript (e.g. "Transferred to Research Agent"), with an optional `avatar` slot. Root is
2478
+ `role="separator"` named by the computed label; the label is announced once on first connect
2479
+ through an internal live region, since a handoff lands mid-stream and later property changes never
2480
+ re-announce.
2481
+ - 4cddc07: Adds `<lr-highlight-layer>`: a presentational overlay that paints highlight rectangles
2482
+ (percent-of-box coordinates) over positioned content — a pdf page, an image, any relatively-positioned
2483
+ frame. Roving-tabindex keyboard access (ArrowUp/Down/Left/Right honoring RTL, Home/End, Enter/Space),
2484
+ `aria-current` on the active rect, a one-shot `flash()` emphasis pulse with a reduced-motion static
2485
+ fallback, and token-mapped tones. Zero dependencies. `lr-pdf-viewer` adopts it next for per-page
2486
+ highlight painting.
2487
+ - 4c707de: Adds `<lr-image-viewer>`: a full pan/zoom raster-image viewer with labeled region highlights and
2488
+ opt-in region annotation (pointer-drag or keyboard), self-registering into the document-viewer
2489
+ registry for `image/png`, `image/jpeg`, `image/webp`, `image/gif`, `image/avif`, and `image/bmp`.
2490
+ Distinct from `<lr-svg-viewer>` (vector documents) and `<lr-image-comparer>` (before/after
2491
+ comparison) — this is the landing surface for `region`-anchored citations (bounding-box grounding).
2492
+ - 2ad038b: `lr-json-viewer` gains an imperative search API (`runSearch()`, `searchNext()`,
2493
+ `searchPrevious()`, `clearSearch()`, event `lr-search-change`) as a thin layer over its existing
2494
+ declarative `search` property -- the property, its highlighting, and its force-expand behavior are
2495
+ unchanged; the new methods add match-count resolution and a navigable cursor (`data-active` on the
2496
+ current match) on top. The count-resolving entry point is named `runSearch()` rather than `search()`
2497
+ (unlike this same quartet on other viewers) because `search` is already this component's own public
2498
+ string property -- a method can't share its name. Previously there was no way to count matches or
2499
+ step between them programmatically.
2500
+ - ac19eb0: `lr-lite-chart` gains a `legendText?: (label: string, datasetIndex: number) => string` hook,
2501
+ appending formatter-supplied text (e.g. a value or percentage share) after each series' label in the
2502
+ built-in legend row — mirrors the existing `pointText`/`tickFormat` opt-in-hook convention. Previously
2503
+ a consumer needing per-series legend text beyond the bare label had to hand-roll an entire replacement
2504
+ legend instead of using the built-in `legend` prop.
2505
+ - c721d97: `lr-map` gains a `dataLayers: GeoJsonDataLayer[]` property: each entry adds a GeoJSON source plus
2506
+ fill/line/circle layers (colored from `--lr-*` tokens by an optional `tone`), independent of the
2507
+ existing `choropleth` prop (which requires `field`/`stops` and can't display plain geometry). Defaults
2508
+ to an empty array — zero behavior change for existing `lr-map` users. This is the enabler for the
2509
+ upcoming GeoJSON-file document-viewer bridge, and is useful standalone for rendering arbitrary
2510
+ GeoJSON shapes (routes, zones, points of interest) without hand-building maplibre-gl layers.
2511
+ - 92955fc: `lr-markdown` gains `heading-anchors` (stamps computed GitHub-slugger-style ids on headings),
2512
+ `getHeadingTree()` (a document-ordered heading outline, computed regardless of `heading-anchors`),
2513
+ `fragment`/`text-quote` anchor-target support (`highlights`, `activeHighlightId`, `anchor`,
2514
+ `scrollToAnchor()`, events `lr-highlight-activate`/`lr-text-select`/`lr-anchor-result`), and
2515
+ `math` (renders `$...$`/`$$...$$` TeX as MathML via the optional `katex` peer, falling back to
2516
+ literal source text when the peer isn't installed). Previously there was no way to deep-link into a
2517
+ section, highlight a quoted passage, or render math in rendered Markdown content.
2518
+ - 3492739: `lr-markdown` gains real shiki syntax highlighting for fenced code blocks, reusing
2519
+ `<lr-code-block>`'s own optional `shiki` peer and grammar-loading machinery directly (not by
2520
+ embedding `<lr-code-block>` itself, which would have hit DOMPurify's default custom-element
2521
+ blocklist and re-mounted — losing state and re-triggering async loads — on every streaming chunk).
2522
+ On by default whenever the `shiki` peer is installed (set `highlightCode="false"` to opt out); new
2523
+ `languages`/`languagesOnly` properties mirror `<lr-code-block>`'s own fine-grained bundle-size
2524
+ controls. Highlighting is skipped entirely while `streaming` is `true` and applied once a stream
2525
+ settles, so there is no added per-chunk cost while content is still arriving.
2526
+ - e5df5af: Adds `<lr-message-actions>`: the per-message action toolbar for `lr-chat-message`'s `actions` slot
2527
+ — opt-in built-ins (`copy` / `regenerate` / `edit` / `feedback`, in `controls`-array order) that emit
2528
+ intent events (`lr-regenerate`, `lr-edit`, plus bubbled `lr-copy`/`lr-change`/`lr-submit`
2529
+ from the embedded copy button and thumbs-only feedback), and a default slot for custom controls (e.g.
2530
+ a slotted `lr-branch-picker`) that participate in the toolbar's ArrowLeft/ArrowRight/Home/End
2531
+ navigation. Optional `reveal-on-hover` hides the bar until the enclosing `lr-chat-message` is
2532
+ hovered or a control inside has focus.
2533
+ - 9544450: Add `lr-message-feedback`: thumbs up/down for one assistant message, with an optional inline
2534
+ detail step (multi-select reason chips + a free-text comment) that opens as a disclosure directly
2535
+ below the thumbs rather than a floating overlay. Fires `lr-change` on every rating toggle and
2536
+ `lr-submit` (`{ value, reasonIds, comment }`) from the panel's submit button; stores nothing
2537
+ itself — a host persists the rating and may reflect a previously-recorded one back via `value` +
2538
+ `disabled`. Re-activating the pressed thumb clears the rating unless its own detail panel is open,
2539
+ in which case that click re-opens the panel with any surviving draft instead.
2540
+ - 22c1006: Adds `<lr-mind-map>`: a radial expandable topic tree (NotebookLM-style Mind Maps) — zero-dependency
2541
+ SVG, closed-form arc-subdivision layout in its own `mind-map-layout.ts` module, single-tab-stop
2542
+ keyboard roving (mirroring `lr-word-cloud`), and `lr-topic-select`/`lr-topic-toggle` events.
2543
+ Multiple root topics hang off an implicit center hub; expansion state is keyed by topic id and
2544
+ survives streaming `topics` reassignment.
2545
+ - 2ad038b: Recorded decision: `.msg` (Outlook) files are not supported this round. `.msg` is OLE/CFB binary per
2546
+ MS-OXMSG; the available npm parser (`@kenjiuno/msgreader` plus its `decompressrtf` companion) is
2547
+ below this library's maintenance bar for an optional peer. `.msg` files continue to resolve to
2548
+ `<lr-document-preview>`'s generic download fallback, exactly like any other unregistered format —
2549
+ convert to `.eml` server-side to use `<lr-email-viewer>` instead. No API change; this changeset
2550
+ exists to document the decision, guarded by a permanent regression test.
2551
+ - 22c1006: Adds `<lr-neighbor-list>`: one entity's relationship rows (relation, direction, neighbor) with
2552
+ per-row navigate (`lr-entity-activate`) and expand-in-graph (`lr-node-expand`, matching
2553
+ `lr-graph`'s own event name/detail) affordances, optional relation grouping, and automatic
2554
+ `lr-virtual-list` virtualization above `virtualizeAt` rows.
2555
+ - 22c1006: Adds `<lr-node-palette>`: a searchable, categorized node library for workflow editors — drag an
2556
+ item onto a `droppable` `lr-flow-canvas`, or place it by keyboard (`lr-palette-place`/
2557
+ `lr-select`). Fully decoupled from the canvas itself, agreeing only on the exported
2558
+ `FLOW_PALETTE_MIME_TYPE` drag-payload constant.
2559
+ - a0e579a: Adds `<lr-notebook-viewer>`: a read-only Jupyter notebook (nbformat 4.x) renderer that parses
2560
+ `.ipynb` JSON natively and composes `lr-markdown`/`lr-code-block`/`lr-json-viewer` per cell,
2561
+ with `node-path`/`fragment` cell anchors and imperative search over cell sources and text outputs.
2562
+ Self-registers into the document-viewer registry for `application/x-ipynb+json`. Execution, kernels,
2563
+ and ipywidgets are out of scope; stream/error outputs render as plain preformatted text this round.
2564
+ - 15062d0: Adds `<lr-page-rail>`: a virtualized vertical thumbnail rail for page-addressed documents, with
2565
+ per-page highlight heat markers. Wired mode (`viewer`/`for`) tracks page/count from a
2566
+ `PageThumbnailSource`-shaped viewer's own `lr-load`/`lr-page-change` events and lazily renders
2567
+ thumbnails as rows materialize (`lr-pdf-viewer` satisfies this structurally); mediated mode
2568
+ (`page-count`/`page`) works as a fully functional pager without a wired viewer. Roving-tabindex
2569
+ keyboard access via `lr-virtual-list`, typed-digit page jump, `lr-page-select` event.
2570
+ - 22c1006: Adds `<lr-path-strip>`: a compact, horizontally scrollable node -> relation -> node chain
2571
+ rendering a GraphRAG reasoning path, with one roving tab stop across every element (nodes and
2572
+ relations alike), logical (RTL-mirroring) directed-edge arrows, and `lr-entity-activate`/
2573
+ `lr-relation-activate` events.
2574
+ - 75c17bd: `lr-pdf-viewer` becomes the reference `DocumentAnchorTarget` implementation: resolves `page`,
2575
+ `text-quote`, and `region` anchors (`scrollToAnchor()`), paints highlights per page via
2576
+ `lr-highlight-layer`, exposes `getPageText(page)` and `renderPageThumbnail(page, canvas, options?)`
2577
+ for rail/search/chunking consumers, and emits `lr-load { pageCount }`,
2578
+ `lr-highlight-activate`/`lr-text-select`/`lr-anchor-result`. The `application/pdf` document-
2579
+ viewer registration now declares its anchor/text-select capabilities and forwards `anchor`/
2580
+ `highlights`. All additive — existing `src`/`page`/`zoom`/`nextPage()`/`previousPage()`/`zoomIn()`/
2581
+ `zoomOut()` and their events are unchanged.
2582
+ - 1879c40: `lr-pdf-viewer` gains an imperative in-document search API (`search()`, `searchNext()`,
2583
+ `searchPrevious()`, `clearSearch()`, event `lr-search-change`), a public `goToPage(page):
2584
+ Promise<boolean>` method, and `getOutline(): Promise<PdfOutlineItem[]>` for reading a PDF's table of
2585
+ contents. Search matches paint as `<mark part="search-match">` (`search-match-active` for the
2586
+ current one) without touching any highlight state. The `application/pdf` document-viewer
2587
+ registration now declares `search: true` in its capabilities. Previously there was no way to search
2588
+ inside a rendered PDF, jump to a page programmatically, or read its outline.
2589
+ - 22c1006: Adds `<lr-provenance-panel>`: the grounding breakdown for one answer — a four-section disclosure
2590
+ panel (Entities / Relationships / Communities / Text chunks) composing `lr-entity-chip`,
2591
+ `lr-path-strip`, compact `lr-community-card`s, and a compact `lr-chunk-inspector`. Every child
2592
+ event bubbles straight through unmodified; its own `lr-toggle` event tracks per-section
2593
+ expand/collapse state, which survives streaming `provenance` reassignment.
2594
+ - 2d15c51: Adds `<lr-push-to-talk>`: a mic capture button owning the full `getUserMedia`/`MediaRecorder`
2595
+ lifecycle — permission request, hold or toggle recording, optional chunked streaming
2596
+ (`lr-record-chunk`) for streaming STT, an opt-in RMS level meter (`lr-level`), a `max-duration-ms`
2597
+ auto-stop guard, and `lr-record-start`/`lr-record-stop`/`lr-record-cancel`/`lr-record-error`
2598
+ events. No SDK dependency — native browser APIs only. Previously lyra-ui had no voice-capture
2599
+ component at all; every agentic voice UI had to hand-roll this lifecycle from scratch.
2600
+ - 3a2f6d2: Add `lr-rubric-form`: a configurable annotation rubric (LangSmith annotation-queue style) —
2601
+ score, category, and freeform-comment keys with a submit-and-next flow for working through an eval
2602
+ queue. Follows `lr-tool-param-form`'s exact `ElementInternals`-attached-directly, JSON-serialized
2603
+ form-value pattern; a `score` key renders `lr-segmented` (≤10 integer steps) or `lr-slider`,
2604
+ `category` renders `lr-select` or `lr-checkbox-group` (`multiple`), and `comment` renders
2605
+ `lr-textarea`.
2606
+ - c388b94: Add themeable static edge fades and native horizontal scrolling to overflowing `lr-segmented` and
2607
+ `lr-tabs` rows.
2608
+ - de5b8b7: Adds `<lr-sequence-strip>`: a compact, one-thin-cell-per-item strip visualizing a sequence of
2609
+ categorical states with an optional secondary per-cell marker (e.g. a CI build-step strip, a
2610
+ log-severity strip, or — the motivating case — a per-turn conversation-history strip). Pure CSS/flex,
2611
+ zero dependencies, `role="img"` with an auto-generated per-category "label: count" `aria-label`
2612
+ summary (matching `lr-sparkline`'s accessibility model), plus a pointer-hover tooltip showing each
2613
+ item's own label.
2614
+ - 22c1006: Adds `<lr-source-picker>`: a checkbox tree/list scoping which sources ground the next answer —
2615
+ tri-state folders, select-all, `lr-file-icon` type icons, and built-in search that keeps matching
2616
+ descendants' ancestors visible. Deliberately not `FormAssociated` (a scoping panel, not a form
2617
+ control, mirroring `lr-tool-select-dialog`'s stance) and renders its own `role="tree"` rather than
2618
+ composing `lr-tree`, since `TreeItem` has no tri-state checkbox model.
2619
+ - 685eb35: Add `lr-span-waterfall`: the horizontal-timeline projection of the same `LyraSpan[]`
2620
+ `lr-trace-tree` consumes — a time axis, one row per span in start order, and status-toned,
2621
+ keyboard-navigable bars (Langfuse timeline / Temporal event-history style). Declarative
2622
+ `viewStartMs`/`viewEndMs` window props (composable with `lr-time-range` as a brush) stand in for
2623
+ zoom/pan gestures this round. Both components emit the same `lr-span-select { id }` and accept
2624
+ the same `activeSpanId`, so a host syncs selection between them with two listeners and one property
2625
+ binding.
2626
+ - 2ad038b: `lr-spreadsheet-viewer` and `lr-csv-viewer` gain `cell-range` anchor-target support
2627
+ (`highlights`, `activeHighlightId`, `scrollToAnchor()`, event `lr-highlight-activate`) and an
2628
+ imperative in-document search API (`search()`, `searchNext()`, `searchPrevious()`, `clearSearch()`,
2629
+ event `lr-search-change`) — identical on both viewers, addressing cells by the same 1-based raw
2630
+ grid (header row included) an A1 reference already implies. Spreadsheet's search/anchor resolution
2631
+ additionally spans every sheet, switching `lr-tabs` as needed. Both registry entries now declare
2632
+ `capabilities: { anchors: ['cell-range'], search: true, textSelect: false }`. Previously there was
2633
+ no way to highlight or search a specific cell/range in a rendered spreadsheet or CSV file.
2634
+ - 761ab24: Adds `<lr-stack-trace>`: parses V8/JS-TS, Firefox/Safari, and Python stack traces (including
2635
+ chained-error groups) into a message plus collapsible, activatable frames (`lr-frame-select`),
2636
+ folding internal frames (`node_modules/`, `node:internal`, `site-packages/`, ...) behind a
2637
+ count-labeled toggle. Falls back to verbatim raw text when nothing parses.
2638
+ - b33bb35: Adds `<lr-suggestion-chips>`: starter prompts (empty thread) and follow-up suggestions (after a
2639
+ response) as a horizontally scrollable chip row (or a wrapping grid via `wrap`), each with an optional
2640
+ secondary detail line. Fires `lr-suggestion-select` (`{ id, label }`) on activation — never writes
2641
+ into a composer or sends anything itself. Keyed `repeat()` on `id` preserves focus across a mid-stream
2642
+ suggestions replacement.
2643
+ - 2ad038b: `lr-svg-viewer` and `lr-document-preview` (its image-format path) gain an opt-in `zoomable`
2644
+ property that wraps the rendered content in an internal `lr-zoomable-frame` for pan/zoom
2645
+ inspection, plus display-only `region` anchor-target support (`highlights`, `activeHighlightId`,
2646
+ `scrollToAnchor()`, event `lr-highlight-activate`) for percent-unit bounding-box highlights that
2647
+ scale with the zoom level. `zoomable` defaults to `false` on both, so an inline thumbnail (e.g. in a
2648
+ chat stream) doesn't unexpectedly grow a focusable zoom-chrome viewport. Previously neither viewer
2649
+ had any pan/zoom or region-highlighting capability.
2650
+ - 1e051a4: `lr-swatch-picker` options gain an optional `icon` field (`SwatchOption.icon`, mirroring
2651
+ `lr-segmented`'s `SegmentedItem.icon`): a consumer-supplied shape (e.g. a brand glyph) rendered in
2652
+ place of the plain filled circle, exposed as `::part(swatch-icon)`. A `currentColor`-based SVG picks up
2653
+ the option's `color` automatically through the swatch's `color` custom property, so consumers who
2654
+ previously hand-rolled a row of colored icon buttons (rather than plain color circles) can now use the
2655
+ picker directly.
2656
+
2657
+ The selected swatch also gains two new opt-in, off-by-default custom properties for a more emphatic
2658
+ selected state: `--lr-swatch-picker-selected-blur` (0 by default, a crisp ring; set a real length for
2659
+ a soft glow tinted by the swatch's own color -- works for both a plain color circle and an icon swatch,
2660
+ via a `box-shadow`/`drop-shadow` split so the glow follows the icon's actual silhouette rather than an
2661
+ invisible transparent box) and `--lr-swatch-picker-shine-duration` (0s by default, static; set a real
2662
+ duration for a rhythmic brighten-and-settle pulse, disabled under `prefers-reduced-motion: reduce`).
2663
+ Together they cover a "shining" gemstone-style accent-theme picker without changing the default look
2664
+ for any existing consumer.
2665
+
2666
+ - 55140c3: `lr-table` gains heat-tint mode: a per-column `heatValue(row)` accessor drives a `color-mix()`-based
2667
+ cell background computed from a shared min/max scale across the whole grid (auto-derived from the
2668
+ data, or overridden via the new `heatTintScale` property), matching `lr-heatmap`'s own
2669
+ `--lr-heatmap-scale-lo`/`-hi` ramp-token convention via new `--lr-table-heat-tint-lo`/`-hi` custom
2670
+ properties. Previously a consumer needing a value-driven cell background had to hand-compute a color
2671
+ string themselves via the existing `cellStyle` escape hatch.
2672
+ - 6f7c938: `lr-table` gains `rowTotal`/`grandTotal`: a trailing column showing each row's total (`rowTotal`)
2673
+ and, when at least one column also defines `footer`, a grand-total cell at its bottom-right
2674
+ intersection (`grandTotal`). Both share the existing `footer(rows)` hook's "consumer computes/renders,
2675
+ table only positions" contract rather than assuming addition. Previously a consumer needing row/grand
2676
+ totals alongside `lr-table`'s existing per-column `footer` had to render them outside the table
2677
+ entirely, breaking column alignment.
2678
+ - 4cae327: Adds `<lr-task-list>`: a live, collapsible tracker for an agent's plan, embedded in the
2679
+ transcript. Renders ordered steps with per-step lifecycle status (`pending`/`running`/`success`/
2680
+ `error`) and one level of nested sub-steps; status changes are announced through an internal
2681
+ throttled live region. A dynamic `detail-<id>` slot per item accepts rich content such as a
2682
+ `<lr-tool-call-chip>`. Unlike `<lr-stepper>` (a single-selection navigation control),
2683
+ `<lr-task-list>` is a read-only status report — several steps may be `running` at once, and
2684
+ there is no selection.
2685
+ - bf223ca: Adds `<lr-terminal>`: a read-only, virtualized ANSI console for streamed agent/tool output — SGR
2686
+ color rendering (16 named colors, 256-color, truecolor), stick-to-bottom `follow` with a
2687
+ `lr-follow-change` event, `write()`/`content` streaming, `\r`/`\b`/`\t` cursor handling so progress
2688
+ bars render correctly, in-buffer `search()`/`searchNext()`/`searchPrevious()`/`clearSearch()`,
2689
+ `line-range` highlight/anchor support (`scrollToAnchor()`, `lr-highlight-activate`), and built-in
2690
+ copy/download affordances. Not a PTY — no stdin/keystroke handling or cursor-addressed full-screen
2691
+ apps.
2692
+ - 52a90e5: Adds `<lr-test-results>`: a pass/fail suite summary with visible (never color-only) per-status
2693
+ counts, `aria-pressed` status filter toggles, and failure rows that auto-expand by default and can
2694
+ host a slotted `detail-{testId}` diff/code block. Row state (expansion, filter) survives a streaming
2695
+ `suites` reassignment mid-run, and a run's completion is announced through an internal live region.
2696
+ - 967e785: Adds `<lr-thread-list>`: the conversation sidebar — a grouped ("Pinned / Today / Yesterday / Previous
2697
+ 7 days / …"), searchable list of chat sessions built on `lr-conversation-item` and virtualized via
2698
+ `lr-virtual-list`. Data mode (`threads` array) renders rows with optional pin/archive/delete row
2699
+ actions, all controlled events (`lr-thread-pin`/`-archive`/`-delete`/`-rename`) carrying the
2700
+ _requested_ new state — no CRUD or persistence of its own. Slotted mode (host-supplied
2701
+ `lr-conversation-item`s) skips grouping/virtualization/row-actions entirely, for a host that wants
2702
+ full control over a short, unconstrained list.
2703
+ - 9448c10: Add `lr-trace-tree`: a collapsible span hierarchy for one agent/LLM trace (Langfuse/LangSmith
2704
+ run-tree style) — kind icon, name, status, an inline duration bar on the shared trace time scale,
2705
+ and optional tokens/cost columns. Consumes a flat `LyraSpan[]` array (hierarchy derived from
2706
+ `parentId`); expand state survives a streaming reassignment of `spans`. The shared `LyraSpan` type
2707
+ (`components/trace-tree/span.ts`) is also consumed by the upcoming `lr-span-waterfall`, so the
2708
+ two components can render the same trace as two synchronized projections.
2709
+ - bef6b0d: Adds `<lr-transcript-feed>`: a data-driven live-captions surface for an in-progress voice session —
2710
+ `entries` in (`{ id, speaker?, text, interim?, timestamp? }[]`), reconciled keyed by `id` so a same-id
2711
+ interim-to-final upgrade moves the row into the announcing `role="log"` region without a duplicate
2712
+ announcement. Ships the shared stick-to-bottom "follow" contract (`follow`/`lr-follow-change`, the
2713
+ same vocabulary `lr-terminal` uses). No dependency, no STT/diarization built in — bring your own
2714
+ transcription source and stream entries in.
2715
+ - ec5fe96: Adds the `DocumentAnchorTarget` mixin (`internal/anchor-target.ts`) and its `LyraAnchorTarget`
2716
+ interface: the shared implementation of the anchor-target contract every anchor-capable lyra-ui
2717
+ viewer adopts — `highlights`/`activeHighlightId`/`anchor` properties, `scrollToAnchor()` with a
2718
+ generation-guarded retry-until-loaded loop and screen-reader announcements, and
2719
+ `lr-highlight-activate`/`lr-text-select`/`lr-anchor-result` event plumbing including
2720
+ selection->anchor emission. Internal module; no adopter yet in this release (`lr-pdf-viewer` adopts
2721
+ it next). No behavior change for any existing component.
2722
+ - 44b6de7: Adds the shared `LyraAnchor`/`LyraHighlight` grounding-bridge type module
2723
+ (`@aceshooting/lyra-ui/components/document-viewer/anchors.js`): a W3C Web-Annotation-inspired
2724
+ discriminated union (`page`, `text-quote`, `fragment`, `line-range`, `cell-range`, `cfi`,
2725
+ `time-range`, `region`, `node-path`) that every anchor-capable viewer and every knowledge-grounded
2726
+ citation surface will address a passage through. Pure types plus one constant; nothing to register,
2727
+ no runtime behavior change for existing components.
2728
+ - c644abd: Widens `DocumentFile` with optional `anchor`/`highlights`/`alt` fields and
2729
+ `DocumentRendererDefinition` with an optional `capabilities` declaration; `lr-document-viewer` gains
2730
+ matching `anchor`/`highlights`/`alt` properties, forwards them to the resolved renderer, and emits
2731
+ `lr-anchor-result { found }` once per applied anchor. Every addition is optional and every existing
2732
+ registration/usage is unaffected — this removes the previous limitation where even a renderer's own
2733
+ props (like pdf's `page`) couldn't be reached through the router.
2734
+ - 5f92994: Adds `internal/text-highlights.ts`: a highlight paint manager for HTML-flow document viewers, using
2735
+ the CSS Custom Highlight API when available and falling back to `<mark>`-wrapping otherwise, with a
2736
+ uniform `acquireHighlightHandle()` API that never requires callers to branch on browser support
2737
+ themselves. Internal module with no public tag and no adopter yet in this release; ships ahead of the
2738
+ markdown/html-viewer/docx-viewer highlight support that will consume it. No behavior change for any
2739
+ existing component.
2740
+ - b067b83: Adds `internal/text-quote.ts`: dependency-free `text-quote` anchor resolution (quote/prefix/suffix ->
2741
+ DOM `Range`, and the reverse — a selection `Range` -> a `text-quote` anchor with captured context).
2742
+ Internal module with no public tag; used by the `DocumentAnchorTarget` mixin's default selection
2743
+ handling and by `lr-pdf-viewer`'s anchor/highlight resolution. No behavior change for any existing
2744
+ component.
2745
+ - bc75a1f: Adds `<lr-usage-badge>`: a compact, static resource strip for one message or run — tokens in/out,
2746
+ cost, latency — with a hover/focus tooltip breakdown (full grouped figures, plus a computed Total
2747
+ tokens row when both counts are set). Purely formatting: it computes no counts, rates, or prices,
2748
+ and every segment is independently optional. Reuses `<lr-tool-call-chip>`'s hover/focus/Escape
2749
+ tooltip contract. Distinct from `<lr-context-meter>` (occupancy of a fixed capacity) and
2750
+ `<lr-generation-status>` (a live ticking readout with a Stop button) — this is the static spend
2751
+ record shown after a message or run completes.
2752
+ - f3c744b: `lr-virtual-list` gains a public `scrollToIndex(index, { align, behavior })` method: scrolls a
2753
+ specific row into view (`align: 'start' | 'end' | 'auto'`, reduced-motion-aware `behavior`) without
2754
+ the `aria-current`/"active row" side effect of the existing `active-id` property. In
2755
+ `row-height="auto"` mode, a far-off target's estimate-based offset is corrected with a single re-scroll
2756
+ once the row's real height is measured. Previously there was no way to programmatically scroll to a
2757
+ specific row at all except by driving `active-id`, which also marks that row as the current selection —
2758
+ a streaming transcript's own stick-to-bottom auto-scroll has nothing to do with "selection."
2759
+ - e24ae10: Adds `<lr-voice-picker>`: a TTS voice selector mirroring `lr-model-select`'s closed-dropdown/
2760
+ free-text-combobox dual mode and form-association, with a `catalog` entry shape carrying
2761
+ `language`/`description`/`previewUrl`, and an event-first preview affordance (`lr-preview-request`,
2762
+ cancelable) that plays through one internal `<audio>` when a `previewUrl` is present and the host
2763
+ doesn't take over. No TTS SDK, no catalog fetching, no selection persistence — those stay host
2764
+ concerns.
2765
+ - 37a89cb: Adds `lr-widget-renderer`'s internal type registry (`registerWidgetType()`,
2766
+ `getDefaultWidgetTypeRegistry()`) and its security-critical, DOM-free allowlist resolver
2767
+ (`resolveTree()`): unknown widget types and disallowed/mistyped props are skipped, never rendered;
2768
+ `forcedProps` always win; a child's `slot` outside its parent's allowlist renders unslotted; depth
2769
+ (32) and node-count (5000) caps are enforced. No public API surface change on its own — groundwork
2770
+ for the `<lr-widget-renderer>` element, landing in the same release.
2771
+ - bcd3c2b: Adds `<lr-widget-renderer>`: renders an agent-streamed declarative JSON widget tree through an
2772
+ allowlisted `type → lyra tag` registry (`card`/`badge`/`button`/`stat`/`result-card`/`result-field`/
2773
+ `markdown`/`image` built in, plus `row`/`col`/`text` structural built-ins) — unknown types and
2774
+ disallowed/mistyped props are silently skipped, never rendered, with a deduped dev-mode warning; a
2775
+ single bubbling `lr-widget-action` event surfaces actions; streamed updates reconcile keyed by
2776
+ `id` (or structural path), so a mapped widget's own internal state survives a re-resolve.
2777
+ `registerWidgetType()` extends the default registry app-side; a per-instance `registry` property
2778
+ fully overrides it. No `innerHTML`/`unsafeHTML` path exists anywhere in the implementation.
2779
+ - dc168c7: Adds `<lr-xml-viewer>`: a `DOMParser`-based collapsible XML tree view mirroring
2780
+ `lr-json-viewer`'s UX (`collapsed-depth`, `copyable`, structural-path expand state that
2781
+ survives a same-shape `xml` reassignment), with an imperative `search()`/`searchNext()`/
2782
+ `searchPrevious()`/`clearSearch()` API and `node-path` anchors (element indices plus an optional
2783
+ trailing `'@attrName'` segment for attribute-level targeting). Self-registers into the
2784
+ document-viewer registry for `application/xml`/`text/xml` and `.xml`/`.xsd`/`.xsl`/`.xslt`/`.rss`/
2785
+ `.atom` files. No XPath/XSLT evaluation, no editing, no schema validation.
2786
+
2787
+ ### Patch Changes
2788
+
2789
+ - 7bbd069: Internal only: adds three new `src/internal/` modules (`slugger.ts`, `cell-range.ts`,
2790
+ `viewer-search.ts`) and five new localization keys (`viewerSearchMatchCount(Plural)`,
2791
+ `viewerSearchNoMatches`, `viewerSearchActiveMatch`, `viewerHighlightLabel`) used by upcoming
2792
+ per-viewer search/anchor/highlight support. No consumer-visible behavior change on its own.
2793
+ - da8bbf0: Requires `@aceshooting/lyra-flags` `^1.4.0` (up from `^1.3.0`) as the optional flag-asset peer.
2794
+ 1.4.0 is a docs/metadata-only release of the flags package (no runtime change), so this is a
2795
+ range refresh, not a behavioral requirement bump.
2796
+ - 967e785: Fixes `<lr-virtual-list>`: a `groups`-supplied group marker no longer carries `role="heading"`
2797
+ `aria-level="2"`. Those markers render inside the scroll container's `role="list"`, and ARIA's `list`
2798
+ role only permits `listitem` as a direct owned child — a `heading` sibling was a critical
2799
+ `aria-required-children` violation for any consumer combining `groups` with an accessibility check
2800
+ (surfaced by `<lr-thread-list>`'s date-grouped rows). The marker is still rendered as visible,
2801
+ non-interactive text; it's just no longer exposed as a heading landmark.
2802
+
2803
+ ## 3.7.0
2804
+
2805
+ ### Minor Changes
2806
+
2807
+ - 05c9f9c: Add `appearance="link"` to `<lr-button>`: a true inline-link tier that renders as zero-chrome underlined text — no padding, border, border-radius, or `min-block-size` floor — colored from the same `--lr-button-accent` token `appearance="plain"` uses (so `variant` still selects the link color) and inheriting the surrounding font-size/weight so it flows within a sentence rather than as a button-shaped control. Previously the smallest `<lr-button>` was still a padded, rounded, 24px-tall pill with a (transparent-but-present) border and no `text-decoration`, so an inline text link had to be hand-rolled; `appearance="link"` now covers that case directly. The notable design choice: the link rules are declared after the per-`size` rules so `font: inherit` and the zero padding/border/min-height win over whatever `size` is set, and the shared `[part='base']:focus-visible` outline is deliberately left intact.
2808
+ - 2ed831d: `<lr-file-icon>` gains a `size` property (bytes, formatted via the same convention as `<lr-attachment-chip>`) shown alongside its label, and exposes the raw MIME type as a `title` tooltip.
2809
+ - a5482d8: Add `<lr-swatch-picker>`, a single-select picker over a small, fixed set of color swatches — the row-of-round-accent-color-buttons pattern apps hand-roll, generalized into a first-party component. It carries the WAI-ARIA APG `radiogroup` contract (`role="radiogroup"`/`role="radio"`, roving tabindex, automatic activation on click or arrow-key move, cyclic Arrow/Home/End navigation), takes an `options: { value; color; label }[]` array plus a controlled `value`, and emits `lr-change` (`detail: { value }`) only when the selection actually changes. It is distinct from `<lr-color-picker>`'s freeform native color input: this picks exactly one of N designer-chosen named colors.
2810
+
2811
+ Notable design choice: the selection ring uses a dedicated `--lr-swatch-picker-selected-color` token (defaulting to `--lr-color-brand`) so it retheme independently of the focus ring, mirroring `<lr-heatmap>`'s `--lr-heatmap-selected-color`; each swatch's fill comes from its option's `color`, applied through a per-swatch custom property so a consumer's `::part(swatch)` background rule can still override it.
2812
+
2813
+ ### Patch Changes
2814
+
2815
+ - f3a606f: Fix `<lr-file-icon>`'s format badge overflowing its fixed size for multi-word localized labels (e.g. "Word document") — long badge text now truncates with an ellipsis instead of spilling outside the badge.
2816
+ - 64e6cb6: Document `<lr-file-icon>`'s new `size` property and `size` csspart in `llms-full.txt`, and add the explicit-MIME-vs-filename-extension precedence test called for by the original feature request's acceptance criteria.
2817
+ - 0975bcd: Fix `<lr-map>` throwing an unhandled error when the underlying maplibre-gl `Map` emits an `'error'` event (e.g. a tile/style source request failing) with no listener attached — maplibre-gl's `Evented` base rethrows in that case. The error is now caught and logged via `console.error` instead of surfacing as an uncaught exception.
2818
+
2819
+ ## 3.6.0
2820
+
2821
+ ### Minor Changes
2822
+
2823
+ - 30db265: Nine new components:
2824
+
2825
+ - `lr-animated-image` — a still/animated-GIF-style image that pauses on `prefers-reduced-motion`
2826
+ and exposes a play/pause toggle.
2827
+ - `lr-animation` — declarative Web Animations API wrapper for a slotted target, with named
2828
+ timing presets, `prefers-reduced-motion` handling, and `lr-start`/`lr-finish`/`lr-cancel`
2829
+ events.
2830
+ - `lr-avatar-group` — a stacked, overlapping set of avatars with a "+N" overflow indicator.
2831
+ - `lr-include` — fetches and renders external HTML/Markdown/plain-text content client-side, with
2832
+ URL validation and DOMPurify sanitization.
2833
+ - `lr-known-date` — a form-associated day/month/year input for approximate or partial dates
2834
+ (e.g. a birth date where only the year is known).
2835
+ - `lr-lightbox` — a full-screen, modal, click-to-enlarge image viewer with prev/next navigation
2836
+ across an ordered set of images, built on the same shared overlay infrastructure as
2837
+ `lr-dialog`/`lr-command-palette`.
2838
+ - `lr-qr-code` — renders a QR code from text/URL data, via the optional `qrcode` peer dependency
2839
+ (same optional-peer pattern as the chart/map bundles).
2840
+ - `lr-random-content` — displays a randomly (or sequentially) chosen subset of its slotted
2841
+ children, with optional autoplay.
2842
+ - `lr-timeline`/`lr-timeline-item` — a vertical event timeline with per-item status/icon
2843
+ markers.
2844
+ - `lr-tour` — a guided, multi-step product-tour overlay that highlights target elements in
2845
+ sequence.
2846
+
2847
+ ### Patch Changes
2848
+
2849
+ - e1aca7e: Shared-infrastructure hardening pass following a full-library audit:
2850
+
2851
+ - `lr-contact-viewer` and `lr-email-viewer` now expose a proper localized `aria-label` on their
2852
+ root surface (previously had no naming mechanism at all); `lr-calendar-viewer` gets the same
2853
+ fallback chain's final localized tier.
2854
+ - `lr-stat`'s trend announcement now interpolates the percentage into one localized template
2855
+ instead of concatenating separately-localized fragments (word order safe for non-English locales).
2856
+ - Fixed a real bug in `lr-model-settings-panel`'s `decimalPlaces` helper that returned `0` instead
2857
+ of the correct precision for exponential-notation step values (e.g. `1e-7`); it now shares the
2858
+ same exponential-aware implementation as `lr-slider`/`lr-time-range` via a new
2859
+ `src/internal/numbers.ts` export instead of a diverging local copy.
2860
+ - Deduplicated five other byte-identical/near-identical helpers that had drifted into 2-5 separate
2861
+ component files each (`prefersReducedMotion`, canvas-context memoization, swatch-color
2862
+ sanitization, slotted-content detection, and a title-attribute-stripping mixin) into single
2863
+ `src/internal/` implementations.
2864
+ - Removed an unused, never-adopted RTL helper (`rtlAwareSide`/`PhysicalSide`) from
2865
+ `src/internal/rtl.ts`.
2866
+ - Added missing accessibility test coverage for `lr-icon-button` and the standalone `lr-option`
2867
+ element (previously the only two custom elements in the library with no axe check).
2868
+
2869
+ ## 3.5.0
2870
+
2871
+ ### Minor Changes
2872
+
2873
+ - 681ed1f: Broad component hardening pass across ~50 components:
2874
+
2875
+ - `lr-command-palette` now uses the shared overlay infrastructure (`lr-dialog`'s
2876
+ focus-trap/Escape/backdrop/scroll-lock manager) instead of a bespoke implementation, adds
2877
+ `aria-activedescendant` tracking, and keeps the highlighted row scrolled into view.
2878
+ - `lr-table` forwards `spellcheck`/`autocapitalize`/`autocorrect` to its filter input and inline
2879
+ text-cell editor, matching the string-aware `spellcheck` converter already used by
2880
+ `lr-textarea`/`lr-model-select`.
2881
+ - `lr-token-input` and `lr-code-editor` fix `label`/`hint`/`error` slot-vs-attribute detection
2882
+ (a `[part]:empty` selector never matches since the part always contains a `<slot>`), and
2883
+ `lr-token-input` adopts the `effectiveDisabled`/`_fieldsetDisabled` pattern so a `<fieldset
2884
+ disabled>` ancestor no longer permanently overwrites its own `disabled` property.
2885
+ - `lr-calendar`: month grid gets proper `role="grid"`/`role="row"`/`role="gridcell"` semantics,
2886
+ per-day `aria-label`, a sanitized event-color style (rejects `url(...)` and anything else that
2887
+ isn't real CSS color syntax), and RTL-aware nav chevrons; `firstDayOfWeek` tolerates out-of-range
2888
+ input instead of producing `Invalid Date`.
2889
+ - `lr-icon` clones custom slotted SVG content into the component's own `<svg>` so slotted
2890
+ path/circle/group children paint reliably in Chromium.
2891
+ - `lr-document-preview` simplifies its abortable-fetch generation tracking onto the shared
2892
+ `beginAbortableLoad` helper.
2893
+ - `lr-app-rail-item`'s tooltip text now ignores text incidentally living in the decorative `icon`
2894
+ slot, mirroring `lr-chip`'s `labelText` getter.
2895
+ - Smaller accessibility/consistency fixes across app-rail, attachment-chip, breadcrumb, callout,
2896
+ chart/histogram, checkbox-group, data-grid, empty, format-\*, heatmap, html-viewer,
2897
+ image-comparer, intersection/mutation/resize-observer, map, model-select, pdf-viewer,
2898
+ phone-input, progress, radio/radio-group, responsive-panel, scroller, segmented, sparkline,
2899
+ split, stat, stepper, streaming-text, switch, tool-param-form, tool-select-dialog, widget, and
2900
+ zoomable-frame, plus a new standalone `breadcrumb-item.styles.ts` module and expanded test
2901
+ coverage throughout.
2902
+
2903
+ ## 3.4.0
2904
+
2905
+ ### Minor Changes
2906
+
2907
+ - d0ee919: Add command-palette, checkbox-group, token-input, icon/icon-button, code-editor, data-grid, and
2908
+ calendar components. Harden file-input with clipboard paste, native directory selection, and
2909
+ dropped-folder rejection reporting.
2910
+ - 1293f48: Hardening pass across ~70 components: document the button/spinner interaction custom-property APIs
2911
+ (`--lr-button-width`, hover-brightness, active-scale, spinner-duration) and add missing cssparts;
2912
+ `lr-breadcrumb` now reads its accessible-name override from the standard `aria-label` attribute
2913
+ (was `accessible-label`); phone-input preserves the caret through adapter reformats and ships a
2914
+ libphonenumber-js-backed adapter path with a clearer incomplete-number message; prune unused
2915
+ localization keys and size/line-height tokens; broaden test coverage across the library.
2916
+
2917
+ ## Unreleased
2918
+
2919
+ ### Minor Changes
2920
+
2921
+ - Added `<lr-command-palette>` with searchable command registration, groups, keyboard navigation,
2922
+ Escape dismissal, and a configurable `mod+k` shortcut.
2923
+ - Added `<lr-checkbox-group>` and `<lr-token-input>` as form-associated composite controls with
2924
+ array values, native reset/validity behavior, localized chrome, and accessible focus/editing APIs.
2925
+ - Added `<lr-icon>` and `<lr-icon-button>` as dependency-free SVG and icon-only action primitives.
2926
+ - Added `<lr-code-editor>` with line numbers, tab insertion, native textarea selection APIs, and
2927
+ editing-assistance passthrough.
2928
+ - Added `<lr-data-grid>` with sortable headers, roving cell focus, row selection events, loading/
2929
+ empty states, and responsive overflow.
2930
+ - Added `<lr-calendar>` with responsive month and agenda views, event markers, date navigation,
2931
+ RTL-aware keyboard navigation, and date/event selection events.
2932
+ - Hardened `<lr-file-input>` with clipboard paste support, optional native directory selection, and
2933
+ explicit dropped-folder rejection reporting.
2934
+ - Updated the component catalog, consumer API reference, custom-elements manifest, stories, and
2935
+ accessibility/behavior coverage for the new public surface.
2936
+
2937
+ ## 3.3.0
2938
+
2939
+ ### Minor Changes
2940
+
2941
+ - 7e7cc44: Harden every remote-resource viewer against oversized, cancelled, and failed loads, and close a set of localization gaps.
2942
+
2943
+ **Resource limits.** A new internal resource loader caps any remote resource a viewer fetches at 25 MB before handing it to a parser, enforced by streaming the response so the cap holds even when the server omits `Content-Length`. Parsed tabular data is additionally capped at 10,000 rows and 1,000 columns before it is retained or rendered. Exceeding either limit now surfaces the localized `documentPreviewResourceTooLarge` message instead of attempting the parse. This is a behavior change for consumers previewing documents above those thresholds — they will now see a size error where the viewer previously tried (and typically hung or crashed) on them.
2944
+
2945
+ **Cancellable loads.** `LyraElement` gained internal `beginAbortableLoad()` and `scheduleAfterUpdate()` helpers. In-flight fetches are now aborted when the element disconnects or its `src` changes again, and loads are coalesced to one per update rather than firing from `willUpdate`. This fixes stale responses racing a newer `src` and work continuing after an element is removed from the DOM. A `src` assigned while an element is detached is held and replayed when it reconnects, rather than being dropped.
2946
+
2947
+ **Error messages no longer leak internals.** Viewers previously rendered raw `error.message` text (fetch/parser internals, URLs) directly into the UI on failure. They now render the localized `documentPreviewFailedToLoad` message, with the underlying error still available to consumers via the `lr-render-error` event.
2948
+
2949
+ Affected viewers: `lr-archive-viewer`, `lr-calendar-viewer`, `lr-contact-viewer`, `lr-csv-viewer`, `lr-dataset-viewer`, `lr-docx-viewer`, `lr-document-preview`, `lr-ebook-viewer`, `lr-email-viewer`, `lr-html-viewer`, `lr-pdf-viewer`, `lr-pptx-viewer`, `lr-spreadsheet-viewer`, `lr-svg-viewer`.
2950
+
2951
+ **Localization fixes.**
2952
+
2953
+ - Form-associated components rendered the required-field validation message as a hardcoded English string (`Please fill out this field.`). It now resolves through the `fieldRequired` message key, so `registerLyraLocale()` and per-element `strings` overrides apply. Note that this also changes the default English text to `This field is required.` — if you assert on `validationMessage`, update the expected string.
2954
+ - Removed a duplicate `hidePassword` member from the `LyraMessageKey` union. The key itself is unchanged and still used by `lr-input`; only the redundant second declaration is gone.
2955
+
2956
+ **Component coverage contract.** A new `check-component-coverage.mjs` gate runs as part of `contract-policy` (and therefore `lint`), requiring every public tag in the manifest to be exercised by a story and a behavior test, and every component family to carry an accessibility assertion. Stories and tests were added across the library to satisfy it, and `test:coverage` now runs the full test suite rather than five hardcoded files. No public API change.
2957
+
2958
+ ## 3.2.0
2959
+
2960
+ ### Minor Changes
2961
+
2962
+ - 62c6b05: `lr-attachment-chip` gains a preview action: a new `previewSrc` property (used when `file` is
2963
+ unset; a real `File` takes precedence via a temporary blob URL) and `previewable` boolean (default
2964
+ `true`) show a new `preview-button` part whenever a file or preview source is available, emitting
2965
+ `lr-preview` (`detail: { id, name, mimeType, src }`) to open `<lr-document-viewer>` with the
2966
+ same effective MIME type. `lr-document-viewer` gains a matching `download-link` slot and
2967
+ `lr-download` event for a safe native download action. Both properties/events are additive and
2968
+ default off/no-op, so existing usages are unaffected.
2969
+
2970
+ ## 3.1.0
2971
+
2972
+ ### Minor Changes
2973
+
2974
+ - de80dc5: Adds `<lr-archive-viewer>` for listing names and human-readable sizes inside `.zip` archives via
2975
+ the optional `jszip` peer. It registers standard ZIP MIME types and a `.zip` filename fallback with
2976
+ `<lr-document-viewer>`; other archive formats remain on the generic download fallback.
2977
+ - de80dc5: Adds the optional `line-numbers` display to `<lr-code-block>` and `<lr-code-block-core>`.
2978
+ - 53c7c13: Add sanitized SVG and HTML viewers, plus PapaParse-backed dataset and vCard contact viewers to the document renderer registry.
2979
+ - c6dd26c: Adds `<lr-document-viewer>`, a dialog-hosted, format-dispatching document viewer, plus a
2980
+ `registerDocumentRenderer()` registry for plugging in per-format renderers. Files without a
2981
+ registered renderer fall back to the existing `<lr-document-preview>` component.
2982
+ - d992ee7: Adds `<lr-docx-viewer>`, rendering `.docx` Word documents as sanitized semantic HTML through the
2983
+ optional `mammoth` and `dompurify` peers. It registers the official WordprocessingML MIME type and
2984
+ falls back to matching `.docx` filenames.
2985
+ - de80dc5: Adds `<lr-ebook-viewer>` using the optional `epubjs` peer and registers EPUB files with the
2986
+ document-viewer registry.
2987
+ - 49f7b87: Adds `<lr-email-viewer>` for sanitized `.eml` messages via the optional `postal-mime` and
2988
+ `dompurify` peers, plus `<lr-calendar-viewer>` for `.ics` event lists via optional `ical.js`.
2989
+ Both viewers register their standard MIME types and filename-extension fallbacks with
2990
+ `<lr-document-viewer>`.
2991
+ - de80dc5: Adds `getFileTypeMetadata()`, `registerFileTypeMetadata()`, and `<lr-file-icon>` for localized,
2992
+ tokenized MIME/filename format presentation.
2993
+ - 68bb5e3: Adds `<lr-pdf-viewer>`, a PDF renderer built on optional `pdfjs-dist`, with pagination, zoom, selectable text, and virtualized page rendering.
2994
+ - de80dc5: Adds `<lr-pptx-viewer>` using the optional `@aiden0z/pptx-renderer` peer for best-effort client-side
2995
+ PPTX rendering with a persistent fidelity notice.
2996
+ - 0b6f412: Add SheetJS-backed spreadsheet and PapaParse-backed CSV document viewers with virtualized rows.
2997
+
2998
+ ## 3.0.0
2999
+
3000
+ ### Major Changes
3001
+
3002
+ - a712749: **Breaking:** the outer, externally-overridable tier of the design-token chain no longer lives in
3003
+ the previous external theme-input namespace — it moved to lyra's own `--lr-theme-*` namespace
3004
+ (for example, the brand fill input now uses `--lr-theme-color-brand-fill-loud`). Any consumer
3005
+ retheming components through the old external custom properties must rename those
3006
+ properties to `--lr-theme-*`; the two-tier override mechanism itself (set one property at any
3007
+ ancestor to retheme every component) is unchanged. This removes lyra-ui's remaining live runtime
3008
+ CSS coupling to Web Awesome.
3009
+
3010
+ ### Minor Changes
3011
+
3012
+ - 66c8819: Adds an independent `--lr-theme-*` shared token layer, aligns `<lr-button>`'s medium size with
3013
+ the standard Lyra font scale, exposes its host-width and size contracts, and adds opt-in native
3014
+ per-cell semantics to `<lr-heatmap>` through `accessible-cells`.
3015
+
3016
+ ### Patch Changes
3017
+
3018
+ - 11e6a03: `lr-details`/`lr-accordion-item` no longer render the localized "Details" fallback text alongside rich content slotted into `summary` when the plain-string `summary` prop is left unset. The fallback previously always rendered whenever `summary` was empty, regardless of whether a `slot="summary"` child was present — visible only when a consumer needed markup (an icon, multiple spans) in the summary rather than a plain string.
3019
+ - 581f5f3: `installHappyDomFormAssociatedShims()` no longer throws a `ReferenceError` when `HTMLElement` isn't a global at all — e.g. a plain Node Vitest environment sharing one `setupFiles` entry with happy-dom/jsdom test files. It previously read `HTMLElement.prototype` unconditionally, contradicting its own documented "safe to call unconditionally from a shared setup file used across multiple test environments" contract.
3020
+ - b5de65c: `lr-popover`/`lr-dropdown`/`lr-tooltip`'s `[part="popup"]` is now `position: fixed` from the start instead of only once the popup is first opened and JS positions it. Previously, while closed, the popup stayed `position: static` sized to its full slotted content, inflating the component's own inline-block host box to match -- an invisible-but-still-hit-testable area that could sit on top of unrelated page content and intercept pointer events until the trigger was first clicked.
3021
+
3022
+ ## 2.13.0
3023
+
3024
+ ### Minor Changes
3025
+
3026
+ - 80cb577: `lr-table` gains opt-in row selection (`selectionMode: 'single' | 'multiple'`, `selectedKeys`,
3027
+ `lr-selection-change`), a built-in filter field (`filterable`, `filterText`, `filter`,
3028
+ `lr-filter-change`), controlled pagination through `<lr-pagination>` (`pageSize`, `page`,
3029
+ `totalItems`, `paginationMode`, `lr-page-change`), a `loading` state with an indeterminate
3030
+ spinner, per-column double-click inline editing (`TableColumn.editable`/`editValue`/`editType`,
3031
+ `lr-cell-edit`), and row grouping (`groupBy`, `groupLabel`). All new properties default to
3032
+ today's exact behavior when left unset.
3033
+ - 5628327: `lr-input` and `lr-textarea` now also emit native-style `input`/`change` events (composed,
3034
+ matching the native element's own timing) alongside the existing `lr-input`/`lr-change`
3035
+ aliases, so consumers migrating from a native `<input>`/`<textarea>` don't need to rename their
3036
+ listeners. Both components also forward `spellcheck`, `autocapitalize`, `autocorrect`,
3037
+ `inputmode`, and `enterkeyhint` to their internal native control.
3038
+ - d009cd8: Adds a new "Web Awesome parity primitives" family: `lr-badge`/`lr-tag`, `lr-callout`,
3039
+ `lr-divider`, `lr-breadcrumb`/`lr-breadcrumb-item`, `lr-details`/`lr-accordion`/
3040
+ `lr-accordion-item`, `lr-button-group`, `lr-carousel`/`lr-carousel-item`,
3041
+ `lr-color-picker`, `lr-drawer`, `lr-popover`/`lr-tooltip`/`lr-dropdown`/
3042
+ `lr-dropdown-item`, `lr-radio`/`lr-radio-group`, `lr-rating`, `lr-spinner`,
3043
+ `lr-progress-bar`/`lr-progress-ring`, `lr-format-number`/`lr-format-date`/
3044
+ `lr-format-bytes`/`lr-relative-time`, `lr-image-comparer`, `lr-zoomable-frame`,
3045
+ `lr-scroller`, and headless `lr-intersection-observer`/`lr-mutation-observer`/
3046
+ `lr-resize-observer` wrappers. `lr-number-input` and `lr-time-input` join `lr-input` as
3047
+ sibling native-input-type primitives.
3048
+
3049
+ These close out the remaining free-tier Web Awesome components with no prior lyra-ui equivalent —
3050
+ 133 tags total, up from 97.
3051
+
3052
+ ### Patch Changes
3053
+
3054
+ - 5766257: `installHappyDomFormAssociatedShims()`'s stub `ElementInternals` now implements `setValidity()` as a no-op. `AnchoredValidityController` (used by every form-associated component) calls `internals.setValidity()` on every update, not just at construction, so a consumer's happy-dom test suite installing the shim would throw the moment any shimmed component's value changed after mount.
3055
+
3056
+ ## 2.12.0
3057
+
3058
+ ### Minor Changes
3059
+
3060
+ - 42036af: `lr-table` gains expandable rows: a table-level `expandedContent?: (row) => unknown` renders a
3061
+ full-width panel beneath any row whose key is in the new consumer-owned `expandedKeys: Set<string |
3062
+ number>` property, toggled via a built-in leading chevron cell and the new `lr-row-expand-toggle`
3063
+ event (`detail: { row, key }`). An optional `canExpand?: (row) => boolean` gates which rows get an
3064
+ interactive toggle at all. All three properties are additive and default to a no-op, so existing
3065
+ tables are unaffected.
3066
+
3067
+ - d612939: Make card headers wrap with their actions in narrow allocations, expose citation previews through
3068
+ a stable tooltip relationship, and localize the complete citation status announcement.
3069
+
3070
+ Add reactive `accessibleLabel` overrides to both code-block variants and media cards so host
3071
+ `aria-label` values reach the actionable or semantic element inside shadow DOM. Media-card's
3072
+ unnamed actions now use complete, per-kind localized messages.
3073
+
3074
+ Keep markdown within logical narrow allocations and make its `streaming` state hold `aria-busy`
3075
+ until the final content update.
3076
+
3077
+ - 159f3c9: `lr-file-input` now forwards host accessible names to its dropzone and file input, exposes an
3078
+ imperative focus target, reports explicit enabled/disabled ARIA state, and announces accepted and
3079
+ rejected file counts with correct singular and plural messages.
3080
+
3081
+ `lr-export-button` now forwards host accessible names to its trigger, exposes native focus and
3082
+ blur methods, and keeps long format menus within the positioned overlay's available space.
3083
+
3084
+ `lr-document-preview` now supports explicit image alternative text (including `alt=""` for
3085
+ decorative previews), aborts superseded text fetches, and documents its sizing, font, and spinner
3086
+ motion custom properties.
3087
+
3088
+ - 3da4f80: `lr-button` ships a default `:hover`/`:active` pointer-interaction treatment on `[part='base']`
3089
+ (`filter: brightness(--lr-button-hover-brightness)` on hover, `transform: scale(--lr-button-active-scale)`
3090
+ on active, both disabled under `prefers-reduced-motion`) -- previously it had zero hover/active CSS,
3091
+ so a mechanical `wa-button` -> `lr-button` rename silently dropped all pointer-interaction feedback.
3092
+
3093
+ `lr-button` is now form-associated (`static formAssociated = true` + `attachInternals()`), so it
3094
+ participates in an ancestor `<form>.elements` the same way `wa-button` does -- a sibling text field's
3095
+ own Enter-to-submit lookup (which scans `form.elements` for a `type === 'submit'` control) now finds
3096
+ it, instead of silently failing to submit the form.
3097
+
3098
+ `lr-button` gains an `appearance="accent"` value -- a loud, high-contrast filled tier equivalent to
3099
+ `wa-button`'s own runtime-default appearance, including for `variant="neutral"` (`'filled'` reads the
3100
+ ambient surface color there, matching `wa-button`'s `appearance="filled"`; `'accent'` reads a solid
3101
+ neutral fill, matching `wa-button`'s own unset-appearance default). New `--lr-button-accent-fill`/
3102
+ `-accent-on-fill` custom properties back it.
3103
+
3104
+ `lr-heatmap` gains a `monthLabelText?: (jsMonth: number, year: number) => string | undefined`
3105
+ property, the month-axis analogue of the existing `weekdayLabelText` -- lets a consumer's calendar-mode
3106
+ month labels track the same locale signal (e.g. an app's own i18n store) as every other localizable
3107
+ string on the component, instead of always following `toLocaleString(undefined, ...)`'s browser/OS-
3108
+ language default. Unset (the default) reproduces today's exact locale-derived output.
3109
+
3110
+ - 8a1777b: `lr-skeleton` adds an `announce` switch so grouped or decorative placeholders can avoid
3111
+ duplicating live-region announcements. Pulse and sheen effects now use the shared
3112
+ `--lr-transition-ambient` motion token and remain disabled by the reduced-motion branch.
3113
+ - 8e8a77f: `lr-tool-result-dialog` now forwards host `aria-label` to the internal dialog, exports its
3114
+ typed event map, localizes complete duration messages, omits non-finite durations, exposes its
3115
+ running-spin timing, and wraps footer actions in narrow layouts.
3116
+
3117
+ ### Patch Changes
3118
+
3119
+ - 6ba4d1f: Localize generation metrics, graph position announcements, attachment upload context, and duration templates. Mirror JSON viewer disclosure chevrons in RTL and give map content a named semantic group with correct host-label precedence.
3120
+ - b67a25e: Forward host accessible names to the semantic canvas or SVG in the chart, histogram, box-plot,
3121
+ and lite-chart families. Localize numeric summaries, mirror chart axes in RTL, refresh derived
3122
+ histogram data, improve BoxPlot theming and reduced-motion behavior, and support narrow allocations
3123
+ with long content across charts and context meters.
3124
+ - 5dd8066: `lr-chat-message` now formats its default timestamp with the component's effective locale,
3125
+ uses the shared ambient-motion token for streaming feedback, and wraps crowded footer controls
3126
+ in narrow allocations.
3127
+ - e95f942: Adds a complete interpolated localization message for citation status announcements so
3128
+ translations can reorder the citation index and status naturally.
3129
+ - 303e701: `lr-heatmap` now localizes its built-in value label and formats legend, accessible-range, cell,
3130
+ and calendar-date values with the component's effective locale. Explicit `value-label` text remains
3131
+ verbatim.
3132
+ - 87eb96a: `lr-heatmap` now mirrors its low-to-high legend ramp in right-to-left layouts, including
3133
+ consumer-provided multi-stop palettes.
3134
+ - 134dba0: Adds a complete interpolated localization message for lite-chart mark announcements so
3135
+ translations can reorder series, label, value, and position naturally.
3136
+ - 0260f9b: Harden `lr-app-rail`, `lr-attachment-chip`, `lr-avatar`, and `lr-chip-group`: respect the
3137
+ configured element prefix, preserve localized attachment-message word order, support image `File`
3138
+ objects in thumbnail-only mode, make spinner timing themeable, retry replacement avatar images,
3139
+ forward avatar accessible-name overrides, and collapse slot-forwarded overflow chips correctly.
3140
+ - 9033a43: Forward host naming and native textarea editing APIs through `lr-chat-composer`, complete
3141
+ `lr-phone-input` selection and range-editing methods, and expose observable focus/blur contracts
3142
+ for pagination, playback, and select controls.
3143
+ - acbbf00: Logical safe-area tokens now mirror the underlying physical browser insets in right-to-left
3144
+ layouts, keeping dialogs, toasts, widgets, and tool overlays clear of notches on the correct side.
3145
+ - 1f93e0c: `lr-sparkline` now applies its generated or consumer-provided accessible name to the internal
3146
+ SVG that owns the image role. Generated value summaries also respect the component's effective
3147
+ locale and per-instance message overrides.
3148
+ - 18003e2: `lr-tool-call-chip` now interpolates duration values through localized message templates and
3149
+ exposes coherent motion controls for its running spin and pending pulse. Its event map is also
3150
+ exported for typed listeners.
3151
+ - 140f9ea: Align `lr-checkbox` with the native checkbox keyboard, focus, reset, ARIA-state, and `input`/`change` event contracts while retaining `lr-change` as a compatibility alias.
3152
+ - d099ea7: Complete the combobox's native editing surface and clearable compatibility, align conversation-item event and story semantics, add accessible disabled and timing controls to copy-button, and localize and theme flag presentation.
3153
+
3154
+ ## 2.11.0
3155
+
3156
+ ### Minor Changes
3157
+
3158
+ - c0648ec: `lr-input` gains a `size: 'xs' | 's' | 'm' | 'l' | 'xl' = 'm'` property (reflected), the same scale
3159
+ `lr-select`/`lr-combobox` already use — `--lr-input-padding-block`/`-padding-inline`/
3160
+ `-font-size` swap per size, the same pattern as `lr-select`'s own size tokens. Unset (the default,
3161
+ `'m'`) reproduces today's exact sizing.
3162
+
3163
+ ## 2.10.0
3164
+
3165
+ ### Minor Changes
3166
+
3167
+ - f506542: `lr-heatmap` gains a `selectedCell` property (`{ row, col }` in matrix mode, `{ date }` in
3168
+ calendar mode) — a controlled, consumer-owned marker (mirroring `lr-lite-chart`'s
3169
+ `selectedIndex`) that draws a persistent canvas ring independent of keyboard focus, appends a
3170
+ "Selected: ..." description to the host's own `aria-label` so it stays discoverable after focus
3171
+ moves elsewhere, and appends a "(selected)" suffix to the keyboard live-region announcement. Unset
3172
+ (the default, `null`) reproduces today's exact output.
3173
+ - 6f6d758: Add `lr-button`, a generic action-button primitive (`variant`/`appearance`/`size`/`loading`/`disabled`/`type`, default + `start`/`end` slots) -- the `lr-*` equivalent of a plain `wa-button`.
3174
+ - 5eda04d: Add `lr-input`, a single-line plain-text input primitive (`type="text"`/`"password"`/`"email"`/`"number"`, label/hint/error chrome, form-associated validation, a built-in password-visibility toggle) -- the `lr-*` equivalent of a plain `wa-input`.
3175
+ - 7c95e95: `lr-tool-result-view` gains a real `fallback="text"` mode (previously accepted as an attribute
3176
+ value but silently treated identically to `"json"`): a string `result` renders as preformatted text
3177
+ instead of being forced through `<lr-json-viewer>`'s tree view, falling back to the `"json"`
3178
+ behavior when `result` isn't a string. A new `copyable` property adds a copy-to-clipboard affordance
3179
+ to either fallback kind. Additive — unset, both fallback kinds and every existing consumer render
3180
+ byte-identical to before.
3181
+
3182
+ ### Patch Changes
3183
+
3184
+ - 83fe6ba: Fix `lr-heatmap`'s `llms-full.txt` section, which was missing four real, already-shipped members
3185
+ (`cellInteractive`, `weekdayLabelText`, `colorSteps`, `refreshTheme`), and add a matching
3186
+ `focus()`/`blur()` mention to `lr-button`'s own section. Add a `pnpm run llms-freshness` lint gate
3187
+ (wired into `contract-policy`, so it runs in `lint`/CI/`publish.sh`) that fails the build if any
3188
+ custom element's public property isn't mentioned anywhere in its own `llms-full.txt` section, so
3189
+ this can't silently drift again. A small baseline of ~20 pre-existing drift items on unrelated
3190
+ components (chart family, dialog, menu, split, tree-node, widget, etc.), discovered while building
3191
+ this check, is exempted for now via a documented allowlist in the script — out of scope for this
3192
+ change, left for a follow-up cleanup.
3193
+
3194
+ ## 2.9.0
3195
+
3196
+ ### Minor Changes
3197
+
3198
+ - b4a6f5b: `lr-heatmap`'s color ramp now preserves a translucent `rgba()`/`hsla()`/hex-with-alpha color instead of silently resolving it to fully opaque. `resolveRgb()`/`hexToRgb()` return an `[r, g, b, a]` quadruple (previously `[r, g, b]`), and the ramp emits `rgba(...)` whenever an endpoint is translucent — unchanged `rgb(...)` output for opaque colors, so an existing consumer using only opaque `--lr-heatmap-scale-lo`/`-hi` values sees no difference. Lets a consumer key a ramp endpoint off a themed semi-transparent surface token (e.g. a "quiet baseline" tint) and get the intended translucent cell color instead of a stark opaque one.
3199
+
3200
+ ## 2.8.0
3201
+
3202
+ ### Minor Changes
3203
+
3204
+ - 0331bbf: `lr-table` gains a public, reflected `showAllColumns` property/`show-all-columns` attribute for its reveal-hidden-columns state, plus a `lr-columns-revealed` event fired when `[part='reveal-columns-button']` toggles it. Consumers can now read the current reveal state back (to persist it) and set an initial one (to restore a previously-persisted preference), mirroring the read-back/set-forward contract `sortKey`/`sortDir` already support. The button still toggles the state itself by default, so existing usage is unaffected.
3205
+
3206
+ ## 2.7.0
3207
+
3208
+ ### Minor Changes
3209
+
3210
+ - af61856: `lr-app-rail`'s navigation landmark (and its `role="dialog"` while the mobile overlay is open) now honors a host-level `aria-label` attribute, taking precedence over the `label` property and its localized `"Navigation"` default, mirroring `<lr-date-input>`'s `accessibleLabel` pattern. Previously a host-level `aria-label` on `<lr-app-rail>` had no effect on the accessible name computed inside its shadow DOM.
3211
+ - 4ee4e76: `lr-chat-composer` forwards `spellcheck`/`autocapitalize`/`autocorrect` onto its internal `<textarea>` and re-dispatches bubbling, composed `blur`/`focus` events so a host-level listener can observe focus changes across the shadow boundary.
3212
+ - 06e5fda: `lr-chip` gains a `--lr-chip-pressed-bg` custom property (falls back to `--lr-chip-bg`) so the pressed/selected background can be set independent of the resting background. A toggleable-but-unpressed chip now announces `aria-pressed="false"` instead of omitting the attribute entirely, matching the ARIA Authoring Practices convention for toggle buttons.
3213
+ - a158b6b: `lr-combobox` gains a `size` property (`'xs'|'s'|'m'|'l'|'xl'`, default `'m'`) mirroring `lr-select`'s existing scale, including matched sizing for the "+N" overflow tag so it stays visually consistent with the trigger at every size. Async `ComboboxSourceRow` results can now carry a decorative `icon`, trailing `badge`, richer `accessibleLabel`, and opaque `data`; the read-only `selectedRows` getter retains the structured rows and payloads for the current selection. The new visuals are exposed through `option-icon` and `option-badge` CSS parts.
3214
+ - 480d9e2: `lr-conversation-item` forwards `spellcheck`/`autocapitalize`/`autocorrect` onto its in-place rename `<input>` and re-dispatches bubbling, composed `blur`/`focus` events so a host-level listener can observe focus changes across the shadow boundary while a rename is in progress.
3215
+ - 74dcaa7: `lr-date-input` forwards `spellcheck`/`autocapitalize`/`autocorrect` onto its internal `<input>` and re-dispatches bubbling, composed `blur`/`focus` events so a host-level listener can observe focus changes across the shadow boundary.
3216
+ - 22f206c: `lr-dialog` now lets a host-level `aria-label` attribute win over its computed accessible name (a slotted heading, `heading`, or `label`), matching `<lr-date-input>`'s `accessibleLabel` pattern. Previously a consumer setting `aria-label` directly on `<lr-dialog>` was silently ignored in favor of the bespoke `label`/`heading` props. Additive — left unset, today's existing three-tier fallback is unchanged.
3217
+ - 80b22ba: `lr-empty`'s `compact` mode gains a `--lr-empty-compact-align` custom property (defaulting to today's exact `flex-start`/`start` pair) so a consumer can combine `compact`'s denser padding with a centered heading/description layout by setting it to `center`.
3218
+ - 0f21c57: `lr-export-button` accepts custom format descriptors with consumer-supplied labels, descriptions, and extension metadata. Custom formats emit `lr-export` for application handling without bundling an encoder, while a new controlled `loading` state exposes busy semantics and prevents duplicate activation during async exports.
3219
+ - 3ac5e4d: `lr-gauge` gains a full-circle `type="ring"` presentation and a `--lr-gauge-fill` custom property for setting the fill stroke per instance across radial, ring, and linear gauges.
3220
+ - f6b2aa5: `lr-graph` nodes gain independent accessible labels and SVG tooltip descriptions. Links gain stable ids, spoken-name/tooltip relationship-label fallbacks (not visible edge text), tooltip descriptions, directed arrowheads, per-link colors, and dash patterns; `lr-link-click` now includes the optional link id and the marker is exposed through the `arrowhead` CSS part. A host `aria-label` is forwarded to the internal semantic SVG.
3221
+ - efc1182: `lr-map` now forwards a host-level `aria-label` attribute onto `[part="base"]`'s accessible name as a fallback when `label` is left unset, matching `lr-slider`/`lr-checkbox`/`lr-switch` — previously a host `aria-label` was silently dropped in favor of the localized `'map'` default.
3222
+ - 085d173: `lr-mention-popover` now honors a host-level `aria-label` attribute as the accessible name for its internal `role="listbox"` popup, taking priority over the `label` property and its localized default. Previously the popup's name came only from `label`/`localize()`, so a plain `aria-label` set on `<lr-mention-popover>` itself was silently ignored — matches the same fallback already used by `lr-combobox`/`lr-table`.
3223
+ - 3b59e94: `lr-menu`'s `role="menu"` popup now honors a host-level `aria-label` attribute over both the `label` prop and its localized default, matching `lr-select`/`lr-model-select`'s established `this.getAttribute('aria-label') || <computed default>` precedence. Additive — `aria-label` is unset by default, so every existing consumer (whether relying on the default `"Menu"` text or an explicit `label` prop) renders byte-identical to before.
3224
+ - 653173d: `lr-model-select` gains an opt-in `hint`/`error-text` form-control chrome (matching named slots and `hint`/`error` CSS parts, mirroring `lr-select`, with `aria-describedby` wired to the rendered ids), plus `spellcheck`/`autocapitalize`/`autocorrect` passthrough and bubbling `blur`/`focus` events on the free-text mode's internal `<input>`. All additive — a bare `<lr-model-select>` with none of these set renders byte-identical to before.
3225
+ - 992b0ba: Add `lr-pagination`, a controlled, localized page-navigation component with previous/next controls, a validated numeric page jump, range summaries, applied-page announcements, loading/empty handling, RTL-aware icons, five sizes, and container-responsive stacking. Enrich `TreeItem` rows with optional `icon`, `description`, and `accessibleLabel` fields plus matching structured CSS parts while preserving the existing tree keyboard model.
3226
+ - dfb2f5e: Add `lr-phone-input`, a form-associated country/telephone field that keeps canonical form values in E.164 while preserving partial editable input. Numbering metadata stays opt-in through an injected adapter or the consumer-loaded `loadLibphonenumberAdapter()` helper; `libphonenumber-js` is an optional peer and international E.164 input works without a formatter.
3227
+ - d88377a: `lr-switch` gains an opt-in `hint`/`error-text` form-control chrome (props + matching named `hint`/`error` slots + CSS parts), mirroring `lr-select`'s pattern for those two pieces, with `aria-describedby` wired to whichever are rendered. Left unset, neither renders and the control is unchanged. The default slot stays the control's visible, clickable label (same as `lr-checkbox`) — no separate top-of-field `label` prop was added.
3228
+ - c8709cd: `lr-textarea` gains optional label/hint/error chrome, accessible-name forwarding, bounded auto-resize, editing-assistance attributes, public native-input and selection/caret APIs, synchronized `setRangeText()`, and bubbling composed focus/blur events. Existing visual and behavioral defaults remain unchanged when the new options are unused.
3229
+ - fca0ffb: `lr-tool-approval-dialog`'s raw-JSON args `<textarea>` now also hardcodes `autocapitalize="off"` and `autocorrect="off"` alongside its existing `spellcheck="false"`, so a mobile browser (notably iOS Safari, which defaults textarea `autocapitalize` to `'sentences'`) can no longer auto-capitalize or auto-correct JSON key/value text while a user edits tool-call arguments, silently corrupting the JSON.
3230
+ - 5b9b056: `lr-tree` now forwards a host-level `aria-label` attribute onto the internal `role="tree"` element's accessible name as a fallback when `label` is left unset, matching `lr-slider`/`lr-select` — previously a host `aria-label` was silently dropped since `role="tree"` lives on an internal element, not the host.
3231
+ - 12595bd: `lr-typing-indicator`'s dots-variant stagger delays are now themeable via `--lr-typing-dot-stagger-1`/`-2` (defaulting to today's exact `600ms`/`1200ms`), so a consumer retiming `--lr-transition-ambient` can keep the stagger proportional.
3232
+
3233
+ ## 2.6.0
3234
+
3235
+ ### Minor Changes
3236
+
3237
+ - 78d4b58: `lr-chat-message` gains an `attachments-position` prop (`'before' | 'after'`, default `'after'`) so the `attachments` slot can render above the message body instead of below it, keeping DOM/visual/reading order in sync.
3238
+ - a072af9: `lr-chip` gains a `--lr-chip-pressed-border` custom property so a consumer can set the pressed/selected border color independent of `--lr-chip-accent` (which also drives the label text color). Falls back to `--lr-chip-accent`, so existing consumers are unaffected.
3239
+ - b56bdb2: `lr-empty` gains a `--lr-empty-compact-padding` custom property to override `compact`'s fixed uniform padding (e.g. with an asymmetric shorthand like `8px 2px`). Falls back to `var(--lr-space-xs)`, today's exact value.
3240
+ - e029ac2: `lr-heatmap` calendar mode gains a `weekdayLabelText?: (jsWeekday: number) => string | undefined` hook to override the weekday-axis label text (e.g. for a consumer with its own locale/translation state independent of the browser's runtime locale).
3241
+ - 6d5f9c4: Add `lr-textarea`, a bare multiline plain-text input primitive (value/rows/resize/placeholder, form-associated validation) — the `lr-*` equivalent of a plain `wa-textarea`.
3242
+ - bbe8007: `lr-segmented`'s `SegmentedItem` gains an optional `icon` field, rendered before the item's label.
3243
+ - e98013a: `lr-table`'s `TableColumn` gains a `headerCell` render hook (mirroring `cell`/`footer`) and `width`/`minWidth` fields. Any column defining `width` switches the table to `table-layout: fixed` so widths are authoritative.
3244
+ - 993809a: `lr-widget` gains a `backdrop-inset` prop to decouple the fullscreen backdrop's inset from the panel's own `fullscreen-inset`. Falls back to `fullscreen-inset`, so existing consumers are unaffected.
3245
+
3246
+ ### Patch Changes
3247
+
3248
+ - 1c78bd2: Fix `lr-poll-status`, `lr-typing-indicator`, and `lr-stream-status`'s ambient "still alive" pulse/bounce animations, which reused `--lr-transition-base` (180ms — reserved for discrete UI micro-interactions) and rendered as a fast flicker instead of a calm breathing loop. Adds a dedicated `--lr-transition-ambient` token (1.8s) for infinite looping indicators.
3249
+ - e029ac2: Fix `lr-heatmap`'s `cellColor` hook silently rendering solid black when it returns a CSS custom property or other non-literal color (e.g. `color-mix(...)`) — the value is now resolved via a cached, hidden probe element before being assigned to the canvas `fillStyle`.
3250
+ - 600544f: Fix `lr-skeleton` rendering as an invisible 0×0 box everywhere: `[part='base']` was a bare `<span>` (UA default `display: inline`), so its own `inline-size`/`block-size` were CSS no-ops per spec. Adds `display: block`.
3251
+
3252
+ ## 2.5.0
3253
+
3254
+ ### Minor Changes
3255
+
3256
+ - 84cefde: `lr-attachment-trigger`'s single-capability trigger `aria-label`s ("Attach files"/"Attach an
3257
+ image"/"Use camera"), its multi-capability menu's "Add attachment" label/aria-label, and its menu
3258
+ item labels ("Upload files"/"Upload a photo"/"Take a photo") now route through `this.localize()`,
3259
+ overridable via `.strings`/`registerLyraLocale()`. Default English output is unchanged when no
3260
+ override is set.
3261
+ - 6bf30ea: `lr-avatar` now accepts default-slotted icon/glyph content (e.g. an inline SVG), shown in place of
3262
+ the image/initials and taking priority over both `src` and `initials` — useful for a chat UI
3263
+ distinguishing an "AI" avatar from a "user" avatar by role glyph rather than a photo or initials. Set
3264
+ `alt` alongside the icon for an accessible name, since the glyph itself is treated as decorative.
3265
+ - 87890ea: `lr-checkbox`'s built-in required-field validation message ("Please check this box if you want
3266
+ to continue.") now routes through `this.localize()`, overridable via `.strings`/
3267
+ `registerLyraLocale()`. Default English output is unchanged when no override is set.
3268
+ - b720eda: Fixed `lr-chip`'s opt-in `selected` toggle/pressed mode so it stays interactive after the first
3269
+ click. `[part='base']`'s `role="button"`, `tabindex`, `aria-pressed`, and click/keydown handlers
3270
+ used to be gated on the _current_ value of `selected`, so a chip that started `selected` and was
3271
+ clicked (flipping it to `false`) lost its focusable/clickable semantics on the next render — there
3272
+ was no way to click it back on. `selected` becoming `true` at any point now latches the chip into
3273
+ toggle mode for good, so it stays clickable in both directions. A chip that must be interactive
3274
+ from the outset while starting **unselected** (e.g. an initially-inactive filter chip) can opt in
3275
+ explicitly with the new `toggleable` property, since `selected`'s own default (`false`) can't be
3276
+ told apart from "never opted in" on its own.
3277
+ - cbfec47: `lr-citation-badge`'s visible status words folded into its computed accessible name ("High
3278
+ confidence"/"Medium confidence"/"Low confidence"/"Verified"/"Unverified") now route through
3279
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English output is
3280
+ unchanged when no override is set.
3281
+ - dba57e9: `lr-context-meter`'s accessible summary ("{used} of {total} used" / "{used} used") now routes
3282
+ through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English
3283
+ output is unchanged when no override is set.
3284
+ - 7379a41: `lr-conversation-item`'s "Untitled conversation" fallback title now routes through
3285
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English output is
3286
+ unchanged when no override is set.
3287
+ - acdaa37: `lr-dock-panel`'s resize-handle and collapse-toggle `aria-label`s ("Resize panel",
3288
+ "Collapse panel"/"Expand panel") now route through `this.localize()`, overridable via
3289
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3290
+ - eca2ea4: `lr-document-preview`'s hardcoded English strings — the image-preview `alt` fallback
3291
+ ("Document preview"), the unsafe-URL error ("Document URL is not allowed."), the non-`Error`
3292
+ fetch-failure message ("Failed to load document."), and the empty-`error-message` fallback
3293
+ ("Something went wrong.") — now route through `this.localize()`, overridable via
3294
+ `.strings`/`registerLyraLocale()`. Its in-flight text-fetch spinner label ("Loading document…")
3295
+ is now also wired through the existing `loadingDocument` message key. Default English output is
3296
+ unchanged when no override is set.
3297
+ - a3c4ebf: `lr-export-button`'s trigger button text (default "Export", also reused for the format menu's
3298
+ `aria-label`) now routes through `this.localize()` when `label` is left at its built-in default,
3299
+ overridable via `.strings`/`registerLyraLocale()` — matching `lr-attachment-chip`'s
3300
+ `removeLabel`/`retryLabel` convention. Setting the `label` attribute/property explicitly still
3301
+ overrides it directly. Default English output is unchanged when no override is set.
3302
+ - df8341b: `lr-generation-status`'s stop-button `aria-label` ("Stop generating") now routes through
3303
+ `this.localize()` (sharing the existing `stopGenerating` key used elsewhere in the library), and
3304
+ the tokens segment's singular/plural noun ("token"/"tokens") is now localizable too, matching
3305
+ `lr-json-viewer`'s/`lr-word-cloud`'s existing count-noun pattern. Overridable via
3306
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3307
+ - 20ae3e7: `lr-graph`'s visually-hidden data-list `aria-label` ("Graph data") now routes through
3308
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English output is
3309
+ unchanged when no override is set.
3310
+ - 8c29581: `lr-segmented` gains a `label` property giving its `role="radiogroup"` root an accessible name.
3311
+ When unset, a plain `aria-label` attribute on the host element is honored as a fallback, matching
3312
+ `lr-slider`'s existing `label`/`aria-label` convention. Previously the radiogroup had no way to
3313
+ receive an accessible name at all.
3314
+ - 259c0c6: Completed a full-library i18n/RTL/styling standardization pass across the remaining component
3315
+ families not yet covered by earlier rounds — `chart` (and `box-plot`/`histogram`/`lite-chart`),
3316
+ `avatar`, `code-block`, `combobox`, `date-picker`, `dialog`, `document-preview`, `export-button`,
3317
+ `file-input`, `graph`, `heatmap`, `map`, `time-range`, `tool-call-chip`, `tool-param-form`,
3318
+ `tool-result-dialog`, `tree`, `widget`, and several smaller components. Highlights:
3319
+
3320
+ - Routed remaining hardcoded English strings (accessible descriptions, aria-labels, empty-state
3321
+ text) through `this.localize()`.
3322
+ - Fixed RTL gaps: `date-picker`'s previous/next chevrons now mirror under `dir="rtl"` (rotating
3323
+ the wrapping `part`, not the icon), matching the grid's own arrow-key swap.
3324
+ - `lr-avatar`: fixed a dangling `--lr-color-surface-alt` token reference, corrected its `size`
3325
+ JSDoc, and extended the accessible-name role/`aria-label` to the initials-fallback path (not
3326
+ just the icon-slot path) whenever `alt` is set.
3327
+ - `lr-export-button` now fires `lr-show`/`lr-hide` on its format menu, matching the same
3328
+ convention already used by `lr-menu`/`lr-select`/`lr-combobox`.
3329
+ - Fixed a `this.localize(key, literalFallback)` pattern that unconditionally short-circuited
3330
+ `registerLyraLocale()` lookups for the affected keys (the fallback is now omitted wherever
3331
+ `DEFAULT_STRINGS` already carries the same default).
3332
+
3333
+ AGENTS.md gained a new "Internationalization (i18n), RTL, and theming" section documenting the
3334
+ resulting standard, and both READMEs now summarize it for consumers.
3335
+
3336
+ - 79e4390: Fixed gaps found during a full re-verification pass over previously-completed work:
3337
+
3338
+ - `lr-menu`'s type-ahead navigation now excludes `hidden`/`aria-hidden` items (it already
3339
+ excluded `disabled` ones), matching the Arrow/Home/End roving-focus navigation it sits next to.
3340
+ - The root barrel (`src/lyra.ts`) now re-exports 13 component event-map types that were previously
3341
+ unreachable from the package root even though their owning classes were exported: `LyraChip`,
3342
+ `LyraChipGroup`, `LyraCitationBadge`, `LyraCopyButton`, `LyraDiffView`, `LyraFileInput`,
3343
+ `LyraHeatmap`, `LyraLiteChart`, `LyraMediaCard`, `LyraSelect`, `LyraSourceCard`, `LyraSplit`, and
3344
+ `LyraTimeRange`'s `*EventMap` types are now all importable from `@aceshooting/lyra-ui`.
3345
+
3346
+ - 59d4477: `lr-media-card`'s hardcoded English fallback strings — the file-chip "Untitled file" name, the
3347
+ `image`/`video` alt-text fallbacks ("Image attachment"/"Video attachment"), and the accessible
3348
+ "Open …" label (both the named and generic-kind forms) — now route through `this.localize()`,
3349
+ overridable via `.strings`/`registerLyraLocale()`. Default English output is unchanged when no
3350
+ override is set.
3351
+ - ea774a8: `lr-mention-popover`'s default listbox accessible name ("Suggestions") now routes through
3352
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()` — matching the already-shared
3353
+ `noMatches` key its empty-state row uses. An explicit `label`/`empty-text` value still wins
3354
+ verbatim. Default English output is unchanged when no override is set.
3355
+ - cd10606: `<lr-menu>` gains an opt-in `closeOnEscapeAnywhere` property. Escape has always closed the menu
3356
+ and refocused the trigger when it originates from a real `<lr-menu-item>`, but slotted non-item
3357
+ content (e.g. a form control slotted alongside the items) previously got full default keyboard
3358
+ behavior with no way to close the menu on Escape. Setting `closeOnEscapeAnywhere` extends that
3359
+ same Escape-closes-and-refocuses behavior to keydowns from anywhere in the list, including slotted
3360
+ non-item content. Defaults to `false`, so existing consumers are unaffected.
3361
+ - 7d63af9: `lr-menu`'s `role="menu"` popup default accessible name ("Menu") now routes through
3362
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. An explicit `label` value
3363
+ still wins verbatim. Default English output is unchanged when no override is set.
3364
+ - f232381: `lr-model-settings-panel`'s hardcoded English strings — the visible "Temperature" caption
3365
+ (also reused as the nested `lr-slider`'s accessible name) and the internal `lr-model-select`'s
3366
+ "Select a model…" placeholder — now route through `this.localize()`, overridable via
3367
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3368
+ - 1686322: `lr-playback`'s play/pause button and position-slider `aria-label`s ("Play"/"Pause",
3369
+ "Playback position") now route through `this.localize()`, overridable via
3370
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3371
+ - 0cacb4d: `lr-poll-status`'s pause/resume button aria-label, due-state countdown text ("Refreshing…"), and its
3372
+ three live-region announcements ("Paused."/"Resumed."/"Refreshing now.") now route through
3373
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. It also now shows a distinct
3374
+ "Paused" countdown state while `paused`, instead of freezing on whatever value it last displayed.
3375
+ Default English output is unchanged when no override is set.
3376
+ - 870523f: `lr-widget` gains two new named slots, `collapse-icon` and `fullscreen-icon`, overriding the
3377
+ built-in chevron/expand-or-close glyphs on the collapse and fullscreen toggle buttons entirely
3378
+ (platform slot-fallback-content mechanism: whatever is assigned wins, otherwise the default glyph
3379
+ renders unchanged). `WidgetView`'s `label` is now optional and a new `ariaLabel` field lets a view
3380
+ toggle be icon-only while still exposing an accessible name — previously a toggle with no `label`
3381
+ had no accessible name at all.
3382
+ - c2bc232: Re-audited every component against the library's i18n/RTL/theming standard and fixed the
3383
+ remaining gaps found:
3384
+
3385
+ - Removed several `this.localize(key, literalFallback)` call sites (`toolApprovalHeading`,
3386
+ `playback`'s play/pause/position labels, `model-settings-panel`'s temperature/model labels,
3387
+ `media-card`'s five accessible-name strings, `kbd`'s shortcut-token labels, `chat-composer`'s
3388
+ composer label) where the literal fallback silently defeated `registerLyraLocale()` translation
3389
+ for that call site.
3390
+ - Routed remaining hardcoded strings through `this.localize()`: `date-picker`'s next-month label
3391
+ and `date-input`'s validation messages, `toast-item`'s/`chip`'s/`combobox`'s remove/close
3392
+ labels (now interpolated via a `{placeholder}` instead of string concatenation), `heatmap`'s
3393
+ matrix/calendar aria-labels and "no data"/row/col fallbacks, `chart`/`box-plot`'s description
3394
+ and data-table text, `lite-chart`'s mark-position announcement, `document-preview`'s empty-state
3395
+ nouns, `json-viewer`'s copy/expand/collapse/count labels, `stat`'s trend announcement,
3396
+ `dialog`'s `confirm()` cancel button, `typing-indicator`'s default label, `tool-param-form`'s
3397
+ edge-case validation message, and `tool-result-dialog`/`tool-call-chip`'s duration seconds unit.
3398
+ - Fixed RTL gaps: `app-rail-item`'s icon tooltip now flips side under `dir="rtl"` via
3399
+ `rtlAwarePlacement()`, `chat-message`'s and `source-list`'s collapse/disclosure chevrons now
3400
+ mirror under RTL, and `lite-chart`'s roving-tabindex point navigation now swaps
3401
+ ArrowLeft/ArrowRight under RTL.
3402
+
3403
+ Also compressed the shared string registry (`internal/localization.ts`): removed 21 `kbd*` base
3404
+ keys (`kbdEnter`, `kbdEscape`, `kbdTab`, etc.) that were fully superseded by their `*Word`/`*Visual`
3405
+ counterparts and had no remaining call sites anywhere in the library, reducing the packed consumer
3406
+ bundle size.
3407
+
3408
+ - aeef118: `lr-select`'s required-field validation message ("Please select an option.") and its
3409
+ trigger's fallback accessible name ("Select", used only when no `aria-label`, `label`, or
3410
+ `placeholder` is set) now route through `this.localize()`, overridable via
3411
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3412
+ - 4fb27a2: `lr-skeleton`'s default accessible name ("Loading…") now routes through `this.localize()`
3413
+ (reusing the shared `loading` key), overridable via `.strings`/`registerLyraLocale()`. An
3414
+ explicit `label` still wins verbatim. Default English output is unchanged when no override is set.
3415
+ - f7b9f0e: `lr-source-list`'s fallback header text ("Sources", used only when neither `label` nor
3416
+ `label-plural` is set) now routes through `this.localize()`, overridable via
3417
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3418
+ - f2ea145: `lr-stepper`'s `StepItem` gains an optional `title` field, rendered as a native `title` tooltip on
3419
+ that step's button — useful for explaining why a `disabled` step is locked (e.g. "Complete Basics
3420
+ first"). Steps that omit it render no `title` attribute at all, unchanged from today.
3421
+ - 9e5864a: `lr-stream-status`'s built-in stalled-message default ("Taking longer than usual…") and its
3422
+ three live-region announcements ("Connection stalled."/"Connection restored."/"No longer
3423
+ stalled.") now route through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`.
3424
+ Default English output is unchanged when no override is set.
3425
+ - 9174500: `lr-switch`'s built-in required-field validation message ("Please turn this on.") now routes
3426
+ through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English
3427
+ output is unchanged when no override is set.
3428
+ - 60084ba: `lr-thinking-panel`'s default header label ("Thinking") and its duration-display text ("Thought
3429
+ for …"/"Thinking…") now route through `this.localize()`, overridable via
3430
+ `.strings`/`registerLyraLocale()`. An explicit `label` still wins verbatim. Default English
3431
+ output is unchanged when no override is set.
3432
+ - b113bda: `lr-tool-approval-dialog`'s heading text, generic tool-name fallback, args-editor accessible
3433
+ name, invalid-JSON fallback error, and its Deny/Edit/Approve button labels now route through
3434
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English output is
3435
+ unchanged when no override is set.
3436
+ - 3b1f930: `lr-tool-call-chip`'s visible status labels (Pending/Running/Success/Error/Denied, shared with
3437
+ `lr-tool-result-dialog`'s identical vocabulary) and its unnamed-tool fallback ("Tool call") now
3438
+ route through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English
3439
+ output is unchanged when no override is set.
3440
+ - bbaea80: `lr-tool-param-form`'s validation messages (required field, wrong type for a string/number/
3441
+ integer/boolean, enum mismatch, const mismatch, unsupported field type, malformed schema shape,
3442
+ non-serializable value) now route through `this.localize()`, overridable via
3443
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3444
+ - bda19ac: `lr-tool-select-dialog`'s dialog title, search placeholder, "use default tools" switch label
3445
+ and hint, category count/"Other" fallback, tools-enabled summary, no-matches message, and the
3446
+ no-tools-available empty state now route through `this.localize()`, overridable via
3447
+ `.strings`/`registerLyraLocale()`. Default English output is unchanged when no override is set.
3448
+ - 220bd73: `lr-widget`'s collapse/expand, exit-fullscreen/expand-to-fullscreen, and view-toggle-group
3449
+ aria-labels, plus its fullscreen dialog's fallback accessible name, now route through
3450
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. The collapse/expand labels
3451
+ reuse `lr-dock-panel`'s existing `dockPanelCollapse`/`dockPanelExpand` keys. Default English
3452
+ output is unchanged when no override is set.
3453
+
3454
+ ### Patch Changes
3455
+
3456
+ - 00ce49f: Fix `lr-date-picker`'s day-grid keyboard navigation to swap ArrowLeft/ArrowRight under `dir="rtl"`, matching the grid's own visual mirroring (the day cells use unset `direction`, so the browser already lays them out right-to-left). ArrowUp/ArrowDown (by week) are unaffected.
3457
+ - 37e1a2f: `lr-table`'s header-cell ArrowLeft/ArrowRight roving-tabindex navigation now derives its RTL
3458
+ check through the shared `isRtl()` helper instead of a duplicated inline `getComputedStyle`
3459
+ check, and gains test coverage confirming ArrowRight/ArrowLeft already swap correctly under
3460
+ `dir="rtl"` (a native `<table>` mirrors column visual order under RTL on its own) while
3461
+ ArrowUp/ArrowDown row navigation is unaffected. No behavior change.
3462
+ - 2fd3786: Fix calendar-heatmap weekday-axis labels to respect firstDayOfWeek instead of always labeling grid rows 1/3/5.
3463
+
3464
+ ## 2.4.0
3465
+
3466
+ ### Minor Changes
3467
+
3468
+ - 171bdbd: `lr-attachment-chip`'s file-size unit abbreviations ("B"/"KB"/"MB"/"GB"/"TB") now route through
3469
+ `this.localize()` when rendered, overridable via `.strings`/`registerLyraLocale()`. The exported
3470
+ `formatFileSize()` pure function gains an optional `unitLabel` resolver parameter, defaulting to the
3471
+ plain English abbreviation — every existing single-argument call is unaffected.
3472
+ - 5f043ba: `lr-chart`'s data-table "Category" column header, per-row "Point N" fallback label, and "Reset
3473
+ zoom" button text now route through `this.localize()`, overridable via `.strings`/
3474
+ `registerLyraLocale()`. Default English text is unchanged.
3475
+ - 5e90140: `lr-chat-composer`'s action button labels ("Send message"/"Stop generating") now route through
3476
+ `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Also adds `stoppable: boolean =
3477
+ true` — when set to `false`, the button never renders as a Stop/cancel control while busy; it stays a
3478
+ disabled Send button instead, for backends with no cancellation endpoint. Default behavior is
3479
+ unchanged.
3480
+ - 558e76c: `lr-chat-message`'s visible status text ("Sending…"/"Responding…"/"Failed to send") and its two
3481
+ live-region status-change announcements ("Message failed to send."/"Message complete.") now route
3482
+ through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English text is
3483
+ unchanged.
3484
+ - 238c8d7: `lr-chip-group`'s collapsed overflow-indicator's visible "+N" text now routes through
3485
+ `this.localize('showMoreCollapsed', ...)`, matching the aria-label it sits beside, which was already
3486
+ localized. Default English output ("+N") is unchanged.
3487
+ - 0d9018f: `lr-code-block`'s collapse-toggle, copy-button, and code-region aria-labels now route entirely
3488
+ through `this.localize()` instead of concatenating a localized verb with a hardcoded English suffix
3489
+ ("code"/"to clipboard"/"Code"). Default English output is unchanged.
3490
+ - a249bd6: `lr-diff-view`'s copy-button aria-label now routes entirely through `this.localize('copyDiff', ...)`
3491
+ instead of concatenating the localized "copy" verb with a hardcoded " diff" suffix. Default English
3492
+ output ("Copy diff") is unchanged.
3493
+ - 58c6e59: `lr-file-input`'s drag-preview live-region announcements ("Release to add the file." / "This file
3494
+ type is not accepted.") now route through `this.localize()`, overridable via `.strings`/
3495
+ `registerLyraLocale()`. Default English text is unchanged. The post-drop `acceptedMessage`/
3496
+ `rejectedMessage` properties and the visible `label` property are unaffected (already
3497
+ consumer-overridable).
3498
+ - b3e3bb6: `lr-json-viewer`'s root-node toggle/copy fallback words ("array"/"object"/"value", used only when a
3499
+ node has no key label) now route through `this.localize()`, overridable via `.strings`/
3500
+ `registerLyraLocale()`. Default English text is unchanged.
3501
+ - b322e75: `lr-model-select`'s synthetic stale-value row badge ("not in catalog") now routes through
3502
+ `this.localize('notInCatalog')`, so it can be overridden via `.strings`/`registerLyraLocale()` like
3503
+ the component's other built-in message (`noMatches`). Default English text is unchanged.
3504
+ - e54eeee: `lr-source-card`'s "Untitled source" fallback and its " — p. N" page-suffix format now route
3505
+ through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English output
3506
+ is unchanged.
3507
+ - 0576643: `lr-split` now redistributes the track space freed when a `panelConstraints` pixel bound clamps a
3508
+ panel's percentage basis down (e.g. a `maxPx` cap on a wide viewport) to sibling panels that have no
3509
+ pixel constraint of their own, instead of leaving that space unused. No behavior change for splits
3510
+ without `panelConstraints`, or where no panel is actually clamped this render.
3511
+ - 97756af: `lr-table`'s `columns[].sticky` option now accepts `'start' | 'end'` in addition to the legacy
3512
+ `boolean` (`true` continues to mean `'start'`, unchanged). An `'end'`-sticky column pins to the
3513
+ inline-end edge instead — useful for a trailing actions column that would otherwise be pushed off
3514
+ a narrow viewport — via the same `inset-inline-*` logical-property approach, so RTL is unaffected.
3515
+ - ffee803: `lr-tool-result-dialog`'s tool-name fallback ("Tool call"), visible status label
3516
+ ("Pending"/"Running"/"Success"/"Error"/"Denied"), and maximize/restore button aria-label now route
3517
+ through `this.localize()`, overridable via `.strings`/`registerLyraLocale()`. Default English output
3518
+ is unchanged.
3519
+ - f9f57f9: `lr-word-cloud`'s default aria-label's pluralized "word"/"words" noun now routes through
3520
+ `this.localize()` too, so a registered translation of the `wordCloud` template's `{word}` slot is no
3521
+ longer stuck in English. Default output is unchanged.
3522
+
3523
+ ## 2.3.0
3524
+
3525
+ ### Minor Changes
3526
+
3527
+ - a1b2f8e: `lr-app-rail` gains `dragging` (reflected boolean, true for the duration of a pointer-driven
3528
+ resize -- not a keyboard step -- so its own `[part='base']` transition suppresses during the drag
3529
+ instead of visibly "chasing" the pointer) and `hideToggle` (suppresses the built-in mobile hamburger
3530
+ button for a consumer that owns its own external toggle wired to `open`).
3531
+ - e9075b8: `lr-app-rail-item` gains an opt-in `tooltip` property: a hover/focus flyout showing the item's
3532
+ label text while `icon-only` hides it from view, using the library's existing Floating-UI-backed
3533
+ `place()` positioner -- an explicit, documented alternative to hand-rolling a `::part()`+`::after`
3534
+ tooltip composition.
3535
+ - 8160548: `lr-attachment-chip`'s `compact` variant now also shrinks font-size and gap (via new
3536
+ `--lr-attachment-chip-compact-font-size`/`-compact-gap` custom properties), not just
3537
+ border/radius/padding/thumbnail-size. Also adds `thumbnailOnly`, which -- combined with `compact`
3538
+ on an image-mime chip -- hides the filename/size text entirely for a pure thumbnail density with
3539
+ no consumer-side CSS.
3540
+ - 099fa8a: Add `lr-avatar`: a small, fixed-size identity marker (image, or an initials fallback) for a
3541
+ user-menu trigger or similar identity affordance -- `size`/`shape`/`tone` variants mirror
3542
+ `lr-chip`'s existing tone vocabulary for consistency.
3543
+ - bf9d442: Add `lr-card`: a generic bordered content container (`appearance` variants mirroring `wa-card`,
3544
+ `header`/`media`/`footer`/`actions` slots) for the "small bordered surface with padding" idiom
3545
+ common across hero highlights and clickable grid tiles -- a real `lyra-ui` parity counterpart to
3546
+ `wa-card`, which this library otherwise mirrors 1:1.
3547
+ - f9ecffd: `lr-chip` gains an opt-in `selected`/pressed interactive mode: `[part='base']` becomes
3548
+ keyboard-activatable and reflects `aria-pressed`, toggling on click/Enter/Space and emitting
3549
+ `lr-chip-select`. Not combinable with `removable` (avoids a nested-interactive a11y violation);
3550
+ today's passive-label-pill usage is unaffected since `selected` defaults to `false`.
3551
+ - db24359: Add `lr-code-block-core`: a build-lean variant of `lr-code-block` for a consumer whose
3552
+ `languages` map already covers every language it renders. Unlike `languagesOnly` (a runtime flag
3553
+ on `lr-code-block` itself, which a bundler can't prove always-true and so can't tree-shake),
3554
+ `lr-code-block-core` is a genuinely separate module that never references shiki's full
3555
+ ~200-language default entry point at all -- importing it instead of `code-block.js` gives a real
3556
+ compile-time exclusion of that table from the build output.
3557
+ - 83ba36c: `lr-dialog` gains `--lr-dialog-width`, unset by default -- when set, the panel actually
3558
+ stretches to that width instead of only shrink-wrapping its content capped at
3559
+ `--lr-dialog-max-width`, which was a real gotcha for anyone porting from `wa-dialog`'s
3560
+ assertive `--width` token.
3561
+ - a1d7030: Add `lr-diff-view`: a real two-string line diff (LCS-aligned), rendered as interleaved
3562
+ unified-diff output -- unlike diff-flavored syntax highlighting over an already-formatted string,
3563
+ this computes the alignment itself, so a one-line change inside a longer block renders as one
3564
+ red/green pair near the change instead of every old line then every new line.
3565
+ - b56abda: `lr-empty`'s `heading`/`description` gain the same slot-override-attribute treatment
3566
+ `lr-stat`'s `caption`/`sub` already have -- a consumer can now pass rich mid-sentence content
3567
+ (e.g. an inline `<code>` reference) while the plain-string attribute stays the default.
3568
+ - 4324a73: `lr-graph` now renders a link whose `target` isn't a real node as a short dashed stub off the
3569
+ source's position, instead of silently dropping it -- for a wiki-style `[[link]]`/broken-reference
3570
+ visualization where "this edge exists but its endpoint doesn't" is a meaningful state, not noise.
3571
+ A dangling `source` is still dropped (no position to draw a stub from).
3572
+ - 1e71d71: Rewrite `lr-heatmap`'s two weekday-axis-label tests to assert against independently fixed dates
3573
+ instead of re-deriving the implementation's own formula, which could never fail regardless of
3574
+ correctness -- the underlying `weekdayLabels()`/`firstDayOfWeek` anchoring was already correct.
3575
+ Also add `cellColor`, an optional per-cell color override function (mirroring the existing
3576
+ `cellText`/`cellInteractive` shape) that bypasses the color ramp entirely for an exact value.
3577
+ - 2e74ea0: Fix `lr-lite-chart`'s `minBarHeight` z-order bug for stacked bars: a floored near-zero segment
3578
+ was being overdrawn by the segment stacked on top of it, since each segment's position was derived
3579
+ independently from cumulative value rather than from where the previous (possibly-floored) segment
3580
+ actually ended on screen. Also add `selectedIndex: number[]`, reflecting `data-selected` onto every
3581
+ bar at a given category index across all datasets, for highlighting a whole selected column.
3582
+ - 00f3b37: `lr-markdown` gains `escapeHtml`, an opt-in property overriding `marked`'s `html` renderer hook
3583
+ to emit escaped text instead of parsed/sanitized markup -- for a consumer rendering arbitrary
3584
+ already-written content (transcripts, logs) where a stray angle bracket should render as visible
3585
+ text rather than a real DOM element, without giving up GFM tables/lists/etc.
3586
+ - d3fbf36: Add `lr-poll-status`: a "next scheduled refresh" countdown with a built-in pause control -- a
3587
+ ticking M:SS display, a "Refreshing…" due state, and an internal live region announcing phase
3588
+ transitions, mirroring `lr-stream-status`'s own composition for a different concern (a scheduled
3589
+ interval, not transport/connection health).
3590
+ - b5464bd: Add `lr-segmented`: a single-select button row with the WAI-ARIA APG `radiogroup` contract
3591
+ (role="radio", roving tabindex, automatic-activation Arrow/Home/End navigation) built in --
3592
+ "choose exactly one of N labeled options" is ubiquitous settings/filter-panel UI that otherwise
3593
+ gets hand-rolled without keyboard/ARIA semantics every time.
3594
+ - 551f272: `lr-select` gains `--lr-select-trigger-height`, unset (auto) by default -- when a consumer sets
3595
+ it, the trigger resolves to exactly that height (both floor and cap) instead of only being
3596
+ floored by `--lr-select-trigger-min-height`, for pixel-matching a sibling form field in the same
3597
+ row without a blunt `::part(trigger){block-size:...}` override.
3598
+ - 1fddbdc: Add `lr-stepper`: ordered multi-step wizard navigation (label + index, current/completed/
3599
+ locked/error state, click-to-jump, horizontal/vertical orientation). Fully data-driven and
3600
+ controlled -- like `lr-table`, it never mutates its own `steps` data, firing a cancelable
3601
+ `lr-step-select` event and leaving state updates to the host, so gating a jump behind an
3602
+ external validity check (e.g. "does the target step's data exist yet") is a normal listener, not a
3603
+ workaround.
3604
+ - 60dbf18: `lr-table` gains two per-column hooks: `footer(rows)`, rendered in a real sticky-bottom
3605
+ `<tfoot>` (only when at least one column defines it) -- e.g. a totals row; and `cellStyle(row)`,
3606
+ applied via `styleMap` directly to the generated `<td>` -- e.g. a computed heat-tint background --
3607
+ which coexists safely with the existing sticky-column offset styling.
3608
+ - 6ce5b87: Add a new `./testing` subpath exporting `installHappyDomFormAssociatedShims()` -- an opt-in,
3609
+ environment-guarded polyfill for `HTMLElement.prototype.attachInternals`, for a downstream
3610
+ consumer's own Vitest+happy-dom test suite (happy-dom has no `ElementInternals` implementation,
3611
+ and every form-associated `lr-*` component calls `attachInternals()` unconditionally in its
3612
+ constructor). Not used by this package's own tests, which already run against real browsers.
3613
+ - 25254f2: `lr-widget` gains a leading `icon` slot, rich `label`/`sublabel` slot overrides (mirroring
3614
+ `lr-stat`'s `caption`/`sub` pattern), and a `views` property driving a built-in header toggle
3615
+ group plus one named slot per entry -- for a chart/table (or similar) toggle inside the same card
3616
+ chrome, so a consumer no longer has to hand-roll that shell around a bare default slot.
3617
+
3618
+ ### Patch Changes
3619
+
3620
+ - 062f036: Fix `lr-attachment-trigger`'s internal hidden `<input type="file">` actually rendering as a
3621
+ visible, focusable-adjacent element in normal document flow — it now has `display: none` by
3622
+ default (and a new `hidden-input` CSS part, for the rare integration that needs to override that).
3623
+ - 9094b39: Fix `lr-chart` losing a user's legend-toggled hidden-dataset state on every data-driven redraw --
3624
+ `draw()` now snapshots each dataset's `isDatasetVisible()` state before reassigning `chart.data` and
3625
+ restores it via `setDatasetVisibility()` afterward, since Chart.js's own dataset-object identity
3626
+ changes on every reactive update from a live-polling consumer.
3627
+ - a413c8c: Fix `lr-chip-group`'s "+N"/"Show less" overflow toggle hardcoding English strings instead of using
3628
+ the library's own existing `localize()`/`strings` override mechanism, which every other component
3629
+ with translatable text already uses (including the identical `showMore`/`showLess` keys, already
3630
+ consumed by `lr-source-card`).
3631
+ - 4010bc4: `lr-menu`'s `onListKeyDown` now ignores a keydown whose target isn't a real `<lr-menu-item>`,
3632
+ matching the same `instanceof LyraMenuItem` guard `onItemSelect`/`onListFocusIn` already use --
3633
+ previously it unconditionally intercepted Arrow/Home/End/Enter/Space/Escape/Tab from any keydown
3634
+ bubbling through `[part="list"]`, including from non-item slotted content (e.g. a custom-range
3635
+ date input), hijacking keystrokes meant for it. Note: Escape/Tab now also only close the menu when
3636
+ the event originates from a real item -- a slotted non-item control gets fully default keyboard
3637
+ behavior instead.
3638
+ - a5a055f: Fix `lr-split`'s fixed-percent panels not reserving space for the auto-inserted divider between
3639
+ them, causing a deterministic `(panelCount - 1) * dividerWidth` container overflow in the default
3640
+ (uncollapsed) state. Panels now get a nonzero `flex-shrink` so they absorb the dividers' own width
3641
+ instead of the row overflowing.
3642
+ - 18003f0: Fix `lr-stat`'s `[part='base']` not stretching to fill its host in a CSS Grid -- a stat tile with
3643
+ a longer `sub`/breakdown-rows line rendered visibly taller than its row-mates. `block-size: 100%` on
3644
+ `[part='base']` now matches the convention `lr-word-cloud`/`lr-context-meter` already use.
3645
+ - 55c384e: Fix `lr-tabs`'s `tablist` part showing a phantom vertical scrollbar on a tablist with no
3646
+ vertically-overflowing content — `overflow-x: auto` alone can leave the y axis's computed overflow
3647
+ at `auto` too per the CSS overflow spec, which sub-pixel rounding can trip; `overflow-y: hidden` is
3648
+ now explicit, since the tablist is never meant to scroll vertically.
3649
+
3650
+ ## 2.2.0
3651
+
3652
+ ### Minor Changes
3653
+
3654
+ - ff41aba: `lr-app-rail`: add a `resizable` opt-in (drag + keyboard-steppable `[part="resizer"]` handle,
3655
+ `railWidthPx`/`minRailWidthPx`/`maxRailWidthPx`, `lr-rail-resize` event) for the `'full'` state's
3656
+ width; add `preferredMode` to manually prefer `'full'`/`'icon-only'` while the mobile breakpoint
3657
+ keeps tracking automatically; and fix the mobile toggle button's `aria-label` to use a proper
3658
+ `openNavigation` message key (consistent with the existing `closeNavigation` key) instead of
3659
+ concatenating a hardcoded `" navigation"` suffix onto a partially-localized string.
3660
+ - 3b1a404: `lr-app-rail-item`: add an `active` property that reflects `aria-current="page"` onto the
3661
+ internal link/button, mirroring `lr-conversation-item`'s existing `active` pattern.
3662
+ - 3b7a98b: `lr-attachment-chip`: fix the uploading progressbar/spinner's `aria-label` to actually use
3663
+ `uploadingLabel` (previously hardcoded, unlike the adjacent visible status text); add an
3664
+ `untitledLabel` override for the empty-name fallback; add a `compact` density variant.
3665
+ - 49be9e4: `lr-attachment-trigger`: add a `triggerTitle` property forwarded to the internal trigger
3666
+ button(s)' native `title` (a sighted-mouse-user hover tooltip, distinct from `triggerLabel`'s
3667
+ `aria-label` role); reduce the internal `.trigger-button:hover` rule's specificity via `:where()`
3668
+ so a consumer's `::part(trigger):hover` override wins without needing `!important`.
3669
+ - 4d04843: `lr-code-block`: add a `languagesOnly` opt-in that skips the default `loadShikiHighlighter()`
3670
+ call entirely, so a consumer whose `languages` map already covers every language it renders has no
3671
+ bundler-reachable path to shiki's full per-language dynamic-import table.
3672
+ - 2968d7b: Add `lr-copy-button`: a standalone icon-only copy-to-clipboard button for a plain text `value`,
3673
+ with no positioning opinion of its own — for a consumer needing just the copy/checkmark-swap
3674
+ affordance without adopting `lr-code-block`'s or `lr-json-viewer`'s full content model.
3675
+ - 49be9e4: `lr-dialog`: add `noLightDismiss` to opt out of backdrop-click dismissal, and make `close()`
3676
+ actually respect a `lr-dialog-close` listener's `preventDefault()` (the event is now genuinely
3677
+ `cancelable: true`) for every dismissal path — Escape, backdrop, the built-in close button, and a
3678
+ consumer's own `close()` call.
3679
+ - 6958595: `lr-heatmap`: add a `cellInteractive` predicate to opt individual cells out of hit-testing and
3680
+ keyboard roving focus, and a `colorSteps` discrete-array ramp as an alternative to the 2-endpoint
3681
+ `--lr-heatmap-scale-lo`/`-hi` linear interpolation (governs both `mode`s and both `scale`
3682
+ values). Also adds test coverage confirming `firstDayOfWeek`'s calendar-mode weekday-axis labels
3683
+ are correct for a non-Sunday-first week (the underlying computation was already correct; only the
3684
+ test combining the two was missing).
3685
+ - 2c6fc82: `lr-lite-chart`: add a `minBarHeight`/`min-bar-height` pixel floor for near-zero stacked
3686
+ segments, fix `scale="sqrt"` proportionality for stacked bars (previously compressed each
3687
+ segment's absolute cumulative stack position independently instead of the bar's total height
3688
+ split linearly by segment share), and add a `chartLabel`/`chart-label` override for the chart's
3689
+ auto-derived `aria-label`.
3690
+ - e29b2f9: `lr-markdown`: add `part="paragraph"`, `part="list"` (both `<ul>` and `<ol>`), and
3691
+ `part="inline-code"` (bare inline codespans only, not a fenced code block's `<code>`, which
3692
+ already has its own `part="code-block"` wrapper) so a consumer's `::part()` CSS can reach plain
3693
+ text elements that previously had no themeable hook.
3694
+ - 3b7a98b: `lr-split`: add a `dividerLabel` function property overriding the auto-inserted divider's
3695
+ hardcoded English `aria-label` template.
3696
+
3697
+ ## 2.1.0
3698
+
3699
+ ### Minor Changes
3700
+
3701
+ - 82a3419: `<lr-attachment-chip>`: added four label-override properties for i18n/locale — `removeLabel`/`retryLabel` (`remove-label`/`retry-label` attributes, the verb prefixed to the remove/retry buttons' `aria-label` ahead of the interpolated filename) and `uploadingLabel`/`uploadFailedLabel` (`uploading-label`/`upload-failed-label` attributes, the verb/phrase used in the visible uploading/error status text, keeping the live percentage interpolation intact for `uploadingLabel`). All four default to today's exact hardcoded English text (`'Remove'`, `'Retry'`, `'Uploading'`, `'Upload failed'`), so leaving them unset changes nothing for existing consumers.
3702
+ - 82a3419: `<lr-attachment-trigger>`: added a `triggerLabel` property (`trigger-label` attribute) that overrides the single-capability trigger button's `aria-label`, which previously came unconditionally from the built-in `CAPABILITY_META` table (e.g. `'Attach files'`, `'Attach an image'`, `'Use camera'`). Lets a host localize the accessible name without forking the component. Unset (the default) preserves today's exact `CAPABILITY_META`-derived label for every capability.
3703
+ - 82a3419: Add `<lr-code-block>` `languages`, a map of language id to an already-imported shiki grammar module (e.g. `import bash from 'shiki/langs/bash.mjs'`). When `language` matches a key in `languages`, highlighting for it is seeded from exactly that pre-supplied grammar via a fine-grained `createHighlighterCore()` highlighter (`code-loader.ts`'s new `loadShikiHighlighterCore()`), bypassing the default `loadShikiHighlighter()` singleton and its dynamic per-language `loadLanguage()` import entirely for that language — no loading skeleton either, since this path never waits on that singleton. shiki's main entry point (what the default path imports) bundles a dynamic `import()` per bundled language (~200 of them), since a bundler can't statically narrow which of those a `loadLanguage(lang: string)` call might request at runtime; `shiki/core`'s fine-grained API has no such table, so a consumer who pins its full, known language set this way gets a build output scoped to just those languages instead of shiki's entire bundled set. A `language` value absent from `languages` (or left unset, or when `languages` itself is unset) still falls back to the ordinary dynamic-import path unchanged — this is a partial, additive opt-in, not a replacement for it.
3704
+ - 82a3419: Fixed 'confirm()''s own usage example to import from the granular subpath
3705
+ ('@aceshooting/lyra-ui/components/dialog/confirm.js') instead of the root barrel
3706
+ ('@aceshooting/lyra-ui') — following the root-barrel example as written previously pulled in the
3707
+ library's entire ~80-component side-effect-import chain into a consumer's eager bundle
3708
+ (confirmed via a real build: +79 KB gzip regression, fixed by switching to the subpath import).
3709
+ No code changed, documentation only.
3710
+ - 82a3419: Add `heading`/`closable` convenience chrome and a `--lr-dialog-max-width` token to `<lr-dialog>`. `<lr-dialog>` previously required a consumer to hand-build any visible title bar (by slotting a real heading element) and any close affordance (via a footer button wired to `close()`) — `heading` now renders a visible header row with that text when no heading element is slotted (still deferring to a slotted heading, unchanged, when present), and `closable` renders a built-in close (X) button in that same header row, wired through the exact same `close()` path Escape/backdrop-dismiss already use, with reason `'close-button'`. `[part="panel"]`'s previously-hardcoded `max-inline-size: min(32rem, 100%)` is now `min(var(--lr-dialog-max-width, 32rem), 100%)`, mirroring `<lr-media-card>`'s `--lr-media-card-max-height` — the default stays exactly `32rem` when unset. All three are additive/opt-in; existing consumers see no behavior change.
3711
+ - 82a3419: `<lr-heatmap>`'s calendar mode gained four additive extensions. `firstDayOfWeek` (0-6, Sunday-first default, same numbering as `CalendarCellPos.weekday`) anchors the week grid at a different weekday instead of always Sunday, threaded into `buildCalendarGrid()`'s new `firstDayOfWeek` parameter; matrix mode ignores it. `rowY` overrides the y-origin computed for each weekday row, the vertical analogue of the existing `columnX`, consulted consistently by drawing, hit-testing, and the keyboard focus ring via a new private `rowYFor()` helper mirroring `columnXFor()`'s exact dispatch-with-computed-fallback shape. The previously matrix-mode-only `cellSize`/`fitToWidth` properties now also size calendar mode's grid, replacing its hardcoded 11px cell constant when explicitly set (unset, calendar mode keeps that original 11px default). The previously matrix-mode-only `scale` property now also governs calendar mode's bucketing: `scale="sqrt"` compresses via the same square-root magnitude compression matrix mode uses instead of always calling `quartileBucket()`, so one heavy day doesn't wash out a skewed dataset; the default `"linear"` preserves today's exact quartile-only calendar behavior. All four are opt-in and no-ops when left unset/default.
3712
+ - 82a3419: `<lr-lite-chart>` gained seven additive properties. `pointText` overrides the per-bar/per-point `<title>`/`aria-label` tooltip text (mirrors `lr-heatmap`'s `cellText` hook), falling back to today's exact raw-value template when unset. `roundedBars` draws bars as a rounded-top-corner path instead of a square-cornered rect (default `false` keeps the plain rect). `skipZero` omits a bar entirely — no mark, no `tabindex`, no tooltip — for a value that is exactly `0`, instead of today's zero-height-but-focusable bar (default `false` unchanged). `padLeft`/`barGapRatio` override the internal `PAD_LEFT`/`BAR_GROUP_GAP` layout constants (36px / 0.2 respectively) when set. `scale` (`'linear' | 'sqrt'`, `type="bar"` only) switches the bar-height mapping from the default linear `niceDomain` fraction to a `Math.sqrt(value / domainMax)` compression mirroring `lr-heatmap`'s matrix-mode `sqrt` scale, so a skewed dataset's smaller bars aren't washed out by one dominant value; `type="line"` ignores `scale` entirely. `hideAxis` suppresses `renderGrid()`'s gridlines and y-axis tick labels altogether (x-axis category labels are unaffected). All seven are opt-in and no-ops when left unset/`false`.
3713
+ - 82a3419: `<lr-markdown>` gains four additive properties. Every rendered `<img>` now carries a `part="img"` (with a matching `[part='img'] { max-width: 100% }` base style), alongside the existing `content`/`heading`/`code-block`/`link`/`table`/`blockquote` parts — previously images went through marked's default renderer with no styling hook at all. `heading-offset` (default `0`) shifts every rendered heading's depth before emitting `<h${depth}>`, clamped to `<h1>`–`<h6>`, letting a consumer nest rendered markdown under an existing heading level without losing document outline. `link-target` (default `'_blank'`, unchanged) can now be set to `null`/`''` to omit `target`/`rel="noopener noreferrer"` entirely and open links in the same tab, instead of always forcing a new tab. `eager-load` (default `false`) skips `connectedCallback()`'s async `marked`/`dompurify` `import()` and renders synchronously whenever the shared module cache (`markdown-loader.ts`) is already warm — e.g. a second `<lr-markdown>` on the same page, or a consumer that primes `loadMarkdownDeps()` at startup — avoiding the brief plain-text fallback paint that otherwise happens on every connect, even when both peers load without error. All four are opt-in; unset, output is byte-identical to before.
3714
+ - 82a3419: `<lr-menu-item>` gained a `type` property (`'normal' | 'checkbox'`, default `'normal'`) and a `checked` boolean, mirroring `wa-dropdown-item`'s identical `type="checkbox"` pattern for building things like a "Word wrap" or "Show minimap" toggle inside a `<lr-menu>`. A `type="checkbox"` item renders `role="menuitemcheckbox"` (instead of `role="menuitem"`) with `aria-checked` reflecting `checked` and a checkmark glyph shown once checked; activating it (click, or Enter/Space via a parent `<lr-menu>`'s roving-focus handling) toggles `checked` and fires a new `lr-menu-item-change` event (`detail: { value, checked }`) in addition to — not instead of — the existing `lr-menu-item-select`, so a parent menu still closes and re-fires its consolidated `lr-menu-select` exactly as before. `type="normal"` (the default, and every existing `<lr-menu-item>` in the wild) is completely unaffected: same role, same rendering, same events as prior releases.
3715
+ - 82a3419: `<lr-model-select>`: added a `label` property that renders a visible `part="form-control-label"` title above the trigger/combobox, paired with it via `for`/`id`, mirroring `<lr-select>`'s own `label` exactly. Once non-empty it also takes over as the accessible-name source, with an explicit host `aria-label` still winning over it (same precedence as `lr-select`). Unset (the default), the control keeps today's exact `aria-label || placeholder || 'Model'` fallback chain unchanged.
3716
+ - 82a3419: `<lr-select>`'s single-enabled-option auto-commit trigger (added 1.3.0) is now gated behind a new `autoCommitSingleOption` property, default `false`. Previously this behavior was unconditional as soon as exactly one `<lr-option>` was enabled, silently swapping the trigger's ARIA role and keyboard model on any consumer whose option list happened to narrow to one entry at runtime. Existing consumers now get the pre-1.3.0 combobox trigger unless they explicitly opt in with `auto-commit-single-option`.
3717
+ - 82a3419: `<lr-split>`'s `collapseState` is now a public accessor with force/auto semantics mirroring `<lr-app-rail>`'s `mode`: it was previously derived only from the `ResizeObserver`-measured container width, but assigning a concrete `'wide'`/`'rail'`/`'floating'` value now pins it there (ignoring further measurement) until released back to automatic tracking by assigning the write-only `'auto'` sentinel, which immediately re-derives it from the current width. `lr-split-collapse-change` fires on both a forced assignment and a release-to-auto, exactly as it already did for a breakpoint crossing, and only when the effective state actually changes. The `'floating'` tier also gains a new `open` property (default `false`): previously this state always rendered its pane as an always-visible overlay card the moment the container narrowed past `float-breakpoint`; it's now a hidden-by-default drawer — the pane renders nothing (hidden, out of the accessibility tree) until a consumer sets `open`, at which point it renders with a `[part="backdrop"]` scrim, traps focus, and closes (`open = false`) on Escape or a backdrop click, mirroring `<lr-app-rail>`'s mobile overlay. `collapseState` still reflects to a `collapse-state` attribute for CSS targeting. `open` defaulting to `false` is a deliberate behavior change for the `'floating'` tier specifically (it was previously always visible); every other collapse behavior, and `collapse="none"` (the default), is unaffected.
3718
+ - 82a3419: `<lr-tabs>` can now render a leading icon inside a generated tab button without changing its accessible name. Give a panel's tab an extra direct-child sibling of `<lr-tabs>` carrying `slot="<id>-icon"` (any markup — an inline SVG, an emoji span, a custom icon element) and it renders ahead of the label inside that tab's button, wrapped in a new `part="tab-icon"` `aria-hidden="true"` span so it's always excluded from the button's accessible name (which stays exactly the `label` attribute's text, as before). A tab with no matching `<id>-icon` sibling renders no icon wrapper at all, so every existing text-only `<lr-tabs>` is byte-for-byte unaffected. A named slot (rather than an `icon="<name>"` attribute keyed into this library's internal `icons.ts`) was chosen because that internal set is a small closed vocabulary of chrome glyphs for this library's own components, not a public icon registry — a slot lets a consumer supply an arbitrary, domain-specific icon instead.
3719
+
3720
+ ## 2.0.0
3721
+
3722
+ ### Major Changes
3723
+
3724
+ - 8b5f729: **Breaking:** the root `@aceshooting/lyra-ui` entry point no longer re-exports or
3725
+ side-effect-registers the optional-peer-dependent component families — `<lr-chart>`
3726
+ and its typed subclasses, `<lr-box-plot>`, `<lr-histogram>`, `<lr-map>`, and
3727
+ `<lr-graph>`. Import each of these directly from its own subpath instead (the README
3728
+ already recommends granular subpath imports as the primary pattern):
3729
+
3730
+ ```js
3731
+ import "@aceshooting/lyra-ui/components/chart/chart.js";
3732
+ import "@aceshooting/lyra-ui/components/map/map.js";
3733
+ ```
3734
+
3735
+ Why: the root barrel previously re-exported every component's public API from one
3736
+ `lyra.ts` file, so TypeScript had to resolve `chart.js`/`maplibre-gl`/`d3-force`'s type
3737
+ declarations even for a consumer who only imports an unrelated component (e.g.
3738
+ `LyraEmpty`) from the package root — a hard compile error for anyone who hadn't
3739
+ installed every optional peer. Splitting these families out of the root barrel means
3740
+ importing `@aceshooting/lyra-ui` (or any of its remaining members) never requires an
3741
+ optional peer's types to be resolvable.
3742
+
3743
+ Every other component (including `<lr-lite-chart>`, which has zero peer
3744
+ dependencies) is unaffected — the root barrel still re-exports/registers everything
3745
+ else exactly as before.
3746
+
3747
+ ### Minor Changes
3748
+
3749
+ - 144ad8f: Add a `compact` flag tier and expose three fidelity tiers via `variant`.
3750
+
3751
+ `@aceshooting/lyra-flags`: the ~65 emblem flags now ship a tiny WebP raster at
3752
+ `flags/compact/<code>.webp` (~1–3 KB) alongside the standard vector and the pristine `detailed`
3753
+ original. `flagUrl(code, { variant: 'compact' | 'standard' | 'detailed' })` selects a tier,
3754
+ code-split per flag _and_ per tier so a bundled app ships only the tiers it actually uses. The
3755
+ `standard` tier was also re-derived from the pristine originals so every flag is now under 80 KB
3756
+ (no fidelity loss perceptible at card/row scale).
3757
+
3758
+ `@aceshooting/lyra-ui`: `<lr-flag>` gains a `variant="compact" | "standard" | "detailed"`
3759
+ property — a tiny raster for icon-scale use (menu items, language selectors), the default
3760
+ icon-optimized vector for card/row sizes, or the pristine full-detail vector for hero display.
3761
+ The `detailed` boolean is deprecated but kept working as an alias for `variant="detailed"`.
3762
+
3763
+ - 2a7390d: Fix `lr-heatmap` calendar mode's month/weekday axis labels to follow the runtime locale instead of hardcoded English, and add a `columnX` override so a calendar's week columns can be pixel-aligned with an external coordinate function.
3764
+ - 43864d6: Add `lr-lite-chart` `layout="scroll"` (fixed-width, horizontally-scrollable bars via `barWidth`), `maxLabels` axis-label decimation, and a `barX` coordinate override for pixel-aligning bars with a sibling `lr-heatmap`.
3765
+ - 043b7b0: Move `LyraSelectSize` above `<lr-select>`'s class JSDoc block so `custom-elements.json` correctly documents `lr-select` as a custom element.
3766
+ - 7bbe3d2: Add `lr-split` opt-in responsive collapse (`collapse="start"|"end"`, `rail-width`, `rail-breakpoint`, `float-breakpoint`): below `rail-breakpoint` the chosen pane clamps to a fixed rail width, below `float-breakpoint` it becomes an absolutely-positioned floating overlay, both signaled via a `data-collapse-state` attribute/dataset marker and the new `lr-split-collapse-change` event.
3767
+ - f14165f: `<lr-stat>` breakdown rows (`StatRow`) gain an optional `exactValue` field, mirroring the headline value's tooltip: setting it renders a `title` tooltip and makes that row's `[part='row-value']` keyboard-focusable, independently per row.
3768
+ - d62725d: `lr-table`'s `[part='reveal-columns-button']` now renders only when a `priority` column is actually hidden by the `@container` breakpoints (or `showAllColumns` force-visible mode is active), instead of whenever any column merely declares a `priority`; the new `columnsHidden` reactive property and `lr-columns-hidden-change` event expose the same real-time state to consumers.
3769
+
3770
+ ### Patch Changes
3771
+
3772
+ - Updated dependencies [144ad8f]
3773
+ - @aceshooting/lyra-flags@1.3.0
3774
+
3775
+ ## 1.3.0
3776
+
3777
+ ### Minor Changes
3778
+
3779
+ - 6358479: Added a "Conversation & Agent UI" family: chat/tool-call/agent-config building blocks for
3780
+ streaming AI interfaces, plus the general-purpose primitives (dialog, tabs, checkbox, switch,
3781
+ menu, chip, JSON viewer, live region, markdown, code block) they're built from. No breaking
3782
+ changes to any existing component.
3783
+
3784
+ New tags: `lr-dialog`/`confirm()`, `lr-tabs`, `lr-checkbox`, `lr-switch`,
3785
+ `lr-json-viewer`, `lr-live-region` (+ `internal/announcer.ts`'s throttled `Announcer`),
3786
+ `lr-markdown` (needs the optional peers `marked`/`dompurify`), `lr-chat-message`,
3787
+ `lr-typing-indicator`, `lr-tool-call-chip`, `lr-tool-result-view` (+ its
3788
+ `registerToolRenderer()` renderer registry), `lr-tool-result-dialog`, `lr-chat-composer`
3789
+ (form-associated), `lr-attachment-chip`, `lr-stream-status`, `lr-virtual-list`,
3790
+ `lr-conversation-item`, `lr-model-select`, `lr-slider` (form-associated),
3791
+ `lr-tool-select-dialog`, `lr-citation-badge`, `lr-source-list`/`lr-source-card`,
3792
+ `lr-app-rail`, `lr-responsive-panel`, `lr-mention-popover`, `lr-streaming-text`,
3793
+ `lr-thinking-panel`, `lr-generation-status`, `lr-code-block` (needs the optional peer
3794
+ `shiki`), `lr-tool-approval-dialog`, `lr-tool-param-form`, `lr-menu`/`lr-menu-item`,
3795
+ `lr-chip`/`lr-chip-group`, `lr-model-settings-panel`, `lr-context-meter`,
3796
+ `lr-dock-panel`, `lr-document-preview`, `lr-media-card`, `lr-attachment-trigger`,
3797
+ `lr-kbd`, `lr-result-card`/`lr-result-field`.
3798
+
3799
+ Also extends `internal/rtl.ts` with `rtlAwareSide()`/`rtlAwarePlacement()` (mirrors a physical
3800
+ `left`/`right` value, or the `left`/`right` component of a Floating UI `Placement`, under RTL) —
3801
+ used by `lr-menu`'s `placement` property so an explicit `placement="left-start"` still anchors
3802
+ to the trailing edge instead of the physical left when the page is RTL.
3803
+
3804
+ - 6358479: `<lr-select>`: when exactly one `<lr-option>` is enabled, the trigger now auto-commits that
3805
+ option on click or Arrow Up/Down instead of opening a single-row listbox — no chevron, no popup,
3806
+ `role="button"` instead of `role="combobox"`. Avoids an unnecessary extra click for "only one
3807
+ choice available" states (e.g. a filtered picker that's converged to a single match). Multi-option
3808
+ selects are unaffected; `value`/validity defaults are unchanged. Not gated behind a new prop — this
3809
+ is the new default trigger behavior for any select with a single enabled option.
3810
+
3811
+ ## 1.2.0
3812
+
3813
+ ### Minor Changes
3814
+
3815
+ - 6e832d5: `<lr-chart>`: added `IntersectionObserver`-gated lazy redraw and content-signature memoization — a
3816
+ chart skips calling into Chart.js while scrolled off-screen (redrawing once when it re-enters the
3817
+ viewport) or when none of its content-affecting properties (`type`, `labels`, `datasets`, `legend`,
3818
+ `area`, `xLabel`, `yLabel`, `y2Label`, `beginAtZero`, `horizontal`, `stacked`, `config`) have actually
3819
+ changed since the last draw. `refreshTheme()` is unaffected and always redraws.
3820
+ - 9d36af5: `<lr-combobox>`: the input's accessible name now checks a host-level `aria-label` attribute before
3821
+ falling back to `label`/`placeholder`/`"Combobox"` — previously a plain `aria-label` on
3822
+ `<lr-combobox>` was silently ignored. Matches the same fix in `<lr-select>`.
3823
+ - 0b3ea6c: `<lr-flag>`: added a `detailed` boolean property that requests the pristine, full-detail source SVG
3824
+ for the minority of flags whose default rendering was recently optimized for icon scale (e.g. `es`,
3825
+ `pt`, `sv` — see the `@aceshooting/lyra-flags` changeset). A safe no-op for every other flag. Useful
3826
+ for a flag rendered larger than icon scale (e.g. a hero display) where the extra illustrative detail
3827
+ is actually visible.
3828
+ - 2027e3f: `<lr-flag>`: the default accessible name (`alt`, used when `label` is unset) is now a human-readable
3829
+ region name via `Intl.DisplayNames` (e.g. `language="en"` → `"United Kingdom"`) instead of the bare
3830
+ uppercase country code (`"GB"`, previously read letter-by-letter by most screen readers).
3831
+ - 49569ed: `<lr-heatmap>`: fixed `role="img"` conflicting with the canvas's own focusable, keyboard-interactive
3832
+ descendant (arrow-key roving focus, Enter/Space activation) — now `role="group"`, matching
3833
+ `lr-lite-chart`/`lr-word-cloud`'s existing pattern. Added `cellText?: (pos, value) => string`, a
3834
+ formatter hook for the per-cell hover tooltip and keyboard live-region announcement (both draw from the
3835
+ built-in English template by default; this is additive, not breaking). Also fixed calendar mode's date
3836
+ label formatting, which hardcoded the literal `'en'` locale instead of the runtime locale.
3837
+ - ef74f4a: `<lr-lite-chart>`: added `tickFormat?: (value: number) => string` to customize y-axis tick label
3838
+ formatting (e.g. currency, duration) instead of the built-in nice-number formatter. Also added
3839
+ `IntersectionObserver`-gated lazy rendering and content-signature memoization — a chart skips
3840
+ recomputing its grid/marks while scrolled off-screen or when none of its content-affecting properties
3841
+ (`type`, `labels`, `datasets`, `legend`, `xLabel`, `yLabel`, `beginAtZero`, `stacked`, plot size) have
3842
+ actually changed since the last render.
3843
+ - 22cf001: `<lr-select>`: added a `size` property (`xs`/`s`/`m`/`l`/`xl`, default `m`, same scale as
3844
+ `lr-toast-item`'s `size`) for compact toolbar placements that don't fit the default trigger height.
3845
+ Also, the trigger's accessible name now checks a host-level `aria-label` attribute before falling back
3846
+ to `label`/`placeholder`/`"Select"` — previously a plain `aria-label` on `<lr-select>` was silently
3847
+ ignored.
3848
+ - 4bf80aa: `<lr-stat>`: added `exact-value` (shown as a hover/focus tooltip on the headline value, e.g.
3849
+ `value="$1.2K" exact-value="$1,204.37"`), a `sub` property/slot (a secondary line distinct from
3850
+ `caption`, e.g. a comparison-period label), a `prose` boolean (renders `value` as smaller/lighter text
3851
+ with `unit` hidden, for a loading/status message in place of a numeric value), and a `compact` boolean
3852
+ (tighter padding for constrained spaces — same convention as `lr-empty`'s and `lr-widget`'s
3853
+ `compact`).
3854
+ - c8206f8: `<lr-widget>`: added `fullscreen-inset` (a raw CSS `inset` shorthand, e.g. `"0 0 0 240px"`, applied to
3855
+ the fullscreen panel and backdrop instead of the default `var(--lr-space-l)` on every side — for apps
3856
+ with a persistent sidebar/toolbar that should stay visible during fullscreen) and a `compact` boolean
3857
+ (tighter header/body padding), matching `lr-empty`'s existing `compact` convention.
3858
+ - a768a20: `<lr-word-cloud>`: fixed the rendered `<svg>` not respecting a host-assigned height —
3859
+ `[part='base']` had no `block-size` rule, so the internal `svg { block-size: 100% }` resolved against
3860
+ an indefinite containing-block height and fell back to the spiral layout's own intrinsic size instead,
3861
+ overflowing past the host's box. `[part='base']` now constrains to `block-size: 100%`, matching the
3862
+ component's own documented `<lr-word-cloud style="height: 20rem">` usage pattern.
3863
+
3864
+ ### Patch Changes
3865
+
3866
+ - Updated dependencies [da766cb]
3867
+ - @aceshooting/lyra-flags@1.2.0
3868
+
3869
+ ## 1.1.0
3870
+
3871
+ ### Minor Changes
3872
+
3873
+ - c033ec0: `@aceshooting/lyra-flags`: `flagUrl(code)` is now genuinely code-split per flag — each code is
3874
+ its own dynamically-`import()`ed chunk, so using it (directly, or via `<lr-flag
3875
+ country=...>`/`<lr-flag language=...>`) only ever fetches the flags actually requested at
3876
+ runtime, not all 249. This makes `flagUrl()` `async` (**breaking**: `Promise<string | undefined>`
3877
+ instead of `string`). `FLAG_URLS` (the old synchronous, eager, all-249-at-once map) is no longer
3878
+ exported from the package root — the equivalent for a consumer that genuinely wants every flag up
3879
+ front (e.g. a flag-picker listing every country) is the new `flagUrls()` (`async`, resolves the
3880
+ full map). `FLAG_LOADERS` (the new lazy per-code map `flagUrl()` is built on) is exported directly
3881
+ for consumers that want the per-code laziness without going through `flagUrl()`.
3882
+
3883
+ `@aceshooting/lyra-ui`: `<lr-flag>` transparently picks up the lazy-loading fix — no changes
3884
+ needed at call sites using `country`/`language`. Also adds a new `src` property: a pre-resolved
3885
+ flag image URL that takes precedence over `country`/`language` and skips the peer-package lookup
3886
+ (and its loading-skeleton round trip) entirely, for consumers who already have a flag's URL at
3887
+ build time (e.g. via `import frUrl from '@aceshooting/lyra-flags/flags/fr.svg?url'`).
3888
+
3889
+ - c033ec0: Added `<lr-lite-chart>` — a dependency-free bar/line chart (plain SVG/DOM rendering, zero peer
3890
+ dependencies) for projects whose architecture forbids a charting dependency outright. Covers
3891
+ grouped/stacked bars, multi-series lines, per-point click (`lr-point-click`, same detail shape as
3892
+ `lr-chart`'s), and hover tooltips via native SVG `<title>`. Not a full `lr-chart` replacement —
3893
+ no zoom/pan, no pie/doughnut/radar/scatter/bubble types, no horizontal/dual-y-axis, no raw-config
3894
+ passthrough. Reuses `lr-chart`'s `--lr-chart-*` theme token names for free cross-component
3895
+ theming.
3896
+ - c033ec0: Added `<lr-word-cloud>` — a dependency-free SVG word/tag cloud, laid out via an outward
3897
+ Archimedean-spiral placement search (heaviest word first). Supports `linear`/`sqrt` weight-to-font
3898
+ scaling, optional `mixed` (rotated) orientation, per-word or per-`group` coloring with a themeable
3899
+ `--lr-word-cloud-color-1..8` palette, and roving-tabindex keyboard navigation matching
3900
+ `lr-heatmap`'s pattern (a single tab stop, arrow keys, Home/End, a live-region announcement).
3901
+
3902
+ Also a hardening pass across the rest of the library — real bugs fixed, not just polish:
3903
+
3904
+ - `lr-skeleton`: `width`/`height` properties had zero visual effect (the custom property was set
3905
+ on the wrong shadow-DOM node); now actually resizes the placeholder.
3906
+ - `lr-combobox`: setting `open` directly (bypassing `show()`) never wired up click-outside or
3907
+ fired `lr-show`/`lr-hide`; picking a row or clearing while using `source` left stale async
3908
+ results displayed; a `<lr-option selected>` appended after the first slotchange was ignored;
3909
+ two nameless `multiple` comboboxes in the same form merged their submitted values; a pending
3910
+ debounced `source` fetch could fire after the element was removed.
3911
+ - `lr-chart`: bubble-chart series got a categorical (not numeric) x-axis, collapsing every point
3912
+ onto one tick; `resetZoom()` double-emitted `lr-zoom`, briefly reporting the stale pre-reset
3913
+ `zoomed` state to `{ once: true }` listeners.
3914
+ - `lr-date-picker` / `lr-date-input`: the already-exported `clampDate()` was never actually
3915
+ wired in, so `goToDate()`/`goToToday()` could navigate to (and focus) an out-of-range date;
3916
+ locale/weekday-format/first-day-of-week wiring gained test coverage; outside-month placeholder
3917
+ cells are now `aria-hidden` only in rows that also have a real visible day.
3918
+ - `lr-tree`: mouse-driven expand/collapse/select could desync the roving-tabindex `activeId` from
3919
+ real DOM focus; arrow-key expand/collapse is now RTL-aware, matching `lr-split`/`lr-time-range`.
3920
+ - `lr-widget`: the fullscreen focus trap didn't pierce into a slotted custom element's own shadow
3921
+ root, letting focus escape to a hidden nested control.
3922
+ - `lr-toast-item`: the close button used the native `disabled` attribute, which force-blurs a
3923
+ focused element with nothing to restore it — switched to `aria-disabled`.
3924
+ - `lr-empty`: gained a live-region announcement when entering the empty state, matching
3925
+ `lr-skeleton`'s existing `role="status"` convention.
3926
+ - Accessibility, documentation, and test-coverage fixes across most other components; `llms.txt`,
3927
+ `llms-full.txt`, and both READMEs corrected for drift against the current API surface.
3928
+
3929
+ No breaking changes.
3930
+
3931
+ ### Patch Changes
3932
+
3933
+ - Updated dependencies [c033ec0]
3934
+ - @aceshooting/lyra-flags@1.1.0
3935
+
3936
+ ## 1.0.1
3937
+
3938
+ ### Patch Changes
3939
+
3940
+ - 436b1ce: Fix `scripts/publish.sh` to commit `CHANGELOG.md` and `custom-elements.json` with each release commit (previously only `package.json`/the lockfile were staged, leaving those generated files uncommitted after every release). Remove the redundant `.github/workflows/publish.yml` CI job, which always failed by re-publishing a version `publish.sh` had already shipped.
3941
+
3942
+ ## 1.0.0
3943
+
3944
+ ### Major Changes
3945
+
3946
+ - 99fb0e0: Added several new components
3947
+
3948
+ ### Patch Changes
3949
+
3950
+ - Updated dependencies [99fb0e0]
3951
+ - @aceshooting/lyra-flags@1.0.0
3952
+
3953
+ All notable changes to `@aceshooting/lyra-ui` are documented in this file.
3954
+
3955
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
3956
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
3957
+
3958
+ Release notes on GitHub (`gh release create --generate-notes`) are generated from commit
3959
+ history and may be more granular than this file; this file is the curated, human-readable
3960
+ summary.
3961
+
3962
+ ## [Unreleased]
3963
+
3964
+ No unreleased changes yet.
3965
+
3966
+ ## [0.1.3] baseline
3967
+
3968
+ Current published baseline at the time this changelog was introduced. Historical versions
3969
+ prior to 0.1.3 were not backfilled into this file — see git tags (`git tag -l`) and GitHub
3970
+ Releases for the full release history.
3971
+
3972
+ - Free, clean-room Lit 3 web-component library — an open-source companion to Web Awesome.
3973
+ - Tiered component set (layout/atoms, forms, overlays, data-viz/dashboard, temporal/graph,
3974
+ map/file/flag families) — see `packages/lyra-ui/llms.txt` and `llms-full.txt` for the full
3975
+ API reference.
3976
+ - `@aceshooting/lyra-flags` optional companion package for `<lr-flag>` artwork.
3977
+
3978
+ [Unreleased]: https://github.com/aceshooting/lyra-ui/compare/0.1.3...HEAD
3979
+ [0.1.3]: https://github.com/aceshooting/lyra-ui/releases/tag/0.1.3