5a027a0081
Splitting the god files made a pile of copy-paste helpers visible and, for the first time, fixable — sharing them previously meant importing a god file. Hashing function bodies through the TypeScript AST found twelve groups desktop-wide; production code is now at zero duplicates. Each helper went to the module that already owns its concern: firstStringField to lib/text, the two REST 404 predicates to lib/gateway-rpc beside isMissingRpcMethod, useDebounced and prefersReducedMotion to their hooks, the superseded-bootstrap guard to electron/ssh-connection, the composer keyup handler to the trigger hook that owns the rest of that state machine, and clampDataUrlReadMaxMb to apps/shared, replacing a "keep these in sync" comment between two copies. Only helpers with no existing owner got a new file: lib/mcp-servers, lib/audio-context, lib/keyed-timeouts, lib/pointer-drag, and the command palette's status row. Error-shape predicates are the worst thing to copy — when the backend changes how it reports a missing route, every copy has to be found.
27 lines
963 B
TypeScript
27 lines
963 B
TypeScript
/** Run a pointer drag until pointerup, then unbind both listeners.
|
|
*
|
|
* Capture-phase, on `window`, so the drag keeps tracking when the pointer
|
|
* leaves the element it started on. Forgetting one half of the teardown leaks
|
|
* a live pointermove handler for the rest of the session, which is why the
|
|
* unbinding lives here rather than at each call site. */
|
|
export function startPointerDrag(
|
|
onMove: (event: PointerEvent) => void,
|
|
onEnd?: (event: PointerEvent) => void
|
|
): () => void {
|
|
const move = (event: PointerEvent) => onMove(event)
|
|
|
|
const up = (event: PointerEvent) => {
|
|
window.removeEventListener('pointermove', move, true)
|
|
window.removeEventListener('pointerup', up, true)
|
|
onEnd?.(event)
|
|
}
|
|
|
|
window.addEventListener('pointermove', move, true)
|
|
window.addEventListener('pointerup', up, true)
|
|
|
|
return () => {
|
|
window.removeEventListener('pointermove', move, true)
|
|
window.removeEventListener('pointerup', up, true)
|
|
}
|
|
}
|