{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "perplexity-search",
  "title": "Perplexity Search",
  "description": "The globe search step with queries, source rows and a +N more expander, built on shadcn Collapsible.",
  "dependencies": [
    "cn",
    "lucide-react"
  ],
  "registryDependencies": [
    "avatar",
    "button",
    "collapsible",
    "empty",
    "item",
    "separator",
    "https://ui.usenotra.com/r/shimmer.json"
  ],
  "files": [
    {
      "path": "registry/notra/perplexity/components/perplexity-search.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { ChevronDown, Search } from \"lucide-react\";\nimport { useCallback, useState } from \"react\";\nimport type { CSSProperties, ReactNode } from \"react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { Empty, EmptyDescription } from \"@/components/ui/empty\";\nimport {\n  Item,\n  ItemContent,\n  ItemGroup,\n  ItemMedia,\n  ItemTitle,\n} from \"@/components/ui/item\";\nimport { Separator } from \"@/components/ui/separator\";\n\nimport { Shimmer } from \"../../shimmer/components/shimmer\";\nimport {\n  PERPLEXITY_SEARCH_PREVIEW_COUNT,\n  PERPLEXITY_SEARCH_STAGGER_MS,\n} from \"../constants/perplexity\";\nimport { usePerplexitySearchSequence } from \"../hooks/use-perplexity-search-sequence\";\nimport type {\n  PerplexitySearchProps,\n  PerplexitySource,\n} from \"../types/perplexity\";\nimport { PerplexityFavicon } from \"./perplexity-favicon\";\nimport {\n  PerplexityShieldIcon,\n  PerplexityWebSearchIcon,\n} from \"./perplexity-icons\";\n\nconst WWW_PREFIX = /^www\\./;\nconst TLD_SUFFIX = /\\.[^.]+$/;\n\nconst EASE = \"ease-[cubic-bezier(0.22,1,0.36,1)]\";\n\nconst ENTER_CLASS = `translate-y-0 opacity-100 transition-[opacity,translate] duration-200 ${EASE} motion-reduce:transition-none starting:translate-y-1.5 starting:opacity-0 motion-reduce:starting:translate-y-0 motion-reduce:starting:opacity-100`;\n\nconst PANEL_CLASS = `grid overflow-hidden transition-[grid-template-rows,opacity] duration-300 ${EASE} outline-none data-closed:grid-rows-[0fr] data-ending-style:grid-rows-[0fr] data-ending-style:opacity-0 data-open:grid-rows-[1fr] data-starting-style:grid-rows-[0fr] data-starting-style:opacity-0 motion-reduce:transition-none`;\n\nconst TRIGGER_CLASS =\n  \"focus-visible:ring-pplx-ring h-auto w-fit max-w-full justify-start gap-1.5 rounded-md border-0 p-0 text-left text-sm leading-5 font-normal whitespace-normal hover:bg-transparent focus-visible:border-transparent focus-visible:ring-2 aria-expanded:bg-transparent dark:hover:bg-transparent dark:aria-expanded:bg-transparent\";\n\nconst CHEVRON_CLASS = `text-pplx-muted size-3.5 shrink-0 transition-transform duration-300 ${EASE} motion-reduce:transition-none`;\n\nconst ROW_CLASS =\n  \"flex-nowrap items-center gap-2 rounded-sm border-0 p-0 text-sm leading-5\";\n\nconst ROW_LINK_CLASS =\n  \"focus-visible:ring-pplx-ring [a]:hover:bg-transparent focus-visible:ring-2 [&:hover_[data-slot=item-title]]:text-pplx-fg\";\n\nconst sourceHref = (url: string | undefined) => {\n  if (!url) {\n    return null;\n  }\n\n  try {\n    const parsed = new URL(url);\n    if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n      return null;\n    }\n    return parsed.href;\n  } catch {\n    return null;\n  }\n};\n\nexport const perplexityShortDomain = (domain: string) => {\n  const host = domain.replace(WWW_PREFIX, \"\");\n  const short = host.replace(TLD_SUFFIX, \"\");\n  return short || host;\n};\n\nconst SourceRow = ({\n  className,\n  source,\n  style,\n}: {\n  className?: string;\n  source: PerplexitySource;\n  style?: CSSProperties;\n}) => {\n  const href = sourceHref(source.url);\n\n  return (\n    <Item\n      className={cn(ROW_CLASS, href && ROW_LINK_CLASS, className)}\n      render={\n        href\n          ? (linkProps) => (\n              <a\n                {...linkProps}\n                href={href}\n                rel=\"noopener noreferrer\"\n                target=\"_blank\"\n              >\n                {linkProps.children}\n              </a>\n            )\n          : undefined\n      }\n      role=\"listitem\"\n      style={style}\n    >\n      <ItemMedia>\n        <PerplexityFavicon domain={source.domain} />\n      </ItemMedia>\n      <ItemContent className=\"min-w-0 flex-row items-center gap-2\">\n        <ItemTitle className=\"text-pplx-muted line-clamp-none block w-auto min-w-0 truncate text-sm leading-5 font-normal transition-colors\">\n          {source.title}\n        </ItemTitle>\n        <span className=\"text-pplx-subtle flex shrink-0 items-center gap-1 text-sm leading-5\">\n          <span className=\"max-w-36 truncate\">\n            {perplexityShortDomain(source.domain)}\n          </span>\n          {source.verified === false ? null : (\n            <PerplexityShieldIcon className=\"size-3\" />\n          )}\n        </span>\n      </ItemContent>\n    </Item>\n  );\n};\n\nconst useControllableOpen = (\n  open: boolean | undefined,\n  defaultOpen: boolean,\n  onChange: ((next: boolean) => void) | undefined\n) => {\n  const [uncontrolled, setUncontrolled] = useState(defaultOpen);\n  const value = open ?? uncontrolled;\n  const setValue = (next: boolean) => {\n    if (open === undefined) {\n      setUncontrolled(next);\n    }\n    onChange?.(next);\n  };\n  return [value, setValue, setUncontrolled] as const;\n};\n\ninterface SearchFooterProps {\n  extraSources: readonly PerplexitySource[];\n  hiddenCount: number;\n  labels: PerplexitySearchProps[\"labels\"];\n  sequential: boolean;\n}\n\nconst SearchFooter = ({\n  extraSources,\n  hiddenCount,\n  labels,\n  sequential,\n}: SearchFooterProps) => {\n  const [extrasOpen, setExtrasOpen] = useState(false);\n  const moreLabel = labels?.more(hiddenCount) ?? `+${hiddenCount} more`;\n\n  if (hiddenCount === 0) {\n    return null;\n  }\n\n  if (extraSources.length === 0) {\n    return (\n      <p\n        className={cn(\n          \"text-pplx-subtle mt-1.5 text-sm leading-5\",\n          sequential && ENTER_CLASS\n        )}\n      >\n        {moreLabel}\n      </p>\n    );\n  }\n\n  return (\n    <Collapsible onOpenChange={setExtrasOpen} open={extrasOpen}>\n      <CollapsibleContent className={PANEL_CLASS} keepMounted>\n        <ItemGroup className=\"min-h-0 gap-1.5 overflow-hidden pb-1.5\">\n          {extraSources.map((source) => (\n            <SourceRow\n              key={`${source.domain}-${source.title}`}\n              source={source}\n            />\n          ))}\n        </ItemGroup>\n      </CollapsibleContent>\n      <CollapsibleTrigger\n        render={\n          <Button\n            className={cn(\n              \"text-pplx-subtle hover:text-pplx-secondary focus-visible:ring-pplx-ring dark:hover:text-pplx-secondary mt-1.5 h-auto rounded-sm border-0 p-0 text-sm leading-5 font-normal no-underline transition-colors hover:no-underline focus-visible:border-transparent focus-visible:ring-2\",\n              sequential && ENTER_CLASS\n            )}\n            variant=\"link\"\n          />\n        }\n      >\n        {extrasOpen ? (labels?.showLess ?? \"Show less\") : moreLabel}\n      </CollapsibleTrigger>\n    </Collapsible>\n  );\n};\n\ninterface SourcesBodyProps extends SearchFooterProps {\n  emptyDescription: ReactNode;\n  previewSources: readonly PerplexitySource[];\n  show: boolean;\n  totalSources: number;\n}\n\nconst SourcesBody = ({\n  emptyDescription,\n  previewSources,\n  show,\n  totalSources,\n  ...footerProps\n}: SourcesBodyProps) => {\n  if (show && totalSources > 0) {\n    return (\n      <div>\n        <ItemGroup className=\"gap-1.5\">\n          {previewSources.map((source, index) => (\n            <SourceRow\n              className={\n                footerProps.sequential\n                  ? cn(ENTER_CLASS, \"delay-(--pplx-delay)\")\n                  : undefined\n              }\n              key={`${source.domain}-${source.title}`}\n              source={source}\n              style={\n                {\n                  \"--pplx-delay\": `${index * PERPLEXITY_SEARCH_STAGGER_MS}ms`,\n                } as CSSProperties\n              }\n            />\n          ))}\n        </ItemGroup>\n        <SearchFooter {...footerProps} />\n      </div>\n    );\n  }\n\n  if (!footerProps.sequential && emptyDescription) {\n    return (\n      <Empty className=\"items-start rounded-none border-0 p-0 text-left md:p-0\">\n        <EmptyDescription className=\"text-pplx-subtle text-sm leading-5\">\n          {emptyDescription}\n        </EmptyDescription>\n      </Empty>\n    );\n  }\n\n  return null;\n};\n\ninterface SearchStepProps {\n  onOpenChange: (next: boolean) => void;\n  open: boolean;\n  queries: readonly string[];\n  sequential: boolean;\n  sourcesBody: ReactNode;\n  title: ReactNode;\n  visibleQueryCount: number;\n}\n\nconst SearchStep = ({\n  onOpenChange,\n  open,\n  queries,\n  sequential,\n  sourcesBody,\n  title,\n  visibleQueryCount,\n}: SearchStepProps) => (\n  <Collapsible onOpenChange={onOpenChange} open={open}>\n    <CollapsibleTrigger\n      render={\n        <Button className={cn(TRIGGER_CLASS, \"group/step\")} variant=\"ghost\" />\n      }\n    >\n      <SearchStepHeader title={title} />\n      <ChevronDown\n        className={cn(\n          CHEVRON_CLASS,\n          \"-rotate-90 group-data-panel-open/step:rotate-0\"\n        )}\n        strokeWidth={1.75}\n      />\n    </CollapsibleTrigger>\n    <CollapsibleContent className={PANEL_CLASS} keepMounted>\n      <div className=\"min-h-0 overflow-hidden\">\n        <div className=\"flex flex-col gap-1.5 ps-7.5 pt-2\">\n          <ItemGroup className=\"gap-1.5\">\n            {queries.slice(0, visibleQueryCount).map((query) => (\n              <Item\n                className={cn(\n                  ROW_CLASS,\n                  \"text-pplx-muted items-start\",\n                  sequential && ENTER_CLASS\n                )}\n                key={query}\n                role=\"listitem\"\n              >\n                <ItemMedia className=\"h-5\">\n                  <Search\n                    className=\"text-pplx-subtle size-4\"\n                    strokeWidth={1.75}\n                  />\n                </ItemMedia>\n                <ItemContent className=\"min-w-0 gap-0\">\n                  <ItemTitle className=\"line-clamp-none w-auto text-sm leading-5 font-normal\">\n                    {query}\n                  </ItemTitle>\n                </ItemContent>\n              </Item>\n            ))}\n          </ItemGroup>\n          {sourcesBody}\n        </div>\n      </div>\n    </CollapsibleContent>\n  </Collapsible>\n);\n\nconst SearchStepHeader = ({ title }: { title: ReactNode }) => (\n  <>\n    <span className=\"flex size-6 shrink-0 items-center justify-center\">\n      <PerplexityWebSearchIcon className=\"text-pplx-muted size-4\" />\n    </span>\n    <span className=\"text-pplx-muted min-w-0\">{title}</span>\n  </>\n);\n\nexport const PerplexitySearch = ({\n  className,\n  defaultOpen = false,\n  duration,\n  emptyDescription,\n  extraCount,\n  label = \"Researched\",\n  labels,\n  onOpenChange,\n  onStepOpenChange,\n  open: openProp,\n  previewCount = PERPLEXITY_SEARCH_PREVIEW_COUNT,\n  queries,\n  reducedMotion = false,\n  runningLabel = \"Researching…\",\n  sequential = false,\n  sources,\n  stepDefaultOpen = false,\n  stepOpen: stepOpenProp,\n  title = \"Searching the web\",\n  ...props\n}: PerplexitySearchProps) => {\n  const shouldSequence = sequential && !reducedMotion;\n  const previewSources = sources.slice(0, Math.max(previewCount, 0));\n  const extraSources = sources.slice(previewSources.length);\n  const hiddenCount = extraSources.length || extraCount || 0;\n  const [uncontrolledOpen, setOpenState] = useState(defaultOpen);\n  const open = openProp ?? uncontrolledOpen;\n  const [stepOpen, setStepOpen, setStepOpenState] = useControllableOpen(\n    stepOpenProp,\n    stepDefaultOpen,\n    onStepOpenChange\n  );\n\n  const handleStart = useCallback(() => {\n    setOpenState(true);\n    setStepOpenState(true);\n  }, [setStepOpenState]);\n  const handleDone = useCallback(() => {\n    setOpenState(false);\n    setStepOpenState(false);\n  }, [setStepOpenState]);\n\n  const progress = usePerplexitySearchSequence({\n    enabled: shouldSequence,\n    onDone: handleDone,\n    onStart: handleStart,\n    previewCount: previewSources.length,\n    queryCount: queries.length,\n  });\n\n  const running = shouldSequence && !progress.done;\n  const hasBody =\n    queries.length > 0 || sources.length > 0 || Boolean(emptyDescription);\n\n  const sourcesBody = (\n    <SourcesBody\n      emptyDescription={emptyDescription}\n      extraSources={extraSources}\n      hiddenCount={hiddenCount}\n      labels={labels}\n      previewSources={previewSources}\n      sequential={shouldSequence}\n      show={shouldSequence ? progress.sources : sources.length > 0}\n      totalSources={sources.length}\n    />\n  );\n\n  return (\n    <Collapsible\n      className={cn(\"font-pplx w-full max-w-2xl\", className)}\n      data-slot=\"perplexity-search\"\n      onOpenChange={(next, details) => {\n        if (openProp === undefined) {\n          setOpenState(next);\n        }\n        onOpenChange?.(next, details);\n      }}\n      open={open}\n      {...props}\n    >\n      <CollapsibleTrigger\n        render={\n          <Button\n            className={cn(TRIGGER_CLASS, \"group/search text-pplx-fg\")}\n            variant=\"ghost\"\n          />\n        }\n      >\n        {running ? (\n          <Shimmer className=\"[--shimmer-highlight:var(--color-pplx-fg)]\">\n            {runningLabel}\n          </Shimmer>\n        ) : (\n          <span>\n            {label}\n            {duration ? (\n              <span className=\"text-pplx-subtle\"> {duration}</span>\n            ) : null}\n          </span>\n        )}\n        <ChevronDown\n          className={cn(\n            CHEVRON_CLASS,\n            \"-rotate-90 group-data-panel-open/search:rotate-0\"\n          )}\n          strokeWidth={1.75}\n        />\n      </CollapsibleTrigger>\n      <CollapsibleContent className={PANEL_CLASS} keepMounted>\n        <div className=\"min-h-0 overflow-hidden\">\n          <div className=\"flex gap-4 pt-2 pb-1\">\n            <Separator className=\"bg-pplx-border\" orientation=\"vertical\" />\n            <div className=\"min-w-0 flex-1\">\n              {hasBody ? (\n                <SearchStep\n                  onOpenChange={setStepOpen}\n                  open={stepOpen}\n                  queries={queries}\n                  sequential={shouldSequence}\n                  sourcesBody={sourcesBody}\n                  title={title}\n                  visibleQueryCount={\n                    shouldSequence ? progress.queries : queries.length\n                  }\n                />\n              ) : (\n                <div className=\"flex items-center gap-1.5 text-sm leading-5\">\n                  <SearchStepHeader title={title} />\n                </div>\n              )}\n            </div>\n          </div>\n        </div>\n      </CollapsibleContent>\n    </Collapsible>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/perplexity/components/perplexity-search.tsx"
    },
    {
      "path": "registry/notra/perplexity/components/perplexity-favicon.tsx",
      "content": "import { cn } from \"cn\";\nimport { Globe } from \"lucide-react\";\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\n\nimport type { PerplexityFaviconProps } from \"../types/perplexity\";\n\nexport const perplexityFaviconSrc = (domain: string) =>\n  `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=64`;\n\nexport const PerplexityFavicon = ({\n  className,\n  domain,\n  ...props\n}: PerplexityFaviconProps) => (\n  <Avatar\n    className={cn(\"size-4 after:hidden\", className)}\n    data-slot=\"perplexity-favicon\"\n    {...props}\n  >\n    <AvatarImage alt=\"\" src={perplexityFaviconSrc(domain)} />\n    <AvatarFallback className=\"text-pplx-subtle bg-transparent\">\n      <Globe className=\"size-full\" strokeWidth={1.75} />\n    </AvatarFallback>\n  </Avatar>\n);\n",
      "type": "registry:component",
      "target": "components/perplexity/components/perplexity-favicon.tsx"
    },
    {
      "path": "registry/notra/perplexity/components/perplexity-icons.tsx",
      "content": "\"use client\";\n\nimport { useId } from \"react\";\nimport type { ComponentProps } from \"react\";\n\nexport const PerplexityShieldIcon = (props: ComponentProps<\"svg\">) => (\n  <svg aria-hidden=\"true\" fill=\"none\" viewBox=\"0 0 12 12\" {...props}>\n    <path\n      d=\"M6 1.15 10.2 2.7v3.05c0 2.28-1.72 3.86-4.2 4.95-2.48-1.09-4.2-2.67-4.2-4.95V2.7L6 1.15Z\"\n      stroke=\"currentColor\"\n      strokeLinejoin=\"round\"\n      strokeWidth=\"0.9\"\n    />\n    <path\n      d=\"M4.15 6.05 5.35 7.2 7.9 4.55\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeWidth=\"0.9\"\n    />\n  </svg>\n);\n\nexport const PerplexityWebSearchIcon = (props: ComponentProps<\"svg\">) => (\n  <svg\n    aria-hidden=\"true\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    strokeLinecap=\"round\"\n    strokeLinejoin=\"round\"\n    strokeWidth=\"1.75\"\n    viewBox=\"0 0 24 24\"\n    {...props}\n  >\n    <path d=\"M20.9 10.6A9 9 0 1 0 10.6 20.9\" />\n    <path d=\"M3.6 9h16.8M3.6 15h7.6\" />\n    <path d=\"M11.6 3a14.5 14.5 0 0 0 0 18\" />\n    <path d=\"M12.4 3a14.5 14.5 0 0 1 3.1 7.6\" />\n    <circle cx=\"17.2\" cy=\"17.2\" r=\"3\" />\n    <path d=\"m19.4 19.4 2.1 2.1\" />\n  </svg>\n);\n\nexport const PerplexityLogoIcon = (props: ComponentProps<\"svg\">) => (\n  <svg\n    aria-hidden=\"true\"\n    fill=\"none\"\n    stroke=\"#20808d\"\n    strokeLinecap=\"round\"\n    strokeLinejoin=\"round\"\n    viewBox=\"0 0 48 48\"\n    {...props}\n  >\n    <path d=\"M24 4.5v39M13.73 16.573v-9.99L24 16.573m0 14.5L13.73 41.417V27.01L24 16.573m0 0l10.27-9.99v9.99\" />\n    <path d=\"M13.73 31.396H9.44V16.573h29.12v14.823h-4.29\" />\n    <path d=\"M24 16.573L34.27 27.01v14.407L24 31.073\" />\n  </svg>\n);\n\nexport const PerplexityOpenAIIcon = (props: ComponentProps<\"svg\">) => (\n  <svg\n    aria-hidden=\"true\"\n    fill=\"currentColor\"\n    preserveAspectRatio=\"xMidYMid\"\n    viewBox=\"0 0 256 260\"\n    {...props}\n  >\n    <path d=\"M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z\" />\n  </svg>\n);\n\nexport const PerplexityGeminiIcon = (props: ComponentProps<\"svg\">) => {\n  const gradientId = `pplx-gemini-${useId().replaceAll(\":\", \"\")}`;\n\n  return (\n    <svg\n      aria-hidden=\"true\"\n      fill=\"none\"\n      overflow=\"visible\"\n      viewBox=\"0 0 296 298\"\n      {...props}\n    >\n      <defs>\n        <linearGradient\n          gradientUnits=\"userSpaceOnUse\"\n          id={gradientId}\n          x1=\"28\"\n          x2=\"268\"\n          y1=\"18\"\n          y2=\"280\"\n        >\n          <stop stopColor=\"#439DDF\" />\n          <stop offset=\".524\" stopColor=\"#4F87ED\" />\n          <stop offset=\".781\" stopColor=\"#9476C5\" />\n          <stop offset=\".888\" stopColor=\"#BC688E\" />\n          <stop offset=\"1\" stopColor=\"#D6645D\" />\n        </linearGradient>\n      </defs>\n      <path\n        d=\"M141.201 4.886c2.282-6.17 11.042-6.071 13.184.148l5.985 17.37a184.004 184.004 0 0 0 111.257 113.049l19.304 6.997c6.143 2.227 6.156 10.91.02 13.155l-19.35 7.082a184.001 184.001 0 0 0-109.495 109.385l-7.573 20.629c-2.241 6.105-10.869 6.121-13.133.025l-7.908-21.296a184 184 0 0 0-109.02-108.658l-19.698-7.239c-6.102-2.243-6.118-10.867-.025-13.132l20.083-7.467A183.998 183.998 0 0 0 133.291 26.28l7.91-21.394Z\"\n        fill={`url(#${gradientId})`}\n      />\n    </svg>\n  );\n};\n\nexport const PerplexityClaudeIcon = (props: ComponentProps<\"svg\">) => (\n  <svg\n    aria-hidden=\"true\"\n    preserveAspectRatio=\"xMidYMid\"\n    viewBox=\"0 0 256 257\"\n    {...props}\n  >\n    <path\n      d=\"m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z\"\n      fill=\"#D97757\"\n    />\n  </svg>\n);\n\nexport const PerplexityKimiIcon = (props: ComponentProps<\"svg\">) => (\n  <svg aria-hidden=\"true\" fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M21.765.351C22.998.351 24 1.353 24 2.586S22.998 4.82 21.765 4.82h-1.974c-.15 0-.26-.12-.26-.26V2.586A2.237 2.237 0 0 1 21.765.35M9.41 13.388l8.447-8.377c.16-.16.07-.471-.14-.471h-4.55s-.1.02-.14.06l-9.099 9.029c-.14.14-.35.02-.35-.21V4.81c0-.15-.1-.27-.221-.27H.22c-.12 0-.22.12-.22.27v18.57c0 .15.1.27.22.27h3.137c.12 0 .22-.12.22-.27v-3.79c0-.08.03-.16.08-.21l2.826-2.796c.07-.07.16-.08.241-.03l7.546 5.551a8.9 8.9 0 0 0 4.018 1.493c.12.01.23-.11.23-.27V19.76c0-.14-.08-.25-.19-.26a5.8 5.8 0 0 1-2.355-.942l-6.533-4.73c-.14-.09-.15-.32-.03-.441\" />\n  </svg>\n);\n\nexport const PerplexityGrokIcon = (props: ComponentProps<\"svg\">) => (\n  <svg\n    aria-hidden=\"true\"\n    fill=\"currentColor\"\n    viewBox=\"0 0 1024 1024\"\n    {...props}\n  >\n    <path d=\"M395.479 633.828L735.91 381.105C752.599 368.715 776.454 373.548 784.406 392.792C826.26 494.285 807.561 616.253 724.288 699.996C641.016 783.739 525.151 802.104 419.247 760.277L303.556 814.143C469.49 928.202 670.987 899.995 796.901 773.282C896.776 672.843 927.708 535.937 898.785 412.476L899.047 412.739C857.105 231.37 909.358 158.874 1016.4 10.6326C1018.93 7.11771 1021.47 3.60279 1024 0L883.144 141.651V141.212L395.392 633.916\" />\n    <path d=\"M325.226 695.251C206.128 580.84 226.662 403.776 328.285 301.668C403.431 226.097 526.549 195.254 634.026 240.596L749.454 186.994C728.657 171.88 702.007 155.623 671.424 144.2C533.19 86.9942 367.693 115.465 255.323 228.382C147.234 337.081 113.244 504.215 171.613 646.833C215.216 753.423 143.739 828.818 71.7385 904.916C46.2237 931.893 20.6216 958.87 0 987.429L325.139 695.339\" />\n  </svg>\n);\n\nexport const PerplexityZaiIcon = (props: ComponentProps<\"svg\">) => (\n  <svg\n    aria-hidden=\"true\"\n    fill=\"currentColor\"\n    fillRule=\"evenodd\"\n    viewBox=\"0 0 24 24\"\n    {...props}\n  >\n    <path d=\"M12.105 2 9.927 4.953H.653L2.83 2h9.275ZM23.254 19.048 21.078 22h-9.242l2.174-2.952h9.244ZM24 2 9.264 22H0L14.736 2H24Z\" />\n  </svg>\n);\n\nexport const PerplexityNvidiaIcon = (props: ComponentProps<\"svg\">) => (\n  <svg aria-hidden=\"true\" fill=\"currentColor\" viewBox=\"0 0 24 24\" {...props}>\n    <path d=\"M8.948 8.798v-1.43a6.7 6.7 0 0 1 .424-.018c3.922-.124 6.493 3.374 6.493 3.374s-2.774 3.851-5.75 3.851c-.398 0-.787-.062-1.158-.185v-4.346c1.528.185 1.837.857 2.747 2.385l2.04-1.714s-1.492-1.952-4-1.952a6.016 6.016 0 0 0-.796.035m0-4.735v2.138l.424-.027c5.45-.185 9.01 4.47 9.01 4.47s-4.08 4.964-8.33 4.964c-.37 0-.733-.035-1.095-.097v1.325c.3.035.61.062.91.062 3.957 0 6.82-2.023 9.593-4.408.459.371 2.34 1.263 2.73 1.652-2.633 2.208-8.772 3.984-12.253 3.984-.335 0-.653-.018-.971-.053v1.864H24V4.063zm0 10.326v1.131c-3.657-.654-4.673-4.46-4.673-4.46s1.758-1.944 4.673-2.262v1.237H8.94c-1.528-.186-2.73 1.245-2.73 1.245s.68 2.412 2.739 3.11M2.456 10.9s2.164-3.197 6.5-3.533V6.201C4.153 6.59 0 10.653 0 10.653s2.35 6.802 8.948 7.42v-1.237c-4.84-.6-6.492-5.936-6.492-5.936z\" />\n  </svg>\n);\n",
      "type": "registry:component",
      "target": "components/perplexity/components/perplexity-icons.tsx"
    },
    {
      "path": "registry/notra/perplexity/constants/perplexity.ts",
      "content": "import type {\n  PerplexityRewriteMode,\n  PerplexityFocusId,\n  PerplexityFocusOption,\n  PerplexityModel,\n} from \"../types/perplexity\";\n\nexport const PERPLEXITY_THINKING_MS = 1500;\nexport const PERPLEXITY_THINKING_GAP_MS = 120;\nexport const PERPLEXITY_SEARCH_HEADER_MS = 480;\nexport const PERPLEXITY_SEARCH_QUERY_MS = 520;\nexport const PERPLEXITY_SEARCH_SOURCES_MS = 720;\nexport const PERPLEXITY_SEARCH_STAGGER_MS = 70;\nexport const PERPLEXITY_SEARCH_SETTLE_MS = 180;\nexport const PERPLEXITY_SEARCH_PREVIEW_COUNT = 4;\n\nexport const PERPLEXITY_SOURCES_PREVIEW_COUNT = 3;\n\nexport const PERPLEXITY_COPIED_RESET_MS = 1500;\n\nexport const PERPLEXITY_TOOLTIP_DELAY_MS = 200;\nexport const PERPLEXITY_CITATION_OPEN_DELAY_MS = 150;\nexport const PERPLEXITY_CITATION_CLOSE_DELAY_MS = 120;\n\nexport const PERPLEXITY_SEARCH_FOCUS: PerplexityFocusOption = {\n  description: \"Quick answers from the live web\",\n  id: \"search\",\n  label: \"Search\",\n};\n\nexport const PERPLEXITY_DEFAULT_FOCUS: PerplexityFocusId =\n  PERPLEXITY_SEARCH_FOCUS.id;\n\nexport const PERPLEXITY_FOCUS_OPTIONS: readonly PerplexityFocusOption[] = [\n  PERPLEXITY_SEARCH_FOCUS,\n  {\n    description: \"Longer reports with more sources\",\n    id: \"research\",\n    label: \"Research\",\n  },\n];\n\nexport const PERPLEXITY_MODELS: readonly PerplexityModel[] = [\n  { id: \"sonar-2\", label: \"Sonar 2\", locked: true, provider: \"perplexity\" },\n  {\n    id: \"gpt-5.6-terra\",\n    label: \"GPT-5.6 Terra\",\n    locked: true,\n    provider: \"openai\",\n  },\n  {\n    badge: \"max\",\n    id: \"gpt-5.6-sol\",\n    label: \"GPT-5.6 Sol\",\n    locked: true,\n    provider: \"openai\",\n  },\n  {\n    badge: \"new\",\n    id: \"gemini-3.7-flash\",\n    label: \"Gemini 3.7 Flash\",\n    locked: true,\n    provider: \"google\",\n  },\n  {\n    id: \"claude-sonnet-5\",\n    label: \"Claude Sonnet 5\",\n    locked: true,\n    provider: \"anthropic\",\n  },\n  {\n    badge: \"max\",\n    id: \"claude-opus-5\",\n    label: \"Claude Opus 5\",\n    locked: true,\n    provider: \"anthropic\",\n  },\n  { id: \"kimi-k3\", label: \"Kimi K3\", locked: true, provider: \"kimi\" },\n  { id: \"glm-5.2\", label: \"GLM 5.2\", locked: true, provider: \"zhipu\" },\n  {\n    badge: \"new\",\n    id: \"grok-4.7\",\n    label: \"Grok 4.7\",\n    locked: true,\n    provider: \"xai\",\n  },\n  {\n    id: \"nemotron-3-super\",\n    label: \"Nemotron 3 Super\",\n    locked: true,\n    provider: \"nvidia\",\n  },\n];\n\nexport const PERPLEXITY_REWRITE_MODES: readonly PerplexityRewriteMode[] = [\n  { id: \"search\", label: \"Search\" },\n  { id: \"deep-research\", label: \"Deep research\", locked: true },\n  { id: \"learn\", label: \"Learn step by step\" },\n];\n",
      "type": "registry:lib",
      "target": "components/perplexity/constants/perplexity.ts"
    },
    {
      "path": "registry/notra/perplexity/types/perplexity.ts",
      "content": "import type { ComponentProps, ReactNode } from \"react\";\n\nimport type { Avatar } from \"@/components/ui/avatar\";\nimport type { Collapsible } from \"@/components/ui/collapsible\";\n\nexport type PerplexityMessageRole = \"user\" | \"assistant\";\n\nexport type PerplexityModelProvider =\n  | \"perplexity\"\n  | \"openai\"\n  | \"google\"\n  | \"anthropic\"\n  | \"kimi\"\n  | \"zhipu\"\n  | \"xai\"\n  | \"nvidia\";\n\nexport type PerplexityModelBadge = \"max\" | \"new\";\n\nexport interface PerplexityModel {\n  badge?: PerplexityModelBadge;\n  id: string;\n  label: string;\n  locked?: boolean;\n  provider: PerplexityModelProvider;\n}\n\nexport type PerplexityFocusId = \"search\" | \"research\";\n\nexport type PerplexityRewriteModeId = \"deep-research\" | \"learn\" | \"search\";\n\nexport interface PerplexityRewriteMode {\n  id: PerplexityRewriteModeId;\n  label: string;\n  locked?: boolean;\n}\n\nexport interface PerplexityRewriteOptions {\n  mode: PerplexityRewriteModeId;\n  model?: string;\n}\n\nexport interface PerplexityRewritePanelProps extends Omit<\n  ComponentProps<\"div\">,\n  \"onSubmit\"\n> {\n  defaultMode?: PerplexityRewriteModeId;\n  modes?: readonly PerplexityRewriteMode[];\n  models?: readonly PerplexityModel[];\n  onClose?: () => void;\n  onRewrite?: (options: PerplexityRewriteOptions) => void;\n  promoLabel?: ReactNode;\n}\n\nexport interface PerplexityFocusOption {\n  description: string;\n  id: PerplexityFocusId;\n  label: string;\n}\n\nexport interface PerplexitySource {\n  domain: string;\n  title: string;\n  url?: string;\n  verified?: boolean;\n}\n\nexport interface PerplexitySearchLabels {\n  more: (count: number) => string;\n  showLess: string;\n}\n\nexport interface PerplexityMessageProps extends ComponentProps<\"div\"> {\n  actions?: ReactNode;\n  from: PerplexityMessageRole;\n  search?: ReactNode;\n}\n\nexport interface PerplexitySearchProps extends Omit<\n  ComponentProps<typeof Collapsible>,\n  \"title\"\n> {\n  duration?: ReactNode;\n  emptyDescription?: ReactNode;\n  extraCount?: number;\n  label?: ReactNode;\n  labels?: PerplexitySearchLabels;\n  onStepOpenChange?: (open: boolean) => void;\n  previewCount?: number;\n  queries: readonly string[];\n  reducedMotion?: boolean;\n  runningLabel?: ReactNode;\n  sequential?: boolean;\n  sources: readonly PerplexitySource[];\n  stepDefaultOpen?: boolean;\n  stepOpen?: boolean;\n  title?: ReactNode;\n}\n\nexport interface PerplexityCitationSource {\n  description?: string;\n  domain: string;\n  title: string;\n  url?: string;\n}\n\nexport interface PerplexityCitationProps extends ComponentProps<\"span\"> {\n  extra?: number;\n  label: string;\n  sources?: readonly PerplexityCitationSource[];\n}\n\nexport interface PerplexityFaviconProps extends ComponentProps<typeof Avatar> {\n  domain: string;\n}\n\nexport interface PerplexityActionsProps extends Omit<\n  ComponentProps<\"div\">,\n  \"onSubmit\"\n> {\n  models?: readonly PerplexityModel[];\n  onBadResponse?: () => void;\n  onDownload?: () => void;\n  onGoodResponse?: () => void;\n  onMore?: () => void;\n  onRewrite?: (options: PerplexityRewriteOptions) => void;\n  onShare?: () => void;\n  sources?: readonly PerplexitySource[];\n  text: string;\n}\n\nexport interface PerplexityUserActionsProps extends ComponentProps<\"div\"> {\n  onEdit?: () => void;\n  text: string;\n  timestamp?: ReactNode;\n}\n\nexport interface PerplexitySourcesSummaryProps extends ComponentProps<\"div\"> {\n  sources: readonly PerplexitySource[];\n}\n\nexport interface PerplexityComposerProps extends Omit<\n  ComponentProps<\"form\">,\n  \"onSubmit\"\n> {\n  busy?: boolean;\n  defaultModel?: string;\n  focus?: PerplexityFocusId;\n  focusOptions?: readonly PerplexityFocusOption[];\n  model?: string;\n  models?: readonly PerplexityModel[];\n  onModelChange?: (model: string) => void;\n  onSend?: (text: string) => void;\n  onStop?: () => void;\n  placeholder?: string;\n}\n\nexport interface PerplexityModelSelectorProps extends ComponentProps<\"button\"> {\n  label?: ReactNode;\n  model?: string;\n  models?: readonly PerplexityModel[];\n  onModelChange?: (model: string) => void;\n  promoLabel?: ReactNode;\n}\n\nexport interface PerplexityModelIconProps extends ComponentProps<\"span\"> {\n  provider: PerplexityModelProvider;\n}\n\nexport interface PerplexityThinkingProps extends ComponentProps<\"div\"> {\n  label?: ReactNode;\n  reducedMotion?: boolean;\n}\n\nexport interface PerplexityThreadCitation {\n  domain: string;\n  extra?: number;\n  id: string;\n  label: string;\n  sources?: readonly PerplexityCitationSource[];\n}\n\nexport interface PerplexityThreadSearch {\n  duration?: string;\n  extraCount?: number;\n  previewCount?: number;\n  queries: string[];\n  sources: PerplexitySource[];\n  title?: string;\n}\n\nexport interface PerplexityThreadMessage {\n  citations?: readonly PerplexityThreadCitation[];\n  from: PerplexityMessageRole;\n  id: string;\n  search?: PerplexityThreadSearch;\n  text: string;\n}\n\nexport interface PerplexityAnswerProps extends ComponentProps<\"div\"> {\n  citations?: readonly PerplexityThreadCitation[];\n  text: string;\n}\n\nexport type PerplexityPlaybackPhase =\n  | { status: \"idle\" }\n  | { status: \"playing\"; source: \"script\" | \"send\"; thinking: boolean };\n",
      "type": "registry:lib",
      "target": "components/perplexity/types/perplexity.ts"
    },
    {
      "path": "registry/notra/perplexity/hooks/use-perplexity-search-sequence.ts",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nimport {\n  PERPLEXITY_SEARCH_HEADER_MS,\n  PERPLEXITY_SEARCH_QUERY_MS,\n  PERPLEXITY_SEARCH_SETTLE_MS,\n  PERPLEXITY_SEARCH_SOURCES_MS,\n  PERPLEXITY_SEARCH_STAGGER_MS,\n} from \"../constants/perplexity\";\n\ninterface PerplexitySearchSequence {\n  done: boolean;\n  queries: number;\n  sources: boolean;\n}\n\ninterface PerplexitySearchSequenceOptions {\n  enabled: boolean;\n  onDone: () => void;\n  onStart: () => void;\n  previewCount: number;\n  queryCount: number;\n}\n\nexport const usePerplexitySearchSequence = ({\n  enabled,\n  onDone,\n  onStart,\n  previewCount,\n  queryCount,\n}: PerplexitySearchSequenceOptions): PerplexitySearchSequence => {\n  const [sequence, setSequence] = useState<PerplexitySearchSequence>({\n    done: false,\n    queries: 0,\n    sources: false,\n  });\n\n  const onStartRef = useRef(onStart);\n  const onDoneRef = useRef(onDone);\n  useEffect(() => {\n    onStartRef.current = onStart;\n    onDoneRef.current = onDone;\n  });\n\n  useEffect(() => {\n    if (!enabled) {\n      return;\n    }\n\n    onStartRef.current();\n    const timers: number[] = [];\n    let elapsed = PERPLEXITY_SEARCH_HEADER_MS;\n\n    for (let index = 0; index < queryCount; index += 1) {\n      timers.push(\n        window.setTimeout(() => {\n          setSequence((current) => ({ ...current, queries: index + 1 }));\n        }, elapsed)\n      );\n      elapsed += PERPLEXITY_SEARCH_QUERY_MS;\n    }\n\n    timers.push(\n      window.setTimeout(() => {\n        setSequence((current) => ({ ...current, sources: true }));\n      }, elapsed)\n    );\n    elapsed += Math.max(\n      PERPLEXITY_SEARCH_SOURCES_MS,\n      previewCount * PERPLEXITY_SEARCH_STAGGER_MS + PERPLEXITY_SEARCH_SETTLE_MS\n    );\n    timers.push(\n      window.setTimeout(() => {\n        setSequence((current) => ({ ...current, done: true }));\n        onDoneRef.current();\n      }, elapsed)\n    );\n\n    return () => {\n      for (const timer of timers) {\n        window.clearTimeout(timer);\n      }\n    };\n  }, [enabled, previewCount, queryCount]);\n\n  return sequence;\n};\n",
      "type": "registry:hook",
      "target": "components/perplexity/hooks/use-perplexity-search-sequence.ts"
    }
  ],
  "cssVars": {
    "theme": {
      "color-pplx-bg": "var(--pplx-bg)",
      "color-pplx-fg": "var(--pplx-fg)",
      "color-pplx-icon": "var(--pplx-icon)",
      "color-pplx-secondary": "var(--pplx-secondary)",
      "color-pplx-muted": "var(--pplx-muted)",
      "color-pplx-subtle": "var(--pplx-subtle)",
      "color-pplx-hover": "var(--pplx-hover)",
      "color-pplx-row-hover": "var(--pplx-row-hover)",
      "color-pplx-bubble": "var(--pplx-bubble)",
      "color-pplx-chip": "var(--pplx-chip)",
      "color-pplx-favicon": "var(--pplx-favicon)",
      "color-pplx-surface": "var(--pplx-surface)",
      "color-pplx-popover": "var(--pplx-popover)",
      "color-pplx-border": "var(--pplx-border)",
      "color-pplx-ring": "var(--pplx-ring)",
      "color-pplx-send": "var(--pplx-send)",
      "color-pplx-send-hover": "var(--pplx-send-hover)",
      "color-pplx-send-fg": "var(--pplx-send-fg)",
      "color-pplx-send-disabled": "var(--pplx-send-disabled)",
      "color-pplx-send-disabled-fg": "var(--pplx-send-disabled-fg)",
      "color-pplx-model-chip": "var(--pplx-model-chip)",
      "color-pplx-model-chip-hover": "var(--pplx-model-chip-hover)",
      "color-pplx-badge": "var(--pplx-badge)",
      "color-pplx-badge-fg": "var(--pplx-badge-fg)",
      "color-pplx-badge-new": "var(--pplx-badge-new)",
      "color-pplx-badge-new-fg": "var(--pplx-badge-new-fg)",
      "color-pplx-promo": "var(--pplx-promo)",
      "color-pplx-promo-hover": "var(--pplx-promo-hover)",
      "color-pplx-lock": "var(--pplx-lock)",
      "font-pplx": "Inter, ui-sans-serif, system-ui, sans-serif",
      "font-pplx-serif": "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif"
    },
    "light": {
      "pplx-bg": "#fdfbfb",
      "pplx-fg": "#27251e",
      "pplx-icon": "#3d3d3d",
      "pplx-secondary": "#5c5c5c",
      "pplx-muted": "#6b6b6b",
      "pplx-subtle": "#8d8d8d",
      "pplx-hover": "#f3f3f3",
      "pplx-row-hover": "#f5f5f5",
      "pplx-bubble": "#f3f3f3",
      "pplx-chip": "#f0f0f0",
      "pplx-favicon": "#e8e8e8",
      "pplx-surface": "#ffffff",
      "pplx-popover": "#ffffff",
      "pplx-border": "rgb(0 0 0 / 8%)",
      "pplx-ring": "rgb(0 0 0 / 15%)",
      "pplx-send": "#2a2a2a",
      "pplx-send-hover": "#1a1a1a",
      "pplx-send-fg": "#ffffff",
      "pplx-send-disabled": "#d9d9d9",
      "pplx-send-disabled-fg": "#ffffff",
      "pplx-model-chip": "#f2f2f2",
      "pplx-model-chip-hover": "#ebebeb",
      "pplx-badge": "#ececec",
      "pplx-badge-fg": "#6b6b6b",
      "pplx-badge-new": "#e8f1fb",
      "pplx-badge-new-fg": "#3d7ab5",
      "pplx-promo": "#e6f3f1",
      "pplx-promo-hover": "#dceeea",
      "pplx-lock": "#b0b0b0"
    },
    "dark": {
      "pplx-bg": "#171615",
      "pplx-fg": "#d6d5d4",
      "pplx-icon": "#fafafa",
      "pplx-secondary": "#b3b3b3",
      "pplx-muted": "#b3b3b3",
      "pplx-subtle": "#a3a3a3",
      "pplx-hover": "rgb(255 255 255 / 10%)",
      "pplx-row-hover": "rgb(255 255 255 / 8%)",
      "pplx-bubble": "rgb(255 255 255 / 10%)",
      "pplx-chip": "rgb(255 255 255 / 10%)",
      "pplx-favicon": "rgb(255 255 255 / 12%)",
      "pplx-surface": "#1c1c1c",
      "pplx-popover": "#1c1c1c",
      "pplx-border": "rgb(255 255 255 / 10%)",
      "pplx-ring": "rgb(255 255 255 / 20%)",
      "pplx-send": "#ffffff",
      "pplx-send-hover": "rgb(255 255 255 / 90%)",
      "pplx-send-fg": "#1a1a1a",
      "pplx-send-disabled": "rgb(255 255 255 / 20%)",
      "pplx-send-disabled-fg": "rgb(255 255 255 / 70%)",
      "pplx-model-chip": "rgb(255 255 255 / 10%)",
      "pplx-model-chip-hover": "rgb(255 255 255 / 15%)",
      "pplx-badge": "rgb(255 255 255 / 10%)",
      "pplx-badge-fg": "#b3b3b3",
      "pplx-badge-new": "rgb(61 122 181 / 20%)",
      "pplx-badge-new-fg": "#8bb8e0",
      "pplx-promo": "#1c3d38",
      "pplx-promo-hover": "#244843",
      "pplx-lock": "#7a7a7a"
    }
  },
  "type": "registry:component"
}