woggioni 12fa3f5aae
CI / Build and push docker image (push) Failing after 39s
Rebuild the web UI on Bootstrap 5 with the dark felt theme
The lobby was hand-rolled CSS and looked plain. Vendor Bootstrap 5.3
(MIT) under web/vendor so builds stay offline, load it in dark mode and
bridge the felt/gold palette onto Bootstrap's CSS variables in a new
theme.css; style.css now only owns the game table, cards and scoring
summary.

Rebuild the lobby, waiting room and shared chrome with Bootstrap
components: a shared navbar, creation form as cards with schema-driven
controls, roster as a list group with seat/team badges, a seats progress
bar and a copy-to-clipboard button on the join code. History,
leaderboard, connection/error states and the 404 page use cards, tables
and buttons too; the play table itself is unchanged.

The copy feedback timer is cancelled when the waiting room is rebuilt by
another player joining, fixing a disposed-signal panic that froze the
page mid-match.
2026-09-21 23:40:44 +08:00
2026-09-18 19:18:04 +08:00

tavolo

A platform for multiplayer card games. The first game is scopone scientifico — the four-player, fixed-partnership Italian card game:

  • server/ — backend: Python + kaya framework, OIDC login, live games in Redis, match statistics in Postgres. A monorepo of three distributions sharing the tavolo namespace: the game-independent tavolo-platform, the scopone game tavolo-scopone, and the tavolo-app composition root wiring them together. See server/README.md.
  • web/ — frontend: Rust + Sycamore compiled to WebAssembly, built with Trunk and styled with Bootstrap 5.3 (MIT, vendored under web/vendor/ so builds work offline) plus the custom felt theme in web/theme.css. Card images are the CC0 woodcut napoletane deck traced from a 1902 Naples print (SONDLecT/woodcut-napoletane).

Run the whole stack

docker compose up --build

Postgres, Redis, a mock OIDC provider (test users alice, bob, carol, dave), the database migrator and the app all come up together. The app — frontend and API — listens on http://127.0.0.1:8080.

Because both the browser and the app talk to the OIDC issuer at http://mockoauth:8180/tavolo, add a host entry once:

echo "127.0.0.1 mockoauth" | sudo tee -a /etc/hosts

Between hands

When a hand ends but the match is not decided, the game pauses on a scoring summary screen: every player sees how each category was won (carte, denara, settebello, primiera, scope — plus napola when enabled) with the running totals and must click "Understood" before the next hand is dealt. If someone is away the next hand is dealt automatically after HAND_ACK_TIMEOUT_SECONDS (default 30s). The match-ending hand is explained on the final screen.

On your turn

Every turn shows a countdown (TURN_TIMEOUT_SECONDS, default 30s). If a player does not move — disconnected or fallen asleep — the server plays a random legal card for them (randomizing among the legal captures when the rules require a capture), so one absent player cannot stall the table.

Development

Backend (from server/):

cd server
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e ./packages/tavolo-platform -e ./packages/tavolo-scopone -e './packages/tavolo-app[dev]'
.venv/bin/python -m unittest discover -s tests -t .
.venv/bin/python -m unittest discover -s packages/tavolo-platform/tests
.venv/bin/python -m unittest discover -s packages/tavolo-scopone/tests

Frontend (from web/), with the backend running on :8080:

cd web
trunk serve          # SPA on http://localhost:8000, /api /auth /ws proxied

Trunk proxies /api, /auth and /ws to 127.0.0.1:8080 (see web/Trunk.toml). For the login redirect to land back on the dev server, run the backend with:

OIDC_POST_LOGIN_REDIRECT=http://localhost:8000/ \
OIDC_POST_LOGOUT_REDIRECT=http://localhost:8000/ \
  .venv/bin/granian --host 127.0.0.1 --port 8080 tavolo.app:app

Card images are committed under web/assets/cards/; web/fetch-cards.sh re-downloads them if needed.

S
Description
Scopone scientifico multiplayer backend built on the kaya framework
Readme
6 MiB
Languages
Python 73.5%
Rust 23.5%
CSS 1.9%
Dockerfile 0.6%
JavaScript 0.2%
Other 0.3%