mavweb: add /history page for command history
- New /history route displays recent facts in a dedicated page with voided-row styling (line-through + opacity + badge). - Navigation link added to /dash page header. - Handler calls core.RecentFacts(ctx, 200) and renders historyTmpl.
This commit is contained in:
@@ -8,7 +8,9 @@
|
||||
.pending{color:#fc6}.acted{color:#6c6}.ignored{color:#888}.snoozed{color:#c9f}
|
||||
.pres{color:#6c6}.away{color:#888}
|
||||
.updated{color:#666;font-size:.85rem;margin-top:-.5rem;margin-bottom:1rem}
|
||||
nav a{color:#8cf;margin-right:1rem;font-size:.85rem}
|
||||
</style>
|
||||
<nav><a href=/history>history</a></nav>
|
||||
<h2>presence</h2>
|
||||
<p><span class={{.Presence.Bucket}}>{{.Presence.Bucket}}</span> — score {{printf "%.2f" .Presence.Score}} ({{ago .Presence.Updated}})</p>
|
||||
<div class=updated>обновляется каждые 10с</div>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!doctype html>
|
||||
<html lang=en>
|
||||
<meta charset=utf-8>
|
||||
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||
<title>maven · history</title>
|
||||
<style>
|
||||
body{font:14px system-ui;margin:1.5rem;max-width:64rem}
|
||||
h1{font-size:1.3rem;margin-bottom:.3rem}
|
||||
nav a{margin-right:1rem}
|
||||
table{border-collapse:collapse;width:100%;margin-top:.5rem}
|
||||
th,td{border:1px solid #ccc;padding:.3rem .5rem;text-align:left;vertical-align:top}
|
||||
th{background:#f6f6f6}
|
||||
.voided{opacity:.45;text-decoration:line-through}
|
||||
.void-badge{color:#999;font-size:.8rem;margin-left:.3rem}
|
||||
.key{font-family:monospace;white-space:nowrap}
|
||||
.val{font-family:monospace;word-break:break-all;max-width:20rem}
|
||||
.src{font-size:.85rem;color:#666}
|
||||
.count{color:#888;font-size:.85rem;margin-top:.3rem}
|
||||
</style>
|
||||
<h1>maven · command history</h1>
|
||||
<nav><a href=/dash>dash</a> <a href=/tools>tools</a> <a href=/auth/passkey>passkey</a></nav>
|
||||
<p class=count>{{len .Facts}} facts shown (newest first)</p>
|
||||
<table>
|
||||
<tr><th>when<th>kind<th>key<th>value<th>source<th>conf</tr>
|
||||
{{range .Facts}}<tr{{if .VoidsID}} class=voided{{end}}>
|
||||
<td>{{if .VoidsID}}<span class=void-badge>⨯ voided</span>{{end}}{{.Ts.Format "2006-01-02 15:04"}}</td>
|
||||
<td>{{.Kind}}</td>
|
||||
<td class=key>{{.Key}}</td>
|
||||
<td class=val>{{.Value}}</td>
|
||||
<td class=src>{{.Source}}</td>
|
||||
<td>{{printf "%.2f" .Confidence}}</td>
|
||||
</tr>{{end}}
|
||||
</table>
|
||||
@@ -45,6 +45,9 @@ var staticFiles embed.FS
|
||||
//go:embed dash.html
|
||||
var dashHTML string
|
||||
|
||||
//go:embed history.html
|
||||
var historyHTML string
|
||||
|
||||
// dashTmpl — the monitoring read surface, server-rendered from dash.html (no JS,
|
||||
// no client fetch); meta-refresh keeps it live. html/template escapes the user
|
||||
// text in facts/nudges. Read-only: browses the append-only store via CoreAPI,
|
||||
@@ -127,6 +130,9 @@ func main() {
|
||||
mux.HandleFunc("/dash", func(w http.ResponseWriter, r *http.Request) {
|
||||
handleDash(w, r, core)
|
||||
})
|
||||
mux.HandleFunc("/history", func(w http.ResponseWriter, r *http.Request) {
|
||||
handleHistory(w, r, core)
|
||||
})
|
||||
// ----- passkey (WebAuthn) endpoints -----
|
||||
// Wired when both -core and a configured origin are present. The origin
|
||||
// must match the browser's view of mavweb (e.g. https://maven.kvmx.ru).
|
||||
@@ -357,6 +363,28 @@ input[type=text]{width:22rem}code{background:#f4f4f4;padding:.1rem .3rem}
|
||||
{{else}}<p>none enabled.</p>{{end}}
|
||||
`
|
||||
|
||||
var historyTmpl = template.Must(template.New("history").Parse(historyHTML))
|
||||
|
||||
func handleHistory(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) {
|
||||
if core == nil {
|
||||
http.Error(w, "history disabled (no -core)", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
facts, err := core.RecentFacts(ctx, 200)
|
||||
if err != nil {
|
||||
log.Printf("history: %v", err)
|
||||
http.Error(w, "core read failed", http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := historyTmpl.Execute(w, struct {
|
||||
Facts []ipc.Fact
|
||||
}{facts}); err != nil {
|
||||
log.Printf("history render: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// handleTools serves the enable surface (GET) and applies an enable (POST).
|
||||
// POST fields: name, cmd (space-separated argv), destructive (checkbox). cmd is
|
||||
// whitespace-split — argv with embedded spaces isn't supported (ponytail: no
|
||||
|
||||
Reference in New Issue
Block a user