@k2b/ui 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/base.css +2 -2
- package/dist/browser/actions/Dropdown.js +6 -3
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/FilterChip.js +137 -7
- package/dist/browser/actions/FilterChip.js.map +1 -1
- package/dist/browser/actions/SegmentedControl.js +5 -9
- package/dist/browser/actions/SegmentedControl.js.map +1 -1
- package/dist/browser/actions/SpotlightSearch.js +6 -3
- package/dist/browser/actions/SpotlightSearch.js.map +1 -1
- package/dist/browser/actions/Tabs.js +5 -2
- package/dist/browser/actions/Tabs.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +294 -0
- package/dist/browser/actions/ViewTabs.js.map +1 -0
- package/dist/browser/actions/filter-messages.js +6 -2
- package/dist/browser/actions/filter-messages.js.map +1 -1
- package/dist/browser/actions/index.js +2 -1
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +1108 -604
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/ChatDecision.js +120 -84
- package/dist/browser/chat/ChatDecision.js.map +1 -1
- package/dist/browser/chat/ChatPrimitives.js +292 -159
- package/dist/browser/chat/ChatPrimitives.js.map +1 -1
- package/dist/browser/chat/chat-messages.js +8 -0
- package/dist/browser/chat/chat-messages.js.map +1 -1
- package/dist/browser/chat/command-search.js +26 -15
- package/dist/browser/chat/command-search.js.map +1 -1
- package/dist/browser/chat/index.js +4 -2
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/content/Calendar.js +143 -116
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/CalendarWeekView.js +291 -266
- package/dist/browser/content/CalendarWeekView.js.map +1 -1
- package/dist/browser/content/DataTable.js +224 -155
- package/dist/browser/content/DataTable.js.map +1 -1
- package/dist/browser/content/data-table-messages.js +4 -2
- package/dist/browser/content/data-table-messages.js.map +1 -1
- package/dist/browser/feedback/MovedHint.js +197 -0
- package/dist/browser/feedback/MovedHint.js.map +1 -0
- package/dist/browser/feedback/dialog-core.js +66 -9
- package/dist/browser/feedback/dialog-core.js.map +1 -1
- package/dist/browser/feedback/index.js +1 -0
- package/dist/browser/feedback/index.js.map +1 -1
- package/dist/browser/feedback/prompts.js +6 -1
- package/dist/browser/feedback/prompts.js.map +1 -1
- package/dist/browser/inputs/DatePicker.js +337 -212
- package/dist/browser/inputs/DatePicker.js.map +1 -1
- package/dist/browser/inputs/date-picker.js +59 -5
- package/dist/browser/inputs/date-picker.js.map +1 -1
- package/dist/browser/intl/messages.js +12 -0
- package/dist/browser/intl/messages.js.map +1 -1
- package/dist/browser/intl/weekend.js +39 -0
- package/dist/browser/intl/weekend.js.map +1 -0
- package/dist/browser/layout/AppOverview.js +0 -1
- package/dist/browser/layout/AppOverview.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +688 -471
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/PageTitle.js +3 -2
- package/dist/browser/layout/PageTitle.js.map +1 -1
- package/dist/browser/layout/SettingsModal.js +2 -2
- package/dist/browser/layout/SettingsModal.js.map +1 -1
- package/dist/browser/layout/SidebarSwitcher.js +13 -12
- package/dist/browser/layout/SidebarSwitcher.js.map +1 -1
- package/dist/browser/layout/TabBar.js +1 -1
- package/dist/browser/layout/TabBar.js.map +1 -1
- package/dist/browser/layout/app-host-width.js +44 -0
- package/dist/browser/layout/app-host-width.js.map +1 -0
- package/dist/browser/layout/app-workspace-controller.js +219 -39
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/index.js +1 -0
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/surfaces/Placeholder.js +36 -13
- package/dist/browser/surfaces/Placeholder.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +5 -2
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/FilterChip.js +59 -7
- package/dist/ssr/actions/FilterChip.js.map +1 -1
- package/dist/ssr/actions/SegmentedControl.js +1 -2
- package/dist/ssr/actions/SegmentedControl.js.map +1 -1
- package/dist/ssr/actions/SpotlightSearch.js +2 -2
- package/dist/ssr/actions/SpotlightSearch.js.map +1 -1
- package/dist/ssr/actions/Tabs.js +2 -2
- package/dist/ssr/actions/Tabs.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +211 -0
- package/dist/ssr/actions/ViewTabs.js.map +1 -0
- package/dist/ssr/actions/filter-messages.js +6 -2
- package/dist/ssr/actions/filter-messages.js.map +1 -1
- package/dist/ssr/actions/index.js +2 -1
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +546 -131
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/ChatDecision.js +33 -19
- package/dist/ssr/chat/ChatDecision.js.map +1 -1
- package/dist/ssr/chat/ChatPrimitives.js +95 -24
- package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
- package/dist/ssr/chat/chat-messages.js +8 -0
- package/dist/ssr/chat/chat-messages.js.map +1 -1
- package/dist/ssr/chat/command-search.js +26 -15
- package/dist/ssr/chat/command-search.js.map +1 -1
- package/dist/ssr/chat/index.js +4 -2
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/content/Calendar.js +109 -100
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/CalendarWeekView.js +42 -28
- package/dist/ssr/content/CalendarWeekView.js.map +1 -1
- package/dist/ssr/content/DataTable.js +78 -31
- package/dist/ssr/content/DataTable.js.map +1 -1
- package/dist/ssr/content/data-table-messages.js +4 -2
- package/dist/ssr/content/data-table-messages.js.map +1 -1
- package/dist/ssr/feedback/MovedHint.js +166 -0
- package/dist/ssr/feedback/MovedHint.js.map +1 -0
- package/dist/ssr/feedback/dialog-core.js +66 -9
- package/dist/ssr/feedback/dialog-core.js.map +1 -1
- package/dist/ssr/feedback/index.js +1 -0
- package/dist/ssr/feedback/index.js.map +1 -1
- package/dist/ssr/feedback/prompts.js +6 -1
- package/dist/ssr/feedback/prompts.js.map +1 -1
- package/dist/ssr/inputs/DatePicker.js +138 -69
- package/dist/ssr/inputs/DatePicker.js.map +1 -1
- package/dist/ssr/inputs/date-picker.js +59 -5
- package/dist/ssr/inputs/date-picker.js.map +1 -1
- package/dist/ssr/intl/messages.js +12 -0
- package/dist/ssr/intl/messages.js.map +1 -1
- package/dist/ssr/intl/weekend.js +39 -0
- package/dist/ssr/intl/weekend.js.map +1 -0
- package/dist/ssr/layout/AppOverview.js +0 -1
- package/dist/ssr/layout/AppOverview.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +203 -39
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/PageTitle.js +3 -2
- package/dist/ssr/layout/PageTitle.js.map +1 -1
- package/dist/ssr/layout/SettingsModal.js +2 -2
- package/dist/ssr/layout/SettingsModal.js.map +1 -1
- package/dist/ssr/layout/SidebarSwitcher.js +13 -12
- package/dist/ssr/layout/SidebarSwitcher.js.map +1 -1
- package/dist/ssr/layout/TabBar.js +1 -1
- package/dist/ssr/layout/TabBar.js.map +1 -1
- package/dist/ssr/layout/app-host-width.js +44 -0
- package/dist/ssr/layout/app-host-width.js.map +1 -0
- package/dist/ssr/layout/app-workspace-controller.js +219 -39
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/index.js +1 -0
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/surfaces/Placeholder.js +24 -7
- package/dist/ssr/surfaces/Placeholder.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Dropdown.d.ts +4 -0
- package/dist/types/actions/FilterChip.d.ts +22 -0
- package/dist/types/actions/ViewTabs.d.ts +30 -0
- package/dist/types/actions/filter-messages.d.ts +2 -0
- package/dist/types/actions/index.d.ts +4 -2
- package/dist/types/chat/ChatComposer.d.ts +52 -0
- package/dist/types/chat/ChatDecision.d.ts +18 -2
- package/dist/types/chat/ChatPrimitives.d.ts +10 -0
- package/dist/types/chat/chat-messages.d.ts +4 -0
- package/dist/types/chat/command-search.d.ts +2 -0
- package/dist/types/chat/index.d.ts +9 -8
- package/dist/types/content/DataTable.d.ts +3 -0
- package/dist/types/content/data-table-messages.d.ts +1 -0
- package/dist/types/feedback/MovedHint.d.ts +25 -0
- package/dist/types/feedback/dialog-core.d.ts +5 -2
- package/dist/types/feedback/index.d.ts +2 -0
- package/dist/types/feedback/prompts.d.ts +2 -0
- package/dist/types/inputs/DatePicker.d.ts +7 -0
- package/dist/types/inputs/date-picker.d.ts +25 -1
- package/dist/types/intl/messages.d.ts +6 -0
- package/dist/types/intl/weekend.d.ts +12 -0
- package/dist/types/layout/AppWorkspace.d.ts +43 -7
- package/dist/types/layout/app-host-width.d.ts +6 -0
- package/dist/types/layout/index.d.ts +2 -1
- package/dist/types/surfaces/Placeholder.d.ts +2 -1
- package/package.json +4 -4
|
@@ -17,42 +17,44 @@ var _tmpl$ = ["<span><strong", ">", "</strong></span>"],
|
|
|
17
17
|
_tmpl$0 = "<textarea class=\"k2b-chat-composer__measure\" rows=\"1\" tabindex=\"-1\" aria-hidden=\"true\" readonly></textarea>",
|
|
18
18
|
_tmpl$1 = ["<div class=\"k2b-chat-composer__input\" role=\"group\"", ">", "<textarea class=\"", "\" rows=\"1\"", "", "></textarea>", "</div>"],
|
|
19
19
|
_tmpl$10 = ["<span class=\"", "\">", "</span>"],
|
|
20
|
-
_tmpl$11 = ["<small", ">", "</small>"],
|
|
20
|
+
_tmpl$11 = ["<span class=\"k2b-chat-composer__command-detail\"", "><small", ">", "</small><small", "><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>", "</small></span>"],
|
|
21
21
|
_tmpl$12 = ["<span class=\"k2b-chat-composer__command-end\"><i class=\"ti ti-check\" aria-hidden=\"true\"></i><span class=\"k2b-sr-only\">", "</span></span>"],
|
|
22
22
|
_tmpl$13 = ["<button class=\"k2b-chat-composer__command\"", " type=\"button\" role=\"option\" tabindex=\"-1\"", "><i", " aria-hidden=\"true\"></i><span class=\"k2b-chat-composer__command-text\"><strong>", "</strong>", "</span>", "</button>"],
|
|
23
23
|
_tmpl$14 = ["<b>", "</b>"],
|
|
24
|
-
_tmpl$15 = ["<
|
|
25
|
-
_tmpl$16 = ["<div
|
|
26
|
-
_tmpl$17 = ["<
|
|
27
|
-
_tmpl$18 = ["<div role=\"status\">", "</div>"],
|
|
28
|
-
_tmpl$19 = ["<div
|
|
29
|
-
_tmpl$20 = ["<div
|
|
30
|
-
_tmpl$21 = ["<div class=\"k2b-chat-
|
|
31
|
-
_tmpl$22 = ["<div class=\"k2b-chat-
|
|
32
|
-
_tmpl$23 = ["<div class=\"k2b-chat-
|
|
33
|
-
_tmpl$24 = ["<div class=\"k2b-chat-
|
|
34
|
-
_tmpl$25 = ["<div class=\"k2b-chat-
|
|
35
|
-
_tmpl$26 = ["<
|
|
36
|
-
_tmpl$27 = ["<
|
|
37
|
-
_tmpl$28 = "<
|
|
38
|
-
_tmpl$29 = "<
|
|
39
|
-
_tmpl$30 =
|
|
40
|
-
_tmpl$31 = ["<
|
|
41
|
-
_tmpl$32 =
|
|
42
|
-
_tmpl$33 = ["<
|
|
43
|
-
_tmpl$34 = ["<
|
|
44
|
-
_tmpl$35 = ["<
|
|
45
|
-
_tmpl$36 = ["<
|
|
46
|
-
_tmpl$37 = ["<div class=\"k2b-chat-composer__attachment
|
|
47
|
-
_tmpl$38 = ["<
|
|
48
|
-
_tmpl$39 = ["<
|
|
49
|
-
_tmpl$40 = ["<
|
|
50
|
-
_tmpl$41 = ["<
|
|
51
|
-
_tmpl$42 = ["<
|
|
52
|
-
_tmpl$43 = ["<
|
|
53
|
-
_tmpl$44 = ["<div class=\"k2b-chat-
|
|
54
|
-
_tmpl$45 = ["<span
|
|
55
|
-
|
|
24
|
+
_tmpl$15 = ["<small", ">", "</small>"],
|
|
25
|
+
_tmpl$16 = ["<div class=\"k2b-chat-composer-accessory\"><div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "</div></div>"],
|
|
26
|
+
_tmpl$17 = ["<label class=\"k2b-chat-composer__command-search\"><i class=\"ti ti-search\" aria-hidden=\"true\"></i><input type=\"text\" role=\"combobox\"", " aria-autocomplete=\"list\"", " aria-expanded=\"true\"", " autocomplete=\"off\" autocapitalize=\"off\" spellcheck=\"false\"", "></label>"],
|
|
27
|
+
_tmpl$18 = ["<div role=\"status\" class=\"k2b-chat-composer__loading\"><i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i><strong>", "</strong></div>"],
|
|
28
|
+
_tmpl$19 = ["<div role=\"status\"><i class=\"ti ti-alert-circle\"></i> ", "</div>"],
|
|
29
|
+
_tmpl$20 = ["<div role=\"status\">", "</div>"],
|
|
30
|
+
_tmpl$21 = ["<div class=\"k2b-chat-composer-accessory\" data-commands", ">", "<div", " class=\"k2b-chat-composer__commands\" role=\"listbox\"", ">", "", "", "", "", "</div></div>"],
|
|
31
|
+
_tmpl$22 = ["<div class=\"k2b-chat-composer-slot\"", "><div style=\"", "\"", ">", "</div>", "", "</div>"],
|
|
32
|
+
_tmpl$23 = ["<div class=\"k2b-chat-composer__hint\" aria-live=\"polite\">", "</div>"],
|
|
33
|
+
_tmpl$24 = ["<div class=\"k2b-chat-composer__attachments\" role=\"list\"", " tabindex=\"0\">", "</div>"],
|
|
34
|
+
_tmpl$25 = ["<div class=\"k2b-chat-composer__error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</div>"],
|
|
35
|
+
_tmpl$26 = ["<div class=\"k2b-chat-composer__row\">", "", "<div class=\"k2b-chat-composer__field\">", "<div class=\"k2b-chat-composer__field-tools\">", "", "", "</div></div>", "</div>"],
|
|
36
|
+
_tmpl$27 = ["<div class=\"k2b-chat-composer-shell\">", "<section class=\"", "\"", " role=\"group\"", ">", "", "", "", "</section><div class=\"k2b-sr-only\" role=\"status\" aria-live=\"polite\">", "</div><div class=\"k2b-chat-composer__status\" role=\"status\">", "", "</div></div>"],
|
|
37
|
+
_tmpl$28 = ["<button id=\"", "\" type=\"button\" role=\"option\" tabindex=\"-1\"", "><span class=\"k2b-chat-composer__emoji-glyph\" aria-hidden=\"true\">", "</span><strong>", "</strong>", "</button>"],
|
|
38
|
+
_tmpl$29 = ["<small>:", ":</small>"],
|
|
39
|
+
_tmpl$30 = "<i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>",
|
|
40
|
+
_tmpl$31 = ["<div class=\"k2b-chat-composer__command-group\" role=\"group\"", "><div class=\"k2b-chat-composer__command-heading\"", " role=\"presentation\"><span>", "</span>", "</div>", "", "</div>"],
|
|
41
|
+
_tmpl$32 = "<div class=\"k2b-chat-composer__command-placeholder\" aria-hidden=\"true\"><span></span><span><span></span><span></span></span></div>",
|
|
42
|
+
_tmpl$33 = ["<span class=\"k2b-chat-composer__hint-text\">", "</span>"],
|
|
43
|
+
_tmpl$34 = ["<span class=\"k2b-chat-composer__dictation\"", "><i", " aria-hidden=\"true\"></i><span>", "</span></span>"],
|
|
44
|
+
_tmpl$35 = ["<button type=\"button\" class=\"k2b-chat-composer__hint-action\"", ">", "</button>"],
|
|
45
|
+
_tmpl$36 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-remove\"", "", "><i class=\"ti ti-x\" aria-hidden=\"true\"></i></button>"],
|
|
46
|
+
_tmpl$37 = ["<div class=\"k2b-chat-composer__attachment\" role=\"listitem\"><div class=\"", "\">", "", "</div>", "</div>"],
|
|
47
|
+
_tmpl$38 = ["<img", ">"],
|
|
48
|
+
_tmpl$39 = ["<div class=\"k2b-chat-composer__attachment-identity\">", "", "</div>"],
|
|
49
|
+
_tmpl$40 = ["<a class=\"k2b-chat-composer__attachment-link\"", " target=\"_blank\" rel=\"noopener noreferrer\"", ">", "", "</a>"],
|
|
50
|
+
_tmpl$41 = ["<button type=\"button\" class=\"k2b-chat-composer__attachment-action\"", ">", "", "</button>"],
|
|
51
|
+
_tmpl$42 = ["<footer class=\"k2b-chat-composer__footer\">", "</footer>"],
|
|
52
|
+
_tmpl$43 = ["<div class=\"k2b-chat-composer__tools\">", "", "", "", "", "", "", "</div>"],
|
|
53
|
+
_tmpl$44 = ["<div class=\"k2b-chat-composer__submit\">", "", "", "", "", "</div>"],
|
|
54
|
+
_tmpl$45 = ["<span>", "</span>"],
|
|
55
|
+
_tmpl$46 = ["<div class=\"k2b-chat-composer__status-line\"", "><i aria-hidden=\"true\"", "></i><span>", "</span></div>"],
|
|
56
|
+
_tmpl$47 = ["<span class=\"k2b-sr-only\">", "</span>"];
|
|
57
|
+
import { batch, children, createEffect, createMemo, createSignal, createUniqueId, For, Index, on, onCleanup, onMount, Show, untrack } from "solid-js";
|
|
56
58
|
import { Dropdown } from "../actions/Dropdown";
|
|
57
59
|
import { dialogHistorySettled, holdDialogHistory } from "../feedback/dialog-core";
|
|
58
60
|
import { Tooltip } from "../feedback/Tooltip";
|
|
@@ -83,6 +85,13 @@ const composerUndoBudgetChars = 1_000_000;
|
|
|
83
85
|
* dialog: `mention` takes the token's place exactly as a `mention` row does, and Ctrl/⌘+Z brings the token back.
|
|
84
86
|
*/
|
|
85
87
|
|
|
88
|
+
/** The draft as a successful `open` leaves it: without the `/…` token, with its references moved along. Save this one. */
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* How an `open` row ends: on success (`void`) the token leaves as for an action; `"cancelled"` changes nothing; `failed`
|
|
92
|
+
* keeps the list open and the draft unchanged, and its text replaces the row's description until the row runs again.
|
|
93
|
+
*/
|
|
94
|
+
|
|
86
95
|
/** A fixed place in the slash list. Sections keep the given order, also while searching. */
|
|
87
96
|
|
|
88
97
|
/** First-word narrowing; Tab writes `words[0]` followed by a space. */
|
|
@@ -171,6 +180,17 @@ export function ChatComposer(props) {
|
|
|
171
180
|
return `${commandListId}-c${commandIds.get(key)}`;
|
|
172
181
|
};
|
|
173
182
|
const [closedAt, setClosedAt] = createSignal();
|
|
183
|
+
/** The list opened from "+": the field's selection when "+" was pressed, and the search field's text. */
|
|
184
|
+
const [addSearch, setAddSearch] = createSignal();
|
|
185
|
+
const [addQuery, setAddQuery] = createSignal("");
|
|
186
|
+
let addSearchRef;
|
|
187
|
+
let addButtonRef;
|
|
188
|
+
let addComposing = false;
|
|
189
|
+
/** The `open` row running now, and why `open` rows failed in this opening, by row key. */
|
|
190
|
+
const [openingRow, setOpeningRow] = createSignal();
|
|
191
|
+
const [openFailures, setOpenFailures] = createSignal({});
|
|
192
|
+
/** Cancels the `open` row running now; `key` follows announced draft moves, `label` is its busy line. */
|
|
193
|
+
let openingRun;
|
|
174
194
|
// Where the pending native edit starts; a selection replaced as a whole starts at its beginning.
|
|
175
195
|
let inputStart;
|
|
176
196
|
let inputEnd;
|
|
@@ -275,7 +295,17 @@ export function ChatComposer(props) {
|
|
|
275
295
|
return token && index ? suggestEmoji(index, token.query, emojiSuggestionLimit) : [];
|
|
276
296
|
});
|
|
277
297
|
const emojiOpen = () => emojiMatches().length > 0;
|
|
278
|
-
|
|
298
|
+
// From "+", the search field's text is the query of an empty token at the field's stored caret.
|
|
299
|
+
const caretCommandToken = createMemo(() => {
|
|
300
|
+
const search = addSearch();
|
|
301
|
+
if (!search) return chatCommandQuery(props.value, caret(), selectionEnd());
|
|
302
|
+
const at = Math.min(search.end, props.value.length);
|
|
303
|
+
return {
|
|
304
|
+
start: at,
|
|
305
|
+
end: at,
|
|
306
|
+
query: addQuery()
|
|
307
|
+
};
|
|
308
|
+
});
|
|
279
309
|
const caretCommandStart = createMemo(() => caretCommandToken()?.start);
|
|
280
310
|
// Retained server rows belong to one `/`: they go when it does or when another `/` starts.
|
|
281
311
|
createEffect(() => {
|
|
@@ -290,6 +320,7 @@ export function ChatComposer(props) {
|
|
|
290
320
|
});
|
|
291
321
|
const commandQuery = createMemo(() => {
|
|
292
322
|
const token = caretCommandToken();
|
|
323
|
+
if (addSearch()) return token;
|
|
293
324
|
return dismissed() || composing() || token?.start === closedAt() ? null : token;
|
|
294
325
|
});
|
|
295
326
|
const openingStart = createMemo(() => commandQuery()?.start);
|
|
@@ -298,6 +329,12 @@ export function ChatComposer(props) {
|
|
|
298
329
|
const start = openingStart();
|
|
299
330
|
if (start !== undefined) setCommandOpening(opening => opening + 1);
|
|
300
331
|
});
|
|
332
|
+
// A failed `open` row says so for the rest of its list opening.
|
|
333
|
+
createEffect(() => {
|
|
334
|
+
props.draftKey;
|
|
335
|
+
commandOpening();
|
|
336
|
+
setOpenFailures({});
|
|
337
|
+
});
|
|
301
338
|
const parsedCommandQuery = createMemo(() => parseChatCommandQuery(commandQuery()?.query ?? "", props.commandScopes ?? []));
|
|
302
339
|
const commandFallbackRow = createMemo(() => {
|
|
303
340
|
const parsed = parsedCommandQuery();
|
|
@@ -313,7 +350,23 @@ export function ChatComposer(props) {
|
|
|
313
350
|
setSearchResults([]);
|
|
314
351
|
setSearchResultsQuery(undefined);
|
|
315
352
|
});
|
|
316
|
-
|
|
353
|
+
// Rows of a `fixedWhileOpen` section are the ones it had when this `/` opened the list; later ones wait for the next
|
|
354
|
+
// `/`. A retry row starts another opening in the same open list, so it does not count.
|
|
355
|
+
const fixedCommandKeys = createMemo(() => {
|
|
356
|
+
props.draftKey;
|
|
357
|
+
if (openingStart() === undefined) return undefined;
|
|
358
|
+
return untrack(() => {
|
|
359
|
+
const fixed = new Set((props.commandSections ?? []).filter(section => section.fixedWhileOpen).map(section => section.id));
|
|
360
|
+
return fixed.size ? new Set((props.commands ?? []).filter(command => fixed.has(command.section ?? "")).map(chatCommandKey)) : undefined;
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
const localCommands = createMemo(() => {
|
|
364
|
+
const commands = props.commands ?? [];
|
|
365
|
+
const keys = fixedCommandKeys();
|
|
366
|
+
if (!keys) return commands;
|
|
367
|
+
const fixed = new Set((props.commandSections ?? []).filter(section => section.fixedWhileOpen).map(section => section.id));
|
|
368
|
+
return commands.filter(command => !fixed.has(command.section ?? "") || keys.has(chatCommandKey(command)));
|
|
369
|
+
});
|
|
317
370
|
const commandModel = createMemo(() => chatCommandListModel({
|
|
318
371
|
query: commandQuery()?.query ?? "",
|
|
319
372
|
scopes: props.commandScopes,
|
|
@@ -337,12 +390,86 @@ export function ChatComposer(props) {
|
|
|
337
390
|
setCommandMoved(false);
|
|
338
391
|
});
|
|
339
392
|
const commandsOpen = () => Boolean(!emojiOpen() && commandQuery() && (localCommands().length || props.searchCommands || commandFallbackRow()));
|
|
340
|
-
|
|
393
|
+
// Rows that arrive for a pending section never move a visible row of the overview: until the person types, the list
|
|
394
|
+
// keeps the height it had with its placeholders and scrolls what does not fit. A list below its maximum height grows
|
|
395
|
+
// upward from the field, so a change of height alone would move its first row. While searching, the rows arrive
|
|
396
|
+
// with the search's own answer, which changes the list anyway.
|
|
397
|
+
// From "+", the whole panel keeps the height it opened with instead.
|
|
398
|
+
const commandsPending = createMemo(() => commandsOpen() && !addSearch() && !parsedCommandQuery().scope && !parsedCommandQuery().query.trim() && commandModel().groups.some(group => group.pendingRows > 0));
|
|
399
|
+
// Typing, another opening or closing the list lets it size itself again.
|
|
400
|
+
const commandListSizeKey = createMemo(() => commandsOpen() ? `${commandOpening()}\u0000${caretCommandToken()?.query}` : undefined);
|
|
401
|
+
// The list's height with its placeholders, for the size key it was measured under.
|
|
402
|
+
let pendingList;
|
|
403
|
+
createEffect(() => {
|
|
404
|
+
const list = commandListRef;
|
|
405
|
+
if (!commandsPending() || !list || typeof ResizeObserver === "undefined") return;
|
|
406
|
+
const measure = () => {
|
|
407
|
+
pendingList = {
|
|
408
|
+
height: list.getBoundingClientRect().height,
|
|
409
|
+
key: untrack(commandListSizeKey)
|
|
410
|
+
};
|
|
411
|
+
};
|
|
412
|
+
measure();
|
|
413
|
+
const observer = new ResizeObserver(measure);
|
|
414
|
+
observer.observe(list);
|
|
415
|
+
onCleanup(() => observer.disconnect());
|
|
416
|
+
});
|
|
417
|
+
createEffect(on(commandsPending, (pending, wasPending) => {
|
|
418
|
+
const held = pendingList;
|
|
419
|
+
pendingList = undefined;
|
|
420
|
+
// Only rows arriving hold the list; the person's typing or closing it does not.
|
|
421
|
+
if (pending || !wasPending || !held || held.key !== commandListSizeKey() || !commandListRef?.isConnected) return;
|
|
422
|
+
commandListRef.style.height = `${held.height}px`;
|
|
423
|
+
commandListRef.setAttribute("data-held", "");
|
|
424
|
+
}));
|
|
425
|
+
createEffect(on(commandListSizeKey, () => {
|
|
426
|
+
commandListRef?.style.removeProperty("height");
|
|
427
|
+
commandListRef?.removeAttribute("data-held");
|
|
428
|
+
}, {
|
|
429
|
+
defer: true
|
|
430
|
+
}));
|
|
431
|
+
// Escape, a press outside, or moving the caret away closes the list; while an `open` row runs, that cancels it, so
|
|
432
|
+
// nothing opens after the person backed out. Success ends the opening and closes the list in one step. A new draft
|
|
433
|
+
// cancels too, unless the opener announced that next move with `follow()`: its busy line moves to the status line,
|
|
434
|
+
// and Escape still cancels there. Any later unannounced draft change cancels.
|
|
435
|
+
createEffect(() => {
|
|
436
|
+
const draftKey = props.draftKey;
|
|
437
|
+
const open = commandsOpen();
|
|
438
|
+
const row = openingRow();
|
|
439
|
+
const run = openingRun;
|
|
440
|
+
if (!run || row === undefined) return;
|
|
441
|
+
if (draftKey !== run.key) {
|
|
442
|
+
if (run.follow) {
|
|
443
|
+
run.follow = false;
|
|
444
|
+
run.key = draftKey;
|
|
445
|
+
run.moved = true;
|
|
446
|
+
showCommandStatus({
|
|
447
|
+
tone: "busy",
|
|
448
|
+
text: run.label
|
|
449
|
+
});
|
|
450
|
+
} else run.controller.abort();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (!open && !run.moved) run.controller.abort();
|
|
454
|
+
});
|
|
455
|
+
onCleanup(() => openingRun?.controller.abort());
|
|
456
|
+
// An `open` row gives the sheet's Back entry up first, so it never races the navigation it starts.
|
|
457
|
+
const commandSheetHeld = createMemo(() => commandsOpen() && isTouchOnly() && openingRow() === undefined);
|
|
341
458
|
createEffect(() => {
|
|
342
459
|
if (!commandSheetHeld()) return;
|
|
343
|
-
onCleanup(holdDialogHistory(() =>
|
|
460
|
+
onCleanup(holdDialogHistory(() => closeCommandList()));
|
|
344
461
|
});
|
|
345
462
|
const suggestionsOpen = () => commandsOpen() || emojiOpen();
|
|
463
|
+
const fieldOwnsList = () => suggestionsOpen() && !addSearch();
|
|
464
|
+
// From "+", the panel keeps the height it opened with while the person types or rows arrive, so neither the search
|
|
465
|
+
// field nor the first row moves; what does not fit scrolls. The space above the field still bounds it.
|
|
466
|
+
const addPanelOpen = createMemo(() => addSearch() !== undefined && commandsOpen());
|
|
467
|
+
createEffect(() => {
|
|
468
|
+
const accessory = addPanelOpen() ? commandListRef?.parentElement : undefined;
|
|
469
|
+
if (!accessory) return;
|
|
470
|
+
const frame = requestAnimationFrame(() => accessory.style.setProperty("--k2b-chat-command-held", `${accessory.getBoundingClientRect().height}px`));
|
|
471
|
+
onCleanup(() => cancelAnimationFrame(frame));
|
|
472
|
+
});
|
|
346
473
|
createEffect(() => {
|
|
347
474
|
if (!suggestionsOpen()) return;
|
|
348
475
|
let disposed = false;
|
|
@@ -370,10 +497,13 @@ export function ChatComposer(props) {
|
|
|
370
497
|
const left = Math.max(0, column?.left ?? 0);
|
|
371
498
|
const right = Math.min(page, column?.right ?? page);
|
|
372
499
|
const slot = accessory.parentElement.getBoundingClientRect();
|
|
373
|
-
|
|
374
|
-
|
|
500
|
+
// From its place in the slot, not its box: the sheet opened from "+" rises into place, and a box measured during
|
|
501
|
+
// that animation would include its offset.
|
|
502
|
+
const bottom = slot.bottom - (Number.parseFloat(window.getComputedStyle(accessory).bottom) || 0);
|
|
503
|
+
// On the accessory, so a panel opened from "+" is bounded as a whole, with its search field.
|
|
504
|
+
accessory.style.setProperty("--k2b-chat-command-space", `${Math.max(40, bottom - top - 8)}px`);
|
|
375
505
|
// Only a list that reaches the screen's top edge keeps clear of a notch or the status bar.
|
|
376
|
-
|
|
506
|
+
accessory.style.setProperty("--k2b-chat-command-safe-top", top === viewportTop ? "env(safe-area-inset-top, 0px)" : "0px");
|
|
377
507
|
accessory.style.setProperty("--k2b-chat-command-reach-left", `${Math.max(0, slot.left - left)}px`);
|
|
378
508
|
accessory.style.setProperty("--k2b-chat-command-reach-right", `${Math.max(0, right - slot.right)}px`);
|
|
379
509
|
};
|
|
@@ -391,11 +521,29 @@ export function ChatComposer(props) {
|
|
|
391
521
|
// A press outside the command list and the field closes the list, as Escape does; the text stays. With a mouse or a
|
|
392
522
|
// trackpad the press then acts as usual. On a touch-only device a tap outside the composer closes only the list: it
|
|
393
523
|
// reaches nothing beneath, and the field keeps focus, so the keyboard stays open.
|
|
524
|
+
// A list opened from "+" is a menu: any press outside it closes only the list, with every pointer, and the field gets
|
|
525
|
+
// its focus back. "+" itself toggles with its click; a press in the field closes the list and lands there as usual.
|
|
394
526
|
createEffect(() => {
|
|
395
527
|
if (!commandsOpen()) return;
|
|
396
528
|
const onPointerDown = event => {
|
|
397
529
|
const target = event.target;
|
|
398
|
-
if (!(target instanceof Node) || commandListRef?.
|
|
530
|
+
if (!(target instanceof Node) || commandListRef?.parentElement?.contains(target)) return;
|
|
531
|
+
if (addSearch()) {
|
|
532
|
+
if (addButtonRef?.contains(target) && !addButtonRef.disabled) return;
|
|
533
|
+
if (textareaRef?.parentElement?.contains(target)) {
|
|
534
|
+
// The field takes the focus before the search field goes, so a phone keeps its keyboard; the press itself
|
|
535
|
+
// then places the caret.
|
|
536
|
+
textareaRef.focus({
|
|
537
|
+
preventScroll: true
|
|
538
|
+
});
|
|
539
|
+
endAddSearch(false);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
swallowTap(event);
|
|
543
|
+
endAddSearch(true);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (textareaRef?.parentElement?.contains(target)) return;
|
|
399
547
|
setClosedAt(commandQuery()?.start);
|
|
400
548
|
if (isTouchOnly() && !shellRef?.contains(target)) swallowTap(event);
|
|
401
549
|
};
|
|
@@ -445,9 +593,10 @@ export function ChatComposer(props) {
|
|
|
445
593
|
});
|
|
446
594
|
});
|
|
447
595
|
let settledEmpty;
|
|
596
|
+
// A space after "Nothing found" ends the `/…` as plain text; a search field opened from "+" just keeps its text.
|
|
448
597
|
createEffect(() => {
|
|
449
598
|
const token = commandQuery();
|
|
450
|
-
if (!token || parsedCommandQuery().scope || searchError()) {
|
|
599
|
+
if (!token || addSearch() || parsedCommandQuery().scope || searchError()) {
|
|
451
600
|
settledEmpty = undefined;
|
|
452
601
|
return;
|
|
453
602
|
}
|
|
@@ -498,6 +647,7 @@ export function ChatComposer(props) {
|
|
|
498
647
|
history.length = 0;
|
|
499
648
|
historyIndex = -1;
|
|
500
649
|
finishModelSelection(null);
|
|
650
|
+
endAddSearch(false);
|
|
501
651
|
setDismissed(true);
|
|
502
652
|
setClosedAt(undefined);
|
|
503
653
|
setSearchResults([]);
|
|
@@ -546,6 +696,8 @@ export function ChatComposer(props) {
|
|
|
546
696
|
setDismissed(false);
|
|
547
697
|
};
|
|
548
698
|
const restoreHistory = direction => {
|
|
699
|
+
// A field that only stays enabled to keep focus, while an `open` row runs, is not edited by undo either.
|
|
700
|
+
if (editBlocked()) return;
|
|
549
701
|
record();
|
|
550
702
|
const next = history[historyIndex + direction];
|
|
551
703
|
if (!next) return;
|
|
@@ -564,6 +716,45 @@ export function ChatComposer(props) {
|
|
|
564
716
|
setSelectionEnd(textareaRef?.selectionEnd ?? props.value.length);
|
|
565
717
|
});
|
|
566
718
|
};
|
|
719
|
+
/** Closes the list opened from "+"; `refocus` gives the field its focus and the selection it had when "+" was pressed. */
|
|
720
|
+
const endAddSearch = refocus => {
|
|
721
|
+
const search = addSearch();
|
|
722
|
+
if (!search) return;
|
|
723
|
+
addComposing = false;
|
|
724
|
+
batch(() => {
|
|
725
|
+
setAddSearch(undefined);
|
|
726
|
+
setAddQuery("");
|
|
727
|
+
});
|
|
728
|
+
if (!refocus || !textareaRef) return;
|
|
729
|
+
const length = textareaRef.value.length;
|
|
730
|
+
textareaRef.focus();
|
|
731
|
+
textareaRef.setSelectionRange(Math.min(search.start, length), Math.min(search.end, length));
|
|
732
|
+
syncCaret();
|
|
733
|
+
};
|
|
734
|
+
/** Escape, Back, a press outside: the list closes and the text stays. */
|
|
735
|
+
const closeCommandList = (refocus = true) => {
|
|
736
|
+
if (addSearch()) endAddSearch(refocus);else setClosedAt(commandQuery()?.start);
|
|
737
|
+
};
|
|
738
|
+
const toggleAddSearch = () => {
|
|
739
|
+
if (addSearch()) {
|
|
740
|
+
endAddSearch(true);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (blocked() || !textareaRef) return;
|
|
744
|
+
const start = textareaRef.selectionStart;
|
|
745
|
+
const end = textareaRef.selectionEnd;
|
|
746
|
+
batch(() => {
|
|
747
|
+
// Emoji suggestions for a `:shortcode` at the caret give way; they would hold the list closed.
|
|
748
|
+
setDismissed(true);
|
|
749
|
+
setAddQuery("");
|
|
750
|
+
setAddSearch({
|
|
751
|
+
start,
|
|
752
|
+
end
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
// Within the press, so a phone opens its keyboard for the search field.
|
|
756
|
+
addSearchRef?.focus();
|
|
757
|
+
};
|
|
567
758
|
const selectedCommand = createMemo(() => {
|
|
568
759
|
const rows = commandMatches();
|
|
569
760
|
return commandMoved() && rows.find(command => chatCommandKey(command) === activeCommandKey()) || rows[0];
|
|
@@ -587,6 +778,8 @@ export function ChatComposer(props) {
|
|
|
587
778
|
const canSubmit = () => !submitting() && submittable();
|
|
588
779
|
const canSelectFiles = () => Boolean(props.fileSelection && !props.fileSelection.disabled && !running() && !blocked());
|
|
589
780
|
const hasAddMenu = () => Boolean(props.fileSelection || props.menuActions?.length);
|
|
781
|
+
/** "+" opens the slash list, as long as there is one; otherwise it stays the add menu. */
|
|
782
|
+
const addOpensCommands = () => Boolean(props.addOpensCommands && (props.commands?.length || props.searchCommands));
|
|
590
783
|
const hasContextUsage = () => {
|
|
591
784
|
if (props.contextPopupAction) return true;
|
|
592
785
|
const context = props.contextUsage;
|
|
@@ -723,7 +916,7 @@ export function ChatComposer(props) {
|
|
|
723
916
|
* key or click that chose the row, because the device's file dialog needs that activation. Closing the selection
|
|
724
917
|
* without a file brings the token back, unless the draft changed meanwhile.
|
|
725
918
|
*/
|
|
726
|
-
const attachFilesFromCommand = token => {
|
|
919
|
+
const attachFilesFromCommand = (token, selection) => {
|
|
727
920
|
if (!canSelectFiles()) return;
|
|
728
921
|
const key = props.draftKey;
|
|
729
922
|
const previous = snapshot();
|
|
@@ -742,7 +935,7 @@ export function ChatComposer(props) {
|
|
|
742
935
|
history.splice(0, history.length, ...previousHistory.entries);
|
|
743
936
|
historyIndex = previousHistory.index;
|
|
744
937
|
queueMicrotask(() => {
|
|
745
|
-
textareaRef?.setSelectionRange(token.end, token.end);
|
|
938
|
+
textareaRef?.setSelectionRange(selection?.start ?? token.end, selection?.end ?? token.end);
|
|
746
939
|
syncCaret();
|
|
747
940
|
focus();
|
|
748
941
|
});
|
|
@@ -774,7 +967,7 @@ export function ChatComposer(props) {
|
|
|
774
967
|
setSubmitting(true);
|
|
775
968
|
try {
|
|
776
969
|
if (isTouchOnly()) {
|
|
777
|
-
if (commandSheetHeld())
|
|
970
|
+
if (commandSheetHeld()) closeCommandList(false);
|
|
778
971
|
// The sheet's Back entry leaves first, so a submit handler that navigates finds the history settled.
|
|
779
972
|
await dialogHistorySettled();
|
|
780
973
|
// A draft that another one replaced, or that may no longer go out, is not sent and keeps its text.
|
|
@@ -826,6 +1019,10 @@ export function ChatComposer(props) {
|
|
|
826
1019
|
const word = scope?.words[0];
|
|
827
1020
|
const token = commandQuery();
|
|
828
1021
|
if (!word || !token || !textareaRef) return false;
|
|
1022
|
+
if (addSearch()) {
|
|
1023
|
+
setAddQuery(`${word} `);
|
|
1024
|
+
return true;
|
|
1025
|
+
}
|
|
829
1026
|
replaceTextareaRange(textareaRef, token.start + 1, token.end, `${word} `);
|
|
830
1027
|
syncCaret();
|
|
831
1028
|
return true;
|
|
@@ -833,6 +1030,8 @@ export function ChatComposer(props) {
|
|
|
833
1030
|
const executeCommand = async (command, tab = false) => {
|
|
834
1031
|
const token = commandQuery();
|
|
835
1032
|
const heldHistory = commandSheetHeld();
|
|
1033
|
+
const addSelection = addSearch();
|
|
1034
|
+
const fromAdd = addSelection !== undefined;
|
|
836
1035
|
if (!token || blocked()) return;
|
|
837
1036
|
if (command.disabled || command.reason) {
|
|
838
1037
|
// A new object each time, so choosing the same row again inserts and announces its reason again.
|
|
@@ -848,19 +1047,33 @@ export function ChatComposer(props) {
|
|
|
848
1047
|
}
|
|
849
1048
|
if (command.scope && (tab || !command.action && !command.mention && !command.attachFiles) && narrowCommand(command)) return;
|
|
850
1049
|
if (command.attachFiles) {
|
|
851
|
-
|
|
1050
|
+
if (!canSelectFiles()) return;
|
|
1051
|
+
// The field has the focus before the selection opens, so closing it without a file comes back there.
|
|
1052
|
+
endAddSearch(true);
|
|
1053
|
+
attachFilesFromCommand(token, addSelection);
|
|
852
1054
|
return;
|
|
853
1055
|
}
|
|
1056
|
+
if (command.open) {
|
|
1057
|
+
await openFromCommand(command, command.open, token);
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
// The field has the focus again before an action opens a dialog, so the dialog gives it back there.
|
|
1061
|
+
endAddSearch(true);
|
|
1062
|
+
// From "+" a reference joins the text at the caret, apart from a word right before it.
|
|
1063
|
+
const lead = value => fromAdd && token.start > 0 && !/\s/.test(value[token.start - 1] ?? "") ? " " : "";
|
|
854
1064
|
const previous = snapshot();
|
|
855
1065
|
const key = props.draftKey;
|
|
856
|
-
const replacement = command.mention ? command.mention.name + " " : "";
|
|
1066
|
+
const replacement = command.mention ? lead(props.value) + command.mention.name + " " : "";
|
|
857
1067
|
const nextValue = props.value.slice(0, token.start) + replacement + props.value.slice(token.end);
|
|
858
1068
|
const nextMentions = reconcileChatMentions(props.value, nextValue, mentions());
|
|
859
|
-
if (command.mention)
|
|
860
|
-
start
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1069
|
+
if (command.mention) {
|
|
1070
|
+
const start = token.start + lead(props.value).length;
|
|
1071
|
+
nextMentions.push({
|
|
1072
|
+
start,
|
|
1073
|
+
end: start + command.mention.name.length,
|
|
1074
|
+
attachment: command.mention
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
864
1077
|
// A cancelled or failed command leaves no undo step behind: restoring puts the history back as well.
|
|
865
1078
|
const previousHistory = {
|
|
866
1079
|
entries: [...history],
|
|
@@ -910,16 +1123,17 @@ export function ChatComposer(props) {
|
|
|
910
1123
|
restore();
|
|
911
1124
|
restored = false;
|
|
912
1125
|
const name = result.mention.name;
|
|
913
|
-
const
|
|
1126
|
+
const start = token.start + lead(previous.value).length;
|
|
1127
|
+
const withMention = previous.value.slice(0, token.start) + lead(previous.value) + name + " " + previous.value.slice(token.end);
|
|
914
1128
|
edit(withMention, [...reconcileChatMentions(previous.value, withMention, previous.mentions), {
|
|
915
|
-
start
|
|
916
|
-
end:
|
|
1129
|
+
start,
|
|
1130
|
+
end: start + name.length,
|
|
917
1131
|
attachment: result.mention
|
|
918
1132
|
}]);
|
|
919
1133
|
setDismissed(true);
|
|
920
1134
|
mentioned = {
|
|
921
1135
|
value: withMention,
|
|
922
|
-
caret:
|
|
1136
|
+
caret: start + name.length + 1
|
|
923
1137
|
};
|
|
924
1138
|
showCommandStatus();
|
|
925
1139
|
} else {
|
|
@@ -953,7 +1167,7 @@ export function ChatComposer(props) {
|
|
|
953
1167
|
if (props.draftKey !== key) return;
|
|
954
1168
|
if (restored || props.value === nextValue || mentioned && props.value === mentioned.value) {
|
|
955
1169
|
const position = restored ? token.end : mentioned?.caret ?? token.start + replacement.length;
|
|
956
|
-
textareaRef?.setSelectionRange(position, position);
|
|
1170
|
+
textareaRef?.setSelectionRange(restored && addSelection ? addSelection.start : position, restored && addSelection ? addSelection.end : position);
|
|
957
1171
|
}
|
|
958
1172
|
syncCaret();
|
|
959
1173
|
autoResize();
|
|
@@ -961,6 +1175,110 @@ export function ChatComposer(props) {
|
|
|
961
1175
|
});
|
|
962
1176
|
};
|
|
963
1177
|
|
|
1178
|
+
/**
|
|
1179
|
+
* Runs an `open` row with the token and the list in place. Success removes the token in one undo step; `failed`
|
|
1180
|
+
* stays in the row while the list is still there, otherwise the status line says it, so it never ends in silence.
|
|
1181
|
+
*/
|
|
1182
|
+
const openFromCommand = async (command, open, token) => {
|
|
1183
|
+
const key = props.draftKey;
|
|
1184
|
+
const row = chatCommandKey(command);
|
|
1185
|
+
const value = props.value;
|
|
1186
|
+
const nextValue = value.slice(0, token.start) + value.slice(token.end);
|
|
1187
|
+
const nextMentions = reconcileChatMentions(value, nextValue, mentions());
|
|
1188
|
+
// Still the same draft and `/…`, so the row is where the person chose it.
|
|
1189
|
+
const inPlace = () => !disposed && props.draftKey === key && props.value === value && commandQuery()?.start === token.start;
|
|
1190
|
+
const heldHistory = commandSheetHeld();
|
|
1191
|
+
// Closing the list or leaving the composer cancels the opening; the row ends at once, without waiting for it.
|
|
1192
|
+
const controller = new AbortController();
|
|
1193
|
+
const run = {
|
|
1194
|
+
controller,
|
|
1195
|
+
key,
|
|
1196
|
+
label: command.busyLabel ?? chatMessages().commandOpening,
|
|
1197
|
+
follow: false,
|
|
1198
|
+
moved: false
|
|
1199
|
+
};
|
|
1200
|
+
openingRun = run;
|
|
1201
|
+
const cancelled = new Promise(resolve => controller.signal.addEventListener("abort", () => resolve("cancelled"), {
|
|
1202
|
+
once: true
|
|
1203
|
+
}));
|
|
1204
|
+
setOpenFailures(({
|
|
1205
|
+
[row]: _,
|
|
1206
|
+
...rest
|
|
1207
|
+
}) => rest);
|
|
1208
|
+
setOpeningRow(row);
|
|
1209
|
+
setExecuting(true);
|
|
1210
|
+
// An opening that ends without leaving, by failure, rejection or cancelling, also aborts `signal`, so the opener
|
|
1211
|
+
// can put back what it saved without the token.
|
|
1212
|
+
const finish = (left = false) => {
|
|
1213
|
+
if (!left) controller.abort();
|
|
1214
|
+
if (openingRun === run) openingRun = undefined;
|
|
1215
|
+
if (commandStatus()?.tone === "busy") showCommandStatus();
|
|
1216
|
+
setOpeningRow(undefined);
|
|
1217
|
+
setExecuting(false);
|
|
1218
|
+
};
|
|
1219
|
+
let result;
|
|
1220
|
+
try {
|
|
1221
|
+
if (heldHistory) {
|
|
1222
|
+
await dialogHistorySettled();
|
|
1223
|
+
if (!inPlace()) {
|
|
1224
|
+
finish();
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
result = await Promise.race([open({
|
|
1229
|
+
value: nextValue,
|
|
1230
|
+
mentions: nextMentions
|
|
1231
|
+
}, controller.signal, () => {
|
|
1232
|
+
run.follow = true;
|
|
1233
|
+
}), cancelled]);
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
finish();
|
|
1236
|
+
if (disposed) {
|
|
1237
|
+
props.onError?.(error);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (inPlace()) closeCommandList();
|
|
1241
|
+
showCommandStatus({
|
|
1242
|
+
tone: "error",
|
|
1243
|
+
text: error instanceof Error && error.message ? error.message : chatMessages().commandFailed
|
|
1244
|
+
});
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
if (!result) {
|
|
1248
|
+
// Success: the token leaves and the list closes in the same step that ends the opening, so a phone sheet never
|
|
1249
|
+
// takes its Back entry again while the page navigates.
|
|
1250
|
+
batch(() => {
|
|
1251
|
+
if (!disposed && props.draftKey === key && props.value === value) {
|
|
1252
|
+
edit(nextValue, nextMentions);
|
|
1253
|
+
setDismissed(true);
|
|
1254
|
+
}
|
|
1255
|
+
endAddSearch(!disposed && props.draftKey === key);
|
|
1256
|
+
finish(true);
|
|
1257
|
+
});
|
|
1258
|
+
queueMicrotask(() => {
|
|
1259
|
+
if (disposed || props.draftKey !== key || props.value !== nextValue) return;
|
|
1260
|
+
textareaRef?.setSelectionRange(token.start, token.start);
|
|
1261
|
+
syncCaret();
|
|
1262
|
+
autoResize();
|
|
1263
|
+
});
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
finish();
|
|
1267
|
+
if (result === "cancelled") return;
|
|
1268
|
+
if (inPlace()) {
|
|
1269
|
+
setOpenFailures(failures => ({
|
|
1270
|
+
...failures,
|
|
1271
|
+
[row]: result.failed
|
|
1272
|
+
}));
|
|
1273
|
+
setAnnouncement({
|
|
1274
|
+
text: result.failed
|
|
1275
|
+
});
|
|
1276
|
+
} else if (disposed) props.onError?.(new Error(result.failed));else showCommandStatus({
|
|
1277
|
+
tone: "error",
|
|
1278
|
+
text: result.failed
|
|
1279
|
+
});
|
|
1280
|
+
};
|
|
1281
|
+
|
|
964
1282
|
/** Replaces `start`–`end` with the emoji through the editing path, so the browser's undo restores the shortcode. */
|
|
965
1283
|
const insertEmoji = (start, end, emoji) => {
|
|
966
1284
|
if (!textareaRef || editBlocked()) return;
|
|
@@ -1001,9 +1319,66 @@ export function ChatComposer(props) {
|
|
|
1001
1319
|
if (untouched && textarea.matches(":focus")) convert(loaded);
|
|
1002
1320
|
}, () => undefined);
|
|
1003
1321
|
};
|
|
1322
|
+
|
|
1323
|
+
/** Moving through and choosing in the command list, from the field or from the search field opened by "+". */
|
|
1324
|
+
const commandListKey = event => {
|
|
1325
|
+
if (!commandsOpen()) return false;
|
|
1326
|
+
const matches = commandMatches();
|
|
1327
|
+
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
1328
|
+
event.preventDefault();
|
|
1329
|
+
const index = matches.indexOf(selectedCommand());
|
|
1330
|
+
const next = matches[nextChatCommandIndex(index, matches.length, event.key === "ArrowUp" ? -1 : 1)];
|
|
1331
|
+
setActiveCommandKey(next ? chatCommandKey(next) : undefined);
|
|
1332
|
+
setCommandMoved(true);
|
|
1333
|
+
setAnnouncement(undefined);
|
|
1334
|
+
return true;
|
|
1335
|
+
}
|
|
1336
|
+
// Ctrl/⌘+Enter sends, also while suggestions are open.
|
|
1337
|
+
const plain = !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
1338
|
+
if (plain && (event.key === "Tab" || event.key === "Enter") && matches.length > 0) {
|
|
1339
|
+
event.preventDefault();
|
|
1340
|
+
const command = selectedCommand();
|
|
1341
|
+
// A server row answering an earlier query may no longer match; wait for fresh results. Local rows match now.
|
|
1342
|
+
const row = commandModel().rows.find(row => chatCommandKey(row.command) === selectedCommandKey());
|
|
1343
|
+
const staleServerRow = row?.server && (searchResultsQuery() !== commandQuery()?.query || searchResultsScope() !== parsedCommandQuery().scope?.id);
|
|
1344
|
+
if (staleServerRow) return true;
|
|
1345
|
+
if (command) void executeCommand(command, event.key === "Tab");
|
|
1346
|
+
return true;
|
|
1347
|
+
}
|
|
1348
|
+
if (event.key === "Escape") {
|
|
1349
|
+
event.preventDefault();
|
|
1350
|
+
closeCommandList();
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
return false;
|
|
1354
|
+
};
|
|
1355
|
+
const onAddSearchKeyDown = event => {
|
|
1356
|
+
// Nothing chooses while an input method composes, as in the field.
|
|
1357
|
+
if (event.isComposing || event.keyCode === 229 || addComposing) return;
|
|
1358
|
+
if (event.key === "Escape" && openingRun) {
|
|
1359
|
+
event.preventDefault();
|
|
1360
|
+
openingRun.controller.abort();
|
|
1361
|
+
endAddSearch(true);
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if (commandListKey(event)) return;
|
|
1365
|
+
// Ctrl/⌘+Enter sends from the search field as from the field, with the list closed first.
|
|
1366
|
+
if (event.key === "Enter" && (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
|
|
1367
|
+
event.preventDefault();
|
|
1368
|
+
endAddSearch(true);
|
|
1369
|
+
void submit();
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1004
1372
|
const onKeyDown = event => {
|
|
1005
1373
|
// WebKit ends the composition before the keydown of the Enter that confirms it; that keydown keeps keyCode 229.
|
|
1006
1374
|
if (event.isComposing || event.keyCode === 229 || composing()) return;
|
|
1375
|
+
// Escape cancels a running `open` row, also once its list is gone because the draft moved to a new chat.
|
|
1376
|
+
if (event.key === "Escape" && openingRun) {
|
|
1377
|
+
event.preventDefault();
|
|
1378
|
+
openingRun.controller.abort();
|
|
1379
|
+
if (commandsOpen()) closeCommandList();
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1007
1382
|
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "z") {
|
|
1008
1383
|
event.preventDefault();
|
|
1009
1384
|
restoreHistory(event.shiftKey ? 1 : -1);
|
|
@@ -1028,38 +1403,11 @@ export function ChatComposer(props) {
|
|
|
1028
1403
|
return;
|
|
1029
1404
|
}
|
|
1030
1405
|
}
|
|
1031
|
-
|
|
1032
|
-
if (commandsOpen()) {
|
|
1033
|
-
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
1034
|
-
event.preventDefault();
|
|
1035
|
-
const index = matches.indexOf(selectedCommand());
|
|
1036
|
-
const next = matches[nextChatCommandIndex(index, matches.length, event.key === "ArrowUp" ? -1 : 1)];
|
|
1037
|
-
setActiveCommandKey(next ? chatCommandKey(next) : undefined);
|
|
1038
|
-
setCommandMoved(true);
|
|
1039
|
-
setAnnouncement(undefined);
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
// Ctrl/⌘+Enter sends, also while suggestions are open.
|
|
1043
|
-
const plain = !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
1044
|
-
if (plain && (event.key === "Tab" || event.key === "Enter") && matches.length > 0) {
|
|
1045
|
-
event.preventDefault();
|
|
1046
|
-
const command = selectedCommand();
|
|
1047
|
-
// A server row answering an earlier query may no longer match; wait for fresh results. Local rows match now.
|
|
1048
|
-
const row = commandModel().rows.find(row => chatCommandKey(row.command) === selectedCommandKey());
|
|
1049
|
-
const staleServerRow = row?.server && (searchResultsQuery() !== commandQuery()?.query || searchResultsScope() !== parsedCommandQuery().scope?.id);
|
|
1050
|
-
if (staleServerRow) return;
|
|
1051
|
-
if (command) void executeCommand(command, event.key === "Tab");
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
if (event.key === "Escape") {
|
|
1055
|
-
event.preventDefault();
|
|
1056
|
-
setClosedAt(commandQuery()?.start);
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1406
|
+
if (commandListKey(event)) return;
|
|
1060
1407
|
const format = props.formatting && textareaRef ? formatShortcut(event) : undefined;
|
|
1061
1408
|
if (format && textareaRef) {
|
|
1062
1409
|
event.preventDefault();
|
|
1410
|
+
if (editBlocked()) return;
|
|
1063
1411
|
format.run(textareaRef);
|
|
1064
1412
|
return;
|
|
1065
1413
|
}
|
|
@@ -1182,8 +1530,46 @@ export function ChatComposer(props) {
|
|
|
1182
1530
|
}
|
|
1183
1531
|
});
|
|
1184
1532
|
|
|
1533
|
+
/** "+" as the way to the slash list on keyboards without a handy "/". */
|
|
1534
|
+
const addSearchButton = (extraClass = "") => _$createComponent(Tooltip.Trigger, {
|
|
1535
|
+
type: "button",
|
|
1536
|
+
"class": `k2b-chat-composer__icon-action${extraClass}`,
|
|
1537
|
+
get ["aria-label"]() {
|
|
1538
|
+
return chatMessages().addMenu;
|
|
1539
|
+
},
|
|
1540
|
+
"aria-haspopup": "listbox",
|
|
1541
|
+
get ["aria-expanded"]() {
|
|
1542
|
+
return addSearch() !== undefined;
|
|
1543
|
+
},
|
|
1544
|
+
get ["aria-controls"]() {
|
|
1545
|
+
return addSearch() ? commandListId : undefined;
|
|
1546
|
+
},
|
|
1547
|
+
get content() {
|
|
1548
|
+
return chatMessages().addMenu;
|
|
1549
|
+
},
|
|
1550
|
+
get disabled() {
|
|
1551
|
+
return blocked();
|
|
1552
|
+
},
|
|
1553
|
+
onMouseDown: keepFieldFocus,
|
|
1554
|
+
onClick: toggleAddSearch,
|
|
1555
|
+
get children() {
|
|
1556
|
+
return _$ssr(_tmpl$3, _$ssrAttribute("class", addingFiles() ? "ti ti-loader-2 k2b-spin" : "ti ti-plus", false));
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1185
1560
|
/** The conversation's attach button opens the file choice directly; other actions turn it into the add menu. */
|
|
1186
1561
|
const attachControl = () => _$createComponent(Show, {
|
|
1562
|
+
get when() {
|
|
1563
|
+
return !addOpensCommands();
|
|
1564
|
+
},
|
|
1565
|
+
get fallback() {
|
|
1566
|
+
return addSearchButton(" k2b-chat-composer__attach");
|
|
1567
|
+
},
|
|
1568
|
+
get children() {
|
|
1569
|
+
return attachChoice();
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
const attachChoice = () => _$createComponent(Show, {
|
|
1187
1573
|
get when() {
|
|
1188
1574
|
return props.menuActions?.length;
|
|
1189
1575
|
},
|
|
@@ -1467,7 +1853,7 @@ export function ChatComposer(props) {
|
|
|
1467
1853
|
children: part => _$ssr(_tmpl$10, Boolean(part.mention) ? "k2b-chat-composer__mention" : "", _$escape(part.text))
|
|
1468
1854
|
})));
|
|
1469
1855
|
}
|
|
1470
|
-
})), mentions().length > 0 ? "k2b-chat-composer__textarea--highlighted" : "", _$ssrAttribute("value", _$escape(props.value, true), false), _$ssrAttribute("disabled", editBlocked(), true) + _$ssrAttribute("enterkeyhint", conversation() ? "enter" : _$escape(undefined, true), false) + _$ssrAttribute("placeholder", _$escape(props.placeholder ?? (running() ? chatMessages().addGuidance : conversation() ? chatMessages().writeConversationMessage : chatMessages().writeMessage), true), false) + _$ssrAttribute("aria-label", _$escape(props.inputLabel ?? messages().message, true), false) + _$ssrAttribute("role",
|
|
1856
|
+
})), mentions().length > 0 ? "k2b-chat-composer__textarea--highlighted" : "", _$ssrAttribute("value", _$escape(props.value, true), false), _$ssrAttribute("disabled", editBlocked() && openingRow() === undefined, true) + _$ssrAttribute("readonly", _$escape(openingRow(), true) !== _$escape(undefined, true), false) + _$ssrAttribute("enterkeyhint", conversation() ? "enter" : _$escape(undefined, true), false) + _$ssrAttribute("placeholder", _$escape(props.placeholder ?? (running() ? chatMessages().addGuidance : conversation() ? chatMessages().writeConversationMessage : chatMessages().writeMessage), true), false) + _$ssrAttribute("aria-label", _$escape(props.inputLabel ?? messages().message, true), false) + _$ssrAttribute("role", fieldOwnsList() ? "combobox" : _$escape(undefined, true), false) + _$ssrAttribute("aria-autocomplete", fieldOwnsList() ? "list" : _$escape(undefined, true), false) + _$ssrAttribute("aria-controls", fieldOwnsList() ? _$escape(commandListId, true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-expanded", fieldOwnsList() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-activedescendant", !fieldOwnsList() ? _$escape(undefined, true) : emojiOpen() ? `${_$escape(commandListId, true)}-${_$escape(selectedCommandIndex(), true)}` : selectedCommand() ? _$escape(commandOptionId(selectedCommand()), true) : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
|
|
1471
1857
|
get when() {
|
|
1472
1858
|
return conversation();
|
|
1473
1859
|
},
|
|
@@ -1480,7 +1866,9 @@ export function ChatComposer(props) {
|
|
|
1480
1866
|
const renderCommandRow = command => {
|
|
1481
1867
|
const highlight = () => commandRowMap().get(command)?.highlight;
|
|
1482
1868
|
const title = () => command.label ?? command.name;
|
|
1483
|
-
|
|
1869
|
+
const opening = () => openingRow() === chatCommandKey(command);
|
|
1870
|
+
const failure = () => openFailures()[chatCommandKey(command)];
|
|
1871
|
+
return _$ssr(_tmpl$13, _$ssrAttribute("id", _$escape(commandOptionId(command), true), false), _$ssrAttribute("aria-selected", _$escape(selectedCommand(), true) === _$escape(command, true), false) + _$ssrAttribute("aria-disabled", _$escape(Boolean(command.disabled || command.reason), true), false) + _$ssrAttribute("aria-busy", opening() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-describedby", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false) + _$ssrAttribute("data-reason", command.reason ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-active", selectedCommand() === command ? "true" : _$escape(undefined, true), false), _$ssrAttribute("class", _$escape(command.icon ?? "ti ti-slash", true), false), _$escape(_$createComponent(Show, {
|
|
1484
1872
|
get when() {
|
|
1485
1873
|
return highlight();
|
|
1486
1874
|
},
|
|
@@ -1490,10 +1878,20 @@ export function ChatComposer(props) {
|
|
|
1490
1878
|
children: range => [title().slice(0, range()[0]), _$ssr(_tmpl$14, _$escape(title().slice(range()[0], range()[1]))), title().slice(range()[1])]
|
|
1491
1879
|
})), _$escape(_$createComponent(Show, {
|
|
1492
1880
|
get when() {
|
|
1493
|
-
return command.
|
|
1881
|
+
return _$memo(() => !!command.open)() && !command.reason;
|
|
1882
|
+
},
|
|
1883
|
+
get fallback() {
|
|
1884
|
+
return _$createComponent(Show, {
|
|
1885
|
+
get when() {
|
|
1886
|
+
return command.reason ?? command.description;
|
|
1887
|
+
},
|
|
1888
|
+
get children() {
|
|
1889
|
+
return _$ssr(_tmpl$15, _$ssrAttribute("id", command.reason ? `${_$escape(commandOptionId(command), true)}-reason` : _$escape(undefined, true), false), _$escape(command.reason ?? command.description));
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1494
1892
|
},
|
|
1495
1893
|
get children() {
|
|
1496
|
-
return _$ssr(_tmpl$11, _$ssrAttribute("
|
|
1894
|
+
return _$ssr(_tmpl$11, _$ssrAttribute("data-state", opening() ? "busy" : failure() ? "failed" : _$escape(undefined, true), false), _$ssrAttribute("aria-hidden", opening() ? "true" : _$escape(undefined, true), false), _$escape(failure() ?? command.description), _$ssrAttribute("aria-hidden", opening() ? _$escape(undefined, true) : "true", false), _$escape(command.busyLabel ?? chatMessages().commandOpening));
|
|
1497
1895
|
}
|
|
1498
1896
|
})), _$escape(_$createComponent(Show, {
|
|
1499
1897
|
get when() {
|
|
@@ -1504,25 +1902,25 @@ export function ChatComposer(props) {
|
|
|
1504
1902
|
}
|
|
1505
1903
|
})));
|
|
1506
1904
|
};
|
|
1507
|
-
return _$ssr(_tmpl$
|
|
1905
|
+
return _$ssr(_tmpl$27, _$escape(_$createComponent(Show, {
|
|
1508
1906
|
get when() {
|
|
1509
1907
|
return suggestionsOpen() || hasAccessory();
|
|
1510
1908
|
},
|
|
1511
1909
|
get children() {
|
|
1512
|
-
return _$ssr(_tmpl$
|
|
1910
|
+
return _$ssr(_tmpl$22, _$ssrAttribute("data-overlay", hasAccessory() ? _$escape(undefined, true) : "true", false), _$ssrStyleProperty("visibility:", suggestionsOpen() ? "hidden" : _$escape(undefined, true)), _$ssrAttribute("inert", suggestionsOpen(), true), _$escape(accessory()), _$escape(_$createComponent(Show, {
|
|
1513
1911
|
get when() {
|
|
1514
1912
|
return emojiOpen();
|
|
1515
1913
|
},
|
|
1516
1914
|
get children() {
|
|
1517
|
-
return _$ssr(_tmpl$
|
|
1915
|
+
return _$ssr(_tmpl$16, _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().emojiSuggestions, true), false), _$escape(_$createComponent(For, {
|
|
1518
1916
|
get each() {
|
|
1519
1917
|
return emojiMatches();
|
|
1520
1918
|
},
|
|
1521
|
-
children: (entry, index) => _$ssr(_tmpl$
|
|
1919
|
+
children: (entry, index) => _$ssr(_tmpl$28, `${_$escape(commandListId, true)}-${_$escape(index(), true)}`, _$ssrAttribute("aria-selected", _$escape(index(), true) === _$escape(selectedCommandIndex(), true), false) + _$ssrAttribute("aria-label", _$escape(emojiName(entry, locale()), true), false) + _$ssrAttribute("data-active", index() === selectedCommandIndex() ? "true" : _$escape(undefined, true), false), _$escape(emojiInTone(entry, props.emoji?.skinTone ?? 0)), _$escape(emojiName(entry, locale())), _$escape(_$createComponent(Show, {
|
|
1522
1920
|
get when() {
|
|
1523
1921
|
return entry.shortcodes[0];
|
|
1524
1922
|
},
|
|
1525
|
-
children: code => _$ssr(_tmpl$
|
|
1923
|
+
children: code => _$ssr(_tmpl$29, _$escape(code()))
|
|
1526
1924
|
})))
|
|
1527
1925
|
})));
|
|
1528
1926
|
}
|
|
@@ -1531,7 +1929,14 @@ export function ChatComposer(props) {
|
|
|
1531
1929
|
return commandsOpen();
|
|
1532
1930
|
},
|
|
1533
1931
|
get children() {
|
|
1534
|
-
return _$ssr(_tmpl$
|
|
1932
|
+
return _$ssr(_tmpl$21, _$ssrAttribute("data-search", addSearch() ? "" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
|
|
1933
|
+
get when() {
|
|
1934
|
+
return addSearch();
|
|
1935
|
+
},
|
|
1936
|
+
get children() {
|
|
1937
|
+
return _$ssr(_tmpl$17, _$ssrAttribute("value", _$escape(addQuery(), true), false) + _$ssrAttribute("placeholder", _$escape(chatMessages().commandSearch, true), false) + _$ssrAttribute("aria-label", _$escape(chatMessages().commandSearchLabel, true), false), _$ssrAttribute("aria-controls", _$escape(commandListId, true), false), _$ssrAttribute("aria-activedescendant", selectedCommand() ? _$escape(commandOptionId(selectedCommand()), true) : _$escape(undefined, true), false), _$ssrAttribute("readonly", _$escape(openingRow(), true) !== _$escape(undefined, true), false));
|
|
1938
|
+
}
|
|
1939
|
+
})), _$ssrAttribute("id", _$escape(commandListId, true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().commands, true), false) + _$ssrAttribute("aria-busy", searching() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(For, {
|
|
1535
1940
|
get each() {
|
|
1536
1941
|
return commandModel().groups.map(commandGroupKey);
|
|
1537
1942
|
},
|
|
@@ -1550,25 +1955,25 @@ export function ChatComposer(props) {
|
|
|
1550
1955
|
});
|
|
1551
1956
|
},
|
|
1552
1957
|
get children() {
|
|
1553
|
-
return _$ssr(_tmpl$
|
|
1958
|
+
return _$ssr(_tmpl$31, _$ssrAttribute("aria-labelledby", _$escape(headingId(), true), false), _$ssrAttribute("id", _$escape(headingId(), true), false), _$escape(group()?.scopeTitle ?? group()?.section?.label), _$escape(_$createComponent(Show, {
|
|
1554
1959
|
get when() {
|
|
1555
1960
|
return group()?.loading;
|
|
1556
1961
|
},
|
|
1557
1962
|
get children() {
|
|
1558
|
-
return _$ssr(_tmpl$
|
|
1963
|
+
return _$ssr(_tmpl$30);
|
|
1559
1964
|
}
|
|
1560
1965
|
})), _$escape(_$createComponent(For, {
|
|
1561
1966
|
get each() {
|
|
1562
1967
|
return rows();
|
|
1563
1968
|
},
|
|
1564
1969
|
children: renderCommandRow
|
|
1565
|
-
})), _$escape(_$createComponent(
|
|
1566
|
-
get
|
|
1567
|
-
return
|
|
1970
|
+
})), _$escape(_$createComponent(Index, {
|
|
1971
|
+
get each() {
|
|
1972
|
+
return Array.from({
|
|
1973
|
+
length: Math.max(group()?.placeholder ? 1 : 0, group()?.pendingRows ?? 0)
|
|
1974
|
+
});
|
|
1568
1975
|
},
|
|
1569
|
-
|
|
1570
|
-
return _$ssr(_tmpl$29);
|
|
1571
|
-
}
|
|
1976
|
+
children: () => _$ssr(_tmpl$32)
|
|
1572
1977
|
})));
|
|
1573
1978
|
}
|
|
1574
1979
|
});
|
|
@@ -1578,21 +1983,21 @@ export function ChatComposer(props) {
|
|
|
1578
1983
|
return _$memo(() => !!(searching() && foundCommandCount() === 0))() && !commandModel().groups.some(group => group.section || group.scopeTitle);
|
|
1579
1984
|
},
|
|
1580
1985
|
get children() {
|
|
1581
|
-
return _$ssr(_tmpl$
|
|
1986
|
+
return _$ssr(_tmpl$18, _$escape(messages().loading));
|
|
1582
1987
|
}
|
|
1583
1988
|
})), _$escape(_$createComponent(Show, {
|
|
1584
1989
|
get when() {
|
|
1585
1990
|
return searchError();
|
|
1586
1991
|
},
|
|
1587
1992
|
get children() {
|
|
1588
|
-
return _$ssr(_tmpl$
|
|
1993
|
+
return _$ssr(_tmpl$19, _$escape(messages().error));
|
|
1589
1994
|
}
|
|
1590
1995
|
})), _$escape(_$createComponent(Show, {
|
|
1591
1996
|
get when() {
|
|
1592
|
-
return _$memo(() => !!(!searching() && !searchError()))() &&
|
|
1997
|
+
return _$memo(() => !!(!searching() && !searchError() && foundCommandCount() === 0))() && !commandModel().groups.some(group => group.pendingRows);
|
|
1593
1998
|
},
|
|
1594
1999
|
get children() {
|
|
1595
|
-
return _$ssr(_tmpl$
|
|
2000
|
+
return _$ssr(_tmpl$20, !parsedCommandQuery().query.trim() ? _$escape(messages().noResults) : _$escape(chatMessages().commandsNothingFound({
|
|
1596
2001
|
query: parsedCommandQuery().query.trim()
|
|
1597
2002
|
})));
|
|
1598
2003
|
}
|
|
@@ -1611,18 +2016,18 @@ export function ChatComposer(props) {
|
|
|
1611
2016
|
return showHint();
|
|
1612
2017
|
},
|
|
1613
2018
|
get children() {
|
|
1614
|
-
return _$ssr(_tmpl$
|
|
2019
|
+
return _$ssr(_tmpl$23, _$escape(_$createComponent(Show, {
|
|
1615
2020
|
get when() {
|
|
1616
2021
|
return dictationStatus();
|
|
1617
2022
|
},
|
|
1618
2023
|
get fallback() {
|
|
1619
|
-
return _$ssr(_tmpl$
|
|
2024
|
+
return _$ssr(_tmpl$33, _$escape(hint()));
|
|
1620
2025
|
},
|
|
1621
|
-
children: status => [_$ssr(_tmpl$
|
|
2026
|
+
children: status => [_$ssr(_tmpl$34, _$ssrAttribute("data-state", _$escape(dictation() ?? undefined, true), false), _$ssrAttribute("class", _$escape(status().icon, true), false), _$escape(status().text)), _$createComponent(Show, {
|
|
1622
2027
|
get when() {
|
|
1623
2028
|
return _$memo(() => dictation() === "refined")() ? props.microphone?.onRestoreOriginal : undefined;
|
|
1624
2029
|
},
|
|
1625
|
-
children: restore => _$ssr(_tmpl$
|
|
2030
|
+
children: restore => _$ssr(_tmpl$35, _$ssrAttribute("disabled", microphoneDisabled(), true), _$escape(chatMessages().restoreOriginal))
|
|
1626
2031
|
})]
|
|
1627
2032
|
})));
|
|
1628
2033
|
}
|
|
@@ -1631,16 +2036,16 @@ export function ChatComposer(props) {
|
|
|
1631
2036
|
return attachments().length > 0;
|
|
1632
2037
|
},
|
|
1633
2038
|
get children() {
|
|
1634
|
-
return _$ssr(_tmpl$
|
|
2039
|
+
return _$ssr(_tmpl$24, _$ssrAttribute("aria-label", _$escape(chatMessages().attachments, true), false), _$escape(_$createComponent(For, {
|
|
1635
2040
|
get each() {
|
|
1636
2041
|
return attachments();
|
|
1637
2042
|
},
|
|
1638
|
-
children: attachment => _$ssr(_tmpl$
|
|
2043
|
+
children: attachment => _$ssr(_tmpl$37, `k2b-chat-composer__attachment-content ${Boolean(attachment.action) ? "k2b-chat-composer__attachment-content--action" : ""}`, _$escape(_$createComponent(Show, {
|
|
1639
2044
|
get when() {
|
|
1640
2045
|
return attachment.href;
|
|
1641
2046
|
},
|
|
1642
2047
|
get fallback() {
|
|
1643
|
-
return _$ssr(_tmpl$
|
|
2048
|
+
return _$ssr(_tmpl$39, _$escape(_$createComponent(Show, {
|
|
1644
2049
|
get when() {
|
|
1645
2050
|
return _$memo(() => attachment.kind === "image")() && attachment.previewUrl;
|
|
1646
2051
|
},
|
|
@@ -1648,11 +2053,11 @@ export function ChatComposer(props) {
|
|
|
1648
2053
|
return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
|
|
1649
2054
|
},
|
|
1650
2055
|
get children() {
|
|
1651
|
-
return _$ssr(_tmpl$
|
|
2056
|
+
return _$ssr(_tmpl$38, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
|
|
1652
2057
|
}
|
|
1653
2058
|
})), _$escape(renderAttachmentCopy(attachment)));
|
|
1654
2059
|
},
|
|
1655
|
-
children: href => _$ssr(_tmpl$
|
|
2060
|
+
children: href => _$ssr(_tmpl$40, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("aria-label", _$escape(chatMessages().openInNewTab({
|
|
1656
2061
|
name: attachment.name
|
|
1657
2062
|
}), true), false), _$escape(_$createComponent(Show, {
|
|
1658
2063
|
get when() {
|
|
@@ -1662,14 +2067,14 @@ export function ChatComposer(props) {
|
|
|
1662
2067
|
return _$ssr(_tmpl$3, _$ssrAttribute("class", _$escape(attachmentIcon(attachment), true), false));
|
|
1663
2068
|
},
|
|
1664
2069
|
get children() {
|
|
1665
|
-
return _$ssr(_tmpl$
|
|
2070
|
+
return _$ssr(_tmpl$38, _$ssrAttribute("src", _$escape(attachment.previewUrl, true), false) + _$ssrAttribute("alt", _$escape(attachment.alt ?? "", true), false));
|
|
1666
2071
|
}
|
|
1667
2072
|
})), _$escape(renderAttachmentCopy(attachment)))
|
|
1668
2073
|
})), _$escape(_$createComponent(Show, {
|
|
1669
2074
|
get when() {
|
|
1670
2075
|
return attachment.action;
|
|
1671
2076
|
},
|
|
1672
|
-
children: action => _$ssr(_tmpl$
|
|
2077
|
+
children: action => _$ssr(_tmpl$41, _$ssrAttribute("disabled", blocked() || action().disabled, true), _$escape(_$createComponent(Show, {
|
|
1673
2078
|
get when() {
|
|
1674
2079
|
return action().icon;
|
|
1675
2080
|
},
|
|
@@ -1680,7 +2085,7 @@ export function ChatComposer(props) {
|
|
|
1680
2085
|
return props.onAttachmentsChange;
|
|
1681
2086
|
},
|
|
1682
2087
|
get children() {
|
|
1683
|
-
return _$ssr(_tmpl$
|
|
2088
|
+
return _$ssr(_tmpl$36, _$ssrAttribute("aria-label", _$escape(selectMessages().removeNamed({
|
|
1684
2089
|
name: attachment.name
|
|
1685
2090
|
}), true), false), _$ssrAttribute("disabled", blocked(), true));
|
|
1686
2091
|
}
|
|
@@ -1697,19 +2102,29 @@ export function ChatComposer(props) {
|
|
|
1697
2102
|
return props.error;
|
|
1698
2103
|
},
|
|
1699
2104
|
get children() {
|
|
1700
|
-
return _$ssr(_tmpl$
|
|
2105
|
+
return _$ssr(_tmpl$25, _$escape(props.error));
|
|
1701
2106
|
}
|
|
1702
|
-
}), formatRow(), _$ssr(_tmpl$
|
|
2107
|
+
}), formatRow(), _$ssr(_tmpl$42, _$escape(_$createComponent(Show, {
|
|
1703
2108
|
get when() {
|
|
1704
2109
|
return footer();
|
|
1705
2110
|
},
|
|
1706
2111
|
get fallback() {
|
|
1707
|
-
return [_$ssr(_tmpl$
|
|
2112
|
+
return [_$ssr(_tmpl$43, _$escape(props.footerTools), _$escape(_$createComponent(Show, {
|
|
1708
2113
|
get when() {
|
|
1709
|
-
return
|
|
2114
|
+
return addOpensCommands();
|
|
2115
|
+
},
|
|
2116
|
+
get fallback() {
|
|
2117
|
+
return _$createComponent(Show, {
|
|
2118
|
+
get when() {
|
|
2119
|
+
return hasAddMenu();
|
|
2120
|
+
},
|
|
2121
|
+
get children() {
|
|
2122
|
+
return addMenu("ti ti-plus");
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
1710
2125
|
},
|
|
1711
2126
|
get children() {
|
|
1712
|
-
return
|
|
2127
|
+
return addSearchButton();
|
|
1713
2128
|
}
|
|
1714
2129
|
})), _$escape(fileInput()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(_$createComponent(Show, {
|
|
1715
2130
|
get when() {
|
|
@@ -1757,7 +2172,7 @@ export function ChatComposer(props) {
|
|
|
1757
2172
|
}
|
|
1758
2173
|
});
|
|
1759
2174
|
}
|
|
1760
|
-
})), _$escape(props.modelDetails)), _$ssr(_tmpl$
|
|
2175
|
+
})), _$escape(props.modelDetails)), _$ssr(_tmpl$44, _$escape(_$createComponent(For, {
|
|
1761
2176
|
get each() {
|
|
1762
2177
|
return props.contextActions;
|
|
1763
2178
|
},
|
|
@@ -1809,9 +2224,9 @@ export function ChatComposer(props) {
|
|
|
1809
2224
|
return props.error;
|
|
1810
2225
|
},
|
|
1811
2226
|
get children() {
|
|
1812
|
-
return _$ssr(_tmpl$
|
|
2227
|
+
return _$ssr(_tmpl$25, _$escape(props.error));
|
|
1813
2228
|
}
|
|
1814
|
-
}), formatRow(), _$ssr(_tmpl$
|
|
2229
|
+
}), formatRow(), _$ssr(_tmpl$26, _$escape(attachControl()), _$escape(fileInput()), _$escape(field()), _$escape(formatToggle()), _$escape(emojiButton()), _$escape(microphoneControl()), _$escape(sendControl()))];
|
|
1815
2230
|
}
|
|
1816
2231
|
})), _$escape(_$createComponent(Show, {
|
|
1817
2232
|
get when() {
|
|
@@ -1837,18 +2252,18 @@ export function ChatComposer(props) {
|
|
|
1837
2252
|
return suggestionAnnouncement();
|
|
1838
2253
|
},
|
|
1839
2254
|
keyed: true,
|
|
1840
|
-
children: announced => _$ssr(_tmpl$
|
|
2255
|
+
children: announced => _$ssr(_tmpl$45, _$escape(announced.text))
|
|
1841
2256
|
})), _$escape(_$createComponent(Show, {
|
|
1842
2257
|
get when() {
|
|
1843
2258
|
return _$memo(() => !!!suggestionsOpen())() && commandStatus();
|
|
1844
2259
|
},
|
|
1845
|
-
children: status => _$ssr(_tmpl$
|
|
2260
|
+
children: status => _$ssr(_tmpl$46, _$ssrAttribute("data-tone", _$escape(status().tone, true), false), _$ssrAttribute("class", status().tone === "busy" ? "ti ti-loader-2 k2b-spin" : status().tone === "done" ? "ti ti-check" : "ti ti-alert-circle", false), _$escape(status().text))
|
|
1846
2261
|
})), _$escape(_$createComponent(Show, {
|
|
1847
2262
|
get when() {
|
|
1848
2263
|
return _$memo(() => !!suggestionsOpen())() && announcement();
|
|
1849
2264
|
},
|
|
1850
2265
|
keyed: true,
|
|
1851
|
-
children: announced => _$ssr(_tmpl$
|
|
2266
|
+
children: announced => _$ssr(_tmpl$47, _$escape(announced.text))
|
|
1852
2267
|
})));
|
|
1853
2268
|
}
|
|
1854
2269
|
//# sourceMappingURL=ChatComposer.js.map
|