// Bootstrap window.app for the browse tool. Mirrors the convention // used by every other ZDDC tool — ./build's CSS/JS concat order means // this file runs FIRST inside the IIFE-of-IIFEs. (function () { 'use strict'; if (!window.app) { window.app = { modules: {}, state: {} }; } // Mount the shared Lucide outline-icon sprite into before // the tree first renders. The sprite is hidden (display:none on // the outer ) — it only exists so per-row // refs resolve. Falls back to deferring until DOMContentLoaded // when isn't ready yet. if (window.zddc && window.zddc.icons) { window.zddc.icons.inject(); } window.app.state = { // Source: 'server' | 'fs' | null. Determines how the loader // resolves entries. source: null, // For server-source: the URL path of the directory currently // being viewed. Always starts with '/' and ends with '/'. // For fs-source: the displayed path string (no semantic // meaning — just for the toolbar). currentPath: '/', // FileSystemAccessAPI root handle (null in server mode). rootHandle: null, // Sort state. key: 'name' | 'size' | 'ext' | 'date'. dir: 1 or -1. sort: { key: 'name', dir: 1 }, // Currently-selected tree node id (for highlight + pop-out). selectedId: null, lastPreviewedNodeId: null, // View mode: 'browse' (tree + preview, default) | 'grid' (classifier). viewMode: 'browse', // The tree's in-memory representation. Each node: // { id, name, isDir, size, modTime, ext, url, handle, depth, // parentId, expanded, loaded, childIds, isZip, // _zipDirHandle, virtual } // - isZip: the node IS a .zip file; expanding it lists // the zip's members (server "<…>.zip/" listing // online, JSZip behind a ZipDirectoryHandle // offline). Members are ordinary dir/file nodes. // - _zipDirHandle: cached ZipDirectoryHandle for an opened zip // (offline / nested-in-zip path only). // - handle: a FileSystemFileHandle/DirectoryHandle (fs // mode) — or, inside an opened zip, a // ZipFileHandle/ZipDirectoryHandle. // Stored flat in a Map keyed by id; render order derived // from a depth-first walk. nodes: new Map(), rootIds: [], nextId: 1, // Single shared popup window for file preview (across // multiple file clicks). Same pattern as archive's preview. previewWindow: null, // Cascade-resolved scope flags, refreshed on each listing // fetch from response headers. // scopeDropTarget: cascade's drop_target at currentPath // scopeDefaultTool: cascade's default_tool at currentPath // (empty when no default declared) // scopeCanonicalFolder: cascade's canonical-folder slot // ('incoming'|'received'|'working'|'staging'|…), // drives scope-aware menu items // scopeOnPlanReview: cascade above has an on_plan_review block // All refreshed by loader.js from response headers on each fetch. scopeDropTarget: false, scopeDefaultTool: '', scopeCanonicalFolder: '', scopeOnPlanReview: false, // Prefetched /.profile/access view for the CURRENT scope // (state.currentPath), via cap.at() — memoised. Supplies // path_verbs / path_is_admin / path_roles to the menu model for // pane-scope create gating and the admin/sub-admin tier items, so // the menu never fetches at open time. null until prefetched / in // FS-Access (offline) mode. scopeAccess: null, // Whether the listing includes dotfiles. Toggled by the // "Show hidden files" menu item; URL-persisted via ?hidden=1. showHidden: false, // Autofilter — when non-empty, the tree hides files that // don't match and folders whose subtree has no matches. // Parsed once on input change so visibleIds() / rowHtml() // can run filter.matches(text, ast) cheaply per node. filterText: '', filterAST: null }; })();