Skip to content

Commit

Permalink
Merge branch 'main' into sma
Browse files Browse the repository at this point in the history
  • Loading branch information
techmannih authored Feb 11, 2025
2 parents ecf77e2 + bfe37e7 commit 94f8190
Show file tree
Hide file tree
Showing 23 changed files with 769 additions and 6 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/bun-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Created using @tscircuit/plop (npm install -g @tscircuit/plop)
name: Bun Test

on:
pull_request:
push:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Run tests
run: bun test
6 changes: 3 additions & 3 deletions .github/workflows/deploy-gallery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

Expand All @@ -24,7 +24,7 @@ jobs:
run: bun run generate-gallery

- name: Rename gallery.html to index.html
run: mv public/gallery.html public/index.html
run: cp gallery/index.html public/index.html

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dist
*.diff.png
package-lock.json
public
gallery/content.ts
Binary file modified bun.lockb
100644 → 100755
Binary file not shown.
75 changes: 75 additions & 0 deletions gallery/generate-grid-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import path from "path"
import fs from "fs"

const snapshotsDir = path.join(__dirname, "..", "tests", "__snapshots__")
const outputFile = path.join(__dirname, "content.ts")

function ensureDirectoryExists(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true })
}
}

function getSvgFiles(dir) {
try {
return fs.readdirSync(dir).filter((file) => file.endsWith(".snap.svg"))
} catch (error) {
console.warn(
`Directory not found: ${dir}. Proceeding with empty file list.`,
)
return []
}
}

function readAndFormatSvg(filePath) {
try {
const svgContent = fs
.readFileSync(filePath, "utf-8")
.replace(/width="\d+"/, 'width="300"')
.replace(/height="\d+"/, 'height="225" viewBox="0 0 800 600"')
return svgContent
} catch (error) {
console.warn(`Failed to read file: ${filePath}. Skipping.`)
return null
}
}

function generateFootprintsData(svgFiles) {
return svgFiles.reduce((data, file) => {
const filePath = path.join(snapshotsDir, file)
const svgContent = readAndFormatSvg(filePath)

if (svgContent) {
data.push({
svgContent,
title: path.basename(file, ".snap.svg"),
})
}
return data
}, [])
}

// Ensure output directory exists
ensureDirectoryExists(path.dirname(outputFile))

// Process SVG files
const svgFiles = getSvgFiles(snapshotsDir)
const data = generateFootprintsData(svgFiles)

// Write to output file
try {
fs.writeFileSync(
outputFile,
`export default ${JSON.stringify(data, null, 2)};`,
)
console.log(`Data successfully written to ${outputFile}`)
} catch (error) {
console.error(`Failed to write to ${outputFile}:`, error)
}

// Build site
const buildOutput = await Bun.build({
outdir: "public",
entrypoints: ["./gallery/index.tsx"],
})
console.log("\n", buildOutput)
21 changes: 21 additions & 0 deletions gallery/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>tscircuit/footprinter gallery</title>
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
<style>
html {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
min-height: 100vh;
overflow-x: hidden;
}
</style>
</head>
<body></body>
<script type="module" src="index.js"></script>
</html>
173 changes: 173 additions & 0 deletions gallery/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
import React, { useState, useCallback } from "react"
import ReactDOM from "react-dom/client"
import { fp } from "src/footprinter"
// @ts-ignore data is built during CI
import data from "./content"

interface Footprint {
svgContent: string
title: string
}

const FootprintCreator: React.FC = () => {
const [definition, setDefinition] = useState("")
const [generatedSvg, setGeneratedSvg] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState("")

// Generate the SVG based on the provided footprint definition.
const generateFootprint = useCallback(async (input: string) => {
setError("")
setGeneratedSvg(null)

if (!input.trim()) {
setError("Please enter a footprint definition.")
return
}

setLoading(true)
try {
const circuitJson = fp.string(input).circuitJson()
const svgContent = convertCircuitJsonToPcbSvg(circuitJson)
setGeneratedSvg(svgContent)
} catch (err: any) {
setError(err.message)
} finally {
setLoading(false)
}
}, [])

// Form submission triggers generation.
const handleGenerate = (e: React.FormEvent) => {
e.preventDefault()
generateFootprint(definition)
}

// When a grid item is clicked, update the definition and generate its SVG.
const handleFootprintClick = (footprint: Footprint) => {
setDefinition(footprint.title)
generateFootprint(footprint.title)
window.scrollTo({ top: 0, behavior: "smooth" })
}

// Allow generation on pressing Enter (without Shift).
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
generateFootprint(definition)
}
}

