FN-7695: fix padding on Cursor CLI auth card

Fixes cramped padding on the Cursor CLI provider auth card in the dashboard.

- Adjusted CSS spacing/padding rules in CursorCliProviderCard.css
- Updated CursorCliProviderCard.tsx to apply the corrected layout
- Added regression tests covering the card's rendering/padding behavior
- Added a changeset documenting the patch-level fix

Files changed:
 .changeset/fn-7695-cursor-cli-padding.md           |  7 ++
 .../app/components/CursorCliProviderCard.css       | 19 +++++
 .../app/components/CursorCliProviderCard.tsx       | 14 +++-
 .../__tests__/CursorCliProviderCard.test.tsx       | 96 ++++++++++++++++++++++
 4 files changed, 134 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7695

Fusion-Task-Lineage: 565a7e70-347c-4cbf-8ba1-a672b57c0021

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-08 18:04:44 -07:00
parent 0faf4ede57
commit d585edbc92
4 changed files with 134 additions and 2 deletions

View File

@@ -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`.

View File

@@ -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);
}
}

View File

@@ -167,8 +167,18 @@ export function CursorCliProviderCard({ authenticated, compact = false, onToggle
</div>
<div className="auth-provider-cli-actions">{actions}</div>
</div>
<small className="settings-muted">{statusText}</small>
{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.
*/}
<div className="cursor-cli-provider-card__body" data-testid="cursor-cli-provider-card-body">
<small className="settings-muted">{statusText}</small>
{binaryPathControl}
</div>
</div>
);
}

View File

@@ -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(<CursorCliProviderCard authenticated compact />);
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(<CursorCliProviderCard authenticated={false} compact />);
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(<CursorCliProviderCard authenticated compact />);
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(<CursorCliProviderCard authenticated />);
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();
});
});