spin

package
v1.8.1 Latest Latest
Warning

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

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package spin is the branded spinner engine of spec section 10.2.5: the gradient label that wipes in column by column while kb waits on a network round trip or a model inference.

It is the branded half of the two-tier split of spec section 10.2.4. The plain half is bubbles' spinner.Dot on theme.Styles.Work.Label and needs no code here: a branded frame announces that the machine is thinking on your behalf and may be a while, so spending it on a local disk write devalues it.

The engine obeys the determinism contract of spec section 10.2.2 without exception. It reads no clock - the elapsed suffix arrives through a closure the surface builds from its own injected now - every frame is rendered once at construction and indexed afterwards, and every tick chain carries a generation so a restart cannot leave two tickers running.

Index

Constants

View Source
const (
	EllipsisStates = 4  // "", ".", "..", "..."
	EllipsisField  = 3  // columns the ellipsis always occupies, space padded
	SuffixField    = 6  // columns reserved for " 1m02s"
	MaxLabelW      = 48 // longest branded label; longer is truncated
	MaxEngines     = 1  // branded engines that may tick at once (section 10.2.6)

	// BirthGlyph is the pre-birth cell, drawn in theme.Styles.Work.Birth.
	BirthGlyph = "."

	// ScrambleRunes is the 36-rune alphabet a cell flashes through between its
	// birth step and its own rune. ASCII only, one column each.
	ScrambleRunes = "abcdefghijklmnopqrstuvwxyz0123456789"
)

The engine vocabulary of spec section 10.2.5. Every duration and tick count the engine reads is a theme.Timing token instead; nothing here is a clock.

View Source
const (
	SeedEditorDraft  uint64 = 1 // card editor, AI draft
	SeedDriftCheck   uint64 = 2 // card detail, drift check
	SeedAdrPropose   uint64 = 3 // ADR split, propose stories
	SeedImportFetch  uint64 = 4 // issue import, source list and preview fetch
	SeedSettingsTest uint64 = 5 // settings, test connection
)

The per-surface birth seeds of spec section 10.2.4. Each is a named constant rather than a runtime instance id, which is what makes a birth schedule a fixed golden instead of a seeded reproduction: two surfaces stagger differently, and the same surface staggers identically in every process.

Variables

This section is empty.

Functions

func Elapsed

func Elapsed(age time.Duration) string

Elapsed formats an operation's age for the suffix field: 12s below one minute, 1m02s at or above it, 59m+ at or above an hour. The engine never reads a clock itself; the surface passes the duration in.

Types

type Engine

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

Engine is one branded spinner. The zero value renders the empty string and arms nothing, which is the settled state of a spinner: a surface that is not busy shows its ordinary static text with no engine output in it at all. That is why the existing goldens need no edits (spec section 10.2.2, point 2).

At most MaxEngines of these tick at once and it belongs to the front-most open surface in the spec section 4 z-order; see Stop and Start.

func (*Engine) Configure

func (e *Engine) Configure(styles *theme.Styles, settings Settings)

Configure resolves the styles and settings of one mount, rebuilding the frame cache when - and only when - the settings hash or the theme instance changed. A rebuild happens on a theme rebuild, on a resize that changes the label width, and on a new operation with a different label; nothing else.

The cache is per engine. Spec section 6.2 forbids package-level mutable style state and a shared frame map is close enough to it to refuse, which the MaxEngines ceiling makes free anyway.

func (Engine) Frame

func (e Engine) Frame(step int) string

Frame is the assembled row at an absolute step count s, ticks since the chain started. It is the whole render path, so a test asserts a fixed string against it without running a clock.

func (Engine) Gen

func (e Engine) Gen() uint32

Gen is the current generation, for a caller that has to build a StepMsg by hand - a test stepping the chain synchronously.

func (Engine) Mounted

func (e Engine) Mounted() bool

Mounted reports whether this engine is ticking. The root counts these across the open z-order stack against MaxEngines.

func (Engine) Seed

func (e Engine) Seed() uint64

Seed is the engine's per-surface constant.

func (*Engine) Start

func (e *Engine) Start() tea.Cmd

Start mounts the engine at step zero and opens a fresh tick chain. The generation bump is what kills any chain still in flight.

Below FidelityFull the wipe is never armed: it is a class-B effect (spec section 10.7.6), its information lives in the color difference between born and unborn cells, and a flattened wipe is a flash of punctuation. The engine mounts settled instead, where the ellipsis and the elapsed suffix - carried by glyph and text, not by hue - go on running unchanged.

func (*Engine) Step

func (e *Engine) Step(msg StepMsg, busy bool) tea.Cmd

Step advances the chain by one tick. busy is the surface's own gate, read on every tick rather than mirrored into an animating flag a code path can forget to clear (spec section 10.2.3).

A nil command is returned - and the chain therefore terminates - for a stale generation, a foreign seed, an unmounted engine, or a settled gate.

func (*Engine) Stop

func (e *Engine) Stop()

Stop clears the mount and bumps the generation, so a tick still in flight is dropped on arrival. The backgrounded surface of spec section 10.2.6 calls this: its operation keeps running, only the animation stops.

func (Engine) View

func (e Engine) View() string

View is the assembled row at the current step, or the empty string while the engine is unmounted or still inside the birth delay. A surface that gets "" renders its ordinary static busy label instead.

The run carries no background; the caller lays it onto its own shade tier with theme.Styles.SurfaceRun.

func (Engine) Width

func (e Engine) Width() int

Width is the fixed column cost of the assembled row: the label, the ellipsis field and the elapsed field, reserved for the life of the mount.

type Settings

type Settings struct {
	Label    string        // the branded label, already truncated by Fit
	Width    int           // resolved label width in columns, set by Fit
	Seed     uint64        // the per-surface constant above
	Scramble bool          // false is crush's NoScramble: the wipe without the flash
	Suffix   func() string // elapsed text, built by the surface from its injected now
}

Settings is the hashed description of one mount. These are all of its fields.

Suffix is deliberately outside the hash: a func has no stable identity, and the suffix is appended live to a cached frame rather than baked into one.

func (Settings) Fit

func (s Settings) Fit(styles *theme.Styles, avail int) Settings

Fit truncates Label onto the row that avail columns can hold and resolves Width. The label takes min(MaxLabelW, avail - EllipsisField - SuffixField) columns, because the ellipsis and the elapsed fields are reserved for the life of the mount whether or not they are showing (spec section 10.4.4).

type StepMsg

type StepMsg struct {
	Seed uint64
	Gen  uint32
}

StepMsg advances one engine by one tick of the one clock.

Seed identifies the engine because a single Update tree routes ticks for more than one of them - the front overlay's, and during the background handoff of spec section 10.2.6 a backgrounded surface's last outstanding tick. Gen is the double-chain fix of spec section 10.2.5: Start and Stop both bump it, so a tick issued by a superseded chain is dropped on arrival instead of doubling the frame rate.

Jump to

Keyboard shortcuts

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