Skip to content

feat: accept any file drag into the chat input - #344

Open
wuxiangru915 wants to merge 2 commits into
agegr:mainfrom
wuxiangru915:feat/drag-files-into-chat-input
Open

feat: accept any file drag into the chat input#344
wuxiangru915 wants to merge 2 commits into
agegr:mainfrom
wuxiangru915:feat/drag-files-into-chat-input

Conversation

@wuxiangru915

Copy link
Copy Markdown

Summary

The chat input currently only accepts image drags (useDragDrop gates on image/*, and ChatInput.addImages filters to images). Dropping any other file silently does nothing. This PR makes the drop zone accept any file from the OS file manager.

Behavior

  • Images — unchanged: attached as base64 multimodal content and sent with the message.
  • Any other file / folder — its real filesystem path is inserted into the input at the caret, so the agent can read it with its own tools. Browsers do not expose the real path on File objects, but OS-originated drags put file:// URIs on the dataTransfer's text/uri-list; since pi-web is a local app, that path is directly usable. Falls back to the bare file name when no path is available (e.g. drag from another browser tab).
  • The full-window drop overlay now shows a document icon instead of the photo icon when the drag contains no images.

Why path insertion (not file upload)

pi's agent core prompt() only supports images attachments, so arbitrary file content cannot be attached end-to-end. Inserting the path lets the agent fetch the file itself and works for every file type, any size.

Implementation

  • lib/dropped-files.ts (new): parses text/uri-list file:// URIs (POSIX + Windows drive paths, URL decoding, comment/blank lines), a single-file text/plain fallback, and name fallback. Unit-tested in lib/dropped-files.test.mjs.
  • hooks/useDragDrop.ts: accept any kind === "file" drag, expose dragHasImages, pass the dataTransfer to the drop callback (needed to read text/uri-list, only readable at drop time).
  • components/ChatInput.tsx: new addFiles(files, dataTransfer) imperative method (images → existing attach flow; others → path insertion). The insertText logic is factored into a reusable insertTextAtCursor.
  • components/ChatWindow.tsx: routes drops to addFiles, swaps the overlay icon based on dragHasImages.
  • hooks/useAgentSession.ts: ChatInputHandle interface updated in sync.

Verification

  • tsc --noEmit clean
  • eslint clean on changed files
  • New unit tests pass (8/8); existing jiti-based tests pass, including the SSR ChatInput.test.mjs

Dropping a file from the OS file manager onto the chat window now works
for any file type, not just images.

- Images keep the existing behavior: they are attached as base64
  multimodal content and sent with the message.
- Other files get their real filesystem path inserted into the input at
  the caret, so the agent can read them with its own tools. Browsers do
  not expose the path on File objects, but OS-originated drags carry
  file:// URIs on the dataTransfer's text/uri-list; since pi-web is a
  local app the path is directly usable. Falls back to the bare file
  name when no path is available.
- The drop overlay icon now distinguishes image vs file drags.

Adds lib/dropped-files.ts with unit-tested parsing logic.
Images were filtered out before resolving paths, so the file count no
longer matched the text/uri-list entries and mixed drops (image + file)
fell back to bare file names. Resolve paths against the full file list
first, then split images from path references.
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.

1 participant