feat(cli): architecture picker + post-fetch O/C/Enter menu
When multiple download links are returned (e.g. x32+x64 for consumer ISOs), an architecture picker is shown before printing the URL. After the URL is printed, an interactive menu lets the user open the link in the default browser (O), copy it to the clipboard (C), or exit (Enter). Menu is suppressed when stdout is not a terminal (piping). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
29d6b6b3f1
commit
3e45bfd0c7
2 changed files with 115 additions and 6 deletions
43
cli/main.go
43
cli/main.go
|
|
@ -308,18 +308,42 @@ func runConsumer(productID, query, langName string, noContribute bool) error {
|
|||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
for _, link := range links {
|
||||
|
||||
var selectedURI string
|
||||
if isTerminal() && len(links) > 1 {
|
||||
link, err := pickArchitecture(links)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := urlFilename(link.URI)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", name)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", strings.Repeat("─", len(name)))
|
||||
fmt.Println(link.URI) // stdout — stays clean for piping
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
fmt.Fprintf(os.Stderr, "\n %s\n", name)
|
||||
fmt.Fprintf(os.Stderr, " %s\n\n", strings.Repeat("─", len(name)))
|
||||
fmt.Println(link.URI)
|
||||
selectedURI = link.URI
|
||||
} else {
|
||||
for _, link := range links {
|
||||
name := urlFilename(link.URI)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", name)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", strings.Repeat("─", len(name)))
|
||||
fmt.Println(link.URI) // stdout — stays clean for piping
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
}
|
||||
if len(links) == 1 {
|
||||
selectedURI = links[0].URI
|
||||
}
|
||||
}
|
||||
|
||||
if !noContribute {
|
||||
fmt.Fprintln(os.Stderr, " ✓ Shared with MSDL Web App (https://msdl.tech-latest.com/)")
|
||||
fmt.Fprintln(os.Stderr, " Opt out: --no-contribute")
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
}
|
||||
|
||||
if isTerminal() && selectedURI != "" {
|
||||
postFetchMenu(selectedURI)
|
||||
}
|
||||
|
||||
if !noContribute {
|
||||
// Wait up to 5s for the contribution to complete before process exit.
|
||||
done := make(chan struct{})
|
||||
go func() { wg.Wait(); close(done) }()
|
||||
|
|
@ -359,6 +383,9 @@ func runEval(slug string) error {
|
|||
|
||||
if len(links) == 1 {
|
||||
fmt.Println(links[0].URL)
|
||||
if isTerminal() {
|
||||
postFetchMenu(links[0].URL)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -377,6 +404,10 @@ func runEval(slug string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(links[n-1].URL)
|
||||
selected := links[n-1].URL
|
||||
fmt.Println(selected)
|
||||
if isTerminal() {
|
||||
postFetchMenu(selected)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -76,3 +78,79 @@ func pickEvalProduct(products []EvalProduct) (EvalProduct, error) {
|
|||
}
|
||||
return products[n-1], nil
|
||||
}
|
||||
|
||||
func isTerminal() bool {
|
||||
fi, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.Mode()&os.ModeCharDevice != 0
|
||||
}
|
||||
|
||||
func pickArchitecture(links []DownloadLink) (DownloadLink, error) {
|
||||
fmt.Fprintln(os.Stderr, "Select architecture:")
|
||||
for i, l := range links {
|
||||
label := l.Architecture
|
||||
if label == "" {
|
||||
label = fmt.Sprintf("link %d", i+1)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, " %2d. %s\n", i+1, label)
|
||||
}
|
||||
fmt.Fprint(os.Stderr, "\nChoice: ")
|
||||
n, err := parseChoice(os.Stdin, len(links))
|
||||
if err != nil {
|
||||
return DownloadLink{}, err
|
||||
}
|
||||
return links[n-1], nil
|
||||
}
|
||||
|
||||
func openInBrowser(rawURL string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "start", "", rawURL)
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", rawURL)
|
||||
default:
|
||||
cmd = exec.Command("xdg-open", rawURL)
|
||||
}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func copyToClipboard(text string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "clip")
|
||||
case "darwin":
|
||||
cmd = exec.Command("pbcopy")
|
||||
default:
|
||||
if _, err := exec.LookPath("xclip"); err == nil {
|
||||
cmd = exec.Command("xclip", "-selection", "clipboard")
|
||||
} else {
|
||||
cmd = exec.Command("xsel", "--clipboard", "--input")
|
||||
}
|
||||
}
|
||||
cmd.Stdin = strings.NewReader(text)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func postFetchMenu(uri string) {
|
||||
fmt.Fprint(os.Stderr, "\n [O] Open in browser [C] Copy to clipboard [Enter] Exit\n > ")
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
if !scanner.Scan() {
|
||||
return
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(scanner.Text())) {
|
||||
case "o":
|
||||
if err := openInBrowser(uri); err != nil {
|
||||
fmt.Fprintln(os.Stderr, " Could not open browser:", err)
|
||||
}
|
||||
case "c":
|
||||
if err := copyToClipboard(uri); err != nil {
|
||||
fmt.Fprintln(os.Stderr, " Could not copy to clipboard:", err)
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, " ✓ Copied to clipboard")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue