Use rune input for copy menu to improve muscle-memorability

This commit is contained in:
2026-05-29 21:17:00 -04:00
parent 6b77e717cf
commit a8de1ce0a6
4 changed files with 34 additions and 29 deletions
+2
View File
@@ -43,3 +43,5 @@ replace golang.org/x/sys => github.com/rwinkhart/sys v0.45.0
replace github.com/Microsoft/go-winio => github.com/rwinkhart/go-winio v0.1.1 replace github.com/Microsoft/go-winio => github.com/rwinkhart/go-winio v0.1.1
replace github.com/charmbracelet/glamour => github.com/rwinkhart/glamour-temp-MUTN v0.7.2 replace github.com/charmbracelet/glamour => github.com/rwinkhart/glamour-temp-MUTN v0.7.2
replace github.com/rwinkhart/go-boilerplate => ../go-boilerplate
-2
View File
@@ -42,8 +42,6 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rwinkhart/glamour-temp-MUTN v0.7.2 h1:/CBexwkGdL8HuZjO7M0zzjGHaHCr9/r4XTOtUJa+pPE= github.com/rwinkhart/glamour-temp-MUTN v0.7.2 h1:/CBexwkGdL8HuZjO7M0zzjGHaHCr9/r4XTOtUJa+pPE=
github.com/rwinkhart/glamour-temp-MUTN v0.7.2/go.mod h1:DN/eJaNQ5h7dgxzfmWFo7By+b82CKEUdNaY6z4R4v3c= github.com/rwinkhart/glamour-temp-MUTN v0.7.2/go.mod h1:DN/eJaNQ5h7dgxzfmWFo7By+b82CKEUdNaY6z4R4v3c=
github.com/rwinkhart/go-boilerplate v0.3.1 h1:vkVRuptO2s1yPzzwpvtiiB0c/hPB6yr0p1mzZ2Myb9E=
github.com/rwinkhart/go-boilerplate v0.3.1/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes=
github.com/rwinkhart/go-highlite v0.1.1 h1:9TxbRhYVfD/3YaEgNk1BtEiZ0t8/5mxDUZqNMXHodT0= github.com/rwinkhart/go-highlite v0.1.1 h1:9TxbRhYVfD/3YaEgNk1BtEiZ0t8/5mxDUZqNMXHodT0=
github.com/rwinkhart/go-highlite v0.1.1/go.mod h1:mWLMtCWcyV0BG4NeyPyAUGMjPvI0ds6vXmUq89QwBFA= github.com/rwinkhart/go-highlite v0.1.1/go.mod h1:mWLMtCWcyV0BG4NeyPyAUGMjPvI0ds6vXmUq89QwBFA=
github.com/rwinkhart/go-winio v0.1.1 h1:kAJKiqneR7cUR01Wn5/doAAV4kOGTEGPug4oinXc5N4= github.com/rwinkhart/go-winio v0.1.1 h1:kAJKiqneR7cUR01Wn5/doAAV4kOGTEGPug4oinXc5N4=
+25 -25
View File
@@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"os" "os"
"os/signal" "os/signal"
"slices"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@@ -40,14 +39,18 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword []byte) {
} }
// determine populated fields in entry // determine populated fields in entry
var fieldStrings = []string{"Username", "Password", "TOTP Code", "URL", "Note (first line)"} var allStrings = [5]string{"Username", "Password", "TOTP Code", "URL", "Note (first line)"}
var indices = []int{1, 0, 2, 3, 4} var allIndices = [5]int{1, 0, 2, 3, 4}
var fieldOptions []string var allRunes = [5]rune{'u', 'p', 't', 'l', 'n'}
for i := range indices { var fieldStrings []string
if len(decSlice) > indices[i] && decSlice[indices[i]] != "" { var fieldRunes []rune
fieldOptions = append(fieldOptions, fieldStrings[i]) for i := range allIndices {
if indices[i] == 0 && oldPassword != nil { if len(decSlice) > allIndices[i] && decSlice[allIndices[i]] != "" {
fieldOptions = append(fieldOptions, "Old Password") fieldStrings = append(fieldStrings, allStrings[i])
fieldRunes = append(fieldRunes, allRunes[i])
if allIndices[i] == 0 && oldPassword != nil {
fieldStrings = append(fieldStrings, "Old Password")
fieldRunes = append(fieldRunes, 'b')
} }
} }
} }
@@ -55,7 +58,7 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword []byte) {
// if notes are included, preview them // if notes are included, preview them
if len(decSlice) > 4 { if len(decSlice) > 4 {
EntryReader(vanityPath, append([]string{"", "", "", ""}, decSlice[4:]...), true) EntryReader(vanityPath, append([]string{"", "", "", ""}, decSlice[4:]...), true)
if len(fieldOptions) < 1 { // if notes are the only thing included, exit if len(fieldStrings) < 1 { // if notes are the only thing included, exit
fmt.Printf("\r%sNo copyable fields present, exiting copy menu...%s\n", back.AnsiWarning, back.AnsiReset) fmt.Printf("\r%sNo copyable fields present, exiting copy menu...%s\n", back.AnsiWarning, back.AnsiReset)
os.Exit(0) os.Exit(0)
} }
@@ -94,26 +97,23 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword []byte) {
}() }()
// copy selected field to clipboard // copy selected field to clipboard
var choice int var runesToIndices = map[rune]int{'u': 1, 'p': 0, 't': 2, 'l': 3, 'n': 4}
var selectedField string var selectedField rune
for { for {
fmt.Println() fmt.Println()
if selectedField != "" { if selectedField != 0 {
choice = front.InputMenuGen("Field to copy:", fieldOptions) selectedField = front.InputMenuGenWithRuneInputs("Field to copy:", fieldStrings, fieldRunes)
} else { } else {
choice = front.InputMenuGen("Field to copy (exiting in 5 seconds):", fieldOptions) selectedField = front.InputMenuGenWithRuneInputs("Field to copy (exiting in 5 seconds):", fieldStrings, fieldRunes)
selectedChan <- true selectedChan <- true
} }
selectedField = fieldOptions[choice-1]
if selectedField == "Old Password" { switch selectedField {
case 'b': // old password
if err = clip.CopyBytes(false, oldPassword); err != nil { if err = clip.CopyBytes(false, oldPassword); err != nil {
other.PrintError("Failed to copy old password to clipboard: "+err.Error(), global.ErrorClipboard) other.PrintError("Failed to copy old password to clipboard: "+err.Error(), global.ErrorClipboard)
} }
continue case 't': // TOTP
}
choice = indices[slices.Index(fieldStrings, selectedField)]
switch choice {
case 2: // TOTP
fmt.Println(back.AnsiWarning + "[Starting]" + back.AnsiReset + " TOTP clipboard refresher") fmt.Println(back.AnsiWarning + "[Starting]" + back.AnsiReset + " TOTP clipboard refresher")
errorChan := make(chan error, 1) errorChan := make(chan error, 1)
done := make(chan bool) done := make(chan bool)
@@ -131,11 +131,11 @@ func CopyMenu(vanityPath string, decSlice []string, oldPassword []byte) {
} }
close(done) close(done)
fmt.Println(back.AnsiBlue + "[Stopped]" + back.AnsiReset + " TOTP clipboard refresher") fmt.Println(back.AnsiBlue + "[Stopped]" + back.AnsiReset + " TOTP clipboard refresher")
case 4: // notes case 'n': // notes
decSlice[choice] = strings.TrimRight(decSlice[choice], " ") decSlice[4] = strings.TrimRight(decSlice[4], " ")
fallthrough fallthrough
default: default:
if err = clip.CopyBytes(false, []byte(decSlice[choice])); err != nil { if err = clip.CopyBytes(false, []byte(decSlice[runesToIndices[selectedField]])); err != nil {
other.PrintError("Failed to copy field to clipboard: "+err.Error(), global.ErrorClipboard) other.PrintError("Failed to copy field to clipboard: "+err.Error(), global.ErrorClipboard)
} }
} }
+7 -2
View File
@@ -37,7 +37,9 @@ func EditEntryField(realPath string, field int) {
var oldPassword []byte var oldPassword []byte
switch field { switch field {
case 0: case 0:
oldPassword = []byte(decSlice[field]) if decSlice[0] != "" {
oldPassword = []byte(decSlice[0])
}
decSlice[field] = string(front.InputSecret("Password:")) decSlice[field] = string(front.InputSecret("Password:"))
case 1: case 1:
decSlice[field] = front.Input("Username:") decSlice[field] = front.Input("Username:")
@@ -75,7 +77,10 @@ func GenUpdate(realPath string) {
} }
// generate a new password // generate a new password
oldPassword := []byte(decSlice[0]) var oldPassword []byte
if decSlice[0] != "" {
oldPassword = []byte(decSlice[0])
}
decSlice[0] = string(inputPasswordGen()) decSlice[0] = string(inputPasswordGen())
// write and preview the modified entry // write and preview the modified entry