"use client" import { usePathname, useRouter } from "next/navigation" import { Suspense, useCallback, useEffect, useRef, useState } from "react" import { DrawIoEmbed } from "react-drawio" import type { ImperativePanelHandle } from "react-resizable-panels" import ChatPanel from "@/components/chat-panel" import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable" import { useDiagram } from "@/contexts/diagram-context" import { type DrawioTheme, isDrawioTheme } from "@/lib/drawio-themes" import { i18n, type Locale } from "@/lib/i18n/config" import { isIndexedDBUsable } from "@/lib/session-storage" export default function Home() { const { drawioRef, handleDiagramExport, handleDiagramAutoSave, onDrawioLoad, resetDrawioReady, } = useDiagram() const router = useRouter() const pathname = usePathname() // Extract current language from pathname (e.g., "/zh/about" → "zh") const currentLang = (pathname.split("/")[1] || i18n.defaultLocale) as Locale const [isMobile, setIsMobile] = useState(false) const [isChatVisible, setIsChatVisible] = useState(true) const [drawioUi, setDrawioUi] = useState("kennedy") const [darkMode, setDarkMode] = useState(false) const [isLoaded, setIsLoaded] = useState(false) const [isDrawioReady, setIsDrawioReady] = useState(false) const [isElectron, setIsElectron] = useState(false) const [canPersist, setCanPersist] = useState(false) const [canPersistChecked, setCanPersistChecked] = useState(false) const [drawioBaseUrl, setDrawioBaseUrl] = useState( process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "https://embed.diagrams.net", ) const chatPanelRef = useRef(null) const isMobileRef = useRef(false) // Load preferences from localStorage after mount useEffect(() => { // Restore saved locale and redirect if needed const savedLocale = localStorage.getItem("next-ai-draw-io-locale") if (savedLocale && i18n.locales.includes(savedLocale as Locale)) { const pathParts = pathname.split("/").filter(Boolean) const currentLocale = pathParts[0] if (currentLocale !== savedLocale) { pathParts[0] = savedLocale router.replace(`/${pathParts.join("/")}`) return // Wait for redirect } } const savedUi = localStorage.getItem("drawio-theme") if (isDrawioTheme(savedUi)) { setDrawioUi(savedUi) } const savedDarkMode = localStorage.getItem("next-ai-draw-io-dark-mode") if (savedDarkMode !== null) { const isDark = savedDarkMode === "true" setDarkMode(isDark) document.documentElement.classList.toggle("dark", isDark) } else { const prefersDark = window.matchMedia( "(prefers-color-scheme: dark)", ).matches setDarkMode(prefersDark) document.documentElement.classList.toggle("dark", prefersDark) } // Detect Electron and use bundled draw.io files for offline use // Note: react-drawio uses `new URL(baseUrl)` so we need absolute URL // Include /index.html because Next.js doesn't auto-serve index.html for directories const electronDetected = !process.env.NEXT_PUBLIC_DRAWIO_BASE_URL && !!(window as unknown as { electronAPI?: unknown }).electronAPI if (electronDetected) { setIsElectron(true) setDrawioBaseUrl(`${window.location.origin}/drawio/index.html`) } void (async () => { const usable = await isIndexedDBUsable() setCanPersist(usable) setCanPersistChecked(true) })() setIsLoaded(true) }, [pathname, router]) const handleDrawioLoad = useCallback(() => { setIsDrawioReady(true) onDrawioLoad() }, [onDrawioLoad]) const handleDrawioAutoSave = useCallback( (data: { xml?: string }) => { handleDiagramAutoSave(data) }, [handleDiagramAutoSave], ) const handleDarkModeChange = () => { const newValue = !darkMode setDarkMode(newValue) localStorage.setItem("next-ai-draw-io-dark-mode", String(newValue)) document.documentElement.classList.toggle("dark", newValue) setIsDrawioReady(false) resetDrawioReady() } const handleDrawioUiChange = (theme: DrawioTheme) => { localStorage.setItem("drawio-theme", theme) setDrawioUi(theme) setIsDrawioReady(false) resetDrawioReady() } // Check mobile - reset draw.io before crossing breakpoint const isInitialRenderRef = useRef(true) useEffect(() => { const checkMobile = () => { const newIsMobile = window.innerWidth < 768 if ( !isInitialRenderRef.current && newIsMobile !== isMobileRef.current ) { setIsDrawioReady(false) resetDrawioReady() } isMobileRef.current = newIsMobile isInitialRenderRef.current = false setIsMobile(newIsMobile) } checkMobile() window.addEventListener("resize", checkMobile) return () => window.removeEventListener("resize", checkMobile) }, [resetDrawioReady]) const toggleChatPanel = () => { const panel = chatPanelRef.current if (panel) { if (panel.isCollapsed()) { panel.expand() setIsChatVisible(true) } else { panel.collapse() setIsChatVisible(false) } } } // Keyboard shortcut for toggling chat panel useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if ((event.ctrlKey || event.metaKey) && event.key === "b") { event.preventDefault() toggleChatPanel() } } window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) }, []) return (
{isLoaded && canPersistChecked && (
)} {(!isLoaded || !isDrawioReady) && (
Draw.io panel is loading...
)}
{/* Chat Panel */} setIsChatVisible(false)} onExpand={() => setIsChatVisible(true)} >
Loading chat...
} >
) }