Commit Graph

112 Commits

Author SHA1 Message Date
Ryan Hamamura
9a8fe4534d feat: add hashfs for static asset cache busting and live clock
All checks were successful
CI / Deploy / test (pull_request) Successful in 32s
CI / Deploy / lint (pull_request) Successful in 42s
CI / Deploy / deploy (pull_request) Has been skipped
- Add assets package with dev/prod build tags
- Dev: serve from filesystem with Cache-Control: no-store
- Prod: use hashfs for cache-busting URLs
- Add LiveClock component to show SSE connection status
- Update templates to use StaticPath for asset URLs
2026-03-03 13:26:52 -10:00
Ryan Hamamura
e0f5d555fb feat: add status indicator dot to live clock
All checks were successful
CI / Deploy / test (pull_request) Successful in 13s
CI / Deploy / lint (pull_request) Successful in 26s
CI / Deploy / deploy (pull_request) Has been skipped
2026-03-03 12:53:46 -10:00
Ryan Hamamura
155ac2c71a feat: replace connection indicator with live clock
All checks were successful
CI / Deploy / test (pull_request) Successful in 13s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
A ticking clock serves as an implicit connection indicator - if it
stops updating, users know the connection is stale. Simpler and more
useful than a status dot.
2026-03-03 12:52:39 -10:00
Ryan Hamamura
1109dccdd8 fix: remove broken connection indicator
All checks were successful
CI / Deploy / test (pull_request) Successful in 15s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
The ConnectionIndicator component caused PatchElementsNoTargetsFound
errors due to complex nested IDs. Removing it for now - we can design
a better solution later if needed.
2026-03-03 12:47:28 -10:00
Ryan Hamamura
cedcadfe3c feat: re-enable connection indicator for SSE status
All checks were successful
CI / Deploy / test (pull_request) Successful in 15s
CI / Deploy / lint (pull_request) Successful in 24s
CI / Deploy / deploy (pull_request) Has been skipped
Add ConnectionIndicator to Connect 4 game page (Snake already had it).
Both games now patch the indicator on initial connect and every 10s
heartbeat, giving users visual feedback that SSE is connected.
2026-03-03 12:34:13 -10:00
Ryan Hamamura
de78ba6d39 refactor: extract GameService for Snake NATS/chat handling
All checks were successful
CI / Deploy / test (pull_request) Successful in 13s
CI / Deploy / lint (pull_request) Successful in 24s
CI / Deploy / deploy (pull_request) Has been skipped
Apply the same service pattern from Connect 4 to Snake game.
Handlers now receive the service and call its methods instead of
managing NATS connections directly. Also aligns heartbeat to 10s
and removes ConnectionIndicator patching (matching C4 changes).
2026-03-03 12:25:25 -10:00
Ryan Hamamura
8536f8e948 refactor: extract GameService for Connect 4 NATS/chat handling
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Move NATS subscription and chat room management into a dedicated
GameService, following the portigo service pattern. Handlers now
receive the service and call its methods instead of managing
NATS connections directly.
2026-03-03 12:23:25 -10:00
Ryan Hamamura
b2b06a062b fix: align SSE architecture with portigo for reliable connections
All checks were successful
CI / Deploy / test (pull_request) Successful in 35s
CI / Deploy / lint (pull_request) Successful in 45s
CI / Deploy / deploy (pull_request) Has been skipped
- Reorder HandleGameEvents to create NATS subscriptions before SSE
- Use chi's middleware.NewWrapResponseWriter for proper http.Flusher support
- Add slog-zerolog adapter for unified logging
- Add ErrorLog to HTTP server for better error visibility
- Change session Cookie.Secure to false for HTTP support
- Change heartbeat from 15s to 10s
- Remove ConnectionIndicator patching (was causing PatchElementsNoTargetsFound)

The key fix was using chi's response writer wrapper which properly
implements http.Flusher, allowing SSE data to be flushed immediately
instead of being buffered.
2026-03-03 11:57:58 -10:00
Ryan Hamamura
331c4c8759 docs: add AGENTS.md with coding guidelines for AI agents
All checks were successful
CI / Deploy / test (push) Successful in 15s
CI / Deploy / lint (push) Successful in 28s
CI / Deploy / deploy (push) Successful in 1m28s
Includes build/test commands, code style guidelines, naming conventions,
error handling patterns, and Go/templ/Datastar patterns used in this repo.
v0.1.5
2026-03-03 10:53:14 -10:00
f6c5949247 Merge pull request 'Fix connection indicator script duplication on SSE patches' (#12) from fix/connection-indicator-script into main
All checks were successful
CI / Deploy / test (push) Successful in 18s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 1m27s
2026-03-03 20:44:56 +00:00
Ryan Hamamura
d6e64763cc fix: use templ.NewOnceHandle to prevent script duplication on SSE patches
All checks were successful
CI / Deploy / test (pull_request) Successful in 15s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Replace ConnectionIndicatorWithScript wrapper with a single ConnectionIndicator
component that uses templ.NewOnceHandle() to ensure the watcher script is only
rendered once per page, even when the indicator is patched via SSE.
2026-03-03 10:43:23 -10:00
589d1f09e8 Merge pull request 'Refactor connection indicator to patch with timestamp' (#11) from refactor/patch-connection-indicator into main
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 1m22s
2026-03-03 20:32:11 +00:00
Ryan Hamamura
06b3839c3a refactor: patch connection indicator with timestamp
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 26s
CI / Deploy / deploy (pull_request) Has been skipped
Server patches the ConnectionIndicator element with a timestamp on
each heartbeat. Client-side JS checks every second if the timestamp
is stale (>20s) and toggles red/green accordingly.

This properly detects connection loss since the indicator will turn
red if no patches are received.
2026-03-03 10:30:55 -10:00
99f14ca170 Merge pull request 'Add connection status indicator with SSE heartbeat' (#10) from feat/sse-heartbeat into main
All checks were successful
CI / Deploy / test (push) Successful in 15s
CI / Deploy / lint (push) Successful in 27s
CI / Deploy / deploy (push) Successful in 1m23s
2026-03-03 20:15:29 +00:00
Ryan Hamamura
da82f31d46 feat: add connection status indicator with SSE heartbeat
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
- Add ConnectionIndicator component showing green/red dot
- Send lastPing signal every 15 seconds via SSE
- Indicator turns red if no ping received in 20 seconds
- Gives users confidence the live connection is active
2026-03-03 10:05:03 -10:00
ffbff8cca5 Merge pull request 'Simplify chat subscription API' (#9) from refactor/chat-subscribe-messages into main
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 1m25s
2026-03-03 19:54:21 +00:00
Ryan Hamamura
bcb1fa3872 refactor: simplify chat subscription API
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Room.Subscribe() now returns a channel of parsed Message structs
instead of raw NATS messages. The room handles NATS subscription
and message parsing internally, so callers no longer need to call
Receive() separately.
2026-03-03 09:45:56 -10:00
bf9a8755f0 Merge pull request 'Add version display in UI footer' (#8) from feat/version-display into main
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 26s
CI / Deploy / deploy (push) Successful in 1m27s
2026-03-03 19:41:59 +00:00
90ef970d14 Merge pull request 'Fix chat messages not appearing without refresh' (#7) from fix/chat-append-messages into main
Some checks failed
CI / Deploy / lint (push) Has been cancelled
CI / Deploy / deploy (push) Has been cancelled
CI / Deploy / test (push) Has been cancelled
2026-03-03 19:41:52 +00:00
Ryan Hamamura
eb75654403 feat: display app version in UI footer
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
- Add version package with build-time variables
- Inject version via ldflags in Dockerfile using git describe
- Show version in footer on every page
- Log version and commit on server startup
2026-03-03 09:40:23 -10:00
Ryan Hamamura
c52c389f0c Reapply "fix: append chat messages instead of re-rendering entire game"
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 2s
CI / Deploy / deploy (pull_request) Has been skipped
This reverts commit 513467470c.
2026-03-03 09:15:46 -10:00
Ryan Hamamura
513467470c Revert "fix: append chat messages instead of re-rendering entire game"
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 3s
This reverts commit 6976b773bd.
2026-03-03 09:15:42 -10:00
Ryan Hamamura
6976b773bd fix: append chat messages instead of re-rendering entire game
All checks were successful
CI / Deploy / test (push) Successful in 15s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 33s
Previously patchAll() re-rendered the full GameContent on every chat
message, which was inefficient and could cause UI glitches. Now we
append just the new ChatMessage to the chat history element.
2026-03-03 09:09:51 -10:00
Ryan Hamamura
ac2492e7c1 fix: correct volume mount path for database persistence
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 33s
The container runs from / so data/games.db resolves to /data/games.db,
but the volume was mounted at /app/data.
2026-03-03 08:57:11 -10:00
65dc672186 Merge pull request 'Fix SSE live updates being cancelled on user interaction' (#6) from fix/sse-request-cancellation into main
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 2m8s
2026-03-03 18:52:06 +00:00
Ryan Hamamura
1db6b2596e fix: disable SSE request cancellation for live game updates
All checks were successful
CI / Deploy / test (pull_request) Successful in 25s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
The default Datastar requestCancellation:'auto' was causing SSE
connections to be cancelled whenever users interacted with the page
(making moves, sending chat messages, etc.), breaking live updates.
2026-03-03 08:49:35 -10:00
Ryan Hamamura
64b5d384ed fix: use correct Datastar keydown event syntax
All checks were successful
CI / Deploy / test (push) Successful in 15s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 34s
Replace invalid .key_enter and .enter modifiers with evt.key === 'Enter'
guard in the expression, per Datastar docs. Also fix __stop and __throttle
modifier syntax to use double underscores.
2026-03-02 23:05:11 -10:00
Ryan Hamamura
235e4afbe3 revert: remove one-time c4 container cleanup from deploy workflow
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 3s
2026-03-02 22:57:30 -10:00
Ryan Hamamura
649762e6c6 fix: stop old c4 container before starting renamed games container
Some checks failed
CI / Deploy / test (push) Successful in 13s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Has been cancelled
The renamed container can't bind port 8080 while the old c4 container
still holds it.
2026-03-02 22:56:23 -10:00
Ryan Hamamura
8780b7c9b1 fix: run templ generate in Dockerfile before build
Some checks failed
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Failing after 34s
The _templ.go files are gitignored, so the Docker build context doesn't
include them. Generate them before compiling.
2026-03-02 22:53:00 -10:00
d77e4af1e2 Merge pull request 'refactor: extract shared player, session, and chat packages' (#5) from refactor/shared-player-session-chat into main
Some checks failed
CI / Deploy / test (push) Successful in 13s
CI / Deploy / lint (push) Successful in 24s
CI / Deploy / deploy (push) Failing after 1m6s
2026-03-03 08:50:13 +00:00
Ryan Hamamura
718e0c55c9 fix: satisfy staticcheck comment style for exported consts
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
2026-03-02 22:48:16 -10:00
Ryan Hamamura
dcf76bb773 refactor: replace session key strings with consts
Some checks failed
CI / Deploy / test (pull_request) Successful in 13s
CI / Deploy / lint (pull_request) Failing after 20s
CI / Deploy / deploy (pull_request) Has been skipped
Define KeyPlayerID, KeyUserID, and KeyNickname in the sessions package
and use them across all handlers to avoid duplicated magic strings.
2026-03-02 22:40:10 -10:00
Ryan Hamamura
4faf4f73b0 refactor: patch entire game content for snake SSE handler
Some checks failed
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Failing after 20s
CI / Deploy / deploy (pull_request) Has been skipped
Same approach as connect4 — extract GameContent component and patch it
as a single element, letting DOM morphing handle the diff.
2026-03-02 22:34:20 -10:00
Ryan Hamamura
0808c4d972 refactor: patch entire game content instead of individual components
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 21s
CI / Deploy / deploy (pull_request) Has been skipped
Extract GameContent from GamePage so the SSE handler can patch a single
element and let DOM morphing diff the changes, replacing the per-component
sendGameComponents helper.
2026-03-02 21:43:25 -10:00
Ryan Hamamura
42211439c9 refactor: drop redundant WithSelectorID from SSE patches
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 22s
CI / Deploy / deploy (pull_request) Has been skipped
All templ components already have id attributes on their root elements,
which PatchElementTempl uses automatically.
2026-03-02 21:34:46 -10:00
Ryan Hamamura
fb6c0e3d90 refactor: replace hardcoded NATS subjects with typed helpers
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 21s
CI / Deploy / deploy (pull_request) Has been skipped
Add GameSubject/ChatSubject helpers to connect4 and snake packages,
eliminating magic string concatenation from handlers and main.go.
2026-03-02 21:30:47 -10:00
Ryan Hamamura
2cfd42b606 refactor: integrate chat persistence into Room
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Move SaveMessage/LoadMessages logic into Room as private methods.
NewPersistentRoom auto-loads history and auto-saves on Send, removing
the need for handlers to coordinate persistence separately.
2026-03-02 21:25:03 -10:00
Ryan Hamamura
6d43bdea16 refactor: rename remaining c4 references to games
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Update binary name, DB path, session cookie, deploy scripts, systemd
service, Docker config, CI workflow, and .dockerignore. Remove stale
Claude command and settings files.
2026-03-02 21:16:12 -10:00
Ryan Hamamura
c6885a069b refactor: rename Go module from c4 to games
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Rename module path github.com/ryanhamamura/c4 to
github.com/ryanhamamura/games across go.mod, all source files,
and golangci config.
2026-03-02 20:41:20 -10:00
Ryan Hamamura
38eb9ee398 refactor: rename game package to connect4, drop Game prefix from types
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Rename game/ -> connect4/ to avoid c4/game stutter. Drop redundant
Game prefix from exported types (GameStore -> Store, GameInstance ->
Instance, GameStatus -> Status). Rename NATS subjects from game.{id}
to connect4.{id}. URL routes unchanged.
2026-03-02 20:31:00 -10:00
Ryan Hamamura
f71acfc73e fix: use format string for datastar.PostSSE in chat component
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 24s
CI / Deploy / deploy (pull_request) Has been skipped
PostSSE requires a constant format string; pass "%s" with the URL
as an argument instead of passing the URL directly.
2026-03-02 19:47:05 -10:00
Ryan Hamamura
10de5d21ad refactor: extract standalone chat package from game-specific handlers
Some checks failed
CI / Deploy / test (pull_request) Failing after 11s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Create chat/ package with Message type, Room (NATS pub/sub + buffer),
DB persistence helpers, and a unified templ component parameterized by
Config (CSS prefix, post URL, color function, key propagation).

Both c4game and snakegame now use chat.Room for message management and
chatcomponents.Chat for rendering, eliminating the duplicated
ChatMessage types, chat templ components, chatAutoScroll scripts,
color functions, and inline buffer management.
2026-03-02 19:20:21 -10:00
Ryan Hamamura
7eadfbbb0c refactor: extract session helpers for player identity resolution
Add GetPlayerID, GetUserID, GetNickname to the sessions package.
Remove the inline player-ID-from-session pattern duplicated across
every handler in c4game and snakegame, and the local getPlayerID
helper in snakegame.
2026-03-02 19:16:09 -10:00
Ryan Hamamura
063b03ce25 refactor: extract shared player.ID type and GenerateID to player package
Both game and snake packages had identical PlayerID types and the snake
package imported game.GenerateID. Now both use player.ID and
player.GenerateID from the shared player package.
2026-03-02 19:09:01 -10:00
f47eb4cdf3 Merge pull request 'refactor: deduplicate persistence, add upsert queries, throttle snake saves' (#4) from refactor/game-efficiency into main
Some checks failed
CI / Deploy / test (push) Successful in 13s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Failing after 17s
2026-03-03 05:02:04 +00:00
Ryan Hamamura
9a20467438 refactor: add save()/savePlayer() methods on game instances
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Wrap free persistence functions in instance methods for cleaner call
sites (gi.save() instead of saveGame(gi.queries, gi.game)). Methods
log errors via zerolog before returning them.
2026-03-02 18:51:18 -10:00
Ryan Hamamura
cb5458c9fc ci: generate templ files before test and lint steps
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
2026-03-02 18:39:33 -10:00
Ryan Hamamura
bc6488f063 refactor: deduplicate persistence, add upsert queries, throttle snake saves
Some checks failed
CI / Deploy / test (pull_request) Failing after 15s
CI / Deploy / lint (pull_request) Failing after 23s
CI / Deploy / deploy (pull_request) Has been skipped
- Replace Create+Get+Update with UpsertGame/UpsertSnakeGame queries
- Extract free functions (saveGame, loadGame, etc.) from duplicated
  receiver methods on Store and Instance types
- Remove duplicate generateID from snake package, reuse game.GenerateID
- Throttle snake game DB writes to every 2s instead of every tick
- Fix double-lock in c4game chat handler
- Update all code for sqlc pointer types (*string instead of sql.NullString)
2026-03-02 16:56:29 -10:00
9c3f659e96 Merge pull request 'fix: add Enter key handlers to all auth and nickname inputs' (#3) from fix/enter-key-handlers into main
Some checks failed
CI / Deploy / test (push) Failing after 12s
CI / Deploy / lint (push) Failing after 24s
CI / Deploy / deploy (push) Has been skipped
2026-03-03 01:34:21 +00:00