@k2b/ui 0.28.0 → 0.30.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 (278) hide show
  1. package/README.md +1 -1
  2. package/dist/base.css +2 -2
  3. package/dist/browser/actions/ContextMenu.js +5 -0
  4. package/dist/browser/actions/ContextMenu.js.map +1 -1
  5. package/dist/browser/actions/DragAndDrop.js +89 -0
  6. package/dist/browser/actions/DragAndDrop.js.map +1 -0
  7. package/dist/browser/actions/Dropdown.js +6 -3
  8. package/dist/browser/actions/Dropdown.js.map +1 -1
  9. package/dist/browser/actions/FilterChip.js +137 -7
  10. package/dist/browser/actions/FilterChip.js.map +1 -1
  11. package/dist/browser/actions/GestureMenu.js +26 -3
  12. package/dist/browser/actions/GestureMenu.js.map +1 -1
  13. package/dist/browser/actions/SegmentedControl.js +5 -9
  14. package/dist/browser/actions/SegmentedControl.js.map +1 -1
  15. package/dist/browser/actions/SpotlightSearch.js +6 -3
  16. package/dist/browser/actions/SpotlightSearch.js.map +1 -1
  17. package/dist/browser/actions/Tabs.js +5 -2
  18. package/dist/browser/actions/Tabs.js.map +1 -1
  19. package/dist/browser/actions/ViewTabs.js +294 -0
  20. package/dist/browser/actions/ViewTabs.js.map +1 -0
  21. package/dist/browser/actions/drag-and-drop-messages.js +75 -0
  22. package/dist/browser/actions/drag-and-drop-messages.js.map +1 -0
  23. package/dist/browser/actions/drag-and-drop.js +692 -0
  24. package/dist/browser/actions/drag-and-drop.js.map +1 -0
  25. package/dist/browser/actions/filter-messages.js +6 -2
  26. package/dist/browser/actions/filter-messages.js.map +1 -1
  27. package/dist/browser/actions/index.js +3 -1
  28. package/dist/browser/actions/index.js.map +1 -1
  29. package/dist/browser/chat/ChatComposer.js +1229 -605
  30. package/dist/browser/chat/ChatComposer.js.map +1 -1
  31. package/dist/browser/chat/ChatDecision.js +814 -0
  32. package/dist/browser/chat/ChatDecision.js.map +1 -0
  33. package/dist/browser/chat/ChatPrimitives.js +292 -153
  34. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  35. package/dist/browser/chat/ChatTimeline.js.map +1 -1
  36. package/dist/browser/chat/chat-messages.js +14 -0
  37. package/dist/browser/chat/chat-messages.js.map +1 -1
  38. package/dist/browser/chat/command-search.js +35 -17
  39. package/dist/browser/chat/command-search.js.map +1 -1
  40. package/dist/browser/chat/index.js +10 -3
  41. package/dist/browser/chat/index.js.map +1 -1
  42. package/dist/browser/chat/types.js.map +1 -1
  43. package/dist/browser/content/Calendar.js +146 -119
  44. package/dist/browser/content/Calendar.js.map +1 -1
  45. package/dist/browser/content/CalendarWeekView.js +291 -266
  46. package/dist/browser/content/CalendarWeekView.js.map +1 -1
  47. package/dist/browser/content/Chart.js +54 -71
  48. package/dist/browser/content/Chart.js.map +1 -1
  49. package/dist/browser/content/DataTable.js +224 -155
  50. package/dist/browser/content/DataTable.js.map +1 -1
  51. package/dist/browser/content/FileBrowser.js +2 -2
  52. package/dist/browser/content/FileBrowser.js.map +1 -1
  53. package/dist/browser/content/FileTree.js +42 -36
  54. package/dist/browser/content/FileTree.js.map +1 -1
  55. package/dist/browser/content/FileTypeIcon.js +72 -0
  56. package/dist/browser/content/FileTypeIcon.js.map +1 -0
  57. package/dist/browser/content/FileView.js +2 -2
  58. package/dist/browser/content/FileView.js.map +1 -1
  59. package/dist/browser/content/ReferenceChip.js +162 -0
  60. package/dist/browser/content/ReferenceChip.js.map +1 -0
  61. package/dist/browser/content/calendar-pointer.js +9 -0
  62. package/dist/browser/content/calendar-pointer.js.map +1 -1
  63. package/dist/browser/content/calendar-time-view.js +6 -3
  64. package/dist/browser/content/calendar-time-view.js.map +1 -1
  65. package/dist/browser/content/data-table-messages.js +4 -2
  66. package/dist/browser/content/data-table-messages.js.map +1 -1
  67. package/dist/browser/content/index.js +2 -0
  68. package/dist/browser/content/index.js.map +1 -1
  69. package/dist/browser/feedback/MovedHint.js +197 -0
  70. package/dist/browser/feedback/MovedHint.js.map +1 -0
  71. package/dist/browser/feedback/dialog-core.js +66 -9
  72. package/dist/browser/feedback/dialog-core.js.map +1 -1
  73. package/dist/browser/feedback/index.js +1 -0
  74. package/dist/browser/feedback/index.js.map +1 -1
  75. package/dist/browser/feedback/prompts.js +6 -1
  76. package/dist/browser/feedback/prompts.js.map +1 -1
  77. package/dist/browser/inputs/DatePicker.js +337 -212
  78. package/dist/browser/inputs/DatePicker.js.map +1 -1
  79. package/dist/browser/inputs/date-picker.js +59 -5
  80. package/dist/browser/inputs/date-picker.js.map +1 -1
  81. package/dist/browser/intl/messages.js +12 -0
  82. package/dist/browser/intl/messages.js.map +1 -1
  83. package/dist/browser/intl/weekend.js +39 -0
  84. package/dist/browser/intl/weekend.js.map +1 -0
  85. package/dist/browser/layout/AppOverview.js +1 -2
  86. package/dist/browser/layout/AppOverview.js.map +1 -1
  87. package/dist/browser/layout/AppWorkspace.js +947 -531
  88. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  89. package/dist/browser/layout/PageTitle.js +3 -2
  90. package/dist/browser/layout/PageTitle.js.map +1 -1
  91. package/dist/browser/layout/PanelDialog.js +88 -86
  92. package/dist/browser/layout/PanelDialog.js.map +1 -1
  93. package/dist/browser/layout/ResizeSeparator.js +81 -0
  94. package/dist/browser/layout/ResizeSeparator.js.map +1 -0
  95. package/dist/browser/layout/SettingsModal.js +2 -2
  96. package/dist/browser/layout/SettingsModal.js.map +1 -1
  97. package/dist/browser/layout/SidebarSwitcher.js +19 -11
  98. package/dist/browser/layout/SidebarSwitcher.js.map +1 -1
  99. package/dist/browser/layout/TabBar.js +16 -13
  100. package/dist/browser/layout/TabBar.js.map +1 -1
  101. package/dist/browser/layout/app-host-width.js +44 -0
  102. package/dist/browser/layout/app-host-width.js.map +1 -0
  103. package/dist/browser/layout/app-workspace-controller.js +337 -44
  104. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  105. package/dist/browser/layout/index.js +4 -1
  106. package/dist/browser/layout/index.js.map +1 -1
  107. package/dist/browser/layout/resize-separator.js +90 -0
  108. package/dist/browser/layout/resize-separator.js.map +1 -0
  109. package/dist/browser/layout/workspace-messages.js +12 -2
  110. package/dist/browser/layout/workspace-messages.js.map +1 -1
  111. package/dist/browser/surfaces/LinkCard.js +1 -1
  112. package/dist/browser/surfaces/LinkCard.js.map +1 -1
  113. package/dist/browser/surfaces/Placeholder.js +45 -11
  114. package/dist/browser/surfaces/Placeholder.js.map +1 -1
  115. package/dist/browser/surfaces/StatCell.js +23 -0
  116. package/dist/browser/surfaces/StatCell.js.map +1 -1
  117. package/dist/browser/widgets/Widget.js +1 -1
  118. package/dist/browser/widgets/Widget.js.map +1 -1
  119. package/dist/ssr/actions/ContextMenu.js +5 -0
  120. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  121. package/dist/ssr/actions/DragAndDrop.js +63 -0
  122. package/dist/ssr/actions/DragAndDrop.js.map +1 -0
  123. package/dist/ssr/actions/Dropdown.js +5 -2
  124. package/dist/ssr/actions/Dropdown.js.map +1 -1
  125. package/dist/ssr/actions/FilterChip.js +59 -7
  126. package/dist/ssr/actions/FilterChip.js.map +1 -1
  127. package/dist/ssr/actions/GestureMenu.js +26 -3
  128. package/dist/ssr/actions/GestureMenu.js.map +1 -1
  129. package/dist/ssr/actions/SegmentedControl.js +1 -2
  130. package/dist/ssr/actions/SegmentedControl.js.map +1 -1
  131. package/dist/ssr/actions/SpotlightSearch.js +2 -2
  132. package/dist/ssr/actions/SpotlightSearch.js.map +1 -1
  133. package/dist/ssr/actions/Tabs.js +2 -2
  134. package/dist/ssr/actions/Tabs.js.map +1 -1
  135. package/dist/ssr/actions/ViewTabs.js +211 -0
  136. package/dist/ssr/actions/ViewTabs.js.map +1 -0
  137. package/dist/ssr/actions/drag-and-drop-messages.js +75 -0
  138. package/dist/ssr/actions/drag-and-drop-messages.js.map +1 -0
  139. package/dist/ssr/actions/drag-and-drop.js +692 -0
  140. package/dist/ssr/actions/drag-and-drop.js.map +1 -0
  141. package/dist/ssr/actions/filter-messages.js +6 -2
  142. package/dist/ssr/actions/filter-messages.js.map +1 -1
  143. package/dist/ssr/actions/index.js +3 -1
  144. package/dist/ssr/actions/index.js.map +1 -1
  145. package/dist/ssr/chat/ChatComposer.js +668 -136
  146. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  147. package/dist/ssr/chat/ChatDecision.js +479 -0
  148. package/dist/ssr/chat/ChatDecision.js.map +1 -0
  149. package/dist/ssr/chat/ChatPrimitives.js +95 -24
  150. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  151. package/dist/ssr/chat/ChatTimeline.js.map +1 -1
  152. package/dist/ssr/chat/chat-messages.js +14 -0
  153. package/dist/ssr/chat/chat-messages.js.map +1 -1
  154. package/dist/ssr/chat/command-search.js +35 -17
  155. package/dist/ssr/chat/command-search.js.map +1 -1
  156. package/dist/ssr/chat/index.js +10 -3
  157. package/dist/ssr/chat/index.js.map +1 -1
  158. package/dist/ssr/chat/types.js.map +1 -1
  159. package/dist/ssr/content/Calendar.js +112 -103
  160. package/dist/ssr/content/Calendar.js.map +1 -1
  161. package/dist/ssr/content/CalendarWeekView.js +42 -28
  162. package/dist/ssr/content/CalendarWeekView.js.map +1 -1
  163. package/dist/ssr/content/Chart.js +19 -23
  164. package/dist/ssr/content/Chart.js.map +1 -1
  165. package/dist/ssr/content/DataTable.js +78 -31
  166. package/dist/ssr/content/DataTable.js.map +1 -1
  167. package/dist/ssr/content/FileBrowser.js +2 -2
  168. package/dist/ssr/content/FileBrowser.js.map +1 -1
  169. package/dist/ssr/content/FileTree.js +21 -14
  170. package/dist/ssr/content/FileTree.js.map +1 -1
  171. package/dist/ssr/content/FileTypeIcon.js +50 -0
  172. package/dist/ssr/content/FileTypeIcon.js.map +1 -0
  173. package/dist/ssr/content/FileView.js +2 -2
  174. package/dist/ssr/content/FileView.js.map +1 -1
  175. package/dist/ssr/content/ReferenceChip.js +121 -0
  176. package/dist/ssr/content/ReferenceChip.js.map +1 -0
  177. package/dist/ssr/content/calendar-pointer.js +9 -0
  178. package/dist/ssr/content/calendar-pointer.js.map +1 -1
  179. package/dist/ssr/content/calendar-time-view.js +6 -3
  180. package/dist/ssr/content/calendar-time-view.js.map +1 -1
  181. package/dist/ssr/content/data-table-messages.js +4 -2
  182. package/dist/ssr/content/data-table-messages.js.map +1 -1
  183. package/dist/ssr/content/index.js +2 -0
  184. package/dist/ssr/content/index.js.map +1 -1
  185. package/dist/ssr/feedback/MovedHint.js +166 -0
  186. package/dist/ssr/feedback/MovedHint.js.map +1 -0
  187. package/dist/ssr/feedback/dialog-core.js +66 -9
  188. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  189. package/dist/ssr/feedback/index.js +1 -0
  190. package/dist/ssr/feedback/index.js.map +1 -1
  191. package/dist/ssr/feedback/prompts.js +6 -1
  192. package/dist/ssr/feedback/prompts.js.map +1 -1
  193. package/dist/ssr/inputs/DatePicker.js +138 -69
  194. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  195. package/dist/ssr/inputs/date-picker.js +59 -5
  196. package/dist/ssr/inputs/date-picker.js.map +1 -1
  197. package/dist/ssr/intl/messages.js +12 -0
  198. package/dist/ssr/intl/messages.js.map +1 -1
  199. package/dist/ssr/intl/weekend.js +39 -0
  200. package/dist/ssr/intl/weekend.js.map +1 -0
  201. package/dist/ssr/layout/AppOverview.js +1 -2
  202. package/dist/ssr/layout/AppOverview.js.map +1 -1
  203. package/dist/ssr/layout/AppWorkspace.js +457 -116
  204. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  205. package/dist/ssr/layout/PageTitle.js +3 -2
  206. package/dist/ssr/layout/PageTitle.js.map +1 -1
  207. package/dist/ssr/layout/PanelDialog.js +7 -6
  208. package/dist/ssr/layout/PanelDialog.js.map +1 -1
  209. package/dist/ssr/layout/ResizeSeparator.js +45 -0
  210. package/dist/ssr/layout/ResizeSeparator.js.map +1 -0
  211. package/dist/ssr/layout/SettingsModal.js +2 -2
  212. package/dist/ssr/layout/SettingsModal.js.map +1 -1
  213. package/dist/ssr/layout/SidebarSwitcher.js +19 -11
  214. package/dist/ssr/layout/SidebarSwitcher.js.map +1 -1
  215. package/dist/ssr/layout/TabBar.js +2 -2
  216. package/dist/ssr/layout/TabBar.js.map +1 -1
  217. package/dist/ssr/layout/app-host-width.js +44 -0
  218. package/dist/ssr/layout/app-host-width.js.map +1 -0
  219. package/dist/ssr/layout/app-workspace-controller.js +337 -44
  220. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  221. package/dist/ssr/layout/index.js +4 -1
  222. package/dist/ssr/layout/index.js.map +1 -1
  223. package/dist/ssr/layout/resize-separator.js +90 -0
  224. package/dist/ssr/layout/resize-separator.js.map +1 -0
  225. package/dist/ssr/layout/workspace-messages.js +12 -2
  226. package/dist/ssr/layout/workspace-messages.js.map +1 -1
  227. package/dist/ssr/surfaces/LinkCard.js +1 -1
  228. package/dist/ssr/surfaces/LinkCard.js.map +1 -1
  229. package/dist/ssr/surfaces/Placeholder.js +25 -6
  230. package/dist/ssr/surfaces/Placeholder.js.map +1 -1
  231. package/dist/ssr/surfaces/StatCell.js +23 -0
  232. package/dist/ssr/surfaces/StatCell.js.map +1 -1
  233. package/dist/ssr/widgets/Widget.js +1 -1
  234. package/dist/ssr/widgets/Widget.js.map +1 -1
  235. package/dist/styles.css +1 -1
  236. package/dist/types/actions/ContextMenu.d.ts +1 -0
  237. package/dist/types/actions/DragAndDrop.d.ts +29 -0
  238. package/dist/types/actions/Dropdown.d.ts +4 -0
  239. package/dist/types/actions/FilterChip.d.ts +22 -0
  240. package/dist/types/actions/ViewTabs.d.ts +30 -0
  241. package/dist/types/actions/drag-and-drop-messages.d.ts +35 -0
  242. package/dist/types/actions/drag-and-drop.d.ts +36 -0
  243. package/dist/types/actions/filter-messages.d.ts +2 -0
  244. package/dist/types/actions/index.d.ts +6 -2
  245. package/dist/types/chat/ChatComposer.d.ts +71 -1
  246. package/dist/types/chat/ChatDecision.d.ts +149 -0
  247. package/dist/types/chat/ChatPrimitives.d.ts +14 -1
  248. package/dist/types/chat/ChatTimeline.d.ts +2 -1
  249. package/dist/types/chat/chat-messages.d.ts +7 -0
  250. package/dist/types/chat/command-search.d.ts +5 -0
  251. package/dist/types/chat/index.d.ts +28 -5
  252. package/dist/types/chat/types.d.ts +54 -1
  253. package/dist/types/content/Chart.d.ts +9 -3
  254. package/dist/types/content/DataTable.d.ts +3 -0
  255. package/dist/types/content/FileTypeIcon.d.ts +27 -0
  256. package/dist/types/content/ReferenceChip.d.ts +49 -0
  257. package/dist/types/content/calendar-pointer.d.ts +6 -0
  258. package/dist/types/content/data-table-messages.d.ts +1 -0
  259. package/dist/types/content/index.d.ts +3 -0
  260. package/dist/types/feedback/MovedHint.d.ts +25 -0
  261. package/dist/types/feedback/dialog-core.d.ts +5 -2
  262. package/dist/types/feedback/index.d.ts +2 -0
  263. package/dist/types/feedback/prompts.d.ts +2 -0
  264. package/dist/types/inputs/DatePicker.d.ts +7 -0
  265. package/dist/types/inputs/date-picker.d.ts +25 -1
  266. package/dist/types/intl/messages.d.ts +6 -0
  267. package/dist/types/intl/weekend.d.ts +12 -0
  268. package/dist/types/layout/AppWorkspace.d.ts +48 -10
  269. package/dist/types/layout/ResizeSeparator.d.ts +9 -0
  270. package/dist/types/layout/TabBar.d.ts +7 -0
  271. package/dist/types/layout/app-host-width.d.ts +6 -0
  272. package/dist/types/layout/app-workspace-controller.d.ts +2 -0
  273. package/dist/types/layout/index.d.ts +5 -2
  274. package/dist/types/layout/resize-separator.d.ts +18 -0
  275. package/dist/types/layout/workspace-messages.d.ts +5 -0
  276. package/dist/types/surfaces/Placeholder.d.ts +2 -1
  277. package/dist/types/surfaces/StatCell.d.ts +6 -0
  278. package/package.json +5 -5
