{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "trace",
  "title": "Trace",
  "description": "The steps an agent takes, on one rail, each with a status, what it acted on and what it found.",
  "dependencies": [
    "@hugeicons/core-free-icons",
    "@hugeicons/react",
    "@stylexjs/stylex"
  ],
  "registryDependencies": [
    "https://constella.aayush.cv/r/base.json",
    "https://constella.aayush.cv/r/collapsible.json",
    "https://constella.aayush.cv/r/shimmer.json",
    "https://constella.aayush.cv/r/swap-text.json"
  ],
  "files": [
    {
      "path": "src/components/ai/trace.tsx",
      "content": "\"use client\";\n\nimport { ArrowDown01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport {\n  createContext,\n  use,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { Shimmer } from \"@/components/ai/shimmer\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/primitives/collapsible\";\nimport { SwapText } from \"@/components/primitives/swap-text\";\nimport type { Sx } from \"@/lib/constella/styles\";\nimport { typeStyles } from \"@/lib/constella/styles\";\nimport {\n  colors,\n  durations,\n  easings,\n  lineHeights,\n  media,\n  motion,\n  opacities,\n  sizes,\n  space,\n  strokes,\n} from \"@/lib/constella/tokens.stylex\";\n\nconst traceStates = [\"running\", \"error\", \"pending\"] as const;\n\ntype TraceState = (typeof traceStates)[number];\n\ntype Styled<T> = Omit<T, \"className\" | \"style\"> & {\n  sx?: Sx;\n};\n\ntype CollapsibleProps = ComponentProps<typeof Collapsible>;\n\ninterface StepContext {\n  // Whether the step is still arriving, so a substep mounted with it doesn't enter twice.\n  arriving: boolean;\n  state: TraceState | undefined;\n}\n\nconst Step = createContext<StepContext>({\n  arriving: false,\n  state: undefined,\n});\n\nconst InStepTrigger = createContext(false);\n\n// True once the Trace has mounted: only a step that arrives after it gets an entrance, so a\n// run read back from history, or rendered on the server, lands without one.\nconst Live = createContext<{ readonly current: boolean }>({ current: false });\n\nconst step = \"[data-slot='trace-step']\";\nconst open = \":is([data-panel-open] *)\";\n// Only the trigger's own flag: the run's panel carries it too, and holds every step's chevron.\nconst instant = \":is([data-slot$='-trigger'][data-collapsible-instant] *)\";\nconst rtl = \":is([dir='rtl'] *)\";\nconst inAction = \":is(a > *, button > *)\";\nconst hovered = \":is(a:hover > *, button:hover > *)\";\nconst pressed = \":is(a:active > *, button:active > *, [data-popup-open] > *)\";\nconst inTrigger = \":is([data-slot='trace-collapsible-trigger'] *)\";\nconst stepHovered = \":is([data-slot='trace-step-trigger']:hover *)\";\nconst stepFocused = \":is([data-slot='trace-step-trigger']:focus-visible *)\";\n\n// The rail stops at each marker's box, measured from its center, half a row down; the box's room\n// around its 14px icon is the gap.\nconst hole = `calc(${sizes.controlXs} / 2)`;\nconst rail = {\n  backgroundColor: colors.border,\n  content: \"''\",\n  insetInlineStart: `calc(${sizes.controlXs} / 2 - ${strokes.border} / 2)`,\n  position: \"absolute\",\n  width: strokes.border,\n} as const;\n\nconst grow = stylex.keyframes({\n  from: { gridTemplateRows: \"0fr\" },\n});\n\nconst arrive = stylex.keyframes({\n  from: {\n    filter: `blur(${motion.crossfadeTextBlur})`,\n    opacity: 0,\n    transform: `translateY(${motion.arriveOffset})`,\n  },\n});\n\nconst fadeIn = stylex.keyframes({\n  from: { opacity: 0 },\n});\n\nconst styles = stylex.create({\n  trace: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    minWidth: 0,\n    width: \"100%\",\n  },\n  // Holds its rail, drawn as two halves around the marker so no mask covers it.\n  step: {\n    display: \"grid\",\n    gridTemplateRows: \"1fr\",\n    position: \"relative\",\n    \"::before\": {\n      ...rail,\n      content: { default: \"''\", [`:nth-child(1 of ${step})`]: \"none\" },\n      height: `calc(${sizes.controlMd} / 2 - ${hole})`,\n      top: 0,\n    },\n    \"::after\": {\n      ...rail,\n      bottom: 0,\n      // The rail ends on the newest step.\n      content: { default: \"''\", [`:nth-last-child(1 of ${step})`]: \"none\" },\n      top: `calc(${sizes.controlMd} / 2 + ${hole})`,\n    },\n  },\n  // A step that arrives opens its space first, so the rail grows down to it.\n  growing: {\n    animationDuration: { default: durations.move, [media.reducedMotion]: \"0s\" },\n    animationName: grow,\n    animationTimingFunction: easings.out,\n  },\n  stepInner: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    minHeight: 0,\n    minWidth: 0,\n  },\n  // Only while the step grows: a clip at rest would cut a focus ring drawn on its edge.\n  clipped: {\n    overflow: \"hidden\",\n  },\n  arriving: {\n    animationDelay: { default: durations.stagger, [media.reducedMotion]: \"0s\" },\n    animationDuration: durations.move,\n    animationFillMode: \"backwards\",\n    animationName: { default: arrive, [media.reducedMotion]: fadeIn },\n    animationTimingFunction: easings.out,\n  },\n  action: {\n    alignItems: \"center\",\n    backgroundColor: \"transparent\",\n    borderBlockEndWidth: 0,\n    borderBlockStartWidth: 0,\n    borderInlineEndWidth: 0,\n    borderInlineStartWidth: 0,\n    color: \"inherit\",\n    cursor: \"pointer\",\n    display: \"flex\",\n    fontFamily: \"inherit\",\n    maxWidth: \"100%\",\n    minWidth: 0,\n    paddingBlockEnd: 0,\n    paddingBlockStart: 0,\n    paddingInlineEnd: 0,\n    paddingInlineStart: 0,\n    textAlign: \"start\",\n    textDecorationLine: \"none\",\n    width: \"fit-content\",\n  },\n  line: {\n    alignItems: \"center\",\n    color: {\n      default: colors.textSecondary,\n      [media.hover]: { default: null, [hovered]: colors.textPrimary },\n      [pressed]: colors.textPrimary,\n    },\n    cursor: { default: null, [inAction]: \"pointer\" },\n    display: \"flex\",\n    gap: space.xs,\n    height: sizes.controlMd,\n    maxWidth: \"100%\",\n    minWidth: 0,\n    transitionDuration: durations.hover,\n    transitionProperty: \"color\",\n    transitionTimingFunction: \"ease\",\n    width: \"fit-content\",\n  },\n  pending: {\n    opacity: opacities.disabled,\n  },\n  marker: {\n    display: \"inline-grid\",\n    flexShrink: 0,\n    height: sizes.controlXs,\n    placeItems: \"center\",\n    transitionDuration: durations.hover,\n    transitionProperty: \"color\",\n    transitionTimingFunction: \"ease\",\n    width: sizes.controlXs,\n  },\n  markerError: {\n    color: colors.dangerText,\n  },\n  markLayer: {\n    display: \"flex\",\n    gridArea: \"1 / 1\",\n    transitionDuration: {\n      default: durations.hover,\n      // Keyboard focus never animates.\n      [stepFocused]: \"0s\",\n    },\n    transitionProperty: {\n      default: \"opacity, scale, filter\",\n      [media.reducedMotion]: \"opacity\",\n    },\n    transitionTimingFunction: easings.crossfade,\n  },\n  // The outgoing layer shrinks and softens, so the two never double-expose.\n  markIdle: {\n    filter: {\n      default: \"none\",\n      [stepFocused]: `blur(${motion.crossfadeTextBlur})`,\n      [media.hover]: {\n        default: \"none\",\n        [stepHovered]: `blur(${motion.crossfadeTextBlur})`,\n      },\n    },\n    opacity: {\n      default: 1,\n      [stepFocused]: 0,\n      [media.hover]: { default: 1, [stepHovered]: 0 },\n    },\n    scale: {\n      default: 1,\n      [stepFocused]: motion.popoverScale,\n      [media.hover]: { default: 1, [stepHovered]: motion.popoverScale },\n    },\n  },\n  markActive: {\n    filter: {\n      default: `blur(${motion.crossfadeTextBlur})`,\n      [stepFocused]: \"none\",\n      [media.hover]: {\n        default: `blur(${motion.crossfadeTextBlur})`,\n        [stepHovered]: \"none\",\n      },\n    },\n    opacity: {\n      default: 0,\n      [stepFocused]: 1,\n      [media.hover]: { default: 0, [stepHovered]: 1 },\n    },\n    scale: {\n      default: motion.popoverScale,\n      [stepFocused]: 1,\n      [media.hover]: { default: motion.popoverScale, [stepHovered]: 1 },\n    },\n  },\n  icon: {\n    alignItems: \"center\",\n    display: \"flex\",\n    flexShrink: 0,\n    height: sizes.iconSm,\n    justifyContent: \"center\",\n    marginInlineStart: {\n      default: null,\n      \":is([data-slot='trace-title'] ~ *)\": `calc(-1 * ${space.xxs})`,\n    },\n    width: sizes.iconSm,\n  },\n  title: {\n    flexShrink: 0,\n    maxWidth: \"100%\",\n    overflow: \"hidden\",\n    textOverflow: \"ellipsis\",\n    whiteSpace: \"nowrap\",\n  },\n  // Off without leaving the tree, so its text morphs as the step settles.\n  still: {\n    animationName: \"none\",\n    backgroundImage: \"none\",\n    WebkitTextFillColor: \"currentColor\",\n  },\n  subject: {\n    color: colors.textMuted,\n    minWidth: 0,\n    overflow: \"hidden\",\n    textOverflow: \"ellipsis\",\n    whiteSpace: \"nowrap\",\n  },\n  description: {\n    color: { default: colors.textMuted, [inTrigger]: \"inherit\" },\n    flexShrink: 0,\n    fontVariantNumeric: \"tabular-nums\",\n  },\n  descriptionError: {\n    color: colors.dangerText,\n  },\n  contentBody: {\n    color: colors.textSecondary,\n    minWidth: 0,\n    overflowWrap: \"break-word\",\n    paddingBlockEnd: space.xs,\n    // Keeps a ring drawn on the edge of what's inside within the clip.\n    paddingBlockStart: space.xxxs,\n    paddingInlineEnd: space.xxxs,\n    paddingInlineStart: `calc(${sizes.controlXs} + ${space.xs})`,\n  },\n  collapsible: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    minWidth: 0,\n    width: \"100%\",\n  },\n  chevron: {\n    display: \"flex\",\n    transform: {\n      default: \"rotate(-90deg)\",\n      [rtl]: \"rotate(90deg)\",\n      [open]: \"none\",\n    },\n    // On the panel's timing each way, so it lands as the panel does.\n    transitionDuration: {\n      default: durations.layoutExit,\n      [open]: {\n        default: durations.layout,\n        [instant]: \"0s\",\n        [media.reducedMotion]: \"0s\",\n      },\n      [instant]: \"0s\",\n      [media.reducedMotion]: \"0s\",\n    },\n    transitionProperty: \"transform\",\n    transitionTimingFunction: {\n      default: easings.out,\n      [open]: easings.layout,\n    },\n  },\n  // A window on the newest lines; older ones scroll up under a fade.\n  reasoning: {\n    alignItems: \"flex-start\",\n    color: colors.textMuted,\n    display: \"flex\",\n    flexDirection: \"column\",\n    justifyContent: \"flex-end\",\n    maxHeight: `calc(${lineHeights.textSm} * 4)`,\n    overflow: \"hidden\",\n  },\n  reasoningFull: {\n    maskImage: `linear-gradient(to bottom, transparent, black ${lineHeights.textSm})`,\n  },\n  substeps: {\n    display: \"flex\",\n    flexDirection: \"column\",\n  },\n  substep: {\n    alignItems: \"center\",\n    color: colors.textSecondary,\n    display: \"flex\",\n    gap: space.xs,\n    height: sizes.controlMd,\n    minWidth: 0,\n    opacity: {\n      default: null,\n      \":is([data-state='pending'])\": opacities.disabled,\n    },\n  },\n});\n\n/** True until the element's own entrance settles; cancelled counts, so a hidden panel won't replay it. */\nfunction useArrival<T extends HTMLElement>(carried = false) {\n  const live = use(Live);\n  const ref = useRef<T>(null);\n  // Read once, at mount: whether the Trace was already on screen when this arrived.\n  const [arrived, setArrived] = useState(() => carried || !live.current);\n  useEffect(() => {\n    const animations = ref.current?.getAnimations() ?? [];\n    const settle = async () => {\n      await Promise.allSettled(\n        animations.map(async (entrance) => await entrance.finished)\n      );\n      setArrived(true);\n    };\n    void settle();\n  }, []);\n  return [!arrived, ref] as const;\n}\n\n/** A link or trigger's styles, for one that wraps a `TraceLine`. */\nfunction traceActionStyles() {\n  return styles.action;\n}\n\nfunction Chevron() {\n  return (\n    <span {...stylex.props(styles.chevron)}>\n      <HugeiconsIcon\n        aria-hidden\n        icon={ArrowDown01Icon}\n        size={sizes.iconSm}\n        strokeWidth={Number(strokes.icon)}\n      />\n    </span>\n  );\n}\n\n/** The steps an agent takes, on one rail. */\nfunction Trace({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  const live = useRef(false);\n  useEffect(() => {\n    live.current = true;\n  }, []);\n  return (\n    <Live value={live}>\n      <div data-slot=\"trace\" {...props} {...stylex.props(styles.trace, sx)} />\n    </Live>\n  );\n}\n\n/**\n * One step: its line, and what opens under it. Without `state` it's done. Its content shows\n * unless `open` or `defaultOpen` is false; a `TraceStepTrigger` toggles it.\n */\nfunction TraceStep({\n  children,\n  defaultOpen = true,\n  onOpenChange,\n  open: shown,\n  state,\n  sx,\n}: {\n  children: ReactNode;\n  state?: TraceState;\n  sx?: Sx;\n} & Pick<CollapsibleProps, \"defaultOpen\" | \"onOpenChange\" | \"open\">) {\n  const [growing, stepRef] = useArrival<HTMLDivElement>();\n  const [arriving, innerRef] = useArrival<HTMLDivElement>();\n  const context = useMemo(\n    () => ({ arriving: growing || arriving, state }),\n    [arriving, growing, state]\n  );\n  return (\n    <Step value={context}>\n      <div\n        data-slot=\"trace-step\"\n        data-state={state}\n        ref={stepRef}\n        {...stylex.props(styles.step, growing && styles.growing, sx)}\n      >\n        <Collapsible\n          defaultOpen={defaultOpen}\n          onOpenChange={onOpenChange}\n          open={shown}\n          render={\n            <div\n              ref={innerRef}\n              {...stylex.props(\n                styles.stepInner,\n                growing && styles.clipped,\n                arriving && styles.arriving\n              )}\n            />\n          }\n        >\n          {children}\n        </Collapsible>\n      </div>\n    </Step>\n  );\n}\n\n/** A step's row. Wrap it in a trigger or a link to make it do something. */\nfunction TraceLine({ sx, ...props }: Styled<ComponentProps<\"span\">>) {\n  const { state } = use(Step);\n  return (\n    <span\n      data-slot=\"trace-line\"\n      {...props}\n      {...stylex.props(\n        [typeStyles.textSm, styles.line],\n        state === \"pending\" && styles.pending,\n        sx\n      )}\n    />\n  );\n}\n\n/**\n * A step's line that opens and shuts its content. Put the line's parts in it; its marker turns\n * into a chevron while it's pointed at. A step with `open` needs `onOpenChange` for it to toggle.\n */\nfunction TraceStepTrigger({\n  children,\n  sx,\n  ...props\n}: Omit<Styled<ComponentProps<typeof CollapsibleTrigger>>, \"render\">) {\n  return (\n    <CollapsibleTrigger\n      data-slot=\"trace-step-trigger\"\n      render={<button type=\"button\" {...stylex.props(styles.action, sx)} />}\n      {...props}\n    >\n      <InStepTrigger value>\n        <TraceLine>{children}</TraceLine>\n      </InStepTrigger>\n    </CollapsibleTrigger>\n  );\n}\n\n/** The step's icon on the rail, red when it failed. In a `TraceStepTrigger` it reveals a chevron. */\nfunction TraceMarker({ children, sx }: { children: ReactNode; sx?: Sx }) {\n  const { state } = use(Step);\n  const inStepTrigger = use(InStepTrigger);\n  return (\n    <span\n      aria-hidden\n      data-slot=\"trace-marker\"\n      {...stylex.props(\n        styles.marker,\n        state === \"error\" && styles.markerError,\n        sx\n      )}\n    >\n      {inStepTrigger ? (\n        <>\n          <span {...stylex.props(styles.markLayer, styles.markIdle)}>\n            {children}\n          </span>\n          <span {...stylex.props(styles.markLayer, styles.markActive)}>\n            <Chevron />\n          </span>\n        </>\n      ) : (\n        children\n      )}\n    </span>\n  );\n}\n\n/** An icon after the subject, like a link's arrow. */\nfunction TraceIcon({ sx, ...props }: Styled<ComponentProps<\"span\">>) {\n  return (\n    <span\n      aria-hidden\n      data-slot=\"trace-icon\"\n      {...props}\n      {...stylex.props(styles.icon, sx)}\n    />\n  );\n}\n\n/** What the step does, as a verb: \"Reading\", \"Read\". It shimmers while the step runs. */\nfunction TraceTitle({ children, sx }: { children: ReactNode; sx?: Sx }) {\n  const { state } = use(Step);\n  return (\n    <span data-slot=\"trace-title\" {...stylex.props(styles.title, sx)}>\n      <Shimmer sx={state === \"running\" ? undefined : styles.still}>\n        {children}\n      </Shimmer>\n    </span>\n  );\n}\n\n/** What the step acted on: \"Swedish orders\". */\nfunction TraceSubject({ sx, ...props }: Styled<ComponentProps<\"span\">>) {\n  return (\n    <span\n      data-slot=\"trace-subject\"\n      {...props}\n      {...stylex.props(styles.subject, sx)}\n    />\n  );\n}\n\n/** A count or a duration after the title; it doesn't truncate. */\nfunction TraceDescription({ sx, ...props }: Styled<ComponentProps<\"span\">>) {\n  const { state } = use(Step);\n  return (\n    <span\n      data-slot=\"trace-description\"\n      {...props}\n      {...stylex.props(\n        styles.description,\n        state === \"error\" && styles.descriptionError,\n        sx\n      )}\n    />\n  );\n}\n\n/** What opens under a step, lined up with its title. The step's `open` shows it. */\nfunction TraceStepContent({ children, sx }: { children: ReactNode; sx?: Sx }) {\n  return (\n    <CollapsibleContent data-slot=\"trace-step-content\" keepMounted>\n      <div {...stylex.props([typeStyles.textSm, styles.contentBody], sx)}>\n        {children}\n      </div>\n    </CollapsibleContent>\n  );\n}\n\n/** A run, or any list of steps, that opens and shuts under its trigger. */\nfunction TraceCollapsible({\n  sx,\n  ...props\n}: Styled<Omit<CollapsibleProps, \"open\">> &\n  (\n    | { open?: undefined }\n    | {\n        open: boolean;\n        onOpenChange: NonNullable<CollapsibleProps[\"onOpenChange\"]>;\n      }\n  )) {\n  return (\n    <Collapsible\n      data-slot=\"trace-collapsible\"\n      {...props}\n      sx={[styles.collapsible, sx]}\n    />\n  );\n}\n\n/** The run's row, with the chevron after it: put a `TraceTitle` and `TraceDescription` in it. */\nfunction TraceCollapsibleTrigger({\n  children,\n  sx,\n  ...props\n}: Omit<Styled<ComponentProps<typeof CollapsibleTrigger>>, \"render\">) {\n  return (\n    <CollapsibleTrigger\n      data-slot=\"trace-collapsible-trigger\"\n      render={<button type=\"button\" {...stylex.props(styles.action, sx)} />}\n      {...props}\n    >\n      <span {...stylex.props([typeStyles.textSm, styles.line])}>\n        {children}\n        <Chevron />\n      </span>\n    </CollapsibleTrigger>\n  );\n}\n\n/** The panel; it stays mounted while shut, so its steps don't arrive again on reopening. */\nfunction TraceCollapsibleContent({\n  sx,\n  ...props\n}: Styled<ComponentProps<typeof CollapsibleContent>>) {\n  return (\n    <CollapsibleContent\n      data-slot=\"trace-collapsible-content\"\n      keepMounted\n      sx={sx}\n      {...props}\n    />\n  );\n}\n\nconst windowLines = 4;\n\nlet measurer: CanvasRenderingContext2D | null = null;\n\n// Torph sets each letter in its own box, so pairs don't kern, and never breaks a line itself.\nconst linesOf = (text: string, width: number, style: CSSStyleDeclaration) => {\n  measurer ??= document.createElement(\"canvas\").getContext(\"2d\");\n  if (measurer === null) {\n    return [text];\n  }\n  measurer.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;\n  measurer.fontKerning = \"none\";\n  measurer.letterSpacing = style.letterSpacing;\n  const lines: string[] = [];\n  // A break in the text is a line of its own, so each paragraph wraps apart.\n  for (const paragraph of text.split(\"\\n\")) {\n    let line = \"\";\n    for (const word of paragraph.split(\" \")) {\n      const next = line === \"\" ? word : `${line} ${word}`;\n      if (line !== \"\" && measurer.measureText(next).width > width) {\n        lines.push(line);\n        line = word;\n      } else {\n        line = next;\n      }\n    }\n    lines.push(line);\n  }\n  return lines;\n};\n\n/**\n * The model's reasoning as it streams: a window on its newest lines, each new word morphing in\n * and the block gliding up a line as one wraps, the oldest fading under the top edge.\n */\nfunction TraceReasoning({ children, sx }: { children: string; sx?: Sx }) {\n  const ref = useRef<HTMLDivElement>(null);\n  const [box, setBox] = useState<{\n    style: CSSStyleDeclaration;\n    width: number;\n  }>();\n  useEffect(() => {\n    const measure = (element: Element, width: number) => {\n      setBox({ style: getComputedStyle(element), width });\n    };\n    const observer = new ResizeObserver(([entry]) => {\n      if (entry !== undefined) {\n        measure(entry.target, entry.contentRect.width);\n      }\n    });\n    if (ref.current !== null) {\n      observer.observe(ref.current);\n    }\n    // A web font landing changes the widths but not the box, so measure once more.\n    const remeasure = async () => {\n      await document.fonts.ready;\n      if (ref.current !== null) {\n        measure(ref.current, ref.current.getBoundingClientRect().width);\n      }\n    };\n    void remeasure();\n    return () => {\n      observer.disconnect();\n    };\n  }, []);\n  const lines = useMemo(\n    () =>\n      box === undefined ? [children] : linesOf(children, box.width, box.style),\n    [box, children]\n  );\n  return (\n    <div\n      data-slot=\"trace-reasoning\"\n      ref={ref}\n      {...stylex.props(\n        styles.reasoning,\n        lines.length > windowLines && styles.reasoningFull,\n        sx\n      )}\n    >\n      <SwapText>{lines.slice(-(windowLines + 1)).join(\"\\n\")}</SwapText>\n    </div>\n  );\n}\n\n/** The smaller steps one step runs, as rows under it. */\nfunction TraceSubsteps({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"trace-substeps\"\n      {...props}\n      {...stylex.props([typeStyles.textSm, styles.substeps], sx)}\n    />\n  );\n}\n\n/** One row of `TraceSubsteps`: a `TraceMarker`, a `TraceTitle` and what it acted on. */\nfunction TraceSubstep({\n  children,\n  state,\n  sx,\n}: {\n  children: ReactNode;\n  state?: TraceState;\n  sx?: Sx;\n}) {\n  const parent = use(Step);\n  const [arriving, ref] = useArrival<HTMLDivElement>(parent.arriving);\n  const context = useMemo(() => ({ arriving, state }), [arriving, state]);\n  return (\n    <Step value={context}>\n      <div\n        data-slot=\"trace-substep\"\n        data-state={state}\n        ref={ref}\n        {...stylex.props(styles.substep, arriving && styles.arriving, sx)}\n      >\n        {children}\n      </div>\n    </Step>\n  );\n}\n\nexport {\n  Trace,\n  traceActionStyles,\n  TraceCollapsible,\n  TraceCollapsibleContent,\n  TraceCollapsibleTrigger,\n  TraceDescription,\n  TraceIcon,\n  TraceLine,\n  TraceMarker,\n  TraceReasoning,\n  traceStates,\n  TraceStep,\n  TraceStepContent,\n  TraceStepTrigger,\n  TraceSubject,\n  TraceSubstep,\n  TraceSubsteps,\n  TraceTitle,\n};\nexport type { TraceState };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Usage: https://constella.aayush.cv/docs/components/trace/index.md",
  "type": "registry:ui"
}