1package repo23import (4 "path/filepath"56 "charm.land/bubbles/v2/key"7 "charm.land/bubbles/v2/spinner"8 tea "charm.land/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)1415// ReadmeMsg is a message sent when the readme is loaded.16type ReadmeMsg struct {17 Content string18 Path string19}2021// Readme is the readme component page.22type Readme struct {23 common common.Common24 code *code.Code25 ref RefMsg26 repo proto.Repository27 readmePath string28 spinner spinner.Model29 isLoading bool30}3132// 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 = true37 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}4647// Path implements common.TabComponent.48func (r *Readme) Path() string {49 return ""50}5152// TabName returns the name of the tab.53func (r *Readme) TabName() string {54 return "Readme"55}5657// SetSize implements common.Component.58func (r *Readme) SetSize(width, height int) {59 r.common.SetSize(width, height)60 r.code.SetSize(width, height)61}6263// ShortHelp implements help.KeyMap.64func (r *Readme) ShortHelp() []key.Binding {65 b := []key.Binding{66 r.common.KeyMap.UpDown,67 }68 return b69}7071// FullHelp implements help.KeyMap.72func (r *Readme) FullHelp() [][]key.Binding {73 k := r.code.KeyMap74 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 b89}9091// Init implements tea.Model.92func (r *Readme) Init() tea.Cmd {93 r.isLoading = true94 return tea.Batch(r.spinner.Tick, r.updateReadmeCmd)95}9697// Update implements tea.Model.98func (r *Readme) Update(msg tea.Msg) (common.Model, tea.Cmd) {99 cmds := make([]tea.Cmd, 0)100 switch msg := msg.(type) {101 case RepoMsg:102 r.repo = msg103 case RefMsg:104 r.ref = msg105 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 = false115 r.readmePath = msg.Path116 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 = s122 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}134135// 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}142143// SpinnerID implements common.TabComponent.144func (r *Readme) SpinnerID() int {145 return r.spinner.ID()146}147148// StatusBarValue implements statusbar.StatusBar.149func (r *Readme) StatusBarValue() string {150 dir := filepath.Dir(r.readmePath)151 if dir == "." || dir == "" {152 return " "153 }154 return dir155}156157// StatusBarInfo implements statusbar.StatusBar.158func (r *Readme) StatusBarInfo() string {159 return common.ScrollPercent(r.code.ScrollPosition())160}161162func (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 = rm169 m.Path = rp170 return m171}