app: shared OTM login/account look, ported verbatim (v0.6.0)

Port the shared otm-login/otm-menu CSS+markup from
@otm/account-panel otm-login-core.ts 0.44.0 (verbatim, kept in sync
via header comments) into login.html and the sidebar account menu in
index.html/app.js. Accent is the studio's --plum, heading font via
the module's --otm-font-display variable. Behaviour is unchanged
(OTM-first / "Other ways to sign in" toggle, server-filled
__OTM_SSO_URL__, same reasons).

The account menu needs to know whether a session came in via OTM to
show the badge/"Manage OTM account" link, which the session didn't
track before — added a `via: "otm"` claim on the SSO callback's
session and exposed it from GET /api/me (additive; password and
operator-magic sessions are unaffected and read as not-linked).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014LCQmFvKshyAJsE8C3B5ET
This commit is contained in:
2026-10-19 00:20:13 -05:00
co-authored by Claude Fable 5.1
parent fc5a82c45a
commit eca75c14b8
6 changed files with 132 additions and 57 deletions
+2 -2
View File
@@ -229,9 +229,9 @@ const routes = {
if (!user) user = await db.user.findFirst({ where: { role: p.role }, orderBy: { id: "asc" } });
if (!user) return fail("no_account");
const next = p.next && p.next.startsWith("/") && !p.next.startsWith("//") ? p.next : "/";
return { _setSession: { email: user.email, exp: Date.now() + SESSION_DAYS * 86400e3 }, _redirect: next };
return { _setSession: { email: user.email, via: "otm", exp: Date.now() + SESSION_DAYS * 86400e3 }, _redirect: next };
},
"GET /api/me": async (_b, _q, session) => ({ email: session.email, operator: session.operator ?? null }),
"GET /api/me": async (_b, _q, session) => ({ email: session.email, operator: session.operator ?? null, via: session.via ?? null }),
"GET /api/summary": async () => ({
todos: (await db.todo.findMany({ orderBy: { id: "desc" }, take: 60 })).map(sTodo),