From 97db282daf89d72c2ad93deef2a96efac5012311 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Thu, 29 Oct 2026 22:14:18 -0400 Subject: [PATCH 1/3] CI: workflows only call craft-common-actions at @main (#1557) AGENTS.md (and CLAUDE.md where present) gets a CI workflows section: workflows only call craft-common-actions at @main, with no other jobs; feature and fix PRs don't touch CI and link or open a craft-common-actions issue (a PR only for a small fix that is safe for every app and does not increase CI time); legacy jobs aren't added to and move only as their own task, staying until a shared workflow runs them. windows-arm64.yml moves from its SHA pin to @main. The pinned revision is on craft-common-actions main and the workflow (and the actions it calls) is identical there, so nothing changes in behaviour. --- .github/workflows/windows-arm64.yml | 4 ++-- AGENTS.md | 5 +++++ CLAUDE.md | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index f577b3f7..1e5b3883 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -24,8 +24,8 @@ permissions: jobs: windows-arm64: - # Immutable supporting workflow revision until the new install contract lands upstream. - uses: storytold/craft-common-actions/.github/workflows/windows-arm64.yml@8bb4e5ab95443171104ad88113fe1ef9b0e5ce3f + # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos. + uses: storytold/craft-common-actions/.github/workflows/windows-arm64.yml@main with: app-name: deckcraft app-dir: DeckCraft diff --git a/AGENTS.md b/AGENTS.md index 8a7ce7cf..c998e871 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -192,3 +192,8 @@ the `ROADMAP.md` progress log, and update each touched doc's status line and rev GitHub profiles). Never hand-edit `contributors.json`. - Maintainers refresh the stats with `python3 ../../craftrules/scripts/contributors.py .` (it also re-verifies who wrote each `people.toml` entry; `--check` only verifies). + +## CI workflows +- Workflows in `.github/workflows/` call the shared workflows in [`storytold/craft-common-actions`](https://github.com/storytold/craft-common-actions) at `@main`, with their inputs and secrets. No other jobs: if no shared workflow does what's needed, it doesn't go here. +- Don't add or change CI in a feature or fix PR. If the existing workflows can't build or test a change, link an existing craft-common-actions issue or PR, or open a new issue, or a PR only for a small fix that is safe for every app and does not increase CI time. +- Some workflow files here still run their own jobs. Don't add to them. Moving a job into craft-common-actions is its own task, never part of another PR, and the job stays here until a shared workflow runs it. diff --git a/CLAUDE.md b/CLAUDE.md index aae6e13f..669965b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,3 +19,8 @@ before modules or other crate items; crate-level `//!` documentation may precede - Commit after each landed arc (`M.: what`) and push to `origin main`. - Keep `ROADMAP.md`, the progress docs in `docs/` (AGENTS.md ยง7) and `plan/STATUS.md` current. - Don't ask the user questions; record genuinely-theirs decisions in `plan/STATUS.md`. + +## CI workflows +- Workflows in `.github/workflows/` call the shared workflows in [`storytold/craft-common-actions`](https://github.com/storytold/craft-common-actions) at `@main`, with their inputs and secrets. No other jobs: if no shared workflow does what's needed, it doesn't go here. +- Don't add or change CI in a feature or fix PR. If the existing workflows can't build or test a change, link an existing craft-common-actions issue or PR, or open a new issue, or a PR only for a small fix that is safe for every app and does not increase CI time. +- Some workflow files here still run their own jobs. Don't add to them. Moving a job into craft-common-actions is its own task, never part of another PR, and the job stays here until a shared workflow runs it. From 505a6f28ac1e35a713d06e489c139ed0fa069e48 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 30 Oct 2026 01:28:38 -0400 Subject: [PATCH 2/3] Update prompt shows the app icon and accent color instead of a letter (#1561) UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#f07a1e, its colour in ArtCraft Launcher). The updater, the self-update helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer). craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434). Clippy fixes for Rust 1.99 in untouched code (same logic): - crates/engine/src/tools/click.rs - crates/engine/src/tools/pointer.rs - crates/metafile/src/emfplus.rs - crates/model/src/trendline.rs - crates/pptx/src/read/dml.rs - xtask/src/layers.rs --- Cargo.lock | 21 +++++++++++---------- Cargo.toml | 3 ++- apps/deckcraft/src/main.rs | 2 +- crates/engine/src/tools/click.rs | 2 +- crates/engine/src/tools/pointer.rs | 5 ++--- crates/metafile/src/emfplus.rs | 2 +- crates/model/src/trendline.rs | 2 +- crates/pptx/src/read/dml.rs | 18 +++++++----------- crates/ui-egui/src/dialogs/updates_ui.rs | 2 +- crates/ui-egui/src/lib.rs | 9 +++++++++ xtask/src/layers.rs | 6 ++---- 11 files changed, 38 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed9a4995..389d774a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ dependencies = [ "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys 0.61.2", "wl-clipboard-rs", "x11rb", ] @@ -888,13 +888,14 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0" +source = "git+https://github.com/storytold/craft-libs?rev=1065653e75854e9c17e1ff563acdd344c1b185f0#1065653e75854e9c17e1ff563acdd344c1b185f0" dependencies = [ "chrono", "dunce", "egui", "flate2", "getrandom 0.3.4", + "image", "log", "plist", "semver", @@ -1651,7 +1652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3013,7 +3014,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3486,7 +3487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.45.0", ] [[package]] @@ -4041,7 +4042,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4098,7 +4099,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4731,10 +4732,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix 1.1.5", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5658,7 +5659,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 07960495..337c36ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,8 @@ vello_common = { version = "0.2", default-features = false } image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif", "tiff", "bmp"] } craft-media = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.1.0", default-features = false } craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.1.0", default-features = false, features = ["write", "write-simple-text"] } -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", features = ["egui"] } +# craft-update with storytold/craft-libs#434: the update prompt shows the app's icon, accent and developer. +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "1065653e75854e9c17e1ff563acdd344c1b185f0", features = ["egui"] } craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.1.0" } i_overlay = "9" quick-xml = "0.41" diff --git a/apps/deckcraft/src/main.rs b/apps/deckcraft/src/main.rs index 605dc84a..03859dc0 100644 --- a/apps/deckcraft/src/main.rs +++ b/apps/deckcraft/src/main.rs @@ -187,7 +187,7 @@ fn main() -> eframe::Result { // First, so every start-up record (and the engine's panic hook, installed with the first // Session) is captured; see `logging`. let logger = logging::install(); - if let Some(done) = craft_update::self_update::finish(&craft_update::suite::DECKCRAFT, &std::env::args_os().skip(1).collect::>()) { + if let Some(done) = craft_update::self_update::finish(&deckcraft_ui_egui::UPDATE_APP, &std::env::args_os().skip(1).collect::>()) { if let Err(e) = done { eprintln!("DeckCraft update helper failed: {e}"); std::process::exit(1); diff --git a/crates/engine/src/tools/click.rs b/crates/engine/src/tools/click.rs index 0f066dc0..802eb8c5 100644 --- a/crates/engine/src/tools/click.rs +++ b/crates/engine/src/tools/click.rs @@ -206,7 +206,7 @@ impl Session { let Some(f) = st.shape(id).and_then(|sh| cmd::table::table_frame(st, sh)) else { return Ok(()) }; let size = if column { f.cols.get(index) } else { f.heights.get(index) }.copied().unwrap_or(0.0); let (start, frame) = (f.local(p), f.affine); - if !st.selection.text.as_ref().is_some_and(|t| t.shape == id) { + if st.selection.text.as_ref().is_none_or(|t| t.shape != id) { self.select(|_, sel| { sel.shapes = vec![id]; sel.text = None; diff --git a/crates/engine/src/tools/pointer.rs b/crates/engine/src/tools/pointer.rs index bee8f320..cb71cfed 100644 --- a/crates/engine/src/tools/pointer.rs +++ b/crates/engine/src/tools/pointer.rs @@ -455,12 +455,11 @@ impl Session { self.execute("guide.move", &json!({"index": index, "pos": pos}))? }; } - Some(Drag::Move { moved: false, enter_text: Some((id, at)), .. }) => { + Some(Drag::Move { moved: false, enter_text: Some((id, at)), .. }) // Plain click on text: caret at the click (in a table, in the clicked cell). - if self.edit_text_at(id, at)? { + if self.edit_text_at(id, at)? => { out = json!({"editing": id}); } - } Some(Drag::Marquee { start, add }) => { let r = Rect::from_points(start, p); self.tool.marquee = None; diff --git a/crates/metafile/src/emfplus.rs b/crates/metafile/src/emfplus.rs index 62e8f714..afdb3c88 100644 --- a/crates/metafile/src/emfplus.rs +++ b/crates/metafile/src/emfplus.rs @@ -93,7 +93,7 @@ impl Fill { if w == 0.0 || h == 0.0 { return [0, 0, 0, 0]; } - if *wrap == 4 && !(0.0..w).contains(&p.0) || *wrap == 4 && !(0.0..h).contains(&p.1) { + if *wrap == 4 && !((0.0..w).contains(&p.0) && (0.0..h).contains(&p.1)) { return [0, 0, 0, 0]; } let x = (p.0.rem_euclid(w) as usize).min(img.w - 1); diff --git a/crates/model/src/trendline.rs b/crates/model/src/trendline.rs index b2645ac6..2b9cdf6b 100644 --- a/crates/model/src/trendline.rs +++ b/crates/model/src/trendline.rs @@ -480,7 +480,7 @@ fn num(v: f64) -> String { if a == 0.0 { return "0".to_string(); } - if a >= 1.0e6 || a < 1.0e-3 { + if !(1.0e-3..1.0e6).contains(&a) { return format!("{v:.2e}").replace('+', ""); } let digits = if a >= 100.0 { diff --git a/crates/pptx/src/read/dml.rs b/crates/pptx/src/read/dml.rs index e96fcd22..767b7dc0 100644 --- a/crates/pptx/src/read/dml.rs +++ b/crates/pptx/src/read/dml.rs @@ -631,18 +631,14 @@ pub fn paragraph(imp: &mut Imp, part: &Part, p: &El) -> Paragraph { let text: String = e.child("t").map(|t| t.text()).unwrap_or_default(); push_text_runs(&mut para, props, &text); } - "br" => { - if para.runs.len() < MAX_PARAGRAPH_RUNS { - let props = run_props(imp, part, e); - para.runs.push(Run { text: String::new(), props, kind: RunKind::Break }); - } + "br" if para.runs.len() < MAX_PARAGRAPH_RUNS => { + let props = run_props(imp, part, e); + para.runs.push(Run { text: String::new(), props, kind: RunKind::Break }); } - "fld" => { - if para.runs.len() < MAX_PARAGRAPH_RUNS { - let props = run_props(imp, part, e); - let text = e.child("t").map(|t| t.text()).unwrap_or_default(); - para.runs.push(Run { text, props, kind: RunKind::Field { field: e.attr("type").unwrap_or("").to_string() } }); - } + "fld" if para.runs.len() < MAX_PARAGRAPH_RUNS => { + let props = run_props(imp, part, e); + let text = e.child("t").map(|t| t.text()).unwrap_or_default(); + para.runs.push(Run { text, props, kind: RunKind::Field { field: e.attr("type").unwrap_or("").to_string() } }); } "m" => push_math_run(&mut para, part, e), "endParaRPr" => para.end_props = rpr(imp, part, e), diff --git a/crates/ui-egui/src/dialogs/updates_ui.rs b/crates/ui-egui/src/dialogs/updates_ui.rs index 1b65dcb2..1fbfe4d2 100644 --- a/crates/ui-egui/src/dialogs/updates_ui.rs +++ b/crates/ui-egui/src/dialogs/updates_ui.rs @@ -16,7 +16,7 @@ pub(crate) struct Runtime { impl Default for Runtime { fn default() -> Self { - Self { updater: Updater::new(&craft_update::suite::DECKCRAFT, VERSION), enabled: false, check_requested: false } + Self { updater: Updater::new(&crate::UPDATE_APP, VERSION), enabled: false, check_requested: false } } } diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 30b5a0cd..5f9e9cd2 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -6,6 +6,15 @@ #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] #![forbid(unsafe_code)] +/// DeckCraft as craft-update knows it, with its 128 px icon and accent colour: the update +/// prompt shows both, and an AppImage's menu entry gets the icon. +#[cfg(not(target_arch = "wasm32"))] +pub const UPDATE_APP: craft_update::app::App = craft_update::app::App { + icon_png: Some(include_bytes!("../../../assets/app-icon/hicolor/128x128/apps/ai.storyteller.deckcraft.png")), + accent: Some([0xf0, 0x7a, 0x1e]), + ..craft_update::suite::DECKCRAFT +}; + pub mod backstage; pub mod canvas; pub mod capture; diff --git a/xtask/src/layers.rs b/xtask/src/layers.rs index 9921f9d7..8712505f 100644 --- a/xtask/src/layers.rs +++ b/xtask/src/layers.rs @@ -183,10 +183,8 @@ pub fn check(crates: &[Crate]) -> Vec { match classify(&d.name) { // Unregistered deps are reported on their own entry. None => {} - Some(Class::Testkit) => { - if d.kind != DepKind::Dev { - out.push(Violation::TestkitAsNormalDep { krate: c.name.clone() }); - } + Some(Class::Testkit) if d.kind != DepKind::Dev => { + out.push(Violation::TestkitAsNormalDep { krate: c.name.clone() }); } Some(dc) => { let to = dc.layer().unwrap_or(u8::MAX); From 3a4e30e10e83c5f988a074dba1a519984828b4fd Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 30 Oct 2026 02:33:11 -0400 Subject: [PATCH 3/3] UPD.1: honor pretend versions and restore dev-testkit checks (#1562) --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- contributors/people.toml | 3 +++ xtask/src/layers.rs | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 389d774a..a6ef452e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -888,7 +888,7 @@ dependencies = [ [[package]] name = "craft-update" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=1065653e75854e9c17e1ff563acdd344c1b185f0#1065653e75854e9c17e1ff563acdd344c1b185f0" +source = "git+https://github.com/storytold/craft-libs?rev=04aa1703b32d299e54f78fcde4182f2b393a9116#04aa1703b32d299e54f78fcde4182f2b393a9116" dependencies = [ "chrono", "dunce", diff --git a/Cargo.toml b/Cargo.toml index 337c36ac..3eada708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,8 +61,8 @@ vello_common = { version = "0.2", default-features = false } image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif", "tiff", "bmp"] } craft-media = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.1.0", default-features = false } craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.1.0", default-features = false, features = ["write", "write-simple-text"] } -# craft-update with storytold/craft-libs#434: the update prompt shows the app's icon, accent and developer. -craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "1065653e75854e9c17e1ff563acdd344c1b185f0", features = ["egui"] } +# craft-update: app-specific pretend versions apply to startup and manual checks. +craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "04aa1703b32d299e54f78fcde4182f2b393a9116", features = ["egui"] } craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.1.0" } i_overlay = "9" quick-xml = "0.41" diff --git a/contributors/people.toml b/contributors/people.toml index 31dccab2..caf4daa4 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -68,3 +68,6 @@ sync_github_name = true [people.SofiaAFigueredo] real_name = "Sofia Alves Figueredo" + +[people.dylanmtaylor] +sync_github_name = true diff --git a/xtask/src/layers.rs b/xtask/src/layers.rs index 8712505f..47a9e67a 100644 --- a/xtask/src/layers.rs +++ b/xtask/src/layers.rs @@ -186,6 +186,8 @@ pub fn check(crates: &[Crate]) -> Vec { Some(Class::Testkit) if d.kind != DepKind::Dev => { out.push(Violation::TestkitAsNormalDep { krate: c.name.clone() }); } + // Test support is allowed as a dev-dependency, regardless of its layer. + Some(Class::Testkit) => {} Some(dc) => { let to = dc.layer().unwrap_or(u8::MAX); let dev_ok = d.kind == DepKind::Dev && DEV_EXCEPTIONS.contains(&(short_name(&c.name), short_name(&d.name)));