@sampleapp.ai/sdk 1.0.43 → 1.0.45

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 (33) hide show
  1. package/dist/components/sandbox/Sandbox.js +44 -7
  2. package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +2 -2
  3. package/dist/components/sandbox/guardian/code-focus-section.js +4 -2
  4. package/dist/components/sandbox/guardian/context/theme-context.js +47 -0
  5. package/dist/components/sandbox/guardian/demo/guardian-demo.js +52 -4
  6. package/dist/components/sandbox/guardian/demo/left-view.js +13 -7
  7. package/dist/components/sandbox/guardian/guardian-component.js +4 -4
  8. package/dist/components/sandbox/guardian/guardian-playground.js +8 -5
  9. package/dist/components/sandbox/guardian/guardian-style-wrapper.js +8 -6
  10. package/dist/components/sandbox/guardian/header.js +15 -6
  11. package/dist/components/sandbox/guardian/hooks/use-frame-messages.js +24 -8
  12. package/dist/components/sandbox/guardian/hooks/use-frame-params.js +94 -36
  13. package/dist/components/sandbox/guardian/index.js +1 -0
  14. package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +4 -4
  15. package/dist/components/sandbox/guardian/right-view/right-top-down-view/network-requests-view.js +117 -0
  16. package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +40 -141
  17. package/dist/components/sandbox/guardian/right-view/right-view.js +3 -3
  18. package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +7 -33
  19. package/dist/components/sandbox/guardian/ui/markdown.js +23 -14
  20. package/dist/components/sandbox/sandbox-home/SandboxCard.js +7 -5
  21. package/dist/components/sandbox/sandbox-home/SandboxHome.js +32 -13
  22. package/dist/components/sandbox/sandbox-home/SearchBar.js +5 -3
  23. package/dist/hooks/use-tree-selector.js +3 -3
  24. package/dist/index.d.ts +43 -7
  25. package/dist/index.es.js +23183 -22721
  26. package/dist/index.js +1 -0
  27. package/dist/index.standalone.umd.js +8 -8
  28. package/dist/lib/generated-css.js +1 -1
  29. package/dist/lib/tree-utils.js +4 -2
  30. package/dist/lib/types/tree-config.js +18 -0
  31. package/dist/sdk.css +1 -1
  32. package/dist/tailwind.css +2 -2
  33. package/package.json +1 -1
@@ -1,18 +1,25 @@
1
1
  "use client";
2
- import React, { useEffect, useState, useCallback } from "react";
3
- import { fetchSandboxConfigWithContent, buildConfigFromContent, } from "./api";
2
+ import React, { useEffect, useState, useCallback, useRef } from "react";
3
+ import { fetchSandboxConfigWithContent, buildConfigFromContent } from "./api";
4
4
  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 { Skeleton } from "../ui/skeleton";
8
8
  import { useTreeSelector } from "../../hooks/use-tree-selector";
9
+ import { SELECTABLE_NODE_TYPES } from "../../lib/types/tree-config";
10
+ import { useFrameParams } from "./guardian/hooks/use-frame-params";
9
11
  /**
10
12
  * Inner component that uses the tree selector hook
11
13
  * Separated to ensure hooks are called after data is loaded
12
14
  */
