diff --git a/packages/ui/src/components/layout/MainLayout.tsx b/packages/ui/src/components/layout/MainLayout.tsx index 04b8bf23..891e16fd 100644 --- a/packages/ui/src/components/layout/MainLayout.tsx +++ b/packages/ui/src/components/layout/MainLayout.tsx @@ -457,13 +457,30 @@ export const MainLayout: React.FC = () => { )} - {mobileLeftDrawerVisible && ( - - - - - - )} + {/* Always mount SessionSidebar on mobile to match desktop behavior. + Conditional mount (mobileLeftDrawerVisible && ...) caused a + data-loading cascade on every drawer open: paginated sessions + fetch, worktree discovery, repo status, PR status, and 10+ memo + recomputations. On Android PWA this manifested as a >10s delay + before the drawer became interactive (issue #1695). Visibility is + controlled by the leftDrawerX transform (off-screen when closed). + The invisible class matters when fully hidden: leftDrawerWidth is + not recomputed on resize/rotation, so a closed drawer translated by + the old width could otherwise peek into the viewport; it also keeps + the off-screen sidebar out of the tab order and skips painting it. */} + + + + + {mobileRightDrawerVisible && ( diff --git a/packages/ui/src/components/layout/__tests__/mainLayoutMobileSidebarMount.test.ts b/packages/ui/src/components/layout/__tests__/mainLayoutMobileSidebarMount.test.ts new file mode 100644 index 00000000..d3d977ab --- /dev/null +++ b/packages/ui/src/components/layout/__tests__/mainLayoutMobileSidebarMount.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, test } from 'bun:test'; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const mainLayoutSource = readFileSync( + join(__dirname, '..', 'MainLayout.tsx'), + 'utf-8', +); + +describe('MainLayout mobile SessionSidebar mount (issue #1695 regression guard)', () => { + test('mobile SessionSidebar is not conditionally mounted on mobileLeftDrawerVisible', () => { + const mobileSidebarIndex = mainLayoutSource.indexOf(' { + const desktopSidebarIndex = mainLayoutSource.indexOf(''); + expect(desktopSidebarIndex).toBeGreaterThan(-1); + + const windowStart = Math.max(0, desktopSidebarIndex - 300); + const precedingWindow = mainLayoutSource.slice(windowStart, desktopSidebarIndex); + + expect(precedingWindow).toContain('