feat(mention): add detectPersonMention with multi-word query support
Comment mentions stop at a space; person mentions must accept spaces because historical display names are commonly multi-word. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
53
frontend/src/lib/utils/personMention.spec.ts
Normal file
53
frontend/src/lib/utils/personMention.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { detectPersonMention } from './personMention';
|
||||
|
||||
describe('detectPersonMention', () => {
|
||||
it('returns null when text has no @', () => {
|
||||
expect(detectPersonMention('hello world', 11)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when @ is preceded by a non-whitespace character (email pattern)', () => {
|
||||
expect(detectPersonMention('user@example', 12)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns query for @ at the very start of string', () => {
|
||||
expect(detectPersonMention('@Aug', 4)).toBe('Aug');
|
||||
});
|
||||
|
||||
it('returns empty string immediately after @', () => {
|
||||
expect(detectPersonMention('@', 1)).toBe('');
|
||||
});
|
||||
|
||||
it('returns single-word query', () => {
|
||||
expect(detectPersonMention('hi @Auguste', 11)).toBe('Auguste');
|
||||
});
|
||||
|
||||
it('keeps the trigger active when the query has a trailing space', () => {
|
||||
expect(detectPersonMention('hi @Auguste ', 12)).toBe('Auguste ');
|
||||
});
|
||||
|
||||
it('returns multi-word query (spaces allowed)', () => {
|
||||
expect(detectPersonMention('hi @Auguste Raddatz', 19)).toBe('Auguste Raddatz');
|
||||
});
|
||||
|
||||
it('returns single-character query', () => {
|
||||
expect(detectPersonMention('@M', 2)).toBe('M');
|
||||
});
|
||||
|
||||
it('returns null when the query crosses a newline', () => {
|
||||
expect(detectPersonMention('@Aug\nfoo', 8)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when a second @ appears in the query (next mention starts)', () => {
|
||||
expect(detectPersonMention('@Aug@bar', 8)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when cursor is before the @', () => {
|
||||
expect(detectPersonMention('@Hans', 0)).toBeNull();
|
||||
});
|
||||
|
||||
it('uses the most recent @ in the text', () => {
|
||||
// cursor is just after the second @ + a few chars
|
||||
expect(detectPersonMention('hi @Anna and @Bert', 18)).toBe('Bert');
|
||||
});
|
||||
});
|
||||
23
frontend/src/lib/utils/personMention.ts
Normal file
23
frontend/src/lib/utils/personMention.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Given the current textarea value and cursor position, returns the
|
||||
* @-person-mention query being typed (the text after the last triggering @),
|
||||
* or null if no person-mention is active.
|
||||
*
|
||||
* Rules — distinct from comment-mentions in `mention.ts`:
|
||||
* - @ must be at the start of the string or preceded by whitespace
|
||||
* - The query may contain spaces (historical persons commonly have multi-word
|
||||
* display names — "Auguste Raddatz", "Maria von Müller-Schultz")
|
||||
* - The query stops at a newline or at a second @ (the next mention starts)
|
||||
*/
|
||||
export function detectPersonMention(text: string, cursorPos: number): string | null {
|
||||
const before = text.slice(0, cursorPos);
|
||||
const atIndex = before.lastIndexOf('@');
|
||||
if (atIndex === -1) return null;
|
||||
|
||||
if (atIndex > 0 && !/\s/.test(before[atIndex - 1])) return null;
|
||||
|
||||
const query = before.slice(atIndex + 1);
|
||||
if (query.includes('\n') || query.includes('@')) return null;
|
||||
|
||||
return query;
|
||||
}
|
||||
Reference in New Issue
Block a user