KQ-2103 · TILING/DESK
Tiling Desk
An app frame where every opened record becomes a window that tiles the desk from a binary tree of splits. Opening a record splits the focused tile — it glides into its half on springs.glide while the new window wipes in from the split line — closing one lets its neighbours glide out to fill, and dragging a window by its title bar previews the half, swap or desk-edge strip it would take on springs.snap before it lands with its release velocity. Cmd or Ctrl with an arrow moves focus between tiles, with Shift swaps them and with Alt docks the window along that edge.
Desk
BasinworksCommand or Control with an arrow moves between windows; add Shift to swap, or Alt to dock along that edge. Delete closes.
3 windows · focus order 3195
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/tiling-desk
Props
- records
- TilingRecord[]
- default defaultTilingRecords
- Everything that can be opened: orders and customers with fields, lines, totals and related records.
- layout / defaultLayout
- TilingNode | null
- default — / defaultTilingLayout
- The layout tree; its leaves are the open record ids, null is an empty desk. Controlled or uncontrolled.
- onLayoutChange
- (layout: TilingNode | null) => void
- default —
- Fires with the whole tree after an open, a close, a drop, a swap or a dock.
- focused / defaultFocused
- string | null
- default — / the first window
- The focused window id, controlled or uncontrolled.
- onFocusChange
- (id: string | null) => void
- default —
- Fires when another window becomes the focused one.
- onOpenRecord
- (record: TilingRecord) => void
- default —
- A record was opened as a new window (not when an open one is brought forward).
- onCloseWindow
- (id: string) => void
- default —
- A window was closed.
- suggestions
- string[]
- default three record ids
- Records offered on an empty desk.
- gap
- number
- default 8
- Space between tiles and around the desk, px.
- splitRule
- "longest" | "alternate" | "spiral"
- default "longest"
- Which side of the focused tile a new window takes: along its longer side, alternating right and below with each window, or spiralling a quarter turn with each.
- accent
- string
- default "var(--accent-bright)"
- Focus, drop zones, the split rule and open markers — any CSS colour.
- maxWindows
- number
- default 6
- The most windows the desk holds.
- renderRecord
- (record, size: { width, height, compact }) => ReactNode
- default —
- Draws a window's body in place of the built-in record card.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the records have arrived.
- onRetry
- () => void
- default —
- Try again was pressed after the records failed to load.
- title
- string
- default "Desk"
- The frame's title.
- workspace
- string
- default "Basinworks"
- The workspace shown beside the title.
- height
- number
- default 520
- The frame's height, px.
- className
- string
- default —
- Classes for the outer frame.
| Prop | Type | Default | Description |
|---|---|---|---|
| records | TilingRecord[] | defaultTilingRecords | Everything that can be opened: orders and customers with fields, lines, totals and related records. |
| layout / defaultLayout | TilingNode | null | — / defaultTilingLayout | The layout tree; its leaves are the open record ids, null is an empty desk. Controlled or uncontrolled. |
| onLayoutChange | (layout: TilingNode | null) => void | — | Fires with the whole tree after an open, a close, a drop, a swap or a dock. |
| focused / defaultFocused | string | null | — / the first window | The focused window id, controlled or uncontrolled. |
| onFocusChange | (id: string | null) => void | — | Fires when another window becomes the focused one. |
| onOpenRecord | (record: TilingRecord) => void | — | A record was opened as a new window (not when an open one is brought forward). |
| onCloseWindow | (id: string) => void | — | A window was closed. |
| suggestions | string[] | three record ids | Records offered on an empty desk. |
| gap | number | 8 | Space between tiles and around the desk, px. |
| splitRule | "longest" | "alternate" | "spiral" | "longest" | Which side of the focused tile a new window takes: along its longer side, alternating right and below with each window, or spiralling a quarter turn with each. |
| accent | string | "var(--accent-bright)" | Focus, drop zones, the split rule and open markers — any CSS colour. |
| maxWindows | number | 6 | The most windows the desk holds. |
| renderRecord | (record, size: { width, height, compact }) => ReactNode | — | Draws a window's body in place of the built-in record card. |
| status | "ready" | "loading" | "error" | "ready" | Whether the records have arrived. |
| onRetry | () => void | — | Try again was pressed after the records failed to load. |
| title | string | "Desk" | The frame's title. |
| workspace | string | "Basinworks" | The workspace shown beside the title. |
| height | number | 520 | The frame's height, px. |
| className | string | — | Classes for the outer frame. |