feat(browse): navigate into a .zip via the server, no whole-zip download

When browse runs against zddc-server, expanding a top-level .zip now
fetches "<…>.zip/" as a normal directory listing (the server extracts
members on demand) instead of downloading the entire archive and
parsing it with JSZip in the browser. Members open/preview via their
real server URLs like any file. Nested zips (a .zip inside a .zip) and
FS-API (offline) mode keep the existing JSZip path; offline gets
migrated to a shared adapter in the next change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-12 12:19:28 -05:00
parent 5e4d4fefb3
commit 735fed89c2

View file

@ -280,18 +280,43 @@
// table); the tree.setSort() method still works but only via
// programmatic callers — there's no UI for changing sort yet.
// True when this zip node lives inside another zip (so its bytes
// can't be fetched as a standalone server resource — we go through
// the parent JSZip / inner-zip download path instead). In server
// mode the URL of a zip-member-inside-a-zip contains ".zip/"; in
// FS-API mode zipParentId is set.
function zipNestedInsideZip(node) {
if (node.zipParentId != null) return true;
if (state.source === 'server') {
return pathFor(node).toLowerCase().indexOf('.zip/') !== -1;
}
return false;
}
// Load a folder's children (lazy; idempotent re-loads). Dispatches
// by node kind:
// - regular folder → server JSON listing OR FS-API enumeration
// - zip file → fetch+JSZip; entries become virtual children
// - top-level zip, server mode → server's virtual-directory listing
// of the zip's members (no whole-zip
// download — zddc-server extracts on demand)
// - zip otherwise → fetch+JSZip; entries become virtual children
// - zip child dir → already-listed entries from the parent zip
// (zips are enumerated whole, so child dirs
// are pre-populated when the zip expands)
// (the whole zip was enumerated when it
// expanded, so child dirs are pre-seeded)
async function loadChildren(node) {
if (node.loaded) return;
try {
if (node.isZip) {
if (state.source === 'server' && !zipNestedInsideZip(node)) {
// zddc-server serves "<…>.zip/" as a virtual
// directory: GET it as a listing, GET a member URL
// to extract just that file. Same shape as any
// server directory, so no JSZip on the client.
setChildren(node.id,
await loader.fetchServerChildren(pathFor(node) + '/'));
} else {
await loadZipChildren(node);
}
} else if (node._zipSyntheticDir) {
// Synthetic dir node materialized when a zip's entry
// list referenced "a/b/file" but had no "a/" entry.