window_state.go RawHistoryFolder

1package entity
2
3import "time"
4
5// WindowID uniquely identifies a browser window.
6type WindowID string
7
8// WindowSnapshot captures the state of a single browser window.
9type WindowSnapshot struct {
10	ID             WindowID      `json:"id"`
11	Tabs           []TabSnapshot `json:"tabs"`
12	ActiveTabIndex int           `json:"active_tab_index"`
13}
14
15// WindowTabListState pairs a window ID with its live TabList.
16type WindowTabListState struct {
17	WindowID WindowID
18	Tabs     *TabList
19}
20
21// SnapshotFromWindowTabLists creates a v2 SessionState from ordered windows.
22func SnapshotFromWindowTabLists(sessionID SessionID, windows []WindowTabListState, activeWindowIndex int, savedAt time.Time) *SessionState {
23	// Clamp active window index
24	if activeWindowIndex < 0 || activeWindowIndex >= len(windows) {
25		activeWindowIndex = 0
26	}
27
28	snapWindows := make([]WindowSnapshot, 0, len(windows))
29	for _, w := range windows {
30		snapWindows = append(snapWindows, windowToSnapshot(w.WindowID, w.Tabs))
31	}
32
33	return &SessionState{
34		Version:           SessionStateVersion,
35		SessionID:         sessionID,
36		Windows:           snapWindows,
37		ActiveWindowIndex: activeWindowIndex,
38		SavedAt:           savedAt,
39	}
40}
41
42// windowToSnapshot converts a live TabList to a WindowSnapshot.
43func windowToSnapshot(id WindowID, tabs *TabList) WindowSnapshot {
44	if tabs == nil {
45		return WindowSnapshot{
46			ID:   id,
47			Tabs: []TabSnapshot{},
48		}
49	}
50
51	snapTabs := make([]TabSnapshot, 0, len(tabs.Tabs))
52	activeTabIndex := 0
53
54	for i, tab := range tabs.Tabs {
55		if tab.ID == tabs.ActiveTabID {
56			activeTabIndex = i
57		}
58		snapTabs = append(snapTabs, snapshotTab(tab))
59	}
60
61	return WindowSnapshot{
62		ID:             id,
63		Tabs:           snapTabs,
64		ActiveTabIndex: activeTabIndex,
65	}
66}
67
68// WindowTabListsFromSnapshot restores window/tab lists from a SessionState.
69// For v2 states (with Windows), each WindowSnapshot is restored independently.
70// For legacy v1 states (flat Tabs), returns a single window with empty WindowID.
71func WindowTabListsFromSnapshot(state *SessionState, idGen IDGenerator) []WindowTabListState {
72	if state == nil {
73		return nil
74	}
75
76	// v2+: restore from Windows, including valid empty-window snapshots.
77	if state.Version >= SessionStateVersion {
78		result := make([]WindowTabListState, 0, len(state.Windows))
79		for _, wSnap := range state.Windows {
80			tabs := tabListFromWindowSnapshot(&wSnap, idGen)
81			result = append(result, WindowTabListState{
82				WindowID: wSnap.ID,
83				Tabs:     tabs,
84			})
85		}
86		return result
87	}
88
89	// v1: legacy flat tabs wrapped in a single window
90	tabs := TabListFromSnapshot(state, idGen)
91	return []WindowTabListState{
92		{WindowID: "", Tabs: tabs},
93	}
94}
95
96// tabListFromWindowSnapshot converts a single WindowSnapshot to a live TabList.
97func tabListFromWindowSnapshot(snap *WindowSnapshot, idGen IDGenerator) *TabList {
98	if snap == nil {
99		return NewTabList()
100	}
101
102	return tabListFromSnapshots(snap.Tabs, snap.ActiveTabIndex, idGen)
103}

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