clone

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

bug/architecture-test-trim-path
puzzletea / app / seed_input.go
seed_input.go 4.0K
1package app23import (4 "strings"56 "github.com/FelineStateMachine/puzzletea/puzzle"7 "github.com/FelineStateMachine/puzzletea/registry"8 "github.com/FelineStateMachine/puzzletea/theme"9 "github.com/FelineStateMachine/puzzletea/ui"1011 "charm.land/bubbles/v2/textinput"12 tea "charm.land/bubbletea/v2"13 "charm.land/lipgloss/v2"14)1516const randomSeedModeLabel = "Random"1718func buildSeedModeOptions(definitions []puzzle.Definition) []seedModeOption {19 options := []seedModeOption{{label: randomSeedModeLabel}}2021 for _, def := range definitions {22 if !definitionHasSeededModes(def) {23 continue24 }2526 options = append(options, seedModeOption{27 key: seedModeKey(def.Name, ""),28 label: def.Name,29 gameType: def.Name,30 })31 }3233 return options34}3536func seedModeKey(gameType, modeTitle string) string {37 if modeTitle == "" {38 return puzzle.NormalizeName(gameType)39 }40 return puzzle.NormalizeName(gameType) + "::" + puzzle.NormalizeName(modeTitle)41}4243func definitionHasSeededModes(def puzzle.Definition) bool {44 return len(def.Variants) > 045}4647func findSeedModeIndex(options []seedModeOption, key string) int {48 if key == "" {49 return 050 }51 for i := range options {52 if options[i].key == key {53 return i54 }55 }56 return 057}5859func (m model) enterSeedInputView() (model, tea.Cmd) {60 ti := textinput.New()61 ti.Placeholder = "any word or phrase"62 ti.CharLimit = 6463 ti.SetWidth(min(m.width, 48))6465 options := buildSeedModeOptions(registry.Definitions())66 index := findSeedModeIndex(options, m.seed.lastModeKey)6768 m.seed.input = ti69 m.seed.modeOptions = options70 m.seed.modeIndex = index71 m.seed.focus = seedFocusText72 m.state = seedInputView73 m = m.initScreen(seedInputView)74 return m, m.seed.input.Focus()75}7677func (m model) currentSeedMode() seedModeOption {78 return currentSeedMode(m.seed)79}8081func currentSeedMode(seed seedState) seedModeOption {82 if len(seed.modeOptions) == 0 {83 return seedModeOption{label: randomSeedModeLabel}84 }85 if seed.modeIndex < 0 || seed.modeIndex >= len(seed.modeOptions) {86 return seed.modeOptions[0]87 }88 return seed.modeOptions[seed.modeIndex]89}9091func (m model) moveSeedMode(step int) model {92 if len(m.seed.modeOptions) == 0 {93 return m94 }9596 index := m.seed.modeIndex + step97 for index < 0 {98 index += len(m.seed.modeOptions)99 }100 m.seed.modeIndex = index % len(m.seed.modeOptions)101 m.seed.lastModeKey = m.currentSeedMode().key102 return m103}104105func (m model) setSeedFocus(focus seedInputFocus) (model, tea.Cmd) {106 m.seed.focus = focus107 if focus == seedFocusText {108 return m, m.seed.input.Focus()109 }110 m.seed.input.Blur()111 return m, nil112}113114func (m model) handleSeedInputUpdate(msg tea.Msg) (model, tea.Cmd) {115 keyMsg, ok := msg.(tea.KeyPressMsg)116 if ok {117 switch keyMsg.String() {118 case "up", "k":119 return m.setSeedFocus(seedFocusText)120 case "down", "j":121 return m.setSeedFocus(seedFocusMode)122 case "left", "h":123 if m.seed.focus == seedFocusMode {124 return m.moveSeedMode(-1), nil125 }126 case "right", "l":127 if m.seed.focus == seedFocusMode {128 return m.moveSeedMode(1), nil129 }130 }131 }132133 if m.seed.focus != seedFocusText {134 return m, nil135 }136137 var cmd tea.Cmd138 m.seed.input, cmd = m.seed.input.Update(msg)139 return m, cmd140}141142func seedInputBody(seed seedState) string {143 selector := renderSeedModeTitle(currentSeedMode(seed).label, seed.modeIndex)144 modePrefix := " "145 if seed.focus == seedFocusMode {146 modePrefix = ui.CursorStyle().Render("> ")147 }148149 inputView := seed.input.View()150 if seed.focus != seedFocusText {151 inputView = strings.Replace(inputView, ">", " ", 1)152 }153154 return lipgloss.JoinVertical(155 lipgloss.Left,156 inputView,157 "",158 modePrefix+selector,159 )160}161162func renderSeedModeTitle(label string, colorIndex int) string {163 colors := theme.Current().ThemeColors()164 if len(colors) == 0 {165 return ui.PanelTitle().Render("game: " + label)166 }167168 bg := colors[((colorIndex%len(colors))+len(colors))%len(colors)]169 chip := lipgloss.NewStyle().170 Bold(true).171 Foreground(theme.TextOnBG(bg)).172 Background(bg).173 Render(label)174175 var b strings.Builder176 b.WriteString(ui.PanelTitle().Render("game: "))177 b.WriteString(chip)178 return b.String()179}