Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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`.
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<p align="center">
A native desktop app that generates <strong>article cover art</strong> in an
<strong>Omakase / Japanese-constructivist</strong> style <strong>five aspect ratios</strong>
<strong>Omakase / Japanese-constructivist</strong> style: <strong>five aspect ratios</strong>
(square, link, wide, portrait, banner), six themes, five patterns, three layouts, a live
preview, exported up to <strong>4K PNG</strong> (plus the source SVG).
</p>
Expand All @@ -29,50 +29,50 @@

## 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
cover generic enough for any platform (blog, dev.to, LinkedIn, X, OG image, thumbnail…).

## 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:

Expand Down Expand Up @@ -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`.

Expand Down
2 changes: 1 addition & 1 deletion assets/article-cover-art-generator.desktop
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading