Enhance mobile responsiveness across all components

Comprehensive mobile improvements:
- Toolbar: Added hamburger menu for mobile, larger touch targets
- FileTabs: Taller tabs with always-visible close buttons on mobile
- FileTree: Larger touch targets, bigger icons and buttons
- ConsolePanel: Collapsed by default on mobile with toggle
- Added touch-manipulation CSS for better tap performance
- Responsive typography and spacing throughout
This commit is contained in:
Claude 2026-01-17 22:18:23 +00:00
parent 024ec42c5e
commit 68c881374d
No known key found for this signature in database
4 changed files with 146 additions and 84 deletions

View file

@ -38,6 +38,7 @@ function App() {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [showPassportLogin, setShowPassportLogin] = useState(false); const [showPassportLogin, setShowPassportLogin] = useState(false);
const [consoleCollapsed, setConsoleCollapsed] = useState(isMobile);
const [user, setUser] = useState<{ login: string; avatarUrl: string; email: string } | null>(() => { const [user, setUser] = useState<{ login: string; avatarUrl: string; email: string } | null>(() => {
const stored = typeof window !== 'undefined' ? localStorage.getItem('aethex-user') : null; const stored = typeof window !== 'undefined' ? localStorage.getItem('aethex-user') : null;
return stored ? JSON.parse(stored) : null; return stored ? JSON.parse(stored) : null;
@ -446,7 +447,10 @@ end)`,
</ResizablePanel> </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>
</div> </div>
<ConsolePanel /> <ConsolePanel
collapsed={consoleCollapsed}
onToggle={() => setConsoleCollapsed(!consoleCollapsed)}
/>
</> </>
)} )}
{/* Unified feature tabs for all major panels */} {/* Unified feature tabs for all major panels */}

View file

@ -19,12 +19,12 @@ export function FileTabs({
if (openFiles.length === 0) return null; if (openFiles.length === 0) return null;
return ( return (
<div className="flex items-center bg-card/70 border-b border-border h-8 min-h-8"> <div className="flex items-center bg-card/70 border-b border-border h-9 md:h-8 min-h-[36px] md:min-h-8">
<div className="flex overflow-x-auto flex-1"> <div className="flex overflow-x-auto flex-1 scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent">
{openFiles.map((file) => ( {openFiles.map((file) => (
<div <div
key={file.id} key={file.id}
className={`flex items-center gap-1 px-3 h-8 border-r border-border cursor-pointer group min-w-0 transition-colors select-none ${ className={`flex items-center gap-1 px-3 md:px-3 py-1 md:py-0 h-9 md:h-8 border-r border-border cursor-pointer group min-w-0 transition-colors select-none ${
activeFileId === file.id activeFileId === file.id
? 'bg-background border-b-2 border-b-accent font-semibold text-accent' ? 'bg-background border-b-2 border-b-accent font-semibold text-accent'
: 'hover:bg-muted/60 text-muted-foreground' : 'hover:bg-muted/60 text-muted-foreground'
@ -32,18 +32,19 @@ export function FileTabs({
onClick={() => onFileSelect(file)} onClick={() => onFileSelect(file)}
title={file.name} title={file.name}
> >
<span className="text-xs truncate max-w-[100px]">{file.name}</span> <span className="text-xs md:text-xs truncate max-w-[100px] md:max-w-[120px]">{file.name}</span>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-4 w-4 p-0 ml-1 opacity-0 group-hover:opacity-100 hover:text-destructive flex-shrink-0" className="h-5 w-5 md:h-4 md:w-4 p-0 ml-1 opacity-70 md:opacity-0 group-hover:opacity-100 hover:text-destructive flex-shrink-0 touch-manipulation"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onFileClose(file.id); onFileClose(file.id);
}} }}
tabIndex={-1} tabIndex={-1}
> >
<X size={12} /> <X size={14} className="md:hidden" />
<X size={12} className="hidden md:block" />
</Button> </Button>
</div> </div>
))} ))}

View file

@ -153,7 +153,7 @@ export function FileTree({
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e, node)} onDrop={(e) => handleDrop(e, node)}
onDragEnd={handleDragEnd} onDragEnd={handleDragEnd}
className={`flex items-center gap-1 px-2 py-1 hover:bg-muted/60 cursor-pointer group rounded-sm transition-colors ${ className={`flex items-center gap-1 px-2 py-1.5 md:py-1 hover:bg-muted/60 cursor-pointer group rounded-sm transition-colors touch-manipulation ${
isSelected ? 'bg-accent/30 text-accent font-semibold' : 'text-foreground' isSelected ? 'bg-accent/30 text-accent font-semibold' : 'text-foreground'
} ${isDragging ? 'opacity-50' : ''} ${isDropTarget && node.type === 'folder' ? 'bg-blue-500/20 border-2 border-blue-500 border-dashed' : ''}`} } ${isDragging ? 'opacity-50' : ''} ${isDropTarget && node.type === 'folder' ? 'bg-blue-500/20 border-2 border-blue-500 border-dashed' : ''}`}
style={{ paddingLeft: `${depth * 10 + 8}px` }} style={{ paddingLeft: `${depth * 10 + 8}px` }}
@ -167,12 +167,12 @@ export function FileTree({
> >
{node.type === 'folder' ? ( {node.type === 'folder' ? (
isExpanded ? ( isExpanded ? (
<FolderOpen size={15} className="flex-shrink-0 opacity-80" /> <FolderOpen size={16} className="flex-shrink-0 opacity-80 md:w-[15px] md:h-[15px]" />
) : ( ) : (
<Folder size={15} className="flex-shrink-0 opacity-80" /> <Folder size={16} className="flex-shrink-0 opacity-80 md:w-[15px] md:h-[15px]" />
) )
) : ( ) : (
<File size={15} className="flex-shrink-0 opacity-80" /> <File size={16} className="flex-shrink-0 opacity-80 md:w-[15px] md:h-[15px]" />
)} )}
{isEditing ? ( {isEditing ? (
@ -197,9 +197,9 @@ export function FileTree({
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-5 w-5 opacity-0 group-hover:opacity-100" className="h-6 w-6 md:h-5 md:w-5 opacity-50 md:opacity-0 group-hover:opacity-100 touch-manipulation"
> >
<DotsThree size={13} /> <DotsThree size={16} className="md:w-[13px] md:h-[13px]" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">

View file

@ -8,7 +8,7 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut } from '@phosphor-icons/react'; import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List } from '@phosphor-icons/react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
@ -56,92 +56,149 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl
return ( return (
<> <>
<div className="flex items-center gap-1 px-2 py-1.5 bg-card border-b border-border min-h-[38px]"> <div className="flex items-center gap-1 px-2 py-1.5 bg-card border-b border-border min-h-[38px] md:min-h-[42px]">
<h1 className="text-lg font-bold tracking-tight leading-none"> <h1 className="text-base md:text-lg font-bold tracking-tight leading-none">
Ae<span className="text-accent">Thex</span> Ae<span className="text-accent">Thex</span>
</h1> </h1>
<span className="text-xs text-muted-foreground ml-1 leading-none">Studio</span> <span className="text-xs text-muted-foreground ml-1 leading-none">Studio</span>
<div className="flex-1" /> <div className="flex-1" />
{/* Desktop: Show all buttons */}
<TooltipProvider> <TooltipProvider>
<Tooltip> <div className="hidden md:flex items-center gap-1">
<TooltipTrigger asChild> <Tooltip>
<Button <TooltipTrigger asChild>
variant="ghost" <Button
size="icon" variant="ghost"
onClick={onNewProjectClick} size="icon"
className="p-1.5 rounded hover:bg-accent/10" onClick={onNewProjectClick}
aria-label="New Project" className="p-1.5 rounded hover:bg-accent/10"
> aria-label="New Project"
<FolderPlus size={18} /> >
</Button> <FolderPlus size={18} />
</TooltipTrigger> </Button>
<TooltipContent>New Project</TooltipContent> </TooltipTrigger>
</Tooltip> <TooltipContent>New Project</TooltipContent>
</Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={onPreviewClick} onClick={onPreviewClick}
className="p-1.5 rounded hover:bg-accent/10" className="p-1.5 rounded hover:bg-accent/10"
aria-label="Preview" aria-label="Preview"
> >
<Play size={18} /> <Play size={18} />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Preview</TooltipContent> <TooltipContent>Preview</TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button variant="ghost" size="icon" onClick={onTemplatesClick} className="p-1.5 rounded hover:bg-accent/10" aria-label="Templates"> <Button variant="ghost" size="icon" onClick={onTemplatesClick} className="p-1.5 rounded hover:bg-accent/10" aria-label="Templates">
<FileCode size={18} /> <FileCode size={18} />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Templates</TooltipContent> <TooltipContent>Templates</TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button variant="ghost" size="icon" onClick={handleCopy} className="p-1.5 rounded hover:bg-accent/10" aria-label="Copy"> <Button variant="ghost" size="icon" onClick={handleCopy} className="p-1.5 rounded hover:bg-accent/10" aria-label="Copy">
<Copy size={18} /> <Copy size={18} />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Copy</TooltipContent> <TooltipContent>Copy</TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={handleExport} onClick={handleExport}
className="p-1.5 rounded hover:bg-accent/10 hidden sm:flex" className="p-1.5 rounded hover:bg-accent/10"
aria-label="Export" aria-label="Export"
> >
<Download size={18} /> <Download size={18} />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Export</TooltipContent> <TooltipContent>Export</TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button variant="ghost" size="icon" onClick={() => setShowInfo(true)} className="p-1.5 rounded hover:bg-accent/10" aria-label="About"> <Button variant="ghost" size="icon" onClick={() => setShowInfo(true)} className="p-1.5 rounded hover:bg-accent/10" aria-label="About">
<Info size={18} /> <Info size={18} />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>About</TooltipContent> <TooltipContent>About</TooltipContent>
</Tooltip> </Tooltip>
</div>
{/* Mobile: Hamburger menu with essential actions */}
<div className="flex md:hidden items-center gap-1">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={onPreviewClick}
className="p-2 rounded hover:bg-accent/10"
aria-label="Preview"
>
<Play size={20} weight="bold" />
</Button>
</TooltipTrigger>
<TooltipContent>Preview</TooltipContent>
</Tooltip>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="p-2 rounded hover:bg-accent/10"
aria-label="Menu"
>
<List size={20} weight="bold" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuItem onClick={onNewProjectClick}>
<FolderPlus className="mr-2" size={16} />
<span>New Project</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={onTemplatesClick}>
<FileCode className="mr-2" size={16} />
<span>Templates</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={handleCopy}>
<Copy className="mr-2" size={16} />
<span>Copy Code</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={handleExport}>
<Download className="mr-2" size={16} />
<span>Export</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setShowInfo(true)}>
<Info className="mr-2" size={16} />
<span>About</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</TooltipProvider> </TooltipProvider>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full p-0"> <Button variant="ghost" size="icon" className="rounded-full p-0 ml-1">
<Avatar className="h-7 w-7"> <Avatar className="h-7 w-7 md:h-8 md:w-8">
<AvatarImage src={user?.avatarUrl} alt={user?.login || 'User'} /> <AvatarImage src={user?.avatarUrl} alt={user?.login || 'User'} />
<AvatarFallback> <AvatarFallback>
<User size={16} /> <User size={16} />