{{/* Shared layout pieces for all CowBull pages. Defines used across page templates must live here (page-local defines are not visible to other pages). */}} {{ define "head" }} CowBull: Word game {{ template "openrun_gen_import" . }} {{ end }} {{/* page is the shared shell; each page file defines "content" and renders this */}} {{ define "page" }} {{ template "head" . }}
{{ template "navbar" . }} {{ template "content" . }} {{ template "footer" . }}
{{ template "name_modal" . }} {{ template "page_end" . }} {{ end }} {{ define "navbar" }} {{ end }} {{ define "player_chip" }} {{ $pname := queryUnescape .Data.PlayerNameEnc }} {{ end }} {{ define "level_picker" }}
{{ end }} {{ define "name_modal" }} {{ $pname := queryUnescape .Data.PlayerNameEnc }} {{ end }} {{ define "footer" }} {{ end }} {{/* page_end renders the toast area; every page puts it right before the closing body tag. Class anchor for the SSE toasts generated by the Go server, keep in sync with router.go: alert shadow-lg bg-base-100 text-sm */}} {{ define "page_end" }} {{ template "confirm_modal" . }}
{{ if not .Data.PlayerId }} {{/* First visit: get a stable player id assigned (cookie set by the container endpoint) */}} {{ end }} {{ end }} {{/* confirm_modal is the styled replacement for the native hx-confirm popup, driven by the htmx:confirm listener in the head script */}} {{ define "confirm_modal" }} {{ end }} {{ define "score_result" }} {{ if .Data.ScoreError }} {{ .Data.ScoreError }} {{ else }}
Score
{{ .Data.Score }}
out of 10
{{ end }} {{ end }} {{ define "error_block" }}
{{ template "icon_alert" "w-4 h-4" }} {{ .Data.Message }}
{{ end }} {{/* player_label renders the player name, with the location appended in the lobby view. Context: dict with Row (board player) and ShowLoc */}} {{ define "player_label" }} {{- .Row.Name -}} {{- if and .ShowLoc .Row.Location (ne .Row.Location "Unknown") }} ({{ .Row.Location }}) {{- end -}} {{ end }} {{/* board_card renders the standings + activity feed for a challenge or tournament. Used by the lobby page and the game page, refreshed over SSE. Context: .Data.Board is the board JSON from the Go API */}} {{ define "board_card" }} {{ $b := .Data.Board }} {{ $showLoc := .Data.ShowLoc }} {{ $rounds := int $b.NumRounds }}

{{ template "icon_medal" "w-5 h-5 text-primary" }} Standings

{{ if not $b.Players }}

No games yet - be the first to play!

{{ else if eq $rounds 1 }}
{{ range $b.Players }} {{ $r := index .Rounds 0 }} {{ if $r }} {{ else }} {{ end }} {{ end }}
Player Guesses Hints Time Score
{{ if .IsLeader }} {{ template "icon_crown" "w-4 h-4 text-primary" }} {{ end }} {{ template "player_label" (dict "Row" . "ShowLoc" $showLoc) }} {{ int $r.Guesses }} {{ int $r.Hints }} {{ $r.Time }} {{ if eq $r.Status "COMPLETED" }} {{ $r.ScoreS }} {{ else if eq $r.Status "RESIGNED" }} {{ template "icon_flag" "w-4 h-4 opacity-60" }} {{ else }} {{ end }} -
{{ else }}
{{ range $k := until $rounds }} {{ end }} {{ range $b.Players }} {{ range .Rounds }} {{ end }} {{ end }}
PlayerR{{ add $k 1 }}Total
{{ if .IsLeader }} {{ template "icon_crown" "w-4 h-4 text-primary" }} {{ end }} {{ template "player_label" (dict "Row" . "ShowLoc" $showLoc) }}
{{ int .CurGuesses }} guesses
{{ int .CurHints }} hints
{{ if . }} {{ if eq .Status "COMPLETED" }} {{ .ScoreS }} {{ else if eq .Status "RESIGNED" }} {{ template "icon_flag" "w-4 h-4 opacity-60" }} {{ else }} {{ end }} {{ else }} · {{ end }} {{ .TotalS }} {{ if .Finished }} Finished {{ else if .DNF }} DNF {{ else }} Playing {{ end }}

Running tally: every finished round adds to the total, even for players who stop early. Highest total wins.

{{ end }} {{ if $b.Events }}

{{ template "icon_megaphone" "w-5 h-5 text-primary" }} Activity

{{ end }}
{{ end }} {{ define "game_main" }} {{ $g := .Data.Game }}
{{ if .Data.Won }} {{ end }}
{{ if .Data.Error }} {{ end }} {{ if eq $g.Status "COMPLETED" }}
{{ template "icon_trophy" "w-14 h-14 text-primary" }}

{{ .Data.Pun }}

{{ if $g.Word }}

The word was {{ upper $g.Word }}

{{ end }}
Score
{{ $g.Score }}
out of 10
Guesses
{{ int $g.GuessCount }}
{{ int $g.HintCount }} hints · {{ $g.Time }}
{{ template "next_step_buttons" . }}
{{ else if eq $g.Status "RESIGNED" }}
{{ template "icon_flag" "w-14 h-14 opacity-60" }}

The word won this time

{{ if $g.Word }}

It was {{ upper $g.Word }}

{{ else }}

The word stays hidden while others are still playing.

{{ end }}
{{ template "next_step_buttons" . }}
{{ else }}
{{ .Data.Nudge }}
{{ end }}
{{ range $g.Clues }} {{ else }} {{ end }}
Word Bulls {{ template "icon_bull" "w-3.5 h-3.5" }} Cows {{ template "icon_cow" "w-4 h-4" }}
{{ upper .Clue }} {{ if .Hint }} hint {{ end }} {{ if eq (int .Bulls) 0 }} {{ else }} {{ range until (int .Bulls) }} {{ template "icon_bull" "w-5 h-5 text-secondary" }} {{ end }} {{ end }} {{ if eq (int .Cows) 0 }} {{ else }} {{ range until (int .Cows) }} {{ template "icon_cow" "w-5 h-5 text-primary" }} {{ end }} {{ end }}
No guesses yet. Any valid 4-letter word gets you clues!
{{ end }} {{ define "next_step_buttons" }} {{ $g := .Data.Game }} {{ if $g.ChallengeId }} {{ if lt (int $g.Round) (int $g.NumRounds) }}
{{ end }} {{ template "icon_medal" "w-4 h-4" }} Standings {{ else }} {{ template "icon_home" "w-4 h-4" }} Home {{ end }} {{ end }}