@aceshooting/lyra-ui 2.2.0 → 2.4.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 (344) hide show
  1. package/README.md +8 -1
  2. package/custom-elements.json +18746 -12825
  3. package/dist/components/app-rail/app-rail-item.class.d.ts +16 -1
  4. package/dist/components/app-rail/app-rail-item.class.d.ts.map +1 -1
  5. package/dist/components/app-rail/app-rail-item.class.js +52 -1
  6. package/dist/components/app-rail/app-rail-item.class.js.map +1 -1
  7. package/dist/components/app-rail/app-rail-item.styles.d.ts.map +1 -1
  8. package/dist/components/app-rail/app-rail-item.styles.js +11 -0
  9. package/dist/components/app-rail/app-rail-item.styles.js.map +1 -1
  10. package/dist/components/app-rail/app-rail.class.d.ts +13 -1
  11. package/dist/components/app-rail/app-rail.class.d.ts.map +1 -1
  12. package/dist/components/app-rail/app-rail.class.js +23 -1
  13. package/dist/components/app-rail/app-rail.class.js.map +1 -1
  14. package/dist/components/app-rail/app-rail.styles.d.ts.map +1 -1
  15. package/dist/components/app-rail/app-rail.styles.js +6 -0
  16. package/dist/components/app-rail/app-rail.styles.js.map +1 -1
  17. package/dist/components/attachment-chip/attachment-chip.class.d.ts +15 -1
  18. package/dist/components/attachment-chip/attachment-chip.class.d.ts.map +1 -1
  19. package/dist/components/attachment-chip/attachment-chip.class.js +27 -4
  20. package/dist/components/attachment-chip/attachment-chip.class.js.map +1 -1
  21. package/dist/components/attachment-chip/attachment-chip.styles.d.ts.map +1 -1
  22. package/dist/components/attachment-chip/attachment-chip.styles.js +7 -0
  23. package/dist/components/attachment-chip/attachment-chip.styles.js.map +1 -1
  24. package/dist/components/attachment-trigger/attachment-trigger.class.d.ts +6 -3
  25. package/dist/components/attachment-trigger/attachment-trigger.class.d.ts.map +1 -1
  26. package/dist/components/attachment-trigger/attachment-trigger.class.js +51 -0
  27. package/dist/components/attachment-trigger/attachment-trigger.class.js.map +1 -1
  28. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts.map +1 -1
  29. package/dist/components/attachment-trigger/attachment-trigger.styles.js +10 -0
  30. package/dist/components/attachment-trigger/attachment-trigger.styles.js.map +1 -1
  31. package/dist/components/avatar/avatar.class.d.ts +43 -0
  32. package/dist/components/avatar/avatar.class.d.ts.map +1 -0
  33. package/dist/components/avatar/avatar.class.js +75 -0
  34. package/dist/components/avatar/avatar.class.js.map +1 -0
  35. package/dist/components/avatar/avatar.d.ts +2 -0
  36. package/dist/components/avatar/avatar.d.ts.map +1 -0
  37. package/dist/components/avatar/avatar.js +5 -0
  38. package/dist/components/avatar/avatar.js.map +1 -0
  39. package/dist/components/avatar/avatar.styles.d.ts +2 -0
  40. package/dist/components/avatar/avatar.styles.d.ts.map +1 -0
  41. package/dist/components/avatar/avatar.styles.js +55 -0
  42. package/dist/components/avatar/avatar.styles.js.map +1 -0
  43. package/dist/components/card/card.class.d.ts +52 -0
  44. package/dist/components/card/card.class.d.ts.map +1 -0
  45. package/dist/components/card/card.class.js +111 -0
  46. package/dist/components/card/card.class.js.map +1 -0
  47. package/dist/components/card/card.d.ts +2 -0
  48. package/dist/components/card/card.d.ts.map +1 -0
  49. package/dist/components/card/card.js +5 -0
  50. package/dist/components/card/card.js.map +1 -0
  51. package/dist/components/card/card.styles.d.ts +2 -0
  52. package/dist/components/card/card.styles.d.ts.map +1 -0
  53. package/dist/components/card/card.styles.js +77 -0
  54. package/dist/components/card/card.styles.js.map +1 -0
  55. package/dist/components/chart/chart.class.d.ts +11 -11
  56. package/dist/components/chart/chart.class.d.ts.map +1 -1
  57. package/dist/components/chart/chart.class.js +44 -3
  58. package/dist/components/chart/chart.class.js.map +1 -1
  59. package/dist/components/chart/lite-chart.class.d.ts +16 -9
  60. package/dist/components/chart/lite-chart.class.d.ts.map +1 -1
  61. package/dist/components/chart/lite-chart.class.js +99 -11
  62. package/dist/components/chart/lite-chart.class.js.map +1 -1
  63. package/dist/components/chart/lite-chart.styles.d.ts.map +1 -1
  64. package/dist/components/chart/lite-chart.styles.js +4 -0
  65. package/dist/components/chart/lite-chart.styles.js.map +1 -1
  66. package/dist/components/chat-composer/chat-composer.class.d.ts +18 -13
  67. package/dist/components/chat-composer/chat-composer.class.d.ts.map +1 -1
  68. package/dist/components/chat-composer/chat-composer.class.js +69 -4
  69. package/dist/components/chat-composer/chat-composer.class.js.map +1 -1
  70. package/dist/components/chat-message/chat-message.class.d.ts +3 -3
  71. package/dist/components/chat-message/chat-message.class.d.ts.map +1 -1
  72. package/dist/components/chat-message/chat-message.class.js +71 -8
  73. package/dist/components/chat-message/chat-message.class.js.map +1 -1
  74. package/dist/components/checkbox/checkbox.class.d.ts +5 -5
  75. package/dist/components/checkbox/checkbox.class.d.ts.map +1 -1
  76. package/dist/components/checkbox/checkbox.class.js +24 -0
  77. package/dist/components/checkbox/checkbox.class.js.map +1 -1
  78. package/dist/components/chip/chip-group.class.d.ts.map +1 -1
  79. package/dist/components/chip/chip-group.class.js +4 -2
  80. package/dist/components/chip/chip-group.class.js.map +1 -1
  81. package/dist/components/chip/chip.class.d.ts +18 -0
  82. package/dist/components/chip/chip.class.d.ts.map +1 -1
  83. package/dist/components/chip/chip.class.js +38 -1
  84. package/dist/components/chip/chip.class.js.map +1 -1
  85. package/dist/components/chip/chip.styles.d.ts.map +1 -1
  86. package/dist/components/chip/chip.styles.js +19 -1
  87. package/dist/components/chip/chip.styles.js.map +1 -1
  88. package/dist/components/code-block/code-block-core.class.d.ts +111 -0
  89. package/dist/components/code-block/code-block-core.class.d.ts.map +1 -0
  90. package/dist/components/code-block/code-block-core.class.js +363 -0
  91. package/dist/components/code-block/code-block-core.class.js.map +1 -0
  92. package/dist/components/code-block/code-block-core.d.ts +3 -0
  93. package/dist/components/code-block/code-block-core.d.ts.map +1 -0
  94. package/dist/components/code-block/code-block-core.js +6 -0
  95. package/dist/components/code-block/code-block-core.js.map +1 -0
  96. package/dist/components/code-block/code-block.class.d.ts +5 -5
  97. package/dist/components/code-block/code-block.class.d.ts.map +1 -1
  98. package/dist/components/code-block/code-block.class.js +75 -3
  99. package/dist/components/code-block/code-block.class.js.map +1 -1
  100. package/dist/components/combobox/combobox.class.d.ts +5 -5
  101. package/dist/components/combobox/combobox.class.d.ts.map +1 -1
  102. package/dist/components/combobox/combobox.class.js +38 -0
  103. package/dist/components/combobox/combobox.class.js.map +1 -1
  104. package/dist/components/conversation-item/conversation-item.class.d.ts +3 -3
  105. package/dist/components/conversation-item/conversation-item.class.d.ts.map +1 -1
  106. package/dist/components/conversation-item/conversation-item.class.js +69 -0
  107. package/dist/components/conversation-item/conversation-item.class.js.map +1 -1
  108. package/dist/components/copy-button/copy-button.class.d.ts +5 -5
  109. package/dist/components/copy-button/copy-button.class.d.ts.map +1 -1
  110. package/dist/components/copy-button/copy-button.class.js +13 -0
  111. package/dist/components/copy-button/copy-button.class.js.map +1 -1
  112. package/dist/components/date-picker/date-input.class.d.ts +10 -10
  113. package/dist/components/date-picker/date-input.class.d.ts.map +1 -1
  114. package/dist/components/date-picker/date-input.class.js +28 -0
  115. package/dist/components/date-picker/date-input.class.js.map +1 -1
  116. package/dist/components/date-picker/date-picker.class.d.ts +4 -4
  117. package/dist/components/date-picker/date-picker.class.d.ts.map +1 -1
  118. package/dist/components/date-picker/date-picker.class.js +28 -0
  119. package/dist/components/date-picker/date-picker.class.js.map +1 -1
  120. package/dist/components/dialog/dialog.class.d.ts +3 -3
  121. package/dist/components/dialog/dialog.class.js +3 -3
  122. package/dist/components/dialog/dialog.styles.d.ts.map +1 -1
  123. package/dist/components/dialog/dialog.styles.js +9 -2
  124. package/dist/components/dialog/dialog.styles.js.map +1 -1
  125. package/dist/components/diff-view/diff-line-diff.d.ts +13 -0
  126. package/dist/components/diff-view/diff-line-diff.d.ts.map +1 -0
  127. package/dist/components/diff-view/diff-line-diff.js +45 -0
  128. package/dist/components/diff-view/diff-line-diff.js.map +1 -0
  129. package/dist/components/diff-view/diff-view.class.d.ts +40 -0
  130. package/dist/components/diff-view/diff-view.class.d.ts.map +1 -0
  131. package/dist/components/diff-view/diff-view.class.js +73 -0
  132. package/dist/components/diff-view/diff-view.class.js.map +1 -0
  133. package/dist/components/diff-view/diff-view.d.ts +2 -0
  134. package/dist/components/diff-view/diff-view.d.ts.map +1 -0
  135. package/dist/components/diff-view/diff-view.js +5 -0
  136. package/dist/components/diff-view/diff-view.js.map +1 -0
  137. package/dist/components/diff-view/diff-view.styles.d.ts +2 -0
  138. package/dist/components/diff-view/diff-view.styles.d.ts.map +1 -0
  139. package/dist/components/diff-view/diff-view.styles.js +49 -0
  140. package/dist/components/diff-view/diff-view.styles.js.map +1 -0
  141. package/dist/components/document-preview/document-preview.class.d.ts +9 -9
  142. package/dist/components/document-preview/document-preview.class.d.ts.map +1 -1
  143. package/dist/components/document-preview/document-preview.class.js +83 -0
  144. package/dist/components/document-preview/document-preview.class.js.map +1 -1
  145. package/dist/components/empty/empty.class.d.ts +6 -0
  146. package/dist/components/empty/empty.class.d.ts.map +1 -1
  147. package/dist/components/empty/empty.class.js +28 -2
  148. package/dist/components/empty/empty.class.js.map +1 -1
  149. package/dist/components/empty/empty.styles.js +2 -2
  150. package/dist/components/export-button/export-button.class.d.ts +8 -8
  151. package/dist/components/export-button/export-button.class.d.ts.map +1 -1
  152. package/dist/components/export-button/export-button.class.js +13 -0
  153. package/dist/components/export-button/export-button.class.js.map +1 -1
  154. package/dist/components/file-input/file-input.class.d.ts +6 -6
  155. package/dist/components/file-input/file-input.class.d.ts.map +1 -1
  156. package/dist/components/file-input/file-input.class.js +16 -2
  157. package/dist/components/file-input/file-input.class.js.map +1 -1
  158. package/dist/components/generation-status/generation-status.class.d.ts +3 -3
  159. package/dist/components/generation-status/generation-status.class.d.ts.map +1 -1
  160. package/dist/components/generation-status/generation-status.class.js +66 -0
  161. package/dist/components/generation-status/generation-status.class.js.map +1 -1
  162. package/dist/components/graph/graph.class.d.ts +14 -9
  163. package/dist/components/graph/graph.class.d.ts.map +1 -1
  164. package/dist/components/graph/graph.class.js +68 -3
  165. package/dist/components/graph/graph.class.js.map +1 -1
  166. package/dist/components/graph/graph.styles.d.ts.map +1 -1
  167. package/dist/components/graph/graph.styles.js +4 -0
  168. package/dist/components/graph/graph.styles.js.map +1 -1
  169. package/dist/components/heatmap/heatmap.class.d.ts +22 -11
  170. package/dist/components/heatmap/heatmap.class.d.ts.map +1 -1
  171. package/dist/components/heatmap/heatmap.class.js +86 -2
  172. package/dist/components/heatmap/heatmap.class.js.map +1 -1
  173. package/dist/components/json-viewer/json-viewer.class.d.ts +5 -5
  174. package/dist/components/json-viewer/json-viewer.class.d.ts.map +1 -1
  175. package/dist/components/json-viewer/json-viewer.class.js +29 -2
  176. package/dist/components/json-viewer/json-viewer.class.js.map +1 -1
  177. package/dist/components/map/map.class.d.ts +7 -7
  178. package/dist/components/map/map.class.d.ts.map +1 -1
  179. package/dist/components/map/map.class.js +27 -0
  180. package/dist/components/map/map.class.js.map +1 -1
  181. package/dist/components/markdown/markdown.class.d.ts +17 -9
  182. package/dist/components/markdown/markdown.class.d.ts.map +1 -1
  183. package/dist/components/markdown/markdown.class.js +74 -1
  184. package/dist/components/markdown/markdown.class.js.map +1 -1
  185. package/dist/components/mention-popover/mention-popover.class.d.ts +3 -3
  186. package/dist/components/mention-popover/mention-popover.class.d.ts.map +1 -1
  187. package/dist/components/mention-popover/mention-popover.class.js +113 -0
  188. package/dist/components/mention-popover/mention-popover.class.js.map +1 -1
  189. package/dist/components/menu/menu-item.class.d.ts +7 -7
  190. package/dist/components/menu/menu-item.class.d.ts.map +1 -1
  191. package/dist/components/menu/menu-item.class.js +55 -0
  192. package/dist/components/menu/menu-item.class.js.map +1 -1
  193. package/dist/components/menu/menu.class.d.ts +5 -5
  194. package/dist/components/menu/menu.class.d.ts.map +1 -1
  195. package/dist/components/menu/menu.class.js +84 -0
  196. package/dist/components/menu/menu.class.js.map +1 -1
  197. package/dist/components/model-select/model-select.class.d.ts +6 -6
  198. package/dist/components/model-select/model-select.class.d.ts.map +1 -1
  199. package/dist/components/model-select/model-select.class.js +33 -1
  200. package/dist/components/model-select/model-select.class.js.map +1 -1
  201. package/dist/components/playback/playback.class.d.ts +7 -7
  202. package/dist/components/playback/playback.class.d.ts.map +1 -1
  203. package/dist/components/playback/playback.class.js +13 -0
  204. package/dist/components/playback/playback.class.js.map +1 -1
  205. package/dist/components/poll-status/poll-status.class.d.ts +54 -0
  206. package/dist/components/poll-status/poll-status.class.d.ts.map +1 -0
  207. package/dist/components/poll-status/poll-status.class.js +150 -0
  208. package/dist/components/poll-status/poll-status.class.js.map +1 -0
  209. package/dist/components/poll-status/poll-status.d.ts +3 -0
  210. package/dist/components/poll-status/poll-status.d.ts.map +1 -0
  211. package/dist/components/poll-status/poll-status.js +6 -0
  212. package/dist/components/poll-status/poll-status.js.map +1 -0
  213. package/dist/components/poll-status/poll-status.styles.d.ts +2 -0
  214. package/dist/components/poll-status/poll-status.styles.d.ts.map +1 -0
  215. package/dist/components/poll-status/poll-status.styles.js +53 -0
  216. package/dist/components/poll-status/poll-status.styles.js.map +1 -0
  217. package/dist/components/segmented/segmented.class.d.ts +44 -0
  218. package/dist/components/segmented/segmented.class.d.ts.map +1 -0
  219. package/dist/components/segmented/segmented.class.js +100 -0
  220. package/dist/components/segmented/segmented.class.js.map +1 -0
  221. package/dist/components/segmented/segmented.d.ts +2 -0
  222. package/dist/components/segmented/segmented.d.ts.map +1 -0
  223. package/dist/components/segmented/segmented.js +5 -0
  224. package/dist/components/segmented/segmented.js.map +1 -0
  225. package/dist/components/segmented/segmented.styles.d.ts +2 -0
  226. package/dist/components/segmented/segmented.styles.d.ts.map +1 -0
  227. package/dist/components/segmented/segmented.styles.js +38 -0
  228. package/dist/components/segmented/segmented.styles.js.map +1 -0
  229. package/dist/components/select/select.class.d.ts +6 -6
  230. package/dist/components/select/select.class.d.ts.map +1 -1
  231. package/dist/components/select/select.class.js +57 -0
  232. package/dist/components/select/select.class.js.map +1 -1
  233. package/dist/components/select/select.styles.d.ts.map +1 -1
  234. package/dist/components/select/select.styles.js +6 -1
  235. package/dist/components/select/select.styles.js.map +1 -1
  236. package/dist/components/slider/slider.class.d.ts +11 -11
  237. package/dist/components/slider/slider.class.d.ts.map +1 -1
  238. package/dist/components/slider/slider.class.js +35 -0
  239. package/dist/components/slider/slider.class.js.map +1 -1
  240. package/dist/components/source-card/source-card.class.d.ts.map +1 -1
  241. package/dist/components/source-card/source-card.class.js +4 -2
  242. package/dist/components/source-card/source-card.class.js.map +1 -1
  243. package/dist/components/split/split.class.d.ts +3 -3
  244. package/dist/components/split/split.class.d.ts.map +1 -1
  245. package/dist/components/split/split.class.js +109 -3
  246. package/dist/components/split/split.class.js.map +1 -1
  247. package/dist/components/stat/stat.styles.d.ts.map +1 -1
  248. package/dist/components/stat/stat.styles.js +2 -0
  249. package/dist/components/stat/stat.styles.js.map +1 -1
  250. package/dist/components/stepper/stepper.class.d.ts +52 -0
  251. package/dist/components/stepper/stepper.class.d.ts.map +1 -0
  252. package/dist/components/stepper/stepper.class.js +138 -0
  253. package/dist/components/stepper/stepper.class.js.map +1 -0
  254. package/dist/components/stepper/stepper.d.ts +2 -0
  255. package/dist/components/stepper/stepper.d.ts.map +1 -0
  256. package/dist/components/stepper/stepper.js +5 -0
  257. package/dist/components/stepper/stepper.js.map +1 -0
  258. package/dist/components/stepper/stepper.styles.d.ts +2 -0
  259. package/dist/components/stepper/stepper.styles.d.ts.map +1 -0
  260. package/dist/components/stepper/stepper.styles.js +67 -0
  261. package/dist/components/stepper/stepper.styles.js.map +1 -0
  262. package/dist/components/stream-status/stream-status.class.d.ts +4 -4
  263. package/dist/components/stream-status/stream-status.class.d.ts.map +1 -1
  264. package/dist/components/stream-status/stream-status.class.js +79 -0
  265. package/dist/components/stream-status/stream-status.class.js.map +1 -1
  266. package/dist/components/switch/switch.class.d.ts +5 -5
  267. package/dist/components/switch/switch.class.d.ts.map +1 -1
  268. package/dist/components/switch/switch.class.js +24 -0
  269. package/dist/components/switch/switch.class.js.map +1 -1
  270. package/dist/components/table/table.class.d.ts +30 -17
  271. package/dist/components/table/table.class.d.ts.map +1 -1
  272. package/dist/components/table/table.class.js +101 -7
  273. package/dist/components/table/table.class.js.map +1 -1
  274. package/dist/components/table/table.styles.d.ts.map +1 -1
  275. package/dist/components/table/table.styles.js +23 -0
  276. package/dist/components/table/table.styles.js.map +1 -1
  277. package/dist/components/tabs/tabs.class.d.ts +5 -5
  278. package/dist/components/tabs/tabs.class.d.ts.map +1 -1
  279. package/dist/components/tabs/tabs.class.js +38 -0
  280. package/dist/components/tabs/tabs.class.js.map +1 -1
  281. package/dist/components/tabs/tabs.styles.d.ts.map +1 -1
  282. package/dist/components/tabs/tabs.styles.js +1 -0
  283. package/dist/components/tabs/tabs.styles.js.map +1 -1
  284. package/dist/components/time-range/time-range.class.d.ts +10 -10
  285. package/dist/components/time-range/time-range.class.d.ts.map +1 -1
  286. package/dist/components/time-range/time-range.class.js +28 -0
  287. package/dist/components/time-range/time-range.class.js.map +1 -1
  288. package/dist/components/toast/toast-item.class.d.ts +6 -6
  289. package/dist/components/toast/toast-item.class.d.ts.map +1 -1
  290. package/dist/components/toast/toast-item.class.js +17 -0
  291. package/dist/components/toast/toast-item.class.js.map +1 -1
  292. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.d.ts +3 -3
  293. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.d.ts.map +1 -1
  294. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.js +71 -0
  295. package/dist/components/tool-approval-dialog/tool-approval-dialog.class.js.map +1 -1
  296. package/dist/components/tool-param-form/tool-param-form.class.d.ts +9 -9
  297. package/dist/components/tool-param-form/tool-param-form.class.d.ts.map +1 -1
  298. package/dist/components/tool-param-form/tool-param-form.class.js +61 -0
  299. package/dist/components/tool-param-form/tool-param-form.class.js.map +1 -1
  300. package/dist/components/tool-result-dialog/tool-result-dialog.class.d.ts.map +1 -1
  301. package/dist/components/tool-result-dialog/tool-result-dialog.class.js +16 -4
  302. package/dist/components/tool-result-dialog/tool-result-dialog.class.js.map +1 -1
  303. package/dist/components/tool-result-view/tool-result-view.class.d.ts +6 -6
  304. package/dist/components/tool-result-view/tool-result-view.class.d.ts.map +1 -1
  305. package/dist/components/tool-result-view/tool-result-view.class.js +27 -0
  306. package/dist/components/tool-result-view/tool-result-view.class.js.map +1 -1
  307. package/dist/components/tree/tree-node.class.d.ts +9 -9
  308. package/dist/components/tree/tree-node.class.d.ts.map +1 -1
  309. package/dist/components/tree/tree-node.class.js +19 -0
  310. package/dist/components/tree/tree-node.class.js.map +1 -1
  311. package/dist/components/virtual-list/virtual-list.class.d.ts +4 -4
  312. package/dist/components/virtual-list/virtual-list.class.d.ts.map +1 -1
  313. package/dist/components/virtual-list/virtual-list.class.js +89 -0
  314. package/dist/components/virtual-list/virtual-list.class.js.map +1 -1
  315. package/dist/components/widget/widget.class.d.ts +34 -4
  316. package/dist/components/widget/widget.class.d.ts.map +1 -1
  317. package/dist/components/widget/widget.class.js +106 -3
  318. package/dist/components/widget/widget.class.js.map +1 -1
  319. package/dist/components/widget/widget.styles.d.ts.map +1 -1
  320. package/dist/components/widget/widget.styles.js +41 -1
  321. package/dist/components/widget/widget.styles.js.map +1 -1
  322. package/dist/components/word-cloud/word-cloud.class.d.ts +7 -7
  323. package/dist/components/word-cloud/word-cloud.class.d.ts.map +1 -1
  324. package/dist/components/word-cloud/word-cloud.class.js +29 -1
  325. package/dist/components/word-cloud/word-cloud.class.js.map +1 -1
  326. package/dist/internal/localization.d.ts +1 -1
  327. package/dist/internal/localization.d.ts.map +1 -1
  328. package/dist/internal/localization.js +42 -0
  329. package/dist/internal/localization.js.map +1 -1
  330. package/dist/internal/root-registration-allowlist.d.ts +1 -1
  331. package/dist/internal/root-registration-allowlist.d.ts.map +1 -1
  332. package/dist/internal/root-registration-allowlist.js +7 -0
  333. package/dist/internal/root-registration-allowlist.js.map +1 -1
  334. package/dist/lyra.d.ts +24 -0
  335. package/dist/lyra.d.ts.map +1 -1
  336. package/dist/lyra.js +15 -0
  337. package/dist/lyra.js.map +1 -1
  338. package/dist/testing/happy-dom-shims.d.ts +40 -0
  339. package/dist/testing/happy-dom-shims.d.ts.map +1 -0
  340. package/dist/testing/happy-dom-shims.js +52 -0
  341. package/dist/testing/happy-dom-shims.js.map +1 -0
  342. package/llms-full.txt +617 -35
  343. package/llms.txt +3 -1
  344. package/package.json +17 -2
