fix(ui): no-cache static assets (stop stale CSS/JS after deploys); live nav-active sync; breadcrumb sized+themed to match back button

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-04 23:23:00 +10:00
parent 261ca6ba9e
commit c32871d9d0
3 changed files with 23 additions and 7 deletions

View File

@@ -101,7 +101,15 @@ export function renderSidebar(root) {
) )
); );
renderSpaceTree(spacesContainer); // Sync the active highlight across ALL nav items (global links + space tree)
// to the current hash. navItem only sets active at creation, so without this
// the highlight stays stuck on the previously-selected tab until a refresh.
function syncActive() {
root.querySelectorAll('a.sb-item').forEach(a =>
a.classList.toggle('active', a.getAttribute('href') === location.hash));
}
renderSpaceTree(spacesContainer).then(syncActive);
// Pending-count badge wiring // Pending-count badge wiring
on('pending-count', (n) => { on('pending-count', (n) => {
@@ -110,6 +118,10 @@ export function renderSidebar(root) {
if (n > 0) inboxItem.appendChild(el('span', { class: 'badge' }, String(n))); if (n > 0) inboxItem.appendChild(el('span', { class: 'badge' }, String(n)));
}); });
// Refresh tree on hashchange (active highlight) and on space creation. // On navigation: re-render the tree (lazy state) then re-sync the highlight.
window.addEventListener('hashchange', () => renderSpaceTree(spacesContainer)); window.addEventListener('hashchange', async () => {
await renderSpaceTree(spacesContainer);
syncActive();
});
syncActive();
} }

View File

@@ -166,12 +166,12 @@ button.ghost:hover { color: var(--text); border-color: var(--accent-dim); }
.doc-head .back-btn { margin-bottom: 0; } .doc-head .back-btn { margin-bottom: 0; }
.doc-head .exp-menu { margin-left: auto; } .doc-head .exp-menu { margin-left: auto; }
/* Breadcrumb: Space parent current */ /* Breadcrumb: Space parent current — sized + themed to sit inline with the back button */
.crumbs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12px; } .crumbs { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 13px; font-family: var(--font-ui); line-height: 1; }
.crumb { color: var(--muted); text-decoration: none; } .crumb { color: var(--muted); text-decoration: none; }
.crumb:hover { color: var(--accent); } .crumb:hover { color: var(--accent); }
.crumb.current { color: var(--text); } .crumb.current { color: var(--text); }
.crumb-sep { color: var(--accent-dim); } .crumb-sep { color: var(--accent-dim); font-size: 14px; }
/* Export dropdown */ /* Export dropdown */
.exp-menu { position: relative; } .exp-menu { position: relative; }

View File

@@ -20,7 +20,11 @@ export function createApp() {
limit: '10mb', limit: '10mb',
verify: (req, _res, buf) => { req.rawBody = buf; } verify: (req, _res, buf) => { req.rawBody = buf; }
})); }));
app.use(express.static('public')); // no-cache (not no-store): the browser may cache but must revalidate, so a
// deploy's new CSS/JS takes effect immediately instead of serving stale assets.
app.use(express.static('public', {
setHeaders: (res) => res.setHeader('Cache-Control', 'no-cache')
}));
// /api/ingest/* bypasses agentOrOwner — webhooks authenticate via HMAC // /api/ingest/* bypasses agentOrOwner — webhooks authenticate via HMAC
// and need access to req.rawBody captured above. // and need access to req.rawBody captured above.