diff --git a/.gitignore b/.gitignore index 7c07181..91376a1 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ debug-output # Snapshot diff files *.diff.png +.vercel diff --git a/package.json b/package.json index 285ef99..83aea27 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,14 @@ "test:update-snapshots": "BUN_UPDATE_SNAPSHOTS=1 bun test", "build": "tsup-node ./lib/index.ts --format esm --dts", "format": "biome format --write .", - "format:check": "biome format ." + "format:check": "biome format .", + "start": "bun site/index.html", + "build:site": "bun build site/index.html --outdir=site-export" }, "devDependencies": { "@biomejs/biome": "^2.3.8", + "@resvg/resvg-js": "^2.6.2", + "@resvg/resvg-wasm": "^2.6.2", "@types/bun": "latest", "circuit-json": "^0.0.286", "looks-same": "^10.0.1", diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..8e2c0fc --- /dev/null +++ b/site/index.html @@ -0,0 +1,253 @@ + + + + + + Circuit JSON to STEP Converter + + + +
+

Circuit JSON to STEP Converter

+

Convert your Circuit JSON files to STEP format for 3D CAD software

+ +
+
📁
+
Drag & Drop your Circuit JSON file here
+
or click to browse
+
+ + + +
+
+
+
+ +
+

Options

+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+
+ + + + diff --git a/site/main.tsx b/site/main.tsx new file mode 100644 index 0000000..17d356d --- /dev/null +++ b/site/main.tsx @@ -0,0 +1,172 @@ +import { circuitJsonToStep } from "../lib/index" + +// Get DOM elements +const uploadArea = document.getElementById("uploadArea")! +const fileInput = document.getElementById("fileInput")! as HTMLInputElement +const fileInfo = document.getElementById("fileInfo")! +const fileName = document.getElementById("fileName")! +const fileSize = document.getElementById("fileSize")! +const convertBtn = document.getElementById("convertBtn")! as HTMLButtonElement +const clearBtn = document.getElementById("clearBtn")! +const status = document.getElementById("status")! +const includeComponentsCheckbox = document.getElementById( + "includeComponents", +)! as HTMLInputElement +const includeExternalMeshesCheckbox = document.getElementById( + "includeExternalMeshes", +)! as HTMLInputElement + +let currentFile: File | null = null +let circuitJson: any = null + +// Handle click on upload area +uploadArea.addEventListener("click", () => { + fileInput.click() +}) + +// Handle file selection +fileInput.addEventListener("change", (e) => { + const target = e.target as HTMLInputElement + if (target.files && target.files.length > 0 && target.files[0]) { + handleFile(target.files[0]) + } +}) + +// Handle drag over +uploadArea.addEventListener("dragover", (e) => { + e.preventDefault() + uploadArea.classList.add("dragover") +}) + +// Handle drag leave +uploadArea.addEventListener("dragleave", () => { + uploadArea.classList.remove("dragover") +}) + +// Handle drop +uploadArea.addEventListener("drop", (e) => { + e.preventDefault() + uploadArea.classList.remove("dragover") + + if ( + e.dataTransfer && + e.dataTransfer.files.length > 0 && + e.dataTransfer.files[0] + ) { + handleFile(e.dataTransfer.files[0]) + } +}) + +// Handle includeComponents checkbox change +includeComponentsCheckbox.addEventListener("change", () => { + if (!includeComponentsCheckbox.checked) { + includeExternalMeshesCheckbox.checked = false + includeExternalMeshesCheckbox.disabled = true + } else { + includeExternalMeshesCheckbox.disabled = false + } +}) + +// Initialize external meshes checkbox state +includeExternalMeshesCheckbox.disabled = true + +// Handle file +async function handleFile(file: File) { + currentFile = file + + // Show file info + fileName.textContent = file.name + fileSize.textContent = `${(file.size / 1024).toFixed(2)} KB` + fileInfo.classList.add("visible") + + // Read and parse the file + try { + showStatus("Reading file...", "processing") + const text = await file.text() + circuitJson = JSON.parse(text) + + // Enable convert button + convertBtn.disabled = false + showStatus("File loaded successfully! Ready to convert.", "success") + } catch (error) { + showStatus( + `Error reading file: ${error instanceof Error ? error.message : String(error)}`, + "error", + ) + convertBtn.disabled = true + circuitJson = null + } +} + +// Convert and download +convertBtn.addEventListener("click", async () => { + if (!circuitJson) return + + try { + showStatus("Converting to STEP format...", "processing") + convertBtn.disabled = true + + // Allow UI to update + await new Promise((resolve) => setTimeout(resolve, 100)) + + // Get base filename without extension + const baseName = currentFile!.name.replace(/\.json$/i, "") + + // Get options from checkboxes + const includeComponents = includeComponentsCheckbox.checked + const includeExternalMeshes = includeExternalMeshesCheckbox.checked + + // Convert to STEP + const stepContent = await circuitJsonToStep(circuitJson, { + includeComponents, + includeExternalMeshes: includeComponents && includeExternalMeshes, + }) + + // Download the STEP file + downloadFile(`${baseName}.step`, stepContent) + + showStatus("Conversion complete! File downloaded.", "success") + convertBtn.disabled = false + } catch (error) { + showStatus( + `Error during conversion: ${error instanceof Error ? error.message : String(error)}`, + "error", + ) + convertBtn.disabled = false + console.error(error) + } +}) + +// Clear button +clearBtn.addEventListener("click", () => { + currentFile = null + circuitJson = null + fileInput.value = "" + fileInfo.classList.remove("visible") + convertBtn.disabled = true + hideStatus() +}) + +// Helper function to download a file +function downloadFile(filename: string, content: string) { + const blob = new Blob([content], { type: "application/step" }) + const url = URL.createObjectURL(blob) + const a = document.createElement("a") + a.href = url + a.download = filename + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) +} + +// Helper function to show status +function showStatus(message: string, type: "success" | "error" | "processing") { + status.textContent = message + status.className = `status visible ${type}` +} + +// Helper function to hide status +function hideStatus() { + status.classList.remove("visible") +} diff --git a/tsconfig.json b/tsconfig.json index f01ef2e..7474645 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,5 +26,5 @@ "noUnusedParameters": false, "noPropertyAccessFromIndexSignature": false }, - "exclude": ["circuit-json", "stepts"] + "exclude": ["circuit-json", "stepts", "site"] }