From 4ede2798fead9f69387a799ec372be1eade4beaf Mon Sep 17 00:00:00 2001 From: _Kerman Date: Fri, 7 Aug 2026 14:20:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20address=20review=20=E2=80=94=20plac?= =?UTF-8?q?eholder=20key=20family,=20menu-open=20gate,=20plan=20precedence?= =?UTF-8?q?,=20note=20wording?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...8-06-web-queue-steer-all-gesture.i18n.yaml | 4 +-- .../2026-08-06-web-queue-steer-all-gesture.md | 2 +- ...26-08-06-web-queue-steer-all-gesture.zh.md | 2 +- .../ui-conversation/src/client/locales.ts | 4 +-- .../src/client/skeleton/InputBar.tsx | 7 +++-- .../ui-conversation/tests/input-bar.spec.tsx | 27 +++++++++++++++++++ 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.i18n.yaml b/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.i18n.yaml index 0e3cc06a83..1935c6de2e 100644 --- a/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.md -2026-08-06-web-queue-steer-all-gesture.md: c4452c16fe01f876aff72715a7d1056a09ae1e1f -2026-08-06-web-queue-steer-all-gesture.zh.md: 27e147304eec0dff8ba6209d4de3f312375fe4df +2026-08-06-web-queue-steer-all-gesture.md: e546f68647dfc9b91ce4699cef4a64694ebc4f76 +2026-08-06-web-queue-steer-all-gesture.zh.md: fb36852f66a86408af12df59001230b2024eccaf diff --git a/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.md b/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.md index c4452c16fe..e546f68647 100644 --- a/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.md +++ b/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.md @@ -14,7 +14,7 @@ Empty-draft Cmd/Ctrl+Enter now steers every still-pending `queued`-placement inb The gesture is strictly the accelerated chord. Plain Enter with an empty draft stays a no-op even under the busy-Enter Steer preference, draft content outranks the queue (accelerated Enter steers only the draft), and idle or subagent sessions keep the existing empty-draft no-op because steering has no live turn to enter. -The same computed availability gate drives discovery: while the draft is empty, the input is unlocked, an ordinary primary session is running, and at least one row remains `queued`, the textarea placeholder advertises that Cmd/Ctrl+Enter steers all queued messages. An owner-supplied placeholder still takes precedence. +The same computed availability gate drives discovery: while the draft is empty, the input is unlocked and not in a transient machine lock, the command menu is closed, an ordinary primary session is running, and at least one row remains `queued`, the textarea placeholder advertises that Cmd/Ctrl+Enter steers all queued messages. An owner-supplied placeholder still takes precedence, and the steer hint deliberately outranks the plan-mode placeholder while available (the gesture genuinely works in that window). ## Consequences diff --git a/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.zh.md b/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.zh.md index 27e147304e..fb36852f66 100644 --- a/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.zh.md +++ b/.agents/notes/implemented/feature/2026-08-06-web-queue-steer-all-gesture.zh.md @@ -14,7 +14,7 @@ Status: implemented 该手势严格限定为加速组合键。空草稿 + 普通 Enter 仍然无操作(即使 busy-Enter 偏好为 Steer);草稿内容优先于队列(加速 Enter 只插话当前草稿);idle 或 subagent 会话保持原有空草稿无操作,因为没有可插入的运行中轮次。 -同一套计算得出的可用性门控也负责提示该手势:当草稿为空、输入框未锁定、普通主会话正在运行且至少一行仍为 `queued` 时,文本框 placeholder 会提示 Cmd/Ctrl+Enter 将全部排队消息插话发送。owner 提供的 placeholder 仍然优先。 +同一套计算得出的可用性门控也负责提示该手势:当草稿为空、输入框未锁定且不处于瞬态机器锁(adjudicating/submitting)、命令菜单未打开、普通主会话正在运行且至少一行仍为 `queued` 时,文本框 placeholder 会提示 Cmd/Ctrl+Enter 将全部排队消息插话发送。owner 提供的 placeholder 仍然优先;可用时 steer 提示会刻意优先于 plan 模式 placeholder(该窗口内手势确实可用)。 ## Consequences diff --git a/packages/client/ui-conversation/src/client/locales.ts b/packages/client/ui-conversation/src/client/locales.ts index 9fd3b21b74..20aab2cfda 100644 --- a/packages/client/ui-conversation/src/client/locales.ts +++ b/packages/client/ui-conversation/src/client/locales.ts @@ -22,7 +22,7 @@ export const zh = { 'input.commands': '命令', 'input.stop': '停止生成', 'input.send': '发送消息', - 'input.steerQueueShortcut': 'Cmd/Ctrl+Enter 插话发送全部排队消息', + 'placeholder.steerQueue': 'Cmd/Ctrl+Enter 插话发送全部排队消息', 'input.accessMode': '访问模式,当前:{name}', 'context.aria': '上下文已用 {percent}', 'context.used': '上下文已用', @@ -163,7 +163,7 @@ export const en = { 'input.commands': 'Commands', 'input.stop': 'Stop generating', 'input.send': 'Send message', - 'input.steerQueueShortcut': 'Cmd/Ctrl+Enter steers all queued messages', + 'placeholder.steerQueue': 'Cmd/Ctrl+Enter steers all queued messages', 'input.accessMode': 'Access mode, current: {name}', 'context.aria': '{percent} of context used', 'context.used': 'of context used', diff --git a/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx b/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx index 5303c7b7d9..e326c8e523 100644 --- a/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx +++ b/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx @@ -89,7 +89,7 @@ export function InputBar({ const disabled = removed || inert || !live const locked = disabled const machineBusy = input?.phase === 'adjudicating' || input?.phase === 'submitting' - const canSteerQueue = !locked && !machineBusy && empty && running && subagent === null + const canSteerQueue = !locked && !machineBusy && !commandMenuOpen && empty && running && subagent === null && input.queue.some(row => row.placement === 'queued') // Scroll the draft scrollport the minimum that brings `caret` into view — the @@ -486,8 +486,11 @@ export function InputBar({ data-phase={input?.phase ?? 'inert'} placeholder={placeholder ?? (disabled ? t('placeholder.unavailable') + // The steer hint deliberately outranks the plan placeholder: + // while it shows, the whole-queue gesture is genuinely available + // (the gate never consults plan mode), so the actionable hint wins. : canSteerQueue - ? t('input.steerQueueShortcut') + ? t('placeholder.steerQueue') : planActive ? t('placeholder.plan') : t('placeholder.default'))} rows={2} onChange={onChange} diff --git a/packages/client/ui-conversation/tests/input-bar.spec.tsx b/packages/client/ui-conversation/tests/input-bar.spec.tsx index f8127fa892..4ae6626b14 100644 --- a/packages/client/ui-conversation/tests/input-bar.spec.tsx +++ b/packages/client/ui-conversation/tests/input-bar.spec.tsx @@ -191,6 +191,33 @@ describe('Enter semantics', () => { queue: [row('q-1')], placeholder: '上层指定提示', }).textarea.placeholder).toBe('上层指定提示') + // The command menu owns Enter while open: neither the hint nor the + // gesture may claim the chord. + expect(bench({ + running: true, + queue: [row('q-1')], + commandMenuOpen: true, + }).textarea.placeholder).toBe('给智能体发消息') + // The steer hint intentionally outranks the plan placeholder: while it + // shows, the whole-queue gesture is genuinely available in plan mode. + expect(bench({ + running: true, + queue: [row('q-1')], + plan: { active: true, pending: false }, + }).textarea.placeholder).toBe('Cmd/Ctrl+Enter 插话发送全部排队消息') + }) + + it('an open command menu withholds the whole-queue steering gesture', () => { + const steerQueue = vi.fn() + const { textarea, sink } = bench({ + running: true, + queue: [row('q-1')], + commandMenuOpen: true, + steerQueue, + }) + fireEvent.keyDown(textarea, { key: 'Enter', metaKey: true }) + expect(steerQueue).not.toHaveBeenCalled() + expect(sink).not.toHaveBeenCalled() }) it('plain Enter submits queue mode through the machine; repeat and empty are suppressed', () => {