m can grab either end of a selected cable and drop it on a different
port / device / IO marker. Mechanics:
- Selected cable renders two .cable-handle circles at its endpoints
(handle radius 7, filled in the cable's colour with a white halo +
drop-shadow). Hidden unless the cable is selected so unrelated cables
don't litter the canvas with grab points.
- pointerdown on a handle calls startCableReplug; the module-level
cableReplug = {cableID, end, x, y} drives renderCanvas to anchor the
affected endpoint at the cursor in world coords. Pointermove keeps
the line tracking; pointerup hit-tests the cursor via
elementsFromPoint (skipping the cable-handle itself).
- Drop target:
port → PATCH {from|to: {port_id}}
device → PATCH {from|to: {device_id}}
IO → PATCH {from|to: {io_id}}
empty / same endpoint → cancel (no PATCH)
- When the cable was auto=1 and the drop commits, the PATCH also sends
promote=true so the server flips it to manual — m took control.
- preventDefault + stopPropagation on the handle pointerdown so canvas
panning / cable-line clicks don't interfere. Pointer capture survives
the drag leaving the SVG bounds.
CSS: .cable-handle gets grab cursor + drop-shadow; .replugging on the
canvas-wrap promotes to grabbing during the gesture.