1package repo
2
3import (
4 "fmt"
5 "path/filepath"
6
7 "github.com/charmbracelet/bubbles/key"
8 "github.com/charmbracelet/bubbles/spinner"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/charmbracelet/soft-serve/pkg/backend"
11 "github.com/charmbracelet/soft-serve/pkg/proto"
12 "github.com/charmbracelet/soft-serve/pkg/ui/common"
13 "github.com/charmbracelet/soft-serve/pkg/ui/components/code"
14)
15
16// ReadmeMsg is a message sent when the readme is loaded.
17type ReadmeMsg struct {
18 Content string
19 Path string
20}
21
22// Readme is the readme component page.
23type Readme struct {
24 common common.Common
25 code *code.Code
26 ref RefMsg
27 repo proto.Repository
28 readmePath string
29 spinner spinner.Model
30 isLoading bool
31}
32
33// NewReadme creates a new readme model.
34func NewReadme(common common.Common) *Readme {
35 readme := code.New(common, "", "")
36 readme.NoContentStyle = readme.NoContentStyle.SetString("No readme found.")
37 readme.UseGlamour = true
38 s := spinner.New(spinner.WithSpinner(spinner.Dot),
39 spinner.WithStyle(common.Styles.Spinner))
40 return &Readme{
41 code: readme,
42 common: common,
43 spinner: s,
44 isLoading: true,
45 }
46}
47
48// Path implements common.TabComponent.
49func (r *Readme) Path() string {
50 return ""
51}
52
53// TabName returns the name of the tab.
54func (r *Readme) TabName() string {
55 return "Readme"
56}
57
58// SetSize implements common.Component.
59func (r *Readme) SetSize(width, height int) {
60 r.common.SetSize(width, height)
61 r.code.SetSize(width, height)
62}
63
64// ShortHelp implements help.KeyMap.
65func (r *Readme) ShortHelp() []key.Binding {
66 b := []key.Binding{
67 r.common.KeyMap.UpDown,
68 }
69 return b
70}
71
72// FullHelp implements help.KeyMap.
73func (r *Readme) FullHelp() [][]key.Binding {
74 k := r.code.KeyMap
75 b := [][]key.Binding{
76 {
77 k.PageDown,
78 k.PageUp,
79 k.HalfPageDown,
80 k.HalfPageUp,
81 },
82 {
83 k.Down,
84 k.Up,
85 r.common.KeyMap.GotoTop,
86 r.common.KeyMap.GotoBottom,
87 },
88 }
89 return b
90}
91
92// Init implements tea.Model.
93func (r *Readme) Init() tea.Cmd {
94 r.isLoading = true
95 return tea.Batch(r.spinner.Tick, r.updateReadmeCmd)
96}
97
98// Update implements tea.Model.
99func (r *Readme) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
100 cmds := make([]tea.Cmd, 0)
101 switch msg := msg.(type) {
102 case RepoMsg:
103 r.repo = msg
104 case RefMsg:
105 r.ref = msg
106 cmds = append(cmds, r.Init())
107 case tea.WindowSizeMsg:
108 r.SetSize(msg.Width, msg.Height)
109 case EmptyRepoMsg:
110 cmds = append(cmds,
111 r.code.SetContent(defaultEmptyRepoMsg(r.common.Config(),
112 r.repo.Name()), ".md"),
113 )
114 case ReadmeMsg:
115 r.isLoading = false
116 r.readmePath = msg.Path
117 r.code.GotoTop()
118 cmds = append(cmds, r.code.SetContent(msg.Content, msg.Path))
119 case spinner.TickMsg:
120 if r.isLoading && r.spinner.ID() == msg.ID {
121 s, cmd := r.spinner.Update(msg)
122 r.spinner = s
123 if cmd != nil {
124 cmds = append(cmds, cmd)
125 }
126 }
127 }
128 c, cmd := r.code.Update(msg)
129 r.code = c.(*code.Code)
130 if cmd != nil {
131 cmds = append(cmds, cmd)
132 }
133 return r, tea.Batch(cmds...)
134}
135
136// View implements tea.Model.
137func (r *Readme) View() string {
138 if r.isLoading {
139 return renderLoading(r.common, r.spinner)
140 }
141 return r.code.View()
142}
143
144// SpinnerID implements common.TabComponent.
145func (r *Readme) SpinnerID() int {
146 return r.spinner.ID()
147}
148
149// StatusBarValue implements statusbar.StatusBar.
150func (r *Readme) StatusBarValue() string {
151 dir := filepath.Dir(r.readmePath)
152 if dir == "." || dir == "" {
153 return " "
154 }
155 return dir
156}
157
158// StatusBarInfo implements statusbar.StatusBar.
159func (r *Readme) StatusBarInfo() string {
160 return fmt.Sprintf("☰ %d%%", r.code.ScrollPosition())
161}
162
163func (r *Readme) updateReadmeCmd() tea.Msg {
164 m := ReadmeMsg{}
165 if r.repo == nil {
166 return common.ErrorMsg(common.ErrMissingRepo)
167 }
168 rm, rp, _ := backend.Readme(r.repo, r.ref)
169 m.Content = rm
170 m.Path = rp
171 return m
172}