§10 review pass: address the architecture + product reviewers (todo §10); hide the gated Dashboard nav node from anonymous visitors in buildPluginChrome (a no-permission link to /dashboard only dead-ended them at /login) and dedup it into a shared DASHBOARD_NAV (admin-nav.ts, reused by chrome + adminNav). New chrome.signInHref bakes the current page in as return_to for the shell's anonymous Sign-in link (shell.ejs + reference overview.ejs), mirrored as optional ShellModel.signInHref so the typed builder is complete. ctx.chrome is now a lazy, memoized getter (context.ts chrome option = a factory) so a json/redirect handler or the public "/" with a standalone home never composes the global menu — app.ts passes the app-level memoized factory at every site. Default /dashboard prints a "Starter dashboard" note framing the mock-data home as a replaceable demo (signals its inert affordances); stale "until §4" comments fixed. RESERVED_PLUGIN_IDS drift-guard test derives the built-in segments from AUTH_FLOWS + ADMIN_*_BASE + host literals (home stays deliberately unreserved). Refreshed the stale plugin-contract status blurb and documented the chrome.*→partials/shell mapping. Reviewers: architecture + product APPROVE (no addressable findings remain), stability APPROVE (no Critical/High/Medium). typecheck + 356 units + visual(10) + full-flow(7) E2E green.

This commit is contained in:
2026-06-21 01:19:40 +02:00
parent 7bdeb24b7f
commit 58398481ca
17 changed files with 117 additions and 46 deletions

View File

@@ -4,7 +4,8 @@
`actions` (topbar buttons), `body` (page content); `styles` is an optional array of
extra stylesheet hrefs (e.g. a plugin's own /public/<id>/x.css). Text locals: `title`, `brand`
({ name, logo?, sub? } — logo image else the default mark), `theme` (default for the
theme-switch), `user`, `breadcrumbs`, `csrfToken` (the Sign-out POST form's hidden field).
theme-switch), `user`, `breadcrumbs`, `csrfToken` (the Sign-out POST form's hidden field),
`signInHref` (anonymous "Sign in" target, carries return_to; default /login).
Branding comes from config/menu.ts; `user`/`csrfToken` from §4 auth.
%><%
const title = locals.title || "Plainpages";
@@ -66,8 +67,9 @@
</div>
</details>
<% } else { %>
<%# anonymous (a public page in the shell, §10): no session to end — offer a way in instead %>
<a class="btn btn-primary" href="/login" style="flex:1 1 auto"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-user" /></svg>Sign in</a>
<%# anonymous (a public page in the shell, §10): no session to end — offer a way in instead.
signInHref carries this page as return_to (chrome.signInHref); falls back to bare /login. %>
<a class="btn btn-primary" href="<%= locals.signInHref || '/login' %>" style="flex:1 1 auto"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-user" /></svg>Sign in</a>
<% } %>
<%- include("menu", {