@devicai/ui 0.2.0 → 0.4.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 (54) hide show
  1. package/dist/cjs/api/client.js +31 -15
  2. package/dist/cjs/api/client.js.map +1 -1
  3. package/dist/cjs/components/AICommandBar/AICommandBar.js +314 -0
  4. package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -0
  5. package/dist/cjs/components/AICommandBar/useAICommandBar.js +595 -0
  6. package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -0
  7. package/dist/cjs/components/ChatDrawer/ChatDrawer.js +73 -5
  8. package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
  9. package/dist/cjs/components/ChatDrawer/ChatMessages.js +5 -2
  10. package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
  11. package/dist/cjs/components/Feedback/FeedbackModal.js +87 -0
  12. package/dist/cjs/components/Feedback/FeedbackModal.js.map +1 -0
  13. package/dist/cjs/components/Feedback/MessageActions.js +74 -0
  14. package/dist/cjs/components/Feedback/MessageActions.js.map +1 -0
  15. package/dist/cjs/index.js +9 -0
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/styles.css +1 -1
  18. package/dist/esm/api/client.d.ts +10 -2
  19. package/dist/esm/api/client.js +31 -15
  20. package/dist/esm/api/client.js.map +1 -1
  21. package/dist/esm/api/types.d.ts +25 -0
  22. package/dist/esm/components/AICommandBar/AICommandBar.d.ts +22 -0
  23. package/dist/esm/components/AICommandBar/AICommandBar.js +312 -0
  24. package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -0
  25. package/dist/esm/components/AICommandBar/AICommandBar.types.d.ts +321 -0
  26. package/dist/esm/components/AICommandBar/index.d.ts +3 -0
  27. package/dist/esm/components/AICommandBar/useAICommandBar.d.ts +57 -0
  28. package/dist/esm/components/AICommandBar/useAICommandBar.js +592 -0
  29. package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -0
  30. package/dist/esm/components/AutocompleteInput/AutocompleteInput.d.ts +4 -0
  31. package/dist/esm/components/AutocompleteInput/AutocompleteInput.types.d.ts +50 -0
  32. package/dist/esm/components/AutocompleteInput/index.d.ts +4 -0
  33. package/dist/esm/components/AutocompleteInput/useAutocomplete.d.ts +29 -0
  34. package/dist/esm/components/ChatDrawer/ChatDrawer.d.ts +4 -2
  35. package/dist/esm/components/ChatDrawer/ChatDrawer.js +74 -6
  36. package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
  37. package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +36 -0
  38. package/dist/esm/components/ChatDrawer/ChatMessages.d.ts +2 -1
  39. package/dist/esm/components/ChatDrawer/ChatMessages.js +5 -2
  40. package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
  41. package/dist/esm/components/ChatDrawer/index.d.ts +1 -1
  42. package/dist/esm/components/Feedback/Feedback.types.d.ts +50 -0
  43. package/dist/esm/components/Feedback/FeedbackModal.d.ts +5 -0
  44. package/dist/esm/components/Feedback/FeedbackModal.js +85 -0
  45. package/dist/esm/components/Feedback/FeedbackModal.js.map +1 -0
  46. package/dist/esm/components/Feedback/MessageActions.d.ts +5 -0
  47. package/dist/esm/components/Feedback/MessageActions.js +72 -0
  48. package/dist/esm/components/Feedback/MessageActions.js.map +1 -0
  49. package/dist/esm/components/Feedback/index.d.ts +3 -0
  50. package/dist/esm/index.d.ts +6 -2
  51. package/dist/esm/index.js +4 -0
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/styles.css +1 -1
  54. package/package.json +1 -1
