1// workspace_operations.go - Workspace operation methods with validation and safety
2package browser
3
4import (
5 "fmt"
6 "github.com/bnema/dumber/internal/logging"
7 "github.com/bnema/dumber/pkg/webkit"
8)
9
10// SplitPane performs a split operation with validation and safety checks
11func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*paneNode, error) {
12 if wm == nil {
13 return nil, fmt.Errorf("workspace manager is nil")
14 }
15
16 // Validate direction parameter
17 validDirections := map[string]bool{
18 DirectionLeft: true,
19 DirectionRight: true,
20 DirectionUp: true,
21 DirectionDown: true,
22 }
23 if !validDirections[direction] {
24 return nil, fmt.Errorf("invalid split direction '%s', expected one of: left, right, up, down", direction)
25 }
26
27 logging.Debug(fmt.Sprintf("[workspace] Starting split operation: target=%p direction=%s", target, direction))
28
29 // Step 1: Validate geometry constraints (only in DebugBasic+)
30 if wm.debugLevel >= DebugBasic {
31 validation := wm.geometryValidator.ValidateSplit(target, direction)
32 if !validation.IsValid {
33 if wm.debugLevel == DebugFull {
34 return nil, fmt.Errorf("split validation failed: %s", validation.Reason)
35 }
36 logging.Error(fmt.Sprintf("[workspace] WARNING: Split validation failed but allowing operation: %s", validation.Reason))
37 }
38
39 // Log if re-validation will be needed due to pending widget allocation
40 if validation.RequiresRevalidation {
41 logging.Info(fmt.Sprintf("[workspace] Split validation passed with pending allocation - operation will proceed"))
42 }
43
44 // Step 3: Validate tree invariants before operation
45 if err := wm.treeValidator.ValidateTree(wm.root, "before_split"); err != nil {
46 if wm.debugLevel == DebugFull {
47 return nil, fmt.Errorf("tree validation failed before split: %w", err)
48 }
49 logging.Error(fmt.Sprintf("[workspace] WARNING: Tree validation failed before split but allowing operation: %v", err))
50 }
51 }
52
53 // Step 2: Execute directly if we're already on the GTK main thread
54 if webkit.IsMainThread() {
55 logging.Info(fmt.Sprintf("[workspace] Already on main thread, executing split directly"))
56 newNode, err := wm.splitNode(target, direction, nil)
57 if err != nil {
58 return nil, err
59 }
60
61 if wm.debugLevel >= DebugBasic {
62 if err := wm.treeValidator.ValidateTree(wm.root, "after_split"); err != nil {
63 logging.Error(fmt.Sprintf("[workspace] Tree validation failed after split: %v", err))
64 }
65 }
66
67 // Tree rebalancing is only needed after close promotions. Splits have correct allocation from GTK.
68
69 logging.Info(fmt.Sprintf("[workspace] Split operation completed successfully (direct execution): newNode=%p", newNode))
70 return newNode, nil
71 }
72
73 // Step 3: Not on main thread, marshal via IdleAdd
74 logging.Info(fmt.Sprintf("[workspace] Not on main thread, marshalling split via IdleAdd"))
75 var newNode *paneNode
76 var splitErr error
77 done := make(chan struct{})
78
79 _ = webkit.IdleAdd(func() bool {
80 newNode, splitErr = wm.splitNode(target, direction, nil)
81 if splitErr == nil {
82 if wm.debugLevel >= DebugBasic {
83 if verr := wm.treeValidator.ValidateTree(wm.root, "after_split"); verr != nil {
84 logging.Error(fmt.Sprintf("[workspace] Tree validation failed after split: %v", verr))
85 }
86 }
87 }
88 close(done)
89 return false
90 })
91
92 <-done
93
94 if splitErr != nil {
95 return nil, splitErr
96 }
97
98 logging.Info(fmt.Sprintf("[workspace] Split operation completed successfully: newNode=%p", newNode))
99 return newNode, nil
100}
101
102// ClosePane performs a close operation with validation and safety checks
103func (wm *WorkspaceManager) ClosePane(node *paneNode) error {
104 if wm == nil {
105 return fmt.Errorf("workspace manager is nil")
106 }
107
108 logging.Info(fmt.Sprintf("[workspace] Starting close operation: node=%p", node))
109 wm.paneCloseLogf("start bulletproof close node=%p", node)
110 wm.dumpTreeState("before_close")
111
112 // Step 1: Validate tree invariants before operation (only in DebugBasic+)
113 if wm.debugLevel >= DebugBasic {
114 if err := wm.treeValidator.ValidateTree(wm.root, "before_close"); err != nil {
115 if wm.debugLevel == DebugFull {
116 return fmt.Errorf("tree validation failed before close: %w", err)
117 }
118 logging.Error(fmt.Sprintf("[workspace] WARNING: Tree validation failed before close but allowing operation: %v", err))
119 }
120 }
121
122 // Step 3: Check if this is a stacked pane and use enhanced lifecycle management
123 if node.parent != nil && node.parent.isStacked {
124 logging.Debug(fmt.Sprintf("[workspace] Using enhanced stack lifecycle management for close"))
125 return wm.stackLifecycleManager.CloseStackedPaneWithLifecycle(node)
126 }
127
128 // Step 4: Execute directly when already on the GTK main thread
129 if webkit.IsMainThread() {
130 logging.Info(fmt.Sprintf("[workspace] Already on main thread, executing close directly"))
131
132 wm.paneCloseLogf("invoking closePane node=%p", node)
133 promoted, err := wm.closePane(node)
134 if err != nil {
135 wm.paneCloseLogf("closePane failed node=%p err=%v", node, err)
136 wm.dumpTreeState("after_close_error")
137 return err
138 }
139
140 if wm.debugLevel >= DebugBasic {
141 if err := wm.treeValidator.ValidateTree(wm.root, "after_close"); err != nil {
142 logging.Error(fmt.Sprintf("[workspace] Tree validation failed after close: %v", err))
143 }
144 }
145 wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root)
146 wm.dumpTreeState("after_close_success")
147
148 if wm.treeRebalancer != nil {
149 if err := wm.treeRebalancer.RebalanceAfterClose(node, promoted); err != nil {
150 logging.Error(fmt.Sprintf("[workspace] Tree rebalancing failed after close: %v", err))
151 }
152 }
153
154 logging.Info(fmt.Sprintf("[workspace] Close operation completed successfully (direct execution)"))
155 return nil
156 }
157
158 // Step 5: Not on main thread, marshal via IdleAdd
159 logging.Info(fmt.Sprintf("[workspace] Not on main thread, marshalling close via IdleAdd"))
160 var promoted *paneNode
161 var closeErr error
162 done := make(chan struct{})
163
164 _ = webkit.IdleAdd(func() bool {
165 wm.paneCloseLogf("invoking closePane node=%p", node)
166 promoted, closeErr = wm.closePane(node)
167 if closeErr != nil {
168 wm.paneCloseLogf("closePane failed node=%p err=%v", node, closeErr)
169 wm.dumpTreeState("after_close_error")
170 } else {
171 if wm.debugLevel >= DebugBasic {
172 if verr := wm.treeValidator.ValidateTree(wm.root, "after_close"); verr != nil {
173 logging.Error(fmt.Sprintf("[workspace] Tree validation failed after close: %v", verr))
174 }
175 }
176 wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root)
177 wm.dumpTreeState("after_close_success")
178
179 if wm.treeRebalancer != nil {
180 if rerr := wm.treeRebalancer.RebalanceAfterClose(node, promoted); rerr != nil {
181 logging.Error(fmt.Sprintf("[workspace] Tree rebalancing failed after close: %v", rerr))
182 }
183 }
184 }
185 close(done)
186 return false
187 })
188
189 <-done
190
191 if closeErr != nil {
192 return closeErr
193 }
194
195 logging.Info(fmt.Sprintf("[workspace] Close operation completed successfully"))
196 return nil
197}
198
199// StackPane performs a stack operation with validation and safety checks
200func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) {
201 if wm == nil {
202 return nil, fmt.Errorf("workspace manager is nil")
203 }
204
205 logging.Info(fmt.Sprintf("[workspace] Starting stack operation: target=%p", target))
206
207 // Step 1: Validate stack operation constraints (only in DebugBasic+)
208 if wm.debugLevel >= DebugBasic {
209 validation := wm.geometryValidator.ValidateStackOperation(target)
210 if !validation.IsValid {
211 if wm.debugLevel == DebugFull {
212 return nil, fmt.Errorf("stack validation failed: %s", validation.Reason)
213 }
214 logging.Error(fmt.Sprintf("[workspace] WARNING: Stack validation failed but allowing operation: %s", validation.Reason))
215 }
216
217 // Log if re-validation will be needed due to pending widget allocation
218 if validation.RequiresRevalidation {
219 logging.Info(fmt.Sprintf("[workspace] Stack validation passed with pending allocation - operation will proceed"))
220 }
221 }
222
223 // Step 2: Validate tree invariants before operation (only in DebugBasic+)
224 if wm.debugLevel >= DebugBasic {
225 if err := wm.treeValidator.ValidateTree(wm.root, "before_stack"); err != nil {
226 if wm.debugLevel == DebugFull {
227 return nil, fmt.Errorf("tree validation failed before stack: %w", err)
228 }
229 logging.Error(fmt.Sprintf("[workspace] WARNING: Tree validation failed before stack but allowing operation: %v", err))
230 }
231 }
232
233 // Step 3: Execute directly if already on the GTK main thread
234 if webkit.IsMainThread() {
235 logging.Info(fmt.Sprintf("[workspace] Already on main thread, executing stack directly"))
236 newNode, err := wm.stackedPaneManager.StackPane(target)
237 if err != nil {
238 return nil, err
239 }
240
241 if wm.debugLevel >= DebugBasic {
242 if err := wm.treeValidator.ValidateTree(wm.root, "after_stack"); err != nil {
243 logging.Error(fmt.Sprintf("[workspace] Tree validation failed after stack: %v", err))
244 }
245 }
246
247 logging.Info(fmt.Sprintf("[workspace] Stack operation completed successfully (direct execution): newNode=%p", newNode))
248 return newNode, nil
249 }
250
251 // Step 4: Not on main thread, marshal via IdleAdd
252 logging.Info(fmt.Sprintf("[workspace] Not on main thread, marshalling stack via IdleAdd"))
253 var newNode *paneNode
254 var stackErr error
255 done := make(chan struct{})
256
257 _ = webkit.IdleAdd(func() bool {
258 newNode, stackErr = wm.stackedPaneManager.StackPane(target)
259 if stackErr == nil {
260 if wm.debugLevel >= DebugBasic {
261 if verr := wm.treeValidator.ValidateTree(wm.root, "after_stack"); verr != nil {
262 logging.Error(fmt.Sprintf("[workspace] Tree validation failed after stack: %v", verr))
263 }
264 }
265 }
266 close(done)
267 return false
268 })
269
270 <-done
271
272 if stackErr != nil {
273 return nil, stackErr
274 }
275
276 logging.Info(fmt.Sprintf("[workspace] Stack operation completed successfully: newNode=%p", newNode))
277 return newNode, nil
278}
279
280// EnableEnhancedMode enables all enhanced validation features
281func (wm *WorkspaceManager) EnableEnhancedMode() {
282 if wm.treeValidator != nil {
283 wm.treeValidator.Enable()
284 }
285 if wm.treeRebalancer != nil {
286 wm.treeRebalancer.Enable()
287 }
288 logging.Info(fmt.Sprintf("[workspace] Enhanced validation mode enabled"))
289}
290
291// DisableEnhancedMode disables enhanced features for performance
292func (wm *WorkspaceManager) DisableEnhancedMode() {
293 if wm.treeValidator != nil {
294 wm.treeValidator.Disable()
295 }
296 if wm.treeRebalancer != nil {
297 wm.treeRebalancer.Disable()
298 }
299 logging.Info(fmt.Sprintf("[workspace] Enhanced validation mode disabled"))
300}
301
302// SetEnhancedDebugMode enables/disables debug mode for all enhanced validation components
303func (wm *WorkspaceManager) SetEnhancedDebugMode(debug bool) {
304 if wm.treeValidator != nil {
305 wm.treeValidator.SetDebugMode(debug)
306 }
307 if wm.geometryValidator != nil {
308 wm.geometryValidator.SetDebugMode(debug)
309 }
310 logging.Debug(fmt.Sprintf("[workspace] Enhanced debug mode set to: %v", debug))
311}
312
313// GetEnhancedStats returns comprehensive statistics about all enhanced validation components
314func (wm *WorkspaceManager) GetEnhancedStats() map[string]interface{} {
315 stats := make(map[string]interface{})
316
317 if wm.treeValidator != nil {
318 stats["tree_validation"] = wm.treeValidator.GetValidationStats()
319 }
320
321 if wm.treeRebalancer != nil {
322 stats["tree_rebalancing"] = wm.treeRebalancer.GetRebalancingStats()
323 }
324
325 // Geometry validator no longer tracks stats - simplified
326
327 return stats
328}
329
330// ValidateWorkspaceIntegrity performs a comprehensive validation of the workspace
331func (wm *WorkspaceManager) ValidateWorkspaceIntegrity() error {
332 logging.Info(fmt.Sprintf("[workspace] Performing comprehensive workspace integrity check"))
333
334 // Tree structure validation
335 if wm.treeValidator != nil {
336 if err := wm.treeValidator.ValidateTree(wm.root, "integrity_check"); err != nil {
337 return fmt.Errorf("tree validation failed: %w", err)
338 }
339 }
340
341 // Geometry validation simplified - ValidateWorkspaceLayout removed
342
343 logging.Info(fmt.Sprintf("[workspace] Workspace integrity check completed successfully"))
344 return nil
345}
346
347// ShutdownEnhancedComponents gracefully shuts down all enhanced validation components
348func (wm *WorkspaceManager) ShutdownEnhancedComponents() {
349 logging.Info(fmt.Sprintf("[workspace] Shutting down enhanced validation components"))
350
351 // Other components don't require explicit shutdown currently
352 logging.Info(fmt.Sprintf("[workspace] Enhanced validation components shutdown complete"))
353}