v0.26.1 75cd777

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close