KQ-1747 · SYNC/PAIRS

Sync Pairs

A two-way field mapping between two apps whose fields face each other across a gutter of soft cables. Dragging from a field draws a live cable 1:1 under the pointer; plugged into a compatible field its sag drops in slack and swings taut on springs.recoil, two visible bounces of a rope catching. Pressing a cable's knot cycles its direction — right, left, both — and the arrowhead turns on springs.flick and slides along the curve to its new end on springs.glide, a second chevron sliding out for both ways; lossy flows draw the cable in warn and name what is lost, and types that do not convert refuse to pair. Each side is a listbox: Enter opens a partner list, D turns a pair, Delete unpairs, and narrow containers turn the cables into rows with direction chips.

KQ-1747 · SYNC/PAIRS

Field sync

Coldbrook CRM ⇄ Fernworks Desk

Up and Down move through a side, Left and Right cross to the other side. Enter opens the list of partners; D changes a pair's direction; Delete unpairs.

  • Full name

    text

    Requester name

    text

  • Email

    email

    Contact email

    email

  • Phone

    phone

    Phone number

    number

    spaces and + signs are dropped

  • Company

    text

    Organisation

    text

  • Lifetime value

    money

    Account value

    number

Add a pair

Not synced: Customer since, Plan, Opted in · First seen, Tier, Marketing consent

5 pairs · 2 both ways · 1 lossy

Phone → Phone number: spaces and + signs are dropped

5 pairs

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/sync-pairs

Props

cableStyle
"soft" | "taut" | "dashed"
default "soft"
How a cable is drawn: a sagging cable with a soft under-stroke that swings on recoil, a nearly straight one that settles on snap, or a dotted lead.
density
"compact" | "regular" | "roomy"
default "regular"
Row height (36, 44 or 52px) and type; regular and roomy rows also name the partner.
accent
"cobalt" | "moss" | "plum" | "ember"
default "cobalt"
The cables' and ports' pigment at a fixed lightness; lossy cables are always warn.
leftApp
SyncApp
default defaultSyncLeftApp
The app on the left: { name, initials }.
rightApp
SyncApp
default defaultSyncRightApp
The app on the right: { name, initials }.
leftFields
SyncField[]
default defaultSyncLeftFields
The left app's fields: { id, name, type } with type text, email, phone, number, money, date, datetime, picklist or boolean.
rightFields
SyncField[]
default defaultSyncRightFields
The right app's fields.
pairs / defaultPairs
SyncPair[]
default — / defaultSyncPairs
Each { left, right, direction: "right" | "left" | "both" }; one partner per field. Controlled or initial.
onPairsChange
(pairs) => void
default —
Fires with every pair after a pairing, an unpairing or a direction change.
onPair
(pair) => void
default —
Two fields were paired; any pair it replaced is reported through onUnpair first.
onUnpair
(pair) => void
default —
A pair was taken apart.
onDirectionChange
(pair, direction) => void
default —
A pair's direction was cycled; directions a type cannot flow in are skipped.
status
"ready" | "loading" | "error"
default "ready"
Loading shows placeholder rows; error explains and offers Try again.
onRetry
() => void
default —
Try again was pressed after the fields failed to load.
title
string
default "Field sync"
The heading.
height
number
default 540
Height of the surface in px; the board scrolls inside it.
disabled
boolean
default false
Read only: nothing can be paired, unpaired or turned.
className
string
default —
Classes for the surface.