@@ -0,0 +1,592 @@
1
+ import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
2
+ import 'react/jsx-runtime';
3
+ import { useOptionalDevicContext } from '../../provider/DevicContext.js';
4
+ import { DevicApiClient } from '../../api/client.js';
5
+ import { usePolling } from '../../hooks/usePolling.js';
6
+ import { useModelInterface } from '../../hooks/useModelInterface.js';
7
+
8
+ /**
9
+ * Parse a shortcut string like "cmd+j" into its components
10
+ */
11
+ function parseShortcut(shortcut) {
12
+ const parts = shortcut.toLowerCase().split('+');
13
+ const key = parts.pop() || '';
14
+ const modifiers = parts;
15
+ return { key, modifiers };
16
+ }
17
+ /**
18
+ * Check if a keyboard event matches a shortcut string
19
+ */
20
+ function matchShortcut(event, shortcut) {
21
+ const { key, modifiers } = parseShortcut(shortcut);
22
+ const keyMatch = event.key.toLowerCase() === key;
23
+ const cmdMatch = modifiers.includes('cmd') === (event.metaKey || event.ctrlKey);
24
+ const shiftMatch = modifiers.includes('shift') === event.shiftKey;
25
+ const altMatch = modifiers.includes('alt') === event.altKey;
26
+ return keyMatch && cmdMatch && shiftMatch && altMatch;
27
+ }
28
+ /**
29
+ * Format a shortcut string for display
30
+ */
31
+ function formatShortcut(shortcut) {
32
+ const isMac = typeof navigator !== 'undefined' && /Mac/.test(navigator.platform);
33
+ return shortcut
34
+ .replace(/cmd/gi, isMac ? '\u2318' : 'Ctrl')
35
+ .replace(/shift/gi, '\u21E7')
36
+ .replace(/alt/gi, isMac ? '\u2325' : 'Alt')
37
+ .replace(/\+/g, ' ')
38
+ .replace(/([a-z])/gi, (match) => match.toUpperCase());
39
+ }
40
+ /**
41
+ * Format tool name to human-readable (fallback when no summary)
42
+ */
43
+ function formatToolName(toolName) {
44
+ // Convert snake_case or camelCase to human-readable
45
+ return toolName
46
+ .replace(/_/g, ' ')
47
+ .replace(/([A-Z])/g, ' $1')
48
+ .trim()
49
+ .toLowerCase()
50
+ .replace(/^./, (c) => c.toUpperCase());
51
+ }
52
+ /**
53
+ * Hook for managing AICommandBar state and behavior
54
+ */
55
+ function useAICommandBar(options) {
56
+ const { assistantId, apiKey: propsApiKey, baseUrl: propsBaseUrl, tenantId, tenantMetadata, options: barOptions = {}, isVisible: controlledVisible, onVisibilityChange, onExecute = 'callback', chatDrawerRef, onResponse, modelInterfaceTools = [], onSubmit, onToolCall, onError, onOpen, onClose, } = options;
57
+ const { shortcut } = barOptions;
58
+ // Get context
59
+ const context = useOptionalDevicContext();
60
+ const apiKey = propsApiKey || context?.apiKey;
61
+ const baseUrl = propsBaseUrl || context?.baseUrl || 'https://api.devic.ai';
62
+ const resolvedTenantId = tenantId || context?.tenantId;
63
+ const resolvedTenantMetadata = { ...context?.tenantMetadata, ...tenantMetadata };
64
+ // Visibility state
65
+ const [internalVisible, setInternalVisible] = useState(false);
66
+ const isVisible = controlledVisible ?? internalVisible;
67
+ // Input state
68
+ const [inputValue, setInputValue] = useState('');
69
+ const inputRef = useRef(null);
70
+ // Processing state
71
+ const [isProcessing, setIsProcessing] = useState(false);
72
+ const [toolCalls, setToolCalls] = useState([]);
73
+ const [currentToolSummary, setCurrentToolSummary] = useState(null);
74
+ // Result state
75
+ const [result, setResult] = useState(null);
76
+ const [chatUid, setChatUid] = useState(null);
77
+ const [error, setError] = useState(null);
78
+ // Polling state
79
+ const [shouldPoll, setShouldPoll] = useState(false);
80
+ // History state
81
+ const enableHistory = barOptions.enableHistory !== false; // default true
82
+ const maxHistoryItems = barOptions.maxHistoryItems ?? 50;
83
+ const historyStorageKey = barOptions.historyStorageKey ?? 'devic-command-bar-history';
84
+ const showHistoryCommand = barOptions.showHistoryCommand !== false; // default true
85
+ const [history, setHistory] = useState(() => {
86
+ if (!enableHistory || typeof window === 'undefined')
87
+ return [];
88
+ try {
89
+ const stored = localStorage.getItem(historyStorageKey);
90
+ return stored ? JSON.parse(stored) : [];
91
+ }
92
+ catch {
93
+ return [];
94
+ }
95
+ });
96
+ const [historyIndex, setHistoryIndex] = useState(-1);
97
+ const [showingHistory, setShowingHistory] = useState(false);
98
+ const [tempInput, setTempInput] = useState(''); // Store current input when navigating history
99
+ // Commands state
100
+ const commands = barOptions.commands ?? [];
101
+ const [selectedCommandIndex, setSelectedCommandIndex] = useState(0);
102
+ // Built-in history command
103
+ const historyCommand = useMemo(() => ({
104
+ keyword: 'history',
105
+ description: 'Show command history',
106
+ message: '', // Special handling
107
+ }), []);
108
+ // All available commands (user commands + built-in)
109
+ const allCommands = useMemo(() => {
110
+ const userCommands = commands;
111
+ // Add history command if enabled and not overwritten
112
+ if (showHistoryCommand && !userCommands.some(c => c.keyword === 'history')) {
113
+ return [...userCommands, historyCommand];
114
+ }
115
+ return userCommands;
116
+ }, [commands, showHistoryCommand, historyCommand]);
117
+ // Detect if showing command suggestions
118
+ const isCommandMode = inputValue.startsWith('/');
119
+ const commandQuery = isCommandMode ? inputValue.slice(1).toLowerCase() : '';
120
+ const showingCommands = isCommandMode && !isProcessing && !result;
121
+ // Filter commands based on query
122
+ const filteredCommands = useMemo(() => {
123
+ if (!showingCommands)
124
+ return [];
125
+ if (commandQuery === '')
126
+ return allCommands;
127
+ return allCommands.filter(cmd => cmd.keyword.toLowerCase().includes(commandQuery) ||
128
+ cmd.description.toLowerCase().includes(commandQuery));
129
+ }, [showingCommands, commandQuery, allCommands]);
130
+ // Reset command selection when filtered list changes
131
+ useEffect(() => {
132
+ setSelectedCommandIndex(0);
133
+ }, [filteredCommands.length]);
134
+ // Callback refs
135
+ const onErrorRef = useRef(onError);
136
+ const onResponseRef = useRef(onResponse);
137
+ const onToolCallRef = useRef(onToolCall);
138
+ const onSubmitRef = useRef(onSubmit);
139
+ const onOpenRef = useRef(onOpen);
140
+ const onCloseRef = useRef(onClose);
141
+ useEffect(() => {
142
+ onErrorRef.current = onError;
143
+ onResponseRef.current = onResponse;
144
+ onToolCallRef.current = onToolCall;
145
+ onSubmitRef.current = onSubmit;
146
+ onOpenRef.current = onOpen;
147
+ onCloseRef.current = onClose;
148
+ });
149
+ // API client
150
+ const clientRef = useRef(null);
151
+ if (!clientRef.current && apiKey) {
152
+ clientRef.current = new DevicApiClient({ apiKey, baseUrl });
153
+ }
154
+ useEffect(() => {
155
+ if (clientRef.current && apiKey) {
156
+ clientRef.current.setConfig({ apiKey, baseUrl });
157
+ }
158
+ }, [apiKey, baseUrl]);
159
+ // Model interface
160
+ const { toolSchemas, handleToolCalls: executeToolCalls, extractPendingToolCalls, } = useModelInterface({
161
+ tools: modelInterfaceTools,
162
+ onToolExecute: onToolCall,
163
+ });
164
+ // Visibility controls
165
+ const open = useCallback(() => {
166
+ setInternalVisible(true);
167
+ onVisibilityChange?.(true);
168
+ onOpenRef.current?.();
169
+ // Focus input after visibility change
170
+ setTimeout(() => inputRef.current?.focus(), 50);
171
+ }, [onVisibilityChange]);
172
+ const close = useCallback(() => {
173
+ setInternalVisible(false);
174
+ onVisibilityChange?.(false);
175
+ onCloseRef.current?.();
176
+ }, [onVisibilityChange]);
177
+ const toggle = useCallback(() => {
178
+ if (isVisible) {
179
+ close();
180
+ }
181
+ else {
182
+ open();
183
+ }
184
+ }, [isVisible, open, close]);
185
+ const focus = useCallback(() => {
186
+ inputRef.current?.focus();
187
+ }, []);
188
+ // Save history to localStorage
189
+ const saveHistory = useCallback((newHistory) => {
190
+ if (!enableHistory || typeof window === 'undefined')
191
+ return;
192
+ try {
193
+ localStorage.setItem(historyStorageKey, JSON.stringify(newHistory));
194
+ }
195
+ catch {
196
+ // Ignore localStorage errors
197
+ }
198
+ }, [enableHistory, historyStorageKey]);
199
+ // Add item to history
200
+ const addToHistory = useCallback((message) => {
201
+ if (!enableHistory || !message.trim() || message.startsWith('/'))
202
+ return;
203
+ setHistory(prev => {
204
+ // Don't add duplicates at the top
205
+ const filtered = prev.filter(item => item !== message);
206
+ const newHistory = [message, ...filtered].slice(0, maxHistoryItems);
207
+ saveHistory(newHistory);
208
+ return newHistory;
209
+ });
210
+ setHistoryIndex(-1);
211
+ }, [enableHistory, maxHistoryItems, saveHistory]);
212
+ // Clear history
213
+ const clearHistory = useCallback(() => {
214
+ setHistory([]);
215
+ setHistoryIndex(-1);
216
+ if (enableHistory && typeof window !== 'undefined') {
217
+ try {
218
+ localStorage.removeItem(historyStorageKey);
219
+ }
220
+ catch {
221
+ // Ignore
222
+ }
223
+ }
224
+ }, [enableHistory, historyStorageKey]);
225
+ // Navigate history
226
+ const navigateHistory = useCallback((direction) => {
227
+ if (!enableHistory || history.length === 0)
228
+ return;
229
+ if (direction === 'up') {
230
+ if (historyIndex === -1) {
231
+ // Save current input before navigating
232
+ setTempInput(inputValue);
233
+ setHistoryIndex(0);
234
+ setInputValue(history[0]);
235
+ }
236
+ else if (historyIndex < history.length - 1) {
237
+ const newIndex = historyIndex + 1;
238
+ setHistoryIndex(newIndex);
239
+ setInputValue(history[newIndex]);
240
+ }
241
+ }
242
+ else {
243
+ if (historyIndex > 0) {
244
+ const newIndex = historyIndex - 1;
245
+ setHistoryIndex(newIndex);
246
+ setInputValue(history[newIndex]);
247
+ }
248
+ else if (historyIndex === 0) {
249
+ setHistoryIndex(-1);
250
+ setInputValue(tempInput);
251
+ }
252
+ }
253
+ }, [enableHistory, history, historyIndex, inputValue, tempInput]);
254
+ // Ref to hold submit function (to avoid circular dependency)
255
+ const submitRef = useRef();
256
+ // Select a command
257
+ const selectCommand = useCallback((command) => {
258
+ if (command.keyword === 'history') {
259
+ // Special handling for history command
260
+ setShowingHistory(true);
261
+ setInputValue('');
262
+ }
263
+ else {
264
+ // Send the command's message
265
+ setInputValue('');
266
+ submitRef.current?.(command.message);
267
+ }
268
+ }, []);
269
+ // Navigate commands
270
+ const navigateCommands = useCallback((direction) => {
271
+ if (filteredCommands.length === 0)
272
+ return;
273
+ if (direction === 'down') {
274
+ setSelectedCommandIndex(prev => prev < filteredCommands.length - 1 ? prev + 1 : 0);
275
+ }
276
+ else {
277
+ setSelectedCommandIndex(prev => prev > 0 ? prev - 1 : filteredCommands.length - 1);
278
+ }
279
+ }, [filteredCommands.length]);
280
+ // Register keyboard shortcut
281
+ useEffect(() => {
282
+ if (!shortcut)
283
+ return;
284
+ const handler = (e) => {
285
+ if (matchShortcut(e, shortcut)) {
286
+ e.preventDefault();
287
+ toggle();
288
+ }
289
+ };
290
+ window.addEventListener('keydown', handler);
291
+ return () => window.removeEventListener('keydown', handler);
292
+ }, [shortcut, toggle]);
293
+ // Process tool calls from realtime data
294
+ const processToolCalls = useCallback((messages) => {
295
+ const summaries = [];
296
+ const toolResponseMap = new Map();
297
+ // Collect tool responses
298
+ for (const msg of messages) {
299
+ if (msg.role === 'tool' && msg.tool_call_id) {
300
+ toolResponseMap.set(msg.tool_call_id, msg.content);
301
+ }
302
+ }
303
+ // Collect tool calls from assistant messages
304
+ for (const msg of messages) {
305
+ if (msg.role === 'assistant' && msg.tool_calls?.length) {
306
+ for (const tc of msg.tool_calls) {
307
+ const hasResponse = toolResponseMap.has(tc.id);
308
+ let input;
309
+ try {
310
+ input = JSON.parse(tc.function.arguments || '{}');
311
+ }
312
+ catch {
313
+ input = {};
314
+ }
315
+ // Use message summary if available, otherwise format tool name
316
+ const summaryText = msg.summary || formatToolName(tc.function.name);
317
+ summaries.push({
318
+ id: tc.id,
319
+ name: tc.function.name,
320
+ status: hasResponse ? 'completed' : 'executing',
321
+ summary: summaryText,
322
+ input,
323
+ output: toolResponseMap.get(tc.id),
324
+ });
325
+ }
326
+ }
327
+ }
328
+ return summaries;
329
+ }, []);
330
+ // Handle pending client-side tool calls
331
+ const handlePendingToolCalls = useCallback(async (data) => {
332
+ if (!clientRef.current || !chatUid)
333
+ return;
334
+ const pendingCalls = data.pendingToolCalls || extractPendingToolCalls(data.chatHistory);
335
+ if (pendingCalls.length === 0)
336
+ return;
337
+ try {
338
+ const responses = await executeToolCalls(pendingCalls);
339
+ if (responses.length > 0) {
340
+ await clientRef.current.sendToolResponses(assistantId, chatUid, responses);
341
+ setShouldPoll(true);
342
+ }
343
+ }
344
+ catch (err) {
345
+ const error = err instanceof Error ? err : new Error(String(err));
346
+ setError(error);
347
+ onErrorRef.current?.(error);
348
+ }
349
+ }, [chatUid, assistantId, executeToolCalls, extractPendingToolCalls]);
350
+ // Polling
351
+ usePolling(shouldPoll ? chatUid : null, async () => {
352
+ if (!clientRef.current || !chatUid) {
353
+ throw new Error('Cannot poll without client or chatUid');
354
+ }
355
+ return clientRef.current.getRealtimeHistory(assistantId, chatUid);
356
+ }, {
357
+ interval: 1000,
358
+ enabled: shouldPoll,
359
+ stopStatuses: ['completed', 'error', 'waiting_for_tool_response'],
360
+ onUpdate: async (data) => {
361
+ // Update tool calls display
362
+ const summaries = processToolCalls(data.chatHistory);
363
+ setToolCalls(summaries);
364
+ // Update current tool summary
365
+ // Prefer showing an executing tool, but fall back to the last tool (completed or not)
366
+ if (summaries.length > 0) {
367
+ const lastExecuting = summaries.filter(s => s.status === 'executing').pop();
368
+ const lastTool = summaries[summaries.length - 1];
369
+ setCurrentToolSummary(lastExecuting?.summary || lastTool?.summary || null);
370
+ }
371
+ // Handle client-side tool calls
372
+ if (data.status === 'waiting_for_tool_response' || data.pendingToolCalls?.length) {
373
+ await handlePendingToolCalls(data);
374
+ }
375
+ },
376
+ onStop: (data) => {
377
+ setShouldPoll(false);
378
+ if (data?.status === 'error') {
379
+ setIsProcessing(false);
380
+ const err = new Error('Processing failed');
381
+ setError(err);
382
+ onErrorRef.current?.(err);
383
+ return;
384
+ }
385
+ if (data?.status === 'completed') {
386
+ setIsProcessing(false);
387
+ // Extract final assistant message
388
+ const assistantMessages = data.chatHistory.filter(m => m.role === 'assistant');
389
+ const lastAssistantMessage = assistantMessages[assistantMessages.length - 1];
390
+ if (lastAssistantMessage && chatUid) {
391
+ const commandResult = {
392
+ chatUid,
393
+ message: lastAssistantMessage,
394
+ toolCalls: processToolCalls(data.chatHistory),
395
+ };
396
+ setResult(commandResult);
397
+ // Handle execution mode
398
+ if (onExecute === 'openDrawer' && chatDrawerRef?.current) {
399
+ chatDrawerRef.current.setChatUid(chatUid);
400
+ chatDrawerRef.current.open();
401
+ // Close and reset the command bar when handing off to drawer
402
+ setResult(null);
403
+ setToolCalls([]);
404
+ setCurrentToolSummary(null);
405
+ setInternalVisible(false);
406
+ onVisibilityChange?.(false);
407
+ onCloseRef.current?.();
408
+ }
409
+ else {
410
+ onResponseRef.current?.(commandResult);
411
+ }
412
+ }
413
+ }
414
+ },
415
+ onError: (err) => {
416
+ setError(err);
417
+ setIsProcessing(false);
418
+ setShouldPoll(false);
419
+ onErrorRef.current?.(err);
420
+ },
421
+ });
422
+ // Submit message
423
+ const submit = useCallback(async (message) => {
424
+ const msg = message ?? inputValue;
425
+ if (!msg.trim())
426
+ return;
427
+ if (!clientRef.current) {
428
+ const err = new Error('API client not configured. Please provide an API key.');
429
+ setError(err);
430
+ onErrorRef.current?.(err);
431
+ return;
432
+ }
433
+ // Add to history before processing
434
+ addToHistory(msg);
435
+ // Clear input and start processing
436
+ setInputValue('');
437
+ setIsProcessing(true);
438
+ setError(null);
439
+ setResult(null);
440
+ setToolCalls([]);
441
+ setCurrentToolSummary(null);
442
+ setShowingHistory(false);
443
+ setHistoryIndex(-1);
444
+ onSubmitRef.current?.(msg);
445
+ try {
446
+ const dto = {
447
+ message: msg,
448
+ chatUid: chatUid || undefined,
449
+ metadata: resolvedTenantMetadata,
450
+ tenantId: resolvedTenantId,
451
+ ...(toolSchemas.length > 0 && { tools: toolSchemas }),
452
+ };
453
+ const response = await clientRef.current.sendMessageAsync(assistantId, dto);
454
+ if (response.chatUid && response.chatUid !== chatUid) {
455
+ setChatUid(response.chatUid);
456
+ }
457
+ setShouldPoll(true);
458
+ }
459
+ catch (err) {
460
+ const error = err instanceof Error ? err : new Error(String(err));
461
+ setError(error);
462
+ setIsProcessing(false);
463
+ onErrorRef.current?.(error);
464
+ }
465
+ }, [inputValue, chatUid, assistantId, resolvedTenantId, resolvedTenantMetadata, toolSchemas, addToHistory]);
466
+ // Update submit ref for use in selectCommand
467
+ submitRef.current = submit;
468
+ // Reset state
469
+ const reset = useCallback(() => {
470
+ setInputValue('');
471
+ setIsProcessing(false);
472
+ setToolCalls([]);
473
+ setCurrentToolSummary(null);
474
+ setResult(null);
475
+ setChatUid(null);
476
+ setError(null);
477
+ setShouldPoll(false);
478
+ }, []);
479
+ // Handle keyboard events
480
+ const handleKeyDown = useCallback((e) => {
481
+ // Handle Escape
482
+ if (e.key === 'Escape') {
483
+ e.preventDefault();
484
+ if (showingHistory) {
485
+ setShowingHistory(false);
486
+ return;
487
+ }
488
+ if (showingCommands) {
489
+ setInputValue('');
490
+ return;
491
+ }
492
+ // Reset if there's a result or if processing
493
+ if (result || isProcessing) {
494
+ reset();
495
+ }
496
+ close();
497
+ return;
498
+ }
499
+ // Handle arrow keys for commands
500
+ if (showingCommands && filteredCommands.length > 0) {
501
+ if (e.key === 'ArrowDown') {
502
+ e.preventDefault();
503
+ navigateCommands('down');
504
+ return;
505
+ }
506
+ if (e.key === 'ArrowUp') {
507
+ e.preventDefault();
508
+ navigateCommands('up');
509
+ return;
510
+ }
511
+ if (e.key === 'Enter' && !e.shiftKey) {
512
+ e.preventDefault();
513
+ const selectedCommand = filteredCommands[selectedCommandIndex];
514
+ if (selectedCommand) {
515
+ selectCommand(selectedCommand);
516
+ }
517
+ return;
518
+ }
519
+ if (e.key === 'Tab') {
520
+ e.preventDefault();
521
+ const selectedCommand = filteredCommands[selectedCommandIndex];
522
+ if (selectedCommand) {
523
+ setInputValue('/' + selectedCommand.keyword + ' ');
524
+ }
525
+ return;
526
+ }
527
+ }
528
+ // Handle arrow keys for history (only when not in command mode)
529
+ if (!showingCommands && !isProcessing) {
530
+ if (e.key === 'ArrowUp') {
531
+ e.preventDefault();
532
+ navigateHistory('up');
533
+ return;
534
+ }
535
+ if (e.key === 'ArrowDown' && historyIndex >= 0) {
536
+ e.preventDefault();
537
+ navigateHistory('down');
538
+ return;
539
+ }
540
+ }
541
+ // Handle Enter to submit
542
+ if (e.key === 'Enter' && !e.shiftKey && !isProcessing) {
543
+ e.preventDefault();
544
+ submit();
545
+ }
546
+ }, [
547
+ isProcessing,
548
+ result,
549
+ showingCommands,
550
+ showingHistory,
551
+ filteredCommands,
552
+ selectedCommandIndex,
553
+ historyIndex,
554
+ submit,
555
+ reset,
556
+ close,
557
+ navigateCommands,
558
+ navigateHistory,
559
+ selectCommand,
560
+ ]);
561
+ return {
562
+ isVisible,
563
+ open,
564
+ close,
565
+ toggle,
566
+ inputValue,
567
+ setInputValue,
568
+ inputRef,
569
+ focus,
570
+ isProcessing,
571
+ toolCalls,
572
+ currentToolSummary,
573
+ result,
574
+ chatUid,
575
+ error,
576
+ history,
577
+ historyIndex,
578
+ showingHistory,
579
+ setShowingHistory,
580
+ showingCommands,
581
+ filteredCommands,
582
+ selectedCommandIndex,
583
+ selectCommand,
584
+ submit,
585
+ reset,
586
+ handleKeyDown,
587
+ clearHistory,
588
+ };
589
+ }
590
+
591
+ export { formatShortcut, useAICommandBar };
592
+ //# sourceMappingURL=useAICommandBar.js.map