package/llms-full.txt CHANGED
@@ -38,7 +38,9 @@ composition without registration; for example, `components/empty/empty.class.js`
38
38
  `lyra-thinking-panel`, `lyra-generation-status`, `lyra-code-block`, `lyra-tool-approval-dialog`,
39
39
  `lyra-tool-param-form`, `lyra-menu`/`lyra-menu-item`, `lyra-chip`/`lyra-chip-group`,
40
40
  `lyra-model-settings-panel`, `lyra-context-meter`, `lyra-dock-panel`, `lyra-document-preview`,
41
- `lyra-media-card`, `lyra-attachment-trigger`, `lyra-kbd`, `lyra-result-card`/`lyra-result-field`
41
+ `lyra-media-card`, `lyra-attachment-trigger`, `lyra-kbd`, `lyra-result-card`/`lyra-result-field`,
42
+ `lyra-avatar`, `lyra-card`, `lyra-stepper`, `lyra-segmented`, `lyra-diff-view`, `lyra-poll-status`,
43
+ `lyra-code-block-core` (needs the optional peer `shiki`)
42
44
  - **Cross-cutting notes** — packaging notes that apply across the whole library
43
45
 
44
46
  ---
@@ -150,6 +152,23 @@ Not a custom element itself — infrastructure every `lyra-*` component in the l
150
152
  that means "anchor to my trailing edge" but has to hand `place()` a physical placement. `lyra-menu`
