fix: visual tuning from live preview — diamond position, borders, corners, header z-index
- Move diamond left to align bottom point with center column divider - Keep all border widths uniform across tiers (remove T4 bold borders) - Remove corner accents entirely (T4 differentiated by glow + prismatic) - Fix T4 header z-index: don't override position on absolutely-positioned topright stat elements (stealing, running, bunting, hit & run) - Add ?tier= query param for dev preview of tier styling on base cards - Add run-local.sh for local API testing against dev database - Add .env.local and .run-local.pid to .gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
830e703e76
commit
d92ab86aa7
2
.gitignore
vendored
2
.gitignore
vendored
@ -59,6 +59,8 @@ pyenv.cfg
|
|||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
docker-compose.*.yml
|
docker-compose.*.yml
|
||||||
|
.run-local.pid
|
||||||
|
.env.local
|
||||||
*.db
|
*.db
|
||||||
venv
|
venv
|
||||||
.claude/
|
.claude/
|
||||||
|
|||||||
@ -737,6 +737,9 @@ async def get_batter_card(
|
|||||||
variant: int = 0,
|
variant: int = 0,
|
||||||
d: str = None,
|
d: str = None,
|
||||||
html: Optional[bool] = False,
|
html: Optional[bool] = False,
|
||||||
|
tier: Optional[int] = Query(
|
||||||
|
None, ge=0, le=4, description="Override refractor tier for preview (dev only)"
|
||||||
|
),
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
this_player = Player.get_by_id(player_id)
|
this_player = Player.get_by_id(player_id)
|
||||||
@ -800,7 +803,9 @@ async def get_batter_card(
|
|||||||
card_data["cardset_name"] = this_player.cardset.name
|
card_data["cardset_name"] = this_player.cardset.name
|
||||||
else:
|
else:
|
||||||
card_data["cardset_name"] = this_player.description
|
card_data["cardset_name"] = this_player.description
|
||||||
card_data["refractor_tier"] = resolve_refractor_tier(player_id, variant)
|
card_data["refractor_tier"] = (
|
||||||
|
tier if tier is not None else resolve_refractor_tier(player_id, variant)
|
||||||
|
)
|
||||||
card_data["request"] = request
|
card_data["request"] = request
|
||||||
html_response = templates.TemplateResponse("player_card.html", card_data)
|
html_response = templates.TemplateResponse("player_card.html", card_data)
|
||||||
|
|
||||||
@ -838,7 +843,9 @@ async def get_batter_card(
|
|||||||
card_data["cardset_name"] = this_player.cardset.name
|
card_data["cardset_name"] = this_player.cardset.name
|
||||||
else:
|
else:
|
||||||
card_data["cardset_name"] = this_player.description
|
card_data["cardset_name"] = this_player.description
|
||||||
card_data["refractor_tier"] = resolve_refractor_tier(player_id, variant)
|
card_data["refractor_tier"] = (
|
||||||
|
tier if tier is not None else resolve_refractor_tier(player_id, variant)
|
||||||
|
)
|
||||||
card_data["request"] = request
|
card_data["request"] = request
|
||||||
html_response = templates.TemplateResponse("player_card.html", card_data)
|
html_response = templates.TemplateResponse("player_card.html", card_data)
|
||||||
|
|
||||||
|
|||||||
132
run-local.sh
Executable file
132
run-local.sh
Executable file
@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# run-local.sh — Spin up the Paper Dynasty Database API locally for testing.
|
||||||
|
#
|
||||||
|
# Connects to the dev PostgreSQL on the homelab (10.10.0.42) so you get real
|
||||||
|
# card data for rendering. Playwright Chromium must be installed locally
|
||||||
|
# (it already is on this workstation).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./run-local.sh # start on default port 8000
|
||||||
|
# ./run-local.sh 8001 # start on custom port
|
||||||
|
# ./run-local.sh --stop # kill a running instance
|
||||||
|
#
|
||||||
|
# Card rendering test URLs (after startup):
|
||||||
|
# HTML preview: http://localhost:8000/api/v2/players/{id}/battingcard/{date}/{variant}?html=True
|
||||||
|
# PNG render: http://localhost:8000/api/v2/players/{id}/battingcard/{date}/{variant}
|
||||||
|
# API docs: http://localhost:8000/api/docs
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
PORT="${1:-8000}"
|
||||||
|
PIDFILE=".run-local.pid"
|
||||||
|
LOGFILE="logs/database/run-local.log"
|
||||||
|
|
||||||
|
# ── Stop mode ────────────────────────────────────────────────────────────────
|
||||||
|
if [[ "${1:-}" == "--stop" ]]; then
|
||||||
|
if [[ -f "$PIDFILE" ]]; then
|
||||||
|
pid=$(cat "$PIDFILE")
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
kill "$pid"
|
||||||
|
echo "Stopped local API (PID $pid)"
|
||||||
|
else
|
||||||
|
echo "PID $pid not running (stale pidfile)"
|
||||||
|
fi
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
else
|
||||||
|
echo "No pidfile found — nothing to stop"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Pre-flight checks ───────────────────────────────────────────────────────
|
||||||
|
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||||
|
echo "Already running (PID $(cat "$PIDFILE")). Use './run-local.sh --stop' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check Python deps are importable
|
||||||
|
python -c "import fastapi, peewee, playwright" 2>/dev/null || {
|
||||||
|
echo "Missing Python dependencies. Install with: pip install -r requirements.txt"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check Playwright Chromium is available
|
||||||
|
python -c "
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
p = sync_playwright().start()
|
||||||
|
b = p.chromium.launch(args=['--no-sandbox'])
|
||||||
|
b.close()
|
||||||
|
p.stop()
|
||||||
|
" 2>/dev/null || {
|
||||||
|
echo "Playwright Chromium not installed. Run: playwright install chromium"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check dev DB is reachable
|
||||||
|
python -c "
|
||||||
|
import socket
|
||||||
|
s = socket.create_connection(('10.10.0.42', 5432), timeout=3)
|
||||||
|
s.close()
|
||||||
|
" 2>/dev/null || {
|
||||||
|
echo "Cannot reach dev PostgreSQL at 10.10.0.42:5432 — is the homelab up?"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Ensure directories exist ────────────────────────────────────────────────
|
||||||
|
mkdir -p logs/database
|
||||||
|
mkdir -p storage/cards
|
||||||
|
|
||||||
|
# ── Launch ───────────────────────────────────────────────────────────────────
|
||||||
|
echo "Starting Paper Dynasty Database API on http://localhost:${PORT}"
|
||||||
|
echo " DB: paperdynasty_dev @ 10.10.0.42"
|
||||||
|
echo " Logs: ${LOGFILE}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Load .env, then .env.local overrides (for passwords not in version control)
|
||||||
|
set -a
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
[[ -f .env ]] && source .env
|
||||||
|
[[ -f .env.local ]] && source .env.local
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Override DB host to point at the dev server's IP (not Docker network name)
|
||||||
|
export DATABASE_TYPE=postgresql
|
||||||
|
export POSTGRES_HOST="${POSTGRES_HOST_LOCAL:-10.10.0.42}"
|
||||||
|
export POSTGRES_PORT="${POSTGRES_PORT:-5432}"
|
||||||
|
export POSTGRES_DB="${POSTGRES_DB:-paperdynasty_dev}"
|
||||||
|
export POSTGRES_USER="${POSTGRES_USER:-sba_admin}"
|
||||||
|
export LOG_LEVEL=INFO
|
||||||
|
export TESTING=True
|
||||||
|
|
||||||
|
if [[ -z "${POSTGRES_PASSWORD:-}" || "$POSTGRES_PASSWORD" == "your_production_password" ]]; then
|
||||||
|
echo "ERROR: POSTGRES_PASSWORD not set or is the placeholder value."
|
||||||
|
echo "Create .env.local with: POSTGRES_PASSWORD=<actual password>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
uvicorn app.main:app \
|
||||||
|
--host 0.0.0.0 \
|
||||||
|
--port "$PORT" \
|
||||||
|
--reload \
|
||||||
|
--reload-dir app \
|
||||||
|
--reload-dir storage/templates \
|
||||||
|
2>&1 | tee "$LOGFILE" &
|
||||||
|
|
||||||
|
echo $! >"$PIDFILE"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
echo "API running (PID $(cat "$PIDFILE"))."
|
||||||
|
echo ""
|
||||||
|
echo "Quick test URLs:"
|
||||||
|
echo " API docs: http://localhost:${PORT}/api/docs"
|
||||||
|
echo " Health: curl -s http://localhost:${PORT}/api/v2/players/1/battingcard?html=True"
|
||||||
|
echo ""
|
||||||
|
echo "Stop with: ./run-local.sh --stop"
|
||||||
|
else
|
||||||
|
echo "Failed to start — check ${LOGFILE}"
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@ -21,12 +21,6 @@
|
|||||||
<div class="diamond-quad{% if refractor_tier >= 3 %} filled{% endif %}" {% if refractor_tier >= 3 %}style="background: {{ filled_bg }};"{% endif %}></div>
|
<div class="diamond-quad{% if refractor_tier >= 3 %} filled{% endif %}" {% if refractor_tier >= 3 %}style="background: {{ filled_bg }};"{% endif %}></div>
|
||||||
<div class="diamond-quad{% if refractor_tier >= 4 %} filled{% endif %}" {% if refractor_tier >= 4 %}style="background: {{ filled_bg }};"{% endif %}></div>
|
<div class="diamond-quad{% if refractor_tier >= 4 %} filled{% endif %}" {% if refractor_tier >= 4 %}style="background: {{ filled_bg }};"{% endif %}></div>
|
||||||
</div>
|
</div>
|
||||||
{% if refractor_tier == 4 %}
|
|
||||||
<div class="corner-accent tl" style="width: 35px; height: 35px; border-top: 3px solid #c9a94e; border-left: 3px solid #c9a94e;"></div>
|
|
||||||
<div class="corner-accent tr" style="width: 35px; height: 35px; border-top: 3px solid #c9a94e; border-right: 3px solid #c9a94e;"></div>
|
|
||||||
<div class="corner-accent bl" style="width: 35px; height: 35px; border-bottom: 3px solid #c9a94e; border-left: 3px solid #c9a94e;"></div>
|
|
||||||
<div class="corner-accent br" style="width: 35px; height: 35px; border-bottom: 3px solid #c9a94e; border-right: 3px solid #c9a94e;"></div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="header" class="row-wrapper header-text border-bot" style="height: 65px">
|
<div id="header" class="row-wrapper header-text border-bot" style="height: 65px">
|
||||||
<!-- <div id="headerLeft" style="flex-grow: 3; height: auto">-->
|
<!-- <div id="headerLeft" style="flex-grow: 3; height: auto">-->
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
.tier-diamond {
|
.tier-diamond {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 600px;
|
left: 597px;
|
||||||
top: 78.5px;
|
top: 78.5px;
|
||||||
transform: translate(-50%, -50%) rotate(45deg);
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -33,17 +33,6 @@
|
|||||||
inset 1px 0 2px rgba(255,255,255,0.15);
|
inset 1px 0 2px rgba(255,255,255,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner-accent {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 6;
|
|
||||||
pointer-events: none;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
.corner-accent.tl { top: 0; left: 0; }
|
|
||||||
.corner-accent.tr { top: 0; right: 0; }
|
|
||||||
.corner-accent.bl { bottom: 0; left: 0; }
|
|
||||||
.corner-accent.br { bottom: 0; right: 0; }
|
|
||||||
|
|
||||||
{% if refractor_tier == 1 %}
|
{% if refractor_tier == 1 %}
|
||||||
/* T1 — Base Chrome */
|
/* T1 — Base Chrome */
|
||||||
@ -169,18 +158,13 @@
|
|||||||
}
|
}
|
||||||
.border-bot {
|
.border-bot {
|
||||||
border-bottom-color: #c9a94e;
|
border-bottom-color: #c9a94e;
|
||||||
border-bottom-width: 6px;
|
border-bottom-width: 4px;
|
||||||
}
|
|
||||||
#resultHeader.border-bot {
|
|
||||||
border-bottom-width: 5px;
|
|
||||||
}
|
}
|
||||||
.border-right-thick {
|
.border-right-thick {
|
||||||
border-right-color: #c9a94e;
|
border-right-color: #c9a94e;
|
||||||
border-right-width: 7px;
|
|
||||||
}
|
}
|
||||||
.border-right-thin {
|
.border-right-thin {
|
||||||
border-right-color: #c9a94e;
|
border-right-color: #c9a94e;
|
||||||
border-right-width: 4px;
|
|
||||||
}
|
}
|
||||||
.vline {
|
.vline {
|
||||||
border-left-color: #c9a94e;
|
border-left-color: #c9a94e;
|
||||||
@ -215,7 +199,7 @@
|
|||||||
animation: t4-prismatic-sweep 6s linear infinite;
|
animation: t4-prismatic-sweep 6s linear infinite;
|
||||||
animation-play-state: paused;
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
#header > * { position: relative; z-index: 2; }
|
#header > * { z-index: 2; }
|
||||||
|
|
||||||
/* T4 diamond glow pulse — paused for static PNG */
|
/* T4 diamond glow pulse — paused for static PNG */
|
||||||
@keyframes diamond-glow-pulse {
|
@keyframes diamond-glow-pulse {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user