<!-- Generated by SpecStory -->

# 抽取 live photo 逻辑为组件 (2025-06-04 01:08:08)

_**User**_

这里吧 LIVE photo 的逻辑单独抽一个组件出来

---

_**Assistant**_



---

我来帮你把 Live Photo 的逻辑抽取成一个独立的组件。首先分析一下 Live Photo 相关的逻辑，然后创建新组件。

---

```diff
- 
+ import { m, useAnimationControls } from 'motion/react'
+ import { useCallback, useEffect, useRef, useState } from 'react'
+ 
+ import { clsxm } from '~/lib/cn'
+ import { isMobileDevice } from '~/lib/device-viewport'
+ import { ImageLoaderManager } from '~/lib/image-loader-manager'
+ import { isWebCodecsSupported } from '~/lib/video-converter'
+ 
+ import type { LoadingIndicatorRef } from './LoadingIndicator'
+ 
+ interface LivePhotoProps {
+   /** Live Photo 视频 URL */
+   videoUrl: string
+   /** 图片加载管理器实例 */
+   imageLoaderManager: ImageLoaderManager
+   /** 加载指示器引用 */
+   loadingIndicatorRef: React.RefObject<LoadingIndicatorRef>
+   /** 是否是当前图片 */
+   isCurrentImage: boolean
+   /** 自定义样式类名 */
+   className?: string
+ }
+ 
+ export const LivePhoto = ({
+   videoUrl,
+   imageLoaderManager,
+   loadingIndicatorRef,
+   isCurrentImage,
+   className,
+ }: LivePhotoProps) => {
+   // Live Photo 相关状态
+   const [isPlayingLivePhoto, setIsPlayingLivePhoto] = useState(false)
+   const [livePhotoVideoLoaded, setLivePhotoVideoLoaded] = useState(false)
+   const [isConvertingVideo, setIsConvertingVideo] = useState(false)
+   const [conversionMethod, setConversionMethod] = useState<string>('')
+ 
+   const videoRef = useRef<HTMLVideoElement>(null)
+   const videoAnimateController = useAnimationControls()
+ 
+   // Live Photo hover 相关
+   const hoverTimerRef = useRef<NodeJS.Timeout | null>(null)
+   const longPressTimerRef = useRef<NodeJS.Timeout | null>(null)
+   const [isLongPressing, setIsLongPressing] = useState(false)
+ 
+   useEffect(() => {
+     if (!isCurrentImage || livePhotoVideoLoaded || isConvertingVideo || !videoRef.current) {
+       return
+     }
+ 
+     setIsConvertingVideo(true)
+ 
+     const processVideo = async () => {
+       try {
+         const videoResult = await imageLoaderManager.processLivePhotoVideo(
+           videoUrl,
+           videoRef.current!,
+           {
+             onLoadingStateUpdate: (state) => {
+               loadingIndicatorRef.current?.updateLoadingState(state)
+             },
+           },
+         )
+ 
+         if (videoResult.conversionMethod) {
+           setConversionMethod(videoResult.conversionMethod)
+         }
+ 
+         setLivePhotoVideoLoaded(true)
+       } catch (videoError) {
+         console.error('Failed to process Live Photo video:', videoError)
+       } finally {
+         setIsConvertingVideo(false)
+       }
+     }
+ 
+     processVideo()
+   }, [
+     isCurrentImage,
+     livePhotoVideoLoaded,
+     isConvertingVideo,
+     videoUrl,
+     imageLoaderManager,
+     loadingIndicatorRef,
+   ])
+ 
+   // 清理函数
+   useEffect(() => {
+     return () => {
+       // Clean up timers
+       if (hoverTimerRef.current) {
+         clearTimeout(hoverTimerRef.current)
+         hoverTimerRef.current = null
+       }
+       if (longPressTimerRef.current) {
+         clearTimeout(longPressTimerRef.current)
+         longPressTimerRef.current = null
+       }
+     }
+   }, [])
+ 
+   // 重置状态（当不是当前图片时）
+   useEffect(() => {
+     if (!isCurrentImage) {
+       setIsPlayingLivePhoto(false)
+       setLivePhotoVideoLoaded(false)
+       setIsConvertingVideo(false)
+       setConversionMethod('')
+       setIsLongPressing(false)
+ 
+       // Clean up timers
+       if (hoverTimerRef.current) {
+         clearTimeout(hoverTimerRef.current)
+         hoverTimerRef.current = null
+       }
+       if (longPressTimerRef.current) {
+         clearTimeout(longPressTimerRef.current)
+         longPressTimerRef.current = null
+       }
+ 
+       // Reset video animation
+       videoAnimateController.set({ opacity: 0 })
+     }
+   }, [isCurrentImage, videoAnimateController])
+ 
+   // Live Photo hover 处理
+   const handleBadgeMouseEnter = useCallback(() => {
+     if (
+       !livePhotoVideoLoaded ||
+       isPlayingLivePhoto ||
+       isConvertingVideo
+     )
+       return
+ 
+     hoverTimerRef.current = setTimeout(async () => {
+       setIsPlayingLivePhoto(true)
+ 
+       // 开始淡入动画
+       await videoAnimateController.start({
+         opacity: 1,
+         transition: { duration: 0.15, ease: 'easeOut' },
+       })
+ 
+       const video = videoRef.current
+       if (video) {
+         video.currentTime = 0
+         video.play()
+       }
+     }, 200) // 200ms hover 延迟
+   }, [
+     livePhotoVideoLoaded,
+     isPlayingLivePhoto,
+     isConvertingVideo,
+     videoAnimateController,
+   ])
+ 
+   const handleBadgeMouseLeave = useCallback(async () => {
+     if (hoverTimerRef.current) {
+       clearTimeout(hoverTimerRef.current)
+       hoverTimerRef.current = null
+     }
+ 
+     if (isPlayingLivePhoto) {
+       const video = videoRef.current
+       if (video) {
+         video.pause()
+         video.currentTime = 0
+       }
+ 
+       // 开始淡出动画
+       await videoAnimateController.start({
+         opacity: 0,
+         transition: { duration: 0.2, ease: 'easeIn' },
+       })
+ 
+       setIsPlayingLivePhoto(false)
+     }
+   }, [isPlayingLivePhoto, videoAnimateController])
+ 
+   // 视频播放结束处理
+   const handleVideoEnded = useCallback(async () => {
+     // 播放结束时淡出
+     await videoAnimateController.start({
+       opacity: 0,
+       transition: { duration: 0.2, ease: 'easeIn' },
+     })
+ 
+     setIsPlayingLivePhoto(false)
+   }, [videoAnimateController])
+ 
+   // Live Photo 长按处理（移动端）
+   const handleTouchStart = useCallback(
+     (e: React.TouchEvent) => {
+       if (
+         !livePhotoVideoLoaded ||
+         isPlayingLivePhoto ||
+         isConvertingVideo ||
+         e.touches.length > 1 // 多指触摸不触发长按
+       )
+         return
+ 
+       longPressTimerRef.current = setTimeout(async () => {
+         setIsLongPressing(true)
+         setIsPlayingLivePhoto(true)
+ 
+         // 开始淡入动画
+         await videoAnimateController.start({
+           opacity: 1,
+           transition: { duration: 0.15, ease: 'easeOut' },
+         })
+ 
+         const video = videoRef.current
+         if (video) {
+           video.currentTime = 0
+           video.play()
+         }
+       }, 500) // 500ms 长按延迟
+     },
+     [
+       livePhotoVideoLoaded,
+       isPlayingLivePhoto,
+       isConvertingVideo,
+       videoAnimateController,
+     ],
+   )
+ 
+   const handleTouchEnd = useCallback(async () => {
+     if (longPressTimerRef.current) {
+       clearTimeout(longPressTimerRef.current)
+       longPressTimerRef.current = null
+     }
+ 
+     if (isLongPressing && isPlayingLivePhoto) {
+       setIsLongPressing(false)
+ 
+       const video = videoRef.current
+       if (video) {
+         video.pause()
+         video.currentTime = 0
+       }
+ 
+       // 开始淡出动画
+       await videoAnimateController.start({
+         opacity: 0,
+         transition: { duration: 0.2, ease: 'easeIn' },
+       })
+ 
+       setIsPlayingLivePhoto(false)
+     }
+   }, [isLongPressing, isPlayingLivePhoto, videoAnimateController])
+ 
+   const handleTouchMove = useCallback(() => {
+     // 触摸移动时取消长按
+     if (longPressTimerRef.current) {
+       clearTimeout(longPressTimerRef.current)
+       longPressTimerRef.current = null
+     }
+   }, [])
+ 
+   return (
+     <>
+       {/* Live Photo 视频 */}
+       <m.video
+         ref={videoRef}
+         className={clsxm("absolute inset-0 z-10 h-full w-full object-contain", className)}
+         muted
+         playsInline
+         onEnded={handleVideoEnded}
+         onTouchStart={handleTouchStart}
+         onTouchEnd={handleTouchEnd}
+         onTouchMove={handleTouchMove}
+         initial={{ opacity: 0 }}
+         animate={videoAnimateController}
+       />
+ 
+       {/* Live Photo 标识 */}
+       <div
+         className={clsxm(
+           'absolute z-50 flex items-center space-x-1 rounded-xl bg-black/50 px-1 py-1 text-xs text-white cursor-pointer transition-all duration-200 hover:bg-black/70',
+           import.meta.env.DEV ? 'top-16 right-4' : 'top-12 lg:top-4 left-4',
+         )}
+         onMouseEnter={handleBadgeMouseEnter}
+         onMouseLeave={handleBadgeMouseLeave}
+         title={isMobileDevice ? '长按播放实况照片' : '悬浮播放实况照片'}
+       >
+         {isConvertingVideo ? (
+           <div className="flex items-center gap-1 px-1">
+             <i className="i-mingcute-loading-line animate-spin" />
+             <span>实况视频转换中</span>
+           </div>
+         ) : (
+           <>
+             <img
+               className="size-4 invert-100"
+               src="data:image/webp;base64, "
+             />
+             <span className="mr-1">实况</span>
+             {conversionMethod && (
+               <span className="rounded bg-white/20 px-1 text-xs">
+                 {conversionMethod === 'webcodecs' ? 'WebCodecs' : 'FFmpeg'}
+               </span>
+             )}
+           </>
+         )}
+       </div>
+ 
+       {/* 操作提示 */}
+       <div className="pointer-events-none absolute bottom-4 left-1/2 z-20 -translate-x-1/2 rounded bg-black/50 px-2 py-1 text-xs text-white opacity-0 duration-200 group-hover:opacity-50">
+         {isConvertingVideo
+           ? `正在使用 ${isWebCodecsSupported() ? 'WebCodecs' : 'FFmpeg'} 转换视频格式...`
+           : isMobileDevice
+             ? '长按播放实况照片 / 双击缩放'
+             : '悬浮实况标识播放 / 双击缩放'}
+       </div>
+     </>
+   )
+ } 
```

