app
Wire Harness Designer
Public Made by Adomby adom
Design wire harnesses in the browser and export a professional, print-ready PDF assembly drawing. Pick connectors, wire gauge, colour and stripe, sleeving, and connector-to-connector lengths; get a live fishbone diagram with a wire schedule and title block. Click any wire or connector in the drawing to edit it. Save and reload the whole harness as JSON.
master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
/*
* render-svg.js — draw list -> SVG string (live on-screen preview).
* Consumes exactly the ops produced by harness-layout.js.
*/
(function (global) {
'use strict';
function esc(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
.replace(/"/g, '"');
}
function fontFamily(f) {
return f === 'mono'
? "'JetBrains Mono','SFMono-Regular',Consolas,monospace"
: "'Helvetica Neue',Helvetica,Arial,sans-serif";
}
function anchor(a) { return a === 'middle' ? 'middle' : a === 'end' ? 'end' : 'start'; }
function dashAttr(d) { return d ? ' stroke-dasharray="' + d.join(',') + '"' : ''; }
function pickAttr(o) {
if (!o.pick) return '';
return ' data-pick-type="' + o.pick.type + '" data-pick-id="' + esc(o.pick.id) + '" class="pickable"';
}
function segToD(d) {
var out = '';
for (var i = 0; i < d.length; i++) {
var c = d[i];
if (c[0] === 'M') out += 'M' + c[1] + ',' + c[2] + ' ';
else if (c[0] === 'L') out += 'L' + c[1] + ',' + c[2] + ' ';
else if (c[0] === 'C') out += 'C' + c[1] + ',' + c[2] + ' ' + c[3] + ',' + c[4] + ' ' + c[5] + ',' + c[6] + ' ';
else if (c[0] === 'Q') out += 'Q' + c[1] + ',' + c[2] + ' ' + c[3] + ',' + c[4] + ' ';
else if (c[0] === 'Z') out += 'Z ';
}
return out.trim();
}
function opToSvg(o) {
if (o.op === 'rect') {
return '<rect x="' + o.x + '" y="' + o.y + '" width="' + o.w + '" height="' + o.h + '"' +
(o.rx ? ' rx="' + o.rx + '"' : '') +
' fill="' + (o.fill || 'none') + '"' +
(o.stroke ? ' stroke="' + o.stroke + '" stroke-width="' + (o.lw || 1) + '"' : ' stroke="none"') +
dashAttr(o.dash) + pickAttr(o) + '/>';
}
if (o.op === 'line') {
return '<line x1="' + o.x1 + '" y1="' + o.y1 + '" x2="' + o.x2 + '" y2="' + o.y2 + '"' +
' stroke="' + (o.stroke || '#000') + '" stroke-width="' + (o.lw || 1) + '"' +
(o.cap ? ' stroke-linecap="' + o.cap + '"' : '') + dashAttr(o.dash) + '/>';
}
if (o.op === 'path') {
if (o.svgOnly) { // invisible, wide, clickable hit-area
return '<path d="' + segToD(o.d) + '" fill="none" stroke="#000" stroke-opacity="0" stroke-width="' + (o.lw || 1) + '"' +
(o.cap ? ' stroke-linecap="' + o.cap + '"' : '') + ' pointer-events="stroke"' + pickAttr(o) + '/>';
}
return '<path d="' + segToD(o.d) + '"' +
' fill="' + (o.fill || 'none') + '"' +
(o.stroke ? ' stroke="' + o.stroke + '" stroke-width="' + (o.lw || 1) + '"' : ' stroke="none"') +
(o.cap ? ' stroke-linecap="' + o.cap + '"' : '') +
(o.join ? ' stroke-linejoin="' + o.join + '"' : '') + dashAttr(o.dash) + '/>';
}
if (o.op === 'circle') {
return '<circle cx="' + o.x + '" cy="' + o.y + '" r="' + o.r + '"' +
' fill="' + (o.fill || 'none') + '"' +
(o.stroke ? ' stroke="' + o.stroke + '" stroke-width="' + (o.lw || 1) + '"' : '') + '/>';
}
if (o.op === 'text') {
var t = '<text x="' + o.x + '" y="' + o.y + '"' +
' font-family="' + fontFamily(o.font) + '"' +
' font-size="' + (o.size || 8) + '"' +
(o.weight === 'bold' ? ' font-weight="700"' : '') +
' fill="' + (o.color || '#000') + '"' +
' text-anchor="' + anchor(o.align) + '"';
if (o.angle) t += ' transform="rotate(' + o.angle + ' ' + o.x + ' ' + o.y + ')"';
t += '>' + esc(o.text) + '</text>';
return t;
}
return '';
}
// Render one page object {w,h,ops} to an <svg> string.
function renderPage(page) {
var body = '';
for (var i = 0; i < page.ops.length; i++) body += opToSvg(page.ops[i]);
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + page.w + ' ' + page.h + '" ' +
'preserveAspectRatio="xMidYMid meet" class="harness-sheet">' +
'<rect x="0" y="0" width="' + page.w + '" height="' + page.h + '" fill="#ffffff"/>' +
body + '</svg>';
}
global.RenderSVG = { renderPage: renderPage };
})(typeof window !== 'undefined' ? window : globalThis);