@sampleapp.ai/sdk 1.0.34 → 1.0.36

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 (28) hide show
  1. package/dist/components/sandbox/Sandbox.js +15 -8
  2. package/dist/components/sandbox/guardian/ask-ai-view.js +4 -4
  3. package/dist/components/sandbox/guardian/code-focus-section.js +166 -28
  4. package/dist/components/sandbox/guardian/default-guide-view.js +1 -1
  5. package/dist/components/sandbox/guardian/demo/left-view.js +2 -2
  6. package/dist/components/sandbox/guardian/guardian-playground.js +2 -2
  7. package/dist/components/sandbox/guardian/guardian-style-wrapper.js +21 -2
  8. package/dist/components/sandbox/guardian/ide/browser.js +30 -30
  9. package/dist/components/sandbox/guardian/right-view/preview-control-bar.js +3 -3
  10. package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +1 -1
  11. package/dist/components/sandbox/guardian/right-view/simplified-editor.js +7 -7
  12. package/dist/components/sandbox/guardian/types/ide-types.js +0 -1
  13. package/dist/components/sandbox/guardian/ui/download-and-open-buttons.js +1 -1
  14. package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +48 -11
  15. package/dist/components/sandbox/guardian/ui/markdown.js +1 -1
  16. package/dist/components/sandbox/sandbox-home/SandboxCard.js +4 -4
  17. package/dist/components/sandbox/sandbox-home/SandboxHome.js +9 -9
  18. package/dist/components/sandbox/sandbox-home/SearchBar.js +2 -2
  19. package/dist/index.d.ts +21 -2
  20. package/dist/index.es.js +10128 -19154
  21. package/dist/index.standalone.umd.js +11 -43
  22. package/dist/index.umd.js +99 -127
  23. package/dist/lib/generated-css.js +1 -1
  24. package/dist/lib/ssr-safe-decode-entity.js +16 -0
  25. package/dist/lib/ssr-safe-decode-named-character-reference.js +257 -0
  26. package/dist/sdk.css +1 -1
  27. package/dist/tailwind.css +1 -1
  28. package/package.json +3 -3
@@ -24,9 +24,16 @@ import { Skeleton } from "../ui/skeleton";
24
24
  * apiKey={process.env.NEXT_PUBLIC_SAMPLEAPP_API_KEY!}
25
25
  * sandboxId="launchdarkly-feature-flags"
26
26
  * />