---

现在我需要更新 `ProgressiveImage.tsx` 文件，移除 Live Photo 相关的逻辑，并使用新创建的 `LivePhoto` 组件：

---

```diff
  import { ImageLoaderManager } from '~/lib/image-loader-manager'
  import { Spring } from '~/lib/spring'
- import { isWebCodecsSupported } from '~/lib/video-converter'
  
  import type { LoadingIndicatorRef } from './LoadingIndicator'
  import { LoadingIndicator } from './LoadingIndicator'
+ import { LivePhoto } from './LivePhoto'
  
  interface ProgressiveImageProps {
```

```diff
    const [highResLoaded, setHighResLoaded] = useState(false)
    const [error, setError] = useState(false)
- 
-   // Live Photo 相关状态
-   const [isPlayingLivePhoto, setIsPlayingLivePhoto] = useState(false)
-   const [livePhotoVideoLoaded, setLivePhotoVideoLoaded] = useState(false)
- 
-   const [isConvertingVideo, setIsConvertingVideo] = useState(false)
-   const [conversionMethod, setConversionMethod] = useState<string>('')
  
    const thumbnailRef = useRef<HTMLImageElement>(null)
-   const videoRef = useRef<HTMLVideoElement>(null)
    const transformRef = useRef<WebGLImageViewerRef>(null)
    const thumbnailAnimateController = useAnimationControls()
-   const videoAnimateController = useAnimationControls()
    const loadingIndicatorRef = useRef<LoadingIndicatorRef>(null)
    const imageLoaderManagerRef = useRef<ImageLoaderManager | null>(null)
- 
-   // Live Photo hover 相关
-   const hoverTimerRef = useRef<NodeJS.Timeout | null>(null)
-   const longPressTimerRef = useRef<NodeJS.Timeout | null>(null)
-   const [isLongPressing, setIsLongPressing] = useState(false)
  
    useEffect(() => {
```

```diff
        setBlobSrc(null)
        setError(false)
-       setIsPlayingLivePhoto(false)
-       setLivePhotoVideoLoaded(false)
- 
-       setIsConvertingVideo(false)
-       setConversionMethod('')
-       setIsLongPressing(false)
  
        // Reset loading indicator
        loadingIndicatorRef.current?.resetLoadingState()
- 
-       // Clean up timers
-       if (hoverTimerRef.current) {
-         clearTimeout(hoverTimerRef.current)
-         hoverTimerRef.current = null
-       }
-       if (longPressTimerRef.current) {
-         clearTimeout(longPressTimerRef.current)
-         longPressTimerRef.current = null
-       }
  
        // Reset transform when image changes
        if (transformRef.current) {
          transformRef.current.resetView()
        }
- 
-       // Reset video animation
-       videoAnimateController.set({ opacity: 0 })
      }
+     
      const loadImage = async () => {
        try {
```

```diff
          setBlobSrc(result.blobSrc)
          setHighResLoaded(true)
- 
-         // 处理 Live Photo 视频（在图片加载完成后）
-         if (
-           isLivePhoto &&
-           livePhotoVideoUrl &&
-           !livePhotoVideoLoaded &&
-           !isConvertingVideo &&
-           videoRef.current
-         ) {
-           setIsConvertingVideo(true)
- 
-           try {
-             const videoResult = await imageLoaderManager.processLivePhotoVideo(
-               livePhotoVideoUrl,
-               videoRef.current,
-               {
-                 onLoadingStateUpdate: (state) => {
-                   loadingIndicatorRef.current?.updateLoadingState(state)
-                 },
-               },
-             )
- 
-             if (videoResult.conversionMethod) {
-               setConversionMethod(videoResult.conversionMethod)
-             }
- 
-             setLivePhotoVideoLoaded(true)
-           } catch (videoError) {
-             console.error('Failed to process Live Photo video:', videoError)
-           } finally {
-             setIsConvertingVideo(false)
-           }
-         }
        } catch (loadError) {
          console.error('Failed to load image:', loadError)
          setError(true)
        }
      }
+     
      cleanup()
      loadImage()
```

```diff
      onError,
      isCurrentImage,
-     isLivePhoto,
-     livePhotoVideoUrl,
-     livePhotoVideoLoaded,
-     isConvertingVideo,
-     videoAnimateController,
-   ])
- 
-   // Live Photo hover 处理
-   const handleBadgeMouseEnter = useCallback(() => {
-     if (
-       !isLivePhoto ||
-       !livePhotoVideoLoaded ||
-       isPlayingLivePhoto ||
-       isConvertingVideo
-     )
-       return
- 
-     hoverTimerRef.current = setTimeout(async () => {
-       setIsPlayingLivePhoto(true)
- 
-       // 开始淡入动画
-       await videoAnimateController.start({
-         opacity: 1,
-         transition: { duration: 0.15, ease: 'easeOut' },
-       })
- 
-       const video = videoRef.current
-       if (video) {
-         video.currentTime = 0
-         video.play()
-       }
-     }, 200) // 200ms hover 延迟
-   }, [
-     isLivePhoto,
-     livePhotoVideoLoaded,
-     isPlayingLivePhoto,
-     isConvertingVideo,
-     videoAnimateController,
-   ])
- 
-   const handleBadgeMouseLeave = useCallback(async () => {
-     if (hoverTimerRef.current) {
-       clearTimeout(hoverTimerRef.current)
-       hoverTimerRef.current = null
-     }
- 
-     if (isPlayingLivePhoto) {
-       const video = videoRef.current
-       if (video) {
-         video.pause()
-         video.currentTime = 0
-       }
- 
-       // 开始淡出动画
-       await videoAnimateController.start({
-         opacity: 0,
-         transition: { duration: 0.2, ease: 'easeIn' },
-       })
- 
-       setIsPlayingLivePhoto(false)
-     }
-   }, [isPlayingLivePhoto, videoAnimateController])
- 
-   // 视频播放结束处理
-   const handleVideoEnded = useCallback(async () => {
-     // 播放结束时淡出
-     await videoAnimateController.start({
-       opacity: 0,
-       transition: { duration: 0.2, ease: 'easeIn' },
-     })
- 
-     setIsPlayingLivePhoto(false)
-   }, [videoAnimateController])
+   ])
  
    const onTransformed = useCallback(
```

