web: open the file an answer names, from the answer - #180
Merged
Conversation
An answer that ends "the PDF is at `report.pdf`" already says where the file is, and the only way to see it was to open the Files pane and find that name in the list by hand. `web/src/file-links.ts` turns an inline code span, or a relative markdown link, that names a listed file into a button, and pressing it opens that file in the artifact pane through the same two calls an investigate run's draft goes through. `main.ts` runs the pass when an answer is drawn, on the replay path and the live one, and again whenever a listing arrives, since an answer can name a file before the listing has caught up. The listing is the only thing that decides what is a file. A code span it does not know stays a code span, so `ClusterIP` in an answer about Kubernetes is still text, and a name the model wrote can only ever reach a file the workspace has already reported. An exact match is tried first, then the basename and only when exactly one listed file carries it: two files with that name resolve to nothing, because a guess there opens the wrong document while looking like it worked. A link with a scheme is never captured either. The pane's own display rules are untouched. This decides which path to open and nothing else, so a `.svg` or a `.html` still goes into the sandbox frame and a `.pdf` into the viewer frame. Nothing here fetches a file and nothing assembles markup: the name lands through `textContent` and `dataset`, and reaches a URL only through `artifactUrl`. Replacing the code span with the button rather than decorating it is what makes a second pass a no-op. `npm run check`, `npm test` (419 pass, 11 new) and `npm run build` from `web/`. No Rust changed. Claude-Session: https://claude.ai/code/session_01PG2vuux5tMsjnZBLWJzMyp
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.
An answer that ends "the PDF is at
k8s-load-balancing.pdf(51 KB)"already says where the file is. Until now the only way to see it was to
open the Files pane and find that same name in the list by hand.
What this does
web/src/file-links.tsexports one function. It takes a root element,the set of known artifact paths and a callback, and turns every
reference in that subtree that names a listed file into a button.
Pressing it opens the artifact pane on that file, through the same two
calls
investigate_donealready uses.main.tsruns the pass when an assistant message is drawn, on thereopen path in
openSessionand on the live path where a streamedmessage finishes, and again whenever a listing arrives: an answer can
name a file before the listing has caught up with it. Running it again
is a no-op on what it already did, because the upgrade replaces the code
span rather than decorating it, so there is nothing left for a second
pass to find.
The listing is the only thing that decides what is a file
A code span the listing does not know stays a code span, so
ClusterIPand
NodePortin an answer about Kubernetes are still text. A name themodel wrote can only ever reach a file the workspace has already
reported, which is the containment as well as the accuracy: no reference
in an answer leads out of the workspace. A link carrying a scheme is
never captured either, or an external URL whose last segment happened to
match a listed name would stop reaching the site it named.
An exact match is tried first, after a leading
./is stripped, thenthe basename and only when exactly one listed file carries it. Two files
with that basename resolve to nothing and the reference stays plain
text: opening one of them would be a guess, and a guess there opens the
wrong document while looking like it worked.
What is untouched
The pane's own display rules. This decides which path to open and
nothing else, so a
.svgor an.htmlstill goes into the sandboxframe and a
.pdfinto the viewer frame, exactly as the note at the topof
artifact-view.tssays. Nothing here fetches or inlines a file.Nothing assembles markup either. The name goes onto the button through
textContentand intodataset, and reaches a URL only through theexisting
artifactUrl, which encodes it. The upgraded reference is areal
button, so the keyboard reaches it and a screen reader says whatit is; it keeps the monospace look, gains an underline and a pointer,
and carries a
titlesaying it opens in the file pane.No
.rsfile changed and no server change was needed.Checks
From
web/:npm run check,npm test(419 pass, 11 of them new) andnpm run build, all clean.The 11 new cases cover a listed name becoming a button and opening on
click, an unlisted name left alone, a basename resolving to a full path,
an ambiguous basename left alone,
./name.md, a markdown link openingthe pane instead of navigating, an external link never captured, a
second pass wrapping nothing twice, a name carrying markup landing as
text with no element built from it, and an empty listing changing
nothing.
https://claude.ai/code/session_01PG2vuux5tMsjnZBLWJzMyp