{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-analysis",
  "title": "File Analysis",
  "description": "Attached files, extraction status, and findings that cite the page or cell they came from.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "button",
    "https://uaiblocks.dev/r/uai-theme.json",
    "https://uaiblocks.dev/r/uai-utils.json",
    "https://uaiblocks.dev/r/attachment.json",
    "https://uaiblocks.dev/r/citation.json",
    "https://uaiblocks.dev/r/progress-summary.json"
  ],
  "files": [
    {
      "path": "src/registry/uai/blocks/file-analysis.tsx",
      "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { type ComponentProps, createContext, useContext, useId } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Attachment,\n  type AttachmentProps,\n  type AttachmentVariant,\n} from \"@/components/ui/uai/attachment\";\nimport { Citation, type CitationProps, type CitationVariant } from \"@/components/ui/uai/citation\";\nimport {\n  ProgressSummary,\n  type ProgressSummaryProps,\n  type ProgressSummaryVariant,\n} from \"@/components/ui/uai/progress-summary\";\nimport { cn } from \"@/lib/uai-utils\";\n\nexport const FILE_ANALYSIS_VARIANTS = [\"split\", \"stacked\", \"compact\"] as const;\nexport type FileAnalysisVariant = (typeof FILE_ANALYSIS_VARIANTS)[number];\nexport type FileAnalysisProps = ComponentProps<\"section\"> & { variant?: FileAnalysisVariant };\nexport const FILE_ANALYSIS_FINDING_TONES = [\"neutral\", \"warning\", \"critical\"] as const;\nexport type FileAnalysisFindingTone = (typeof FILE_ANALYSIS_FINDING_TONES)[number];\n\ntype AnalysisContext = { id: string; variant: FileAnalysisVariant };\nconst Context = createContext<AnalysisContext | null>(null);\nfunction useAnalysis(part: string) {\n  const context = useContext(Context);\n  if (!context) throw new Error(`${part} must be used within FileAnalysis`);\n  return context;\n}\nconst PanelContext = createContext<string | null>(null);\nfunction usePanel(part: string) {\n  const id = useContext(PanelContext);\n  if (!id) throw new Error(`${part} must be used within a FileAnalysis panel`);\n  return id;\n}\n\nconst attachmentVariants: Record<FileAnalysisVariant, AttachmentVariant> = {\n  split: \"row\",\n  stacked: \"card\",\n  compact: \"chip\",\n};\nconst progressVariants: Record<FileAnalysisVariant, ProgressSummaryVariant> = {\n  split: \"inline\",\n  stacked: \"card\",\n  compact: \"compact\",\n};\nconst citationVariants: Record<FileAnalysisVariant, CitationVariant> = {\n  split: \"chip\",\n  stacked: \"chip\",\n  compact: \"number\",\n};\n\nconst tones: Record<FileAnalysisFindingTone, { label: string; className: string }> = {\n  neutral: { label: \"Note\", className: \"bg-muted-foreground/16 text-muted-foreground\" },\n  warning: { label: \"Review\", className: \"bg-warning/14 text-warning-ink\" },\n  critical: {\n    label: \"Risk\",\n    className: \"bg-destructive/14 text-destructive-ink\",\n  },\n};\n\n/** Files, extraction status, and cited findings. Split places the files beside the findings at 720px. */\nexport function FileAnalysis({\n  variant = \"split\",\n  className,\n  children,\n  ...props\n}: FileAnalysisProps) {\n  const id = useId();\n  return (\n    <Context.Provider value={{ id, variant }}>\n      <section\n        aria-labelledby={`${id}-title`}\n        data-slot=\"file-analysis\"\n        data-variant={variant}\n        className={cn(\"@container min-w-0 text-[13px]/[18px] text-foreground\", className)}\n        {...props}\n      >\n        <div\n          className={cn(\n            \"grid min-w-0 items-start\",\n            variant === \"compact\" ? \"gap-3\" : \"gap-4\",\n            variant === \"split\" &&\n              \"@min-[720px]:grid-cols-[minmax(240px,0.8fr)_minmax(0,1.3fr)] @min-[720px]:gap-5\",\n          )}\n        >\n          {children}\n        </div>\n      </section>\n    </Context.Provider>\n  );\n}\n\nexport function FileAnalysisHeader({ className, ...props }: ComponentProps<\"header\">) {\n  useAnalysis(\"FileAnalysisHeader\");\n  return (\n    <header\n      data-slot=\"file-analysis-header\"\n      className={cn(\n        \"flex min-w-0 flex-wrap items-end justify-between gap-3 @min-[720px]:col-span-full\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function FileAnalysisHeading({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"file-analysis-heading\"\n      className={cn(\"grid min-w-0 flex-[1_1_240px] gap-1\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function FileAnalysisTitle({ className, ...props }: ComponentProps<\"h2\">) {\n  const { id, variant } = useAnalysis(\"FileAnalysisTitle\");\n  return (\n    <h2\n      data-slot=\"file-analysis-title\"\n      className={cn(\n        \"m-0 font-semibold tracking-[-0.01em] wrap-anywhere\",\n        variant === \"compact\" ? \"text-[15px]/5\" : \"text-lg/6\",\n        className,\n      )}\n      {...props}\n      id={`${id}-title`}\n    />\n  );\n}\n\nexport function FileAnalysisDescription({ className, ...props }: ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"file-analysis-description\"\n      className={cn(\"m-0 text-pretty text-muted-foreground\", className)}\n      {...props}\n    />\n  );\n}\n\nconst fileAnalysisActionVariants = cva(\n  \"cursor-pointer gap-1.5 rounded-full border-0 py-0 transition-[background-color,filter,scale] duration-[120ms,120ms,140ms] ease-[ease-out,ease-out,cubic-bezier(0.23,1,0.32,1)] focus-visible:ring-0 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring active:not-disabled:scale-[0.97] disabled:cursor-not-allowed disabled:opacity-50 motion-reduce:transition-none motion-reduce:active:not-disabled:scale-100\",\n  {\n    variants: {\n      emphasis: {\n        primary: \"bg-primary text-primary-foreground hover:bg-primary hover:brightness-[1.08]\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklab,var(--secondary)_85%,var(--foreground))]\",\n      },\n      size: {\n        default: \"h-7.5 px-[13px] text-[12.5px] has-[>svg]:px-[13px]\",\n        compact: \"h-6.5 px-2.5 text-[12px] has-[>svg]:px-2.5\",\n      },\n    },\n  },\n);\n\nexport function FileAnalysisAction({\n  emphasis = \"secondary\",\n  type = \"button\",\n  className,\n  ...props\n}: ComponentProps<\"button\"> & { emphasis?: \"primary\" | \"secondary\" }) {\n  const { variant } = useAnalysis(\"FileAnalysisAction\");\n  return (\n    <Button\n      data-slot=\"file-analysis-action\"\n      variant={emphasis === \"primary\" ? \"default\" : \"secondary\"}\n      data-emphasis={emphasis}\n      className={cn(\n        fileAnalysisActionVariants({\n          emphasis,\n          size: variant === \"compact\" ? \"compact\" : \"default\",\n        }),\n        className,\n      )}\n      {...props}\n      type={type}\n    />\n  );\n}\n\nfunction Panel({\n  part,\n  slot,\n  className,\n  ...props\n}: ComponentProps<\"section\"> & { part: string; slot: string }) {\n  const { variant } = useAnalysis(part);\n  const id = useId();\n  return (\n    <PanelContext.Provider value={id}>\n      <section\n        aria-labelledby={id}\n        data-slot={slot}\n        className={cn(\n          \"grid min-w-0 content-start border bg-card\",\n          variant === \"compact\" ? \"gap-2 rounded-xl p-3\" : \"gap-3 rounded-[14px] p-4\",\n          className,\n        )}\n        {...props}\n      />\n    </PanelContext.Provider>\n  );\n}\n\n/** The analysed files and their extraction status. */\nexport function FileAnalysisFiles(props: ComponentProps<\"section\">) {\n  return <Panel {...props} part=\"FileAnalysisFiles\" slot=\"file-analysis-files\" />;\n}\n\n/** What the analysis found. Compose findings with citations inside it. */\nexport function FileAnalysisFindings(props: ComponentProps<\"section\">) {\n  return <Panel {...props} part=\"FileAnalysisFindings\" slot=\"file-analysis-findings\" />;\n}\n\nexport function FileAnalysisPanelTitle({ className, ...props }: ComponentProps<\"h3\">) {\n  const id = usePanel(\"FileAnalysisPanelTitle\");\n  return (\n    <h3\n      data-slot=\"file-analysis-panel-title\"\n      className={cn(\"m-0 text-[13px]/[18px] font-medium\", className)}\n      {...props}\n      id={id}\n    />\n  );\n}\n\n/** Overall extraction status. Compose Progress Summary parts inside it. */\nexport function FileAnalysisExtraction(props: Omit<ProgressSummaryProps, \"variant\">) {\n  const { variant } = useAnalysis(\"FileAnalysisExtraction\");\n  return (\n    <ProgressSummary\n      data-slot=\"file-analysis-extraction\"\n      {...props}\n      variant={progressVariants[variant]}\n    />\n  );\n}\n\nexport function FileAnalysisFileList({ className, ...props }: ComponentProps<\"ul\">) {\n  const id = usePanel(\"FileAnalysisFileList\");\n  const { variant } = useAnalysis(\"FileAnalysisFileList\");\n  return (\n    <ul\n      aria-labelledby={id}\n      data-slot=\"file-analysis-file-list\"\n      className={cn(\n        \"m-0 min-w-0 list-none flex-wrap p-0\",\n        variant === \"split\" ? \"grid\" : \"flex\",\n        variant === \"compact\" ? \"gap-1.5\" : \"gap-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** One file. Compose Attachment parts inside it. */\nexport function FileAnalysisFile(props: Omit<AttachmentProps, \"variant\">) {\n  const { variant } = useAnalysis(\"FileAnalysisFile\");\n  return (\n    <li data-slot=\"file-analysis-file\" className=\"min-w-0 max-w-full\">\n      <Attachment {...props} variant={attachmentVariants[variant]} />\n    </li>\n  );\n}\n\nexport function FileAnalysisFindingList({ className, ...props }: ComponentProps<\"ol\">) {\n  const id = usePanel(\"FileAnalysisFindingList\");\n  const { variant } = useAnalysis(\"FileAnalysisFindingList\");\n  return (\n    <ol\n      aria-labelledby={id}\n      data-slot=\"file-analysis-finding-list\"\n      className={cn(\n        \"m-0 grid min-w-0 list-none p-0\",\n        variant === \"stacked\"\n          ? \"grid-cols-[repeat(auto-fit,minmax(min(100%,260px),1fr))]\"\n          : \"grid-cols-[minmax(0,1fr)]\",\n        variant === \"compact\" ? \"gap-1.5\" : \"gap-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport type FileAnalysisFindingProps = ComponentProps<\"li\"> & {\n  tone?: FileAnalysisFindingTone;\n  /** Replaces the written tone label. */\n  toneLabel?: string;\n};\n\nexport function FileAnalysisFinding({\n  tone = \"neutral\",\n  toneLabel,\n  children,\n  className,\n  ...props\n}: FileAnalysisFindingProps) {\n  const { variant } = useAnalysis(\"FileAnalysisFinding\");\n  const details = tones[tone];\n  return (\n    <li\n      data-slot=\"file-analysis-finding\"\n      data-tone={tone}\n      className={cn(\n        \"grid min-w-0 content-start justify-items-start bg-[color-mix(in_oklab,var(--muted)_70%,var(--card))]\",\n        \"animate-in fade-in-0 slide-in-from-bottom-1 duration-240 ease-out-quint fill-mode-both nth-2:[--tw-animation-delay:40ms] nth-3:[--tw-animation-delay:80ms] nth-[n+4]:[--tw-animation-delay:120ms] motion-reduce:animate-none\",\n        variant === \"compact\"\n          ? \"gap-1 rounded-lg px-2.5 py-2\"\n          : \"gap-1.5 rounded-[10px] px-3.5 py-3\",\n        className,\n      )}\n      {...props}\n    >\n      <span\n        className={cn(\n          \"inline-flex items-center gap-1.25 rounded-full px-2 text-[11.5px]/5 font-medium\",\n          details.className,\n        )}\n      >\n        <span aria-hidden=\"true\" className=\"size-1.5 rounded-full bg-current\" />\n        {toneLabel ?? details.label}\n      </span>\n      {children}\n    </li>\n  );\n}\n\nexport function FileAnalysisFindingTitle({ className, ...props }: ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"file-analysis-finding-title\"\n      className={cn(\"m-0 font-medium wrap-anywhere\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function FileAnalysisFindingDetail({ className, ...props }: ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"file-analysis-finding-detail\"\n      className={cn(\"m-0 text-muted-foreground wrap-anywhere\", className)}\n      {...props}\n    />\n  );\n}\n\n/** A marker that points to the page or cell a finding came from. Compose Citation parts inside it. */\nexport function FileAnalysisCitation(props: Omit<CitationProps, \"variant\">) {\n  const { variant } = useAnalysis(\"FileAnalysisCitation\");\n  return (\n    <Citation data-slot=\"file-analysis-citation\" {...props} variant={citationVariants[variant]} />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/uai/file-analysis.tsx"
    }
  ],
  "categories": [
    "ai",
    "files",
    "analysis"
  ],
  "type": "registry:block"
}