feat(ui): add responsive layout support for mobile and small screens

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xintaofei
2026-04-07 15:47:16 +08:00
parent dd659dcaa5
commit 768d1326b1
17 changed files with 664 additions and 216 deletions

View File

@@ -51,6 +51,8 @@ import {
import type { AgentType } from "@/lib/types"
import { cn } from "@/lib/utils"
import { useFolderContext } from "@/contexts/folder-context"
import { useIsMobile } from "@/hooks/use-mobile"
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet"
function FolderDocumentTitle() {
const { folder } = useFolderContext()
@@ -246,6 +248,90 @@ function WorkspaceContent({ children }: { children: React.ReactNode }) {
)
}
function MobileWorkspaceContent({ children }: { children: React.ReactNode }) {
const { mode } = useWorkspaceContext()
// On mobile, fusion mode falls back to conversation view
const showConversation = mode === "conversation" || mode === "fusion"
return (
<div className="relative h-full min-h-0 overflow-hidden">
{showConversation ? (
<section className="flex h-full min-h-0 flex-col overflow-hidden">
<TabBar />
<div className="relative flex-1 min-h-0 overflow-hidden">
{children}
</div>
</section>
) : (
<section className="flex h-full min-h-0 flex-col overflow-hidden">
<FileWorkspaceTabBar />
<div className="flex-1 min-h-0 overflow-hidden">
<FileWorkspacePanel />
</div>
</section>
)}
</div>
)
}
function MobileFolderWorkspaceShell({
children,
}: {
children: React.ReactNode
}) {
const { isOpen: sidebarOpen, toggle: toggleSidebar } = useSidebarContext()
const { isOpen: auxOpen, toggle: toggleAux } = useAuxPanelContext()
const { isOpen: terminalOpen, toggle: toggleTerminal } = useTerminalContext()
return (
<div className="flex flex-1 min-h-0 overflow-hidden">
{/* Sidebar Sheet (left) */}
<Sheet open={sidebarOpen} onOpenChange={toggleSidebar}>
<SheetContent
side="left"
showCloseButton={false}
className="w-[85%] max-w-[360px] p-0"
>
<SheetTitle className="sr-only">Sidebar</SheetTitle>
<Sidebar />
</SheetContent>
</Sheet>
{/* Main workspace */}
<main className="flex h-full min-h-0 w-full flex-col overflow-hidden">
<MobileWorkspaceContent>{children}</MobileWorkspaceContent>
</main>
{/* Aux panel Sheet (right) */}
<Sheet open={auxOpen} onOpenChange={toggleAux}>
<SheetContent
side="right"
showCloseButton={false}
className="w-[85%] max-w-[360px] p-0"
>
<SheetTitle className="sr-only">Panel</SheetTitle>
<AuxPanel />
</SheetContent>
</Sheet>
{/* Terminal Sheet (bottom) */}
<Sheet open={terminalOpen} onOpenChange={toggleTerminal}>
<SheetContent
side="bottom"
showCloseButton={false}
className="!h-[70vh] p-0"
>
<SheetTitle className="sr-only">Terminal</SheetTitle>
<div className="h-full min-h-0 overflow-hidden">
<TerminalPanel />
</div>
</SheetContent>
</Sheet>
</div>
)
}
function FolderWorkspaceShell({ children }: { children: React.ReactNode }) {
const {
isOpen: sidebarOpen,
@@ -661,6 +747,27 @@ function FolderWorkspaceShell({ children }: { children: React.ReactNode }) {
)
}
function FolderLayoutShell({ children }: { children: React.ReactNode }) {
const isMobile = useIsMobile()
return (
<div className="flex h-screen flex-col overflow-hidden">
<FolderTitleBar />
{isMobile ? (
<MobileFolderWorkspaceShell>{children}</MobileFolderWorkspaceShell>
) : (
<FolderWorkspaceShell>{children}</FolderWorkspaceShell>
)}
<StatusBar />
<AppToaster
position="bottom-right"
duration={TOAST_DURATION_MS}
closeButton
/>
</div>
)
}
function FolderLayoutInner({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams()
const folderId = Number(searchParams.get("id") ?? "0")
@@ -693,18 +800,7 @@ function FolderLayoutInner({ children }: { children: React.ReactNode }) {
folderId={normalizedFolderId}
>
<TerminalProvider>
<div className="flex h-screen flex-col overflow-hidden">
<FolderTitleBar />
<FolderWorkspaceShell>
{children}
</FolderWorkspaceShell>
<StatusBar />
<AppToaster
position="bottom-right"
duration={TOAST_DURATION_MS}
closeButton
/>
</div>
<FolderLayoutShell>{children}</FolderLayoutShell>
</TerminalProvider>
</AuxPanelProvider>
</SidebarProvider>

View File

@@ -167,6 +167,10 @@
}
body {
@apply bg-background text-foreground;
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
}

View File

@@ -1,4 +1,4 @@
import type { Metadata } from "next"
import type { Metadata, Viewport } from "next"
import "./globals.css"
import { JetBrains_Mono } from "next/font/google"
import { NextIntlClientProvider } from "next-intl"
@@ -13,6 +13,12 @@ const jetbrainsMono = JetBrains_Mono({
variable: "--font-sans",
})
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
viewportFit: "cover",
}
export const metadata: Metadata = {
title: "codeg",
description: "AI Coding Agent Conversation Manager",

View File

@@ -69,7 +69,7 @@ export default function LoginPage() {
onChange={(e) => setToken(e.target.value)}
placeholder="Access Token"
autoFocus
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base md:text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
/>
</div>