{
  "name": "recently-viewed",
  "title": "RecentlyViewed",
  "description": "Horizontally scrollable rail of recently viewed products, hydrated from local activity state.",
  "type": "component",
  "registryDependencies": [
    "product-card",
    "cn"
  ],
  "files": [
    {
      "path": "recently-viewed.tsx",
      "content": "\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport { useActivityState } from \"./hooks/use-activity-state\";\nimport { prefetchProduct } from \"./hooks/use-product\";\nimport { speculate } from \"./utils/prefetch-policy\";\nimport { useOptionalCimplifyClient } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\ninterface ViewedProduct {\n  product_id: string;\n  product_name?: string;\n  category_id?: string;\n}\n\nexport interface RecentlyViewedClassNames {\n  root?: string;\n  item?: string;\n  empty?: string;\n  loading?: string;\n}\n\nexport interface RecentlyViewedProps {\n  /** Maximum number of viewed products to display. */\n  limit?: number;\n  /** Called when a viewed product is clicked. */\n  onProductClick?: (product: ViewedProduct) => void;\n  /** Custom product renderer. */\n  renderProduct?: (product: ViewedProduct) => React.ReactNode;\n  /** Text shown when no viewed products exist. */\n  emptyMessage?: string;\n  className?: string;\n  classNames?: RecentlyViewedClassNames;\n}\n\n/**\n * RecentlyViewed — displays products the user has recently viewed during their session.\n *\n * Extracts `state.activity.viewed_products` from the activity state.\n * Returns `null` when there are no viewed products.\n */\nexport function RecentlyViewed({\n  limit,\n  onProductClick,\n  renderProduct,\n  emptyMessage,\n  className,\n  classNames,\n}: RecentlyViewedProps): React.ReactElement | null {\n  const { state, isLoading } = useActivityState();\n\n  const context = useOptionalCimplifyClient();\n  const warmViewed = useCallback(\n    (productId: string) => {\n      if (!context || context.prefetch.mode === \"off\") return;\n      speculate(() => prefetchProduct(context.client, productId));\n    },\n    [context],\n  );\n\n  const rawViewed = state?.activity?.viewed_products ?? [];\n  const viewed = limit ? rawViewed.slice(0, limit) : rawViewed;\n\n  if (isLoading && viewed.length === 0) {\n    return (\n      <div\n        data-cimplify-recently-viewed\n        aria-busy=\"true\"\n        className={cn(className, classNames?.root, classNames?.loading)}\n      />\n    );\n  }\n\n  if (viewed.length === 0) {\n    return null;\n  }\n\n  return (\n    <div data-cimplify-recently-viewed className={cn(className, classNames?.root)}>\n      {viewed.map((product) => (\n        <button\n          key={product.product_id}\n          type=\"button\"\n          onClick={() => onProductClick?.(product)}\n          onPointerEnter={() => warmViewed(product.product_id)}\n          onFocus={() => warmViewed(product.product_id)}\n          data-cimplify-recently-viewed-item\n          className={classNames?.item}\n        >\n          {renderProduct ? (\n            renderProduct(product)\n          ) : (\n            <span>{product.product_name ?? product.product_id}</span>\n          )}\n        </button>\n      ))}\n    </div>\n  );\n}\n"
    }
  ]
}
