feat: last-used persona cookie, emoji dropdown, theme support, auth status move

- cx_last_persona cookie set on serve_ui; root/login/help/settings
  redirects use preferred persona from cookie instead of alphabetically first
- /api/personas returns [{name, emoji}] objects; persona switcher dropdown
  renders emoji + name with flex layout and .pd-emoji span
- Help, Settings, Model Registry pages apply localStorage theme on load
  (no flash); CSS variables for dark/light replacing all hardcoded hex values
- Claude CLI auth status moved from prominent chat banner to Anthropic
  provider block in Model Registry — live dot indicator (ok/warn/err)
- Auth banner removed from main chat UI (index.html, app.js, style.css)
- Add Model collapsed into Models section as <details> to shorten page
- Light-mode overrides for provider icons, model badges, ctx-badge, tags
  (Anthropic/Google/local colors now readable in both themes)
- Help page gains table, pre/code, hr styles for HELP.md rendered content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-04-28 22:52:34 -04:00
parent 1222f806ce
commit 66cb197de0
9 changed files with 305 additions and 245 deletions

View File

@@ -7,7 +7,23 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
<script>(function(){var t=localStorage.getItem('theme')||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');document.documentElement.setAttribute('data-theme',t);})();</script>
<style>
:root {
--pg-bg: #0f1117; --pg-surface: #1a1d27;
--pg-border: #2d3148;
--pg-text: #e2e8f0; --pg-muted: #94a3b8;
--pg-dim: #64748b; --pg-dimmer: #475569;
--pg-bright: #cbd5e1; --pg-nav-hover: rgba(255,255,255,0.05);
}
[data-theme="light"] {
--pg-bg: #f4f2fa; --pg-surface: #ffffff;
--pg-border: #d0c8e8;
--pg-text: #1a1228; --pg-muted: #5a5478;
--pg-dim: #7a7290; --pg-dimmer: #9e98b0;
--pg-bright: #1a1228; --pg-nav-hover: rgba(0,0,0,0.05);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
@@ -15,18 +31,18 @@
display: flex;
align-items: center;
justify-content: center;
background: #0f1117;
background: var(--pg-bg);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-weight: 450;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #e2e8f0;
color: var(--pg-text);
padding: 1.5rem;
}
.card {
background: #1a1d27;
border: 1px solid #2d3148;
background: var(--pg-surface);
border: 1px solid var(--pg-border);
border-radius: 12px;
padding: 2.5rem 2rem;
width: 100%;
@@ -47,30 +63,30 @@
border-radius: 6px;
font-size: 0.8rem;
font-weight: 500;
color: #64748b;
color: var(--pg-dim);
text-decoration: none;
transition: color 0.15s, background 0.15s;
white-space: nowrap;
}
.nav-link:hover { color: #cbd5e1; background: rgba(255,255,255,0.05); }
.nav-link:hover { color: var(--pg-bright); background: var(--pg-nav-hover); }
.nav-link.active { color: #a78bfa; }
.nav-spacer { flex: 1; min-width: 0.5rem; }
.nav-link.nav-logout { color: #475569; }
.nav-link.nav-logout:hover { color: #94a3b8; background: none; }
.nav-link.nav-logout { color: var(--pg-dimmer); }
.nav-link.nav-logout:hover { color: var(--pg-muted); background: none; }
.logo {
margin-bottom: 1.75rem;
}
.logo h1 { font-size: 1.4rem; font-weight: 700; color: #a78bfa; }
.logo p { font-size: 0.8rem; color: #94a3b8; margin-top: 0.2rem; }
.logo p { font-size: 0.8rem; color: var(--pg-muted); margin-top: 0.2rem; }
h2 {
font-size: 0.9rem;
font-weight: 600;
color: #94a3b8;
color: var(--pg-muted);
margin-bottom: 1rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid #2d3148;
border-bottom: 1px solid var(--pg-border);
}
.section { margin-bottom: 2rem; }
@@ -79,23 +95,23 @@
display: block;
font-size: 0.8rem;
font-weight: 500;
color: #94a3b8;
color: var(--pg-muted);
margin-bottom: 0.4rem;
}
input {
width: 100%;
padding: 0.65rem 0.85rem;
background: #0f1117;
border: 1px solid #2d3148;
background: var(--pg-bg);
border: 1px solid var(--pg-border);
border-radius: 6px;
color: #e2e8f0;
color: var(--pg-text);
font-size: 0.95rem;
outline: none;
transition: border-color 0.15s;
}
input:focus { border-color: #7c3aed; }
input[readonly] { color: #94a3b8; cursor: default; }
input[readonly] { color: var(--pg-muted); cursor: default; }
.field { margin-bottom: 1rem; }
@@ -144,8 +160,8 @@
.persona-link {
display: inline-block;
padding: 0.3rem 0.75rem;
background: #0f1117;
border: 1px solid #2d3148;
background: var(--pg-bg);
border: 1px solid var(--pg-border);
border-radius: 20px;
color: #a78bfa;
font-size: 0.85rem;
@@ -153,12 +169,12 @@
transition: border-color 0.15s;
}
.persona-link:hover { border-color: #7c3aed; }
.persona-list li em { color: #94a3b8; font-size: 0.85rem; }
.persona-list li em { color: var(--pg-muted); font-size: 0.85rem; }
.persona-rename-toggle {
background: none;
border: none;
color: #94a3b8;
color: var(--pg-muted);
font-size: 0.85rem;
cursor: pointer;
padding: 0.2rem 0.4rem;
@@ -172,10 +188,10 @@
.persona-rename-form input[type="text"] {
width: 12rem;
padding: 0.3rem 0.6rem;
background: #0f1117;
background: var(--pg-bg);
border: 1px solid #7c3aed;
border-radius: 6px;
color: #e2e8f0;
color: var(--pg-text);
font-size: 0.9rem;
outline: none;
}
@@ -187,20 +203,20 @@
}
.persona-rename-cancel {
background: none;
border: 1px solid #2d3148;
border: 1px solid var(--pg-border);
border-radius: 6px;
color: #94a3b8;
color: var(--pg-muted);
font-size: 0.85rem;
padding: 0.3rem 0.6rem;
cursor: pointer;
}
.persona-rename-cancel:hover { border-color: #94a3b8; color: #e2e8f0; }
.persona-rename-cancel:hover { border-color: var(--pg-muted); color: var(--pg-text); }
.add-persona {
display: inline-block;
margin-top: 0.75rem;
font-size: 0.8rem;
color: #94a3b8;
color: var(--pg-muted);
text-decoration: none;
}
.add-persona:hover { color: #a78bfa; }
@@ -232,7 +248,7 @@
<input type="text" value="{{ username }}" readonly>
</div>
<button type="button" id="show-rename-user" class="persona-rename-toggle"
style="opacity:0.7; font-size:0.8rem; padding:0.3rem 0.6rem; border:1px solid #2d3148; border-radius:6px; margin-top:0.25rem;">
style="opacity:0.7; font-size:0.8rem; padding:0.3rem 0.6rem; border:1px solid var(--pg-border); border-radius:6px; margin-top:0.25rem;">
✏ Change username
</button>
<form id="rename-user-form" method="POST" action="/settings/username"
@@ -243,14 +259,14 @@
value="{{ username }}"
pattern="[a-z_][a-z0-9_\-]{0,31}" required autofocus
autocomplete="off" data-form-type="other">
<p style="font-size:0.75rem; color:#94a3b8; margin-top:0.3rem;">
<p style="font-size:0.75rem; color:var(--pg-muted); margin-top:0.3rem;">
Lowercase letters, digits, _ or - only. You will be logged out after renaming.
</p>
</div>
<div style="display:flex; gap:0.5rem;">
<button type="submit" style="flex:1; padding:0.5rem; background:#7c3aed; border:none; border-radius:6px; color:#fff; font-size:0.9rem; font-weight:600; cursor:pointer;">Save</button>
<button type="button" id="cancel-rename-user"
style="padding:0.5rem 0.9rem; background:none; border:1px solid #2d3148; border-radius:6px; color:#94a3b8; font-size:0.9rem; cursor:pointer;">Cancel</button>
style="padding:0.5rem 0.9rem; background:none; border:1px solid var(--pg-border); border-radius:6px; color:var(--pg-muted); font-size:0.9rem; cursor:pointer;">Cancel</button>
</div>
</form>
</div>
@@ -262,9 +278,9 @@
<label>Google Account</label>
<input type="text" value="{{ google_email }}" readonly
placeholder="No Google account linked"
style="{{ google_email == '' and 'color:#475569' or '' }}">
style="{{ google_email == '' and 'color:var(--pg-dimmer)' or '' }}">
</div>
<p style="font-size:0.75rem; color:#94a3b8; margin-top:-0.5rem;">
<p style="font-size:0.75rem; color:var(--pg-muted); margin-top:-0.5rem;">
To link or change your Google account, contact Scott.
</p>
</div>
@@ -272,13 +288,13 @@
<!-- Browser cache -->
<div class="section">
<h2>Browser Cache</h2>
<p style="font-size:0.8rem; color:#94a3b8; margin-bottom:0.85rem; line-height:1.55;">
<p style="font-size:0.8rem; color:var(--pg-muted); margin-bottom:0.85rem; line-height:1.55;">
Clears UI preferences stored in this browser: active mode, session ID, memory toggles,
theme, font size, and context tier. Does not sign you out.
</p>
<button type="button" id="clear-ls-btn"
style="padding:0.5rem 1rem; background:none; border:1px solid #2d3148; border-radius:6px;
color:#94a3b8; font-size:0.88rem; font-weight:500; cursor:pointer;
style="padding:0.5rem 1rem; background:none; border:1px solid var(--pg-border); border-radius:6px;
color:var(--pg-muted); font-size:0.88rem; font-weight:500; cursor:pointer;
transition:border-color 0.15s, color 0.15s;">
Clear browser cache
</button>
@@ -290,7 +306,7 @@
<!-- Model Registry link -->
<div class="section">
<h2>Model Registry</h2>
<p style="font-size:0.8rem; color:#94a3b8; margin-bottom:0.85rem; line-height:1.55;">
<p style="font-size:0.8rem; color:var(--pg-muted); margin-bottom:0.85rem; line-height:1.55;">
Configure AI providers (Anthropic, Google), local hosts (Open WebUI, Ollama, OpenRouter, etc.),
and assign models to roles — chat, orchestrator, distill, and more.
</p>