feat: web UI redesign with PWA support, voice page, and mobile layout

- Redesign dash/history/notifications/trace pages with unified CSS framework
  (ui.css): cards, badges, scrollable tables, responsive grid, PWA shell.
- Add /voice page with chat-bubble transcript, listening indicator, and
  push-to-talk button for voice interaction.
- Add mavweb.js: client-side JS for live transcript polling, auto-scroll,
  push-to-talk, and service-worker registration.
- Update PWA manifest, icon, and service-worker for installable web app.
- Add WebAuthn credential management (register/list/delete) with modal UI.
- Update main.go: /voice route, WebAuthn handlers, new page navigation.
- Update handlers_test.go for new voice and WebAuthn routes.
This commit is contained in:
kami
2026-07-10 15:49:18 +04:00
parent 25357bf267
commit 7a95097cc7
14 changed files with 1392 additions and 243 deletions
+11 -15
View File
@@ -1,11 +1,6 @@
<!doctype html><meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>maven · rule trace</title>
<link rel=stylesheet href=/ui.css>
{{template "nav" "trace"}}
<main class=page>
<h1>rule trace</h1>
<div class=updated style=margin-bottom:var(--space-lg)>{{.Now | ago}} — winner: <strong>{{if .Winner}}{{.Winner}}{{else}}nothing fired{{end}}</strong></div>
{{template "shellTop" "trace"}}
<h1>Rule Trace</h1>
<div class=hint style=margin-bottom:var(--ethos-space-4)>{{.Now | ago}} — winner: <strong>{{if .Winner}}{{.Winner}}{{else}}nothing fired{{end}}</strong></div>
<div class=scroll><table class=mono>
<tr><th>rule<th>sev<th>predicate<th>gate<th>blocked by<th>detail<th>selected<th>lost to</tr>
{{range .Rules}}<tr>
@@ -15,15 +10,16 @@
<td class={{if .GateResult}}green{{else if .GateBlockedBy}}red{{else}}gray{{end}}>{{.GateResult}}</td>
<td>{{.GateBlockedBy}}</td>
<td><details><summary>gate</summary>
<span class=muted>snooze_until:</span> {{.GateDetail.SnoozeUntil | fmtTime}}<br>
<span class=muted>cooldown_until:</span> {{.GateDetail.CooldownUntil | fmtTime}}<br>
<span class=muted>quiet_hours:</span> {{.GateDetail.QuietHours}}<br>
<span class=muted>calendar_busy:</span> {{.GateDetail.CalendarBusy}}<br>
<span class=muted>presence:</span> {{.GateDetail.Presence}}<br>
<span class=muted>inert_keys_missing:</span> {{if .GateDetail.InertKeysMissing}}{{join .GateDetail.InertKeysMissing ", "}}{{else}}—{{end}}
<span class=hint>snooze_until:</span> {{.GateDetail.SnoozeUntil | fmtTime}}<br>
<span class=hint>cooldown_until:</span> {{.GateDetail.CooldownUntil | fmtTime}}<br>
<span class=hint>quiet_hours:</span> {{.GateDetail.QuietHours}}<br>
<span class=hint>calendar_busy:</span> {{.GateDetail.CalendarBusy}}<br>
<span class=hint>presence:</span> {{.GateDetail.Presence}}<br>
<span class=hint>inert_keys_missing:</span> {{if .GateDetail.InertKeysMissing}}{{join .GateDetail.InertKeysMissing ", "}}{{else}}—{{end}}
</details></td>
<td class={{if .WasSelected}}green{{end}}>{{.WasSelected}}</td>
<td>{{.LostTo}}</td>
</tr>{{end}}
</table></div>
</main>
{{template "shellBottom"}}
</html>