Skip to content

Add a share button that puts the gzip'd CSV in the link - #53

Merged
aaronj1335 merged 2 commits into
mainfrom
claude/share-button-gzip-csv-sdg8wf
Sep 16, 2026
Merged

aaronj1335 merged 2 commits into
mainfrom
claude/share-button-gzip-csv-sdg8wf

Conversation

@aaronj1335

@aaronj1335 aaronj1335 commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

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'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, 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') plus btoa over 32 KiB chunks, since String.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.clipboard is 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 via replaceState (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.html is renderReport output with the same inlined __INITIAL_CSV__), and sharing from it is the point. http: and https: keep the button; file:, blob: and data: do not get one.

Testing

npm run validate passes — 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 both shareCSV paths.

Driven in Chromium as well, since the interesting parts are runtime behavior:

  • Clicking produced a 28.9 KB link whose fragment gunzips back to the source CSV; opening it in a fresh tab rendered the same 426 rows.
  • With navigator.clipboard stubbed to undefined, the button read "In URL bar" and the address bar held the same working link.
  • One CLI-built report.html, loaded both ways: over file:// no share button (upload button and all 426 rows still there), and served over http:// the button appeared and copied a working link.

🤖 Generated with Claude Code

https://claude.ai/code/session_01B7uWnd8S3d9M1kAsJTHom1

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
@aaronj1335
aaronj1335 merged commit d3763dc into main Sep 16, 2026
5 checks passed
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.

2 participants