151
153
  resolves its `placement` property through `rtlAwarePlacement()` before positioning.
152
154
 
155
+ **Testing subpath: `@aceshooting/lyra-ui/testing`** — a separate entry point (`testing/happy-dom-shims.ts`)
156
+ exporting `installHappyDomFormAssociatedShims(): void`, for a **downstream consumer's own**
157
+ Vitest+happy-dom test suite — this package's own tests run against real browsers via
158
+ `@web/test-runner`, where `ElementInternals` already exists natively, so this package never calls it
159
+ itself. happy-dom has no `ElementInternals` implementation at all, and every form-associated `lyra-*`
160
+ component (`lyra-switch`, `lyra-combobox`, `lyra-select`, `lyra-checkbox`, `lyra-model-select`,
161
+ `lyra-time-range`, `lyra-tool-param-form`, plus anything built on the shared `FormAssociated` mixin)
162
+ calls `this.attachInternals()` unconditionally in its constructor, so instantiating any of them under
163
+ happy-dom throws immediately without this shim installed first. Call
164
+ `installHappyDomFormAssociatedShims()` once, in a Vitest `setupFiles` entry, before importing any
165
+ `lyra-ui` component — it patches `HTMLElement.prototype.attachInternals` with a stub that satisfies
166
+ the shape `FormAssociated`/`LyraElement` subclasses actually call (`setFormValue()`,
167
+ `checkValidity()`, `reportValidity()`, plus `form`/`labels`/`validity`/`validationMessage`/
168
+ `willValidate` readonly fields), and is a no-op wherever `attachInternals` already exists (any real
169
+ browser, or an environment that already supports it) — safe to call unconditionally from a shared
170
+ setup file used across multiple test environments.
171
+
153
172
  **Known gotchas:**
154
173
  - `formResetCallback()`'s restore-to-constructed-default behavior (both here and in
155
174
  `lyra-combobox`'s hand-rolled copy) only captures the default from the `value` *content
@@ -362,7 +381,10 @@ secondary line, when `sub` is set), `expand-icon`, `error`, `hint`
362
381
 
363
382
  **Themeable custom properties:** `--lyra-select-trigger-padding`, `--lyra-select-trigger-min-height`,
364
383
  `--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.
384
+ `lyra-toast-item`'s `--lyra-toast-padding`/`--lyra-toast-font-size` use. `--lyra-select-trigger-height`
385
+ (default `auto`) — unset, it leaves `--lyra-select-trigger-min-height` as a floor only, unchanged; set
386
+ it to force an exact trigger height (e.g. to pixel-match a sibling text field in the same row), which
387
+ both floors and caps the trigger at that height.
366
388
 
367
389
  **Optional peer deps:** none.
368
390
 
@@ -838,7 +860,9 @@ First-party "no data" state (no Web Awesome equivalent).
838
860
 
839
861
  **Events:** none.
840
862
 
841
- **Slots:** default (icon/illustration), `actions` (buttons/links below the description)
863
+ **Slots:** default (icon/illustration), `heading` (rich heading content, overrides the `heading`
864
+ attribute), `description` (rich description content, overrides the `description` attribute),
865
+ `actions` (buttons/links below the description)
842
866
 
843
867
  **CSS parts:** `base`, `icon`, `heading`, `description`, `actions`
844
868
 
@@ -994,7 +1018,13 @@ of `rows` — this component only renders and emits intents.
994
1018
  `[part='reveal-columns-button']`; `sticky` pins that column's header cell/cells to the inline-start
995
1019
  edge (`position: sticky`) while the table scrolls horizontally — multiple `sticky` columns stack
996
1020
  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
1021
+ `--lyra-table-sticky-offset`) instead of overlapping at the same edge; `footer` renders a
1022
+ sticky-bottom footer cell for that column, computed from every currently-rendered row (post-sort,
1023
+ pre-pagination) — e.g. a column total — omit it for a column with no footer value, and a
1024
+ `[part='foot']` (`<tfoot>`) only renders at all when at least one column defines `footer`;
1025
+ `cellStyle` is applied directly to the generated `<td>` via `styleMap` — e.g. a computed heat-tint
1026
+ background a `cell()`-returned inner element can't paint into the cell's own padding — omit it for
1027
+ no per-cell style override (the default, unchanged output)
998
1028
  - `columnsHidden: boolean = false` (attribute `columns-hidden`, reflected) — computed/read-only: true
999
1029
  when a `priority` column is *actually* hidden right now by the `@container` breakpoints above, or
1000
1030
  `showAllColumns` force-visible mode is currently active. Measured via a `ResizeObserver` on
@@ -1031,7 +1061,8 @@ of `rows` — this component only renders and emits intents.
1031
1061
 
1032
1062
  **CSS parts:** `base`, `table`, `head`, `header-cell`, `row`, `cell`, `more-button`, `sort-icon` (a
1033
1063
  chevron indicator shown on the active sortable header, rotated per `sortDir`), `reveal-columns-button`
1034
- (shown only when `columnsHidden` is true)
1064
+ (shown only when `columnsHidden` is true), `foot` (the `<tfoot>`, only rendered when at least one
1065
+ column defines `footer`), `footer-row` (the single footer row), `footer-cell` (a single footer cell)
1035
1066
 
1036
1067
  **Themeable custom properties:** `--lyra-table-max-height` (default `none`; controls the scrollable
1037
1068
  body's `max-block-size`).
@@ -1360,16 +1391,32 @@ position) survives the transition.
1360
1391
  sidebar/toolbar visible during fullscreen
1361
1392
  - `compact: boolean = false` (reflected) — tighter header/body padding, same convention as
1362
1393
  `lyra-empty`'s `compact`
1394
+ - `views: WidgetView[] = []` (attribute: false) — named alternate views for the panel body, e.g. a
1395
+ chart/table toggle inside the same card chrome; `WidgetView { id: string; label: string; icon?:
1396
+ TemplateResult }`. Each entry gets a header toggle button (`[part='view-toggle']`) and a
1397
+ `<slot name="view-${id}">`. Empty (the default) renders today's single unnamed default slot as the
1398
+ sole view, unchanged.
1399
+ - `activeView: string = ''` (attribute: false) — the currently active view's `id`; defaults to the
1400
+ first entry of `views` (or `''` when `views` is empty). Settable directly to control the active
1401
+ view externally; also updated internally when a view toggle is clicked.
1363
1402
 
1364
1403
  **Events:** `lyra-collapse-change` (`detail: boolean`, the new `collapsed` state),
1365
1404
  `lyra-fullscreen-change` (`detail: boolean`, the new `fullscreen` state — also fired when fullscreen
1366
- is exited via Escape or a backdrop click, not just the toggle button)
1367
-
1368
- **Slots:** default (the panel body), `actions` (header action controls, rendered before the
1369
- collapse/expand buttons)
1370
-
1371
- **CSS parts:** `base`, `header`, `title`, `label`, `sublabel`, `actions`, `collapse-button`,
1372
- `fullscreen-button`, `body`, `backdrop`
1405
+ is exited via Escape or a backdrop click, not just the toggle button), `lyra-view-change`
1406
+ (`detail: string`, the new active view's `id` — fired when it changes via a header view-toggle click,
1407
+ not when a consumer sets `activeView` directly)
1408
+
1409
+ **Slots:** default (the panel body, rendered only while `views` is empty), `icon` (optional leading
1410
+ icon in the title row), `label` (rich label content, overrides the `label` attribute), `sublabel`
1411
+ (rich sublabel content, overrides the `sublabel` attribute), `actions` (header action controls,
1412
+ rendered before the collapse/expand buttons); each `views` entry additionally gets its own
1413
+ `view-${id}` slot instead of the default slot
1414
+
1415
+ **CSS parts:** `base`, `header`, `title`, `icon` (wrapper around the `icon` slot, hidden entirely when
1416
+ empty), `label-group` (wrapper around the label and sublabel), `label`, `sublabel`, `actions`,
1417
+ `view-toggles` (the header toggle-button group, only rendered when `views` is non-empty),
1418
+ `view-toggle` (a single view toggle button), `collapse-button`, `fullscreen-button`, `body`,
1419
+ `backdrop`
1373
1420
 
1374
1421
  **Themeable custom properties:** `--lyra-widget-overlay-color` (default `rgb(0 0 0 / 0.5)` — the
1375
1422
  fullscreen backdrop scrim color; component-specific since no shared `--wa-*`/`--lyra-*` overlay token
@@ -1707,6 +1754,13 @@ click, or Enter/Space on the focused cell, fires `lyra-cell-click`.
1707
1754
  ring (also consulted at `weekday = 7` to size the canvas height, mirroring `columnX` at
1708
1755
  `week = weekCount`). Unset (the default) is the original formula, unchanged. Ignored in matrix
1709
1756
  mode.
1757
+ - `cellColor?: (pos: MatrixCellPos | CalendarCellPos, value: number) => string | undefined`
1758
+ (attribute: false) — overrides a cell's computed ramp/no-data color entirely for an exact value;
1759
+ return a CSS color string to force that cell to it, or `undefined` to fall back to the normal ramp
1760
+ math unchanged. Lets a consumer designate a value as categorically outside the ramp (e.g. a real
1761
+ zero-count day rendered as a neutral hairline, distinct from both "no data" and the ramp's own
1762
+ lightest step) without a synthetic ramp color, which can't safely reserve an exact value on a
1763
+ skewed dataset. Unset (the default) reproduces the exact ramp/no-data behavior for every cell.
1710
1764
 
1711
1765
  **Events:** `lyra-cell-click` (fired on click, or Enter/Space on the keyboard-focused cell —
1712
1766
  `detail: { row, col, value }` in matrix mode, `detail: { date, value }` in calendar mode)
@@ -1796,8 +1850,12 @@ A force-directed node-link diagram with pan/zoom/drag, built on `d3-force`.
1796
1850
  number; color?: string }`; `radius` is clamped to `[6, 24]` (an unset/non-finite value falls back
1797
1851
  to the midpoint, `15`) so a node can never render invisibly small or absurdly large
