package tui import ( "strings" "charm.land/lipgloss/v2" ) // KeyHint displays a keyboard shortcut hint. type KeyHint struct { Key string Action string } // KeyHints renders a row of key hints. type KeyHints struct { hints []KeyHint styles KeyHintStyles maxWidth int } // KeyHintStyles holds styling for key hints. type KeyHintStyles struct { Key lipgloss.Style Action lipgloss.Style Divider lipgloss.Style } // DefaultKeyHintStyles returns default styles. func DefaultKeyHintStyles(isDark bool) KeyHintStyles { if isDark { return KeyHintStyles{ Key: lipgloss.NewStyle().Foreground(lipgloss.Color("#88c0d0")).Background(lipgloss.Color("#3b4252")).Padding(0, 1), Action: lipgloss.NewStyle().Foreground(lipgloss.Color("#4c566a")), Divider: lipgloss.NewStyle().Foreground(lipgloss.Color("#3b4252")), } } return KeyHintStyles{ Key: lipgloss.NewStyle().Foreground(lipgloss.Color("#4f8f8f")).Background(lipgloss.Color("#e5e9f0")).Padding(0, 1), Action: lipgloss.NewStyle().Foreground(lipgloss.Color("#9ca0a8")), Divider: lipgloss.NewStyle().Foreground(lipgloss.Color("#d8dee9")), } } // NewKeyHints creates a new key hints component. func NewKeyHints(hints []KeyHint, maxWidth int, isDark bool) *KeyHints { return &KeyHints{ hints: hints, styles: DefaultKeyHintStyles(isDark), maxWidth: maxWidth, } } // SetDark updates theme. func (kh *KeyHints) SetDark(isDark bool) { kh.styles = DefaultKeyHintStyles(isDark) } // SetHints updates the hints. func (kh *KeyHints) SetHints(hints []KeyHint) { kh.hints = hints } // Render returns the key hints as a single line. func (kh *KeyHints) Render() string { if len(kh.hints) == 0 { return "" } var b strings.Builder b.WriteString(kh.styles.Divider.Render("│")) for i, hint := range kh.hints { if i > 0 { b.WriteString(" ") } b.WriteString(kh.styles.Key.Render(hint.Key)) b.WriteString(" ") b.WriteString(kh.styles.Action.Render(hint.Action)) } return b.String() } // RenderInline renders hints as inline text (no key box). func (kh *KeyHints) RenderInline() string { if len(kh.hints) == 0 { return "" } var b strings.Builder for i, hint := range kh.hints { if i > 0 { b.WriteString(" · ") } b.WriteString(hint.Key) b.WriteString(" ") b.WriteString(kh.styles.Action.Render(hint.Action)) } return b.String() } // SetMaxWidth sets the maximum width for wrapping. func (kh *KeyHints) SetMaxWidth(w int) { kh.maxWidth = w } func defaultHintsForLang(lang Lang) []KeyHint { loc := Locale(lang) return []KeyHint{ {Key: "Enter", Action: loc.HintSend}, {Key: "Tab", Action: loc.HintComplete}, {Key: "?", Action: loc.HintHelp}, {Key: "Esc", Action: loc.HintCancel}, {Key: "Ctrl+C / F10", Action: loc.HintQuit}, } } // DefaultKeyHints returns common key hints for the application. func DefaultKeyHints(lang Lang, isDark bool) *KeyHints { return NewKeyHints(defaultHintsForLang(lang), 60, isDark) } // FooterHints returns hints shown in the footer. func FooterHints(lang Lang, keys KeyMap, isDark bool) *KeyHints { loc := Locale(lang) hints := []KeyHint{ {Key: "?", Action: loc.HintHelp}, {Key: "Ctrl+N", Action: loc.HintNew}, {Key: "Ctrl+L", Action: loc.HintClear}, } return NewKeyHints(hints, 40, isDark) } // InputHints returns hints shown when typing. func InputHints(lang Lang, keys KeyMap, isDark bool) *KeyHints { loc := Locale(lang) hints := []KeyHint{ {Key: "Tab", Action: loc.HintComplete}, {Key: "/", Action: loc.HintCommands}, {Key: "@", Action: loc.HintFiles}, {Key: "#", Action: loc.HintSkills}, } return NewKeyHints(hints, 40, isDark) }