Documentation
¶
Overview ¶
Package ui is the reusable terminal view layer for TwixT: a board renderer with two drawing scales and a scrolling viewport, a layout engine that fits board and information panel into any terminal size, and a data-driven keymap. Game screens are built on top of it; the package itself owns no game flow beyond a demonstration model.
Index ¶
Constants ¶
const ( MinWidth = 20 MinHeight = 6 )
Minimum terminal size for showing a board. Below either bound the frame is an explicit too-small notice instead: the smallest useful board view needs the row-number gutter plus a handful of hole columns, and the letters row plus a few board rows plus the status line.
const JumpStep = 3
JumpStep is how many holes the shifted movement keys jump.
Variables ¶
var ( Compact = Scale{/* contains filtered or unexported fields */} Detail = Scale{/* contains filtered or unexported fields */} )
The two supported scales. Compact fits a standard 24×24 board into roughly 52×26 cells; Detail spreads it over roughly 98×49 and draws every link with three-cell strokes.
Functions ¶
func Compose ¶
func Compose(arr Arrangement, board, panel []string, status string, st *Styles) string
Compose assembles the final frame from the rendered board, the panel lines and the status line, clipped so that no line exceeds the arrangement's width and no more than its height lines are emitted. Board lines must come from BoardView.Render with the arrangement's board bounds.
Types ¶
type Action ¶
type Action uint8
Action is something the board UI can do in response to a key.
const ( ActNone Action = iota ActMoveLeft ActMoveRight ActMoveUp ActMoveDown ActJumpLeft ActJumpRight ActJumpUp ActJumpDown ActEdgeTop ActEdgeBottom ActEdgeLeft ActEdgeRight ActPlacePeg ActConfirm ActLinkMode ActToggleLink ActAbortTurn ActExitMode ActQuit )
Actions. Movement actions apply in every context; link actions only in link mode. ActConfirm is context-sensitive: it places the peg when none is staged and commits the turn otherwise.
type Arrangement ¶
type Arrangement struct {
Width, Height int
TooSmall bool
Scale Scale
// BoardAvailW and BoardAvailH bound what BoardView.Render may use.
BoardAvailW, BoardAvailH int
// BoardW and BoardH are the actual block size after clipping to the
// available space, used to place the panel without wasted columns.
BoardW, BoardH int
Panel PanelPlacement
PanelW, PanelH int
}
Arrangement is the layout decision for one terminal size: which scale to draw the board at, how much space the board gets, and where the information panel sits. A status line is always reserved at the bottom except in the too-small state.
func Arrange ¶
func Arrange(width, height, n int) Arrangement
Arrange decides the layout for a terminal of width×height showing an n-hole board. The rules, in order: the detail scale is used when its full board fits; otherwise the compact scale, with a viewport when even that does not fit. The panel goes beside the board when there is width for it, else below when there is height, else information lives in the status line alone.
type Binding ¶
type Binding struct {
Action Action
Keys []string
Contexts Context
// Label is the short key name shown in help, Help the one-line meaning,
// Short an optional terse verb for one-line hint strings.
Label string
Help string
Short string
}
Binding maps keys to an action. Keys are Bubble Tea key strings as returned by tea.KeyPressMsg.String(). Every key here is reliable inside a terminal multiplexer: unmodified printables, uppercase letters, and the basic special keys — no modified arrows, no protocol-dependent combinations.
type BoardView ¶
type BoardView struct {
// Scale selects the drawing density. The layout engine picks it per frame.
Scale Scale
// Cursor is the hole the cursor sits on; ShowCursor turns it on. It is
// drawn as square brackets either side of the hole, or, where a link owns
// those cells, as a mark on the hole itself.
Cursor game.Point
ShowCursor bool
// LastMove marks the peg just played, so it can be found on a large board
// without reading the coordinate off the panel. ShowLastMove turns it on.
// An overlay on the same hole takes the cell, so the ring is hidden while
// the cursor rests on the last move, which is the one time it is not
// needed.
LastMove game.Point
ShowLastMove bool
// Highlights marks holes to call out (hints, tutorial steps, the staged
// peg). They render as round brackets around the hole, falling back to a
// mark on the hole itself the same way the cursor does.
Highlights []game.Point
// Digits overlays link-direction digits on target holes in link mode.
Digits map[game.Point]rune
// contains filtered or unexported fields
}
BoardView renders a game onto a scrolling viewport. The zero value is usable; set Scale before rendering. Viewport position is internal state that follows the cursor and survives resizes.
type Context ¶
type Context uint8
Context is a keymap context. Contexts form a bitmask so one binding can serve several.
type Demo ¶
type Demo struct {
// contains filtered or unexported fields
}
Demo is a Bubble Tea model exercising the whole view layer: a hotseat sandbox where both sides are played from the keyboard. Game screens are built elsewhere; this model exists to prove the renderer, layout, viewport and keymap against a live engine, and to serve as the wiring example.
func NewDemo ¶
NewDemo returns a demo model for the given ruleset. Colour is dropped when NO_COLOR is set, per the convention.
type Keymap ¶
type Keymap []Binding
Keymap is an ordered list of bindings: one source of truth for dispatch, help text and documentation.
func (Keymap) HelpEntries ¶
HelpEntries returns the help rows for a context, in keymap order.
func (Keymap) HelpTable ¶
HelpTable renders the whole keymap as aligned plain-text rows, one binding per line, for documentation and the tutorial.
type PanelPlacement ¶
type PanelPlacement uint8
PanelPlacement says where the information panel goes.
const ( PanelNone PanelPlacement = iota PanelSide PanelBottom )
Panel placements: none (status line only), beside the board, below it.
type Scale ¶
type Scale struct {
// contains filtered or unexported fields
}
Scale is a board drawing density: how many screen columns and rows separate neighbouring holes.
The knight-move link geometry constrains the choice hard. With colStep=2c and rowStep=c, a steep link (column ±1, row ±2) spans 2c×2c screen cells — an exact diagonal — and a shallow link (column ±2, row ±1) runs at slope 1/4, which the renderer draws as a ramp of horizontal scan-line glyphs. Both provided scales keep that shape, so one rasteriser serves both, and both match the roughly 1:2 width:height aspect of terminal cells, so the board looks square.
func ScaleFor ¶ added in v0.1.1
ScaleFor picks the drawing scale for an n-hole board from the space the board itself is going to get: the detail scale when its whole block fits there, the compact one otherwise, which a viewport then clips if even that does not fit.
The space to pass is the board's share, not the terminal's. A caller that takes rows away from the board before drawing — the tutorial gives most of the screen to its prose — and asks about the terminal instead will choose detail for a board that then has to be clipped, when compact would have shown the whole of it.
func (Scale) BlockSize ¶
BlockSize returns the full size of the rendered board block — canvas plus gutter and letters row — before any viewport clipping.
func (Scale) CanvasSize ¶
CanvasSize returns the size of the unclipped board canvas for an n-hole side, excluding coordinate labels. One margin column on each side leaves room for cursor brackets around edge holes.
type Styles ¶
type Styles struct {
// Plain suppresses all styling, leaving pure text.
Plain bool
Hole lipgloss.Style
PegVertical lipgloss.Style
PegHorizontal lipgloss.Style
LinkVertical lipgloss.Style
LinkHorizontal lipgloss.Style
Cursor lipgloss.Style
Highlight lipgloss.Style
LinkDigit lipgloss.Style
LastMove lipgloss.Style
Label lipgloss.Style
PanelTitle lipgloss.Style
PanelText lipgloss.Style
Status lipgloss.Style
Message lipgloss.Style
TooSmall lipgloss.Style
}
Styles holds every style the view layer uses. The zero value styles nothing; use DefaultStyles for the coloured set and PlainStyles when colour must be off (NO_COLOR). Rendering never depends on these for meaning — they are reinforcement over an already unambiguous glyph set.
func DefaultStyles ¶
func DefaultStyles() Styles
DefaultStyles returns the coloured style set. Colours come from the ANSI-16 palette so they follow the user's terminal theme, and the colour profile machinery in Bubble Tea degrades them further when the terminal is limited.
func PlainStyles ¶
func PlainStyles() Styles
PlainStyles returns a style set that applies nothing, for NO_COLOR and for tests that assert on raw glyphs.
func StylesFor ¶
StylesFor maps a colour scheme onto the style set the view layer uses.
A scheme with no colours, which is both the monochrome theme and what a terminal reporting no colour support gets, produces the plain style set rather than a set of empty styles: rendering then takes the same path as a test asserting on raw glyphs, so there is one behaviour to reason about instead of two that only look alike.