chore: gitignore generated _templ.go files, track .templ sources
Some checks failed
CI / Deploy / test (pull_request) Failing after 13s
CI / Deploy / lint (pull_request) Failing after 24s
CI / Deploy / deploy (pull_request) Has been skipped

Generated _templ.go files are deterministic output from .templ sources,
same as output.css from input.css. Remove them from version control to
reduce diff noise and merge conflicts. Add build:templ and live:templ
tasks to the Taskfile so generation happens as part of the build.
This commit is contained in:
Ryan Hamamura
2026-03-02 15:27:38 -10:00
parent 4f1ee11fa3
commit 2bea5bb489
27 changed files with 1044 additions and 3236 deletions

View File

@@ -0,0 +1,109 @@
package components
import (
"fmt"
"time"
"github.com/ryanhamamura/c4/game"
"github.com/starfederation/datastar-go/datastar"
)
templ GameList(games []GameListItem) {
if len(games) > 0 {
<div class="mt-8 text-left">
<h3 class="mb-4 text-center text-lg font-bold">Your Games</h3>
<div class="flex flex-col gap-2">
for _, g := range games {
@gameListEntry(g)
}
</div>
</div>
}
}
templ gameListEntry(g GameListItem) {
<div class="flex items-center gap-2 p-2 bg-base-200 rounded-lg transition-colors hover:bg-base-300">
<a
href={ templ.SafeURL("/games/" + g.ID) }
class="flex-1 flex justify-between items-center px-2 py-1 no-underline text-base-content"
>
<div class="flex flex-col gap-1">
<span class="font-bold">{ opponentDisplay(g) }</span>
<span class={ statusClass(g) }>{ statusText(g) }</span>
</div>
<div>
<span class="text-xs opacity-60">{ formatTimeAgo(g.LastPlayed) }</span>
</div>
</a>
<button
type="button"
class="btn btn-ghost btn-sm btn-square hover:btn-error"
data-on:click={ datastar.DeleteSSE("/games/%s", g.ID) }
>
&times;
</button>
</div>
}
func statusText(g GameListItem) string {
switch game.GameStatus(g.Status) {
case game.StatusWaitingForPlayer:
return "Waiting for opponent"
case game.StatusInProgress:
if g.IsMyTurn {
return "Your turn!"
}
return "Opponent's turn"
}
return ""
}
func statusClass(g GameListItem) string {
switch game.GameStatus(g.Status) {
case game.StatusWaitingForPlayer:
return "text-sm opacity-60"
case game.StatusInProgress:
if g.IsMyTurn {
return "text-sm text-success font-bold"
}
return "text-sm"
}
return ""
}
func opponentDisplay(g GameListItem) string {
if g.OpponentName == "" {
return "Waiting for opponent..."
}
return "vs " + g.OpponentName
}
func formatTimeAgo(t time.Time) string {
if t.IsZero() {
return ""
}
duration := time.Since(t)
if duration < time.Minute {
return "just now"
}
if duration < time.Hour {
mins := int(duration.Minutes())
if mins == 1 {
return "1 minute ago"
}
return fmt.Sprintf("%d minutes ago", mins)
}
if duration < 24*time.Hour {
hours := int(duration.Hours())
if hours == 1 {
return "1 hour ago"
}
return fmt.Sprintf("%d hours ago", hours)
}
days := int(duration.Hours() / 24)
if days == 1 {
return "yesterday"
}
return fmt.Sprintf("%d days ago", days)
}

View File

