chore(frontend): shrink panel controls, configure Netlify deploy
- Shrink category cards and panel buttons for wider code view - Add netlify.toml, document backend URL/port setup for Netlify deploys
This commit is contained in:
parent
7a6df57ad0
commit
faeede3447
5 changed files with 75 additions and 13 deletions
23
README.md
23
README.md
|
|
@ -49,6 +49,29 @@ WebSocket server to it. Matchmaking and room/session logic are not
|
||||||
implemented yet — see `backend/src/matchmaking/README.md` and
|
implemented yet — see `backend/src/matchmaking/README.md` and
|
||||||
`backend/src/rooms/README.md`.
|
`backend/src/rooms/README.md`.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
The frontend (static Vite build) and backend (persistent Node
|
||||||
|
HTTP+WebSocket process) deploy independently and to different kinds of
|
||||||
|
host.
|
||||||
|
|
||||||
|
**Frontend on Netlify:** the root `netlify.toml` builds from
|
||||||
|
`frontend/` (`npm run build`, publish `frontend/dist`). Point this site
|
||||||
|
at your deployed backend by setting `VITE_WS_URL` under Site settings
|
||||||
|
-> Environment variables (or `netlify env:set VITE_WS_URL
|
||||||
|
wss://your-backend-host`) — it's read at build time by
|
||||||
|
`frontend/src/net/client.ts`. Use `wss://`, not `ws://`: once the
|
||||||
|
Netlify site is served over https, a plain `ws://` socket is blocked as
|
||||||
|
mixed content (the client warns in the console if this is
|
||||||
|
misconfigured). See `frontend/.env.example`.
|
||||||
|
|
||||||
|
**Backend:** Netlify doesn't run long-lived Node processes, so
|
||||||
|
`backend/` needs a real host (Fly.io, Render, Railway, a VPS, ...) that
|
||||||
|
can keep a WebSocket server alive. It listens on `process.env.PORT`
|
||||||
|
(see `backend/src/index.js`), which most such hosts set automatically;
|
||||||
|
override it manually if yours doesn't. Whatever host/port it ends up
|
||||||
|
on, put that address into the frontend's `VITE_WS_URL` above.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Single-player game: playable. Multiplayer: backend is a bare HTTP+WS
|
Single-player game: playable. Multiplayer: backend is a bare HTTP+WS
|
||||||
|
|
|
||||||
|
|
@ -1 +1,13 @@
|
||||||
|
# Multiplayer WebSocket backend URL, read at build time via
|
||||||
|
# `import.meta.env.VITE_WS_URL` (see src/net/client.ts). Defaults to
|
||||||
|
# ws://localhost:8080 for local dev when unset.
|
||||||
|
#
|
||||||
|
# For a Netlify deploy, set this in Site settings -> Environment
|
||||||
|
# variables (or `netlify env:set VITE_WS_URL wss://your-backend-host`)
|
||||||
|
# to the deployed backend's address. Use wss:// (not ws://) once the
|
||||||
|
# Netlify site is served over https, or the browser blocks the
|
||||||
|
# connection as mixed content. The backend's own listen port is
|
||||||
|
# separately configurable via its host's PORT env var — see
|
||||||
|
# backend/src/index.js — and is not set here; it only affects which
|
||||||
|
# port the backend process binds to, not this URL.
|
||||||
VITE_WS_URL=ws://localhost:8080
|
VITE_WS_URL=ws://localhost:8080
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,17 @@ const DEFAULT_WS_URL = 'ws://localhost:8080';
|
||||||
* dev backend. */
|
* dev backend. */
|
||||||
export const WS_URL = import.meta.env.VITE_WS_URL || DEFAULT_WS_URL;
|
export const WS_URL = import.meta.env.VITE_WS_URL || DEFAULT_WS_URL;
|
||||||
|
|
||||||
|
// A page served over https can't open a plain ws:// socket (browsers
|
||||||
|
// block it as mixed content) — this only bites once VITE_WS_URL is
|
||||||
|
// left unset/misconfigured on an https deploy (e.g. Netlify), so warn
|
||||||
|
// loudly instead of failing silently as a closed connection.
|
||||||
|
if (typeof location !== 'undefined' && location.protocol === 'https:' && WS_URL.startsWith('ws://')) {
|
||||||
|
console.warn(
|
||||||
|
`[net] VITE_WS_URL is "${WS_URL}" but this page is served over https — ` +
|
||||||
|
'the browser will block it as mixed content. Set VITE_WS_URL to a wss:// URL.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Lifecycle of the underlying socket. `closed` covers both a clean
|
/** Lifecycle of the underlying socket. `closed` covers both a clean
|
||||||
* close and an error — reconnecting is out of scope here, callers that
|
* close and an error — reconnecting is out of scope here, callers that
|
||||||
* care just need to know the connection is gone. */
|
* care just need to know the connection is gone. */
|
||||||
|
|
|
||||||
|
|
@ -257,12 +257,12 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-col {
|
.panel-col {
|
||||||
width: 330px;
|
width: 260px;
|
||||||
flex: 0 0 330px;
|
flex: 0 0 260px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
|
|
@ -271,7 +271,7 @@ body {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
padding: 2px 6px 2px 2px;
|
padding: 2px 6px 2px 2px;
|
||||||
}
|
}
|
||||||
|
|
@ -280,9 +280,9 @@ body {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
.panel-actions .btn { width: 100%; }
|
.panel-actions .btn { width: 100%; padding: 7px 10px; font-size: 9px; }
|
||||||
/* `game/multiplayerMatch.ts`'s mutual reveal-vote status line, shown
|
/* `game/multiplayerMatch.ts`'s mutual reveal-vote status line, shown
|
||||||
* below the Reveal button once the local player has voted to reveal
|
* below the Reveal button once the local player has voted to reveal
|
||||||
* early and is waiting on the opponent to do the same (or vice versa). */
|
* early and is waiting on the opponent to do the same (or vice versa). */
|
||||||
|
|
@ -361,10 +361,10 @@ body {
|
||||||
.cat-card {
|
.cat-card {
|
||||||
background: var(--panel-2);
|
background: var(--panel-2);
|
||||||
border: 3px solid var(--ink);
|
border: 3px solid var(--ink);
|
||||||
padding: 8px;
|
padding: 5px 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: 3px 3px 0 var(--ink);
|
box-shadow: 3px 3px 0 var(--ink);
|
||||||
transition: transform .08s ease, box-shadow .08s ease;
|
transition: transform .08s ease, box-shadow .08s ease;
|
||||||
|
|
@ -386,7 +386,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cat-swatch {
|
.cat-swatch {
|
||||||
width: 24px; height: 24px;
|
width: 16px; height: 16px;
|
||||||
border: 2px solid var(--ink);
|
border: 2px solid var(--ink);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: repeating-conic-gradient(#4b4540 0% 25%, #302f2a 0% 50%) 0 0/8px 8px;
|
background: repeating-conic-gradient(#4b4540 0% 25%, #302f2a 0% 50%) 0 0/8px 8px;
|
||||||
|
|
@ -401,15 +401,15 @@ body {
|
||||||
|
|
||||||
.cat-info { min-width: 0; }
|
.cat-info { min-width: 0; }
|
||||||
.cat-label {
|
.cat-label {
|
||||||
font-size: 8px;
|
font-size: 7px;
|
||||||
line-height: 1.5;
|
line-height: 1.4;
|
||||||
font-family: var(--pixel);
|
font-family: var(--pixel);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
.cat-count { font-size: 10px; color: var(--fg-dim); }
|
.cat-count { font-size: 9px; color: var(--fg-dim); }
|
||||||
|
|
||||||
/* ---------- popover ---------- */
|
/* ---------- popover ---------- */
|
||||||
|
|
||||||
|
|
|
||||||
16
netlify.toml
Normal file
16
netlify.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[build]
|
||||||
|
base = "frontend"
|
||||||
|
command = "npm run build"
|
||||||
|
publish = "dist"
|
||||||
|
|
||||||
|
[build.environment]
|
||||||
|
NODE_VERSION = "20"
|
||||||
|
|
||||||
|
# The multiplayer WebSocket backend (backend/) is a persistent Node
|
||||||
|
# process — it does not run on Netlify. Deploy it separately (Fly.io,
|
||||||
|
# Render, Railway, a VPS, ...) and point this site at it by setting
|
||||||
|
# VITE_WS_URL in Site settings -> Environment variables (Netlify
|
||||||
|
# dashboard) or via `netlify env:set VITE_WS_URL wss://your-backend-host`.
|
||||||
|
# It must be `wss://` (not `ws://`) once this site is served over
|
||||||
|
# https, or browsers block the connection as mixed content. See
|
||||||
|
# frontend/.env.example and frontend/src/net/client.ts.
|
||||||
Loading…
Reference in a new issue