mavweb: unify UI — shared ui.css + nav partial across all pages

One theme (the PWA's dark palette) for dash/history/trace/notifications/
tools/passkey via static/ui.css; shared nav template with active-page
highlight; tables wrapped in .scroll so they pan on phones; PWA nav no
longer clips the RU/EN toggle; dash 'updated' timestamp fixed (selector
matched nothing). AGENTS.md documents the local preview/screenshot recipe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASstMtsZWLSRcD1Tq8T68Q
This commit is contained in:
kami
2026-07-05 18:36:31 +04:00
parent a158ffd1d2
commit d7c0cf89d3
9 changed files with 215 additions and 158 deletions
+10 -6
View File
@@ -67,11 +67,15 @@ func (h *PasskeyHandle) Page(w http.ResponseWriter, r *http.Request) {
const passkeyPageHTML = `<!doctype html><meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>maven · passkey</title>
<style>body{font:16px system-ui;max-width:34rem;margin:3rem auto;padding:0 1rem}
button{font:inherit;padding:.5rem 1rem;margin:.3rem .3rem 0 0;cursor:pointer}
#msg{margin-top:1rem;padding:.6rem;border-radius:.3rem;white-space:pre-wrap}
.ok{background:#e6ffed}.err{background:#ffe6e6}</style>
<h1>maven passkey</h1>
<link rel=stylesheet href=/ui.css>
<style>button{padding:.5rem 1rem;margin:.3rem .3rem 0 0}
#msg{white-space:pre-wrap}</style>
<nav class=site>
<a href=/>voice</a> <a href=/dash>dash</a> <a href=/history>history</a>
<a href=/trace>trace</a> <a href=/notifications>notifications</a>
<a href=/tools>tools</a> <a href=/auth/passkey class=active>passkey</a>
</nav>
<h1>passkey</h1>
<p>Enroll a passkey once, then assert it to unlock destructive actions
(tool enable) for a few minutes.</p>
<button onclick=enroll()>enroll passkey</button>
@@ -81,7 +85,7 @@ button{font:inherit;padding:.5rem 1rem;margin:.3rem .3rem 0 0;cursor:pointer}
<script>
const b64u=b=>btoa(String.fromCharCode(...new Uint8Array(b))).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');
const ub64=s=>{s=s.replace(/-/g,'+').replace(/_/g,'/');const b=atob(s),a=new Uint8Array(b.length);for(let i=0;i<b.length;i++)a[i]=b.charCodeAt(i);return a;};
const say=(t,ok)=>{const m=document.getElementById('msg');m.textContent=t;m.className=ok?'ok':'err';};
const say=(t,ok)=>{const m=document.getElementById('msg');m.textContent=t;m.className=ok?'msg msg-ok':'msg msg-err';};
async function enroll(){try{
const {challenge,options}=await (await fetch('/auth/webauthn/register/begin')).json();
options.challenge=ub64(options.challenge);