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