@gravity-ui/aikit 2.24.1-beta.883c85d04b93d8521b2733baa3e86da20249b4e7.0 → 2.25.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 (118) hide show
  1. package/build/cjs/components/molecules/PromptInputBody/PromptInputBody.d.ts +1 -0
  2. package/build/cjs/components/molecules/PromptInputBody/PromptInputBody.js +2 -8
  3. package/build/cjs/components/molecules/PromptInputBody/PromptInputBody.js.map +1 -1
  4. package/build/cjs/components/organisms/PromptInput/PromptInput.css +37 -0
  5. package/build/cjs/components/organisms/PromptInput/PromptInput.d.ts +13 -2
  6. package/build/cjs/components/organisms/PromptInput/PromptInput.js +64 -3
  7. package/build/cjs/components/organisms/PromptInput/PromptInput.js.map +1 -1
  8. package/build/cjs/components/organisms/PromptInput/PromptInputCommands.d.ts +9 -0
  9. package/build/cjs/components/organisms/PromptInput/PromptInputCommands.js +39 -0
  10. package/build/cjs/components/organisms/PromptInput/PromptInputCommands.js.map +1 -0
  11. package/build/cjs/components/organisms/PromptInput/PromptInputFull.d.ts +2 -0
  12. package/build/cjs/components/organisms/PromptInput/PromptInputFull.js +4 -4
  13. package/build/cjs/components/organisms/PromptInput/PromptInputFull.js.map +1 -1
  14. package/build/cjs/components/organisms/PromptInput/PromptInputSimple.d.ts +2 -0
  15. package/build/cjs/components/organisms/PromptInput/PromptInputSimple.js +3 -3
  16. package/build/cjs/components/organisms/PromptInput/PromptInputSimple.js.map +1 -1
  17. package/build/cjs/components/organisms/PromptInput/commandUtils.d.ts +13 -0
  18. package/build/cjs/components/organisms/PromptInput/commandUtils.js +27 -0
  19. package/build/cjs/components/organisms/PromptInput/commandUtils.js.map +1 -0
  20. package/build/cjs/components/organisms/PromptInput/i18n/en.json +8 -0
  21. package/build/cjs/components/organisms/PromptInput/i18n/index.d.ts +13 -0
  22. package/build/cjs/components/organisms/PromptInput/i18n/index.js +10 -0
  23. package/build/cjs/components/organisms/PromptInput/i18n/index.js.map +1 -0
  24. package/build/cjs/components/organisms/PromptInput/i18n/ru.json +8 -0
  25. package/build/cjs/components/organisms/PromptInput/index.d.ts +2 -1
  26. package/build/cjs/components/organisms/PromptInput/index.js.map +1 -1
  27. package/build/cjs/components/organisms/PromptInput/types.d.ts +1 -0
  28. package/build/cjs/components/organisms/PromptInput/types.js.map +1 -1
  29. package/build/cjs/components/organisms/PromptInput/usePromptCommands.d.ts +41 -0
  30. package/build/cjs/components/organisms/PromptInput/usePromptCommands.js +222 -0
  31. package/build/cjs/components/organisms/PromptInput/usePromptCommands.js.map +1 -0
  32. package/build/cjs/components/organisms/PromptInput/usePromptInput.d.ts +10 -1
  33. package/build/cjs/components/organisms/PromptInput/usePromptInput.js +100 -11
  34. package/build/cjs/components/organisms/PromptInput/usePromptInput.js.map +1 -1
  35. package/build/cjs/components/pages/AIStudioChat/AIStudioChat.js +139 -52
  36. package/build/cjs/components/pages/AIStudioChat/AIStudioChat.js.map +1 -1
  37. package/build/cjs/components/pages/AIStudioChat/request.d.ts +4 -0
  38. package/build/cjs/components/pages/AIStudioChat/request.js +11 -0
  39. package/build/cjs/components/pages/AIStudioChat/request.js.map +1 -0
  40. package/build/cjs/components/pages/AIStudioChat/types.d.ts +5 -5
  41. package/build/cjs/components/pages/AIStudioChat/types.js.map +1 -1
  42. package/build/cjs/components/pages/ChatContainer/ChatContainer.js +10 -4
  43. package/build/cjs/components/pages/ChatContainer/ChatContainer.js.map +1 -1
  44. package/build/cjs/components/pages/ChatContainer/types.d.ts +1 -1
  45. package/build/cjs/components/pages/ChatContainer/types.js.map +1 -1
  46. package/build/cjs/components/pages/ChatContainer/useChatContainer.d.ts +1 -0
  47. package/build/cjs/components/pages/ChatContainer/useChatContainer.js +5 -1
  48. package/build/cjs/components/pages/ChatContainer/useChatContainer.js.map +1 -1
  49. package/build/cjs/package.json +1 -1
  50. package/build/cjs/types/index.d.ts +1 -0
  51. package/build/cjs/types/index.js +1 -0
  52. package/build/cjs/types/index.js.map +1 -1
  53. package/build/cjs/types/messages.d.ts +3 -0
  54. package/build/cjs/types/messages.js.map +1 -1
  55. package/build/cjs/types/promptCommands.d.ts +44 -0
  56. package/build/cjs/types/promptCommands.js +3 -0
  57. package/build/cjs/types/promptCommands.js.map +1 -0
  58. package/build/docs/components/molecules/PromptInputBody.md +16 -15
  59. package/build/docs/components/organisms/PromptInput.md +151 -0
  60. package/build/docs/components/pages/AIStudioChat.md +38 -0
  61. package/build/esm/components/molecules/PromptInputBody/PromptInputBody.d.ts +1 -0
  62. package/build/esm/components/molecules/PromptInputBody/PromptInputBody.js +2 -8
  63. package/build/esm/components/molecules/PromptInputBody/PromptInputBody.js.map +1 -1
  64. package/build/esm/components/organisms/PromptInput/PromptInput.css +37 -0
  65. package/build/esm/components/organisms/PromptInput/PromptInput.d.ts +13 -2
  66. package/build/esm/components/organisms/PromptInput/PromptInput.js +65 -4
  67. package/build/esm/components/organisms/PromptInput/PromptInput.js.map +1 -1
  68. package/build/esm/components/organisms/PromptInput/PromptInputCommands.d.ts +9 -0
  69. package/build/esm/components/organisms/PromptInput/PromptInputCommands.js +36 -0
  70. package/build/esm/components/organisms/PromptInput/PromptInputCommands.js.map +1 -0
  71. package/build/esm/components/organisms/PromptInput/PromptInputFull.d.ts +2 -0
  72. package/build/esm/components/organisms/PromptInput/PromptInputFull.js +4 -4
  73. package/build/esm/components/organisms/PromptInput/PromptInputFull.js.map +1 -1
  74. package/build/esm/components/organisms/PromptInput/PromptInputSimple.d.ts +2 -0
  75. package/build/esm/components/organisms/PromptInput/PromptInputSimple.js +3 -3
  76. package/build/esm/components/organisms/PromptInput/PromptInputSimple.js.map +1 -1
  77. package/build/esm/components/organisms/PromptInput/commandUtils.d.ts +13 -0
  78. package/build/esm/components/organisms/PromptInput/commandUtils.js +23 -0
  79. package/build/esm/components/organisms/PromptInput/commandUtils.js.map +1 -0
  80. package/build/esm/components/organisms/PromptInput/i18n/en.json +8 -0
  81. package/build/esm/components/organisms/PromptInput/i18n/index.d.ts +13 -0
  82. package/build/esm/components/organisms/PromptInput/i18n/index.js +6 -0
  83. package/build/esm/components/organisms/PromptInput/i18n/index.js.map +1 -0
  84. package/build/esm/components/organisms/PromptInput/i18n/ru.json +8 -0
  85. package/build/esm/components/organisms/PromptInput/index.d.ts +2 -1
  86. package/build/esm/components/organisms/PromptInput/index.js.map +1 -1
  87. package/build/esm/components/organisms/PromptInput/types.d.ts +1 -0
  88. package/build/esm/components/organisms/PromptInput/types.js.map +1 -1
  89. package/build/esm/components/organisms/PromptInput/usePromptCommands.d.ts +41 -0
  90. package/build/esm/components/organisms/PromptInput/usePromptCommands.js +219 -0
  91. package/build/esm/components/organisms/PromptInput/usePromptCommands.js.map +1 -0
  92. package/build/esm/components/organisms/PromptInput/usePromptInput.d.ts +10 -1
  93. package/build/esm/components/organisms/PromptInput/usePromptInput.js +101 -12
  94. package/build/esm/components/organisms/PromptInput/usePromptInput.js.map +1 -1
  95. package/build/esm/components/pages/AIStudioChat/AIStudioChat.js +139 -52
  96. package/build/esm/components/pages/AIStudioChat/AIStudioChat.js.map +1 -1
  97. package/build/esm/components/pages/AIStudioChat/request.d.ts +4 -0
  98. package/build/esm/components/pages/AIStudioChat/request.js +8 -0
  99. package/build/esm/components/pages/AIStudioChat/request.js.map +1 -0
  100. package/build/esm/components/pages/AIStudioChat/types.d.ts +5 -5
  101. package/build/esm/components/pages/AIStudioChat/types.js.map +1 -1
  102. package/build/esm/components/pages/ChatContainer/ChatContainer.js +12 -6
  103. package/build/esm/components/pages/ChatContainer/ChatContainer.js.map +1 -1
  104. package/build/esm/components/pages/ChatContainer/types.d.ts +1 -1
  105. package/build/esm/components/pages/ChatContainer/types.js.map +1 -1
  106. package/build/esm/components/pages/ChatContainer/useChatContainer.d.ts +1 -0
  107. package/build/esm/components/pages/ChatContainer/useChatContainer.js +5 -1
  108. package/build/esm/components/pages/ChatContainer/useChatContainer.js.map +1 -1
  109. package/build/esm/package.json +1 -1
  110. package/build/esm/types/index.d.ts +1 -0
  111. package/build/esm/types/index.js +1 -0
  112. package/build/esm/types/index.js.map +1 -1
  113. package/build/esm/types/messages.d.ts +3 -0
  114. package/build/esm/types/messages.js.map +1 -1
  115. package/build/esm/types/promptCommands.d.ts +44 -0
  116. package/build/esm/types/promptCommands.js +2 -0
  117. package/build/esm/types/promptCommands.js.map +1 -0
  118. package/package.json +1 -1
@@ -0,0 +1,219 @@
1
+ import { useEffect, useId, useRef, useState } from 'react';
2
+ import { findCommandMatch, replaceCommand } from "./commandUtils.js";
3
+ const EMPTY_SELECTIONS = [];
4
+ export function usePromptCommands({ config, resetKey, value, onChange, inputRef, disabled, maxLength, }) {
5
+ var _a;
6
+ const [internalSelections, setInternalSelections] = useState([]);
7
+ const selections = config ? ((_a = config.selections) !== null && _a !== void 0 ? _a : internalSelections) : EMPTY_SELECTIONS;
8
+ const [session, setSession] = useState();
9
+ const [result, setResult] = useState();
10
+ const [activeIndex, setActiveIndex] = useState(0);
11
+ const [selectionError, setSelectionError] = useState(false);
12
+ const suppressed = useRef();
13
+ const selectionRevision = useRef(0);
14
+ const previousResetKey = useRef(resetKey);
15
+ useEffect(() => {
16
+ if (previousResetKey.current === resetKey)
17
+ return;
18
+ previousResetKey.current = resetKey;
19
+ selectionRevision.current++;
20
+ setInternalSelections([]);
21
+ setSession(undefined);
22
+ setResult(undefined);
23
+ setSelectionError(false);
24
+ suppressed.current = undefined;
25
+ }, [resetKey]);
26
+ const listId = useId();
27
+ const source = config === null || config === void 0 ? void 0 : config.sources.find((item) => item.id === (session === null || session === void 0 ? void 0 : session.sourceId));
28
+ const open = Boolean(session && source && !disabled);
29
+ const items = result && result.source === source && result.session === session ? result.items : [];
30
+ const loading = open && ((result === null || result === void 0 ? void 0 : result.session) !== session || (result === null || result === void 0 ? void 0 : result.source) !== source);
31
+ const error = selectionError ||
32
+ (result && result.source === source && result.session === session && result.error);
33
+ const mounted = useRef(true);
34
+ useEffect(() => {
35
+ mounted.current = true;
36
+ return () => {
37
+ mounted.current = false;
38
+ };
39
+ }, []);
40
+ const latest = useRef({ selections, config });
41
+ if (latest.current.selections.length !== selections.length ||
42
+ latest.current.selections.some((item, index) => item !== selections[index])) {
43
+ selectionRevision.current++;
44
+ }
45
+ latest.current = { selections, config };
46
+ const updateSelections = (next) => {
47
+ var _a, _b;
48
+ if (!mounted.current)
49
+ return;
50
+ selectionRevision.current++;
51
+ latest.current.selections = next;
52
+ setInternalSelections(next);
53
+ (_b = (_a = latest.current.config) === null || _a === void 0 ? void 0 : _a.onSelectionsChange) === null || _b === void 0 ? void 0 : _b.call(_a, next);
54
+ };
55
+ const clearSubmitted = (submitted) => {
56
+ updateSelections(latest.current.selections.filter((item) => !submitted.includes(item)));
57
+ };
58
+ const close = () => {
59
+ suppressed.current = value;
60
+ setSession(undefined);
61
+ };
62
+ const detect = (text, start, end, edited = false) => {
63
+ if (disabled || !config || (!edited && suppressed.current !== undefined))
64
+ return;
65
+ if (edited)
66
+ suppressed.current = undefined;
67
+ const match = findCommandMatch(text, start, end, config.sources);
68
+ setSession((previous) => {
69
+ if (!match)
70
+ return undefined;
71
+ if ((previous === null || previous === void 0 ? void 0 : previous.origin) === 'input' &&
72
+ previous.sourceId === match.sourceId &&
73
+ previous.query === match.query &&
74
+ previous.start === match.start &&
75
+ previous.end === match.end)
76
+ return previous;
77
+ return Object.assign(Object.assign({}, match), { origin: 'input' });
78
+ });
79
+ };
80
+ useEffect(() => {
81
+ if (disabled)
82
+ setSession(undefined);
83
+ }, [disabled]);
84
+ useEffect(() => {
85
+ if (!session || !source || !open)
86
+ return undefined;
87
+ const controller = new AbortController();
88
+ setActiveIndex(0);
89
+ setSelectionError(false);
90
+ const load = async () => {
91
+ var _a;
92
+ try {
93
+ const matches = source.getItems
94
+ ? await source.getItems(session.query, { signal: controller.signal })
95
+ : ((_a = source.items) !== null && _a !== void 0 ? _a : []).filter((item) => {
96
+ var _a;
97
+ return [item.title, item.description, ...((_a = item.keywords) !== null && _a !== void 0 ? _a : [])].some((text) => text === null || text === void 0 ? void 0 : text.toLocaleLowerCase().includes(session.query.toLocaleLowerCase()));
98
+ });
99
+ if (!controller.signal.aborted) {
100
+ setResult({ session, source, items: matches });
101
+ setActiveIndex(Math.max(0, matches.findIndex((item) => !item.disabled)));
102
+ }
103
+ }
104
+ catch (_b) {
105
+ if (!controller.signal.aborted)
106
+ setResult({ session, source, items: [], error: true });
107
+ }
108
+ };
109
+ load();
110
+ return () => controller.abort();
111
+ }, [session, source, open]);
112
+ const select = (item) => {
113
+ var _a;
114
+ if (!session || !source || item.disabled || disabled)
115
+ return;
116
+ const replacement = session.origin === 'button' && item.insertText === undefined
117
+ ? { value, caret: session.start }
118
+ : replaceCommand(value, session, item.insertText);
119
+ if (maxLength !== undefined && replacement.value.length > maxLength) {
120
+ setSelectionError(true);
121
+ return;
122
+ }
123
+ try {
124
+ (_a = source.onSelect) === null || _a === void 0 ? void 0 : _a.call(source, item, { query: session.query, origin: session.origin });
125
+ }
126
+ catch (_b) {
127
+ setSelectionError(true);
128
+ return;
129
+ }
130
+ if (item.keepSelected !== false) {
131
+ const selection = Object.assign({ sourceId: source.id, id: item.id, title: item.title }, (item.data && { data: item.data }));
132
+ updateSelections([
133
+ ...selections.filter((entry) => entry.sourceId !== source.id || entry.id !== item.id),
134
+ selection,
135
+ ]);
136
+ }
137
+ onChange(replacement.value);
138
+ suppressed.current = replacement.value;
139
+ setSession(undefined);
140
+ requestAnimationFrame(() => {
141
+ const input = inputRef.current;
142
+ if (!(input === null || input === void 0 ? void 0 : input.isConnected))
143
+ return;
144
+ input.focus();
145
+ input.setSelectionRange(replacement.caret, replacement.caret);
146
+ });
147
+ };
148
+ const handleKeyDown = (event) => {
149
+ if (!open || event.nativeEvent.isComposing)
150
+ return false;
151
+ if (event.key === 'Escape' || event.key === 'Tab') {
152
+ if (event.key === 'Escape')
153
+ event.preventDefault();
154
+ close();
155
+ return true;
156
+ }
157
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
158
+ event.preventDefault();
159
+ const direction = event.key === 'ArrowDown' ? 1 : -1;
160
+ for (let step = 1; step <= items.length; step++) {
161
+ const index = (activeIndex + direction * step + items.length) % items.length;
162
+ if (!items[index].disabled) {
163
+ setActiveIndex(index);
164
+ break;
165
+ }
166
+ }
167
+ return true;
168
+ }
169
+ if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey) {
170
+ event.preventDefault();
171
+ if (items[activeIndex])
172
+ select(items[activeIndex]);
173
+ return true;
174
+ }
175
+ return false;
176
+ };
177
+ return {
178
+ selections,
179
+ clearSubmitted,
180
+ captureSubmission: () => {
181
+ const submitted = [...latest.current.selections];
182
+ clearSubmitted(submitted);
183
+ let expectedRevision = selectionRevision.current;
184
+ return {
185
+ selections: submitted,
186
+ hasChanged: () => selectionRevision.current !== expectedRevision,
187
+ restore: () => {
188
+ updateSelections(submitted);
189
+ expectedRevision = selectionRevision.current;
190
+ },
191
+ clear: () => clearSubmitted(submitted),
192
+ };
193
+ },
194
+ open,
195
+ source,
196
+ session,
197
+ items,
198
+ loading,
199
+ error,
200
+ listId,
201
+ activeIndex,
202
+ select,
203
+ close,
204
+ handleKeyDown,
205
+ detect,
206
+ remove: (selection) => updateSelections(selections.filter((item) => item !== selection)),
207
+ openSource: (sourceId) => {
208
+ var _a, _b, _c, _d;
209
+ if (disabled)
210
+ return;
211
+ const start = (_b = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.selectionStart) !== null && _b !== void 0 ? _b : value.length;
212
+ const end = (_d = (_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.selectionEnd) !== null && _d !== void 0 ? _d : start;
213
+ setSession({ sourceId, query: '', start, end, origin: 'button' });
214
+ },
215
+ search: (query) => setSession((previous) => (previous ? Object.assign(Object.assign({}, previous), { query }) : previous)),
216
+ retry: () => setSession((previous) => (previous ? Object.assign({}, previous) : previous)),
217
+ };
218
+ }
219
+ //# sourceMappingURL=usePromptCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePromptCommands.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/usePromptCommands.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useId, useRef, useState} from 'react';\nimport type {KeyboardEvent, RefObject} from 'react';\n\nimport type {\n PromptCommandItem,\n PromptCommandSource,\n PromptCommandsConfig,\n PromptSelection,\n} from '../../../types';\n\nimport {type CommandMatch, findCommandMatch, replaceCommand} from './commandUtils';\n\ntype Session = CommandMatch & {origin: 'input' | 'button'};\nconst EMPTY_SELECTIONS: readonly PromptSelection[] = [];\n\nexport function usePromptCommands({\n config,\n resetKey,\n value,\n onChange,\n inputRef,\n disabled,\n maxLength,\n}: {\n config?: PromptCommandsConfig;\n resetKey?: string | number;\n value: string;\n onChange: (value: string) => void;\n inputRef: RefObject<HTMLTextAreaElement>;\n disabled: boolean;\n maxLength?: number;\n}) {\n const [internalSelections, setInternalSelections] = useState<PromptSelection[]>([]);\n const selections = config ? (config.selections ?? internalSelections) : EMPTY_SELECTIONS;\n const [session, setSession] = useState<Session>();\n const [result, setResult] = useState<{\n session: Session;\n source: PromptCommandSource;\n items: readonly PromptCommandItem[];\n error?: boolean;\n }>();\n const [activeIndex, setActiveIndex] = useState(0);\n const [selectionError, setSelectionError] = useState(false);\n const suppressed = useRef<string>();\n const selectionRevision = useRef(0);\n const previousResetKey = useRef(resetKey);\n useEffect(() => {\n if (previousResetKey.current === resetKey) return;\n previousResetKey.current = resetKey;\n selectionRevision.current++;\n setInternalSelections([]);\n setSession(undefined);\n setResult(undefined);\n setSelectionError(false);\n suppressed.current = undefined;\n }, [resetKey]);\n const listId = useId();\n const source = config?.sources.find((item) => item.id === session?.sourceId);\n const open = Boolean(session && source && !disabled);\n const items =\n result && result.source === source && result.session === session ? result.items : [];\n const loading = open && (result?.session !== session || result?.source !== source);\n const error =\n selectionError ||\n (result && result.source === source && result.session === session && result.error);\n const mounted = useRef(true);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const latest = useRef({selections, config});\n if (\n latest.current.selections.length !== selections.length ||\n latest.current.selections.some((item, index) => item !== selections[index])\n ) {\n selectionRevision.current++;\n }\n latest.current = {selections, config};\n\n const updateSelections = (next: PromptSelection[]) => {\n if (!mounted.current) return;\n selectionRevision.current++;\n latest.current.selections = next;\n setInternalSelections(next);\n latest.current.config?.onSelectionsChange?.(next);\n };\n const clearSubmitted = (submitted: readonly PromptSelection[]) => {\n updateSelections(latest.current.selections.filter((item) => !submitted.includes(item)));\n };\n const close = () => {\n suppressed.current = value;\n setSession(undefined);\n };\n const detect = (text: string, start: number, end: number, edited = false) => {\n if (disabled || !config || (!edited && suppressed.current !== undefined)) return;\n if (edited) suppressed.current = undefined;\n const match = findCommandMatch(text, start, end, config.sources);\n setSession((previous) => {\n if (!match) return undefined;\n if (\n previous?.origin === 'input' &&\n previous.sourceId === match.sourceId &&\n previous.query === match.query &&\n previous.start === match.start &&\n previous.end === match.end\n )\n return previous;\n return {...match, origin: 'input'};\n });\n };\n useEffect(() => {\n if (disabled) setSession(undefined);\n }, [disabled]);\n useEffect(() => {\n if (!session || !source || !open) return undefined;\n const controller = new AbortController();\n setActiveIndex(0);\n setSelectionError(false);\n const load = async () => {\n try {\n const matches = source.getItems\n ? await source.getItems(session.query, {signal: controller.signal})\n : (source.items ?? []).filter((item) =>\n [item.title, item.description, ...(item.keywords ?? [])].some((text) =>\n text?.toLocaleLowerCase().includes(session.query.toLocaleLowerCase()),\n ),\n );\n if (!controller.signal.aborted) {\n setResult({session, source, items: matches});\n setActiveIndex(\n Math.max(\n 0,\n matches.findIndex((item) => !item.disabled),\n ),\n );\n }\n } catch {\n if (!controller.signal.aborted)\n setResult({session, source, items: [], error: true});\n }\n };\n load();\n return () => controller.abort();\n }, [session, source, open]);\n\n const select = (item: PromptCommandItem) => {\n if (!session || !source || item.disabled || disabled) return;\n const replacement =\n session.origin === 'button' && item.insertText === undefined\n ? {value, caret: session.start}\n : replaceCommand(value, session, item.insertText);\n if (maxLength !== undefined && replacement.value.length > maxLength) {\n setSelectionError(true);\n return;\n }\n try {\n source.onSelect?.(item, {query: session.query, origin: session.origin});\n } catch {\n setSelectionError(true);\n return;\n }\n if (item.keepSelected !== false) {\n const selection: PromptSelection = {\n sourceId: source.id,\n id: item.id,\n title: item.title,\n ...(item.data && {data: item.data}),\n };\n updateSelections([\n ...selections.filter(\n (entry) => entry.sourceId !== source.id || entry.id !== item.id,\n ),\n selection,\n ]);\n }\n onChange(replacement.value);\n suppressed.current = replacement.value;\n setSession(undefined);\n requestAnimationFrame(() => {\n const input = inputRef.current;\n if (!input?.isConnected) return;\n input.focus();\n input.setSelectionRange(replacement.caret, replacement.caret);\n });\n };\n const handleKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (!open || event.nativeEvent.isComposing) return false;\n if (event.key === 'Escape' || event.key === 'Tab') {\n if (event.key === 'Escape') event.preventDefault();\n close();\n return true;\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n const direction = event.key === 'ArrowDown' ? 1 : -1;\n for (let step = 1; step <= items.length; step++) {\n const index = (activeIndex + direction * step + items.length) % items.length;\n if (!items[index].disabled) {\n setActiveIndex(index);\n break;\n }\n }\n return true;\n }\n if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n if (items[activeIndex]) select(items[activeIndex]);\n return true;\n }\n return false;\n };\n return {\n selections,\n clearSubmitted,\n captureSubmission: () => {\n const submitted = [...latest.current.selections];\n clearSubmitted(submitted);\n let expectedRevision = selectionRevision.current;\n return {\n selections: submitted,\n hasChanged: () => selectionRevision.current !== expectedRevision,\n restore: () => {\n updateSelections(submitted);\n expectedRevision = selectionRevision.current;\n },\n clear: () => clearSubmitted(submitted),\n };\n },\n open,\n source,\n session,\n items,\n loading,\n error,\n listId,\n activeIndex,\n select,\n close,\n handleKeyDown,\n detect,\n remove: (selection: PromptSelection) =>\n updateSelections(selections.filter((item) => item !== selection)),\n openSource: (sourceId: string) => {\n if (disabled) return;\n const start = inputRef.current?.selectionStart ?? value.length;\n const end = inputRef.current?.selectionEnd ?? start;\n setSession({sourceId, query: '', start, end, origin: 'button'});\n },\n search: (query: string) =>\n setSession((previous) => (previous ? {...previous, query} : previous)),\n retry: () => setSession((previous) => (previous ? {...previous} : previous)),\n };\n}\n"]}
@@ -4,7 +4,13 @@ import { ChatStatus, TSubmitData } from "../../../types/index.js";
4
4
  */
