{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"media-viewer","type":"registry:ui","title":"MediaViewer","description":"Picks the right viewer (image \/ video \/ audio \/ PDF) from a mime or src; download fallback. Exports resolveMediaType.","package":"react-fancy","dependencies":[],"registryDependencies":["image-viewer","video-viewer","audio-viewer","pdf-viewer"],"files":[{"path":"components\/fancy\/media-viewer\/MediaFallback.tsx","content":"import { cn } from \"..\/..\/utils\/cn\";\n\ninterface MediaFallbackProps {\n  src: string;\n  mime?: string;\n  label?: string;\n  className?: string;\n}\n\n\/** Filename guess from a URL, ignoring query\/hash and `data:`\/`blob:` URLs. *\/\nfunction basename(src: string): string | null {\n  if (\/^(data|blob):\/i.test(src)) return null;\n  const seg = src.split(\/[?#]\/, 1)[0].split(\"\/\").pop();\n  return seg ? decodeURIComponent(seg) : null;\n}\n\n\/**\n * Shown by `<MediaViewer>` when a source isn't a previewable media type \u2014 a\n * small themed card naming the file and offering a download.\n *\/\nexport function MediaFallback({ src, mime, label, className }: MediaFallbackProps) {\n  const name = label || basename(src);\n  return (\n    <div\n      data-react-fancy-media-fallback=\"\"\n      className={cn(\n        \"flex flex-col items-center justify-center gap-2 rounded-lg border border-zinc-200 bg-white p-6 text-center dark:border-zinc-700 dark:bg-zinc-900\",\n        className,\n      )}\n    >\n      <span className=\"text-sm font-medium text-zinc-700 dark:text-zinc-200\">\n        No preview available\n      <\/span>\n      {(name || mime) && (\n        <span className=\"max-w-full truncate text-xs text-zinc-500 dark:text-zinc-400\">\n          {name}\n          {name && mime ? \" \u00b7 \" : \"\"}\n          {mime}\n        <\/span>\n      )}\n      <a\n        href={src}\n        download={name ?? undefined}\n        className=\"text-xs font-medium text-blue-600 hover:underline dark:text-blue-400\"\n      >\n        Download\n      <\/a>\n    <\/div>\n  );\n}\n","type":"registry:ui","target":"components\/fancy\/media-viewer\/MediaFallback.tsx"},{"path":"components\/fancy\/media-viewer\/MediaViewer.tsx","content":"import { forwardRef } from \"react\";\nimport { resolveMediaType } from \"..\/..\/utils\/media-type\";\nimport { ImageViewer } from \"..\/ImageViewer\";\nimport { VideoViewer } from \"..\/VideoViewer\";\nimport { AudioViewer } from \"..\/AudioViewer\";\nimport { PdfViewer } from \"..\/PdfViewer\";\nimport { MediaFallback } from \".\/MediaFallback\";\nimport type { MediaViewerProps } from \".\/MediaViewer.types\";\n\n\/**\n * Renders the right viewer for a media file, picked from its `mime` (preferred)\n * or `src` \u2014 image, video, audio, or PDF \u2014 and a download fallback for anything\n * else. SVG renders through `<ImageViewer>`. The single entry point for showing\n * a file from a tree\/workspace when you don't know its type ahead of time.\n *\n * ```tsx\n * <MediaViewer src={url} mime=\"image\/png\" alt=\"logo.png\" style={{ height: 320 }} \/>\n * ```\n *\/\nexport const MediaViewer = forwardRef<HTMLDivElement, MediaViewerProps>(\n  (\n    {\n      src,\n      mime,\n      alt,\n      kind,\n      imageProps,\n      videoProps,\n      audioProps,\n      pdfProps,\n      fallback,\n      onError,\n      className,\n      style,\n    },\n    ref,\n  ) => {\n    const resolved = kind ?? resolveMediaType({ mime, src });\n\n    switch (resolved) {\n      case \"image\":\n        return (\n          <ImageViewer\n            ref={ref}\n            src={src}\n            alt={alt ?? \"\"}\n            onError={onError}\n            className={className}\n            style={style}\n            {...imageProps}\n          \/>\n        );\n      case \"video\":\n        return (\n          <VideoViewer\n            ref={ref}\n            src={src}\n            onError={onError}\n            className={className}\n            style={style}\n            {...videoProps}\n          \/>\n        );\n      case \"audio\":\n        return (\n          <AudioViewer\n            ref={ref}\n            src={src}\n            title={alt}\n            onError={onError}\n            className={className}\n            style={style}\n            {...audioProps}\n          \/>\n        );\n      case \"pdf\":\n        return (\n          <PdfViewer\n            ref={ref}\n            src={src}\n            title={alt}\n            className={className}\n            style={style}\n            {...pdfProps}\n          \/>\n        );\n      default:\n        return (\n          <div\n            ref={ref}\n            data-react-fancy-media-viewer=\"\"\n            className={className}\n            style={style}\n          >\n            {fallback ?? <MediaFallback src={src} mime={mime} label={alt} \/>}\n          <\/div>\n        );\n    }\n  },\n);\n\nMediaViewer.displayName = \"MediaViewer\";\n","type":"registry:ui","target":"components\/fancy\/media-viewer\/MediaViewer.tsx"},{"path":"components\/fancy\/media-viewer\/MediaViewer.types.ts","content":"import type { CSSProperties, ReactNode } from \"react\";\nimport type { MediaKind } from \"..\/..\/utils\/media-type\";\nimport type { ImageViewerProps } from \"..\/ImageViewer\/ImageViewer.types\";\nimport type { VideoViewerProps } from \"..\/VideoViewer\/VideoViewer.types\";\nimport type { AudioViewerProps } from \"..\/AudioViewer\/AudioViewer.types\";\nimport type { PdfViewerProps } from \"..\/PdfViewer\/PdfViewer.types\";\n\nexport interface MediaViewerProps {\n  \/** Media source \u2014 an `http(s):`, `data:`, or `blob:` URL. *\/\n  src: string;\n  \/**\n   * MIME type \u2014 preferred for picking the viewer. When omitted, the kind is\n   * sniffed from `src` (extension or `data:` URI). Always pass `mime` with a\n   * bare `blob:` URL, which carries no type of its own.\n   *\/\n  mime?: string;\n  \/** Alt text \/ label, forwarded as image alt and audio\/PDF title. *\/\n  alt?: string;\n  \/** Force a specific viewer, bypassing detection. *\/\n  kind?: MediaKind;\n  \/** Extra props for the underlying `<ImageViewer>`. *\/\n  imageProps?: Partial<Omit<ImageViewerProps, \"src\" | \"alt\">>;\n  \/** Extra props for the underlying `<VideoViewer>`. *\/\n  videoProps?: Partial<Omit<VideoViewerProps, \"src\">>;\n  \/** Extra props for the underlying `<AudioViewer>`. *\/\n  audioProps?: Partial<Omit<AudioViewerProps, \"src\">>;\n  \/** Extra props for the underlying `<PdfViewer>`. *\/\n  pdfProps?: Partial<Omit<PdfViewerProps, \"src\">>;\n  \/** Rendered for unknown \/ unpreviewable types (default: a download card). *\/\n  fallback?: ReactNode;\n  \/** Fired when the chosen viewer reports a load error. *\/\n  onError?: () => void;\n  \/** Additional CSS classes, forwarded to the chosen viewer. *\/\n  className?: string;\n  \/** Inline styles, forwarded to the chosen viewer. *\/\n  style?: CSSProperties;\n}\n","type":"registry:ui","target":"components\/fancy\/media-viewer\/MediaViewer.types.ts"},{"path":"components\/fancy\/media-viewer\/index.ts","content":"export { MediaViewer } from \".\/MediaViewer\";\nexport type { MediaViewerProps } from \".\/MediaViewer.types\";\n","type":"registry:ui","target":"components\/fancy\/media-viewer\/index.ts"}]}