tui: show binary version in the welcome banner for 1 second
splash variant of the welcome line: ▌ i'm zot (0.0.6). yet another coding agent harness. reverts to: ▌ i'm zot. yet another coding agent harness. after welcomeVersionDuration (1s). a one-shot AfterFunc fires i.invalidate() so the banner refreshes on its own even if the user has not typed anything by then. dev builds without ldflags-injected version still print "(0.0.0)" during the splash; release tags propagate through the makefile into main.version normally. bundles a small printhelp tagline tweak that was already staged.
- commit
- 6a4166429ecf9f660d1fc087699c39e3d5495257
- author
- patriceckhart · · 2026-04-19 11:22 UTC
- parent
- 154420e
- signature
- unsigned
- refs
- tag: v0.0.20
3 files · +32 -4
M internal/agent/args.go +1 -1
| @@ -184,7 +184,7 @@ func ParseArgs(in []string) (Args, error) { | |||
| 184 | 184 | ||
| 185 | 185 | // PrintHelp writes the help text to w (stderr by default). | |
| 186 | 186 | func PrintHelp(version string) { | |
| 187 | - | fmt.Fprintf(os.Stderr, `zot %s — lightweight terminal coding agent | |
| 187 | + | fmt.Fprintf(os.Stderr, `zot %s — Yet another coding agent harness. | |
| 188 | 188 | ||
| 189 | 189 | usage: | |
| 190 | 190 | zot interactive tui | |
| @@ -184,7 +184,7 @@ func ParseArgs(in []string) (Args, error) { | |||||
| 184 | 184 | ||||
| 185 | // PrintHelp writes the help text to w (stderr by default). | 185 | // PrintHelp writes the help text to w (stderr by default). | ||
| 186 | func PrintHelp(version string) { | 186 | func PrintHelp(version string) { | ||
| 187 | - | fmt.Fprintf(os.Stderr, `zot %s — lightweight terminal coding agent | 187 | + | fmt.Fprintf(os.Stderr, `zot %s — Yet another coding agent harness. |
| 188 | 188 | ||||
| 189 | usage: | 189 | usage: | ||
| 190 | zot interactive tui | 190 | zot interactive tui | ||
M internal/agent/modes/interactive.go +20 -1
| @@ -140,8 +140,18 @@ type Interactive struct { | |||
| 140 | 140 | // clears the editor / cancels a turn / shows a hint; a second press | |
| 141 | 141 | // within ctrlCExitWindow exits. Mirrors the python-repl convention. | |
| 142 | 142 | lastCtrlC time.Time | |
| 143 | + | ||
| 144 | + | // welcomeStart is when the interactive run began. The welcome | |
| 145 | + | // banner shows the binary version for welcomeVersionDuration | |
| 146 | + | // after this point and reverts to plain text after. | |
| 147 | + | welcomeStart time.Time | |
| 143 | 148 | } | |
| 144 | 149 | ||
| 150 | + | // welcomeVersionDuration is how long the welcome banner shows the | |
| 151 | + | // version suffix before reverting to the plain headline. 1 second | |
| 152 | + | // is enough to read at a glance and keeps the splash short. | |
| 153 | + | const welcomeVersionDuration = 1 * time.Second | |
| 154 | + | ||
| 145 | 155 | // NewInteractive constructs an Interactive from cfg. | |
| 146 | 156 | func NewInteractive(cfg InteractiveConfig) *Interactive { | |
| 147 | 157 | i := &Interactive{ | |
| @@ -200,6 +210,12 @@ func (i *Interactive) Run(ctx context.Context) error { | |||
| 200 | 210 | i.ed.SetValue(i.cfg.InitialInput) | |
| 201 | 211 | } | |
| 202 | 212 | ||
| 213 | + | // Stamp the welcome time and schedule a one-shot redraw at the | |
| 214 | + | // expiry so the version suffix disappears on its own even if the | |
| 215 | + | // user hasn't typed anything yet. | |
| 216 | + | i.welcomeStart = time.Now() | |
| 217 | + | time.AfterFunc(welcomeVersionDuration, i.invalidate) | |
| 218 | + | ||
| 203 | 219 | // If the agent was constructed with a pre-loaded transcript | |
| 204 | 220 | // (--continue, --resume, --session) park the viewport on the | |
| 205 | 221 | // most recent turn so the user lands looking at where the | |
| @@ -431,8 +447,11 @@ func (i *Interactive) redraw() { | |||
| 431 | 447 | ||
| 432 | 448 | // Welcome banner: shown at the top of the chat area when there is | |
| 433 | 449 | // no transcript yet. Disappears after the first message is sent. | |
| 450 | + | // The version suffix is shown for welcomeVersionDuration after | |
| 451 | + | // startup, then drops off automatically. | |
| 434 | 452 | if len(i.view.Messages) == 0 && !i.streamOn && len(i.toolOrder) == 0 { | |
| 435 | - | chat = append(welcomeBanner(i.cfg.Theme), chat...) | |
| 453 | + | showVer := !i.welcomeStart.IsZero() && time.Since(i.welcomeStart) < welcomeVersionDuration | |
| 454 | + | chat = append(welcomeBanner(i.cfg.Theme, i.cfg.Version, showVer), chat...) | |
| 436 | 455 | } | |
| 437 | 456 | ||
| 438 | 457 | // Update-available banner: prepended above everything else so it's | |
| @@ -140,8 +140,18 @@ type Interactive struct { | |||||
| 140 | // clears the editor / cancels a turn / shows a hint; a second press | 140 | // clears the editor / cancels a turn / shows a hint; a second press | ||
| 141 | // within ctrlCExitWindow exits. Mirrors the python-repl convention. | 141 | // within ctrlCExitWindow exits. Mirrors the python-repl convention. | ||
| 142 | lastCtrlC time.Time | 142 | lastCtrlC time.Time | ||
| 143 | + | ||||
| 144 | + | // welcomeStart is when the interactive run began. The welcome | |||
| 145 | + | // banner shows the binary version for welcomeVersionDuration | |||
| 146 | + | // after this point and reverts to plain text after. | |||
| 147 | + | welcomeStart time.Time | |||
| 143 | } | 148 | } | ||
| 144 | 149 | ||||
| 150 | + | // welcomeVersionDuration is how long the welcome banner shows the | |||
| 151 | + | // version suffix before reverting to the plain headline. 1 second | |||
| 152 | + | // is enough to read at a glance and keeps the splash short. | |||
| 153 | + | const welcomeVersionDuration = 1 * time.Second | |||
| 154 | + | ||||
| 145 | // NewInteractive constructs an Interactive from cfg. | 155 | // NewInteractive constructs an Interactive from cfg. | ||
| 146 | func NewInteractive(cfg InteractiveConfig) *Interactive { | 156 | func NewInteractive(cfg InteractiveConfig) *Interactive { | ||
| 147 | i := &Interactive{ | 157 | i := &Interactive{ | ||
| @@ -200,6 +210,12 @@ func (i *Interactive) Run(ctx context.Context) error { | |||||
| 200 | i.ed.SetValue(i.cfg.InitialInput) | 210 | i.ed.SetValue(i.cfg.InitialInput) | ||
| 201 | } | 211 | } | ||
| 202 | 212 | ||||
| 213 | + | // Stamp the welcome time and schedule a one-shot redraw at the | |||
| 214 | + | // expiry so the version suffix disappears on its own even if the | |||
| 215 | + | // user hasn't typed anything yet. | |||
| 216 | + | i.welcomeStart = time.Now() | |||
| 217 | + | time.AfterFunc(welcomeVersionDuration, i.invalidate) | |||
| 218 | + | ||||
| 203 | // If the agent was constructed with a pre-loaded transcript | 219 | // If the agent was constructed with a pre-loaded transcript | ||
| 204 | // (--continue, --resume, --session) park the viewport on the | 220 | // (--continue, --resume, --session) park the viewport on the | ||
| 205 | // most recent turn so the user lands looking at where the | 221 | // most recent turn so the user lands looking at where the | ||
| @@ -431,8 +447,11 @@ func (i *Interactive) redraw() { | |||||
| 431 | 447 | ||||
| 432 | // Welcome banner: shown at the top of the chat area when there is | 448 | // Welcome banner: shown at the top of the chat area when there is | ||
| 433 | // no transcript yet. Disappears after the first message is sent. | 449 | // no transcript yet. Disappears after the first message is sent. | ||
| 450 | + | // The version suffix is shown for welcomeVersionDuration after | |||
| 451 | + | // startup, then drops off automatically. | |||
| 434 | if len(i.view.Messages) == 0 && !i.streamOn && len(i.toolOrder) == 0 { | 452 | if len(i.view.Messages) == 0 && !i.streamOn && len(i.toolOrder) == 0 { | ||
| 435 | - | chat = append(welcomeBanner(i.cfg.Theme), chat...) | 453 | + | showVer := !i.welcomeStart.IsZero() && time.Since(i.welcomeStart) < welcomeVersionDuration |
| 454 | + | chat = append(welcomeBanner(i.cfg.Theme, i.cfg.Version, showVer), chat...) | |||
| 436 | } | 455 | } | ||
| 437 | 456 | ||||
| 438 | // Update-available banner: prepended above everything else so it's | 457 | // Update-available banner: prepended above everything else so it's | ||
M internal/agent/modes/welcome.go +11 -2
| @@ -4,9 +4,18 @@ import "github.com/patriceckhart/zot/internal/tui" | |||
| 4 | 4 | ||
| 5 | 5 | // welcomeBanner returns the intro text shown at the top of an empty chat. | |
| 6 | 6 | // It uses the `zot` label color (same as the assistant) for consistency. | |
| 7 | - | func welcomeBanner(th tui.Theme) []string { | |
| 7 | + | // | |
| 8 | + | // When version is non-empty AND showVersion is true, the headline | |
| 9 | + | // reads "i'm zot (vX.Y.Z). ..." so users see which build they're on | |
| 10 | + | // the moment zot starts. After welcomeVersionDuration the caller | |
| 11 | + | // flips showVersion off and the headline reverts to plain text. | |
| 12 | + | func welcomeBanner(th tui.Theme, version string, showVersion bool) []string { | |
| 13 | + | headline := "▌ i'm zot. yet another coding agent harness." | |
| 14 | + | if showVersion && version != "" { | |
| 15 | + | headline = "▌ i'm zot (" + version + "). yet another coding agent harness." | |
| 16 | + | } | |
| 8 | 17 | return []string{ | |
| 9 | - | th.FG256(th.Assistant, tui.Bold("▌ i'm zot. yet another coding agent harness.")), | |
| 18 | + | th.FG256(th.Assistant, tui.Bold(headline)), | |
| 10 | 19 | th.FG256(th.Muted, " ask anything, or type /help to see commands."), | |
| 11 | 20 | "", | |
| 12 | 21 | } | |
| @@ -4,9 +4,18 @@ import "github.com/patriceckhart/zot/internal/tui" | |||||
| 4 | 4 | ||||
| 5 | // welcomeBanner returns the intro text shown at the top of an empty chat. | 5 | // welcomeBanner returns the intro text shown at the top of an empty chat. | ||
| 6 | // It uses the `zot` label color (same as the assistant) for consistency. | 6 | // It uses the `zot` label color (same as the assistant) for consistency. | ||
| 7 | - | func welcomeBanner(th tui.Theme) []string { | 7 | + | // |
| 8 | + | // When version is non-empty AND showVersion is true, the headline | |||
| 9 | + | // reads "i'm zot (vX.Y.Z). ..." so users see which build they're on | |||
| 10 | + | // the moment zot starts. After welcomeVersionDuration the caller | |||
| 11 | + | // flips showVersion off and the headline reverts to plain text. | |||
| 12 | + | func welcomeBanner(th tui.Theme, version string, showVersion bool) []string { | |||
| 13 | + | headline := "▌ i'm zot. yet another coding agent harness." | |||
| 14 | + | if showVersion && version != "" { | |||
| 15 | + | headline = "▌ i'm zot (" + version + "). yet another coding agent harness." | |||
| 16 | + | } | |||
| 8 | return []string{ | 17 | return []string{ | ||
| 9 | - | th.FG256(th.Assistant, tui.Bold("▌ i'm zot. yet another coding agent harness.")), | 18 | + | th.FG256(th.Assistant, tui.Bold(headline)), |
| 10 | th.FG256(th.Muted, " ask anything, or type /help to see commands."), | 19 | th.FG256(th.Muted, " ask anything, or type /help to see commands."), | ||
| 11 | "", | 20 | "", | ||
| 12 | } | 21 | } | ||