Skip to content

fix(web-shell): split-view pane fixes (remove "current" badge, clear composer on send) - #6454

Merged
yiliang114 merged 3 commits into
QwenLM:mainfrom
wenshao:fix/web-shell-remove-split-current-badge
Jul 7, 2026
Merged

fix(web-shell): split-view pane fixes (remove "current" badge, clear composer on send)#6454
yiliang114 merged 3 commits into
QwenLM:mainfrom
wenshao:fix/web-shell-remove-split-current-badge

Conversation

@wenshao

@wenshao wenshao commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Two small fixes to the in-window split view, both in the split-pane ChatPane:

  1. Remove the "current" badge. Panes no longer show a rounded "当前 / current" pill (or the matching accent-colored border) on the one pane that happens to be the workspace's active session. The isCurrent prop, the badge, and the border-highlight branch are removed from ChatPane, and SplitView no longer passes isCurrent. The session sidebar and the session-overview grid keep their own "current" indicators — those are unchanged.

  2. Clear the composer on send, not at turn end. After sending a message from a split pane, the text stayed in the composer for the entire response. handleSubmit committed (cleared) the draft on the sendPrompt promise resolving, but that promise resolves only when the whole turn finishes (waitForAcceptedPromptCompletion), not when the prompt is accepted. It now clears via the onAdmitted hook, which fires at admission — matching the main view. A prompt rejected before admission still preserves the draft and surfaces the error.

Why it's needed

  1. Inside the split view every pane is an equal, independently interactive session — its own DaemonSessionProvider, SSE stream, transcript, and approvals, with keyboard focus scoped per pane by the browser. The "current" pane had no special behavior; the marker only surfaced a single-view singleton concept (connection.sessionId) into a layout of equal peers, where it conveyed nothing actionable and reliably prompted "what does this do?" questions. Panes are already identified by their titles. The sidebar and overview keep their indicators on purpose, because there they are genuine "you are here" navigation.

  2. Committing the draft only on turn completion meant the just-sent text sat in the composer for the whole (possibly long) response — confusing, and easy to accidentally resend. Clearing at admission is prompt and still safe: if the daemon rejects the prompt before admission (transcript still loading, session disconnected, or a turn already active), onAdmitted never fires, so the draft is preserved.

Reviewer Test Plan

How to verify

  • Unit (from packages/web-shell): npx vitest run client/components/ChatPane.test.tsx client/components/SplitView.test.tsx — 33 tests pass. New/updated cases: the composer commits the draft on admission and NOT again on turn completion (guarding against regressing to the turn-end behavior); SplitView's "seeds with the current session" test keeps its seed assertion, dropping only the assertion on the removed data-current marker.
  • Type check: npx tsc --noEmit -p packages/web-shell/tsconfig.lib.json (the build's typecheck) is clean. The broader packages/web-shell/tsconfig.json reports the same pre-existing errors in unrelated test fixtures before and after this change — zero new errors.
  • Manual: open a split view with two or more panes, one of which is the active session. (a) No pane shows a "当前 / current" pill or accent border, and the sidebar still highlights the active session. (b) Type in a pane and press Enter — the composer clears immediately (at admission), not only after the model finishes responding.

Evidence (Before & After)

  • Badge: before, the active-session pane rendered a rounded "当前" pill plus an accent-colored (--primary) border; after, neither — panes render identically apart from their titles.
  • Composer: before, the sent text stayed in the split-pane composer until the turn finished; after, it clears as soon as the prompt is admitted.

Both are behavior/markup changes exercised by the unit tests above; the badge removal is a pure element removal (the .currentBadge span and .paneCurrent border class), so there is no new UI to screenshot.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

Vitest (jsdom) unit tests plus a local tsc type check, run on macOS.

Risk & Scope

  • Main risk or tradeoff: low. The badge removal is a pure visual removal with no behavior, data, or API change. The composer fix moves the clear from turn-end to admission via the existing onAdmitted hook, preserving the "don't drop the draft if the prompt is rejected before admission" property.
  • Not validated / out of scope: the sidebar and session-overview "current" indicators are intentionally kept and unchanged; no change to the main-view composer.
  • Breaking changes / migration notes: none. ChatPane's isCurrent prop is removed and its only caller (SplitView) is updated in the same change; currentSessionId is retained purely to seed the initial pane.

Linked Issues

中文说明

这个 PR 做了什么

对窗口内分屏视图的两处小修复,都在分屏窗格 ChatPane 里:

  1. 移除「当前」徽标。 窗格不再对「恰好是工作区活动会话」的那一个窗格显示圆角的「当前 / current」药丸(以及配套的高亮色边框)。ChatPane 中的 isCurrent 属性、徽标、边框高亮分支都被移除,SplitView 也不再传 isCurrent。会话侧栏和会话总览网格各自的「当前」标识保持不变。

  2. 发送时清空输入框,而不是等整轮结束。 从分屏窗格发送消息后,文本会在整个回复期间一直留在输入框里。handleSubmit 原本是在 sendPrompt 的 Promise resolve 时才清空草稿,但那个 Promise 要等整轮结束(waitForAcceptedPromptCompletion)才 resolve,而不是在 prompt 被受理时。现改为通过 onAdmitted 钩子清空——它在受理时触发,和主界面一致。若 prompt 在受理前被拒绝,草稿仍会保留并弹出错误。

为什么需要

  1. 在分屏视图内部,每个窗格都是完全对等、可独立交互的会话——各自拥有 DaemonSessionProvider、SSE 流、转录和审批,键盘焦点由浏览器按窗格隔离。「当前」那个窗格没有任何特殊行为;这个标记只是把一个单视图的单例概念(connection.sessionId)暴露进对等窗格并排的布局,在那里它传达不了任何可操作的信息,反而稳定地引发「这是干嘛的?」这类疑问。窗格本来就靠标题区分。侧栏和总览之所以保留标识,是因为在那些场景里它们是真正的「你在这里」导航。

  2. 只在整轮完成时才清空草稿,意味着刚发出的文本会在整段(可能很长的)回复期间一直留在输入框里——既令人困惑,又容易误重发。改为在受理时清空既及时又安全:若 daemon 在受理前拒绝了 prompt(转录仍在加载、会话断连,或已有一轮在进行),onAdmitted 不会触发,草稿因此得以保留。

复核测试计划

如何验证

  • 单元测试(在 packages/web-shell 下):npx vitest run client/components/ChatPane.test.tsx client/components/SplitView.test.tsx —— 33 个测试通过。新增/更新用例:输入框在受理时提交草稿,并且在整轮完成时不再二次提交(防止回退到旧的「轮末清空」行为);SplitView 的 "seeds with the current session" 测试保留 seed 断言,只删掉针对已移除的 data-current 标记的那条断言。
  • 类型检查:npx tsc --noEmit -p packages/web-shell/tsconfig.lib.json(构建所用的类型检查)干净通过。更宽的 packages/web-shell/tsconfig.json 在改动前后都报同样的既有错误(都在无关的测试夹具里)——零新增。
  • 手动:打开一个有两个及以上窗格的分屏,其中一个是活动会话。(a) 没有窗格显示「当前 / current」药丸或高亮边框,侧栏仍高亮活动会话。(b) 在某个窗格里输入并回车——输入框立即(在受理时)清空,而不是等模型回复完才清空。

证据(改动前后)

  • 徽标:改动前,活动会话的那个窗格会渲染一颗圆角「当前」药丸外加高亮色(--primary)边框;改动后两者都没有——除各自标题外所有窗格渲染一致。
  • 输入框:改动前,发出的文本会留在分屏窗格的输入框里直到整轮结束;改动后,prompt 一被受理就清空。

两者都由上面的单元测试覆盖;徽标移除是纯粹的元素删除(.currentBadge span 和 .paneCurrent 边框类),因此没有新的 UI 可供截图。

测试平台

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境(可选)

Vitest(jsdom)单元测试,外加本地 tsc 类型检查,在 macOS 上运行。

风险与范围

  • 主要风险或权衡:低。徽标移除是纯视觉删除,不涉及行为、数据或 API 变化。输入框修复通过既有的 onAdmitted 钩子把清空时机从轮末挪到受理,保留了「受理前被拒则不丢草稿」的性质。
  • 未验证 / 范围之外:侧栏和会话总览的「当前」标识有意保留、保持不变;主界面 composer 不变。
  • 破坏性变更 / 迁移说明:无。ChatPaneisCurrent 属性被移除,其唯一调用方(SplitView)在同一改动中一并更新;currentSessionId 仅保留用于 seed 初始窗格。

In the split view every pane is an equal, independently interactive session (its own DaemonSessionProvider, SSE, transcript, and approvals), so tagging one pane as the workspace's "current" session carried no operational meaning. It only leaked a single-view concept into a peer-of-equals layout and reliably prompted "what is this?" questions from users. Panes are already identified by their titles.

Drop the isCurrent badge and its border highlight from ChatPane, and stop passing isCurrent from SplitView. The sidebar and session overview keep their "current" indicators, which are legitimate "you are here" navigation. currentSessionId is retained only to seed the initial pane.
@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — all required sections present, bilingual, with detailed reviewer test plan.

Problem: Two real, observable UI issues in the split-view pane: (1) a "current" badge on one pane that conveys no actionable information among equal peers, and (2) composer text persisting through the entire response instead of clearing on send. Both are directly verifiable by opening a split view — not theoretical hardening.

Direction: Aligned. The badge removal eliminates a confusing singleton concept leaking into a peer layout. The composer fix matches main-view behavior (clear on admission via the existing onAdmitted hook). Neither change touches auth, sandbox, model selection, telemetry, or public contract.

Size: 55 production lines (ChatPane.module.css: 14 deletions, ChatPane.tsx: 17+/18−, SplitView.tsx: 2+/4−), 78 test lines. Single package (web-shell), no core paths. Not applicable for core module thresholds.

Approach: Scope is tight — two related fixes in the same component area, every edit serves the stated goal. No drive-by refactors; the formatting adjustments (line wrapping) are minor Prettier-style changes. Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓ — 所有章节齐全,中英双语,附带详细的复核测试计划。

问题:分屏窗格中两个真实可观测的 UI 问题:(1)「当前」徽标在对等窗格中传达不了任何可操作信息;(2) 发送后文本在整个回复期间留在输入框而非立即清空。两者均可通过打开分屏直接验证——不是理论性加固。

方向:对齐。移除徽标消除了单例概念泄漏到对等布局的困惑;输入框修复通过既有 onAdmitted 钩子与主视图行为保持一致。不涉及认证、沙箱、模型选择、遥测或公开契约。

规模:55 行生产代码(ChatPane.module.css 删 14 行、ChatPane.tsx 增 17/删 18、SplitView.tsx 增 2/删 4),78 行测试代码。单一包(web-shell),不触及核心路径。核心模块阈值不适用。

方案:范围紧凑——同一组件区域的两个相关修复,每处改动都服务于目标。无顺手重构;格式调整(换行)属 Prettier 风格微调。进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: To fix these two issues, I would: (1) remove the isCurrent prop from ChatPane's interface, delete the badge JSX and .currentBadge/.paneCurrent CSS rules, and stop passing isCurrent from SplitView; (2) change the composer clear from .then(() => commitAccepted?.()) on the sendPrompt promise to passing commitAccepted as the onAdmitted callback in the SendPromptOptions, which fires at admission rather than turn completion. Both changes match what this PR does.

Comparison with the diff: The PR's approach matches my proposal exactly. The badge removal is straightforward dead-code elimination — prop, JSX, CSS class, all gone in one atomic change. The composer fix correctly uses the existing onAdmitted hook that SendPromptOptions already supports (verified in packages/webui/src/daemon/session/types.ts:249). The new call shape — always passing an options object with onAdmitted: commitAccepted — is functionally equivalent to the old conditional-undefined pattern and cleaner.

Reuse check: onAdmitted already exists on SendPromptOptions and is used by the main-view composer in App.tsx:2612. The PR correctly reuses this existing hook rather than inventing a new mechanism.

No critical blockers. No security concerns. The changes are localized to a single component with no downstream consumers affected.

Testing

Unit Tests (PR branch, 34/34 pass)

 ✓ components/ChatPane.test.tsx (18 tests) 67ms
 ✓ components/SplitView.test.tsx (16 tests) 105ms

 Test Files  2 passed (2)
      Tests  34 passed (34)
   Duration  731ms

Key test assertions verified:

  • Draft commits on admission (sendPromptAdmit()), not on turn completion
  • Turn completion after admission does NOT trigger a second commit (regression guard)
  • Turn failure after admission: draft stays cleared, error still surfaced to onError
  • Prompt rejection before admission: draft preserved (existing test, still passes)
  • data-current attribute assertion removed from SplitView seed test

Type Check

npx tsc --noEmit -p packages/web-shell/tsconfig.lib.json

Clean — no output, exit code 0.

Tmux Real-Scenario Test

N/A — this PR modifies web-shell split-view components (browser UI), not CLI behavior. The changes (CSS removal, composer timing) require a browser + running daemon to exercise, which is outside tmux's scope. The unit tests thoroughly cover both behavior changes with controlled promise/admission timing.

中文说明

代码审查

独立方案: 修复这两个问题我会:(1) 从 ChatPane 接口移除 isCurrent 属性,删除徽标 JSX 和 .currentBadge/.paneCurrent CSS 规则,SplitView 不再传 isCurrent;(2) 将输入框清空从 sendPrompt promise 的 .then(() => commitAccepted?.()) 改为通过 SendPromptOptionsonAdmitted 回调传入 commitAccepted,在受理而非整轮结束时触发。PR 的做法与我的方案完全一致。

与 diff 对比: PR 的方案与我的方案完全匹配。徽标移除是直接的死代码消除——属性、JSX、CSS 类,一次性原子删除。输入框修复正确使用了 SendPromptOptions 已有的 onAdmitted 钩子(已在 packages/webui/src/daemon/session/types.ts:249 验证)。新的调用形式——始终传带 onAdmitted: commitAccepted 的 options 对象——比旧的条件式 undefined 模式更清晰,功能等价。

复用检查: onAdmitted 已存在于 SendPromptOptions 并被主视图 composer 使用(App.tsx:2612)。PR 正确复用了这个既有钩子。

无阻断性问题。 无安全顾虑。改动局限于单一组件,不影响下游消费者。

测试

单元测试(PR 分支,34/34 通过)

 ✓ components/ChatPane.test.tsx (18 tests) 67ms
 ✓ components/SplitView.test.tsx (16 tests) 105ms

 Test Files  2 passed (2)
      Tests  34 passed (34)
   Duration  731ms

关键测试断言已验证:草稿在受理时提交而非整轮结束时;整轮结束后不二次提交;整轮失败后草稿保持清空且错误仍上报;受理前拒绝时草稿保留。

类型检查

npx tsc --noEmit -p packages/web-shell/tsconfig.lib.json — 干净通过。

Tmux 真实场景测试

不适用——本 PR 修改 web-shell 分屏组件(浏览器 UI),不涉及 CLI 行为。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

This is a clean, well-scoped two-part fix. The badge removal is dead-code elimination done right — prop, JSX, CSS, and caller all updated atomically with no orphaned references. The composer timing fix is the more interesting change: it correctly moves draft clearing from turn-completion to admission using the existing onAdmitted hook, matching the main-view behavior. The test suite is thorough, including a regression guard that asserts the turn promise resolving later does NOT trigger a second commit.

My independent proposal matched the PR's approach exactly. I don't see a simpler path — both changes are minimal and necessary for the stated goals. The diff is focused: no scope creep, no unrelated refactors, just the two fixes and their tests.

34/34 unit tests pass, typecheck is clean. The problem is real (not theoretical), the direction is sound, and the implementation is idiomatic.

Approving. ✅

中文说明

这是一个干净、范围良好的两部分修复。徽标移除是标准的死代码消除——属性、JSX、CSS 和调用方一次性原子更新,无残留引用。输入框时序修复是更有趣的那个改动:它正确地将草稿清空从整轮完成移到受理,使用既有的 onAdmitted 钩子,与主视图行为一致。测试套件很充分,包括一个回归保护断言——确认整轮 promise 后续 resolve 不会触发二次提交。

我的独立方案与 PR 的做法完全一致。看不到更简路径——两处改动都是目标所需的最小集。diff 专注:无范围蔓延,无无关重构,只有两个修复及其测试。

34/34 单元测试通过,类型检查干净。问题真实(非理论性),方向正确,实现符合项目惯例。

批准 ✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

A split-view pane kept the just-sent text sitting in its composer until the whole turn finished. handleSubmit committed the draft on the sendPrompt promise resolving, but that promise resolves via waitForAcceptedPromptCompletion (turn end), not at admission. Switch to the onAdmitted hook so the composer clears the moment the daemon accepts the prompt, matching the main view. A prompt rejected before admission still preserves the draft and surfaces the error.
@wenshao wenshao changed the title fix(web-shell): remove meaningless "current" badge from split-view panes fix(web-shell): split-view pane fixes (remove "current" badge, clear composer on send) Jul 7, 2026

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅ Downgraded from Approve to Comment: self-PR; CI still running.

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@ytahdn ytahdn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No review findings. Downgraded from Approve to Comment: CI checks still pending.

— qwen3.7-max via Qwen Code /review

@ytahdn

ytahdn commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Suggestions — commit 46bc5ed45a93ad32fda1a8c9408450dea7e3b13a

  • packages/web-shell/client/components/ChatPane.tsx:98-103 — When the daemon hangs (websocket alive but unresponsive), onAdmitted never fires and .catch() never fires either. The user sees text stuck in the composer with no signal to distinguish "admission never happened" from "admission happened but UI didn't update." Consider adding console.debug in the onAdmitted callback and .catch() to aid future debugging.

— qwen3.7-max via Qwen Code /review

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

Comment thread packages/web-shell/client/components/ChatPane.tsx Outdated
Comment thread packages/web-shell/client/components/ChatPane.test.tsx
…it-then-fail

Review follow-up:
- commitAccepted is already `() => void`, so pass it directly as the onAdmitted option instead of wrapping it in a redundant `() => commitAccepted?.()` closure.
- Add a test for the turn failing after admission: the draft stays cleared (no second commit) and the error is still surfaced to onError.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No review findings. Downgraded from Approve to Comment: CI still running.

Both fixes are well-implemented: the "current" badge removal is clean with no dead references, and the composer draft-clear timing change correctly uses the onAdmitted hook with solid test coverage (admission, rejection-before-admission, and post-admission-failure paths all tested). LGTM. ✅

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@yiliang114
yiliang114 enabled auto-merge July 7, 2026 14:03

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@yiliang114
yiliang114 added this pull request to the merge queue Jul 7, 2026
Merged via the queue into QwenLM:main with commit 55b2886 Jul 7, 2026
63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants