diff --git a/zddc/internal/apps/embedded/archive.html b/zddc/internal/apps/embedded/archive.html
index 8cdd360..c39a872 100644
--- a/zddc/internal/apps/embedded/archive.html
+++ b/zddc/internal/apps/embedded/archive.html
@@ -2582,7 +2582,7 @@ td[data-field="trackingNumber"] {
@@ -6193,10 +6193,10 @@ X.B(E,Y);return E}return J}())
}
async function scanHttpRoot(scanRootUrl, rootUrl, callbacks) {
- // Mode 1 — multi-project (?projects= set). Skip listing scanRootUrl entirely:
- // the zddc-server returns a ProjectInfo array there (not a Caddy fileInfo
- // listing), so iterating it as if it were a directory listing wouldn't work.
- // Project URLs are deterministic — go straight to each one.
+ // Mode 1 — multi-project (?projects= set). Skip listing scanRootUrl
+ // entirely: project URLs are deterministic, so go straight to each one
+ // (the names in projectFilter, slash-normalised in app.js against the
+ // server's root listing). Avoids depending on the root listing's shape.
if (window.app.projectFilter && window.app.projectFilter.size > 0) {
const tasks = [];
for (const name of window.app.projectFilter) {
@@ -9818,11 +9818,22 @@ window.app.modules.filtering = {
var serverProjects = await resp.json();
if (Array.isArray(serverProjects) && serverProjects.length > 0
&& serverProjects[0] && typeof serverProjects[0].name === 'string') {
- serverNames = new Set(serverProjects.map(function(p) { return p.name; }));
+ // GET / Accept: application/json returns listing.FileInfo
+ // entries (not the legacy ProjectInfo shape): directory
+ // names carry a trailing "/", and the listing can include
+ // non-directory entries. Normalise to bare directory names
+ // so they match the slash-free projectFilter parsed from
+ // ?projects= (url-state.js). Without this, every URL-listed
+ // project misses the intersection below → "no access"
+ // banner + empty scan.
+ var bareName = function (p) { return p.name.replace(/\/+$/, ''); };
+ var isProjectDir = function (p) { return p.is_dir === true || /\/$/.test(p.name); };
+ var projectEntries = serverProjects.filter(isProjectDir);
+ serverNames = new Set(projectEntries.map(bareName));
var titles = {};
- serverProjects.forEach(function (p) {
+ projectEntries.forEach(function (p) {
if (p && typeof p.title === 'string' && p.title) {
- titles[p.name] = p.title;
+ titles[bareName(p)] = p.title;
}
});
window.app.projectTitles = titles;
diff --git a/zddc/internal/apps/embedded/browse.html b/zddc/internal/apps/embedded/browse.html
index 5c842c6..237e74b 100644
--- a/zddc/internal/apps/embedded/browse.html
+++ b/zddc/internal/apps/embedded/browse.html
@@ -2344,7 +2344,7 @@ body {
diff --git a/zddc/internal/apps/embedded/classifier.html b/zddc/internal/apps/embedded/classifier.html
index 0d81cff..4d93f58 100644
--- a/zddc/internal/apps/embedded/classifier.html
+++ b/zddc/internal/apps/embedded/classifier.html
@@ -1793,7 +1793,7 @@ body.is-elevated::after {
diff --git a/zddc/internal/apps/embedded/index.html b/zddc/internal/apps/embedded/index.html
index 5e2eff9..415b012 100644
--- a/zddc/internal/apps/embedded/index.html
+++ b/zddc/internal/apps/embedded/index.html
@@ -1536,7 +1536,7 @@ body {