diff --git a/internal/ui/common/elements.go b/internal/ui/common/elements.go index 9c60a305d6d0c601efe2c57c363784d46eeda9f3..ff0bc751c4662323deeff04af425200127d586e7 100644 --- a/internal/ui/common/elements.go +++ b/internal/ui/common/elements.go @@ -193,7 +193,7 @@ func DialogTitle(t *styles.Styles, title string, width int, fromColor, toColor c remainingWidth := width - length if remainingWidth > 0 { lines := strings.Repeat(char, remainingWidth) - lines = styles.ApplyForegroundGrad(t, lines, fromColor, toColor) + lines = styles.ApplyForegroundGrad(t.Base, lines, fromColor, toColor) title = title + " " + lines } return title diff --git a/internal/ui/logo/example/main.go b/internal/ui/logo/example/main.go index 32a67d0d68ca885ac6426e6282ff916694d4d488..4d977d5020c94d32133449afc2edd5bf53e5d197 100644 --- a/internal/ui/logo/example/main.go +++ b/internal/ui/logo/example/main.go @@ -3,10 +3,13 @@ package main import ( "fmt" "math/rand/v2" + "os" "charm.land/lipgloss/v2" "github.com/charmbracelet/crush/internal/ui/logo" + "github.com/charmbracelet/crush/internal/ui/styles" "github.com/charmbracelet/x/exp/slice" + "github.com/charmbracelet/x/term" ) func renderLetterforms(stretch bool) string { @@ -42,8 +45,26 @@ func renderLetterforms(stretch bool) string { } func main() { + w, _, err := term.GetSize(os.Stdout.Fd()) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not get terminal size: %s", err) + } + + s := styles.DefaultStyles() + opts := logo.Opts{ + FieldColor: s.LogoFieldColor, + TitleColorA: s.LogoTitleColorA, + TitleColorB: s.LogoTitleColorB, + CharmColor: s.LogoCharmColor, + VersionColor: s.LogoVersionColor, + Width: w, + } + + lipgloss.Println(logo.Render(s.Base, "v1.0.0", false, opts)) + lipgloss.Println(logo.Render(s.Base, "v1.0.0", true, opts)) + fmt.Println(renderLetterforms(false)) - for range 10 { + for range 5 { fmt.Println(renderLetterforms(true)) } } diff --git a/internal/ui/logo/logo.go b/internal/ui/logo/logo.go index ac4ada2be171ece4150d95c0dccdec0c22d57247..847d366f42fe949e778c71eb5d03ef22854e6ff6 100644 --- a/internal/ui/logo/logo.go +++ b/internal/ui/logo/logo.go @@ -23,8 +23,9 @@ type Opts struct { TitleColorA color.Color // left gradient ramp point TitleColorB color.Color // right gradient ramp point CharmColor color.Color // Charm™ text color - VersionColor color.Color // Version text color + VersionColor color.Color // version text color Width int // width of the rendered logo, used for truncation + Hyper bool // whether it is Crush or Hypercrush } // Render renders the Crush logo. Set the argument to true to render the narrow @@ -32,7 +33,7 @@ type Opts struct { // // The compact argument determines whether it renders compact for the sidebar // or wider for the main pane. -func Render(s *styles.Styles, version string, compact bool, o Opts) string { +func Render(base lipgloss.Style, version string, compact bool, o Opts) string { const charm = " Charm™" fg := func(c color.Color, s string) string { @@ -52,12 +53,11 @@ func Render(s *styles.Styles, version string, compact bool, o Opts) string { if !compact { stretchIndex = cachedRandN(len(letterforms)) } - crush := renderWord(spacing, stretchIndex, letterforms...) crushWidth := lipgloss.Width(crush) b := new(strings.Builder) for r := range strings.SplitSeq(crush, "\n") { - fmt.Fprintln(b, styles.ApplyForegroundGrad(s, r, o.TitleColorA, o.TitleColorB)) + fmt.Fprintln(b, styles.ApplyForegroundGrad(base, r, o.TitleColorA, o.TitleColorB)) } crush = b.String() @@ -117,7 +117,7 @@ func Render(s *styles.Styles, version string, compact bool, o Opts) string { // smaller windows or sidebar usage. func SmallRender(t *styles.Styles, width int) string { title := t.Base.Foreground(t.Secondary).Render("Charm™") - title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t, "Crush", t.Secondary, t.Primary)) + title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Base, "Crush", t.Secondary, t.Primary)) remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush" if remainingWidth > 0 { lines := strings.Repeat("╱", remainingWidth) diff --git a/internal/ui/styles/grad.go b/internal/ui/styles/grad.go index 866a00fa501b48caa2a69f559efd7d45964cec97..c4f5177b251791ab61c3fe91e09ce0a9ef6c2fe2 100644 --- a/internal/ui/styles/grad.go +++ b/internal/ui/styles/grad.go @@ -5,7 +5,7 @@ import ( "image/color" "strings" - "github.com/lucasb-eyer/go-colorful" + "charm.land/lipgloss/v2" "github.com/rivo/uniseg" ) @@ -13,12 +13,12 @@ import ( // rendered with a horizontal gradient foreground from color1 to color2. Each // string in the returned slice corresponds to a grapheme cluster in the input // string. If bold is true, the rendered strings will be bolded. -func ForegroundGrad(t *Styles, input string, bold bool, color1, color2 color.Color) []string { +func ForegroundGrad(base lipgloss.Style, input string, bold bool, color1, color2 color.Color) []string { if input == "" { return []string{""} } if len(input) == 1 { - style := t.Base.Foreground(color1) + style := base.Foreground(color1) if bold { style.Bold(true) } @@ -30,9 +30,9 @@ func ForegroundGrad(t *Styles, input string, bold bool, color1, color2 color.Col clusters = append(clusters, string(gr.Runes())) } - ramp := blendColors(len(clusters), color1, color2) + ramp := lipgloss.Blend1D(len(clusters), color1, color2) for i, c := range ramp { - style := t.Base.Foreground(c) + style := base.Foreground(c) if bold { style.Bold(true) } @@ -43,12 +43,12 @@ func ForegroundGrad(t *Styles, input string, bold bool, color1, color2 color.Col // ApplyForegroundGrad renders a given string with a horizontal gradient // foreground. -func ApplyForegroundGrad(t *Styles, input string, color1, color2 color.Color) string { +func ApplyForegroundGrad(base lipgloss.Style, input string, color1, color2 color.Color) string { if input == "" { return "" } var o strings.Builder - clusters := ForegroundGrad(t, input, false, color1, color2) + clusters := ForegroundGrad(base, input, false, color1, color2) for _, c := range clusters { fmt.Fprint(&o, c) } @@ -57,61 +57,14 @@ func ApplyForegroundGrad(t *Styles, input string, color1, color2 color.Color) st // ApplyBoldForegroundGrad renders a given string with a horizontal gradient // foreground. -func ApplyBoldForegroundGrad(t *Styles, input string, color1, color2 color.Color) string { +func ApplyBoldForegroundGrad(base lipgloss.Style, input string, color1, color2 color.Color) string { if input == "" { return "" } var o strings.Builder - clusters := ForegroundGrad(t, input, true, color1, color2) + clusters := ForegroundGrad(base, input, true, color1, color2) for _, c := range clusters { fmt.Fprint(&o, c) } return o.String() } - -// blendColors returns a slice of colors blended between the given keys. -// Blending is done in Hcl to stay in gamut. -func blendColors(size int, stops ...color.Color) []color.Color { - if len(stops) < 2 { - return nil - } - - stopsPrime := make([]colorful.Color, len(stops)) - for i, k := range stops { - stopsPrime[i], _ = colorful.MakeColor(k) - } - - numSegments := len(stopsPrime) - 1 - blended := make([]color.Color, 0, size) - - // Calculate how many colors each segment should have. - segmentSizes := make([]int, numSegments) - baseSize := size / numSegments - remainder := size % numSegments - - // Distribute the remainder across segments. - for i := range numSegments { - segmentSizes[i] = baseSize - if i < remainder { - segmentSizes[i]++ - } - } - - // Generate colors for each segment. - for i := range numSegments { - c1 := stopsPrime[i] - c2 := stopsPrime[i+1] - segmentSize := segmentSizes[i] - - for j := range segmentSize { - var t float64 - if segmentSize > 1 { - t = float64(j) / float64(segmentSize-1) - } - c := c1.BlendHcl(c2, t) - blended = append(blended, c) - } - } - - return blended -}