{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-N6CULJYR.cjs","../src/utils/content-ref-groups.ts","../src/utils/suggestion-url.ts","../src/components/pagination.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-N6CULJYR.cjs","sourcesContent":[null,"/**\n * Content Ref Group Configuration\n *\n * Single source of truth for grouping content_refs by type. Used by both:\n *  - components/related-content (detail page rail — lives in this lib)\n *  - the hub's lib/utils/investor-email-utils.ts (email builder, via the\n *    hub's re-export shim at lib/config/content-ref-groups.ts)\n *\n * Lives in utils/ (server-safe tsup block) so hub server-side code can\n * import it.\n *\n * Each entry carries the metadata RelatedContentSection needs to render the\n * group WITHOUT re-implementing per-type logic locally:\n *   - `label`: section heading.\n *   - `order`: source-stable ordering (lowest first).\n *   - `layout`: 'list' (one-per-row, full-width cards) or 'grid' (responsive\n *     column grid).\n *   - `gridSize`: card-size variant passed to the per-type card dispatch.\n *     `'lg'` matches the canonical large-card variant openframe + flamingo\n *     apps already use; `'default'` is the legacy denser variant. New types\n *     should pick `'lg'` whenever the card has a large variant.\n *\n * Adding a new content type = one entry here. RelatedContentSection picks up\n * the new group automatically (no hardcoded set to update). NOTE: the hub's\n * related-content SUGGESTION candidate list is DERIVED from these keys (minus\n * an explicit exclusion knob) — a new entry here becomes a suggestion\n * candidate iff it also resolves through the hub's TYPE_TO_ENTITY → RAG\n * config chain (unresolvable entries are dropped loudly at module load).\n */\n\nexport type ContentRefLayout = 'list' | 'grid';\n\n/** Subset of `EntityCardSize` (entity-card dispatch) appropriate for\n *  grid/list rendering in RelatedContentSection. Duplicated as a literal\n *  union here to avoid a config→component import cycle. Kept in lockstep\n *  with `EntityCardSize`; widen here when a new size is added there. */\nexport type ContentRefGridSize = 'lg' | 'default' | 'sm';\n\nexport interface ContentRefGroupConfig {\n  label: string;\n  order: number;\n  layout: ContentRefLayout;\n  gridSize: ContentRefGridSize;\n}\n\nexport const CONTENT_REF_GROUPS: Record<string, ContentRefGroupConfig> = {\n  investor_update: { label: 'Investor Updates', order: 1, layout: 'grid', gridSize: 'default' },\n  product_release: { label: 'Product Releases', order: 2, layout: 'list', gridSize: 'lg' },\n  podcast: { label: 'Podcasts', order: 3, layout: 'list', gridSize: 'default' },\n  webinar: { label: 'Webinars', order: 4, layout: 'list', gridSize: 'default' },\n  case_study: { label: 'Case Studies', order: 5, layout: 'grid', gridSize: 'default' },\n  event: { label: 'Events', order: 6, layout: 'list', gridSize: 'default' },\n  blog_post_existing: { label: 'Blog Posts', order: 7, layout: 'grid', gridSize: 'default' },\n  customer_interview: { label: 'Customer Interviews', order: 8, layout: 'grid', gridSize: 'default' },\n  onboarding_guide: { label: 'Onboarding Guides', order: 9, layout: 'list', gridSize: 'default' },\n  what_i_shipped: { label: 'What I Shipped', order: 10, layout: 'grid', gridSize: 'default' },\n  how_i_work: { label: 'How I Work', order: 11, layout: 'grid', gridSize: 'default' },\n};\n\n/** Human-readable label for a content_ref `type`. Returns null when the type\n *  isn't registered — caller decides the fallback. Use `getContentRefLabelOrTitleCase`\n *  to get a consistent title-cased fallback across all surfaces. */\nexport function getContentRefLabel(type: string): string | null {\n  return CONTENT_REF_GROUPS[type]?.label ?? null;\n}\n\n/** Resolved label with a single shared fallback shape — title-cased version\n *  of the raw type string for any unregistered type. Both RelatedContentSection\n *  AND the investor-email builder consume this so cross-surface label rendering\n *  for unregistered types is identical (no drift between \"podcast guest\" and\n *  \"Podcast Guest\"). */\nexport function getContentRefLabelOrTitleCase(type: string): string {\n  return CONTENT_REF_GROUPS[type]?.label ?? type.replace(/_/g, ' ').replace(/\\b\\w/g, ch => ch.toUpperCase());\n}\n\n/** Sort a set of present content_ref types into the canonical group order\n *  (registered types first in CONTENT_REF_GROUPS `order`, then unregistered\n *  types appended in insertion order). Used by RelatedContentSection AND the\n *  investor-email builder so cross-surface ordering stays identical. */\nexport function orderContentRefTypes(present: Iterable<string>): string[] {\n  const presentSet = new Set(present);\n  const registeredInOrder = Object.entries(CONTENT_REF_GROUPS)\n    .sort(([, a], [, b]) => a.order - b.order)\n    .map(([type]) => type)\n    .filter(type => presentSet.has(type));\n  const unregistered = [...presentSet].filter(type => !CONTENT_REF_GROUPS[type]);\n  return [...registeredInOrder, ...unregistered];\n}\n","/**\n * Shared fetch-URL composer for the two suggestion sections (FaqSection +\n * RelatedContentSection) — ONE owner of the entityType/entityId/count query\n * shape so the sections can never drift. Pure + server-safe.\n *\n * Byte-parity contract with the original `buildFaqsUrl`: when only\n * entityType/entityId are set, the param order is `entityType`, `entityId`;\n * with nothing set, the bare `path` is returned (no `?`).\n */\nexport interface SuggestionUrlOptions {\n  /** Fetch-URL prefix for third-party embeds / reverse proxies\n   *  ('' = same-origin relative). */\n  apiBaseUrl?: string;\n  /** Both required together for entity scope; partial → bare path. */\n  entityType?: string;\n  entityId?: number | string;\n  /** Maps to the server's `count` param. Undefined → param not sent\n   *  (server default applies). */\n  count?: number;\n  /** Extra query params appended after the shared trio (e.g. the\n   *  related-content `excludeTypes` deny-list). Empty/undefined values\n   *  are skipped. */\n  extraParams?: Record<string, string | undefined>;\n}\n\nexport function buildSuggestionUrl(path: string, opts: SuggestionUrlOptions = {}): string {\n  const qs = new URLSearchParams();\n  const { entityType, entityId } = opts;\n  if (entityType && entityId !== undefined && entityId !== null && entityId !== '') {\n    qs.set('entityType', entityType);\n    qs.set('entityId', String(entityId));\n  }\n  if (opts.count !== undefined) qs.set('count', String(opts.count));\n  for (const [key, value] of Object.entries(opts.extraParams ?? {})) {\n    if (value !== undefined && value !== '') qs.set(key, value);\n  }\n  const query = qs.toString();\n  return `${opts.apiBaseUrl ?? ''}${path}${query ? `?${query}` : ''}`;\n}\n","'use client';\n\nimport { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';\nimport { type ButtonHTMLAttributes, type HTMLAttributes, forwardRef } from 'react';\nimport { cn } from '../utils/cn';\nimport { Button } from './ui/button';\n\ninterface PaginationProps {\n  currentPage: number;\n  totalPages: number;\n  onPageChange: (page: number) => void;\n  className?: string;\n}\n\nconst Pagination = ({ currentPage, totalPages, onPageChange, className }: PaginationProps) => {\n  // Generate page numbers to display\n  const getPageNumbers = () => {\n    const pages = [];\n    const maxPagesToShow = 5;\n\n    if (totalPages <= maxPagesToShow) {\n      // Show all pages if there are fewer than maxPagesToShow\n      for (let i = 1; i <= totalPages; i++) {\n        pages.push(i);\n      }\n    } else {\n      // Always show first page\n      pages.push(1);\n\n      // Calculate start and end of page range\n      let start = Math.max(2, currentPage - 1);\n      let end = Math.min(totalPages - 1, currentPage + 1);\n\n      // Adjust if at the beginning or end\n      if (currentPage <= 2) {\n        end = Math.min(totalPages - 1, 4);\n      } else if (currentPage >= totalPages - 1) {\n        start = Math.max(2, totalPages - 3);\n      }\n\n      // Add ellipsis if needed\n      if (start > 2) {\n        pages.push(-1); // -1 represents ellipsis\n      }\n\n      // Add middle pages\n      for (let i = start; i <= end; i++) {\n        pages.push(i);\n      }\n\n      // Add ellipsis if needed\n      if (end < totalPages - 1) {\n        pages.push(-2); // -2 represents ellipsis\n      }\n\n      // Always show last page\n      if (totalPages > 1) {\n        pages.push(totalPages);\n      }\n    }\n\n    return pages;\n  };\n\n  const pageNumbers = getPageNumbers();\n\n  return (\n    <nav role=\"navigation\" aria-label=\"pagination\" className={cn('mx-auto flex w-full justify-center', className)}>\n      <ul className=\"flex flex-row items-center gap-1\">\n        <li>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            onClick={() => onPageChange(currentPage - 1)}\n            disabled={currentPage === 1}\n            aria-label=\"Go to previous page\"\n            className=\"!h-10 !w-10 md:!h-10 md:!w-10\"\n          >\n            <ChevronLeft className=\"h-4 w-4\" />\n          </Button>\n        </li>\n\n        {pageNumbers.map((page, index) => {\n          if (page < 0) {\n            // Render ellipsis\n            return (\n              <li key={`ellipsis-${index}`}>\n                <span className=\"flex h-9 w-9 items-center justify-center text-muted-foreground text-h6\">\n                  <MoreHorizontal className=\"h-4 w-4\" />\n                  <span className=\"sr-only\">More pages</span>\n                </span>\n              </li>\n            );\n          }\n\n          return (\n            <li key={page}>\n              <Button\n                variant={currentPage === page ? 'accent' : 'outline'}\n                size=\"icon\"\n                onClick={() => onPageChange(page)}\n                className=\"!h-9 !w-9 md:!h-9 md:!w-9\"\n                aria-label={`Page ${page}`}\n                aria-current={currentPage === page ? 'page' : undefined}\n              >\n                {page}\n              </Button>\n            </li>\n          );\n        })}\n\n        <li>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            onClick={() => onPageChange(currentPage + 1)}\n            disabled={currentPage === totalPages}\n            aria-label=\"Go to next page\"\n            className=\"!h-10 !w-10 md:!h-10 md:!w-10\"\n          >\n            <ChevronRight className=\"h-4 w-4\" />\n            <span className=\"sr-only\">Next page</span>\n          </Button>\n        </li>\n      </ul>\n    </nav>\n  );\n};\n\nconst PaginationContent = forwardRef<HTMLUListElement, HTMLAttributes<HTMLUListElement>>(\n  ({ className, ...props }, ref) => {\n    return <ul ref={ref} className={cn('flex items-center justify-center', className)} {...props} />;\n  },\n);\nPaginationContent.displayName = 'PaginationContent';\n\nconst PaginationItem = forwardRef<HTMLLIElement, HTMLAttributes<HTMLLIElement>>(({ className, ...props }, ref) => {\n  return <li ref={ref} className={cn('', className)} {...props} />;\n});\nPaginationItem.displayName = 'PaginationItem';\n\nconst PaginationLink = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(\n  ({ className, ...props }, ref) => {\n    return (\n      <button\n        ref={ref}\n        className={cn(\n          'inline-flex h-10 w-10 items-center justify-center rounded-md border border-input bg-background transition-colors text-h6 hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n          className,\n        )}\n        {...props}\n      />\n    );\n  },\n);\nPaginationLink.displayName = 'PaginationLink';\n\nconst PaginationEllipsis = forwardRef<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>>(\n  ({ className, ...props }, ref) => {\n    return (\n      <span\n        ref={ref}\n        className={cn('flex h-10 w-10 items-center justify-center text-muted-foreground', className)}\n        {...props}\n      >\n        <MoreHorizontal className=\"h-4 w-4\" />\n        <span className=\"sr-only\">More pages</span>\n      </span>\n    );\n  },\n);\nPaginationEllipsis.displayName = 'PaginationEllipsis';\n\nconst PaginationPrevious = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(\n  ({ className, ...props }, ref) => {\n    return (\n      <Button ref={ref} variant=\"outline\" size=\"icon\" className={cn('h-10 w-10', className)} {...props}>\n        <ChevronLeft className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Previous page</span>\n      </Button>\n    );\n  },\n);\nPaginationPrevious.displayName = 'PaginationPrevious';\n\nconst PaginationNext = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(\n  ({ className, ...props }, ref) => {\n    return (\n      <Button ref={ref} variant=\"outline\" size=\"icon\" className={cn('h-10 w-10', className)} {...props}>\n        <ChevronRight className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Next page</span>\n      </Button>\n    );\n  },\n);\nPaginationNext.displayName = 'PaginationNext';\n\nexport {\n  Pagination,\n  PaginationContent,\n  PaginationEllipsis,\n  PaginationItem,\n  PaginationLink,\n  PaginationNext,\n  PaginationPrevious,\n};\n"]}