13
- function SandboxInner({ configWithContent, apiKey, env, themeColor, theme, config, }) {
15
+ function SandboxInner({ configWithContent, apiKey, env, themeColor, theme, config }) {
14
16
  var _a;
15
17
  const [currentConfig, setCurrentConfig] = useState(configWithContent.legacyConfig);
18
+ // Get frame params for syncing external selection changes
19
+ const frameParams = useFrameParams();
20
+ const isFrame = (config === null || config === void 0 ? void 0 : config.isFrame) || frameParams.isFrame;
21
+ // Track previous nodeTypes to detect changes
22
+ const prevNodeTypesRef = useRef({});
16
23
  // Handle selection changes - update the config when user selects different options
17
24
  const handleSelectionChange = useCallback((selection, content) => {
18
25
  if (content) {
@@ -26,12 +33,42 @@ function SandboxInner({ configWithContent, apiKey, env, themeColor, theme, confi
26
33
  }
27
34
  }, [themeColor]);
28
35
  // Use the tree selector hook for managing selection state
29
- const { sections, selectOption, currentContent } = useTreeSelector({
36
+ const { sections, selectOption, currentContent, selection } = useTreeSelector({
30
37
  config: configWithContent.techStackConfig,
31
38
  allContent: configWithContent.allContent,
32
39
  initialPath: configWithContent.initialSelectionPath,
33
- onSelectionChange: handleSelectionChange,
40
+ onSelectionChange: handleSelectionChange
34
41
  });
42
+ // Sync frame params with tree selector when in frame mode
43
+ // This effect listens for changes in URL params (via postMessage or direct URL changes)
44
+ // and updates the tree selector state accordingly
45
+ useEffect(() => {
46
+ if (!isFrame) {
47
+ return;
48
+ }
49
+ const prevNodeTypes = prevNodeTypesRef.current;
50
+ const currentNodeTypes = frameParams.nodeTypes;
51
+ // Check each node type for changes and call selectOption if changed
52
+ for (const nodeType of SELECTABLE_NODE_TYPES) {
53
+ const prevValue = prevNodeTypes[nodeType];
54
+ const currentValue = currentNodeTypes[nodeType];
55
+ // If the value changed and there's a new value, update the selection
56
+ if (currentValue && currentValue !== prevValue) {
57
+ // Only select if this nodeType exists in the current sections
58
+ const sectionExists = sections.some((s) => s.nodeType === nodeType);
59
+ if (sectionExists) {
60
+ // Check if the option exists in the section
61
+ const section = sections.find((s) => s.nodeType === nodeType);
62
+ const optionExists = section === null || section === void 0 ? void 0 : section.options.some((o) => o.key === currentValue);
63
+ if (optionExists) {
64
+ selectOption(nodeType, currentValue);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ // Update the ref for next comparison
70
+ prevNodeTypesRef.current = Object.assign({}, currentNodeTypes);
71
+ }, [isFrame, frameParams.nodeTypes, sections, selectOption]);
35
72
  // Get the first use case and framework from current config
36
73
  const firstUseCase = currentConfig.useCases[0];
37
74
  const firstFramework = (_a = firstUseCase === null || firstUseCase === void 0 ? void 0 : firstUseCase.frameworks[0]) === null || _a === void 0 ? void 0 : _a.key;
@@ -42,7 +79,7 @@ function SandboxInner({ configWithContent, apiKey, env, themeColor, theme, confi
42
79
  return (React.createElement(GuardianProvider, null,
43
80
  React.createElement(VmProvider, null,
44
81
  React.createElement("div", { className: "h-screen w-screen bg-white dark:bg-black" },
45
- React.createElement(GuardianPlayground, { sandboxConfig: currentConfig, sections: sections, onSelect: selectOption, useCase: firstUseCase.id, framework: firstFramework, isFrame: config === null || config === void 0 ? void 0 : config.isFrame, apiKey: apiKey, env: env, chatUid: currentConfig.chatUid, theme: theme })))));
82
+ React.createElement(GuardianPlayground, { sandboxConfig: currentConfig, sections: sections, onSelect: selectOption, useCase: firstUseCase.id, framework: firstFramework, isFrame: isFrame, apiKey: apiKey, env: env, chatUid: currentConfig.chatUid, theme: theme, hasNetworkView: config === null || config === void 0 ? void 0 : config.hasNetworkView })))));
46
83
  }
47
84
  /**
48
85
  * Sandbox component - simplified API for embedding sandboxes
@@ -71,7 +108,7 @@ function SandboxInner({ configWithContent, apiKey, env, themeColor, theme, confi
71
108
  * />
72
109
  * ```
73
110
  */
74
- export default function Sandbox({ apiKey, sandboxId, env, themeColor, theme = "dark", config, }) {
111
+ export default function Sandbox({ apiKey, sandboxId, env, themeColor, theme = "dark", config }) {
75
112
  // Validate apiKey immediately
76
113
  if (!apiKey || apiKey.trim() === "") {
77
114
  return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-white dark:bg-black" },
@@ -1,10 +1,10 @@
1
1
  import React from "react";
2
2
  import { cn } from "../../../lib/utils";
3
- export default function AppLayoutNoSidebar({ header, hasBodyPadding = true, children, textColorStyle, }) {
3
+ export default function AppLayoutNoSidebar({ header, hasBodyPadding = true, children, textColorStyle }) {
4
4
  return (React.createElement("div", { className: "flex flex-col h-full" },
5
5
  header && (React.createElement("header", { className: "flex h-20 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 w-full" }, header))),
7
- React.createElement("div", { className: cn("flex flex-1 flex-col pt-0 min-h-0 pr-6 pb-6", hasBodyPadding && "gap-4 p-4",
7
+ React.createElement("div", { className: cn("flex flex-1 flex-col pt-0 min-h-0 md:pr-6 pb-6", hasBodyPadding && "gap-4 p-4",
8
8
  // Use CSS variable with Tailwind arbitrary value
9
9
  textColorStyle &&
10
10
  "text-[var(--guardian-text-color)] dark:text-[var(--guardian-text-color-dark)]"), style: textColorStyle }, children)));
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import React, { useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import { useGuardianContext } from "./context/guardian-context";
4
+ import { useSdkTheme } from "./context/theme-context";
4
5
  // Helper function to find a node in the file tree by path
5
6
  function getTargetNode(fileTree, targetPath) {
6
7
  const parts = targetPath.split("/").filter(Boolean);
@@ -163,6 +164,7 @@ let sectionIdCounter = 0;
163
164
  export default function CodeFocusSection({ filePath, lineRange, title, description, children, themeColor, }) {
164
165
  const sectionRef = useRef(null);
165
166
  const sectionIdRef = useRef(`code-focus-${++sectionIdCounter}`);
167
+ const { isDark } = useSdkTheme();
166
168
  const { generatedCode, fileTree, setActiveFilePath, setActiveFileName, setActiveLineNumber, setActiveLineRange, updateCode, setLanguage, filesEdited, activeFilePath, activeLineRange, } = useGuardianContext();
167
169
  // Memoize the focus code function to prevent unnecessary recreations
168
170
  const focusCode = useCallback(() => {
@@ -306,7 +308,7 @@ export default function CodeFocusSection({ filePath, lineRange, title, descripti
306
308
  marginLeft: "-2rem",
307
309
  } })),
308
310
  React.createElement("div", { className: "flex items-center gap-2" },
309
- React.createElement("h3", { className: "text-md font-semibold text-gray-900 dark:text-gray-100" }, title)),
310
- description && (React.createElement("p", { className: "text-sm text-gray-400 mt-3" }, description)),
311
+ React.createElement("h3", { className: `text-md font-semibold ${isDark ? "text-gray-100" : "text-gray-900"}` }, title)),
312
+ description && (React.createElement("p", { className: `text-sm mt-3 ${isDark ? "text-gray-400" : "text-gray-600"}` }, description)),
311
313
  children && React.createElement("div", { className: "mt-3" }, children)));
312
314
  }
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import React, { createContext, useContext, useState, useEffect, } from "react";
3
+ const ThemeContext = createContext(undefined);
4
+ export function SdkThemeProvider({ children, theme = "dark" }) {
5
+ const [resolvedTheme, setResolvedTheme] = useState(theme === "system" ? "dark" : theme);
6
+ // Handle system theme preference
7
+ useEffect(() => {
8
+ if (theme === "system") {
9
+ // Check if we're in browser environment
10
+ if (typeof window === "undefined")
11
+ return;
12
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
13
+ setResolvedTheme(mediaQuery.matches ? "dark" : "light");
14
+ const handler = (e) => {
15
+ setResolvedTheme(e.matches ? "dark" : "light");
16
+ };
17
+ mediaQuery.addEventListener("change", handler);
18
+ return () => mediaQuery.removeEventListener("change", handler);
19
+ }
20
+ else {
21
+ setResolvedTheme(theme);
22
+ }
23
+ }, [theme]);
24
+ const value = {
25
+ themeMode: theme,
26
+ resolvedTheme,
27
+ isDark: resolvedTheme === "dark",
28
+ };
29
+ return (React.createElement(ThemeContext.Provider, { value: value }, children));
30
+ }
31
+ export function useSdkTheme() {
32
+ const context = useContext(ThemeContext);
33
+ if (!context) {
34
+ throw new Error("useSdkTheme must be used within a SdkThemeProvider");
35
+ }
36
+ return context;
37
+ }
38
+ /**
39
+ * Hook that returns theme-aware class names
40
+ * @param lightClass - Class to use in light mode
41
+ * @param darkClass - Class to use in dark mode
42
+ * @returns The appropriate class based on current theme
43
+ */
44
+ export function useThemeClass(lightClass, darkClass) {
45
+ const { isDark } = useSdkTheme();
46
+ return isDark ? darkClass : lightClass;
47
+ }
@@ -1,10 +1,14 @@
1
1
  "use client";
2
2
  import React, { useEffect, useState, useCallback } from "react";
3
+ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
3
4
  import ConsoleWithApp from "../ui/console-with-app";
4
5
  import { useGuardianContext } from "../context/guardian-context";
5
6
  import ConsoleWithGuide from "./left-view";
6
7
  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, gitUrl, isBrowserUrlReady = false, sandboxId, apiKey, sandboxError, }) {
8
+ import RightTopDownView from "../right-view/right-top-down-view";
9
+ import { cn } from "../../../../lib/utils";
10
+ import { BookOpen, Monitor } from "lucide-react";
11
+ export default function GuardianDemo({ CustomConsole, GuideView, browserUrl, playgroundUid, useVm, codeZipFile, completeCodeZipFile, variant, themeColor, hasPreview, isFrame = false, isGuardian = false, gitUrl, isBrowserUrlReady = false, sandboxId, apiKey, sandboxError, hasNetworkView = false }) {
8
12
  const { isBrowserMaximized } = useGuardianContext();
9
13
  const { setCurrentView } = useGuardianContext();
10
14
  const [reloadCounter, setReloadCounter] = useState(0);
@@ -19,17 +23,61 @@ export default function GuardianDemo({ CustomConsole, GuideView, browserUrl, pla
19
23
  setTimeout(() => setOverlayStage("hidden"), 3000);
20
24
  }
21
25
  }, []);
26
+ const [mobileTab, setMobileTab] = useState("guide");
22
27
  useEffect(() => {
23
28
  setCurrentView("preview");
24
29
  }, [setCurrentView]);
25
30
  // If isFrame, just return RightView directly
26
31
  if (isFrame) {
27
- return (React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian, isBrowserMaximized: isBrowserMaximized, isBrowserUrlReady: isBrowserUrlReady, isFrame: isFrame, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError }));
32
+ return (React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian, isBrowserMaximized: isBrowserMaximized, isBrowserUrlReady: isBrowserUrlReady, isFrame: isFrame, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError, hasNetworkView: hasNetworkView }));
28
33
  }
29
34
  // If browser is maximized, render RightView at full size without the console
30
35
  if (isBrowserMaximized && isGuardian) {
31
36
  return (React.createElement("div", { className: "w-full h-full rounded-2xl overflow-hidden" },
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, isBrowserUrlReady: isBrowserUrlReady, isFrame: isFrame, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError })));
37
+ 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, isBrowserUrlReady: isBrowserUrlReady, isFrame: isFrame, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError, hasNetworkView: hasNetworkView })));
33
38
  }
34
- return (React.createElement(ConsoleWithApp, { containerClassName: "", console: React.createElement(ConsoleWithGuide, { CustomConsole: CustomConsole, GuideView: GuideView, playgroundUid: playgroundUid, onReloadPreview: handleReloadPreview, onStageChange: handleStageChange, codeZipFile: codeZipFile, themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }), app: React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian, isBrowserMaximized: isBrowserMaximized, isBrowserUrlReady: isBrowserUrlReady, isFrame: isFrame, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError }) }));
39
+ const rightViewProps = {
40
+ reloadCounter,
41
+ overlayStage,
42
+ browserUrl,
43
+ useVm,
44
+ codeZipFile,
45
+ hasPreview,
46
+ variant,
47
+ themeColor,
48
+ completeCodeZipFile,
49
+ isGuardian,
50
+ isBrowserMaximized,
51
+ isBrowserUrlReady: isBrowserUrlReady !== null && isBrowserUrlReady !== void 0 ? isBrowserUrlReady : false,
52
+ isFrame,
53
+ sandboxId,
54
+ apiKey,
55
+ sandboxError,
56
+ hasNetworkView
57
+ };
58
+ return (React.createElement(React.Fragment, null,
59
+ React.createElement("div", { className: "md:hidden h-full flex flex-col min-h-0" },
60
+ React.createElement("div", { className: "flex-1 min-h-0 overflow-hidden flex flex-col" }, mobileTab === "guide" ? (React.createElement(PanelGroup, { direction: "vertical", className: "flex-1 min-h-0" },
61
+ React.createElement(Panel, { defaultSize: 50, minSize: 25, maxSize: 75, order: 1 },
62
+ React.createElement("div", { className: "h-full w-full overflow-y-auto overflow-x-hidden" },
63
+ React.createElement(ConsoleWithGuide, { CustomConsole: CustomConsole, GuideView: GuideView, playgroundUid: playgroundUid, onReloadPreview: handleReloadPreview, onStageChange: handleStageChange, codeZipFile: codeZipFile, themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }))),
64
+ React.createElement(PanelResizeHandle, { className: "h-2 relative group flex-shrink-0 bg-zinc-900 dark:bg-zinc-950" },
65
+ React.createElement("div", { className: "absolute inset-x-0 top-1/2 -translate-y-1/2 h-1 -mt-0.5 group-active:bg-blue-400/30 transition-colors flex justify-center items-center" },
66
+ React.createElement("div", { className: "h-full w-12 rounded-full bg-zinc-600 dark:bg-zinc-500" }))),
67
+ React.createElement(Panel, { defaultSize: 50, minSize: 25, maxSize: 75, order: 2 },
68
+ React.createElement("div", { className: "h-full w-full min-h-0" },
69
+ React.createElement(RightTopDownView, Object.assign({}, rightViewProps, { hasPreview: false, codeOnly: true })))))) : (React.createElement("div", { className: "flex-1 min-h-0" },
70
+ React.createElement(RightView, Object.assign({}, rightViewProps, { previewOnly: true }))))),
71
+ React.createElement("div", { className: "flex-shrink-0 border-t border-zinc-800 dark:border-zinc-700 bg-zinc-900/95 dark:bg-zinc-950/95 backdrop-blur-sm px-3 py-2 flex items-center justify-center gap-1" },
72
+ React.createElement("button", { type: "button", onClick: () => setMobileTab("guide"), className: cn("flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium transition-colors", mobileTab === "guide"
73
+ ? "bg-zinc-700 dark:bg-zinc-600 text-white"
74
+ : "text-zinc-500 hover:text-zinc-300") },
75
+ React.createElement(BookOpen, { className: "w-4 h-4" }),
76
+ "Guide"),
77
+ React.createElement("button", { type: "button", onClick: () => setMobileTab("preview"), className: cn("flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium transition-colors", mobileTab === "preview"
78
+ ? "bg-zinc-700 dark:bg-zinc-600 text-white"
79
+ : "text-zinc-500 hover:text-zinc-300") },
80
+ React.createElement(Monitor, { className: "w-4 h-4" }),
81
+ "Preview"))),
82
+ React.createElement(ConsoleWithApp, { containerClassName: "hidden md:block", console: React.createElement(ConsoleWithGuide, { CustomConsole: CustomConsole, GuideView: GuideView, playgroundUid: playgroundUid, onReloadPreview: handleReloadPreview, onStageChange: handleStageChange, codeZipFile: codeZipFile, themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }), app: React.createElement(RightView, { reloadCounter: reloadCounter, overlayStage: overlayStage, browserUrl: browserUrl, useVm: useVm, codeZipFile: codeZipFile, hasPreview: hasPreview, variant: variant, themeColor: themeColor, completeCodeZipFile: completeCodeZipFile, isGuardian: isGuardian, isBrowserMaximized: isBrowserMaximized, isBrowserUrlReady: isBrowserUrlReady, isFrame: isFrame, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError, hasNetworkView: hasNetworkView }) })));
35
83
  }