@@ -17,42 +17,44 @@ var _tmpl$ = ["<span><strong", ">", "</strong></span>"],
17
17
  _tmpl$0 = "<textarea class=\"k2b-chat-composer__measure\" rows=\"1\" tabindex=\"-1\" aria-hidden=\"true\" readonly></textarea>",
18
18
  _tmpl$1 = ["<div class=\"k2b-chat-composer__input\" role=\"group\"", ">", "<textarea class=\"", "\" rows=\"1\"", "", "></textarea>", "</div>"],
19
19
  _tmpl$10 = ["<span class=\"", "\">", "</span>"],
20
- _tmpl$11 = ["<small", ">", "</small>"],
20
+ _tmpl$11 = ["<span class=\"k2b-chat-composer__command-detail\"", "><small", ">", "</small><small", "><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>", "</small></span>"],
21
21
  _tmpl$12 = ["<span class=\"k2b-chat-composer__command-end\"><i class=\"ti ti-check\" aria-hidden=\"true\"></i><span class=\"k2b-sr-only\">", "</span></span>"],
22
22
  _tmpl$13 = ["<button class=\"k2b-chat-composer__command\"", " type=\"button\" role=\"option\" tabindex=\"-1\"", "><i", " aria-hidden=\"true\"></i><span class=\"k2b-chat-composer__command-text\"><strong>", "</strong>", "</span>", "</button>"],
23
23
  _tmpl$14 = ["<b>", "</b>"],
