Skip to content

Commit 9600924

Browse files
authored
Merge pull request #17 from AnasSarkiz/main
feat: Add loading state and error handling for circuit file uploads fixed freezing screen
2 parents ea635a5 + dae4208 commit 9600924

File tree

4 files changed

+80
-42
lines changed

4 files changed

+80
-42
lines changed

lib/components/blank-workspace.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { Upload, FileUp, Zap, ArrowRight, CircuitBoard } from "lucide-react"
55
import { useWorkspace } from "./workspace-context"
66

77
export function BlankWorkspace() {
8-
const { processCircuitFile, processCircuitDrop } = useWorkspace()
8+
const { processCircuitFile, processCircuitDrop, setIsProcessingFile } =
9+
useWorkspace()
910
const [isDragOver, setIsDragOver] = useState(false)
1011
const fileInputRef = useRef<HTMLInputElement>(null)
1112

@@ -14,7 +15,14 @@ export function BlankWorkspace() {
1415
) => {
1516
const file = event.target.files?.[0]
1617
if (file) {
17-
await processCircuitFile(file)
18+
try {
19+
setIsProcessingFile(true)
20+
await processCircuitFile(file)
21+
} catch (err) {
22+
alert(err instanceof Error ? err.message : "Failed to process file")
23+
} finally {
24+
setIsProcessingFile(false)
25+
}
1826
}
1927
}
2028

lib/components/preview-canvas.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useState, useRef } from "react"
88
import { useSvgGeneration, useSvgTransform } from "../hooks/preview-hooks"
99
import { cn } from "@/utils"
1010
export function PreviewCanvas() {
11-
const { circuitJson, lbrnOptions } = useWorkspace()
11+
const { circuitJson, lbrnOptions, isProcessingFile } = useWorkspace()
1212
const [viewMode, setViewMode] = useState<"lbrn" | "pcb" | "both">("lbrn")
1313
const { lbrnSvg, pcbSvg, isGenerating } = useSvgGeneration({
1414
circuitJson,
@@ -28,6 +28,29 @@ export function PreviewCanvas() {
2828
const handleRotate = () => {
2929
// TODO: implement rotation if needed
3030
}
31+
32+
// Show loading screen when processing file but no circuit yet
33+
if (!circuitJson && isProcessingFile) {
34+
return (
35+
<div className="h-full flex items-center justify-center">
36+
<div className="text-center">
37+
<div className="size-12 rounded-full border-2 border-primary/20 border-t-primary animate-spin mx-auto mb-4" />
38+
<p className="text-muted-foreground mb-4">Loading circuit...</p>
39+
<div className="w-64 mx-auto">
40+
<div className="h-2 bg-muted rounded-full overflow-hidden">
41+
<div
42+
className="h-full bg-primary rounded-full animate-pulse"
43+
style={{
44+
width: "100%",
45+
animation: "shimmer 1.5s ease-in-out infinite",
46+
}}
47+
/>
48+
</div>
49+
</div>
50+
</div>
51+
</div>
52+
)
53+
}
3154
return (
3255
<div className="h-full flex flex-col bg-muted/20">
3356
{/* Canvas Header */}

lib/components/workspace-context.tsx

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface WorkspaceState {
1717
lbrnFileContent: LbrnFileContent | null
1818
lbrnOptions: ConvertCircuitJsonToLbrnOptions
1919
isConverting: boolean
20+
isProcessingFile: boolean
2021
error: string | null
2122
}
2223

@@ -25,6 +26,7 @@ interface WorkspaceContextType extends WorkspaceState {
2526
setLbrnFileContent: (data: LbrnFileContent | null) => void
2627
setLbrnOptions: (options: Partial<ConvertCircuitJsonToLbrnOptions>) => void
2728
setIsConverting: (converting: boolean) => void
29+
setIsProcessingFile: (processing: boolean) => void
2830
setError: (error: string | null) => void
2931
convertToLbrn: (options?: any) => Promise<void>
3032
loadKicadPcbFile: (pcbContent: string) => Promise<void>
@@ -54,6 +56,7 @@ export function WorkspaceProvider({ children }: { children: ReactNode }) {
5456
origin: { x: 0, y: 0 },
5557
})
5658
const [isConverting, setIsConverting] = useState(false)
59+
const [isProcessingFile, setIsProcessingFile] = useState(false)
5760
const [error, setError] = useState<string | null>(null)
5861

5962
const setCircuitJson = (data: CircuitJson | null) => {
@@ -106,23 +109,15 @@ export function WorkspaceProvider({ children }: { children: ReactNode }) {
106109

107110
if (fileName.endsWith(".json")) {
108111
// Handle Circuit JSON files
109-
try {
110-
const text = await file.text()
111-
const circuitJsonData = JSON.parse(text)
112-
setCircuitJson(circuitJsonData)
113-
} catch (err) {
114-
alert("Invalid JSON file")
115-
}
112+
const text = await file.text()
113+
const circuitJsonData = JSON.parse(text)
114+
setCircuitJson(circuitJsonData)
116115
} else if (fileName.endsWith(".kicad_pcb")) {
117116
// Handle KiCad PCB files
118-
try {
119-
const text = await file.text()
120-
await loadKicadPcbFile(text)
121-
} catch (err) {
122-
alert(`Failed to convert KiCad file: ${err}`)
123-
}
117+
const text = await file.text()
118+
await loadKicadPcbFile(text)
124119
} else {
125-
alert("Please upload a .json or .kicad_pcb file")
120+
throw new Error("Please upload a .json or .kicad_pcb file")
126121
}
127122
}
128123

@@ -159,30 +154,36 @@ export function WorkspaceProvider({ children }: { children: ReactNode }) {
159154
const items = dataTransfer.items
160155
if (items.length === 0) return
161156

162-
// Check if it's a directory drop
163-
for (let i = 0; i < items.length; i++) {
164-
const item = items[i]
165-
const entry = item.webkitGetAsEntry?.()
166-
167-
if (entry?.isDirectory) {
168-
// Handle folder drop - look for .kicad_pcb file
169-
const kicadFile = await findKicadPcbInDirectory(
170-
entry as FileSystemDirectoryEntry,
171-
)
172-
if (kicadFile) {
173-
await processCircuitFile(kicadFile)
174-
return
175-
} else {
176-
alert("No .kicad_pcb file found in the dropped folder")
177-
return
157+
try {
158+
setIsProcessingFile(true)
159+
160+
// Check if it's a directory drop
161+
for (let i = 0; i < items.length; i++) {
162+
const item = items[i]
163+
const entry = item.webkitGetAsEntry?.()
164+
165+
if (entry?.isDirectory) {
166+
// Handle folder drop - look for .kicad_pcb file
167+
const kicadFile = await findKicadPcbInDirectory(
168+
entry as FileSystemDirectoryEntry,
169+
)
170+
if (kicadFile) {
171+
await processCircuitFile(kicadFile)
172+
return
173+
} else {
174+
alert("No .kicad_pcb file found in the dropped folder")
175+
return
176+
}
178177
}
179178
}
180-
}
181179

182-
// Handle file drops (existing behavior)
183-
const files = dataTransfer.files
184-
if (files.length > 0) {
185-
await processCircuitFile(files[0])
180+
// Handle file drops (existing behavior)
181+
const files = dataTransfer.files
182+
if (files.length > 0) {
183+
await processCircuitFile(files[0])
184+
}
185+
} finally {
186+
setIsProcessingFile(false)
186187
}
187188
}
188189

@@ -218,11 +219,13 @@ export function WorkspaceProvider({ children }: { children: ReactNode }) {
218219
lbrnFileContent,
219220
lbrnOptions,
220221
isConverting,
222+
isProcessingFile,
221223
error,
222224
setCircuitJson,
223225
setLbrnFileContent,
224226
setLbrnOptions,
225227
setIsConverting,
228+
setIsProcessingFile,
226229
setError,
227230
convertToLbrn,
228231
loadKicadPcbFile,

lib/components/workspace-layout.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function WorkspaceContent({
2626
sidebarOpen,
2727
setSidebarOpen,
2828
}: { sidebarOpen: boolean; setSidebarOpen: (open: boolean) => void }) {
29-
const { circuitJson } = useWorkspace()
29+
const { circuitJson, isProcessingFile } = useWorkspace()
3030
const navigate = useNavigate()
3131

3232
return (
@@ -61,8 +61,8 @@ export function WorkspaceContent({
6161

6262
{/* Main Content Area */}
6363
<div className="flex-1 flex overflow-hidden">
64-
{/* Left Settings Panel - Only show when circuit is loaded */}
65-
{circuitJson && (
64+
{/* Left Settings Panel - Only show when circuit is loaded and not processing */}
65+
{circuitJson && !isProcessingFile && (
6666
<aside
6767
className={`${
6868
sidebarOpen ? "translate-x-0" : "-translate-x-full"
@@ -74,7 +74,11 @@ export function WorkspaceContent({
7474

7575
{/* Canvas Preview Area */}
7676
<main className="flex-1 overflow-hidden">
77-
{circuitJson ? <PreviewCanvas /> : <BlankWorkspace />}
77+
{circuitJson || isProcessingFile ? (
78+
<PreviewCanvas />
79+
) : (
80+
<BlankWorkspace />
81+
)}
7882
</main>
7983
</div>
8084
</div>

0 commit comments

Comments
 (0)