返回 DeepSeek-Reasonix
session_topic_open.go
根目录 / desktop / session_topic_open.go
1 package main
2
3 import "reasonix/internal/session"
4
5 // Resolve the durable identity before publishing a readable topic surface.
6 // Runtime ownership and controller startup belong to the existing tab build,
7 // not the synchronous phase of sidebar navigation.
8 func (a *App) canonicalTopicOpen(path string) (SessionTarget, *canonicalTabBinding, error) {
9 id, ok := parseSessionRoute(path)
10 if !ok {
11 return SessionTarget{}, nil, nil
12 }
13 ref := session.SessionRef{HostID: localDesktopHostID, SessionID: id}
14 target, err := a.resolveCanonicalSessionTarget(ref, "")
15 if err != nil {
16 return SessionTarget{}, nil, err
17 }
18 workspace, err := a.canonicalSessionWorkspace(a.bootContext(), ref)
19 if err != nil {
20 return SessionTarget{}, nil, err
21 }
22 state, err := a.workspaceRegistry().Load(a.bootContext())
23 if err != nil {
24 return SessionTarget{}, nil, err
25 }
26 target.Scope, target.WorkspaceRoot = canonicalWorkspaceScope(workspace), workspace.Root
27 if target.Scope == "global" {
28 target.WorkspaceRoot = ""
29 }
30 title, source := a.canonicalTabTitle(a.bootContext(), state, ref)
31 return target, &canonicalTabBinding{ref: ref, workspaceID: workspace.ID, title: title, titleSource: source}, nil
32 }
33
34 // A cancelled startup may still be unwinding when its session is selected
35 // again. Reusing that shell would wait on a build that can no longer publish.
36 // Leave it to its own completion and let the normal open path create a fresh
37 // surface for the same durable session. Callers hold App.mu.
38 func topicTabReusableLocked(tab *WorkspaceTab) bool {
39 return tab != nil && !tab.removed && (tab.Ctrl != nil || tab.buildDoneGen == 0 || tab.buildDoneGen == tab.buildGeneration)
40 }
41
42 // newTopicTabLocked constructs the identity and profile before any runtime is
43 // started, so history readers can address the selected session immediately.
44 func (a *App) newTopicTabLocked(scope, workspaceRoot, actualRoot, topicID, sessionPath string, canonical *canonicalTabBinding) *WorkspaceTab {
45 topicTitle, topicSource := "", ""
46 if canonical != nil {
47 topicTitle, topicSource = canonical.title, canonical.titleSource
48 } else {
49 topicTitle = topicTitleForTab(scope, workspaceRoot, topicID)
50 if title, source, ok := topicTitleFallbackForOpen(workspaceRoot, topicID, sessionPath); ok {
51 topicTitle = title
52 _ = setTopicTitleWithSource(workspaceRoot, topicID, title, source)
53 }
54 topicSource = loadTopicTitleSource(topicTitleRoot(scope, workspaceRoot), topicID)
55 }
56 profile := defaultTabSessionProfile()
57 if sessionPath != "" && canonical == nil {
58 profile = loadTabSessionProfile(sessionPath)
59 }
60 tab := &WorkspaceTab{
61 ID: a.newUniqueTabIDLocked(),
62 Scope: scope,
63 WorkspaceRoot: actualRoot,
64 TopicID: topicID,
65 TopicTitle: topicTitle,
66 topicTitleSource: topicSource,
67 SessionPath: sessionPath,
68 disabledMCP: map[string]ServerView{},
69 }
70 // Existing canonical sessions publish their identity now. New topics are
71 // bound by the controller build without creating an empty legacy log.
72 if canonical != nil {
73 canonical.applyLocked(tab)
74 }
75 applyTabSessionProfile(tab, profile)
76 tab.sink = &tabEventSink{tabID: tab.ID, app: a}
77 return tab
78 }
79
79 lines GO