@sampleapp.ai/sdk 1.0.42 → 1.0.44
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/sandbox/Sandbox.js +96 -29
- package/dist/components/sandbox/api.js +73 -1
- package/dist/components/sandbox/guardian/code-focus-section.js +4 -2
- package/dist/components/sandbox/guardian/context/theme-context.js +47 -0
- package/dist/components/sandbox/guardian/demo/guardian-demo.js +4 -4
- package/dist/components/sandbox/guardian/demo/left-view.js +13 -7
- package/dist/components/sandbox/guardian/guardian-component.js +39 -8
- package/dist/components/sandbox/guardian/guardian-playground.js +14 -15
- package/dist/components/sandbox/guardian/guardian-style-wrapper.js +8 -6
- package/dist/components/sandbox/guardian/header.js +37 -41
- package/dist/components/sandbox/guardian/hooks/use-frame-messages.js +24 -8
- package/dist/components/sandbox/guardian/hooks/use-frame-params.js +94 -36
- package/dist/components/sandbox/guardian/hooks/use-sandbox-url-loader.js +16 -4
- package/dist/components/sandbox/guardian/index.js +2 -0
- package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +70 -20
- package/dist/components/sandbox/guardian/right-view/preview-control-bar.js +5 -2
- package/dist/components/sandbox/guardian/right-view/right-top-down-view/network-requests-view.js +117 -0
- package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +38 -140
- package/dist/components/sandbox/guardian/right-view/right-view.js +3 -3
- package/dist/components/sandbox/guardian/right-view/simplified-editor.js +9 -2
- package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +7 -33
- package/dist/components/sandbox/guardian/ui/markdown.js +23 -14
- package/dist/components/sandbox/guardian/utils.js +4 -16
- package/dist/components/sandbox/sandbox-home/SandboxCard.js +7 -5
- package/dist/components/sandbox/sandbox-home/SandboxHome.js +32 -13
- package/dist/components/sandbox/sandbox-home/SearchBar.js +5 -3
- package/dist/hooks/use-tree-selector.js +98 -0
- package/dist/index.d.ts +631 -21
- package/dist/index.es.js +22806 -22028
- package/dist/index.js +13 -4
- package/dist/index.standalone.umd.js +8 -8
- package/dist/lib/api-client.js +68 -0
- package/dist/lib/content-matcher.js +99 -0
- package/dist/lib/generated-css.js +1 -1
- package/dist/lib/shadow-dom-wrapper.js +31 -10
- package/dist/lib/tree-utils.js +193 -0
- package/dist/lib/types/tree-config.js +36 -0
- package/dist/sdk.css +1 -1
- package/dist/tailwind.css +2 -2
- package/package.json +2 -2
|
@@ -1,11 +1,86 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, { useEffect, useState } from "react";
|
|
3
|
-
import {
|
|
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
|
-
import { buildGuardianConfig } from "./guardian/utils";
|
|
8
7
|
import { Skeleton } from "../ui/skeleton";
|
|
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";
|
|
11
|
+
/**
|
|
12
|
+
* Inner component that uses the tree selector hook
|
|
13
|
+
* Separated to ensure hooks are called after data is loaded
|
|
14
|
+
*/
|
|
15
|
+
function SandboxInner({ configWithContent, apiKey, env, themeColor, theme, config }) {
|
|
16
|
+
var _a;
|
|
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({});
|
|
23
|
+
// Handle selection changes - update the config when user selects different options
|
|
24
|
+
const handleSelectionChange = useCallback((selection, content) => {
|
|
25
|
+
if (content) {
|
|
26
|
+
const newConfig = buildConfigFromContent(content);
|
|
27
|
+
// Apply theme color override if provided
|
|
28
|
+
if (themeColor) {
|
|
29
|
+
newConfig.themeColor = themeColor;
|
|
30
|
+
newConfig.useCases = newConfig.useCases.map((uc) => (Object.assign(Object.assign({}, uc), { themeColor: themeColor, frameworks: uc.frameworks.map((fw) => (Object.assign(Object.assign({}, fw), { themeColor: themeColor }))) })));
|
|
31
|
+
}
|
|
32
|
+
setCurrentConfig(newConfig);
|
|
33
|
+
}
|
|
34
|
+
}, [themeColor]);
|
|
35
|
+
// Use the tree selector hook for managing selection state
|
|
36
|
+
const { sections, selectOption, currentContent, selection } = useTreeSelector({
|
|
37
|
+
config: configWithContent.techStackConfig,
|
|
38
|
+
allContent: configWithContent.allContent,
|
|
39
|
+
initialPath: configWithContent.initialSelectionPath,
|
|
40
|
+
onSelectionChange: handleSelectionChange
|
|
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]);
|
|
72
|
+
// Get the first use case and framework from current config
|
|
73
|
+
const firstUseCase = currentConfig.useCases[0];
|
|
74
|
+
const firstFramework = (_a = firstUseCase === null || firstUseCase === void 0 ? void 0 : firstUseCase.frameworks[0]) === null || _a === void 0 ? void 0 : _a.key;
|
|
75
|
+
if (!firstUseCase || !firstFramework) {
|
|
76
|
+
return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-white dark:bg-black" },
|
|
77
|
+
React.createElement("div", { className: "text-red-500" }, "No use cases or frameworks found")));
|
|
78
|
+
}
|
|
79
|
+
return (React.createElement(GuardianProvider, null,
|
|
80
|
+
React.createElement(VmProvider, null,
|
|
81
|
+
React.createElement("div", { className: "h-screen w-screen bg-white dark:bg-black" },
|
|
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, stationaryPublishedUrl: config === null || config === void 0 ? void 0 : config.stationaryPublishedUrl })))));
|
|
83
|
+
}
|
|
9
84
|
/**
|
|
10
85
|
* Sandbox component - simplified API for embedding sandboxes
|
|
11
86
|
*
|
|
@@ -33,14 +108,22 @@ import { Skeleton } from "../ui/skeleton";
|
|
|
33
108
|
* />
|
|
34
109
|
* ```
|
|
35
110
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
* Configurations for the config param
|
|
114
|
+
* {
|
|
115
|
+
* isFrame: boolean;
|
|
116
|
+
* }
|
|
117
|
+
*
|
|
118
|
+
*
|
|
119
|
+
*/
|
|
120
|
+
export default function Sandbox({ apiKey, sandboxId, env, themeColor, theme = "dark", config }) {
|
|
38
121
|
// Validate apiKey immediately
|
|
39
122
|
if (!apiKey || apiKey.trim() === "") {
|
|
40
123
|
return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-white dark:bg-black" },
|
|
41
124
|
React.createElement("div", { className: "text-red-500" }, "Error: apiKey is required. Please provide a valid API key.")));
|
|
42
125
|
}
|
|
43
|
-
const [
|
|
126
|
+
const [configWithContent, setConfigWithContent] = useState(null);
|
|
44
127
|
const [loading, setLoading] = useState(true);
|
|
45
128
|
const [error, setError] = useState(null);
|
|
46
129
|
useEffect(() => {
|
|
@@ -49,14 +132,15 @@ export default function Sandbox({ apiKey, sandboxId, env, themeColor, theme = "d
|
|
|
49
132
|
try {
|
|
50
133
|
setLoading(true);
|
|
51
134
|
setError(null);
|
|
52
|
-
const fetchedConfig = await
|
|
135
|
+
const fetchedConfig = await fetchSandboxConfigWithContent(apiKey, sandboxId);
|
|
53
136
|
if (!cancelled) {
|
|
54
137
|
// Override theme color if provided
|
|
55
138
|
if (themeColor) {
|
|
56
|
-
fetchedConfig.themeColor = themeColor;
|
|
57
|
-
fetchedConfig.useCases =
|
|
139
|
+
fetchedConfig.legacyConfig.themeColor = themeColor;
|
|
140
|
+
fetchedConfig.legacyConfig.useCases =
|
|
141
|
+
fetchedConfig.legacyConfig.useCases.map((uc) => (Object.assign(Object.assign({}, uc), { themeColor: themeColor, frameworks: uc.frameworks.map((fw) => (Object.assign(Object.assign({}, fw), { themeColor: themeColor }))) })));
|
|
58
142
|
}
|
|
59
|
-
|
|
143
|
+
setConfigWithContent(fetchedConfig);
|
|
60
144
|
setLoading(false);
|
|
61
145
|
}
|
|
62
146
|
}
|
|
@@ -82,25 +166,8 @@ export default function Sandbox({ apiKey, sandboxId, env, themeColor, theme = "d
|
|
|
82
166
|
"Error: ",
|
|
83
167
|
error)));
|
|
84
168
|
}
|
|
85
|
-
if (!
|
|
169
|
+
if (!configWithContent) {
|
|
86
170
|
return null;
|
|
87
171
|
}
|
|
88
|
-
|
|
89
|
-
const nestedConfig = buildGuardianConfig(config.useCases, {
|
|
90
|
-
playgroundUid: sandboxId,
|
|
91
|
-
playgroundLogo: config.playgroundLogo || (React.createElement("div", { className: "text-zinc-900 dark:text-white font-bold text-xl" }, config.name)),
|
|
92
|
-
});
|
|
93
|
-
// Get the first use case and framework as defaults
|
|
94
|
-
const firstUseCase = config.useCases[0];
|
|
95
|
-
const firstFramework = (_a = firstUseCase === null || firstUseCase === void 0 ? void 0 : firstUseCase.frameworks[0]) === null || _a === void 0 ? void 0 : _a.key;
|
|
96
|
-
if (!firstUseCase || !firstFramework) {
|
|
97
|
-
return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-white dark:bg-black" },
|
|
98
|
-
React.createElement("div", { className: "text-red-500" }, "No use cases or frameworks found")));
|
|
99
|
-
}
|
|
100
|
-
// TODO: Pass env variables to the container runtime
|
|
101
|
-
// This will be implemented when integrating with the container technology
|
|
102
|
-
return (React.createElement(GuardianProvider, null,
|
|
103
|
-
React.createElement(VmProvider, null,
|
|
104
|
-
React.createElement("div", { className: "h-screen w-screen bg-white dark:bg-black" },
|
|
105
|
-
React.createElement(GuardianPlayground, { nestedConfig: nestedConfig, useCase: firstUseCase.id, framework: firstFramework, isFrame: false, apiKey: apiKey, env: env, chatUid: config.chatUid, theme: theme })))));
|
|
172
|
+
return (React.createElement(SandboxInner, { configWithContent: configWithContent, apiKey: apiKey, env: env, themeColor: themeColor, theme: theme, config: config }));
|
|
106
173
|
}
|
|
@@ -15,10 +15,82 @@ function createDownloadLink(downloadEndpoint) {
|
|
|
15
15
|
document.body.removeChild(link);
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Fetches sandbox configuration
|
|
18
|
+
* Fetches sandbox configuration with all content for upfront loading
|
|
19
|
+
* This enables instant switching between tech stack options without API calls
|
|
20
|
+
*
|
|
21
|
+
* @param apiKey - API key from SandboxProps
|
|
22
|
+
* @param sandboxId - Sandbox content UID (maps to sandbox_content_uid)
|
|
23
|
+
* @returns Extended sandbox configuration with all content
|
|
24
|
+
*/
|
|
25
|
+
export async function fetchSandboxConfigWithContent(apiKey, sandboxId) {
|
|
26
|
+
const client = createApiClient({ apiKey });
|
|
27
|
+
// 1. Fetch the initially requested sandbox content
|
|
28
|
+
const initialContent = await client.sandboxContent.getPublic(sandboxId);
|
|
29
|
+
// 2. Fetch the use case to get tech_stack_config
|
|
30
|
+
const useCase = await client.sandboxUseCase.getById(initialContent.use_case_id);
|
|
31
|
+
// 3. Fetch ALL sandbox content for this use case (for local filtering)
|
|
32
|
+
const allContent = await client.sandboxContent.getByUseCaseId(initialContent.use_case_id);
|
|
33
|
+
// 4. Build legacy config for backward compatibility
|
|
34
|
+
const legacyConfig = buildLegacyConfig(initialContent);
|
|
35
|
+
return {
|
|
36
|
+
useCase,
|
|
37
|
+
techStackConfig: useCase.tech_stack_config,
|
|
38
|
+
allContent,
|
|
39
|
+
initialContent,
|
|
40
|
+
initialSelectionPath: initialContent.selection_path || [],
|
|
41
|
+
legacyConfig,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build legacy SandboxConfig from SandboxContent for backward compatibility
|
|
46
|
+
*/
|
|
47
|
+
function buildLegacyConfig(sandboxContent) {
|
|
48
|
+
const frameworkKey = sandboxContent.framework || Framework.NEXTJS;
|
|
49
|
+
const generatedCode = sandboxContent.generated_code || {};
|
|
50
|
+
const completeCodeZipFile = "";
|
|
51
|
+
return {
|
|
52
|
+
id: sandboxContent.uid,
|
|
53
|
+
name: sandboxContent.uid,
|
|
54
|
+
description: sandboxContent.markdown || "",
|
|
55
|
+
themeColor: "#3b82f6",
|
|
56
|
+
hasPreview: sandboxContent.has_preview,
|
|
57
|
+
chatUid: sandboxContent.chat_uid || "",
|
|
58
|
+
useCases: [
|
|
59
|
+
{
|
|
60
|
+
id: sandboxContent.uid,
|
|
61
|
+
name: sandboxContent.uid,
|
|
62
|
+
description: sandboxContent.markdown || "",
|
|
63
|
+
themeColor: "#3b82f6",
|
|
64
|
+
hasPreview: sandboxContent.has_preview,
|
|
65
|
+
frameworks: [
|
|
66
|
+
{
|
|
67
|
+
key: frameworkKey,
|
|
68
|
+
browserUrl: sandboxContent.published_url || "",
|
|
69
|
+
sandboxUid: sandboxContent.uid,
|
|
70
|
+
codeZipFile: Object.keys(generatedCode).length > 0 ? generatedCode : "",
|
|
71
|
+
completeCodeZipFile,
|
|
72
|
+
useVm: sandboxContent.is_vm_enabled || false,
|
|
73
|
+
CustomConsole: sandboxContent.markdown || "",
|
|
74
|
+
GuideView: "Default Guide",
|
|
75
|
+
gitUrl: sandboxContent.github_url || undefined,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build SandboxConfig from any SandboxContent (for switching between selections)
|
|
84
|
+
*/
|
|
85
|
+
export function buildConfigFromContent(content) {
|
|
86
|
+
return buildLegacyConfig(content);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Fetches sandbox configuration from the API (legacy method)
|
|
19
90
|
* @param apiKey - API key from SandboxProps
|
|
20
91
|
* @param sandboxId - Sandbox content UID (maps to sandbox_content_uid)
|
|
21
92
|
* @returns Sandbox configuration
|
|
93
|
+
* @deprecated Use fetchSandboxConfigWithContent for tree-based selection
|
|
22
94
|
*/
|
|
23
95
|
export async function fetchSandboxConfig(apiKey, sandboxId) {
|
|
24
96
|
const client = createApiClient({
|
|
@@ -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:
|
|
310
|
-
description && (React.createElement("p", { className:
|
|
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
|
+
}
|
|
@@ -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, gitUrl, isBrowserUrlReady = false, sandboxId, apiKey, sandboxError, }) {
|
|
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, hasNetworkView = false, }) {
|
|
8
8
|
const { isBrowserMaximized } = useGuardianContext();
|
|
9
9
|
const { setCurrentView } = useGuardianContext();
|
|
10
10
|
const [reloadCounter, setReloadCounter] = useState(0);
|
|
@@ -24,12 +24,12 @@ export default function GuardianDemo({ CustomConsole, GuideView, browserUrl, pla
|
|
|
24
24
|
}, [setCurrentView]);
|
|
25
25
|
// If isFrame, just return RightView directly
|
|
26
26
|
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, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError }));
|
|
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, hasNetworkView: hasNetworkView }));
|
|
28
28
|
}
|
|
29
29
|
// If browser is maximized, render RightView at full size without the console
|
|
30
30
|
if (isBrowserMaximized && isGuardian) {
|
|
31
31
|
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, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError })));
|
|
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, hasNetworkView: hasNetworkView })));
|
|
33
33
|
}
|
|
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, sandboxId: sandboxId, apiKey: apiKey, sandboxError: sandboxError }) }));
|
|
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, hasNetworkView: hasNetworkView }) }));
|
|
35
35
|
}
|
|
@@ -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: "
|
|
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, {
|
|
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(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
}
|
|
@@ -8,15 +8,17 @@ import { useSandboxUrlLoader } from "./hooks/use-sandbox-url-loader";
|
|
|
8
8
|
import { useFrameMessages } from "./hooks/use-frame-messages";
|
|
9
9
|
import { useVmContext } from "./context/vm-context";
|
|
10
10
|
import { cn } from "../../../lib/utils";
|
|
11
|
-
export default function GuardianComponent({
|
|
12
|
-
gitUrl, }) {
|
|
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, hasNetworkView = false, stationaryPublishedUrl }) {
|
|
13
13
|
const { previewUrl, setPreviewUrl } = useGuardianContext();
|
|
14
14
|
const { vmResolution } = useVmContext();
|
|
15
15
|
const [sandboxError, setSandboxError] = React.useState(null);
|
|
16
16
|
// Build startSandboxConfig for use-sandbox-url-loader
|
|
17
17
|
// apiKey is always included (guaranteed by parent validation)
|
|
18
18
|
// Include chatUid and env when browserUrl is missing (needed for startSandbox API call)
|
|
19
|
-
|
|
19
|
+
// Include hasPreview to skip env/chatUid validation when no preview is needed
|
|
20
|
+
const startSandboxConfig = Object.assign({ apiKey,
|
|
21
|
+
hasPreview }, (!browserUrl && chatUid ? { env, chatUid } : {}));
|
|
20
22
|
// Determine if browserUrl is ready to use immediately (from published_url)
|
|
21
23
|
// If browserUrl exists, it's ready (startSandboxConfig may still exist with just apiKey for VM calls)
|
|
22
24
|
const isBrowserUrlReady = !!browserUrl;
|
|
@@ -26,9 +28,20 @@ gitUrl, }) {
|
|
|
26
28
|
useFrameMessages();
|
|
27
29
|
// Track which main sandbox we've loaded to prevent duplicates
|
|
28
30
|
const loadedSandboxRef = useRef("");
|
|
31
|
+
// Track initial URLs per use case for stationaryPublishedUrl feature
|
|
32
|
+
const initialUrlsByUseCaseRef = useRef(new Map());
|
|
29
33
|
useEffect(() => {
|
|
30
34
|
// Create a key for this sandbox load
|
|
31
35
|
const sandboxKey = `${sandboxUid}-${browserUrl}-${useVm}`;
|
|
36
|
+
const useCaseKey = currentUseCase || "default";
|
|
37
|
+
// If stationaryPublishedUrl is enabled and we already have a URL for this use case,
|
|
38
|
+
// use the stored URL and skip loading a new one
|
|
39
|
+
if (stationaryPublishedUrl &&
|
|
40
|
+
initialUrlsByUseCaseRef.current.has(useCaseKey)) {
|
|
41
|
+
const storedUrl = initialUrlsByUseCaseRef.current.get(useCaseKey);
|
|
42
|
+
setPreviewUrl(storedUrl);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
32
45
|
// Check if we've already initiated a load for this exact sandbox config
|
|
33
46
|
if (loadedSandboxRef.current === sandboxKey) {
|
|
34
47
|
return;
|
|
@@ -39,6 +52,11 @@ gitUrl, }) {
|
|
|
39
52
|
// Found cached URL for this exact combination, use it immediately
|
|
40
53
|
setPreviewUrl(existingUrl);
|
|
41
54
|
loadedSandboxRef.current = sandboxKey;
|
|
55
|
+
// Store as initial URL for this use case if stationaryPublishedUrl is enabled
|
|
56
|
+
if (stationaryPublishedUrl &&
|
|
57
|
+
!initialUrlsByUseCaseRef.current.has(useCaseKey)) {
|
|
58
|
+
initialUrlsByUseCaseRef.current.set(useCaseKey, existingUrl);
|
|
59
|
+
}
|
|
42
60
|
return;
|
|
43
61
|
}
|
|
44
62
|
// If useVm is true, wait for resolution from context before loading
|
|
@@ -59,9 +77,14 @@ gitUrl, }) {
|
|
|
59
77
|
browserUrl,
|
|
60
78
|
useVm,
|
|
61
79
|
resolution: vmResolution,
|
|
62
|
-
apiKey
|
|
80
|
+
apiKey
|
|
63
81
|
})
|
|
64
82
|
.then((url) => {
|
|
83
|
+
// Store as initial URL for this use case if stationaryPublishedUrl is enabled
|
|
84
|
+
if (stationaryPublishedUrl &&
|
|
85
|
+
!initialUrlsByUseCaseRef.current.has(useCaseKey)) {
|
|
86
|
+
initialUrlsByUseCaseRef.current.set(useCaseKey, url);
|
|
87
|
+
}
|
|
65
88
|
setPreviewUrl(url);
|
|
66
89
|
setSandboxError(null); // Clear error on success
|
|
67
90
|
})
|
|
@@ -74,13 +97,21 @@ gitUrl, }) {
|
|
|
74
97
|
});
|
|
75
98
|
// Re-run when sandbox parameters change OR when resolution becomes available
|
|
76
99
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
77
|
-
}, [
|
|
100
|
+
}, [
|
|
101
|
+
sandboxUid,
|
|
102
|
+
browserUrl,
|
|
103
|
+
useVm,
|
|
104
|
+
vmResolution,
|
|
105
|
+
apiKey,
|
|
106
|
+
currentUseCase,
|
|
107
|
+
stationaryPublishedUrl
|
|
108
|
+
]);
|
|
78
109
|
// If isFrame, just render GuardianDemo without the header/layout wrapper
|
|
79
110
|
if (isFrame) {
|
|
80
111
|
return (React.createElement("div", { className: "h-[100vh]" },
|
|
81
|
-
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 })));
|
|
112
|
+
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 })));
|
|
82
113
|
}
|
|
83
|
-
return (React.createElement(AppLayoutNoSidebar, { header: !hideHeader ? (React.createElement(Header, {
|
|
114
|
+
return (React.createElement(AppLayoutNoSidebar, { header: !hideHeader ? (React.createElement(Header, { sections: sections, onSelect: onSelect, playgroundLogo: playgroundLogo, themecolor: themeColor })) : undefined, hasBodyPadding: false },
|
|
84
115
|
React.createElement("div", { className: cn("flex-1 min-h-0 flex flex-col", hideHeader && "pt-4") },
|
|
85
|
-
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 }))));
|
|
116
|
+
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 }))));
|
|
86
117
|
}
|
|
@@ -3,17 +3,24 @@ import React from "react";
|
|
|
3
3
|
import GuardianComponent from "./guardian-component";
|
|
4
4
|
import { useFrameParams } from "./hooks/use-frame-params";
|
|
5
5
|
import { GuardianStyleWrapper } from "./guardian-style-wrapper";
|
|
6
|
+
import { buildGuardianConfig } from "./utils";
|
|
6
7
|
/**
|
|
7
|
-
* Guardian Playground component with
|
|
8
|
-
*
|
|
8
|
+
* Guardian Playground component with tree-based tech stack selection.
|
|
9
|
+
* Uses sections from useTreeSelector for dynamic header rendering.
|
|
9
10
|
*
|
|
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.
|
|
11
14
|
*/
|
|
12
|
-
export default function GuardianPlayground({
|
|
15
|
+
export default function GuardianPlayground({ sandboxConfig, sections, onSelect, useCase, framework, isFrame = false, apiKey, env, chatUid, theme = "dark", hasNetworkView = false, stationaryPublishedUrl }) {
|
|
13
16
|
var _a;
|
|
14
|
-
//
|
|
17
|
+
// Get frame params for iframeUrl override only
|
|
15
18
|
const frameParams = useFrameParams();
|
|
16
|
-
|
|
19
|
+
// Build the nested config from use cases
|
|
20
|
+
const nestedConfig = buildGuardianConfig(sandboxConfig.useCases, {
|
|
21
|
+
playgroundUid: sandboxConfig.id,
|
|
22
|
+
playgroundLogo: sandboxConfig.playgroundLogo || (React.createElement("div", { className: "text-zinc-900 dark:text-white font-bold text-xl" }, sandboxConfig.name))
|
|
23
|
+
});
|
|
17
24
|
const useCaseConfig = nestedConfig[useCase];
|
|
18
25
|
if (!useCaseConfig) {
|
|
19
26
|
return (React.createElement("div", { className: "flex items-center justify-center h-screen" },
|
|
@@ -32,14 +39,6 @@ export default function GuardianPlayground({ nestedConfig, useCase, framework: s
|
|
|
32
39
|
useCase,
|
|
33
40
|
"\"")));
|
|
34
41
|
}
|
|
35
|
-
// Build a map of use case ID to first available framework key
|
|
36
|
-
const firstFrameworkByUseCase = {};
|
|
37
|
-
Object.entries(nestedConfig).forEach(([useCaseId, uc]) => {
|
|
38
|
-
const firstFrameworkKey = Object.keys(uc.frameworks)[0];
|
|
39
|
-
if (firstFrameworkKey) {
|
|
40
|
-
firstFrameworkByUseCase[useCaseId] = firstFrameworkKey;
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
42
|
return (React.createElement(GuardianStyleWrapper, { themeColor: frameworkConfig.themeColor, theme: theme },
|
|
44
|
-
React.createElement(GuardianComponent, {
|
|
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, stationaryPublishedUrl: stationaryPublishedUrl })));
|
|
45
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(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
};
|