@aceshooting/lyra-ui 2.0.0 → 2.2.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 (1133) hide show
  1. package/README.md +107 -19
  2. package/custom-elements.json +40136 -17102
  3. package/dist/components/app-rail/app-rail-item.class.d.ts +36 -0
  4. package/dist/components/app-rail/app-rail-item.class.d.ts.map +1 -0
  5. package/dist/components/app-rail/app-rail-item.class.js +77 -0
  6. package/dist/components/app-rail/app-rail-item.class.js.map +1 -0
  7. package/dist/components/app-rail/app-rail-item.d.ts +2 -0
  8. package/dist/components/app-rail/app-rail-item.d.ts.map +1 -0
  9. package/dist/components/app-rail/app-rail-item.js +5 -0
  10. package/dist/components/app-rail/app-rail-item.js.map +1 -0
  11. package/dist/components/app-rail/app-rail-item.styles.d.ts +2 -0
  12. package/dist/components/app-rail/app-rail-item.styles.d.ts.map +1 -0
  13. package/dist/components/app-rail/app-rail-item.styles.js +71 -0
  14. package/dist/components/app-rail/app-rail-item.styles.js.map +1 -0
  15. package/dist/components/app-rail/app-rail.class.d.ts +213 -0
  16. package/dist/components/app-rail/app-rail.class.d.ts.map +1 -0
  17. package/dist/components/app-rail/app-rail.class.js +545 -0
  18. package/dist/components/app-rail/app-rail.class.js.map +1 -0
  19. package/dist/components/app-rail/app-rail.d.ts +3 -171
  20. package/dist/components/app-rail/app-rail.d.ts.map +1 -0
  21. package/dist/components/app-rail/app-rail.js +4 -481
  22. package/dist/components/app-rail/app-rail.js.map +1 -0
  23. package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
  24. package/dist/components/app-rail/app-rail.styles.d.ts.map +1 -0
  25. package/dist/components/app-rail/app-rail.styles.js +42 -10
  26. package/dist/components/app-rail/app-rail.styles.js.map +1 -0
  27. package/dist/components/attachment-chip/attachment-chip.class.d.ts +151 -0
  28. package/dist/components/attachment-chip/attachment-chip.class.d.ts.map +1 -0
  29. package/dist/components/attachment-chip/attachment-chip.class.js +370 -0
  30. package/dist/components/attachment-chip/attachment-chip.class.js.map +1 -0
  31. package/dist/components/attachment-chip/attachment-chip.d.ts +2 -114
  32. package/dist/components/attachment-chip/attachment-chip.d.ts.map +1 -0
  33. package/dist/components/attachment-chip/attachment-chip.js +3 -310
  34. package/dist/components/attachment-chip/attachment-chip.js.map +1 -0
  35. package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
  36. package/dist/components/attachment-chip/attachment-chip.styles.d.ts.map +1 -0
  37. package/dist/components/attachment-chip/attachment-chip.styles.js +29 -13
  38. package/dist/components/attachment-chip/attachment-chip.styles.js.map +1 -0
  39. package/dist/components/attachment-trigger/attachment-trigger.class.d.ts +106 -0
  40. package/dist/components/attachment-trigger/attachment-trigger.class.d.ts.map +1 -0
  41. package/dist/components/attachment-trigger/attachment-trigger.class.js +227 -0
  42. package/dist/components/attachment-trigger/attachment-trigger.class.js.map +1 -0
  43. package/dist/components/attachment-trigger/attachment-trigger.d.ts +4 -83
  44. package/dist/components/attachment-trigger/attachment-trigger.d.ts.map +1 -0
  45. package/dist/components/attachment-trigger/attachment-trigger.js +5 -256
  46. package/dist/components/attachment-trigger/attachment-trigger.js.map +1 -0
  47. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
  48. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts.map +1 -0
  49. package/dist/components/attachment-trigger/attachment-trigger.styles.js +38 -11
  50. package/dist/components/attachment-trigger/attachment-trigger.styles.js.map +1 -0
  51. package/dist/components/chart/bar-chart.class.d.ts +15 -0
  52. package/dist/components/chart/bar-chart.class.d.ts.map +1 -0
  53. package/dist/components/chart/bar-chart.class.js +10 -0
  54. package/dist/components/chart/bar-chart.class.js.map +1 -0
  55. package/dist/components/chart/bar-chart.d.ts +3 -14
  56. package/dist/components/chart/bar-chart.d.ts.map +1 -0
  57. package/dist/components/chart/bar-chart.js +4 -25
  58. package/dist/components/chart/bar-chart.js.map +1 -0
  59. package/dist/components/chart/box-plot.class.d.ts +78 -0
  60. package/dist/components/chart/box-plot.class.d.ts.map +1 -0
  61. package/dist/components/chart/box-plot.class.js +320 -0
  62. package/dist/components/chart/box-plot.class.js.map +1 -0
  63. package/dist/components/chart/box-plot.d.ts +3 -53
  64. package/dist/components/chart/box-plot.d.ts.map +1 -0
  65. package/dist/components/chart/box-plot.js +4 -189
  66. package/dist/components/chart/box-plot.js.map +1 -0
  67. package/dist/components/chart/box-plot.styles.d.ts +1 -0
  68. package/dist/components/chart/box-plot.styles.d.ts.map +1 -0
  69. package/dist/components/chart/box-plot.styles.js +15 -1
  70. package/dist/components/chart/box-plot.styles.js.map +1 -0
  71. package/dist/components/chart/bubble-chart.class.d.ts +19 -0
  72. package/dist/components/chart/bubble-chart.class.d.ts.map +1 -0
  73. package/dist/components/chart/bubble-chart.class.js +14 -0
  74. package/dist/components/chart/bubble-chart.class.js.map +1 -0
  75. package/dist/components/chart/bubble-chart.d.ts +3 -18
  76. package/dist/components/chart/bubble-chart.d.ts.map +1 -0
  77. package/dist/components/chart/bubble-chart.js +4 -29
  78. package/dist/components/chart/bubble-chart.js.map +1 -0
  79. package/dist/components/chart/chart-loader.d.ts +12 -9
  80. package/dist/components/chart/chart-loader.d.ts.map +1 -0
  81. package/dist/components/chart/chart-loader.js +1 -0
  82. package/dist/components/chart/chart-loader.js.map +1 -0
  83. package/dist/components/chart/chart.class.d.ts +209 -0
  84. package/dist/components/chart/chart.class.d.ts.map +1 -0
  85. package/dist/components/chart/chart.class.js +651 -0
  86. package/dist/components/chart/chart.class.js.map +1 -0
  87. package/dist/components/chart/chart.d.ts +3 -152
  88. package/dist/components/chart/chart.d.ts.map +1 -0
  89. package/dist/components/chart/chart.js +4 -539
  90. package/dist/components/chart/chart.js.map +1 -0
  91. package/dist/components/chart/chart.styles.d.ts +1 -0
  92. package/dist/components/chart/chart.styles.d.ts.map +1 -0
  93. package/dist/components/chart/chart.styles.js +5 -4
  94. package/dist/components/chart/chart.styles.js.map +1 -0
  95. package/dist/components/chart/doughnut-chart.class.d.ts +15 -0
  96. package/dist/components/chart/doughnut-chart.class.d.ts.map +1 -0
  97. package/dist/components/chart/doughnut-chart.class.js +10 -0
  98. package/dist/components/chart/doughnut-chart.class.js.map +1 -0
  99. package/dist/components/chart/doughnut-chart.d.ts +3 -14
  100. package/dist/components/chart/doughnut-chart.d.ts.map +1 -0
  101. package/dist/components/chart/doughnut-chart.js +4 -25
  102. package/dist/components/chart/doughnut-chart.js.map +1 -0
  103. package/dist/components/chart/histogram-bin.d.ts +6 -2
  104. package/dist/components/chart/histogram-bin.d.ts.map +1 -0
  105. package/dist/components/chart/histogram-bin.js +13 -4
  106. package/dist/components/chart/histogram-bin.js.map +1 -0
  107. package/dist/components/chart/histogram.class.d.ts +23 -0
  108. package/dist/components/chart/histogram.class.d.ts.map +1 -0
  109. package/dist/components/chart/histogram.class.js +91 -0
  110. package/dist/components/chart/histogram.class.js.map +1 -0
  111. package/dist/components/chart/histogram.d.ts +3 -22
  112. package/dist/components/chart/histogram.d.ts.map +1 -0
  113. package/dist/components/chart/histogram.js +4 -98
  114. package/dist/components/chart/histogram.js.map +1 -0
  115. package/dist/components/chart/histogram.styles.d.ts +1 -0
  116. package/dist/components/chart/histogram.styles.d.ts.map +1 -0
  117. package/dist/components/chart/histogram.styles.js +1 -0
  118. package/dist/components/chart/histogram.styles.js.map +1 -0
  119. package/dist/components/chart/line-chart.class.d.ts +15 -0
  120. package/dist/components/chart/line-chart.class.d.ts.map +1 -0
  121. package/dist/components/chart/line-chart.class.js +10 -0
  122. package/dist/components/chart/line-chart.class.js.map +1 -0
  123. package/dist/components/chart/line-chart.d.ts +3 -14
  124. package/dist/components/chart/line-chart.d.ts.map +1 -0
  125. package/dist/components/chart/line-chart.js +4 -25
  126. package/dist/components/chart/line-chart.js.map +1 -0
  127. package/dist/components/chart/lite-chart.class.d.ts +237 -0
  128. package/dist/components/chart/lite-chart.class.d.ts.map +1 -0
  129. package/dist/components/chart/lite-chart.class.js +757 -0
  130. package/dist/components/chart/lite-chart.class.js.map +1 -0
  131. package/dist/components/chart/lite-chart.d.ts +2 -137
  132. package/dist/components/chart/lite-chart.d.ts.map +1 -0
  133. package/dist/components/chart/lite-chart.js +3 -540
  134. package/dist/components/chart/lite-chart.js.map +1 -0
  135. package/dist/components/chart/lite-chart.styles.d.ts +1 -0
  136. package/dist/components/chart/lite-chart.styles.d.ts.map +1 -0
  137. package/dist/components/chart/lite-chart.styles.js +19 -10
  138. package/dist/components/chart/lite-chart.styles.js.map +1 -0
  139. package/dist/components/chart/pie-chart.class.d.ts +16 -0
  140. package/dist/components/chart/pie-chart.class.d.ts.map +1 -0
  141. package/dist/components/chart/pie-chart.class.js +11 -0
  142. package/dist/components/chart/pie-chart.class.js.map +1 -0
  143. package/dist/components/chart/pie-chart.d.ts +3 -15
  144. package/dist/components/chart/pie-chart.d.ts.map +1 -0
  145. package/dist/components/chart/pie-chart.js +4 -26
  146. package/dist/components/chart/pie-chart.js.map +1 -0
  147. package/dist/components/chart/polar-area-chart.class.d.ts +15 -0
  148. package/dist/components/chart/polar-area-chart.class.d.ts.map +1 -0
  149. package/dist/components/chart/polar-area-chart.class.js +10 -0
  150. package/dist/components/chart/polar-area-chart.class.js.map +1 -0
  151. package/dist/components/chart/polar-area-chart.d.ts +3 -14
  152. package/dist/components/chart/polar-area-chart.d.ts.map +1 -0
  153. package/dist/components/chart/polar-area-chart.js +4 -25
  154. package/dist/components/chart/polar-area-chart.js.map +1 -0
  155. package/dist/components/chart/radar-chart.class.d.ts +15 -0
  156. package/dist/components/chart/radar-chart.class.d.ts.map +1 -0
  157. package/dist/components/chart/radar-chart.class.js +10 -0
  158. package/dist/components/chart/radar-chart.class.js.map +1 -0
  159. package/dist/components/chart/radar-chart.d.ts +3 -14
  160. package/dist/components/chart/radar-chart.d.ts.map +1 -0
  161. package/dist/components/chart/radar-chart.js +4 -25
  162. package/dist/components/chart/radar-chart.js.map +1 -0
  163. package/dist/components/chart/scatter-chart.class.d.ts +16 -0
  164. package/dist/components/chart/scatter-chart.class.d.ts.map +1 -0
  165. package/dist/components/chart/scatter-chart.class.js +11 -0
  166. package/dist/components/chart/scatter-chart.class.js.map +1 -0
  167. package/dist/components/chart/scatter-chart.d.ts +3 -15
  168. package/dist/components/chart/scatter-chart.d.ts.map +1 -0
  169. package/dist/components/chart/scatter-chart.js +4 -26
  170. package/dist/components/chart/scatter-chart.js.map +1 -0
  171. package/dist/components/chat-composer/chat-composer.class.d.ts +145 -0
  172. package/dist/components/chat-composer/chat-composer.class.d.ts.map +1 -0
  173. package/dist/components/chat-composer/chat-composer.class.js +314 -0
  174. package/dist/components/chat-composer/chat-composer.class.js.map +1 -0
  175. package/dist/components/chat-composer/chat-composer.d.ts +2 -92
  176. package/dist/components/chat-composer/chat-composer.d.ts.map +1 -0
  177. package/dist/components/chat-composer/chat-composer.js +3 -283
  178. package/dist/components/chat-composer/chat-composer.js.map +1 -0
  179. package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
  180. package/dist/components/chat-composer/chat-composer.styles.d.ts.map +1 -0
  181. package/dist/components/chat-composer/chat-composer.styles.js +16 -5
  182. package/dist/components/chat-composer/chat-composer.styles.js.map +1 -0
  183. package/dist/components/chat-message/chat-message.class.d.ts +145 -0
  184. package/dist/components/chat-message/chat-message.class.d.ts.map +1 -0
  185. package/dist/components/chat-message/chat-message.class.js +270 -0
  186. package/dist/components/chat-message/chat-message.class.js.map +1 -0
  187. package/dist/components/chat-message/chat-message.d.ts +3 -113
  188. package/dist/components/chat-message/chat-message.d.ts.map +1 -0
  189. package/dist/components/chat-message/chat-message.js +4 -289
  190. package/dist/components/chat-message/chat-message.js.map +1 -0
  191. package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
  192. package/dist/components/chat-message/chat-message.styles.d.ts.map +1 -0
  193. package/dist/components/chat-message/chat-message.styles.js +13 -12
  194. package/dist/components/chat-message/chat-message.styles.js.map +1 -0
  195. package/dist/components/checkbox/checkbox.class.d.ts +123 -0
  196. package/dist/components/checkbox/checkbox.class.d.ts.map +1 -0
  197. package/dist/components/checkbox/checkbox.class.js +328 -0
  198. package/dist/components/checkbox/checkbox.class.js.map +1 -0
  199. package/dist/components/checkbox/checkbox.d.ts +2 -59
  200. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  201. package/dist/components/checkbox/checkbox.js +3 -228
  202. package/dist/components/checkbox/checkbox.js.map +1 -0
  203. package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
  204. package/dist/components/checkbox/checkbox.styles.d.ts.map +1 -0
  205. package/dist/components/checkbox/checkbox.styles.js +14 -6
  206. package/dist/components/checkbox/checkbox.styles.js.map +1 -0
  207. package/dist/components/chip/chip-group.class.d.ts +57 -0
  208. package/dist/components/chip/chip-group.class.d.ts.map +1 -0
  209. package/dist/components/chip/chip-group.class.js +129 -0
  210. package/dist/components/chip/chip-group.class.js.map +1 -0
  211. package/dist/components/chip/chip-group.d.ts +2 -56
  212. package/dist/components/chip/chip-group.d.ts.map +1 -0
  213. package/dist/components/chip/chip-group.js +3 -122
  214. package/dist/components/chip/chip-group.js.map +1 -0
  215. package/dist/components/chip/chip-group.styles.d.ts +1 -0
  216. package/dist/components/chip/chip-group.styles.d.ts.map +1 -0
  217. package/dist/components/chip/chip-group.styles.js +7 -6
  218. package/dist/components/chip/chip-group.styles.js.map +1 -0
  219. package/dist/components/chip/chip.class.d.ts +65 -0
  220. package/dist/components/chip/chip.class.d.ts.map +1 -0
  221. package/dist/components/chip/chip.class.js +121 -0
  222. package/dist/components/chip/chip.class.js.map +1 -0
  223. package/dist/components/chip/chip.d.ts +2 -64
  224. package/dist/components/chip/chip.d.ts.map +1 -0
  225. package/dist/components/chip/chip.js +3 -113
  226. package/dist/components/chip/chip.js.map +1 -0
  227. package/dist/components/chip/chip.styles.d.ts +1 -0
  228. package/dist/components/chip/chip.styles.d.ts.map +1 -0
  229. package/dist/components/chip/chip.styles.js +12 -11
  230. package/dist/components/chip/chip.styles.js.map +1 -0
  231. package/dist/components/citation-badge/citation-badge.class.d.ts +114 -0
  232. package/dist/components/citation-badge/citation-badge.class.d.ts.map +1 -0
  233. package/dist/components/citation-badge/citation-badge.class.js +317 -0
  234. package/dist/components/citation-badge/citation-badge.class.js.map +1 -0
  235. package/dist/components/citation-badge/citation-badge.d.ts +2 -113
  236. package/dist/components/citation-badge/citation-badge.d.ts.map +1 -0
  237. package/dist/components/citation-badge/citation-badge.js +3 -298
  238. package/dist/components/citation-badge/citation-badge.js.map +1 -0
  239. package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
  240. package/dist/components/citation-badge/citation-badge.styles.d.ts.map +1 -0
  241. package/dist/components/citation-badge/citation-badge.styles.js +12 -11
  242. package/dist/components/citation-badge/citation-badge.styles.js.map +1 -0
  243. package/dist/components/code-block/code-block.class.d.ts +141 -0
  244. package/dist/components/code-block/code-block.class.d.ts.map +1 -0
  245. package/dist/components/code-block/code-block.class.js +333 -0
  246. package/dist/components/code-block/code-block.class.js.map +1 -0
  247. package/dist/components/code-block/code-block.d.ts +3 -89
  248. package/dist/components/code-block/code-block.d.ts.map +1 -0
  249. package/dist/components/code-block/code-block.js +4 -315
  250. package/dist/components/code-block/code-block.js.map +1 -0
  251. package/dist/components/code-block/code-block.styles.d.ts +1 -0
  252. package/dist/components/code-block/code-block.styles.d.ts.map +1 -0
  253. package/dist/components/code-block/code-block.styles.js +25 -21
  254. package/dist/components/code-block/code-block.styles.js.map +1 -0
  255. package/dist/components/code-block/code-loader.d.ts +49 -5
  256. package/dist/components/code-block/code-loader.d.ts.map +1 -0
  257. package/dist/components/code-block/code-loader.js +64 -0
  258. package/dist/components/code-block/code-loader.js.map +1 -0
  259. package/dist/components/combobox/combobox.class.d.ts +205 -0
  260. package/dist/components/combobox/combobox.class.d.ts.map +1 -0
  261. package/dist/components/combobox/combobox.class.js +854 -0
  262. package/dist/components/combobox/combobox.class.js.map +1 -0
  263. package/dist/components/combobox/combobox.d.ts +3 -158
  264. package/dist/components/combobox/combobox.d.ts.map +1 -0
  265. package/dist/components/combobox/combobox.js +4 -751
  266. package/dist/components/combobox/combobox.js.map +1 -0
  267. package/dist/components/combobox/combobox.styles.d.ts +1 -0
  268. package/dist/components/combobox/combobox.styles.d.ts.map +1 -0
  269. package/dist/components/combobox/combobox.styles.js +34 -33
  270. package/dist/components/combobox/combobox.styles.js.map +1 -0
  271. package/dist/components/combobox/option.class.d.ts +44 -0
  272. package/dist/components/combobox/option.class.d.ts.map +1 -0
  273. package/dist/components/combobox/option.class.js +101 -0
  274. package/dist/components/combobox/option.class.js.map +1 -0
  275. package/dist/components/combobox/option.d.ts +2 -40
  276. package/dist/components/combobox/option.d.ts.map +1 -0
  277. package/dist/components/combobox/option.js +3 -109
  278. package/dist/components/combobox/option.js.map +1 -0
  279. package/dist/components/context-meter/context-meter.class.d.ts +54 -0
  280. package/dist/components/context-meter/context-meter.class.d.ts.map +1 -0
  281. package/dist/components/context-meter/context-meter.class.js +158 -0
  282. package/dist/components/context-meter/context-meter.class.js.map +1 -0
  283. package/dist/components/context-meter/context-meter.d.ts +2 -53
  284. package/dist/components/context-meter/context-meter.d.ts.map +1 -0
  285. package/dist/components/context-meter/context-meter.js +3 -148
  286. package/dist/components/context-meter/context-meter.js.map +1 -0
  287. package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
  288. package/dist/components/context-meter/context-meter.styles.d.ts.map +1 -0
  289. package/dist/components/context-meter/context-meter.styles.js +7 -12
  290. package/dist/components/context-meter/context-meter.styles.js.map +1 -0
  291. package/dist/components/conversation-item/conversation-item.class.d.ts +127 -0
  292. package/dist/components/conversation-item/conversation-item.class.d.ts.map +1 -0
  293. package/dist/components/conversation-item/conversation-item.class.js +259 -0
  294. package/dist/components/conversation-item/conversation-item.class.js.map +1 -0
  295. package/dist/components/conversation-item/conversation-item.d.ts +2 -128
  296. package/dist/components/conversation-item/conversation-item.d.ts.map +1 -0
  297. package/dist/components/conversation-item/conversation-item.js +3 -320
  298. package/dist/components/conversation-item/conversation-item.js.map +1 -0
  299. package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
  300. package/dist/components/conversation-item/conversation-item.styles.d.ts.map +1 -0
  301. package/dist/components/conversation-item/conversation-item.styles.js +12 -11
  302. package/dist/components/conversation-item/conversation-item.styles.js.map +1 -0
  303. package/dist/components/copy-button/copy-button.class.d.ts +37 -0
  304. package/dist/components/copy-button/copy-button.class.d.ts.map +1 -0
  305. package/dist/components/copy-button/copy-button.class.js +103 -0
  306. package/dist/components/copy-button/copy-button.class.js.map +1 -0
  307. package/dist/components/copy-button/copy-button.d.ts +2 -0
  308. package/dist/components/copy-button/copy-button.d.ts.map +1 -0
  309. package/dist/components/copy-button/copy-button.js +5 -0
  310. package/dist/components/copy-button/copy-button.js.map +1 -0
  311. package/dist/components/copy-button/copy-button.styles.d.ts +2 -0
  312. package/dist/components/copy-button/copy-button.styles.d.ts.map +1 -0
  313. package/dist/components/copy-button/copy-button.styles.js +40 -0
  314. package/dist/components/copy-button/copy-button.styles.js.map +1 -0
  315. package/dist/components/date-picker/calendar-core.d.ts +25 -0
  316. package/dist/components/date-picker/calendar-core.d.ts.map +1 -0
  317. package/dist/components/date-picker/calendar-core.js +52 -4
  318. package/dist/components/date-picker/calendar-core.js.map +1 -0
  319. package/dist/components/date-picker/date-input.class.d.ts +185 -0
  320. package/dist/components/date-picker/date-input.class.d.ts.map +1 -0
  321. package/dist/components/date-picker/date-input.class.js +676 -0
  322. package/dist/components/date-picker/date-input.class.js.map +1 -0
  323. package/dist/components/date-picker/date-input.d.ts +3 -101
  324. package/dist/components/date-picker/date-input.d.ts.map +1 -0
  325. package/dist/components/date-picker/date-input.js +4 -470
  326. package/dist/components/date-picker/date-input.js.map +1 -0
  327. package/dist/components/date-picker/date-input.styles.d.ts +1 -0
  328. package/dist/components/date-picker/date-input.styles.d.ts.map +1 -0
  329. package/dist/components/date-picker/date-input.styles.js +11 -10
  330. package/dist/components/date-picker/date-input.styles.js.map +1 -0
  331. package/dist/components/date-picker/date-picker.class.d.ts +121 -0
  332. package/dist/components/date-picker/date-picker.class.d.ts.map +1 -0
  333. package/dist/components/date-picker/date-picker.class.js +513 -0
  334. package/dist/components/date-picker/date-picker.class.js.map +1 -0
  335. package/dist/components/date-picker/date-picker.d.ts +2 -79
  336. package/dist/components/date-picker/date-picker.d.ts.map +1 -0
  337. package/dist/components/date-picker/date-picker.js +3 -432
  338. package/dist/components/date-picker/date-picker.js.map +1 -0
  339. package/dist/components/date-picker/date-picker.styles.d.ts +1 -0
  340. package/dist/components/date-picker/date-picker.styles.d.ts.map +1 -0
  341. package/dist/components/date-picker/date-picker.styles.js +10 -9
  342. package/dist/components/date-picker/date-picker.styles.js.map +1 -0
  343. package/dist/components/dialog/confirm.d.ts +1 -0
  344. package/dist/components/dialog/confirm.d.ts.map +1 -0
  345. package/dist/components/dialog/confirm.js +4 -6
  346. package/dist/components/dialog/confirm.js.map +1 -0
  347. package/dist/components/dialog/dialog.class.d.ts +137 -0
  348. package/dist/components/dialog/dialog.class.d.ts.map +1 -0
  349. package/dist/components/dialog/dialog.class.js +301 -0
  350. package/dist/components/dialog/dialog.class.js.map +1 -0
  351. package/dist/components/dialog/dialog.d.ts +2 -85
  352. package/dist/components/dialog/dialog.d.ts.map +1 -0
  353. package/dist/components/dialog/dialog.js +3 -270
  354. package/dist/components/dialog/dialog.js.map +1 -0
  355. package/dist/components/dialog/dialog.styles.d.ts +1 -0
  356. package/dist/components/dialog/dialog.styles.d.ts.map +1 -0
  357. package/dist/components/dialog/dialog.styles.js +54 -6
  358. package/dist/components/dialog/dialog.styles.js.map +1 -0
  359. package/dist/components/dock-panel/dock-panel.class.d.ts +143 -0
  360. package/dist/components/dock-panel/dock-panel.class.d.ts.map +1 -0
  361. package/dist/components/dock-panel/dock-panel.class.js +358 -0
  362. package/dist/components/dock-panel/dock-panel.class.js.map +1 -0
  363. package/dist/components/dock-panel/dock-panel.d.ts +2 -134
  364. package/dist/components/dock-panel/dock-panel.d.ts.map +1 -0
  365. package/dist/components/dock-panel/dock-panel.js +3 -343
  366. package/dist/components/dock-panel/dock-panel.js.map +1 -0
  367. package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
  368. package/dist/components/dock-panel/dock-panel.styles.d.ts.map +1 -0
  369. package/dist/components/dock-panel/dock-panel.styles.js +34 -13
  370. package/dist/components/dock-panel/dock-panel.styles.js.map +1 -0
  371. package/dist/components/document-preview/document-preview.class.d.ts +153 -0
  372. package/dist/components/document-preview/document-preview.class.d.ts.map +1 -0
  373. package/dist/components/document-preview/document-preview.class.js +304 -0
  374. package/dist/components/document-preview/document-preview.class.js.map +1 -0
  375. package/dist/components/document-preview/document-preview.d.ts +2 -135
  376. package/dist/components/document-preview/document-preview.d.ts.map +1 -0
  377. package/dist/components/document-preview/document-preview.js +3 -370
  378. package/dist/components/document-preview/document-preview.js.map +1 -0
  379. package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
  380. package/dist/components/document-preview/document-preview.styles.d.ts.map +1 -0
  381. package/dist/components/document-preview/document-preview.styles.js +22 -21
  382. package/dist/components/document-preview/document-preview.styles.js.map +1 -0
  383. package/dist/components/empty/empty.class.d.ts +43 -0
  384. package/dist/components/empty/empty.class.d.ts.map +1 -0
  385. package/dist/components/empty/empty.class.js +112 -0
  386. package/dist/components/empty/empty.class.js.map +1 -0
  387. package/dist/components/empty/empty.d.ts +2 -43
  388. package/dist/components/empty/empty.d.ts.map +1 -0
  389. package/dist/components/empty/empty.js +3 -105
  390. package/dist/components/empty/empty.js.map +1 -0
  391. package/dist/components/empty/empty.styles.d.ts +1 -0
  392. package/dist/components/empty/empty.styles.d.ts.map +1 -0
  393. package/dist/components/empty/empty.styles.js +6 -8
  394. package/dist/components/empty/empty.styles.js.map +1 -0
  395. package/dist/components/export-button/csv.d.ts +1 -0
  396. package/dist/components/export-button/csv.d.ts.map +1 -0
  397. package/dist/components/export-button/csv.js +1 -0
  398. package/dist/components/export-button/csv.js.map +1 -0
  399. package/dist/components/export-button/export-button.class.d.ts +76 -0
  400. package/dist/components/export-button/export-button.class.d.ts.map +1 -0
  401. package/dist/components/export-button/export-button.class.js +249 -0
  402. package/dist/components/export-button/export-button.class.js.map +1 -0
  403. package/dist/components/export-button/export-button.d.ts +2 -55
  404. package/dist/components/export-button/export-button.d.ts.map +1 -0
  405. package/dist/components/export-button/export-button.js +3 -233
  406. package/dist/components/export-button/export-button.js.map +1 -0
  407. package/dist/components/export-button/export-button.styles.d.ts +1 -0
  408. package/dist/components/export-button/export-button.styles.d.ts.map +1 -0
  409. package/dist/components/export-button/export-button.styles.js +11 -6
  410. package/dist/components/export-button/export-button.styles.js.map +1 -0
  411. package/dist/components/file-input/accept.d.ts +1 -0
  412. package/dist/components/file-input/accept.d.ts.map +1 -0
  413. package/dist/components/file-input/accept.js +3 -0
  414. package/dist/components/file-input/accept.js.map +1 -0
  415. package/dist/components/file-input/file-input.class.d.ts +66 -0
  416. package/dist/components/file-input/file-input.class.d.ts.map +1 -0
  417. package/dist/components/file-input/file-input.class.js +213 -0
  418. package/dist/components/file-input/file-input.class.js.map +1 -0
  419. package/dist/components/file-input/file-input.d.ts +2 -50
  420. package/dist/components/file-input/file-input.d.ts.map +1 -0
  421. package/dist/components/file-input/file-input.js +3 -195
  422. package/dist/components/file-input/file-input.js.map +1 -0
  423. package/dist/components/file-input/file-input.styles.d.ts +1 -0
  424. package/dist/components/file-input/file-input.styles.d.ts.map +1 -0
  425. package/dist/components/file-input/file-input.styles.js +5 -4
  426. package/dist/components/file-input/file-input.styles.js.map +1 -0
  427. package/dist/components/flag/flag-peer.d.ts +8 -0
  428. package/dist/components/flag/flag-peer.d.ts.map +1 -0
  429. package/dist/components/flag/flag-peer.js +13 -0
  430. package/dist/components/flag/flag-peer.js.map +1 -0
  431. package/dist/components/flag/flag.class.d.ts +124 -0
  432. package/dist/components/flag/flag.class.d.ts.map +1 -0
  433. package/dist/components/flag/flag.class.js +251 -0
  434. package/dist/components/flag/flag.class.js.map +1 -0
  435. package/dist/components/flag/flag.d.ts +3 -121
  436. package/dist/components/flag/flag.d.ts.map +1 -0
  437. package/dist/components/flag/flag.js +4 -225
  438. package/dist/components/flag/flag.js.map +1 -0
  439. package/dist/components/flag/flag.styles.d.ts +1 -0
  440. package/dist/components/flag/flag.styles.d.ts.map +1 -0
  441. package/dist/components/flag/flag.styles.js +6 -5
  442. package/dist/components/flag/flag.styles.js.map +1 -0
  443. package/dist/components/flag/language-map.d.ts +5 -1
  444. package/dist/components/flag/language-map.d.ts.map +1 -0
  445. package/dist/components/flag/language-map.js +7 -3
  446. package/dist/components/flag/language-map.js.map +1 -0
  447. package/dist/components/gauge/gauge.class.d.ts +41 -0
  448. package/dist/components/gauge/gauge.class.d.ts.map +1 -0
  449. package/dist/components/gauge/gauge.class.js +178 -0
  450. package/dist/components/gauge/gauge.class.js.map +1 -0
  451. package/dist/components/gauge/gauge.d.ts +2 -31
  452. package/dist/components/gauge/gauge.d.ts.map +1 -0
  453. package/dist/components/gauge/gauge.js +3 -158
  454. package/dist/components/gauge/gauge.js.map +1 -0
  455. package/dist/components/gauge/gauge.styles.d.ts +1 -0
  456. package/dist/components/gauge/gauge.styles.d.ts.map +1 -0
  457. package/dist/components/gauge/gauge.styles.js +9 -8
  458. package/dist/components/gauge/gauge.styles.js.map +1 -0
  459. package/dist/components/generation-status/generation-status.class.d.ts +121 -0
  460. package/dist/components/generation-status/generation-status.class.d.ts.map +1 -0
  461. package/dist/components/generation-status/generation-status.class.js +260 -0
  462. package/dist/components/generation-status/generation-status.class.js.map +1 -0
  463. package/dist/components/generation-status/generation-status.d.ts +2 -109
  464. package/dist/components/generation-status/generation-status.d.ts.map +1 -0
  465. package/dist/components/generation-status/generation-status.js +3 -271
  466. package/dist/components/generation-status/generation-status.js.map +1 -0
  467. package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
  468. package/dist/components/generation-status/generation-status.styles.d.ts.map +1 -0
  469. package/dist/components/generation-status/generation-status.styles.js +7 -6
  470. package/dist/components/generation-status/generation-status.styles.js.map +1 -0
  471. package/dist/components/graph/graph-loader.d.ts +13 -13
  472. package/dist/components/graph/graph-loader.d.ts.map +1 -0
  473. package/dist/components/graph/graph-loader.js +1 -0
  474. package/dist/components/graph/graph-loader.js.map +1 -0
  475. package/dist/components/graph/graph.class.d.ts +167 -0
  476. package/dist/components/graph/graph.class.d.ts.map +1 -0
  477. package/dist/components/graph/graph.class.js +552 -0
  478. package/dist/components/graph/graph.class.js.map +1 -0
  479. package/dist/components/graph/graph.d.ts +3 -123
  480. package/dist/components/graph/graph.d.ts.map +1 -0
  481. package/dist/components/graph/graph.js +4 -465
  482. package/dist/components/graph/graph.js.map +1 -0
  483. package/dist/components/graph/graph.styles.d.ts +1 -0
  484. package/dist/components/graph/graph.styles.d.ts.map +1 -0
  485. package/dist/components/graph/graph.styles.js +2 -1
  486. package/dist/components/graph/graph.styles.js.map +1 -0
  487. package/dist/components/heatmap/calendar-grid.d.ts +16 -6
  488. package/dist/components/heatmap/calendar-grid.d.ts.map +1 -0
  489. package/dist/components/heatmap/calendar-grid.js +18 -6
  490. package/dist/components/heatmap/calendar-grid.js.map +1 -0
  491. package/dist/components/heatmap/heatmap-scale.d.ts +14 -1
  492. package/dist/components/heatmap/heatmap-scale.d.ts.map +1 -0
  493. package/dist/components/heatmap/heatmap-scale.js +21 -2
  494. package/dist/components/heatmap/heatmap-scale.js.map +1 -0
  495. package/dist/components/heatmap/heatmap.class.d.ts +478 -0
  496. package/dist/components/heatmap/heatmap.class.d.ts.map +1 -0
  497. package/dist/components/heatmap/heatmap.class.js +1211 -0
  498. package/dist/components/heatmap/heatmap.class.js.map +1 -0
  499. package/dist/components/heatmap/heatmap.d.ts +2 -296
  500. package/dist/components/heatmap/heatmap.d.ts.map +1 -0
  501. package/dist/components/heatmap/heatmap.js +3 -892
  502. package/dist/components/heatmap/heatmap.js.map +1 -0
  503. package/dist/components/heatmap/heatmap.styles.d.ts +1 -0
  504. package/dist/components/heatmap/heatmap.styles.d.ts.map +1 -0
  505. package/dist/components/heatmap/heatmap.styles.js +20 -20
  506. package/dist/components/heatmap/heatmap.styles.js.map +1 -0
  507. package/dist/components/json-viewer/json-viewer.class.d.ts +77 -0
  508. package/dist/components/json-viewer/json-viewer.class.d.ts.map +1 -0
  509. package/dist/components/json-viewer/json-viewer.class.js +340 -0
  510. package/dist/components/json-viewer/json-viewer.class.js.map +1 -0
  511. package/dist/components/json-viewer/json-viewer.d.ts +2 -53
  512. package/dist/components/json-viewer/json-viewer.d.ts.map +1 -0
  513. package/dist/components/json-viewer/json-viewer.js +3 -281
  514. package/dist/components/json-viewer/json-viewer.js.map +1 -0
  515. package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
  516. package/dist/components/json-viewer/json-viewer.styles.d.ts.map +1 -0
  517. package/dist/components/json-viewer/json-viewer.styles.js +25 -21
  518. package/dist/components/json-viewer/json-viewer.styles.js.map +1 -0
  519. package/dist/components/kbd/kbd.class.d.ts +78 -0
  520. package/dist/components/kbd/kbd.class.d.ts.map +1 -0
  521. package/dist/components/kbd/kbd.class.js +230 -0
  522. package/dist/components/kbd/kbd.class.js.map +1 -0
  523. package/dist/components/kbd/kbd.d.ts +2 -77
  524. package/dist/components/kbd/kbd.d.ts.map +1 -0
  525. package/dist/components/kbd/kbd.js +3 -212
  526. package/dist/components/kbd/kbd.js.map +1 -0
  527. package/dist/components/kbd/kbd.styles.d.ts +1 -0
  528. package/dist/components/kbd/kbd.styles.d.ts.map +1 -0
  529. package/dist/components/kbd/kbd.styles.js +10 -9
  530. package/dist/components/kbd/kbd.styles.js.map +1 -0
  531. package/dist/components/live-region/live-region.class.d.ts +91 -0
  532. package/dist/components/live-region/live-region.class.d.ts.map +1 -0
  533. package/dist/components/live-region/live-region.class.js +188 -0
  534. package/dist/components/live-region/live-region.class.js.map +1 -0
  535. package/dist/components/live-region/live-region.d.ts +2 -81
  536. package/dist/components/live-region/live-region.d.ts.map +1 -0
  537. package/dist/components/live-region/live-region.js +3 -151
  538. package/dist/components/live-region/live-region.js.map +1 -0
  539. package/dist/components/live-region/live-region.styles.d.ts +1 -0
  540. package/dist/components/live-region/live-region.styles.d.ts.map +1 -0
  541. package/dist/components/live-region/live-region.styles.js +1 -0
  542. package/dist/components/live-region/live-region.styles.js.map +1 -0
  543. package/dist/components/map/map-loader.d.ts +5 -2
  544. package/dist/components/map/map-loader.d.ts.map +1 -0
  545. package/dist/components/map/map-loader.js +1 -0
  546. package/dist/components/map/map-loader.js.map +1 -0
  547. package/dist/components/map/map.class.d.ts +121 -0
  548. package/dist/components/map/map.class.d.ts.map +1 -0
  549. package/dist/components/map/map.class.js +450 -0
  550. package/dist/components/map/map.class.js.map +1 -0
  551. package/dist/components/map/map.d.ts +3 -88
  552. package/dist/components/map/map.d.ts.map +1 -0
  553. package/dist/components/map/map.js +4 -412
  554. package/dist/components/map/map.js.map +1 -0
  555. package/dist/components/map/map.styles.d.ts +1 -0
  556. package/dist/components/map/map.styles.d.ts.map +1 -0
  557. package/dist/components/map/map.styles.js +8 -7
  558. package/dist/components/map/map.styles.js.map +1 -0
  559. package/dist/components/markdown/markdown-loader.d.ts +30 -6
  560. package/dist/components/markdown/markdown-loader.d.ts.map +1 -0
  561. package/dist/components/markdown/markdown-loader.js +36 -1
  562. package/dist/components/markdown/markdown-loader.js.map +1 -0
  563. package/dist/components/markdown/markdown.class.d.ts +129 -0
  564. package/dist/components/markdown/markdown.class.d.ts.map +1 -0
  565. package/dist/components/markdown/markdown.class.js +354 -0
  566. package/dist/components/markdown/markdown.class.js.map +1 -0
  567. package/dist/components/markdown/markdown.d.ts +2 -81
  568. package/dist/components/markdown/markdown.d.ts.map +1 -0
  569. package/dist/components/markdown/markdown.js +3 -282
  570. package/dist/components/markdown/markdown.js.map +1 -0
  571. package/dist/components/markdown/markdown.styles.d.ts +1 -0
  572. package/dist/components/markdown/markdown.styles.d.ts.map +1 -0
  573. package/dist/components/markdown/markdown.styles.js +19 -12
  574. package/dist/components/markdown/markdown.styles.js.map +1 -0
  575. package/dist/components/media-card/media-card.class.d.ts +131 -0
  576. package/dist/components/media-card/media-card.class.d.ts.map +1 -0
  577. package/dist/components/media-card/media-card.class.js +272 -0
  578. package/dist/components/media-card/media-card.class.js.map +1 -0
  579. package/dist/components/media-card/media-card.d.ts +2 -122
  580. package/dist/components/media-card/media-card.d.ts.map +1 -0
  581. package/dist/components/media-card/media-card.js +3 -293
  582. package/dist/components/media-card/media-card.js.map +1 -0
  583. package/dist/components/media-card/media-card.styles.d.ts +1 -0
  584. package/dist/components/media-card/media-card.styles.d.ts.map +1 -0
  585. package/dist/components/media-card/media-card.styles.js +8 -7
  586. package/dist/components/media-card/media-card.styles.js.map +1 -0
  587. package/dist/components/mention-popover/mention-popover.class.d.ts +207 -0
  588. package/dist/components/mention-popover/mention-popover.class.d.ts.map +1 -0
  589. package/dist/components/mention-popover/mention-popover.class.js +379 -0
  590. package/dist/components/mention-popover/mention-popover.class.js.map +1 -0
  591. package/dist/components/mention-popover/mention-popover.d.ts +2 -200
  592. package/dist/components/mention-popover/mention-popover.d.ts.map +1 -0
  593. package/dist/components/mention-popover/mention-popover.js +3 -473
  594. package/dist/components/mention-popover/mention-popover.js.map +1 -0
  595. package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
  596. package/dist/components/mention-popover/mention-popover.styles.d.ts.map +1 -0
  597. package/dist/components/mention-popover/mention-popover.styles.js +11 -10
  598. package/dist/components/mention-popover/mention-popover.styles.js.map +1 -0
  599. package/dist/components/menu/menu-item.class.d.ts +99 -0
  600. package/dist/components/menu/menu-item.class.d.ts.map +1 -0
  601. package/dist/components/menu/menu-item.class.js +150 -0
  602. package/dist/components/menu/menu-item.class.js.map +1 -0
  603. package/dist/components/menu/menu-item.d.ts +2 -62
  604. package/dist/components/menu/menu-item.d.ts.map +1 -0
  605. package/dist/components/menu/menu-item.js +3 -117
  606. package/dist/components/menu/menu-item.js.map +1 -0
  607. package/dist/components/menu/menu-item.styles.d.ts +1 -0
  608. package/dist/components/menu/menu-item.styles.d.ts.map +1 -0
  609. package/dist/components/menu/menu-item.styles.js +11 -2
  610. package/dist/components/menu/menu-item.styles.js.map +1 -0
  611. package/dist/components/menu/menu.class.d.ts +173 -0
  612. package/dist/components/menu/menu.class.d.ts.map +1 -0
  613. package/dist/components/menu/menu.class.js +378 -0
  614. package/dist/components/menu/menu.class.js.map +1 -0
  615. package/dist/components/menu/menu.d.ts +3 -143
  616. package/dist/components/menu/menu.d.ts.map +1 -0
  617. package/dist/components/menu/menu.js +4 -363
  618. package/dist/components/menu/menu.js.map +1 -0
  619. package/dist/components/menu/menu.styles.d.ts +1 -0
  620. package/dist/components/menu/menu.styles.d.ts.map +1 -0
  621. package/dist/components/menu/menu.styles.js +9 -7
  622. package/dist/components/menu/menu.styles.js.map +1 -0
  623. package/dist/components/model-select/model-select.class.d.ts +177 -0
  624. package/dist/components/model-select/model-select.class.d.ts.map +1 -0
  625. package/dist/components/model-select/model-select.class.js +555 -0
  626. package/dist/components/model-select/model-select.class.js.map +1 -0
  627. package/dist/components/model-select/model-select.d.ts +2 -125
  628. package/dist/components/model-select/model-select.d.ts.map +1 -0
  629. package/dist/components/model-select/model-select.js +3 -488
  630. package/dist/components/model-select/model-select.js.map +1 -0
  631. package/dist/components/model-select/model-select.styles.d.ts +1 -0
  632. package/dist/components/model-select/model-select.styles.d.ts.map +1 -0
  633. package/dist/components/model-select/model-select.styles.js +39 -24
  634. package/dist/components/model-select/model-select.styles.js.map +1 -0
  635. package/dist/components/model-settings-panel/model-settings-panel.class.d.ts +94 -0
  636. package/dist/components/model-settings-panel/model-settings-panel.class.d.ts.map +1 -0
  637. package/dist/components/model-settings-panel/model-settings-panel.class.js +208 -0
  638. package/dist/components/model-settings-panel/model-settings-panel.class.js.map +1 -0
  639. package/dist/components/model-settings-panel/model-settings-panel.d.ts +4 -69
  640. package/dist/components/model-settings-panel/model-settings-panel.d.ts.map +1 -0
  641. package/dist/components/model-settings-panel/model-settings-panel.js +5 -143
  642. package/dist/components/model-settings-panel/model-settings-panel.js.map +1 -0
  643. package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
  644. package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts.map +1 -0
  645. package/dist/components/model-settings-panel/model-settings-panel.styles.js +12 -11
  646. package/dist/components/model-settings-panel/model-settings-panel.styles.js.map +1 -0
  647. package/dist/components/playback/playback.class.d.ts +79 -0
  648. package/dist/components/playback/playback.class.d.ts.map +1 -0
  649. package/dist/components/playback/playback.class.js +238 -0
  650. package/dist/components/playback/playback.class.js.map +1 -0
  651. package/dist/components/playback/playback.d.ts +2 -67
  652. package/dist/components/playback/playback.d.ts.map +1 -0
  653. package/dist/components/playback/playback.js +3 -207
  654. package/dist/components/playback/playback.js.map +1 -0
  655. package/dist/components/playback/playback.styles.d.ts +1 -0
  656. package/dist/components/playback/playback.styles.d.ts.map +1 -0
  657. package/dist/components/playback/playback.styles.js +8 -2
  658. package/dist/components/playback/playback.styles.js.map +1 -0
  659. package/dist/components/responsive-panel/responsive-panel.class.d.ts +164 -0
  660. package/dist/components/responsive-panel/responsive-panel.class.d.ts.map +1 -0
  661. package/dist/components/responsive-panel/responsive-panel.class.js +362 -0
  662. package/dist/components/responsive-panel/responsive-panel.class.js.map +1 -0
  663. package/dist/components/responsive-panel/responsive-panel.d.ts +2 -150
  664. package/dist/components/responsive-panel/responsive-panel.d.ts.map +1 -0
  665. package/dist/components/responsive-panel/responsive-panel.js +3 -363
  666. package/dist/components/responsive-panel/responsive-panel.js.map +1 -0
  667. package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
  668. package/dist/components/responsive-panel/responsive-panel.styles.d.ts.map +1 -0
  669. package/dist/components/responsive-panel/responsive-panel.styles.js +8 -5
  670. package/dist/components/responsive-panel/responsive-panel.styles.js.map +1 -0
  671. package/dist/components/result-card/result-card.class.d.ts +51 -0
  672. package/dist/components/result-card/result-card.class.d.ts.map +1 -0
  673. package/dist/components/result-card/result-card.class.js +88 -0
  674. package/dist/components/result-card/result-card.class.js.map +1 -0
  675. package/dist/components/result-card/result-card.d.ts +2 -44
  676. package/dist/components/result-card/result-card.d.ts.map +1 -0
  677. package/dist/components/result-card/result-card.js +3 -81
  678. package/dist/components/result-card/result-card.js.map +1 -0
  679. package/dist/components/result-card/result-card.styles.d.ts +1 -0
  680. package/dist/components/result-card/result-card.styles.d.ts.map +1 -0
  681. package/dist/components/result-card/result-card.styles.js +5 -5
  682. package/dist/components/result-card/result-card.styles.js.map +1 -0
  683. package/dist/components/result-card/result-field.class.d.ts +37 -0
  684. package/dist/components/result-card/result-field.class.d.ts.map +1 -0
  685. package/dist/components/result-card/result-field.class.js +86 -0
  686. package/dist/components/result-card/result-field.class.js.map +1 -0
  687. package/dist/components/result-card/result-field.d.ts +2 -36
  688. package/dist/components/result-card/result-field.d.ts.map +1 -0
  689. package/dist/components/result-card/result-field.js +3 -85
  690. package/dist/components/result-card/result-field.js.map +1 -0
  691. package/dist/components/result-card/result-field.styles.d.ts +1 -0
  692. package/dist/components/result-card/result-field.styles.d.ts.map +1 -0
  693. package/dist/components/result-card/result-field.styles.js +3 -2
  694. package/dist/components/result-card/result-field.styles.js.map +1 -0
  695. package/dist/components/select/select.class.d.ts +212 -0
  696. package/dist/components/select/select.class.d.ts.map +1 -0
  697. package/dist/components/select/select.class.js +637 -0
  698. package/dist/components/select/select.class.js.map +1 -0
  699. package/dist/components/select/select.d.ts +3 -166
  700. package/dist/components/select/select.d.ts.map +1 -0
  701. package/dist/components/select/select.js +4 -611
  702. package/dist/components/select/select.js.map +1 -0
  703. package/dist/components/select/select.styles.d.ts +1 -0
  704. package/dist/components/select/select.styles.d.ts.map +1 -0
  705. package/dist/components/select/select.styles.js +35 -34
  706. package/dist/components/select/select.styles.js.map +1 -0
  707. package/dist/components/skeleton/skeleton.class.d.ts +30 -0
  708. package/dist/components/skeleton/skeleton.class.d.ts.map +1 -0
  709. package/dist/components/skeleton/skeleton.class.js +67 -0
  710. package/dist/components/skeleton/skeleton.class.js.map +1 -0
  711. package/dist/components/skeleton/skeleton.d.ts +2 -26
  712. package/dist/components/skeleton/skeleton.d.ts.map +1 -0
  713. package/dist/components/skeleton/skeleton.js +3 -60
  714. package/dist/components/skeleton/skeleton.js.map +1 -0
  715. package/dist/components/skeleton/skeleton.styles.d.ts +1 -0
  716. package/dist/components/skeleton/skeleton.styles.d.ts.map +1 -0
  717. package/dist/components/skeleton/skeleton.styles.js +7 -5
  718. package/dist/components/skeleton/skeleton.styles.js.map +1 -0
  719. package/dist/components/slider/slider.class.d.ts +152 -0
  720. package/dist/components/slider/slider.class.d.ts.map +1 -0
  721. package/dist/components/slider/slider.class.js +433 -0
  722. package/dist/components/slider/slider.class.js.map +1 -0
  723. package/dist/components/slider/slider.d.ts +2 -89
  724. package/dist/components/slider/slider.d.ts.map +1 -0
  725. package/dist/components/slider/slider.js +3 -350
  726. package/dist/components/slider/slider.js.map +1 -0
  727. package/dist/components/slider/slider.styles.d.ts +1 -0
  728. package/dist/components/slider/slider.styles.d.ts.map +1 -0
  729. package/dist/components/slider/slider.styles.js +15 -14
  730. package/dist/components/slider/slider.styles.js.map +1 -0
  731. package/dist/components/source-card/source-card.class.d.ts +105 -0
  732. package/dist/components/source-card/source-card.class.d.ts.map +1 -0
  733. package/dist/components/source-card/source-card.class.js +194 -0
  734. package/dist/components/source-card/source-card.class.js.map +1 -0
  735. package/dist/components/source-card/source-card.d.ts +2 -92
  736. package/dist/components/source-card/source-card.d.ts.map +1 -0
  737. package/dist/components/source-card/source-card.js +3 -146
  738. package/dist/components/source-card/source-card.js.map +1 -0
  739. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  740. package/dist/components/source-card/source-card.styles.d.ts.map +1 -0
  741. package/dist/components/source-card/source-card.styles.js +12 -11
  742. package/dist/components/source-card/source-card.styles.js.map +1 -0
  743. package/dist/components/source-list/source-list.class.d.ts +64 -0
  744. package/dist/components/source-list/source-list.class.d.ts.map +1 -0
  745. package/dist/components/source-list/source-list.class.js +129 -0
  746. package/dist/components/source-list/source-list.class.js.map +1 -0
  747. package/dist/components/source-list/source-list.d.ts +2 -63
  748. package/dist/components/source-list/source-list.d.ts.map +1 -0
  749. package/dist/components/source-list/source-list.js +3 -120
  750. package/dist/components/source-list/source-list.js.map +1 -0
  751. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  752. package/dist/components/source-list/source-list.styles.d.ts.map +1 -0
  753. package/dist/components/source-list/source-list.styles.js +5 -4
  754. package/dist/components/source-list/source-list.styles.js.map +1 -0
  755. package/dist/components/sparkline/sparkline.class.d.ts +31 -0
  756. package/dist/components/sparkline/sparkline.class.d.ts.map +1 -0
  757. package/dist/components/sparkline/sparkline.class.js +140 -0
  758. package/dist/components/sparkline/sparkline.class.js.map +1 -0
  759. package/dist/components/sparkline/sparkline.d.ts +2 -30
  760. package/dist/components/sparkline/sparkline.d.ts.map +1 -0
  761. package/dist/components/sparkline/sparkline.js +3 -113
  762. package/dist/components/sparkline/sparkline.js.map +1 -0
  763. package/dist/components/sparkline/sparkline.styles.d.ts +1 -0
  764. package/dist/components/sparkline/sparkline.styles.d.ts.map +1 -0
  765. package/dist/components/sparkline/sparkline.styles.js +3 -2
  766. package/dist/components/sparkline/sparkline.styles.js.map +1 -0
  767. package/dist/components/split/split.class.d.ts +293 -0
  768. package/dist/components/split/split.class.d.ts.map +1 -0
  769. package/dist/components/split/split.class.js +831 -0
  770. package/dist/components/split/split.class.js.map +1 -0
  771. package/dist/components/split/split.d.ts +2 -155
  772. package/dist/components/split/split.d.ts.map +1 -0
  773. package/dist/components/split/split.js +3 -583
  774. package/dist/components/split/split.js.map +1 -0
  775. package/dist/components/split/split.styles.d.ts +1 -0
  776. package/dist/components/split/split.styles.d.ts.map +1 -0
  777. package/dist/components/split/split.styles.js +24 -6
  778. package/dist/components/split/split.styles.js.map +1 -0
  779. package/dist/components/stat/stat.class.d.ts +90 -0
  780. package/dist/components/stat/stat.class.d.ts.map +1 -0
  781. package/dist/components/stat/stat.class.js +249 -0
  782. package/dist/components/stat/stat.class.js.map +1 -0
  783. package/dist/components/stat/stat.d.ts +2 -83
  784. package/dist/components/stat/stat.d.ts.map +1 -0
  785. package/dist/components/stat/stat.js +3 -213
  786. package/dist/components/stat/stat.js.map +1 -0
  787. package/dist/components/stat/stat.styles.d.ts +1 -0
  788. package/dist/components/stat/stat.styles.d.ts.map +1 -0
  789. package/dist/components/stat/stat.styles.js +23 -22
  790. package/dist/components/stat/stat.styles.js.map +1 -0
  791. package/dist/components/stream-status/stream-status.class.d.ts +131 -0
  792. package/dist/components/stream-status/stream-status.class.d.ts.map +1 -0
  793. package/dist/components/stream-status/stream-status.class.js +212 -0
  794. package/dist/components/stream-status/stream-status.class.js.map +1 -0
  795. package/dist/components/stream-status/stream-status.d.ts +3 -110
  796. package/dist/components/stream-status/stream-status.d.ts.map +1 -0
  797. package/dist/components/stream-status/stream-status.js +4 -208
  798. package/dist/components/stream-status/stream-status.js.map +1 -0
  799. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  800. package/dist/components/stream-status/stream-status.styles.d.ts.map +1 -0
  801. package/dist/components/stream-status/stream-status.styles.js +7 -6
  802. package/dist/components/stream-status/stream-status.styles.js.map +1 -0
  803. package/dist/components/streaming-text/streaming-text.class.d.ts +105 -0
  804. package/dist/components/streaming-text/streaming-text.class.d.ts.map +1 -0
  805. package/dist/components/streaming-text/streaming-text.class.js +223 -0
  806. package/dist/components/streaming-text/streaming-text.class.js.map +1 -0
  807. package/dist/components/streaming-text/streaming-text.d.ts +3 -98
  808. package/dist/components/streaming-text/streaming-text.d.ts.map +1 -0
  809. package/dist/components/streaming-text/streaming-text.js +4 -207
  810. package/dist/components/streaming-text/streaming-text.js.map +1 -0
  811. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  812. package/dist/components/streaming-text/streaming-text.styles.d.ts.map +1 -0
  813. package/dist/components/streaming-text/streaming-text.styles.js +6 -4
  814. package/dist/components/streaming-text/streaming-text.styles.js.map +1 -0
  815. package/dist/components/switch/switch.class.d.ts +109 -0
  816. package/dist/components/switch/switch.class.d.ts.map +1 -0
  817. package/dist/components/switch/switch.class.js +225 -0
  818. package/dist/components/switch/switch.class.js.map +1 -0
  819. package/dist/components/switch/switch.d.ts +2 -58
  820. package/dist/components/switch/switch.d.ts.map +1 -0
  821. package/dist/components/switch/switch.js +3 -177
  822. package/dist/components/switch/switch.js.map +1 -0
  823. package/dist/components/switch/switch.styles.d.ts +1 -0
  824. package/dist/components/switch/switch.styles.d.ts.map +1 -0
  825. package/dist/components/switch/switch.styles.js +19 -15
  826. package/dist/components/switch/switch.styles.js.map +1 -0
  827. package/dist/components/table/table.class.d.ts +212 -0
  828. package/dist/components/table/table.class.d.ts.map +1 -0
  829. package/dist/components/table/table.class.js +562 -0
  830. package/dist/components/table/table.class.js.map +1 -0
  831. package/dist/components/table/table.d.ts +3 -184
  832. package/dist/components/table/table.d.ts.map +1 -0
  833. package/dist/components/table/table.js +4 -543
  834. package/dist/components/table/table.js.map +1 -0
  835. package/dist/components/table/table.styles.d.ts +1 -0
  836. package/dist/components/table/table.styles.d.ts.map +1 -0
  837. package/dist/components/table/table.styles.js +9 -8
  838. package/dist/components/table/table.styles.js.map +1 -0
  839. package/dist/components/tabs/tabs.class.d.ts +80 -0
  840. package/dist/components/tabs/tabs.class.d.ts.map +1 -0
  841. package/dist/components/tabs/tabs.class.js +215 -0
  842. package/dist/components/tabs/tabs.class.js.map +1 -0
  843. package/dist/components/tabs/tabs.d.ts +2 -51
  844. package/dist/components/tabs/tabs.d.ts.map +1 -0
  845. package/dist/components/tabs/tabs.js +3 -188
  846. package/dist/components/tabs/tabs.js.map +1 -0
  847. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  848. package/dist/components/tabs/tabs.styles.d.ts.map +1 -0
  849. package/dist/components/tabs/tabs.styles.js +21 -4
  850. package/dist/components/tabs/tabs.styles.js.map +1 -0
  851. package/dist/components/thinking-panel/thinking-panel.class.d.ts +124 -0
  852. package/dist/components/thinking-panel/thinking-panel.class.d.ts.map +1 -0
  853. package/dist/components/thinking-panel/thinking-panel.class.js +259 -0
  854. package/dist/components/thinking-panel/thinking-panel.class.js.map +1 -0
  855. package/dist/components/thinking-panel/thinking-panel.d.ts +2 -121
  856. package/dist/components/thinking-panel/thinking-panel.d.ts.map +1 -0
  857. package/dist/components/thinking-panel/thinking-panel.js +3 -233
  858. package/dist/components/thinking-panel/thinking-panel.js.map +1 -0
  859. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  860. package/dist/components/thinking-panel/thinking-panel.styles.d.ts.map +1 -0
  861. package/dist/components/thinking-panel/thinking-panel.styles.js +19 -12
  862. package/dist/components/thinking-panel/thinking-panel.styles.js.map +1 -0
  863. package/dist/components/time-range/time-range.class.d.ts +137 -0
  864. package/dist/components/time-range/time-range.class.d.ts.map +1 -0
  865. package/dist/components/time-range/time-range.class.js +467 -0
  866. package/dist/components/time-range/time-range.class.js.map +1 -0
  867. package/dist/components/time-range/time-range.d.ts +2 -69
  868. package/dist/components/time-range/time-range.d.ts.map +1 -0
  869. package/dist/components/time-range/time-range.js +3 -398
  870. package/dist/components/time-range/time-range.js.map +1 -0
  871. package/dist/components/time-range/time-range.styles.d.ts +1 -0
  872. package/dist/components/time-range/time-range.styles.d.ts.map +1 -0
  873. package/dist/components/time-range/time-range.styles.js +21 -13
  874. package/dist/components/time-range/time-range.styles.js.map +1 -0
  875. package/dist/components/toast/toast-item.class.d.ts +79 -0
  876. package/dist/components/toast/toast-item.class.d.ts.map +1 -0
  877. package/dist/components/toast/toast-item.class.js +307 -0
  878. package/dist/components/toast/toast-item.class.js.map +1 -0
  879. package/dist/components/toast/toast-item.d.ts +2 -63
  880. package/dist/components/toast/toast-item.d.ts.map +1 -0
  881. package/dist/components/toast/toast-item.js +3 -286
  882. package/dist/components/toast/toast-item.js.map +1 -0
  883. package/dist/components/toast/toast-item.styles.d.ts +1 -0
  884. package/dist/components/toast/toast-item.styles.d.ts.map +1 -0
  885. package/dist/components/toast/toast-item.styles.js +64 -32
  886. package/dist/components/toast/toast-item.styles.js.map +1 -0
  887. package/dist/components/toast/toast.class.d.ts +33 -0
  888. package/dist/components/toast/toast.class.d.ts.map +1 -0
  889. package/dist/components/toast/toast.class.js +59 -0
  890. package/dist/components/toast/toast.class.js.map +1 -0
  891. package/dist/components/toast/toast.d.ts +3 -32
  892. package/dist/components/toast/toast.d.ts.map +1 -0
  893. package/dist/components/toast/toast.js +4 -46
  894. package/dist/components/toast/toast.js.map +1 -0
  895. package/dist/components/toast/toast.styles.d.ts +1 -0
  896. package/dist/components/toast/toast.styles.d.ts.map +1 -0
  897. package/dist/components/toast/toast.styles.js +20 -16
  898. package/dist/components/toast/toast.styles.js.map +1 -0
  899. package/dist/components/toast/toaster.d.ts +1 -0
  900. package/dist/components/toast/toaster.d.ts.map +1 -0
  901. package/dist/components/toast/toaster.js +11 -1
  902. package/dist/components/toast/toaster.js.map +1 -0
  903. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.d.ts +135 -0
  904. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.d.ts.map +1 -0
  905. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.js +257 -0
  906. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.js.map +1 -0
  907. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +3 -130
  908. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts.map +1 -0
  909. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +4 -401
  910. package/dist/components/tool-approval-dialog/tool-approval-dialog.js.map +1 -0
  911. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  912. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts.map +1 -0
  913. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +22 -18
  914. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js.map +1 -0
  915. package/dist/components/tool-call-chip/tool-call-chip.class.d.ts +106 -0
  916. package/dist/components/tool-call-chip/tool-call-chip.class.d.ts.map +1 -0
  917. package/dist/components/tool-call-chip/tool-call-chip.class.js +348 -0
  918. package/dist/components/tool-call-chip/tool-call-chip.class.js.map +1 -0
  919. package/dist/components/tool-call-chip/tool-call-chip.d.ts +2 -89
  920. package/dist/components/tool-call-chip/tool-call-chip.d.ts.map +1 -0
  921. package/dist/components/tool-call-chip/tool-call-chip.js +3 -286
  922. package/dist/components/tool-call-chip/tool-call-chip.js.map +1 -0
  923. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  924. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts.map +1 -0
  925. package/dist/components/tool-call-chip/tool-call-chip.styles.js +22 -21
  926. package/dist/components/tool-call-chip/tool-call-chip.styles.js.map +1 -0
  927. package/dist/components/tool-param-form/tool-param-form.class.d.ts +216 -0
  928. package/dist/components/tool-param-form/tool-param-form.class.d.ts.map +1 -0
  929. package/dist/components/tool-param-form/tool-param-form.class.js +548 -0
  930. package/dist/components/tool-param-form/tool-param-form.class.js.map +1 -0
  931. package/dist/components/tool-param-form/tool-param-form.d.ts +5 -145
  932. package/dist/components/tool-param-form/tool-param-form.d.ts.map +1 -0
  933. package/dist/components/tool-param-form/tool-param-form.js +6 -364
  934. package/dist/components/tool-param-form/tool-param-form.js.map +1 -0
  935. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  936. package/dist/components/tool-param-form/tool-param-form.styles.d.ts.map +1 -0
  937. package/dist/components/tool-param-form/tool-param-form.styles.js +9 -8
  938. package/dist/components/tool-param-form/tool-param-form.styles.js.map +1 -0
  939. package/dist/components/tool-result-dialog/tool-result-dialog.class.d.ts +123 -0
  940. package/dist/components/tool-result-dialog/tool-result-dialog.class.d.ts.map +1 -0
  941. package/dist/components/tool-result-dialog/tool-result-dialog.class.js +357 -0
  942. package/dist/components/tool-result-dialog/tool-result-dialog.class.js.map +1 -0
  943. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +2 -102
  944. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts.map +1 -0
  945. package/dist/components/tool-result-dialog/tool-result-dialog.js +3 -391
  946. package/dist/components/tool-result-dialog/tool-result-dialog.js.map +1 -0
  947. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  948. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts.map +1 -0
  949. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +24 -16
  950. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js.map +1 -0
  951. package/dist/components/tool-result-view/registry.d.ts +1 -8
  952. package/dist/components/tool-result-view/registry.d.ts.map +1 -0
  953. package/dist/components/tool-result-view/registry.js +8 -2
  954. package/dist/components/tool-result-view/registry.js.map +1 -0
  955. package/dist/components/tool-result-view/tool-result-view.class.d.ts +65 -0
  956. package/dist/components/tool-result-view/tool-result-view.class.d.ts.map +1 -0
  957. package/dist/components/tool-result-view/tool-result-view.class.js +124 -0
  958. package/dist/components/tool-result-view/tool-result-view.class.js.map +1 -0
  959. package/dist/components/tool-result-view/tool-result-view.d.ts +4 -55
  960. package/dist/components/tool-result-view/tool-result-view.d.ts.map +1 -0
  961. package/dist/components/tool-result-view/tool-result-view.js +5 -136
  962. package/dist/components/tool-result-view/tool-result-view.js.map +1 -0
  963. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  964. package/dist/components/tool-result-view/tool-result-view.styles.d.ts.map +1 -0
  965. package/dist/components/tool-result-view/tool-result-view.styles.js +1 -0
  966. package/dist/components/tool-result-view/tool-result-view.styles.js.map +1 -0
  967. package/dist/components/tool-select-dialog/tool-select-dialog.class.d.ts +152 -0
  968. package/dist/components/tool-select-dialog/tool-select-dialog.class.d.ts.map +1 -0
  969. package/dist/components/tool-select-dialog/tool-select-dialog.class.js +349 -0
  970. package/dist/components/tool-select-dialog/tool-select-dialog.class.js.map +1 -0
  971. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +4 -151
  972. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts.map +1 -0
  973. package/dist/components/tool-select-dialog/tool-select-dialog.js +5 -437
  974. package/dist/components/tool-select-dialog/tool-select-dialog.js.map +1 -0
  975. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  976. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts.map +1 -0
  977. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +40 -27
  978. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js.map +1 -0
  979. package/dist/components/tree/tree-node.class.d.ts +78 -0
  980. package/dist/components/tree/tree-node.class.d.ts.map +1 -0
  981. package/dist/components/tree/tree-node.class.js +204 -0
  982. package/dist/components/tree/tree-node.class.js.map +1 -0
  983. package/dist/components/tree/tree-node.d.ts +2 -65
  984. package/dist/components/tree/tree-node.d.ts.map +1 -0
  985. package/dist/components/tree/tree-node.js +4 -219
  986. package/dist/components/tree/tree-node.js.map +1 -0
  987. package/dist/components/tree/tree.class.d.ts +130 -0
  988. package/dist/components/tree/tree.class.d.ts.map +1 -0
  989. package/dist/components/tree/tree.class.js +326 -0
  990. package/dist/components/tree/tree.class.js.map +1 -0
  991. package/dist/components/tree/tree.d.ts +3 -120
  992. package/dist/components/tree/tree.d.ts.map +1 -0
  993. package/dist/components/tree/tree.js +5 -324
  994. package/dist/components/tree/tree.js.map +1 -0
  995. package/dist/components/tree/tree.styles.d.ts +1 -0
  996. package/dist/components/tree/tree.styles.d.ts.map +1 -0
  997. package/dist/components/tree/tree.styles.js +1 -0
  998. package/dist/components/tree/tree.styles.js.map +1 -0
  999. package/dist/components/tree/update-cascade.d.ts +1 -0
  1000. package/dist/components/tree/update-cascade.d.ts.map +1 -0
  1001. package/dist/components/tree/update-cascade.js +1 -0
  1002. package/dist/components/tree/update-cascade.js.map +1 -0
  1003. package/dist/components/typing-indicator/typing-indicator.class.d.ts +71 -0
  1004. package/dist/components/typing-indicator/typing-indicator.class.d.ts.map +1 -0
  1005. package/dist/components/typing-indicator/typing-indicator.class.js +107 -0
  1006. package/dist/components/typing-indicator/typing-indicator.class.js.map +1 -0
  1007. package/dist/components/typing-indicator/typing-indicator.d.ts +2 -60
  1008. package/dist/components/typing-indicator/typing-indicator.d.ts.map +1 -0
  1009. package/dist/components/typing-indicator/typing-indicator.js +3 -94
  1010. package/dist/components/typing-indicator/typing-indicator.js.map +1 -0
  1011. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  1012. package/dist/components/typing-indicator/typing-indicator.styles.d.ts.map +1 -0
  1013. package/dist/components/typing-indicator/typing-indicator.styles.js +9 -8
  1014. package/dist/components/typing-indicator/typing-indicator.styles.js.map +1 -0
  1015. package/dist/components/virtual-list/virtual-list.class.d.ts +215 -0
  1016. package/dist/components/virtual-list/virtual-list.class.d.ts.map +1 -0
  1017. package/dist/components/virtual-list/virtual-list.class.js +520 -0
  1018. package/dist/components/virtual-list/virtual-list.class.js.map +1 -0
  1019. package/dist/components/virtual-list/virtual-list.d.ts +2 -173
  1020. package/dist/components/virtual-list/virtual-list.d.ts.map +1 -0
  1021. package/dist/components/virtual-list/virtual-list.js +3 -459
  1022. package/dist/components/virtual-list/virtual-list.js.map +1 -0
  1023. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  1024. package/dist/components/virtual-list/virtual-list.styles.d.ts.map +1 -0
  1025. package/dist/components/virtual-list/virtual-list.styles.js +14 -1
  1026. package/dist/components/virtual-list/virtual-list.styles.js.map +1 -0
  1027. package/dist/components/widget/widget.class.d.ts +70 -0
  1028. package/dist/components/widget/widget.class.d.ts.map +1 -0
  1029. package/dist/components/widget/widget.class.js +224 -0
  1030. package/dist/components/widget/widget.class.js.map +1 -0
  1031. package/dist/components/widget/widget.d.ts +2 -64
  1032. package/dist/components/widget/widget.d.ts.map +1 -0
  1033. package/dist/components/widget/widget.js +3 -315
  1034. package/dist/components/widget/widget.js.map +1 -0
  1035. package/dist/components/widget/widget.styles.d.ts +1 -0
  1036. package/dist/components/widget/widget.styles.d.ts.map +1 -0
  1037. package/dist/components/widget/widget.styles.js +19 -8
  1038. package/dist/components/widget/widget.styles.js.map +1 -0
  1039. package/dist/components/word-cloud/word-cloud-layout.d.ts +8 -0
  1040. package/dist/components/word-cloud/word-cloud-layout.d.ts.map +1 -0
  1041. package/dist/components/word-cloud/word-cloud-layout.js +21 -6
  1042. package/dist/components/word-cloud/word-cloud-layout.js.map +1 -0
  1043. package/dist/components/word-cloud/word-cloud.class.d.ts +99 -0
  1044. package/dist/components/word-cloud/word-cloud.class.d.ts.map +1 -0
  1045. package/dist/components/word-cloud/word-cloud.class.js +279 -0
  1046. package/dist/components/word-cloud/word-cloud.class.js.map +1 -0
  1047. package/dist/components/word-cloud/word-cloud.d.ts +2 -71
  1048. package/dist/components/word-cloud/word-cloud.d.ts.map +1 -0
  1049. package/dist/components/word-cloud/word-cloud.js +3 -264
  1050. package/dist/components/word-cloud/word-cloud.js.map +1 -0
  1051. package/dist/components/word-cloud/word-cloud.styles.d.ts +1 -0
  1052. package/dist/components/word-cloud/word-cloud.styles.d.ts.map +1 -0
  1053. package/dist/components/word-cloud/word-cloud.styles.js +37 -11
  1054. package/dist/components/word-cloud/word-cloud.styles.js.map +1 -0
  1055. package/dist/internal/a11y.d.ts +1 -0
  1056. package/dist/internal/a11y.d.ts.map +1 -0
  1057. package/dist/internal/a11y.js +1 -0
  1058. package/dist/internal/a11y.js.map +1 -0
  1059. package/dist/internal/anchored-validity.d.ts +2 -0
  1060. package/dist/internal/anchored-validity.d.ts.map +1 -0
  1061. package/dist/internal/anchored-validity.js +87 -0
  1062. package/dist/internal/anchored-validity.js.map +1 -0
  1063. package/dist/internal/announcer.d.ts +1 -0
  1064. package/dist/internal/announcer.d.ts.map +1 -0
  1065. package/dist/internal/announcer.js +1 -0
  1066. package/dist/internal/announcer.js.map +1 -0
  1067. package/dist/internal/form-associated.d.ts +8 -0
  1068. package/dist/internal/form-associated.d.ts.map +1 -0
  1069. package/dist/internal/form-associated.js +44 -4
  1070. package/dist/internal/form-associated.js.map +1 -0
  1071. package/dist/internal/group-by-recency.d.ts +1 -0
  1072. package/dist/internal/group-by-recency.d.ts.map +1 -0
  1073. package/dist/internal/group-by-recency.js +1 -0
  1074. package/dist/internal/group-by-recency.js.map +1 -0
  1075. package/dist/internal/icons.d.ts +1 -0
  1076. package/dist/internal/icons.d.ts.map +1 -0
  1077. package/dist/internal/icons.js +1 -0
  1078. package/dist/internal/icons.js.map +1 -0
  1079. package/dist/internal/localization.d.ts +27 -0
  1080. package/dist/internal/localization.d.ts.map +1 -0
  1081. package/dist/internal/localization.js +141 -0
  1082. package/dist/internal/localization.js.map +1 -0
  1083. package/dist/internal/lyra-element.d.ts +27 -3
  1084. package/dist/internal/lyra-element.d.ts.map +1 -0
  1085. package/dist/internal/lyra-element.js +52 -3
  1086. package/dist/internal/lyra-element.js.map +1 -0
  1087. package/dist/internal/numbers.d.ts +13 -0
  1088. package/dist/internal/numbers.d.ts.map +1 -0
  1089. package/dist/internal/numbers.js +27 -0
  1090. package/dist/internal/numbers.js.map +1 -0
  1091. package/dist/internal/optional-peer-types.d.ts +8 -0
  1092. package/dist/internal/optional-peer-types.d.ts.map +1 -0
  1093. package/dist/internal/optional-peer-types.js +2 -0
  1094. package/dist/internal/optional-peer-types.js.map +1 -0
  1095. package/dist/internal/overlay-manager.d.ts +52 -0
  1096. package/dist/internal/overlay-manager.d.ts.map +1 -0
  1097. package/dist/internal/overlay-manager.js +524 -0
  1098. package/dist/internal/overlay-manager.js.map +1 -0
  1099. package/dist/internal/positioner.d.ts +1 -0
  1100. package/dist/internal/positioner.d.ts.map +1 -0
  1101. package/dist/internal/positioner.js +28 -3
  1102. package/dist/internal/positioner.js.map +1 -0
  1103. package/dist/internal/prefix.d.ts +1 -0
  1104. package/dist/internal/prefix.d.ts.map +1 -0
  1105. package/dist/internal/prefix.js +9 -2
  1106. package/dist/internal/prefix.js.map +1 -0
  1107. package/dist/internal/root-registration-allowlist.d.ts +8 -0
  1108. package/dist/internal/root-registration-allowlist.d.ts.map +1 -0
  1109. package/dist/internal/root-registration-allowlist.js +95 -0
  1110. package/dist/internal/root-registration-allowlist.js.map +1 -0
  1111. package/dist/internal/rtl.d.ts +1 -0
  1112. package/dist/internal/rtl.d.ts.map +1 -0
  1113. package/dist/internal/rtl.js +1 -0
  1114. package/dist/internal/rtl.js.map +1 -0
  1115. package/dist/internal/safe-url.d.ts +13 -0
  1116. package/dist/internal/safe-url.d.ts.map +1 -0
  1117. package/dist/internal/safe-url.js +42 -0
  1118. package/dist/internal/safe-url.js.map +1 -0
  1119. package/dist/internal/scroll-lock.d.ts +1 -0
  1120. package/dist/internal/scroll-lock.d.ts.map +1 -0
  1121. package/dist/internal/scroll-lock.js +1 -0
  1122. package/dist/internal/scroll-lock.js.map +1 -0
  1123. package/dist/internal/tokens.styles.d.ts +1 -0
  1124. package/dist/internal/tokens.styles.d.ts.map +1 -0
  1125. package/dist/internal/tokens.styles.js +224 -11
  1126. package/dist/internal/tokens.styles.js.map +1 -0
  1127. package/dist/lyra.d.ts +56 -5
  1128. package/dist/lyra.d.ts.map +1 -0
  1129. package/dist/lyra.js +7 -2
  1130. package/dist/lyra.js.map +1 -0
  1131. package/llms-full.txt +1079 -472
  1132. package/llms.txt +11 -10
  1133. package/package.json +201 -6
