返回 DeepSeek-Reasonix
rewind_tab.go
根目录 / desktop / rewind_tab.go
1 package main
2
3 import (
4 "strings"
5
6 "reasonix/internal/control"
7 )
8
9 // CommitRewindForTab executes prepare (if planID empty) then commit immediately.
10 func (a *App) CommitRewindForTab(tabID, planID string, turn int, scope string) RewindResultView {
11 tab, ctrl := a.tabAndCtrlByID(tabID)
12 if a.tabIsReadOnly(tab) {
13 return RewindResultView{OK: false, Error: readOnlyChannelErr().Error()}
14 }
15 if ctrl == nil {
16 return RewindResultView{OK: false, Error: "no controller"}
17 }
18 s := control.RewindBoth
19 switch scope {
20 case "code":
21 s = control.RewindCode
22 case "conversation":
23 s = control.RewindConversation
24 }
25 if planID == "" {
26 plan, err := ctrl.PrepareRewind(turn, s)
27 if err != nil {
28 return RewindResultView{OK: false, Error: err.Error()}
29 }
30 // Conversation-only is allowed when its boundary is valid. File scopes
31 // never fall back to the legacy force-restore path.
32 if s == control.RewindConversation {
33 if !plan.CanConversation {
34 return RewindResultView{OK: false, Error: nonEmptyStr(plan.DisabledReason, "conversation rewind unavailable")}
35 }
36 } else if !plan.CanFiles {
37 return RewindResultView{OK: false, Error: nonEmptyStr(plan.DisabledReason, "file rewind unavailable"), Conflicts: conflictStrings(plan), Coverage: string(plan.Coverage)}
38 }
39 planID = plan.PlanID
40 }
41 // Conversation rewinds always publish an independent child session. The
42 // source tab and source event log stay unchanged and remain available as
43 // history; execution ownership is attached only after the child is complete.
44 result, err := ctrl.CommitRewind(planID)
45 view := rewindResultToView(result)
46 if err != nil {
47 view.OK = false
48 if view.Error == "" {
49 view.Error = err.Error()
50 }
51 return view
52 }
53 if view.OK && view.ConversationForked && strings.TrimSpace(view.Branch) != "" && tab != nil {
54 view = a.attachForkedRewindTab(tab, view)
55 }
56 return view
57 }
58
59 // UndoRewindForTab undoes the last successful rewind on the tab when available.
60 func (a *App) UndoRewindForTab(tabID, transactionID string) RewindResultView {
61 tab, ctrl := a.tabAndCtrlByID(tabID)
62 if a.tabIsReadOnly(tab) {
63 return RewindResultView{OK: false, Error: readOnlyChannelErr().Error()}
64 }
65 if ctrl == nil {
66 return RewindResultView{OK: false, Error: "no controller"}
67 }
68 before, _ := ctrl.SessionHead()
69 result, err := ctrl.UndoRewind(transactionID)
70 view := rewindResultToView(result)
71 if err != nil {
72 view.OK = false
73 if view.Error == "" {
74 view.Error = err.Error()
75 }
76 }
77 if after, ok := ctrl.SessionHead(); ok && after.HeadID != before.HeadID && tab != nil {
78 meta := a.tabMetaAfterHeadSwitch(tab)
79 view.TabID, view.Tab = meta.ID, &meta
80 }
81 return view
82 }
83
83 lines GO