very simple nav setup to support sandbox mode

This commit is contained in:
Will Chen
2025-04-12 22:34:11 -07:00
parent 39565ce6d2
commit 9f8302a0b8
2 changed files with 43 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
import { selectedAppIdAtom, appUrlAtom, appOutputAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom, appUrlAtom, appOutputAtom } from "@/atoms/appAtoms";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { useRunApp } from "@/hooks/useRunApp"; import { useRunApp } from "@/hooks/useRunApp";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { import {
@@ -287,6 +287,8 @@ export const PreviewIframe = ({
// Navigate to the URL // Navigate to the URL
iframeRef.current.contentWindow.location.href = newUrl; iframeRef.current.contentWindow.location.href = newUrl;
// iframeRef.current.src = newUrl;
// Update navigation history // Update navigation history
const newHistory = [ const newHistory = [
...navigationHistory.slice(0, currentHistoryPosition + 1), ...navigationHistory.slice(0, currentHistoryPosition + 1),
@@ -321,14 +323,24 @@ export const PreviewIframe = ({
<div className="flex space-x-1"> <div className="flex space-x-1">
<button <button
className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed dark:text-gray-300" className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed dark:text-gray-300"
disabled={!canGoBack || loading || !selectedAppId} disabled={
!canGoBack ||
loading ||
!selectedAppId ||
settings?.runtimeMode === "web-sandbox"
}
onClick={handleNavigateBack} onClick={handleNavigateBack}
> >
<ArrowLeft size={16} /> <ArrowLeft size={16} />
</button> </button>
<button <button
className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed dark:text-gray-300" className="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed dark:text-gray-300"
disabled={!canGoForward || loading || !selectedAppId} disabled={
!canGoForward ||
loading ||
!selectedAppId ||
settings?.runtimeMode === "web-sandbox"
}
onClick={handleNavigateForward} onClick={handleNavigateForward}
> >
<ArrowRight size={16} /> <ArrowRight size={16} />
@@ -380,6 +392,12 @@ export const PreviewIframe = ({
{/* Action Buttons */} {/* Action Buttons */}
<div className="flex space-x-1"> <div className="flex space-x-1">
<button <button
title={
settings?.runtimeMode === "web-sandbox"
? "Open in browser (disabled in sandbox mode)"
: undefined
}
disabled={settings?.runtimeMode === "web-sandbox"}
onClick={() => { onClick={() => {
if (appUrl) { if (appUrl) {
IpcClient.getInstance().openExternalUrl(appUrl); IpcClient.getInstance().openExternalUrl(appUrl);
@@ -402,7 +420,7 @@ export const PreviewIframe = ({
/> />
{settings?.runtimeMode === "web-sandbox" ? ( {settings?.runtimeMode === "web-sandbox" ? (
<SandpackIframe reloadKey={reloadKey} /> <SandpackIframe reloadKey={reloadKey} iframeRef={iframeRef} />
) : !appUrl ? ( ) : !appUrl ? (
<div className="absolute inset-0 flex flex-col items-center justify-center space-y-4 bg-gray-50 dark:bg-gray-950"> <div className="absolute inset-0 flex flex-col items-center justify-center space-y-4 bg-gray-50 dark:bg-gray-950">
<Loader2 className="w-8 h-8 animate-spin text-gray-400 dark:text-gray-500" /> <Loader2 className="w-8 h-8 animate-spin text-gray-400 dark:text-gray-500" />
@@ -431,7 +449,14 @@ const parseTailwindConfig = (config: string) => {
return `{theme: ${match[1]}};`; return `{theme: ${match[1]}};`;
}; };
const SandpackIframe = ({ reloadKey }: { reloadKey: number }) => { const SandpackIframe = ({
reloadKey,
iframeRef,
}: {
reloadKey: number;
iframeRef: React.RefObject<HTMLIFrameElement | null>;
}) => {
const [appUrlObj, setAppUrlObj] = useAtom(appUrlAtom);
const selectedAppId = useAtomValue(selectedAppIdAtom); const selectedAppId = useAtomValue(selectedAppIdAtom);
const { app } = useLoadApp(selectedAppId); const { app } = useLoadApp(selectedAppId);
const keyRef = useRef<number | null>(null); const keyRef = useRef<number | null>(null);
@@ -453,9 +478,9 @@ const SandpackIframe = ({ reloadKey }: { reloadKey: number }) => {
if (!iframeRef.current || !app) return; if (!iframeRef.current || !app) return;
const sandpackConfig: SandboxSetup = mapSandpackConfig(sandboxConfig); const sandpackConfig: SandboxSetup = mapSandpackConfig(sandboxConfig);
const url = "http://localhost:31111";
const options: ClientOptions = { const options: ClientOptions = {
bundlerURL: "http://localhost:31111", bundlerURL: url,
showOpenInCodeSandbox: false, showOpenInCodeSandbox: false,
showLoadingScreen: true, showLoadingScreen: true,
showErrorScreen: true, showErrorScreen: true,
@@ -469,6 +494,10 @@ const SandpackIframe = ({ reloadKey }: { reloadKey: number }) => {
sandpackConfig, sandpackConfig,
options options
); );
setAppUrlObj({
appUrl: url,
appId: selectedAppId,
});
sandpackClientRef.current = client; sandpackClientRef.current = client;
return client; return client;
} catch (error) { } catch (error) {
@@ -494,7 +523,6 @@ const SandpackIframe = ({ reloadKey }: { reloadKey: number }) => {
if (!iframeRef.current || !app || !selectedAppId) return; if (!iframeRef.current || !app || !selectedAppId) return;
loadSandpack(); loadSandpack();
}, [reloadKey]); }, [reloadKey]);
const iframeRef = useRef<HTMLIFrameElement>(null);
return ( return (
<iframe <iframe

View File

@@ -115,9 +115,13 @@ const handleRequest = (config) => async (req, res) => {
path.join(config.rootDir, parsedUrl.pathname) path.join(config.rootDir, parsedUrl.pathname)
); );
// Handle root path or directory paths, serve index.html // Handle root path, directory paths, or paths without file extensions by serving index.html
if (filePath === path.normalize(config.rootDir) || filePath.endsWith("/")) { if (
filePath = path.join(filePath, "index.html"); filePath === path.normalize(config.rootDir) ||
filePath.endsWith("/") ||
!path.extname(filePath)
) {
filePath = path.join(config.rootDir, "index.html");
} }
// Check if file exists and get its stats // Check if file exists and get its stats