model.go

v0.5.1
Doc Versions Source
1
package tui
2
3
import (
4
	"strings"
5
6
	tea "github.com/charmbracelet/bubbletea"
7
	"github.com/charmbracelet/lipgloss"
8
	"sourcecraft.dev/bigbes/claudio/config"
9
	"sourcecraft.dev/bigbes/claudio/provider"
10
)
11
12
func (m Model) Cfg() *config.Config     { return m.cfg }
13
func (m Model) Quitting() bool          { return m.quitting }
14
func (m Model) LaunchAfterConfig() bool { return m.launchAfterConfig }
15
16
func (m Model) Init() tea.Cmd { return nil }
17
18
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
19
	if msg, ok := msg.(tea.WindowSizeMsg); ok {
20
		m.width = msg.Width
21
		m.height = msg.Height
22
	}
23
	switch m.phase {
24
	case phaseSelect:
25
		return m.updateSelect(msg)
26
	case phaseInputKey:
27
		return m.updateInput(msg)
28
	case phaseSelectPlan:
29
		return m.updateSelectPlan(msg)
30
	case phaseLoadingModels:
31
		return m.updateLoadingModels(msg)
32
	case phaseSelectModel:
33
		return m.updateSelectModel(msg)
34
	case phaseFilePicker:
35
		return m.updateFilePicker(msg)
36
	default:
37
		return m, tea.Quit
38
	}
39
}
40
41
func (m Model) View() string {
42
	switch m.phase {
43
	case phaseSelect:
44
		return m.viewSelect()
45
	case phaseInputKey:
46
		return m.viewInput()
47
	case phaseSelectPlan:
48
		return m.viewSelectPlan()
49
	case phaseLoadingModels:
50
		return m.viewLoadingModels()
51
	case phaseSelectModel:
52
		return m.viewSelectModel()
53
	case phaseFilePicker:
54
		return m.viewFilePicker()
55
	default:
56
		return ""
57
	}
58
}
59
60
func (m *Model) recomputeFilter() {
61
	query := strings.ToLower(m.filterInput.Value())
62
	m.filteredIdx = nil
63
	for i, e := range m.entries {
64
		if query == "" || strings.Contains(strings.ToLower(e.Name), query) || strings.Contains(strings.ToLower(e.Key), query) {
65
			m.filteredIdx = append(m.filteredIdx, i)
66
		}
67
	}
68
	if m.cursor >= len(m.filteredIdx) {
69
		m.cursor = max(0, len(m.filteredIdx)-1)
70
	}
71
}
72
73
func (m Model) resolveEntry() entry {
74
	if m.filterActive && len(m.filteredIdx) > 0 && m.cursor < len(m.filteredIdx) {
75
		return m.entries[m.filteredIdx[m.cursor]]
76
	}
77
	return m.entries[m.cursor]
78
}
79
80
// preSelectModel positions cursors on the currently active model.
81
func (m *Model) preSelectModel() {
82
	m.familyCursor = 0
83
	m.modelCursor = 0
84
	m.modelFocus = false
85
86
	pc := m.cfg.Providers[m.pickerProvider]
87
	current := pc.Model
88
	if current == "" {
89
		if reg, ok := provider.Registry[m.pickerProvider]; ok {
90
			current = reg.Model
91
		}
92
	}
93
	for fi, fam := range m.families {
94
		for mi, item := range m.familyModels[fam] {
95
			if item.ID == current {
96
				m.familyCursor = fi
97
				m.modelCursor = mi
98
				return
99
			}
100
		}
101
	}
102
}
103
104
func (m Model) pickerLabel() string {
105
	if reg, ok := provider.Registry[m.pickerProvider]; ok {
106
		return reg.Name
107
	}
108
	return m.pickerProvider
109
}
110
111
func (m Model) viewLoadingModels() string {
112
	label := m.pickerLabel()
113
	var b strings.Builder
114
	b.WriteString(logoStyle.Render(logo))
115
	b.WriteString("\n\n")
116
	b.WriteString(promptStyle.Render(" Fetching available " + label + " models..."))
117
	content := b.String()
118
	if m.width > 0 && m.height > 0 {
119
		return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, content)
120
	}
121
	return content
122
}
123

Source Files