fix(tui): render a smaller logo on splash screen

Ayman Bagabas created

When we have a smaller window, we render a smaller version of the Crush
logo.

Change summary

internal/tui/components/chat/sidebar/sidebar.go | 32 ++++++------------
internal/tui/components/chat/splash/splash.go   | 21 ++++++++---
internal/tui/components/logo/logo.go            | 14 ++++++++
3 files changed, 40 insertions(+), 27 deletions(-)

Detailed changes

internal/tui/components/chat/sidebar/sidebar.go 🔗

@@ -114,12 +114,22 @@ func (m *sidebarCmp) View() string {
 	t := styles.CurrentTheme()
 	parts := []string{}
 
+	style := t.S().Base.
+		Width(m.width).
+		Height(m.height).
+		Padding(1)
+	if m.compactMode {
+		style = style.PaddingTop(0)
+	}
+
 	if !m.compactMode {
 		if m.height > LogoHeightBreakpoint {
 			parts = append(parts, m.logo)
 		} else {
 			// Use a smaller logo for smaller screens
-			parts = append(parts, m.smallerScreenLogo(), "")
+			parts = append(parts,
+				logo.SmallRender(m.width-style.GetHorizontalFrameSize()),
+				"")
 		}
 	}
 
@@ -159,13 +169,6 @@ func (m *sidebarCmp) View() string {
 		)
 	}
 
-	style := t.S().Base.
-		Width(m.width).
-		Height(m.height).
-		Padding(1)
-	if m.compactMode {
-		style = style.PaddingTop(0)
-	}
 	return style.Render(
 		lipgloss.JoinVertical(lipgloss.Left, parts...),
 	)
@@ -934,19 +937,6 @@ func (s *sidebarCmp) currentModelBlock() string {
 	)
 }
 
-func (m *sidebarCmp) smallerScreenLogo() string {
-	t := styles.CurrentTheme()
-	title := t.S().Base.Foreground(t.Secondary).Render("Charm™")
-	title += " " + styles.ApplyBoldForegroundGrad("CRUSH", t.Secondary, t.Primary)
-	remainingWidth := m.width - lipgloss.Width(title) - 3
-	if remainingWidth > 0 {
-		char := "╱"
-		lines := strings.Repeat(char, remainingWidth)
-		title += " " + t.S().Base.Foreground(t.Primary).Render(lines)
-	}
-	return title
-}
-
 // SetSession implements Sidebar.
 func (m *sidebarCmp) SetSession(session session.Session) tea.Cmd {
 	m.session = session

internal/tui/components/chat/splash/splash.go 🔗

@@ -134,10 +134,8 @@ func (s *splashCmp) Init() tea.Cmd {
 // SetSize implements SplashPage.
 func (s *splashCmp) SetSize(width int, height int) tea.Cmd {
 	s.height = height
-	if width != s.width {
-		s.width = width
-		s.logoRendered = s.logoBlock()
-	}
+	s.width = width
+	s.logoRendered = s.logoBlock()
 	// remove padding, logo height, gap, title space
 	s.listHeight = s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2) - s.logoGap() - 2
 	listWidth := min(60, width)
@@ -467,14 +465,25 @@ func (s *splashCmp) infoSection() string {
 
 func (s *splashCmp) logoBlock() string {
 	t := styles.CurrentTheme()
-	return t.S().Base.Padding(0, 2).Width(s.width).Render(
+	logoStyle := t.S().Base.Padding(0, 2).Width(s.width)
+	if s.width < 40 || s.height < 20 {
+		// If the width is too small, render a smaller version of the logo
+		// NOTE: 20 is not correct because [splashCmp.height] is not the
+		// *actual* window height, instead, it is the height of the splash
+		// component and that depends on other variables like compact mode and
+		// the height of the editor.
+		return logoStyle.Render(
+			logo.SmallRender(s.width - logoStyle.GetHorizontalFrameSize()),
+		)
+	}
+	return logoStyle.Render(
 		logo.Render(version.Version, false, logo.Opts{
 			FieldColor:   t.Primary,
 			TitleColorA:  t.Secondary,
 			TitleColorB:  t.Primary,
 			CharmColor:   t.Secondary,
 			VersionColor: t.Primary,
-			Width:        s.width - 4,
+			Width:        s.width - logoStyle.GetHorizontalFrameSize(),
 		}),
 	)
 }

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

@@ -110,6 +110,20 @@ func Render(version string, compact bool, o Opts) string {
 	return logo
 }
 
+// SmallRender renders a smaller version of the Crush logo, suitable for
+// smaller windows or sidebar usage.
+func SmallRender(width int) string {
+	t := styles.CurrentTheme()
+	title := t.S().Base.Foreground(t.Secondary).Render("Charm™")
+	title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad("Crush", t.Secondary, t.Primary))
+	remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
+	if remainingWidth > 0 {
+		lines := strings.Repeat("╱", remainingWidth)
+		title = fmt.Sprintf("%s %s", title, t.S().Base.Foreground(t.Primary).Render(lines))
+	}
+	return title
+}
+
 // renderWord renders letterforms to fork a word.
 func renderWord(spacing int, stretchRandomLetter bool, letterforms ...letterform) string {
 	if spacing < 0 {