on main help sign in
Merge pull request #235 from charmbracelet/smaller-logo
3247a8cKujtim Hoxha
internal/tui/components/chat/chat.go+2−2
@@ -107,7 +107,7 @@ func (m *messageListCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
107func (m *messageListCmp) View() string {
107func (m *messageListCmp) View() string {
108 t := styles.CurrentTheme()
108 t := styles.CurrentTheme()
109 return t.S().Base.
109 return t.S().Base.
110− Padding(1).
110+ Padding(1, 1, 0, 1).
111 Width(m.width).
111 Width(m.width).
112 Height(m.height).
112 Height(m.height).
113 Render(
113 Render(
@@ -508,7 +508,7 @@ func (m *messageListCmp) GetSize() (int, int) {
508func (m *messageListCmp) SetSize(width int, height int) tea.Cmd {
508func (m *messageListCmp) SetSize(width int, height int) tea.Cmd {
509 m.width = width
509 m.width = width
510 m.height = height
510 m.height = height
511− return m.listCmp.SetSize(width-2, height-2) // for padding
511+ return m.listCmp.SetSize(width-2, height-1) // for padding
512}
512}
513
513
514// Blur implements MessageListCmp.
514// Blur implements MessageListCmp.
internal/tui/components/chat/sidebar/sidebar.go+11−21
@@ -114,12 +114,22 @@ func (m *sidebarCmp) View() string {
114 t := styles.CurrentTheme()
114 t := styles.CurrentTheme()
115 parts := []string{}
115 parts := []string{}
116
116
117+ style := t.S().Base.
118+ Width(m.width).
119+ Height(m.height).
120+ Padding(1)
121+ if m.compactMode {
122+ style = style.PaddingTop(0)
123+ }
124+
117 if !m.compactMode {
125 if !m.compactMode {
118 if m.height > LogoHeightBreakpoint {
126 if m.height > LogoHeightBreakpoint {
119 parts = append(parts, m.logo)
127 parts = append(parts, m.logo)
120 } else {
128 } else {
121 // Use a smaller logo for smaller screens
129 // Use a smaller logo for smaller screens
122− parts = append(parts, m.smallerScreenLogo(), "")
130+ parts = append(parts,
131+ logo.SmallRender(m.width-style.GetHorizontalFrameSize()),
132+ "")
123 }
133 }
124 }
134 }
125
135
@@ -159,13 +169,6 @@ func (m *sidebarCmp) View() string {
159 )
169 )
160 }
170 }
161
171
162− style := t.S().Base.
163− Width(m.width).
164− Height(m.height).
165− Padding(1)
166− if m.compactMode {
167− style = style.PaddingTop(0)
168− }
169 return style.Render(
172 return style.Render(
170 lipgloss.JoinVertical(lipgloss.Left, parts...),
173 lipgloss.JoinVertical(lipgloss.Left, parts...),
171 )
174 )
@@ -934,19 +937,6 @@ func (s *sidebarCmp) currentModelBlock() string {
934 )
937 )
935}
938}
936
939
937−func (m *sidebarCmp) smallerScreenLogo() string {
938− t := styles.CurrentTheme()
939− title := t.S().Base.Foreground(t.Secondary).Render("Charm™")
940− title += " " + styles.ApplyBoldForegroundGrad("CRUSH", t.Secondary, t.Primary)
941− remainingWidth := m.width - lipgloss.Width(title) - 3
942− if remainingWidth > 0 {
943− char := "╱"
944− lines := strings.Repeat(char, remainingWidth)
945− title += " " + t.S().Base.Foreground(t.Primary).Render(lines)
946− }
947− return title
948−}
949−
950// SetSession implements Sidebar.
940// SetSession implements Sidebar.
951func (m *sidebarCmp) SetSession(session session.Session) tea.Cmd {
941func (m *sidebarCmp) SetSession(session session.Session) tea.Cmd {
952 m.session = session
942 m.session = session
internal/tui/components/chat/splash/splash.go+28−5
@@ -143,9 +143,11 @@ func (s *splashCmp) Init() tea.Cmd {
143
143
144// SetSize implements SplashPage.
144// SetSize implements SplashPage.
145func (s *splashCmp) SetSize(width int, height int) tea.Cmd {
145func (s *splashCmp) SetSize(width int, height int) tea.Cmd {
146+ wasSmallScreen := s.isSmallScreen()
147+ rerenderLogo := width != s.width
146 s.height = height
148 s.height = height
147− if width != s.width {
149+ s.width = width
148− s.width = width
150+ if rerenderLogo || wasSmallScreen != s.isSmallScreen() {
149 s.logoRendered = s.logoBlock()
151 s.logoRendered = s.logoBlock()
150 }
152 }
151 // remove padding, logo height, gap, title space
153 // remove padding, logo height, gap, title space
@@ -541,9 +543,19 @@ func (s *splashCmp) Cursor() *tea.Cursor {
541 return nil
543 return nil
542}
544}
543
545
546+func (s *splashCmp) isSmallScreen() bool {
547+ // Consider a screen small if either the width is less than 40 or if the
548+ // height is less than 20
549+ return s.width < 55 || s.height < 20
550+}
551+
544func (s *splashCmp) infoSection() string {
552func (s *splashCmp) infoSection() string {
545 t := styles.CurrentTheme()
553 t := styles.CurrentTheme()
546− return t.S().Base.PaddingLeft(2).Render(
554+ infoStyle := t.S().Base.PaddingLeft(2)
555+ if s.isSmallScreen() {
556+ infoStyle = infoStyle.MarginTop(1)
557+ }
558+ return infoStyle.Render(
547 lipgloss.JoinVertical(
559 lipgloss.JoinVertical(
548 lipgloss.Left,
560 lipgloss.Left,
549 s.cwd(),
561 s.cwd(),
@@ -556,14 +568,25 @@ func (s *splashCmp) infoSection() string {
556
568
557func (s *splashCmp) logoBlock() string {
569func (s *splashCmp) logoBlock() string {
558 t := styles.CurrentTheme()
570 t := styles.CurrentTheme()
559− return t.S().Base.Padding(0, 2).Width(s.width).Render(
571+ logoStyle := t.S().Base.Padding(0, 2).Width(s.width)
572+ if s.isSmallScreen() {
573+ // If the width is too small, render a smaller version of the logo
574+ // NOTE: 20 is not correct because [splashCmp.height] is not the
575+ // *actual* window height, instead, it is the height of the splash
576+ // component and that depends on other variables like compact mode and
577+ // the height of the editor.
578+ return logoStyle.Render(
579+ logo.SmallRender(s.width - logoStyle.GetHorizontalFrameSize()),
580+ )
581+ }
582+ return logoStyle.Render(
560 logo.Render(version.Version, false, logo.Opts{
583 logo.Render(version.Version, false, logo.Opts{
561 FieldColor: t.Primary,
584 FieldColor: t.Primary,
562 TitleColorA: t.Secondary,
585 TitleColorA: t.Secondary,
563 TitleColorB: t.Primary,
586 TitleColorB: t.Primary,
564 CharmColor: t.Secondary,
587 CharmColor: t.Secondary,
565 VersionColor: t.Primary,
588 VersionColor: t.Primary,
566− Width: s.width - 4,
589+ Width: s.width - logoStyle.GetHorizontalFrameSize(),
567 }),
590 }),
568 )
591 )
569}
592}
internal/tui/components/logo/logo.go+25−9
@@ -44,13 +44,19 @@ func Render(version string, compact bool, o Opts) string {
44
44
45 // Title.
45 // Title.
46 const spacing = 1
46 const spacing = 1
47− crush := renderWord(spacing, !compact,
47+ letterforms := []letterform{
48 letterC,
48 letterC,
49 letterR,
49 letterR,
50 letterU,
50 letterU,
51 letterSStylized,
51 letterSStylized,
52 letterH,
52 letterH,
53− )
53+ }
54+ stretchIndex := -1 // -1 means no stretching.
55+ if !compact {
56+ stretchIndex = rand.IntN(len(letterforms))
57+ }
58+
59+ crush := renderWord(spacing, stretchIndex, letterforms...)
54 crushWidth := lipgloss.Width(crush)
60 crushWidth := lipgloss.Width(crush)
55 b := new(strings.Builder)
61 b := new(strings.Builder)
56 for r := range strings.SplitSeq(crush, "\n") {
62 for r := range strings.SplitSeq(crush, "\n") {
@@ -110,8 +116,23 @@ func Render(version string, compact bool, o Opts) string {
110 return logo
116 return logo
111}
117}
112
118
119+// SmallRender renders a smaller version of the Crush logo, suitable for
120+// smaller windows or sidebar usage.
121+func SmallRender(width int) string {
122+ t := styles.CurrentTheme()
123+ title := t.S().Base.Foreground(t.Secondary).Render("Charm™")
124+ title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad("Crush", t.Secondary, t.Primary))
125+ remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
126+ if remainingWidth > 0 {
127+ lines := strings.Repeat("╱", remainingWidth)
128+ title = fmt.Sprintf("%s %s", title, t.S().Base.Foreground(t.Primary).Render(lines))
129+ }
130+ return title
131+}
132+
113−// renderWord renders letterforms to fork a word.
133+// renderWord renders letterforms to fork a word. stretchIndex is the index of
134+// the letter to stretch, or -1 if no letter should be stretched.
114−func renderWord(spacing int, stretchRandomLetter bool, letterforms ...letterform) string {
135+func renderWord(spacing int, stretchIndex int, letterforms ...letterform) string {
115 if spacing < 0 {
136 if spacing < 0 {
116 spacing = 0
137 spacing = 0
117 }
138 }
@@ -119,11 +140,6 @@ func renderWord(spacing int, stretchRandomLetter bool, letterforms ...letterform
119 renderedLetterforms := make([]string, len(letterforms))
140 renderedLetterforms := make([]string, len(letterforms))
120
141
121 // pick one letter randomly to stretch
142 // pick one letter randomly to stretch
122− stretchIndex := -1
123− if stretchRandomLetter {
124− stretchIndex = rand.IntN(len(letterforms)) //nolint:gosec
125− }
126−
127 for i, letter := range letterforms {
143 for i, letter := range letterforms {
128 renderedLetterforms[i] = letter(i == stretchIndex)
144 renderedLetterforms[i] = letter(i == stretchIndex)
129 }
145 }