diff --git a/frontend/src/lib/shared/discussion/MentionDropdown.svelte b/frontend/src/lib/shared/discussion/MentionDropdown.svelte index 07c0f628..5e6c2157 100644 --- a/frontend/src/lib/shared/discussion/MentionDropdown.svelte +++ b/frontend/src/lib/shared/discussion/MentionDropdown.svelte @@ -20,17 +20,25 @@ type DropdownState = { let { model, - initialQuery = '', + editorQuery = '', onSearch = () => {} }: { model: DropdownState; - initialQuery?: string; + /** Text typed after `@` in the host editor. Mirrors into the search input + * until the user takes manual ownership by typing into the input itself. */ + editorQuery?: string; onSearch?: (query: string) => void; } = $props(); -// initialQuery is a one-shot prop — PersonMentionEditor mounts a fresh dropdown -// with the typed text on each Tiptap onStart, so we deliberately snapshot here. -let searchQuery = $state(untrack(() => initialQuery)); +let searchQuery = $state(untrack(() => editorQuery)); +let userHasEdited = $state(false); + +// Mirror the editor's typed text until the user takes ownership. +$effect(() => { + if (!userHasEdited) { + searchQuery = editorQuery; + } +}); // highlightedIndex must be both writable (keyboard handler mutates it) and // reset when `items` changes (so it never points past the end of a new list). @@ -153,15 +161,24 @@ function selectItem(item: Person) { class="min-h-[44px] w-full bg-transparent font-sans text-sm text-ink placeholder:text-ink-3 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-inset" placeholder={m.person_mention_search_prompt()} bind:value={searchQuery} - oninput={(e) => onSearch(e.currentTarget.value)} + oninput={(e) => { + userHasEdited = true; + onSearch(e.currentTarget.value); + }} onmousedown={(e) => e.stopPropagation()} /> {#if model.items.length === 0} -

- {m.person_mention_popup_empty()} -

+ {#if searchQuery.trim() === ''} +

+ {m.person_mention_search_prompt()} +

+ {:else} +

+ {m.person_mention_popup_empty()} +

+ {/if}