on main help sign in
fix: realtime session file changes (#2134)
d29d0e2Kujtim Hoxha
internal/ui/model/session.go+63−105
@@ -43,63 +43,68 @@ func (m *UI) loadSession(sessionID string) tea.Cmd {
43 return func() tea.Msg {
43 return func() tea.Msg {
44 session, err := m.com.App.Sessions.Get(context.Background(), sessionID)
44 session, err := m.com.App.Sessions.Get(context.Background(), sessionID)
45 if err != nil {
45 if err != nil {
46− // TODO: better error handling
47− return util.ReportError(err)()
46+ return util.ReportError(err)
48 }
47 }
49
48
50− files, err := m.com.App.History.ListBySession(context.Background(), sessionID)
49+ sessionFiles, err := m.loadSessionFiles(sessionID)
51 if err != nil {
50 if err != nil {
52− // TODO: better error handling
53− return util.ReportError(err)()
51+ return util.ReportError(err)
54 }
52 }
55
53
56− filesByPath := make(map[string][]history.File)
54+ return loadSessionMsg{
57− for _, f := range files {
55+ session: &session,
58− filesByPath[f.Path] = append(filesByPath[f.Path], f)
56+ files: sessionFiles,
59 }
57 }
58+ }
59+}
60
60
61− sessionFiles := make([]SessionFile, 0, len(filesByPath))
61+func (m *UI) loadSessionFiles(sessionID string) ([]SessionFile, error) {
62− for _, versions := range filesByPath {
62+ files, err := m.com.App.History.ListBySession(context.Background(), sessionID)
63− if len(versions) == 0 {
63+ if err != nil {
64− continue
64+ return nil, err
65− }
66−
67− first := versions[0]
68− last := versions[0]
69− for _, v := range versions {
70− if v.Version < first.Version {
71− first = v
72− }
73− if v.Version > last.Version {
74− last = v
75− }
76− }
65+ }
77−
78− _, additions, deletions := diff.GenerateDiff(first.Content, last.Content, first.Path)
79
66
67+ filesByPath := make(map[string][]history.File)
68+ for _, f := range files {
69+ filesByPath[f.Path] = append(filesByPath[f.Path], f)
70+ }
80− sessionFiles = append(sessionFiles, SessionFile{
71+ sessionFiles := make([]SessionFile, 0, len(filesByPath))
81− FirstVersion: first,
72+ for _, versions := range filesByPath {
82− LatestVersion: last,
73+ if len(versions) == 0 {
83− Additions: additions,
74+ continue
84− Deletions: deletions,
85− })
86 }
75 }
87
76
88− slices.SortFunc(sessionFiles, func(a, b SessionFile) int {
77+ first := versions[0]
89− if a.LatestVersion.UpdatedAt > b.LatestVersion.UpdatedAt {
78+ last := versions[0]
90− return -1
79+ for _, v := range versions {
80+ if v.Version < first.Version {
81+ first = v
91 }
82 }
92− if a.LatestVersion.UpdatedAt < b.LatestVersion.UpdatedAt {
83+ if v.Version > last.Version {
93− return 1
84+ last = v
94 }
85 }
95− return 0
86+ }
87+
88+ _, additions, deletions := diff.GenerateDiff(first.Content, last.Content, first.Path)
89+
90+ sessionFiles = append(sessionFiles, SessionFile{
91+ FirstVersion: first,
92+ LatestVersion: last,
93+ Additions: additions,
94+ Deletions: deletions,
96 })
95 })
96+ }
97
97
98+ slices.SortFunc(sessionFiles, func(a, b SessionFile) int {
99+ if a.LatestVersion.UpdatedAt > b.LatestVersion.UpdatedAt {
98− return loadSessionMsg{
100+ return -1
99− session: &session,
100− files: sessionFiles,
101 }
101 }
102+ if a.LatestVersion.UpdatedAt < b.LatestVersion.UpdatedAt {
103+ return 1
104+ }
105+ return 0
102− }
106+ })
107+ return sessionFiles, nil
103}
108}
104
109
105// handleFileEvent processes file change events and updates the session file
110// handleFileEvent processes file change events and updates the session file
@@ -110,59 +115,14 @@ func (m *UI) handleFileEvent(file history.File) tea.Cmd {
110 }
115 }
111
116
112 return func() tea.Msg {
117 return func() tea.Msg {
113− existingIdx := -1
118+ sessionFiles, err := m.loadSessionFiles(m.session.ID)
114− for i, sf := range m.sessionFiles {
119+ // could not load session files
115− if sf.FirstVersion.Path == file.Path {
120+ if err != nil {
116− existingIdx = i
121+ return util.NewErrorMsg(err)
117− break
118− }
119− }
120−
121− if existingIdx == -1 {
122− newFiles := make([]SessionFile, 0, len(m.sessionFiles)+1)
123− newFiles = append(newFiles, SessionFile{
124− FirstVersion: file,
125− LatestVersion: file,
126− Additions: 0,
127− Deletions: 0,
128− })
129− newFiles = append(newFiles, m.sessionFiles...)
130−
131− return loadSessionMsg{
132− session: m.session,
133− files: newFiles,
134− }
135− }
136−
137− updated := m.sessionFiles[existingIdx]
138−
139− if file.Version < updated.FirstVersion.Version {
140− updated.FirstVersion = file
141− }
142−
143− if file.Version > updated.LatestVersion.Version {
144− updated.LatestVersion = file
145− }
146−
147− _, additions, deletions := diff.GenerateDiff(
148− updated.FirstVersion.Content,
149− updated.LatestVersion.Content,
150− updated.FirstVersion.Path,
151− )
152− updated.Additions = additions
153− updated.Deletions = deletions
154−
155− newFiles := make([]SessionFile, 0, len(m.sessionFiles))
156− newFiles = append(newFiles, updated)
157− for i, sf := range m.sessionFiles {
158− if i != existingIdx {
159− newFiles = append(newFiles, sf)
160− }
161 }
122 }
162
123
163− return loadSessionMsg{
124+ return sessionFilesUpdatesMsg{
164− session: m.session,
125+ sessionFiles: sessionFiles,
165− files: newFiles,
166 }
126 }
167 }
127 }
168}
128}
@@ -177,9 +137,15 @@ func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
177 title = common.Section(t, "Modified Files", width)
137 title = common.Section(t, "Modified Files", width)
178 }
138 }
179 list := t.Subtle.Render("None")
139 list := t.Subtle.Render("None")
180−
140+ var filesWithChanges []SessionFile
181− if len(m.sessionFiles) > 0 {
141+ for _, f := range m.sessionFiles {
142+ if f.Additions == 0 && f.Deletions == 0 {
143+ continue
144+ }
145+ filesWithChanges = append(filesWithChanges, f)
146+ }
147+ if len(filesWithChanges) > 0 {
182− list = fileList(t, cwd, m.sessionFiles, width, maxItems)
148+ list = fileList(t, cwd, filesWithChanges, width, maxItems)
183 }
149 }
184
150
185 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
151 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
@@ -187,21 +153,13 @@ func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
187
153
188// fileList renders a list of files with their diff statistics, truncating to
154// fileList renders a list of files with their diff statistics, truncating to
189// maxItems and showing a "...and N more" message if needed.
155// maxItems and showing a "...and N more" message if needed.
190−func fileList(t *styles.Styles, cwd string, files []SessionFile, width, maxItems int) string {
156+func fileList(t *styles.Styles, cwd string, filesWithChanges []SessionFile, width, maxItems int) string {
191 if maxItems <= 0 {
157 if maxItems <= 0 {
192 return ""
158 return ""
193 }
159 }
194 var renderedFiles []string
160 var renderedFiles []string
195 filesShown := 0
161 filesShown := 0
196
162
197− var filesWithChanges []SessionFile
198− for _, f := range files {
199− if f.Additions == 0 && f.Deletions == 0 {
200− continue
201− }
202− filesWithChanges = append(filesWithChanges, f)
203− }
204−
205 for _, f := range filesWithChanges {
163 for _, f := range filesWithChanges {
206 // Skip files with no changes
164 // Skip files with no changes
207 if filesShown >= maxItems {
165 if filesShown >= maxItems {
internal/ui/model/ui.go+7
@@ -109,6 +109,11 @@ type (
109
109
110 // copyChatHighlightMsg is sent to copy the current chat highlight to clipboard.
110 // copyChatHighlightMsg is sent to copy the current chat highlight to clipboard.
111 copyChatHighlightMsg struct{}
111 copyChatHighlightMsg struct{}
112+
113+ // sessionFilesUpdatesMsg is sent when the files for this session have been updated
114+ sessionFilesUpdatesMsg struct {
115+ sessionFiles []SessionFile
116+ }
112)
117)
113
118
114// UI represents the main user interface model.
119// UI represents the main user interface model.
@@ -409,6 +414,8 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
409 m.historyReset()
414 m.historyReset()
410 cmds = append(cmds, m.loadPromptHistory())
415 cmds = append(cmds, m.loadPromptHistory())
411 m.updateLayoutAndSize()
416 m.updateLayoutAndSize()
417+ case sessionFilesUpdatesMsg:
418+ m.sessionFiles = msg.sessionFiles
412
419
413 case sendMessageMsg:
420 case sendMessageMsg:
414 cmds = append(cmds, m.sendMessage(msg.Content, msg.Attachments...))
421 cmds = append(cmds, m.sendMessage(msg.Content, msg.Attachments...))