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.
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
| Layer | Owner in this recreation |
|---|---|
| library | Drag 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. |
| workarounds | Forced 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.