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:
+50
-4
@@ -873,10 +873,56 @@ async function ideas() {
|
||||
}));
|
||||
}
|
||||
|
||||
document.getElementById("signout")?.addEventListener("click", async () => {
|
||||
await api("/api/logout", {});
|
||||
location.href = "/login";
|
||||
});
|
||||
// Account menu in the sidebar — plain-HTML port of @otm/account-panel's
|
||||
// OtmAccountMenu (otm-login.tsx 0.44.0): initials avatar, OTM badge + "Manage
|
||||
// OTM account" when the session came in via Sign in with OTM, Sign out
|
||||
// always. No passwordHref — the studio has no local "change password" flow.
|
||||
function initials(name, fallback = "?") {
|
||||
const parts = String(name ?? "").trim().split(/\s+/).filter(Boolean);
|
||||
if (!parts.length) return fallback;
|
||||
return parts.slice(0, 2).map((p) => p[0].toUpperCase()).join("");
|
||||
}
|
||||
async function buildAccountMenu() {
|
||||
const nav = document.getElementById("account-nav");
|
||||
if (!nav) return;
|
||||
// fetch() directly, not the shared `get` helper — that's defined further
|
||||
// down this file and this runs immediately at script load.
|
||||
const me = await fetch("/api/me").then((r) => r.json()).catch(() => ({}));
|
||||
const email = me.email || "";
|
||||
const linked = me.via === "otm";
|
||||
nav.innerHTML = `
|
||||
<details class="otm-menu" style="--otm-accent:#8d6b94;--otm-accent-ink:#fff">
|
||||
<summary aria-label="Account menu">
|
||||
<span class="otm-menu__avatar" aria-hidden="true">${esc(initials(email))}</span>
|
||||
<span class="otm-menu__name">${esc(email)}</span>
|
||||
${linked ? `<span class="otm-badge">OTM</span>` : ""}
|
||||
</summary>
|
||||
<div class="otm-menu__panel" role="menu">
|
||||
<div class="otm-menu__who">
|
||||
${linked ? `<strong><span class="otm-badge">OTM</span> Your OTM account</strong>` : ""}
|
||||
<span>${esc(email)}</span>
|
||||
</div>
|
||||
${linked ? `<a class="otm-menu__item" role="menuitem" href="https://admin.poweredbyotm.com/account" target="_blank" rel="noopener noreferrer">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="M15 3h6v6"/><path d="M10 14 21 3"/></svg>
|
||||
Manage OTM account
|
||||
</a>` : ""}
|
||||
<button type="button" class="otm-menu__item" role="menuitem" id="signout">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/></svg>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
</details>`;
|
||||
document.getElementById("signout")?.addEventListener("click", async () => {
|
||||
await api("/api/logout", {});
|
||||
location.href = "/login";
|
||||
});
|
||||
const menu = nav.querySelector("details.otm-menu");
|
||||
if (menu) {
|
||||
document.addEventListener("mousedown", (e) => { if (menu.open && !menu.contains(e.target)) menu.open = false; });
|
||||
document.addEventListener("keydown", (e) => { if (e.key === "Escape" && menu.open) menu.open = false; });
|
||||
}
|
||||
}
|
||||
buildAccountMenu();
|
||||
|
||||
// ── version + one-tap update (OTM self-update) ───────────────────
|
||||
const foot = $("#foot");
|
||||
|
||||
Reference in New Issue
Block a user