Files
dashboard/CLAUDE.md
T
pitfriedrich 012caf5672
Build / build (push) Successful in 10m3s
Build infra + CLAUDE.md
2026-08-09 18:09:16 +02:00

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.go wires up tui.NewApp(tui.WithRootComponent(Dashboard(eventCh)), tui.WithMouse()) and runs it. A background goroutine (produceEvents) pushes random events onto eventCh.
  • dashboardApp (defined in dashboard.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, consumes eventCh).
    • Render(*tui.App) *tui.Element — the generated view tree (from the templ block).
    • BindApp(*tui.App) — generated; wires every *tui.State[T] field to the app so state changes trigger re-renders. If you override BindApp yourself, call the generated bindAppFields helper 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.Ref gives imperative access to the events panel element (used for MaxScroll()/auto-scroll-to-bottom in addEvent).
  • Adding a new stateful field to dashboardApp means: add the tui.State/tui.Ref field, initialize it in Dashboard(...), use it in the templ block — the generator regenerates bindAppFields and 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 generatego buildgo vet.