Documentation
¶
Overview ¶
Package ux is the bubbletea front-end for genspec-tui: a single root Model composing a header line, three panels (source tree, spec, diagnostics), and a status/help line.
Structure borrows from fredbi/git-janitor — one root model owning panel values, an enum-based key dispatch, mouse focus/scroll, and a recalcLayout that distributes the terminal size across panels.
What lives where ¶
The panels (panels) and the modal overlays (help, options) are separate packages because each owns its own state and needs almost nothing from the model. What is left here does not split that way: the cross-reference layer alone reaches into all three panels, both spec indexes, the source index and the status line, so it is spread across files rather than hidden behind a package boundary it could not honestly keep.
- model.go the Model struct, its lifecycle (New/Close/Init/Update/View), layout and focus routing
- keys.go key dispatch: global bindings, per-pane handlers, the search input, the editor
- crossref.go follow modes, go-to-definition, find-references, gutters, the spec render
- refcycle.go the find-references walk, as its own small type
- source.go the open file: loading, saving, syntax runs, diagnostic marks, buffer coordinates
- scanflow.go running a scan and absorbing its result, the file watcher's debounce, transient notices
- chrome.go the header, status line and follow badge
- mouse.go click-to-focus and wheel scrolling
- overlay.go the Overlay contract the modals satisfy, and their precedence
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the root bubbletea model.
func New ¶
New builds the root model around a ready-made scan config; the source tree browses cfg.WorkDir.
Taking the whole Options rather than a handful of arguments means a new CLI flag needs no signature change here — and the boolean knobs the overlay drives are the same struct the caller filled in.
A file watcher is started best-effort — if it can't initialize, live reload is simply unavailable and the user falls back to `r` (manual rescan).
func (*Model) Close ¶
func (m *Model) Close()
Close releases the file watcher.
Call after the program exits.
func (*Model) Init ¶
Init implements tea.Model: kick off the initial whole-scope scan and, if a watcher is available, begin listening for source changes.
type Overlay ¶ added in v0.36.3
type Overlay interface {
SetSize(w, h int)
IsOpen() bool
HandleKey(msg tea.KeyMsg) tea.Cmd
View() string
}
Overlay is a modal layer: it covers the base UI, captures every key until it dismisses itself, and renders a self-contained box the model draws over everything else.
Like the panels, an overlay is a concrete type the model owns and drives — it is deliberately NOT a tea.Model. An overlay shaped as a tea.Model has to be handed the root model just to hand it back, and ends up deciding app policy (quitting) on the root's behalf. The panels never needed that, and neither do these.
Whatever an overlay wants to happen when it closes, it records; the model decides what applying it means.
type RefCycle ¶ added in v0.36.3
type RefCycle struct {
Anchor string // the definition pointer whose uses are being cycled
Sites []index.RefSite // its reference sites, ordered by rendered line
Cursor int // which site we are parked on
Status string // persistent status line while a cycle is active
}
RefCycle is the find-references walk (F3 / shift+F3): the definition whose uses are being visited, its reference sites in rendered-line order, and which one the cursor is parked on.
It is valid only for the render it was computed against — every line number in it is a line of THAT render — so a rescan or a format toggle drops it.
func (*RefCycle) Describe ¶ added in v0.36.3
Describe renders the status line: which site of how many, of what, pointing where.
func (*RefCycle) ParkedOn ¶ added in v0.36.3
ParkedOn reports whether a cycle is under way and line is still the site it last moved to.
This is what makes "F3 repeatedly" walk one definition's uses: move the cursor off the site and the next step re-anchors on the node you are now on, rather than chasing the definition of whatever it last landed on.
func (*RefCycle) Reset ¶ added in v0.36.3
func (c *RefCycle) Reset()
Reset drops the cycle.
Called whenever the render it was computed against is replaced (rescan, format toggle) or the user moves on.
func (*RefCycle) Site ¶ added in v0.36.3
Site is the reference site the cycle is currently parked on.
type ScanState ¶ added in v0.36.3
type ScanState struct {
Running bool
Spin spinner.Model
Elapsed time.Duration
NumPaths int
NumDefs int
JSON string
YAML string
Diags []grammar.Diagnostic
Err error // hard error from the last codescan.Run, shown in the diag pane
}
ScanState is what the last scan produced, plus whether another is in flight.
It has exactly two writers — startScan, when one begins, and absorbScan, when its result lands. Everything else only reads it: the header and status line are projections of it, and the cross-ref layer renders from its JSON or YAML. Keeping it in one struct is what makes that one-way traffic visible.
func NewScanState ¶ added in v0.36.3
func NewScanState() ScanState
NewScanState builds the idle state, with the spinner the header shows while a scan runs.
type SearchBox ¶ added in v0.36.3
type SearchBox struct {
// contains filtered or unexported fields
}
SearchBox is the spec-pane search prompt: the text input, and whether it currently holds the keyboard.
While it is active it takes the status line and every key, which is why "is the user typing a query" has to be answerable from outside — but nothing outside needs to know how the input works.
func NewSearchBox ¶ added in v0.36.3
func NewSearchBox() SearchBox
NewSearchBox builds a closed search prompt.
func (*SearchBox) Close ¶ added in v0.36.3
func (s *SearchBox) Close()
Close dismisses the prompt, releasing the keyboard.
func (*SearchBox) Open ¶ added in v0.36.3
Open clears the prompt and gives it the keyboard.
Always from empty: a search is started to look for something, not to resume the last one.
type SourceWatch ¶ added in v0.36.3
type SourceWatch struct {
// contains filtered or unexported fields
}
SourceWatch is the live-reload plumbing: the file watcher, the channel its events arrive on, and the debounce generation that lets a newer change discard a timer already in flight.
func NewSourceWatch ¶ added in v0.36.3
func NewSourceWatch(dir string) SourceWatch
NewSourceWatch starts a watcher on dir, best-effort.
If it cannot initialize, live reload is simply unavailable and the user falls back to `r` (manual rescan) — so the error is deliberately swallowed rather than failing the whole TUI.
func (*SourceWatch) Bump ¶ added in v0.36.3
func (s *SourceWatch) Bump() int
Bump opens a new debounce window, invalidating any timer already running.
func (*SourceWatch) Close ¶ added in v0.36.3
func (s *SourceWatch) Close()
Close releases the watcher.
func (*SourceWatch) Current ¶ added in v0.36.3
func (s *SourceWatch) Current(gen int) bool
Current reports whether gen is still the newest debounce window — false means a later change superseded it.
func (*SourceWatch) Events ¶ added in v0.36.3
func (s *SourceWatch) Events() <-chan struct{}
Events is the channel a change arrives on.
func (*SourceWatch) Listening ¶ added in v0.36.3
func (s *SourceWatch) Listening() bool
Listening reports whether a watcher came up and events can be waited on.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package gadgets holds small, self-contained TUI helpers.
|
Package gadgets holds small, self-contained TUI helpers. |
|
Package help is the keymap overlay: a scrollable modal listing every binding, grouped by the context it applies in.
|
Package help is the keymap overlay: a scrollable modal listing every binding, grouped by the context it applies in. |
|
Package key normalizes tea.KeyMsg values into a small enum of named bindings, so the model dispatches on a plain string switch rather than a key-binding library.
|
Package key normalizes tea.KeyMsg values into a small enum of named bindings, so the model dispatches on a plain string switch rather than a key-binding library. |
|
Package options is the scanner-options overlay: a scrollable modal of boolean toggles bound directly to the codescan.Options the app scans with.
|
Package options is the scanner-options overlay: a scrollable modal of boolean toggles bound directly to the codescan.Options the app scans with. |
|
Package panels holds the three scrollable sub-panels of the genspec-tui layout: the source tree (left), the generated spec (right) and the diagnostics (bottom).
|
Package panels holds the three scrollable sub-panels of the genspec-tui layout: the source tree (left), the generated spec (right) and the diagnostics (bottom). |
|
Package theme holds the lipgloss styles shared by the model and its panels: a rounded-border panel box (bright when focused, dim otherwise), a panel title, and the status line.
|
Package theme holds the lipgloss styles shared by the model and its panels: a rounded-border panel box (bright when focused, dim otherwise), a panel title, and the status line. |