feat(otm): shared OTM login look, ported verbatim (0.4.2)
Build / Build and release (push) Failing after 1s
Build / Build and release (push) Failing after 1s
Port OTM_LOGIN_CSS + the otm-login markup structure from @otm/account-panel otm-login-core.ts 0.44.0 verbatim into login_page() (brand "13ft" / "Read the article without the cruft.", accent --otm-accent:#b5562a copper, otherwise the module's neutral ink defaults). Still OTM-only (no "Other ways to sign in"), fail-closed 503 copy + reasons + tides beacon unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014LCQmFvKshyAJsE8C3B5ET
This commit is contained in:
+44
-19
@@ -32,7 +32,7 @@ from typing import Any, Optional
|
||||
|
||||
import requests
|
||||
|
||||
APP_VERSION = "0.4.1"
|
||||
APP_VERSION = "0.4.2"
|
||||
OTM_AUDIENCE = "app:13ft"
|
||||
OTM_BASE = os.environ.get("OTM_SSO_AUTHORIZE_BASE", "https://admin.poweredbyotm.com")
|
||||
SESSION_COOKIE = "thirteen_session"
|
||||
@@ -243,34 +243,59 @@ SSO_REASONS = {
|
||||
}
|
||||
|
||||
|
||||
# Ported from @otm/account-panel otm-login-core.ts 0.44.0 (OTM_LOGIN_CSS) —
|
||||
# keep in sync, verbatim. 13ft is OTM-only (no local accounts), so only the
|
||||
# .otm-login rules are needed — no .otm-menu/.otm-badge here.
|
||||
OTM_LOGIN_CSS = """
|
||||
.otm-login{--otm-accent:#b5562a;--otm-accent-ink:#fff;--otm-bg:#faf8f5;--otm-card:#fff;--otm-fg:#1f1b18;--otm-muted:#6b6560;--otm-line:rgba(31,27,24,.12);--otm-radius:20px;--otm-font-display:inherit;
|
||||
min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px 16px;background:var(--otm-bg);color:var(--otm-fg);box-sizing:border-box}
|
||||
@media (prefers-color-scheme:dark){.otm-login{--otm-bg:#161412;--otm-card:#211e1b;--otm-fg:#f3efe9;--otm-muted:#a39b92;--otm-line:rgba(243,239,233,.12)}}
|
||||
.otm-login *{box-sizing:border-box}
|
||||
.otm-login__col{width:100%;max-width:26rem}
|
||||
.otm-login__brand{text-align:center;margin-bottom:24px}
|
||||
.otm-login__mark{width:56px;height:56px;border-radius:999px;background:var(--otm-accent);color:var(--otm-accent-ink);display:inline-flex;align-items:center;justify-content:center;font-weight:700;font-size:20px;letter-spacing:.02em;margin-bottom:12px}
|
||||
.otm-login__mark svg,.otm-login__mark img{width:28px;height:28px}
|
||||
.otm-login__name{font-family:var(--otm-font-display);font-size:30px;line-height:1.1;letter-spacing:-.01em;font-weight:600;margin:0}
|
||||
.otm-login__tagline{color:var(--otm-muted);font-size:14px;margin:6px 0 0}
|
||||
.otm-login__card{background:var(--otm-card);border:1px solid var(--otm-line);border-radius:var(--otm-radius);padding:24px;box-shadow:0 4px 24px rgba(0,0,0,.06)}
|
||||
.otm-login__btn{display:flex;align-items:center;justify-content:center;width:100%;height:44px;border-radius:12px;border:0;background:var(--otm-accent);color:var(--otm-accent-ink);font:inherit;font-weight:600;font-size:15px;text-decoration:none;cursor:pointer}
|
||||
.otm-login__btn:hover{filter:brightness(1.06)}
|
||||
.otm-login__btn--outline{background:transparent;color:var(--otm-fg);border:1px solid var(--otm-line)}
|
||||
.otm-login__btn--outline:hover{background:rgba(127,127,127,.06);filter:none}
|
||||
.otm-login__btn[disabled]{opacity:.55;cursor:default}
|
||||
.otm-login__helper{color:var(--otm-muted);font-size:14px;text-align:center;margin:12px 0 0;line-height:1.45}
|
||||
.otm-login__error{color:#b3556b;font-size:14px;margin:0 0 12px;text-align:center}
|
||||
.otm-login__link{display:inline-block;background:none;border:0;padding:0;font:inherit;font-size:14px;color:var(--otm-muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
|
||||
.otm-login__link:hover{color:var(--otm-fg)}
|
||||
.otm-login__foot{text-align:center;margin-top:16px}
|
||||
.otm-login__stack>*+*{margin-top:10px}
|
||||
"""
|
||||
|
||||
|
||||
def login_page(next_path: str, reason: Optional[str], configured: bool) -> str:
|
||||
msg = ""
|
||||
if reason:
|
||||
msg = f'<p style="color:#c0566e;margin:0 0 1rem;font-size:.9rem">{html_mod.escape(SSO_REASONS.get(reason, "Sign-in failed."))}</p>'
|
||||
msg = f'<p class="otm-login__error" role="alert">{html_mod.escape(SSO_REASONS.get(reason, "Sign-in failed."))}</p>'
|
||||
if not configured:
|
||||
msg = '<p style="color:#c0566e;margin:0;font-size:.9rem">This 13ft isn\'t wired to OTM yet, so nobody can sign in. (Fails closed on purpose — it\'s a fetch proxy.)</p>'
|
||||
# OTM-only app (no local accounts) — step 2's primary button is the whole
|
||||
# card, with the passkey helper line under it. There is no step 3 ("Other
|
||||
# ways to sign in"): there is no other way.
|
||||
msg = '<p class="otm-login__error" role="alert">This 13ft isn\'t wired to OTM yet, so nobody can sign in. (Fails closed on purpose — it\'s a fetch proxy.)</p>'
|
||||
# OTM-only app (no local accounts) — the "Sign in with OTM" button is the
|
||||
# whole card, with the passkey helper line under it. There is no "Other
|
||||
# ways to sign in": there is no other way.
|
||||
card = msg + (
|
||||
f'<a href="{html_mod.escape(otm_sign_in_url(next_path))}" '
|
||||
'style="display:block;box-sizing:border-box;width:100%;text-align:center;padding:.85em 1.2em;'
|
||||
'margin-top:.75rem;border-radius:9px;border:1px solid var(--accent);background:var(--accent);'
|
||||
'color:var(--accent-ink);text-decoration:none;font-weight:600">Sign in with OTM</a>'
|
||||
'<p style="text-align:center;font-size:.8rem;opacity:.7;margin:.9rem 0 0">'
|
||||
'Your OTM account — Face ID, Touch ID or a passkey works there.</p>'
|
||||
f'<a class="otm-login__btn" href="{html_mod.escape(otm_sign_in_url(next_path))}">Sign in with OTM</a>'
|
||||
'<p class="otm-login__helper">Your OTM account — Face ID, Touch ID or a passkey works there.</p>'
|
||||
if configured else ""
|
||||
)
|
||||
return (
|
||||
'<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">'
|
||||
'<title>13ft — sign in</title><style>:root{color-scheme:light dark;--accent:#3a6df0;--accent-ink:#fff}'
|
||||
'body{font-family:system-ui,sans-serif;max-width:24rem;margin:10vh auto;padding:0 1rem;line-height:1.5}'
|
||||
'.brand{text-align:center;margin:0 0 1.75rem}.brand h1{margin:0 0 .3rem;font-size:1.9rem}'
|
||||
'.brand p{margin:0;opacity:.7;font-size:.95rem}'
|
||||
'.card{border:1px solid #8884;border-radius:14px;padding:1.5rem}</style></head>'
|
||||
f'<title>13ft — sign in</title><style>{OTM_LOGIN_CSS}</style></head>'
|
||||
'<body>'
|
||||
'<div class="brand"><h1>13ft</h1><p>Read the article without the cruft.</p></div>'
|
||||
f'<div class="card">{card}</div>'
|
||||
'<div class="otm-login" style="--otm-accent:#b5562a;--otm-accent-ink:#fff">'
|
||||
'<div class="otm-login__col">'
|
||||
'<div class="otm-login__brand"><div class="otm-login__mark">13</div>'
|
||||
'<h1 class="otm-login__name">13ft</h1><p class="otm-login__tagline">Read the article without the cruft.</p></div>'
|
||||
f'<div class="otm-login__card">{card}</div>'
|
||||
'</div></div>'
|
||||
f'{tides_beacon()}</body></html>'
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user