24
- _tmpl$15 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "</div></div>"],
25
- _tmpl$16 = ["<div role=\"status\" class=\"k2b-chat-composer__loading\"><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i><strong>", "</strong></div>"],
26
- _tmpl$17 = ["<div role=\"status\"><i class=\"ti ti-alert-circle\"></i> ", "</div>"],
27
- _tmpl$18 = ["<div role=\"status\">", "</div>"],
28
- _tmpl$19 = ["<div class=\"k2b-chat-composer-accessory\" data-commands><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "", "", "", "</div></div>"],
29
- _tmpl$20 = ["<div class=\"k2b-chat-composer-slot\"", "><div style=\"", "\"", ">", "</div>", "", "</div>"],
30
- _tmpl$21 = ["<div class=\"k2b-chat-composer__hint\" aria-live=\"polite\">", "</div>"],
31
- _tmpl$22 = ["<div class=\"k2b-chat-composer__attachments\" role=\"list\"", " tabindex=\"0\">", "</div>"],
32
- _tmpl$23 = ["<div class=\"k2b-chat-composer__error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</div>"],
33
- _tmpl$24 = ["<div class=\"k2b-chat-composer__row\">", "", "<div class=\"k2b-chat-composer__field\">", "<div class=\"k2b-chat-composer__field-tools\">", "", "", "</div></div>", "</div>"],
34
- _tmpl$25 = ["<div class=\"k2b-chat-composer-shell\">", "<section class=\"", "\"", " role=\"group\"", ">", "", "", "", "</section><div class=\"k2b-sr-only\" role=\"status\" aria-live=\"polite\">", "</div><div class=\"k2b-chat-composer__status\" role=\"status\">", "", "</div></div>"],
35
- _tmpl$26 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><span class=\"k2b-chat-composer__emoji-glyph\" aria-hidden=\"true\">", "</span><strong>", "</strong>", "</button>"],
36
- _tmpl$27 = ["<small>:", ":</small>"],
37
- _tmpl$28 = "<i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>",
38
- _tmpl$29 = "<div class=\"k2b-chat-composer__command-placeholder\" aria-hidden=\"true\"><span></span><span><span></span><span></span></span></div>",
39
- _tmpl$30 = ["<div class=\"k2b-chat-composer__command-group\" role=\"group\"", "><div class=\"k2b-chat-composer__command-heading\"", " role=\"presentation\"><span>", "</span>", "</div>", "", "</div>"],
40
- _tmpl$31 = ["<span class=\"k2b-chat-composer__hint-text\">", "</span>"],
41
- _tmpl$32 = ["<span class=\"k2b-chat-composer__dictation\"", "><i", " aria-hidden=\"true\"></i><span>", "</span></span>"],
42
- _tmpl$33 = ["<button type=\"button\" class=\"k2b-chat-composer__hint-action\"", ">", "</button>"],
43
- _tmpl$34 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-remove\"", "", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
44
- _tmpl$35 = ["<div class=\"k2b-chat-composer__attachment\" role=\"listitem\"><div class=\"", "\">", "", "</div>", "</div>"],
45
- _tmpl$36 = ["<img", ">"],
46
- _tmpl$37 = ["<div class=\"k2b-chat-composer__attachment-identity\">", "", "</div>"],
47
- _tmpl$38 = ["<a class=\"k2b-chat-composer__attachment-link\"", " target=\"_blank\" rel=\"noopener noreferrer\"", ">", "", "</a>"],
48
- _tmpl$39 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-action\"", ">", "", "</button>"],
49
- _tmpl$40 = ["<footer class=\"k2b-chat-composer__footer\">", "</footer>"],
50
- _tmpl$41 = ["<div class=\"k2b-chat-composer__tools\">", "", "", "", "", "", "", "</div>"],
51
- _tmpl$42 = ["<div class=\"k2b-chat-composer__submit\">", "", "", "", "", "</div>"],
52
- _tmpl$43 = ["<span>", "</span>"],
53
- _tmpl$44 = ["<div class=\"k2b-chat-composer__status-line\"", "><i aria-hidden=\"true\"", "></i><span>", "</span></div>"],
54
- _tmpl$45 = ["<span class=\"k2b-sr-only\">", "</span>"];
55
- import { batch, children, createEffect, createMemo, createSignal, createUniqueId, For, onCleanup, onMount, Show, untrack } from "solid-js";
24
+ _tmpl$15 = ["<small", ">", "</small>"],
25
+ _tmpl$16 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "</div></div>"],
26
+ _tmpl$17 = ["<label class=\"k2b-chat-composer__command-search\"><i class=\"ti ti-search\" aria-hidden=\"true\"></i><input type=\"text\" role=\"combobox\"", " aria-autocomplete=\"list\"", " aria-expanded=\"true\"", " autocomplete=\"off\" autocapitalize=\"off\" spellcheck=\"false\"", "></label>"],
27
+ _tmpl$18 = ["<div role=\"status\" class=\"k2b-chat-composer__loading\"><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i><strong>", "</strong></div>"],
28
+ _tmpl$19 = ["<div role=\"status\"><i class=\"ti ti-alert-circle\"></i> ", "</div>"],
29
+ _tmpl$20 = ["<div role=\"status\">", "</div>"],
30
+ _tmpl$21 = ["<div class=\"k2b-chat-composer-accessory\" data-commands", ">", "<div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "", "", "", "", "</div></div>"],
31
+ _tmpl$22 = ["<div class=\"k2b-chat-composer-slot\"", "><div style=\"", "\"", ">", "</div>", "", "</div>"],
32
+ _tmpl$23 = ["<div class=\"k2b-chat-composer__hint\" aria-live=\"polite\">", "</div>"],
33
+ _tmpl$24 = ["<div class=\"k2b-chat-composer__attachments\" role=\"list\"", " tabindex=\"0\">", "</div>"],
34
+ _tmpl$25 = ["<div class=\"k2b-chat-composer__error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</div>"],
35
+ _tmpl$26 = ["<div class=\"k2b-chat-composer__row\">", "", "<div class=\"k2b-chat-composer__field\">", "<div class=\"k2b-chat-composer__field-tools\">", "", "", "</div></div>", "</div>"],
36
+ _tmpl$27 = ["<div class=\"k2b-chat-composer-shell\">", "<section class=\"", "\"", " role=\"group\"", ">", "", "", "", "</section><div class=\"k2b-sr-only\" role=\"status\" aria-live=\"polite\">", "</div><div class=\"k2b-chat-composer__status\" role=\"status\">", "", "</div></div>"],
37
+ _tmpl$28 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><span class=\"k2b-chat-composer__emoji-glyph\" aria-hidden=\"true\">", "</span><strong>", "</strong>", "</button>"],
38
+ _tmpl$29 = ["<small>:", ":</small>"],
39
+ _tmpl$30 = "<i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>",
40
+ _tmpl$31 = ["<div class=\"k2b-chat-composer__command-group\" role=\"group\"", "><div class=\"k2b-chat-composer__command-heading\"", " role=\"presentation\"><span>", "</span>", "</div>", "", "</div>"],
41
+ _tmpl$32 = "<div class=\"k2b-chat-composer__command-placeholder\" aria-hidden=\"true\"><span></span><span><span></span><span></span></span></div>",
42
+ _tmpl$33 = ["<span class=\"k2b-chat-composer__hint-text\">", "</span>"],
43
+ _tmpl$34 = ["<span class=\"k2b-chat-composer__dictation\"", "><i", " aria-hidden=\"true\"></i><span>", "</span></span>"],
44
+ _tmpl$35 = ["<button type=\"button\" class=\"k2b-chat-composer__hint-action\"", ">", "</button>"],
45
+ _tmpl$36 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-remove\"", "", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
46
+ _tmpl$37 = ["<div class=\"k2b-chat-composer__attachment\" role=\"listitem\"><div class=\"", "\">", "", "</div>", "</div>"],
47
+ _tmpl$38 = ["<img", ">"],
48
+ _tmpl$39 = ["<div class=\"k2b-chat-composer__attachment-identity\">", "", "</div>"],
49
+ _tmpl$40 = ["<a class=\"k2b-chat-composer__attachment-link\"", " target=\"_blank\" rel=\"noopener noreferrer\"", ">", "", "</a>"],
50
+ _tmpl$41 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-action\"", ">", "", "</button>"],
51
+ _tmpl$42 = ["<footer class=\"k2b-chat-composer__footer\">", "</footer>"],
52
+ _tmpl$43 = ["<div class=\"k2b-chat-composer__tools\">", "", "", "", "", "", "", "</div>"],
53
+ _tmpl$44 = ["<div class=\"k2b-chat-composer__submit\">", "", "", "", "", "</div>"],
54
+ _tmpl$45 = ["<span>", "</span>"],
55
+ _tmpl$46 = ["<div class=\"k2b-chat-composer__status-line\"", "><i aria-hidden=\"true\"", "></i><span>", "</span></div>"],
56
+ _tmpl$47 = ["<span class=\"k2b-sr-only\">", "</span>"];
57
+ import { batch, children, createEffect, createMemo, createSignal, createUniqueId, For, Index, on, onCleanup, onMount, Show, untrack } from "solid-js";
56
58
  import { Dropdown } from "../actions/Dropdown";
57
59
  import { dialogHistorySettled, holdDialogHistory } from "../feedback/dialog-core";
58
60
  import { Tooltip } from "../feedback/Tooltip";
@@ -78,7 +80,17 @@ const emojiSuggestionLimit = 8;
78
80
  // Keep editor undo text within approximately 2 MiB, plus the current edit.
79
81
  const composerUndoBudgetChars = 1_000_000;
80
82
 
81
- /** An action can cancel and restore its token, or report a short completion message. */
83
+ /**
84
+ * An action can cancel and restore its token, report a short completion message, or hand back a reference chosen in a
85
+ * dialog: `mention` takes the token's place exactly as a `mention` row does, and Ctrl/⌘+Z brings the token back.
86
+ */
87
+
88
+ /** The draft as a successful `open` leaves it: without the `/…` token, with its references moved along. Save this one. */
89
+
90
+ /**
91
+ * How an `open` row ends: on success (`void`) the token leaves as for an action; `"cancelled"` changes nothing; `failed`
92
+ * keeps the list open and the draft unchanged, and its text replaces the row's description until the row runs again.
93
+ */
82
94
 
83
95
  /** A fixed place in the slash list. Sections keep the given order, also while searching. */
84
96
 
@@ -168,6 +180,17 @@ export function ChatComposer(props) {
168
180
  return `${commandListId}-c${commandIds.get(key)}`;
169
181
  };
170
182
  const [closedAt, setClosedAt] = createSignal();
183
+ /** The list opened from "+": the field's selection when "+" was pressed, and the search field's text. */
184
+ const [addSearch, setAddSearch] = createSignal();
185
+ const [addQuery, setAddQuery] = createSignal("");
186
+ let addSearchRef;
187
+ let addButtonRef;
188
+ let addComposing = false;
189
+ /** The `open` row running now, and why `open` rows failed in this opening, by row key. */
190
+ const [openingRow, setOpeningRow] = createSignal();
191
+ const [openFailures, setOpenFailures] = createSignal({});
192
+ /** Cancels the `open` row running now; `key` follows announced draft moves, `label` is its busy line. */
193
+ let openingRun;
171
194
  // Where the pending native edit starts; a selection replaced as a whole starts at its beginning.
172
195
  let inputStart;
173
196
  let inputEnd;
@@ -272,7 +295,17 @@ export function ChatComposer(props) {
272
295
  return token && index ? suggestEmoji(index, token.query, emojiSuggestionLimit) : [];
273
296
  });
274
297
  const emojiOpen = () => emojiMatches().length > 0;
275
- const caretCommandToken = createMemo(() => chatCommandQuery(props.value, caret(), selectionEnd()));
298
+ // From "+", the search field's text is the query of an empty token at the field's stored caret.
299
+ const caretCommandToken = createMemo(() => {
300
+ const search = addSearch();
301
+ if (!search) return chatCommandQuery(props.value, caret(), selectionEnd());
302
+ const at = Math.min(search.end, props.value.length);
303
+ return {
304
+ start: at,
305
+ end: at,
306
+ query: addQuery()
307
+ };
308
+ });
276
309
  const caretCommandStart = createMemo(() => caretCommandToken()?.start);
277
310
  // Retained server rows belong to one `/`: they go when it does or when another `/` starts.
278
311
  createEffect(() => {
@@ -287,6 +320,7 @@ export function ChatComposer(props) {
287
320
  });
288
321
  const commandQuery = createMemo(() => {
289
322
  const token = caretCommandToken();
323
+ if (addSearch()) return token;
290
324
  return dismissed() || composing() || token?.start === closedAt() ? null : token;
291
325
  });
292
326
  const openingStart = createMemo(() => commandQuery()?.start);
@@ -295,14 +329,44 @@ export function ChatComposer(props) {
295
329
  const start = openingStart();
296
330
  if (start !== undefined) setCommandOpening(opening => opening + 1);
297
331
  });
332
+ // A failed `open` row says so for the rest of its list opening.
333
+ createEffect(() => {
334
+ props.draftKey;
335
+ commandOpening();
336
+ setOpenFailures({});
337
+ });
298
338
  const parsedCommandQuery = createMemo(() => parseChatCommandQuery(commandQuery()?.query ?? "", props.commandScopes ?? []));
339
+ const commandFallbackRow = createMemo(() => {
340
+ const parsed = parsedCommandQuery();
341
+ const query = parsed.query.trim();
342
+ return commandQuery() && (query || parsed.scope) ? props.commandFallback?.({
343
+ query,
344
+ scope: parsed.scope?.id
345
+ }) : undefined;
346
+ });
299
347
  const currentCommandScope = createMemo(() => parsedCommandQuery().scope?.id);
300
348
  createEffect(() => {
301
349
  currentCommandScope();
302
350
  setSearchResults([]);
303
351
  setSearchResultsQuery(undefined);
304
352
  });
