clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

chore/app-model-decomp
puzzletea / app / mode_display.go
mode_display.go 2.5K
1package app23import (4 "strings"56 "charm.land/bubbles/v2/list"7 "github.com/FelineStateMachine/puzzletea/puzzle"8 "github.com/FelineStateMachine/puzzletea/registry"9)1011type modeDisplayItem struct {12 mode registry.VariantEntry13 title string14 description string15 filterValue string16}1718func (i modeDisplayItem) Title() string { return i.title }19func (i modeDisplayItem) Description() string { return i.description }20func (i modeDisplayItem) FilterValue() string { return i.filterValue }21func (i modeDisplayItem) Original() registry.VariantEntry { return i.mode }2223func buildModeDisplayItems(entry registry.Entry) []list.Item {24 titles := modeDisplayTitles(entry)25 items := make([]list.Item, 0, len(entry.Variants))2627 for idx, mode := range entry.Variants {28 displayTitle := titles[idx]29 filterValue := displayTitle + " " + mode.Definition.Description30 if displayTitle != mode.Definition.Title {31 filterValue += " " + mode.Definition.Title32 }3334 items = append(items, modeDisplayItem{35 mode: mode,36 title: displayTitle,37 description: mode.Definition.Description,38 filterValue: strings.TrimSpace(filterValue),39 })40 }4142 return items43}4445func modeDisplayTitles(entry registry.Entry) []string {46 counts := make(map[string]int, len(entry.Variants))47 titles := make([]string, 0, len(entry.Variants))4849 for _, mode := range entry.Variants {50 base, _ := trimGridSizeSuffix(mode.Definition.Title)51 counts[puzzle.NormalizeName(base)]++52 }5354 for _, mode := range entry.Variants {55 title := mode.Definition.Title56 base, hadSize := trimGridSizeSuffix(title)57 if hadSize && counts[puzzle.NormalizeName(base)] == 1 {58 title = base59 }60 titles = append(titles, title)61 }6263 return titles64}6566func unwrapModeDisplayItem(item list.Item) any {67 displayItem, ok := item.(interface{ Original() registry.VariantEntry })68 if !ok {69 return item70 }71 return displayItem.Original()72}7374func trimGridSizeSuffix(title string) (string, bool) {75 fields := strings.Fields(title)76 if len(fields) < 2 {77 return title, false78 }7980 if !isGridSizeToken(fields[len(fields)-1]) {81 return title, false82 }8384 base := strings.TrimSpace(strings.Join(fields[:len(fields)-1], " "))85 if base == "" {86 return title, false87 }8889 return base, true90}9192func isGridSizeToken(token string) bool {93 width, height, found := strings.Cut(token, "x")94 if !found || width == "" || height == "" {95 return false96 }97 return digitsOnly(width) && digitsOnly(height)98}99100func digitsOnly(s string) bool {101 for _, r := range s {102 if r < '0' || r > '9' {103 return false104 }105 }106 return true107}