pane.go RawHistoryFolder
1// Package entity contains domain entities representing core business concepts.
2// These entities are pure Go types with no infrastructure dependencies.
3package entity
4
5import "time"
6
7// PaneID uniquely identifies a pane within the browser.
8type PaneID string
9
10// SplitDirection indicates how a pane container splits its children.
11type SplitDirection int
12
13const (
14 SplitNone SplitDirection = iota // Leaf node or stacked container
15 SplitHorizontal // Left/right split
16 SplitVertical // Top/bottom split
17)
18
19// WindowType indicates the type of browser window.
20type WindowType int
21
22const (
23 WindowMain WindowType = iota // Regular browser tab
24 WindowPopup // Popup window (OAuth, feature-restricted)
25)
26
27// Pane represents a single browsing context (a WebView container).
28// This is the leaf-level entity that holds navigation state.
29type Pane struct {
30 ID PaneID
31 URI string
32 Title string
33 FaviconURL string
34 WindowType WindowType
35 ZoomFactor float64
36 CanGoBack bool
37 CanForward bool
38 IsLoading bool
39 CreatedAt time.Time
40
41 // Popup-specific fields
42 IsRelated bool // Shares context with parent
43 ParentPaneID *PaneID // Parent pane if this is a related popup
44 AutoClose bool // Auto-close on OAuth success
45 RequestID string // Request ID for popup tracking
46}
47
48// NewPane creates a new pane with default values.
49func NewPane(id PaneID) *Pane {
50 return &Pane{
51 ID: id,
52 WindowType: WindowMain,
53 ZoomFactor: 1.0,
54 CreatedAt: time.Now(),
55 }
56}
57
58// PaneNode represents a node in the workspace pane tree structure.
59// It can be either:
60// - Leaf node: Contains a single Pane
61// - Split node: Contains two children (left/right or top/bottom)
62// - Stacked node: Contains multiple panes in a tabbed view
63type PaneNode struct {
64 ID string
65 Pane *Pane // Non-nil for leaf nodes
66 Parent *PaneNode // nil for root
67 Children []*PaneNode
68
69 // Layout
70 SplitDir SplitDirection
71 SplitRatio float64 // 0.0-1.0, position of divider
72
73 // Stacked panes (alternative to split)
74 IsStacked bool
75 ActiveStackIndex int
76}
77
78// IsLeaf returns true if this node contains a pane (no children).
79func (n *PaneNode) IsLeaf() bool {
80 return n.Pane != nil && len(n.Children) == 0 && !n.IsStacked
81}
82
83// IsContainer returns true if this is a split or stacked container.
84func (n *PaneNode) IsContainer() bool {
85 return !n.IsLeaf()
86}
87
88// IsSplit returns true if this node splits into two children.
89func (n *PaneNode) IsSplit() bool {
90 return n.SplitDir != SplitNone && len(n.Children) == 2
91}
92
93// Left returns the left/top child in a split node.
94func (n *PaneNode) Left() *PaneNode {
95 if len(n.Children) > 0 {
96 return n.Children[0]
97 }
98 return nil
99}
100
101// Right returns the right/bottom child in a split node.
102func (n *PaneNode) Right() *PaneNode {
103 if len(n.Children) > 1 {
104 return n.Children[1]
105 }
106 return nil
107}
108
109// StackedPanes returns the list of panes if this is a stacked container.
110func (n *PaneNode) StackedPanes() []*PaneNode {
111 if n.IsStacked {
112 return n.Children
113 }
114 return nil
115}
116
117// ActivePane returns the currently visible pane in a stacked container.
118func (n *PaneNode) ActivePane() *PaneNode {
119 if n.IsStacked && n.ActiveStackIndex >= 0 && n.ActiveStackIndex < len(n.Children) {
120 return n.Children[n.ActiveStackIndex]
121 }
122 return nil
123}
124
125// Walk traverses the tree calling fn for each node. Returns early if fn returns false.
126func (n *PaneNode) Walk(fn func(*PaneNode) bool) {
127 if !fn(n) {
128 return
129 }
130 for _, child := range n.Children {
131 child.Walk(fn)
132 }
133}
134
135// FindPane searches the tree for a pane with the given ID.
136func (n *PaneNode) FindPane(id PaneID) *PaneNode {
137 var found *PaneNode
138 n.Walk(func(node *PaneNode) bool {
139 if node.Pane != nil && node.Pane.ID == id {
140 found = node
141 return false
142 }
143 return true
144 })
145 return found
146}
147
148// LeafCount returns the number of leaf nodes (panes) in the tree.
149func (n *PaneNode) LeafCount() int {
150 count := 0
151 n.Walk(func(node *PaneNode) bool {
152 if node.IsLeaf() {
153 count++
154 }
155 return true
156 })
157 return count
158}
159
160// VisibleAreaCount returns the number of visible pane areas in the tree.
161// A stacked node counts as 1 visible area (only one pane visible at a time).
162// A leaf node counts as 1 visible area.
163// A split node's visible areas are the sum of its children's visible areas.
164func (n *PaneNode) VisibleAreaCount() int {
165 switch {
166 case n.IsLeaf():
167 return 1
168 case n.IsStacked:
169 // Stacked panes: only one is visible at a time
170 return 1
171 case n.IsSplit():
172 // Split: sum of both children's visible areas
173 count := 0
174 if left := n.Left(); left != nil {
175 count += left.VisibleAreaCount()
176 }
177 if right := n.Right(); right != nil {
178 count += right.VisibleAreaCount()
179 }
180 return count
181 default:
182 // Fallback for other container types
183 if len(n.Children) == 0 {
184 return 1
185 }
186 count := 0
187 for _, child := range n.Children {
188 count += child.VisibleAreaCount()
189 }
190 return count
191 }
192}