import { useNavigate, useSearchParams } from 'react-router-dom'; import { ArrowLeft } from 'lucide-react'; import { useTranslation } from 'react-i18next'; export function GCodeViewerPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { t } = useTranslation(); // Safety guard: if this React app is itself inside an iframe (e.g. the // StaticFiles mount isn't registered and serve_spa returned us here), // don't render another iframe — that would create an infinite loop. if (window !== window.top) { return (
GCode viewer static files not found. Check that the{' '} gcode_viewer/ directory exists and restart uvicorn.
); } const cameFromArchive = searchParams.has('archive'); const cameFromLibrary = searchParams.has('library_file'); const fallbackPath = cameFromArchive ? '/archives' : cameFromLibrary ? '/files' : '/'; const backLabel = cameFromArchive ? t('gcodeViewer.backToArchives') : cameFromLibrary ? t('gcodeViewer.backToFiles') : t('gcodeViewer.back'); const handleBack = () => { // Prefer browser history so we land where the user actually was (preserving // scroll position, filters, etc.). Fall back to a sensible default route // when the viewer was opened from a fresh tab / shared link. if (window.history.length > 1) { navigate(-1); } else { navigate(fallbackPath); } }; // Forward the outer page's query string (e.g. ?archive=82) to the iframe so // the adapter inside can pick up the archive to load. The iframe itself must // keep the trailing slash on /gcode-viewer/ so it hits the raw-viewer route; // the outer SPA URL uses no trailing slash so a reload falls through to the // SPA catch-all and keeps the Bambuddy layout shell. const iframeSrc = `/gcode-viewer/${window.location.search}`; return ( // h-14 (3.5 rem) is the fixed header height defined in Layout.tsx. // Subtracting it prevents a double scrollbar inside the layout shell.