Skip to content

feat(web): rebuild streaming demo as a verified download + rename demos to concepts - #886

Merged
heavygweit merged 12 commits into
mainfrom
fix/streaming-demo-verified-download
Jul 17, 2026
Merged

feat(web): rebuild streaming demo as a verified download + rename demos to concepts#886
heavygweit merged 12 commits into
mainfrom
fix/streaming-demo-verified-download

Conversation

@heavygweit

@heavygweit heavygweit commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Rebuilds the streaming tab around a single idea: a progressive verified download — the generated grid file streams in chunk by chunk, each chunk arriving as a Bao slice that is verified against the root before a byte of it is shown.

The experience

  • The download auto-starts on load and paints the image row by row from verified bytes only. A chunk-summary line (9 FastCDC chunks, avg size) keeps the content-defined-chunking story visible.
  • Corrupt the connection (a switch above the controls): while it's on, every fetch arrives tampered and the verifier rejects every attempt — the stream pins at the current chunk, the image visibly stops filling (red stall band at the paint row), waste and rejection counters climb, and a narration line explains the stall. Flip it off and the stream resumes where it stalled. Corruption never reaches the image, and the demo never pretends otherwise.
  • Toggling the switch while idle restarts the download so the effect is always immediately visible.
  • Idle state (after Reset) is a single Start CTA; controls appear once a download exists.
  • Per design review: no uploads (the demo demonstrates a concept, not a file manager), no side-by-side panes, minimal copy.

Also in this PR

  • Page rename: /demos/concepts (nav label, slug, SEO). All six legacy demo routes 301 to /concepts#…, plus a new /demos/concepts redirect; tests cover all seven.
  • Bug fixes: the originally-reported tamper-slider snap-back, and a measured perf stall (the old auto-edit loop + three extra demo sections saturated the main thread with ~1.7 s tasks in dev, starving the stream timer to ~3.4 s/chunk; the rebuilt page streams at its designed 600 ms cadence with zero long tasks — headless-probe verified).
  • Structure: pure PPM helpers extracted to lib/ppm.ts; streaming-demo.tsx is 461 lines (was 924 before the PR) with the upload-era generality (per-tick batching, non-PPM fallbacks) removed.

Measured numbers (Node, against the built wasm)

Input chunks outboard per-chunk proof overhead
default 768 KiB PPM 9 48 KiB (6.25%) ~6% of chunk

🤖 Generated with Claude Code

heavygweit and others added 2 commits July 17, 2026 10:45
The reset effect re-anchored `tamperByte` to the file's midpoint on every
change of the `FileAsset` object, and auto-edit replaces that object every
500 ms — so the slider snapped back to center within half a second of the
user releasing it. Clamp to the new byte length instead of re-centering on
object identity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`StreamingBaoVerify` reset its playback state on every `file`-identity
change, and `baoFile` (its input) updated every 4th auto-edit tick — so
playback and the tamper dropdown got killed mid-use roughly every 2s. Same
bug class as the tamper-slider snap-back.

Replace it with `StreamingVerifiedDownload`: pressing Start snapshots the
current file once (chunks, bao root, outboard), then streams it back as a
sequence of bao slices, verifying each against the root as it lands. Verified
bytes assemble into a buffer that paints the default PPM's canvas
progressively, top row to bottom, so the image visibly fills in only from
bytes that passed verification. A "Corrupt the connection" toggle flips a
byte in every freshly-fetched slice; the verifier rejects it (visible red ×
on the chunk strip), then the retry arrives clean and painting resumes.

