fix(ux): fix crush logo flicker on window resizes (#1338)

Andrey Nering created

Change summary

internal/tui/components/logo/logo.go |  5 ++---
internal/tui/components/logo/rand.go | 24 ++++++++++++++++++++++++
2 files changed, 26 insertions(+), 3 deletions(-)

Detailed changes

internal/tui/components/logo/logo.go 🔗

@@ -4,7 +4,6 @@ package logo
 import (
 	"fmt"
 	"image/color"
-	"math/rand/v2"
 	"strings"
 
 	"github.com/MakeNowJust/heredoc"
@@ -53,7 +52,7 @@ func Render(version string, compact bool, o Opts) string {
 	}
 	stretchIndex := -1 // -1 means no stretching.
 	if !compact {
-		stretchIndex = rand.IntN(len(letterforms))
+		stretchIndex = cachedRandN(len(letterforms))
 	}
 
 	crush := renderWord(spacing, stretchIndex, letterforms...)
@@ -337,7 +336,7 @@ func stretchLetterformPart(s string, p letterformProps) string {
 	}
 	n := p.width
 	if p.stretch {
-		n = rand.IntN(p.maxStretch-p.minStretch) + p.minStretch //nolint:gosec
+		n = cachedRandN(p.maxStretch-p.minStretch) + p.minStretch //nolint:gosec
 	}
 	parts := make([]string, n)
 	for i := range parts {

internal/tui/components/logo/rand.go 🔗

@@ -0,0 +1,24 @@
+package logo
+
+import (
+	"math/rand/v2"
+	"sync"
+)
+
+var (
+	randCaches   = make(map[int]int)
+	randCachesMu sync.Mutex
+)
+
+func cachedRandN(n int) int {
+	randCachesMu.Lock()
+	defer randCachesMu.Unlock()
+
+	if n, ok := randCaches[n]; ok {
+		return n
+	}
+
+	r := rand.IntN(n)
+	randCaches[n] = r
+	return r
+}