@aceshooting/lyra-ui 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +11 -0
  2. package/custom-elements.json +23341 -11202
  3. package/dist/components/app-rail/app-rail.d.ts +171 -0
  4. package/dist/components/app-rail/app-rail.js +483 -0
  5. package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail.styles.js +125 -0
  7. package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
  8. package/dist/components/attachment-chip/attachment-chip.js +312 -0
  9. package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
  10. package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
  11. package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
  12. package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
  15. package/dist/components/chart/box-plot.js +11 -1
  16. package/dist/components/chart/chart-loader.d.ts +44 -13
  17. package/dist/components/chart/chart-loader.js +65 -18
  18. package/dist/components/chart/chart.d.ts +1 -2
  19. package/dist/components/chart/chart.js +67 -12
  20. package/dist/components/chart/histogram-bin.d.ts +4 -1
  21. package/dist/components/chart/histogram-bin.js +24 -14
  22. package/dist/components/chart/lite-chart.d.ts +54 -0
  23. package/dist/components/chart/lite-chart.js +140 -22
  24. package/dist/components/chart/lite-chart.styles.js +9 -0
  25. package/dist/components/chat-composer/chat-composer.d.ts +92 -0
  26. package/dist/components/chat-composer/chat-composer.js +285 -0
  27. package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
  28. package/dist/components/chat-composer/chat-composer.styles.js +135 -0
  29. package/dist/components/chat-message/chat-message.d.ts +113 -0
  30. package/dist/components/chat-message/chat-message.js +291 -0
  31. package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
  32. package/dist/components/chat-message/chat-message.styles.js +203 -0
  33. package/dist/components/checkbox/checkbox.d.ts +59 -0
  34. package/dist/components/checkbox/checkbox.js +230 -0
  35. package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
  36. package/dist/components/checkbox/checkbox.styles.js +70 -0
  37. package/dist/components/chip/chip-group.d.ts +56 -0
  38. package/dist/components/chip/chip-group.js +124 -0
  39. package/dist/components/chip/chip-group.styles.d.ts +1 -0
  40. package/dist/components/chip/chip-group.styles.js +57 -0
  41. package/dist/components/chip/chip.d.ts +64 -0
  42. package/dist/components/chip/chip.js +115 -0
  43. package/dist/components/chip/chip.styles.d.ts +1 -0
  44. package/dist/components/chip/chip.styles.js +115 -0
  45. package/dist/components/citation-badge/citation-badge.d.ts +113 -0
  46. package/dist/components/citation-badge/citation-badge.js +300 -0
  47. package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
  48. package/dist/components/citation-badge/citation-badge.styles.js +96 -0
  49. package/dist/components/code-block/code-block.d.ts +89 -0
  50. package/dist/components/code-block/code-block.js +317 -0
  51. package/dist/components/code-block/code-block.styles.d.ts +1 -0
  52. package/dist/components/code-block/code-block.styles.js +187 -0
  53. package/dist/components/code-block/code-loader.d.ts +44 -0
  54. package/dist/components/code-block/code-loader.js +79 -0
  55. package/dist/components/combobox/combobox.d.ts +19 -1
  56. package/dist/components/combobox/combobox.js +84 -14
  57. package/dist/components/combobox/option.d.ts +5 -1
  58. package/dist/components/combobox/option.js +35 -0
  59. package/dist/components/context-meter/context-meter.d.ts +53 -0
  60. package/dist/components/context-meter/context-meter.js +150 -0
  61. package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
  62. package/dist/components/context-meter/context-meter.styles.js +120 -0
  63. package/dist/components/conversation-item/conversation-item.d.ts +128 -0
  64. package/dist/components/conversation-item/conversation-item.js +322 -0
  65. package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
  66. package/dist/components/conversation-item/conversation-item.styles.js +155 -0
  67. package/dist/components/date-picker/date-input.d.ts +24 -0
  68. package/dist/components/date-picker/date-input.js +134 -22
  69. package/dist/components/date-picker/date-picker.d.ts +7 -0
  70. package/dist/components/date-picker/date-picker.js +85 -16
  71. package/dist/components/dialog/confirm.d.ts +40 -0
  72. package/dist/components/dialog/confirm.js +79 -0
  73. package/dist/components/dialog/dialog.d.ts +85 -0
  74. package/dist/components/dialog/dialog.js +272 -0
  75. package/dist/components/dialog/dialog.styles.d.ts +1 -0
  76. package/dist/components/dialog/dialog.styles.js +52 -0
  77. package/dist/components/dock-panel/dock-panel.d.ts +134 -0
  78. package/dist/components/dock-panel/dock-panel.js +345 -0
  79. package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
  80. package/dist/components/dock-panel/dock-panel.styles.js +145 -0
  81. package/dist/components/document-preview/document-preview.d.ts +135 -0
  82. package/dist/components/document-preview/document-preview.js +372 -0
  83. package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
  84. package/dist/components/document-preview/document-preview.styles.js +191 -0
  85. package/dist/components/export-button/csv.js +1 -1
  86. package/dist/components/export-button/export-button.js +2 -0
  87. package/dist/components/flag/flag.d.ts +29 -13
  88. package/dist/components/flag/flag.js +39 -13
  89. package/dist/components/gauge/gauge.js +19 -10
  90. package/dist/components/generation-status/generation-status.d.ts +109 -0
  91. package/dist/components/generation-status/generation-status.js +273 -0
  92. package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
  93. package/dist/components/generation-status/generation-status.styles.js +84 -0
  94. package/dist/components/heatmap/calendar-grid.d.ts +4 -1
  95. package/dist/components/heatmap/calendar-grid.js +25 -12
  96. package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
  97. package/dist/components/heatmap/heatmap-scale.js +7 -1
  98. package/dist/components/heatmap/heatmap.d.ts +74 -0
  99. package/dist/components/heatmap/heatmap.js +135 -23
  100. package/dist/components/json-viewer/json-viewer.d.ts +53 -0
  101. package/dist/components/json-viewer/json-viewer.js +283 -0
  102. package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
  103. package/dist/components/json-viewer/json-viewer.styles.js +176 -0
  104. package/dist/components/kbd/kbd.d.ts +77 -0
  105. package/dist/components/kbd/kbd.js +214 -0
  106. package/dist/components/kbd/kbd.styles.d.ts +1 -0
  107. package/dist/components/kbd/kbd.styles.js +52 -0
  108. package/dist/components/live-region/live-region.d.ts +81 -0
  109. package/dist/components/live-region/live-region.js +153 -0
  110. package/dist/components/live-region/live-region.styles.d.ts +1 -0
  111. package/dist/components/live-region/live-region.styles.js +10 -0
  112. package/dist/components/map/map.d.ts +8 -6
  113. package/dist/components/map/map.js +44 -8
  114. package/dist/components/markdown/markdown-loader.d.ts +34 -0
  115. package/dist/components/markdown/markdown-loader.js +43 -0
  116. package/dist/components/markdown/markdown.d.ts +81 -0
  117. package/dist/components/markdown/markdown.js +284 -0
  118. package/dist/components/markdown/markdown.styles.d.ts +1 -0
  119. package/dist/components/markdown/markdown.styles.js +82 -0
  120. package/dist/components/media-card/media-card.d.ts +122 -0
  121. package/dist/components/media-card/media-card.js +295 -0
  122. package/dist/components/media-card/media-card.styles.d.ts +1 -0
  123. package/dist/components/media-card/media-card.styles.js +132 -0
  124. package/dist/components/mention-popover/mention-popover.d.ts +200 -0
  125. package/dist/components/mention-popover/mention-popover.js +475 -0
  126. package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
  127. package/dist/components/mention-popover/mention-popover.styles.js +101 -0
  128. package/dist/components/menu/menu-item.d.ts +62 -0
  129. package/dist/components/menu/menu-item.js +119 -0
  130. package/dist/components/menu/menu-item.styles.d.ts +1 -0
  131. package/dist/components/menu/menu-item.styles.js +67 -0
  132. package/dist/components/menu/menu.d.ts +143 -0
  133. package/dist/components/menu/menu.js +365 -0
  134. package/dist/components/menu/menu.styles.d.ts +1 -0
  135. package/dist/components/menu/menu.styles.js +66 -0
  136. package/dist/components/model-select/model-select.d.ts +125 -0
  137. package/dist/components/model-select/model-select.js +490 -0
  138. package/dist/components/model-select/model-select.styles.d.ts +1 -0
  139. package/dist/components/model-select/model-select.styles.js +189 -0
  140. package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
  141. package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
  142. package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
  143. package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
  144. package/dist/components/playback/playback.d.ts +13 -1
  145. package/dist/components/playback/playback.js +47 -14
  146. package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
  147. package/dist/components/responsive-panel/responsive-panel.js +365 -0
  148. package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
  149. package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
  150. package/dist/components/result-card/result-card.d.ts +44 -0
  151. package/dist/components/result-card/result-card.js +83 -0
  152. package/dist/components/result-card/result-card.styles.d.ts +1 -0
  153. package/dist/components/result-card/result-card.styles.js +50 -0
  154. package/dist/components/result-card/result-field.d.ts +36 -0
  155. package/dist/components/result-card/result-field.js +87 -0
  156. package/dist/components/result-card/result-field.styles.d.ts +1 -0
  157. package/dist/components/result-card/result-field.styles.js +32 -0
  158. package/dist/components/select/select.d.ts +43 -2
  159. package/dist/components/select/select.js +150 -17
  160. package/dist/components/slider/slider.d.ts +89 -0
  161. package/dist/components/slider/slider.js +352 -0
  162. package/dist/components/slider/slider.styles.d.ts +1 -0
  163. package/dist/components/slider/slider.styles.js +87 -0
  164. package/dist/components/source-card/source-card.d.ts +92 -0
  165. package/dist/components/source-card/source-card.js +148 -0
  166. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  167. package/dist/components/source-card/source-card.styles.js +69 -0
  168. package/dist/components/source-list/source-list.d.ts +63 -0
  169. package/dist/components/source-list/source-list.js +122 -0
  170. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  171. package/dist/components/source-list/source-list.styles.js +59 -0
  172. package/dist/components/sparkline/sparkline.js +27 -13
  173. package/dist/components/split/split.d.ts +93 -1
  174. package/dist/components/split/split.js +284 -15
  175. package/dist/components/split/split.styles.js +30 -0
  176. package/dist/components/stat/stat.d.ts +6 -1
  177. package/dist/components/stat/stat.js +8 -2
  178. package/dist/components/stream-status/stream-status.d.ts +110 -0
  179. package/dist/components/stream-status/stream-status.js +210 -0
  180. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  181. package/dist/components/stream-status/stream-status.styles.js +103 -0
  182. package/dist/components/streaming-text/streaming-text.d.ts +98 -0
  183. package/dist/components/streaming-text/streaming-text.js +209 -0
  184. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  185. package/dist/components/streaming-text/streaming-text.styles.js +62 -0
  186. package/dist/components/switch/switch.d.ts +58 -0
  187. package/dist/components/switch/switch.js +179 -0
  188. package/dist/components/switch/switch.styles.d.ts +1 -0
  189. package/dist/components/switch/switch.styles.js +75 -0
  190. package/dist/components/table/table.d.ts +73 -4
  191. package/dist/components/table/table.js +169 -20
  192. package/dist/components/table/table.styles.js +6 -1
  193. package/dist/components/tabs/tabs.d.ts +51 -0
  194. package/dist/components/tabs/tabs.js +190 -0
  195. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  196. package/dist/components/tabs/tabs.styles.js +63 -0
  197. package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
  198. package/dist/components/thinking-panel/thinking-panel.js +235 -0
  199. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  200. package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
  201. package/dist/components/toast/toast-item.d.ts +1 -0
  202. package/dist/components/toast/toast-item.js +31 -6
  203. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
  204. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
  205. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  206. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
  207. package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
  208. package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
  209. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  210. package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
  211. package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
  212. package/dist/components/tool-param-form/tool-param-form.js +366 -0
  213. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  214. package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
  215. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
  216. package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
  217. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  218. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
  219. package/dist/components/tool-result-view/registry.d.ts +81 -0
  220. package/dist/components/tool-result-view/registry.js +84 -0
  221. package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
  222. package/dist/components/tool-result-view/tool-result-view.js +138 -0
  223. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  224. package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
  225. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
  226. package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
  227. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  228. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
  229. package/dist/components/tree/tree-node.d.ts +11 -2
  230. package/dist/components/tree/tree-node.js +16 -6
  231. package/dist/components/tree/tree.d.ts +8 -2
  232. package/dist/components/tree/tree.js +18 -11
  233. package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
  234. package/dist/components/typing-indicator/typing-indicator.js +96 -0
  235. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  236. package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
  237. package/dist/components/virtual-list/virtual-list.d.ts +173 -0
  238. package/dist/components/virtual-list/virtual-list.js +461 -0
  239. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  240. package/dist/components/virtual-list/virtual-list.styles.js +44 -0
  241. package/dist/components/word-cloud/word-cloud-layout.js +14 -1
  242. package/dist/components/word-cloud/word-cloud.d.ts +6 -0
  243. package/dist/components/word-cloud/word-cloud.js +25 -14
  244. package/dist/components/word-cloud/word-cloud.styles.js +3 -2
  245. package/dist/internal/announcer.d.ts +57 -0
  246. package/dist/internal/announcer.js +71 -0
  247. package/dist/internal/form-associated.d.ts +1 -0
  248. package/dist/internal/form-associated.js +55 -11
  249. package/dist/internal/group-by-recency.d.ts +50 -0
  250. package/dist/internal/group-by-recency.js +80 -0
  251. package/dist/internal/rtl.d.ts +23 -0
  252. package/dist/internal/rtl.js +28 -0
  253. package/dist/internal/scroll-lock.d.ts +10 -5
  254. package/dist/internal/scroll-lock.js +30 -14
  255. package/dist/internal/tokens.styles.js +6 -0
  256. package/dist/lyra.d.ts +130 -31
  257. package/dist/lyra.js +94 -26
  258. package/llms-full.txt +3836 -17
  259. package/llms.txt +16 -1
  260. package/package.json +23 -5
  261. package/dist/components/chart/bar-chart.stories.d.ts +0 -5
  262. package/dist/components/chart/bar-chart.stories.js +0 -20
  263. package/dist/components/chart/box-plot.stories.d.ts +0 -5
  264. package/dist/components/chart/box-plot.stories.js +0 -29
  265. package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
  266. package/dist/components/chart/bubble-chart.stories.js +0 -26
  267. package/dist/components/chart/chart.stories.d.ts +0 -32
  268. package/dist/components/chart/chart.stories.js +0 -141
  269. package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
  270. package/dist/components/chart/doughnut-chart.stories.js +0 -26
  271. package/dist/components/chart/histogram.stories.d.ts +0 -5
  272. package/dist/components/chart/histogram.stories.js +0 -17
  273. package/dist/components/chart/line-chart.stories.d.ts +0 -22
  274. package/dist/components/chart/line-chart.stories.js +0 -99
  275. package/dist/components/chart/lite-chart.stories.d.ts +0 -14
  276. package/dist/components/chart/lite-chart.stories.js +0 -114
  277. package/dist/components/chart/pie-chart.stories.d.ts +0 -5
  278. package/dist/components/chart/pie-chart.stories.js +0 -26
  279. package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
  280. package/dist/components/chart/polar-area-chart.stories.js +0 -20
  281. package/dist/components/chart/radar-chart.stories.d.ts +0 -5
  282. package/dist/components/chart/radar-chart.stories.js +0 -20
  283. package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
  284. package/dist/components/chart/scatter-chart.stories.js +0 -31
  285. package/dist/components/combobox/combobox.stories.d.ts +0 -24
  286. package/dist/components/combobox/combobox.stories.js +0 -102
  287. package/dist/components/date-picker/date-input.stories.d.ts +0 -10
  288. package/dist/components/date-picker/date-input.stories.js +0 -48
  289. package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
  290. package/dist/components/date-picker/date-picker.stories.js +0 -25
  291. package/dist/components/empty/empty.stories.d.ts +0 -7
  292. package/dist/components/empty/empty.stories.js +0 -34
  293. package/dist/components/export-button/export-button.stories.d.ts +0 -7
  294. package/dist/components/export-button/export-button.stories.js +0 -37
  295. package/dist/components/file-input/file-input.stories.d.ts +0 -7
  296. package/dist/components/file-input/file-input.stories.js +0 -19
  297. package/dist/components/flag/flag.stories.d.ts +0 -6
  298. package/dist/components/flag/flag.stories.js +0 -31
  299. package/dist/components/gauge/gauge.stories.d.ts +0 -8
  300. package/dist/components/gauge/gauge.stories.js +0 -19
  301. package/dist/components/graph/graph.stories.d.ts +0 -8
  302. package/dist/components/graph/graph.stories.js +0 -68
  303. package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
  304. package/dist/components/heatmap/heatmap.stories.js +0 -179
  305. package/dist/components/map/map.stories.d.ts +0 -15
  306. package/dist/components/map/map.stories.js +0 -135
  307. package/dist/components/playback/playback.stories.d.ts +0 -7
  308. package/dist/components/playback/playback.stories.js +0 -16
  309. package/dist/components/select/select.stories.d.ts +0 -21
  310. package/dist/components/select/select.stories.js +0 -91
  311. package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
  312. package/dist/components/skeleton/skeleton.stories.js +0 -16
  313. package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
  314. package/dist/components/sparkline/sparkline.stories.js +0 -39
  315. package/dist/components/split/split.stories.d.ts +0 -7
  316. package/dist/components/split/split.stories.js +0 -36
  317. package/dist/components/stat/stat.stories.d.ts +0 -9
  318. package/dist/components/stat/stat.stories.js +0 -81
  319. package/dist/components/table/table.stories.d.ts +0 -12
  320. package/dist/components/table/table.stories.js +0 -67
  321. package/dist/components/time-range/time-range.stories.d.ts +0 -8
  322. package/dist/components/time-range/time-range.stories.js +0 -26
  323. package/dist/components/toast/toast.stories.d.ts +0 -8
  324. package/dist/components/toast/toast.stories.js +0 -72
  325. package/dist/components/tree/tree.stories.d.ts +0 -9
  326. package/dist/components/tree/tree.stories.js +0 -52
  327. package/dist/components/widget/widget.stories.d.ts +0 -9
  328. package/dist/components/widget/widget.stories.js +0 -74
  329. package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
  330. package/dist/components/word-cloud/word-cloud.stories.js +0 -55
