Skip to main content

bnto_core/editor/
mutations.rs

1// Editor mutations — all state changes that push undo snapshots.
2
3use std::collections::HashMap;
4
5use crate::metadata::NodeTypeInfo;
6
7use super::types::{EditorModel, EditorNode, EditorSnapshot};
8
9impl EditorModel {
10    /// Add a node with defaults populated from engine metadata.
11    pub fn add_node(&mut self, node_type: &str, info: &NodeTypeInfo) {
12        self.push_undo();
13        let id = format!("{}-{}", node_type, self.nodes.len() + 1);
14        self.nodes.push(EditorNode {
15            id,
16            node_type: node_type.to_string(),
17            label: info.label.clone(),
18            params: HashMap::new(),
19            expanded: false,
20        });
21        self.selected_index = Some(self.nodes.len() - 1);
22        self.dirty = true;
23    }
24
25    /// Add a node with pre-populated default parameter values from metadata.
26    pub fn add_node_with_defaults(
27        &mut self,
28        node_type: &str,
29        info: &NodeTypeInfo,
30        param_defaults: &[(String, serde_json::Value)],
31    ) {
32        self.push_undo();
33        let id = format!("{}-{}", node_type, self.nodes.len() + 1);
34        let params: HashMap<String, serde_json::Value> = param_defaults.iter().cloned().collect();
35        self.nodes.push(EditorNode {
36            id,
37            node_type: node_type.to_string(),
38            label: info.label.clone(),
39            params,
40            expanded: false,
41        });
42        self.selected_index = Some(self.nodes.len() - 1);
43        self.dirty = true;
44    }
45
46    /// Remove the node at the given index.
47    pub fn remove_node(&mut self, index: usize) {
48        if index >= self.nodes.len() {
49            return;
50        }
51        self.push_undo();
52        self.nodes.remove(index);
53        self.selected_index = if self.nodes.is_empty() {
54            None
55        } else {
56            Some(index.min(self.nodes.len() - 1))
57        };
58        self.dirty = true;
59    }
60
61    /// Swap nodes at positions `from` and `to`.
62    pub fn reorder(&mut self, from: usize, to: usize) {
63        if from >= self.nodes.len() || to >= self.nodes.len() || from == to {
64            return;
65        }
66        self.push_undo();
67        self.nodes.swap(from, to);
68        self.selected_index = Some(to);
69        self.dirty = true;
70    }
71
72    /// Update a single parameter value on the node at `index`.
73    pub fn update_param(&mut self, index: usize, key: &str, value: serde_json::Value) {
74        if index >= self.nodes.len() {
75            return;
76        }
77        self.push_undo();
78        self.nodes[index].params.insert(key.to_string(), value);
79        self.dirty = true;
80    }
81
82    /// Undo the last mutation.
83    pub fn undo(&mut self) {
84        if let Some(snapshot) = self.undo_stack.pop() {
85            let current = EditorSnapshot::capture(self);
86            self.redo_stack.push(current);
87            self.restore(&snapshot);
88        }
89    }
90
91    /// Redo the last undone mutation.
92    pub fn redo(&mut self) {
93        if let Some(snapshot) = self.redo_stack.pop() {
94            let current = EditorSnapshot::capture(self);
95            self.undo_stack.push(current);
96            self.restore(&snapshot);
97        }
98    }
99
100    /// Mark the model as clean (after a successful save).
101    pub fn mark_clean(&mut self) {
102        self.dirty = false;
103    }
104
105    fn push_undo(&mut self) {
106        self.undo_stack.push(EditorSnapshot::capture(self));
107        self.redo_stack.clear();
108    }
109
110    fn restore(&mut self, snapshot: &EditorSnapshot) {
111        self.recipe_name = snapshot.recipe_name.clone();
112        self.recipe_description = snapshot.recipe_description.clone();
113        self.nodes = snapshot.nodes.clone();
114        self.selected_index = if self.nodes.is_empty() {
115            None
116        } else {
117            self.selected_index.map(|i| i.min(self.nodes.len() - 1))
118        };
119    }
120}