feat(web): keep the selected conversation in the address bar - #78
Merged
Conversation
Selecting a conversation changed the page and not the URL, so a reload went back to a new chat and the conversation you were reading was two clicks away again. Nothing you would reasonably bookmark, send to yourself, or reach with the back button was reachable. The id now rides in a query parameter, and the page opens whatever the URL names on load and on back/forward. A query parameter rather than a path segment on purpose. A path would need the server to serve index.html for /s/<id>, and a reload on a path the server does not know is exactly the bug being fixed, so the fix would have depended on fixing it twice. A query parameter rides along on the existing static route and needs no server change at all. Its own module because adding this makes the URL an input. Every other session id in the page comes from the server; this one comes from whatever is in the address bar, so it is parsed and refused rather than trusted. api.ts already encodes the value into the request path, so this is not the only thing between a hostile URL and a bad request, but a value that cannot be a real id should not produce a request at all. Traversal, spaces, angle brackets and anything over 128 characters are refused, and refusing means opening a new chat rather than showing an error. Three smaller decisions worth stating. Other query parameters survive a selection: token is how a non-loopback server is reached at all, and dropping it on a click would log the page out. Restoring from the URL does not push a history entry, since coming back to what the URL already says is not a navigation. And a link to a conversation that no longer exists falls through to a new chat, because clicking a stale link is a normal thing to do and not a fault to report. Eleven tests on the parsing and building, which is where the logic is. Verified in a browser as well: select, reload, and the transcript, sidebar highlight and title all come back. Claude-Session: https://claude.ai/code/session_01KGPVQ8wUG7h36zashWYCp4
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.
Selecting a conversation changed the page and not the URL, so a reload went back to a new chat and the conversation you were reading was two clicks away again. Nothing you would reasonably bookmark, send to yourself, or reach with the back button was reachable.
The id now rides in a query parameter, and the page opens whatever the URL names on load and on back/forward.
Why a query parameter and not a path
A path would need the server to serve
index.htmlfor/s/<id>. A reload on a path the server does not know is exactly the bug being fixed — the fix would have depended on fixing it twice. A query parameter rides along on the existing static route and needs no server change at all.The URL is now an input
Every other session id in the page comes from the server. This one comes from whatever is in the address bar, so it gets its own module and is parsed and refused rather than trusted.
api.tsalready encodes the value into the request path, so this is not the only thing between a hostile URL and a bad request — but a value that cannot be a real id should not produce a request at all:Refusing means opening a new chat, not showing an error.
Three smaller decisions
tokenis how a non-loopback server is reached at all; dropping it on a click would log the page out.Verification
11 unit tests on the parsing and building, which is where the logic is. Watched them fail before the module existed.
Also verified in a real browser: select a conversation → URL becomes
?session=18cdeff3d7c38b78-9dc2→ reload → transcript, sidebar highlight, and title all come back.npm run checkclean,npm testgreen (149 tests, up from 138),npm run buildclean.Pre-existing, not from this change: opening a session logs a 404 on
/api/sessions/:id/auto-approve.openSessionalready calledrefreshApprovalMode()before this PR, so the change only makes it visible on load too. Worth a separate look.https://claude.ai/code/session_01KGPVQ8wUG7h36zashWYCp4