@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
@@ -0,0 +1,121 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ export type ThinkingPanelMode = 'live' | 'post-hoc';
4
+ export interface ThinkingPanelToggleDetail {
5
+ expanded: boolean;
6
+ }
7
+ /**
8
+ * `<lyra-thinking-panel>` — a collapsible panel for an AI agent's
9
+ * intermediate reasoning/"thinking" transcript, kept visually and
10
+ * semantically distinct from its final response. Same collapsible
11
+ * header-button-plus-region shape as `<lyra-source-list>`; the default slot
12
+ * is entirely free-form (a consumer-composed `<lyra-streaming-text>`,
13
+ * `<lyra-markdown>`, or plain text) — this component has no dependency on
14
+ * either and imposes no structure on what's slotted.
15
+ *
16
+ * `mode` (`'live'` while reasoning is actively streaming in, `'post-hoc'`
17
+ * once it's complete and being reviewed after the fact) drives two concrete
18
+ * behavior differences, not just a styling hook:
19
+ * - **Header hint.** While `duration-ms` is unset, `'live'` shows a pulsing
20
+ * "Thinking…" placeholder in `[part="duration"]`; `'post-hoc'` shows
21
+ * nothing there instead (a finished review with no known duration has
22
+ * nothing useful to say in that slot). Once `duration-ms` is set, both
23
+ * modes show the same static `"Thought for …"` text — a `'post-hoc'`
24
+ * consumer that captured a duration is free to supply it too.
25
+ * - **Auto-scroll.** Only `'live'` mode auto-follows new content appended to
26
+ * the default slot while `expanded` (see below); `'post-hoc'` never
27
+ * scrolls on its own, since reviewing finished reasoning is expected to
28
+ * start from the top like reading any other completed document. This
29
+ * library otherwise defaults to *not* editorializing about a host's data
30
+ * (see `<lyra-empty>`'s plain-`description` stance), but scroll position
31
+ * is presentation, not data, so this one behavior difference earns its
32
+ * keep rather than being left as a bare visual/semantic hint the host
33
+ * would have to reimplement identically itself.
34
+ *
35
+ * Live-mode auto-scroll ("stick to bottom") is the classic chat-transcript
36
+ * convention: while `mode="live"` and `expanded`, new content keeps the
37
+ * panel scrolled to its latest line — *unless* the user has manually
38
+ * scrolled up to (re-)read earlier content, in which case their position is
39
+ * never yanked away from them. This is tracked continuously via a `scroll`
40
+ * listener on `[part="body"]` (not recomputed from the mutation itself,
41
+ * which necessarily observes the DOM only *after* it has already changed):
42
+ * every user-driven scroll records whether the body was left within
43
+ * `NEAR_BOTTOM_PX` of its own max scroll position, and only a mutation that
44
+ * arrives while that's still true triggers a follow-up scroll-to-bottom.
45
+ * Opening an already-`'live'` panel (or one that later becomes `'live'`)
46
+ * always resets this to "anchored" and jumps to the latest content, the same
47
+ * way a chat app's own transcript does when you re-open it.
48
+ *
49
+ * New content is detected via a `MutationObserver` on this element's own
50
+ * light DOM (`childList`+`subtree`+`characterData`) rather than the default
51
+ * slot's `slotchange` event, because `slotchange` only fires when the set of
52
+ * top-level assigned nodes changes — never for a text node mutating *inside*
53
+ * an already-slotted element, which is the more likely shape for streamed
54
+ * reasoning (a consumer appending chunks to an existing node's `textContent`
55
+ * rather than re-slotting a whole new element per token). The one thing this
56
+ * can't see is a mutation entirely inside a slotted custom element's own
57
+ * shadow root (e.g. a `<lyra-markdown>` re-rendering its shadow tree after a
58
+ * `content` property change) — Shadow DOM encapsulation blocks that by
59
+ * design, and there is no way for this component to reach across that
60
+ * boundary. A slotted element whose *own* internal updates should drive this
61
+ * panel's auto-scroll needs to append/mutate visible light-DOM text itself
62
+ * (as `<lyra-streaming-text>` is expected to), or the host can call this
63
+ * panel's own `scrollToBottom()` directly.
64
+ *
65
+ * `aria-controls` linking the header to the body region uses `nextId()`
66
+ * (`../../internal/a11y.js`) for a collision-safe id, the same convention
67
+ * `<lyra-source-list>` and `<lyra-widget>` already establish for every
68
+ * toggle-controls-region pairing in this library.
69
+ *
70
+ * @customElement lyra-thinking-panel
71
+ * @slot - The reasoning/thinking content.
72
+ * @event lyra-toggle - The header was activated, expanding or collapsing the
73
+ * panel. `detail: { expanded }` — same event name and shape as
74
+ * `<lyra-source-list>`'s own `lyra-toggle`.
75
+ * @csspart base - The outer container.
76
+ * @csspart header - The clickable header (`<button>`) toggling `expanded`.
77
+ * @csspart label - The `label` text.
78
+ * @csspart duration - The formatted duration / "Thinking…" placeholder, when shown.
79
+ * @csspart toggle - The chevron indicator inside the header.
80
+ * @csspart body - The wrapper around the default slot, `hidden` while collapsed.
81
+ */
82
+ export declare class LyraThinkingPanel extends LyraElement {
83
+ static styles: import("lit").CSSResultGroup[];
84
+ /** Header text. */
85
+ label: string;
86
+ /** Whether the reasoning transcript is currently shown. Starts collapsed,
87
+ * matching `<lyra-source-list>`'s default -- a consumer running `'live'`
88
+ * reasoning sets this `true` itself to stream it in view as it arrives. */
89
+ expanded: boolean;
90
+ /** `'live'` while reasoning is actively streaming in right now; `'post-hoc'`
91
+ * once it's complete and being reviewed after the fact. See the class doc
92
+ * for the concrete behavior differences this drives. */
93
+ mode: ThinkingPanelMode;
94
+ /** How long the reasoning took, in milliseconds, once known. Omitted
95
+ * entirely (nothing rendered in `'post-hoc'`, a pulsing placeholder in
96
+ * `'live'`) while unset. */
97
+ durationMs?: number;
98
+ private readonly bodyId;
99
+ private contentObserver?;
100
+ private scrollRafId?;
101
+ private stickToBottom;
102
+ connectedCallback(): void;
103
+ disconnectedCallback(): void;
104
+ protected updated(changed: PropertyValues): void;
105
+ /** Scrolls the body to its current bottom immediately (no smooth-scroll
106
+ * animation, deliberately -- see onContentMutated()'s rAF-coalescing
107
+ * comment for why an instant jump reads better than a stack of competing
108
+ * smooth scrolls under a fast token stream). Safe to call directly, e.g.
109
+ * from a host that wants to force a jump-to-latest action of its own. */
110
+ scrollToBottom(): void;
111
+ private onScroll;
112
+ private onContentMutated;
113
+ private toggle;
114
+ private get durationDisplay();
115
+ render(): TemplateResult;
116
+ }
117
+ declare global {
118
+ interface HTMLElementTagNameMap {
119
+ 'lyra-thinking-panel': LyraThinkingPanel;
120
+ }
121
+ }
@@ -0,0 +1,235 @@
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 } from 'lit';
8
+ import { property } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { nextId } from '../../internal/a11y.js';
12
+ import { chevronIcon } from '../../internal/icons.js';
13
+ import { styles } from './thinking-panel.styles.js';
14
+ /** "Close enough to the body's own max scroll position to count as anchored
15
+ * there" -- comfortably larger than one line of streamed text, so a user
16
+ * who has barely nudged the scrollbar while reading the latest line isn't
17
+ * mistaken for having deliberately scrolled away to read earlier content. */
18
+ const NEAR_BOTTOM_PX = 48;
19
+ /** `820` -> `"820ms"`; `1500` -> `"1.5s"`; `2000` -> `"2s"`. Identical
20
+ * algorithm to lyra-tool-call-chip's and lyra-tool-result-dialog's own
21
+ * `formatDuration` -- duplicated rather than imported (three independent,
22
+ * separately-consumable components) but kept in lockstep so the same
23
+ * elapsed time reads identically everywhere it's shown in this library. */
24
+ function formatDuration(ms) {
25
+ if (!Number.isFinite(ms) || ms < 1000) {
26
+ return `${Math.round(Math.max(0, ms))}ms`;
27
+ }
28
+ const seconds = ms / 1000;
29
+ const rounded = Math.round(seconds * 10) / 10;
30
+ return `${Number.isInteger(rounded) ? rounded.toFixed(0) : rounded.toFixed(1)}s`;
31
+ }
32
+ /**
33
+ * `<lyra-thinking-panel>` — a collapsible panel for an AI agent's
34
+ * intermediate reasoning/"thinking" transcript, kept visually and
35
+ * semantically distinct from its final response. Same collapsible
36
+ * header-button-plus-region shape as `<lyra-source-list>`; the default slot
37
+ * is entirely free-form (a consumer-composed `<lyra-streaming-text>`,
38
+ * `<lyra-markdown>`, or plain text) — this component has no dependency on
39
+ * either and imposes no structure on what's slotted.
40
+ *
41
+ * `mode` (`'live'` while reasoning is actively streaming in, `'post-hoc'`
42
+ * once it's complete and being reviewed after the fact) drives two concrete
43
+ * behavior differences, not just a styling hook:
44
+ * - **Header hint.** While `duration-ms` is unset, `'live'` shows a pulsing
45
+ * "Thinking…" placeholder in `[part="duration"]`; `'post-hoc'` shows
46
+ * nothing there instead (a finished review with no known duration has
47
+ * nothing useful to say in that slot). Once `duration-ms` is set, both
48
+ * modes show the same static `"Thought for …"` text — a `'post-hoc'`
49
+ * consumer that captured a duration is free to supply it too.
50
+ * - **Auto-scroll.** Only `'live'` mode auto-follows new content appended to
51
+ * the default slot while `expanded` (see below); `'post-hoc'` never
52
+ * scrolls on its own, since reviewing finished reasoning is expected to
53
+ * start from the top like reading any other completed document. This
54
+ * library otherwise defaults to *not* editorializing about a host's data
55
+ * (see `<lyra-empty>`'s plain-`description` stance), but scroll position
56
+ * is presentation, not data, so this one behavior difference earns its
57
+ * keep rather than being left as a bare visual/semantic hint the host
58
+ * would have to reimplement identically itself.
59
+ *
60
+ * Live-mode auto-scroll ("stick to bottom") is the classic chat-transcript
61
+ * convention: while `mode="live"` and `expanded`, new content keeps the
62
+ * panel scrolled to its latest line — *unless* the user has manually
63
+ * scrolled up to (re-)read earlier content, in which case their position is
64
+ * never yanked away from them. This is tracked continuously via a `scroll`
65
+ * listener on `[part="body"]` (not recomputed from the mutation itself,
66
+ * which necessarily observes the DOM only *after* it has already changed):
67
+ * every user-driven scroll records whether the body was left within
68
+ * `NEAR_BOTTOM_PX` of its own max scroll position, and only a mutation that
69
+ * arrives while that's still true triggers a follow-up scroll-to-bottom.
70
+ * Opening an already-`'live'` panel (or one that later becomes `'live'`)
71
+ * always resets this to "anchored" and jumps to the latest content, the same
72
+ * way a chat app's own transcript does when you re-open it.
73
+ *
74
+ * New content is detected via a `MutationObserver` on this element's own
75
+ * light DOM (`childList`+`subtree`+`characterData`) rather than the default
76
+ * slot's `slotchange` event, because `slotchange` only fires when the set of
77
+ * top-level assigned nodes changes — never for a text node mutating *inside*
78
+ * an already-slotted element, which is the more likely shape for streamed
79
+ * reasoning (a consumer appending chunks to an existing node's `textContent`
80
+ * rather than re-slotting a whole new element per token). The one thing this
81
+ * can't see is a mutation entirely inside a slotted custom element's own
82
+ * shadow root (e.g. a `<lyra-markdown>` re-rendering its shadow tree after a
83
+ * `content` property change) — Shadow DOM encapsulation blocks that by
84
+ * design, and there is no way for this component to reach across that
85
+ * boundary. A slotted element whose *own* internal updates should drive this
86
+ * panel's auto-scroll needs to append/mutate visible light-DOM text itself
87
+ * (as `<lyra-streaming-text>` is expected to), or the host can call this
88
+ * panel's own `scrollToBottom()` directly.
89
+ *
90
+ * `aria-controls` linking the header to the body region uses `nextId()`
91
+ * (`../../internal/a11y.js`) for a collision-safe id, the same convention
92
+ * `<lyra-source-list>` and `<lyra-widget>` already establish for every
93
+ * toggle-controls-region pairing in this library.
94
+ *
95
+ * @customElement lyra-thinking-panel
96
+ * @slot - The reasoning/thinking content.
97
+ * @event lyra-toggle - The header was activated, expanding or collapsing the
98
+ * panel. `detail: { expanded }` — same event name and shape as
99
+ * `<lyra-source-list>`'s own `lyra-toggle`.
100
+ * @csspart base - The outer container.
101
+ * @csspart header - The clickable header (`<button>`) toggling `expanded`.
102
+ * @csspart label - The `label` text.
103
+ * @csspart duration - The formatted duration / "Thinking…" placeholder, when shown.
104
+ * @csspart toggle - The chevron indicator inside the header.
105
+ * @csspart body - The wrapper around the default slot, `hidden` while collapsed.
106
+ */
107
+ export class LyraThinkingPanel extends LyraElement {
108
+ constructor() {
109
+ super(...arguments);
110
+ /** Header text. */
111
+ this.label = 'Thinking';
112
+ /** Whether the reasoning transcript is currently shown. Starts collapsed,
113
+ * matching `<lyra-source-list>`'s default -- a consumer running `'live'`
114
+ * reasoning sets this `true` itself to stream it in view as it arrives. */
115
+ this.expanded = false;
116
+ /** `'live'` while reasoning is actively streaming in right now; `'post-hoc'`
117
+ * once it's complete and being reviewed after the fact. See the class doc
118
+ * for the concrete behavior differences this drives. */
119
+ this.mode = 'live';
120
+ this.bodyId = nextId('thinking-panel-body');
121
+ // Whether the body was left within NEAR_BOTTOM_PX of its own max scroll
122
+ // position the last time a real (user-driven) scroll was recorded -- see
123
+ // onScroll(). Starts `true` so a panel that mounts already `expanded` and
124
+ // `mode="live"` follows its very first streamed content by default,
125
+ // before any scroll event has ever fired.
126
+ this.stickToBottom = true;
127
+ this.onScroll = (e) => {
128
+ const body = e.currentTarget;
129
+ this.stickToBottom = body.scrollHeight - body.scrollTop - body.clientHeight <= NEAR_BOTTOM_PX;
130
+ };
131
+ this.onContentMutated = () => {
132
+ if (this.mode !== 'live' || !this.expanded || !this.stickToBottom)
133
+ return;
134
+ // Coalesce to at most one scroll-to-bottom per animation frame -- a fast
135
+ // token stream can otherwise fire many mutation records in quick
136
+ // succession, each individually cheap to react to but wasteful (and
137
+ // visually janky) to lay out for separately.
138
+ if (this.scrollRafId !== undefined)
139
+ return;
140
+ this.scrollRafId = requestAnimationFrame(() => {
141
+ this.scrollRafId = undefined;
142
+ this.scrollToBottom();
143
+ });
144
+ };
145
+ this.toggle = () => {
146
+ this.expanded = !this.expanded;
147
+ this.emit('lyra-toggle', { expanded: this.expanded });
148
+ };
149
+ }
150
+ static { this.styles = [LyraElement.styles, styles]; }
151
+ connectedCallback() {
152
+ super.connectedCallback();
153
+ // Watches this element's own light-DOM subtree, not the shadow-DOM
154
+ // [part="body"] wrapper -- the slotted reasoning content actually lives
155
+ // here, as this element's children. See the class doc for why this
156
+ // (rather than `slotchange`) is what detects streamed-in content.
157
+ this.contentObserver = new MutationObserver(this.onContentMutated);
158
+ this.contentObserver.observe(this, { childList: true, subtree: true, characterData: true });
159
+ }
160
+ disconnectedCallback() {
161
+ super.disconnectedCallback();
162
+ this.contentObserver?.disconnect();
163
+ this.contentObserver = undefined;
164
+ if (this.scrollRafId !== undefined) {
165
+ cancelAnimationFrame(this.scrollRafId);
166
+ this.scrollRafId = undefined;
167
+ }
168
+ }
169
+ updated(changed) {
170
+ if (changed.has('expanded') && this.expanded && this.mode === 'live') {
171
+ // Opening a live panel always jumps straight to the newest content and
172
+ // resets the sticky-bottom flag, so it starts following again
173
+ // regardless of wherever the body happened to be scrolled the last
174
+ // time it was collapsed. `'post-hoc'` deliberately skips this -- see
175
+ // the class doc's Auto-scroll section.
176
+ this.stickToBottom = true;
177
+ this.scrollToBottom();
178
+ }
179
+ }
180
+ /** Scrolls the body to its current bottom immediately (no smooth-scroll
181
+ * animation, deliberately -- see onContentMutated()'s rAF-coalescing
182
+ * comment for why an instant jump reads better than a stack of competing
183
+ * smooth scrolls under a fast token stream). Safe to call directly, e.g.
184
+ * from a host that wants to force a jump-to-latest action of its own. */
185
+ scrollToBottom() {
186
+ const body = this.renderRoot.querySelector('[part="body"]');
187
+ if (!body)
188
+ return;
189
+ body.scrollTop = body.scrollHeight;
190
+ }
191
+ get durationDisplay() {
192
+ if (this.durationMs != null && Number.isFinite(this.durationMs)) {
193
+ return { text: `Thought for ${formatDuration(this.durationMs)}`, pending: false };
194
+ }
195
+ return this.mode === 'live' ? { text: 'Thinking…', pending: true } : null;
196
+ }
197
+ render() {
198
+ const duration = this.durationDisplay;
199
+ return html `
200
+ <div part="base">
201
+ <button
202
+ part="header"
203
+ type="button"
204
+ aria-expanded=${this.expanded ? 'true' : 'false'}
205
+ aria-controls=${this.bodyId}
206
+ @click=${this.toggle}
207
+ >
208
+ <span part="toggle" aria-hidden="true">${chevronIcon()}</span>
209
+ <span part="label">${this.label}</span>
210
+ ${duration
211
+ ? html `<span part="duration" ?data-pending=${duration.pending}
212
+ >${duration.pending ? html `<span class="pending-dot" aria-hidden="true"></span>` : nothing}${duration.text}</span
213
+ >`
214
+ : nothing}
215
+ </button>
216
+ <div part="body" id=${this.bodyId} ?hidden=${!this.expanded} @scroll=${this.onScroll}>
217
+ <slot></slot>
218
+ </div>
219
+ </div>
220
+ `;
221
+ }
222
+ }
223
+ __decorate([
224
+ property()
225
+ ], LyraThinkingPanel.prototype, "label", void 0);
226
+ __decorate([
227
+ property({ type: Boolean, reflect: true })
228
+ ], LyraThinkingPanel.prototype, "expanded", void 0);
229
+ __decorate([
230
+ property({ reflect: true })
231
+ ], LyraThinkingPanel.prototype, "mode", void 0);
232
+ __decorate([
233
+ property({ type: Number, attribute: 'duration-ms' })
234
+ ], LyraThinkingPanel.prototype, "durationMs", void 0);
235
+ defineElement('thinking-panel', LyraThinkingPanel);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,117 @@
1
+ import { css } from 'lit';
2
+ // Shares its collapsible-header shape (border/radius/hover/focus-ring, the
3
+ // rotating chevron) with lyra-source-list's styles almost verbatim -- the two
4
+ // are siblings in the same "collapsible region behind a header button"
5
+ // family and are meant to sit comfortably next to each other in a message.
6
+ export const styles = css `
7
+ :host {
8
+ display: block;
9
+ /* Consumer-overridable cap on how tall the reasoning transcript grows
10
+ before it scrolls internally, e.g. via ::part(body) or by overriding
11
+ this custom property directly -- not exposed as a component prop
12
+ since it's a pure layout knob, not something a template branches on. */
13
+ --lyra-thinking-panel-max-block-size: 16rem;
14
+ }
15
+ [part='base'] {
16
+ border: 1px solid var(--lyra-color-border);
17
+ border-radius: var(--lyra-radius);
18
+ background: var(--lyra-color-surface);
19
+ overflow: hidden;
20
+ }
21
+ [part='header'] {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: var(--lyra-space-xs);
25
+ inline-size: 100%;
26
+ padding: var(--lyra-space-s) var(--lyra-space-m);
27
+ border: none;
28
+ background: none;
29
+ color: var(--lyra-color-text);
30
+ font: inherit;
31
+ font-weight: 600;
32
+ font-size: 0.875rem;
33
+ text-align: start;
34
+ cursor: pointer;
35
+ }
36
+ [part='header']:hover {
37
+ background: var(--lyra-color-brand-quiet);
38
+ color: var(--lyra-color-brand);
39
+ }
40
+ [part='header']:focus-visible {
41
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
42
+ outline-offset: calc(-1 * var(--lyra-focus-ring-offset));
43
+ }
44
+ [part='toggle'] {
45
+ display: inline-flex;
46
+ flex: 0 0 auto;
47
+ transition: transform var(--lyra-transition-fast);
48
+ }
49
+ :host([expanded]) [part='toggle'] {
50
+ transform: rotate(90deg);
51
+ }
52
+ [part='label'] {
53
+ flex: 1 1 auto;
54
+ min-inline-size: 0;
55
+ overflow: hidden;
56
+ text-overflow: ellipsis;
57
+ white-space: nowrap;
58
+ }
59
+ [part='duration'] {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ gap: 0.35em;
63
+ flex: 0 0 auto;
64
+ font-weight: 400;
65
+ font-size: 0.8125rem;
66
+ color: var(--lyra-color-text-quiet);
67
+ }
68
+ /* Actively streaming with no duration yet -- a full-opacity brand-colored
69
+ label plus a separate decorative pulse dot, deliberately *not* animating
70
+ the text's own opacity the way lyra-typing-indicator's pulse variant
71
+ does: that dims it well below AA contrast for part of every cycle, fine
72
+ for a purely decorative shape but not for text carrying real content
73
+ ("Thinking…"). */
74
+ [part='duration'][data-pending] {
75
+ color: var(--lyra-color-brand);
76
+ }
77
+ .pending-dot {
78
+ inline-size: 0.375rem;
79
+ block-size: 0.375rem;
80
+ border-radius: 50%;
81
+ background: currentColor;
82
+ animation: lyra-thinking-panel-pulse var(--lyra-transition-base) infinite;
83
+ }
84
+ @keyframes lyra-thinking-panel-pulse {
85
+ 0%,
86
+ 100% {
87
+ opacity: 0.4;
88
+ transform: scale(0.85);
89
+ }
90
+ 50% {
91
+ opacity: 1;
92
+ transform: scale(1);
93
+ }
94
+ }
95
+ [part='body'] {
96
+ max-block-size: var(--lyra-thinking-panel-max-block-size);
97
+ overflow-y: auto;
98
+ padding: var(--lyra-space-m);
99
+ border-block-start: 1px solid var(--lyra-color-border);
100
+ color: var(--lyra-color-text-quiet);
101
+ font-size: 0.875rem;
102
+ line-height: 1.5;
103
+ }
104
+ [part='body'][hidden] {
105
+ display: none;
106
+ }
107
+ @media (prefers-reduced-motion: reduce) {
108
+ [part='toggle'] {
109
+ transition: none !important;
110
+ }
111
+ .pending-dot {
112
+ animation: none !important;
113
+ opacity: 1;
114
+ transform: none;
115
+ }
116
+ }
117
+ `;
@@ -36,6 +36,7 @@ export declare class LyraToastItem extends LyraElement {
36
36
  private showRafId?;
37
37
  private showAnimTimer?;
38
38
  private hideAnimTimer?;
39
+ private resolvePendingHide?;
39
40
  private hovering;
40
41
  private focused;
41
42
  private hiding;
@@ -76,8 +76,12 @@ export class LyraToastItem extends LyraElement {
76
76
  if (!isFinite(this.duration) || this.duration <= 0)
77
77
  return;
78
78
  const remaining = this.duration - this.elapsedMs;
79
- if (remaining <= 0)
79
+ // A duration shortened below the already-elapsed time must hide promptly,
80
+ // not silently never schedule anything.
81
+ if (remaining <= 0) {
82
+ void this.hide();
80
83
  return;
84
+ }
81
85
  this.startedAt = performance.now();
82
86
  this.timer = window.setTimeout(() => this.hide(), remaining);
83
87
  };
@@ -124,11 +128,12 @@ export class LyraToastItem extends LyraElement {
124
128
  // `elapsedMs`/`duration` are re-read fresh every time the timer is
125
129
  // (re)scheduled, so a `duration` change while paused (hovering/focused)
126
130
  // or before the timer has ever started needs no action here -- the next
127
- // resumeTimer()/startTimer() call already picks up the new value. Only a
128
- // timer that's actively counting down right now is running against a
129
- // setTimeout scheduled for the *old* duration, so that's the one case
130
- // that needs an explicit reschedule.
131
- if (changed.has('duration') && this.timerStarted && this.timer !== undefined) {
131
+ // resumeTimer()/startTimer() call already picks up the new value.
132
+ // Re-evaluate on any duration change once the show sequence has started,
133
+ // not only while a timer is already actively counting down -- this also
134
+ // covers duration flipping from disabled (0/Infinity) back to a positive
135
+ // value, which previously never had `this.timer !== undefined` to gate on.
136
+ if (changed.has('duration') && this.timerStarted && !this.hovering && !this.focused) {
132
137
  this.pauseTimer();
133
138
  this.resumeTimer();
134
139
  }
@@ -136,6 +141,11 @@ export class LyraToastItem extends LyraElement {
136
141
  firstUpdated() {
137
142
  this.showRafId = requestAnimationFrame(() => {
138
143
  this.showRafId = undefined;
144
+ // hide() may have already run synchronously before this frame fired
145
+ // (e.g. a caller creates the toast and immediately dismisses it) --
146
+ // don't resurrect the show sequence on top of an already-hiding item.
147
+ if (this.hiding)
148
+ return;
139
149
  this.setAttribute('data-visible', '');
140
150
  this.emit('lyra-show');
141
151
  this.showAnimTimer = window.setTimeout(() => {
@@ -158,6 +168,12 @@ export class LyraToastItem extends LyraElement {
158
168
  if (this.hideAnimTimer !== undefined) {
159
169
  clearTimeout(this.hideAnimTimer);
160
170
  this.hideAnimTimer = undefined;
171
+ // hide()'s own promise is awaiting this timer firing to resolve() --
172
+ // a disconnect (e.g. the toast's ancestor is removed from the DOM,
173
+ // bypassing this.remove()) must still let that promise settle instead
174
+ // of leaving it pending forever.
175
+ this.resolvePendingHide?.();
176
+ this.resolvePendingHide = undefined;
161
177
  }
162
178
  this.clearTimer();
163
179
  }
@@ -203,11 +219,20 @@ export class LyraToastItem extends LyraElement {
203
219
  this.emit('lyra-hide');
204
220
  this.removeAttribute('data-visible');
205
221
  await new Promise((resolve) => {
222
+ this.resolvePendingHide = resolve;
206
223
  this.hideAnimTimer = window.setTimeout(() => {
207
224
  this.hideAnimTimer = undefined;
225
+ this.resolvePendingHide = undefined;
208
226
  resolve();
209
227
  }, animMs());
210
228
  });
229
+ // An out-of-band disconnect (see disconnectedCallback) resolves this same
230
+ // promise so callers awaiting hide() never hang, but it does so *without*
231
+ // this having reached its normal completion -- guard against then still
232
+ // emitting a "finished hiding" event (and redundantly calling remove())
233
+ // for a node that's already been torn down some other way.
234
+ if (!this.isConnected)
235
+ return;
211
236
  this.emit('lyra-after-hide');
212
237
  this.remove();
213
238
  }