package/llms-full.txt CHANGED
@@ -1,24 +1,29 @@
1
1
  # @aceshooting/lyra-ui — LLM API Reference
2
2
 
3
3
  `@aceshooting/lyra-ui` is a library of free, clean-room [Lit](https://lit.dev) web components — a
4
- companion to [Web Awesome](https://webawesome.com) that reimplements several Web Awesome **Pro**
5
- components as open-source equivalents (same tag shape, attributes, slots, events, and CSS parts,
6
- under a `lyra-` prefix instead of `wa-`), plus a number of extras that have no Web Awesome
4
+ companion to [Web Awesome](https://webawesome.com) that reimplements selected Web Awesome **Pro**
5
+ components as open-source equivalents (documented compatible tag shape, attributes, slots, events,
6
+ and CSS parts under a `lyra-` prefix instead of `wa-`), plus a number of extras that have no Web Awesome
7
7
  counterpart at all (dashboard atoms, temporal/graph widgets, chart wrappers, a map component, a
8
8
  file dropzone). It is **token-compatible with Web Awesome**: every component's stylesheet resolves
9
9
  through Web Awesome's `--wa-*` design tokens first, falling back to its own `--lyra-*` tokens, so
10
10
  components read as native inside a Web Awesome-themed app and are equally usable standalone with no
11
11
  theme at all. This document is the complete, component-by-component API reference intended for
12
- coding assistants integrating the library; for install instructions, the full tag/tier table, and
12
+ coding assistants integrating the library; for install instructions, the full component catalog, and
13
13
  the `<wa-*>` → `<lyra-*>` migration notes, see the package [README](./README.md) — or the shorter
14
14
  curated index at [`llms.txt`](./llms.txt) if you just need the shape of the library before diving in
15
15
  here.
16
16
 
17
+ Component entry points such as `components/empty/empty.js` register their custom-element tag.
18
+ Every component also exposes a matching `.class.js` entry for subclassing or type-directed
19
+ composition without registration; for example, `components/empty/empty.class.js` exports
20
+ `LyraEmpty` without touching `customElements`.
21
+
17
22
  ## Contents
18
23
 
19
24
  - **Shared foundation** — `internal/` (base class, form-association mixin, positioning helper, design tokens)
20
- - **v1 core** — `lyra-combobox`/`lyra-option`, `lyra-select`, `lyra-date-picker`/`lyra-date-input`, `lyra-toast` family, `lyra-sparkline`, `lyra-flag`
21
- - **Dashboard atoms** — `lyra-empty`, `lyra-skeleton`, `lyra-stat`, `lyra-table`, `lyra-gauge`, `lyra-export-button`, `lyra-split`, `lyra-widget`, `lyra-word-cloud`
25
+ - **Form controls, toasts, sparkline, and flags** — `lyra-combobox`/`lyra-option`, `lyra-select`, `lyra-date-picker`/`lyra-date-input`, `lyra-toast` family, `lyra-sparkline`, `lyra-flag`
26
+ - **Dashboard atoms** — `lyra-empty`, `lyra-skeleton`, `lyra-stat`, `lyra-table`, `lyra-gauge`, `lyra-export-button`, `lyra-copy-button`, `lyra-split`, `lyra-widget`, `lyra-word-cloud`
22
27
  - **Temporal & graph** — `lyra-time-range`, `lyra-playback`, `lyra-heatmap`, `lyra-graph`, `lyra-tree`/`lyra-tree-node`
23
28
  - **Chart family** — `lyra-chart` (core), typed subclasses, `lyra-histogram`, `lyra-box-plot`,
24
29
  `lyra-lite-chart` (dependency-free bar/line alternative, no peer deps)
@@ -34,7 +39,7 @@ here.
34
39
  `lyra-tool-param-form`, `lyra-menu`/`lyra-menu-item`, `lyra-chip`/`lyra-chip-group`,
35
40
  `lyra-model-settings-panel`, `lyra-context-meter`, `lyra-dock-panel`, `lyra-document-preview`,
36
41
  `lyra-media-card`, `lyra-attachment-trigger`, `lyra-kbd`, `lyra-result-card`/`lyra-result-field`
37
- - **Cross-cutting notes** — packaging and adoption caveats that apply across the whole library
42
+ - **Cross-cutting notes** — packaging notes that apply across the whole library
38
43
 
39
44
  ---
40
45
 
@@ -45,41 +50,67 @@ Not a custom element itself — infrastructure every `lyra-*` component in the l
45
50
  - **`LyraElement`** (`internal/lyra-element.ts`) — base class every `lyra-*` component `extends`.
46
51
  `static styles = [tokens]` (the design-token layer below); subclasses prepend `LyraElement.styles`
47
52
  to their own `static styles` array to inherit it. Supplies
48
- `protected emit<T>(name: string, detail?: T): CustomEvent<T>` — dispatches a
49
- `{ bubbles: true, composed: true, cancelable: true }` `CustomEvent` and returns it, so callers can
50
- do `const ev = this.emit('lyra-export', {...}); if (ev.defaultPrevented) return;`.
53
+ `protected emit<T>(name: string, detail?: T, options?: LyraEmitOptions): CustomEvent<T>` — dispatches
54
+ a bubbling/composed notification that is non-cancelable by default. A true veto point opts into
55
+ `{ cancelable: true }` and checks `defaultPrevented` before applying its operation, as
56
+ `lyra-export` does. Native-like `input`/`change` events use the same non-cancelable default.
51
57
  - **`FormAssociated(Base)`** mixin (`internal/form-associated.ts`) — makes a `LitElement`
52
- form-associated: `static formAssociated = true`, `attachInternals()` in the constructor. Adds
53
- properties `name: string`, `value: string` (hand-written accessor via Lit's `noAccessor` so
54
- `internals.setFormValue()` fires synchronously on assignment, not on the async update cycle),
55
- `disabled: boolean` (reflected), `required: boolean` (reflected). Provides
56
- `checkValidity()`/`reportValidity()` (delegate to `internals`), `formDisabledCallback(disabled)`,
57
- and a real `updateValidity()` that calls `internals.setValidity({ valueMissing: true }, ...)`
58
- whenever `required` is set and `value === ''` (re-run on every `value`/`required` change and once
59
- from `connectedCallback()`) so `checkValidity()`/`reportValidity()`/`:invalid`/`:user-invalid`
60
- now reflect real constraint state instead of always reporting valid. `formResetCallback()`
61
- restores the value captured from the element's original `value` *content attribute* (native
62
- `defaultValue` semantics), not a hardcoded blank only a later `setAttribute('value', ...)` or
63
- declarative markup updates that captured default; assigning the `.value` IDL property never does.
64
- Only `lyra-date-input` uses this mixin; `lyra-combobox` and `lyra-select` each hand-roll an
65
- equivalent independently instead (same `setValidity`/default-capture behavior`lyra-combobox`
66
- needs its own copy because its value can be an array in `multiple` mode, `lyra-select` because its
67
- default is captured from a declared-`selected` `<lyra-option>` rather than a `value` content
68
- attribute see each component's own gotchas for where its copy diverges from the mixin).
58
+ form-associated: `static formAssociated = true`, `attachInternals()` in the constructor (which also
59
+ eagerly calls `internals.setFormValue('')`, so an untouched control is present in `FormData` as
60
+ `""` from construction matching native `<input>` instead of being absent). Adds properties
61
+ `name: string`, `value: string`, `disabled: boolean` (reflected), `required: boolean` (reflected)
62
+ **all four** use a hand-written accessor via Lit's `noAccessor` (not a plain reactive property), so
63
+ the attribute write/`internals` call fires synchronously on assignment instead of on Lit's async
64
+ update cycle (e.g. `internals.setFormValue()` runs synchronously off `value`; `disabled`'s
65
+ attribute reflection lands before same-tick form APIs run). Provides
66
+ `checkValidity()`/`reportValidity()` (delegate to `internals`), readonly `form`, `labels`,
67
+ `validity`, `validationMessage`, `willValidate`, and `effectiveDisabled` getters
68
+ `effectiveDisabled` is own `disabled` OR an ancestor `<fieldset disabled>`'s cascaded state;
69
+ `formDisabledCallback(fieldsetDisabled)` stores that ancestor state in a private field rather than
70
+ writing into `disabled` itself, so `disabled` always reflects only the consumer's own
71
+ attribute/property (never mutated by fieldset cascading, matching native `<input>`) **read
72
+ `effectiveDisabled`, not `disabled`, to get the merged state**. A real `updateValidity()` calls
73
+ `internals.setValidity({ valueMissing: true }, ...)` whenever `required` is set and `value === ''`
74
+ (re-run on every `value`/`required` change and once from `connectedCallback()`) so
75
+ `checkValidity()`/`reportValidity()`/`:invalid`/`:user-invalid` now reflect real constraint state
76
+ instead of always reporting valid. Validity is routed through an internal
77
+ `AnchoredValidityController` (`internal/anchored-validity.ts`): it calls
78
+ `internals.setValidity(flags, message, anchor)` with `anchor` = the first focusable descendant in
79
+ the shadow root (`input:not([type='hidden']), textarea, select, button,
80
+ [tabindex]:not([tabindex='-1'])`), re-resolved after every render — required because the browser
81
+ can't focus the (non-focusable) custom-element host itself when `reportValidity()`/native
82
+ validation UI tries to focus the invalid control. `formResetCallback()` restores the value captured
83
+ from the element's original `value` *content attribute* (native `defaultValue` semantics), not a
84
+ hardcoded blank — only a later `setAttribute('value', ...)` or declarative markup updates that
85
+ captured default; assigning the `.value` IDL property never does. `formStateRestoreCallback()`
86
+ restores string state synchronously without emitting a user event. `lyra-date-input`,
87
+ `lyra-chat-composer`, and `lyra-slider` use this mixin; controls with non-string values and
88
+ controls whose defaults come from child markup hand-roll an equivalent independently instead (same
89
+ `setValidity`/default-capture behavior — `lyra-combobox` needs its own copy because its value can
90
+ be an array in `multiple` mode, `lyra-select` because its default is captured from a
91
+ declared-`selected` `<lyra-option>` rather than a `value` content attribute — see each component's
92
+ own gotchas for where its copy diverges from the mixin). Every form-associated control exposes the
93
+ same six readonly native-like getters (`form`, `labels`, `validity`, `validationMessage`,
94
+ `willValidate`, `effectiveDisabled`), including `lyra-time-range`, whose form association exists
95
+ only for fieldset-cascaded disablement and has no submission-value/state-restoration contract.
69
96
  - **`positioner.ts` → `place(anchor, popup, opts?): () => void`** — thin wrapper over
70
97
  `@floating-ui/dom`'s `computePosition` + `autoUpdate`. Forces `strategy: 'fixed'` (to match the
71
98
  popup's own `position:fixed` CSS — otherwise it lands offset by the page scroll position),
72
99
  middleware `offset(opts.offset ?? 4)`, `flip()`, `shift({ padding: 8 })`, default
73
100
  `placement: 'bottom-start'`. Returns a cleanup function that stops the `autoUpdate` loop — call it
74
101
  in `disconnectedCallback()`. Used by `lyra-combobox`, `lyra-select`, `lyra-date-input`,
75
- `lyra-export-button`.
102
+ `lyra-export-button`, `lyra-model-select`, `lyra-mention-popover`, `lyra-tool-call-chip`,
103
+ `lyra-citation-badge`, and `lyra-menu` (which resolves its `placement` through
104
+ `rtlAwarePlacement()` first — see `rtl.ts` below — before handing it to `place()`).
76
105
  - **`tokens.styles.ts` → `tokens`** (a `css` template prepended to every component's stylesheet) —
77
106
  the whole design-token layer. Every value chains through a `--wa-*` Web Awesome token first, with
78
107
  a hard-coded fallback, e.g. `--lyra-color-brand: var(--wa-color-brand-fill-loud, #0969da)`. Full
79
108
  set: `--lyra-color-surface / -text / -text-quiet / -border / -brand / -brand-quiet / -success /
80
- -warning / -danger`, `--lyra-color-on-brand` (text/icon color for content painted on a solid
81
- brand-fill background, e.g. selected calendar days — not itself light/dark-swapped, since it's
82
- white on both fills), `--lyra-space-xs / -s / -m / -l`, `--lyra-radius`, `--lyra-shadow`,
109
+ -success-quiet / -warning / -warning-quiet / -danger / -danger-quiet`,
110
+ `--lyra-color-on-brand / -on-success / -on-warning / -on-danger` (paired
111
+ text/icon colors for content painted on the corresponding solid semantic fill; standalone light
112
+ and dark fallbacks choose a contrasting foreground for each fill, while a Web Awesome theme can
113
+ provide its own `*-on-loud` values), `--lyra-space-xs / -s / -m / -l`, `--lyra-radius`, `--lyra-shadow`,
83
114
  `--lyra-font`, `--lyra-transition-fast` / `-base` (motion timing every animating component should
84
115
  share), `--lyra-opacity-disabled` (one shared disabled-state opacity), `--lyra-focus-ring-width` /
85
116
  `-color` / `-offset` (every `:focus-visible` rule should reference these), `--lyra-icon-button-size`
@@ -100,6 +131,16 @@ Not a custom element itself — infrastructure every `lyra-*` component in the l
100
131
  - **`scroll-lock.ts` → `lockScroll(): () => void`** — ref-counted `document.documentElement`
101
132
  scroll lock (used by `lyra-widget`'s fullscreen mode); safe to acquire/release more than once
102
133
  concurrently, restores the original `overflow` only once the last outstanding lock releases.
134
+ - **`overlay-manager.ts` → `activateOverlay(options): OverlayHandle`** — per-`Document`
135
+ coordination for `lyra-dialog`, overlay-mode `lyra-responsive-panel`, the three tool dialogs,
136
+ mobile `lyra-app-rail`, and fullscreen `lyra-widget`. Overlays from different families share one
137
+ visual/topmost stack: only the top entry handles Escape, Tab trapping, and backdrop dismissal.
138
+ Content outside the active modal's composed path is inert, including lower overlays and page
139
+ content added while it is open. Focus traversal crosses slots and open shadow roots; activation
140
+ preserves focus already inside but moves outside focus into the panel, and an open-state close
141
+ restores the still-connected opener even after a direct property/attribute transition. Nested
142
+ closes restore into the surviving overlay before eventually returning to the original page
143
+ trigger.
103
144
  - **`rtl.ts` → `isRtl(el): boolean`** — reads `getComputedStyle(el).direction === 'rtl'`, for the
104
145
  pointer/keyboard math (drag ratios, arrow-key direction) in `lyra-split`, `lyra-time-range`, and
105
146
  `lyra-dock-panel` that reasons about physical left/right and can't rely on logical CSS properties
@@ -121,7 +162,7 @@ Not a custom element itself — infrastructure every `lyra-*` component in the l
121
162
 
122
163
  ---
123
164
 
124
- # v1 core: forms, toasts, sparkline, flag
165
+ # Form controls, toasts, sparkline, and flags
125
166
 
126
167
  ## `lyra-combobox` / `lyra-option`
127
168
 
@@ -148,16 +189,20 @@ shared `FormAssociated` mixin — see gotchas).
148
189
  - `maxOptionsVisible: number = 3` (attribute `max-options-visible` — caps how many selected tags
149
190
  show before collapsing to `+N`)
150
191
  - `emptyText: string = 'No results'` (attribute `empty-text`)
192
+ - `loadingText: string = 'Loading…'` (attribute `loading-text` — listbox row shown while a `source`
193
+ fetch is in flight)
194
+ - `overflowText: string = '+{n} more — refine your search'` (attribute `overflow-text` — listbox row
195
+ shown when `maxRender` caps the row list; `{n}` is replaced with the hidden count)
151
196
  - `filter: OptionFilter | null = null` (attribute: false — `(option, query) => boolean`; default
152
197
  matches `label`/`searchText` case-insensitively; ignored while `source` is set)
153
198
  - `source: ComboboxSource | null = null` (attribute: false — `(query: string) =>
154
199
  Promise<ComboboxSourceRow[]>`; when set, replaces the light-DOM `<lyra-option>` list with an async
155
- lookup, debounced ~200ms after each keystroke and re-run on clear/pick. A "Loading…" row is shown
200
+ lookup, debounced ~200ms after each keystroke and re-run on clear/pick. `loadingText` is shown
156
201
  while a call is in flight; a stale in-flight call that resolves after a newer one (or after
157
202
  disconnect) is dropped via a monotonic token)
158
203
  - `maxRender: number = 200` (attribute `max-render` — caps how many rows render at once, always
159
- keeping the current selection visible even if it's outside the cap; the excess shows as an
160
- "+N more — refine your search" row instead of being dropped silently)
204
+ keeping the current selection visible even if it's outside the cap; the excess renders as one
205
+ `overflowText` row instead of being dropped silently)
161
206
  - `value: string | string[]` — a getter/setter: plain `string` in single mode, `string[]` in
162
207
  `multiple` mode
163
208
 
@@ -165,8 +210,13 @@ shared `FormAssociated` mixin — see gotchas).
165
210
  string; disabled?: boolean }` — the row shape both the async `source` and the light-DOM
166
211
  `<lyra-option>` path normalize to internally.
167
212
 
168
- **Events:** `change` (native-style selection changed), `input` (native-style typed or selection
169
- changed), `lyra-show`, `lyra-hide`, `lyra-clear`
213
+ **Events:** typing in the filter exposes the original bubbling/composed, non-cancelable `InputEvent`
214
+ as exactly one host `input` event and does not fire `change`. An actual user selection mutation —
215
+ pointer or keyboard selection, multiple-value toggle, tag/Backspace removal, or clear — emits exactly
216
+ one bubbling/composed, non-cancelable plain `input` `Event`, immediately followed by the same shape
217
+ of `change` `Event`. Re-picking the current single value and programmatic/default/reset/restore
218
+ writes are silent. The clear button emits one `lyra-clear` after its `input`/`change` pair.
219
+ `lyra-show` and `lyra-hide` report listbox visibility transitions.
170
220
 
171
221
  **Slots:** default (`<lyra-option>` children), `label`, `hint`, `error` (overrides the `errorText`
172
222
  attribute when provided)
@@ -222,6 +272,11 @@ Multi-select submits as **repeated `FormData` entries** (not a joined string)
222
272
  (`multiple` with no `name`) contributes nothing to the form at all, matching a nameless native
223
273
  `<select multiple>`, rather than falling back to a shared literal key that could collide with
224
274
  another unnamed combobox in the same form.
275
+ Session-history/autofill state is stored as a name-independent JSON string array. A valid string
276
+ array restores the selection (single mode keeps its first entry); malformed or wrong-shape state
277
+ restores an empty selection. Restored state wins even when it arrives before the first option
278
+ collection, while `form.reset()` still returns to the declarative selected default. Restoration is
279
+ synchronous and fires no `input`/`change` event.
225
280
 
226
281
  **Known gotchas:**
227
282
  - naming drift from Web Awesome parity: the shipped attribute is **`with-clear`**, not
@@ -257,19 +312,22 @@ as `lyra-combobox`, see the shared-foundation notes above). The trigger is a `<b
257
312
  input: click/Enter/Space/ArrowDown opens it, and there's no typing-to-filter. Options are
258
313
  `<lyra-option value>` children — the same element `<lyra-combobox>` uses — reconciled the same way
259
314
  combobox does, and the popup reuses `internal/positioner.ts` for placement.
315
+ Session-history/autofill restoration assigns the stored string through the same synchronous
316
+ value/form/validity path as a programmatic value write and does not emit `input` or `change`.
260
317
 
261
318
  Single-select only, with no `filter`/`source`/`with-clear`/`max-options-visible`/`empty-text`/
262
319
  `max-render`/`multiple` surface — reach for `<lyra-combobox>` instead whenever any of those apply.
263
320
 
264
- **Single-option auto-commit.** When exactly one option is enabled (regardless of how many disabled
265
- ones exist alongside it), the popup never opens at all: a click, Enter, Space, ArrowDown, or ArrowUp
266
- on the trigger commits that sole option directly, and the trigger renders as a plain `role="button"`
267
- with no chevron/`aria-haspopup`/`aria-expanded`/`aria-controls`/`aria-activedescendant` rather than a
268
- combobox with a permanently inert popup state. This is unconditional default behavior, not
269
- opt-in opening a one-row list to pick the only available choice is pure friction with no real
270
- decision behind it. It never changes `value`/validity defaults on its own: an unselected
271
- single-option select stays unselected (and a `required` one stays invalid) exactly like the
272
- multi-option case, until the trigger is actually activated.
321
+ **Single-option auto-commit.** Opt-in via `autoCommitSingleOption` (default `false` a select always
322
+ renders the normal combobox/listbox/chevron trigger unless enabled, matching pre-1.3.0 behavior).
323
+ When set and exactly one option is enabled (regardless of how many disabled ones exist alongside
324
+ it), the popup never opens at all: a click, Enter, Space, ArrowDown, or ArrowUp on the trigger
325
+ commits that sole option directly, and the trigger renders as a plain `role="button"` with no
326
+ chevron/`aria-haspopup`/`aria-expanded`/`aria-controls`/`aria-activedescendant` rather than a
327
+ combobox with a permanently inert popup state opening a one-row list to pick the only available
328
+ choice is pure friction with no real decision behind it. It never changes `value`/validity defaults
329
+ on its own: an unselected single-option select stays unselected (and a `required` one stays invalid)
330
+ exactly like the multi-option case, until the trigger is actually activated.
273
331
 
274
332
  **Properties:**
275
333
  - `placeholder: string = ''`
@@ -283,6 +341,8 @@ multi-option case, until the trigger is actually activated.
283
341
  - `open: boolean = false` (reflected)
284
342
  - `size: 'xs'|'s'|'m'|'l'|'xl' = 'm'` (reflected — same scale as `lyra-toast-item`'s `size`, for
285
343
  compact toolbar placements that don't fit the default trigger height)
344
+ - `autoCommitSingleOption: boolean = false` (attribute `auto-commit-single-option`) — opts in to the
345
+ single-option auto-commit behavior described above
286
346
  - `value: string` — a getter/setter; always a single string (no `multiple` mode)
287
347
 
288
348
  **Events:** `change` (native-style — selection changed), `input` (fired alongside `change` on every
@@ -292,11 +352,17 @@ selection change — a native `<select>` doesn't meaningfully distinguish the tw
292
352
  **Slots:** default (`<lyra-option>` children), `label`, `hint`, `error` (overrides the `errorText`
293
353
  attribute when provided)
294
354
 
355
+ When hint/error content is present, the trigger's `aria-describedby` references stable shadow-local
356
+ IDs for both messages (error first, then hint), so the visible supporting text is part of the
357
+ control's accessible description.
358
+
295
359
  **CSS parts:** `form-control`, `form-control-label`, `trigger`, `listbox`, `option`, `option-dot`
296
360
  (the leading status dot, when a row's `dotColor` is set), `option-label`, `option-sub` (a row's
297
361
  secondary line, when `sub` is set), `expand-icon`, `error`, `hint`
298
362
 
299
- **Themeable custom properties:** shared tokens only.
363
+ **Themeable custom properties:** `--lyra-select-trigger-padding`, `--lyra-select-trigger-min-height`,
364
+ `--lyra-select-font-size` — all three auto-swapped per `size` (`xs`…`xl`), the same pattern
365
+ `lyra-toast-item`'s `--lyra-toast-padding`/`--lyra-toast-font-size` use.
300
366
 
301
367
  **Optional peer deps:** none.
302
368
 
@@ -327,10 +393,11 @@ secondary line, when `sub` is set), `expand-icon`, `error`, `hint`
327
393
  - The trigger's accessible name now checks a host-level `aria-label` attribute first, before falling
328
394
  back to `label`/`placeholder`/`"Select"` — a plain `aria-label` on `<lyra-select>` is no longer
329
395
  silently ignored.
330
- - A select with exactly one enabled option never exposes `role="combobox"`/opens a listbox at
331
- all — see "Single-option auto-commit" above. Testing/automation code that always expects a
332
- `role="combobox"` trigger, or that opens the listbox before asserting on a row, needs at least two
333
- enabled options to observe the normal dropdown chrome.
396
+ - With `autoCommitSingleOption` set, a select with exactly one enabled option never exposes
397
+ `role="combobox"`/opens a listbox at all — see "Single-option auto-commit" above.
398
+ Testing/automation code that always expects a `role="combobox"` trigger, or that opens the
399
+ listbox before asserting on a row, either needs at least two enabled options or should leave
400
+ `autoCommitSingleOption` unset to observe the normal dropdown chrome.
334
401
 
335
402
  ---
336
403
 
@@ -346,24 +413,30 @@ Inline month-grid calendar, not form-associated (used standalone or embedded ins
346
413
 
347
414
  **Properties:**
348
415
  - `value: string = ''`
349
- - `mode: 'single'|'range' = 'single'`
416
+ - `mode: 'single'|'range' = 'single'` — unknown runtime values fall back to `single`
350
417
  - `min: string = ''`
351
418
  - `max: string = ''`
352
419
  - `disabled: boolean = false` (reflected)
353
420
  - `readonly: boolean = false` (reflected)
354
- - `months: 1|2 = 1`
355
- - `locale: string = ''`
421
+ - `months: 1|2 = 1` — finite runtime values are truncated and clamped to `1..2`; non-finite
422
+ values fall back to `1`
423
+ - `locale: string = ''` — malformed locale tags fall back to the platform locale
356
424
  - `firstDayOfWeek: string = 'auto'` (attribute `first-day-of-week` — see gotchas)
357
- - `weekdayFormat: 'narrow'|'short'|'long' = 'short'` (attribute `weekday-format`)
425
+ - `weekdayFormat: 'narrow'|'short'|'long' = 'short'` (attribute `weekday-format`; unknown runtime
426
+ values fall back to `short`)
358
427
  - `disablePast: boolean = false` (attribute `disable-past`)
359
428
  - `disableFuture: boolean = false` (attribute `disable-future`)
360
429
  - `withOutsideDays: boolean = false` (attribute `with-outside-days`)
430
+ - `previousLabel: string = 'Previous month'` (attribute `previous-label` — accessible label for the
431
+ previous-month nav button; override for a non-English `locale`)
432
+ - `nextLabel: string = 'Next month'` (attribute `next-label` — accessible label for the next-month
433
+ nav button)
361
434
 
362
435
  **Getters:** `selection: { from: Date|null; to: Date|null }`, `valueAsDate: Date | null` (single
363
436
  mode only)
364
437
 
365
- **Methods:** `clear()`, `goToToday()`, `goToDate(date: string | Date)` (clamped to `min`/`max` before
366
- navigating/focusing)
438
+ **Methods:** `clear()`, `goToToday()`, `goToDate(date: string | Date)` (valid dates are clamped to
439
+ `min`/`max` before navigating/focusing; invalid `Date` objects and strings are ignored)
367
440
 
368
441
  **Events:** `input` (during interaction — for range mode, fires after the first click of a pair),
369
442
  `change` (committed value)
@@ -388,16 +461,18 @@ Text field + calendar popover, **form-associated** via the shared `FormAssociate
388
461
  - `mode: 'single'|'range' = 'single'`
389
462
  - `min: string = ''`
390
463
  - `max: string = ''`
391
- - `readonly: boolean = false` (reflected)
464
+ - `readonly: boolean = false` (reflected; preserves the submitted value but bars required and
465
+ date-bound constraint validation until removed)
392
466
  - `open: boolean = false` (reflected)
393
467
  - `withClear: boolean = false` (attribute `with-clear`)
394
468
  - `label: string = ''`
395
469
  - `hint: string = ''`
396
470
  - `placeholder: string = ''`
397
- - `locale: string = ''`
398
- - `months: 1|2 = 1`
471
+ - `locale: string = ''` — malformed locale tags fall back to the platform locale
472
+ - `months: 1|2 = 1` — finite runtime values are truncated and clamped to `1..2`; non-finite
473
+ values fall back to `1`
399
474
  - `firstDayOfWeek: string = 'auto'`
400
- - `weekdayFormat: 'narrow'|'short'|'long' = 'short'`
475
+ - `weekdayFormat: 'narrow'|'short'|'long' = 'short'` — unknown runtime values fall back to `short`
401
476
  - `disablePast: boolean = false` (attribute `disable-past`)
402
477
  - `disableFuture: boolean = false` (attribute `disable-future`)
403
478
  - `withOutsideDays: boolean = false` (attribute `with-outside-days`)
@@ -405,6 +480,11 @@ Text field + calendar popover, **form-associated** via the shared `FormAssociate
405
480
  **Properties (own, continued):**
406
481
  - `errorText: string = ''` (attribute `error-text` — static error copy; overridden by slotted
407
482
  `error` content when provided)
483
+ - `clearLabel: string = 'Clear'` (attribute `clear-label` — accessible label for the clear button)
484
+ - `openLabel: string = 'Open calendar'` (attribute `open-label` — accessible label for the
485
+ calendar-toggle button)
486
+ - `dialogLabel: string = 'Choose date'` (attribute `dialog-label` — accessible name for the
487
+ `role="dialog"` calendar popover)
408
488
 
409
489
  **Methods:** `show()`, `hide()`, `clear()`
410
490
 
@@ -430,9 +510,13 @@ Text field + calendar popover, **form-associated** via the shared `FormAssociate
430
510
 
431
511
  `calendar-core.ts` exports the pure date math both components share (**internal — not re-exported
432
512
  from the package root**, but worth knowing when reasoning about behavior): `parseISO`, `formatISO`,
433
- `isSameDay`, `addMonths`, `clampDate`, `monthMatrix(year, month, firstDayOfWeek)`,
434
- `weekdayLabels(firstDayOfWeek, format, locale)`, `monthTitle(year, month, locale)`,
435
- `resolveFirstDayOfWeek(value, locale?)`.
513
+ `isSameDay`, `addMonths`, `addMonthsClampingDay(date, n)` (like `addMonths` but clamps the result to
514
+ the target month's last day instead of overflowing into the month after — e.g. Jan 31 + 1 month
515
+ lands on Feb 28/29, not Mar 3; backs `lyra-date-picker`'s PageUp/PageDown), `clampDate`,
516
+ `monthMatrix(year, month, firstDayOfWeek)`, `weekdayLabels(firstDayOfWeek, format, locale)`,
517
+ `monthTitle(year, month, locale)`, `resolveFirstDayOfWeek(value, locale?)`,
518
+ `normalizeCalendarMode(value)`, `normalizeCalendarMonths(value)`, `normalizeWeekdayFormat(value)`,
519
+ and `dateTimeFormat(locale, options)`.
436
520
 
437
521
  **Known gotchas:**
438
522
  - `first-day-of-week="auto"` now derives from `locale` when the runtime's `Intl.Locale` exposes
@@ -442,16 +526,30 @@ from the package root**, but worth knowing when reasoning about behavior): `pars
442
526
  show; on an older runtime it still falls back to Sunday-first silently. **If you need a guaranteed
443
527
  locale-correct week start regardless of runtime**, pass an explicit
444
528
  `first-day-of-week="mon"` (accepted values: `sun`/`mon`/`tue`/`wed`/`thu`/`fri`/`sat`).
445
- - (date-input only) opening the calendar doesn't move focus into it (`show()` only flips
446
- `open`) a keyboard user opening via `Alt+ArrowDown` lands on a visible popover with focus still
447
- on the text input. Escape *does* close it from anywhere inside the form control (including the
448
- nested picker's own day/nav buttons), returning focus to the expand button.
529
+ - Runtime attribute/property inputs are normalized before calendar math or `Intl` formatting:
530
+ unsupported modes and weekday formats use their documented defaults, month counts cannot exceed
531
+ the two-grid API, malformed locale tags use the platform locale, and invalid `Date` objects do
532
+ not replace the current view.
533
+ - (date-input only) — opening the calendar does not move focus into it: `Alt+ArrowDown` leaves focus
534
+ on the text input, and the expand button keeps focus when clicked. The focused opener is
535
+ remembered. Escape and a finalized calendar selection close the popup and return focus to that
536
+ same connected element; a direct `.open = false` also restores it when focus would otherwise
537
+ remain inside the now-hidden popup. Outside-pointer dismissal deliberately leaves focus at the
538
+ clicked target.
449
539
  - (date-input only) — typing an unparseable string and blurring/committing sets `badInput` via
450
- `internals.setValidity()` and reverts the displayed text to the last valid commit, but does not
451
- reflect `aria-invalid` onto the visible `<input part="input">` itself (unlike `lyra-combobox`,
452
- which does reflect `aria-invalid`/`aria-required` onto its own input) rely on `data-invalid`
453
- (a host attribute, toggled the same way) or `reportValidity()` rather than assuming the input's own
454
- ARIA state.
540
+ `internals.setValidity()` and reverts the displayed text to the last valid commit. The visible
541
+ `<input part="input">` mirrors `required` through native `required`/`aria-required` and exposes
542
+ touched outer validity through `aria-invalid`; this includes required, bad-input, and range
543
+ failures owned by the form-associated host. A host `aria-label` is reactively forwarded to the
544
+ input, including later changes/removal, and takes precedence over the component's
545
+ label/placeholder fallback. Native validation attempts reveal `aria-invalid`; `form.reset()`
546
+ clears that touched presentation. A parseable typed date outside an active bound is committed
547
+ instead and reports the precise `rangeUnderflow`/`rangeOverflow` state.
548
+ - (date-input only) — declarative, IDL, reset, and restored values are sanitized to exact ISO dates:
549
+ calendar-invalid or malformed strings become `''` (and therefore `valueMissing` when required),
550
+ while valid dates outside `min`/`max` or `disable-past`/`disable-future` remain submitted and expose
551
+ `rangeUnderflow`/`rangeOverflow`. Changes to these constraints, `mode`, and `readonly` recompute
552
+ validity synchronously; range mode validates both endpoints.
455
553
  - The grid keyboard pattern (Arrow/PageUp/PageDown/Home/End navigation with correct focus
456
554
  sequencing) is implemented correctly and safe to rely on, as is the selected/range-day text color
457
555
  (`--lyra-color-on-brand`, not a hardcoded literal — safe to override `--lyra-color-brand` without
@@ -480,9 +578,8 @@ One per page recommended — the region.
480
578
 
481
579
  **CSS parts:** `stack`
482
580
 
483
- **Themeable custom properties:** `--gap` (default `var(--lyra-space-s)`), `--width` (default
484
- `28rem`) — set directly on the `<lyra-toast>` element (not `--lyra-`-prefixed, but overridable
485
- per-instance).
581
+ **Themeable custom properties:** `--lyra-toast-gap` (default `var(--lyra-space-s)`),
582
+ `--lyra-toast-width` (default `28rem`) — set directly on the `<lyra-toast>` element.
486
583
 
487
584
  **Optional peer deps:** none.
488
585
 
@@ -492,7 +589,7 @@ A single notification.
492
589
 
493
590
  **Properties:**
494
591
  - `duration: number = 5000` (ms; `Infinity` or `<= 0` disables auto-dismiss)
495
- - `size: 'xs'|'s'|'m'|'l'|'xl' = 'm'` (reflected — drives both `--padding` and the toast's own
592
+ - `size: 'xs'|'s'|'m'|'l'|'xl' = 'm'` (reflected — drives both `--lyra-toast-padding` and the toast's own
496
593
  font-size via `:host([size=...])`, from a compact `xs` up to a roomier `xl`)
497
594
  - `variant: 'brand'|'success'|'warning'|'danger'|'neutral' = 'neutral'` (reflected)
498
595
  - `withIcon: boolean = false` (attribute `with-icon`)
@@ -506,11 +603,12 @@ DOM.
506
603
 
507
604
  **CSS parts:** `toast-item`, `accent`, `icon`, `content`, `close-button`
508
605
 
509
- **Themeable custom properties:** `--accent-width` (4px), `--show-duration`/`--hide-duration`
606
+ **Themeable custom properties:** `--lyra-toast-accent-width` (4px),
607
+ `--lyra-toast-show-duration`/`--lyra-toast-hide-duration`
510
608
  (`var(--lyra-transition-base, 180ms ease-out)` — 180ms by default, matching the JS-side show/hide
511
- animation timer, which is not itself reading this token), `--padding` (`var(--lyra-space-m)`),
512
- `--font-size` (`1rem`) — both `--padding` and `--font-size` are auto-swapped per `size`, from a
513
- compact `xs` up to a roomier `xl` — `--accent-color` (defaults to `--lyra-color-border`,
609
+ animation timer, which is not itself reading this token), `--lyra-toast-padding`
610
+ (`var(--lyra-space-m)`), `--lyra-toast-font-size` (`1rem`) — both are auto-swapped per `size`, from a
611
+ compact `xs` up to a roomier `xl` — `--lyra-toast-accent-color` (defaults to `--lyra-color-border`,
514
612
  auto-swapped per `variant` to `--lyra-color-brand/-success/-warning/-danger`).
515
613
 
516
614
  **Optional peer deps:** none.
@@ -604,11 +702,14 @@ the reviewed family (pure CSS cascade, no JS/`getComputedStyle` bridging needed)
604
702
  a glance-value sparkline, worth reconsidering if it's ever the *sole* representation of a metric.
605
703
  - flat data (every value equal, so the auto-computed range spans zero) now renders a centered
606
704
  midline/mid-height bars instead of collapsing every point to the bottom edge, and a single-value
607
- series renders a visible flat line (a zero-length path was previously invisible). A `type="bar"`
608
- series past 500 points is decimated to at most 500 rendered `<rect>`s (evenly sampled, not
609
- aggregated/averaged) so an unbounded `values` array can't turn directly into that many shadow-DOM
610
- nodes; `line`/`area` types have no such cap (they already collapse to one `<path>` regardless of
611
- point count).
705
+ series renders a visible flat line (a zero-length path was previously invisible). **Every** `type`
706
+ (`line`/`area`/`bar`) decimates a `values` array past 500 points down to at most 500 plotted
707
+ samples evenly sampled by index, always keeping the first and last value exactly, not
708
+ aggregated/averaged. `type="bar"` caps at 500 rendered `<rect>`s directly; `line`/`area` cap the
709
+ point count baked into the single `<path>`'s `d` string instead (an uncapped path string also
710
+ grows unbounded, even though the element count stays at one `<path>`). Auto `min`/`max` is still
711
+ scanned from the *full* pre-decimation `values` array, so a real extreme value that decimation
712
+ happens to drop can't silently narrow the rendered scale.
612
713
 
613
714
  ---
614
715
 
@@ -629,9 +730,16 @@ Country/language flag image. Flag artwork ships in a **separate, optional peer p
629
730
  - `label?: string` (accessible name / `alt` text — **defaults to a localized, human-readable region
630
731
  name derived from the *resolved country* code via `Intl.DisplayNames` if omitted**, see gotchas)
631
732
  - `round: boolean = false` (reflected — circular crop)
632
- - `detailed: boolean = false` (reflected requests the pristine, pre-optimization source SVG
633
- instead of the default icon-optimized one; a no-op for the majority of codes whose source art was
634
- never large enough to need optimizing. Has no effect when `src` is set. See gotchas.)
733
+ - `variant?: 'compact' | 'standard' | 'detailed'` (attribute `variant`, not reflected picks a
734
+ fidelity tier for the ~65 codes whose source art embeds a coat of arms/seal/emblem; every other
735
+ code resolves to the same file regardless of `variant`. `'compact'` = a tiny WebP raster for
736
+ icon-scale use (menus, language pickers, ~12–28px); `'standard'` (the effective default, when both
737
+ `variant` and `detailed` are unset) = the icon-optimized vector for card/row sizes (~28–96px);
738
+ `'detailed'` = the pristine full-fidelity vector for hero-scale display. Takes precedence over the
739
+ deprecated `detailed` below. No effect when `src` is set.)
740
+ - `detailed: boolean = false` (reflected — **deprecated: use `variant="detailed"` instead**. Kept as
741
+ an alias for one minor cycle — when `variant` is left unset, `detailed` still maps to the detailed
742
+ tier — scheduled for removal in the next major. No effect when `src` is set.)
635
743
 
636
744
  **Events:** none.
637
745
 
@@ -639,11 +747,15 @@ Country/language flag image. Flag artwork ships in a **separate, optional peer p
639
747
 
640
748
  **CSS parts:** `image`
641
749
 
642
- **Themeable custom properties:** `--lyra-flag-radius` (default `2px`, non-`round` corner radius);
643
- also consumes `--lyra-color-border` (the inset ring around the flag image).
750
+ **Themeable custom properties:** `--lyra-flag-radius` (default `calc(var(--lyra-radius) * 0.33)`
751
+ ≈2px at the library's default `--lyra-radius` of `0.375rem`; scales with `--lyra-radius` unless
752
+ overridden directly — non-`round` corner radius); also consumes `--lyra-color-border` (the inset
753
+ ring around the flag image).
644
754
 
645
755
  **Optional peer deps:** `@aceshooting/lyra-flags` — required for the component to actually render an
646
- image; without it, renders an empty template (see gotchas).
756
+ image when `country` or `language` is used. Import `components/flag/flag-peer.js` once to opt into
757
+ that resolver; a pre-resolved `src` works without the peer registration entry. If the peer is not
758
+ installed, renders an empty template (see gotchas).
647
759
 
648
760
  Also exported from the package root:
649
761
  `languageToCountry(language: string): string | undefined` and the `LANGUAGE_TO_COUNTRY` lookup
@@ -653,15 +765,23 @@ need different defaults).
653
765
  ```html
654
766
  <lyra-flag country="fr" label="France"></lyra-flag>
655
767
  <lyra-flag language="en-US" round></lyra-flag>
768
+ <lyra-flag country="es" variant="compact"></lyra-flag> <!-- tiny WebP raster, icon-scale -->
769
+ <lyra-flag country="es" variant="detailed"></lyra-flag> <!-- pristine full-fidelity vector -->
656
770
  ```
657
771
 
658
772
  ```bash
659
773
  pnpm add @aceshooting/lyra-flags # required peer — without it, <lyra-flag> renders nothing
660
774
  ```
661
775
 
776
+ ```js
777
+ import '@aceshooting/lyra-ui/components/flag/flag-peer.js';
778
+ ```
779
+
662
780
  **Known gotchas:**
663
- - Requires the optional peer `@aceshooting/lyra-flags` to actually render an image; without it the
664
- component still shows a `<lyra-skeleton variant="rect">` placeholder (with `aria-busy="true"` on
781
+ - `country`/`language` resolution is opt-in through `components/flag/flag-peer.js`; the root barrel
782
+ registers the component without importing the optional flag asset graph. Requires the optional
783
+ peer `@aceshooting/lyra-flags` to actually render an image; without it the component still shows a
784
+ `<lyra-skeleton variant="rect">` placeholder (with `aria-busy="true"` on
665
785
  the host) while resolving, then settles into an **empty template** plus a one-time `console.warn`
666
786
  once the resolver rejects (lazy `import()`, cached module-wide so the warning fires only once per
667
787
  page even with many `<lyra-flag>` instances).
@@ -684,18 +804,22 @@ pnpm add @aceshooting/lyra-flags # required peer — without it, <lyra-flag> r
684
804
  newer one: each resolver call is tagged with a token captured at the time it was kicked off, and a
685
805
  `.then()` that fires after a later change (or after the field was cleared) is discarded instead of
686
806
  clobbering the rendered image/`loading`.
687
- - `country`/`language` resolve through `@aceshooting/lyra-flags`'s `flagUrl(code)`, which is itself
688
- genuinely code-split per flag: using `<lyra-flag country="fr">` anywhere in an app fetches only
689
- the flags actually requested at runtime (verified with a real Vite build referencing 2 codes
690
- shipped ~28 KB total, not all 249). If you already have a flag's URL at build time, `src` skips
691
- the peer-package round trip (and its loading-skeleton flash) entirely.
807
+ - `country`/`language` resolve through `@aceshooting/lyra-flags`'s `flagUrl(code)`, which lazily
808
+ fetches one requested flag at runtime. A bundler may still emit the complete reachable lazy-chunk
809
+ graph because every supported code has a literal loader import; use a literal asset subpath
810
+ import when the deployment artifact must be pruned. If you already have a flag's URL at build
811
+ time, `src` skips the peer-package round trip (and its loading-skeleton flash) entirely.
692
812
  - 65 of `@aceshooting/lyra-flags`' 249 flags (any whose design includes a detailed coat of
693
- arms/seal/emblem, e.g. `es`, `pt`, `sv`) ship a second, pristine full-detail source SVG alongside
694
- the default icon-optimized one (`flagUrl(code, { variant: 'detailed' })`, or `detailed` on
695
- `<lyra-flag>`) the default variant is the one `country`/`language` resolve to unless `detailed`
696
- is set, and is what almost every consumer wants (an icon-scale rendering, ~65% smaller on average
697
- for the 65 affected codes, no visible fidelity loss at that scale). `detailed` is a safe no-op for
698
- the other 184 codes same file either way.
813
+ arms/seal/emblem, e.g. `es`, `pt`, `sv`) ship **three** fidelity tiers, selected via the `variant`
814
+ property (`flagUrl(code, { variant })` under the hood): `"compact"` — a tiny WebP raster for
815
+ icon-scale use (menus, language pickers, dense lists); `"standard"` the default, the
816
+ icon-optimized vector for card/row sizes, ~65% smaller on average than the pristine source for the
817
+ 65 affected codes with no visible fidelity loss at that scale; `"detailed"` the pristine
818
+ full-fidelity vector, for hero-scale display where the extra illustrative detail is actually
819
+ visible. The other 184 codes resolve to the same file regardless of `variant` — a safe no-op. The
820
+ older boolean `detailed` attribute predates `variant` and is now **deprecated**
821
+ (`variant="detailed"` is the replacement); left unset, `variant` falls back to honoring `detailed`
822
+ for one more minor version before removal.
699
823
 
700
824
  ---
701
825
 
@@ -749,6 +873,9 @@ Loading placeholder (`text`/`circle`/`rect` shapes, `pulse`/`sheen` effects).
749
873
  - `effect: 'pulse'|'sheen' = 'pulse'` (reflected)
750
874
  - `width?: string`
751
875
  - `height?: string`
876
+ - `label: string = 'Loading…'` — accessible name for this instance's own `role="status"` (rendered as
877
+ visually-hidden text inside `[part="base"]`); override with a description of what's actually
878
+ loading, e.g. `label="Loading chart"`
752
879
 
753
880
  **Events:** none.
754
881
 
@@ -763,16 +890,18 @@ Loading placeholder (`text`/`circle`/`rect` shapes, `pulse`/`sheen` effects).
763
890
 
764
891
  ```html
765
892
  <lyra-skeleton variant="circle" width="3rem" height="3rem"></lyra-skeleton>
766
- <lyra-skeleton variant="text"></lyra-skeleton>
893
+ <lyra-skeleton variant="text" label="Loading name"></lyra-skeleton>
767
894
  ```
768
895
 
769
896
  **Known gotchas:**
770
- - every instance is an independent, simultaneously-firing `role="status"` live region
771
- (via `willUpdate` setting `role="status"` plus visually-hidden "Loading…" text). A realistic
772
- loading state (e.g. a 10×5 skeleton table = 50 instances) mounts 50 separate live regions at
773
- nearly the same tick — expect screen readers to announce "Loading…" dozens of times, not once.
774
- There's no grouping primitive to dedupe this; if it matters, wrap your own single `role="status"`
775
- region around a set of skeletons and mark the individual ones `aria-hidden`.
897
+ - every instance is an independent, simultaneously-firing `role="status"` live region (set via
898
+ `willUpdate`) with its own visually-hidden `label` text (default `"Loading…"`). A realistic loading
899
+ state (e.g. a 10×5 skeleton table = 50 instances) mounts 50 separate live regions at nearly the same
900
+ tick — expect screen readers to announce the label dozens of times, not once, even if every
901
+ instance shares the same default `label`. A more specific per-instance `label` doesn't fix the
902
+ fan-out by itself; there's still no grouping primitive to dedupe it — if it matters, wrap your own
903
+ single `role="status"` region around the set of skeletons and mark the individual ones
904
+ `aria-hidden`.
776
905
  - no `lines`/`count` shorthand for "N lines of skeleton text" — stamp out N elements
777
906
  yourself.
778
907
  - Respects `prefers-reduced-motion` (both effects) — safe to leave as-is for that concern.
@@ -824,7 +953,10 @@ render one itself), `sub` (rich sub-line content — overrides the `sub` attribu
824
953
  is provided)
825
954
 
826
955
  **CSS parts:** `base`, `icon`, `label`, `value-row`, `value`, `unit`, `trend`, `sub`, `spark`,
827
- `caption`, `rows`, `row`, `row-label`, `row-value`
956
+ `caption`, `rows`, `row`, `row-label`, `row-value` — `[part="value"]` gets `aria-labelledby` pairing
957
+ it with `[part="label"]`'s generated id whenever `label` is non-empty (so tabbing straight to the
958
+ `exactValue`-focusable value still announces e.g. "Revenue $1.2K", not just the bare value); each
959
+ `[part="row-value"]` is paired the same way with its own row's `[part="row-label"]`.
828
960
 
829
961
  **Themeable custom properties:** shared tokens only (`--lyra-color-success/-warning/-danger` drive
830
962
  the `variant`-colored value text and up/down trend pill; `--lyra-color-brand` drives `emphasis`'s
@@ -860,7 +992,9 @@ of `rows` — this component only renders and emits intents.
860
992
  (`'low'` hides first, under a ~900px container width; `'medium'` next, under ~640px; both
861
993
  breakpoints are fixed in `table.styles.ts`, not themeable tokens), reversible via
862
994
  `[part='reveal-columns-button']`; `sticky` pins that column's header cell/cells to the inline-start
863
- edge (`position: sticky`) while the table scrolls horizontally
995
+ edge (`position: sticky`) while the table scrolls horizontally — multiple `sticky` columns stack
996
+ left-to-right (each measures every earlier sticky column's rendered width via
997
+ `--lyra-table-sticky-offset`) instead of overlapping at the same edge
864
998
  - `columnsHidden: boolean = false` (attribute `columns-hidden`, reflected) — computed/read-only: true
865
999
  when a `priority` column is *actually* hidden right now by the `@container` breakpoints above, or
866
1000
  `showAllColumns` force-visible mode is currently active. Measured via a `ResizeObserver` on
@@ -873,7 +1007,10 @@ of `rows` — this component only renders and emits intents.
873
1007
  - `rows: T[] = []` (attribute: false)
874
1008
  - `sortKey: string = ''` (attribute `sort-key`)
875
1009
  - `sortDir: 'asc'|'desc' = 'asc'` (attribute `sort-dir`)
876
- - `rowKey?: (row: T) => string | number` (attribute: false)
1010
+ - `rowKey?: (row: T) => string | number` (attribute: false) — derives each row's stable identity for
1011
+ DOM-reconciliation and the delegated row click/keydown lookup; falls back to the row's array index
1012
+ when omitted, which is only safe while `rows` never reorders — set it whenever `rows` can be
1013
+ sorted/filtered/re-ordered across renders, or selection/click can silently attach to the wrong row
877
1014
  - `selectedKey: string | number | null = null` (attribute: false) — **single**-selection only
878
1015
  - `hasMore: boolean = false` (attribute `has-more`, reflected)
879
1016
  - `moreLabel: string = 'Load more'` (attribute `more-label`)
@@ -920,18 +1057,23 @@ body's `max-block-size`).
920
1057
  - only single-row selection is modeled (`selectedKey: string | number | null`);
921
1058
  there's no bulk-select/checkbox-column API — you must hand-roll a checkbox column entirely inside
922
1059
  a `cell()` callback if you need multi-select.
923
- - no `caption`/`aria-label` property, so the rendered `role="grid"` has no accessible name
924
- distinguishing it from any other grid on the page.
1060
+ - no `caption`/`aria-label` *property* exists, but a plain `aria-label` HTML attribute set on the
1061
+ host **is** forwarded into the shadow-DOM `<table role="grid">` (read via
1062
+ `this.getAttribute('aria-label')` at render time — a plain global-attribute read, not a reactive
1063
+ Lit `@property`): `<lyra-table aria-label="Scores">` gives the grid an accessible name; omit it and
1064
+ the shadow table simply has none.
925
1065
  - Full roving-tabindex grid keyboard pattern (one `tabindex="0"` stop among header cells, one among
926
1066
  body rows) — Left/Right/Home/End move within the header row, Up/Down/Home/End move within the
927
1067
  body, Down from the header enters the body's roving stop and Up from the body's first row returns
928
1068
  to the header, Enter/Space still only sort/activate — a genuine strength versus most siblings in
929
1069
  this family. A `priority`-hidden header/cell is skipped when computing the visible header stops,
930
1070
  so arrow-key navigation never strands the roving stop on a hidden column.
931
- - a `cell()` template can render its own interactive content (a button, link, input, etc.) without
932
- it being swallowed by row/column activation: clicks and Enter/Space landing on (or bubbling
933
- through) anything matching `button, a[href], input, select, textarea, [role="button"]` are left
934
- alone by the table's own delegated `click`/`keydown` handlers.
1071
+ - a `cell()` template can render its own interactive content without it being swallowed by
1072
+ row/column activation: clicks and Enter/Space landing on (or bubbling through) anything matching
1073
+ `button, a[href], input, select, textarea, [role="button"], [role="combobox"], [role="listbox"],
1074
+ [role="slider"]`, **or any custom element** (any tag name containing a hyphen — e.g. a
1075
+ `<lyra-select>`/`<lyra-combobox>` rendered inside a `cell()`), are left alone by the table's own
1076
+ delegated `click`/`keydown` handlers instead of triggering `lyra-sort`/`lyra-row-click`.
935
1077
 
936
1078
  ---
937
1079
 
@@ -979,11 +1121,9 @@ gauge-specific custom property exists).
979
1121
  element instead.
980
1122
  - Divide-by-zero guarded (`max - min || 1`), and radial/linear share one component via the `type`
981
1123
  attribute.
982
- - a non-finite `value`/`min`/`max` (e.g. left `NaN` mid-binding) no longer leaks a literal `"NaN"`
983
- into `aria-valuenow`/`aria-valuemax`/the rendered value text the affected ARIA attribute is
984
- omitted entirely instead, and a finite `value` is clamped into `[min, max]` before being written
985
- to `aria-valuenow` (the SVG arc/bar fill was already clamped via `ratio`; the ARIA number now
986
- matches what's drawn).
1124
+ - non-finite `value` text remains blank, while non-finite `min`/`max` use finite default domain
1125
+ bounds; no `NaN`/`Infinity` value leaks into the SVG geometry or ARIA attributes, and a finite
1126
+ value is clamped into the resolved domain before being announced.
987
1127
 
988
1128
  ---
989
1129
 
@@ -994,15 +1134,18 @@ opens a small menu).
994
1134
 
995
1135
  **Properties:**
996
1136
  - `rows: Record<string, unknown>[] = []` (attribute: false)
997
- - `columns: CsvColumn[] = []` (attribute: false) — `{ key, label }`; also acts as a field allow-list
998
- for **both** export formats when non-empty (CSV always projected through `columns`; JSON now picks
999
- the same `columns[].key` set rather than dumping every field on `rows`, so it can't leak a field
1000
- CSV would have hidden) leave `columns` empty to export every field of `rows` as-is in JSON
1137
+ - `columns: CsvColumn[] = []` (attribute: false) — `{ key, label }`; acts as a field allow-list **and**
1138
+ CSV header-label source for **both** export formats when non-empty. Left empty, **both** CSV and
1139
+ JSON fall back to the union of the rows' own keys (`key`/`label` both set to the key name) instead
1140
+ of CSV degrading to a header-less/blank file while only JSON had a fallback so an unconfigured
1141
+ export still produces a proper header + data file in either format
1001
1142
  - `filename: string = 'export'`
1002
1143
  - `formats: ExportFormat[] = ['csv']` (attribute: false; `ExportFormat = 'csv' | 'json'` **only** —
1003
1144
  no XLSX despite the family sometimes being described as covering it)
1004
- - `disabled: boolean = false` (reflected)
1005
- - `label: string = 'Export'`
1145
+ - `disabled: boolean = false` (reflected) — also disables every `[part="menu-item"]` button, not just
1146
+ the trigger
1147
+ - `label: string = 'Export'` — trigger button text; also feeds the format-choice menu's `aria-label`
1148
+ as `` `${label} format` `` so assistive tech gets an accessible name for the menu
1006
1149
  - `open: boolean = false` (reflected)
1007
1150
 
1008
1151
  **Events:** `lyra-export` (`detail: { format }`, **cancelable** — call `preventDefault()` to
@@ -1049,6 +1192,40 @@ downloadBlob(content: string, filename: string, mime: string): void // trig
1049
1192
 
1050
1193
  ---
1051
1194
 
1195
+ ## `lyra-copy-button`
1196
+
1197
+ A standalone icon-only copy-to-clipboard button for a plain text `value` — swaps its icon to a
1198
+ checkmark for ~1.5s on activation. Takes no positioning opinion of its own; a consumer
1199
+ wraps/positions the host element (e.g. absolutely positioned in the corner of a `wa-textarea` or a
1200
+ read-only output field). Unlike `lyra-code-block`'s or `lyra-json-viewer`'s own built-in copy
1201
+ buttons, this has no code/JSON content model to adopt just to reuse the copy affordance.
1202
+
1203
+ **Properties:**
1204
+ - `value: string = ''` — the plain text to copy.
1205
+
1206
+ **Events:** `lyra-copy` (`detail: { text: string }`) — fires on activation, always with the current
1207
+ `value`, regardless of whether the OS clipboard write actually succeeded (same convention as
1208
+ `lyra-code-block`'s/`lyra-json-viewer`'s own copy buttons).
1209
+
1210
+ **Slots:** none.
1211
+
1212
+ **CSS parts:** `base` — the button itself.
1213
+
1214
+ **Themeable custom properties:** shared tokens only.
1215
+
1216
+ **Optional peer deps:** none.
1217
+
1218
+ ```html
1219
+ <lyra-copy-button value="npm install @aceshooting/lyra-ui"></lyra-copy-button>
1220
+ ```
1221
+
1222
+ **Known gotchas:**
1223
+ - best-effort clipboard write: `navigator.clipboard` is absent in insecure contexts/older browsers,
1224
+ and some engines throw synchronously rather than rejecting — either way `lyra-copy` still fires
1225
+ with the intended text so a consumer can always show its own confirmation/fallback UI.
1226
+
1227
+ ---
1228
+
1052
1229
  ## `lyra-split`
1053
1230
 
1054
1231
  Resizable panels for dashboard layouts. Direct **light-DOM children are the panels**; a divider is
@@ -1083,6 +1260,20 @@ auto-inserted between each adjacent pair.
1083
1260
  - `floatBreakpoint: number = 400` (attribute `float-breakpoint`, px) — below this narrower container
1084
1261
  width, the collapsing panel instead becomes an absolutely-positioned overlay ("floating card") on
1085
1262
  top of the other pane(s), removed from the normal flex flow; the sibling(s) take the full width.
1263
+ - `collapseState: 'wide'|'rail'|'floating'` (reflected, attribute `collapse-state`) — a public
1264
+ accessor with force/auto semantics mirroring `<lyra-app-rail>`'s `mode`: normally derived
1265
+ automatically from the measured container width, but assigning it a concrete value pins it there
1266
+ (stopping automatic breakpoint tracking) — useful for a consumer-driven toggle (e.g. a button that
1267
+ forces `'floating'` regardless of width). Assigning the write-only `'auto'` sentinel releases the
1268
+ pin and immediately re-derives the state from the current measured width; the getter never returns
1269
+ `'auto'`.
1270
+ - `open: boolean = false` (reflected) — whether the `'floating'` collapse state's drawer is shown.
1271
+ While `collapseState` is `'floating'` and `open` is `false` (the default), the collapsing panel
1272
+ renders nothing (`hidden`, out of the accessibility tree) instead of the always-visible overlay
1273
+ card this state rendered before `open` existed. Setting `open = true` reveals it as a
1274
+ focus-trapped floating panel with a `[part="backdrop"]` scrim; Escape or a backdrop click set
1275
+ `open` back to `false`. Leaving `'floating'` while `open` is still `true` also closes it, the same
1276
+ way `<lyra-app-rail>` closes its mobile overlay when leaving `'mobile'` while open.
1086
1277
 
1087
1278
  `collapse`'s three resulting states — `'wide'` (default, today's plain layout) / `'rail'` / `'floating'`
1088
1279
  — are exposed as: a `data-collapse-state` attribute on both the host and the collapsing panel element
@@ -1092,12 +1283,17 @@ collapsed. `collapse="none"` (the default) is byte-for-byte identical to pre-col
1092
1283
 
1093
1284
  **Events:** `lyra-resize` (`detail: { sizes }`, fired on every drag step/release **and** every
1094
1285
  keyboard step), `lyra-split-collapse-change` (`detail: { state: 'wide'|'rail'|'floating' }`, fired only
1095
- on a real `collapse`-state transition, never on every resize/render)
1286
+ on a real `collapse`-state transition, never on every resize/render),
1287
+ `lyra-split-constraints-invalid` (`detail: SplitConstraintIssueDetail`, fired once when the configured
1288
+ panel minimums/maximums cannot fit the track; the infeasible set is rejected for interaction and a
1289
+ normalized percent minimum is used instead)
1096
1290
 
1097
1291
  **Slots:** default (each direct child element is one panel).
1098
1292
 
1099
1293
  **CSS parts:** `base` (`position: relative`, so the `'floating'` state can anchor to it), `divider`
1100
- (carries `aria-disabled="true"` and is drag/keyboard-inert while its adjacent panel is collapsed)
1294
+ (carries `aria-disabled="true"` and is drag/keyboard-inert while its adjacent panel is collapsed),
1295
+ `backdrop` (the `'floating'` drawer's scrim — only rendered while `collapseState === 'floating'` and
1296
+ `open`)
1101
1297
 
1102
1298
  **Themeable custom properties:** shared tokens only.
1103
1299
 
@@ -1128,8 +1324,9 @@ the visually-adjacent panel).
1128
1324
  `aria-valuemin`/`aria-valuemax` report. Each divider also now has its own `aria-label` ("Resize
1129
1325
  divider between panel N and panel N+1") distinguishing it from any other divider in a
1130
1326
  multi-divider layout.
1131
- - no feasibility check on `min` vs. panel count (e.g. 3 panels with `min=40` is
1132
- unsatisfiable) result is a silently frozen splitter with no warning.
1327
+ - infeasible aggregate constraints (for example, three panels with `min=40`) are reported through
1328
+ `lyra-split-constraints-invalid`; interaction rejects that set and uses a normalized percent minimum
1329
+ with aggregate slack, so the divider remains operable instead of silently freezing.
1133
1330
  - concurrent drags are tracked per `pointerId` (not a single scalar), so a multi-touch drag on two
1134
1331
  different dividers moves both independently instead of the second pointer clobbering the first's
1135
1332
  drag state; `pointercancel`/`lostpointercapture` (not just `pointerup`) both end a drag.
@@ -1201,10 +1398,10 @@ fullscreen panel/backdrop — it overrides the default `var(--lyra-space-l)` ins
1201
1398
  both `[part="base"]` and `[part="backdrop"]`. Set `compact` for tighter header/body padding.
1202
1399
 
1203
1400
  **Known gotchas:**
1204
- - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) restores the
1205
- scroll lock and the Tab-trap `keydown` listener if `fullscreen` was still `true` across the
1206
- move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in between, so
1207
- `willUpdate()` alone wouldn't otherwise notice.
1401
+ - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) resumes its
1402
+ shared overlay registration and re-acquires the scroll lock if `fullscreen` was still `true`
1403
+ across the move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in
1404
+ between, so `willUpdate()` alone wouldn't otherwise notice.
1208
1405
  - `collapsed` hides the body via `hidden` rather than an animated height transition — collapsing is
1209
1406
  instant, not a slide.
1210
1407
 
@@ -1215,15 +1412,27 @@ both `[part="base"]` and `[part="backdrop"]`. Set `compact` for tighter header/b
1215
1412
  Dependency-free SVG word/tag cloud. First-party invention (no Web Awesome equivalent). Lays words
1216
1413
  out via an outward Archimedean-spiral search — heaviest word placed first, each word spiraling from
1217
1414
  the center until it clears every word already placed. Unlike sibling `lyra-sparkline`/`lyra-heatmap`
1218
- (one `role="img"` glyph standing in for an aggregate value), each word here is its own focusable
1219
- `role="button"`, since the individual words are the meaningful interactive content.
1415
+ (one `role="img"` glyph standing in for an aggregate value), the individual words here *are* the
1416
+ meaningful interactive content but with up to `MAX_WORDS` (150) of them, making every single one
1417
+ its own tab stop would be a poor keyboard experience. Instead, like `lyra-heatmap`'s cells, the whole
1418
+ `[part="svg"]` is **one tab stop with roving arrow-key focus**: `ArrowRight`/`ArrowDown` move the
1419
+ focus cursor to the next word in **declaration order** (not weight/placement order),
1420
+ `ArrowLeft`/`ArrowUp` to the previous, `Home`/`End` to the first/last, and `Enter`/`Space` fires
1421
+ `lyra-word-click` for the currently-focused word. A `[part="focus-ring"]` `<rect>` is drawn around
1422
+ the focused word (absent until a word has actually been focused via keyboard or click), and a
1423
+ visually-hidden `[part="live-region"]` (`role="status" aria-live="polite"`) announces
1424
+ `"${text}, ${weight}"` on every focus move.
1220
1425
 
1221
1426
  **Properties:**
1222
1427
  - `words: WordCloudWord[] = []` (attribute: false) — `{ text: string, weight: number, color?:
1223
- string, group?: string }`; `weight` drives font size, `color` overrides the palette for that word,
1224
- `group` shares one palette color across every word with the same `group` value
1225
- - `minFontSize: number = 12` (attribute `min-font-size`) px, applied to the lowest-weight word
1226
- - `maxFontSize: number = 48` (attribute `max-font-size`) — px, applied to the highest-weight word
1428
+ string, group?: string }`; `weight` drives font size (a negative/non-finite `weight` is clamped to
1429
+ `0` for sizing purposes only the original value is still echoed verbatim in `lyra-word-click`'s
1430
+ `detail`), `color` overrides the palette for that word, `group` shares one palette color across
1431
+ every word with the same `group` value
1432
+ - `minFontSize: number = 12` (attribute `min-font-size`) — px, applied to the lowest-weight word;
1433
+ layout clamps positive finite values to at most 512px and uses 1px for invalid/non-positive values
1434
+ - `maxFontSize: number = 48` (attribute `max-font-size`) — px, applied to the highest-weight word;
1435
+ normalized by the same 1–512px layout bound (reversed min/max bounds are swapped)
1227
1436
  - `scale: 'linear'|'sqrt' = 'linear'` — `sqrt` compresses the weight→font-size mapping so one heavy
1228
1437
  word doesn't dwarf the rest, matching `lyra-heatmap`'s `scale` property
1229
1438
  - `orientations: 'horizontal'|'mixed' = 'horizontal'` — `mixed` lets ~25% of words render rotated
@@ -1231,12 +1440,20 @@ the center until it clears every word already placed. Unlike sibling `lyra-spark
1231
1440
  - `palette?: string[]` (attribute: false) — custom categorical colors, cycled by word index (or by
1232
1441
  `group`); defaults to the `--lyra-word-cloud-color-1..8` tokens
1233
1442
 
1443
+ **Methods:** `refreshTheme(): void` — forces a relayout so the `--lyra-font` custom property is
1444
+ re-read from computed style (font-family affects the canvas text measurement layout depends on);
1445
+ call this from your own theme-toggle handler, since there's no global theme-change event to
1446
+ subscribe to automatically (mirrors `lyra-chart`'s `refreshTheme()`).
1447
+
1234
1448
  **Events:** `lyra-word-click` (`detail: { text, weight, group }`, fires on click, or Enter/Space on
1235
- a focused word)
1449
+ the currently-focused word — a no-op if nothing is focused yet)
1236
1450
 
1237
1451
  **Slots:** none.
1238
1452
 
1239
- **CSS parts:** `base`, `svg`, `word` (each `<text>`), `empty` (the no-data placeholder)
1453
+ **CSS parts:** `base`, `svg`, `word` (each `<text>`), `focus-ring` (the rect around the roving-focus
1454
+ cursor's word), `live-region` (visually-hidden `role="status" aria-live="polite"` announcement text),
1455
+ `empty` (the no-data placeholder — hardcoded `"No data"` text, no property to customize it, unlike
1456
+ `lyra-empty`/`lyra-table`)
1240
1457
 
1241
1458
  **Themeable custom properties:** `--lyra-word-cloud-color-1` through `-8` (the default categorical
1242
1459
  palette, a data-driven literal exception like `lyra-heatmap`'s scale-ramp endpoints — exposed as
@@ -1257,20 +1474,34 @@ retheme-able custom properties instead of hardcoded), plus shared tokens (`--lyr
1257
1474
  </script>
1258
1475
  ```
1259
1476
 
1477
+ The host itself gets `role="group"` and an auto-computed `aria-label` (e.g. `"Word cloud of 12
1478
+ words"` / `"Word cloud of 1 word"`, counting only words actually rendered — post `MAX_WORDS`-cap and
1479
+ post-drop, not the raw `words.length`) **unless** the host already carried its own `role`/`aria-label`
1480
+ attribute at first render. That opt-out check runs exactly once (the very first update) and is never
1481
+ re-checked afterwards, so setting `role`/`aria-label` yourself *after* the component has already
1482
+ rendered at least once only sticks until the next `words`-driven relayout, which overwrites it back
1483
+ to the auto default — set it in the initial markup (or before first paint) to opt out permanently.
1484
+
1260
1485
  **Known gotchas:**
1261
1486
  - capped at 150 words (`MAX_WORDS` in `word-cloud-layout.ts`, mirroring `lyra-sparkline`'s
1262
- `MAX_BARS` DOM-node-count guard) — anything beyond that is silently dropped from layout (not
1263
- rendered, not an error); a pathological input (e.g. one huge word repeated many times) can also
1264
- exhaust the spiral search's radius bound and get dropped the same way.
1265
- - every word is its own tab stop (`tabindex="0"` `role="button"`), not a single roving-tabindex
1266
- container like `lyra-heatmap` fine for a modest word count, but tabbing through a cloud near the
1267
- 150-word cap is a lot of stops; there's no non-visual "get the whole word+weight list at once"
1268
- alternative yet.
1269
- - text width is measured via a detached `<canvas>` 2D context (`ctx.measureText`), not the actual
1270
- rendered SVG glyph metrics close enough for collision-avoidance spacing, but not pixel-exact.
1487
+ `MAX_BARS` DOM-node-count guard) — over the cap, the **heaviest** 150 survive and the rest are
1488
+ dropped, regardless of where they fell in the input array (it is not simply "first 150 in, rest
1489
+ dropped"). A pathological input (e.g. one huge word repeated many times) can also exhaust the
1490
+ spiral search's radius bound and get dropped the same way; blank/whitespace-only `text` is dropped
1491
+ too. Every drop reason logs one deduplicated `console.warn` per distinct skipped-count (not one
1492
+ warning per word, and not repeated twice for the same count) nothing throws.
1493
+ - each word's spiral search tests at most 4,096 candidate positions. Together with the 150-word and
1494
+ 512px font-size caps, this bounds placement work even for dense or adversarial layouts; a word
1495
+ that exhausts the search budget is reported through the same skipped-word path.
1496
+ - text width is measured via a detached `<canvas>` 2D context (`ctx.measureText`) at a hardcoded
1497
+ `font-weight: 600` matching `[part='word']`'s own default CSS — close enough for
1498
+ collision-avoidance spacing, but not pixel-exact, and overriding `[part='word']`'s `font-weight` via
1499
+ `::part()` desyncs measurement from what's actually painted (looser/denser packing, not a crash).
1271
1500
  - rotation (`orientations="mixed"`) is genuinely random per layout (`Math.random()`, not seeded), so
1272
1501
  which words render rotated changes on every re-layout (any `words`/`minFontSize`/`maxFontSize`/
1273
1502
  `scale`/`orientations` change) — don't rely on rotation being stable across renders.
1503
+ - only one word is ever in the page's tab sequence at a time (the roving cursor on `[part="svg"]`) —
1504
+ there's no way to Tab directly to the Nth word; arrow-key/Home/End your way there, or click it.
1274
1505
 
1275
1506
  ---
1276
1507
 
@@ -1279,7 +1510,10 @@ retheme-able custom properties instead of hardcoded), plus shared tokens (`--lyr
1279
1510
  ## `lyra-time-range`
1280
1511
 
1281
1512
  A two-handle brush/scrubber over a numeric domain (no date logic — callers map their own time axis
1282
- onto `[min, max]`).
1513
+ onto `[min, max]`). Form-associated (`static formAssociated = true`, via `ElementInternals`): an
1514
+ ancestor `<fieldset disabled>` disables both handles and every preset button through an internal
1515
+ `effectiveDisabled` getter, the same way it would a native `<input>`, without ever mutating the
1516
+ consumer-facing `disabled` property/attribute itself.
1283
1517
 
1284
1518
  **Properties:**
1285
1519
  - `min: number = 0`
@@ -1288,6 +1522,8 @@ onto `[min, max]`).
1288
1522
  - `end: number = 100`
1289
1523
  - `step: number = 1`
1290
1524
  - `disabled: boolean = false` (reflected)
1525
+ - `startLabel: string = 'Range start'` (attribute `start-label`) — `aria-label` for the start handle
1526
+ - `endLabel: string = 'Range end'` (attribute `end-label`) — `aria-label` for the end handle
1291
1527
  - `presets: TimeRangePreset[] = []` (attribute: false) — `TimeRangePreset { label: string; start:
1292
1528
  number; end: number }`; optional discrete presets (e.g. "Last 7 days") rendered as a
1293
1529
  `[part="presets"]` button row above the track — purely additive, the continuous brush is
@@ -1303,9 +1539,10 @@ key-up-commit, or when a preset button is clicked, `detail: { start, end }`)
1303
1539
  **CSS parts:** `base`, `track`, `range`, `handle-start`, `handle-end`, `presets`, `preset-button`
1304
1540
 
1305
1541
  **Themeable custom properties:** shared tokens only — `--lyra-color-border`, `--lyra-color-brand`,
1306
- `--lyra-color-surface`, `--lyra-shadow` (track/handles), plus (for `presets`) `--lyra-color-text`,
1307
- `--lyra-color-on-brand` (the active preset button's text), `--lyra-radius`, `--lyra-space-xs/-s`,
1308
- `--lyra-transition-fast`, `--lyra-focus-ring-*`. No component-local tokens.
1542
+ `--lyra-color-surface`, `--lyra-shadow` (track/handles), `--lyra-opacity-disabled` (`:host([disabled])`
1543
+ dimming), plus (for `presets`) `--lyra-color-text`, `--lyra-color-on-brand` (the active preset
1544
+ button's text), `--lyra-radius`, `--lyra-space-xs/-s`, `--lyra-transition-fast`,
1545
+ `--lyra-focus-ring-*`. No component-local tokens.
1309
1546
 
1310
1547
  **Optional peer deps:** none.
1311
1548
 
@@ -1339,6 +1576,14 @@ key-up-commit, or when a preset button is clicked, `detail: { start, end }`)
1339
1576
  moves both independently instead of the second pointer hijacking which handle the first pointer's
1340
1577
  moves apply to; `pointercancel`/`lostpointercapture` (not just `pointerup`) both end a drag, same
1341
1578
  fix as `lyra-split`.
1579
+ - Non-finite domain/handle values use finite fallback geometry, and non-finite or negative steps are
1580
+ treated as unstepped; invalid values never become `NaN`/`Infinity` CSS or ARIA strings.
1581
+ - `startLabel`/`endLabel` only override each handle's `aria-label`; they don't affect
1582
+ `aria-valuenow`/`aria-valuemin`/`aria-valuemax` (still raw numbers) or any visible text.
1583
+ - An ancestor `<fieldset disabled>` toggling is reflected via `formDisabledCallback` into
1584
+ `effectiveDisabled` (tracked separately from the consumer's own `disabled`), so re-enabling the
1585
+ fieldset correctly restores a handle that had `disabled` set explicitly by the consumer, and vice
1586
+ versa — mirrors `lyra-combobox`'s identical pattern.
1342
1587
 
1343
1588
  ---
1344
1589
 
@@ -1367,7 +1612,8 @@ tick and manual step)
1367
1612
 
1368
1613
  **Themeable custom properties:** shared tokens only — `--lyra-space-s`, `--lyra-color-border`,
1369
1614
  `--lyra-color-surface`, `--lyra-color-text`, `--lyra-color-brand`, `--lyra-icon-button-size` (the
1370
- play button's box).
1615
+ play button's box), `--lyra-opacity-disabled` (play button/slider dimming at `length <= 1`),
1616
+ `--lyra-focus-ring-*`.
1371
1617
 
1372
1618
  **Optional peer deps:** none.
1373
1619
 
@@ -1389,6 +1635,12 @@ play button's box).
1389
1635
  long-lived `setInterval`), so `intervalMs` is re-read fresh before every tick — changing
1390
1636
  `interval-ms` while `playing` takes effect on the very next step instead of only after a
1391
1637
  pause/play cycle.
1638
+ - `length` and `index` are normalized to finite non-negative integer counts, with `index` clamped
1639
+ into `[0, length)`; fractional, negative, `NaN`, infinite, and oversized values cannot poison
1640
+ end conditions or the slider.
1641
+ - `interval-ms` is clamped to the 16ms floor and the browser's finite timer ceiling: a non-finite or
1642
+ lower value ticks at 16ms, while an oversized value uses the timer ceiling. Each distinct invalid
1643
+ value is warned once (deduplicated per value, not a single once-ever flag).
1392
1644
  - No `aria-valuetext`/visible "N of M" position label on the range input.
1393
1645
  - Calling `play()`/`pause()` programmatically (not via the button) gives no `aria-live`
1394
1646
  announcement of the Play/Pause state change.
@@ -1411,19 +1663,27 @@ click, or Enter/Space on the focused cell, fires `lyra-cell-click`.
1411
1663
  - `colLabels: string[] = []` (attribute: false — matrix mode only)
1412
1664
  - `values: number[][] = []` (attribute: false — matrix mode only) — `-1` or any non-finite value is
1413
1665
  the "no data" sentinel; ragged/sparse rows are safe (`?? -1`)
1414
- - `cellSize: number = 22` (attribute `cell-size` — matrix mode only, ignored when `fitToWidth` is
1415
- set)
1416
- - `fitToWidth: boolean = false` (attribute `fit-to-width` matrix mode only; derives `cellSize` from
1417
- the host's measured `clientWidth` on every draw/resize instead of the fixed `cell-size`, so the
1418
- grid actually fills the available width see gotchas for the default, non-`fit-to-width` behavior)
1666
+ - `cellSize: number = 22` (attribute `cell-size` — default `22` in matrix mode, `11` in calendar
1667
+ mode when left unset; explicitly setting it now governs both modes' per-cell size alike, and it's
1668
+ ignored in either mode when `fitToWidth` is set)
1669
+ - `fitToWidth: boolean = false` (attribute `fit-to-width` derives `cellSize` from the host's
1670
+ measured `clientWidth` on every draw/resize instead of the fixed `cell-size`, so the grid actually
1671
+ fills the available width; now applies to calendar mode as well as matrix mode — see gotchas for
1672
+ the default, non-`fit-to-width` behavior)
1419
1673
  - `valueLabel: string = 'value'` (attribute `value-label`)
1420
- - `scale: 'linear' | 'sqrt' = 'linear'` (matrix mode only calendar mode always buckets by quartile)
1674
+ - `scale: 'linear' | 'sqrt' = 'linear'` governs both modes: in matrix mode, `'sqrt'` compresses the
1675
+ color ramp via `sqrtStep()` instead of mapping linearly; in calendar mode, the default `'linear'`
1676
+ still buckets by quartile (`quartileBucket()`, unchanged), while `'sqrt'` instead compresses via the
1677
+ same `sqrtStep()` magnitude compression as matrix mode, so one heavy day doesn't wash out the rest
1421
1678
  - `mode: 'matrix' | 'calendar' = 'matrix'`
1422
1679
  - `days: CalendarDay[] = []` (attribute: false — calendar mode only) — `CalendarDay { date:
1423
1680
  string /* ISO yyyy-mm-dd */; value: number }`; need not be sorted or contiguous, and an entry whose
1424
1681
  `date` doesn't parse is dropped rather than poisoning the whole grid
1682
+ - `firstDayOfWeek: number = 0` (attribute `first-day-of-week` — calendar mode only, no-op in matrix
1683
+ mode) — anchors the calendar grid at a different weekday instead of always Sunday; `0`-`6`, same
1684
+ numbering as `CalendarCellPos.weekday` (`0` Sunday .. `6` Saturday)
1425
1685
  - `bucketCount: number = 5` (attribute `bucket-count` — calendar mode only; non-finite values fall
1426
- back to 5 with a one-time console warning, and the count is floored to an integer >= 2)
1686
+ back to 5, while finite values are floored and clamped to 2–256 before the color-ramp allocation)
1427
1687
  - `annotations: HeatmapAnnotation[] = []` (attribute: false) — `HeatmapAnnotation { row?: number;
1428
1688
  col?: number; date?: string; label?: string }`: matrix mode matches by `row`/`col`, calendar mode
1429
1689
  by `date` (whichever pair matches the active `mode`; the other fields are ignored). Draws a
@@ -1432,14 +1692,21 @@ click, or Enter/Space on the focused cell, fires `lyra-cell-click`.
1432
1692
  - `cellText?: (pos: MatrixCellPos | CalendarCellPos, value: number) => string` (attribute: false) —
1433
1693
  formats the per-cell hover tooltip and keyboard live-region announcement text; receives the cell
1434
1694
  position (`{ row, col }` in matrix mode, `{ week, weekday }` in calendar mode) and its value.
1435
- Unset (the default) falls back to the built-in English "Row X, Col Y: value" / "Mon DD: value"
1436
- templateadditive, not breaking.
1695
+ Unset (the default) falls back to the built-in English "Row X, Col Y: value" (matrix) / "Jan 15:
1696
+ value"short month + day, **not** a weekday abbreviation (calendar) — template. Additive, not
1697
+ breaking.
1437
1698
  - `columnX?: (index: number) => number` (attribute: false, calendar mode only) — overrides the
1438
1699
  internal week-column x-coordinate formula (`CAL_PAD_LEFT + week * (CAL_CELL + CAL_GAP)`) used
1439
1700
  consistently across drawing, hit-testing, the focus ring, and month-label positioning, so a
1440
1701
  consumer can pixel-align this calendar's week columns with a sibling `<lyra-lite-chart>`'s bars
1441
1702
  (see that component's own `barX`) by supplying the same coordinate function to both. Unset (the
1442
1703
  default) is the original formula, unchanged.
1704
+ - `rowY?: (weekday: number) => number` (attribute: false, calendar mode only) — the vertical
1705
+ analogue of `columnX`: overrides the internal weekday-row y-coordinate formula (`CAL_LABEL_H +
1706
+ weekday * (cellSize + CAL_GAP)`), consulted consistently by drawing, hit-testing, and the focus
1707
+ ring (also consulted at `weekday = 7` to size the canvas height, mirroring `columnX` at
1708
+ `week = weekCount`). Unset (the default) is the original formula, unchanged. Ignored in matrix
1709
+ mode.
1443
1710
 
1444
1711
  **Events:** `lyra-cell-click` (fired on click, or Enter/Space on the keyboard-focused cell —
1445
1712
  `detail: { row, col, value }` in matrix mode, `detail: { date, value }` in calendar mode)
@@ -1466,7 +1733,9 @@ visible regardless of what it's drawn over). `--lyra-heatmap-tooltip-bg` (defaul
1466
1733
  `var(--lyra-color-surface)`) and `--lyra-heatmap-tooltip-text` (default `var(--lyra-color-text)`) —
1467
1734
  unlike the canvas-drawn tokens above, `[part="tooltip"]` is a real DOM element and consumes these
1468
1735
  directly, no `getComputedStyle` bridging needed. Also consumes `--lyra-color-text-quiet` (axis label
1469
- color) and `--lyra-space-xs`.
1736
+ color), `--lyra-space-xs`, `--lyra-radius`/`--lyra-shadow` (tooltip box), and `--lyra-focus-ring-width`/
1737
+ `--lyra-focus-ring-offset` (the real `[part="canvas"]:focus-visible` DOM outline, stroked in the
1738
+ same color as `--lyra-heatmap-focus-ring-color`).
1470
1739
 
1471
1740
  **Optional peer deps:** none.
1472
1741
 
@@ -1493,13 +1762,12 @@ color) and `--lyra-space-xs`.
1493
1762
  ```
1494
1763
 
1495
1764
  **Known gotchas:**
1496
- - the `ResizeObserver` only actually resizes the drawn grid in matrix mode **when
1497
- `fit-to-width` is set**. Without it (the default), `draw()` sizes the canvas as
1498
- `PAD_LEFT + cols * cellSize`, never from the host's measured width, so a container-resize redraw is
1499
- a geometric no-op; the stylesheet's `canvas { inline-size: 100% }` is also dead code in that case,
1500
- since `draw()` unconditionally sets an inline `canvas.style.width/height` that wins over it.
1501
- Calendar mode ignores `fit-to-width` entirely its canvas is always sized from the computed
1502
- `weekCount`.
1765
+ - the `ResizeObserver` only actually resizes the drawn grid **when `fit-to-width` is set**, in either
1766
+ mode. Without it (the default), `draw()` sizes the canvas as `PAD_LEFT + cols * cellSize` (matrix
1767
+ mode) or `CAL_PAD_LEFT + weekCount * cellSize` (calendar mode), never from the host's measured
1768
+ width, so a container-resize redraw is a geometric no-op; the stylesheet's
1769
+ `canvas { inline-size: 100% }` is also dead code in that case, since `draw()` unconditionally sets
1770
+ an inline `canvas.style.width/height` that wins over it.
1503
1771
  - the host is `role="group"` (not `role="img"`) with a dimensions+range summary `aria-label`
1504
1772
  (calendar mode: a day-count + range summary instead) — `[part="canvas"]` inside it is a real
1505
1773
  focusable, keyboard-operable, per-cell-interactive control (roving arrow-key focus,
@@ -1525,7 +1793,8 @@ A force-directed node-link diagram with pan/zoom/drag, built on `d3-force`.
1525
1793
 
1526
1794
  **Properties:**
1527
1795
  - `nodes: GraphNode[] = []` (attribute: false) — `GraphNode { id: string; label?: string; radius?:
1528
- number; color?: string }`
1796
+ number; color?: string }`; `radius` is clamped to `[6, 24]` (an unset/non-finite value falls back
1797
+ to the midpoint, `15`) so a node can never render invisibly small or absurdly large
1529
1798
  - `links: GraphLink[] = []` (attribute: false) — `GraphLink { source: string; target: string;
1530
1799
  width?: number }` (source/target are node ids; links whose endpoints don't resolve are silently
1531
1800
  dropped)
@@ -1547,7 +1816,8 @@ A force-directed node-link diagram with pan/zoom/drag, built on `d3-force`.
1547
1816
  **CSS parts:** `base`, `svg`, `node`, `link`, `label`
1548
1817
 
1549
1818
  **Themeable custom properties:** `--lyra-node-fill` (set inline per-node from `GraphNode.color`,
1550
- falls back to `--lyra-color-brand`); also uses `--lyra-color-border`, `--lyra-color-text`.
1819
+ falls back to `--lyra-color-brand`); also uses `--lyra-color-border` (link stroke), `--lyra-color-text`
1820
+ + `--lyra-font` (label text), `--lyra-focus-ring-*` (node/link `:focus-visible` outline).
1551
1821
 
1552
1822
  **Optional peer deps:** `d3-force`, `d3-drag`, `d3-zoom`, `d3-selection` (all four required
1553
1823
  together; lazy-`import()`ed once per page, `console.warn` once and renders empty if missing —
@@ -1574,6 +1844,10 @@ install with `pnpm add d3-force d3-drag d3-zoom d3-selection`).
1574
1844
  - `chargeStrength`/`linkDistance` **are** live-reactive post-mount now (retuned on the existing
1575
1845
  force objects and the simulation nudged via `alpha(0.3).restart()`) — no need to also touch
1576
1846
  `nodes`/`links` to see the effect.
1847
+ - `width`/`height` are also live-reactive post-mount: changing either re-centers the `forceCenter`
1848
+ force on the new midpoint and nudges the simulation via `alpha(0.1).restart()`, in addition to
1849
+ resizing the rendered `viewBox` — both branches apply independently, so setting `width` and
1850
+ `chargeStrength` in the same synchronous batch retunes both, not just one.
1577
1851
  - zoom is bounded via `minZoom`/`maxZoom` (`d3-zoom`'s `.scaleExtent(...)`, live-reactive); pan/
1578
1852
  zoom/drag are still pointer-only with no keyboard equivalent. Links (`<line part="link">`) are now
1579
1853
  keyboard-operable too (`tabindex="0"`, `role="button"`, `aria-label`, Enter/Space), matching nodes.
@@ -1636,7 +1910,9 @@ observed internally to keep the roving `activeId` in sync with clicks).
1636
1910
  **CSS parts:** `base`
1637
1911
 
1638
1912
  **Themeable custom properties:** shared tokens `--lyra-space-xs`/`-s`, `--lyra-color-brand-quiet`,
1639
- `--lyra-color-text-quiet`, `--lyra-color-border`, `--lyra-radius`.
1913
+ `--lyra-color-text-quiet`, `--lyra-color-border`, `--lyra-color-text`, `--lyra-radius`,
1914
+ `--lyra-focus-ring-*` (row `:focus-visible` ring, driven by `:host(:focus-visible)` since the host
1915
+ itself is the focusable `role="treeitem"`).
1640
1916
 
1641
1917
  **Optional peer deps:** none.
1642
1918
 
@@ -1710,7 +1986,8 @@ Chart.js wrapper every other `lyra-*-chart` tag subclasses; supports both a simp
1710
1986
  **Properties:**
1711
1987
  - `type: LyraChartType = 'line'` — `LyraChartType = 'line' | 'bar' | 'scatter' | 'pie' | 'doughnut' |
1712
1988
  'radar' | 'polarArea' | 'bubble'` — every type string the typed subclasses below lock `type` to is
1713
- already a first-class member, so `<lyra-chart type="pie">` needs no subclass or cast to work
1989
+ already a first-class member, so `<lyra-chart type="pie">` needs no subclass or cast to work;
1990
+ unknown runtime attribute/property values fall back to `line` before reaching Chart.js
1714
1991
  - `labels: string[] = []` (attribute: false)
1715
1992
  - `datasets: Series[] = []` (attribute: false) — `Series { label: string; data?: (number|null)[];
1716
1993
  points?: {x,y,label?}[]; color?: string|string[]; fill?: boolean; width?: number; dash?: boolean;
@@ -1730,7 +2007,8 @@ Chart.js wrapper every other `lyra-*-chart` tag subclasses; supports both a simp
1730
2007
  meaningful for `bar`/`line` types (scatter/bubble's linear `x` scale and the radial `r` scale used
1731
2008
  by radar/polar-area are out of scope)
1732
2009
  - `config?: Partial<ChartConfiguration>` (attribute: false) — deep-merged over the generated
1733
- config; any nested key wins without clobbering sibling generated keys
2010
+ config; any nested key wins without clobbering sibling generated keys. This is the raw Chart.js
2011
+ escape hatch, so a caller-supplied `config.type` is passed through rather than normalized.
1734
2012
 
1735
2013
  **Methods:** `resetZoom()` (reset any active zoom/pan to the original view), `refreshTheme()`
1736
2014
  (forces a redraw so the `--lyra-chart-*` tokens below are re-read from the current computed style —
@@ -1753,11 +2031,16 @@ properties only cascade downward); `--lyra-chart-grid-color` (default `var(--lyr
1753
2031
  (default `var(--lyra-color-text)`), `--lyra-chart-tooltip-bg` (default `var(--lyra-color-surface)`),
1754
2032
  `--lyra-chart-tooltip-text` (default `var(--lyra-color-text)`) — each resolved fresh via
1755
2033
  `getComputedStyle` on every draw (Chart.js renders to canvas, not the DOM, so it can't consume CSS
1756
- `var()` directly), driving the grid lines, tick labels, legend text, and tooltip background/text
1757
- respectively; plus shared `--lyra-space-xs`.
2034
+ `var()` directly), driving the grid lines, tick labels **and axis titles** (`xLabel`/`yLabel`/
2035
+ `y2Label` title text reuses `--lyra-chart-tick-color` too — there's no separate title-color token),
2036
+ legend text, and tooltip background/text respectively; plus shared `--lyra-space-xs`.
1758
2037
 
1759
- **Optional peer deps:** `chart.js`, `chartjs-plugin-zoom` (lazy-loaded once per page via
1760
- `chart-loader.ts`; registers only the tree-shaken controller/element/scale subset used).
2038
+ **Optional peer deps:** `chart.js` (mandatory peer, lazy-imported on every `connectedCallback()`
2039
+ regardless of `zoom`), `chartjs-plugin-zoom` (lazy-imported *additionally* only when `zoom` is or
2040
+ later becomes — `true`; never fetched for a chart that keeps `zoom` unset/false, since the plugin
2041
+ has a hard dependency on `hammerjs`). Both loads are memoized once per page via `chart-loader.ts`
2042
+ (`loadChartJs()` / `loadChartJsWithZoom()`), registering only the tree-shaken controller/element/scale
2043
+ subset actually used.
1761
2044
 
1762
2045
  ```html
1763
2046
  <lyra-chart type="line" x-label="Day" y-label="kWh" legend></lyra-chart>
@@ -1777,20 +2060,26 @@ respectively; plus shared `--lyra-space-xs`.
1777
2060
  a consumer flipping something upstream (e.g. a `data-theme` attribute) that doesn't otherwise
1778
2061
  change any `lyra-chart` property must call `refreshTheme()` itself to make Chart.js re-read the
1779
2062
  `--lyra-chart-*` tokens above.
1780
- - generated `scales` are type-appropriate: no scale at all for `lyra-pie-chart`/
1781
- `lyra-doughnut-chart`, and a single radial `r` scale (respecting `beginAtZero`) for
1782
- `lyra-radar-chart`/`lyra-polar-area-chart`, instead of always generating the cartesian `x`/`y`/`y2`
1783
- block. `xLabel`/`yLabel`/`y2Label` are still silently inert for all four of those subclasses (a
2063
+ - generated `scales` are keyed off the *effective* type (`config.type` ?? `type`, see
2064
+ `effectiveType()`) and are type-appropriate: no scale at all for `type="pie"`/`"doughnut"` (true of
2065
+ `<lyra-chart type="pie">` directly, not just the `lyra-pie-chart`/`lyra-doughnut-chart` subclasses),
2066
+ and a single radial `r` scale (respecting `beginAtZero`) for `type="radar"`/`"polarArea"`
2067
+ (`lyra-radar-chart`/`lyra-polar-area-chart`), instead of always generating the cartesian `x`/`y`/
2068
+ `y2` block. `xLabel`/`yLabel`/`y2Label` are still silently inert for all four of those types (a
1784
2069
  radial scale and "no scale" both have nowhere to put an axis title) — reach a titled radial scale
1785
2070
  only via raw `config`.
1786
2071
  - No `chartjs-plugin-annotation` is registered by default — reachable only by importing it
1787
2072
  separately and using the raw `config` passthrough (Chart.js's registry is a global singleton).
1788
- - while the `chart.js` peer is resolving, the host shows a `<lyra-skeleton variant="rect">` with
1789
- `aria-busy="true"` (same lazy-load pattern as `lyra-graph`/`lyra-map`/`lyra-flag`); Chart.js's own
1790
- ~1s draw-in animation is skipped under `prefers-reduced-motion: reduce`. The raw `config`
1791
- passthrough is deep-merged with `__proto__`/`constructor`/`prototype` keys skipped unconditionally,
1792
- so a JSON-sourced `config` (e.g. parsed from an API response) can't reach up and pollute
1793
- `Object.prototype` through the merge.
2073
+ - while the `chart.js` peer is resolving, `render()` swaps in a `<lyra-skeleton variant="rect">` for
2074
+ the canvas, and the **host element itself** (not the skeleton) carries `aria-busy="true"` set/
2075
+ cleared in `updated()` off the private `loading` state (same lazy-load pattern as
2076
+ `lyra-graph`/`lyra-map`/`lyra-flag`). Chart.js's own ~1000ms draw-in animation only ever fires on
2077
+ initial construction or a type change that rebuilds the `Chart` instance (every in-place data
2078
+ update already passes `'none'` to `Chart#update()` and never animates regardless); that
2079
+ construction-time animation is additionally skipped outright under `prefers-reduced-motion: reduce`.
2080
+ The raw `config` passthrough is deep-merged with `__proto__`/`constructor`/`prototype` keys skipped
2081
+ unconditionally, so a JSON-sourced `config` (e.g. parsed from an API response) can't reach up and
2082
+ pollute `Object.prototype` through the merge.
1794
2083
  - lazy-redraw + memoization: an `IntersectionObserver` gates `draw()` — while the host is scrolled
1795
2084
  off-screen, property changes that would otherwise trigger a Chart.js redraw are skipped (and a
1796
2085
  single redraw fires once it re-enters the viewport). Independently, a content-signature check
@@ -1843,6 +2132,25 @@ passthrough). Not a subclass of `LyraChart`.
1843
2132
  consumer can pixel-align this chart's bars with a sibling `<lyra-heatmap>` calendar's week columns
1844
2133
  (see that component's own `columnX`) by supplying the same coordinate function to both. Unset (the
1845
2134
  default) is the original formula, unchanged.
2135
+ - `pointText?: (label: string, value: number, datasetIndex: number) => string` (attribute: false) —
2136
+ overrides the per-bar/per-point `<title>`/`aria-label` tooltip text (mirrors `lyra-heatmap`'s
2137
+ `cellText`). Falls back to the built-in raw-value template when unset.
2138
+ - `roundedBars: boolean = false` (attribute `rounded-bars`, bar type only) — draws each bar as a
2139
+ rounded-top-corner shape instead of a square-cornered `<rect>`.
2140
+ - `skipZero: boolean = false` (attribute `skip-zero`, bar type only) — omits a bar entirely (no
2141
+ mark/tabindex/tooltip) for a value that is exactly `0`; `null`/non-finite values are always
2142
+ skipped regardless.
2143
+ - `padLeft?: number` (attribute `pad-left`) — overrides the internal 36px `PAD_LEFT` plot-left-
2144
+ padding constant. Unset keeps the fixed 36px.
2145
+ - `barGapRatio?: number` (attribute `bar-gap-ratio`) — overrides the internal 0.2 `BAR_GROUP_GAP`
2146
+ fraction of a category slot left as a gap between categories. Unset keeps the fixed 0.2.
2147
+ - `scale: 'linear' | 'sqrt' = 'linear'` (bar type only) — `'sqrt'` maps a bar's value to height via
2148
+ `Math.sqrt(value / domainMax)` instead of the standard linear `niceDomain` fraction (mirroring
2149
+ `lyra-heatmap`'s matrix-mode `sqrt` scale), so a skewed dataset's smaller bars aren't washed out
2150
+ by one dominant value; gridlines/tick labels stay on the linear domain either way, and `type="line"`
2151
+ ignores it entirely.
2152
+ - `hideAxis: boolean = false` (attribute `hide-axis`) — suppresses gridlines/y-axis tick labels
2153
+ altogether; x-axis category labels (rendered separately) are unaffected.
1846
2154
 
1847
2155
  **Events:** `lyra-point-click` — fired when a bar/point is activated (click, or Enter/Space while
1848
2156
  focused). `detail: { datasetIndex: number, index: number, label: string | undefined, value: number
@@ -1934,7 +2242,8 @@ Bins `values` into `bins` equal-width buckets and renders as a bar chart (extend
1934
2242
  `type` fixed to `'bar'`).
1935
2243
 
1936
2244
  **Properties:**
1937
- - `bins: number = 10`
2245
+ - `bins: number = 10` — finite values are floored and clamped to 0–1,000 before allocation;
2246
+ non-finite values produce no buckets
1938
2247
  - `values: number[] = []` (attribute: false)
1939
2248
  - `label: string = 'Frequency'`
1940
2249
  - `labels`/`datasets`/`type` are **derived, read-only** (installed as getter/setter pairs on the
@@ -1957,6 +2266,8 @@ Bins `values` into `bins` equal-width buckets and renders as a bar chart (extend
1957
2266
  **Known gotchas:**
1958
2267
  - `bins <= 0` no longer crashes: `binValues()` now returns an empty bucket array for `binCount <= 0`
1959
2268
  (or empty `values`), so the histogram just renders with no bars instead of throwing.
2269
+ - excessively large finite bin counts are capped at 1,000, preventing an attribute or direct
2270
+ property write from requesting an unbounded bucket array.
1960
2271
 
1961
2272
  ---
1962
2273
 
@@ -2024,12 +2335,16 @@ raw `map` escape hatch for anything unexposed.
2024
2335
  `[value, color]` pair — an empty array is ignored, leaving whatever fill layer already exists, if
2025
2336
  any, untouched, rather than being applied)
2026
2337
  - `markers: MapMarker[] = []` (attribute: false) — `MapMarker { id?: string; lngLat: [number,
2027
- number]; color?: string; label?: string; html?: string }`; reconciled by `id` (falling back to a
2028
- `lng,lat` key when `id` is omitted) so an unchanged marker isn't torn down and recreated on every
2029
- `markers` reassignment its `lngLat` **and** its popup content (`html`/`label`, in that
2030
- precedence) are both updated in place, and the popup is removed if a later update sets neither.
2031
- `html` is rendered via `Popup.setHTML()` **raw markup, inline event handlers included** only
2032
- pass trusted content, sanitize anything derived from user input first.
2338
+ number]; color?: string; label?: string; unsafeHtml?: string }`; reconciled by `id` (falling back
2339
+ to a `lng,lat` key, disambiguated by occurrence order for duplicate-coordinate id-less markers,
2340
+ when `id` is omitted) so an unchanged marker isn't torn down and recreated on every `markers`
2341
+ reassignment its `lngLat` **and** its popup content (`unsafeHtml`/`label`, in that precedence)
2342
+ are both updated in place, and the popup is removed if a later update sets neither. `unsafeHtml` is
2343
+ rendered via `Popup.setHTML()` — **raw markup, inline event handlers included** only pass trusted
2344
+ content, sanitize anything derived from user input first; prefer `label` (`Popup.setText()`,
2345
+ escaped) when the content is plain text. A marker whose `color` changes for a persisting `id`
2346
+ can't be recolored in place (no `Marker.setColor()`) and is torn down/reconstructed instead — see
2347
+ gotchas.
2033
2348
 
2034
2349
  **Getters:** `map` → the raw `maplibregl.Map` instance.
2035
2350
 
@@ -2041,7 +2356,7 @@ exists and was hit)
2041
2356
 
2042
2357
  **CSS parts:** `base`, `container`, `legend`, `legend-swatch`
2043
2358
 
2044
- **Themeable custom properties:** shared tokens only — `--lyra-space-s`, `--lyra-color-surface`,
2359
+ **Themeable custom properties:** shared tokens only — `--lyra-space-xs/-s`, `--lyra-color-surface`,
2045
2360
  `--lyra-color-border`, `--lyra-shadow`, `--lyra-radius`.
2046
2361
 
2047
2362
  **Optional peer deps:** `maplibre-gl` (lazy-loaded; consumer must **separately** `import
@@ -2075,16 +2390,27 @@ exists and was hit)
2075
2390
  about.
2076
2391
  - Point markers now have a declarative API (`markers`, above) with popup support — the `.map` escape
2077
2392
  hatch and manual `new maplibregl.Marker()` are no longer the only way to place pins.
2393
+ - a marker whose `color` changes for a persisting `id` is torn down and reconstructed (maplibre-gl's
2394
+ `Marker` has no `setColor()`) rather than mutated in place — this also closes any popup the user
2395
+ currently has open on that marker (a fresh, closed `Popup` is built for the new instance); an
2396
+ accepted side effect of the reconstruction fallback, not a bug.
2078
2397
  - No click-select visual highlight on choropleth features (no `setFeatureState` call) — clicking
2079
2398
  only fires the event, no built-in visual feedback. Popups are still only reachable declaratively
2080
- through `markers`' `html`/`label` — a choropleth-feature click still has no built-in popup, only the
2081
- raw `lyra-map-click` event.
2399
+ through `markers`' `unsafeHtml`/`label` — a choropleth-feature click still has no built-in popup,
2400
+ only the raw `lyra-map-click` event.
2082
2401
  - `LegendEntry.color` is validated against a strict CSS-color-syntax allowlist before being applied
2083
2402
  to the legend swatch's `background`, rejecting anything that isn't recognizable color syntax
2084
2403
  (notably `url(...)`, which `background` also accepts and would otherwise fetch as soon as the
2085
2404
  swatch renders).
2086
2405
  - while the `maplibre-gl` peer is resolving, the host shows a `<lyra-skeleton variant="rect">` with
2087
2406
  `aria-busy="true"` in place of the map container.
2407
+ - construction of the real `maplibregl.Map` (and its WebGL context) is additionally gated on this
2408
+ element being observed intersecting the viewport (`IntersectionObserver`), independent of whether
2409
+ the `maplibre-gl` peer has already loaded — an off-screen `<lyra-map>` swaps its skeleton for the
2410
+ empty `[part="container"]` div as soon as the peer resolves, but `map` stays `undefined` and
2411
+ `lyra-map-load` never fires until the element is actually scrolled into view. Deliberate: caps
2412
+ concurrent WebGL contexts when many `<lyra-map>`s sit in one dashboard/grid. Skipped entirely
2413
+ (constructs immediately once the peer loads) when `IntersectionObserver` itself is unavailable.
2088
2414
 
2089
2415
  ---
2090
2416
 
@@ -2122,7 +2448,9 @@ element carrying the drag accept/reject announcement)
2122
2448
 
2123
2449
  **Themeable custom properties:** shared tokens only — `--lyra-space-xs`, `--lyra-space-l`,
2124
2450
  `--lyra-color-border`, `--lyra-radius`, `--lyra-color-surface`, `--lyra-color-text-quiet`,
2125
- `--lyra-color-success` (drag-accept state), `--lyra-color-danger` (drag-reject state).
2451
+ `--lyra-color-success` (drag-accept state), `--lyra-color-danger` (drag-reject state),
2452
+ `--lyra-focus-ring-width/-color/-offset` (`[part="base"]:focus-visible` outline),
2453
+ `--lyra-opacity-disabled` (`:host([disabled])` dimming).
2126
2454
 
2127
2455
  **Optional peer deps:** none.
2128
2456
 
@@ -2167,35 +2495,58 @@ General-purpose modal/overlay plus a promise-based confirmation helper built on
2167
2495
  ### `lyra-dialog`
2168
2496
 
2169
2497
  A modal/overlay: `role="dialog"`, focus-trapped while open, dismissible via Escape or a backdrop
2170
- click, and scroll-locks the document for as long as it's open. Chrome stays minimal — no built-in
2171
- title bar or close button; a consumer supplies a heading and any close affordance itself via the
2172
- default/`footer` slots.
2498
+ click, and scroll-locks the document for as long as it's open. Chrome stays minimal by default — no
2499
+ built-in title bar or close button; a consumer supplies a heading and any close affordance itself via
2500
+ the default/`footer` slots. `heading`/`closable` are an opt-in convenience for the common case where
2501
+ hand-building that chrome isn't worth it.
2173
2502
 
2174
2503
  **Properties:**
2175
2504
  - `open: boolean = false` (reflected) — there is no separate `show()`/`hide()` pair; set this (or
2176
2505
  call `close()`)
2177
- - `label: string = ''` — accessible name used only when no heading is slotted (see below)
2506
+ - `label: string = ''` — accessible name used only when no heading is slotted and `heading` is unset
2507
+ (see below)
2508
+ - `heading?: string` — visible header text (rendered in `[part="header"]`/`[part="heading"]`), used
2509
+ only when no light-DOM heading is slotted into the default slot; has no effect (renders nothing)
2510
+ when a heading element *is* slotted, which keeps working completely unchanged either way
2511
+ - `closable: boolean = false` (attribute `closable`) — renders a built-in close (X) button in the
2512
+ header row (creating one, with no heading text, if `heading` is unset), wired to the same
2513
+ `close()` path Escape/backdrop-dismiss already use, with reason `'close-button'`
2178
2514
 
2179
2515
  **Methods:** `close(reason: DialogCloseReason = 'api'): void` — closes the dialog, emits
2180
2516
  `lyra-dialog-close` with `reason`, and returns focus to whatever had it right before the dialog
2181
- opened. `DialogCloseReason = 'escape' | 'backdrop' | 'api' | string` `'escape'`/`'backdrop'` are
2182
- emitted by the dialog's own built-in dismiss triggers; any other string is whatever a caller passes
2183
- (e.g. a footer Cancel button calling `dlg.close('cancel')`, or `confirm()`'s own `'confirm'`/`'cancel'`).
2517
+ opened. `DialogCloseReason = 'escape' | 'backdrop' | 'close-button' | 'api' | 'unmount' | string`
2518
+ `'escape'`/`'backdrop'` are emitted by the dialog's own built-in dismiss triggers; `'close-button'`
2519
+ by the built-in header close button (rendered when `closable` is set); `'unmount'` is emitted
2520
+ automatically if the dialog is removed from the DOM while still `open` by anything other than its
2521
+ own `close()` (a consumer's own cleanup code, a parent re-render that drops it); any other string is
2522
+ whatever a caller passes (e.g. a footer Cancel button calling `dlg.close('cancel')`, or `confirm()`'s
2523
+ own `'confirm'`/`'cancel'`).
2184
2524
 
2185
2525
  **Events:** `lyra-dialog-close` (`detail: DialogCloseReason`) — fired on every dismissal path
2186
- (Escape, backdrop click, or any `close()` call).
2526
+ (Escape, backdrop click, any `close()` call, or an `'unmount'` removal as above).
2527
+
2528
+ **Stacking:** participates in the shared per-document overlay stack described above. A dialog can be
2529
+ stacked with another dialog or any other modal family; only the visually topmost overlay receives
2530
+ Escape, Tab trapping, or backdrop dismissal, while overlays beneath remain open until the top one
2531
+ closes.
2187
2532
 
2188
2533
  **Slots:** default (the dialog body), `footer` (action buttons, rendered in a bottom row, hidden
2189
2534
  entirely when empty)
2190
2535
 
2191
2536
  **CSS parts:** `backdrop` (the full-viewport scrim), `panel` (the dialog panel, `role="dialog"`
2192
- while open), `label` (the invisible label-text element used for `aria-labelledby` when no heading
2193
- is slotted), `body` (wrapper around the default slot), `footer` (wrapper around the `footer` slot)
2537
+ while open; its max-inline-size is controlled by `--lyra-dialog-max-width`), `header` (the header
2538
+ row, rendered when `heading` is set and no heading is slotted and/or `closable` is `true`),
2539
+ `heading` (the visible `heading`-text element inside `header`, rendered only when `heading` is set
2540
+ and no heading is slotted), `close-button` (the built-in close button inside `header`, rendered only
2541
+ when `closable` is `true`), `label` (the invisible label-text element used for `aria-labelledby`
2542
+ when no heading is slotted and `heading` is unset), `body` (wrapper around the default slot),
2543
+ `footer` (wrapper around the `footer` slot)
2194
2544
 
2195
2545
  **Themeable custom properties:** `--lyra-dialog-overlay-color` (default `rgb(0 0 0 / 0.5)` — the
2196
2546
  backdrop scrim color; component-specific since no shared `--wa-*`/`--lyra-*` overlay token exists),
2197
- plus shared tokens `--lyra-space-l/-m/-s`, `--lyra-color-surface/-border`, `--lyra-radius`,
2198
- `--lyra-shadow`.
2547
+ `--lyra-dialog-max-width` (default `32rem` — the panel's max-inline-size, via
2548
+ `min(var(--lyra-dialog-max-width, 32rem), 100%)`), plus shared tokens `--lyra-space-l/-m/-s`,
2549
+ `--lyra-color-surface/-border`, `--lyra-radius`, `--lyra-shadow`.
2199
2550
 
2200
2551
  **Optional peer deps:** none.
2201
2552
 
@@ -2216,14 +2567,21 @@ plus shared tokens `--lyra-space-l/-m/-s`, `--lyra-color-surface/-border`, `--ly
2216
2567
  </script>
2217
2568
  ```
2218
2569
 
2219
- Accessible name resolution: if a heading element (`h1`–`h6` or `[role="heading"]`) is a *direct
2220
- child* (not inside `slot="footer"`), its text content becomes `aria-label` on the panel. Otherwise,
2221
- when `label` is set, an invisible (`.sr-only`, exposed as the `label` part) element carrying that
2222
- text is rendered inside the panel and `aria-labelledby` points at it instead. Either way `label`
2223
- itself never renders visible chrome a slotted heading is what a sighted user sees. The heading
2224
- case deliberately uses `aria-label` (a copied string) rather than `aria-labelledby` pointing at the
2225
- heading's `id`, because the heading is light-DOM content while `[part="panel"]` lives in shadow DOM
2226
- and an ID-reference attribute can't resolve across that boundary.
2570
+ Accessible name / visible header, in priority order: (1) if a heading element (`h1`–`h6` or
2571
+ `[role="heading"]`) is a *direct child* (not inside `slot="footer"`), its text content becomes
2572
+ `aria-label` on the panel takes priority over `heading` below so an existing consumer that already
2573
+ slots its own heading keeps rendering it exactly as before; (2) otherwise, when `heading` is set, a
2574
+ visible header row (`[part="header"]`) renders containing that text (`[part="heading"]`), which
2575
+ becomes the `aria-labelledby` target; (3) otherwise, when `label` is set, an invisible (`.sr-only`,
2576
+ exposed as the `label` part) element carrying that text is rendered inside the panel and
2577
+ `aria-labelledby` points at it instead. Only one of cases 2/3 ever renders at a time. `label` itself
2578
+ never renders visible chrome on its own — `::part(label)` can be restyled to make the sr-only text
2579
+ visible, or `heading` can be set instead, for visible chrome without slotting a real heading element.
2580
+ The slotted-heading case (1) deliberately uses `aria-label` (a copied string) rather than
2581
+ `aria-labelledby` pointing at the heading's `id`, because the heading is light-DOM content while
2582
+ `[part="panel"]` lives in shadow DOM and an ID-reference attribute can't resolve across that
2583
+ boundary; cases 2/3 use `aria-labelledby` safely since their target renders inside the same shadow
2584
+ root it labels.
2227
2585
 
2228
2586
  **Known gotchas:**
2229
2587
  - `role="dialog"`/`aria-modal="true"` are only present on `[part="panel"]` while `open` is `true` —
@@ -2233,8 +2591,8 @@ and an ID-reference attribute can't resolve across that boundary.
2233
2591
  `aria-label`; set `label` instead for a title that needs to change live.
2234
2592
  - Only *direct* children are scanned for a heading — one nested several layers deep, or inside a
2235
2593
  slotted custom element's own shadow root, is left to the consumer to label explicitly via `label`.
2236
- - A reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) restores the
2237
- scroll lock and the Escape/Tab-trap `keydown` listener if `open` was still `true` across the
2594
+ - A reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) resumes its
2595
+ shared overlay registration and re-acquires the scroll lock if `open` was still `true` across the
2238
2596
  move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in between, so
2239
2597
  `willUpdate()` alone wouldn't otherwise notice.
2240
2598
  - Tab-trap focus order follows the default (body) slot, then the `footer` slot — the same order the
@@ -2247,7 +2605,7 @@ and an ID-reference attribute can't resolve across that boundary.
2247
2605
  A drop-in async replacement for `window.confirm()`, built on `<lyra-dialog>`.
2248
2606
 
2249
2607
  ```ts
2250
- import { confirm } from '@aceshooting/lyra-ui';
2608
+ import { confirm } from '@aceshooting/lyra-ui/components/dialog/confirm.js';
2251
2609
 
2252
2610
  const ok = await confirm({
2253
2611
  title: 'Delete conversation?',
@@ -2278,9 +2636,10 @@ used is still a `--lyra-*` token reference, never a raw literal.
2278
2636
  `<lyra-dialog>`'s own `close()`/`lyra-dialog-close` event, so there is exactly one place that
2279
2637
  resolves the promise and tears the dialog down — a consumer never needs to (and shouldn't) call
2280
2638
  `.remove()` itself.
2281
- - Both buttons reuse `--lyra-color-on-brand` for their text color, even in the `danger` tone — there
2282
- is no separate `--lyra-color-on-danger` token, and `--lyra-color-on-brand` happens to be white in
2283
- both light and dark themes so it reads fine over the danger fill too.
2639
+ - The neutral confirm button pairs `--lyra-color-on-brand` with `--lyra-color-brand`; the danger
2640
+ tone pairs `--lyra-color-on-danger` with `--lyra-color-danger`. Each token chains through Web
2641
+ Awesome's matching `*-on-loud` semantic role and has contrast-tested standalone light/dark
2642
+ fallbacks.
2284
2643
  - Importing `confirm` alone is enough to register `<lyra-dialog>` — `confirm.ts` imports
2285
2644
  `./dialog.js` for its side effect, so a consumer doesn't need a separate import for the dialog
2286
2645
  element.
@@ -2296,6 +2655,14 @@ child, simply never produces a tab. Implements the WAI-ARIA APG tabs pattern wit
2296
2655
  activation: Left/Right (swapped under RTL) move focus *and* selection together, Home/End jump to the
2297
2656
  first/last enabled tab, and a roving `tabindex` follows whichever tab is currently selected.
2298
2657
 
2658
+ A tab button's *visible* content can carry a leading icon without ever changing its *accessible
2659
+ name* (always exactly `label`'s text): give a tab an extra direct-child sibling of `<lyra-tabs>`
2660
+ carrying `slot="<id>-icon"` (that sibling's own content — inline SVG, emoji span, a custom icon
2661
+ element, anything — is entirely up to the consumer). It renders ahead of the label inside that tab's
2662
+ button, wrapped in an `aria-hidden="true"` `[part="tab-icon"]` so it's excluded from accessible-name
2663
+ computation regardless of content. A tab with no matching `<id>-icon` sibling renders no icon
2664
+ wrapper at all, so existing text-only tabs are unaffected.
2665
+
2299
2666
  **Properties:**
2300
2667
  - `active: string = ''` (reflected) — the active tab's `slot`/id; falls back to the first enabled
2301
2668
  tab whenever the current value doesn't resolve to one (including on every children/attribute
@@ -2306,13 +2673,15 @@ click or keyboard. Not fired when `active` self-corrects to a valid tab (initial
2306
2673
  disappearing/becoming disabled underneath the current selection).
2307
2674
 
2308
2675
  **Slots:** default — direct children with `slot="<id>" label="<text>"` (and optionally `disabled`);
2309
- one becomes each tab's panel.
2676
+ one becomes each tab's panel. `<id>-icon` — optional sibling direct child supplying a tab's leading
2677
+ icon content; excluded from the tab button's accessible name.
2310
2678
 
2311
2679
  **CSS parts:** `base` (root wrapper around the tablist and panels), `tablist` (the `role="tablist"`
2312
- row of tab buttons), `tab` (a single tab button), `panel` (a single `role="tabpanel"` wrapper, one
2313
- per tab, hidden unless active)
2680
+ row of tab buttons), `tab` (a single tab button), `tab-icon` (the optional leading-icon wrapper
2681
+ inside a tab button; only rendered when that tab has a matching `<id>-icon` sibling), `panel` (a
2682
+ single `role="tabpanel"` wrapper, one per tab, hidden unless active)
2314
2683
 
2315
- **Themeable custom properties:** shared tokens only — `--lyra-space-s/-m`,
2684
+ **Themeable custom properties:** shared tokens only — `--lyra-space-xs/-s/-m`,
2316
2685
  `--lyra-color-border/-text-quiet/-text/-brand`, `--lyra-transition-fast`, `--lyra-radius`,
2317
2686
  `--lyra-focus-ring-width/-color/-offset`, `--lyra-opacity-disabled`.
2318
2687
 
@@ -2371,8 +2740,8 @@ showing the checkmark/indeterminate dash), `checkmark` (the checkmark or indeter
2371
2740
  `label` (wrapper around the default slot)
2372
2741
 
2373
2742
  **Themeable custom properties:** shared tokens only — `--lyra-space-s`, `--lyra-icon-button-size`,
2374
- `--lyra-color-border/-surface/-on-brand/-brand/-text`, `--lyra-radius`, `--lyra-transition-fast`,
2375
- `--lyra-focus-ring-width/-color/-offset`, `--lyra-opacity-disabled`.
2743
+ `--lyra-color-border/-surface/-on-brand/-brand/-text/-danger`, `--lyra-radius`,
2744
+ `--lyra-transition-fast`, `--lyra-focus-ring-width/-color/-offset`, `--lyra-opacity-disabled`.
2376
2745
 
2377
2746
  **Optional peer deps:** none.
2378
2747
 
@@ -2389,6 +2758,11 @@ Form-associated via a directly-attached `ElementInternals` (not the shared `Form
2389
2758
  whose `value` accessor assumes a plain string default flow) with its own hand-rolled
2390
2759
  `updateValidity()` — same shape as `<lyra-combobox>`'s and `<lyra-switch>`'s direct-`ElementInternals`
2391
2760
  handling.
2761
+ Session-history/autofill restoration uses four explicit state tokens: `checked`, `unchecked`,
2762
+ `checked/indeterminate`, and `unchecked/indeterminate`. This preserves both public booleans while
2763
+ keeping an unchecked control distinguishable from a checked control whose submitted value is an
2764
+ empty string. Restoration updates state, form data, and validity synchronously without firing
2765
+ `lyra-change`.
2392
2766
 
2393
2767
  **Known gotchas:**
2394
2768
  - `formResetCallback()` restores `checked` to whatever the declarative `checked` attribute parsed to
@@ -2426,10 +2800,11 @@ the control still has an accessible name.
2426
2800
  background), `thumb` (the circular knob that slides across the track), `label` (wrapper around the
2427
2801
  default slot)
2428
2802
 
2429
- **Themeable custom properties:** `--track-inline-size` (default `2.25rem`), `--track-block-size`
2430
- (default `1.25rem`), `--thumb-offset` (default `2px`) — component-local geometry knobs, set on
2431
- `:host`, since a fully-rounded pill/thumb needs a radius well past the shared `--lyra-radius`
2432
- default — plus shared tokens `--lyra-space-s`, `--lyra-color-border/-brand/-surface/-text`,
2803
+ **Themeable custom properties:** `--lyra-switch-track-inline-size` (default `2.25rem`),
2804
+ `--lyra-switch-track-block-size` (default `1.25rem`), `--lyra-switch-thumb-offset` (default
2805
+ `2px`) — component-local geometry knobs, set on `:host`, since a fully-rounded pill/thumb needs
2806
+ a radius well past the shared `--lyra-radius` default — plus shared tokens
2807
+ `--lyra-space-s`, `--lyra-color-border/-brand/-surface/-text`,
2433
2808
  `--lyra-transition-fast`, `--lyra-focus-ring-width/-color/-offset`, `--lyra-opacity-disabled`.
2434
2809
 
2435
2810
  **Optional peer deps:** none.
@@ -2447,6 +2822,8 @@ Form-associated the same way as `<lyra-checkbox>`: a directly-attached `ElementI
2447
2822
  hand-rolled `updateValidity()`, not the shared `FormAssociated` mixin. The thumb animates the
2448
2823
  logical `inset-inline-start` property (not a physical `transform: translateX()`), so the slide
2449
2824
  direction mirrors correctly under `dir="rtl"`.
2825
+ Session-history/autofill restoration uses the same explicit `checked`/`unchecked` state tokens as
2826
+ checkbox and does not emit `lyra-change`.
2450
2827
 
2451
2828
  **Known gotchas:**
2452
2829
  - `formResetCallback()` restores `checked` to the value captured from the declarative `checked`
@@ -2463,7 +2840,9 @@ A collapsible, copyable tree view for an arbitrary JSON-serializable value (obje
2463
2840
  number, boolean, null, or `undefined`). Serves as a fallback renderer wherever a raw payload needs
2464
2841
  inspecting without a bespoke view. Expand/collapse state is keyed by structural path (not object
2465
2842
  identity), so it survives a `data` reassignment that keeps the same shape — e.g. a streaming result
2466
- being patched in place.
2843
+ being patched in place. A container value that self-references (directly or through a longer cycle)
2844
+ renders as a leaf `Circular reference` marker (`data-type="circular"`) instead of recursing — no
2845
+ stack overflow on cyclic `data`.
2467
2846
 
2468
2847
  **Properties:**
2469
2848
  - `data: unknown` (attribute `false` — property-only, not settable via an HTML attribute)
@@ -2485,12 +2864,14 @@ context, older browser, a stubbed-out test environment), so a consumer can still
2485
2864
 
2486
2865
  **CSS parts:** `base` (root scroll container, respects `max-height`), `toolbar` (wrapper around the
2487
2866
  top-level copy button, only rendered when `copyable`), `tree` (wrapper around the rendered node
2488
- tree), `key` (an object property key or array index label), `value` (a primitive value's text —
2489
- carries `data-type` of `string`/`number`/`boolean`/`null`/`undefined` for per-type coloring, and
2490
- `data-match` while it matches `search`), `bracket` (a `{`, `}`, `[`, or `]` delimiter), `toggle` (a
2491
- container node's expand/collapse button; hidden but present for row alignment on leaf/empty nodes),
2492
- `copy-button` (a copy-to-clipboard button the top-level one in `toolbar`, or a per-node one; only
2493
- rendered when `copyable`)
2867
+ tree), `key` (an object property key or array index label, `data-match` while it matches `search`),
2868
+ `value` (a primitive value's text — carries `data-type` of
2869
+ `string`/`number`/`boolean`/`null`/`undefined`/`circular` for per-type coloring, and `data-match`
2870
+ while it matches `search`), `bracket` (a `{`, `}`, `[`, or `]` delimiter), `toggle` (a container
2871
+ node's expand/collapse button; hidden but present for row alignment on leaf/empty nodes),
2872
+ `copy-button` (a copy-to-clipboard button — the top-level one in `toolbar` (aria-label "Copy JSON to
2873
+ clipboard") or a per-node one (aria-label `Copy ${key/type}`, e.g. "Copy age"); only rendered when
2874
+ `copyable`)
2494
2875
 
2495
2876
  **Themeable custom properties:** `--lyra-json-viewer-max-height` (default `none` — grows with content
2496
2877
  until `max-height` is set), `--lyra-json-viewer-font` (default `ui-monospace, SFMono-Regular, Menlo,
@@ -2582,7 +2963,7 @@ window and flushes immediately.
2582
2963
 
2583
2964
  **Slots:** none.
2584
2965
 
2585
- **CSS parts:** `region` — the visually-hidden element carrying `role`/`aria-live`.
2966
+ **CSS parts:** `region` — the visually-hidden element carrying `role`/`aria-live`/`aria-atomic="true"`.
2586
2967
 
2587
2968
  **Themeable custom properties:** none component-specific — the region is hidden via the shared
2588
2969
  `.sr-only` helper class (`internal/a11y.ts`), not tokenized CSS.
@@ -2607,14 +2988,16 @@ A parent Lit component would instead hold the reference via `@query('lyra-live-r
2607
2988
  - Re-announcing text identical to what was last written is special-cased: screen readers announce a
2608
2989
  live region only on text-content *change*, so the component clears `textContent` first and
2609
2990
  re-sets it on the next animation frame (not the same tick, which can coalesce into nothing ever
2610
- appearing to change) to give assistive tech a real empty-to-populated transition to observe.
2991
+ appearing to change) to give assistive tech a real empty-to-populated transition to observe. The
2992
+ frame is scheduled and canceled through the region's own document window, including after the
2993
+ element is adopted into an iframe.
2611
2994
  - The region's DOM is tracked outside Lit's own template bindings (`write()` mutates
2612
2995
  `regionEl.textContent` directly) — an `announce()`/flush landing before `firstUpdated()` has run
2613
2996
  (e.g. a consumer creates, appends, and calls `announce()` synchronously) is buffered and applied
2614
2997
  on the next `firstUpdated()` rather than dropped.
2615
- - `disconnectedCallback()` cancels any pending (unflushed) announcement and any in-flight re-announce
2616
- animation-frame callback — an element removed mid-throttle-window silently drops whatever was
2617
- queued.
2998
+ - `disconnectedCallback()` cancels any pending (unflushed) announcement, any before-first-render
2999
+ buffered write, and any in-flight re-announce animation-frame callback — an element removed before
3000
+ a deferred write lands silently drops it, including across a later reconnect.
2618
3001
  - Changing `throttle-ms` updates the live `Announcer`'s window immediately, but a flush already
2619
3002
  scheduled under the old window keeps the deadline it was scheduled with.
2620
3003
 
@@ -2626,23 +3009,36 @@ Sanitized Markdown-to-HTML rendering (GFM tables, fenced code blocks, links, blo
2626
3009
  two optional peer dependencies — `marked` (parsing) and `dompurify` (sanitizing) — both lazy-loaded
2627
3010
  independently via `markdown-loader.ts`'s `loadMarkdownDeps()` on first connect, cached per page the
2628
3011
  same way `chart-loader.ts`/`map-loader.ts` cache their load promise so every `<lyra-markdown>`
2629
- instance on a page shares one load. `heading`/`code`/`blockquote`/`table`/`link` tokens are rendered
2630
- through a `marked` renderer override that injects `part="..."` attributes directly into the produced
2631
- HTML in a single pass (no second DOM walk after insertion).
3012
+ instance on a page shares one load. `heading`/`code`/`blockquote`/`table`/`link`/`image` tokens are
3013
+ rendered through a `marked` renderer override that injects `part="..."` attributes directly into the
3014
+ produced HTML in a single pass (no second DOM walk after insertion).
2632
3015
 
2633
3016
  **Properties:**
2634
3017
  - `content: string = ''` — the Markdown source to render
2635
3018
  - `sanitize: boolean = true` — sanitize `marked`'s HTML output with DOMPurify before rendering
2636
3019
  - `gfm: boolean = true` — GitHub-flavored Markdown (tables, strikethrough, autolinks, task lists)
2637
- - `linkTarget: string = '_blank'` (attribute `link-target`) — `target` applied to every rendered
2638
- `<a>`; `rel="noopener noreferrer"` is always added alongside it regardless of this value
3020
+ - `linkTarget: string | null = '_blank'` (attribute `link-target`) — `target` applied to every
3021
+ rendered `<a>`, with `rel="noopener noreferrer"` always added alongside it whenever a `target` is
3022
+ emitted. `'_blank'` (the default) preserves the original output; a falsy value (`null`, or the
3023
+ empty string via `link-target=""`) omits `target`/`rel` entirely instead of always defaulting to
3024
+ `_blank`, so rendered links open in the same tab
2639
3025
  - `internalLinkPrefix: string = ''` (attribute `internal-link-prefix`) — when set, a rendered link
2640
3026
  whose `href` *attribute* (not the browser-resolved `.href` property) starts with this prefix is
2641
3027
  intercepted on click and reported via `lyra-link-click` instead of navigating; empty (the default)
2642
3028
  means every link is treated as external
2643
- - `streaming: boolean = false` (reflected) — forward-compatible hint for a future streaming renderer
2644
- expected to build on this component (coalescing partial tokens as they arrive); setting it has no
2645
- rendering effect yet, it only lets a consumer already target `lyra-markdown[streaming]` in CSS
3029
+ - `headingOffset: number = 0` (attribute `heading-offset`) — added to every rendered heading's
3030
+ source `token.depth` before emitting `<h${depth}>` (e.g. `heading-offset="2"` renders a source `#`
3031
+ as `<h3>`); clamped to `[1, 6]` so a source `######` with a positive offset stays at `<h6>` rather
3032
+ than overflowing past the HTML heading levels. `0` (the default) preserves the original
3033
+ `<h${token.depth}>` output
3034
+ - `eagerLoad: boolean = false` (attribute `eager-load`) — when `true`, `connectedCallback()` skips
3035
+ awaiting the async `loadMarkdownDeps()` import and renders synchronously if the shared
3036
+ `marked`/`dompurify` module cache is *already* warm (e.g. an earlier `<lyra-markdown>` instance on
3037
+ the page already finished loading); falls back to the normal async path (with its brief
3038
+ plain-text-fallback first paint) when the cache isn't warm yet. `false` (the default) is
3039
+ byte-identical to always taking the async path
3040
+ - `streaming: boolean = false` (reflected) — reserved hint for a host-provided streaming renderer;
3041
+ setting it has no rendering effect, but lets a consumer target `lyra-markdown[streaming]` in CSS
2646
3042
 
2647
3043
  **Events:** `lyra-link-click` (`detail: { href: string, internal: true }`, fired — with the click
2648
3044
  prevented — when a rendered link's `href` starts with `internal-link-prefix`; ordinary external
@@ -2651,9 +3047,12 @@ fired whenever rendering falls back to plain text — see the fallback matrix be
2651
3047
 
2652
3048
  **Slots:** none — content comes from the `content` property, not light-DOM children.
2653
3049
 
2654
- **CSS parts:** `content` (the wrapper around the rendered or plain-text-fallback output), `heading`
2655
- (every rendered `<h1>`–`<h6>`), `code-block` (every rendered fenced/indented `<pre>`), `link` (every
2656
- rendered `<a>`), `table` (every rendered `<table>`), `blockquote` (every rendered `<blockquote>`)
3050
+ **CSS parts:** `content` (the wrapper around the rendered or plain-text-fallback output; carries
3051
+ `data-fallback` while showing the plain-text fallback still-loading peers or a failed render —
3052
+ so a consumer can target `lyra-markdown [part='content'][data-fallback]` to style it distinctly),
3053
+ `heading` (every rendered `<h1>`–`<h6>`, shifted by `heading-offset`), `code-block` (every rendered
3054
+ fenced/indented `<pre>`), `link` (every rendered `<a>`), `table` (every rendered `<table>`),
3055
+ `blockquote` (every rendered `<blockquote>`), `img` (every rendered `<img>`)
2657
3056
 
2658
3057
  **Themeable custom properties:** `--lyra-markdown-font-mono` (default `ui-monospace, SFMono-Regular,
2659
3058
  Menlo, Consolas, monospace` — the code/code-block font; component-specific since no shared
@@ -2718,9 +3117,10 @@ a `<lyra-markdown>`, a custom template, anything) and this component only suppli
2718
3117
  chrome: alignment/coloring by `role`, an avatar/badges header row, an optional collapse toggle, an
2719
3118
  attachments strip, and a status-aware footer (a live-updating status dot + text, the formatted
2720
3119
  `timestamp`, a built-in retry affordance for `status="failed"`, and an `actions` slot for everything
2721
- else). No built-in copy button is rendered — slot a copy control into `actions` and fire `lyra-copy`
2722
- from it if you want one (matching `<lyra-json-viewer>`'s copy-affordance event name for anything
2723
- listening at the conversation-surface level).
3120
+ else). No built-in copy button is rendered — slot a copy control into `actions` and fire
3121
+ `lyra-copy` (`detail: { text: string }`) from it if you want one (matching `<lyra-json-viewer>`'s
3122
+ and `<lyra-code-block>`'s copy-affordance event name/shape, for anything listening at the
3123
+ conversation-surface level).
2724
3124
 
2725
3125
  **Properties:**
2726
3126
  - `role: ChatMessageRole = 'assistant'` (`'user' | 'assistant' | 'system'`) — reflects to
@@ -2885,7 +3285,8 @@ a compact call summary is useful, with or without a detail surface behind it.
2885
3285
  - `category: string = ''` — optional grouping label, e.g. `research`
2886
3286
  - `status: 'pending'|'running'|'success'|'error'|'denied' = 'pending'` (reflected) — drives the
2887
3287
  glyph, accent color, and `status-text`; same status vocabulary as `<lyra-tool-result-dialog>` so a
2888
- call's chip and its detail dialog always agree
3288
+ call's chip and its detail dialog always agree; unknown runtime values render the pending icon,
3289
+ text, and accessible label instead of failing the update
2889
3290
  - `summary: string = ''` — short human-readable status text, e.g. `Searching web…`
2890
3291
  - `durationMs?: number` (attribute `duration-ms`) — how long the call took, in milliseconds; the
2891
3292
  `duration` part is omitted entirely when unset
@@ -2956,8 +3357,9 @@ sub-1000ms `"820ms"`, else trimmed to at most one decimal of seconds (`"1.5s"`,
2956
3357
  ## `lyra-tool-result-view`
2957
3358
 
2958
3359
  Renders a tool call's result via whichever custom renderer a host app has registered for it,
2959
- falling back to `<lyra-json-viewer>` whenever no renderer matches, a renderer's optional `load()`
2960
- rejects, or its `render()` throws. First-party invention (no Web Awesome equivalent). This component
3360
+ falling back to `<lyra-json-viewer>` whenever no renderer matches, a candidate renderer's
3361
+ `matches()` predicate throws during dispatch, a renderer's optional `load()` rejects, or its
3362
+ `render()` throws. First-party invention (no Web Awesome equivalent). This component
2961
3363
  owns none of the actual visual weight of a populated tool result — that's entirely whatever the
2962
3364
  registered renderer returns; `<lyra-tool-result-view>` is just the dispatch + fallback + loading-state
2963
3365
  shell around it.
@@ -2972,14 +3374,14 @@ shell around it.
2972
3374
  `<lyra-json-viewer>` fallback)
2973
3375
  - `args: unknown` (property only, no attribute) — the tool call's original arguments, if available,
2974
3376
  handed to the matched renderer's `render()` alongside `result`
2975
- - `fallback: string = 'json'` (reflected) — forward-compatible fallback-kind selector; only `"json"`
2976
- (an unconditional `<lyra-json-viewer>`) is implemented today, but the property/attribute is still
2977
- accepted so markup that already sets `fallback="…"` for a future value doesn't need to change again
2978
- once more fallback kinds land
3377
+ - `fallback: string = 'json'` (reflected) — fallback-kind selector; only `"json"` (an unconditional
3378
+ `<lyra-json-viewer>`) is currently implemented, but other values are accepted and use the same
3379
+ JSON fallback
2979
3380
 
2980
3381
  **Events:** `lyra-render-error` (`detail: { toolName: string; error: unknown }`) — fired immediately
2981
- before falling back to `<lyra-json-viewer>`, whether because no renderer matched, a renderer's
2982
- `load()` rejected, or its `render()` threw.
3382
+ before falling back to `<lyra-json-viewer>`, whether because no renderer matched, a candidate
3383
+ renderer's `matches()` predicate threw during dispatch, a renderer's `load()` rejected, or its
3384
+ `render()` threw.
2983
3385
 
2984
3386
  **Slots:** none.
2985
3387
 
@@ -3097,10 +3499,10 @@ registerToolRenderer('web_search', {
3097
3499
  A full tool-call detail overlay: a status/duration header plus a `body` slot where a consumer
3098
3500
  typically places a `<lyra-tabs>` with Input/Preview/JSON/Raw panels. First-party invention (no Web
3099
3501
  Awesome equivalent). This component knows nothing about what's inside that slot — it only supplies
3100
- the modal chrome around it. It is its own standalone overlay implementation (`role="dialog"`,
3101
- focus-trapped, Escape/backdrop-dismissible, scroll-locking) rather than nesting a `<lyra-dialog>` in
3102
- its shadow template, so that slot-forwarding doesn't put a forwarding `<slot>` where a slotted
3103
- `<lyra-tabs>`'s own light-DOM child scan expects real projected content.
3502
+ the modal chrome around it. It keeps its own shadow template rather than nesting a `<lyra-dialog>`,
3503
+ so slot-forwarding does not put a forwarding `<slot>` where a slotted `<lyra-tabs>`'s own light-DOM
3504
+ child scan expects real projected content, while its modal behavior participates in the shared
3505
+ overlay stack.
3104
3506
 
3105
3507
  **Properties:**
3106
3508
  - `open: boolean = false` (reflected) — whether the dialog is open; set this (or call `close()`) —
@@ -3151,8 +3553,8 @@ tokens `--lyra-color-surface/-border/-text-quiet/-brand/-brand-quiet/-success/-s
3151
3553
  @lyra-maximize-change=${(e) => console.log('maximized:', e.detail)}
3152
3554
  >
3153
3555
  <lyra-tabs slot="body">
3154
- <lyra-tab-panel label="Preview">…</lyra-tab-panel>
3155
- <lyra-tab-panel label="JSON"><lyra-json-viewer .data=${result}></lyra-json-viewer></lyra-tab-panel>
3556
+ <div slot="preview" label="Preview">…</div>
3557
+ <div slot="json" label="JSON"><lyra-json-viewer .data=${result}></lyra-json-viewer></div>
3156
3558
  </lyra-tabs>
3157
3559
  <button slot="footer">Rerun</button>
3158
3560
  </lyra-tool-result-dialog>
@@ -3170,14 +3572,13 @@ open/close lifecycle — unlike `<lyra-widget>`'s fullscreen mode there's no sep
3170
3572
  state, so no additional scroll-lock/focus-trap bookkeeping is needed for that transition alone.
3171
3573
 
3172
3574
  **Known gotchas:**
3173
- - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) restores the
3174
- scroll lock and the document `keydown` listener if `open` was still `true` across the move —
3175
- `disconnectedCallback`/`connectedCallback` fire back-to-back with no intervening update, so
3176
- `willUpdate` never reruns to notice `open` didn't change
3177
- - this component deliberately does **not** compose `<lyra-dialog>` internally, even though the two
3178
- share nearly identical overlay mechanics (focus trap, scroll lock, Escape/backdrop dismiss) the
3179
- duplication is intentional so a slotted `<lyra-tabs>` (or any other light-DOM-scanning child) sees
3180
- real projected content rather than a forwarding `<slot>` from a nested dialog
3575
+ - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) resumes its
3576
+ shared overlay registration and re-acquires the scroll lock if `open` was still `true` across the
3577
+ move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no intervening update, so
3578
+ `willUpdate` never reruns to notice `open` did not change
3579
+ - this component deliberately does **not** compose `<lyra-dialog>` internally; it keeps its own
3580
+ panel template so a slotted `<lyra-tabs>` (or any other light-DOM-scanning child) sees real
3581
+ projected content rather than a forwarding `<slot>`, while still sharing the overlay stack
3181
3582
  - `close()` is a no-op when `open` is already `false` — calling it twice in a row only fires
3182
3583
  `lyra-dialog-close` once
3183
3584
  - the `maximize`/`close` buttons are always the first elements in the Tab order while open,
@@ -3194,6 +3595,11 @@ built-in send/stop button. **Form-associated** via the shared `FormAssociated` m
3194
3595
  `checkValidity()`/`reportValidity()`, so it participates in native `<form>` submission/validation/
3195
3596
  reset like any other text control.
3196
3597
 
3598
+ The inner textarea mirrors `required` through native `required`/`aria-required`. Its
3599
+ `aria-invalid` remains false until the textarea has been blurred, then follows the host's
3600
+ `ElementInternals` validity as `value` or `required` changes. A native validation attempt also
3601
+ reveals the invalid state, and `form.reset()` clears the touched presentation.
3602
+
3197
3603
  **Properties (own):**
3198
3604
  - `placeholder: string = ''`
3199
3605
  - `minRows: number = 1` (attribute `min-rows`) — floored to `1` at render time
@@ -3250,7 +3656,11 @@ entirely when it has assigned content)
3250
3656
  Auto-resize (`resizeTextarea()`) reads the textarea's own *computed* line-height/padding/border at
3251
3657
  call time rather than assuming a fixed px-per-row constant, so it stays correct under a consumer's
3252
3658
  own font-size/line-height overrides; it grows between `min-rows` and `max-rows`, then switches to
3253
- internal scrolling (`overflow-y: auto`) past `max-rows`. Enter-to-send only fires while
3659
+ internal scrolling (`overflow-y: auto`) past `max-rows`. A `ResizeObserver` on the textarea itself
3660
+ also re-runs this fit (one animation frame later, to avoid a `ResizeObserver`-loop console error)
3661
+ whenever the textarea's own *width* changes — a sidebar collapsing, a responsive breakpoint, a
3662
+ window resize — even though `value`/`min-rows`/`max-rows` never did, since the same text now wraps
3663
+ across a different number of lines. Enter-to-send only fires while
3254
3664
  `submit-on-enter` is `true` (the default): plain Enter submits and prevents the default newline;
3255
3665
  Shift+Enter always inserts a newline regardless of `submit-on-enter`; an IME composition step
3256
3666
  (checked via `isComposing`, with `keyCode === 229` as a defense-in-depth fallback for browsers that
@@ -3302,6 +3712,15 @@ wins when both are present.
3302
3712
  - `progress: number = 0` — upload completion, 0-100; only meaningful while `status="uploading"`, a
3303
3713
  value of `0` or `NaN` falls back to the indeterminate spinner
3304
3714
  - `removable: boolean = true` (reflected) — shows the remove (×) button
3715
+ - `removeLabel: string = 'Remove'` (attribute `remove-label`) — verb used in the remove button's
3716
+ `aria-label`, interpolated as `` `${removeLabel} ${displayName}` ``; override for i18n/locale
3717
+ - `retryLabel: string = 'Retry'` (attribute `retry-label`) — verb used in the retry button's
3718
+ `aria-label`, interpolated as `` `${retryLabel} ${displayName}` ``; override for i18n/locale
3719
+ - `uploadingLabel: string = 'Uploading'` (attribute `uploading-label`) — verb used in the visible
3720
+ uploading status text, rendered as `` `${uploadingLabel} ${percent}%` `` once progress is a
3721
+ meaningful number, else `` `${uploadingLabel}…` ``; override for i18n/locale
3722
+ - `uploadFailedLabel: string = 'Upload failed'` (attribute `upload-failed-label`) — visible status
3723
+ text shown for `status="error"`; override for i18n/locale
3305
3724
 
3306
3725
  The component identifies *which* attachment a `lyra-remove`/`lyra-retry` event is about via the
3307
3726
  platform's own `id` attribute/property rather than a second, differently-named prop. Set `id="..."`
@@ -3384,7 +3803,9 @@ reads as stalled.
3384
3803
  - `stallThresholdMs: number = 10000` (attribute `stall-threshold-ms`) — how long `phase` may stay
3385
3804
  `'streaming'` with no `recordActivity()` call before the component auto-transitions to
3386
3805
  `'stalled'`. A non-finite or `<= 0` value disables the stall timer entirely (arming becomes a
3387
- no-op, so the phase will never auto-stall).
3806
+ no-op, so the phase will never auto-stall). Changing this value while already `'streaming'`
3807
+ re-arms the timer immediately against the new value, rather than waiting for the next
3808
+ `recordActivity()` call or phase change.
3388
3809
 
3389
3810
  **Methods:**
3390
3811
  - `recordActivity(): void` — call on every semantic (non-heartbeat) frame received while
@@ -3445,8 +3866,13 @@ the instant `phase` becomes anything else, including a host-driven reassignment
3445
3866
  on. Phase transitions into/out of `'stalled'` are announced through an internal
3446
3867
  `<lyra-live-region>` rather than a hand-rolled `aria-live` region: entering `'stalled'` announces
3447
3868
  "Connection stalled." with `mode="assertive"` (a stall can need the user's attention before they
3448
- give up and navigate away); recovering announces "Connection restored." with `mode="polite"` (good
3449
- news doesn't need to interrupt). Calling `recordActivity()` itself never announces anything, no
3869
+ give up and navigate away). Leaving `'stalled'` always announces with `mode="polite"` (good news
3870
+ doesn't need to interrupt), but the *wording* depends on the destination phase: "Connection
3871
+ restored." only when leaving `'stalled'` for `'streaming'` (a genuine recovery, typically via
3872
+ `recordActivity()`); a neutral "No longer stalled." when the destination is `'idle'`/`'connecting'`
3873
+ instead (the host gave up on the stream, which is not the same thing as it recovering — a
3874
+ screen-reader user must not be told the opposite of what a sighted user sees). Calling
3875
+ `recordActivity()` itself never announces anything, no
3450
3876
  matter how often the host calls it — only the phase *transition* announces, exactly once. The
3451
3877
  decorative indicator dot is `aria-hidden` (a color/motion cue only) and only pulses while
3452
3878
  `phase="streaming"`; `'stalled'` is styled as a warning tone, not danger, since a stall is usually
@@ -3466,6 +3892,10 @@ instead. The pulse animation is suppressed under `prefers-reduced-motion: reduce
3466
3892
  `phase`.
3467
3893
  - `phase` remains directly settable at all times; assigning `'stalled'` yourself fires `lyra-stall`
3468
3894
  and the assertive announcement exactly as if the timer had fired.
3895
+ - reconnecting the element while still `phase === 'streaming'` (e.g. a drag-and-drop reparent that
3896
+ keeps the same instance) automatically re-arms the stall timer in `connectedCallback` —
3897
+ `disconnectedCallback` always disarms it, and disconnect/reconnect fire back-to-back with no
3898
+ `updated()` cycle in between, so nothing else would otherwise notice.
3469
3899
  - The `actions` slot's wrapper is always present in the DOM and toggled with the `hidden` attribute
3470
3900
  based on slotted content, not gated by `phase` — content placed there is visible regardless of
3471
3901
  the current phase.
@@ -3494,13 +3924,14 @@ invention (no Web Awesome equivalent).
3494
3924
  which is only a safe identity while `items` never reorders/inserts/removes — provide this whenever
3495
3925
  possible, or scroll position and per-row DOM state (e.g. an `<audio>` element's playback position)
3496
3926
  can attach to the wrong row across a mutation.
3497
- - `groups?: VirtualListGroup[]` (attribute: false) — reserved for a future sticky-header sectioning
3498
- feature; accepted but **not currently read or rendered** — every row still renders flat, in `items`
3499
- order.
3927
+ - `groups?: VirtualListGroup[]` (attribute: false) — accepted for compatibility but **not currently
3928
+ read or rendered** — every row still renders flat, in `items` order.
3500
3929
  - `rowHeight: string = 'auto'` (attribute `row-height`) — `'auto'` measures each row's real height via
3501
3930
  `ResizeObserver`; a numeric string (e.g. `"56"`) fixes every row to that many pixels. Anything else
3502
3931
  (non-numeric, zero, negative, non-finite) silently falls back to `'auto'` rather than throwing.
3503
- - `overscan: number = 6` — extra rows rendered beyond the visible viewport on each side.
3932
+ - `overscan: number = 6` — extra rows rendered beyond the visible viewport on each side; finite
3933
+ values are floored and clamped to 0–100, while non-finite values use the default 6, so an invalid
3934
+ runtime value cannot disable windowing and render the entire collection.
3504
3935
  - `activeId: string = ''` (attribute `active-id`) — when set and it matches a row's `keyFunction`
3505
3936
  result (compared as a string), that row is smoothly scrolled into view whenever this changes, and
3506
3937
  rendered with `aria-current="true"`.
@@ -3510,10 +3941,11 @@ invention (no Web Awesome equivalent).
3510
3941
  fires `lyra-load-more` (gated by `loading`).
3511
3942
 
3512
3943
  **Exported types:** `VirtualListRange { start: number; end: number }` (the `lyra-visible-range-changed`
3513
- detail shape); `VirtualListGroup { key: string | number; label?: string; startIndex: number }` — a
3514
- placeholder shape for the not-yet-implemented grouping feature above. No exported helper functions ship
3515
- in this pass (a paired `groupByRecency()` helper referenced in the source's own class doc doesn't exist
3516
- yet).
3944
+ detail shape); `VirtualListGroup { key: string | number; label?: string; startIndex: number }` — an
3945
+ accepted grouping shape that the list does not currently render.
3946
+ The package root also exports `groupByRecency(items, options?)`, a DOM-free helper that returns
3947
+ non-empty Today/Yesterday/Previous 7 Days/Older buckets, preserves input order within each bucket,
3948
+ and accepts a timestamp extractor, reference date, and label overrides.
3517
3949
 
3518
3950
  **Events:** `lyra-load-more` (no detail — fired once per approach to the bottom of the list while
3519
3951
  `has-more` is true and `loading` is false; does not refire on every scroll tick while still near the
@@ -3555,13 +3987,17 @@ so it isn't clipped by the container's own `overflow: auto`).
3555
3987
  ```
3556
3988
 
3557
3989
  Every row — in both `row-height` modes — is positioned by a `transform: translateY(offset)` computed
3558
- from a single cumulative offsets array rebuilt on every update, rather than by page flow; this is what
3559
- lets a small DOM window exist while the scrollbar still reflects the true total content height. In
3560
- `row-height="auto"` mode, unmeasured rows contribute a fixed estimate to that array until their real
3561
- `ResizeObserver`-reported height lands, so only rows after a newly-measured one shift on each
3562
- measurement rather than the whole list reflowing. The offsets rebuild is an `O(n)` loop appropriate for
3563
- hundreds to a few thousand rows (a scrollable history sidebar); it is not the right approach for a
3564
- hundred-thousand-row list without further work.
3990
+ from a single cumulative offsets array, rather than by page flow; this is what lets a small DOM window
3991
+ exist while the scrollbar still reflects the true total content height. That array is rebuilt only
3992
+ when `items`, `row-height`, or `keyFunction` change, or a row's measured height changes **not** on
3993
+ every update, so a pure scroll-position tick (potentially every `rAF` while scrolling) only re-runs the
3994
+ cheap range/visibility math, never the `O(n)` offsets rebuild (which, in `row-height="auto"` mode, also
3995
+ means a `keyFunction` call per item). In `row-height="auto"` mode, unmeasured rows contribute a fixed
3996
+ estimate to that array until their real `ResizeObserver`-reported height lands, so only rows after a
3997
+ newly-measured one shift on each measurement rather than the whole list reflowing. The offsets rebuild,
3998
+ when it does run, is an `O(n)` loop appropriate for hundreds to a few thousand rows (a scrollable
3999
+ history sidebar); it is not the right approach for a hundred-thousand-row list without further work
4000
+ (e.g. a Fenwick/segment tree for `O(log n)` offset queries+updates).
3565
4001
 
3566
4002
  **Known gotchas:**
3567
4003
  - `items`, `renderItem`, `keyFunction`, and `groups` are all `attribute: false` — they must be set as
@@ -3600,7 +4036,9 @@ First-party invention (no Web Awesome equivalent).
3600
4036
  - `active: boolean = false` (reflected) — whether this is the currently-selected/open session; drives
3601
4037
  the brand-quiet background treatment.
3602
4038
  - `editable: boolean = true` (reflected) — whether inline-rename is available at all. When `false`, the
3603
- rename button never renders and the row can never enter its editing state.
4039
+ rename button never renders and the row can never enter its editing state; flipping it to `false`
4040
+ while a rename is already in progress cancels that edit (discards the draft, like Escape) rather
4041
+ than leaving it stranded and still committable.
3604
4042
 
3605
4043
  **Events:** `lyra-select` (no detail payload — identify the row via the platform `id` attribute on the
3606
4044
  event's `target`/`currentTarget`, the same convention `<lyra-attachment-chip>` uses; fires on a click on
@@ -3611,7 +4049,7 @@ component — not fired when the trimmed draft is empty or unchanged from the or
3611
4049
  as an implicit cancel)
3612
4050
 
3613
4051
  **Slots:** `actions` — overflow/icon-button controls rendered at the trailing edge of the row (e.g. a
3614
- future pin/delete button); only visually shown once it actually has assigned elements.
4052
+ pin/delete control); only visually shown once it actually has assigned elements.
3615
4053
 
3616
4054
  **CSS parts:** `base`, `option`, `content`, `title`, `title-input`, `rename-button`, `excerpt`,
3617
4055
  `timestamp`, `actions`
@@ -3656,6 +4094,9 @@ just visually swapping content.
3656
4094
  single-click `lyra-select`.
3657
4095
  - While renaming, `[part="option"]` has no `role`/`tabindex`/`aria-selected`/`aria-label` at all — a
3658
4096
  screen reader briefly stops announcing it as a listbox option for the duration of the edit.
4097
+ - Setting `editable = false` mid-rename silently discards the in-progress draft (no `lyra-rename`
4098
+ fires) — a consumer toggling `editable` off (e.g. in response to some other row entering rename
4099
+ mode) should not expect the previous edit to be committed first.
3659
4100
 
3660
4101
  ---
3661
4102
 
@@ -3668,6 +4109,8 @@ shared `FormAssociated` mixin — same reasoning as `lyra-combobox`/`lyra-select
3668
4109
  notes). Built on the same trigger-button/`aria-activedescendant` listbox technique `<lyra-select>` uses
3669
4110
  and the filter-as-you-type suggestion-popup technique `<lyra-combobox>` uses, without composing either
3670
4111
  element. First-party invention (no Web Awesome equivalent).
4112
+ Session-history/autofill restoration synchronously restores the model id and form entry without
4113
+ emitting `lyra-change`.
3671
4114
 
3672
4115
  **Exported types:**
3673
4116
  - `LyraModelCatalogEntry { id: string; label: string }` — one catalog row.
@@ -3681,13 +4124,19 @@ element. First-party invention (no Web Awesome equivalent).
3681
4124
  back to plain free-text entry.
3682
4125
  - `allowCustom: boolean = false` (attribute `allow-custom`, reflected) — let the user type/commit a
3683
4126
  value that isn't in `catalog`, even when `catalog` is non-empty.
4127
+ - `label: string = ''` — optional visible title above the control, mirroring `<lyra-select>`'s own
4128
+ `label` exactly: rendered via a `[part="form-control-label"]` `<label>` paired with the control's
4129
+ id, and once non-empty it takes over as the accessible-name source (an `aria-label` override is
4130
+ then only consulted as the fallback). Empty (the default) keeps the original
4131
+ `aria-label || placeholder || 'Model'` accessible-name chain untouched.
3684
4132
  - `placeholder: string = ''`
3685
- - `name: string = ''`
4133
+ - `name: string = ''` (reflected)
3686
4134
  - `disabled: boolean = false` (reflected)
3687
4135
  - `required: boolean = false` (reflected — enforced via `internals.setValidity()`)
3688
4136
  - `open: boolean = false` (reflected)
3689
4137
  - `value: string` — getter/setter (hand-rolled, not the `FormAssociated` mixin); the current model id,
3690
- `''` when nothing is selected. Writing it calls `internals.setFormValue()` synchronously.
4138
+ `''` when nothing is selected. Writing it calls `internals.setFormValue()` synchronously. A named,
4139
+ untouched model-select contributes `''` to `FormData` instead of omitting its key.
3691
4140
 
3692
4141
  **Mode switching:** `closedMode` (private) is `true` whenever `normalizedCatalog.length > 0 &&
3693
4142
  !allowCustom` — a non-empty `catalog` with `allowCustom` left `false` renders the closed dropdown
@@ -3710,8 +4159,13 @@ from a real catalog pick)
3710
4159
 
3711
4160
  **Slots:** none.
3712
4161
 
3713
- **CSS parts:** `trigger`, `combobox`, `combobox-input`, `provider-badge`, `listbox`, `option`,
3714
- `option-label`, `option-badge`
4162
+ **CSS parts:** `form-control-label` (the `<label>` element — only rendered, and only contributes to
4163
+ the accessible name, once `label` is non-empty), `trigger` (closed-dropdown mode's
4164
+ `<button role="combobox">`, also its positioning anchor), `combobox` (free-text mode's input
4165
+ container, also its positioning anchor), `combobox-input` (the free-text `<input>`),
4166
+ `provider-badge` (the optional leading `provider` label), `listbox` (the options popover, shared by
4167
+ both modes), `option`, `option-label`, `option-badge` (the "not in catalog" badge on a synthetic
4168
+ stale-value row), `expand-icon` (the dropdown chevron, present in both modes)
3715
4169
 
3716
4170
  **Themeable custom properties:** shared tokens only — `--lyra-space-xs/-s`,
3717
4171
  `--lyra-color-border/-surface/-brand/-brand-quiet/-text-quiet`, `--lyra-radius`, `--lyra-shadow`,
@@ -3757,7 +4211,10 @@ A numeric range control (e.g. an LLM "temperature" setting). **Form-associated**
3757
4211
  `FormAssociated` mixin (`name`, `value`, `disabled`, `required` all inherited). Mirrors native `<input
3758
4212
  type="range">` semantics: `value` is the string form-submitted via the mixin, `valueAsNumber` is the
3759
4213
  ergonomic numeric accessor (matching native `<input type=range>`'s IDL attribute of the same name) kept
3760
- in sync with it in both directions. First-party invention (no Web Awesome equivalent).
4214
+ in sync with it in both directions. Clicking anywhere on `[part="base"]` (not just the thumb) jumps the
4215
+ thumb to that point and continues the same gesture as a drag, matching native `<input type=range>`
4216
+ click-to-seek — the thumb is also `.focus()`ed on that click, so keyboard interaction can continue
4217
+ seamlessly right after. First-party invention (no Web Awesome equivalent).
3761
4218
 
3762
4219
  **Properties:**
3763
4220
  - `min: number = 0`
@@ -3832,10 +4289,9 @@ native range input either.
3832
4289
  ## `lyra-tool-select-dialog`
3833
4290
 
3834
4291
  A category-grouped, filterable, searchable tool-enablement dialog for picking which agent tools are
3835
- available in a conversation. Its own standalone overlay implementation (`role="dialog"`, focus-trapped,
3836
- Escape/backdrop-dismissible, scroll-locking) rather than nesting `<lyra-dialog>` in its shadow template
3837
- duplicated locally the same way `<lyra-tool-result-dialog>` does, so this component has no dependency on
3838
- the general-purpose dialog. First-party invention (no Web Awesome equivalent).
4292
+ available in a conversation. It keeps its own panel template rather than nesting `<lyra-dialog>`, so
4293
+ it has no dependency on the general-purpose dialog, while its modal behavior participates in the
4294
+ shared overlay stack. First-party invention (no Web Awesome equivalent).
3839
4295
 
3840
4296
  **Exported types:**
3841
4297
  - `ToolSelectDialogTool { id: string; name: string; description?: string; category?: string; icon?:
@@ -3889,7 +4345,7 @@ assigned elements.
3889
4345
  the backdrop scrim color; component-specific since no shared overlay token exists, mirrors
3890
4346
  `<lyra-dialog>`'s/`<lyra-tool-result-dialog>`'s identical pattern), plus shared `--lyra-space-*`,
3891
4347
  `--lyra-color-surface/-border/-text/-text-quiet/-warning`, `--lyra-radius`, `--lyra-shadow`,
3892
- `--lyra-focus-ring-width/-color/-offset`, `--lyra-opacity-disabled`, `--lyra-icon-button-size`.
4348
+ `--lyra-focus-ring-width/-color/-offset`, `--lyra-opacity-disabled`.
3893
4349
 
3894
4350
  **Optional peer deps:** none — internally renders `<lyra-checkbox>` and `<lyra-switch>`, both bundled
3895
4351
  dependencies of this package imported directly, not optional peers.
@@ -3931,7 +4387,7 @@ checkboxes for editing.
3931
4387
  into a trailing "Other" bucket that's always rendered last. A category left with zero matches after
3932
4388
  filtering is dropped entirely, not rendered as an empty heading.
3933
4389
  - Reconnecting the element while still `open` (e.g. a drag-and-drop reparent that keeps the same
3934
- instance) automatically re-acquires the scroll lock and keydown trap it dropped in
4390
+ instance) resumes its shared overlay registration and re-acquires the scroll lock dropped in
3935
4391
  `disconnectedCallback`.
3936
4392
  - The search input is the first focusable element in the panel and receives focus automatically on open.
3937
4393
 
@@ -3975,10 +4431,10 @@ popover on hover/focus. This is *not* the badge's visible content (the badge alw
3975
4431
  **Themeable custom properties:** `--lyra-citation-badge-accent` / `--lyra-citation-badge-bg` /
3976
4432
  `--lyra-citation-badge-border` (internal per-status accent variables, not typically overridden
3977
4433
  directly — set instead by the `:host([status=...])` rules), plus shared tokens
3978
- `--lyra-color-text-quiet`, `--lyra-color-success` / `-success-quiet`, `--lyra-color-warning` /
3979
- `-warning-quiet`, `--lyra-color-danger` / `-danger-quiet`, `--lyra-radius`, `--lyra-color-surface`,
3980
- `--lyra-color-border`, `--lyra-shadow`, `--lyra-space-s`/`-m`, `--lyra-transition-fast`,
3981
- `--lyra-focus-ring-*`.
4434
+ `--lyra-color-text-quiet`, `--lyra-color-text`, `--lyra-color-success` / `-success-quiet`,
4435
+ `--lyra-color-warning` / `-warning-quiet`, `--lyra-color-danger` / `-danger-quiet`, `--lyra-radius`,
4436
+ `--lyra-color-surface`, `--lyra-color-border`, `--lyra-shadow`, `--lyra-space-s`/`-m`,
4437
+ `--lyra-transition-fast`, `--lyra-focus-ring-*`.
3982
4438
 
3983
4439
  **Optional peer deps:** none.
3984
4440
 
@@ -4081,13 +4537,15 @@ while collapsed).
4081
4537
  component never navigates on its own (a controlled component, the same convention
4082
4538
  `<lyra-tool-call-chip>`'s `lyra-tool-chip-select` follows); a listener decides what "open" means.
4083
4539
 
4084
- **Slots:** `excerpt` (a short preview, always visible), `full` (the complete source text/chunk,
4085
- hidden behind the "Show more"/"Show less" toggle — when left empty, no toggle renders at all).
4540
+ **Slots:** `excerpt` (a short preview; when left empty, the `excerpt` part collapses away entirely —
4541
+ its wrapper is `hidden`, not just visually empty), `full` (the complete source text/chunk, hidden
4542
+ behind the "Show more"/"Show less" toggle — when left empty, no toggle renders at all; removing all
4543
+ `full`-slotted content while expanded auto-collapses it back).
4086
4544
 
4087
4545
  **CSS parts:** `base` (outer container), `title` (the clickable title/page heading, a `<button>`),
4088
- `excerpt` (wrapper around the `excerpt` slot), `full` (wrapper around the `full` slot, `hidden` while
4089
- collapsed), `toggle` (the "Show more"/"Show less" button — only rendered when the `full` slot has
4090
- content).
4546
+ `excerpt` (wrapper around the `excerpt` slot, `hidden` when the slot has no assigned content), `full`
4547
+ (wrapper around the `full` slot, `hidden` while collapsed), `toggle` (the "Show more"/"Show less"
4548
+ button — only rendered when the `full` slot has content).
4091
4549
 
4092
4550
  **Themeable custom properties:** shared tokens only — `--lyra-color-border`, `--lyra-color-surface`,
4093
4551
  `--lyra-color-text` / `-text-quiet`, `--lyra-color-brand`, `--lyra-radius`, `--lyra-space-xs`/`-s`,
@@ -4134,6 +4592,10 @@ time.
4134
4592
  independent of the parent list's `expanded`/`lyra-toggle` — collapsing the list doesn't reset an
4135
4593
  individual card's `fullExpanded` state, and there is no cross-talk between the two components at
4136
4594
  all beyond DOM nesting.
4595
+ - `lyra-source-card` actively strips a bare host-level `title` attribute right after Lit syncs it into
4596
+ the `title` property — otherwise the whole card would grow an unsolicited native tooltip repeating
4597
+ the title text on hover. Set `title` only as a property/attribute meant to become the rendered
4598
+ heading; don't rely on it surviving as a DOM attribute afterward.
4137
4599
 
4138
4600
  ---
4139
4601
 
@@ -4218,10 +4680,10 @@ exposed standalone so a consumer can compute or unit-test the same three-state r
4218
4680
  real browser window. `mobileMatches` wins over `iconOnlyMatches` when both are true (the viewport is
4219
4681
  narrower than both breakpoints at once).
4220
4682
 
4221
- This is its own standalone focus-trapped overlay implementation for the `'mobile'` state
4222
- (`role="dialog"` while open, Escape/backdrop-dismissible, scroll-locking) rather than nesting a
4223
- `<lyra-dialog>` in its shadow template. It's a plain `<div>` with an explicit `role="navigation"`
4224
- (swapping to `role="dialog"` while the overlay is open) rather than a literal `<nav>` tag, since a
4683
+ The mobile state keeps its own panel template rather than nesting `<lyra-dialog>`, while its modal
4684
+ behavior participates in the shared overlay stack. It is a plain `<div>` with an explicit
4685
+ `role="navigation"` (swapping to `role="dialog"` while the overlay is open) rather than a literal
4686
+ `<nav>` tag, since a
4225
4687
  `<nav>`'s implicit role can't be overridden to `role="dialog"` without an `aria-allowed-role`
4226
4688
  violation. In `'icon-only'` mode, slotted nav items lose their visible text label — give each one a
4227
4689
  real accessible name (`aria-label`, visually hidden text, or `title`) regardless, since this
@@ -4247,10 +4709,26 @@ component only lays out whatever is slotted and can't inspect or fix up a consum
4247
4709
  selector (`translateX(100%)`), not through the shared `internal/rtl.ts` JS helper used for pointer/
4248
4710
  keyboard math elsewhere in this library — a physical `transform` isn't expressible with logical
4249
4711
  properties, so this one case is handled purely in CSS.
4250
- - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) restores the
4251
- scroll lock and the Tab-trap `keydown` listener if the overlay was still active across the move —
4252
- `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in between, so
4253
- `willUpdate()` alone wouldn't otherwise notice.
4712
+ - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) resumes its
4713
+ shared overlay registration and re-acquires the scroll lock if the overlay was still active
4714
+ across the move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in
4715
+ between, so `willUpdate()` alone wouldn't otherwise notice.
4716
+
4717
+ ### `lyra-app-rail-item`
4718
+
4719
+ An explicit navigation item for `<lyra-app-rail>`. It renders an accessible link when `href` is
4720
+ set and enabled, otherwise a button; the rail can add its `icon-only` presentation state without
4721
+ removing the label from the accessibility tree.
4722
+
4723
+ **Properties:** `href: string = ''`, `target: string = ''`, and `disabled: boolean = false`
4724
+ (reflected). A disabled item remains visible but cannot be activated.
4725
+
4726
+ **Slots:** default (the visible label), `icon` (the leading icon, hidden from assistive technology
4727
+ when the item has an explicit `aria-label`).
4728
+
4729
+ **CSS parts:** `base`, `icon`, `label`.
4730
+
4731
+ **Optional peer deps:** none.
4254
4732
 
4255
4733
  ---
4256
4734
 
@@ -4270,8 +4748,14 @@ sidebar that's a permanent docked pane on a wide screen but a modal on a phone.
4270
4748
  presentation's visual treatment: `'fullscreen'` covers the whole viewport; `'bottom-sheet'` slides
4271
4749
  up from the bottom and doesn't cover the full height. Has no visual effect while the effective
4272
4750
  presentation resolves to `'inline'`.
4273
- - `label: string = ''` — accessible name for the overlay presentation's `role="dialog"`. Unused in
4274
- the inline presentation, which has no dialog semantics to name.
4751
+ - `label: string = ''` — accessible name for the overlay presentation's `role="dialog"`, used
4752
+ verbatim when set. When empty, falls back to the `header` slot's content: a heading element
4753
+ (`h1`–`h6` or `[role="heading"]`) among the slotted header content wins if present, otherwise the
4754
+ header slot's combined text content is used (mirrors `lyra-dialog`'s `detectHeading()` fallback,
4755
+ via `aria-label` rather than `aria-labelledby` since the header content is light DOM while
4756
+ `[part="panel"]` is in shadow DOM). A panel opened with neither `label` nor header content still
4757
+ renders `role="dialog"` with no accessible name. Unused in the inline presentation, which has no
4758
+ dialog semantics to name.
4275
4759
  - `mobileBreakpoint: string = '768px'` (attribute `mobile-breakpoint`) — CSS length passed to
4276
4760
  `matchMedia` as `(max-width: <this>)` to decide, in `mode="auto"`, whether the effective
4277
4761
  presentation is `'overlay'` (below/at this width) or `'inline'` (above it).
@@ -4313,16 +4797,13 @@ shared token exists), plus shared tokens (`--lyra-color-border`, `--lyra-color-s
4313
4797
 
4314
4798
  Breakpoint detection uses `matchMedia('(max-width: ' + mobileBreakpoint + ')')`, re-evaluated live
4315
4799
  while connected — resizing/rotating a device that crosses the breakpoint while `mode="auto"` (the
4316
- default) updates the effective presentation without unmounting or re-creating the slotted content:
4317
- inline and overlay presentation share the exact same shadow DOM structure (only a CSS class, and the
4318
- overlay-only `role`/`aria-modal` attributes and backdrop element, differ), so lit-html's diffing
4319
- keeps `[part="body"]` and its `<slot>` as the same DOM node across the transition scroll position
4320
- and focus inside the slotted content survive it for free. The one exception is focus: crossing the
4321
- breakpoint while already open does *not* forcibly move focus into the panel (unlike a genuine `open`
4322
- transition, which does) doing so on a resize could yank focus away from an input the user is
4323
- actively typing into inside the now-newly-modal panel. The overlay presentation duplicates
4324
- `<lyra-dialog>`'s `role="dialog"` + focus-trap + Escape/backdrop-dismiss + scroll-lock mechanics
4325
- locally rather than nesting a `<lyra-dialog>` inside this component's shadow DOM.
4800
+ default) updates the effective presentation without unmounting or re-creating the slotted content.
4801
+ Inline and overlay presentations share the same shadow DOM, so slotted content and scroll position
4802
+ survive the transition. Focus already inside the panel is preserved. If focus is outside when an
4803
+ open inline panel becomes an overlay, focus moves to the first composed focus target (falling back
4804
+ to the panel), so it cannot remain behind `aria-modal="true"`. Closing restores the element captured
4805
+ when the panel originally opened, even when that original open happened inline. The overlay
4806
+ presentation participates in the shared modal stack rather than nesting a `<lyra-dialog>`.
4326
4807
 
4327
4808
  The package root also exports the pure `resolveEffectiveMode(mode: ResponsivePanelMode,
4328
4809
  belowBreakpoint: boolean): ResponsivePanelEffectiveMode` resolver — renamed on export to
@@ -4335,17 +4816,18 @@ exposed standalone so a consumer can compute or unit-test the same resolution wi
4335
4816
  window.
4336
4817
 
4337
4818
  **Known gotchas:**
4338
- - setting `open = true`/`open = false` directly does not fire `lyra-close` and does not run the
4339
- focus-return-to-trigger logic either only calling `close()` does both; a consumer that toggles
4340
- the property directly instead of calling `close()` silently loses both behaviors.
4341
- - crossing the breakpoint while the panel is already open does not move focus into it, unlike a
4342
- genuine `open` transition see above; don't rely on a resize alone to place initial focus.
4819
+ - assigning `open` directly still does not emit `lyra-close`; use `close()` when the dismissal
4820
+ event/reason is required. While overlay chrome is active, however, the `true` `false` state
4821
+ transition restores opener focus regardless of whether it came from `close()`, a property write,
4822
+ or attribute removal.
4823
+ - crossing inline overlay while already open preserves focus that is already inside and moves
4824
+ outside focus into the panel; do not expect focus to remain on page content behind the modal.
4343
4825
  - `variant="bottom-sheet"` has no visible effect at all while the effective presentation is
4344
4826
  `'inline'` — it only changes the overlay presentation's anchoring/height.
4345
- - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) restores the
4346
- scroll lock and the Tab-trap `keydown` listener if overlay chrome was still active across the move
4347
- — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in between, so
4348
- `willUpdate()` alone wouldn't otherwise notice.
4827
+ - a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) resumes its
4828
+ shared overlay registration and re-acquires the scroll lock if overlay chrome was still active
4829
+ across the move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in
4830
+ between, so `willUpdate()` alone wouldn't otherwise notice.
4349
4831
  - the exported pure function is named `resolveEffectiveMode` in the component module but re-exported
4350
4832
  from the package root as `resolveResponsivePanelEffectiveMode` — importing the un-prefixed name
4351
4833
  from `@aceshooting/lyra-ui` will fail.
@@ -4381,10 +4863,13 @@ own listbox.
4381
4863
  also wants to wire `aria-controls`.
4382
4864
 
4383
4865
  **Methods:** `handleKeyDown(e: KeyboardEvent): boolean` — the host's own text-control `keydown`
4384
- handler calls this while the popover is open. Handles `ArrowDown`/`ArrowUp` (moves the highlight),
4385
- `Enter`/`Tab` (commits the highlighted row, no-op that returns `false` if nothing is highlighted),
4386
- and `Escape` (closes with no selection). Returns `true` when the key was intercepted
4387
- (`preventDefault()` already called) and the host should not also act on it.
4866
+ handler calls this while the popover is open. Handles `ArrowDown`/`ArrowUp` (moves the highlight) and
4867
+ `Enter`/`Tab` (commits the highlighted row) both pairs return `false` with no `preventDefault()`
4868
+ when `filteredItems` is empty, letting the keystroke fall through to the host's own control unchanged
4869
+ (e.g. the textarea's caret still moves to the next line on ArrowDown when there's nothing to
4870
+ navigate); `Escape` always closes with no selection and returns `true`. Returns `true` whenever the
4871
+ key was intercepted (`preventDefault()` already called) and the host should not also act on it,
4872
+ `false` otherwise (including for any key this method doesn't recognize).
4388
4873
 
4389
4874
  **Exported types:** `MentionItem { id: string; label: string; description?: string; icon?: string
4390
4875
  }`; `MentionFilter = (item: MentionItem, query: string) => boolean`; `MentionSelectDetail { id:
@@ -4464,6 +4949,10 @@ fresh `query` is the proxy for "the caret may have moved").
4464
4949
  `open` or reassigning `anchor`.
4465
4950
  - `activeIndex` resets to `0` whenever `query` or `items` changes, but not when only `anchor`
4466
4951
  changes — reassigning `anchor` alone preserves whatever row was last highlighted.
4952
+ - There's no persisted "selection" the way `<lyra-combobox>`'s own listbox has one — a mention is
4953
+ either committed (closing the popover) or dismissed with nothing chosen. `aria-selected="true"`
4954
+ here marks whichever row is currently *active* (what Enter/Tab would commit right now, per the
4955
+ WAI-ARIA combobox-with-list-autocomplete pattern), not a separate persisted value.
4467
4956
 
4468
4957
  ---
4469
4958
 
@@ -4501,10 +4990,11 @@ of the stream arrives.
4501
4990
  **CSS parts:** `base`, `cursor` (only rendered while `streaming` is `true`)
4502
4991
 
4503
4992
  **Themeable custom properties:** `--lyra-streaming-text-cursor-width` (default `0.125rem` — the
4504
- cursor bar's inline size; component-specific, since no shared "inline cursor bar" token exists,
4505
- the same pattern `<lyra-typing-indicator>`'s own `--lyra-typing-cursor-width`/`-height` use), plus
4506
- shared `--lyra-space-xs` (cursor's `margin-inline-start`) and `--lyra-transition-base` (blink
4507
- animation cycle length).
4993
+ cursor bar's inline size), `--lyra-streaming-text-cursor-height` (default `1em`) both
4994
+ component-specific, since no shared "inline cursor bar" token exists, the same pattern
4995
+ `<lyra-typing-indicator>`'s own `--lyra-typing-cursor-width`/`-height` use, plus shared
4996
+ `--lyra-space-xs` (cursor's `margin-inline-start`) and `--lyra-transition-base` (blink animation
4997
+ cycle length).
4508
4998
 
4509
4999
  **Optional peer deps:** none — internally imports and auto-registers `<lyra-markdown>` for
4510
5000
  Markdown-mode rendering (a side-effect import; the host never needs to import or register it
@@ -4529,8 +5019,9 @@ purely as a generic "coalesce rapid calls, flush the latest" timing primitive
4529
5019
  class's usual DOM/ARIA plumbing. Within any `coalesce-ms` window, only the *last* `content` value
4530
5020
  assigned actually reaches the rendered DOM. Two cases always bypass the throttle and flush
4531
5021
  immediately: the very first `content` assignment after mount, and any transition of `streaming`
4532
- from `true` to `false`, so the final chunk of a finished stream can never be left stranded mid-
4533
- window.
5022
+ between `true` and `false` in *either* direction — so the final chunk of a finished stream can
5023
+ never be left stranded mid-window, and a stream restarting on a reused element can never keep
5024
+ showing the previous stream's stale final content for the length of the window.
4534
5025
 
4535
5026
  Rendering itself is never reimplemented here: Markdown mode composes `<lyra-markdown>` directly,
4536
5027
  forwarding this component's own `streaming` through as that component's `streaming` hint prop;
@@ -4543,8 +5034,10 @@ happens to end with.
4543
5034
  **Known gotchas:**
4544
5035
  - `content` must always be the complete string so far, never a delta — this component does no
4545
5036
  accumulation of its own.
4546
- - Only the very *first* `content` assignment after mount bypasses `coalesceMs` — every later
4547
- assignment is throttled normally until `streaming` flips to `false`.
5037
+ - Only the very *first* `content` assignment after mount bypasses `coalesceMs` unconditionally
5038
+ every later assignment is throttled normally except when it lands in the same update as a
5039
+ `streaming` transition (either `true → false` or `false → true`), which also forces an immediate
5040
+ flush.
4548
5041
  - `markdown="false"` (any string value other than exactly `"false"` is treated as `true` by the
4549
5042
  converter) forces plain-text mode even if the text obviously contains Markdown syntax.
4550
5043
  - Purely presentational: no events, and it does not announce anything to assistive tech itself — a
@@ -4612,8 +5105,8 @@ Live-mode auto-scroll ("stick to bottom") is the classic chat-transcript convent
4612
5105
  user has manually scrolled up to re-read earlier content (tracked via a `scroll` listener on
4613
5106
  `[part="body"]`: every user-driven scroll records whether the body was left within 48px of its own
4614
5107
  max scroll position, and only a mutation that arrives while that's still true triggers a follow-up
4615
- scroll). Opening an already-`'live'` panel always resets this to "anchored" and jumps to the latest
4616
- content. New content is detected via a `MutationObserver` on this element's own light DOM
5108
+ scroll). Opening an already-`'live'` panel or a still-`expanded` panel later becoming `'live'`
5109
+ always resets this to "anchored" and jumps to the latest content. New content is detected via a `MutationObserver` on this element's own light DOM
4617
5110
  (`childList`+`subtree`+`characterData`), not `slotchange`, since streamed reasoning typically
4618
5111
  appends chunks to an existing node's `textContent` rather than re-slotting a whole new element per
4619
5112
  token; scroll-to-bottom calls are coalesced to at most one per animation frame under a fast token
@@ -4625,9 +5118,11 @@ stream.
4625
5118
  `<lyra-markdown>` re-rendering its shadow tree after a `content` change). A slotted element whose
4626
5119
  own internal updates should drive auto-scroll needs to append/mutate visible light-DOM text
4627
5120
  itself (as `<lyra-streaming-text>` does), or the host can call `scrollToBottom()` directly.
4628
- - Switching `mode` from `'post-hoc'` to `'live'` while the panel is already `expanded` does not by
4629
- itself jump to the bottom or reset stickiness — only an `expanded` transition to `true` while
4630
- `mode === 'live'` does that.
5121
+ - Either half of the pair can trigger the jump-to-bottom/reset-stickiness behavior, as long as the
5122
+ *other* half already holds: an `expanded` transition to `true` while `mode` is already `'live'`,
5123
+ **or** a `mode` transition to `'live'` while the panel is already `expanded`, both jump to the
5124
+ bottom and reset stickiness. Only a change that leaves the panel in some other combination
5125
+ (collapsed, or `mode !== 'live'`) skips it.
4631
5126
  - The sticky-bottom flag starts `true` internally, so a panel that mounts already `expanded` and
4632
5127
  `mode="live"` follows its very first content mutation even before any `scroll` event has fired.
4633
5128
 
@@ -4643,17 +5138,24 @@ equivalent). Renders as e.g. `12.3s · 340 tokens · 27 tok/s [Stop]`.
4643
5138
  - `active: boolean = false` (reflected) — whether generation is currently in progress. The
4644
5139
  elapsed-time ticker (a ~1s `setInterval`) runs only while this is `true`.
4645
5140
  - `startedAt?: number` (attribute `started-at`) — epoch-ms timestamp of when generation began.
4646
- Optional — when unset while `active` is `true`, this component captures `Date.now()` itself the
4647
- moment `active` becomes `true` and counts from there instead.
5141
+ Optional — when unset, or set to a value that fails to parse as a finite number (e.g. an ISO-8601
5142
+ date string, which `type: Number` conversion turns into `NaN`), while `active` is `true`, this
5143
+ component captures `Date.now()` itself the moment `active` becomes `true` and counts from there
5144
+ instead — an invalid value is treated identically to "unset", never rendered as literal `"NaNs"`.
4648
5145
  - `tokenCount?: number` (attribute `token-count`) — running token count so far. Omitted from the
4649
5146
  readout entirely (no `tokens` segment) while unset.
4650
5147
  - `tokensPerSecond?: number` (attribute `tokens-per-second`) — host-computed tokens/sec figure,
4651
5148
  used as-is when set. When unset, derived from `token-count`/elapsed time instead (see prose).
4652
- - `showStop: boolean = true` (attribute `show-stop`) — whether the built-in Stop button renders at
4653
- all. **Not reflected**, and its class-field default is `true` assign `false` via a PROPERTY
4654
- binding, e.g. `.showStop=${false}` a `?show-stop=${false}` boolean-attribute binding can never
4655
- override a `true` default, since removing an attribute that was never present fires no
4656
- `attributeChangedCallback` (see AGENTS.md).
5149
+ - `showStop: boolean = true` (attribute `show-stop`, **not reflected**) — whether the built-in Stop
5150
+ button renders at all. Uses a string-value-aware `ComplexAttributeConverter` (not Lit's default
5151
+ presence-based `type: Boolean`), so a plain-HTML `show-stop="false"` content attribute correctly
5152
+ turns it off — the literal string `"false"` maps to `false`; the attribute's mere presence with any
5153
+ other value (or no value) maps to `true`. A Lit template can instead use a `.showStop=${false}`
5154
+ property binding. **Caveat:** a `?show-stop=${false}` boolean-attribute *binding* still can't turn
5155
+ it off when the attribute was never present in markup to begin with — that binding only ever
5156
+ removes the attribute when falsy, and removing an attribute that's already absent fires no
5157
+ `attributeChangedCallback` (see AGENTS.md); use `.showStop=${false}` or the plain
5158
+ `show-stop="false"` string form instead.
4657
5159
 
4658
5160
  **Events:** `lyra-stop` (no detail payload — `this.emit('lyra-stop')` is called with no second
4659
5161
  argument, so `event.detail` is `null`, not `undefined`) — fired when the built-in Stop button is
@@ -4704,8 +5206,10 @@ something that announces state *transitions* instead. The Stop button gets a nor
4704
5206
  `aria-label="Stop generating"`, no different from any other icon-only button in this library.
4705
5207
 
4706
5208
  **Known gotchas:**
4707
- - `showStop` defaults to `true` and is not a reflected property see the property list above for
4708
- the property-vs-attribute-binding footgun this creates.
5209
+ - `showStop` defaults to `true` and is not a reflected property. Its `ComplexAttributeConverter`
5210
+ makes the plain content attribute `show-stop="false"` work correctly, but a `?show-stop=${false}`
5211
+ Lit boolean-attribute *binding* still can't turn it off starting from absent markup — see the
5212
+ property list above for the exact footgun.
4709
5213
  - The derived `tokens-per-second` figure only appears once `elapsedMs >= 1000`; before that, the
4710
5214
  `throughput` part simply doesn't render — supply `tokens-per-second` yourself for a stable figure
4711
5215
  from the very first tick.
@@ -4741,9 +5245,19 @@ and it's what every instance renders at zero extra bytes until shiki resolves.
4741
5245
  cannot override a true default)
4742
5246
  - `maxHeight: string = ''` (attribute `max-height`) — a CSS length (e.g. `"20rem"`); once set, the
4743
5247
  code scrolls internally past this height instead of growing the page
5248
+ - `languages?: Record<string, ShikiLanguageInput>` (attribute: false) — a map of language id to an
5249
+ already-imported shiki grammar module (e.g. `{ bash: bashGrammar }` from a module-scope
5250
+ `import bash from 'shiki/langs/bash.mjs'`). When `language` matches a key here, highlighting is
5251
+ seeded from exactly that pre-supplied grammar via a fine-grained `createHighlighterCore()`
5252
+ highlighter, bypassing the default ~200-language dynamic-import path (`loadShikiHighlighter()`)
5253
+ for that language — an additive, opt-in escape hatch for a build scoped to just the languages a
5254
+ consumer actually needs. A `language` absent from this map (or `languages` left unset) falls back
5255
+ to the default dynamic-import path unchanged.
4744
5256
 
4745
5257
  **Events:** `lyra-copy` (`detail: { text: string }` — always the raw `code` value, never the
4746
- highlighted HTML, and always fires regardless of whether the actual OS clipboard write succeeded)
5258
+ highlighted HTML, and always fires regardless of whether the actual OS clipboard write succeeded),
5259
+ `lyra-toggle` (`detail: { collapsed: boolean }` — fired when the built-in collapse/expand header
5260
+ button is activated, same event name/shape convention as `<lyra-thinking-panel>`'s own `lyra-toggle`)
4747
5261
 
4748
5262
  **Slots:** none.
4749
5263
 
@@ -4810,10 +5324,9 @@ theme data.
4810
5324
 
4811
5325
  A human-in-the-loop gate: presents one proposed tool/function call (`toolName` + `args`) and blocks an
4812
5326
  agent from executing it until a person explicitly approves or denies it, with an optional inline
4813
- "edit the arguments before approving" step. First-party invention (no Web Awesome equivalent). It is
4814
- its own standalone overlay implementation (`role="dialog"`, focus-trapped, Escape/backdrop-dismissible,
4815
- scroll-locking) rather than nesting `<lyra-dialog>` in its shadow template, so it has no dependency on
4816
- the general-purpose dialog component.
5327
+ "edit the arguments before approving" step. First-party invention (no Web Awesome equivalent). It
5328
+ keeps its own panel template rather than nesting `<lyra-dialog>`, so it has no dependency on the
5329
+ general-purpose dialog component, while its modal behavior participates in the shared overlay stack.
4817
5330
 
4818
5331
  Approve/Deny/Edit are built-in chrome, not a `footer` slot a consumer must assemble — there is exactly
4819
5332
  one correct action set for "approve this call". The `footer` slot is offered only for *supplementary*
@@ -4893,10 +5406,9 @@ Initial focus deliberately does *not* land on Approve: approving a tool call is
4893
5406
  potentially irreversible action, so a user who opens the dialog and reflexively presses Enter/Space
4894
5407
  before reading anything should deny, not approve. Deny gets the initial focus instead — the same
4895
5408
  "focus the safe action" convention a native destructive-confirmation dialog typically follows for its
4896
- own Cancel button. Tab/Shift+Tab are bounded to the panel's own focusable content (shadow-piercingly,
4897
- so `<lyra-json-viewer>`'s internal toggle/copy buttons and any slotted custom element's real focusable
4898
- target are found too) via a locally-duplicated focus-trap helper, the same shape `<lyra-dialog>`'s and
4899
- `<lyra-tool-select-dialog>`'s own overlays use.
5409
+ own Cancel button. Tab/Shift+Tab are bounded to the panel's own focusable content, and
5410
+ `<lyra-json-viewer>`'s internal controls plus slotted custom-element controls are found through the
5411
+ shared composed-tree focus traversal used by the other modal families.
4900
5412
 
4901
5413
  **Known gotchas:**
4902
5414
  - `editable` defaults to `true` and reflects — see the property note above about overriding it with a
@@ -4905,12 +5417,11 @@ target are found too) via a locally-duplicated focus-trap helper, the same shape
4905
5417
  - a consumer flipping `editable` off while an edit is already in progress automatically exits edit mode
4906
5418
  and discards the draft, so an unreachable "Cancel" affordance is never left stranded on screen.
4907
5419
  - reconnecting the element while still `open` (e.g. a drag-and-drop reparent that keeps the same
4908
- instance) automatically re-acquires the ref-counted scroll lock and keydown trap it dropped in
4909
- `disconnectedCallback` — `willUpdate()` alone wouldn't otherwise notice, since disconnect/reconnect
4910
- fire back-to-back with no update in between.
5420
+ instance) resumes its shared overlay registration and re-acquires the ref-counted scroll lock
5421
+ dropped in `disconnectedCallback` — `willUpdate()` alone wouldn't otherwise notice, since
5422
+ disconnect/reconnect fire back-to-back with no update in between.
4911
5423
  - the Approve button's native `disabled` attribute (while the draft is invalid JSON) automatically
4912
- excludes it from the Tab trap, since the shared `FOCUSABLE_SELECTOR` used to compute the trap's
4913
- focusable set already skips disabled controls.
5424
+ excludes it from the shared Tab trap, whose focusable-set computation skips disabled controls.
4914
5425
 
4915
5426
  ---
4916
5427
 
@@ -4920,20 +5431,22 @@ Renders one form control per top-level property of a JSON Schema object, for ad
4920
5431
  approval-editing UIs (e.g. "the agent wants to call `create_event(title, attendees, allDay)` — let the
4921
5432
  user tweak the arguments before running it"). First-party invention (no Web Awesome equivalent).
4922
5433
 
4923
- **Scope limitation (intentional, not accidental):** this component only understands a *flat* object
4924
- schema — every `properties` entry must be `'string'`, `'number'`, `'integer'`, `'boolean'`, or a string
4925
- `enum`. Nested objects, arrays, `oneOf`/`anyOf`/`allOf`, `$ref`, and any other JSON Schema keyword are
4926
- not read. A property whose `type` isn't one of the four above renders a visible "Unsupported field
4927
- type" note instead of silently dropping it or throwing.
5434
+ **Supported schema subset:** a *flat* object whose properties use one primitive `type`
5435
+ (`'string'`, `'number'`, `'integer'`, or `'boolean'`), `required` property presence, string `enum`,
5436
+ primitive `const`, and the `title`/`description`/`default` annotations. Nested objects, arrays, type
5437
+ unions, `oneOf`/`anyOf`/`allOf`, `$ref`, string/numeric constraints, and schema-valued
5438
+ `additionalProperties` are not interpreted. An unsupported property type renders a visible fallback
5439
+ and makes the form invalid instead of being silently accepted.
4928
5440
 
4929
5441
  **Exported types:**
4930
5442
  - `ToolParamFormPropertyType = 'string' | 'number' | 'integer' | 'boolean'` — the four leaf property
4931
5443
  types this renderer understands
5444
+ - `ToolParamFormPrimitive = string | number | boolean` — values accepted by the supported `const`
4932
5445
  - `ToolParamFormProperty { type: ToolParamFormPropertyType; enum?: string[]; description?: string;
4933
- title?: string; default?: unknown }` — one `schema.properties` entry. `enum` is only meaningful when
4934
- `type` is `'string'` (rendered as a `<lyra-select>`); `title` is the display label, falling back to
4935
- the property key itself; `description` renders as helper text under the control; `default` pre-fills
4936
- a field whenever `value` doesn't already have that key.
5446
+ title?: string; default?: unknown; const?: ToolParamFormPrimitive }` — one `schema.properties`
5447
+ entry. `enum` is only meaningful when `type` is `'string'` (rendered as a `<lyra-select>`); `const`
5448
+ enforces one exact primitive value; `title` is the display label; `description` is helper text;
5449
+ `default` pre-fills a field whenever `value` doesn't already have that key.
4937
5450
  - `ToolParamFormSchema { type: 'object'; properties: Record<string, ToolParamFormProperty>; required?:
4938
5451
  string[] }` — the (intentionally flat) schema shape this component can render.
4939
5452
 
@@ -4942,7 +5455,9 @@ type" note instead of silently dropping it or throwing.
4942
5455
  - `value: Record<string, unknown> = {}` (attribute: false) — exactly what the consumer last set it to.
4943
5456
  A field with no entry in `value` but a schema `default` *displays* (and is *emitted*, via
4944
5457
  `lyra-input`) as that default, but the `value` property itself is left alone until the user actually
4945
- edits that field mirrors an uncontrolled `<input placeholder>` not writing to `.value`.
5458
+ edits that field. JSON Schema ordinarily treats `default` as an annotation; this renderer
5459
+ deliberately materializes it before validation/submission, so a valid default can satisfy
5460
+ `required`.
4946
5461
  - `name: string = ''` — submission key for optional native `<form>` participation
4947
5462
  - `disabled: boolean = false` (reflected)
4948
5463
 
@@ -4952,21 +5467,23 @@ type" note instead of silently dropping it or throwing.
4952
5467
  A key the user has explicitly cleared (a real own property set to `undefined`) stays cleared rather
4953
5468
  than snapping back to its default — only a key genuinely absent from `value` falls back.
4954
5469
  - `errors: Record<string, string>` — the current per-field validation errors (`{ [propertyKey]:
4955
- message }`), required-and-empty only; independent of which fields have been visited, so it doesn't
4956
- need a prior `reportValidity()` call to be accurate.
5470
+ message }`) for required presence, primitive type, finite number/integer, enum, const, and
5471
+ unsupported type; independent of which fields have been visited.
5472
+ - `formError: string` — a schema-wide/JSON-serialization error that has no honest field key; empty
5473
+ when the current effective value is safe to submit.
4957
5474
 
4958
5475
  **Methods:**
4959
- - `checkValidity(): boolean` — non-mutating; delegates to the underlying `ElementInternals`, never
4960
- reveals inline errors.
4961
- - `reportValidity(): boolean` — reveals inline errors for every currently-invalid required field (as
4962
- if each had been visited) and returns overall validity; the hook a consumer's own Submit/Approve
4963
- button should call right before acting, mirroring a native `<form>`'s `reportValidity()`.
5476
+ - `checkValidity(): boolean` — synchronously re-snapshots even an in-place-mutated value/schema,
5477
+ updates `ElementInternals`, and returns validity without revealing inline errors.
5478
+ - `reportValidity(): boolean` — performs the same resynchronization, reveals all current field/root
5479
+ errors, focuses the first invalid generated field when one exists, and delegates to native
5480
+ `ElementInternals.reportValidity()`.
4964
5481
 
4965
5482
  **Events:** `lyra-input` (`detail: { value: Record<string, unknown> }` — the full current value
4966
5483
  object, every property with defaults resolved, not just the field that changed), `lyra-validity-change`
4967
- (`detail: { valid: boolean; errors: Record<string, string> }` — fired whenever overall validity
4968
- changes, including once up front at connect time, so a consumer sees `valid: false` immediately if a
4969
- required field starts out empty without waiting for user interaction)
5484
+ (`detail: { valid: boolean; errors: Record<string, string> }` — fired whenever overall validity or
5485
+ the field-error set changes, including once up front at connect time; serialization-only failures
5486
+ set `valid: false` while `formError`, rather than a fabricated field key, carries the root message)
4970
5487
 
4971
5488
  **Slots:** none.
4972
5489
 
@@ -5007,29 +5524,39 @@ This component owns no Submit/Cancel/Approve chrome — a consumer composes it i
5007
5524
  that's `<lyra-checkbox>`'s documented way to give itself an accessible name, unlike `aria-describedby`
5008
5525
  (neither `<lyra-select>` nor `<lyra-checkbox>` forward a host-level `aria-describedby` to their
5009
5526
  internal focusable element, so for the enum-select case the error message is folded into `aria-label`
5010
- instead, alongside the always-visible adjacent `[part="description"]` text). A `required` boolean
5011
- field means "must be checked" there is no third "unset" visual state a rendered checkbox can
5012
- represent, so `isFieldEmpty()` treats anything other than `true` as empty for that field's type.
5527
+ instead, alongside the always-visible adjacent `[part="description"]` text). The outer component owns
5528
+ schema validity rather than forwarding HTML `required` constraints: JSON Schema `required` means an
5529
+ own property is present, so `''`, `0`, and `false` are valid present values. Use
5530
+ `{ type: 'boolean', const: true }` together with `required` for a must-confirm checkbox.
5013
5531
 
5014
5532
  Optional native `<form>` participation is implemented via `ElementInternals` attached directly in the
5015
5533
  constructor (`static formAssociated = true`) rather than a string-value mixin, since this component's
5016
5534
  value is a whole object: `formResetCallback()` clears `value`/touched state back to `{}`, and
5017
- `formDisabledCallback(disabled)` mirrors the wrapping form's disabled state onto the `disabled`
5018
- property. This is a nice-to-have layered on top of the primary `value` + `lyra-input`/
5019
- `lyra-validity-change` contract, not a requirement a consumer that never puts this inside a `<form>`
5020
- loses nothing.
5535
+ `formDisabledCallback(disabled)` tracks inherited fieldset state separately from the author-owned
5536
+ `disabled` property. JSON serialization is guarded: circular values, `BigInt`, throwing getters/`toJSON`, and
5537
+ non-finite numbers cannot escape from an assignment or leave stale form data; the form entry is
5538
+ temporarily removed and `formError`/custom validity are set until a serializable value replaces it.
5539
+ This is layered on top of the primary `value` + `lyra-input`/`lyra-validity-change` contract.
5540
+ The same safe serialized object is used as session-history/autofill state. Restoration accepts only
5541
+ a JSON object, falls back to `{}` for malformed/non-object state, and does not emit `lyra-input`.
5021
5542
 
5022
5543
  **Known gotchas:**
5023
5544
  - a schema property whose `type` isn't `'string'`/`'number'`/`'integer'`/`'boolean'` renders an inline
5024
5545
  "Unsupported field type" message (a plain `.unsupported`-classed element, not one of the documented
5025
- CSS parts) instead of throwing or being silently dropped.
5546
+ CSS parts) and fails closed with custom validity instead of throwing or being silently dropped.
5026
5547
  - inline per-field errors only render once a field has been visited (`focusout`) at least once, or
5027
5548
  after an explicit `reportValidity()` call — `checkValidity()` alone never reveals them, matching
5028
5549
  every other form control in this library (`<lyra-select>`/`<lyra-combobox>`/`<lyra-model-select>`
5029
5550
  all avoid flashing red before the user has touched anything).
5030
5551
  - `effectiveValue` distinguishes "key absent from `value`" (falls back to `default`) from "key present
5031
- but `undefined`" (stays cleared) via `hasOwnProperty`, not an `=== undefined` check — relevant if you
5032
- ever construct `value` objects by hand rather than round-tripping `lyra-input`'s detail.
5552
+ but `undefined`" (stays cleared and counts as absent for `required`) via `hasOwnProperty`, not an
5553
+ `=== undefined` fallback check.
5554
+ - additional value keys are retained and submitted (matching JSON Schema's default open-object
5555
+ behavior), but schema-valued/false `additionalProperties` is outside this renderer's subset.
5556
+ - reassign `value`/`schema` after changing them. Direct in-place mutation is resnapshotted by the
5557
+ component's own `checkValidity()`/`reportValidity()`, but native `form.checkValidity()` and
5558
+ submission use the last synchronized snapshot because the browser cannot observe arbitrary object
5559
+ mutation.
5033
5560
  - `lyra-validity-change` fires once immediately at connect time even before any user interaction, so a
5034
5561
  form with an unmet required field announces `valid: false` on mount, not only after the first edit.
5035
5562
 
@@ -5090,17 +5617,32 @@ internal shadow-DOM button; `<lyra-menu>` is the sole owner of this element's `t
5090
5617
  `<lyra-menu>`'s roving-tabindex navigation entirely)
5091
5618
  - `destructive: boolean = false` (reflected — tints the row with `--lyra-color-danger`, for a
5092
5619
  dangerous action like "Delete")
5620
+ - `type: 'normal' | 'checkbox' = 'normal'` — `'checkbox'` (mirroring `wa-dropdown-item`'s identical
5621
+ `type` option) renders `role="menuitemcheckbox"` in place of `role="menuitem"`, with `aria-checked`
5622
+ reflecting `checked` and a checkmark glyph shown once `checked` is `true`. `'normal'` (the default)
5623
+ renders and behaves exactly as before this option existed.
5624
+ - `checked: boolean = false` (reflected) — whether a `type="checkbox"` item is checked; meaningless
5625
+ (ignored) for `type="normal"`
5626
+
5627
+ **Methods:** `select(): void` — fires `lyra-menu-item-select` (no-op while `disabled`). Called
5628
+ internally by this element's own click handler and by `<lyra-menu>`'s Enter/Space keydown handling
5629
+ of the roving-focused item; also the cleanest way for a consumer/test to trigger selection
5630
+ programmatically instead of clicking the shadow-DOM `[part="base"]` element (see the gotcha below).
5631
+ For `type="checkbox"`, also toggles `checked` and fires `lyra-menu-item-change` first.
5093
5632
 
5094
5633
  **Events:** `lyra-menu-item-select` (no detail payload — `this.emit('lyra-menu-item-select')` is
5095
5634
  called with no second argument, so `event.detail` is `null`, not `undefined`; fires on click, or
5096
5635
  when the parent `<lyra-menu>`'s own Enter/Space keydown handling calls `select()` on the currently
5097
- roving-focused item)
5636
+ roving-focused item), `lyra-menu-item-change` (`detail: { value, checked }` — fired when a
5637
+ `type="checkbox"` item is activated and its `checked` state toggled, in addition to — never instead
5638
+ of — `lyra-menu-item-select`; never fired for `type="normal"`)
5098
5639
 
5099
5640
  **Slots:** default (the item's label content), `icon` (optional leading icon)
5100
5641
 
5101
5642
  **CSS parts:** `base` (the row — `role` lives on the host, not this part), `icon` (wrapper around
5102
5643
  the `icon` slot; not rendered/hidden entirely while the slot is empty), `label` (wrapper around the
5103
- default slot)
5644
+ default slot), `checkmark` (the checkmark glyph shown when a `type="checkbox"` item is `checked`;
5645
+ not rendered at all for `type="normal"`)
5104
5646
 
5105
5647
  **Themeable custom properties:** shared tokens only (`--lyra-radius`, `--lyra-focus-ring-width`,
5106
5648
  `--lyra-focus-ring-color`, `--lyra-space-xs`, `--lyra-space-s`, `--lyra-color-brand-quiet`,
@@ -5125,17 +5667,24 @@ The popup is always rendered in the DOM (never `display:none`) so `.focus()` cal
5125
5667
  work synchronously the instant it opens — closed state is conveyed via `visibility`/`opacity`
5126
5668
  instead. `visibility` is an inherited CSS property that pierces the `<slot>` projection boundary, so
5127
5669
  every closed-state `<lyra-menu-item>` is automatically excluded from sequential (Tab-key)
5128
- navigation with no separate JS bookkeeping. Once open, ArrowDown/ArrowUp move the roving focus among
5129
- non-disabled items and wrap past either end; Home/End jump to the first/last non-disabled item;
5130
- Enter/Space activate the focused item; Escape closes and refocuses the trigger. A click outside both
5131
- the trigger and the open popup also closes it, but deliberately does *not* refocus the trigger the
5132
- outside click itself already moved focus somewhere the user chose.
5670
+ navigation with no separate JS bookkeeping. ArrowDown/ArrowUp *on the trigger while closed* also
5671
+ opens the menu, focusing the first/last non-disabled item respectively (mirrors native `<select>`).
5672
+ Once open, ArrowDown/ArrowUp move the roving focus among non-disabled items and wrap past either end
5673
+ (unlike `<lyra-select>`'s clamped listbox nav); Home/End jump to the first/last non-disabled item;
5674
+ Enter/Space activate the focused item; Escape closes and refocuses the trigger; Tab closes without
5675
+ `preventDefault()`, letting the browser's own Tab navigation proceed untouched. A printable keypress
5676
+ runs type-ahead: roving focus jumps to the next non-disabled item whose text starts with the
5677
+ accumulated buffer (cycling from just after the active item, buffer resets ~500ms after the last
5678
+ keystroke) — mirrors `<lyra-select>`'s identical listbox type-ahead. A click outside both the trigger
5679
+ and the open popup also closes it, but deliberately does *not* refocus the trigger — the outside
5680
+ click itself already moved focus somewhere the user chose.
5133
5681
 
5134
5682
  **Known gotchas:**
5135
5683
  - `<lyra-menu-item>`'s click handler lives on an inner shadow-DOM element (`[part="base"]`), not the
5136
5684
  host — calling `.click()` directly on the `<lyra-menu-item>` host element in a test does **not**
5137
- trigger selection; you must click (or dispatch on) the element returned by
5138
- `menuItemEl.shadowRoot.querySelector('[part="base"]')`.
5685
+ trigger selection; either click (or dispatch on) the element returned by
5686
+ `menuItemEl.shadowRoot.querySelector('[part="base"]')`, or just call the item's own `select()`
5687
+ method directly.
5139
5688
  - `lyra-show`/`lyra-hide` are suppressed on the very first render even if `open` is already `true`
5140
5689
  in markup — only later `open` transitions fire them.
5141
5690
  - `lyra-menu-item-select` carries no detail payload (`event.detail === null`); read
@@ -5279,6 +5828,10 @@ of `catalog`/`allowCustom` and `temperatureMin`/`temperatureMax`/`temperatureSte
5279
5828
  - `layout: 'vertical' | 'compact' = 'vertical'` (reflected) — `vertical` stacks full-width rows with
5280
5829
  visible labels; `compact` runs the same two rows side by side with a smaller, uppercase temperature
5281
5830
  caption, for toolbars/sidebars where the vertical layout's height doesn't fit.
5831
+ - `disabled: boolean = false` (reflected) — disables the panel as a unit by forwarding to *both*
5832
+ internal `lyra-model-select` and `lyra-slider`; a wrapping `<fieldset disabled>` alone would not
5833
+ reach either, since a form-associated control's own `disabled` IDL property/attribute is never
5834
+ mutated by fieldset cascading.
5282
5835
 
5283
5836
  **Events:** `lyra-change` — `detail: { modelValue: string; inCatalog: boolean; temperature: number }`.
5284
5837
  Fires whenever *either* child control's own `lyra-change` fires, and always carries the full current
@@ -5313,7 +5866,13 @@ internally (both imported unconditionally as side effects, not optional).
5313
5866
  The internal `lyra-slider` renders with its own value readout suppressed (`.showValue=${false}`);
5314
5867
  the current temperature is instead shown via this component's own `[part="temperature-value"]` span,
5315
5868
  which interpolates `temperature` verbatim with no `toFixed`/formatting applied — a value like `0.1`
5316
- shows as `0.1`, and any floating-point noise a slider drag produces would render digit-for-digit.
5869
+ shows as `0.1`, and any floating-point noise a slider drag produces would render digit-for-digit. The
5870
+ panel's own `temperature` property mirrors the nested slider's *live* value on every one of its
5871
+ `lyra-input` events (drag/key-repeat), not just its committed `lyra-change` — so `temperature` (and
5872
+ the visible readout) tick continuously during a drag, but the panel's own `lyra-change` event only
5873
+ fires once the slider's own `lyra-change` commits (pointerup/keyup) or the model changes; reading
5874
+ `.temperature` mid-drag will already reflect the live position even though no `lyra-change` has fired
5875
+ yet for it.
5317
5876
 
5318
5877
  **Known gotchas:**
5319
5878
  - `catalog` is JS-only (`attribute: false`) — set it via a property binding (`.catalog=`), never as
@@ -5355,8 +5914,8 @@ capacity), `segment` (one occupied segment — carries `data-tone` for styling),
5355
5914
 
5356
5915
  **Themeable custom properties:** no component-specific custom properties; consumes shared tokens
5357
5916
  `--lyra-space-xs`, `--lyra-color-text-quiet`, `--lyra-font`, `--lyra-radius`, `--lyra-color-border`,
5358
- `--lyra-color-brand`, `--lyra-color-success`, `--lyra-color-warning`, `--lyra-color-danger`,
5359
- `--lyra-transition-base`.
5917
+ `--lyra-color-surface` (the bar variant's inter-segment seam), `--lyra-color-brand`,
5918
+ `--lyra-color-success`, `--lyra-color-warning`, `--lyra-color-danger`, `--lyra-transition-base`.
5360
5919
 
5361
5920
  **Optional peer deps:** none.
5362
5921
 
@@ -5377,15 +5936,17 @@ capacity), `segment` (one occupied segment — carries `data-tone` for styling),
5377
5936
  `role="img"` and a computed `aria-label` are set imperatively on the *host* element itself in
5378
5937
  `willUpdate` (mirroring `lyra-gauge`'s "meter" role convention) — every internal node (`track`,
5379
5938
  `segment`, the ring's `<svg>`, `label`) is `aria-hidden`, so a screen reader gets one meaningful
5380
- summary string instead of the raw markup. That summary's "used" figure is the plain, unclamped sum of
5381
- `segments[].value` (`"150,000 of 128,000 used"` is possible if segments overshoot `total`), even
5382
- though the *visual* meter itself never overflows: each segment's ratio is clamped so the running
5383
- cumulative fill across all segments can never exceed 100% of the bar/ring an over-`total` `segments`
5384
- array renders as a fully (not over-) filled meter, with later segments truncated or squeezed to zero
5385
- width/arc-length as the budget runs out. `total <= 0` (or non-finite) renders zero segments — an empty
5386
- track/ring regardless of what's in `segments`. Ring geometry (a 40-radius circle, 12px stroke,
5387
- centered at 50,50) intentionally matches `lyra-gauge`'s own radial numbers, so the two circular-meter
5388
- components in the library share one visual scale.
5939
+ summary string instead of the raw markup. That summary's "used" figure is the sum of
5940
+ `segments[].value`, clamped to `total` whenever `total > 0` so the announced text can never claim
5941
+ more than 100% used (e.g. `segments` summing to `150000` against `total="128000"` still announces
5942
+ `"128,000 of 128,000 used"`) matching what the *visual* meter shows, since each segment's ratio is
5943
+ independently clamped so the running cumulative fill across all segments can never exceed 100% of the
5944
+ bar/ring either: an over-`total` `segments` array renders as a fully (not over-) filled meter, with
5945
+ later segments truncated or squeezed to zero width/arc-length as the budget runs out. `total <= 0`
5946
+ (or non-finite) renders zero segments an empty track/ring — and the announced summary falls back to
5947
+ just `"{used} used"` with no `"of {total}"` clause, regardless of what's in `segments`. Ring geometry
5948
+ (a 40-radius circle, 12px stroke, centered at 50,50) intentionally matches `lyra-gauge`'s own radial
5949
+ numbers, so the two circular-meter components in the library share one visual scale.
5389
5950
 
5390
5951
  **Known gotchas:**
5391
5952
  - The ring variant's per-segment `<title>` and the bar variant's per-segment `title=` attribute are
@@ -5523,10 +6084,21 @@ way to get a `<pre>`'s text content from a URL — gated behind a generation-cou
5523
6084
  (`lyra-tool-result-view`'s `resolve()` uses the identical pattern) so a `src` reassigned mid-fetch
5524
6085
  can't have a stale response clobber a newer one.
5525
6086
 
6087
+ Every `src` is validated (via `internal/safe-url.ts`) against a scheme allowlist specific to the DOM/
6088
+ API sink it's about to reach: `fetch(src)` (text preview) and an `<img src>` (image preview) both
6089
+ allow relative URLs plus `http:`, `https:`, `blob:`, and `data:`; the download `<a href>` deliberately
6090
+ excludes `data:` (following a `data:text/html` URL can create an active document, unlike using it as
6091
+ inert media/fetch data). A `src` that fails its sink's check never reaches `fetch()`/`<img>`/the
6092
+ anchor: the text preview renders `[part="error"]` with `"Document URL is not allowed."`, the image
6093
+ preview silently falls back to the generic download-or-`unsupported`-slot fallback, and the generic
6094
+ fallback simply omits `[part="download-link"]` entirely.
6095
+
5526
6096
  **Properties:**
5527
6097
  - `src: string = ''` — URL to fetch (for `text`/`application/json`) or display (`image`, or as the
5528
6098
  generic fallback's download `href`). Optional — gracefully absent while, e.g., a conversion is
5529
- still in progress.
6099
+ still in progress. Validated per-sink before use — see the URL-safety note above; an unsafe/
6100
+ malformed value is treated as if `src` were unusable for that sink, never passed to `fetch()`/
6101
+ `<img>`/the anchor.
5530
6102
  - `mimeType: string = ''` (attribute `mime-type`) — drives format dispatch (see above).
5531
6103
  - `filename: string = ''` — shown in the header and used as the download link's suggested filename.
5532
6104
  - `status: 'idle' | 'converting' | 'ready' | 'error' = 'idle'` (reflected) — host-owned lifecycle
@@ -5557,7 +6129,8 @@ download fallback for any `mime-type` this component doesn't natively support. I
5557
6129
  `spinner` (indeterminate `role="status"`, or `role="progressbar"` once numeric progress is known —
5558
6130
  used both for `status="converting"` and this component's own in-flight text fetch), `error`
5559
6131
  (`role="alert"` — used both for `status="error"` and a failed text fetch), `download-link` (only
5560
- rendered when `src` is set)
6132
+ rendered when `src` is set *and* passes the link-safe scheme allowlist — see the URL-safety note
6133
+ above; excludes `data:` even though the other two sinks allow it)
5561
6134
 
5562
6135
  **Themeable custom properties:** `--lyra-document-preview-max-height` (default `none`) — the
5563
6136
  consumer-tunable scroll cap on `[part="body"]`, set from `max-height`; `none` means the preview grows
@@ -5612,6 +6185,13 @@ available, the region becomes a standard `role="progressbar"` instead, self-desc
5612
6185
  just via two different mechanisms depending on timing.
5613
6186
  - `download-link` (and thus `lyra-download`) only renders/fires when `src` is set — a generic-fallback
5614
6187
  state with no `src` shows only the file glyph and message, with no download affordance at all.
6188
+ - `download-link` also never renders for a `data:` URL, even though `data:` is accepted for text/image
6189
+ sinks — a `src="data:..."` document renders/fetches fine but falls back to no download affordance
6190
+ in the generic state.
6191
+ - A `src` that fails its sink's URL-safety check does **not** raise `lyra-render-error` — that event is
6192
+ reserved for a `fetch()` that was actually attempted and failed at the network layer; an unsafe/
6193
+ malformed `src` is silently treated as unusable instead (a rendered `[part="error"]` message for
6194
+ text, a silent fallback for image/download).
5615
6195
 
5616
6196
  ---
5617
6197
 
@@ -5633,13 +6213,20 @@ final.
5633
6213
  filename, and folded into the accessible name.
5634
6214
  - `alt: string = ''` — alt text for the image case (and reused as a video label fallback). Falls
5635
6215
  back to `filename`, then a generic per-kind description.
6216
+ - `maxHeight: string = ''` (attribute `max-height`) — a CSS length (e.g. `"16rem"`); once set,
6217
+ overrides the `--lyra-media-card-max-height` custom property for this instance only (applied
6218
+ inline on `[part="base"]`, so it reliably wins over a `:host{}`-declared default from outside the
6219
+ shadow root) — same contract as `<lyra-document-preview>`'s identically-named prop.
5636
6220
 
5637
6221
  **Events:** `lyra-open` (`detail: { src: string; filename: string }`, cancelable) — fired when the
5638
- card (or, for `kind="video"`, its separate `open-button`) is activated. This component never
5639
- navigates on its own for `image`/`video` a host decides what "open" means. The `file`-chip case is
5640
- the exception: when `src` passes the stricter href safety check, the chip is a real `<a href
5641
- download>` so a bare drop-in still does something useful, but `lyra-open` fires first a host
5642
- calling `preventDefault()` on it suppresses that default download/open so it can substitute its own
6222
+ card (or, for `kind="video"`, its separate `open-button`) is activated. `detail.src` is whichever
6223
+ safe-URL sink actually rendered (`safeMediaSrc(src) ?? safeLinkHref(src) ?? src.trim()`), not
6224
+ necessarily the raw `src` property verbatim a whitespace-padded value is trimmed, so `detail.src`
6225
+ always matches what the DOM would show if it were safe. This component never navigates on its own
6226
+ for `image`/`video` a host decides what "open" means. The `file`-chip case is the exception: when
6227
+ `src` passes the stricter href safety check, the chip is a real `<a href download>` so a bare
6228
+ drop-in still does something useful, but `lyra-open` fires first — a host calling
6229
+ `preventDefault()` on it suppresses that default download/open so it can substitute its own
5643
6230
  handling.
5644
6231
 
5645
6232
  **Slots:** none.
@@ -5649,8 +6236,13 @@ an `<a>` or `<span>` for the file-chip fallback depending on href safety), `medi
5649
6236
  `<video>`), `file-icon`, `filename` (file-chip fallback only), `open-button` (video only — see
5650
6237
  below).
5651
6238
 
5652
- **Themeable custom properties:** shared tokens only`--lyra-space-xs`/`-s`, `--lyra-color-border`,
5653
- `--lyra-color-surface`, `--lyra-color-text`/`-text-quiet`, `--lyra-radius`, `--lyra-focus-ring-*`.
6239
+ **Themeable custom properties:** `--lyra-media-card-max-height` (default `20rem`caps `[part="media"]`'s
6240
+ block-size so one oversized image/video can't blow out a chat bubble; same naming/contract as
6241
+ `<lyra-document-preview>`'s identical `--lyra-document-preview-max-height`; override per-instance via
6242
+ the `max-height` attribute instead of this property directly). Plus shared tokens
6243
+ `--lyra-space-xs`/`-s`, `--lyra-color-border`, `--lyra-color-surface`, `--lyra-color-text`/`-text-quiet`,
6244
+ `--lyra-color-brand` (hover border), `--lyra-radius`, `--lyra-icon-button-size` (video's `open-button`
6245
+ sizing), `--lyra-focus-ring-*`, `--lyra-transition-fast`.
5654
6246
 
5655
6247
  **Optional peer deps:** none.
5656
6248
 
@@ -5715,6 +6307,11 @@ capability as a row.
5715
6307
  - `multiple: boolean = true` (reflected) — forwarded to the hidden file input's own `multiple`
5716
6308
  attribute.
5717
6309
  - `disabled: boolean = false` (reflected)
6310
+ - `triggerLabel?: string` (attribute `trigger-label`) — overrides the single-capability trigger
6311
+ button's `aria-label`, which otherwise comes from `CAPABILITY_META[capability].triggerLabel` (e.g.
6312
+ `'Attach files'`); only affects the single-capability button (`[part='trigger']`) — the
6313
+ multi-capability menu's own trigger keeps its fixed `'Add attachment'` label regardless. Unset
6314
+ (the default) keeps the built-in English default.
5718
6315
 
5719
6316
  **Events:** `lyra-pick` (`detail: { capability: 'files' | 'image'; files: FileList }`) — fired once a
5720
6317
  file-backed capability's hidden input produces a real selection. The `FileList` is an independent
@@ -5729,10 +6326,13 @@ native wrapper's own camera API).
5729
6326
 
5730
6327
  **CSS parts:** `trigger` (the single-capability button, only rendered when
5731
6328
  `capabilities.length === 1`), `menu` (the `lyra-menu` wrapper, only rendered when
5732
- `capabilities.length > 1`).
6329
+ `capabilities.length > 1`), `menu-trigger` (the multi-capability button slotted into `lyra-menu`'s own
6330
+ `trigger` slot, only rendered when `capabilities.length > 1`), `expand-icon` (the disclosure chevron
6331
+ inside the multi-capability trigger button, only rendered when `capabilities.length > 1`).
5733
6332
 
5734
- **Themeable custom properties:** shared tokens only — `--lyra-space-xs`, `--lyra-color-text`,
5735
- `--lyra-icon-button-size`, `--lyra-focus-ring-*`, `--lyra-opacity-disabled`, `--lyra-radius`.
6333
+ **Themeable custom properties:** shared tokens only — `--lyra-space-xs`, `--lyra-color-text`/
6334
+ `-text-quiet`, `--lyra-icon-button-size`, `--lyra-focus-ring-*`, `--lyra-opacity-disabled`,
6335
+ `--lyra-radius`, `--lyra-transition-fast`.
5736
6336
 
5737
6337
  **Optional peer deps:** none.
5738
6338
 
@@ -5786,13 +6386,14 @@ string.
5786
6386
 
5787
6387
  **Slots:** default — an escape hatch for fully custom key-cap content (e.g. an icon instead of a
5788
6388
  text glyph). When it has any real (non-whitespace) content, it replaces the `keys`-driven rendering
5789
- entirely and this component stops asserting its own `aria-label`, leaving the slotted content to
5790
- carry its own accessible name.
6389
+ entirely and this component stops *computing* its own `aria-label` from `keys`, leaving the slotted
6390
+ content to carry its own accessible name — a host-supplied `aria-label` attribute is still forwarded
6391
+ onto the rendered chip in either mode, custom content included.
5791
6392
 
5792
6393
  **CSS parts:** `base` (the chip root), `key` (one per rendered token).
5793
6394
 
5794
6395
  **Themeable custom properties:** shared tokens only — `--lyra-space-xs`, `--lyra-color-surface`/
5795
- `-border`/`-text-quiet`, `--lyra-radius`, `--lyra-font`.
6396
+ `-border`/`-text`/`-text-quiet`, `--lyra-radius`, `--lyra-font`.
5796
6397
 
5797
6398
  **Optional peer deps:** none.
5798
6399
 
@@ -5811,7 +6412,10 @@ The rendered chip carries `role="img"` with a single spelled-out `aria-label` (e
5811
6412
  rather than exposing each glyph/`+`-separator as separate accessible-tree text, since the individual
5812
6413
  pieces aren't real words and would read worse piecemeal than as one label — glyphs like ⌘/⇧/⌥ are
5813
6414
  not reliably announced by every screen reader/platform combination, which is exactly why the
5814
- spelled-out word form exists at all.
6415
+ spelled-out word form exists at all. An empty `keys` with no explicit `aria-label` override and no
6416
+ slotted content renders nothing visible and is marked `aria-hidden="true"` (no `role="img"`) instead
6417
+ of exposing a nameless image element — `role`/`aria-hidden` are both derived from the same
6418
+ computed-label value so the two can never disagree.
5815
6419
 
5816
6420
  ---
5817
6421
 
@@ -5839,7 +6443,10 @@ title).
5839
6443
 
5840
6444
  **CSS parts:** `base` (outer bordered container), `header` (present in the DOM at all times so a
5841
6445
  later `slotchange` on `actions` is still observed, but `hidden` whenever there's no `title` and no
5842
- `actions` content), `title`, `actions` (`hidden` whenever the slot has no assigned content), `body`.
6446
+ `actions` content), `title` (truncates with an ellipsis when it overflows; carries its own native
6447
+ `title` attribute — the full string — so hovering the truncated text reveals it via the browser's
6448
+ default tooltip, scoped to just this element rather than the whole card), `actions` (`hidden`
6449
+ whenever the slot has no assigned content), `body`.
5843
6450
 
5844
6451
  **Themeable custom properties:** shared tokens only — `--lyra-space-xs`/`-s`, `--lyra-color-border`/
5845
6452
  `-surface`/`-text`, `--lyra-radius`.