Dropping a screenshot into a session only worked on the composer box
itself. Anywhere else — the transcript, the sidebar, the empty space
beside the composer — the drop fell through to the browser, which
navigated away from the session to render the file and lost the page. A
file dragged into a chat has no other meaning, so claim the whole page.
A shared `useWindowFileDrop` hook binds the drag listeners on `window`
and reports whether a file drag is in flight; both composers (in-session
and the new-chat landing) route it to their existing `addFiles`, so
validation and the chips are unchanged. The drop cue moves from a badge
inside the box to a page-wide `FileDropOverlay`, since the target is now
the page. Only drags carrying files are claimed, so dragging selected
text into the textarea keeps its native behavior.
Signed-off-by: harry-yao_data <harry.yao@databricks.com>
Related issue
No tracking issue — this came in as direct feedback ("can we allow dragging
images anywhere in the page, not just in the input?").
Summary
Dropping a file into a chat only worked on the composer box itself. Drop a
screenshot on the transcript or the empty space beside the composer and the drop
fell through to the browser, which navigated away from the session to render the
file and lost the page. A file dragged into a chat has no other meaning, so the
whole chat column now takes it.
useFileDropTargethook binds the drag listeners on a given element andreports whether a file drag is in flight over it. Both composers (in-session
ChatPageand the new-chat landing screen) route it to their existingaddFiles, so attachment validation, chips, and the upload path areunchanged.
[data-chat-surface], the main column holdingthe transcript and the composer, resolved from the composer card's ancestor;
the landing surface on
/. The shell around it (sidebar, workspace rail)keeps whatever drag behavior it had.
FileDropOverlayportalled into the drop target, so it covers exactly what accepts the drop.
The composer keeps its inset ring so the destination stays obvious.
dataTransfer.typesincludes"Files") areclaimed, so dragging selected text into the textarea keeps its native
behavior.
preventDefaulton a filedragoveris also what stops thebrowser from opening a dropped file over the app.
Test Plan
Automated:
pnpm vitest run src/hooks/useFileDropTarget.test.tsx src/pages/ChatPage.composer.test.tsx src/shell/NewChatDialog.test.tsx— 469 passed. New unit coverage: the drop lands anywhere inside the target, a drop outside it is neither claimed nor attached, the cue survives the pointer crossing child elements, a cancelled drag clears it, a non-file drag is ignored and left un-prevented, and the listeners unbind on unmount.pytest tests/e2e_ui/chat/test_composer_attachments.py --ui-skip-build— 7 passed, includingtest_file_dropped_on_the_transcript_attaches(dispatches a realDataTransferdrag onto the transcript in Chromium, asserts the cue, that the app calledpreventDefaulton the drop, and that the chip appears) andtest_file_dropped_outside_the_chat_column_is_ignored(same drag on the sidebar: not claimed, no cue, no chip).ChatPage.tsxand it fails —element(s) not found … waiting for get_by_test_id("file-drop-overlay").Manual:
(not the composer) — the chat column dims with a dashed border and "Drop
files here"; release, and the file appears as a chip in the composer. Before
this change the browser navigated away and the session was gone.
an attachment.
/), drop a file onto the heading — itattaches to the landing composer.
inserts the text (a non-file drag is not intercepted).
Demo
Dragging a file over the transcript — the cue spans the chat column, and the
sidebar / workspace rail stay out of it:
Released on the transcript, well outside the composer box — the file attaches:
Same behavior on the new-chat landing screen:
Type of change
Test coverage
Coverage notes
Manual verification covers what neither layer can: a real OS-to-browser drag.
Playwright can only synthesize a
DataTransferin the page, and jsdom has nodrag semantics at all, so the steps above were walked in a local dev build
(screenshots in Demo were captured from that build via Playwright).
Changelog
Drop an image anywhere in the chat to attach it, not just on the message box