KQ-2126 · ZOOM/READOUT
Zoom Readout
The top strip of a whiteboard over the board it drives: the active tool sits on a marker that slides between tools on springs.snap, and the zoom readout is an odometer whose digits are separate wheels — a step turns only the wheels whose digit changed, the short way in the direction the zoom moved, with snap's one crisp overshoot, while fit to selection turns them on springs.glide in the same frame as the fit icon's brackets close on its centre and four brackets on the board converge onto the rectangle the selection glides into. A trackpad pinch follows the fingers 1:1 about the pointer and the hand tool drags the board 1:1 and coasts to its projected rest on glide; V, H and 1–8 pick tools, Cmd or Ctrl with = and − step the zoom, Shift+1 fits, and below 36rem the tools fold into one menu.
Fernworks / Q4 launch board
Select · 50% · 1 selected · fit ready
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/zoom-readout
Props
- ref
- React.Ref<ZoomReadoutHandle>
- default —
- Imperative handle with fit(target?) and zoomTo(zoom), for hosts that drive the board from elsewhere.
- tools
- ZoomReadoutTool[]
- default defaultZoomReadoutTools
- The tools in the strip, in order: { id, label, icon, key? }. The key is a single-letter shortcut; 1–9 always pick by position.
- tool / defaultTool
- string
- default "select"
- The active tool id, controlled or initial. The hand tool makes the whole board a pan surface.
- onToolChange
- (tool: string) => void
- default —
- Fires from the press, menu choice or key that picked a tool.
- zoom / defaultZoom
- number
- default 50
- Zoom in whole percent, controlled or initial. A controlled change the host refuses never moves the board.
- onZoomChange
- (zoom: number) => void
- default —
- Fires with the new whole-percent zoom from the step, preset, pinch or fit that changed it.
- minZoom
- number
- default 10
- Lowest zoom, percent.
- maxZoom
- number
- default 800
- Highest zoom, percent. Its digit count sets how many wheels the readout has.
- presets
- number[]
- default [10, 25, 50, 75, 100, 150, 200, 400, 800]
- The stops Zoom in and Zoom out step through, also listed in the readout's menu.
- items
- ZoomReadoutItem[]
- default defaultZoomReadoutItems
- What is on the board, in board pixels: { id, kind: "frame" | "sticky" | "shape" | "ellipse" | "text", x, y, w, h, label, note?, tone? }.
- selection / defaultSelection
- string[]
- default ["pricing"]
- Selected item ids, controlled or initial. Fit selection is aria-disabled, with the reason in its name, while this is empty.
- onSelectionChange
- (ids: string[]) => void
- default —
- Fires from the click, key or Escape that changed the selection.
- onFit
- (fit: { target: "selection" | "all"; zoom: number; ids: string[] }) => void
- default —
- Fires when a fit runs, with what was fitted and the zoom it landed on.
- fitPadding
- number
- default 20
- Screen pixels left around a fitted selection.
- toolSize
- number
- default 32
- Tool and zoom button size in px. The strip is 12 px taller and the readout's type scales with it.
- readoutStyle
- "roll" | "tiles" | "meter"
- default "roll"
- How the zoom reads: bare rolling digits, each digit on a split tile like a counter, or digits over a level meter that fills on a log scale between minZoom and maxZoom.
- accent
- "cobalt" | "plum" | "mint" | "ink" | string
- default "cobalt"
- Tool marker, selection outline, fit brackets and meter: a named accent or any CSS colour.
- title
- string
- default "Q4 launch board"
- The board's name, shown from 40rem; with the workspace it is the piece's accessible name.
- workspace
- string
- default "Fernworks"
- The workspace the board belongs to; its initial is the monogram.
- viewportHeight
- number
- default 164
- Height of the board viewport under the strip, px. Menus open inside it.
- shortcuts
- boolean
- default true
- Listen for tool letters, 1–9, Cmd or Ctrl with = − 0, Shift+1, Shift+0 and Escape while focus is inside the piece.
- status
- "ready" | "loading" | "error"
- default "ready"
- Board state: loading draws a still skeleton (it pulses only when motion is allowed), error offers Retry. Fit is off in both.
- onRetry
- () => void
- default —
- Called by the error state's Retry button.
- disabled
- boolean
- default false
- Turns every control and the board off.
- className
- string
- default —
- Classes for the outer section.
| Prop | Type | Default | Description |
|---|---|---|---|
| ref | React.Ref<ZoomReadoutHandle> | — | Imperative handle with fit(target?) and zoomTo(zoom), for hosts that drive the board from elsewhere. |
| tools | ZoomReadoutTool[] | defaultZoomReadoutTools | The tools in the strip, in order: { id, label, icon, key? }. The key is a single-letter shortcut; 1–9 always pick by position. |
| tool / defaultTool | string | "select" | The active tool id, controlled or initial. The hand tool makes the whole board a pan surface. |
| onToolChange | (tool: string) => void | — | Fires from the press, menu choice or key that picked a tool. |
| zoom / defaultZoom | number | 50 | Zoom in whole percent, controlled or initial. A controlled change the host refuses never moves the board. |
| onZoomChange | (zoom: number) => void | — | Fires with the new whole-percent zoom from the step, preset, pinch or fit that changed it. |
| minZoom | number | 10 | Lowest zoom, percent. |
| maxZoom | number | 800 | Highest zoom, percent. Its digit count sets how many wheels the readout has. |
| presets | number[] | [10, 25, 50, 75, 100, 150, 200, 400, 800] | The stops Zoom in and Zoom out step through, also listed in the readout's menu. |
| items | ZoomReadoutItem[] | defaultZoomReadoutItems | What is on the board, in board pixels: { id, kind: "frame" | "sticky" | "shape" | "ellipse" | "text", x, y, w, h, label, note?, tone? }. |
| selection / defaultSelection | string[] | ["pricing"] | Selected item ids, controlled or initial. Fit selection is aria-disabled, with the reason in its name, while this is empty. |
| onSelectionChange | (ids: string[]) => void | — | Fires from the click, key or Escape that changed the selection. |
| onFit | (fit: { target: "selection" | "all"; zoom: number; ids: string[] }) => void | — | Fires when a fit runs, with what was fitted and the zoom it landed on. |
| fitPadding | number | 20 | Screen pixels left around a fitted selection. |
| toolSize | number | 32 | Tool and zoom button size in px. The strip is 12 px taller and the readout's type scales with it. |
| readoutStyle | "roll" | "tiles" | "meter" | "roll" | How the zoom reads: bare rolling digits, each digit on a split tile like a counter, or digits over a level meter that fills on a log scale between minZoom and maxZoom. |
| accent | "cobalt" | "plum" | "mint" | "ink" | string | "cobalt" | Tool marker, selection outline, fit brackets and meter: a named accent or any CSS colour. |
| title | string | "Q4 launch board" | The board's name, shown from 40rem; with the workspace it is the piece's accessible name. |
| workspace | string | "Fernworks" | The workspace the board belongs to; its initial is the monogram. |
| viewportHeight | number | 164 | Height of the board viewport under the strip, px. Menus open inside it. |
| shortcuts | boolean | true | Listen for tool letters, 1–9, Cmd or Ctrl with = − 0, Shift+1, Shift+0 and Escape while focus is inside the piece. |
| status | "ready" | "loading" | "error" | "ready" | Board state: loading draws a still skeleton (it pulses only when motion is allowed), error offers Retry. Fit is off in both. |
| onRetry | () => void | — | Called by the error state's Retry button. |
| disabled | boolean | false | Turns every control and the board off. |
| className | string | — | Classes for the outer section. |