diff --git a/services/web/frontend/js/features/ide-react/components/layout/main-layout.tsx b/services/web/frontend/js/features/ide-react/components/layout/main-layout.tsx index 40b92eb9d5..e6d1346d78 100644 --- a/services/web/frontend/js/features/ide-react/components/layout/main-layout.tsx +++ b/services/web/frontend/js/features/ide-react/components/layout/main-layout.tsx @@ -41,7 +41,11 @@ function detectMobile() { export default function MainLayout() { const [resizing, setResizing] = useState(false) - const { resizing: railResizing } = useRailContext() + const { + resizing: railResizing, + handlePaneCollapse: collapseRail, + isOpen: railIsOpen, + } = useRailContext() const { togglePdfPane, handlePdfPaneExpand, @@ -65,6 +69,16 @@ export default function MainLayout() { } }, []) + // On mobile, collapse the file-tree rail so editor+PDF panels get full width. + useEffect(() => { + if (isMobile && railIsOpen) { + collapseRail() + } + // Only run on mount and when isMobile changes — don't re-collapse if the + // user manually re-opens the rail during the session. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isMobile]) + // verticalSplit is always vertical; sideBySide becomes vertical on mobile const isVertical = pdfLayout === 'verticalSplit' || diff --git a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx index e132d6964f..4e91937e6f 100644 --- a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx +++ b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx @@ -370,7 +370,7 @@ export function ProjectListLumiere() { ))} - {/* Mobile-only: filter pills + zoom control */} + {/* Mobile-only: filter pills */}
))}
-
- {MOBILE_ZOOM_OPTIONS.map(({ value, label }) => ( - - ))} -
- + {/* On mobile: new project + zoom share a row below the search bar */} +
+ + {/* Zoom control: on desktop it sits in the title row; on mobile it moves here */} +
+ {MOBILE_ZOOM_OPTIONS.map(({ value, label }) => ( + + ))} +
+
{selectedProjects.length > 0 && ( diff --git a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss index 0d49bdb39e..74858b1df2 100644 --- a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss +++ b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss @@ -341,6 +341,12 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi flex-wrap: wrap; } + // On desktop the inner row wrapper is transparent — new project button flows + // naturally inside the flex row alongside the search form. + .lumiere-header-actions-row { + display: contents; + } + // Search bar — wide enough to show the full placeholder (relaxed on mobile) form.project-search .form-control { min-width: 360px; @@ -894,25 +900,37 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi @media (max-width: 767px) { // Stack the header vertically so nothing overflows the viewport width. - // Without this, lumiere-header-actions (flex-shrink:0) forces the flex - // container wider than the screen. .lumiere-header { flex-direction: column; align-items: stretch; gap: 0.5rem; } + // Stack header-actions vertically: search on its own row, then new-project+zoom .lumiere-header-actions { width: 100%; - flex-shrink: 1; - min-width: 0; + flex-direction: column; + align-items: stretch; + gap: 0.5rem; form.project-search { - flex: 1; + width: 100%; min-width: 0; } } + // New project button + zoom buttons share a row below the search bar + .lumiere-header-actions-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + + .new-project-dropdown { + flex: 1; + } + } + // Compact row: 2-line layout // Row 1: [checkbox] [name+tags] [⋮ action] // Row 2: [checkbox] [format · owner · date] @@ -977,12 +995,9 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi } } - // ── Mobile toolbar: filter tabs + zoom control ─────────────────────────── - // Two-row layout: filter tabs on top (wrapping), zoom on bottom-right. - // flex-wrap:wrap on the filter container is unconditionally safe (no - // overflow-x:auto ancestor chain needed). flex:1 1 auto on each tab makes - // all tabs in the same row equal-width regardless of label length, so the - // rows align cleanly without producing page overflow on narrow screens. + // ── Mobile toolbar: filter tabs ────────────────────────────────────────── + // flex-wrap:wrap on the filter container makes all tabs in the same row + // equal-width regardless of label length. .lumiere-mobile-toolbar { flex-direction: column; @@ -999,9 +1014,9 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi width: 100%; } + // Zoom buttons when rendered inside lumiere-header-actions-row on mobile .lumiere-mobile-zoom { flex-shrink: 0; - align-self: flex-end; } // Filter tabs: equal-width per row, teal underline on active