```diff
      })
    }, [thumbnailAnimateController])
- 
-   // Live Photo 长按处理（移动端）
-   const handleTouchStart = useCallback(
-     (e: React.TouchEvent) => {
-       if (
-         !isLivePhoto ||
-         !livePhotoVideoLoaded ||
-         isPlayingLivePhoto ||
-         isConvertingVideo ||
-         e.touches.length > 1 // 多指触摸不触发长按
-       )
-         return
- 
-       longPressTimerRef.current = setTimeout(async () => {
-         setIsLongPressing(true)
-         setIsPlayingLivePhoto(true)
- 
-         // 开始淡入动画
-         await videoAnimateController.start({
-           opacity: 1,
-           transition: { duration: 0.15, ease: 'easeOut' },
-         })
- 
-         const video = videoRef.current
-         if (video) {
-           video.currentTime = 0
-           video.play()
-         }
-       }, 500) // 500ms 长按延迟
-     },
-     [
-       isLivePhoto,
-       livePhotoVideoLoaded,
-       isPlayingLivePhoto,
-       isConvertingVideo,
-       videoAnimateController,
-     ],
-   )
- 
-   const handleTouchEnd = useCallback(async () => {
-     if (longPressTimerRef.current) {
-       clearTimeout(longPressTimerRef.current)
-       longPressTimerRef.current = null
-     }
- 
-     if (isLongPressing && isPlayingLivePhoto) {
-       setIsLongPressing(false)
- 
-       const video = videoRef.current
-       if (video) {
-         video.pause()
-         video.currentTime = 0
-       }
- 
-       // 开始淡出动画
-       await videoAnimateController.start({
-         opacity: 0,
-         transition: { duration: 0.2, ease: 'easeIn' },
-       })
- 
-       setIsPlayingLivePhoto(false)
-     }
-   }, [isLongPressing, isPlayingLivePhoto, videoAnimateController])
- 
-   const handleTouchMove = useCallback(() => {
-     // 触摸移动时取消长按
-     if (longPressTimerRef.current) {
-       clearTimeout(longPressTimerRef.current)
-       longPressTimerRef.current = null
-     }
-   }, [])
  
    const showContextMenu = useShowContextMenu()
```

```diff
            onZoomChange={onTransformed}
            debug={import.meta.env.DEV}
-           onTouchStart={handleTouchStart}
-           onTouchEnd={handleTouchEnd}
-           onTouchMove={handleTouchMove}
            onContextMenu={(e) =>
              showContextMenu(
```

```diff
        )}
  
-       {/* Live Photo 视频 */}
-       {isLivePhoto && livePhotoVideoUrl && (
-         <m.video
-           ref={videoRef}
-           className="absolute inset-0 z-10 h-full w-full object-contain"
-           muted
-           playsInline
-           onEnded={handleVideoEnded}
-           initial={{ opacity: 0 }}
-           animate={videoAnimateController}
+       {/* Live Photo 组件 */}
+       {isLivePhoto && livePhotoVideoUrl && imageLoaderManagerRef.current && (
+         <LivePhoto
+           videoUrl={livePhotoVideoUrl}
+           imageLoaderManager={imageLoaderManagerRef.current}
+           loadingIndicatorRef={loadingIndicatorRef}
+           isCurrentImage={isCurrentImage}
          />
        )}
```

