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
- Variables
- func BytePosToRunePos(s string, bytePos int) int
- func CalculateModalWidth(content string, minWidth, maxWidth int) int
- func ClampModalWidth(width, screenWidth int) int
- func ExpandTabs(line string, tabWidth int) string
- func GetSelectionBgANSI() string
- func InjectCharacterRangeBackground(line string, startCol, endCol int) string
- func InjectSelectionBackground(s string) string
- func OverlayModal(background, modal string, width, height int) string
- func RenderButtonPair(confirmLabel, cancelLabel string, focusIdx, hoverIdx int) string
- func RenderDivider(height int) string
- func RenderScrollbar(params ScrollbarParams) string
- func ResolveButtonStyle(focusIdx, hoverIdx, btnIdx int) lipgloss.Style
- func SafeByteSlice(s string, byteStart, byteEnd int) string
- func SkeletonTick() tea.Cmd
- func TruncateMid(s string, width int, highlightRuneStart, highlightRuneEnd int) (string, int, int)
- func TruncateStart(s string, width int) string
- func TruncateString(s string, width int) string
- func VisualColAtRelativeX(expandedLine string, relX int) int
- func VisualSubstring(s string, startCol, endCol int) string
- type BrailleSpinner
- type ConfirmDialog
- type ScrollbarParams
- type SelectionPoint
- type SelectionState
- func (s *SelectionState) Clear()
- func (s *SelectionState) FinishDrag()
- func (s *SelectionState) GetLineSelectionCols(lineIdx int) (startCol, endCol int)
- func (s *SelectionState) HandleDrag(lineIdx, col int)
- func (s *SelectionState) HasSelection() bool
- func (s *SelectionState) IsLineSelected(lineIdx int) bool
- func (s *SelectionState) PrepareDrag(lineIdx, col int, viewRect mouse.Rect)
- func (s *SelectionState) SelectedText(lines []string, startLine int, tabWidth int) []string
- type Skeleton
- type SkeletonTickMsg
- type TruncateCache
- func (c *TruncateCache) Clear()
- func (c *TruncateCache) Size() int
- func (c *TruncateCache) Truncate(content string, width int, tail string) string
- func (c *TruncateCache) TruncateLeft(content string, offset int, tail string) string
- func (c *TruncateCache) TruncateLeftRight(content string, leftOffset int, width int) string
Constants ¶
const ( ModalWidthSmall = 40 // Simple confirmations ModalWidthMedium = 50 // Standard modals with inputs ModalWidthLarge = 60 // Modals with longer content )
Standard modal widths
const ( DimSequence = "\x1b[2m" ResetSequence = "\x1b[0m" )
DimSequence and ResetSequence are the raw ANSI codes used by DimStyle. Exported for testing.
const SkeletonTickInterval = 80 * time.Millisecond
SkeletonTickInterval is the animation frame rate (60fps feel).
Variables ¶
var AnsiResetRe = regexp.MustCompile(`\x1b\[0?m`)
AnsiResetRe matches ANSI reset sequences (both \x1b[0m and \x1b[m).
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 ¶
BytePosToRunePos converts a byte position to a rune position.
func CalculateModalWidth ¶
CalculateModalWidth returns an appropriate width based on content. Ensures width is within min/max bounds.
func ClampModalWidth ¶
ClampModalWidth clamps a width value between min and max screen bounds.
func ExpandTabs ¶
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 ¶
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 ¶
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 ¶
OverlayModal composites a modal on top of a dimmed background. The modal is centered, with dimmed background visible on all sides.
func RenderButtonPair ¶
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 ¶
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 ¶
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 ¶
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 ¶
SkeletonTick returns a command to start/continue the skeleton animation. Call this from your plugin's Start() or when enabling the skeleton.
func TruncateMid ¶
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 ¶
TruncateStart truncates the start of the string if it exceeds width. "..." + suffix
func TruncateString ¶
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 ¶
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 ¶
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) 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.
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 ¶
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.
type SkeletonTickMsg ¶
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.