okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawnadd charm tui skillf7fcae1

main
2.3 KiB97 linesraw
1package main
2
3import (
4	tea "charm.land/bubbletea/v2"
5	"context"
6	"example.com/charm-tui-worked/internal/display"
7	"fmt"
8	"strings"
9)
10
11type lineMsg string
12type closedMsg struct{}
13
14func waitForLine(ctx context.Context, ch <-chan string) tea.Cmd {
15	return func() tea.Msg {
16		line, ok := receive(ctx, ch)
17		if !ok {
18			return closedMsg{}
19		}
20		return lineMsg(line)
21	}
22}
23
24type model struct {
25	ctx           context.Context
26	cancel        context.CancelFunc
27	events        <-chan string
28	history       history
29	width, height int
30	ended         bool
31}
32
33func (m *model) rows() int     { return display.BodyRows(m.height, 1, 2) }
34func (m *model) Init() tea.Cmd { return waitForLine(m.ctx, m.events) }
35func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
36	switch msg := msg.(type) {
37	case tea.WindowSizeMsg:
38		m.width = max(0, msg.Width)
39		m.height = max(0, msg.Height)
40		m.history.resize(m.rows())
41	case tea.KeyPressMsg:
42		switch msg.String() {
43		case "q", "ctrl+c":
44			m.cancel()
45			return m, tea.Quit
46		case "up", "k":
47			m.history.scroll(-1, m.rows())
48		case "down", "j":
49			m.history.scroll(1, m.rows())
50		case "end":
51			m.history.latest(m.rows())
52		}
53	case lineMsg:
54		if m.ended {
55			return m, nil
56		}
57		m.history.append(string(msg), m.rows())
58		return m, waitForLine(m.ctx, m.events) // Exactly one successor listener.
59	case closedMsg:
60		m.ended = true
61		return m, nil // Never re-arm a closed stream.
62	}
63	return m, nil
64}
65func (m *model) View() tea.View {
66	v := tea.NewView("")
67	v.AltScreen = true
68	if m.width <= 0 || m.height <= 0 {
69		return v
70	}
71	if m.width < 20 || m.height < 5 {
72		v.Content = display.Line("resize · q quit", m.width)
73		return v
74	}
75	state := "following"
76	if !m.history.follow {
77		state = fmt.Sprintf("reading · %d new · end follows", m.history.unseen)
78	}
79	if m.history.expired {
80		state = "old anchor evicted · end follows"
81	}
82	if m.ended {
83		state += " · stream ended"
84	}
85	state += fmt.Sprintf(" · %d evicted", m.history.first)
86	lines := m.history.visible(m.rows())
87	for i := range lines {
88		lines[i] = display.SingleLine(lines[i])
89	}
90	v.Content = strings.Join([]string{
91		display.Line("worker events · retain last 32 records", m.width),
92		display.FillRows(lines, m.width, m.rows()),
93		display.Line(state, m.width),
94		display.Line("↑/↓ scroll · end latest · q quit", m.width),
95	}, "\n")
96	return v
97}