ui

package
v0.74.1 Latest Latest
Warning

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

Go to latest
Published: Feb 19, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package ui provides reusable TUI components including modals, buttons, scrollbars, skeleton loaders, overlays, and text utilities.

Package ui provides shared UI components and helpers for the TUI.

Index

Constants

View Source
const (
	ModalWidthSmall  = 40 // Simple confirmations
	ModalWidthMedium = 50 // Standard modals with inputs
	ModalWidthLarge  = 60 // Modals with longer content
)

Standard modal widths

View Source
const (
	DimSequence   = "\x1b[2m"
	ResetSequence = "\x1b[0m"
)

DimSequence and ResetSequence are the raw ANSI codes used by DimStyle. Exported for testing.

View Source
const SkeletonTickInterval = 80 * time.Millisecond

SkeletonTickInterval is the animation frame rate (60fps feel).

Variables

View Source
var AnsiResetRe = regexp.MustCompile(`\x1b\[0?m`)

AnsiResetRe matches ANSI reset sequences (both \x1b[0m and \x1b[m).

View Source
var DimStyle = lipgloss.NewStyle().Foreground(styles.TextMuted)

DimStyle applies a dim gray color to background content behind modals. We strip existing ANSI codes and apply gray because SGR 2 (faint) doesn't reliably combine with existing color codes in most terminals.

Functions

func BytePosToRunePos

func BytePosToRunePos(s string, bytePos int) int

BytePosToRunePos converts a byte position to a rune position.

func CalculateModalWidth

func CalculateModalWidth(content string, minWidth, maxWidth int) int

CalculateModalWidth returns an appropriate width based on content. Ensures width is within min/max bounds.

func ClampModalWidth

func ClampModalWidth(width, screenWidth int) int

ClampModalWidth clamps a width value between min and max screen bounds.

func ExpandTabs

func ExpandTabs(line string, tabWidth int) string

ExpandTabs replaces tabs with spaces, preserving ANSI sequences and column widths.

func GetSelectionBgANSI

func GetSelectionBgANSI() string

GetSelectionBgANSI returns the ANSI 24-bit background code for selection highlight based on the current theme's BgTertiary color.

func InjectCharacterRangeBackground

func InjectCharacterRangeBackground(line string, startCol, endCol int) string

InjectCharacterRangeBackground applies selection background to visual columns [startCol, endCol] (inclusive) within the line. startCol and endCol are in absolute visual space (post-tab-expansion). Handles ANSI codes correctly. If endCol is -1, highlights to end of line.

func InjectSelectionBackground

func InjectSelectionBackground(s string) string

InjectSelectionBackground adds a selection background while preserving ANSI resets. It prepends the background at the start and re-injects after any reset sequences.

func OverlayModal

func OverlayModal(background, modal string, width, height int) string

OverlayModal composites a modal on top of a dimmed background. The modal is centered, with dimmed background visible on all sides.

func RenderButtonPair

func RenderButtonPair(confirmLabel, cancelLabel string, focusIdx, hoverIdx int) string

RenderButtonPair renders a confirm/cancel button pair with proper spacing. focusIdx: 1 for confirm focused, 2 for cancel focused, 0 for neither hoverIdx: 1 for confirm hovered, 2 for cancel hovered, 0 for neither

func RenderDivider

func RenderDivider(height int) string

RenderDivider renders a vertical divider for separating panes. The divider uses BorderNormal color and is shifted down by 1 line to align with bordered pane content (below top border). Height should be the full pane height; divider renders height-2 lines to stop above the bottom border.

func RenderScrollbar

func RenderScrollbar(params ScrollbarParams) string

RenderScrollbar returns a single-column string (newline-separated) representing a vertical scrollbar track. Returns a column of spaces if all content is visible (TotalItems <= VisibleItems) to reserve the width and prevent layout jitter. Output has exactly TrackHeight lines, each 1 character wide.

func ResolveButtonStyle

func ResolveButtonStyle(focusIdx, hoverIdx, btnIdx int) lipgloss.Style

ResolveButtonStyle returns the appropriate style based on focus and hover state. focusIdx: which button index is focused (0-based), -1 for none hoverIdx: which button index is hovered (0-based), -1 for none btnIdx: the index of the button being styled

func SafeByteSlice

func SafeByteSlice(s string, byteStart, byteEnd int) string

SafeByteSlice extracts a substring using byte positions, ensuring the slice boundaries fall on valid UTF-8 boundaries. Returns the substring or empty string if positions are invalid.

func SkeletonTick

func SkeletonTick() tea.Cmd

SkeletonTick returns a command to start/continue the skeleton animation. Call this from your plugin's Start() or when enabling the skeleton.

func TruncateMid

func TruncateMid(s string, width int, highlightRuneStart, highlightRuneEnd int) (string, int, int)

TruncateMid truncates a string to fit width, centering around a visual position. Returns (truncated string, new highlight start rune index, new highlight end rune index). If the original highlight fits, indices are adjusted for any leading truncation.

func TruncateStart

func TruncateStart(s string, width int) string

TruncateStart truncates the start of the string if it exceeds width. "..." + suffix

func TruncateString

func TruncateString(s string, width int) string

TruncateString truncates a string to the given visual width. It handles multi-byte characters and full-width characters correctly. If the string is truncated, "..." is appended (and accounted for in width). Pre-condition: width should be at least 3.

func VisualColAtRelativeX

func VisualColAtRelativeX(expandedLine string, relX int) int

VisualColAtRelativeX takes an already-expanded line and a relative X offset, walks graphemes using ansi.GraphemeWidth.DecodeSequenceInString, snaps to character boundaries, and clamps to last char if beyond end.

func VisualSubstring

func VisualSubstring(s string, startCol, endCol int) string

VisualSubstring extracts a substring by visual column range [startCol, endCol). endCol is EXCLUSIVE (one past last included column). Handles ANSI escape codes (skipped in column counting). If endCol is -1, extracts to end of string. Returns plain text (ANSI stripped) for clipboard use.

Types

type BrailleSpinner

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

BrailleSpinner renders an animated braille dot pattern. It is a passive component — it does not generate its own ticks. Call Tick() from an existing SkeletonTickMsg handler to advance the frame.

func NewBrailleSpinner

func NewBrailleSpinner() BrailleSpinner

NewBrailleSpinner creates a new braille spinner (inactive by default).

func (BrailleSpinner) IsActive

func (b BrailleSpinner) IsActive() bool

IsActive returns whether the spinner is running.

func (*BrailleSpinner) Start

func (b *BrailleSpinner) Start()

Start marks the spinner as active.

func (*BrailleSpinner) Stop

func (b *BrailleSpinner) Stop()

Stop halts the animation.

func (*BrailleSpinner) Tick

func (b *BrailleSpinner) Tick()

Tick advances the animation frame. Call this from a SkeletonTickMsg handler.

func (BrailleSpinner) View

func (b BrailleSpinner) View() string

View renders the current spinner frame.

func (BrailleSpinner) ViewFill

func (b BrailleSpinner) ViewFill(width int, label string) string

ViewFill renders the spinner centered in the given width, with a label.

type ConfirmDialog

type ConfirmDialog struct {
	Title        string
	Message      string
	ConfirmLabel string         // e.g., " Confirm ", " Delete ", " Yes "
	CancelLabel  string         // e.g., " Cancel ", " No "
	BorderColor  lipgloss.Color // Modal border color
	Width        int            // Modal width (default 50)
}

ConfirmDialog is a reusable confirmation modal with interactive buttons.

func NewConfirmDialog

func NewConfirmDialog(title, message string) *ConfirmDialog

NewConfirmDialog creates a dialog with sensible defaults.

func (*ConfirmDialog) ToModal

func (d *ConfirmDialog) ToModal() *modal.Modal

ToModal adapts the dialog configuration into a modal.Modal instance.

type ScrollbarParams

type ScrollbarParams struct {
	TotalItems   int // Total logical items in the list
	ScrollOffset int // Index of first visible item (scroll offset)
	VisibleItems int // Number of items that fit in the viewport
	TrackHeight  int // Height of the scrollbar track in terminal rows
}

ScrollbarParams configures a vertical scrollbar rendering.

type SelectionPoint

type SelectionPoint struct {
	Line int // buffer line index, -1 = unset
	Col  int // visual column, -1 = unset
}

SelectionPoint represents a position as (line, col) in visual space. Col is post-tab-expansion, accounting for multi-width chars. EndCol convention: INCLUSIVE — the character under the cursor IS selected.

func (SelectionPoint) Before

func (p SelectionPoint) Before(other SelectionPoint) bool

Before returns true if p is before other in document order.

func (SelectionPoint) Valid

func (p SelectionPoint) Valid() bool

Valid returns true if the point has been set.

type SelectionState

type SelectionState struct {
	Active   bool
	Start    SelectionPoint
	End      SelectionPoint
	Anchor   SelectionPoint
	ViewRect mouse.Rect
}

SelectionState holds all selection state for a single selectable region.

func (*SelectionState) Clear

func (s *SelectionState) Clear()

Clear resets all fields, Start/End/Anchor to {-1,-1}.

func (*SelectionState) FinishDrag

func (s *SelectionState) FinishDrag()

FinishDrag completes a drag operation. If start not valid (no drag motion), calls Clear(). Otherwise sets Active=false.

func (*SelectionState) GetLineSelectionCols

func (s *SelectionState) GetLineSelectionCols(lineIdx int) (startCol, endCol int)

GetLineSelectionCols returns the visual column range [start, end] inclusive for the given line. Returns (-1,-1) if not selected. endCol==-1 means "to end of line". Logic: single-line returns both cols; first line of multi returns (startCol, -1); last line returns (0, endCol); middle returns (0, -1).

func (*SelectionState) HandleDrag

func (s *SelectionState) HandleDrag(lineIdx, col int)

HandleDrag updates selection state during a drag operation. If start not valid, initialize start=end=anchor. Set Active=true. Order start/end by document position relative to anchor.

func (*SelectionState) HasSelection

func (s *SelectionState) HasSelection() bool

HasSelection returns true if both Start and End are valid.

func (*SelectionState) IsLineSelected

func (s *SelectionState) IsLineSelected(lineIdx int) bool

IsLineSelected checks if line is in [Start.Line, End.Line].

func (*SelectionState) PrepareDrag

func (s *SelectionState) PrepareDrag(lineIdx, col int, viewRect mouse.Rect)

PrepareDrag stores the click position and starts drag tracking without initializing selection. Selection only activates on actual drag motion.

func (*SelectionState) SelectedText

func (s *SelectionState) SelectedText(lines []string, startLine int, tabWidth int) []string

SelectedText takes raw lines in the selected range starting at startLine index, expands tabs, extracts with column precision using VisualSubstring. For single-line: extract [startCol, endCol+1). For multi-line: first line from startCol to end, middle lines ANSI-stripped, last line from 0 to endCol+1.

type Skeleton

type Skeleton struct {
	// Configuration
	Rows      int   // Number of skeleton rows to display
	RowWidths []int // Width pattern for each row (cycles if fewer than Rows)
	// contains filtered or unexported fields
}

Skeleton renders animated placeholder rows with a shimmer effect.

func NewSkeleton

func NewSkeleton(rows int, rowWidths []int) Skeleton

NewSkeleton creates a skeleton loader with the given row count. RowWidths defines the relative width of each row (e.g., []int{80, 60, 75} for varied lengths). If rowWidths is nil, uses a default varied pattern.

func (Skeleton) IsActive

func (s Skeleton) IsActive() bool

IsActive returns whether the animation is running.

func (*Skeleton) Start

func (s *Skeleton) Start() tea.Cmd

Start begins the shimmer animation.

func (*Skeleton) Stop

func (s *Skeleton) Stop()

Stop halts the shimmer animation.

func (*Skeleton) Update

func (s *Skeleton) Update(msg tea.Msg) tea.Cmd

Update handles tick messages for animation.

func (Skeleton) View

func (s Skeleton) View(width int) string

View renders the skeleton rows with shimmer effect. width is the available content width.

type SkeletonTickMsg

type SkeletonTickMsg time.Time

SkeletonTickMsg is sent to update the shimmer animation frame.

type TruncateCache

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

TruncateCache provides cached ANSI-aware truncation to eliminate allocation churn. Thread-safe for concurrent access from rendering goroutines.

func NewTruncateCache

func NewTruncateCache(maxSize int) *TruncateCache

NewTruncateCache creates a new truncation cache with the given maximum size. maxSize limits memory growth; when exceeded, the cache is cleared.

func (*TruncateCache) Clear

func (c *TruncateCache) Clear()

Clear removes all cached entries. Should be called when window resizes to avoid stale results.

func (*TruncateCache) Size

func (c *TruncateCache) Size() int

Size returns the current number of cached entries (for testing/monitoring).

func (*TruncateCache) Truncate

func (c *TruncateCache) Truncate(content string, width int, tail string) string

Truncate returns the content truncated to width using ANSI-aware truncation. Results are cached to avoid repeated parser allocations.

func (*TruncateCache) TruncateLeft

func (c *TruncateCache) TruncateLeft(content string, offset int, tail string) string

TruncateLeft returns the content truncated from the left to width using ANSI-aware truncation. Results are cached to avoid repeated parser allocations. NOTE: For horizontal scrolling with varying offsets, prefer TruncateLeftRight to avoid cache thrashing. Each unique offset creates a new cache entry.

func (*TruncateCache) TruncateLeftRight

func (c *TruncateCache) TruncateLeftRight(content string, leftOffset int, width int) string

TruncateLeftRight applies a left offset and then truncates to width. This is optimized for horizontal scrolling where the offset varies frequently. To prevent cache thrashing, offsets are normalized (rounded down to nearest 5).

Benefits: - Reduces cache key variance from millions of possible offsets to ~1000 - Combines two parse operations into one (width+offset in single pass) - High cache hit rate for repeated/back-and-forth scrolling

This prevents cellbuf allocation churn that occurs when TruncateLeft is called with constantly-varying offset values.

Jump to

Keyboard shortcuts

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