@sampleapp.ai/sdk 1.0.29 → 1.0.31

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 (145) hide show
  1. package/dist/components/guardian/app-layout-no-sidebar.js +8 -0
  2. package/dist/components/guardian/ask-ai-view.js +249 -0
  3. package/dist/components/guardian/code-focus-section.d.ts +41 -0
  4. package/dist/components/guardian/code-focus-section.js +174 -0
  5. package/dist/components/guardian/context/guardian-context.js +94 -0
  6. package/dist/components/guardian/context/vm-context.js +28 -0
  7. package/dist/components/guardian/default-guide-view.js +34 -0
  8. package/dist/components/guardian/demo/guardian-demo.js +35 -0
  9. package/dist/components/guardian/demo/left-view/toggle.js +28 -0
  10. package/dist/components/guardian/demo/left-view.js +49 -0
  11. package/dist/components/guardian/guardian-component.js +79 -0
  12. package/dist/components/guardian/guardian-demo.js +35 -0
  13. package/dist/components/guardian/guardian-home.d.ts +4 -0
  14. package/dist/components/guardian/guardian-home.js +61 -0
  15. package/dist/components/guardian/guardian-playground.js +45 -0
  16. package/dist/components/guardian/guardian-style-wrapper.js +29 -0
  17. package/dist/components/guardian/guardian-upload-spec.d.ts +14 -0
  18. package/dist/components/guardian/guardian-upload-spec.js +160 -0
  19. package/dist/components/guardian/header/glassmorphic-combobox.d.ts +15 -0
  20. package/dist/components/guardian/header/glassmorphic-combobox.js +30 -0
  21. package/dist/components/guardian/header.js +61 -0
  22. package/dist/components/guardian/hooks/use-frame-messages.js +65 -0
  23. package/dist/components/guardian/hooks/use-frame-params.js +44 -0
  24. package/dist/components/guardian/hooks/use-sandbox-url-loader.js +101 -0
  25. package/dist/components/guardian/ide/browser.js +538 -0
  26. package/dist/components/guardian/index.js +8 -0
  27. package/dist/components/guardian/layout/app-layout-no-sidebar.js +8 -0
  28. package/dist/components/guardian/layout/header/glassmorphic-combobox.js +48 -0
  29. package/dist/components/guardian/layout/header.js +63 -0
  30. package/dist/components/guardian/right-view/code-view.js +56 -0
  31. package/dist/components/guardian/right-view/pill-file-selector.js +233 -0
  32. package/dist/components/guardian/right-view/preview-control-bar.js +25 -0
  33. package/dist/components/guardian/right-view/right-panel-view.js +38 -0
  34. package/dist/components/guardian/right-view/right-top-down-view.js +289 -0
  35. package/dist/components/guardian/right-view/right-view.js +28 -0
  36. package/dist/components/guardian/right-view/simplified-editor.js +234 -0
  37. package/dist/components/guardian/types/ide-types.js +162 -0
  38. package/dist/components/guardian/types.js +3 -0
  39. package/dist/components/guardian/ui/ai-loader.js +48 -0
  40. package/dist/components/guardian/ui/badge.js +24 -0
  41. package/dist/components/guardian/ui/button.js +45 -0
  42. package/dist/components/guardian/ui/command.js +63 -0
  43. package/dist/components/guardian/ui/console-with-app.js +17 -0
  44. package/dist/components/guardian/ui/dialog.js +57 -0
  45. package/dist/components/guardian/ui/dropdown-menu.js +82 -0
  46. package/dist/components/guardian/ui/markdown.js +57 -0
  47. package/dist/components/guardian/ui/popover.js +25 -0
  48. package/dist/components/guardian/ui/tooltip.js +25 -0
  49. package/dist/components/guardian/utils.js +88 -0
  50. package/dist/components/guardian/zip-to-codebase.js +246 -0
  51. package/dist/components/guardian/zip-to-filetree.js +284 -0
  52. package/dist/components/icons.js +22 -0
  53. package/dist/components/sandbox/Sandbox.js +88 -0
  54. package/dist/components/sandbox/SandboxHome.js +141 -0
  55. package/dist/components/sandbox/api.js +84 -0
  56. package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +11 -0
  57. package/dist/components/sandbox/guardian/ask-ai-view.js +249 -0
  58. package/dist/components/sandbox/guardian/code-focus-section.js +174 -0
  59. package/dist/components/sandbox/guardian/context/guardian-context.js +94 -0
  60. package/dist/components/sandbox/guardian/context/vm-context.js +28 -0
  61. package/dist/components/sandbox/guardian/default-guide-view.js +34 -0
  62. package/dist/components/sandbox/guardian/demo/guardian-demo.js +35 -0
  63. package/dist/components/sandbox/guardian/demo/left-view/toggle.js +28 -0
  64. package/dist/components/sandbox/guardian/demo/left-view.js +70 -0
  65. package/dist/components/sandbox/guardian/guardian-component.js +99 -0
  66. package/dist/components/sandbox/guardian/guardian-demo.js +35 -0
  67. package/dist/components/sandbox/guardian/guardian-home.d.ts +4 -0
  68. package/dist/components/sandbox/guardian/guardian-home.js +61 -0
  69. package/dist/components/sandbox/guardian/guardian-playground.js +45 -0
  70. package/dist/components/sandbox/guardian/guardian-style-wrapper.js +47 -0
  71. package/dist/components/sandbox/guardian/guardian-upload-spec.d.ts +14 -0
  72. package/dist/components/sandbox/guardian/guardian-upload-spec.js +160 -0
  73. package/dist/components/sandbox/guardian/header/glassmorphic-combobox.js +30 -0
  74. package/dist/components/sandbox/guardian/header.js +61 -0
  75. package/dist/components/sandbox/guardian/hooks/use-frame-messages.js +65 -0
  76. package/dist/components/sandbox/guardian/hooks/use-frame-params.js +44 -0
  77. package/dist/components/sandbox/guardian/hooks/use-sandbox-url-loader.js +125 -0
  78. package/dist/components/sandbox/guardian/ide/browser.js +538 -0
  79. package/dist/components/sandbox/guardian/index.js +8 -0
  80. package/dist/components/sandbox/guardian/right-view/code-view.js +60 -0
  81. package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +246 -0
  82. package/dist/components/sandbox/guardian/right-view/preview-control-bar.js +25 -0
  83. package/dist/components/sandbox/guardian/right-view/right-panel-view.js +51 -0
  84. package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +281 -0
  85. package/dist/components/sandbox/guardian/right-view/right-view.js +28 -0
  86. package/dist/components/sandbox/guardian/right-view/simplified-editor.js +234 -0
  87. package/dist/components/sandbox/guardian/types/ide-types.js +162 -0
  88. package/dist/components/sandbox/guardian/types.js +3 -0
  89. package/dist/components/sandbox/guardian/ui/ai-loader.js +91 -0
  90. package/dist/components/sandbox/guardian/ui/badge.js +24 -0
  91. package/dist/components/sandbox/guardian/ui/button.js +45 -0
  92. package/dist/components/sandbox/guardian/ui/command.js +63 -0
  93. package/dist/components/sandbox/guardian/ui/console-with-app.js +17 -0
  94. package/dist/components/sandbox/guardian/ui/dialog.js +57 -0
  95. package/dist/components/sandbox/guardian/ui/download-and-open-buttons.js +117 -0
  96. package/dist/components/sandbox/guardian/ui/dropdown-menu.js +82 -0
  97. package/dist/components/sandbox/guardian/ui/markdown/accordion-group/accordion.js +62 -0
  98. package/dist/components/sandbox/guardian/ui/markdown/accordion-group.js +23 -0
  99. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-check.js +4 -0
  100. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-error.js +4 -0
  101. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-info.js +4 -0
  102. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-note.js +4 -0
  103. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-tip.js +4 -0
  104. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-warning.js +4 -0
  105. package/dist/components/sandbox/guardian/ui/markdown/callout/shared/callout.js +9 -0
  106. package/dist/components/sandbox/guardian/ui/markdown/callout/shared/types.js +1 -0
  107. package/dist/components/sandbox/guardian/ui/markdown/card-group/card.js +18 -0
  108. package/dist/components/sandbox/guardian/ui/markdown/card-group.js +25 -0
  109. package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +54 -0
  110. package/dist/components/sandbox/guardian/ui/markdown/code-group.js +101 -0
  111. package/dist/components/sandbox/guardian/ui/markdown/icon.js +31 -0
  112. package/dist/components/sandbox/guardian/ui/markdown.js +791 -0
  113. package/dist/components/sandbox/guardian/ui/popover.js +25 -0
  114. package/dist/components/sandbox/guardian/ui/tooltip.js +25 -0
  115. package/dist/components/sandbox/guardian/utils.js +89 -0
  116. package/dist/components/sandbox/guardian/zip-to-codebase.js +259 -0
  117. package/dist/components/sandbox/guardian/zip-to-filetree.js +284 -0
  118. package/dist/components/sandbox/index.js +4 -0
  119. package/dist/components/sandbox/sandbox-control-bar.js +91 -0
  120. package/dist/components/sandbox/sandbox-header.js +52 -0
  121. package/dist/components/sandbox/sandbox-home/SandboxCard.js +65 -0
  122. package/dist/components/sandbox/sandbox-home/SandboxHome.js +115 -0
  123. package/dist/components/sandbox/sandbox-home/SearchBar.js +12 -0
  124. package/dist/components/sandbox/sandbox-home/index.js +3 -0
  125. package/dist/components/sandbox/sandbox-left-panel.js +248 -0
  126. package/dist/components/sandbox/sandbox-loading.js +48 -0
  127. package/dist/components/sandbox/sandbox-right-panel.js +247 -0
  128. package/dist/components/sandbox/types.js +1 -0
  129. package/dist/components/sandbox.js +32 -0
  130. package/dist/components/tailwind-example.js +46 -0
  131. package/dist/components/ui/skeleton.js +18 -0
  132. package/dist/index.d.ts +32 -103
  133. package/dist/index.es.js +90529 -423
  134. package/dist/index.js +13 -2
  135. package/dist/index.standalone.js +61 -53
  136. package/dist/index.standalone.umd.js +17 -24
  137. package/dist/lib/api-client.example.js +60 -0
  138. package/dist/lib/api-client.js +140 -0
  139. package/dist/lib/generated-css.js +4 -0
  140. package/dist/lib/inject-styles.js +42 -0
  141. package/dist/lib/shadow-dom-wrapper.js +42 -0
  142. package/dist/lib/utils.js +5 -0
  143. package/dist/sdk.css +1 -1
  144. package/dist/tailwind.css +1 -0
  145. package/package.json +41 -9
@@ -0,0 +1,284 @@
1
+ import JSZip from "jszip";
2
+ import { CodeLanguage, filePathToCodeLanguage, } from "./types/ide-types";
3
+ /**
4
+ * Converts a zip file to a FileTreeNode structure
5
+ * @param zipData - The zip file as Blob, File, ArrayBuffer, or Uint8Array
6
+ * @returns Promise<FileTreeNode> - The root node of the file tree
7
+ */
8
+ export async function zipToFileTree(zipData) {
9
+ try {
10
+ const zip = await JSZip.loadAsync(zipData);
11
+ // Create root node
12
+ const root = {
13
+ name: "root",
14
+ children: {},
15
+ type: "folder",
16
+ metadata: {
17
+ response_type: "CodeOutput",
18
+ file_path: "",
19
+ code_file_name: "",
20
+ code_language: CodeLanguage.TEXT,
21
+ dependencies_to_install: [],
22
+ framework: null,
23
+ full_code: "",
24
+ },
25
+ };
26
+ // Process all files in the zip
27
+ const filePromises = [];
28
+ for (const relativePath in zip.files) {
29
+ const file = zip.files[relativePath];
30
+ // Skip directories (they're handled by the path structure)
31
+ if (file.dir) {
32
+ continue;
33
+ }
34
+ // Get file content as text
35
+ const contentPromise = file
36
+ .async("string")
37
+ .then((content) => {
38
+ // Build the tree structure from the path
39
+ const pathParts = relativePath.split("/").filter(Boolean);
40
+ let currentNode = root;
41
+ // Navigate/create folder structure
42
+ for (let i = 0; i < pathParts.length - 1; i++) {
43
+ const part = pathParts[i];
44
+ if (!currentNode.children[part]) {
45
+ currentNode.children[part] = {
46
+ name: part,
47
+ children: {},
48
+ type: "folder",
49
+ metadata: {
50
+ response_type: "CodeOutput",
51
+ file_path: pathParts.slice(0, i + 1).join("/"),
52
+ code_file_name: part,
53
+ code_language: CodeLanguage.TEXT,
54
+ dependencies_to_install: [],
55
+ framework: null,
56
+ full_code: "",
57
+ },
58
+ };
59
+ }
60
+ currentNode = currentNode.children[part];
61
+ }
62
+ // Add the file
63
+ const fileName = pathParts[pathParts.length - 1];
64
+ const filePath = relativePath;
65
+ const codeLanguage = filePathToCodeLanguage(filePath);
66
+ currentNode.children[fileName] = {
67
+ name: fileName,
68
+ children: {},
69
+ type: "file",
70
+ metadata: {
71
+ response_type: "CodeOutput",
72
+ file_path: filePath,
73
+ code_file_name: fileName,
74
+ code_language: codeLanguage,
75
+ dependencies_to_install: [],
76
+ framework: null,
77
+ full_code: content,
78
+ },
79
+ };
80
+ })
81
+ .catch((error) => {
82
+ // If file can't be read as text, skip it or handle binary files
83
+ console.warn(`Could not read file ${relativePath} as text:`, error);
84
+ });
85
+ filePromises.push(contentPromise);
86
+ }
87
+ // Wait for all files to be processed
88
+ await Promise.all(filePromises);
89
+ return root;
90
+ }
91
+ catch (error) {
92
+ console.error("Error parsing zip file:", error);
93
+ throw new Error(`Failed to parse zip file: ${error instanceof Error ? error.message : String(error)}`);
94
+ }
95
+ }
96
+ /**
97
+ * Reads a zip file from a path and converts it to a FileTreeNode structure
98
+ * @param filePath - The path to the zip file (e.g., "/public/code/guardian/launchdarkly-guardian/code-1.zip" or "/code/guardian/launchdarkly-guardian/code-1.zip")
99
+ * @returns Promise<FileTreeNode> - The root node of the file tree
100
+ */
101
+ export async function zipPathToFileTree(filePath) {
102
+ try {
103
+ // Normalize the path - remove /public prefix if present (Next.js serves public folder from root)
104
+ let normalizedPath = filePath;
105
+ if (normalizedPath.startsWith("/public/")) {
106
+ normalizedPath = normalizedPath.slice("/public".length);
107
+ }
108
+ // Ensure path starts with /
109
+ if (!normalizedPath.startsWith("/")) {
110
+ normalizedPath = "/" + normalizedPath;
111
+ }
112
+ // Build the URL - handle both client-side (relative) and server-side (absolute) cases
113
+ let url;
114
+ if (typeof window !== "undefined") {
115
+ // Client-side: use relative URL
116
+ url = normalizedPath;
117
+ }
118
+ else {
119
+ // Server-side: need absolute URL
120
+ // Try to get base URL from environment or use a default
121
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ||
122
+ process.env.VERCEL_URL ||
123
+ "http://localhost:3000";
124
+ url = `${baseUrl}${normalizedPath}`;
125
+ }
126
+ // Fetch the file from the public folder
127
+ const response = await fetch(url);
128
+ if (!response.ok) {
129
+ throw new Error(`Failed to fetch zip file from ${url}: ${response.status} ${response.statusText}`);
130
+ }
131
+ // Get the file as a blob
132
+ const blob = await response.blob();
133
+ // Convert to FileTreeNode using zipToFileTree
134
+ return await zipToFileTree(blob);
135
+ }
136
+ catch (error) {
137
+ console.error(`Error reading zip file from path ${filePath}:`, error);
138
+ throw new Error(`Failed to read zip file from path: ${error instanceof Error ? error.message : String(error)}`);
139
+ }
140
+ }
141
+ /**
142
+ * Reads a zip file from a path and converts it to a FileTreeNode structure with binary file support
143
+ * Binary files will have their content as base64 encoded strings
144
+ * @param filePath - The path to the zip file (e.g., "/public/code/guardian/launchdarkly-guardian/code-1.zip" or "/code/guardian/launchdarkly-guardian/code-1.zip")
145
+ * @returns Promise<FileTreeNode> - The root node of the file tree
146
+ */
147
+ export async function zipPathToFileTreeWithBinary(filePath) {
148
+ try {
149
+ // Normalize the path - remove /public prefix if present (Next.js serves public folder from root)
150
+ let normalizedPath = filePath;
151
+ if (normalizedPath.startsWith("/public/")) {
152
+ normalizedPath = normalizedPath.slice("/public".length);
153
+ }
154
+ // Ensure path starts with /
155
+ if (!normalizedPath.startsWith("/")) {
156
+ normalizedPath = "/" + normalizedPath;
157
+ }
158
+ // Build the URL - handle both client-side (relative) and server-side (absolute) cases
159
+ let url;
160
+ if (typeof window !== "undefined") {
161
+ // Client-side: use relative URL
162
+ url = normalizedPath;
163
+ }
164
+ else {
165
+ // Server-side: need absolute URL
166
+ // Try to get base URL from environment or use a default
167
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ||
168
+ process.env.VERCEL_URL ||
169
+ "http://localhost:3000";
170
+ url = `${baseUrl}${normalizedPath}`;
171
+ }
172
+ // Fetch the file from the public folder
173
+ const response = await fetch(url);
174
+ if (!response.ok) {
175
+ throw new Error(`Failed to fetch zip file from ${url}: ${response.status} ${response.statusText}`);
176
+ }
177
+ // Get the file as a blob
178
+ const blob = await response.blob();
179
+ // Convert to FileTreeNode using zipToFileTreeWithBinary
180
+ return await zipToFileTreeWithBinary(blob);
181
+ }
182
+ catch (error) {
183
+ console.error(`Error reading zip file from path ${filePath}:`, error);
184
+ throw new Error(`Failed to read zip file from path: ${error instanceof Error ? error.message : String(error)}`);
185
+ }
186
+ }
187
+ /**
188
+ * Converts a zip file to a FileTreeNode structure with binary file support
189
+ * Binary files will have their content as base64 encoded strings
190
+ * @param zipData - The zip file as Blob, File, ArrayBuffer, or Uint8Array
191
+ * @returns Promise<FileTreeNode> - The root node of the file tree
192
+ */
193
+ export async function zipToFileTreeWithBinary(zipData) {
194
+ try {
195
+ const zip = await JSZip.loadAsync(zipData);
196
+ // Create root node
197
+ const root = {
198
+ name: "root",
199
+ children: {},
200
+ type: "folder",
201
+ metadata: {
202
+ response_type: "CodeOutput",
203
+ file_path: "",
204
+ code_file_name: "",
205
+ code_language: CodeLanguage.TEXT,
206
+ dependencies_to_install: [],
207
+ framework: null,
208
+ full_code: "",
209
+ },
210
+ };
211
+ // Process all files in the zip
212
+ const filePromises = [];
213
+ for (const relativePath in zip.files) {
214
+ const file = zip.files[relativePath];
215
+ // Skip directories (they're handled by the path structure)
216
+ if (file.dir) {
217
+ continue;
218
+ }
219
+ // Try to read as text first, fallback to base64 for binary files
220
+ const contentPromise = (async () => {
221
+ let content;
222
+ try {
223
+ // Try reading as text
224
+ content = await file.async("string");
225
+ }
226
+ catch (_a) {
227
+ // If that fails, read as base64
228
+ const binary = await file.async("base64");
229
+ content = binary;
230
+ }
231
+ // Build the tree structure from the path
232
+ const pathParts = relativePath.split("/").filter(Boolean);
233
+ let currentNode = root;
234
+ // Navigate/create folder structure
235
+ for (let i = 0; i < pathParts.length - 1; i++) {
236
+ const part = pathParts[i];
237
+ if (!currentNode.children[part]) {
238
+ currentNode.children[part] = {
239
+ name: part,
240
+ children: {},
241
+ type: "folder",
242
+ metadata: {
243
+ response_type: "CodeOutput",
244
+ file_path: pathParts.slice(0, i + 1).join("/"),
245
+ code_file_name: part,
246
+ code_language: CodeLanguage.TEXT,
247
+ dependencies_to_install: [],
248
+ framework: null,
249
+ full_code: "",
250
+ },
251
+ };
252
+ }
253
+ currentNode = currentNode.children[part];
254
+ }
255
+ // Add the file
256
+ const fileName = pathParts[pathParts.length - 1];
257
+ const filePath = relativePath;
258
+ const codeLanguage = filePathToCodeLanguage(filePath);
259
+ currentNode.children[fileName] = {
260
+ name: fileName,
261
+ children: {},
262
+ type: "file",
263
+ metadata: {
264
+ response_type: "CodeOutput",
265
+ file_path: filePath,
266
+ code_file_name: fileName,
267
+ code_language: codeLanguage,
268
+ dependencies_to_install: [],
269
+ framework: null,
270
+ full_code: content,
271
+ },
272
+ };
273
+ })();
274
+ filePromises.push(contentPromise);
275
+ }
276
+ // Wait for all files to be processed
277
+ await Promise.all(filePromises);
278
+ return root;
279
+ }
280
+ catch (error) {
281
+ console.error("Error parsing zip file:", error);
282
+ throw new Error(`Failed to parse zip file: ${error instanceof Error ? error.message : String(error)}`);
283
+ }
284
+ }
@@ -0,0 +1,4 @@
1
+ export { default as Sandbox } from "./Sandbox";
2
+ export { SandboxHome, SandboxCard } from "./sandbox-home";
3
+ // Re-export guardian components for advanced usage
4
+ export { GuardianPlayground, GuardianComponent, GuardianProvider, VmProvider, buildGuardianConfig, createSandboxUrlConfigs, } from "./guardian";
@@ -0,0 +1,91 @@
1
+ import React from "react";
2
+ import { ExternalLinkIcon, RefreshCwIcon, LoaderIcon } from "../icons";
3
+ /**
4
+ * Control bar for the sandbox with refresh and external link buttons.
5
+ */
6
+ export function SandboxControlBar({ src, themeColor, onRefresh, onOpenExternal, isLoading, }) {
7
+ return (React.createElement("div", { style: {
8
+ display: "flex",
9
+ alignItems: "center",
10
+ justifyContent: "space-between",
11
+ padding: "8px 12px",
12
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
13
+ backgroundColor: "rgba(0, 0, 0, 0.3)",
14
+ backdropFilter: "blur(8px)",
15
+ } },
16
+ React.createElement("div", { style: {
17
+ display: "flex",
18
+ alignItems: "center",
19
+ gap: "8px",
20
+ flex: 1,
21
+ minWidth: 0,
22
+ } },
23
+ React.createElement("div", { style: {
24
+ width: "8px",
25
+ height: "8px",
26
+ borderRadius: "50%",
27
+ backgroundColor: isLoading ? "#f59e0b" : "#22c55e",
28
+ flexShrink: 0,
29
+ } }),
30
+ React.createElement("span", { style: {
31
+ fontSize: "12px",
32
+ color: "rgba(255, 255, 255, 0.6)",
33
+ overflow: "hidden",
34
+ textOverflow: "ellipsis",
35
+ whiteSpace: "nowrap",
36
+ fontFamily: "monospace",
37
+ } }, src)),
38
+ React.createElement("div", { style: {
39
+ display: "flex",
40
+ alignItems: "center",
41
+ gap: "4px",
42
+ } },
43
+ React.createElement("button", { onClick: onRefresh, disabled: isLoading, style: {
44
+ display: "flex",
45
+ alignItems: "center",
46
+ justifyContent: "center",
47
+ width: "28px",
48
+ height: "28px",
49
+ borderRadius: "6px",
50
+ border: "none",
51
+ backgroundColor: "transparent",
52
+ color: "rgba(255, 255, 255, 0.7)",
53
+ cursor: isLoading ? "not-allowed" : "pointer",
54
+ opacity: isLoading ? 0.5 : 1,
55
+ transition: "all 0.15s ease",
56
+ }, onMouseEnter: (e) => {
57
+ if (!isLoading) {
58
+ e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.1)";
59
+ e.currentTarget.style.color = themeColor;
60
+ }
61
+ }, onMouseLeave: (e) => {
62
+ e.currentTarget.style.backgroundColor = "transparent";
63
+ e.currentTarget.style.color = "rgba(255, 255, 255, 0.7)";
64
+ }, title: "Refresh" }, isLoading ? (React.createElement(LoaderIcon, { size: 14, style: { animation: "spin 1s linear infinite" } })) : (React.createElement(RefreshCwIcon, { size: 14 }))),
65
+ React.createElement("button", { onClick: onOpenExternal, style: {
66
+ display: "flex",
67
+ alignItems: "center",
68
+ justifyContent: "center",
69
+ width: "28px",
70
+ height: "28px",
71
+ borderRadius: "6px",
72
+ border: "none",
73
+ backgroundColor: "transparent",
74
+ color: "rgba(255, 255, 255, 0.7)",
75
+ cursor: "pointer",
76
+ transition: "all 0.15s ease",
77
+ }, onMouseEnter: (e) => {
78
+ e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.1)";
79
+ e.currentTarget.style.color = themeColor;
80
+ }, onMouseLeave: (e) => {
81
+ e.currentTarget.style.backgroundColor = "transparent";
82
+ e.currentTarget.style.color = "rgba(255, 255, 255, 0.7)";
83
+ }, title: "Open in new tab" },
84
+ React.createElement(ExternalLinkIcon, { size: 14 }))),
85
+ React.createElement("style", null, `
86
+ @keyframes spin {
87
+ from { transform: rotate(0deg); }
88
+ to { transform: rotate(360deg); }
89
+ }
90
+ `)));
91
+ }
@@ -0,0 +1,52 @@
1
+ import React from "react";
2
+ /**
3
+ * Header component for the Sandbox
4
+ */
5
+ export function SandboxHeader({ title, description, logo, themeColor, }) {
6
+ return (React.createElement("header", { style: {
7
+ display: "flex",
8
+ height: "64px",
9
+ alignItems: "center",
10
+ justifyContent: "space-between",
11
+ padding: "0 24px",
12
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
13
+ backgroundColor: "rgba(0, 0, 0, 0.3)",
14
+ backdropFilter: "blur(8px)",
15
+ flexShrink: 0,
16
+ } },
17
+ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: "12px" } }, logo),
18
+ title && (React.createElement("div", { style: { textAlign: "center" } },
19
+ React.createElement("h1", { style: {
20
+ fontSize: "14px",
21
+ fontWeight: 600,
22
+ color: "rgba(255, 255, 255, 0.9)",
23
+ margin: 0,
24
+ } }, title),
25
+ description && (React.createElement("p", { style: {
26
+ fontSize: "12px",
27
+ color: "rgba(255, 255, 255, 0.5)",
28
+ margin: 0,
29
+ marginTop: "2px",
30
+ } }, description)))),
31
+ React.createElement("div", { style: {
32
+ display: "flex",
33
+ alignItems: "center",
34
+ gap: "8px",
35
+ padding: "6px 12px",
36
+ borderRadius: "9999px",
37
+ border: `1px solid ${themeColor}30`,
38
+ backgroundColor: `${themeColor}10`,
39
+ } },
40
+ React.createElement("span", { style: {
41
+ width: "8px",
42
+ height: "8px",
43
+ borderRadius: "50%",
44
+ backgroundColor: themeColor,
45
+ boxShadow: `0 0 0 3px ${themeColor}25`,
46
+ } }),
47
+ React.createElement("span", { style: {
48
+ fontSize: "12px",
49
+ fontWeight: 500,
50
+ color: themeColor,
51
+ } }, "Sandbox"))));
52
+ }
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import React from "react";
3
+ // Generate a deterministic gradient based on the title
4
+ function getGradientColors(title) {
5
+ // Defensive check - ensure we have a valid string
6
+ if (title === undefined || title === null || typeof title !== "string") {
7
+ return "from-slate-700 to-slate-900";
8
+ }
9
+ const safeTitle = String(title); // Convert to string as final safety
10
+ if (safeTitle.length === 0) {
11
+ return "from-slate-700 to-slate-900";
12
+ }
13
+ try {
14
+ const hash = safeTitle
15
+ .split("")
16
+ .reduce((acc, char) => char.charCodeAt(0) + acc, 0);
17
+ const gradients = [
18
+ "from-slate-700 to-slate-900",
19
+ "from-zinc-700 to-zinc-900",
20
+ "from-gray-700 to-gray-900",
21
+ "from-neutral-700 to-neutral-900",
22
+ "from-stone-700 to-stone-900",
23
+ "from-slate-600 to-slate-800",
24
+ "from-zinc-600 to-zinc-800",
25
+ "from-gray-600 to-gray-800",
26
+ ];
27
+ return gradients[Math.abs(hash) % gradients.length];
28
+ }
29
+ catch (e) {
30
+ return "from-slate-700 to-slate-900";
31
+ }
32
+ }
33
+ /**
34
+ * SandboxCard component - displays an individual template card
35
+ */
36
+ export const SandboxCard = ({ title, description, sandboxId, basePath = "/sandbox", imageUrl, }) => {
37
+ // Ensure title is always a string
38
+ const safeTitle = title || "";
39
+ const safeDescription = description || "";
40
+ const gradient = getGradientColors(safeTitle);
41
+ const handleClick = () => {
42
+ if (sandboxId) {
43
+ // Ensure basePath starts with / and doesn't end with /
44
+ const normalizedBasePath = basePath.startsWith("/")
45
+ ? basePath
46
+ : `/${basePath}`;
47
+ const cleanBasePath = normalizedBasePath.endsWith("/")
48
+ ? normalizedBasePath.slice(0, -1)
49
+ : normalizedBasePath;
50
+ window.location.href = `${cleanBasePath}/${sandboxId}`;
51
+ }
52
+ };
53
+ return (React.createElement("div", { onClick: handleClick, className: "group relative block min-w-[240px] w-full h-[200px] border border-[#333] bg-[#0a0a0a] rounded-lg overflow-hidden transition-all duration-100 ease-out hover:border-[#444] focus-visible:outline-none cursor-pointer" },
54
+ React.createElement("div", { className: "flex flex-col gap-1 px-6 py-5" },
55
+ React.createElement("div", { className: "flex items-center max-w-max pr-2.5" },
56
+ React.createElement("h3", { className: "text-[16px] font-medium m-0 w-max whitespace-nowrap overflow-hidden text-ellipsis flex-1 mr-0 text-white" }, safeTitle),
57
+ React.createElement("svg", { className: "rotate-[-45deg] translate-x-[-2px] opacity-0 transition-all duration-100 ease-out origin-center group-hover:opacity-100 group-hover:translate-x-[4px] ml-1", height: "16", strokeLinejoin: "round", style: { width: 12, height: 12, color: "currentColor" }, viewBox: "0 0 16 16", width: "16" },
58
+ React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.53033 2.21968L9 1.68935L7.93934 2.75001L8.46967 3.28034L12.4393 7.25001H1.75H1V8.75001H1.75H12.4393L8.46967 12.7197L7.93934 13.25L9 14.3107L9.53033 13.7803L14.6036 8.70711C14.9941 8.31659 14.9941 7.68342 14.6036 7.2929L9.53033 2.21968Z", fill: "currentColor" }))),
59
+ React.createElement("p", { className: "text-[14px] text-gray-400 m-0 line-clamp-2" }, safeDescription)),
60
+ React.createElement("div", { className: "absolute top-[110px] -right-10 w-[300px] h-[100px] rounded-lg border border-[#333] overflow-hidden shadow-lg transition-transform duration-100 ease-out rotate-[-5deg] group-hover:rotate-[-3deg] group-hover:-translate-y-1 group-hover:-translate-x-0.5" }, imageUrl ? (React.createElement("img", { src: imageUrl, alt: safeTitle, className: "w-full h-full object-cover", style: { transform: "rotate(0deg)" } })) : (React.createElement("div", { className: `w-full h-full bg-gradient-to-br ${gradient} flex items-center justify-center` },
61
+ React.createElement("div", { className: "w-full px-6 space-y-2" },
62
+ React.createElement("div", { className: "h-2 bg-white/20 rounded w-1/2 mx-auto" }),
63
+ React.createElement("div", { className: "h-1.5 bg-white/10 rounded w-3/4 mx-auto" }),
64
+ React.createElement("div", { className: "h-1.5 bg-white/10 rounded w-2/3 mx-auto" })))))));
65
+ };
@@ -0,0 +1,115 @@
1
+ "use client";
2
+ import React, { useState, useMemo, useEffect } from "react";
3
+ import { SandboxCard } from "./SandboxCard";
4
+ import { SearchBar } from "./SearchBar";
5
+ import { createApiClient } from "../../../lib/api-client";
6
+ import { Skeleton } from "../../ui/skeleton";
7
+ /**
8
+ * Transform SandboxContentPublic to SandboxItem
9
+ */
10
+ function transformSandboxContent(content) {
11
+ // Extract title from markdown (first heading) or use uid as fallback
12
+ let title = content.uid;
13
+ let description = "No description available";
14
+ if (content.markdown) {
15
+ // Try to extract title from markdown (first # heading or first line)
16
+ const lines = content.markdown.split("\n");
17
+ const titleMatch = lines.find((line) => line.trim().startsWith("#"));
18
+ if (titleMatch) {
19
+ title = titleMatch.replace(/^#+\s*/, "").trim();
20
+ }
21
+ else if (lines[0]) {
22
+ title = lines[0].trim();
23
+ }
24
+ // Extract description (first paragraph or first few lines after title)
25
+ const descriptionLines = lines
26
+ .slice(titleMatch ? lines.indexOf(titleMatch) + 1 : 1)
27
+ .filter((line) => line.trim() && !line.trim().startsWith("#"))
28
+ .slice(0, 3);
29
+ if (descriptionLines.length > 0) {
30
+ description = descriptionLines.join(" ").trim().substring(0, 200);
31
+ }
32
+ }
33
+ return {
34
+ id: content.uid,
35
+ title,
36
+ description,
37
+ sandboxId: content.uid,
38
+ };
39
+ }
40
+ export const SandboxHome = ({ apiKey, orgid, sandboxes, basePath = "/sandbox", }) => {
41
+ // Start loading as true if we need to fetch (no sandboxes prop provided)
42
+ const [loading, setLoading] = useState(!sandboxes);
43
+ const [error, setError] = useState(null);
44
+ const [searchQuery, setSearchQuery] = useState("");
45
+ const [fetchedSandboxes, setFetchedSandboxes] = useState([]);
46
+ // Fetch sandboxes from API if not provided
47
+ useEffect(() => {
48
+ if (sandboxes) {
49
+ // If sandboxes are provided as prop, use them
50
+ setFetchedSandboxes([]);
51
+ setLoading(false);
52
+ return;
53
+ }
54
+ const fetchSandboxes = async () => {
55
+ setLoading(true);
56
+ setError(null);
57
+ try {
58
+ const client = createApiClient({
59
+ apiKey,
60
+ });
61
+ const sandboxContents = await client.sandboxContent.getByPlayground(orgid);
62
+ const transformed = sandboxContents.map(transformSandboxContent);
63
+ setFetchedSandboxes(transformed);
64
+ }
65
+ catch (err) {
66
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch sandboxes";
67
+ setError(errorMessage);
68
+ console.error("Error fetching sandboxes:", err);
69
+ // Don't fall back to default sandboxes - show error state instead
70
+ setFetchedSandboxes([]);
71
+ }
72
+ finally {
73
+ setLoading(false);
74
+ }
75
+ };
76
+ fetchSandboxes();
77
+ }, [apiKey, orgid, sandboxes]);
78
+ // Use provided sandboxes or fetched sandboxes (no default fallback to prevent flickering)
79
+ const allSandboxes = sandboxes || fetchedSandboxes;
80
+ // Filter sandboxes based on search query
81
+ const filteredSandboxes = useMemo(() => {
82
+ if (!searchQuery.trim()) {
83
+ return allSandboxes;
84
+ }
85
+ const query = searchQuery.toLowerCase();
86
+ return allSandboxes.filter((sandbox) => {
87
+ var _a, _b;
88
+ const matchesTitle = ((_a = sandbox.title) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query)) || false;
89
+ const matchesDescription = ((_b = sandbox.description) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(query)) || false;
90
+ return matchesTitle || matchesDescription;
91
+ });
92
+ }, [allSandboxes, searchQuery]);
93
+ return (React.createElement("div", { className: "min-h-screen bg-black" },
94
+ React.createElement("div", { className: "pt-16 pb-12 px-4 md:px-6" },
95
+ React.createElement("div", { className: "max-w-[1400px] mx-auto" },
96
+ React.createElement(SearchBar, { value: searchQuery, onChange: setSearchQuery }))),
97
+ React.createElement("div", { className: "px-4 md:px-6 pb-16" },
98
+ React.createElement("div", { className: "max-w-[1400px] mx-auto" }, loading ? (React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4" }, [...Array(6)].map((_, i) => (React.createElement("div", { key: i, className: "relative block min-w-[240px] w-full h-[200px] border border-[#333] bg-[#0a0a0a] rounded-lg overflow-hidden" },
99
+ React.createElement("div", { className: "flex flex-col gap-1 px-6 py-5" },
100
+ React.createElement("div", { className: "flex items-center max-w-max pr-2.5" },
101
+ React.createElement(Skeleton, { className: "h-5 w-32 bg-zinc-800" })),
102
+ React.createElement(Skeleton, { className: "h-4 w-full bg-zinc-800 mt-1" }),
103
+ React.createElement(Skeleton, { className: "h-4 w-3/4 bg-zinc-800 mt-1" })),
104
+ React.createElement("div", { className: "absolute top-[110px] -right-10 w-[300px] h-[100px] rounded-lg border border-[#333] overflow-hidden shadow-lg rotate-[-5deg]" },
105
+ React.createElement(Skeleton, { className: "w-full h-full bg-zinc-800" }))))))) : error && !sandboxes ? (React.createElement("div", { className: "col-span-full text-center py-16" },
106
+ React.createElement("p", { className: "text-red-500 text-lg" },
107
+ "Error: ",
108
+ error),
109
+ React.createElement("p", { className: "text-gray-500 text-sm mt-2" }, "Please check your API key and try again."))) : (React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4" }, filteredSandboxes && filteredSandboxes.length > 0 ? (filteredSandboxes
110
+ .filter((sandbox) => sandbox && sandbox.title && sandbox.description)
111
+ .map((sandbox) => (React.createElement(SandboxCard, { key: sandbox.id, title: sandbox.title || "", description: sandbox.description || "", sandboxId: sandbox.sandboxId, basePath: basePath, imageUrl: `https://sampleappassets.blob.core.windows.net/assets/${orgid}/${sandbox.id}.png` })))) : (React.createElement("div", { className: "col-span-full text-center py-16" },
112
+ React.createElement("p", { className: "text-gray-500 text-lg" }, searchQuery
113
+ ? `No sandboxes found matching "${searchQuery}".`
114
+ : "No sandboxes available. Provide sandboxes prop to display cards.")))))))));
115
+ };
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import React from "react";
3
+ /**
4
+ * SearchBar component - provides search input for filtering sandboxes
5
+ */
6
+ export const SearchBar = ({ value, onChange, placeholder = "Search sample apps...", }) => {
7
+ return (React.createElement("div", { className: "relative max-w-md mx-auto" },
8
+ React.createElement("div", { className: "absolute inset-y-0 left-4 flex items-center pointer-events-none" },
9
+ React.createElement("svg", { className: "w-4 h-4 text-gray-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" },
10
+ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }))),
11
+ React.createElement("input", { type: "text", placeholder: placeholder, value: value, onChange: (e) => onChange(e.target.value), className: "w-full bg-[#0a0a0a] border border-[#222] rounded-lg py-3 pl-11 pr-4 text-sm text-white placeholder:text-gray-500 focus:outline-none focus:border-gray-600 transition-colors" })));
12
+ };
@@ -0,0 +1,3 @@
1
+ export { SandboxHome } from "./SandboxHome";
2
+ export { SandboxCard } from "./SandboxCard";
3
+ export { SearchBar } from "./SearchBar";