Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
115721e
fix(ui): normalize tool card change titles
Waishnav Aug 5, 2026
091cc4b
fix(ui): scope pretty scrollbars to payloads
Waishnav Aug 5, 2026
517653f
style(ui): compact and clarify tool cards
Waishnav Aug 5, 2026
265f04f
refactor(ui): structure tool card details
Waishnav Aug 5, 2026
ca32b27
fix(ui): preserve process state accents
Waishnav Aug 5, 2026
09c015d
fix(ui): reveal payload scrollbars reliably
Waishnav Aug 5, 2026
897c57e
refactor(ui): clean up workspace details
Waishnav Aug 5, 2026
b0bf8be
fix(ui): make workspace skills expandable
Waishnav Aug 5, 2026
d770997
fix(ui): clamp workspace detail rows
Waishnav Aug 5, 2026
20a4a86
fix(ui): preserve workspace disclosure state
Waishnav Aug 5, 2026
54f88c5
fix(ui): align diff file rows
Waishnav Aug 5, 2026
97f8b8d
style(ui): use precise webkit scrollbars
Waishnav Aug 5, 2026
210324a
style(ui): neutralize scrollbar thumb
Waishnav Aug 5, 2026
9cce6f4
fix(ui): classify patch file operations
Waishnav Aug 5, 2026
3f4e548
feat(ui): expand workspace instructions
Waishnav Aug 5, 2026
363c69a
feat(ui): show rename source paths
Waishnav Aug 5, 2026
06ba03b
feat(ui): open single-file patch diffs
Waishnav Aug 5, 2026
3d43ba8
refactor(ui): hide worktree implementation flags
Waishnav Aug 5, 2026
54dc7c8
fix(ui): preserve review file identity
Waishnav Aug 6, 2026
ef552d0
fix(ui): preserve card visual hierarchy
Waishnav Aug 6, 2026
0d3ca70
fix(ui): simplify workspace disclosure labels
Waishnav Aug 6, 2026
8ddc6e4
fix(ui): ignore internal workspace diagnostics
Waishnav Aug 6, 2026
5bc44a7
refactor(ui): share workspace disclosure rows
Waishnav Aug 6, 2026
35416ac
refactor(ui): keep diagnostics out of cards
Waishnav Aug 6, 2026
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
5 changes: 4 additions & 1 deletion src/apply-patch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,16 @@ assert.throws(

const overwriteRoot = await mkdtemp(join(tmpdir(), "devspace-apply-patch-overwrite-"));
await writeFile(join(overwriteRoot, "duplicate.txt"), "old content\n");
await applyPatch(
const overwriteResult = await applyPatch(
overwriteRoot,
`*** Begin Patch
*** Add File: duplicate.txt
+new content
*** End Patch`,
);
assert.deepEqual(overwriteResult.files, [
{ path: "duplicate.txt", operation: "update" },
]);
assert.equal(await readFile(join(overwriteRoot, "duplicate.txt"), "utf8"), "new content\n");

await writeFile(join(overwriteRoot, "source.txt"), "from\n");
Expand Down
2 changes: 1 addition & 1 deletion src/apply-patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export async function applyPatch(root: string, patch: string): Promise<ApplyPatc
const original = await readStagedOptional(absolute, action.path);
staged.set(absolute, { content: action.content, mode: original?.mode });
patches.push(unifiedFilePatch(action.path, action.path, original?.content ?? null, action.content));
results.push({ path: action.path, operation: "add" });
results.push({ path: action.path, operation: original ? "update" : "add" });
continue;
}

Expand Down
1 change: 0 additions & 1 deletion src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ test("open_workspace keeps lifecycle flags out of model output and preserves com
assert.ok(Array.isArray(card.skills));
assert.ok(Array.isArray(card.agentProviders));
assert.ok(Array.isArray(card.agents));
assert.ok(Array.isArray(card.skillDiagnostics));
});

