app icon: calico cat in a polka-dot scarf (owner's ArtCraft drawing), Dock/taskbar/launcher wiring, palette docs

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-10-02 09:44:07 -04:00
1 parent 7143973f52
commit b97828eb50
27 files changed
+336 -1

No files matched your search

+16
View File
@@ -38,6 +38,22 @@ Generated-in-code art is original and has no file to list: the UI icon set (`cra
| `crates/render/tests/golden/feather.png` | DesignCraft contributors (generated) | rendered by DesignCraft's own renderer (`crates/render/tests/effects.rs`, synthetic shapes and bundled fonts) | MIT OR Apache-2.0 | Golden test image for soft effects |
| `crates/render/tests/golden/inner-shadow.png` | DesignCraft contributors (generated) | rendered by DesignCraft's own renderer (`crates/render/tests/effects.rs`, synthetic shapes and bundled fonts) | MIT OR Apache-2.0 | Golden test image for soft effects |
| `crates/render/tests/golden/outer-glow.png` | DesignCraft contributors (generated) | rendered by DesignCraft's own renderer (`crates/render/tests/effects.rs`, synthetic shapes and bundled fonts) | MIT OR Apache-2.0 | Golden test image for soft effects |
| `assets/app-icon/LICENSE.txt` | (licence text) | this project | — | Licence for the app icon |
| `assets/app-icon/designcraft-1024.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/designcraft-macos-512.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/designcraft-small.svg` | DesignCraft project owner (drawn in ArtCraft) | owner's ArtCraft drawing, vectorised with craftrules `assets/logo-options/_tools/vectorize_tile.py` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/designcraft.icns` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/designcraft.ico` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/designcraft.svg` | DesignCraft project owner (drawn in ArtCraft) | owner's ArtCraft drawing, vectorised with craftrules `assets/logo-options/_tools/vectorize_tile.py` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/128x128/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/16x16/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/24x24/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/256x256/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/32x32/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/48x48/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/512x512/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/64x64/apps/ai.storyteller.designcraft.png` | DesignCraft project owner (drawn in ArtCraft) | rendered from `assets/app-icon/designcraft.svg` by `packaging/icons.sh` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `assets/app-icon/hicolor/scalable/apps/ai.storyteller.designcraft.svg` | DesignCraft project owner (drawn in ArtCraft) | owner's ArtCraft drawing, vectorised with craftrules `assets/logo-options/_tools/vectorize_tile.py` | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | App icon: calico cat in a polka-dot scarf (see `assets/app-icon/README.md`) |
| `docs/brand/artcraft-logo.svg`, `docs/brand/artcraft-logo.png` | ArtCraft / Storyteller | ArtCraft brand kit (project owner) | ArtCraft trademark — used with the owner's permission; not covered by the code licence | ArtCraft wordmark (README header) |
| `docs/brand/artcraft-logo-white.svg`, `docs/brand/artcraft-logo-white.png` | ArtCraft / Storyteller | ArtCraft brand kit (project owner) | ArtCraft trademark — used with the owner's permission; not covered by the code licence | Wordmark for dark backgrounds |
| `docs/brand/artcraft-mark.svg`, `docs/brand/artcraft-mark.png` | ArtCraft / Storyteller | ArtCraft brand kit (project owner) | ArtCraft trademark — used with the owner's permission; not covered by the code licence | The mark; its outline is drawn in code by `crates/ui-egui/src/about.rs` (About splash, start screen) |
Generated
+19
View File
@@ -853,6 +853,7 @@ dependencies = [
"muda",
"rfd",
"serde_json",
"winresource",
]
[[package]]
@@ -4070,10 +4071,12 @@ version = "1.1.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
@@ -4107,6 +4110,12 @@ dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
[[package]]
name = "tracing"
version = "0.1.44"
@@ -5152,6 +5161,16 @@ dependencies = [
"memchr",
]
[[package]]
name = "winresource"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
dependencies = [
"toml",
"version_check",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
+5
View File
@@ -7,6 +7,10 @@
</a>
</p>
<p align="center">
<img alt="DesignCraft app icon: a calico cat wearing a polka-dot scarf" src="assets/app-icon/hicolor/256x256/apps/ai.storyteller.designcraft.png" width="128">
</p>
<h1 align="center">DesignCraft</h1>
<p align="center">
@@ -139,6 +143,7 @@ egui frontend is a separate crate, so the UI can be swapped without touching the
- **Status and milestones:** [ROADMAP.md](ROADMAP.md)
- **Contributor and agent rules** (clean-room, asset policy, quality gates): [`AGENTS.md`](AGENTS.md)
- **Bundled assets:** every one is listed with its licence in [`ASSETS.md`](ASSETS.md)
- **App icon and colour:** a calico cat in a polka-dot scarf on DesignCraft green `#7bb51c`; see [`assets/app-icon/`](assets/app-icon/README.md)
## The Crafting Apps
+4
View File
@@ -17,6 +17,10 @@ egui = { workspace = true }
serde_json = { workspace = true }
log = { workspace = true }
[build-dependencies]
# Icon + VERSIONINFO for designcraft.exe; build.rs only uses it when the target is Windows.
winresource = "0.1"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rfd = { workspace = true }
+29
View File
@@ -0,0 +1,29 @@
//! Windows only: embed the app icon and version info (VERSIONINFO) into `designcraft.exe`, so it
//! shows in Explorer, the taskbar, the Start menu and Alt-Tab.
//!
//! On every other target this does nothing. A missing resource compiler is a warning, so a
//! cross-compile from macOS or Linux still links, unless `DESIGNCRAFT_REQUIRE_WINRES=1` turns it
//! into an error (for release builds).
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=../../assets/app-icon/designcraft.ico");
println!("cargo:rerun-if-env-changed=DESIGNCRAFT_REQUIRE_WINRES");
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
return;
}
let mut res = winresource::WindowsResource::new();
res.set_icon("../../assets/app-icon/designcraft.ico")
.set("ProductName", "DesignCraft")
.set("FileDescription", "DesignCraft page layout")
.set("CompanyName", "Learning Machines LLC")
.set("LegalCopyright", "Copyright (c) the DesignCraft authors. MIT OR Apache-2.0.")
.set("OriginalFilename", "designcraft.exe")
.set("InternalName", "designcraft");
if let Err(e) = res.compile() {
if std::env::var_os("DESIGNCRAFT_REQUIRE_WINRES").is_some() {
panic!("embedding Windows resources failed: {e}");
}
println!("cargo:warning=designcraft.exe built without icon/version resources: {e}");
}
}
+18 -1
View File
@@ -99,6 +99,19 @@ fn services() -> Services {
}
}
/// Desktop app ID: the Linux `.desktop` file name and hicolor icon name (Wayland matches windows by it).
const APP_ID: &str = "ai.storyteller.designcraft";
/// The window, Dock, taskbar and Alt-Tab icon. macOS gets Apple's icon grid (transparent margin);
/// elsewhere the full tile. Regenerate with `packaging/icons.sh`.
fn app_icon() -> Option<egui::IconData> {
#[cfg(target_os = "macos")]
let png: &[u8] = include_bytes!("../../../assets/app-icon/designcraft-macos-512.png");
#[cfg(not(target_os = "macos"))]
let png: &[u8] = include_bytes!("../../../assets/app-icon/hicolor/256x256/apps/ai.storyteller.designcraft.png");
eframe::icon_data::from_png_bytes(png).map_err(|e| log::warn!("app icon: {e}")).ok()
}
fn main() -> eframe::Result {
let mut control_port: Option<u16> = std::env::var("DESIGNCRAFT_CONTROL_PORT").ok().and_then(|p| p.parse().ok());
let mut files = Vec::new();
@@ -115,7 +128,7 @@ fn main() -> eframe::Result {
_ => files.push(a),
}
}
let options = eframe::NativeOptions {
let mut options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("DesignCraft")
.with_inner_size([1440.0, 900.0])
@@ -126,6 +139,10 @@ fn main() -> eframe::Result {
.with_title_shown(false),
..Default::default()
};
options.viewport = options.viewport.with_app_id(APP_ID);
if let Some(icon) = app_icon() {
options.viewport = options.viewport.with_icon(icon);
}
eframe::run_native(
"DesignCraft",
options,
+13
View File
@@ -0,0 +1,13 @@
DesignCraft app icon (designcraft.svg, designcraft-small.svg and every file derived from them in
this directory: designcraft-1024.png, designcraft-macos-512.png, designcraft.icns, designcraft.ico,
hicolor/).
Original artwork made for this project by the project owner in ArtCraft, then vectorised. No
third-party material. Licensed like the rest of DesignCraft, under either of
* Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
* MIT license (https://opensource.org/licenses/MIT)
at your option.
Regenerate the derived files with packaging/icons.sh.
+50
View File
@@ -0,0 +1,50 @@
# DesignCraft app icon
**A calico cat wearing a polka-dot scarf**, as an engraving-style head-and-shoulders portrait in the
Crafting Apps' owl-template framing: a full-bleed colour field, the cat in three-quarter view, its
shoulders running off the bottom edge.
## Palette
Exactly three colours:
| Colour | Hex | Used for |
|---|---|---|
| Ink | `#0b0b0c` | Line work and contour |
| Paper | `#efe9dc` | The figure (the cat and its scarf) |
| DesignCraft green (app colour) | `#7bb51c` | The full-bleed background field |
## Geometry
A 512 × 512 viewBox, clipped to a rounded square with `rx=112`. Windows and Linux use the full tile. The
macOS icons (`.icns`, `designcraft-macos-512.png`) sit on Apple's grid: an 824/1024 body with a
transparent margin.
## Provenance
The owner drew the cat in ArtCraft. The 2880 px drawing, keyed to the three-colour palette, lives
outside this repo in craftrules at `assets/app-icons/designcraft/source.png`. It was vectorised with
craftrules' `assets/logo-options/_tools/vectorize_tile.py`: `designcraft.svg` is traced at 2048 px and
`designcraft-small.svg` at 1024 px. Design rules: craftrules `standards/icon-design.md`.
## Files
| File | Use |
|---|---|
| `designcraft.svg` | Canonical artwork (about 680 KB) |
| `designcraft-small.svg` | Lighter vector (about 350 KB); also `hicolor/scalable` |
| `designcraft-1024.png` | 1024 px render of the full tile |
| `designcraft-macos-512.png` | Runtime Dock icon on macOS (embedded by `apps/designcraft`) |
| `designcraft.icns` | macOS bundle icon (`CFBundleIconFile`) |
| `designcraft.ico` | Windows exe icon, 16–256 px (embedded by `apps/designcraft/build.rs`) |
| `hicolor/<size>/apps/ai.storyteller.designcraft.png` | Linux icon theme, 16–512 px; the 256 px one is also the runtime window icon on Windows and Linux |
| `hicolor/scalable/apps/ai.storyteller.designcraft.svg` | Linux scalable icon |
| `LICENSE.txt` | MIT OR Apache-2.0 |
The app ID is `ai.storyteller.designcraft`. It's used for the hicolor icon names, the Wayland app ID and
`packaging/linux/ai.storyteller.designcraft.desktop`.
## Regenerate
Edit `designcraft.svg` (or `designcraft-small.svg`), then run `packaging/icons.sh`. It needs `resvg`, and
`iconutil` for the `.icns` (macOS only). It packs the `.ico` with `cargo xtask ico`.
Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><clipPath id="t"><rect width="512" height="512" rx="112"/></clipPath></defs><g clip-path="url(#t)"><rect width="512" height="512" fill="#7bb51c"/><path fill="#efe9dc" fill-rule="evenodd" d="M103 505C61 502 24 473 12 433C7 420 7 417 7 394L6 374 8 373C9 372 10 372 11 372C11 372 12 371 13 370C13 369 14 368 15 368C15 368 17 367 18 366C19 365 21 363 23 362C24 362 26 360 27 359C28 358 30 357 30 357C30 357 32 356 34 354C35 352 37 351 38 351C38 351 40 350 41 349C42 348 43 347 44 347C45 346 47 346 47 345C48 344 49 344 50 344C50 344 51 344 52 343C52 342 58 338 60 338C60 338 61 338 62 337C62 336 64 335 65 335C68 333 69 333 71 331C72 330 73 330 73 330C73 330 74 329 75 328C75 327 77 326 79 326C83 324 85 322 82 322C78 322 79 315 83 313C84 312 84 311 84 311C84 310 85 309 86 308C87 306 86 306 85 306C82 305 82 300 85 298C86 297 87 296 87 296C87 295 88 294 89 293C91 291 92 290 92 290C92 289 92 288 92 288C93 288 93 286 92 285C91 285 90 284 90 284C90 282 90 281 88 280C87 280 87 279 87 276C88 265 88 265 89 263C90 262 91 261 90 260C90 259 91 257 91 256C92 254 92 253 91 253C90 252 90 252 91 252C92 252 93 251 93 248C94 246 93 246 91 246C90 246 90 241 91 237C91 234 91 234 91 233C90 233 90 232 90 231C90 231 90 230 89 229C89 228 89 228 89 227C90 226 90 225 90 224C90 222 90 222 91 222C93 223 92 220 91 219L89 218 90 217C91 216 91 215 91 214C91 213 91 212 92 212C93 212 93 212 94 210C94 210 94 208 95 207C95 206 95 205 95 205C95 204 96 203 97 202C98 201 98 201 98 198C98 196 99 195 100 194C102 192 103 190 104 188C104 187 104 187 105 186C105 186 106 186 106 185C106 184 107 182 107 180C108 179 108 177 108 177C108 177 109 176 109 175C110 174 110 167 110 163C109 161 110 161 110 160C111 159 111 159 111 157C110 155 110 154 111 155C113 155 114 152 112 151C112 149 112 149 113 150C115 150 117 149 117 148C117 146 120 142 121 141C122 141 123 140 123 139C123 138 124 137 125 137C126 137 126 136 127 135C127 134 127 133 128 132C129 131 130 130 130 129C130 129 138 120 139 120C139 120 140 119 140 118C141 117 143 116 144 117C145 118 146 117 147 116C148 115 149 115 149 115C150 115 151 115 152 114C153 113 156 112 159 112C162 112 162 111 166 109C168 108 168 108 169 106C169 104 170 103 170 102C171 101 172 99 172 98C173 95 175 91 176 90C177 90 178 89 178 88C178 86 178 85 179 85C180 85 180 83 181 82C181 80 181 80 183 80C185 80 185 79 186 77C186 74 187 72 189 71C190 71 190 70 190 69C190 69 190 67 191 66C192 65 192 63 193 62C193 61 194 60 194 60C196 60 196 59 196 57C196 56 197 55 198 54C199 53 201 51 202 49C203 48 204 46 205 45C205 45 206 44 206 43C206 41 211 37 213 37C214 37 215 37 218 35C224 32 226 32 229 35C230 36 231 37 231 38C231 39 231 40 232 40C232 41 233 44 233 47C233 48 233 49 234 50C234 51 234 52 234 53C234 53 234 54 234 55C235 55 235 56 235 57C235 58 235 59 235 59C236 59 236 61 236 63C236 66 236 66 237 67C238 67 238 67 238 69C238 70 238 71 238 71C239 72 239 73 239 74C238 76 239 77 240 79C241 81 242 83 242 83C242 84 242 85 242 85C243 86 244 88 245 90C246 92 247 94 248 94C248 94 249 94 250 95C251 95 253 95 253 95C254 95 255 96 256 97C258 99 258 99 259 99C260 98 261 98 265 100C266 100 268 100 272 101C274 101 276 102 278 102C281 103 282 103 284 102C284 102 285 103 285 103C285 105 285 105 287 104C288 103 289 103 289 104C288 105 290 106 291 105C292 104 292 105 293 105C294 106 294 107 296 106C297 106 298 106 299 107C299 107 300 108 301 108C301 108 302 108 302 108C302 109 303 109 304 109C305 109 306 110 307 111C308 112 309 113 310 113C311 113 318 117 318 118C319 119 319 120 320 120C321 120 322 120 323 120C327 122 328 122 337 113C338 112 340 111 340 111C340 111 342 110 343 109C345 108 347 106 348 104C350 103 352 101 354 100C355 98 357 97 358 96C358 96 360 94 361 93C362 91 363 90 364 90C365 89 371 84 371 82C372 82 373 80 374 80C380 75 382 74 383 74C384 74 386 76 386 77C386 78 386 79 386 79C386 79 387 80 387 82C387 83 387 84 388 84C390 86 391 89 390 91C390 91 389 93 389 94C389 96 389 98 388 99C388 101 388 102 390 106C391 111 392 111 391 116C391 122 392 124 393 130C394 133 392 142 391 144C390 145 390 146 390 147C390 149 389 149 388 150C387 151 386 151 386 153C386 153 385 155 385 155C384 156 382 160 382 162C383 164 382 165 382 166C381 168 381 168 382 170C383 173 383 177 382 180C381 181 381 181 382 182C385 187 385 192 383 194C382 194 382 195 382 196C382 198 382 199 380 200C379 201 379 202 380 204C380 205 380 205 380 206C379 206 379 208 380 209C381 209 381 210 381 211C381 211 381 212 382 213C382 214 383 214 382 216C382 218 383 219 384 219C386 220 389 225 388 227C387 229 387 230 389 232C389 232 390 234 390 235C390 236 391 237 393 239C394 239 395 240 395 241C395 242 396 243 396 244C397 244 398 245 398 246C399 247 399 249 400 250C402 251 402 253 401 256C401 258 401 258 402 260C404 261 407 267 407 269C407 270 408 270 409 269C410 268 411 268 412 268C413 268 414 267 416 265C418 263 419 262 420 262C421 262 422 261 423 260C425 259 430 257 434 257C435 257 439 256 442 256L448 256 454 259C460 262 461 262 461 264Line truncated

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><clipPath id="t"><rect width="512" height="512" rx="112"/></clipPath></defs><g clip-path="url(#t)"><rect width="512" height="512" fill="#7bb51c"/><path fill="#efe9dc" fill-rule="evenodd" d="M101.8 505.4C68.7 502.1 39.1 483.6 21.7 455.2C14.5 443.5 9.6 430.1 7.4 416.2C6.4 410.1 6.3 406.4 6.3 390.1L6.2 374.3 7.2 373.4C8.4 372.4 9.4 371.8 10.8 371.4C11.6 371.2 12 370.8 12.6 370C13.2 369.1 13.7 368.7 14.8 368.3C16 367.8 16.5 367.5 18.5 365.4C19.5 364.4 21.9 362.8 23.9 361.9C25.3 361.2 25.9 360.8 26.6 359.7C27.8 358.2 29.3 357 30.3 357C30.8 357 31.2 356.7 32.1 355.5C33.2 354.2 36.5 351 36.9 351C37 351 37.6 350.9 38.1 350.9C38.8 350.8 39.2 350.6 39.7 350.1C41.3 348 42.4 347.2 44.1 346.7C45.4 346.3 46.3 345.9 47.2 345C48.4 344 48.8 343.9 49.8 343.8C51.1 343.8 51.7 343.4 51.7 342.8C51.8 342.5 51.9 342.2 52.2 341.9C53 341.3 58.3 338.7 59.5 338.5C60.4 338.3 60.9 337.9 61.9 336.9C62.8 335.9 63.5 335.4 64.6 335C65.4 334.7 66.5 334.1 66.9 333.7C67.4 333.3 67.9 333 68.1 333C68.4 333 69.3 332.3 70.2 331.4C71.2 330.5 72.3 329.7 72.9 329.5C73.5 329.3 74.3 328.7 75 328C76.1 326.7 79.6 325 80.9 325C82.1 325 82.7 324.6 82.9 323.6C83.1 322.4 82.5 321.6 81.4 321.9C80.8 322 80.6 321.8 79.9 321.2C79.2 320.4 79.2 320.3 79.3 318.5C79.5 315.6 80.4 314 82.8 312.7C84 312 84.7 310.9 84.8 310C84.8 309.6 85.1 308.9 85.5 308.3C86.5 306.7 86.5 305.8 85.3 305.4C84.7 305.2 84.1 304.7 83.6 303.9C82.7 302.7 82.7 302.6 83 301.7C83.8 299 84.1 298.4 85.1 297.9C85.9 297.5 86.2 297.1 86.5 296C87.1 294.4 87.9 293.5 89.6 292.5C91 291.7 91.5 291 91.5 289.8C91.5 289.4 91.7 288.7 92 288.3C92.7 287.1 92.6 286 91.7 285.5C90.7 285.1 90.2 284.2 90.3 283.1C90.5 281.9 89.9 280.8 89.2 280.8C87.7 280.8 86.7 278.4 87.2 276.4C87.5 275.6 87.5 275.1 87.3 274.4C87.1 273.7 87.1 273.2 87.4 272.4C87.7 271.7 87.8 270.7 87.7 269.5C87.6 265.9 87.7 265.5 88.4 264.6C91 260.8 91 260.9 90.5 259.3C90.2 258.6 90.2 258.3 90.7 257.2C91.3 255.8 91.8 253 91.6 252.8C91.5 252.7 91.2 252.6 90.9 252.6C90.5 252.6 90.4 252.5 90.4 252C90.4 251.5 90.5 251.4 91 251.4C91.9 251.5 92.6 250.7 93.1 248.7C93.7 246.6 93.3 245.6 92 245.7C90 245.9 90.1 246.1 90.1 244.1C90.1 243.2 90.2 242.2 90.4 242.1C90.5 241.9 90.5 241.2 90.3 240.3C90 239.2 90 238.9 90.3 238.5C90.5 238.3 90.8 237.5 90.9 236.8C90.9 236 91.1 235 91.3 234.5C91.5 233.7 91.5 233.7 90.6 232.9C89.8 232.2 89.7 232.1 89.9 231.2C90 230.3 89.9 230 89.5 229.5C88.7 228.5 88.9 226.8 89.9 226.2C90.3 225.9 90.4 224.5 90 223.7C89.8 223.3 89.8 222.9 90 222.4C90.3 221.7 90.6 221.6 91.2 222.1C91.9 222.8 92.2 222.6 92.2 221.5C92.3 220.1 91.9 219.5 90.6 219C89.2 218.3 89.2 217.6 90.5 216.9C91.1 216.6 91.1 216.5 91.1 214.7C91.1 212.5 91.4 212 92.4 212.5C93.2 212.8 93.9 211.9 93.7 210.6C93.6 209.5 93.9 208.3 94.5 207.6C94.8 207.3 95.1 206.4 95.2 205.4C95.6 203.3 95.6 203.3 96.8 202.8C97.8 202.4 97.9 202.2 98.2 201C98.4 200.2 98.4 199.4 98.4 199.1C98.2 198.4 98.7 195.6 99.2 194.5C99.5 193.9 99.9 193.5 100.5 193.3C101.1 193.1 101.5 192.8 101.9 192C102.2 191.5 102.6 190.9 102.8 190.7C103 190.5 103.3 189.8 103.4 189C104 186.2 104.8 185.4 105.3 187.2C105.6 188.4 106.1 188.2 105.8 187.1C105.4 185.2 106.3 181.1 107.5 179.7C107.8 179.3 108.2 178.4 108.4 177.5C108.6 176.5 108.9 175.8 109.3 175.4C110.2 174.6 110.4 173.3 110 171.2C109.7 169.1 109.7 167 110 166.1C110.2 165.6 110.1 164.9 109.8 163.3C109.4 160.8 109.4 160.4 110.4 160C110.7 159.9 111.1 159.6 111.1 159.4C111.3 158.9 110.8 157.2 110.2 156.4C109.4 155.4 109.7 154.6 110.8 154.7C111.6 154.7 111.8 154.6 112.5 153.9C113.4 152.8 113.4 152.1 112.5 150.9C111.3 149.3 111.8 148.2 113.2 149.3C113.8 149.9 114.1 150 115.1 149.9C116.8 149.8 117.4 149.1 117.1 147.4C117 146.3 117 146.2 117.8 145.4C118.3 145 118.8 144.3 118.8 144C119.1 143.1 120.4 141.8 121.1 141.8C121.8 141.8 123 140.5 123 139.8C123 139 123.8 137 124.2 136.8C124.3 136.8 124.7 136.8 124.9 137C125.6 137.6 126.1 136.8 126.6 134.4C126.7 134 127.2 133.3 127.6 132.8C128.6 131.8 129.5 129.9 129.5 129.1C129.5 128.7 130.4 127.7 135.2 122.9C136 122.1 136.9 121.1 137.1 120.7C137.4 120.3 137.8 120 138.1 120C139.2 120 140.3 119.1 140.3 118.2C140.2 117.7 140.4 117.2 140.6 117.1C141.1 116.6 142.8 116.5 143.6 116.9C144.7 117.5 146.4 117.2 146.9 116.3C147.4 115.6 148.7 115 149.8 115C150.5 115 150.9 114.8 151.8 113.9C152.5 113.3 153.3 112.7 153.8 112.6C154.2 112.5 154.9 112.2 155.3 112C156.1 111.4 156.8 111.4 158.2 111.9C159.9 112.4 162.8 111.6 165.3 109.7C165.9 109.3 166.5 109 166.8 109C167.7 109 168.5 108 168.8 106.6C169.3 104 169.5 103.3 170.3 102.1C171.6 100.3 171.8 99.8 172 98.3C172.1 97.5 172.5 96.2 172.9 95.4C173.2 94.7 173.5 93.9 173.5 93.7C173.5 92.6 175.1 90.8 176.3 90.5C177.6 90.1 178.1 89.3 178.1 87.7C178 85.7 178.2 85.3 179.2 84.8C180 84.4 180.1 84.2 180.6 82.3C181.4 79.3 182.1 78.8 183.3 79.8C183.7 80.1 184.2 80.2 184.4 80.2C185.1 80.1 185.6 78.4 185.4 77C185.3 76 185.4 75.5 186.2 73.8C187.3 71.5 187.8 71 188.8 71C189.7 71 190 70.4 190.2 68.6C190.3 67.7 190.5 66.9 190.9 66.4C191.6 65.4 191.8Line truncated

After

Width:  |  Height:  |  Size: 664 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><clipPath id="t"><rect width="512" height="512" rx="112"/></clipPath></defs><g clip-path="url(#t)"><rect width="512" height="512" fill="#7bb51c"/><path fill="#efe9dc" fill-rule="evenodd" d="M103 505C61 502 24 473 12 433C7 420 7 417 7 394L6 374 8 373C9 372 10 372 11 372C11 372 12 371 13 370C13 369 14 368 15 368C15 368 17 367 18 366C19 365 21 363 23 362C24 362 26 360 27 359C28 358 30 357 30 357C30 357 32 356 34 354C35 352 37 351 38 351C38 351 40 350 41 349C42 348 43 347 44 347C45 346 47 346 47 345C48 344 49 344 50 344C50 344 51 344 52 343C52 342 58 338 60 338C60 338 61 338 62 337C62 336 64 335 65 335C68 333 69 333 71 331C72 330 73 330 73 330C73 330 74 329 75 328C75 327 77 326 79 326C83 324 85 322 82 322C78 322 79 315 83 313C84 312 84 311 84 311C84 310 85 309 86 308C87 306 86 306 85 306C82 305 82 300 85 298C86 297 87 296 87 296C87 295 88 294 89 293C91 291 92 290 92 290C92 289 92 288 92 288C93 288 93 286 92 285C91 285 90 284 90 284C90 282 90 281 88 280C87 280 87 279 87 276C88 265 88 265 89 263C90 262 91 261 90 260C90 259 91 257 91 256C92 254 92 253 91 253C90 252 90 252 91 252C92 252 93 251 93 248C94 246 93 246 91 246C90 246 90 241 91 237C91 234 91 234 91 233C90 233 90 232 90 231C90 231 90 230 89 229C89 228 89 228 89 227C90 226 90 225 90 224C90 222 90 222 91 222C93 223 92 220 91 219L89 218 90 217C91 216 91 215 91 214C91 213 91 212 92 212C93 212 93 212 94 210C94 210 94 208 95 207C95 206 95 205 95 205C95 204 96 203 97 202C98 201 98 201 98 198C98 196 99 195 100 194C102 192 103 190 104 188C104 187 104 187 105 186C105 186 106 186 106 185C106 184 107 182 107 180C108 179 108 177 108 177C108 177 109 176 109 175C110 174 110 167 110 163C109 161 110 161 110 160C111 159 111 159 111 157C110 155 110 154 111 155C113 155 114 152 112 151C112 149 112 149 113 150C115 150 117 149 117 148C117 146 120 142 121 141C122 141 123 140 123 139C123 138 124 137 125 137C126 137 126 136 127 135C127 134 127 133 128 132C129 131 130 130 130 129C130 129 138 120 139 120C139 120 140 119 140 118C141 117 143 116 144 117C145 118 146 117 147 116C148 115 149 115 149 115C150 115 151 115 152 114C153 113 156 112 159 112C162 112 162 111 166 109C168 108 168 108 169 106C169 104 170 103 170 102C171 101 172 99 172 98C173 95 175 91 176 90C177 90 178 89 178 88C178 86 178 85 179 85C180 85 180 83 181 82C181 80 181 80 183 80C185 80 185 79 186 77C186 74 187 72 189 71C190 71 190 70 190 69C190 69 190 67 191 66C192 65 192 63 193 62C193 61 194 60 194 60C196 60 196 59 196 57C196 56 197 55 198 54C199 53 201 51 202 49C203 48 204 46 205 45C205 45 206 44 206 43C206 41 211 37 213 37C214 37 215 37 218 35C224 32 226 32 229 35C230 36 231 37 231 38C231 39 231 40 232 40C232 41 233 44 233 47C233 48 233 49 234 50C234 51 234 52 234 53C234 53 234 54 234 55C235 55 235 56 235 57C235 58 235 59 235 59C236 59 236 61 236 63C236 66 236 66 237 67C238 67 238 67 238 69C238 70 238 71 238 71C239 72 239 73 239 74C238 76 239 77 240 79C241 81 242 83 242 83C242 84 242 85 242 85C243 86 244 88 245 90C246 92 247 94 248 94C248 94 249 94 250 95C251 95 253 95 253 95C254 95 255 96 256 97C258 99 258 99 259 99C260 98 261 98 265 100C266 100 268 100 272 101C274 101 276 102 278 102C281 103 282 103 284 102C284 102 285 103 285 103C285 105 285 105 287 104C288 103 289 103 289 104C288 105 290 106 291 105C292 104 292 105 293 105C294 106 294 107 296 106C297 106 298 106 299 107C299 107 300 108 301 108C301 108 302 108 302 108C302 109 303 109 304 109C305 109 306 110 307 111C308 112 309 113 310 113C311 113 318 117 318 118C319 119 319 120 320 120C321 120 322 120 323 120C327 122 328 122 337 113C338 112 340 111 340 111C340 111 342 110 343 109C345 108 347 106 348 104C350 103 352 101 354 100C355 98 357 97 358 96C358 96 360 94 361 93C362 91 363 90 364 90C365 89 371 84 371 82C372 82 373 80 374 80C380 75 382 74 383 74C384 74 386 76 386 77C386 78 386 79 386 79C386 79 387 80 387 82C387 83 387 84 388 84C390 86 391 89 390 91C390 91 389 93 389 94C389 96 389 98 388 99C388 101 388 102 390 106C391 111 392 111 391 116C391 122 392 124 393 130C394 133 392 142 391 144C390 145 390 146 390 147C390 149 389 149 388 150C387 151 386 151 386 153C386 153 385 155 385 155C384 156 382 160 382 162C383 164 382 165 382 166C381 168 381 168 382 170C383 173 383 177 382 180C381 181 381 181 382 182C385 187 385 192 383 194C382 194 382 195 382 196C382 198 382 199 380 200C379 201 379 202 380 204C380 205 380 205 380 206C379 206 379 208 380 209C381 209 381 210 381 211C381 211 381 212 382 213C382 214 383 214 382 216C382 218 383 219 384 219C386 220 389 225 388 227C387 229 387 230 389 232C389 232 390 234 390 235C390 236 391 237 393 239C394 239 395 240 395 241C395 242 396 243 396 244C397 244 398 245 398 246C399 247 399 249 400 250C402 251 402 253 401 256C401 258 401 258 402 260C404 261 407 267 407 269C407 270 408 270 409 269C410 268 411 268 412 268C413 268 414 267 416 265C418 263 419 262 420 262C421 262 422 261 423 260C425 259 430 257 434 257C435 257 439 256 442 256L448 256 454 259C460 262 461 262 461 264Line truncated

After

Width:  |  Height:  |  Size: 348 KiB

+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Regenerate every app icon from assets/app-icon/designcraft.svg.
#
# Needs: resvg (brew install resvg / cargo install resvg). On macOS, iconutil also writes the
# .icns. The outputs are committed, so builds and packaging never need these tools.
#
# packaging/icons.sh
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DIR="$ROOT/assets/app-icon"
SVG="$DIR/designcraft.svg"
ID="ai.storyteller.designcraft"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
command -v resvg >/dev/null || { echo "error: resvg not found (brew install resvg)" >&2; exit 1; }
# The artwork is a full-bleed 512 tile (rx=112). Windows and Linux use it as is. macOS icons sit on
# Apple's grid: an 824/1024 body with a transparent margin, so widen the viewBox to 512*1024/824.
MAC="$TMP/mac.svg"
sed 's/viewBox="0 0 512 512"/viewBox="-62.136 -62.136 636.272 636.272"/' "$SVG" >"$MAC"
grep -q 'viewBox="-62.136' "$MAC" || { echo "error: expected viewBox=\"0 0 512 512\" in $SVG" >&2; exit 1; }
render() { resvg -w "$2" -h "$2" "$1" "$3" </dev/null; }
render "$SVG" 1024 "$DIR/designcraft-1024.png"
# Runtime window/Dock icon on macOS (embedded by apps/designcraft); other platforms use hicolor 256.
render "$MAC" 512 "$DIR/designcraft-macos-512.png"
# Linux hicolor theme.
for s in 16 24 32 48 64 128 256 512; do
mkdir -p "$DIR/hicolor/${s}x${s}/apps"
render "$SVG" "$s" "$DIR/hicolor/${s}x${s}/apps/$ID.png"
done
mkdir -p "$DIR/hicolor/scalable/apps"
cp "$DIR/designcraft-small.svg" "$DIR/hicolor/scalable/apps/$ID.svg"
# Windows .ico.
ICO_PNGS=()
for s in 16 20 24 32 40 48 64 128 256; do
render "$SVG" "$s" "$TMP/ico-$s.png"
ICO_PNGS+=("$TMP/ico-$s.png")
done
(cd "$ROOT" && cargo run -q -p xtask -- ico "$DIR/designcraft.ico" "${ICO_PNGS[@]}")
# macOS .icns.
if command -v iconutil >/dev/null; then
SET="$TMP/designcraft.iconset"
mkdir -p "$SET"
for s in 16 32 128 256 512; do
render "$MAC" "$s" "$SET/icon_${s}x${s}.png"
render "$MAC" $((s * 2)) "$SET/icon_${s}x${s}@2x.png"
done
iconutil -c icns -o "$DIR/designcraft.icns" "$SET"
else
echo "warning: iconutil not found (macOS only); designcraft.icns not regenerated" >&2
fi
echo "icons written to $DIR"
@@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Name=DesignCraft
GenericName=Page Layout
Comment=Lay out magazines, books and print documents
Exec=designcraft %F
TryExec=designcraft
Icon=ai.storyteller.designcraft
Terminal=false
StartupNotify=true
StartupWMClass=ai.storyteller.designcraft
Categories=Graphics;Publishing;
Keywords=layout;page;desktop publishing;dtp;magazine;book;indesign;idml;typesetting;
+104
View File
@@ -0,0 +1,104 @@
//! `cargo xtask ico <out.ico> <in.png>…`: pack PNG images into a Windows `.ico`.
//!
//! Vista and later read PNG-compressed icon entries, so each PNG is stored as is. The size comes
//! from the PNG's IHDR chunk; 256 px is written as 0, per the ICO format.
use std::path::Path;
/// Width and height from a PNG's IHDR chunk.
pub fn png_size(png: &[u8]) -> Result<(u32, u32), String> {
const SIG: &[u8] = b"\x89PNG\r\n\x1a\n";
if png.len() < 24 || &png[..8] != SIG || &png[12..16] != b"IHDR" {
return Err("not a PNG file".into());
}
let w = u32::from_be_bytes([png[16], png[17], png[18], png[19]]);
let h = u32::from_be_bytes([png[20], png[21], png[22], png[23]]);
Ok((w, h))
}
/// An ICO file holding `pngs`, which must be square and at most 256 px.
pub fn pack(pngs: &[Vec<u8>]) -> Result<Vec<u8>, String> {
if pngs.is_empty() || pngs.len() > u16::MAX as usize {
return Err("need at least one PNG".into());
}
let mut out = Vec::new();
out.extend_from_slice(&0u16.to_le_bytes()); // reserved
out.extend_from_slice(&1u16.to_le_bytes()); // type: icon
out.extend_from_slice(&(pngs.len() as u16).to_le_bytes());
let mut offset = 6 + 16 * pngs.len();
for png in pngs {
let (w, h) = png_size(png)?;
if w != h || w == 0 || w > 256 {
return Err(format!("icon images must be square and at most 256 px, got {w}x{h}"));
}
let dim = if w == 256 { 0 } else { w as u8 };
out.extend_from_slice(&[dim, dim, 0, 0]); // width, height, palette size, reserved
out.extend_from_slice(&1u16.to_le_bytes()); // colour planes
out.extend_from_slice(&32u16.to_le_bytes()); // bits per pixel
out.extend_from_slice(&(png.len() as u32).to_le_bytes());
out.extend_from_slice(&(offset as u32).to_le_bytes());
offset += png.len();
}
for png in pngs {
out.extend_from_slice(png);
}
Ok(out)
}
pub fn run(args: &[&str]) -> Result<(), String> {
let [out, inputs @ ..] = args else {
return Err("usage: cargo xtask ico <out.ico> <in.png>…".into());
};
let pngs = inputs.iter().map(|p| std::fs::read(p).map_err(|e| format!("read {p}: {e}"))).collect::<Result<Vec<_>, _>>()?;
let ico = pack(&pngs)?;
std::fs::write(Path::new(out), &ico).map_err(|e| format!("write {out}: {e}"))?;
println!("{out}: {} images, {} bytes", pngs.len(), ico.len());
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn fake_png(size: u32) -> Vec<u8> {
let mut v = b"\x89PNG\r\n\x1a\n\0\0\0\x0dIHDR".to_vec();
v.extend_from_slice(&size.to_be_bytes());
v.extend_from_slice(&size.to_be_bytes());
v.extend_from_slice(&[8, 6, 0, 0, 0, 1, 2, 3, 4]);
v
}
#[test]
fn packs_directory_and_payloads() {
let a = fake_png(16);
let b = fake_png(256);
let ico = pack(&[a.clone(), b.clone()]).unwrap();
assert_eq!(&ico[..6], &[0, 0, 1, 0, 2, 0]);
// Entry 0: 16 px at offset 6 + 32.
assert_eq!(&ico[6..8], &[16, 16]);
assert_eq!(u32::from_le_bytes(ico[14..18].try_into().unwrap()), a.len() as u32);
assert_eq!(u32::from_le_bytes(ico[18..22].try_into().unwrap()), 38);
// Entry 1: 256 px stored as 0, right after the first payload.
assert_eq!(&ico[22..24], &[0, 0]);
assert_eq!(u32::from_le_bytes(ico[34..38].try_into().unwrap()), 38 + a.len() as u32);
assert_eq!(&ico[38..38 + a.len()], &a[..]);
assert_eq!(&ico[38 + a.len()..], &b[..]);
}
#[test]
fn rejects_bad_input() {
assert!(pack(&[]).is_err());
assert!(pack(&[fake_png(512)]).is_err());
assert!(pack(&[b"GIF89a".to_vec()]).is_err());
assert_eq!(png_size(&fake_png(48)).unwrap(), (48, 48));
}
#[test]
fn committed_icon_is_valid() {
let path = crate::root().join("assets/app-icon/designcraft.ico");
let Ok(ico) = std::fs::read(&path) else { return };
assert_eq!(&ico[..4], &[0, 0, 1, 0]);
let n = u16::from_le_bytes([ico[4], ico[5]]) as usize;
assert!(n >= 4, "expected several sizes in {}", path.display());
}
}
+4
View File
@@ -4,6 +4,7 @@
//! invoked through `std::process::Command`.
mod assets;
mod ico;
mod layers;
mod parity;
mod stats;
@@ -23,6 +24,8 @@ commands:
show where test corpora live; --download fetches PngSuite into corpus/pngsuite
stats [--exact] count tests and lines per crate (--exact: ask the test harness via `-- --list`)
parity recompute the InDesign feature-parity summary in docs/parity.md
ico <out.ico> <in.png>...
pack square PNGs (<= 256 px) into a Windows .ico (see packaging/icons.sh)
";
fn main() -> ExitCode {
@@ -36,6 +39,7 @@ fn main() -> ExitCode {
Some("corpus") => cmd_corpus(rest.contains(&"--download")),
Some("stats") => stats::run(&root(), rest.contains(&"--exact")),
Some("parity") => parity::run(&root()),
Some("ico") => ico::run(&rest),
Some("-h" | "--help" | "help") | None => {
print!("{USAGE}");
Ok(())