27
+ *
28
+ * // Light mode
29
+ * <Sandbox
30
+ * apiKey={process.env.NEXT_PUBLIC_SAMPLEAPP_API_KEY!}
31
+ * sandboxId="launchdarkly-feature-flags"
32
+ * theme="light"
33
+ * />
27
34
  * ```
28
35
  */
29
- export default function Sandbox({ apiKey, sandboxId, env, themeColor, }) {
36
+ export default function Sandbox({ apiKey, sandboxId, env, themeColor, theme = "dark", }) {
30
37
  var _a;
31
38
  const [config, setConfig] = useState(null);
32
39
  const [loading, setLoading] = useState(true);
@@ -61,11 +68,11 @@ export default function Sandbox({ apiKey, sandboxId, env, themeColor, }) {
61
68
  };
62
69
  }, [apiKey, sandboxId, themeColor]);
63
70
  if (loading) {
64
- return (React.createElement("div", { className: "h-screen w-screen bg-black" },
65
- React.createElement(Skeleton, { className: "w-full h-full bg-zinc-900" })));
71
+ return (React.createElement("div", { className: "h-screen w-screen bg-white dark:bg-black" },
72
+ React.createElement(Skeleton, { className: "w-full h-full bg-zinc-100 dark:bg-zinc-900" })));
66
73
  }
67
74
  if (error) {
68
- return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
75
+ return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-white dark:bg-black" },
69
76
  React.createElement("div", { className: "text-red-500" },
70
77
  "Error: ",
71
78
  error)));
@@ -76,19 +83,19 @@ export default function Sandbox({ apiKey, sandboxId, env, themeColor, }) {
76
83
  // Build the nested config from use cases
77
84
  const nestedConfig = buildGuardianConfig(config.useCases, {
78
85
  playgroundUid: sandboxId,
79
- playgroundLogo: config.playgroundLogo || (React.createElement("div", { className: "text-white font-bold text-xl" }, config.name)),
86
+ playgroundLogo: config.playgroundLogo || (React.createElement("div", { className: "text-zinc-900 dark:text-white font-bold text-xl" }, config.name)),
80
87
  });
81
88
  // Get the first use case and framework as defaults
82
89
  const firstUseCase = config.useCases[0];
83
90
  const firstFramework = (_a = firstUseCase === null || firstUseCase === void 0 ? void 0 : firstUseCase.frameworks[0]) === null || _a === void 0 ? void 0 : _a.key;
84
91
  if (!firstUseCase || !firstFramework) {
85
- return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
92
+ return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-white dark:bg-black" },
86
93
  React.createElement("div", { className: "text-red-500" }, "No use cases or frameworks found")));
87
94
  }
88
95
  // TODO: Pass env variables to the container runtime
89
96
  // This will be implemented when integrating with the container technology
90
97
  return (React.createElement(GuardianProvider, null,
91
98
  React.createElement(VmProvider, null,
92
- React.createElement("div", { className: "h-screen w-screen bg-black" },
93
- React.createElement(GuardianPlayground, { nestedConfig: nestedConfig, useCase: firstUseCase.id, framework: firstFramework, isFrame: false, apiKey: apiKey, env: env, chatUid: config.chatUid })))));
99
+ React.createElement("div", { className: "h-screen w-screen bg-white dark:bg-black" },
100
+ React.createElement(GuardianPlayground, { nestedConfig: nestedConfig, useCase: firstUseCase.id, framework: firstFramework, isFrame: false, apiKey: apiKey, env: env, chatUid: config.chatUid, theme: theme })))));
94
101
  }
@@ -181,12 +181,12 @@ export default function AskAiView({ codeZipFile, playgroundUid, themeColor, }) {
181
181
  React.createElement("span", { className: "text-muted-foreground text-sm" }, "Thinking...")))))) : (React.createElement("div", { className: "flex h-full items-center justify-center" },
182
182
  React.createElement("div", { className: "flex flex-col items-start space-y-4 px-4 max-w-4xl" },
183
183
  React.createElement("div", { className: "text-start" },
184
- React.createElement("h2", { className: "mb-2 text-2xl font-semibold tracking-tight text-gray-200" }, "Hi! Ask me anything"),
184
+ React.createElement("h2", { className: "mb-2 text-2xl font-semibold tracking-tight text-zinc-800 dark:text-gray-200" }, "Hi! Ask me anything"),
185
185
  React.createElement("p", { className: "text-muted-foreground text-base" }, "Try: \u201CHow does this sample app work?\u201D"))))),
186
186
  React.createElement("div", { ref: messagesEndRef })),
187
- React.createElement("div", { className: "shrink-0 border-t border-gray-800/50 bg-[#0a0b0f]/95 backdrop-blur-sm" },
187
+ React.createElement("div", { className: "shrink-0 border-t border-zinc-200 dark:border-gray-800/50 bg-zinc-50/95 dark:bg-[#0a0b0f]/95 backdrop-blur-sm" },
188
188
  React.createElement("div", { className: "max-w-4xl mx-auto px-4 py-4" },
189
- React.createElement("div", { className: "flex items-end gap-3 bg-[#1a1b23] rounded-2xl border border-gray-800/50 px-4 py-3 transition-all", style: {
189
+ React.createElement("div", { className: "flex items-end gap-3 bg-white dark:bg-[#1a1b23] rounded-2xl border border-zinc-200 dark:border-gray-800/50 px-4 py-3 transition-all", style: {
190
190
  boxShadow: `0 0 0 1px transparent`,
191
191
  } },
192
192
  React.createElement("div", { className: "flex-1 min-w-0" },
@@ -195,7 +195,7 @@ export default function AskAiView({ codeZipFile, playgroundUid, themeColor, }) {
195
195
  e.preventDefault();
196
196
  handleSend();
197
197
  }
198
- }, placeholder: "Type your message...", disabled: isLoading, rows: 1, className: "w-full bg-transparent border-0 resize-none text-base text-gray-200 placeholder:text-gray-500 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed max-h-40 overflow-y-auto leading-relaxed", style: {
198
+ }, placeholder: "Type your message...", disabled: isLoading, rows: 1, className: "w-full bg-transparent border-0 resize-none text-base text-zinc-800 dark:text-gray-200 placeholder:text-zinc-400 dark:placeholder:text-gray-500 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed max-h-40 overflow-y-auto leading-relaxed", style: {
199
199
  height: "auto",
200
200
  minHeight: "32px",
201
201
  }, onInput: (e) => {
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import React, { useEffect, useMemo, useRef } from "react";
2
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import { useGuardianContext } from "./context/guardian-context";
4
4
  // Helper function to find a node in the file tree by path
5
5
  function getTargetNode(fileTree, targetPath) {
@@ -13,15 +13,159 @@ function getTargetNode(fileTree, targetPath) {
13
13
  }
14
14
  return current;
15
15
  }
16
+ class CodeFocusSectionCoordinator {
17
+ constructor() {
18
+ this.sections = new Map();
19
+ this.lastActiveSectionId = null;
20
+ this.scrollContainers = new Set();
21
+ this.scrollHandler = null;
22
+ if (typeof window !== "undefined") {
23
+ this.scrollHandler = this.handleScroll.bind(this);
24
+ }
25
+ }
26
+ handleScroll() {
27
+ this.updateActiveSection();
28
+ }
29
+ /**
30
+ * Calculate how "centered" an element is relative to its scroll container.
31
+ * Returns a score from 0 to 1, where 1 means the element's top is at the ideal reading position.
32
+ */
33
+ calculateScore(element) {
34
+ // Find the scroll container by walking up the DOM
35
+ const scrollContainer = this.findScrollContainer(element);
36
+ if (!scrollContainer)
37
+ return 0;
38
+ const containerRect = scrollContainer.getBoundingClientRect();
39
+ const elementRect = element.getBoundingClientRect();
40
+ // Calculate position relative to the scroll container
41
+ const relativeTop = elementRect.top - containerRect.top;
42
+ const relativeBottom = elementRect.bottom - containerRect.top;
43
+ const containerHeight = containerRect.height;
44
+ // Check if element is visible in the container
45
+ if (relativeBottom < 0 || relativeTop > containerHeight) {
46
+ return 0; // Not visible
47
+ }
48
+ // Target point: 20-30% from top of container for natural reading flow
49
+ // This is where the user's eyes naturally focus when reading
50
+ const targetPoint = containerHeight * 0.25;
51
+ // Calculate how close the element's top is to the target point
52
+ const distanceFromTarget = Math.abs(relativeTop - targetPoint);
53
+ const maxDistance = containerHeight;
54
+ // Base score from distance (closer to target = higher score)
55
+ const distanceScore = Math.max(0, 1 - distanceFromTarget / maxDistance);
56
+ // Bonus for elements that are in the "reading zone" (top 50% of container)
57
+ const inReadingZone = relativeTop >= 0 && relativeTop < containerHeight * 0.5;
58
+ const readingZoneBonus = inReadingZone ? 0.3 : 0;
59
+ // Visibility factor - how much of the element is visible
60
+ const visibleTop = Math.max(0, relativeTop);
61
+ const visibleBottom = Math.min(containerHeight, relativeBottom);
62
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
63
+ const elementHeight = elementRect.height;
64
+ const visibilityRatio = elementHeight > 0 ? visibleHeight / elementHeight : 0;
65
+ const visibilityFactor = visibilityRatio * 0.2;
66
+ return distanceScore * 0.5 + readingZoneBonus + visibilityFactor;
67
+ }
68
+ findScrollContainer(element) {
69
+ let current = element.parentElement;
70
+ while (current) {
71
+ const style = window.getComputedStyle(current);
72
+ const overflowY = style.overflowY;
73
+ if ((overflowY === "auto" || overflowY === "scroll") &&
74
+ current.scrollHeight > current.clientHeight) {
75
+ return current;
76
+ }
77
+ current = current.parentElement;
78
+ }
79
+ return null;
80
+ }
81
+ updateActiveSection() {
82
+ if (this.sections.size === 0)
83
+ return;
84
+ // Find the section with the highest score
85
+ let bestSection = null;
86
+ let bestScore = -1;
87
+ let bestId = null;
88
+ for (const [id, entry] of this.sections) {
89
+ const score = this.calculateScore(entry.element);
90
+ if (score > bestScore) {
91
+ bestScore = score;
92
+ bestSection = entry;
93
+ bestId = id;
94
+ }
95
+ }
96
+ // Only trigger if we have a valid section with a reasonable score
97
+ // and it's different from the last one
98
+ if (bestSection && bestId && bestScore > 0.1 && bestId !== this.lastActiveSectionId) {
99
+ this.lastActiveSectionId = bestId;
100
+ bestSection.focusCallback();
101
+ }
102
+ }
103
+ register(id, element, focusCallback) {
104
+ const entry = { element, focusCallback, id };
105
+ this.sections.set(id, entry);
106
+ element.setAttribute("data-code-focus-id", id);
107
+ // Find and attach to the scroll container
108
+ const scrollContainer = this.findScrollContainer(element);
109
+ if (scrollContainer && this.scrollHandler) {
110
+ if (!this.scrollContainers.has(scrollContainer)) {
111
+ scrollContainer.addEventListener("scroll", this.scrollHandler, { passive: true });
112
+ this.scrollContainers.add(scrollContainer);
113
+ }
114
+ }
115
+ // Trigger initial update
116
+ this.updateActiveSection();
117
+ // Return cleanup function
118
+ return () => {
119
+ this.sections.delete(id);
120
+ if (this.lastActiveSectionId === id) {
121
+ this.lastActiveSectionId = null;
122
+ }
123
+ // Clean up scroll listeners if no more sections
124
+ if (this.sections.size === 0) {
125
+ for (const container of this.scrollContainers) {
126
+ if (this.scrollHandler) {
127
+ container.removeEventListener("scroll", this.scrollHandler);
128
+ }
129
+ }
130
+ this.scrollContainers.clear();
131
+ }
132
+ };
133
+ }
134
+ // Force focus a specific section (for click handling)
135
+ forceFocus(id) {
136
+ const section = this.sections.get(id);
137
+ if (section) {
138
+ this.lastActiveSectionId = id;
139
+ section.focusCallback();
140
+ }
141
+ }
142
+ }
143
+ // Singleton instance - created lazily to avoid SSR issues
144
+ let coordinatorInstance = null;
145
+ function getCoordinator() {
146
+ if (!coordinatorInstance && typeof window !== "undefined") {
147
+ coordinatorInstance = new CodeFocusSectionCoordinator();
148
+ }
149
+ return coordinatorInstance;
150
+ }
151
+ // Counter for generating unique IDs
152
+ let sectionIdCounter = 0;
16
153
  /**
17
154
  * Component that focuses on a specific file and line range in the code editor
18
155
  * when it comes into view while scrolling, or when the user clicks "View Code".
156
+ *
157
+ * Uses a global coordinator pattern (similar to Stripe docs) to ensure:
158
+ * - Only ONE section is active at a time
159
+ * - The section closest to the viewport center is selected
160
+ * - Smooth transitions without flickering during fast scrolling
161
+ * - Natural reading flow with slight bias towards top of viewport
19
162
  */
20
- export default function CodeFocusSection({ filePath, lineRange, title, description, children, threshold = 0, themeColor, }) {
163
+ export default function CodeFocusSection({ filePath, lineRange, title, description, children, themeColor, }) {
21
164
  const sectionRef = useRef(null);
165
+ const sectionIdRef = useRef(`code-focus-${++sectionIdCounter}`);
22
166
  const { generatedCode, fileTree, setActiveFilePath, setActiveFileName, setActiveLineNumber, setActiveLineRange, updateCode, setLanguage, filesEdited, activeFilePath, activeLineRange, } = useGuardianContext();
23
- // Moves the editor's focus to this file/lines
24
- const focusCode = () => {
167
+ // Memoize the focus code function to prevent unnecessary recreations
168
+ const focusCode = useCallback(() => {
25
169
  // Get the file code from generatedCode or fileTree
26
170
  // Always search by file_path property first, as that's the source of truth
27
171
  let code = "";
@@ -94,41 +238,35 @@ export default function CodeFocusSection({ filePath, lineRange, title, descripti
94
238
  setActiveLineNumber(undefined);
95
239
  }
96
240
  }
97
- };
98
- useEffect(() => {
99
- const element = sectionRef.current;
100
- if (!element)
101
- return;
102
- const observer = new IntersectionObserver((entries) => {
103
- entries.forEach((entry) => {
104
- // Trigger when the element enters the middle of the viewport
105
- if (entry.isIntersecting) {
106
- focusCode();
107
- }
108
- });
109
- }, {
110
- threshold,
111
- rootMargin: "-40% 0px -40% 0px", // Trigger when element enters the middle 20% of viewport
112
- });
113
- observer.observe(element);
114
- return () => {
115
- observer.disconnect();
116
- };
117
- // eslint-disable-next-line react-hooks/exhaustive-deps
118
241
  }, [
119
242
  filePath,
120
243
  lineRange,
121
244
  generatedCode,
122
245
  fileTree,
246
+ filesEdited,
123
247
  setActiveFilePath,
124
248
  setActiveFileName,
125
249
  setActiveLineNumber,
126
250
  setActiveLineRange,
127
251
  updateCode,
128
252
  setLanguage,
129
- filesEdited,
130
- threshold,
131
253
  ]);
254
+ // Register with the global coordinator
255
+ useEffect(() => {
256
+ const element = sectionRef.current;
257
+ if (!element || typeof window === "undefined")
258
+ return;
259
+ const coordinator = getCoordinator();
260
+ const cleanup = coordinator.register(sectionIdRef.current, element, focusCode);
261
+ return cleanup;
262
+ }, [focusCode]);
263
+ // Handle click to force focus
264
+ const handleClick = useCallback(() => {
265
+ if (typeof window !== "undefined") {
266
+ const coordinator = getCoordinator();
267
+ coordinator.forceFocus(sectionIdRef.current);
268
+ }
269
+ }, []);
132
270
  // Get the resolved file path for comparison
133
271
  // This ensures we compare against the actual file_path from the code structure
134
272
  const resolvedFilePathForComparison = useMemo(() => {
@@ -154,7 +292,7 @@ export default function CodeFocusSection({ filePath, lineRange, title, descripti
154
292
  }
155
293
  return filePath;
156
294
  }, [filePath, generatedCode, fileTree]);
157
- return (React.createElement("div", { ref: sectionRef, className: `relative transition-colors py-6 cursor-pointer`, onClick: focusCode },
295
+ return (React.createElement("div", { ref: sectionRef, className: `relative transition-colors py-6 cursor-pointer`, onClick: handleClick },
158
296
  React.createElement("div", { className: "pointer-events-none absolute inset-x-[-2rem] top-0 border-t border-border" }),
159
297
  React.createElement("div", { className: "pointer-events-none absolute inset-x-[-2rem] bottom-0 border-b border-border" }),
160
298
  (activeFilePath === resolvedFilePathForComparison ||
@@ -4,7 +4,7 @@ import { Markdown } from "./ui/markdown";
4
4
  export default function DefaultGuideView({ themeColor, }) {
5
5
  return (React.createElement("div", { className: "h-full flex flex-col" },
6
6
  React.createElement("div", { className: "flex-1 px-8 py-6" },
7
- React.createElement(Markdown, { className: "text-gray-100", themeColor: themeColor }, `# Guardian Guide
7
+ React.createElement(Markdown, { className: "text-zinc-900 dark:text-gray-100", themeColor: themeColor }, `# Guardian Guide
8
8
 
9
9
  Learn how to use Guardian to build and deploy your application with confidence.
10
10
 
@@ -20,7 +20,7 @@ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundU
20
20
  if (typeof CustomConsole === "string") {
21
21
  return (React.createElement("div", { className: "h-full flex flex-col" },
22
22
  React.createElement("div", { className: "flex-1 px-8 py-6" },
23
- React.createElement(Markdown, { className: "text-gray-100", themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }, CustomConsole))));
23
+ React.createElement(Markdown, { className: "text-zinc-900 dark:text-gray-100", themeColor: themeColor, browserUrl: browserUrl, gitUrl: gitUrl, sandboxId: sandboxId, apiKey: apiKey }, CustomConsole))));
24
24
  }
25
25
  const CustomConsoleComponent = CustomConsole;
26
26
  return (React.createElement(CustomConsoleComponent, { onReloadPreview: onReloadPreview !== null && onReloadPreview !== void 0 ? onReloadPreview : (() => { }), onStageChange: onStageChange !== null && onStageChange !== void 0 ? onStageChange : (() => { }), themeColor: themeColor }));
@@ -64,7 +64,7 @@ export default function ConsoleWithGuide({ CustomConsole, GuideView, playgroundU
64
64
  React.createElement("div", { className: "flex-1 min-h-0 overflow-y-auto" }, isConsoleView && renderCustomConsole()),
65
65
  React.createElement("div", { className: "relative" },
66
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"),
67
+ React.createElement("span", { className: "text-zinc-600 dark:text-gray-300" }, "Powered by"),
68
68
  React.createElement("span", { className: "hover:bg-accent rounded-sm p-1 mx-1" },
69
69
  React.createElement(Logo, { width: 100, href: "https://sampleapp.ai" })))))));
70
70
  }
@@ -9,7 +9,7 @@ import { GuardianStyleWrapper } from "./guardian-style-wrapper";
9
9
  *
10
10
  * When isFrame=true, reads framework from URL params client-side.
11
11
  */
12
- export default function GuardianPlayground({ nestedConfig, useCase, framework: serverFramework, isFrame = false, apiKey, env, chatUid, }) {
12
+ export default function GuardianPlayground({ nestedConfig, useCase, framework: serverFramework, isFrame = false, apiKey, env, chatUid, theme = "dark", }) {
13
13
  var _a, _b;
14
14
  // When in frame mode, allow URL params to override framework
15
15
  const frameParams = useFrameParams();
@@ -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, { themeColor: frameworkConfig.themeColor },
43
+ return (React.createElement(GuardianStyleWrapper, { themeColor: frameworkConfig.themeColor, theme: theme },
44
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
  }
@@ -5,9 +5,28 @@ import { TAILWIND_CSS } from "../../../lib/generated-css";
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, themeColor = "#3b82f6", }) => {
8
+ export const GuardianStyleWrapper = ({ children, themeColor = "#3b82f6", theme = "dark", }) => {
9
9
  const [stylesInjected, setStylesInjected] = useState(false);
10
+ const [resolvedTheme, setResolvedTheme] = useState(theme === "system" ? "dark" : theme);
10
11
  const rootRef = useRef(null);
12
+ // Handle system theme preference
13
+ useEffect(() => {
14
+ if (theme === "system") {
15
+ // Check if we're in browser environment
16
+ if (typeof window === "undefined")
17
+ return;
18
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
19
+ setResolvedTheme(mediaQuery.matches ? "dark" : "light");
20
+ const handler = (e) => {
21
+ setResolvedTheme(e.matches ? "dark" : "light");
22
+ };
23
+ mediaQuery.addEventListener("change", handler);
24
+ return () => mediaQuery.removeEventListener("change", handler);
25
+ }
26
+ else {
27
+ setResolvedTheme(theme);
28
+ }
29
+ }, [theme]);
11
30
  useEffect(() => {
12
31
  // Only run on client side
13
32
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -39,7 +58,7 @@ export const GuardianStyleWrapper = ({ children, themeColor = "#3b82f6", }) => {
39
58
  rootRef.current.style.setProperty("--colors-secondary", secondaryColor);
40
59
  }
41
60
  }, [themeColor]);
42
- return (React.createElement("div", { ref: rootRef, className: "guardian-sdk-root dark bg-background text-foreground", style: {
61
+ return (React.createElement("div", { ref: rootRef, className: `guardian-sdk-root bg-background text-foreground ${resolvedTheme === "dark" ? "dark" : ""}`, style: {
43
62
  width: "100%",
44
63
  height: "100%",
45
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"',
@@ -494,57 +494,57 @@ export default function Browser({ previewUrl, setPreviewUrl, containerEndpoint,
494
494
  } }));
495
495
  };
496
496
  return (React.createElement("div", { className: "flex h-full w-full flex-col" },
497
- !useVm && !isGuardian && (React.createElement("div", { className: "bg-white dark:bg-black border-b border-zinc-200 dark:border-zinc-800 flex-shrink-0" },
497
+ !useVm && !isGuardian && (React.createElement("div", { className: "bg-black border-b border-zinc-800 flex-shrink-0" },
498
498
  React.createElement("div", { className: "flex items-center gap-1.5 px-2 py-1.5 h-[2.38rem]" },
499
499
  React.createElement("div", { className: "flex gap-0.5" },
500
- React.createElement("button", { onClick: handleBack, disabled: historyIndex === 0, className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-900 rounded disabled:opacity-30 disabled:hover:bg-transparent transition-colors", title: "Back" },
501
- React.createElement(ChevronLeft, { size: 14, className: "text-zinc-600 dark:text-zinc-400" })),
502
- React.createElement("button", { onClick: handleForward, disabled: historyIndex === historyLength - 1, className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-900 rounded disabled:opacity-30 disabled:hover:bg-transparent transition-colors", title: "Forward" },
503
- React.createElement(ChevronRight, { size: 14, className: "text-zinc-600 dark:text-zinc-400" })),
504
- React.createElement("button", { onClick: handleRefresh, className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-900 rounded transition-colors", title: "Refresh" },
505
- React.createElement(RefreshCw, { size: 14, className: `text-zinc-600 dark:text-zinc-400 ${isLoading ? "animate-spin" : ""}` }))),
506
- React.createElement("div", { className: "flex-1 flex items-center gap-1.5 px-2 py-1 bg-zinc-50 dark:bg-zinc-950 rounded text-zinc-600 dark:text-zinc-400 focus-within:bg-white dark:focus-within:bg-zinc-900 transition-colors" },
507
- error ? (React.createElement(AlertTriangle, { size: 12, className: "text-red-500 flex-shrink-0" })) : (React.createElement(Lock, { size: 12, className: "text-zinc-400 dark:text-zinc-600 flex-shrink-0" })),
500
+ React.createElement("button", { onClick: handleBack, disabled: historyIndex === 0, className: "p-1 hover:bg-zinc-900 rounded disabled:opacity-30 disabled:hover:bg-transparent transition-colors", title: "Back" },
501
+ React.createElement(ChevronLeft, { size: 14, className: "text-zinc-400" })),
502
+ React.createElement("button", { onClick: handleForward, disabled: historyIndex === historyLength - 1, className: "p-1 hover:bg-zinc-900 rounded disabled:opacity-30 disabled:hover:bg-transparent transition-colors", title: "Forward" },
503
+ React.createElement(ChevronRight, { size: 14, className: "text-zinc-400" })),
504
+ React.createElement("button", { onClick: handleRefresh, className: "p-1 hover:bg-zinc-900 rounded transition-colors", title: "Refresh" },
505
+ React.createElement(RefreshCw, { size: 14, className: `text-zinc-400 ${isLoading ? "animate-spin" : ""}` }))),
506
+ React.createElement("div", { className: "flex-1 flex items-center gap-1.5 px-2 py-1 bg-zinc-950 rounded text-zinc-400 focus-within:bg-zinc-900 transition-colors" },
507
+ error ? (React.createElement(AlertTriangle, { size: 12, className: "text-red-500 flex-shrink-0" })) : (React.createElement(Lock, { size: 12, className: "text-zinc-600 flex-shrink-0" })),
508
508
  React.createElement("input", { type: "text",
509
509
  // value={urlInput}
510
- value: "/", onChange: (e) => setUrlInput(e.target.value), onKeyDown: handleUrlChange, className: "flex-1 text-xs border-none outline-none bg-transparent text-zinc-700 dark:text-zinc-300 placeholder:text-zinc-400 dark:placeholder:text-zinc-600", placeholder: "Enter URL" }),
511
- isGuardian && (React.createElement("button", { onClick: () => setIsBrowserMaximized(!isBrowserMaximized), className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-900 rounded transition-colors", title: isBrowserMaximized
510
+ value: "/", onChange: (e) => setUrlInput(e.target.value), onKeyDown: handleUrlChange, className: "flex-1 text-xs border-none outline-none bg-transparent text-zinc-300 placeholder:text-zinc-600", placeholder: "Enter URL" }),
511
+ isGuardian && (React.createElement("button", { onClick: () => setIsBrowserMaximized(!isBrowserMaximized), className: "p-1 hover:bg-zinc-900 rounded transition-colors", title: isBrowserMaximized
512
512
  ? "Exit fullscreen"
513
- : "Fullscreen preview" }, isBrowserMaximized ? (React.createElement(Minimize2, { size: 14, className: "text-zinc-600 dark:text-zinc-400" })) : (React.createElement(Maximize2, { size: 14, className: "text-zinc-600 dark:text-zinc-400" })))),
513
+ : "Fullscreen preview" }, isBrowserMaximized ? (React.createElement(Minimize2, { size: 14, className: "text-zinc-400" })) : (React.createElement(Maximize2, { size: 14, className: "text-zinc-400" })))),
514
514
  React.createElement("button", { onClick: () => {
515
515
  window.open(previewUrl, "_blank", "noopener,noreferrer");
516
- }, className: "ml-1 p-1 hover:bg-zinc-100 dark:hover:bg-zinc-900 rounded transition-colors", title: "Open in new tab", disabled: !urlInput || !!error, tabIndex: -1, type: "button" },
517
- React.createElement(ExternalLink, { size: 14, className: "text-zinc-600 dark:text-zinc-400" })),
516
+ }, className: "ml-1 p-1 hover:bg-zinc-900 rounded transition-colors", title: "Open in new tab", disabled: !urlInput || !!error, tabIndex: -1, type: "button" },
517
+ React.createElement(ExternalLink, { size: 14, className: "text-zinc-400" })),
518
518
  React.createElement(DropdownMenu, null,
519
519
  React.createElement(DropdownMenuTrigger, { asChild: true },
520
- React.createElement("button", { className: "ml-1 p-1 hover:bg-zinc-100 dark:hover:bg-zinc-900 rounded transition-colors", title: "More options", type: "button" },
521
- React.createElement(MoreVertical, { size: 14, className: "text-zinc-600 dark:text-zinc-400" }))),
522
- React.createElement(DropdownMenuContent, { align: "end", className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800" },
520
+ React.createElement("button", { className: "ml-1 p-1 hover:bg-zinc-900 rounded transition-colors", title: "More options", type: "button" },
521
+ React.createElement(MoreVertical, { size: 14, className: "text-zinc-400" }))),
522
+ React.createElement(DropdownMenuContent, { align: "end", className: "bg-zinc-900 border border-zinc-800" },
523
523
  React.createElement(DropdownMenuItem, { onClick: () => {
524
524
  // Restart functionality - can be implemented if needed
525
- }, className: "cursor-pointer text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800" }, "Restart App"))))))),
526
- error && (React.createElement("div", { className: "p-4 bg-red-50 border-b border-red-100 text-red-700 text-sm dark:bg-red-950 dark:border-red-800 dark:text-red-200" },
525
+ }, className: "cursor-pointer text-zinc-300 hover:bg-zinc-800" }, "Restart App"))))))),
526
+ error && (React.createElement("div", { className: "p-4 bg-red-950 border-b border-red-800 text-red-200 text-sm" },
527
527
  React.createElement("div", { className: "flex items-center gap-2" },
528
528
  React.createElement(AlertTriangle, { size: 16 }),
529
529
  React.createElement("span", null, error)))),
530
- React.createElement("div", { ref: containerRef, className: "flex-1 min-h-0 relative" }, sandboxError && sandboxError.includes("env is required") ? (React.createElement("div", { className: "h-full w-full flex items-center justify-center bg-zinc-50 dark:bg-zinc-900" },
530
+ React.createElement("div", { ref: containerRef, className: "flex-1 min-h-0 relative" }, sandboxError && sandboxError.includes("env is required") ? (React.createElement("div", { className: "h-full w-full flex items-center justify-center bg-zinc-900" },
531
531
  React.createElement("div", { className: "max-w-md mx-auto p-8 text-center" },
532
532
  React.createElement("div", { className: "mb-4 flex justify-center" },
533
- React.createElement(AlertTriangle, { size: 48, className: "text-amber-500 dark:text-amber-400" })),
534
- React.createElement("h2", { className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100 mb-2" }, "Environment Variables Required"),
535
- React.createElement("p", { className: "text-sm text-zinc-600 dark:text-zinc-400 mb-4" },
533
+ React.createElement(AlertTriangle, { size: 48, className: "text-amber-400" })),
534
+ React.createElement("h2", { className: "text-xl font-semibold text-zinc-100 mb-2" }, "Environment Variables Required"),
535
+ React.createElement("p", { className: "text-sm text-zinc-400 mb-4" },
536
536
  "This sandbox doesn't have a published URL and needs to be started dynamically. Please provide the",
537
537
  " ",
538
- React.createElement("code", { className: "px-1.5 py-0.5 bg-zinc-200 dark:bg-zinc-800 rounded text-xs font-mono" }, "env"),
538
+ React.createElement("code", { className: "px-1.5 py-0.5 bg-zinc-800 rounded text-xs font-mono" }, "env"),
539
539
  " ",
540
540
  "prop to the",
541
541
  " ",
542
- React.createElement("code", { className: "px-1.5 py-0.5 bg-zinc-200 dark:bg-zinc-800 rounded text-xs font-mono" }, "<Sandbox>"),
542
+ React.createElement("code", { className: "px-1.5 py-0.5 bg-zinc-800 rounded text-xs font-mono" }, "<Sandbox>"),
543
543
  " ",
544
544
  "component."),
545
- React.createElement("div", { className: "mt-6 p-4 bg-zinc-100 dark:bg-zinc-800 rounded-lg text-left" },
546
- React.createElement("p", { className: "text-xs font-mono text-zinc-700 dark:text-zinc-300 mb-2" }, "Example:"),
547
- React.createElement("pre", { className: "text-xs text-zinc-600 dark:text-zinc-400 overflow-x-auto" }, `<Sandbox
545
+ React.createElement("div", { className: "mt-6 p-4 bg-zinc-800 rounded-lg text-left" },
546
+ React.createElement("p", { className: "text-xs font-mono text-zinc-300 mb-2" }, "Example:"),
547
+ React.createElement("pre", { className: "text-xs text-zinc-400 overflow-x-auto" }, `<Sandbox
548
548
  apiKey="your-api-key"
549
549
  sandboxId="sandbox-id"
550
550
  env={{
@@ -553,8 +553,8 @@ export default function Browser({ previewUrl, setPreviewUrl, containerEndpoint,
553
553
  }}
554
554
  />`))))) : (React.createElement(React.Fragment, null,
555
555
  renderIframe(),
556
- isLoading && (React.createElement("div", { className: "absolute inset-0 bg-zinc-100 dark:bg-zinc-800 bg-opacity-50 flex items-center justify-center z-10" },
557
- React.createElement("div", { className: "text-sm text-zinc-600 bg-white dark:bg-black p-3 rounded-lg shadow-md" },
556
+ isLoading && (React.createElement("div", { className: "absolute inset-0 bg-zinc-800 bg-opacity-50 flex items-center justify-center z-10" },
557
+ React.createElement("div", { className: "text-sm text-zinc-400 bg-black p-3 rounded-lg shadow-md" },
558
558
  "Loading ",
559
559
  urlInput,
560
560
  "..."))),
@@ -16,10 +16,10 @@ export default function PreviewControlBar({ isMinimized, onToggle, onRefresh, th
16
16
  React.createElement(ChevronDown, { className: "w-4 h-4" })),
17
17
  React.createElement("span", { className: "text-sm font-medium", style: { color: effectiveThemeColor } }, "Preview")),
18
18
  React.createElement("div", { className: "flex items-center gap-2" },
19
- externalUrl && (React.createElement("a", { href: externalUrl, target: "_blank", rel: "noopener noreferrer", title: "Open in new tab", style: { color: effectiveThemeColor }, className: "p-1.5 rounded transition-colors hover:bg-zinc-800 dark:hover:bg-zinc-800" },
19
+ externalUrl && (React.createElement("a", { href: externalUrl, target: "_blank", rel: "noopener noreferrer", title: "Open in new tab", style: { color: effectiveThemeColor }, className: "p-1.5 rounded transition-colors hover:bg-zinc-800" },
20
20
  React.createElement(ExternalLink, { className: "w-4 h-4" }),
21
21
  React.createElement("span", { className: "sr-only" }, "Open external link"))),
22
- React.createElement("button", { onClick: onRefresh, className: "p-1.5 rounded transition-colors hover:bg-zinc-800 dark:hover:bg-zinc-800", title: "Refresh preview", style: { color: effectiveThemeColor } },
22
+ React.createElement("button", { onClick: onRefresh, className: "p-1.5 rounded transition-colors hover:bg-zinc-800", title: "Refresh preview", style: { color: effectiveThemeColor } },
23
23
  React.createElement(RefreshCw, { className: "w-4 h-4" })),
24
- React.createElement("button", { onClick: () => setIsBrowserMaximized(!isBrowserMaximized), className: "p-1.5 rounded transition-colors hover:bg-zinc-800 dark:hover:bg-zinc-800", title: isBrowserMaximized ? "Exit fullscreen" : "Fullscreen preview", style: { color: effectiveThemeColor } }, isBrowserMaximized ? (React.createElement(Minimize2, { className: "w-4 h-4" })) : (React.createElement(Maximize2, { className: "w-4 h-4" }))))));
24
+ React.createElement("button", { onClick: () => setIsBrowserMaximized(!isBrowserMaximized), className: "p-1.5 rounded transition-colors hover:bg-zinc-800", title: isBrowserMaximized ? "Exit fullscreen" : "Fullscreen preview", style: { color: effectiveThemeColor } }, isBrowserMaximized ? (React.createElement(Minimize2, { className: "w-4 h-4" })) : (React.createElement(Maximize2, { className: "w-4 h-4" }))))));
25
25
  }
@@ -217,7 +217,7 @@ export default function RightView({ reloadCounter, overlayStage, browserUrl, use
217
217
  React.createElement("div", { className: "h-full w-full flex flex-col" },
218
218
  React.createElement("div", { className: "flex-1 min-h-0 flex flex-col" },
219
219
  React.createElement(React.Fragment, null,
220
- 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" },
220
+ React.createElement("div", { className: "flex items-center justify-between border-b border-zinc-800 bg-zinc-900" },
221
221
  React.createElement(PillFileSelector, { themeColor: effectiveThemeColor }),
222
222
  React.createElement("div", { className: "px-2 flex-shrink-0" },
223
223
  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: handleDownloadAsZip },
@@ -207,27 +207,27 @@ export default function SimplifiedEditor({ themeColor }) {
207
207
  colorDecorators: true,
208
208
  })), { domReadOnly: isGenerating, readOnly: isGenerating }) })),
209
209
  React.createElement("style", null, `
210
- :global(.search-highlight-line) {
210
+ .search-highlight-line {
211
211
  background-color: ${highlightBackground} !important;
212
212
  }
213
- :global(.search-highlight-glyph) {
213
+ .search-highlight-glyph {
214
214
  background-color: ${glyphBackground};
215
215
  }
216
- :global(.muted-code-text) {
216
+ .muted-code-text {
217
217
  opacity: 0.5;
218
218
  filter: grayscale(0.4);
219
219
  }
220
220
  /* Remove blue focus outline from Monaco Editor */
221
- :global(.monaco-editor .inputarea.ime-input) {
221
+ .monaco-editor .inputarea.ime-input {
222
222
  outline: none !important;
223
223
  }
224
- :global(.monaco-editor) {
224
+ .monaco-editor {
225
225
  outline: none !important;
226
226
  }
227
- :global(.monaco-editor .monaco-editor-background) {
227
+ .monaco-editor .monaco-editor-background {
228
228
  outline: none !important;
229
229
  }
230
- :global(.monaco-editor .view-overlays) {
230
+ .monaco-editor .view-overlays {
231
231
  outline: none !important;
232
232
  }
233
233
  `)));
