diff --git a/electron/main.ts b/electron/main.ts index 1165a590..60b18418 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -1385,7 +1385,8 @@ app.whenReady().then(async () => { let port: number; if (isDev) { - port = 3000; + const envPort = process.env.PORT ? parseInt(process.env.PORT, 10) : NaN; + port = Number.isNaN(envPort) ? 3000 : envPort; console.log(`Dev mode: connecting to http://127.0.0.1:${port}`); serverPort = port; createWindow(`http://127.0.0.1:${port}`); diff --git a/src/hooks/useSlashCommands.ts b/src/hooks/useSlashCommands.ts index f320f123..42f5f762 100644 --- a/src/hooks/useSlashCommands.ts +++ b/src/hooks/useSlashCommands.ts @@ -196,9 +196,15 @@ export function useSlashCommands(opts: { // Block during streaming — badges dispatch as slash/skill prompts, not queueable if (isStreaming) { closePopover(); return; } setBadge(result.badge!); - setInputValue(''); + setInputValue(result.newInputValue ?? ''); closePopover(); - setTimeout(() => textareaRef.current?.focus(), 0); + setTimeout(() => { + const el = textareaRef.current; + if (!el) return; + el.focus(); + const pos = el.value.length; + el.setSelectionRange(pos, pos); + }, 0); return; case 'insert_file_mention': diff --git a/src/lib/message-input-logic.ts b/src/lib/message-input-logic.ts index 213a2c94..925c88d0 100644 --- a/src/lib/message-input-logic.ts +++ b/src/lib/message-input-logic.ts @@ -84,6 +84,21 @@ export function filterItems(items: PopoverItem[], filter: string): PopoverItem[] ); } +/** + * Splits input text around a popover trigger, removing the trigger character + * and any filter text that was typed after it. + */ +function splitAroundTrigger( + inputValue: string, + triggerPos: number, + popoverFilter: string, +): { before: string; after: string } { + const before = inputValue.slice(0, triggerPos); + const cursorEnd = triggerPos + popoverFilter.length + 1; // +1 to consume the trigger character + const after = inputValue.slice(cursorEnd); + return { before, after }; +} + /** * Determines what happens when an item is selected from the popover. * Used by insertItem in useSlashCommands. @@ -100,8 +115,9 @@ export function resolveItemSelection( return { action: 'immediate_command', commandValue: item.value }; } - // Non-immediate commands: show as badge + // Non-immediate commands: show as badge, preserving any text outside the trigger if (popoverMode === 'skill') { + const { before, after } = splitAroundTrigger(inputValue, triggerPos, popoverFilter); return { action: 'set_badge', badge: { @@ -111,13 +127,12 @@ export function resolveItemSelection( kind: item.kind || 'slash_command', installedSource: item.installedSource, }, + newInputValue: before + after, }; } // File mention: insert into text - const before = inputValue.slice(0, triggerPos); - const cursorEnd = triggerPos + popoverFilter.length + 1; - const after = inputValue.slice(cursorEnd); + const { before, after } = splitAroundTrigger(inputValue, triggerPos, popoverFilter); const insertText = `@${item.value} `; return { action: 'insert_file_mention',