From 9dd485c8a246edb58cf8c20dabf68d90b7ced475 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 20 Oct 2025 18:38:46 -0400 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=A4=96=20Add=20mobile-responsive=20si?= =?UTF-8?q?debar=20with=20overlay=20and=20hamburger=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add responsive sidebar that becomes an overlay on mobile (≤768px) - Add hamburger menu button visible only on mobile when sidebar collapsed - Add backdrop overlay that closes sidebar on tap (mobile only) - Auto-collapse sidebar by default on mobile viewports - Update App, AIView, and RightSidebar layouts for mobile stacking - Improve touch interactions with larger tap targets on mobile - Add proper viewport handling and text size adjustment UX improvements: - Sidebar slides in/out with smooth animations on mobile - Overlay provides visual focus and intuitive dismissal - Chat and panels stack vertically on small screens - Hamburger button positioned for easy thumb access - No min-width constraints on mobile for better space usage Generated with `cmux` --- src/App.tsx | 44 +++++++++++++++- src/components/AIView.tsx | 18 +++++++ src/components/LeftSidebar.tsx | 92 +++++++++++++++++++++++++++++++-- src/components/RightSidebar.tsx | 10 ++++ 4 files changed, 158 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e8c1f164a..91472d03f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -59,6 +59,29 @@ const globalStyles = css` -moz-osx-font-smoothing: grayscale; } + /* Mobile: Improve touch interactions */ + @media (max-width: 768px) { + html { + /* Prevent text size adjustment on orientation change */ + -webkit-text-size-adjust: 100%; + /* Improve tap responsiveness */ + touch-action: manipulation; + } + + body { + /* Slightly larger font for better readability on mobile */ + font-size: 15px; + } + + /* Make buttons and interactive elements easier to tap */ + button, + a, + [role="button"] { + min-height: 44px; + min-width: 44px; + } + } + code { font-family: var(--font-monospace); } @@ -135,6 +158,11 @@ const AppContainer = styled.div` height: 100vh; overflow: hidden; background: #1e1e1e; + + /* Mobile: Ensure content takes full width */ + @media (max-width: 768px) { + flex-direction: column; + } `; const MainContent = styled.div` @@ -142,12 +170,23 @@ const MainContent = styled.div` display: flex; flex-direction: column; overflow: hidden; + min-width: 0; /* Allow content to shrink below its minimum content size */ + + /* Mobile: Take full width */ + @media (max-width: 768px) { + width: 100%; + } `; const ContentArea = styled.div` flex: 1; display: flex; overflow: hidden; + + /* Mobile: Stack content vertically if needed */ + @media (max-width: 768px) { + flex-direction: column; + } `; const WelcomeView = styled.div` @@ -186,7 +225,10 @@ function AppInner() { ); const [workspaceModalLoadError, setWorkspaceModalLoadError] = useState(null); const workspaceModalProjectRef = useRef(null); - const [sidebarCollapsed, setSidebarCollapsed] = usePersistedState("sidebarCollapsed", false); + + // Auto-collapse sidebar on mobile by default + const isMobile = typeof window !== "undefined" && window.innerWidth <= 768; + const [sidebarCollapsed, setSidebarCollapsed] = usePersistedState("sidebarCollapsed", isMobile); const handleToggleSidebar = useCallback(() => { setSidebarCollapsed((prev) => !prev); diff --git a/src/components/AIView.tsx b/src/components/AIView.tsx index 841f90f07..b5e9087bf 100644 --- a/src/components/AIView.tsx +++ b/src/components/AIView.tsx @@ -41,6 +41,11 @@ const ViewContainer = styled.div` overflow-x: auto; overflow-y: hidden; container-type: inline-size; + + /* Mobile: Stack vertically */ + @media (max-width: 768px) { + flex-direction: column; + } `; const ChatArea = styled.div` @@ -48,6 +53,12 @@ const ChatArea = styled.div` min-width: 400px; /* Reduced from 750px to allow narrower layout when Review panel is wide */ display: flex; flex-direction: column; + + /* Mobile: Remove min-width and take full width */ + @media (max-width: 768px) { + min-width: 0; + width: 100%; + } `; const ViewHeader = styled.div` @@ -57,6 +68,13 @@ const ViewHeader = styled.div` display: flex; justify-content: space-between; align-items: center; + + /* Mobile: Add padding for hamburger button and adjust spacing */ + @media (max-width: 768px) { + padding: 8px 15px 8px 60px; /* Extra left padding for hamburger button */ + flex-wrap: wrap; + gap: 8px; + } `; const WorkspaceTitle = styled.div` diff --git a/src/components/LeftSidebar.tsx b/src/components/LeftSidebar.tsx index 2daa34ead..ca3a0267f 100644 --- a/src/components/LeftSidebar.tsx +++ b/src/components/LeftSidebar.tsx @@ -17,6 +17,71 @@ const LeftSidebarContainer = styled.div<{ collapsed?: boolean }>` flex-shrink: 0; transition: width 0.2s ease; overflow: hidden; + position: relative; + z-index: 100; + + /* Mobile: Sidebar becomes overlay */ + @media (max-width: 768px) { + position: fixed; + left: 0; + top: 0; + width: 280px; + z-index: 1000; + transform: ${(props) => (props.collapsed ? "translateX(-100%)" : "translateX(0)")}; + transition: transform 0.3s ease; + box-shadow: ${(props) => (props.collapsed ? "none" : "2px 0 8px rgba(0, 0, 0, 0.5)")}; + } +`; + +const Overlay = styled.div<{ visible: boolean }>` + display: none; + + /* Mobile: Show overlay backdrop when sidebar is open */ + @media (max-width: 768px) { + display: ${(props) => (props.visible ? "block" : "none")}; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 999; + backdrop-filter: blur(2px); + } +`; + +const HamburgerButton = styled.button` + display: none; + + /* Mobile: Show hamburger menu */ + @media (max-width: 768px) { + display: flex; + position: fixed; + top: 12px; + left: 12px; + z-index: 998; + width: 40px; + height: 40px; + background: #252526; + border: 1px solid #3c3c3c; + border-radius: 6px; + cursor: pointer; + align-items: center; + justify-content: center; + color: #cccccc; + font-size: 20px; + transition: all 0.2s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + + &:hover { + background: #2a2a2b; + border-color: #4c4c4c; + } + + &:active { + transform: scale(0.95); + } + } `; interface LeftSidebarProps { @@ -46,12 +111,29 @@ interface LeftSidebarProps { } export function LeftSidebar(props: LeftSidebarProps) { - const { collapsed, ...projectSidebarProps } = props; + const { collapsed, onToggleCollapsed, ...projectSidebarProps } = props; return ( - - {!collapsed && } - - + <> + {/* Hamburger menu button - only visible on mobile */} + {collapsed && ( + + ☰ + + )} + + {/* Overlay backdrop - only visible on mobile when sidebar is open */} + + + {/* Sidebar */} + + {!collapsed && } + + + ); } diff --git a/src/components/RightSidebar.tsx b/src/components/RightSidebar.tsx index 9be4770b4..497193a46 100644 --- a/src/components/RightSidebar.tsx +++ b/src/components/RightSidebar.tsx @@ -54,6 +54,16 @@ const SidebarContainer = styled.div` z-index: 10; box-shadow: -2px 0 4px rgba(0, 0, 0, 0.2); `} + + /* Mobile: Full width when expanded, hide when collapsed */ + @media (max-width: 768px) { + width: ${(props) => (props.collapsed ? "0" : "100%")}; + border-left: none; + border-top: 1px solid #3e3e42; + max-height: ${(props) => (props.collapsed ? "0" : "50vh")}; + position: ${(props) => (props.collapsed ? "absolute" : "relative")}; + bottom: ${(props) => (props.collapsed ? "0" : "auto")}; + } `; const FullView = styled.div<{ visible: boolean }>` From 2d4f082536b7c481467461b58a4662154ae25861 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Tue, 21 Oct 2025 01:09:27 -0400 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A4=96=20Fix=20code=20formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/LeftSidebar.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/LeftSidebar.tsx b/src/components/LeftSidebar.tsx index ca3a0267f..ded545ceb 100644 --- a/src/components/LeftSidebar.tsx +++ b/src/components/LeftSidebar.tsx @@ -132,7 +132,11 @@ export function LeftSidebar(props: LeftSidebarProps) { {/* Sidebar */} {!collapsed && } - + );