Repository navigation
feat: support drag and drop img in web-shell - #8696
Conversation
Allow Web Shell composers to ingest image files reliably while preserving the existing multimodal prompt protocol. - Share ordered image ingestion across desktop and mobile editors - Support image-only prompts and BMP preview and provider-safe handling - Preserve queued payloads across retries and uncertain outcomes - Add lifecycle guards, user feedback, unit coverage, and browser tests
Preserve complete prompt payloads and prevent duplicate or uncertain delivery states when admission responses race with queue lifecycle events. - Correlate admission, queue, and terminal events by prompt ID - Restore images and input annotations across retry and edit flows - Bound image reader concurrency and encoded attachment memory - Reconcile confirmed removals and explain ambiguous queue entries
Document the reviewed admission, recovery, and resource invariants. Keep the design aligned with the hardened Web Shell implementation. - Record bounded image ingestion and encoded-data budgeting - Clarify prompt lifecycle correlation and confirmed removal behavior - Describe annotation restoration and internal action boundaries - Update focused validation evidence and acceptance criteria
|
@wenshao @yiliang114 Please take a look |
Review Summary — PR #8696Reviewed: 37 files, +4482/−414 at head of What this PR doesAdds image drag-and-drop to every Web Shell composer, reusing the existing paste/attachment/multimodal pipeline, and hardens the async prompt lifecycle (ordered bounded ingestion, prompt-ID correlation, dedup, safe retry/edit). Strengths
Observations[Info] Size/complexity. The PR is large and touches many lifecycle paths; the complexity is justified by the races described, but it's a lot to land at once. [Info] No blocking issues. CI green (Test, web-shell E2E, Capture visuals). Only ci-bot has approved so far. Approve. |
ytahdn
left a comment
There was a problem hiding this comment.
LGTM. Well-engineered drag-drop ingestion (bounded, ordered, budgeted) and thorough prompt-lifecycle hardening (prompt-ID correlation, dedup, safe retry). Capture-phase preventDefault prevents navigation. Strong test coverage, CI green.
|
@yiliang114 Please take a look |
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. The hard parts check out: reader concurrency is genuinely bounded (worker-pool index handoff, batches serialized on a tail promise, cumulative 8 MiB budget inclusive at the boundary), stale readers are killed by lane identity + generation + abort with a post-await recheck, the race matrix is pinned by tests (terminal-before-response binding, no double append, removed-before-response no-append, exactly-once restore after definite rejection, unknown-payload restore/discard without resend), lost-response prompts are never auto-resent, and there is no new XSS surface (count-only notices, data-URL thumbnails, SVG stays blocked). The removed-before-dispatch vs started-then-cancelled distinction and the admission classifier are correct, and the core-package change is test-only.
One P2 worth fixing or testing away before merge: restoreQueuedPromptsToEditor drops the #7134 guard — images are now restored unconditionally after mergeRestoredPromptText, keyed by local row id. Same-row double restore is blocked, but if the payload text is already in the editor when a different row carrying the same payload is restored (re-typed/re-queued identical content, or a row re-materializing with a fresh id), the text dedupes while the images append again — exactly the case the old condition guarded. Suggested fix: skip image/annotation restore when nextText === currentText, or dedup on serverPromptId/payload hash; plus a restore-while-text-already-present regression test.
Minor notes: no content sniffing (extension/declared MIME trusted, parity with paste — the provider is the real validation boundary; worth a line in the design doc's trust-boundary section), drop protection is composer-scoped so drops on the message list still navigate (pre-existing), and the branch now conflicts with main and needs a rebase. Nothing blocks from my side.
…rop-img # Conflicts: # packages/web-shell/client/App.tsx
e6c85df
|
@ytahdn @yiliang114 All the conflicts have been resolved. Please take another look. |
|
@qwen-code-ci-bot @qwen-code-dev-bot Please take a look |
|
One P2 remains on the current head ( I reproduced this against the current head with a focused regression test: prefill the editor with Suggested minimal fix: when non-empty restored text is already present ( |
Skip payload attachments when restoring text is a no-op because the same prompt text already exists in the composer. - Restore images and annotations only when their text is inserted - Preserve image-only restoration regardless of the current draft - Add regression coverage for duplicate text with attachments
|
@ytahdn @yiliang114 Fixed in dc75bf6.
I added the requested regression test using an existing Validation:
Thanks for catching this. |
ytahdn
left a comment
There was a problem hiding this comment.
Re-reviewed at dc75bf6. The duplicate attachment restoration issue is fixed: attachments are skipped when non-empty restored text is already present, while image-only restoration remains intact. The focused queue restoration suites pass 42/42 locally. No remaining blockers from my review.
yiliang114
left a comment
There was a problem hiding this comment.
Re-approving at head dc75bf6 after my approval was dismissed by the new push. The P2 is fixed exactly as recommended: restoreQueuedPromptsToEditor now tracks textWasRestored (true only when mergeRestoredPromptText actually changes the editor text) and skips image/annotation restore when the payload text is already present — normal restores and image-only prompts are unaffected, annotation offset alignment holds in both branches, and the new dom test reproduces the exact duplicate scenario (payload text already in editor + 413 reject) asserting no restore calls while the queue drains. The accepted tradeoff is the safer direction: in the dedupe case a not-yet-restored attachment is silently dropped instead of duplicated, and it stays visible to the user. I also verified the main-merge resolutions in e6c85df are clean unions — all four conflicted files keep the full PR wiring (the fourth onImageIngestionNotice site correctly folded into main's artifactPanelSharedProps), so nothing from the original review was dropped. CI green on this head. Nothing blocks merge.
What this PR does
This PR adds image drag-and-drop support to every Web Shell composer while reusing the existing paste, attachment preview, and multimodal prompt pipeline.
It supports PNG, JPEG, GIF, WebP, and BMP images, preserves attachment order across files and batches, and allows image-only prompts in the main chat, split panes, and side tasks. It also provides drop-state feedback, attachment removal, queued submission, edit, and retry behavior.
The prompt lifecycle has been hardened to:
The daemon wire format, ACP/Core protocol, and public Web Shell API remain unchanged.
Why it's needed
Web Shell already supported pasting images, but dropping image files onto the composer was not handled. Browser default drop behavior could also insert unwanted content or navigate away from the page.
The existing asynchronous ingestion and queue lifecycle had additional edge cases: attachments could be reordered, an image could be omitted if the prompt was submitted before reading completed, and response/event races could produce duplicate transcript entries or lose recoverable payload data.
This PR closes those gaps and makes image prompts reliable across direct, queued, image-only, retry, and editing flows.
Reviewer Test Plan
How to verify
git diff --checkpassed.Evidence (Before & After)
Automated Chromium coverage verifies PNG/BMP drop, preview ordering, image-only submission, deletion, request payload contents, transcript rendering, admission failure retry, and post-admission turn-error retry.
20260807-170544_._.s.video.meeting_0807_video.mp4
Tested on
Risk & Scope
Linked Issues
Closes #8321
中文说明
What this PR does
本 PR 为所有 Web Shell composer 增加图片拖放能力,并复用现有的图片粘贴、附件预览和多模态 prompt 链路。
支持 PNG、JPEG、GIF、WebP 和 BMP,能够保持多文件及多批次附件顺序,并让主聊天、split pane 和 side task 都可以发送 image-only prompt。同时覆盖拖放状态反馈、附件删除、排队发送、编辑和重试。
本次还强化了 prompt 生命周期:
daemon wire format、ACP/Core 协议及公开 Web Shell API 均保持不变。
Why it's needed
Web Shell 已经支持粘贴图片,但将图片文件拖入 composer 时不会被接管,浏览器默认行为还可能插入无效内容或离开当前页面。
原有异步摄取和队列生命周期也存在一些边界问题:附件可能因为读取完成顺序不同而乱序;读取完成前提交会遗漏图片;response 与生命周期事件发生竞态时,可能产生重复 transcript message 或丢失可恢复的完整 payload。
本 PR 补齐这些缺口,使 direct、queued、image-only、retry 和 edit 等图片 prompt 流程更加可靠。
Reviewer Test Plan
How to verify
git diff --check通过。Evidence (Before & After)
Chromium 自动化覆盖 PNG/BMP drop、预览顺序、image-only 提交、附件删除、请求内容、transcript 渲染、admission failure retry 和 post-admission turn-error retry。
20260807-170544_._.s.video.meeting_0807_video.mp4
Tested on
Environment (optional)
macOS 26.0、Node.js v22.22.3、本地 Web Shell mock daemon,以及 Chromium Playwright smoke tests。
Risk & Scope
Linked Issues
Closes #8321