diff --git a/src/App.css b/src/App.css index 5eaacb4..88339c3 100644 --- a/src/App.css +++ b/src/App.css @@ -9,3 +9,74 @@ transform: translateX(100%); } } + +/* Skeleton loading animation */ +@keyframes skeleton-loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +/* Fade in animation */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Slide in from right */ +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* Pulse glow effect */ +@keyframes pulseGlow { + 0%, 100% { + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); + } + 50% { + box-shadow: 0 0 0 8px rgba(59, 130, 246, 0); + } +} + +/* Success checkmark animation */ +@keyframes checkmark { + 0% { + stroke-dashoffset: 100; + } + 100% { + stroke-dashoffset: 0; + } +} + +/* Utility classes */ +.animate-fadeIn { + animation: fadeIn 0.4s ease-out; +} + +.animate-slideInRight { + animation: slideInRight 0.4s ease-out; +} + +.animate-pulseGlow { + animation: pulseGlow 2s ease-in-out infinite; +} + +.animate-skeleton { + animation: skeleton-loading 2s ease-in-out infinite; +} diff --git a/src/App.tsx b/src/App.tsx index 3c0a4c5..5a2dff3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -66,7 +66,8 @@ function App() { const handleDeletePattern = useCallback(async () => { await machine.deletePattern(); setPatternUploaded(false); - setPesData(null); + // NOTE: We intentionally DON'T clear setPesData(null) here + // so the pattern remains visible in the canvas for re-editing and re-uploading }, [machine]); // Track pattern uploaded state based on machine status @@ -87,7 +88,7 @@ function App() { return (
-
+

SKiTCH Controller

@@ -108,18 +109,38 @@ function App() {
{/* Global errors */} {machine.error && ( -
- Error: {machine.error} +
+
+ + + +
+ Error: {machine.error} +
+
)} {pyodideError && ( -
- Python Error: {pyodideError} +
+
+ + + +
+ Python Error: {pyodideError} +
+
)} {!pyodideReady && !pyodideError && ( -
- Initializing Python environment... +
+
+ + + + + Initializing Python environment... +
)} @@ -177,17 +198,48 @@ function App() { machineInfo={machine.machineInfo} initialPatternOffset={patternOffset} onPatternOffsetChange={handlePatternOffsetChange} + patternUploaded={patternUploaded} /> ) : ( -
+

Pattern Preview

-
-
- - - -

No Pattern Loaded

-

Connect to your machine and choose a PES file to begin

+
+ {/* Decorative background pattern */} +
+
+
+
+
+ +
+
+ + + +
+ + + +
+
+

No Pattern Loaded

+

+ Connect to your machine and choose a PES embroidery file to see your design preview +

+
+
+
+ Drag to Position +
+
+
+ Zoom & Pan +
+
+
+ Real-time Preview +
+
diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx index 31818ec..118eee9 100644 --- a/src/components/FileUpload.tsx +++ b/src/components/FileUpload.tsx @@ -2,6 +2,8 @@ import { useState, useCallback } from 'react'; import { convertPesToPen, type PesPatternData } from '../utils/pystitchConverter'; import { MachineStatus } from '../types/machine'; import { canUploadPattern, getMachineStateCategory } from '../utils/machineStateHelpers'; +import { PatternInfoSkeleton } from './SkeletonLoader'; +import { ArrowUpTrayIcon, CheckCircleIcon } from '@heroicons/react/24/solid'; interface FileUploadProps { isConnected: boolean; @@ -74,7 +76,7 @@ export function FileUpload({ }, [pesData, displayFileName, onUpload, patternOffset]); return ( -
+

Pattern File

@@ -103,30 +105,81 @@ export function FileUpload({ className="hidden" disabled={!pyodideReady || isLoading || patternUploaded} /> -