@sampleapp.ai/sdk 1.0.30 → 1.0.32

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.
Files changed (30) hide show
  1. package/dist/components/sandbox/Sandbox.js +3 -2
  2. package/dist/components/sandbox/api.js +4 -28
  3. package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +6 -3
  4. package/dist/components/sandbox/guardian/demo/guardian-demo.js +2 -2
  5. package/dist/components/sandbox/guardian/demo/left-view.js +17 -5
  6. package/dist/components/sandbox/guardian/guardian-component.js +7 -5
  7. package/dist/components/sandbox/guardian/guardian-playground.js +2 -2
  8. package/dist/components/sandbox/guardian/guardian-style-wrapper.js +17 -3
  9. package/dist/components/sandbox/guardian/hooks/use-sandbox-url-loader.js +0 -20
  10. package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +13 -0
  11. package/dist/components/sandbox/guardian/right-view/right-panel-view.js +17 -4
  12. package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +42 -50
  13. package/dist/components/sandbox/guardian/ui/ai-loader.js +56 -13
  14. package/dist/components/sandbox/guardian/ui/download-and-open-buttons.js +117 -0
  15. package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +33 -66
  16. package/dist/components/sandbox/guardian/ui/markdown.js +15 -10
  17. package/dist/components/sandbox/guardian/ui/theme-color-context.d.ts +6 -0
  18. package/dist/components/sandbox/guardian/utils.js +1 -0
  19. package/dist/components/sandbox/sandbox-home/SandboxCard.js +14 -8
  20. package/dist/components/sandbox/sandbox-home/SandboxHome.js +22 -81
  21. package/dist/components/sandbox/sandbox-home/SearchBar.js +1 -1
  22. package/dist/components/ui/skeleton.js +18 -0
  23. package/dist/index.d.ts +8 -2
  24. package/dist/index.es.js +23944 -23548
  25. package/dist/index.standalone.umd.js +13 -13
  26. package/dist/lib/api-client.js +47 -5
  27. package/dist/lib/generated-css.js +1 -1
  28. package/dist/sdk.css +1 -1
  29. package/dist/tailwind.css +1 -1
  30. package/package.json +12 -7
@@ -5,6 +5,7 @@ import GuardianPlayground from "./guardian/guardian-playground";
5
5
  import { GuardianProvider } from "./guardian/context/guardian-context";
6
6
  import { VmProvider } from "./guardian/context/vm-context";
7
7
  import { buildGuardianConfig } from "./guardian/utils";
8
+ import { Skeleton } from "../ui/skeleton";
8
9
  /**
9
10
  * Sandbox component - simplified API for embedding sandboxes
10
11
  *
@@ -54,8 +55,8 @@ export default function Sandbox({ apiKey, sandboxId, env, themeColor, }) {
54
55
  };
55
56
  }, [apiKey, sandboxId, themeColor]);
56
57
  if (loading) {
57
- return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
58
- React.createElement("div", { className: "text-white" }, "Loading sandbox...")));
58
+ return (React.createElement("div", { className: "h-screen w-screen bg-black" },
59
+ React.createElement(Skeleton, { className: "w-full h-full bg-zinc-900" })));
59
60
  }
60
61
  if (error) {
61
62
  return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
@@ -1,29 +1,5 @@
1
1
  import { Framework, } from "./guardian/types/ide-types";
2
2
  import { createApiClient } from "../../lib/api-client";
3
- /**
4
- * Get the base URL for API requests
5
- * In browser, use relative URL or environment variable
6
- * In Node.js, use environment variable or default
7
- */
8
- function getBaseUrl() {
9
- return "https://api.sampleapp.ai";
10
- // return "http://127.0.0.1:8000";
11
- // if (typeof window !== "undefined") {
12
- // // Browser: try to get from environment or use relative URL
13
- // return (
14
- // (window as any).__SAMPLEAPP_API_BASE_URL__ ||f
15
- // process.env.NEXT_PUBLIC_FASTAPI_APP_URL ||
16
- // process.env.FASTAPI_APP_URL ||
17
- // "http://127.0.0.1:8000"
18
- // );
19
- // }
20
- // // Node.js: use environment variable or default
21
- // return (
22
- // process.env.FASTAPI_APP_URL ||
23
- // process.env.NEXT_PUBLIC_FASTAPI_APP_URL ||
24
- // "http://127.0.0.1:8000"
25
- // );
26
- }
27
3
  /**
28
4
  * Helper function to download a file from a URL
29
5
  */
