Documentation
¶
Overview ¶
Package gotermboard draws a self-refreshing terminal board on Bubble Tea v2: a tab per view, folding groups on shared columns, a side panel that follows the cursor, and one action table the keys, the footer and the ? overlay all read.
A view is a read model, as in CQRS. Its reader joins sources into a snapshot, and its renderer turns the snapshot into a Frame this package draws. A failed read never gives up a good value: a Source keeps its last good read and says how old it is.
Index ¶
- Constants
- func Ago(d time.Duration) string
- func EditorCommand(path string) *exec.Cmd
- func LastLine(out []byte, err error) string
- func Once(board Board, pal gotermstyle.Palette, width, height int) string
- func OnceSize(fallbackWidth, fallbackHeight int) (width, height int)
- func OpenWindow(name, dir string, argv []string, needs ...string) error
- func RefreshAll(ctx context.Context, sources ...Refresher)
- func SpinnerAt(now time.Time) string
- func TableKeys() []string
- type Action
- type Board
- type BusyBoard
- type Cell
- type Column
- type CursorBoard
- type Document
- type Frame
- type Group
- type HandedBack
- type Keyed
- type Line
- type Model
- func (m *Model) Ask(q Question)
- func (m *Model) CloseInPlace() bool
- func (m *Model) Cursor() string
- func (m *Model) HalfScreen() int
- func (m *Model) HandOver(cmd *exec.Cmd, then func(m *Model, err error, stderr []byte) tea.Cmd) tea.Cmd
- func (m *Model) Init() tea.Cmd
- func (m *Model) MoveCursorTo(key string) bool
- func (m *Model) Read(title string, document Document)
- func (m *Model) Rebuild()
- func (m *Model) Restart() tea.Cmd
- func (m *Model) Reveal()
- func (m *Model) Say(text string)
- func (m *Model) ScrollBy(lines int)
- func (m *Model) Tall(document Document) bool
- func (m *Model) Toggle() tea.Cmd
- func (m *Model) ToggleEveryRow() tea.Cmd
- func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd)
- func (m *Model) View() tea.View
- type MovingBoard
- type Question
- type Reader
- type ReceivingBoard
- type Refresher
- type Row
- type RuleWeight
- type Source
- type Status
- type Tab
- type Tabs
- type TailBoard
- type TwoLineBoard
Constants ¶
const ( CueGlyph = "•" CueCode = BoldCyan CueFor = 30 * time.Second )
The cue a row or group wears after it changed. It sits in the pointer's column, so no column moves for it.
const ( Red = gotermstyle.CodeRed Yellow = gotermstyle.CodeYellow Green = gotermstyle.CodeGreen Cyan = gotermstyle.CodeCyan Blue = gotermstyle.CodeBlue Magenta = gotermstyle.CodeMagenta White = gotermstyle.CodeWhite Bold = gotermstyle.CodeBold BoldRed = "\033[1;91m" BoldYellow = "\033[1;93m" BoldGreen = "\033[1;92m" BoldCyan = "\033[1;96m" BoldBlue = "\033[1;94m" BoldMagenta = "\033[1;95m" Reverse = "\033[7m" )
The codes a view colors a cell with, all in ANSI 0-15 so they follow the theme.
const ( MarkGlyph = "◆" MarkCode = BoldMagenta )
The mark a Marked row draws.
const PanelFrom = 160
PanelFrom is the narrowest screen that gets the side panel.
const ReadingWidth = 100
ReadingWidth is the widest a reader lays text out, because a longer line is hard to follow back to its start. A wider screen keeps the rest as margin. A view that lays the same text out in its table reads this, so the two wrap alike.
const RuleCode = "\033[0;90m"
RuleCode is the gray a rule draws in, which the terminal palette's theme sets.
const SpinEvery = 100 * time.Millisecond
SpinEvery is how long a spinner holds each drawing: the footer's beside work under way, and one a board turns on its own clock.
Variables ¶
This section is empty.
Functions ¶
func EditorCommand ¶ added in v0.22.0
EditorCommand is the user's editor on path: $VISUAL, then $EDITOR, then vi, split on spaces, so EDITOR="code --wait" works. A board's key hands it the terminal with Model.HandOver, as the Reader's e does with tea.ExecProcess.
func LastLine ¶ added in v0.30.0
LastLine is the last line a failed command wrote, or how it ended where it wrote nothing.
func Once ¶
func Once(board Board, pal gotermstyle.Palette, width, height int) string
Once is one frame at a size, drawn from what the board's sources already hold.
func OnceSize ¶
OnceSize is the size one printed frame draws at: COLUMNS by LINES, then the terminal's own size, then the fallback. It is printedframe.Size.
func OpenWindow ¶ added in v0.30.0
OpenWindow runs argv in a new tmux window named name, in dir where dir is not empty. tmux makes the window current, and the board keeps running in its own. tmux runs several arguments with no shell, so none needs quoting, and runs a lone one through its default-shell.
tmux opens the window whether or not its program starts, and in another directory where dir is gone, so tmux, argv[0] and every program in needs are looked for on PATH first, and dir is looked for too. A lone argument holding a space is a shell command rather than a program, so needs names what it runs. Outside tmux it opens nothing, because tmux would open the window in whichever session it last used.
func RefreshAll ¶
RefreshAll reads every source once, all at once, and returns when each ended.
func SpinnerAt ¶ added in v0.23.0
SpinnerAt is the spinner's drawing at now, one cell wide, for a board that turns one on its own clock, such as a MovingBoard's strip while a read runs. It turns through the footer's drawings at the footer's pace.
func TableKeys ¶ added in v0.19.0
func TableKeys() []string
TableKeys is every key the built-in table binds, and tab and shift+tab, which Tabs binds where it shows more than one board. A board's key hides the table's, so a board taking keys from its own config asks this which ones would take a key from the table.
Types ¶
type Action ¶
type Action struct {
Keys []string
Label string
Run func(*Model) tea.Cmd
// Applies says whether this view can use the key now. Nil means always.
// A board's key hides the table's action for it even while Applies is false.
Applies func(*Model) bool
}
Action is one row of the key table the handler, the footer and the ? overlay all read.
type Board ¶
type Board interface {
Sources() []Refresher
Groupings() []string
Frame(now time.Time, grouping string) Frame
}
Board is one view, as this package draws it: sources to keep fresh, and the frame their last good reads make now.
type BusyBoard ¶ added in v0.21.0
type BusyBoard interface {
UnderWay() string
}
BusyBoard is a Board with work under way, such as a command its key started. The footer names it after a spinner, above what the last key did, until UnderWay is empty again. A key never clears it.
type Cell ¶
type Cell struct {
Text string
Code string
// Role is the TUI theme role the cell draws in, such as syntax.keyword, with Code's
// attributes over it. Where a board or a Reader paints a Document's lines, the role resolves
// in its live theme, so a reload recolors it. Line.Paint resolves it in the built-in theme,
// and a row's own cells draw their Code alone.
Role string
Ticks bool
// Section says this cell starts the words of a heading, where a Reader's [ and ] land, and
// how deep the heading sits: 1 is outermost, and 0 is no heading. A heading closes every
// open one at its depth or deeper, which is the path a Reader's top line draws.
Section int
// contains filtered or unexported fields
}
Cell is text and its color. Ticks says the text follows the clock, as an age does, so its changing is no change to the row, though its color changing still is.
type Column ¶
type Column struct {
Title string
// Min holds the column at least this wide, for what a group's Beside fits into.
Min int
Max int
Right bool
}
Column is one column of the table. The last is unpadded, so the screen's edge cuts it first. Max caps its width, where one long value must not take the row.
type CursorBoard ¶ added in v0.3.0
type CursorBoard interface {
CursorOn(key string)
}
CursorBoard is a Board told which key the cursor is on, for a read that follows it.
type Document ¶ added in v0.4.0
Document is text that lays itself out at a width, for what only the draw can place: hanging bullets, tables and boxes of code. Its lines are drawn as they come.
type Frame ¶
type Frame struct {
Strip []Line
// Charts is drawn under the strip at the screen's width, for what has to be laid out at the
// width it gets, as a chart is. It is handed the screen's height too, so a board can keep it to
// a share of a short screen. A moving strip leaves it as the frame drew it.
Charts func(width, height int) []Line
Columns []Column
Groups []Group
// StartAt is the key the cursor starts on, where that key is showing. Without one it
// starts on the first line holding cells, or the last on a TailBoard, past any group with
// no panel text. With no such line it starts on the first line with text to read.
StartAt string
}
Frame is one screen of a view, before it is laid out to a size.
type Group ¶
type Group struct {
Key string
Title Line
// Count is drawn after the title as " · n", where Counted says to.
Count int
Counted bool
// Cells draw on the columns past the first, which holds the title, so a group's
// totals line up with the rows under it.
Cells []Cell
// WrapLast is the last column as Row.WrapLast has it.
WrapLast Line
// Below are lines drawn under the header whether the group is folded or not. The
// cursor moves past them as one node.
Below []Line
// Span draws whole past the title on the header's line, wrapping under itself, so a
// long value never sets the first column's width. The cells then take a line of their
// own beneath it, and Beside sits ahead of them in the first column.
Span Line
// Beside is what the first column holds on a Span group's line of cells, under the
// Span's start. An Items line there is cut to fit.
Beside Line
// Under is the group's summary, wrapping on a └─ row under Below while those are on.
Under Cell
// Document draws under Below whether the group is folded or not, so a view can open
// a group's text in place. The cursor moves past it too.
Document Document
// Summary is drawn past the title while the group is folded.
Summary Line
Folded bool
// Gap draws a blank line after the group.
Gap bool
// Rule draws a thin line after the group, in place of Gap's blank one.
Rule bool
// BlankAboveRule draws a blank line between the group's last line and its Rule.
BlankAboveRule bool
// Tone is the code the group's overline and underline draw in, where not the rule gray.
Tone string
// Overline draws a line across the table above the header.
Overline RuleWeight
// Underline draws a line across the table under the header, ahead of Below.
Underline RuleWeight
// Still keeps the header open with no fold mark, and the cursor passes over it. At
// the top of a frame it stays on screen with the column titles while the table scrolls.
Still bool
// Heading says the title only labels what sits under it, so the filter looks past it.
Heading bool
// FilterOnly keeps the group out of the table until a filter is typed, so a search
// reaches past what a view shows.
FilterOnly bool
Rows []Row
Groups []Group
Detail []Cell
// Panel is what the side panel shows in Detail's place, as Row.Panel has it.
Panel Document
// Text is more the filter matches beyond what the group draws.
Text string
// contains filtered or unexported fields
}
Group is a header carrying its count, with rows and groups under it.
type HandedBack ¶ added in v0.30.0
type HandedBack struct {
// Err is how the program ended, nil where it exited 0.
Err error
// Stderr is the end of what the program wrote to stderr, empty where the caller set
// cmd.Stderr.
Stderr []byte
// contains filtered or unexported fields
}
HandedBack is what a program Model.HandOver gave the terminal to sends once it exits. Only the model that handed it over takes it, so among Tabs the tab whose key started it runs its then, hidden or not, and no other tab does. That model's board receives it first through ReceivingBoard, to read again what the program may have changed.
type Keyed ¶
type Keyed interface {
Actions() []Action
}
Keyed is a Board with keys of its own. They are read ahead of the table's, so a view can add a key or take one over, and the footer and the ? overlay list them.
type Line ¶
type Line []Cell
Line is cells drawn one after another.
func Items ¶ added in v0.3.0
Items is a line of a Group's Below that the draw cuts to the items that fit, ending in +N for the rest, rather than letting the screen's edge cut one in half. The width it has moves with the side panel, so only the draw knows it. The filter and the change cue read every item, shown or not.
func ReadStatus ¶
ReadStatus is the one line a strip says about its reads. While every read is on schedule it says how old the newest is. A failed read is named in red with its reason, a late one in yellow, and a note follows plain, because a read that covered less still read. The screen's edge cuts them in that order.
func TallyBar ¶ added in v0.14.0
TallyBar is one cell per unit counted: done ▰ in green, under way ▱ in yellow, blocked ✕ in red and the rest ▱ plain. Its length shows how many there are, and one blocked unit among sixty still shows, where a fixed width would round it away. Without color only done is filled and blocked is crossed, so no unfinished unit reads as done. In a WrapLast it keeps every cell however narrow the row.
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the Bubble Tea program of one view. Tabs holds one per tab.
func NewModel ¶
func NewModel(board Board, pal gotermstyle.Palette) *Model
NewModel is a model reading board, sized until the terminal says otherwise. It draws in the TUI theme file gotermstyle's tuitheme finds, and reads it again on SIGUSR2 while it runs as the program. A file that will not load at start leaves ANSI 0-15, and on a reload keeps the colors on screen. The footer says why.
func (*Model) Ask ¶ added in v0.17.0
Ask puts q on the footer's lines. Until enter or esc every key and paste goes to it, so q and ? type themselves and tab changes no tab.
func (*Model) CloseInPlace ¶ added in v0.28.0
CloseInPlace closes the text open under the cursor's row, as the table's space does, and says whether any was open. A board that binds space to a row verb, such as a mark, calls it first, so space still closes a row open in place.
func (*Model) Cursor ¶
Cursor is the key of the group or row the cursor is on, for a board's own keys.
func (*Model) HalfScreen ¶
HalfScreen is half the table's height in lines.
func (*Model) HandOver ¶ added in v0.30.0
func (m *Model) HandOver(cmd *exec.Cmd, then func(m *Model, err error, stderr []byte) tea.Cmd) tea.Cmd
HandOver gives the terminal to cmd until it exits, then runs then with how it ended and the end of what it wrote to stderr. LastLine(stderr, err) is the sentence a failure says.
To keep stderr, the program's fd 2 is a pipe whose bytes reach the terminal as they come, so a program that asks whether stderr is a terminal hears no. A caller handing over such a program sets cmd.Stderr, such as to os.Stderr, and keeps nothing.
func (*Model) MoveCursorTo ¶ added in v0.28.0
MoveCursorTo puts the cursor on the group or row keyed key, for a board's key that acts on the cursor's row and moves on, and says whether key shows. A row in a folded group, or one the filter hides, does not. A key that changed what the board holds calls Rebuild first.
func (*Model) Read ¶ added in v0.6.0
Read opens document on a full-screen Reader over the board, until esc or space closes it. The reader's top line names the groups holding the cursor's line, then title.
func (*Model) Rebuild ¶
func (m *Model) Rebuild()
Rebuild is for a key that changed what the board shows from what it already holds.
func (*Model) Restart ¶
Restart puts the cursor back at the top and reads every source now, for a board's key that changes what the view shows.
func (*Model) Reveal ¶ added in v0.3.0
func (m *Model) Reveal()
Reveal scrolls the cursor's row and what opens under it into view, for a board's key that opens text under a row.
func (*Model) ScrollBy ¶
ScrollBy moves the screen over the table by lines and leaves the cursor, for reading through a row's detail taller than the screen. Moving the cursor ends it.
func (*Model) Tall ¶ added in v0.6.0
Tall says document runs past the screen, so enter reads it on a Reader.
func (*Model) Toggle ¶
Toggle is what enter does, for a board's key that takes enter and acts only on some rows: a row's Document that runs past the screen opens on a Reader, open in place or not, and anything else folds or opens in place. Bind it to enter alone and leave space to the table, which folds in place however tall the text. A row open in place that turns tall closes only on that space.
func (*Model) ToggleEveryRow ¶
ToggleEveryRow opens every showing row's detail, or closes them all where every one is open already, so a whole group reads as one document.
type MovingBoard ¶ added in v0.12.0
type MovingBoard interface {
// MoveEvery is how long the strip holds each drawing. Zero or less holds it still, and the
// next tick asks again, so a board with nothing moving spends nothing.
MoveEvery() time.Duration
// StripAt is the whole strip at now. It stands in for Frame.Strip on every draw, so a line
// holding still is returned as it is. A strip that changes its height redraws the table.
StripAt(now time.Time) []Line
}
MovingBoard is a Board whose strip moves between its frames, as a diagram of work in motion does. Only the strip redraws at this pace, so the table and its change cue wait for their frame. Only the tab showing runs it, and a reader over the board holds it.
type Question ¶ added in v0.17.0
type Question struct {
// Listing draws above Text, a line each, for a question about several things that names
// each one. Each line is cut at the screen's edge. While the question is open the table
// keeps one line, and a listing longer than the rest of the screen ends on how many more
// it held.
Listing []Line
// Text draws in bold as written, so its own punctuation ends it.
Text string
// Typed takes one line typed under the question as the answer. Without it enter alone
// answers, for an act that takes no text, and Answer is handed an empty string.
Typed bool
// Answer runs on enter with what was typed, trimmed. The question has closed by then, so it
// can Say what it did, ask another question, or return a command such as an editor.
Answer func(typed string) tea.Cmd
// Cancel, where set, runs on esc or ctrl+c, so a board can say that nothing was done.
Cancel func() tea.Cmd
}
Question is what a key asks on the footer's lines, until enter answers it or esc takes it away.
type Reader ¶ added in v0.6.0
type Reader struct {
// CopyText is what y copies. Nil copies the text as drawn.
CopyText func() string
// EditFile is the file e opens in the editor, after which the text is laid out again.
// Empty leaves e unbound.
EditFile string
// contains filtered or unexported fields
}
Reader is a full screen over one Document. It lays out every row at the width and draws the window that fits, because Bubble Tea keeps only the last lines of a taller view. It runs as a program of its own, or a Model opens one with Read.
func NewReader ¶ added in v0.6.0
func NewReader(title string, document Document, pal gotermstyle.Palette) *Reader
NewReader is a Reader over document, sized until the terminal says otherwise. It draws in the TUI theme file gotermstyle's tuitheme finds, and reads it again on SIGUSR2 while it runs as the program.
func (*Reader) ClosesWith ¶ added in v0.31.0
func (r *Reader) ClosesWith(back func())
ClosesWith is for a program drawing the reader inside a screen of its own, as Model.Read does over a board: esc and space call back once nothing is left to clear, where on a reader running as the program they do nothing.
func (*Reader) Once ¶ added in v0.6.0
Once is the whole document at width, for output nobody scrolls.
type ReceivingBoard ¶ added in v0.21.0
ReceivingBoard is a Board handed every message the model does not handle itself, such as the one a command its key started sends when it ends. Receive runs with the model, so it can Say what came of the command, Rebuild, or return the next command. Tabs hands such a message to every tab, because the tab whose key started the command may be hidden by the time it ends, so a board ignores a message of a type it does not own. A HandedBack is the exception, and reaches only the board whose model handed the terminal over.
type Refresher ¶
type Refresher interface {
// Claim reports whether a read is due at now, and marks one under way.
Claim(now time.Time) bool
Refresh(ctx context.Context)
// Force makes the next Claim true.
Force()
Status() Status
}
Refresher is a Source with its value's type out of the way, for the loop that only starts reads.
type Row ¶
type Row struct {
Key string
Symbol Cell
Cells []Cell
Span Line
// WrapLast is the last column as cells of their own colors. It continues on
// further lines at that column, where a cell in Cells is cut at the screen's edge.
WrapLast Line
// Under is the row's summary, drawn on a └─ row while those are on.
Under Cell
// UnderWhereCut draws Under only where the last column is too narrow for the row's cell
// there, for a summary that repeats that cell. Only the draw knows the column's width.
UnderWhereCut bool
// Detail is what the side panel shows while the cursor is on the row, and what opens
// under the row. Each cell is a paragraph that wraps. A row holding Detail or a
// Document draws a chevron in its indent.
Detail []Cell
// Document is what opens under the row and the side panel shows, in Detail's place.
Document Document
// ReaderTitle heads the Reader enter opens on a tall Document, and the side panel where
// it draws the Document. Empty, the row's own words head the Reader.
ReaderTitle string
// StartsOpen shows the row's Detail or Document under it until a key closes it, so a
// view can draw its newest entry whole and still fold it to its first line. A row whose
// Document the side panel draws starts folded while the panel shows, since the panel
// draws it whole beside the table.
StartsOpen bool
// Panel is what the side panel shows in place of both, each line wrapping at spaces
// with its cells' own colors. Without one, the panel draws the Document.
Panel Document
// Text is more the filter matches beyond what the row draws.
Text string
// Marked draws MarkGlyph in the row's indent and its first cell in MarkCode. No
// column moves for it.
Marked bool
// contains filtered or unexported fields
}
Row is one line of the table: Cells on the columns, or Span across them.
type RuleWeight ¶ added in v0.8.0
type RuleWeight int
RuleWeight is how heavy a line across the table is drawn.
const ( RuleNone RuleWeight = iota RuleThin RuleDouble )
The weights a rule takes.
type Source ¶
type Source[T any] struct { // contains filtered or unexported fields }
Source is one read at its own interval, holding its last good value.
func NewSource ¶
func NewSource[T any](name string, every time.Duration, read func(context.Context) (T, string, error)) *Source[T]
NewSource declares a read. read answers the value, a note on what it could not cover, and a failure, which leaves the last good value in place.
func (*Source[T]) Claim ¶
Claim is Refresher's. A read still under way is never started twice, so a slow source falls behind rather than piling up.
type Status ¶
type Status struct {
Name string `json:"name"`
Every string `json:"every"`
ReadAt time.Time `json:"read_at,omitzero"`
Took string `json:"took,omitempty"`
TriedAt time.Time `json:"tried_at,omitzero"`
// Fault is why the newest try failed, and empty where it read.
Fault string `json:"fault,omitempty"`
// Note is what a read that succeeded could not cover, such as a host asleep.
Note string `json:"note,omitempty"`
// Reading says a read is under way.
Reading bool `json:"reading,omitempty"`
// contains filtered or unexported fields
}
Status is what a view says about one source, on its strip and in its --json.
type Tabs ¶
type Tabs struct {
// contains filtered or unexported fields
}
Tabs is the Bubble Tea program of one Model per tab, the Elm Architecture's composition. Each tab keeps its own cursor, folds and filter. A hidden tab starts no read, builds no frame and moves no strip. On its return it draws its last good read with that read's age, and whatever is due reads at once.
func NewTabs ¶
NewTabs is a program of these tabs, opening on the one named open, or the first where open is empty. A name no tab has is an error naming the ones there are. Every tab draws in the one TUI theme file gotermstyle's tuitheme finds, read again on SIGUSR2.
type TailBoard ¶
type TailBoard interface {
Tails() bool
}
TailBoard is a Board read from its newest row, as a log is. Its cursor starts on the last row, and stays on the last row as rows land while it is there.
type TwoLineBoard ¶ added in v0.26.0
type TwoLineBoard interface {
TwoLines() bool
}
TwoLineBoard is a Board whose rows read in two lines, the second a row's Under, so its summaries start shown. f still hides them.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package breadcrumb fits a path of names, such as "uv › add › Options", on one line.
|
Package breadcrumb fits a path of names, such as "uv › add › Options", on one line. |
|
Package chart draws plain series as lines of cells for a board.
|
Package chart draws plain series as lines of cells for a board. |
|
cmd
|
|
|
read-markdown
command
read-markdown draws a markdown file, or what is piped to it, on a full-screen reader.
|
read-markdown draws a markdown file, or what is piped to it, on a full-screen reader. |
|
Package markdown lays markdown out as a board's lines at a given width, naming a TUI theme role for each cell, such as markdown.heading.2 or syntax.keyword.
|
Package markdown lays markdown out as a board's lines at a given width, naming a TUI theme role for each cell, such as markdown.heading.2 or syntax.keyword. |
|
Package printedframe picks the size one printed frame draws at, as a TUI's --once prints it.
|
Package printedframe picks the size one printed frame draws at, as a TUI's --once prints it. |