From 0d2dcb3a404ebc0662820e9259e13c4c75a35138 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 17 Jul 2026 00:04:41 -0700 Subject: [PATCH] FN-8189: fix mobile task-detail scrollbar gutter Hide the mobile task-detail scrollbar to keep content insets visually symmetric. - Preserve touch scrolling while removing the mobile non-overlay scrollbar track - Add responsive regression coverage for scrollbar and inset behavior - Add a patch changeset for the mobile layout fix Files changed: .../fn-8189-task-detail-mobile-scrollbar-gutter.md | 7 +++ .../dashboard/app/components/TaskDetailModal.css | 12 +++++ ...etailModal.responsive-and-dependencies.test.tsx | 55 ++++++++++++++++++++++ 3 files changed, 74 insertions(+) Fusion-Task-Id: FN-8189 Fusion-Task-Lineage: 3a9300f7-9732-417b-95b0-b65a79efaaf6 Co-authored-by: Fusion (runfusion.ai) --- ...189-task-detail-mobile-scrollbar-gutter.md | 7 +++ .../app/components/TaskDetailModal.css | 12 ++++ ...Modal.responsive-and-dependencies.test.tsx | 55 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 .changeset/fn-8189-task-detail-mobile-scrollbar-gutter.md diff --git a/.changeset/fn-8189-task-detail-mobile-scrollbar-gutter.md b/.changeset/fn-8189-task-detail-mobile-scrollbar-gutter.md new file mode 100644 index 0000000000..7ac31f52a8 --- /dev/null +++ b/.changeset/fn-8189-task-detail-mobile-scrollbar-gutter.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Fix lopsided right gutter in the task detail view on mobile. +category: fix +dev: Hides the mobile `.detail-body` scrollbar while preserving touch scrolling and the desktop scrollbar. diff --git a/packages/dashboard/app/components/TaskDetailModal.css b/packages/dashboard/app/components/TaskDetailModal.css index 5545a1b6b6..64c870ce9f 100644 --- a/packages/dashboard/app/components/TaskDetailModal.css +++ b/packages/dashboard/app/components/TaskDetailModal.css @@ -3427,10 +3427,22 @@ Live and Feed Activity expansion overlays the content instead of reserving a too margin-left: 0; } + /* + FNXC:TaskDetailModalResponsive 2026-07-16-00:00: + FN-8189 hides the mobile `.detail-body` scrollbar so a non-overlay vertical + track cannot reserve right-edge layout space and recreate the lopsided gutter + fixed by FN-8166. Keep touch scrolling automatic; desktop retains its thin, + visible scrollbar outside this breakpoint. + */ .detail-body { padding: calc(var(--space-md) + var(--space-xs) / 2); overflow-x: hidden; overflow-y: auto; + scrollbar-width: none; + } + + .detail-body::-webkit-scrollbar { + display: none; } .detail-title { 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 12a502450a..a16dda763c 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 @@ -732,6 +732,61 @@ describe("TaskDetailModal", () => { } }); + it("FN-8189 hides only the mobile detail-body scrollbar to preserve symmetric task-detail insets", () => { + const css = readDashboardStylesSource(); + const baseDetailBodyBlock = getExactCssRuleBlock(css, ".detail-body"); + const baseScrollbarBlock = getExactCssRuleBlock(css, ".detail-body::-webkit-scrollbar"); + const mobileBlock = getCssAtRuleBlockContainingExactRule(css, "@media (max-width: 768px)", ".detail-body"); + const mobileDetailBodyBlock = getExactCssRuleBlock(mobileBlock, ".detail-body"); + const mobileScrollbarBlock = getExactCssRuleBlock(mobileBlock, ".detail-body::-webkit-scrollbar"); + const mobileActivityBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity"); + const mobileInterventionsBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity--interventions"); + const mobilePrBlock = getExactCssRuleBlock( + getCssAtRuleBlockContainingExactRule(css, "@media (max-width: 768px)", ".detail-pr-tab"), + ".detail-pr-tab", + ); + const embeddedBodyBlock = getExactCssRuleBlock( + css, + ".task-detail-content--embedded .modal-header,\n.task-detail-content--embedded .detail-body,\n.task-detail-content--embedded .detail-tabs,\n.task-detail-content--embedded .modal-actions", + ); + const allMobileCss = getCssAtRuleBlocks(css, "@media (max-width: 768px)").join("\n"); + + expect(baseDetailBodyBlock).toContain("scrollbar-width: thin;"); + expect(baseScrollbarBlock).toContain("width: 6px;"); + expect(mobileDetailBodyBlock).toContain("padding: calc(var(--space-md) + var(--space-xs) / 2);"); + expect(mobileDetailBodyBlock).toContain("overflow-x: hidden;"); + expect(mobileDetailBodyBlock).toContain("overflow-y: auto;"); + expect(mobileDetailBodyBlock).toContain("scrollbar-width: none;"); + expect(mobileScrollbarBlock).toContain("display: none;"); + + expect(mobileActivityBlock).toContain("padding-inline-end: 0;"); + expect(mobileBlock).toContain("padding-inline-end: calc(var(--space-2xl) + var(--space-sm));"); + expect(mobileInterventionsBlock).toContain("padding-inline-end: 0;"); + expect(mobilePrBlock.trim()).toBe("gap: var(--space-md);"); + expect(allMobileCss).not.toMatch(/\.task-changes-tab\s*\{[^}]*\bpadding(?:-[\w-]+)?\s*:/); + expect(embeddedBodyBlock).toContain("width: 100%;"); + expect(embeddedBodyBlock).toContain("min-width: 0;"); + expect(embeddedBodyBlock).toContain("max-width: 100%;"); + + for (const selector of [ + ".detail-section", + ".detail-section--plan-prompt", + ".detail-section--original-prompt", + ".detail-body--chat", + ".detail-section--chat", + ".detail-body--agent-log", + ".detail-body--planner-chat", + ]) { + const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const exactSelector = `${escapedSelector}(?![-\\w])`; + const declarations = [...neutralizeCssCommentBraces(allMobileCss).matchAll(new RegExp(`${exactSelector}[^{}]*\\{([^{}]*)\\}`, "g"))] + .map((match) => match[1]) + .join("\n"); + expect(declarations, `${selector} mobile right inset`).not.toMatch(/\bpadding-(?:inline-end|right)\s*:/); + expect(declarations, `${selector} mobile asymmetric padding`).not.toMatch(/\bpadding\s*:/); + } + }); + it("renders responsive structural classes (modal-lg, overlay, spacer, tabs, detail-body)", () => { const { container } = render(