@@ -45,9 +21,7 @@ function createDownloadLink(downloadEndpoint) {
45
21
  * @returns Sandbox configuration
46
22
  */
47
23
  export async function fetchSandboxConfig(apiKey, sandboxId) {
48
- const baseUrl = getBaseUrl();
49
24
  const client = createApiClient({
50
- baseUrl,
51
25
  apiKey,
52
26
  });
53
27
  // Fetch sandbox content from API
@@ -70,8 +44,8 @@ export async function fetchSandboxConfig(apiKey, sandboxId) {
70
44
  // document.body.removeChild(link);
71
45
  const containerId = ""; // Will be populated when sandbox is started via sdk.startSandbox()
72
46
  const completeCodeZipFile = containerId
73
- ? `${baseUrl}/fileshare?container_id=${containerId}`
74
- : `${baseUrl}/fileshare?container_id=placeholder`; // Placeholder until sandbox is started
47
+ ? `${"hello there"}/fileshare?container_id=${containerId}`
48
+ : `${"hello there"}/fileshare?container_id=placeholder`; // Placeholder until sandbox is started
75
49
  // Transform SandboxContent to SandboxConfig
76
50
  const config = {
77
51
  id: sandboxContent.uid,
@@ -99,6 +73,8 @@ export async function fetchSandboxConfig(apiKey, sandboxId) {
99
73
  // CustomConsole is now sandboxContent.markdown
100
74
  CustomConsole: sandboxContent.markdown || "",
101
75
  GuideView: "Default Guide",
76
+ // Get gitUrl from sandboxContent.github_url
77
+ gitUrl: sandboxContent.github_url || undefined,
102
78
  },
103
79
  ],
104
80
  },
@@ -1,8 +1,11 @@
1
1
  import React from "react";
2
2
  import { cn } from "../../../lib/utils";
3
- export default function AppLayoutNoSidebar({ header, hasBodyPadding = true, children, bodyHeight = "h-[calc(100vh-6rem)]", }) {
4
- return (React.createElement("div", null,
3
+ export default function AppLayoutNoSidebar({ header, hasBodyPadding = true, children, textColorStyle, }) {
4
+ return (React.createElement("div", { className: "flex flex-col h-full" },
5
5
  header && (React.createElement("header", { className: "flex h-24 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 w-full" },
6
6
  React.createElement("div", { className: "flex items-center gap-2 px-4 w-full" }, header))),
7
- React.createElement("div", { className: cn("flex flex-1 flex-col pt-0", hasBodyPadding && "gap-4 p-4", bodyHeight) }, children)));
7
+ React.createElement("div", { className: cn("flex flex-1 flex-col pt-0 min-h-0", hasBodyPadding && "gap-4 p-4",
8
+ // Use CSS variable with Tailwind arbitrary value
9
+ textColorStyle &&
10
+ "text-[var(--guardian-text-color)] dark:text-[var(--guardian-text-color-dark)]"), style: textColorStyle }, children)));
8
11
  }
@@ -4,7 +4,7 @@ import ConsoleWithApp from "../ui/console-with-app";
4
4
  import { useGuardianContext } from "../context/guardian-context";
5
5
  import ConsoleWithGuide from "./left-view";
6
6
  import RightView from "../right-view/right-view";
7
- export default function GuardianDemo({ CustomConsole, GuideView, browserUrl, playgroundUid, useVm, codeZipFile, completeCodeZipFile, variant, themeColor, hasPreview, isFrame = false, isGuardian = false, }) {
7
+ export default function GuardianDemo({ CustomConsole, GuideView, browserUrl, playgroundUid, useVm, codeZipFile, completeCodeZipFile, variant, themeColor, hasPreview, isFrame = false, isGuardian = false, gitUrl, }) {
8
8
  const { isBrowserMaximized } = useGuardianContext();
9
9
  const { setCurrentView } = useGuardianContext();
10
10
  const [reloadCounter, setReloadCounter] = useState(0);
@@ -31,5 +31,5 @@ export default function GuardianDemo({ CustomConsole, GuideView, browserUrl, pla
31
31
  return (React.createElement("div", { className: "w-full h-full rounded-2xl border border-border overflow-hidden" },
32
32
  React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian, isBrowserMaximized: true })));
33
33
  }
34
- return (React.createElement(ConsoleWithApp, { containerClassName: "rounded-2xl border border-border", console: React.createElement(ConsoleWithGuide, { CustomConsole: CustomConsole, GuideView: GuideView, playgroundUid: playgroundUid, onReloadPreview: handleReloadPreview, onStageChange: handleStageChange, codeZipFile: codeZipFile, themeColor: themeColor }), app: React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian }) }));
34
+ return (React.createElement(ConsoleWithApp, { containerClassName: "rounded-2xl border border-border", console: React.createElement(ConsoleWithGuide, { CustomConsole: CustomConsole, GuideView: GuideView, playgroundUid: playgroundUid, onReloadPreview: handleReloadPreview, onStageChange: handleStageChange, codeZipFile: codeZipFile, themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl }), app: React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian }) }));
35
35
  }
@@ -4,7 +4,14 @@ import SlidingConsoleGuideToggle from "./left-view/toggle";
4
4
  // import AskAiView from "../ask-ai-view";
5
5
  // import DefaultGuideView from "../default-guide-view";
6
6
  import { Markdown } from "../ui/markdown";
7
- export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundUid, onReloadPreview, onStageChange, codeZipFile, themeColor, }) {
7
+ import sampleappLogo from "../../../../assets/sampleapp-logo.png";
8
+ function Logo({ href, width }) {
9
+ return (React.createElement("div", { className: "flex justify-center md:justify-start" },
10
+ React.createElement("a", { href: href ? href : "https://sampleapp.ai", className: "flex", target: "_blank", rel: "noopener noreferrer" },
11
+ React.createElement("div", { className: "invert dark:invert-0" },
12
+ React.createElement("img", { src: sampleappLogo, alt: "SampleApp Logo", width: width || 160, height: 40 })))));
13
+ }
14
+ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundUid, onReloadPreview, onStageChange, codeZipFile, themeColor, browserUrl, gitUrl, }) {
8
15
  const [currentView, setCurrentView] = useState("console");
9
16
  const isConsoleView = useMemo(() => currentView === "console", [currentView]);
10
17
  // const isGuideView = useMemo(() => currentView === "guide", [currentView]);
@@ -13,7 +20,7 @@ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundU
13
20
  if (typeof CustomConsole === "string") {
14
21
  return (React.createElement("div", { className: "h-full flex flex-col" },
15
22
  React.createElement("div", { className: "flex-1 px-8 py-6" },
16
- React.createElement(Markdown, { className: "text-gray-100", themeColor: themeColor }, CustomConsole))));
23
+ React.createElement(Markdown, { className: "text-gray-100", themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl }, CustomConsole))));
17
24
  }
18
25
  const CustomConsoleComponent = CustomConsole;
