summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2025-04-05 18:46:17 +0200
committerSanto Cariotti <santo@dcariotti.me>2025-04-05 18:46:17 +0200
commit6d5824f7d2225bdb434becd4c5afa838aa2af440 (patch)
treeb036dd2efea4dcf8945729b200d24960633ec6cb
parentd304cffee2f45361ef527181b74bfc1b2a670115 (diff)
Play model
-rw-r--r--ui/views/auth.go21
-rw-r--r--ui/views/play.go213
-rw-r--r--ui/views/tabs.go5
-rw-r--r--ui/views/views.go26
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)