diff --git a/apps/web/src/composer-logic.test.ts b/apps/web/src/composer-logic.test.ts index df3e54b4ddfc7b1eab6e260fcf5a420327c2c4af..7ebdf6ba1e88af04243b8b351e4820074a78448e 100644 --- a/apps/web/src/composer-logic.test.ts +++ b/apps/web/src/composer-logic.test.ts @@ -246,6 +246,31 @@ describe("composerSubmissionIntentForKey", () => { }); describe("detectComposerTrigger", () => { + it.each(["Use ", "Use\t", "Use\n "])("detects inline slash skills after %j", (prefix) => { + const text = `${prefix}/skill:review later`; + const cursor = `${prefix}/skill:review`.length; + expect(detectComposerTrigger(text, cursor)).toEqual({ + kind: "slash-command", + query: "skill:review", + rangeStart: prefix.length, + rangeEnd: cursor, + }); + }); + + it.each(["Use /", "Use /review"])("detects the inline slash token in %j", (text) => { + expect(detectComposerTrigger(text, text.length)).toEqual({ + kind: "slash-command", + query: text.slice(5), + rangeStart: 4, + rangeEnd: text.length, + }); + }); + + it.each(["Use/review", "https://example.com/review", "Use /review "])( + "does not trigger for %j", + (text) => expect(detectComposerTrigger(text, text.length)).toBeNull(), + ); + it("detects @path trigger at cursor", () => { const text = "Please check @src/com"; const trigger = detectComposerTrigger(text, text.length); diff --git a/apps/web/src/composer-logic.ts b/apps/web/src/composer-logic.ts index a44b999f2ba63a6c5a3e76b380b8336f8baadd6b..2dc9f6d08f8182b23e2bec4906e91920bd0077ce 100644 --- a/apps/web/src/composer-logic.ts +++ b/apps/web/src/composer-logic.ts @@ -258,24 +258,16 @@ export function isCollapsedCursorAdjacentToInlineToken( export function detectComposerTrigger(text: string, cursorInput: number): ComposerTrigger | null { const cursor = clampCursor(text, cursorInput); - const lineStart = text.lastIndexOf("\n", Math.max(0, cursor - 1)) + 1; - const linePrefix = text.slice(lineStart, cursor); - - if (linePrefix.startsWith("/")) { - const commandMatch = /^\/(\S*)$/.exec(linePrefix); - if (commandMatch) { - const commandQuery = commandMatch[1] ?? ""; - return { - kind: "slash-command", - query: commandQuery, - rangeStart: lineStart, - rangeEnd: cursor, - }; - } - } - const tokenStart = tokenStartForCursor(text, cursor); const token = text.slice(tokenStart, cursor); + if (token.startsWith("/")) { + return { + kind: "slash-command", + query: token.slice(1), + rangeStart: tokenStart, + rangeEnd: cursor, + }; + } const pullRequestMatch = /^#([\p{L}\p{N}][\p{L}\p{N}_-]*)?$/u.exec(token); if (pullRequestMatch) { return { diff --git a/packages/shared/src/composerTrigger.test.ts b/packages/shared/src/composerTrigger.test.ts index 06b7038dad2b23824fb7ad9381ea3c4163e586b8..f783b4765b9dede20ed1a29f69e31dc54648eeb2 100644 --- a/packages/shared/src/composerTrigger.test.ts +++ b/packages/shared/src/composerTrigger.test.ts @@ -3,6 +3,31 @@ import { describe, expect, it } from "vite-plus/test"; import { detectComposerTrigger, serializeComposerFileLink } from "./composerTrigger.ts"; describe("detectComposerTrigger", () => { + it.each(["Use ", "Use\t", "Use\n "])("detects inline slash skills after %j", (prefix) => { + const text = `${prefix}/skill:review later`; + const cursor = `${prefix}/skill:review`.length; + expect(detectComposerTrigger(text, cursor)).toEqual({ + kind: "slash-command", + query: "skill:review", + rangeStart: prefix.length, + rangeEnd: cursor, + }); + }); + + it.each(["Use /", "Use /review"])("detects the inline slash token in %j", (text) => { + expect(detectComposerTrigger(text, text.length)).toEqual({ + kind: "slash-command", + query: text.slice(5), + rangeStart: 4, + rangeEnd: text.length, + }); + }); + + it.each(["Use/review", "https://example.com/review", "Use /review "])( + "does not trigger for %j", + (text) => expect(detectComposerTrigger(text, text.length)).toBeNull(), + ); + it.each(["$", "€", "£", "¥", "₹", "₩", "₿", "𑿝"])( "detects %s skill prefixes and their source range", (prefix) => { diff --git a/packages/shared/src/composerTrigger.ts b/packages/shared/src/composerTrigger.ts index 6153978edb92a931a8a76ef7327f5e717935c1db..9143f27172e11dac875121454688226e2834d59c 100644 --- a/packages/shared/src/composerTrigger.ts +++ b/packages/shared/src/composerTrigger.ts @@ -100,6 +100,14 @@ export function detectComposerTrigger( const tokenStart = tokenIdx + 1; const token = text.slice(tokenStart, cursor); + if (token.startsWith("/")) { + return { + kind: "slash-command", + query: token.slice(1), + rangeStart: tokenStart, + rangeEnd: cursor, + }; + } const pullRequestMatch = /^#([\p{L}\p{N}][\p{L}\p{N}_-]*)?$/u.exec(token); if (pullRequestMatch) return {