Adds a UI checkbox next to the existing Sort dropdown that surfaces
hidden entries when ACL would otherwise allow read. Default off
(matches today's filtered behavior). On toggle, browse re-fetches
the current directory with ?hidden=1 and re-renders.
┌─ browse toolbar ─────────────────────────────────────────────┐
│ Sort: [Name (A→Z) ▾] ☐ Show hidden │
└──────────────────────────────────────────────────────────────┘
Server-side surface:
- internal/fs/tree.go ListDirectory gains an `includeHidden bool`
parameter. The .-prefix filter (previously hard-coded) now also
drops _-prefix entries (matches dispatch's reserved-prefix guard)
and honors the new flag.
- internal/handler/directory.go reads `?hidden=1` from the request
and threads it through.
- cmd/zddc-server/main.go dispatcher relaxes its dot-prefix and
_-prefix guards for GET/HEAD when `?hidden=1` is set, so clicking
a hidden entry's link works. `_app/` (apps cache) stays
unconditionally reserved — those bytes must go through the apps
resolver. Writes to hidden paths stay blocked (the file API has
its own segment check that the flag does NOT relax).
- internal/listing/listing.go: signature parity (the lower-level
helper that's used by tests + non-cascade listing paths).
Security model unchanged: the ACL chain on the parent dir is the only
real gate. Whoever can read the dir can see its contents — toggling
"Show hidden" just stops the client-side filter from masking
.-prefixed and _-prefixed entries. Hidden paths today:
• <dir>/.zddc ACL YAML — already exposed via /.profile/zddc
• <dir>/.converted/<base> cached MD→DOCX/HTML/PDF, same sensitivity as source
• <root>/.zddc.d/tokens/ per-token metadata; filename = sha256(token)
so not bearer-usable. Default root ACL
restricts to admins; matches /.tokens UI.
• <root>/.zddc.d/logs/ access logs; same admins-only audience
• <root>/_app/ cached upstream tool HTML (public)
• <root>/_template/ install.zip scaffolding (public)
None of these contain bearer credentials or secret material that the
existing ACL doesn't already gate. The walls are still the cascade.
172 lines
8.8 KiB
HTML
172 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>ZDDC Browse</title>
|
|
<link rel="icon" type="image/svg+xml" href="{{FAVICON}}">
|
|
<style>
|
|
{{CSS_PLACEHOLDER}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header class="app-header">
|
|
<div class="header-left">
|
|
<svg class="app-header__logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" aria-hidden="true">
|
|
<rect width="64" height="64" rx="12" fill="#1e3a5f"/>
|
|
<g fill="#fff">
|
|
<rect x="14" y="18" width="36" height="7"/>
|
|
<polygon points="43,25 50,25 21,43 14,43"/>
|
|
<rect x="14" y="43" width="36" height="7"/>
|
|
</g>
|
|
</svg>
|
|
<div class="header-title-group">
|
|
<span class="app-header__title">ZDDC Browse</span>
|
|
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
|
</div>
|
|
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
|
|
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh listing" aria-label="Refresh listing">⟳</button>
|
|
</div>
|
|
<div class="header-right">
|
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
|
<button id="help-btn" class="btn btn-secondary" title="Help" aria-label="Help">?</button>
|
|
</div>
|
|
</header>
|
|
|
|
<main id="appMain">
|
|
<div id="emptyState" class="empty-state empty-state--overlay">
|
|
<div class="empty-state__inner empty-state__inner--centered">
|
|
<h2>ZDDC Browse</h2>
|
|
<p>A two-pane file browser for ZDDC archives — and any directory.</p>
|
|
<ul class="welcome-list">
|
|
<li><b>Online</b> — when this page is served by zddc-server, the
|
|
listing for the current directory loads automatically.</li>
|
|
<li><b>Local</b> — click <i>Add Local Directory</i> to pick any folder
|
|
on your computer (Chromium-based browsers).</li>
|
|
</ul>
|
|
<p>Once loaded: click folders to expand, click files to preview them in
|
|
the right pane. Markdown files open in a full editor with TOC.
|
|
Switch to <b>Grid</b> mode to bulk-rename ZDDC files
|
|
spreadsheet-style.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="browseRoot" class="browse-root hidden">
|
|
<div class="browse-toolbar">
|
|
<nav class="breadcrumbs" id="breadcrumbs" aria-label="Path"></nav>
|
|
<span class="toolbar__count" id="entryCount"></span>
|
|
<button id="downloadZipBtn" class="btn btn-sm btn-secondary hidden"
|
|
title="Download this folder (and everything under it you can access) as a .zip"
|
|
aria-label="Download this folder as a zip">⤓ Download (zip)</button>
|
|
<label class="sort-control" for="sortBy" title="Sort tree entries">
|
|
<span class="sort-control__label">Sort:</span>
|
|
<select id="sortBy" class="sort-control__select" aria-label="Sort tree entries">
|
|
<option value="name:asc">Name (A→Z)</option>
|
|
<option value="name:desc">Name (Z→A)</option>
|
|
<option value="date:desc">Modified (new→old)</option>
|
|
<option value="date:asc">Modified (old→new)</option>
|
|
<option value="size:desc">Size (large→small)</option>
|
|
<option value="size:asc">Size (small→large)</option>
|
|
<option value="ext:asc">Type (A→Z)</option>
|
|
</select>
|
|
</label>
|
|
<label class="sort-control" for="showHidden"
|
|
title="Surface .-prefixed and _-prefixed entries (.zddc, .converted/, _app/, …). ACL still applies — you only see what you'd already be allowed to read.">
|
|
<input type="checkbox" id="showHidden" class="sort-control__checkbox"
|
|
aria-label="Show hidden files">
|
|
<span class="sort-control__label">Show hidden</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Browse mode (default): two-pane tree + preview -->
|
|
<div id="browseView" class="browse-view">
|
|
<div class="pane tree-pane" id="treePane">
|
|
<div class="tree-pane__body" id="treeBody" role="tree" aria-label="Files"></div>
|
|
</div>
|
|
<div class="pane-resizer" data-resizer-for="tree-pane" aria-hidden="true"></div>
|
|
<div class="pane preview-pane" id="previewPane">
|
|
<div class="preview-pane__header">
|
|
<span class="preview-pane__title" id="previewTitle">No file selected</span>
|
|
<span class="preview-pane__meta" id="previewMeta"></span>
|
|
<button id="previewPopout" class="btn btn-sm btn-secondary hidden" title="Pop out into a separate window" aria-label="Pop out into a separate window">⤴ Pop out</button>
|
|
</div>
|
|
<div class="preview-pane__body" id="previewBody">
|
|
<div class="preview-empty">Click a file in the tree to preview it.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grid mode: classifier-style spreadsheet rooted at the current dir -->
|
|
<div id="gridView" class="grid-view hidden">
|
|
<div class="grid-empty">
|
|
Grid view is loading…
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<div id="statusBar" class="status-bar"></div>
|
|
|
|
<!-- Help Panel -->
|
|
<aside id="help-panel" class="help-panel" hidden aria-labelledby="help-panel-title">
|
|
<div class="help-panel__header">
|
|
<h2 id="help-panel-title" class="help-panel__title">Help — ZDDC Browse</h2>
|
|
<button type="button" class="help-panel__close" id="help-panel-close" aria-label="Close">×</button>
|
|
</div>
|
|
<div class="help-panel__body">
|
|
<h3>What is Browse?</h3>
|
|
<p>Browse is the ZDDC file experience. Two top-level modes:</p>
|
|
<dl>
|
|
<dt>Browse mode</dt>
|
|
<dd>File tree on the left, preview on the right. Click a folder to
|
|
expand, click a file to preview. Markdown files open in a full editor;
|
|
PDF, image, ZIP, XLSX, DOCX, TIFF all render inline.</dd>
|
|
<dt>Grid mode</dt>
|
|
<dd>Spreadsheet view of the current subtree's files for bulk
|
|
ZDDC renaming. Edit cells directly, copy/paste with Excel,
|
|
save back to disk.</dd>
|
|
</dl>
|
|
|
|
<h3>Tree navigation (Browse mode)</h3>
|
|
<dl>
|
|
<dt>Click a folder</dt>
|
|
<dd>Expand or collapse it inline.</dd>
|
|
<dt>Shift-click a folder</dt>
|
|
<dd>Recursive expand or collapse — the whole subtree.</dd>
|
|
<dt>Click a file</dt>
|
|
<dd>Preview it in the right pane.</dd>
|
|
<dt>⤴ Pop out</dt>
|
|
<dd>Open the current preview in a separate window — useful for a second
|
|
monitor.</dd>
|
|
<dt>ZIP files</dt>
|
|
<dd>Behave as folders — click to inspect contents inline. JSZip is
|
|
bundled, so this works offline.</dd>
|
|
<dt>⤓ Download (zip)</dt>
|
|
<dd>Downloads the directory you're currently viewing — and everything
|
|
under it that you're allowed to see — as a single <code>.zip</code>.
|
|
Navigate into a subfolder first to download just that subtree. Online,
|
|
the server streams it; locally, the browser bundles the picked folder
|
|
(a confirmation appears if it's very large).</dd>
|
|
<dt>Refresh</dt>
|
|
<dd>Re-fetches the current directory listing — works for both
|
|
local (re-enumerates the FS handle) and online (re-fetches the JSON).</dd>
|
|
</dl>
|
|
|
|
<h3>Header buttons</h3>
|
|
<dl>
|
|
<dt>Add Local Directory</dt>
|
|
<dd>Pick a folder from your computer. Works in both modes; in online
|
|
mode it's de-emphasized but still available.</dd>
|
|
<dt>⟳ Refresh</dt>
|
|
<dd>Re-load the current directory listing.</dd>
|
|
<dt>◐ Theme</dt>
|
|
<dd>Cycle auto / light / dark.</dd>
|
|
</dl>
|
|
</div>
|
|
</aside>
|
|
|
|
<script>
|
|
{{JS_PLACEHOLDER}}
|
|
</script>
|
|
</body>
|
|
</html>
|