panels

package
v0.7.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package panels contains the eight read-only panels rendered by the TUI. Each panel is a self-contained bubbletea sub-model that renders against the shared state owned by the root model.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Emit

func Emit(r state.WriteRequest) tea.Cmd

Emit returns a tea.Cmd that delivers the WriteActionMsg to the root model.

func EmitSystemDetail

func EmitSystemDetail(name string) tea.Cmd

EmitSystemDetail returns a Cmd delivering a SystemDetailMsg.

func EmitTalkgroupDetail

func EmitTalkgroupDetail(id uint32) tea.Cmd

EmitTalkgroupDetail returns a Cmd delivering a TalkgroupDetailMsg.

func RefreshTheme

func RefreshTheme()

RefreshTheme rebuilds the package-level styles from the active theme.Theme(). Safe to call any time; mutates module-level vars so don't call concurrently with a render.

Types

type ActivePanel

type ActivePanel struct {
	// contains filtered or unexported fields
}

ActivePanel renders calls currently being followed.

func NewActive

func NewActive() *ActivePanel

func (*ActivePanel) HandleMouse

func (p *ActivePanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks. After picking a row the `e` keybind ends that call.

func (ActivePanel) Keys

func (ActivePanel) Keys() []key.Binding

func (ActivePanel) Title

func (ActivePanel) Title() string

func (*ActivePanel) Update

func (p *ActivePanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*ActivePanel) View

func (p *ActivePanel) View(width, height int, focused bool, s *state.SharedState) string

type DashboardPanel

type DashboardPanel struct{}

DashboardPanel is the at-a-glance landing screen. It is a pure renderer over SharedState; it owns no local state.

func NewDashboard

func NewDashboard() *DashboardPanel

NewDashboard returns the default landing panel.

func (DashboardPanel) Keys

func (DashboardPanel) Keys() []key.Binding

func (DashboardPanel) Title

func (DashboardPanel) Title() string

func (*DashboardPanel) Update

func (p *DashboardPanel) Update(_ tea.Msg, _ *state.SharedState) (Panel, tea.Cmd)

func (*DashboardPanel) View

func (p *DashboardPanel) View(width, height int, _ bool, s *state.SharedState) string

type DevicesPanel

type DevicesPanel struct {
	// contains filtered or unexported fields
}

DevicesPanel renders the SDR pool snapshot — every dongle the daemon has opened, with role / tuner / configured gain / PPM / bias-tee state.

func NewDevices

func NewDevices() *DevicesPanel

func (*DevicesPanel) HandleMouse

func (p *DevicesPanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks.

func (DevicesPanel) Keys

func (DevicesPanel) Keys() []key.Binding

func (*DevicesPanel) Reveal

func (p *DevicesPanel) Reveal(key string)

Reveal positions the cursor on the row whose Serial matches key.

func (DevicesPanel) Title

func (DevicesPanel) Title() string

func (*DevicesPanel) Update

func (p *DevicesPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*DevicesPanel) View

func (p *DevicesPanel) View(width, height int, focused bool, s *state.SharedState) string

type EventsPanel

type EventsPanel struct {
	// contains filtered or unexported fields
}

EventsPanel renders the rolling SSE event log.

func NewEvents

func NewEvents() *EventsPanel

func (EventsPanel) Keys

func (EventsPanel) Keys() []key.Binding

func (EventsPanel) Title

func (EventsPanel) Title() string

func (*EventsPanel) Update

func (p *EventsPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*EventsPanel) View

func (p *EventsPanel) View(width, height int, focused bool, s *state.SharedState) string

type HistoryPanel

type HistoryPanel struct {
	// contains filtered or unexported fields
}

HistoryPanel renders ended calls from /api/v1/calls/history.

Loading is on-demand: the root model issues the initial fetch when this panel becomes active and again when the user presses `r`. The panel does not poll continuously — history is a stable list and the daemon already paginates it.

Refresh is asynchronous: when the SharedState hash for History changes, Update returns a tea.Cmd that builds the new []table.Row slice off the Update goroutine. The result arrives back as a HistoryRefreshedMsg and is committed only when its hash still matches the latest snapshot — newer results win.

func NewHistory

func NewHistory() *HistoryPanel

NewHistory constructs the panel.

func (*HistoryPanel) HandleMouse

func (p *HistoryPanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks. History is read-only — the cursor just controls which row is highlighted.

func (HistoryPanel) Keys

func (HistoryPanel) Keys() []key.Binding

func (*HistoryPanel) ReloadRequested

func (p *HistoryPanel) ReloadRequested() bool

ReloadRequested returns true when the user pressed `r` since the last call. The root model checks this each Update and issues the reload Cmd as needed.

func (HistoryPanel) Title

func (HistoryPanel) Title() string

func (*HistoryPanel) Update

func (p *HistoryPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*HistoryPanel) View

func (p *HistoryPanel) View(width, height int, focused bool, s *state.SharedState) string

type HistoryRefreshedMsg

type HistoryRefreshedMsg struct {
	Hash uint64
	Rows []table.Row
}

HistoryRefreshedMsg carries the result of an async refresh job. Exported so the root model can route it to the history panel.

type HuntPanel added in v0.3.5

type HuntPanel struct {
	// contains filtered or unexported fields
}

HuntPanel renders the live system-discovery ("hunt") run — its state, sweep/ identify progress, and the discovered system summary — and lets the operator start a run (bands to sweep + an optional name) or stop the active one.

func NewHunt added in v0.3.5

func NewHunt() *HuntPanel

func (HuntPanel) Keys added in v0.3.5

func (HuntPanel) Keys() []key.Binding

func (HuntPanel) Title added in v0.3.5

func (HuntPanel) Title() string

func (*HuntPanel) Update added in v0.3.5

func (p *HuntPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*HuntPanel) View added in v0.3.5

func (p *HuntPanel) View(width, height int, focused bool, s *state.SharedState) string

type ImportCommitMsg added in v0.1.5

type ImportCommitMsg struct {
	ID    string
	Force bool
}

ImportCommitMsg is emitted when the operator presses 'c' on the Preview view.

type ImportDiscardMsg added in v0.1.5

type ImportDiscardMsg struct {
	ID string
}

ImportDiscardMsg is emitted when the operator presses 'd' on the Preview view.

type ImportPanel added in v0.1.5

type ImportPanel struct {
	// contains filtered or unexported fields
}

ImportPanel is the live-import cockpit. Three views:

  1. Stage — operator adds local file paths via a textinput; pressing 'u' uploads them to the daemon and we transition to Preview.
  2. Preview — table of parsed systems returned by the daemon; 'c' commits, 'd' discards, esc returns to Stage.
  3. Result — list of systems_added / systems_replaced; esc returns to Stage.

func NewImport added in v0.1.5

func NewImport() *ImportPanel

NewImport returns a fresh import panel.

func (*ImportPanel) Keys added in v0.1.5

func (*ImportPanel) Keys() []key.Binding

func (*ImportPanel) Title added in v0.1.5

func (*ImportPanel) Title() string

func (*ImportPanel) Update added in v0.1.5

func (p *ImportPanel) Update(msg tea.Msg, shared *state.SharedState) (Panel, tea.Cmd)

func (*ImportPanel) View added in v0.1.5

func (p *ImportPanel) View(width, height int, focused bool, shared *state.SharedState) string

type ImportPreviewArrivedMsg added in v0.1.5

type ImportPreviewArrivedMsg struct {
	Preview client.ImportPreview
	Err     error
}

ImportPreviewArrivedMsg is consumed by the panel when the root model has received the staging response.

type ImportResultArrivedMsg added in v0.1.5

type ImportResultArrivedMsg struct {
	Result client.ImportResult
	Err    error
}

ImportResultArrivedMsg is consumed by the panel when a commit finishes.

type ImportUploadMsg added in v0.1.5

type ImportUploadMsg struct {
	Paths []string
}

ImportUploadMsg is emitted when the operator has confirmed an upload via 'u'. The root model resolves it into an HTTP call.

type MetricsPanel

type MetricsPanel struct {
	// contains filtered or unexported fields
}

MetricsPanel surfaces a curated subset of /metrics. Showing every Prometheus series produced by the daemon would be noisy; this whitelist captures the headline counters operators usually care about.

func NewMetrics

func NewMetrics() *MetricsPanel

func (*MetricsPanel) HandleMouse

func (p *MetricsPanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks. Metrics rows aren't actionable; the cursor is purely for keyboard navigation.

func (MetricsPanel) Keys

func (MetricsPanel) Keys() []key.Binding

func (MetricsPanel) Title

func (MetricsPanel) Title() string

func (*MetricsPanel) Update

func (p *MetricsPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*MetricsPanel) View

func (p *MetricsPanel) View(width, height int, focused bool, s *state.SharedState) string

type MouseAware

type MouseAware interface {
	HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd
}

MouseAware is an optional interface for panels that want to react to mouse events inside their body. localY is the row offset relative to the panel's top-left (0 = panel border row); msg carries the full bubbletea mouse payload so the panel can distinguish a left-click on a data row (move cursor) from a scroll-wheel tick (advance one row) from a button release (no-op). Returning a tea.Cmd is permitted but most implementations will return nil.

type Panel

type Panel interface {
	Title() string
	Keys() []key.Binding
	Update(msg tea.Msg, shared *state.SharedState) (Panel, tea.Cmd)
	View(width, height int, focused bool, shared *state.SharedState) string
}

Panel is the contract every visible panel implements. Update may return a replacement Panel — bubbletea's Elm-style "return next state" pattern. Panels never call the network; they read from shared state populated by the root model's polling Cmds.

type Revealer

type Revealer interface {
	Reveal(key string)
}

Revealer is an optional interface for panels that can pre-position their internal cursor on a specific row when the operator jumps in from the command palette. key is panel-defined: SystemsPanel takes a system name, TalkgroupsPanel a decimal ID, DevicesPanel a serial, ScannerPanel "sys:<name>" or "conv:<idx>".

type ScannerPanel

type ScannerPanel struct {
	// contains filtered or unexported fields
}

ScannerPanel is the police-scanner cockpit. Renders three sections:

  1. Systems (trunked) — per-system CC hunt state + last grant.
  2. Conventional — fixed-frequency analog channels + current dwell.
  3. TG scan summary — global scan_mode + scan-list size.

Operator mutations (hold / resume / retune / dwell / cycle scan mode) ride the existing WriteRequest machinery so the daemon's allow_mutations gate + the TUI's --write flag govern them.

func NewScanner

func NewScanner() *ScannerPanel

NewScanner returns a new read+write scanner cockpit.

func (ScannerPanel) Keys

func (ScannerPanel) Keys() []key.Binding

func (*ScannerPanel) Reveal

func (p *ScannerPanel) Reveal(key string)

Reveal pre-positions the scanner's virtual cursor on a system or conventional channel. key forms:

"sys:<name>"   — trunked system by name
"conv:<index>" — conventional channel by zero-based index

The actual cursor index can't be computed until we see SharedState, so we stash the request and resolve it on the next Update tick.

func (ScannerPanel) Title

func (ScannerPanel) Title() string

func (*ScannerPanel) Update

func (p *ScannerPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*ScannerPanel) View

func (p *ScannerPanel) View(width, height int, focused bool, s *state.SharedState) string

type SettingsErrorMsg added in v0.1.5

type SettingsErrorMsg struct {
	Field   string
	Message string
}

SettingsErrorMsg is dispatched by the root model when a Settings PATCH comes back with an error (validation failure, 503, etc.). The panel re-opens the editor on the failing row and surfaces the message inline so the operator can correct without re-navigating.

type SettingsPanel

type SettingsPanel struct {
	// contains filtered or unexported fields
}

SettingsPanel is the tabbed inspector that surfaces every config knob, output, protocol surface, and runtime fact. Driven by SharedState.Runtime; one /api/v1/runtime poll feeds every tab so the operator sees a coherent snapshot.

func NewSettings

func NewSettings() *SettingsPanel

func (SettingsPanel) Keys

func (SettingsPanel) Keys() []key.Binding

func (SettingsPanel) Title

func (SettingsPanel) Title() string

func (*SettingsPanel) Update

func (p *SettingsPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*SettingsPanel) View

func (p *SettingsPanel) View(width, height int, focused bool, s *state.SharedState) string

type SystemDetailMsg

type SystemDetailMsg struct{ Name string }

SystemDetailMsg is emitted by the Systems panel when the operator presses Enter on a row. The root model fetches the detail record via GET /api/v1/systems/{name} and renders a read-only modal.

type SystemsPanel

type SystemsPanel struct {
	// contains filtered or unexported fields
}

SystemsPanel renders the configured trunking systems.

func NewSystems

func NewSystems() *SystemsPanel

func (*SystemsPanel) Cursor

func (p *SystemsPanel) Cursor() int

Cursor exposes the current table cursor index for tests.

func (*SystemsPanel) HandleMouse

func (p *SystemsPanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks. Chrome clicks are ignored.

func (SystemsPanel) Keys

func (SystemsPanel) Keys() []key.Binding

func (*SystemsPanel) Reveal

func (p *SystemsPanel) Reveal(key string)

Reveal positions the cursor on the row whose Name matches the given key. No-op when the row is absent; the operator still ends up on the panel.

func (SystemsPanel) Title

func (SystemsPanel) Title() string

func (*SystemsPanel) Update

func (p *SystemsPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*SystemsPanel) View

func (p *SystemsPanel) View(width, height int, focused bool, s *state.SharedState) string

type TalkgroupDetailMsg

type TalkgroupDetailMsg struct{ ID uint32 }

TalkgroupDetailMsg is emitted by the Talkgroups panel when the operator presses Enter on a row.

type TalkgroupsPanel

type TalkgroupsPanel struct {
	// contains filtered or unexported fields
}

TalkgroupsPanel renders all configured talkgroups with a substring filter and sort cycle.

func NewTalkgroups

func NewTalkgroups() *TalkgroupsPanel

func (*TalkgroupsPanel) FilterValue

func (p *TalkgroupsPanel) FilterValue() string

FilterValue exposes the current filter for testing.

func (*TalkgroupsPanel) HandleMouse

func (p *TalkgroupsPanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks. The first body line is reserved for the filter input + sort summary, so chromeRows == 1.

func (TalkgroupsPanel) Keys

func (TalkgroupsPanel) Keys() []key.Binding

func (*TalkgroupsPanel) Reveal

func (p *TalkgroupsPanel) Reveal(key string)

Reveal positions the cursor on the row whose ID (decimal string in the first column) matches key. Used by the command palette to land the operator on the talkgroup they searched for.

func (*TalkgroupsPanel) RowCount

func (p *TalkgroupsPanel) RowCount() int

RowCount returns the number of rows currently in the table.

func (*TalkgroupsPanel) SetFilterValue

func (p *TalkgroupsPanel) SetFilterValue(v string)

SetFilterValue sets the filter from a test harness without keystroke simulation.

func (TalkgroupsPanel) Title

func (TalkgroupsPanel) Title() string

func (*TalkgroupsPanel) Update

func (p *TalkgroupsPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*TalkgroupsPanel) View

func (p *TalkgroupsPanel) View(width, height int, focused bool, s *state.SharedState) string

type ThemeChangedMsg

type ThemeChangedMsg struct{}

ThemeChangedMsg is broadcast by the root model after a palette swap. Panels that cache lipgloss styles (chiefly the bubbles/table-backed ones, which call SetStyles in their constructor) handle it by re-applying tableStyles() so the new palette takes effect on the next render. Read-only panels that fetch theme.Theme() on every View() can ignore it.

type TonesPanel

type TonesPanel struct {
	// contains filtered or unexported fields
}

TonesPanel renders the tone-alert ring buffer.

func NewTones

func NewTones() *TonesPanel

func (*TonesPanel) HandleMouse

func (p *TonesPanel) HandleMouse(msg tea.MouseMsg, localY int) tea.Cmd

HandleMouse moves the cursor on a left-click and forwards wheel ticks. After picking a row the `R` keybind resets that device's tone-detector.

func (TonesPanel) Keys

func (TonesPanel) Keys() []key.Binding

func (TonesPanel) Title

func (TonesPanel) Title() string

func (*TonesPanel) Update

func (p *TonesPanel) Update(msg tea.Msg, s *state.SharedState) (Panel, tea.Cmd)

func (*TonesPanel) View

func (p *TonesPanel) View(width, height int, focused bool, s *state.SharedState) string

type WriteActionMsg

type WriteActionMsg struct{ Request state.WriteRequest }

WriteActionMsg is what panels emit to ask the root model to run a mutation. The root catches this exact type, so panels stay decoupled from the root's modal mechanics and HTTP client.

Exported so tests in other packages can assert on it; intended to be opaque to consumers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL