diff --git a/browse/js/events.js b/browse/js/events.js index 0942fc6..90bf600 100644 --- a/browse/js/events.js +++ b/browse/js/events.js @@ -1121,11 +1121,12 @@ // View mode is URL-driven, not UI-driven. // - // ?view=grid → grid mode (only honored where classifier is - // available; otherwise falls back to browse) - // ?view=browse → browse mode (always) - // default → path-based: grid when inside an incoming/ - // subtree, browse everywhere else + // ?view=grid → embedded-tool view (only honored where the cascade's + // default_tool is an embeddable full-page tool — + // classifier/transmittal/archive; else falls back to browse) + // ?view=browse → browse listing (always) + // default → embedded-tool view when the dir's default_tool is one + // of those tools, browse listing everywhere else // // resolveViewMode reads the current location and returns the mode // to render; applyResolvedViewMode toggles the panes accordingly. @@ -1134,10 +1135,10 @@ var qs = new URLSearchParams(window.location.search); var explicit = (qs.get('view') || '').toLowerCase(); var grid = window.app.modules.grid; - var classifierHere = !!(grid && grid.availableHere && grid.availableHere()); - if (explicit === 'grid') return classifierHere ? 'grid' : 'browse'; + var toolHere = !!(grid && grid.availableHere && grid.availableHere()); + if (explicit === 'grid') return toolHere ? 'grid' : 'browse'; if (explicit === 'browse') return 'browse'; - return classifierHere ? 'grid' : 'browse'; + return toolHere ? 'grid' : 'browse'; } function applyResolvedViewMode() { diff --git a/browse/js/grid.js b/browse/js/grid.js index ce53feb..7b720d7 100644 --- a/browse/js/grid.js +++ b/browse/js/grid.js @@ -1,48 +1,53 @@ -// grid.js — "Grid mode" plugin for browse. Loads the classifier tool -// as an iframe scoped to the current directory so users get classifier's -// full bulk-rename workflow without leaving browse. +// grid.js — in-pane tool embed for browse (the browse-as-shell bridge; see +// ARCHITECTURE.md's ADR). Loads a heavy, full-page tool as an iframe scoped to +// the current directory so the user gets that tool's full workflow without +// leaving the browse shell. browse stays the top-level app; the cascade's +// default_tool decides which tool embeds here. // -// Availability: the cascade decides. Grid auto-activates wherever the -// .zddc cascade resolves default_tool=classifier (defaults.zddc.yaml -// declares this for archive//incoming/). Operators can extend -// — e.g. setting default_tool=classifier on a custom dir activates -// grid mode there too — without touching this code. -// -// Iframe src resolution: /classifier.html. Iframe -// embedding only works in server mode; file:// pages don't get the -// Grid toggle. +// Availability: the cascade decides — `state.scopeDefaultTool` (the +// X-ZDDC-Default-Tool header) must name one of the EMBEDDABLE full-page tools: +// classifier (archive//incoming/), transmittal (…/staging/), archive +// (the archive index). tables/forms embed in the preview pane instead +// (table-leaf / form view); landing/browse don't self-embed. Operators extend +// by setting default_tool on a dir — no code change. Iframe src: +// /.html. Server mode only (file:// has no server). (function () { 'use strict'; var state = window.app.state; var mounted = false; - function classifierAvailableHere() { - // state.scopeDefaultTool is set by the loader from the - // X-ZDDC-Default-Tool response header on every listing fetch. - // Grid mode is meaningful exactly where the cascade picks - // classifier as the default — no client-side path matching. - return state.scopeDefaultTool === 'classifier'; + // Full-page tools that embed in the gridView pane when they're the dir's + // default_tool. (tables/form embed in the preview pane; landing/browse are + // not in-pane embeds.) + var EMBEDDABLE = { classifier: 1, transmittal: 1, archive: 1 }; + + // The cascade-resolved default tool for the current dir when it's an + // embeddable full-page tool; "" otherwise. + function embedToolHere() { + var t = state.scopeDefaultTool; + return (t && EMBEDDABLE[t]) ? t : ''; } function activate() { var host = document.getElementById('gridView'); if (!host) return; if (mounted) return; - if (state.source !== 'server' || !classifierAvailableHere()) return; + var tool = embedToolHere(); + if (state.source !== 'server' || !tool) return; - // Compute the iframe src: current page's directory + classifier.html. + // Compute the iframe src: current page's directory + .html. var pathname = window.location.pathname || '/'; if (!pathname.endsWith('/')) { var lastSlash = pathname.lastIndexOf('/'); pathname = lastSlash >= 0 ? pathname.substring(0, lastSlash + 1) : '/'; } - var src = pathname + 'classifier.html'; + var src = pathname + tool + '.html'; host.innerHTML = ''; var frame = document.createElement('iframe'); frame.src = src; - frame.title = 'ZDDC Classifier (Grid mode)'; + frame.title = 'ZDDC ' + tool; frame.style.cssText = 'width:100%;height:100%;border:0;display:block;' + 'background:var(--bg);'; host.appendChild(frame); @@ -61,9 +66,12 @@ window.app.modules.grid = { activate: activate, reset: reset, - // Hook for events.js to show/hide the Grid toggle button. + // Hook for events.js's view-mode resolution: is an embeddable tool the + // default here? availableHere: function () { - return state.source === 'server' && classifierAvailableHere(); - } + return state.source === 'server' && !!embedToolHere(); + }, + // The embeddable tool name (or "") — lets the shell label the view. + toolHere: embedToolHere }; })();