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,137 @@
package components
import (
"fmt"
"math"
"time"
"github.com/ryanhamamura/c4/config"
"github.com/ryanhamamura/c4/snake"
"github.com/starfederation/datastar-go/datastar"
)
templ StatusBanner(sg *snake.SnakeGame, mySlot int, gameID string) {
<div id="snake-status">
switch sg.Status {
case snake.StatusWaitingForPlayers:
if sg.Mode == snake.ModeSinglePlayer {
<div class="alert bg-base-200 text-xl font-bold">Ready?</div>
} else {
<div class="alert bg-base-200 text-xl font-bold">Waiting for players...</div>
}
case snake.StatusCountdown:
{{ remaining := time.Until(sg.CountdownEnd) }}
{{ secs := int(math.Ceil(remaining.Seconds())) }}
if secs < 0 {
{{ secs = 0 }}
}
<div class="alert alert-info text-xl font-bold">
{ fmt.Sprintf("Starting in %d...", secs) }
</div>
case snake.StatusInProgress:
if sg.State != nil && mySlot >= 0 && mySlot < len(sg.State.Snakes) && sg.State.Snakes[mySlot] != nil && !sg.State.Snakes[mySlot].Alive {
<div class="alert alert-error text-xl font-bold">You're out!</div>
} else if sg.Mode == snake.ModeSinglePlayer {
<div class="alert alert-success text-xl font-bold">
{ fmt.Sprintf("Score: %d", sg.Score) }
</div>
} else {
<div class="alert alert-success text-xl font-bold">Go!</div>
}
case snake.StatusFinished:
@finishedBanner(sg, mySlot, gameID)
}
</div>
}
templ finishedBanner(sg *snake.SnakeGame, mySlot int, gameID string) {
if sg.Mode == snake.ModeSinglePlayer {
<div class="alert alert-info text-xl font-bold">
{ fmt.Sprintf("Game Over! Score: %d", sg.Score) }
@rematchOrJoin(sg, gameID)
</div>
} else if sg.Winner != nil {
if sg.Winner.Slot == mySlot {
<div class="alert alert-success text-xl font-bold">
You win!
@rematchOrJoin(sg, gameID)
</div>
} else {
<div class="alert alert-error text-xl font-bold">
{ sg.Winner.Nickname + " wins!" }
@rematchOrJoin(sg, gameID)
</div>
}
} else {
<div class="alert alert-warning text-xl font-bold">
It's a draw!
@rematchOrJoin(sg, gameID)
</div>
}
}
templ rematchOrJoin(sg *snake.SnakeGame, gameID string) {
if sg.RematchGameID != nil {
<a class="btn btn-sm bg-white text-gray-800 border-none ml-4" href={ templ.SafeURL("/snake/" + *sg.RematchGameID) }>
Join Rematch
</a>
} else {
<button
class="btn btn-sm bg-white text-gray-800 border-none ml-4"
type="button"
data-on:click={ datastar.PostSSE("/snake/%s/rematch", gameID) }
>
Play again
</button>
}
}
templ PlayerList(sg *snake.SnakeGame, mySlot int) {
<div id="snake-players" class="flex flex-wrap gap-4 mb-2">
for i, p := range sg.Players {
if p != nil {
<div class="flex items-center gap-2">
<span style={ fmt.Sprintf("width:16px;height:16px;border-radius:50%%;background:%s;display:inline-block;", snakeColor(i)) }></span>
<span>
{ p.Nickname }
if i == mySlot {
{ " (You)" }
}
</span>
if sg.Status == snake.StatusInProgress || sg.Status == snake.StatusFinished {
if sg.State != nil && i < len(sg.State.Snakes) && sg.State.Snakes[i] != nil {
if sg.State.Snakes[i].Alive {
<span class="text-sm opacity-60">
{ fmt.Sprintf("(%d)", len(sg.State.Snakes[i].Body)) }
</span>
} else {
<span class="text-sm opacity-40">(dead)</span>
}
}
}
</div>
}
}
</div>
}
templ InviteLink(gameID string) {
{{ fullURL := config.Global.AppURL + "/snake/" + gameID }}
<div id="snake-invite" class="mt-4 text-center">
<p>Share this link to invite players:</p>
<div class="bg-base-200 p-4 rounded-lg font-mono break-all my-2">
{ fullURL }
</div>
<button
class="btn btn-sm mt-2"
type="button"
onclick={ copyToClipboard(fullURL) }
>
Copy Link
</button>
</div>
}
script copyToClipboard(url string) {
navigator.clipboard.writeText(url)
}