mirror of
https://github.com/sst/opencode.git
synced 2025-08-28 17:04:10 +00:00
138 lines
2.6 KiB
Go
138 lines
2.6 KiB
Go
package util
|
|
|
|
import (
|
|
"math"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/lipgloss/v2"
|
|
"github.com/charmbracelet/lipgloss/v2/compat"
|
|
"github.com/sst/opencode/internal/styles"
|
|
)
|
|
|
|
var (
|
|
shimmerStart = time.Now()
|
|
trueColorSupport = hasTrueColor()
|
|
)
|
|
|
|
// Shimmer renders text with a moving foreground highlight.
|
|
// bg is the background color, dim is the base text color, bright is the highlight color.
|
|
func Shimmer(s string, bg compat.AdaptiveColor, _ compat.AdaptiveColor, _ compat.AdaptiveColor) string {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
|
|
runes := []rune(s)
|
|
n := len(runes)
|
|
if n == 0 {
|
|
return s
|
|
}
|
|
|
|
pad := 10
|
|
period := float64(n + pad*2)
|
|
sweep := 2.5
|
|
elapsed := time.Since(shimmerStart).Seconds()
|
|
pos := (math.Mod(elapsed, sweep) / sweep) * period
|
|
|
|
half := 2.0
|
|
|
|
type seg struct {
|
|
useHex bool
|
|
hex string
|
|
bold bool
|
|
faint bool
|
|
text string
|
|
}
|
|
segs := make([]seg, 0, n/4)
|
|
|
|
useHex := trueColorSupport
|
|
for i, r := range runes {
|
|
ip := float64(i + pad)
|
|
dist := math.Abs(ip - pos)
|
|
|
|
bold := false
|
|
faint := true
|
|
hex := ""
|
|
|
|
if dist <= half {
|
|
// Simple 3-level brightness based on distance
|
|
if dist <= half/3 {
|
|
// Center: brightest
|
|
bold = true
|
|
faint = false
|
|
if useHex {
|
|
hex = "#ffffff"
|
|
}
|
|
} else {
|
|
// Edge: medium bright
|
|
bold = false
|
|
faint = false
|
|
if useHex {
|
|
hex = "#cccccc"
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(segs) == 0 ||
|
|
segs[len(segs)-1].useHex != useHex ||
|
|
segs[len(segs)-1].hex != hex ||
|
|
segs[len(segs)-1].bold != bold ||
|
|
segs[len(segs)-1].faint != faint {
|
|
segs = append(segs, seg{useHex: useHex, hex: hex, bold: bold, faint: faint, text: string(r)})
|
|
} else {
|
|
segs[len(segs)-1].text += string(r)
|
|
}
|
|
}
|
|
|
|
baseStyle := styles.NewStyle().Background(bg)
|
|
var b strings.Builder
|
|
b.Grow(len(s) * 2)
|
|
for _, g := range segs {
|
|
st := baseStyle
|
|
if g.useHex && g.hex != "" {
|
|
c := compat.AdaptiveColor{Dark: lipgloss.Color(g.hex), Light: lipgloss.Color(g.hex)}
|
|
st = st.Foreground(c)
|
|
}
|
|
if g.bold {
|
|
st = st.Bold(true)
|
|
}
|
|
if g.faint {
|
|
st = st.Faint(true)
|
|
}
|
|
b.WriteString(st.Render(g.text))
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func hasTrueColor() bool {
|
|
c := strings.ToLower(os.Getenv("COLORTERM"))
|
|
return strings.Contains(c, "truecolor") || strings.Contains(c, "24bit")
|
|
}
|
|
|
|
func rgbHex(r, g, b int) string {
|
|
if r < 0 {
|
|
r = 0
|
|
}
|
|
if r > 255 {
|
|
r = 255
|
|
}
|
|
if g < 0 {
|
|
g = 0
|
|
}
|
|
if g > 255 {
|
|
g = 255
|
|
}
|
|
if b < 0 {
|
|
b = 0
|
|
}
|
|
if b > 255 {
|
|
b = 255
|
|
}
|
|
return "#" + hex2(r) + hex2(g) + hex2(b)
|
|
}
|
|
|
|
func hex2(v int) string {
|
|
const digits = "0123456789abcdef"
|
|
return string([]byte{digits[(v>>4)&0xF], digits[v&0xF]})
|
|
}
|