1package about
2
3import (
4 "github.com/charmbracelet/bubbles/viewport"
5 tea "github.com/charmbracelet/bubbletea"
6 "github.com/charmbracelet/soft-serve/git"
7 "github.com/charmbracelet/soft-serve/internal/tui/style"
8 "github.com/charmbracelet/soft-serve/tui/common"
9 "github.com/charmbracelet/soft-serve/tui/refs"
10 vp "github.com/charmbracelet/soft-serve/tui/viewport"
11 "github.com/muesli/reflow/wrap"
12)
13
14type Bubble struct {
15 readmeViewport *vp.ViewportBubble
16 repo common.GitRepo
17 styles *style.Styles
18 height int
19 heightMargin int
20 width int
21 widthMargin int
22 ref *git.Reference
23}
24
25func NewBubble(repo common.GitRepo, styles *style.Styles, width, wm, height, hm int) *Bubble {
26 b := &Bubble{
27 readmeViewport: &vp.ViewportBubble{
28 Viewport: &viewport.Model{},
29 },
30 repo: repo,
31 styles: styles,
32 widthMargin: wm,
33 heightMargin: hm,
34 }
35 b.SetSize(width, height)
36 return b
37}
38func (b *Bubble) Init() tea.Cmd {
39 return b.reset
40}
41
42func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
43 var cmds []tea.Cmd
44 switch msg := msg.(type) {
45 case tea.WindowSizeMsg:
46 b.SetSize(msg.Width, msg.Height)
47 // XXX: if we find that longer readmes take more than a few
48 // milliseconds to render we may need to move Glamour rendering into a
49 // command.
50 md, err := b.glamourize()
51 if err != nil {
52 return b, nil
53 }
54 b.readmeViewport.Viewport.SetContent(md)
55 case tea.KeyMsg:
56 switch msg.String() {
57 case "R":
58 return b, b.reset
59 }
60 case refs.RefMsg:
61 b.ref = msg
62 return b, b.reset
63 }
64 rv, cmd := b.readmeViewport.Update(msg)
65 b.readmeViewport = rv.(*vp.ViewportBubble)
66 cmds = append(cmds, cmd)
67 return b, tea.Batch(cmds...)
68}
69
70func (b *Bubble) SetSize(w, h int) {
71 b.width = w
72 b.height = h
73 b.readmeViewport.Viewport.Width = w - b.widthMargin
74 b.readmeViewport.Viewport.Height = h - b.heightMargin
75}
76
77func (b *Bubble) GotoTop() {
78 b.readmeViewport.Viewport.GotoTop()
79}
80
81func (b *Bubble) View() string {
82 return b.readmeViewport.View()
83}
84
85func (b *Bubble) Help() []common.HelpEntry {
86 return nil
87}
88
89func (b *Bubble) glamourize() (string, error) {
90 w := b.width - b.widthMargin - b.styles.RepoBody.GetHorizontalFrameSize()
91 rm, rp := b.repo.Readme()
92 if rm == "" {
93 return b.styles.AboutNoReadme.Render("No readme found."), nil
94 }
95 f, err := common.RenderFile(rp, rm, w)
96 if err != nil {
97 return "", err
98 }
99 // For now, hard-wrap long lines in Glamour that would otherwise break the
100 // layout when wrapping. This may be due to #43 in Reflow, which has to do
101 // with a bug in the way lines longer than the given width are wrapped.
102 //
103 // https://github.com/muesli/reflow/issues/43
104 //
105 // TODO: solve this upstream in Glamour/Reflow.
106 return wrap.String(f, w), nil
107}
108
109func (b *Bubble) reset() tea.Msg {
110 md, err := b.glamourize()
111 if err != nil {
112 return common.ErrMsg{Err: err}
113 }
114 head, err := b.repo.HEAD()
115 if err != nil {
116 return common.ErrMsg{Err: err}
117 }
118 b.ref = head
119 b.readmeViewport.Viewport.SetContent(md)
120 b.GotoTop()
121 return nil
122}