Fix desktop slash commands (#8341)

Signed-off-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-04-06 11:31:08 -04:00
committed by GitHub
parent e8c66f8c71
commit 771bc38731
2 changed files with 58 additions and 41 deletions
+52 -41
View File
@@ -17,6 +17,10 @@ const i18n = defineMessages({
id: 'mentionPopover.scanningFiles', id: 'mentionPopover.scanningFiles',
defaultMessage: 'Scanning files...', defaultMessage: 'Scanning files...',
}, },
loadingCommands: {
id: 'mentionPopover.loadingCommands',
defaultMessage: 'Loading commands...',
},
itemsFound: { itemsFound: {
id: 'mentionPopover.itemsFound', id: 'mentionPopover.itemsFound',
defaultMessage: '{count, plural, one {# item} other {# items}} found', defaultMessage: '{count, plural, one {# item} other {# items}} found',
@@ -25,6 +29,10 @@ const i18n = defineMessages({
id: 'mentionPopover.noItemsFound', id: 'mentionPopover.noItemsFound',
defaultMessage: 'No items found matching "{query}"', defaultMessage: 'No items found matching "{query}"',
}, },
noCommandsFound: {
id: 'mentionPopover.noCommandsFound',
defaultMessage: 'No commands found matching "{query}"',
},
}); });
type DisplayItemType = CommandType | 'Directory' | 'File'; type DisplayItemType = CommandType | 'Directory' | 'File';
@@ -374,30 +382,11 @@ const MentionPopover = forwardRef<
[] []
); );
const scanFilesFromRoot = useCallback(async () => { const getDefaultStartPath = (): string => {
setIsLoading(true); if (window.electron.platform === 'win32') return 'C:\\Users';
try { if (window.electron.platform === 'linux') return '/home';
let startPath = currentWorkingDir; return '/Users';
};
if (!startPath) {
if (window.electron.platform === 'win32') {
startPath = 'C:\\Users';
} else if (window.electron.platform === 'linux') {
startPath = '/home';
} else {
startPath = '/Users'; // Default to macOS
}
}
const scannedFiles = await scanDirectoryFromRoot(startPath);
setItems(scannedFiles);
} catch (error) {
console.error('Error scanning items from root:', error);
setItems([]);
} finally {
setIsLoading(false);
}
}, [scanDirectoryFromRoot, currentWorkingDir]);
const compareByType = (a: DisplayItemWithMatch, b: DisplayItemWithMatch) => { const compareByType = (a: DisplayItemWithMatch, b: DisplayItemWithMatch) => {
const orderA = typeOrder[a.itemType] ?? Number.MAX_SAFE_INTEGER; const orderA = typeOrder[a.itemType] ?? Number.MAX_SAFE_INTEGER;
@@ -485,28 +474,50 @@ const MentionPopover = forwardRef<
); );
useEffect(() => { useEffect(() => {
let cancelled = false;
const loadData = async () => { const loadData = async () => {
if (isSlashCommand) { setItems([]);
const response = await getSlashCommands({ setIsLoading(true);
query: { working_dir: currentWorkingDir }, try {
throwOnError: true, if (isSlashCommand) {
}); const response = await getSlashCommands({
const commandItems: DisplayItem[] = (response.data?.commands || []).map((cmd) => ({ query: { working_dir: currentWorkingDir },
name: cmd.command, throwOnError: true,
extra: cmd.help, });
itemType: cmd.command_type, if (cancelled) return;
relativePath: cmd.command, const commandItems: DisplayItem[] = (response.data?.commands || []).map((cmd) => ({
})); name: cmd.command,
setItems(commandItems); extra: cmd.help,
} else { itemType: cmd.command_type,
await scanFilesFromRoot(); relativePath: cmd.command,
}));
setItems(commandItems);
} else {
const scannedFiles = await scanDirectoryFromRoot(currentWorkingDir || getDefaultStartPath());
if (cancelled) return;
setItems(scannedFiles);
}
} catch (error) {
if (!cancelled) {
console.error('Error loading popover items:', error);
setItems([]);
}
} finally {
if (!cancelled) {
setIsLoading(false);
}
} }
}; };
if (isOpen) { if (isOpen) {
loadData(); loadData();
} }
}, [isOpen, isSlashCommand, scanFilesFromRoot, currentWorkingDir]);
return () => {
cancelled = true;
};
}, [isOpen, isSlashCommand, scanDirectoryFromRoot, currentWorkingDir]);
useEffect(() => { useEffect(() => {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
@@ -561,7 +572,7 @@ const MentionPopover = forwardRef<
{isLoading ? ( {isLoading ? (
<div className="flex items-center justify-center py-4"> <div className="flex items-center justify-center py-4">
<div className="animate-spin rounded-full h-4 w-4 border-t-2 border-b-2"></div> <div className="animate-spin rounded-full h-4 w-4 border-t-2 border-b-2"></div>
<span className="ml-2 text-sm text-text-secondary">{intl.formatMessage(i18n.scanningFiles)}</span> <span className="ml-2 text-sm text-text-secondary">{intl.formatMessage(isSlashCommand ? i18n.loadingCommands : i18n.scanningFiles)}</span>
</div> </div>
) : ( ) : (
<> <>
@@ -596,7 +607,7 @@ const MentionPopover = forwardRef<
{!isLoading && displayItems.length === 0 && query && ( {!isLoading && displayItems.length === 0 && query && (
<div className="p-4 text-center text-text-secondary text-sm"> <div className="p-4 text-center text-text-secondary text-sm">
{intl.formatMessage(i18n.noItemsFound, { query })} {intl.formatMessage(isSlashCommand ? i18n.noCommandsFound : i18n.noItemsFound, { query })}
</div> </div>
)} )}
</div> </div>
+6
View File
@@ -2021,6 +2021,12 @@
"mentionPopover.itemsFound": { "mentionPopover.itemsFound": {
"defaultMessage": "{count,plural,one{# item found} other{# items found}}" "defaultMessage": "{count,plural,one{# item found} other{# items found}}"
}, },
"mentionPopover.loadingCommands": {
"defaultMessage": "Loading commands..."
},
"mentionPopover.noCommandsFound": {
"defaultMessage": "No commands found matching \"{query}\""
},
"mentionPopover.noItemsFound": { "mentionPopover.noItemsFound": {
"defaultMessage": "No items found matching \"{query}\"" "defaultMessage": "No items found matching \"{query}\""
}, },