style: replace middle-dot separators with ascii hyphens
Swept the TUI strings and README for the stray U+00B7 MIDDLE DOT (\u00b7) separators left over from earlier UI iterations. They read fine on terminals that render the glyph as a small bullet, but on some fonts (especially the telegram desktop client, a few linux terminal fonts) it renders as an off-center dot that looks like a smudge or a broken pipe. Plain ' - ' is universally readable and matches every other separator already in the status bar and dialogs. Touched: README.md paragraph separators modes/btw_dialog.go header joiner modes/help.go table row separators modes/interactive.go status bar tags, telegram mirror modes/jump_dialog.go row separators modes/login_dialog.go header joiners, status line modes/model_dialog.go model + source joiner modes/slash_suggest.go commands list tui/view.go assorted tui separators No functional change. go test ./... still passes.
- commit
- 1f28b62a479078a84669478e3b7fa067d7877e34
- author
- patriceckhart · · 2026-04-21 15:39 UTC
- parent
- c4150ce
- signature
- unsigned
- refs
- tag: v0.0.80
9 files · +27 -27
M README.md +1 -1
| @@ -357,7 +357,7 @@ Type `/telegram` in the running TUI to open a picker with **connect**, **disconn | |||
| 357 | 357 | - DMs from the paired user become prompts in the **same** session you're typing in, so you can continue a conversation from the terminal on your phone and back again. | |
| 358 | 358 | - Messages you type in the TUI are mirrored into the Telegram thread prefixed `you: …` and the assistant's replies come back prefixed `zot: …`, so the Telegram chat stays a complete record of both sides of the conversation. | |
| 359 | 359 | - Messages sent from Telegram show up as your own bubble in Telegram (no mirror) and the assistant's reply to them comes back bare (no prefix). | |
| 360 | - | - The status bar shows a `· tg ·` tag while the bridge is active. | |
| 360 | + | - The status bar shows a `- tg -` tag while the bridge is active. | |
| 361 | 361 | - `/telegram connect` / `/telegram disconnect` / `/telegram status` (or `/tg`) also work as direct commands without the picker. | |
| 362 | 362 | ||
| 363 | 363 | The in-TUI bridge refuses to start while the standalone daemon (below) is running, since two concurrent long-poll consumers of the same bot race on every update and silently drop messages. | |
| @@ -357,7 +357,7 @@ Type `/telegram` in the running TUI to open a picker with **connect**, **disconn | |||||
| 357 | - DMs from the paired user become prompts in the **same** session you're typing in, so you can continue a conversation from the terminal on your phone and back again. | 357 | - DMs from the paired user become prompts in the **same** session you're typing in, so you can continue a conversation from the terminal on your phone and back again. | ||
| 358 | - Messages you type in the TUI are mirrored into the Telegram thread prefixed `you: …` and the assistant's replies come back prefixed `zot: …`, so the Telegram chat stays a complete record of both sides of the conversation. | 358 | - Messages you type in the TUI are mirrored into the Telegram thread prefixed `you: …` and the assistant's replies come back prefixed `zot: …`, so the Telegram chat stays a complete record of both sides of the conversation. | ||
| 359 | - Messages sent from Telegram show up as your own bubble in Telegram (no mirror) and the assistant's reply to them comes back bare (no prefix). | 359 | - Messages sent from Telegram show up as your own bubble in Telegram (no mirror) and the assistant's reply to them comes back bare (no prefix). | ||
| 360 | - | - The status bar shows a `· tg ·` tag while the bridge is active. | 360 | + | - The status bar shows a `- tg -` tag while the bridge is active. |
| 361 | - `/telegram connect` / `/telegram disconnect` / `/telegram status` (or `/tg`) also work as direct commands without the picker. | 361 | - `/telegram connect` / `/telegram disconnect` / `/telegram status` (or `/tg`) also work as direct commands without the picker. | ||
| 362 | 362 | ||||
| 363 | The in-TUI bridge refuses to start while the standalone daemon (below) is running, since two concurrent long-poll consumers of the same bot race on every update and silently drop messages. | 363 | The in-TUI bridge refuses to start while the standalone daemon (below) is running, since two concurrent long-poll consumers of the same bot race on every update and silently drop messages. | ||
M internal/agent/modes/btw_dialog.go +1 -1
| @@ -297,7 +297,7 @@ func (d *btwDialog) Render(th tui.Theme, width int) []string { | |||
| 297 | 297 | // Match the main chat busy prefix shape: spinner glyph, | |
| 298 | 298 | // rotating funny-line, elapsed seconds, then a muted hint | |
| 299 | 299 | // that esc cancels. | |
| 300 | - | prefix := fmt.Sprintf("%s %s · %s", | |
| 300 | + | prefix := fmt.Sprintf("%s %s - %s", | |
| 301 | 301 | th.FG256(th.Assistant, d.spin.Frame()), | |
| 302 | 302 | th.FG256(th.Assistant, d.spin.Message()), | |
| 303 | 303 | th.FG256(th.Muted, d.spin.Elapsed().String()), | |
| @@ -297,7 +297,7 @@ func (d *btwDialog) Render(th tui.Theme, width int) []string { | |||||
| 297 | // Match the main chat busy prefix shape: spinner glyph, | 297 | // Match the main chat busy prefix shape: spinner glyph, | ||
| 298 | // rotating funny-line, elapsed seconds, then a muted hint | 298 | // rotating funny-line, elapsed seconds, then a muted hint | ||
| 299 | // that esc cancels. | 299 | // that esc cancels. | ||
| 300 | - | prefix := fmt.Sprintf("%s %s · %s", | 300 | + | prefix := fmt.Sprintf("%s %s - %s", |
| 301 | th.FG256(th.Assistant, d.spin.Frame()), | 301 | th.FG256(th.Assistant, d.spin.Frame()), | ||
| 302 | th.FG256(th.Assistant, d.spin.Message()), | 302 | th.FG256(th.Assistant, d.spin.Message()), | ||
| 303 | th.FG256(th.Muted, d.spin.Elapsed().String()), | 303 | th.FG256(th.Muted, d.spin.Elapsed().String()), | ||
M internal/agent/modes/help.go +4 -4
| @@ -13,8 +13,8 @@ var helpKeyRows = [][2]string{ | |||
| 13 | 13 | {"enter", "submit the current input"}, | |
| 14 | 14 | {"alt+enter", "insert a newline"}, | |
| 15 | 15 | {"tab", "complete the highlighted slash command"}, | |
| 16 | - | {"esc", "cancel the current turn (while busy) · clear the input (while idle)"}, | |
| 17 | - | {"ctrl+c", "exit (while idle) · cancel the current turn (while busy)"}, | |
| 16 | + | {"esc", "cancel the current turn (while busy) - clear the input (while idle)"}, | |
| 17 | + | {"ctrl+c", "exit (while idle) - cancel the current turn (while busy)"}, | |
| 18 | 18 | {"ctrl+w", "delete previous word"}, | |
| 19 | 19 | {"alt+backspace", "delete previous word (same as ctrl+w)"}, | |
| 20 | 20 | {"ctrl+u / ctrl+k", "delete to start / end of line"}, | |
| @@ -23,7 +23,7 @@ var helpKeyRows = [][2]string{ | |||
| 23 | 23 | {"ctrl+l", "redraw the screen"}, | |
| 24 | 24 | {"ctrl+o", "expand / collapse long tool results"}, | |
| 25 | 25 | {"pgup / pgdn", "scroll the chat one page up / down"}, | |
| 26 | - | {"up / down", "scroll by 3 lines (when input is empty) · prompt history (otherwise)"}, | |
| 26 | + | {"up / down", "scroll by 3 lines (when input is empty) - prompt history (otherwise)"}, | |
| 27 | 27 | } | |
| 28 | 28 | ||
| 29 | 29 | // renderHelpBlock builds the friendly /help view. Uses the shared | |
| @@ -42,7 +42,7 @@ func renderHelpBlock(th tui.Theme, width int) []string { | |||
| 42 | 42 | } | |
| 43 | 43 | ||
| 44 | 44 | // Label column width uses display cells, not byte length, so | |
| 45 | - | // single-cell multibyte runes (← → · …) don't over-count and leave | |
| 45 | + | // single-cell multibyte runes (← → - …) don't over-count and leave | |
| 46 | 46 | // a raggedy right edge. `len("alt+← / alt+→")` is 17 bytes but | |
| 47 | 47 | // only 13 cells; padding off byte length would either overshoot | |
| 48 | 48 | // (setting labelWidth too high and wasting space on every row) | |
| @@ -13,8 +13,8 @@ var helpKeyRows = [][2]string{ | |||||
| 13 | {"enter", "submit the current input"}, | 13 | {"enter", "submit the current input"}, | ||
| 14 | {"alt+enter", "insert a newline"}, | 14 | {"alt+enter", "insert a newline"}, | ||
| 15 | {"tab", "complete the highlighted slash command"}, | 15 | {"tab", "complete the highlighted slash command"}, | ||
| 16 | - | {"esc", "cancel the current turn (while busy) · clear the input (while idle)"}, | 16 | + | {"esc", "cancel the current turn (while busy) - clear the input (while idle)"}, |
| 17 | - | {"ctrl+c", "exit (while idle) · cancel the current turn (while busy)"}, | 17 | + | {"ctrl+c", "exit (while idle) - cancel the current turn (while busy)"}, |
| 18 | {"ctrl+w", "delete previous word"}, | 18 | {"ctrl+w", "delete previous word"}, | ||
| 19 | {"alt+backspace", "delete previous word (same as ctrl+w)"}, | 19 | {"alt+backspace", "delete previous word (same as ctrl+w)"}, | ||
| 20 | {"ctrl+u / ctrl+k", "delete to start / end of line"}, | 20 | {"ctrl+u / ctrl+k", "delete to start / end of line"}, | ||
| @@ -23,7 +23,7 @@ var helpKeyRows = [][2]string{ | |||||
| 23 | {"ctrl+l", "redraw the screen"}, | 23 | {"ctrl+l", "redraw the screen"}, | ||
| 24 | {"ctrl+o", "expand / collapse long tool results"}, | 24 | {"ctrl+o", "expand / collapse long tool results"}, | ||
| 25 | {"pgup / pgdn", "scroll the chat one page up / down"}, | 25 | {"pgup / pgdn", "scroll the chat one page up / down"}, | ||
| 26 | - | {"up / down", "scroll by 3 lines (when input is empty) · prompt history (otherwise)"}, | 26 | + | {"up / down", "scroll by 3 lines (when input is empty) - prompt history (otherwise)"}, |
| 27 | } | 27 | } | ||
| 28 | 28 | ||||
| 29 | // renderHelpBlock builds the friendly /help view. Uses the shared | 29 | // renderHelpBlock builds the friendly /help view. Uses the shared | ||
| @@ -42,7 +42,7 @@ func renderHelpBlock(th tui.Theme, width int) []string { | |||||
| 42 | } | 42 | } | ||
| 43 | 43 | ||||
| 44 | // Label column width uses display cells, not byte length, so | 44 | // Label column width uses display cells, not byte length, so | ||
| 45 | - | // single-cell multibyte runes (← → · …) don't over-count and leave | 45 | + | // single-cell multibyte runes (← → - …) don't over-count and leave |
| 46 | // a raggedy right edge. `len("alt+← / alt+→")` is 17 bytes but | 46 | // a raggedy right edge. `len("alt+← / alt+→")` is 17 bytes but | ||
| 47 | // only 13 cells; padding off byte length would either overshoot | 47 | // only 13 cells; padding off byte length would either overshoot | ||
| 48 | // (setting labelWidth too high and wasting space on every row) | 48 | // (setting labelWidth too high and wasting space on every row) | ||
M internal/agent/modes/interactive.go +6 -6
| @@ -719,7 +719,7 @@ func (i *Interactive) redraw() { | |||
| 719 | 719 | // drifts every second and shouldn't grab focus. | |
| 720 | 720 | var busyPrefix string | |
| 721 | 721 | if i.busy { | |
| 722 | - | busyPrefix = fmt.Sprintf("%s %s · %s", | |
| 722 | + | busyPrefix = fmt.Sprintf("%s %s - %s", | |
| 723 | 723 | i.cfg.Theme.FG256(i.cfg.Theme.Assistant, i.spin.Frame()), | |
| 724 | 724 | i.cfg.Theme.FG256(i.cfg.Theme.Assistant, i.spin.Message()), | |
| 725 | 725 | i.cfg.Theme.FG256(i.cfg.Theme.Muted, i.spin.Elapsed().String()), | |
| @@ -805,7 +805,7 @@ func (i *Interactive) redraw() { | |||
| 805 | 805 | if i.scrollOffset > 0 && len(visibleChat) > 0 { | |
| 806 | 806 | var text string | |
| 807 | 807 | if i.parkedTurn > 0 && i.parkedTotal > 0 { | |
| 808 | - | text = fmt.Sprintf(" ↑ viewing turn %d of %d · %d lines more below (pgdn / end)", | |
| 808 | + | text = fmt.Sprintf(" ↑ viewing turn %d of %d - %d lines more below (pgdn / end)", | |
| 809 | 809 | i.parkedTurn, i.parkedTotal, i.scrollOffset) | |
| 810 | 810 | } else { | |
| 811 | 811 | text = fmt.Sprintf(" ↑ %d lines more below (end to jump)", i.scrollOffset) | |
| @@ -1929,7 +1929,7 @@ func (i *Interactive) scrollToLastTurn(msgs []provider.Message) { | |||
| 1929 | 1929 | i.mu.Lock() | |
| 1930 | 1930 | i.scrollOffset = offset | |
| 1931 | 1931 | // Mark the parked-turn footer so the user sees "viewing turn N of | |
| 1932 | - | // M · pgdn to catch up" — same affordance as /jump. Tells them at | |
| 1932 | + | // M - pgdn to catch up" — same affordance as /jump. Tells them at | |
| 1933 | 1933 | // a glance that they're looking at history, not the live tail. | |
| 1934 | 1934 | if offset > 0 { | |
| 1935 | 1935 | i.parkedTurn = turnNo | |
| @@ -2603,12 +2603,12 @@ func (i *Interactive) telegramStatus() { | |||
| 2603 | 2603 | msg += " as @" + s.Username | |
| 2604 | 2604 | } | |
| 2605 | 2605 | if s.PairedID != 0 { | |
| 2606 | - | msg += fmt.Sprintf(" · paired with user %d", s.PairedID) | |
| 2606 | + | msg += fmt.Sprintf(" - paired with user %d", s.PairedID) | |
| 2607 | 2607 | } else { | |
| 2608 | - | msg += " · awaiting pairing" | |
| 2608 | + | msg += " - awaiting pairing" | |
| 2609 | 2609 | } | |
| 2610 | 2610 | } else if pid, alive, _ := telegram.IsRunning(i.cfg.ZotHome); alive && pid > 0 { | |
| 2611 | - | msg = fmt.Sprintf("telegram: background daemon running (pid %d) · /telegram connect won't work until you stop it", pid) | |
| 2611 | + | msg = fmt.Sprintf("telegram: background daemon running (pid %d) - /telegram connect won't work until you stop it", pid) | |
| 2612 | 2612 | } else { | |
| 2613 | 2613 | cfg, _ := telegram.LoadConfig(i.cfg.ZotHome) | |
| 2614 | 2614 | if cfg.BotToken == "" { | |
| @@ -719,7 +719,7 @@ func (i *Interactive) redraw() { | |||||
| 719 | // drifts every second and shouldn't grab focus. | 719 | // drifts every second and shouldn't grab focus. | ||
| 720 | var busyPrefix string | 720 | var busyPrefix string | ||
| 721 | if i.busy { | 721 | if i.busy { | ||
| 722 | - | busyPrefix = fmt.Sprintf("%s %s · %s", | 722 | + | busyPrefix = fmt.Sprintf("%s %s - %s", |
| 723 | i.cfg.Theme.FG256(i.cfg.Theme.Assistant, i.spin.Frame()), | 723 | i.cfg.Theme.FG256(i.cfg.Theme.Assistant, i.spin.Frame()), | ||
| 724 | i.cfg.Theme.FG256(i.cfg.Theme.Assistant, i.spin.Message()), | 724 | i.cfg.Theme.FG256(i.cfg.Theme.Assistant, i.spin.Message()), | ||
| 725 | i.cfg.Theme.FG256(i.cfg.Theme.Muted, i.spin.Elapsed().String()), | 725 | i.cfg.Theme.FG256(i.cfg.Theme.Muted, i.spin.Elapsed().String()), | ||
| @@ -805,7 +805,7 @@ func (i *Interactive) redraw() { | |||||
| 805 | if i.scrollOffset > 0 && len(visibleChat) > 0 { | 805 | if i.scrollOffset > 0 && len(visibleChat) > 0 { | ||
| 806 | var text string | 806 | var text string | ||
| 807 | if i.parkedTurn > 0 && i.parkedTotal > 0 { | 807 | if i.parkedTurn > 0 && i.parkedTotal > 0 { | ||
| 808 | - | text = fmt.Sprintf(" ↑ viewing turn %d of %d · %d lines more below (pgdn / end)", | 808 | + | text = fmt.Sprintf(" ↑ viewing turn %d of %d - %d lines more below (pgdn / end)", |
| 809 | i.parkedTurn, i.parkedTotal, i.scrollOffset) | 809 | i.parkedTurn, i.parkedTotal, i.scrollOffset) | ||
| 810 | } else { | 810 | } else { | ||
| 811 | text = fmt.Sprintf(" ↑ %d lines more below (end to jump)", i.scrollOffset) | 811 | text = fmt.Sprintf(" ↑ %d lines more below (end to jump)", i.scrollOffset) | ||
| @@ -1929,7 +1929,7 @@ func (i *Interactive) scrollToLastTurn(msgs []provider.Message) { | |||||
| 1929 | i.mu.Lock() | 1929 | i.mu.Lock() | ||
| 1930 | i.scrollOffset = offset | 1930 | i.scrollOffset = offset | ||
| 1931 | // Mark the parked-turn footer so the user sees "viewing turn N of | 1931 | // Mark the parked-turn footer so the user sees "viewing turn N of | ||
| 1932 | - | // M · pgdn to catch up" — same affordance as /jump. Tells them at | 1932 | + | // M - pgdn to catch up" — same affordance as /jump. Tells them at |
| 1933 | // a glance that they're looking at history, not the live tail. | 1933 | // a glance that they're looking at history, not the live tail. | ||
| 1934 | if offset > 0 { | 1934 | if offset > 0 { | ||
| 1935 | i.parkedTurn = turnNo | 1935 | i.parkedTurn = turnNo | ||
| @@ -2603,12 +2603,12 @@ func (i *Interactive) telegramStatus() { | |||||
| 2603 | msg += " as @" + s.Username | 2603 | msg += " as @" + s.Username | ||
| 2604 | } | 2604 | } | ||
| 2605 | if s.PairedID != 0 { | 2605 | if s.PairedID != 0 { | ||
| 2606 | - | msg += fmt.Sprintf(" · paired with user %d", s.PairedID) | 2606 | + | msg += fmt.Sprintf(" - paired with user %d", s.PairedID) |
| 2607 | } else { | 2607 | } else { | ||
| 2608 | - | msg += " · awaiting pairing" | 2608 | + | msg += " - awaiting pairing" |
| 2609 | } | 2609 | } | ||
| 2610 | } else if pid, alive, _ := telegram.IsRunning(i.cfg.ZotHome); alive && pid > 0 { | 2610 | } else if pid, alive, _ := telegram.IsRunning(i.cfg.ZotHome); alive && pid > 0 { | ||
| 2611 | - | msg = fmt.Sprintf("telegram: background daemon running (pid %d) · /telegram connect won't work until you stop it", pid) | 2611 | + | msg = fmt.Sprintf("telegram: background daemon running (pid %d) - /telegram connect won't work until you stop it", pid) |
| 2612 | } else { | 2612 | } else { | ||
| 2613 | cfg, _ := telegram.LoadConfig(i.cfg.ZotHome) | 2613 | cfg, _ := telegram.LoadConfig(i.cfg.ZotHome) | ||
| 2614 | if cfg.BotToken == "" { | 2614 | if cfg.BotToken == "" { | ||
M internal/agent/modes/jump_dialog.go +3 -3
| @@ -111,9 +111,9 @@ func (d *jumpDialog) Render(th tui.Theme, width int) []string { | |||
| 111 | 111 | } | |
| 112 | 112 | ||
| 113 | 113 | // Status line: shows the active filter, visible count, and hints. | |
| 114 | - | hint := "↑/↓ pick · enter jump · esc cancel · type to filter" | |
| 114 | + | hint := "↑/↓ pick - enter jump - esc cancel - type to filter" | |
| 115 | 115 | if d.filter != "" { | |
| 116 | - | hint = fmt.Sprintf("filter: %q · %d match · ", d.filter, len(d.visible)) + hint | |
| 116 | + | hint = fmt.Sprintf("filter: %q - %d match - ", d.filter, len(d.visible)) + hint | |
| 117 | 117 | } | |
| 118 | 118 | lines = append(lines, th.FG256(th.Muted, hint)) | |
| 119 | 119 | ||
| @@ -266,7 +266,7 @@ func firstLineOfUserMessage(m provider.Message) string { | |||
| 266 | 266 | } | |
| 267 | 267 | } | |
| 268 | 268 | case provider.ImageBlock: | |
| 269 | - | return fmt.Sprintf("[image · %s · %d bytes]", b.MimeType, len(b.Data)) | |
| 269 | + | return fmt.Sprintf("[image - %s - %d bytes]", b.MimeType, len(b.Data)) | |
| 270 | 270 | } | |
| 271 | 271 | } | |
| 272 | 272 | return "(empty)" | |
| @@ -111,9 +111,9 @@ func (d *jumpDialog) Render(th tui.Theme, width int) []string { | |||||
| 111 | } | 111 | } | ||
| 112 | 112 | ||||
| 113 | // Status line: shows the active filter, visible count, and hints. | 113 | // Status line: shows the active filter, visible count, and hints. | ||
| 114 | - | hint := "↑/↓ pick · enter jump · esc cancel · type to filter" | 114 | + | hint := "↑/↓ pick - enter jump - esc cancel - type to filter" |
| 115 | if d.filter != "" { | 115 | if d.filter != "" { | ||
| 116 | - | hint = fmt.Sprintf("filter: %q · %d match · ", d.filter, len(d.visible)) + hint | 116 | + | hint = fmt.Sprintf("filter: %q - %d match - ", d.filter, len(d.visible)) + hint |
| 117 | } | 117 | } | ||
| 118 | lines = append(lines, th.FG256(th.Muted, hint)) | 118 | lines = append(lines, th.FG256(th.Muted, hint)) | ||
| 119 | 119 | ||||
| @@ -266,7 +266,7 @@ func firstLineOfUserMessage(m provider.Message) string { | |||||
| 266 | } | 266 | } | ||
| 267 | } | 267 | } | ||
| 268 | case provider.ImageBlock: | 268 | case provider.ImageBlock: | ||
| 269 | - | return fmt.Sprintf("[image · %s · %d bytes]", b.MimeType, len(b.Data)) | 269 | + | return fmt.Sprintf("[image - %s - %d bytes]", b.MimeType, len(b.Data)) |
| 270 | } | 270 | } | ||
| 271 | } | 271 | } | ||
| 272 | return "(empty)" | 272 | return "(empty)" | ||
M internal/agent/modes/login_dialog.go +5 -5
| @@ -91,7 +91,7 @@ func (d *loginDialog) Render(th tui.Theme, width int) []string { | |||
| 91 | 91 | case loginStepMethod: | |
| 92 | 92 | opts := []string{ | |
| 93 | 93 | "api key", | |
| 94 | - | "subscription (claude pro/max · chatgpt plus/pro)", | |
| 94 | + | "subscription (claude pro/max - chatgpt plus/pro)", | |
| 95 | 95 | } | |
| 96 | 96 | lines = append(lines, frameHeader(th, "login", width)) | |
| 97 | 97 | for _, l := range d.renderStatusLines(th) { | |
| @@ -109,7 +109,7 @@ func (d *loginDialog) Render(th tui.Theme, width int) []string { | |||
| 109 | 109 | lines = append(lines, frameRule(th, width)) | |
| 110 | 110 | case loginStepProvider: | |
| 111 | 111 | opts := []string{"anthropic", "openai"} | |
| 112 | - | lines = append(lines, frameHeader(th, "login · "+d.method, width)) | |
| 112 | + | lines = append(lines, frameHeader(th, "login - "+d.method, width)) | |
| 113 | 113 | for _, l := range d.renderStatusLines(th) { | |
| 114 | 114 | lines = append(lines, l) | |
| 115 | 115 | } | |
| @@ -134,17 +134,17 @@ func (d *loginDialog) Render(th tui.Theme, width int) []string { | |||
| 134 | 134 | } | |
| 135 | 135 | lines = append(lines, frameRule(th, width)) | |
| 136 | 136 | case loginStepWaiting: | |
| 137 | - | lines = append(lines, frameHeader(th, "login · "+d.method+" · "+d.provider, width)) | |
| 137 | + | lines = append(lines, frameHeader(th, "login - "+d.method+" - "+d.provider, width)) | |
| 138 | 138 | lines = append(lines, th.FG256(th.FG, "opening browser...")) | |
| 139 | 139 | lines = append(lines, th.FG256(th.Muted, d.url)) | |
| 140 | 140 | lines = append(lines, "") | |
| 141 | 141 | lines = append(lines, th.FG256(th.Muted, "waiting for callback. press esc to cancel.")) | |
| 142 | 142 | lines = append(lines, frameRule(th, width)) | |
| 143 | 143 | case loginStepDone: | |
| 144 | - | title := "login · failed" | |
| 144 | + | title := "login - failed" | |
| 145 | 145 | body := th.FG256(th.Error, d.message) | |
| 146 | 146 | if d.success { | |
| 147 | - | title = "login · success" | |
| 147 | + | title = "login - success" | |
| 148 | 148 | body = th.FG256(th.Tool, fmt.Sprintf("logged in to %s via %s", d.provider, d.method)) | |
| 149 | 149 | } | |
| 150 | 150 | lines = append(lines, frameHeader(th, title, width)) | |
| @@ -91,7 +91,7 @@ func (d *loginDialog) Render(th tui.Theme, width int) []string { | |||||
| 91 | case loginStepMethod: | 91 | case loginStepMethod: | ||
| 92 | opts := []string{ | 92 | opts := []string{ | ||
| 93 | "api key", | 93 | "api key", | ||
| 94 | - | "subscription (claude pro/max · chatgpt plus/pro)", | 94 | + | "subscription (claude pro/max - chatgpt plus/pro)", |
| 95 | } | 95 | } | ||
| 96 | lines = append(lines, frameHeader(th, "login", width)) | 96 | lines = append(lines, frameHeader(th, "login", width)) | ||
| 97 | for _, l := range d.renderStatusLines(th) { | 97 | for _, l := range d.renderStatusLines(th) { | ||
| @@ -109,7 +109,7 @@ func (d *loginDialog) Render(th tui.Theme, width int) []string { | |||||
| 109 | lines = append(lines, frameRule(th, width)) | 109 | lines = append(lines, frameRule(th, width)) | ||
| 110 | case loginStepProvider: | 110 | case loginStepProvider: | ||
| 111 | opts := []string{"anthropic", "openai"} | 111 | opts := []string{"anthropic", "openai"} | ||
| 112 | - | lines = append(lines, frameHeader(th, "login · "+d.method, width)) | 112 | + | lines = append(lines, frameHeader(th, "login - "+d.method, width)) |
| 113 | for _, l := range d.renderStatusLines(th) { | 113 | for _, l := range d.renderStatusLines(th) { | ||
| 114 | lines = append(lines, l) | 114 | lines = append(lines, l) | ||
| 115 | } | 115 | } | ||
| @@ -134,17 +134,17 @@ func (d *loginDialog) Render(th tui.Theme, width int) []string { | |||||
| 134 | } | 134 | } | ||
| 135 | lines = append(lines, frameRule(th, width)) | 135 | lines = append(lines, frameRule(th, width)) | ||
| 136 | case loginStepWaiting: | 136 | case loginStepWaiting: | ||
| 137 | - | lines = append(lines, frameHeader(th, "login · "+d.method+" · "+d.provider, width)) | 137 | + | lines = append(lines, frameHeader(th, "login - "+d.method+" - "+d.provider, width)) |
| 138 | lines = append(lines, th.FG256(th.FG, "opening browser...")) | 138 | lines = append(lines, th.FG256(th.FG, "opening browser...")) | ||
| 139 | lines = append(lines, th.FG256(th.Muted, d.url)) | 139 | lines = append(lines, th.FG256(th.Muted, d.url)) | ||
| 140 | lines = append(lines, "") | 140 | lines = append(lines, "") | ||
| 141 | lines = append(lines, th.FG256(th.Muted, "waiting for callback. press esc to cancel.")) | 141 | lines = append(lines, th.FG256(th.Muted, "waiting for callback. press esc to cancel.")) | ||
| 142 | lines = append(lines, frameRule(th, width)) | 142 | lines = append(lines, frameRule(th, width)) | ||
| 143 | case loginStepDone: | 143 | case loginStepDone: | ||
| 144 | - | title := "login · failed" | 144 | + | title := "login - failed" |
| 145 | body := th.FG256(th.Error, d.message) | 145 | body := th.FG256(th.Error, d.message) | ||
| 146 | if d.success { | 146 | if d.success { | ||
| 147 | - | title = "login · success" | 147 | + | title = "login - success" |
| 148 | body = th.FG256(th.Tool, fmt.Sprintf("logged in to %s via %s", d.provider, d.method)) | 148 | body = th.FG256(th.Tool, fmt.Sprintf("logged in to %s via %s", d.provider, d.method)) | ||
| 149 | } | 149 | } | ||
| 150 | lines = append(lines, frameHeader(th, title, width)) | 150 | lines = append(lines, frameHeader(th, title, width)) | ||
M internal/agent/modes/model_dialog.go +1 -1
| @@ -120,7 +120,7 @@ func (d *modelDialog) Render(th tui.Theme, width int) []string { | |||
| 120 | 120 | hint = fmt.Sprintf("filter: %s (%d matches)", d.query, len(d.view)) | |
| 121 | 121 | } | |
| 122 | 122 | } else { | |
| 123 | - | hint += " · type to filter" | |
| 123 | + | hint += " - type to filter" | |
| 124 | 124 | } | |
| 125 | 125 | lines = append(lines, th.FG256(th.Muted, hint)) | |
| 126 | 126 | ||
| @@ -120,7 +120,7 @@ func (d *modelDialog) Render(th tui.Theme, width int) []string { | |||||
| 120 | hint = fmt.Sprintf("filter: %s (%d matches)", d.query, len(d.view)) | 120 | hint = fmt.Sprintf("filter: %s (%d matches)", d.query, len(d.view)) | ||
| 121 | } | 121 | } | ||
| 122 | } else { | 122 | } else { | ||
| 123 | - | hint += " · type to filter" | 123 | + | hint += " - type to filter" |
| 124 | } | 124 | } | ||
| 125 | lines = append(lines, th.FG256(th.Muted, hint)) | 125 | lines = append(lines, th.FG256(th.Muted, hint)) | ||
| 126 | 126 | ||||
M internal/agent/modes/slash_suggest.go +1 -1
| @@ -361,7 +361,7 @@ func (s *slashSuggester) Render(input string, th tui.Theme, width int) []string | |||
| 361 | 361 | // Blank row before the hint visually detaches it from the | |
| 362 | 362 | // command list and groups it with its trailing blank. | |
| 363 | 363 | lines = append(lines, "") | |
| 364 | - | lines = append(lines, th.FG256(th.Muted, " ↑/↓ navigate · tab complete · enter run")) | |
| 364 | + | lines = append(lines, th.FG256(th.Muted, " ↑/↓ navigate - tab complete - enter run")) | |
| 365 | 365 | // Blank row after the hint separates the popup from the status | |
| 366 | 366 | // bar / editor below it. | |
| 367 | 367 | lines = append(lines, "") | |
| @@ -361,7 +361,7 @@ func (s *slashSuggester) Render(input string, th tui.Theme, width int) []string | |||||
| 361 | // Blank row before the hint visually detaches it from the | 361 | // Blank row before the hint visually detaches it from the | ||
| 362 | // command list and groups it with its trailing blank. | 362 | // command list and groups it with its trailing blank. | ||
| 363 | lines = append(lines, "") | 363 | lines = append(lines, "") | ||
| 364 | - | lines = append(lines, th.FG256(th.Muted, " ↑/↓ navigate · tab complete · enter run")) | 364 | + | lines = append(lines, th.FG256(th.Muted, " ↑/↓ navigate - tab complete - enter run")) |
| 365 | // Blank row after the hint separates the popup from the status | 365 | // Blank row after the hint separates the popup from the status | ||
| 366 | // bar / editor below it. | 366 | // bar / editor below it. | ||
| 367 | lines = append(lines, "") | 367 | lines = append(lines, "") | ||
M internal/tui/view.go +5 -5
| @@ -841,7 +841,7 @@ func (v *View) renderDiffRow(line string, width, color int, lineNo int, mark byt | |||
| 841 | 841 | func (v *View) renderImageBlock(b provider.ImageBlock, width int) []string { | |
| 842 | 842 | w, h := ImageDimensions(b.Data) | |
| 843 | 843 | kb := len(b.Data) / 1024 | |
| 844 | - | info := fmt.Sprintf(" image · %s · %dx%d · %d KB", b.MimeType, w, h, kb) | |
| 844 | + | info := fmt.Sprintf(" image - %s - %dx%d - %d KB", b.MimeType, w, h, kb) | |
| 845 | 845 | ||
| 846 | 846 | if v.ImageProto != ImageProtocolNone { | |
| 847 | 847 | // Clamp rendered width so the image never overflows the chat | |
| @@ -1293,7 +1293,7 @@ type StatusBarParams struct { | |||
| 1293 | 1293 | AutoCompacting bool | |
| 1294 | 1294 | ||
| 1295 | 1295 | // Telegram true when the telegram bridge is connected. Adds a | |
| 1296 | - | // small "· tg ·" tag to the cwd line so the user can tell at a | |
| 1296 | + | // small "- tg -" tag to the cwd line so the user can tell at a | |
| 1297 | 1297 | // glance that dms are being mirrored into this session. | |
| 1298 | 1298 | Telegram bool | |
| 1299 | 1299 | ||
| @@ -1387,13 +1387,13 @@ func StatusBar(p StatusBarParams) []string { | |||
| 1387 | 1387 | cwd := shortenHome(p.CWD) | |
| 1388 | 1388 | tags := "" | |
| 1389 | 1389 | if p.Locked { | |
| 1390 | - | tags += "· jailed " | |
| 1390 | + | tags += "- jailed " | |
| 1391 | 1391 | } | |
| 1392 | 1392 | if p.Telegram { | |
| 1393 | - | tags += "· tg " | |
| 1393 | + | tags += "- tg " | |
| 1394 | 1394 | } | |
| 1395 | 1395 | if tags != "" && cwd != "" { | |
| 1396 | - | cwd = tags + "· " + cwd | |
| 1396 | + | cwd = tags + "- " + cwd | |
| 1397 | 1397 | } | |
| 1398 | 1398 | ||
| 1399 | 1399 | primary := leftBuilder.String() | |
| @@ -841,7 +841,7 @@ func (v *View) renderDiffRow(line string, width, color int, lineNo int, mark byt | |||||
| 841 | func (v *View) renderImageBlock(b provider.ImageBlock, width int) []string { | 841 | func (v *View) renderImageBlock(b provider.ImageBlock, width int) []string { | ||
| 842 | w, h := ImageDimensions(b.Data) | 842 | w, h := ImageDimensions(b.Data) | ||
| 843 | kb := len(b.Data) / 1024 | 843 | kb := len(b.Data) / 1024 | ||
| 844 | - | info := fmt.Sprintf(" image · %s · %dx%d · %d KB", b.MimeType, w, h, kb) | 844 | + | info := fmt.Sprintf(" image - %s - %dx%d - %d KB", b.MimeType, w, h, kb) |
| 845 | 845 | ||||
| 846 | if v.ImageProto != ImageProtocolNone { | 846 | if v.ImageProto != ImageProtocolNone { | ||
| 847 | // Clamp rendered width so the image never overflows the chat | 847 | // Clamp rendered width so the image never overflows the chat | ||
| @@ -1293,7 +1293,7 @@ type StatusBarParams struct { | |||||
| 1293 | AutoCompacting bool | 1293 | AutoCompacting bool | ||
| 1294 | 1294 | ||||
| 1295 | // Telegram true when the telegram bridge is connected. Adds a | 1295 | // Telegram true when the telegram bridge is connected. Adds a | ||
| 1296 | - | // small "· tg ·" tag to the cwd line so the user can tell at a | 1296 | + | // small "- tg -" tag to the cwd line so the user can tell at a |
| 1297 | // glance that dms are being mirrored into this session. | 1297 | // glance that dms are being mirrored into this session. | ||
| 1298 | Telegram bool | 1298 | Telegram bool | ||
| 1299 | 1299 | ||||
| @@ -1387,13 +1387,13 @@ func StatusBar(p StatusBarParams) []string { | |||||
| 1387 | cwd := shortenHome(p.CWD) | 1387 | cwd := shortenHome(p.CWD) | ||
| 1388 | tags := "" | 1388 | tags := "" | ||
| 1389 | if p.Locked { | 1389 | if p.Locked { | ||
| 1390 | - | tags += "· jailed " | 1390 | + | tags += "- jailed " |
| 1391 | } | 1391 | } | ||
| 1392 | if p.Telegram { | 1392 | if p.Telegram { | ||
| 1393 | - | tags += "· tg " | 1393 | + | tags += "- tg " |
| 1394 | } | 1394 | } | ||
| 1395 | if tags != "" && cwd != "" { | 1395 | if tags != "" && cwd != "" { | ||
| 1396 | - | cwd = tags + "· " + cwd | 1396 | + | cwd = tags + "- " + cwd |
| 1397 | } | 1397 | } | ||
| 1398 | 1398 | ||||
| 1399 | primary := leftBuilder.String() | 1399 | primary := leftBuilder.String() | ||