authorgravatar for ilyap@wix.comIllia Panasenko <ilyap@wix.com> 2026-06-22 05:31:26+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-21 20:31:26-07:00
log28107e89c25711a835369c2b52c552f82e8de02a
tree5fab968be2acb70580754dcdb76b3f0448bdc6e8
parent82a9bcc7f4e1c96bfb9cdc5acdd5be67151c707c
signature Signed by PGP key B5690EEEBB952194

chore: remove `AnnotatableFileDiff` leftovers, rename file (#3488)


3 files changed, 269 insertions(+), 447 deletions(-)

apps/web/src/components/DiffPanel.tsx+1-1
......@@ -38,7 +38,7 @@ import { resolveThreadRouteRef } from "../threadRoutes";
3838import { useClientSettings } from "../hooks/useSettings";
3939import { formatShortTimestamp } from "../timestampFormat";
4040import { DiffPanelLoadingState, DiffPanelShell, type DiffPanelMode } from "./DiffPanelShell";
41import { AnnotatableCodeView, type AnnotatableCodeViewHandle } from "./diffs/AnnotatableFileDiff";
41import { AnnotatableCodeView, type AnnotatableCodeViewHandle } from "./diffs/AnnotatableCodeView";
4242import { ToggleGroup, Toggle } from "./ui/toggle-group";
4343import { Switch } from "./ui/switch";
4444import {
apps/web/src/components/diffs/AnnotatableCodeView.tsx created+268
......@@ -0,0 +1,268 @@
1import type {
2 AnnotationSide,
3 CodeViewDiffItem,
4 CodeViewItem,
5 DiffLineAnnotation,
6 FileDiffMetadata,
7 SelectedLineRange,
8} from "@pierre/diffs";
9import { CodeView, type CodeViewHandle, type CodeViewProps } from "@pierre/diffs/react";
10import type { ScopedThreadRef } from "@t3tools/contracts";
11import { useCallback, useMemo, useState, type ReactNode, type Ref } from "react";
12
13import { type DraftId, useComposerDraftStore } from "~/composerDraftStore";
14import { fnv1a32 } from "~/lib/diffRendering";
15import {
16 buildDiffReviewComment,
17 restoreDiffReviewCommentRange,
18 type ReviewCommentContext,
19} from "~/reviewCommentContext";
20
21import { LocalCommentAnnotation } from "../files/LocalCommentAnnotation";
22import { nextFileCommentId } from "../files/fileCommentAnnotations";
23
24interface DiffCommentAnnotationEntry {
25 id: string;
26 kind: "draft" | "comment";
27 range: SelectedLineRange;
28 rangeLabel: string;
29 text: string;
30}
31
32interface DiffCommentAnnotationGroup {
33 entries: DiffCommentAnnotationEntry[];
34}
35
36type DiffCommentLineAnnotation = DiffLineAnnotation<DiffCommentAnnotationGroup>;
37export type AnnotatableCodeViewHandle = CodeViewHandle<DiffCommentAnnotationGroup>;
38const EMPTY_REVIEW_COMMENTS: ReadonlyArray<ReviewCommentContext> = [];
39
40function annotationSide(range: SelectedLineRange): AnnotationSide {
41 return (range.endSide ?? range.side) === "deletions" ? "deletions" : "additions";
42}
43
44function appendAnnotationEntry(
45 annotations: ReadonlyArray<DiffCommentLineAnnotation>,
46 range: SelectedLineRange,
47 entry: DiffCommentAnnotationEntry,
48): DiffCommentLineAnnotation[] {
49 const side = annotationSide(range);
50 const annotationIndex = annotations.findIndex(
51 (annotation) => annotation.side === side && annotation.lineNumber === range.end,
52 );
53 if (annotationIndex < 0) {
54 return [
55 ...annotations,
56 {
57 side,
58 lineNumber: range.end,
59 metadata: { entries: [entry] },
60 },
61 ];
62 }
63 return annotations.map((annotation, index) =>
64 index === annotationIndex
65 ? {
66 ...annotation,
67 metadata: { entries: [...annotation.metadata.entries, entry] },
68 }
69 : annotation,
70 );
71}
72
73interface AnnotatableCodeViewProps {
74 files: ReadonlyArray<{
75 fileDiff: FileDiffMetadata;
76 filePath: string;
77 fileKey: string;
78 collapsed: boolean;
79 }>;
80 sectionId: string;
81 sectionTitle: string;
82 composerDraftTarget: ScopedThreadRef | DraftId;
83 options: NonNullable<CodeViewProps<DiffCommentAnnotationGroup>["options"]>;
84 viewerRef?: Ref<AnnotatableCodeViewHandle>;
85 className?: string;
86 renderHeaderPrefix: (
87 fileDiff: FileDiffMetadata,
88 fileKey: string,
89 collapsed: boolean,
90 ) => ReactNode;
91}
92
93interface DiffSelectionContext {
94 item: CodeViewItem<DiffCommentAnnotationGroup>;
95}
96
97export function AnnotatableCodeView({
98 files,
99 sectionId,
100 sectionTitle,
101 composerDraftTarget,
102 options,
103 viewerRef,
104 className,
105 renderHeaderPrefix,
106}: AnnotatableCodeViewProps) {
107 const addReviewComment = useComposerDraftStore((store) => store.addReviewComment);
108 const removeReviewComment = useComposerDraftStore((store) => store.removeReviewComment);
109 const reviewComments = useComposerDraftStore(
110 (store) => store.getComposerDraft(composerDraftTarget)?.reviewComments ?? EMPTY_REVIEW_COMMENTS,
111 );
112 const [selectedLines, setSelectedLines] = useState<{
113 id: string;
114 range: SelectedLineRange;
115 } | null>(null);
116 const [draft, setDraft] = useState<{
117 fileKey: string;
118 annotation: DiffCommentLineAnnotation;
119 } | null>(null);
120
121 const filesByKey = useMemo(() => new Map(files.map((file) => [file.fileKey, file])), [files]);
122 const items = useMemo<CodeViewDiffItem<DiffCommentAnnotationGroup>[]>(
123 () =>
124 files.map(({ fileDiff, filePath, fileKey, collapsed }) => {
125 const persisted = reviewComments
126 .filter(
127 (comment) =>
128 comment.sectionId === sectionId &&
129 comment.filePath === filePath &&
130 (comment.fenceLanguage ?? "diff") === "diff",
131 )
132 .reduce<DiffCommentLineAnnotation[]>((annotations, comment) => {
133 const range = restoreDiffReviewCommentRange(fileDiff, comment);
134 if (!range) return annotations;
135 return appendAnnotationEntry(annotations, range, {
136 id: comment.id,
137 kind: "comment",
138 range,
139 rangeLabel: comment.rangeLabel,
140 text: comment.text,
141 });
142 }, []);
143 const annotations =
144 draft?.fileKey === fileKey ? [...persisted, draft.annotation] : persisted;
145 return {
146 id: fileKey,
147 type: "diff",
148 fileDiff,
149 annotations,
150 collapsed,
151 version: fnv1a32(
152 `${collapsed ? "1" : "0"}:${annotations
153 .flatMap((annotation) =>
154 annotation.metadata.entries.map(
155 (entry) => `${entry.id}:${entry.rangeLabel}:${entry.text}`,
156 ),
157 )
158 .join(":")}`,
159 ),
160 };
161 }),
162 [draft, files, reviewComments, sectionId],
163 );
164
165 const removeEntry = useCallback(
166 (entryId: string) => {
167 setSelectedLines(null);
168 if (draft?.annotation.metadata.entries.some((entry) => entry.id === entryId)) {
169 setDraft(null);
170 } else {
171 removeReviewComment(composerDraftTarget, entryId);
172 }
173 },
174 [composerDraftTarget, draft, removeReviewComment],
175 );
176
177 const submitEntry = useCallback(
178 (entryId: string, text: string) => {
179 const entry = draft?.annotation.metadata.entries.find(
180 (candidate) => candidate.id === entryId,
181 );
182 const file = draft ? filesByKey.get(draft.fileKey) : undefined;
183 if (!entry || !file) return;
184 const comment = buildDiffReviewComment({
185 id: entry.id,
186 sectionId,
187 sectionTitle,
188 filePath: file.filePath,
189 fileDiff: file.fileDiff,
190 range: entry.range,
191 text,
192 });
193 if (comment) addReviewComment(composerDraftTarget, comment);
194 setSelectedLines(null);
195 setDraft(null);
196 },
197 [addReviewComment, composerDraftTarget, draft, filesByKey, sectionId, sectionTitle],
198 );
199
200 const beginComment = useCallback(
201 (range: SelectedLineRange | null, context: DiffSelectionContext) => {
202 if (!range) return;
203 const item = context.item;
204 if (item.type !== "diff") return;
205 const file = filesByKey.get(item.id);
206 if (!file) return;
207 const id = nextFileCommentId();
208 const comment = buildDiffReviewComment({
209 id,
210 sectionId,
211 sectionTitle,
212 filePath: file.filePath,
213 fileDiff: file.fileDiff,
214 range,
215 text: "",
216 });
217 if (!comment) return;
218 setDraft({
219 fileKey: item.id,
220 annotation: {
221 side: annotationSide(range),
222 lineNumber: range.end,
223 metadata: {
224 entries: [{ id, kind: "draft", range, rangeLabel: comment.rangeLabel, text: "" }],
225 },
226 },
227 });
228 },
229 [filesByKey, sectionId, sectionTitle],
230 );
231
232 const hasOpenComment = draft !== null;
233 return (
234 <CodeView<DiffCommentAnnotationGroup>
235 {...(viewerRef ? { ref: viewerRef } : {})}
236 {...(className ? { className } : {})}
237 items={items}
238 selectedLines={selectedLines}
239 onSelectedLinesChange={setSelectedLines}
240 options={{
241 ...options,
242 enableGutterUtility: !hasOpenComment,
243 enableLineSelection: !hasOpenComment,
244 onLineSelectionEnd: beginComment,
245 }}
246 renderHeaderPrefix={(item) =>
247 item.type === "diff"
248 ? renderHeaderPrefix(item.fileDiff, item.id, item.collapsed === true)
249 : null
250 }
251 renderAnnotation={(annotation) => (
252 <div className="py-1">
253 {annotation.metadata.entries.map((entry) => (
254 <LocalCommentAnnotation
255 key={entry.id}
256 kind={entry.kind}
257 rangeLabel={entry.rangeLabel}
258 text={entry.text}
259 onCancel={() => removeEntry(entry.id)}
260 onComment={(text) => submitEntry(entry.id, text)}
261 onDelete={() => removeEntry(entry.id)}
262 />
263 ))}
264 </div>
265 )}
266 />
267 );
268}
apps/web/src/components/diffs/AnnotatableFileDiff.tsx deleted-446
......@@ -1,446 +0,0 @@
1import type {
2 AnnotationSide,
3 CodeViewDiffItem,
4 CodeViewItem,
5 DiffLineAnnotation,
6 FileDiffMetadata,
7 SelectedLineRange,
8} from "@pierre/diffs";
9import {
10 CodeView,
11 type CodeViewHandle,
12 type CodeViewProps,
13 FileDiff,
14 type FileDiffProps,
15} from "@pierre/diffs/react";
16import type { ScopedThreadRef } from "@t3tools/contracts";
17import { useCallback, useMemo, useState, type ReactNode, type Ref } from "react";
18
19import { type DraftId, useComposerDraftStore } from "~/composerDraftStore";
20import { fnv1a32 } from "~/lib/diffRendering";
21import {
22 buildDiffReviewComment,
23 restoreDiffReviewCommentRange,
24 type ReviewCommentContext,
25} from "~/reviewCommentContext";
26
27import { LocalCommentAnnotation } from "../files/LocalCommentAnnotation";
28import { nextFileCommentId } from "../files/fileCommentAnnotations";
29
30interface DiffCommentAnnotationEntry {
31 id: string;
32 kind: "draft" | "comment";
33 range: SelectedLineRange;
34 rangeLabel: string;
35 text: string;
36}
37
38interface DiffCommentAnnotationGroup {
39 entries: DiffCommentAnnotationEntry[];
40}
41
42type DiffCommentLineAnnotation = DiffLineAnnotation<DiffCommentAnnotationGroup>;
43export type AnnotatableCodeViewHandle = CodeViewHandle<DiffCommentAnnotationGroup>;
44const EMPTY_REVIEW_COMMENTS: ReadonlyArray<ReviewCommentContext> = [];
45
46function annotationSide(range: SelectedLineRange): AnnotationSide {
47 return (range.endSide ?? range.side) === "deletions" ? "deletions" : "additions";
48}
49
50function appendAnnotationEntry(
51 annotations: ReadonlyArray<DiffCommentLineAnnotation>,
52 range: SelectedLineRange,
53 entry: DiffCommentAnnotationEntry,
54): DiffCommentLineAnnotation[] {
55 const side = annotationSide(range);
56 const annotationIndex = annotations.findIndex(
57 (annotation) => annotation.side === side && annotation.lineNumber === range.end,
58 );
59 if (annotationIndex < 0) {
60 return [
61 ...annotations,
62 {
63 side,
64 lineNumber: range.end,
65 metadata: { entries: [entry] },
66 },
67 ];
68 }
69 return annotations.map((annotation, index) =>
70 index === annotationIndex
71 ? {
72 ...annotation,
73 metadata: { entries: [...annotation.metadata.entries, entry] },
74 }
75 : annotation,
76 );
77}
78
79interface AnnotatableFileDiffProps {
80 fileDiff: FileDiffMetadata;
81 filePath: string;
82 sectionId: string;
83 sectionTitle: string;
84 composerDraftTarget: ScopedThreadRef | DraftId;
85 options: FileDiffProps<DiffCommentAnnotationGroup>["options"];
86 renderHeaderPrefix: (fileDiff: FileDiffMetadata) => ReactNode;
87}
88
89export function AnnotatableFileDiff({
90 fileDiff,
91 filePath,
92 sectionId,
93 sectionTitle,
94 composerDraftTarget,
95 options,
96 renderHeaderPrefix,
97}: AnnotatableFileDiffProps) {
98 const addReviewComment = useComposerDraftStore((store) => store.addReviewComment);
99 const removeReviewComment = useComposerDraftStore((store) => store.removeReviewComment);
100 const reviewComments = useComposerDraftStore(
101 (store) => store.getComposerDraft(composerDraftTarget)?.reviewComments ?? EMPTY_REVIEW_COMMENTS,
102 );
103 const [selectedRange, setSelectedRange] = useState<SelectedLineRange | null>(null);
104 const [draftAnnotation, setDraftAnnotation] = useState<DiffCommentLineAnnotation | null>(null);
105 const persistedAnnotations = useMemo(
106 () =>
107 reviewComments
108 .filter(
109 (comment) =>
110 comment.sectionId === sectionId &&
111 comment.filePath === filePath &&
112 (comment.fenceLanguage ?? "diff") === "diff",
113 )
114 .reduce<DiffCommentLineAnnotation[]>((annotations, comment) => {
115 const range = restoreDiffReviewCommentRange(fileDiff, comment);
116 if (!range) return annotations;
117 return appendAnnotationEntry(annotations, range, {
118 id: comment.id,
119 kind: "comment",
120 range,
121 rangeLabel: comment.rangeLabel,
122 text: comment.text,
123 });
124 }, []),
125 [fileDiff, filePath, reviewComments, sectionId],
126 );
127 const lineAnnotations = useMemo(
128 () => (draftAnnotation ? [...persistedAnnotations, draftAnnotation] : persistedAnnotations),
129 [draftAnnotation, persistedAnnotations],
130 );
131
132 const removeAnnotationEntry = useCallback(
133 (entryId: string) => {
134 setSelectedRange(null);
135 if (
136 draftAnnotation?.metadata.entries.some(
137 (entry) => entry.id === entryId && entry.kind === "draft",
138 )
139 ) {
140 setDraftAnnotation(null);
141 return;
142 }
143 removeReviewComment(composerDraftTarget, entryId);
144 },
145 [composerDraftTarget, draftAnnotation, removeReviewComment],
146 );
147
148 const submitAnnotationEntry = useCallback(
149 (entryId: string, text: string) => {
150 const entry = draftAnnotation?.metadata.entries.find((candidate) => candidate.id === entryId);
151 if (!entry) return;
152
153 const comment = buildDiffReviewComment({
154 id: entry.id,
155 sectionId,
156 sectionTitle,
157 filePath,
158 fileDiff,
159 range: entry.range,
160 text,
161 });
162 if (comment) {
163 addReviewComment(composerDraftTarget, comment);
164 }
165 setSelectedRange(null);
166 setDraftAnnotation(null);
167 },
168 [
169 addReviewComment,
170 composerDraftTarget,
171 fileDiff,
172 filePath,
173 draftAnnotation,
174 sectionId,
175 sectionTitle,
176 ],
177 );
178
179 const beginComment = useCallback(
180 (range: SelectedLineRange) => {
181 const id = nextFileCommentId();
182 const comment = buildDiffReviewComment({
183 id,
184 sectionId,
185 sectionTitle,
186 filePath,
187 fileDiff,
188 range,
189 text: "",
190 });
191 if (!comment) return;
192
193 const draftEntry: DiffCommentAnnotationEntry = {
194 id,
195 kind: "draft",
196 range,
197 rangeLabel: comment.rangeLabel,
198 text: "",
199 };
200 setDraftAnnotation({
201 side: annotationSide(range),
202 lineNumber: range.end,
203 metadata: { entries: [draftEntry] },
204 });
205 },
206 [fileDiff, filePath, sectionId, sectionTitle],
207 );
208
209 const hasOpenCommentForm = draftAnnotation !== null;
210 const handleLineSelectionEnd = useCallback(
211 (range: SelectedLineRange | null) => {
212 setSelectedRange(range);
213 if (range) beginComment(range);
214 },
215 [beginComment],
216 );
217
218 return (
219 <FileDiff<DiffCommentAnnotationGroup>
220 fileDiff={fileDiff}
221 renderHeaderPrefix={renderHeaderPrefix}
222 options={{
223 ...options,
224 enableGutterUtility: !hasOpenCommentForm,
225 enableLineSelection: !hasOpenCommentForm,
226 onGutterUtilityClick: setSelectedRange,
227 onLineSelectionChange: setSelectedRange,
228 onLineSelectionEnd: handleLineSelectionEnd,
229 }}
230 selectedLines={selectedRange}
231 lineAnnotations={lineAnnotations}
232 renderAnnotation={(annotation) => (
233 <div className="py-1">
234 {annotation.metadata.entries.map((entry) => (
235 <LocalCommentAnnotation
236 key={entry.id}
237 kind={entry.kind}
238 rangeLabel={entry.rangeLabel}
239 text={entry.text}
240 onCancel={() => removeAnnotationEntry(entry.id)}
241 onComment={(text) => submitAnnotationEntry(entry.id, text)}
242 onDelete={() => removeAnnotationEntry(entry.id)}
243 />
244 ))}
245 </div>
246 )}
247 />
248 );
249}
250
251interface AnnotatableCodeViewProps {
252 files: ReadonlyArray<{
253 fileDiff: FileDiffMetadata;
254 filePath: string;
255 fileKey: string;
256 collapsed: boolean;
257 }>;
258 sectionId: string;
259 sectionTitle: string;
260 composerDraftTarget: ScopedThreadRef | DraftId;
261 options: NonNullable<CodeViewProps<DiffCommentAnnotationGroup>["options"]>;
262 viewerRef?: Ref<AnnotatableCodeViewHandle>;
263 className?: string;
264 renderHeaderPrefix: (
265 fileDiff: FileDiffMetadata,
266 fileKey: string,
267 collapsed: boolean,
268 ) => ReactNode;
269}
270
271interface DiffSelectionContext {
272 item: CodeViewItem<DiffCommentAnnotationGroup>;
273}
274
275export function AnnotatableCodeView({
276 files,
277 sectionId,
278 sectionTitle,
279 composerDraftTarget,
280 options,
281 viewerRef,
282 className,
283 renderHeaderPrefix,
284}: AnnotatableCodeViewProps) {
285 const addReviewComment = useComposerDraftStore((store) => store.addReviewComment);
286 const removeReviewComment = useComposerDraftStore((store) => store.removeReviewComment);
287 const reviewComments = useComposerDraftStore(
288 (store) => store.getComposerDraft(composerDraftTarget)?.reviewComments ?? EMPTY_REVIEW_COMMENTS,
289 );
290 const [selectedLines, setSelectedLines] = useState<{
291 id: string;
292 range: SelectedLineRange;
293 } | null>(null);
294 const [draft, setDraft] = useState<{
295 fileKey: string;
296 annotation: DiffCommentLineAnnotation;
297 } | null>(null);
298
299 const filesByKey = useMemo(() => new Map(files.map((file) => [file.fileKey, file])), [files]);
300 const items = useMemo<CodeViewDiffItem<DiffCommentAnnotationGroup>[]>(
301 () =>
302 files.map(({ fileDiff, filePath, fileKey, collapsed }) => {
303 const persisted = reviewComments
304 .filter(
305 (comment) =>
306 comment.sectionId === sectionId &&
307 comment.filePath === filePath &&
308 (comment.fenceLanguage ?? "diff") === "diff",
309 )
310 .reduce<DiffCommentLineAnnotation[]>((annotations, comment) => {
311 const range = restoreDiffReviewCommentRange(fileDiff, comment);
312 if (!range) return annotations;
313 return appendAnnotationEntry(annotations, range, {
314 id: comment.id,
315 kind: "comment",
316 range,
317 rangeLabel: comment.rangeLabel,
318 text: comment.text,
319 });
320 }, []);
321 const annotations =
322 draft?.fileKey === fileKey ? [...persisted, draft.annotation] : persisted;
323 return {
324 id: fileKey,
325 type: "diff",
326 fileDiff,
327 annotations,
328 collapsed,
329 version: fnv1a32(
330 `${collapsed ? "1" : "0"}:${annotations
331 .flatMap((annotation) =>
332 annotation.metadata.entries.map(
333 (entry) => `${entry.id}:${entry.rangeLabel}:${entry.text}`,
334 ),
335 )
336 .join(":")}`,
337 ),
338 };
339 }),
340 [draft, files, reviewComments, sectionId],
341 );
342
343 const removeEntry = useCallback(
344 (entryId: string) => {
345 setSelectedLines(null);
346 if (draft?.annotation.metadata.entries.some((entry) => entry.id === entryId)) {
347 setDraft(null);
348 } else {
349 removeReviewComment(composerDraftTarget, entryId);
350 }
351 },
352 [composerDraftTarget, draft, removeReviewComment],
353 );
354
355 const submitEntry = useCallback(
356 (entryId: string, text: string) => {
357 const entry = draft?.annotation.metadata.entries.find(
358 (candidate) => candidate.id === entryId,
359 );
360 const file = draft ? filesByKey.get(draft.fileKey) : undefined;
361 if (!entry || !file) return;
362 const comment = buildDiffReviewComment({
363 id: entry.id,
364 sectionId,
365 sectionTitle,
366 filePath: file.filePath,
367 fileDiff: file.fileDiff,
368 range: entry.range,
369 text,
370 });
371 if (comment) addReviewComment(composerDraftTarget, comment);
372 setSelectedLines(null);
373 setDraft(null);
374 },
375 [addReviewComment, composerDraftTarget, draft, filesByKey, sectionId, sectionTitle],
376 );
377
378 const beginComment = useCallback(
379 (range: SelectedLineRange | null, context: DiffSelectionContext) => {
380 if (!range) return;
381 const item = context.item;
382 if (item.type !== "diff") return;
383 const file = filesByKey.get(item.id);
384 if (!file) return;
385 const id = nextFileCommentId();
386 const comment = buildDiffReviewComment({
387 id,
388 sectionId,
389 sectionTitle,
390 filePath: file.filePath,
391 fileDiff: file.fileDiff,
392 range,
393 text: "",
394 });
395 if (!comment) return;
396 setDraft({
397 fileKey: item.id,
398 annotation: {
399 side: annotationSide(range),
400 lineNumber: range.end,
401 metadata: {
402 entries: [{ id, kind: "draft", range, rangeLabel: comment.rangeLabel, text: "" }],
403 },
404 },
405 });
406 },
407 [filesByKey, sectionId, sectionTitle],
408 );
409
410 const hasOpenComment = draft !== null;
411 return (
412 <CodeView<DiffCommentAnnotationGroup>
413 {...(viewerRef ? { ref: viewerRef } : {})}
414 {...(className ? { className } : {})}
415 items={items}
416 selectedLines={selectedLines}
417 onSelectedLinesChange={setSelectedLines}
418 options={{
419 ...options,
420 enableGutterUtility: !hasOpenComment,
421 enableLineSelection: !hasOpenComment,
422 onLineSelectionEnd: beginComment,
423 }}
424 renderHeaderPrefix={(item) =>
425 item.type === "diff"
426 ? renderHeaderPrefix(item.fileDiff, item.id, item.collapsed === true)
427 : null
428 }
429 renderAnnotation={(annotation) => (
430 <div className="py-1">
431 {annotation.metadata.entries.map((entry) => (
432 <LocalCommentAnnotation
433 key={entry.id}
434 kind={entry.kind}
435 rangeLabel={entry.rangeLabel}
436 text={entry.text}
437 onCancel={() => removeEntry(entry.id)}
438 onComment={(text) => submitEntry(entry.id, text)}
439 onDelete={() => removeEntry(entry.id)}
440 />
441 ))}
442 </div>
443 )}
444 />
445 );
446}