17 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 94b2e29448 |
feat(browse): SPA overhaul — context menu, YAML editor, icons, hovercard, deep links, autofilter
Major upgrade to the browse tool's UX, plus a few shared modules other tools can adopt. User-facing: - Right-click context menu on tree rows AND empty pane space. Traditional file-manager grouping (Open / Download / New / Rename-Delete / Copy / Tree ops / View). Items stay visible but disabled when not applicable so muscle memory carries. Generic shared/context-menu.js framework supports normal items, toggles, submenus, separators, danger styling. - YAML editor for .yaml / .yml / .zddc files (CodeMirror 5 vendored at shared/vendor/codemirror-yaml.min.*). js-yaml lint on every change for parse errors. For .zddc cascade files, an additional schema-aware lint pass flags unknown keys, bad enum values, and wrong types. - Per-row drag-drop upload using webkitGetAsEntry (folder uploads work recursively). Per-row drop indicator; doc-level overlay still fires for blank-space drops at drop_target scopes. - New folder / New markdown file context-menu items (server mode). Rename + Delete with native confirm() dialog. File-API helpers removeNode / renameNode use the existing PUT/POST/DELETE endpoints. - Hover info card with the row's full metadata (ZDDC fields + filesystem info + path/URL). Interactive — mouse into it, drag-select text, Ctrl/Cmd-C or right-click → Copy. 200ms grace before dismiss. - Autofilter input at the top of the tree pane. Same grammar as archive's column filters (zddc.filter.parse / matches). Filters files; folders without matches collapse out. Non-matching folders force-open visually when descendants match, without mutating the user's actual expand state. - Two-line ZDDC label: title-first, tracking/rev/status as monospace meta below. Icon column anchors to the title line. Chevron is a Lucide outline `chevron-right` SVG, rotated 90° on `.expanded`. - File-type Lucide icon sprite (shared/icons.js — 16 outline glyphs, ~5 KB). PDF / Word / Spreadsheet / Slides / Image / Video / Audio / CAD / Web / Config / Code / Archive get distinct icons; folders tinted with --primary. - Header wraps gracefully at narrow viewports (shared/base.css flex-wrap + title min-width:0 ellipsis). Body becomes flex column in browse so a wrapping header doesn't break #appMain height. - Markdown editor opens in WYSIWYG mode by default. YAML front-matter + TOC sidebar reworked: flexbox layout (single visible resizer between FM and TOC), both bodies overflow:auto for X+Y scrollbars. - `?file=<path>` deep links open browse pre-positioned at a specific file. Multi-segment paths walk into subdirectories on the way. Auto-flips Show hidden when a segment is dot/underscore-prefixed. - Refresh + show-hidden toggle preserve expansion / selection / preview pinning. Path-keyed snapshot survives a re-fetched listing. - "Add Local Directory" → "Use Local Directory" across the four tools that have it (browse, archive, classifier, +transmittal comment). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 72c0552750 |
feat(browse): "Show hidden" toggle — list .-prefixed and _-prefixed entries
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.
|
|||
| b34edcecac |
feat(browse): markdown editor — editable YAML front matter + DOCX/HTML/PDF download buttons
Two improvements to browse's preview-markdown plugin so it can replace the standalone mdedit tool: 1. **YAML front-matter editing.** The FM pane above the outline used to render a read-only <dl> of parsed keys — sparse and unusable when the file had no envelope yet. It's now a dedicated <textarea> that's always present. On load, parseFrontMatter() splits the `---\n…\n---` envelope off the body: the body feeds Toast UI Editor, the envelope feeds the textarea. On save, assembleContent() recombines them. Dirty tracking covers both halves via a SHA-256 of the assembled bytes. The shell mirrors mdedit's old layout (FM textarea top, outline below) but the FM pane is now always functional, eliminating the "empty pane over the TOC" problem. 2. **Download as DOCX / HTML / PDF.** When the file handle is HTTP- backed (server mode) and the file is a .md, three buttons appear in the info header next to Save. Clicking one fetches the server's ?convert=<fmt> endpoint and triggers a browser download with a clean filename (foo.md → foo.docx). Auto-saves the buffer first if dirty so the converted bytes reflect what's on screen. Helper at window.zddc.source.downloadConverted (shared/zddc-source.js) so other tools — archive, transmittal — can reuse the same flow later. Friendly error messages map HTTP 503 / 422 / 504 to actionable toasts. |
|||
| 4af0d8ca7c |
feat(browse): drag-drop upload into working/staging/incoming
Drop files anywhere on a browse page; if the current scope is inside
a working/, staging/, or incoming/ subtree the files are PUT to the
current directory via the existing file API. Per-file ACL is enforced
server-side (authorizeAction); a 403 surfaces as a per-file error
toast and the rest of the batch proceeds.
UX:
- dragenter → semi-transparent overlay with a dashed-border panel
showing the destination path. Hides immediately on dragleave or
drop.
- drop → "Uploading N files…" toast, then per-file failure toasts
inline, then a summary toast (success / partial / all-failed).
- listing auto-refreshes after the batch so new files appear in
the tree without a manual reload.
Scope:
- upload-eligible paths are matched by /\/(working|staging|incoming)
(\/|$)/i — same convention as the new grid-mode URL token.
- 256 MiB per-file cap (UPLOAD_MAX_BYTES) since browse's single-
body PUT loads the file as a Blob in the tab; larger uploads
should use a dedicated client.
- Outside the upload-eligible set the overlay never appears; drops
are silently ignored (drag effect = none).
Sequential uploads keep progress predictable; parallel batching can
land later if needed. The module hooks document-level dragenter/leave
/over/drop so it works regardless of which pane the user drags over.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| d052e9fed3 |
Round of UX fixes: tool strip removed, MDL routing, browse markdown layout, reviewing depth-2
Four user-reported items:
1. landing: remove the standalone-tool strip from the site picker.
Per user, it was awkward — links pointing at zddc.varasys.io
releases from inside a deployment is a layering confusion. The
nav.tool-strip block in landing/template.html and its CSS are
gone.
2. zddc-server: route /Project/archive/<party>/mdl[/] to the tables
app for the virtual-MDL case where the on-disk folder doesn't
exist yet. Previously fell through to 404 because the dispatcher
only routed virtual mdl/ via the IsDir branch — the IsNotExist
branch was missing the equivalent check. Now both shapes (with
and without trailing slash) hit RecognizeTableRequest's default-
MDL fallback and ServeTable serves the embedded tables.html.
3. browse: re-layout the markdown editor to mirror mdedit's layout.
Was: sidebar on right with TOC top + front-matter bottom.
Now: sidebar on LEFT with YAML front matter top + Outline bottom,
content on RIGHT with an informational header (file title +
save controls + status + source) above the Toast UI editor.
New horizontal resizer between the front-matter and outline
sections inside the sidebar (drag the row boundary; arrow keys
step by 24 px). Browse test selectors updated.
4. zddc-server reviewing aggregator: extend to depth ≥ 2 so the
user can preview files inside virtual reviewing/<tracking>/
received/ and staged/ folders. IsReviewingPath now returns a
sidePath ("received[/rest]" or "staged[/rest]"); ServeReviewing's
depth-2 branch proxies the underlying real folder's listing,
emitting folder entries with virtual reviewing/ URLs (so
navigation stays in the aggregator) and file entries with
canonical archive/ or staging/ URLs (so byte fetches resolve
directly). ACL is enforced against the real path; depth-1
received/ + staged/ URLs are now virtual too (was canonical),
so the user smoothly descends into the depth-2 listing.
Tests updated for the new IsReviewingPath signature and the depth-1
URL shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| cb2cf1ebe3 |
fix(browse): re-implement markdown editor layout on CSS Grid
The previous nested-flexbox layout produced indeterminate heights
inside the Toast UI editor host and made the TOC pane width fragile —
visually the editor and outline weren't laying out reliably. This
swaps the whole shell to CSS Grid, which gives every cell a definite
size.
Layout:
┌──────────────────────────────────────────────────────────────┐
│ toolbar (Save | ● modified | status | source) │
├─────────────────────────────────────┬────────────────────────┤
│ │ Outline │
│ Toast UI Editor │ • Heading 1 │
│ (md / wysiwyg / preview) │ • Subheading │
│ ├────────────────────────┤
│ │ Front matter │
│ │ title: … rev: … │
└─────────────────────────────────────┴────────────────────────┘
Notes:
- The shell mounts as a single child of #previewBody (not by
re-classing previewBody itself), so the outer flex layout that
fills the preview pane is preserved.
- Sidebar is its own grid (outline 1fr + front-matter auto/max 40%),
each section independently scrollable.
- Resizer is a 6 px element on the grid column boundary; drag
updates grid-template-columns. Keyboard left/right adjust by 24 px.
Width persists across mounts (lastTocWidth) within a session.
- parseHeadings now skips front-matter envelope + fenced code so a
"##" inside ```bash``` doesn't show up as an outline entry.
- scrollEditorToHeading uses findScrollParent + scrollTo({behavior:
'smooth'}) so jumps feel less jarring.
- Class names follow BEM: .md-shell__*, .md-side__*, .md-toc__*,
.md-fm__*. Tests updated to the new selectors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 319a3c0ce7 |
fix(browse): dblclick navigates; show virtual canonical folders at project root
#5 — Double-click on a folder no longer toggles collapse.
Root cause: the single-click handler called tree.render() immediately,
which replaced the clicked row element. The browser's double-click
detection requires the second click to land on the SAME target as the
first, so dblclick never fired for folders.
Fix: defer the single-click toggle by 220ms. A pending dblclick within
the window cancels the toggle and runs navigateIntoFolder instead.
Modifier-clicks (shift/alt for recursive) and ZIP expands skip the
deferral — they're never followed by a dblclick navigation.
#3 — Browse at /<project>/ now always shows the four canonical
folders (archive, working, staging, reviewing) even when they don't
yet exist on disk. Each missing folder is synthesized client-side as
a "virtual" row: muted icon + label + "(empty)" hint, double-clickable
to navigate. zddc-server already serves an empty listing for these
paths (commit
|
|||
| 7904a99c21 |
feat(browse): markdown front-matter pane + TOC resizer; misc UX fixes
Markdown preview pane now surfaces YAML front-matter above the TOC as a key/value list (definition list), so engineering documents with header metadata (title, revision, status, etc.) show their identity at a glance without opening the file in mdedit. Front-matter parsing handles both scalar and array values; arrays render as comma-joined. TOC pane is now resizable (4px col-resize handle on its left edge); preserves the user's chosen width across re-renders inside a single session. mdedit welcome banner moved inside #welcome-screen so the "browse opens md in this same editor" callout only shows when no file is open — it was previously visible in every state which was noisy. archive.spec.js: wait for #filePreviewToggle to be attached before clicking, fixing a Playwright flake where the preview button hadn't mounted yet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0b69367901 |
feat(browse): sort dropdown in the tree toolbar
The tree's underlying setSort API was carried forward from the old
table-with-clickable-headers UI but had no widget driving it after
the layout reshape. Adds an explicit dropdown in the toolbar:
Sort: [Name (A→Z) ▾]
[Name (Z→A) ]
[Modified (new→old) ]
[Modified (old→new) ]
[Size (large→small) ]
[Size (small→large) ]
[Type (A→Z) ]
Implementation:
- new tree.setSortExplicit(key, dir) — sets both axes in one call
(the existing tree.setSort toggles direction on repeat-clicks,
which is the right semantics for column-header clicks but wrong
for an explicit dropdown).
- events.js parses the dropdown value as "<key>:<asc|desc>" and
calls setSortExplicit. The dropdown is initialised to reflect
the current sort state on mount.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 0b382716e3 |
feat(browse): TOC pane + FS-API saves in the markdown plugin
Completes the markdown plugin's deferred v2 items:
1. TOC pane
A third pane to the right of the Toast UI editor lists every heading
in the current document, hierarchically indented by level. Click an
item → editor scrolls to that heading (markdown-mode uses
setSelection + preview scroll; WYSIWYG mode uses DOM text matching;
the target heading flashes briefly via primary-light background).
The TOC re-renders on every editor change (debounced 250ms) so it
stays in sync with edits.
Heading parser supports ATX-style `^#{1,6}\s+` lines, strips inline
markdown emphasis/code/links/strike from the displayed label.
Empty file → "Empty file." Headingless file → "No headings."
2. FS-API writes
Saves now route to whichever source the file came from:
- node.handle + createWritable available → FileSystemWritableFileStream
(local folder picker). The user's chosen file gets overwritten
via the browser's File System Access API.
- node.url + server source → PUT to the server URL (as before).
- zip-virtual file → save disabled (no writable stream from JSZip).
- Anything else → save disabled with a tooltip.
Save status surfaces via the existing toolbar (`Saved 10:42:18`) AND
a shared toast notification ("Saved readme.md" / "Save failed: …")
so the success/failure is visible regardless of whether the user is
looking at the toolbar.
Source-hint chip on the toolbar shows "local" / "server" /
"read-only (inside zip)" so the user knows which write path is
active before they make changes.
CSS additions in browse/css/tree.css for .md-toolbar, .md-split,
.md-editor-host, .md-toc-pane, .toc-list, and the .toc-level-1..6
indentation rules.
A new Playwright test exercises the markdown plugin end-to-end:
mounts the editor on a .md click, asserts the three DOM regions are
visible, verifies the TOC contains the three expected headings from
the test fixture's markdown content, and confirms the source hint
reads "local" for FS-API mode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 7d4d2dc9a2 |
feat(browse): two-pane shell + markdown plugin + grid mode (Phases A/B/C/D)
Reshape browse from "tree-as-table with popup preview" into a unified
file-experience tool with three layered behaviors:
Phase A — Two-pane shell
Phase B — Markdown plugin (Toast UI inline)
Phase C — Grid mode (classifier workflow)
Phase D — Deprecation banners on standalone classifier + mdedit
= Phase A: two-pane shell + lightweight preview plugins =
Browse's table view becomes a tree-pane on the left + preview-pane on
the right with a draggable resizer. Click a folder → expand inline.
Click a file → render in the right pane. The previous popup window
becomes an explicit "⤴ Pop out" button in the right-pane header for
users with a second monitor.
Preview rendering reuses shared/preview-lib.js (PDF iframe, image
<img>, TIFF, ZIP listing, text <pre>). Unknown types show a download
link. browse/js/preview.js refactored into renderInline (default) +
renderInPopup (Pop out button); both share the same plugin
dispatch logic.
Filter rows were already removed earlier this session. Sort columns
likewise — the tree is alphabetical by default; the underlying
setSort API still exists for future re-introduction.
= Phase B: markdown plugin =
New browse/js/preview-markdown.js: when a .md or .markdown file is
clicked, the right pane mounts a Toast UI editor (initial-value =
file contents) with a small toolbar containing Save + dirty indicator
+ status text. Save sends PUT through the file API for server-mode
files; non-server sources are read-only for now (deferred to a
follow-up that wires zddc-source.js writes too). Ctrl+S / Cmd+S
inside the editor saves.
Toast UI Editor (~700 KB JS + ~160 KB CSS) was previously bundled
only in mdedit/vendor/. Moved to shared/vendor/ so browse and mdedit
both pull from one location.
= Phase C: grid mode =
View-mode toggle [Browse | Grid] in the toolbar. Grid mode loads the
classifier tool as an iframe scoped to the current directory (server
mode at working/staging/incoming locations) — classifier's full
bulk-rename workflow without leaving browse. v1 implementation; a
future iteration could bundle classifier's modules directly into
browse for tighter integration. Hostile cases (file:// origin, paths
outside working/staging/incoming) show a friendly explanation
instead of a blank iframe.
new browse/js/grid.js handles the activation logic.
= Phase D: deprecation banners =
mdedit and classifier standalones gain a "this tool is being absorbed
into Browse" advisory banner. Both standalones remain fully
functional and continue to ship — they're useful for offline single-
file editing and air-gapped environments. The banner just points
users toward the unified browse experience.
= Files =
+ browse/js/preview-markdown.js (markdown plugin)
+ browse/js/grid.js (grid-mode plugin)
M browse/template.html (two-pane layout, view toggle, banners)
M browse/css/tree.css (two-pane CSS, replaces table styles)
M browse/js/init.js (state additions: selectedId, viewMode)
M browse/js/tree.js (rowHtml: <tr>+<td> → <div>)
M browse/js/preview.js (renderInline / renderInPopup split)
M browse/js/events.js (toggle wiring, resizer, click handlers
adapted from <table> to <div>)
M browse/build.sh (Toast UI vendor + new modules)
R mdedit/vendor/toastui-* → shared/vendor/ (one bundle, two tools)
M mdedit/build.sh (paths)
M mdedit/template.html (deprecation banner)
M classifier/template.html (deprecation banner)
M tests/browse.spec.js (selectors updated for new layout +
new "click file → preview" test)
Bundle sizes after this commit:
browse: ~1020 KB (was ~290 KB; added Toast UI ~700 KB)
classifier: ~1470 KB (unchanged from prior baseline)
mdedit: ~2140 KB (unchanged; vendor location moved but not added)
What's deferred:
- TOC + front-matter pane in browse's markdown plugin (mdedit has
these; browse v1 uses just the editor).
- FS-API writes from browse's markdown plugin (server PUT works).
- Classifier modules bundled directly into browse (v1 uses iframe).
- Sort UI in the new tree (model still supports it; no widget yet).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 53eb58b90b |
refactor(browse): remove auto-filter rows from header
The two filter rows (📄 file/ext + 📁 folder) didn't really earn their header real estate. Browse is for navigating directory structure; ad-hoc filters across a tree of mixed file types and depths weren't the right affordance, and the visual weight competed with the column headers and the breadcrumb. Removed entirely: - template.html: dropped both <tr class="filter-row"> rows in <thead>, the related "Filter rows" help section, and the empty-state copy that mentioned the 📄/📁 rows. - init.js: dropped state.filters (file/folder/ext slots). - events.js: dropped the .column-filter[data-filter] input wiring. - tree.js: dropped recomputeVisibility() and the n.visible plumbing in visibleIds() and updateCount(). Render is now a straight depth- first walk over expanded subtrees; the count is just total rows. setFilter is removed from the public API. - css/tree.css: dropped .filter-row*, .filter-row__icon, and the browse-local .column-filter rules (.column-filter is also defined in shared/base.css for tools that still use it; that stays). No test changes — tests/browse.spec.js never exercised filters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 22c142e45a |
chore(headers): standardize across all 7 tools
Bring every tool's header in line with archive's pattern: [logo] [title] [version] [Add Local Directory] [⟳] ............... [◐] [?] ------------- header-left --------------- ----- header-right - Changes per tool: * browse: rename "Select Directory" → "Add Local Directory"; add the red-non-stable wrap to the build label (was missing); add a help panel + bundle shared/help.js. * classifier: rename selectDirectoryBtn → addDirectoryBtn, refreshBtn → refreshHeaderBtn for consistency. Update all JS callers and welcome-screen copy to the new label. * mdedit: same id rename. Move the previously-in-pane refresh button into the header. Stop renaming the dir button to "Directory: <name>" once a folder is loaded — instead use the shared btn--subtle variant to de-emphasize while keeping the standard label. * transmittal: convert non-standard <div class="app-header"> with spacer/icons containers to <header class="app-header"> with the canonical header-left/header-right pair. Move the publish split- button into header-left (Transmittal-specific primary action). Remove dead .app-header__spacer/__icons/header-icon-btn CSS now that nothing references those classes. * landing, form: add help-btn + help-panel + bundle shared/help.js. Each panel is tool-specific (project picker docs for landing, schema-driven form docs for form). Cross-cutting: * shared/base.css: promote .btn--subtle from browse/css/tree.css so any tool with an online mode can de-emphasize Add Local Directory consistently. Verified all 7 tools in headless Chromium: header structure correct, build label red on non-stable cuts, help panel opens + closes via button + Esc. |
|||
| 582db6d86d |
feat(browse): vendored JSZip, SVG home icon, auto-filter rows
- Vendor JSZip locally (shared/vendor/jszip.min.js) and bundle into
the browse build instead of CDN-loading. Eliminates the failure
mode where ZIP rows can't expand because the CDN script doesn't
load (CSP, network, etc.). Tool now works fully offline.
- Replace the toolbar filter input + ext multi-select with two
spreadsheet-style auto-filter rows in <thead>:
- 📄 row: file-name filter + extension filter
- 📁 row: folder-name filter
Each input uses shared/zddc-filter syntax (substring/!negate/
^startsWith/$endsWith/regex/| or/space and).
- New visibility model with ancestor-of-match awareness:
- file matches keep their ancestor folders visible (path-to-hit)
- folder match keeps its descendants visible
- filters compose (file ∧ folder ∧ ext) so combinations narrow
Computed model-side; render walks only visible nodes.
- Replace 🏠 emoji breadcrumb-root with an inline outline-stroke SVG
that tints with currentColor.
|
|||
| 424bf8e769 |
feat(browse): Phase 2 — preview popup, ZIP expansion, ext filter, breadcrumbs
Bundles Phase 2 polish + the user-requested header/breadcrumb work:
- Breadcrumbs replacing the plain currentPath span. Server mode
renders linkified ancestor segments (each <a> navigates to that
directory; the browser fetches browse.html, the new instance
auto-loads the listing). FS-API mode renders the rootHandle name
as a non-link (no ancestor handles to navigate). Both prefix the
path with a 🏠 root icon. Trailing slash + bold-current segment
match common file-explorer conventions.
- Subdued 'Select Directory' button in server mode. Once browse is
serving a real directory listing, the local-folder switcher is
available but visually quiet (btn--subtle: transparent, muted
color). FS-API mode keeps the primary styling (it's how the user
got there). New btn--subtle CSS class added to browse's tree.css.
A refresh button (⟳) appears next to it in both modes; clicking
it re-fetches the current root listing.
- Header consistency: browse now matches archive's header layout
(refresh + help buttons in addition to theme on the right). Help
is a placeholder for future help dialog wiring.
- File preview popup. Click a file row → opens a popup window with
the file rendered. Plain types (PDF, HTML, image) load in
iframes; TIFF + ZIP listings via shared/preview-lib.js's
renderTiff / renderZipListing helpers; text via <pre>; unknown
types → 'click Download' placeholder. Modifier-click (ctrl/cmd/
shift) and middle-click still open the file in a new tab via the
underlying <a target=_blank>. Single popup window is reused
across multiple file clicks (matches archive's UX).
- ZIP inline expansion. .zip files have a chevron and act like
folders in the tree. First expand fetches the zip bytes
(server URL or FS handle or parent-zip read), parses with JSZip
(auto-loaded from CDN), and synthesizes the entry tree. Nested
directories within the zip lazy-expand on demand by re-walking
the cached entry list at the right path prefix. Click on a
zip-entry file opens the preview popup with bytes read from
JSZip. Recursive expand-all skips zip archives by design — they
can be very large, and explicit click-to-expand is safer.
- Extension multi-select filter. Toolbar now has a <select
multiple> populated with extensions present in the current
view. Filter is OR-of-selected; combined with the name filter
it's AND-of-both. Folders pass through (so expanding a folder
whose name doesn't match the ext filter still shows its file
children that do match).
|
|||
| 7caf3ecf3f |
fix(browse): listing fetch + row height + recursive expand/collapse
Three issues from initial v0.0.12 dev/prod testing:
1. Online listings empty.
directory.go was missing Vary: Accept on its responses, so
browser/CDN cached the HTML response (the embedded browse.html)
and served it again when browse's JS later fetched the same URL
with Accept: application/json. JSON parse failed, autoDetect
returned null, empty state showed. Adds Vary: Accept on both
branches and changes browse.html cache-control to no-cache so
deployed updates land immediately.
2. Top-level folder rows tall, shrink as subtree expands.
The .browse-table had flex:1 in a flex column. <table> in flex
doesn't reliably distribute height across rows — with few rows,
each row stretched. Wrap the table in a div with overflow:auto
and drop flex:1 from the table itself.
3. Recursive expand/collapse.
Shift-click (or alt-click) on a folder now expand-all or
collapse-all its subtree. Plain click still toggles just that
folder. Implementation: tree.expandSubtree() walks BFS, loading
each level's children in parallel, re-rendering between levels
so the user sees progress. tree.collapseSubtree() recursively
marks the subtree collapsed (children stay loaded for instant
re-expand).
|
|||
| fb13ff4fd8 |
feat(browse): generic directory listing tool — default at folder URLs
All checks were successful
Notify chart dev on beta cut / notify-chart-dev (push) Successful in 5s
A new HTML tool — browse — that lists the contents of any directory.
Designed for ZDDC archives but no ZDDC-specific filtering; just a
straight folder browser with expand/collapse, sort, and name filter.
Modes (auto-detected at page load):
- Online: when served by zddc-server at a folder URL, queries
the same URL with Accept: application/json to load the listing
and renders it. Auto-served as the default at any directory
under ZDDC_ROOT without an index.html (replacing the previous
minimal-HTML stub from directory.go).
- Local: 'Select Directory' button uses FileSystemAccessAPI to
pick any folder on disk; works in Chromium-based browsers.
Features (Phase 1 — what's in this commit):
- Tree view with lazy-loaded folders (children fetched on first
expand).
- Sort by name / size / extension / date (column header click).
- Filter by name substring (toolbar input).
- File click opens in a new tab — for server-backed pages,
routes through zddc-server's normal handler so .archive
redirects + apps cascade overrides + ACL all apply.
Phase 2 deferred:
- ZIP files inline expansion (treat archive entries as virtual
children).
- File preview popup (reuse shared/preview-lib.js).
- Extension multi-select filter.
Wiring:
- browse/ added to top-level ./build's per-tool list, embed
block, versions.txt, and the lockstep release commit + tag set.
All seven tools (archive, transmittal, classifier, mdedit,
landing, form, browse) advance together on stable cuts.
- shared/build-lib.sh: browse added to ZDDC_RELEASE_TOOLS and
verify_channel_links's per-tool loop.
- zddc/internal/apps/embed.go: //go:embed browse.html +
EmbeddedBytes("browse") case.
- zddc/internal/apps/availability.go: browse available at every
directory (same as archive).
- zddc/internal/apps/handler.go: MatchAppHTML routes
/<dir>/browse.html → 'browse'.
- zddc/internal/handler/directory.go: when a directory request
arrives with Accept: text/html and no index.html exists,
serve the embedded browse.html bytes (with a JSON-fallback
if the embedded slot is empty during bootstrap).
|