From d585edbc92e87622597e4f4cf07ab080246ad891 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 8 Jul 2026 18:04:44 -0700 Subject: [PATCH] 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) --- .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(-) create mode 100644 .changeset/fn-7695-cursor-cli-padding.md create mode 100644 packages/dashboard/app/components/__tests__/CursorCliProviderCard.test.tsx 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(); + }); +});