From 77e3d467696a38ade6dbd2ebe8352af2f93221d9 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 8 Nov 2026 19:35:14 +0100 Subject: [PATCH] Rename ShellUser.menu to profileLinks, so menu keeps one meaning in the shell --- README.md | 2 +- src/ui/chrome.test.ts | 2 +- src/ui/chrome.ts | 6 +++--- src/ui/shell.test.ts | 2 +- views/partials/shell.ejs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cb94f40..f27b87a 100644 --- a/README.md +++ b/README.md @@ -608,7 +608,7 @@ interface RequestContext { } ``` -**`ctx.chrome`** — `{ brand, csrfToken, nav, signInHref, theme, user }` (`user.menu` holds the profile-dropdown links). Hand each field to the +**`ctx.chrome`** — `{ brand, csrfToken, nav, signInHref, theme, user }` (`user.profileLinks` holds the profile-dropdown links). Hand each field to the matching `partials/shell` local and a `view` result renders the **native app shell**, exactly as `examples/plugins/scheduling/views/overview.ejs` does; a field you omit falls back to its shell default rather than erroring. `chrome.nav` is the whole global menu — every installed plugin's diff --git a/src/ui/chrome.test.ts b/src/ui/chrome.test.ts index bf61b47..3450d85 100644 --- a/src/ui/chrome.test.ts +++ b/src/ui/chrome.test.ts @@ -103,7 +103,7 @@ const entries = (opts: { permissions?: string[]; signedIn?: boolean }): { href: plugins: [account], translatorFor: () => (key) => (key === "account.settings" ? "Inställningar" : key), ...(opts.signedIn === false ? {} : { user: { email: "ada@x.io", id: "u1", permissions: opts.permissions ?? [] } }), - }).user.menu!; + }).user.profileLinks!; test("profile menu entries are gated like nav nodes, translated by their plugin and carry the locale", () => { assert.deepEqual(entries({}), [ diff --git a/src/ui/chrome.ts b/src/ui/chrome.ts index 93102a6..8c84d68 100644 --- a/src/ui/chrome.ts +++ b/src/ui/chrome.ts @@ -22,11 +22,11 @@ export interface ProfileLink { // What a plugin's `profileMenu` declares: a link plus the one gate that decides who sees it. export interface ProfileMenuEntry extends ProfileLink, Gate {} -// `menu` rides on the user, which every view already hands to the shell, so no view needs a new local. +// `profileLinks` rides on the user, which every view already hands to the shell, so no view needs a new local. export interface ShellUser { email: string; initials: string; - menu?: ProfileLink[]; // gate-filtered, translated, locale-carried. The host always sets it; a hand-built fixture may omit it + profileLinks?: ProfileLink[]; // gate-filtered, translated, locale-carried. The host always sets it; a hand-built fixture may omit it name: string; } @@ -79,7 +79,7 @@ export function buildPluginChrome(opts: ChromeOptions): PageChrome { nav: carryLocaleInto(nav, carryLocale), signInHref: carryLocale(returnTo), ...(theme != null ? { theme } : {}), - user: { ...shellUser(opts.user, t), menu: profileLinks(opts, t, carryLocale) }, + user: { ...shellUser(opts.user, t), profileLinks: profileLinks(opts, t, carryLocale) }, }; } diff --git a/src/ui/shell.test.ts b/src/ui/shell.test.ts index cd5006a..8fd3e4a 100644 --- a/src/ui/shell.test.ts +++ b/src/ui/shell.test.ts @@ -137,7 +137,7 @@ test("profile menu entries render as links above Sign out, with their icon", asy csrfToken: "tok.sig", user: { email: "ada@acme.io", initials: "AD", name: "ada", - menu: [{ href: "/account/settings", icon: "i-user", label: "My " }, { href: "/account/help", label: "Help" }], + profileLinks: [{ href: "/account/settings", icon: "i-user", label: "My " }, { href: "/account/help", label: "Help" }], }, }); const panel = html.slice(html.indexOf('id="profile-menu"'), html.indexOf("")); diff --git a/views/partials/shell.ejs b/views/partials/shell.ejs index a771b05..701adb8 100644 --- a/views/partials/shell.ejs +++ b/views/partials/shell.ejs @@ -71,7 +71,7 @@