Files
task-master/internal/ui/helpbar.go
T
SkrueckenandCursor 6527d91b4f Allow opening the editor while creating a ticket with Ctrl+e.
Draft headline from the create input is passed into $EDITOR so title and description can be written before the ticket is saved.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 10:35:34 +02:00

157 lines
3.0 KiB
Go

package ui
import (
"strings"
"github.com/charmbracelet/lipgloss"
)
type helpEntry struct {
key string
desc string
}
func (m Model) helpEntries() []helpEntry {
switch m.mode {
case ModeCreate:
entries := []helpEntry{
{"enter", "save"},
{"esc", "cancel"},
}
if !m.creatingProject {
entries = append([]helpEntry{{"ctrl+e", "editor"}}, entries...)
}
return entries
case ModeSearch:
return []helpEntry{
{"enter", "apply"},
{"esc", "cancel"},
}
case ModeProjects:
return []helpEntry{
{"j/k", "navigate"},
{"enter", "select"},
{"n", "new project"},
{"esc", "cancel"},
}
case ModeStatus:
return []helpEntry{
{"j/k", "select"},
{"enter", "apply"},
{"esc", "cancel"},
}
case ModeBoard:
return []helpEntry{
{"j/k", "select"},
{"enter", "apply"},
{"esc", "cancel"},
}
case ModeConfirmDelete:
return []helpEntry{
{"y/enter", "confirm"},
{"esc", "cancel"},
}
default:
return []helpEntry{
{"j/k", "navigate"},
{"n", "new"},
{"e", "edit"},
{"u/U", "undo/redo"},
{"/", "search"},
{"p", "project"},
{"l", "list"},
{"s", "status"},
{"x", "delete"},
{"X", "force delete"},
{"q", "quit"},
}
}
}
func (m Model) helpParts() []string {
parts := make([]string, 0, len(m.helpEntries()))
for _, e := range m.helpEntries() {
parts = append(parts, m.styles.HelpKey.Render(e.key)+" "+m.styles.HelpDesc.Render(e.desc))
}
return parts
}
func (m Model) helpSep() string {
sep := m.styles.HelpSep.String()
if sep == "" {
return " "
}
return sep
}
// wrapHelpParts packs styled help snippets into lines that fit within width.
func wrapHelpParts(parts []string, width int, sep string) []string {
if width < 1 {
width = 1
}
sepW := lipgloss.Width(sep)
var lines []string
var cur string
for _, p := range parts {
pw := lipgloss.Width(p)
if cur == "" {
cur = p
// Oversized single entry still gets its own line so nothing is dropped.
if pw > width {
lines = append(lines, cur)
cur = ""
}
continue
}
if lipgloss.Width(cur)+sepW+pw <= width {
cur = cur + sep + p
continue
}
lines = append(lines, cur)
cur = p
if pw > width {
lines = append(lines, cur)
cur = ""
}
}
if cur != "" {
lines = append(lines, cur)
}
if len(lines) == 0 {
return []string{""}
}
return lines
}
func (m Model) renderFooter() string {
width := m.width
if width <= 0 {
width = 80
}
ver := m.styles.Version.Render(m.version)
verW := lipgloss.Width(ver)
sep := m.helpSep()
parts := m.helpParts()
// Wrap help across the full width so all bindings stay visible.
lines := wrapHelpParts(parts, width, sep)
// Place version on the last line to the right when it fits; otherwise
// put it on an extra right-aligned line.
last := lines[len(lines)-1]
gap := width - lipgloss.Width(last) - verW
if gap >= 1 {
lines[len(lines)-1] = last + strings.Repeat(" ", gap) + ver
} else {
pad := width - verW
if pad < 0 {
pad = 0
}
lines = append(lines, strings.Repeat(" ", pad)+ver)
}
return strings.Join(lines, "\n")
}