Skip to content

Show files sent with SendUserFile in the web UI (inline preview or clickable link) #434

Closed

Description

@manolitto

Problem

When Claude sends files with the SendUserFile tool, herdr-web-ui shows only the raw tool call and its text output. The files can't be seen or opened from the web UI. This happens in both modes:

Terminal mode: the caption and plain-text local paths:

⏺ <caption text>

  › [image] /private/tmp/claude-<uid>/<project-slug>/<session-id>/scratchpad/image-a.png (258.6KB)
  › [image] /private/tmp/claude-<uid>/<project-slug>/<session-id>/scratchpad/image-b.png (491.8KB)

Chat mode: a collapsible JSON block plus a text output:

{
  "files": [
    "/private/tmp/claude-<uid>/<project-slug>/<session-id>/scratchpad/image-a.png",
    "/private/tmp/claude-<uid>/<project-slug>/<session-id>/scratchpad/image-b.png"
  ],
  "caption": "<caption text>",
  "status": "normal",
  "display": "render"
}
2 files delivered to user.
  /private/tmp/.../scratchpad/image-a.png → file_uuid: <uuid-a>
  /private/tmp/.../scratchpad/image-b.png → file_uuid: <uuid-b>

SendUserFile exists to put a result (screenshot, rendered preview, report) in front of the user, often when they follow the session from another device. In the web UI the user only gets a local path, which they can't open from a phone or a remote browser.

Proposal

Render SendUserFile calls as a proper message instead of raw JSON:

  • Caption shown as normal message text.
  • Images (png, jpg, gif, webp, svg) shown as inline previews when "display": "render" is set. Clicking one opens the full-size image.
  • Other files (PDF, HTML, text, archives…) shown as a clickable chip with the file name and size, which opens or downloads the file.
  • Raw tool input/output kept in a collapsed "details" section.
  • Terminal mode: turn the › [image] <path> lines into links (or show a preview on hover/tap).

Implementation notes:

  • The tool input already has everything needed: files[], caption, display. The output maps each path to a file_uuid, which could serve as a stable ID for a route such as /files/<file_uuid>.
  • herdr-web-ui already stores and shows pasted images (.herdr-web-ui/paste-*.png), so that serving path could probably be reused.
  • Security: only serve paths that actually appeared in a SendUserFile call of the current session. No general "serve any local path" endpoint. Resolve symlinks and reject .. before checking.
  • Scratchpad files under /private/tmp/... may disappear after the session ends or after a reboot. Show a "file no longer available" placeholder instead of a broken image.

Where

Chat and prompt cards

Workarounds and alternatives

Current workarounds

  • Copy the path from the tool output and open it locally (open <path> on macOS). This only works on the machine running the agent, not from another device.
  • Ask Claude to publish the content in another form (e.g. a hosted artifact/page) and share the link. This works, but it's an extra step and it's no longer the file itself.
  • Have Claude copy the file into a folder synced to the other device. This is manual, slow, and clutters the project.

Alternatives considered

  • Links only, no inline preview: simpler and still fixes the core problem. Inline image previews could come later.
  • Copy files into .herdr-web-ui/ when the tool is called: keeps them available after the scratchpad is cleaned up, but uses disk space and needs a cleanup policy.
  • Generic local file browser in the web UI: much more powerful, but a far bigger security surface. Limiting access to the files Claude explicitly sent is the safer choice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions