feat: standard parts library, fuse box configurator, and theming

Standard parts (partsLibrary.js)
- ACDC TXL palette: 15 solids plus the 16 stocked stripe combinations
- 29 wire functions fix colour, stripe and gauge per signal, so a wire
  drawn from a given pin comes out identical on every build
- 16 pre-labelled parts: Skudak VCU (GRAY/BLACK), openinverter LDU
  23-pin, Dilong OBC/DCDC, BMW pedal, Honeywell CSSV1500, Bender
  ISO175, Bosch iBooster, Prius EPS, Volvo PS pump, cluster, DNR
  switch, 32-way enclosure bulkhead, contactors, VW MEB BMS
- OEM colours override the in-house standard where we splice into a
  factory loom (VW MEB LV connector, recovered from the VW bms diagram)
- Brown is ground, matching the MEB loom and European practice; orange
  is reserved for AC mains and OEM high-voltage runs

Fuse box configurator (pdmLibrary.js)
- GEP FRH-A12/A24 and the 48-way PDM as 280-footprint cavity grids
- Place, drag and rotate fuses, relays, diodes, breakers and bus bars
- Bus bars occupy their own collision layer and feed the blades they
  cross, suppressing those inputs so one wire supplies the run
- Cavity capacity is tracked; Apply is blocked while a placement
  overlaps or overhangs
- Naming a circuit renames its pins: "VCU" gives "VCU in" / "VCU out"

Theming (theme.js)
- Light and dark modes; canvas background, grid dots, part fill and
  part outline are configurable and persisted
- CSS converted to custom properties with property-aware light-mode
  pairs, since a colour used as text and as a surface must flip
  differently; no rule falls below 3:1 contrast in either theme
- Canvas devices, pins, labels, cable rows and the harness view are
  themed too, since Konva bakes colours in at draw time and cannot
  read CSS variables

Also
- Pass-through bulkheads: one device covering both connector faces,
  with a single centred label per circuit
