KQ-1651 · EXCEPTION/TRIAGE
Exception Triage
An error inbox where each row is a group of exceptions sharing a fingerprint: its level, type and message, where it was thrown, a 24-hour sparkline, events, users affected, when it was last and first seen, and who owns it. Resolving plays in three beats: the sparkline's points glide flat onto the floor on springs.glide while it turns to success, the flat line writes a Resolved label uncovered left to right along it, and the row folds away on the exit ease while the rows below close up on glide; an issue that regresses slides back to the top on glide and stamps a Reopened tag on springs.recoil. It is a grid: J and K move, E resolves, I ignores, A opens the assign menu and Enter opens the issue.
Waylight checkout
8 open · 1 reopened
J and K move between issues. E resolves, I ignores, A assigns, Enter opens.
8 open · 1 reopened · j k move · e resolve
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/exception-triage
Props
- sparkStyle
- "line" | "bars" | "area"
- default "line"
- How each 24-hour sparkline is drawn: a line ending in a dot, bars, or a filled area.
- density
- "compact" | "cozy" | "roomy"
- default "cozy"
- Row height (54, 64 or 74px) and type size.
- palette
- "cobalt" | "ember" | "mono"
- default "cobalt"
- Colours for sparklines and level stripes: cobalt, ember (warm by level) or mono.
- issues
- TriageIssue[]
- default defaultTriageIssues
- Issue groups: { id, fingerprint, type, message, culprit, level, events, users, firstSeen, lastSeen, hourly, status, assignee? }. Unresolved and regressed ones are listed, regressions first.
- members
- TriageMember[]
- default defaultTriageMembers
- Who an issue can be assigned to: { id, name, initials }.
- now
- number
- default defaultTriageNow
- The time last and first seen are measured from, in ms; never read from the clock.
- onResolve
- (issue: TriageIssue) => void
- default —
- Fires from the button or the E key; the row then plays its resolve and folds away.
- onIgnore
- (issue: TriageIssue) => void
- default —
- Fires from the button or the I key; the row folds away.
- onAssign
- (issue: TriageIssue, memberId: string | null) => void
- default —
- Fires when someone is picked in the assign menu (A opens it), or null to unassign.
- onOpen
- (issue: TriageIssue) => void
- default —
- Enter on an issue, or a press on it.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading draws skeleton rows; error shows its own state with Retry; an empty inbox has its own state too.
- onRetry
- () => void
- default —
- Retry was pressed after the issues failed to load.
- labelHold
- number
- default 520
- How long the Resolved label holds before the row folds, in ms.
- title
- string
- default "Waylight checkout"
- The heading and the grid's accessible name.
- emptyLabel
- string
- default "Inbox zero. Nothing new to triage."
- What an empty inbox says.
- height
- number
- default 520
- Height of the surface in px; the list scrolls under its header.
- disabled
- boolean
- default false
- Nothing can be resolved, ignored or assigned; the surface dims.
- className
- string
- default —
- Classes for the outer surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| sparkStyle | "line" | "bars" | "area" | "line" | How each 24-hour sparkline is drawn: a line ending in a dot, bars, or a filled area. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Row height (54, 64 or 74px) and type size. |
| palette | "cobalt" | "ember" | "mono" | "cobalt" | Colours for sparklines and level stripes: cobalt, ember (warm by level) or mono. |
| issues | TriageIssue[] | defaultTriageIssues | Issue groups: { id, fingerprint, type, message, culprit, level, events, users, firstSeen, lastSeen, hourly, status, assignee? }. Unresolved and regressed ones are listed, regressions first. |
| members | TriageMember[] | defaultTriageMembers | Who an issue can be assigned to: { id, name, initials }. |
| now | number | defaultTriageNow | The time last and first seen are measured from, in ms; never read from the clock. |
| onResolve | (issue: TriageIssue) => void | — | Fires from the button or the E key; the row then plays its resolve and folds away. |
| onIgnore | (issue: TriageIssue) => void | — | Fires from the button or the I key; the row folds away. |
| onAssign | (issue: TriageIssue, memberId: string | null) => void | — | Fires when someone is picked in the assign menu (A opens it), or null to unassign. |
| onOpen | (issue: TriageIssue) => void | — | Enter on an issue, or a press on it. |
| status | "ready" | "loading" | "error" | "ready" | Loading draws skeleton rows; error shows its own state with Retry; an empty inbox has its own state too. |
| onRetry | () => void | — | Retry was pressed after the issues failed to load. |
| labelHold | number | 520 | How long the Resolved label holds before the row folds, in ms. |
| title | string | "Waylight checkout" | The heading and the grid's accessible name. |
| emptyLabel | string | "Inbox zero. Nothing new to triage." | What an empty inbox says. |
| height | number | 520 | Height of the surface in px; the list scrolls under its header. |
| disabled | boolean | false | Nothing can be resolved, ignored or assigned; the surface dims. |
| className | string | — | Classes for the outer surface. |