phase 0.3
This commit is contained in:
commit
4a94bfc4e7
52 changed files with 11653 additions and 0 deletions
38
cmd/admin/main.go
Normal file
38
cmd/admin/main.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"skyrama-tui/internal/adminui"
|
||||
"skyrama-tui/internal/db"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dsn := flag.String("dsn", "",
|
||||
"MySQL DSN. Overrides SKYRAMA_DSN env var.\n"+
|
||||
" Format: user:pass@tcp(host:port)/dbname?parseTime=true")
|
||||
flag.Parse()
|
||||
|
||||
if *dsn == "" {
|
||||
*dsn = os.Getenv("SKYRAMA_DSN")
|
||||
}
|
||||
if *dsn == "" {
|
||||
*dsn = "skyramaUser:Skyrama1234.@tcp(127.0.0.1:3306)/skyrama_clone?parseTime=true"
|
||||
}
|
||||
|
||||
client, err := db.NewAdminClient(*dsn)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cannot connect to database: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
p := tea.NewProgram(adminui.NewApp(client), tea.WithAltScreen())
|
||||
if _, err := p.Run(); err != nil {
|
||||
log.Fatalf("fatal: %v", err)
|
||||
}
|
||||
}
|
||||
159
cmd/skyrama/main.go
Normal file
159
cmd/skyrama/main.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
"skyrama-tui/internal/db"
|
||||
"skyrama-tui/internal/service"
|
||||
"skyrama-tui/internal/ui"
|
||||
)
|
||||
|
||||
// ─── Root model ───────────────────────────────────────────────────────────────
|
||||
|
||||
type screen int
|
||||
|
||||
const (
|
||||
screenLogin screen = iota
|
||||
screenApp
|
||||
)
|
||||
|
||||
type rootModel struct {
|
||||
client *service.GameService
|
||||
screen screen
|
||||
login ui.LoginModel
|
||||
app ui.AppModel
|
||||
winWidth int
|
||||
winHeight int
|
||||
}
|
||||
|
||||
func newRootModel(client *service.GameService) rootModel {
|
||||
return rootModel{
|
||||
client: client,
|
||||
screen: screenLogin,
|
||||
login: ui.NewLoginModel(client),
|
||||
}
|
||||
}
|
||||
|
||||
func (r rootModel) Init() tea.Cmd {
|
||||
return r.login.Init()
|
||||
}
|
||||
|
||||
func (r rootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Always track window size so we can replay it into AppModel on login.
|
||||
if ws, ok := msg.(tea.WindowSizeMsg); ok {
|
||||
r.winWidth, r.winHeight = ws.Width, ws.Height
|
||||
}
|
||||
|
||||
switch r.screen {
|
||||
// ── Login screen ──────────────────────────────────────────────────────
|
||||
case screenLogin:
|
||||
// Handle login result messages at this level so we can transition screens
|
||||
switch m := msg.(type) {
|
||||
case ui.LoginSuccessMsg:
|
||||
// Build the full AppModel and inject sub-models
|
||||
app := ui.NewAppModel(r.client, m.UserID, m.Username)
|
||||
fleet := ui.NewFleetModel(r.client, 0)
|
||||
app.Fleet(&fleet)
|
||||
buildings := ui.NewBuildingsModel(r.client, 0)
|
||||
app.SetBuildings(&buildings)
|
||||
contracts := ui.NewContractsModel(r.client, 0)
|
||||
app.SetContracts(&contracts)
|
||||
shop := ui.NewShopModel(r.client, 0, 1)
|
||||
app.SetShop(&shop)
|
||||
|
||||
r.screen = screenApp
|
||||
r.app = app
|
||||
|
||||
// Replay the window size into AppModel so list viewports are
|
||||
// sized correctly — the initial WindowSizeMsg arrived while
|
||||
// the login screen was active and was never forwarded to AppModel.
|
||||
var cmds []tea.Cmd
|
||||
cmds = append(cmds, r.app.Init())
|
||||
if r.winWidth > 0 {
|
||||
updated, cmd := r.app.Update(tea.WindowSizeMsg{
|
||||
Width: r.winWidth, Height: r.winHeight,
|
||||
})
|
||||
r.app = updated.(ui.AppModel)
|
||||
if cmd != nil {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
return r, tea.Batch(cmds...)
|
||||
|
||||
case ui.LoginErrMsg:
|
||||
// Inject the error then re-run update for cursor blink etc.
|
||||
r.login.SetErr(m.Err.Error())
|
||||
updated, cmd := r.login.Update(msg)
|
||||
r.login = updated
|
||||
return r, cmd
|
||||
|
||||
default:
|
||||
updated, cmd := r.login.Update(msg)
|
||||
r.login = updated
|
||||
return r, cmd
|
||||
}
|
||||
|
||||
// ── Main app ──────────────────────────────────────────────────────────
|
||||
case screenApp:
|
||||
updated, cmd := r.app.Update(msg)
|
||||
r.app = updated.(ui.AppModel)
|
||||
return r, cmd
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r rootModel) View() string {
|
||||
switch r.screen {
|
||||
case screenLogin:
|
||||
return r.login.View()
|
||||
case screenApp:
|
||||
return r.app.View()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ─── Entry point ──────────────────────────────────────────────────────────────
|
||||
|
||||
func main() {
|
||||
dsn := flag.String("dsn", "",
|
||||
"MySQL DSN string. Overrides SKYRAMA_DSN env var.\n"+
|
||||
" Format: user:pass@tcp(host:port)/dbname?parseTime=true")
|
||||
flag.Parse()
|
||||
|
||||
if *dsn == "" {
|
||||
*dsn = os.Getenv("SKYRAMA_DSN")
|
||||
}
|
||||
// Fall back to the same DSN used in db_manager.go
|
||||
if *dsn == "" {
|
||||
*dsn = "skyramaUser:Skyrama1234.@tcp(127.0.0.1:3306)/skyrama_clone?parseTime=true"
|
||||
}
|
||||
|
||||
client, err := db.NewClient(*dsn)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cannot connect to database: %v\n\n", err)
|
||||
fmt.Fprintln(os.Stderr, "Set the connection string via --dsn flag or the SKYRAMA_DSN environment variable.")
|
||||
fmt.Fprintln(os.Stderr, "Example:")
|
||||
fmt.Fprintln(os.Stderr, " SKYRAMA_DSN='skyramaUser:Skyrama1234.@tcp(127.0.0.1:3306)/skyrama_clone?parseTime=true' ./skyrama-tui")
|
||||
os.Exit(1)
|
||||
}
|
||||
// Wrap the data-access client in the business-logic service the UI depends on.
|
||||
// Central ports / factions are seeded out-of-band via sql/seed_factions.sql.
|
||||
svc := service.NewGameService(client)
|
||||
defer svc.Close()
|
||||
|
||||
p := tea.NewProgram(
|
||||
newRootModel(svc),
|
||||
tea.WithAltScreen(), // use alternate screen buffer (hides shell history)
|
||||
tea.WithMouseCellMotion(), // mouse support for scroll
|
||||
)
|
||||
|
||||
if _, err := p.Run(); err != nil {
|
||||
log.Fatalf("fatal: %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue