@@ -3,7 +3,7 @@ package anim
5 "image/color"
5 "image/color"
6− "math/rand"
6+ "math/rand/v2"
@@ -41,7 +41,7 @@ type cyclingChar struct {
43func (c cyclingChar) randomRune() rune {
43func (c cyclingChar) randomRune() rune {
44− return (charRunes)[rand.Intn(len(charRunes))] //nolint:gosec
44+ return (charRunes)[rand.IntN(len(charRunes))] //nolint:gosec
47func (c cyclingChar) state(start time.Time) charState {
47func (c cyclingChar) state(start time.Time) charState {
@@ -137,7 +137,7 @@ func New(cyclingCharsSize uint, label string, opts ...animOption) Animation {
139 makeDelay := func(a int32, b time.Duration) time.Duration {
139 makeDelay := func(a int32, b time.Duration) time.Duration {
140− return time.Duration(rand.Int31n(a)) * (time.Millisecond * b) //nolint:gosec
140+ return time.Duration(rand.Int32N(a)) * (time.Millisecond * b) //nolint:gosec
143 makeInitialDelay := func() time.Duration {
143 makeInitialDelay := func() time.Duration {
@@ -226,22 +226,34 @@ func (a anim) ID() string {
228func (a *anim) updateChars(chars *[]cyclingChar) {
228func (a *anim) updateChars(chars *[]cyclingChar) {
229+ charSlice := *chars // dereference to avoid repeated pointer access
229− for i, c := range *chars {
230+ for i, c := range charSlice {
230 switch c.state(a.start) {
231 switch c.state(a.start) {
231 case charInitialState:
232 case charInitialState:
232− (*chars)[i].currentValue = '.'
233+ charSlice[i].currentValue = '.'
233 case charCyclingState:
234 case charCyclingState:
234− (*chars)[i].currentValue = c.randomRune()
235+ charSlice[i].currentValue = c.randomRune()
235 case charEndOfLifeState:
236 case charEndOfLifeState:
236− (*chars)[i].currentValue = c.finalValue
237+ charSlice[i].currentValue = c.finalValue
241// View renders the animation.
242// View renders the animation.
242func (a anim) View() tea.View {
243func (a anim) View() tea.View {
243− t := styles.CurrentTheme()
245+ t = styles.CurrentTheme()
244− var b strings.Builder
246+ b strings.Builder
249+ // Pre-allocate builder capacity to avoid reallocations.
250+ // Estimate: cycling chars + label chars + ellipsis + style overhead.
252+ bytesPerChar = 20 // ANSI styling
253+ bufferSize = 50 // ellipsis and safety margin
255+ estimatedCap := len(a.cyclingChars)*bytesPerChar + len(a.labelChars)*bytesPerChar + bufferSize
256+ b.Grow(estimatedCap)
246 for i, c := range a.cyclingChars {
258 for i, c := range a.cyclingChars {
247 if len(a.ramp) > i {
259 if len(a.ramp) > i {
@@ -258,7 +270,7 @@ func (a anim) View() tea.View {
258 textStyle.Render(string(c.currentValue)),
270 textStyle.Render(string(c.currentValue)),
261− return tea.NewView(b.String() + textStyle.Render(a.ellipsis.View()))
273+ b.WriteString(textStyle.Render(a.ellipsis.View()))
264 return tea.NewView(b.String())
276 return tea.NewView(b.String())