Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 593d923f09 | |||
| 660f26e2fd | |||
| fe23624202 | |||
| 8b3d7e8516 | |||
| 115cf2b972 | |||
| 91efb894f7 |
@@ -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:
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "tools/craft-skills"]
|
||||
path = tools/craft-skills
|
||||
url = https://git.marcospaulo.dev.br/storytold/craft-skills.git
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 — 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
|
||||
|
||||
[](https://www.star-history.com/?repos=storytold%2Feffectcraft&type=date&legend=top-left)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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}")
|
||||
}
|
||||
|
||||
@@ -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] ---
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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")));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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")),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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:?}");
|
||||
}
|
||||
}
|
||||
@@ -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,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() {
|
||||
|
||||
@@ -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.
|
||||
|
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).
|
||||
|
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).
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@@ -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).
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
@@ -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).
|
||||
|
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).
|
||||
@@ -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).
|
||||
|
Before Width: | Height: | Size: 24 KiB |
@@ -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).
|
||||
@@ -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 |
@@ -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).
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||