```diff
        <LoadingIndicator ref={loadingIndicatorRef} />
  
-       {/* Live Photo 标识 */}
-       {isLivePhoto && (
-         <div
-           className={clsxm(
-             'absolute z-50 flex items-center space-x-1 rounded-xl bg-black/50 px-1 py-1 text-xs text-white cursor-pointer transition-all duration-200 hover:bg-black/70',
-             import.meta.env.DEV ? 'top-16 right-4' : 'top-12 lg:top-4 left-4',
-           )}
-           onMouseEnter={handleBadgeMouseEnter}
-           onMouseLeave={handleBadgeMouseLeave}
-           title={isMobileDevice ? '长按播放实况照片' : '悬浮播放实况照片'}
-         >
-           {isConvertingVideo ? (
-             <div className="flex items-center gap-1 px-1">
-               <i className="i-mingcute-loading-line animate-spin" />
-               <span>实况视频转换中</span>
-             </div>
-           ) : (
-             <>
-               <img
-                 className="size-4 invert-100"
-                 src="data:image/webp;base64, "
-               />
-               <span className="mr-1">实况</span>
-               {conversionMethod && (
-                 <span className="rounded bg-white/20 px-1 text-xs">
-                   {conversionMethod === 'webcodecs' ? 'WebCodecs' : 'FFmpeg'}
-                 </span>
-               )}
-             </>
-           )}
+       {/* 操作提示 */}
+       {!isLivePhoto && (
+         <div className="pointer-events-none absolute bottom-4 left-1/2 z-20 -translate-x-1/2 rounded bg-black/50 px-2 py-1 text-xs text-white opacity-0 duration-200 group-hover:opacity-50">
+           双击或双指缩放
          </div>
        )}
- 
-       {/* 操作提示 */}
-       <div className="pointer-events-none absolute bottom-4 left-1/2 z-20 -translate-x-1/2 rounded bg-black/50 px-2 py-1 text-xs text-white opacity-0 duration-200 group-hover:opacity-50">
-         {isLivePhoto
-           ? isConvertingVideo
-             ? `正在使用 ${isWebCodecsSupported() ? 'WebCodecs' : 'FFmpeg'} 转换视频格式...`
-             : isMobileDevice
-               ? '长按播放实况照片 / 双击缩放'
-               : '悬浮实况标识播放 / 双击缩放'
-           : '双击或双指缩放'}
-       </div>
      </div>
    )
```

---

我需要修复 TypeScript 类型错误。问题是 `loadingIndicatorRef` 的类型不匹配。让我修复这个问题：

---

```diff
    imageLoaderManager: ImageLoaderManager
    /** 加载指示器引用 */
-   loadingIndicatorRef: React.RefObject<LoadingIndicatorRef>
+   loadingIndicatorRef: React.RefObject<LoadingIndicatorRef | null>
    /** 是否是当前图片 */
    isCurrentImage: boolean
```

---

现在让我们测试一下重构后的代码是否正常工作：

---

```bash
cd /Users/innei/git/vibe-coding/photo-gallery-site && npm run typecheck
```

```
be-coding/photo-gallery-site && npm run typecheck         cd /Users/innei/git/vibe-coding/photo-gallery-site    npm
 ERR_PNPM_NO_SCRIPT  Missing script: typecheck

Command "typecheck" not found.

innei in photo-gallery-site on  main [$!?] via  v22.15.0 on ☁️  innei@rss3.io at     1bd7115 "refactor(webgl-vie
wer): restructure WebGL image viewer component"  by tukon479@gmail.com 
at 01:11:26 ❯
```

---

让我检查一下 package.json 中有哪些可用的脚本：

---

Read file: package.json

---

让我检查 web 应用的 package.json：

---

Read file: apps/web/package.json

---

让我运行 lint 来检查代码质量：

---

```bash
cd apps/web && npm run lint
```

