{"version":3,"file":"MediaRenderer-DIDEPlE7.cjs","names":["Video","Image"],"sources":["../../widgets/src/components/MediaRenderer.tsx"],"sourcesContent":["import type React from \"react\";\nimport { useRef, useCallback } from \"react\";\nimport { Image, Video } from \"lucide-react\";\nimport type { ShareableItem, WidgetSchema } from \"@fluid-app/portal-core/types\";\n\nconst inlineVideoPlaybackAttributes = {\n  playsInline: true,\n  \"webkit-playsinline\": \"true\",\n} as const;\n\nexport type MediaRendererProps = {\n  src?: string | undefined;\n  alt?: string | undefined;\n  objectFit?: \"contain\" | \"cover\" | \"fill\" | \"none\" | undefined;\n  focusPoint?: string | undefined;\n  mediaType?: \"image\" | \"video\" | undefined;\n  poster?: string | undefined;\n  autoplay?: boolean | undefined;\n  loop?: boolean | undefined;\n  muted?: boolean | undefined;\n  controls?: boolean | undefined;\n};\n\nexport function MediaRenderer({\n  src,\n  alt = \"Media\",\n  objectFit = \"cover\",\n  focusPoint,\n  mediaType = \"image\",\n  poster,\n  autoplay = false,\n  loop = false,\n  muted = true,\n  controls = true,\n}: MediaRendererProps): React.JSX.Element {\n  const hasValidSrc = src && src.trim() !== \"\";\n  const objectPosition = focusPoint?.replace(\"-\", \" \");\n\n  if (!hasValidSrc) {\n    const Icon = mediaType === \"video\" ? Video : Image;\n    const label =\n      mediaType === \"video\" ? \"No video selected\" : \"No image selected\";\n    return (\n      <div className=\"bg-muted @container flex h-full w-full items-center justify-center\">\n        <div className=\"text-muted-foreground flex flex-col items-center gap-2\">\n          <Icon className=\"size-5 @[10rem]:size-12\" />\n          <p className=\"hidden text-sm @[10rem]:block\">{label}</p>\n        </div>\n      </div>\n    );\n  }\n\n  const isContainMode = objectFit === \"contain\";\n  const blurSrc = isContainMode ? `${src}?tr=bl-50` : undefined;\n\n  if (mediaType === \"video\") {\n    return (\n      <VideoRenderer\n        src={src}\n        poster={poster}\n        controls={controls}\n        autoplay={autoplay}\n        loop={loop}\n        muted={muted}\n        objectFit={objectFit}\n        objectPosition={objectPosition}\n        isContainMode={isContainMode}\n        blurSrc={blurSrc}\n      />\n    );\n  }\n\n  if (isContainMode) {\n    return (\n      <div className=\"relative h-full w-full overflow-hidden\">\n        <img\n          src={blurSrc}\n          alt=\"\"\n          className=\"absolute inset-0 h-full w-full object-cover\"\n          aria-hidden=\"true\"\n        />\n        <div\n          className=\"pointer-events-none absolute inset-0 bg-black/30\"\n          aria-hidden=\"true\"\n        />\n        <img\n          src={src}\n          alt={alt}\n          className=\"relative z-10 h-full w-full object-contain\"\n        />\n      </div>\n    );\n  }\n\n  return (\n    <img\n      src={src}\n      alt={alt}\n      className={`h-full w-full object-${objectFit}`}\n      style={objectPosition ? { objectPosition } : undefined}\n    />\n  );\n}\n\nfunction VideoRenderer({\n  src,\n  poster,\n  controls,\n  autoplay,\n  loop,\n  muted,\n  objectFit,\n  objectPosition,\n  isContainMode,\n  blurSrc,\n}: {\n  src: string;\n  poster: string | undefined;\n  controls: boolean;\n  autoplay: boolean;\n  loop: boolean;\n  muted: boolean;\n  objectFit: string;\n  objectPosition: string | undefined;\n  isContainMode: boolean;\n  blurSrc: string | undefined;\n}): React.JSX.Element {\n  const videoRef = useRef<HTMLVideoElement>(null);\n\n  // Safari fallback: the loop attribute doesn't always work with autoplay,\n  // so replay the video when it ends.\n  const handleEnded = useCallback(() => {\n    if (!loop) return;\n    const video = videoRef.current;\n    if (!video) return;\n    video.currentTime = 0;\n    video.play().catch(() => {});\n  }, [loop]);\n\n  const noInteraction = !controls ? \"pointer-events-none\" : \"\";\n\n  const blurredBgSrc = isContainMode\n    ? poster\n      ? `${poster}?tr=bl-50`\n      : blurSrc\n    : undefined;\n\n  if (isContainMode && blurredBgSrc) {\n    return (\n      <div className=\"relative h-full w-full overflow-hidden\">\n        <img\n          src={blurredBgSrc}\n          alt=\"\"\n          className=\"absolute inset-0 h-full w-full object-cover\"\n          aria-hidden=\"true\"\n        />\n        <div\n          className=\"pointer-events-none absolute inset-0 bg-black/30\"\n          aria-hidden=\"true\"\n        />\n        <video\n          ref={videoRef}\n          src={src}\n          poster={poster}\n          controls={controls}\n          autoPlay={autoplay}\n          loop={loop}\n          muted={muted}\n          {...inlineVideoPlaybackAttributes}\n          onEnded={handleEnded}\n          className={`relative z-10 h-full w-full object-contain ${noInteraction}`}\n        >\n          Your browser does not support the video tag.\n        </video>\n      </div>\n    );\n  }\n\n  return (\n    <video\n      ref={videoRef}\n      src={src}\n      poster={poster}\n      controls={controls}\n      autoPlay={autoplay}\n      loop={loop}\n      muted={muted}\n      {...inlineVideoPlaybackAttributes}\n      onEnded={handleEnded}\n      className={`h-full w-full object-${objectFit} ${noInteraction}`}\n      style={objectPosition ? { objectPosition } : undefined}\n    >\n      Your browser does not support the video tag.\n    </video>\n  );\n}\n\n/**\n * Converts a ShareableItem to MediaRenderer props.\n * Replaces the createWidgetFromShareable → ScreenRenderer pattern.\n */\nexport function getMediaPropsFromShareable(\n  item: ShareableItem,\n): MediaRendererProps {\n  const isVideo = item.kind === \"video\" || !!item.videoUrl;\n\n  if (isVideo && item.videoUrl) {\n    const result: MediaRendererProps = {\n      mediaType: \"video\",\n      src: item.videoUrl,\n      alt: item.title ?? \"Video\",\n    };\n    if (item.imageUrl != null) result.poster = item.imageUrl;\n    return result;\n  }\n\n  const result: MediaRendererProps = {\n    mediaType: \"image\",\n    alt: item.title || \"Image\",\n    objectFit: \"cover\",\n  };\n  if (item.imageUrl != null) result.src = item.imageUrl;\n  return result;\n}\n\n/**\n * Extracts media props from an ImageWidget/VideoWidget schema.\n * Used by CarouselWidget for slide.content rendering.\n */\nexport function getMediaPropsFromWidgetSchema(\n  schema: WidgetSchema,\n): MediaRendererProps {\n  const props = schema.props;\n\n  const resource = props.resource as ShareableItem | undefined;\n  const useCustomUrl = props.useCustomUrl as boolean | undefined;\n  const focusPoint = props.focusPoint as string | undefined;\n  const displayFit = props.displayFit as\n    | MediaRendererProps[\"objectFit\"]\n    | undefined;\n\n  if (schema.type === \"VideoWidget\") {\n    const result: MediaRendererProps = {\n      mediaType: \"video\",\n      autoplay: (props.autoplay as boolean | undefined) ?? false,\n      loop: (props.loop as boolean | undefined) ?? false,\n      muted: (props.muted as boolean | undefined) ?? true,\n      controls: (props.controls as boolean | undefined) ?? true,\n      objectFit: displayFit ?? \"cover\",\n      focusPoint,\n    };\n    if (useCustomUrl) {\n      if (typeof props.src === \"string\") result.src = props.src;\n      if (typeof props.poster === \"string\") result.poster = props.poster;\n    } else if (resource) {\n      if (resource.videoUrl) result.src = resource.videoUrl;\n      else if (typeof props.src === \"string\") result.src = props.src;\n      if (resource.imageUrl) result.poster = resource.imageUrl;\n      else if (typeof props.poster === \"string\") result.poster = props.poster;\n    } else {\n      if (typeof props.src === \"string\") result.src = props.src;\n      if (typeof props.poster === \"string\") result.poster = props.poster;\n    }\n    return result;\n  }\n\n  const result: MediaRendererProps = {\n    mediaType: \"image\",\n    alt: (props.alt as string | undefined) ?? \"Image\",\n    objectFit: displayFit ?? \"cover\",\n    focusPoint,\n  };\n  if (useCustomUrl) {\n    if (typeof props.src === \"string\") result.src = props.src;\n  } else if (resource) {\n    if (resource.imageUrl) result.src = resource.imageUrl;\n    else if (typeof props.src === \"string\") result.src = props.src;\n    if (resource.title) result.alt = resource.title;\n  } else {\n    if (typeof props.src === \"string\") result.src = props.src;\n  }\n  return result;\n}\n"],"mappings":"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"}