305
- const localCommands = createMemo(() => props.commands ?? []);
353
+ // Rows of a `fixedWhileOpen` section are the ones it had when this `/` opened the list; later ones wait for the next
354
+ // `/`. A retry row starts another opening in the same open list, so it does not count.
355
+ const fixedCommandKeys = createMemo(() => {
356
+ props.draftKey;
357
+ if (openingStart() === undefined) return undefined;
358
+ return untrack(() => {
359
+ const fixed = new Set((props.commandSections ?? []).filter(section => section.fixedWhileOpen).map(section => section.id));
360
+ return fixed.size ? new Set((props.commands ?? []).filter(command => fixed.has(command.section ?? "")).map(chatCommandKey)) : undefined;
361
+ });
362
+ });
363
+ const localCommands = createMemo(() => {
364
+ const commands = props.commands ?? [];
365
+ const keys = fixedCommandKeys();
366
+ if (!keys) return commands;
367
+ const fixed = new Set((props.commandSections ?? []).filter(section => section.fixedWhileOpen).map(section => section.id));
368
+ return commands.filter(command => !fixed.has(command.section ?? "") || keys.has(chatCommandKey(command)));
369
+ });
306
370
  const commandModel = createMemo(() => chatCommandListModel({
307
371
  query: commandQuery()?.query ?? "",
308
372
  scopes: props.commandScopes,
@@ -311,9 +375,12 @@ export function ChatComposer(props) {
311
375
  searchResults: searchResultsScope() === parsedCommandQuery().scope?.id ? searchResults() : [],
312
376
  searchResultsFresh: searchResultsQuery() === commandQuery()?.query && searchResultsScope() === parsedCommandQuery().scope?.id,
313
377
  searchSettled: searchResultsQuery() !== undefined && searchResultsScope() === parsedCommandQuery().scope?.id,
314
- searching: searching()
378
+ searching: searching(),
379
+ fallback: commandFallbackRow()
315
380
  }));
316
381
  const commandMatches = createMemo(() => commandQuery() ? commandModel().rows.map(row => row.command) : []);
382
+ /** Rows that matched; an unscoped fallback row only follows them. */
383
+ const foundCommandCount = () => commandMatches().length - (commandModel().fallback ? 1 : 0);
317
384
  const currentCommandQuery = createMemo(() => caretCommandToken()?.query);
318
385
  // Arrow marking belongs to one token in one draft; another draft or `/` starts at the top row again.
319
386
  createEffect(() => {
@@ -322,13 +389,87 @@ export function ChatComposer(props) {
322
389
  currentCommandQuery();
323
390
  setCommandMoved(false);
324
391
  });
325
- const commandsOpen = () => Boolean(!emojiOpen() && commandQuery() && (localCommands().length || props.searchCommands));
326
- const commandSheetHeld = createMemo(() => commandsOpen() && isTouchOnly());
392
+ const commandsOpen = () => Boolean(!emojiOpen() && commandQuery() && (localCommands().length || props.searchCommands || commandFallbackRow()));
393
+ // Rows that arrive for a pending section never move a visible row of the overview: until the person types, the list
394
+ // keeps the height it had with its placeholders and scrolls what does not fit. A list below its maximum height grows
395
+ // upward from the field, so a change of height alone would move its first row. While searching, the rows arrive
396
+ // with the search's own answer, which changes the list anyway.
397
+ // From "+", the whole panel keeps the height it opened with instead.
398
+ const commandsPending = createMemo(() => commandsOpen() && !addSearch() && !parsedCommandQuery().scope && !parsedCommandQuery().query.trim() && commandModel().groups.some(group => group.pendingRows > 0));
399
+ // Typing, another opening or closing the list lets it size itself again.
400
+ const commandListSizeKey = createMemo(() => commandsOpen() ? `${commandOpening()}\u0000${caretCommandToken()?.query}` : undefined);
401
+ // The list's height with its placeholders, for the size key it was measured under.
402
+ let pendingList;
403
+ createEffect(() => {
404
+ const list = commandListRef;
405
+ if (!commandsPending() || !list || typeof ResizeObserver === "undefined") return;
406
+ const measure = () => {
407
+ pendingList = {
408
+ height: list.getBoundingClientRect().height,
409
+ key: untrack(commandListSizeKey)
410
+ };
411
+ };
412
+ measure();
413
+ const observer = new ResizeObserver(measure);
414
+ observer.observe(list);
415
+ onCleanup(() => observer.disconnect());
416
+ });
417
+ createEffect(on(commandsPending, (pending, wasPending) => {
418
+ const held = pendingList;
419
+ pendingList = undefined;
420
+ // Only rows arriving hold the list; the person's typing or closing it does not.
421
+ if (pending || !wasPending || !held || held.key !== commandListSizeKey() || !commandListRef?.isConnected) return;
422
+ commandListRef.style.height = `${held.height}px`;
423
+ commandListRef.setAttribute("data-held", "");
424
+ }));
425
+ createEffect(on(commandListSizeKey, () => {
426
+ commandListRef?.style.removeProperty("height");
427
+ commandListRef?.removeAttribute("data-held");
428
+ }, {
429
+ defer: true
430
+ }));
431
+ // Escape, a press outside, or moving the caret away closes the list; while an `open` row runs, that cancels it, so
432
+ // nothing opens after the person backed out. Success ends the opening and closes the list in one step. A new draft
433
+ // cancels too, unless the opener announced that next move with `follow()`: its busy line moves to the status line,
434
+ // and Escape still cancels there. Any later unannounced draft change cancels.
435
+ createEffect(() => {
436
+ const draftKey = props.draftKey;
437
+ const open = commandsOpen();
438
+ const row = openingRow();
439
+ const run = openingRun;
440
+ if (!run || row === undefined) return;
441
+ if (draftKey !== run.key) {
442
+ if (run.follow) {
443
+ run.follow = false;
444
+ run.key = draftKey;
445
+ run.moved = true;
446
+ showCommandStatus({
447
+ tone: "busy",
448
+ text: run.label
449
+ });
450
+ } else run.controller.abort();
451
+ return;
452
+ }
453
+ if (!open && !run.moved) run.controller.abort();
454
+ });
455
+ onCleanup(() => openingRun?.controller.abort());
456
+ // An `open` row gives the sheet's Back entry up first, so it never races the navigation it starts.
457
+ const commandSheetHeld = createMemo(() => commandsOpen() && isTouchOnly() && openingRow() === undefined);
327
458
  createEffect(() => {
328
459
  if (!commandSheetHeld()) return;
329
- onCleanup(holdDialogHistory(() => setClosedAt(commandQuery()?.start)));
460
+ onCleanup(holdDialogHistory(() => closeCommandList()));
330
461
  });
331
462
  const suggestionsOpen = () => commandsOpen() || emojiOpen();
463
+ const fieldOwnsList = () => suggestionsOpen() && !addSearch();
464
+ // From "+", the panel keeps the height it opened with while the person types or rows arrive, so neither the search
465
+ // field nor the first row moves; what does not fit scrolls. The space above the field still bounds it.
466
+ const addPanelOpen = createMemo(() => addSearch() !== undefined && commandsOpen());
467
+ createEffect(() => {
468
+ const accessory = addPanelOpen() ? commandListRef?.parentElement : undefined;
469
+ if (!accessory) return;
470
+ const frame = requestAnimationFrame(() => accessory.style.setProperty("--k2b-chat-command-held", `${accessory.getBoundingClientRect().height}px`));
471
+ onCleanup(() => cancelAnimationFrame(frame));
472
+ });
332
473
  createEffect(() => {
333
474
  if (!suggestionsOpen()) return;
334
475
  let disposed = false;
@@ -356,10 +497,13 @@ export function ChatComposer(props) {
356
497
  const left = Math.max(0, column?.left ?? 0);
357
498
  const right = Math.min(page, column?.right ?? page);
358
499
  const slot = accessory.parentElement.getBoundingClientRect();
359
- const bottom = accessory.getBoundingClientRect().bottom;
360
- commandListRef.style.setProperty("--k2b-chat-command-space", `${Math.max(40, bottom - top - 8)}px`);
500
+ // From its place in the slot, not its box: the sheet opened from "+" rises into place, and a box measured during
501
+ // that animation would include its offset.
502
+ const bottom = slot.bottom - (Number.parseFloat(window.getComputedStyle(accessory).bottom) || 0);
503
+ // On the accessory, so a panel opened from "+" is bounded as a whole, with its search field.
504
+ accessory.style.setProperty("--k2b-chat-command-space", `${Math.max(40, bottom - top - 8)}px`);
361
505
  // Only a list that reaches the screen's top edge keeps clear of a notch or the status bar.
362
- commandListRef.style.setProperty("--k2b-chat-command-safe-top", top === viewportTop ? "env(safe-area-inset-top, 0px)" : "0px");
506
+ accessory.style.setProperty("--k2b-chat-command-safe-top", top === viewportTop ? "env(safe-area-inset-top, 0px)" : "0px");
363
507
  accessory.style.setProperty("--k2b-chat-command-reach-left", `${Math.max(0, slot.left - left)}px`);
364
508
  accessory.style.setProperty("--k2b-chat-command-reach-right", `${Math.max(0, right - slot.right)}px`);
365
509
  };
@@ -377,11 +521,29 @@ export function ChatComposer(props) {
377
521
  // A press outside the command list and the field closes the list, as Escape does; the text stays. With a mouse or a
378
522
  // trackpad the press then acts as usual. On a touch-only device a tap outside the composer closes only the list: it
379
523
  // reaches nothing beneath, and the field keeps focus, so the keyboard stays open.
524
+ // A list opened from "+" is a menu: any press outside it closes only the list, with every pointer, and the field gets
525
+ // its focus back. "+" itself toggles with its click; a press in the field closes the list and lands there as usual.
380
526
  createEffect(() => {
381
527
  if (!commandsOpen()) return;
382
528
  const onPointerDown = event => {
383
529
  const target = event.target;
384
- if (!(target instanceof Node) || commandListRef?.contains(target) || textareaRef?.parentElement?.contains(target)) return;
530
+ if (!(target instanceof Node) || commandListRef?.parentElement?.contains(target)) return;
531
+ if (addSearch()) {
532
+ if (addButtonRef?.contains(target) && !addButtonRef.disabled) return;
533
+ if (textareaRef?.parentElement?.contains(target)) {
534
+ // The field takes the focus before the search field goes, so a phone keeps its keyboard; the press itself
535
+ // then places the caret.
536
+ textareaRef.focus({
537
+ preventScroll: true
538
+ });
539
+ endAddSearch(false);
540
+ return;
541
+ }
542
+ swallowTap(event);
543
+ endAddSearch(true);
544
+ return;
545
+ }
546
+ if (textareaRef?.parentElement?.contains(target)) return;
385
547
  setClosedAt(commandQuery()?.start);
386
548
  if (isTouchOnly() && !shellRef?.contains(target)) swallowTap(event);
387
549
  };
@@ -431,9 +593,10 @@ export function ChatComposer(props) {
431
593
  });
432
594
  });
433
595
  let settledEmpty;
596
+ // A space after "Nothing found" ends the `/…` as plain text; a search field opened from "+" just keeps its text.
434
597
  createEffect(() => {
435
598
  const token = commandQuery();
436
- if (!token || parsedCommandQuery().scope || searchError()) {
599
+ if (!token || addSearch() || parsedCommandQuery().scope || searchError()) {
437
600
  settledEmpty = undefined;
438
601
  return;
439
602
  }
@@ -484,6 +647,7 @@ export function ChatComposer(props) {
484
647
  history.length = 0;
485
648
  historyIndex = -1;
486
649
  finishModelSelection(null);
650
+ endAddSearch(false);
487
651
  setDismissed(true);
488
652
  setClosedAt(undefined);
489
653
  setSearchResults([]);
@@ -532,6 +696,8 @@ export function ChatComposer(props) {
532
696
  setDismissed(false);
533
697
  };
534
698
  const restoreHistory = direction => {
699
+ // A field that only stays enabled to keep focus, while an `open` row runs, is not edited by undo either.
700
+ if (editBlocked()) return;
535
701
  record();
536
702
  const next = history[historyIndex + direction];
537
703
  if (!next) return;
@@ -550,6 +716,45 @@ export function ChatComposer(props) {
550
716
  setSelectionEnd(textareaRef?.selectionEnd ?? props.value.length);
551
717
  });
552
718
  };
719
+ /** Closes the list opened from "+"; `refocus` gives the field its focus and the selection it had when "+" was pressed. */
720
+ const endAddSearch = refocus => {
721
+ const search = addSearch();
722
+ if (!search) return;
723
+ addComposing = false;
724
+ batch(() => {
725
+ setAddSearch(undefined);
726
+ setAddQuery("");
727
+ });
728
+ if (!refocus || !textareaRef) return;
729
+ const length = textareaRef.value.length;
730
+ textareaRef.focus();
731
+ textareaRef.setSelectionRange(Math.min(search.start, length), Math.min(search.end, length));
732
+ syncCaret();
733
+ };
734
+ /** Escape, Back, a press outside: the list closes and the text stays. */
735
+ const closeCommandList = (refocus = true) => {
736
+ if (addSearch()) endAddSearch(refocus);else setClosedAt(commandQuery()?.start);
737
+ };
738
+ const toggleAddSearch = () => {
739
+ if (addSearch()) {
740
+ endAddSearch(true);
741
+ return;
742
+ }
743
+ if (blocked() || !textareaRef) return;
744
+ const start = textareaRef.selectionStart;
745
+ const end = textareaRef.selectionEnd;
746
+ batch(() => {
747
+ // Emoji suggestions for a `:shortcode` at the caret give way; they would hold the list closed.
748
+ setDismissed(true);
749
+ setAddQuery("");
750
+ setAddSearch({
751
+ start,
752
+ end
753
+ });
754
+ });
755
+ // Within the press, so a phone opens its keyboard for the search field.
756
+ addSearchRef?.focus();
757
+ };
553
758
  const selectedCommand = createMemo(() => {
554
759
  const rows = commandMatches();
555
760
  return commandMoved() && rows.find(command => chatCommandKey(command) === activeCommandKey()) || rows[0];
@@ -573,6 +778,8 @@ export function ChatComposer(props) {
573
778
  const canSubmit = () => !submitting() && submittable();
574
779
  const canSelectFiles = () => Boolean(props.fileSelection && !props.fileSelection.disabled && !running() && !blocked());
575
780
  const hasAddMenu = () => Boolean(props.fileSelection || props.menuActions?.length);
781
+ /** "+" opens the slash list, as long as there is one; otherwise it stays the add menu. */
782
+ const addOpensCommands = () => Boolean(props.addOpensCommands && (props.commands?.length || props.searchCommands));
576
783
  const hasContextUsage = () => {
577
784
  if (props.contextPopupAction) return true;
578
785
  const context = props.contextUsage;
@@ -666,12 +873,78 @@ export function ChatComposer(props) {
666
873
  });
667
874
  const setAttachments = next => props.onAttachmentsChange?.(next);
668
875
  const renderAttachmentCopy = attachment => _$ssr(_tmpl$, _$ssrAttribute("title", _$escape(attachment.name, true), false), _$escape(attachment.name));
669
- const chooseFiles = () => {
876
+
877
+ /** `onNothing` runs when the person closes the selection without a file. */
878
+ const chooseFiles = onNothing => {
670
879
  const selection = props.fileSelection;
671
- if (!selection?.choose) return fileInputRef?.click();
672
- selection.choose().then(
880
+ if (!selection?.choose) {
881
+ const input = fileInputRef;
882
+ if (input && onNothing) {
883
+ const done = new AbortController();
884
+ input.addEventListener("cancel", () => {
885
+ done.abort();
886
+ onNothing();
887
+ }, {
888
+ signal: done.signal
889
+ });
890
+ input.addEventListener("change", () => done.abort(), {
891
+ signal: done.signal
892
+ });
893
+ }
894
+ return input?.click();
895
+ }
896
+ const failed = error => {
897
+ onNothing?.();
898
+ (props.fileSelection?.onError ?? props.onError)?.(error);
899
+ };
900
+ let chosen;
901
+ try {
902
+ chosen = selection.choose();
903
+ } catch (error) {
904
+ failed(error);
905
+ return;
906
+ }
907
+ chosen.then(
673
908
  // A single-file composer keeps the first chosen file, as its file dialog and drops do.
674
- files => void runFiles(selection.multiple === false ? files.slice(0, 1) : files), error => (props.fileSelection?.onError ?? props.onError)?.(error));
909
+ files => {
910
+ if (files.length === 0) onNothing?.();else void runFiles(selection.multiple === false ? files.slice(0, 1) : files);
911
+ }, failed);
912
+ };
913
+
914
+ /**
915
+ * "Attach files" from the slash list: the token leaves as one edit, and the selection opens synchronously, inside the
916
+ * key or click that chose the row, because the device's file dialog needs that activation. Closing the selection
917
+ * without a file brings the token back, unless the draft changed meanwhile.
918
+ */
919
+ const attachFilesFromCommand = (token, selection) => {
920
+ if (!canSelectFiles()) return;
921
+ const key = props.draftKey;
922
+ const previous = snapshot();
923
+ const previousHistory = {
924
+ entries: [...history],
925
+ index: historyIndex
926
+ };
927
+ const nextValue = props.value.slice(0, token.start) + props.value.slice(token.end);
928
+ edit(nextValue, reconcileChatMentions(props.value, nextValue, mentions()));
929
+ setDismissed(true);
930
+ chooseFiles(() => {
931
+ if (props.draftKey !== key || props.value !== nextValue) return;
932
+ setDismissed(true);
933
+ props.onValueChange(previous.value);
934
+ props.onMentionsChange?.(previous.mentions);
935
+ history.splice(0, history.length, ...previousHistory.entries);
936
+ historyIndex = previousHistory.index;
937
+ queueMicrotask(() => {
938
+ textareaRef?.setSelectionRange(selection?.start ?? token.end, selection?.end ?? token.end);
939
+ syncCaret();
940
+ focus();
941
+ });
942
+ });
943
+ queueMicrotask(() => {
944
+ if (props.draftKey !== key || props.value !== nextValue) return;
945
+ textareaRef?.setSelectionRange(token.start, token.start);
946
+ syncCaret();
947
+ });
675
948
  };
676
949
  const runFiles = async files => {
677
950
  if (!canSelectFiles()) return;
@@ -694,7 +967,7 @@ export function ChatComposer(props) {
694
967
  setSubmitting(true);
695
968
  try {
696
969
  if (isTouchOnly()) {
697
- if (commandSheetHeld()) setClosedAt(commandQuery()?.start);
970
+ if (commandSheetHeld()) closeCommandList(false);
698
971
  // The sheet's Back entry leaves first, so a submit handler that navigates finds the history settled.
699
972
  await dialogHistorySettled();
700
973
  // A draft that another one replaced, or that may no longer go out, is not sent and keeps its text.
@@ -746,6 +1019,10 @@ export function ChatComposer(props) {
746
1019
  const word = scope?.words[0];
747
1020
  const token = commandQuery();
748
1021
  if (!word || !token || !textareaRef) return false;
1022
+ if (addSearch()) {
1023
+ setAddQuery(`${word} `);
1024
+ return true;
1025
+ }
749
1026
  replaceTextareaRange(textareaRef, token.start + 1, token.end, `${word} `);
750
1027
  syncCaret();
751
1028
  return true;
@@ -753,6 +1030,8 @@ export function ChatComposer(props) {
753
1030
  const executeCommand = async (command, tab = false) => {
754
1031
  const token = commandQuery();
755
1032
  const heldHistory = commandSheetHeld();
1033
+ const addSelection = addSearch();
1034
+ const fromAdd = addSelection !== undefined;
756
1035
  if (!token || blocked()) return;
757
1036
  if (command.disabled || command.reason) {
758
1037
  // A new object each time, so choosing the same row again inserts and announces its reason again.
@@ -766,17 +1045,35 @@ export function ChatComposer(props) {
766
1045
  setCommandOpening(opening => opening + 1);
767
1046
  return;
768
1047
  }
769
- if (command.scope && (tab || !command.action && !command.mention) && narrowCommand(command)) return;
1048
+ if (command.scope && (tab || !command.action && !command.mention && !command.attachFiles) && narrowCommand(command)) return;
1049
+ if (command.attachFiles) {
1050
+ if (!canSelectFiles()) return;
1051
+ // The field has the focus before the selection opens, so closing it without a file comes back there.
1052
+ endAddSearch(true);
1053
+ attachFilesFromCommand(token, addSelection);
1054
+ return;
1055
+ }
1056
+ if (command.open) {
1057
+ await openFromCommand(command, command.open, token);
1058
+ return;
1059
+ }
1060
+ // The field has the focus again before an action opens a dialog, so the dialog gives it back there.
1061
+ endAddSearch(true);
1062
+ // From "+" a reference joins the text at the caret, apart from a word right before it.
1063
+ const lead = value => fromAdd && token.start > 0 && !/\s/.test(value[token.start - 1] ?? "") ? " " : "";
770
1064
  const previous = snapshot();
771
1065
  const key = props.draftKey;
772
- const replacement = command.mention ? command.mention.name + " " : "";
1066
+ const replacement = command.mention ? lead(props.value) + command.mention.name + " " : "";
773
1067
  const nextValue = props.value.slice(0, token.start) + replacement + props.value.slice(token.end);
774
1068
  const nextMentions = reconcileChatMentions(props.value, nextValue, mentions());
775
- if (command.mention) nextMentions.push({
776
- start: token.start,
777
- end: token.start + command.mention.name.length,
778
- attachment: command.mention
779
- });
1069
+ if (command.mention) {
1070
+ const start = token.start + lead(props.value).length;
1071
+ nextMentions.push({
1072
+ start,
1073
+ end: start + command.mention.name.length,
1074
+ attachment: command.mention
1075
+ });
1076
+ }
780
1077
  // A cancelled or failed command leaves no undo step behind: restoring puts the history back as well.
781
1078
  const previousHistory = {
782
1079
  entries: [...history],
@@ -787,6 +1084,7 @@ export function ChatComposer(props) {
787
1084
  setExecuting(true);
788
1085
  let submitRequested = false;
789
1086
  let restored = false;
1087
+ let mentioned;
790
1088
  const restore = () => {
791
1089
  if (props.draftKey !== key || props.value !== nextValue) return;
792
1090
  setDismissed(true);
@@ -819,6 +1117,33 @@ export function ChatComposer(props) {
819
1117
  submitRequested = false;
820
1118
  restore();
821
1119
  showCommandStatus();
1120
+ } else if (result && "mention" in result) {
1121
+ // One undo step from the token to the reference, as if the row had been a reference itself.
1122
+ if (props.draftKey === key && props.value === nextValue) {
1123
+ restore();
1124
+ restored = false;
1125
+ const name = result.mention.name;
1126
+ const start = token.start + lead(previous.value).length;
1127
+ const withMention = previous.value.slice(0, token.start) + lead(previous.value) + name + " " + previous.value.slice(token.end);
1128
+ edit(withMention, [...reconcileChatMentions(previous.value, withMention, previous.mentions), {
1129
+ start,
1130
+ end: start + name.length,
1131
+ attachment: result.mention
1132
+ }]);
1133
+ setDismissed(true);
1134
+ mentioned = {
1135
+ value: withMention,
1136
+ caret: start + name.length + 1
1137
+ };
1138
+ showCommandStatus();
1139
+ } else {
1140
+ // The pick is lost; say so, also when the draft moved to another key meanwhile (such as a new chat saved
1141
+ // while the dialog was open), so it never ends in silence.
1142
+ showCommandStatus({
1143
+ tone: "error",
1144
+ text: chatMessages().commandDraftChanged
1145
+ });
1146
+ }
822
1147
  } else if (result) {
823
1148
  showCommandStatus({
824
1149
  tone: "done",
@@ -840,9 +1165,9 @@ export function ChatComposer(props) {
840
1165
  if (submitRequested && props.draftKey === key) await submit();
841
1166
  queueMicrotask(() => {
842
1167
  if (props.draftKey !== key) return;
843
- if (restored || props.value === nextValue) {
844
- const position = restored ? token.end : token.start + replacement.length;
845
- textareaRef?.setSelectionRange(position, position);
1168
+ if (restored || props.value === nextValue || mentioned && props.value === mentioned.value) {
1169
+ const position = restored ? token.end : mentioned?.caret ?? token.start + replacement.length;
1170
+ textareaRef?.setSelectionRange(restored && addSelection ? addSelection.start : position, restored && addSelection ? addSelection.end : position);
846
1171
  }
847
1172
  syncCaret();
848
1173
  autoResize();
@@ -850,6 +1175,110 @@ export function ChatComposer(props) {
850
1175
  });
851
1176
  };
852
1177
 
1178
+ /**
1179
+ * Runs an `open` row with the token and the list in place. Success removes the token in one undo step; `failed`
1180
+ * stays in the row while the list is still there, otherwise the status line says it, so it never ends in silence.
1181
+ */
1182
+ const openFromCommand = async (command, open, token) => {
1183
+ const key = props.draftKey;
1184
+ const row = chatCommandKey(command);
1185
+ const value = props.value;
1186
+ const nextValue = value.slice(0, token.start) + value.slice(token.end);
1187
+ const nextMentions = reconcileChatMentions(value, nextValue, mentions());
1188
+ // Still the same draft and `/…`, so the row is where the person chose it.
1189
+ const inPlace = () => !disposed && props.draftKey === key && props.value === value && commandQuery()?.start === token.start;
1190
+ const heldHistory = commandSheetHeld();
1191
+ // Closing the list or leaving the composer cancels the opening; the row ends at once, without waiting for it.
1192
+ const controller = new AbortController();
1193
+ const run = {
1194
+ controller,
1195
+ key,
1196
+ label: command.busyLabel ?? chatMessages().commandOpening,
1197
+ follow: false,
1198
+ moved: false
1199
+ };
1200
+ openingRun = run;
1201
+ const cancelled = new Promise(resolve => controller.signal.addEventListener("abort", () => resolve("cancelled"), {
1202
+ once: true
1203
+ }));
1204
+ setOpenFailures(({
1205
+ [row]: _,
1206
+ ...rest
1207
+ }) => rest);
1208
+ setOpeningRow(row);
1209
+ setExecuting(true);
1210
+ // An opening that ends without leaving, by failure, rejection or cancelling, also aborts `signal`, so the opener
1211
+ // can put back what it saved without the token.
1212
+ const finish = (left = false) => {
1213
+ if (!left) controller.abort();
1214
+ if (openingRun === run) openingRun = undefined;
1215
+ if (commandStatus()?.tone === "busy") showCommandStatus();
1216
+ setOpeningRow(undefined);
1217
+ setExecuting(false);
1218
+ };
1219
+ let result;
1220
+ try {
1221
+ if (heldHistory) {
1222
+ await dialogHistorySettled();
1223
+ if (!inPlace()) {
1224
+ finish();
1225
+ return;
1226
+ }
1227
+ }
1228
+ result = await Promise.race([open({
1229
+ value: nextValue,
1230
+ mentions: nextMentions
1231
+ }, controller.signal, () => {
1232
+ run.follow = true;
1233
+ }), cancelled]);
1234
+ } catch (error) {
1235
+ finish();
1236
+ if (disposed) {
1237
+ props.onError?.(error);
1238
+ return;
1239
+ }
1240
+ if (inPlace()) closeCommandList();
1241
+ showCommandStatus({
1242
+ tone: "error",
1243
+ text: error instanceof Error && error.message ? error.message : chatMessages().commandFailed
1244
+ });
1245
+ return;
1246
+ }
1247
+ if (!result) {
1248
+ // Success: the token leaves and the list closes in the same step that ends the opening, so a phone sheet never
1249
+ // takes its Back entry again while the page navigates.
1250
+ batch(() => {
1251
+ if (!disposed && props.draftKey === key && props.value === value) {
1252
+ edit(nextValue, nextMentions);
1253
+ setDismissed(true);
1254
+ }
1255
+ endAddSearch(!disposed && props.draftKey === key);
1256
+ finish(true);
1257
+ });
1258
+ queueMicrotask(() => {
1259
+ if (disposed || props.draftKey !== key || props.value !== nextValue) return;
1260
+ textareaRef?.setSelectionRange(token.start, token.start);
1261
+ syncCaret();
1262
+ autoResize();
1263
+ });
1264
+ return;
1265
+ }
1266
+ finish();
1267
+ if (result === "cancelled") return;
1268
+ if (inPlace()) {
1269
+ setOpenFailures(failures => ({
1270
+ ...failures,
1271
+ [row]: result.failed
1272
+ }));
1273
+ setAnnouncement({
1274
+ text: result.failed
1275
+ });
1276
+ } else if (disposed) props.onError?.(new Error(result.failed));else showCommandStatus({
1277
+ tone: "error",
1278
+ text: result.failed
1279
+ });
1280
+ };
1281
+
853
1282
  /** Replaces `start`–`end` with the emoji through the editing path, so the browser's undo restores the shortcode. */
854
1283
  const insertEmoji = (start, end, emoji) => {
855
1284
  if (!textareaRef || editBlocked()) return;
@@ -890,9 +1319,66 @@ export function ChatComposer(props) {
890
1319
  if (untouched && textarea.matches(":focus")) convert(loaded);
891
1320
  }, () => undefined);
892
1321
  };
1322
+
1323
+ /** Moving through and choosing in the command list, from the field or from the search field opened by "+". */
1324
+ const commandListKey = event => {
1325
+ if (!commandsOpen()) return false;
1326
+ const matches = commandMatches();
1327
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
1328
+ event.preventDefault();
1329
+ const index = matches.indexOf(selectedCommand());
1330
+ const next = matches[nextChatCommandIndex(index, matches.length, event.key === "ArrowUp" ? -1 : 1)];
1331
+ setActiveCommandKey(next ? chatCommandKey(next) : undefined);
1332
+ setCommandMoved(true);
1333
+ setAnnouncement(undefined);
1334
+ return true;
1335
+ }
1336
+ // Ctrl/⌘+Enter sends, also while suggestions are open.
1337
+ const plain = !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
1338
+ if (plain && (event.key === "Tab" || event.key === "Enter") && matches.length > 0) {
1339
+ event.preventDefault();
1340
+ const command = selectedCommand();
1341
+ // A server row answering an earlier query may no longer match; wait for fresh results. Local rows match now.
1342
+ const row = commandModel().rows.find(row => chatCommandKey(row.command) === selectedCommandKey());
1343
+ const staleServerRow = row?.server && (searchResultsQuery() !== commandQuery()?.query || searchResultsScope() !== parsedCommandQuery().scope?.id);
1344
+ if (staleServerRow) return true;
1345
+ if (command) void executeCommand(command, event.key === "Tab");
1346
+ return true;
1347
+ }
1348
+ if (event.key === "Escape") {
1349
+ event.preventDefault();
1350
+ closeCommandList();
1351
+ return true;
1352
+ }
1353
+ return false;
1354
+ };
1355
+ const onAddSearchKeyDown = event => {
1356
+ // Nothing chooses while an input method composes, as in the field.
1357
+ if (event.isComposing || event.keyCode === 229 || addComposing) return;
1358
+ if (event.key === "Escape" && openingRun) {
1359
+ event.preventDefault();
1360
+ openingRun.controller.abort();
1361
+ endAddSearch(true);
1362
+ return;
1363
+ }
1364
+ if (commandListKey(event)) return;
1365
+ // Ctrl/⌘+Enter sends from the search field as from the field, with the list closed first.
1366
+ if (event.key === "Enter" && (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
1367
+ event.preventDefault();
1368
+ endAddSearch(true);
1369
+ void submit();
1370
+ }
1371
+ };
893
1372
  const onKeyDown = event => {
894
1373
  // WebKit ends the composition before the keydown of the Enter that confirms it; that keydown keeps keyCode 229.
895
1374
  if (event.isComposing || event.keyCode === 229 || composing()) return;
1375
+ // Escape cancels a running `open` row, also once its list is gone because the draft moved to a new chat.
1376
+ if (event.key === "Escape" && openingRun) {
1377
+ event.preventDefault();
1378
+ openingRun.controller.abort();
1379
+ if (commandsOpen()) closeCommandList();
1380
+ return;
1381
+ }
896
1382
  if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "z") {
897
1383
  event.preventDefault();
898
1384
  restoreHistory(event.shiftKey ? 1 : -1);
@@ -917,38 +1403,11 @@ export function ChatComposer(props) {
917
1403
  return;
918
1404
  }
919
1405
  }
920
- const matches = commandMatches();
921
- if (commandsOpen()) {
922
- if (event.key === "ArrowUp" || event.key === "ArrowDown") {
923
- event.preventDefault();
924
- const index = matches.indexOf(selectedCommand());
925
- const next = matches[nextChatCommandIndex(index, matches.length, event.key === "ArrowUp" ? -1 : 1)];
926
- setActiveCommandKey(next ? chatCommandKey(next) : undefined);
927
- setCommandMoved(true);
928
- setAnnouncement(undefined);
929
- return;
930
- }
931
- // Ctrl/⌘+Enter sends, also while suggestions are open.
932
- const plain = !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
933
- if (plain && (event.key === "Tab" || event.key === "Enter") && matches.length > 0) {
934
- event.preventDefault();
935
- const command = selectedCommand();
936
- // A server row answering an earlier query may no longer match; wait for fresh results. Local rows match now.
937
- const row = commandModel().rows.find(row => chatCommandKey(row.command) === selectedCommandKey());
938
- const staleServerRow = row?.server && (searchResultsQuery() !== commandQuery()?.query || searchResultsScope() !== parsedCommandQuery().scope?.id);
939
- if (staleServerRow) return;
940
- if (command) void executeCommand(command, event.key === "Tab");
941
- return;
942
- }
943
- if (event.key === "Escape") {
944
- event.preventDefault();
945
- setClosedAt(commandQuery()?.start);
946
- return;
947
- }
948
- }
1406
+ if (commandListKey(event)) return;
949
1407
  const format = props.formatting && textareaRef ? formatShortcut(event) : undefined;
950
1408
  if (format && textareaRef) {
951
1409
  event.preventDefault();
1410
+ if (editBlocked()) return;
952
1411
  format.run(textareaRef);
953
1412
  return;
954
1413
  }
@@ -1071,8 +1530,46 @@ export function ChatComposer(props) {
1071
1530
  }
1072
1531
  });
1073
1532
 
1533
+ /** "+" as the way to the slash list on keyboards without a handy "/". */
1534
+ const addSearchButton = (extraClass = "") => _$createComponent(Tooltip.Trigger, {
1535
+ type: "button",
1536
+ "class": `k2b-chat-composer__icon-action${extraClass}`,
1537
+ get ["aria-label"]() {
1538
+ return chatMessages().addMenu;
1539
+ },
1540
+ "aria-haspopup": "listbox",
1541
+ get ["aria-expanded"]() {
1542
+ return addSearch() !== undefined;
1543
+ },
1544
+ get ["aria-controls"]() {
1545
+ return addSearch() ? commandListId : undefined;
1546
+ },
1547
+ get content() {
1548
+ return chatMessages().addMenu;
1549
+ },
1550
+ get disabled() {
1551
+ return blocked();
1552
+ },
1553
+ onMouseDown: keepFieldFocus,
1554
+ onClick: toggleAddSearch,
1555
+ get children() {
1556
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-plus", false));
1557
+ }
1558
+ });
1559
+
1074
1560
  /** The conversation's attach button opens the file choice directly; other actions turn it into the add menu. */
1075
1561
  const attachControl = () => _$createComponent(Show, {
1562
+ get when() {
1563
+ return !addOpensCommands();
1564
+ },
1565
+ get fallback() {
1566
+ return addSearchButton(" k2b-chat-composer__attach");
1567
+ },
1568
+ get children() {
1569
+ return attachChoice();
1570
+ }
1571
+ });
1572
+ const attachChoice = () => _$createComponent(Show, {
1076
1573
  get when() {
1077
1574
  return props.menuActions?.length;
1078
1575
  },
@@ -1093,7 +1590,7 @@ export function ChatComposer(props) {
1093
1590
  get disabled() {
1094
1591
  return !canSelectFiles();
1095
1592
  },
1096
- onClick: chooseFiles,
1593
+ onClick: () => chooseFiles(),
1097
1594
  get children() {
1098
1595
  return _$ssr(_tmpl$3, _$ssrAttribute("class", addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-plus", false));
1099
1596
  }
@@ -1356,7 +1853,7 @@ export function ChatComposer(props) {
1356
1853
  children: part => _$ssr(_tmpl$10, Boolean(part.mention) ? "k2b-chat-composer__mention" : "", _$escape(part.text))
1357
1854
  })));
1358
1855
  }
1359
- })), mentions().length > 0 ? "k2b-chat-composer__textarea--highlighted" : "", _$ssrAttribute("value", _$escape(props.value, true), false), _$ssrAttribute("disabled", editBlocked(), true) + _$ssrAttribute("enterkeyhint", conversation() ? "enter" : _$escape(undefined, true), false) + _$ssrAttribute("placeholder", _$escape(props.placeholder ?? (running() ? chatMessages().addGuidance : conversation() ? chatMessages().writeConversationMessage : chatMessages().writeMessage), true), false) + _$ssrAttribute("aria-label", _$escape(props.inputLabel ?? messages().message, true), false) + _$ssrAttribute("role", suggestionsOpen() ? "combobox" : _$escape(undefined, true), false) + _$ssrAttribute("aria-autocomplete", suggestionsOpen() ? "list" : _$escape(undefined, true), false) + _$ssrAttribute("aria-controls", suggestionsOpen() ? _$escape(commandListId, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-expanded", suggestionsOpen() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-activedescendant", emojiOpen() ? `${_$escape(commandListId, true)}-${_$escape(selectedCommandIndex(), true)}` : selectedCommand() ? _$escape(commandOptionId(selectedCommand()), true) : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1856
+ })), mentions().length > 0 ? "k2b-chat-composer__textarea--highlighted" : "", _$ssrAttribute("value", _$escape(props.value, true), false), _$ssrAttribute("disabled", editBlocked() && openingRow() === undefined, true) + _$ssrAttribute("readonly", _$escape(openingRow(), true) !== _$escape(undefined, true), false) + _$ssrAttribute("enterkeyhint", conversation() ? "enter" : _$escape(undefined, true), false) + _$ssrAttribute("placeholder", _$escape(props.placeholder ?? (running() ? chatMessages().addGuidance : conversation() ? chatMessages().writeConversationMessage : chatMessages().writeMessage), true), false) + _$ssrAttribute("aria-label", _$escape(props.inputLabel ?? messages().message, true), false) + _$ssrAttribute("role", fieldOwnsList() ? "combobox" : _$escape(undefined, true), false) + _$ssrAttribute("aria-autocomplete", fieldOwnsList() ? "list" : _$escape(undefined, true), false) + _$ssrAttribute("aria-controls", fieldOwnsList() ? _$escape(commandListId, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-expanded", fieldOwnsList() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-activedescendant", !fieldOwnsList() ? _$escape(undefined, true) : emojiOpen() ? `${_$escape(commandListId, true)}-${_$escape(selectedCommandIndex(), true)}` : selectedCommand() ? _$escape(commandOptionId(selectedCommand()), true) : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1360
1857
  get when() {
1361
1858
  return conversation();
1362
1859
  },
@@ -1369,7 +1866,9 @@ export function ChatComposer(props) {
1369
1866
  const renderCommandRow = command => {
1370
1867
  const highlight = () => commandRowMap().get(command)?.highlight;
1371
1868
  const title = () => command.label ?? command.name;
1372
- return _$ssr(_tmpl$13, _$ssrAttribute("id", _$escape(commandOptionId(command), true), false), _$ssrAttribute("aria-selected", _$escape(selectedCommand(), true) === _$escape(command, true), false) + _$ssrAttribute("aria-disabled", _$escape(Boolean(command.disabled || command.reason), true), false) + _$ssrAttribute("aria-describedby", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false) + _$ssrAttribute("data-reason", command.reason ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-active", selectedCommand() === command ? "true" : _$escape(undefined, true), false), _$ssrAttribute("class", _$escape(command.icon ?? "ti ti-slash", true), false), _$escape(_$createComponent(Show, {
1869
+ const opening = () => openingRow() === chatCommandKey(command);
1870
+ const failure = () => openFailures()[chatCommandKey(command)];
1871
+ return _$ssr(_tmpl$13, _$ssrAttribute("id", _$escape(commandOptionId(command), true), false), _$ssrAttribute("aria-selected", _$escape(selectedCommand(), true) === _$escape(command, true), false) + _$ssrAttribute("aria-disabled", _$escape(Boolean(command.disabled || command.reason), true), false) + _$ssrAttribute("aria-busy", opening() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-describedby", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false) + _$ssrAttribute("data-reason", command.reason ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-active", selectedCommand() === command ? "true" : _$escape(undefined, true), false), _$ssrAttribute("class", _$escape(command.icon ?? "ti ti-slash", true), false), _$escape(_$createComponent(Show, {
1373
1872
  get when() {
1374
1873
  return highlight();
1375
1874
  },
@@ -1379,10 +1878,20 @@ export function ChatComposer(props) {
1379
1878
  children: range => [title().slice(0, range()[0]), _$ssr(_tmpl$14, _$escape(title().slice(range()[0], range()[1]))), title().slice(range()[1])]
1380
1879
  })), _$escape(_$createComponent(Show, {
1381
1880
  get when() {
1382
- return command.reason ?? command.description;
1881
+ return _$memo(() => !!command.open)() && !command.reason;
1882
+ },
1883
+ get fallback() {
1884
+ return _$createComponent(Show, {
1885
+ get when() {
1886
+ return command.reason ?? command.description;
1887
+ },
1888
+ get children() {
1889
+ return _$ssr(_tmpl$15, _$ssrAttribute("id", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false), _$escape(command.reason ?? command.description));
1890
+ }
1891
+ });
1383
1892
  },
1384
1893
  get children() {
1385
- return _$ssr(_tmpl$11, _$ssrAttribute("id", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false), _$escape(command.reason ?? command.description));
1894
+ return _$ssr(_tmpl$11, _$ssrAttribute("data-state", opening() ? "busy" : failure() ? "failed" : _$escape(undefined, true), false), _$ssrAttribute("aria-hidden", opening() ? "true" : _$escape(undefined, true), false), _$escape(failure() ?? command.description), _$ssrAttribute("aria-hidden", opening() ? _$escape(undefined, true) : "true", false), _$escape(command.busyLabel ?? chatMessages().commandOpening));
1386
1895
  }
1387
1896
  })), _$escape(_$createComponent(Show, {
1388
1897
  get when() {
@@ -1393,25 +1902,25 @@ export function ChatComposer(props) {
1393
1902
  }
1394
1903
  })));
1395
1904
  };
1396
- return _$ssr(_tmpl$25, _$escape(_$createComponent(Show, {
1905
+ return _$ssr(_tmpl$27, _$escape(_$createComponent(Show, {
1397
1906
  get when() {
1398
1907
  return suggestionsOpen() || hasAccessory();
1399
1908
  },
1400
1909
  get children() {
1401
- return _$ssr(_tmpl$20, _$ssrAttribute("data-overlay", hasAccessory() ? _$escape(undefined, true) : "true", false), _$ssrStyleProperty("visibility:", suggestionsOpen() ? "hidden" : _$escape(undefined, true)), _$ssrAttribute("inert", suggestionsOpen(), true), _$escape(accessory()), _$escape(_$createComponent(Show, {
1910
+ return _$ssr(_tmpl$22, _$ssrAttribute("data-overlay", hasAccessory() ? _$escape(undefined, true) : "true", false), _$ssrStyleProperty("visibility:", suggestionsOpen() ? "hidden" : _$escape(undefined, true)), _$ssrAttribute("inert", suggestionsOpen(), true), _$escape(accessory()), _$escape(_$createComponent(Show, {
1402
1911
  get when() {
1403
1912
  return emojiOpen();
1404
1913
  },
1405
1914
  get children() {
1406
- return _$ssr(_tmpl$15, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().emojiSuggestions, true), false), _$escape(_$createComponent(For, {
1915
+ return _$ssr(_tmpl$16, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().emojiSuggestions, true), false), _$escape(_$createComponent(For, {
1407
1916
  get each() {
1408
1917
  return emojiMatches();
1409
1918
  },
1410
- children: (entry, index) => _$ssr(_tmpl$26, `${_$escape(commandListId, true)}-${_$escape(index(), true)}`, _$ssrAttribute("aria-selected", _$escape(index(), true) === _$escape(selectedCommandIndex(), true), false) + _$ssrAttribute("aria-label", _$escape(emojiName(entry, locale()), true), false) + _$ssrAttribute("data-active", index() === selectedCommandIndex() ? "true" : _$escape(undefined, true), false), _$escape(emojiInTone(entry, props.emoji?.skinTone ?? 0)), _$escape(emojiName(entry, locale())), _$escape(_$createComponent(Show, {
1919
+ children: (entry, index) => _$ssr(_tmpl$28, `${_$escape(commandListId, true)}-${_$escape(index(), true)}`, _$ssrAttribute("aria-selected", _$escape(index(), true) === _$escape(selectedCommandIndex(), true), false) + _$ssrAttribute("aria-label", _$escape(emojiName(entry, locale()), true), false) + _$ssrAttribute("data-active", index() === selectedCommandIndex() ? "true" : _$escape(undefined, true), false), _$escape(emojiInTone(entry, props.emoji?.skinTone ?? 0)), _$escape(emojiName(entry, locale())), _$escape(_$createComponent(Show, {
1411
1920
  get when() {
1412
1921
  return entry.shortcodes[0];
1413
1922
  },
1414
- children: code => _$ssr(_tmpl$27, _$escape(code()))
1923
+ children: code => _$ssr(_tmpl$29, _$escape(code()))
1415
1924
  })))
1416
1925
  })));
1417
1926
  }
@@ -1420,7 +1929,14 @@ export function ChatComposer(props) {
1420
1929
  return commandsOpen();
1421
1930
  },
1422
1931
  get children() {
1423
- return _$ssr(_tmpl$19, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().commands, true), false) + _$ssrAttribute("aria-busy", searching() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(For, {
1932
+ return _$ssr(_tmpl$21, _$ssrAttribute("data-search", addSearch() ? "" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
1933
+ get when() {
1934
+ return addSearch();
1935
+ },
1936
+ get children() {
1937
+ return _$ssr(_tmpl$17, _$ssrAttribute("value", _$escape(addQuery(), true), false) + _$ssrAttribute("placeholder", _$escape(chatMessages().commandSearch, true), false) + _$ssrAttribute("aria-label", _$escape(chatMessages().commandSearchLabel, true), false), _$ssrAttribute("aria-controls", _$escape(commandListId, true), false), _$ssrAttribute("aria-activedescendant", selectedCommand() ? _$escape(commandOptionId(selectedCommand()), true) : _$escape(undefined, true), false), _$ssrAttribute("readonly", _$escape(openingRow(), true) !== _$escape(undefined, true), false));
1938
+ }
1939
+ })), _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().commands, true), false) + _$ssrAttribute("aria-busy", searching() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(For, {
1424
1940
  get each() {
1425
1941
  return commandModel().groups.map(commandGroupKey);
1426
1942
  },
@@ -1439,52 +1955,58 @@ export function ChatComposer(props) {
1439
1955
  });
1440
1956
  },
1441
1957
  get children() {
1442
- return _$ssr(_tmpl$30, _$ssrAttribute("aria-labelledby", _$escape(headingId(), true), false), _$ssrAttribute("id", _$escape(headingId(), true), false), _$escape(group()?.scopeTitle ?? group()?.section?.label), _$escape(_$createComponent(Show, {
1958
+ return _$ssr(_tmpl$31, _$ssrAttribute("aria-labelledby", _$escape(headingId(), true), false), _$ssrAttribute("id", _$escape(headingId(), true), false), _$escape(group()?.scopeTitle ?? group()?.section?.label), _$escape(_$createComponent(Show, {
1443
1959
  get when() {
1444
1960
  return group()?.loading;
1445
1961
  },
1446
1962
  get children() {
1447
- return _$ssr(_tmpl$28);
1963
+ return _$ssr(_tmpl$30);
1448
1964
  }
1449
1965
  })), _$escape(_$createComponent(For, {
1450
1966
  get each() {
1451
1967
  return rows();
1452
1968
  },
1453
1969
  children: renderCommandRow
1454
- })), _$escape(_$createComponent(Show, {
1455
- get when() {
1456
- return group()?.placeholder;
1970
+ })), _$escape(_$createComponent(Index, {
1971
+ get each() {
1972
+ return Array.from({
1973
+ length: Math.max(group()?.placeholder ? 1 : 0, group()?.pendingRows ?? 0)
1974
+ });
1457
1975
  },
1458
- get children() {
1459
- return _$ssr(_tmpl$29);
1460
- }
1976
+ children: () => _$ssr(_tmpl$32)
1461
1977
  })));
1462
1978
  }
1463
1979
  });
1464
1980
  }
1465
1981
  })), _$escape(_$createComponent(Show, {
1466
1982
  get when() {
1467
- return _$memo(() => !!(searching() && commandMatches().length === 0))() && !commandModel().groups.some(group => group.section || group.scopeTitle);
1983
+ return _$memo(() => !!(searching() && foundCommandCount() === 0))() && !commandModel().groups.some(group => group.section || group.scopeTitle);
1468
1984
  },
1469
1985
  get children() {
1470
- return _$ssr(_tmpl$16, _$escape(messages().loading));
1986
+ return _$ssr(_tmpl$18, _$escape(messages().loading));
1471
1987
  }
1472
1988
  })), _$escape(_$createComponent(Show, {
1473
1989
  get when() {
1474
1990
  return searchError();
1475
1991
  },
1476
1992
  get children() {
1477
- return _$ssr(_tmpl$17, _$escape(messages().error));
1993
+ return _$ssr(_tmpl$19, _$escape(messages().error));
1478
1994
  }
1479
1995
  })), _$escape(_$createComponent(Show, {
1480
1996
  get when() {
1481
- return _$memo(() => !!(!searching() && !searchError()))() && commandMatches().length === 0;
1997
+ return _$memo(() => !!(!searching() && !searchError() && foundCommandCount() === 0))() && !commandModel().groups.some(group => group.pendingRows);
1482
1998
  },
1483
1999
  get children() {
1484
- return _$ssr(_tmpl$18, !parsedCommandQuery().query.trim() ? _$escape(messages().noResults) : _$escape(chatMessages().commandsNothingFound({
2000
+ return _$ssr(_tmpl$20, !parsedCommandQuery().query.trim() ? _$escape(messages().noResults) : _$escape(chatMessages().commandsNothingFound({
1485
2001
  query: parsedCommandQuery().query.trim()
1486
2002
  })));
1487
2003
  }
2004
+ })), _$escape(_$createComponent(Show, {
2005
+ get when() {
2006
+ return commandModel().fallback?.command;
2007
+ },
2008
+ keyed: true,
2009
+ children: command => renderCommandRow(command)
1488
2010
  })));
1489
2011
  }
1490
2012
  })));
@@ -1494,18 +2016,18 @@ export function ChatComposer(props) {
1494
2016
  return showHint();
1495
2017
  },
1496
2018
  get children() {
1497
- return _$ssr(_tmpl$21, _$escape(_$createComponent(Show, {
2019
+ return _$ssr(_tmpl$23, _$escape(_$createComponent(Show, {
1498
2020
  get when() {
1499
2021
  return dictationStatus();
1500
2022
  },
1501
2023
  get fallback() {
1502
- return _$ssr(_tmpl$31, _$escape(hint()));
2024
+ return _$ssr(_tmpl$33, _$escape(hint()));
1503
2025
  },
1504
- children: status => [_$ssr(_tmpl$32, _$ssrAttribute("data-state", _$escape(dictation() ?? undefined, true), false), _$ssrAttribute("class", _$escape(status().icon, true), false), _$escape(status().text)), _$createComponent(Show, {
2026
+ children: status => [_$ssr(_tmpl$34, _$ssrAttribute("data-state", _$escape(dictation() ?? undefined, true), false), _$ssrAttribute("class", _$escape(status().icon, true), false), _$escape(status().text)), _$createComponent(Show, {
1505
2027
  get when() {
1506
2028
  return _$memo(() => dictation() === "refined")() ? props.microphone?.onRestoreOriginal : undefined;
1507
2029
  },
1508
- children: restore => _$ssr(_tmpl$33, _$ssrAttribute("disabled", microphoneDisabled(), true), _$escape(chatMessages().restoreOriginal))
2030
+ children: restore => _$ssr(_tmpl$35, _$ssrAttribute("disabled", microphoneDisabled(), true), _$escape(chatMessages().restoreOriginal))
1509
2031
  })]
1510
2032
  })));
1511
2033
  }
@@ -1514,16 +2036,16 @@ export function ChatComposer(props) {
1514
2036
  return attachments().length > 0;
1515
2037
  },
1516
2038
  get children() {
1517
- return _$ssr(_tmpl$22, _$ssrAttribute("aria-label", _$escape(chatMessages().attachments, true), false), _$escape(_$createComponent(For, {
2039
+ return _$ssr(_tmpl$24, _$ssrAttribute("aria-label", _$escape(chatMessages().attachments, true), false), _$escape(_$createComponent(For, {
1518
2040
  get each() {
1519
2041
  return attachments();
1520
2042
  },
1521
- children: attachment => _$ssr(_tmpl$35, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
2043
+ children: attachment => _$ssr(_tmpl$37, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
1522
2044
  get when() {
1523
2045
  return attachment.href;
1524
2046
  },
1525
2047
  get fallback() {
1526
- return _$ssr(_tmpl$37, _$escape(_$createComponent(Show, {
2048
+ return _$ssr(_tmpl$39, _$escape(_$createComponent(Show, {
1527
2049
  get when() {
1528
2050
  return _$memo(() => attachment.kind === "image")() && attachment.previewUrl;
1529
2051
  },
@@ -1531,11 +2053,11 @@ export function ChatComposer(props) {
1531
2053
  return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
1532
2054
  },
1533
2055
  get children() {
1534
- return _$ssr(_tmpl$36, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
2056
+ return _$ssr(_tmpl$38, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1535
2057
  }
1536
2058
  })), _$escape(renderAttachmentCopy(attachment)));
1537
2059
  },
1538
- children: href => _$ssr(_tmpl$38, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().openInNewTab({
2060
+ children: href => _$ssr(_tmpl$40, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().openInNewTab({
1539
2061
  name: attachment.name
1540
2062
  }), true), false), _$escape(_$createComponent(Show, {
1541
2063
  get when() {
@@ -1545,14 +2067,14 @@ export function ChatComposer(props) {
1545
2067
  return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
1546
2068
  },
1547
2069
  get children() {
1548
- return _$ssr(_tmpl$36, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
2070
+ return _$ssr(_tmpl$38, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1549
2071
  }
1550
2072
  })), _$escape(renderAttachmentCopy(attachment)))
1551
2073
  })), _$escape(_$createComponent(Show, {
1552
2074
  get when() {
1553
2075
  return attachment.action;
1554
2076
  },
1555
- children: action => _$ssr(_tmpl$39, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
2077
+ children: action => _$ssr(_tmpl$41, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
1556
2078
  get when() {
1557
2079
  return action().icon;
1558
2080
  },
@@ -1563,7 +2085,7 @@ export function ChatComposer(props) {
1563
2085
  return props.onAttachmentsChange;
1564
2086
  },
1565
2087
  get children() {
1566
- return _$ssr(_tmpl$34, _$ssrAttribute("aria-label", _$escape(selectMessages().removeNamed({
2088
+ return _$ssr(_tmpl$36, _$ssrAttribute("aria-label", _$escape(selectMessages().removeNamed({
1567
2089
  name: attachment.name
1568
2090
  }), true), false), _$ssrAttribute("disabled", blocked(), true));
1569
2091
  }
@@ -1580,19 +2102,29 @@ export function ChatComposer(props) {
1580
2102
  return props.error;
1581
2103
  },
1582
2104
  get children() {
1583
- return _$ssr(_tmpl$23, _$escape(props.error));
2105
+ return _$ssr(_tmpl$25, _$escape(props.error));
1584
2106
  }
1585
- }), formatRow(), _$ssr(_tmpl$40, _$escape(_$createComponent(Show, {
2107
+ }), formatRow(), _$ssr(_tmpl$42, _$escape(_$createComponent(Show, {
1586
2108
  get when() {
1587
2109
  return footer();
1588
2110
  },
1589
2111
  get fallback() {
1590
- return [_$ssr(_tmpl$41, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
2112
+ return [_$ssr(_tmpl$43, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
1591
2113
  get when() {
1592
- return hasAddMenu();
2114
+ return addOpensCommands();
2115
+ },
2116
+ get fallback() {
2117
+ return _$createComponent(Show, {
2118
+ get when() {
2119
+ return hasAddMenu();
2120
+ },
2121
+ get children() {
2122
+ return addMenu("ti ti-plus");
2123
+ }
2124
+ });
1593
2125
  },
1594
2126
  get children() {
1595
- return addMenu("ti ti-plus");
2127
+ return addSearchButton();
1596
2128
  }
1597
2129
  })), _$escape(fileInput()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(_$createComponent(Show, {
1598
2130
  get when() {
@@ -1640,7 +2172,7 @@ export function ChatComposer(props) {
1640
2172
  }
1641
2173
  });
1642
2174
  }
1643
- })), _$escape(props.modelDetails)), _$ssr(_tmpl$42, _$escape(_$createComponent(For, {
2175
+ })), _$escape(props.modelDetails)), _$ssr(_tmpl$44, _$escape(_$createComponent(For, {
1644
2176
  get each() {
1645
2177
  return props.contextActions;
1646
2178
  },
@@ -1692,9 +2224,9 @@ export function ChatComposer(props) {
1692
2224
  return props.error;
1693
2225
  },
1694
2226
  get children() {
1695
- return _$ssr(_tmpl$23, _$escape(props.error));
2227
+ return _$ssr(_tmpl$25, _$escape(props.error));
1696
2228
  }
1697
- }), formatRow(), _$ssr(_tmpl$24, _$escape(attachControl()), _$escape(fileInput()), _$escape(field()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(microphoneControl()), _$escape(sendControl()))];
2229
+ }), formatRow(), _$ssr(_tmpl$26, _$escape(attachControl()), _$escape(fileInput()), _$escape(field()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(microphoneControl()), _$escape(sendControl()))];
1698
2230
  }
1699
2231
  })), _$escape(_$createComponent(Show, {
1700
2232
  get when() {
@@ -1720,18 +2252,18 @@ export function ChatComposer(props) {
1720
2252
  return suggestionAnnouncement();
1721
2253
  },
1722
2254
  keyed: true,
1723
- children: announced => _$ssr(_tmpl$43, _$escape(announced.text))
2255
+ children: announced => _$ssr(_tmpl$45, _$escape(announced.text))
1724
2256
  })), _$escape(_$createComponent(Show, {
1725
2257
  get when() {
1726
2258
  return _$memo(() => !!!suggestionsOpen())() && commandStatus();
1727
2259
  },
1728
- children: status => _$ssr(_tmpl$44, _$ssrAttribute("data-tone", _$escape(status().tone, true), false), _$ssrAttribute("class", status().tone === "busy" ? "ti ti-loader-2 k2b-spin" : status().tone === "done" ? "ti ti-check" : "ti ti-alert-circle", false), _$escape(status().text))
2260
+ children: status => _$ssr(_tmpl$46, _$ssrAttribute("data-tone", _$escape(status().tone, true), false), _$ssrAttribute("class", status().tone === "busy" ? "ti ti-loader-2 k2b-spin" : status().tone === "done" ? "ti ti-check" : "ti ti-alert-circle", false), _$escape(status().text))
1729
2261
  })), _$escape(_$createComponent(Show, {
1730
2262
  get when() {
1731
2263
  return _$memo(() => !!suggestionsOpen())() && announcement();
1732
2264
  },
1733
2265
  keyed: true,
1734
- children: announced => _$ssr(_tmpl$45, _$escape(announced.text))
2266
+ children: announced => _$ssr(_tmpl$47, _$escape(announced.text))
1735
2267
  })));
1736
2268
  }
1737
2269
  //# sourceMappingURL=ChatComposer.js.map