@@ -5,10 +5,12 @@ import React, { useMemo, useState } from "react";
5
5
  // import DefaultGuideView from "../default-guide-view";
6
6
  import { Markdown } from "../ui/markdown";
7
7
  import sampleappLogo from "../../../../assets/sampleapp-logo.png";
8
+ import { useSdkTheme } from "../context/theme-context";
8
9
  function Logo({ href, width }) {
10
+ const { isDark } = useSdkTheme();
9
11
  return (React.createElement("div", { className: "flex justify-center md:justify-start" },
10
12
  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" },
13
+ React.createElement("div", { className: isDark ? "" : "invert" },
12
14
  React.createElement("img", { src: sampleappLogo, alt: "SampleApp Logo", width: width || 160, height: 40 })))));
13
15
  }
14
16
  export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundUid, onReloadPreview, onStageChange, codeZipFile, themeColor, browserUrl, gitUrl, sandboxId, apiKey, }) {
@@ -20,7 +22,7 @@ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundU
20
22
  if (typeof CustomConsole === "string") {
21
23
  return (React.createElement("div", { className: "h-full flex flex-col" },
22
24
  React.createElement("div", { className: "flex-1 px-8 " },
23
- React.createElement(Markdown, { className: "text-zinc-900 dark:text-gray-100", themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }, CustomConsole))));
25
+ React.createElement(Markdown, { themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }, CustomConsole))));
24
26
  }
