'use client'
import { useState, useEffect, useRef } from 'react'
const tips = [
{
title: "Git Version Control",
content: "Track every change with timestamps. Commit your work to GitHub for professional backup and collaboration. Try different versions safely with branching."
},
{
title: "AI Agent Capabilities",
content: "Your AI can read, edit, and manage files directly. Ask it to 'find all mentions of the villain' or 'improve the dialogue in chapter 3' - it will search and act automatically."
},
{
title: "Semantic Search",
content: "Search by meaning, not just exact text. Find content about 'betrayal' even if the word isn't used, or discover themes across your entire book."
},
{
title: "Effective AI Prompting",
content: "Be specific: 'Help me develop the backstory for my shy protagonist who discovers magic' works better than 'write a character.' Provide context about your story and goals."
},
{
title: "AI Image Generation",
content: "Create book covers, character portraits, and scene illustrations with AI. Use detailed descriptions like 'Fantasy cover with dragons silhouetted against misty mountains.'"
},
{
title: "File Organization",
content: "Break your book into manageable pieces. Each chapter, character profile, or outline can be its own file. Use folders to organize by themes or story arcs."
},
{
title: "Slash Commands",
content: "Type '/' in the editor for quick formatting. Try /h1 for headings, /list for bullet points, /quote for blockquotes, or /table for data organization."
},
{
title: "AI Writing Techniques",
content: "Use AI for brainstorming, character interviews, plot problem-solving, and style improvements. Start with rough ideas and iterate to build on what works."
}
]
export default function BookEditorSkeleton() {
// Use a ref to track if we've initialized to avoid hydration issues
const initializedRef = useRef(false)
const [currentTipIndex, setCurrentTipIndex] = useState(0)
const [isClient, setIsClient] = useState(false)
const intervalRef = useRef<NodeJS.Timeout | null>(null)
const mountedRef = useRef(false)
// Initialize with random tip on client side
useEffect(() => {
if (!initializedRef.current) {
initializedRef.current = true
setIsClient(true)
// Randomize immediately
const randomIndex = Math.floor(Math.random() * tips.length)
setCurrentTipIndex(randomIndex)
}
}, [])
useEffect(() => {
// Only set up interval after client has mounted and randomized the tip
if (!isClient) return
// Prevent multiple intervals if component re-renders
if (mountedRef.current) return
mountedRef.current = true
// Set up interval
intervalRef.current = setInterval(() => {
setCurrentTipIndex((prev) => {
const next = (prev + 1) % tips.length
return next
})
}, 1500) // Change tip every 1.5 seconds
// Cleanup on unmount
return () => {
mountedRef.current = false
if (intervalRef.current) {
clearInterval(intervalRef.current)
intervalRef.current = null
}
}
}, [isClient])
return (
<div className='h-screen flex flex-col md:flex-row bg-black relative overflow-hidden'>
{/* Modern gradient background */}
<div className="absolute inset-0 bg-gradient-to-br from-black via-gray-950 to-black pointer-events-none" />
{/* Subtle animated gradient overlay */}
<div className="absolute inset-0 opacity-30 pointer-events-none">
<div className="absolute top-0 left-1/4 w-96 h-96 bg-gradient-to-r from-blue-600/20 to-purple-600/20 rounded-full blur-3xl animate-pulse" />
<div className="absolute bottom-0 right-1/4 w-96 h-96 bg-gradient-to-r from-purple-600/20 to-pink-600/20 rounded-full blur-3xl animate-pulse" style={{ animationDelay: '1s' }} />
</div>
{/* Tips overlay */}
<div className="absolute inset-0 flex items-center justify-center z-20 pointer-events-none">
<div className="bg-slate-900/80 backdrop-blur-sm border border-slate-700/50 rounded-2xl p-6 max-w-md mx-4 text-center">
<div className="text-2xl mb-3 animate-pulse">✨</div>
{isClient ? (
<>
<h4 className="text-slate-100 font-semibold text-sm mb-2">
{tips[currentTipIndex].title}
</h4>
<p className="text-slate-200 text-sm leading-relaxed">
{tips[currentTipIndex].content}
</p>
<div className="flex justify-center mt-4 space-x-1">
{tips.map((_, index) => (
<div
key={index}
className={`w-2 h-2 rounded-full transition-all duration-300 ${
index === currentTipIndex
? 'bg-teal-400'
: 'bg-slate-600'
}`}
/>
))}
</div>
</>
) : null}
</div>
</div>
{/* Desktop Layout Skeleton */}
<div className="hidden md:flex w-full relative z-10">
{/* Left Panel - File Explorer Skeleton */}
<div className="w-80 bg-slate-900/40 backdrop-blur-sm border-r border-slate-700/50">
<div className="h-full flex flex-col">
{/* Header skeleton */}
<div className='h-12 bg-slate-800/50 border-b border-slate-700/50'>
<div className='grid grid-cols-4 h-12'>
{[1, 2, 3, 4].map((index) => (
<div
key={index}
className="flex items-center justify-center"
>
<div
className="w-4 h-4 bg-slate-600/50 rounded animate-pulse"
style={{ animationDelay: `${index * 100}ms` }}
></div>
</div>
))}
</div>
</div>
{/* File explorer content skeleton */}
<div className="flex-1 overflow-y-auto p-4 space-y-3">
{/* Project name skeleton */}
<div className="flex items-center gap-2 mb-4">
<div className="w-4 h-4 bg-slate-600/50 rounded animate-pulse"></div>
<div className="w-32 h-4 bg-slate-600/50 rounded animate-pulse"></div>
</div>
{/* File tree skeleton */}
{[1, 2, 3, 4, 5, 6, 7, 8].map((index) => (
<div
key={index}
className="flex items-center gap-2"
style={{ paddingLeft: `${8 + ((index - 1) % 3) * 16}px` }}
>
<div
className="w-3 h-3 bg-slate-600/50 rounded animate-pulse"
style={{ animationDelay: `${index * 150}ms` }}
></div>
<div
className="w-24 h-3 bg-slate-600/50 rounded animate-pulse"
style={{ animationDelay: `${index * 150 + 50}ms` }}
></div>
</div>
))}
</div>
</div>
</div>
{/* Center Panel - Editor Skeleton */}
<div className='flex-1 flex flex-col min-w-0'>
{/* Editor header skeleton */}
<div className='h-12 bg-slate-800/50 border-b border-slate-700/50 flex items-center px-4 justify-between'>
<div className="flex items-center gap-3">
<div className="w-32 h-4 bg-slate-600/50 rounded animate-pulse"></div>
<div className="w-12 h-4 bg-slate-600/50 rounded animate-pulse"></div>
</div>
<div className="flex items-center gap-2">
<div className="w-8 h-6 bg-slate-600/50 rounded animate-pulse"></div>
<div className="w-12 h-6 bg-slate-600/50 rounded animate-pulse"></div>
</div>
</div>
{/* Editor content skeleton */}
<div className='flex-1 bg-slate-900/40 backdrop-blur-sm p-6'>
<div className="space-y-4">
{/* Editor lines skeleton */}
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].map((index) => (
<div
key={index}
className="flex items-center gap-3"
>
<div
className="w-8 h-3 bg-slate-600/30 rounded animate-pulse"
style={{ animationDelay: `${index * 100}ms` }}
></div>
<div
className={`h-3 bg-slate-600/50 rounded animate-pulse ${
index % 3 === 0 ? 'w-full' :
index % 3 === 1 ? 'w-3/4' : 'w-1/2'
}`}
style={{ animationDelay: `${index * 100 + 50}ms` }}
></div>
</div>
))}
</div>
</div>
{/* Editor footer skeleton */}
<div className="h-8 bg-slate-800 border-t border-slate-700 flex items-center justify-between px-4">
<div className="flex items-center gap-3">
<div className="w-16 h-3 bg-slate-600/50 rounded animate-pulse"></div>
</div>
<div className="w-12 h-3 bg-slate-600/50 rounded animate-pulse"></div>
</div>
</div>
{/* Right Panel - Chat Skeleton */}
<div className="w-96 bg-slate-800/40 backdrop-blur-sm border-l border-slate-700/50">
<div className="h-full flex flex-col">
{/* Chat header skeleton */}
<div className="h-12 bg-slate-800/50 border-b border-slate-700/50 flex items-center px-4">
<div className="flex items-center gap-2">
<div className="w-4 h-4 bg-slate-600/50 rounded animate-pulse"></div>
<div className="w-20 h-4 bg-slate-600/50 rounded animate-pulse"></div>
</div>
</div>
{/* Chat messages skeleton */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{[1, 2, 3, 4, 5].map((index) => (
<div
key={index}
className={`flex ${index % 2 === 0 ? 'justify-end' : 'justify-start'}`}
>
<div
className={`w-48 p-3 rounded-lg animate-pulse ${
index % 2 === 0
? 'bg-teal-600/20 border border-teal-500/30'
: 'bg-slate-700/50 border border-slate-600/50'
}`}
style={{ animationDelay: `${index * 200}ms` }}
>
<div className="space-y-2">
<div
className={`h-3 rounded ${
index % 2 === 0 ? 'bg-teal-400/30' : 'bg-slate-600/50'
}`}
style={{ width: `${60 + (index * 10)}%` }}
></div>
<div
className={`h-3 rounded ${
index % 2 === 0 ? 'bg-teal-400/30' : 'bg-slate-600/50'
}`}
style={{ width: `${40 + (index * 15)}%` }}
></div>
</div>
</div>
</div>
))}
</div>
{/* Chat input skeleton */}
<div className="p-4 border-t border-slate-700/50">
<div className="flex items-center gap-2">
<div className="flex-1 h-10 bg-slate-700/50 rounded-lg animate-pulse"></div>
<div className="w-10 h-10 bg-slate-600/50 rounded-lg animate-pulse"></div>
</div>
</div>
</div>
</div>
</div>
{/* Mobile Layout Skeleton */}
<div className="flex-1 flex md:hidden flex-col relative z-10 overflow-hidden">
{/* Mobile header skeleton - matches the actual mobile header grid */}
<div className="h-12 bg-slate-800/50 border-b border-slate-700/50">
<div className="grid grid-cols-4 h-12">
{[1, 2, 3, 4].map((index) => (
<div
key={index}
className="flex items-center justify-center"
>
<div
className="w-4 h-4 bg-slate-600/50 rounded animate-pulse"
style={{ animationDelay: `${index * 100}ms` }}
></div>
</div>
))}
</div>
</div>
{/* Mobile content skeleton */}
<div className="flex-1 bg-slate-900/40 backdrop-blur-sm">
{/* Project header skeleton - matches ExplorerTab px-4 py-3 */}
<div className="px-4 py-3">
<div className="flex items-center justify-between group">
<div className="flex items-center text-sm text-slate-200 font-medium">
<div className="w-8 h-8 rounded-xl bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center mr-3">
<div className="w-4 h-4 bg-slate-600/50 rounded animate-pulse"></div>
</div>
<div className="w-32 h-4 bg-slate-600/50 rounded animate-pulse"></div>
</div>
<div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-all duration-200">
<div className="w-6 h-6 bg-slate-600/50 rounded animate-pulse"></div>
<div className="w-6 h-6 bg-slate-600/50 rounded animate-pulse"></div>
</div>
</div>
</div>
{/* File explorer skeleton - matches FileExplorer px-4 py-2 */}
<div className="px-4 py-2">
<div className="mt-2">
{[1, 2, 3, 4, 5, 6, 7, 8].map((index) => (
<div
key={index}
className="flex items-center gap-2 mb-2"
style={{ paddingLeft: `${8 + ((index - 1) % 3) * 16}px` }}
>
<div
className="w-3 h-3 bg-slate-600/50 rounded animate-pulse"
style={{ animationDelay: `${index * 150}ms` }}
></div>
<div
className="w-24 h-3 bg-slate-600/50 rounded animate-pulse"
style={{ animationDelay: `${index * 150 + 50}ms` }}
></div>
</div>
))}
</div>
</div>
</div>
{/* Mobile bottom navigation skeleton */}
<div className="h-12 bg-slate-900/95 backdrop-blur-md border-t border-slate-700/60 flex items-center justify-around px-2">
{[1, 2, 3].map((index) => (
<div
key={index}
className={`flex items-center justify-center py-2 px-4 rounded-xl transition-all duration-200 relative group ${
index === 1 ? 'bg-teal-500/15 shadow-lg shadow-teal-500/10' : ''
}`}
>
<div className="flex items-center gap-2">
<div
className={`w-4 h-4 rounded animate-pulse ${
index === 1 ? 'bg-teal-400/60' : 'bg-slate-600/50'
}`}
style={{ animationDelay: `${index * 100}ms` }}
></div>
<div
className={`w-8 h-3 rounded animate-pulse ${
index === 1 ? 'bg-teal-400/40' : 'bg-slate-600/50'
}`}
style={{ animationDelay: `${index * 100 + 50}ms` }}
></div>
</div>
</div>
))}
</div>
</div>
</div>
)
}