diff --git a/backend/sandbox/README.md b/backend/sandbox/README.md index aa7d2a85..f3efc5c1 100644 --- a/backend/sandbox/README.md +++ b/backend/sandbox/README.md @@ -20,7 +20,7 @@ You can modify the sandbox environment for development or to add new capabilitie ``` cd backend/sandbox/docker docker compose build - docker push kortix/suna:0.1.3.9 + docker push kortix/suna:0.1.3.11 ``` 3. Test your changes locally using docker-compose diff --git a/backend/sandbox/docker/docker-compose.yml b/backend/sandbox/docker/docker-compose.yml index ec9b9978..d6ce2e47 100644 --- a/backend/sandbox/docker/docker-compose.yml +++ b/backend/sandbox/docker/docker-compose.yml @@ -6,7 +6,7 @@ services: dockerfile: ${DOCKERFILE:-Dockerfile} args: TARGETPLATFORM: ${TARGETPLATFORM:-linux/amd64} - image: kortix/suna:0.1.3.9 + image: kortix/suna:0.1.3.11 ports: - "6080:6080" # noVNC web interface - "5901:5901" # VNC port diff --git a/backend/sandbox/docker/html_to_pdf_router.py b/backend/sandbox/docker/html_to_pdf_router.py index 65995d9e..d545083a 100644 --- a/backend/sandbox/docker/html_to_pdf_router.py +++ b/backend/sandbox/docker/html_to_pdf_router.py @@ -74,11 +74,11 @@ class PresentationToPDFAPI: for slide_num, slide_data in slides.items(): filename = slide_data.get('filename') + file_path = slide_data.get('file_path') title = slide_data.get('title', f'Slide {slide_num}') - if filename: - # Treat filename as absolute path only - html_path = Path(filename) + if file_path: + html_path = Path(f"/workspace/{file_path}") print(f"Using path: {html_path}") # Verify the path exists diff --git a/backend/sandbox/docker/requirements.txt b/backend/sandbox/docker/requirements.txt index 3ebb1cff..f906d34e 100644 --- a/backend/sandbox/docker/requirements.txt +++ b/backend/sandbox/docker/requirements.txt @@ -6,4 +6,5 @@ pydantic==2.6.1 pytesseract==0.3.13 pandas==2.3.0 playwright>=1.40.0 -PyPDF2>=3.0.0 \ No newline at end of file +PyPDF2>=3.0.0 +bs4==0.0.2 \ No newline at end of file diff --git a/backend/sandbox/docker/visual_html_editor_router.py b/backend/sandbox/docker/visual_html_editor_router.py index 5e30b922..cbd229ae 100644 --- a/backend/sandbox/docker/visual_html_editor_router.py +++ b/backend/sandbox/docker/visual_html_editor_router.py @@ -535,6 +535,7 @@ def inject_editor_functionality(html_content: str, file_path: str) -> str: box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: flex; gap: 4px; + z-index: 1000; } .save-btn, .cancel-btn { diff --git a/backend/utils/config.py b/backend/utils/config.py index 0df03f2b..19d8f5fa 100644 --- a/backend/utils/config.py +++ b/backend/utils/config.py @@ -306,8 +306,8 @@ class Configuration: STRIPE_PRODUCT_ID_STAGING: str = 'prod_SCgIj3G7yPOAWY' # Sandbox configuration - SANDBOX_IMAGE_NAME = "kortix/suna:0.1.3.9" - SANDBOX_SNAPSHOT_NAME = "kortix/suna:0.1.3.9" + SANDBOX_IMAGE_NAME = "kortix/suna:0.1.3.11" + SANDBOX_SNAPSHOT_NAME = "kortix/suna:0.1.3.11" SANDBOX_ENTRYPOINT = "/usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf" # LangFuse configuration diff --git a/docs/SELF-HOSTING.md b/docs/SELF-HOSTING.md index fef279b6..d3e3b369 100644 --- a/docs/SELF-HOSTING.md +++ b/docs/SELF-HOSTING.md @@ -127,8 +127,8 @@ As part of the setup, you'll need to: 1. Create a Daytona account 2. Generate an API key 3. Create a Snapshot: - - Name: `kortix/suna:0.1.3.9` - - Image name: `kortix/suna:0.1.3.9` + - Name: `kortix/suna:0.1.3.11` + - Image name: `kortix/suna:0.1.3.11` - Entrypoint: `/usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf` ## Manual Configuration diff --git a/frontend/src/components/thread/tool-views/presentation-tools/FullScreenPresentationViewer.tsx b/frontend/src/components/thread/tool-views/presentation-tools/FullScreenPresentationViewer.tsx index cf83d868..aa202b5f 100644 --- a/frontend/src/components/thread/tool-views/presentation-tools/FullScreenPresentationViewer.tsx +++ b/frontend/src/components/thread/tool-views/presentation-tools/FullScreenPresentationViewer.tsx @@ -12,6 +12,7 @@ import { SkipBack, SkipForward, Edit, + Loader2, } from 'lucide-react'; import { DropdownMenu, @@ -44,6 +45,7 @@ interface FullScreenPresentationViewerProps { presentationName?: string; sandboxUrl?: string; initialSlide?: number; + onPDFDownload?: (setIsDownloadingPDF: (isDownloading: boolean) => void) => void; } export function FullScreenPresentationViewer({ @@ -52,12 +54,15 @@ export function FullScreenPresentationViewer({ presentationName, sandboxUrl, initialSlide = 1, + onPDFDownload, }: FullScreenPresentationViewerProps) { const [metadata, setMetadata] = useState(null); const [currentSlide, setCurrentSlide] = useState(initialSlide); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [showControls, setShowControls] = useState(true); + const [showEditor, setShowEditor] = useState(false); + const [isDownloadingPDF, setIsDownloadingPDF] = useState(false); // Create a stable refresh timestamp when metadata changes (like PresentationViewer) const refreshTimestamp = useMemo(() => Date.now(), [metadata]); @@ -115,6 +120,24 @@ export function FullScreenPresentationViewer({ } }, [isOpen, loadMetadata, initialSlide]); + // Reload metadata when exiting editor mode to refresh with latest changes + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + if (!showEditor) { + // Add a small delay to allow the editor to save changes + timeoutId = setTimeout(() => { + loadMetadata(); + }, 300); + } + + return () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + }, [showEditor, loadMetadata]); + // Navigation functions const goToNextSlide = useCallback(() => { if (currentSlide < totalSlides) { @@ -133,6 +156,7 @@ export function FullScreenPresentationViewer({ if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { + // Prevent default for all our handled keys const handledKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', ' ', 'Home', 'End', 'Escape']; if (handledKeys.includes(e.key)) { @@ -157,7 +181,11 @@ export function FullScreenPresentationViewer({ setCurrentSlide(totalSlides); break; case 'Escape': - onClose(); + if (showEditor) { + setShowEditor(false); + } else { + onClose(); + } break; } }; @@ -165,7 +193,7 @@ export function FullScreenPresentationViewer({ // Add event listener to document with capture to ensure we get the events first document.addEventListener('keydown', handleKeyDown, true); return () => document.removeEventListener('keydown', handleKeyDown, true); - }, [isOpen, goToNextSlide, goToPreviousSlide, totalSlides, onClose]); + }, [isOpen, goToNextSlide, goToPreviousSlide, totalSlides, onClose, showEditor]); @@ -247,11 +275,11 @@ export function FullScreenPresentationViewer({ }} >