Detailed changes
@@ -16,8 +16,8 @@ func TruncateString(s string, max int) string {
return truncate.StringWithTail(s, uint(max), "…")
}
-// RepoURL returns the URL of the repository.
-func RepoURL(publicURL, name string) string {
+// CloneCmd returns the URL of the repository.
+func CloneCmd(publicURL, name string) string {
name = utils.SanitizeRepo(name) + ".git"
url, err := url.Parse(publicURL)
if err == nil {
@@ -9,16 +9,19 @@ import (
// StatusBarMsg is a message sent to the status bar.
type StatusBarMsg struct {
- Key string
- Value string
- Info string
- Branch string
+ Key string
+ Value string
+ Info string
+ Extra string
}
// StatusBar is a status bar model.
type StatusBar struct {
common common.Common
- msg StatusBarMsg
+ key string
+ value string
+ info string
+ extra string
}
// Model is an interface that supports setting the status bar information.
@@ -50,7 +53,18 @@ func (s *StatusBar) Init() tea.Cmd {
func (s *StatusBar) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case StatusBarMsg:
- s.msg = msg
+ if msg.Key != "" {
+ s.key = msg.Key
+ }
+ if msg.Value != "" {
+ s.value = msg.Value
+ }
+ if msg.Info != "" {
+ s.info = msg.Info
+ }
+ if msg.Extra != "" {
+ s.extra = msg.Extra
+ }
}
return s, nil
}
@@ -63,14 +77,14 @@ func (s *StatusBar) View() string {
"repo-help",
st.StatusBarHelp.Render("? Help"),
)
- key := st.StatusBarKey.Render(s.msg.Key)
+ key := st.StatusBarKey.Render(s.key)
info := ""
- if s.msg.Info != "" {
- info = st.StatusBarInfo.Render(s.msg.Info)
+ if s.info != "" {
+ info = st.StatusBarInfo.Render(s.info)
}
- branch := st.StatusBarBranch.Render(s.msg.Branch)
+ branch := st.StatusBarBranch.Render(s.extra)
maxWidth := s.common.Width - w(key) - w(info) - w(branch) - w(help)
- v := truncate.StringWithTail(s.msg.Value, uint(maxWidth-st.StatusBarValue.GetHorizontalFrameSize()), "…")
+ v := truncate.StringWithTail(s.value, uint(maxWidth-st.StatusBarValue.GetHorizontalFrameSize()), "…")
value := st.StatusBarValue.
Width(maxWidth).
Render(v)
@@ -36,5 +36,5 @@ git push -u origin main
git remote add origin %[1]s
git push -u origin main
`+"```"+`
-`, common.RepoURL(cfg.SSH.PublicURL, repo))
+`, common.CloneCmd(cfg.SSH.PublicURL, repo))
}
@@ -243,7 +243,7 @@ func (f *Files) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case key.Matches(msg, f.common.KeyMap.BackItem):
cmds = append(cmds, backCmd)
case key.Matches(msg, f.common.KeyMap.Copy):
- f.common.Copy.Copy(f.currentContent.content)
+ cmds = append(cmds, copyCmd(f.currentContent.content, "File contents copied to clipboard"))
case key.Matches(msg, lineNo):
f.lineNumber = !f.lineNumber
f.code.SetShowLineNumber(f.lineNumber)
@@ -78,7 +78,6 @@ func (d FileItemDelegate) Spacing() int { return 0 }
// Update implements list.ItemDelegate.
func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
- idx := m.Index()
item, ok := m.SelectedItem().(FileItem)
if !ok {
return nil
@@ -87,8 +86,7 @@ func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
- d.common.Copy.Copy(item.Title())
- return m.SetItem(idx, item)
+ return copyCmd(item.entry.Name(), fmt.Sprintf("File name %q copied to clipboard", item.entry.Name()))
}
}
return nil
@@ -18,7 +18,6 @@ import (
// LogItem is a item in the log list that displays a git commit.
type LogItem struct {
*git.Commit
- copied time.Time
}
// ID implements selector.IdentifiableItem.
@@ -57,7 +56,6 @@ func (d LogItemDelegate) Spacing() int { return 1 }
// Update updates the item. Implements list.ItemDelegate.
func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
- idx := m.Index()
item, ok := m.SelectedItem().(LogItem)
if !ok {
return nil
@@ -66,9 +64,7 @@ func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
- item.copied = time.Now()
- d.common.Copy.Copy(item.Hash())
- return m.SetItem(idx, item)
+ return copyCmd(item.Hash(), fmt.Sprintf("Commit hash %q copied to clipboard", item.Hash()))
}
}
return nil
@@ -92,9 +88,6 @@ func (d LogItemDelegate) Render(w io.Writer, m list.Model, index int, listItem l
horizontalFrameSize := styles.Base.GetHorizontalFrameSize()
hash := i.Commit.ID.String()[:7]
- if !i.copied.IsZero() && i.copied.Add(time.Second).After(time.Now()) {
- hash = "copied"
- }
title := styles.Title.Render(
common.TruncateString(i.Title(),
m.Width()-
@@ -67,7 +67,6 @@ func (d RefItemDelegate) Spacing() int { return 0 }
// Update implements list.ItemDelegate.
func (d RefItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
- idx := m.Index()
item, ok := m.SelectedItem().(RefItem)
if !ok {
return nil
@@ -76,8 +75,7 @@ func (d RefItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
- d.common.Copy.Copy(item.Title())
- return m.SetItem(idx, item)
+ return copyCmd(item.ID(), fmt.Sprintf("Reference %q copied to clipboard", item.ID()))
}
}
return nil
@@ -2,7 +2,6 @@ package repo
import (
"fmt"
- "time"
"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
@@ -56,9 +55,6 @@ type EmptyRepoMsg struct{}
// CopyURLMsg is a message to copy the URL of the current repository.
type CopyURLMsg struct{}
-// ResetURLMsg is a message to reset the URL string.
-type ResetURLMsg struct{}
-
// UpdateStatusBarMsg updates the status bar.
type UpdateStatusBarMsg struct{}
@@ -68,6 +64,12 @@ type RepoMsg backend.Repository
// BackMsg is a message to go back to the previous view.
type BackMsg struct{}
+// CopyMsg is a message to indicate copied text.
+type CopyMsg struct {
+ Text string
+ Message string
+}
+
// Repo is a view for a git repository.
type Repo struct {
common common.Common
@@ -77,7 +79,6 @@ type Repo struct {
statusbar *statusbar.StatusBar
panes []common.Component
ref *git.Reference
- copyURL time.Time
state state
spinner spinner.Model
panesReady [lastTab]bool
@@ -215,8 +216,9 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if r.selectedRepo != nil {
cmds = append(cmds, r.updateStatusBarCmd)
urlID := fmt.Sprintf("%s-url", r.selectedRepo.Name())
+ cmd := common.CloneCmd(r.common.Config().SSH.PublicURL, r.selectedRepo.Name())
if msg, ok := msg.(tea.MouseMsg); ok && r.common.Zone.Get(urlID).InBounds(msg) {
- cmds = append(cmds, r.copyURLCmd())
+ cmds = append(cmds, copyCmd(cmd, "Command copied to clipboard"))
}
}
switch msg := msg.(type) {
@@ -234,14 +236,16 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
}
- case CopyURLMsg:
+ case CopyMsg:
+ txt := msg.Text
if cfg := r.common.Config(); cfg != nil {
- r.common.Copy.Copy(
- common.RepoURL(cfg.SSH.PublicURL, r.selectedRepo.Name()),
- )
+ r.common.Copy.Copy(txt)
}
- case ResetURLMsg:
- r.copyURL = time.Time{}
+ cmds = append(cmds, func() tea.Msg {
+ return statusbar.StatusBarMsg{
+ Value: msg.Message,
+ }
+ })
case ReadmeMsg, FileItemsMsg, LogCountMsg, LogItemsMsg, RefItemsMsg:
cmds = append(cmds, r.updateRepo(msg))
// We have two spinners, one is used to when loading the repository and the
@@ -345,10 +349,7 @@ func (r *Repo) headerView() string {
Align(lipgloss.Right)
var url string
if cfg := r.common.Config(); cfg != nil {
- url = common.RepoURL(cfg.SSH.PublicURL, r.selectedRepo.Name())
- }
- if !r.copyURL.IsZero() && r.copyURL.Add(time.Second).After(time.Now()) {
- url = "copied!"
+ url = common.CloneCmd(cfg.SSH.PublicURL, r.selectedRepo.Name())
}
url = common.TruncateString(url, r.common.Width-lipgloss.Width(desc)-1)
url = r.common.Zone.Mark(
@@ -378,10 +379,10 @@ func (r *Repo) updateStatusBarCmd() tea.Msg {
branch += " " + r.ref.Name().Short()
}
return statusbar.StatusBarMsg{
- Key: r.selectedRepo.Name(),
- Value: value,
- Info: info,
- Branch: branch,
+ Key: r.selectedRepo.Name(),
+ Value: value,
+ Info: info,
+ Extra: branch,
}
}
@@ -458,16 +459,13 @@ func (r *Repo) isReady() bool {
return ready
}
-func (r *Repo) copyURLCmd() tea.Cmd {
- r.copyURL = time.Now()
- return tea.Batch(
- func() tea.Msg {
- return CopyURLMsg{}
- },
- tea.Tick(time.Second, func(time.Time) tea.Msg {
- return ResetURLMsg{}
- }),
- )
+func copyCmd(text, msg string) tea.Cmd {
+ return func() tea.Msg {
+ return CopyMsg{
+ Text: text,
+ Message: msg,
+ }
+ }
}
func updateStatusBarCmd() tea.Msg {
@@ -51,7 +51,6 @@ type Item struct {
repo backend.Repository
lastUpdate *time.Time
cmd string
- copied time.Time
}
// New creates a new Item.
@@ -64,7 +63,7 @@ func NewItem(repo backend.Repository, cfg *config.Config) (Item, error) {
return Item{
repo: repo,
lastUpdate: lastUpdate,
- cmd: common.RepoURL(cfg.SSH.PublicURL, repo.Name()),
+ cmd: common.CloneCmd(cfg.SSH.PublicURL, repo.Name()),
}, nil
}
@@ -98,6 +97,16 @@ func (i Item) Command() string {
type ItemDelegate struct {
common *common.Common
activePane *pane
+ copiedIdx int
+}
+
+// NewItemDelegate creates a new ItemDelegate.
+func NewItemDelegate(common *common.Common, activePane *pane) *ItemDelegate {
+ return &ItemDelegate{
+ common: common,
+ activePane: activePane,
+ copiedIdx: -1,
+ }
}
// Width returns the item width.
@@ -107,16 +116,16 @@ func (d ItemDelegate) Width() int {
}
// Height returns the item height. Implements list.ItemDelegate.
-func (d ItemDelegate) Height() int {
+func (d *ItemDelegate) Height() int {
height := d.common.Styles.MenuItem.GetVerticalFrameSize() + d.common.Styles.MenuItem.GetHeight()
return height
}
// Spacing returns the spacing between items. Implements list.ItemDelegate.
-func (d ItemDelegate) Spacing() int { return 1 }
+func (d *ItemDelegate) Spacing() int { return 1 }
// Update implements list.ItemDelegate.
-func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
+func (d *ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
idx := m.Index()
item, ok := m.SelectedItem().(Item)
if !ok {
@@ -126,7 +135,7 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
- item.copied = time.Now()
+ d.copiedIdx = idx
d.common.Copy.Copy(item.Command())
return m.SetItem(idx, item)
}
@@ -135,7 +144,7 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
}
// Render implements list.ItemDelegate.
-func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
+func (d *ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
i := listItem.(Item)
s := strings.Builder{}
var matchedRunes []int
@@ -192,8 +201,9 @@ func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list
s.WriteRune('\n')
cmd := common.TruncateString(i.Command(), m.Width()-styles.Base.GetHorizontalFrameSize())
cmd = styles.Command.Render(cmd)
- if !i.copied.IsZero() && i.copied.Add(time.Second).After(time.Now()) {
- cmd = styles.Command.Render("Copied!")
+ if d.copiedIdx == index {
+ cmd += " " + styles.Desc.Render("(copied to clipboard)")
+ d.copiedIdx = -1
}
s.WriteString(cmd)
fmt.Fprint(w,
@@ -71,7 +71,7 @@ func New(c common.Common) *Selection {
SetString(defaultNoContent)
selector := selector.New(c,
[]selector.IdentifiableItem{},
- ItemDelegate{&c, &sel.activePane})
+ NewItemDelegate(&c, &sel.activePane))
selector.SetShowTitle(false)
selector.SetShowHelp(false)
selector.SetShowStatusBar(false)