@@ -1,239 +0,0 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1001
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"fmt"
"time"
"github.com/ryanhamamura/c4/game"
"github.com/starfederation/datastar-go/datastar"
)
func GameList(games []GameListItem) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if len(games) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"mt-8 text-left\"><h3 class=\"mb-4 text-center text-lg font-bold\">Your Games</h3><div class=\"flex flex-col gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, g := range games {
templ_7745c5c3_Err = gameListEntry(g).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
func gameListEntry(g GameListItem) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"flex items-center gap-2 p-2 bg-base-200 rounded-lg transition-colors hover:bg-base-300\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 templ.SafeURL
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/games/" + g.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/components/gamelist.templ`, Line: 27, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" class=\"flex-1 flex justify-between items-center px-2 py-1 no-underline text-base-content\"><div class=\"flex flex-col gap-1\"><span class=\"font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(opponentDisplay(g))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/components/gamelist.templ`, Line: 31, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 = []any{statusClass(g)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<span class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var5).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/components/gamelist.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(statusText(g))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/components/gamelist.templ`, Line: 32, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</span></div><div><span class=\"text-xs opacity-60\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(formatTimeAgo(g.LastPlayed))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/components/gamelist.templ`, Line: 35, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span></div></a> <button type=\"button\" class=\"btn btn-ghost btn-sm btn-square hover:btn-error\" data-on:click=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(datastar.DeleteSSE("/games/%s", g.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/components/gamelist.templ`, Line: 41, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\">&times;</button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func statusText(g GameListItem) string {
switch game.GameStatus(g.Status) {
case game.StatusWaitingForPlayer:
return "Waiting for opponent"
case game.StatusInProgress:
if g.IsMyTurn {
return "Your turn!"
}
return "Opponent's turn"
}
return ""
}
func statusClass(g GameListItem) string {
switch game.GameStatus(g.Status) {
case game.StatusWaitingForPlayer:
return "text-sm opacity-60"
case game.StatusInProgress:
if g.IsMyTurn {
return "text-sm text-success font-bold"
}
return "text-sm"
}
return ""
}
func opponentDisplay(g GameListItem) string {
if g.OpponentName == "" {
return "Waiting for opponent..."
}
return "vs " + g.OpponentName
}
func formatTimeAgo(t time.Time) string {
if t.IsZero() {
return ""
}
duration := time.Since(t)
if duration < time.Minute {
return "just now"
}
if duration < time.Hour {
mins := int(duration.Minutes())
if mins == 1 {
return "1 minute ago"
}
return fmt.Sprintf("%d minutes ago", mins)
}
if duration < 24*time.Hour {
hours := int(duration.Hours())
if hours == 1 {
return "1 hour ago"
}
return fmt.Sprintf("%d hours ago", hours)
}
days := int(duration.Hours() / 24)
if days == 1 {
return "yesterday"
}
return fmt.Sprintf("%d days ago", days)
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,171 @@
package pages
import (
"fmt"
"github.com/ryanhamamura/c4/features/common/components"
"github.com/ryanhamamura/c4/features/common/layouts"
lobbycomponents "github.com/ryanhamamura/c4/features/lobby/components"
"github.com/ryanhamamura/c4/snake"
"github.com/starfederation/datastar-go/datastar"
)
templ LobbyPage(data LobbyData) {
@layouts.Base("Game Lobby") {
<main
class="max-w-md mx-auto mt-8 text-center"
data-signals="{activeTab: 'connect4', nickname: '', selectedSpeed: 1}"
>
// Auth header
if data.IsLoggedIn {
<div class="flex justify-center items-center gap-4 mb-4 p-2 bg-base-200 rounded-lg">
<span>Logged in as <strong>{ data.Username }</strong></span>
<button
type="button"
class="btn btn-ghost btn-sm"
data-on:click={ datastar.PostSSE("/logout") }
>
Logout
</button>
</div>
} else {
<div class="alert text-sm mb-4">
Playing as guest.
<a class="link" href="/login">Login</a>
or
<a class="link" href="/register">Register</a>
to save your games.
</div>
}
// Title
<h1 class="text-3xl font-bold mb-4">
@components.StealthTitle("")
</h1>
// Tab buttons
<div class="tabs tabs-box mb-6 justify-center">
<button
class="tab"
type="button"
data-class="{'tab-active': $activeTab==='connect4'}"
data-on:click="$activeTab='connect4'"
>
@components.StealthTitle("")
</button>
<button
class="tab"
type="button"
data-class="{'tab-active': $activeTab==='snake'}"
data-on:click="$activeTab='snake'"
>
~~~~
</button>
</div>
// Connect4 tab
<div data-show="$activeTab==='connect4'">
<p class="mb-4">Start a new session</p>
<form>
<fieldset class="fieldset">
<label class="label" for="nickname">Your Nickname</label>
<input
class="input input-bordered w-full"
id="nickname"
type="text"
placeholder="Enter your nickname"
data-bind="nickname"
required
data-on:keydown.enter={ datastar.PostSSE("/games") }
/>
</fieldset>
<button
class="btn btn-primary w-full"
type="button"
data-on:click={ datastar.PostSSE("/games") }
>
Create Game
</button>
</form>
@lobbycomponents.GameList(data.UserGames)
</div>
// Snake tab
<div data-show="$activeTab==='snake'">
// Nickname
<div class="mb-4">
<fieldset class="fieldset">
<label class="label" for="snake-nickname">Your Nickname</label>
<input
class="input input-bordered w-full"
id="snake-nickname"
type="text"
placeholder="Enter your nickname"
data-bind="nickname"
required
/>
</fieldset>
</div>
// Speed selector
<div class="mb-4">
<label class="label">Speed</label>
<div class="btn-group">
for i, preset := range snake.SpeedPresets {
<button
class="btn btn-sm"
type="button"
data-class={ fmt.Sprintf("{'btn-active': $selectedSpeed===%d}", i) }
data-on:click={ fmt.Sprintf("$selectedSpeed=%d", i) }
>
{ preset.Name }
</button>
}
</div>
</div>
// Solo play
<div class="mb-6">
<h3 class="text-lg font-bold mb-2">Play Solo</h3>
<div class="flex gap-2 justify-center flex-wrap">
for i, preset := range snake.GridPresets {
<button
class="btn btn-secondary"
type="button"
data-on:click={ datastar.PostSSE("/snake?mode=solo&preset=%d", i) }
>
{ fmt.Sprintf("%s (%d\u00d7%d)", preset.Name, preset.Width, preset.Height) }
</button>
}
</div>
</div>
// Multiplayer
<div class="mb-6">
<h3 class="text-lg font-bold mb-2">Create Multiplayer Game</h3>
<div class="flex gap-2 justify-center flex-wrap">
for i, preset := range snake.GridPresets {
<button
class="btn btn-primary"
type="button"
data-on:click={ datastar.PostSSE("/snake?mode=multi&preset=%d", i) }
>
{ fmt.Sprintf("%s (%d\u00d7%d)", preset.Name, preset.Width, preset.Height) }
</button>
}
</div>
</div>
// Active snake games
if len(data.ActiveSnakeGames) > 0 {
<div class="mt-6">
<h3 class="text-lg font-bold mb-2 text-center">Join a Game</h3>
<div class="flex flex-col gap-2">
for _, g := range data.ActiveSnakeGames {
<a
href={ templ.SafeURL("/snake/" + g.ID) }
class="flex justify-between items-center p-3 bg-base-200 rounded-lg hover:bg-base-300 no-underline text-base-content"
>
<span>{ fmt.Sprintf("%d\u00d7%d \u2014 %d/8 players", g.Width, g.Height, g.PlayerCount) }</span>
<span class="text-sm opacity-60">{ g.StatusLabel }</span>
</a>
}
</div>
</div>
}
</div>
</main>
}
}

View File

@@ -1,339 +0,0 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1001
package pages
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"fmt"
"github.com/ryanhamamura/c4/features/common/components"
"github.com/ryanhamamura/c4/features/common/layouts"
lobbycomponents "github.com/ryanhamamura/c4/features/lobby/components"
"github.com/ryanhamamura/c4/snake"
"github.com/starfederation/datastar-go/datastar"
)
func LobbyPage(data LobbyData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<main class=\"max-w-md mx-auto mt-8 text-center\" data-signals=\"{activeTab: 'connect4', nickname: '', selectedSpeed: 1}\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.IsLoggedIn {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"flex justify-center items-center gap-4 mb-4 p-2 bg-base-200 rounded-lg\"><span>Logged in as <strong>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 22, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</strong></span> <button type=\"button\" class=\"btn btn-ghost btn-sm\" data-on:click=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(datastar.PostSSE("/logout"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 26, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">Logout</button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"alert text-sm mb-4\">Playing as guest. <a class=\"link\" href=\"/login\">Login</a> or <a class=\"link\" href=\"/register\">Register</a> to save your games.</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<h1 class=\"text-3xl font-bold mb-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = components.StealthTitle("").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</h1><div class=\"tabs tabs-box mb-6 justify-center\"><button class=\"tab\" type=\"button\" data-class=\"{'tab-active': $activeTab==='connect4'}\" data-on:click=\"$activeTab='connect4'\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = components.StealthTitle("").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</button> <button class=\"tab\" type=\"button\" data-class=\"{'tab-active': $activeTab==='snake'}\" data-on:click=\"$activeTab='snake'\">~~~~</button></div><div data-show=\"$activeTab==='connect4'\"><p class=\"mb-4\">Start a new session</p><form><fieldset class=\"fieldset\"><label class=\"label\" for=\"nickname\">Your Nickname</label> <input class=\"input input-bordered w-full\" id=\"nickname\" type=\"text\" placeholder=\"Enter your nickname\" data-bind=\"nickname\" required data-on:keydown.enter=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(datastar.PostSSE("/games"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 76, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"></fieldset><button class=\"btn btn-primary w-full\" type=\"button\" data-on:click=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(datastar.PostSSE("/games"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 82, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\">Create Game</button></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = lobbycomponents.GameList(data.UserGames).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div><div data-show=\"$activeTab==='snake'\"><div class=\"mb-4\"><fieldset class=\"fieldset\"><label class=\"label\" for=\"snake-nickname\">Your Nickname</label> <input class=\"input input-bordered w-full\" id=\"snake-nickname\" type=\"text\" placeholder=\"Enter your nickname\" data-bind=\"nickname\" required></fieldset></div><div class=\"mb-4\"><label class=\"label\">Speed</label><div class=\"btn-group\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i, preset := range snake.SpeedPresets {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<button class=\"btn btn-sm\" type=\"button\" data-class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("{'btn-active': $selectedSpeed===%d}", i))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 113, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" data-on:click=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("$selectedSpeed=%d", i))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 114, Col: 59}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(preset.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 116, Col: 21}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div><div class=\"mb-6\"><h3 class=\"text-lg font-bold mb-2\">Play Solo</h3><div class=\"flex gap-2 justify-center flex-wrap\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i, preset := range snake.GridPresets {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<button class=\"btn btn-secondary\" type=\"button\" data-on:click=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(datastar.PostSSE("/snake?mode=solo&preset=%d", i))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 129, Col: 73}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s (%d\u00d7%d)", preset.Name, preset.Width, preset.Height))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 131, Col: 82}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div></div><div class=\"mb-6\"><h3 class=\"text-lg font-bold mb-2\">Create Multiplayer Game</h3><div class=\"flex gap-2 justify-center flex-wrap\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i, preset := range snake.GridPresets {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<button class=\"btn btn-primary\" type=\"button\" data-on:click=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(datastar.PostSSE("/snake?mode=multi&preset=%d", i))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 144, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s (%d\u00d7%d)", preset.Name, preset.Width, preset.Height))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 146, Col: 82}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(data.ActiveSnakeGames) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"mt-6\"><h3 class=\"text-lg font-bold mb-2 text-center\">Join a Game</h3><div class=\"flex flex-col gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, g := range data.ActiveSnakeGames {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 templ.SafeURL
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/snake/" + g.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 158, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"flex justify-between items-center p-3 bg-base-200 rounded-lg hover:bg-base-300 no-underline text-base-content\"><span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d\u00d7%d \u2014 %d/8 players", g.Width, g.Height, g.PlayerCount))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 161, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</span> <span class=\"text-sm opacity-60\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(g.StatusLabel)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/lobby/pages/lobby.templ`, Line: 162, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</span></a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</div></main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = layouts.Base("Game Lobby").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate