Files
Wiring-Designer/frontend/js/deviceTypes.js
T
Kyle f32203b630 Initial commit — wiring diagram maker with full feature set
- FastAPI + SQLite backend with routers for diagrams, devices, wires,
  bundles, connectors, export, and Octopart/Nexar search
- Konva.js canvas: ortho/direct/curved routing, wire crossings, harness
  mode, snap-to-grid, multi-select, drag, resize, undo
- Wire features: color/stripe, twisted pair helix, shielded glow,
  gauge size label, multi-core bundle grouping, length/unit tracking
- Device library: connector, terminal block, cable, splice, label, group,
  relay, fuse, switch, component with custom connector support
- Exports: BOM CSV, assembly TXT, JSON, formboard layout, PNG image
- Auto-migration for schema changes via ALTER TABLE at startup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 12:51:21 -04:00

273 lines
9.9 KiB
JavaScript
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.
const WIRE_COLORS = [
{ name: "Black", hex: "#000000" },
{ name: "Brown", hex: "#8B4513" },
{ name: "Red", hex: "#CC0000" },
{ name: "Orange", hex: "#FF8C00" },
{ name: "Yellow", hex: "#FFD700" },
{ name: "Green", hex: "#007700" },
{ name: "Blue", hex: "#0000CC" },
{ name: "Violet", hex: "#7B00CC" },
{ name: "Grey", hex: "#808080" },
{ name: "White", hex: "#DDDDDD" },
{ name: "Pink", hex: "#FF69B4" },
{ name: "Tan", hex: "#D2B48C" },
];
const SIDE = { LEFT: "left", RIGHT: "right", TOP: "top", BOTTOM: "bottom" };
function makePins(side, count, w, h, prefix = "") {
return Array.from({ length: count }, (_, i) => {
const t = (i + 1) / (count + 1);
const x = side === SIDE.LEFT ? 0 : side === SIDE.RIGHT ? w : t * w;
const y = side === SIDE.TOP ? 0 : side === SIDE.BOTTOM ? h : t * h;
return { id: `${prefix}pin_${i + 1}`, name: String(i + 1), side, x_offset: x, y_offset: y };
});
}
const DEVICE_TYPES = {
connector: {
label: "Connector",
description: "Multi-pin connector (inline/header)",
icon: "🔌",
defaultProps: { pinCount: 4, orientation: "right", partNumber: "", manufacturer: "" },
defaultSize: (p) => ({ w: 100, h: Math.max(60, (p.pinCount || 4) * 20 + 20) }),
getPins: (p, w, h) => makePins(p.orientation || SIDE.RIGHT, p.pinCount || 4, w, h),
},
terminal_block: {
label: "Terminal Block",
description: "Screw terminal block",
icon: "⬛",
defaultProps: { terminalCount: 4, partNumber: "", manufacturer: "" },
defaultSize: (p) => ({ w: Math.max(60, (p.terminalCount || 4) * 24), h: 60 }),
getPins: (p, w, h) => {
const count = p.terminalCount || 4;
const pins = [];
for (let i = 0; i < count; i++) {
const x = ((i + 0.5) / count) * w;
pins.push({ id: `t${i + 1}`, name: String(i + 1), side: SIDE.TOP, x_offset: x, y_offset: 0 });
pins.push({ id: `b${i + 1}`, name: String(i + 1), side: SIDE.BOTTOM, x_offset: x, y_offset: h });
}
return pins;
},
},
component: {
label: "Component",
description: "Generic component / IC",
icon: "⬜",
defaultProps: { leftPins: 2, rightPins: 2, partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 120, h: 80 }),
getPins: (p, w, h) => {
const left = makePins(SIDE.LEFT, p.leftPins || 2, w, h, "L");
const right = makePins(SIDE.RIGHT, p.rightPins || 2, w, h, "R");
left.forEach((pin, i) => (pin.name = `L${i + 1}`));
right.forEach((pin, i) => (pin.name = `R${i + 1}`));
return [...left, ...right];
},
},
splice: {
label: "Splice / Junction",
description: "Wire splice or junction point",
icon: "✕",
defaultProps: {},
defaultSize: () => ({ w: 32, h: 32 }),
getPins: (p, w, h) => [
{ id: "left", name: "L", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "right", name: "R", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
{ id: "top", name: "T", side: SIDE.TOP, x_offset: w / 2, y_offset: 0 },
{ id: "bottom", name: "B", side: SIDE.BOTTOM, x_offset: w / 2, y_offset: h },
],
},
label: {
label: "Note / Label",
description: "Text annotation on the diagram",
icon: "📝",
defaultProps: { text: "Note" },
defaultSize: () => ({ w: 160, h: 40 }),
getPins: () => [],
},
group: {
label: "Section Box",
description: "Labeled section rectangle for visual grouping",
icon: "⬚",
defaultProps: { fillColor: "#2828a0", fillOpacity: 0.15 },
defaultSize: () => ({ w: 320, h: 220 }),
getPins: () => [],
},
// ── Electrical components ───────────────────────────────────────────────────
fuse: {
label: "Fuse",
description: "In-line fuse or fusible link",
icon: "⚡",
defaultProps: { ampRating: "10A", fuseType: "blade", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 80, h: 44 }),
getPins: (p, w, h) => [
{ id: "A", name: "A", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "B", name: "B", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
],
},
relay: {
label: "Relay",
description: "Electromagnetic relay — SPDT 5-pin (85/86/30/87/87a)",
icon: "🔁",
defaultProps: { coilVoltage: "12V", ampRating: "30A", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 110, h: 100 }),
getPins: (p, w, h) => [
{ id: "85", name: "85", side: SIDE.LEFT, x_offset: 0, y_offset: h * 0.30 },
{ id: "86", name: "86", side: SIDE.LEFT, x_offset: 0, y_offset: h * 0.70 },
{ id: "87a", name: "87a", side: SIDE.RIGHT, x_offset: w, y_offset: h * 0.20 },
{ id: "30", name: "30", side: SIDE.RIGHT, x_offset: w, y_offset: h * 0.50 },
{ id: "87", name: "87", side: SIDE.RIGHT, x_offset: w, y_offset: h * 0.80 },
],
},
switch: {
label: "Switch",
description: "SPDT switch — common, normally-open, normally-closed",
icon: "⎘",
defaultProps: { switchType: "SPDT", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 90, h: 70 }),
getPins: (p, w, h) => [
{ id: "C", name: "C", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "NO", name: "NO", side: SIDE.RIGHT, x_offset: w, y_offset: h * 0.30 },
{ id: "NC", name: "NC", side: SIDE.RIGHT, x_offset: w, y_offset: h * 0.70 },
],
},
bulb: {
label: "Bulb / Light",
description: "Indicator lamp or lighting load",
icon: "💡",
defaultProps: { voltage: "12V", wattage: "", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 80, h: 50 }),
getPins: (p, w, h) => [
{ id: "+", name: "+", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "-", name: "", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
],
},
motor: {
label: "Motor",
description: "DC motor or actuator",
icon: "⚙",
defaultProps: { voltage: "12V", ampRating: "", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 90, h: 60 }),
getPins: (p, w, h) => [
{ id: "+", name: "+", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "-", name: "", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
],
},
diode: {
label: "Diode",
description: "Diode, Schottky, or TVS — anode / cathode",
icon: "⊳",
defaultProps: { diodeType: "rectifier", voltage: "", current: "", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 80, h: 44 }),
getPins: (p, w, h) => [
{ id: "A", name: "A", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "K", name: "K", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
],
},
resistor: {
label: "Resistor",
description: "Fixed resistor or load",
icon: "Ω",
defaultProps: { resistance: "", wattage: "", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 80, h: 44 }),
getPins: (p, w, h) => [
{ id: "1", name: "1", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "2", name: "2", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
],
},
capacitor: {
label: "Capacitor",
description: "Electrolytic or film capacitor",
icon: "⊏",
defaultProps: { capacitance: "", voltage: "", partNumber: "", manufacturer: "" },
defaultSize: () => ({ w: 80, h: 44 }),
getPins: (p, w, h) => [
{ id: "+", name: "+", side: SIDE.LEFT, x_offset: 0, y_offset: h / 2 },
{ id: "-", name: "", side: SIDE.RIGHT, x_offset: w, y_offset: h / 2 },
],
},
ground: {
label: "Ground",
description: "Chassis or signal ground reference",
icon: "⏚",
defaultProps: { groundType: "chassis", reference: "" },
defaultSize: () => ({ w: 60, h: 50 }),
getPins: (p, w, h) => [
{ id: "GND", name: "GND", side: SIDE.TOP, x_offset: w / 2, y_offset: 0 },
],
},
power: {
label: "Power Rail",
description: "Battery positive, ignition, or supply rail",
icon: "⊕",
defaultProps: { voltage: "12V", sourceType: "battery", reference: "" },
defaultSize: () => ({ w: 70, h: 50 }),
getPins: (p, w, h) => [
{ id: "PWR", name: "+", side: SIDE.BOTTOM, x_offset: w / 2, y_offset: h },
],
},
};
const CABLE_DEFAULT_COLORS = [
"#dddddd", "#cc0000", "#111111", "#007700",
"#0000cc", "#ff8c00", "#8b4513", "#7b00cc",
];
const DEVICE_TYPES_EXTRA = {
cable: {
label: "Cable",
description: "Multi-conductor cable under one jacket",
icon: "🔗",
defaultProps: {
conductorCount: 4,
partNumber: "",
manufacturer: "",
jacketColor: "#2a2a2a",
sleeveLength: 60,
conductors: [
{ name: "1", color: "#dddddd" },
{ name: "2", color: "#cc0000" },
{ name: "3", color: "#111111" },
{ name: "4", color: "#007700" },
],
},
defaultSize: (p) => {
const n = p.conductorCount || p.conductors?.length || 4;
return { w: 200, h: n * 24 + 40 };
},
getPins: (p, w, h) => {
const conductors = p.conductors || Array.from({ length: p.conductorCount || 4 }, (_, i) => ({ name: String(i + 1) }));
return conductors.flatMap((c, i) => {
const yOff = 26 + i * 24 + 12;
return [
{ id: `L${i + 1}`, name: c.name || String(i + 1), side: "left", x_offset: 0, y_offset: yOff },
{ id: `R${i + 1}`, name: c.name || String(i + 1), side: "right", x_offset: w, y_offset: yOff },
];
});
},
},
};
Object.assign(DEVICE_TYPES, DEVICE_TYPES_EXTRA);
function buildDefaultDevice(typeKey, diagramId) {
const type = DEVICE_TYPES[typeKey];
if (!type) return null;
const props = { ...type.defaultProps };
const { w, h } = type.defaultSize(props);
const pins = type.getPins(props, w, h);
return {
diagram_id: diagramId,
device_type: typeKey,
label: type.label,
reference: "",
x: 200,
y: 200,
width: w,
height: h,
properties: props,
pins,
};
}