In the By-tracking table the revision name is now a preview link for its placed
file (clicking it opens the preview; hover names the original), and the per-cell
count bubble is gone — a revision is one document, so the number was noise.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The nested tracking tree wasted huge vertical space showing the hierarchy by
indentation. Render it as a spreadsheet-style table instead: each configured
field is a column, ancestor cells span (rowspan) their descendants' rows, the
revision gets its own aligned column, and each placed file is a row. Far denser,
and the hierarchy reads left-to-right.
- classify.js: a per-workspace pattern config { trackingFields[{name,optional}],
statuses, modifiers }, default ORIG·PROJ·DISC·TYPE·SEQ + optional SUFFIX,
seeded statuses from the shared enum. Serialized with the workspace; reset
keeps it (it's a setting, not data). getConfig/setConfig/getTrackingFields.
- target-tree.js: tree → flat rows → merged-cell <table>. A revision leaf is
detected by its "(STATUS)" suffix, so SEQ "0001" and revision "A (IFR)" land
in the right columns. Drop targets, CRUD, editable ZDDC name, validation,
filter and cross-reveal all preserved on the cells.
- css: sticky header + sticky merged-cell values (the value stays pinned under
the header while you scroll a tall group, so a span never reads blank).
- The original filename moves to a hover tooltip on the editable name.
Next: the revision context-menu (status / draft ~ / +Cn toggles + stepper) and
the settings UI to edit the pattern/status/modifier lists.
Tests: merged ancestors get the right rowspan; revisions align in one column;
the date revision stays intact (49 green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an autofilter input above the source tree and above each target tree.
Typing substring-matches (ANDing space-separated terms) against the full file
path/name (and folder/node names) and reveals every match with the folder
hierarchy leading to it — non-matching branches collapse out, matching branches
auto-expand. So you can type "master deliverables list" and jump straight to it.
- Source tree (tree.js): one-pass visible-set over path+name; composes with the
Show Unassigned/Assigned/Excluded toggles; auto-expands to reveal hits.
- Target trees (target-tree.js): tracking + transmittal nodes are filter-aware
(match node names + each placed file's original/derived name); one shared
query mirrored across both tab inputs.
Tests: source-tree path reveal + tracking-tree node filter (classify.spec.js -> 36).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Classify & Copy interaction pass (replaces the single "Hide Assigned" toggle):
- Source-tree filters: three "Show Unassigned / Show Assigned / Show Excluded"
checkboxes (classify mode only) with live per-tab counts; "Hide Compliant" is
now rename-mode only. Folders with nothing visible collapse out.
- Target tree: ctrl/cmd-click a toggle to expand/collapse the whole subtree.
- Tracking drop-to-any-level: dropping on a node that isn't already a complete
leaf prompts for the remaining levels (e.g. "0001_0 (IFU)"), which are parsed
and nested under the drop target. Dropping on a finished leaf assigns directly.
- Placed-file rows: click to preview; the derived filename is now an inline
input — edit it (full "TRACKING_REV (STATUS) - Title.ext") and the item is
re-filed onto the parsed tracking path (created if needed) + title override.
New classify helpers: trackingNodeComplete, trackingPathLabel. tree.setShowFilters
replaces setHideAssigned. Tests updated/added (classify.spec.js -> 33 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the Classify & Copy add-folder work:
- Add-folder now parses each (brace-expanded) name into the nested tracking
levels it represents — split on "-", then the FINAL "_" splits the leaf
revision. "CPO-0001_0 (IFU)" → CPO / 0001 / 0 (IFU); a braced pattern nests
every expansion and shares common ancestors. New classify.parseFolderLevels
+ addTrackingPath (ensure-path with name reuse).
- Node add/edit/delete controls moved back to the RIGHT of the level name and
revealed on hover (was left + always-visible).
Tests: parseFolderLevels cases + a nested-chain/shared-ancestor test; updated
the "+ Root folder" test for the new nesting (classify.spec.js -> 31 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Classify & Copy polish — in either target tab the goal is to assign or exclude
every left-pane file until nothing remains:
- Hide Assigned checkbox (classify mode, in the folder-tree pane header):
collapses the source tree to only what's left on the ACTIVE axis — hides
files already assigned in the current tab (or excluded) and any folder whose
scanned subtree is thereby empty. Re-renders on tab switch; target-tree
exposes activeAxis().
- Node add/edit/delete controls moved to the LEFT of the level name and made
always-visible (was right-aligned + hover-only), so building/pruning the
tracking and transmittal trees is one click.
- Brace expansion in the add-folder box: "BMB-187023-{PM,EL,EM}-MOM-
{0001-0002,0005}_A (IFR)" creates all 9 folders — {a,b} alternation +
{N-M} zero-padded numeric ranges, cartesian product across groups; a
multi-create is confirmed first. New classify.expandFolderPattern().
Tests: expandFolderPattern unit cases + a Hide-Assigned DOM test
(classify.spec.js → 29 passed; classifier.spec.js → 4 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Copy button (enabled once >=1 file is fully classified) copies the mapped
files into a user-chosen output directory under their canonical names/layout
<party>/{received,issued}/<transmittal>/<filename> — reading the source, never
writing it.
- copy.js: plan() (complete, non-excluded files) → conflict scan (two sources
→ same output path are reported + skipped) → copyTo() engine on the generic
FS-Access shape (ensureDir + getFileHandle + createWritable). Per-file dedup:
identical target (sha256) is skipped; existing-but-different is left
untouched and reported; live footer progress; completion toast.
- app.js: restores the saved map on launch (keyed by source-relative path, so
it re-attaches when the same directory is re-opened) and persists the source
handle on open; Copy button wired.
- target-tree.js: enables/labels the Copy button from the done count.
- 2 copy-engine tests with mock FS handles (copy/skip/differ + conflict);
24 classify+classifier tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Each source file row shows a classification state dot (unassigned →
has-tracking/transmittal → done), and each folder shows an aggregate dot
over its subtree.
- Right-click a file or folder to Exclude/Include from the copy (folder applies
to its whole subtree) or clear an axis; excluded files are struck through and
never copied.
- Cross-tree find is bidirectional: click a placed file in the target pane to
reveal+flash it in the source tree (expanding its folders); click a source
file to switch the target pane to its placed axis and flash the node.
- Target pane now reverse-looks-up over ALL scanned files (the left tree), not
the selection-scoped grid, with placements grouped in one pass per render.
- classify.getAssignment() read-only accessor; 5 new tests (18 total green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In Classify & Copy mode the left tree now lists each folder's files as
draggable rows (with a classification state dot), and folder rows are
draggable for a group-drag of the whole subtree. Target-tree nodes are drop
zones: a tracking folder (any node) or a transmittal bin; dropping assigns the
dragged source key(s) along that axis via classify.place().
- dnd.js: drag-payload bus (keys held in a module var since dataTransfer can't
be read during dragover; carries a marker for the copy cursor).
- tree.js: createFileElement + group-drag dragstart; classify-mode file rows.
- target-tree.js: setupDropZone with dragover highlight + drop assignment
(tracking = any node, transmittal = bins only).
- app.js: source tree re-renders on classify state change.
- 2 DnD drop-handler tests (14 total green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Header gets a Rename / Classify & Copy switch. In Classify & Copy mode the
spreadsheet pane is replaced by a tabbed target pane (By tracking number /
By transmittal), while the source tree stays on the left.
- target-tree.js: renders both trees from classify state; tracking-folder
create/rename/delete (leaf folders styled as the revision); party CRUD +
per-slot inline transmittal-bin form (date + TRN/SUB + seq + optional
status/title); shows the derived filename + a validation badge for each
placed file; live header stats (done / in progress / unassigned / excluded).
- app.js setMode(): swaps panes, toggles classify mode, re-renders both trees.
- 3 UI smoke tests added to classify.spec.js (12 total green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>