19
26
  return (React.createElement(CustomConsoleComponent, { onReloadPreview: onReloadPreview !== null && onReloadPreview !== void 0 ? onReloadPreview : (() => { }), onStageChange: onStageChange !== null && onStageChange !== void 0 ? onStageChange : (() => { }), themeColor: themeColor }));
@@ -46,13 +53,18 @@ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundU
46
53
  // const GuideComponent = GuideView as ComponentType;
47
54
  // return <GuideComponent />;
48
55
  // };
49
- return (React.createElement("div", { className: "flex-1 min-h-0 relative" },
56
+ return (React.createElement("div", { className: "flex-1 min-h-0 relative flex flex-col" },
50
57
  React.createElement("div", { className: "flex justify-center border-b border-border" },
51
58
  React.createElement(SlidingConsoleGuideToggle, { playgroundUid: playgroundUid, variant: "pill", buttonClassName: "text-sm px-4 py-1", currentView: currentView, setCurrentView: setCurrentView, tabs: [
52
59
  { id: "console", label: "Guide" },
53
60
  // ...(GuideView ? [{id: "guide-og", label: "Guide OG"}] : []),
54
61
  // {id: "chat", label: "Ask AI"},
55
62
  ] })),
56
- React.createElement("div", { className: "h-[calc(100vh-9.5rem)] min-h-0 overflow-hidden" },
57
- React.createElement("div", { className: "h-full min-h-0 overflow-y-auto" }, isConsoleView && renderCustomConsole()))));
63
+ React.createElement("div", { className: "h-[calc(100vh-9.5rem)] min-h-0 overflow-hidden flex-1 flex flex-col" },
64
+ React.createElement("div", { className: "flex-1 min-h-0 overflow-y-auto" }, isConsoleView && renderCustomConsole()),
65
+ React.createElement("div", { className: "relative" },
66
+ React.createElement("div", { className: "flex flex-row w-full items-center text-xs justify-center opacity-30 mt-1 pb-2 bg-background" },
67
+ React.createElement("span", { className: "text-gray-300" }, "Powered by"),
68
+ React.createElement("span", { className: "hover:bg-accent rounded-sm p-1 mx-1" },
69
+ React.createElement(Logo, { width: 100, href: "https://sampleapp.ai" })))))));
58
70
  }
@@ -6,7 +6,9 @@ import React, { useEffect, useRef } from "react";
6
6
  import { useGuardianContext } from "./context/guardian-context";
7
7
  import { useSandboxUrlLoader, } from "./hooks/use-sandbox-url-loader";
8
8
  import { useFrameMessages } from "./hooks/use-frame-messages";