5
5
  export type UsePromptInputProps = {
6
6
  /** Callback when message is sent */
7
- onSend: (data: TSubmitData) => Promise<void>;
7
+ onSend: (data: TSubmitData) => Promise<void | false>;
8
+ resetKey?: string | number;
9
+ captureSubmission?: () => {
10
+ restore: () => void;
11
+ hasChanged: () => boolean;
12
+ clear: () => void;
13
+ };
8
14
  /** Callback when sending is cancelled */
9
15
  onCancel?: () => Promise<void>;
10
16
  /** Initial value */
@@ -22,6 +28,7 @@ export type UsePromptInputProps = {
22
28
  * Hook return type for PromptInput state management
23
29
  */
24
30
  export type UsePromptInputReturn = {
31
+ sendError?: boolean;
25
32
  /** Current input value */
26
33
  value: string;
27
34
  /** Can submit the form */
@@ -36,6 +43,8 @@ export type UsePromptInputReturn = {
36
43
  handleKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
37
44
  /** Handle submit */
38
45
  handleSubmit: () => Promise<void>;
46
+ submit: (data?: TSubmitData) => Promise<void>;
47
+ clearFailedDraft: () => boolean;
39
48
  /** Attachments */
40
49
  attachments: File[];
41
50
  /** Set attachments */
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from 'react';
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  /**
3
3
  * Custom hook for managing PromptInput state and behavior
4
4
  *
@@ -6,14 +6,36 @@ import { useCallback, useState } from 'react';
6
6
  * @returns Hook return value with state and handlers
7
7
  */
8
8
  export function usePromptInput(props) {
9
- const { onSend, onCancel, initialValue = '', maxLength, disabled = false, status = 'ready', onValueChange, } = props;
9
+ const { onSend, onCancel, initialValue = '', maxLength, disabled = false, status = 'ready', onValueChange, resetKey, captureSubmission, } = props;
10
10
  const [value, setValue] = useState(initialValue);
11
- const [attachments, setAttachments] = useState([]);
11
+ const [sending, setSending] = useState(false);
12
+ const [sendError, setSendError] = useState(false);
13
+ const mounted = useRef(true);
14
+ useEffect(() => {
15
+ mounted.current = true;
16
+ return () => {
17
+ mounted.current = false;
18
+ };
19
+ }, []);
20
+ const revision = useRef(0);
21
+ const inFlight = useRef(false);
22
+ const [attachments, updateAttachments] = useState([]);
23
+ const setAttachments = useCallback((files) => {
24
+ revision.current++;
25
+ updateAttachments(files);
26
+ }, []);
27
+ const failedDraft = useRef(null);
28
+ useEffect(() => {
29
+ revision.current++;
30
+ failedDraft.current = null;
31
+ }, [resetKey]);
12
32
  const updateValue = useCallback((nextValue) => {
33
+ revision.current++;
34
+ setSendError(false);
13
35
  setValue(nextValue);
14
36
  onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(nextValue);
15
37
  }, [onValueChange]);
16
- const isSubmitted = status === 'submitted';
38
+ const isSubmitted = status === 'submitted' || sending;
17
39
  const trimmedValue = value.trim();
18
40
  const canSubmit = !disabled && !isSubmitted && trimmedValue.length > 0;
19
41
  // Map ChatStatus to submit button state
@@ -48,6 +70,8 @@ export function usePromptInput(props) {
48
70
  }
49
71
  }
50
72
  // Input is disabled when loading
73
+ if (sending && submitButtonState !== 'cancelable')
74
+ submitButtonState = 'loading';
51
75
  const isInputDisabled = submitButtonState === 'loading';
52
76
  const handleChange = useCallback((newValue) => {
53
77
  if (maxLength && newValue.length > maxLength) {
@@ -55,20 +79,82 @@ export function usePromptInput(props) {
55
79
  }
56
80
  updateValue(newValue);
57
81
  }, [maxLength, updateValue]);
82
+ const submit = useCallback(async (override) => {
83
+ var _a;
84
+ const content = (_a = override === null || override === void 0 ? void 0 : override.content.trim()) !== null && _a !== void 0 ? _a : trimmedValue;
85
+ if (disabled || isSubmitted || inFlight.current || !content)
86
+ return;
87
+ const draftValue = value;
88
+ const submitData = Object.assign(Object.assign(Object.assign({}, override), { content }), (attachments.length > 0 && { attachments }));
89
+ inFlight.current = true;
90
+ setSending(true);
91
+ setSendError(false);
92
+ failedDraft.current = null;
93
+ const selectionSnapshot = captureSubmission === null || captureSubmission === void 0 ? void 0 : captureSubmission();
94
+ updateValue('');
95
+ updateAttachments([]);
96
+ const clearedRevision = revision.current;
97
+ const restore = () => {
98
+ if (!mounted.current ||
99
+ revision.current !== clearedRevision ||
100
+ (selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.hasChanged()))
101
+ return;
102
+ updateValue(draftValue);
103
+ updateAttachments(attachments);
104
+ selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.restore();
105
+ const restoredRevision = revision.current;
106
+ failedDraft.current = () => {
107
+ if (!mounted.current ||
108
+ revision.current !== restoredRevision ||
109
+ (selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.hasChanged()))
110
+ return false;
111
+ updateValue('');
112
+ updateAttachments([]);
113
+ selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.clear();
114
+ return true;
115
+ };
116
+ return true;
117
+ };
118
+ try {
119
+ const result = await onSend(submitData);
120
+ if (result === false)
121
+ restore();
122
+ }
123
+ catch (_b) {
124
+ if (restore())
125
+ setSendError(true);
126
+ }
127
+ finally {
128
+ inFlight.current = false;
129
+ if (mounted.current)
130
+ setSending(false);
131
+ }
132
+ }, [
133
+ disabled,
134
+ isSubmitted,
135
+ trimmedValue,
136
+ value,
137
+ attachments,
138
+ onSend,
139
+ updateValue,
140
+ captureSubmission,
141
+ ]);
142
+ const clearFailedDraft = useCallback(() => {
143
+ var _a;
144
+ const clear = failedDraft.current;
145
+ failedDraft.current = null;
146
+ return (_a = clear === null || clear === void 0 ? void 0 : clear()) !== null && _a !== void 0 ? _a : false;
147
+ }, []);
58
148
  const handleSubmit = useCallback(async () => {
59
149
  if (submitButtonState === 'cancelable' && onCancel) {
60
150
  await onCancel();
61
151
  return;
62
152
  }
63
- if (!canSubmit) {
64
- return;
65
- }
66
- const submitData = Object.assign({ content: trimmedValue }, (attachments.length > 0 && { attachments }));
67
- onSend(submitData);
68
- updateValue('');
69
- setAttachments([]);
70
- }, [submitButtonState, canSubmit, trimmedValue, attachments, onSend, onCancel, updateValue]);
153
+ await submit();
154
+ }, [submitButtonState, onCancel, submit]);
71
155
  const handleKeyDown = useCallback((event) => {
156
+ if (event.nativeEvent.isComposing || event.defaultPrevented)
157
+ return;
72
158
  const isEnter = event.code === 'Enter' || event.code === 'NumpadEnter';
73
159
  // Insert new line on Ctrl/Cmd + Enter
74
160
  if (isEnter && (event.ctrlKey || event.metaKey)) {
@@ -89,12 +175,15 @@ export function usePromptInput(props) {
89
175
  }, [canSubmit, handleSubmit, updateValue, value]);
90
176
  return {
91
177
  value,
178
+ sendError,
92
179
  canSubmit,
93
180
  submitButtonState,
94
181
  isInputDisabled,
95
182
  handleChange,
96
183
  handleKeyDown,
97
184
  handleSubmit,
185
+ submit,
186
+ clearFailedDraft,
98
187
  attachments,
99
188
  setAttachments,
100
189
  };
@@ -1 +1 @@
1
- {"version":3,"file":"usePromptInput.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/usePromptInput.ts"],"names":[],"mappings":"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","sourcesContent":["import {useCallback, useState} from 'react';\n\nimport {ChatStatus, TSubmitData} from '../../../types';\n\n/**\n * Hook props for managing PromptInput state\n */\nexport type UsePromptInputProps = {\n /** Callback when message is sent */\n onSend: (data: TSubmitData) => Promise<void>;\n /** Callback when sending is cancelled */\n onCancel?: () => Promise<void>;\n /** Initial value */\n initialValue?: string;\n /** Maximum length of input */\n maxLength?: number;\n /** Disabled state */\n disabled?: boolean;\n /** Chat status to determine input behavior */\n status?: ChatStatus;\n /** Called whenever the hook changes its internal value. */\n onValueChange?: (value: string) => void;\n};\n\n/**\n * Hook return type for PromptInput state management\n */\nexport type UsePromptInputReturn = {\n /** Current input value */\n value: string;\n /** Can submit the form */\n canSubmit: boolean;\n /** Submit button state */\n submitButtonState: 'enabled' | 'disabled' | 'loading' | 'cancelable';\n /** Is input disabled (when loading) */\n isInputDisabled: boolean;\n /** Handle input change */\n handleChange: (value: string) => void;\n /** Handle key down */\n handleKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;\n /** Handle submit */\n handleSubmit: () => Promise<void>;\n /** Attachments */\n attachments: File[];\n /** Set attachments */\n setAttachments: (files: File[]) => void;\n};\n\n/**\n * Custom hook for managing PromptInput state and behavior\n *\n * @param props - Hook props\n * @returns Hook return value with state and handlers\n */\nexport function usePromptInput(props: UsePromptInputProps): UsePromptInputReturn {\n const {\n onSend,\n onCancel,\n initialValue = '',\n maxLength,\n disabled = false,\n status = 'ready',\n onValueChange,\n } = props;\n\n const [value, setValue] = useState(initialValue);\n const [attachments, setAttachments] = useState<File[]>([]);\n\n const updateValue = useCallback(\n (nextValue: string) => {\n setValue(nextValue);\n onValueChange?.(nextValue);\n },\n [onValueChange],\n );\n const isSubmitted = status === 'submitted';\n\n const trimmedValue = value.trim();\n const canSubmit = !disabled && !isSubmitted && trimmedValue.length > 0;\n\n // Map ChatStatus to submit button state\n // ChatStatus.ready → submitButtonState.enabled\n // ChatStatus.error → submitButtonState.enabled\n // ChatStatus.streaming → submitButtonState.cancelable\n // ChatStatus.streaming_loading → submitButtonState.cancelable (same as streaming)\n // ChatStatus.submitted → submitButtonState.loading\n let submitButtonState: 'enabled' | 'disabled' | 'loading' | 'cancelable' = 'disabled';\n\n // disabled by props or empty value and status is ready\n if (disabled) {\n submitButtonState = 'disabled';\n } else if (!trimmedValue && (status === 'ready' || status === 'error')) {\n submitButtonState = 'disabled';\n } else {\n switch (status) {\n case 'ready':\n case 'error':\n submitButtonState = 'enabled';\n break;\n case 'streaming':\n case 'streaming_loading':\n submitButtonState = onCancel ? 'cancelable' : 'enabled';\n break;\n case 'submitted':\n submitButtonState = 'loading';\n break;\n default:\n submitButtonState = 'enabled';\n }\n }\n\n // Input is disabled when loading\n const isInputDisabled = submitButtonState === 'loading';\n\n const handleChange = useCallback(\n (newValue: string) => {\n if (maxLength && newValue.length > maxLength) {\n return;\n }\n updateValue(newValue);\n },\n [maxLength, updateValue],\n );\n\n const handleSubmit = useCallback(async () => {\n if (submitButtonState === 'cancelable' && onCancel) {\n await onCancel();\n return;\n }\n\n if (!canSubmit) {\n return;\n }\n\n const submitData: TSubmitData = {\n content: trimmedValue,\n ...(attachments.length > 0 && {attachments}),\n };\n\n onSend(submitData);\n updateValue('');\n setAttachments([]);\n }, [submitButtonState, canSubmit, trimmedValue, attachments, onSend, onCancel, updateValue]);\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n const isEnter = event.code === 'Enter' || event.code === 'NumpadEnter';\n\n // Insert new line on Ctrl/Cmd + Enter\n if (isEnter && (event.ctrlKey || event.metaKey)) {\n updateValue(`${value}\\n`);\n event.preventDefault();\n return;\n }\n\n // Prevent submission if can't submit\n if (isEnter && !event.shiftKey && !canSubmit) {\n event.preventDefault();\n return;\n }\n\n // Submit on Enter (without Shift)\n if (isEnter && !event.shiftKey && canSubmit) {\n event.preventDefault();\n handleSubmit();\n }\n },\n [canSubmit, handleSubmit, updateValue, value],\n );\n\n return {\n value,\n canSubmit,\n submitButtonState,\n isInputDisabled,\n handleChange,\n handleKeyDown,\n handleSubmit,\n attachments,\n setAttachments,\n };\n}\n"]}
1
+ {"version":3,"file":"usePromptInput.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/usePromptInput.ts"],"names":[],"mappings":"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","sourcesContent":["import {useCallback, useEffect, useRef, useState} from 'react';\n\nimport {ChatStatus, TSubmitData} from '../../../types';\n\n/**\n * Hook props for managing PromptInput state\n */\nexport type UsePromptInputProps = {\n /** Callback when message is sent */\n onSend: (data: TSubmitData) => Promise<void | false>;\n resetKey?: string | number;\n captureSubmission?: () => {restore: () => void; hasChanged: () => boolean; clear: () => void};\n /** Callback when sending is cancelled */\n onCancel?: () => Promise<void>;\n /** Initial value */\n initialValue?: string;\n /** Maximum length of input */\n maxLength?: number;\n /** Disabled state */\n disabled?: boolean;\n /** Chat status to determine input behavior */\n status?: ChatStatus;\n /** Called whenever the hook changes its internal value. */\n onValueChange?: (value: string) => void;\n};\n\n/**\n * Hook return type for PromptInput state management\n */\nexport type UsePromptInputReturn = {\n sendError?: boolean;\n /** Current input value */\n value: string;\n /** Can submit the form */\n canSubmit: boolean;\n /** Submit button state */\n submitButtonState: 'enabled' | 'disabled' | 'loading' | 'cancelable';\n /** Is input disabled (when loading) */\n isInputDisabled: boolean;\n /** Handle input change */\n handleChange: (value: string) => void;\n /** Handle key down */\n handleKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;\n /** Handle submit */\n handleSubmit: () => Promise<void>;\n submit: (data?: TSubmitData) => Promise<void>;\n clearFailedDraft: () => boolean;\n /** Attachments */\n attachments: File[];\n /** Set attachments */\n setAttachments: (files: File[]) => void;\n};\n\n/**\n * Custom hook for managing PromptInput state and behavior\n *\n * @param props - Hook props\n * @returns Hook return value with state and handlers\n */\nexport function usePromptInput(props: UsePromptInputProps): UsePromptInputReturn {\n const {\n onSend,\n onCancel,\n initialValue = '',\n maxLength,\n disabled = false,\n status = 'ready',\n onValueChange,\n resetKey,\n captureSubmission,\n } = props;\n\n const [value, setValue] = useState(initialValue);\n const [sending, setSending] = useState(false);\n const [sendError, setSendError] = useState(false);\n const mounted = useRef(true);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const revision = useRef(0);\n const inFlight = useRef(false);\n const [attachments, updateAttachments] = useState<File[]>([]);\n const setAttachments = useCallback((files: File[]) => {\n revision.current++;\n updateAttachments(files);\n }, []);\n const failedDraft = useRef<(() => boolean) | null>(null);\n useEffect(() => {\n revision.current++;\n failedDraft.current = null;\n }, [resetKey]);\n\n const updateValue = useCallback(\n (nextValue: string) => {\n revision.current++;\n setSendError(false);\n setValue(nextValue);\n onValueChange?.(nextValue);\n },\n [onValueChange],\n );\n const isSubmitted = status === 'submitted' || sending;\n\n const trimmedValue = value.trim();\n const canSubmit = !disabled && !isSubmitted && trimmedValue.length > 0;\n\n // Map ChatStatus to submit button state\n // ChatStatus.ready → submitButtonState.enabled\n // ChatStatus.error → submitButtonState.enabled\n // ChatStatus.streaming → submitButtonState.cancelable\n // ChatStatus.streaming_loading → submitButtonState.cancelable (same as streaming)\n // ChatStatus.submitted → submitButtonState.loading\n let submitButtonState: 'enabled' | 'disabled' | 'loading' | 'cancelable' = 'disabled';\n\n // disabled by props or empty value and status is ready\n if (disabled) {\n submitButtonState = 'disabled';\n } else if (!trimmedValue && (status === 'ready' || status === 'error')) {\n submitButtonState = 'disabled';\n } else {\n switch (status) {\n case 'ready':\n case 'error':\n submitButtonState = 'enabled';\n break;\n case 'streaming':\n case 'streaming_loading':\n submitButtonState = onCancel ? 'cancelable' : 'enabled';\n break;\n case 'submitted':\n submitButtonState = 'loading';\n break;\n default:\n submitButtonState = 'enabled';\n }\n }\n\n // Input is disabled when loading\n if (sending && submitButtonState !== 'cancelable') submitButtonState = 'loading';\n const isInputDisabled = submitButtonState === 'loading';\n\n const handleChange = useCallback(\n (newValue: string) => {\n if (maxLength && newValue.length > maxLength) {\n return;\n }\n updateValue(newValue);\n },\n [maxLength, updateValue],\n );\n\n const submit = useCallback(\n async (override?: TSubmitData) => {\n const content = override?.content.trim() ?? trimmedValue;\n if (disabled || isSubmitted || inFlight.current || !content) return;\n const draftValue = value;\n const submitData: TSubmitData = {\n ...override,\n content,\n ...(attachments.length > 0 && {attachments}),\n };\n inFlight.current = true;\n setSending(true);\n setSendError(false);\n failedDraft.current = null;\n const selectionSnapshot = captureSubmission?.();\n updateValue('');\n updateAttachments([]);\n const clearedRevision = revision.current;\n const restore = () => {\n if (\n !mounted.current ||\n revision.current !== clearedRevision ||\n selectionSnapshot?.hasChanged()\n )\n return;\n updateValue(draftValue);\n updateAttachments(attachments);\n selectionSnapshot?.restore();\n const restoredRevision = revision.current;\n failedDraft.current = () => {\n if (\n !mounted.current ||\n revision.current !== restoredRevision ||\n selectionSnapshot?.hasChanged()\n )\n return false;\n updateValue('');\n updateAttachments([]);\n selectionSnapshot?.clear();\n return true;\n };\n return true;\n };\n try {\n const result = await onSend(submitData);\n if (result === false) restore();\n } catch {\n if (restore()) setSendError(true);\n } finally {\n inFlight.current = false;\n if (mounted.current) setSending(false);\n }\n },\n [\n disabled,\n isSubmitted,\n trimmedValue,\n value,\n attachments,\n onSend,\n updateValue,\n captureSubmission,\n ],\n );\n\n const clearFailedDraft = useCallback(() => {\n const clear = failedDraft.current;\n failedDraft.current = null;\n return clear?.() ?? false;\n }, []);\n\n const handleSubmit = useCallback(async () => {\n if (submitButtonState === 'cancelable' && onCancel) {\n await onCancel();\n return;\n }\n await submit();\n }, [submitButtonState, onCancel, submit]);\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.nativeEvent.isComposing || event.defaultPrevented) return;\n const isEnter = event.code === 'Enter' || event.code === 'NumpadEnter';\n\n // Insert new line on Ctrl/Cmd + Enter\n if (isEnter && (event.ctrlKey || event.metaKey)) {\n updateValue(`${value}\\n`);\n event.preventDefault();\n return;\n }\n\n // Prevent submission if can't submit\n if (isEnter && !event.shiftKey && !canSubmit) {\n event.preventDefault();\n return;\n }\n\n // Submit on Enter (without Shift)\n if (isEnter && !event.shiftKey && canSubmit) {\n event.preventDefault();\n handleSubmit();\n }\n },\n [canSubmit, handleSubmit, updateValue, value],\n );\n\n return {\n value,\n sendError,\n canSubmit,\n submitButtonState,\n isInputDisabled,\n handleChange,\n handleKeyDown,\n handleSubmit,\n submit,\n clearFailedDraft,\n attachments,\n setAttachments,\n };\n}\n"]}