Because the section now snapshots at Start instead of tracking the live
file, no effect is keyed on `file` identity, and the recurring `bao_encode`
pass (the demo's heaviest wasm call) no longer runs on a timer — it runs
once, on click. This removes the `baoFile` / `baoTickRef` /
`BAO_THROTTLE_TICKS` throttle machinery from `StreamingDemo` entirely.

Measured (Node experiment against the built wasm pkg):

| Input             | chunks | outboard             | proof/slice   | bao_slice  | bao_verify_slice |
|-------------------|--------|-----------------------|---------------|------------|-------------------|
| default ~768 KiB  | 9      | 49,160 B (6.25%)      | 1.9-13.9 KiB  | ~0.1 ms    | ~0.15 ms          |
| 16 MiB random     | 197    | 1,048,520 B (6.25%)   | 2.2-18.3 KiB  | ~0.3 ms    | ~0.1 ms           |
| 128 MiB (cap)     | 1,576  | 8,388,552 B (6.25%)   | 2.4-9.9 KiB   | 3-11 ms    | ~0.2 ms           |
| 19 B file         | 1      | 8 B                   | 8 B           | ~0         | ~0                |

At the 128 MiB cap, the one-time Start cost is ~320 ms sync
(`chunk_boundaries` 189 ms + `bao_encode` 133 ms) — acceptable in a click
handler.

No new tests: jsdom lacks canvas, so the parent `StreamingDemo` already
can't mount under vitest; existing suites stay green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heavygweit
heavygweit requested a review from a team as a code owner July 17, 2026 14:46
@heavygweit heavygweit added this to the post-release improvements milestone Jul 17, 2026
@heavygweit heavygweit added bug Something isn't working web Web demo, WASM packaging, or browser behavior labels Jul 17, 2026
@heavygweit heavygweit self-assigned this Jul 17, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
mkit-web 2026817 Commit Preview URL

Branch Preview URL
Jul 17 2026, 05:39 PM

@heavygweit

Copy link
Copy Markdown
Contributor Author

Code quality review

Overall this is a solid change — the snapshot-at-Start design deletes the baoFile/BAO_THROTTLE_TICKS throttle machinery rather than adding to it, the download state is a clean three-phase machine, and no reset effect keyed on file identity remains anywhere. Three findings, in priority order:

  1. The PR pushes streaming-demo.tsx past 1k lines (924 → 1016). The file mixes UI with ~200 lines of pure byte/format helpers that have no React dependency (PPM generate/encode/decode, header detection, byte mutation, the baseline-grid cache). Can we move those to apps/web/src/lib/ppm.ts, alongside the other pure helpers in lib/? That drops the component file to ~815 lines and separates data-shaping from UI — pure relocation, no new abstraction.

  2. This adds a fifth copy of the chunk_boundariesStripChunk[] mapping loop (chunker, blob, delta ×2, and now start()). A module-local stripChunks(r) helper used at all five sites would delete the duplication.

  3. reset() puts the module-level DOWNLOAD_IDLE — including its shared verified Set instance — into live state. It's currently safe only because every reader copies before mutating, which is a latent aliasing footgun. Replace the constant with a freshDownloadState() factory so every state reset gets its own Set (as start() already does manually).

No structural regressions otherwise: the corrupt/retry flow reads clearly, the counters' arithmetic checks out (proof = wire − wasted − verified), and the slider fix is exactly as small as it should be.

heavygweit and others added 2 commits July 17, 2026 10:54
…hunk mapping

Extracts the pure byte/format helpers into apps/web/src/lib/ppm.ts, dedupes the five copies of the chunk-mapping loop behind a stripChunks helper, and replaces the DOWNLOAD_IDLE shared constant with a freshDownloadState() factory. Addresses the code-quality review findings on #886.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ers<>

The overridesRef type in streaming-demo.tsx leaned on Parameters<typeof
generateDefaultPpm>[1] to avoid exporting the type — indirection where a
one-line type export is the direct contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heavygweit

Copy link
Copy Markdown
Contributor Author

Review findings addressed in 21970c5 and a040d87:

  1. streaming-demo.tsx is back to 805 lines — the pure PPM/byte helpers now live in apps/web/src/lib/ppm.ts (213 lines), pure relocation.
  2. The chunk-mapping loop is deduplicated behind a module-local stripChunks, used at all five sites.
  3. DOWNLOAD_IDLE is replaced by a freshDownloadState() factory, so no state reset shares a Set instance.

All four checks pass locally (fmt / lint / typecheck / test, 214/214).

heavygweit and others added 8 commits July 17, 2026 11:51
Erica's direction: the old chunker/blob/delta sections and the auto-edit loop are gone — the progressive verified download is now the entire streaming demo. This also fixes a measured perf stall: the auto-edit loop plus the old sections saturated the main thread with ~1.7s long tasks in dev, starving the stream timer down to ~3.4s/chunk; with them removed the designed ~600ms cadence is restored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…download

Corruption was invisible by design: rejected bytes never reached the verified
image, so the toggle just filled a button. Add an unverified pane next to the
verified one that paints whatever arrives, first attempt wins, so tampered
chunks land and stay while the verified pane rejects, stalls, and re-fetches
clean. The corrupt toggle now restarts an idle stream so the effect is
immediate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Panes now sit side by side (w-40 pins caption wrap), canvases actually
clear on restart (clearRect before the translucent placeholder fill),
and corrupted spans render as solid black stripes — XOR noise was
invisible against the noise-grid default file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, CTA idle state, drop uploads

Implements Erica's live-page annotation feedback on the verified-download demo: collapses the raw/verified comparison to one verified image, turns the corrupt control into an accessible switch placed above the buttons, and makes the idle state a lone Start CTA. Removes duplicated/explanatory copy and upload support (drag-and-drop, file input, size cap, and the now-dead FileAsset.source field and FilePreview blob/img fallback) since the demo only ever streams its generated PPM.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pill was a bare role='switch' button whose only child was aria-hidden —
no accessible name, and clicking the visible text did nothing. Track, thumb,
and label now live inside the button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The old "retry arrives clean" rule made corruption consequence-free: every
rejected chunk auto-healed on its second attempt, so the image always
completed and the chunk strip just flickered red. Now every attempt is
tampered while the toggle is on — the stream pins at the current chunk,
waste climbs, and the image visibly stops filling until the connection is
fixed. Deletes the retryPending/isRetry mode and the per-tick batching
that only existed for the removed 128 MiB uploads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heavygweit heavygweit changed the title fix(web): streaming demo — sticky controls + progressive verified download feat(web): rebuild streaming demo as a verified download + rename demos to concepts Jul 17, 2026
@heavygweit
heavygweit merged commit 7fef36a into main Jul 17, 2026
36 checks passed
@heavygweit
heavygweit deleted the fix/streaming-demo-verified-download branch July 17, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working web Web demo, WASM packaging, or browser behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant