Fix robustness, white border, GIF size estimate

- Fix Robust profile: V20-Q (97 modules, 315px) instead of broken V31-Q
  — fewer modules = bigger squares = camera-friendly
  — K=16, R=16 (100% overhead for max robustness)
  — Actual max payload: 450 bytes (verified against QR library)
- Fix Balanced: V25-Q, K=20, R=12
- Fix Fast: V35-M, K=24, R=8
- Remove ugly white border: global CSS reset (margin:0, padding:0)
- Remove broken estimated GIF size (was off by orders of magnitude)
- Show actual GIF size after generation instead
- Add 8MB file size limit
- Remove warning banners
This commit is contained in:
Hermes Agent
2026-05-02 22:08:36 +00:00
parent b20e5c9c17
commit efdf9d0e13
4 changed files with 27 additions and 31 deletions
+4
View File
@@ -3,6 +3,10 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0d1117; }
</style>
<title>QR-over-GIF Transfer</title>
</head>
<body>
+4 -12
View File
@@ -204,6 +204,7 @@ export function SenderPage() {
mime = 'text/plain';
} else {
if (!file) { setError('Please select a file.'); return; }
if (file.size > 8 * 1024 * 1024) { setError('File too large. Maximum size is 8 MB.'); return; }
data = await file.arrayBuffer();
filename = file.name;
mime = file.type || 'application/octet-stream';
@@ -307,7 +308,7 @@ export function SenderPage() {
}, [gifResult, profileId, stats]);
// ─── Compute warnings ──────────────────────────────────────────────────────
const showWarning = stats && stats.estimatedGifBytes > 5 * 1024 * 1024;
const showWarning = false;
return (
<div>
@@ -407,18 +408,9 @@ export function SenderPage() {
<span style={S.infoValue}>{stats.frameCount}</span>
<span style={S.infoLabel}>Generations</span>
<span style={S.infoValue}>{stats.totalGenerations}</span>
<span style={S.infoLabel}>Estimated GIF size</span>
<span style={S.infoValue}>{formatBytes(stats.estimatedGifBytes)}</span>
<span style={S.infoLabel}>Estimated throughput</span>
<span style={S.infoValue}>
{estimateThroughput(stats.originalSize, stats.frameCount, profileId)}
</span>
<span style={S.infoLabel}>GIF size</span>
<span style={S.infoValue}>{gifResult ? formatBytes(gifResult.gifData.byteLength) : '…'}</span>
</div>
{showWarning && (
<div style={S.warn}>
Large GIF ({formatBytes(stats.estimatedGifBytes)}) transfer may take several minutes.
</div>
)}
</div>
)}
</div>
+16 -16
View File
@@ -54,11 +54,11 @@ export enum Flags {
/** QR profile identifiers. */
export enum ProfileId {
/** Robust profile: QR V31, ECC Q, K=24, R=12. */
/** Robust profile: QR V20, ECC Q, K=16, R=16 (100% overhead, big modules). */
ROBUST = 0,
/** Balanced profile: QR V35, ECC M, K=24, R=8. */
/** Balanced profile: QR V25, ECC Q, K=20, R=12 (60% overhead). */
BALANCED = 1,
/** Fast profile: QR V40, ECC M, K=32, R=8. */
/** Fast profile: QR V35, ECC M, K=24, R=8 (33% overhead). */
FAST = 2,
}
@@ -86,28 +86,28 @@ export interface ProfileConfig {
/** Lookup of all defined profiles by their ProfileId. */
export const PROFILES: Record<ProfileId, ProfileConfig> = {
[ProfileId.ROBUST]: {
qrVersion: 31,
qrVersion: 20,
eccLevel: 'Q',
k: 24,
r: 12,
k: 16,
r: 16,
frameDelay: 30,
maxPacketPayload: 1230,
maxPacketPayload: 450,
},
[ProfileId.BALANCED]: {
qrVersion: 25,
eccLevel: 'Q',
k: 20,
r: 12,
frameDelay: 20,
maxPacketPayload: 683,
},
[ProfileId.FAST]: {
qrVersion: 35,
eccLevel: 'M',
k: 24,
r: 8,
frameDelay: 20,
maxPacketPayload: 1770,
},
[ProfileId.FAST]: {
qrVersion: 40,
eccLevel: 'M',
k: 32,
r: 8,
frameDelay: 15,
maxPacketPayload: 2290,
maxPacketPayload: 1777,
},
};
+3 -3
View File
@@ -46,9 +46,9 @@ describe('Constants', () => {
expect(p.maxPacketPayload).toBeGreaterThan(100);
}
});
it('Robust has highest overhead (K=24, R=12)', () => {
expect(PROFILES[ProfileId.ROBUST].k).toBe(24);
expect(PROFILES[ProfileId.ROBUST].r).toBe(12);
it('Robust has highest overhead (K=16, R=16)', () => {
expect(PROFILES[ProfileId.ROBUST].k).toBe(16);
expect(PROFILES[ProfileId.ROBUST].r).toBe(16);
});
it('createSessionId returns a bigint', () => {
const id = createSessionId();