Documentation
¶
Overview ¶
Package bullets provides a colorful terminal logger with bullet-style output. Inspired by goreleaser's logging output.
Index ¶
- Constants
- Variables
- func BatchUpdate(_ []*BulletHandle, updates map[*BulletHandle]struct{ ... })
- func Colorize(color, text string) string
- type ANSIEvent
- type ANSIEventType
- type AnimationFrame
- type BulletHandle
- func (h *BulletHandle) Error(msg string) *BulletHandle
- func (h *BulletHandle) GetState() HandleState
- func (h *BulletHandle) Progress(current, total int) *BulletHandle
- func (h *BulletHandle) Pulse(duration time.Duration, alternateMsg string)
- func (h *BulletHandle) SetState(state HandleState) *BulletHandle
- func (h *BulletHandle) Success(msg string) *BulletHandle
- func (h *BulletHandle) Update(level Level, msg string) *BulletHandle
- func (h *BulletHandle) UpdateBullet(bullet string) *BulletHandle
- func (h *BulletHandle) UpdateColor(color string) *BulletHandle
- func (h *BulletHandle) UpdateLevel(level Level) *BulletHandle
- func (h *BulletHandle) UpdateMessage(msg string) *BulletHandle
- func (h *BulletHandle) Warning(msg string) *BulletHandle
- func (h *BulletHandle) WithField(key string, value interface{}) *BulletHandle
- func (h *BulletHandle) WithFields(fields map[string]interface{}) *BulletHandle
- type CursorPosition
- type HandleChain
- type HandleGroup
- func (hg *HandleGroup) Add(handle *BulletHandle)
- func (hg *HandleGroup) Clear()
- func (hg *HandleGroup) ErrorAll(msg string)
- func (hg *HandleGroup) Get(index int) *BulletHandle
- func (hg *HandleGroup) Size() int
- func (hg *HandleGroup) SuccessAll(msg string)
- func (hg *HandleGroup) UpdateAll(level Level, msg string)
- func (hg *HandleGroup) UpdateEach(updates map[int]struct{ ... })
- type HandleState
- type Level
- type Logger
- func (l *Logger) Debug(msg string)
- func (l *Logger) Debugf(format string, args ...interface{})
- func (l *Logger) DecreasePadding()
- func (l *Logger) Error(msg string)
- func (l *Logger) Errorf(format string, args ...interface{})
- func (l *Logger) Fatal(msg string)
- func (l *Logger) Fatalf(format string, args ...interface{})
- func (l *Logger) GetLevel() Level
- func (l *Logger) IncreasePadding()
- func (l *Logger) Info(msg string)
- func (l *Logger) Infof(format string, args ...interface{})
- func (l *Logger) Ln()
- func (l *Logger) ResetPadding()
- func (l *Logger) SetBullet(level Level, bullet string)
- func (l *Logger) SetBullets(bullets map[Level]string)
- func (l *Logger) SetLevel(level Level)
- func (l *Logger) SetUseSpecialBullets(use bool)
- func (l *Logger) Spinner(msg string) *Spinner
- func (l *Logger) SpinnerBounce(msg string) *Spinner
- func (l *Logger) SpinnerCircle(msg string) *Spinner
- func (l *Logger) SpinnerDots(msg string) *Spinner
- func (l *Logger) SpinnerWithFrames(msg string, frames []string) *Spinner
- func (l *Logger) Step(msg string) func()
- func (l *Logger) Success(msg string)
- func (l *Logger) Successf(format string, args ...interface{})
- func (l *Logger) Warn(msg string)
- func (l *Logger) Warnf(format string, args ...interface{})
- func (l *Logger) WithError(err error) *Logger
- func (l *Logger) WithField(key string, value interface{}) *Logger
- func (l *Logger) WithFields(fields map[string]interface{}) *Logger
- type Spinner
- type SpinnerCoordinator
- type SpinnerState
- type SpinnerTestCapture
- func (s *SpinnerTestCapture) CountEventType(eventType ANSIEventType) int
- func (s *SpinnerTestCapture) DumpEvents(t *testing.T, maxEvents int)
- func (s *SpinnerTestCapture) ExtractFrames() []AnimationFrame
- func (s *SpinnerTestCapture) GetCursorHistory() []CursorPosition
- func (s *SpinnerTestCapture) GetEvents() []ANSIEvent
- func (s *SpinnerTestCapture) GetMoveDownValues() []int
- func (s *SpinnerTestCapture) GetMoveUpValues() []int
- func (s *SpinnerTestCapture) GetRawOutput() string
- func (s *SpinnerTestCapture) ValidateCursorStability(t *testing.T) bool
- func (s *SpinnerTestCapture) ValidateNoBlankLines(t *testing.T) bool
- func (s *SpinnerTestCapture) ValidateNoPositionDrift(t *testing.T) bool
- func (s *SpinnerTestCapture) Write(p []byte) (int, error)
- type UpdatableLogger
- func (ul *UpdatableLogger) Debug(msg string)
- func (ul *UpdatableLogger) DebugHandle(msg string) *BulletHandle
- func (ul *UpdatableLogger) Error(msg string)
- func (ul *UpdatableLogger) ErrorHandle(msg string) *BulletHandle
- func (ul *UpdatableLogger) IncrementLineCount()
- func (ul *UpdatableLogger) Info(msg string)
- func (ul *UpdatableLogger) InfoHandle(msg string) *BulletHandle
- func (ul *UpdatableLogger) Success(msg string)
- func (ul *UpdatableLogger) Warn(msg string)
- func (ul *UpdatableLogger) WarnHandle(msg string) *BulletHandle
Constants ¶
const ( Reset = reset // Exported version // Exported color constants for public use. ColorBlack = black ColorRed = red ColorGreen = green ColorYellow = yellow ColorBlue = blue ColorMagenta = magenta ColorCyan = cyan ColorWhite = white // Exported bright color constants. ColorBrightBlack = brightBlack ColorBrightRed = brightRed ColorBrightGreen = brightGreen ColorBrightYellow = brightYellow ColorBrightBlue = brightBlue ColorBrightMagenta = brightMagenta ColorBrightCyan = brightCyan ColorBrightWhite = brightWhite // Exported style constants. StyleBold = bold StyleDim = dim StyleItalic = italic StyleUnderline = underline )
ANSI color codes for terminal output.
Variables ¶
var ErrInvalidLevel = errors.New("invalid log level")
ErrInvalidLevel is returned when parsing an invalid level string.
Functions ¶
func BatchUpdate ¶
func BatchUpdate(_ []*BulletHandle, updates map[*BulletHandle]struct { Level Level Message string })
BatchUpdate allows updating multiple handles at once. Note: The handles parameter is currently unused but kept for API compatibility.
Types ¶
type ANSIEvent ¶ added in v0.3.0
type ANSIEvent struct {
Timestamp time.Time
Raw string
Type ANSIEventType
Value int // For move operations
Text string // For text content
}
ANSIEvent represents a parsed ANSI escape sequence or text event.
type ANSIEventType ¶ added in v0.3.0
type ANSIEventType string
ANSIEventType categorizes ANSI events.
const ( EventMoveUp ANSIEventType = "moveUp" EventMoveDown ANSIEventType = "moveDown" EventClearLine ANSIEventType = "clearLine" EventMoveToCol ANSIEventType = "moveToCol" EventText ANSIEventType = "text" EventNewline ANSIEventType = "newline" EventUnknown ANSIEventType = "unknown" )
ANSI event type constants.
type AnimationFrame ¶ added in v0.3.0
type AnimationFrame struct {
Timestamp time.Time
SpinnerStates []SpinnerState
CursorPos CursorPosition
}
AnimationFrame represents a complete animation frame with all spinner states.
type BulletHandle ¶
type BulletHandle struct {
// contains filtered or unexported fields
}
BulletHandle represents a handle to an updatable bullet.
func (*BulletHandle) Error ¶
func (h *BulletHandle) Error(msg string) *BulletHandle
Error updates the bullet to show an error.
func (*BulletHandle) GetState ¶
func (h *BulletHandle) GetState() HandleState
GetState returns the current state of the handle.
func (*BulletHandle) Progress ¶
func (h *BulletHandle) Progress(current, total int) *BulletHandle
Progress updates the bullet to show progress.
func (*BulletHandle) Pulse ¶
func (h *BulletHandle) Pulse(duration time.Duration, alternateMsg string)
Pulse creates a pulsing effect by alternating between two states.
func (*BulletHandle) SetState ¶
func (h *BulletHandle) SetState(state HandleState) *BulletHandle
SetState sets the complete state of the handle.
func (*BulletHandle) Success ¶
func (h *BulletHandle) Success(msg string) *BulletHandle
Success updates the bullet to show success.
func (*BulletHandle) Update ¶
func (h *BulletHandle) Update(level Level, msg string) *BulletHandle
Update updates the bullet with a new message and level.
func (*BulletHandle) UpdateBullet ¶
func (h *BulletHandle) UpdateBullet(bullet string) *BulletHandle
UpdateBullet updates just the bullet symbol.
func (*BulletHandle) UpdateColor ¶
func (h *BulletHandle) UpdateColor(color string) *BulletHandle
UpdateColor updates just the color of the bullet.
func (*BulletHandle) UpdateLevel ¶
func (h *BulletHandle) UpdateLevel(level Level) *BulletHandle
UpdateLevel updates just the level (and thus color/bullet).
func (*BulletHandle) UpdateMessage ¶
func (h *BulletHandle) UpdateMessage(msg string) *BulletHandle
UpdateMessage updates just the message text.
func (*BulletHandle) Warning ¶
func (h *BulletHandle) Warning(msg string) *BulletHandle
Warning updates the bullet to show a warning.
func (*BulletHandle) WithField ¶
func (h *BulletHandle) WithField(key string, value interface{}) *BulletHandle
WithField adds a field to this bullet.
func (*BulletHandle) WithFields ¶
func (h *BulletHandle) WithFields(fields map[string]interface{}) *BulletHandle
WithFields adds multiple fields to this bullet.
type CursorPosition ¶ added in v0.3.0
CursorPosition tracks the current cursor position.
type HandleChain ¶
type HandleChain struct {
// contains filtered or unexported fields
}
HandleChain allows chaining updates to multiple handles.
func (*HandleChain) Error ¶
func (hc *HandleChain) Error(msg string) *HandleChain
Error marks all handles in the chain as error.
func (*HandleChain) Success ¶
func (hc *HandleChain) Success(msg string) *HandleChain
Success marks all handles in the chain as success.
func (*HandleChain) Update ¶
func (hc *HandleChain) Update(level Level, msg string) *HandleChain
Update updates all handles in the chain.
func (*HandleChain) WithField ¶
func (hc *HandleChain) WithField(key string, value interface{}) *HandleChain
WithField adds a field to all handles in the chain.
type HandleGroup ¶
type HandleGroup struct {
// contains filtered or unexported fields
}
HandleGroup manages a group of related handles.
func NewHandleGroup ¶
func NewHandleGroup(handles ...*BulletHandle) *HandleGroup
NewHandleGroup creates a new handle group.
func (*HandleGroup) Add ¶
func (hg *HandleGroup) Add(handle *BulletHandle)
Add adds a handle to the group.
func (*HandleGroup) Clear ¶
func (hg *HandleGroup) Clear()
Clear removes all handles from the group.
func (*HandleGroup) ErrorAll ¶
func (hg *HandleGroup) ErrorAll(msg string)
ErrorAll marks all handles as error.
func (*HandleGroup) Get ¶
func (hg *HandleGroup) Get(index int) *BulletHandle
Get returns the handle at the specified index.
func (*HandleGroup) Size ¶
func (hg *HandleGroup) Size() int
Size returns the number of handles in the group.
func (*HandleGroup) SuccessAll ¶
func (hg *HandleGroup) SuccessAll(msg string)
SuccessAll marks all handles as success.
func (*HandleGroup) UpdateAll ¶
func (hg *HandleGroup) UpdateAll(level Level, msg string)
UpdateAll updates all handles in the group.
func (*HandleGroup) UpdateEach ¶
func (hg *HandleGroup) UpdateEach(updates map[int]struct { Level Level Message string })
UpdateEach updates each handle with a different message.
type HandleState ¶
type HandleState struct {
Level Level
Message string
Color string
Bullet string
Fields map[string]interface{}
}
HandleState represents the state of a bullet handle.
type Level ¶
type Level uint32
Level represents a log level.
func MustParseLevel ¶
MustParseLevel parses a level string or panics.
func ParseLevel ¶
ParseLevel parses a level string into a Level.
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger represents a logger with configurable level and output.
func (*Logger) DecreasePadding ¶
func (l *Logger) DecreasePadding()
DecreasePadding decreases the indentation level.
func (*Logger) IncreasePadding ¶
func (l *Logger) IncreasePadding()
IncreasePadding increases the indentation level.
func (*Logger) Ln ¶ added in v0.3.0
func (l *Logger) Ln()
Ln prints a blank line without indentation. This method always outputs regardless of the log level.
func (*Logger) ResetPadding ¶
func (l *Logger) ResetPadding()
ResetPadding resets the indentation to zero.
func (*Logger) SetBullet ¶
SetBullet sets a custom bullet symbol for a specific log level. Custom bullets take priority over special bullets.
func (*Logger) SetBullets ¶
SetBullets sets custom bullet symbols for multiple log levels. Custom bullets take priority over special bullets.
func (*Logger) SetUseSpecialBullets ¶
SetUseSpecialBullets enables or disables special bullet symbols (✓, ✗, ⚠). When disabled (default), all levels use the circle bullet (●) with level-specific colors.
func (*Logger) Spinner ¶
Spinner creates and starts a spinner with default Braille dots animation.
Multiple spinners can run concurrently with automatic coordination. The spinner animates until stopped with Stop(), Success(), Error(), or Replace().
Example:
spinner := logger.Spinner("Processing data")
// ... do work ...
spinner.Success("Processing complete")
In TTY mode, the spinner animates in-place. In non-TTY mode (logs, CI/CD), it displays as a static message.
Thread-safe: Multiple spinners can be created from different goroutines.
func (*Logger) SpinnerBounce ¶
SpinnerBounce creates a spinner with bouncing dot pattern.
Creates a smooth bouncing animation effect using Braille dots that appear to bounce vertically. The animation uses the default speed.
Thread-safe: Multiple spinners can be created from different goroutines.
func (*Logger) SpinnerCircle ¶
SpinnerCircle creates a spinner with growing/shrinking circle pattern.
Creates a glassy circular rotation effect using quarter-circle characters. The animation is slower than the default Braille dots for a more relaxed feel.
Thread-safe: Multiple spinners can be created from different goroutines.
func (*Logger) SpinnerDots ¶
SpinnerDots creates a spinner with rotating Braille dots pattern.
This is the default spinner style with smooth dot transitions. Identical to Spinner().
Thread-safe: Multiple spinners can be created from different goroutines.
func (*Logger) SpinnerWithFrames ¶
SpinnerWithFrames creates and starts a spinner with custom animation frames.
Frames will cycle through the provided slice of strings. If frames is empty, defaults to the standard Braille dots pattern.
Example:
frames := []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}
spinner := logger.SpinnerWithFrames("Compiling", frames)
// ... do work ...
spinner.Success("Compilation complete")
Thread-safe: Multiple spinners can be created from different goroutines.
func (*Logger) Step ¶
Step logs a step message with timing information. It returns a function that should be called when the step is complete.
func (*Logger) WithFields ¶
WithFields returns a new logger with the given fields added.
type Spinner ¶
type Spinner struct {
// contains filtered or unexported fields
}
Spinner represents an animated spinner that can be stopped and replaced.
Spinners provide visual feedback for long-running operations. Multiple spinners can run concurrently with automatic coordination via SpinnerCoordinator.
In TTY mode, spinners animate in-place using ANSI escape codes. In non-TTY mode, they display as static messages for compatibility with logs and CI/CD systems.
Example usage:
logger := bullets.New(os.Stdout)
spinner := logger.SpinnerCircle("Processing data")
// ... do work ...
spinner.Success("Processing complete")
Thread Safety:
All spinner methods are thread-safe and can be called from multiple goroutines. The coordinator ensures proper serialization of terminal updates.
func (*Spinner) Error ¶
Error stops the spinner and replaces it with an error message.
The completion message is displayed with an error bullet (red color). The bullet symbol respects custom bullets and special bullet settings.
In TTY mode, the spinner line is overwritten with the final message. In non-TTY mode, a new line is printed with the completion message.
Example:
spinner := logger.SpinnerCircle("Connecting")
// ... do work ...
spinner.Error("Connection failed: timeout")
Thread-safe: Can be called from any goroutine.
func (*Spinner) Fail ¶
Fail is an alias for Error.
This method behaves identically to Error() and is provided for convenience. Thread-safe: Can be called from any goroutine.
func (*Spinner) Replace ¶
Replace stops the spinner and replaces it with a custom message at info level.
The completion message is displayed with an info bullet (cyan color). The bullet symbol respects custom bullets and special bullet settings.
Use Replace when the operation completes but you want to show a custom message that doesn't imply success or failure.
Example:
spinner := logger.SpinnerCircle("Processing")
// ... do work ...
spinner.Replace("Processed 1000 records in 5.2s")
Thread-safe: Can be called from any goroutine.
func (*Spinner) Stop ¶
func (s *Spinner) Stop()
Stop stops the spinner and clears the line without displaying a completion message.
This method immediately halts the animation and unregisters the spinner from the coordinator. The spinner cannot be reused after calling Stop().
Thread-safe: Can be called from any goroutine.
func (*Spinner) Success ¶
Success stops the spinner and replaces it with a success message.
The completion message is displayed with a success bullet (green color). The bullet symbol respects custom bullets and special bullet settings.
In TTY mode, the spinner line is overwritten with the final message. In non-TTY mode, a new line is printed with the completion message.
Example:
spinner := logger.SpinnerCircle("Connecting")
// ... do work ...
spinner.Success("Connected successfully")
Thread-safe: Can be called from any goroutine.
func (*Spinner) UpdateText ¶ added in v0.4.0
UpdateText updates the spinner's message text while keeping it running.
The new message will be displayed on the next animation frame. In TTY mode, the spinner line is updated in-place. In non-TTY mode, the update is silently ignored (no additional output).
Calling UpdateText() on a stopped spinner is safe (idempotent). Multiple calls to UpdateText() simply override the message with the latest value.
Example:
spinner := logger.Spinner("Processing items")
for i := 1; i <= 100; i++ {
spinner.UpdateText(fmt.Sprintf("Processing items (%d/100)", i))
time.Sleep(10 * time.Millisecond)
}
spinner.Success("All items processed")
Thread-safe: Can be called from any goroutine.
type SpinnerCoordinator ¶ added in v0.2.0
type SpinnerCoordinator struct {
// contains filtered or unexported fields
}
SpinnerCoordinator manages all spinner instances and coordinates their output.
The coordinator implements a centralized pattern where a single goroutine handles all spinner animations and updates. This eliminates timing issues and ensures smooth, flicker-free animations even with multiple concurrent spinners.
Architecture:
- Central ticker goroutine updates all active spinners (80ms interval)
- Channel-based communication for thread-safe spinner updates
- Automatic line number allocation and recalculation
- Unified TTY detection for consistent behavior
The coordinator is automatically created by Logger and managed internally. Users don't need to interact with it directly.
Thread Safety:
All coordinator methods are thread-safe and can be called from multiple goroutines. Internal state is protected by mutexes and channel synchronization.
type SpinnerState ¶ added in v0.3.0
SpinnerState represents the state of a single spinner at a point in time.
type SpinnerTestCapture ¶ added in v0.3.0
type SpinnerTestCapture struct {
// contains filtered or unexported fields
}
SpinnerTestCapture extends ansiCapture with comprehensive testing utilities.
func NewSpinnerTestCapture ¶ added in v0.3.0
func NewSpinnerTestCapture() *SpinnerTestCapture
NewSpinnerTestCapture creates a new test capture utility.
func (*SpinnerTestCapture) CountEventType ¶ added in v0.3.0
func (s *SpinnerTestCapture) CountEventType(eventType ANSIEventType) int
CountEventType counts occurrences of a specific event type.
func (*SpinnerTestCapture) DumpEvents ¶ added in v0.3.0
func (s *SpinnerTestCapture) DumpEvents(t *testing.T, maxEvents int)
DumpEvents prints all captured events for debugging.
func (*SpinnerTestCapture) ExtractFrames ¶ added in v0.3.0
func (s *SpinnerTestCapture) ExtractFrames() []AnimationFrame
ExtractFrames analyzes events to identify distinct animation frames.
func (*SpinnerTestCapture) GetCursorHistory ¶ added in v0.3.0
func (s *SpinnerTestCapture) GetCursorHistory() []CursorPosition
GetCursorHistory returns the complete cursor movement history.
func (*SpinnerTestCapture) GetEvents ¶ added in v0.3.0
func (s *SpinnerTestCapture) GetEvents() []ANSIEvent
GetEvents returns a thread-safe copy of captured events.
func (*SpinnerTestCapture) GetMoveDownValues ¶ added in v0.3.0
func (s *SpinnerTestCapture) GetMoveDownValues() []int
GetMoveDownValues extracts all moveDown values for pattern analysis.
func (*SpinnerTestCapture) GetMoveUpValues ¶ added in v0.3.0
func (s *SpinnerTestCapture) GetMoveUpValues() []int
GetMoveUpValues extracts all moveUp values for pattern analysis.
func (*SpinnerTestCapture) GetRawOutput ¶ added in v0.3.0
func (s *SpinnerTestCapture) GetRawOutput() string
GetRawOutput returns the complete raw output.
func (*SpinnerTestCapture) ValidateCursorStability ¶ added in v0.3.0
func (s *SpinnerTestCapture) ValidateCursorStability(t *testing.T) bool
ValidateCursorStability checks that cursor movements are consistent.
func (*SpinnerTestCapture) ValidateNoBlankLines ¶ added in v0.3.0
func (s *SpinnerTestCapture) ValidateNoBlankLines(t *testing.T) bool
ValidateNoBlankLines checks that no extra blank lines exist in output.
func (*SpinnerTestCapture) ValidateNoPositionDrift ¶ added in v0.3.0
func (s *SpinnerTestCapture) ValidateNoPositionDrift(t *testing.T) bool
ValidateNoPositionDrift checks that line positions remain stable over time.
type UpdatableLogger ¶
type UpdatableLogger struct {
*Logger
// contains filtered or unexported fields
}
UpdatableLogger wraps a regular Logger and provides updatable bullet functionality.
func NewUpdatable ¶
func NewUpdatable(w io.Writer) *UpdatableLogger
NewUpdatable creates a new updatable logger.
func (*UpdatableLogger) Debug ¶
func (ul *UpdatableLogger) Debug(msg string)
Debug logs a debug message and increments line count.
func (*UpdatableLogger) DebugHandle ¶
func (ul *UpdatableLogger) DebugHandle(msg string) *BulletHandle
DebugHandle logs a debug message and returns a handle for updates.
func (*UpdatableLogger) Error ¶
func (ul *UpdatableLogger) Error(msg string)
Error logs an error message and increments line count.
func (*UpdatableLogger) ErrorHandle ¶
func (ul *UpdatableLogger) ErrorHandle(msg string) *BulletHandle
ErrorHandle logs an error message and returns a handle for updates.
func (*UpdatableLogger) IncrementLineCount ¶
func (ul *UpdatableLogger) IncrementLineCount()
IncrementLineCount increments the line count (called by regular log methods).
func (*UpdatableLogger) Info ¶
func (ul *UpdatableLogger) Info(msg string)
Info logs an info message and increments line count.
func (*UpdatableLogger) InfoHandle ¶
func (ul *UpdatableLogger) InfoHandle(msg string) *BulletHandle
InfoHandle logs an info message and returns a handle for updates.
func (*UpdatableLogger) Success ¶
func (ul *UpdatableLogger) Success(msg string)
Success logs a success message and increments line count.
func (*UpdatableLogger) Warn ¶
func (ul *UpdatableLogger) Warn(msg string)
Warn logs a warning message and increments line count.
func (*UpdatableLogger) WarnHandle ¶
func (ul *UpdatableLogger) WarnHandle(msg string) *BulletHandle
WarnHandle logs a warning message and returns a handle for updates.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Package main demonstrates the basic usage of the bullets logger.
|
Package main demonstrates the basic usage of the bullets logger. |
|
spinner
command
Package main demonstrates concurrent spinner usage with the bullets logger.
|
Package main demonstrates concurrent spinner usage with the bullets logger. |
|
spinner2
command
Package main demonstrates the basic usage of the bullets logger.
|
Package main demonstrates the basic usage of the bullets logger. |
|
spinner_stress
command
Package main demonstrates high-concurrency spinner usage with the bullets logger.
|
Package main demonstrates high-concurrency spinner usage with the bullets logger. |
|
updatable
command
Package main demonstrates updatable bullet functionality with real-time updates.
|
Package main demonstrates updatable bullet functionality with real-time updates. |
