diff --git a/.changeset/fn-7695-cursor-cli-padding.md b/.changeset/fn-7695-cursor-cli-padding.md
new file mode 100644
index 0000000000..e106e00e87
--- /dev/null
+++ b/.changeset/fn-7695-cursor-cli-padding.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: Fix misaligned padding on the Cursor CLI authentication card.
+category: fix
+dev: Wraps `CursorCliProviderCard`'s compact status line + binary-path control in a padded `.cursor-cli-provider-card__body` to match the header inset, mirroring the Claude CLI card's `.auth-provider-cli-details-body`.
diff --git a/packages/dashboard/app/components/CursorCliProviderCard.css b/packages/dashboard/app/components/CursorCliProviderCard.css
index cbe7824d06..3543b37d5d 100644
--- a/packages/dashboard/app/components/CursorCliProviderCard.css
+++ b/packages/dashboard/app/components/CursorCliProviderCard.css
@@ -5,6 +5,20 @@
align-items: center;
}
+/*
+FNXC:CursorCli 2026-07-08-00:00:
+Compact card body must be inset to match `.auth-provider-header`'s horizontal padding
+(`var(--space-sm) var(--space-md)`), mirroring `.auth-provider-cli-details-body` used by the
+Claude CLI card (`0 16px 12px` desktop / `0 14px 12px` mobile). Without this, the status line and
+binary-path control render flush against the card's left/right/bottom edges. See FN-7695.
+*/
+.cursor-cli-provider-card__body {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-sm);
+ padding: 0 var(--space-md) var(--space-sm);
+}
+
.cursor-cli-binary-path-control {
display: grid;
gap: var(--space-xs);
@@ -45,4 +59,9 @@
.cursor-cli-binary-path-input {
width: 100%;
}
+
+ /* FNXC:CursorCli 2026-07-08-00:00: mirror `.auth-provider-cli-details-body`'s 14px mobile inset. */
+ .cursor-cli-provider-card__body {
+ padding: 0 var(--space-sm) var(--space-sm);
+ }
}
diff --git a/packages/dashboard/app/components/CursorCliProviderCard.tsx b/packages/dashboard/app/components/CursorCliProviderCard.tsx
index 4a13b1a7ad..4955012916 100644
--- a/packages/dashboard/app/components/CursorCliProviderCard.tsx
+++ b/packages/dashboard/app/components/CursorCliProviderCard.tsx
@@ -167,8 +167,18 @@ export function CursorCliProviderCard({ authenticated, compact = false, onToggle
{actions}
- {statusText}
- {binaryPathControl}
+ {/*
+ FNXC:CursorCli 2026-07-08-00:00:
+ `.auth-provider-card` has no padding of its own (padding:0; overflow:hidden) — only
+ `.auth-provider-header` supplies the horizontal inset via `padding: var(--space-sm) var(--space-md)`.
+ The status line and binary-path control below the header must be wrapped in a padded body
+ so they line up with the header instead of rendering flush against the card edges,
+ mirroring `.auth-provider-cli-details-body` on the Claude CLI card. See FN-7695.
+ */}
+
+ {statusText}
+ {binaryPathControl}
+
);
}
diff --git a/packages/dashboard/app/components/__tests__/CursorCliProviderCard.test.tsx b/packages/dashboard/app/components/__tests__/CursorCliProviderCard.test.tsx
new file mode 100644
index 0000000000..d94a536f4a
--- /dev/null
+++ b/packages/dashboard/app/components/__tests__/CursorCliProviderCard.test.tsx
@@ -0,0 +1,96 @@
+import { describe, expect, it, vi, beforeEach } from "vitest";
+import { render, screen, waitFor } from "@testing-library/react";
+import { CursorCliProviderCard } from "../CursorCliProviderCard";
+
+const fetchCursorCliStatus = vi.fn();
+const setCursorCliBinaryPath = vi.fn();
+const setCursorCliEnabled = vi.fn();
+
+vi.mock("../../api", () => ({
+ fetchCursorCliStatus: (...args: unknown[]) => fetchCursorCliStatus(...args),
+ setCursorCliBinaryPath: (...args: unknown[]) => setCursorCliBinaryPath(...args),
+ setCursorCliEnabled: (...args: unknown[]) => setCursorCliEnabled(...args),
+}));
+
+const baseStatus = {
+ binary: { available: true, version: "1.0.0", binaryPath: "/usr/local/bin/cursor-agent", probeDurationMs: 5 },
+ enabled: true,
+ binaryPath: "/usr/local/bin/cursor-agent",
+ extension: null,
+ ready: true,
+};
+
+/*
+FNXC:CursorCli 2026-07-08-00:00:
+Regression coverage for FN-7695: the compact card's below-header content (status line +
+binary-path control) must be nested inside `.cursor-cli-provider-card__body`
+(data-testid="cursor-cli-provider-card-body") rather than being a bare direct child of
+`.auth-provider-card`, so it inherits the same horizontal/bottom inset as the header. The
+non-compact onboarding layout must NOT render this wrapper (its content already lives in the
+padded `.onboarding-provider-card__body`).
+*/
+describe("CursorCliProviderCard", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ fetchCursorCliStatus.mockResolvedValue(baseStatus);
+ setCursorCliEnabled.mockResolvedValue({ enabled: true, binaryPath: baseStatus.binaryPath, restartRequired: true });
+ setCursorCliBinaryPath.mockResolvedValue({ enabled: true, binaryPath: baseStatus.binaryPath, restartRequired: true });
+ });
+
+ it("wraps compact status line + binary-path control in the padded body wrapper", async () => {
+ render();
+
+ const body = await screen.findByTestId("cursor-cli-provider-card-body");
+ expect(body).toHaveClass("cursor-cli-provider-card__body");
+
+ // Status line must be inside the body wrapper.
+ const status = await screen.findByText(/Connected/i);
+ expect(body).toContainElement(status);
+
+ // Binary-path control (label + input) must be inside the body wrapper too.
+ const label = screen.getByText("Cursor CLI binary path");
+ expect(body).toContainElement(label);
+ const input = screen.getByLabelText("Cursor CLI binary path");
+ expect(body).toContainElement(input);
+
+ // The wrapper must be a child of the card root, not a sibling bare child alongside it.
+ const card = screen.getByTestId("cursor-cli-provider-card");
+ expect(card).toContainElement(body);
+ });
+
+ it("keeps the body wrapper present before the status probe resolves (Probing…)", async () => {
+ fetchCursorCliStatus.mockReturnValue(new Promise(() => {}));
+ render();
+
+ const body = await screen.findByTestId("cursor-cli-provider-card-body");
+ const status = await screen.findByText(/Probing local CLI/i);
+ expect(body).toContainElement(status);
+ });
+
+ it("keeps the body wrapper present when a pathMessage is shown after a failed save", async () => {
+ setCursorCliBinaryPath.mockRejectedValueOnce(new Error("binary not found"));
+ const { default: userEvent } = await import("@testing-library/user-event");
+ const user = userEvent.setup();
+
+ render();
+ const input = await screen.findByLabelText("Cursor CLI binary path");
+ await user.clear(input);
+ await user.type(input, "/tmp/does-not-exist");
+
+ const saveButton = screen.getByRole("button", { name: /Save & Test/i });
+ await user.click(saveButton);
+
+ const errorText = await screen.findByText("binary not found");
+ const body = screen.getByTestId("cursor-cli-provider-card-body");
+ expect(body).toContainElement(errorText);
+ });
+
+ it("does not render the body wrapper in the non-compact onboarding layout", async () => {
+ render();
+
+ const card = await screen.findByTestId("cursor-cli-provider-card");
+ expect(card).toHaveClass("onboarding-provider-card");
+ await waitFor(() => expect(fetchCursorCliStatus).toHaveBeenCalled());
+ expect(screen.queryByTestId("cursor-cli-provider-card-body")).not.toBeInTheDocument();
+ });
+});