From 2689715c2db5ebc6ed82e519540cf4d4969c0836 Mon Sep 17 00:00:00 2001 From: claude Date: Tue, 4 Aug 2026 05:29:00 +0400 Subject: [PATCH] mavweb: the last three page templates leave main.go (V-409) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /tools, /routines and /chat were the only pages whose markup still lived in a Go string constant. They are tools.html, routines.html and chat.html now, embedded exactly like the eight that already were, so no page markup is left in Go and the "HTML in Go" complaint is answered with no framework, no build step and no second artifact. routineRow/routineRows are routineView/toRoutineViews. The pattern is right — it maps wire structs to display structs so a template never formats an interval or a timestamp — but "rows" read like database rows when these are view models. Checked the other half of that review thread while renaming: handleRoutines calls the mapper once and formats nothing itself, so there is no duplicated work between the handler and it. Content is verbatim. htmx is deliberately not added here; per the task it comes later and only where a page wants partial updates. --- cmd/mavweb/chat.html | 24 +++++++ cmd/mavweb/main.go | 141 ++++++--------------------------------- cmd/mavweb/routines.html | 24 +++++++ cmd/mavweb/tools.html | 58 ++++++++++++++++ 4 files changed, 127 insertions(+), 120 deletions(-) create mode 100644 cmd/mavweb/chat.html create mode 100644 cmd/mavweb/routines.html create mode 100644 cmd/mavweb/tools.html diff --git a/cmd/mavweb/chat.html b/cmd/mavweb/chat.html new file mode 100644 index 0000000..b04f0d4 --- /dev/null +++ b/cmd/mavweb/chat.html @@ -0,0 +1,24 @@ +{{template "shellTop" "chat"}} +

Chat

+
+{{if .Error}}
{{.Error}}
{{end}} +
+{{range .Messages}} +
{{if eq .Role "user"}}you{{else}}maven{{end}}: {{.Text}}
+{{else}} +
+ +
start a conversation
+
+{{end}} +
+
+ + +
+
+ +{{template "shellBottom"}} diff --git a/cmd/mavweb/main.go b/cmd/mavweb/main.go index e6c8c0c..69bcf58 100644 --- a/cmd/mavweb/main.go +++ b/cmd/mavweb/main.go @@ -75,6 +75,15 @@ var morningHTML string //go:embed events.html var eventsHTML string +//go:embed tools.html +var toolsHTML string + +//go:embed routines.html +var routinesHTML string + +//go:embed chat.html +var chatPageHTML string + // shellHTML — the shell partial every page is wrapped in: "shellTop", the // "sidebar" it calls, and "shellBottom". It used to be two Go string constants // with the sidebar assembled by a strings.Builder, which is the one piece of @@ -672,92 +681,6 @@ var toolsTmpl = template.Must(template.New("tools").Funcs(func() template.FuncMa return m }()).Parse(shellHTML + toolsHTML)) -const toolsHTML = `{{template "shellTop" "tools"}} -

Tools

-

enabling requires step-up — assert a passkey first.

-{{if .Msg}}
{{.Msg}}
{{end}} -
-

proposed {{len .Proposed}}

-{{if .Proposed}}

maven drafted these from acts she couldn't run. Fill the command (argv, space-separated) and enable. A row in an mcp: scope came from an MCP server and already knows what it calls — check the command, then enable.

-
-{{range .Proposed}} - - -{{end}}
namescopefrom utteranceenable as
{{.Name}}{{.Scope}}{{.Utterance}}
- - - - - -
-
- - -
-{{else}}
- -
no proposed tools
-
maven will propose tools here when she needs help running an action
-
{{end}} -
-
-

enabled {{len .Enabled}}

-{{if .Enabled}}
-{{range .Enabled}} - -{{end}}
namescopecommand
{{.Name}}{{.Scope}}{{join .Cmd " "}}{{if .Destructive}}destructive{{end}}
- - - -
-{{else}}
- -
no tools enabled
-
enable proposed tools above, or ask maven to configure one
-
{{end}} -
-
-

MCP servers {{len .MCP}}

-{{if .MCP}}

servers she connects OUT to. Their tools appear above as proposals — a configured server is a place she may look, not a capability she has. A stdio target is a process on this box; an http one on a loopback or LAN address is inside the network, so treat its tools accordingly.

-
-{{range .MCP}} - -{{end}}
nametransporttargetstatetools
{{.Name}}{{.Transport}}{{.Target}}{{if .Connected}}connected{{if .Server}} — {{.Server}}{{end}}{{else}}down{{if .Err}} — {{.Err}}{{end}}{{end}}{{.Tools}}
-{{else}}
- -
no MCP servers configured
-
add an mcp.servers block to mavend.json to let her use an external tool server
-
{{end}} -
-{{template "shellBottom"}}` - -// routinesHTML — proposed routine review surface. One row per thing maven -// noticed, in her words, with at most two actions: accept or dismiss. -const routinesHTML = `{{template "shellTop" "routines"}} -

Routines

-{{if .Msg}}
{{.Msg}}
{{end}} -
-

noticed {{len .Proposed}}

-{{if .Proposed}}
-{{range .Proposed}} - - - -{{end}}
maven noticedwhen
{{.Phrase}}{{.Noticed}}
- - -
- - -
-{{else}}
- -
no proposed routines
-
maven will propose routines here when she detects a recurring pattern
-
{{end}} -
-{{template "shellBottom"}}` - var historyTmpl = template.Must(template.New("history").Funcs(shellFuncs()).Parse(shellHTML + historyHTML)) var notificationsTmpl = template.Must(template.New("notifications").Funcs(shellFuncs()).Parse(shellHTML + notificationsHTML)) @@ -770,6 +693,8 @@ var voiceTmpl = template.Must(template.New("voice").Funcs(shellFuncs()).Parse(sh var tasksTmpl = template.Must(template.New("tasks").Funcs(shellFuncs()).Parse(shellHTML + tasksHTML)) +// routinesTmpl — the proposed-routine review surface. One row per thing maven +// noticed, in her words, with at most two actions: accept or dismiss. var routinesTmpl = template.Must(template.New("routines").Funcs(shellFuncs()).Parse(shellHTML + routinesHTML)) var traceTmpl = template.Must(template.New("trace").Funcs(func() template.FuncMap { @@ -1095,9 +1020,10 @@ func fmtTaskDate(t *time.Time) string { return t.Local().Format("02 Jan") } -// routineRow is one line on the page: what maven noticed, in her words, and -// how long ago she noticed it. -type routineRow struct { +// routineView is one line on the page: what maven noticed, in her words, and +// how long ago she noticed it. A view model, not a database row — the template +// never formats an interval or a timestamp itself. +type routineView struct { ID int64 Phrase string Noticed string @@ -1159,23 +1085,23 @@ func handleRoutines(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI, se w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := routinesTmpl.Execute(w, struct { Msg string - Proposed []routineRow - }{msg, routineRows(proposed)}); err != nil { + Proposed []routineView + }{msg, toRoutineViews(proposed)}); err != nil { log.Printf("routines render: %v", err) } } -// routineRows turns the wire rows into display rows. The phrase comes from +// toRoutineViews turns the wire rows into view models. The phrase comes from // pattern.PhraseRoutine so the page says the same thing maven's voice says. -func routineRows(rs []ipc.ProposedRoutine) []routineRow { - out := make([]routineRow, 0, len(rs)) +func toRoutineViews(rs []ipc.ProposedRoutine) []routineView { + out := make([]routineView, 0, len(rs)) for _, r := range rs { p := pattern.ProposedRoutine{Action: r.Action, Object: r.Object, IntervalDays: r.IntervalDays} noticed := "just now" if r.CreatedTs > 0 { noticed = time.Since(time.UnixMilli(r.CreatedTs)).Round(time.Minute).String() + " ago" } - out = append(out, routineRow{ID: r.ID, Phrase: pattern.PhraseRoutine(&p), Noticed: noticed}) + out = append(out, routineView{ID: r.ID, Phrase: pattern.PhraseRoutine(&p), Noticed: noticed}) } return out } @@ -1574,31 +1500,6 @@ func handlePTT(w http.ResponseWriter, r *http.Request, voiceAddr string, session // and the handler redirects back to /chat with the response. var chatTmpl = template.Must(template.New("chat").Funcs(shellFuncs()).Parse(shellHTML + chatPageHTML)) -const chatPageHTML = `{{template "shellTop" "chat"}} -

Chat

-
-{{if .Error}}
{{.Error}}
{{end}} -
-{{range .Messages}} -
{{if eq .Role "user"}}you{{else}}maven{{end}}: {{.Text}}
-{{else}} -
- -
start a conversation
-
-{{end}} -
-
- - -
-
- -{{template "shellBottom"}}` - // handleChatPage renders the chat conversation page. func handleChatPage(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) { if core == nil { diff --git a/cmd/mavweb/routines.html b/cmd/mavweb/routines.html new file mode 100644 index 0000000..1262624 --- /dev/null +++ b/cmd/mavweb/routines.html @@ -0,0 +1,24 @@ +{{template "shellTop" "routines"}} +

Routines

+{{if .Msg}}
{{.Msg}}
{{end}} +
+

noticed {{len .Proposed}}

+{{if .Proposed}}
+{{range .Proposed}} + + + +{{end}}
maven noticedwhen
{{.Phrase}}{{.Noticed}}
+ + +
+ + +
+{{else}}
+ +
no proposed routines
+
maven will propose routines here when she detects a recurring pattern
+
{{end}} +
+{{template "shellBottom"}} diff --git a/cmd/mavweb/tools.html b/cmd/mavweb/tools.html new file mode 100644 index 0000000..7805d65 --- /dev/null +++ b/cmd/mavweb/tools.html @@ -0,0 +1,58 @@ +{{template "shellTop" "tools"}} +

Tools

+

enabling requires step-up — assert a passkey first.

+{{if .Msg}}
{{.Msg}}
{{end}} +
+

proposed {{len .Proposed}}

+{{if .Proposed}}

maven drafted these from acts she couldn't run. Fill the command (argv, space-separated) and enable. A row in an mcp: scope came from an MCP server and already knows what it calls — check the command, then enable.

+
+{{range .Proposed}} + + +{{end}}
namescopefrom utteranceenable as
{{.Name}}{{.Scope}}{{.Utterance}}
+ + + + + +
+
+ + +
+{{else}}
+ +
no proposed tools
+
maven will propose tools here when she needs help running an action
+
{{end}} +
+
+

enabled {{len .Enabled}}

+{{if .Enabled}}
+{{range .Enabled}} + +{{end}}
namescopecommand
{{.Name}}{{.Scope}}{{join .Cmd " "}}{{if .Destructive}}destructive{{end}}
+ + + +
+{{else}}
+ +
no tools enabled
+
enable proposed tools above, or ask maven to configure one
+
{{end}} +
+
+

MCP servers {{len .MCP}}

+{{if .MCP}}

servers she connects OUT to. Their tools appear above as proposals — a configured server is a place she may look, not a capability she has. A stdio target is a process on this box; an http one on a loopback or LAN address is inside the network, so treat its tools accordingly.

+
+{{range .MCP}} + +{{end}}
nametransporttargetstatetools
{{.Name}}{{.Transport}}{{.Target}}{{if .Connected}}connected{{if .Server}} — {{.Server}}{{end}}{{else}}down{{if .Err}} — {{.Err}}{{end}}{{end}}{{.Tools}}
+{{else}}
+ +
no MCP servers configured
+
add an mcp.servers block to mavend.json to let her use an external tool server
+
{{end}} +
+{{template "shellBottom"}}