bnto_core/editor/
mutations.rs1use std::collections::HashMap;
4
5use crate::metadata::NodeTypeInfo;
6
7use super::types::{EditorModel, EditorNode, EditorSnapshot};
8
9impl EditorModel {
10 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 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 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 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 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 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 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 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}