← All examples

Floating Chat: Bubble & Window (neodrag)

Chat bubble & floating window recreated on @neodrag/svelte v2: library drag mechanics (thresholds, handles, viewport bounds) composed with the real app's ported clamp/snap/smart-placement/persistence math via a fixed-at-0,0 coordinate bridge.

demo Not part of parity
Verdict On par. Side-by-side test passed, Aug 28.

The recreation held up under a direct Captain comparison with the hand-rolled original. The quirks that showed up — bubble size and the translucent top bar — are agent design liberties, not library constraints: the same liberties exist in both versions and neither comes from @neodrag/svelte.

What neodrag provided vs didn't

LayerOwner in this recreation
libraryDrag mechanics only: drag-time bounds clamping, declarative handle/cancel zones, the userSelect hack, drag classes, transform application.
ours~80% of floating-widget difficulty: the position store — clamp, snap-to-home, button↔window linkage, smart placement, persistence, viewport re-clamp. Identical byte-for-byte in both versions; a drag library never touches it.
workaroundsForced by the library's internals: the threshold counts pointer_delta + position (breaks any non-zero position → hence the zero-anchored position: { x: 0, y: 0 } contract), event offsets rest on an unsynced internal translate, and offsets are scale-corrected (hover-scale corrupts them) — which is why our own onpointerdown handlers survive in the library version.

Where neodrag would win

Plain "make this draggable" jobs with no persistence or button↔window linkage — a settings palette, a divider, a repositionable panel. There the library is ~5 config lines where a hand-rolled engine is ~150 pointer-event lines.

Recommendation

  • Keep the hand-rolled engine for the chat-widget family — proven, zero deps.
  • Keep neodrag in the toolbox for simple drag.
  • If adopted anywhere real: pin the version (single maintainer, stable and quiet since 2025-06) and keep a vendor fallback plan.