From 218bb296eece95325d3a084e5db25c3110907701 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 30 Sep 2026 14:13:47 -0700 Subject: [PATCH] Compact home page entries; set up Compose for Arcane git sync Podium, Spotlight and Grid render smaller on the home page on desktop so one entry no longer fills the screen; list pages keep full size. Compose now builds on every deploy (pull_policy: build), stores data in the canon-data named volume outside the synced folder (CANON_DATA_PATH overrides), and takes optional bind/port/version settings. Co-Authored-By: Claude Opus 5.5 --- .env.example | 10 ++++++++++ README.md | 17 +++++++++++++++++ docker-compose.yml | 22 +++++++++++++++++++--- web/static/css/app.css | 25 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 423eda0..4840ba4 100644 --- a/.env.example +++ b/.env.example @@ -20,3 +20,13 @@ CANON_IGDB_CLIENT_SECRET= # CANON_HOST=0.0.0.0 # all network interfaces; 127.0.0.1 = this machine only # CANON_PORT=4242 # CANON_LOG_LEVEL=debug + +# ---- Docker Compose only (not read by Canon itself) ---- +# Where the database and images live. Default: the canon_canon-data volume. +# Use an absolute host path to keep them in a folder (writable by UID 1000). +# CANON_DATA_PATH=/srv/canon +# Host address and port to publish on. +# CANON_BIND=0.0.0.0 +# CANON_PUBLISH_PORT=4242 +# Version label baked into the image at build time. +# CANON_VERSION=dev diff --git a/README.md b/README.md index 4df96cb..7526250 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,23 @@ To skip the setup page, set `CANON_ADMIN_USERNAME` and `CANON_ADMIN_PASSWORD` in Canon listens on all network interfaces (`0.0.0.0`) by default, so phones and other computers on your network can open `http://:4242`. When run outside Docker, the startup log prints the exact addresses. If another device can't connect, the host's firewall is probably blocking the port (for example `sudo ufw allow 4242/tcp`). To keep Canon private to one machine, set `CANON_HOST=127.0.0.1`, or publish `127.0.0.1:4242:4242` in `docker-compose.yml`. +### With Arcane (Git sync) + +The compose file works as an [Arcane](https://getarcane.app) project synced from this repository: + +1. **Projects → New → Git sync**, direction **Pull**, compose file path `docker-compose.yml`. +2. Turn on **Redeploy after sync** to rebuild whenever you push. `pull_policy: build` makes every deploy build the image from the synced source; it never looks for `canon:latest` on a registry. +3. Put your settings (admin account, API keys) in the project's environment in Arcane. Arcane writes them to `.env`, which Canon reads. + +The database lives in the `canon_canon-data` named volume, outside the synced folder, so a sync can't overwrite or delete it. To use a host folder instead, set `CANON_DATA_PATH` to an absolute path (it must be writable by UID 1000). To move data from an existing `./data` folder into the volume: + +```sh +docker compose stop canon +docker run --rm -v "$PWD/data:/from" -v canon_canon-data:/to alpine sh -c 'cp -a /from/. /to/ && chown -R 1000:1000 /to' +``` + +The compose file joins the external `chromanet` network; create it first (`docker network create chromanet`) or remove the `networks` entries. + ### Without Compose ```sh diff --git a/docker-compose.yml b/docker-compose.yml index c85893a..bb3df02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,37 @@ name: canon +# Works with a plain `docker compose up -d --build` and with Arcane's Git sync +# (Pull). Arcane pulls this file's whole directory, so the build context is +# the repository itself. + services: canon: - build: . + build: + context: . + args: + VERSION: ${CANON_VERSION:-dev} image: canon:latest + # Never look for canon:latest on a registry: every deploy (including + # Arcane's "Pull image" / "Redeploy after sync") builds from the source + # that was just synced. + pull_policy: build container_name: canon restart: unless-stopped ports: - - "0.0.0.0:4242:4242" + - "${CANON_BIND:-0.0.0.0}:${CANON_PUBLISH_PORT:-4242}:4242" volumes: - - ./data:/data + # A named volume by default, so a Git sync never touches the database. + # Set CANON_DATA_PATH to a host folder (e.g. ./data) to keep it there. + - ${CANON_DATA_PATH:-canon-data}:/data env_file: - path: .env required: false networks: - chromanet +volumes: + canon-data: + networks: chromanet: external: true diff --git a/web/static/css/app.css b/web/static/css/app.css index 42a408e..1192253 100644 --- a/web/static/css/app.css +++ b/web/static/css/app.css @@ -166,6 +166,10 @@ code { font: 0.9em ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; bac .items { list-style: none; margin: 0; padding: 0; } .items--grid { display: grid; gap: 22px 18px; grid-template-columns: repeat(auto-fill, minmax(156px, 1fr)); } .aspect-square .items--grid { grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); } +@media (min-width: 641px) { + .page-home .items--grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); } + .page-home .aspect-square .items--grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); } +} @media (max-width: 640px) { .items--grid { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 16px 12px; } .aspect-square .items--grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); } @@ -325,6 +329,21 @@ code { font: 0.9em ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; bac .podium-card { padding-bottom: 10px; } } +/* The home page is a preview of every list, so its podium is compact; the + list page keeps the full-size one. */ +@media (min-width: 641px) { + .page-home .podium { max-width: 580px; gap: 0 16px; } + .page-home .podium--2 { max-width: 390px; } + .page-home .podium--1 { grid-template-columns: minmax(0, 190px); } + .page-home .podium-card { gap: 10px; padding-bottom: 10px; } + .page-home .podium-card .item-title { font-size: 14.5px; } + .page-home .podium-spot--1 .item-title { font-size: 16px; } + .page-home .podium-step { font-size: 22px; } + .page-home .podium-spot--1 .podium-step { height: 56px; font-size: 28px; } + .page-home .podium-spot--2 .podium-step { height: 40px; } + .page-home .podium-spot--3 .podium-step { height: 28px; font-size: 18px; } +} + /* "Show N more" under a podium: the browser's own
, so it works without JavaScript. */ .more { padding-top: 18px; } @@ -372,6 +391,12 @@ code { font: 0.9em ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; bac display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; } .spotlight-more { margin-top: 10px; } +@media (min-width: 641px) { + .page-home .spotlight, .page-home .aspect-square .spotlight { grid-template-columns: 150px 1fr; gap: 24px; padding: 18px; } + .page-home .spotlight-rank { font-size: 32px; } + .page-home .spotlight-title { font-size: clamp(20px, 2.2vw, 26px); } + .page-home .spotlight-desc, .page-home .spotlight-note { -webkit-line-clamp: 3; font-size: 14.5px; } +} @media (max-width: 640px) { .spotlight, .aspect-square .spotlight { grid-template-columns: 1fr; gap: 18px; padding: 18px; } .spotlight-art { width: min(100%, 220px); justify-self: center; }