diff options
| author | Paul Buetow <paul@buetow.org> | 2025-09-14 23:40:26 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-09-14 23:40:26 +0300 |
| commit | f4470bbcfbe3b14c99baeef475fe872825a13a39 (patch) | |
| tree | e12fc6168d21119dfff3a0fef5b6c5b54149f3ab /internal/hexaiaction/tui_custom.go | |
| parent | 68438c98d23545ff791768e3e219cd21d3814e0c (diff) | |
release: v0.10.0v0.10.0
Diffstat (limited to 'internal/hexaiaction/tui_custom.go')
| -rw-r--r-- | internal/hexaiaction/tui_custom.go | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/internal/hexaiaction/tui_custom.go b/internal/hexaiaction/tui_custom.go new file mode 100644 index 0000000..91d4b81 --- /dev/null +++ b/internal/hexaiaction/tui_custom.go @@ -0,0 +1,76 @@ +package hexaiaction + +import ( + "unicode/utf8" + + "github.com/charmbracelet/bubbles/list" + tea "github.com/charmbracelet/bubbletea" + + "codeberg.org/snonux/hexai/internal/appconfig" +) + +// RunTUIWithCustom shows the main menu plus a configurable "Custom actions…" item. +// If the user selects that item, it shows a submenu listing user-defined custom actions. +// On picking one, it sets selectedCustom and returns ActionCustom. +func RunTUIWithCustom(customs []appconfig.CustomAction, menuHotkey string) (ActionKind, error) { + // When no customs, fall back to default menu + if len(customs) == 0 { + return RunTUI() + } + // Build main menu with an extra entry + hk := 'a' + if r, _ := utf8.DecodeRuneInString(menuHotkey); r != utf8.RuneError && r != 0 { + hk = r + } + // Create a model with default items plus Custom actions… + m := newModel() + items := m.list.Items() + items = append(items, item{title: "Custom actions…", desc: "", kind: ActionCustom, hotkey: hk}) + m.list.SetItems(items) + // Run main menu + p := teaNewProgram(m) + md, err := p.Run() + if err != nil { + return ActionSkip, err + } + if mm, ok := md.(model); ok { + if mm.chosen != ActionCustom { + return mm.chosen, nil + } + } + // Custom submenu: list each action; select one maps to ActionCustom and sets global + sub := newModel() + subItems := make([]list.Item, 0, len(customs)) + for _, ca := range customs { + r := rune(0) + if rr, _ := utf8.DecodeRuneInString(ca.Hotkey); rr != utf8.RuneError && rr != 0 { + r = rr + } + subItems = append(subItems, item{title: ca.Title, desc: "", kind: ActionCustom, hotkey: r}) + } + sub.list.SetItems(subItems) + sp := teaNewProgram(sub) + smd, err := sp.Run() + if err != nil { + return ActionSkip, err + } + if sm, ok := smd.(model); ok { + if it, ok := sm.list.SelectedItem().(item); ok { + // Map by title + for i := range customs { + if customs[i].Title == it.title { + c := customs[i] + selectedCustom = &c + return ActionCustom, nil + } + } + } + } + return ActionSkip, nil +} + +// teaNewProgram is a tiny seam for tests to stub bubbletea program creation. +var teaNewProgram = func(m model) teaProgram { return tea.NewProgram(m) } + +// teaProgram is the subset of bubbletea.Program we need; enables testing seam. +type teaProgram interface{ Run() (tea.Model, error) } |
