Files
busbar-designer/static/index.html
T
wenil 676d976937 holder + scad: SCAD source editor on /holder + new /scad universal renderer
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.
2026-05-25 12:52:18 +03:00

179 lines
7.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Busbar Designer</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header class="topbar">
<h1>Busbar Designer</h1>
<nav class="topbar-nav">
<a href="/holder">Holder</a>
<a href="/" class="active">Busbars</a>
<a href="/scad">SCAD</a>
</nav>
<div class="project-bar">
<select id="project-select" title="Open project">
<option value="">— select project —</option>
</select>
<input id="project-name" type="text" placeholder="Project name" />
<button id="btn-project-new" title="Create a new empty project">+ New</button>
<button id="btn-project-del" class="danger" title="Delete current project">×</button>
<button id="btn-save-now" class="primary" title="Save current state to server now (no waiting for auto-save)">Save</button>
<button id="btn-history" title="View snapshot history">History</button>
<span id="save-status" class="save-status"></span>
<span id="status" class="topbar-status">No cells loaded</span>
</div>
<div class="actions">
<button id="btn-save" title="Download project as JSON file">Save .json</button>
<button id="btn-load" title="Import project from JSON file">Load .json</button>
<input type="file" id="file-load" accept=".json" hidden />
<span class="sep"></span>
<button id="btn-export-step" class="primary">Export STEP</button>
<button id="btn-export-dxf">Export DXF</button>
<button id="btn-export-svg">Export SVG</button>
</div>
</header>
<main>
<aside class="left">
<section class="panel">
<h2>1. Cell import</h2>
<div class="tabs" id="import-tabs">
<button class="tab active" data-tab="paste">Paste</button>
<button class="tab" data-tab="csv">CSV</button>
<button class="tab" data-tab="json">JSON</button>
<button class="tab" data-tab="gen">Generator</button>
</div>
<div class="tab-body" data-tab-body="paste">
<p class="hint">Paste OpenSCAD ECHO lines (<code>ECHO: "Cell 1: x = …, y = …"</code>) or plain <code>id x y</code> lines.</p>
<textarea id="paste-text" rows="8" placeholder='ECHO: "Cell 1: x = 0, y = 0"&#10;ECHO: "Cell 2: x = 22.8, y = 0"'></textarea>
<button id="btn-import-paste">Import</button>
</div>
<div class="tab-body hidden" data-tab-body="csv">
<p class="hint">CSV with optional header: <code>index,x,y</code></p>
<textarea id="csv-text" rows="8" placeholder="1,0,0&#10;2,22.8,0"></textarea>
<button id="btn-import-csv">Import</button>
</div>
<div class="tab-body hidden" data-tab-body="json">
<p class="hint">JSON: <code>[{"id":1,"x":0,"y":0}, …]</code> or <code>[[x,y], …]</code></p>
<textarea id="json-text" rows="8" placeholder='[[0,0],[22.8,0]]'></textarea>
<button id="btn-import-json">Import</button>
</div>
<div class="tab-body hidden" data-tab-body="gen">
<p class="hint">Exact formulas from <code>hex_cell.scad</code>.</p>
<div class="grid">
<label>Cell dia (mm) <input type="number" id="gen-cell-dia" value="21.2" step="0.1"></label>
<label>Wall (mm) <input type="number" id="gen-wall" value="0.8" step="0.1"></label>
<label>Rows <input type="number" id="gen-rows" value="4" min="1"></label>
<label>Cols <input type="number" id="gen-cols" value="6" min="1"></label>
<label>Style
<select id="gen-style">
<option value="rect">rect</option>
<option value="para">para</option>
<option value="tria">tria</option>
</select>
</label>
</div>
<button id="btn-import-gen">Generate</button>
</div>
</section>
<section class="panel">
<h2>2. Cell &amp; busbar params</h2>
<div class="preset-row">
<select id="preset-select">
<option value="">— saved preset —</option>
</select>
<button id="btn-preset-apply" title="Apply selected preset">Apply</button>
<button id="btn-preset-save" class="primary" title="Save current params as new preset">Save as preset</button>
<button id="btn-preset-del" class="danger" title="Delete selected preset">×</button>
</div>
<div class="grid">
<label>Cell dia (mm) <input type="number" id="p-cell-dia" value="21.2" step="0.1"></label>
<label>Opening dia (mm) <input type="number" id="p-opening-dia" value="15.2" step="0.1"></label>
<label>Pad radius (mm) <input type="number" id="p-pad-radius" value="9.0" step="0.1" title="Disc radius over each cell (panel + wire)"></label>
<label>Strip width (mm) <input type="number" id="p-strip-width" value="6.0" step="0.1" title="Connector width between cells"></label>
<label>Hole shape
<select id="p-hole-shape">
<option value="cross" selected>cross (slit)</option>
<option value="circle">circle</option>
</select>
</label>
<label>Hole radius (mm) <input type="number" id="p-hole-radius" value="6.0" step="0.1" title="For cross: half-length of each arm; for circle: radius"></label>
<label>Slit width (mm) <input type="number" id="p-slit-width" value="1.0" step="0.1" title="Width of each cross arm"></label>
<label>Neighbor factor <input type="number" id="p-neighbor-factor" value="1.15" step="0.05" min="1.0" title="Bridge two cells if distance ≤ factor × shortest pair distance"></label>
<label class="checkbox"><input type="checkbox" id="p-extrude"> Extrude solid</label>
<label>Thickness (mm) <input type="number" id="p-thickness" value="0.2" step="0.05"></label>
</div>
</section>
<section class="panel">
<h2>3. Busbars</h2>
<div class="busbar-toolbar">
<button id="btn-new-busbar" class="primary">+ New busbar from selection</button>
<span class="sel-info" id="sel-info">0 selected</span>
</div>
<ul id="busbar-list" class="busbar-list"></ul>
<p class="hint">Click cell to select. Shift+click extend; Alt+click deselect. Right-mouse drag to pan; wheel to zoom.</p>
</section>
</aside>
<section class="right">
<div class="viewport-wrap">
<canvas id="viewport"></canvas>
<div class="viewport-overlay">
<div id="cursor-pos">x: — , y: —</div>
<div id="zoom-info">1.0 px/mm</div>
</div>
</div>
<div class="step-preview-wrap collapsed" id="step-preview-wrap">
<div class="step-preview-header">
<button id="step-preview-collapse" class="step-preview-collapse" title="Show / hide preview"></button>
<label class="checkbox">
<input type="checkbox" id="step-preview-toggle" checked>
Live STEP preview
</label>
<span id="step-preview-status" class="hint">idle</span>
</div>
<div class="step-preview" id="step-preview-content">
<div class="step-preview-empty">Create a busbar to see the exported geometry here.</div>
</div>
</div>
</section>
</main>
<!-- History modal -->
<div id="history-modal" class="modal hidden">
<div class="modal-content">
<div class="modal-header">
<h3>Snapshot history</h3>
<button id="btn-history-close" class="modal-close">×</button>
</div>
<div class="modal-body">
<p class="hint">Each auto-save creates a snapshot of the prior state (max 20 per project). Restore rolls back; the current state is auto-snapshotted first.</p>
<ul id="history-list" class="history-list"></ul>
</div>
</div>
</div>
<script src="js/importer.js"></script>
<script src="js/groups.js"></script>
<script src="js/geometry.js"></script>
<script src="js/viewport.js"></script>
<script src="js/exporter.js"></script>
<script src="js/api.js"></script>
<script src="js/app.js"></script>
</body>
</html>