6 Commits

Author SHA1 Message Date
Marcos Paulo 593d923f09 chore: release 0.9.3 2026-10-12 21:40:57 -03:00
Marcos Paulo 660f26e2fd feat(ui): Catppuccin themes, View > Preferences > Theme and a custom title bar
Adds the four Catppuccin themes, the View > Preferences > Theme menu, and a window-drawn
title bar on Windows and Linux (Settings > Appearance > Use System Title Bar opts out).
macOS gets the traffic lights over the top row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:56:53 -03:00
Marcos Paulo fe23624202 build: use the masi/craft-libs fork
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:53:00 -03:00
Marcos Paulo 8b3d7e8516 build(windows): set working directory to repo root in package.ps1 2026-10-12 20:53:00 -03:00
Marcos Paulo 115cf2b972 build(windows): package standalone effectcraft-cli zip and auto-detect rc.exe 2026-10-12 20:53:00 -03:00
Marcos Paulo 91efb894f7 chore: debrand ArtCraft and Discord references
Remove Discord button, Help menu links, sibling-app links and brand assets; rewrite docs and packaging URLs. Add .debrand rules.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:53:00 -03:00
78 changed files with 778 additions and 562 deletions
+28
View File
@@ -0,0 +1,28 @@
# Strip upstream (ArtCraft / Discord) branding. Run: perl tools/craft-skills/skills/debrand/debrand.pl . (--check in CI)
delete docs/brand
# MIT/Apache texts stay verbatim. The copyright holder line stays everywhere (legal requirement).
keep ^(LICENSE-(MIT|APACHE)|\.debrand)$
dropline ^ATTRIBUTION\.md$ :: \|\s*`docs/brand/
sub (?s)\nThe ArtCraft name, wordmark and logos in docs/brand/.*?LICENSE-brand\.txt\.\n =>
sub drawn in ArtCraft, vectorised => drawn by hand, vectorised
sub drawing by the project owner in ArtCraft\) => drawing by the project owner)
sub made for this project by the project owner in ArtCraft, then => made for this project by the project owner by hand, then
sub the project owner drew it in ArtCraft => the project owner drew it by hand
sub owner's ArtCraft unicorn drawing => owner's unicorn drawing
sub The owner's ArtCraft drawing => The owner's drawing
sub https://getartcraft\.com/apps/effectcraft => https://github.com/storytold/effectcraft
sub https://discord\.gg/artcraft => https://github.com/storytold/effectcraft/issues
sub https://getartcraft\.com => https://example.com
sub \[Discord\]\(https://github\.com/storytold/effectcraft/issues\) and in\nGitHub issues => GitHub issues
sub Come tell us what matters most to you on \[Discord\]\(https://github\.com/storytold/effectcraft/issues\)\. => Tell us what matters most to you in [GitHub issues](https://github.com/storytold/effectcraft/issues).
sub links \(Help menu, Discord, Settings\) => links (Help menu, Settings)
sub the Discord and other links do nothing => the links do nothing
sub \(6 October, on Discord\) => (6 October)
sub the Tools bar and About dialog carry the ArtCraft mark => the Tools bar and About dialog carry the app mark
forbid (?i)artcraft(?! Team and the EffectCraft contributors)
forbid (?i)discord
sub ffmpeg export, Discord presence, => ffmpeg export,
dropline ^crates/ui-egui/src/i18n(?:\.rs|/ui\.rs)$ :: \s*\(".*(?:ArtCraft|Discord)
sub (?m)^ \(\n "[^"\n]*ArtCraft[^"\n]*",\n "[^"\n]*",\n \),\n =>
sub \(#612, reported on Discord\) => (#612, reported by a user)
sub Reported on Discord: => Reported by a user:
+3
View File
@@ -0,0 +1,3 @@
[submodule "tools/craft-skills"]
path = tools/craft-skills
url = https://git.marcospaulo.dev.br/storytold/craft-skills.git
-6
View File
@@ -76,12 +76,6 @@ reproduce an image (for example, point lists traced from someone else's icon).
`crates/engine/src/demo.rs`, generators in `crates/effects`) are original work under the project licence
and are listed in `ATTRIBUTION.md`.
7. **When in doubt, leave it out** and draw or generate it yourself.
8. **The one exception: first-party ArtCraft brand marks.** The ArtCraft name and logos in `docs/brand/`
are trademarks of the ArtCraft Team, not open source, usable only unmodified and only in the context of
EffectCraft under `docs/brand/LICENSE-brand.txt` (forks and modified versions must remove them). They still
need a sidecar and an `ATTRIBUTION.md` row (licence `LicenseRef-ArtCraft-Trademark`). No other
non-open asset is allowed, and this exception never covers third-party marks (Adobe, Discord, GitHub
and other logos stay out; draw a generic icon instead).
## 2. Clean-room code
+15 -33
View File
@@ -18,21 +18,21 @@ iconography, images or other Adobe assets; open licences only; every asset attri
| `assets/fonts/JetBrainsMono-Regular.ttf` | The JetBrains Mono Project Authors | https://github.com/JetBrains/JetBrainsMono | OFL-1.1 (`assets/fonts/OFL-JetBrainsMono.txt`) |
| `apps/effectcraft-web/web/favicon.svg` | EffectCraft contributors | Original work: web app icon, a copy of `assets/app-icon/effectcraft-small.svg` (the unicorn app icon) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `packaging/macos/dmg/background.svg` | @XusBadia | Original work: macOS DMG Finder window background, derived from the EffectCraft app icon (`assets/app-icon/effectcraft.svg`); rendered to a TIFF by `packaging/macos/package.sh` at package time | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) |
| `assets/app-icon/effectcraft-1024.png` | EffectCraft contributors | Original work: effectCraft app icon, 1024 px (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft-macos-512.png` | EffectCraft contributors | Original work: effectCraft runtime Dock icon (macOS) (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft-small.svg` | EffectCraft contributors | Original work: effectCraft app icon, light vector (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft.icns` | EffectCraft contributors | Original work: effectCraft macOS app icon (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft.ico` | EffectCraft contributors | Original work: effectCraft Windows app icon (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft.svg` | EffectCraft contributors | Original work: effectCraft app icon, master vector (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 128x128 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 16x16 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 24x24 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 256x256 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 32x32 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 48x48 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 512x512 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 64x64 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor scalable (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft-1024.png` | EffectCraft contributors | Original work: effectCraft app icon, 1024 px (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft-macos-512.png` | EffectCraft contributors | Original work: effectCraft runtime Dock icon (macOS) (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft-small.svg` | EffectCraft contributors | Original work: effectCraft app icon, light vector (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft.icns` | EffectCraft contributors | Original work: effectCraft macOS app icon (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft.ico` | EffectCraft contributors | Original work: effectCraft Windows app icon (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/effectcraft.svg` | EffectCraft contributors | Original work: effectCraft app icon, master vector (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 128x128 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 16x16 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 24x24 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 256x256 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 32x32 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 48x48 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 512x512 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 64x64 (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor scalable (owner's unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `crates/lottie/tests/fixtures/bouncing-ball.json` | EffectCraft contributors | Original work: hand-written Lottie test fixture (bouncing ball, eased position keys) | MIT OR Apache-2.0 |
| `crates/lottie/tests/fixtures/shapes-gradient.json` | EffectCraft contributors | Original work: hand-written Lottie test fixture (star, rectangle, gradient fill, dashed stroke, trim paths) | MIT OR Apache-2.0 |
| `crates/lottie/tests/fixtures/precomp-matte-text.json` | EffectCraft contributors | Original work: hand-written Lottie test fixture (precomp, track matte, parented text) | MIT OR Apache-2.0 |
@@ -103,22 +103,4 @@ configuration from these Apache-2.0 projects:
- `crates/segment/src/mobilesam.rs` follows the MobileSAM, TinyViT and Segment Anything
architectures and their Apache-2.0 reference code.
## First-party brand marks
The ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team. They are not open source and are not
covered by the project licence; they may be used only unmodified and only as part of this repository and EffectCraft,
under [`docs/brand/LICENSE-brand.txt`](docs/brand/LICENSE-brand.txt). Forks and modified versions must remove them
(AGENTS.md §1.8).
| File | Author | Source | Licence |
|---|---|---|---|
| `docs/brand/artcraft-logo-white.png` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark, light ink for dark backgrounds) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-logo-white.svg` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark, light ink for dark backgrounds) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-logo.png` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-logo.svg` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-mark-black.png` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), black) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-mark-black.svg` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), black) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-mark.png` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), blue) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
| `docs/brand/artcraft-mark.svg` | ArtCraft Team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), blue) | ArtCraft brand terms, not open source (`docs/brand/LICENSE-brand.txt`) |
- Appearance theme previews, drawn procedurally from original shapes: `crates/ui-egui/src/panels/settings.rs` (original work, MIT OR Apache-2.0).
+1 -1
View File
@@ -19,7 +19,7 @@ EffectCraft is a clean-room, open-source, pure-Rust motion graphics and visual e
- **Property tree:** everything animatable is a `Property` in the layer's `PropGroup` tree, addressed by paths (`transform/position`, `effects/#1/blurriness`, `@uid`).
- **Everything is a command** (`crates/engine`): id, label, menu path, shortcut, params, enabled(), run(). UI, CLI, control channel and MCP dispatch by id.
- **Everything is agent-drivable:** every interactive widget registers an automation id; UI state is serde.
- **Community links** (Help menu, About, header Discord button): https://discord.gg/artcraft, https://getartcraft.com, https://getartcraft.com/apps/effectcraft, https://github.com/storytold/effectcraft.
- **Project links** (Help menu, About): https://github.com/storytold/effectcraft.
- **Quality gates** before every commit: `cargo xtask ci` (fmt, clippy -D warnings, tests, layers, assets, wasm).
- **Commits:** one task id per commit (`M6.3: trim paths`). Only green states.
+1 -2
View File
@@ -42,5 +42,4 @@ cargo xtask ci # what every commit must pass
## Talk to us
Questions, ideas and bug reports are welcome on [Discord](https://discord.gg/artcraft) and in
GitHub issues.
Questions, ideas and bug reports are welcome on GitHub issues.
Generated
+37 -37
View File
@@ -965,7 +965,7 @@ dependencies = [
[[package]]
name = "craft-ui"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=5c874d51eeb3de70e735c8ce87a59a7dedd47675#5c874d51eeb3de70e735c8ce87a59a7dedd47675"
source = "git+https://git.marcospaulo.dev.br/masi/craft-libs?rev=22544b6d619d4f16779b8bdfa3f0b642a86fc065#22544b6d619d4f16779b8bdfa3f0b642a86fc065"
dependencies = [
"egui",
"serde",
@@ -1143,7 +1143,7 @@ dependencies = [
[[package]]
name = "effectcraft"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"cpal",
"effectcraft-engine",
@@ -1168,7 +1168,7 @@ dependencies = [
[[package]]
name = "effectcraft-automation"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-engine",
"effectcraft-host",
@@ -1180,14 +1180,14 @@ dependencies = [
[[package]]
name = "effectcraft-av1enc"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"filmcraft-av1",
]
[[package]]
name = "effectcraft-cli"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"cpu-time",
"effectcraft-automation",
@@ -1203,7 +1203,7 @@ dependencies = [
[[package]]
name = "effectcraft-color"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"proptest",
"serde",
@@ -1211,7 +1211,7 @@ dependencies = [
[[package]]
name = "effectcraft-effects"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-geom",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]]
name = "effectcraft-engine"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-effects",
@@ -1261,7 +1261,7 @@ dependencies = [
[[package]]
name = "effectcraft-export"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-av1enc",
"effectcraft-color",
@@ -1291,7 +1291,7 @@ dependencies = [
[[package]]
name = "effectcraft-expr"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"boa_engine",
"effectcraft-color",
@@ -1308,7 +1308,7 @@ dependencies = [
[[package]]
name = "effectcraft-geom"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"proptest",
"serde",
@@ -1316,7 +1316,7 @@ dependencies = [
[[package]]
name = "effectcraft-gpu"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"bytemuck",
"effectcraft-color",
@@ -1338,14 +1338,14 @@ dependencies = [
[[package]]
name = "effectcraft-hevcenc"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"filmcraft-hevc",
]
[[package]]
name = "effectcraft-host"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-engine",
"effectcraft-export",
@@ -1369,7 +1369,7 @@ dependencies = [
[[package]]
name = "effectcraft-interchange"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-keyframe",
@@ -1388,7 +1388,7 @@ dependencies = [
[[package]]
name = "effectcraft-keyframe"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-geom",
@@ -1400,7 +1400,7 @@ dependencies = [
[[package]]
name = "effectcraft-lottie"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-effects",
@@ -1417,7 +1417,7 @@ dependencies = [
[[package]]
name = "effectcraft-media"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-model",
"effectcraft-pdf",
@@ -1444,7 +1444,7 @@ dependencies = [
[[package]]
name = "effectcraft-model"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-geom",
"image",
@@ -1456,14 +1456,14 @@ dependencies = [
[[package]]
name = "effectcraft-opusenc"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"filmcraft-opus",
]
[[package]]
name = "effectcraft-path"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-geom",
"effectcraft-keyframe",
@@ -1475,7 +1475,7 @@ dependencies = [
[[package]]
name = "effectcraft-pdf"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-raster",
"effectcraft-svg",
@@ -1489,7 +1489,7 @@ dependencies = [
[[package]]
name = "effectcraft-plugin"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-effects",
"effectcraft-keyframe",
@@ -1501,7 +1501,7 @@ dependencies = [
[[package]]
name = "effectcraft-project"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-geom",
@@ -1514,14 +1514,14 @@ dependencies = [
[[package]]
name = "effectcraft-psd"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"miniz_oxide 0.8.9",
]
[[package]]
name = "effectcraft-raster"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-geom",
@@ -1530,7 +1530,7 @@ dependencies = [
[[package]]
name = "effectcraft-render"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-color",
"effectcraft-effects",
@@ -1552,7 +1552,7 @@ dependencies = [
[[package]]
name = "effectcraft-script"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"boa_engine",
"effectcraft-engine",
@@ -1569,7 +1569,7 @@ dependencies = [
[[package]]
name = "effectcraft-segment"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"miniz_oxide 0.8.9",
"ndarray",
@@ -1578,7 +1578,7 @@ dependencies = [
[[package]]
name = "effectcraft-svg"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-geom",
"effectcraft-path",
@@ -1590,7 +1590,7 @@ dependencies = [
[[package]]
name = "effectcraft-text"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-keyframe",
"harfrust",
@@ -1602,7 +1602,7 @@ dependencies = [
[[package]]
name = "effectcraft-time"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"proptest",
"serde",
@@ -1610,7 +1610,7 @@ dependencies = [
[[package]]
name = "effectcraft-track"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-raster",
"effectcraft-segment",
@@ -1622,7 +1622,7 @@ dependencies = [
[[package]]
name = "effectcraft-ui-egui"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"craft-ui",
"effectcraft-color",
@@ -1654,11 +1654,11 @@ dependencies = [
[[package]]
name = "effectcraft-vp9enc"
version = "0.9.2"
version = "0.9.3"
[[package]]
name = "effectcraft-web"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"effectcraft-engine",
"effectcraft-export",
@@ -6468,7 +6468,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.9.2"
version = "0.9.3"
dependencies = [
"serde_json",
]
+2 -2
View File
@@ -4,7 +4,7 @@ members = ["crates/*", "apps/*", "xtask"]
default-members = ["crates/*", "apps/*"]
[workspace.package]
version = "0.9.2"
version = "0.9.3"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
@@ -99,7 +99,7 @@ pollster = "0.4"
# ui
# Shared frontend components; keep this dependency above the engine boundary.
craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "5c874d51eeb3de70e735c8ce87a59a7dedd47675", features = ["serde"] }
craft-ui = { version = "0.1.0", git = "https://git.marcospaulo.dev.br/masi/craft-libs", rev = "22544b6d619d4f16779b8bdfa3f0b642a86fc065", features = ["serde"] }
egui = "0.36"
eframe = { version = "0.36", default-features = false }
egui_kittest = "0.36"
-4
View File
@@ -3,10 +3,6 @@ Copyright (c) 2026 ArtCraft Team and the EffectCraft contributors.
Licensed under the MIT License or the Apache License, Version 2.0, at your option
(LICENSE-MIT, LICENSE-APACHE).
The ArtCraft name, wordmark and logos in docs/brand/ are trademarks of the ArtCraft Team. They are
not open source and are not covered by the license above; their terms are in
docs/brand/LICENSE-brand.txt.
Third-party material
This product bundles third-party material under its own licenses, listed below. The per-file
+11 -70
View File
@@ -1,13 +1,3 @@
<p align="center">
<a href="https://getartcraft.com/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/brand/artcraft-logo-white.svg">
<img alt="ArtCraft" src="docs/brand/artcraft-logo.svg" width="200">
</picture>
</a>
</p>
<h1 align="center">EffectCraft</h1>
<p align="center">
@@ -26,26 +16,12 @@
<img alt="License: MIT OR Apache-2.0" src="https://img.shields.io/badge/license-MIT%20%2F%20Apache--2.0-555?style=flat-square">
</p>
<p align="center">
<a href="https://discord.gg/artcraft"><img alt="Join the ArtCraft community on Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
</p>
<p align="center">
<a href="https://getartcraft.com/apps/effectcraft"><b>EffectCraft on getartcraft.com</b></a> ·
<a href="https://getartcraft.com/">ArtCraft</a> ·
<a href="https://getartcraft.com/apps">All Crafting Apps</a>
</p>
<br>
<p align="center">
<img src="docs/images/effectcraft-hero.png" alt="EffectCraft's main window: the animated demo composition in the Composition panel, the Project panel, a Timeline with text, shape and solid layers, and the Properties panel showing the selected text layer's transform, font and paragraph settings" width="100%">
</p>
> [!NOTE]
> **ArtCraft is a community of artists from all walks of life.** Digital, generative, music,
> games &mdash; if you make things, you're one of us. **[Come say hi on Discord](https://discord.gg/artcraft).**
<p align="center">
<a href="#what-effectcraft-is">What it is</a> ·
<a href="#animate">Animate</a> ·
@@ -254,7 +230,7 @@ send us right now.
## Downloads
**New to EffectCraft?** Download it from the [EffectCraft page on getartcraft.com](https://getartcraft.com/apps/effectcraft). That's the easiest way to install it.
**New to EffectCraft?** Download it from the [latest release](https://github.com/storytold/effectcraft/releases/latest). That's the easiest way to install it.
**Want a specific build or format?** On GitHub, the [latest release](https://github.com/storytold/effectcraft/releases/latest) has every build listed below, and [all releases](https://github.com/storytold/effectcraft/releases) has earlier versions and their notes. `<ver>` in the file names is the version number, and `SHA256SUMS.txt` lists a checksum for every file.
@@ -317,43 +293,18 @@ File ▸ Import fallback instead.
## The Crafting Apps
EffectCraft is one of the **Crafting Apps**: free, open-source creative tools from the
[ArtCraft](https://getartcraft.com/) team, each written from scratch in Rust and each able to
EffectCraft is one of the **Crafting Apps**: free, open-source creative tools, each written from scratch in Rust and each able to
stand on its own.
| | App | What it's for | Code | Learn more |
|:-:|---|---|---|---|
| <img src="https://raw.githubusercontent.com/storytold/photocraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.photocraft.png" alt="" width="32" height="32"> | **PhotoCraft** | Image editing: layers, masks, type and real PSD files | [GitHub](https://github.com/storytold/photocraft) | [Website](https://getartcraft.com/apps/photocraft) |
| <img src="https://raw.githubusercontent.com/storytold/vectorcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.vectorcraft.png" alt="" width="32" height="32"> | **VectorCraft** | Vector illustration | [GitHub](https://github.com/storytold/vectorcraft) | [Website](https://getartcraft.com/apps/vectorcraft) |
| <img src="https://raw.githubusercontent.com/storytold/filmcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.filmcraft.png" alt="" width="32" height="32"> | **FilmCraft** | Video editing, color and sound | [GitHub](https://github.com/storytold/filmcraft) | [Website](https://getartcraft.com/apps/filmcraft) |
| <img src="https://raw.githubusercontent.com/storytold/lightcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.lightcraft.png" alt="" width="32" height="32"> | **LightCraft** | Photo library and raw development | [GitHub](https://github.com/storytold/lightcraft) | [Website](https://getartcraft.com/apps/lightcraft) |
| <img src="https://raw.githubusercontent.com/storytold/pdfcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.pdfcraft.png" alt="" width="32" height="32"> | **PdfCraft** | Reading, organizing and protecting PDFs | [GitHub](https://github.com/storytold/pdfcraft) | [Website](https://getartcraft.com/apps/pdfcraft) |
| <img src="https://raw.githubusercontent.com/storytold/effectcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png" alt="" width="32" height="32"> | **EffectCraft** | **Motion graphics and visual effects · you are here** | [GitHub](https://github.com/storytold/effectcraft) | [Website](https://getartcraft.com/apps/effectcraft) |
| <img src="https://raw.githubusercontent.com/storytold/designcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.designcraft.png" alt="" width="32" height="32"> | **DesignCraft** | Page layout and publishing | [GitHub](https://github.com/storytold/designcraft) | [Website](https://getartcraft.com/apps/designcraft) |
And [**ArtCraft**](https://getartcraft.com/) itself, our AI image and video studio for artists who want real control.
<br>
<p align="center">
<a href="https://discord.gg/artcraft"><img alt="Join the ArtCraft community on Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
</p>
<h3 align="center">Come make things with us</h3>
<p align="center">
Our Discord is where artists of every kind hang out: people who paint, shoot, draw, cut film,
set type, and people still figuring out what they like to make. Share what you're working on,
ask for help, tell us what's broken, or tell us what you wish these tools could do.
Whatever your medium and however long you've been at it, you're welcome here.
</p>
<p align="center">
<a href="https://discord.gg/artcraft"><b>discord.gg/artcraft</b></a> ·
<a href="https://getartcraft.com/">getartcraft.com</a> ·
<a href="https://getartcraft.com/apps">The Crafting Apps</a> ·
<a href="https://getartcraft.com/apps/effectcraft">EffectCraft</a>
</p>
| | App | What it's for | Code |
|:-:|---|---|---|
| <img src="https://raw.githubusercontent.com/storytold/photocraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.photocraft.png" alt="" width="32" height="32"> | **PhotoCraft** | Image editing: layers, masks, type and real PSD files | [GitHub](https://github.com/storytold/photocraft) |
| <img src="https://raw.githubusercontent.com/storytold/vectorcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.vectorcraft.png" alt="" width="32" height="32"> | **VectorCraft** | Vector illustration | [GitHub](https://github.com/storytold/vectorcraft) |
| <img src="https://raw.githubusercontent.com/storytold/filmcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.filmcraft.png" alt="" width="32" height="32"> | **FilmCraft** | Video editing, color and sound | [GitHub](https://github.com/storytold/filmcraft) |
| <img src="https://raw.githubusercontent.com/storytold/lightcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.lightcraft.png" alt="" width="32" height="32"> | **LightCraft** | Photo library and raw development | [GitHub](https://github.com/storytold/lightcraft) |
| <img src="https://raw.githubusercontent.com/storytold/pdfcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.pdfcraft.png" alt="" width="32" height="32"> | **PdfCraft** | Reading, organizing and protecting PDFs | [GitHub](https://github.com/storytold/pdfcraft) |
| <img src="https://raw.githubusercontent.com/storytold/effectcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png" alt="" width="32" height="32"> | **EffectCraft** | **Motion graphics and visual effects · you are here** | [GitHub](https://github.com/storytold/effectcraft) |
| <img src="https://raw.githubusercontent.com/storytold/designcraft/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.designcraft.png" alt="" width="32" height="32"> | **DesignCraft** | Page layout and publishing | [GitHub](https://github.com/storytold/designcraft) |
## License and credits
@@ -363,18 +314,8 @@ Copyright (c) 2026 ArtCraft Team and the EffectCraft contributors. Required noti
Bundled fonts, icons, images and other assets keep their own open licenses; each one is listed
with its author, source and license in [ATTRIBUTION.md](ATTRIBUTION.md).
The ArtCraft name, wordmark and logos in [`docs/brand/`](docs/brand/) are trademarks of the
ArtCraft Team and are not covered by this license. They may be used only unmodified, and only as
part of this repository and EffectCraft, under [`docs/brand/LICENSE-brand.txt`](docs/brand/LICENSE-brand.txt).
Forks and modified versions must remove them.
<sub>Adobe, Photoshop, Illustrator, Premiere Pro, Lightroom, Acrobat, After Effects and InDesign are trademarks or registered trademarks of Adobe Inc. in the United States and/or other countries. EffectCraft is an independent, open-source project and is not affiliated with, sponsored by or endorsed by Adobe Inc.; these names are used only to describe the workflows it is compatible with.</sub>
<p align="center">
<a href="https://getartcraft.com/"><img alt="ArtCraft" src="docs/brand/artcraft-mark.svg" width="28"></a><br>
<sub>Made by the <a href="https://getartcraft.com/">ArtCraft</a> team and community.</sub>
</p>
## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=storytold/effectcraft&type=Date&legend=top-left)](https://www.star-history.com/?repos=storytold%2Feffectcraft&type=date&legend=top-left)
+1 -1
View File
@@ -113,7 +113,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap
| 5 | Performance: benchmarks at 1080p / 4K, real-time cached playback, hardware video (G5) | 50–100 |
| 6 | Localization depth: effect names, all panels, French / German / Korean (G9) | 60–110 |
Come tell us what matters most to you on [Discord](https://discord.gg/artcraft).
Tell us what matters most to you in [GitHub issues](https://github.com/storytold/effectcraft/issues).
## Progress log
@@ -1,6 +1,6 @@
asset: favicon.svg
title: EffectCraft web app icon (favicon)
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
+1 -1
View File
@@ -14,7 +14,7 @@ effectcraft-engine = { workspace = true }
effectcraft-host = { workspace = true }
effectcraft-ui-egui = { workspace = true }
egui = { workspace = true }
# `links`: links (Help menu, Discord, Settings) open in the system browser.
# `links`: links (Help menu, Settings) open in the system browser.
eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", "links", "wayland", "x11"] }
serde = { workspace = true }
serde_json = { workspace = true }
+36 -1
View File
@@ -45,6 +45,21 @@ fn app_icon() -> egui::IconData {
/// Both native JSON projects and Save a Copy As XML projects are openable on launch.
/// Keep this classification shared by project opening and the subsequent media-import pass.
/// Windows and Linux: no OS title bar; the menu bar row is the title bar
/// (`effectcraft_ui_egui::titlebar`). macOS keeps its traffic lights over the integrated top row.
const CUSTOM_TITLEBAR: bool = !cfg!(target_os = "macos");
/// Whether this start draws its own title bar: Windows and Linux do, unless Settings ▸ Appearance ▸
/// Use System Title Bar asks for the system's. Read from the saved settings before the window
/// opens; a missing or unreadable file keeps the default.
fn custom_titlebar(config_dir: Option<&std::path::Path>) -> bool {
let prefs = config_dir
.and_then(|d| std::fs::read_to_string(d.join(effectcraft_engine::prefs::PREFS_FILE)).ok())
.map(|t| effectcraft_engine::prefs::Prefs::from_json(&t))
.unwrap_or_default();
CUSTOM_TITLEBAR && !prefs.appearance.system_title_bar
}
fn is_project_file(path: &str) -> bool {
std::path::Path::new(path)
.extension()
@@ -117,12 +132,17 @@ fn main() -> eframe::Result {
let mut launch = launch_guard::Launch::begin(effectcraft_host::config_dir().as_deref(), std::env::var_os("WGPU_BACKEND").is_some());
let on_gl = launch.backends == Some(eframe::wgpu::Backends::GL);
let notice = launch.notice.take();
let custom_titlebar = custom_titlebar(effectcraft_host::config_dir().as_deref());
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("EffectCraft")
.with_inner_size(window::DEFAULT_SIZE)
.with_min_inner_size(window::MIN_SIZE)
.with_drag_and_drop(true)
.with_decorations(!custom_titlebar)
.with_fullsize_content_view(true)
.with_titlebar_shown(false)
.with_title_shown(false)
// Started for an agent (`--control`): open without taking the user's keyboard focus.
.with_active(control_port.is_none())
.with_icon(app_icon())
@@ -205,6 +225,8 @@ fn main() -> eframe::Result {
// reported in a toast and forgotten).
let _ = session.execute("window.restoreScriptPanels", json!({}));
let mut app = EffectcraftApp::new(session);
app.custom_titlebar = custom_titlebar;
app.integrated_titlebar = cfg!(target_os = "macos");
app.set_gpu_failure_bridge(gpu_failures);
app.ui.start_screen = show_home;
if let Some(n) = notice {
@@ -424,7 +446,20 @@ fn disable_app_nap() {
#[cfg(test)]
mod tests {
use super::{Args, parse_args};
use super::{Args, custom_titlebar, parse_args};
#[test]
fn the_system_title_bar_setting_keeps_the_window_decorations() {
let dir = std::env::temp_dir().join(format!("ec-titlebar-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let file = dir.join(effectcraft_engine::prefs::PREFS_FILE);
assert_eq!(custom_titlebar(None), super::CUSTOM_TITLEBAR, "no settings folder: the default");
std::fs::write(&file, "not json").unwrap();
assert_eq!(custom_titlebar(Some(&dir)), super::CUSTOM_TITLEBAR, "an unreadable file: the default");
std::fs::write(&file, r#"{"appearance":{"systemTitleBar":true}}"#).unwrap();
assert!(!custom_titlebar(Some(&dir)));
std::fs::remove_dir_all(&dir).ok();
}
fn parse(args: &[&str]) -> Option<Args> {
parse_args(args.iter().map(|a| a.to_string()), None)
+1 -1
View File
@@ -2,7 +2,7 @@ EffectCraft app icon (effectcraft.svg, effectcraft-small.svg and every file deri
this directory: effectcraft-1024.png, effectcraft-macos-512.png, effectcraft.icns, effectcraft.ico,
hicolor/), and the web favicon apps/effectcraft-web/web/favicon.svg.
Original artwork made for this project by the project owner in ArtCraft, then vectorised (no
Original artwork made for this project by the project owner by hand, then vectorised (no
third-party material). Licensed like the rest of EffectCraft, under either of
* Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
+1 -1
View File
@@ -15,7 +15,7 @@ full-bleed colour field, tight head-and-shoulders crop, the body running off the
| Paper | `#efe9dc` | the figure |
| Ink | `#0b0b0c` | line work and contour |
**Provenance:** the project owner drew it in ArtCraft (a 2880 px engraving-style image, kept in
**Provenance:** the project owner drew it by hand (a 2880 px engraving-style image, kept in
craftrules at `assets/app-icons/effectcraft/source.png`). It was vectorised with craftrules
`assets/logo-options/_tools/vectorize_tile.py`, which keys out the field and traces the ink with potrace.
Licence: see [LICENSE.txt](LICENSE.txt) (MIT OR Apache-2.0, like the repo).
@@ -1,6 +1,6 @@
asset: effectcraft-1024.png
title: EffectCraft app icon, 1024 px
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: effectcraft-macos-512.png
title: EffectCraft runtime Dock icon (macOS)
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: effectcraft-small.svg
title: EffectCraft app icon, light vector
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
+1 -1
View File
@@ -1,6 +1,6 @@
asset: effectcraft.icns
title: EffectCraft macOS app icon
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
+1 -1
View File
@@ -1,6 +1,6 @@
asset: effectcraft.ico
title: EffectCraft Windows app icon
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
+2 -2
View File
@@ -1,8 +1,8 @@
asset: effectcraft.svg
title: EffectCraft app icon, master vector
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
notes: The owner's ArtCraft drawing of an engraved unicorn bust, vectorised at 2048 px with craftrules assets/logo-options/_tools/vectorize_tile.py (potrace; three colours). Canonical artwork; the other icon files derive from it.
notes: The owner's drawing of an engraved unicorn bust, vectorised at 2048 px with craftrules assets/logo-options/_tools/vectorize_tile.py (potrace; three colours). Canonical artwork; the other icon files derive from it.
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 128x128
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 16x16
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 24x24
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 256x256
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 32x32
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 48x48
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 512x512
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.png
title: EffectCraft app icon, Linux hicolor 64x64
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
@@ -1,6 +1,6 @@
asset: ai.storyteller.effectcraft.svg
title: EffectCraft app icon, Linux hicolor scalable
author: EffectCraft contributors (drawing by the project owner in ArtCraft)
author: EffectCraft contributors (drawing by the project owner)
source: original work
license: MIT OR Apache-2.0
license-file: assets/app-icon/LICENSE.txt
+10
View File
@@ -37,6 +37,16 @@ pub fn specs() -> Vec<CommandSpec> {
fe!("app.keyboardShortcuts", "Keyboard Shortcuts", ["Edit"], Some("Cmd+Alt+'"), "{}", always),
fe!("app.templates", "Templates", [], None, "{kind: renderSettings|outputModule}", always),
fe!("app.find", "Find", ["File"], Some("Cmd+F"), "{query?}", always),
// Themes (View ▸ Preferences ▸ Theme): `view.theme.toggle` cycles the appearance mode, the
// others show that theme and fix the mode to its family (see `appearance.theme`).
fe!("view.theme.toggle", "Next Appearance Mode", [], None, "{}", always),
fe!("view.theme.dark", "Theme: Dark", [], None, "{}", always),
fe!("view.theme.darker", "Theme: Darker", [], None, "{}", always),
fe!("view.theme.light", "Theme: Light", [], None, "{}", always),
fe!("view.theme.catppuccinLatte", "Theme: Catppuccin Latte", [], None, "{}", always),
fe!("view.theme.catppuccinFrappe", "Theme: Catppuccin Frappé", [], None, "{}", always),
fe!("view.theme.catppuccinMacchiato", "Theme: Catppuccin Macchiato", [], None, "{}", always),
fe!("view.theme.catppuccinMocha", "Theme: Catppuccin Mocha", [], None, "{}", always),
// Preview.
fe!(
"playback.toggle",
+2 -19
View File
@@ -1,4 +1,4 @@
//! Help menu: community and project links (the frontend opens `Event::OpenUrl`).
//! Help menu: project links (the frontend opens `Event::OpenUrl`).
use serde_json::{Value, json};
@@ -11,11 +11,6 @@ fn open(s: &mut Session, url: String) -> Result<Value> {
Ok(json!({"url": url}))
}
fn sibling(s: &mut Session, p: &Value) -> Result<Value> {
let slug = str_p(p, "app").unwrap_or("photocraft").to_ascii_lowercase();
if str_p(p, "kind") == Some("github") { open(s, links::github_of(&slug)) } else { open(s, links::page_of(&slug)) }
}
/// Help ▸ EffectCraft Help / Scripting Help / Expression Reference / Effect Reference.
fn docs(s: &mut Session, p: &Value) -> Result<Value> {
let page = match str_p(p, "page").unwrap_or("help") {
@@ -31,25 +26,13 @@ fn docs(s: &mut Session, p: &Value) -> Result<Value> {
pub fn specs() -> Vec<CommandSpec> {
vec![
cmd!("help.docs", "EffectCraft Help...", [], Some("F1"), "{page?: help|scripting|expressions|effects|agents}", always, docs),
cmd!("help.discord", "Join the ArtCraft Discord...", ["Help"], None, "{}", always, |s, _| open(s, links::DISCORD.into())),
cmd!("help.website", "ArtCraft Website", ["Help"], None, "{}", always, |s, _| open(s, links::WEBSITE.into())),
cmd!("help.appPage", "EffectCraft Home Page", ["Help"], None, "{}", always, |s, _| open(s, links::APP_PAGE.into())),
cmd!("help.github", "EffectCraft on GitHub", ["Help"], None, "{}", always, |s, _| open(s, links::GITHUB.into())),
cmd!("help.onlineTutorials", "Online Tutorials...", ["Help"], None, "{}", always, |s, _| open(s, links::APP_PAGE.into())),
cmd!("help.onlineTutorials", "Online Tutorials...", ["Help"], None, "{}", always, |s, _| open(s, links::GITHUB.into())),
cmd!("help.inAppTutorials", "In-App Tutorials...", ["Help"], None, "{} → the Home screen's Learn tab", always, |s, p| super::frontend(
s,
"help.inAppTutorials",
p
)),
cmd!("help.reportIssue", "Provide Feedback...", ["Help"], None, "{}", always, |s, _| open(s, links::ISSUES.into())),
cmd!(
"help.sibling",
"Other ArtCraft Apps",
[],
None,
"{app: photocraft|vectorcraft|filmcraft|lightcraft|pdfcraft|designcraft, kind?: page|github}",
always,
sibling
),
]
}
+1 -22
View File
@@ -1,27 +1,6 @@
//! Community and project links (shown in Help ▸, the About dialog, the start screen and the
//! header's Discord button).
//! Project links (shown in Help ▸, the About dialog and the start screen).
pub const APP_NAME: &str = "EffectCraft";
pub const APP_SLUG: &str = "effectcraft";
pub const DISCORD: &str = "https://discord.gg/artcraft";
pub const WEBSITE: &str = "https://getartcraft.com";
pub const APP_PAGE: &str = "https://getartcraft.com/apps/effectcraft";
pub const GITHUB: &str = "https://github.com/storytold/effectcraft";
pub const ISSUES: &str = "https://github.com/storytold/effectcraft/issues";
/// Sibling apps of the ArtCraft family: (name, what it is, GitHub repo, app page).
pub const SIBLINGS: &[(&str, &str)] = &[
("PhotoCraft", "photocraft"),
("VectorCraft", "vectorcraft"),
("FilmCraft", "filmcraft"),
("LightCraft", "lightcraft"),
("PdfCraft", "pdfcraft"),
("DesignCraft", "designcraft"),
];
pub fn github_of(slug: &str) -> String {
format!("https://github.com/storytold/{slug}")
}
pub fn page_of(slug: &str) -> String {
format!("https://getartcraft.com/apps/{slug}")
}
+14 -3
View File
@@ -1150,6 +1150,20 @@ View
---
Go to Time... | time.set
Enter Full Screen | view.fullScreen
---
Preferences
Theme
Dark | view.theme.dark
Darker | view.theme.darker
Light | view.theme.light
---
Catppuccin Latte | view.theme.catppuccinLatte
Catppuccin Frappé | view.theme.catppuccinFrappe
Catppuccin Macchiato | view.theme.catppuccinMacchiato
Catppuccin Mocha | view.theme.catppuccinMocha
---
Next Appearance Mode | view.theme.toggle
Appearance... | app.settings {"page":"appearance"}
Window
Workspace
Default | window.workspace {"name":"Default"} | Shift+F10
@@ -1230,11 +1244,8 @@ Help
Enable Logging | help.enableLogging
Reveal Logging File | help.revealLogFile
---
Join the ArtCraft Discord... | help.discord
Provide Feedback... | help.reportIssue
---
ArtCraft Website | help.website
EffectCraft Home Page | help.appPage
EffectCraft on GitHub | help.github
Open Demo Project | file.openDemoProject
[!mac] ---
+22 -3
View File
@@ -46,13 +46,27 @@ pub const WINDOW_GRAPHICS: &[(&str, &str)] = &[("Automatic", "auto"), ("OpenGL (
pub const APPEARANCE_MODES: &[(&str, &str)] = &[("Sync with System", "auto"), ("Dark", "dark"), ("Light", "light")];
/// The dark family of themes: (label, `appearance.darkTheme` value).
pub const DARK_THEMES: &[(&str, &str)] = &[("Dark", "dark"), ("Darker", "darker")];
pub const DARK_THEMES: &[(&str, &str)] = &[
("Dark", "dark"),
("Darker", "darker"),
("Catppuccin Frappé", "catppuccinFrappe"),
("Catppuccin Macchiato", "catppuccinMacchiato"),
("Catppuccin Mocha", "catppuccinMocha"),
];
/// The light family of themes: (label, `appearance.lightTheme` value).
pub const LIGHT_THEMES: &[(&str, &str)] = &[("Light", "light")];
pub const LIGHT_THEMES: &[(&str, &str)] = &[("Light", "light"), ("Catppuccin Latte", "catppuccinLatte")];
/// Every theme (the legacy single-theme `appearance.theme`).
pub const THEMES: &[(&str, &str)] = &[("Dark", "dark"), ("Darker", "darker"), ("Light", "light")];
pub const THEMES: &[(&str, &str)] = &[
("Dark", "dark"),
("Darker", "darker"),
("Light", "light"),
("Catppuccin Latte", "catppuccinLatte"),
("Catppuccin Frappé", "catppuccinFrappe"),
("Catppuccin Macchiato", "catppuccinMacchiato"),
("Catppuccin Mocha", "catppuccinMocha"),
];
/// The settings whose value must be one of their choices.
fn choices(key: &str) -> Option<&'static [(&'static str, &'static str)]> {
@@ -188,6 +202,9 @@ page!(Appearance {
use_gradients: bool = true,
/// macOS: draw the menu bar inside the window instead of the system menu bar.
in_window_menu_bar_mac: bool = false,
/// Windows and Linux: keep the system's title bar instead of drawing the window's own.
/// Read before the window opens: it applies from the next launch.
system_title_bar: bool = false,
});
page!(Grids {
@@ -1242,6 +1259,8 @@ pub fn pages() -> Vec<Page> {
s("appearance.useGradients", "Use Gradients", B, true),
Section("Menu Bar"),
s("appearance.inWindowMenuBarMac", "Use In-Window Menu Bar on macOS", B, true),
Section("Title Bar"),
s("appearance.systemTitleBar", "Use System Title Bar on Windows and Linux", B, true),
],
},
Page {
+2 -3
View File
@@ -379,9 +379,8 @@ fn save_and_open_roundtrip() {
#[test]
fn help_links_emit_urls() {
let mut s = Session::default();
let r = s.execute("help.discord", json!({})).unwrap();
assert_eq!(r["url"], "https://discord.gg/artcraft");
assert!(s.drain_events().iter().any(|e| matches!(e, crate::Event::OpenUrl(u) if u.contains("discord"))));
s.execute("help.github", json!({})).unwrap();
assert!(s.drain_events().iter().any(|e| matches!(e, crate::Event::OpenUrl(u) if u.contains("github"))));
assert_eq!(s.execute("help.github", json!({})).unwrap()["url"], "https://github.com/storytold/effectcraft");
}
+1 -1
View File
@@ -74,7 +74,7 @@ fn help_tutorials() {
s.execute("help.onlineTutorials", json!({})).unwrap();
s.execute("help.inAppTutorials", json!({})).unwrap();
let ev = s.drain_events();
assert!(ev.iter().any(|e| matches!(e, Event::OpenUrl(u) if u == crate::links::APP_PAGE)));
assert!(ev.iter().any(|e| matches!(e, Event::OpenUrl(u) if u == crate::links::GITHUB)));
assert!(ev.iter().any(|e| matches!(e, Event::Frontend { command, .. } if command == "help.inAppTutorials")));
}
+2 -2
View File
@@ -13,7 +13,7 @@ fn comp_marker_dialog_fields_and_undo() {
let r = s
.execute(
"markers.set",
json!({"index": 0, "time": 1.51, "duration": 0.5, "comment": "Intro", "chapter": "One", "url": "https://getartcraft.com", "frameTarget": "_blank",
json!({"index": 0, "time": 1.51, "duration": 0.5, "comment": "Intro", "chapter": "One", "url": "https://example.com", "frameTarget": "_blank",
"cuePoint": {"name": "cue", "navigation": true, "params": [["k", "v"]]}, "protected": true, "label": "Aqua"}),
)
.unwrap();
@@ -25,7 +25,7 @@ fn comp_marker_dialog_fields_and_undo() {
assert!((m["duration"].as_f64().unwrap() - 0.5).abs() < 1e-9);
assert_eq!(m["comment"], "Intro");
assert_eq!(m["chapter"], "One");
assert_eq!(m["url"], "https://getartcraft.com");
assert_eq!(m["url"], "https://example.com");
assert_eq!(m["frameTarget"], "_blank");
assert_eq!(m["cuePoint"]["name"], "cue");
assert_eq!(m["cuePoint"]["navigation"], true);
+16 -34
View File
@@ -1,11 +1,11 @@
//! The Tools bar: home, the tool slots, tool options, snapping, workspaces and the community
//! buttons (Discord is always one click away).
//! The Tools bar: home, the tool slots, tool options, snapping, workspaces and the project
//! buttons.
use crate::i18n::{tr, tr_args};
use egui::{Align2, Color32, Rect, Sense, Stroke, StrokeKind, pos2, vec2};
use serde_json::json;
use crate::icons::{self, Icon};
use crate::icons::Icon;
use crate::state::Tool;
use crate::theme::Tokens;
use crate::widgets;
@@ -182,32 +182,15 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
let label = p.text(pos2(snap.max.x + 4.0, cy), Align2::LEFT_CENTER, tr("Snapping"), Tokens::ui(12.0), t.text_dim);
snapping_options(app, ui, Rect::from_min_size(pos2(label.max.x + 2.0, cy - 9.0), vec2(18.0, 18.0)));
// Right side: community buttons, workspaces.
// Right side: project buttons, workspaces.
let mut rx = rect.max.x - 10.0;
// The pill fits its label (icon, gap, text) with even padding: it was a fixed 104 px with the
// text at its left, which left a wide gap on the right (#581).
let label = p.layout_no_wrap(tr("Discord").to_string(), Tokens::semibold(12.0), Color32::WHITE);
let discord = Rect::from_min_max(pos2(rx - (10.0 + 14.0 + 6.0 + label.size().x + 12.0), cy - 13.0), pos2(rx, cy + 13.0));
let dresp = ui.interact(discord, egui::Id::new("hdr-discord"), Sense::click());
let dc = Color32::from_rgb(0x58, 0x65, 0xf2);
p.rect_filled(discord, 13.0, if dresp.hovered() { dc.gamma_multiply(1.2) } else { dc });
icons::paint(&p, Rect::from_center_size(pos2(discord.min.x + 17.0, cy), vec2(14.0, 14.0)), Icon::Chat, Color32::WHITE);
p.galley(pos2(discord.min.x + 30.0, cy - label.size().y / 2.0), label, Color32::WHITE);
app.auto.add("header.discord", discord, "Join the ArtCraft Discord");
if dresp.on_hover_text(tr("Join the ArtCraft community on Discord")).clicked() {
let _ = app.session.execute("help.discord", json!({}));
}
rx = discord.min.x - 6.0;
for (id, icon, tip, cmd) in
[("hdr-github", Icon::Code, "EffectCraft on GitHub", "help.github"), ("hdr-web", Icon::Globe, "EffectCraft on getartcraft.com", "help.appPage")]
{
let r = Rect::from_min_max(pos2(rx - 26.0, cy - 13.0), pos2(rx, cy + 13.0));
if widgets::icon_button(ui, r, icon, false, &t, egui::Id::new(id)).on_hover_text(tip).clicked() {
let _ = app.session.execute(cmd, json!({}));
}
app.auto.add(&format!("header.{}", &id[4..]), r, tip);
rx = r.min.x - 4.0;
let tip = "EffectCraft on GitHub";
let r = Rect::from_min_max(pos2(rx - 26.0, cy - 13.0), pos2(rx, cy + 13.0));
if widgets::icon_button(ui, r, Icon::Code, false, &t, egui::Id::new("hdr-github")).on_hover_text(tip).clicked() {
let _ = app.session.execute("help.github", json!({}));
}
app.auto.add("header.github", r, tip);
rx = r.min.x - 4.0;
// Appearance: Sync with System, Light, Dark (a monitor, sun or moon), one click to the next.
let (icon, mode) = match app.session.prefs.appearance.appearance_mode.as_str() {
"auto" => (Icon::Monitor, tr("Sync with System")),
@@ -654,20 +637,19 @@ fn ws_menu(app: &mut EffectcraftApp, ui: &mut egui::Ui) {
}
}
/// The ArtCraft mark as supplied in `docs/brand` (the README's and getartcraft.com's logo; used
/// unmodified, see `docs/brand/LICENSE-brand.txt`).
static ARTCRAFT_MARK: &[u8] = include_bytes!("../../../docs/brand/artcraft-mark.png");
/// The EffectCraft app icon (`assets/app-icon`).
static APP_MARK: &[u8] = include_bytes!("../../../assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png");
/// The ArtCraft mark in `r` (decoded once into a mipmapped texture, so it stays crisp from the
/// The app mark in `r` (decoded once into a mipmapped texture, so it stays crisp from the
/// 24 px Tools bar to the About dialog). Nothing is drawn if it can't be decoded.
pub fn paint_logo(p: &egui::Painter, r: Rect) {
let ctx = p.ctx();
let id = egui::Id::new("artcraft-mark");
let id = egui::Id::new("app-mark");
let tex = ctx.data(|d| d.get_temp::<Option<egui::TextureHandle>>(id)).unwrap_or_else(|| {
let tex = image::load_from_memory_with_format(ARTCRAFT_MARK, image::ImageFormat::Png).ok().map(|img| {
let tex = image::load_from_memory_with_format(APP_MARK, image::ImageFormat::Png).ok().map(|img| {
let img = img.to_rgba8();
let ci = egui::ColorImage::from_rgba_unmultiplied([img.width() as usize, img.height() as usize], img.as_raw());
ctx.load_texture("artcraft-mark", ci, egui::TextureOptions::LINEAR.with_mipmap_mode(Some(egui::TextureFilter::Linear)))
ctx.load_texture("app-mark", ci, egui::TextureOptions::LINEAR.with_mipmap_mode(Some(egui::TextureFilter::Linear)))
});
ctx.data_mut(|d| d.insert_temp(id, tex.clone()));
tex
+54 -26
View File
@@ -413,6 +413,15 @@ const SIMPLIFIED_CHINESE: &[(&str, &str, &str)] = &[
("app.templates", "Output Module...", "输出模块..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "键盘快捷键"),
("", "Preferences", "首选项"),
("", "Theme", "主题"),
("view.theme.dark", "Dark", "深色"),
("view.theme.darker", "Darker", "更深"),
("view.theme.light", "Light", "浅色"),
("view.theme.catppuccinLatte", "Catppuccin Latte", "Catppuccin Latte"),
("view.theme.catppuccinFrappe", "Catppuccin Frappé", "Catppuccin Frappé"),
("view.theme.catppuccinMacchiato", "Catppuccin Macchiato", "Catppuccin Macchiato"),
("view.theme.catppuccinMocha", "Catppuccin Mocha", "Catppuccin Mocha"),
("view.theme.toggle", "Next Appearance Mode", "下一个外观模式"),
("", "Composition", "合成"),
("comp.new", "New Composition...", "新建合成..."),
("comp.settings", "Composition Settings...", "合成设置..."),
@@ -875,10 +884,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "系统兼容性报告..."),
("help.enableLogging", "Enable Logging", "启用日志记录"),
("help.revealLogFile", "Reveal Logging File", "显示日志记录文件"),
("help.discord", "Join the ArtCraft Discord...", "加入 ArtCraft 的 Discord 社区..."),
("help.reportIssue", "Provide Feedback...", "提供反馈..."),
("help.website", "ArtCraft Website", "ArtCraft 网站"),
("help.appPage", "EffectCraft Home Page", "EffectCraft 主页"),
("help.github", "EffectCraft on GitHub", "GitHub 上的 EffectCraft"),
("file.openDemoProject", "Open Demo Project", "打开示例项目"),
];
@@ -1043,6 +1049,15 @@ const TRADITIONAL_CHINESE: &[(&str, &str, &str)] = &[
("app.templates", "Output Module...", "輸出模組..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "鍵盤快速鍵"),
("", "Preferences", "偏好設定"),
("", "Theme", "主題"),
("view.theme.dark", "Dark", "深色"),
("view.theme.darker", "Darker", "更深"),
("view.theme.light", "Light", "淺色"),
("view.theme.catppuccinLatte", "Catppuccin Latte", "Catppuccin Latte"),
("view.theme.catppuccinFrappe", "Catppuccin Frappé", "Catppuccin Frappé"),
("view.theme.catppuccinMacchiato", "Catppuccin Macchiato", "Catppuccin Macchiato"),
("view.theme.catppuccinMocha", "Catppuccin Mocha", "Catppuccin Mocha"),
("view.theme.toggle", "Next Appearance Mode", "下一個外觀模式"),
("", "Composition", "合成"),
("comp.new", "New Composition...", "新增合成..."),
("comp.settings", "Composition Settings...", "合成設定..."),
@@ -1505,10 +1520,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "系統相容性報告..."),
("help.enableLogging", "Enable Logging", "啟用日誌記錄"),
("help.revealLogFile", "Reveal Logging File", "顯示日誌檔案"),
("help.discord", "Join the ArtCraft Discord...", "加入 ArtCraft 的 Discord 社群..."),
("help.reportIssue", "Provide Feedback...", "提供意見..."),
("help.website", "ArtCraft Website", "ArtCraft 網站"),
("help.appPage", "EffectCraft Home Page", "EffectCraft 首頁"),
("help.github", "EffectCraft on GitHub", "GitHub 上的 EffectCraft"),
("file.openDemoProject", "Open Demo Project", "開啟範例專案"),
];
@@ -1670,6 +1682,15 @@ const JAPANESE: &[(&str, &str, &str)] = &[
("app.templates", "Output Module...", "出力モジュール..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "キーボードショートカット"),
("", "Preferences", "環境設定"),
("", "Theme", "テーマ"),
("view.theme.dark", "Dark", "ダーク"),
("view.theme.darker", "Darker", "暗く"),
("view.theme.light", "Light", "ライト"),
("view.theme.catppuccinLatte", "Catppuccin Latte", "Catppuccin Latte"),
("view.theme.catppuccinFrappe", "Catppuccin Frappé", "Catppuccin Frappé"),
("view.theme.catppuccinMacchiato", "Catppuccin Macchiato", "Catppuccin Macchiato"),
("view.theme.catppuccinMocha", "Catppuccin Mocha", "Catppuccin Mocha"),
("view.theme.toggle", "Next Appearance Mode", "次の外観モード"),
("", "Composition", "コンポジション"),
("comp.new", "New Composition...", "新規コンポジション..."),
("comp.settings", "Composition Settings...", "コンポジション設定..."),
@@ -2132,10 +2153,7 @@ const JAPANESE: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "システム互換性レポート..."),
("help.enableLogging", "Enable Logging", "ログ記録を有効化"),
("help.revealLogFile", "Reveal Logging File", "ログファイルを表示"),
("help.discord", "Join the ArtCraft Discord...", "ArtCraft Discordに参加..."),
("help.reportIssue", "Provide Feedback...", "フィードバックを送信..."),
("help.website", "ArtCraft Website", "ArtCraftウェブサイト"),
("help.appPage", "EffectCraft Home Page", "EffectCraftホームページ"),
("help.github", "EffectCraft on GitHub", "GitHubのEffectCraft"),
("file.openDemoProject", "Open Demo Project", "デモプロジェクトを開く"),
];
@@ -2300,6 +2318,15 @@ const UKRAINIAN: &[(&str, &str, &str)] = &[
("app.templates", "Output Module...", "Модуль виведення..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "Клавіатурні скорочення"),
("", "Preferences", "Налаштування"),
("", "Theme", "Тема"),
("view.theme.dark", "Dark", "Темна"),
("view.theme.darker", "Darker", "Темніше"),
("view.theme.light", "Light", "Світла"),
("view.theme.catppuccinLatte", "Catppuccin Latte", "Catppuccin Latte"),
("view.theme.catppuccinFrappe", "Catppuccin Frappé", "Catppuccin Frappé"),
("view.theme.catppuccinMacchiato", "Catppuccin Macchiato", "Catppuccin Macchiato"),
("view.theme.catppuccinMocha", "Catppuccin Mocha", "Catppuccin Mocha"),
("view.theme.toggle", "Next Appearance Mode", "Наступний режим вигляду"),
("", "Composition", "Композиція"),
("comp.new", "New Composition...", "Нова композиція..."),
("comp.settings", "Composition Settings...", "Налаштування композиції..."),
@@ -2762,10 +2789,7 @@ const UKRAINIAN: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Звіт про сумісність системи..."),
("help.enableLogging", "Enable Logging", "Увімкнути журналювання"),
("help.revealLogFile", "Reveal Logging File", "Показати файл журналу"),
("help.discord", "Join the ArtCraft Discord...", "Приєднатися до Discord ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Надіслати відгук..."),
("help.website", "ArtCraft Website", "Вебсайт ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Домашня сторінка EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft на GitHub"),
("file.openDemoProject", "Open Demo Project", "Відкрити демонстраційний проєкт"),
];
@@ -2931,6 +2955,15 @@ const SPANISH: &[(&str, &str, &str)] = &[
("app.templates", "Output Module...", "Módulo de salida..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "Métodos abreviados de teclado"),
("", "Preferences", "Preferencias"),
("", "Theme", "Tema"),
("view.theme.dark", "Dark", "Oscuro"),
("view.theme.darker", "Darker", "Más oscuro"),
("view.theme.light", "Light", "Claro"),
("view.theme.catppuccinLatte", "Catppuccin Latte", "Catppuccin Latte"),
("view.theme.catppuccinFrappe", "Catppuccin Frappé", "Catppuccin Frappé"),
("view.theme.catppuccinMacchiato", "Catppuccin Macchiato", "Catppuccin Macchiato"),
("view.theme.catppuccinMocha", "Catppuccin Mocha", "Catppuccin Mocha"),
("view.theme.toggle", "Next Appearance Mode", "Siguiente modo de apariencia"),
("", "Composition", "Composición"),
("comp.new", "New Composition...", "Nueva composición..."),
("comp.settings", "Composition Settings...", "Configuración de la composición..."),
@@ -3393,10 +3426,7 @@ const SPANISH: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Informe de compatibilidad del sistema..."),
("help.enableLogging", "Enable Logging", "Habilitar registro"),
("help.revealLogFile", "Reveal Logging File", "Mostrar archivo de registro"),
("help.discord", "Join the ArtCraft Discord...", "Unirse al Discord de ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Enviar comentarios..."),
("help.website", "ArtCraft Website", "Sitio web de ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Página de inicio de EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft en GitHub"),
("file.openDemoProject", "Open Demo Project", "Abrir proyecto de demostración"),
];
@@ -3560,6 +3590,15 @@ const PORTUGUESE: &[(&str, &str, &str)] = &[
("app.templates", "Output Module...", "Módulo de saída..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "Atalhos de teclado"),
("", "Preferences", "Preferências"),
("", "Theme", "Tema"),
("view.theme.dark", "Dark", "Escuro"),
("view.theme.darker", "Darker", "Mais escuro"),
("view.theme.light", "Light", "Claro"),
("view.theme.catppuccinLatte", "Catppuccin Latte", "Catppuccin Latte"),
("view.theme.catppuccinFrappe", "Catppuccin Frappé", "Catppuccin Frappé"),
("view.theme.catppuccinMacchiato", "Catppuccin Macchiato", "Catppuccin Macchiato"),
("view.theme.catppuccinMocha", "Catppuccin Mocha", "Catppuccin Mocha"),
("view.theme.toggle", "Next Appearance Mode", "Próximo modo de aparência"),
("", "Composition", "Composição"),
("comp.new", "New Composition...", "Nova composição..."),
("comp.settings", "Composition Settings...", "Configurações da composição..."),
@@ -4022,10 +4061,7 @@ const PORTUGUESE: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Relatório de compatibilidade do sistema..."),
("help.enableLogging", "Enable Logging", "Ativar registro em log"),
("help.revealLogFile", "Reveal Logging File", "Revelar arquivo de log"),
("help.discord", "Join the ArtCraft Discord...", "Entrar no Discord do ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Enviar comentários..."),
("help.website", "ArtCraft Website", "Site do ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Página inicial do EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft no GitHub"),
("file.openDemoProject", "Open Demo Project", "Abrir projeto de demonstração"),
];
@@ -4653,9 +4689,7 @@ const RUSSIAN: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Отчет о совместимости системы..."),
("help.enableLogging", "Enable Logging", "Включить ведение журнала"),
("help.revealLogFile", "Reveal Logging File", "Показать файл журнала"),
("help.discord", "Join the ArtCraft Discord...", "Присоединяйтесь к Discord ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Оставить отзыв..."),
("help.website", "ArtCraft Website", "Сайт ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Главная страница EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft на GitHub"),
("file.openDemoProject", "Open Demo Project", "Открыть демонстрационный проект"),
@@ -5282,9 +5316,7 @@ const FRENCH: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Rapport de compatibilité du système..."),
("help.enableLogging", "Enable Logging", "Activer la journalisation"),
("help.revealLogFile", "Reveal Logging File", "Révéler le fichier journal"),
("help.discord", "Join the ArtCraft Discord...", "Rejoindre le Discord ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Envoyer des commentaires..."),
("help.website", "ArtCraft Website", "Site web ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Page d’accueil EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft sur GitHub"),
("file.openDemoProject", "Open Demo Project", "Ouvrir le projet de démonstration"),
@@ -5911,9 +5943,7 @@ const GERMAN: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Bericht zur Systemkompatibilität..."),
("help.enableLogging", "Enable Logging", "Protokollierung aktivieren"),
("help.revealLogFile", "Reveal Logging File", "Protokolldatei anzeigen"),
("help.discord", "Join the ArtCraft Discord...", "ArtCraft-Discord beitreten..."),
("help.reportIssue", "Provide Feedback...", "Feedback senden..."),
("help.website", "ArtCraft Website", "ArtCraft-Website"),
("help.appPage", "EffectCraft Home Page", "EffectCraft-Startseite"),
("help.github", "EffectCraft on GitHub", "EffectCraft auf GitHub"),
("file.openDemoProject", "Open Demo Project", "Demoprojekt öffnen"),
@@ -6541,9 +6571,7 @@ const CZECH: &[(&str, &str, &str)] = &[
("help.systemReport", "System Compatibility Report...", "Zpráva o kompatibilitě systému..."),
("help.enableLogging", "Enable Logging", "Povolit protokolování"),
("help.revealLogFile", "Reveal Logging File", "Zobrazit soubor protokolu"),
("help.discord", "Join the ArtCraft Discord...", "Připojit se na Discord ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Poskytnout zpětnou vazbu..."),
("help.website", "ArtCraft Website", "Web ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Domovská stránka EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft na GitHubu"),
("file.openDemoProject", "Open Demo Project", "Otevřít ukázkový projekt"),
+29 -40
View File
@@ -95,8 +95,8 @@ const JAPANESE: &[(&str, &str)] = &[
("360 View Orientation", "360度ビューの方向"),
("3:2 Pulldown: {}", "3:2プルダウン:{}"),
(
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.",
"モーショングラフィックスとビジュアルエフェクト向けの、クリーンルーム方式で開発されたオープンソースのコンポジットソフトウェアです。macOS、Windows、Linuxでネイティブに動作し、ブラウザーでも使えます。ArtCraftクリエイティブアプリファミリーの一員です。",
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser.",
"モーショングラフィックスとビジュアルエフェクト向けの、クリーンルーム方式で開発されたオープンソースのコンポジットソフトウェアです。macOS、Windows、Linuxでネイティブに動作し、ブラウザーでも使えます。",
),
("About EffectCraft", "EffectCraftについて"),
("About...", "情報..."),
@@ -195,7 +195,6 @@ const JAPANESE: &[(&str, &str)] = &[
("Delete selected project items", "選択したプロジェクト項目を削除"),
("Delete this template", "このテンプレートを削除"),
("Depth of Field", "被写界深度"),
("Discord", "Discord"),
("Distribute Layers:", "レイヤーの分布:"),
("Don't Recover", "復元しない"),
("Don't Save", "保存しない"),
@@ -267,7 +266,6 @@ const JAPANESE: &[(&str, &str)] = &[
("Install from File…", "ファイルからインストール…"),
("Intensity", "強度"),
("Inverted", "反転"),
("Join the ArtCraft community on Discord", "DiscordでArtCraftコミュニティに参加"),
("Key: {}", "キー:{}"),
("LEARN", "学習"),
("Label", "ラベル"),
@@ -304,7 +302,6 @@ const JAPANESE: &[(&str, &str)] = &[
("Models folder: {}", "モデルフォルダー:{}"),
("Modifiers:", "修飾キー:"),
("More", "その他"),
("More ArtCraft apps", "ほかのArtCraftアプリ"),
("More tutorials", "ほかのチュートリアル"),
("Motion Blur", "モーションブラー"),
("Motion Blur: {}", "モーションブラー:{}"),
@@ -525,6 +522,11 @@ const JAPANESE: &[(&str, &str)] = &[
("⌘/Ctrl-drag a pin to record its motion", "⌘/Ctrl+ドラッグでピンの動きを記録"),
("▣ {}", "▣ {}"),
("⚠ {} is used by:", "⚠ {}は次のコマンドで使用中:"),
("Minimize", "最小化"),
("Maximize", "最大化"),
("Restore", "元に戻す"),
("Title Bar", "タイトルバー"),
("Use System Title Bar on Windows and Linux", "Windows と Linux でシステムのタイトルバーを使用"),
];
const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
@@ -538,8 +540,8 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("360 View Orientation", "360 视图方向"),
("3:2 Pulldown: {}", "3:2 折叠:{}"),
(
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.",
"一款从零编写、开源的动态图形与视觉效果合成软件:原生支持 macOS、Windows 和 Linux,也可在浏览器中运行。属于 ArtCraft 创意应用家族。",
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser.",
"一款从零编写、开源的动态图形与视觉效果合成软件:原生支持 macOS、Windows 和 Linux,也可在浏览器中运行。",
),
("About EffectCraft", "关于 EffectCraft"),
("About...", "关于..."),
@@ -639,7 +641,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Delete selected project items", "删除所选的项目项"),
("Delete this template", "删除此模板"),
("Depth of Field", "景深"),
("Discord", "Discord"),
("Distribute Layers:", "分布图层:"),
("Don't Recover", "不恢复"),
("Don't Save", "不保存"),
@@ -712,7 +713,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Install from File…", "从文件安装…"),
("Intensity", "强度"),
("Inverted", "反转"),
("Join the ArtCraft community on Discord", "加入 ArtCraft 的 Discord 社区"),
("Key: {}", "按键:{}"),
("LEARN", "学习"),
("Label", "标签"),
@@ -751,7 +751,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Models folder: {}", "模型文件夹:{}"),
("Modifiers:", "修饰键:"),
("More", "更多"),
("More ArtCraft apps", "更多 ArtCraft 应用"),
("More tutorials", "更多教程"),
("Motion Blur", "运动模糊"),
("Motion Blur: {}", "运动模糊:{}"),
@@ -1016,7 +1015,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Import PDF / Illustrator File", "导入 PDF / Illustrator 文件"),
("Import Photoshop File", "导入 Photoshop 文件"),
("Interpret Unlabeled Alpha As", "将未标记的 Alpha 解释为"),
("Join the ArtCraft Discord", "加入 ArtCraft Discord"),
("Mercury GPU Acceleration ({})", "Mercury GPU 加速({})"),
("Mercury Software Only", "仅 Mercury 软件"),
("OCIO (built-in ACES config)", "OCIO(内置 ACES 配置)"),
@@ -1033,7 +1031,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("New Project", "新建项目"),
("Open Project…", "打开项目…"),
("Templates", "模板"),
("EffectCraft home page", "EffectCraft 主页"),
("EffectCraft on GitHub", "GitHub 上的 EffectCraft"),
("Motion graphics and visual effects · version {}", "动态图形和视觉效果 · 版本 {}"),
("New Composition", "新建合成"),
@@ -1060,6 +1057,11 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("After Effects project", "After Effects 项目"),
("Lottie animation", "Lottie 动画"),
("Project", "项目"),
("Minimize", "最小化"),
("Maximize", "最大化"),
("Restore", "还原"),
("Title Bar", "标题栏"),
("Use System Title Bar on Windows and Linux", "在 Windows 和 Linux 上使用系统标题栏"),
];
const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
@@ -1073,8 +1075,8 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("360 View Orientation", "360 檢視方向"),
("3:2 Pulldown: {}", "3:2 摺疊:{}"),
(
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.",
"一款從零編寫、開源的動態圖形與視覺效果合成軟件:原生支援 macOS、Windows 和 Linux,也可在瀏覽器中執行。屬於 ArtCraft 創意應用家族。",
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser.",
"一款從零編寫、開源的動態圖形與視覺效果合成軟件:原生支援 macOS、Windows 和 Linux,也可在瀏覽器中執行。",
),
("About EffectCraft", "關於 EffectCraft"),
("About...", "關於..."),
@@ -1174,7 +1176,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Delete selected project items", "刪除所選的專案項"),
("Delete this template", "刪除此範本"),
("Depth of Field", "景深"),
("Discord", "Discord"),
("Distribute Layers:", "分佈圖層:"),
("Don't Recover", "不恢復"),
("Don't Save", "不儲存"),
@@ -1247,7 +1248,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Install from File…", "從文件安裝…"),
("Intensity", "強度"),
("Inverted", "反轉"),
("Join the ArtCraft community on Discord", "加入 ArtCraft 的 Discord 社區"),
("Key: {}", "按鍵:{}"),
("LEARN", "學習"),
("Label", "標籤"),
@@ -1286,7 +1286,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Models folder: {}", "模型資料夾:{}"),
("Modifiers:", "修飾鍵:"),
("More", "更多"),
("More ArtCraft apps", "更多 ArtCraft 應用"),
("More tutorials", "更多教學"),
("Motion Blur", "運動模糊"),
("Motion Blur: {}", "動態模糊:{}"),
@@ -1546,7 +1545,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Import PDF / Illustrator File", "Import PDF / Illustrator File"),
("Import Photoshop File", "Import Photoshop File"),
("Interpret Unlabeled Alpha As", "Interpret Unlabeled Alpha As"),
("Join the ArtCraft Discord", "加入 ArtCraft Discord"),
("Mercury GPU Acceleration ({})", "Mercury GPU Acceleration ({})"),
("Mercury Software Only", "Mercury Software Only"),
("OCIO (built-in ACES config)", "OCIO (built-in ACES config)"),
@@ -1563,7 +1561,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("New Project", "新增專案"),
("Open Project…", "開啟專案…"),
("Templates", "範本"),
("EffectCraft home page", "EffectCraft 首頁"),
("EffectCraft on GitHub", "GitHub 上的 EffectCraft"),
("Motion graphics and visual effects · version {}", "動態圖形與視覺效果 · 版本 {}"),
("New Composition", "新增合成"),
@@ -1590,6 +1587,11 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("After Effects project", "After Effects 專案"),
("Lottie animation", "Lottie 動畫"),
("Project", "專案"),
("Minimize", "最小化"),
("Maximize", "最大化"),
("Restore", "還原"),
("Title Bar", "標題列"),
("Use System Title Bar on Windows and Linux", "在 Windows 和 Linux 上使用系統標題列"),
];
/// Ukrainian (uk): original translations of the same interface rows as the Chinese catalogs.
@@ -1605,8 +1607,8 @@ const UKRAINIAN: &[(&str, &str)] = &[
("360 View Orientation", "Орієнтація огляду 360°"),
("3:2 Pulldown: {}", "Перетворення кадрів 3:2: {}"),
(
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.",
"Незалежно розроблена програма з відкритим кодом для компонування анімованої графіки та візуальних ефектів: працює на macOS, Windows і Linux, а також у браузері. Належить до родини творчих програм ArtCraft.",
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser.",
"Незалежно розроблена програма з відкритим кодом для компонування анімованої графіки та візуальних ефектів: працює на macOS, Windows і Linux, а також у браузері.",
),
("About EffectCraft", "Про EffectCraft"),
("About...", "Про програму..."),
@@ -1712,7 +1714,6 @@ const UKRAINIAN: &[(&str, &str)] = &[
("Delete selected project items", "Видалити виділені елементи проєкту"),
("Delete this template", "Видалити цей шаблон"),
("Depth of Field", "Глибина різкості"),
("Discord", "Discord"),
("Distribute Layers:", "Розподілити шари:"),
("Don't Recover", "Не відновлювати"),
("Don't Save", "Не зберігати"),
@@ -1785,7 +1786,6 @@ const UKRAINIAN: &[(&str, &str)] = &[
("Install from File…", "Встановити з файлу…"),
("Intensity", "Інтенсивність"),
("Inverted", "Інвертовано"),
("Join the ArtCraft community on Discord", "Приєднайтеся до спільноти ArtCraft у Discord"),
("Key: {}", "Клавіша: {}"),
("LEARN", "НАВЧАННЯ"),
("Label", "Мітка"),
@@ -1824,7 +1824,6 @@ const UKRAINIAN: &[(&str, &str)] = &[
("Models folder: {}", "Тека моделей: {}"),
("Modifiers:", "Модифікатори:"),
("More", "Більше"),
("More ArtCraft apps", "Інші програми ArtCraft"),
("More tutorials", "Інші уроки"),
("Motion Blur", "Розмиття руху"),
("Motion Blur: {}", "Розмиття руху: {}"),
@@ -2100,7 +2099,6 @@ const UKRAINIAN: &[(&str, &str)] = &[
("Import PDF / Illustrator File", "Імпорт файлу PDF / Illustrator"),
("Import Photoshop File", "Імпорт файлу Photoshop"),
("Interpret Unlabeled Alpha As", "Інтерпретувати непозначену альфу як"),
("Join the ArtCraft Discord", "Приєднатися до Discord ArtCraft"),
("Mercury GPU Acceleration ({})", "Прискорення Mercury GPU ({})"),
("Mercury Software Only", "Лише програмний Mercury"),
("OCIO (built-in ACES config)", "OCIO (вбудована конфігурація ACES)"),
@@ -2146,6 +2144,11 @@ const UKRAINIAN: &[(&str, &str)] = &[
("After Effects project", "Проєкт After Effects"),
("Lottie animation", "Анімація Lottie"),
("Project", "Проєкт"),
("Minimize", "Згорнути"),
("Maximize", "Розгорнути"),
("Restore", "Відновити"),
("Title Bar", "Рядок заголовка"),
("Use System Title Bar on Windows and Linux", "Використовувати системний рядок заголовка у Windows і Linux"),
];
// Brazilian Portuguese.
@@ -2156,10 +2159,6 @@ const PORTUGUESE: &[(&str, &str)] = &[
("(missing)", "(ausente)"),
("360 View Orientation", "Orientação da vista 360°"),
("3:2 Pulldown: {}", "Pulldown 3:2: {}"),
(
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.",
"Um compositor de código aberto, desenvolvido em clean-room, para motion graphics e efeitos visuais: nativo no macOS, Windows e Linux, e no navegador. Parte da família de aplicativos criativos ArtCraft.",
),
("About EffectCraft", "Sobre o EffectCraft"),
("About...", "Sobre..."),
("Active Camera", "Câmera ativa"),
@@ -2253,7 +2252,6 @@ const PORTUGUESE: &[(&str, &str)] = &[
("Delete selected project items", "Excluir itens de projeto selecionados"),
("Delete this template", "Excluir este modelo"),
("Depth of Field", "Profundidade de campo"),
("Discord", "Discord"),
("Distribute Layers:", "Distribuir camadas:"),
("Don't Recover", "Não recuperar"),
("Don't Save", "Não salvar"),
@@ -2324,7 +2322,6 @@ const PORTUGUESE: &[(&str, &str)] = &[
("Install from File…", "Instalar a partir de arquivo…"),
("Intensity", "Intensidade"),
("Inverted", "Invertido"),
("Join the ArtCraft community on Discord", "Entre na comunidade ArtCraft no Discord"),
("Key: {}", "Tecla: {}"),
("LEARN", "APRENDER"),
("Label", "Rótulo"),
@@ -2360,7 +2357,6 @@ const PORTUGUESE: &[(&str, &str)] = &[
("Models folder: {}", "Pasta de modelos: {}"),
("Modifiers:", "Modificadores:"),
("More", "Mais"),
("More ArtCraft apps", "Mais aplicativos ArtCraft"),
("More tutorials", "Mais tutoriais"),
("Motion Blur", "Desfoque de movimento"),
("Motion Blur: {}", "Desfoque de movimento: {}"),
@@ -2590,10 +2586,6 @@ const CZECH: &[(&str, &str)] = &[
("(missing)", "(chybí)"),
("360 View Orientation", "Orientace zobrazení 360°"),
("3:2 Pulldown: {}", "Převod 3:2 pulldown: {}"),
(
"A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.",
"Kompoziční nástroj s otevřeným zdrojovým kódem pro pohyblivou grafiku a vizuální efekty, vyvinutý nezávisle metodou clean-room. Běží nativně na macOS, Windows a Linuxu i v prohlížeči. Patří do rodiny kreativních aplikací ArtCraft.",
),
("About EffectCraft", "O aplikaci EffectCraft"),
("About...", "O aplikaci…"),
("Active", "Aktivní"),
@@ -2694,7 +2686,6 @@ const CZECH: &[(&str, &str)] = &[
("Delete selected project items", "Odstranit vybrané položky projektu"),
("Delete this template", "Odstranit tuto šablonu"),
("Depth of Field", "Hloubka ostrosti"),
("Discord", "Discord"),
("Distribute Layers:", "Rozmístit vrstvy:"),
("Don't Recover", "Neobnovovat"),
("Don't Save", "Neukládat"),
@@ -2766,7 +2757,6 @@ const CZECH: &[(&str, &str)] = &[
("Install from File…", "Nainstalovat ze souboru…"),
("Intensity", "Intenzita"),
("Inverted", "Invertovaná"),
("Join the ArtCraft community on Discord", "Připojit se ke komunitě ArtCraft na Discordu"),
("Key: {}", "Klávesa: {}"),
("LEARN", "VÝUKA"),
("Label", "Štítek"),
@@ -2804,7 +2794,6 @@ const CZECH: &[(&str, &str)] = &[
("Models folder: {}", "Složka modelů: {}"),
("Modifiers:", "Modifikátory:"),
("More", "Další"),
("More ArtCraft apps", "Další aplikace ArtCraft"),
("More tutorials", "Další výukové lekce"),
("Motion Blur", "Rozostření pohybem"),
("Motion Blur: {}", "Rozostření pohybem: {}"),
@@ -3064,7 +3053,6 @@ const CZECH: &[(&str, &str)] = &[
("Import PDF / Illustrator File", "Importovat soubor PDF / Illustrator"),
("Import Photoshop File", "Importovat soubor Photoshop"),
("Interpret Unlabeled Alpha As", "Interpretovat neoznačenou alfu jako"),
("Join the ArtCraft Discord", "Připojit se na Discord ArtCraft"),
("Mercury GPU Acceleration ({})", "GPU akcelerace Mercury ({})"),
("Mercury Software Only", "Pouze softwarové zpracování Mercury"),
("OCIO (built-in ACES config)", "OCIO (vestavěná konfigurace ACES)"),
@@ -3127,6 +3115,7 @@ mod tests {
("color_picker.rs", include_str!("../color_picker.rs")),
("credits.rs", include_str!("../credits.rs")),
("header.rs", include_str!("../header.rs")),
("titlebar.rs", include_str!("../titlebar.rs")),
("menus.rs", include_str!("../menus.rs")),
("widgets.rs", include_str!("../widgets.rs")),
("panels/anim_tools.rs", include_str!("../panels/anim_tools.rs")),
+24 -3
View File
@@ -30,6 +30,7 @@ mod pointer_wrap;
pub mod prefs_live;
pub mod state;
pub mod theme;
pub mod titlebar;
pub mod widgets;
mod workspace_state;
@@ -318,7 +319,10 @@ pub struct EffectcraftApp {
/// Pointer position in comp pixels (Info panel).
pub(crate) pointer_comp: Option<[f32; 2]>,
pub(crate) dialog_state: panels::dialogs::DialogState,
/// macOS: the traffic lights sit over the top row (no system title bar).
pub integrated_titlebar: bool,
/// Windows and Linux: no OS decorations; the menu bar row is the title bar ([`titlebar`]).
pub custom_titlebar: bool,
/// Audio preview while playing (audio clock drives playback).
pub audio: Option<audio::AudioPlayback>,
/// Audio scrubbing's open output (Ctrl/Cmd-drag the current time, `playback.scrubAudio`).
@@ -409,6 +413,7 @@ impl EffectcraftApp {
pointer_comp: None,
dialog_state: Default::default(),
integrated_titlebar: false,
custom_titlebar: false,
audio: None,
scrub: None,
meter: Default::default(),
@@ -1661,15 +1666,28 @@ impl EffectcraftApp {
}
ui.painter().rect_filled(full, 0.0, t.app_bg);
let mut top = full.min.y;
if self.ui.show_menu_bar {
let mb = egui::Rect::from_min_size(full.min, egui::vec2(full.width(), 24.0));
if self.ui.show_menu_bar || self.custom_titlebar {
let h = if self.custom_titlebar { titlebar::HEIGHT } else { 24.0 };
let mb = egui::Rect::from_min_size(full.min, egui::vec2(full.width(), h));
if self.custom_titlebar || self.integrated_titlebar {
titlebar::drag_area(ui, mb);
}
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(mb));
child.painter().rect_filled(mb, 0.0, t.header_bg);
menus::menu_bar(self, &mut child);
if self.ui.show_menu_bar {
menus::menu_bar(self, &mut child);
}
if self.custom_titlebar {
titlebar::caption_buttons(self, ui, mb);
}
top = mb.max.y;
}
let header_h = 40.0;
let header = egui::Rect::from_min_size(egui::pos2(full.min.x, top), egui::vec2(full.width(), header_h));
// macOS without a menu row: the Tools bar is the title bar, under the traffic lights.
if self.integrated_titlebar && !self.ui.show_menu_bar {
titlebar::drag_area(ui, header);
}
header::show(self, ui, header);
let body = egui::Rect::from_min_max(egui::pos2(full.min.x + 4.0, header.max.y + 2.0), egui::pos2(full.max.x - 4.0, full.max.y - 4.0));
self.dock_area(ui, body);
@@ -1686,6 +1704,9 @@ impl EffectcraftApp {
panels::learn::coach(self, &ctx);
let lift = panels::media_panels::import_card(self, ui, full);
self.draw_toast(ui, full, lift);
if self.custom_titlebar {
titlebar::resize_zones(ui);
}
if self.session.config.is_some() && self.workspace_store.has_changes(&self.ui) && !ctx.input(|i| i.pointer.any_down()) {
if self.workspace_store.due(ctx.input(|i| i.time)) {
self.save_workspace_state();
+2 -5
View File
@@ -46,10 +46,6 @@ pub const UI_COMMANDS: &[UiCommand] = &[
uic!("view.safeMargins", "Title/Action Safe", [], None),
uic!("view.transparencyGrid", "Transparency Grid", [], None),
uic!("view.fastPreviews", "Fast Previews", [], None),
uic!("view.theme.toggle", "Next Appearance Mode", [], None),
uic!("view.theme.dark", "Theme: Dark", [], None),
uic!("view.theme.darker", "Theme: Darker", [], None),
uic!("view.theme.light", "Theme: Light", [], None),
uic!("timeline.zoomIn", "Zoom In Time", [], Some("=")),
uic!("timeline.zoomOut", "Zoom Out Time", [], Some("-")),
uic!("timeline.zoomFit", "Zoom to Fit Comp", [], None),
@@ -1833,7 +1829,8 @@ pub fn menu_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui) {
let mut clicked: Option<(String, Value)> = None;
let mut nav = crate::menu_keys::Nav::default();
ui.horizontal_centered(|ui| {
ui.add_space(6.0);
// macOS: clear of the traffic lights over the row.
ui.add_space(if app.integrated_titlebar { crate::titlebar::MAC_INSET } else { 6.0 });
egui::MenuBar::new().config(egui::containers::menu::MenuConfig::new().style(crate::widgets::menu_style)).ui(ui, |ui| {
for node in effectcraft_engine::menus::menu_bar() {
if let MenuNode::Submenu { label, children } = node {
+5 -27
View File
@@ -295,39 +295,17 @@ fn about_main(app: &mut EffectcraftApp, ui: &mut egui::Ui, t: &Tokens, cmd: &mut
t.text_dim,
);
ui.add_space(12.0);
ui.label(tr("A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps."));
ui.label(tr("A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser."));
ui.add_space(14.0);
let links: [(Icon, &str, &str, &'static str); 4] = [
(Icon::Chat, "Join the ArtCraft Discord", effectcraft_engine::links::DISCORD, "help.discord"),
(Icon::Globe, "ArtCraft website", effectcraft_engine::links::WEBSITE, "help.website"),
(Icon::Sparkle, "EffectCraft home page", effectcraft_engine::links::APP_PAGE, "help.appPage"),
(Icon::Code, "Source code on GitHub", effectcraft_engine::links::GITHUB, "help.github"),
];
let links: [(Icon, &str, &str, &'static str); 1] = [(Icon::Code, "Source code on GitHub", effectcraft_engine::links::GITHUB, "help.github")];
for (icon, label, url, c) in links {
let (r, resp) = ui.allocate_exact_size(vec2(ui.available_width(), 30.0), Sense::click());
let p = ui.painter();
let discord = c == "help.discord";
p.rect_filled(
r,
6.0,
if discord {
Color32::from_rgb(0x58, 0x65, 0xf2)
} else if resp.hovered() {
t.hover
} else {
t.field_bg
},
);
let foreground = if discord { Color32::WHITE } else { t.text };
p.rect_filled(r, 6.0, if resp.hovered() { t.hover } else { t.field_bg });
let foreground = t.text;
icons::paint(p, Rect::from_center_size(pos2(r.min.x + 18.0, r.center().y), vec2(15.0, 15.0)), icon, foreground);
p.text(pos2(r.min.x + 36.0, r.center().y), Align2::LEFT_CENTER, tr(label), Tokens::medium(12.5), foreground);
p.text(
pos2(r.max.x - 12.0, r.center().y),
Align2::RIGHT_CENTER,
url,
Tokens::ui(11.0),
if discord { Color32::from_white_alpha(200) } else { t.text_dim },
);
p.text(pos2(r.max.x - 12.0, r.center().y), Align2::RIGHT_CENTER, url, Tokens::ui(11.0), t.text_dim);
app.auto.add(&format!("about.{c}"), r, label);
if resp.clicked() {
*cmd = Some(c);
+7 -32
View File
@@ -1,6 +1,6 @@
//! The Home screen, laid out like After Effects' (shown over the whole workspace at launch, from
//! the Tools bar's Home button and by the Learn workspace). A left rail holds New Project / Open
//! Project, the Home / Templates / Learn pages and, at its foot, the ArtCraft community links.
//! Project, the Home / Templates / Learn pages and, at its foot, the project links.
//! The Home page welcomes you with quick-start tiles (New Composition, the demo project, Import,
//! New from Template) over the recent projects (File ▸ Open Recent, from Settings): a filterable
//! table with thumbnails and Name / Opened / Size / Kind columns. Templates is the New from
@@ -270,16 +270,9 @@ fn rail_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, rail:
}
y += 38.0;
}
// Community links at the foot (below the pages when the window is short).
let links = [
(Icon::Chat, "Join the ArtCraft Discord", "help.discord"),
(Icon::Globe, "getartcraft.com", "help.website"),
(Icon::Globe, "EffectCraft home page", "help.appPage"),
(Icon::Code, "EffectCraft on GitHub", "help.github"),
];
let sib = effectcraft_engine::links::SIBLINGS;
let sib_rows = sib.len().div_ceil(2) as f32;
let foot_h = 22.0 + links.len() as f32 * 30.0 + 22.0 + sib_rows * 26.0;
// Project link at the foot (below the pages when the window is short).
let links = [(Icon::Code, "EffectCraft on GitHub", "help.github")];
let foot_h = 22.0 + links.len() as f32 * 30.0;
let mut cy = (rail.max.y - 16.0 - foot_h).max(y + 16.0);
p.line_segment([pos2(x0, cy - 10.0), pos2(x0 + w, cy - 10.0)], Stroke::new(1.0, t.separator));
p.text(pos2(x0, cy + 6.0), Align2::LEFT_CENTER, tr("Community"), Tokens::semibold(12.0), t.text_dim);
@@ -287,34 +280,17 @@ fn rail_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, rail:
for (icon, label, cmd) in links {
let r = Rect::from_min_size(pos2(x0, cy), vec2(w, 26.0));
let resp = ui.interact(r, egui::Id::new(("home-link", cmd)), Sense::click());
let discord = cmd == "help.discord";
if discord {
p.rect_filled(r, 13.0, Color32::from_rgb(0x58, 0x65, 0xf2));
} else if resp.hovered() {
if resp.hovered() {
p.rect_filled(r, 13.0, t.hover);
}
let col = if discord { Color32::WHITE } else { t.text };
icons::paint(p, Rect::from_center_size(pos2(r.min.x + 16.0, r.center().y), vec2(13.0, 13.0)), icon, col);
p.text(pos2(r.min.x + 30.0, r.center().y), Align2::LEFT_CENTER, tr(label), Tokens::medium(12.0), col);
icons::paint(p, Rect::from_center_size(pos2(r.min.x + 16.0, r.center().y), vec2(13.0, 13.0)), icon, t.text);
p.text(pos2(r.min.x + 30.0, r.center().y), Align2::LEFT_CENTER, tr(label), Tokens::medium(12.0), t.text);
app.auto.add(&format!("home.{cmd}"), r, label);
if resp.clicked() {
let _ = app.session.execute(cmd, json!({}));
}
cy += 30.0;
}
p.text(pos2(x0, cy + 8.0), Align2::LEFT_CENTER, tr("More ArtCraft apps"), Tokens::ui(11.0), t.text_faint);
cy += 20.0;
let sw = (w - 6.0) / 2.0;
for (i, (name, slug)) in sib.iter().enumerate() {
let r = Rect::from_min_size(pos2(x0 + (i % 2) as f32 * (sw + 6.0), cy + (i / 2) as f32 * 26.0), vec2(sw, 22.0));
let resp = ui.interact(r, egui::Id::new(("sib", *slug)), Sense::click());
p.rect_filled(r, 11.0, if resp.hovered() { t.hover } else { t.field_bg });
p.text(r.center(), Align2::CENTER_CENTER, *name, Tokens::ui(11.0), t.text);
app.auto.add(&format!("home.sibling.{slug}"), r, name);
if resp.clicked() {
let _ = app.session.execute("help.sibling", json!({"app": slug}));
}
}
}
/// The Home page: the welcome, quick-start tiles and the recent projects table.
@@ -569,7 +545,6 @@ mod tests {
"home.file.open",
"home.app.newComp",
"home.file.openDemoProject",
"home.help.discord",
"home.help.github",
"home.recent.0",
"home.filter",
+1 -1
View File
@@ -486,7 +486,7 @@ mod tests {
assert_eq!(effectcraft_engine::color::Label::ALL[d.label].name(), "Blue");
d.duration = 0.5;
d.chapter = "Ch".into();
d.url = "https://getartcraft.com".into();
d.url = "https://example.com".into();
d.frame_target = "_self".into();
d.protected = true;
d.cue = false;
+1 -1
View File
@@ -225,7 +225,7 @@ fn theme_preview(ui: &mut egui::Ui, kind: ThemeKind, width: f32) {
painter.rect_filled(rect, 3.0, p.app_bg);
let bar = Rect::from_min_size(rect.min, vec2(rect.width(), 13.0));
painter.rect_filled(bar, 0.0, p.header_bg);
// The brand mark, the tools (the Selection tool is active) and the Discord pill.
// The brand mark, the tools (the Selection tool is active).
painter.rect_filled(Rect::from_min_size(bar.min + vec2(4.0, 3.0), vec2(7.0, 7.0)), 2.0, p.accent);
for i in 0..7 {
let c = if i == 0 { p.accent } else { p.icon };
+93 -7
View File
@@ -6,6 +6,7 @@
use std::sync::Arc;
use craft_ui::theme::catppuccin::Flavor;
use egui::{Color32, FontData, FontDefinitions, FontFamily, FontId, Stroke, TextStyle, Visuals};
use serde::{Deserialize, Serialize};
@@ -15,15 +16,29 @@ pub enum ThemeKind {
Dark,
Darker,
Light,
CatppuccinLatte,
CatppuccinFrappe,
CatppuccinMacchiato,
CatppuccinMocha,
}
impl ThemeKind {
pub const ALL: [ThemeKind; 7] = [
ThemeKind::Dark,
ThemeKind::Darker,
ThemeKind::Light,
ThemeKind::CatppuccinLatte,
ThemeKind::CatppuccinFrappe,
ThemeKind::CatppuccinMacchiato,
ThemeKind::CatppuccinMocha,
];
pub fn from_name(s: &str) -> Option<ThemeKind> {
match s.to_ascii_lowercase().as_str() {
"dark" | "default" => Some(ThemeKind::Dark),
"darker" | "darkest" => Some(ThemeKind::Darker),
"light" => Some(ThemeKind::Light),
_ => None,
let s = s.to_ascii_lowercase();
match s.as_str() {
"default" => Some(ThemeKind::Dark),
"darkest" => Some(ThemeKind::Darker),
_ => Self::ALL.into_iter().find(|k| k.name().eq_ignore_ascii_case(&s)),
}
}
@@ -33,11 +48,26 @@ impl ThemeKind {
ThemeKind::Dark => "dark",
ThemeKind::Darker => "darker",
ThemeKind::Light => "light",
ThemeKind::CatppuccinLatte => "catppuccinLatte",
ThemeKind::CatppuccinFrappe => "catppuccinFrappe",
ThemeKind::CatppuccinMacchiato => "catppuccinMacchiato",
ThemeKind::CatppuccinMocha => "catppuccinMocha",
}
}
/// The Catppuccin flavour behind this theme, if it is one.
pub fn flavor(self) -> Option<Flavor> {
match self {
ThemeKind::CatppuccinLatte => Some(Flavor::Latte),
ThemeKind::CatppuccinFrappe => Some(Flavor::Frappe),
ThemeKind::CatppuccinMacchiato => Some(Flavor::Macchiato),
ThemeKind::CatppuccinMocha => Some(Flavor::Mocha),
_ => None,
}
}
pub fn is_light(self) -> bool {
self == ThemeKind::Light
matches!(self, ThemeKind::Light | ThemeKind::CatppuccinLatte)
}
}
@@ -127,6 +157,59 @@ impl Tokens {
}
}
/// A Catppuccin flavour (accent Mauve), over `base`'s metrics and label colours.
fn catppuccin(base: Tokens, kind: ThemeKind, f: Flavor) -> Tokens {
let p = f.palette();
Tokens {
kind,
app_bg: p.crust,
header_bg: p.mantle,
panel_bg: p.base,
tab_text: p.subtext0,
tab_text_active: p.text,
focus: p.mauve,
accent: p.mauve,
accent_hover: p.lavender,
text: p.text,
text_dim: p.subtext0,
text_faint: p.overlay1,
icon: p.subtext1,
icon_active: p.text,
hover: p.surface0,
pressed: p.surface1,
field_bg: p.mantle,
field_border: p.surface1,
separator: p.surface0,
row: p.base,
row_alt: p.mantle,
row_selected: p.surface1,
hot_text: p.mauve,
tl_bg: p.mantle,
tl_ruler_bg: p.base,
tl_ruler_tick: p.overlay0,
tl_ruler_text: p.subtext0,
cti: p.mauve,
work_area: p.surface1,
cache_green: p.green,
cache_blue: p.blue,
keyframe: p.subtext0,
keyframe_selected: p.mauve,
pasteboard: p.crust,
timecode: p.mauve,
danger: p.red,
warning: p.peach,
fx_header: p.surface0,
fx_header_active: p.surface1,
tl_graph_selected: p.surface0,
switch_well: p.mantle,
tl_nav_bar: p.surface2,
tl_work_area_bar: p.overlay0,
expr_bg: p.mantle,
expr_text: p.peach,
..base
}
}
impl Tokens {
pub fn for_kind(kind: ThemeKind) -> Self {
let dark = Tokens {
@@ -183,8 +266,11 @@ impl Tokens {
row_h: 19.0,
gradients: true,
};
if let Some(f) = kind.flavor() {
return catppuccin(dark, kind, f);
}
match kind {
ThemeKind::Dark => dark,
ThemeKind::Dark | ThemeKind::CatppuccinLatte | ThemeKind::CatppuccinFrappe | ThemeKind::CatppuccinMacchiato | ThemeKind::CatppuccinMocha => dark,
ThemeKind::Darker => Tokens {
app_bg: Color32::from_rgb(0x08, 0x08, 0x08),
header_bg: Color32::from_rgb(0x16, 0x16, 0x16),
+290
View File
@@ -0,0 +1,290 @@
//! The window's own title bar on Windows and Linux, where the window has no OS decorations
//! ([`EffectcraftApp::custom_titlebar`]): the menu bar row is the title bar. Empty space in it
//! drags the window and a double-click there maximizes it, caption buttons at the far right
//! minimize, maximize/restore and close, and thin invisible zones along the window's edges resize
//! it. On macOS the traffic lights sit over the same row ([`MAC_INSET`]) and only the drag works.
use egui::{CursorIcon, Id, LayerId, Order, PointerButton, Rect, ResizeDirection, Sense, Stroke, Ui, ViewportCommand, pos2, vec2};
use serde_json::json;
use crate::EffectcraftApp;
use crate::i18n::tr;
/// One caption button: as wide as the native Windows ones.
pub const BUTTON_WIDTH: f32 = 46.0;
/// The three caption buttons together.
pub const WIDTH: f32 = 3.0 * BUTTON_WIDTH;
/// Height of the title row (menu bar) when it carries the caption buttons.
pub const HEIGHT: f32 = 32.0;
/// Room for the macOS traffic lights at the row's left end.
pub const MAC_INSET: f32 = 80.0;
/// Resize zone thickness along an edge, and the side of a corner zone.
const EDGE: f32 = 6.0;
const CORNER: f32 = 12.0;
#[derive(Clone, Copy, PartialEq, Debug)]
enum Caption {
Minimize,
Maximize,
Close,
}
/// Left to right.
const CAPTIONS: [Caption; 3] = [Caption::Minimize, Caption::Maximize, Caption::Close];
/// Caption button ids (stable, so tests and agents can find them).
fn caption_id(c: Caption) -> Id {
Id::new(("titlebar-caption", c as u8))
}
fn maximized(ctx: &egui::Context) -> bool {
ctx.input(|i| i.viewport().maximized.unwrap_or(false))
}
fn toggle_maximize(ctx: &egui::Context) {
ctx.send_viewport_cmd(ViewportCommand::Maximized(!maximized(ctx)));
}
/// The bar's background: a press-and-drag on empty space moves the window, a double-click toggles
/// maximize. Call it before laying out the bar so its widgets, registered later, take the clicks.
pub fn drag_area(ui: &Ui, rect: Rect) {
let ctx = ui.ctx();
let resp = ui.interact(rect, Id::new("titlebar-drag"), Sense::click_and_drag());
// egui hands a drag that starts on a click-only widget (a menu title) to the drag-sensing
// background below it, so remember whether the press itself landed on bare background.
if ctx.input(|i| i.pointer.any_pressed()) {
let bare = ctx.viewport(|v| v.hits.click.is_some_and(|w| w.id == resp.id));
ctx.data_mut(|d| d.insert_temp(resp.id, bare));
}
if resp.double_clicked() {
toggle_maximize(ctx);
} else if resp.drag_started_by(PointerButton::Primary) && ctx.data(|d| d.get_temp(resp.id).unwrap_or(false)) {
ctx.send_viewport_cmd(ViewportCommand::StartDrag);
}
}
/// Minimize, Maximize/Restore and Close at the right end of `bar` (the title row's outer rect),
/// full row height, flush with the window's edge so a maximized window's corner hits Close. Close
/// runs `app.quit`, which asks about unsaved changes first.
pub fn caption_buttons(app: &mut EffectcraftApp, ui: &mut Ui, bar: Rect) {
let t = app.tokens;
let max = maximized(ui.ctx());
let mut clicked = None;
for (i, c) in CAPTIONS.into_iter().enumerate() {
let r = Rect::from_min_size(pos2(bar.right() - WIDTH + i as f32 * BUTTON_WIDTH, bar.top()), vec2(BUTTON_WIDTH, bar.height() - 1.0));
let resp = ui.interact(r, caption_id(c), Sense::click());
let close = c == Caption::Close;
let fill = match (resp.is_pointer_button_down_on(), resp.hovered()) {
(true, _) if close => t.danger.gamma_multiply(0.8),
(_, true) if close => t.danger,
(true, _) => t.pressed,
(_, true) => t.hover,
_ => egui::Color32::TRANSPARENT,
};
ui.painter().rect_filled(r, 0.0, fill);
let ink = if close && resp.hovered() { egui::Color32::WHITE } else { t.icon };
paint_glyph(ui, c, max, r.center(), Stroke::new(1.0, ink));
let tip = match c {
Caption::Minimize => tr("Minimize"),
Caption::Maximize if max => tr("Restore"),
Caption::Maximize => tr("Maximize"),
Caption::Close => tr("Close"),
};
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, tip));
if resp.on_hover_text(tip).clicked() {
clicked = Some(c);
}
}
match clicked {
Some(Caption::Minimize) => ui.ctx().send_viewport_cmd(ViewportCommand::Minimized(true)),
Some(Caption::Maximize) => toggle_maximize(ui.ctx()),
Some(Caption::Close) => {
let ctx = ui.ctx().clone();
if let Err(e) = crate::menus::invoke(app, &ctx, "app.quit", json!({})) {
app.ui.status = e;
}
}
None => {}
}
}
/// The 10 pt line glyphs of the caption buttons.
fn paint_glyph(ui: &Ui, c: Caption, maximized: bool, at: egui::Pos2, s: Stroke) {
let p = ui.painter();
let g = Rect::from_center_size(at, vec2(10.0, 10.0));
match c {
Caption::Minimize => {
p.line_segment([g.left_center(), g.right_center()], s);
}
// Restore: a front square with the corner of the one behind it.
Caption::Maximize if maximized => {
let front = Rect::from_min_max(g.min + vec2(0.0, 2.0), g.max - vec2(2.0, 0.0));
p.rect_stroke(front, 0.0, s, egui::StrokeKind::Middle);
p.line(vec![g.min + vec2(2.0, 2.0), g.min + vec2(2.0, 0.0), g.right_top(), g.max - vec2(0.0, 2.0), g.max - vec2(2.0, 2.0)], s);
}
Caption::Maximize => {
p.rect_stroke(g, 0.0, s, egui::StrokeKind::Middle);
}
Caption::Close => {
p.line_segment([g.left_top(), g.right_bottom()], s);
p.line_segment([g.right_top(), g.left_bottom()], s);
}
}
}
/// The edge and corner zones of a window with content rect `w`: (zone, direction, cursor). Corners
/// come last so they win where they overlap the edges.
fn zones(w: Rect) -> [(Rect, ResizeDirection, CursorIcon); 8] {
use ResizeDirection::*;
let (e, c) = (EDGE, CORNER);
[
(Rect::from_min_max(w.left_top(), pos2(w.right(), w.top() + e)), North, CursorIcon::ResizeVertical),
(Rect::from_min_max(pos2(w.left(), w.bottom() - e), w.right_bottom()), South, CursorIcon::ResizeVertical),
(Rect::from_min_max(w.left_top(), pos2(w.left() + e, w.bottom())), West, CursorIcon::ResizeHorizontal),
(Rect::from_min_max(pos2(w.right() - e, w.top()), w.right_bottom()), East, CursorIcon::ResizeHorizontal),
(Rect::from_min_size(w.left_top(), vec2(c, c)), NorthWest, CursorIcon::ResizeNwSe),
(Rect::from_min_size(w.right_top() - vec2(c, 0.0), vec2(c, c)), NorthEast, CursorIcon::ResizeNeSw),
(Rect::from_min_size(w.left_bottom() - vec2(0.0, c), vec2(c, c)), SouthWest, CursorIcon::ResizeNeSw),
(Rect::from_min_size(w.right_bottom() - vec2(c, c), vec2(c, c)), SouthEast, CursorIcon::ResizeNwSe),
]
}
/// Invisible resize zones along the window's edges, above everything else; a press in one starts
/// an OS resize in that direction. None while the window is maximized or fullscreen.
pub fn resize_zones(ui: &mut Ui) {
let ctx = ui.ctx().clone();
if maximized(&ctx) || ctx.input(|i| i.viewport().fullscreen.unwrap_or(false)) {
return;
}
let w = ctx.content_rect();
let layer = LayerId::new(Order::Foreground, Id::new("titlebar-resize"));
let zui = ui.new_child(egui::UiBuilder::new().layer_id(layer).max_rect(w));
for (i, (r, dir, cursor)) in zones(w).into_iter().enumerate() {
let resp = zui.interact(r, Id::new(("titlebar-resize", i)), Sense::drag()).on_hover_cursor(cursor);
if resp.drag_started_by(PointerButton::Primary) {
ctx.send_viewport_cmd(ViewportCommand::BeginResize(dir));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use egui::{Event, Pos2, RawInput, ViewportId};
/// A headless window `width` pt wide with a title row: the drag area, the caption buttons and
/// the resize zones.
struct Win {
ctx: egui::Context,
app: EffectcraftApp,
width: f32,
time: f64,
maximized: bool,
}
impl Win {
fn new(width: f32) -> Self {
let ctx = egui::Context::default();
let mut app = EffectcraftApp::new(effectcraft_engine::Session::default());
app.custom_titlebar = true;
app.apply_prefs(&ctx);
Self { ctx, app, width, time: 0.0, maximized: false }
}
fn frame(&mut self, events: Vec<Event>) -> egui::FullOutput {
self.time += 0.05;
let mut input =
RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(self.width, 600.0))), time: Some(self.time), events, ..Default::default() };
if let Some(v) = input.viewports.get_mut(&ViewportId::ROOT) {
v.maximized = Some(self.maximized);
}
let app = &mut self.app;
let width = self.width;
let mut out = self.ctx.run_ui(input, |ui| {
let bar = Rect::from_min_size(Pos2::ZERO, vec2(width, HEIGHT));
drag_area(ui, bar);
caption_buttons(app, ui, bar);
resize_zones(ui);
});
out.textures_delta.clear();
out
}
fn gesture(&mut self, path: &[Pos2]) -> Vec<ViewportCommand> {
let button = |p: Pos2, pressed| Event::PointerButton { pos: p, button: PointerButton::Primary, pressed, modifiers: Default::default() };
let mut events = vec![vec![Event::PointerMoved(path[0])], vec![button(path[0], true)]];
events.extend(path[1..].iter().map(|p| vec![Event::PointerMoved(*p)]));
events.push(vec![button(*path.last().unwrap_or(&path[0]), false)]);
events.into_iter().flat_map(|e| self.frame(e).viewport_output.remove(&ViewportId::ROOT).map(|v| v.commands).unwrap_or_default()).collect()
}
fn click(&mut self, p: Pos2) -> Vec<ViewportCommand> {
self.gesture(&[p])
}
fn caption(&self, c: Caption) -> Rect {
self.ctx.read_response(caption_id(c)).map(|r| r.rect).unwrap_or(Rect::NOTHING)
}
}
#[test]
fn caption_buttons_take_the_right_end_of_the_row() {
for width in [800.0, 1440.0] {
let mut w = Win::new(width);
w.frame(vec![]);
w.frame(vec![]);
let [min, max, close] = CAPTIONS.map(|c| w.caption(c));
assert_eq!((close.right(), close.top()), (width, 0.0), "Close sits in the window's corner at {width}");
assert!(close.height() >= 30.0 && [min, max, close].iter().all(|r| r.width() == BUTTON_WIDTH));
assert_eq!((min.right(), max.right()), (max.left(), close.left()));
}
}
#[test]
fn minimize_maximize_and_close_follow_the_window_state() {
let mut w = Win::new(1000.0);
w.frame(vec![]);
let (min, max, close) = (w.caption(Caption::Minimize).center(), w.caption(Caption::Maximize).center(), w.caption(Caption::Close).center());
assert!(w.click(min).contains(&ViewportCommand::Minimized(true)));
assert!(w.click(max).contains(&ViewportCommand::Maximized(true)));
w.maximized = true;
assert!(w.click(max).contains(&ViewportCommand::Maximized(false)), "Restore");
// Close goes through app.quit (which asks about unsaved changes) rather than the OS.
assert!(w.click(close).contains(&ViewportCommand::Close));
}
#[test]
fn empty_row_space_drags_and_double_click_maximizes() {
let mut w = Win::new(1440.0);
w.frame(vec![]);
let empty = pos2(700.0, 16.0);
let cmds = w.gesture(&[empty, empty + vec2(12.0, 4.0), empty + vec2(30.0, 8.0)]);
assert_eq!(cmds.iter().filter(|c| matches!(c, ViewportCommand::StartDrag)).count(), 1, "{cmds:?}");
let mut cmds = w.click(empty);
cmds.extend(w.click(empty));
assert!(cmds.contains(&ViewportCommand::Maximized(true)), "{cmds:?}");
}
#[test]
fn edge_zones_resize_unless_maximized() {
use ResizeDirection::*;
let mut w = Win::new(1000.0);
w.frame(vec![]);
for (p, dir) in [
(pos2(1.0, 300.0), West),
(pos2(998.0, 300.0), East),
(pos2(500.0, 1.0), North),
(pos2(500.0, 598.0), South),
(pos2(2.0, 2.0), NorthWest),
(pos2(997.0, 3.0), NorthEast),
(pos2(3.0, 597.0), SouthWest),
(pos2(998.0, 598.0), SouthEast),
] {
let cmds = w.gesture(&[p, p + vec2(6.0, 6.0)]);
assert_eq!(cmds, vec![ViewportCommand::BeginResize(dir)], "at {p:?}");
}
w.maximized = true;
let cmds = w.gesture(&[pos2(1.0, 300.0), pos2(8.0, 300.0)]);
assert!(!cmds.iter().any(|c| matches!(c, ViewportCommand::BeginResize(_))), "{cmds:?}");
}
}
-11
View File
@@ -37,17 +37,6 @@ fn click(h: &mut Harness<'_, EffectcraftApp>, id: &str) {
h.run_steps(2);
}
/// #581: the header's Discord pill fits its label with even padding (it was a fixed 104 px with
/// the text at its left).
#[test]
fn discord_pill_fits_its_label() {
let h = harness();
let e = h.state().auto.find("header.discord").expect("the Discord pill").clone();
let text = h.ctx.fonts_mut(|f| f.layout_no_wrap("Discord".into(), effectcraft_ui_egui::theme::Tokens::semibold(12.0), egui::Color32::WHITE).size().x);
// Padding 10, the 14 px icon, a 6 px gap, the text, padding 12.
assert!((e.rect[2] - (text + 42.0)).abs() < 0.5, "{} for a {text} px label", e.rect[2]);
}
#[test]
fn window_menu_opens_the_new_panels() {
let mut h = harness();
+1 -1
View File
@@ -1,4 +1,4 @@
//! The toolbar's Fill / Stroke swatch with Linear or Radial Gradient (#612, reported on Discord):
//! The toolbar's Fill / Stroke swatch with Linear or Radial Gradient (#612, reported by a user):
//! it opens the Gradient Editor on the selected shape layer's Gradient Fill / Gradient Stroke
//! Colors, as in After Effects, instead of Fill / Stroke Options. The words still open the
//! options, a Solid Color swatch the colour picker, Alt-click still steps through the kinds, and
@@ -432,7 +432,7 @@ fn timeline_selected_effect_row_highlights() {
/// Right-clicking a layer in the Timeline keeps the selection when the layer is part of it,
/// so the context menu's commands act on every selected layer; right-clicking an unselected
/// layer selects only it (After Effects). Reported on Discord: Pre-compose and Delete from the
/// layer selects only it (After Effects). Reported by a user: Pre-compose and Delete from the
/// menu acted on the right-clicked layer only (#878).
#[test]
fn timeline_context_menu_acts_on_the_whole_selection() {
-42
View File
@@ -1,42 +0,0 @@
ArtCraft brand assets: license
The ArtCraft name, wordmark and mark (the "ArtCraft Marks"), including every file in this
directory in every color, size and format, are owned by the ArtCraft Team. They are trademarks
and are NOT open source. They are not covered by EffectCraft's MIT OR Apache-2.0 license, and nothing in
that license, in NOTICE or in any other file of this repository grants any right to them.
Permission
The ArtCraft Team grants a limited, non-exclusive, non-transferable, revocable, royalty-free
permission to use the ArtCraft Marks only:
1. as part of copies of this repository; and
2. within EffectCraft itself, its documentation and its official builds, to identify EffectCraft as made
by the ArtCraft team and community and to link to getartcraft.com and its community.
Conditions
The permission holds only while every one of these is true:
- The marks are used exactly as supplied in this directory. Do not redraw, recolor, crop,
stretch, rotate, animate, outline or add effects to them, extract parts of them, or combine
them with any other name, mark or artwork.
- They are used only in the context of EffectCraft, and only to say that the ArtCraft team makes it.
They must never suggest that the ArtCraft Team made, sponsors, endorses, certifies or supports
any other product, service, fork, modified version, plug-in, organization or person.
- Neither the marks nor the ArtCraft name appear in the name, logo, icon, domain, social media
handle, app-store listing, advertising or marketing of anything other than EffectCraft.
Forks and modified versions
If you distribute or publish a modified version of EffectCraft, or any work derived from it, you must
remove the ArtCraft Marks from it (or replace them with your own) and must not call it ArtCraft or
present it as an ArtCraft product. You may say, in plain text, that your work is based on EffectCraft by
the ArtCraft team. A fork kept only to propose changes back to this repository may keep the marks
unchanged.
Everything else
Any use not expressly permitted here needs prior written permission from the ArtCraft Team
(https://getartcraft.com/). The permission ends automatically for anyone who breaks these terms.
All other rights are reserved.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

@@ -1,8 +0,0 @@
asset: artcraft-logo-white.png
title: ArtCraft wordmark, light ink for dark backgrounds
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

@@ -1,8 +0,0 @@
asset: artcraft-logo-white.svg
title: ArtCraft wordmark, light ink for dark backgrounds
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-8
View File
@@ -1,8 +0,0 @@
asset: artcraft-logo.png
title: ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

-8
View File
@@ -1,8 +0,0 @@
asset: artcraft-logo.svg
title: ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

@@ -1,8 +0,0 @@
asset: artcraft-mark-black.png
title: ArtCraft symbol ("A" mark), black
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
-4
View File
@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -19.42 136.34 136.34" role="img" aria-label="ArtCraft">
<title>ArtCraft</title>
<path fill="#0b0b0c" d="M104.28,49.49L81.55,0h-31.23l-3.17,4.76L14.75,53.63,0,75.85l21.55,21.55,63.79-36.94,16.99,37.04,14.01-21.74-12.06-26.27ZM32.89,65.66l32.42-48.87,10.91,23.77-43.32,25.09Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 341 B

@@ -1,8 +0,0 @@
asset: artcraft-mark-black.svg
title: ArtCraft symbol ("A" mark), black
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

-8
View File
@@ -1,8 +0,0 @@
asset: artcraft-mark.png
title: ArtCraft symbol ("A" mark), blue
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
-4
View File
@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -19.42 136.34 136.34" role="img" aria-label="ArtCraft">
<title>ArtCraft</title>
<path fill="#4e7bfb" d="M104.28,49.49L81.55,0h-31.23l-3.17,4.76L14.75,53.63,0,75.85l21.55,21.55,63.79-36.94,16.99,37.04,14.01-21.74-12.06-26.27ZM32.89,65.66l32.42-48.87,10.91,23.77-43.32,25.09Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 341 B

-8
View File
@@ -1,8 +0,0 @@
asset: artcraft-mark.svg
title: ArtCraft symbol ("A" mark), blue
author: ArtCraft Team
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft Team)
license: LicenseRef-ArtCraft-Trademark — ArtCraft name, wordmark and logos are trademarks of the ArtCraft Team, not open source and not covered by EffectCraft's MIT OR Apache-2.0 license; usable only unmodified and only in the context of EffectCraft, under docs/brand/LICENSE-brand.txt.
license-file: docs/brand/LICENSE-brand.txt
added: 2026-10-01 by Claude (agent) for Brandon Thomas
notes: First-party ArtCraft brand mark owned by the ArtCraft Team, kept in docs/brand for the README and brand use: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and modified versions must remove it (docs/brand/LICENSE-brand.txt).
+2 -2
View File
File diff suppressed because one or more lines are too long
+7 -4
View File
@@ -55,10 +55,10 @@ Commands (CLI, MCP, control channel):
background, from the current time on, until it is cached or the budget is full
- `previews.fastPreviews`: Fast Previews (Draft 3D, Faster Effects)
- `appearance.appearanceMode`: Appearance Mode (`auto` / `dark` / `light`). `auto` (Sync with System) follows the operating system's light or dark appearance while the app runs: on Linux the desktop app reads the XDG desktop portal (`org.freedesktop.appearance` `color-scheme`) once and then listens for its `SettingChanged` signal on a worker thread, repainting only when the value changes (nothing polls); `gsettings` runs at most once at start-up, by absolute path, when the portal gives no answer. Elsewhere it uses what the window system reports. With no answer it shows the dark theme. New installs stay `dark`; Sync with System is opt-in. The Tools bar's appearance button (a monitor, sun or moon) cycles Sync with System, Light and Dark (`view.theme.toggle`, Next Appearance Mode).
- `appearance.darkTheme`: Dark Theme (`dark` / `darker`), shown in dark appearance
- `appearance.lightTheme`: Light Theme (`light`), shown in light appearance. Settings ▸ Appearance shows the mode above a light and a dark theme card, each with a preview of the EffectCraft window in that theme.
- `appearance.favoriteThemes`: a list of unique theme ids (`dark`, `darker`, `light`), empty by default. Hover a theme row to star it; saved favorites keep an accent-colored star. Right-click the appearance button for Appearance Settings and favorites grouped light first, then dark. Selecting a favorite fixes its appearance family and preserves the opposite family choice; left-click still cycles modes. Settings OK saves favorites and Cancel restores them. Unknown or duplicate ids are rejected by `prefs.set` and removed when loading old settings.
- `appearance.theme`: the single theme from before appearance modes, kept for older settings files and automation. A saved file with only `theme` loads as a fixed Dark or Light mode showing it; setting it (`prefs.set`, `ui.set {"theme": …}`, `view.theme.dark|darker|light`) selects that theme and fixes the mode to its family. While the mode is Dark or Light it mirrors the theme in use.
- `appearance.darkTheme`: Dark Theme (`dark`, `darker`, `catppuccinFrappe`, `catppuccinMacchiato` or `catppuccinMocha`), shown in dark appearance
- `appearance.lightTheme`: Light Theme (`light` or `catppuccinLatte`), shown in light appearance. Settings ▸ Appearance shows the mode above a light and a dark theme card, each with a preview of the EffectCraft window in that theme.
- `appearance.favoriteThemes`: a list of unique theme ids (any theme id, such as `dark`, `darker`, `light` or `catppuccinMocha`), empty by default. Hover a theme row to star it; saved favorites keep an accent-colored star. Right-click the appearance button for Appearance Settings and favorites grouped light first, then dark. Selecting a favorite fixes its appearance family and preserves the opposite family choice; left-click still cycles modes. Settings OK saves favorites and Cancel restores them. Unknown or duplicate ids are rejected by `prefs.set` and removed when loading old settings.
- `appearance.theme`: the single theme from before appearance modes, kept for older settings files and automation. A saved file with only `theme` loads as a fixed Dark or Light mode showing it; setting it (`prefs.set`, `ui.set {"theme": …}`, `view.theme.dark|darker|light|catppuccinLatte|catppuccinFrappe|catppuccinMacchiato|catppuccinMocha`; View ▸ Preferences ▸ Theme) selects that theme and fixes the mode to its family. While the mode is Dark or Light it mirrors the theme in use.
- `appearance.brightness`: Brightness
- `appearance.uiScale`: UI Scale, the size of the whole interface (any whole percentage from
75 to 200, on top of the display's own scaling), dragged or typed; it applies as soon as it is
@@ -106,6 +106,9 @@ Commands (CLI, MCP, control channel):
- `appearance.useGradients`: soft gradient on panel tab strips
- `appearance.inWindowMenuBarMac`: on macOS, draw the menu bar inside the window instead of the
native menu bar
- `appearance.systemTitleBar`: on Windows and Linux, keep the system's title bar instead of the
window's own (the menu bar row with minimize, maximize and close buttons); applies from the next
launch
- `grids.gridStyle`, `grids.guideStyle`: lines, dashed lines or dots (grid, proportional grid,
guides); `grids.proportionalHorizontal` / `grids.proportionalVertical`: View ▸ Show
Proportional Grid divisions
+2 -2
View File
@@ -252,7 +252,7 @@ agent-generated maintainer replies:
list of Mac issues, so people are trying it on real work.
- **Open issues (136):** ≈ 95 are core-path bugs (masks, keyframes, timeline, preview, audio,
text, render, crashes; this includes 15 community `fix(...)` proposals), ≈ 40 are feature
requests or niche (plug-in hosting, OpenFX, ffmpeg export, Discord presence, Store listing,
requests or niche (plug-in hosting, OpenFX, ffmpeg export, Store listing,
rigging tools). Mostly core-path bugs, which matches the discounts above.
## Out of scope by design
@@ -828,7 +828,7 @@ meanwhile. Footage, solids, proxies and the project settings are hashed by value
states are kept, so a long drag doesn't hold on to every intermediate state. The disk cache's
content key now also covers proxies and Use Proxy.
Since (M13.30–M13.31): the Tools bar and About dialog carry the ArtCraft mark. The
Since (M13.30–M13.31): the Tools bar and About dialog carry the app mark. The
Home screen is laid out like After Effects' and covers the whole workspace. A left rail holds New
Project / Open Project, the Home, Templates and Learn pages and, at its foot, the community
links. The Home page has a "Welcome to EffectCraft" heading, quick-start tiles (New Composition,
@@ -20,10 +20,10 @@
<launchable type="desktop-id">ai.storyteller.effectcraft.desktop</launchable>
<icon type="stock">ai.storyteller.effectcraft</icon>
<!-- Only url types known to appstream 0.15 (Ubuntu 22.04, where CI validates): no vcs-browser. -->
<url type="homepage">https://getartcraft.com/apps/effectcraft</url>
<url type="homepage">https://github.com/storytold/effectcraft</url>
<url type="bugtracker">https://github.com/storytold/effectcraft/issues</url>
<url type="help">https://github.com/storytold/effectcraft</url>
<url type="contact">https://discord.gg/artcraft</url>
<url type="contact">https://github.com/storytold/effectcraft/issues</url>
<categories>
<category>Graphics</category>
<category>RasterGraphics</category>
+1 -1
View File
@@ -14,7 +14,7 @@ section: graphics
priority: optional
maintainer: ${EFFECTCRAFT_MAINTAINER}
vendor: Learning Machines LLC
homepage: https://getartcraft.com/apps/effectcraft
homepage: https://github.com/storytold/effectcraft
license: MIT OR Apache-2.0
description: |-
Open-source motion graphics and visual effects compositor.
+3 -3
View File
@@ -27,9 +27,9 @@
<Icon Id="EffectcraftIcon.ico" SourceFile="$(var.IconPath)" />
<Property Id="ARPPRODUCTICON" Value="EffectcraftIcon.ico" />
<Property Id="ARPURLINFOABOUT" Value="https://getartcraft.com/apps/effectcraft" />
<Property Id="ARPURLUPDATEINFO" Value="https://getartcraft.com/apps/effectcraft" />
<Property Id="ARPHELPLINK" Value="https://discord.gg/artcraft" />
<Property Id="ARPURLINFOABOUT" Value="https://github.com/storytold/effectcraft" />
<Property Id="ARPURLUPDATEINFO" Value="https://github.com/storytold/effectcraft" />
<Property Id="ARPHELPLINK" Value="https://github.com/storytold/effectcraft/issues" />
<!-- An upgrade, repair or silent reinstall goes where the installed copy already is: the App Paths
"Path" value written below, or for releases before this installer change (no Path value; they
+27 -1
View File
@@ -6,6 +6,7 @@
Produces, in $env:DIST (default: dist/release):
effectcraft-<version>-windows-<arch>.msi per-machine installer (WiX v5)
effectcraft-<version>-windows-<arch>-portable.zip effectcraft.exe + effectcraft-cli.exe
effectcraft-cli-<version>-windows-<arch>.zip effectcraft-cli.exe standalone
The binaries link the C runtime statically (+crt-static), so neither the MSI nor the portable
zip needs the Visual C++ redistributable. Signing is delegated to sign.ps1 (skipped with a
@@ -25,6 +26,7 @@ param(
)
$ErrorActionPreference = 'Stop'
$Root = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
Set-Location $Root
function Invoke-Native([string] $What, [scriptblock] $Block) {
Write-Output "==> $What"
@@ -56,6 +58,17 @@ if (-not $env:EFFECTCRAFT_BUILD_DATE) { $env:EFFECTCRAFT_BUILD_DATE = (Get-Date)
Write-Output "EffectCraft $Version for Windows $Arch ($Target)"
if (-not $SkipBuild) {
# Locate rc.exe if not in PATH
if (-not (Get-Command rc.exe -ErrorAction SilentlyContinue)) {
$kits = Join-Path ${env:ProgramFiles(x86)} 'Windows Kits\10\bin'
$foundRc = Get-ChildItem -Path $kits -Recurse -Filter rc.exe -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match '\\x64\\rc\.exe$' } |
Sort-Object FullName -Descending | Select-Object -First 1
if ($foundRc) {
$env:PATH = "$($foundRc.DirectoryName);$env:PATH"
}
}
# Static CRT: no VC++ redistributable needed. Scoped to the target so host build scripts and
# proc-macros are unaffected.
$flagVar = 'CARGO_TARGET_' + ($Target.ToUpper() -replace '-', '_') + '_RUSTFLAGS'
@@ -114,6 +127,19 @@ $Zip = Join-Path $Dist "effectcraft-$Version-windows-$Arch-portable.zip"
Remove-Item -Force $Zip -ErrorAction SilentlyContinue
Compress-Archive -Path $Portable -DestinationPath $Zip
# ---- cli zip -----------------------------------------------------------------------------------
$CliPortable = Join-Path $TargetDir "windows-package\effectcraft-cli-$Version-windows-$Arch"
Remove-Item -Recurse -Force $CliPortable -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $CliPortable | Out-Null
Copy-Item (Join-Path $Stage 'effectcraft-cli.exe') $CliPortable
foreach ($f in 'README.md', 'LICENSE', 'LICENSE-MIT', 'LICENSE-APACHE') {
$p = Join-Path $Root $f
if (Test-Path $p) { Copy-Item $p $CliPortable }
}
$CliZip = Join-Path $Dist "effectcraft-cli-$Version-windows-$Arch.zip"
Remove-Item -Force $CliZip -ErrorAction SilentlyContinue
Compress-Archive -Path $CliPortable -DestinationPath $CliZip
# Smoke-test the CLI when this machine can run it. An ARM64 build made on an x64 runner can't run
# here; .github/workflows/windows-arm64.yml installs and runs it on ARM64 instead.
$HostArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()
@@ -122,4 +148,4 @@ if ($Arch -ne 'arm64' -or $HostArch -eq 'arm64') {
} else {
Write-Output "skipping effectcraft-cli --version: an $Arch build doesn't run on this $HostArch machine"
}
Get-Item $Msi, $Zip | Format-Table Name, Length
Get-Item $Msi, $Zip, $CliZip | Format-Table Name, Length
Submodule tools/craft-skills added at 2c1fed9237