@@ -3,14 +3,19 @@ package tui
4 "github.com/charmbracelet/bubbles/key"
4 "github.com/charmbracelet/bubbles/key"
5 tea "github.com/charmbracelet/bubbletea"
5 tea "github.com/charmbracelet/bubbletea"
6+ "github.com/charmbracelet/lipgloss"
7+ "github.com/kujtimiihoxha/termai/internal/tui/components/core"
6 "github.com/kujtimiihoxha/termai/internal/tui/layout"
8 "github.com/kujtimiihoxha/termai/internal/tui/layout"
7 "github.com/kujtimiihoxha/termai/internal/tui/page"
9 "github.com/kujtimiihoxha/termai/internal/tui/page"
10+ "github.com/kujtimiihoxha/termai/internal/tui/util"
10type keyMap struct {
13type keyMap struct {
11− Logs key.Binding
15+ Return key.Binding
12− Back key.Binding
16+ Back key.Binding
13− Quit key.Binding
18+ Help key.Binding
16var keys = keyMap{
21var keys = keyMap{
@@ -18,14 +23,22 @@ var keys = keyMap{
18 key.WithKeys("L"),
23 key.WithKeys("L"),
19 key.WithHelp("L", "logs"),
24 key.WithHelp("L", "logs"),
21− Back: key.NewBinding(
26+ Return: key.NewBinding(
22 key.WithKeys("esc"),
27 key.WithKeys("esc"),
23− key.WithHelp("esc", "back"),
28+ key.WithHelp("esc", "close"),
30+ Back: key.NewBinding(
31+ key.WithKeys("backspace"),
32+ key.WithHelp("backspace", "back"),
25 Quit: key.NewBinding(
34 Quit: key.NewBinding(
26 key.WithKeys("ctrl+c", "q"),
35 key.WithKeys("ctrl+c", "q"),
27 key.WithHelp("ctrl+c/q", "quit"),
36 key.WithHelp("ctrl+c/q", "quit"),
38+ Help: key.NewBinding(
40+ key.WithHelp("?", "toggle help"),
31type appModel struct {
44type appModel struct {
@@ -34,6 +47,9 @@ type appModel struct {
34 previousPage page.PageID
47 previousPage page.PageID
35 pages map[page.PageID]tea.Model
48 pages map[page.PageID]tea.Model
36 loadedPages map[page.PageID]bool
49 loadedPages map[page.PageID]bool
39func (a appModel) Init() tea.Cmd {
55func (a appModel) Init() tea.Cmd {
@@ -45,28 +61,61 @@ func (a appModel) Init() tea.Cmd {
45func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
61func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
46 switch msg := msg.(type) {
62 switch msg := msg.(type) {
47 case tea.WindowSizeMsg:
63 case tea.WindowSizeMsg:
64+ msg.Height -= 1 // Make space for the status bar
48 a.width, a.height = msg.Width, msg.Height
65 a.width, a.height = msg.Width, msg.Height
67+ a.status, _ = a.status.Update(msg)
69+ uh, _ := a.help.Update(msg)
70+ a.help = uh.(core.HelpCmp)
72+ p, cmd := a.pages[a.currentPage].Update(msg)
73+ a.pages[a.currentPage] = p
76+ a.status, _ = a.status.Update(msg)
78+ a.status, _ = a.status.Update(msg)
49 case tea.KeyMsg:
79 case tea.KeyMsg:
50− if key.Matches(msg, keys.Quit) {
81+ case key.Matches(msg, keys.Quit):
51 return a, tea.Quit
82 return a, tea.Quit
53− if key.Matches(msg, keys.Back) {
83+ case key.Matches(msg, keys.Back):
54 if a.previousPage != "" {
84 if a.previousPage != "" {
55 return a, a.moveToPage(a.previousPage)
85 return a, a.moveToPage(a.previousPage)
87+ case key.Matches(msg, keys.Return):
57 return a, nil
92 return a, nil
59− if key.Matches(msg, keys.Logs) {
93+ case key.Matches(msg, keys.Logs):
60 return a, a.moveToPage(page.LogsPage)
94 return a, a.moveToPage(page.LogsPage)
95+ case key.Matches(msg, keys.Help):
63 p, cmd := a.pages[a.currentPage].Update(msg)
100 p, cmd := a.pages[a.currentPage].Update(msg)
65− a.pages[a.currentPage] = p
101+ a.pages[a.currentPage] = p
67 return a, cmd
102 return a, cmd
105+func (a *appModel) ToggleHelp() {
108+ a.height += a.help.Height()
111+ a.height -= a.help.Height()
114+ if sizable, ok := a.pages[a.currentPage].(layout.Sizeable); ok {
115+ sizable.SetSize(a.width, a.height)
70func (a *appModel) moveToPage(pageID page.PageID) tea.Cmd {
119func (a *appModel) moveToPage(pageID page.PageID) tea.Cmd {
71 var cmd tea.Cmd
120 var cmd tea.Cmd
72 if _, ok := a.loadedPages[pageID]; !ok {
121 if _, ok := a.loadedPages[pageID]; !ok {
@@ -83,13 +132,30 @@ func (a *appModel) moveToPage(pageID page.PageID) tea.Cmd {
85func (a appModel) View() string {
134func (a appModel) View() string {
86− return a.pages[a.currentPage].View()
135+ components := []string{
136+ a.pages[a.currentPage].View(),
140+ bindings := layout.KeyMapToSlice(keys)
141+ if p, ok := a.pages[a.currentPage].(layout.Bindings); ok {
142+ bindings = append(bindings, p.BindingKeys()...)
144+ a.help.SetBindings(bindings)
145+ components = append(components, a.help.View())
148+ components = append(components, a.status.View())
150+ return lipgloss.JoinVertical(lipgloss.Top, components...)
89func New() tea.Model {
153func New() tea.Model {
90 return &appModel{
154 return &appModel{
91 currentPage: page.ReplPage,
155 currentPage: page.ReplPage,
92 loadedPages: make(map[page.PageID]bool),
156 loadedPages: make(map[page.PageID]bool),
157+ status: core.NewStatusCmp(),
158+ help: core.NewHelpCmp(),
93 pages: map[page.PageID]tea.Model{
159 pages: map[page.PageID]tea.Model{
94 page.LogsPage: page.NewLogsPage(),
160 page.LogsPage: page.NewLogsPage(),
95 page.InitPage: page.NewInitPage(),
161 page.InitPage: page.NewInitPage(),