feat(navigation): add direction-aware stack entry logic
- commit
- 7c1a5d94194d5609fa9a13d2f993dcf875d51aaa
- author
- bnema · · 2025-11-13 19:05 UTC
- committer
- bnema · · 2025-11-13 22:18 UTC
- parent
- 0319096
- signature
- unsigned
1 file · +54 -0
M internal/app/browser/workspace_pane_ops.go +54 -0
| @@ -52,6 +52,60 @@ func (wm *WorkspaceManager) collectLeaves() []*paneNode { | |||
| 52 | 52 | return wm.collectLeavesFrom(wm.root) | |
| 53 | 53 | } | |
| 54 | 54 | ||
| 55 | + | // collectLeavesFromWithDirection collects leaves from a subtree with direction-aware stack entry | |
| 56 | + | func (wm *WorkspaceManager) collectLeavesFromWithDirection(node *paneNode, direction string) []*paneNode { | |
| 57 | + | var leaves []*paneNode | |
| 58 | + | visited := make(map[*paneNode]bool) | |
| 59 | + | ||
| 60 | + | var walk func(*paneNode, int) | |
| 61 | + | walk = func(n *paneNode, depth int) { | |
| 62 | + | // Prevent infinite recursion and cycles | |
| 63 | + | const maxDepth = 50 | |
| 64 | + | if n == nil || depth > maxDepth { | |
| 65 | + | return | |
| 66 | + | } | |
| 67 | + | if visited[n] { | |
| 68 | + | log.Printf("[workspace] collectLeavesFromWithDirection: cycle detected in tree") | |
| 69 | + | return | |
| 70 | + | } | |
| 71 | + | visited[n] = true | |
| 72 | + | ||
| 73 | + | if n.isLeaf { | |
| 74 | + | leaves = append(leaves, n) | |
| 75 | + | return | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | // Handle stacked panes - select entry pane based on direction | |
| 79 | + | if n.isStacked && len(n.stackedPanes) > 0 { | |
| 80 | + | var entryIndex int | |
| 81 | + | switch direction { | |
| 82 | + | case DirectionDown: | |
| 83 | + | // Coming from above - enter at first pane | |
| 84 | + | entryIndex = 0 | |
| 85 | + | case DirectionUp: | |
| 86 | + | // Coming from below - enter at last pane | |
| 87 | + | entryIndex = len(n.stackedPanes) - 1 | |
| 88 | + | default: | |
| 89 | + | // For horizontal directions, use active pane | |
| 90 | + | entryIndex = n.activeStackIndex | |
| 91 | + | if entryIndex < 0 || entryIndex >= len(n.stackedPanes) { | |
| 92 | + | entryIndex = 0 | |
| 93 | + | } | |
| 94 | + | } | |
| 95 | + | if entryIndex >= 0 && entryIndex < len(n.stackedPanes) { | |
| 96 | + | walk(n.stackedPanes[entryIndex], depth+1) | |
| 97 | + | } | |
| 98 | + | return | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | // Handle regular split nodes | |
| 102 | + | walk(n.left, depth+1) | |
| 103 | + | walk(n.right, depth+1) | |
| 104 | + | } | |
| 105 | + | walk(node, 0) | |
| 106 | + | return leaves | |
| 107 | + | } | |
| 108 | + | ||
| 55 | 109 | // collectLeavesFrom collects all leaf nodes from a given subtree | |
| 56 | 110 | func (wm *WorkspaceManager) collectLeavesFrom(node *paneNode) []*paneNode { | |
| 57 | 111 | var leaves []*paneNode | |
| @@ -52,6 +52,60 @@ func (wm *WorkspaceManager) collectLeaves() []*paneNode { | |||||
| 52 | return wm.collectLeavesFrom(wm.root) | 52 | return wm.collectLeavesFrom(wm.root) | ||
| 53 | } | 53 | } | ||
| 54 | 54 | ||||
| 55 | + | // collectLeavesFromWithDirection collects leaves from a subtree with direction-aware stack entry | |||
| 56 | + | func (wm *WorkspaceManager) collectLeavesFromWithDirection(node *paneNode, direction string) []*paneNode { | |||
| 57 | + | var leaves []*paneNode | |||
| 58 | + | visited := make(map[*paneNode]bool) | |||
| 59 | + | ||||
| 60 | + | var walk func(*paneNode, int) | |||
| 61 | + | walk = func(n *paneNode, depth int) { | |||
| 62 | + | // Prevent infinite recursion and cycles | |||
| 63 | + | const maxDepth = 50 | |||
| 64 | + | if n == nil || depth > maxDepth { | |||
| 65 | + | return | |||
| 66 | + | } | |||
| 67 | + | if visited[n] { | |||
| 68 | + | log.Printf("[workspace] collectLeavesFromWithDirection: cycle detected in tree") | |||
| 69 | + | return | |||
| 70 | + | } | |||
| 71 | + | visited[n] = true | |||
| 72 | + | ||||
| 73 | + | if n.isLeaf { | |||
| 74 | + | leaves = append(leaves, n) | |||
| 75 | + | return | |||
| 76 | + | } | |||
| 77 | + | ||||
| 78 | + | // Handle stacked panes - select entry pane based on direction | |||
| 79 | + | if n.isStacked && len(n.stackedPanes) > 0 { | |||
| 80 | + | var entryIndex int | |||
| 81 | + | switch direction { | |||
| 82 | + | case DirectionDown: | |||
| 83 | + | // Coming from above - enter at first pane | |||
| 84 | + | entryIndex = 0 | |||
| 85 | + | case DirectionUp: | |||
| 86 | + | // Coming from below - enter at last pane | |||
| 87 | + | entryIndex = len(n.stackedPanes) - 1 | |||
| 88 | + | default: | |||
| 89 | + | // For horizontal directions, use active pane | |||
| 90 | + | entryIndex = n.activeStackIndex | |||
| 91 | + | if entryIndex < 0 || entryIndex >= len(n.stackedPanes) { | |||
| 92 | + | entryIndex = 0 | |||
| 93 | + | } | |||
| 94 | + | } | |||
| 95 | + | if entryIndex >= 0 && entryIndex < len(n.stackedPanes) { | |||
| 96 | + | walk(n.stackedPanes[entryIndex], depth+1) | |||
| 97 | + | } | |||
| 98 | + | return | |||
| 99 | + | } | |||
| 100 | + | ||||
| 101 | + | // Handle regular split nodes | |||
| 102 | + | walk(n.left, depth+1) | |||
| 103 | + | walk(n.right, depth+1) | |||
| 104 | + | } | |||
| 105 | + | walk(node, 0) | |||
| 106 | + | return leaves | |||
| 107 | + | } | |||
| 108 | + | ||||
| 55 | // collectLeavesFrom collects all leaf nodes from a given subtree | 109 | // collectLeavesFrom collects all leaf nodes from a given subtree | ||
| 56 | func (wm *WorkspaceManager) collectLeavesFrom(node *paneNode) []*paneNode { | 110 | func (wm *WorkspaceManager) collectLeavesFrom(node *paneNode) []*paneNode { | ||
| 57 | var leaves []*paneNode | 111 | var leaves []*paneNode | ||