diff --git a/ART-LICENSE.md b/ART-LICENSE.md index 360500d5..64ad61a7 100644 --- a/ART-LICENSE.md +++ b/ART-LICENSE.md @@ -4,7 +4,8 @@ Copyright (c) 2026 Cevdet Baran Oral. The paintings and sprites under `art/` and `assets/`, and the artwork in the cards drawn from them, are licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0): https://creativecommons.org/licenses/by-nc/4.0/ -- **Non-commercial use is free**, for example cards on your own GitHub profile, with credit: "questlog-github-stats art by Cevdet Baran Oral". Linking the cards to this repository counts as credit. +- **Non-commercial use is free**, for example cards on your own GitHub profile, with credit: "questlog by Cevdet Baran Oral". Using the cards from questlog.cc, or linking them to this repository, counts as credit. +- **A self-hosted instance** (a server built from this repository that serves the paintings or cards drawn from them) must show that credit as **"questlog by Cevdet Baran Oral"**, linked to https://questlog.cc, on every page it serves that shows the cards, and in its README or about page. It may use its own address, but it may not remove the questlog name from the credit or credit the art to anyone else. A changed instance must also say that it is changed (for example "based on questlog by Cevdet Baran Oral"). - **Commercial use needs written permission.** That covers any use primarily intended for commercial advantage or monetary compensation, such as a paid product or service, advertising, or a commercial self-hosted instance. Ask at contact@cevdetbaran.com. The code is licensed separately under the MIT license (see `LICENSE`). The fonts keep their own licenses (see `fonts/README.md`). diff --git a/CHANGELOG.md b/CHANGELOG.md index db6653f9..77c81431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ # Changelog -## Unreleased +## 1.1.0 - **Cards are WebP:** lossless, animated where they move, about a third of the size of the PNGs before, pixel for pixel the same. - **Decorations** at `/decorations/`: painted banners for README sections, with an optional title (`reading-nook`, `workbench`, `raven-moon`, `enchanted-clearing`, `crystal-owl`, `forest-divider`, `forest-footer`). +- **Chunky pixels:** `pixels=chunky` draws any card's or decoration's painting in pixels twice as big. The profile builder has a Fine / Chunky switch for every card at once, and each card can still have its own (the switch then says Mixed). +- **Guided tour** in the profile builder: once the first card is in, a short, skippable tour points out what each part does. +- **Spellbook runes** keep a 1 px gap between rows, and their sparkles pass behind them. - **Decorations by night:** every decoration has a night painting (`time=night`), and the profile builder can switch them with dark mode like the cards. - **Status page** at [questlog.cc/status](https://questlog.cc/status): whether cards are served, whether stats arrive from GitHub, and whether new users get in right now, with times in UTC. - **Readable logs** for whoever runs an instance: one plain line per request and per background event, and an admin page behind Cloudflare Access (see "Watching it" in the README). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6885a283..e8ac78e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,9 +22,9 @@ GITHUB_TOKEN=... DATA_DIR=./tmp-data PORT=8080 HOST=127.0.0.1 cargo run --releas Then open http://127.0.0.1:8080. Every `/demo/...` card works without a token. -- `src/` is the card server (Rust, axum): `server/` answers requests, `widgets/` draws the three cards, `card.rs`, `canvas.rs`, `text.rs` and `raster.rs` draw pixels, `github.rs` and `queue.rs` fetch stats. -- `site/` is the website (Next.js static export): the home page, the wiki and the profile builder. -- `art/` holds the paintings and their layout files, one folder per scene. +- `src/` is the card server (Rust, axum): `server/` answers requests (`admin.rs` checks the admin login), `widgets/` draws the three cards, `decorations.rs` the banners, `card.rs`, `canvas.rs`, `text.rs` and `raster.rs` draw pixels, `github.rs` and `queue.rs` fetch stats, `log.rs` writes the log lines. +- `site/` is the website (Next.js static export): the home page, the wiki, the profile builder and the status pages. +- `art/` holds the paintings and their layout files, one folder per scene, and `art/decorations/` the banners by day and night. - `tests/goldens/` holds the expected pixels of every card. ## Changing how cards look diff --git a/Cargo.lock b/Cargo.lock index 1a9f8898..8d7af67c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1282,7 +1282,7 @@ dependencies = [ [[package]] name = "questlog" -version = "1.0.0" +version = "1.1.0" dependencies = [ "axum", "jsonwebtoken", diff --git a/Cargo.toml b/Cargo.toml index 5e51f1aa..bc6c7c0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "questlog" -version = "1.0.0" +version = "1.1.0" edition = "2024" license = "MIT" publish = false diff --git a/README.md b/README.md index 22de5eeb..9447d3a9 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Pixel art GitHub profile cards with a medieval feel: a character sheet, a spellbook of your languages and a campfire for your streak. Paste one line into your profile README: no token, no workflow, no setup. +**[Website](https://questlog.cc)** · **[Profile builder](https://questlog.cc/builder)** · **[Wiki](https://questlog.cc/wiki)** · **[Status](https://questlog.cc/status)** + ![The forest scene: Character Sheet, Spellbook and Campfire, by day and by night](assets/readme/hero.png) | Card | Shows | @@ -14,7 +16,7 @@ Pixel art GitHub profile cards with a medieval feel: a character sheet, a spellb ## Use it -Open the [profile builder](https://questlog.cc/builder), type your login, add cards and copy your README. Or write it yourself, one line per card: +Open the [profile builder](https://questlog.cc/builder), type your login, add cards and copy your README; a short tour shows you around the first time. Or write it yourself, one line per card: ```md ![octocat's stats](https://questlog.cc/octocat/stats) @@ -77,7 +79,7 @@ To switch to the night painting for readers in dark mode, add `time=night` to th | `forest-divider` | a mossy log, lower than the others; no title unless you give one | | `forest-footer` | glowing mushrooms and ferns, as low as the divider; for the end of a README | -Decorations take only `title`, `title_color`, `outline_color`, `truncate`, `animate`, `scale` and `time` (`time=night` for the night painting); any other option draws an error card. +Decorations take only `title`, `title_color`, `outline_color`, `truncate`, `animate`, `scale`, `time` (`time=night` for the night painting) and `pixels`; any other option draws an error card. ## Options @@ -86,6 +88,7 @@ Decorations take only `title`, `title_color`, `outline_color`, `truncate`, `anim | `user` | your GitHub login | required | | `scene` | any scene above | `forest` | | `time` | `day`, `night` | `day` | +| `pixels` | `fine`, `chunky`: `chunky` draws the painting in pixels twice as big; text, bars and fire stay sharp | `fine` | | `title` | any text, up to 60 characters | the scene's title | | `subtitle` | any text, up to 60 characters | `/stats`: a class from your top language; none on the others | | `labels` | `key:Text,key:Text`, e.g. `commits:Deeds`; keys below | the scene's labels | @@ -120,13 +123,15 @@ docker build -t questlog . docker run -d -p 8080:8080 --env-file .env -v questlog-data:/data questlog ``` +A self-hosted instance must credit the paintings as **"questlog by Cevdet Baran Oral"**, linked to https://questlog.cc, on the pages that show the cards and in its README (see `ART-LICENSE.md`); it can run under its own address. + `.env` holds `GITHUB_TOKEN=` with a fine-grained token that can read public repositories (see `.env.example`). `NEW_LOGINS_PER_HOUR` (600 by default) caps how many logins are fetched for the first time each hour; raise it for a busy day, GitHub's own limit still pauses fetching when it runs low. The server listens on `PORT` (8080) and keeps its cache in `DATA_DIR` (`/data`). With your own token, your own instance can count your private contributions too. **Watching it.** The server writes one plain line per request and per background event to stderr (`docker logs questlog`): what was asked, whether the card came from the memory cache, the disk cache or was drawn (and how long that took), each GitHub fetch with the quota left, queue decisions, and a status line every 15 minutes. Client IPs are never written; the Cloudflare ray id links a line to Cloudflare's own logs. `/status` and `/health` show the public state (up, whether GitHub fetching runs, whether new users get in, with times in UTC). `/admin/status` and `/admin/health` show every count and capacity, the last hour's traffic and the latest log lines. Put them behind a Cloudflare Access app and set `ACCESS_TEAM_DOMAIN`, `ACCESS_AUD` and `ADMIN_EMAILS` (see `.env.example`): the server then checks the login token Access signs on every request, so the pages stay closed even if someone reaches the server another way. Without those settings they open only for a local run. ## Development -The pages at `/`, `/builder` and `/wiki` come from `site/`, a Next.js static export that the binary embeds. Build it before `cargo build`: `cd site && npm ci && npm run build` (it writes `static/`). +The pages at `/`, `/builder`, `/wiki`, `/status` and `/admin/status` come from `site/`, a Next.js static export that the binary embeds. Build it before `cargo build`: `cd site && npm ci && npm run build` (it writes `static/`). The cards on those pages are not drawn live: the site ships its own WebP copies in `site/public/cards/`. After a change to the art or the drawing, build the site, run a local server of the new build, draw the copies again with `uv run site/scripts/snap_cards.py http://127.0.0.1:8080`, then build the site once more. @@ -141,5 +146,6 @@ The code and the art have different licenses. - **Code: MIT** (see `LICENSE`). Use it, change it, self-host it. - **Art: © Cevdet Baran Oral, CC BY-NC 4.0** (see `ART-LICENSE.md`). This covers the paintings and sprites in `art/` and `assets/`, and the cards drawn from them. - Free for non-commercial use with credit, such as cards on your own GitHub profile. + - A self-hosted instance keeps the credit "questlog by Cevdet Baran Oral", linked to questlog.cc. - Commercial use needs written permission, and that includes a commercial self-hosted instance. Ask at contact@cevdetbaran.com. - **Fonts:** their own licenses (see `fonts/README.md`). diff --git a/SECURITY.md b/SECURITY.md index 7b6125ca..3f325fa2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,7 +11,7 @@ Say what you found, how to reproduce it, and what someone could do with it. You ## What counts -The hosted service at questlog.cc and the code in this repository: for example a way to read data that is not public, to make the server fetch or show something it should not, to use the server against someone else, or to take it down with little effort. +The hosted service at questlog.cc and the code in this repository: for example a way to read data that is not public, to make the server fetch or show something it should not, to use the server against someone else, or to take it down with little effort. The admin pages (`/admin/status`, `/admin/health`) are meant to open only for the owner's Cloudflare Access login; any way in without it counts. Things that are not security problems: the public stats a card shows (they come from public GitHub data), and the per-IP limit on new drawings answering "busy". diff --git a/site/app/admin/status/page.tsx b/site/app/admin/status/page.tsx index 2b9f52dd..0eae78e8 100644 --- a/site/app/admin/status/page.tsx +++ b/site/app/admin/status/page.tsx @@ -2,7 +2,7 @@ import type { Metadata } from "next"; import Nav from "@/components/Nav"; import Health from "@/components/Health"; -// Behind Cloudflare Access: every number, for Baran. Not indexed, not linked from the site. +// Behind Cloudflare Access; not indexed or linked from the site. export const metadata: Metadata = { title: "questlog health, every number", robots: { index: false, follow: false }, diff --git a/site/app/globals.css b/site/app/globals.css index d837e1b7..b24c8cd1 100644 --- a/site/app/globals.css +++ b/site/app/globals.css @@ -246,7 +246,6 @@ pre { } } -/* Buttons */ .btn { display: inline-flex; align-items: center; @@ -348,7 +347,6 @@ pre { white-space: nowrap; } -/* Footer */ .footer { width: min(100% - 2 * var(--gutter), var(--wide)); margin: 0 auto; @@ -399,7 +397,6 @@ pre { color: var(--muted); } -/* Attributions: a button at the foot of every page that opens the full list of credits in a modal. */ .attributions-open { min-height: 2.75rem; margin-top: 1.75rem; diff --git a/site/app/page.tsx b/site/app/page.tsx index 301e5288..1ae257d3 100644 --- a/site/app/page.tsx +++ b/site/app/page.tsx @@ -14,7 +14,7 @@ const HERO = "/stats?user=BaranDev&scene=castle&scale=3"; export default function Home() { return ( <> - {/* the card maker that lived at /#demo is now the profile builder */} + {/* old /#demo links go to the builder */}