{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "run-01",
  "title": "Tool calls",
  "description": "A turn's tool calls under one line that sums them up, each call folding open for its arguments and result: a result line slides in under a call as its mark turns into a check, and a failed call is retried in place.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://ui.ndekocode.dev/r/agent-run.json",
    "https://ui.ndekocode.dev/r/badge.json",
    "https://ui.ndekocode.dev/r/button.json",
    "https://ui.ndekocode.dev/r/call-status.json",
    "https://ui.ndekocode.dev/r/chat-motion.json",
    "https://ui.ndekocode.dev/r/copy-button.json",
    "https://ui.ndekocode.dev/r/demonstration.json",
    "https://ui.ndekocode.dev/r/icons.json",
    "https://ui.ndekocode.dev/r/json-view.json",
    "https://ui.ndekocode.dev/r/live-duration.json",
    "https://ui.ndekocode.dev/r/live-region.json",
    "https://ui.ndekocode.dev/r/motion-presets.json",
    "https://ui.ndekocode.dev/r/motion-word-roll.json",
    "https://ui.ndekocode.dev/r/replay-button.json",
    "https://ui.ndekocode.dev/r/run-session.json",
    "https://ui.ndekocode.dev/r/theme.json",
    "https://ui.ndekocode.dev/r/use-kept-height.json",
    "https://ui.ndekocode.dev/r/use-prefers-reduced-motion.json",
    "https://ui.ndekocode.dev/r/use-session.json",
    "https://ui.ndekocode.dev/r/use-timeouts.json",
    "https://ui.ndekocode.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/ndeko/components/run/run-01.tsx",
      "content": "'use client';\n\nimport { AnimatePresence, motion } from 'motion/react';\nimport { useId, useState } from 'react';\n\nimport { WordRoll } from '@/components/core/word-roll';\nimport { useKeptHeight } from '@/registry/ndeko/hooks/use-kept-height';\nimport { usePrefersReducedMotion } from '@/registry/ndeko/hooks/use-prefers-reduced-motion';\nimport {\n  useSession,\n  type SessionStep,\n} from '@/registry/ndeko/hooks/use-session';\nimport {\n  emptyRun,\n  reduceRun,\n  type CallStatus,\n  type RunEvent,\n  type RunSnapshot,\n  type ToolCall,\n} from '@/registry/ndeko/lib/agent-run';\nimport {\n  pressedQuietOf,\n  spokenUnless,\n} from '@/registry/ndeko/lib/demonstration';\nimport { ChevronDownIcon, RotateCcwIcon } from '@/registry/ndeko/lib/icons';\nimport { applyBatchAt, restingOf } from '@/registry/ndeko/lib/run-session';\nimport { cn } from '@/registry/ndeko/lib/utils';\nimport { Badge } from '@/registry/ndeko/ui/badge';\nimport { Button } from '@/registry/ndeko/ui/button';\nimport { Fold, Swap } from '@/registry/ndeko/ui/chat-motion';\n\nimport {\n  CALL_STATUS_WORD,\n  CallStatusMark,\n} from '@/registry/ndeko/ui/call-status';\nimport { CopyButton } from '@/registry/ndeko/ui/copy-button';\nimport { JsonView, jsonTextOf } from '@/registry/ndeko/ui/json-view';\nimport { ClockGate, LiveDuration } from '@/registry/ndeko/ui/live-duration';\nimport { LiveRegion } from '@/registry/ndeko/ui/live-region';\nimport { rowPresenceOf } from '@/registry/ndeko/lib/motion-presets';\nimport { ReplayButton } from '@/registry/ndeko/ui/replay-button';\nimport { Collapse } from './_parts/collapse';\nimport {\n  PAGE_ERROR,\n  PAGE_INPUT,\n  PAGE_OUTPUT,\n  RUN_TITLE,\n  SEARCH_INPUT,\n  SEARCH_OUTPUT,\n  SHEET_INPUT,\n  SHEET_OUTPUT,\n} from './_parts/scenario';\nimport { useTimeouts } from '@/registry/ndeko/hooks/use-timeouts';\n\nfunction chevronTurnOf(isOpen: boolean) {\n  if (isOpen) {\n    return 'rotate-180';\n  }\n  return 'rotate-0';\n}\n\nfunction isActiveStatus(status: CallStatus) {\n  const isQueued = status === 'queued';\n  const isRunning = status === 'running';\n  const isWaiting = status === 'waiting';\n  return isQueued || isRunning || isWaiting;\n}\n\nfunction retryWordOf(status: CallStatus) {\n  if (status === 'running') {\n    return 'Second try';\n  }\n  if (status === 'failed') {\n    return 'Failed again';\n  }\n  return 'Retried';\n}\n\n/** A running call and a finished one share one key, so the clock keeps counting instead of swapping. */\nfunction timeKeyOf(status: CallStatus) {\n  if (status === 'queued') {\n    return 'queued';\n  }\n  if (status === 'waiting') {\n    return 'waiting';\n  }\n  return 'timed';\n}\n\n/** A duration that ticks keeps its width, so nothing beside it shifts ten times a second. */\nconst TICKING_TIME =\n  'text-muted-foreground inline-block min-w-8 text-end text-xs';\n\nfunction CallTime({ call }: { call: ToolCall }) {\n  const isRunning = call.status === 'running';\n  const hasStarted = call.startedAt !== undefined;\n  if (call.status === 'queued') {\n    return <span className='text-muted-foreground text-xs'>Queued</span>;\n  }\n  if (call.status === 'waiting') {\n    return <span className='text-warning-foreground text-xs'>Waiting</span>;\n  }\n  if (!hasStarted) {\n    return null;\n  }\n  return (\n    <LiveDuration item={call} isRunning={isRunning} className={TICKING_TIME} />\n  );\n}\n\nfunction SectionHeader({\n  title,\n  copyText,\n  copyLabel,\n}: {\n  title: string;\n  copyText: string;\n  copyLabel: string;\n}) {\n  return (\n    <div className='flex h-6 items-center justify-between gap-2'>\n      <span className='text-muted-foreground text-xs font-medium'>{title}</span>\n      <CopyButton text={copyText} label={copyLabel} />\n    </div>\n  );\n}\n\nfunction CallDetails({ call }: { call: ToolCall }) {\n  const inputText = jsonTextOf(call.input ?? {});\n  const outputText = jsonTextOf(call.output);\n  const hasOutput = call.output !== undefined;\n  const isFailed = call.status === 'failed';\n  const isPending = isActiveStatus(call.status);\n\n  return (\n    <div className='flex flex-col gap-2.5 ps-9 pe-2 pb-3'>\n      <div>\n        <SectionHeader\n          title='Arguments'\n          copyText={inputText}\n          copyLabel={`Copy the arguments of ${call.tool}`}\n        />\n        <JsonView value={call.input ?? {}} />\n      </div>\n      {hasOutput && (\n        <div>\n          <SectionHeader\n            title='Result'\n            copyText={outputText}\n            copyLabel={`Copy the result of ${call.tool}`}\n          />\n          <JsonView value={call.output} />\n        </div>\n      )}\n      {isFailed && (\n        <div>\n          <SectionHeader\n            title='Error'\n            copyText={call.error ?? ''}\n            copyLabel={`Copy the error of ${call.tool}`}\n          />\n          <p className='bg-destructive/10 text-destructive-ink rounded-lg p-2.5 font-mono text-xs leading-relaxed'>\n            {call.error}\n          </p>\n        </div>\n      )}\n      {isPending && (\n        <p className='text-muted-foreground text-xs'>\n          The result shows here once the call returns\n        </p>\n      )}\n    </div>\n  );\n}\n\nexport type ToolCallCardProps = {\n  call: ToolCall;\n  /** How many times the call has run. From the second, the row says it was retried. */\n  attempt?: number;\n  onRetry?: (callId: string) => void;\n  /** One line on what came back. It slides in under the call when it is done. */\n  resultLine?: string;\n  /** Shows Retry held down, for a demonstration that presses it. */\n  isRetryPressed?: boolean;\n  defaultOpen?: boolean;\n  className?: string;\n};\n\nexport function ToolCallCard({\n  call,\n  attempt = 1,\n  onRetry,\n  resultLine,\n  isRetryPressed = false,\n  defaultOpen = false,\n  className,\n}: ToolCallCardProps) {\n  const [isOpen, setIsOpen] = useState(defaultOpen);\n  const panelId = useId();\n  const shouldReduceMotion = usePrefersReducedMotion();\n  const rowPresence = rowPresenceOf(shouldReduceMotion);\n  const isFailed = call.status === 'failed';\n  const isDone = call.status === 'done';\n  const hasResultLine = resultLine !== undefined;\n  const showsResult = isDone && hasResultLine;\n  const wasRetried = attempt > 1;\n  const retryWord = retryWordOf(call.status);\n  const canRetry = onRetry !== undefined;\n\n  function toggle() {\n    setIsOpen((current) => !current);\n  }\n\n  function retry() {\n    onRetry?.(call.id);\n  }\n\n  return (\n    <div data-status={call.status} className={cn('rounded-lg', className)}>\n      <button\n        type='button'\n        aria-expanded={isOpen}\n        aria-controls={panelId}\n        onClick={toggle}\n        className='hover:bg-foreground/[0.03] focus-visible:ring-ring/25 @container flex min-h-10 w-full cursor-pointer items-center gap-3 rounded-lg px-2 py-2 text-start outline-none focus-visible:ring-[3px]'\n      >\n        <CallStatusMark status={call.status} />\n        <span className='flex min-w-0 flex-1 flex-col gap-0.5 @md:flex-row @md:items-baseline @md:gap-2'>\n          <span className='text-muted-foreground order-1 shrink-0 font-mono text-[11px] @md:order-none @md:text-xs'>\n            {call.tool}\n          </span>\n          <span className='min-w-0 truncate text-[13px]'>{call.label}</span>\n        </span>\n        {wasRetried && (\n          <Badge variant='secondary'>\n            <Swap swapKey={retryWord}>{retryWord}</Swap>\n          </Badge>\n        )}\n        <Swap swapKey={timeKeyOf(call.status)} className='shrink-0'>\n          <CallTime call={call} />\n        </Swap>\n        <ChevronDownIcon\n          className={cn(\n            'text-muted-foreground size-4 shrink-0 transition-transform duration-200 ease-(--ease-out-strong)',\n            chevronTurnOf(isOpen)\n          )}\n        />\n      </button>\n      <AnimatePresence initial={false}>\n        {isFailed && (\n          <motion.div key='error' className='overflow-hidden' {...rowPresence}>\n            <div className='flex items-center gap-3 ps-9 pe-2 pb-2'>\n              <p className='text-destructive-ink min-w-0 flex-1 text-xs leading-5'>\n                {call.error}\n              </p>\n              {canRetry && (\n                <Button\n                  type='button'\n                  variant='outline'\n                  size='sm'\n                  onClick={retry}\n                  aria-label={`Retry ${call.tool}`}\n                  className={pressedQuietOf(isRetryPressed)}\n                >\n                  <RotateCcwIcon />\n                  Retry\n                </Button>\n              )}\n            </div>\n          </motion.div>\n        )}\n        {showsResult && (\n          <motion.p\n            key='result'\n            className='text-muted-foreground overflow-hidden text-xs'\n            {...rowPresence}\n          >\n            <span className='block truncate ps-9 pe-2 pb-2 leading-5'>\n              {resultLine}\n            </span>\n          </motion.p>\n        )}\n      </AnimatePresence>\n      <Fold open={isOpen} id={panelId}>\n        <CallDetails call={call} />\n      </Fold>\n    </div>\n  );\n}\n\ntype GroupStatus = 'queued' | 'running' | 'failed' | 'done';\n\nfunction groupStatusOf(calls: ToolCall[]): GroupStatus {\n  if (calls.length === 0) {\n    return 'queued';\n  }\n  const hasActive = calls.some((call) => isActiveStatus(call.status));\n  if (hasActive) {\n    return 'running';\n  }\n  const hasFailed = calls.some((call) => call.status === 'failed');\n  if (hasFailed) {\n    return 'failed';\n  }\n  return 'done';\n}\n\nfunction toolWordOf(count: number) {\n  if (count === 1) {\n    return '1 tool';\n  }\n  return `${count} tools`;\n}\n\nfunction verbOf(status: GroupStatus) {\n  if (status === 'running') {\n    return 'Using';\n  }\n  return 'Used';\n}\n\nfunction groupSummaryOf(calls: ToolCall[]) {\n  const status = groupStatusOf(calls);\n  if (status === 'queued') {\n    return 'Waiting for the first tool';\n  }\n  const verb = verbOf(status);\n  const tools = toolWordOf(calls.length);\n  const failedCalls = calls.filter((call) => call.status === 'failed');\n  const failedCount = failedCalls.length;\n  if (failedCount === 0) {\n    return `${verb} ${tools}`;\n  }\n  return `${verb} ${tools}, ${failedCount} failed`;\n}\n\ntype Span = { startedAt?: number; endedAt?: number };\n\nfunction earlierOf(current: number | undefined, candidate: number) {\n  if (current === undefined) {\n    return candidate;\n  }\n  return Math.min(current, candidate);\n}\n\nfunction laterOf(current: number | undefined, candidate: number) {\n  if (current === undefined) {\n    return candidate;\n  }\n  return Math.max(current, candidate);\n}\n\nfunction spanOf(calls: ToolCall[]): Span {\n  let startedAt: number | undefined;\n  let endedAt: number | undefined;\n  for (const call of calls) {\n    if (call.startedAt !== undefined) {\n      startedAt = earlierOf(startedAt, call.startedAt);\n    }\n    if (call.endedAt !== undefined) {\n      endedAt = laterOf(endedAt, call.endedAt);\n    }\n  }\n  const hasActive = calls.some((call) => isActiveStatus(call.status));\n  if (hasActive) {\n    return { startedAt };\n  }\n  return { startedAt, endedAt };\n}\n\nexport type ToolCallGroupProps = {\n  calls: ToolCall[];\n  attempts?: Record<string, number>;\n  onRetry?: (callId: string) => void;\n  /** One line per call id on what came back, shown under the call once it is done. */\n  resultLines?: Record<string, string>;\n  /** The call whose Retry a demonstration holds down. */\n  pressedRetryId?: string | null;\n  defaultOpen?: boolean;\n  className?: string;\n};\n\nexport function ToolCallGroup({\n  calls,\n  attempts = {},\n  onRetry,\n  resultLines = {},\n  pressedRetryId = null,\n  defaultOpen = true,\n  className,\n}: ToolCallGroupProps) {\n  const [isOpen, setIsOpen] = useState(defaultOpen);\n  const listId = useId();\n  const shouldReduceMotion = usePrefersReducedMotion();\n  const rowPresence = rowPresenceOf(shouldReduceMotion);\n  const status = groupStatusOf(calls);\n  const summary = groupSummaryOf(calls);\n  const span = spanOf(calls);\n  const isRunning = status === 'running';\n  const hasStarted = span.startedAt !== undefined;\n\n  function toggle() {\n    setIsOpen((current) => !current);\n  }\n\n  return (\n    <section\n      aria-label='Tool calls'\n      className={cn(\n        'bg-card hairline-ring ring-border rounded-xl p-1.5 shadow-[0_1px_2px_rgb(0_0_0/0.04)]',\n        className\n      )}\n    >\n      <button\n        type='button'\n        aria-expanded={isOpen}\n        aria-controls={listId}\n        onClick={toggle}\n        className='hover:bg-foreground/[0.03] focus-visible:ring-ring/25 flex h-9 w-full cursor-pointer items-center gap-3 rounded-lg px-2 text-start outline-none focus-visible:ring-[3px]'\n      >\n        <CallStatusMark status={status} />\n        <span className='min-w-0 flex-1 truncate text-[13px] font-medium'>\n          <WordRoll text={summary} />\n        </span>\n        {hasStarted && (\n          <LiveDuration\n            item={span}\n            isRunning={isRunning}\n            className={TICKING_TIME}\n          />\n        )}\n        <ChevronDownIcon\n          className={cn(\n            'text-muted-foreground size-4 shrink-0 transition-transform duration-200 ease-(--ease-out-strong)',\n            chevronTurnOf(isOpen)\n          )}\n        />\n      </button>\n      <Collapse open={isOpen} id={listId}>\n        <ul className='flex flex-col'>\n          <AnimatePresence initial={false}>\n            {calls.map((call) => (\n              <motion.li\n                key={call.id}\n                className='overflow-hidden'\n                {...rowPresence}\n              >\n                <ToolCallCard\n                  call={call}\n                  attempt={attempts[call.id] ?? 1}\n                  onRetry={onRetry}\n                  resultLine={resultLines[call.id]}\n                  isRetryPressed={pressedRetryId === call.id}\n                />\n              </motion.li>\n            ))}\n          </AnimatePresence>\n        </ul>\n      </Collapse>\n    </section>\n  );\n}\n\nconst RETRY_MS = 1300;\n\n/** A call that waits on a person is retried by the demonstration after this long. */\nconst RETRY_AFTER_MS = 2500;\n\nconst INITIAL_RUN = emptyRun({ id: 'brightmoor-turn', title: RUN_TITLE });\n\nconst RETRY_OUTPUTS: Record<string, unknown> = {\n  page: PAGE_OUTPUT,\n};\n\nconst RESULT_LINES: Record<string, string> = {\n  search: '5 results, the price list first',\n  page: 'Wholesale pricing, 3 tables',\n  sheet: '117 rows, $48,210.50 in total',\n};\n\ntype CallsState = {\n  run: RunSnapshot;\n  attempts: Record<string, number>;\n};\n\ntype CallsAction =\n  | { type: 'events'; events: RunEvent[] }\n  | { type: 'retry'; callId: string }\n  | { type: 'retried'; callId: string };\n\ntype CallsStep = SessionStep<CallsState, CallsAction>;\n\nconst INITIAL: CallsState = { run: INITIAL_RUN, attempts: {} };\n\nfunction callOf(state: CallsState, callId: string) {\n  return state.run.calls.find((call) => call.id === callId);\n}\n\nfunction attemptOf(state: CallsState, callId: string) {\n  return state.attempts[callId] ?? 1;\n}\n\nfunction withRetry(state: CallsState, callId: string, now: number) {\n  const call = callOf(state, callId);\n  const isFailed = call?.status === 'failed';\n  if (!isFailed) {\n    return state;\n  }\n  const previous = attemptOf(state, callId);\n  const attempts = { ...state.attempts, [callId]: previous + 1 };\n  const event: RunEvent = {\n    type: 'call.updated',\n    id: callId,\n    patch: {\n      status: 'running',\n      error: undefined,\n      startedAt: undefined,\n      endedAt: undefined,\n    },\n  };\n  const run = reduceRun(state.run, event, now);\n  return { run, attempts };\n}\n\n/** The script and the hand of a person may both land the same retry: only the first one counts. */\nfunction withRetryLanded(state: CallsState, callId: string, now: number) {\n  const call = callOf(state, callId);\n  const isRunning = call?.status === 'running';\n  const attempt = attemptOf(state, callId);\n  const wasRetried = attempt > 1;\n  const isRetrying = isRunning && wasRetried;\n  if (!isRetrying) {\n    return state;\n  }\n  const event: RunEvent = {\n    type: 'call.updated',\n    id: callId,\n    patch: { status: 'done', output: RETRY_OUTPUTS[callId] },\n  };\n  const run = reduceRun(state.run, event, now);\n  return { ...state, run };\n}\n\nfunction applyAt(\n  state: CallsState,\n  action: CallsAction,\n  now: number\n): CallsState {\n  if (action.type === 'retry') {\n    return withRetry(state, action.callId, now);\n  }\n  if (action.type === 'retried') {\n    return withRetryLanded(state, action.callId, now);\n  }\n  const run = applyBatchAt(state.run, action, now);\n  return { ...state, run };\n}\n\n/** Times are stamped when an action lands, as a backend would stream it. */\nfunction reducer(state: CallsState, action: CallsAction) {\n  return applyAt(state, action, Date.now());\n}\n\nfunction landing(after: number, ...events: RunEvent[]): CallsStep {\n  return { after, action: { type: 'events', events } };\n}\n\nfunction hasRetriedPage(state: CallsState) {\n  const attempt = attemptOf(state, 'page');\n  return attempt > 1;\n}\n\nconst SCRIPT: CallsStep[] = [\n  landing(500, { type: 'run.started' }),\n  landing(200, {\n    type: 'call.started',\n    call: {\n      id: 'search',\n      tool: 'web_search',\n      label: 'Searched the web for Brightmoor prices',\n      status: 'running',\n      input: SEARCH_INPUT,\n    },\n  }),\n  landing(150, {\n    type: 'call.started',\n    call: {\n      id: 'page',\n      tool: 'fetch_page',\n      label: 'Read the Brightmoor pricing page',\n      status: 'queued',\n      input: PAGE_INPUT,\n    },\n  }),\n  landing(150, {\n    type: 'call.started',\n    call: {\n      id: 'sheet',\n      tool: 'read_sheet',\n      label: 'Read the Q3 invoices spreadsheet',\n      status: 'queued',\n      input: SHEET_INPUT,\n    },\n  }),\n  landing(\n    900,\n    {\n      type: 'call.updated',\n      id: 'search',\n      patch: { status: 'done', output: SEARCH_OUTPUT },\n    },\n    { type: 'call.updated', id: 'page', patch: { status: 'running' } }\n  ),\n  landing(\n    1100,\n    {\n      type: 'call.updated',\n      id: 'page',\n      patch: { status: 'failed', error: PAGE_ERROR },\n    },\n    { type: 'call.updated', id: 'sheet', patch: { status: 'running' } }\n  ),\n  landing(\n    800,\n    {\n      type: 'call.updated',\n      id: 'sheet',\n      patch: { status: 'done', output: SHEET_OUTPUT },\n    },\n    { type: 'run.finished', status: 'done' }\n  ),\n  {\n    after: RETRY_AFTER_MS,\n    by: 'person',\n    press: 'page',\n    action: { type: 'retry', callId: 'page' },\n    isAnswered: hasRetriedPage,\n  },\n  { after: RETRY_MS, action: { type: 'retried', callId: 'page' } },\n];\n\nconst SESSION = {\n  reducer,\n  initial: INITIAL,\n  resting: restingOf(INITIAL, SCRIPT, applyAt),\n  script: SCRIPT,\n  loopAfter: 3000,\n};\n\nfunction lastChangeOf(call: ToolCall) {\n  if (call.endedAt !== undefined) {\n    return call.endedAt;\n  }\n  if (call.startedAt !== undefined) {\n    return call.startedAt;\n  }\n  return -Infinity;\n}\n\nfunction latestOf(calls: ToolCall[]) {\n  let latest: ToolCall | undefined;\n  let latestTime = -Infinity;\n  for (const call of calls) {\n    const time = lastChangeOf(call);\n    if (time >= latestTime) {\n      latest = call;\n      latestTime = time;\n    }\n  }\n  return latest;\n}\n\nfunction announcementOf(calls: ToolCall[]) {\n  const latest = latestOf(calls);\n  if (!latest) {\n    return '';\n  }\n  const word = CALL_STATUS_WORD[latest.status];\n  return `${latest.label}: ${word}`;\n}\n\nexport default function Run01() {\n  const { schedule, clearAll } = useTimeouts();\n  const {\n    ref,\n    isInView,\n    isAutoplaying,\n    state,\n    act,\n    pressed,\n    isFinished,\n    restart,\n  } = useSession(SESSION);\n  const calls = state.run.calls;\n  const announcement = announcementOf(calls);\n  const canReplay = isFinished && !isAutoplaying;\n\n  function retry(callId: string) {\n    act({ type: 'retry', callId });\n    schedule(() => act({ type: 'retried', callId }), RETRY_MS);\n  }\n\n  function replay() {\n    clearAll();\n    restart();\n  }\n\n  useKeptHeight(ref);\n\n  return (\n    <div\n      ref={ref}\n      className='flex min-h-[22rem] w-full max-w-[34rem] flex-col gap-2'\n    >\n      <ClockGate isActive={isInView}>\n        <ToolCallGroup\n          calls={calls}\n          attempts={state.attempts}\n          onRetry={retry}\n          resultLines={RESULT_LINES}\n          pressedRetryId={pressed}\n        />\n      </ClockGate>\n      <div className='flex h-7 items-center justify-end'>\n        <ReplayButton isShown={canReplay} onReplay={replay} />\n      </div>\n      <LiveRegion message={spokenUnless(isAutoplaying, announcement)} />\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/run/run-01.tsx"
    },
    {
      "path": "registry/ndeko/components/run/_parts/collapse.tsx",
      "content": "'use client';\n\nimport { motion, type Transition } from 'motion/react';\n\nimport { usePrefersReducedMotion } from '@/registry/ndeko/hooks/use-prefers-reduced-motion';\nimport { EASE_OUT_STRONG, INSTANT } from '@/registry/ndeko/lib/motion-presets';\n\nconst OPEN = { height: 'auto', opacity: 1 };\n\nconst CLOSED = { height: 0, opacity: 0 };\n\nconst OPENING: Transition = { duration: 0.2, ease: EASE_OUT_STRONG };\n\nconst CLOSING: Transition = { duration: 0.14, ease: EASE_OUT_STRONG };\n\nfunction targetOf(open: boolean) {\n  if (open) {\n    return OPEN;\n  }\n  return CLOSED;\n}\n\nfunction transitionOf(open: boolean, shouldReduceMotion: boolean | null) {\n  if (shouldReduceMotion) {\n    return INSTANT;\n  }\n  if (open) {\n    return OPENING;\n  }\n  return CLOSING;\n}\n\ntype CollapseProps = {\n  open: boolean;\n  id?: string;\n  children: React.ReactNode;\n};\n\n/**\n * A panel that folds and keeps its content in the page. Open, it takes the height of its content\n * without animating it, so a row that arrives inside moves once, by its own presence. The padding\n * leaves room for focus rings and the margin takes it back, also when the panel is closed.\n */\nexport function Collapse({ open, id, children }: CollapseProps) {\n  const shouldReduceMotion = usePrefersReducedMotion();\n\n  return (\n    <motion.div\n      id={id}\n      inert={!open}\n      initial={false}\n      animate={targetOf(open)}\n      transition={transitionOf(open, shouldReduceMotion)}\n      className='-m-1 overflow-hidden p-1'\n    >\n      {children}\n    </motion.div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/run/_parts/collapse.tsx"
    },
    {
      "path": "registry/ndeko/components/run/_parts/scenario.ts",
      "content": "import type { RunAgent } from '@/registry/ndeko/lib/agent-run';\n\nexport const RUN_TITLE = 'Quarterly vendor review for Brightmoor Supply';\n\nexport const PLANNER: RunAgent = {\n  id: 'planner',\n  name: 'Planner',\n  series: 1,\n  status: 'idle',\n  model: 'Claude Opus',\n  role: 'Lead',\n};\n\nexport const RESEARCHER: RunAgent = {\n  id: 'researcher',\n  name: 'Researcher',\n  series: 2,\n  status: 'idle',\n  model: 'Claude Sonnet',\n  parentId: 'planner',\n};\n\nexport const WRITER: RunAgent = {\n  id: 'writer',\n  name: 'Writer',\n  series: 3,\n  status: 'idle',\n  model: 'Claude Haiku',\n  parentId: 'planner',\n};\n\nexport const PRICING_URL = 'https://brightmoor-supply.example/pricing';\n\nexport const SEARCH_INPUT = {\n  query: 'Brightmoor Supply wholesale price list',\n  limit: 5,\n};\n\nexport const SEARCH_OUTPUT = {\n  results: 5,\n  top: PRICING_URL,\n};\n\nexport const PAGE_INPUT = {\n  url: PRICING_URL,\n  timeoutMs: 8000,\n};\n\nexport const PAGE_ERROR = '403 Forbidden: the site refused the reader';\n\nexport const PAGE_OUTPUT = {\n  title: 'Wholesale pricing',\n  tables: 3,\n  updated: '2026-10-02',\n};\n\nexport const SHEET_INPUT = {\n  file: 'q3-invoices.xlsx',\n  sheet: 'Brightmoor',\n  range: 'A1:F118',\n};\n\nexport const SHEET_OUTPUT = {\n  rows: 117,\n  total: 48210.5,\n  currency: 'USD',\n  flagged: false,\n};\n\nexport const SCRIPT_INPUT = {\n  script: 'compare_prices.py',\n  args: ['--quarter', 'Q3'],\n};\n\nexport const SCRIPT_OUTPUT = {\n  items: 42,\n  aboveList: 6,\n  overpaid: 1840.2,\n};\n\nexport const EMAIL_INPUT = {\n  to: 'accounts@brightmoor-supply.example',\n  subject: 'Q3 pricing review',\n};\n\nexport const EMAIL_OUTPUT = {\n  words: 186,\n  attachments: 1,\n};\n",
      "type": "registry:component",
      "target": "components/run/_parts/scenario.ts"
    }
  ],
  "categories": [
    "components",
    "run"
  ],
  "type": "registry:component"
}