mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-12 00:43:16 -06:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e73c4e147f | ||
|
|
0c5b1e85e6 | ||
|
|
7f6a79fd98 | ||
|
|
6dbfbc4058 | ||
|
|
e258db5f19 |
Generated
+27
-25
@@ -910,12 +910,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-fonts-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1b34eba41e62138fc2438d2734523b302840b45d#1b34eba41e62138fc2438d2734523b302840b45d"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b"
|
||||
|
||||
[[package]]
|
||||
name = "craft-i18n"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1b34eba41e62138fc2438d2734523b302840b45d#1b34eba41e62138fc2438d2734523b302840b45d"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b"
|
||||
dependencies = [
|
||||
"fixed_decimal",
|
||||
"icu_decimal",
|
||||
@@ -938,7 +938,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-print"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1b34eba41e62138fc2438d2734523b302840b45d#1b34eba41e62138fc2438d2734523b302840b45d"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b"
|
||||
dependencies = [
|
||||
"craft-winprint-gdi",
|
||||
"log",
|
||||
@@ -952,7 +952,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-ui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1b34eba41e62138fc2438d2734523b302840b45d#1b34eba41e62138fc2438d2734523b302840b45d"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b"
|
||||
dependencies = [
|
||||
"craft-fonts-build",
|
||||
"craft-i18n",
|
||||
@@ -972,7 +972,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-update"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1b34eba41e62138fc2438d2734523b302840b45d#1b34eba41e62138fc2438d2734523b302840b45d"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dunce",
|
||||
@@ -982,6 +982,7 @@ dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
"plist",
|
||||
"rustix 1.1.5",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1018,7 +1019,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-window"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1b34eba41e62138fc2438d2734523b302840b45d#1b34eba41e62138fc2438d2734523b302840b45d"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=6e03f65bf45181db77ba6229716e6624db9eb25b#6e03f65bf45181db77ba6229716e6624db9eb25b"
|
||||
dependencies = [
|
||||
"egui",
|
||||
"objc2 0.6.5",
|
||||
@@ -2022,7 +2023,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"chrono",
|
||||
@@ -2050,7 +2051,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-calc"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-core",
|
||||
"gridcraft-formula",
|
||||
@@ -2062,7 +2063,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-chart"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-calc",
|
||||
"gridcraft-core",
|
||||
@@ -2073,7 +2074,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-cli"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-engine",
|
||||
"gridcraft-mcp",
|
||||
@@ -2086,7 +2087,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-core"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"js-sys",
|
||||
@@ -2097,7 +2098,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-engine"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-calc",
|
||||
"gridcraft-chart",
|
||||
@@ -2120,7 +2121,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-formula"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-core",
|
||||
"serde",
|
||||
@@ -2129,7 +2130,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-functions"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-core",
|
||||
"regex",
|
||||
@@ -2137,7 +2138,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-math"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"ratex-font",
|
||||
"ratex-layout",
|
||||
@@ -2147,7 +2148,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-mcp"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-engine",
|
||||
"log",
|
||||
@@ -2156,7 +2157,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-model"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-core",
|
||||
"gridcraft-formula",
|
||||
@@ -2169,18 +2170,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-numfmt"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-pdf"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-sandbox"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"landlock",
|
||||
"libc",
|
||||
@@ -2191,7 +2192,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-ui-egui"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"craft-ui",
|
||||
"craft-update",
|
||||
@@ -2218,7 +2219,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-web"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"craft-web-api",
|
||||
"craft-web-host",
|
||||
@@ -2237,7 +2238,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gridcraft-xlsx"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"gridcraft-core",
|
||||
@@ -6519,7 +6520,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "winprint"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/storytold/winprint.rs?rev=51934db382f225454cf152a8657eb03789be63c4#51934db382f225454cf152a8657eb03789be63c4"
|
||||
source = "git+https://github.com/storytold/winprint.rs?rev=bc06a7c48b768eb27a828c7986f6d2868b245ee0#bc06a7c48b768eb27a828c7986f6d2868b245ee0"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"fmt-derive",
|
||||
@@ -6534,6 +6535,7 @@ dependencies = [
|
||||
"windows",
|
||||
"windows-core",
|
||||
"windows-implement",
|
||||
"windows-numerics",
|
||||
"xml",
|
||||
]
|
||||
|
||||
@@ -6708,7 +6710,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
dependencies = [
|
||||
"gridcraft-engine",
|
||||
"serde_json",
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
default-members = ["crates/*", "apps/gridcraft", "apps/gridcraft-cli", "xtask"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.95"
|
||||
@@ -29,7 +29,7 @@ gridcraft-math = { path = "crates/math" }
|
||||
# Every craft-libs crate (craft-update and craft-window here, craft-ui in ui-egui, craft-print in
|
||||
# the app) comes from one craft-libs revision.
|
||||
# craft-update: app-specific pretend versions apply to startup and manual checks.
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", rev = "1b34eba41e62138fc2438d2734523b302840b45d", version = "=0.1.0", features = ["egui"] }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", rev = "6e03f65bf45181db77ba6229716e6624db9eb25b", version = "=0.1.0", features = ["egui"] }
|
||||
|
||||
# foundation
|
||||
thiserror = "2"
|
||||
@@ -72,7 +72,7 @@ skrifa = { version = "0.44", default-features = false, features = ["std"] }
|
||||
epaint_default_fonts = "0.37"
|
||||
# One native window per workbook: the window list, Window menu titles and the immediate-viewport
|
||||
# driver. Same craft-libs revision as craft-update above.
|
||||
craft-window = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "1b34eba41e62138fc2438d2734523b302840b45d", default-features = false, features = ["egui"] }
|
||||
craft-window = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "6e03f65bf45181db77ba6229716e6624db9eb25b", default-features = false, features = ["egui"] }
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
|
||||
@@ -35,7 +35,7 @@ winit = { version = "0.30", default-features = false, features = ["x11", "waylan
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# Same craft-libs revision as the workspace's craft-update and craft-window (see the root
|
||||
# Cargo.toml).
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "1b34eba41e62138fc2438d2734523b302840b45d" }
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "6e03f65bf45181db77ba6229716e6624db9eb25b" }
|
||||
craft-update = { workspace = true }
|
||||
rfd = { workspace = true }
|
||||
arboard = { workspace = true, features = ["image-data"] }
|
||||
|
||||
@@ -467,19 +467,11 @@ pub(crate) fn commit<R>(d: &mut DocState, f: impl FnOnce(&mut Ctx) -> Result<R>)
|
||||
if let Some(t0) = t0 {
|
||||
eprintln!("commit: + rows {:?}", since(t0));
|
||||
}
|
||||
// A grant to edit a protected range belongs to the sheet and range it was given for: a sheet that
|
||||
// was renamed, deleted or replaced by another of the same name no longer carries it.
|
||||
purge_unlock_grants(&mut d.unlocked_ranges, &wb);
|
||||
d.wb = std::sync::Arc::new(wb);
|
||||
d.selection = sel;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Drops the grants to edit protected ranges whose sheet name or range title `wb` no longer has.
|
||||
pub(crate) fn purge_unlock_grants(grants: &mut Vec<(String, String)>, wb: &Workbook) {
|
||||
grants.retain(|(sheet, title)| wb.sheets.iter().any(|sh| sh.name == *sheet && sh.edit_ranges.iter().any(|r| r.title == *title)));
|
||||
}
|
||||
|
||||
/// "Set precision as displayed": a number typed, pasted or reformatted is stored as its format
|
||||
/// shows it (Excel rounds a constant when it is entered and when its cell's format changes,
|
||||
/// observed through COM). Only cells the edit touched are visited, so this costs nothing for
|
||||
|
||||
@@ -209,7 +209,6 @@ fn undo(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
crate::push_history(&mut d.redo, label.clone(), cur_wb, &d.wb, cur_selection);
|
||||
}
|
||||
super::extra::keep_watch_list(&now, &mut d.wb);
|
||||
crate::cmd::purge_unlock_grants(&mut d.unlocked_ranges, &d.wb);
|
||||
d.calc.rebuild(&d.wb);
|
||||
d.revision += 1;
|
||||
Ok(json!({"undone": label}))
|
||||
@@ -231,7 +230,6 @@ fn redo(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
crate::push_history(&mut d.undo, label.clone(), cur_wb, &d.wb, cur_selection);
|
||||
}
|
||||
super::extra::keep_watch_list(&now, &mut d.wb);
|
||||
crate::cmd::purge_unlock_grants(&mut d.unlocked_ranges, &d.wb);
|
||||
d.calc.rebuild(&d.wb);
|
||||
d.revision += 1;
|
||||
Ok(json!({"redone": label}))
|
||||
|
||||
@@ -75,9 +75,6 @@ fn denied_script_step(id: &str, params: &Json) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Commands that read the session's clipboard, which a script may use only for a copy made in its own workbook.
|
||||
const CLIPBOARD_READERS: [&str; 3] = ["edit.paste", "edit.pasteSpecial", "home.insertCopiedCells"];
|
||||
|
||||
pub(super) fn trust_scripts(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
// Only a missing `trusted` means "trust"; `"false"`, `0` or `null` are mistakes, not consent.
|
||||
let trusted = match p.get("trusted") {
|
||||
@@ -89,23 +86,6 @@ pub(super) fn trust_scripts(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
Ok(json!({"trusted": trusted}))
|
||||
}
|
||||
|
||||
/// Whether step `id` with `params` takes cells from the session clipboard: not `edit.paste` of explicit
|
||||
/// text that differs from it (that pastes the text, see `edit::clipboard::paste`).
|
||||
fn reads_clipboard(s: &Session, id: &str, params: &Json) -> bool {
|
||||
if !CLIPBOARD_READERS.contains(&id) {
|
||||
return false;
|
||||
}
|
||||
let trim = |t: &str| t.trim_end_matches(['\r', '\n']).to_string();
|
||||
match (id, str_param(params, "text")) {
|
||||
("edit.paste", Some(text)) => s.clipboard.as_ref().is_some_and(|c| trim(&c.text) == trim(text)),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn d_uid(s: &Session) -> Result<u64> {
|
||||
Ok(s.doc()?.uid)
|
||||
}
|
||||
|
||||
/// Runs a stored script. Scripts in a workbook opened from a file run only once the workbook is
|
||||
/// trusted for this session (`automate.trustScripts`, or `trusted: true` on this call); steps that
|
||||
/// reach outside the workbook ([`denied_script_step`]) are refused before any step runs.
|
||||
@@ -128,7 +108,6 @@ pub(super) fn run_script(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
}
|
||||
}
|
||||
let _sandbox = crate::io::ScriptSandbox::enter();
|
||||
let home = d_uid(s)?;
|
||||
let mut n = 0;
|
||||
// What ran, for a recorder in progress: the recording keeps these steps with the run, so
|
||||
// editing or deleting the script before the recording stops does not change what it saved.
|
||||
@@ -138,25 +117,9 @@ pub(super) fn run_script(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
if id.starts_with("automate.") {
|
||||
continue;
|
||||
}
|
||||
if reads_clipboard(s, id, &st["params"]) && s.clipboard.as_ref().is_some_and(|c| c.doc_uid != home) {
|
||||
return Err(bad("automate.runScript", format!("scripts can't run `{id}` on cells copied from another workbook")));
|
||||
}
|
||||
let before = s.ui_requests.len();
|
||||
let wb_before = s.doc().ok().map(|d| (d.uid, d.wb.clone()));
|
||||
// A stored step is not the person: an `approve` it carries is not consent to run a macro.
|
||||
let mut params = st["params"].clone();
|
||||
if let Some(o) = params.as_object_mut() {
|
||||
o.remove("approve");
|
||||
}
|
||||
let ran = s.execute(id, params);
|
||||
// A step (a nested macro, an alias of a window command) that switched windows would point the
|
||||
// rest of the script at another workbook: put the script's own back and stop.
|
||||
if s.doc().map(|d| d.uid).ok() != Some(home) {
|
||||
if let Some(i) = s.docs.iter().position(|d| d.uid == home) {
|
||||
s.set_active(i);
|
||||
}
|
||||
return Err(bad("automate.runScript", format!("`{id}` switched to another workbook; scripts stay in their own")));
|
||||
}
|
||||
let ran = s.execute(id, st["params"].clone());
|
||||
// A step that asks the host to open a URL leaves the app: drop the request and stop.
|
||||
if s.ui_requests.get(before..).is_some_and(|new| new.iter().any(|r| matches!(r, crate::UiRequest::OpenUrl(_)))) {
|
||||
let mut i = 0;
|
||||
|
||||
@@ -6480,34 +6480,6 @@ fn clearing_links_and_notes_keeps_allow_edit_ranges() {
|
||||
assert!(sh.hyperlinks.is_empty() && sh.comments.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_edit_range_grant_does_not_outlive_its_sheet_name() {
|
||||
let mut s = s();
|
||||
s.execute("review.allowEditRanges", json!({"action": "add", "title": "Kept", "range": "C5", "password": "example"})).unwrap();
|
||||
s.execute("review.protectSheet", json!({})).unwrap();
|
||||
s.execute("review.allowEditRanges", json!({"action": "unlock", "title": "Kept", "password": "example"})).unwrap();
|
||||
assert!(!s.doc().unwrap().unlocked_ranges.is_empty());
|
||||
// The sheet is renamed and another takes its name, with a range of the same title and password.
|
||||
s.execute("review.unprotectSheet", json!({})).unwrap();
|
||||
let old = s.doc().unwrap().wb.active().unwrap().name.clone();
|
||||
s.execute("sheet.rename", json!({"name": "Renamed"})).unwrap();
|
||||
assert!(s.doc().unwrap().unlocked_ranges.is_empty(), "the grant went with the old name");
|
||||
s.execute("home.insertSheet", json!({"name": old})).unwrap();
|
||||
s.execute("review.allowEditRanges", json!({"action": "add", "title": "Kept", "range": "C5", "password": "other"})).unwrap();
|
||||
s.execute("review.protectSheet", json!({})).unwrap();
|
||||
assert!(s.execute("cell.set", json!({"cell": "C5", "input": "1"})).is_err(), "the new sheet's range is locked");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undo_drops_the_grant_of_a_range_it_removes() {
|
||||
let mut s = s();
|
||||
s.execute("review.allowEditRanges", json!({"action": "add", "title": "Kept", "range": "C5", "password": "example"})).unwrap();
|
||||
s.execute("review.allowEditRanges", json!({"action": "unlock", "title": "Kept", "password": "example"})).unwrap();
|
||||
assert!(!s.doc().unwrap().unlocked_ranges.is_empty());
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
assert!(s.doc().unwrap().unlocked_ranges.is_empty(), "the range is gone, so is its grant");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formulatext_does_not_reveal_a_hidden_formula() {
|
||||
let mut s = s();
|
||||
|
||||
@@ -2514,40 +2514,3 @@ fn a_step_that_deletes_a_form_control_after_a_partial_footprint_is_caught() {
|
||||
s.execute("macro.run", json!({"name": "Drop", "inputs": "A1", "overwrite": true})).unwrap();
|
||||
assert!(s.doc().unwrap().wb.active().unwrap().controls.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_script_cannot_paste_what_another_workbook_copied() {
|
||||
let mut s = s();
|
||||
let steps = json!([{"command": "edit.paste", "params": {"at": "B2"}}]);
|
||||
s.execute("automate.newScript", json!({"name": "Paste", "commands": steps})).unwrap();
|
||||
// A copy made in this workbook may be pasted by its script.
|
||||
s.execute("cell.set", json!({"cell": "A1", "input": "mine"})).unwrap();
|
||||
s.execute("edit.copy", json!({"range": "A1"})).unwrap();
|
||||
s.execute("automate.runScript", json!({"name": "Paste"})).unwrap();
|
||||
// A copy from another workbook is not read.
|
||||
s.execute("file.new", json!({})).unwrap();
|
||||
s.execute("cell.set", json!({"cell": "A1", "input": "secret"})).unwrap();
|
||||
s.execute("edit.copy", json!({"range": "A1"})).unwrap();
|
||||
s.set_active(0);
|
||||
let err = s.execute("automate.runScript", json!({"name": "Paste"})).unwrap_err().to_string();
|
||||
assert!(err.contains("another workbook"), "{err}");
|
||||
// Explicit text that is not the clipboard's does not read it.
|
||||
let own = json!([{"command": "edit.paste", "params": {"at": "C3", "text": "typed"}}]);
|
||||
s.execute("automate.newScript", json!({"name": "Typed", "commands": own})).unwrap();
|
||||
s.execute("automate.runScript", json!({"name": "Typed"})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_script_step_cannot_approve_a_macro_for_the_person() {
|
||||
let mut s = s();
|
||||
let mut wb = gridcraft_model::Workbook::new();
|
||||
wb.macros.push(crate::macros::parse_macro(&json!({"name": "Theirs", "output": {"onConflict": "overwrite"}, "steps": [{"command": "cell.set", "params": {"cell": "A1", "input": "ran"}}]})).unwrap());
|
||||
let bytes = serde_json::to_vec(&wb).unwrap();
|
||||
s.execute("file.open", json!({"name": "theirs.json", "base64": crate::io::base64_encode(&bytes)})).unwrap();
|
||||
let steps = json!([{"command": "macro.run", "params": {"name": "Theirs", "approve": true}}]);
|
||||
s.execute("automate.newScript", json!({"name": "Run", "commands": steps})).unwrap();
|
||||
let r = s.execute("automate.runScript", json!({"name": "Run", "trusted": true}));
|
||||
assert!(matches!(r, Err(EngineError::Approve(_))), "{r:?}");
|
||||
assert!(s.macro_approvals.is_empty(), "the step's flag approved nothing");
|
||||
assert!(v(&s, "A1").is_empty());
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ workspace = true
|
||||
[dependencies]
|
||||
# The shared capture slot (`craft_ui::capture`) comes from the one craft-libs revision every
|
||||
# craft-libs dependency uses (see the root Cargo.toml).
|
||||
craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "1b34eba41e62138fc2438d2734523b302840b45d" }
|
||||
craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "6e03f65bf45181db77ba6229716e6624db9eb25b" }
|
||||
gridcraft-engine = { workspace = true }
|
||||
gridcraft-chart = { workspace = true }
|
||||
# `monochrome_emoji_fonts` restores the Noto Emoji and emoji-icon faces that 0.37 moved behind
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//! Release and source Flatpaks must allow release checks and host update helpers.
|
||||
fn finish_args(manifest: &str) -> Vec<&str> {
|
||||
manifest
|
||||
.split("finish-args:")
|
||||
.nth(1)
|
||||
.unwrap_or_default()
|
||||
.lines()
|
||||
.take_while(|line| line.is_empty() || line.starts_with(' ') || line.starts_with('#'))
|
||||
.filter_map(|line| line.trim().strip_prefix("- "))
|
||||
.map(|arg| arg.trim_matches(['\'', '"']))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatpak_update_permissions() {
|
||||
let manifests = [
|
||||
include_str!("../../../packaging/linux/flatpak/ai.storyteller.gridcraft.bundle.yml"),
|
||||
include_str!("../../../packaging/linux/flatpak/ai.storyteller.gridcraft.yml"),
|
||||
];
|
||||
let args = manifests.map(finish_args);
|
||||
assert_eq!(args[0], args[1], "source and release permissions must agree");
|
||||
for permissions in args {
|
||||
for required in ["--share=network", "--talk-name=org.freedesktop.Flatpak"] {
|
||||
assert!(permissions.contains(&required), "missing update permission: {required}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ runtime-version: '25.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: gridcraft
|
||||
finish-args:
|
||||
# The shared updater inspects and updates this installation via flatpak-spawn --host.
|
||||
- --talk-name=org.freedesktop.Flatpak
|
||||
- --filesystem=xdg-data/ArtCraft/versions:ro
|
||||
# Display: Wayland first; X11 only when no Wayland compositor is running.
|
||||
- --socket=wayland
|
||||
|
||||
@@ -18,6 +18,8 @@ sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.rust-stable
|
||||
command: gridcraft
|
||||
finish-args:
|
||||
# The shared updater inspects and updates this installation via flatpak-spawn --host.
|
||||
- --talk-name=org.freedesktop.Flatpak
|
||||
- --filesystem=xdg-data/ArtCraft/versions:ro
|
||||
# Display: Wayland first; X11 only when no Wayland compositor is running.
|
||||
- --socket=wayland
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
sonar.projectKey=storytold_gridcraft
|
||||
sonar.organization=storytold
|
||||
|
||||
# The region table is one record per locale with the same field names and many shared values
|
||||
# (month names, formats); that repetition is data, not copy-pasted logic.
|
||||
sonar.cpd.exclusions=crates/core/src/region.rs
|
||||
|
||||
Reference in New Issue
Block a user