From f7c9feb395caa27ec79386b1ed3ae7b4675486a1 Mon Sep 17 00:00:00 2001 From: ethernet Date: Fri, 10 Jul 2026 13:24:40 -0400 Subject: [PATCH] fix(desktop): only show slash popover when / is first char MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SLASH_TRIGGER_RE regex used (?:^|[\s]) as its left anchor, so typing a / anywhere in the message (e.g. "hello /") opened the slash command popover — even though slash commands only execute at the beginning of a message. Anchor the regex strictly at position 0 (^) so the popover only appears when / is the first character, matching the actual execution semantics. The @-mention trigger is left untouched since those work anywhere in the text. --- apps/desktop/src/app/chat/composer/text-utils.test.ts | 8 ++++++++ apps/desktop/src/app/chat/composer/text-utils.ts | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) 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 {