@k2b/ui 0.27.0 → 0.28.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 (191) hide show
  1. package/README.md +1 -1
  2. package/dist/base.css +147 -2
  3. package/dist/browser/actions/ContextMenu.js +30 -4
  4. package/dist/browser/actions/ContextMenu.js.map +1 -1
  5. package/dist/browser/actions/Dropdown.js +184 -158
  6. package/dist/browser/actions/Dropdown.js.map +1 -1
  7. package/dist/browser/actions/GestureMenu.js +71 -138
  8. package/dist/browser/actions/GestureMenu.js.map +1 -1
  9. package/dist/browser/actions/Toolbar.js +379 -48
  10. package/dist/browser/actions/Toolbar.js.map +1 -1
  11. package/dist/browser/actions/index.js.map +1 -1
  12. package/dist/browser/actions/menu-rows.js +15 -0
  13. package/dist/browser/actions/menu-rows.js.map +1 -0
  14. package/dist/browser/actions/menu-sheet.js +154 -0
  15. package/dist/browser/actions/menu-sheet.js.map +1 -0
  16. package/dist/browser/actions/toolbar-messages.js +20 -0
  17. package/dist/browser/actions/toolbar-messages.js.map +1 -0
  18. package/dist/browser/chart-inspection.js +3 -0
  19. package/dist/browser/chart-inspection.js.map +1 -0
  20. package/dist/browser/chat/ChatComposer.js +581 -397
  21. package/dist/browser/chat/ChatComposer.js.map +1 -1
  22. package/dist/browser/chat/chat-messages.js +19 -1
  23. package/dist/browser/chat/chat-messages.js.map +1 -1
  24. package/dist/browser/chat/command-search.js +3 -0
  25. package/dist/browser/chat/command-search.js.map +1 -1
  26. package/dist/browser/chat/index.js +2 -0
  27. package/dist/browser/chat/index.js.map +1 -1
  28. package/dist/browser/chat/reaction-people.js +15 -0
  29. package/dist/browser/chat/reaction-people.js.map +1 -0
  30. package/dist/browser/content/Chart.js +6 -15
  31. package/dist/browser/content/Chart.js.map +1 -1
  32. package/dist/browser/content/ChartSnapshotView.js +7 -10
  33. package/dist/browser/content/ChartSnapshotView.js.map +1 -1
  34. package/dist/browser/content/MessageRow.js +330 -448
  35. package/dist/browser/content/MessageRow.js.map +1 -1
  36. package/dist/browser/content/ReactionBar.js +419 -0
  37. package/dist/browser/content/ReactionBar.js.map +1 -0
  38. package/dist/browser/content/chart-inspection-core.js +557 -0
  39. package/dist/browser/content/chart-inspection-core.js.map +1 -0
  40. package/dist/browser/content/chart-inspection.js +10 -445
  41. package/dist/browser/content/chart-inspection.js.map +1 -1
  42. package/dist/browser/content/chart-line-gaps.js +1 -1
  43. package/dist/browser/content/chart-line-gaps.js.map +1 -1
  44. package/dist/browser/content/chart-snapshot.js +1 -1
  45. package/dist/browser/content/chart-snapshot.js.map +1 -1
  46. package/dist/browser/content/index.js +1 -0
  47. package/dist/browser/content/index.js.map +1 -1
  48. package/dist/browser/feedback/dialog-core.js +135 -23
  49. package/dist/browser/feedback/dialog-core.js.map +1 -1
  50. package/dist/browser/index.js +1 -0
  51. package/dist/browser/index.js.map +1 -1
  52. package/dist/browser/input-mode.js +40 -0
  53. package/dist/browser/input-mode.js.map +1 -0
  54. package/dist/browser/inputs/FileDropTarget.js +24 -9
  55. package/dist/browser/inputs/FileDropTarget.js.map +1 -1
  56. package/dist/browser/inputs/FileInputs.js +2 -2
  57. package/dist/browser/inputs/FileInputs.js.map +1 -1
  58. package/dist/browser/inputs/SelectChip.js +9 -0
  59. package/dist/browser/inputs/SelectChip.js.map +1 -1
  60. package/dist/browser/inputs/emoji/EmojiPicker.js +13 -0
  61. package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -1
  62. package/dist/browser/inputs/file-drop.js.map +1 -1
  63. package/dist/browser/layout/DetailPanel.js +283 -197
  64. package/dist/browser/layout/DetailPanel.js.map +1 -1
  65. package/dist/browser/layout/Discussion.js +233 -185
  66. package/dist/browser/layout/Discussion.js.map +1 -1
  67. package/dist/browser/layout/TableOfContents.js +10 -2
  68. package/dist/browser/layout/TableOfContents.js.map +1 -1
  69. package/dist/browser/layout/detail-panel-messages.js +35 -0
  70. package/dist/browser/layout/detail-panel-messages.js.map +1 -0
  71. package/dist/browser/surfaces/Avatar.js +50 -20
  72. package/dist/browser/surfaces/Avatar.js.map +1 -1
  73. package/dist/browser/surfaces/AvatarStack.js +406 -0
  74. package/dist/browser/surfaces/AvatarStack.js.map +1 -0
  75. package/dist/browser/surfaces/avatar-messages.js +8 -2
  76. package/dist/browser/surfaces/avatar-messages.js.map +1 -1
  77. package/dist/browser/surfaces/index.js +1 -0
  78. package/dist/browser/surfaces/index.js.map +1 -1
  79. package/dist/ssr/actions/ContextMenu.js +21 -3
  80. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  81. package/dist/ssr/actions/Dropdown.js +140 -72
  82. package/dist/ssr/actions/Dropdown.js.map +1 -1
  83. package/dist/ssr/actions/GestureMenu.js +52 -106
  84. package/dist/ssr/actions/GestureMenu.js.map +1 -1
  85. package/dist/ssr/actions/Toolbar.js +294 -14
  86. package/dist/ssr/actions/Toolbar.js.map +1 -1
  87. package/dist/ssr/actions/index.js.map +1 -1
  88. package/dist/ssr/actions/menu-rows.js +15 -0
  89. package/dist/ssr/actions/menu-rows.js.map +1 -0
  90. package/dist/ssr/actions/menu-sheet.js +138 -0
  91. package/dist/ssr/actions/menu-sheet.js.map +1 -0
  92. package/dist/ssr/actions/toolbar-messages.js +20 -0
  93. package/dist/ssr/actions/toolbar-messages.js.map +1 -0
  94. package/dist/ssr/chart-inspection.js +3 -0
  95. package/dist/ssr/chart-inspection.js.map +1 -0
  96. package/dist/ssr/chat/ChatComposer.js +263 -89
  97. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  98. package/dist/ssr/chat/chat-messages.js +19 -1
  99. package/dist/ssr/chat/chat-messages.js.map +1 -1
  100. package/dist/ssr/chat/command-search.js +3 -0
  101. package/dist/ssr/chat/command-search.js.map +1 -1
  102. package/dist/ssr/chat/index.js +2 -0
  103. package/dist/ssr/chat/index.js.map +1 -1
  104. package/dist/ssr/chat/reaction-people.js +15 -0
  105. package/dist/ssr/chat/reaction-people.js.map +1 -0
  106. package/dist/ssr/content/Chart.js +6 -15
  107. package/dist/ssr/content/Chart.js.map +1 -1
  108. package/dist/ssr/content/ChartSnapshotView.js +7 -10
  109. package/dist/ssr/content/ChartSnapshotView.js.map +1 -1
  110. package/dist/ssr/content/MessageRow.js +98 -152
  111. package/dist/ssr/content/MessageRow.js.map +1 -1
  112. package/dist/ssr/content/ReactionBar.js +304 -0
  113. package/dist/ssr/content/ReactionBar.js.map +1 -0
  114. package/dist/ssr/content/chart-inspection-core.js +557 -0
  115. package/dist/ssr/content/chart-inspection-core.js.map +1 -0
  116. package/dist/ssr/content/chart-inspection.js +10 -445
  117. package/dist/ssr/content/chart-inspection.js.map +1 -1
  118. package/dist/ssr/content/chart-line-gaps.js +1 -1
  119. package/dist/ssr/content/chart-line-gaps.js.map +1 -1
  120. package/dist/ssr/content/chart-snapshot.js +1 -1
  121. package/dist/ssr/content/chart-snapshot.js.map +1 -1
  122. package/dist/ssr/content/index.js +1 -0
  123. package/dist/ssr/content/index.js.map +1 -1
  124. package/dist/ssr/feedback/dialog-core.js +135 -23
  125. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  126. package/dist/ssr/index.js +1 -0
  127. package/dist/ssr/index.js.map +1 -1
  128. package/dist/ssr/input-mode.js +40 -0
  129. package/dist/ssr/input-mode.js.map +1 -0
  130. package/dist/ssr/inputs/FileDropTarget.js +17 -5
  131. package/dist/ssr/inputs/FileDropTarget.js.map +1 -1
  132. package/dist/ssr/inputs/FileInputs.js +2 -2
  133. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  134. package/dist/ssr/inputs/SelectChip.js +9 -0
  135. package/dist/ssr/inputs/SelectChip.js.map +1 -1
  136. package/dist/ssr/inputs/emoji/EmojiPicker.js +12 -0
  137. package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -1
  138. package/dist/ssr/inputs/file-drop.js.map +1 -1
  139. package/dist/ssr/layout/DetailPanel.js +112 -58
  140. package/dist/ssr/layout/DetailPanel.js.map +1 -1
  141. package/dist/ssr/layout/Discussion.js +63 -36
  142. package/dist/ssr/layout/Discussion.js.map +1 -1
  143. package/dist/ssr/layout/TableOfContents.js +10 -2
  144. package/dist/ssr/layout/TableOfContents.js.map +1 -1
  145. package/dist/ssr/layout/detail-panel-messages.js +35 -0
  146. package/dist/ssr/layout/detail-panel-messages.js.map +1 -0
  147. package/dist/ssr/surfaces/Avatar.js +20 -2
  148. package/dist/ssr/surfaces/Avatar.js.map +1 -1
  149. package/dist/ssr/surfaces/AvatarStack.js +286 -0
  150. package/dist/ssr/surfaces/AvatarStack.js.map +1 -0
  151. package/dist/ssr/surfaces/avatar-messages.js +8 -2
  152. package/dist/ssr/surfaces/avatar-messages.js.map +1 -1
  153. package/dist/ssr/surfaces/index.js +1 -0
  154. package/dist/ssr/surfaces/index.js.map +1 -1
  155. package/dist/styles.css +1 -1
  156. package/dist/types/actions/ContextMenu.d.ts +8 -0
  157. package/dist/types/actions/Dropdown.d.ts +12 -5
  158. package/dist/types/actions/GestureMenu.d.ts +6 -4
  159. package/dist/types/actions/Toolbar.d.ts +20 -0
  160. package/dist/types/actions/index.d.ts +1 -1
  161. package/dist/types/actions/menu-rows.d.ts +5 -0
  162. package/dist/types/actions/menu-sheet.d.ts +22 -0
  163. package/dist/types/actions/toolbar-messages.d.ts +8 -0
  164. package/dist/types/chart-inspection.d.ts +2 -0
  165. package/dist/types/chat/ChatComposer.d.ts +7 -0
  166. package/dist/types/chat/chat-messages.d.ts +10 -2
  167. package/dist/types/chat/command-search.d.ts +2 -0
  168. package/dist/types/chat/index.d.ts +2 -0
  169. package/dist/types/chat/reaction-people.d.ts +9 -0
  170. package/dist/types/content/Chart.d.ts +2 -2
  171. package/dist/types/content/MessageRow.d.ts +3 -10
  172. package/dist/types/content/ReactionBar.d.ts +32 -0
  173. package/dist/types/content/chart-inspection-core.d.ts +61 -0
  174. package/dist/types/content/chart-inspection.d.ts +4 -20
  175. package/dist/types/content/chart-snapshot.d.ts +1 -1
  176. package/dist/types/content/index.d.ts +2 -1
  177. package/dist/types/feedback/dialog-core.d.ts +16 -1
  178. package/dist/types/index.d.ts +1 -0
  179. package/dist/types/input-mode.d.ts +7 -0
  180. package/dist/types/inputs/FileDropTarget.d.ts +6 -3
  181. package/dist/types/inputs/SelectChip.d.ts +2 -0
  182. package/dist/types/inputs/file-drop.d.ts +2 -0
  183. package/dist/types/layout/DetailPanel.d.ts +11 -0
  184. package/dist/types/layout/Discussion.d.ts +8 -0
  185. package/dist/types/layout/TableOfContents.d.ts +2 -0
  186. package/dist/types/layout/detail-panel-messages.d.ts +15 -0
  187. package/dist/types/surfaces/Avatar.d.ts +12 -0
  188. package/dist/types/surfaces/AvatarStack.d.ts +41 -0
  189. package/dist/types/surfaces/avatar-messages.d.ts +3 -0
  190. package/dist/types/surfaces/index.d.ts +3 -1
  191. package/package.json +6 -1
