From 3c8cd4eca31ab23127bcdfbab24a77646065ec41 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Thu, 30 Jul 2026 01:34:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20repair=20merge=20tails=20=E2=80=94?= =?UTF-8?q?=20todo-row=20selector=20and=20composer=20handler=20clone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The #921 selector change and the duplication gate both first ran against this branch after the merge made it MERGEABLE: assembly-surfaces still queried the retired data-sample="todo-row" hook (the composed TodoRow carries ToolRow's data-tool attribute instead), and the redesigned QuestionComposer duplicated the custom-draft onChange/onKeyDown pair across its inline input and optionless textarea. The spec now anchors on data-tool="todo_write", and the composer shares one draftCustom / continueFromCustom handler pair (Enter continues, Shift+Enter stays a newline, IME composition stays inert). --- .../tests/assembly-surfaces.spec.tsx | 4 +- .../src/client/QuestionComposer.tsx | 47 +++++++++---------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx b/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx index 5d7f4c05e7..74336315d3 100644 --- a/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx +++ b/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx @@ -97,7 +97,7 @@ describe('todo_write assembly (product registrations, no outlet twins)', () => { const view = runtime.renderRoot() // Keyed toolview registration took the row (summary derived from args). - const row = view.container.querySelector('[data-sample="todo-row"]') + const row = view.container.querySelector('[data-tool="todo_write"]') expect(row).not.toBeNull() expect(row!.textContent).toContain('1/3 已完成 · 实现 fixture 样本') @@ -117,7 +117,7 @@ describe('todo_write assembly (product registrations, no outlet twins)', () => { await waitFor(() => { expect(view.container.querySelector('[data-testid="todo-panel"]')).toBeNull() }) - expect(view.container.querySelector('[data-sample="todo-row"]')).not.toBeNull() + expect(view.container.querySelector('[data-tool="todo_write"]')).not.toBeNull() await runtime.dispose() }) }) diff --git a/packages/client/ui-question/src/client/QuestionComposer.tsx b/packages/client/ui-question/src/client/QuestionComposer.tsx index db1c2ec353..d8ae56d502 100644 --- a/packages/client/ui-question/src/client/QuestionComposer.tsx +++ b/packages/client/ui-question/src/client/QuestionComposer.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState, type KeyboardEvent } from 'react' +import { useMemo, useState, type ChangeEvent, type KeyboardEvent } from 'react' import clsx from 'clsx' import { Button, IconCheckOutline14, IconChevronLeftOutline14, IconChevronRightOutline14, @@ -149,6 +149,23 @@ function QuestionFlow({ pending, t, useLocale }: { submitDrafts(drafts) } + // Shared by the inline custom input and the optionless textarea: typing a + // custom draft clears any selection, and Enter continues the flow + // (Shift+Enter stays a newline in the textarea; on the single-line input it + // is inert either way). + const draftCustom = (event: ChangeEvent): void => { + const value = event.target.value + updateDraft(current => ({ + ...current, selected: [], custom: value, skipped: false, + })) + } + + const continueFromCustom = (event: KeyboardEvent): void => { + if (event.key !== 'Enter' || event.shiftKey || isComposing(event)) return + event.preventDefault() + continueFlow() + } + const skipQuestion = (): void => { const nextDrafts = drafts.map((item, itemIndex) => itemIndex === index ? { selected: [], custom: '', skipped: true } @@ -247,18 +264,8 @@ function QuestionFlow({ pending, t, useLocale }: { value={draft.custom} disabled={busy !== null} placeholder={t('custom.placeholder')} - onChange={(event) => { - const value = event.target.value - updateDraft(current => ({ - ...current, selected: [], custom: value, skipped: false, - })) - }} - onKeyDown={(event) => { - if (event.key === 'Enter' && !isComposing(event)) { - event.preventDefault() - continueFlow() - } - }} + onChange={draftCustom} + onKeyDown={continueFromCustom} /> ) @@ -270,18 +277,8 @@ function QuestionFlow({ pending, t, useLocale }: { disabled={busy !== null} rows={2} placeholder={t('custom.placeholder')} - onChange={(event) => { - const value = event.target.value - updateDraft(current => ({ - ...current, selected: [], custom: value, skipped: false, - })) - }} - onKeyDown={(event) => { - if (event.key === 'Enter' && !event.shiftKey && !isComposing(event)) { - event.preventDefault() - continueFlow() - } - }} + onChange={draftCustom} + onKeyDown={continueFromCustom} /> )}