diff --git a/apps/desktop/src/app/chat/composer/text-utils.test.ts b/apps/desktop/src/app/chat/composer/text-utils.test.ts index f80e6db4385..6c6a20780f6 100644 --- a/apps/desktop/src/app/chat/composer/text-utils.test.ts +++ b/apps/desktop/src/app/chat/composer/text-utils.test.ts @@ -46,6 +46,14 @@ describe('detectTrigger', () => { expect(detectTrigger('/path/to/file')).toBeNull() }) + it('does not trigger slash popover mid-message', () => { + expect(detectTrigger('hello /')).toBeNull() + expect(detectTrigger('hello /skill')).toBeNull() + expect(detectTrigger('hello there /personality alic')).toBeNull() + expect(detectTrigger('text\n/skill')).toBeNull() + expect(detectTrigger('multi word message /')).toBeNull() + }) + it('still anchors at-mention triggers strictly at the token edge', () => { expect(detectTrigger('@file:path with space')).toBeNull() }) diff --git a/apps/desktop/src/app/chat/composer/text-utils.ts b/apps/desktop/src/app/chat/composer/text-utils.ts index 4535d6963c3..b9b6adc07f1 100644 --- a/apps/desktop/src/app/chat/composer/text-utils.ts +++ b/apps/desktop/src/app/chat/composer/text-utils.ts @@ -11,8 +11,12 @@ export interface TriggerState { // user types args (`/personality alic` → arg completer suggests `alice`). // Restricting the slash command name to `[a-zA-Z][\w-]*` avoids matching file // paths like `src/foo/bar`. +// +// Slash commands only execute at the beginning of a message, so the `/` +// trigger is anchored strictly at position 0 — not after whitespace — to +// avoid opening the popover mid-message (e.g. `hello /`). const AT_TRIGGER_RE = /(?:^|[\s])(@)([^\s@/]*)$/ -const SLASH_TRIGGER_RE = /(?:^|[\s])(\/)((?:[a-zA-Z][\w-]*(?:\s+\S*)*)?)$/ +const SLASH_TRIGGER_RE = /^(\/)((?:[a-zA-Z][\w-]*(?:\s+\S*)*)?)$/ /** Stable key for paste dedupe — `items` and `files` often mirror the same image as different objects. */ export function blobDedupeKey(blob: Blob): string {