fix(transcription): neutralize legacy items() to dedupe @mention fetch

Tiptap's suggestion items() callback fired a fetch on every keystroke
after `@`, in parallel with the debounced search-input fetch. Its result
was discarded by updateState, so it was pure waste — doubling the load
on /api/persons and confusing the debounce.

Returning [] from items() routes the entire fetch flow through the
search-input -> debounced onSearch path. New test pins @Walter to
exactly one fetch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-19 22:11:17 +02:00
parent 896d34cfcd
commit ecc4d1aa67
2 changed files with 25 additions and 10 deletions

View File

@@ -195,6 +195,24 @@ describe('PersonMentionEditor — AC-2/3: search input drives fetch', () => {
expect(fetchesAfterSearch).toBe(1);
});
it('fires exactly one /api/persons fetch when the user types @Walter (debounced)', async () => {
const fetchMock = vi
.fn()
.mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue([AUGUSTE]) });
vi.stubGlobal('fetch', fetchMock);
renderHost();
await userEvent.type(page.getByRole('textbox'), '@Walter');
// Wait beyond the 150 ms debounce window so the trailing call has flushed.
await new Promise((r) => setTimeout(r, 300));
const personsFetches = fetchMock.mock.calls.filter(
([url]) => typeof url === 'string' && url.startsWith('/api/persons')
);
expect(personsFetches.length).toBe(1);
});
it('clearing the search input clears the list without firing a fetch', async () => {
const fetchMock = vi
.fn()