Xzero UI
Pro library

Catch upPro

A catch me up digest for when you return, where one button reads Slack, Linear, GitHub and Gmail for the range you pick and streams back a short ranked summary: Needs you, Decisions made, Shipped and FYI. Each item is one sentence with its source mark, people and a jump link, and marking items as read clears them with a smooth collapse.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/catch-up
Live · keyboard ready

Drop it on a home or inbox screen for the moment someone comes back. Pass generate(range, signal) returning CatchUpItem[] (id, section, source, one-sentence text, context, when, people, href) and abort when the signal fires. Pass counts keyed by range for the per-source totals shown before it runs, plus onOpen and onMarkRead to wire jump links and read state. ranges, range/onRangeChange, model and title are optional. With no props it plays a simulated three-day digest once it scrolls into view.

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/catch-up

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { CatchUp } from "@/components/xzero/blocks/catch-up/catch-up";

export function ReturnDigest() {
  return (
    <CatchUp
      generate={async (range, signal) => {
        const response = await fetch(`/api/digest?range=${range}`, { signal });
        if (!response.ok) throw new Error("Gmail did not respond. Try again in a minute.");
        return response.json();
      }}
      counts={{ "1d": { slack: 31, linear: 9 }, "3d": { slack: 86, linear: 31 }, "1w": { slack: 214, linear: 77 } }}
      onOpen={item => window.open(item.href, "_blank")}
      onMarkRead={ids => fetch("/api/digest/read", { method: "POST", body: JSON.stringify({ ids }) })}
    />
  );
}

API reference

CatchUp

A catch me up digest card. One button reads Slack, Linear, GitHub and Gmail for a time range and streams back a prioritized summary in four sections: Needs you, Decisions made, Shipped and FYI. With no props it plays a simulated three day digest.

PropTypeDefaultDescription
generate(range: string, signal: AbortSignal) => Promise<CatchUpItem[]>–Builds the digest for a range. Abort the request when the signal fires (Stop, a new range, unmount). Leave it out to play the built in sample digest, which reads nothing.
rangesCatchUpRange[]1 day, 3 days, 1 weekOptions in the time range selector: { value, label, since }. The label also fills the button ("Catch up on 3 days") and since fills the summary line.
rangestring–Controlled range value. Pair with onRangeChange.
defaultRangestringsecond rangeStarting range when uncontrolled.
onRangeChange(range: string) => void–Called when the range changes. A digest that is already showing rebuilds for the new range.
countsRecord<string, Partial<Record<CatchUpSource, number>>>sample countsUnread updates per source, keyed by range value. Shown before the digest runs and summed in the summary line.
onOpen(item: CatchUpItem) => void–Jump to an item in its app. Without it, items with an href open in a new tab, and items without one show a simulated notice.
onMarkRead(ids: string[]) => void–Called with the ids marked as read, one item at a time or all at once from Mark all as read.
model{ name: string; mark?: ReactNode }Claude Haiku 5.5The model that writes the digest, shown in the footer.
titlestring"Welcome back"Card title.
autoStartbooleantrue without generateRuns the digest once when the card first scrolls into view.
classNamestring–Extra class on the root card.

CatchUpItem

One digest line.

PropTypeDefaultDescription
id (required)string–Stable id, passed back to onMarkRead.
section (required)"needs-you" | "decisions" | "shipped" | "fyi"–Which section the item belongs to. Sections show in this order and hide when empty.
source (required)"slack" | "linear" | "github" | "gmail"–Source app, drawn with its real mark.
text (required)string–One sentence. It streams in a few words at a time.
contextstring–Where it happened: a channel, issue, pull request or thread.
whenstring–Short time label, such as "Sun 13:10".
people{ name: string; avatar?: string }[]–People involved. Up to three faces show, with the first name and a count.
hrefstring–Jump link to the item in its app.

Keyboard interactions

KeysAction
TabMoves through the range selector, each item's open and mark as read buttons, then the footer action.
Arrow keysMove between time ranges in the selector.
Enter / SpaceRuns the digest, stops it, opens an item, or marks it as read.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Guidance for AI tools

Blocks are complete, self-contained screens with sample data. Replace the sample data and connect the callbacks described above. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components