KQ-1755 · SCREENSHOT/MARKUP

Screenshot Markup

An image annotation editor: arrows, boxes, labels and redaction over a screenshot, every mark kept in the image's own pixels. Drawing an arrow, the shaft bows behind the hand — a curve whose control point trails the pointer on springs.glide — and on release it pulls taut while the head springs into place on springs.recoil with two visible bounces. The redact brush pixelates whatever passes under it: cells of a grid fill with the real mosaic of the image beneath, popping in as separate tiles on springs.flick, and when the stroke ends they settle — gaps closing and corners squaring on springs.snap in the order they were laid — into one solid mosaic. Tools have single keys, the canvas has a keyboard cursor so Enter, arrows and Enter draw any mark, [ and ] select marks, arrows nudge them and Delete removes them, and undo and redo walk a history.

KQ-1755 · SCREENSHOT/MARKUP

Basinworks · levels, 14 Oct

Tools: V select, A arrow, B box, T text, R redact. Arrow keys move the cursor; Enter starts a mark there, arrows extend it and Enter ends it. Left and right brackets select marks; arrows nudge the selected mark, Shift for ten pixels; Delete removes it; Escape cancels.

BasinworksOverviewReservoirsAlertsContactsReportsReservoir levelsTuesday 14 October · updated 09:40IDNorth basin82%Weir pool64%Ridge tank41%Inflow, last 7 daysmegalitresWedThuFriSatSunMonTueOn call this weekNameEmailPhoneAccountInes Duarteines.duarte@basinworks.example0417 552 091ACC-4417-0092Tomas Reyest.reyes@basinworks.example0428 190 334ACC-4417-0131Mara Okoromara.okoro@basinworks.example0433 806 217ACC-4417-0207Storm inflow, Sunday

arrow · 4 marks · 1 redaction

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/screenshot-markup

Props

strokeWidth
number
default 3
Stroke of arrows and boxes in image px; it also sizes arrowheads, labels and the redact brush (six times the stroke).
toolPalette
"rail" | "float" | "strip"
default "rail"
Where the tools live: a rail down the left, a pill floating over the image, or a strip under the header. Below a 36rem container they dock at the bottom whatever this is.
accent
string
default "var(--danger)"
The markup colour, taken at a fixed lightness so marks read the same over either theme.
image
MarkupImage
default defaultMarkupImage
The screenshot as { width, height, alt, src?, content? }: a raster src, or SVG content drawn in the image's coordinates (the default Basinworks dashboard).
annotations / defaultAnnotations
MarkupAnnotation[]
default — / defaultMarkupAnnotations
Marks in image pixels: arrow { x1, y1, x2, y2 }, box { x, y, w, h }, text { x, y, text } and redact { size, cells: [column, row][] }.
onChange
(annotations) => void
default —
Fires with every mark after one is drawn, moved, nudged, edited, removed, undone or redone.
tool / defaultTool
"select" | "arrow" | "box" | "text" | "redact"
default — / "arrow"
The active tool.
onToolChange
(tool) => void
default —
Fires from the button or single key (V, A, B, T, R) that picked a tool.
selected / defaultSelected
string | null
default — / null
The selected mark's id.
onSelectedChange
(id) => void
default —
Fires when a click, a bracket key or a new mark changes the selection.
onUndo
(annotations) => void
default —
Undo was pressed or Command/Control+Z typed, with the marks it went back to.
onRedo
(annotations) => void
default —
Redo was pressed or Shift+Command/Control+Z typed, with the marks it restored.
onExport
(annotations, image) => void | Promise
default —
Export was pressed. Return a promise to show it exporting; a rejection offers Retry.
status
"ready" | "loading" | "error"
default "ready"
Whether the screenshot has arrived: loading shows a ruled frame and a progress bar, error offers Try again.
progress
number
default —
How much of a large screenshot has loaded, 0 to 1; omit it for an indeterminate bar.
onRetry
() => void
default —
Try again was pressed after the screenshot failed to load.
title
string
default "Basinworks · levels, 14 Oct"
The editor's heading.
cellSize
number
default 16
The redaction grid in image px: the size of each pixelated block.
height
number
default 540
The editor's tallest height in px; the image letterboxes inside.
disabled
boolean
default false
Marks can be viewed but not drawn, moved, removed or exported.
className
string
default —
Classes for the editor's outer box.