@sampleapp.ai/sdk 1.0.29 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/guardian/app-layout-no-sidebar.js +8 -0
- package/dist/components/guardian/ask-ai-view.js +249 -0
- package/dist/components/guardian/code-focus-section.d.ts +41 -0
- package/dist/components/guardian/code-focus-section.js +174 -0
- package/dist/components/guardian/context/guardian-context.js +94 -0
- package/dist/components/guardian/context/vm-context.js +28 -0
- package/dist/components/guardian/default-guide-view.js +34 -0
- package/dist/components/guardian/demo/guardian-demo.js +35 -0
- package/dist/components/guardian/demo/left-view/toggle.js +28 -0
- package/dist/components/guardian/demo/left-view.js +49 -0
- package/dist/components/guardian/guardian-component.js +79 -0
- package/dist/components/guardian/guardian-demo.js +35 -0
- package/dist/components/guardian/guardian-home.d.ts +4 -0
- package/dist/components/guardian/guardian-home.js +61 -0
- package/dist/components/guardian/guardian-playground.js +45 -0
- package/dist/components/guardian/guardian-style-wrapper.js +29 -0
- package/dist/components/guardian/guardian-upload-spec.d.ts +14 -0
- package/dist/components/guardian/guardian-upload-spec.js +160 -0
- package/dist/components/guardian/header/glassmorphic-combobox.d.ts +15 -0
- package/dist/components/guardian/header/glassmorphic-combobox.js +30 -0
- package/dist/components/guardian/header.js +61 -0
- package/dist/components/guardian/hooks/use-frame-messages.js +65 -0
- package/dist/components/guardian/hooks/use-frame-params.js +44 -0
- package/dist/components/guardian/hooks/use-sandbox-url-loader.js +101 -0
- package/dist/components/guardian/ide/browser.js +538 -0
- package/dist/components/guardian/index.js +8 -0
- package/dist/components/guardian/layout/app-layout-no-sidebar.js +8 -0
- package/dist/components/guardian/layout/header/glassmorphic-combobox.js +48 -0
- package/dist/components/guardian/layout/header.js +63 -0
- package/dist/components/guardian/right-view/code-view.js +56 -0
- package/dist/components/guardian/right-view/pill-file-selector.js +233 -0
- package/dist/components/guardian/right-view/preview-control-bar.js +25 -0
- package/dist/components/guardian/right-view/right-panel-view.js +38 -0
- package/dist/components/guardian/right-view/right-top-down-view.js +289 -0
- package/dist/components/guardian/right-view/right-view.js +28 -0
- package/dist/components/guardian/right-view/simplified-editor.js +234 -0
- package/dist/components/guardian/types/ide-types.js +162 -0
- package/dist/components/guardian/types.js +3 -0
- package/dist/components/guardian/ui/ai-loader.js +48 -0
- package/dist/components/guardian/ui/badge.js +24 -0
- package/dist/components/guardian/ui/button.js +45 -0
- package/dist/components/guardian/ui/command.js +63 -0
- package/dist/components/guardian/ui/console-with-app.js +17 -0
- package/dist/components/guardian/ui/dialog.js +57 -0
- package/dist/components/guardian/ui/dropdown-menu.js +82 -0
- package/dist/components/guardian/ui/markdown.js +57 -0
- package/dist/components/guardian/ui/popover.js +25 -0
- package/dist/components/guardian/ui/tooltip.js +25 -0
- package/dist/components/guardian/utils.js +88 -0
- package/dist/components/guardian/zip-to-codebase.js +246 -0
- package/dist/components/guardian/zip-to-filetree.js +284 -0
- package/dist/components/icons.js +22 -0
- package/dist/components/sandbox/Sandbox.js +87 -0
- package/dist/components/sandbox/SandboxHome.js +141 -0
- package/dist/components/sandbox/api.js +108 -0
- package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +8 -0
- package/dist/components/sandbox/guardian/ask-ai-view.js +249 -0
- package/dist/components/sandbox/guardian/code-focus-section.js +174 -0
- package/dist/components/sandbox/guardian/context/guardian-context.js +94 -0
- package/dist/components/sandbox/guardian/context/vm-context.js +28 -0
- package/dist/components/sandbox/guardian/default-guide-view.js +34 -0
- package/dist/components/sandbox/guardian/demo/guardian-demo.js +35 -0
- package/dist/components/sandbox/guardian/demo/left-view/toggle.js +28 -0
- package/dist/components/sandbox/guardian/demo/left-view.js +58 -0
- package/dist/components/sandbox/guardian/guardian-component.js +97 -0
- package/dist/components/sandbox/guardian/guardian-demo.js +35 -0
- package/dist/components/sandbox/guardian/guardian-home.d.ts +4 -0
- package/dist/components/sandbox/guardian/guardian-home.js +61 -0
- package/dist/components/sandbox/guardian/guardian-playground.js +45 -0
- package/dist/components/sandbox/guardian/guardian-style-wrapper.js +33 -0
- package/dist/components/sandbox/guardian/guardian-upload-spec.d.ts +14 -0
- package/dist/components/sandbox/guardian/guardian-upload-spec.js +160 -0
- package/dist/components/sandbox/guardian/header/glassmorphic-combobox.js +30 -0
- package/dist/components/sandbox/guardian/header.js +61 -0
- package/dist/components/sandbox/guardian/hooks/use-frame-messages.js +65 -0
- package/dist/components/sandbox/guardian/hooks/use-frame-params.js +44 -0
- package/dist/components/sandbox/guardian/hooks/use-sandbox-url-loader.js +145 -0
- package/dist/components/sandbox/guardian/ide/browser.js +538 -0
- package/dist/components/sandbox/guardian/index.js +8 -0
- package/dist/components/sandbox/guardian/right-view/code-view.js +60 -0
- package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +233 -0
- package/dist/components/sandbox/guardian/right-view/preview-control-bar.js +25 -0
- package/dist/components/sandbox/guardian/right-view/right-panel-view.js +38 -0
- package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +289 -0
- package/dist/components/sandbox/guardian/right-view/right-view.js +28 -0
- package/dist/components/sandbox/guardian/right-view/simplified-editor.js +234 -0
- package/dist/components/sandbox/guardian/types/ide-types.js +162 -0
- package/dist/components/sandbox/guardian/types.js +3 -0
- package/dist/components/sandbox/guardian/ui/ai-loader.js +48 -0
- package/dist/components/sandbox/guardian/ui/badge.js +24 -0
- package/dist/components/sandbox/guardian/ui/button.js +45 -0
- package/dist/components/sandbox/guardian/ui/command.js +63 -0
- package/dist/components/sandbox/guardian/ui/console-with-app.js +17 -0
- package/dist/components/sandbox/guardian/ui/dialog.js +57 -0
- package/dist/components/sandbox/guardian/ui/dropdown-menu.js +82 -0
- package/dist/components/sandbox/guardian/ui/markdown/accordion-group/accordion.js +62 -0
- package/dist/components/sandbox/guardian/ui/markdown/accordion-group.js +23 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-check.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-error.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-info.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-note.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-tip.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-warning.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/shared/callout.js +9 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/shared/types.js +1 -0
- package/dist/components/sandbox/guardian/ui/markdown/card-group/card.js +18 -0
- package/dist/components/sandbox/guardian/ui/markdown/card-group.js +25 -0
- package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +87 -0
- package/dist/components/sandbox/guardian/ui/markdown/code-group.js +101 -0
- package/dist/components/sandbox/guardian/ui/markdown/icon.js +31 -0
- package/dist/components/sandbox/guardian/ui/markdown.js +786 -0
- package/dist/components/sandbox/guardian/ui/popover.js +25 -0
- package/dist/components/sandbox/guardian/ui/tooltip.js +25 -0
- package/dist/components/sandbox/guardian/utils.js +88 -0
- package/dist/components/sandbox/guardian/zip-to-codebase.js +259 -0
- package/dist/components/sandbox/guardian/zip-to-filetree.js +284 -0
- package/dist/components/sandbox/index.js +4 -0
- package/dist/components/sandbox/sandbox-control-bar.js +91 -0
- package/dist/components/sandbox/sandbox-header.js +52 -0
- package/dist/components/sandbox/sandbox-home/SandboxCard.js +59 -0
- package/dist/components/sandbox/sandbox-home/SandboxHome.js +174 -0
- package/dist/components/sandbox/sandbox-home/SearchBar.js +12 -0
- package/dist/components/sandbox/sandbox-home/index.js +3 -0
- package/dist/components/sandbox/sandbox-left-panel.js +248 -0
- package/dist/components/sandbox/sandbox-loading.js +48 -0
- package/dist/components/sandbox/sandbox-right-panel.js +247 -0
- package/dist/components/sandbox/types.js +1 -0
- package/dist/components/sandbox.js +32 -0
- package/dist/components/tailwind-example.js +46 -0
- package/dist/index.d.ts +336 -1
- package/dist/index.es.js +90131 -421
- package/dist/index.js +13 -2
- package/dist/index.standalone.js +61 -53
- package/dist/index.standalone.umd.js +17 -24
- package/dist/lib/api-client.example.js +60 -0
- package/dist/lib/api-client.js +98 -0
- package/dist/lib/generated-css.js +4 -0
- package/dist/lib/inject-styles.js +42 -0
- package/dist/lib/shadow-dom-wrapper.js +42 -0
- package/dist/lib/utils.js +5 -0
- package/dist/sdk.css +1 -1
- package/dist/tailwind.css +1 -0
- package/package.json +32 -5
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Badge } from "./ui/badge";
|
|
4
|
+
import GlassmorphicCombobox from "./header/glassmorphic-combobox";
|
|
5
|
+
export default function Header({ demoOptions, frameworkOptions, firstFrameworkByUseCase, currentFramework, playgroundLogo, currentUseCase, }) {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
// Derive values directly from props (which come from the URL)
|
|
8
|
+
// No internal state - the URL is the source of truth
|
|
9
|
+
const selectedUseCase = currentUseCase || ((_a = demoOptions[0]) === null || _a === void 0 ? void 0 : _a.value);
|
|
10
|
+
const selectedFramework = currentFramework || ((_b = frameworkOptions[0]) === null || _b === void 0 ? void 0 : _b.value);
|
|
11
|
+
// Build options with hrefs
|
|
12
|
+
const demoOptionsWithHrefs = demoOptions.map((opt) => {
|
|
13
|
+
var _a;
|
|
14
|
+
return (Object.assign(Object.assign({}, opt), {
|
|
15
|
+
// Link to first available framework for this use case
|
|
16
|
+
href: `/${opt.value}?framework=${(firstFrameworkByUseCase === null || firstFrameworkByUseCase === void 0 ? void 0 : firstFrameworkByUseCase[opt.value]) || ((_a = frameworkOptions[0]) === null || _a === void 0 ? void 0 : _a.value)}` }));
|
|
17
|
+
});
|
|
18
|
+
const frameworkOptionsWithHrefs = frameworkOptions.map((opt) => (Object.assign(Object.assign({}, opt), { href: `/${selectedUseCase}?framework=${opt.value}` })));
|
|
19
|
+
// Derive environment metadata for badge styling and label
|
|
20
|
+
const environment = (process.env.NEXT_PUBLIC_ENVIRONMENT ||
|
|
21
|
+
process.env.NODE_ENV ||
|
|
22
|
+
"development").toLowerCase();
|
|
23
|
+
const envMeta = {
|
|
24
|
+
production: {
|
|
25
|
+
label: "Production",
|
|
26
|
+
badgeClass: "border-emerald-500/30 bg-emerald-500/10 text-emerald-200",
|
|
27
|
+
dotClass: "bg-emerald-400 shadow-[0_0_0_3px_rgba(16,185,129,0.25)]",
|
|
28
|
+
},
|
|
29
|
+
staging: {
|
|
30
|
+
label: "Staging",
|
|
31
|
+
badgeClass: "border-amber-500/30 bg-amber-500/10 text-amber-100",
|
|
32
|
+
dotClass: "bg-amber-400 shadow-[0_0_0_3px_rgba(245,158,11,0.25)]",
|
|
33
|
+
},
|
|
34
|
+
test: {
|
|
35
|
+
label: "Test",
|
|
36
|
+
badgeClass: "border-cyan-500/30 bg-cyan-500/10 text-cyan-100",
|
|
37
|
+
dotClass: "bg-cyan-400 shadow-[0_0_0_3px_rgba(34,211,238,0.25)]",
|
|
38
|
+
},
|
|
39
|
+
preview: {
|
|
40
|
+
label: "Preview",
|
|
41
|
+
badgeClass: "border-sky-500/30 bg-sky-500/10 text-sky-100",
|
|
42
|
+
dotClass: "bg-sky-400 shadow-[0_0_0_3px_rgba(56,189,248,0.25)]",
|
|
43
|
+
},
|
|
44
|
+
development: {
|
|
45
|
+
label: "Demo Environment",
|
|
46
|
+
badgeClass: "border-violet-500/30 bg-violet-500/10 text-violet-100",
|
|
47
|
+
dotClass: "bg-violet-400 shadow-[0_0_0_3px_rgba(139,92,246,0.25)]",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const meta = envMeta[environment] || envMeta.development;
|
|
51
|
+
return (React.createElement("div", { className: "grid grid-cols-[1fr_auto_1fr] items-center w-full ml-6" },
|
|
52
|
+
React.createElement("div", { className: "justify-self-start" }, playgroundLogo),
|
|
53
|
+
React.createElement("div", { className: "justify-self-center" },
|
|
54
|
+
React.createElement("div", { className: "flex gap-2" },
|
|
55
|
+
React.createElement(GlassmorphicCombobox, { options: demoOptionsWithHrefs, value: selectedUseCase, placeholder: "Select use case...", searchPlaceholder: "Search use case..." }),
|
|
56
|
+
React.createElement(GlassmorphicCombobox, { options: frameworkOptionsWithHrefs, value: selectedFramework, placeholder: "Select framework...", searchPlaceholder: "Search framework...", className: "w-32" }))),
|
|
57
|
+
React.createElement("div", { className: "justify-self-end mr-6" },
|
|
58
|
+
React.createElement(Badge, { className: `rounded-full px-5 py-3 flex items-center gap-2 border ${meta.badgeClass} backdrop-blur-sm shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] text-sm`, "aria-label": `${meta.label} environment` },
|
|
59
|
+
React.createElement("span", { className: `inline-block h-2 w-2 rounded-full ${meta.dotClass}` }),
|
|
60
|
+
meta.label))));
|
|
61
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Hook to handle postMessage communication with parent window when in iframe mode.
|
|
5
|
+
* - Sends IFRAME_READY when mounted
|
|
6
|
+
* - Listens for UPDATE_VIEW messages and updates URL params accordingly
|
|
7
|
+
* - Works with useFrameParams() which reads the updated URL params
|
|
8
|
+
*
|
|
9
|
+
* Supported UPDATE_VIEW message fields:
|
|
10
|
+
* - framework: Switch between frameworks (e.g., "nextjs", "react")
|
|
11
|
+
* - activeFilePath: Change the active file in the editor
|
|
12
|
+
* - linesStart/linesEnd: Highlight specific line range in the editor
|
|
13
|
+
* - theme: Override the theme color (hex color)
|
|
14
|
+
*/
|
|
15
|
+
export function useFrameMessages() {
|
|
16
|
+
const hasSignaledReady = useRef(false);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (typeof window === "undefined")
|
|
19
|
+
return;
|
|
20
|
+
// Check if we're in an iframe
|
|
21
|
+
const isFrame = window.self !== window.top;
|
|
22
|
+
if (!isFrame)
|
|
23
|
+
return;
|
|
24
|
+
// Signal to parent that we're ready to receive messages
|
|
25
|
+
if (!hasSignaledReady.current && window.parent) {
|
|
26
|
+
window.parent.postMessage({ type: "IFRAME_READY" }, "*");
|
|
27
|
+
hasSignaledReady.current = true;
|
|
28
|
+
}
|
|
29
|
+
const handleMessage = (event) => {
|
|
30
|
+
// Optional: Add origin validation for security in production
|
|
31
|
+
// if (event.origin !== 'https://your-allowed-origin.com') return;
|
|
32
|
+
const data = event.data;
|
|
33
|
+
if (data.type === "UPDATE_VIEW") {
|
|
34
|
+
const url = new URL(window.location.href);
|
|
35
|
+
// Update URL params based on message
|
|
36
|
+
if (data.framework !== undefined) {
|
|
37
|
+
url.searchParams.set("framework", data.framework);
|
|
38
|
+
}
|
|
39
|
+
if (data.activeFilePath !== undefined) {
|
|
40
|
+
url.searchParams.set("activeFilePath", data.activeFilePath);
|
|
41
|
+
}
|
|
42
|
+
if (data.linesStart !== undefined) {
|
|
43
|
+
url.searchParams.set("linesStart", String(data.linesStart));
|
|
44
|
+
}
|
|
45
|
+
if (data.linesEnd !== undefined) {
|
|
46
|
+
url.searchParams.set("linesEnd", String(data.linesEnd));
|
|
47
|
+
}
|
|
48
|
+
if (data.theme !== undefined) {
|
|
49
|
+
url.searchParams.set("theme", data.theme);
|
|
50
|
+
}
|
|
51
|
+
if (data.iframeUrl !== undefined) {
|
|
52
|
+
url.searchParams.set("iframeUrl", data.iframeUrl);
|
|
53
|
+
}
|
|
54
|
+
// Update URL without page reload
|
|
55
|
+
window.history.replaceState({}, "", url.toString());
|
|
56
|
+
// Dispatch a custom event so React components can react immediately
|
|
57
|
+
// (useFrameParams may have a slight delay)
|
|
58
|
+
window.dispatchEvent(new CustomEvent("frameParamsUpdated", { detail: data }));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
window.addEventListener("message", handleMessage);
|
|
62
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
63
|
+
}, []);
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Hook to read frame-mode search parameters from URL.
|
|
4
|
+
* These params override config/context when isFrame=true.
|
|
5
|
+
*
|
|
6
|
+
* Note: This is a simplified version that reads from window.location.search.
|
|
7
|
+
* For Next.js apps, you can use the Next.js version that uses useSearchParams.
|
|
8
|
+
*/
|
|
9
|
+
export function useFrameParams() {
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
return {
|
|
13
|
+
isFrame: false,
|
|
14
|
+
framework: undefined,
|
|
15
|
+
theme: undefined,
|
|
16
|
+
activeFilePath: undefined,
|
|
17
|
+
linesStart: undefined,
|
|
18
|
+
linesEnd: undefined,
|
|
19
|
+
iframeUrl: undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
23
|
+
const isFrame = searchParams.get("isFrame") === "true";
|
|
24
|
+
const framework = searchParams.get("framework") || undefined;
|
|
25
|
+
const theme = searchParams.get("theme") || undefined;
|
|
26
|
+
const activeFilePath = searchParams.get("activeFilePath") || undefined;
|
|
27
|
+
const linesStart = searchParams.get("linesStart")
|
|
28
|
+
? parseInt(searchParams.get("linesStart"), 10)
|
|
29
|
+
: undefined;
|
|
30
|
+
const linesEnd = searchParams.get("linesEnd")
|
|
31
|
+
? parseInt(searchParams.get("linesEnd"), 10)
|
|
32
|
+
: undefined;
|
|
33
|
+
const iframeUrl = searchParams.get("iframeUrl") || undefined;
|
|
34
|
+
return {
|
|
35
|
+
isFrame,
|
|
36
|
+
framework,
|
|
37
|
+
theme,
|
|
38
|
+
activeFilePath,
|
|
39
|
+
linesStart,
|
|
40
|
+
linesEnd,
|
|
41
|
+
iframeUrl,
|
|
42
|
+
};
|
|
43
|
+
}, [typeof window !== "undefined" ? window.location.search : ""]);
|
|
44
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { useVmContext } from "../context/vm-context";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a cache key from sandboxUid and browserUrl.
|
|
6
|
+
* This allows caching different URLs for the same sandbox.
|
|
7
|
+
*/
|
|
8
|
+
function getCacheKey(sandboxUid, browserUrl) {
|
|
9
|
+
return `${sandboxUid}::${browserUrl}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Hook that abstracts the logic for loading sandbox URLs.
|
|
13
|
+
* Returns functions to load URLs and check if they're already loaded.
|
|
14
|
+
*/
|
|
15
|
+
export function useSandboxUrlLoader() {
|
|
16
|
+
const { setVmUrl, getVmUrl, setVmError, hasError } = useVmContext();
|
|
17
|
+
/**
|
|
18
|
+
* Loads a sandbox URL. If useVm is true, fetches from the API.
|
|
19
|
+
* Otherwise, uses the browserUrl directly.
|
|
20
|
+
* Returns a promise that resolves to the final URL, or throws if loading fails.
|
|
21
|
+
*
|
|
22
|
+
* Caches by sandboxUid + browserUrl to support multiple URLs per sandbox.
|
|
23
|
+
*/
|
|
24
|
+
const loadSandboxUrl = useCallback(async (config) => {
|
|
25
|
+
const { sandboxUid, browserUrl, useVm } = config;
|
|
26
|
+
const cacheKey = getCacheKey(sandboxUid, browserUrl);
|
|
27
|
+
// Check if URL is already loaded for this specific browserUrl
|
|
28
|
+
const existingUrl = getVmUrl(cacheKey);
|
|
29
|
+
if (existingUrl !== undefined) {
|
|
30
|
+
return existingUrl;
|
|
31
|
+
}
|
|
32
|
+
// Check if we've already attempted and failed for this specific browserUrl
|
|
33
|
+
if (hasError(cacheKey)) {
|
|
34
|
+
throw new Error(`Previously failed to load sandbox URL for ${sandboxUid} with ${browserUrl}`);
|
|
35
|
+
}
|
|
36
|
+
if (useVm) {
|
|
37
|
+
try {
|
|
38
|
+
// Fetch VM URL from API
|
|
39
|
+
// Note: This assumes the API endpoint is available at /api/auto-sandbox
|
|
40
|
+
// You may need to configure this based on your deployment
|
|
41
|
+
const apiBaseUrl = typeof window !== "undefined"
|
|
42
|
+
? window.location.origin
|
|
43
|
+
: "";
|
|
44
|
+
const response = await fetch(`${apiBaseUrl}/api/auto-sandbox?url=${encodeURIComponent(browserUrl)}&mode=json`);
|
|
45
|
+
// Check if response is successful
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const errorText = await response.text();
|
|
48
|
+
console.error(`Failed to fetch sandbox URL for ${sandboxUid}:`, errorText);
|
|
49
|
+
// Mark as error to prevent infinite retries
|
|
50
|
+
setVmError(cacheKey);
|
|
51
|
+
throw new Error(`Failed to create sandbox: ${response.status} ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
if (!data.vncUrl) {
|
|
55
|
+
console.error(`No vncUrl in response for ${sandboxUid}:`, data);
|
|
56
|
+
setVmError(cacheKey);
|
|
57
|
+
throw new Error("No vncUrl in sandbox response");
|
|
58
|
+
}
|
|
59
|
+
const vncUrl = data.vncUrl;
|
|
60
|
+
setVmUrl(cacheKey, vncUrl);
|
|
61
|
+
return vncUrl;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// Mark as error to prevent infinite retries
|
|
65
|
+
setVmError(cacheKey);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Use browserUrl directly (non-VM mode)
|
|
71
|
+
setVmUrl(cacheKey, browserUrl);
|
|
72
|
+
return browserUrl;
|
|
73
|
+
}
|
|
74
|
+
}, [getVmUrl, setVmUrl, hasError, setVmError]);
|
|
75
|
+
/**
|
|
76
|
+
* Gets the URL for a sandbox + browserUrl combination if it's already loaded.
|
|
77
|
+
* Returns undefined if not loaded.
|
|
78
|
+
*/
|
|
79
|
+
const getSandboxUrl = useCallback((sandboxUid, browserUrl) => {
|
|
80
|
+
const cacheKey = getCacheKey(sandboxUid, browserUrl);
|
|
81
|
+
return getVmUrl(cacheKey);
|
|
82
|
+
}, [getVmUrl]);
|
|
83
|
+
/**
|
|
84
|
+
* Preloads URLs for multiple sandboxes in parallel.
|
|
85
|
+
* Errors are logged but don't stop other URLs from loading.
|
|
86
|
+
*/
|
|
87
|
+
const preloadSandboxUrls = useCallback(async (configs) => {
|
|
88
|
+
// Filter out configs that are already loaded or have errors
|
|
89
|
+
const configsToLoad = configs.filter((config) => {
|
|
90
|
+
const cacheKey = getCacheKey(config.sandboxUid, config.browserUrl);
|
|
91
|
+
return getVmUrl(cacheKey) === undefined && !hasError(cacheKey);
|
|
92
|
+
});
|
|
93
|
+
// Load all URLs in parallel, but catch errors individually
|
|
94
|
+
await Promise.allSettled(configsToLoad.map((config) => loadSandboxUrl(config)));
|
|
95
|
+
}, [getVmUrl, loadSandboxUrl, hasError]);
|
|
96
|
+
return {
|
|
97
|
+
loadSandboxUrl,
|
|
98
|
+
getSandboxUrl,
|
|
99
|
+
preloadSandboxUrls,
|
|
100
|
+
};
|
|
101
|
+
}
|