feat: share popups draw repos and files like everywhere else
CI / Checks (pull_request) Successful in 38s
PR title / Conventional title (pull_request) Successful in 2s

repoLabel draws a repo, or a file in it, with pathLabel: the repo icon
and colour. The share popups use it for what's shared, where it's
from and where it's saved, and are titled for what's shared
("Share .env", "Share 2 env files"), the key picker too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kebandClaude Opus 5.5 committed 2026-10-13 12:22:23 +02:00
1 parent 0bfa2a3141
commit cd265a632e
3 files changed
+45 -22

No files matched your search

+6
View File
@@ -138,6 +138,12 @@ func (m Model) pathLabel(kind provider.Kind, path []string, account string) stri
return label
}
// repoLabel draws a repo, or a file in it, as everywhere else: the repo
// icon and colour, "app / apps/api/.env".
func (m Model) repoLabel(repo string, file ...string) string {
return m.pathLabel(provider.KindRepo, append([]string{repo}, file...), "")
}
// sourceMatches reports whether src is the subscription or workspace r
// links to.
func sourceMatches(r ref.Ref, src provider.Source) bool {
+38 -21
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
@@ -109,14 +110,27 @@ func (m *Model) handleShareFiles(msg shareFilesMsg) tea.Cmd {
return nil
}
// shareTitle names the action: "Share .env", "Share 2 env files".
func shareTitle(s sharing) string {
if len(s.files) == 1 {
return "Share " + path.Base(s.files[0].Path)
}
return "Share " + plural(len(s.files), "env file")
}
// sharedFiles lists the files being shared, under what's happening.
func (m *Model) sharedFiles(s sharing) []string {
lines := []string{"Sharing " + plural(len(s.files), "env file") + " as an encrypted link:"}
for _, f := range s.files {
lines = append(lines, " "+m.repoLabel(s.repo.Name, f.Path))
}
return lines
}
// confirmShare says what the link will hold before making it.
func (m *Model) confirmShare(s sharing) {
lines := []string{m.st.Muted.Render(plural(len(s.files), "env file") + " from " + s.repo.Name + ":")}
for _, f := range s.files {
lines = append(lines, " "+f.Path)
}
lines = append(lines, "", m.st.Muted.Render("encrypted with ")+m.refLabel(s.key))
m.showMenu("Share as an encrypted link", lines, []option{
lines := append(m.sharedFiles(s), "", m.st.Muted.Render("encrypted with ")+m.refLabel(s.key))
m.showMenu(shareTitle(s), lines, []option{
{key: "c", label: "Copy the link", run: func(m *Model) tea.Cmd { return m.seal(s) }},
{key: "k", label: "Encrypt with another secret…", run: func(m *Model) tea.Cmd { return m.pickShareKey(s) }},
})
@@ -130,16 +144,10 @@ func (m *Model) pickShareKey(s sharing) tea.Cmd {
m.confirmShare(s)
return nil
}
m.search.pickTitle = "Encrypt with which secret?"
what := s.files[0].Path
if len(s.files) > 1 {
what = plural(len(s.files), "env file")
}
m.search.pickLines = []string{
"Sharing " + what + " from " + s.repo.Name + " as an encrypted link.",
"Pick the secret to encrypt with: whoever can read it can open the link.",
m.st.Muted.Render("Its value isn't shown or put in the link. share.key sets a default."),
}
m.search.pickTitle = shareTitle(s)
m.search.pickLines = append(m.sharedFiles(s), "",
"Pick the secret to encrypt them with: whoever can read it can open the link.",
m.st.Muted.Render("Its value isn't shown or put in the link. share.key sets a default."))
m.fillSearch(false)
return tea.Batch(cmd, m.searchLoads())
}
@@ -213,10 +221,10 @@ func (m *Model) offerShare(r ref.Ref) tea.Cmd {
}
from := ""
if s.Repo != "" {
from = " from " + s.Repo
from = m.st.Muted.Render(" from ") + m.repoLabel(s.Repo)
}
m.showMenu("Shared env files", []string{
m.st.Muted.Render(plural(s.Files, "env file") + from),
m.st.Muted.Render(plural(s.Files, "env file")) + from,
m.st.Muted.Render("encrypted with ") + m.refLabel(s.Key),
}, []option{
{key: "o", label: "Decrypt them…", tip: "reads the secret, then asks before saving", run: func(m *Model) tea.Cmd { return m.openShare(s) }},
@@ -302,10 +310,11 @@ func (m *Model) offerWrite(files []share.File, dir string) {
var lines []string
exist := 0
for _, t := range targets {
line := " " + t.Path + m.st.Muted.Render(" · new")
file := " " + m.repoLabel(filepath.Base(dir), t.Path)
line := file + m.st.Muted.Render(" · new")
if t.Exists {
exist++
line = " " + t.Path + m.st.Muted.Render(" · replaces the one there")
line = file + m.st.Muted.Render(" · replaces the one there")
}
if t.Exposed {
line += m.st.Error.Render(" · not ignored by git")
@@ -339,7 +348,7 @@ func (m *Model) offerWrite(files []share.File, dir string) {
default:
opts = append(opts,
option{key: "w", label: fmt.Sprintf("Save all, replacing %d", exist), run: write(false)},
option{key: "n", label: "Save only the new " + plural(len(targets)-exist, "file"), run: write(true)},
option{key: "n", label: onlyNew(len(targets) - exist), run: write(true)},
)
}
m.showMenu("Save the shared files", lines, opts)
@@ -354,3 +363,11 @@ func expandHome(dir string) string {
}
return dir
}
// onlyNew labels saving only the n files not there yet.
func onlyNew(n int) string {
if n == 1 {
return "Save only the new one"
}
return fmt.Sprintf("Save only the %d new ones", n)
}
+1 -1
View File
@@ -74,7 +74,7 @@ func shareFrom(t *testing.T, root string, paste bool) string {
t.Errorf("not a secret: %s", o.label)
}
}
if !strings.Contains(d.screen(), "Encrypt with which secret?") {
if !strings.Contains(d.screen(), "Share 2 env files") && !strings.Contains(d.screen(), "Share .env") {
t.Errorf("not titled as a picker:\n%s", d.screen())
}
d.keys("enter")