@k2b/ui 0.18.0 → 0.20.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 (204) hide show
  1. package/README.md +3 -2
  2. package/dist/browser/actions/ContextMenu.js +3 -1
  3. package/dist/browser/actions/ContextMenu.js.map +1 -1
  4. package/dist/browser/actions/Dropdown.js +91 -46
  5. package/dist/browser/actions/Dropdown.js.map +1 -1
  6. package/dist/browser/actions/FilterChip.js +4 -1
  7. package/dist/browser/actions/FilterChip.js.map +1 -1
  8. package/dist/browser/actions/GestureMenu.js +467 -0
  9. package/dist/browser/actions/GestureMenu.js.map +1 -0
  10. package/dist/browser/actions/index.js +1 -0
  11. package/dist/browser/actions/index.js.map +1 -1
  12. package/dist/browser/chat/ChatComposer.js +1255 -534
  13. package/dist/browser/chat/ChatComposer.js.map +1 -1
  14. package/dist/browser/chat/ChatPrimitives.js +9 -3
  15. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  16. package/dist/browser/chat/ChatTimeline.js +19 -1
  17. package/dist/browser/chat/ChatTimeline.js.map +1 -1
  18. package/dist/browser/chat/chat-behavior.js +9 -0
  19. package/dist/browser/chat/chat-behavior.js.map +1 -1
  20. package/dist/browser/chat/index.js.map +1 -1
  21. package/dist/browser/chat/types.js.map +1 -1
  22. package/dist/browser/content/Calendar.js +579 -533
  23. package/dist/browser/content/Calendar.js.map +1 -1
  24. package/dist/browser/content/Lightbox.js +76 -40
  25. package/dist/browser/content/Lightbox.js.map +1 -1
  26. package/dist/browser/content/MessageRow.js +2 -2
  27. package/dist/browser/content/MessageRow.js.map +1 -1
  28. package/dist/browser/content/PdfPreview.js +146 -79
  29. package/dist/browser/content/PdfPreview.js.map +1 -1
  30. package/dist/browser/content/Timeline.js +1038 -0
  31. package/dist/browser/content/Timeline.js.map +1 -0
  32. package/dist/browser/content/VideoPlayer.js +29 -9
  33. package/dist/browser/content/VideoPlayer.js.map +1 -1
  34. package/dist/browser/content/chart-inspection.js +7 -2
  35. package/dist/browser/content/chart-inspection.js.map +1 -1
  36. package/dist/browser/content/index.js +1 -0
  37. package/dist/browser/content/index.js.map +1 -1
  38. package/dist/browser/content/timeline-model.js +579 -0
  39. package/dist/browser/content/timeline-model.js.map +1 -0
  40. package/dist/browser/feedback/dialog-core.js +26 -14
  41. package/dist/browser/feedback/dialog-core.js.map +1 -1
  42. package/dist/browser/inputs/ChoiceChips.js +66 -0
  43. package/dist/browser/inputs/ChoiceChips.js.map +1 -0
  44. package/dist/browser/inputs/ChoiceGroups.js +53 -12
  45. package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
  46. package/dist/browser/inputs/FileInputs.js +48 -33
  47. package/dist/browser/inputs/FileInputs.js.map +1 -1
  48. package/dist/browser/inputs/SignatureInput.js +558 -0
  49. package/dist/browser/inputs/SignatureInput.js.map +1 -0
  50. package/dist/browser/inputs/TextInput.js +13 -1
  51. package/dist/browser/inputs/TextInput.js.map +1 -1
  52. package/dist/browser/inputs/emoji/EmojiPicker.js +841 -0
  53. package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -0
  54. package/dist/browser/inputs/emoji/emoji-data.js +1903 -0
  55. package/dist/browser/inputs/emoji/emoji-data.js.map +1 -0
  56. package/dist/browser/inputs/emoji/emoji-index.js +209 -0
  57. package/dist/browser/inputs/emoji/emoji-index.js.map +1 -0
  58. package/dist/browser/inputs/emoji/emoji-messages.js +64 -0
  59. package/dist/browser/inputs/emoji/emoji-messages.js.map +1 -0
  60. package/dist/browser/inputs/index.js +5 -0
  61. package/dist/browser/inputs/index.js.map +1 -1
  62. package/dist/browser/inputs/markdown/actions.js +6 -2
  63. package/dist/browser/inputs/markdown/actions.js.map +1 -1
  64. package/dist/browser/inputs/markdown/code-zone.js +38 -2
  65. package/dist/browser/inputs/markdown/code-zone.js.map +1 -1
  66. package/dist/browser/inputs/signature-messages.js +39 -0
  67. package/dist/browser/inputs/signature-messages.js.map +1 -0
  68. package/dist/browser/inputs/signature.js +208 -0
  69. package/dist/browser/inputs/signature.js.map +1 -0
  70. package/dist/browser/internal/render-error.js +49 -0
  71. package/dist/browser/internal/render-error.js.map +1 -0
  72. package/dist/browser/intl/messages.js +102 -0
  73. package/dist/browser/intl/messages.js.map +1 -1
  74. package/dist/browser/layout/AppWorkspace.js +321 -222
  75. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  76. package/dist/browser/layout/FloatingWindow.js +4 -1
  77. package/dist/browser/layout/FloatingWindow.js.map +1 -1
  78. package/dist/browser/layout/SidebarItemPreview.js +5 -1
  79. package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
  80. package/dist/browser/layout/index.js.map +1 -1
  81. package/dist/browser/surfaces/ResourceCard.js +212 -0
  82. package/dist/browser/surfaces/ResourceCard.js.map +1 -0
  83. package/dist/browser/surfaces/index.js +1 -0
  84. package/dist/browser/surfaces/index.js.map +1 -1
  85. package/dist/licenses/emojibase-data.txt +21 -0
  86. package/dist/ssr/actions/ContextMenu.js +3 -1
  87. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  88. package/dist/ssr/actions/Dropdown.js +49 -12
  89. package/dist/ssr/actions/Dropdown.js.map +1 -1
  90. package/dist/ssr/actions/FilterChip.js +4 -1
  91. package/dist/ssr/actions/FilterChip.js.map +1 -1
  92. package/dist/ssr/actions/GestureMenu.js +411 -0
  93. package/dist/ssr/actions/GestureMenu.js.map +1 -0
  94. package/dist/ssr/actions/index.js +1 -0
  95. package/dist/ssr/actions/index.js.map +1 -1
  96. package/dist/ssr/chat/ChatComposer.js +767 -214
  97. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  98. package/dist/ssr/chat/ChatPrimitives.js +9 -3
  99. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  100. package/dist/ssr/chat/ChatTimeline.js +19 -1
  101. package/dist/ssr/chat/ChatTimeline.js.map +1 -1
  102. package/dist/ssr/chat/chat-behavior.js +9 -0
  103. package/dist/ssr/chat/chat-behavior.js.map +1 -1
  104. package/dist/ssr/chat/index.js.map +1 -1
  105. package/dist/ssr/chat/types.js.map +1 -1
  106. package/dist/ssr/content/Calendar.js +183 -142
  107. package/dist/ssr/content/Calendar.js.map +1 -1
  108. package/dist/ssr/content/Lightbox.js +19 -5
  109. package/dist/ssr/content/Lightbox.js.map +1 -1
  110. package/dist/ssr/content/MessageRow.js +2 -2
  111. package/dist/ssr/content/MessageRow.js.map +1 -1
  112. package/dist/ssr/content/PdfPreview.js +77 -36
  113. package/dist/ssr/content/PdfPreview.js.map +1 -1
  114. package/dist/ssr/content/Timeline.js +804 -0
  115. package/dist/ssr/content/Timeline.js.map +1 -0
  116. package/dist/ssr/content/VideoPlayer.js +29 -9
  117. package/dist/ssr/content/VideoPlayer.js.map +1 -1
  118. package/dist/ssr/content/chart-inspection.js +7 -2
  119. package/dist/ssr/content/chart-inspection.js.map +1 -1
  120. package/dist/ssr/content/index.js +1 -0
  121. package/dist/ssr/content/index.js.map +1 -1
  122. package/dist/ssr/content/timeline-model.js +579 -0
  123. package/dist/ssr/content/timeline-model.js.map +1 -0
  124. package/dist/ssr/feedback/dialog-core.js +26 -14
  125. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  126. package/dist/ssr/inputs/ChoiceChips.js +66 -0
  127. package/dist/ssr/inputs/ChoiceChips.js.map +1 -0
  128. package/dist/ssr/inputs/ChoiceGroups.js +12 -4
  129. package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
  130. package/dist/ssr/inputs/FileInputs.js +19 -4
  131. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  132. package/dist/ssr/inputs/SignatureInput.js +404 -0
  133. package/dist/ssr/inputs/SignatureInput.js.map +1 -0
  134. package/dist/ssr/inputs/TextInput.js +13 -1
  135. package/dist/ssr/inputs/TextInput.js.map +1 -1
  136. package/dist/ssr/inputs/emoji/EmojiPicker.js +551 -0
  137. package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -0
  138. package/dist/ssr/inputs/emoji/emoji-data.js +1903 -0
  139. package/dist/ssr/inputs/emoji/emoji-data.js.map +1 -0
  140. package/dist/ssr/inputs/emoji/emoji-index.js +209 -0
  141. package/dist/ssr/inputs/emoji/emoji-index.js.map +1 -0
  142. package/dist/ssr/inputs/emoji/emoji-messages.js +64 -0
  143. package/dist/ssr/inputs/emoji/emoji-messages.js.map +1 -0
  144. package/dist/ssr/inputs/index.js +5 -0
  145. package/dist/ssr/inputs/index.js.map +1 -1
  146. package/dist/ssr/inputs/markdown/actions.js +6 -2
  147. package/dist/ssr/inputs/markdown/actions.js.map +1 -1
  148. package/dist/ssr/inputs/markdown/code-zone.js +38 -2
  149. package/dist/ssr/inputs/markdown/code-zone.js.map +1 -1
  150. package/dist/ssr/inputs/signature-messages.js +39 -0
  151. package/dist/ssr/inputs/signature-messages.js.map +1 -0
  152. package/dist/ssr/inputs/signature.js +208 -0
  153. package/dist/ssr/inputs/signature.js.map +1 -0
  154. package/dist/ssr/internal/render-error.js +49 -0
  155. package/dist/ssr/internal/render-error.js.map +1 -0
  156. package/dist/ssr/intl/messages.js +102 -0
  157. package/dist/ssr/intl/messages.js.map +1 -1
  158. package/dist/ssr/layout/AppWorkspace.js +112 -41
  159. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  160. package/dist/ssr/layout/FloatingWindow.js +4 -1
  161. package/dist/ssr/layout/FloatingWindow.js.map +1 -1
  162. package/dist/ssr/layout/SidebarItemPreview.js +5 -1
  163. package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
  164. package/dist/ssr/layout/index.js.map +1 -1
  165. package/dist/ssr/surfaces/ResourceCard.js +115 -0
  166. package/dist/ssr/surfaces/ResourceCard.js.map +1 -0
  167. package/dist/ssr/surfaces/index.js +1 -0
  168. package/dist/ssr/surfaces/index.js.map +1 -1
  169. package/dist/styles.css +1 -1
  170. package/dist/types/actions/Dropdown.d.ts +13 -1
  171. package/dist/types/actions/FilterChip.d.ts +5 -0
  172. package/dist/types/actions/GestureMenu.d.ts +35 -0
  173. package/dist/types/actions/index.d.ts +2 -0
  174. package/dist/types/chat/ChatComposer.d.ts +27 -1
  175. package/dist/types/chat/chat-behavior.d.ts +15 -0
  176. package/dist/types/chat/index.d.ts +1 -1
  177. package/dist/types/chat/types.d.ts +34 -0
  178. package/dist/types/content/Calendar.d.ts +28 -8
  179. package/dist/types/content/Lightbox.d.ts +8 -0
  180. package/dist/types/content/Timeline.d.ts +49 -0
  181. package/dist/types/content/index.d.ts +4 -2
  182. package/dist/types/content/timeline-model.d.ts +148 -0
  183. package/dist/types/feedback/dialog-core.d.ts +1 -1
  184. package/dist/types/inputs/ChoiceChips.d.ts +20 -0
  185. package/dist/types/inputs/ChoiceGroups.d.ts +11 -2
  186. package/dist/types/inputs/FileInputs.d.ts +6 -0
  187. package/dist/types/inputs/SignatureInput.d.ts +22 -0
  188. package/dist/types/inputs/emoji/EmojiPicker.d.ts +42 -0
  189. package/dist/types/inputs/emoji/emoji-data.d.ts +2 -0
  190. package/dist/types/inputs/emoji/emoji-index.d.ts +109 -0
  191. package/dist/types/inputs/emoji/emoji-messages.d.ts +30 -0
  192. package/dist/types/inputs/index.d.ts +10 -0
  193. package/dist/types/inputs/markdown/actions.d.ts +2 -0
  194. package/dist/types/inputs/markdown/code-zone.d.ts +7 -0
  195. package/dist/types/inputs/signature-messages.d.ts +11 -0
  196. package/dist/types/inputs/signature.d.ts +72 -0
  197. package/dist/types/internal/render-error.d.ts +13 -0
  198. package/dist/types/intl/messages.d.ts +51 -0
  199. package/dist/types/layout/AppWorkspace.d.ts +17 -0
  200. package/dist/types/layout/SidebarItemPreview.d.ts +2 -0
  201. package/dist/types/layout/index.d.ts +1 -1
  202. package/dist/types/surfaces/ResourceCard.d.ts +31 -0
  203. package/dist/types/surfaces/index.d.ts +2 -0
  204. package/package.json +6 -5
