on main help sign in
fix: realtime session file changes (#2134)
commitd29d0e21216eb5bb68bce18134e1fba8d2d7d362 authorKujtim Hoxha <kujtimii.h@gmail.com> dateThu 5 Feb 2026, 14:06 (8 months ago) parent8adfe70
2 files changed+70−105
internal/ui/model/session.go+63−105 internal/ui/model/ui.go+7
internal/ui/model/session.go+63−105
@@ -43,63 +43,68 @@ func (m *UI) loadSession(sessionID string) tea.Cmd {
4343 return func() tea.Msg {
4444 session, err := m.com.App.Sessions.Get(context.Background(), sessionID)
4545 if err != nil {
46− // TODO: better error handling
47− return util.ReportError(err)()
46+ return util.ReportError(err)
4847 }
4948
50− files, err := m.com.App.History.ListBySession(context.Background(), sessionID)
49+ sessionFiles, err := m.loadSessionFiles(sessionID)
5150 if err != nil {
52− // TODO: better error handling
53− return util.ReportError(err)()
51+ return util.ReportError(err)
5452 }
5553
56− filesByPath := make(map[string][]history.File)
57− for _, f := range files {
58− filesByPath[f.Path] = append(filesByPath[f.Path], f)
54+ return loadSessionMsg{
55+ session: &session,
56+ files: sessionFiles,
5957 }
58+ }
59+}
6060
61− sessionFiles := make([]SessionFile, 0, len(filesByPath))
62− for _, versions := range filesByPath {
63− if len(versions) == 0 {
64− continue
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− }
77−
78− _, additions, deletions := diff.GenerateDiff(first.Content, last.Content, first.Path)
61+func (m *UI) loadSessionFiles(sessionID string) ([]SessionFile, error) {
62+ files, err := m.com.App.History.ListBySession(context.Background(), sessionID)
63+ if err != nil {
64+ return nil, err
65+ }
7966
80− sessionFiles = append(sessionFiles, SessionFile{
81− FirstVersion: first,
82− LatestVersion: last,
83− Additions: additions,
84− Deletions: deletions,
85− })
67+ filesByPath := make(map[string][]history.File)
68+ for _, f := range files {
69+ filesByPath[f.Path] = append(filesByPath[f.Path], f)
70+ }
71+ sessionFiles := make([]SessionFile, 0, len(filesByPath))
72+ for _, versions := range filesByPath {
73+ if len(versions) == 0 {
74+ continue
8675 }
8776
88− slices.SortFunc(sessionFiles, func(a, b SessionFile) int {
89− if a.LatestVersion.UpdatedAt > b.LatestVersion.UpdatedAt {
90− return -1
77+ first := versions[0]
78+ last := versions[0]
79+ for _, v := range versions {
80+ if v.Version < first.Version {
81+ first = v
9182 }
92− if a.LatestVersion.UpdatedAt < b.LatestVersion.UpdatedAt {
93− return 1
83+ if v.Version > last.Version {
84+ last = v
9485 }
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,
9695 })
96+ }
9797
98− return loadSessionMsg{
99− session: &session,
100− files: sessionFiles,
98+ slices.SortFunc(sessionFiles, func(a, b SessionFile) int {
99+ if a.LatestVersion.UpdatedAt > b.LatestVersion.UpdatedAt {
100+ return -1
101101 }
102− }
102+ if a.LatestVersion.UpdatedAt < b.LatestVersion.UpdatedAt {
103+ return 1
104+ }
105+ return 0
106+ })
107+ return sessionFiles, nil
103108}
104109
105110// handleFileEvent processes file change events and updates the session file
@@ -110,59 +115,14 @@ func (m *UI) handleFileEvent(file history.File) tea.Cmd {
110115 }
111116
112117 return func() tea.Msg {
113− existingIdx := -1
114− for i, sf := range m.sessionFiles {
115− if sf.FirstVersion.Path == file.Path {
116− existingIdx = i
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− }
118+ sessionFiles, err := m.loadSessionFiles(m.session.ID)
119+ // could not load session files
120+ if err != nil {
121+ return util.NewErrorMsg(err)
161122 }
162123
163− return loadSessionMsg{
164− session: m.session,
165− files: newFiles,
124+ return sessionFilesUpdatesMsg{
125+ sessionFiles: sessionFiles,
166126 }
167127 }
168128}
@@ -177,9 +137,15 @@ func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
177137 title = common.Section(t, "Modified Files", width)
178138 }
179139 list := t.Subtle.Render("None")
180−
181− if len(m.sessionFiles) > 0 {
182− list = fileList(t, cwd, m.sessionFiles, width, maxItems)
140+ var filesWithChanges []SessionFile
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 {
148+ list = fileList(t, cwd, filesWithChanges, width, maxItems)
183149 }
184150
185151 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 {
187153
188154// fileList renders a list of files with their diff statistics, truncating to
189155// 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 {
191157 if maxItems <= 0 {
192158 return ""
193159 }
194160 var renderedFiles []string
195161 filesShown := 0
196162
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−
205163 for _, f := range filesWithChanges {
206164 // Skip files with no changes
207165 if filesShown >= maxItems {
internal/ui/model/ui.go+7
@@ -109,6 +109,11 @@ type (
109109
110110 // copyChatHighlightMsg is sent to copy the current chat highlight to clipboard.
111111 copyChatHighlightMsg struct{}
112+
113+ // sessionFilesUpdatesMsg is sent when the files for this session have been updated
114+ sessionFilesUpdatesMsg struct {
115+ sessionFiles []SessionFile
116+ }
112117)
113118
114119// UI represents the main user interface model.
@@ -409,6 +414,8 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
409414 m.historyReset()
410415 cmds = append(cmds, m.loadPromptHistory())
411416 m.updateLayoutAndSize()
417+ case sessionFilesUpdatesMsg:
418+ m.sessionFiles = msg.sessionFiles
412419
413420 case sendMessageMsg:
414421 cmds = append(cmds, m.sendMessage(msg.Content, msg.Attachments...))