Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion apps/web/src/components/files/FilePreviewPanel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ProjectReadFileError } from "@t3tools/contracts";
import * as Schema from "effect/Schema";
import { describe, expect, it, vi } from "vite-plus/test";

import { remapFileCommentAnnotations } from "./fileCommentAnnotations";
import { promptFileCommentEntries, remapFileCommentAnnotations } from "./fileCommentAnnotations";
import {
filePreviewReadErrorMessage,
isMarkdownPreviewFile,
Expand Down Expand Up @@ -63,6 +63,22 @@ describe("file comment annotations", () => {
},
]);
});

it("shows only drafts and comments still in the prompt", () => {
const entry = (id: string, kind: "draft" | "comment") => ({
id,
kind,
startLine: 3,
endLine: 3,
text: "",
});
expect(
promptFileCommentEntries(
[entry("sent", "comment"), entry("new", "draft"), entry("kept", "comment")],
new Set(["kept"]),
),
).toEqual([entry("new", "draft"), entry("kept", "comment")]);
});
});

describe("isMarkdownPreviewFile", () => {
Expand Down
67 changes: 42 additions & 25 deletions apps/web/src/components/files/FilePreviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ import { resolvePathLinkTarget } from "@t3tools/shared/fileLinks";
import { ScrollArea } from "~/components/ui/scroll-area";
import { stackedThreadToast, toastManager } from "~/components/ui/toast";
import { type DraftId, useComposerDraftStore } from "~/composerDraftStore";
import { buildFileReviewComment } from "~/reviewCommentContext";
import { buildFileReviewComment, type ReviewCommentContext } from "~/reviewCommentContext";
import { assetEnvironment } from "~/state/assets";
import { usePreviewAvailable } from "~/browser/previewRuntime";
import { useEnvironmentHttpBaseUrl, usePrimaryEnvironmentId } from "~/state/environments";
Expand Down Expand Up @@ -105,6 +105,7 @@ import {
nextFileCommentId,
normalizeFileCommentRange,
remapFileCommentAnnotations,
promptFileCommentEntries,
} from "./fileCommentAnnotations";
import { installFileEditorDismissal } from "./fileEditorDismissal";
import {
Expand Down Expand Up @@ -667,6 +668,8 @@ function useFileLineReveal(
};
}

const EMPTY_REVIEW_COMMENTS: ReadonlyArray<ReviewCommentContext> = [];

const createFileEditor: EditorFactory<FileCommentAnnotationGroup, undefined> = (
editorType,
options,
Expand Down Expand Up @@ -760,6 +763,12 @@ function EditableFileSurface({
}: EditableFileSurfaceProps) {
const addReviewComment = useComposerDraftStore((store) => store.addReviewComment);
const removeReviewComment = useComposerDraftStore((store) => store.removeReviewComment);
const promptCommentIds = new Set(
useComposerDraftStore(
(store) =>
store.getComposerDraft(composerDraftTarget)?.reviewComments ?? EMPTY_REVIEW_COMMENTS,
).map((comment) => comment.id),
);
const [lineAnnotations, setLineAnnotations] = useState<FileCommentLineAnnotation[]>([]);
const [selectionOverride, setSelectionOverride] = useState<FileSelectionOverride | null>(null);
const selectedRange =
Expand Down Expand Up @@ -808,7 +817,7 @@ function EditableFileSurface({
setLineAnnotations((current) => (current === nextLineAnnotations ? current : remapped));
for (const annotation of remapped) {
for (const entry of annotation.metadata.entries) {
if (entry.kind !== "comment") continue;
if (entry.kind !== "comment" || !promptCommentIds.has(entry.id)) continue;
addReviewComment(
composerDraftTarget,
buildFileReviewComment({
Expand Down Expand Up @@ -974,29 +983,37 @@ function EditableFileSurface({
}}
selectedLines={selectedRange}
lineAnnotations={lineAnnotations}
renderAnnotation={(annotation) => (
<div className="py-1">
{annotation.metadata.entries.map((entry) => (
<DiffCommentAnnotation
key={entry.id}
environmentId={environmentId}
projectId={projectId}
ownerKey={JSON.stringify([
environmentId,
composerDraftTarget,
relativePath,
entry.id,
])}
kind={entry.kind}
rangeLabel={formatFileCommentRange(entry.startLine, entry.endLine)}
text={entry.text}
onCancel={() => removeAnnotationEntry(entry.id)}
onComment={(text) => submitAnnotationEntry(entry.id, text)}
onDelete={() => removeAnnotationEntry(entry.id)}
/>
))}
</div>
)}
renderAnnotation={(annotation) => {
// Sent comments stay annotated so a failed send can restore them at remapped lines.
const entries = promptFileCommentEntries(
annotation.metadata.entries,
promptCommentIds,
);
if (entries.length === 0) return null;
return (
<div className="py-1">
{entries.map((entry) => (
<DiffCommentAnnotation
key={entry.id}
environmentId={environmentId}
projectId={projectId}
ownerKey={JSON.stringify([
environmentId,
composerDraftTarget,
relativePath,
entry.id,
])}
kind={entry.kind}
rangeLabel={formatFileCommentRange(entry.startLine, entry.endLine)}
text={entry.text}
onCancel={() => removeAnnotationEntry(entry.id)}
onComment={(text) => submitAnnotationEntry(entry.id, text)}
onDelete={() => removeAnnotationEntry(entry.id)}
/>
))}
</div>
);
}}
className="min-h-full"
/>
</Virtualizer>
Expand Down
8 changes: 8 additions & 0 deletions apps/web/src/components/files/fileCommentAnnotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ export function remapFileCommentAnnotations(
},
}));
}

/** Drafts and the comments still in the prompt; sent or removed ones are not shown or re-added. */
export function promptFileCommentEntries(
entries: ReadonlyArray<FileCommentAnnotationEntry>,
promptCommentIds: ReadonlySet<string>,
): FileCommentAnnotationEntry[] {
return entries.filter((entry) => entry.kind === "draft" || promptCommentIds.has(entry.id));
}
Loading