Merge main into M13 (web app): community links and ArtCraft wordmark
Co-Authored-By: Claude Opus 5.5 <[email protected]>
No files matched your search
@@ -43,6 +43,11 @@ reproduce an image (for example, point lists traced from someone else's icon).
|
||||
`crates/media`, procedural looks in `crates/render`) 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/`
|
||||
(supplied by the ArtCraft team) are trademarks, all rights reserved, used with permission. 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
|
||||
|
||||
|
||||
@@ -78,3 +78,19 @@ is traced or derived from Adobe artwork.
|
||||
| Demo footage (ocean sunset, aurora, city night, dunes, forest, plasma), bars and tone, counting leader, colour matte | `crates/media/src/generators.rs` |
|
||||
| Lumetri "Look" presets (Teal & Orange, Warm Film, …) | `crates/render/src/effects.rs` (`apply_look`): procedural colour transforms, not LUT files |
|
||||
| Colour palette and layout metrics | `crates/ui-egui/src/theme.rs`: colour values and sizes only; no artwork |
|
||||
|
||||
## First-party brand marks
|
||||
|
||||
The ArtCraft name and logo are trademarks of the ArtCraft team, used in FilmCraft with permission. They are not
|
||||
openly licensed and are not covered by the project licence; forks must remove or replace 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 trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-logo-white.svg` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark, light ink for dark backgrounds) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-logo.png` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-logo.svg` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-mark-black.png` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), black) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-mark-black.svg` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), black) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-mark.png` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), blue) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
| `docs/brand/artcraft-mark.svg` | ArtCraft team | Original work: first-party ArtCraft brand mark (ArtCraft symbol ("A" mark), blue) | ArtCraft trademark, all rights reserved; used with permission (not MIT/Apache) |
|
||||
@@ -1,29 +1,68 @@
|
||||
<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">FilmCraft</h1>
|
||||
|
||||
<p align="center">
|
||||
<b>Professional video editing, rebuilt from scratch in pure Rust.</b><br>
|
||||
An open-source, clean-room take on the Adobe Premiere Pro workflow: native on macOS, Windows and Linux, and in the browser via WebAssembly.<br>
|
||||
<i>By the artcraft team.</i>
|
||||
<b>Professional video editing, rebuilt from scratch in pure Rust.</b>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
An open-source, clean-room take on the Adobe Premiere Pro workflow: native on macOS, Windows and Linux, and in the browser via WebAssembly.<br>
|
||||
By the ArtCraft team.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#license"><img alt="License: MIT or Apache-2.0" src="https://img.shields.io/badge/license-MIT%20%7C%20Apache--2.0-8b5cf6"></a>
|
||||
<img alt="Written in pure Rust" src="https://img.shields.io/badge/pure-Rust-6a3fd6?logo=rust&logoColor=white">
|
||||
<img alt="Runs on macOS, Windows and Linux" src="https://img.shields.io/badge/runs%20on-macOS%20%7C%20Windows%20%7C%20Linux-8b5cf6">
|
||||
<a href="#status"><img alt="Status: young and moving fast" src="https://img.shields.io/badge/status-young%20and%20moving%20fast-6a3fd6"></a>
|
||||
</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/filmcraft"><b>FilmCraft 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/filmcraft-hero.png" alt="FilmCraft in the Color workspace, mid-way through an Apollo 11 documentary cut from NASA footage: the Program monitor on the Saturn V clearing the launch tower with a Launch Complex 39A lower third and an air-to-ground subtitle, Effect Controls with Lumetri and Scale keyframes on the shot, the Lumetri Color panel, bins of NASA selects, and a timeline with 49 picture cuts, B-roll, titles, a caption track, mission audio, a ducked music bed, named markers, a rendered section and live loudness meters" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub><i>Apollo 11 - Tranquility</i>: a three-minute documentary edit of NASA's 1969 launch, landing and moonwalk film, with subtitles from the mission transcript. Every frame in these screenshots comes from public-domain footage, decoded, composited and graded by FilmCraft's own code.</sub></p>
|
||||
|
||||
> [!NOTE]
|
||||
> **ArtCraft is a community of artists from all walks of life.** Painters, photographers,
|
||||
> filmmakers, illustrators, designers, animators, hobbyists, and people who picked up a pencil
|
||||
> last week. If you make things, you're one of us. **[Come say hi on Discord](https://discord.gg/artcraft).**
|
||||
|
||||
<p align="center">
|
||||
<a href="#edit">Edit</a> ·
|
||||
<a href="#color">Color</a> ·
|
||||
<a href="#effects-and-motion">Effects</a> ·
|
||||
<a href="#audio">Audio</a> ·
|
||||
<a href="#titles-and-captions">Titles</a> ·
|
||||
<a href="#formats-and-codecs">Formats</a> ·
|
||||
<a href="#export">Export</a> ·
|
||||
<a href="#interchange">Interchange</a> ·
|
||||
<a href="#built-for-agents">Agents</a> ·
|
||||
<a href="#get-started">Get started</a>
|
||||
<a href="#get-started">Get started</a> ·
|
||||
<a href="#documentation">Docs</a> ·
|
||||
<a href="#status">Status</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-hero.png" alt="FilmCraft in the Color workspace, mid-way through an Apollo 11 documentary cut from NASA footage: the Program monitor on the Saturn V clearing the launch tower with a Launch Complex 39A lower third and an air-to-ground subtitle, Effect Controls with Lumetri and Scale keyframes on the shot, the Lumetri Color panel, bins of NASA selects, and a timeline with 49 picture cuts, B-roll, titles, a caption track, mission audio, a ducked music bed, named markers, a rendered section and live loudness meters" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub><i>Apollo 11 — Tranquility</i>: a three-minute documentary edit of NASA's 1969 launch, landing and moonwalk film, with subtitles from the mission transcript. Every frame in these screenshots comes from public-domain footage, decoded, composited and graded by FilmCraft's own code.</sub></p>
|
||||
|
||||
<br>
|
||||
|
||||
FilmCraft is a non-linear editor for people who know Premiere: the same panels, workspaces, tools and shortcuts, so your hands already know where everything is. Underneath, it is new from the bitstream up. The H.264, HEVC, ProRes and AAC codecs are our own, written in Rust from the public specifications. A GPU compositor works in linear light. Frame math runs on exact integer time, so edits never drift. And every action in the app is a command that an AI agent can drive as precisely as you can.
|
||||
@@ -33,9 +72,11 @@ FilmCraft is a non-linear editor for people who know Premiere: the same panels,
|
||||
## Edit
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-assembly.png" alt="Assembly workspace: Carnival of Souls in the Source monitor with In and Out marks, Night of the Living Dead in the Program monitor, the trailer in the timeline" width="100%">
|
||||
<img src="docs/images/filmcraft-assembly.png" alt="Assembly workspace: bins of film thumbnails, Carnival of Souls in the Source monitor, a Night of the Living Dead cemetery shot in the Program monitor, and the trailer on a three-track timeline with a Chopin score on A2 and loudness meters" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub>The Assembly workspace, cutting a trailer for <i>Night of the Living Dead</i> (1968) with an insert from <i>Carnival of Souls</i> (1962).</sub></p>
|
||||
|
||||
**A timeline you already know.** Source and Program monitors, bins with thumbnails, a multi-track timeline with patch and target buttons, sync locks and linked selection. The Editing, Assembly, Color, Effects and Audio workspaces are all there, and every panel docks wherever you want it.
|
||||
|
||||
- **Three-point editing.** Mark In and Out in the Source monitor, then insert (`,`) or overwrite (`.`) onto the patched tracks. Lift (`;`) and extract (`'`) take ranges back out.
|
||||
@@ -46,17 +87,21 @@ FilmCraft is a non-linear editor for people who know Premiere: the same panels,
|
||||
- **Never lose work.** Saves are atomic, auto-save keeps a rolling set of versions, and a crash-recovery journal written about a second after each edit brings back unsaved changes after a crash or power cut.
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-timeline.png" alt="Timeline detail: Night of the Living Dead shots with dissolves, a Carnival of Souls insert on V2, the Chopin score as a waveform on A2, markers and loudness meters" width="100%">
|
||||
<img src="docs/images/filmcraft-timeline.png" alt="Timeline close-up: Night of the Living Dead shots with dissolves on V1, a Carnival of Souls insert on V2, the Chopin nocturne as a green waveform on A2, coloured markers along the ruler, and the loudness meter reading momentary, short-term, integrated and true-peak values" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub>Up close: dissolves, an insert on V2, the score on A2, coloured markers, and the loudness meters on the right.</sub></p>
|
||||
|
||||
<br>
|
||||
|
||||
## Color
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-color.png" alt="Color workspace: a Lumetri grade on Charade (1963) with curves, colour sliders, waveform and vectorscope" width="100%">
|
||||
<img src="docs/images/filmcraft-color.png" alt="Color workspace on Charade (1963): Cary Grant and Audrey Hepburn in the Program monitor, the waveform and vectorscope on the left, and the trailer bins and timeline below" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub>The Color workspace on <i>Charade</i> (1963), with live scopes beside the Program monitor.</sub></p>
|
||||
|
||||
**A complete Lumetri-style grading panel**, in the order a colourist works:
|
||||
|
||||
- **Basic Correction:** temperature and tint, exposure, contrast, highlights, shadows, whites, blacks, saturation.
|
||||
@@ -68,8 +113,14 @@ FilmCraft is a non-linear editor for people who know Premiere: the same panels,
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="34%" valign="top"><img src="docs/images/filmcraft-lumetri.png" alt="The Lumetri Color panel: basic correction sliders with temperature and tint gradients, and three colour wheels"></td>
|
||||
<td width="66%" valign="top"><img src="docs/images/filmcraft-scopes.png" alt="Lumetri Scopes: waveform and vectorscope of a Charade frame"><br><br><sub><b>Scopes that tell the truth.</b> The waveform and vectorscope are computed from the graded frame. All grading happens in linear light, in 32-bit float.</sub></td>
|
||||
<td width="34%" valign="top" align="center">
|
||||
<img src="docs/images/filmcraft-lumetri.png" alt="The Lumetri Color panel on Charade (1963): Basic Correction sliders with coloured temperature and tint tracks, collapsed Creative and Curves sections, shadow, midtone and highlight colour wheels, and HSL Secondary and Vignette toggles"><br>
|
||||
<sub><b>The Lumetri Color panel.</b> Basic Correction, Color Wheels and every other section, each with its own on/off switch.</sub>
|
||||
</td>
|
||||
<td width="66%" valign="top" align="center">
|
||||
<img src="docs/images/filmcraft-scopes.png" alt="Lumetri Scopes: a green luma waveform and a vectorscope of a Charade (1963) frame"><br>
|
||||
<sub><b>Scopes that tell the truth.</b> The waveform and vectorscope are computed from the graded frame. All grading happens in linear light, in 32-bit float.</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -78,16 +129,18 @@ FilmCraft is a non-linear editor for people who know Premiere: the same panels,
|
||||
## Effects and motion
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-effects.png" alt="Effects workspace: Effect Controls, NASA Earth Views from the ISS with a Golden Hour look, and the Effects browser" width="100%">
|
||||
<img src="docs/images/filmcraft-effects.png" alt="Effects workspace: Effect Controls with Motion, Opacity, Time Remapping and Lumetri Color, NASA's Earth Views from the ISS in the Program monitor, and the Effects browser open on the Dissolve transitions" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub>NASA's <i>Earth Views from the ISS</i> with a look applied, and the Effects browser open on the dissolves.</sub></p>
|
||||
|
||||
- **Around 55 video effects and 30 transitions.** Blurs, keys, distortions, stylize and colour effects. Cross dissolve, dip to black or white, film dissolve, wipes, irises, pushes, slides, zooms, page peel, cube spin and more.
|
||||
- **Motion and opacity on every clip:** position, scale, rotation, anchor point and anti-flicker, plus 26 blend modes.
|
||||
- **Keyframes like Premiere's:** linear, Bezier, auto and continuous Bezier, hold, ease in and ease out. Effect Controls shows a keyframe lane for every parameter, and each animated parameter opens into **value and velocity graphs** with draggable influence handles.
|
||||
- **A GPU compositor** built on wgpu (Metal, Vulkan, DirectX 12, WebGPU). It samples YUV straight from the decoder with footprint supersampling and blends in linear light. A CPU path renders the same frames, and the two are tested against each other.
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-keyframes.png" alt="Effect Controls with the value and velocity graphs of an eased Scale push-in on the Night of the Living Dead title" width="100%">
|
||||
<img src="docs/images/filmcraft-keyframes.png" alt="Effect Controls with the value and velocity graphs of an eased Scale push-in on the Night of the Living Dead title card, the Program monitor showing the title over a country road, and the Properties panel with Transform and Crop" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub>An eased push-in on the title card: the value graph, the velocity graph and the Bezier influence handle.</sub></p>
|
||||
@@ -104,6 +157,13 @@ FilmCraft is a non-linear editor for people who know Premiere: the same panels,
|
||||
|
||||
<br>
|
||||
|
||||
## Titles and captions
|
||||
|
||||
- **A real text engine:** OpenType shaping with kerning and ligatures, bidirectional text, line breaking, tracking and leading, drawn in linear light and sharp at any scale or rotation.
|
||||
- **Type, Shape and Pen tools** on the Program monitor: click to type, edit with a caret and selection, drag out rectangles, ellipses and paths. Graphic clips hold text and shape layers with fill, strokes, background and shadow, all keyframable, and edited in the Properties panel.
|
||||
- **Captions:** caption tracks in Subtitle, CEA-608, CEA-708 and Teletext formats; import and export SRT, WebVTT and SCC with frame-exact timing; edit captions in the Text panel and burn them into exports.
|
||||
<br>
|
||||
|
||||
## Formats and codecs
|
||||
|
||||
No FFmpeg inside. The video codecs, AAC, Opus and the containers are our own Rust code, implemented from the public ITU-T, ISO and IETF specifications and tested frame by frame against ffmpeg as an external oracle.
|
||||
@@ -128,20 +188,14 @@ MXF and hardware decode are next ([roadmap](ROADMAP.md)).
|
||||
|
||||
<br>
|
||||
|
||||
## Titles and captions
|
||||
|
||||
- **A real text engine:** OpenType shaping with kerning and ligatures, bidirectional text, line breaking, tracking and leading, drawn in linear light and sharp at any scale or rotation.
|
||||
- **Type, Shape and Pen tools** on the Program monitor: click to type, edit with a caret and selection, drag out rectangles, ellipses and paths. Graphic clips hold text and shape layers with fill, strokes, background and shadow, all keyframable, and edited in the Properties panel.
|
||||
- **Captions:** caption tracks in Subtitle, CEA-608, CEA-708 and Teletext formats; import and export SRT, WebVTT and SCC with frame-exact timing; edit captions in the Text panel and burn them into exports.
|
||||
|
||||
<br>
|
||||
|
||||
## Export
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/images/filmcraft-export.png" alt="Export mode with H.264 MP4 settings" width="100%">
|
||||
<img src="docs/images/filmcraft-export.png" alt="Export mode: destinations for Media File, YouTube, Vimeo, TikTok, Instagram and FTP on the left, a Night of the Living Dead frame in the preview, and H.264 settings on the right (1440x1080 at 23.976 fps, 48000 Hz stereo, entire sequence)" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><sub>Export mode, set to write the trailer as H.264 MP4.</sub></p>
|
||||
|
||||
- **H.264 MP4 with AAC, using our own encoders.** A 6-second 960×540 render takes 1.3 seconds and decodes cleanly in ffmpeg with error concealment switched off.
|
||||
- **Also:** Apple ProRes 422 HQ and Motion JPEG in QuickTime, PNG sequences, animated GIF and WAV.
|
||||
- **Background jobs** with progress and cancel, so you keep editing while it renders.
|
||||
@@ -196,7 +250,7 @@ cargo run --release -p filmcraft-cli -- commands # list every engine c
|
||||
cargo run --release -p filmcraft-cli -- mcp # MCP server (headless)
|
||||
```
|
||||
|
||||
The control protocol is documented in [docs/control-protocol.md](docs/control-protocol.md).
|
||||
The control protocol is documented in [docs/control-protocol.md](docs/control-protocol.md). Stuck, or want to show what you made? Ask in [Discord](https://discord.gg/artcraft).
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -232,39 +286,61 @@ A layered Cargo workspace:
|
||||
|
||||
Nothing below the front ends depends on a UI toolkit or OS API. `cargo xtask ci` checks formatting, lints, tests, the layering rules, asset attribution and the wasm build.
|
||||
|
||||
## Crafting Apps
|
||||
## The Crafting Apps
|
||||
|
||||
Open-source, clean-room, pure-Rust creative tools. Each runs natively on macOS, Windows and Linux, and on the web.
|
||||
FilmCraft 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
|
||||
stand on its own.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="20%" align="center"><a href="https://github.com/storytold/photocraft"><b>PhotoCraft</b></a></td>
|
||||
<td>Image editing and compositing, Photoshop-class: layers, masks, adjustments and byte-exact PSD/PSB round trips.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/storytold/drawcraft"><b>DrawCraft</b></a></td>
|
||||
<td>Vector illustration, Illustrator-class: paths, shapes, type and artboards.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/storytold/filmcraft"><b>FilmCraft</b></a></td>
|
||||
<td>Non-linear video editing, Premiere Pro-class: timeline editing, own codecs, GPU compositing, colour and export. <i>You are here.</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/storytold/lightcraft"><b>LightCraft</b></a></td>
|
||||
<td>Photo library and non-destructive raw developer, Lightroom-class: catalog, culling, develop and export.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/storytold/printcraft"><b>PrintCraft</b></a></td>
|
||||
<td>PDF viewing and editing, Acrobat-class: pages, annotations, forms and document tools.</td>
|
||||
</tr>
|
||||
</table>
|
||||
| App | What it's for | Code | Learn more |
|
||||
|---|---|---|---|
|
||||
| <img src="https://img.shields.io/badge/PhotoCraft-2f7bf5?style=for-the-badge" alt="PhotoCraft" height="24"> | Image editing: layers, masks, type and real PSD files | [GitHub](https://github.com/storytold/photocraft) | [getartcraft.com](https://getartcraft.com/apps/photocraft) |
|
||||
| <img src="https://img.shields.io/badge/VectorCraft-e8573f?style=for-the-badge" alt="VectorCraft" height="24"> | Vector illustration (formerly DrawCraft) | [GitHub](https://github.com/storytold/vectorcraft) | [getartcraft.com](https://getartcraft.com/apps/drawcraft) |
|
||||
| <img src="https://img.shields.io/badge/FilmCraft-8b5cf6?style=for-the-badge" alt="FilmCraft" height="24"> | **Video editing, color and sound · you are here** | [GitHub](https://github.com/storytold/filmcraft) | [getartcraft.com](https://getartcraft.com/apps/filmcraft) |
|
||||
| <img src="https://img.shields.io/badge/LightCraft-f2a516?style=for-the-badge" alt="LightCraft" height="24"> | Photo library and raw development | [GitHub](https://github.com/storytold/lightcraft) | [getartcraft.com](https://getartcraft.com/apps/lightcraft) |
|
||||
| <img src="https://img.shields.io/badge/PrintCraft-12a58a?style=for-the-badge" alt="PrintCraft" height="24"> | Reading, organizing and protecting PDFs | [GitHub](https://github.com/storytold/printcraft) | [getartcraft.com](https://getartcraft.com/apps/printcraft) |
|
||||
| <img src="https://img.shields.io/badge/EffectCraft-e0368f?style=for-the-badge" alt="EffectCraft" height="24"> | Motion graphics and visual effects | [GitHub](https://github.com/storytold/effectcraft) | [getartcraft.com](https://getartcraft.com/apps/effectcraft) |
|
||||
| <img src="https://img.shields.io/badge/DesignCraft-7bb51c?style=for-the-badge" alt="DesignCraft" height="24"> | Page layout and publishing | [GitHub](https://github.com/storytold/designcraft) | [getartcraft.com](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/filmcraft">FilmCraft</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
## Credits and clean room
|
||||
|
||||
**Footage and music in the screenshots:** NASA's Apollo 11 film and television footage from [images.nasa.gov](https://images.nasa.gov) (launch, Launch Control Center, lunar surface and recovery) and the Apollo 11 air-to-ground voice transcript, all US Government works in the public domain (NASA does not endorse this project); *Night of the Living Dead* (1968), *Carnival of Souls* (1962) and *Charade* (1963), all in the US public domain; *Earth Views from the ISS* by NASA; Chopin's Nocturne Op. 48 No. 1 and Ballade No. 1, performed for Musopen and released under CC0. The media itself is not in this repository. Sources and details for every asset are in [ATTRIBUTION.md](ATTRIBUTION.md).
|
||||
|
||||
FilmCraft is an independent implementation. It contains no Adobe code, icons, images, presets or LUTs, and no GPL or LGPL code; every icon is drawn in code and every asset is openly licensed and attributed ([AGENTS.md](AGENTS.md)). ffmpeg is used only as an external test oracle. Adobe and Premiere Pro are trademarks of Adobe Inc.; FilmCraft is not affiliated with Adobe.
|
||||
FilmCraft is an independent implementation. It contains no Adobe code, icons, images, presets or LUTs, and no GPL or LGPL code; every icon is drawn in code and every asset is openly licensed and attributed ([AGENTS.md](AGENTS.md)), apart from the ArtCraft name and logo, which are trademarks of the ArtCraft team used with permission. ffmpeg is used only as an external test oracle. Adobe and Premiere Pro are trademarks of Adobe Inc.; FilmCraft is not affiliated with Adobe.
|
||||
|
||||
## License
|
||||
|
||||
FilmCraft is dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option. Bundled assets keep their own open licences, listed in [ATTRIBUTION.md](ATTRIBUTION.md).
|
||||
FilmCraft is dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option. Bundled assets keep their own open licences, listed in [ATTRIBUTION.md](ATTRIBUTION.md). The ArtCraft name and logos in `docs/brand/` are trademarks of the ArtCraft team and are not covered by this licence.
|
||||
|
||||
<br>
|
||||
|
||||
<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>
|
||||
@@ -29,6 +29,7 @@ pub fn install(app: &FilmcraftApp, ctx: egui::Context) -> (Receiver<String>, Sho
|
||||
let app_menu = Submenu::new("FilmCraft", true);
|
||||
let _ = app_menu.append_items(&[
|
||||
&MenuItem::with_id("app.about", "About FilmCraft", true, None),
|
||||
&MenuItem::with_id("help.discord", "Join the ArtCraft Discord…", true, None),
|
||||
&PredefinedMenuItem::separator(),
|
||||
&MenuItem::with_id("app.preferences.autoSave", "Settings…", true, None),
|
||||
&PredefinedMenuItem::separator(),
|
||||
|
||||
@@ -29,6 +29,8 @@ serde_json = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
web-time = "1"
|
||||
# ArtCraft wordmark (PNG) for the About dialog and Home screen.
|
||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||
|
||||
# Thread/process CPU clocks for frame-job profiling (safe wrappers; see frames.rs).
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
@@ -38,4 +40,3 @@ rustix = { version = "1", default-features = false, features = ["std", "time"] }
|
||||
pollster = "0.4"
|
||||
filmcraft-codecs = { workspace = true }
|
||||
egui_kittest = { workspace = true, features = ["eframe", "wgpu"] }
|
||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||
@@ -0,0 +1,33 @@
|
||||
//! The ArtCraft wordmark (first-party trademark, used with permission; see `docs/brand/` and
|
||||
//! AGENTS.md §1.8), decoded once and kept as a texture.
|
||||
|
||||
const LOGO_LIGHT_INK: &[u8] = include_bytes!("../../../docs/brand/artcraft-logo-white.png");
|
||||
const LOGO_DARK_INK: &[u8] = include_bytes!("../../../docs/brand/artcraft-logo.png");
|
||||
|
||||
/// The wordmark for a dark (`dark = true`) or light UI: light ink on dark themes. Its aspect
|
||||
/// ratio is width / height of the returned texture.
|
||||
pub fn wordmark(ctx: &egui::Context, dark: bool) -> Option<egui::TextureHandle> {
|
||||
let id = egui::Id::new(("artcraft-wordmark", dark));
|
||||
if let Some(t) = ctx.data(|d| d.get_temp::<egui::TextureHandle>(id)) {
|
||||
return Some(t);
|
||||
}
|
||||
let bytes = if dark { LOGO_LIGHT_INK } else { LOGO_DARK_INK };
|
||||
let img = image::load_from_memory_with_format(bytes, image::ImageFormat::Png).ok()?;
|
||||
// Large enough to stay sharp at 2x for the sizes we draw (≤ 320 pt wide).
|
||||
let img = img.resize(1024, 1024, image::imageops::FilterType::Lanczos3).to_rgba8();
|
||||
let color = egui::ColorImage::from_rgba_unmultiplied([img.width() as usize, img.height() as usize], img.as_raw());
|
||||
let tex = ctx.load_texture("artcraft-wordmark", color, egui::TextureOptions::LINEAR);
|
||||
ctx.data_mut(|d| d.insert_temp(id, tex.clone()));
|
||||
Some(tex)
|
||||
}
|
||||
|
||||
/// Draw the wordmark `height` points tall with its left edge at `left_center` (vertically centred).
|
||||
/// Returns the drawn rect.
|
||||
pub fn paint_wordmark(ui: &egui::Ui, left_center: egui::Pos2, height: f32, dark: bool) -> Option<egui::Rect> {
|
||||
let tex = wordmark(ui.ctx(), dark)?;
|
||||
let [w, h] = tex.size();
|
||||
let width = height * w as f32 / h.max(1) as f32;
|
||||
let r = egui::Rect::from_min_size(egui::pos2(left_center.x, left_center.y - height / 2.0), egui::vec2(width, height));
|
||||
ui.painter().image(tex.id(), r, egui::Rect::from_min_max(egui::pos2(0.0, 0.0), egui::pos2(1.0, 1.0)), egui::Color32::WHITE);
|
||||
Some(r)
|
||||
}
|
||||
@@ -103,6 +103,21 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
let wresp = ui.interact(wr, egui::Id::new("hdr-ws-name"), Sense::click());
|
||||
app.auto.add("header.workspaceName", wr, &ws);
|
||||
p.galley_with_override_text_color(pos2(wr.min.x + 4.0, rect.center().y - wg.size().y / 2.0), wg, if wresp.hovered() { t.text } else { t.text_dim });
|
||||
// Community: a labelled Discord button, always one click away.
|
||||
{
|
||||
let label = "Discord";
|
||||
let g = p.layout_no_wrap(label.to_string(), Tokens::ui(12.0), Color32::WHITE);
|
||||
let w = g.size().x + 34.0;
|
||||
let r = Rect::from_min_size(pos2(wr.min.x - w - 14.0, rect.center().y - 12.0), vec2(w, 24.0));
|
||||
let resp = ui.interact(r, egui::Id::new("hdr-discord"), Sense::click()).on_hover_text("Join the ArtCraft Discord (discord.gg/artcraft)");
|
||||
app.auto.add("header.discord", r, "Join the ArtCraft Discord");
|
||||
ui.painter().rect_filled(r, 12.0, if resp.hovered() { t.accent_hover } else { t.accent });
|
||||
icons::paint(ui.painter(), Rect::from_center_size(pos2(r.min.x + 14.0, r.center().y), vec2(14.0, 14.0)), Icon::Chat, Color32::WHITE);
|
||||
ui.painter().galley(pos2(r.min.x + 25.0, r.center().y - g.size().y / 2.0), g, Color32::WHITE);
|
||||
if resp.clicked() {
|
||||
crate::links::open(ui.ctx(), crate::links::DISCORD);
|
||||
}
|
||||
}
|
||||
let popup_id = egui::Id::new("workspaces-popup");
|
||||
if ws_resp.clicked() || wresp.clicked() {
|
||||
ui.ctx().data_mut(|d| d.insert_temp(popup_id, true));
|
||||
|
||||
@@ -80,6 +80,13 @@ pub enum Icon {
|
||||
Undo,
|
||||
Redo,
|
||||
Bell,
|
||||
/// Community chat (the ArtCraft Discord): a speech bubble with three dots. Our own drawing,
|
||||
/// not the Discord logo.
|
||||
Chat,
|
||||
/// Website: a globe.
|
||||
Globe,
|
||||
/// Source code (GitHub repository): angle brackets and a slash.
|
||||
Code,
|
||||
Sparkle,
|
||||
Grid,
|
||||
Square,
|
||||
@@ -488,6 +495,38 @@ pub fn paint(painter: &Painter, rect: Rect, icon: Icon, color: Color32) {
|
||||
pen.arc(8.0, 7.0, 3.5, 180.0, 360.0);
|
||||
pen.dot(8.0, 13.8, 1.0);
|
||||
}
|
||||
Chat => {
|
||||
pen.line(&[
|
||||
(3.5, 3.0),
|
||||
(12.5, 3.0),
|
||||
(14.0, 4.5),
|
||||
(14.0, 9.5),
|
||||
(12.5, 11.0),
|
||||
(7.0, 11.0),
|
||||
(4.0, 14.0),
|
||||
(4.5, 11.0),
|
||||
(3.5, 11.0),
|
||||
(2.0, 9.5),
|
||||
(2.0, 4.5),
|
||||
(3.5, 3.0),
|
||||
]);
|
||||
pen.dot(5.5, 7.0, 0.9);
|
||||
pen.dot(8.0, 7.0, 0.9);
|
||||
pen.dot(10.5, 7.0, 0.9);
|
||||
}
|
||||
Globe => {
|
||||
pen.circle(8.0, 8.0, 6.0);
|
||||
pen.line(&[(2.0, 8.0), (14.0, 8.0)]);
|
||||
pen.line(&[(3.0, 5.0), (13.0, 5.0)]);
|
||||
pen.line(&[(3.0, 11.0), (13.0, 11.0)]);
|
||||
pen.line(&[(8.0, 2.0), (6.0, 5.0), (5.5, 8.0), (6.0, 11.0), (8.0, 14.0)]);
|
||||
pen.line(&[(8.0, 2.0), (10.0, 5.0), (10.5, 8.0), (10.0, 11.0), (8.0, 14.0)]);
|
||||
}
|
||||
Code => {
|
||||
pen.line(&[(5.0, 4.0), (1.5, 8.0), (5.0, 12.0)]);
|
||||
pen.line(&[(11.0, 4.0), (14.5, 8.0), (11.0, 12.0)]);
|
||||
pen.line(&[(9.5, 2.5), (6.5, 13.5)]);
|
||||
}
|
||||
Sparkle => {
|
||||
pen.fill(&[(8.0, 1.5), (9.5, 6.5), (14.5, 8.0), (9.5, 9.5), (8.0, 14.5), (6.5, 9.5), (1.5, 8.0), (6.5, 6.5)]);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
//! control-channel handlers. Swap it for another toolkit without touching the engine.
|
||||
|
||||
pub mod automation;
|
||||
pub mod brand;
|
||||
pub mod control;
|
||||
pub mod dock;
|
||||
pub mod frames;
|
||||
pub mod header;
|
||||
pub mod icons;
|
||||
pub mod links;
|
||||
pub mod menus;
|
||||
pub mod panels;
|
||||
pub mod state;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//! Community and project links (Help menu, About dialog, header Discord button, Home screen).
|
||||
//!
|
||||
//! FilmCraft is part of the ArtCraft family: the community lives on the ArtCraft Discord, every
|
||||
//! app has a page on the ArtCraft website and a public GitHub repository.
|
||||
|
||||
/// The app's short name, used in the website and repository URLs.
|
||||
pub const APP: &str = "filmcraft";
|
||||
|
||||
/// The ArtCraft community Discord.
|
||||
pub const DISCORD: &str = "https://discord.gg/artcraft";
|
||||
/// The ArtCraft website.
|
||||
pub const WEBSITE: &str = "https://getartcraft.com";
|
||||
/// FilmCraft's page on the ArtCraft website.
|
||||
pub const APP_PAGE: &str = "https://getartcraft.com/apps/filmcraft";
|
||||
/// FilmCraft's source repository.
|
||||
pub const GITHUB: &str = "https://github.com/storytold/filmcraft";
|
||||
/// New issue on FilmCraft's repository.
|
||||
pub const ISSUES: &str = "https://github.com/storytold/filmcraft/issues";
|
||||
|
||||
/// (command id, label, url) for every link, in menu order.
|
||||
pub const ALL: [(&str, &str, &str); 5] = [
|
||||
("help.discord", "Join the ArtCraft Discord…", DISCORD),
|
||||
("help.website", "ArtCraft Website", WEBSITE),
|
||||
("help.appPage", "FilmCraft on getartcraft.com", APP_PAGE),
|
||||
("help.github", "FilmCraft on GitHub", GITHUB),
|
||||
("help.reportIssue", "Report an Issue…", ISSUES),
|
||||
];
|
||||
|
||||
/// The URL a `help.*` link command opens.
|
||||
pub fn url_for(command: &str) -> Option<&'static str> {
|
||||
ALL.iter().find(|(id, _, _)| *id == command).map(|(_, _, u)| *u)
|
||||
}
|
||||
|
||||
/// Open `url` in the system browser (a new tab on the web).
|
||||
pub fn open(ctx: &egui::Context, url: &str) {
|
||||
ctx.open_url(egui::OpenUrl::new_tab(url));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn urls_follow_the_artcraft_scheme() {
|
||||
assert_eq!(APP_PAGE, format!("{WEBSITE}/apps/{APP}"));
|
||||
assert_eq!(GITHUB, format!("https://github.com/storytold/{APP}"));
|
||||
assert!(ALL.iter().all(|(id, _, u)| id.starts_with("help.") && u.starts_with("https://")));
|
||||
assert_eq!(url_for("help.discord"), Some(DISCORD));
|
||||
assert_eq!(url_for("help.nope"), None);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,11 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
||||
uic!("mode.import", "Import", [], None),
|
||||
uic!("mode.edit", "Edit", [], None),
|
||||
uic!("mode.export", "Export", ["File", "Export"], Some("Cmd+M")),
|
||||
uic!("help.discord", "Join the ArtCraft Discord…", ["Help"], None),
|
||||
uic!("help.website", "ArtCraft Website", ["Help"], None),
|
||||
uic!("help.appPage", "FilmCraft on getartcraft.com", ["Help"], None),
|
||||
uic!("help.github", "FilmCraft on GitHub", ["Help"], None),
|
||||
uic!("help.reportIssue", "Report an Issue…", ["Help"], None),
|
||||
uic!("app.about", "About FilmCraft", ["Help"], None),
|
||||
uic!("app.keyboardShortcuts", "Keyboard Shortcuts…", ["Edit"], Some("Cmd+Alt+K")),
|
||||
uic!("app.preferences.autoSave", "Auto Save…", ["Edit", "Preferences"], Some("Cmd+,")),
|
||||
@@ -178,6 +183,12 @@ pub fn invoke(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, params: Val
|
||||
app.dialog = Some(crate::Dialog::About);
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
id if crate::links::url_for(id).is_some() => {
|
||||
let url = crate::links::url_for(id).unwrap_or_default();
|
||||
crate::links::open(ctx, url);
|
||||
app.ui.status = format!("Opened {url}");
|
||||
return Ok(json!({"url": url}));
|
||||
}
|
||||
"help.shortcuts" | "app.keyboardShortcuts" => {
|
||||
crate::panels::shortcuts_dialog::open(app);
|
||||
return Ok(Value::Null);
|
||||
|
||||
@@ -18,12 +18,15 @@ pub fn show(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let mut open = true;
|
||||
match d {
|
||||
Dialog::About => {
|
||||
egui::Window::new("About FilmCraft").open(&mut open).collapsible(false).resizable(false).show(ctx, |ui| {
|
||||
ui.heading("FilmCraft");
|
||||
ui.label(format!("Version {}", env!("CARGO_PKG_VERSION")));
|
||||
ui.label("A clean-room, pure-Rust non-linear video editor.");
|
||||
ui.label("Fonts: Inter and JetBrains Mono (SIL OFL 1.1).");
|
||||
});
|
||||
egui::Window::new("About FilmCraft")
|
||||
.open(&mut open)
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0))
|
||||
.show(ctx, |ui| about(app, ui));
|
||||
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
Dialog::Shortcuts => {
|
||||
if !crate::panels::shortcuts_dialog::show(app, ctx) && app.dialog == Some(d) {
|
||||
@@ -124,3 +127,58 @@ fn audio_gain(app: &mut FilmcraftApp, ctx: &egui::Context) -> bool {
|
||||
app.ui.audio_gain = draft;
|
||||
keep
|
||||
}
|
||||
|
||||
/// Help ▸ About FilmCraft: version, credits and the ArtCraft community links. Joining the Discord
|
||||
/// is the first, accented button.
|
||||
///
|
||||
/// Automation ids: `about.discord`, `about.website`, `about.appPage`, `about.github`,
|
||||
/// `about.reportIssue`.
|
||||
fn about(app: &mut FilmcraftApp, ui: &mut egui::Ui) {
|
||||
use crate::icons::{self, Icon};
|
||||
use crate::links;
|
||||
let t = app.tokens;
|
||||
ui.set_width(380.0);
|
||||
// ArtCraft wordmark (first-party trademark, docs/brand/).
|
||||
let (r, _) = ui.allocate_exact_size(egui::vec2(ui.available_width(), 30.0), egui::Sense::hover());
|
||||
crate::brand::paint_wordmark(ui, egui::pos2(r.min.x, r.center().y), 20.0, app.ui.dark);
|
||||
ui.add_space(6.0);
|
||||
ui.heading("FilmCraft");
|
||||
ui.label(format!("Version {}", env!("CARGO_PKG_VERSION")));
|
||||
ui.label("A clean-room, pure-Rust non-linear video editor. Part of the ArtCraft family.");
|
||||
ui.add_space(10.0);
|
||||
let mut link = |ui: &mut egui::Ui, id: &str, icon: Icon, label: &str, url: &str, primary: bool| {
|
||||
let size = egui::vec2(ui.available_width(), if primary { 36.0 } else { 28.0 });
|
||||
let (r, resp) = ui.allocate_exact_size(size, egui::Sense::click());
|
||||
let resp = resp.on_hover_text(url);
|
||||
app.auto.add(&format!("about.{id}"), r, label);
|
||||
let bg = if primary {
|
||||
if resp.hovered() { t.accent_hover } else { t.accent }
|
||||
} else if resp.hovered() {
|
||||
t.hover
|
||||
} else {
|
||||
egui::Color32::TRANSPARENT
|
||||
};
|
||||
ui.painter().rect_filled(r, 6.0, bg);
|
||||
let fg = if primary { egui::Color32::WHITE } else { t.text };
|
||||
let ir = egui::Rect::from_center_size(egui::pos2(r.min.x + 20.0, r.center().y), egui::vec2(18.0, 18.0));
|
||||
icons::paint(ui.painter(), ir, icon, fg);
|
||||
ui.painter().text(
|
||||
egui::pos2(r.min.x + 40.0, r.center().y),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
label,
|
||||
crate::theme::Tokens::ui(if primary { 14.0 } else { 13.0 }),
|
||||
fg,
|
||||
);
|
||||
if resp.clicked() {
|
||||
links::open(ui.ctx(), url);
|
||||
}
|
||||
};
|
||||
link(ui, "discord", Icon::Chat, "Join the ArtCraft Discord", links::DISCORD, true);
|
||||
ui.add_space(6.0);
|
||||
link(ui, "website", Icon::Globe, "getartcraft.com", links::WEBSITE, false);
|
||||
link(ui, "appPage", Icon::Globe, "FilmCraft on getartcraft.com", links::APP_PAGE, false);
|
||||
link(ui, "github", Icon::Code, "Source code on GitHub", links::GITHUB, false);
|
||||
link(ui, "reportIssue", Icon::Code, "Report an issue", links::ISSUES, false);
|
||||
ui.add_space(10.0);
|
||||
ui.label(egui::RichText::new("MIT OR Apache-2.0. Fonts: Inter, Noto Serif and JetBrains Mono (SIL OFL 1.1).").small().color(t.text_dim));
|
||||
}
|
||||
@@ -43,5 +43,32 @@ pub fn show(app: &mut FilmcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
app.ui.mode = crate::state::Mode::Edit;
|
||||
}
|
||||
}
|
||||
let _ = pos2(0.0, 0.0);
|
||||
// Community and project links.
|
||||
let y = 90.0 + 150.0 + 36.0;
|
||||
// ArtCraft wordmark (first-party trademark, docs/brand/), then the section title.
|
||||
let logo = crate::brand::paint_wordmark(ui, rect.min + vec2(24.0, y), 15.0, app.ui.dark);
|
||||
let tx = logo.map_or(rect.min.x + 24.0, |r| r.max.x + 12.0);
|
||||
ui.painter().text(pos2(tx, rect.min.y + y), Align2::LEFT_CENTER, "Community", Tokens::semibold(15.0), t.text);
|
||||
let mut x = rect.min.x + 24.0;
|
||||
for (i, (id, label, url)) in crate::links::ALL.iter().take(4).enumerate() {
|
||||
let primary = i == 0;
|
||||
let label = if primary { "Join us on Discord" } else { *label };
|
||||
let g = ui.painter().layout_no_wrap(label.to_string(), Tokens::ui(13.0), t.text);
|
||||
let r = Rect::from_min_size(pos2(x, rect.min.y + y + 18.0), vec2(g.size().x + 44.0, 34.0));
|
||||
let resp = ui.interact(r, egui::Id::new(("imp-link", *id)), Sense::click()).on_hover_text(*url);
|
||||
app.auto.add(&format!("import.link.{}", id.trim_start_matches("help.")), r, label);
|
||||
let (bg, fg) = if primary {
|
||||
(if resp.hovered() { t.accent_hover } else { t.accent }, Color32::WHITE)
|
||||
} else {
|
||||
(if resp.hovered() { t.hover } else { t.tl_header_bg }, t.text)
|
||||
};
|
||||
ui.painter().rect_filled(r, 8.0, bg);
|
||||
let icon = [crate::icons::Icon::Chat, crate::icons::Icon::Globe, crate::icons::Icon::Globe, crate::icons::Icon::Code][i];
|
||||
crate::icons::paint(ui.painter(), Rect::from_center_size(pos2(r.min.x + 20.0, r.center().y), vec2(16.0, 16.0)), icon, fg);
|
||||
ui.painter().galley(pos2(r.min.x + 34.0, r.center().y - g.size().y / 2.0), g, fg);
|
||||
if resp.clicked() {
|
||||
crate::links::open(&ctx, url);
|
||||
}
|
||||
x = r.max.x + 12.0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//! Headless UI test of the community links: Help menu commands, the header Discord button, the
|
||||
//! About dialog and the Home (Import) screen.
|
||||
//!
|
||||
//! Set `FILMCRAFT_UI_SNAPSHOT_DIR=<dir>` to also render the window offscreen with wgpu and write
|
||||
//! `links-*.png` there; without it no GPU is needed.
|
||||
|
||||
use std::sync::mpsc::{Sender, channel};
|
||||
|
||||
use egui_kittest::Harness;
|
||||
use filmcraft_engine::Session;
|
||||
use filmcraft_ui_egui::FilmcraftApp;
|
||||
use filmcraft_ui_egui::control::ControlRequest;
|
||||
use filmcraft_ui_egui::links;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
struct Driver {
|
||||
harness: Harness<'static, FilmcraftApp>,
|
||||
tx: Sender<ControlRequest>,
|
||||
snapshots: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
impl Driver {
|
||||
fn demo() -> Self {
|
||||
let mut session = Session::default();
|
||||
session.execute("file.openDemoProject", json!({})).expect("demo project");
|
||||
let (tx, rx) = channel();
|
||||
let app = FilmcraftApp::new(session).with_control(rx);
|
||||
let snapshots = std::env::var_os("FILMCRAFT_UI_SNAPSHOT_DIR").map(std::path::PathBuf::from);
|
||||
let mut b = Harness::builder().with_size(egui::vec2(1600.0, 980.0)).with_max_steps(10_000);
|
||||
if snapshots.is_some() {
|
||||
b = b.wgpu();
|
||||
}
|
||||
let harness = b.build_eframe(move |_cc| app);
|
||||
let mut d = Driver { harness, tx, snapshots };
|
||||
d.frames(4);
|
||||
d
|
||||
}
|
||||
|
||||
fn frames(&mut self, n: usize) {
|
||||
for _ in 0..n {
|
||||
let ctx = self.harness.ctx.clone();
|
||||
let mut raw = std::mem::take(self.harness.input_mut());
|
||||
eframe::App::raw_input_hook(self.harness.state_mut(), &ctx, &mut raw);
|
||||
*self.harness.input_mut() = raw;
|
||||
self.harness.step();
|
||||
}
|
||||
}
|
||||
|
||||
fn ok(&mut self, method: &str, params: Value) -> Value {
|
||||
let (req, reply) = ControlRequest::new(method, params.clone());
|
||||
self.tx.send(req).unwrap();
|
||||
for _ in 0..600 {
|
||||
self.frames(1);
|
||||
if let Ok(v) = reply.try_recv() {
|
||||
assert_eq!(v["ok"], json!(true), "{method} {params} failed: {v}");
|
||||
return v["result"].clone();
|
||||
}
|
||||
}
|
||||
panic!("no reply to {method} {params}");
|
||||
}
|
||||
|
||||
fn ids(&mut self, prefix: &str) -> Vec<String> {
|
||||
let v = self.ok("ui.elements", json!({"prefix": prefix}));
|
||||
v.as_array().unwrap().iter().filter_map(|e| e["id"].as_str().map(str::to_string)).collect()
|
||||
}
|
||||
|
||||
fn snapshot(&mut self, name: &str) {
|
||||
let Some(dir) = self.snapshots.clone() else { return };
|
||||
self.frames(2);
|
||||
match self.harness.render() {
|
||||
Ok(img) => {
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
img.save(dir.join(format!("links-{name}.png"))).unwrap();
|
||||
}
|
||||
Err(e) => eprintln!("snapshot {name} skipped: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn community_links_everywhere() {
|
||||
let mut d = Driver::demo();
|
||||
// Help menu: every link is a command that reports the URL it opened.
|
||||
for (id, _, url) in links::ALL {
|
||||
let r = d.ok("engine.execute", json!({"command": id, "params": {}}));
|
||||
assert_eq!(r["url"], json!(url), "{id}");
|
||||
}
|
||||
let menu = d.ok("ui.menu.list", json!({}));
|
||||
let text = menu.to_string();
|
||||
for (id, label, _) in links::ALL {
|
||||
assert!(text.contains(id) && text.contains(label), "Help menu lists {id}");
|
||||
}
|
||||
// Header: the Discord button is always there.
|
||||
assert_eq!(d.ids("header.discord"), vec!["header.discord".to_string()]);
|
||||
d.snapshot("header");
|
||||
// About dialog: Discord first, then website, app page, GitHub, issues.
|
||||
d.ok("engine.execute", json!({"command": "app.about", "params": {}}));
|
||||
d.frames(3);
|
||||
let about = d.ids("about.");
|
||||
for id in ["about.discord", "about.website", "about.appPage", "about.github", "about.reportIssue"] {
|
||||
assert!(about.iter().any(|a| a == id), "{id} in {about:?}");
|
||||
}
|
||||
d.snapshot("about");
|
||||
d.ok("ui.key", json!({"key": "Escape"}));
|
||||
d.frames(3);
|
||||
assert!(d.ids("about.").is_empty(), "Escape closes About");
|
||||
// Home screen (Import mode): community links.
|
||||
d.ok("engine.execute", json!({"command": "mode.import", "params": {}}));
|
||||
d.frames(3);
|
||||
let home = d.ids("import.link.");
|
||||
for id in ["import.link.discord", "import.link.website", "import.link.appPage", "import.link.github"] {
|
||||
assert!(home.iter().any(|a| a == id), "{id} in {home:?}");
|
||||
}
|
||||
d.snapshot("home");
|
||||
}
|
||||
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-logo-white.png
|
||||
title: ArtCraft wordmark, light ink for dark backgrounds
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: 3200x333 PNG. Bundled in the app (About dialog, Home screen) and shown in the README header. First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 1038.05 108" role="img" aria-label="ArtCraft">
|
||||
<title>ArtCraft</title>
|
||||
<g fill="#f2f1ee">
|
||||
<path transform="scale(1.02564)" 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"/>
|
||||
<path d="M142.07824124466492 100.0V-0.20598030646411303H224.5093337988741Q235.3915640626314 -0.20598030646411303 242.47102827716765 4.2448305180051875Q249.55049249170392 8.695641342474488 253.08388530613996 15.97633917894725Q256.617278120576 23.25703701542001 256.617278120576 31.917827817510926Q256.617278120576 41.29161779108965 252.08564909759403 49.09045161033163Q247.55402007461208 56.88928542957361 238.35448280295117 61.38287869556304L259.0352045196513 100.0H227.60565795214035L210.562880671027 66.09194563707179H169.9554082596294V100.0ZM169.9554082596294 45.01525167126002H215.67753779224603Q221.38490322307234 45.01525167126002 224.71075426312564 41.60858243269406Q228.03660530317896 38.201913194128096 228.03660530317896 32.65618416032707Q228.03660530317896 28.910440606208994 226.55352218432876 26.28334868406067Q225.07043906547852 23.656256761912346 222.3149998187698 22.30626765215014Q219.55956057206106 20.956278542387935 215.67753779224603 20.956278542387935H169.9554082596294Z M312.0332229234414 100.0V22.638971534484327H268.35528177627936V-0.20598030646411303H383.5883310855678V22.638971534484327H339.91038993840584V100.0Z M458.27161556705016 101.74927113702624Q437.40714777937706 101.74927113702624 423.46369302582383 95.8454810495627Q409.5202382722706 89.94169096209913 402.57861475134996 78.38287353515625Q395.6369912304293 66.82405610821337 395.6369912304293 49.85422740524781Q395.6369912304293 25.012678585664176 411.6814700222251 11.52871357108691Q427.7259488140208 -1.9552514434903543 458.3064661074337 -1.9552514434903543Q475.456917945535 -1.9552514434903543 488.5083118603833 3.126178886035433Q501.55970577523163 8.20760921556122 508.8958646388887 17.516444792900415Q516.2320235025458 26.82528037023961 516.2320235025458 39.59306099672011H489.03300511868554Q489.03300511868554 33.14733922307762 485.1161317984869 28.595099713294914Q481.1992584782883 24.042860203512205 474.2052968660206 21.62493380443695Q467.21133525375296 19.207007405361693 457.80894061146523 19.207007405361693Q447.110456418681 19.207007405361693 439.60629072478065 22.77368929226266Q432.1021250308803 26.340371179163625 428.1899983952093 32.79558632186133Q424.2778717595383 39.25080146455903 424.2778717595383 47.95918367346938V51.60349854227405Q424.2778717595383 60.27703021080084 428.1899983952093 66.80513165087463Q432.1021250308803 73.33323309094843 439.5587615987329 76.9601226895613Q447.0153981665855 80.5870122881742 457.5934669359459 80.5870122881742Q467.61380249762595 80.5870122881742 474.8010658306936 78.272076042331Q481.98832916376114 75.9571397964878 485.8925238982956 71.46510799841701Q489.79671863283005 66.97307620034621 489.79671863283005 60.61291930974399H516.3175883855861Q516.3175883855861 73.0289470350082 509.2048350985849 82.30767875649144Q502.0920818115837 91.58641047797467 489.08347033825555 96.66784080750045Q476.07485886492736 101.74927113702624 458.27161556705016 101.74927113702624Z M537.413518212595 100.0V-0.20598030646411303H619.8446107668042Q630.7268410305614 -0.20598030646411303 637.8063052450977 4.2448305180051875Q644.885769459634 8.695641342474488 648.41916227407 15.97633917894725Q651.9525550885061 23.25703701542001 651.9525550885061 31.917827817510926Q651.9525550885061 41.29161779108965 647.4209260655241 49.09045161033163Q642.8892970425421 56.88928542957361 633.6897597708812 61.38287869556304L654.3704814875814 100.0H622.9409349200704L605.8981576389571 66.09194563707179H565.2906852275595V100.0ZM565.2906852275595 45.01525167126002H611.0128147601761Q616.7201801910024 45.01525167126002 620.0460312310557 41.60858243269406Q623.371882271109 38.201913194128096 623.371882271109 32.65618416032707Q623.371882271109 28.910440606208994 621.8887991522588 26.28334868406067Q620.4057160334086 23.656256761912346 617.6502767866998 22.30626765215014Q614.8948375399912 20.956278542387935 611.0128147601761 20.956278542387935H565.2906852275595Z M662.6163133024502 100.0 712.0998010683712 -0.20598030646411303H743.193458536939L792.6769463028602 100.0H762.4325672314772L754.402333653671 83.1409687898597H699.2874274093653L691.317401543271 100.0ZM708.9368987701752 62.32096947316873H744.7528622928611L735.0177014874674 40.96197456382106Q734.4092539371304 39.53909915668276 733.2953489896883 36.886650063206076Q732.1814440422461 34.2342009697294 731.007331383092 31.285460002220745Q729.833218723938 28.33671903471209 728.8603396867204 25.901367365445054Q727.8874606495028 23.46601569617802 727.500857207903 22.53754284917092H726.3948841615974Q725.2635539439634 25.272371609078903 723.7978960953051 28.821628225788082Q722.3322382466466 32.37088484249726 720.9822491368843 35.68880398141399Q719.6322600271221 39.00672312033072 718.6720595755688 41.04753944686133Z M807.9674540726534 100.0V-0.20598030646411303H905.8314549324839V20.956278542387935H835.8446210876178V42.14414368565506H898.3527086906836V63.30640253450711H835.8446210876178V100.0Z M958.534680649389 100.0V22.638971534484327H914.856739502227V-0.20598030646411303H1030.0897888115155V22.638971534484327H986.41184Line truncated
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-logo-white.svg
|
||||
title: ArtCraft wordmark, light ink for dark backgrounds
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: SVG. Bundled in the app (About dialog, Home screen) and shown in the README header. First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-logo.png
|
||||
title: ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: 3200x333 PNG. Bundled in the app (About dialog, Home screen) and shown in the README header. First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -4 1038.05 108" role="img" aria-label="ArtCraft">
|
||||
<title>ArtCraft</title>
|
||||
<g fill="#0b0b0c">
|
||||
<path transform="scale(1.02564)" 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"/>
|
||||
<path d="M142.07824124466492 100.0V-0.20598030646411303H224.5093337988741Q235.3915640626314 -0.20598030646411303 242.47102827716765 4.2448305180051875Q249.55049249170392 8.695641342474488 253.08388530613996 15.97633917894725Q256.617278120576 23.25703701542001 256.617278120576 31.917827817510926Q256.617278120576 41.29161779108965 252.08564909759403 49.09045161033163Q247.55402007461208 56.88928542957361 238.35448280295117 61.38287869556304L259.0352045196513 100.0H227.60565795214035L210.562880671027 66.09194563707179H169.9554082596294V100.0ZM169.9554082596294 45.01525167126002H215.67753779224603Q221.38490322307234 45.01525167126002 224.71075426312564 41.60858243269406Q228.03660530317896 38.201913194128096 228.03660530317896 32.65618416032707Q228.03660530317896 28.910440606208994 226.55352218432876 26.28334868406067Q225.07043906547852 23.656256761912346 222.3149998187698 22.30626765215014Q219.55956057206106 20.956278542387935 215.67753779224603 20.956278542387935H169.9554082596294Z M312.0332229234414 100.0V22.638971534484327H268.35528177627936V-0.20598030646411303H383.5883310855678V22.638971534484327H339.91038993840584V100.0Z M458.27161556705016 101.74927113702624Q437.40714777937706 101.74927113702624 423.46369302582383 95.8454810495627Q409.5202382722706 89.94169096209913 402.57861475134996 78.38287353515625Q395.6369912304293 66.82405610821337 395.6369912304293 49.85422740524781Q395.6369912304293 25.012678585664176 411.6814700222251 11.52871357108691Q427.7259488140208 -1.9552514434903543 458.3064661074337 -1.9552514434903543Q475.456917945535 -1.9552514434903543 488.5083118603833 3.126178886035433Q501.55970577523163 8.20760921556122 508.8958646388887 17.516444792900415Q516.2320235025458 26.82528037023961 516.2320235025458 39.59306099672011H489.03300511868554Q489.03300511868554 33.14733922307762 485.1161317984869 28.595099713294914Q481.1992584782883 24.042860203512205 474.2052968660206 21.62493380443695Q467.21133525375296 19.207007405361693 457.80894061146523 19.207007405361693Q447.110456418681 19.207007405361693 439.60629072478065 22.77368929226266Q432.1021250308803 26.340371179163625 428.1899983952093 32.79558632186133Q424.2778717595383 39.25080146455903 424.2778717595383 47.95918367346938V51.60349854227405Q424.2778717595383 60.27703021080084 428.1899983952093 66.80513165087463Q432.1021250308803 73.33323309094843 439.5587615987329 76.9601226895613Q447.0153981665855 80.5870122881742 457.5934669359459 80.5870122881742Q467.61380249762595 80.5870122881742 474.8010658306936 78.272076042331Q481.98832916376114 75.9571397964878 485.8925238982956 71.46510799841701Q489.79671863283005 66.97307620034621 489.79671863283005 60.61291930974399H516.3175883855861Q516.3175883855861 73.0289470350082 509.2048350985849 82.30767875649144Q502.0920818115837 91.58641047797467 489.08347033825555 96.66784080750045Q476.07485886492736 101.74927113702624 458.27161556705016 101.74927113702624Z M537.413518212595 100.0V-0.20598030646411303H619.8446107668042Q630.7268410305614 -0.20598030646411303 637.8063052450977 4.2448305180051875Q644.885769459634 8.695641342474488 648.41916227407 15.97633917894725Q651.9525550885061 23.25703701542001 651.9525550885061 31.917827817510926Q651.9525550885061 41.29161779108965 647.4209260655241 49.09045161033163Q642.8892970425421 56.88928542957361 633.6897597708812 61.38287869556304L654.3704814875814 100.0H622.9409349200704L605.8981576389571 66.09194563707179H565.2906852275595V100.0ZM565.2906852275595 45.01525167126002H611.0128147601761Q616.7201801910024 45.01525167126002 620.0460312310557 41.60858243269406Q623.371882271109 38.201913194128096 623.371882271109 32.65618416032707Q623.371882271109 28.910440606208994 621.8887991522588 26.28334868406067Q620.4057160334086 23.656256761912346 617.6502767866998 22.30626765215014Q614.8948375399912 20.956278542387935 611.0128147601761 20.956278542387935H565.2906852275595Z M662.6163133024502 100.0 712.0998010683712 -0.20598030646411303H743.193458536939L792.6769463028602 100.0H762.4325672314772L754.402333653671 83.1409687898597H699.2874274093653L691.317401543271 100.0ZM708.9368987701752 62.32096947316873H744.7528622928611L735.0177014874674 40.96197456382106Q734.4092539371304 39.53909915668276 733.2953489896883 36.886650063206076Q732.1814440422461 34.2342009697294 731.007331383092 31.285460002220745Q729.833218723938 28.33671903471209 728.8603396867204 25.901367365445054Q727.8874606495028 23.46601569617802 727.500857207903 22.53754284917092H726.3948841615974Q725.2635539439634 25.272371609078903 723.7978960953051 28.821628225788082Q722.3322382466466 32.37088484249726 720.9822491368843 35.68880398141399Q719.6322600271221 39.00672312033072 718.6720595755688 41.04753944686133Z M807.9674540726534 100.0V-0.20598030646411303H905.8314549324839V20.956278542387935H835.8446210876178V42.14414368565506H898.3527086906836V63.30640253450711H835.8446210876178V100.0Z M958.534680649389 100.0V22.638971534484327H914.856739502227V-0.20598030646411303H1030.0897888115155V22.638971534484327H986.41184Line truncated
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-logo.svg
|
||||
title: ArtCraft wordmark (symbol + "ARTCRAFT"), dark ink for light backgrounds
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: SVG. Bundled in the app (About dialog, Home screen) and shown in the README header. First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-mark-black.png
|
||||
title: ArtCraft symbol ("A" mark), black
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: 1024x1024 PNG. Kept for brand use (README, docs). First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
@@ -0,0 +1,4 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 341 B |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-mark-black.svg
|
||||
title: ArtCraft symbol ("A" mark), black
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: SVG. Kept for brand use (README, docs). First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-mark.png
|
||||
title: ArtCraft symbol ("A" mark), blue
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: 1024x1024 PNG. Bundled in the app (About dialog, Home screen) and shown in the README header. First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||
@@ -0,0 +1,4 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 341 B |
@@ -0,0 +1,8 @@
|
||||
asset: artcraft-mark.svg
|
||||
title: ArtCraft symbol ("A" mark), blue
|
||||
author: ArtCraft team (Storyteller)
|
||||
source: original work (first-party ArtCraft brand asset supplied by the ArtCraft team)
|
||||
license: LicenseRef-ArtCraft-Trademark — ArtCraft name and logo are trademarks of the ArtCraft team, all rights reserved; used in FilmCraft with permission. Not covered by the project's MIT OR Apache-2.0 licence.
|
||||
license-file:
|
||||
added: 2026-10-01 by Claude (agent) for Brandon Thomas
|
||||
notes: SVG. Bundled in the app (About dialog, Home screen) and shown in the README header. First-party brand mark: the one exception to the open-licence rule (AGENTS.md §1.8). Forks and redistributions must remove or replace it unless they have permission.
|
||||