3.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
A terminal dashboard (TUI) built with github.com/grindlemire/go-tui, showing simulated CPU/memory/disk gauges, network sparklines, and a scrolling event feed. Single binary, no external services.
Commands
# Regenerate dashboard_gsx.go from dashboard.gsx (required before build if the .go file is missing/stale)
go run github.com/grindlemire/go-tui/cmd/tui generate ./...
# Build
go build ./...
# Vet
go vet ./...
# Run the app
go run .
There are no test files in this repo yet.
Architecture
The .gsx → .go code generation step
dashboard.gsx is the source of truth for the UI. It's written in .gsx, a JSX-like DSL (Tailwind-esque class strings, {expr} interpolation, for/if control flow, a templ method that compiles to Render) provided by the go-tui module. dashboard_gsx.go is generated output — do not hand-edit it. It's git-ignored and carries a // Code generated by tui generate. DO NOT EDIT. header.
To change the UI: edit dashboard.gsx, then run go run github.com/grindlemire/go-tui/cmd/tui generate ./... to regenerate dashboard_gsx.go. Non-templ Go code (helper funcs, the dashboardApp struct, produceEvents, etc.) also lives in dashboard.gsx — the whole file is the source, not just the template block.
The tui CLI is wired into go.mod as a Go 1.24+ tool dependency (tool github.com/grindlemire/go-tui/cmd/tui), which is why go.sum carries golang.org/x/tools etc. even though the app code doesn't import them directly. Don't go mod tidy this away — it strips the generator's transitive deps and breaks go run .../cmd/tui generate.
CI (.gitea/workflows/build.yml) regenerates dashboard_gsx.go from scratch on every run before building, so the generated file never needs to be committed or kept in sync manually.
App structure (go-tui conventions)
main.gowires uptui.NewApp(tui.WithRootComponent(Dashboard(eventCh)), tui.WithMouse())and runs it. A background goroutine (produceEvents) pushes random events ontoeventCh.dashboardApp(defined indashboard.gsx) is the root component. It implements several go-tui interfaces, each independently optional and picked up via interface satisfaction (see the compile-time checks at the bottom of the generated file):KeyMap() tui.KeyMap— key bindings (q/Esc to quit,j/k/arrows to scroll).HandleMouse(tui.MouseEvent) bool— mouse wheel scroll support.Watchers() []tui.Watcher— background subscriptions: a 500ms timer (updateMetrics, simulates metric drift) and a channel watch (addEvent, consumeseventCh).Render(*tui.App) *tui.Element— the generated view tree (from thetemplblock).BindApp(*tui.App)— generated; wires every*tui.State[T]field to the app so state changes trigger re-renders. If you overrideBindAppyourself, call the generatedbindAppFieldshelper rather than re-listing fields.
- All mutable UI state (
cpu,mem,sparkIn,events,scrollY, ...) is*tui.State[T], read via.Get()and mutated via.Set().eventsRef *tui.Refgives imperative access to the events panel element (used forMaxScroll()/auto-scroll-to-bottom inaddEvent). - Adding a new stateful field to
dashboardAppmeans: add thetui.State/tui.Reffield, initialize it inDashboard(...), use it in thetemplblock — the generator regeneratesbindAppFieldsand the compile-time interface checks automatically.
CI
.gitea/workflows/build.yml runs on push/PR to main: checkout → setup-go (version from go.mod) → tui generate → go build → go vet.