@@ -1,58 +1,120 @@
1
1
  import { mergeProps as _$mergeProps } from "solid-js/web";
2
- import { createComponent as _$createComponent } from "solid-js/web";
3
2
  import { ssrStyleProperty as _$ssrStyleProperty } from "solid-js/web";
4
3
  import { memo as _$memo } from "solid-js/web";
4
+ import { createComponent as _$createComponent } from "solid-js/web";
5
5
  import { ssr as _$ssr } from "solid-js/web";
6
6
  import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
7
7
  import { escape as _$escape } from "solid-js/web";
8
8
  var _tmpl$ = ["<span><strong", ">", "</strong></span>"],
9
- _tmpl$2 = ["<div role=\"status\" class=\"k2b-chat-composer__loading\"><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i><strong>", "</strong></div>"],
10
- _tmpl$3 = ["<div role=\"status\"><i class=\"ti ti-alert-circle\"></i> ", "</div>"],
11
- _tmpl$4 = ["<div role=\"status\">", "</div>"],
12
- _tmpl$5 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "", "", "", "</div></div>"],
13
- _tmpl$6 = ["<div class=\"k2b-chat-composer-slot\"><div style=\"", "\"", ">", "</div>", "</div>"],
14
- _tmpl$7 = ["<div class=\"k2b-chat-composer__attachments\" role=\"list\"", " tabindex=\"0\">", "</div>"],
15
- _tmpl$8 = ["<div class=\"k2b-chat-composer__highlight\" aria-hidden=\"true\">", "\n</div>"],
16
- _tmpl$9 = ["<div class=\"k2b-chat-composer__error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</div>"],
17
- _tmpl$0 = ["<div class=\"k2b-chat-composer-shell\">", "<section class=\"", "\"", " role=\"group\"", ">", "<div class=\"k2b-chat-composer__input\" role=\"group\"", ">", "<textarea class=\"", "\" rows=\"1\"", "", "></textarea></div>", "<footer class=\"k2b-chat-composer__footer\">", "</footer>", "</section></div>"],
18
- _tmpl$1 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><i", " aria-hidden=\"true\"></i><strong>", "</strong><small>", "</small></button>"],
19
- _tmpl$10 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-remove\"", "", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
20
- _tmpl$11 = ["<div class=\"k2b-chat-composer__attachment\" role=\"listitem\"><div class=\"", "\">", "", "</div>", "</div>"],
21
- _tmpl$12 = ["<img", ">"],
22
- _tmpl$13 = ["<div class=\"k2b-chat-composer__attachment-identity\">", "", "</div>"],
23
- _tmpl$14 = ["<i", " aria-hidden=\"true\"></i>"],
24
- _tmpl$15 = ["<a class=\"k2b-chat-composer__attachment-link\"", " target=\"_blank\" rel=\"noopener noreferrer\"", ">", "", "</a>"],
25
- _tmpl$16 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-action\"", ">", "", "</button>"],
26
- _tmpl$17 = ["<span class=\"", "\">", "</span>"],
27
- _tmpl$18 = "<i class=\"ti ti-plus\" aria-hidden=\"true\"></i>",
28
- _tmpl$19 = ["<input class=\"k2b-sr-only\" type=\"file\" tabindex=\"-1\" aria-hidden=\"true\"", "", ">"],
29
- _tmpl$20 = ["<div class=\"k2b-chat-composer__tools\">", "", "", "", "", "</div>"],
30
- _tmpl$21 = ["<div class=\"k2b-chat-composer__submit\">", "", "", "", "</div>"];
9
+ _tmpl$2 = ["<input class=\"k2b-sr-only\" type=\"file\" tabindex=\"-1\" aria-hidden=\"true\"", "", ">"],
10
+ _tmpl$3 = ["<i", " aria-hidden=\"true\"></i>"],
11
+ _tmpl$4 = "<i class=\"ti ti-letter-case\" aria-hidden=\"true\"></i>",
12
+ _tmpl$5 = ["<div", " class=\"k2b-chat-composer__format\" role=\"group\"", ">", "</div>"],
13
+ _tmpl$6 = "<i class=\"ti ti-mood-smile\" aria-hidden=\"true\"></i>",
14
+ _tmpl$7 = "<i class=\"ti ti-chevron-up\" aria-hidden=\"true\"></i>",
15
+ _tmpl$8 = ["<div class=\"k2b-chat-composer__microphone\">", "", "</div>"],
16
+ _tmpl$9 = ["<div class=\"k2b-chat-composer__highlight\" aria-hidden=\"true\">", "\n</div>"],
17
+ _tmpl$0 = "<textarea class=\"k2b-chat-composer__measure\" rows=\"1\" tabindex=\"-1\" aria-hidden=\"true\" readonly></textarea>",
18
+ _tmpl$1 = ["<div class=\"k2b-chat-composer__input\" role=\"group\"", ">", "<textarea class=\"", "\" rows=\"1\"", "", "></textarea>", "</div>"],
19
+ _tmpl$10 = ["<span class=\"", "\">", "</span>"],
20
+ _tmpl$11 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "</div></div>"],
21
+ _tmpl$12 = ["<div role=\"status\" class=\"k2b-chat-composer__loading\"><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i><strong>", "</strong></div>"],
22
+ _tmpl$13 = ["<div role=\"status\"><i class=\"ti ti-alert-circle\"></i> ", "</div>"],
23
+ _tmpl$14 = ["<div role=\"status\">", "</div>"],
24
+ _tmpl$15 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "", "", "", "</div></div>"],
25
+ _tmpl$16 = ["<div class=\"k2b-chat-composer-slot\"", "><div style=\"", "\"", ">", "</div>", "", "</div>"],
26
+ _tmpl$17 = ["<div class=\"k2b-chat-composer__hint\" aria-live=\"polite\">", "</div>"],
27
+ _tmpl$18 = ["<div class=\"k2b-chat-composer__attachments\" role=\"list\"", " tabindex=\"0\">", "</div>"],
28
+ _tmpl$19 = ["<div class=\"k2b-chat-composer__error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</div>"],
29
+ _tmpl$20 = ["<div class=\"k2b-chat-composer__row\">", "", "<div class=\"k2b-chat-composer__field\">", "<div class=\"k2b-chat-composer__field-tools\">", "", "", "</div></div>", "</div>"],
30
+ _tmpl$21 = ["<div class=\"k2b-chat-composer-shell\">", "<section class=\"", "\"", " role=\"group\"", ">", "", "", "", "</section></div>"],
31
+ _tmpl$22 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><span class=\"k2b-chat-composer__emoji-glyph\" aria-hidden=\"true\">", "</span><strong>", "</strong>", "</button>"],
32
+ _tmpl$23 = ["<small>:", ":</small>"],
33
+ _tmpl$24 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><i", " aria-hidden=\"true\"></i><strong>", "</strong><small>", "</small></button>"],
34
+ _tmpl$25 = ["<span class=\"k2b-chat-composer__hint-text\">", "</span>"],
35
+ _tmpl$26 = ["<span class=\"k2b-chat-composer__dictation\"", "><i", " aria-hidden=\"true\"></i><span>", "</span></span>"],
36
+ _tmpl$27 = ["<button type=\"button\" class=\"k2b-chat-composer__hint-action\"", ">", "</button>"],
37
+ _tmpl$28 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-remove\"", "", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
38
+ _tmpl$29 = ["<div class=\"k2b-chat-composer__attachment\" role=\"listitem\"><div class=\"", "\">", "", "</div>", "</div>"],
39
+ _tmpl$30 = ["<img", ">"],
40
+ _tmpl$31 = ["<div class=\"k2b-chat-composer__attachment-identity\">", "", "</div>"],
41
+ _tmpl$32 = ["<a class=\"k2b-chat-composer__attachment-link\"", " target=\"_blank\" rel=\"noopener noreferrer\"", ">", "", "</a>"],
42
+ _tmpl$33 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-action\"", ">", "", "</button>"],
43
+ _tmpl$34 = ["<footer class=\"k2b-chat-composer__footer\">", "</footer>"],
44
+ _tmpl$35 = ["<div class=\"k2b-chat-composer__tools\">", "", "", "", "", "", "", "</div>"],
45
+ _tmpl$36 = ["<div class=\"k2b-chat-composer__submit\">", "", "", "", "", "</div>"];
31
46
  import { children, createEffect, createMemo, createSignal, createUniqueId, For, onCleanup, onMount, Show, untrack } from "solid-js";
