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..ded545ceb 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,33 @@ 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 }>`