@sampleapp.ai/sdk 1.0.28 → 1.0.30

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/chat-bar.js +1 -2
  2. package/dist/components/guardian/app-layout-no-sidebar.js +8 -0
  3. package/dist/components/guardian/ask-ai-view.js +249 -0
  4. package/dist/components/guardian/code-focus-section.d.ts +41 -0
  5. package/dist/components/guardian/code-focus-section.js +174 -0
  6. package/dist/components/guardian/context/guardian-context.js +94 -0
  7. package/dist/components/guardian/context/vm-context.js +28 -0
  8. package/dist/components/guardian/default-guide-view.js +34 -0
  9. package/dist/components/guardian/demo/guardian-demo.js +35 -0
  10. package/dist/components/guardian/demo/left-view/toggle.js +28 -0
  11. package/dist/components/guardian/demo/left-view.js +49 -0
  12. package/dist/components/guardian/guardian-component.js +79 -0
  13. package/dist/components/guardian/guardian-demo.js +35 -0
  14. package/dist/components/guardian/guardian-home.d.ts +4 -0
  15. package/dist/components/guardian/guardian-home.js +61 -0
  16. package/dist/components/guardian/guardian-playground.js +45 -0
  17. package/dist/components/guardian/guardian-style-wrapper.js +29 -0
  18. package/dist/components/guardian/guardian-upload-spec.d.ts +14 -0
  19. package/dist/components/guardian/guardian-upload-spec.js +160 -0
  20. package/dist/components/guardian/header/glassmorphic-combobox.d.ts +15 -0
  21. package/dist/components/guardian/header/glassmorphic-combobox.js +30 -0
  22. package/dist/components/guardian/header.js +61 -0
  23. package/dist/components/guardian/hooks/use-frame-messages.js +65 -0
  24. package/dist/components/guardian/hooks/use-frame-params.js +44 -0
  25. package/dist/components/guardian/hooks/use-sandbox-url-loader.js +101 -0
  26. package/dist/components/guardian/ide/browser.js +538 -0
  27. package/dist/components/guardian/index.js +8 -0
  28. package/dist/components/guardian/layout/app-layout-no-sidebar.js +8 -0
  29. package/dist/components/guardian/layout/header/glassmorphic-combobox.js +48 -0
  30. package/dist/components/guardian/layout/header.js +63 -0
  31. package/dist/components/guardian/right-view/code-view.js +56 -0
  32. package/dist/components/guardian/right-view/pill-file-selector.js +233 -0
  33. package/dist/components/guardian/right-view/preview-control-bar.js +25 -0
  34. package/dist/components/guardian/right-view/right-panel-view.js +38 -0
  35. package/dist/components/guardian/right-view/right-top-down-view.js +289 -0
  36. package/dist/components/guardian/right-view/right-view.js +28 -0
  37. package/dist/components/guardian/right-view/simplified-editor.js +234 -0
  38. package/dist/components/guardian/types/ide-types.js +162 -0
  39. package/dist/components/guardian/types.js +3 -0
  40. package/dist/components/guardian/ui/ai-loader.js +48 -0
  41. package/dist/components/guardian/ui/badge.js +24 -0
  42. package/dist/components/guardian/ui/button.js +45 -0
  43. package/dist/components/guardian/ui/command.js +63 -0
  44. package/dist/components/guardian/ui/console-with-app.js +17 -0
  45. package/dist/components/guardian/ui/dialog.js +57 -0
  46. package/dist/components/guardian/ui/dropdown-menu.js +82 -0
  47. package/dist/components/guardian/ui/markdown.js +57 -0
  48. package/dist/components/guardian/ui/popover.js +25 -0
  49. package/dist/components/guardian/ui/tooltip.js +25 -0
  50. package/dist/components/guardian/utils.js +88 -0
  51. package/dist/components/guardian/zip-to-codebase.js +246 -0
  52. package/dist/components/guardian/zip-to-filetree.js +284 -0
  53. package/dist/components/icons.js +22 -0
  54. package/dist/components/sandbox/Sandbox.js +87 -0
  55. package/dist/components/sandbox/SandboxHome.js +141 -0
  56. package/dist/components/sandbox/api.js +108 -0
  57. package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +8 -0
  58. package/dist/components/sandbox/guardian/ask-ai-view.js +249 -0
  59. package/dist/components/sandbox/guardian/code-focus-section.js +174 -0
  60. package/dist/components/sandbox/guardian/context/guardian-context.js +94 -0
  61. package/dist/components/sandbox/guardian/context/vm-context.js +28 -0
  62. package/dist/components/sandbox/guardian/default-guide-view.js +34 -0
  63. package/dist/components/sandbox/guardian/demo/guardian-demo.js +35 -0
  64. package/dist/components/sandbox/guardian/demo/left-view/toggle.js +28 -0
  65. package/dist/components/sandbox/guardian/demo/left-view.js +58 -0
  66. package/dist/components/sandbox/guardian/guardian-component.js +97 -0
  67. package/dist/components/sandbox/guardian/guardian-demo.js +35 -0
  68. package/dist/components/sandbox/guardian/guardian-home.d.ts +4 -0
  69. package/dist/components/sandbox/guardian/guardian-home.js +61 -0
  70. package/dist/components/sandbox/guardian/guardian-playground.js +45 -0
  71. package/dist/components/sandbox/guardian/guardian-style-wrapper.js +33 -0
  72. package/dist/components/sandbox/guardian/guardian-upload-spec.d.ts +14 -0
  73. package/dist/components/sandbox/guardian/guardian-upload-spec.js +160 -0
  74. package/dist/components/sandbox/guardian/header/glassmorphic-combobox.js +30 -0
  75. package/dist/components/sandbox/guardian/header.js +61 -0
  76. package/dist/components/sandbox/guardian/hooks/use-frame-messages.js +65 -0
  77. package/dist/components/sandbox/guardian/hooks/use-frame-params.js +44 -0
  78. package/dist/components/sandbox/guardian/hooks/use-sandbox-url-loader.js +145 -0
  79. package/dist/components/sandbox/guardian/ide/browser.js +538 -0
  80. package/dist/components/sandbox/guardian/index.js +8 -0
  81. package/dist/components/sandbox/guardian/right-view/code-view.js +60 -0
  82. package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +233 -0
  83. package/dist/components/sandbox/guardian/right-view/preview-control-bar.js +25 -0
  84. package/dist/components/sandbox/guardian/right-view/right-panel-view.js +38 -0
  85. package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +289 -0
  86. package/dist/components/sandbox/guardian/right-view/right-view.js +28 -0
  87. package/dist/components/sandbox/guardian/right-view/simplified-editor.js +234 -0
  88. package/dist/components/sandbox/guardian/types/ide-types.js +162 -0
  89. package/dist/components/sandbox/guardian/types.js +3 -0
  90. package/dist/components/sandbox/guardian/ui/ai-loader.js +48 -0
  91. package/dist/components/sandbox/guardian/ui/badge.js +24 -0
  92. package/dist/components/sandbox/guardian/ui/button.js +45 -0
  93. package/dist/components/sandbox/guardian/ui/command.js +63 -0
  94. package/dist/components/sandbox/guardian/ui/console-with-app.js +17 -0
  95. package/dist/components/sandbox/guardian/ui/dialog.js +57 -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 +87 -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 +786 -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 +88 -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 +59 -0
  122. package/dist/components/sandbox/sandbox-home/SandboxHome.js +174 -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/index.d.ts +336 -1
  132. package/dist/index.es.js +90151 -667
  133. package/dist/index.js +13 -2
  134. package/dist/index.standalone.js +61 -53
  135. package/dist/index.standalone.umd.js +17 -89
  136. package/dist/index.umd.js +86 -151
  137. package/dist/lib/api-client.example.js +60 -0
  138. package/dist/lib/api-client.js +98 -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 +32 -5
@@ -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,59 @@
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, }) => {
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
+ window.location.href = `/sample/${sandboxId}`;
44
+ }
45
+ };
46
+ 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" },
47
+ React.createElement("div", { className: "flex flex-col gap-1 px-6 py-5" },
48
+ React.createElement("div", { className: "flex items-center max-w-max pr-2.5" },
49
+ 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),
50
+ 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" },
51
+ 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" }))),
52
+ React.createElement("p", { className: "text-[14px] text-gray-400 m-0 line-clamp-2" }, safeDescription)),
53
+ 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" },
54
+ React.createElement("div", { className: `w-full h-full bg-gradient-to-br ${gradient} flex items-center justify-center` },
55
+ React.createElement("div", { className: "w-full px-6 space-y-2" },
56
+ React.createElement("div", { className: "h-2 bg-white/20 rounded w-1/2 mx-auto" }),
57
+ React.createElement("div", { className: "h-1.5 bg-white/10 rounded w-3/4 mx-auto" }),
58
+ React.createElement("div", { className: "h-1.5 bg-white/10 rounded w-2/3 mx-auto" }))))));
59
+ };
@@ -0,0 +1,174 @@
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
+ // Default sandboxes array - will be replaced with API call in the future
7
+ const DEFAULT_SANDBOXES = [
8
+ {
9
+ id: "1",
10
+ title: "Next.js AI Chatbot",
11
+ description: "A full-featured, hackable Next.js AI chatbot built by Vercel",
12
+ sandboxId: "nextjs-ai-chatbot",
13
+ },
14
+ {
15
+ id: "2",
16
+ title: "Hume AI - Empathic Voice Interface",
17
+ description: "This template creates a voice chat using Hume AI's Empathic Voice Interface.",
18
+ sandboxId: "hume-ai-voice",
19
+ },
20
+ {
21
+ id: "3",
22
+ title: "Lead Agent",
23
+ description: "An inbound lead qualification and research agent built with Next.js, AI SDK, Workflow...",
24
+ sandboxId: "lead-agent",
25
+ },
26
+ {
27
+ id: "4",
28
+ title: "Slack Agent Template",
29
+ description: "This is a Slack Agent template built with Bolt for JavaScript (TypeScript) and the Nitro server...",
30
+ sandboxId: "slack-agent",
31
+ },
32
+ {
33
+ id: "5",
34
+ title: "Customer Reviews AI Summary",
35
+ description: "Use a Large Language Model to summarize customer feedback.",
36
+ sandboxId: "customer-reviews-ai",
37
+ },
38
+ {
39
+ id: "6",
40
+ title: "Nuxt AI Chatbot",
41
+ description: "An AI chatbot template to build your own chatbot powered by Nuxt MDC and Vercel AI...",
42
+ sandboxId: "nuxt-ai-chatbot",
43
+ },
44
+ {
45
+ id: "7",
46
+ title: "Morphic: AI-powered answer engine",
47
+ description: "AI answer engine with Generative UI.",
48
+ sandboxId: "morphic-ai",
49
+ },
50
+ {
51
+ id: "8",
52
+ title: "Pinecone - Vercel AI SDK Starter",
53
+ description: "A Next.js starter chatbot using Vercel's AI SDK and implements the Retrieval-Augmented...",
54
+ sandboxId: "pinecone-ai-sdk",
55
+ },
56
+ {
57
+ id: "9",
58
+ title: "qrGPT - AI QR Code Generator",
59
+ description: "QrGPT is an AI tool for you to generate beautiful QR codes using AI with one click. Powered by...",
60
+ sandboxId: "qrgpt-ai",
61
+ },
62
+ ];
63
+ /**
64
+ * Get the base URL for API requests
65
+ */
66
+ function getBaseUrl(customBaseUrl) {
67
+ if (customBaseUrl) {
68
+ return customBaseUrl;
69
+ }
70
+ return "http://127.0.0.1:8000";
71
+ }
72
+ /**
73
+ * Transform SandboxContent to SandboxItem
74
+ */
75
+ function transformSandboxContent(content) {
76
+ // Extract title from markdown (first heading) or use uid as fallback
77
+ let title = content.uid;
78
+ let description = "No description available";
79
+ if (content.markdown) {
80
+ // Try to extract title from markdown (first # heading or first line)
81
+ const lines = content.markdown.split("\n");
82
+ const titleMatch = lines.find((line) => line.trim().startsWith("#"));
83
+ if (titleMatch) {
84
+ title = titleMatch.replace(/^#+\s*/, "").trim();
85
+ }
86
+ else if (lines[0]) {
87
+ title = lines[0].trim();
88
+ }
89
+ // Extract description (first paragraph or first few lines after title)
90
+ const descriptionLines = lines
91
+ .slice(titleMatch ? lines.indexOf(titleMatch) + 1 : 1)
92
+ .filter((line) => line.trim() && !line.trim().startsWith("#"))
93
+ .slice(0, 3);
94
+ if (descriptionLines.length > 0) {
95
+ description = descriptionLines.join(" ").trim().substring(0, 200);
96
+ }
97
+ }
98
+ return {
99
+ id: content.uid || content.id.toString(),
100
+ title,
101
+ description,
102
+ sandboxId: content.uid,
103
+ };
104
+ }
105
+ export const SandboxHome = ({ apiKey, orgid, baseUrl, sandboxes, }) => {
106
+ const [loading, setLoading] = useState(false);
107
+ const [error, setError] = useState(null);
108
+ const [searchQuery, setSearchQuery] = useState("");
109
+ const [fetchedSandboxes, setFetchedSandboxes] = useState([]);
110
+ // Fetch sandboxes from API if not provided
111
+ useEffect(() => {
112
+ if (sandboxes) {
113
+ // If sandboxes are provided as prop, use them
114
+ setFetchedSandboxes([]);
115
+ return;
116
+ }
117
+ const fetchSandboxes = async () => {
118
+ setLoading(true);
119
+ setError(null);
120
+ try {
121
+ const client = createApiClient({
122
+ baseUrl: getBaseUrl(baseUrl),
123
+ apiKey,
124
+ });
125
+ const sandboxContents = await client.sandboxContent.getByPlayground(orgid);
126
+ const transformed = sandboxContents.map(transformSandboxContent);
127
+ setFetchedSandboxes(transformed);
128
+ }
129
+ catch (err) {
130
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch sandboxes";
131
+ setError(errorMessage);
132
+ console.error("Error fetching sandboxes:", err);
133
+ // Fall back to default sandboxes on error
134
+ setFetchedSandboxes(DEFAULT_SANDBOXES);
135
+ }
136
+ finally {
137
+ setLoading(false);
138
+ }
139
+ };
140
+ fetchSandboxes();
141
+ }, [apiKey, orgid, baseUrl, sandboxes]);
142
+ // Use provided sandboxes, fetched sandboxes, or default array
143
+ const allSandboxes = sandboxes ||
144
+ (fetchedSandboxes.length > 0 ? fetchedSandboxes : DEFAULT_SANDBOXES);
145
+ // Filter sandboxes based on search query
146
+ const filteredSandboxes = useMemo(() => {
147
+ if (!searchQuery.trim()) {
148
+ return allSandboxes;
149
+ }
150
+ const query = searchQuery.toLowerCase();
151
+ return allSandboxes.filter((sandbox) => {
152
+ var _a, _b;
153
+ const matchesTitle = ((_a = sandbox.title) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query)) || false;
154
+ const matchesDescription = ((_b = sandbox.description) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(query)) || false;
155
+ return matchesTitle || matchesDescription;
156
+ });
157
+ }, [allSandboxes, searchQuery]);
158
+ return (React.createElement("div", { className: "min-h-screen bg-black" },
159
+ React.createElement("div", { className: "pt-16 pb-12 px-4 md:px-6" },
160
+ React.createElement("div", { className: "max-w-[1400px] mx-auto" },
161
+ React.createElement(SearchBar, { value: searchQuery, onChange: setSearchQuery }))),
162
+ React.createElement("div", { className: "px-4 md:px-6 pb-16" },
163
+ React.createElement("div", { className: "max-w-[1400px] mx-auto" }, loading ? (React.createElement("div", { className: "col-span-full text-center py-16" },
164
+ React.createElement("p", { className: "text-gray-500 text-lg" }, "Loading sandboxes..."))) : error && !sandboxes ? (React.createElement("div", { className: "col-span-full text-center py-16" },
165
+ React.createElement("p", { className: "text-red-500 text-lg" },
166
+ "Error: ",
167
+ error),
168
+ React.createElement("p", { className: "text-gray-500 text-sm mt-2" }, "Falling back to default sandboxes."))) : (React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4" }, filteredSandboxes && filteredSandboxes.length > 0 ? (filteredSandboxes
169
+ .filter((sandbox) => sandbox && sandbox.title && sandbox.description)
170
+ .map((sandbox) => (React.createElement(SandboxCard, { key: sandbox.id, title: sandbox.title || "", description: sandbox.description || "", sandboxId: sandbox.sandboxId })))) : (React.createElement("div", { className: "col-span-full text-center py-16" },
171
+ React.createElement("p", { className: "text-gray-500 text-lg" }, searchQuery
172
+ ? `No sandboxes found matching "${searchQuery}".`
173
+ : "No sandboxes available. Provide sandboxes prop to display cards.")))))))));
174
+ };
@@ -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 templates...", }) => {
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";
@@ -0,0 +1,248 @@
1
+ "use client";
2
+ import React, { useState, useCallback, useRef, useEffect } from "react";
3
+ import { LoaderIcon, ArrowUpIcon } from "../icons";
4
+ /**
5
+ * Left panel with Guide and Ask AI tabs
6
+ */
7
+ export function SandboxLeftPanel({ mdx, GuideComponent, playgroundUid, themeColor, apiKey, apiBaseUrl = "https://api.sampleapp.ai", files, }) {
8
+ const [activeTab, setActiveTab] = useState("guide");
9
+ return (React.createElement("div", { style: {
10
+ display: "flex",
11
+ flexDirection: "column",
12
+ height: "100%",
13
+ backgroundColor: "#18181b",
14
+ borderRight: "1px solid rgba(255, 255, 255, 0.1)",
15
+ } },
16
+ React.createElement("div", { style: {
17
+ display: "flex",
18
+ justifyContent: "center",
19
+ padding: "12px 16px",
20
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
21
+ } },
22
+ React.createElement("div", { style: {
23
+ display: "flex",
24
+ gap: "4px",
25
+ padding: "4px",
26
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
27
+ borderRadius: "8px",
28
+ } },
29
+ React.createElement(TabButton, { active: activeTab === "guide", onClick: () => setActiveTab("guide"), themeColor: themeColor }, "Guide"),
30
+ React.createElement(TabButton, { active: activeTab === "chat", onClick: () => setActiveTab("chat"), themeColor: themeColor }, "Ask AI"))),
31
+ React.createElement("div", { style: { flex: 1, overflow: "hidden", minHeight: 0 } },
32
+ activeTab === "guide" && (React.createElement(GuideTab, { mdx: mdx, GuideComponent: GuideComponent, themeColor: themeColor })),
33
+ activeTab === "chat" && (React.createElement(ChatTab, { playgroundUid: playgroundUid, themeColor: themeColor, apiKey: apiKey, apiBaseUrl: apiBaseUrl, files: files })))));
34
+ }
35
+ function TabButton({ children, active, onClick, themeColor, }) {
36
+ return (React.createElement("button", { onClick: onClick, style: {
37
+ padding: "8px 16px",
38
+ fontSize: "13px",
39
+ fontWeight: 500,
40
+ borderRadius: "6px",
41
+ border: "none",
42
+ cursor: "pointer",
43
+ transition: "all 0.15s ease",
44
+ backgroundColor: active ? themeColor : "transparent",
45
+ color: active ? "white" : "rgba(255, 255, 255, 0.6)",
46
+ } }, children));
47
+ }
48
+ function GuideTab({ mdx, GuideComponent, themeColor, }) {
49
+ if (GuideComponent) {
50
+ return (React.createElement("div", { style: { height: "100%", overflow: "auto", padding: "24px" } },
51
+ React.createElement(GuideComponent, null)));
52
+ }
53
+ if (mdx) {
54
+ return (React.createElement("div", { style: {
55
+ height: "100%",
56
+ overflow: "auto",
57
+ padding: "24px",
58
+ } },
59
+ React.createElement("div", { style: {
60
+ fontSize: "14px",
61
+ lineHeight: 1.7,
62
+ color: "rgba(255, 255, 255, 0.8)",
63
+ }, dangerouslySetInnerHTML: { __html: mdx } })));
64
+ }
65
+ return (React.createElement("div", { style: {
66
+ height: "100%",
67
+ display: "flex",
68
+ alignItems: "center",
69
+ justifyContent: "center",
70
+ padding: "24px",
71
+ } },
72
+ React.createElement("div", { style: { textAlign: "center" } },
73
+ React.createElement("p", { style: {
74
+ fontSize: "14px",
75
+ color: "rgba(255, 255, 255, 0.5)",
76
+ } }, "No guide content provided."),
77
+ React.createElement("p", { style: {
78
+ fontSize: "12px",
79
+ color: "rgba(255, 255, 255, 0.3)",
80
+ marginTop: "8px",
81
+ } },
82
+ "Pass ",
83
+ React.createElement("code", { style: { color: themeColor } }, "mdx"),
84
+ " or",
85
+ " ",
86
+ React.createElement("code", { style: { color: themeColor } }, "GuideComponent"),
87
+ " prop to display content."))));
88
+ }
89
+ function ChatTab({ playgroundUid, themeColor, apiKey, apiBaseUrl, files, }) {
90
+ const [messages, setMessages] = useState([]);
91
+ const [input, setInput] = useState("");
92
+ const [isLoading, setIsLoading] = useState(false);
93
+ const messagesEndRef = useRef(null);
94
+ const textareaRef = useRef(null);
95
+ // Scroll to bottom when messages change
96
+ useEffect(() => {
97
+ var _a;
98
+ (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
99
+ }, [messages]);
100
+ const handleSubmit = useCallback(async (e) => {
101
+ e.preventDefault();
102
+ if (!input.trim() || isLoading)
103
+ return;
104
+ const userMessage = input.trim();
105
+ setInput("");
106
+ setMessages((prev) => [...prev, { role: "user", content: userMessage }]);
107
+ setIsLoading(true);
108
+ try {
109
+ // Build context from files if available
110
+ const codeContext = files
111
+ ? files.map((f) => `--- ${f.path} ---\n${f.content}`).join("\n\n")
112
+ : "";
113
+ const response = await fetch(`${apiBaseUrl}/v1/chat`, {
114
+ method: "POST",
115
+ headers: Object.assign({ "Content-Type": "application/json" }, (apiKey && { Authorization: `Bearer ${apiKey}` })),
116
+ body: JSON.stringify({
117
+ playgroundUid,
118
+ messages: [
119
+ ...messages,
120
+ { role: "user", content: userMessage },
121
+ ],
122
+ context: codeContext,
123
+ }),
124
+ });
125
+ if (!response.ok) {
126
+ throw new Error(`API error: ${response.status}`);
127
+ }
128
+ const data = await response.json();
129
+ setMessages((prev) => [
130
+ ...prev,
131
+ { role: "assistant", content: data.message || data.content },
132
+ ]);
133
+ }
134
+ catch (error) {
135
+ console.error("Chat error:", error);
136
+ setMessages((prev) => [
137
+ ...prev,
138
+ {
139
+ role: "assistant",
140
+ content: "Sorry, I encountered an error. Please check your API key and try again.",
141
+ },
142
+ ]);
143
+ }
144
+ finally {
145
+ setIsLoading(false);
146
+ }
147
+ }, [input, isLoading, messages, playgroundUid, apiKey, apiBaseUrl, files]);
148
+ return (React.createElement("div", { style: {
149
+ height: "100%",
150
+ display: "flex",
151
+ flexDirection: "column",
152
+ } },
153
+ React.createElement("div", { style: {
154
+ flex: 1,
155
+ overflow: "auto",
156
+ padding: "16px",
157
+ } }, messages.length === 0 ? (React.createElement("div", { style: {
158
+ height: "100%",
159
+ display: "flex",
160
+ alignItems: "center",
161
+ justifyContent: "center",
162
+ } },
163
+ React.createElement("div", { style: { textAlign: "center", maxWidth: "280px" } },
164
+ React.createElement("div", { style: {
165
+ width: "48px",
166
+ height: "48px",
167
+ borderRadius: "12px",
168
+ backgroundColor: `${themeColor}20`,
169
+ display: "flex",
170
+ alignItems: "center",
171
+ justifyContent: "center",
172
+ margin: "0 auto 16px",
173
+ } },
174
+ React.createElement("span", { style: { fontSize: "24px" } }, "\uD83D\uDCAC")),
175
+ React.createElement("p", { style: {
176
+ fontSize: "14px",
177
+ fontWeight: 500,
178
+ color: "rgba(255, 255, 255, 0.8)",
179
+ marginBottom: "8px",
180
+ } }, "Ask AI about this code"),
181
+ React.createElement("p", { style: {
182
+ fontSize: "12px",
183
+ color: "rgba(255, 255, 255, 0.4)",
184
+ } }, "Get help understanding the code, debugging issues, or extending functionality.")))) : (React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "16px" } },
185
+ messages.map((msg, i) => (React.createElement("div", { key: i, style: {
186
+ display: "flex",
187
+ flexDirection: "column",
188
+ alignItems: msg.role === "user" ? "flex-end" : "flex-start",
189
+ } },
190
+ React.createElement("div", { style: {
191
+ maxWidth: "85%",
192
+ padding: "12px 16px",
193
+ borderRadius: "12px",
194
+ backgroundColor: msg.role === "user" ? themeColor : "rgba(255, 255, 255, 0.1)",
195
+ color: "rgba(255, 255, 255, 0.9)",
196
+ fontSize: "13px",
197
+ lineHeight: 1.5,
198
+ } }, msg.content)))),
199
+ isLoading && (React.createElement("div", { style: { display: "flex", alignItems: "flex-start" } },
200
+ React.createElement("div", { style: {
201
+ padding: "12px 16px",
202
+ borderRadius: "12px",
203
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
204
+ } },
205
+ React.createElement(LoaderIcon, { size: 16, style: { color: themeColor } })))),
206
+ React.createElement("div", { ref: messagesEndRef })))),
207
+ React.createElement("div", { style: {
208
+ padding: "16px",
209
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
210
+ } },
211
+ React.createElement("form", { onSubmit: handleSubmit },
212
+ React.createElement("div", { style: {
213
+ display: "flex",
214
+ gap: "8px",
215
+ alignItems: "flex-end",
216
+ } },
217
+ React.createElement("textarea", { ref: textareaRef, value: input, onChange: (e) => setInput(e.target.value), onKeyDown: (e) => {
218
+ if (e.key === "Enter" && !e.shiftKey) {
219
+ e.preventDefault();
220
+ handleSubmit(e);
221
+ }
222
+ }, placeholder: "Ask a question...", rows: 1, style: {
223
+ flex: 1,
224
+ padding: "12px",
225
+ borderRadius: "8px",
226
+ border: "1px solid rgba(255, 255, 255, 0.1)",
227
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
228
+ color: "rgba(255, 255, 255, 0.9)",
229
+ fontSize: "13px",
230
+ resize: "none",
231
+ outline: "none",
232
+ fontFamily: "inherit",
233
+ } }),
234
+ React.createElement("button", { type: "submit", disabled: !input.trim() || isLoading, style: {
235
+ width: "40px",
236
+ height: "40px",
237
+ borderRadius: "8px",
238
+ border: "none",
239
+ backgroundColor: input.trim() ? themeColor : "rgba(255, 255, 255, 0.1)",
240
+ color: "white",
241
+ cursor: input.trim() && !isLoading ? "pointer" : "not-allowed",
242
+ display: "flex",
243
+ alignItems: "center",
244
+ justifyContent: "center",
245
+ transition: "all 0.15s ease",
246
+ opacity: isLoading ? 0.5 : 1,
247
+ } }, isLoading ? (React.createElement(LoaderIcon, { size: 18 })) : (React.createElement(ArrowUpIcon, { size: 18 }))))))));
248
+ }