feat(web): ambient line in state envelope + /ambient/snooze

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 21:46:45 -04:00
parent ef845ce463
commit fa178a1fd2
2 changed files with 61 additions and 1 deletions
+33 -1
View File
@@ -14,6 +14,7 @@ import (
"time"
"keel/internal/harness"
"keel/internal/mode"
"keel/internal/mode/focus/statemachine"
"keel/internal/settings"
@@ -28,6 +29,9 @@ type Server struct {
h *harness.Harness
bcast *Broadcaster
ambientLine func() string
ambientSnooze func(time.Duration)
mu sync.Mutex
timer *time.Timer
@@ -77,14 +81,42 @@ func (s *Server) Router() *gin.Engine {
r.GET("/settings", s.handleGetSettings)
r.POST("/settings", s.handlePostSettings)
r.GET("/fs/browse", s.handleBrowse)
r.POST("/ambient/snooze", s.handleAmbientSnooze)
return r
}
func (s *Server) stateJSON() string {
data, _ := json.Marshal(s.h.State())
line := ""
if s.ambientLine != nil {
line = s.ambientLine()
}
payload := struct {
mode.Envelope
Ambient string `json:"ambient"`
}{Envelope: s.h.State(), Ambient: line}
data, _ := json.Marshal(payload)
return string(data)
}
// SetAmbient injects the ambient sentinel's line accessor and snooze action,
// wired by main. Called once at startup before serving.
func (s *Server) SetAmbient(line func() string, snooze func(time.Duration)) {
s.ambientLine = line
s.ambientSnooze = snooze
}
// Broadcast pushes the current state envelope to SSE subscribers. Exposed so the
// ambient sentinel's change signal can refresh the UI alongside harness changes.
func (s *Server) Broadcast() { s.broadcast() }
// handleAmbientSnooze mutes the ambient coach for an hour.
func (s *Server) handleAmbientSnooze(c *gin.Context) {
if s.ambientSnooze != nil {
s.ambientSnooze(time.Hour)
}
c.Status(http.StatusNoContent)
}
func (s *Server) broadcast() {
s.bcast.Publish(s.stateJSON())
}