Detailed changes
@@ -86,3 +86,15 @@ func (s *StatusBar) View() string {
),
)
}
+
+// StatusBarCmd returns a command that sets the status bar information.
+func StatusBarCmd(key, value, info, branch string) tea.Cmd {
+ return func() tea.Msg {
+ return StatusBarMsg{
+ Key: key,
+ Value: value,
+ Info: info,
+ Branch: branch,
+ }
+ }
+}
@@ -0,0 +1,45 @@
+package repo
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/charmbracelet/soft-serve/server/config"
+)
+
+func defaultEmptyRepoMsg(cfg *config.Config, repo string) string {
+ host := cfg.Host
+ if cfg.SSH.Port != 22 {
+ host = fmt.Sprintf("%s:%d", host, cfg.SSH.Port)
+ }
+ repo = strings.TrimSuffix(repo, ".git")
+ return fmt.Sprintf(`# Quick Start
+
+Get started by cloning this repository, add your files, commit, and push.
+
+## Clone this repository.
+
+`+"```"+`sh
+git clone ssh://%[1]s/%[2]s.git
+`+"```"+`
+
+## Creating a new repository on the command line
+
+`+"```"+`sh
+touch README.md
+git init
+git add README.md
+git branch -M main
+git commit -m "first commit"
+git remote add origin ssh://%[1]s/%[2]s.git
+git push -u origin main
+`+"```"+`
+
+## Pushing an existing repository from the command line
+
+`+"```"+`sh
+git remote add origin ssh://%[1]s/%[2]s.git
+git push -u origin main
+`+"```"+`
+`, host, repo)
+}
@@ -2,6 +2,7 @@ package repo
import (
"fmt"
+ "path/filepath"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
@@ -17,10 +18,11 @@ type ReadmeMsg struct {
// Readme is the readme component page.
type Readme struct {
- common common.Common
- code *code.Code
- ref RefMsg
- repo *git.Repository
+ common common.Common
+ code *code.Code
+ ref RefMsg
+ repo *git.Repository
+ readmePath string
}
// NewReadme creates a new readme model.
@@ -79,6 +81,9 @@ func (r *Readme) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case RefMsg:
r.ref = msg
cmds = append(cmds, r.Init())
+ case EmptyRepoMsg:
+ r.code.SetContent(defaultEmptyRepoMsg(r.common.Config(),
+ r.repo.Info.Name()), ".md")
}
c, cmd := r.code.Update(msg)
r.code = c.(*code.Code)
@@ -95,7 +100,11 @@ func (r *Readme) View() string {
// StatusBarValue implements statusbar.StatusBar.
func (r *Readme) StatusBarValue() string {
- return ""
+ dir := filepath.Dir(r.readmePath)
+ if dir == "." {
+ return ""
+ }
+ return dir
}
// StatusBarInfo implements statusbar.StatusBar.
@@ -109,6 +118,7 @@ func (r *Readme) updateReadmeCmd() tea.Msg {
return common.ErrorCmd(git.ErrMissingRepo)
}
rm, rp := r.repo.Readme()
+ r.readmePath = rp
r.code.GotoTop()
cmd := r.code.SetContent(rm, rp)
if cmd != nil {
@@ -204,6 +204,9 @@ func UpdateRefCmd(repo *git.Repository) tea.Cmd {
return func() tea.Msg {
ref, err := repo.Repo.Repository().HEAD()
if err != nil {
+ if bs, err := repo.Repo.Repository().Branches(); err != nil && len(bs) == 0 {
+ return EmptyRepoMsg{}
+ }
log.Printf("ui: error getting HEAD reference: %v", err)
return common.ErrorMsg(err)
}
@@ -21,7 +21,7 @@ type state int
const (
loadingState state = iota
- loadedState
+ readyState
)
type tab int
@@ -45,6 +45,9 @@ func (t tab) String() string {
}[t]
}
+// EmptyRepoMsg is a message to indicate that the repository is empty.
+type EmptyRepoMsg struct{}
+
// CopyURLMsg is a message to copy the URL of the current repository.
type CopyURLMsg struct{}
@@ -257,6 +260,11 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, r.updateStatusBarCmd)
case tea.WindowSizeMsg:
cmds = append(cmds, r.updateModels(msg))
+ case EmptyRepoMsg:
+ r.state = readyState
+ cmds = append(cmds, r.updateStatusBarCmd)
+ case common.ErrorMsg:
+ r.state = readyState
}
s, cmd := r.statusbar.Update(msg)
r.statusbar = s.(*statusbar.StatusBar)
@@ -290,7 +298,7 @@ func (r *Repo) View() string {
switch r.state {
case loadingState:
main = fmt.Sprintf("%s loadingβ¦", r.spinner.View())
- case loadedState:
+ case readyState:
main = r.panes[r.activeTab].View()
statusbar = r.statusbar.View()
}
@@ -353,15 +361,15 @@ func (r *Repo) updateStatusBarCmd() tea.Msg {
}
value := r.panes[r.activeTab].(statusbar.Model).StatusBarValue()
info := r.panes[r.activeTab].(statusbar.Model).StatusBarInfo()
- ref := ""
+ branch := "*"
if r.ref != nil {
- ref = r.ref.Name().Short()
+ branch += " " + r.ref.Name().Short()
}
return statusbar.StatusBarMsg{
Key: r.selectedRepo.Info.Name(),
Value: value,
Info: info,
- Branch: fmt.Sprintf("* %s", ref),
+ Branch: branch,
}
}
@@ -418,7 +426,7 @@ func (r *Repo) updateRepo(msg tea.Msg) tea.Cmd {
r.panesReady[readmeTab] = true
}
if r.isReady() {
- r.state = loadedState
+ r.state = readyState
}
return tea.Batch(cmds...)
}
@@ -119,7 +119,6 @@ type Styles struct {
Selector lipgloss.Style
FileContent lipgloss.Style
Paginator lipgloss.Style
- NoItems lipgloss.Style
}
Spinner lipgloss.Style
@@ -405,8 +404,6 @@ func DefaultStyles() *Styles {
s.Tree.Paginator = s.Log.Paginator.Copy()
- s.Tree.NoItems = s.AboutNoReadme.Copy()
-
s.Spinner = lipgloss.NewStyle().
MarginTop(1).
MarginLeft(2).
@@ -420,9 +417,7 @@ func DefaultStyles() *Styles {
MarginLeft(2).
Foreground(lipgloss.Color("242"))
- s.NoItems = lipgloss.NewStyle().
- MarginLeft(2).
- Foreground(lipgloss.Color("242"))
+ s.NoItems = s.AboutNoReadme.Copy()
s.StatusBar = lipgloss.NewStyle().
Height(1)
@@ -27,9 +27,9 @@ const (
type sessionState int
const (
- startState sessionState = iota
+ loadingState sessionState = iota
errorState
- loadedState
+ readyState
)
// UI is the main UI model.
@@ -58,7 +58,7 @@ func New(c common.Common, initialRepo string) *UI {
common: c,
pages: make([]common.Component, 2), // selection & repo
activePage: selectionPage,
- state: startState,
+ state: loadingState,
header: h,
initialRepo: initialRepo,
showFooter: true,
@@ -92,7 +92,7 @@ func (ui *UI) ShortHelp() []key.Binding {
switch ui.state {
case errorState:
b = append(b, ui.common.KeyMap.Back)
- case loadedState:
+ case readyState:
b = append(b, ui.pages[ui.activePage].ShortHelp()...)
}
if !ui.IsFiltering() {
@@ -108,7 +108,7 @@ func (ui *UI) FullHelp() [][]key.Binding {
switch ui.state {
case errorState:
b = append(b, []key.Binding{ui.common.KeyMap.Back})
- case loadedState:
+ case readyState:
b = append(b, ui.pages[ui.activePage].FullHelp()...)
}
h := []key.Binding{
@@ -147,7 +147,7 @@ func (ui *UI) Init() tea.Cmd {
if ui.initialRepo != "" {
cmds = append(cmds, ui.initialRepoCmd(ui.initialRepo))
}
- ui.state = loadedState
+ ui.state = readyState
ui.SetSize(ui.common.Width, ui.common.Height)
return tea.Batch(cmds...)
}
@@ -182,7 +182,7 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch {
case key.Matches(msg, ui.common.KeyMap.Back) && ui.error != nil:
ui.error = nil
- ui.state = loadedState
+ ui.state = readyState
// Always show the footer on error.
ui.showFooter = ui.footer.ShowAll()
case key.Matches(msg, ui.common.KeyMap.Help):
@@ -223,7 +223,6 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
ui.error = msg
ui.state = errorState
ui.showFooter = true
- return ui, nil
case selector.SelectMsg:
switch msg.IdentifiableItem.(type) {
case selection.Item:
@@ -242,7 +241,7 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if cmd != nil {
cmds = append(cmds, cmd)
}
- if ui.state == loadedState {
+ if ui.state != loadingState {
m, cmd := ui.pages[ui.activePage].Update(msg)
ui.pages[ui.activePage] = m.(common.Component)
if cmd != nil {
@@ -259,7 +258,7 @@ func (ui *UI) View() string {
var view string
wm, hm := ui.getMargins()
switch ui.state {
- case startState:
+ case loadingState:
view = "Loading..."
case errorState:
err := ui.common.Styles.ErrorTitle.Render("Bummer")
@@ -272,7 +271,7 @@ func (ui *UI) View() string {
hm -
ui.common.Styles.Error.GetVerticalFrameSize()).
Render(err)
- case loadedState:
+ case readyState:
view = ui.pages[ui.activePage].View()
default:
view = "Unknown state :/ this is a bug!"