+Port (device inspector):
- New button on the device inspector arms a port-placement tool with
the device + currently-active cable type pre-selected.
- Click anywhere on the canvas: snapToDeviceEdge() finds the closest
edge of the selected device, clamps the perpendicular coord, POSTs a
new port. The new port renders immediately (state.ports.push +
render()).
- Per-port × delete button in the inspector ports grid.
Manual cable draw:
- Port circles are now clickable (slice 4 had pointer-events:none).
- Click a port → starts a cable draw with that port as the source
(state.cableDrawFromPortID, port highlighted via .cable-from class).
- Click another port → POSTs a cable with from_port_id + to_port_id,
type derived from source port, auto=false. If the target port's type
differs, confirm-prompt warns m before committing.
- Shift+click target port → binds to the target's parent device
(to_device_id) instead of the port.
- Click an IO marker mid-draw → terminates the cable with to_io_id.
- Esc cancels the draw + clears state.cableDrawFromPortID.
- "Draw cable" toolbar button is now enabled (data-tool=cable, keyboard
is implicit via port-click). armTool() teardown clears the source-port
state.
Cable inspector tweak (slice 6 callback):
- "driver" row now renders as a clickable button showing the
requirement's "FromName ↔ ToName" instead of the raw id; click jumps
the inspector to that requirement.
CSS:
- tool-port + tool-cable add the same crosshair cursor as the other
tools (descendant-targeted with !important to beat svg-draggable's
grab cursor — same fix-pattern as slice 3's cursor-cache pass).
- .port-circle.cable-from gives the source port a glow.
- .btn-link styles for inspector inline buttons.