1798
1852
  - `links: GraphLink[] = []` (attribute: false) — `GraphLink { source: string; target: string;
1799
- width?: number }` (source/target are node ids; links whose endpoints don't resolve are silently
1800
- dropped)
1853
+ width?: number }` (source/target are node ids). A link whose `source` id doesn't resolve to a real
1854
+ node is still dropped entirely (there's no position to draw a stub from). A link whose `target` id
1855
+ doesn't resolve instead renders as a short, dashed, non-interactive stub off `source`'s own
1856
+ position (`[part='link'][data-dangling]`, `aria-hidden="true"`) rather than being silently dropped
1857
+ — e.g. for a wiki-style `[[link]]` reference to a not-yet-created node. A dangling stub is excluded
1858
+ from `d3-force`'s own simulation input and from click/keyboard interaction.
1801
1859
  - `width: number = 800`
1802
1860
  - `height: number = 600`
1803
1861
  - `chargeStrength: number = -300` (attribute `charge-strength` — live-reactive, see gotchas)
@@ -2151,6 +2209,11 @@ passthrough). Not a subclass of `LyraChart`.
2151
2209
  ignores it entirely.
2152
2210
  - `hideAxis: boolean = false` (attribute `hide-axis`) — suppresses gridlines/y-axis tick labels
2153
2211
  altogether; x-axis category labels (rendered separately) are unaffected.
2212
+ - `selectedIndex: number[] = []` (attribute: false) — category indexes to mark `data-selected` on
2213
+ every bar/segment at that index, across every dataset — e.g. to highlight a whole selected week's
2214
+ column in a stacked chart. Empty (the default) reproduces the exact existing output: no bar
2215
+ carries `data-selected`. This component takes no opinion on what the highlight looks like, only
2216
+ which bars it applies to — style it via `::part(bar)[data-selected]`.
2154
2217
 
2155
2218
  **Events:** `lyra-point-click` — fired when a bar/point is activated (click, or Enter/Space while
2156
2219
  focused). `detail: { datasetIndex: number, index: number, label: string | undefined, value: number
@@ -2167,7 +2230,8 @@ too.
2167
2230
 
2168
2231
  **Slots:** none.
2169
2232
 
2170
- **CSS parts:** `base`, `grid-line`, `axis-label`, `axis-title`, `bar`, `line`, `point`, `legend`,
2233
+ **CSS parts:** `base`, `grid-line`, `axis-label`, `axis-title`, `bar` (each bar rect; carries
2234
+ `data-selected` when its category index is in `selectedIndex`), `line`, `point`, `legend`,
2171
2235
  `legend-item`, `legend-swatch`.
2172
2236
 
2173
2237
  **Themeable custom properties:** `--lyra-chart-height` (same host-level property as `lyra-chart`);
@@ -2544,8 +2608,12 @@ when no heading is slotted and `heading` is unset), `body` (wrapper around the d
2544
2608
 
2545
2609
  **Themeable custom properties:** `--lyra-dialog-overlay-color` (default `rgb(0 0 0 / 0.5)` — the
2546
2610
  backdrop scrim color; component-specific since no shared `--wa-*`/`--lyra-*` overlay token exists),
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`,
2611
+ `--lyra-dialog-width` (default unset/`auto` — the panel shrink-wraps to content, unchanged; set it
2612
+ for an assertive width instead), `--lyra-dialog-max-width` (default `32rem` the panel's
2613
+ max-inline-size cap, via `min(var(--lyra-dialog-max-width, var(--lyra-dialog-width, 32rem)), 100%)`;
2614
+ when `--lyra-dialog-width` is set but `--lyra-dialog-max-width` is left at its default, the cap falls
2615
+ back to the requested width itself — not the `32rem` default — so an assertive width isn't silently
2616
+ clipped; the viewport is still a hard limit either way), plus shared tokens `--lyra-space-l/-m/-s`,
2549
2617
  `--lyra-color-surface/-border`, `--lyra-radius`, `--lyra-shadow`.
2550
2618
 
2551
2619
  **Optional peer deps:** none.
@@ -2711,6 +2779,117 @@ single `role="tabpanel"` wrapper, one per tab, hidden unless active)
2711
2779
 
2712
2780
  ---
2713
2781
 
2782
+ ## `lyra-stepper`
2783
+
2784
+ Ordered multi-step wizard/form navigation: an index/label per step, `current`/`completed`/`disabled`/
2785
+ `error` state, and click-to-jump. First-party invention (no Web Awesome equivalent). Fully
2786
+ data-driven and controlled, like `lyra-table`'s `columns`/`rows` — it never mutates `steps` itself; a
2787
+ click, or Enter/Space on a non-disabled step, fires a cancelable `lyra-step-select`, and the host
2788
+ decides whether/how `steps` changes in response (mirroring `lyra-dialog-close`'s cancelable-event
2789
+ convention).
2790
+
2791
+ **Properties:**
2792
+ - `steps: StepItem[] = []` (attribute: false) — `StepItem { id: string; label: string; state:
2793
+ 'pending' | 'current' | 'completed' | 'disabled' | 'error' }`; never mutated by this component.
2794
+ Empty (the default) renders nothing.
2795
+ - `orientation: 'horizontal' | 'vertical' = 'horizontal'` (reflected) — `'horizontal'` (the default)
2796
+ lays steps out in a row (Left/Right, RTL-aware, navigate); `'vertical'` stacks them (Up/Down
2797
+ navigate instead, no RTL swap needed).
2798
+
2799
+ **Events:** `lyra-step-select` (`detail: { index, id }`) — fired on click, or Enter/Space while
2800
+ focused, on a non-`disabled` step. Cancelable, though this component takes no default action of its
2801
+ own to prevent (it never mutates `steps`) — `preventDefault()` is available for a host that wants a
2802
+ single place to short-circuit its own listener's follow-up work.
2803
+
2804
+ **Slots:** none.
2805
+
2806
+ **CSS parts:** `base` (root wrapper, `role="tablist"`), `step` (a single step button, `role="tab"`),
2807
+ `step-index` (the numbered index chip, shown for `pending`/`current`/`error` steps), `step-check`
2808
+ (the completed-checkmark glyph, shown for `completed` steps instead of `step-index`), `step-label`
2809
+ (the step's label text).
2810
+
2811
+ **Themeable custom properties:** shared tokens only — `--lyra-space-m`/`-xs`/`-2xs`,
2812
+ `--lyra-color-text-quiet`/`-text`/`-danger`/`-brand`/`-surface`, `--lyra-radius`/`-pill`,
2813
+ `--lyra-font-size-xs`, `--lyra-font-weight-semibold`, `--lyra-opacity-disabled`,
2814
+ `--lyra-focus-ring-*`.
2815
+
2816
+ **Optional peer deps:** none.
2817
+
2818
+ ```html
2819
+ <lyra-stepper></lyra-stepper>
2820
+ <script type="module">
2821
+ const stepper = document.querySelector('lyra-stepper');
2822
+ stepper.steps = [
2823
+ { id: 'account', label: 'Account', state: 'completed' },
2824
+ { id: 'billing', label: 'Billing', state: 'current' },
2825
+ { id: 'review', label: 'Review', state: 'pending' },
2826
+ ];
2827
+ stepper.addEventListener('lyra-step-select', (e) => console.log(e.detail.index, e.detail.id));
2828
+ </script>
2829
+ ```
2830
+
2831
+ **Known gotchas:**
2832
+ - there's no built-in "step forward/back" method — advancing the wizard is entirely the host's job:
2833
+ react to `lyra-step-select` (or its own Next/Back buttons) and reassign `steps` with updated
2834
+ `state` values.
2835
+ - `role="tablist"`/`role="tab"` back the keyboard/focus contract (roving tabindex, arrow-key
2836
+ navigation) even though this isn't a `<lyra-tabs>`-style panel switcher — there's no associated
2837
+ `role="tabpanel"`, since this component renders no panel content of its own.
2838
+ - Left/Right (horizontal) and Up/Down (vertical) are mutually exclusive per `orientation` — there's
2839
+ no single set of keys that works in both.
2840
+
2841
+ ---
2842
+
2843
+ ## `lyra-segmented`
2844
+
2845
+ A single-select button row with the WAI-ARIA APG `radiogroup` contract built in:
2846
+ `role="radiogroup"`/`role="radio"`, roving tabindex, automatic activation (click or arrow-key move
2847
+ both select immediately, like a native radio group), cyclic Arrow/Home/End navigation among
2848
+ non-disabled items. First-party invention (no Web Awesome equivalent) — "choose exactly one of N
2849
+ labeled options, rendered as a button row" is ubiquitous settings/filter-panel UI.
2850
+
2851
+ **Properties:**
2852
+ - `items: SegmentedItem[] = []` (attribute: false) — `SegmentedItem { value: string; label: string;
2853
+ disabled?: boolean }`.
2854
+ - `value: string = ''` — the currently selected item's `value`.
2855
+
2856
+ **Events:** `lyra-change` (`detail: { value }`) — fired when the selected value changes via click or
2857
+ keyboard.
2858
+
2859
+ **Slots:** none.
2860
+
2861
+ **CSS parts:** `base` (the `role="radiogroup"` root), `segment` (a single `role="radio"` button),
2862
+ `segment-label` (the segment's label text).
2863
+
2864
+ **Themeable custom properties:** shared tokens only — `--lyra-color-border`/`-surface`/`-text`/
2865
+ `-text-quiet`, `--lyra-radius`, `--lyra-font-size-sm`, `--lyra-font-weight-semibold`,
2866
+ `--lyra-space-s`, `--lyra-shadow`, `--lyra-opacity-disabled`, `--lyra-focus-ring-*`.
2867
+
2868
+ **Optional peer deps:** none.
2869
+
2870
+ ```html
2871
+ <lyra-segmented></lyra-segmented>
2872
+ <script type="module">
2873
+ const seg = document.querySelector('lyra-segmented');
2874
+ seg.items = [
2875
+ { value: 'day', label: 'Day' },
2876
+ { value: 'week', label: 'Week' },
2877
+ { value: 'month', label: 'Month' },
2878
+ ];
2879
+ seg.value = 'week';
2880
+ seg.addEventListener('lyra-change', (e) => console.log(e.detail.value));
2881
+ </script>
2882
+ ```
2883
+
2884
+ **Known gotchas:**
2885
+ - arrow-key navigation cycles (past the last non-disabled item wraps to the first, and vice versa)
2886
+ rather than clamping at the first/last item, unlike `lyra-stepper`'s clamped Left/Right.
2887
+ - this component self-selects on navigation: clicking or arrow-navigating to an item immediately
2888
+ updates `value` and fires `lyra-change` — there's no separate "commit" step the way, e.g.,
2889
+ `lyra-select`'s popup has.
2890
+
2891
+ ---
2892
+
2714
2893
  ## `lyra-checkbox`
2715
2894
 
2716
2895
  A boolean form control. `role="checkbox"` with an `aria-checked` that can also be `"mixed"`, and a
@@ -3016,6 +3195,13 @@ produced HTML in a single pass (no second DOM walk after insertion).
3016
3195
  **Properties:**
3017
3196
  - `content: string = ''` — the Markdown source to render
3018
3197
  - `sanitize: boolean = true` — sanitize `marked`'s HTML output with DOMPurify before rendering
3198
+ - `escapeHtml: boolean = false` (attribute `escape-html`) — when `true`, overrides `marked`'s `html`
3199
+ renderer hook to emit the HTML-escaped source text instead of passing raw/sanitized markup through
3200
+ — for rendering arbitrary already-written content (e.g. a historical chat/agent transcript full of
3201
+ code/XML/HTML snippets) where a stray angle bracket should render as visible text, not a real DOM
3202
+ element. GFM tables/lists/etc. still render normally — only raw embedded HTML is affected. `false`
3203
+ (the default) reproduces the exact `marked`-default (sanitized-when-`sanitize`) passthrough
3204
+ behavior.
3019
3205
  - `gfm: boolean = true` — GitHub-flavored Markdown (tables, strikethrough, autolinks, task lists)
3020
3206
  - `linkTarget: string | null = '_blank'` (attribute `link-target`) — `target` applied to every
3021
3207
  rendered `<a>`, with `rel="noopener noreferrer"` always added alongside it whenever a `target` is
@@ -3712,6 +3898,13 @@ wins when both are present.
3712
3898
  - `progress: number = 0` — upload completion, 0-100; only meaningful while `status="uploading"`, a
3713
3899
  value of `0` or `NaN` falls back to the indeterminate spinner
3714
3900
  - `removable: boolean = true` (reflected) — shows the remove (×) button
3901
+ - `compact: boolean = false` (reflected) — renders a smaller, borderless pill presentation instead of
3902
+ the default bordered/chrome-heavy chip, e.g. for a composer's pending-attachment tray. `false` (the
3903
+ default) is visually identical to the standard chip.
3904
+ - `thumbnailOnly: boolean = false` (reflected, attribute `thumbnail-only`) — when both this and
3905
+ `compact` are set, hides `[part='meta']` (the filename/size text) entirely for an image-mime
3906
+ attachment, leaving only the thumbnail. Has no effect for a non-image chip, or when `compact` is
3907
+ unset. `false` (the default) reproduces the chip's exact existing output.
3715
3908
  - `removeLabel: string = 'Remove'` (attribute `remove-label`) — verb used in the remove button's
3716
3909
  `aria-label`, interpolated as `` `${removeLabel} ${displayName}` ``; override for i18n/locale
3717
3910
  - `retryLabel: string = 'Retry'` (attribute `retry-label`) — verb used in the retry button's
@@ -3740,7 +3933,10 @@ generated internal id is used as a last resort.
3740
3933
  `var(--lyra-color-text-quiet)`), `--lyra-attachment-chip-bg` (default `var(--lyra-color-surface)`),
3741
3934
  `--lyra-attachment-chip-border` (default `var(--lyra-color-border)`) — this trio is swapped per
3742
3935
  `status` (`uploading` → brand/brand-quiet/transparent, `error` → danger/danger-quiet/transparent,
3743
- `done` → success/success-quiet/transparent); plus shared tokens `--lyra-space-xs`, `--lyra-space-s`,
3936
+ `done` → success/success-quiet/transparent); `--lyra-attachment-chip-compact-thumbnail-size` (default
3937
+ `1.75rem`), `--lyra-attachment-chip-compact-font-size` (default `var(--lyra-font-size-xs)`),
3938
+ `--lyra-attachment-chip-compact-gap` (default `0.25rem`) — govern the chip's thumbnail size, text
3939
+ size, and internal gap while `compact` is set; plus shared tokens `--lyra-space-xs`, `--lyra-space-s`,
3744
3940
  `--lyra-radius`, `--lyra-color-text`, `--lyra-color-danger`, `--lyra-icon-button-size`,
3745
3941
  `--lyra-transition-fast`, `--lyra-transition-base`, `--lyra-focus-ring-width`,
3746
3942
  `--lyra-focus-ring-color`, `--lyra-focus-ring-offset`.
@@ -3905,6 +4101,66 @@ instead. The pulse animation is suppressed under `prefers-reduced-motion: reduce
3905
4101
 
3906
4102
  ---
3907
4103
 
4104
+ ## `lyra-poll-status`
4105
+
4106
+ A "next scheduled refresh" countdown with a built-in pause control: a ticking `M:SS` display counting
4107
+ down to the next scheduled action, a "Refreshing…" state at zero, and a pause/resume toggle.
4108
+ First-party invention (no Web Awesome equivalent); the closest existing component,
4109
+ `<lyra-stream-status>`, is scoped to transport/connection-health phases, a different concern from a
4110
+ scheduled-interval countdown — this mirrors its internal `<lyra-live-region>` composition for
4111
+ accessible phase-transition announcements.
4112
+
4113
+ **Properties:**
4114
+ - `nextInMs?: number` (attribute `next-in-ms`) — milliseconds until the next scheduled action, as of
4115
+ whenever this was last set; setting it (re)starts the countdown from "now." Unset (the default)
4116
+ shows no countdown.
4117
+ - `active: boolean = true` (reflected) — whether the poll cycle is running at all.
4118
+ - `paused: boolean = false` (reflected) — user-toggled pause; while `true`, the countdown display
4119
+ freezes and `lyra-poll-due` never fires.
4120
+
4121
+ **Events:** `lyra-poll-due` (no detail — fired once when the countdown reaches zero, not fired while
4122
+ `paused`), `lyra-pause-change` (`detail: boolean` — fired when `paused` changes via the built-in
4123
+ button).
4124
+
4125
+ **Slots:** none.
4126
+
4127
+ **CSS parts:** `base`, `indicator` (the pulsing status dot), `countdown` (the `M:SS`, or
4128
+ "Refreshing…", text), `pause-button` (the built-in pause/resume toggle).
4129
+
4130
+ **Themeable custom properties:** shared tokens only — `--lyra-space-xs`, `--lyra-font-size-sm`,
4131
+ `--lyra-color-text-quiet`, `--lyra-color-brand`, `--lyra-color-success`, `--lyra-radius`/`-pill`,
4132
+ `--lyra-focus-ring-*`.
4133
+
4134
+ **Optional peer deps:** none.
4135
+
4136
+ ```html
4137
+ <lyra-poll-status next-in-ms="30000"></lyra-poll-status>
4138
+ <script type="module">
4139
+ const status = document.querySelector('lyra-poll-status');
4140
+ status.addEventListener('lyra-poll-due', () => refreshData());
4141
+ status.addEventListener('lyra-pause-change', (e) => console.log('paused:', e.detail));
4142
+ </script>
4143
+ ```
4144
+
4145
+ Internally, a 1-second ticker re-derives the remaining time from a captured target timestamp (rather
4146
+ than a naive per-tick decrement), so the countdown stays accurate even if the tab was backgrounded
4147
+ and timers were throttled. Reassigning `nextInMs` at any time restarts the countdown from that new
4148
+ value; pausing/resuming, or toggling `active`, starts or stops the ticker without discarding the
4149
+ current remaining time. Phase transitions ("Paused.", "Resumed.", "Refreshing now.") are announced
4150
+ via an internal `<lyra-live-region>` in polite mode.
4151
+
4152
+ **Known gotchas:**
4153
+ - there's no built-in "reset" or "extend" method beyond reassigning `nextInMs` — a host that wants to
4154
+ push the deadline back out on user activity re-sets `nextInMs` itself.
4155
+ - `active="false"` and `paused` both stop the ticker independently, but only `paused` fires
4156
+ `lyra-pause-change` — that event is scoped to the built-in pause button's own toggle, not to
4157
+ `active`.
4158
+ - the countdown rounds up to the nearest whole second, so it never shows a literal "0:00" — the
4159
+ display jumps straight from a small count (e.g. "0:01") to "Refreshing…" once the deadline is
4160
+ actually reached.
4161
+
4162
+ ---
4163
+
3908
4164
  ## `lyra-virtual-list`
3909
4165
 
3910
4166
  A generic windowed/virtualized list host. Renders only the items within the current viewport (plus
@@ -4613,6 +4869,15 @@ presentation) and `[part="panel"]` (the mobile overlay) are the *same* element p
4613
4869
  across modes (mirrors `<lyra-widget>`'s fullscreen mode) — never both at once, and slotted nav
4614
4870
  content is never duplicated.
4615
4871
 
4872
+ Opting in to `resizable` adds a continuously draggable width for the `'full'` state: a
4873
+ `[part="resizer"]` handle (pointer-drag and Left/Right-arrow keyboard stepping, RTL-aware) clamped to
4874
+ `[minRailWidthPx, maxRailWidthPx]`, with no built-in persistence — a consumer that wants the chosen
4875
+ width to survive a reload listens for `lyra-rail-resize` and persists `railWidthPx` itself.
4876
+ `preferredMode` separately lets a host manually prefer `'full'`/`'icon-only'` for the non-mobile
4877
+ breakpoint axis (e.g. a user's own collapse toggle) while `mobile-breakpoint` continues to be tracked
4878
+ automatically regardless — it's only consulted while `mode` isn't force-pinned via the `mode`
4879
+ accessor itself, which still takes full priority.
4880
+
4616
4881
  **Properties:**
4617
4882
  - `mode: AppRailMode` (custom accessor, reflected) — the getter always returns one of the three real
4618
4883
  modes (`'full'|'icon-only'|'mobile'`), never `'auto'`. The setter accepts
@@ -4633,21 +4898,52 @@ content is never duplicated.
4633
4898
  toggle button — there is no separate `show()`/`hide()` pair.
4634
4899
  - `label: string = 'Navigation'` — accessible name for the rail's navigation landmark, and for its
4635
4900
  dialog role while the mobile overlay is open.
4901
+ - `preferredMode?: 'full' | 'icon-only' | null` (attribute `preferred-mode`) — manually prefers
4902
+ `'full'` or `'icon-only'` for the non-mobile breakpoint axis, while `mobile-breakpoint` continues to
4903
+ be tracked automatically regardless — e.g. a user's manual collapse toggle that should still yield
4904
+ to a genuinely too-narrow-for-any-inline-rail viewport. Only consulted while `mode` isn't
4905
+ force-pinned via its own accessor (see above) — that continues to take full priority, unchanged.
4906
+ Unset (the default, `null`) reproduces the original breakpoint-only behavior exactly.
4907
+ - `hideToggle: boolean = false` (reflected, attribute `hide-toggle`) — suppresses the built-in mobile
4908
+ `[part='toggle']` hamburger/close button entirely, for a consumer that already owns an external
4909
+ mobile-menu toggle wired to this rail's own `open` property. `false` (the default) reproduces the
4910
+ exact existing output; note `open` still has no built-in external trigger of its own once this is
4911
+ set, since `lyra-toggle` only fires from the toggle button being activated.
4912
+ - `resizable: boolean = false` (reflected) — opts a continuously draggable width in for the `'full'`
4913
+ state, exposing a `[part='resizer']` handle clamped to `[minRailWidthPx, maxRailWidthPx]`. `false`
4914
+ (the default) renders no resizer and leaves the fixed-width `--lyra-app-rail-width` CSS token
4915
+ exactly as before this property existed.
4916
+ - `railWidthPx?: number` (attribute `rail-width-px`) — the rail's current width in px while
4917
+ `resizable`; settable/gettable directly. Unset defers to `--lyra-app-rail-width`'s own resolved
4918
+ width.
4919
+ - `minRailWidthPx: number = 190` (attribute `min-rail-width-px`) — minimum `railWidthPx` a
4920
+ drag/keyboard resize can reach.
4921
+ - `maxRailWidthPx: number = 440` (attribute `max-rail-width-px`) — maximum `railWidthPx` a
4922
+ drag/keyboard resize can reach.
4923
+ - `dragging: boolean = false` (reflected) — `true` for the duration of an active pointer-driven
4924
+ resize drag (not a keyboard step); reflected so a consumer (or this component's own styles) can
4925
+ suppress `[part='base']`'s `transition: inline-size` during the drag, which otherwise visibly
4926
+ "chases" the pointer instead of tracking it 1:1. Effectively read-only (this component owns the
4927
+ transitions), but a plain reflected property like every other boolean here.
4636
4928
 
4637
4929
  **Events:** `lyra-mode-change` (`detail: AppRailModeChangeDetail` = `{ mode: AppRailMode }`; the
4638
4930
  effective mode changed, whether from a breakpoint crossing or an explicit `mode` assignment — not
4639
4931
  fired for a redundant reassignment to the mode already in effect), `lyra-toggle`
4640
4932
  (`detail: AppRailToggleDetail` = `{ open: boolean }`; the mobile overlay opened or closed — via the
4641
4933
  built-in toggle button, Escape, a backdrop click, a nav-item click while open, or a breakpoint/forced
4642
- mode change leaving `'mobile'` while open — not fired when a consumer sets `open` directly).
4934
+ mode change leaving `'mobile'` while open — not fired when a consumer sets `open` directly),
4935
+ `lyra-rail-resize` (`detail: AppRailResizeDetail` = `{ widthPx: number }`; the `resizable` rail's
4936
+ width changed via drag or keyboard stepping — not fired when a consumer sets `railWidthPx` directly).
4643
4937
 
4644
4938
  **Slots:** default (nav items — generic slotted content, e.g. `<a>`/`<button>` elements the consumer
4645
4939
  builds with its own icon+label structure; clicking anywhere in this slot closes the mobile overlay if
4646
4940
  open), `header` (logo/brand content, shown above the nav items in every mode), `footer` (a trailing
4647
4941
  user/settings trigger, shown below the nav items).
4648
4942
 
4649
- **CSS parts:** `base`, `header`, `nav`, `footer`, `toggle`, `backdrop`, `panel` (`base`/`panel` are
4650
- mutually exclusive on the same underlying element — see above).
4943
+ **CSS parts:** `base`, `header`, `nav`, `footer`, `toggle` (hidden via CSS outside `'mobile'` mode, or
4944
+ entirely via `hideToggle`), `backdrop`, `panel` (`base`/`panel` are mutually exclusive on the same
4945
+ underlying element — see above), `resizer` (the `resizable` opt-in's drag handle, only rendered while
4946
+ `resizable` and `mode` is `'full'`).
4651
4947
 
4652
4948
  **Themeable custom properties:** `--lyra-app-rail-width` (default `15rem` — the inline rail width in
4653
4949
  `'full'` mode), `--lyra-app-rail-icon-width` (default `4rem` — the inline rail width in `'icon-only'`
@@ -4656,17 +4952,22 @@ width), `--lyra-app-rail-overlay-color` (default `rgb(0 0 0 / 0.5)` — the mobi
4656
4952
  color; component-specific since no shared token exists), plus shared tokens (`--lyra-color-border`,
4657
4953
  `--lyra-color-surface`, `--lyra-color-text`, `--lyra-color-brand`, `--lyra-color-brand-quiet`,
4658
4954
  `--lyra-space-*`, `--lyra-radius`, `--lyra-shadow`, `--lyra-icon-button-size`,
4659
- `--lyra-focus-ring-*`, `--lyra-transition-base`).
4955
+ `--lyra-focus-ring-*`, `--lyra-transition-base`). `resizable`'s width is driven entirely by
4956
+ `railWidthPx`'s inline `inline-size` style rather than a new custom property.
4660
4957
 
4661
4958
  **Optional peer deps:** none.
4662
4959
 
4663
4960
  ```html
4664
- <lyra-app-rail label="Main navigation" icon-only-breakpoint="960px" mobile-breakpoint="600px">
4961
+ <lyra-app-rail label="Main navigation" icon-only-breakpoint="960px" mobile-breakpoint="600px" resizable>
4665
4962
  <span slot="header"><img src="/logo.svg" alt="Acme" /></span>
4666
4963
  <a href="/inbox" aria-label="Inbox"><svg aria-hidden="true">...</svg><span>Inbox</span></a>
4667
4964
  <a href="/settings" aria-label="Settings"><svg aria-hidden="true">...</svg><span>Settings</span></a>
4668
4965
  <span slot="footer"><button>Profile</button></span>
4669
4966
  </lyra-app-rail>
4967
+ <script type="module">
4968
+ const rail = document.querySelector('lyra-app-rail');
4969
+ rail.addEventListener('lyra-rail-resize', (e) => localStorage.setItem('railWidthPx', String(e.detail.widthPx)));
4970
+ </script>
4670
4971
  ```
4671
4972
  ```ts
4672
4973
  rail.mode = 'icon-only'; // force a presentation regardless of viewport width
@@ -4674,11 +4975,12 @@ rail.mode = 'auto'; // release the force, resume live breakpoint tracking
4674
4975
  ```
4675
4976
 
4676
4977
  The package root also exports a pure `computeAppRailMode(iconOnlyMatches: boolean, mobileMatches:
4677
- boolean): AppRailMode` resolver (plus the `AppRailMode`/`AppRailModeInput`/`AppRailModeChangeDetail`/
4678
- `AppRailToggleDetail` types) — the same logic the element's internal `matchMedia` listeners call,
4679
- exposed standalone so a consumer can compute or unit-test the same three-state resolution without a
4680
- real browser window. `mobileMatches` wins over `iconOnlyMatches` when both are true (the viewport is
4681
- narrower than both breakpoints at once).
4978
+ boolean, preferredMode?: 'full' | 'icon-only' | null): AppRailMode` resolver (plus the
4979
+ `AppRailMode`/`AppRailModeInput`/`AppRailModeChangeDetail`/`AppRailToggleDetail`/`AppRailResizeDetail`
4980
+ types) the same logic the element's internal `matchMedia` listeners call, exposed standalone so a
4981
+ consumer can compute or unit-test the same resolution without a real browser window. `mobileMatches`
4982
+ wins over everything else when true (the viewport is narrower than both breakpoints at once);
4983
+ otherwise `preferredMode` (when set) wins over `iconOnlyMatches`.
4682
4984
 
4683
4985
  The mobile state keeps its own panel template rather than nesting `<lyra-dialog>`, while its modal
4684
4986
  behavior participates in the shared overlay stack. It is a plain `<div>` with an explicit
@@ -4713,6 +5015,15 @@ component only lays out whatever is slotted and can't inspect or fix up a consum
4713
5015
  shared overlay registration and re-acquires the scroll lock if the overlay was still active
4714
5016
  across the move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in
4715
5017
  between, so `willUpdate()` alone wouldn't otherwise notice.
5018
+ - `resizable`'s drag handle is pointer-only (`pointerdown`/`pointermove`/`pointerup`/
5019
+ `pointercancel`/`lostpointercapture`) plus discrete Left/Right-arrow keyboard stepping (8px per
5020
+ press, RTL-aware) — there's no dedicated touch gesture beyond what Pointer Events already unify.
5021
+ - reassigning `railWidthPx` while `resizable` is unset has no visible effect on the rendered width —
5022
+ the fixed-width `--lyra-app-rail-width` token still governs `'full'`-mode width until `resizable`
5023
+ is also set.
5024
+ - reassigning `icon-only-breakpoint`/`mobile-breakpoint`/`preferredMode` does not itself un-force a
5025
+ previously-forced `mode` — same caveat as above, `preferredMode` is only consulted while `mode`
5026
+ isn't force-pinned.
4716
5027
 
4717
5028
  ### `lyra-app-rail-item`
4718
5029
 
@@ -4720,13 +5031,25 @@ An explicit navigation item for `<lyra-app-rail>`. It renders an accessible link
4720
5031
  set and enabled, otherwise a button; the rail can add its `icon-only` presentation state without
4721
5032
  removing the label from the accessibility tree.
4722
5033
 
4723
- **Properties:** `href: string = ''`, `target: string = ''`, and `disabled: boolean = false`
4724
- (reflected). A disabled item remains visible but cannot be activated.
5034
+ **Properties:**
5035
+ - `href: string = ''` — optional destination. Without it, the item renders as a button.
5036
+ - `target: string = ''` — optional link target.
5037
+ - `disabled: boolean = false` (reflected) — prevents activation while retaining the item in the rail.
5038
+ - `active: boolean = false` (reflected) — marks this as the destination for the current page/view;
5039
+ reflects `aria-current="page"` on `[part='base']` and drives the active visual treatment. The rail
5040
+ has no built-in routing, so the consumer sets this per item (e.g. by comparing `href` against the
5041
+ current location).
5042
+ - `tooltip: boolean = false` (reflected) — opt-in hover/focus flyout (`[part='tooltip']`) showing
5043
+ this item's label text while the rail's `icon-only` mode (set externally by the parent
5044
+ `<lyra-app-rail>` as the viewport narrows) hides it from view. No effect outside icon-only mode,
5045
+ since the label is already visible there. `false` (the default) reproduces the exact existing
5046
+ output.
4725
5047
 
4726
5048
  **Slots:** default (the visible label), `icon` (the leading icon, hidden from assistive technology
4727
5049
  when the item has an explicit `aria-label`).
4728
5050
 
4729
- **CSS parts:** `base`, `icon`, `label`.
5051
+ **CSS parts:** `base`, `icon`, `label`, `tooltip` (the hover/focus label flyout, only rendered while
5052
+ `tooltip` is set, the item is `icon-only`, and it is hovered or focused).
4730
5053
 
4731
5054
  **Optional peer deps:** none.
4732
5055
 
@@ -5320,6 +5643,141 @@ theme data.
5320
5643
 
5321
5644
  ---
5322
5645
 
5646
+ ## `lyra-code-block-core`
5647
+
5648
+ A build-lean sibling of `<lyra-code-block>` above, for a consumer whose `languages` map already
5649
+ covers every language it will ever render. Where `<lyra-code-block>` unconditionally calls
5650
+ `loadShikiHighlighter()` — the default ~200-language dynamic-import table loader, whose bundled
5651
+ lookup table a bundler can't statically narrow away even when a consumer never actually uses it —
5652
+ this component's own module never imports or calls that function at all. It only ever calls
5653
+ `loadShikiHighlighterCore(languages)` (shiki's "fine-grained bundle" recipe: `createHighlighterCore()`
5654
+ plus an explicit oniguruma engine, seeded with *only* the grammars in `languages`), so a consumer
5655
+ importing this entry point instead of `code-block.js` gets a build genuinely free of shiki's full
5656
+ language table.
5657
+
5658
+ A `language` value absent from `languages` always renders the plain `<pre><code>` fallback — there is
5659
+ no default/full-table highlighter here to fall back to, unlike `<lyra-code-block>`'s dynamic-import
5660
+ path for an unmapped language. That fallback is the *default* rendering path, not a degraded one,
5661
+ same as `<lyra-code-block>`'s own plain-text fallback. Everything else — `code`/`language`/
5662
+ `filename`/`copyable`/`collapsible`/`collapsed`/`maxHeight`, the copy button, the collapse header
5663
+ toggle, the loading-skeleton behavior while the fine-grained highlighter resolves — matches
5664
+ `<lyra-code-block>` exactly, including its CSS parts, themeable custom properties, and stylesheet
5665
+ (this component reuses `code-block.styles.ts` directly).
5666
+
5667
+ **Properties:**
5668
+ - `code: string = ''` — the raw source text.
5669
+ - `language: string = ''` — a shiki-recognized language id or alias; when unset, or when it isn't a
5670
+ key in `languages`, the code renders as plain unhighlighted text — this component has no
5671
+ default/full-table highlighter to fall back to.
5672
+ - `filename: string = ''` — shown in the header, when set.
5673
+ - `collapsible: boolean = false` (reflected) — shows the collapse/expand chevron button.
5674
+ - `collapsed: boolean = false` (reflected) — only has a visible effect while `collapsible` is also
5675
+ true.
5676
+ - `copyable: boolean = true` (reflected) — shows the copy-to-clipboard button.
5677
+ - `maxHeight: string = ''` (attribute `max-height`) — a CSS length (e.g. `"20rem"`); once set, the
5678
+ code scrolls internally past this height instead of growing the page.
5679
+ - `languages: Record<string, ShikiLanguageInput> = {}` (attribute: false) — grammar definitions this
5680
+ instance can highlight, e.g. `{ json: jsonGrammar }` (import from `shiki/langs/<name>.mjs`). Empty
5681
+ (the default) never highlights at all — every `language` renders the plain-text fallback.
5682
+
5683
+ **Events:** `lyra-copy` (`detail: { text: string }` — always the raw `code` value, never the
5684
+ highlighted HTML, and always fires regardless of whether the actual OS clipboard write succeeded),
5685
+ `lyra-toggle` (`detail: { collapsed: boolean }` — fired when the built-in collapse/expand header
5686
+ button is activated).
5687
+
5688
+ **Slots:** none.
5689
+
5690
+ **CSS parts:** `base`, `header`, `filename`, `language`, `copy-button`, `toggle`, `body`, `pre`,
5691
+ `code` — identical set to `<lyra-code-block>`.
5692
+
5693
+ **Themeable custom properties:** identical to `<lyra-code-block>` — `--lyra-code-block-max-height`,
5694
+ `--lyra-code-block-font`, plus the same shared tokens.
5695
+
5696
+ **Optional peer deps:** `shiki` (specifically its `shiki/core`, `shiki/engine/oniguruma`,
5697
+ `shiki/wasm`, and `shiki/themes/github-{light,dark}.mjs` subpaths — never `shiki`'s main entry point,
5698
+ which is what carries the ~200-language table this component exists to avoid). Building the
5699
+ fine-grained highlighter is cached per `languages` object identity (a `WeakMap`), so passing the same
5700
+ module-level `languages` constant on every render builds it only once.
5701
+
5702
+ ```html
5703
+ <script type="module">
5704
+ import jsonGrammar from 'shiki/langs/json.mjs';
5705
+ </script>
5706
+ <lyra-code-block-core
5707
+ language="json"
5708
+ .languages=${{ json: jsonGrammar }}
5709
+ .code=${'{"ok": true}'}
5710
+ ></lyra-code-block-core>
5711
+ ```
5712
+
5713
+ **Known gotchas:**
5714
+ - there is no default highlighter and no dynamic-import fallback table — a `language` you haven't
5715
+ added to `languages` will never highlight, no matter how common that language is elsewhere. Reach
5716
+ for `<lyra-code-block>` instead if you need to support an open-ended set of languages without
5717
+ pre-declaring each one.
5718
+ - `languages` is keyed by object identity for caching purposes — pass a stable, module-level constant
5719
+ (not a fresh object literal per render), or every render rebuilds its own fine-grained highlighter.
5720
+
5721
+ ---
5722
+
5723
+ ## `lyra-diff-view`
5724
+
5725
+ A real two-string line diff (a classic longest-common-subsequence dynamic program, not a Myers
5726
+ implementation), rendered as interleaved unified-diff output — not diff-flavored syntax highlighting
5727
+ over an already-formatted string (`<lyra-code-block>`'s `language="diff"` only lexically colors a
5728
+ string the consumer already unified-diffed; it has no two-string-compare entry point of its own).
5729
+ First-party invention (no Web Awesome equivalent).
5730
+
5731
+ **Properties:**
5732
+ - `oldText: string = ''` (attribute: false) — the "before" text. Default `''` renders an
5733
+ all-additions diff of `newText`.
5734
+ - `newText: string = ''` (attribute: false) — the "after" text. Default `''` renders an
5735
+ all-removals diff of `oldText`.
5736
+ - `copyable: boolean = false` — shows a copy-to-clipboard button for the full unified-diff text.
5737
+ `false` (the default) renders no button.
5738
+
5739
+ **Events:** `lyra-copy` (`detail: { text: string }` — the full unified-diff text, fired on
5740
+ copy-button activation regardless of whether the clipboard write actually succeeded).
5741
+
5742
+ **Slots:** none.
5743
+
5744
+ **CSS parts:** `base` (the root wrapper), `line` (a single line; carries
5745
+ `data-type="equal"|"add"|"remove"`), `copy-button` (the copy affordance, only rendered while
5746
+ `copyable`).
5747
+
5748
+ **Themeable custom properties:** `--lyra-diff-view-font` (default `var(--lyra-font-mono)`), plus
5749
+ shared tokens `--lyra-color-border`/`-surface`/`-success`/`-success-quiet`/`-danger`/
5750
+ `-danger-quiet`/`-text`, `--lyra-radius`, `--lyra-space-xs`/`-s`, `--lyra-font-size-sm`,
5751
+ `--lyra-line-height-snug`, `--lyra-focus-ring-*`.
5752
+
5753
+ **Optional peer deps:** none.
5754
+
5755
+ ```html
5756
+ <lyra-diff-view copyable></lyra-diff-view>
5757
+ <script type="module">
5758
+ const diff = document.querySelector('lyra-diff-view');
5759
+ diff.oldText = 'line one\nline two\nline three';
5760
+ diff.newText = 'line one\nline TWO\nline three\nline four';
5761
+ diff.addEventListener('lyra-copy', (e) => console.log(e.detail.text));
5762
+ </script>
5763
+ ```
5764
+
5765
+ The package root also exports the pure `computeLineDiff(oldLines: string[], newLines: string[]):
5766
+ DiffOp[]` helper (plus the `DiffOp` type, `{ type: 'equal' | 'add' | 'remove'; text: string }`) — the
5767
+ same line-diff function this component's own `render()`/copy handler call, exposed standalone so a
5768
+ consumer can compute or unit-test the same alignment without instantiating the element at all.
5769
+
5770
+ **Known gotchas:**
5771
+ - the alignment is a genuine O(n·m) longest-common-subsequence dynamic program over lines (split on
5772
+ `\n`), not a naive "every removed line then every added line" — fine for a typical
5773
+ tool-call/transcript-sized diff, but a very large pair of texts costs quadratic time/memory;
5774
+ there's no line-count guard or chunking of its own.
5775
+ - `oldText`/`newText` are recomputed into the diff on every render — this component keeps no separate
5776
+ cached diff state, so binding either property to something that changes on every keystroke
5777
+ recomputes the whole alignment each time.
5778
+
5779
+ ---
5780
+
5323
5781
  ## `lyra-tool-approval-dialog`
5324
5782
 
5325
5783
  A human-in-the-loop gate: presents one proposed tool/function call (`toolName` + `args`) and blocks an
@@ -5672,8 +6130,11 @@ opens the menu, focusing the first/last non-disabled item respectively (mirrors
5672
6130
  Once open, ArrowDown/ArrowUp move the roving focus among non-disabled items and wrap past either end
5673
6131
  (unlike `<lyra-select>`'s clamped listbox nav); Home/End jump to the first/last non-disabled item;
5674
6132
  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
6133
+ `preventDefault()`, letting the browser's own Tab navigation proceed untouched. All of this list-level
6134
+ keydown handling Escape, Tab, the arrow keys, Home/End, and type-ahead alike only responds when
6135
+ the triggering keydown event's own target is an actual `<lyra-menu-item>` element; a keydown bubbling
6136
+ up from any other node inside the popup is ignored rather than misread as list navigation. A printable
6137
+ keypress runs type-ahead: roving focus jumps to the next non-disabled item whose text starts with the
5677
6138
  accumulated buffer (cycling from just after the active item, buffer resets ~500ms after the last
5678
6139
  keystroke) — mirrors `<lyra-select>`'s identical listbox type-ahead. A click outside both the trigger
5679
6140
  and the open popup also closes it, but deliberately does *not* refocus the trigger — the outside
@@ -5712,12 +6173,23 @@ already follow.
5712
6173
  whole pill using a loud-color-on-quiet-tint convention; `neutral` has no dedicated token pair, so
5713
6174
  it falls back to a plain bordered-surface look)
5714
6175
  - `removable: boolean = false` (reflected — shows the remove (×) button)
6176
+ - `selected: boolean = false` (reflected) — opt-in toggle/pressed mode: when set, `[part='base']`
6177
+ itself becomes focusable and keyboard-activatable (Enter/Space, mirroring native `<button>`
6178
+ behavior), reflects `aria-pressed`, and toggles on click/activation, emitting `lyra-chip-select`.
6179
+ Has no effect (no interactive semantics added to `[part='base']`) when combined with `removable`,
6180
+ since the remove button already nests inside `[part='base']` — a focusable descendant of a
6181
+ `role="button"` ancestor isn't allowed there, and this component's two real use cases (a
6182
+ chart-series visibility toggle, a category filter chip) never need both at once. `false` (the
6183
+ default) reproduces the exact passive-label-pill output.
5715
6184
  - `value?: string` — opaque consumer bookkeeping value, never read, validated, or rendered by this
5716
6185
  component itself, only ever echoed back verbatim (including `undefined` if never set) in
5717
6186
  `lyra-remove`'s detail
5718
6187
 
5719
6188
  **Events:** `lyra-remove` (`detail: { value }` — the remove (×) button was activated via click or
5720
- Enter/Space while focused; only rendered/reachable while `removable`)
6189
+ Enter/Space while focused; only rendered/reachable while `removable`), `lyra-chip-select`
6190
+ (`detail: { value, selected }` — fired on click, or Enter/Space while focused, when `selected` mode
6191
+ is active and `removable` is not set; the chip has already toggled its own `selected` state by the
6192
+ time this fires)
5721
6193
 
5722
6194
  **Slots:** default (the chip's label content), `icon` (optional leading icon or status dot; nothing
5723
6195
  reserved for it — no extra gap — when left empty)
@@ -6328,7 +6800,10 @@ native wrapper's own camera API).
6328
6800
  `capabilities.length === 1`), `menu` (the `lyra-menu` wrapper, only rendered when
6329
6801
  `capabilities.length > 1`), `menu-trigger` (the multi-capability button slotted into `lyra-menu`'s own
6330
6802
  `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`).
6803
+ inside the multi-capability trigger button, only rendered when `capabilities.length > 1`),
6804
+ `hidden-input` (the internal native `<input type="file">` that actually opens the OS file picker;
6805
+ hidden via CSS by default, exposed as a part only so a consumer can override that with
6806
+ `::part(hidden-input)` in the unlikely case their integration needs to).
6332
6807
 
6333
6808
  **Themeable custom properties:** shared tokens only — `--lyra-space-xs`, `--lyra-color-text`/
6334
6809
  `-text-quiet`, `--lyra-icon-button-size`, `--lyra-focus-ring-*`, `--lyra-opacity-disabled`,
@@ -6495,6 +6970,113 @@ around either the slotted content or the plain `value` text).
6495
6970
 
6496
6971
  ---
6497
6972
 
6973
+ ## `lyra-avatar`
6974
+
6975
+ A small, fixed-size identity marker: an image, or a fallback of initials text when no image is set
6976
+ (or the image fails to load). First-party invention (no Web Awesome equivalent) — purely
6977
+ presentational, with no built-in interactivity; wrap it in a `<button>`/`<lyra-menu>` trigger for a
6978
+ user-menu affordance.
6979
+
6980
+ **Properties:**
6981
+ - `initials: string = ''` — fallback text (typically 1-2 characters), shown whenever no image is set
6982
+ or the image fails to load.
6983
+ - `src?: string` — image URL; takes priority over `initials` when set and loads successfully, falling
6984
+ back to `initials` on a load error.
6985
+ - `alt: string = ''` — image alt text; set alongside `src` for accessibility.
6986
+ - `size: 'sm' | 'md' | 'lg' = 'md'` (reflected).
6987
+ - `shape: 'circle' | 'square' = 'circle'` (reflected).
6988
+ - `tone: 'neutral' | 'brand' | 'success' | 'warning' | 'danger' = 'neutral'` (reflected) — recolors
6989
+ the initials-fallback background/text, mirroring `lyra-chip`'s `tone` vocabulary; `neutral` (the
6990
+ default) reads as a plain, unaccented circle.
6991
+
6992
+ **Events:** none.
6993
+
6994
+ **Slots:** none.
6995
+
6996
+ **CSS parts:** `base` (the outer circle/square container), `image` (the `<img>`, only rendered while
6997
+ `src` is set and hasn't failed to load), `initials` (the fallback initials text, rendered whenever
6998
+ `image` isn't).
6999
+
7000
+ **Themeable custom properties:** `--lyra-avatar-size` (default `2rem`, swapped to `1.5rem`/`2.5rem`
7001
+ per `size="sm"`/`"lg"`), `--lyra-avatar-bg` (default `var(--lyra-color-surface-alt,
7002
+ var(--lyra-color-border))`, swapped per `tone`), `--lyra-avatar-color` (default
7003
+ `var(--lyra-color-text)`, swapped per `tone`), plus shared tokens `--lyra-radius`/`-pill`,
7004
+ `--lyra-font-size-sm`, `--lyra-font-weight-semibold`.
7005
+
7006
+ **Optional peer deps:** none.
7007
+
7008
+ ```html
7009
+ <lyra-avatar initials="JS" tone="brand"></lyra-avatar>
7010
+ <lyra-avatar src="/users/42/photo.jpg" alt="Jane Smith" size="lg"></lyra-avatar>
7011
+ ```
7012
+
7013
+ **Known gotchas:**
7014
+ - an image load failure falls back to `initials` automatically (an `error` listener flips internal
7015
+ state) — there's no manual reset/retry method; reassigning `src` (even to the same value)
7016
+ re-attempts the load.
7017
+ - the initials fallback is `aria-hidden="true"` whenever `alt` is set, regardless of whether an image
7018
+ is actually showing — so if `src`/`alt` are both set but the image fails to load, the rendered
7019
+ initials text is still hidden from assistive tech, leaving the avatar with no accessible name of
7020
+ its own unless the surrounding context supplies one.
7021
+
7022
+ ---
7023
+
7024
+ ## `lyra-card`
7025
+
7026
+ A generic, styled bordered content container — the "small bordered surface with padding" idiom common
7027
+ to hero highlights, clickable grid tiles, and management-list items. A direct `<lyra-*>` counterpart
7028
+ to `<wa-card>`'s contract, staying slot-compatible with `lyra-result-card` where they overlap.
7029
+
7030
+ **Properties:**
7031
+ - `appearance: 'accent' | 'filled' | 'outlined' | 'filled-outlined' | 'plain' = 'outlined'`
7032
+ (reflected) — `'outlined'` (the default) is a plain bordered surface; `'filled'` swaps the border
7033
+ for a quiet brand-tinted background; `'filled-outlined'` keeps the border and adds that same tinted
7034
+ background; `'accent'` drops the border for a single colored accent stripe on the leading edge;
7035
+ `'plain'` has no border or background at all.
7036
+ - `interactive: boolean = false` (reflected) — opt-in hover/focus-visible treatment (border-color
7037
+ shift, cursor: pointer) for a card used as a clickable tile — purely visual; this component takes
7038
+ no position on what "activate" means unless `href` is also set. `false` (the default) reproduces a
7039
+ plain static card.
7040
+ - `href?: string` — when set, the card's root renders as a real `<a href=...>` instead of a `<div>`,
7041
+ for a whole-card link (e.g. a wide CTA tile). Unset (the default) renders a plain `<div>`.
7042
+
7043
+ **Events:** none.
7044
+
7045
+ **Slots:** default (the card body), `header` (header row content, rendered above the body), `media`
7046
+ (media content, e.g. an image, rendered above the header), `footer` (footer content, rendered below
7047
+ the body), `actions` (small header controls, rendered alongside the header content).
7048
+
7049
+ **CSS parts:** `base` (the outer container — a `<div>`, or an `<a>` when `href` is set), `media`
7050
+ (wrapper around the `media` slot, hidden entirely when empty), `header` (wrapper around the `header`
7051
+ slot and `actions`, hidden entirely when both are empty), `actions` (wrapper around the `actions`
7052
+ slot, hidden entirely when empty), `body` (wrapper around the default slot), `footer` (wrapper around
7053
+ the `footer` slot, hidden entirely when empty).
7054
+
7055
+ **Themeable custom properties:** shared tokens only — `--lyra-color-border`/`-surface`/`-brand`/
7056
+ `-brand-quiet`, `--lyra-radius`, `--lyra-space-s`/`-m`, `--lyra-transition-fast`,
7057
+ `--lyra-focus-ring-*`.
7058
+
7059
+ **Optional peer deps:** none.
7060
+
7061
+ ```html
7062
+ <lyra-card appearance="outlined" interactive href="/reports/42">
7063
+ <img slot="media" src="/thumb.png" alt="" />
7064
+ <span slot="header">Q3 Report</span>
7065
+ <span slot="actions"><lyra-chip tone="success">Ready</lyra-chip></span>
7066
+ Revenue up 12% quarter-over-quarter.
7067
+ <span slot="footer">Updated 2 days ago</span>
7068
+ </lyra-card>
7069
+ ```
7070
+
7071
+ **Known gotchas:**
7072
+ - every `appearance` renders on the *same* `[part="base"]` element — there's no separate element per
7073
+ variant, so a `::part(base)` override applies uniformly regardless of `appearance`.
7074
+ - slot-presence (`header`/`media`/`footer`/`actions`) is tracked in JS, not via CSS `:empty` (a
7075
+ `[part]` wrapper always contains a literal `<slot>` child, so `:empty` never matches) — the same
7076
+ pattern `lyra-empty`/`lyra-widget` use.
7077
+
7078
+ ---
7079
+
6498
7080
  ## Cross-cutting notes
6499
7081
 
6500
7082
  These apply across the whole library rather than to any one component: