// Package tui реализует интерактивный выбор варианта: список превью слева, // полный текущий превью справа, стрелки для навигации, Enter — подтвердить. package tui import ( "fmt" "asciigen" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) var ( selectedStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("42")) normalStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("245")) previewBox = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).Padding(1, 2) ) // Model — состояние TUI. Renderer абстрагирует, картинка перед нами или текст. type Model struct { renderer asciigen.Renderer variants []asciigen.Variant cursor int chosen string // ID выбранного варианта после подтверждения, "" пока не выбрано fullText string // отрендеренная полная версия, заполняется после Enter err error } func New(r asciigen.Renderer) (Model, error) { variants, err := r.Previews() if err != nil { return Model{}, err } return Model{renderer: r, variants: variants}, nil } func (m Model) Init() tea.Cmd { return nil } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": return m, tea.Quit case "up", "k": if m.cursor > 0 { m.cursor-- } case "down", "j": if m.cursor < len(m.variants)-1 { m.cursor++ } case "enter": id := m.variants[m.cursor].ID full, err := m.renderer.Full(id) if err != nil { m.err = err return m, nil } m.chosen = id m.fullText = full return m, tea.Quit } } return m, nil } func (m Model) View() string { if m.err != nil { return fmt.Sprintf("Ошибка: %v\n", m.err) } var list string for i, v := range m.variants { line := v.Label if i == m.cursor { line = selectedStyle.Render("> " + line) } else { line = normalStyle.Render(" " + line) } list += line + "\n" } preview := previewBox.Render(m.variants[m.cursor].Preview) return lipgloss.JoinHorizontal(lipgloss.Top, list, " ", preview) + "\n\n↑/↓ выбор • Enter подтвердить • q выход\n" } // Chosen возвращает (ID, полный рендер, ok) — вызывать после того как // tea.NewProgram(...).Run() завершился. func (m Model) Chosen() (string, string, bool) { return m.chosen, m.fullText, m.chosen != "" }