@@ -144,7 +144,6 @@ export function filePathToCodeLanguage(filePath) {
144
144
  return CodeLanguage.ELIXIR;
145
145
  case "lua":
146
146
  return CodeLanguage.LUA;
147
- case "m":
148
147
  case "mm":
149
148
  return CodeLanguage.OBJECTIVEC;
150
149
  case "md":
@@ -59,7 +59,7 @@ export const DownloadAndOpenButtons = ({ downloadUrl, themeColor, gitUrl, browse
59
59
  React.createElement("button", { type: "button", className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md !text-white transition-all hover:opacity-90 active:scale-[0.98]", style: { backgroundColor: themeColor }, onClick: handleDownload },
60
60
  React.createElement(DownloadIcon, { className: "w-3.5 h-3.5 !text-white" }),
61
61
  "Download example"),
62
- React.createElement("button", { type: "button", className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border transition-all hover:opacity-90 active:scale-[0.98] bg-zinc-900 dark:bg-zinc-800 border-zinc-700 dark:border-zinc-700 !text-white", onClick: handleOpenInVSCode },
62
+ React.createElement("button", { type: "button", className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border transition-all hover:opacity-90 active:scale-[0.98] bg-zinc-100 dark:bg-zinc-800 border-zinc-300 dark:border-zinc-700 text-zinc-900 dark:text-white", onClick: handleOpenInVSCode },
63
63
  React.createElement(VSCodeLogo, { className: "w-3.5 h-3.5" }),
64
64
  "Integrate in VS Code")),
65
65
  React.createElement(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen },