From 751a0e2525f1bcd7993a45f1fe019ac216bfe4ad Mon Sep 17 00:00:00 2001 From: Pagwin Date: Wed, 17 Jun 2026 16:23:09 -0400 Subject: [PATCH] frontend tweaks --- Dockerfile | 23 +++++++++++ docker-compose.yml | 14 +++++++ index.html | 96 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..93a0504 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.13-slim + +WORKDIR /app + +# Install uv +RUN pip install --no-cache-dir uv + +# Copy project files +COPY pyproject.toml uv.lock ./ + +# Install dependencies with uv +RUN uv sync --no-editable + +# Copy source code +COPY solve.py web_solve.py ./ +COPY templates/ templates/ + +# Set environment variables +ENV FLASK_APP=web_solve.py +ENV PYTHONUNBUFFERED=1 + +# Run Flask app +CMD ["uv", "run", "python", "web_solve.py"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6ad89a1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.8' + +services: + web: + build: . + ports: + - "5555:5000" + environment: + - FLASK_ENV=development + - FLASK_DEBUG=1 + volumes: + - .:/app + command: uv run python web_solve.py + restart: unless-stopped diff --git a/index.html b/index.html index bb80a69..85f733c 100644 --- a/index.html +++ b/index.html @@ -129,10 +129,21 @@ padding: .55rem .7rem; display: grid; gap: .45rem .8rem; - align-items: end; + align-items: start; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } + /* Top-align cells but keep every field's input on a common baseline by + reserving a uniform (two-line) label height — so a wrapping label no + longer drops its input below the others, and a stacked cell (Type + + "Can renovate") can hang its extra control underneath while its primary + input still lines up with the rest. */ + .card .field:not(.check):not(.vat-row)>span { + min-height: 2.8em; + display: flex; + align-items: flex-end; + } + .card input, .card select, .card textarea { @@ -149,17 +160,70 @@ gap: .35rem; } + /* Stack two controls vertically inside a single grid cell (e.g. the city + Type field with its "Can renovate" checkbox tucked underneath). Top-align + so the Type input stays on the same row as the card's other inputs while + the checkbox hangs below. */ + .card .stack { + display: flex; + flex-direction: column; + gap: .45rem; + min-width: 0; + align-self: start; + } + + /* Vat amounts: stacked rows, each with its label to the left of the input. */ + .card .vat-group { + display: flex; + flex-direction: column; + gap: .3rem; + min-width: 0; + } + + .card .vat-head { + font-size: .8rem; + opacity: .8; + } + + .card .vat-row { + flex-direction: row; + align-items: center; + gap: .4rem; + } + + .card .vat-row>span { + flex: 0 0 4rem; + } + + .card .vat-row input { + width: auto; + flex: 1; + min-width: 0; + } + .card-actions { align-self: start; justify-self: end; } + /* Keep the "Log" checkbox and its JS-function field together in one cell + and reserve the space up front, so enabling the field only fills the + already-allotted room instead of reflowing the whole card. */ + .card .log-group { + grid-column: span 2; + display: flex; + align-items: start; + gap: .5rem; + min-width: 0; + } + .log-cell { display: none; } .card.log-on .log-cell { display: flex; + flex: 1; } /* Output tables: a CSS grid kept inside a scroll container so it never @@ -466,15 +530,18 @@ } } // Vat amounts only matter for Foundries; hide them otherwise and keep - // them as the card's last fields (before the actions). - const vatFields = [ - field("Vat steel", vs), - field("Vat brass", vb), - field("Vat electrum", ve), - ]; + // them as the card's last field (before the actions). They stack in a + // single cell, each input labeled on its left. + const vatRow = (label, input) => + el("label", {class: "field vat-row"}, [el("span", {}, label), input]); + const vatGroup = el("div", {class: "vat-group"}, [ + el("span", {class: "vat-head"}, "Vat amounts"), + vatRow("steel", vs), + vatRow("brass", vb), + vatRow("electrum", ve), + ]); function renderVats() { - const show = type.value === "foundry"; - for (const f of vatFields) f.style.display = show ? "" : "none"; + vatGroup.style.display = type.value === "foundry" ? "" : "none"; } type.onchange = () => {renderUpgrades(); renderVats();}; renderUpgrades(); @@ -499,14 +566,16 @@ }; card.append( field("Name", name), - field("Type", type), + el("div", {class: "stack"}, [ + field("Type", type), + checkField("Can renovate", reno), + ]), field("Renown", renown), field("Upgrades (already installed)", upWrap), - checkField("Can renovate", reno), field("Adjacent cities (csv of names)", adjacent), field("Forced actions (turn:action, csv)", forced), field("Avail turns (csv, blank=all)", avail), - ...vatFields, + vatGroup, el("div", {class: "card-actions"}, removeBtn(card))); document.getElementById("cities").append(card); } @@ -584,8 +653,7 @@ resF, field("Scalar", scalar), turnF, - checkField("Log?", isLog), - exprF, + el("div", {class: "log-group"}, [field("Log", isLog), exprF]), el("div", {class: "card-actions"}, removeBtn(card))); document.getElementById("terms").append(card); toggle();