```

> @photo-gallery/web@0.0.1 lint /Users/innei/git/vibe-coding/photo-gallery-site/apps/web
> eslint --fix

Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.
Cannot resolve default tailwindcss config path. Please manually set the config option.

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/atoms/route.ts
  34:70  warning  React Hook useMemo was passed a dependency list that is not an array literal. This means we can't 
statically verify whether you've passed the correct dependencies
         react-hooks/exhaustive-deps
  34:70  warning  React Hook useMemo has a missing dependency: 'selector'. Either include it or remove the dependenc
y array. If 'selector' changes too often, find the parent component that defines it and wrap that definition in useC
allback  react-hooks/exhaustive-deps

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/components/ui/button/Button.tsx
  135:36  warning  Fast refresh only works when a file only exports components. Use a new file to share constants or
 functions between components  react-refresh/only-export-components

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/components/ui/portal/provider.tsx
  3:14  warning  Fast refresh only works when a file only exports components. Use a new file to share constants or f
unctions between components  react-refresh/only-export-components

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/components/ui/typography/EllipsisWithTooltip.tsx
  40:6  warning  React Hook useEffect has a missing dependency: 'judgment'. Either include it or remove the dependen
cy array  react-hooks/exhaustive-deps
  52:6  warning  React Hook useEffect has a missing dependency: 'judgment'. Either include it or remove the dependen
cy array  react-hooks/exhaustive-deps

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/hooks/common/useTitle.ts
  11:40  warning  The ref value 'currentTitleRef.current' will likely have changed by the time this effect cleanup f
unction runs. If this ref points to a node rendered by React, copy 'currentTitleRef.current' to a variable inside th
e effect, and use that variable in the cleanup function  react-hooks/exhaustive-deps

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/hooks/useViewport.ts
  15:55  warning  React Hook useCallback has a missing dependency: 'selector'. Either include it or remove the depen
dency array. If 'selector' changes too often, find the parent component that defines it and wrap that definition in
useCallback  react-hooks/exhaustive-deps

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/lib/blob-url-manager.ts
   97:6  warning  React Hook useEffect has a missing dependency: 'url'. Either include it or remove the dependency a
rray  react-hooks/exhaustive-deps
  106:6  warning  React Hook useEffect has a missing dependency: 'url'. Either include it or remove the dependency a
rray  react-hooks/exhaustive-deps

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/lib/jotai.ts
  33:46  warning  React Hook useCallback was passed a dependency list that is not an array literal. This means we ca
n't statically verify whether you've passed the correct dependencies
             react-hooks/exhaustive-deps
  33:46  warning  React Hook useCallback has a missing dependency: 'selector'. Either include it or remove the depen
dency array. If 'selector' changes too often, find the parent component that defines it and wrap that definition in
useCallback  react-hooks/exhaustive-deps

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/lib/scroller.ts
  80:5  warning  Assignment to function parameter 'el'  no-param-reassign

/Users/innei/git/vibe-coding/photo-gallery-site/apps/web/src/providers/context-menu-provider.tsx
   82:31  warning  Do not use item index in the array as its key  @eslint-react/no-array-index-key
  138:30  warning  Do not use item index in the array as its key  @eslint-react/no-array-index-key

✖ 15 problems (0 errors, 15 warnings)


```

---

很好！lint 检查通过了，只有一些警告。让我试一下构建来确保没有编译错误：

---

```bash
npm run build
```

```
at 01:11:43 ❯ npm run build

> @photo-gallery/web@0.0.1 build /Users/innei/git/vibe-coding/photo-gallery-site/apps/web
> vite build

📚 Loaded 44 photos from manifest
vite v6.3.5 building for production...
✅ Generated favicon: favicon-16x16.png (16x16)
✅ Generated favicon: favicon-32x32.png (32x32)
✅ Generated favicon: favicon-48x48.png (48x48)
✅ Generated favicon: apple-touch-icon.png (180x180)
✅ Generated favicon: android-chrome-192x192.png (192x192)
✅ Generated favicon: android-chrome-512x512.png (512x512)
✅ Generated main favicon: favicon.ico
✅ Generated web manifest: site.webmanifest
🎨 All favicons generated successfully!
📸 Found 4 latest photos
📷 Added photo: DSCF3449 at position (580, 220) with rotation -12°
📷 Added photo: DSCF3447 at position (670, 260) with rotation 5°
📷 Added photo: DSCF3443 at position (760, 190) with rotation -8°
📷 Added photo: DSCF3439 at position (850, 270) with rotation 10°
✅ OG image generated: /Users/innei/git/vibe-coding/photo-gallery-site/apps/web/public/og-image-1748970710524.png
🖼️  OG image generated: /og-image-1748970710524.png
🗑️  Deleted old OG image: og-image-1748970158025.png
✅ Cleaned up 1 old OG images
transforming (29) src/components/common/ErrorElement.tsx/*! 🌼 daisyUI 5.0.43 */
✓ 1110 modules transformed.
dist/index.html                                 12.63 kB │ gzip:   2.88 kB
dist/assets/GeistVF.p2GUc8.woff2                56.80 kB
dist/assets/index.D543_y.css                     8.91 kB │ gzip:   3.50 kB
dist/assets/index.h0rO5f.css                   125.95 kB │ gzip:  20.80 kB
dist/assets/frame-C-OEHO6m.js                    0.61 kB │ gzip:   0.41 kB
dist/assets/blurhash-CYHzaf4C.js                 0.74 kB │ gzip:   0.47 kB
dist/assets/webgl-preview-DcVkCe1M.js            1.28 kB │ gzip:   0.72 kB
dist/assets/heic-converter-C5vdPBKK.js           1.48 kB │ gzip:   0.85 kB
dist/assets/WebGLImageViewer-DOVBAy6o.js        25.18 kB │ gzip:   7.43 kB
dist/assets/visual-element-lgpFBzt_.js          34.25 kB │ gzip:  12.59 kB
dist/assets/usePhotoViewer-B58zSvwq.js          38.92 kB │ gzip:  13.78 kB
dist/assets/layout-DQPfIIal.js                  43.11 kB │ gzip:  15.40 kB
dist/assets/framer-lazy-feature-CuyQKJcL.js     64.71 kB │ gzip:  20.54 kB
dist/assets/photos-DkA3E5gc.js                  99.40 kB │ gzip:  12.18 kB
dist/assets/index-D1yk-P8i.js                  139.10 kB │ gzip:  44.44 kB
dist/vendor/1-CkyqGQkw.js                      187.60 kB │ gzip:  59.12 kB
dist/assets/index-C_FuO7ue.js                  323.30 kB │ gzip: 106.96 kB
dist/vendor/0-kq-hUwqs.js                    2,185.75 kB │ gzip: 547.25 kB

(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#outpu
t-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 3.38s

innei in photo-gallery-site/apps/web on  main [$!?] via 🎁 v0.0.1 via  v22.15.0 on ☁️  innei@rss3.io at     1bd7
115 "refactor(webgl-viewer): restructure WebGL image viewer component"  by tukon479@gmail.com took 4s 
at 01:11:53 ❯
```

