mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-15 06:44:14 +00:00
Merge fix/612
This commit is contained in:
commit
1328092a7e
2 files changed
+75
-4
No files matched your search
@@ -0,0 +1,51 @@
|
||||
//! Imported file bytes already live in the document: the command log keeps no second copy.
|
||||
|
||||
use base64::Engine as _;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::Session;
|
||||
|
||||
const SVG: &str = r##"<svg xmlns="http://www.w3.org/2000/svg" width="40" height="20"><rect width="40" height="20" fill="#3366cc"/></svg>"##;
|
||||
|
||||
fn payload() -> String {
|
||||
base64::engine::general_purpose::STANDARD.encode(SVG)
|
||||
}
|
||||
|
||||
fn holds_payload(journal: &[(String, Value)]) -> bool {
|
||||
let payload = payload();
|
||||
journal.iter().any(|(_, params)| params.to_string().contains(&payload))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_imports_keep_no_payload_in_the_journal() {
|
||||
let mut s = Session::new();
|
||||
let before = s.doc().unwrap().node_count();
|
||||
s.execute("file.import", &json!({"data": payload(), "name": "pasted.svg"})).unwrap();
|
||||
assert!(!holds_payload(&s.journal), "the journal must not retain the Base64 file");
|
||||
// Not journaling is no change to the edit itself: still one undo step.
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().node_count(), before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_wrappers_do_not_retain_import_payloads() {
|
||||
let mut s = Session::new();
|
||||
let nested =
|
||||
json!({"command": "command.batch", "params": {"commands": [{"command": "file.import", "params": {"data": payload(), "name": "a.svg"}}]}});
|
||||
s.execute("command.batch", &json!({"commands": [nested]})).unwrap();
|
||||
assert!(!holds_payload(&s.journal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_imports_and_ordinary_edits_are_still_journaled() {
|
||||
let dir = std::env::temp_dir().join(format!("figcraft-import-journal-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("kit.svg");
|
||||
std::fs::write(&path, SVG).unwrap();
|
||||
let mut s = Session::new();
|
||||
s.execute("file.import", &json!({"path": path.to_string_lossy()})).unwrap();
|
||||
s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap();
|
||||
let ids: Vec<_> = s.journal.iter().map(|(id, _)| id.as_str()).collect();
|
||||
assert_eq!(ids, ["file.import", "shape.rectangle"]);
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
@@ -721,7 +721,8 @@ pub struct Session {
|
||||
/// dark page colour while it is dark (added pages take the current page's). Headless sessions (CLI, MCP, exports) have no
|
||||
/// theme, so what scripts make is the same everywhere.
|
||||
pub ui_dark: Option<bool>,
|
||||
/// Command log (id + params) for actions/automation.
|
||||
/// Command log (id + params) for actions/automation. File imports that carry the file bytes
|
||||
/// (`file.import` with `data`) are not logged, so the log never holds a copy of a pasted file.
|
||||
pub journal: Vec<(String, Value)>,
|
||||
/// Recently used colours.
|
||||
pub recent_colors: Vec<figcraft_color::Color>,
|
||||
@@ -746,6 +747,19 @@ impl Default for Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a command, or a batch wrapping one, imports a file passed as Base64 `data`.
|
||||
fn command_carries_import_bytes(id: &str, params: &Value) -> bool {
|
||||
match id {
|
||||
"file.import" => params::get(params, "data").is_some(),
|
||||
"command.batch" => params.get("commands").and_then(Value::as_array).is_some_and(|commands| {
|
||||
commands.iter().any(|c| {
|
||||
c.get("command").and_then(Value::as_str).zip(c.get("params")).is_some_and(|(id, params)| command_carries_import_bytes(id, params))
|
||||
})
|
||||
}),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// A session with one new document.
|
||||
pub fn new() -> Self {
|
||||
@@ -885,9 +899,13 @@ impl Session {
|
||||
if spec.history && id != "edit.repeat" {
|
||||
self.last_command = Some((id.to_string(), params.clone()));
|
||||
}
|
||||
self.journal.push((id.to_string(), params.clone()));
|
||||
if self.journal.len() > 5000 {
|
||||
self.journal.drain(..1000);
|
||||
// Imported bytes already live in the document: a Base64 copy in the log would stay
|
||||
// for the whole session.
|
||||
if !command_carries_import_bytes(id, params) {
|
||||
self.journal.push((id.to_string(), params.clone()));
|
||||
if self.journal.len() > 5000 {
|
||||
self.journal.drain(..1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
@@ -1550,6 +1568,8 @@ mod html_codegen_tests;
|
||||
#[cfg(test)]
|
||||
mod import_export_tests;
|
||||
#[cfg(test)]
|
||||
mod import_journal_tests;
|
||||
#[cfg(test)]
|
||||
mod inspect_behaviour_tests;
|
||||
#[cfg(test)]
|
||||
mod interaction_rollback_tests;
|
||||
|
||||
Reference in new issue
Block a user