diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 53f49c95..2ff47cca 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -2,15 +2,22 @@ import { BrowserRouter, Routes, Route } from "react-router-dom"; import { HomePage } from "./pages/home-page"; import { LoginPage } from "./pages/login-page"; import { ToolPage } from "./pages/tool-page"; +import { AutomatePage } from "./pages/automate-page"; +import { FullscreenGridPage } from "./pages/fullscreen-grid-page"; +import { KeyboardShortcutProvider } from "./components/common/keyboard-shortcut-provider"; export function App() { return ( - - } /> - } /> - } /> - + + + } /> + } /> + } /> + } /> + } /> + + ); } diff --git a/apps/web/src/components/layout/app-layout.tsx b/apps/web/src/components/layout/app-layout.tsx index 57740a94..a8c16583 100644 --- a/apps/web/src/components/layout/app-layout.tsx +++ b/apps/web/src/components/layout/app-layout.tsx @@ -1,8 +1,20 @@ import { useState } from "react"; +import { Link } from "react-router-dom"; +import { + LayoutGrid, + Workflow, + FolderOpen, + Settings as SettingsIcon, + Menu, + X, +} from "lucide-react"; import { Sidebar } from "./sidebar"; import { ToolPanel } from "./tool-panel"; import { Footer } from "./footer"; import { Dropzone } from "../common/dropzone"; +import { SettingsDialog } from "../settings/settings-dialog"; +import { useMobile } from "@/hooks/use-mobile"; +import { cn } from "@/lib/utils"; interface AppLayoutProps { children?: React.ReactNode; @@ -10,21 +22,125 @@ interface AppLayoutProps { } export function AppLayout({ children, showToolPanel = true }: AppLayoutProps) { - const [_settingsOpen, setSettingsOpen] = useState(false); + const [settingsOpen, setSettingsOpen] = useState(false); + const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); + const isMobile = useMobile(); return (
- setSettingsOpen(true)} /> - {showToolPanel && } -
+ {/* Desktop sidebar */} + {!isMobile && ( + setSettingsOpen(true)} /> + )} + + {/* Mobile sidebar overlay */} + {isMobile && mobileSidebarOpen && ( + <> +
setMobileSidebarOpen(false)} + /> +
+
+ + Stirling Image + + +
+
setMobileSidebarOpen(false)}> + { + setMobileSidebarOpen(false); + setSettingsOpen(true); + }} + expanded + /> +
+
+ + )} + + {/* Mobile top bar */} + {isMobile && ( +
+ + + Stirling Image + +
+ )} + + {showToolPanel && !isMobile && } + +
{children || }
-
- Privacy Policy -
+ {!isMobile && ( +
+ Privacy Policy +
+ )}
-
+ + {!isMobile &&
} + + {/* Mobile bottom nav */} + {isMobile && ( + + )} + + {/* Settings dialog */} + setSettingsOpen(false)} + />
); } + +function MobileNavItem({ + icon: Icon, + label, + href, +}: { + icon: React.ComponentType<{ className?: string }>; + label: string; + href: string; +}) { + return ( + + + {label} + + ); +} diff --git a/apps/web/src/components/layout/sidebar.tsx b/apps/web/src/components/layout/sidebar.tsx index 6e0a4bac..f5721633 100644 --- a/apps/web/src/components/layout/sidebar.tsx +++ b/apps/web/src/components/layout/sidebar.tsx @@ -7,6 +7,7 @@ import { FolderOpen, HelpCircle, Settings, + Grid3x3, } from "lucide-react"; import type { LucideIcon } from "lucide-react"; @@ -18,7 +19,7 @@ interface SidebarItem { const topItems: SidebarItem[] = [ { icon: LayoutGrid, label: "Tools", href: "/" }, - { icon: BookOpen, label: "Reader", href: "/reader" }, + { icon: Grid3x3, label: "Grid", href: "/fullscreen" }, { icon: Workflow, label: "Automate", href: "/automate" }, { icon: FolderOpen, label: "Files", href: "/files" }, ]; @@ -30,13 +31,27 @@ const bottomItems: SidebarItem[] = [ interface SidebarProps { onSettingsClick: () => void; + /** When true, renders in expanded mode (for mobile overlay). */ + expanded?: boolean; } -export function Sidebar({ onSettingsClick }: SidebarProps) { +export function Sidebar({ onSettingsClick, expanded = false }: SidebarProps) { const location = useLocation(); const renderItem = (item: SidebarItem, isActive: boolean) => { - const content = ( + const content = expanded ? ( +
+ + {item.label} +
+ ) : (
+ {topItems.map((item) => + renderItem(item, location.pathname === item.href) + )} +
+ {bottomItems.map((item) => renderItem(item, false))} +
+ ); + } + return (