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