Skip to content

Commit 69dc757

Browse files
authored
feat: render agent notes inline across review stream (#69)
* feat: render agent notes inline across review stream * chore: format inline note follow-up
1 parent f7e332b commit 69dc757

9 files changed

Lines changed: 201 additions & 211 deletions

src/ui/App.tsx

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import { FilesPane } from "./components/panes/FilesPane";
3333
import { PaneDivider } from "./components/panes/PaneDivider";
3434
import { useHunkSessionBridge } from "./hooks/useHunkSessionBridge";
3535
import { useMenuController } from "./hooks/useMenuController";
36-
import { getSelectedAnnotations } from "./lib/agentAnnotations";
3736
import { buildAppMenus } from "./lib/appMenus";
3837
import { buildFileListEntry } from "./lib/files";
3938
import { buildHunkCursors, findNextHunkCursor } from "./lib/hunks";
@@ -94,7 +93,6 @@ function AppShell({
9493
const [filesPaneWidth, setFilesPaneWidth] = useState(34);
9594
const [resizeDragOriginX, setResizeDragOriginX] = useState<number | null>(null);
9695
const [resizeStartWidth, setResizeStartWidth] = useState<number | null>(null);
97-
const [dismissedAgentNoteIds, setDismissedAgentNoteIds] = useState<string[]>([]);
9896
const [selectedFileId, setSelectedFileId] = useState(bootstrap.changeset.files[0]?.id ?? "");
9997
const [selectedHunkIndex, setSelectedHunkIndex] = useState(0);
10098
const deferredFilter = useDeferredValue(filter);
@@ -109,7 +107,6 @@ function AppShell({
109107
}, []);
110108

111109
const openAgentNotes = useCallback(() => {
112-
setDismissedAgentNoteIds([]);
113110
setShowAgentNotes(true);
114111
}, []);
115112

@@ -176,8 +173,6 @@ function AppShell({
176173
(responsiveLayout.showFilesPane || (forceSidebarOpen && canForceShowFilesPane));
177174
const centerWidth = bodyWidth;
178175
const resolvedLayout = responsiveLayout.layout;
179-
const currentHunk = selectedFile?.metadata.hunks[selectedHunkIndex];
180-
const activeAnnotations = getSelectedAnnotations(selectedFile, currentHunk);
181176
const availableCenterWidth = showFilesPane
182177
? Math.max(0, centerWidth - DIVIDER_WIDTH)
183178
: Math.max(0, centerWidth);
@@ -247,11 +242,6 @@ function AppShell({
247242
filesScrollRef.current?.scrollChildIntoView(fileRowId(selectedFile.id));
248243
}, [selectedFile]);
249244

250-
useEffect(() => {
251-
// Dismissed notes are hunk-local, so reset them when the review focus moves.
252-
setDismissedAgentNoteIds([]);
253-
}, [selectedFile?.id, selectedHunkIndex]);
254-
255245
/** Move the review focus across hunks in stream order. */
256246
const moveHunk = (delta: number) => {
257247
const nextCursor = findNextHunkCursor(hunkCursors, selectedFile?.id, selectedHunkIndex, delta);
@@ -287,20 +277,9 @@ function AppShell({
287277
jumpToFile(nextFile.id);
288278
};
289279

290-
/** Toggle the note layer while keeping dismissals scoped to the visible hunk. */
280+
/** Toggle the global agent note layer on or off. */
291281
const toggleAgentNotes = () => {
292-
if (showAgentNotes) {
293-
setShowAgentNotes(false);
294-
setDismissedAgentNoteIds([]);
295-
return;
296-
}
297-
298-
openAgentNotes();
299-
};
300-
301-
/** Hide one visible note card until the selection changes. */
302-
const dismissAgentNote = (noteId: string) => {
303-
setDismissedAgentNoteIds((current) => [...current, noteId]);
282+
setShowAgentNotes((current) => !current);
304283
};
305284

306285
/** Toggle line-number gutters without changing the diff content itself. */
@@ -337,10 +316,9 @@ function AppShell({
337316
setShowHunkHeaders((current) => !current);
338317
};
339318

340-
/** Jump to the annotated hunk before opening the note layer. */
319+
/** Jump to an annotated hunk without changing the global note visibility toggle. */
341320
const openAgentNotesAtHunk = (fileId: string, hunkIndex: number) => {
342321
jumpToFile(fileId, hunkIndex);
343-
openAgentNotes();
344322
};
345323

346324
/** Leave the app through the shell-owned shutdown path. */
@@ -784,9 +762,7 @@ function AppShell({
784762
) : null}
785763

786764
<DiffPane
787-
activeAnnotations={activeAnnotations}
788765
diffContentWidth={diffContentWidth}
789-
dismissedAgentNoteIds={dismissedAgentNoteIds}
790766
files={filteredFiles}
791767
pagerMode={pagerMode}
792768
headerLabelWidth={diffHeaderLabelWidth}
@@ -802,7 +778,6 @@ function AppShell({
802778
wrapLines={wrapLines}
803779
theme={activeTheme}
804780
width={diffPaneWidth}
805-
onDismissAgentNote={dismissAgentNote}
806781
onOpenAgentNotesAtHunk={openAgentNotesAtHunk}
807782
onSelectFile={jumpToFile}
808783
/>

src/ui/components/panes/DiffPane.tsx

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ScrollBoxRenderable } from "@opentui/core";
22
import { useCallback, useEffect, useLayoutEffect, useMemo, useState, type RefObject } from "react";
3-
import type { AgentAnnotation, DiffFile, LayoutMode } from "../../../core/types";
3+
import type { DiffFile, LayoutMode } from "../../../core/types";
44
import type { VisibleAgentNote } from "../../lib/agentAnnotations";
55
import { measureDiffSectionMetrics } from "../../lib/sectionHeights";
66
import { diffHunkId, diffSectionId } from "../../lib/ids";
@@ -12,9 +12,7 @@ const EMPTY_VISIBLE_AGENT_NOTES: VisibleAgentNote[] = [];
1212

1313
/** Render the main multi-file review stream. */
1414
export function DiffPane({
15-
activeAnnotations,
1615
diffContentWidth,
17-
dismissedAgentNoteIds,
1816
files,
1917
headerLabelWidth,
2018
headerStatsWidth,
@@ -30,13 +28,10 @@ export function DiffPane({
3028
wrapLines,
3129
theme,
3230
width,
33-
onDismissAgentNote,
3431
onOpenAgentNotesAtHunk,
3532
onSelectFile,
3633
}: {
37-
activeAnnotations: AgentAnnotation[];
3834
diffContentWidth: number;
39-
dismissedAgentNoteIds: string[];
4035
files: DiffFile[];
4136
headerLabelWidth: number;
4237
headerStatsWidth: number;
@@ -52,7 +47,6 @@ export function DiffPane({
5247
wrapLines: boolean;
5348
theme: AppTheme;
5449
width: number;
55-
onDismissAgentNote: (id: string) => void;
5650
onOpenAgentNotesAtHunk: (fileId: string, hunkIndex: number) => void;
5751
onSelectFile: (fileId: string) => void;
5852
}) {
@@ -100,25 +94,27 @@ export function DiffPane({
10094
const visibleAgentNotesByFile = useMemo(() => {
10195
const next = new Map<string, VisibleAgentNote[]>();
10296

103-
if (!showAgentNotes || !selectedFileId) {
97+
if (!showAgentNotes) {
10498
return next;
10599
}
106100

107-
const dismissedIdSet = new Set(dismissedAgentNoteIds);
108-
const visibleNotes = activeAnnotations
109-
.map((annotation, index) => ({
110-
id: `annotation:${selectedFileId}:${selectedHunkIndex}:${index}`,
111-
annotation,
112-
}))
113-
.filter((note) => !dismissedIdSet.has(note.id));
114-
115-
// Notes only render for the currently selected file/hunk so they stay spatially anchored.
116-
if (visibleNotes.length > 0) {
117-
next.set(selectedFileId, visibleNotes);
118-
}
101+
files.forEach((file) => {
102+
const annotations = file.agent?.annotations ?? [];
103+
if (annotations.length === 0) {
104+
return;
105+
}
106+
107+
next.set(
108+
file.id,
109+
annotations.map((annotation, index) => ({
110+
id: `annotation:${file.id}:${annotation.id ?? index}`,
111+
annotation,
112+
})),
113+
);
114+
});
119115

120116
return next;
121-
}, [activeAnnotations, dismissedAgentNoteIds, selectedFileId, selectedHunkIndex, showAgentNotes]);
117+
}, [files, showAgentNotes]);
122118

123119
// Keep exact row rendering for wrapped lines and visible notes; otherwise reserve
124120
// offscreen section height and only materialize rows near the viewport.
@@ -292,14 +288,7 @@ export function DiffPane({
292288
verticalScrollbarOptions={{ visible: false }}
293289
horizontalScrollbarOptions={{ visible: false }}
294290
>
295-
<box
296-
style={{
297-
width: "100%",
298-
flexDirection: "column",
299-
position: "relative",
300-
overflow: "visible",
301-
}}
302-
>
291+
<box style={{ width: "100%", flexDirection: "column", overflow: "visible" }}>
303292
{files.map((file, index) => {
304293
const shouldRenderSection = visibleWindowedFileIds?.has(file.id) ?? true;
305294
const shouldPrefetchVisibleHighlight =
@@ -334,7 +323,6 @@ export function DiffPane({
334323
visibleAgentNotes={
335324
visibleAgentNotesByFile.get(file.id) ?? EMPTY_VISIBLE_AGENT_NOTES
336325
}
337-
onDismissAgentNote={onDismissAgentNote}
338326
onOpenAgentNotesAtHunk={(hunkIndex) => onOpenAgentNotesAtHunk(file.id, hunkIndex)}
339327
onSelect={() => onSelectFile(file.id)}
340328
/>

src/ui/components/panes/DiffSection.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ interface DiffSectionProps {
2424
theme: AppTheme;
2525
visibleAgentNotes: VisibleAgentNote[];
2626
viewWidth: number;
27-
onDismissAgentNote: (id: string) => void;
2827
onOpenAgentNotesAtHunk: (hunkIndex: number) => void;
2928
onSelect: () => void;
3029
}
@@ -47,7 +46,6 @@ function DiffSectionComponent({
4746
theme,
4847
visibleAgentNotes,
4948
viewWidth,
50-
onDismissAgentNote,
5149
onOpenAgentNotesAtHunk,
5250
onSelect,
5351
}: DiffSectionProps) {
@@ -117,7 +115,6 @@ function DiffSectionComponent({
117115
width={viewWidth}
118116
annotatedHunkIndices={annotatedHunkIndices}
119117
visibleAgentNotes={visibleAgentNotes}
120-
onDismissAgentNote={onDismissAgentNote}
121118
onOpenAgentNotesAtHunk={onOpenAgentNotesAtHunk}
122119
onHighlightReady={onHighlightReady}
123120
selectedHunkIndex={selectedHunkIndex}

src/ui/diff/PierreDiffView.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo } from "react";
22
import type { DiffFile, LayoutMode } from "../../core/types";
33
import { AgentInlineNote, AgentInlineNoteGuideCap } from "../components/panes/AgentInlineNote";
4-
import { type VisibleAgentNote } from "../lib/agentAnnotations";
4+
import type { VisibleAgentNote } from "../lib/agentAnnotations";
55
import type { AppTheme } from "../themes";
66
import { buildSplitRows, buildStackRows } from "./pierre";
77
import { buildReviewRenderPlan } from "./reviewRenderPlan";
@@ -16,7 +16,6 @@ export function PierreDiffView({
1616
annotatedHunkIndices = EMPTY_ANNOTATED_HUNK_INDICES,
1717
file,
1818
layout,
19-
onDismissAgentNote,
2019
onOpenAgentNotesAtHunk,
2120
onHighlightReady,
2221
showLineNumbers = true,
@@ -32,7 +31,6 @@ export function PierreDiffView({
3231
annotatedHunkIndices?: Set<number>;
3332
file: DiffFile | undefined;
3433
layout: Exclude<LayoutMode, "auto">;
35-
onDismissAgentNote?: (id: string) => void;
3634
onOpenAgentNotesAtHunk?: (hunkIndex: number) => void;
3735
onHighlightReady?: () => void;
3836
showLineNumbers?: boolean;
@@ -67,12 +65,11 @@ export function PierreDiffView({
6765
? buildReviewRenderPlan({
6866
fileId: file.id,
6967
rows,
70-
selectedHunkIndex,
7168
showHunkHeaders,
7269
visibleAgentNotes,
7370
})
7471
: [],
75-
[file, rows, selectedHunkIndex, showHunkHeaders, visibleAgentNotes],
72+
[file, rows, showHunkHeaders, visibleAgentNotes],
7673
);
7774
const lineNumberDigits = useMemo(() => String(file ? findMaxLineNumber(file) : 1).length, [file]);
7875

@@ -106,9 +103,6 @@ export function PierreDiffView({
106103
noteIndex={plannedRow.noteIndex}
107104
theme={theme}
108105
width={width}
109-
onClose={
110-
onDismissAgentNote ? () => onDismissAgentNote(plannedRow.annotationId) : undefined
111-
}
112106
/>
113107
);
114108
}

0 commit comments

Comments
 (0)