board

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package board is a kanban screen: cards in lanes, grouped by a field the application names, with the cursor moving between them and cards moving between lanes.

Nothing here knows what a card is. The application says what its two lines read, which lanes it belongs in, and what moving it between them means; the board does the grouping, the layout, the scrolling and the hit-testing.

Index

Constants

View Source
const Lane = "(none)"

Lane is the catch-all column: the cards with no value for the current axis. It always sorts last, where an empty bucket belongs.

Variables

This section is empty.

Functions

func Order

func Order(lanes ...string) func(a, b string) int

Order is a comparison that puts named lanes in the given order and sorts anything else alphabetically after them — a workflow, in other words, where alphabetical would read backwards.

Types

type Axis

type Axis struct {
	// Name is what the axis is called, in the caption and in the toast that
	// refuses a move on a view-only one.
	Name string

	// Lanes returns the lane names a card belongs to. Returning several puts
	// the card in each of them, which is how a board grouped by label reads;
	// returning none puts it in the catch-all lane.
	Lanes func(Card) []string

	// Order sorts two lane names. Without one, lanes sort alphabetically —
	// which is wrong for a workflow, where Done must not come first.
	Order func(a, b string) int

	// Move writes a card into another lane. Leaving it nil makes the axis
	// view-only, and the board says so rather than appearing to do nothing.
	Move func(c Card, from, to string) tea.Cmd
}

Axis is one way of grouping the cards.

type Board

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

Board is the screen. Push it onto the shell and call Reload.

func New

func New(nb *ninebox.Model, opts Options) *Board

New builds a board. It fetches nothing until Reload is called, so an app can push it and load in one command.

func (*Board) Actions

func (b *Board) Actions() []ninebox.Action

Actions is what the board can do, in the order a newcomer needs it.

func (*Board) Axis

func (b *Board) Axis() Axis

Axis is the grouping currently on show.

func (*Board) Collapsed added in v0.4.0

func (b *Board) Collapsed(name string) bool

Collapsed reports whether a lane is folded.

func (*Board) Columns

func (b *Board) Columns() []Column

Columns are the lanes as they stand.

func (*Board) Commands

func (b *Board) Commands() []ninebox.Command

Commands are the same, by name.

func (*Board) Current

func (b *Board) Current() (Card, bool)

Current is the card under the cursor. A folded lane draws no cards, so it has nothing under the cursor. Answering false there is what makes every card action — moving one, opening it, the application's own keys — a no-op on a folded lane without any of them having to learn about folding: they all already ask.

func (*Board) Cursor

func (b *Board) Cursor() (lane, card int)

Cursor is which lane and which card in it are selected.

func (*Board) Key

func (b *Board) Key(msg tea.KeyMsg) (tea.Cmd, bool)

Key drives the board.

The arrows move around it and h/l move the card, which is the one pairing worth learning: on a kanban board the thing you do most is push a card one lane over, and it deserves the keys under your fingers.

func (*Board) Mouse

func (b *Board) Mouse(msg tea.MouseMsg) tea.Cmd

Mouse drives the board with the pointer.

Terminals give no drag event, so moving a card is pick-then-place: click a card to select it, then click another lane's heading or empty space to send it there.

func (*Board) Move

func (b *Board) Move(lanes, cards int)

Move shifts the cursor by lanes and cards. Changing lane starts at its top: a lane is a list of its own, and landing halfway down one is disorienting.

func (*Board) MoveCard

func (b *Board) MoveCard(delta int) tea.Cmd

MoveCard writes the selected card into a neighbouring lane.

func (*Board) MoveTo

func (b *Board) MoveTo(lane, card int)

MoveTo puts the cursor on one card, for a click.

func (*Board) NextAxis

func (b *Board) NextAxis() tea.Cmd

NextAxis cycles the grouping and re-fetches, because an axis may need different data — a status board needs statuses expanded, a plain one does not.

The cursor keeps the card it was on rather than returning to the first lane: regrouping is a different arrangement of the same things, and the card someone was looking at is the one they still want to be looking at. Only the sideways scroll resets, since the lanes it was showing are gone.

func (*Board) Offset

func (b *Board) Offset() int

Offset is the first visible lane.

func (*Board) Receive

func (b *Board) Receive(msg tea.Msg) tea.Cmd

Receive takes the board's own messages.

func (*Board) Refresh

func (b *Board) Refresh() tea.Cmd

Refresh makes the board one of the screens the shell's timer re-fetches.

func (*Board) Reload

func (b *Board) Reload() tea.Cmd

Reload fetches the cards again for the current axis.

func (*Board) Scope

func (b *Board) Scope() string

Scope is what the board covers.

func (*Board) Scroll

func (b *Board) Scroll(lanes int)

Scroll slides the visible lanes sideways.

func (*Board) SetScope

func (b *Board) SetScope(scope string)

SetScope changes it, for a board reused across projects.

func (*Board) Title

func (b *Board) Title() string

Title is the pane caption: what the board covers, what it is grouped by, and how many cards are on it.

func (*Board) ToggleCollapse added in v0.4.0

func (b *Board) ToggleCollapse() tea.Cmd

ToggleCollapse folds or unfolds the lane the cursor is in.

func (*Board) View

func (b *Board) View(width, height int) string

View renders the lanes side by side.

type Card

type Card struct {
	// ID identifies the card across a reload, so the cursor stays on the card
	// someone was looking at rather than on whatever ends up in its position.
	ID    string
	Head  string
	Title string
	Style lipgloss.Style // how the head line is coloured; the zero value is plain
	// Meta is what the card carries besides its title — labels, a milestone,
	// whatever tells two cards apart without opening either. Drawn on a third
	// line, in order, until the column runs out.
	Meta    []Chip
	Payload any
}

Card is one thing on the board.

Head and Title are the two lines it shows — an identifier and a summary, in practice. Payload is the application's own object, which the axes read and the actions act on.

type Chip added in v0.4.0

type Chip struct {
	Text  string
	Style lipgloss.Style
}

Chip is one piece of a card's metadata, with its own colour: a label keeps the one the provider gave it, where a milestone is just dim.

type Column

type Column struct {
	Name  string
	Cards []Card
}

Column is one lane: a name, and the cards that landed in it.

type Options

type Options struct {
	// Title is the word in the caption — "board" unless an app has a better one.
	Title string
	// Scope is what the board covers, shown in the caption beside the axis.
	Scope string
	// Axes are the groupings, in the order the cycle key steps through them.
	// The first is where the board opens.
	Axes []Axis
	// Load fetches the cards. The axis is passed because a provider may be able
	// to filter or expand differently for each.
	Load func(ctx context.Context, axis Axis) (Result, error)
	// Open is what enter does to a card.
	Open func(Card) tea.Cmd
	// Link is the page `o` opens in a browser.
	Link func(Card) string
}

Options is everything an application supplies.

type Result

type Result struct {
	Cards []Card
	// Axis, when set, is the axis the cards were actually grouped by. An app
	// that finds the asked-for axis empty can group by another and say so here
	// rather than showing one useless lane.
	Axis string
	// Note is said in the status line once the cards land: why the axis
	// changed, what was left out.
	Note string
	// Lanes is the full set of lanes to draw, whether or not a card landed in
	// one — the statuses a project defines, the labels it has. A board whose
	// lanes come only from its cards has no Done column until something is
	// done, and nowhere to drag a card that would put it there.
	//
	// Empty means the lanes are the ones the cards imply, as before. The set
	// is a floor rather than a filter: a lane a card carries is drawn whether
	// or not it is named here, because a group board aggregates projects that
	// define their own values.
	Lanes []string
	// Collapsed are the lanes to fold on sight — finished work, usually, which
	// a board draws for completeness and nobody reads.
	//
	// It applies only to a lane the board has not drawn before, so a reload
	// cannot undo a fold or unfold someone made by hand.
	Collapsed []string
}

Result is what a load produced.

Jump to

Keyboard shortcuts

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