windows-iso-downloader/cli/homepage.go
Shekhar Vaidya 3994a82d6d feat(cli): homepage landing screen for bare msdl; fix search substring bug
The bare `msdl` invocation showed all 22 products (17 consumer + 5
eval) as a flat numbered list. Replaces it with a curated landing
screen: 5 popular products (picked from real usage telemetry) plus a
2-item eval shortlist, with "list" as an escape hatch to today's full
picker and free text routed through the existing search flow.

Also fixes a search bug: querying "windows 10" incorrectly matched
Windows 11 24H2 because "10" is a substring of its build number
(26100.1742). Query words now must start at a word boundary rather
than matching anywhere, so digit fragments inside unrelated numbers
can no longer collide -- while "arm" still fuzzy-matches "ARM64".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 12:24:43 +05:30

114 lines
3.8 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// homepageFeaturedIDs are the consumer products shown on the bare `msdl`
// landing screen, picked from real usage telemetry rather than the full
// catalog. The full catalog is still one step away via "list".
var homepageFeaturedIDs = []string{"3262", "2618", "3321", "3113", "3265"}
// homepageEvalSlugs are the eval products shown on the landing screen.
var homepageEvalSlugs = []string{"server-2025", "win11-ent"}
type homepageKind int
const (
homepageKindProduct homepageKind = iota
homepageKindEval
homepageKindList
homepageKindSearch
)
type homepageChoice struct {
kind homepageKind
productID string
evalSlug string
query string
}
const homepageRule = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
// showHomepage prints the landing screen for a bare `msdl` invocation and
// reads the user's choice: a number, "list" for the full catalog, or free
// text to search by name.
func showHomepage(latestVersion string) (homepageChoice, error) {
fmt.Fprintln(os.Stderr, homepageRule)
fmt.Fprintln(os.Stderr, "msdl — Windows ISO Downloader")
fmt.Fprintln(os.Stderr, "Direct links from Microsoft's CDN. No browser, no Media Creation Tool.")
fmt.Fprintln(os.Stderr, homepageRule)
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "Popular:")
featured := make([]Product, 0, len(homepageFeaturedIDs))
for _, id := range homepageFeaturedIDs {
if p, ok := findProductByID(id); ok {
featured = append(featured, p)
}
}
for i, p := range featured {
fmt.Fprintf(os.Stderr, "%d. %s\n", i+1, p.Name)
}
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "Evaluation / Enterprise:")
evalShortlist := make([]EvalProduct, 0, len(homepageEvalSlugs))
for _, slug := range homepageEvalSlugs {
if ep, ok := findEvalProduct(slug); ok {
evalShortlist = append(evalShortlist, ep)
}
}
for i, ep := range evalShortlist {
fmt.Fprintf(os.Stderr, "%d. %s\n", len(featured)+i+1, ep.Name)
}
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, `Commands: --list (full catalog) · --help`)
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, homepageRule)
if latestVersion != "" && latestVersion != Version && Version != "dev" {
fmt.Fprintf(os.Stderr, "Version %s (Update Available %s) - a TechLatest Open-source Contribution\n", Version, latestVersion)
} else {
fmt.Fprintf(os.Stderr, "Version %s - a TechLatest Open-source Contribution\n", Version)
}
fmt.Fprintln(os.Stderr)
max := len(featured) + len(evalShortlist)
fmt.Fprintf(os.Stderr, `Choice [1-%d], search by name, or "list": `, max)
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
if err := scanner.Err(); err != nil {
return homepageChoice{}, fmt.Errorf("reading input: %w", err)
}
return homepageChoice{}, fmt.Errorf("no input")
}
return parseHomepageInput(scanner.Text(), featured, evalShortlist)
}
// parseHomepageInput interprets the raw input line from the homepage prompt.
func parseHomepageInput(input string, featured []Product, evalShortlist []EvalProduct) (homepageChoice, error) {
text := strings.TrimSpace(input)
if text == "" {
return homepageChoice{}, fmt.Errorf(`enter a choice, search term, or "list"`)
}
if strings.EqualFold(text, "list") {
return homepageChoice{kind: homepageKindList}, nil
}
if n, err := strconv.Atoi(text); err == nil {
max := len(featured) + len(evalShortlist)
if n < 1 || n > max {
return homepageChoice{}, fmt.Errorf(`enter a number between 1 and %d, a search term, or "list"`, max)
}
if n <= len(featured) {
return homepageChoice{kind: homepageKindProduct, productID: featured[n-1].ID}, nil
}
return homepageChoice{kind: homepageKindEval, evalSlug: evalShortlist[n-len(featured)-1].Slug}, nil
}
return homepageChoice{kind: homepageKindSearch, query: text}, nil
}