detached d46f1d5

feat(ext-panel): allow invisible selection marker via U+200B

ext_panel_dialog.Render sniffs the leading glyph of each panel line
to decide which row is selected and apply the theme's selection
band: today that's '> ' (U+25B8) or '* ' (U+25CF), both visible.
Extensions that want the highlight without a visible glyph had no
way to opt in - they had to ship the arrow.

Adds U+200B (zero-width space) as a third recognised prefix.
Extensions can now emit '\u200B' at the start of the selected row
to get the blue selection band without any visible marker; the
character renders as zero columns so other rows that omit it line
up perfectly. The visible arrow markers keep working unchanged.

Used by zot-workspaces to highlight the cursor row without
inserting a triangle into the otherwise clean list.
commit
d46f1d58245096b11e540ecf2124c6b1ea4682d1
author
patriceckhart · · 2026-05-19 17:48 UTC
parent
e48f4dd
signature
unsigned
refs
tag: v0.1.86
1 file · +8 -1
M internal/agent/modes/ext_panel_dialog.go +8 -1
@@ -63,7 +63,14 @@ func (d *extPanelDialog) Render(th tui.Theme, width int) []string {
6363 for _, l := range d.lines {
6464 plain := stripANSIBytes(l)
6565 trimmed := strings.TrimLeft(plain, " ")
66- selected := strings.HasPrefix(trimmed, "▸ ") || strings.HasPrefix(trimmed, "● ")
66+ // Selection markers, in order of precedence:
67+ // "▸ " / "● " visible glyph the user sees
68+ // "\u200b" invisible zero-width-space sentinel for
69+ // extensions that want the row highlight
70+ // without rendering an arrow
71+ selected := strings.HasPrefix(trimmed, "▸ ") ||
72+ strings.HasPrefix(trimmed, "● ") ||
73+ strings.HasPrefix(trimmed, "\u200b")
6774 out = append(out, styleExtPanelLine(th, l, plain, width, selected))
6875 }
6976 if strings.TrimSpace(d.footer) != "" {
@@ -63,7 +63,14 @@ func (d *extPanelDialog) Render(th tui.Theme, width int) []string {
63 for _, l := range d.lines {63 for _, l := range d.lines {
64 plain := stripANSIBytes(l)64 plain := stripANSIBytes(l)
65 trimmed := strings.TrimLeft(plain, " ")65 trimmed := strings.TrimLeft(plain, " ")
66- selected := strings.HasPrefix(trimmed, "▸ ") || strings.HasPrefix(trimmed, "● ")66+ // Selection markers, in order of precedence:
67+ // "▸ " / "● " visible glyph the user sees
68+ // "\u200b" invisible zero-width-space sentinel for
69+ // extensions that want the row highlight
70+ // without rendering an arrow
71+ selected := strings.HasPrefix(trimmed, "▸ ") ||
72+ strings.HasPrefix(trimmed, "● ") ||
73+ strings.HasPrefix(trimmed, "\u200b")
67 out = append(out, styleExtPanelLine(th, l, plain, width, selected))74 out = append(out, styleExtPanelLine(th, l, plain, width, selected))
68 }75 }
69 if strings.TrimSpace(d.footer) != "" {76 if strings.TrimSpace(d.footer) != "" {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close