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
12 changes: 10 additions & 2 deletions apps/server/src/routes/agents/lifecycle-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,9 @@ export async function registerAgentLifecycleRoutes(
(agent.worktreePath || gitContextWorktreePath ? "main" : null);

try {
const result = await getAgentDiff(worktreePath, baseRef);
const result = await getAgentDiff(worktreePath, baseRef, undefined, {
ignoreWhitespace: true,
});
if (!result) {
return { baseRef: null, files: [] };
}
Expand Down Expand Up @@ -377,7 +379,13 @@ export async function registerAgentLifecycleRoutes(
(agent.worktreePath || gitContextWorktreePath ? "main" : null);

try {
const result = await getAgentFileDiff(worktreePath, baseRef, query.path);
const result = await getAgentFileDiff(
worktreePath,
baseRef,
query.path,
undefined,
{ ignoreWhitespace: true }
);
if (!result) {
return reply.code(404).send({ error: "File not found in diff." });
}
Expand Down
55 changes: 43 additions & 12 deletions apps/server/src/shared/git/agent-diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ function buildUntrackedDiff(filePath: string, content: string): string {
export async function getAgentDiff(
worktreePath: string,
baseRef: string | null,
run: CommandRunner = runCommand
run: CommandRunner = runCommand,
options?: { ignoreWhitespace?: boolean }
): Promise<DiffResponse | null> {
const resolvedBase = await resolveBaseRef(worktreePath, baseRef, {
runCommand: run,
Expand All @@ -204,19 +205,36 @@ export async function getAgentDiff(
}
const mergeBaseSha = mergeBaseResult.stdout.trim();

const wsFlag = options?.ignoreWhitespace ? ["-w"] : [];
const [numstatResult, statusResult, diffResult, untrackedResult] =
await Promise.all([
run("git", ["-C", worktreePath, "diff", mergeBaseSha, "--numstat"], {
allowedExitCodes: [0],
timeoutMs: GIT_TIMEOUT_MS,
}),
run("git", ["-C", worktreePath, "diff", mergeBaseSha, "--name-status"], {
allowedExitCodes: [0],
timeoutMs: GIT_TIMEOUT_MS,
}),
run(
"git",
["-C", worktreePath, "diff", mergeBaseSha, "-U3", "--no-color"],
["-C", worktreePath, "diff", mergeBaseSha, "--numstat", ...wsFlag],
{
allowedExitCodes: [0],
timeoutMs: GIT_TIMEOUT_MS,
}
),
run(
"git",
["-C", worktreePath, "diff", mergeBaseSha, "--name-status", ...wsFlag],
{
allowedExitCodes: [0],
timeoutMs: GIT_TIMEOUT_MS,
}
),
run(
"git",
[
"-C",
worktreePath,
"diff",
mergeBaseSha,
"-U3",
"--no-color",
...wsFlag,
],
{
allowedExitCodes: [0],
timeoutMs: GIT_TIMEOUT_MS,
Expand Down Expand Up @@ -298,7 +316,8 @@ export async function getAgentFileDiff(
worktreePath: string,
baseRef: string | null,
filePath: string,
run: CommandRunner = runCommand
run: CommandRunner = runCommand,
options?: { ignoreWhitespace?: boolean }
): Promise<FileDiffResponse | null> {
const resolvedBase = await resolveBaseRef(worktreePath, baseRef, {
runCommand: run,
Expand All @@ -315,10 +334,20 @@ export async function getAgentFileDiff(
}
const mergeBaseSha = mergeBaseResult.stdout.trim();

const wsFlag2 = options?.ignoreWhitespace ? ["-w"] : [];
const [numstatResult, statusResult, diffResult] = await Promise.all([
run(
"git",
["-C", worktreePath, "diff", mergeBaseSha, "--numstat", "--", filePath],
[
"-C",
worktreePath,
"diff",
mergeBaseSha,
"--numstat",
...wsFlag2,
"--",
filePath,
],
{ allowedExitCodes: [0], timeoutMs: GIT_TIMEOUT_MS }
),
run(
Expand All @@ -329,6 +358,7 @@ export async function getAgentFileDiff(
"diff",
mergeBaseSha,
"--name-status",
...wsFlag2,
"--",
filePath,
],
Expand All @@ -343,6 +373,7 @@ export async function getAgentFileDiff(
mergeBaseSha,
"-U3",
"--no-color",
...wsFlag2,
"--",
filePath,
],
Expand Down
5 changes: 5 additions & 0 deletions apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,15 @@
midnight: { background: "#000000", foreground: "#f1f1f4" },
mytra: { background: "#030303", foreground: "#dee2e7" },
};
var lightThemes = { light: 1, "solarized-light": 1, daylight: 1 };
var selected = palette[theme] || palette.default;
if (theme && theme !== "default") {
document.documentElement.setAttribute("data-theme", theme);
}
document.documentElement.setAttribute(
"data-theme-mode",
lightThemes[theme] ? "light" : "dark"
);
document.documentElement.style.backgroundColor = selected.background;
document.body.style.backgroundColor = selected.background;
document.body.style.color = selected.foreground;
Expand Down
2 changes: 2 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"cmdk": "^1.1.1",
"cronstrue": "^3.14.0",
"date-fns": "^4.1.0",
"diff-match-patch": "^1.0.5",
"framer-motion": "^12.35.2",
"highlight.js": "^11.11.1",
"jotai": "^2.19.0",
Expand All @@ -55,6 +56,7 @@
"@eslint/js": "^9.39.4",
"@tailwindcss/typography": "^0.5.19",
"@testing-library/react": "^16.3.2",
"@types/diff-match-patch": "^1.0.36",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.7.0",
Expand Down
67 changes: 34 additions & 33 deletions apps/web/src/components/app/agents-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,32 +487,31 @@ export function AgentsView({
)}
onTransitionEnd={handleFeedbackTransitionEnd}
>
<div className="relative h-full min-h-0 min-w-0">
<div className="pointer-events-none absolute left-3 top-3 z-10 flex items-center gap-1">
{!leftPanelOpen ? (
<Button
size="icon"
variant="ghost"
className="pointer-events-auto"
onClick={() => handleSetLeftPanelOpen(true)}
title="Open sidebar"
>
<PanelRightOpen className="h-4 w-4" />
</Button>
) : null}
<TipSpot tipId="quick-phrases" side="bottom" align="center">
<QuickPhrasesButton
agentId={
hasActiveAgent && connState === "connected"
? focusedAgentId!
: null
}
focusTerminal={focusTerminal}
/>
</TipSpot>
</div>
<div className="relative h-full min-h-0 min-w-0 pb-14 pt-14">
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 flex h-14 flex-col items-center justify-center bg-background px-16">
<div className="relative flex h-full min-h-0 min-w-0 flex-col">
<div className="relative z-10 flex h-14 shrink-0 items-center bg-background px-3">
<div className="flex items-center gap-1">
{!leftPanelOpen ? (
<Button
size="icon"
variant="ghost"
onClick={() => handleSetLeftPanelOpen(true)}
title="Open sidebar"
>
<PanelRightOpen className="h-4 w-4" />
</Button>
) : null}
<TipSpot tipId="quick-phrases" side="bottom" align="center">
<QuickPhrasesButton
agentId={
hasActiveAgent && connState === "connected"
? focusedAgentId!
: null
}
focusTerminal={focusTerminal}
/>
</TipSpot>
</div>
<div className="flex flex-1 items-center justify-center">
{focusedAgent?.name ? (
<>
<span
Expand All @@ -529,12 +528,12 @@ export function AgentsView({
</>
) : null}
</div>
{hasActiveAgent && (!mediaPanelOpen || isMobile) ? (
<div className="pointer-events-none absolute right-3 top-3 z-10">
<div className="flex items-center gap-1">
{hasActiveAgent && (!mediaPanelOpen || isMobile) ? (
<Button
size="icon"
variant="ghost"
className="pointer-events-auto relative"
className="relative"
onClick={() => setMediaOpen(true)}
title="Open media sidebar"
data-testid="toggle-media-sidebar"
Expand All @@ -546,13 +545,15 @@ export function AgentsView({
</span>
) : null}
</Button>
</div>
) : null}
) : null}
</div>
{connState === "reconnecting" ? (
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-0.5 overflow-hidden">
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-0.5 overflow-hidden">
<div className="dispatch-reconnect-scan h-full w-1/3 will-change-transform bg-[linear-gradient(to_right,transparent,hsl(var(--status-blocked)),hsl(var(--status-waiting)),hsl(var(--status-working)),hsl(var(--status-done)),transparent)] saturate-[1.35] brightness-[1.05] animate-[reconnect-scan_1350ms_ease-in-out_infinite] motion-reduce:animate-none motion-reduce:translate-x-[140%]" />
</div>
) : null}
</div>
<div className="relative min-h-0 flex-1">
<div className={cn("h-full", changesMatch && "hidden")}>
<TerminalPane
isAttached={isAttached}
Expand Down Expand Up @@ -580,7 +581,7 @@ export function AgentsView({
/>
</Routes>
{!isMobile ? (
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-14">
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-14 bg-background">
<AmbientTipBar />
</div>
) : null}
Expand Down
Loading