Run heartbeat

Pro

A run's state that never claims it's alive when nothing has been heard. Running and heard from recently, it says "Running · heard 1 minute ago". Running and silent past staleAfter (two minutes unless set), it says "No update since 10:42" instead, and that it may have stopped. A host the app can't reach says so, with when. A run that needs the person says what for, with a Respond the app handles. Before the page has a clock (server rendering) it gives the last update's time and claims nothing. Without lastHeardAt the app isn't tracking freshness, and the status is shown as given.

Markdown

Preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/run-heartbeat

Brings in: button, locale, relative-time, status, status-dot, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import { RunHeartbeat } from "@/components/run-heartbeat"

const minutesAgo = (m: number) => new Date(Date.now() - m * 60_000).toISOString()

export default function Example() {
  return (
    <div className="flex max-w-md flex-col divide-y rounded-lg border bg-card">
      {/* Heard from a minute ago: running. */}
      <RunHeartbeat className="px-4 py-3" status="running" lastHeardAt={minutesAgo(1)} />
      {/* Silent for 14 minutes: it no longer says running. */}
      <RunHeartbeat className="px-4 py-3" status="running" lastHeardAt={minutesAgo(14)} />
      {/* Its machine can't be reached. */}
      <RunHeartbeat className="px-4 py-3" status="running" lastHeardAt={minutesAgo(7)} hostOffline={{ host: "Runner 3", since: minutesAgo(6) }} />
      {/* Waiting on a person, with what for. */}
      <RunHeartbeat className="px-4 py-3" status="waiting" needs="Approve dropping partner_tier_old" onRespond={() => {}} />
      <RunHeartbeat className="px-4 py-3" status="succeeded" />
    </div>
  )
}

<RunHeartbeat>

PropTypeDefaultDescription
statusrequiredRunStatus—The run's status as the service last reported it.
lastHeardAtDate | string | undefined—When the app last heard anything from the run: an event, a log line, a heartbeat.
hostOfflineHostOffline | undefined—Set when the machine or worker running it can't be reached. Shown ahead of everything but a finished run.
needsstring | undefined—What the run needs from the person, in plain words. Set, or with status "waiting", it shows as needing you.
labelstring | undefined—Your word for the status ("Making"), used while the run is live or finished.
staleAfternumber | undefinedSTALE_AFTERHow long a running run may be silent before it's no longer called running, in milliseconds. Defaults to two minutes.
onRespond(() => void) | undefined—Called when the person picks Respond on a run that needs them. Without it, there's no button.
compactboolean | undefinedfalseOne line, for lists: no hint and no button.
classNamestring | undefined—Extra classes for the outer element.

Functions

useLivenessWords(staleAfter = STALE_AFTER)

The words for a run that can't be called alive, for components that show many runs: const words = useLivenessWords(); words(run) gives its liveness and, when it's stale, offline or unchecked, the line to show instead of its status ("No update since 10:42"). For a live or finished run the text is null.

Strings

The fixed words, in English by default. Change any of them under runHeartbeat in <LocaleProvider strings={{ … }}>; the table is exported as runHeartbeatStrings.

KeyEnglish
heardheard {time}
staleNo update since {time}
noUpdateNo update received
staleHintIt may have stopped. Nothing has been heard from it since.
offline{host} is offline
offlineSince{host} offline since {time}
hostThe host
lastUpdateLast update {time}
needsYouNeeds you
respondRespond