KQ-1658 · EMPTY/ROOM

Empty Room

The empty state of a space nobody has joined: a table seen from above with a dashed ghost seat for each person the directory suggests. Inviting someone turns their seat solid with a small hop — up on an enter tween, down on springs.recoil, the landing spring — while the disc grows in on springs.snap, one ring leaves the seat, the table warms toward the accent and the copy moves from waiting to welcoming; Invite all runs the hops round the table on cascade(). Seats are buttons in clockwise order, so Tab (or the arrows, Home and End) walks round the table and Enter invites; a refused invite marks its seat for a retry.

KQ-1658 · EMPTY/ROOM

Harbour Ops

New space · created 4 min ago

basinworks/join/harbour-ops-7qf2

Nobody's here yet.

Harbour Ops is ready for the people who belong in it, suggested from the Basinworks directory.

Tab or the arrow keys move round the table. Enter invites the person in that seat.

0 of 8 seats · link not copied

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/empty-room

Props

suggestions
EmptyRoomPerson[]
default defaultEmptyRoomSuggestions
People from the directory, { id, name, role?, reason? }, one per seat in clockwise order from the top left.
invited / defaultInvited
string[]
default — / []
Ids of the people already invited, controlled or initial.
onInvitedChange
(ids: string[]) => void
default —
Fires with every invited id once an invite has gone through.
onInvite
(person: EmptyRoomPerson) => void | Promise<unknown>
default —
A seat was pressed. Return a promise to show the invite in flight; a rejection marks the seat for a retry.
onCopyLink
(link: string) => void
default —
The share link was copied (it is also written to the clipboard when one is available).
inviteLink
string
default "basinworks/join/harbour-ops-7qf2"
The space's share link, shown in the header and copied by Copy.
spaceName
string
default "Harbour Ops"
The new space's name; also the surface's accessible name.
directory
string
default "Basinworks directory"
Where the suggestions come from, used in the copy.
createdAt
number
default four minutes before now
When the space was created, in ms.
now
number
default defaultEmptyRoomNow
The moment ages count from, in ms; never read from the clock during render.
status
"ready" | "loading"
default "ready"
Loading draws blank breathing seats with skeleton names, and nothing can be invited.
seatCount
number
default 8
How many chairs the table seats, 4 to 10; the ends take a seat each from six.
seatShape
"round" | "square" | "chair"
default "round"
Seats as circles, rounded squares, or chairs whose backs face away from the table.
accent
string
default "var(--accent-bright)"
The colour of a taken seat, its welcome ring and the table's warmth. Any CSS colour; seats take its hue at a fixed lightness.
inviteAll
boolean
default true
Offer Invite all, which seats everyone left in one run round the table.
disabled
boolean
default false
Nothing can be invited or copied; the surface dims.
className
string
default —
Classes for the outer surface.