@@ -18,41 +18,43 @@ var _tmpl$ = ["<span><strong", ">", "</strong></span>"],
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
20
  _tmpl$11 = ["<small", ">", "</small>"],
21
- _tmpl$12 = ["<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>"],
22
- _tmpl$13 = ["<b>", "</b>"],
23
- _tmpl$14 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "</div></div>"],
24
- _tmpl$15 = ["<div role=\"status\" class=\"k2b-chat-composer__loading\"><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i><strong>", "</strong></div>"],
25
- _tmpl$16 = ["<div role=\"status\"><i class=\"ti ti-alert-circle\"></i> ", "</div>"],
26
- _tmpl$17 = ["<div role=\"status\">", "</div>"],
27
- _tmpl$18 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "", "", "", "</div></div>"],
28
- _tmpl$19 = ["<div class=\"k2b-chat-composer-slot\"", "><div style=\"", "\"", ">", "</div>", "", "</div>"],
29
- _tmpl$20 = ["<div class=\"k2b-chat-composer__hint\" aria-live=\"polite\">", "</div>"],
30
- _tmpl$21 = ["<div class=\"k2b-chat-composer__attachments\" role=\"list\"", " tabindex=\"0\">", "</div>"],
31
- _tmpl$22 = ["<div class=\"k2b-chat-composer__error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</div>"],
32
- _tmpl$23 = ["<div class=\"k2b-chat-composer__row\">", "", "<div class=\"k2b-chat-composer__field\">", "<div class=\"k2b-chat-composer__field-tools\">", "", "", "</div></div>", "</div>"],
33
- _tmpl$24 = ["<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>"],
34
- _tmpl$25 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><span class=\"k2b-chat-composer__emoji-glyph\" aria-hidden=\"true\">", "</span><strong>", "</strong>", "</button>"],
35
- _tmpl$26 = ["<small>:", ":</small>"],
36
- _tmpl$27 = "<i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>",
37
- _tmpl$28 = "<div class=\"k2b-chat-composer__command-placeholder\" aria-hidden=\"true\"><span></span><span><span></span><span></span></span></div>",
38
- _tmpl$29 = ["<div class=\"k2b-chat-composer__command-group\" role=\"group\"", "><div class=\"k2b-chat-composer__command-heading\"", " role=\"presentation\"><span>", "</span>", "</div>", "", "</div>"],
39
- _tmpl$30 = ["<span class=\"k2b-chat-composer__hint-text\">", "</span>"],
40
- _tmpl$31 = ["<span class=\"k2b-chat-composer__dictation\"", "><i", " aria-hidden=\"true\"></i><span>", "</span></span>"],
41
- _tmpl$32 = ["<button type=\"button\" class=\"k2b-chat-composer__hint-action\"", ">", "</button>"],
42
- _tmpl$33 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-remove\"", "", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
43
- _tmpl$34 = ["<div class=\"k2b-chat-composer__attachment\" role=\"listitem\"><div class=\"", "\">", "", "</div>", "</div>"],
44
- _tmpl$35 = ["<img", ">"],
45
- _tmpl$36 = ["<div class=\"k2b-chat-composer__attachment-identity\">", "", "</div>"],
46
- _tmpl$37 = ["<a class=\"k2b-chat-composer__attachment-link\"", " target=\"_blank\" rel=\"noopener noreferrer\"", ">", "", "</a>"],
47
- _tmpl$38 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-action\"", ">", "", "</button>"],
48
- _tmpl$39 = ["<footer class=\"k2b-chat-composer__footer\">", "</footer>"],
49
- _tmpl$40 = ["<div class=\"k2b-chat-composer__tools\">", "", "", "", "", "", "", "</div>"],
50
- _tmpl$41 = ["<div class=\"k2b-chat-composer__submit\">", "", "", "", "", "</div>"],
51
- _tmpl$42 = ["<span>", "</span>"],
52
- _tmpl$43 = ["<div class=\"k2b-chat-composer__status-line\"", "><i aria-hidden=\"true\"", "></i><span>", "</span></div>"],
53
- _tmpl$44 = ["<span class=\"k2b-sr-only\">", "</span>"];
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
+ _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
+ _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>"];
54
55
  import { batch, children, createEffect, createMemo, createSignal, createUniqueId, For, onCleanup, onMount, Show, untrack } from "solid-js";
