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

View file

@ -19,12 +19,12 @@ export function FileTabs({
if (openFiles.length === 0) return null;
return (
<div className="flex items-center bg-card/70 border-b border-border h-8 min-h-8">
<div className="flex overflow-x-auto flex-1">
<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 scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent">
{openFiles.map((file) => (
<div
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
? 'bg-background border-b-2 border-b-accent font-semibold text-accent'
: 'hover:bg-muted/60 text-muted-foreground'
@ -32,18 +32,19 @@ export function FileTabs({
onClick={() => onFileSelect(file)}
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
variant="ghost"
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) => {
e.stopPropagation();
onFileClose(file.id);
}}
tabIndex={-1}
>
<X size={12} />
<X size={14} className="md:hidden" />
<X size={12} className="hidden md:block" />
</Button>
</div>
))}

View file

@ -153,7 +153,7 @@ export function FileTree({
onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e, node)}
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'
} ${isDragging ? 'opacity-50' : ''} ${isDropTarget && node.type === 'folder' ? 'bg-blue-500/20 border-2 border-blue-500 border-dashed' : ''}`}
style={{ paddingLeft: `${depth * 10 + 8}px` }}
@ -167,12 +167,12 @@ export function FileTree({
>
{node.type === 'folder' ? (
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 ? (
@ -197,9 +197,9 @@ export function FileTree({
<Button
variant="ghost"
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>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">

View file

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