32
47
  import { Dropdown } from "../actions/Dropdown";
33
48
  import { Tooltip } from "../feedback/Tooltip";
49
+ import { replaceTextareaRange } from "../inputs/editor-dom";
50
+ import { completedEmojiShortcode, emojiInTone, emojiName, emojiShortcodeQuery, loadEmojiIndex, suggestEmoji } from "../inputs/emoji/emoji-index";
34
51
  import { FileDropTarget } from "../inputs/FileDropTarget";
52
+ import { toggleBulletList, toggleInlineMarker } from "../inputs/markdown/actions";
53
+ import { openCodeFence } from "../inputs/markdown/code-zone";
35
54
  import { SelectChip } from "../inputs/SelectChip";
55
+ import { useLocale } from "../intl/locale";
36
56
  import { useUiMessages } from "../intl/messages";
37
57
  import { ChatContextUsage as ContextUsage } from "./ChatPrimitives";
38
- import { executeChatAction, nextChatCommandIndex, reportChatFailure, runChatSubmission } from "./chat-behavior";
58
+ import { conversationEnterAction, executeChatAction, nextChatCommandIndex, reportChatFailure, runChatSubmission } from "./chat-behavior";
39
59
  import { chatCommandQuery, chatMentionSegments, reconcileChatMentions } from "./composer-document";
40
60
  const composerMaxInputHeight = 309;
61
+ /** Emoji suggestions for a `:shortcode`, as many as fit the list without scrolling on a phone. */
62
+ const emojiSuggestionLimit = 8;
41
63
  // Keep editor undo text within approximately 2 MiB, plus the current edit.
42
64
  const composerUndoBudgetChars = 1_000_000;
65
+ /** The row stays short: what a chat message needs. Other Markdown, typed by hand, keeps working. */
66
+ const formatTools = [{
67
+ id: "bold",
68
+ icon: "ti ti-bold",
69
+ label: m => m.boldShortcut,
70
+ run: textarea => toggleInlineMarker(textarea, "**"),
71
+ key: "b"
72
+ }, {
73
+ id: "italic",
74
+ icon: "ti ti-italic",
75
+ label: m => m.italicShortcut,
76
+ run: textarea => toggleInlineMarker(textarea, "*"),
77
+ key: "i"
78
+ }, {
79
+ id: "bullet-list",
80
+ icon: "ti ti-list",
81
+ label: m => m.bulletListShortcut,
82
+ run: toggleBulletList,
83
+ shiftCode: "Digit8"
84
+ }];
85
+ const formatShortcut = event => {
86
+ if (event.altKey || !(event.ctrlKey || event.metaKey)) return undefined;
87
+ return formatTools.find(tool => event.shiftKey ? tool.shiftCode === event.code : tool.key === event.key.toLowerCase());
88
+ };
89
+ const touchOnlyQuery = "(any-pointer: coarse) and (not (any-pointer: fine))";
90
+ const isTouchOnly = () => typeof matchMedia === "function" && matchMedia(touchOnlyQuery).matches;
91
+ /** How long a press on the microphone lasts before it asks for a voice message instead of dictation. */
92
+ const microphoneHoldMs = 500;
93
+ /** Keeps the field focused (and a phone's keyboard open) when a composer button is pressed with a mouse or finger. */
94
+ const keepFieldFocus = event => event.preventDefault();
43
95
  const attachmentIcon = attachment => attachment.icon ?? (attachment.kind === "image" ? "ti ti-photo" : attachment.kind === "resource" ? "ti ti-link" : "ti ti-file");