- Pin labels scale with the device font instead of a fixed 8px
- Keyboard handling is modal-scoped, so Delete no longer removes the
  canvas device while the fuse box editor is open

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-19 10:54:49 -04:00
parent 43c0ea46c6
commit cca03cd2f3
8 changed files with 2410 additions and 200 deletions
+149
View File
@@ -38,6 +38,7 @@
</div>
<div class="toolbar-right">
<button id="btn-theme" title="Appearance — theme and colours">◐ Theme</button>
<button id="btn-git" title="Git version control">⎇ Git</button>
<button id="btn-drc" title="Run design-rule check">⚠ DRC</button>
<div id="saved-indicator">✓ Saved</div>
@@ -233,6 +234,9 @@
<div style="margin-top:4px">
<button id="prop-pinout-btn" style="display:none;width:100%;font-size:11px;padding:3px 6px">⊙ View Pinout</button>
</div>
<div style="margin-top:4px">
<button id="prop-pdm-btn" class="btn-primary" style="display:none;width:100%;font-size:11px;padding:4px 6px">▦ Configure Fuse Box</button>
</div>
</div>
</div>
@@ -515,6 +519,148 @@
</div>
</div>
<div id="theme-modal" style="display:none">
<div class="modal-box" style="min-width:420px;max-width:520px">
<div class="modal-header"><h3>◐ Appearance</h3></div>
<div class="modal-body" style="padding:14px;gap:14px">
<div class="cm-row">
<label class="cm-label">Mode</label>
<div style="display:flex;gap:6px">
<button id="theme-dark" class="pdm-add" style="flex:1">Dark</button>
<button id="theme-light" class="pdm-add" style="flex:1">Light</button>
</div>
</div>
<div class="cm-row">
<label class="cm-label">Canvas background</label>
<div class="theme-pick">
<input id="theme-canvas-bg" type="color">
<div id="theme-bg-swatches" class="theme-swatches"></div>
</div>
</div>
<div class="cm-row">
<label class="cm-label">Grid dots</label>
<div class="theme-pick"><input id="theme-grid-dot" type="color"></div>
</div>
<div class="cm-row">
<label class="cm-label">Part outline</label>
<div class="theme-pick"><input id="theme-dev-stroke" type="color"></div>
</div>
<div class="cm-row">
<label class="cm-label">Part fill</label>
<div class="theme-pick">
<input id="theme-dev-fill" type="color">
<label class="theme-inline">
<input id="theme-fill-bytype" type="checkbox"> Colour by device type
</label>
</div>
</div>
<p class="muted small" style="margin:0">
Wires keep their own colours — these control what they sit against.
</p>
</div>
<div class="modal-footer">
<button id="theme-reset">Reset to defaults</button>
<div style="flex:1"></div>
<button id="theme-close" class="btn-primary">Done</button>
</div>
</div>
</div>
<!-- Fuse box configurator — edits the selected PDM device on the canvas -->
<div id="pdm-modal" style="display:none">
<div class="modal-box" style="min-width:720px;max-width:900px">
<div class="modal-header">
<h3>▦ Fuse Box</h3>
<span id="pdm-linked-ref" class="git-badge"></span>
</div>
<div class="modal-body" style="padding:12px;gap:10px">
<div class="cm-row-2col">
<div class="cm-row">
<label class="cm-label">Module</label>
<select id="pdm-module" class="cm-input"></select>
</div>
<div class="cm-row">
<label class="cm-label">Reference / label</label>
<input id="pdm-label" class="cm-input" type="text" placeholder="e.g. PDM1 — front">
</div>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
<button id="pdm-add-fuse" class="pdm-add">+ Fuse</button>
<button id="pdm-add-spdt" class="pdm-add">+ Relay 5-pin</button>
<button id="pdm-add-spst" class="pdm-add">+ Relay 4-pin</button>
<button id="pdm-add-diode" class="pdm-add">+ Diode</button>
<button id="pdm-add-breaker" class="pdm-add">+ Breaker</button>
<button id="pdm-add-bus" class="pdm-add">+ Bus bar</button>
<div style="width:1px;height:18px;background:#2a2a44"></div>
<button id="pdm-rotate" class="pdm-add" title="Rotate selected (R)">⟳ Rotate</button>
<button id="pdm-delete" class="pdm-add" title="Remove selected (Del)">✕ Remove</button>
</div>
<div class="pdm-split">
<!-- Cavity grid -->
<div class="pdm-grid-pane">
<div id="pdm-grid" class="pdm-grid" tabindex="0"></div>
<div id="pdm-capacity" class="pdm-capacity">
<div class="pdm-bar"><div id="pdm-bar-fill" class="pdm-bar-fill"></div></div>
<span id="pdm-capacity-text" class="pdm-capacity-text"></span>
</div>
<p class="muted small" style="margin:0">Drag to move · R to rotate · Del to remove</p>
</div>
<!-- Selected component + full pinout -->
<div class="pdm-side">
<div id="pdm-sel" style="display:none">
<label class="cm-label">Circuit name</label>
<input id="pdm-sel-name" class="cm-input" type="text" placeholder="e.g. Coolant pump">
<div class="cm-row-2col" style="margin-top:6px">
<div class="cm-row">
<label class="cm-label">Component</label>
<select id="pdm-sel-type" class="cm-input"></select>
</div>
<div class="cm-row">
<label class="cm-label">Rating</label>
<select id="pdm-sel-rating" class="cm-input"></select>
</div>
</div>
</div>
<p id="pdm-sel-empty" class="muted small" style="padding:8px 0">
Select a component in the grid to name it.
</p>
<label class="cm-label" style="margin-top:8px">Pinout</label>
<div class="pdm-pinout-wrap">
<table class="pdm-table">
<thead>
<tr><th style="width:54px">Cavity</th><th style="width:46px">Term</th><th>Pin name</th></tr>
</thead>
<tbody id="pdm-pinout"></tbody>
</table>
<p id="pdm-empty" class="muted small" style="padding:12px;text-align:center">
Empty box. Add a fuse or relay to begin.
</p>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<span id="pdm-warn" class="pdm-warn"></span>
<div style="flex:1"></div>
<button id="pdm-cancel">Cancel</button>
<button id="pdm-save" class="btn-primary">Apply to device</button>
</div>
</div>
</div>
<div id="git-modal" style="display:none">
<div class="modal-box" style="min-width:520px;max-width:700px">
<div class="modal-header">
@@ -563,9 +709,12 @@
</div>
<script src="https://unpkg.com/konva@9/konva.min.js"></script>
<script src="/js/theme.js"></script>
<script src="/js/api.js"></script>
<script src="/js/deviceTypes.js"></script>
<script src="/js/connectorLibrary.js"></script>
<script src="/js/partsLibrary.js"></script>
<script src="/js/pdmLibrary.js"></script>
<script src="/js/canvas.js"></script>
<script src="/js/formboard.js"></script>
<script src="/js/app.js"></script>