25
27
  const CustomConsoleComponent = CustomConsole;
26
28
  return (React.createElement(CustomConsoleComponent, { onReloadPreview: onReloadPreview !== null && onReloadPreview !== void 0 ? onReloadPreview : (() => { }), onStageChange: onStageChange !== null && onStageChange !== void 0 ? onStageChange : (() => { }), themeColor: themeColor }));
@@ -56,9 +58,13 @@ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundU
56
58
  return (React.createElement("div", { className: "flex-1 min-h-0 relative flex flex-col" },
57
59
  React.createElement("div", { className: "h-[calc(100vh-9.5rem)] min-h-0 overflow-hidden flex-1 flex flex-col" },
58
60
  React.createElement("div", { className: "flex-1 min-h-0 overflow-y-auto" }, isConsoleView && renderCustomConsole()),
59
- React.createElement("div", { className: "relative" },
60
- React.createElement("div", { className: "flex flex-row w-full items-center text-xs justify-center opacity-30 mt-1 pb-2 bg-background" },
61
- React.createElement("span", { className: "text-zinc-600 dark:text-gray-300" }, "Powered by"),
62
- React.createElement("span", { className: "hover:bg-accent rounded-sm p-1 mx-1" },
63
- React.createElement(Logo, { width: 100, href: "https://sampleapp.ai" })))))));
61
+ React.createElement(PoweredByFooter, null))));
62
+ }
63
+ function PoweredByFooter() {
64
+ const { isDark } = useSdkTheme();
65
+ return (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: isDark ? "text-gray-300" : "text-zinc-600" }, "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" })))));
64
70
  }
