@@ -0,0 +1,28 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
- name: Generate dashboard_gsx.go from dashboard.gsx
|
||||||
|
run: go run github.com/grindlemire/go-tui/cmd/tui generate ./...
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Vet
|
||||||
|
run: go vet ./...
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 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 generate` → `go build` → `go vet`.
|
||||||
@@ -2,7 +2,13 @@ module dashboard
|
|||||||
|
|
||||||
go 1.26.5
|
go 1.26.5
|
||||||
|
|
||||||
|
require github.com/grindlemire/go-tui v0.19.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/grindlemire/go-tui v0.19.0 // indirect
|
golang.org/x/mod v0.32.0 // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
|
golang.org/x/tools v0.41.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
tool github.com/grindlemire/go-tui/cmd/tui
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
github.com/grindlemire/go-tui v0.19.0 h1:4k4MRoYUFChUCfO5Lr4Z05SI3jlTSuH3W1H5/HKVH8s=
|
github.com/grindlemire/go-tui v0.19.0 h1:4k4MRoYUFChUCfO5Lr4Z05SI3jlTSuH3W1H5/HKVH8s=
|
||||||
github.com/grindlemire/go-tui v0.19.0/go.mod h1:95oaPy6Iu6MZpiByUqxYz2Wcbckzvaw3K0jgQeI4dD0=
|
github.com/grindlemire/go-tui v0.19.0/go.mod h1:95oaPy6Iu6MZpiByUqxYz2Wcbckzvaw3K0jgQeI4dD0=
|
||||||
|
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||||
|
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||||
|
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||||
|
|||||||
Reference in New Issue
Block a user