---

太好了！构建成功完成。现在让我为你创建一个总结，展示我们重构的内容。

## 重构总结

我已经成功将 `ProgressiveImage.tsx` 中的 Live Photo 逻辑抽取成了一个独立的组件 `LivePhoto.tsx`。以下是重构的详细内容：

### 新创建的文件

**`apps/web/src/components/ui/photo-viewer/LivePhoto.tsx`**

这个新组件包含了：

1. **状态管理**：
   - `isPlayingLivePhoto` - 是否正在播放实况照片
   - `livePhotoVideoLoaded` - 实况照片视频是否已加载
   - `isConvertingVideo` - 是否正在转换视频格式
   - `conversionMethod` - 视频转换方法
   - `isLongPressing` - 是否正在长按（移动端）

2. **核心功能**：
   - 视频加载和处理逻辑
   - 桌面端鼠标悬停播放
   - 移动端长按播放
   - 播放结束自动淡出
   - 状态重置和清理

3. **UI 组件**：
   - Live Photo 视频元素
   - Live Photo 标识徽章
   - 转换状态显示
   - 操作提示信息

### 重构后的 `ProgressiveImage.tsx`

**移除的内容**：
- 所有 Live Photo 相关的状态变量
- Live Photo 相关的 refs（`videoRef`, `hoverTimerRef`, `longPressTimerRef`）
- 所有 Live Photo 事件处理函数
- Live Photo 相关的 useEffect 清理逻辑
- Live Photo 相关的视频处理逻辑
- Live Photo 标识和操作提示的渲染逻辑

**保留并简化的内容**：
- 核心的图片加载逻辑
- WebGL 图片查看器
- 右键菜单功能
- 基础的操作提示（仅在非 Live Photo 时显示）

### 主要好处

1. **代码分离**：Live Photo 功能完全独立，便于维护和测试
2. **可复用性**：`LivePhoto` 组件可以在其他地方重复使用
3. **可读性**：`ProgressiveImage` 组件变得更简洁，更容易理解
4. **类型安全**：修复了 TypeScript 类型错误
5. **功能完整**：所有原有的 Live Photo 功能都保持完整

### 使用方式

在 `ProgressiveImage` 中，现在只需要简单地条件渲染 `LivePhoto` 组件：

```tsx
{/* Live Photo 组件 */}
{isLivePhoto && livePhotoVideoUrl && imageLoaderManagerRef.current && (
  <LivePhoto
    videoUrl={livePhotoVideoUrl}
    imageLoaderManager={imageLoaderManagerRef.current}
    loadingIndicatorRef={loadingIndicatorRef}
    isCurrentImage={isCurrentImage}
  />
)}
```

构建和 lint 检查都通过了，说明重构是成功的！Live Photo 的所有功能都已经完整地迁移到了新组件中。

---