@@ -75,6 +75,17 @@ function niceDomain(dataLo, dataHi, beginAtZero, count) {
75
75
  * `getComputedStyle()`-based re-theming step is needed the way `chart.ts`
76
76
  * needs one for its canvas.
77
77
  *
78
+ * By default (`layout="fit"`) the plot always squeezes to the measured host
79
+ * width. Three independent, opt-in escape hatches for dense/aligned data:
80
+ * `layout="scroll"` (+ `barWidth`) gives every bar a fixed pixel width and
81
+ * lets the plot overflow the host horizontally (scrollable) instead of
82
+ * squeezing; `maxLabels` decimates which x-axis text labels render (bars
83
+ * always still render) once there are more categories than that; and
84
+ * `barX` lets a consumer hand in its own per-category x-coordinate function
85
+ * — e.g. to pixel-align this chart's bars with a sibling `lyra-heatmap`'s
86
+ * calendar columns — overriding the internal slot math for both bars and
87
+ * their labels. All three are additive and no-ops when left unset.
88
+ *
78
89
  * @customElement lyra-lite-chart
79
90
  * @event lyra-point-click - Fired when a bar/point is activated (click, or
80
91
  * Enter/Space while focused). `detail: { datasetIndex: number, index:
@@ -104,6 +115,15 @@ export class LyraLiteChart extends LyraElement {
104
115
  this.beginAtZero = true;
105
116
  /** Stacks each category's bars into one segmented bar. Ignored for `type="line"`. */
106
117
  this.stacked = false;
118
+ /** `'fit'` (default) squeezes the plot into the measured host width, unchanged from before this
119
+ * property existed. `'scroll'` gives every bar a fixed `barWidth` instead, letting the plot's
120
+ * content width exceed the host's — the host becomes horizontally scrollable
121
+ * (`overflow-x: auto`) so every bar stays exactly `barWidth` wide regardless of category count.
122
+ * Reflects to the `layout` attribute (e.g. for `:host([layout='scroll'])` host styling). */
123
+ this.layout = 'fit';
124
+ /** Fixed per-category bar width in px, used only when `layout="scroll"`. Ignored (as before this
125
+ * property existed) in `layout="fit"`, the default. */
126
+ this.barWidth = 32;
107
127
  this.plotWidth = 0;
108
128
  this.plotHeight = 0;
109
129
  this.visible = true;
@@ -132,12 +152,30 @@ export class LyraLiteChart extends LyraElement {
132
152
  });
133
153
  this.intersectionObserver.observe(this);
134
154
  }
155
+ // A reconnect re-creates the observer above but the `<svg>` render root
156
+ // content survives across disconnect/reconnect (Lit doesn't tear down the
157
+ // shadow root) — re-observe it here (firstUpdated() only ever runs once,
158
+ // on the very first render, so it can't be relied on for a reconnect).
159
+ if (this.svgEl)
160
+ this.resizeObserver.observe(this.svgEl);
135
161
  }
136
162
  disconnectedCallback() {
137
163
  super.disconnectedCallback();
138
164
  this.resizeObserver?.disconnect();
139
165
  this.intersectionObserver?.disconnect();
140
166
  }
167
+ // Kept (not deleted, despite the "delete it" alternative the fix brief
168
+ // offered): connectedCallback() fires *before* Lit's first render, so on
169
+ // the very first mount `this.svgEl` (a `@query('svg')`) is still
170
+ // undefined there and its guarded `observe()` call is a no-op — verified
171
+ // empirically: deleting firstUpdated() here hung every existing test that
172
+ // waits for the ResizeObserver's initial measurement. firstUpdated() runs
173
+ // once, after that first render, and is what actually arms the observer
174
+ // for the initial mount; connectedCallback()'s own observe() call (added
175
+ // above) only ever succeeds on a *reconnect*, when the shadow DOM (and
176
+ // svgEl) already exists from before the disconnect. Together the two
177
+ // cover first-mount and reconnect without ever double-observing the same
178
+ // element from the same callback path.
141
179
  firstUpdated() {
142
180
  if (this.svgEl)
143
181
  this.resizeObserver?.observe(this.svgEl);
@@ -200,6 +238,14 @@ export class LyraLiteChart extends LyraElement {
200
238
  this.stacked,
201
239
  this.plotWidth,
202
240
  this.plotHeight,
241
+ this.layout,
242
+ this.barWidth,
243
+ this.maxLabels,
244
+ // `tickFormat`/`barX` are functions -- JSON.stringify() silently drops
245
+ // function-valued entries, so (matching tickFormat's existing,
246
+ // pre-this-change exclusion) they can't be included here anyway; a
247
+ // consumer swapping one in/out post-mount should also change some
248
+ // other signature input, same pre-existing caveat as tickFormat.
203
249
  ]);
204
250
  }
205
251
  emitPoint(datasetIndex, index) {
@@ -223,28 +269,39 @@ export class LyraLiteChart extends LyraElement {
223
269
  `;
224
270
  });
