1
0
Эх сурвалжийг харах

Revert UI refinements, keep Docker changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
tuanchris 3 долоо хоног өмнө
parent
commit
f59128bcd7

+ 27 - 46
frontend/src/components/layout/Layout.tsx

@@ -2,7 +2,6 @@ import { Outlet, Link, useLocation } from 'react-router-dom'
 import { useEffect, useState, useRef } from 'react'
 import { useEffect, useState, useRef } from 'react'
 import { toast } from 'sonner'
 import { toast } from 'sonner'
 import { NowPlayingBar } from '@/components/NowPlayingBar'
 import { NowPlayingBar } from '@/components/NowPlayingBar'
-import { Button } from '@/components/ui/button'
 
 
 const navItems = [
 const navItems = [
   { path: '/', label: 'Browse', icon: 'grid_view', title: 'Browse Patterns' },
   { path: '/', label: 'Browse', icon: 'grid_view', title: 'Browse Patterns' },
@@ -477,53 +476,45 @@ export function Layout() {
             />
             />
             <span className="font-semibold text-lg">{appName}</span>
             <span className="font-semibold text-lg">{appName}</span>
             <span
             <span
-              className={`w-2 h-2 rounded-full ${isConnected ? 'bg-green-500 animate-pulse' : 'bg-red-500'}`}
-              title={isConnected ? 'Connected to table' : 'Disconnected from table'}
+              className={`w-2 h-2 rounded-full ${isConnected ? 'bg-green-500' : 'bg-red-500'}`}
+              title={isConnected ? 'Connected' : 'Disconnected'}
             />
             />
           </Link>
           </Link>
           <div className="flex items-center gap-1">
           <div className="flex items-center gap-1">
-            <Button
-              variant="ghost"
-              size="icon"
+            <button
               onClick={() => setIsDark(!isDark)}
               onClick={() => setIsDark(!isDark)}
-              className="rounded-full"
+              className="rounded-full w-10 h-10 flex items-center justify-center hover:bg-accent"
               aria-label="Toggle dark mode"
               aria-label="Toggle dark mode"
               title="Toggle Theme"
               title="Toggle Theme"
             >
             >
               <span className="material-icons-outlined">
               <span className="material-icons-outlined">
                 {isDark ? 'light_mode' : 'dark_mode'}
                 {isDark ? 'light_mode' : 'dark_mode'}
               </span>
               </span>
-            </Button>
-            <Button
-              variant="ghost"
-              size="icon"
+            </button>
+            <button
               onClick={handleOpenLogs}
               onClick={handleOpenLogs}
-              className="rounded-full"
+              className="rounded-full w-10 h-10 flex items-center justify-center hover:bg-accent"
               aria-label="View logs"
               aria-label="View logs"
               title="View Application Logs"
               title="View Application Logs"
             >
             >
               <span className="material-icons-outlined">article</span>
               <span className="material-icons-outlined">article</span>
-            </Button>
-            <Button
-              variant="ghost"
-              size="icon"
+            </button>
+            <button
               onClick={handleRestart}
               onClick={handleRestart}
-              className="rounded-full text-amber-500 hover:text-amber-600"
+              className="rounded-full w-10 h-10 flex items-center justify-center hover:bg-accent text-amber-500"
               aria-label="Restart system"
               aria-label="Restart system"
               title="Restart System"
               title="Restart System"
             >
             >
               <span className="material-icons-outlined">restart_alt</span>
               <span className="material-icons-outlined">restart_alt</span>
-            </Button>
-            <Button
-              variant="ghost"
-              size="icon"
+            </button>
+            <button
               onClick={handleShutdown}
               onClick={handleShutdown}
-              className="rounded-full text-red-500 hover:text-red-600"
+              className="rounded-full w-10 h-10 flex items-center justify-center hover:bg-accent text-red-500"
               aria-label="Shutdown system"
               aria-label="Shutdown system"
               title="Shutdown System"
               title="Shutdown System"
             >
             >
               <span className="material-icons-outlined">power_settings_new</span>
               <span className="material-icons-outlined">power_settings_new</span>
-            </Button>
+            </button>
           </div>
           </div>
         </div>
         </div>
       </header>
       </header>
@@ -550,7 +541,7 @@ export function Layout() {
       {!isNowPlayingOpen && (
       {!isNowPlayingOpen && (
         <button
         <button
           onClick={() => setIsNowPlayingOpen(true)}
           onClick={() => setIsNowPlayingOpen(true)}
-          className="fixed right-4 bottom-20 z-30 w-12 h-12 rounded-full bg-primary text-primary-foreground shadow-lg flex items-center justify-center transition-all duration-200 hover:bg-primary/90 hover:shadow-xl hover:scale-110 active:scale-95"
+          className="fixed right-4 bottom-20 z-30 w-12 h-12 rounded-full bg-primary text-primary-foreground shadow-lg flex items-center justify-center hover:bg-primary/90 transition-all"
           title="Now Playing"
           title="Now Playing"
         >
         >
           <span className="material-icons">play_circle</span>
           <span className="material-icons">play_circle</span>
@@ -584,33 +575,27 @@ export function Layout() {
                 </span>
                 </span>
               </div>
               </div>
               <div className="flex items-center gap-1">
               <div className="flex items-center gap-1">
-                <Button
-                  variant="ghost"
-                  size="icon-sm"
+                <button
                   onClick={handleCopyLogs}
                   onClick={handleCopyLogs}
-                  className="rounded-full"
+                  className="rounded-full w-7 h-7 flex items-center justify-center hover:bg-accent"
                   title="Copy logs"
                   title="Copy logs"
                 >
                 >
                   <span className="material-icons-outlined text-base">content_copy</span>
                   <span className="material-icons-outlined text-base">content_copy</span>
-                </Button>
-                <Button
-                  variant="ghost"
-                  size="icon-sm"
+                </button>
+                <button
                   onClick={handleDownloadLogs}
                   onClick={handleDownloadLogs}
-                  className="rounded-full"
+                  className="rounded-full w-7 h-7 flex items-center justify-center hover:bg-accent"
                   title="Download logs"
                   title="Download logs"
                 >
                 >
                   <span className="material-icons-outlined text-base">download</span>
                   <span className="material-icons-outlined text-base">download</span>
-                </Button>
-                <Button
-                  variant="ghost"
-                  size="icon-sm"
+                </button>
+                <button
                   onClick={() => setIsLogsOpen(false)}
                   onClick={() => setIsLogsOpen(false)}
-                  className="rounded-full"
+                  className="rounded-full w-7 h-7 flex items-center justify-center hover:bg-accent"
                   title="Close logs"
                   title="Close logs"
                 >
                 >
                   <span className="material-icons-outlined text-base">close</span>
                   <span className="material-icons-outlined text-base">close</span>
-                </Button>
+                </button>
               </div>
               </div>
             </div>
             </div>
             <div
             <div
@@ -651,17 +636,13 @@ export function Layout() {
               <Link
               <Link
                 key={item.path}
                 key={item.path}
                 to={item.path}
                 to={item.path}
-                className={`relative flex flex-col items-center justify-center gap-1 transition-all duration-200 ${
+                className={`flex flex-col items-center justify-center gap-1 transition-colors ${
                   isActive
                   isActive
                     ? 'text-primary'
                     ? 'text-primary'
-                    : 'text-muted-foreground hover:text-foreground active:scale-95'
+                    : 'text-muted-foreground hover:text-foreground'
                 }`}
                 }`}
               >
               >
-                {/* Active indicator pill */}
-                {isActive && (
-                  <span className="absolute -top-0.5 w-8 h-1 rounded-full bg-primary" />
-                )}
-                <span className={`text-xl ${isActive ? 'material-icons' : 'material-icons-outlined'}`}>
+                <span className="material-icons-outlined text-xl">
                   {item.icon}
                   {item.icon}
                 </span>
                 </span>
                 <span className="text-xs font-medium">{item.label}</span>
                 <span className="text-xs font-medium">{item.label}</span>

+ 1 - 3
frontend/src/components/ui/button.tsx

@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
 import { cn } from "@/lib/utils"
 import { cn } from "@/lib/utils"
 
 
 const buttonVariants = cva(
 const buttonVariants = cva(
-  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-95 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
   {
   {
     variants: {
     variants: {
       variant: {
       variant: {
@@ -24,8 +24,6 @@ const buttonVariants = cva(
         sm: "h-9 rounded-md px-3",
         sm: "h-9 rounded-md px-3",
         lg: "h-11 rounded-md px-8",
         lg: "h-11 rounded-md px-8",
         icon: "h-10 w-10",
         icon: "h-10 w-10",
-        "icon-sm": "h-8 w-8",
-        "icon-lg": "h-12 w-12",
       },
       },
     },
     },
     defaultVariants: {
     defaultVariants: {

+ 0 - 46
frontend/src/index.css

@@ -140,49 +140,3 @@ body {
 .animate-marquee:hover {
 .animate-marquee:hover {
   animation-play-state: paused;
   animation-play-state: paused;
 }
 }
-
-/* Smooth fade in for lazy-loaded content */
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-    transform: scale(0.98);
-  }
-  to {
-    opacity: 1;
-    transform: scale(1);
-  }
-}
-
-.animate-fade-in {
-  animation: fadeIn 0.2s ease-out forwards;
-}
-
-/* Subtle pulse for connection status */
-@keyframes subtle-pulse {
-  0%, 100% {
-    opacity: 1;
-  }
-  50% {
-    opacity: 0.6;
-  }
-}
-
-.animate-subtle-pulse {
-  animation: subtle-pulse 2s ease-in-out infinite;
-}
-
-/* Slide in from right for panels */
-@keyframes slideInRight {
-  from {
-    transform: translateX(100%);
-    opacity: 0;
-  }
-  to {
-    transform: translateX(0);
-    opacity: 1;
-  }
-}
-
-.animate-slide-in-right {
-  animation: slideInRight 0.3s ease-out forwards;
-}

+ 15 - 95
frontend/src/pages/BrowsePage.tsx

@@ -101,10 +101,6 @@ export function BrowsePage() {
   // Favorites state
   // Favorites state
   const [favorites, setFavorites] = useState<Set<string>>(new Set())
   const [favorites, setFavorites] = useState<Set<string>>(new Set())
 
 
-  // Upload state
-  const fileInputRef = useRef<HTMLInputElement>(null)
-  const [isUploading, setIsUploading] = useState(false)
-
   // Close panel when playback starts
   // Close panel when playback starts
   useEffect(() => {
   useEffect(() => {
     const handlePlaybackStarted = () => {
     const handlePlaybackStarted = () => {
@@ -821,52 +817,6 @@ export function BrowsePage() {
     }
     }
   }
   }
 
 
-  // Handle pattern file upload
-  const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
-    const file = e.target.files?.[0]
-    if (!file) return
-
-    // Validate file extension
-    if (!file.name.endsWith('.thr')) {
-      toast.error('Please select a .thr file')
-      return
-    }
-
-    setIsUploading(true)
-    try {
-      const formData = new FormData()
-      formData.append('file', file)
-
-      const response = await fetch('/upload_theta_rho', {
-        method: 'POST',
-        body: formData,
-      })
-
-      if (!response.ok) {
-        const error = await response.json()
-        throw new Error(error.detail || 'Upload failed')
-      }
-
-      toast.success(`Pattern "${file.name}" uploaded successfully`)
-
-      // Refresh patterns list
-      const patternsRes = await fetch('/list_theta_rho_files')
-      if (patternsRes.ok) {
-        const data = await patternsRes.json()
-        setPatterns(data.files || [])
-      }
-    } catch (error) {
-      console.error('Upload error:', error)
-      toast.error(error instanceof Error ? error.message : 'Failed to upload pattern')
-    } finally {
-      setIsUploading(false)
-      // Reset file input
-      if (fileInputRef.current) {
-        fileInputRef.current.value = ''
-      }
-    }
-  }
-
   if (isLoading) {
   if (isLoading) {
     return (
     return (
       <div className="flex items-center justify-center min-h-[60vh]">
       <div className="flex items-center justify-center min-h-[60vh]">
@@ -879,36 +829,12 @@ export function BrowsePage() {
 
 
   return (
   return (
     <div className={`flex flex-col w-full max-w-5xl mx-auto gap-6 py-6 px-4 transition-all duration-300 ${isPanelOpen ? 'lg:mr-[28rem]' : ''}`}>
     <div className={`flex flex-col w-full max-w-5xl mx-auto gap-6 py-6 px-4 transition-all duration-300 ${isPanelOpen ? 'lg:mr-[28rem]' : ''}`}>
-      {/* Hidden file input for pattern upload */}
-      <input
-        ref={fileInputRef}
-        type="file"
-        accept=".thr"
-        onChange={handleFileUpload}
-        className="hidden"
-      />
-
       {/* Page Header */}
       {/* Page Header */}
-      <div className="flex items-start justify-between gap-4">
-        <div className="space-y-1">
-          <h1 className="text-3xl font-bold tracking-tight">Browse Patterns</h1>
-          <p className="text-muted-foreground">
-            Explore and run patterns on your sand table · {patterns.length} patterns available
-          </p>
-        </div>
-        <Button
-          variant="outline"
-          onClick={() => fileInputRef.current?.click()}
-          disabled={isUploading}
-          className="gap-2 shrink-0"
-        >
-          {isUploading ? (
-            <span className="material-icons-outlined animate-spin text-lg">sync</span>
-          ) : (
-            <span className="material-icons-outlined text-lg">add</span>
-          )}
-          <span className="hidden sm:inline">Add Pattern</span>
-        </Button>
+      <div className="space-y-1">
+        <h1 className="text-3xl font-bold tracking-tight">Browse Patterns</h1>
+        <p className="text-muted-foreground">
+          Explore and run patterns on your sand table · {patterns.length} patterns available
+        </p>
       </div>
       </div>
 
 
       <Separator />
       <Separator />
@@ -928,14 +854,12 @@ export function BrowsePage() {
                 className="pl-10 pr-10"
                 className="pl-10 pr-10"
               />
               />
               {searchQuery && (
               {searchQuery && (
-                <Button
-                  variant="ghost"
-                  size="icon-sm"
+                <button
                   onClick={() => setSearchQuery('')}
                   onClick={() => setSearchQuery('')}
                   className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
                   className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
                 >
                 >
                   <span className="material-icons-outlined text-xl">close</span>
                   <span className="material-icons-outlined text-xl">close</span>
-                </Button>
+                </button>
               )}
               )}
             </div>
             </div>
           </div>
           </div>
@@ -1063,14 +987,12 @@ export function BrowsePage() {
             <h2 className="text-lg font-semibold truncate pr-4">
             <h2 className="text-lg font-semibold truncate pr-4">
               {selectedPattern?.name || 'Pattern Details'}
               {selectedPattern?.name || 'Pattern Details'}
             </h2>
             </h2>
-            <Button
-              variant="ghost"
-              size="icon"
+            <button
               onClick={handleClosePanel}
               onClick={handleClosePanel}
-              className="rounded-full text-muted-foreground"
+              className="rounded-full w-10 h-10 flex items-center justify-center text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
             >
             >
               <span className="material-icons-outlined">close</span>
               <span className="material-icons-outlined">close</span>
-            </Button>
+            </button>
           </header>
           </header>
 
 
           {selectedPattern && (
           {selectedPattern && (
@@ -1209,14 +1131,12 @@ export function BrowsePage() {
               <h3 className="text-xl font-semibold">
               <h3 className="text-xl font-semibold">
                 {selectedPattern?.name || 'Animated Preview'}
                 {selectedPattern?.name || 'Animated Preview'}
               </h3>
               </h3>
-              <Button
-                variant="ghost"
-                size="icon"
+              <button
                 onClick={handleCloseAnimatedPreview}
                 onClick={handleCloseAnimatedPreview}
-                className="rounded-full"
+                className="text-muted-foreground hover:text-foreground transition-colors"
               >
               >
                 <span className="material-icons text-2xl">close</span>
                 <span className="material-icons text-2xl">close</span>
-              </Button>
+              </button>
             </div>
             </div>
 
 
             {/* Modal Content */}
             {/* Modal Content */}
@@ -1347,8 +1267,8 @@ function PatternCard({ pattern, isSelected, isFavorite, onToggleFavorite, onClic
     <button
     <button
       ref={cardRef}
       ref={cardRef}
       onClick={onClick}
       onClick={onClick}
-      className={`group flex flex-col items-center gap-2 p-2 rounded-lg transition-all duration-200 ease-out hover:-translate-y-1 hover:scale-[1.02] hover:shadow-lg hover:bg-accent/30 active:scale-95 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${
-        isSelected ? 'ring-2 ring-primary ring-offset-2 ring-offset-background bg-accent/20' : ''
+      className={`group flex flex-col items-center gap-2 p-2 rounded-lg transition-all hover:-translate-y-1 hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${
+        isSelected ? 'ring-2 ring-primary ring-offset-2 ring-offset-background' : ''
       }`}
       }`}
     >
     >
       <div className="relative w-full aspect-square">
       <div className="relative w-full aspect-square">

+ 6 - 10
frontend/src/pages/PlaylistsPage.tsx

@@ -543,10 +543,8 @@ export function PlaylistsPage() {
                   <span className="truncate text-sm font-medium">{name}</span>
                   <span className="truncate text-sm font-medium">{name}</span>
                 </div>
                 </div>
                 <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
                 <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
-                  <Button
-                    variant="ghost"
-                    size="icon-sm"
-                    className="h-7 w-7"
+                  <button
+                    className="p-1 rounded hover:bg-muted-foreground/20"
                     onClick={(e) => {
                     onClick={(e) => {
                       e.stopPropagation()
                       e.stopPropagation()
                       setPlaylistToRename(name)
                       setPlaylistToRename(name)
@@ -555,18 +553,16 @@ export function PlaylistsPage() {
                     }}
                     }}
                   >
                   >
                     <span className="material-icons-outlined text-base">edit</span>
                     <span className="material-icons-outlined text-base">edit</span>
-                  </Button>
-                  <Button
-                    variant="ghost"
-                    size="icon-sm"
-                    className="h-7 w-7 text-destructive hover:text-destructive hover:bg-destructive/20"
+                  </button>
+                  <button
+                    className="p-1 rounded hover:bg-destructive/20 text-destructive"
                     onClick={(e) => {
                     onClick={(e) => {
                       e.stopPropagation()
                       e.stopPropagation()
                       handleDeletePlaylist(name)
                       handleDeletePlaylist(name)
                     }}
                     }}
                   >
                   >
                     <span className="material-icons-outlined text-base">delete</span>
                     <span className="material-icons-outlined text-base">delete</span>
-                  </Button>
+                  </button>
                 </div>
                 </div>
               </div>
               </div>
             ))
             ))

+ 4 - 4
frontend/src/pages/TableControlPage.tsx

@@ -178,7 +178,7 @@ export function TableControlPage() {
         {/* Main Controls Grid - 2x2 */}
         {/* Main Controls Grid - 2x2 */}
         <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
         <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
           {/* Primary Actions */}
           {/* Primary Actions */}
-          <Card className="transition-all duration-200 hover:shadow-md hover:border-primary/20">
+          <Card>
             <CardHeader className="pb-3">
             <CardHeader className="pb-3">
               <CardTitle className="text-lg">Primary Actions</CardTitle>
               <CardTitle className="text-lg">Primary Actions</CardTitle>
               <CardDescription>Calibrate or stop the table</CardDescription>
               <CardDescription>Calibrate or stop the table</CardDescription>
@@ -226,7 +226,7 @@ export function TableControlPage() {
           </Card>
           </Card>
 
 
           {/* Speed Control */}
           {/* Speed Control */}
-          <Card className="transition-all duration-200 hover:shadow-md hover:border-primary/20">
+          <Card>
             <CardHeader className="pb-3">
             <CardHeader className="pb-3">
               <div className="flex items-center justify-between">
               <div className="flex items-center justify-between">
                 <div>
                 <div>
@@ -267,7 +267,7 @@ export function TableControlPage() {
           </Card>
           </Card>
 
 
           {/* Position */}
           {/* Position */}
-          <Card className="transition-all duration-200 hover:shadow-md hover:border-primary/20">
+          <Card>
             <CardHeader className="pb-3">
             <CardHeader className="pb-3">
               <CardTitle className="text-lg">Position</CardTitle>
               <CardTitle className="text-lg">Position</CardTitle>
               <CardDescription>Move ball to a specific location</CardDescription>
               <CardDescription>Move ball to a specific location</CardDescription>
@@ -402,7 +402,7 @@ export function TableControlPage() {
           </Card>
           </Card>
 
 
           {/* Clear Patterns */}
           {/* Clear Patterns */}
-          <Card className="transition-all duration-200 hover:shadow-md hover:border-primary/20">
+          <Card>
             <CardHeader className="pb-3">
             <CardHeader className="pb-3">
               <CardTitle className="text-lg">Clear Sand</CardTitle>
               <CardTitle className="text-lg">Clear Sand</CardTitle>
               <CardDescription>Erase current pattern from the table</CardDescription>
               <CardDescription>Erase current pattern from the table</CardDescription>