55
56
  import { Dropdown } from "../actions/Dropdown";
57
+ import { dialogHistorySettled, holdDialogHistory } from "../feedback/dialog-core";
56
58
  import { Tooltip } from "../feedback/Tooltip";
57
59
  import { replaceTextareaRange } from "../inputs/editor-dom";
58
60
  import { completedEmojiShortcode, emojiInTone, emojiName, emojiShortcodeQuery, loadEmojiIndex, suggestEmoji } from "../inputs/emoji/emoji-index";
@@ -114,12 +116,40 @@ const isTouchOnly = () => typeof matchMedia === "function" && matchMedia(touchOn
114
116
  const microphoneHoldMs = 500;
115
117
  /** Keeps the field focused (and a phone's keyboard open) when a composer button is pressed with a mouse or finger. */
116
118
  const keepFieldFocus = event => event.preventDefault();
119
+ /**
120
+ * Keeps one tap from reaching the page: its pointer, touch and mouse events stop at the window, and neither focus nor a
121
+ * click happens. A finger's tap ends with `touchend`, a scroll with `pointercancel`, a mouse's press with `click`.
122
+ */
123
+ const swallowTap = down => {
124
+ const blocked = ["pointerup", "pointercancel", "touchstart", "touchend", "mousedown", "mouseup", "click", "contextmenu"];
125
+ const stop = () => {
126
+ window.removeEventListener("pointerdown", next, true);
127
+ for (const type of blocked) window.removeEventListener(type, block, true);
128
+ };
129
+ const block = event => {
130
+ event.stopPropagation();
131
+ if (event.type !== "pointercancel" && event.type !== "touchstart" && event.type !== "pointerup") event.preventDefault();
132
+ if (event.type === "click" || event.type === "touchend" || event.type === "pointercancel") stop();
133
+ };
134
+ // The next press is a new gesture, whatever became of this one.
135
+ const next = event => {
136
+ if (event !== down) stop();
137
+ };
138
+ down.stopPropagation();
139
+ down.preventDefault();
140
+ window.addEventListener("pointerdown", next, true);
141
+ for (const type of blocked) window.addEventListener(type, block, {
142
+ capture: true,
143
+ passive: false
144
+ });
145
+ };
117
146
  const attachmentIcon = attachment => attachment.icon ?? (attachment.kind === "image" ? "ti ti-photo" : attachment.kind === "resource" ? "ti ti-link" : "ti ti-file");
118
147
  const hasContent = items => items.some(item => item != null && typeof item !== "boolean" && item !== "");
119
148
  export function ChatComposer(props) {
120
149
  const accessory = children(() => props.accessory);
121
150
  const hasAccessory = () => hasContent(accessory.toArray());
122
151
  const hint = children(() => props.hint);
152
+ const footer = children(() => props.footerContent);
123
153
  const messages = useUiMessages();
124
154
  const markdownToolbarMessages = useMarkdownToolbarMessages();
125
155
  const chatMessages = useChatMessages();
@@ -140,6 +170,7 @@ export function ChatComposer(props) {
140
170
  const [closedAt, setClosedAt] = createSignal();
141
171
  // Where the pending native edit starts; a selection replaced as a whole starts at its beginning.
142
172
  let inputStart;
173
+ let inputEnd;
143
174
  const [commandStatus, setCommandStatus] = createSignal();
144
175
  const [announcement, setAnnouncement] = createSignal();
145
176
  let statusTimer;
@@ -158,6 +189,7 @@ export function ChatComposer(props) {
158
189
  });
159
190
  const [addingFiles, setAddingFiles] = createSignal(false);
160
191
  const [submitting, setSubmitting] = createSignal(false);
192
+ let shellRef;
161
193
  let composerRef;
162
194
  let textareaRef;
163
195
  let fileInputRef;
@@ -185,6 +217,45 @@ export function ChatComposer(props) {
185
217
  const [searching, setSearching] = createSignal(false);
186
218
  const [searchError, setSearchError] = createSignal(false);
187
219
  const [executing, setExecuting] = createSignal(false);
220
+ const [modelOpen, setModelOpen] = createSignal();
221
+ const [choosingModel, setChoosingModel] = createSignal(false);
222
+ let resolveModel;
223
+ const finishModelSelection = id => {
224
+ const resolve = resolveModel;
225
+ resolveModel = undefined;
226
+ if (choosingModel()) {
227
+ setModelOpen(false);
228
+ // Let the controlled close reach Dropdown before returning normal clicks to its own state.
229
+ queueMicrotask(() => {
230
+ if (!choosingModel()) setModelOpen(undefined);
231
+ });
232
+ }
233
+ setChoosingModel(false);
234
+ resolve?.(id);
235
+ };
236
+ const blockedBesidesCommand = () => Boolean(props.disabled || stopping() || state() === "submitting" || addingFiles() || submitting());
237
+ const usableModelControl = () => !conversation() && !footer() && !blockedBesidesCommand() && !running() && Boolean(props.models?.length && props.onModelChange);
238
+ const openModelSelection = () => {
239
+ if (!usableModelControl()) return Promise.resolve(null);
240
+ finishModelSelection(null);
241
+ return new Promise(resolve => {
242
+ resolveModel = resolve;
243
+ setChoosingModel(true);
244
+ setModelOpen(true);
245
+ queueMicrotask(() => {
246
+ if (!choosingModel() || !modelOpen()) return;
247
+ // Start on the current model, as a click on the chip would show it checked.
248
+ const menu = composerRef?.querySelector(".k2b-chat-composer__model");
249
+ (menu?.querySelector('[role="menuitemradio"][aria-checked="true"]:not(:disabled)') ?? menu?.querySelector('[role="menuitemradio"]:not(:disabled)'))?.focus();
250
+ });
251
+ });
252
+ };
253
+ createEffect(() => {
254
+ if (!usableModelControl()) finishModelSelection(null);
255
+ });
256
+ onCleanup(() => finishModelSelection(null));
257
+ const [commandOpening, setCommandOpening] = createSignal(0);
258
+ let retrySearch = false;
188
259
  let highlightRef;
189
260
  let commandListRef;
190
261
  const mentions = () => props.mentions ?? [];
@@ -218,6 +289,12 @@ export function ChatComposer(props) {
218
289
  const token = caretCommandToken();
219
290
  return dismissed() || composing() || token?.start === closedAt() ? null : token;
220
291
  });
292
+ const openingStart = createMemo(() => commandQuery()?.start);
293
+ createEffect(() => {
294
+ props.draftKey;
295
+ const start = openingStart();
296
+ if (start !== undefined) setCommandOpening(opening => opening + 1);
297
+ });
221
298
  const parsedCommandQuery = createMemo(() => parseChatCommandQuery(commandQuery()?.query ?? "", props.commandScopes ?? []));
222
299
  const currentCommandScope = createMemo(() => parsedCommandQuery().scope?.id);
223
300
  createEffect(() => {
@@ -246,20 +323,45 @@ export function ChatComposer(props) {
246
323
  setCommandMoved(false);
247
324
  });
248
325
  const commandsOpen = () => Boolean(!emojiOpen() && commandQuery() && (localCommands().length || props.searchCommands));
326
+ const commandSheetHeld = createMemo(() => commandsOpen() && isTouchOnly());
327
+ createEffect(() => {
328
+ if (!commandSheetHeld()) return;
329
+ onCleanup(holdDialogHistory(() => setClosedAt(commandQuery()?.start)));
330
+ });
249
331
  const suggestionsOpen = () => commandsOpen() || emojiOpen();
250
332
  createEffect(() => {
251
333
  if (!suggestionsOpen()) return;
252
334
  let disposed = false;
253
335
  const measure = () => {
254
336
  if (disposed || !commandListRef) return;
255
- let top = window.visualViewport?.offsetTop ?? 0;
256
- for (let parent = commandListRef.parentElement; parent; parent = parent.parentElement) {
257
- if (/(auto|scroll|hidden|clip)/.test(window.getComputedStyle(parent).overflowY)) {
337
+ const accessory = commandListRef.parentElement;
338
+ const viewportTop = window.visualViewport?.offsetTop ?? 0;
339
+ let top = viewportTop;
340
+ // The column a phone sheet spans: the nearest ancestor that clips sideways, or the page.
341
+ let column;
342
+ for (let parent = accessory.parentElement; parent; parent = parent.parentElement) {
343
+ const style = window.getComputedStyle(parent);
344
+ if (/(auto|scroll|hidden|clip)/.test(style.overflowY)) {
258
345
  top = Math.max(top, parent.getBoundingClientRect().top);
259
346
  }
347
+ if (!column && /(auto|scroll|hidden|clip)/.test(style.overflowX)) {
348
+ const left = parent.getBoundingClientRect().left + parent.clientLeft;
349
+ column = {
350
+ left,
351
+ right: left + parent.clientWidth
352
+ };
353
+ }
260
354
  }
261
- const bottom = commandListRef.parentElement.getBoundingClientRect().bottom;
355
+ const page = document.documentElement.clientWidth;
356
+ const left = Math.max(0, column?.left ?? 0);
357
+ const right = Math.min(page, column?.right ?? page);
358
+ const slot = accessory.parentElement.getBoundingClientRect();
359
+ const bottom = accessory.getBoundingClientRect().bottom;
262
360
  commandListRef.style.setProperty("--k2b-chat-command-space", `${Math.max(40, bottom - top - 8)}px`);
361
+ // 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");
363
+ accessory.style.setProperty("--k2b-chat-command-reach-left", `${Math.max(0, slot.left - left)}px`);
364
+ accessory.style.setProperty("--k2b-chat-command-reach-right", `${Math.max(0, right - slot.right)}px`);
263
365
  };
264
366
  queueMicrotask(measure);
265
367
  window.addEventListener("resize", measure);
@@ -272,6 +374,20 @@ export function ChatComposer(props) {
272
374
  window.visualViewport?.removeEventListener("resize", measure);
273
375
  });
274
376
  });
377
+ // A press outside the command list and the field closes the list, as Escape does; the text stays. With a mouse or a
378
+ // trackpad the press then acts as usual. On a touch-only device a tap outside the composer closes only the list: it
379
+ // reaches nothing beneath, and the field keeps focus, so the keyboard stays open.
380
+ createEffect(() => {
381
+ if (!commandsOpen()) return;
382
+ const onPointerDown = event => {
383
+ const target = event.target;
384
+ if (!(target instanceof Node) || commandListRef?.contains(target) || textareaRef?.parentElement?.contains(target)) return;
385
+ setClosedAt(commandQuery()?.start);
386
+ if (isTouchOnly() && !shellRef?.contains(target)) swallowTap(event);
387
+ };
388
+ window.addEventListener("pointerdown", onPointerDown, true);
389
+ onCleanup(() => window.removeEventListener("pointerdown", onPointerDown, true));
390
+ });
275
391
  createEffect(() => {
276
392
  props.draftKey;
277
393
  const token = commandQuery();
@@ -280,12 +396,17 @@ export function ChatComposer(props) {
280
396
  setSearching(false);
281
397
  return;
282
398
  }
399
+ running();
283
400
  const parsed = parsedCommandQuery();
401
+ const opening = commandOpening();
284
402
  const abort = new AbortController();
285
403
  setSearching(true);
404
+ const delay = retrySearch ? 0 : 120;
405
+ retrySearch = false;
286
406
  const timer = setTimeout(() => {
287
407
  Promise.resolve().then(() => props.searchCommands(parsed.query, abort.signal, {
288
- scope: parsed.scope?.id
408
+ scope: parsed.scope?.id,
409
+ opening
289
410
  })).then(results => {
290
411
  if (!abort.signal.aborted) {
291
412
  batch(() => {
@@ -303,7 +424,7 @@ export function ChatComposer(props) {
303
424
  }).finally(() => {
304
425
  if (!abort.signal.aborted) setSearching(false);
305
426
  });
306
- }, 120);
427
+ }, delay);
307
428
  onCleanup(() => {
308
429
  clearTimeout(timer);
309
430
  abort.abort();
@@ -362,6 +483,7 @@ export function ChatComposer(props) {
362
483
  untrack(() => {
363
484
  history.length = 0;
364
485
  historyIndex = -1;
486
+ finishModelSelection(null);
365
487
  setDismissed(true);
366
488
  setClosedAt(undefined);
367
489
  setSearchResults([]);
@@ -387,6 +509,13 @@ export function ChatComposer(props) {
387
509
  };
388
510
  const edit = (value, nextMentions = reconcileChatMentions(props.value, value, mentions())) => {
389
511
  if (commandStatus()?.tone === "error") showCommandStatus();
512
+ // Replacing `/` can leave its position and query identical, but starts a new opening.
513
+ if (inputStart !== undefined && inputEnd !== undefined && props.value.slice(inputStart, inputEnd).includes("/")) {
514
+ setCommandOpening(opening => opening + 1);
515
+ setSearchResults([]);
516
+ setSearchResultsQuery(undefined);
517
+ setCommandMoved(false);
518
+ }
390
519
  // An edit that starts at or before the closed `/` (select all, then type `/`) replaces it; the new `/` may open again.
391
520
  const closed = closedAt();
392
521
  if (closed !== undefined) {
@@ -395,6 +524,7 @@ export function ChatComposer(props) {
395
524
  if (Math.min(same, inputStart ?? same) <= closed) setClosedAt(undefined);
396
525
  }
397
526
  inputStart = undefined;
527
+ inputEnd = undefined;
398
528
  record();
399
529
  props.onValueChange(value);
400
530
  props.onMentionsChange?.(nextMentions);
@@ -438,7 +568,9 @@ export function ChatComposer(props) {
438
568
  /** The conversation field stays editable while a message is sent, so it keeps the focus and a phone its keyboard. */
439
569
  const editBlocked = () => conversation() ? busy() : blocked();
440
570
  const hasDraft = () => Boolean(props.value.trim() || !running() && attachments().length > 0);
441
- const canSubmit = () => !blocked() && hasDraft() && !(running() && runningSubmitIntent() === "steer" && mentions().length);
571
+ /** The draft may go out now, apart from a submission that is already on its way. */
572
+ const submittable = () => !busy() && hasDraft() && !(running() && runningSubmitIntent() === "steer" && mentions().length);
573
+ const canSubmit = () => !submitting() && submittable();
442
574
  const canSelectFiles = () => Boolean(props.fileSelection && !props.fileSelection.disabled && !running() && !blocked());
443
575
  const hasAddMenu = () => Boolean(props.fileSelection || props.menuActions?.length);
444
576
  const hasContextUsage = () => {
@@ -558,18 +690,26 @@ export function ChatComposer(props) {
558
690
  };
559
691
  const submit = async () => {
560
692
  if (!canSubmit()) return;
561
- const intent = running() ? runningSubmitIntent() : "send";
562
- const previousValue = props.value;
563
- const previousAttachments = attachments();
564
- const previousMentions = mentions();
565
- const input = {
566
- intent,
567
- text: previousValue,
568
- mentions: previousMentions,
569
- attachments: intent !== "steer" ? previousAttachments : []
570
- };
693
+ const key = props.draftKey;
571
694
  setSubmitting(true);
572
695
  try {
696
+ if (isTouchOnly()) {
697
+ if (commandSheetHeld()) setClosedAt(commandQuery()?.start);
698
+ // The sheet's Back entry leaves first, so a submit handler that navigates finds the history settled.
699
+ await dialogHistorySettled();
700
+ // A draft that another one replaced, or that may no longer go out, is not sent and keeps its text.
701
+ if (disposed || props.draftKey !== key || !submittable()) return;
702
+ }
703
+ const intent = running() ? runningSubmitIntent() : "send";
704
+ const previousValue = props.value;
705
+ const previousAttachments = attachments();
706
+ const previousMentions = mentions();
707
+ const input = {
708
+ intent,
709
+ text: previousValue,
710
+ mentions: previousMentions,
711
+ attachments: intent !== "steer" ? previousAttachments : []
712
+ };
573
713
  await runChatSubmission({
574
714
  clear: () => {
575
715
  props.onValueChange("");
@@ -612,6 +752,7 @@ export function ChatComposer(props) {
612
752
  };
613
753
  const executeCommand = async (command, tab = false) => {
614
754
  const token = commandQuery();
755
+ const heldHistory = commandSheetHeld();
615
756
  if (!token || blocked()) return;
616
757
  if (command.disabled || command.reason) {
617
758
  // A new object each time, so choosing the same row again inserts and announces its reason again.
@@ -620,6 +761,11 @@ export function ChatComposer(props) {
620
761
  } : undefined);
621
762
  return;
622
763
  }
764
+ if (command.searchAgain) {
765
+ retrySearch = true;
766
+ setCommandOpening(opening => opening + 1);
767
+ return;
768
+ }
623
769
  if (command.scope && (tab || !command.action && !command.mention) && narrowCommand(command)) return;
624
770
  const previous = snapshot();
625
771
  const key = props.draftKey;
@@ -655,12 +801,19 @@ export function ChatComposer(props) {
655
801
  text: command.busyLabel
656
802
  } : undefined);
657
803
  try {
804
+ // The sheet's Back entry leaves first, so an action that navigates or opens a dialog finds the history settled.
805
+ if (heldHistory && command.action) {
806
+ await dialogHistorySettled();
807
+ // Another draft or no composer any more: the command does not run against it.
808
+ if (disposed || props.draftKey !== key) return;
809
+ }
658
810
  const result = await command.action?.({
659
811
  setValue: value => edit(value),
660
812
  submit: () => {
661
813
  submitRequested = true;
662
814
  },
663
- focus
815
+ focus,
816
+ openModelSelection
664
817
  });
665
818
  if (result === "cancelled") {
666
819
  submitRequested = false;
@@ -1216,14 +1369,14 @@ export function ChatComposer(props) {
1216
1369
  const renderCommandRow = command => {
1217
1370
  const highlight = () => commandRowMap().get(command)?.highlight;
1218
1371
  const title = () => command.label ?? command.name;
1219
- return _$ssr(_tmpl$12, _$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, {
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, {
1220
1373
  get when() {
1221
1374
  return highlight();
1222
1375
  },
1223
1376
  get fallback() {
1224
1377
  return title();
1225
1378
  },
1226
- children: range => [title().slice(0, range()[0]), _$ssr(_tmpl$13, _$escape(title().slice(range()[0], range()[1]))), title().slice(range()[1])]
1379
+ children: range => [title().slice(0, range()[0]), _$ssr(_tmpl$14, _$escape(title().slice(range()[0], range()[1]))), title().slice(range()[1])]
1227
1380
  })), _$escape(_$createComponent(Show, {
1228
1381
  get when() {
1229
1382
  return command.reason ?? command.description;
@@ -1231,27 +1384,34 @@ export function ChatComposer(props) {
1231
1384
  get children() {
1232
1385
  return _$ssr(_tmpl$11, _$ssrAttribute("id", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false), _$escape(command.reason ?? command.description));
1233
1386
  }
1387
+ })), _$escape(_$createComponent(Show, {
1388
+ get when() {
1389
+ return command.selected;
1390
+ },
1391
+ get children() {
1392
+ return _$ssr(_tmpl$12, _$escape(chatMessages().commandSelected));
1393
+ }
1234
1394
  })));
1235
1395
  };
1236
- return _$ssr(_tmpl$24, _$escape(_$createComponent(Show, {
1396
+ return _$ssr(_tmpl$25, _$escape(_$createComponent(Show, {
1237
1397
  get when() {
1238
1398
  return suggestionsOpen() || hasAccessory();
1239
1399
  },
1240
1400
  get children() {
1241
- return _$ssr(_tmpl$19, _$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, {
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, {
1242
1402
  get when() {
1243
1403
  return emojiOpen();
1244
1404
  },
1245
1405
  get children() {
1246
- return _$ssr(_tmpl$14, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().emojiSuggestions, true), false), _$escape(_$createComponent(For, {
1406
+ return _$ssr(_tmpl$15, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().emojiSuggestions, true), false), _$escape(_$createComponent(For, {
1247
1407
  get each() {
1248
1408
  return emojiMatches();
1249
1409
  },
1250
- children: (entry, index) => _$ssr(_tmpl$25, `${_$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, {
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, {
1251
1411
  get when() {
1252
1412
  return entry.shortcodes[0];
1253
1413
  },
1254
- children: code => _$ssr(_tmpl$26, _$escape(code()))
1414
+ children: code => _$ssr(_tmpl$27, _$escape(code()))
1255
1415
  })))
1256
1416
  })));
1257
1417
  }
@@ -1260,7 +1420,7 @@ export function ChatComposer(props) {
1260
1420
  return commandsOpen();
1261
1421
  },
1262
1422
  get children() {
1263
- return _$ssr(_tmpl$18, _$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, {
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, {
1264
1424
  get each() {
1265
1425
  return commandModel().groups.map(commandGroupKey);
1266
1426
  },
@@ -1279,12 +1439,12 @@ export function ChatComposer(props) {
1279
1439
  });
1280
1440
  },
1281
1441
  get children() {
1282
- return _$ssr(_tmpl$29, _$ssrAttribute("aria-labelledby", _$escape(headingId(), true), false), _$ssrAttribute("id", _$escape(headingId(), true), false), _$escape(group()?.scopeTitle ?? group()?.section?.label), _$escape(_$createComponent(Show, {
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, {
1283
1443
  get when() {
1284
1444
  return group()?.loading;
1285
1445
  },
1286
1446
  get children() {
1287
- return _$ssr(_tmpl$27);
1447
+ return _$ssr(_tmpl$28);
1288
1448
  }
1289
1449
  })), _$escape(_$createComponent(For, {
1290
1450
  get each() {
@@ -1296,7 +1456,7 @@ export function ChatComposer(props) {
1296
1456
  return group()?.placeholder;
1297
1457
  },
1298
1458
  get children() {
1299
- return _$ssr(_tmpl$28);
1459
+ return _$ssr(_tmpl$29);
1300
1460
  }
1301
1461
  })));
1302
1462
  }
@@ -1307,21 +1467,21 @@ export function ChatComposer(props) {
1307
1467
  return _$memo(() => !!(searching() && commandMatches().length === 0))() && !commandModel().groups.some(group => group.section || group.scopeTitle);
1308
1468
  },
1309
1469
  get children() {
1310
- return _$ssr(_tmpl$15, _$escape(messages().loading));
1470
+ return _$ssr(_tmpl$16, _$escape(messages().loading));
1311
1471
  }
1312
1472
  })), _$escape(_$createComponent(Show, {
1313
1473
  get when() {
1314
1474
  return searchError();
1315
1475
  },
1316
1476
  get children() {
1317
- return _$ssr(_tmpl$16, _$escape(messages().error));
1477
+ return _$ssr(_tmpl$17, _$escape(messages().error));
1318
1478
  }
1319
1479
  })), _$escape(_$createComponent(Show, {
1320
1480
  get when() {
1321
1481
  return _$memo(() => !!(!searching() && !searchError()))() && commandMatches().length === 0;
1322
1482
  },
1323
1483
  get children() {
1324
- return _$ssr(_tmpl$17, !parsedCommandQuery().query.trim() ? _$escape(messages().noResults) : _$escape(chatMessages().commandsNothingFound({
1484
+ return _$ssr(_tmpl$18, !parsedCommandQuery().query.trim() ? _$escape(messages().noResults) : _$escape(chatMessages().commandsNothingFound({
1325
1485
  query: parsedCommandQuery().query.trim()
1326
1486
  })));
1327
1487
  }
@@ -1334,18 +1494,18 @@ export function ChatComposer(props) {
1334
1494
  return showHint();
1335
1495
  },
1336
1496
  get children() {
1337
- return _$ssr(_tmpl$20, _$escape(_$createComponent(Show, {
1497
+ return _$ssr(_tmpl$21, _$escape(_$createComponent(Show, {
1338
1498
  get when() {
1339
1499
  return dictationStatus();
1340
1500
  },
1341
1501
  get fallback() {
1342
- return _$ssr(_tmpl$30, _$escape(hint()));
1502
+ return _$ssr(_tmpl$31, _$escape(hint()));
1343
1503
  },
1344
- children: status => [_$ssr(_tmpl$31, _$ssrAttribute("data-state", _$escape(dictation() ?? undefined, true), false), _$ssrAttribute("class", _$escape(status().icon, true), false), _$escape(status().text)), _$createComponent(Show, {
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, {
1345
1505
  get when() {
1346
1506
  return _$memo(() => dictation() === "refined")() ? props.microphone?.onRestoreOriginal : undefined;
1347
1507
  },
1348
- children: restore => _$ssr(_tmpl$32, _$ssrAttribute("disabled", microphoneDisabled(), true), _$escape(chatMessages().restoreOriginal))
1508
+ children: restore => _$ssr(_tmpl$33, _$ssrAttribute("disabled", microphoneDisabled(), true), _$escape(chatMessages().restoreOriginal))
1349
1509
  })]
1350
1510
  })));
1351
1511
  }
@@ -1354,16 +1514,16 @@ export function ChatComposer(props) {
1354
1514
  return attachments().length > 0;
1355
1515
  },
1356
1516
  get children() {
1357
- return _$ssr(_tmpl$21, _$ssrAttribute("aria-label", _$escape(chatMessages().attachments, true), false), _$escape(_$createComponent(For, {
1517
+ return _$ssr(_tmpl$22, _$ssrAttribute("aria-label", _$escape(chatMessages().attachments, true), false), _$escape(_$createComponent(For, {
1358
1518
  get each() {
1359
1519
  return attachments();
1360
1520
  },
1361
- children: attachment => _$ssr(_tmpl$34, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
1521
+ children: attachment => _$ssr(_tmpl$35, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
1362
1522
  get when() {
1363
1523
  return attachment.href;
1364
1524
  },
1365
1525
  get fallback() {
1366
- return _$ssr(_tmpl$36, _$escape(_$createComponent(Show, {
1526
+ return _$ssr(_tmpl$37, _$escape(_$createComponent(Show, {
1367
1527
  get when() {
1368
1528
  return _$memo(() => attachment.kind === "image")() && attachment.previewUrl;
1369
1529
  },
@@ -1371,11 +1531,11 @@ export function ChatComposer(props) {
1371
1531
  return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
1372
1532
  },
1373
1533
  get children() {
1374
- return _$ssr(_tmpl$35, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1534
+ return _$ssr(_tmpl$36, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1375
1535
  }
1376
1536
  })), _$escape(renderAttachmentCopy(attachment)));
1377
1537
  },
1378
- children: href => _$ssr(_tmpl$37, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().openInNewTab({
1538
+ children: href => _$ssr(_tmpl$38, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().openInNewTab({
1379
1539
  name: attachment.name
1380
1540
  }), true), false), _$escape(_$createComponent(Show, {
1381
1541
  get when() {
@@ -1385,14 +1545,14 @@ export function ChatComposer(props) {
1385
1545
  return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
1386
1546
  },
1387
1547
  get children() {
1388
- return _$ssr(_tmpl$35, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1548
+ return _$ssr(_tmpl$36, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1389
1549
  }
1390
1550
  })), _$escape(renderAttachmentCopy(attachment)))
1391
1551
  })), _$escape(_$createComponent(Show, {
1392
1552
  get when() {
1393
1553
  return attachment.action;
1394
1554
  },
1395
- children: action => _$ssr(_tmpl$38, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
1555
+ children: action => _$ssr(_tmpl$39, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
1396
1556
  get when() {
1397
1557
  return action().icon;
1398
1558
  },
@@ -1403,7 +1563,7 @@ export function ChatComposer(props) {
1403
1563
  return props.onAttachmentsChange;
1404
1564
  },
1405
1565
  get children() {
1406
- return _$ssr(_tmpl$33, _$ssrAttribute("aria-label", _$escape(selectMessages().removeNamed({
1566
+ return _$ssr(_tmpl$34, _$ssrAttribute("aria-label", _$escape(selectMessages().removeNamed({
1407
1567
  name: attachment.name
1408
1568
  }), true), false), _$ssrAttribute("disabled", blocked(), true));
1409
1569
  }
@@ -1420,14 +1580,14 @@ export function ChatComposer(props) {
1420
1580
  return props.error;
1421
1581
  },
1422
1582
  get children() {
1423
- return _$ssr(_tmpl$22, _$escape(props.error));
1583
+ return _$ssr(_tmpl$23, _$escape(props.error));
1424
1584
  }
1425
- }), formatRow(), _$ssr(_tmpl$39, _$escape(_$createComponent(Show, {
1585
+ }), formatRow(), _$ssr(_tmpl$40, _$escape(_$createComponent(Show, {
1426
1586
  get when() {
1427
- return props.footerContent;
1587
+ return footer();
1428
1588
  },
1429
1589
  get fallback() {
1430
- return [_$ssr(_tmpl$40, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
1590
+ return [_$ssr(_tmpl$41, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
1431
1591
  get when() {
1432
1592
  return hasAddMenu();
1433
1593
  },
@@ -1459,16 +1619,28 @@ export function ChatComposer(props) {
1459
1619
  image: model.image
1460
1620
  }));
1461
1621
  },
1622
+ get open() {
1623
+ return modelOpen();
1624
+ },
1625
+ onOpenChange: open => {
1626
+ if (!choosingModel()) return;
1627
+ setModelOpen(open);
1628
+ // Dropdown closes before committing the choice; let onValueChange settle it first.
1629
+ if (!open) queueMicrotask(() => {
1630
+ if (!modelOpen()) finishModelSelection(null);
1631
+ });
1632
+ },
1462
1633
  get disabled() {
1463
- return blocked() || running() || !props.onModelChange;
1634
+ return (choosingModel() ? blockedBesidesCommand() : blocked()) || running() || !props.onModelChange;
1464
1635
  },
1465
1636
  onValueChange: modelId => {
1466
1637
  props.onModelChange?.(modelId);
1638
+ finishModelSelection(modelId);
1467
1639
  queueMicrotask(focus);
1468
1640
  }
1469
1641
  });
1470
1642
  }
1471
- })), _$escape(props.modelDetails)), _$ssr(_tmpl$41, _$escape(_$createComponent(For, {
1643
+ })), _$escape(props.modelDetails)), _$ssr(_tmpl$42, _$escape(_$createComponent(For, {
1472
1644
  get each() {
1473
1645
  return props.contextActions;
1474
1646
  },
@@ -1509,7 +1681,9 @@ export function ChatComposer(props) {
1509
1681
  }))
1510
1682
  })), _$escape(props.submitTools), _$escape(microphoneControl()), _$escape(sendControl()))];
1511
1683
  },
1512
- children: content => content()
1684
+ get children() {
1685
+ return footer();
1686
+ }
1513
1687
  })))];
1514
1688
  },
1515
1689
  get children() {
@@ -1518,9 +1692,9 @@ export function ChatComposer(props) {
1518
1692
  return props.error;
1519
1693
  },
1520
1694
  get children() {
1521
- return _$ssr(_tmpl$22, _$escape(props.error));
1695
+ return _$ssr(_tmpl$23, _$escape(props.error));
1522
1696
  }
1523
- }), formatRow(), _$ssr(_tmpl$23, _$escape(attachControl()), _$escape(fileInput()), _$escape(field()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(microphoneControl()), _$escape(sendControl()))];
1697
+ }), formatRow(), _$ssr(_tmpl$24, _$escape(attachControl()), _$escape(fileInput()), _$escape(field()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(microphoneControl()), _$escape(sendControl()))];
1524
1698
  }
1525
1699
  })), _$escape(_$createComponent(Show, {
1526
1700
  get when() {
@@ -1546,18 +1720,18 @@ export function ChatComposer(props) {
1546
1720
  return suggestionAnnouncement();
1547
1721
  },
1548
1722
  keyed: true,
1549
- children: announced => _$ssr(_tmpl$42, _$escape(announced.text))
1723
+ children: announced => _$ssr(_tmpl$43, _$escape(announced.text))
1550
1724
  })), _$escape(_$createComponent(Show, {
1551
1725
  get when() {
1552
1726
  return _$memo(() => !!!suggestionsOpen())() && commandStatus();
1553
1727
  },
1554
- children: status => _$ssr(_tmpl$43, _$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))
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))
1555
1729
  })), _$escape(_$createComponent(Show, {
1556
1730
  get when() {
1557
1731
  return _$memo(() => !!suggestionsOpen())() && announcement();
1558
1732
  },
1559
1733
  keyed: true,
1560
- children: announced => _$ssr(_tmpl$44, _$escape(announced.text))
1734
+ children: announced => _$ssr(_tmpl$45, _$escape(announced.text))
1561
1735
  })));
1562
1736
  }
1563
1737
  //# sourceMappingURL=ChatComposer.js.map