diff options
-rw-r--r-- | ui/views/auth.go | 21 | ||||
-rw-r--r-- | ui/views/play.go | 213 | ||||
-rw-r--r-- | ui/views/tabs.go | 5 | ||||
-rw-r--r-- | ui/views/views.go | 26 |
4 files changed, 206 insertions, 59 deletions
diff --git a/ui/views/auth.go b/ui/views/auth.go index 9599b4a..a695466 100644 --- a/ui/views/auth.go +++ b/ui/views/auth.go @@ -63,8 +63,6 @@ func NewAuthModel(width, height int) AuthModel { // Initialize loginModel func initLoginModel(width, height int) loginModel { - inputStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#7EE2A8")) - username := textinput.New() username.Prompt = " " username.TextStyle = inputStyle @@ -150,6 +148,11 @@ func (m AuthModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + return m, nil + case tea.KeyMsg: switch msg.String() { case "alt+1": @@ -481,11 +484,6 @@ func (m loginModel) renderContent() string { Align(lipgloss.Center). Width(formWidth - 4) // Account for padding - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#ff0000")). - Align(lipgloss.Center). - Width(formWidth - 4) // Account for padding - statusStyle := lipgloss.NewStyle(). Align(lipgloss.Center). Bold(true). @@ -506,7 +504,7 @@ func (m loginModel) renderContent() string { form := lipgloss.JoinVertical(lipgloss.Center, titleStyle.Render("Sign in to your account"), "\n", - errorStyle.Render(formError), + errorStyle.Align(lipgloss.Center).Width(formWidth-4).Render(formError), inputWrapStyle.Render( lipgloss.JoinHorizontal(lipgloss.Left, labelStyle.Render("Username:"), @@ -545,11 +543,6 @@ func (m signupModel) renderContent() string { Align(lipgloss.Center). Width(formWidth - 4) // Account for padding - errorStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#ff0000")). - Align(lipgloss.Center). - Width(formWidth - 4) // Account for padding - statusStyle := lipgloss.NewStyle(). Align(lipgloss.Center). Bold(true). @@ -570,7 +563,7 @@ func (m signupModel) renderContent() string { form := lipgloss.JoinVertical(lipgloss.Center, titleStyle.Render("Create a new account"), "\n", - errorStyle.Render(formError), + errorStyle.Align(lipgloss.Center).Width(formWidth-4).Render(formError), inputWrapStyle.Render( lipgloss.JoinHorizontal(lipgloss.Left, labelStyle.Render("Username:"), diff --git a/ui/views/play.go b/ui/views/play.go index 0c09d2a..b9d9230 100644 --- a/ui/views/play.go +++ b/ui/views/play.go @@ -1,21 +1,92 @@ package views import ( + "errors" + "fmt" + "os" + + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) +var chess string = ` + A B C D E F G H ++---------------+ +8 |♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜| 8 +7 |♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟| 7 +6 |. . . . . . . .| 6 +5 |. . . . . . . .| 5 +4 |. . . . . . . .| 4 +3 |. . . . . . . .| 3 +2 |♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙| 2 +1 |♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖| 1 ++---------------+ + A B C D E F G H +` + +type playKeyMap struct { + EnterNewPlay key.Binding + StartNewPlay key.Binding + GoLogout key.Binding + Quit key.Binding +} + +var defaultPlayKeyMap = playKeyMap{ + EnterNewPlay: key.NewBinding( + key.WithKeys("alt+E", "alt+e"), + key.WithHelp("Alt+E", "Enter a play using code"), + ), + StartNewPlay: key.NewBinding( + key.WithKeys("alt+s", "alt+s"), + key.WithHelp("Alt+S", "Start a new play"), + ), + GoLogout: key.NewBinding( + key.WithKeys("alt+Q", "alt+q"), + key.WithHelp("Alt+Q", "Logout"), + ), + Quit: key.NewBinding( + key.WithKeys("Q", "q"), + key.WithHelp(" Q", "Quit"), + ), +} + +type PlayModelPage int + +const ( + LandingPage PlayModelPage = iota + InsertCodePage + StartPlayPage +) + type PlayModel struct { - width int - height int + width int + height int + err error + keys playKeyMap + namePrompt textinput.Model + page PlayModelPage + isLoading bool } func NewPlayModel(width, height int) PlayModel { + namePrompt := textinput.New() + namePrompt.Prompt = " " + namePrompt.TextStyle = inputStyle + namePrompt.Placeholder = "rectangular-lake" + namePrompt.Focus() + namePrompt.CharLimit = 23 + namePrompt.Width = 23 return PlayModel{ - width: width, - height: height, + width: width, + height: height, + err: nil, + keys: defaultPlayKeyMap, + namePrompt: namePrompt, + page: LandingPage, + isLoading: false, } } @@ -30,40 +101,122 @@ func (m PlayModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "q": - return m, SwitchModelCmd(NewAuthModel(m.width, m.height)) + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + return m, nil + case tea.KeyMsg: + switch { + case key.Matches(msg, m.keys.EnterNewPlay): + m.page = InsertCodePage + return m, nil + case key.Matches(msg, m.keys.StartNewPlay): + // TODO: handle new play + return m, nil + case key.Matches(msg, m.keys.GoLogout): + if err := os.Remove(".rahannarc"); err != nil { + m.err = err + return m, nil + } + return m, SwitchModelCmd(NewAuthModel(m.width, m.height+1)) + case key.Matches(msg, m.keys.Quit): + return m, tea.Quit + case msg.Type == tea.KeyEnter: + if m.page == InsertCodePage { + m.err = errors.New("Can't join for now...") + } } } - return m, nil + + var cmd tea.Cmd = nil + + if m.page == InsertCodePage { + m.namePrompt, cmd = m.namePrompt.Update(msg) + } + + return m, tea.Batch(cmd) } func (m PlayModel) View() string { - width, height := m.width, m.height + formWidth := getFormWidth(m.width) - // Create the window with tab content - ui := lipgloss.JoinVertical(lipgloss.Center, - windowStyle.Width(getFormWidth(width)).Render("New Play"), - ) + // Error message + formError := "" + if m.err != nil { + formError = fmt.Sprintf("Error: %v", m.err.Error()) + } - // Center logo and form in available space - contentHeight := lipgloss.Height(logo) + lipgloss.Height(ui) + 2 - paddingTop := (height - contentHeight) / 2 - if paddingTop < 0 { - paddingTop = 0 + // Status message + statusMsg := fmt.Sprintf("Press %s to join", lipgloss.NewStyle().Italic(true).Render("Enter")) + if m.isLoading { + statusMsg = "Creating account..." } - // Combine logo and tabs with vertical centering - output := lipgloss.NewStyle(). - MarginTop(paddingTop). - Render( - lipgloss.JoinVertical(lipgloss.Center, - getLogo(m.width), - lipgloss.PlaceHorizontal(width, lipgloss.Center, ui), - ), - ) - - return output + var content string + + switch m.page { + case LandingPage: + content = chess + m.namePrompt.Blur() + case InsertCodePage: + m.namePrompt.Focus() + content = m.namePrompt.View() + + content = lipgloss.NewStyle(). + Align(lipgloss.Center). + Width(m.width). + Render( + lipgloss.JoinVertical(lipgloss.Left, + lipgloss.NewStyle().Width(23).Render("Insert play code:"), + m.namePrompt.View(), + lipgloss.NewStyle(). + Align(lipgloss.Center). + PaddingTop(2). + Width(23). + Bold(true). + Render(statusMsg), + ), + ) + } + + windowContent := lipgloss.JoinVertical( + lipgloss.Center, + windowStyle. + Width(formWidth). + Render(lipgloss.JoinVertical( + lipgloss.Center, + errorStyle.Align(lipgloss.Center).Width(formWidth-4).Render(formError), + content, + )), + ) + + enterKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.EnterNewPlay.Help().Key), m.keys.EnterNewPlay.Help().Desc) + startKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.StartNewPlay.Help().Key), m.keys.StartNewPlay.Help().Desc) + logoutKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.GoLogout.Help().Key), m.keys.GoLogout.Help().Desc) + quitKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.Quit.Help().Key), m.keys.Quit.Help().Desc) + + // Vertically align the buttons + buttons := lipgloss.JoinVertical( + lipgloss.Left, + enterKey, + startKey, + logoutKey, + quitKey, + ) + + centeredContent := lipgloss.JoinVertical( + lipgloss.Center, + getLogo(m.width), + windowContent, + lipgloss.NewStyle().MarginTop(2).Render(buttons), + ) + + return lipgloss.Place( + m.width, + m.height, + lipgloss.Center, + lipgloss.Center, + centeredContent, + ) } diff --git a/ui/views/tabs.go b/ui/views/tabs.go index edfd02a..13e3672 100644 --- a/ui/views/tabs.go +++ b/ui/views/tabs.go @@ -6,17 +6,12 @@ import ( "github.com/charmbracelet/lipgloss" ) -// TabType represents the available tabs - type TabType int var ( - highlightColor = lipgloss.Color("#7ee2a8") tabStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(highlightColor).Padding(0, 2) inactiveTabStyle = tabStyle activeTabStyle = tabStyle - altCodeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Bold(true) - windowStyle = lipgloss.NewStyle().BorderForeground(highlightColor).Padding(2, 0).Align(lipgloss.Center).Border(lipgloss.RoundedBorder()) ) func getTabsRow(tabsText []string, activeTab TabType) string { diff --git a/ui/views/views.go b/ui/views/views.go index f830687..fa70035 100644 --- a/ui/views/views.go +++ b/ui/views/views.go @@ -3,6 +3,7 @@ package views import ( "errors" "os" + "os/exec" tea "github.com/charmbracelet/bubbletea" @@ -17,6 +18,14 @@ var logo = ` ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌ ` +var ( + highlightColor = lipgloss.Color("#7ee2a8") + errorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#ff0000")) + altCodeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Bold(true) + windowStyle = lipgloss.NewStyle().BorderForeground(highlightColor).Padding(2, 0).Align(lipgloss.Center).Border(lipgloss.RoundedBorder()) + inputStyle = lipgloss.NewStyle().Foreground(highlightColor) +) + // Get terminal size dynamically func GetTerminalSize() (width, height int) { fd := int(os.Stdin.Fd()) @@ -28,12 +37,14 @@ func GetTerminalSize() (width, height int) { // Clear terminal screen func ClearScreen() { - cmd := exec.Command("clear") // Unix (Linux/macOS) - if os.Getenv("OS") == "Windows_NT" { - cmd = exec.Command("cmd", "/c", "cls") // Windows + if len(os.Getenv("DEBUG")) == 0 { + cmd := exec.Command("clear") + if os.Getenv("OS") == "Windows_NT" { + cmd = exec.Command("cmd", "/c", "cls") + } + cmd.Stdout = os.Stdout + cmd.Run() } - cmd.Stdout = os.Stdout - cmd.Run() } func getFormWidth(width int) int { @@ -99,11 +110,6 @@ func (m RahannaModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case switchModel: m.currentModel = msg.model return m, nil - - case tea.WindowSizeMsg: - m.width = msg.Width - m.height = msg.Height - return m, nil } var cmd tea.Cmd m.currentModel, cmd = m.currentModel.Update(msg) |