From d29d0e21216eb5bb68bce18134e1fba8d2d7d362 Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Thu, 5 Feb 2026 14:06:43 +0100 Subject: [PATCH] fix: realtime session file changes (#2134) --- internal/ui/model/session.go | 168 +++++++++++++---------------------- internal/ui/model/ui.go | 7 ++ 2 files changed, 70 insertions(+), 105 deletions(-) diff --git a/internal/ui/model/session.go b/internal/ui/model/session.go index 5b38da8b0d042486b19060047d2c715d514aef82..1438d0a914556574d513d3606bb1481cde008709 100644 --- a/internal/ui/model/session.go +++ b/internal/ui/model/session.go @@ -43,63 +43,68 @@ func (m *UI) loadSession(sessionID string) tea.Cmd { return func() tea.Msg { session, err := m.com.App.Sessions.Get(context.Background(), sessionID) if err != nil { - // TODO: better error handling - return util.ReportError(err)() + return util.ReportError(err) } - files, err := m.com.App.History.ListBySession(context.Background(), sessionID) + sessionFiles, err := m.loadSessionFiles(sessionID) if err != nil { - // TODO: better error handling - return util.ReportError(err)() + return util.ReportError(err) } - filesByPath := make(map[string][]history.File) - for _, f := range files { - filesByPath[f.Path] = append(filesByPath[f.Path], f) + return loadSessionMsg{ + session: &session, + files: sessionFiles, } + } +} - sessionFiles := make([]SessionFile, 0, len(filesByPath)) - for _, versions := range filesByPath { - if len(versions) == 0 { - continue - } - - first := versions[0] - last := versions[0] - for _, v := range versions { - if v.Version < first.Version { - first = v - } - if v.Version > last.Version { - last = v - } - } - - _, additions, deletions := diff.GenerateDiff(first.Content, last.Content, first.Path) +func (m *UI) loadSessionFiles(sessionID string) ([]SessionFile, error) { + files, err := m.com.App.History.ListBySession(context.Background(), sessionID) + if err != nil { + return nil, err + } - sessionFiles = append(sessionFiles, SessionFile{ - FirstVersion: first, - LatestVersion: last, - Additions: additions, - Deletions: deletions, - }) + filesByPath := make(map[string][]history.File) + for _, f := range files { + filesByPath[f.Path] = append(filesByPath[f.Path], f) + } + sessionFiles := make([]SessionFile, 0, len(filesByPath)) + for _, versions := range filesByPath { + if len(versions) == 0 { + continue } - slices.SortFunc(sessionFiles, func(a, b SessionFile) int { - if a.LatestVersion.UpdatedAt > b.LatestVersion.UpdatedAt { - return -1 + first := versions[0] + last := versions[0] + for _, v := range versions { + if v.Version < first.Version { + first = v } - if a.LatestVersion.UpdatedAt < b.LatestVersion.UpdatedAt { - return 1 + if v.Version > last.Version { + last = v } - return 0 + } + + _, additions, deletions := diff.GenerateDiff(first.Content, last.Content, first.Path) + + sessionFiles = append(sessionFiles, SessionFile{ + FirstVersion: first, + LatestVersion: last, + Additions: additions, + Deletions: deletions, }) + } - return loadSessionMsg{ - session: &session, - files: sessionFiles, + slices.SortFunc(sessionFiles, func(a, b SessionFile) int { + if a.LatestVersion.UpdatedAt > b.LatestVersion.UpdatedAt { + return -1 } - } + if a.LatestVersion.UpdatedAt < b.LatestVersion.UpdatedAt { + return 1 + } + return 0 + }) + return sessionFiles, nil } // handleFileEvent processes file change events and updates the session file @@ -110,59 +115,14 @@ func (m *UI) handleFileEvent(file history.File) tea.Cmd { } return func() tea.Msg { - existingIdx := -1 - for i, sf := range m.sessionFiles { - if sf.FirstVersion.Path == file.Path { - existingIdx = i - break - } - } - - if existingIdx == -1 { - newFiles := make([]SessionFile, 0, len(m.sessionFiles)+1) - newFiles = append(newFiles, SessionFile{ - FirstVersion: file, - LatestVersion: file, - Additions: 0, - Deletions: 0, - }) - newFiles = append(newFiles, m.sessionFiles...) - - return loadSessionMsg{ - session: m.session, - files: newFiles, - } - } - - updated := m.sessionFiles[existingIdx] - - if file.Version < updated.FirstVersion.Version { - updated.FirstVersion = file - } - - if file.Version > updated.LatestVersion.Version { - updated.LatestVersion = file - } - - _, additions, deletions := diff.GenerateDiff( - updated.FirstVersion.Content, - updated.LatestVersion.Content, - updated.FirstVersion.Path, - ) - updated.Additions = additions - updated.Deletions = deletions - - newFiles := make([]SessionFile, 0, len(m.sessionFiles)) - newFiles = append(newFiles, updated) - for i, sf := range m.sessionFiles { - if i != existingIdx { - newFiles = append(newFiles, sf) - } + sessionFiles, err := m.loadSessionFiles(m.session.ID) + // could not load session files + if err != nil { + return util.NewErrorMsg(err) } - return loadSessionMsg{ - session: m.session, - files: newFiles, + return sessionFilesUpdatesMsg{ + sessionFiles: sessionFiles, } } } @@ -177,9 +137,15 @@ func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string { title = common.Section(t, "Modified Files", width) } list := t.Subtle.Render("None") - - if len(m.sessionFiles) > 0 { - list = fileList(t, cwd, m.sessionFiles, width, maxItems) + var filesWithChanges []SessionFile + for _, f := range m.sessionFiles { + if f.Additions == 0 && f.Deletions == 0 { + continue + } + filesWithChanges = append(filesWithChanges, f) + } + if len(filesWithChanges) > 0 { + list = fileList(t, cwd, filesWithChanges, width, maxItems) } 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 { // fileList renders a list of files with their diff statistics, truncating to // maxItems and showing a "...and N more" message if needed. -func fileList(t *styles.Styles, cwd string, files []SessionFile, width, maxItems int) string { +func fileList(t *styles.Styles, cwd string, filesWithChanges []SessionFile, width, maxItems int) string { if maxItems <= 0 { return "" } var renderedFiles []string filesShown := 0 - var filesWithChanges []SessionFile - for _, f := range files { - if f.Additions == 0 && f.Deletions == 0 { - continue - } - filesWithChanges = append(filesWithChanges, f) - } - for _, f := range filesWithChanges { // Skip files with no changes if filesShown >= maxItems { diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index 65d1e720cd91d50c87d57f5409a42595915c1e40..a01fafc2905f84e31fce9ce1914bdd8274e26ad4 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -109,6 +109,11 @@ type ( // copyChatHighlightMsg is sent to copy the current chat highlight to clipboard. copyChatHighlightMsg struct{} + + // sessionFilesUpdatesMsg is sent when the files for this session have been updated + sessionFilesUpdatesMsg struct { + sessionFiles []SessionFile + } ) // UI represents the main user interface model. @@ -409,6 +414,8 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.historyReset() cmds = append(cmds, m.loadPromptHistory()) m.updateLayoutAndSize() + case sessionFilesUpdatesMsg: + m.sessionFiles = msg.sessionFiles case sendMessageMsg: cmds = append(cmds, m.sendMessage(msg.Content, msg.Attachments...))