96
+ const hasContent = items => items.some(item => item != null && typeof item !== "boolean" && item !== "");
44
97
  export function ChatComposer(props) {
45
98
  const accessory = children(() => props.accessory);
46
- const hasAccessory = () => accessory.toArray().some(item => item != null && typeof item !== "boolean" && item !== "");
99
+ const hasAccessory = () => hasContent(accessory.toArray());
100
+ const hint = children(() => props.hint);
47
101
  const messages = useUiMessages();
48
102
  const commandListId = `k2b-chat-commands-${createUniqueId().replace(/[^A-Za-z0-9_-]/g, "-")}`;
103
+ const formatGroupId = `${commandListId}-format`;
49
104
  const [selectedCommandIndex, setSelectedCommandIndex] = createSignal(0);
50
105
  const [addingFiles, setAddingFiles] = createSignal(false);
51
106
  const [submitting, setSubmitting] = createSignal(false);
52
107
  let composerRef;
53
108
  let textareaRef;
54
109
  let fileInputRef;
110
+ let measureRef;
111
+ let microphoneRef;
55
112
  let sawRunning = props.state === "running";
113
+ const conversation = () => props.variant === "conversation";
114
+ const [formattingOpen, setFormattingOpen] = createSignal(false);
115
+ const [microphoneMenuOpen, setMicrophoneMenuOpen] = createSignal(false);
116
+ const dictation = () => props.microphone?.dictation ?? null;
117
+ const showHint = () => conversation() || dictation() !== null || hasContent(hint.toArray());
56
118
  const state = () => props.state ?? "idle";
57
119
  const running = () => state() === "running";
58
120
  const stopping = () => state() === "stopping";
@@ -69,15 +131,29 @@ export function ChatComposer(props) {
69
131
  let highlightRef;
70
132
  let commandListRef;
71
133
  const mentions = () => props.mentions ?? [];
134
+ const locale = useLocale();
135
+ const [emojiIndex, setEmojiIndex] = createSignal();
136
+ const emojiQuery = createMemo(() => !props.emoji || dismissed() || composing() ? null : emojiShortcodeQuery(props.value, caret(), selectionEnd()));
137
+ // The emoji data loads with the first `:shortcode`; a failed load only means no suggestions.
138
+ createEffect(() => {
139
+ if (emojiQuery() && !emojiIndex()) loadEmojiIndex().then(setEmojiIndex, () => undefined);
140
+ });
141
+ const emojiMatches = createMemo(() => {
142
+ const token = emojiQuery();
143
+ const index = emojiIndex();
144
+ return token && index ? suggestEmoji(index, token.query, emojiSuggestionLimit) : [];
145
+ });
146
+ const emojiOpen = () => emojiMatches().length > 0;
72
147
  const commandQuery = createMemo(() => dismissed() || composing() ? null : chatCommandQuery(props.value, caret(), selectionEnd()));
73
148
  const commandMatches = createMemo(() => {
74
149
  const token = commandQuery();
75
150
  if (!token) return [];
76
151
  return [...(props.commands ?? []).filter(command => command.name.toLowerCase().includes(token.query.toLowerCase())), ...searchResults()];
77
152
  });
78
- const commandsOpen = () => Boolean(commandQuery() && (props.commands?.length || props.searchCommands));
153
+ const commandsOpen = () => Boolean(!emojiOpen() && commandQuery() && (props.commands?.length || props.searchCommands));
154
+ const suggestionsOpen = () => commandsOpen() || emojiOpen();
79
155
  createEffect(() => {
80
- if (!commandsOpen()) return;
156
+ if (!suggestionsOpen()) return;
81
157
  let disposed = false;
82
158
  const measure = () => {
83
159
  if (disposed || !commandListRef) return;
@@ -174,7 +250,10 @@ export function ChatComposer(props) {
174
250
  setSelectionEnd(textareaRef?.selectionEnd ?? props.value.length);
175
251
  };
176
252
  const selectedCommand = () => commandMatches()[selectedCommandIndex()];
177
- const blocked = () => Boolean(props.disabled || stopping() || state() === "submitting" || executing() || addingFiles() || submitting());
253
+ const busy = () => Boolean(props.disabled || stopping() || state() === "submitting" || executing() || addingFiles());
254
+ const blocked = () => busy() || submitting();
255
+ /** The conversation field stays editable while a message is sent, so it keeps the focus and a phone its keyboard. */
256
+ const editBlocked = () => conversation() ? busy() : blocked();
178
257
  const hasDraft = () => Boolean(props.value.trim() || !running() && attachments().length > 0);
179
258
  const canSubmit = () => !blocked() && hasDraft() && !(running() && runningSubmitIntent() === "steer" && mentions().length);
180
259
  const canSelectFiles = () => Boolean(props.fileSelection && !props.fileSelection.disabled && !running() && !blocked());
@@ -194,7 +273,7 @@ export function ChatComposer(props) {
194
273
  icon: addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-paperclip",
195
274
  label: props.fileSelection.label ?? messages().attachFiles,
196
275
  disabled: !canSelectFiles(),
197
- action: () => fileInputRef?.click()
276
+ action: () => chooseFiles()
198
277
  });
199
278
  }
200
279
  for (const action of props.menuActions ?? []) {
@@ -210,6 +289,14 @@ export function ChatComposer(props) {
210
289
  };
211
290
  const autoResize = () => {
212
291
  if (!textareaRef) return;
292
+ if (conversation()) {
293
+ // A hidden copy measures the text. Collapsing the field itself would shrink the composer for a frame, and
294
+ // WebKit then clamps the scroll position of the conversation above it. CSS caps the height.
295
+ if (!measureRef) return;
296
+ measureRef.value = textareaRef.value;
297
+ textareaRef.style.height = `${measureRef.scrollHeight}px`;
298
+ return;
299
+ }
213
300
  textareaRef.style.height = "auto";
214
301
  textareaRef.style.height = `${Math.min(textareaRef.scrollHeight, composerMaxInputHeight)}px`;
215
302
  };
@@ -217,14 +304,22 @@ export function ChatComposer(props) {
217
304
  onMount(() => {
218
305
  autoResize();
219
306
  if (props.focusToken !== undefined) focus();
307
+ // The copy follows the field's width, so a narrower composer measures more lines.
308
+ if (measureRef && typeof ResizeObserver === "function") {
309
+ const observer = new ResizeObserver(() => autoResize());
310
+ observer.observe(measureRef);
311
+ onCleanup(() => observer.disconnect());
312
+ }
220
313
  });
221
314
  createEffect(() => {
222
315
  commandMatches();
316
+ emojiMatches();
223
317
  setSelectedCommandIndex(0);
224
318
  });
225
319
  createEffect(() => {
226
320
  const index = selectedCommandIndex();
227
321
  commandMatches();
322
+ emojiMatches();
228
323
  queueMicrotask(() => {
229
324
  const list = commandListRef;
230
325
  const item = list?.querySelector(`[id="${commandListId}-${index}"]`);
@@ -254,6 +349,13 @@ export function ChatComposer(props) {
254
349
  });
255
350
  const setAttachments = next => props.onAttachmentsChange?.(next);
256
351
  const renderAttachmentCopy = attachment => _$ssr(_tmpl$, _$ssrAttribute("title", _$escape(attachment.name, true), false), _$escape(attachment.name));
352
+ const chooseFiles = () => {
353
+ const selection = props.fileSelection;
354
+ if (!selection?.choose) return fileInputRef?.click();
355
+ selection.choose().then(
356
+ // A single-file composer keeps the first chosen file, as its file dialog and drops do.
357
+ files => void runFiles(selection.multiple === false ? files.slice(0, 1) : files), error => (props.fileSelection?.onError ?? props.onError)?.(error));
358
+ };
257
359
  const runFiles = async files => {
258
360
  if (!canSelectFiles()) return;
259
361
  const selected = Array.from(files);
@@ -291,8 +393,17 @@ export function ChatComposer(props) {
291
393
  },
292
394
  perform: () => props.onSubmit(input),
293
395
  restore: () => {
294
- props.onValueChange(previousValue);
295
- props.onMentionsChange?.(previousMentions);
396
+ // Text typed while the submission ran stays, after the returned draft. Its mentions are read first, because
397
+ // an application may already move them when the text changes.
398
+ const typed = props.value;
399
+ const typedMentions = mentions();
400
+ const shift = previousValue.length + 1;
401
+ props.onValueChange(typed ? `${previousValue}\n${typed}` : previousValue);
402
+ props.onMentionsChange?.(typed ? [...previousMentions, ...typedMentions.map(mention => ({
403
+ ...mention,
404
+ start: mention.start + shift,
405
+ end: mention.end + shift
406
+ }))] : previousMentions);
296
407
  if (intent !== "steer") setAttachments(previousAttachments);
297
408
  },
298
409
  onError: props.onError
@@ -349,13 +460,74 @@ export function ChatComposer(props) {
349
460
  focus();
350
461
  });
351
462
  };
463
+
464
+ /** Replaces `start`–`end` with the emoji through the editing path, so the browser's undo restores the shortcode. */
465
+ const insertEmoji = (start, end, emoji) => {
466
+ if (!textareaRef || editBlocked()) return;
467
+ replaceTextareaRange(textareaRef, start, end, emoji);
468
+ syncCaret();
469
+ reportChatFailure(() => props.emoji?.onPick?.(emoji), props.onError);
470
+ };
471
+ const pickEmojiSuggestion = position => {
472
+ const token = emojiQuery();
473
+ const entry = emojiMatches()[position];
474
+ if (!token || !entry) return;
475
+ insertEmoji(token.start, token.end, emojiInTone(entry, props.emoji?.skinTone ?? 0));
476
+ };
477
+ /**
478
+ * A typed colon loads the emoji data, and the closing colon of a known `:shortcode:` turns it into its emoji. A
479
+ * shortcode closed before the data arrived turns when it arrives, as long as nothing changed in the meantime.
480
+ */
481
+ const completeShortcode = () => {
482
+ const textarea = textareaRef;
483
+ if (!props.emoji || !textarea) return;
484
+ const {
485
+ value,
486
+ selectionStart: caret
487
+ } = textarea;
488
+ const completed = completedEmojiShortcode(value, caret);
489
+ const convert = index => {
490
+ const entry = completed && index.byShortcode.get(completed.shortcode.toLowerCase());
491
+ if (completed && entry) insertEmoji(completed.start, completed.end, emojiInTone(entry, props.emoji?.skinTone ?? 0));
492
+ };
493
+ const index = emojiIndex();
494
+ if (index) {
495
+ convert(index);
496
+ return;
497
+ }
498
+ loadEmojiIndex().then(loaded => {
499
+ setEmojiIndex(loaded);
500
+ const untouched = textarea.value === value && textarea.selectionStart === caret && textarea.selectionEnd === caret;
501
+ if (untouched && textarea.matches(":focus")) convert(loaded);
502
+ }, () => undefined);
503
+ };
352
504
  const onKeyDown = event => {
353
- if (event.isComposing || composing()) return;
505
+ // WebKit ends the composition before the keydown of the Enter that confirms it; that keydown keeps keyCode 229.
506
+ if (event.isComposing || event.keyCode === 229 || composing()) return;
354
507
  if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "z") {
355
508
  event.preventDefault();
356
509
  restoreHistory(event.shiftKey ? 1 : -1);
357
510
  return;
358
511
  }
512
+ if (emojiOpen()) {
513
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
514
+ event.preventDefault();
515
+ setSelectedCommandIndex(index => nextChatCommandIndex(index, emojiMatches().length, event.key === "ArrowUp" ? -1 : 1));
516
+ return;
517
+ }
518
+ // Only a plain Enter or Tab picks: Shift+Enter still breaks the line, Shift+Tab still moves the focus back.
519
+ const plain = !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
520
+ if (plain && (event.key === "Enter" || event.key === "Tab")) {
521
+ event.preventDefault();
522
+ pickEmojiSuggestion(selectedCommandIndex());
523
+ return;
524
+ }
525
+ if (event.key === "Escape") {
526
+ event.preventDefault();
527
+ setDismissed(true);
528
+ return;
529
+ }
530
+ }
359
531
  const matches = commandMatches();
360
532
  if (commandsOpen()) {
361
533
  if (event.key === "ArrowUp" || event.key === "ArrowDown") {
@@ -363,7 +535,8 @@ export function ChatComposer(props) {
363
535
  setSelectedCommandIndex(index => nextChatCommandIndex(index, matches.length, event.key === "ArrowUp" ? -1 : 1));
364
536
  return;
365
537
  }
366
- if ((event.key === "Enter" || event.key === "Tab") && matches.length > 0) {
538
+ // Ctrl/⌘+Enter sends, also while suggestions are open.
539
+ if ((event.key === "Tab" || event.key === "Enter" && !event.ctrlKey && !event.metaKey) && matches.length > 0) {
367
540
  event.preventDefault();
368
541
  const command = selectedCommand();
369
542
  if (command) void executeCommand(command);
@@ -375,294 +548,674 @@ export function ChatComposer(props) {
375
548
  return;
376
549
  }
377
550
  }
378
- if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
551
+ const format = props.formatting && textareaRef ? formatShortcut(event) : undefined;
552
+ if (format && textareaRef) {
553
+ event.preventDefault();
554
+ format.run(textareaRef);
555
+ return;
556
+ }
557
+ if (event.key !== "Enter") return;
558
+ if (conversation()) {
559
+ const action = conversationEnterAction(event, {
560
+ sendKey: props.sendKey ?? "enter",
561
+ touchOnly: isTouchOnly(),
562
+ inCodeBlock: openCodeFence(props.value, textareaRef?.selectionStart ?? props.value.length) !== null
563
+ });
564
+ if (action === "send") {
565
+ event.preventDefault();
566
+ void submit();
567
+ }
568
+ return;
569
+ }
570
+ const send = props.sendKey === "mod-enter" ? event.ctrlKey || event.metaKey : !event.shiftKey;
571
+ if (send && !event.isComposing) {
379
572
  event.preventDefault();
380
573
  void submit();
381
574
  }
382
575
  };
383
- return _$ssr(_tmpl$0, _$escape(_$createComponent(Show, {
576
+ const insertText = text => {
577
+ if (!textareaRef || editBlocked()) return;
578
+ replaceTextareaRange(textareaRef, textareaRef.selectionStart, textareaRef.selectionEnd, text);
579
+ };
580
+ let holdTimer;
581
+ let held = false;
582
+ const releaseHold = () => {
583
+ clearTimeout(holdTimer);
584
+ holdTimer = undefined;
585
+ };
586
+ onCleanup(releaseHold);
587
+ const microphoneDisabled = () => Boolean(props.microphone?.disabled || blocked() || dictation() === "refining");
588
+ const microphoneItems = () => {
589
+ const microphone = props.microphone;
590
+ const items = [];
591
+ if (microphone?.onDictate) {
592
+ const dictate = microphone.onDictate;
593
+ items.push({
594
+ icon: "ti ti-microphone",
595
+ label: messages().dictate,
596
+ action: () => reportChatFailure(dictate, props.onError)
597
+ });
598
+ }
599
+ if (microphone?.onVoiceMessage) {
600
+ const record = microphone.onVoiceMessage;
601
+ items.push({
602
+ icon: "ti ti-player-record",
603
+ label: messages().recordVoiceMessage,
604
+ action: () => reportChatFailure(record, props.onError)
605
+ });
606
+ }
607
+ return items;
608
+ };
609
+ const dictationStatus = () => {
610
+ switch (dictation()) {
611
+ case "listening":
612
+ return {
613
+ icon: "ti ti-microphone",
614
+ text: messages().dictationListening
615
+ };
616
+ case "refining":
617
+ return {
618
+ icon: "ti ti-loader-2 k2b-spin",
619
+ text: messages().dictationRefining
620
+ };
621
+ case "refined":
622
+ return {
623
+ icon: "ti ti-sparkles",
624
+ text: messages().dictationRefined
625
+ };
626
+ case "unrefined":
627
+ return {
628
+ icon: "ti ti-microphone",
629
+ text: messages().dictationUnrefined
630
+ };
631
+ case "interrupted":
632
+ return {
633
+ icon: "ti ti-alert-triangle",
634
+ text: messages().dictationInterrupted
635
+ };
636
+ default:
637
+ return null;
638
+ }
639
+ };
640
+ const fileInput = () => _$createComponent(Show, {
384
641
  get when() {
385
- return commandsOpen() || hasAccessory();
642
+ return props.fileSelection;
386
643
  },
387
644
  get children() {
388
- return _$ssr(_tmpl$6, _$ssrStyleProperty("visibility:", commandsOpen() ? "hidden" : _$escape(undefined, true)), _$ssrAttribute("inert", commandsOpen(), true), _$escape(accessory()), _$escape(_$createComponent(Show, {
645
+ return _$ssr(_tmpl$2, _$ssrAttribute("accept", _$escape(props.fileSelection?.accept, true), false), _$ssrAttribute("multiple", props.fileSelection?.multiple ?? true, true));
646
+ }
647
+ });
648
+ const addMenu = (icon, extraClass = "") => _$createComponent(Dropdown.Root, {
649
+ position: "top-right",
650
+ get label() {
651
+ return messages().addToChat;
652
+ },
653
+ get items() {
654
+ return menuItems();
655
+ },
656
+ get disabled() {
657
+ return blocked();
658
+ },
659
+ get children() {
660
+ return _$createComponent(Dropdown.Trigger, {
661
+ appearance: "plain",
662
+ "class": `k2b-chat-composer__icon-action${extraClass}`,
663
+ get label() {
664
+ return messages().addToChat;
665
+ },
666
+ get tooltip() {
667
+ return messages().addToChat;
668
+ },
669
+ get children() {
670
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(icon, true), false));
671
+ }
672
+ });
673
+ }
674
+ });
675
+
676
+ /** The conversation's attach button opens the file choice directly; other actions turn it into the add menu. */
677
+ const attachControl = () => _$createComponent(Show, {
678
+ get when() {
679
+ return props.menuActions?.length;
680
+ },
681
+ get fallback() {
682
+ return _$createComponent(Show, {
683
+ get when() {
684
+ return props.fileSelection;
685
+ },
686
+ children: selection => _$createComponent(Tooltip.Trigger, {
687
+ type: "button",
688
+ "class": "k2b-chat-composer__icon-action k2b-chat-composer__attach",
689
+ get ["aria-label"]() {
690
+ return selection().label ?? messages().attachFiles;
691
+ },
692
+ get content() {
693
+ return selection().label ?? messages().attachFiles;
694
+ },
695
+ get disabled() {
696
+ return !canSelectFiles();
697
+ },
698
+ onClick: chooseFiles,
699
+ get children() {
700
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-plus", false));
701
+ }
702
+ })
703
+ });
704
+ },
705
+ get children() {
706
+ return addMenu("ti ti-plus", " k2b-chat-composer__attach");
707
+ }
708
+ });
709
+ const formatToggle = () => _$createComponent(Show, {
710
+ get when() {
711
+ return props.formatting;
712
+ },
713
+ get children() {
714
+ return _$createComponent(Tooltip.Trigger, {
715
+ type: "button",
716
+ "class": "k2b-chat-composer__icon-action k2b-chat-composer__format-toggle",
717
+ get ["aria-label"]() {
718
+ return messages().composerFormatting;
719
+ },
720
+ get ["aria-pressed"]() {
721
+ return formattingOpen();
722
+ },
723
+ get ["aria-controls"]() {
724
+ return formattingOpen() ? formatGroupId : undefined;
725
+ },
726
+ get content() {
727
+ return messages().composerFormatting;
728
+ },
729
+ onMouseDown: keepFieldFocus,
730
+ onClick: () => setFormattingOpen(open => !open),
731
+ get children() {
732
+ return _$ssr(_tmpl$4);
733
+ }
734
+ });
735
+ }
736
+ });
737
+
738
+ /** One row of fixed height. It shows every button at once, also on a phone, so nothing hides behind a scroll. */
739
+ const formatRow = () => _$createComponent(Show, {
740
+ get when() {
741
+ return _$memo(() => !!props.formatting)() && formattingOpen();
742
+ },
743
+ get children() {
744
+ return _$ssr(_tmpl$5, _$ssrAttribute("id", _$escape(formatGroupId, true), false), _$ssrAttribute("aria-label", _$escape(messages().composerFormatting, true), false), _$escape(_$createComponent(For, {
745
+ each: formatTools,
746
+ children: tool => _$createComponent(Tooltip.Trigger, {
747
+ type: "button",
748
+ "class": "k2b-chat-composer__icon-action",
749
+ get ["aria-label"]() {
750
+ return tool.label(messages());
751
+ },
752
+ get content() {
753
+ return tool.label(messages());
754
+ },
755
+ get disabled() {
756
+ return editBlocked();
757
+ },
758
+ onMouseDown: keepFieldFocus,
759
+ onClick: () => {
760
+ if (textareaRef) tool.run(textareaRef);
761
+ },
762
+ get children() {
763
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(tool.icon, true), false));
764
+ }
765
+ })
766
+ })));
767
+ }
768
+ });
769
+ const emojiButton = () => _$createComponent(Show, {
770
+ get when() {
771
+ return props.emoji;
772
+ },
773
+ children: emoji => _$createComponent(Tooltip.Trigger, {
774
+ type: "button",
775
+ "class": "k2b-chat-composer__icon-action k2b-chat-composer__emoji",
776
+ get ["aria-label"]() {
777
+ return messages().insertEmoji;
778
+ },
779
+ get content() {
780
+ return messages().insertEmoji;
781
+ },
782
+ get disabled() {
783
+ return editBlocked();
784
+ },
785
+ onMouseDown: keepFieldFocus,
786
+ onClick: event => {
787
+ const anchor = event.currentTarget;
788
+ reportChatFailure(() => emoji().onOpen({
789
+ anchor,
790
+ insert: insertText
791
+ }), props.onError);
792
+ },
793
+ get children() {
794
+ return _$ssr(_tmpl$6);
795
+ }
796
+ })
797
+ });
798
+ const microphoneControl = () => _$createComponent(Show, {
799
+ get when() {
800
+ return _$memo(() => !!(props.microphone?.onDictate || props.microphone?.onVoiceMessage))() ? props.microphone : undefined;
801
+ },
802
+ children: microphone => _$ssr(_tmpl$8, _$escape(_$createComponent(Tooltip.Trigger, {
803
+ type: "button",
804
+ "class": "k2b-chat-composer__icon-action k2b-chat-composer__microphone-button",
805
+ get ["data-state"]() {
806
+ return dictation() ?? undefined;
807
+ },
808
+ get disabled() {
809
+ return microphoneDisabled();
810
+ },
811
+ get ["aria-pressed"]() {
812
+ return _$memo(() => !!microphone().onDictate)() ? dictation() === "listening" : undefined;
813
+ },
814
+ get ["aria-haspopup"]() {
815
+ return microphone().onDictate ? undefined : "menu";
816
+ },
817
+ get ["aria-expanded"]() {
818
+ return _$memo(() => !!microphone().onDictate)() ? undefined : microphoneMenuOpen();
819
+ },
820
+ get ["aria-label"]() {
821
+ return _$memo(() => !!microphone().onDictate)() ? messages().dictate : messages().recordVoiceMessage;
822
+ },
823
+ get content() {
824
+ return _$memo(() => dictation() === "listening")() ? messages().stopDictation : _$memo(() => !!microphone().onDictate)() ? _$memo(() => !!microphone().onVoiceMessage)() ? messages().dictateOrHold : messages().dictate : messages().recordVoiceMessage;
825
+ },
826
+ onMouseDown: keepFieldFocus,
827
+ onContextMenu: event => event.preventDefault(),
828
+ onPointerDown: event => {
829
+ held = false;
830
+ releaseHold();
831
+ const record = microphone().onVoiceMessage;
832
+ if (event.button !== 0 || !record || dictation() === "listening") return;
833
+ holdTimer = setTimeout(() => {
834
+ // The composer may have become blocked, or recording withdrawn, during the press.
835
+ const current = props.microphone?.onVoiceMessage;
836
+ if (!current || microphoneDisabled()) return;
837
+ held = true;
838
+ reportChatFailure(current, props.onError);
839
+ }, microphoneHoldMs);
840
+ },
841
+ onPointerUp: releaseHold,
842
+ onPointerCancel: releaseHold,
843
+ onPointerLeave: releaseHold,
844
+ onClick: event => {
845
+ // A press that already asked for a voice message ends here; a key press never holds.
846
+ if (held) {
847
+ held = false;
848
+ if (event.detail > 0) return;
849
+ }
850
+ const dictate = microphone().onDictate;
851
+ if (dictate) {
852
+ reportChatFailure(dictate, props.onError);
853
+ return;
854
+ }
855
+ setMicrophoneMenuOpen(true);
856
+ // From the keyboard it opens the menu like a menu button: the focus moves to the first item.
857
+ if (event.detail === 0) {
858
+ queueMicrotask(() => microphoneRef?.querySelector('[role="menuitem"]:not(:disabled)')?.focus());
859
+ }
860
+ },
861
+ get children() {
862
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", dictation() === "listening" ? "ti ti-player-stop" : dictation() === "refining" ? "ti ti-loader-2 k2b-spin" : "ti ti-microphone", false));
863
+ }
864
+ })), _$escape(_$createComponent(Show, {
865
+ get when() {
866
+ return microphone().onVoiceMessage;
867
+ },
868
+ get children() {
869
+ return _$createComponent(Dropdown.Root, {
870
+ position: "top-left",
871
+ get label() {
872
+ return messages().microphoneOptions;
873
+ },
874
+ get items() {
875
+ return microphoneItems();
876
+ },
877
+ get disabled() {
878
+ return microphoneDisabled();
879
+ },
880
+ get open() {
881
+ return microphoneMenuOpen();
882
+ },
883
+ onOpenChange: setMicrophoneMenuOpen,
884
+ get children() {
885
+ return _$createComponent(Dropdown.Trigger, {
886
+ appearance: "plain",
887
+ "class": "k2b-chat-composer__icon-action k2b-chat-composer__microphone-menu",
888
+ get label() {
889
+ return messages().microphoneOptions;
890
+ },
891
+ get tooltip() {
892
+ return messages().microphoneOptions;
893
+ },
894
+ get children() {
895
+ return _$ssr(_tmpl$7);
896
+ }
897
+ });
898
+ }
899
+ });
900
+ }
901
+ })))
902
+ });
903
+ const sendControl = () => _$createComponent(Show, {
904
+ get when() {
905
+ return _$memo(() => !!(running() && !hasDraft()))() && props.onStop;
906
+ },
907
+ get fallback() {
908
+ return _$createComponent(Tooltip.Trigger, {
909
+ type: "button",
910
+ "class": "k2b-chat-composer__send",
911
+ get disabled() {
912
+ return !canSubmit();
913
+ },
914
+ onMouseDown: event => {
915
+ if (conversation()) keepFieldFocus(event);
916
+ },
917
+ get ["aria-label"]() {
918
+ return _$memo(() => !!submitting())() ? _$memo(() => !!running())() ? _$memo(() => runningSubmitIntent() === "queue")() ? messages().queueing : messages().steering : messages().sending : _$memo(() => !!running())() ? _$memo(() => runningSubmitIntent() === "queue")() ? messages().queueMessage : messages().steerResponse : messages().sendMessage;
919
+ },
920
+ get content() {
921
+ return _$memo(() => !!running())() ? _$memo(() => runningSubmitIntent() === "queue")() ? messages().queueMessage : messages().steerResponse : messages().sendMessage;
922
+ },
923
+ onClick: () => void submit(),
924
+ get children() {
925
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", submitting() ? "ti ti-loader-2 k2b-spin" : conversation() ? "ti ti-send" : "ti ti-arrow-up", false));
926
+ }
927
+ });
928
+ },
929
+ get children() {
930
+ return _$createComponent(Tooltip.Trigger, {
931
+ type: "button",
932
+ "class": "k2b-chat-composer__stop",
933
+ get disabled() {
934
+ return stopping();
935
+ },
936
+ get ["aria-label"]() {
937
+ return _$memo(() => !!stopping())() ? messages().stopping : messages().stopResponse;
938
+ },
939
+ get content() {
940
+ return messages().stopResponse;
941
+ },
942
+ onClick: () => reportChatFailure(() => props.onStop?.(), props.onError),
943
+ get children() {
944
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", stopping() ? "ti ti-loader-2 k2b-spin" : "ti ti-player-stop", false));
945
+ }
946
+ });
947
+ }
948
+ });
949
+ const field = () => _$ssr(_tmpl$1, _$ssrAttribute("aria-label", _$escape(messages().messageInput, true), false), _$escape(_$createComponent(Show, {
950
+ get when() {
951
+ return mentions().length > 0;
952
+ },
953
+ get children() {
954
+ return _$ssr(_tmpl$9, _$escape(_$createComponent(For, {
955
+ get each() {
956
+ return chatMentionSegments(props.value, mentions());
957
+ },
958
+ children: part => _$ssr(_tmpl$10, Boolean(part.mention) ? "k2b-chat-composer__mention" : "", _$escape(part.text))
959
+ })));
960
+ }
961
+ })), 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() ? messages().addGuidance : conversation() ? messages().writeConversationMessage : messages().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() ? emojiMatches()[selectedCommandIndex()] : selectedCommand()) ? `${_$escape(commandListId, true)}-${_$escape(selectedCommandIndex(), true)}` : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
962
+ get when() {
963
+ return conversation();
964
+ },
965
+ get children() {
966
+ return _$ssr(_tmpl$0);
967
+ }
968
+ })));
969
+ return _$ssr(_tmpl$21, _$escape(_$createComponent(Show, {
970
+ get when() {
971
+ return suggestionsOpen() || hasAccessory();
972
+ },
973
+ get children() {
974
+ return _$ssr(_tmpl$16, _$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, {
975
+ get when() {
976
+ return emojiOpen();
977
+ },
978
+ get children() {
979
+ return _$ssr(_tmpl$11, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(messages().emojiSuggestions, true), false), _$escape(_$createComponent(For, {
980
+ get each() {
981
+ return emojiMatches();
982
+ },
983
+ children: (entry, index) => _$ssr(_tmpl$22, `${_$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, {
984
+ get when() {
985
+ return entry.shortcodes[0];
986
+ },
987
+ children: code => _$ssr(_tmpl$23, _$escape(code()))
988
+ })))
989
+ })));
990
+ }
991
+ })), _$escape(_$createComponent(Show, {
389
992
  get when() {
390
993
  return commandsOpen();
391
994
  },
392
995
  get children() {
393
- return _$ssr(_tmpl$5, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(messages().commands, true), false), _$escape(_$createComponent(For, {
996
+ return _$ssr(_tmpl$15, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(messages().commands, true), false), _$escape(_$createComponent(For, {
394
997
  get each() {
395
998
  return commandMatches();
396
999
  },
397
- children: (command, index) => _$ssr(_tmpl$1, `${_$escape(commandListId, true)}-${_$escape(index(), true)}`, _$ssrAttribute("aria-selected", _$escape(index(), true) === _$escape(selectedCommandIndex(), true), false) + _$ssrAttribute("aria-disabled", _$escape(command.disabled, true), false) + _$ssrAttribute("data-active", index() === selectedCommandIndex() ? "true" : _$escape(undefined, true), false), _$ssrAttribute("class", _$escape(command.icon ?? "ti ti-slash", true), false), _$escape(command.label ?? `/${command.name}`), _$escape(command.description))
1000
+ children: (command, index) => _$ssr(_tmpl$24, `${_$escape(commandListId, true)}-${_$escape(index(), true)}`, _$ssrAttribute("aria-selected", _$escape(index(), true) === _$escape(selectedCommandIndex(), true), false) + _$ssrAttribute("aria-disabled", _$escape(command.disabled, true), false) + _$ssrAttribute("data-active", index() === selectedCommandIndex() ? "true" : _$escape(undefined, true), false), _$ssrAttribute("class", _$escape(command.icon ?? "ti ti-slash", true), false), _$escape(command.label ?? `/${command.name}`), _$escape(command.description))
398
1001
  })), _$escape(_$createComponent(Show, {
399
1002
  get when() {
400
1003
  return searching();
401
1004
  },
402
1005
  get children() {
403
- return _$ssr(_tmpl$2, _$escape(messages().loading));
1006
+ return _$ssr(_tmpl$12, _$escape(messages().loading));
404
1007
  }
405
1008
  })), _$escape(_$createComponent(Show, {
406
1009
  get when() {
407
1010
  return searchError();
408
1011
  },
409
1012
  get children() {
410
- return _$ssr(_tmpl$3, _$escape(messages().error));
1013
+ return _$ssr(_tmpl$13, _$escape(messages().error));
411
1014
  }
412
1015
  })), _$escape(_$createComponent(Show, {
413
1016
  get when() {
414
1017
  return _$memo(() => !!(!searching() && !searchError()))() && commandMatches().length === 0;
415
1018
  },
416
1019
  get children() {
417
- return _$ssr(_tmpl$4, _$escape(messages().noResults));
1020
+ return _$ssr(_tmpl$14, _$escape(messages().noResults));
418
1021
  }
419
1022
  })));
420
1023
  }
421
1024
  })));
422
1025
  }
423
- })), `k2b-chat-composer ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-running", running() ? "true" : _$escape(undefined, true), false), _$ssrAttribute("aria-label", _$escape(props.label ?? messages().messageComposer, true), false), _$escape(_$createComponent(Show, {
1026
+ })), `k2b-chat-composer ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-running", running() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-variant", conversation() ? "conversation" : _$escape(undefined, true), false), _$ssrAttribute("aria-label", _$escape(props.label ?? messages().messageComposer, true), false), _$escape(_$createComponent(Show, {
1027
+ get when() {
1028
+ return showHint();
1029
+ },
1030
+ get children() {
1031
+ return _$ssr(_tmpl$17, _$escape(_$createComponent(Show, {
1032
+ get when() {
1033
+ return dictationStatus();
1034
+ },
1035
+ get fallback() {
1036
+ return _$ssr(_tmpl$25, _$escape(hint()));
1037
+ },
1038
+ children: status => [_$ssr(_tmpl$26, _$ssrAttribute("data-state", _$escape(dictation() ?? undefined, true), false), _$ssrAttribute("class", _$escape(status().icon, true), false), _$escape(status().text)), _$createComponent(Show, {
1039
+ get when() {
1040
+ return _$memo(() => dictation() === "refined")() ? props.microphone?.onRestoreOriginal : undefined;
1041
+ },
1042
+ children: restore => _$ssr(_tmpl$27, _$ssrAttribute("disabled", microphoneDisabled(), true), _$escape(messages().restoreOriginal))
1043
+ })]
1044
+ })));
1045
+ }
1046
+ })), _$escape(_$createComponent(Show, {
424
1047
  get when() {
425
1048
  return attachments().length > 0;
426
1049
  },
427
1050
  get children() {
428
- return _$ssr(_tmpl$7, _$ssrAttribute("aria-label", _$escape(messages().attachments, true), false), _$escape(_$createComponent(For, {
1051
+ return _$ssr(_tmpl$18, _$ssrAttribute("aria-label", _$escape(messages().attachments, true), false), _$escape(_$createComponent(For, {
429
1052
  get each() {
430
1053
  return attachments();
431
1054
  },
432
- children: attachment => _$ssr(_tmpl$11, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
1055
+ children: attachment => _$ssr(_tmpl$29, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
433
1056
  get when() {
434
1057
  return attachment.href;
435
1058
  },
436
1059
  get fallback() {
437
- return _$ssr(_tmpl$13, _$escape(_$createComponent(Show, {
1060
+ return _$ssr(_tmpl$31, _$escape(_$createComponent(Show, {
438
1061
  get when() {
439
1062
  return _$memo(() => attachment.kind === "image")() && attachment.previewUrl;
440
1063
  },
441
1064
  get fallback() {
442
- return _$ssr(_tmpl$14, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
1065
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
443
1066
  },
444
1067
  get children() {
445
- return _$ssr(_tmpl$12, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1068
+ return _$ssr(_tmpl$30, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
446
1069
  }
447
1070
  })), _$escape(renderAttachmentCopy(attachment)));
448
1071
  },
449
- children: href => _$ssr(_tmpl$15, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(messages().openInNewTab({
1072
+ children: href => _$ssr(_tmpl$32, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(messages().openInNewTab({
450
1073
  name: attachment.name
451
1074
  }), true), false), _$escape(_$createComponent(Show, {
452
1075
  get when() {
453
1076
  return _$memo(() => attachment.kind === "image")() && attachment.previewUrl;
454
1077
  },
455
1078
  get fallback() {
456
- return _$ssr(_tmpl$14, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
1079
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
457
1080
  },
458
1081
  get children() {
459
- return _$ssr(_tmpl$12, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
1082
+ return _$ssr(_tmpl$30, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
460
1083
  }
461
1084
  })), _$escape(renderAttachmentCopy(attachment)))
462
1085
  })), _$escape(_$createComponent(Show, {
463
1086
  get when() {
464
1087
  return attachment.action;
465
1088
  },
466
- children: action => _$ssr(_tmpl$16, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
1089
+ children: action => _$ssr(_tmpl$33, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
467
1090
  get when() {
468
1091
  return action().icon;
469
1092
  },
470
- children: icon => _$ssr(_tmpl$14, _$ssrAttribute("class", _$escape(icon(), true), false))
1093
+ children: icon => _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(icon(), true), false))
471
1094
  })), _$escape(action().label))
472
1095
  })), _$escape(_$createComponent(Show, {
473
1096
  get when() {
474
1097
  return props.onAttachmentsChange;
475
1098
  },
476
1099
  get children() {
477
- return _$ssr(_tmpl$10, _$ssrAttribute("aria-label", _$escape(messages().removeNamed({
1100
+ return _$ssr(_tmpl$28, _$ssrAttribute("aria-label", _$escape(messages().removeNamed({
478
1101
  name: attachment.name
479
1102
  }), true), false), _$ssrAttribute("disabled", blocked(), true));
480
1103
  }
481
1104
  })))
482
1105
  })));
483
1106
  }
484
- })), _$ssrAttribute("aria-label", _$escape(messages().messageInput, true), false), _$escape(_$createComponent(Show, {
485
- get when() {
486
- return mentions().length > 0;
487
- },
488
- get children() {
489
- return _$ssr(_tmpl$8, _$escape(_$createComponent(For, {
490
- get each() {
491
- return chatMentionSegments(props.value, mentions());
492
- },
493
- children: part => _$ssr(_tmpl$17, Boolean(part.mention) ? "k2b-chat-composer__mention" : "", _$escape(part.text))
494
- })));
495
- }
496
- })), mentions().length > 0 ? "k2b-chat-composer__textarea--highlighted" : "", _$ssrAttribute("value", _$escape(props.value, true), false), _$ssrAttribute("disabled", blocked(), true) + _$ssrAttribute("placeholder", _$escape(props.placeholder ?? (running() ? messages().addGuidance : messages().writeMessage), true), false) + _$ssrAttribute("aria-label", _$escape(props.inputLabel ?? messages().message, true), false) + _$ssrAttribute("role", commandsOpen() ? "combobox" : _$escape(undefined, true), false) + _$ssrAttribute("aria-autocomplete", commandsOpen() ? "list" : _$escape(undefined, true), false) + _$ssrAttribute("aria-controls", commandsOpen() ? _$escape(commandListId, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-expanded", commandsOpen() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-activedescendant", selectedCommand() ? `${_$escape(commandListId, true)}-${_$escape(selectedCommandIndex(), true)}` : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
497
- get when() {
498
- return props.error;
499
- },
500
- get children() {
501
- return _$ssr(_tmpl$9, _$escape(props.error));
502
- }
503
1107
  })), _$escape(_$createComponent(Show, {
504
1108
  get when() {
505
- return props.footerContent;
1109
+ return conversation();
506
1110
  },
507
1111
  get fallback() {
508
- return [_$ssr(_tmpl$20, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
1112
+ return [field(), _$createComponent(Show, {
509
1113
  get when() {
510
- return hasAddMenu();
1114
+ return props.error;
511
1115
  },
512
1116
  get children() {
513
- return _$createComponent(Dropdown.Root, {
514
- position: "top-right",
515
- get label() {
516
- return messages().addToChat;
517
- },
518
- get items() {
519
- return menuItems();
1117
+ return _$ssr(_tmpl$19, _$escape(props.error));
1118
+ }
1119
+ }), formatRow(), _$ssr(_tmpl$34, _$escape(_$createComponent(Show, {
1120
+ get when() {
1121
+ return props.footerContent;
1122
+ },
1123
+ get fallback() {
1124
+ return [_$ssr(_tmpl$35, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
1125
+ get when() {
1126
+ return hasAddMenu();
520
1127
  },
521
- get disabled() {
522
- return blocked();
1128
+ get children() {
1129
+ return addMenu("ti ti-plus");
1130
+ }
1131
+ })), _$escape(fileInput()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(_$createComponent(Show, {
1132
+ get when() {
1133
+ return (props.models?.length ?? 0) > 0;
523
1134
  },
524
1135
  get children() {
525
- return _$createComponent(Dropdown.Trigger, {
526
- appearance: "plain",
527
- "class": "k2b-chat-composer__icon-action",
528
- get label() {
529
- return messages().addToChat;
1136
+ return _$createComponent(SelectChip, {
1137
+ get ["aria-label"]() {
1138
+ return messages().chooseModel;
1139
+ },
1140
+ position: "top-right",
1141
+ "class": "k2b-chat-composer__model",
1142
+ menuWidth: "15rem",
1143
+ get placeholder() {
1144
+ return messages().model;
530
1145
  },
531
- get tooltip() {
532
- return messages().addToChat;
1146
+ value: () => props.selectedModelId ?? "",
1147
+ get options() {
1148
+ return (props.models ?? []).map(model => ({
1149
+ value: model.id,
1150
+ label: model.label,
1151
+ description: model.description,
1152
+ icon: model.icon,
1153
+ image: model.image
1154
+ }));
533
1155
  },
534
- get children() {
535
- return _$ssr(_tmpl$18);
1156
+ get disabled() {
1157
+ return blocked() || running() || !props.onModelChange;
1158
+ },
1159
+ onValueChange: modelId => {
1160
+ props.onModelChange?.(modelId);
1161
+ queueMicrotask(focus);
536
1162
  }
537
1163
  });
538
1164
  }
539
- });
540
- }
541
- })), _$escape(_$createComponent(Show, {
542
- get when() {
543
- return props.fileSelection;
544
- },
545
- get children() {
546
- return _$ssr(_tmpl$19, _$ssrAttribute("accept", _$escape(props.fileSelection?.accept, true), false), _$ssrAttribute("multiple", props.fileSelection?.multiple ?? true, true));
547
- }
548
- })), _$escape(_$createComponent(Show, {
549
- get when() {
550
- return (props.models?.length ?? 0) > 0;
551
- },
552
- get children() {
553
- return _$createComponent(SelectChip, {
554
- get ["aria-label"]() {
555
- return messages().chooseModel;
556
- },
557
- position: "top-right",
558
- "class": "k2b-chat-composer__model",
559
- menuWidth: "15rem",
560
- get placeholder() {
561
- return messages().model;
562
- },
563
- value: () => props.selectedModelId ?? "",
564
- get options() {
565
- return (props.models ?? []).map(model => ({
566
- value: model.id,
567
- label: model.label,
568
- description: model.description,
569
- icon: model.icon,
570
- image: model.image
571
- }));
1165
+ })), _$escape(props.modelDetails)), _$ssr(_tmpl$36, _$escape(_$createComponent(For, {
1166
+ get each() {
1167
+ return props.contextActions;
572
1168
  },
573
- get disabled() {
574
- return blocked() || running() || !props.onModelChange;
1169
+ children: action => _$createComponent(Tooltip.Trigger, {
1170
+ type: "button",
1171
+ "class": "k2b-chat-composer__icon-action",
1172
+ get ["data-tone"]() {
1173
+ return action.variant === "danger" ? "danger" : undefined;
1174
+ },
1175
+ get disabled() {
1176
+ return action.disabled;
1177
+ },
1178
+ get ["aria-pressed"]() {
1179
+ return action.pressed;
1180
+ },
1181
+ get ["aria-label"]() {
1182
+ return action.label;
1183
+ },
1184
+ get content() {
1185
+ return action.label;
1186
+ },
1187
+ onClick: () => reportChatFailure(() => executeChatAction(action), props.onError),
1188
+ get children() {
1189
+ return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(action.icon ?? "ti ti-dots", true), false));
1190
+ }
1191
+ })
1192
+ })), _$escape(_$createComponent(Show, {
1193
+ get when() {
1194
+ return _$memo(() => !!hasContextUsage())() ? props.contextUsage ?? {} : undefined;
575
1195
  },
576
- onValueChange: modelId => {
577
- props.onModelChange?.(modelId);
578
- queueMicrotask(focus);
579
- }
580
- });
581
- }
582
- })), _$escape(props.modelDetails)), _$ssr(_tmpl$21, _$escape(_$createComponent(For, {
583
- get each() {
584
- return props.contextActions;
585
- },
586
- children: action => _$createComponent(Tooltip.Trigger, {
587
- type: "button",
588
- "class": "k2b-chat-composer__icon-action",
589
- get ["data-tone"]() {
590
- return action.variant === "danger" ? "danger" : undefined;
591
- },
592
- get disabled() {
593
- return action.disabled;
594
- },
595
- get ["aria-pressed"]() {
596
- return action.pressed;
597
- },
598
- get ["aria-label"]() {
599
- return action.label;
600
- },
601
- get content() {
602
- return action.label;
603
- },
604
- onClick: () => reportChatFailure(() => executeChatAction(action), props.onError),
605
- get children() {
606
- return _$ssr(_tmpl$14, _$ssrAttribute("class", _$escape(action.icon ?? "ti ti-dots", true), false));
607
- }
608
- })
609
- })), _$escape(_$createComponent(Show, {
610
- get when() {
611
- return _$memo(() => !!hasContextUsage())() ? props.contextUsage ?? {} : undefined;
1196
+ children: usage => _$createComponent(ContextUsage, _$mergeProps(usage, {
1197
+ get action() {
1198
+ return props.contextPopupAction;
1199
+ },
1200
+ get onActionError() {
1201
+ return props.onError;
1202
+ }
1203
+ }))
1204
+ })), _$escape(props.submitTools), _$escape(microphoneControl()), _$escape(sendControl()))];
612
1205
  },
613
- children: usage => _$createComponent(ContextUsage, _$mergeProps(usage, {
614
- get action() {
615
- return props.contextPopupAction;
616
- },
617
- get onActionError() {
618
- return props.onError;
619
- }
620
- }))
621
- })), _$escape(props.submitTools), _$escape(_$createComponent(Show, {
1206
+ children: content => content()
1207
+ })))];
1208
+ },
1209
+ get children() {
1210
+ return [_$createComponent(Show, {
622
1211
  get when() {
623
- return _$memo(() => !!(running() && !hasDraft()))() && props.onStop;
624
- },
625
- get fallback() {
626
- return _$createComponent(Tooltip.Trigger, {
627
- type: "button",
628
- "class": "k2b-chat-composer__send",
629
- get disabled() {
630
- return !canSubmit();
631
- },
632
- get ["aria-label"]() {
633
- return _$memo(() => !!submitting())() ? _$memo(() => !!running())() ? _$memo(() => runningSubmitIntent() === "queue")() ? messages().queueing : messages().steering : messages().sending : _$memo(() => !!running())() ? _$memo(() => runningSubmitIntent() === "queue")() ? messages().queueMessage : messages().steerResponse : messages().sendMessage;
634
- },
635
- get content() {
636
- return _$memo(() => !!running())() ? _$memo(() => runningSubmitIntent() === "queue")() ? messages().queueMessage : messages().steerResponse : messages().sendMessage;
637
- },
638
- onClick: () => void submit(),
639
- get children() {
640
- return _$ssr(_tmpl$14, _$ssrAttribute("class", submitting() ? "ti ti-loader-2 k2b-spin" : "ti ti-arrow-up", false));
641
- }
642
- });
1212
+ return props.error;
643
1213
  },
644
1214
  get children() {
645
- return _$createComponent(Tooltip.Trigger, {
646
- type: "button",
647
- "class": "k2b-chat-composer__stop",
648
- get disabled() {
649
- return stopping();
650
- },
651
- get ["aria-label"]() {
652
- return _$memo(() => !!stopping())() ? messages().stopping : messages().stopResponse;
653
- },
654
- get content() {
655
- return messages().stopResponse;
656
- },
657
- onClick: () => reportChatFailure(() => props.onStop?.(), props.onError),
658
- get children() {
659
- return _$ssr(_tmpl$14, _$ssrAttribute("class", stopping() ? "ti ti-loader-2 k2b-spin" : "ti ti-player-stop", false));
660
- }
661
- });
1215
+ return _$ssr(_tmpl$19, _$escape(props.error));
662
1216
  }
663
- })))];
664
- },
665
- children: content => content()
1217
+ }), formatRow(), _$ssr(_tmpl$20, _$escape(attachControl()), _$escape(fileInput()), _$escape(field()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(microphoneControl()), _$escape(sendControl()))];
1218
+ }
666
1219
  })), _$escape(_$createComponent(Show, {
667
1220
  get when() {
668
1221
  return props.fileSelection;