fix: load designs off-thread and cache recent thumbnails as PNGs

This commit is contained in:
bherbruck committed 2026-10-10 20:55:14 -04:00
1 parent b375c2a92a
commit 4b7e1d317d
23 files changed
+688 -29

No files matched your search

+36
View File
@@ -439,6 +439,42 @@ impl Session {
}
}
/// Refresh UI caches when adopting a session or switching tabs, without discarding its
/// already prepared world-space geometry. This does not change the document.
pub fn invalidate_view(&mut self, previous_revision: u64) {
let old = self.revision;
self.revision = old.max(previous_revision) + 1;
if let Ok(mut cache) = self.world_cache.lock()
&& let Some((revision, _)) = cache.as_mut()
&& *revision == old
{
*revision = self.revision;
}
}
/// Commit commands evaluated on a scratch copy without losing this session's file,
/// saved state, view state, checkpoints or earlier undo steps.
pub fn accept_computed(&mut self, mut computed: Session) {
let previous_revision = self.revision;
self.doc = computed.doc;
self.model = computed.model;
self.world_cache = computed.world_cache;
self.undo.append(&mut computed.undo);
if self.undo.len() > MAX_UNDO {
self.undo.drain(..self.undo.len() - MAX_UNDO);
}
self.redo = computed.redo;
self.active_sketch = computed.active_sketch;
self.selection = computed.selection;
self.revision = computed.revision;
self.invalidate_view(previous_revision);
self.active_component = computed.active_component;
self.pending_moves = computed.pending_moves;
for message in computed.log {
self.echo(message);
}
}
/// Live preview: run `commands` (id, parameters) on a scratch copy and return the model
/// before and after. Nothing in this session changes (document, undo history, selection).
/// A `timeline.redefine` is shown even while the timeline is rolled back to the feature
+37
View File
@@ -2032,3 +2032,40 @@ fn drawing_on_existing_points_connects_to_them() {
let si = run(&mut s, "sketch.inspect", json!({}));
assert_ne!(si["status"], "conflict", "{si}");
}
#[test]
fn computed_import_preserves_file_and_earlier_undo_history() {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10}));
s.path = Some("existing.solvecraft".into());
s.mark_saved();
let saved = s.doc.clone();
let mut computed = s.scratch();
run(&mut computed, "solid.box", json!({"length": 5, "width": 5, "height": 5, "corner": [30, 0, 0]}));
s.accept_computed(computed);
assert_eq!(s.path.as_deref(), Some("existing.solvecraft"));
assert!(s.is_dirty());
assert_eq!(s.model.state().bodies.len(), 2);
run(&mut s, "edit.undo", json!({}));
assert!(std::sync::Arc::ptr_eq(&saved, &s.doc));
assert!(!s.is_dirty());
run(&mut s, "edit.undo", json!({}));
assert!(s.model.state().bodies.is_empty());
}
#[test]
fn adopting_a_computed_component_keeps_its_warm_world_geometry() {
let mut s = Session::default();
run(&mut s, "component.create", json!({"name": "Part"}));
run(&mut s, "solid.box", json!({"length":10,"width":10,"height":10}));
let mut computed = s.scratch();
run(&mut computed, "solid.box", json!({"length":5,"width":5,"height":5,"corner":[30,0,0]}));
let prepared = computed.world_state();
for b in &prepared.bodies {
let _ = b.mesh();
}
s.accept_computed(computed);
assert!(std::sync::Arc::ptr_eq(&prepared, &s.world_state()));
s.invalidate_view(100);
assert!(std::sync::Arc::ptr_eq(&prepared, &s.world_state()));
}