Commit graph

5 commits

Author SHA1 Message Date
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>
2026-05-10 15:46:51 -05:00
d6206b03e7 feat(shared): bake xlsx + utif + jszip + docx-preview into every tool
Removes every runtime CDN load. The "ship the record player with the
record" philosophy: a downloaded .html file works offline against any
file the user can open, with no network dependency at runtime.

Newly vendored under shared/vendor/:
  - xlsx.full.min.js (SheetJS, 928 KB) — XLSX/XLS preview
  - utif.min.js     (UTIF, 57 KB)      — TIFF preview

Already there but now used by mdedit too:
  - jszip.min.js, docx-preview.min.js

Call sites updated to drop the `await loadLibrary(URL)` pattern —
since the vendor JS is concatenated into the inline <script> at build
time, window.XLSX / window.JSZip / window.UTIF / window.docx are
available synchronously from page load.

Per-tool changes:

  - archive/build.sh:        +xlsx, +utif
  - classifier/build.sh:     +xlsx, +utif
  - transmittal/build.sh:    +xlsx, +utif
  - mdedit/build.sh:         +jszip, +docx-preview, +xlsx, +utif
                              (mdedit was the only tool not yet
                               bundling any of the preview deps)
  - browse/build.sh:         +utif
  - archive/js/table.js, classifier/js/preview.js,
    transmittal/js/files-preview.js, mdedit/js/file-tree.js (×2):
    drop the `await loadLibrary('…cdn…')` lines.
  - shared/preview-lib.js:
    drop the loadLibrary(UTIF) / loadLibrary(JSZip) wrappers; assume
    window.UTIF and window.JSZip are present.

Net bundle-size delta after baking:
  archive:     +990 KB → ~1.47 MB
  browse:       +57 KB → ~292 KB
  classifier:  +990 KB → ~1.43 MB
  mdedit:    +1100 KB → ~2.09 MB
  transmittal: +990 KB → ~1.63 MB

Docs (AGENTS.md, ARCHITECTURE.md) updated: removed the "runtime CDN
loading exception" paragraph and the table row that flagged xlsx as
CDN-loaded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:09:38 -05:00
9ca36f25d8 feat(tables): new sortable/filterable grid tool for directories of YAML files
Tables is the eighth HTML tool: a read-only tabular view over a
directory of YAML files declared via `tables:` in `.zddc`. Anchor use
case is the Master Deliverables List, where each row is one
`<tracking>.yaml` under `Archive/<Party>/MDL/`. Rows click through to
the existing form renderer for editing.

Schema (zddc/internal/zddc/file.go)
  - New `Tables map[string]string` on ZddcFile. Map key becomes the URL
    stem (`tables[MDL]` → `<dir>/MDL.table.html`); the value is a path
    relative to the .zddc pointing at a `*.table.yaml` spec describing
    columns + the rows directory. No upward cascade in v1 — each
    directory hosting a table declares it directly.

Server handler (zddc/internal/handler/tablehandler.go)
  - `RecognizeTableRequest` matches GET `/<dir>/<name>.table.html`
    against the cascade's `tables:` declarations. Dispatch routes
    table requests before the form-system intercept.
  - `ServeTable` ACL-gates with `policy.ActionRead` and serves the
    embedded `tables.html` template; client walks the directory itself
    via the listing JSON or FS Access API.
  - tables.html embedded via //go:embed — same pattern as form.html.

Frontend (tables/)
  - Vanilla JS: app/context/util/filters/sort/render/main modules.
  - Reads spec + row YAML files via window.zddc.source (HTTP polyfill
    or local FS handle); js-yaml 4.1.0 vendored in shared/vendor for
    client-side parsing.
  - Sample fixtures under tables/sample/ for local testing.

Build + CI
  - Lockstep build registers tables alongside the other 7 tools (HTML
    output, embed mirror, versions.txt, release-output, tags).
  - Playwright project added; `npx playwright test --project=tables`
    is part of `npm test`.

Drive-by: rename mdedit Playwright selectors `#select-directory` →
`#addDirectoryBtn` to fix three pre-existing failing tests.

Drive-by: ignore locally-built `zddc/zddc-server` binary so it doesn't
get accidentally staged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 20:32:01 -05:00
9481122570 perf(tools): vendor jszip + docx-preview for archive/transmittal/classifier
Same pattern as the browse fix. archive, transmittal, classifier
previously CDN-loaded jszip + docx-preview on first preview of a
.zip / .docx file via shared/preview-lib.js's loadLibrary helper.
That meant each first-preview blocked on a CDN round-trip + parse,
and broke entirely under restrictive networks or CSPs.

Vendor both libs under shared/vendor/ and concat them at the top of
each tool's build, ahead of init.js. window.JSZip + window.docx are
now defined immediately on page load. Drop the redundant loadLibrary
calls (and classifier's stray <script src="cdn..."> tag in the
template, plus archive's bespoke loadJSZip helper in export.js).

xlsx (SheetJS) intentionally stays CDN-loaded — at ~900 KB it's too
large to inline, and only fires on .xlsx preview which is a rarer
path.

Bundle size impact (uncompressed):
  archive:     304 KB → 476 KB  (+172 KB)
  transmittal: 449 KB → 621 KB  (+172 KB)
  classifier:  252 KB → 424 KB  (+172 KB)

With the gzip middleware (~75% reduction on HTML) and ETag-cached
revalidation now in place, the wire-size delta is ~40 KB per tool
on the first load and 0 on every subsequent load until redeploy.
2026-05-04 07:49:17 -05:00
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.
2026-05-03 21:35:15 -05:00