From fef9692cba6ebb805d037f56c4fb3b0315040873 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Mon, 3 Aug 2026 12:49:23 -0700 Subject: [PATCH] FN-8766: align task detail popup inset Align the desktop Task Detail popup header and content edges without losing resize access. - Remove the Task Detail desktop body gutter while preserving shared scrollbar clearance. - Move Task Detail east resize targets outside the painted shell and retain mobile clipping. - Add geometry regression coverage and a patch changeset. Files changed: .changeset/fn-8766-task-detail-popup-spacing.md | 7 +++ .../dashboard/app/components/FloatingWindow.css | 30 +++++++++++ .../components/__tests__/FloatingWindow.test.tsx | 32 +++++++----- ...etailModal.responsive-and-dependencies.test.tsx | 58 ++++++++++++++++++++++ 4 files changed, 115 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-8766 Fusion-Task-Lineage: b4d23a5e-51d0-45cb-8cbb-4fefb81fa09f Co-authored-by: Fusion (runfusion.ai) --- .../fn-8766-task-detail-popup-spacing.md | 7 +++ .../app/components/FloatingWindow.css | 30 ++++++++++ .../__tests__/FloatingWindow.test.tsx | 32 ++++++---- ...Modal.responsive-and-dependencies.test.tsx | 58 +++++++++++++++++++ 4 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 .changeset/fn-8766-task-detail-popup-spacing.md diff --git a/.changeset/fn-8766-task-detail-popup-spacing.md b/.changeset/fn-8766-task-detail-popup-spacing.md new file mode 100644 index 0000000000..122aaf14c1 --- /dev/null +++ b/.changeset/fn-8766-task-detail-popup-spacing.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Align the floating Task Detail popup close control with its header edge. +category: fix +dev: Moves only Task Detail desktop resize targets outboard while retaining shared scrollbar clearance. diff --git a/packages/dashboard/app/components/FloatingWindow.css b/packages/dashboard/app/components/FloatingWindow.css index e886df6196..4c73392c14 100644 --- a/packages/dashboard/app/components/FloatingWindow.css +++ b/packages/dashboard/app/components/FloatingWindow.css @@ -261,6 +261,35 @@ Phone sheets are strictly below 768px. At exactly 768px this query must not matc FNXC:ChatModal 2026-06-22-14:49: On mobile/narrow app viewports, opening Quick Chat should present the full Chat modal as a full-screen sheet instead of a small draggable desktop window. Scope this to the chat FloatingWindow and override the inline desktop geometry only at the mobile breakpoint; desktop pop-out behavior remains movable/resizable. */ +/* +FNXC:TaskDetailLayout 2026-08-03-19:36: +FN-8766 keeps the desktop Task Detail popup's visible header/content edge symmetric while +preserving the shared body's scrollbar clearance for every other FloatingWindow consumer. This +headerless host owns its close button, so remove only its visible body gutter and move its desktop +east resize hit areas outside the painted shell; the scrollbar remains fully usable and the close +control does not sit behind a resize target. Tablet retains its existing touch geometry and phones +remain full-screen sheets below. +*/ +.floating-window--task-detail:not(.floating-window--tablet-viewport) { + overflow: visible; +} + +.floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__body { + margin-inline-end: 0; +} + +.floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__resize-handle--e { + right: calc(var(--space-sm) * -1); +} + +.floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__resize-handle--ne { + right: calc(var(--space-lg) * -1); +} + +.floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__resize-handle--se { + right: calc(var(--space-lg) * -1); +} + /* FNXC:ModalTouchGeometry 2026-07-26-13:25: FN-8606 migrated core and workflow dialogs share FloatingWindow's pointer contract. Their @@ -452,6 +481,7 @@ all floating affordances so persisted desktop geometry cannot imply a draggable */ .floating-window--task-detail { inset: 0 !important; + overflow: hidden !important; width: 100vw !important; height: 100dvh !important; min-width: 0 !important; diff --git a/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx b/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx index ca79dd8c72..76e06a9b8a 100644 --- a/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx +++ b/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx @@ -161,12 +161,14 @@ describe("FloatingWindow", () => { expect(cssRuleContaining(floatingWindowCss, ".floating-window__resize-handle--ne", "right: 0;")).toContain("right: 0;"); expect(cssRuleContaining(floatingWindowCss, ".floating-window__resize-handle--se", "right: 0;")).toContain("right: 0;"); - // No shared caller may move a right handle back into the reserved scrollbar - // gutter, nor override the body gutter, AT DESKTOP WIDTHS. Mobile full-screen - // sheet overrides (inside @media) are legitimate and excluded from this scan. + /* + FNXC:TaskDetailLayout 2026-08-03-19:36: + FN-8766 is the sole desktop exception: its header-owned close control requires a symmetric + painted edge, so its gutter is removed only while its east resize targets move outboard. + Every unrelated consumer keeps the shared scrollbar-clearance geometry. + */ const desktopAppCss = stripAtMediaBlocks(allAppCss); for (const callerClass of [ - "floating-window--task-detail", "floating-window--automation", "floating-window--mission-interview", "floating-window--pr-create", @@ -182,14 +184,20 @@ describe("FloatingWindow", () => { expect(bodyRules.some((rule) => /margin-inline-end\s*:/.test(rule)), callerClass).toBe(false); } + const desktopTaskBody = cssRuleContaining( + desktopAppCss, + ".floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__body", + "margin-inline-end", + ); + expect(desktopTaskBody).toContain("margin-inline-end: 0;"); + expect(cssRuleContaining(desktopAppCss, ".floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__resize-handle--e", "right")).toContain("right: calc(var(--space-sm) * -1);"); + expect(cssRuleContaining(desktopAppCss, ".floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__resize-handle--ne", "right")).toContain("right: calc(var(--space-lg) * -1);"); + expect(cssRuleContaining(desktopAppCss, ".floating-window--task-detail:not(.floating-window--tablet-viewport) .floating-window__resize-handle--se", "right")).toContain("right: calc(var(--space-lg) * -1);"); + /* - FNXC:MobileTaskPopups 2026-07-17-08:20: - Regression guard for the mobile task-detail left-shift fix: the full-screen - task-detail sheet hides all resize handles, so FN-8015's inherited - `margin-inline-end: var(--space-lg)` body gutter only added dead space on the - right and shifted the whole panel left. The mobile breakpoint must zero it so - `.detail-body`'s own padding defines both insets equally. This is the sole - legitimate body-gutter override and lives only inside the mobile @media block. + FNXC:MobileTaskPopups 2026-08-03-19:36: + The full-screen task-detail sheet hides every resize handle, so the inherited gutter must + remain zero and the desktop outboard-target host must resume clipping for sheet geometry. */ const mobileTaskDetailBody = cssRuleContaining( allAppCss, @@ -197,7 +205,7 @@ describe("FloatingWindow", () => { "margin-inline-end", ); expect(mobileTaskDetailBody).toContain("margin-inline-end: 0;"); - expect(cssRulesForClass(desktopAppCss, "floating-window--task-detail").some((rule) => rule.includes("floating-window__body"))).toBe(false); + expect(cssRuleContaining(allAppCss, ".floating-window--task-detail", "overflow: hidden !important;")).toContain("overflow: hidden !important;"); /* FNXC:ModalTouchGeometry 2026-08-01-03:48: diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx index 053bc1ae12..829c3f6266 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx @@ -24,6 +24,7 @@ import { setupTaskDetailModalHooks, } from "./TaskDetailModal.test-helpers"; import { TaskDetailModal, TaskDetailContent } from "../TaskDetailModal"; +import { FloatingWindow } from "../FloatingWindow"; import { assertModalGeometryRecoveryAndSheetContracts, assertRenderedModalTouchGeometry, @@ -856,6 +857,63 @@ describe("TaskDetailModal", () => { } }); + it("keeps the floating task header symmetric without sacrificing its resize targets", () => { + const floatingCss = readFileSync(resolve(__dirname, "../FloatingWindow.css"), "utf8"); + const desktopTaskSelector = ".floating-window--task-detail:not(.floating-window--tablet-viewport)"; + const taskPopupBody = getExactCssRuleBlock(floatingCss, `${desktopTaskSelector} .floating-window__body`); + const sharedBody = getExactCssRuleBlock(floatingCss, ".floating-window__body"); + const header = getExactCssRuleBlock(readDashboardStylesSource(), ".modal-header"); + const eastResize = getExactCssRuleBlock(floatingCss, `${desktopTaskSelector} .floating-window__resize-handle--e`); + const northEastResize = getExactCssRuleBlock(floatingCss, `${desktopTaskSelector} .floating-window__resize-handle--ne`); + const southEastResize = getExactCssRuleBlock(floatingCss, `${desktopTaskSelector} .floating-window__resize-handle--se`); + const onRequestClose = vi.fn(); + + /* + FNXC:TaskDetailLayout 2026-08-03-19:36: + The shared body reserves desktop scrollbar clearance. Task Detail moves only its resize + hit areas outboard, so the embedded header retains matching tokenized edges. + */ + expect(sharedBody).toContain("margin-inline-end: var(--space-lg);"); + expect(taskPopupBody).toContain("margin-inline-end: 0;"); + expect(header).toContain("padding: var(--modal-padding);"); + expect(eastResize).toContain("right: calc(var(--space-sm) * -1);"); + expect(northEastResize).toContain("right: calc(var(--space-lg) * -1);"); + expect(southEastResize).toContain("right: calc(var(--space-lg) * -1);"); + + const { baseElement, unmount } = render( + + + , + ); + + const popup = baseElement.querySelector("[data-testid='floating-window-task-detail-inset']"); + const close = screen.getByRole("button", { name: "Close" }); + expect(popup).toHaveClass("floating-window--task-detail"); + expect(popup?.querySelector(".task-detail-content--embedded > .modal-header")).toContainElement(close); + expect(close).toHaveClass("task-detail-floating-close"); + expect(popup?.querySelectorAll(".floating-window__resize-handle")).toHaveLength(8); + fireEvent.click(close); + expect(onRequestClose).toHaveBeenCalledTimes(1); + unmount(); + }); + it("uses production FloatingWindow geometry for touch drag, eight-direction resize, recovery, and sheets", () => { const mount = () => render(