diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e01c361 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,93 @@ +# AGENTS.md + +Guidance for AI agents working in this repository. + +## What this is + +`acag` (article cover art generator) is a native desktop app that generates +article cover art in five aspect ratios (square, link/Open Graph, wide, portrait, +banner) in an Omakase / Japanese-constructivist style. Rust + +[Slint](https://slint.dev) for the GUI, [resvg](https://github.com/linebender/resvg) +/ tiny-skia for rasterization. Single binary named `acag`. + +## Core invariant + +`render_cover_svg(&CoverConfig) -> String` (in `src/cover`) is the single source +of truth. The live preview and both exports rasterize the **same SVG**, so what +you see is exactly the file you get. Never fork rendering between preview and +export: change the SVG function and both follow. + +## Layout + +``` +src/ + design/ themes · wagara patterns · WCAG contrast + cover/ config · format (aspect ratios) · typesetting · render · layouts + raster.rs SVG → Pixmap/PNG by longest edge (resvg + embedded Montserrat) + export.rs save SVG / PNG (2K or 4K) + preset.rs save/load named presets as TOML + main.rs Slint GUI wiring (background export + spinner) +ui/app.slint the editor + live preview +examples/ gallery.rs (docs/samples), icon.rs (app icon + icon.ico) +build.rs compiles the Slint UI; embeds the Windows icon resource +``` + +The library crate holds all logic and is what tests cover; `main.rs` is thin GUI +glue. Fonts (Montserrat Black/Bold/Regular) are embedded via `include_bytes!`, so +no system fonts are needed at runtime. + +## Commands + +```sh +cargo run --release # run the app +cargo test # unit tests +cargo fmt --all --check # formatting gate +cargo clippy --all-targets -- -D warnings # lint gate +cargo llvm-cov --lib --fail-under-lines 80 # coverage gate (library ≥ 80%) +cargo run --example gallery # regenerate docs/samples +cargo run --example icon # regenerate assets/icons/* (PNGs + icon.ico) +``` + +CI (`.github/workflows/ci.yml`) runs fmt, clippy `-D warnings`, `cargo test`, and +library line coverage ≥ 80%. All four must pass; run them before proposing a +change is done. + +## Conventions + +- Edition 2024. Note `std::env::set_var` is `unsafe` here; only call it before any + threads/backends start (see `main.rs`). +- Code, comments, and identifiers in English. Prefer full descriptive names, but + keep the project's established abbreviations (`config`, `cfg`, `ctx`); match the + surrounding code rather than renaming. +- No filler comments. Comment only non-obvious *why*, not *what*. +- Tests: cover basic functionality first, then edge cases; keep the library at + ≥ 80% lines. Use real types, never ad-hoc untyped mocks. +- Imports inside the same directory use `crate::...` / `super::...` paths already + established in the file; follow the surrounding style. + +## Platform & runtime notes + +- **Renderer**: defaults to Slint's software (CPU) renderer (`SLINT_BACKEND=winit-software` + set in `main.rs` when unset) so it runs without OpenGL (VMs, RDP). Femtovg/GPU + is opt-in via `SLINT_BACKEND=winit-femtovg`. Don't assume a GL context exists. +- **Windows**: `#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]` + hides the console for release builds. The `.exe` icon is embedded from + `assets/icons/icon.ico` via `winresource` in `build.rs` (only on a Windows host). +- **Output dirs**: covers go to `~/Pictures/article-covers/` (Linux) / + `%USERPROFILE%\Pictures\article-covers\` (Windows), override `ACAG_OUTPUT_DIR`. + Presets live under the platform config dir, override `ACAG_PRESETS_DIR`. + +## Release + +`.github/workflows/release.yml` triggers on `v*` tags: builds a Linux AppImage + +tarball and a standalone Windows `.exe` (static CRT), attached to the GitHub +release. The AppImage `.desktop` `Categories` must use only freedesktop-registered +values (no unregistered `Design`, etc.) or `appimagetool` fails. + +## Gotchas + +- Keep preview and export pixel-identical: only the rasterization size differs. +- Regenerate `docs/samples` (`gallery`) and the icon set (`icon`) when their + inputs change; both are committed assets. +- After editing `ui/app.slint`, the build re-runs `slint-build`; mismatched + property names surface as compile errors in `main.rs`. diff --git a/README.md b/README.md index 061d03b..922b770 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@
A native desktop app that generates article cover art in an - Omakase / Japanese-constructivist style — five aspect ratios + Omakase / Japanese-constructivist style: five aspect ratios (square, link, wide, portrait, banner), six themes, five patterns, three layouts, a live preview, exported up to 4K PNG (plus the source SVG).
@@ -29,35 +29,35 @@ ## The idea -Built around **Omakase** (お任せ — "I leave it to you", the sushi chef's choice) and the +Built around **Omakase** (お任せ, "I leave it to you", the sushi chef's choice) and the geometry of traditional Japanese patterns (**wagara**, 和柄), tempered with bold, structural, constructivist typography. The result is calm but assertive: a huge **Montserrat Black** title, a lot of **Ma** (negative space), and a quiet wave "seal". -The Japanese identity comes **only from geometry and color** — there are deliberately +The Japanese identity comes **only from geometry and color**; there are deliberately **no Japanese glyphs in the artwork**. It shares its design language (themes, wagara, the sumi-ê palette) with [skvggor.dev](https://skvggor.dev) and [`waka-readme`](https://github.com/skvggor/waka-readme). ## Features -- **6 sumi-ê themes** — `terracotta` 赤土 · `sumi` 墨 · `matcha` 抹茶 · `washi` 和紙 · `ai` 藍 · `sakura` 桜 -- **5 wagara patterns** — `seigaiha` (waves) · `shippo` (interlocking circles) · `kikko` +- **6 sumi-ê themes**: `terracotta` 赤土 · `sumi` 墨 · `matcha` 抹茶 · `washi` 和紙 · `ai` 藍 · `sakura` 桜 +- **5 wagara patterns**: `seigaiha` (waves) · `shippo` (interlocking circles) · `kikko` (tortoiseshell) · `yabane` (arrow feathers) · `asanoha` (hemp leaf) -- **3 layouts** — `editorial` (asymmetric), `bloco` (constructivist color block), `ma` (negative space) -- **5 formats** — `1:1` square, `1.91:1` link/Open Graph (LinkedIn, Facebook, X, Slack), - `16:9` wide (native LinkedIn article, YouTube), `4:5` portrait, `2:1` banner — the layouts +- **3 layouts**: `editorial` (asymmetric), `bloco` (constructivist color block), `ma` (negative space) +- **5 formats**: `1:1` square, `1.91:1` link/Open Graph (LinkedIn, Facebook, X, Slack), + `16:9` wide (native LinkedIn article, YouTube), `4:5` portrait, `2:1` banner; the layouts adapt to each ratio -- **Live preview** — every control updates the preview instantly +- **Live preview**: every control updates the preview instantly - **Adjustable film grain** and **pattern strength** sliders, shown live in the preview -- **Omakase button** — randomizes the visual style and lets the house plate it for you -- **Named presets** — save any number of looks as TOML and load/delete any of them (`~/.config/article-cover-art-generator/presets/` on Linux, `%APPDATA%\article-cover-art-generator\presets\` on Windows) -- **WCAG AAA** — all readable text is forced to ≥ 7:1 contrast against its background +- **Omakase button**: randomizes the visual style and lets the house plate it for you +- **Named presets**: save any number of looks as TOML and load/delete any of them (`~/.config/article-cover-art-generator/presets/` on Linux, `%APPDATA%\article-cover-art-generator\presets\` on Windows) +- **WCAG AAA**: all readable text is forced to ≥ 7:1 contrast against its background - **Montserrat** Black / Bold / Regular, embedded in the binary (no system fonts needed) -- **Export** — **2K or 4K** (longest edge) PNG at each format's exact dimensions, plus the +- **Export**: **2K or 4K** (longest edge) PNG at each format's exact dimensions, plus the resolution-independent source SVG; a non-blocking save (spinner while it rasterizes) and optional open-after-export -- **Native** — Rust + [slint](https://slint.dev) + [resvg](https://github.com/linebender/resvg), +- **Native**: Rust + [slint](https://slint.dev) + [resvg](https://github.com/linebender/resvg), running directly on Wayland (no web view, no Node) Only the **title** is required; category, date, number and brand are optional, keeping the @@ -65,14 +65,14 @@ cover generic enough for any platform (blog, dev.to, LinkedIn, X, OG image, thum ## Download -Prebuilt binaries are attached to each [GitHub release](https://github.com/skvggor/acag/releases) — no Rust toolchain or system dependencies required. +Prebuilt binaries are attached to each [GitHub release](https://github.com/skvggor/acag/releases). No Rust toolchain or system dependencies required. -- **Linux** — `acag-*-x86_64.AppImage` (self-contained: `chmod +x` and run), or `acag-*-linux-x86_64.tar.gz` (raw binary). -- **Windows** — `acag-*-windows-x86_64.zip` (standalone `acag.exe`, no Visual C++ runtime needed). +- **Linux**: `acag-*-x86_64.AppImage` (self-contained: `chmod +x` and run), or `acag-*-linux-x86_64.tar.gz` (raw binary). +- **Windows**: `acag-*-windows-x86_64.zip` (standalone `acag.exe`, no Visual C++ runtime needed). ### Rendering backend -By default the app uses Slint's **software (CPU) renderer**, so it runs everywhere — +By default the app uses Slint's **software (CPU) renderer**, so it runs everywhere, including headless VMs and RDP sessions without a usable OpenGL driver. For this form-plus-preview UI the difference is imperceptible. To opt into GPU rendering: @@ -157,7 +157,7 @@ cargo run --example icon # regenerate the app icon ## Credits -- **Montserrat** by Julieta Ulanovsky et al. — [SIL Open Font License](https://github.com/JulietaUla/Montserrat). +- **Montserrat** by Julieta Ulanovsky et al., under the [SIL Open Font License](https://github.com/JulietaUla/Montserrat). - **resvg / tiny-skia** and **slint** for native rendering and UI. - Design language shared with [skvggor.dev](https://skvggor.dev) and `waka-readme`. diff --git a/assets/article-cover-art-generator.desktop b/assets/article-cover-art-generator.desktop index 61bfb04..ae4d399 100644 --- a/assets/article-cover-art-generator.desktop +++ b/assets/article-cover-art-generator.desktop @@ -2,7 +2,7 @@ Type=Application Name=acag GenericName=Article Cover Art Generator -Comment=Generate 1:1 article cover art in an Omakase style +Comment=Generate article cover art in an Omakase style Exec=acag Icon=article-cover-art-generator Terminal=false