Files
nib-v1/internal/tui/help.go
T
lerko b5b7f6b6ee feat(tui): collapsible tag rail with ambient tag awareness
Persistent left panel showing tags with counts. Provides ambient
awareness of tag landscape without requiring a modal.

- New tagRailModel in tagrail.go: tag list with cursor, scroll, counts
- Rail visible at >=100 cols width, 18% width (min 16 chars)
- ctrl+b toggles rail visibility
- focusTagRail added to focus cycle: capture → tags → list → detail
- j/k navigates, enter filters/unfilters by tag
- Active filter tag highlighted bold in rail
- Tags refresh after entity create/delete/absorb
- Rail auto-hides on narrow terminals, # modal still works as fallback
- Width allocation accounts for rail in split and non-split layouts
2026-05-20 14:32:32 -04:00

98 lines
2.4 KiB
Go

package tui
import "strings"
func renderHelp(width, height int) string {
sections := []struct {
title string
binds [][2]string
}{
{"Focus", [][2]string{
{"tab", "cycle focus: capture → tags → list → detail"},
{"esc", "back / clear filter / to capture"},
{"a", "focus capture bar"},
{"ctrl+b", "toggle tag rail"},
}},
{"Capture Bar", [][2]string{
{"enter", "submit (or browse if empty)"},
{"?…", "search (type ?query)"},
{"-", "todo prefix"},
{"@", "event prefix"},
{"!", "reminder prefix"},
}},
{"Navigation", [][2]string{
{"j/k ↑/↓", "move cursor"},
{"g/G home/end", "top / bottom"},
{"pgup/pgdn", "page up / down"},
{"enter", "view detail"},
}},
{"Views", [][2]string{
{"1", "stream view"},
{"2", "cards view"},
{"s", "cycle sort (cards)"},
{"i", "cycle intent (cards)"},
}},
{"Actions", [][2]string{
{"d", "delete (with confirm)"},
{"x", "toggle todo completion"},
{"!", "toggle pin"},
{"#", "filter by tag"},
{"m", "absorb (merge into target)"},
{"p", "promote to card"},
}},
{"Detail View", [][2]string{
{"p", "promote to card"},
{"D", "demote to fluid"},
{"c", "copy to clipboard"},
{"e", "edit in $EDITOR"},
{"!", "toggle pin"},
{"r", "run checklist"},
{"f", "fill template"},
}},
{"Run Mode", [][2]string{
{"j/k", "move between steps"},
{"space", "toggle step"},
{"r", "reset all steps"},
{"esc", "save + exit"},
}},
{"Fill Mode", [][2]string{
{"tab", "next slot"},
{"shift+tab", "prev slot"},
{"enter", "copy resolved"},
{"esc", "cancel"},
}},
{"Split View", [][2]string{
{"l", "focus detail pane"},
{"h", "focus list pane"},
{"esc", "close detail / back"},
}},
{"Global", [][2]string{
{"?", "toggle help"},
{"q / ctrl+c", "quit"},
}},
}
var b strings.Builder
b.WriteString(detailHeaderStyle.Render("keybindings"))
b.WriteString("\n\n")
for _, s := range sections {
b.WriteString(titleStyle.Render(s.title))
b.WriteString("\n")
for _, bind := range s.binds {
key := helpKeyStyle.Render(bind[0])
desc := helpDescStyle.Render(bind[1])
b.WriteString(" " + key + " " + desc + "\n")
}
b.WriteString("\n")
}
b.WriteString(helpStyle.Render("press ? or esc to close"))
lines := strings.Split(b.String(), "\n")
if len(lines) > height {
lines = lines[:height]
}
return strings.Join(lines, "\n")
}