From aed1796a8fae2d2a8df8c299a587be25803df31f Mon Sep 17 00:00:00 2001 From: David Montero Date: Sat, 15 Aug 2026 20:08:40 +0200 Subject: [PATCH] =?UTF-8?q?fix(wires):=20stop=20board-corner=20wire=20anch?= =?UTF-8?q?ors=20=E2=80=94=20alias=20fallbacks=20for=20pin-name=20spelling?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A wire endpoint naming a pin the board element does not expose silently resolved to nothing and rendered anchored at the board corner. A full audit (all gallery examples + every saved project on velxio.dev) found 15 examples and 28 user projects doing it — the same electrical pin spelled differently per surface: GND.1 on the classic ESP32 (element exposes GND/GND2), 3.3V vs 3V3, GND2 vs GND.2, D13 vs 13 vs GP13, A0-A2 vs GP26-28 on the Pico, 36/39 vs VP/VN, bare 5V vs VBUS/VIN. calculatePinPosition now tries those equivalences AFTER the exact match and the two existing fallbacks miss (so no current resolution changes), healing old saves, .vlx imports and agent-authored wires without a data migration. The Pico element gains A0/A1/A2 alias pads on the ADC pins (RX2/16 dual-entry precedent) and a GP25 pad at the onboard LED silk so pico-blink-style wires have an anchor. The two examples that were outright wrong (microsd-card-esp32 GND.1, pico-serial-echo TX) now use the element's real pin names. Netlist snapshots (166) untouched; verifier + builder suites green. --- .../velxio-components/PiPicoWElement.ts | 11 ++++++++ frontend/src/data/examples-storage-microsd.ts | 2 +- frontend/src/data/examples.ts | 2 +- frontend/src/utils/pinPositionCalculator.ts | 25 +++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/velxio-components/PiPicoWElement.ts b/frontend/src/components/velxio-components/PiPicoWElement.ts index cd9efb5a..0c7c6f73 100644 --- a/frontend/src/components/velxio-components/PiPicoWElement.ts +++ b/frontend/src/components/velxio-components/PiPicoWElement.ts @@ -39,15 +39,26 @@ const PINS_PI_PICO_W = [ { name: 'GP22', x: 97, y: 157 }, { name: 'RUN', x: 97, y: 144 }, { name: 'GP26', x: 97, y: 131 }, + // Alias entries: examples and saved projects also reference the ADC pins + // as A0/A1/A2 (Arduino-style) — same physical pins, same coordinates. + // Without these the wire endpoint anchored at the board corner. Mirrors + // the RX2/16 dual-entry precedent in Esp32Element. + { name: 'A0', x: 97, y: 131 }, { name: 'GP27', x: 97, y: 119 }, + { name: 'A1', x: 97, y: 119 }, { name: 'GND.7', x: 97, y: 106 }, { name: 'GP28', x: 97, y: 93 }, + { name: 'A2', x: 97, y: 93 }, { name: 'ADC_VREF', x: 97, y: 81 }, { name: '3V3', x: 97, y: 68 }, { name: '3V3_EN', x: 97, y: 55 }, { name: 'GND.8', x: 97, y: 42 }, { name: 'VSYS', x: 97, y: 30 }, { name: 'VBUS', x: 97, y: 17 }, + // GP25 is the onboard LED, not a header pin — but pico-blink-style + // examples wire an external LED to it for visibility. Anchor at the LED + // silk so those wires stop falling back to the board corner. + { name: 'GP25', x: 40, y: 14 }, ]; class PiPicoWElement extends HTMLElement { diff --git a/frontend/src/data/examples-storage-microsd.ts b/frontend/src/data/examples-storage-microsd.ts index 0a43fed2..66d3d91f 100644 --- a/frontend/src/data/examples-storage-microsd.ts +++ b/frontend/src/data/examples-storage-microsd.ts @@ -194,7 +194,7 @@ export const microsdExamples: ExampleProject[] = [ { id: 'w-miso', start: { componentId: 'esp32', pinName: '19' }, end: { componentId: 'sd1', pinName: 'MISO' }, color: '#22cc22' }, { id: 'w-sck', start: { componentId: 'esp32', pinName: '18' }, end: { componentId: 'sd1', pinName: 'SCK' }, color: '#ffdd33' }, { id: 'w-vcc', start: { componentId: 'esp32', pinName: '3V3' }, end: { componentId: 'sd1', pinName: 'VCC' }, color: '#ff4444' }, - { id: 'w-gnd', start: { componentId: 'esp32', pinName: 'GND.1' }, end: { componentId: 'sd1', pinName: 'GND' }, color: '#000000' }, + { id: 'w-gnd', start: { componentId: 'esp32', pinName: 'GND' }, end: { componentId: 'sd1', pinName: 'GND' }, color: '#000000' }, ], }, ]; diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index f30c4960..03134251 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -4484,7 +4484,7 @@ void loop() { wires: [ { id: 'w1', - start: { componentId: 'arduino-uno', pinName: 'TX' }, + start: { componentId: 'arduino-uno', pinName: 'GP0' }, end: { componentId: 'r-led-rx-a', pinName: '1' }, color: '#ff8800', }, diff --git a/frontend/src/utils/pinPositionCalculator.ts b/frontend/src/utils/pinPositionCalculator.ts index 10b5b501..74437123 100644 --- a/frontend/src/utils/pinPositionCalculator.ts +++ b/frontend/src/utils/pinPositionCalculator.ts @@ -71,6 +71,31 @@ export function calculatePinPosition( pin = pinInfo.find((p: any) => p.description === `GPIO${gpioNum}`); } } + // Generic alias fallbacks — every unresolved endpoint used to silently + // anchor at the board CORNER (0,0 of the element), which users read as a + // broken circuit. The names below are electrically identical spellings + // that boards/examples/saved projects disagree on (a 2026-08 audit found + // 15 gallery examples and 28 saved projects wired to a spelling their + // board element doesn't expose). Only consulted after exact + the two + // legacy fallbacks above miss, so no existing resolution changes. + if (!pin) { + const candidates: string[] = []; + const dot = pinName.match(/^(.+)\.\d+$/); + if (dot) candidates.push(dot[1]); // GND.1 -> GND (esp32 classic) + if (pinName === '3.3V') candidates.push('3V3', '3V3.1', '3V'); + if (pinName === '3V3') candidates.push('3V3.1', '3.3V', '3V'); + if (pinName === '5V') candidates.push('5V.1', 'VBUS', 'VIN'); + if (pinName === 'GND2') candidates.push('GND.2'); + const dNum = pinName.match(/^D(\d+)$/); + if (dNum) candidates.push(dNum[1], `GP${dNum[1]}`); // D13 -> 13 (esp32) / GP13 (pico) + if (/^\d+$/.test(pinName)) candidates.push(`D${pinName}`, `GP${pinName}`); + if (pinName === '36') candidates.push('VP'); // ESP32 devkit silk for GPIO36 + if (pinName === '39') candidates.push('VN'); // ESP32 devkit silk for GPIO39 + for (const c of candidates) { + pin = pinInfo.find((p: any) => p.name === c); + if (pin) break; + } + } if (!pin) { console.warn(`[pinPositionCalculator] Pin ${pinName} not found on component ${componentId}`); console.warn(