diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f93219fae..68a705399 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,6 +12,7 @@ on:
permissions:
contents: write
+ packages: write
concurrency:
group: release-${{ github.ref }}
diff --git a/Cargo.lock b/Cargo.lock
index 797f6498d..b9fb98654 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1763,7 +1763,7 @@ checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693"
[[package]]
name = "figcraft"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"arboard",
"craft-menu",
@@ -1795,7 +1795,7 @@ dependencies = [
[[package]]
name = "figcraft-cli"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"clap",
"clap_complete",
@@ -1813,7 +1813,7 @@ dependencies = [
[[package]]
name = "figcraft-collab"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -1831,7 +1831,7 @@ dependencies = [
[[package]]
name = "figcraft-collab-net"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"figcraft-collab",
@@ -1854,7 +1854,7 @@ dependencies = [
[[package]]
name = "figcraft-color"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"kurbo",
"serde",
@@ -1863,7 +1863,7 @@ dependencies = [
[[package]]
name = "figcraft-doc"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-color",
"figcraft-geom",
@@ -1876,7 +1876,7 @@ dependencies = [
[[package]]
name = "figcraft-engine"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"craft-docpath",
@@ -1910,7 +1910,7 @@ dependencies = [
[[package]]
name = "figcraft-format"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-doc",
"figcraft-layout",
@@ -1927,7 +1927,7 @@ dependencies = [
[[package]]
name = "figcraft-geom"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"kurbo",
"serde",
@@ -1935,7 +1935,7 @@ dependencies = [
[[package]]
name = "figcraft-layout"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-doc",
"figcraft-geom",
@@ -1943,7 +1943,7 @@ dependencies = [
[[package]]
name = "figcraft-mcp"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"figcraft-collab-net",
@@ -1959,7 +1959,7 @@ dependencies = [
[[package]]
name = "figcraft-pathops"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-doc",
"figcraft-geom",
@@ -1968,7 +1968,7 @@ dependencies = [
[[package]]
name = "figcraft-pdf"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-color",
"figcraft-doc",
@@ -1985,7 +1985,7 @@ dependencies = [
[[package]]
name = "figcraft-plugin"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-color",
"serde",
@@ -1998,7 +1998,7 @@ dependencies = [
[[package]]
name = "figcraft-render"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"crc32fast",
"figcraft-color",
@@ -2017,7 +2017,7 @@ dependencies = [
[[package]]
name = "figcraft-server"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"chrono",
"crc32fast",
@@ -2042,7 +2042,7 @@ dependencies = [
[[package]]
name = "figcraft-svg"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"figcraft-color",
@@ -2058,7 +2058,7 @@ dependencies = [
[[package]]
name = "figcraft-text"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"core-text",
"craft-font-catalog",
@@ -2076,7 +2076,7 @@ dependencies = [
[[package]]
name = "figcraft-ui-egui"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -2104,7 +2104,7 @@ dependencies = [
[[package]]
name = "figcraft-vectordrawable"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-color",
"figcraft-doc",
@@ -2118,7 +2118,7 @@ dependencies = [
[[package]]
name = "figcraft-web"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"base64 0.22.1",
"eframe",
@@ -6739,7 +6739,7 @@ checksum = "9440ea3e5aeabb0ac63af70daf835274065238cdd0cec83418f417eae38bacee"
[[package]]
name = "xtask"
-version = "0.13.1"
+version = "0.15.0"
dependencies = [
"figcraft-engine",
"serde_json",
diff --git a/Cargo.toml b/Cargo.toml
index a560971a8..c9b915a69 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ exclude = ["plugins"]
# default-members = ["crates/*", "apps/figcraft", "apps/figcraft-cli", "xtask"]
[workspace.package]
-version = "0.13.1"
+version = "0.15.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
diff --git a/README.md b/README.md
index 63c8fb3d1..012ca857a 100644
--- a/README.md
+++ b/README.md
@@ -314,14 +314,22 @@ stand on its own.
| | App | What it's for | Code | Learn more |
|:-:|---|---|---|---|
-|
| **FigCraft** | **Interface design, components and prototypes · you are here** | [GitHub](https://github.com/storytold/figcraft) | [Website](https://getartcraft.com/apps/figcraft) |
-|
| **PhotoCraft** | Image editing | [GitHub](https://github.com/storytold/photocraft) | [Website](https://getartcraft.com/apps/photocraft) |
+|
| **PhotoCraft** | Image editing: layers, masks, type and real PSD files | [GitHub](https://github.com/storytold/photocraft) | [Website](https://getartcraft.com/apps/photocraft) |
|
| **VectorCraft** | Vector illustration | [GitHub](https://github.com/storytold/vectorcraft) | [Website](https://getartcraft.com/apps/vectorcraft) |
|
| **FilmCraft** | Video editing, color and sound | [GitHub](https://github.com/storytold/filmcraft) | [Website](https://getartcraft.com/apps/filmcraft) |
|
| **LightCraft** | Photo library and raw development | [GitHub](https://github.com/storytold/lightcraft) | [Website](https://getartcraft.com/apps/lightcraft) |
|
| **PdfCraft** | Reading, organizing and protecting PDFs | [GitHub](https://github.com/storytold/pdfcraft) | [Website](https://getartcraft.com/apps/pdfcraft) |
|
| **EffectCraft** | Motion graphics and visual effects | [GitHub](https://github.com/storytold/effectcraft) | [Website](https://getartcraft.com/apps/effectcraft) |
|
| **DesignCraft** | Page layout and publishing | [GitHub](https://github.com/storytold/designcraft) | [Website](https://getartcraft.com/apps/designcraft) |
+|
| **FigCraft** | Interface design and prototyping | [GitHub](https://github.com/storytold/figcraft) | [Website](https://getartcraft.com/apps/figcraft) |
+|
| **WordCraft** | Word processing and documents | [GitHub](https://github.com/storytold/wordcraft) | [Website](https://getartcraft.com/apps/wordcraft) |
+|
| **SoundCraft** | Audio recording and editing | [GitHub](https://github.com/storytold/soundcraft) | [Website](https://getartcraft.com/apps/soundcraft) |
+|
| **DeckCraft** | Presentations and slide decks | [GitHub](https://github.com/storytold/deckcraft) | [Website](https://getartcraft.com/apps/deckcraft) |
+|
| **CADCraft** | Computer-aided design | [GitHub](https://github.com/storytold/cadcraft) | [Website](https://getartcraft.com/apps/cadcraft) |
+|
| **GridCraft** | Spreadsheets and data | [GitHub](https://github.com/storytold/gridcraft) | [Website](https://getartcraft.com/apps/gridcraft) |
+|
| **TextureCraft** | 3D texture painting | [GitHub](https://github.com/storytold/texturecraft) | [Website](https://getartcraft.com/apps/texturecraft) |
+|
| **Craft Anything** | See how any software works, then build your own | [GitHub](https://github.com/storytold/craft-anything) | [Website](https://getartcraft.com/apps/craft-anything) |
+|
| **SolidCraft** | Parametric 3D CAD by Tangency Labs (community app) | [GitHub](https://github.com/tangency-labs/solidcraft) | |
And [**ArtCraft**](https://getartcraft.com/) itself, our AI image and video studio for artists who want real control.
diff --git a/apps/figcraft-cli/examples/man-and-completions.rs b/apps/figcraft-cli/examples/man-and-completions.rs
index c67827477..2b119d6a8 100644
--- a/apps/figcraft-cli/examples/man-and-completions.rs
+++ b/apps/figcraft-cli/examples/man-and-completions.rs
@@ -6,7 +6,7 @@
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
#[path = "../src/args.rs"]
-#[expect(dead_code, reason = "Generation uses clap's command metadata, not the parsed fields or runtime parser.")]
+#[cfg_attr(not(test), expect(dead_code, reason = "Generation uses clap's command metadata, not the parsed fields or runtime parser."))]
mod args;
use std::path::Path;
diff --git a/contributors/people.toml b/contributors/people.toml
index db1f5ce59..c846d8725 100644
--- a/contributors/people.toml
+++ b/contributors/people.toml
@@ -98,3 +98,6 @@ sync_github_name = true
[people.dylanmtaylor]
sync_github_name = true
+
+[people.aleksei-suholita]
+real_name = "Oleksii Sukholita"
diff --git a/crates/collab/src/change.rs b/crates/collab/src/change.rs
index bcc09ad86..442376f37 100644
--- a/crates/collab/src/change.rs
+++ b/crates/collab/src/change.rs
@@ -333,6 +333,17 @@ impl Change {
}
impl Entry {
+ /// The synced list this entry belongs to (`None` for entries kept in maps).
+ pub(crate) fn family(&self) -> Option {
+ match self {
+ Entry::Style(_) => Some(Family::Styles),
+ Entry::Comment(_) => Some(Family::Comments),
+ Entry::Collection(_) => Some(Family::Collections),
+ Entry::Variable(_) => Some(Family::Variables),
+ Entry::DevSnapshot(_) | Entry::Image(_) | Entry::Ballot(_) => None,
+ }
+ }
+
/// The client id space the entry's id lies in ([`figcraft_doc::ids`]): ids made during a live
/// session carry their creator's client id, ids from before and ids of other forms are in
/// space 0. `None` for entries keyed by something else (dev snapshots by layer, images by
diff --git a/crates/collab/src/docprops.rs b/crates/collab/src/docprops.rs
index e203d2c3b..b590064df 100644
--- a/crates/collab/src/docprops.rs
+++ b/crates/collab/src/docprops.rs
@@ -54,18 +54,71 @@ struct List {
entry: fn(T::Key) -> Entry,
get: fn(&Document) -> &Vec,
get_mut: fn(&mut Document) -> &mut Vec,
+ /// The list's slot in an [`Index`].
+ index: fn(&mut Index) -> &mut Option>,
}
-const STYLES: List