返回 DeepSeek-Reasonix
session_history_listing.go
根目录 / desktop / session_history_listing.go
1 package main
2
3 import (
4 "reasonix/internal/config"
5 "reasonix/internal/sessioncatalog"
6 "sort"
7 )
8
9 // ListSessions returns the saved sessions newest-first for the history panel,
10 // marking the one the current conversation is writing to and attaching any
11 // user-chosen titles.
12 func (a *App) ListSessions() []SessionMeta {
13 dir := a.activeSessionDir()
14 active := a.activeSessionPath(dir)
15 if tab := a.activeTab(); tab != nil {
16 active = tab.currentSessionIdentity()
17 }
18 return a.listSessionsFromDir(dir, active)
19 }
20
21 // ListSessionsForTab returns sessions from the directory owned by tabID. Task
22 // Monitor uses this stable target after asynchronous control lookups so a tab
23 // switch cannot redirect the eventual session lookup to another workspace.
24 func (a *App) ListSessionsForTab(tabID string) []SessionMeta {
25 target, err := a.taskMonitorTargetForTab(tabID)
26 if err != nil {
27 return []SessionMeta{}
28 }
29 active := target.sessionPath
30 if tab := a.tabByID(tabID); tab != nil {
31 active = tab.currentSessionIdentity()
32 }
33 return a.listSessionsFromDir(target.sessionDir, active)
34 }
35
36 func (a *App) listSessionsFromDir(dir, active string) []SessionMeta {
37 v3 := a.listCanonicalSessionsFromDir(dir, active)
38 state, stateErr := a.workspaceRegistry().Load(a.bootContext())
39 if stateErr != nil {
40 return v3
41 }
42 scope, root := "", ""
43 if sameDesktopPath(dir, config.SessionDir()) || sameDesktopPath(dir, desktopSessionDir(globalWorkspaceRoot())) {
44 scope = "global"
45 } else {
46 for _, target := range a.sessionCatalogTargets() {
47 if sameDesktopPath(dir, target.Path) {
48 scope, root = target.Scope, target.WorkspaceRoot
49 break
50 }
51 }
52 }
53 historical := a.historicalCanonicalTopics(scope, root, state)
54 saved, _ := readHistoricalSidecar()
55 applyHistoricalPresentations(historical, saved)
56 for _, node := range historical {
57 v3 = append(v3, SessionMeta{Source: node.Source, Historical: true, PreparationStatus: node.PreparationStatus,
58 Path: node.SessionPath, Title: node.Label, TopicID: node.TopicID, Scope: scope, WorkspaceRoot: root,
59 Preview: node.Preview, Turns: node.Turns, TurnsState: node.TurnsState,
60 CreatedAt: node.CreatedAt, LastActivityAt: node.LastActivityAt, ModTime: node.LastActivityAt})
61 }
62 adopted := map[string]bool{}
63 for _, mapping := range state.SourceMappings {
64 adopted["source\x00local\x00"+mapping.SourceKey] = true
65 if sourceMappingHasPathAlias(mapping) {
66 adopted[sessionRuntimeKey(mapping.Path)] = true
67 }
68 }
69 catalog := a.sessionCatalog.Load()
70 if catalog == nil {
71 return v3
72 }
73 target := sessioncatalog.DirectoryTarget{Path: dir, Scope: "global"}
74 for _, candidate := range a.sessionCatalogTargets() {
75 if sameProjectRoot(candidate.Path, dir) {
76 target = candidate
77 break
78 }
79 }
80 records, err := listCatalogSessionsForDirectory(a.bootContext(), catalog, target, dir)
81 if err != nil {
82 return v3
83 }
84 open := a.openSessionPaths(dir)
85 channelRoutes := channelSessionRoutesForDir(dir)
86 out := make([]SessionMeta, 0, len(records)+len(v3))
87 out = append(out, v3...)
88 for _, record := range records {
89 if adopted[sessionRuntimeKey(record.Path)] {
90 continue
91 }
92 _, isOpen := open[record.Path]
93 meta := sessionMetaFromCatalog(record, record.Path == active, isOpen)
94 if route, ok := channelRoutes[sessionRuntimeKey(record.Path)]; ok {
95 applyChannelSessionRoute(&meta, route)
96 }
97 for _, row := range expandSessionSourceRows(ProjectNode{SessionPath: record.Path}) {
98 if row.Source == nil {
99 out = append(out, meta)
100 continue
101 }
102 if adopted[projectNodeSessionKey(row)] {
103 continue
104 }
105 headMeta := meta
106 headMeta.Source = row.Source
107 headMeta.Historical, headMeta.HistoricalBranch = true, row.HistoricalBranch
108 headMeta.PreparationStatus = a.historicalPreparationStatus(row.Source.SourceKey)
109 if presentation := saved.Presentations[row.Source.SourceKey]; presentation.Title != "" {
110 headMeta.Title = presentation.Title
111 }
112 headMeta.Turns, headMeta.Preview = row.Turns, row.Preview
113 if row.LastActivityAt > 0 {
114 headMeta.LastActivityAt, headMeta.ModTime = row.LastActivityAt, row.LastActivityAt
115 }
116 if row.CreatedAt > 0 {
117 headMeta.CreatedAt = row.CreatedAt
118 }
119 headMeta.Current = sessionRuntimeKey(headMeta.Path) == sessionRuntimeKey(active)
120 out = append(out, headMeta)
121 }
122 }
123 sort.SliceStable(out, func(i, j int) bool { return out[i].LastActivityAt > out[j].LastActivityAt })
124 return out
125 }
126
126 lines GO