centaurus-cli 2.8.9 → 2.9.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/dist/cli-adapter.d.ts +21 -2
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +302 -68
- package/dist/cli-adapter.js.map +1 -1
- package/dist/config/models.d.ts +8 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +29 -0
- package/dist/config/models.js.map +1 -1
- package/dist/config/slash-commands.d.ts +1 -0
- package/dist/config/slash-commands.d.ts.map +1 -1
- package/dist/config/slash-commands.js +13 -1
- package/dist/config/slash-commands.js.map +1 -1
- package/dist/hooks/useConnectivity.d.ts +2 -0
- package/dist/hooks/useConnectivity.d.ts.map +1 -0
- package/dist/hooks/useConnectivity.js +12 -0
- package/dist/hooks/useConnectivity.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/mcp-command-handler.d.ts.map +1 -1
- package/dist/mcp/mcp-command-handler.js +0 -3
- package/dist/mcp/mcp-command-handler.js.map +1 -1
- package/dist/mcp/mcp-tool-wrapper.d.ts.map +1 -1
- package/dist/mcp/mcp-tool-wrapper.js +8 -0
- package/dist/mcp/mcp-tool-wrapper.js.map +1 -1
- package/dist/services/ai-service-client.d.ts +1 -0
- package/dist/services/ai-service-client.d.ts.map +1 -1
- package/dist/services/ai-service-client.js.map +1 -1
- package/dist/services/api-client.d.ts +26 -40
- package/dist/services/api-client.d.ts.map +1 -1
- package/dist/services/api-client.js +32 -34
- package/dist/services/api-client.js.map +1 -1
- package/dist/services/connectivity-manager.d.ts +18 -0
- package/dist/services/connectivity-manager.d.ts.map +1 -0
- package/dist/services/connectivity-manager.js +72 -0
- package/dist/services/connectivity-manager.js.map +1 -0
- package/dist/services/local-chat-storage.d.ts +5 -0
- package/dist/services/local-chat-storage.d.ts.map +1 -1
- package/dist/services/local-chat-storage.js +33 -0
- package/dist/services/local-chat-storage.js.map +1 -1
- package/dist/tools/background-command.d.ts +11 -0
- package/dist/tools/background-command.d.ts.map +1 -0
- package/dist/tools/background-command.js +162 -0
- package/dist/tools/background-command.js.map +1 -0
- package/dist/tools/command.d.ts.map +1 -1
- package/dist/tools/command.js +6 -3
- package/dist/tools/command.js.map +1 -1
- package/dist/tools/create-image.d.ts +10 -0
- package/dist/tools/create-image.d.ts.map +1 -0
- package/dist/tools/create-image.js +189 -0
- package/dist/tools/create-image.js.map +1 -0
- package/dist/ui/components/App.d.ts +3 -2
- package/dist/ui/components/App.d.ts.map +1 -1
- package/dist/ui/components/App.js +127 -44
- package/dist/ui/components/App.js.map +1 -1
- package/dist/ui/components/ContextWindowIndicator.d.ts.map +1 -1
- package/dist/ui/components/ContextWindowIndicator.js +43 -22
- package/dist/ui/components/ContextWindowIndicator.js.map +1 -1
- package/dist/ui/components/InputBox.d.ts.map +1 -1
- package/dist/ui/components/InputBox.js +198 -199
- package/dist/ui/components/InputBox.js.map +1 -1
- package/dist/ui/components/MessageDisplay.d.ts.map +1 -1
- package/dist/ui/components/MessageDisplay.js +8 -15
- package/dist/ui/components/MessageDisplay.js.map +1 -1
- package/dist/ui/components/SlashCommandAutocomplete.d.ts +2 -0
- package/dist/ui/components/SlashCommandAutocomplete.d.ts.map +1 -1
- package/dist/ui/components/SlashCommandAutocomplete.js +19 -10
- package/dist/ui/components/SlashCommandAutocomplete.js.map +1 -1
- package/dist/ui/components/StatusBar.d.ts.map +1 -1
- package/dist/ui/components/StatusBar.js +4 -0
- package/dist/ui/components/StatusBar.js.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.d.ts.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.js +153 -39
- package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
- package/dist/ui/components/ToolExecutionStatus.d.ts.map +1 -1
- package/dist/ui/components/ToolExecutionStatus.js +1 -0
- package/dist/ui/components/ToolExecutionStatus.js.map +1 -1
- package/dist/utils/chat-formatter.d.ts +12 -0
- package/dist/utils/chat-formatter.d.ts.map +1 -0
- package/dist/utils/chat-formatter.js +326 -0
- package/dist/utils/chat-formatter.js.map +1 -0
- package/dist/utils/input-classifier.d.ts.map +1 -1
- package/dist/utils/input-classifier.js +139 -20
- package/dist/utils/input-classifier.js.map +1 -1
- package/dist/utils/text-clipboard.d.ts +12 -0
- package/dist/utils/text-clipboard.d.ts.map +1 -0
- package/dist/utils/text-clipboard.js +63 -0
- package/dist/utils/text-clipboard.js.map +1 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef, useMemo } from 'react';
|
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
+
import { useConnectivity } from '../../hooks/useConnectivity.js';
|
|
5
6
|
import { Breadcrumbs } from './Breadcrumbs.js';
|
|
6
7
|
import { ContextWindowIndicator } from './ContextWindowIndicator.js';
|
|
7
8
|
import { logDebug } from '../../utils/logger.js';
|
|
@@ -9,9 +10,9 @@ import { detectIntent } from '../../utils/input-classifier.js';
|
|
|
9
10
|
import { CommandHistoryManager } from '../../utils/command-history.js';
|
|
10
11
|
import { SlashCommandAutocomplete } from './SlashCommandAutocomplete.js';
|
|
11
12
|
import { FileTagAutocomplete } from './FileTagAutocomplete.js';
|
|
12
|
-
import { ClipboardImageAutocomplete } from './ClipboardImageAutocomplete.js';
|
|
13
13
|
import { filterCommands } from '../../config/slash-commands.js';
|
|
14
14
|
import { getClipboardImages } from '../../services/clipboard-service.js';
|
|
15
|
+
import { useTerminalDimensions, TERMINAL_HEIGHT_CONSTANTS } from '../../hooks/useTerminalDimensions.js';
|
|
15
16
|
const getVisualLines = (text, width) => {
|
|
16
17
|
const logicalLines = text.split('\n');
|
|
17
18
|
const visualLines = [];
|
|
@@ -58,6 +59,10 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
58
59
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
59
60
|
const [tempValue, setTempValue] = useState('');
|
|
60
61
|
const ignoreNextChangeRef = useRef(false);
|
|
62
|
+
// Refs to track current value and cursor for paste handling
|
|
63
|
+
// This prevents stale closure issues when Ink calls useInput multiple times during paste
|
|
64
|
+
const valueRef = useRef(initialValue);
|
|
65
|
+
const cursorOffsetRef = useRef(0);
|
|
61
66
|
// Auto Mode State
|
|
62
67
|
const [isAutoMode, setIsAutoMode] = useState(true);
|
|
63
68
|
const [detectedIntent, setDetectedIntent] = useState('ai');
|
|
@@ -74,30 +79,34 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
74
79
|
const [slashAutocompleteVisible, setSlashAutocompleteVisible] = useState(false);
|
|
75
80
|
const [slashAutocompleteCommands, setSlashAutocompleteCommands] = useState([]);
|
|
76
81
|
const [slashAutocompleteSelectedIndex, setSlashAutocompleteSelectedIndex] = useState(0);
|
|
82
|
+
const [slashAutocompleteScrollOffset, setSlashAutocompleteScrollOffset] = useState(0);
|
|
83
|
+
// Connectivity State
|
|
84
|
+
const isConnected = useConnectivity();
|
|
85
|
+
// Terminal dimensions for height-aware autocomplete
|
|
86
|
+
const dimensions = useTerminalDimensions();
|
|
87
|
+
// Max 5 items, min 0 for very small terminals (use MIN_ROWS_FOR_STREAMING as threshold)
|
|
88
|
+
const slashMaxVisibleItems = dimensions.rows < TERMINAL_HEIGHT_CONSTANTS.MIN_ROWS_FOR_STREAMING
|
|
89
|
+
? 0
|
|
90
|
+
: Math.min(5, Math.max(1, Math.floor((dimensions.rows - 20) / 3)));
|
|
77
91
|
// File Tag Autocomplete State (@ symbol)
|
|
78
92
|
const [fileTagAutocompleteVisible, setFileTagAutocompleteVisible] = useState(false);
|
|
79
93
|
const [fileTagSuggestions, setFileTagSuggestions] = useState([]);
|
|
80
94
|
const [fileTagSelectedIndex, setFileTagSelectedIndex] = useState(0);
|
|
81
95
|
const [activeFileTagStart, setActiveFileTagStart] = useState(null);
|
|
82
96
|
const [confirmedFileTags, setConfirmedFileTags] = useState([]);
|
|
83
|
-
// Clipboard Image State (
|
|
84
|
-
const [clipboardAutocompleteVisible, setClipboardAutocompleteVisible] = useState(false);
|
|
85
|
-
const [clipboardImages, setClipboardImages] = useState([]);
|
|
86
|
-
const [clipboardSelectedIndex, setClipboardSelectedIndex] = useState(0);
|
|
87
|
-
const [clipboardLoading, setClipboardLoading] = useState(false);
|
|
88
|
-
const [activeClipboardStart, setActiveClipboardStart] = useState(null);
|
|
97
|
+
// Clipboard Image State (Alt+V paste)
|
|
89
98
|
const [confirmedClipboardImages, setConfirmedClipboardImages] = useState([]);
|
|
90
|
-
// Track positions of validated #image commands (for pink highlighting)
|
|
91
|
-
const [validatedImagePositions, setValidatedImagePositions] = useState([]);
|
|
92
99
|
// Track visual line count to force re-renders when text wraps
|
|
93
100
|
// This is necessary because Ink doesn't automatically update layout when text wraps
|
|
94
101
|
const [visualLineCount, setVisualLineCount] = useState(1);
|
|
95
102
|
// Configuration for scrolling
|
|
96
103
|
const MAX_VISIBLE_LINES = 9;
|
|
97
|
-
// Wrapper for setValue that also notifies parent of changes
|
|
104
|
+
// Wrapper for setValue that also notifies parent of changes and updates ref
|
|
98
105
|
const setValue = React.useCallback((newValue) => {
|
|
99
106
|
setValueInternal(prev => {
|
|
100
107
|
const resolvedValue = typeof newValue === 'function' ? newValue(prev) : newValue;
|
|
108
|
+
// Update ref synchronously for paste handling
|
|
109
|
+
valueRef.current = resolvedValue;
|
|
101
110
|
// Notify parent of value change for preservation across screen transitions
|
|
102
111
|
if (onValueChange) {
|
|
103
112
|
onValueChange(resolvedValue);
|
|
@@ -105,12 +114,29 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
105
114
|
return resolvedValue;
|
|
106
115
|
});
|
|
107
116
|
}, [onValueChange]);
|
|
117
|
+
// Wrapper for setCursorOffset that also updates ref
|
|
118
|
+
const setCursorOffsetWithRef = React.useCallback((newOffset) => {
|
|
119
|
+
setCursorOffset(prev => {
|
|
120
|
+
const resolvedOffset = typeof newOffset === 'function' ? newOffset(prev) : newOffset;
|
|
121
|
+
// Update ref synchronously for paste handling
|
|
122
|
+
cursorOffsetRef.current = resolvedOffset;
|
|
123
|
+
return resolvedOffset;
|
|
124
|
+
});
|
|
125
|
+
}, []);
|
|
108
126
|
// Initialize cursor position when initialValue is provided
|
|
109
127
|
useEffect(() => {
|
|
110
128
|
if (initialValue && initialValue.length > 0) {
|
|
111
129
|
setCursorOffset(initialValue.length);
|
|
130
|
+
cursorOffsetRef.current = initialValue.length;
|
|
112
131
|
}
|
|
113
132
|
}, []); // Only run on mount
|
|
133
|
+
// Keep refs in sync with state (for cases where state is updated directly)
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
valueRef.current = value;
|
|
136
|
+
}, [value]);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
cursorOffsetRef.current = cursorOffset;
|
|
139
|
+
}, [cursorOffset]);
|
|
114
140
|
// Load history on mount
|
|
115
141
|
useEffect(() => {
|
|
116
142
|
CommandHistoryManager.getInstance().load();
|
|
@@ -289,127 +315,6 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
289
315
|
setActiveFileTagStart(null);
|
|
290
316
|
}
|
|
291
317
|
}, [value, cursorOffset, commandMode, currentWorkingDirectory]);
|
|
292
|
-
// Hash (#) symbol detection effect for clipboard image autocomplete
|
|
293
|
-
// When user types # in Agent mode, check clipboard for images and show dropdown
|
|
294
|
-
useEffect(() => {
|
|
295
|
-
// Don't show in command mode
|
|
296
|
-
if (commandMode) {
|
|
297
|
-
setClipboardAutocompleteVisible(false);
|
|
298
|
-
setActiveClipboardStart(null);
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
// Find if cursor is right after a # character (or typing after #)
|
|
302
|
-
let hashPosition = -1;
|
|
303
|
-
for (let i = cursorOffset - 1; i >= 0; i--) {
|
|
304
|
-
const char = value[i];
|
|
305
|
-
// Stop if we hit whitespace
|
|
306
|
-
if (/[\s\n]/.test(char))
|
|
307
|
-
break;
|
|
308
|
-
if (char === '#') {
|
|
309
|
-
hashPosition = i;
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
// Only treat # as image trigger if it's at start of input OR preceded by whitespace
|
|
314
|
-
if (hashPosition === -1 || (hashPosition > 0 && !/[\s\n]/.test(value[hashPosition - 1]))) {
|
|
315
|
-
setClipboardAutocompleteVisible(false);
|
|
316
|
-
setActiveClipboardStart(null);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
// Extract what's typed after #
|
|
320
|
-
const query = value.slice(hashPosition + 1, cursorOffset).toLowerCase();
|
|
321
|
-
// If #image is fully typed (with space or at end), hide dropdown (validation will handle it)
|
|
322
|
-
if (query === 'image' || query.startsWith('image ')) {
|
|
323
|
-
setClipboardAutocompleteVisible(false);
|
|
324
|
-
setActiveClipboardStart(null);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
// If user has typed something that doesn't match "image" prefix, hide dropdown
|
|
328
|
-
if (query.length > 0 && !'image'.startsWith(query)) {
|
|
329
|
-
setClipboardAutocompleteVisible(false);
|
|
330
|
-
setActiveClipboardStart(null);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
// Show dropdown and check clipboard
|
|
334
|
-
setActiveClipboardStart(hashPosition);
|
|
335
|
-
setClipboardLoading(true);
|
|
336
|
-
setClipboardAutocompleteVisible(true);
|
|
337
|
-
// Check clipboard asynchronously
|
|
338
|
-
const checkClipboard = async () => {
|
|
339
|
-
try {
|
|
340
|
-
const images = await getClipboardImages();
|
|
341
|
-
setClipboardImages(images);
|
|
342
|
-
setClipboardLoading(false);
|
|
343
|
-
setClipboardSelectedIndex(0);
|
|
344
|
-
}
|
|
345
|
-
catch (error) {
|
|
346
|
-
logDebug(`Failed to check clipboard: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
347
|
-
setClipboardImages([]);
|
|
348
|
-
setClipboardLoading(false);
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
checkClipboard();
|
|
352
|
-
}, [value, cursorOffset, commandMode]);
|
|
353
|
-
// #image command detection effect
|
|
354
|
-
// When user types #image, check clipboard for images and validate
|
|
355
|
-
useEffect(() => {
|
|
356
|
-
// Don't check in command mode
|
|
357
|
-
if (commandMode)
|
|
358
|
-
return;
|
|
359
|
-
// Find all #image occurrences in the text
|
|
360
|
-
const regex = /#image\b/gi;
|
|
361
|
-
const matches = [];
|
|
362
|
-
let match;
|
|
363
|
-
while ((match = regex.exec(value)) !== null) {
|
|
364
|
-
matches.push({ start: match.index, end: match.index + match[0].length });
|
|
365
|
-
}
|
|
366
|
-
// If no #image in text, clear validated positions
|
|
367
|
-
if (matches.length === 0) {
|
|
368
|
-
if (validatedImagePositions.length > 0) {
|
|
369
|
-
setValidatedImagePositions([]);
|
|
370
|
-
setConfirmedClipboardImages([]);
|
|
371
|
-
}
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
// Check for new #image occurrences that haven't been validated yet
|
|
375
|
-
const unvalidatedMatches = matches.filter(m => !validatedImagePositions.some(v => v.start === m.start && v.end === m.end));
|
|
376
|
-
if (unvalidatedMatches.length === 0)
|
|
377
|
-
return;
|
|
378
|
-
// Check clipboard and validate new #image occurrences
|
|
379
|
-
const validateImages = async () => {
|
|
380
|
-
logDebug(`Found ${unvalidatedMatches.length} new #image occurrences to validate`);
|
|
381
|
-
try {
|
|
382
|
-
const images = await getClipboardImages();
|
|
383
|
-
logDebug(`Clipboard check returned ${images.length} images`);
|
|
384
|
-
if (images.length > 0) {
|
|
385
|
-
const image = images[0];
|
|
386
|
-
logDebug(`Image found in clipboard: ${image.displayName}, ${image.sizeBytes} bytes`);
|
|
387
|
-
// Add the clipboard image once for EACH #image occurrence
|
|
388
|
-
// This allows multiple #image tags in the same prompt to each attach the clipboard image
|
|
389
|
-
// Each #image tag represents an intent to include the image at that location
|
|
390
|
-
setConfirmedClipboardImages(prev => {
|
|
391
|
-
// Create a unique copy of the image for each #image occurrence
|
|
392
|
-
const newImages = unvalidatedMatches.map((_, index) => ({
|
|
393
|
-
...image,
|
|
394
|
-
// Give each a unique id so they're treated as separate entries for upload
|
|
395
|
-
id: `${image.id}_${Date.now()}_${index}`
|
|
396
|
-
}));
|
|
397
|
-
return [...prev, ...newImages];
|
|
398
|
-
});
|
|
399
|
-
// Mark all unvalidated positions as validated
|
|
400
|
-
setValidatedImagePositions(prev => [...prev, ...unvalidatedMatches]);
|
|
401
|
-
logDebug(`Validated ${unvalidatedMatches.length} #image occurrences, added ${unvalidatedMatches.length} images to queue`);
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
logDebug('No image in clipboard - #image command not validated');
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
catch (error) {
|
|
408
|
-
logDebug(`Failed to check clipboard: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
validateImages();
|
|
412
|
-
}, [value, commandMode]);
|
|
413
318
|
const pushToUndoStack = () => {
|
|
414
319
|
setUndoStack(prev => [...prev, { value, cursorOffset }]);
|
|
415
320
|
setRedoStack([]); // Clear redo stack on new action
|
|
@@ -433,11 +338,21 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
433
338
|
// Handle slash command autocomplete navigation
|
|
434
339
|
if (slashAutocompleteVisible) {
|
|
435
340
|
if (key.downArrow) {
|
|
436
|
-
|
|
341
|
+
const newIndex = Math.min(slashAutocompleteSelectedIndex + 1, slashAutocompleteCommands.length - 1);
|
|
342
|
+
setSlashAutocompleteSelectedIndex(newIndex);
|
|
343
|
+
// Scroll down if selected is below visible window
|
|
344
|
+
if (newIndex >= slashAutocompleteScrollOffset + slashMaxVisibleItems) {
|
|
345
|
+
setSlashAutocompleteScrollOffset(newIndex - slashMaxVisibleItems + 1);
|
|
346
|
+
}
|
|
437
347
|
return;
|
|
438
348
|
}
|
|
439
349
|
if (key.upArrow) {
|
|
440
|
-
|
|
350
|
+
const newIndex = Math.max(slashAutocompleteSelectedIndex - 1, 0);
|
|
351
|
+
setSlashAutocompleteSelectedIndex(newIndex);
|
|
352
|
+
// Scroll up if selected is above visible window
|
|
353
|
+
if (newIndex < slashAutocompleteScrollOffset) {
|
|
354
|
+
setSlashAutocompleteScrollOffset(newIndex);
|
|
355
|
+
}
|
|
441
356
|
return;
|
|
442
357
|
}
|
|
443
358
|
if (key.return && input.length <= 1 && !key.shift && !key.ctrl) {
|
|
@@ -474,6 +389,13 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
474
389
|
setCursorOffset(newValue.length);
|
|
475
390
|
setSlashAutocompleteVisible(false);
|
|
476
391
|
}
|
|
392
|
+
else if (value.startsWith('/sync ')) {
|
|
393
|
+
// We're selecting a sync subcommand
|
|
394
|
+
const newValue = `/sync ${selected.name} `;
|
|
395
|
+
setValue(newValue);
|
|
396
|
+
setCursorOffset(newValue.length);
|
|
397
|
+
setSlashAutocompleteVisible(false);
|
|
398
|
+
}
|
|
477
399
|
else {
|
|
478
400
|
// Regular slash command, replace everything
|
|
479
401
|
const newValue = `/${selected.name} `;
|
|
@@ -486,6 +408,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
486
408
|
if (subcommandMatches.length > 0) {
|
|
487
409
|
setSlashAutocompleteCommands(subcommandMatches);
|
|
488
410
|
setSlashAutocompleteSelectedIndex(0);
|
|
411
|
+
setSlashAutocompleteScrollOffset(0);
|
|
489
412
|
// Keep autocomplete visible for subcommands
|
|
490
413
|
}
|
|
491
414
|
else {
|
|
@@ -497,6 +420,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
497
420
|
if (subcommandMatches.length > 0) {
|
|
498
421
|
setSlashAutocompleteCommands(subcommandMatches);
|
|
499
422
|
setSlashAutocompleteSelectedIndex(0);
|
|
423
|
+
setSlashAutocompleteScrollOffset(0);
|
|
500
424
|
// Keep autocomplete visible for subcommands
|
|
501
425
|
}
|
|
502
426
|
else {
|
|
@@ -508,6 +432,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
508
432
|
if (subcommandMatches.length > 0) {
|
|
509
433
|
setSlashAutocompleteCommands(subcommandMatches);
|
|
510
434
|
setSlashAutocompleteSelectedIndex(0);
|
|
435
|
+
setSlashAutocompleteScrollOffset(0);
|
|
511
436
|
// Keep autocomplete visible for subcommands
|
|
512
437
|
}
|
|
513
438
|
else {
|
|
@@ -519,6 +444,19 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
519
444
|
if (subcommandMatches.length > 0) {
|
|
520
445
|
setSlashAutocompleteCommands(subcommandMatches);
|
|
521
446
|
setSlashAutocompleteSelectedIndex(0);
|
|
447
|
+
setSlashAutocompleteScrollOffset(0);
|
|
448
|
+
// Keep autocomplete visible for subcommands
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
setSlashAutocompleteVisible(false);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
else if (selected.name === 'sync') {
|
|
455
|
+
const subcommandMatches = filterCommands('sync ');
|
|
456
|
+
if (subcommandMatches.length > 0) {
|
|
457
|
+
setSlashAutocompleteCommands(subcommandMatches);
|
|
458
|
+
setSlashAutocompleteSelectedIndex(0);
|
|
459
|
+
setSlashAutocompleteScrollOffset(0);
|
|
522
460
|
// Keep autocomplete visible for subcommands
|
|
523
461
|
}
|
|
524
462
|
else {
|
|
@@ -609,54 +547,36 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
609
547
|
return;
|
|
610
548
|
}
|
|
611
549
|
}
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
550
|
+
// Alt+V: Paste image from clipboard
|
|
551
|
+
// Detect Alt+V on Windows/Linux (key.meta is often Alt on Windows in Ink)
|
|
552
|
+
const isAltV = (key.meta && input === 'v') || (input === '√'); // '√' is Alt+V on some systems
|
|
553
|
+
if (isAltV && !commandMode) {
|
|
554
|
+
// Check clipboard for images asynchronously
|
|
555
|
+
(async () => {
|
|
556
|
+
try {
|
|
557
|
+
const images = await getClipboardImages();
|
|
558
|
+
if (images.length > 0) {
|
|
559
|
+
const image = images[0];
|
|
560
|
+
logDebug(`Alt+V: Image found in clipboard: ${image.displayName}, ${image.sizeBytes} bytes`);
|
|
561
|
+
// Add image to confirmed list with unique ID
|
|
562
|
+
setConfirmedClipboardImages(prev => [
|
|
563
|
+
...prev,
|
|
564
|
+
{
|
|
565
|
+
...image,
|
|
566
|
+
id: `${image.id}_${Date.now()}`
|
|
567
|
+
}
|
|
568
|
+
]);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
logDebug('Alt+V: No image in clipboard');
|
|
572
|
+
}
|
|
635
573
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (key.escape) {
|
|
639
|
-
setClipboardAutocompleteVisible(false);
|
|
640
|
-
setActiveClipboardStart(null);
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
// Tab also selects
|
|
644
|
-
if (key.tab && !key.shift) {
|
|
645
|
-
if (clipboardImages.length > 0 && activeClipboardStart !== null) {
|
|
646
|
-
pushToUndoStack();
|
|
647
|
-
const beforeHash = value.slice(0, activeClipboardStart);
|
|
648
|
-
const afterCursor = value.slice(cursorOffset);
|
|
649
|
-
const newValue = beforeHash + '#image ' + afterCursor;
|
|
650
|
-
const newCursorPos = activeClipboardStart + 7;
|
|
651
|
-
setValue(newValue);
|
|
652
|
-
setCursorOffset(newCursorPos);
|
|
653
|
-
setClipboardAutocompleteVisible(false);
|
|
654
|
-
setActiveClipboardStart(null);
|
|
574
|
+
catch (error) {
|
|
575
|
+
logDebug(`Alt+V: Failed to check clipboard: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
655
576
|
}
|
|
656
|
-
|
|
657
|
-
|
|
577
|
+
})();
|
|
578
|
+
return;
|
|
658
579
|
}
|
|
659
|
-
// Clipboard image paste is handled via Ctrl+V below
|
|
660
580
|
// DELETE WORD BACKWARDS - Check this FIRST before standard backspace/delete
|
|
661
581
|
// Triggers on any of these conditions:
|
|
662
582
|
// 1. Ctrl+W (char code 23) - Standard Unix terminal shortcut
|
|
@@ -690,6 +610,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
690
610
|
setSlashAutocompleteCommands(matches);
|
|
691
611
|
setSlashAutocompleteVisible(true);
|
|
692
612
|
setSlashAutocompleteSelectedIndex(0);
|
|
613
|
+
setSlashAutocompleteScrollOffset(0);
|
|
693
614
|
}
|
|
694
615
|
else {
|
|
695
616
|
setSlashAutocompleteVisible(false);
|
|
@@ -703,6 +624,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
703
624
|
setSlashAutocompleteCommands(matches);
|
|
704
625
|
setSlashAutocompleteVisible(true);
|
|
705
626
|
setSlashAutocompleteSelectedIndex(0);
|
|
627
|
+
setSlashAutocompleteScrollOffset(0);
|
|
706
628
|
}
|
|
707
629
|
else {
|
|
708
630
|
setSlashAutocompleteVisible(false);
|
|
@@ -716,6 +638,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
716
638
|
setSlashAutocompleteCommands(matches);
|
|
717
639
|
setSlashAutocompleteVisible(true);
|
|
718
640
|
setSlashAutocompleteSelectedIndex(0);
|
|
641
|
+
setSlashAutocompleteScrollOffset(0);
|
|
719
642
|
}
|
|
720
643
|
else {
|
|
721
644
|
setSlashAutocompleteVisible(false);
|
|
@@ -729,6 +652,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
729
652
|
setSlashAutocompleteCommands(matches);
|
|
730
653
|
setSlashAutocompleteVisible(true);
|
|
731
654
|
setSlashAutocompleteSelectedIndex(0);
|
|
655
|
+
setSlashAutocompleteScrollOffset(0);
|
|
732
656
|
}
|
|
733
657
|
else {
|
|
734
658
|
setSlashAutocompleteVisible(false);
|
|
@@ -742,6 +666,21 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
742
666
|
setSlashAutocompleteCommands(matches);
|
|
743
667
|
setSlashAutocompleteVisible(true);
|
|
744
668
|
setSlashAutocompleteSelectedIndex(0);
|
|
669
|
+
setSlashAutocompleteScrollOffset(0);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
setSlashAutocompleteVisible(false);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
else if (newValue.startsWith('/sync ')) {
|
|
676
|
+
// Sync subcommands
|
|
677
|
+
const fullQuery = newValue.slice(1);
|
|
678
|
+
const matches = filterCommands(fullQuery);
|
|
679
|
+
if (matches.length > 0) {
|
|
680
|
+
setSlashAutocompleteCommands(matches);
|
|
681
|
+
setSlashAutocompleteVisible(true);
|
|
682
|
+
setSlashAutocompleteSelectedIndex(0);
|
|
683
|
+
setSlashAutocompleteScrollOffset(0);
|
|
745
684
|
}
|
|
746
685
|
else {
|
|
747
686
|
setSlashAutocompleteVisible(false);
|
|
@@ -813,7 +752,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
813
752
|
setCursorOffset(value.length);
|
|
814
753
|
return;
|
|
815
754
|
}
|
|
816
|
-
// Note: Clipboard images are handled via
|
|
755
|
+
// Note: Clipboard images are handled via Alt+V keyboard shortcut
|
|
817
756
|
// DELETE CHAR - Only runs if Delete Word did NOT trigger
|
|
818
757
|
// Triggers on:
|
|
819
758
|
// 1. Backspace or Delete key flag is present
|
|
@@ -853,6 +792,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
853
792
|
setSlashAutocompleteCommands(matches);
|
|
854
793
|
setSlashAutocompleteVisible(true);
|
|
855
794
|
setSlashAutocompleteSelectedIndex(0);
|
|
795
|
+
setSlashAutocompleteScrollOffset(0);
|
|
856
796
|
}
|
|
857
797
|
else {
|
|
858
798
|
setSlashAutocompleteVisible(false);
|
|
@@ -866,6 +806,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
866
806
|
setSlashAutocompleteCommands(matches);
|
|
867
807
|
setSlashAutocompleteVisible(true);
|
|
868
808
|
setSlashAutocompleteSelectedIndex(0);
|
|
809
|
+
setSlashAutocompleteScrollOffset(0);
|
|
869
810
|
}
|
|
870
811
|
else {
|
|
871
812
|
setSlashAutocompleteVisible(false);
|
|
@@ -879,6 +820,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
879
820
|
setSlashAutocompleteCommands(matches);
|
|
880
821
|
setSlashAutocompleteVisible(true);
|
|
881
822
|
setSlashAutocompleteSelectedIndex(0);
|
|
823
|
+
setSlashAutocompleteScrollOffset(0);
|
|
882
824
|
}
|
|
883
825
|
else {
|
|
884
826
|
setSlashAutocompleteVisible(false);
|
|
@@ -892,6 +834,35 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
892
834
|
setSlashAutocompleteCommands(matches);
|
|
893
835
|
setSlashAutocompleteVisible(true);
|
|
894
836
|
setSlashAutocompleteSelectedIndex(0);
|
|
837
|
+
setSlashAutocompleteScrollOffset(0);
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
setSlashAutocompleteVisible(false);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
else if (newValue.startsWith('/background-task ') || newValue.startsWith('/bkg ') || newValue.startsWith('/bg-task ')) {
|
|
844
|
+
// Background-task subcommands
|
|
845
|
+
const fullQuery = newValue.slice(1);
|
|
846
|
+
const matches = filterCommands(fullQuery);
|
|
847
|
+
if (matches.length > 0) {
|
|
848
|
+
setSlashAutocompleteCommands(matches);
|
|
849
|
+
setSlashAutocompleteVisible(true);
|
|
850
|
+
setSlashAutocompleteSelectedIndex(0);
|
|
851
|
+
setSlashAutocompleteScrollOffset(0);
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
setSlashAutocompleteVisible(false);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
else if (newValue.startsWith('/sync ')) {
|
|
858
|
+
// Sync subcommands
|
|
859
|
+
const fullQuery = newValue.slice(1);
|
|
860
|
+
const matches = filterCommands(fullQuery);
|
|
861
|
+
if (matches.length > 0) {
|
|
862
|
+
setSlashAutocompleteCommands(matches);
|
|
863
|
+
setSlashAutocompleteVisible(true);
|
|
864
|
+
setSlashAutocompleteSelectedIndex(0);
|
|
865
|
+
setSlashAutocompleteScrollOffset(0);
|
|
895
866
|
}
|
|
896
867
|
else {
|
|
897
868
|
setSlashAutocompleteVisible(false);
|
|
@@ -930,6 +901,16 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
930
901
|
}
|
|
931
902
|
else {
|
|
932
903
|
// Enter: Submit
|
|
904
|
+
// Check if submission is allowed when disconnected
|
|
905
|
+
// Allowed: /exit command, OR Command Mode is active, OR detected intent is 'command'
|
|
906
|
+
const isExitCommand = value.trim() === '/exit';
|
|
907
|
+
const isCommandIntent = detectedIntent === 'command';
|
|
908
|
+
const isAllowedOffline = isConnected || isExitCommand || commandMode || isCommandIntent;
|
|
909
|
+
if (!isAllowedOffline) {
|
|
910
|
+
setRejectFlash(true);
|
|
911
|
+
setTimeout(() => setRejectFlash(false), 1000);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
933
914
|
handleSubmit();
|
|
934
915
|
}
|
|
935
916
|
return;
|
|
@@ -1125,21 +1106,28 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1125
1106
|
pushToUndoStack();
|
|
1126
1107
|
// Handle paste with newlines
|
|
1127
1108
|
const cleanedInput = input.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
1128
|
-
|
|
1129
|
-
|
|
1109
|
+
// Use refs to get the latest value and cursor position
|
|
1110
|
+
// This prevents stale closure issues when Ink calls useInput multiple times during paste
|
|
1111
|
+
const currentValue = valueRef.current;
|
|
1112
|
+
const currentCursorOffset = cursorOffsetRef.current;
|
|
1113
|
+
let newValue = currentValue;
|
|
1114
|
+
let newOffset = currentCursorOffset;
|
|
1130
1115
|
if (selection) {
|
|
1131
1116
|
const start = Math.min(selection.start, selection.end);
|
|
1132
1117
|
const end = Math.max(selection.start, selection.end);
|
|
1133
|
-
newValue =
|
|
1118
|
+
newValue = currentValue.slice(0, start) + cleanedInput + currentValue.slice(end);
|
|
1134
1119
|
newOffset = start + cleanedInput.length;
|
|
1135
1120
|
setSelection(null);
|
|
1136
1121
|
}
|
|
1137
1122
|
else {
|
|
1138
|
-
newValue =
|
|
1139
|
-
newOffset =
|
|
1123
|
+
newValue = currentValue.slice(0, currentCursorOffset) + cleanedInput + currentValue.slice(currentCursorOffset);
|
|
1124
|
+
newOffset = currentCursorOffset + cleanedInput.length;
|
|
1140
1125
|
}
|
|
1126
|
+
// Update refs immediately for subsequent paste chunks
|
|
1127
|
+
valueRef.current = newValue;
|
|
1128
|
+
cursorOffsetRef.current = newOffset;
|
|
1141
1129
|
setValue(newValue);
|
|
1142
|
-
|
|
1130
|
+
setCursorOffsetWithRef(newOffset);
|
|
1143
1131
|
// Reset history/completions
|
|
1144
1132
|
setHistoryIndex(-1);
|
|
1145
1133
|
setCompletions([]);
|
|
@@ -1152,6 +1140,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1152
1140
|
setSlashAutocompleteCommands(matches);
|
|
1153
1141
|
setSlashAutocompleteVisible(true);
|
|
1154
1142
|
setSlashAutocompleteSelectedIndex(0);
|
|
1143
|
+
setSlashAutocompleteScrollOffset(0);
|
|
1155
1144
|
}
|
|
1156
1145
|
else {
|
|
1157
1146
|
setSlashAutocompleteVisible(false);
|
|
@@ -1165,6 +1154,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1165
1154
|
setSlashAutocompleteCommands(matches);
|
|
1166
1155
|
setSlashAutocompleteVisible(true);
|
|
1167
1156
|
setSlashAutocompleteSelectedIndex(0);
|
|
1157
|
+
setSlashAutocompleteScrollOffset(0);
|
|
1168
1158
|
}
|
|
1169
1159
|
else {
|
|
1170
1160
|
setSlashAutocompleteVisible(false);
|
|
@@ -1178,6 +1168,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1178
1168
|
setSlashAutocompleteCommands(matches);
|
|
1179
1169
|
setSlashAutocompleteVisible(true);
|
|
1180
1170
|
setSlashAutocompleteSelectedIndex(0);
|
|
1171
|
+
setSlashAutocompleteScrollOffset(0);
|
|
1181
1172
|
}
|
|
1182
1173
|
else {
|
|
1183
1174
|
setSlashAutocompleteVisible(false);
|
|
@@ -1191,6 +1182,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1191
1182
|
setSlashAutocompleteCommands(matches);
|
|
1192
1183
|
setSlashAutocompleteVisible(true);
|
|
1193
1184
|
setSlashAutocompleteSelectedIndex(0);
|
|
1185
|
+
setSlashAutocompleteScrollOffset(0);
|
|
1194
1186
|
}
|
|
1195
1187
|
else {
|
|
1196
1188
|
setSlashAutocompleteVisible(false);
|
|
@@ -1204,6 +1196,21 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1204
1196
|
setSlashAutocompleteCommands(matches);
|
|
1205
1197
|
setSlashAutocompleteVisible(true);
|
|
1206
1198
|
setSlashAutocompleteSelectedIndex(0);
|
|
1199
|
+
setSlashAutocompleteScrollOffset(0);
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
setSlashAutocompleteVisible(false);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
else if (newValue.startsWith('/sync ')) {
|
|
1206
|
+
// Sync subcommands (when user types "/sync ")
|
|
1207
|
+
const fullQuery = newValue.slice(1); // Remove leading "/", pass "sync <subquery>" to filterCommands
|
|
1208
|
+
const matches = filterCommands(fullQuery);
|
|
1209
|
+
if (matches.length > 0) {
|
|
1210
|
+
setSlashAutocompleteCommands(matches);
|
|
1211
|
+
setSlashAutocompleteVisible(true);
|
|
1212
|
+
setSlashAutocompleteSelectedIndex(0);
|
|
1213
|
+
setSlashAutocompleteScrollOffset(0);
|
|
1207
1214
|
}
|
|
1208
1215
|
else {
|
|
1209
1216
|
setSlashAutocompleteVisible(false);
|
|
@@ -1453,17 +1460,6 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1453
1460
|
if (isInConfirmedFileTag || isInActiveFileTag) {
|
|
1454
1461
|
return React.createElement(Text, { key: charIdx, color: "#00ccff", bold: true }, char);
|
|
1455
1462
|
}
|
|
1456
|
-
// Check if this character is part of a validated #image command
|
|
1457
|
-
let isInValidatedImage = false;
|
|
1458
|
-
for (const pos of validatedImagePositions) {
|
|
1459
|
-
if (absPos >= pos.start && absPos < pos.end) {
|
|
1460
|
-
isInValidatedImage = true;
|
|
1461
|
-
break;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
if (isInValidatedImage) {
|
|
1465
|
-
return React.createElement(Text, { key: charIdx, color: "#ff69b4", bold: true }, char);
|
|
1466
|
-
}
|
|
1467
1463
|
return React.createElement(Text, { key: charIdx }, char);
|
|
1468
1464
|
});
|
|
1469
1465
|
// Handle cursor at end of line
|
|
@@ -1501,6 +1497,10 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1501
1497
|
React.createElement(Text, { color: "#00ccff", bold: true }, "Auto ["),
|
|
1502
1498
|
detectedIntent === 'command' ? (React.createElement(Text, { color: "#00cc66", bold: true }, "Terminal")) : (React.createElement(Text, { color: "#00ccff" }, "Agent")),
|
|
1503
1499
|
React.createElement(Text, { color: "#00ccff", bold: true }, "]"))) : backgroundMode ? (React.createElement(Text, { color: "#9966ff", bold: true }, "Background")) : commandMode ? (React.createElement(Text, { color: "#00cc66", bold: true }, "Terminal")) : planMode ? (React.createElement(Text, { color: "#ffaa00", bold: true }, "Plan")) : (React.createElement(Text, { color: "#00ccff" }, "Agent"))))),
|
|
1500
|
+
confirmedClipboardImages.length > 0 && (React.createElement(Box, { marginBottom: 1, flexDirection: "row", flexWrap: "wrap", gap: 1 }, confirmedClipboardImages.map((img, index) => (React.createElement(Box, { key: img.id, borderStyle: "round", borderColor: "#ff69b4", paddingX: 1 },
|
|
1501
|
+
React.createElement(Text, { color: "#ff69b4", bold: true },
|
|
1502
|
+
"image_",
|
|
1503
|
+
index + 1)))))),
|
|
1504
1504
|
React.createElement(Box, { flexDirection: "row", width: "100%" },
|
|
1505
1505
|
React.createElement(Text, { color: "#666666" }, "> "),
|
|
1506
1506
|
renderInput()),
|
|
@@ -1515,8 +1515,7 @@ export const InputBox = React.memo(({ onSubmit, placeholder = 'Ask anything...',
|
|
|
1515
1515
|
!commandMode && autoAcceptMode ? (React.createElement(Text, { color: "#00cc66", bold: true }, "[AUTO-ACCEPT: ON]")) : !commandMode ? (React.createElement(Text, { color: "#666666", dimColor: true }, "[AUTO-ACCEPT: OFF]")) : null,
|
|
1516
1516
|
!commandMode && (React.createElement(Box, { marginLeft: 1 },
|
|
1517
1517
|
React.createElement(ContextWindowIndicator, { currentTokens: currentTokens, maxTokens: maxTokens }))))),
|
|
1518
|
-
slashAutocompleteVisible && (React.createElement(SlashCommandAutocomplete, { commands: slashAutocompleteCommands, selectedIndex: slashAutocompleteSelectedIndex })),
|
|
1519
|
-
fileTagAutocompleteVisible && (React.createElement(FileTagAutocomplete, { files: fileTagSuggestions, selectedIndex: fileTagSelectedIndex }))
|
|
1520
|
-
clipboardAutocompleteVisible && (React.createElement(ClipboardImageAutocomplete, { images: clipboardImages, selectedIndex: clipboardSelectedIndex, isLoading: clipboardLoading }))));
|
|
1518
|
+
slashAutocompleteVisible && slashMaxVisibleItems > 0 && (React.createElement(SlashCommandAutocomplete, { commands: slashAutocompleteCommands, selectedIndex: slashAutocompleteSelectedIndex, maxVisibleItems: slashMaxVisibleItems, scrollOffset: slashAutocompleteScrollOffset })),
|
|
1519
|
+
fileTagAutocompleteVisible && (React.createElement(FileTagAutocomplete, { files: fileTagSuggestions, selectedIndex: fileTagSelectedIndex }))));
|
|
1521
1520
|
});
|
|
1522
1521
|
//# sourceMappingURL=InputBox.js.map
|