Detailed changes
@@ -21,6 +21,11 @@ type IdentifiableItem interface {
ID() string
}
+// ItemDelegate is a wrapper around list.ItemDelegate.
+type ItemDelegate interface {
+ list.ItemDelegate
+}
+
// SelectMsg is a message that is sent when an item is selected.
type SelectMsg struct{ IdentifiableItem }
@@ -28,7 +33,7 @@ type SelectMsg struct{ IdentifiableItem }
type ActiveMsg struct{ IdentifiableItem }
// New creates a new selector.
-func New(common common.Common, items []IdentifiableItem, delegate list.ItemDelegate) *Selector {
+func New(common common.Common, items []IdentifiableItem, delegate ItemDelegate) *Selector {
itms := make([]list.Item, len(items))
for i, item := range items {
itms[i] = item
@@ -109,8 +114,12 @@ func (s *Selector) SetSize(width, height int) {
}
// SetItems sets the items in the selector.
-func (s *Selector) SetItems(items []list.Item) tea.Cmd {
- return s.Model.SetItems(items)
+func (s *Selector) SetItems(items []IdentifiableItem) tea.Cmd {
+ its := make([]list.Item, len(items))
+ for i, item := range items {
+ its[i] = item
+ }
+ return s.Model.SetItems(its)
}
// Index returns the index of the selected item.
@@ -0,0 +1,273 @@
+package repo
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ "path/filepath"
+
+ tea "github.com/charmbracelet/bubbletea"
+ ggit "github.com/charmbracelet/soft-serve/git"
+ "github.com/charmbracelet/soft-serve/ui/common"
+ "github.com/charmbracelet/soft-serve/ui/components/code"
+ "github.com/charmbracelet/soft-serve/ui/components/selector"
+ "github.com/charmbracelet/soft-serve/ui/git"
+)
+
+type filesView int
+
+const (
+ filesViewFiles filesView = iota
+ filesViewContent
+)
+
+var (
+ errNoFileSelected = errors.New("no file selected")
+ errBinaryFile = errors.New("binary file")
+ errFileTooLarge = errors.New("file is too large")
+ errInvalidFile = errors.New("invalid file")
+)
+
+// FileItemsMsg is a message that contains a list of files.
+type FileItemsMsg []selector.IdentifiableItem
+
+// FileContentMsg is a message that contains the content of a file.
+type FileContentMsg struct {
+ content string
+ ext string
+}
+
+// Files is the model for the files view.
+type Files struct {
+ common common.Common
+ selector *selector.Selector
+ ref *ggit.Reference
+ activeView filesView
+ repo git.GitRepo
+ code *code.Code
+ path string
+ currentItem *FileItem
+ currentContent FileContentMsg
+ lastSelected []int
+}
+
+// NewFiles creates a new files model.
+func NewFiles(common common.Common) *Files {
+ f := &Files{
+ common: common,
+ code: code.New(common, "", ""),
+ activeView: filesViewFiles,
+ lastSelected: make([]int, 0),
+ }
+ selector := selector.New(common, []selector.IdentifiableItem{}, FileItemDelegate{common.Styles})
+ selector.SetShowFilter(false)
+ selector.SetShowHelp(false)
+ selector.SetShowPagination(false)
+ selector.SetShowStatusBar(false)
+ selector.SetShowTitle(false)
+ selector.SetFilteringEnabled(false)
+ selector.DisableQuitKeybindings()
+ selector.KeyMap.NextPage = common.KeyMap.NextPage
+ selector.KeyMap.PrevPage = common.KeyMap.PrevPage
+ f.selector = selector
+ return f
+}
+
+// SetSize implements common.Component.
+func (f *Files) SetSize(width, height int) {
+ f.common.SetSize(width, height)
+ f.selector.SetSize(width, height)
+ f.code.SetSize(width, height)
+}
+
+// Init implements tea.Model.
+func (f *Files) Init() tea.Cmd {
+ f.path = ""
+ f.currentItem = nil
+ f.activeView = filesViewFiles
+ f.lastSelected = make([]int, 0)
+ return f.updateFilesCmd
+}
+
+// Update implements tea.Model.
+func (f *Files) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+ cmds := make([]tea.Cmd, 0)
+ switch msg := msg.(type) {
+ case RepoMsg:
+ f.selector.Select(0)
+ f.repo = git.GitRepo(msg)
+ cmds = append(cmds, f.Init())
+ case RefMsg:
+ f.ref = msg
+ cmds = append(cmds, f.Init())
+ case FileItemsMsg:
+ cmds = append(cmds,
+ f.selector.SetItems(msg),
+ updateStatusBarCmd,
+ )
+ case FileContentMsg:
+ f.activeView = filesViewContent
+ f.currentContent = msg
+ f.code.SetContent(msg.content, msg.ext)
+ f.code.GotoTop()
+ cmds = append(cmds, updateStatusBarCmd)
+ case selector.SelectMsg:
+ switch sel := msg.IdentifiableItem.(type) {
+ case FileItem:
+ f.currentItem = &sel
+ f.path = filepath.Join(f.path, sel.entry.Name())
+ log.Printf("selected index %d", f.selector.Index())
+ if sel.entry.IsTree() {
+ cmds = append(cmds, f.selectTreeCmd)
+ } else {
+ cmds = append(cmds, f.selectFileCmd)
+ }
+ }
+ case tea.KeyMsg:
+ switch f.activeView {
+ case filesViewFiles:
+ switch msg.String() {
+ case "l", "right":
+ cmds = append(cmds, f.selector.SelectItem)
+ case "h", "left":
+ cmds = append(cmds, f.deselectItemCmd)
+ }
+ case filesViewContent:
+ switch msg.String() {
+ case "h", "left":
+ cmds = append(cmds, f.deselectItemCmd)
+ }
+ }
+ case tea.WindowSizeMsg:
+ if f.currentContent.content != "" {
+ m, cmd := f.code.Update(msg)
+ f.code = m.(*code.Code)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ }
+ if f.repo != nil {
+ cmds = append(cmds, f.updateFilesCmd)
+ }
+ }
+ switch f.activeView {
+ case filesViewFiles:
+ m, cmd := f.selector.Update(msg)
+ f.selector = m.(*selector.Selector)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ case filesViewContent:
+ m, cmd := f.code.Update(msg)
+ f.code = m.(*code.Code)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ }
+ return f, tea.Batch(cmds...)
+}
+
+// View implements tea.Model.
+func (f *Files) View() string {
+ switch f.activeView {
+ case filesViewFiles:
+ return f.selector.View()
+ case filesViewContent:
+ return f.code.View()
+ default:
+ return ""
+ }
+}
+
+// StatusBarValue returns the status bar value.
+func (f *Files) StatusBarValue() string {
+ p := f.path
+ if p == "." {
+ return ""
+ }
+ return p
+}
+
+// StatusBarInfo returns the status bar info.
+func (f *Files) StatusBarInfo() string {
+ switch f.activeView {
+ case filesViewFiles:
+ return fmt.Sprintf("%d/%d", f.selector.Index()+1, len(f.selector.VisibleItems()))
+ case filesViewContent:
+ return fmt.Sprintf("%.f%%", f.code.ScrollPercent()*100)
+ default:
+ return ""
+ }
+}
+
+func (f *Files) updateFilesCmd() tea.Msg {
+ files := make([]selector.IdentifiableItem, 0)
+ dirs := make([]selector.IdentifiableItem, 0)
+ t, err := f.repo.Tree(f.ref, f.path)
+ if err != nil {
+ return common.ErrorMsg(err)
+ }
+ ents, err := t.Entries()
+ if err != nil {
+ return common.ErrorMsg(err)
+ }
+ ents.Sort()
+ for _, e := range ents {
+ if e.IsTree() {
+ dirs = append(dirs, FileItem{e})
+ } else {
+ files = append(files, FileItem{e})
+ }
+ }
+ return FileItemsMsg(append(dirs, files...))
+}
+
+func (f *Files) selectTreeCmd() tea.Msg {
+ if f.currentItem != nil && f.currentItem.entry.IsTree() {
+ f.lastSelected = append(f.lastSelected, f.selector.Index())
+ f.selector.Select(0)
+ return f.updateFilesCmd()
+ }
+ return common.ErrorMsg(errNoFileSelected)
+}
+
+func (f *Files) selectFileCmd() tea.Msg {
+ i := f.currentItem
+ if i != nil && !i.entry.IsTree() {
+ fi := i.entry.File()
+ if i.Mode().IsDir() || f == nil {
+ return common.ErrorMsg(errInvalidFile)
+ }
+ bin, err := fi.IsBinary()
+ if err != nil {
+ f.path = filepath.Dir(f.path)
+ return common.ErrorMsg(err)
+ }
+ if bin {
+ f.path = filepath.Dir(f.path)
+ return common.ErrorMsg(errBinaryFile)
+ }
+ c, err := fi.Bytes()
+ if err != nil {
+ f.path = filepath.Dir(f.path)
+ return common.ErrorMsg(err)
+ }
+ f.lastSelected = append(f.lastSelected, f.selector.Index())
+ return FileContentMsg{string(c), i.entry.Name()}
+ }
+ return common.ErrorMsg(errNoFileSelected)
+}
+
+func (f *Files) deselectItemCmd() tea.Msg {
+ f.path = filepath.Dir(f.path)
+ f.activeView = filesViewFiles
+ msg := f.updateFilesCmd()
+ index := 0
+ if len(f.lastSelected) > 0 {
+ index = f.lastSelected[len(f.lastSelected)-1]
+ f.lastSelected = f.lastSelected[:len(f.lastSelected)-1]
+ }
+ log.Printf("deselect %d", index)
+ f.selector.Select(index)
+ return msg
+}
@@ -0,0 +1,119 @@
+package repo
+
+import (
+ "fmt"
+ "io"
+ "io/fs"
+
+ "github.com/charmbracelet/bubbles/list"
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/soft-serve/git"
+ "github.com/charmbracelet/soft-serve/ui/styles"
+ "github.com/dustin/go-humanize"
+)
+
+// FileItem is a list item for a file.
+type FileItem struct {
+ entry *git.TreeEntry
+}
+
+// ID returns the ID of the file item.
+func (i FileItem) ID() string {
+ return i.entry.Name()
+}
+
+// Title returns the title of the file item.
+func (i FileItem) Title() string {
+ return i.entry.Name()
+}
+
+// Description returns the description of the file item.
+func (i FileItem) Description() string {
+ return ""
+}
+
+// Mode returns the mode of the file item.
+func (i FileItem) Mode() fs.FileMode {
+ return i.entry.Mode()
+}
+
+// FilterValue implements list.Item.
+func (i FileItem) FilterValue() string { return i.Title() }
+
+// FileItems is a list of file items.
+type FileItems []FileItem
+
+// Len implements sort.Interface.
+func (cl FileItems) Len() int { return len(cl) }
+
+// Swap implements sort.Interface.
+func (cl FileItems) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
+
+// Less implements sort.Interface.
+func (cl FileItems) Less(i, j int) bool {
+ if cl[i].entry.IsTree() && cl[j].entry.IsTree() {
+ return cl[i].Title() < cl[j].Title()
+ } else if cl[i].entry.IsTree() {
+ return true
+ } else if cl[j].entry.IsTree() {
+ return false
+ } else {
+ return cl[i].Title() < cl[j].Title()
+ }
+}
+
+// FileItemDelegate is the delegate for the file item list.
+type FileItemDelegate struct {
+ style *styles.Styles
+}
+
+// Height returns the height of the file item list. Implements list.ItemDelegate.
+func (d FileItemDelegate) Height() int { return 1 }
+
+// Spacing returns the spacing of the file item list. Implements list.ItemDelegate.
+func (d FileItemDelegate) Spacing() int { return 0 }
+
+// Update implements list.ItemDelegate.
+func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
+
+// Render implements list.ItemDelegate.
+func (d FileItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
+ s := d.style
+ i, ok := listItem.(FileItem)
+ if !ok {
+ return
+ }
+
+ name := i.Title()
+ size := humanize.Bytes(uint64(i.entry.Size()))
+ if i.entry.IsTree() {
+ size = ""
+ name = s.TreeFileDir.Render(name)
+ }
+ var cs lipgloss.Style
+ mode := i.Mode()
+ if index == m.Index() {
+ cs = s.TreeItemActive
+ fmt.Fprint(w, s.TreeItemSelector.Render(">"))
+ } else {
+ cs = s.TreeItemInactive
+ fmt.Fprint(w, s.TreeItemSelector.Render(" "))
+ }
+ leftMargin := s.TreeItemSelector.GetMarginLeft() +
+ s.TreeItemSelector.GetWidth() +
+ s.TreeFileMode.GetMarginLeft() +
+ s.TreeFileMode.GetWidth() +
+ cs.GetMarginLeft()
+ rightMargin := s.TreeFileSize.GetMarginLeft() + lipgloss.Width(size)
+ name = truncateString(name, m.Width()-leftMargin-rightMargin, "…")
+ sizeStyle := s.TreeFileSize.Copy().
+ Width(m.Width() -
+ leftMargin -
+ s.TreeFileSize.GetMarginLeft() -
+ lipgloss.Width(name)).
+ Align(lipgloss.Right)
+ fmt.Fprint(w, s.TreeFileMode.Render(mode.String())+
+ cs.Render(name)+
+ sizeStyle.Render(size))
+}
@@ -6,7 +6,6 @@ import (
"time"
"github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
gansi "github.com/charmbracelet/glamour/ansi"
@@ -20,18 +19,18 @@ import (
"github.com/muesli/termenv"
)
-type view int
+type logView int
const (
- logView view = iota
- commitView
+ logViewCommits logView = iota
+ logViewDiff
)
// LogCountMsg is a message that contains the number of commits in a repo.
type LogCountMsg int64
// LogItemsMsg is a message that contains a slice of LogItem.
-type LogItemsMsg []list.Item
+type LogItemsMsg []selector.IdentifiableItem
// LogCommitMsg is a message that contains a git commit.
type LogCommitMsg *ggit.Commit
@@ -44,11 +43,12 @@ type Log struct {
common common.Common
selector *selector.Selector
vp *viewport.Viewport
- activeView view
+ activeView logView
repo git.GitRepo
ref *ggit.Reference
count int64
nextPage int
+ activeCommit *ggit.Commit
selectedCommit *ggit.Commit
currentDiff *ggit.Diff
}
@@ -58,7 +58,7 @@ func NewLog(common common.Common) *Log {
l := &Log{
common: common,
vp: viewport.New(),
- activeView: logView,
+ activeView: logViewCommits,
}
selector := selector.New(common, []selector.IdentifiableItem{}, LogItemDelegate{common.Styles})
selector.SetShowFilter(false)
@@ -84,7 +84,7 @@ func (l *Log) SetSize(width, height int) {
// ShortHelp implements key.KeyMap.
func (l *Log) ShortHelp() []key.Binding {
switch l.activeView {
- case logView:
+ case logViewCommits:
return []key.Binding{
key.NewBinding(
key.WithKeys(
@@ -97,7 +97,7 @@ func (l *Log) ShortHelp() []key.Binding {
),
),
}
- case commitView:
+ case logViewDiff:
return []key.Binding{
l.common.KeyMap.UpDown,
key.NewBinding(
@@ -143,9 +143,10 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, l.selector.SetItems(msg))
l.selector.SetPage(l.nextPage)
l.SetSize(l.common.Width, l.common.Height)
+ l.activeCommit = l.selector.SelectedItem().(LogItem).Commit
case tea.KeyMsg, tea.MouseMsg:
switch l.activeView {
- case logView:
+ case logViewCommits:
switch key := msg.(type) {
case tea.KeyMsg:
switch key.String() {
@@ -164,15 +165,21 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, l.updateCommitsCmd)
}
cmds = append(cmds, cmd)
- case commitView:
+ case logViewDiff:
switch key := msg.(type) {
case tea.KeyMsg:
switch key.String() {
case "h", "left":
- l.activeView = logView
+ l.activeView = logViewCommits
}
}
}
+ case selector.ActiveMsg:
+ switch sel := msg.IdentifiableItem.(type) {
+ case LogItem:
+ l.activeCommit = sel.Commit
+ }
+ cmds = append(cmds, updateStatusBarCmd)
case selector.SelectMsg:
switch sel := msg.IdentifiableItem.(type) {
case LogItem:
@@ -191,7 +198,7 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
),
)
l.vp.GotoTop()
- l.activeView = commitView
+ l.activeView = logViewDiff
cmds = append(cmds, updateStatusBarCmd)
case tea.WindowSizeMsg:
if l.selectedCommit != nil && l.currentDiff != nil {
@@ -208,7 +215,7 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
switch l.activeView {
- case commitView:
+ case logViewDiff:
vp, cmd := l.vp.Update(msg)
l.vp = vp.(*viewport.Viewport)
if cmd != nil {
@@ -221,23 +228,36 @@ func (l *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// View implements tea.Model.
func (l *Log) View() string {
switch l.activeView {
- case logView:
+ case logViewCommits:
return l.selector.View()
- case commitView:
+ case logViewDiff:
return l.vp.View()
default:
return ""
}
}
+// StatusBarValue returns the status bar value.
+func (l *Log) StatusBarValue() string {
+ c := l.activeCommit
+ if c == nil {
+ return ""
+ }
+ return fmt.Sprintf("%s by %s on %s",
+ c.ID.String()[:7],
+ c.Author.Name,
+ c.Author.When.Format("02 Jan 2006"),
+ )
+}
+
// StatusBarInfo returns the status bar info.
func (l *Log) StatusBarInfo() string {
switch l.activeView {
- case logView:
+ case logViewCommits:
// We're using l.nextPage instead of l.selector.Paginator.Page because
// of the paginator hack above.
return fmt.Sprintf("%d/%d", l.nextPage+1, l.selector.TotalPages())
- case commitView:
+ case logViewDiff:
return fmt.Sprintf("%.f%%", l.vp.ScrollPercent()*100)
default:
return ""
@@ -262,7 +282,7 @@ func (l *Log) updateCommitsCmd() tea.Msg {
count = int64(msg)
}
}
- items := make([]list.Item, count)
+ items := make([]selector.IdentifiableItem, count)
page := l.nextPage
limit := l.selector.PerPage()
skip := page * limit
@@ -45,6 +45,7 @@ type Repo struct {
statusbar *statusbar.StatusBar
readme *code.Code
log *Log
+ files *Files
ref *ggit.Reference
}
@@ -55,6 +56,7 @@ func New(common common.Common, rs git.GitRepoSource) *Repo {
readme := code.New(common, "", "")
readme.NoContentStyle = readme.NoContentStyle.SetString("No readme found.")
log := NewLog(common)
+ files := NewFiles(common)
r := &Repo{
common: common,
rs: rs,
@@ -62,6 +64,7 @@ func New(common common.Common, rs git.GitRepoSource) *Repo {
statusbar: sb,
readme: readme,
log: log,
+ files: files,
}
return r
}
@@ -79,6 +82,7 @@ func (r *Repo) SetSize(width, height int) {
r.statusbar.SetSize(width, height-hm)
r.readme.SetSize(width, height-hm)
r.log.SetSize(width, height-hm)
+ r.files.SetSize(width, height-hm)
}
// ShortHelp implements help.KeyMap.
@@ -127,25 +131,16 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
r.tabs.Init(),
r.updateReadmeCmd,
r.updateRefCmd,
+ r.updateModels(msg),
)
- // Pass msg to log.
- l, cmd := r.log.Update(msg)
- r.log = l.(*Log)
- if cmd != nil {
- cmds = append(cmds, cmd)
- }
case RefMsg:
r.ref = msg
cmds = append(cmds,
r.updateStatusBarCmd,
r.log.Init(),
+ r.files.Init(),
+ r.updateModels(msg),
)
- // Pass msg to log.
- l, cmd := r.log.Update(msg)
- r.log = l.(*Log)
- if cmd != nil {
- cmds = append(cmds, cmd)
- }
case tabs.ActiveTabMsg:
r.activeTab = tab(msg)
if r.selectedRepo != nil {
@@ -155,6 +150,12 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if r.selectedRepo != nil {
cmds = append(cmds, r.updateStatusBarCmd)
}
+ case FileItemsMsg:
+ f, cmd := r.files.Update(msg)
+ r.files = f.(*Files)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
case LogCountMsg, LogItemsMsg:
l, cmd := r.log.Update(msg)
r.log = l.(*Log)
@@ -169,11 +170,7 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if cmd != nil {
cmds = append(cmds, cmd)
}
- l, cmd := r.log.Update(msg)
- r.log = l.(*Log)
- if cmd != nil {
- cmds = append(cmds, cmd)
- }
+ cmds = append(cmds, r.updateModels(msg))
}
t, cmd := r.tabs.Update(msg)
r.tabs = t.(*tabs.Tabs)
@@ -193,6 +190,11 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, cmd)
}
case filesTab:
+ f, cmd := r.files.Update(msg)
+ r.files = f.(*Files)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
case commitsTab:
l, cmd := r.log.Update(msg)
r.log = l.(*Log)
@@ -219,18 +221,21 @@ func (r *Repo) View() string {
r.common.Styles.Tabs.GetVerticalFrameSize()
mainStyle := repoBodyStyle.
Height(r.common.Height - hm)
- main := mainStyle.Render("")
+ main := ""
switch r.activeTab {
case readmeTab:
- main = mainStyle.Render(r.readme.View())
+ main = r.readme.View()
case filesTab:
+ main = r.files.View()
case commitsTab:
- main = mainStyle.Render(r.log.View())
+ main = r.log.View()
+ case branchesTab:
+ case tagsTab:
}
view := lipgloss.JoinVertical(lipgloss.Top,
r.headerView(),
r.tabs.View(),
- main,
+ mainStyle.Render(main),
r.statusbar.View(),
)
return s.Render(view)
@@ -242,7 +247,11 @@ func (r *Repo) headerView() string {
}
name := r.common.Styles.RepoHeaderName.Render(r.selectedItem.Title())
// TODO move this into a style.
- url := lipgloss.NewStyle().MarginLeft(2).Render(r.selectedItem.URL())
+ url := lipgloss.NewStyle().
+ MarginLeft(1).
+ Width(r.common.Width - lipgloss.Width(name) - 1).
+ Align(lipgloss.Right).
+ Render(r.selectedItem.URL())
desc := r.common.Styles.RepoHeaderDesc.Render(r.selectedItem.Description())
style := r.common.Styles.RepoHeader.Copy().Width(r.common.Width)
return style.Render(
@@ -268,16 +277,21 @@ func (r *Repo) setRepoCmd(repo string) tea.Cmd {
}
func (r *Repo) updateStatusBarCmd() tea.Msg {
+ value := ""
info := ""
switch r.activeTab {
case readmeTab:
info = fmt.Sprintf("%.f%%", r.readme.ScrollPercent()*100)
case commitsTab:
+ value = r.log.StatusBarValue()
info = r.log.StatusBarInfo()
+ case filesTab:
+ value = r.files.StatusBarValue()
+ info = r.files.StatusBarInfo()
}
return statusbar.StatusBarMsg{
Key: r.selectedRepo.Name(),
- Value: "",
+ Value: value,
Info: info,
Branch: fmt.Sprintf(" %s", r.ref.Name().Short()),
}
@@ -299,6 +313,21 @@ func (r *Repo) updateRefCmd() tea.Msg {
return RefMsg(head)
}
+func (r *Repo) updateModels(msg tea.Msg) tea.Cmd {
+ cmds := make([]tea.Cmd, 0)
+ l, cmd := r.log.Update(msg)
+ r.log = l.(*Log)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ f, cmd := r.files.Update(msg)
+ r.files = f.(*Files)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ return tea.Batch(cmds...)
+}
+
func updateStatusBarCmd() tea.Msg {
return UpdateStatusBarMsg{}
}
@@ -5,7 +5,6 @@ import (
"strings"
"github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
appCfg "github.com/charmbracelet/soft-serve/config"
@@ -130,7 +129,7 @@ func (s *Selection) Init() tea.Cmd {
pty, _, _ := session.Pty()
environ = append(environ, fmt.Sprintf("TERM=%s", pty.Term))
}
- items := make([]list.Item, 0)
+ items := make([]selector.IdentifiableItem, 0)
cfg := s.s.Config()
// TODO clean up this and move style to its own var.
yank := func(text string) *yankable.Yankable {
@@ -120,6 +120,7 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds := make([]tea.Cmd, 0)
switch msg := msg.(type) {
case tea.WindowSizeMsg:
+ ui.SetSize(msg.Width, msg.Height)
h, cmd := ui.header.Update(msg)
ui.header = h.(*header.Header)
if cmd != nil {
@@ -137,7 +138,6 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, cmd)
}
}
- ui.SetSize(msg.Width, msg.Height)
case tea.KeyMsg:
switch {
case key.Matches(msg, ui.common.KeyMap.Back) && ui.error != nil:
@@ -161,10 +161,12 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
}
- m, cmd := ui.pages[ui.activePage].Update(msg)
- ui.pages[ui.activePage] = m.(common.Page)
- if cmd != nil {
- cmds = append(cmds, cmd)
+ if ui.state == loadedState {
+ m, cmd := ui.pages[ui.activePage].Update(msg)
+ ui.pages[ui.activePage] = m.(common.Page)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
}
return ui, tea.Batch(cmds...)
}