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}