test("concurrent checkout opens return one full context and one reuse instruction", async (t) => {
Expand Down
2 changes: 0 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,6 @@ export function createMcpServer(
skills: cardSkills,
agentProviders: cardAgentProviders,
agents: cardAgents,
skillDiagnostics: workspace.skillDiagnostics,
instruction: cardInstruction,
summary: {
mode: workspace.mode,
Expand All @@ -923,7 +922,6 @@ export function createMcpServer(
skills: cardSkills.length,
agentProviders: cardAgentProviders.length,
agents: cardAgents.length,
skillDiagnostics: workspace.skillDiagnostics.length,
},
},
},
Expand Down
37 changes: 37 additions & 0 deletions src/ui/card-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import test from "node:test";
import {
isEditTool,
isExpandableCard,
isInitiallyExpandedCard,
isPatchTool,
isShellTool,
isToolName,
Expand Down Expand Up @@ -31,6 +32,42 @@ test("a patch card expands only when it contains patch content", () => {
assert.equal(isExpandableCard({ tool: "apply_patch" }), false);
});

test("a single-file patch opens immediately", () => {
assert.equal(
isInitiallyExpandedCard({
tool: "apply_patch",
files: [{ path: "src/a.ts", operation: "update" }],
payload: { patch: "diff --git a/src/a.ts b/src/a.ts" },
}),
true,
);
});

test("a multi-file patch stays collapsed", () => {
assert.equal(
isInitiallyExpandedCard({
tool: "apply_patch",
files: [
{ path: "src/a.ts", operation: "update" },
{ path: "src/b.ts", operation: "add" },
],
payload: { patch: "diff --git a/src/a.ts b/src/a.ts" },
}),
false,
);
});

test("show changes still opens immediately", () => {
assert.equal(
isInitiallyExpandedCard({
tool: "show_changes",
files: [{ path: "src/a.ts", type: "change" }],
payload: { patch: "diff --git a/src/a.ts b/src/a.ts" },
}),
true,
);
});

test("a workspace card expands when it contains provider metadata", () => {
assert.equal(
isExpandableCard({
Expand Down
21 changes: 16 additions & 5 deletions src/ui/card-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export type ToolName =
export type HostContext = NonNullable<ReturnType<App["getHostContext"]>>;

export type PatchOperation = "add" | "update" | "delete" | "move";
export type ReviewFileType =
| "change"
| "rename-pure"
| "rename-changed"
| "new"
| "deleted";

export interface ToolResultCard {
tool: ToolName;
Expand All @@ -41,7 +47,7 @@ export interface ToolResultCard {
path?: string;
previousPath?: string;
operation?: PatchOperation;
type?: string;
type?: ReviewFileType;
additions?: number;
removals?: number;
}>;
Expand Down Expand Up @@ -72,7 +78,6 @@ export interface ToolResultCard {
providerAvailable?: boolean;
providerUnavailableReason?: string;
}>;
skillDiagnostics?: unknown[];
instruction?: string;
}

Expand Down Expand Up @@ -165,15 +170,13 @@ export function isExpandableCard(card: ToolResultCard): boolean {
Number(card.summary?.skills ?? 0) > 0 ||
Number(card.summary?.agentProviders ?? 0) > 0 ||
Number(card.summary?.agents ?? 0) > 0 ||
Number(card.summary?.skillDiagnostics ?? 0) > 0 ||
Boolean(card.agentsFiles?.length) ||
Boolean(card.availableAgentsFiles?.length) ||
Boolean(card.skills?.length) ||
Boolean(card.agentProviders?.length) ||
Boolean(card.agents?.length) ||
Boolean(card.worktree) ||
Boolean(card.instruction) ||
Boolean(card.skillDiagnostics?.length)
Boolean(card.instruction)
);
}

Expand All @@ -182,3 +185,11 @@ export function isExpandableCard(card: ToolResultCard): boolean {

return Boolean(card.payload);
}

export function isInitiallyExpandedCard(card: ToolResultCard): boolean {
if (isReviewTool(card.tool)) return isExpandableCard(card);
if (isPatchTool(card.tool)) {
return card.files?.length === 1 && isExpandableCard(card);
}
return false;
}
9 changes: 6 additions & 3 deletions src/ui/heavy-payload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
type HostContext,
type ToolResultCard,
} from "./card-types.js";
import { pierrePrettyScrollbarCss } from "./scrollbar.js";

type ThemeType = "light" | "dark";

Expand Down Expand Up @@ -77,7 +78,7 @@ function HeavyPayload({
);
}

return <pre className={`text-payload ${card.tool}`}>{text}</pre>;
return <pre className={`text-payload pretty-scrollbar ${card.tool}`}>{text}</pre>;
}

function FilePayload({
Expand All @@ -100,6 +101,7 @@ function FilePayload({
},
themeType,
overflow: "scroll",
unsafeCSS: pierrePrettyScrollbarCss,
}),
[themeType],
);
Expand Down Expand Up @@ -134,7 +136,7 @@ function FilePayload({
};
}, [fileOptions, path, startLine, text]);

return <div ref={wrapperRef} className="pierre-file" />;
return <div ref={wrapperRef} className="pierre-file pretty-scrollbar" />;
}

function DiffPayload({
Expand All @@ -158,12 +160,13 @@ function DiffPayload({
hunkSeparators: "line-info",
lineDiffType: "word-alt",
overflow: "scroll",
unsafeCSS: pierrePrettyScrollbarCss,
collapsedContextThreshold: 4,
expansionLineCount: 20,
stickyHeader: true,
disableFileHeader: true,
}}
className="pierre-diff"
className="pierre-diff pretty-scrollbar"
/>
);
}
Expand Down
18 changes: 18 additions & 0 deletions src/ui/icons.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import {
Blocks,
Bot,
ChevronDown,
CircleAlert,
Cpu,
FileDiff,
FileCheck2,
FileMinus,
FilePenLine,
FilePlus,
FileText,
Files,
FolderGit2,
FolderOpen,
FolderTree,
GitBranch,
GitCommitHorizontal,
LoaderCircle,
Search,
SquareTerminal,
Expand All @@ -17,18 +25,28 @@ import {
} from "lucide";

export const toolIcons = {
agents: Bot,
base: GitCommitHorizontal,
chevronDown: ChevronDown,
deleteFile: FileMinus,
diff: FileDiff,
editFile: FilePenLine,
files: Files,
folderOpen: FolderOpen,
folderTree: FolderTree,
gitBranch: GitBranch,
instructions: FileText,
instructionAvailable: FileText,
instructionLoaded: FileCheck2,
loading: LoaderCircle,
providers: Cpu,
readFile: FileText,
search: Search,
skills: Blocks,
sourceCheckout: FolderGit2,
terminal: Terminal,
terminalSquare: SquareTerminal,
warning: CircleAlert,
writeFile: FilePlus,
} as const satisfies Record<string, IconNode>;

Expand Down
Loading
Loading