diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx index 211df4ef2..daaa64040 100644 --- a/apps/web/src/components/DiffPanel.tsx +++ b/apps/web/src/components/DiffPanel.tsx @@ -14,6 +14,7 @@ import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch" import { useTheme } from "../hooks/useTheme"; import { buildPatchCacheKey } from "../lib/diffRendering"; import { + acceptAllDiffFiles, expandDiffFile, reconcileDiffFileReviewState, setDiffFileContextMode, @@ -512,6 +513,16 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { const activeReviewState = patchReviewSelectionKey ? (reviewStateBySelectionKey[patchReviewSelectionKey] ?? {}) : {}; + const acceptedFileCount = useMemo( + () => + renderableFilePaths.reduce( + (count, filePath) => count + (activeReviewState[filePath]?.accepted ? 1 : 0), + 0, + ), + [activeReviewState, renderableFilePaths], + ); + const hasUnacceptedFiles = + renderableFilePaths.length > 0 && acceptedFileCount < renderableFilePaths.length; useEffect(() => { if (diffOpen && !previousDiffOpenRef.current) { @@ -595,6 +606,12 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { }, [updateActiveReviewState], ); + const onAcceptAllFiles = useCallback(() => { + if (renderableFilePaths.length === 0) { + return; + } + updateActiveReviewState((current) => acceptAllDiffFiles(current, renderableFilePaths)); + }, [renderableFilePaths, updateActiveReviewState]); const onToggleFileCollapsed = useCallback( (filePath: string) => { updateActiveReviewState((current) => toggleDiffFileCollapsed(current, filePath)); @@ -704,6 +721,18 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
+ {renderablePatch?.kind === "files" ? ( + + ) : null} - {skill.name} - {skill.description} - - -
- {("tags" in skill ? skill.tags : []).map((tag) => ( - - {tag} - - ))} +
+
+ +
+
+ {skill.name} + {skill.description} +
+ {tags.length > 0 && ( +
+ {tags.map((tag) => ( + + {tag} + + ))} +
+ )} + +
-

Slash commands

-

/{slashName}

-

/skill read {slashName}

+

+ Slash commands +

+
+

+ /{slashName} +

+

+ /skill read {slashName} +

+
{pathValue ? (
-

Path

-

{pathValue}

+

+ Installed path +

+

+ {pathValue} +

) : null}
@@ -422,7 +445,7 @@ export function SkillsPage(props: {
-
+
diff --git a/apps/web/src/components/ui/dialog.tsx b/apps/web/src/components/ui/dialog.tsx index 774047fee..73ae52e11 100644 --- a/apps/web/src/components/ui/dialog.tsx +++ b/apps/web/src/components/ui/dialog.tsx @@ -90,7 +90,13 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { } function DialogPanel({ className, ...props }: React.ComponentProps<"div">) { - return
; + return ( +
+ ); } function DialogFooter({ diff --git a/apps/web/src/lib/diffFileReviewState.test.ts b/apps/web/src/lib/diffFileReviewState.test.ts index a53596963..7b14ceb4d 100644 --- a/apps/web/src/lib/diffFileReviewState.test.ts +++ b/apps/web/src/lib/diffFileReviewState.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + acceptAllDiffFiles, expandDiffFile, reconcileDiffFileReviewState, setDiffFileContextMode, @@ -47,6 +48,31 @@ describe("toggleDiffFileAccepted", () => { }); }); +describe("acceptAllDiffFiles", () => { + it("marks every listed file accepted and collapsed", () => { + expect( + acceptAllDiffFiles( + { + "src/a.ts": { accepted: false, collapsed: false, contextMode: "full" }, + }, + ["src/a.ts", "src/b.ts"], + ), + ).toEqual({ + "src/a.ts": { accepted: true, collapsed: true, contextMode: "full" }, + "src/b.ts": { accepted: true, collapsed: true, contextMode: "patch" }, + }); + }); + + it("returns the same object when every listed file is already accepted", () => { + const state = { + "src/a.ts": { accepted: true, collapsed: true, contextMode: "patch" as const }, + "src/b.ts": { accepted: true, collapsed: true, contextMode: "full" as const }, + }; + + expect(acceptAllDiffFiles(state, ["src/a.ts", "src/b.ts"])).toBe(state); + }); +}); + describe("toggleDiffFileCollapsed", () => { it("toggles collapsed without changing acceptance", () => { expect( diff --git a/apps/web/src/lib/diffFileReviewState.ts b/apps/web/src/lib/diffFileReviewState.ts index 7e15789ce..1fc22d12a 100644 --- a/apps/web/src/lib/diffFileReviewState.ts +++ b/apps/web/src/lib/diffFileReviewState.ts @@ -39,6 +39,30 @@ export function toggleDiffFileAccepted( }; } +export function acceptAllDiffFiles( + current: DiffFileReviewStateByPath, + paths: ReadonlyArray, +): DiffFileReviewStateByPath { + let next = current; + + for (const path of paths) { + const previous = next[path] ?? DEFAULT_DIFF_FILE_REVIEW_STATE; + if (previous.accepted && previous.collapsed) { + continue; + } + if (next === current) { + next = { ...current }; + } + next[path] = { + accepted: true, + collapsed: true, + contextMode: previous.contextMode, + }; + } + + return next; +} + export function toggleDiffFileCollapsed( current: DiffFileReviewStateByPath, path: string,