From e5ba2b616875513cda15de0ab96db0f314727673 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Thu, 14 May 2026 12:12:06 -0500 Subject: [PATCH] fix(tables): handle bare-directory URLs served as default_tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visiting `/Project-1/archive/PartyA/mdl` (no trailing slash) errored with `Unrecognized table URL` because tableNameFromUrl only matched `…//table.html`. The cascade declares `default_tool: tables` at `archive//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 `…//table.html` and the new bare-directory shape. Co-Authored-By: Claude Opus 4.7 (1M context) --- shared/zddc-source.js | 26 +++++++++++++++++++++++--- tables/js/context.js | 28 ++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/shared/zddc-source.js b/shared/zddc-source.js index ab649ec..c550452 100644 --- a/shared/zddc-source.js +++ b/shared/zddc-source.js @@ -289,13 +289,33 @@ // Top-level helpers // ----------------------------------------------------------------- - // Strip a trailing tool .html (e.g. classifier.html) from a path - // to land on the "directory the tool was opened in". + // Resolve "the directory the tool was opened in" for the current + // page URL. Two URL shapes serve a tool: + // + // /…/.html — file URL; strip the trailing filename. + // /…// — trailing-slash directory URL; keep it. + // /…/ — bare-directory URL served by the + // cascade's `default_tool` (e.g. + // archive//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) { if (!pathname) return '/'; if (pathname.endsWith('/')) return pathname; 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: diff --git a/tables/js/context.js b/tables/js/context.js index 656cd33..5f77c6a 100644 --- a/tables/js/context.js +++ b/tables/js/context.js @@ -117,10 +117,20 @@ } function tableNameFromUrl(pathname) { - // //...//table.html → name is the rows-dir's - // basename. - const m = String(pathname || '').match(/\/([^\/]+)\/table\.html$/); - return m ? m[1] : null; + // Two URL shapes resolve to a table page: + // Form A — /<…>//table.html (legacy/explicit + // entry-point; the tool was opened via the + // literal file URL). + // Form B — /<…>/ or /<…>// (served + // by the cascade's `default_tool: tables` at + // archive//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) { @@ -198,11 +208,13 @@ return rows; } - // Re-edit URL for one row. Page is at //table.html; row file - // lives at //.yaml; form re-edit URL is - // //.yaml.html — same directory. + // Re-edit URL for one row. The page directory is the same + // directory the rows live in, regardless of which URL shape + // (Form A `…/table.html` vs Form B bare `…/`) we were + // opened with — see tableNameFromUrl. 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'; }