From ee90b05159ee2e98a808668979850056f4d76d2d Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 27 Apr 2026 20:36:59 +0200 Subject: [PATCH] fix: guard inline input types --- src/components/WikilinkChatInput.test.tsx | 21 +++++++++++++++++ src/components/inlineWikilinkBeforeInput.ts | 25 ++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/components/WikilinkChatInput.test.tsx b/src/components/WikilinkChatInput.test.tsx index 1980a263..b31f2404 100644 --- a/src/components/WikilinkChatInput.test.tsx +++ b/src/components/WikilinkChatInput.test.tsx @@ -549,6 +549,7 @@ describe('WikilinkChatInput', () => { it('treats missing inputType as a non-insert beforeinput event', () => { expect(() => isInsertBeforeInput({} as InputEvent)).not.toThrow() expect(isInsertBeforeInput({} as InputEvent)).toBe(false) + expect(isInsertBeforeInput({ inputType: 42 })).toBe(false) expect(isInsertBeforeInput({ inputType: 'insertFromPaste' } as InputEvent)).toBe(true) }) @@ -567,6 +568,26 @@ describe('WikilinkChatInput', () => { expect(editor.textContent).toContain('still works') }) + it('ignores beforeinput events with non-string inputType instead of calling startsWith', () => { + render() + + const editor = screen.getByTestId('agent-input') + const startsWith = vi.fn(() => true) + const beforeInputEvent = new Event('beforeinput', { + bubbles: true, + cancelable: true, + }) + Object.defineProperty(beforeInputEvent, 'inputType', { + value: { startsWith }, + }) + + expect(() => fireEvent(editor, beforeInputEvent)).not.toThrow() + expect(startsWith).not.toHaveBeenCalled() + + updateEditorText('still works') + expect(editor.textContent).toContain('still works') + }) + it('recovers if unsupported media lands in the editor DOM', async () => { const onUnsupportedPaste = vi.fn() render() diff --git a/src/components/inlineWikilinkBeforeInput.ts b/src/components/inlineWikilinkBeforeInput.ts index a9a6d39c..9c834287 100644 --- a/src/components/inlineWikilinkBeforeInput.ts +++ b/src/components/inlineWikilinkBeforeInput.ts @@ -1,11 +1,26 @@ -export function isInsertBeforeInput(nativeEvent: Pick) { - return typeof nativeEvent.inputType === 'string' - && nativeEvent.inputType.startsWith('insert') +type BeforeInputLike = { + inputType?: unknown +} + +type PlainTextBeforeInputLike = BeforeInputLike & { + data?: unknown + isComposing?: unknown +} + +type PlainTextBeforeInput = PlainTextBeforeInputLike & { + data: string + inputType: 'insertText' + isComposing?: false +} + +export function isInsertBeforeInput(nativeEvent: BeforeInputLike) { + const { inputType } = nativeEvent + return typeof inputType === 'string' && inputType.startsWith('insert') } export function isPlainTextBeforeInput( - nativeEvent: Pick, -): nativeEvent is Pick & { data: string } { + nativeEvent: PlainTextBeforeInputLike, +): nativeEvent is PlainTextBeforeInput { return nativeEvent.inputType === 'insertText' && !nativeEvent.isComposing && typeof nativeEvent.data === 'string'