return (
<div className="min-h-screen bg-gradient-to-br from-blue-100 to-purple-100">
{/* Header */}
<header className="bg-blue-600 text-white py-4 shadow-md">
<div className="container mx-auto px-4 flex items-center">
<h1 className="text-2xl font-bold">
<a
href="https://github.com/tscircuit/footprinter"
className="hover:underline"
>
@tscircuit/footprinter
</a>
</h1>
<div className="ml-auto">
<a
href="https://github.com/tscircuit/footprinter"
target="_blank"
rel="noopener noreferrer"
>
<img
alt="GitHub stars"
src="https://img.shields.io/github/stars/tscircuit/footprinter?style=social"
className="h-6"
/>
</a>
</div>
</div>
</header>

{/* Main Content */}
<main className="container mx-auto px-4 py-6">
<div className="bg-white relative rounded-lg shadow-lg p-6">
{/* Responsive Left/Right Layout */}
<div className="grid place-items-center w-full grid-cols-1 md:grid-cols-5 gap-4 gap-6">
{/* Left: Form */}
<section className="w-full h-full grid place-items-center col-span-2">
<form onSubmit={handleGenerate} className="flex flex-col w-full">
<textarea
spellCheck={false}
placeholder="Enter footprint definition (e.g., breakoutheaders_left15_right15_w8mm_p1.54mm)"
value={definition}
onChange={(e) => setDefinition(e.target.value)}
onKeyDown={handleKeyDown}
className="w-full p-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400 resize-y min-h-[150px] text-lg font-medium"
/>
<button
type="submit"
disabled={loading}
className="mt-4 bottom-4 w-full bg-blue-600 text-white py-3 rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors cursor-pointer"
>
{loading ? "Generating..." : "Generate Footprint"}
</button>
</form>
{error && (
<p className="mt-3 text-red-500 font-bold text-center">
{error}
</p>
)}
</section>

{/* Right: SVG Preview */}
<section className="w-full h-full rounded-md col-span-3 grid place-items-center shadow p-4">
{generatedSvg ? (
<>
<img
src={`data:image/svg+xml;base64,${btoa(generatedSvg)}`}
alt="Generated Footprint"
className="w-full h-full object-contain"
/>
</>
) : (
<p className="text-gray-500">
Preview will appear here once generated.
</p>
)}
</section>
</div>
</div>

{/* Gallery Section */}
<section className="mt-10">
<h2 className="text-2xl font-bold text-center mb-6">
Existing Footprints
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{data.map((footprint, index) => (
<div
key={index}
className="relative grid place-items-center"
onClick={() => handleFootprintClick(footprint)}
>
<img
src={`data:image/svg+xml;base64,${btoa(
footprint.svgContent,
)}`}
alt={`${footprint.title} Footprint SVG`}
className="rounded-md shadow cursor-pointer hover:shadow-lg transition-shadow w-full h-full object-contain"
/>
<div className="absolute bottom-2 left-2 bg-white bg-opacity-80 px-2 py-1 text-xs rounded !break-all">
{footprint.title}
</div>
</div>
))}
</div>
</section>
</main>
</div>
)
}

const root = ReactDOM.createRoot(document.body!)
root.render(<FootprintCreator />)
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@tscircuit/footprinter",
"type": "module",
"version": "0.0.115",
"version": "0.0.120",
"description": "",
"main": "dist/index.js",
"files": [
Expand All @@ -12,7 +12,7 @@
"dev": "cd previews && npm i && npm run storybook",
"build": "tsup ./src/index.ts --format esm --dts --sourcemap",
"format": "biome format . --write",
"generate-gallery": "bun run scripts/generate-gallery-page.ts"
"generate-gallery": "bun run gallery/generate-grid-content.ts"
},
"keywords": [],
"author": "",
Expand All @@ -27,6 +27,8 @@
"circuit-to-svg": "^0.0.42",
"esbuild": "^0.21.4",
"esbuild-register": "^3.5.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"ts-node": "^10.9.2",
"tsup": "^8.0.2",
"typescript": "^5.4.5"
Expand Down
3 changes: 3 additions & 0 deletions src/fn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ export { pad } from "./pad"
export { to92 } from "./to92"
export { sod523 } from "./sod523"
export { sop8 } from "./sop8"
export { sod923 } from "./sod923"
export { sod882 } from "./sod882"
export { sod323f } from "./sod323f"
export { sod123f } from "./sod123f"
export { sod723 } from "./sod723"
export { sod128 } from "./sod128"
export { to220 } from "./to220"
export { minimelf } from "./minimelf"
export { sod882d } from "./sod882d"
export { melf } from "./melf"
export { micromelf } from "./micromelf"
Expand Down
Loading

0 comments on commit 94f8190

Please sign in to comment.