diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 2fbba1f..65e5268 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -4,18 +4,37 @@ import Join from './pages/Join.svelte'; import Dashboard from './pages/Dashboard.svelte'; import Admin from './pages/Admin.svelte'; - import Rules from './pages/Rules.svelte'; const routes = { '/': Home, '/game/:id/join': Join, '/game/:id/player/:pid': Dashboard, '/game/:id/admin': Admin, - '/rules': Rules, };
- 📖 Rules + 📖 Rules
+ + diff --git a/frontend/src/app.css b/frontend/src/app.css index 622e989..1e0e604 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -9,4 +9,3 @@ @import './assets/css/card.css'; @import './assets/css/upkeep.css'; @import './assets/css/admin.css'; -@import './assets/css/rules.css'; diff --git a/frontend/src/assets/css/rules.css b/frontend/src/assets/css/rules.css deleted file mode 100644 index e66b2a7..0000000 --- a/frontend/src/assets/css/rules.css +++ /dev/null @@ -1,242 +0,0 @@ -/* --- Rules Page --- */ -.rules-page { - max-width: 860px; - margin: 0 auto; - padding: 2rem 1rem 4rem; - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.rules-page .glass-panel { - padding: 1.5rem 2rem; -} - -.rules-header { - text-align: center; -} - -.rules-header h1 { - font-family: var(--font-heading); - color: var(--gold); - font-size: 2.4rem; - margin-bottom: 0.25rem; -} - -.rules-subtitle { - color: var(--text-primary); - font-size: 1.15rem; - margin-bottom: 0.75rem; -} - -.rules-credits { - color: var(--text-muted); - font-size: 0.85rem; - margin-bottom: 0.5rem; -} - -.rules-credits a { - color: var(--neon-cyan); -} - -.playtest-note { - margin-top: 0.75rem; - padding: 0.5rem 1rem; - border: 1px dashed var(--glass-border); - border-radius: 8px; - color: var(--gold); - font-size: 0.9rem; -} - -.rules-toc h2 { - font-family: var(--font-heading); - color: var(--gold); - margin-bottom: 0.75rem; -} - -.rules-toc ul { - list-style: none; - padding-left: 0; -} - -.rules-toc ul ul { - padding-left: 1.5rem; -} - -.rules-toc li { - margin: 0.2rem 0; -} - -.rules-toc button { - background: none; - border: none; - color: var(--neon-cyan); - cursor: pointer; - font-family: var(--font-body); - font-size: 1rem; - padding: 0; - text-align: left; -} - -.rules-toc button:hover { - text-decoration: underline; -} - -.rules-body { - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.rules-body section { - scroll-margin-top: 1rem; -} - -.rules-body h2 { - font-family: var(--font-heading); - color: var(--gold); - font-size: 1.8rem; - margin-bottom: 1rem; - border-bottom: 1px solid var(--glass-border); - padding-bottom: 0.5rem; -} - -.rules-body h3 { - font-family: var(--font-heading); - color: var(--text-primary); - font-size: 1.35rem; - margin: 1.5rem 0 0.75rem; - scroll-margin-top: 1rem; -} - -.rules-body h4 { - color: var(--gold); - font-size: 1.1rem; - margin: 1.25rem 0 0.5rem; -} - -.rules-body h5 { - color: var(--text-primary); - font-size: 1rem; - margin: 1rem 0 0.5rem; -} - -.objective-note { - color: var(--text-muted); - font-weight: normal; - font-style: italic; - font-size: 0.9em; -} - -.rules-body p { - margin-bottom: 0.75rem; -} - -.rules-body ul, -.rules-body ol { - margin: 0 0 0.75rem 1.5rem; -} - -.rules-body li { - margin-bottom: 0.35rem; -} - -.rules-body .aside { - color: var(--text-muted); - font-style: italic; - font-size: 0.95em; -} - -.technique-examples { - list-style: none; - margin-left: 0.5rem; -} - -.rules-body table { - width: 100%; - border-collapse: collapse; - margin: 0.75rem 0 1rem; - background: rgba(7, 11, 18, 0.5); - border: 1px solid var(--glass-border); - border-radius: 8px; -} - -.rules-body th, -.rules-body td { - padding: 0.5rem 0.75rem; - border-bottom: 1px solid rgba(212, 175, 55, 0.1); - text-align: left; - vertical-align: top; -} - -.rules-body thead th { - color: var(--gold); - font-family: var(--font-heading); -} - -.obstacle-chart th { - width: 2.5rem; - text-align: center; - color: var(--gold); - font-family: var(--font-heading); -} - -.suit-ways th { - width: 7.5rem; - white-space: nowrap; -} - -.suit-red { - color: var(--red-suit); -} - -.suit-black { - color: var(--black-suit); -} - -.rules-body details { - border: 1px solid var(--glass-border); - border-radius: 8px; - padding: 0.75rem 1rem; -} - -.rules-body details summary { - cursor: pointer; - color: var(--neon-cyan); - font-weight: 600; - padding: 0.25rem 0; -} - -.rules-body details[open] summary { - margin-bottom: 0.75rem; -} - -.signature { - color: var(--gold); - font-style: italic; -} - -.back-to-top { - text-align: center; -} - -/* --- Always-available rules corner link (rendered by App.svelte) --- */ -.rules-corner-link { - position: fixed; - top: 12px; - left: 16px; - z-index: 1001; - padding: 6px 14px; - border-radius: 16px; - background: rgba(10, 15, 25, 0.9); - border: 1px solid var(--glass-border-focus); - color: var(--neon-cyan); - font-size: 0.85rem; - font-weight: 700; - text-decoration: none; - font-family: var(--font-heading); -} - -.rules-corner-link:hover { - background: rgba(0, 242, 254, 0.1); -} diff --git a/frontend/src/pages/Home.svelte b/frontend/src/pages/Home.svelte index fd20c11..a261024 100644 --- a/frontend/src/pages/Home.svelte +++ b/frontend/src/pages/Home.svelte @@ -56,6 +56,6 @@ diff --git a/frontend/vite.config.js b/frontend/vite.config.js index e677226..50f8725 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -9,6 +9,10 @@ export default defineConfig({ '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true, + }, + '/rules': { + target: 'http://127.0.0.1:8000', + changeOrigin: true, } } } diff --git a/pyproject.toml b/pyproject.toml index 611fc71..883fbdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ pirats = "pirats.main:main" where = ["src"] [tool.setuptools.package-data] -pirats = ["static/**/*"] +pirats = ["static/**/*", "rules.html"] [tool.pytest.ini_options] pythonpath = ["src"] diff --git a/src/pirats/main.py b/src/pirats/main.py index ae71cbb..ea5ab44 100644 --- a/src/pirats/main.py +++ b/src/pirats/main.py @@ -1,6 +1,7 @@ from contextlib import asynccontextmanager from typing import Optional from fastapi import FastAPI, Form, Depends, HTTPException, APIRouter +from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from sqlmodel import Session import uvicorn @@ -105,6 +106,11 @@ api.include_router(admin_router) # Mount API at /api app.include_router(api, prefix="/api") +# Static rulebook page (registered before the SPA mount so it takes precedence) +@app.get("/rules", include_in_schema=False) +def rules_page(): + return FileResponse(BASE_DIR / "rules.html", media_type="text/html") + # Mount SPA static_dir = BASE_DIR / "static" if os.path.exists(static_dir): diff --git a/frontend/src/pages/Rules.svelte b/src/pirats/rules.html similarity index 94% rename from frontend/src/pages/Rules.svelte rename to src/pirats/rules.html index f15db2a..b531717 100644 --- a/frontend/src/pages/Rules.svelte +++ b/src/pirats/rules.html @@ -1,11 +1,186 @@ - + + + + + +Rats with Gats — Rulebook + + +

Rats with Gats

@@ -25,30 +200,30 @@ @@ -1122,6 +1297,8 @@

— Nick

-

+

⬆ Back to top

+ +