9
- export default function GuardianComponent({ demoOptions, frameworkOptions, firstFrameworkByUseCase, currentFramework, currentUseCase, CustomConsole, GuideView, playgroundLogo, playgroundUid, browserUrl, useVm, sandboxUid, codeZipFile, consoleUrlConfigs, completeCodeZipFile, variant, themeColor, hasPreview = true, isFrame = false, apiKey, env, chatUid, }) {
9
+ import { cn } from "../../../lib/utils";
10
+ export default function GuardianComponent({ demoOptions, frameworkOptions, firstFrameworkByUseCase, currentFramework, currentUseCase, CustomConsole, GuideView, playgroundLogo, playgroundUid, browserUrl, useVm, sandboxUid, codeZipFile, consoleUrlConfigs, completeCodeZipFile, variant, themeColor, hasPreview = true, isFrame = false, apiKey, env, chatUid, hideHeader = true, // Hardcoded to true by default, not exposed in Sandbox.tsx
11
+ gitUrl, }) {
10
12
  const { previewUrl, setPreviewUrl } = useGuardianContext();
11
13
  // Debug: Log the props received for sandbox configuration
12
14
  console.log("[GuardianComponent] API config props:", {
@@ -89,9 +91,9 @@ export default function GuardianComponent({ demoOptions, frameworkOptions, first
89
91
  // If isFrame, just render GuardianDemo without the header/layout wrapper
90
92
  if (isFrame) {
91
93
  return (React.createElement("div", { className: "h-[100vh]" },
92
- React.createElement(GuardianDemo, { CustomConsole: CustomConsole, GuideView: GuideView, browserUrl: previewUrl, playgroundUid: playgroundUid, useVm: useVm, codeZipFile: codeZipFile, completeCodeZipFile: completeCodeZipFile, variant: variant, themeColor: themeColor, hasPreview: hasPreview, isFrame: isFrame, isGuardian: true })));
94
+ React.createElement(GuardianDemo, { CustomConsole: CustomConsole, GuideView: GuideView, browserUrl: previewUrl, playgroundUid: playgroundUid, useVm: useVm, codeZipFile: codeZipFile, completeCodeZipFile: completeCodeZipFile, variant: variant, themeColor: themeColor, hasPreview: hasPreview, isFrame: isFrame, isGuardian: true, gitUrl: gitUrl })));
93
95
  }
94
- return (React.createElement(AppLayoutNoSidebar, { header: React.createElement(Header, { demoOptions: demoOptions, frameworkOptions: frameworkOptions, currentFramework: currentFramework, currentUseCase: currentUseCase, playgroundLogo: playgroundLogo, firstFrameworkByUseCase: firstFrameworkByUseCase }) },
95
- React.createElement("div", { className: "absolute inset-0 mt-24 px-4 pb-4" },
96
- React.createElement(GuardianDemo, { CustomConsole: CustomConsole, GuideView: GuideView, browserUrl: previewUrl, playgroundUid: playgroundUid, useVm: useVm, codeZipFile: codeZipFile, completeCodeZipFile: completeCodeZipFile, variant: variant, themeColor: themeColor, hasPreview: hasPreview, isFrame: isFrame, isGuardian: true }))));
96
+ return (React.createElement(AppLayoutNoSidebar, { header: !hideHeader ? (React.createElement(Header, { demoOptions: demoOptions, frameworkOptions: frameworkOptions, currentFramework: currentFramework, currentUseCase: currentUseCase, playgroundLogo: playgroundLogo, firstFrameworkByUseCase: firstFrameworkByUseCase })) : undefined, hasBodyPadding: false },
97
+ React.createElement("div", { className: cn("flex-1 min-h-0 flex flex-col px-4 pb-4", hideHeader && "pt-4") },
98
+ React.createElement(GuardianDemo, { CustomConsole: CustomConsole, GuideView: GuideView, browserUrl: previewUrl, playgroundUid: playgroundUid, useVm: useVm, codeZipFile: codeZipFile, completeCodeZipFile: completeCodeZipFile, variant: variant, themeColor: themeColor, hasPreview: hasPreview, isFrame: isFrame, isGuardian: true, gitUrl: gitUrl }))));
97
99
  }
@@ -40,6 +40,6 @@ export default function GuardianPlayground({ nestedConfig, useCase, framework: s
40
40
  firstFrameworkByUseCase[useCaseId] = firstFrameworkKey;
41
41
  }
42
42
  });
43
- return (React.createElement(GuardianStyleWrapper, null,
44
- React.createElement(GuardianComponent, { demoOptions: frameworkConfig.demoOptions, frameworkOptions: frameworkConfig.frameworkOptions, firstFrameworkByUseCase: firstFrameworkByUseCase, currentFramework: frameworkConfig.currentFramework, CustomConsole: frameworkConfig.CustomConsole, GuideView: frameworkConfig.GuideView, playgroundUid: frameworkConfig.playgroundUid, browserUrl: (_a = frameParams.iframeUrl) !== null && _a !== void 0 ? _a : frameworkConfig.browserUrl, useVm: frameworkConfig.useVm, playgroundLogo: frameworkConfig.playgroundLogo, sandboxUid: frameworkConfig.sandboxUid, name: frameworkConfig.name, description: frameworkConfig.description, codeZipFile: frameworkConfig.codeZipFile, completeCodeZipFile: frameworkConfig.completeCodeZipFile, consoleUrlConfigs: frameworkConfig.consoleUrlConfigs, variant: frameworkConfig.variant, themeColor: frameworkConfig.themeColor, hasPreview: (_b = frameworkConfig.hasPreview) !== null && _b !== void 0 ? _b : true, currentUseCase: useCase, isFrame: isFrame, apiKey: apiKey, env: env, chatUid: chatUid })));
43
+ return (React.createElement(GuardianStyleWrapper, { themeColor: frameworkConfig.themeColor },
44
+ React.createElement(GuardianComponent, { demoOptions: frameworkConfig.demoOptions, frameworkOptions: frameworkConfig.frameworkOptions, firstFrameworkByUseCase: firstFrameworkByUseCase, currentFramework: frameworkConfig.currentFramework, CustomConsole: frameworkConfig.CustomConsole, GuideView: frameworkConfig.GuideView, playgroundUid: frameworkConfig.playgroundUid, browserUrl: (_a = frameParams.iframeUrl) !== null && _a !== void 0 ? _a : frameworkConfig.browserUrl, useVm: frameworkConfig.useVm, playgroundLogo: frameworkConfig.playgroundLogo, sandboxUid: frameworkConfig.sandboxUid, name: frameworkConfig.name, description: frameworkConfig.description, codeZipFile: frameworkConfig.codeZipFile, completeCodeZipFile: frameworkConfig.completeCodeZipFile, consoleUrlConfigs: frameworkConfig.consoleUrlConfigs, variant: frameworkConfig.variant, themeColor: frameworkConfig.themeColor, hasPreview: (_b = frameworkConfig.hasPreview) !== null && _b !== void 0 ? _b : true, currentUseCase: useCase, isFrame: isFrame, apiKey: apiKey, env: env, chatUid: chatUid, gitUrl: frameworkConfig.gitUrl })));
45
45
  }
@@ -1,12 +1,13 @@
1
1
  "use client";
2
- import React, { useEffect, useState } from "react";
2
+ import React, { useEffect, useRef, useState } from "react";
3
3
  import { TAILWIND_CSS } from "../../../lib/generated-css";
4
4
  /**
5
5
  * Style wrapper for Guardian components that injects Tailwind CSS
6
6
  * into the document without using Shadow DOM (preserves React context)
7
7
  */
8
- export const GuardianStyleWrapper = ({ children, }) => {
8
+ export const GuardianStyleWrapper = ({ children, themeColor = "#3b82f6", }) => {
9
9
  const [stylesInjected, setStylesInjected] = useState(false);
10
+ const rootRef = useRef(null);
10
11
  useEffect(() => {
11
12
  // Only run on client side
12
13
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -29,5 +30,18 @@ export const GuardianStyleWrapper = ({ children, }) => {
29
30
  // Don't remove styles on unmount as other Guardian components might use them
30
31
  };
31
32
  }, []);
32
- return (React.createElement("div", { className: "guardian-sdk-root", style: { width: "100%", height: "100%" } }, children));
33
+ // Set CSS variables for colors based on themeColor
34
+ useEffect(() => {
35
+ if (rootRef.current) {
36
+ rootRef.current.style.setProperty("--colors-primary", themeColor);
37
+ // Create a slightly lighter version for secondary
38
+ const secondaryColor = themeColor; // Can be customized if needed
39
+ rootRef.current.style.setProperty("--colors-secondary", secondaryColor);
40
+ }
41
+ }, [themeColor]);
42
+ return (React.createElement("div", { ref: rootRef, className: "guardian-sdk-root dark bg-background text-foreground", style: {
43
+ width: "100%",
44
+ height: "100%",
45
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
46
+ } }, children));
33
47
  };
@@ -2,24 +2,6 @@
2
2
  import { useCallback } from "react";
3
3
  import { useVmContext } from "../context/vm-context";
4
4
  import { createApiClient } from "../../../../lib/api-client";
5
- /**
6
- * Get the base URL for API requests
7
- * In browser, use relative URL or environment variable
8
- * In Node.js, use environment variable or default
9
- */
10
- function getBaseUrl() {
11
- if (typeof window !== "undefined") {
12
- // Browser: try to get from environment or use relative URL
13
- return (window.__SAMPLEAPP_API_BASE_URL__ ||
14
- process.env.NEXT_PUBLIC_FASTAPI_APP_URL ||
15
- process.env.FASTAPI_APP_URL ||
16
- "http://127.0.0.1:8000");
17
- }
18
- // Node.js: use environment variable or default
19
- return (process.env.FASTAPI_APP_URL ||
20
- process.env.NEXT_PUBLIC_FASTAPI_APP_URL ||
21
- "http://127.0.0.1:8000");
22
- }
23
5
  /**
24
6
  * Creates a cache key from sandboxUid and browserUrl.
25
7
  * This allows caching different URLs for the same sandbox.
@@ -87,9 +69,7 @@ export function useSandboxUrlLoader(startSandboxConfig) {
87
69
  // Non-VM mode: call startSandbox API to get container URL
88
70
  if (startSandboxConfig) {
89
71
  try {
90
- const baseUrl = getBaseUrl();
91
72
  const client = createApiClient({
92
- baseUrl,
93
73
  apiKey: startSandboxConfig.apiKey,
94
74
  });
95
75
  const response = await client.sdk.startSandbox({
@@ -88,12 +88,25 @@ export default function PillFileSelector({ themeColor }) {
88
88
  };
89
89
  // Exclude certain files from display
90
90
  const excludedFiles = [
91
+ // Common config/environment files
91
92
  ".env",
93
+ // Vite/plugin instrumentation files
92
94
  "vite-plugin-react-instrumentation.ts",
93
95
  "instrumentation-client.ts",
96
+ // Global styles (often not needed in code editor)
97
+ "globals.css",
94
98
  ];
95
99
  const filteredFiles = fileEntries.filter(([filePath]) => {
96
100
  const fileName = filePath.split("/").pop() || filePath;
101
+ // Exclude markdown files (*.md)
102
+ if (fileName.endsWith(".md")) {
103
+ return false;
104
+ }
105
+ // Exclude .gitignore files
106
+ if (fileName === ".gitignore" || fileName.endsWith(".gitignore")) {
107
+ return false;
108
+ }
109
+ // Exclude other specific files
97
110
  return !excludedFiles.includes(fileName);
98
111
  });
99
112
  // Scroll a pill into view whenever the active file changes
@@ -1,12 +1,25 @@
1
1
  "use client";
2
- import React from "react";
2
+ import React, { useState, useEffect } from "react";
3
3
  import SlidingEditPreviewToggle from "../demo/left-view/toggle";
4
4
  import Browser from "../ide/browser";
5
5
  import CodeView from "./code-view";
6
6
  import { useGuardianContext } from "../context/guardian-context";
7
7
  import { Component as AiLoader } from "../ui/ai-loader";
8
- export default function RightPanelView({ reloadCounter, overlayStage, browserUrl, useVm, codeZipFile, }) {
8
+ export default function RightPanelView({ reloadCounter, overlayStage, browserUrl, useVm, codeZipFile, themeColor = "#3b82f6", }) {
9
9
  const { setCurrentView, currentView } = useGuardianContext();
10
+ const [showLoader, setShowLoader] = useState(!browserUrl);
11
+ // Delay hiding the loader for 5 seconds after browserUrl becomes available
12
+ useEffect(() => {
13
+ if (browserUrl) {
14
+ const timer = setTimeout(() => {
15
+ setShowLoader(false);
16
+ }, 7000);
17
+ return () => clearTimeout(timer);
18
+ }
19
+ else {
20
+ setShowLoader(true);
21
+ }
22
+ }, [browserUrl]);
10
23
  return (React.createElement("div", null,
11
24
  React.createElement("div", { className: "flex justify-center border-b" },
12
25
  React.createElement(SlidingEditPreviewToggle, { playgroundUid: "guardian", currentView: currentView, setCurrentView: (view) => setCurrentView(view), variant: "pill", buttonClassName: "text-sm px-4 py-1 h-auto", tabs: [
@@ -15,8 +28,8 @@ export default function RightPanelView({ reloadCounter, overlayStage, browserUrl
15
28
  ] })),
16
29
  React.createElement("div", { style: {
17
30
  display: currentView === "preview" ? "block" : "none",
18
- } }, !browserUrl ? (React.createElement("div", { className: "h-[calc(100vh-12.2rem)] w-full relative overflow-hidden rounded-b-none" },
19
- React.createElement(AiLoader, { text: "Booting up...", fullScreen: false }))) : (React.createElement(Browser, { previewUrl: browserUrl, setPreviewUrl: () => { }, containerEndpoint: browserUrl, outerContainerClassName: useVm
31
+ } }, showLoader ? (React.createElement("div", { className: "h-[calc(100vh-12.2rem)] w-full relative overflow-hidden rounded-b-none" },
32
+ React.createElement(AiLoader, { text: "Booting up...", fullScreen: false, themeColor: themeColor }))) : (React.createElement(Browser, { previewUrl: browserUrl, setPreviewUrl: () => { }, containerEndpoint: browserUrl, outerContainerClassName: useVm
20
33
  ? "h-[calc(100vh-9.6rem)] w-full border-none rounded-b-none"
21
34
  : "h-[calc(100vh-12.2rem)] w-full border-none rounded-b-none", reloadSignal: reloadCounter, useVm: useVm }, overlayStage !== "hidden" && (React.createElement("div", { className: "absolute inset-0 z-20 flex items-center justify-center" },
22
35
  overlayStage === "error" && (React.createElement("div", { className: "w-full h-full bg-red-950 text-red-200 flex items-center justify-center" },
@@ -9,26 +9,41 @@ import { useGuardianContext } from "../context/guardian-context";
9
9
  import { useEffect, useMemo, useState } from "react";
10
10
  import { zipPathToFileTree } from "../zip-to-filetree";
11
11
  import { codeZipFileToCodebase } from "../zip-to-codebase";
12
- import { DownloadIcon, Globe, Code2 } from "lucide-react";
12
+ import { DownloadIcon, Globe } from "lucide-react";
13
13
  import { useFrameParams } from "../hooks/use-frame-params";
14
14
  import { cn } from "../../../../lib/utils";
15
+ import { extractContainerIdFromUrl } from "../../../../lib/api-client";
15
16
  export default function RightView({ reloadCounter, overlayStage, browserUrl, useVm, codeZipFile, themeColor, completeCodeZipFile, hasPreview, isPreviewMinimized = false, isGuardian = false, isBrowserMaximized = false, }) {
16
17
  const { setFileTree, setGeneratedCode } = useGuardianContext();
17
18
  const frameParams = useFrameParams();
18
19
  const [activeTab, setActiveTab] = useState("code");
19
20
  const [networkRequests, setNetworkRequests] = useState([]);
20
- // Use frame param override for theme if in frame mode
21
- const effectiveThemeColor = frameParams.isFrame && frameParams.theme ? frameParams.theme : themeColor;
22
- const normalizedZipDownloadPath = useMemo(() => {
23
- let normalizedPath = completeCodeZipFile;
24
- if (normalizedPath.startsWith("/public/")) {
25
- normalizedPath = normalizedPath.slice("/public".length);
21
+ const [showLoader, setShowLoader] = useState(!browserUrl);
22
+ // Delay hiding the loader for 5 seconds after browserUrl becomes available
23
+ useEffect(() => {
24
+ if (browserUrl) {
25
+ const timer = setTimeout(() => {
26
+ setShowLoader(false);
27
+ }, 5000);
28
+ return () => clearTimeout(timer);
26
29
  }
27
- if (!normalizedPath.startsWith("/")) {
28
- normalizedPath = "/" + normalizedPath;
30
+ else {
31
+ setShowLoader(true);
29
32
  }
30
- return normalizedPath;
31
- }, [completeCodeZipFile]);
33
+ }, [browserUrl]);
34
+ // Use frame param override for theme if in frame mode
35
+ const effectiveThemeColor = frameParams.isFrame && frameParams.theme ? frameParams.theme : themeColor;
36
+ // Extract container ID from browserUrl and build download URL
37
+ const downloadCodeUrl = useMemo(() => {
38
+ if (!browserUrl)
39
+ return null;
40
+ const containerId = extractContainerIdFromUrl(browserUrl);
41
+ if (!containerId)
42
+ return null;
43
+ // Use the API base URL for the download endpoint
44
+ const baseUrl = process.env.BASE_API_URL || "https://api.sampleapp.ai";
45
+ return `${baseUrl}/api/v1/sdk/download-code?container_id=${encodeURIComponent(containerId)}`;
46
+ }, [browserUrl]);
32
47
  // Extract only the nested children (actual API calls to external services)
33
48
  const childrenRequests = useMemo(() => {
34
49
  const children = [];
@@ -85,8 +100,8 @@ export default function RightView({ reloadCounter, overlayStage, browserUrl, use
85
100
  }, []);
86
101
  // When browser is maximized, render only the browser at full size
87
102
  if (isBrowserMaximized) {
88
- return (React.createElement("div", { className: "h-full w-full" }, !browserUrl ? (React.createElement("div", { className: "h-full w-full relative overflow-hidden" },
89
- React.createElement(AiLoader, { text: "Booting up...", fullScreen: false }))) : (React.createElement(Browser, { previewUrl: browserUrl, setPreviewUrl: () => { }, containerEndpoint: browserUrl, outerContainerClassName: "h-full w-full border-none", reloadSignal: reloadCounter, useVm: useVm, isGuardian: isGuardian }, overlayStage !== "hidden" && (React.createElement("div", { className: "absolute inset-0 z-20 flex items-center justify-center" },
103
+ return (React.createElement("div", { className: "h-full w-full" }, showLoader ? (React.createElement("div", { className: "h-full w-full relative overflow-hidden" },
104
+ React.createElement(AiLoader, { text: "Booting up...", fullScreen: false, themeColor: effectiveThemeColor }))) : (React.createElement(Browser, { previewUrl: browserUrl, setPreviewUrl: () => { }, containerEndpoint: browserUrl, outerContainerClassName: "h-full w-full border-none", reloadSignal: reloadCounter, useVm: useVm, isGuardian: isGuardian }, overlayStage !== "hidden" && (React.createElement("div", { className: "absolute inset-0 z-20 flex items-center justify-center" },
90
105
  overlayStage === "error" && (React.createElement("div", { className: "w-full h-full bg-red-950 text-red-200 flex items-center justify-center" },
91
106
  React.createElement("div", { className: "text-center" },
92
107
  React.createElement("div", { className: "text-2xl font-semibold" }, "Service Unavailable"),
@@ -104,8 +119,8 @@ export default function RightView({ reloadCounter, overlayStage, browserUrl, use
104
119
  React.createElement(PanelGroup, { direction: "vertical", className: "relative h-full" },
105
120
  hasPreview && !isPreviewMinimized && (React.createElement(React.Fragment, null,
106
121
  React.createElement(Panel, { defaultSize: 58, minSize: 20, maxSize: 80, order: 1 },
107
- React.createElement("div", { className: "h-full w-full" }, !browserUrl ? (React.createElement("div", { className: "h-full w-full relative overflow-hidden" },
108
- React.createElement(AiLoader, { text: "Booting up...", fullScreen: false }))) : (React.createElement(Browser, { previewUrl: browserUrl, setPreviewUrl: () => { }, containerEndpoint: browserUrl, outerContainerClassName: "h-full w-full border-none", reloadSignal: reloadCounter, useVm: useVm, isGuardian: isGuardian }, overlayStage !== "hidden" && (React.createElement("div", { className: "absolute inset-0 z-20 flex items-center justify-center" },
122
+ React.createElement("div", { className: "h-full w-full" }, showLoader ? (React.createElement("div", { className: "h-full w-full relative overflow-hidden" },
123
+ React.createElement(AiLoader, { text: "Booting up...", fullScreen: false, themeColor: effectiveThemeColor }))) : (React.createElement(Browser, { previewUrl: browserUrl, setPreviewUrl: () => { }, containerEndpoint: browserUrl, outerContainerClassName: "h-full w-full border-none", reloadSignal: reloadCounter, useVm: useVm, isGuardian: isGuardian }, overlayStage !== "hidden" && (React.createElement("div", { className: "absolute inset-0 z-20 flex items-center justify-center" },
109
124
  overlayStage === "error" && (React.createElement("div", { className: "w-full h-full bg-red-950 text-red-200 flex items-center justify-center" },
110
125
  React.createElement("div", { className: "text-center" },
111
126
  React.createElement("div", { className: "text-2xl font-semibold" }, "Service Unavailable"),
@@ -125,41 +140,18 @@ export default function RightView({ reloadCounter, overlayStage, browserUrl, use
125
140
  React.createElement("div", { className: "h-full w-px bg-border group-hover:bg-blue-400 transition-colors relative z-10" }))))),
126
141
  React.createElement(Panel, { defaultSize: 42, minSize: 20, maxSize: 80, order: 2 },
127
142
  React.createElement("div", { className: "h-full w-full flex flex-col" },
128
- React.createElement("div", { className: "flex items-center justify-between border-b border-zinc-800 dark:border-zinc-700 bg-zinc-900 dark:bg-zinc-950 flex-shrink-0" },
129
- React.createElement("div", { className: "flex" },
130
- React.createElement("button", { type: "button", onClick: () => setActiveTab("code"), className: cn("flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium transition-colors border-b-2", activeTab === "code"
131
- ? "border-current text-white"
132
- : "border-transparent text-zinc-500 hover:text-zinc-300"), style: activeTab === "code"
133
- ? {
134
- borderColor: effectiveThemeColor,
135
- color: effectiveThemeColor,
136
- }
137
- : undefined },
138
- React.createElement(Code2, { className: "w-3.5 h-3.5" }),
139
- "Code"),
140
- React.createElement("button", { type: "button", onClick: () => setActiveTab("network"), className: cn("flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium transition-colors border-b-2", activeTab === "network"
141
- ? "border-current text-white"
142
- : "border-transparent text-zinc-500 hover:text-zinc-300"), style: activeTab === "network"
143
- ? {
144
- borderColor: effectiveThemeColor,
145
- color: effectiveThemeColor,
146
- }
147
- : undefined },
148
- React.createElement(Globe, { className: "w-3.5 h-3.5" }),
149
- "Network",
150
- childrenRequests.length > 0 && (React.createElement("span", { className: "ml-1 px-1.5 py-0.5 text-[10px] rounded-full bg-zinc-700 text-zinc-300" }, childrenRequests.length)))),
151
- activeTab === "network" && childrenRequests.length > 0 && (React.createElement("button", { type: "button", className: "px-2 text-[10px] font-medium text-zinc-500 hover:text-zinc-300 transition-colors", onClick: () => setNetworkRequests([]) }, "Clear"))),
152
- React.createElement("div", { className: "flex-1 min-h-0 flex flex-col" }, activeTab === "code" ? (React.createElement(React.Fragment, null,
153
- React.createElement("div", { className: "flex items-center justify-between border-b border-zinc-800 dark:border-zinc-700 bg-zinc-900 dark:bg-zinc-950" },
154
- React.createElement(PillFileSelector, { themeColor: effectiveThemeColor }),
155
- React.createElement("div", { className: "px-2 flex-shrink-0" },
156
- React.createElement("button", { type: "button", className: "inline-flex items-center gap-1 text-[10px] font-medium transition-colors hover:opacity-80", style: { color: effectiveThemeColor }, onClick: () => {
157
- window.open(normalizedZipDownloadPath, "_blank");
158
- } },
159
- React.createElement(DownloadIcon, { className: "w-4 h-4" }),
160
- "Download Code"))),
161
- React.createElement("div", { className: "flex-1 min-h-0" },
162
- React.createElement(SimplifiedEditor, { themeColor: effectiveThemeColor })))) : (React.createElement(NetworkRequestsView, { requests: childrenRequests, themeColor: effectiveThemeColor }))))))));
143
+ React.createElement("div", { className: "flex-1 min-h-0 flex flex-col" },
144
+ React.createElement(React.Fragment, null,
145
+ React.createElement("div", { className: "flex items-center justify-between border-b border-zinc-800 dark:border-zinc-700 bg-zinc-900 dark:bg-zinc-950" },
146
+ React.createElement(PillFileSelector, { themeColor: effectiveThemeColor }),
147
+ downloadCodeUrl && (React.createElement("div", { className: "px-2 flex-shrink-0" },
148
+ React.createElement("button", { type: "button", className: "inline-flex items-center gap-1 text-[10px] font-medium transition-colors hover:opacity-80", style: { color: effectiveThemeColor }, onClick: () => {
149
+ window.open(downloadCodeUrl, "_blank");
150
+ } },
151
+ React.createElement(DownloadIcon, { className: "w-4 h-4" }),
152
+ "Download Code")))),
153
+ React.createElement("div", { className: "flex-1 min-h-0" },
154
+ React.createElement(SimplifiedEditor, { themeColor: effectiveThemeColor })))))))));
163
155
  }
164
156
  // Simple Network Requests View Component
165
157
  function NetworkRequestsView({ requests, themeColor, }) {
@@ -1,32 +1,75 @@
1
1
  import * as React from "react";
2
- export const Component = ({ size = 180, text = "Generating", fullScreen = true, }) => {
2
+ // Helper to convert hex to RGB
3
+ function hexToRgb(hex) {
4
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
5
+ return result
6
+ ? {
7
+ r: parseInt(result[1], 16),
8
+ g: parseInt(result[2], 16),
9
+ b: parseInt(result[3], 16),
10
+ }
11
+ : null;
12
+ }
13
+ // Helper to darken a color
14
+ function darkenColor(hex, percent) {
15
+ const rgb = hexToRgb(hex);
16
+ if (!rgb)
17
+ return hex;
18
+ const factor = 1 - percent / 100;
19
+ const r = Math.round(rgb.r * factor);
20
+ const g = Math.round(rgb.g * factor);
21
+ const b = Math.round(rgb.b * factor);
22
+ return `rgb(${r}, ${g}, ${b})`;
23
+ }
24
+ export const Component = ({ size = 180, text = "Generating", fullScreen = true, themeColor = "#3b82f6", // Default blue
25
+ }) => {
3
26
  const letters = text.split("");
27
+ // Generate color variants from themeColor
28
+ const rgb = hexToRgb(themeColor);
29
+ const colorDark = darkenColor(themeColor, 60);
30
+ const colorMid = darkenColor(themeColor, 40);
31
+ const colorLight = darkenColor(themeColor, 20);
32
+ const rgbaGlow = rgb
33
+ ? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3)`
34
+ : "rgba(59, 130, 246, 0.3)";
35
+ // Generate gradient colors for dark mode
36
+ const gradientFrom = darkenColor(themeColor, 30);
4
37
  return (React.createElement("div", { className: [
5
- "flex items-center justify-center bg-gradient-to-b from-gray-100 via-gray-200 to-gray-300 dark:from-[#1a3379] dark:via-[#0f172a] dark:to-black",
38
+ "flex items-center justify-center",
6
39
  fullScreen ? "fixed inset-0 z-50" : "w-full h-full",
7
- ].join(" ") },
40
+ ].join(" "), style: {
41
+ background: `linear-gradient(to bottom, ${gradientFrom}, #0f172a, black)`,
42
+ } },
8
43
  React.createElement("div", { className: "relative flex items-center justify-center font-inter select-none", style: { width: size, height: size } },
9
44
  letters.map((letter, index) => {
10
45
  // Preserve visual spacing for spaces by using a non‑breaking space
11
46
  const displayChar = letter === " " ? "\u00A0" : letter;
12
- return (React.createElement("span", { key: `${letter}-${index}`, className: "inline-block text-gray-800 dark:text-white opacity-40 animate-loaderLetter", style: { animationDelay: `${index * 0.1}s` } }, displayChar));
47
+ return (React.createElement("span", { key: `${letter}-${index}`, className: "inline-block text-white opacity-40 animate-loaderLetter", style: { animationDelay: `${index * 0.1}s` } }, displayChar));
13
48
  }),
14
- React.createElement("div", { className: "absolute inset-0 rounded-full animate-loaderCircle" })),
49
+ React.createElement("div", { className: "absolute inset-0 rounded-full animate-loaderCircle", style: {
50
+ // @ts-ignore - CSS custom property
51
+ "--loader-color-dark": colorDark,
52
+ "--loader-color-mid": colorMid,
53
+ "--loader-color-light": colorLight,
54
+ "--loader-color-glow": rgbaGlow,
55
+ } })),
15
56
  React.createElement("style", null, `
16
57
  @keyframes loaderCircle {
17
58
  0% {
18
59
  transform: rotate(90deg);
19
- box-shadow: 0 6px 12px 0 #4b5563 inset, 0 12px 18px 0 #6b7280 inset,
20
- 0 36px 36px 0 #9ca3af inset,
21
- 0 0 3px 1.2px rgba(107, 114, 128, 0.3),
22
- 0 0 0 0 rgba(107, 114, 128, 0.3);
60
+ box-shadow: 0 6px 12px 0 var(--loader-color-dark) inset,
61
+ 0 12px 18px 0 var(--loader-color-mid) inset,
62
+ 0 36px 36px 0 var(--loader-color-light) inset,
63
+ 0 0 3px 1.2px var(--loader-color-glow),
64
+ 0 0 0 0 var(--loader-color-glow);
23
65
  }
24
66
  100% {
25
67
  transform: rotate(450deg);
26
- box-shadow: 0 6px 12px 0 #4b5563 inset, 0 12px 18px 0 #6b7280 inset,
27
- 0 36px 36px 0 #9ca3af inset,
28
- 0 0 3px 1.2px rgba(107, 114, 128, 0.3),
29
- 0 0 0 0 rgba(107, 114, 128, 0.3);
68
+ box-shadow: 0 6px 12px 0 var(--loader-color-dark) inset,
69
+ 0 12px 18px 0 var(--loader-color-mid) inset,
70
+ 0 36px 36px 0 var(--loader-color-light) inset,
71
+ 0 0 3px 1.2px var(--loader-color-glow),
72
+ 0 0 0 0 var(--loader-color-glow);
30
73
  }
31
74
  }
32
75
  @keyframes loaderLetter {