fix(tables): handle bare-directory URLs served as default_tool

Visiting `/Project-1/archive/PartyA/mdl` (no trailing slash) errored with
`Unrecognized table URL` because tableNameFromUrl only matched
`…/<rowsdir>/table.html`. The cascade declares `default_tool: tables` at
`archive/<party>/mdl`, so the server serves the tables HTML at the bare
directory URL — a shape the client didn't recognize.

Two coordinated fixes:

- shared/zddc-source.js `pathToDir`: was over-eagerly stripping the last
  segment when the URL didn't end in `/`. Now checks whether the last
  segment contains a dot — file URLs strip to parent (original behavior
  preserved), bare-directory URLs append the missing slash. Only call
  site is detectServerRoot, so blast radius is contained.
- tables/js/context.js `tableNameFromUrl` + `rowEditUrl`: accept both
  legacy `…/<rowsdir>/table.html` and the new bare-directory shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-14 12:12:06 -05:00
parent a62960b712
commit e5ba2b6168
2 changed files with 43 additions and 11 deletions

View file

@ -289,13 +289,33 @@
// Top-level helpers // Top-level helpers
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Strip a trailing tool .html (e.g. classifier.html) from a path // Resolve "the directory the tool was opened in" for the current
// to land on the "directory the tool was opened in". // page URL. Two URL shapes serve a tool:
//
// /…/<tool>.html — file URL; strip the trailing filename.
// /…/<dir>/ — trailing-slash directory URL; keep it.
// /…/<dir> — bare-directory URL served by the
// cascade's `default_tool` (e.g.
// archive/<party>/mdl serves the tables
// tool). Treat as the directory itself
// and append the missing slash.
//
// Discrimination is "does the last segment contain a dot?" — a dot
// is a reliable proxy for "looks like a file with an extension"
// since neither directory names nor default_tool paths contain
// them in this system.
function pathToDir(pathname) { function pathToDir(pathname) {
if (!pathname) return '/'; if (!pathname) return '/';
if (pathname.endsWith('/')) return pathname; if (pathname.endsWith('/')) return pathname;
var slash = pathname.lastIndexOf('/'); var slash = pathname.lastIndexOf('/');
return slash >= 0 ? pathname.substring(0, slash + 1) : '/'; var lastSeg = slash >= 0 ? pathname.substring(slash + 1) : pathname;
if (lastSeg.indexOf('.') !== -1) {
// Has an extension → looks like a file URL → strip the
// filename to land on the parent directory.
return slash >= 0 ? pathname.substring(0, slash + 1) : '/';
}
// No extension → the URL IS the directory; just close it.
return pathname + '/';
} }
// Probe the server-mode root for the current page. Returns: // Probe the server-mode root for the current page. Returns:

View file

@ -117,10 +117,20 @@
} }
function tableNameFromUrl(pathname) { function tableNameFromUrl(pathname) {
// /<dir>/.../<rowsdir>/table.html → name is the rows-dir's // Two URL shapes resolve to a table page:
// basename. // Form A — /<…>/<rowsdir>/table.html (legacy/explicit
const m = String(pathname || '').match(/\/([^\/]+)\/table\.html$/); // entry-point; the tool was opened via the
return m ? m[1] : null; // literal file URL).
// Form B — /<…>/<rowsdir> or /<…>/<rowsdir>/ (served
// by the cascade's `default_tool: tables` at
// archive/<party>/mdl; the URL is the directory
// itself, no trailing filename).
// In both cases the table name is the rows-directory basename.
const a = String(pathname || '').match(/\/([^\/]+)\/table\.html$/);
if (a) return a[1];
const trimmed = String(pathname || '').replace(/\/$/, '');
const b = trimmed.match(/\/([^\/]+)$/);
return b ? b[1] : null;
} }
function stripDotSlash(p) { function stripDotSlash(p) {
@ -198,11 +208,13 @@
return rows; return rows;
} }
// Re-edit URL for one row. Page is at /<dir>/table.html; row file // Re-edit URL for one row. The page directory is the same
// lives at /<dir>/<basename>.yaml; form re-edit URL is // directory the rows live in, regardless of which URL shape
// /<dir>/<basename>.yaml.html — same directory. // (Form A `…/table.html` vs Form B bare `…/<rowsdir>`) we were
// opened with — see tableNameFromUrl.
function rowEditUrl(rowFileName) { function rowEditUrl(rowFileName) {
const pageDir = location.pathname.replace(/\/table\.html$/, '/'); let pageDir = location.pathname.replace(/\/table\.html$/, '/');
if (!pageDir.endsWith('/')) pageDir += '/';
return pageDir + rowFileName + '.html'; return pageDir + rowFileName + '.html';
} }