[mike@mwxm4]

This commit is contained in:
2026-07-28 15:25:54 +02:00
parent 1e3ae9a4c7
commit 113527f220
14 changed files with 60 additions and 57 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ The commands available directly from the shell are `clone`, `init`, `log`,
`pull`, `fetch`, `status`, `diff`, `overview`, `config`, `count`, `login` and `pull`, `fetch`, `status`, `diff`, `overview`, `config`, `count`, `login` and
`cloneall`; every other command is interactive-only. `cloneall`; every other command is interactive-only.
The interactive prompt is colored (Catppuccin Mocha) and shows the active The interactive prompt is colored (Catppuccin-flavored) and shows the active
project, its git branch and a `*` dirty marker: project, its git branch and a `*` dirty marker:
``` ```
+2 -2
View File
@@ -241,7 +241,7 @@ func handleUnalias(name string) {
func listAliases() { func listAliases() {
if len(aliases) == 0 { if len(aliases) == 0 {
fmt.Println(col(cGray, "no aliases defined")) fmt.Println(col(cDark, "no aliases defined"))
return return
} }
for _, n := range aliasNames() { for _, n := range aliasNames() {
@@ -251,7 +251,7 @@ func listAliases() {
// formatAlias renders `name = 'body'` with color. // formatAlias renders `name = 'body'` with color.
func formatAlias(name, body string) string { func formatAlias(name, body string) string {
return col(cGreen, name) + col(cGray, " = ") + col(cYellow, "'"+body+"'") return col(cGreen, name) + col(cDark, " = ") + col(cYellow, "'"+body+"'")
} }
// expandAlias substitutes positional parameters in an alias body. With no // expandAlias substitutes positional parameters in an alias body. With no
+2 -2
View File
@@ -114,8 +114,8 @@ func (r *remoteAPI) doUpload(method, endpoint, ctype string, body io.Reader, siz
func (r *remoteAPI) uploadAssets(owner, repo string, ref releaseRef, assets []releaseAsset) error { func (r *remoteAPI) uploadAssets(owner, repo string, ref releaseRef, assets []releaseAsset) error {
failed := 0 failed := 0
for _, a := range assets { for _, a := range assets {
fmt.Printf(" %s %s %s\n", col(cGray, "uploading"), fmt.Printf(" %s %s %s\n", col(cDark, "uploading"),
col(cGreen, padRight(a.name, 28)), col(cGray, humanSize(a.size))) col(cGreen, padRight(a.name, 28)), col(cDark, humanSize(a.size)))
if err := r.uploadAsset(owner, repo, ref, a); err != nil { if err := r.uploadAsset(owner, repo, ref, a); err != nil {
errorln(" " + a.name + ": " + err.Error()) errorln(" " + a.name + ": " + err.Error())
failed++ failed++
+14 -11
View File
@@ -7,21 +7,24 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// Colors for the prompt, banner and output, using the Catppuccin Mocha palette // Colors for the prompt, banner and output, as 24-bit truecolor escapes: the
// as 24-bit truecolor escapes // Catppuccin Mocha accents with a warm white and three greys of its own.
// (https://terminalcolors.com/themes/catppuccin/mocha/).
const ( const (
cReset = "\033[0m" cReset = "\033[0m"
cBold = "\033[1m" cBold = "\033[1m"
cDim = "\033[2m" cDim = "\033[2m"
cGreen = "\033[38;2;166;227;161m" // Green #a6e3a1 cYellow = "\033[38;2;249;226;175m" // #f9e2af
cYellow = "\033[38;2;249;226;175m" // Yellow #f9e2af cOrange = "\033[38;2;250;179;135m" // #fab387
cRed = "\033[38;2;243;139;168m" // Red #f38ba8 cRed = "\033[38;2;243;139;168m" // #f38ba8
cCyan = "\033[38;2;148;226;213m" // Teal #94e2d5 cGreen = "\033[38;2;148;226;213m" // #94e2d5
cPurple = "\033[38;2;203;166;247m" // Mauve #cba6f7 cBlue = "\033[38;2;180;190;254m" // #b4befe
cWhite = "\033[38;2;205;214;244m" // Text #cdd6f4 cPink = "\033[38;2;245;178;247m" // #f5b2f7
cGray = "\033[38;2;108;112;134m" // Overlay0 #6c7086 cViolet = "\033[38;2;203;166;247m" // #cba6f7
cWhite = "\033[38;2;240;240;234m" // #f0f0ea
cGrey = "\033[38;2;170;170;187m" // #aaaabb
cDark = "\033[38;2;119;119;136m" // #777788
cDarker = "\033[38;2;68;68;85m" // #444455
) )
// col wraps s in color c, but only when color output is enabled. // col wraps s in color c, but only when color output is enabled.
@@ -66,7 +69,7 @@ func formatRepoList(entries []lsEntry, withSize bool) string {
for _, e := range entries { for _, e := range entries {
fmt.Fprintf(&b, " %s %s", col(cGreen, padRight(e.name, width)), col(cYellow, e.date)) fmt.Fprintf(&b, " %s %s", col(cGreen, padRight(e.name, width)), col(cYellow, e.date))
if withSize { if withSize {
fmt.Fprintf(&b, " %s", col(cGray, fmt.Sprintf("%7s", humanSize(e.size)))) fmt.Fprintf(&b, " %s", col(cDark, fmt.Sprintf("%7s", humanSize(e.size))))
} }
b.WriteByte('\n') b.WriteByte('\n')
} }
+6 -6
View File
@@ -282,7 +282,7 @@ func runCommandDepth(line string, depth int) bool {
if pat != "" { if pat != "" {
what = "no " + many + " matching '" + word(words, 1) + "'" what = "no " + many + " matching '" + word(words, 1) + "'"
} }
fmt.Println(col(cGray, what)) fmt.Println(col(cDark, what))
break break
} }
// no size column when the server gave no usable sizes, rather than a // no size column when the server gave no usable sizes, rather than a
@@ -296,7 +296,7 @@ func runCommandDepth(line string, depth int) bool {
if total > 0 { if total > 0 {
summary += " · " + humanSize(total) summary += " · " + humanSize(total)
} }
fmt.Println(col(cGray, summary)) fmt.Println(col(cDark, summary))
case "show": // show a repository's log directly on the server case "show": // show a repository's log directly on the server
prj := PRJ prj := PRJ
@@ -610,7 +610,7 @@ func runCommandDepth(line string, depth int) bool {
default: // unknown command — no longer forwarded to the shell default: // unknown command — no longer forwarded to the shell
fmt.Println(col(cRed, "unknown command: "+words[0]) + fmt.Println(col(cRed, "unknown command: "+words[0]) +
col(cGray, " (prefix with '!' to run a shell command)")) col(cDark, " (prefix with '!' to run a shell command)"))
} }
return true return true
@@ -671,9 +671,9 @@ func formatLog(lines []string, now time.Time) string {
n++ n++
} }
fmt.Fprintf(&full, "%s %s %s\n", col(cPurple, hash), col(cYellow, z), subj) fmt.Fprintf(&full, "%s %s %s\n", col(cViolet, hash), col(cYellow, z), subj)
fmt.Fprintf(&short, "%s %s %s\n", col(cPurple, hash), col(cYellow, zs), subj) fmt.Fprintf(&short, "%s %s %s\n", col(cViolet, hash), col(cYellow, zs), subj)
fmt.Fprintf(&tiny, "%s %s %s\n", col(cPurple, hash), col(cYellow, zss), subj) fmt.Fprintf(&tiny, "%s %s %s\n", col(cViolet, hash), col(cYellow, zss), subj)
} }
switch { switch {
+2 -2
View File
@@ -188,9 +188,9 @@ func migrateRemoteKeys(path, data string) {
errorln("could not update " + path + ": " + err.Error()) errorln("could not update " + path + ": " + err.Error())
return return
} }
fmt.Println(col(cGray, path+": mirror settings renamed to the remote.<name>.* form")) fmt.Println(col(cDark, path+": mirror settings renamed to the remote.<name>.* form"))
for _, r := range renamed { for _, r := range renamed {
fmt.Println(col(cGray, " "+r)) fmt.Println(col(cDark, " "+r))
} }
} }
+1 -1
View File
@@ -109,7 +109,7 @@ func TestFormatRepoList(t *testing.T) {
colored := formatRepoList(entries, false) colored := formatRepoList(entries, false)
useColor = false useColor = false
strip := func(s string) string { strip := func(s string) string {
for _, c := range []string{cReset, cGreen, cYellow, cGray} { for _, c := range []string{cReset, cGreen, cYellow, cDark} {
s = strings.ReplaceAll(s, c, "") s = strings.ReplaceAll(s, c, "")
} }
return s return s
+7 -7
View File
@@ -82,7 +82,7 @@ func overviewAll() {
}) })
if len(rows) == 0 { if len(rows) == 0 {
fmt.Println(col(cGray, "nothing under "+BASE)) fmt.Println(col(cDark, "nothing under "+BASE))
return return
} }
@@ -109,9 +109,9 @@ func overviewAll() {
if initN > 0 { if initN > 0 {
summary += fmt.Sprintf(" · %d to init", initN) summary += fmt.Sprintf(" · %d to init", initN)
} }
fmt.Println(col(cGray, summary)) fmt.Println(col(cDark, summary))
if srv.err != nil { if srv.err != nil {
fmt.Println(col(cGray, " git server not reachable — local view only")) fmt.Println(col(cDark, " git server not reachable — local view only"))
} }
} }
@@ -301,7 +301,7 @@ func syncColor(s projStatus) string {
case s.ahead > 0: case s.ahead > 0:
return cGreen return cGreen
default: default:
return cGray return cDark
} }
} }
@@ -339,8 +339,8 @@ func formatProjStatus(s projStatus, w overviewWidths) string {
if !s.lastWhen.IsZero() { if !s.lastWhen.IsZero() {
age = shortAge(time.Since(s.lastWhen)) age = shortAge(time.Since(s.lastWhen))
} }
b.WriteString(" " + col(cGray, padRight(s.lastHost, w.host))) b.WriteString(" " + col(cDark, padRight(s.lastHost, w.host)))
b.WriteString(" " + col(cGray, fmt.Sprintf("%4s", age))) b.WriteString(" " + col(cDark, fmt.Sprintf("%4s", age)))
} }
if w.hint { if w.hint {
hint := "" hint := ""
@@ -350,7 +350,7 @@ func formatProjStatus(s projStatus, w overviewWidths) string {
b.WriteString(" " + col(cYellow, padRight(hint, hintWidth))) b.WriteString(" " + col(cYellow, padRight(hint, hintWidth)))
} }
if len(s.mirrors) > 0 { if len(s.mirrors) > 0 {
b.WriteString(col(cGray, " → "+strings.Join(s.mirrors, " "))) b.WriteString(col(cDark, " → "+strings.Join(s.mirrors, " ")))
} }
return strings.TrimRight(b.String(), " ") return strings.TrimRight(b.String(), " ")
} }
+5 -5
View File
@@ -45,21 +45,21 @@ func plainPrompt() string {
func coloredPrompt() string { func coloredPrompt() string {
var b strings.Builder var b strings.Builder
b.WriteString(cBold + cPurple + "< " + cReset) b.WriteString(cBold + cViolet + "< " + cReset)
b.WriteString(cCyan + filepath.Base(BASE) + cReset) b.WriteString(cBlue + filepath.Base(BASE) + cReset)
if PRJ != "" && isDir(BASE+"/"+PRJ) { if PRJ != "" && isDir(BASE+"/"+PRJ) {
b.WriteString(cBold + cWhite + "/" + cReset + cBold + cGreen + PRJ + cReset) b.WriteString(cBold + cWhite + "/" + cReset + cBold + cGreen + PRJ + cReset)
if BPLSTATE != "" { if BPLSTATE != "" {
b.WriteString(cRed + BPLSTATE + cReset) b.WriteString(cRed + BPLSTATE + cReset)
} }
if BRANCH != "" { if BRANCH != "" {
b.WriteString(cGray + " (" + cReset + cCyan + BRANCH + cReset) b.WriteString(cDark + " (" + cReset + cBlue + BRANCH + cReset)
if DIRTY { if DIRTY {
b.WriteString(cRed + "*" + cReset) b.WriteString(cRed + "*" + cReset)
} }
b.WriteString(cGray + ")" + cReset) b.WriteString(cDark + ")" + cReset)
} }
} }
b.WriteString(" " + cBold + cPurple + ">" + cReset + " ") b.WriteString(" " + cBold + cViolet + ">" + cReset + " ")
return b.String() return b.String()
} }
+5 -5
View File
@@ -193,8 +193,8 @@ func handleRelease(args string) {
errorln("skipping " + s + ": another directory already contributes that name") errorln("skipping " + s + ": another directory already contributes that name")
} }
if len(assets) > 0 { if len(assets) > 0 {
fmt.Printf("%s %s %s\n", col(cGray, "attaching"), col(cYellow, assetSummary(assets)), fmt.Printf("%s %s %s\n", col(cDark, "attaching"), col(cYellow, assetSummary(assets)),
col(cGray, "from "+strings.Join(assetDirList(DIR), " and "))) col(cDark, "from "+strings.Join(assetDirList(DIR), " and ")))
} }
done := 0 done := 0
@@ -204,7 +204,7 @@ func handleRelease(args string) {
} }
} }
if len(targets) > 1 { if len(targets) > 1 {
fmt.Printf("%s %d/%d remotes released\n", col(cGray, "release:"), done, len(targets)) fmt.Printf("%s %d/%d remotes released\n", col(cDark, "release:"), done, len(targets))
} }
} }
@@ -303,8 +303,8 @@ func publishRelease(t RemoteTarget, repo, tag, body string, assets []releaseAsse
if rel.Prerelease { if rel.Prerelease {
what += " (pre-release)" what += " (pre-release)"
} }
fmt.Printf("%s %s %s %s\n", col(cGray, "remote"), col(cYellow, t.Name), fmt.Printf("%s %s %s %s\n", col(cDark, "remote"), col(cYellow, t.Name),
col(cGreen, what), col(cCyan, api.repoWebURL(owner, repo))) col(cGreen, what), col(cBlue, api.repoWebURL(owner, repo)))
if len(assets) > 0 { if len(assets) > 0 {
if err := api.uploadAssets(owner, repo, ref, assets); err != nil { if err := api.uploadAssets(owner, repo, ref, assets); err != nil {
+3 -3
View File
@@ -296,7 +296,7 @@ func handlePushRemote(args string) {
} }
} }
if len(targets) > 1 { if len(targets) > 1 {
fmt.Printf("%s %d/%d remotes updated\n", col(cGray, "pushremote:"), done, len(targets)) fmt.Printf("%s %d/%d remotes updated\n", col(cDark, "pushremote:"), done, len(targets))
} }
} }
@@ -313,7 +313,7 @@ func pushToRemote(t RemoteTarget, repo, description string) bool {
return false return false
} }
fmt.Printf("%s %s %s (as %s)\n", fmt.Printf("%s %s %s (as %s)\n",
col(cGray, "remote"), col(cYellow, t.Name), col(cCyan, api.url), col(cGreen, owner)) col(cDark, "remote"), col(cYellow, t.Name), col(cBlue, api.url), col(cGreen, owner))
exists, err := api.repoExists(owner, repo) exists, err := api.repoExists(owner, repo)
if err != nil { if err != nil {
@@ -343,7 +343,7 @@ func pushToRemote(t RemoteTarget, repo, description string) bool {
return false return false
} }
gitPushHeader(DIR, t.Name, header, "--tags") gitPushHeader(DIR, t.Name, header, "--tags")
fmt.Println(col(cGreen, "pushed to ") + col(cCyan, web)) fmt.Println(col(cGreen, "pushed to ") + col(cBlue, web))
return true return true
} }
+2 -2
View File
@@ -191,9 +191,9 @@ func secretsApproved(dir string) bool {
where += ":" + strconv.Itoa(h.lineNo) where += ":" + strconv.Itoa(h.lineNo)
} }
fmt.Printf(" %s %s\n %s\n", fmt.Printf(" %s %s\n %s\n",
col(cYellow, where), col(cGray, h.kind), col(cRed, ellipsis(strings.TrimSpace(h.text), 100))) col(cYellow, where), col(cDark, h.kind), col(cRed, ellipsis(strings.TrimSpace(h.text), 100)))
} }
fmt.Println(col(cGray, " (set 'secretscan = off' to skip this check)")) fmt.Println(col(cDark, " (set 'secretscan = off' to skip this check)"))
return yesno("push anyway?", false) return yesno("push anyway?", false)
} }
+9 -9
View File
@@ -24,11 +24,11 @@ func showConfig() {
project = DIR + "/" + projectRC project = DIR + "/" + projectRC
} }
fmt.Printf("%s %s\n", col(cGray, "global "), col(cCyan, global)) fmt.Printf("%s %s\n", col(cDark, "global "), col(cBlue, global))
if project != "" { if project != "" {
fmt.Printf("%s %s\n", col(cGray, "project"), col(cCyan, project)) fmt.Printf("%s %s\n", col(cDark, "project"), col(cBlue, project))
} else if PRJ != "" { } else if PRJ != "" {
fmt.Printf("%s %s\n", col(cGray, "project"), col(cGray, "no "+projectRC+" in "+PRJ)) fmt.Printf("%s %s\n", col(cDark, "project"), col(cDark, "no "+projectRC+" in "+PRJ))
} }
fmt.Println() fmt.Println()
@@ -72,13 +72,13 @@ func showConfig() {
} }
if k := sshKeyPath(); k != "" { if k := sshKeyPath(); k != "" {
fmt.Printf(" %s%s\n", col(cGray, padRight("ssh identity", 14)), col(cGray, k)) fmt.Printf(" %s%s\n", col(cDark, padRight("ssh identity", 14)), col(cDark, k))
} }
fmt.Printf(" %s%s\n", col(cGray, padRight("clone url", 14)), col(cGray, URL)) fmt.Printf(" %s%s\n", col(cDark, padRight("clone url", 14)), col(cDark, URL))
// the project's real origin: it can differ from what the current settings // the project's real origin: it can differ from what the current settings
// would produce, e.g. after moving the server or editing a project .mgshrc // would produce, e.g. after moving the server or editing a project .mgshrc
if o := originURL(); o != "" { if o := originURL(); o != "" {
fmt.Printf(" %s%s\n", col(cGray, padRight("origin", 14)), col(cGray, o)) fmt.Printf(" %s%s\n", col(cDark, padRight("origin", 14)), col(cDark, o))
} }
showRemotes() showRemotes()
@@ -90,11 +90,11 @@ func showRemotes() {
targets, incomplete := cfg.mirrorTargets() targets, incomplete := cfg.mirrorTargets()
fmt.Println() fmt.Println()
if len(targets) == 0 && len(incomplete) == 0 { if len(targets) == 0 && len(incomplete) == 0 {
fmt.Println(col(cGray, "no pushremote targets configured")) fmt.Println(col(cDark, "no pushremote targets configured"))
return return
} }
fmt.Println(col(cGray, "pushremote targets (in push order):")) fmt.Println(col(cDark, "pushremote targets (in push order):"))
for _, t := range targets { for _, t := range targets {
vis := "private" vis := "private"
if strings.EqualFold(strings.TrimSpace(t.Vis), "public") { if strings.EqualFold(strings.TrimSpace(t.Vis), "public") {
@@ -106,7 +106,7 @@ func showRemotes() {
} }
fmt.Printf(" %s%s %s\n", fmt.Printf(" %s%s %s\n",
col(cGreen, padRight("@"+t.Name, 14)), t.URL, col(cGreen, padRight("@"+t.Name, 14)), t.URL,
col(cGray, kind+", "+vis+", key "+maskSecret(t.Key))) col(cDark, kind+", "+vis+", key "+maskSecret(t.Key)))
} }
for _, n := range incomplete { for _, n := range incomplete {
fmt.Printf(" %s%s\n", col(cRed, padRight("@"+n, 14)), col(cRed, "incomplete: url or key missing")) fmt.Printf(" %s%s\n", col(cRed, padRight("@"+n, 14)), col(cRed, "incomplete: url or key missing"))
+1 -1
View File
@@ -1 +1 @@
4.0.58 4.0.62