{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-H6TOSMGU.cjs","../src/components/platform/SoftwareInfo.tsx","../src/components/platform/SoftwareSourceBadge.tsx","../src/components/platform/CveLink.tsx","../src/components/platform/ToolBadge.tsx","../src/components/platform/ShellTypeBadge.tsx","../src/components/platform/ScriptInfoSection.tsx","../src/components/platform/ScriptArguments.tsx","../src/components/shared/onboarding/onboarding-step-card.tsx","../src/components/shared/onboarding/onboarding-walkthrough.tsx"],"names":["jsx","jsxs","cn"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-H6TOSMGU.cjs","sourcesContent":[null,"/**\n * SoftwareInfo Component\n *\n * Displays software name with optional vendor information below it.\n * Part of the ODS (OpenFrame Design System) platform components.\n *\n * @example\n * ```tsx\n * <SoftwareInfo name=\"Visual Studio Code\" vendor=\"Microsoft\" />\n * <SoftwareInfo name=\"Chrome\" /> // Without vendor\n * ```\n */\n\nimport type React from 'react';\nimport { cn } from '../../utils/cn';\n\nexport interface SoftwareInfoProps {\n  /** Software name (required) */\n  name: string;\n  /** Software vendor (optional) */\n  vendor?: string;\n  /** Software version (optional) */\n  version?: string;\n  /** Additional CSS classes */\n  className?: string;\n}\n\nexport const SoftwareInfo: React.FC<SoftwareInfoProps> = ({ name, vendor, version, className }) => {\n  return (\n    <div className={cn('flex flex-col gap-1', className)}>\n      <div className=\"flex items-center gap-2\">\n        <div className=\"text-ods-text-primary text-h4\">{name}</div>\n        {version && (\n          <div className=\"rounded bg-ods-bg-surface px-2 py-1 text-ods-text-secondary text-h6\">{version}</div>\n        )}\n      </div>\n      {vendor && <div className=\"text-ods-text-secondary text-h6\">{vendor}</div>}\n    </div>\n  );\n};\n\nSoftwareInfo.displayName = 'SoftwareInfo';\n","/**\n * SoftwareSourceBadge Component\n *\n * Displays a badge for software source types from Fleet MDM.\n * Supports all osquery software source types with appropriate styling.\n *\n * Fleet MDM Software Sources:\n * - apps: macOS Applications\n * - chrome_extensions: Chrome Browser Extensions\n * - firefox_addons: Firefox Browser Extensions\n * - safari_extensions: Safari Browser Extensions\n * - ie_extensions: Internet Explorer Extensions\n * - vscode_extensions: VS Code Extensions\n * - atom_packages: Atom Packages\n * - homebrew_packages: Homebrew Packages (macOS)\n * - npm_packages: NPM Packages\n * - python_packages: Python Packages\n * - apt_sources: APT Packages (Debian/Ubuntu)\n * - deb_packages: DEB Packages\n * - rpm_packages: RPM Packages (Red Hat/CentOS)\n * - yum_sources: YUM Packages\n * - portage_packages: Portage Packages (Gentoo)\n * - chocolatey_packages: Chocolatey Packages (Windows)\n * - programs: Windows Programs\n * - pkg_packages: PKG Packages\n *\n * @example\n * ```tsx\n * <SoftwareSourceBadge source=\"apps\" />\n * <SoftwareSourceBadge source=\"chrome_extensions\" />\n * <SoftwareSourceBadge source=\"python_packages\" />\n * ```\n */\n\nimport type React from 'react';\nimport { cn } from '../../utils/common';\nimport { PackageIcon, WindowsIcon, MacOSIcon, LinuxIcon } from '../icons';\n\nexport type SoftwareSource =\n  | 'apps'\n  | 'chrome_extensions'\n  | 'firefox_addons'\n  | 'safari_extensions'\n  | 'ie_extensions'\n  | 'vscode_extensions'\n  | 'atom_packages'\n  | 'homebrew_packages'\n  | 'npm_packages'\n  | 'python_packages'\n  | 'apt_sources'\n  | 'deb_packages'\n  | 'rpm_packages'\n  | 'yum_sources'\n  | 'portage_packages'\n  | 'chocolatey_packages'\n  | 'programs'\n  | 'pkg_packages';\n\nexport interface SoftwareSourceBadgeProps {\n  /** Software source type from Fleet MDM */\n  source: SoftwareSource;\n  /** Additional CSS classes */\n  className?: string;\n}\n\n/**\n * Maps source types to human-readable labels and icons\n */\nconst sourceConfig: Record<SoftwareSource, { label: string; icon: React.ReactNode }> = {\n  apps: { label: 'App', icon: <MacOSIcon className=\"h-4 w-4\" /> },\n  chrome_extensions: { label: 'Chrome', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  firefox_addons: { label: 'Firefox', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  safari_extensions: { label: 'Safari', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  ie_extensions: { label: 'IE', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  vscode_extensions: { label: 'VS Code', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  atom_packages: { label: 'Atom', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  homebrew_packages: { label: 'Homebrew', icon: <MacOSIcon className=\"h-4 w-4\" /> },\n  npm_packages: { label: 'NPM', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  python_packages: { label: 'Python', icon: <PackageIcon className=\"h-4 w-4\" /> },\n  apt_sources: { label: 'APT', icon: <LinuxIcon className=\"h-4 w-4\" /> },\n  deb_packages: { label: 'DEB', icon: <LinuxIcon className=\"h-4 w-4\" /> },\n  rpm_packages: { label: 'RPM', icon: <LinuxIcon className=\"h-4 w-4\" /> },\n  yum_sources: { label: 'YUM', icon: <LinuxIcon className=\"h-4 w-4\" /> },\n  portage_packages: { label: 'Portage', icon: <LinuxIcon className=\"h-4 w-4\" /> },\n  chocolatey_packages: { label: 'Chocolatey', icon: <WindowsIcon className=\"h-4 w-4\" /> },\n  programs: { label: 'Windows', icon: <WindowsIcon className=\"h-4 w-4\" /> },\n  pkg_packages: { label: 'PKG', icon: <PackageIcon className=\"h-4 w-4\" /> },\n};\n\nexport const SoftwareSourceBadge: React.FC<SoftwareSourceBadgeProps> = ({ source, className }) => {\n  const config = sourceConfig[source] || { label: source, icon: <PackageIcon className=\"h-4 w-4\" /> };\n\n  return (\n    <span className={cn('inline-flex items-center gap-2 text-ods-text-primary', className)}>\n      {config.icon}\n      {config.label}\n    </span>\n  );\n};\n\nSoftwareSourceBadge.displayName = 'SoftwareSourceBadge';\n","/**\n * CveLink Component\n *\n * Displays a CVE ID with an external link icon that links to the NIST NVD database.\n * Part of the ODS (OpenFrame Design System) platform components.\n *\n * @example\n * ```tsx\n * <CveLink cveId=\"CVE-2024-1234\" />\n * ```\n */\n\nimport type React from 'react';\nimport { cn } from '../../utils/cn';\nimport { CustomExternalLinkIcon } from '../icons';\n\nexport interface CveLinkProps {\n  /** CVE ID (e.g., \"CVE-2024-1234\") */\n  cveId: string;\n  /** Additional CSS classes */\n  className?: string;\n}\n\nexport const CveLink: React.FC<CveLinkProps> = ({ cveId, className }) => {\n  const nistUrl = `https://nvd.nist.gov/vuln/detail/${cveId}`;\n\n  return (\n    <a\n      href={nistUrl}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      className={cn(\n        'group inline-flex items-center gap-2 text-ods-text-primary transition-colors text-code hover:text-ods-accent',\n        className,\n      )}\n    >\n      <span>{cveId}</span>\n      <CustomExternalLinkIcon className=\"h-4 w-4 text-ods-text-secondary transition-colors group-hover:text-ods-accent\" />\n    </a>\n  );\n};\n\nCveLink.displayName = 'CveLink';\n","/**\n * ToolBadge Component\n *\n * Displays a tool type badge with icon for OpenFrame integrated tools.\n * Used in tables to show tool sources like Fleet MDM, MeshCentral, etc.\n *\n * @example\n * ```tsx\n * <ToolBadge toolType=\"OPENFRAME_RMM\" />\n * <ToolBadge toolType=\"FLEET\" />\n * <ToolBadge toolType=\"MESHCENTRAL\" />\n * ```\n */\n\nimport type React from 'react';\nimport type { ToolType } from '../../types/tool.types';\nimport { cn } from '../../utils/cn';\nimport { getToolLabel } from '../../utils/tool-utils';\nimport { ToolIcon } from '../tool-icon';\n\nexport type { ToolType } from '../../types/tool.types';\n\nexport interface ToolBadgeProps {\n  /** Tool type */\n  toolType: ToolType;\n  /** Additional CSS classes */\n  className?: string;\n  iconClassName?: string;\n}\n\nexport const ToolBadge: React.FC<ToolBadgeProps> = ({ toolType, className, iconClassName }) => {\n  const label = getToolLabel(toolType);\n\n  return (\n    <div className={cn('flex items-center gap-1 text-ods-text-secondary', className)}>\n      <ToolIcon toolType={toolType} className={iconClassName} size={16} />\n      <span className=\"text-ods-text-primary text-h4\">{label}</span>\n    </div>\n  );\n};\n\nToolBadge.displayName = 'ToolBadge';\n","/**\n * ShellTypeBadge Component\n *\n * Displays a badge for shell/script types with appropriate icon and label.\n * Supports all RMM shell types.\n */\n\nimport type React from 'react';\nimport { type ShellType, ShellTypeValues } from '../../types/shell.types';\nimport { cn } from '../../utils/cn';\nimport { getShellLabel } from '../../utils/shell-utils';\nimport { BashIcon, CmdIcon, DenoIcon, NushellIcon, ShellIcon } from '../icons';\nimport { PowershellLogoGreyIcon } from '../icons-v2-generated/brand-logos/powershell-logo-grey-icon';\nimport { PythonLogoIcon } from '../icons-v2-generated/brand-logos/python-logo-icon';\n\n/**\n * Shell icon configuration with typed mapping\n */\ninterface ShellIconConfig {\n  icon: React.ComponentType<{ size?: number; color?: string; className?: string }>;\n  props: { size: number } & ({ className: string } | { color: string });\n}\n\n/**\n * Typed mapping of shell types to their icon configurations\n * Using Record<ShellType, ...> ensures all shell types are covered\n */\nconst shellIconMap: Record<ShellType, ShellIconConfig> = {\n  [ShellTypeValues.POWERSHELL]: {\n    icon: PowershellLogoGreyIcon,\n    props: { size: 16, className: 'text-ods-text-secondary' },\n  },\n  [ShellTypeValues.CMD]: {\n    icon: CmdIcon,\n    props: { size: 16, color: 'var(--color-text-secondary)' },\n  },\n  [ShellTypeValues.BASH]: {\n    icon: BashIcon,\n    props: { size: 16, color: 'var(--color-text-secondary)' },\n  },\n  [ShellTypeValues.PYTHON]: {\n    icon: PythonLogoIcon,\n    props: { size: 16, className: 'text-ods-text-secondary' },\n  },\n  [ShellTypeValues.NUSHELL]: {\n    icon: NushellIcon,\n    props: { size: 16, color: 'var(--color-text-secondary)' },\n  },\n  [ShellTypeValues.DENO]: {\n    icon: DenoIcon,\n    props: { size: 16, color: 'var(--color-text-secondary)' },\n  },\n  [ShellTypeValues.SHELL]: {\n    icon: ShellIcon,\n    props: { size: 16, color: 'var(--color-text-secondary)' },\n  },\n} as const;\n\nconst defaultIconConfig: ShellIconConfig = {\n  icon: ShellIcon,\n  props: { size: 16, color: 'var(--color-text-secondary)' },\n};\n\nexport interface ShellTypeBadgeProps {\n  shellType: ShellType;\n  className?: string;\n  iconClassName?: string;\n}\n\nexport const ShellTypeBadge: React.FC<ShellTypeBadgeProps> = ({ shellType, className, iconClassName }) => {\n  const normalizedType = shellType?.toUpperCase() as ShellType;\n  const label = getShellLabel(normalizedType);\n  const { icon: IconComponent, props: iconProps } = shellIconMap[normalizedType] ?? defaultIconConfig;\n  const defaultIconClassName = 'className' in iconProps ? iconProps.className : undefined;\n\n  return (\n    <div className={cn('flex items-center gap-1', className)}>\n      <IconComponent size={16} className={cn(defaultIconClassName, iconClassName)} />\n      <span className=\"text-ods-text-primary text-h4\">{label}</span>\n    </div>\n  );\n};\n\nShellTypeBadge.displayName = 'ShellTypeBadge';\n","/**\n * ScriptInfoSection Component\n *\n * Displays script information in a card with header (title, description)\n * and detail cells (shell type, supported platforms, category, author).\n * Responsive layout: stacks cells on mobile, shows grid on desktop.\n */\n\nimport type React from 'react';\nimport Image from '../../embed-shims/next-image';\nimport { cn } from '../../utils/cn';\nimport { nameInitials } from '../../utils/format';\nimport { getOSLabel } from '../../utils/os-utils';\nimport { getShellLabel } from '../../utils/shell-utils';\n\n/**\n * Props for author avatar display\n */\nexport interface ScriptAuthor {\n  /** Author name */\n  name: string;\n  /** Author initials (used when no photo) */\n  initials?: string;\n  /** URL to author's photo */\n  photoUrl?: string;\n}\n\n/**\n * Props for ScriptInfoSection component\n */\nexport interface ScriptInfoSectionProps {\n  /** Script title/name */\n  headline: string;\n  /** Script description */\n  subheadline?: string;\n  /** Shell type (POWERSHELL, BASH, CMD, etc.) */\n  shellType?: string;\n  /** Array of supported platform strings (windows, darwin, linux, etc.) */\n  supportedPlatforms?: string[];\n  /** Script category */\n  category: string;\n  /** Author information */\n  author?: ScriptAuthor;\n  /** Additional CSS classes */\n  className?: string;\n}\n\n/**\n * Formats supported platforms array into a display string\n * @param platforms - Array of platform strings (e.g., ['windows', 'darwin', 'linux'])\n * @returns Formatted string (e.g., 'Windows, macOS, Linux')\n */\nfunction formatSupportedPlatforms(platforms?: string[]): string {\n  if (!platforms || platforms.length === 0) {\n    return 'All Platforms';\n  }\n  return platforms.map(platform => getOSLabel(platform)).join(', ');\n}\n\n/**\n * InfoCell - Single info cell with label and value\n */\ninterface InfoCellProps {\n  label: string;\n  value: string;\n  avatar?: ScriptAuthor;\n  className?: string;\n}\n\nfunction InfoCell({ label, value, avatar, className }: InfoCellProps) {\n  return (\n    <div className={cn('flex min-w-0 items-center gap-2', className)}>\n      {/* Avatar for author cell */}\n      {avatar && (\n        <div className=\"relative size-8 shrink-0 overflow-hidden rounded-full border border-ods-border bg-ods-bg\">\n          {avatar.photoUrl ? (\n            <Image src={avatar.photoUrl} alt={avatar.name} className=\"object-cover\" fill sizes=\"32px\" unoptimized />\n          ) : (\n            <div className=\"flex size-full items-center justify-center text-ods-text-secondary text-h6\">\n              {avatar.initials || nameInitials(avatar.name)}\n            </div>\n          )}\n        </div>\n      )}\n\n      {/* Text content */}\n      <div className=\"flex min-w-0 flex-col\">\n        <span className=\"truncate text-ods-text-primary text-h4\" title={value}>\n          {value}\n        </span>\n        <span className=\"truncate text-ods-text-secondary text-h6\">{label}</span>\n      </div>\n    </div>\n  );\n}\n\n/**\n * ScriptInfoSection - Displays script information in a structured card\n *\n * @example\n * ```tsx\n * <ScriptInfoSection\n *   title=\"System Backup Script\"\n *   description=\"Comprehensive Linux system backup script...\"\n *   shellType=\"BASH\"\n *   supportedPlatforms={['linux']}\n *   category=\"System Maintenance\"\n *   author={{ name: 'John Doe', photoUrl: '/avatars/john.jpg' }}\n * />\n * ```\n */\nexport const ScriptInfoSection: React.FC<ScriptInfoSectionProps> = ({\n  headline,\n  subheadline,\n  shellType,\n  supportedPlatforms,\n  category,\n  author,\n  className,\n}) => {\n  const shellLabel = getShellLabel(shellType);\n  const platformsLabel = formatSupportedPlatforms(supportedPlatforms);\n\n  return (\n    <div className={cn('overflow-hidden rounded-[6px] border border-ods-border bg-ods-card', className)}>\n      {/* Header row with title and description */}\n      <div className=\"border-b border-ods-border p-4\">\n        <div className=\"flex flex-col gap-1\">\n          <h3 className=\"text-ods-text-primary text-h4\">{headline}</h3>\n          {subheadline && <p className=\"break-words text-ods-text-secondary text-h6\">{subheadline}</p>}\n        </div>\n      </div>\n\n      {/* Details section with info cells\n          - Mobile/Tablet (<lg): 2x2 grid with divider between rows\n          - Desktop (lg+): 4 columns in single row\n      */}\n      {/* First row: Shell Type, Supported Platforms */}\n      <div className=\"grid grid-cols-2 gap-4 border-b border-ods-border px-4 py-4 lg:grid-cols-4 lg:border-b-0\">\n        <InfoCell label=\"Shell Type\" value={shellLabel} />\n        <InfoCell label=\"Supported Platforms\" value={platformsLabel} />\n        {/* Desktop only: Category and Author in same row */}\n        <InfoCell label=\"Category\" value={category} className=\"hidden lg:flex\" />\n        {author && <InfoCell label=\"Added by\" value={author.name} avatar={author} className=\"hidden lg:flex\" />}\n      </div>\n      {/* Second row (mobile/tablet only): Category, Author */}\n      <div className=\"grid grid-cols-2 gap-4 px-4 py-4 lg:hidden\">\n        <InfoCell label=\"Category\" value={category} />\n        {author && <InfoCell label=\"Added by\" value={author.name} avatar={author} />}\n      </div>\n    </div>\n  );\n};\n\nScriptInfoSection.displayName = 'ScriptInfoSection';\n","/**\n * ScriptArguments Component\n *\n * Displays and manages script arguments as key-value pairs.\n * Each argument has a name (key) and value input field with delete button.\n * Includes an \"Add Script Argument\" button to add new entries.\n */\n\nimport { Trash2 } from 'lucide-react';\nimport type React from 'react';\nimport { cn } from '../../utils/cn';\nimport { PlusCircleIcon } from '../icons-v2-generated';\nimport { Button } from '../ui/button';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\n/**\n * Single script argument with key and value\n */\nexport interface ScriptArgument {\n  /** Unique identifier for the argument */\n  id: string;\n  /** Argument name/key */\n  key: string;\n  /** Argument value (empty string for flag-type arguments) */\n  value: string;\n}\n\n/**\n * Props for ScriptArguments component\n */\nexport interface ScriptArgumentsProps {\n  /** Array of script arguments */\n  arguments: ScriptArgument[];\n  /** Callback when arguments change */\n  onArgumentsChange?: (args: ScriptArgument[]) => void;\n  /** Placeholder for key input */\n  keyPlaceholder?: string;\n  /** Placeholder for value input */\n  valuePlaceholder?: string;\n  /** Label for the add button */\n  addButtonLabel?: string;\n  /** Whether the component is disabled */\n  disabled?: boolean;\n  /** Additional CSS classes */\n  className?: string;\n  /** Label for the title input */\n  titleLabel: string;\n}\n\n/**\n * ScriptArguments - Displays and manages script arguments\n *\n * @example\n * ```tsx\n * <ScriptArguments\n *   arguments={[\n *     { id: '1', key: 'port', value: '3000' },\n *     { id: '2', key: 'verbose', value: '' },\n *   ]}\n *   onArgumentsChange={(args) => setArguments(args)}\n * />\n * ```\n */\nexport const ScriptArguments: React.FC<ScriptArgumentsProps> = ({\n  arguments: args,\n  onArgumentsChange,\n  keyPlaceholder = 'Enter Key',\n  valuePlaceholder = 'Enter Value (empty=flag)',\n  addButtonLabel = 'Add Script Argument',\n  disabled = false,\n  className,\n  titleLabel,\n}) => {\n  const handleKeyChange = (id: string, newKey: string) => {\n    if (!onArgumentsChange) return;\n    const updated = args.map(arg => (arg.id === id ? { ...arg, key: newKey } : arg));\n    onArgumentsChange(updated);\n  };\n\n  const handleValueChange = (id: string, newValue: string) => {\n    if (!onArgumentsChange) return;\n    const updated = args.map(arg => (arg.id === id ? { ...arg, value: newValue } : arg));\n    onArgumentsChange(updated);\n  };\n\n  const handleDelete = (id: string) => {\n    if (!onArgumentsChange) return;\n    const updated = args.filter(arg => arg.id !== id);\n    onArgumentsChange(updated);\n  };\n\n  const handleAdd = () => {\n    if (!onArgumentsChange) return;\n    const newArg: ScriptArgument = {\n      id: crypto.randomUUID(),\n      key: '',\n      value: '',\n    };\n    onArgumentsChange([...args, newArg]);\n  };\n\n  const isFirstRow = (index: number) => index === 0;\n\n  return (\n    <div className={cn('flex flex-col gap-2', className)}>\n      {args.map((arg, index) => (\n        <div key={arg.id} className=\"flex w-full items-end gap-2\">\n          {/* Key input - with label only on first row */}\n          <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n            {isFirstRow(index) && <Label spacing=\"tight\">{titleLabel}</Label>}\n            <Input\n              value={arg.key}\n              onChange={e => handleKeyChange(arg.id, e.target.value)}\n              placeholder={keyPlaceholder}\n              disabled={disabled}\n            />\n          </div>\n\n          {/* Value input with delete button */}\n          {/* Value input with delete button */}\n          <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n            <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n              <Input\n                value={arg.value}\n                onChange={e => handleValueChange(arg.id, e.target.value)}\n                placeholder={valuePlaceholder}\n                disabled={disabled}\n                className={cn(!arg.value && 'placeholder:text-ods-text-muted')}\n              />\n            </div>\n            <Button\n              type=\"button\"\n              variant=\"outline\"\n              size=\"icon\"\n              onClick={() => handleDelete(arg.id)}\n              disabled={disabled}\n              aria-label=\"Delete argument\"\n              leftIcon={<Trash2 className=\"size-4 md:size-6\" color=\"var(--color-error)\" />}\n            />\n          </div>\n        </div>\n      ))}\n\n      {/* Add button - aligned left */}\n      <div className=\"flex justify-start\">\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          size=\"small\"\n          onClick={handleAdd}\n          disabled={disabled}\n          className=\"self-start\"\n          leftIcon={<PlusCircleIcon className=\"text-ods-text-secondary\" />}\n        >\n          {addButtonLabel}\n        </Button>\n      </div>\n    </div>\n  );\n};\n\nScriptArguments.displayName = 'ScriptArguments';\n","'use client';\n\nimport React from 'react';\nimport { cn } from '../../../utils/cn';\nimport { Button } from '../../ui/button';\nimport { InteractiveCard } from '../../ui/interactive-card';\nimport { StatusBadge } from '../../ui/status-badge';\n\nexport interface OnboardingStepCardProps {\n  step: {\n    id: string;\n    title: string;\n    description: string;\n    actionIcon: (color?: string) => React.ReactNode;\n    actionText: string;\n    completedText: string;\n    onAction: () => void | Promise<void>;\n    onSkip?: () => void;\n  };\n  isCompleted: boolean;\n  isSkipped: boolean;\n  isCheckingCompletion: boolean;\n  onAction: () => void | Promise<void>;\n  onSkip: () => void;\n  className?: string;\n}\n\nexport function OnboardingStepCard({\n  step,\n  isCompleted,\n  isSkipped,\n  isCheckingCompletion,\n  onAction,\n  onSkip,\n  className,\n}: OnboardingStepCardProps) {\n  const [isProcessing, setIsProcessing] = React.useState(false);\n\n  const handleAction = async (e: React.MouseEvent) => {\n    e.stopPropagation();\n    setIsProcessing(true);\n    try {\n      await onAction();\n    } finally {\n      setIsProcessing(false);\n    }\n  };\n\n  const handleSkip = (e: React.MouseEvent) => {\n    e.stopPropagation();\n    onSkip();\n  };\n\n  return (\n    <InteractiveCard\n      clickable={false}\n      className={cn(\n        'rounded-[6px] border border-ods-border bg-ods-card',\n        'flex flex-col md:flex-row',\n        'min-h-[80px] md:h-[80px]',\n        'items-start md:items-center',\n        'gap-3 md:gap-4',\n        'px-4 py-4 md:py-0',\n        className,\n      )}\n    >\n      {/* Left column - content */}\n      <div className=\"flex w-full min-w-0 flex-1 flex-col justify-center gap-1 md:w-auto\">\n        <h3 className=\"truncate text-ods-text-primary text-h4\" title={step.title}>\n          {step.title}\n        </h3>\n        <p className=\"h-[20px] truncate text-ods-text-secondary text-h6\" title={step.description}>\n          {step.description}\n        </p>\n      </div>\n\n      {/* Right column - action buttons, completed badge, or skipped badge */}\n      <div\n        className=\"flex w-full shrink-0 items-center justify-start gap-2 md:w-auto md:justify-end\"\n        onClick={e => e.stopPropagation()}\n      >\n        {isCheckingCompletion ? (\n          <>\n            <div className=\"h-[32px] w-[100px] animate-pulse rounded-[6px] bg-ods-border\" />\n            <div className=\"h-[32px] w-[120px] animate-pulse rounded-[6px] bg-ods-border\" />\n          </>\n        ) : isCompleted ? (\n          <>\n            <StatusBadge text=\"COMPLETED\" variant=\"card\" colorScheme=\"success\" />\n            <Button\n              variant=\"outline\"\n              onClick={handleAction}\n              disabled={isProcessing}\n              leftIcon={step.actionIcon('white')}\n            >\n              {step.completedText}\n            </Button>\n          </>\n        ) : isSkipped ? (\n          <>\n            <StatusBadge text=\"SKIPPED\" variant=\"card\" colorScheme=\"default\" />\n          </>\n        ) : (\n          <>\n            <Button variant=\"outline\" onClick={handleSkip}>\n              Skip Step\n            </Button>\n            <Button variant=\"accent\" onClick={handleAction} disabled={isProcessing} leftIcon={step.actionIcon('black')}>\n              {step.actionText}\n            </Button>\n          </>\n        )}\n      </div>\n    </InteractiveCard>\n  );\n}\n","'use client';\n\nimport React, { useRef, useCallback } from 'react';\nimport { useOnboardingState, type OnboardingStepConfig } from '../../../hooks/ui/use-onboarding-state';\nimport { cn } from '../../../utils/cn';\nimport { SECTION_HEADING_CLASS } from '../../layout/page-heading';\nimport { Button } from '../../ui/button';\nimport { OnboardingStepCard } from './onboarding-step-card';\n\nexport interface OnboardingWalkthroughProps {\n  steps: OnboardingStepConfig[];\n  onDismiss?: () => void;\n  storageKey?: string;\n  className?: string;\n  spacing?: string;\n  /**\n   * Completion status from hook-based checks (e.g., useOnboardingCompletion)\n   * Object mapping step IDs to boolean completion status\n   */\n  completionStatus?: Record<string, boolean>;\n  /**\n   * Whether completion checks are still loading\n   */\n  isLoadingCompletion?: boolean;\n}\n\nexport function OnboardingWalkthrough({\n  steps,\n  onDismiss,\n  storageKey = 'openframe-onboarding-state',\n  className,\n  spacing = 'space-y-4',\n  completionStatus,\n  isLoadingCompletion = false,\n}: OnboardingWalkthroughProps) {\n  const {\n    state,\n    markComplete,\n    markSkipped,\n    dismissOnboarding,\n    isStepComplete,\n    isStepSkipped,\n    allStepsComplete,\n    markMultipleComplete,\n  } = useOnboardingState(storageKey);\n\n  // Refs to prevent race conditions - ALL refs must be declared before any conditional returns\n  const hasAutoMarkedRef = useRef(false);\n  const autoMarkingInProgressRef = useRef(false);\n  const lastCompletionStatusRef = useRef<string | null>(null);\n  // Ref to track in-flight action handlers - moved here to be before conditional return\n  const actionInProgressRef = useRef<Set<string>>(new Set());\n\n  // Sync completion status from hooks to localStorage\n  // Uses refs to prevent duplicate calls and race conditions\n  React.useEffect(() => {\n    // Skip if still loading\n    if (isLoadingCompletion) {\n      hasAutoMarkedRef.current = false; // Reset when loading starts\n      return;\n    }\n\n    // Skip if no completion status\n    if (!completionStatus) return;\n\n    // Create a stable key from completion status to detect actual changes\n    const statusKey = JSON.stringify(completionStatus);\n\n    // Skip if we've already processed this exact status\n    if (lastCompletionStatusRef.current === statusKey) return;\n\n    // Skip if auto-marking is currently in progress\n    if (autoMarkingInProgressRef.current) return;\n\n    // Find steps that need to be marked complete\n    // Read directly from state to avoid stale closure issues\n    const completedStepIds = Object.entries(completionStatus)\n      .filter(([stepId, isComplete]) => {\n        if (!isComplete) return false;\n        // Check against current state, not the callback\n        return !state.completedSteps.includes(stepId);\n      })\n      .map(([stepId]) => stepId);\n\n    if (completedStepIds.length > 0) {\n      // Mark as in progress to prevent concurrent calls\n      autoMarkingInProgressRef.current = true;\n\n      console.log('📊 Auto-marking steps as complete from hook data:', completedStepIds);\n      markMultipleComplete(completedStepIds);\n\n      // Update the last processed status key\n      lastCompletionStatusRef.current = statusKey;\n\n      // Reset the in-progress flag after a short delay\n      // This prevents rapid successive calls while allowing future updates\n      setTimeout(() => {\n        autoMarkingInProgressRef.current = false;\n      }, 100);\n    } else {\n      // Even if no steps to mark, update the status key to prevent re-processing\n      lastCompletionStatusRef.current = statusKey;\n    }\n  }, [completionStatus, isLoadingCompletion, state.completedSteps, markMultipleComplete]);\n\n  // useCallback must be called unconditionally (before any early returns)\n  const handleStepAction = useCallback(\n    async (step: OnboardingStepConfig) => {\n      // Prevent duplicate action handling for the same step\n      if (actionInProgressRef.current.has(step.id)) {\n        console.log(`⏳ Action already in progress for \"${step.id}\", skipping`);\n        return;\n      }\n\n      // Mark as in progress\n      actionInProgressRef.current.add(step.id);\n\n      try {\n        await step.onAction();\n\n        // Skip if already completed (could have been auto-marked while action was running)\n        if (state.completedSteps.includes(step.id)) {\n          console.log(`✓ Step \"${step.id}\" already completed, skipping mark`);\n          return;\n        }\n\n        // Auto-mark Knowledge Base as complete (no verification needed)\n        if (step.id === 'knowledge-base') {\n          console.log(`🎯 Auto-completing Knowledge Base`);\n          markComplete(step.id);\n          return;\n        }\n\n        // For other steps, re-check completion after action\n        if (step.checkComplete) {\n          try {\n            const isComplete = await step.checkComplete();\n            console.log(`🔍 Post-action check for \"${step.id}\": isComplete=${isComplete}`);\n\n            // Double-check it wasn't completed while we were checking\n            if (isComplete && !state.completedSteps.includes(step.id)) {\n              markComplete(step.id);\n            }\n          } catch (error) {\n            console.error(`Error checking completion for \"${step.id}\":`, error);\n          }\n        }\n      } finally {\n        // Remove from in-progress set\n        actionInProgressRef.current.delete(step.id);\n      }\n    },\n    [state.completedSteps, markComplete],\n  );\n\n  const handleStepSkip = (step: OnboardingStepConfig) => {\n    step.onSkip?.();\n    markSkipped(step.id);\n  };\n\n  const handleDismiss = () => {\n    dismissOnboarding();\n    onDismiss?.();\n  };\n\n  // ALL hooks have been called above - now safe to do conditional returns\n  // Don't render if dismissed\n  if (state.dismissed) {\n    return null;\n  }\n\n  const isAllComplete = allStepsComplete(steps);\n\n  return (\n    <div className={cn('w-full space-y-4', className)}>\n      {/* Header - responsive: stacks on mobile */}\n      <div className=\"flex flex-col items-start justify-between gap-3 md:flex-row md:items-center md:gap-0\">\n        <h2 className={SECTION_HEADING_CLASS}>Get Started</h2>\n\n        {isLoadingCompletion ? (\n          <div className=\"h-[48px] w-full animate-pulse rounded-[6px] bg-ods-border md:w-[160px]\" />\n        ) : isAllComplete ? (\n          <Button variant=\"accent\" onClick={handleDismiss} className=\"w-full md:w-auto\">\n            Close Onboarding\n          </Button>\n        ) : (\n          <Button variant=\"outline\" onClick={handleDismiss} className=\"w-full md:w-auto\">\n            Skip Onboarding\n          </Button>\n        )}\n      </div>\n\n      {/* Step cards - no wrapper, direct rendering */}\n      <div className={cn(spacing)}>\n        {steps.map(step => (\n          <OnboardingStepCard\n            key={step.id}\n            step={step}\n            isCompleted={isStepComplete(step.id)}\n            isSkipped={isStepSkipped(step.id)}\n            isCheckingCompletion={isLoadingCompletion}\n            onAction={() => handleStepAction(step)}\n            onSkip={() => handleStepSkip(step)}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n"]}