{
  "name": "session-message-banner",
  "title": "SessionMessageBanner",
  "description": "Top-of-page banner for session-scoped messages (promo nudges, abandoned cart prompts, low-stock alerts) with dismiss tracking.",
  "type": "component",
  "registryDependencies": [
    "cn"
  ],
  "files": [
    {
      "path": "session-message-banner.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\nimport type { SessionMessage } from \"../activity\";\nimport { useActivityState } from \"./hooks/use-activity-state\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface SessionMessageBannerClassNames {\n  root?: string;\n  message?: string;\n  text?: string;\n  dismissButton?: string;\n  empty?: string;\n}\n\nexport interface SessionMessageBannerProps {\n  /** Override messages (skips fetch). For SSR, pass pre-fetched messages. */\n  messages?: SessionMessage[];\n  /** Called when a message is dismissed. Receives the message code. */\n  onDismiss?: (code: string) => void;\n  /** Custom message renderer. */\n  renderMessage?: (message: SessionMessage) => React.ReactNode;\n  className?: string;\n  classNames?: SessionMessageBannerClassNames;\n}\n\n/**\n * SessionMessageBanner — renders activity-based session messages.\n *\n * Displays dismissible message cards with level-based styling (info, promotion, urgency, suggestion).\n * Returns `null` when there are no messages.\n */\nexport function SessionMessageBanner({\n  messages: messagesProp,\n  onDismiss,\n  renderMessage,\n  className,\n  classNames,\n}: SessionMessageBannerProps): React.ReactElement | null {\n  const { messages: fetched, isLoading, dismissMessage } = useActivityState({\n    enabled: messagesProp === undefined,\n  });\n\n  const messages = messagesProp ?? fetched;\n\n  const handleDismiss = async (code: string): Promise<void> => {\n    if (onDismiss) {\n      onDismiss(code);\n    } else {\n      await dismissMessage(code);\n    }\n  };\n\n  if (isLoading && messages.length === 0) {\n    return (\n      <div\n        data-cimplify-session-message-banner\n        aria-busy=\"true\"\n        className={cn(className, classNames?.root)}\n      />\n    );\n  }\n\n  if (messages.length === 0) {\n    return null;\n  }\n\n  return (\n    <div data-cimplify-session-message-banner className={cn(className, classNames?.root)}>\n      {messages.map((msg) => (\n        <div\n          key={msg.code}\n          data-cimplify-session-message\n          data-level={msg.level}\n          className={classNames?.message}\n        >\n          {renderMessage ? (\n            renderMessage(msg)\n          ) : (\n            <>\n              <span data-cimplify-session-message-text className={classNames?.text}>\n                {msg.text}\n              </span>\n              {msg.dismissible && (\n                <button\n                  type=\"button\"\n                  onClick={() => void handleDismiss(msg.code)}\n                  data-cimplify-session-message-dismiss\n                  className={classNames?.dismissButton}\n                  aria-label=\"Dismiss message\"\n                >\n                  &times;\n                </button>\n              )}\n            </>\n          )}\n        </div>\n      ))}\n    </div>\n  );\n}\n"
    }
  ]
}
