Go 110 lines
package tui
import (
"strings"
"github.com/charmbracelet/lipgloss"
)
// eecoLogo is the home-view banner. Heavy box-drawing glyphs trace
// the lowercase outline wordmark used in assets/eeco_logo_{dark,light}.png;
// the rendering pipeline splits the first 'e' from the rest so the two
// halves can be coloured independently (mirrors the dark/indigo split
// in the PNG marks).
const eecoLogo = `┏━━━━━━━┓ ┏━━━━━━━┓ ┏━━━━━━━━ ┏━━━━━━━┓
┃ ┃ ┃ ┃ ┃ ┃ ┃
┣━━━━━━━┛ ┣━━━━━━━┛ ┃ ┃ ┃
┃ ┃ ┃ ┃ ┃
┗━━━━━━━ ┗━━━━━━━ ┗━━━━━━━━ ┗━━━━━━━┛`
// logoSplit is the rune offset that separates the leading "e" glyph
// from the "eco" remainder on every row of eecoLogo. Each glyph is 9
// cells wide with a 1-cell gap; the leading glyph therefore ends at
// rune 9.
const logoSplit = 9
// hintLine is the dim affordance at the foot of the home view. Names no
// external tool and uses no first person (Constraint 4).
const hintLine = "type /<module>"
// renderHome builds the first-impression home view: the centred logo, a
// dim version line, one rotating usage tip, and the hint, stacked with
// generous breathing room. Command discovery lives in the `/` palette and
// the `?` overlay, so the home no longer lists commands. The caller
// concatenates the prompt input and dim footer below.
func renderHome(width int, st styles, version, tip string) string {
var b strings.Builder
b.WriteString(renderLogo(width, st))
b.WriteByte('\n')
b.WriteByte('\n')
b.WriteString(renderVersion(width, st, version))
b.WriteByte('\n')
b.WriteByte('\n')
b.WriteByte('\n')
b.WriteString(renderTip(width, st, tip))
b.WriteByte('\n')
b.WriteByte('\n')
b.WriteByte('\n')
b.WriteString(renderHint(width, st))
b.WriteByte('\n')
return b.String()
}
// renderLogo centres each line of eecoLogo on width and applies the
// two-tone colouring: the leading "e" is rendered in the muted style
// (the dark half of the README mark), the rest in brand. With width <=
// 0 (no WindowSizeMsg yet) the logo renders left-flush, which is the
// pass-through behaviour the rest of the View already uses.
func renderLogo(width int, st styles) string {
lines := strings.Split(eecoLogo, "\n")
for i, ln := range lines {
runes := []rune(ln)
var head, tail string
if len(runes) >= logoSplit {
head = string(runes[:logoSplit])
tail = string(runes[logoSplit:])
} else {
tail = ln
}
out := st.logoMuted.Render(head) + st.brand.Render(tail)
if width > 0 {
out = lipgloss.PlaceHorizontal(width, lipgloss.Center, out)
}
lines[i] = out
}
return strings.Join(lines, "\n")
}
// renderVersion is the dim, centred version line shown once below the
// logo on the home view. It is the only place the running version is
// surfaced: the status footer (barLine) elides it deliberately, so the
// banner carries it.
func renderVersion(width int, st styles, version string) string {
out := st.dim.Render(version)
if width > 0 {
out = lipgloss.PlaceHorizontal(width, lipgloss.Center, out)
}
return out
}
// renderTip formats one rotating usage hint: a `tip` label in the key
// style and the hint body in dim, centred as a whole at width. The hint
// is chosen once per session (pickTip); discovery of the commands
// themselves lives in the `/` palette and the `?` overlay.
func renderTip(width int, st styles, tip string) string {
out := st.key.Render("tip") + st.dim.Render(" · "+tip)
if width > 0 {
out = lipgloss.PlaceHorizontal(width, lipgloss.Center, out)
}
return out
}
// renderHint is the dim one-liner at the foot of the home view.
func renderHint(width int, st styles) string {
out := st.dim.Render(hintLine)
if width > 0 {
out = lipgloss.PlaceHorizontal(width, lipgloss.Center, out)
}
return out
}