Phase 2 — SCAD editor on /holder
================================
Some adjustments need more control than the parameter form gives
(e.g. tweaking the .scad logic itself). A collapsible CodeMirror-based
editor now slides up from the bottom of the viewport when you click
the "</> Source" button at the bottom-right.
- holder.html / holder.css: editor panel, toggle button, Reset and
Close buttons. CodeMirror 5 loaded from cdnjs (single CSS + JS
pair, clike mode for syntax highlighting since OpenSCAD is C-like).
- holder-app.js: lazy-initialises the editor on first show, fetches
bundled source from /api/holder/source, tracks whether the editor
content has been modified ("modified" tag in the panel title).
When the editor is visible AND content differs from bundled,
_doRender() switches from /api/holder/render -> /api/scad/render
with {source, params}. Cell-count cap doesn't apply in that mode
(the source may not even use the holder schema).
Phase 3 — Universal OpenSCAD playground at /scad
================================================
A standalone page for rendering arbitrary OpenSCAD. Paste code, click
Render (or Ctrl+Enter), see STL in the same Three.js viewer the
holder uses. Useful for prototyping new generators before wiring
them into a parameter form.
- New page: static/scad.html + scad.css + js/scad-app.js. Reuses
holder-viewer.js (the Three.js scene module is generic enough).
- "Load example" populates a 6-hole rounded bracket so new users
can verify the renderer works in two clicks.
- Same progress UI (elapsed timer + indeterminate bar + Cancel
via AbortController) as /holder.
- Top nav now has Holder / Busbars / SCAD on every page.
Backend (shared by phase 2 & 3)
================================
- holder.py: split _run_openscad() out of render_stl(). New
render_source(source, params=None) writes the source to a temp
file and renders it; enforces MAX_SOURCE_BYTES (default 512 KB)
and RENDER_TIMEOUT but skips the cell-count cap. New bundled_source()
returns the hex_cell.scad text for editor pre-population.
- app.py: GET /api/holder/source returns the bundled .scad text.
POST /api/scad/render takes {source, params?} and returns STL +
X-Holder-Dimensions header. ValueError -> 400, RuntimeError -> 500.
_stl_response() factored out so both render endpoints emit the same
headers consistently.
Busbar Designer + Hex Holder Designer
Web "combine" for cylindrical-cell battery pack production. Two pages:
| Page | Purpose |
|---|---|
/holder |
Parametric hex cell holder designer — adjust pack size / cell type, see 3D, download STL for printing. |
/ |
Busbar designer — import cell coordinates, group into busbars (parallel + series), export STEP/DXF/SVG for laser cutting. |
The holder page has a Design busbars → button that auto-creates a busbar project pre-loaded with the exact cell coordinates from the configured holder.
Web tool for designing nickel/copper busbars over cylindrical-cell battery packs (21700, 18650, ...) built with hex-shaped cell holders.
Workflow:
- Import cell-center coordinates (paste from OpenSCAD console, CSV, JSON — or generate from
cell_dia / wall / rows / cols). - View cells on a 2D canvas at real mm scale.
- Click cells to select; group selected cells into named busbars (parallel groups), then chain busbars in series.
- Each busbar is drawn as a strip of configurable width with circular pads + welding-window holes over each cell center.
- Export busbar geometry to STEP (mandatory), plus DXF and SVG for laser/waterjet cutting.
Quick start
Requires Python 3.10+ and OpenSCAD on the host (only needed for the /holder page; the busbar page works without it).
# Windows / PowerShell
cd busbar-designer
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python app.py
OpenSCAD install: Windows — winget install OpenSCAD.OpenSCAD; Linux — apt install openscad.
Then open http://localhost:5000 for the busbar designer, or http://localhost:5000/holder for the hex holder designer.
First-time install pulls build123d (~ 200 MB; bundles OpenCASCADE via OCP) and may take several minutes.
Linux / macOS
cd busbar-designer
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
Geometry source of truth
All cell-center math matches Addy's Hex-Cell-Holder/hex_cell.scad:
| Variable | Formula | 21700 default |
|---|---|---|
hex_w |
cell_dia + 2*wall |
22.8 mm |
hex_pt |
hex_w / 2 / cos(30°) |
13.164 mm |
opening_dia |
cell_dia − 2*cell_top_overlap |
15.2 mm |
| row pitch (Y) | 1.5 * hex_pt |
19.746 mm |
| col pitch (X) | hex_w |
22.8 mm |
rect row shift |
0 (even) / 0.5*hex_w (odd) |
11.4 mm |
Import formats
The importer auto-detects:
- OpenSCAD ECHO:
ECHO: "Cell 1: x = 0.0, y = 0.0"(paste straight from console).- If your
hex_cell.scaddoesn't print these, dropscad_snippet.scad(in this repo) at the bottom of the file.
- If your
- CSV:
index,x,y(header optional). - JSON:
[{"id":1,"x":0,"y":0}, ...]or[[x,y], ...]. - Generator tab: enter
cell_dia / wall / rows / cols / pack_style— coordinates are computed with the exact OpenSCAD formulas.
Persistence
State is stored in a SQLite file (data/busbar.db by default; override with BUSBAR_DB env var):
| Table | What |
|---|---|
projects |
Full editor state (cells + busbars + params) per project |
presets |
Named param bundles you save from the params panel |
snapshots |
Auto-history per project; max SNAPSHOT_RETENTION (default 20) kept |
REST API (all JSON):
| Method | Path | What |
|---|---|---|
| GET | /api/projects |
list projects |
| POST | /api/projects |
create {name, data} |
| GET | /api/projects/{id} |
full project |
| PUT | /api/projects/{id} |
update {name?, data?, snapshot?, note?} |
| DELETE | /api/projects/{id} |
delete (cascades to snapshots) |
| GET | /api/projects/{id}/snapshots |
list snapshots |
| GET | /api/snapshots/{sid} |
full snapshot |
| POST | /api/snapshots/{sid}/restore |
roll project back |
| GET | /api/presets |
list presets |
| POST | /api/presets |
create {name, params} |
| PUT | /api/presets/{id} |
update |
| DELETE | /api/presets/{id} |
delete |
UI behaviour:
- Active project ID is in the URL:
?p=42. Bookmark or open from another device → same state. - Auto-save fires 1.5 s after the last change. The first save in any 60 s window also creates a history snapshot.
- "History" button shows the snapshot list with a Restore action.
- Presets are saved server-side and apply to any project with one click.
Backup: the entire DB is one file. Copy data/busbar.db (or the data/ folder in Docker) for offline backup.
Export
| Format | Use | Backend |
|---|---|---|
| STEP | CAD (FreeCAD, Fusion, SolidWorks) | build123d → OpenCASCADE |
| DXF | Laser cutter / CNC (2D) | build123d → ezdxf |
| SVG | Quick preview, illustrator | build123d |
STEP files are written as flat 3D faces (not solids) so a fab shop can lay them out before cutting. If you need a thick solid (e.g., 0.2 mm nickel) toggle "Extrude busbars" — the backend will extrude each strip to the configured thickness.
Project layout
busbar-designer/
├── app.py # Flask server: static files + /api/export/{step,dxf,svg}
├── busbar_export.py # build123d → STEP / DXF / SVG
├── requirements.txt
├── scad_snippet.scad # paste into hex_cell.scad to echo per-cell coordinates
├── CLAUDE.md # architecture notes for Claude / future contributors
├── tests/
│ └── test_export.py
└── static/
├── index.html
├── styles.css
└── js/
├── app.js
├── importer.js
├── viewport.js
├── groups.js
├── geometry.js
└── exporter.js
Deploy to a home server
Recommended: manual LXC + install.sh
- Create LXC in Proxmox web UI — Debian 12 standard template, unprivileged, 4 cores / 8 GB RAM / 32 GB disk, network DHCP. Enable
Features → nesting=1. - Inside the LXC (
pct enter <CTID>from the host):REPO_URL=https://gitea.example.com/me/busbar-designer.git \ bash -c "$(curl -fsSL https://gitea.example.com/me/busbar-designer/raw/branch/main/deploy/install.sh)" - Open
http://<lxc-ip>:5000from any device on your LAN.
Update after git push to your Gitea:
pct exec <CTID> -- bash /opt/busbar-designer/deploy/update.sh
See deploy/README.md for backup/restore, troubleshooting, and what install.sh actually installs.
Alternative: Docker
git clone <repo> busbar-designer && cd busbar-designer
mkdir -p data
docker compose up -d --build
data/ is bind-mounted at /app/data so the SQLite DB survives docker compose down.
Reverse proxy (optional)
If you want it behind your existing nginx / Caddy / Traefik:
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_read_timeout 120s; # STEP export can take a few seconds
client_max_body_size 5m;
}
For Caddy:
busbar.example.com {
reverse_proxy 127.0.0.1:5000
}
Security
The app has no authentication. Either:
- expose only on your LAN (default), or
- put it behind an authenticating reverse proxy (Caddy
basic_auth, Authelia, Pocket-ID, etc.).
Troubleshooting
pip install build123dfails on Windows / Python 3.13 — the prebuilt OCP wheels lag the latest Python release. If yourpython --versionis 3.13, install Python 3.12 from python.org and use it for the venv:py -3.12 -m venv .venv.- STEP opens empty in FreeCAD — make sure at least one busbar exists and contains ≥ 1 cell. The exporter writes only assigned busbars.
- Port 5000 in use — set
PORT=5050 python app.py.
License
MIT.