Skip to content

Commit 970d9a9

Browse files
committed
feat(web): preserve file MIME type in the .pbf.json container
The browser File API gives file.type for free, so the Encode tab now records it as an optional 'mime' field (omitted when the browser reports none). On decode the metadata panel shows it and, more usefully, the download Blob uses it instead of generic application/octet-stream so images/pdfs/etc. open correctly. CLIs ignore and omit it (no MIME detection dependency); the field is additive/backward-compatible.
1 parent afbbf2f commit 970d9a9

3 files changed

Lines changed: 7 additions & 5 deletions

File tree

index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ <h1>PrintableBinary Encoder/Decoder</h1>
340340
if (fmt === 'container') {
341341
updateProgress(40);
342342
const bytes = new Uint8Array(await file.arrayBuffer());
343-
const container = encoder.encodeToContainer(bytes, { filename: file.name, modified_ms: file.lastModified });
343+
const container = encoder.encodeToContainer(bytes, { filename: file.name, modified_ms: file.lastModified, mime: file.type || undefined });
344344
const json = JSON.stringify(container, null, 2);
345345
updateProgress(100);
346346
const dur = ((performance.now() - start) / 1000).toFixed(2);
@@ -385,6 +385,7 @@ <h1>PrintableBinary Encoder/Decoder</h1>
385385
if (m.created_ms != null) html += `<div class="mp-row"><span class="mp-key">created</span> ${new Date(m.created_ms).toLocaleString()}</div>`;
386386
if (m.mode != null) html += `<div class="mp-row"><span class="mp-key">mode</span> ${escapeHtml(String(m.mode))}</div>`;
387387
if (m.owner != null) html += `<div class="mp-row"><span class="mp-key">owner</span> ${escapeHtml(String(m.owner))}${m.group != null ? ':' + escapeHtml(String(m.group)) : ''}</div>`;
388+
if (m.mime) html += `<div class="mp-row"><span class="mp-key">type</span> ${escapeHtml(String(m.mime))}</div>`;
388389
html += `<div class="mp-row"><span class="mp-key">integrity</span> <span class="mp-ok">✓ CRC-32 verified</span></div>`;
389390
} else {
390391
html += `<div class="mp-title">Raw printable-binary → decoded</div>`;
@@ -410,7 +411,7 @@ <h1>PrintableBinary Encoder/Decoder</h1>
410411
renderMeta(res);
411412
decStats.textContent = `Decoded ${formatFileSize(res.bytes.length)} in ${dur}s — ready to download as “${res.filename}”.`;
412413
decDownloadBtn.disabled = false;
413-
decDownloadBtn.payload = { bytes: res.bytes, filename: res.filename, mime: 'application/octet-stream' };
414+
decDownloadBtn.payload = { bytes: res.bytes, filename: res.filename, mime: res.meta.mime || 'application/octet-stream' };
414415
decClearBtn.disabled = false;
415416
} catch (err) {
416417
metaPanel.classList.remove('visible');

js/printable_binary.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ class PrintableBinary {
713713
crc32: this.crc32hex(bytes),
714714
crc32_encoded: this.crc32hex(dataBytes),
715715
};
716-
for (const k of ["modified_ms", "created_ms", "mode", "owner", "group"]) {
716+
for (const k of ["modified_ms", "created_ms", "mode", "owner", "group", "mime"]) {
717717
if (meta[k] !== undefined && meta[k] !== null) container[k] = meta[k];
718718
}
719719
// `data` is appended LAST so all metadata sits up front (JS object key
@@ -760,7 +760,7 @@ class PrintableBinary {
760760
}
761761
}
762762
const meta = {};
763-
for (const k of ["filename", "modified_ms", "created_ms", "mode", "owner", "group"]) {
763+
for (const k of ["filename", "modified_ms", "created_ms", "mode", "owner", "group", "mime"]) {
764764
if (c[k] !== undefined && c[k] !== null) meta[k] = c[k];
765765
}
766766
return { bytes, meta };

test/js/test_printable_binary.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ console.log('\n--- Container (printable-binary-file.json) Tests ---');
316316
// Container round-trip (MFIC inverse-pair oracle): bytes + metadata identical
317317
const bytes = new Uint8Array([0, 1, 2, 255, 254, 65, 66, 10, 9, 0x7f, 0x80, 0]);
318318
const container = encoder.encodeToContainer(bytes, {
319-
filename: "x.bin", modified_ms: 1719430000000, mode: "0644",
319+
filename: "x.bin", modified_ms: 1719430000000, mode: "0644", mime: "image/png",
320320
});
321321
assertEquals(container.format, "printable-binary-file", "container has format discriminator");
322322
assertEquals(container.version, 1, "container version 1");
@@ -328,6 +328,7 @@ console.log('\n--- Container (printable-binary-file.json) Tests ---');
328328
assertEquals(meta.filename, "x.bin", "container round-trip: filename restored");
329329
assertEquals(meta.modified_ms, 1719430000000, "container round-trip: modified_ms restored");
330330
assertEquals(meta.mode, "0644", "container round-trip: mode restored");
331+
assertEquals(meta.mime, "image/png", "container round-trip: mime restored");
331332

332333
// Round-trip through a JSON string too (the on-disk form)
333334
const rt = encoder.decodeFromContainer(JSON.stringify(container));

0 commit comments

Comments
 (0)