feat(v2-chat): clicking a file pill in chat opens it in the inspector#303
Closed
lilyshen0722 wants to merge 1 commit intomainfrom
Closed
feat(v2-chat): clicking a file pill in chat opens it in the inspector#303lilyshen0722 wants to merge 1 commit intomainfrom
lilyshen0722 wants to merge 1 commit intomainfrom
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
[[upload:fileName|name|size]]real-upload pill[[file:name.md]]static demo pillonOpenFile(older hosts)How it works
V2Layoutowns apendingOpenFileNamestate and threadsonOpenFile(name)down throughV2PodChat→V2MessageBubble→FilePill. The inspector watches the pending value, resolves against its already-loadedpodFiles[](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 --noEmitclean.mdfile pill in chat → inspector opens to artifact page → markdown renders.csvfile pill → inspector opens to artifact page → tabular preview[[file:name.md]]pill wherename.mdmatches a pod file → opens the pod-file artifactonOpenFile→ unchanged (new tab)🤖 Generated with Claude Code