@@ -9,7 +9,7 @@ import { useFrameMessages } from "./hooks/use-frame-messages";
9
9
  import { useVmContext } from "./context/vm-context";
10
10
  import { cn } from "../../../lib/utils";
11
11
  export default function GuardianComponent({ sections, onSelect, currentFramework, currentUseCase, CustomConsole, GuideView, playgroundLogo, playgroundUid, browserUrl, useVm, sandboxUid, codeZipFile, completeCodeZipFile, variant, themeColor, hasPreview = true, isFrame = false, apiKey, env, chatUid, hideHeader = false, // Hardcoded to true by default, not exposed in Sandbox.tsx
12
- gitUrl, }) {
12
+ gitUrl, hasNetworkView = false }) {
13
13
  const { previewUrl, setPreviewUrl } = useGuardianContext();
14
14
  const { vmResolution } = useVmContext();
15
15
  const [sandboxError, setSandboxError] = React.useState(null);
@@ -61,7 +61,7 @@ gitUrl, }) {
61
61
  browserUrl,
62
62
  useVm,
63
63
  resolution: vmResolution,
64
- apiKey,
64
+ apiKey
65
65
  })
66
66
  .then((url) => {
67
67
  setPreviewUrl(url);
@@ -80,9 +80,9 @@ gitUrl, }) {
80
80
  // If isFrame, just render GuardianDemo without the header/layout wrapper
81
81
  if (isFrame) {
82
82
  return (React.createElement("div", { className: "h-[100vh]" },
83
- 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, isBrowserUrlReady: isBrowserUrlReady, sandboxId: sandboxUid, apiKey: apiKey, sandboxError: sandboxError })));
83
+ 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, isBrowserUrlReady: isBrowserUrlReady, sandboxId: sandboxUid, apiKey: apiKey, sandboxError: sandboxError, hasNetworkView: hasNetworkView })));
84
84
  }
85
85
  return (React.createElement(AppLayoutNoSidebar, { header: !hideHeader ? (React.createElement(Header, { sections: sections, onSelect: onSelect, playgroundLogo: playgroundLogo, themecolor: themeColor })) : undefined, hasBodyPadding: false },
86
86
  React.createElement("div", { className: cn("flex-1 min-h-0 flex flex-col", hideHeader && "pt-4") },
87
- 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, isBrowserUrlReady: isBrowserUrlReady, sandboxId: sandboxUid, apiKey: apiKey, sandboxError: sandboxError }))));
87
+ 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, isBrowserUrlReady: isBrowserUrlReady, sandboxId: sandboxUid, apiKey: apiKey, sandboxError: sandboxError, hasNetworkView: hasNetworkView }))));
88
88
  }
@@ -7,16 +7,19 @@ import { buildGuardianConfig } from "./utils";
7
7
  /**
8
8
  * Guardian Playground component with tree-based tech stack selection.
9
9
  * Uses sections from useTreeSelector for dynamic header rendering.
10
+ *
11
+ * Note: Frame param overrides for node types (architecture, frontend, backend, etc.)
12
+ * are now handled in Sandbox.tsx via the tree selector sync mechanism.
13
+ * This component receives the already-resolved framework from the tree selector.
10
14
  */
11
- export default function GuardianPlayground({ sandboxConfig, sections, onSelect, useCase, framework: serverFramework, isFrame = false, apiKey, env, chatUid, theme = "dark", }) {
15
+ export default function GuardianPlayground({ sandboxConfig, sections, onSelect, useCase, framework, isFrame = false, apiKey, env, chatUid, theme = "dark", hasNetworkView = false }) {
12
16
  var _a;
13
- // When in frame mode, allow URL params to override framework
17
+ // Get frame params for iframeUrl override only
14
18
  const frameParams = useFrameParams();
15
- const framework = isFrame && frameParams.framework ? frameParams.framework : serverFramework;
16
19
  // Build the nested config from use cases
17
20
  const nestedConfig = buildGuardianConfig(sandboxConfig.useCases, {
18
21
  playgroundUid: sandboxConfig.id,
19
- playgroundLogo: sandboxConfig.playgroundLogo || (React.createElement("div", { className: "text-zinc-900 dark:text-white font-bold text-xl" }, sandboxConfig.name)),
22
+ playgroundLogo: sandboxConfig.playgroundLogo || (React.createElement("div", { className: "text-zinc-900 dark:text-white font-bold text-xl" }, sandboxConfig.name))
20
23
  });
21
24
  const useCaseConfig = nestedConfig[useCase];
22
25
  if (!useCaseConfig) {
@@ -37,5 +40,5 @@ export default function GuardianPlayground({ sandboxConfig, sections, onSelect,
37
40
  "\"")));
38
41
  }
39
42
  return (React.createElement(GuardianStyleWrapper, { themeColor: frameworkConfig.themeColor, theme: theme },
40
- React.createElement(GuardianComponent, { sections: sections, onSelect: onSelect, 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, variant: frameworkConfig.variant, themeColor: frameworkConfig.themeColor, hasPreview: frameworkConfig.hasPreview, currentUseCase: useCase, isFrame: isFrame, apiKey: apiKey, env: env, chatUid: chatUid, gitUrl: frameworkConfig.gitUrl, hideHeader: false })));
43
+ React.createElement(GuardianComponent, { sections: sections, onSelect: onSelect, 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, variant: frameworkConfig.variant, themeColor: frameworkConfig.themeColor, hasPreview: frameworkConfig.hasPreview, currentUseCase: useCase, isFrame: isFrame, apiKey: apiKey, env: env, chatUid: chatUid, gitUrl: frameworkConfig.gitUrl, hideHeader: false, hasNetworkView: hasNetworkView })));
41
44
  }
@@ -1,11 +1,12 @@
1
1
  "use client";
2
2
  import React, { useEffect, useRef, useState } from "react";
3
3
  import { TAILWIND_CSS } from "../../../lib/generated-css";
4
+ import { SdkThemeProvider } from "./context/theme-context";
4
5
  /**
5
6
  * Style wrapper for Guardian components that injects Tailwind CSS
6
7
  * into the document without using Shadow DOM (preserves React context)
7
8
  */
8
- export const GuardianStyleWrapper = ({ children, themeColor = "#3b82f6", theme = "dark", }) => {
9
+ export const GuardianStyleWrapper = ({ children, themeColor = "#3b82f6", theme = "dark" }) => {
9
10
  const [stylesInjected, setStylesInjected] = useState(false);
10
11
  const [resolvedTheme, setResolvedTheme] = useState(theme === "system" ? "dark" : theme);
11
12
  const rootRef = useRef(null);
@@ -58,9 +59,10 @@ export const GuardianStyleWrapper = ({ children, themeColor = "#3b82f6", theme =
58
59
  rootRef.current.style.setProperty("--colors-secondary", secondaryColor);
59
60
  }
60
61
  }, [themeColor]);
61
- return (React.createElement("div", { ref: rootRef, className: `guardian-sdk-root bg-background text-foreground ${resolvedTheme === "dark" ? "dark" : ""}`, style: {
62
- width: "100%",
63
- height: "100%",
64
- 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"',
65
- } }, children));
62
+ return (React.createElement(SdkThemeProvider, { theme: theme },
63
+ React.createElement("div", { ref: rootRef, className: `guardian-sdk-root bg-background text-foreground ${resolvedTheme === "dark" ? "dark" : ""}`, style: {
64
+ width: "100%",
65
+ height: "100%",
66
+ 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"'
67
+ } }, children)));
66
68
  };
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import React from "react";
3
+ import { useSdkTheme } from "./context/theme-context";
3
4
  /**
4
5
  * Header component for dynamic tech stack selection
5
6
  *
@@ -7,24 +8,30 @@ import React from "react";
7
8
  * based on the tech_stack_config tree structure.
8
9
  */
9
10
  export default function Header({ sections, onSelect, playgroundLogo, themecolor }) {
10
- console.log("playgroundLogo", playgroundLogo);
11
+ const { isDark } = useSdkTheme();
11
12
  return (React.createElement("div", { className: "w-full sticky top-0 z-50 px-4 lg:px-8 backdrop-blur-md" },
12
13
  React.createElement("div", { className: "flex flex-wrap items-center justify-between py-2" },
13
14
  React.createElement("div", { className: "flex flex-wrap items-center gap-6" }, sections.map((section) => {
14
15
  var _a;
15
16
  return (React.createElement("div", { key: section.nodeType, className: "flex items-center flex-wrap gap-3" },
16
- React.createElement("span", { className: "text-sm font-semibold text-gray-600 dark:text-gray-400 whitespace-nowrap" }, section.label),
17
+ React.createElement("span", { className: `text-sm font-semibold whitespace-nowrap ${isDark ? "text-gray-400" : "text-gray-600"}` }, section.label),
17
18
  React.createElement("div", { className: "hidden md:block" },
18
19
  React.createElement("div", { className: "flex items-center gap-2" }, section.options.map((option) => {
19
20
  const isSelected = option.isSelected;
20
- const isDisabled = option.hasContent === false;
21
+ // Only disable non-leaf nodes that have no content
22
+ // Leaf nodes should always be clickable (they represent final selections)
23
+ const isDisabled = option.hasContent === false && !option.isLeaf;
21
24
  return (React.createElement("button", { key: option.key, onClick: () => {
22
25
  if (!isDisabled) {
23
26
  onSelect(section.nodeType, option.key);
24
27
  }
25
28
  }, disabled: isDisabled, className: `inline-flex items-center px-3 py-1.5 text-xs font-medium rounded-md border transition-all cursor-pointer active:scale-[0.98] ${isSelected
26
- ? "bg-white dark:bg-zinc-900 hover:bg-zinc-100 dark:hover:bg-zinc-800"
27
- : "bg-white dark:bg-zinc-900 border-zinc-300 dark:border-zinc-700 text-zinc-900 dark:text-zinc-100 hover:bg-zinc-100 dark:hover:bg-zinc-800"} ${isDisabled ? "opacity-50 cursor-not-allowed" : ""}`, style: isSelected && themecolor
29
+ ? isDark
30
+ ? "bg-zinc-900 hover:bg-zinc-800"
31
+ : "bg-white hover:bg-zinc-100"
32
+ : isDark
33
+ ? "bg-zinc-900 border-zinc-700 text-zinc-100 hover:bg-zinc-800"
34
+ : "bg-white border-zinc-300 text-zinc-900 hover:bg-zinc-100"} ${isDisabled ? "opacity-50 cursor-not-allowed" : ""}`, style: isSelected && themecolor
28
35
  ? {
29
36
  borderColor: themecolor,
30
37
  color: themecolor
@@ -34,7 +41,9 @@ export default function Header({ sections, onSelect, playgroundLogo, themecolor
34
41
  React.createElement("div", { className: "block md:hidden relative" },
35
42
  React.createElement("select", { value: ((_a = section.options.find((o) => o.isSelected)) === null || _a === void 0 ? void 0 : _a.key) || "", onChange: (e) => {
36
43
  onSelect(section.nodeType, e.target.value);
37
- }, className: "appearance-none bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-md px-3 py-1.5 pr-8 text-xs font-medium text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary dark:focus:ring-primary-light focus:border-transparent transition-all hover:bg-zinc-100 dark:hover:bg-zinc-800" }, section.options.map((option) => (React.createElement("option", { key: option.key, value: option.key, disabled: option.hasContent === false }, option.label)))),
44
+ }, className: `appearance-none border rounded-md px-3 py-1.5 pr-8 text-xs font-medium focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all ${isDark
45
+ ? "bg-zinc-900 border-zinc-700 text-zinc-100 hover:bg-zinc-800"
46
+ : "bg-white border-zinc-300 text-zinc-900 hover:bg-zinc-100"}` }, section.options.map((option) => (React.createElement("option", { key: option.key, value: option.key, disabled: option.hasContent === false && !option.isLeaf }, option.label)))),
38
47
  React.createElement("svg", { "aria-hidden": "true", width: "16", height: "16", viewBox: "0 0 16 16", xmlns: "http://www.w3.org/2000/svg", className: "absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none text-zinc-400" },
39
48
  React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M2.35 9.915a.875.875 0 0 1 1.235-.065L8 13.823l4.415-3.973a.875.875 0 0 1 1.17 1.3l-5 4.5a.873.873 0 0 1-1.17 0l-5-4.5a.875.875 0 0 1-.065-1.235ZM7.415.35a.873.873 0 0 1 1.17 0l5 4.5a.875.875 0 1 1-1.17 1.3L8 2.177 3.585 6.15a.875.875 0 0 1-1.17-1.3l5-4.5Z", fill: "currentColor" })))));
40
49
  })))));
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { useEffect, useRef } from "react";
3
+ import { SELECTABLE_NODE_TYPES } from "../../../../lib/types/tree-config";
3
4
  /**
4
5
  * Hook to handle postMessage communication with parent window when in iframe mode.
5
6
  * - Sends IFRAME_READY when mounted
@@ -7,10 +8,11 @@ import { useEffect, useRef } from "react";
7
8
  * - Works with useFrameParams() which reads the updated URL params
8
9
  *
9
10
  * Supported UPDATE_VIEW message fields:
10
- * - framework: Switch between frameworks (e.g., "nextjs", "react")
11
+ * - Node type selections: product, version, architecture, frontend, backend, framework, integration, platform
11
12
  * - activeFilePath: Change the active file in the editor
12
13
  * - linesStart/linesEnd: Highlight specific line range in the editor
13
14
  * - theme: Override the theme color (hex color)
15
+ * - iframeUrl: Override the preview iframe URL
14
16
  */
15
17
  export function useFrameMessages() {
16
18
  const hasSignaledReady = useRef(false);
@@ -18,11 +20,16 @@ export function useFrameMessages() {
18
20
  if (typeof window === "undefined")
19
21
  return;
20
22
  // Check if we're in an iframe
21
- const isFrame = window.self !== window.top;
22
- if (!isFrame)
23
+ const isInIframe = window.self !== window.top;
24
+ // Also check URL param for isFrame (for testing without actual iframe)
25
+ const searchParams = new URLSearchParams(window.location.search);
26
+ const isFrameParam = searchParams.get("isFrame") === "true";
27
+ // Listen for messages if we're in an iframe OR if isFrame=true in URL
28
+ if (!isInIframe && !isFrameParam) {
23
29
  return;
24
- // Signal to parent that we're ready to receive messages
25
- if (!hasSignaledReady.current && window.parent) {
30
+ }
31
+ // Signal to parent that we're ready to receive messages (only if actually in iframe)
32
+ if (isInIframe && !hasSignaledReady.current && window.parent) {
26
33
  window.parent.postMessage({ type: "IFRAME_READY" }, "*");
27
34
  hasSignaledReady.current = true;
28
35
  }
@@ -32,10 +39,18 @@ export function useFrameMessages() {
32
39
  const data = event.data;
33
40
  if (data.type === "UPDATE_VIEW") {
34
41
  const url = new URL(window.location.href);
35
- // Update URL params based on message
42
+ // Update all node type params
43
+ for (const nodeType of SELECTABLE_NODE_TYPES) {
44
+ const value = data[nodeType];
45
+ if (value !== undefined && typeof value === "string") {
46
+ url.searchParams.set(nodeType, value);
47
+ }
48
+ }
49
+ // Legacy framework param (also handled above via SELECTABLE_NODE_TYPES)
36
50
  if (data.framework !== undefined) {
37
51
  url.searchParams.set("framework", data.framework);
38
52
  }
53
+ // Editor-related params
39
54
  if (data.activeFilePath !== undefined) {
40
55
  url.searchParams.set("activeFilePath", data.activeFilePath);
41
56
  }
@@ -54,12 +69,13 @@ export function useFrameMessages() {
54
69
  // Update URL without page reload
55
70
  window.history.replaceState({}, "", url.toString());
56
71
  // Dispatch a custom event so React components can react immediately
57
- // (useFrameParams may have a slight delay)
58
72
  window.dispatchEvent(new CustomEvent("frameParamsUpdated", { detail: data }));
59
73
  }
60
74
  };
61
75
  window.addEventListener("message", handleMessage);
62
- return () => window.removeEventListener("message", handleMessage);
76
+ return () => {
77
+ window.removeEventListener("message", handleMessage);
78
+ };
63
79
  }, []);
64
80
  return {};
65
81
  }