{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"file-viewer","type":"registry:ui","title":"FileViewer","description":"Unified file viewer \u2014 CodeEditor for text, react-fancy MediaViewer for image \/ video \/ audio \/ PDF; resolveFileKind decides.","package":"fancy-code","dependencies":["@particle-academy\/react-fancy"],"registryDependencies":["code-editor","file-type"],"files":[{"path":"components\/fancy\/file-viewer\/FileViewer.tsx","content":"import { cn, MediaViewer } from \"@particle-academy\/react-fancy\";\nimport { CodeEditor } from \"..\/CodeEditor\";\nimport { resolveFileKind } from \"..\/..\/file-type\";\nimport type { FileViewerProps } from \".\/FileViewer.types\";\n\n\/**\n * A unified file viewer: renders text files in a read-only `CodeEditor` (with\n * syntax highlighting picked from the filename) and media files \u2014 image, video,\n * audio, PDF \u2014 via react-fancy's `<MediaViewer>`. The text-vs-media decision is\n * made by {@link resolveFileKind}; pass `fileKind` to override it, or call\n * `resolveFileKind` yourself to branch your own chrome (tabs, save buttons).\n *\n * Give the container a height for media files (PDF\/image\/video fill their box).\n *\n * ```tsx\n * \/\/ text\n * <FileViewer filename=\"app.tsx\" value={source} \/>\n * \/\/ media\n * <FileViewer filename=\"logo.png\" src={url} style={{ height: 360 }} \/>\n * ```\n *\/\nexport function FileViewer({\n  filename,\n  mime,\n  src,\n  value = \"\",\n  onChange,\n  readOnly = true,\n  theme = \"auto\",\n  fileKind,\n  lineNumbers = true,\n  wordWrap = false,\n  minHeight,\n  maxHeight,\n  mediaProps,\n  className,\n  style,\n}: FileViewerProps) {\n  const resolved = fileKind ?? resolveFileKind({ filename, mime });\n\n  if (resolved.kind === \"media\") {\n    \/\/ Image \/ video \/ PDF fill the sized wrapper so a height set on FileViewer\n    \/\/ reaches the viewer; audio is a fixed-height card and shouldn't stretch.\n    const { className: mediaClassName, ...restMediaProps } = mediaProps ?? {};\n    const fill = resolved.mediaKind !== \"audio\";\n    return (\n      <div\n        data-fancy-file-viewer=\"\"\n        data-kind=\"media\"\n        data-media-kind={resolved.mediaKind}\n        className={cn(\"overflow-hidden\", className)}\n        style={style}\n      >\n        <MediaViewer\n          src={src ?? \"\"}\n          mime={mime}\n          alt={filename}\n          kind={resolved.mediaKind}\n          className={cn(fill && \"h-full w-full\", mediaClassName)}\n          {...restMediaProps}\n        \/>\n      <\/div>\n    );\n  }\n\n  return (\n    <div\n      data-fancy-file-viewer=\"\"\n      data-kind=\"text\"\n      className={className}\n      style={style}\n    >\n      <CodeEditor\n        value={value}\n        onChange={readOnly ? undefined : onChange}\n        language={resolved.language}\n        theme={theme}\n        readOnly={readOnly}\n        lineNumbers={lineNumbers}\n        wordWrap={wordWrap}\n        minHeight={minHeight}\n        maxHeight={maxHeight}\n      >\n        <CodeEditor.Panel \/>\n      <\/CodeEditor>\n    <\/div>\n  );\n}\n\nFileViewer.displayName = \"FileViewer\";\n","type":"registry:ui","target":"components\/fancy\/file-viewer\/FileViewer.tsx"},{"path":"components\/fancy\/file-viewer\/FileViewer.types.ts","content":"import type { CSSProperties } from \"react\";\nimport type { MediaViewerProps } from \"@particle-academy\/react-fancy\";\nimport type { FileKind } from \"..\/..\/file-type\";\n\nexport interface FileViewerProps {\n  \/**\n   * File name or path \u2014 used to detect the file kind \/ editor language, and as\n   * a label \/ alt text. Pass `mime` too when you have it (it's preferred).\n   *\/\n  filename?: string;\n  \/** MIME type \u2014 preferred over `filename` for detection. *\/\n  mime?: string;\n  \/**\n   * Source URL for **media** files (image \/ video \/ audio \/ PDF) \u2014\n   * `http(s):` \/ `data:` \/ `blob:`. Required when the file resolves to media.\n   *\/\n  src?: string;\n  \/** Text content for **text** files. *\/\n  value?: string;\n  \/** Called when text content changes (ignored when `readOnly`). *\/\n  onChange?: (value: string) => void;\n  \/**\n   * Read-only. Defaults to `true` \u2014 `FileViewer` is a viewer. Media is always\n   * read-only regardless. Set `false` (with `onChange`) to edit text.\n   *\/\n  readOnly?: boolean;\n  \/** Editor theme for text: `\"light\" | \"dark\" | \"auto\"` or a registered name (default `\"auto\"`). *\/\n  theme?: string;\n  \/** Force the file kind, bypassing detection. *\/\n  fileKind?: FileKind;\n  \/** Show line numbers in the text editor (default `true`). *\/\n  lineNumbers?: boolean;\n  \/** Wrap long lines in the text editor (default `false`). *\/\n  wordWrap?: boolean;\n  \/** Editor height bounds (px). *\/\n  minHeight?: number;\n  maxHeight?: number;\n  \/** Extra props forwarded to `<MediaViewer>` for media files. *\/\n  mediaProps?: Partial<Omit<MediaViewerProps, \"src\" | \"mime\" | \"alt\">>;\n  className?: string;\n  style?: CSSProperties;\n}\n","type":"registry:ui","target":"components\/fancy\/file-viewer\/FileViewer.types.ts"},{"path":"components\/fancy\/file-viewer\/index.ts","content":"export { FileViewer } from \".\/FileViewer\";\nexport type { FileViewerProps } from \".\/FileViewer.types\";\n","type":"registry:ui","target":"components\/fancy\/file-viewer\/index.ts"}]}