parityCount: Math.ceil → Math.floor
- Small files (G ≤ 33) now truly get 0 parity generations
- Large files (G ≥ 34) still get proportional parity (≈3%)
- Previously Math.ceil always gave ≥1 parity for any G ≥ 1
neededPackets: K * sourceGenerations → K * totalGenerations
- Aligns with the round-robin scheduler: symbols are interleaved
across ALL generations, so practical minimum is K × totalGens
- Fixes the mismatch where 'needed' showed 32 but actual
decode required ~48 frames (for 2 source + 1 parity case)
Tests: Updated assembly test comments and totalGenerations params
to match new parityCount behavior.
Closes the long-standing 'needed frames count is off because of
outer error correction' issue.
- framesWithQR now counts ALL frames with a valid QR (including duplicates),
not just deduplicated ones. Moved increment before dedup check.
- acceptedPackets already correctly counts linearly-independent packets
(rank increase), capped at K per generation by the decoder.
- neededPackets = K * sourceGenerations, computed from constants.
- Keep generation count (solvedGens/sourceGens) as secondary indicator.
This gives an honest rough progress bar: useful can approach or briefly
touch required while some generations are still partially filled, but the
generation count is the definitive completion signal.
The compact indicator previously showed framesWithQR/acceptedPackets/neededPackets.
Because acceptedPackets counts parity-generation packets and uneven distribution
across generations, it could exceed neededPackets while the file was still not
decoded. This is confusing.
Change the primary compact indicator to generation counts:
framesWithQR / solvedGens / sourceGens gens
This is always meaningful: solved never exceeds required, and when they match,
the transfer is complete. Packet count is kept as a raw secondary number.
Small files are already well-protected by QR code ECC + fountain code.
Outer Reed-Solomon only kicks in when sourceGenerations ≥ 34,
where ceil(G * 0.03) ≥ 1. This eliminates 100% overhead for tiny files.
- parityCount(): remove Math.max(1, ...) floor
- Update tests to not assume parity always exists
- Increase outer_ec_benefit.test.ts payloads to >34 gens so
outer RS is actually active during benchmark
- Timer now starts when the first QR frame is detected (first progress
message from worker with totalFrames > 0) instead of when camera is
turned on. This eliminates the jump where elapsed time showed a few
seconds before any QR was actually seen.
- Replace separate 'scanned' and 'useful' stats with a compact
'QRs scanned/useful/required: X/Y/Z' indicator that clearly shows
progress toward the total packets needed for decode. The 'required'
count correctly accounts for outer RS overhead via K * sourceGens.
- Same display format applies to both camera and GIF-file input modes.
README.md now focuses on user-facing content:
- General description and live demo link
- Installation instructions (npm, Bun, npx, bunx)
- CLI usage examples (file, stdin, --serve)
- CLI flags table
- Dev setup with all relevant commands and scripts
- Link to ARCHITECTURE.md
ARCHITECTURE.md contains all technical implementation details:
- High-level architecture diagram
- Full project structure tree
- Runtime and dev dependencies
- Protocol spec (packet format, constants, metadata wrapping)
- Algorithm deep dives (RLNC, GF256, QR encode/decode, GIF, scheduling)
- Sender and receiver data flow diagrams
- Design decisions and rationale
- Common pitfalls
- Rename CLI entry from qr-terminal.ts to qr-stream.ts
- Rename package to qr-stream, version 0.6.0
- Add --serve / -s flag to start built-in static file server for web UI preview
- Include dist/ and src/ in npm files so consumers get both bundled CLI and source code
- Add static_server.ts helper using Node built-in http module
- Update help text and tests for new name and --serve flag
- Remove old qr-terminal.js from deployment
- Add MIT LICENSE
- Remove "private": true
- Set name to qr-transfer-terminal, version 0.5.0
- Add files, engines, keywords, repository, bugs, homepage fields
- Move typescript from peerDependencies to devDependencies
- Add prepublishOnly script to ensure fresh CLI bundle
- Add 'bin' field to package.json so npm/npx/bunx can invoke it as
'qr-terminal'. The bin points to dist/qr-terminal.js.
- Change shebang to '#!/usr/bin/env node' for universal compatibility.
- Fix displaced chars: each frame write now ends with '\n' and
moveCursorUp() appends '\x1b[G' to move to column 0. Previously the
cursor stayed at the end of the previous last line, causing redraws
to start mid-line.
- Position QR at top-left (removed padTop/padLeft centre logic) since
the alt-buffer is clean and only the QR is shown.
- Update help text to show npx/bunx invocations.
- Enter alternate screen buffer (\x1b[?1049h) before drawing and exit
it (\x1b[?1049l) on cleanup. This preserves the user's terminal
content when the app quits.
- Clear screen once after entering alt buffer so drawing starts from
the top-left.
- Add -h and --help flags that print usage, stdin/file modes, and
controls (q / Ctrl-C to quit).
- Update tests for new alt-buffer helpers and help text.
- Rebuild CLI bundle (dist/qr-terminal.js 95.4kb) and redeploy webapp.
- Stop clearing the entire screen every frame — now clears once on first
draw, then uses cursor-up (\x1b[nA) to overwrite the QR block in place.
- Removed frame counter, generation info, and 'press q' text — only the
QR code is shown, keeping the display minimal and static-area free.
- Simplified buildFrames return type (Uint8Array[] only).
- Added moveCursorUp() helper to terminal_raster.ts.
Terminal block characters render in the foreground colour; on dark-themed
terminals the QR looked inverted. Now every line is wrapped with
\x1b[47m\x1b[30m (white bg / black fg) so dark modules appear black and
light modules appear white on any terminal theme.
- renderToTerminal now pads with a 4-module quiet zone (white border)
horizontally and vertically, matching the webapp rasterizer.
- Keyboard input now tries /dev/tty first so q/Ctrl-C works even when
stdin is a pipe (e.g. echo ... | bun run src/cli/qr-terminal.ts).
- Update all CLI renderer tests for padded output; add tests for custom
and zero quiet zone.
- Increase outer_ec_benefit test timeout to 15s to avoid flaky failures.
- Rebuild CLI bundle and webapp; redeploy to nginx.
The transfer stats bar was wrapped in {scanning && ...} so elapsed
time and KB/s vanished when scanning auto-stopped on completion.
Add a persistent summary section that stays visible after scanning
stops, showing final transfer time, average throughput, and data size.
Uses formatDuration, formatBytes utility helpers already in the file.
- Add outer Reed-Solomon error correction across generations
(3% overhead, 1 parity gen per ~33 source gens)
- Fix progress display: show generations solved/needed instead of
raw packet counts, making outer EC benefit visible
- Add decode timer: tracks elapsed time from first frame to completion
and shows throughput in KB/s
- Add benchmark test confirming outer EC reduces tail waiting time
under biased frame loss
- Remove per-generation hash verification (superseded by outer RS)
- Update deployment base path to /hermes-web-demos/qr/