diff --git a/.changeset/fn-8474-tablet-task-popup-gutter.md b/.changeset/fn-8474-tablet-task-popup-gutter.md new file mode 100644 index 0000000000..860b6ecc9d --- /dev/null +++ b/.changeset/fn-8474-tablet-task-popup-gutter.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Remove excess right padding from task popups on tablets. +category: fix +dev: Tablet task popups no longer reserve desktop resize-handle scrollbar clearance. diff --git a/packages/dashboard/app/components/FloatingWindow.css b/packages/dashboard/app/components/FloatingWindow.css index 6902dff866..a812a8012d 100644 --- a/packages/dashboard/app/components/FloatingWindow.css +++ b/packages/dashboard/app/components/FloatingWindow.css @@ -106,6 +106,20 @@ Headerless task pop-outs still need a visible drag affordance. The embedded task overflow: hidden; } +/* +FNXC:FloatingWindow 2026-07-21-00:00: +Tablet task-detail popups prioritize symmetric content insets over desktop resize affordances. Their body removes the desktop scrollbar-clearance gutter only while all resize handles are suppressed; desktop retains the clearance and resize-safe handles, and the delegated task header remains draggable. +*/ +@media (min-width: 769px) and (max-width: 1024px) { + .floating-window--task-detail .floating-window__body { + margin-inline-end: 0; + } + + .floating-window--task-detail .floating-window__resize-handle { + display: none; + } +} + /* 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. diff --git a/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx b/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx index e603e352d0..d86269dc37 100644 --- a/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx +++ b/packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx @@ -70,6 +70,20 @@ function stripAtMediaBlocks(css: string): string { return out; } +function mediaBlockFor(css: string, query: string): string { + const start = css.indexOf(`@media ${query}`); + if (start === -1) return ""; + const open = css.indexOf("{", start); + let depth = 1; + let index = open + 1; + while (index < css.length && depth > 0) { + if (css[index] === "{") depth++; + else if (css[index] === "}") depth--; + index++; + } + return css.slice(open + 1, index - 1); +} + function setSheetViewport(isSheetWidth: boolean): void { vi.stubGlobal("matchMedia", vi.fn((query: string) => ({ matches: query === "(max-width: 768px)" ? isSheetWidth : query === "(max-height: 480px)", @@ -182,6 +196,47 @@ describe("FloatingWindow", () => { expect(cssRuleFor(floatingWindowCss, ".floating-window--chat.floating-window--headerless .floating-window__body")).toContain("overflow: hidden;"); }); + it("removes only tablet task-detail resize clearance and handles for empty and populated popups", () => { + const tabletBlock = mediaBlockFor(floatingWindowCss, "(min-width: 769px) and (max-width: 1024px)"); + const mobileBlock = mediaBlockFor(floatingWindowCss, "(max-width: 768px)"); + const desktopCss = stripAtMediaBlocks(floatingWindowCss); + + expect(cssRuleFor(tabletBlock, ".floating-window--task-detail .floating-window__body")).toContain("margin-inline-end: 0;"); + expect(cssRuleFor(tabletBlock, ".floating-window--task-detail .floating-window__resize-handle")).toContain("display: none;"); + expect(desktopCss.match(/(?:^|\n)\.floating-window__body\s*\{[^}]*\}/)?.[0]).toContain("margin-inline-end: var(--space-lg);"); + expect(cssRulesForClass(desktopCss, "floating-window--task-detail").some((rule) => /margin-inline-end|display:\s*none/.test(rule))).toBe(false); + + // Mobile remains a full-screen sheet with its independent no-gutter/no-handle contract. + expect(cssRuleFor(mobileBlock, ".floating-window--task-detail")).toContain("width: 100vw !important;"); + expect(cssRuleFor(mobileBlock, ".floating-window--task-detail")).toContain("border-radius: 0;"); + expect(cssRuleFor(mobileBlock, ".floating-window--task-detail .floating-window__body")).toContain("margin-inline-end: 0;"); + expect(cssRuleFor(mobileBlock, ".floating-window--task-detail .floating-window__resize-handle")).toContain("display: none;"); + expect(mobileBlock).toContain(".floating-window--task-detail .task-detail-content--embedded > .modal-header"); + expect(mobileBlock).toContain("cursor: default;"); + expect(mobileBlock).toContain("touch-action: auto;"); + + Object.defineProperty(window, "innerWidth", { configurable: true, value: 834 }); + Object.defineProperty(window, "innerHeight", { configurable: true, value: 1112 }); + setSheetViewport(false); + render( + <> + {}} className="floating-window--task-detail"> +
+ + {}} className="floating-window--task-detail"> +
{Array.from({ length: 40 }, (_, index) =>

Scrollable detail {index}

)}
+
+ , + ); + + for (const key of ["tablet-empty", "tablet-populated"]) { + const panel = screen.getByTestId(`floating-window-${key}`); + expect(panel).toHaveClass("floating-window--task-detail"); + expect(Number.parseFloat(panel.style.width)).toBeLessThanOrEqual(834); + expect(screen.getByTestId(`floating-window-body-${key}`)).toHaveClass("floating-window__body"); + } + }); + it("keeps task-detail long content clear of right handles while preserving short-content right-edge resize", () => { const longContent = Array.from({ length: 40 }, (_, index) =>

Scrollable task detail {index}

); const { unmount } = render( @@ -950,6 +1005,6 @@ describe("FloatingWindow", () => { expect(mobileBlock).toContain("width: 100vw !important;"); expect(mobileBlock).toContain("height: 100dvh !important;"); expect(mobileBlock).toContain(".floating-window--chat .floating-window__resize-handle"); - expect(floatingWindowCss).not.toMatch(/@media\s*\(min-width:\s*769px\)[\s\S]*\.floating-window--chat[\s\S]*100dvh/); + expect(mediaBlockFor(floatingWindowCss, "(min-width: 769px) and (max-width: 1024px)")).not.toContain(".floating-window--chat"); }); });