Repository navigation
Add a share button that puts the gzip'd CSV in the link - #53
Merged
Merged
Conversation
The `#csv=` fragment was read-only: a link that carries a whole dataset could only be built by going back to the file and running the README's `gzip | base64 | tr` pipeline over it -- which does not help at all for a CSV that got into the page through the upload button. So `csvFragment.ts` gains the other half, `encodeCSVFragment`, and a Share Link button sits next to Upload CSV and copies the result. The link is built in the tab out of the CSV text as it was read, options and comments intact, so nothing about the data leaves the machine -- the fragment never reaches a server, and the compression runs in the browser. `?csv=` is dropped from the link rather than carried along: the fragment outranks it on read, so keeping it would only pad the link with a second copy of the data -- the copy that does travel to the server. Where `navigator.clipboard` is missing -- a report served over plain http, or a permissions policy that withholds it -- the link goes to the address bar via `replaceState` instead, which is the same link one ctrl-L from being copied by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B7uWnd8S3d9M1kAsJTHom1
A report built by the CLI and opened off disk can still build a link -- it just builds `file:///home/you/report.html#csv=...`, which carries the whole dataset to someone who, to use it, already has the file. The test is the protocol, not whether the page is a report. The published site is a report too: `pages-public/index.html` is `renderReport` output with the same inlined `__INITIAL_CSV__`, and sharing from it is the point. So `http:` and `https:` keep the button and everything else -- `file:`, `blob:`, `data:` -- does not get one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B7uWnd8S3d9M1kAsJTHom1
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.
The
#csv=fragment was read-only. A link carrying a whole dataset could only be built by going back to the file and running the README'sgzip | base64 | trpipeline over it — which does not help at all for a CSV that got into the page through the upload button.So
csvFragment.tsgains the other half, and a Share Link button sits next to Upload CSV — outlined rather than filled, since uploading is what the toolbar is mostly for and two solid buttons side by side would not say which.Clicking it gzips whatever the page is currently plotting, base64url-encodes it, and copies
<this page>#csv=…to the clipboard, flashing "Copied!" for a couple of seconds. The compression runs in the tab and the fragment never reaches a server, so the data still never leaves the machine.What's here
src/csvFragment.ts—encodeCSVFragment, the inverse of the existing decoder:CompressionStream('gzip')plusbtoaover 32 KiB chunks, sinceString.fromCharCode(...bytes)on a megabyte overflows the argument stack. Padding is dropped; the output needs no percent-encoding.src/share.ts—buildShareURL/shareCSV/canShare.src/components/ShareButton.tsx— the button and its transient label.src/App.tsx— keeps the raw CSV text, not just the parsed dataset, so a shared link reopens the same file with its comments and{type: percent}-style column options intact.Three judgment calls
?csv=is dropped from the link. The fragment outranks it when the link is opened, so keeping it would only pad the URL with a second copy of the data — the copy that does travel to the server. Other query parameters (ymin,ymax) are left alone.There is a fallback for a missing clipboard.
navigator.clipboardis typed as always present but is absent outside a secure context — a report served over plain http, or a permissions policy that withholds it. There the link goes to the address bar viareplaceState(no history entry to back out of) and the button says "In URL bar".No button where the link would not open for anyone. A report opened off disk can still build a link; it just builds
file:///home/you/report.html#csv=..., which carries the whole dataset to someone who, to use it, already has the file. The test is the protocol rather than whether the page is a report — the published site is a report too (pages-public/index.htmlisrenderReportoutput with the same inlined__INITIAL_CSV__), and sharing from it is the point.http:andhttps:keep the button;file:,blob:anddata:do not get one.Testing
npm run validatepasses — lint, typecheck, format check and 182 tests, with new coverage for the encoder (gzip magic, the base64url alphabet, round-trips, and a payload past one argument per byte),buildShareURL,canShare, and bothshareCSVpaths.Driven in Chromium as well, since the interesting parts are runtime behavior:
navigator.clipboardstubbed toundefined, the button read "In URL bar" and the address bar held the same working link.report.html, loaded both ways: overfile://no share button (upload button and all 426 rows still there), and served overhttp://the button appeared and copied a working link.🤖 Generated with Claude Code
https://claude.ai/code/session_01B7uWnd8S3d9M1kAsJTHom1