Skip to content

feat(v2-chat): clicking a file pill in chat opens it in the inspector#303

Closed
lilyshen0722 wants to merge 1 commit intomainfrom
lily/v2-chat-file-opens-inspector
Closed

feat(v2-chat): clicking a file pill in chat opens it in the inspector#303
lilyshen0722 wants to merge 1 commit intomainfrom
lily/v2-chat-file-opens-inspector

Conversation

@lilyshen0722
Copy link
Copy Markdown
Contributor

Summary

Click a file pill in a chat message → the inspector opens to that file's preview (markdown rendered, CSV tabular, PDF in viewer, etc). Previously each click window.open()'d a signed URL and dumped raw bytes into a new tab.

Behavior

Trigger Before After
Click [[upload:fileName|name|size]] real-upload pill New tab, raw signed URL Inspector → ArtifactPreview (markdown rendered, CSV tabular, etc.)
Click [[file:name.md]] static demo pill Non-clickable Clickable; resolves by originalName if a real pod-file matches
Surfaces without onOpenFile (older hosts) New tab (unchanged) New tab (unchanged)

How it works

V2Layout owns a pendingOpenFileName state and threads onOpenFile(name) down through V2PodChatV2MessageBubbleFilePill. The inspector watches the pending value, resolves against its already-loaded podFiles[] (match on ObjectStore key OR originalName), routes to the artifact sub-page, clears the pending value. Declarative, survives re-mounts, no global event bus.

Test plan

  • tsc --noEmit clean
  • Click a .md file pill in chat → inspector opens to artifact page → markdown renders
  • Click a .csv file pill → inspector opens to artifact page → tabular preview
  • Click a static [[file:name.md]] pill where name.md matches a pod file → opens the pod-file artifact
  • Click a static pill with no backend match → no-op (clears pending, no navigation)
  • Surfaces that don't pass onOpenFile → unchanged (new tab)

🤖 Generated with Claude Code

Previously, clicking a file pill in a chat message minted a signed URL
and `window.open()`'d it in a new tab — so a `.md` file dumped raw
markdown source into the browser, a `.csv` triggered a download, etc.
The inspector already had a richer ArtifactPreview that renders
markdown via ReactMarkdown, pretty-prints JSON, slices CSVs into
tables, and wraps PDFs in a viewer — but it was unreachable from
chat. Users had to manually navigate to the inspector's Artifacts
section and re-find the file by name.

This commit threads a new `onOpenFile(fileName)` callback all the
way down (V2Layout → V2PodChat → V2MessageBubble → FilePill). When
provided:

- Clicking a clickable file pill (real upload) routes the inspector
  to that artifact's preview sub-page instead of opening a new tab
- Clicking a static `[[file:foo.md]]` demo token (no backend ref) now
  also tries to resolve via the inspector's podFiles by originalName,
  rendering as a clickable pill if a match exists

V2Layout introduces `pendingOpenFileName: string | null` state. The
chat sets it; the inspector watches for changes, resolves against
its loaded `podFiles[]` (matching either ObjectStore key or
originalName), calls `onOpenArtifact(\`file-\${_id}\`)`, and clears
the pending value via callback. Declarative, survives re-mounts,
and degrades gracefully — if the file isn't in the pod's files
index, the pending value clears with no navigation, no spinner.

Backwards compat: surfaces that don't pass `onOpenFile` (older
chat hosts, embedded surfaces) get the original `window.open()`
flow. No behavior change for them.

Why this exists
---------------
File previews in chat are the difference between "we attached a
spec.md to the conversation" and "click the spec to read it inline."
For a multi-agent workspace whose pitch is "agents produce real
artifacts," the second feels like a real product; the first feels
like a chat with attached blobs.

Net diff:
- V2Layout: 14 lines (pending state + handler)
- V2PodChat: 6 lines (prop pass-through)
- V2MessageBubble: 32 lines (prop typed, threaded to FilePill,
  static-pill case made clickable when handler present)
- V2PodInspector: 25 lines (props, resolver useEffect)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lilyshen0722 lilyshen0722 deleted the lily/v2-chat-file-opens-inspector branch May 4, 2026 17:55
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