Limitations and traps
Limitations
Section titled “Limitations”- It’s a CSS transform. Zooming scales your DOM, so text is re-rasterized (crisp in modern browsers) and very large
worlds at very small zoom still lay out every node; cull off-screen nodes with
visibleRect()if you have thousands. - No node model. Positions, selection, snapping and undo are yours.
coordinatesplugs into a window manager that has them: window-algebra’s renderer and input adapter accept a coordinates hook from 0.1.1 (window-algebra#9). - Wires don’t route around things. They’re single cubics; with many crossing wires, you get spaghetti, as on a real patch bay.
- Multi-touch pinch relies on the browser’s Ctrl-wheel translation of trackpad pinches; two-finger touch-screen pinch is not implemented yet.
The gesture traps (the stage consumes the wheel, wheel: "zoom" makes pinch pan, keyboard needs the stage itself
focused, panOnDrag replaces every default) are on the Viewport page. The
rest:
data-patchbay-ignoreis checked withclosest()from the event target, and only counts inside the stage. Put it on the scrollable element or an ancestor of it within the stage; an ancestor outside the stage has no effect.- Every conversion goes through the container’s client rect. Code that subtracts
getBoundingClientRect()of anything else breaks when the stage isn’t at the page origin. Usecoordinates. - Wires don’t redraw themselves. Call
wires.schedule()after anything moves, including after the nodes’ own layout changes size (a card that grew moves its ports). - One
attachViewport()per stage. Each call adds its own listeners; calldetach()before attaching again (for example on hot reload), or wheel events get handled twice.
Testing without a browser
Section titled “Testing without a browser”The viewport math and wirePath() need no DOM and test under plain Node. The gesture code does, and
happy-dom differs from real browsers in two ways that matter here:
-
Its
WheelEventdropsctrlKey,metaKey,clientXandclientYfrom the init dictionary (they read asundefined), whiledeltaX/deltaYsurvive. A test that dispatchesnew WheelEvent("wheel", { ctrlKey: true, ... })is not testing a Ctrl wheel. Copy the lost fields back onto the event before dispatching it:const wheel = (target, init) => {const event = new window.WheelEvent("wheel", { bubbles: true, cancelable: true, ...init });const full = { ctrlKey: false, metaKey: false, clientX: 0, clientY: 0, ...init };for (const [key, value] of Object.entries(full)) {if (event[key] !== value) Object.defineProperty(event, key, { value });}return target.dispatchEvent(event);};Before release, the same gap made a missing
ctrlKeyread as a zoom gesture; the handler now coerces modifiers withBoolean(), so anundefinedmodifier means “not held”. -
It has no layout. Every client rect is empty, so give the container a known rect in the test (
container.getBoundingClientRect = () => ({ left: 100, top: 50, width: 800, height: 600, ... })) and don’t rely onanchorOf()orelementFromPoint.
A headless test proves the logic, not the feel: mouse-wheel and trackpad deltas differ by about 30×. After changing
gesture code or wheelZoomSpeed, try a real mouse notch, a real pinch, and a scrollable data-patchbay-ignore panel in
the repository’s browser example (examples/04-cards-and-wires-in-a-browser/).