225
271
  }
226
- renderBars(plotX, plotY, plotW, plotH, lo, hi) {
272
+ /**
273
+ * `slot` is the per-category width to lay bars out against — either the
274
+ * measured-width-derived `plotW / n` (`layout="fit"`) or the fixed
275
+ * `barWidth` (`layout="scroll"`), computed once by the caller
276
+ * (`renderChart()`) and handed to both this method and the category-label
277
+ * x-position calc so the two can never drift apart from each other.
278
+ */
279
+ renderBars(plotX, plotY, plotH, slot, lo, hi) {
227
280
  const span = hi - lo || 1;
228
281
  const n = this.labels.length;
229
- const slot = n > 0 ? plotW / n : 0;
230
282
  const groupCount = this.stacked ? 1 : Math.max(1, this.datasets.length);
231
283
  const groupW = slot * (1 - BAR_GROUP_GAP);
232
284
  const barW = (groupW - BAR_GAP * slot * (groupCount - 1)) / groupCount;
233
285
  const bars = [];
234
286
  for (let i = 0; i < n; i++) {
235
- const slotStart = plotX + i * slot + (slot - groupW) / 2;
287
+ // `this.barX`, when set, overrides just the category's own x-origin
288
+ // (what an unset `barX` would compute as `plotX + i * slot`) -- the
289
+ // group-centering/per-dataset-offset math below still layers on top of
290
+ // it, same as it does on top of the internal formula.
291
+ const origin = this.barX ? this.barX(i) : plotX + i * slot;
292
+ const slotStart = origin + (slot - groupW) / 2;
236
293
  let stackPos = 0; // running positive-side offset (in value units) for stacked bars
237
294
  let stackNeg = 0; // running negative-side offset
238
295
  this.datasets.forEach((s, di) => {
239
296
  const v = s.data[i];
240
- if (v == null)
297
+ if (v == null || !Number.isFinite(v))
241
298
  return;
242
299
  const color = this.colorFor(di, s);
243
- let barX;
300
+ let rectX;
244
301
  let barValLo;
245
302
  let barValHi;
246
303
  if (this.stacked) {
247
- barX = slotStart;
304
+ rectX = slotStart;
248
305
  if (v >= 0) {
249
306
  barValLo = stackPos;
250
307
  barValHi = stackPos + v;
@@ -257,9 +314,10 @@ export class LyraLiteChart extends LyraElement {
257
314
  }
258
315
  }
259
316
  else {
260
- barX = slotStart + di * (barW + BAR_GAP * slot);
261
- barValLo = Math.min(0, v);
262
- barValHi = Math.max(0, v);
317
+ rectX = slotStart + di * (barW + BAR_GAP * slot);
318
+ const zeroClamped = Math.min(hi, Math.max(lo, 0));
319
+ barValLo = Math.min(zeroClamped, v);
320
+ barValHi = Math.max(zeroClamped, v);
263
321
  }
264
322
  const y1 = plotY + plotH - ((barValHi - lo) / span) * plotH;
265
323
  const y2 = plotY + plotH - ((barValLo - lo) / span) * plotH;
@@ -267,7 +325,7 @@ export class LyraLiteChart extends LyraElement {
267
325
  bars.push(svg `
268
326
  <rect
269
327
  part="bar"
270
- x=${barX}
328
+ x=${rectX}
271
329
  y=${y1}
272
330
  width=${Math.max(0, barW)}
273
331
  height=${Math.max(0, y2 - y1)}
@@ -290,12 +348,19 @@ export class LyraLiteChart extends LyraElement {
290
348
  const yFor = (v) => plotY + plotH - ((v - lo) / span) * plotH;
291
349
  return this.datasets.map((s, di) => {
292
350
  const color = this.colorFor(di, s);
293
- const pts = s.data
294
- .map((v, i) => (v == null ? null : [xFor(i), yFor(v)]))
295
- .filter((p) => p !== null);
296
- const d = pts.map(([x, y], i) => `${i ? 'L' : 'M'}${x},${y}`).join(' ');
351
+ let d = '';
352
+ let penDown = false;
353
+ s.data.forEach((v, i) => {
354
+ if (v == null || !Number.isFinite(v)) {
355
+ penDown = false;
356
+ return;
357
+ }
358
+ const cmd = penDown ? 'L' : 'M';
359
+ d += `${cmd}${xFor(i)},${yFor(v)} `;
360
+ penDown = true;
361
+ });
297
362
  const dots = s.data.map((v, i) => {
298
- if (v == null)
363
+ if (v == null || !Number.isFinite(v))
299
364
  return nothing;
300
365
  const label = this.labels[i];
301
366
  return svg `
@@ -313,7 +378,7 @@ export class LyraLiteChart extends LyraElement {
313
378
  ><title>${s.label} — ${label}: ${v}</title></circle>
314
379
  `;
315
380
  });
316
- return svg `<path part="line" d=${d} stroke=${color}></path>${dots}`;
381
+ return svg `<path part="line" d=${d.trim()} stroke=${color}></path>${dots}`;
317
382
  });
318
383
  }
319
384
  render() {
@@ -327,29 +392,70 @@ export class LyraLiteChart extends LyraElement {
327
392
  this.lastResult = result;
328
393
  return result;
329
394
  }
395
+ /**
396
+ * Step size for `maxLabels` decimation: 1 (show every label, today's
397
+ * unchanged default) when `maxLabels` is unset or already >= the label
398
+ * count, otherwise the ceil-divide spacing that lands close to
399
+ * `maxLabels` shown labels while always keeping index 0 and the last
400
+ * index (enforced by the caller, not here).
401
+ */
402
+ labelStep(n) {
403
+ const max = this.maxLabels;
404
+ if (max == null || n <= max)
405
+ return 1;
406
+ return Math.max(1, Math.ceil((n - 1) / Math.max(1, max - 1)));
407
+ }
330
408
  renderChart() {
331
- const w = this.plotWidth || 400;
409
+ const n = this.labels.length;
332
410
  const h = this.plotHeight || 200;
333
411
  const padLeft = PAD_LEFT + (this.yLabel ? AXIS_TITLE_SPACE : 0);
334
412
  const padBottom = PAD_BOTTOM + (this.xLabel ? AXIS_TITLE_SPACE : 0);
335
413
  const plotX = padLeft;
336
414
  const plotY = PAD_TOP;
337
- const plotW = Math.max(0, w - padLeft - PAD_RIGHT);
338
415
  const plotH = Math.max(0, h - plotY - padBottom);
416
+ let w;
417
+ let plotW;
418
+ let slot;
419
+ if (this.layout === 'scroll') {
420
+ // Fixed-width bars: content width is driven by category count ×
421
+ // barWidth instead of the measured host width, and CAN exceed it --
422
+ // the svg gets an explicit inline-size below (not 100%) and
423
+ // `:host([layout='scroll']) [part='base']` (lite-chart.styles.ts)
424
+ // turns on `overflow-x: auto` so the host scrolls to reveal the rest.
425
+ slot = this.barWidth;
426
+ plotW = Math.max(0, n * slot);
427
+ w = plotX + plotW + PAD_RIGHT;
428
+ }
429
+ else {
430
+ // 'fit' (default): squeeze to the measured host width, byte-for-byte
431
+ // the same computation as before `layout` existed.
432
+ w = this.plotWidth || 400;
433
+ plotW = Math.max(0, w - plotX - PAD_RIGHT);
434
+ slot = n > 0 ? plotW / n : 0;
435
+ }
339
436
  const { lo, hi, ticks } = this.domain();
340
437
  const grid = this.renderGrid(plotX, plotY, plotW, plotH, ticks, lo, hi);
341
438
  const marks = this.type === 'bar'
342
- ? this.renderBars(plotX, plotY, plotW, plotH, lo, hi)
439
+ ? this.renderBars(plotX, plotY, plotH, slot, lo, hi)
343
440
  : this.renderLines(plotX, plotY, plotW, plotH, lo, hi);
441
+ const step = this.labelStep(n);
344
442
  const categoryLabels = this.labels.map((label, i) => {
345
- const n = this.labels.length;
346
- const x = plotX + (n > 1 ? (i / (n - 1)) * plotW : plotW / 2);
443
+ if (i !== 0 && i !== n - 1 && i % step !== 0)
444
+ return nothing; // maxLabels decimation
445
+ const x = this.type === 'bar' && n > 0
446
+ ? (this.barX ? this.barX(i) : plotX + i * slot) + slot / 2 // matches renderBars()'s own per-category slot center (or the barX override, when set)
447
+ : plotX + (n > 1 ? (i / (n - 1)) * plotW : plotW / 2);
347
448
  return svg `<text part="axis-label" x=${x} y=${plotY + plotH + 14} text-anchor="middle">${label}</text>`;
348
449
  });
349
450
  const chartLabel = this.datasets.map((d) => d.label).join(', ') || 'Chart';
350
451
  return html `
351
452
  <div part="base">
352
- <svg viewBox="0 0 ${w} ${h}" role="group" aria-label=${chartLabel}>
453
+ <svg
454
+ viewBox="0 0 ${w} ${h}"
455
+ style=${this.layout === 'scroll' ? `inline-size: ${w}px` : nothing}
456
+ role="group"
457
+ aria-label=${chartLabel}
458
+ >
353
459
  ${grid}
354
460
  ${categoryLabels}
355
461
  ${marks}
@@ -404,6 +510,18 @@ __decorate([
404
510
  __decorate([
405
511
  property({ attribute: false })
406
512
  ], LyraLiteChart.prototype, "tickFormat", void 0);
513
+ __decorate([
514
+ property({ reflect: true })
515
+ ], LyraLiteChart.prototype, "layout", void 0);
516
+ __decorate([
517
+ property({ type: Number, attribute: 'bar-width' })
518
+ ], LyraLiteChart.prototype, "barWidth", void 0);
519
+ __decorate([
520
+ property({ type: Number, attribute: 'max-labels' })
521
+ ], LyraLiteChart.prototype, "maxLabels", void 0);
522
+ __decorate([
523
+ property({ attribute: false })
524
+ ], LyraLiteChart.prototype, "barX", void 0);
407
525
  __decorate([
408
526
  state()
409
527
  ], LyraLiteChart.prototype, "plotWidth", void 0);
@@ -23,6 +23,15 @@ export const styles = css `
23
23
  flex-direction: column;
24
24
  gap: var(--lyra-space-xs);
25
25
  }
26
+ /* layout="scroll": the svg below gets an explicit inline-size (its
27
+ computed content width, set inline per-render since it depends on
28
+ category count/barWidth) instead of the 100% below, and can end up
29
+ wider than this container -- scroll to reveal the rest instead of
30
+ squeezing. Scoped strictly to the reflected [layout='scroll'] attribute
31
+ so layout="fit" (the default) never triggers this rule. */
32
+ :host([layout='scroll']) [part='base'] {
33
+ overflow-x: auto;
34
+ }
26
35
  svg {
27
36
  flex: 1 1 auto;
28
37
  inline-size: 100%;
@@ -0,0 +1,92 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ export type ChatComposerStatus = 'idle' | 'sending' | 'streaming';
4
+ declare const LyraChatComposer_base: typeof LyraElement & (new (...args: any[]) => import("../../internal/form-associated.js").FormAssociatedInterface);
5
+ /**
6
+ * `<lyra-chat-composer>` — the message input for a chat/agent conversation
7
+ * surface: an auto-resizing textarea plus a send/stop button. Form-
8
+ * associated via the `FormAssociated` mixin (see `<lyra-date-input>` for the
9
+ * same shape), so it participates in native `<form>` submission/validation/
10
+ * reset like any other text control — `name`/`value`/`disabled`/`required`/
11
+ * `checkValidity()`/`reportValidity()` all come from that mixin.
12
+ *
13
+ * Auto-resize reads the textarea's own *computed* line-height/padding/
14
+ * border at runtime (`resizeTextarea()`) rather than assuming a fixed
15
+ * px-per-row constant, so it stays correct under a consumer's own font-size/
16
+ * line-height overrides. It grows between `min-rows` and `max-rows`, then
17
+ * switches to internal scrolling.
18
+ *
19
+ * Enter-to-send (only active while `submit-on-enter` is true, the default):
20
+ * plain Enter submits and is prevented from inserting a newline; Shift+Enter
21
+ * always inserts a newline no matter what; an IME composition step (checked
22
+ * via `isComposing`, with `keyCode === 229` as a defense-in-depth fallback
23
+ * for browsers that report `isComposing` inconsistently on the
24
+ * compositionend-adjacent keydown) is never treated as a submit trigger;
25
+ * and while `status` isn't `"idle"` Enter is left alone to insert a newline
26
+ * too, rather than trying to submit again -- there is nothing meaningful to
27
+ * submit while a previous message is still sending/streaming, and the
28
+ * textarea deliberately stays interactive (see `disabled` below) so a user
29
+ * can keep composing their next message in the meantime.
30
+ *
31
+ * `lyra-submit`'s `detail.value` is always the exact, untrimmed current
32
+ * value (`detail.value === value` at the moment it fires) -- trimming is
33
+ * left to the consumer so it never silently diverges from what `value`
34
+ * itself reports. Submitting does not clear `value`; the consumer clears it
35
+ * once the submission has actually been accepted (so e.g. a failed send can
36
+ * leave the text in place for retry).
37
+ *
38
+ * @customElement lyra-chat-composer
39
+ * @slot leading - Content rendered before the textarea (e.g. an attach-file trigger button).
40
+ * @slot chips - An attachment tray rendered above the input row (e.g. files queued for this message).
41
+ * @slot trailing - Overrides the built-in send/stop button entirely when it has assigned content.
42
+ * @event lyra-input - Fired on every user-driven edit of the textarea (not a programmatic `.value` assignment). `detail: { value }`.
43
+ * @event lyra-submit - Fired by Enter (per `submit-on-enter`) or the built-in button while `status="idle"`. `detail: { value }`.
44
+ * @event lyra-stop - Fired by the built-in button while `status` is `"sending"` or `"streaming"`. No detail.
45
+ * @csspart base - The bordered root container.
46
+ * @csspart chips - The wrapper around the `chips` slot. Hidden entirely when the slot is empty.
47
+ * @csspart row - The row holding the leading slot, textarea, and trailing slot/button.
48
+ * @csspart leading - The wrapper around the `leading` slot. Hidden entirely when the slot is empty.
49
+ * @csspart textarea - The auto-resizing `<textarea>` itself.
50
+ * @csspart trailing - The wrapper around the `trailing` slot and the built-in `action-button`.
51
+ * @csspart action-button - The built-in send/stop button. Absent from the accessibility tree's meaningful content whenever `trailing` has assigned content. Style its busy treatment via `:host([status='sending'])`/`:host([status='streaming'])`.
52
+ */
53
+ export declare class LyraChatComposer extends LyraChatComposer_base {
54
+ static styles: import("lit").CSSResultGroup[];
55
+ placeholder: string;
56
+ minRows: number;
57
+ maxRows: number;
58
+ status: ChatComposerStatus;
59
+ submitOnEnter: boolean;
60
+ private hasLeadingSlot;
61
+ private hasChipsSlot;
62
+ private hasTrailingSlot;
63
+ private textareaEl?;
64
+ protected willUpdate(): void;
65
+ protected firstUpdated(changed: PropertyValues): void;
66
+ protected updated(changed: PropertyValues): void;
67
+ private hasSlotted;
68
+ /**
69
+ * Grows/shrinks the textarea between `min-rows` and `max-rows`, reading
70
+ * the *computed* line-height/padding/border back out of the element at
71
+ * call time instead of assuming a fixed px-per-row figure -- correct
72
+ * whatever font-size/line-height a consumer's own CSS ends up applying.
73
+ * Past `max-rows` the box stops growing and switches to internal
74
+ * scrolling (`overflow-y: auto`).
75
+ */
76
+ private resizeTextarea;
77
+ private onTextareaInput;
78
+ private onTextareaKeyDown;
79
+ private onActionClick;
80
+ private submit;
81
+ private onLeadingSlotChange;
82
+ private onChipsSlotChange;
83
+ private onTrailingSlotChange;
84
+ private renderActionButton;
85
+ render(): TemplateResult;
86
+ }
87
+ declare global {
88
+ interface HTMLElementTagNameMap {
89
+ 'lyra-chat-composer': LyraChatComposer;
90
+ }
91
+ }
92
+ export {};
@@ -0,0 +1,285 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing, svg } from 'lit';
8
+ import { property, state, query } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { FormAssociated } from '../../internal/form-associated.js';
11
+ import { defineElement } from '../../internal/prefix.js';
12
+ import { styles } from './chat-composer.styles.js';
13
+ // Mirrors the shared icon set's viewBox/stroke conventions
14
+ // (internal/icons.ts's chevronIcon()/closeIcon()/etc.) without adding
15
+ // send/stop glyphs to that module -- it's off limits here -- so these
16
+ // one-off icons still read as part of the same visual language as the rest
17
+ // of the library's inline icons. Same approach lyra-checkbox's and
18
+ // lyra-chat-message's own local glyphs take for the identical reason.
19
+ const ICON_VIEW_BOX = '0 0 24 24';
20
+ const ICON_STROKE_WIDTH = '1.75';
21
+ function sendIcon() {
22
+ return svg `
23
+ <svg
24
+ width="1em"
25
+ height="1em"
26
+ viewBox=${ICON_VIEW_BOX}
27
+ fill="none"
28
+ stroke="currentColor"
29
+ stroke-width=${ICON_STROKE_WIDTH}
30
+ stroke-linecap="round"
31
+ stroke-linejoin="round"
32
+ aria-hidden="true"
33
+ focusable="false"
34
+ ><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
35
+ `;
36
+ }
37
+ /** A filled square -- the conventional "stop generating" glyph. */
38
+ function stopIcon() {
39
+ return svg `
40
+ <svg
41
+ width="1em"
42
+ height="1em"
43
+ viewBox=${ICON_VIEW_BOX}
44
+ fill="currentColor"
45
+ stroke="none"
46
+ aria-hidden="true"
47
+ focusable="false"
48
+ ><rect x="6" y="6" width="12" height="12" rx="1.5"></rect></svg>
49
+ `;
50
+ }
51
+ /**
52
+ * `<lyra-chat-composer>` — the message input for a chat/agent conversation
53
+ * surface: an auto-resizing textarea plus a send/stop button. Form-
54
+ * associated via the `FormAssociated` mixin (see `<lyra-date-input>` for the
55
+ * same shape), so it participates in native `<form>` submission/validation/
56
+ * reset like any other text control — `name`/`value`/`disabled`/`required`/
57
+ * `checkValidity()`/`reportValidity()` all come from that mixin.
58
+ *
59
+ * Auto-resize reads the textarea's own *computed* line-height/padding/
60
+ * border at runtime (`resizeTextarea()`) rather than assuming a fixed
61
+ * px-per-row constant, so it stays correct under a consumer's own font-size/
62
+ * line-height overrides. It grows between `min-rows` and `max-rows`, then
63
+ * switches to internal scrolling.
64
+ *
65
+ * Enter-to-send (only active while `submit-on-enter` is true, the default):
66
+ * plain Enter submits and is prevented from inserting a newline; Shift+Enter
67
+ * always inserts a newline no matter what; an IME composition step (checked
68
+ * via `isComposing`, with `keyCode === 229` as a defense-in-depth fallback
69
+ * for browsers that report `isComposing` inconsistently on the
70
+ * compositionend-adjacent keydown) is never treated as a submit trigger;
71
+ * and while `status` isn't `"idle"` Enter is left alone to insert a newline
72
+ * too, rather than trying to submit again -- there is nothing meaningful to
73
+ * submit while a previous message is still sending/streaming, and the
74
+ * textarea deliberately stays interactive (see `disabled` below) so a user
75
+ * can keep composing their next message in the meantime.
76
+ *
77
+ * `lyra-submit`'s `detail.value` is always the exact, untrimmed current
78
+ * value (`detail.value === value` at the moment it fires) -- trimming is
79
+ * left to the consumer so it never silently diverges from what `value`
80
+ * itself reports. Submitting does not clear `value`; the consumer clears it
81
+ * once the submission has actually been accepted (so e.g. a failed send can
82
+ * leave the text in place for retry).
83
+ *
84
+ * @customElement lyra-chat-composer
85
+ * @slot leading - Content rendered before the textarea (e.g. an attach-file trigger button).
86
+ * @slot chips - An attachment tray rendered above the input row (e.g. files queued for this message).
87
+ * @slot trailing - Overrides the built-in send/stop button entirely when it has assigned content.
88
+ * @event lyra-input - Fired on every user-driven edit of the textarea (not a programmatic `.value` assignment). `detail: { value }`.
89
+ * @event lyra-submit - Fired by Enter (per `submit-on-enter`) or the built-in button while `status="idle"`. `detail: { value }`.
90
+ * @event lyra-stop - Fired by the built-in button while `status` is `"sending"` or `"streaming"`. No detail.
91
+ * @csspart base - The bordered root container.
92
+ * @csspart chips - The wrapper around the `chips` slot. Hidden entirely when the slot is empty.
93
+ * @csspart row - The row holding the leading slot, textarea, and trailing slot/button.
94
+ * @csspart leading - The wrapper around the `leading` slot. Hidden entirely when the slot is empty.
95
+ * @csspart textarea - The auto-resizing `<textarea>` itself.
96
+ * @csspart trailing - The wrapper around the `trailing` slot and the built-in `action-button`.
97
+ * @csspart action-button - The built-in send/stop button. Absent from the accessibility tree's meaningful content whenever `trailing` has assigned content. Style its busy treatment via `:host([status='sending'])`/`:host([status='streaming'])`.
98
+ */
99
+ export class LyraChatComposer extends FormAssociated(LyraElement) {
100
+ constructor() {
101
+ super(...arguments);
102
+ this.placeholder = '';
103
+ this.minRows = 1;
104
+ this.maxRows = 8;
105
+ this.status = 'idle';
106
+ this.submitOnEnter = true;
107
+ this.hasLeadingSlot = false;
108
+ this.hasChipsSlot = false;
109
+ this.hasTrailingSlot = false;
110
+ this.onTextareaInput = (e) => {
111
+ this.value = e.target.value;
112
+ this.emit('lyra-input', { value: this.value });
113
+ };
114
+ this.onTextareaKeyDown = (e) => {
115
+ if (e.key !== 'Enter')
116
+ return;
117
+ // Shift+Enter always inserts a newline, regardless of submit-on-enter --
118
+ // leave the browser's own default action alone.
119
+ if (e.shiftKey)
120
+ return;
121
+ // submit-on-enter="false": Enter always inserts a newline too.
122
+ if (!this.submitOnEnter)
123
+ return;
124
+ // An IME composition step (e.g. confirming a Japanese/Chinese/Korean
125
+ // candidate) must never be treated as "the user pressed Enter to send" --
126
+ // keyCode 229 is a defense-in-depth fallback for browsers that report
127
+ // isComposing inconsistently on the compositionend-adjacent keydown.
128
+ if (e.isComposing || e.keyCode === 229)
129
+ return;
130
+ // Nothing meaningful to (re-)submit while already sending/streaming --
131
+ // fall through to the default newline insertion instead, so the
132
+ // textarea stays a normal place to keep composing the next message.
133
+ if (this.status !== 'idle')
134
+ return;
135
+ e.preventDefault();
136
+ this.submit();
137
+ };
138
+ this.onActionClick = () => {
139
+ if (this.effectiveDisabled)
140
+ return;
141
+ if (this.status === 'idle') {
142
+ this.submit();
143
+ }
144
+ else {
145
+ this.emit('lyra-stop');
146
+ }
147
+ };
148
+ this.onLeadingSlotChange = (e) => {
149
+ this.hasLeadingSlot = e.target.assignedElements({ flatten: true }).length > 0;
150
+ };
151
+ this.onChipsSlotChange = (e) => {
152
+ this.hasChipsSlot = e.target.assignedElements({ flatten: true }).length > 0;
153
+ };
154
+ this.onTrailingSlotChange = (e) => {
155
+ this.hasTrailingSlot = e.target.assignedElements({ flatten: true }).length > 0;
156
+ };
157
+ }
158
+ static { this.styles = [LyraElement.styles, styles]; }
159
+ willUpdate() {
160
+ if (!this.hasUpdated) {
161
+ this.hasLeadingSlot = this.hasSlotted('leading');
162
+ this.hasChipsSlot = this.hasSlotted('chips');
163
+ this.hasTrailingSlot = this.hasSlotted('trailing');
164
+ }
165
+ }
166
+ firstUpdated(changed) {
167
+ super.firstUpdated(changed);
168
+ this.resizeTextarea();
169
+ }
170
+ updated(changed) {
171
+ super.updated(changed);
172
+ if (changed.has('value') || changed.has('minRows') || changed.has('maxRows')) {
173
+ this.resizeTextarea();
174
+ }
175
+ }
176
+ hasSlotted(name) {
177
+ return Array.from(this.children).some((el) => el.getAttribute('slot') === name);
178
+ }
179
+ /**
180
+ * Grows/shrinks the textarea between `min-rows` and `max-rows`, reading
181
+ * the *computed* line-height/padding/border back out of the element at
182
+ * call time instead of assuming a fixed px-per-row figure -- correct
183
+ * whatever font-size/line-height a consumer's own CSS ends up applying.
184
+ * Past `max-rows` the box stops growing and switches to internal
185
+ * scrolling (`overflow-y: auto`).
186
+ */
187
+ resizeTextarea() {
188
+ const ta = this.textareaEl;
189
+ if (!ta)
190
+ return;
191
+ const cs = getComputedStyle(ta);
192
+ const lineHeight = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2;
193
+ const paddingBlock = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
194
+ const borderBlock = parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth);
195
+ const minRows = Math.max(1, this.minRows || 1);
196
+ const maxRows = Math.max(minRows, this.maxRows || minRows);
197
+ const minHeight = lineHeight * minRows + paddingBlock + borderBlock;
198
+ const maxHeight = lineHeight * maxRows + paddingBlock + borderBlock;
199
+ // Collapse first so a shrinking edit (e.g. deleting a wrapped line)
200
+ // reports its true scrollHeight rather than the previous, taller,
201
+ // explicitly-set height.
202
+ ta.style.height = 'auto';
203
+ // scrollHeight never includes border, so it's added back in to compare
204
+ // apples-to-apples against min/maxHeight, both of which are border-box
205
+ // figures (this component inherits box-sizing: border-box from
206
+ // LyraElement's tokens.styles.ts).
207
+ const contentHeight = ta.scrollHeight + borderBlock;
208
+ const next = Math.min(Math.max(contentHeight, minHeight), maxHeight);
209
+ ta.style.height = `${next}px`;
210
+ ta.style.overflowY = contentHeight > maxHeight ? 'auto' : 'hidden';
211
+ }
212
+ submit() {
213
+ this.emit('lyra-submit', { value: this.value });
214
+ }
215
+ renderActionButton() {
216
+ const busy = this.status !== 'idle';
217
+ return html `
218
+ <button
219
+ part="action-button"
220
+ type="button"
221
+ aria-label=${busy ? 'Stop generating' : 'Send message'}
222
+ ?disabled=${this.effectiveDisabled}
223
+ @click=${this.onActionClick}
224
+ >
225
+ ${busy ? stopIcon() : sendIcon()}
226
+ </button>
227
+ `;
228
+ }
229
+ render() {
230
+ return html `
231
+ <div part="base">
232
+ <div part="chips" ?hidden=${!this.hasChipsSlot}>
233
+ <slot name="chips" @slotchange=${this.onChipsSlotChange}></slot>
234
+ </div>
235
+ <div part="row">
236
+ <span part="leading" ?hidden=${!this.hasLeadingSlot}>
237
+ <slot name="leading" @slotchange=${this.onLeadingSlotChange}></slot>
238
+ </span>
239
+ <textarea
240
+ part="textarea"
241
+ aria-label=${this.placeholder || 'Message'}
242
+ .value=${this.value}
243
+ placeholder=${this.placeholder}
244
+ rows=${Math.max(1, this.minRows || 1)}
245
+ ?disabled=${this.effectiveDisabled}
246
+ @input=${this.onTextareaInput}
247
+ @keydown=${this.onTextareaKeyDown}
248
+ ></textarea>
249
+ <span part="trailing">
250
+ <slot name="trailing" @slotchange=${this.onTrailingSlotChange}></slot>
251
+ ${this.hasTrailingSlot ? nothing : this.renderActionButton()}
252
+ </span>
253
+ </div>
254
+ </div>
255
+ `;
256
+ }
257
+ }
258
+ __decorate([
259
+ property()
260
+ ], LyraChatComposer.prototype, "placeholder", void 0);
261
+ __decorate([
262
+ property({ type: Number, attribute: 'min-rows' })
263
+ ], LyraChatComposer.prototype, "minRows", void 0);
264
+ __decorate([
265
+ property({ type: Number, attribute: 'max-rows' })
266
+ ], LyraChatComposer.prototype, "maxRows", void 0);
267
+ __decorate([
268
+ property({ reflect: true })
269
+ ], LyraChatComposer.prototype, "status", void 0);
270
+ __decorate([
271
+ property({ type: Boolean, reflect: true, attribute: 'submit-on-enter' })
272
+ ], LyraChatComposer.prototype, "submitOnEnter", void 0);
273
+ __decorate([
274
+ state()
275
+ ], LyraChatComposer.prototype, "hasLeadingSlot", void 0);
276
+ __decorate([
277
+ state()
278
+ ], LyraChatComposer.prototype, "hasChipsSlot", void 0);
279
+ __decorate([
280
+ state()
281
+ ], LyraChatComposer.prototype, "hasTrailingSlot", void 0);
282
+ __decorate([
283
+ query('textarea')
284
+ ], LyraChatComposer.prototype, "textareaEl", void 0);
285
+ defineElement('chat-composer', LyraChatComposer);