@sampleapp.ai/sdk 1.0.29 → 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 (143) 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 +87 -0
  54. package/dist/components/sandbox/SandboxHome.js +141 -0
  55. package/dist/components/sandbox/api.js +108 -0
  56. package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +8 -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 +58 -0
  65. package/dist/components/sandbox/guardian/guardian-component.js +97 -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 +33 -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 +145 -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 +233 -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 +38 -0
  84. package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +289 -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 +48 -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/dropdown-menu.js +82 -0
  96. package/dist/components/sandbox/guardian/ui/markdown/accordion-group/accordion.js +62 -0
  97. package/dist/components/sandbox/guardian/ui/markdown/accordion-group.js +23 -0
  98. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-check.js +4 -0
  99. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-error.js +4 -0
  100. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-info.js +4 -0
  101. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-note.js +4 -0
  102. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-tip.js +4 -0
  103. package/dist/components/sandbox/guardian/ui/markdown/callout/callout-warning.js +4 -0
  104. package/dist/components/sandbox/guardian/ui/markdown/callout/shared/callout.js +9 -0
  105. package/dist/components/sandbox/guardian/ui/markdown/callout/shared/types.js +1 -0
  106. package/dist/components/sandbox/guardian/ui/markdown/card-group/card.js +18 -0
  107. package/dist/components/sandbox/guardian/ui/markdown/card-group.js +25 -0
  108. package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +87 -0
  109. package/dist/components/sandbox/guardian/ui/markdown/code-group.js +101 -0
  110. package/dist/components/sandbox/guardian/ui/markdown/icon.js +31 -0
  111. package/dist/components/sandbox/guardian/ui/markdown.js +786 -0
  112. package/dist/components/sandbox/guardian/ui/popover.js +25 -0
  113. package/dist/components/sandbox/guardian/ui/tooltip.js +25 -0
  114. package/dist/components/sandbox/guardian/utils.js +88 -0
  115. package/dist/components/sandbox/guardian/zip-to-codebase.js +259 -0
  116. package/dist/components/sandbox/guardian/zip-to-filetree.js +284 -0
  117. package/dist/components/sandbox/index.js +4 -0
  118. package/dist/components/sandbox/sandbox-control-bar.js +91 -0
  119. package/dist/components/sandbox/sandbox-header.js +52 -0
  120. package/dist/components/sandbox/sandbox-home/SandboxCard.js +59 -0
  121. package/dist/components/sandbox/sandbox-home/SandboxHome.js +174 -0
  122. package/dist/components/sandbox/sandbox-home/SearchBar.js +12 -0
  123. package/dist/components/sandbox/sandbox-home/index.js +3 -0
  124. package/dist/components/sandbox/sandbox-left-panel.js +248 -0
  125. package/dist/components/sandbox/sandbox-loading.js +48 -0
  126. package/dist/components/sandbox/sandbox-right-panel.js +247 -0
  127. package/dist/components/sandbox/types.js +1 -0
  128. package/dist/components/sandbox.js +32 -0
  129. package/dist/components/tailwind-example.js +46 -0
  130. package/dist/index.d.ts +336 -1
  131. package/dist/index.es.js +90131 -421
  132. package/dist/index.js +13 -2
  133. package/dist/index.standalone.js +61 -53
  134. package/dist/index.standalone.umd.js +17 -24
  135. package/dist/lib/api-client.example.js +60 -0
  136. package/dist/lib/api-client.js +98 -0
  137. package/dist/lib/generated-css.js +4 -0
  138. package/dist/lib/inject-styles.js +42 -0
  139. package/dist/lib/shadow-dom-wrapper.js +42 -0
  140. package/dist/lib/utils.js +5 -0
  141. package/dist/sdk.css +1 -1
  142. package/dist/tailwind.css +1 -0
  143. package/package.json +32 -5
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import React, { useEffect, useState } from "react";
3
+ import { fetchSandboxConfig } from "./api";
4
+ import GuardianPlayground from "./guardian/guardian-playground";
5
+ import { GuardianProvider } from "./guardian/context/guardian-context";
6
+ import { VmProvider } from "./guardian/context/vm-context";
7
+ import { buildGuardianConfig } from "./guardian/utils";
8
+ /**
9
+ * Sandbox component - simplified API for embedding sandboxes
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <Sandbox
14
+ * apiKey={process.env.NEXT_PUBLIC_SAMPLEAPP_API_KEY!}
15
+ * sandboxId="launchdarkly-feature-flags"
16
+ * env={{
17
+ * LAUNCHDARKLY_SDK_KEY: "sdk-xxx",
18
+ * }}
19
+ * />
20
+ * ```
21
+ */
22
+ export default function Sandbox({ apiKey, sandboxId, env, themeColor, }) {
23
+ var _a;
24
+ const [config, setConfig] = useState(null);
25
+ const [loading, setLoading] = useState(true);
26
+ const [error, setError] = useState(null);
27
+ useEffect(() => {
28
+ let cancelled = false;
29
+ async function loadConfig() {
30
+ try {
31
+ setLoading(true);
32
+ setError(null);
33
+ const fetchedConfig = await fetchSandboxConfig(apiKey, sandboxId);
34
+ if (!cancelled) {
35
+ // Override theme color if provided
36
+ if (themeColor) {
37
+ fetchedConfig.themeColor = themeColor;
38
+ fetchedConfig.useCases = fetchedConfig.useCases.map((uc) => (Object.assign(Object.assign({}, uc), { themeColor: themeColor, frameworks: uc.frameworks.map((fw) => (Object.assign(Object.assign({}, fw), { themeColor: themeColor }))) })));
39
+ }
40
+ setConfig(fetchedConfig);
41
+ setLoading(false);
42
+ }
43
+ }
44
+ catch (err) {
45
+ if (!cancelled) {
46
+ setError(err instanceof Error ? err.message : "Failed to load sandbox");
47
+ setLoading(false);
48
+ }
49
+ }
50
+ }
51
+ loadConfig();
52
+ return () => {
53
+ cancelled = true;
54
+ };
55
+ }, [apiKey, sandboxId, themeColor]);
56
+ if (loading) {
57
+ return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
58
+ React.createElement("div", { className: "text-white" }, "Loading sandbox...")));
59
+ }
60
+ if (error) {
61
+ return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
62
+ React.createElement("div", { className: "text-red-500" },
63
+ "Error: ",
64
+ error)));
65
+ }
66
+ if (!config) {
67
+ return null;
68
+ }
69
+ // Build the nested config from use cases
70
+ const nestedConfig = buildGuardianConfig(config.useCases, {
71
+ playgroundUid: sandboxId,
72
+ playgroundLogo: config.playgroundLogo || (React.createElement("div", { className: "text-white font-bold text-xl" }, config.name)),
73
+ });
74
+ // Get the first use case and framework as defaults
75
+ const firstUseCase = config.useCases[0];
76
+ const firstFramework = (_a = firstUseCase === null || firstUseCase === void 0 ? void 0 : firstUseCase.frameworks[0]) === null || _a === void 0 ? void 0 : _a.key;
77
+ if (!firstUseCase || !firstFramework) {
78
+ return (React.createElement("div", { className: "flex items-center justify-center h-screen bg-black" },
79
+ React.createElement("div", { className: "text-red-500" }, "No use cases or frameworks found")));
80
+ }
81
+ // TODO: Pass env variables to the container runtime
82
+ // This will be implemented when integrating with the container technology
83
+ return (React.createElement(GuardianProvider, null,
84
+ React.createElement(VmProvider, null,
85
+ React.createElement("div", { className: "h-screen w-screen bg-black" },
86
+ React.createElement(GuardianPlayground, { nestedConfig: nestedConfig, useCase: firstUseCase.id, framework: firstFramework, isFrame: false, apiKey: apiKey, env: env, chatUid: config.chatUid })))));
87
+ }
@@ -0,0 +1,141 @@
1
+ "use client";
2
+ import React, { useState } from "react";
3
+ // Default sandboxes array - will be replaced with API call in the future
4
+ const DEFAULT_SANDBOXES = [
5
+ {
6
+ id: "1",
7
+ title: "Next.js AI Chatbot",
8
+ description: "A full-featured, hackable Next.js AI chatbot built by Vercel",
9
+ sandboxId: "nextjs-ai-chatbot",
10
+ },
11
+ {
12
+ id: "2",
13
+ title: "Hume AI - Empathic Voice Interface",
14
+ description: "This template creates a voice chat using Hume AI's Empathic Voice Interface.",
15
+ sandboxId: "hume-ai-voice",
16
+ },
17
+ {
18
+ id: "3",
19
+ title: "Lead Agent",
20
+ description: "An inbound lead qualification and research agent built with Next.js, AI SDK, Workflow...",
21
+ sandboxId: "lead-agent",
22
+ },
23
+ {
24
+ id: "4",
25
+ title: "Slack Agent Template",
26
+ description: "This is a Slack Agent template built with Bolt for JavaScript (TypeScript) and the Nitro server...",
27
+ sandboxId: "slack-agent",
28
+ },
29
+ {
30
+ id: "5",
31
+ title: "Customer Reviews AI Summary",
32
+ description: "Use a Large Language Model to summarize customer feedback.",
33
+ sandboxId: "customer-reviews-ai",
34
+ },
35
+ {
36
+ id: "6",
37
+ title: "Nuxt AI Chatbot",
38
+ description: "An AI chatbot template to build your own chatbot powered by Nuxt MDC and Vercel AI...",
39
+ sandboxId: "nuxt-ai-chatbot",
40
+ },
41
+ {
42
+ id: "7",
43
+ title: "Morphic: AI-powered answer engine",
44
+ description: "AI answer engine with Generative UI.",
45
+ sandboxId: "morphic-ai",
46
+ },
47
+ {
48
+ id: "8",
49
+ title: "Pinecone - Vercel AI SDK Starter",
50
+ description: "A Next.js starter chatbot using Vercel's AI SDK and implements the Retrieval-Augmented...",
51
+ sandboxId: "pinecone-ai-sdk",
52
+ },
53
+ {
54
+ id: "9",
55
+ title: "qrGPT - AI QR Code Generator",
56
+ description: "QrGPT is an AI tool for you to generate beautiful QR codes using AI with one click. Powered by...",
57
+ sandboxId: "qrgpt-ai",
58
+ },
59
+ ];
60
+ // Generate a deterministic gradient based on the title
61
+ function getGradientColors(title) {
62
+ // Defensive check - ensure we have a valid string
63
+ if (title === undefined || title === null || typeof title !== "string") {
64
+ return "from-slate-700 to-slate-900";
65
+ }
66
+ const safeTitle = String(title); // Convert to string as final safety
67
+ if (safeTitle.length === 0) {
68
+ return "from-slate-700 to-slate-900";
69
+ }
70
+ try {
71
+ const hash = safeTitle
72
+ .split("")
73
+ .reduce((acc, char) => char.charCodeAt(0) + acc, 0);
74
+ const gradients = [
75
+ "from-slate-700 to-slate-900",
76
+ "from-zinc-700 to-zinc-900",
77
+ "from-gray-700 to-gray-900",
78
+ "from-neutral-700 to-neutral-900",
79
+ "from-stone-700 to-stone-900",
80
+ "from-slate-600 to-slate-800",
81
+ "from-zinc-600 to-zinc-800",
82
+ "from-gray-600 to-gray-800",
83
+ ];
84
+ return gradients[Math.abs(hash) % gradients.length];
85
+ }
86
+ catch (e) {
87
+ return "from-slate-700 to-slate-900";
88
+ }
89
+ }
90
+ // Individual Sandbox Card Component
91
+ function SandboxCard({ title, description, sandboxId, }) {
92
+ // Ensure title is always a string
93
+ const safeTitle = title || "";
94
+ const safeDescription = description || "";
95
+ const gradient = getGradientColors(safeTitle);
96
+ return (React.createElement("div", { 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" },
97
+ React.createElement("div", { className: "flex flex-col gap-1 px-6 py-5" },
98
+ React.createElement("div", { className: "flex items-center max-w-max pr-2.5" },
99
+ 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),
100
+ 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" },
101
+ 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" }))),
102
+ React.createElement("p", { className: "text-[14px] text-gray-400 m-0 line-clamp-2" }, safeDescription)),
103
+ 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" },
104
+ React.createElement("div", { className: `w-full h-full bg-gradient-to-br ${gradient} flex items-center justify-center` },
105
+ React.createElement("div", { className: "w-full px-6 space-y-2" },
106
+ React.createElement("div", { className: "h-2 bg-white/20 rounded w-1/2 mx-auto" }),
107
+ React.createElement("div", { className: "h-1.5 bg-white/10 rounded w-3/4 mx-auto" }),
108
+ React.createElement("div", { className: "h-1.5 bg-white/10 rounded w-2/3 mx-auto" }))))));
109
+ }
110
+ /**
111
+ * SandboxHome component - displays a grid of template cards for sandbox applications
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * <SandboxHome
116
+ * apiKey={process.env.NEXT_PUBLIC_SAMPLEAPP_API_KEY!}
117
+ * orgid="org-123"
118
+ * sandboxes={[
119
+ * {
120
+ * id: "1",
121
+ * title: "Next.js AI Chatbot",
122
+ * description: "A full-featured, hackable Next.js AI chatbot built by Vercel",
123
+ * sandboxId: "nextjs-ai-chatbot"
124
+ * }
125
+ * ]}
126
+ * />
127
+ * ```
128
+ */
129
+ export const SandboxHome = ({ apiKey, orgid, sandboxes, }) => {
130
+ const [loading, setLoading] = useState(false);
131
+ const [error, setError] = useState(null);
132
+ // Use provided sandboxes or default array
133
+ // TODO: Replace DEFAULT_SANDBOXES with API call using apiKey and orgid
134
+ // const displaySandboxes = await fetchSandboxes(apiKey, orgid);
135
+ const displaySandboxes = sandboxes || DEFAULT_SANDBOXES;
136
+ return (React.createElement("div", { className: "min-h-screen bg-black" },
137
+ React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4" }, displaySandboxes && displaySandboxes.length > 0 ? (displaySandboxes
138
+ .filter((sandbox) => sandbox && sandbox.title && sandbox.description)
139
+ .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" },
140
+ React.createElement("p", { className: "text-gray-500 text-lg" }, "No sandboxes available. Provide sandboxes prop to display cards."))))));
141
+ };
@@ -0,0 +1,108 @@
1
+ import { Framework, } from "./guardian/types/ide-types";
2
+ import { createApiClient } from "../../lib/api-client";
3
+ /**
4
+ * Get the base URL for API requests
5
+ * In browser, use relative URL or environment variable
6
+ * In Node.js, use environment variable or default
7
+ */
8
+ function getBaseUrl() {
9
+ return "https://api.sampleapp.ai";
10
+ // return "http://127.0.0.1:8000";
11
+ // if (typeof window !== "undefined") {
12
+ // // Browser: try to get from environment or use relative URL
13
+ // return (
14
+ // (window as any).__SAMPLEAPP_API_BASE_URL__ ||f
15
+ // process.env.NEXT_PUBLIC_FASTAPI_APP_URL ||
16
+ // process.env.FASTAPI_APP_URL ||
17
+ // "http://127.0.0.1:8000"
18
+ // );
19
+ // }
20
+ // // Node.js: use environment variable or default
21
+ // return (
22
+ // process.env.FASTAPI_APP_URL ||
23
+ // process.env.NEXT_PUBLIC_FASTAPI_APP_URL ||
24
+ // "http://127.0.0.1:8000"
25
+ // );
26
+ }
27
+ /**
28
+ * Helper function to download a file from a URL
29
+ */
30
+ function createDownloadLink(downloadEndpoint) {
31
+ if (typeof window === "undefined") {
32
+ return; // Server-side, skip
33
+ }
34
+ const link = document.createElement("a");
35
+ link.href = downloadEndpoint;
36
+ link.download = "project.zip";
37
+ document.body.appendChild(link);
38
+ link.click();
39
+ document.body.removeChild(link);
40
+ }
41
+ /**
42
+ * Fetches sandbox configuration from the API
43
+ * @param apiKey - API key from SandboxProps
44
+ * @param sandboxId - Sandbox content UID (maps to sandbox_content_uid)
45
+ * @returns Sandbox configuration
46
+ */
47
+ export async function fetchSandboxConfig(apiKey, sandboxId) {
48
+ const baseUrl = getBaseUrl();
49
+ const client = createApiClient({
50
+ baseUrl,
51
+ apiKey,
52
+ });
53
+ // Fetch sandbox content from API
54
+ const sandboxContent = await client.sandboxContent.getPublic(sandboxId);
55
+ // Get framework from sandbox content (default to nextjs if not specified)
56
+ const frameworkKey = sandboxContent.framework || Framework.NEXTJS;
57
+ // Get generated_code from sandbox content
58
+ // The generated_code is already in the correct UpdateContainerCodebaseType format
59
+ const generatedCode = sandboxContent.generated_code || {};
60
+ // Build completeCodeZipFile download endpoint
61
+ // Note: containerId would come from starting the sandbox via sdk.startSandbox()
62
+ // For now, we'll create a placeholder that can be updated when the sandbox is started
63
+ // The download endpoint format: `${baseUrl}/fileshare?container_id=${containerId}`
64
+ // When this URL is accessed, it should trigger a download with the logic:
65
+ // const link = document.createElement("a");
66
+ // link.href = downloadEndpoint;
67
+ // link.download = "project.zip";
68
+ // document.body.appendChild(link);
69
+ // link.click();
70
+ // document.body.removeChild(link);
71
+ const containerId = ""; // Will be populated when sandbox is started via sdk.startSandbox()
72
+ const completeCodeZipFile = containerId
73
+ ? `${baseUrl}/fileshare?container_id=${containerId}`
74
+ : `${baseUrl}/fileshare?container_id=placeholder`; // Placeholder until sandbox is started
75
+ // Transform SandboxContent to SandboxConfig
76
+ const config = {
77
+ id: sandboxContent.uid,
78
+ name: sandboxContent.uid, // Use UID as name if no name field exists
79
+ description: sandboxContent.markdown || "", // Use markdown as description
80
+ themeColor: "#3b82f6", // Default theme color
81
+ hasPreview: true,
82
+ chatUid: sandboxContent.chat_uid || "", // Include chatUid for startSandbox
83
+ useCases: [
84
+ {
85
+ id: sandboxContent.uid,
86
+ name: sandboxContent.uid,
87
+ description: sandboxContent.markdown || "",
88
+ themeColor: "#3b82f6",
89
+ hasPreview: true,
90
+ frameworks: [
91
+ {
92
+ key: frameworkKey,
93
+ browserUrl: "", // Will be set when sandbox is started
94
+ sandboxUid: sandboxContent.uid,
95
+ // Use generated_code if available, otherwise fallback to empty object
96
+ codeZipFile: Object.keys(generatedCode).length > 0 ? generatedCode : "", // Backward compatible: empty string if no generated_code
97
+ completeCodeZipFile,
98
+ useVm: false,
99
+ // CustomConsole is now sandboxContent.markdown
100
+ CustomConsole: sandboxContent.markdown || "",
101
+ GuideView: "Default Guide",
102
+ },
103
+ ],
104
+ },
105
+ ],
106
+ };
107
+ return config;
108
+ }
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { cn } from "../../../lib/utils";
3
+ export default function AppLayoutNoSidebar({ header, hasBodyPadding = true, children, bodyHeight = "h-[calc(100vh-6rem)]", }) {
4
+ return (React.createElement("div", null,
5
+ header && (React.createElement("header", { className: "flex h-24 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 w-full" },
6
+ React.createElement("div", { className: "flex items-center gap-2 px-4 w-full" }, header))),
7
+ React.createElement("div", { className: cn("flex flex-1 flex-col pt-0", hasBodyPadding && "gap-4 p-4", bodyHeight) }, children)));
8
+ }
@@ -0,0 +1,249 @@
1
+ "use client";
2
+ import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
3
+ import { useChat } from "@ai-sdk/react";
4
+ import { cn } from "../../../lib/utils";
5
+ import { Markdown } from "./ui/markdown";
6
+ import { codeZipFileToCodebase } from "./zip-to-codebase";
7
+ // Loading animation component - matching mcp-chat-view exactly
8
+ function MessageLoading() {
9
+ return (React.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", className: "text-foreground" },
10
+ React.createElement("circle", { cx: "4", cy: "12", r: "2", fill: "currentColor" },
11
+ React.createElement("animate", { id: "spinner_qFRN", begin: "0;spinner_OcgL.end+0.25s", attributeName: "cy", calcMode: "spline", dur: "0.6s", values: "12;6;12", keySplines: ".33,.66,.66,1;.33,0,.66,.33" })),
12
+ React.createElement("circle", { cx: "12", cy: "12", r: "2", fill: "currentColor" },
13
+ React.createElement("animate", { begin: "spinner_qFRN.begin+0.1s", attributeName: "cy", calcMode: "spline", dur: "0.6s", values: "12;6;12", keySplines: ".33,.66,.66,1;.33,0,.66,.33" })),
14
+ React.createElement("circle", { cx: "20", cy: "12", r: "2", fill: "currentColor" },
15
+ React.createElement("animate", { id: "spinner_OcgL", begin: "spinner_qFRN.begin+0.2s", attributeName: "cy", calcMode: "spline", dur: "0.6s", values: "12;6;12", keySplines: ".33,.66,.66,1;.33,0,.66,.33" }))));
16
+ }
17
+ export default function AskAiView({ codeZipFile, playgroundUid, themeColor, }) {
18
+ const [input, setInput] = useState("");
19
+ const messagesEndRef = useRef(null);
20
+ // Track current message ID from stream
21
+ const currentMessageIdRef = useRef(null);
22
+ const [generatedCode, setGeneratedCode] = useState(null);
23
+ useEffect(() => {
24
+ codeZipFileToCodebase(codeZipFile).then((generatedCode) => {
25
+ setGeneratedCode(generatedCode);
26
+ });
27
+ }, [codeZipFile]);
28
+ // Custom fetch to intercept stream and process events (matching mcp-chat-view pattern)
29
+ const customFetch = useCallback(async (input, init) => {
30
+ const response = await fetch(input, init);
31
+ if (!response.body)
32
+ return response;
33
+ // Create a new readable stream that processes the original stream
34
+ const reader = response.body.getReader();
35
+ const decoder = new TextDecoder();
36
+ let buffer = "";
37
+ const stream = new ReadableStream({
38
+ async start(controller) {
39
+ while (true) {
40
+ const { done, value } = await reader.read();
41
+ if (done) {
42
+ // Process any remaining buffer
43
+ if (buffer.trim()) {
44
+ controller.enqueue(new TextEncoder().encode(buffer));
45
+ }
46
+ controller.close();
47
+ break;
48
+ }
49
+ buffer += decoder.decode(value, { stream: true });
50
+ const lines = buffer.split("\n");
51
+ // Keep the last incomplete line in buffer
52
+ buffer = lines.pop() || "";
53
+ for (const line of lines) {
54
+ if (line.trim() === "")
55
+ continue;
56
+ if (line.startsWith("data: ")) {
57
+ const dataStr = line.slice(6).trim();
58
+ if (dataStr === "[DONE]") {
59
+ controller.enqueue(new TextEncoder().encode(`data: ${dataStr}\n\n`));
60
+ continue;
61
+ }
62
+ try {
63
+ const data = JSON.parse(dataStr);
64
+ const dataType = data.type;
65
+ // Track message ID from start event
66
+ if (dataType === "start" && data.messageId) {
67
+ currentMessageIdRef.current = data.messageId;
68
+ }
69
+ }
70
+ catch (_a) {
71
+ // Not JSON, pass through
72
+ }
73
+ }
74
+ // Pass through the original line
75
+ controller.enqueue(new TextEncoder().encode(line + "\n"));
76
+ }
77
+ }
78
+ },
79
+ });
80
+ return new Response(stream, {
81
+ headers: response.headers,
82
+ status: response.status,
83
+ statusText: response.statusText,
84
+ });
85
+ }, []);
86
+ const initialMessages = [
87
+ {
88
+ id: "welcome",
89
+ role: "assistant",
90
+ content: "Hi! Ask me anything about feature flags or this demo. Try: 'How do I enable the sample-feature for beta users?'",
91
+ parts: [
92
+ {
93
+ type: "text",
94
+ text: "Hi! Ask me anything about feature flags or this demo. Try: 'How do I enable the sample-feature for beta users?'",
95
+ },
96
+ ],
97
+ },
98
+ ];
99
+ // const {messages, sendMessage, status, setMessages} = useChat({
100
+ const { messages, sendMessage, status } = useChat({
101
+ // @ts-expect-error - api property exists in ChatInit but types may not be properly inferred
102
+ api: "/api/chat",
103
+ fetch: customFetch,
104
+ initialMessages,
105
+ });
106
+ // Auto-scroll to bottom when new messages arrive
107
+ useEffect(() => {
108
+ var _a;
109
+ (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
110
+ }, [messages]);
111
+ // Format generatedCode into a readable string for the system prompt, skipping any .env files
112
+ const codebaseContext = useMemo(() => {
113
+ if (!generatedCode || Object.keys(generatedCode).length === 0) {
114
+ return "";
115
+ }
116
+ const codebaseLines = [];
117
+ codebaseLines.push("Here is the codebase context:\n");
118
+ // Sort files by path for consistent ordering
119
+ const sortedFiles = Object.entries(generatedCode)
120
+ .filter(([filePath]) => !filePath.endsWith(".env")) // skip .env files
121
+ .sort(([a], [b]) => a.localeCompare(b));
122
+ for (const [filePath, codeOutput] of sortedFiles) {
123
+ codebaseLines.push(`\nFile: ${filePath}`);
124
+ if (codeOutput.code_language) {
125
+ codebaseLines.push(`Language: ${codeOutput.code_language}`);
126
+ }
127
+ if (codeOutput.full_code) {
128
+ codebaseLines.push("Code:");
129
+ codebaseLines.push("```");
130
+ codebaseLines.push(codeOutput.full_code);
131
+ codebaseLines.push("```");
132
+ }
133
+ codebaseLines.push(""); // Empty line between files
134
+ }
135
+ return codebaseLines.join("\n");
136
+ }, [generatedCode]);
137
+ const handleSend = () => {
138
+ const trimmed = input.trim();
139
+ if (!trimmed || status === "submitted" || status === "streaming")
140
+ return;
141
+ // Build system prompt with codebase context
142
+ const baseSystemPrompt = `You are a helpful AI assistant. You help users understand the codebase and the project and how things are being used, specifically focused on the ${playgroundUid} or related APIs and SDKs and why they are designed in a certain way.
143
+
144
+ Response style:
145
+ - VERY VERY IMPORTANT: Be concise, like 50% of the usual response length.
146
+ - Be concise: Answer directly without unnecessary preamble or repetition
147
+ - Use code examples when they clarify better than words
148
+ - For complex topics, break down into key points rather than lengthy prose
149
+ - Prioritize relevance over completeness`;
150
+ const systemPrompt = codebaseContext
151
+ ? `${baseSystemPrompt}\n\n${codebaseContext}\n\nWhen answering questions, you can reference the codebase above to provide accurate and context-aware responses.`
152
+ : baseSystemPrompt;
153
+ // Send message without mcpUrl to use simple AI chat
154
+ // Include system prompt with codebase context
155
+ sendMessage({ text: trimmed }, {
156
+ body: {
157
+ system: systemPrompt,
158
+ model: "Claude Haiku 4.5",
159
+ },
160
+ });
161
+ setInput("");
162
+ };
163
+ const isLoading = status === "submitted" || status === "streaming";
164
+ // Filter out system messages (only show user and assistant)
165
+ const visibleMessages = messages.filter((m) => m.role === "user" || m.role === "assistant");
166
+ // const handleClearChat = () => {
167
+ // // Clear all messages and reset to initial state
168
+ // // Use the initialMessages directly since they now match the UIMessage structure
169
+ // setMessages(initialMessages as unknown as typeof messages);
170
+ // setInput("");
171
+ // };
172
+ return (React.createElement("div", { className: "flex-1 flex flex-col min-h-0 h-full" },
173
+ React.createElement("div", { className: "flex-1 overflow-y-auto min-w-0 w-full px-4 py-4" },
174
+ visibleMessages.length > 0 ? (React.createElement("div", { className: "space-y-4 max-w-4xl mx-auto" },
175
+ visibleMessages.map((message) => {
176
+ return (React.createElement("div", { key: message.id }, message.role === "user" ? (React.createElement(UserMessage, { message: message, themeColor: themeColor })) : (React.createElement(AssistantMessage, { message: message }))));
177
+ }),
178
+ status === "submitted" && (React.createElement("div", { className: "flex justify-start" },
179
+ React.createElement("div", { className: "flex items-center gap-2 px-2" },
180
+ React.createElement(MessageLoading, null),
181
+ React.createElement("span", { className: "text-muted-foreground text-sm" }, "Thinking...")))))) : (React.createElement("div", { className: "flex h-full items-center justify-center" },
182
+ React.createElement("div", { className: "flex flex-col items-start space-y-4 px-4 max-w-4xl" },
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"),
185
+ React.createElement("p", { className: "text-muted-foreground text-base" }, "Try: \u201CHow does this sample app work?\u201D"))))),
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" },
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: {
190
+ boxShadow: `0 0 0 1px transparent`,
191
+ } },
192
+ React.createElement("div", { className: "flex-1 min-w-0" },
193
+ React.createElement("textarea", { value: input, onChange: (e) => setInput(e.target.value), onKeyDown: (e) => {
194
+ if (e.key === "Enter" && !e.shiftKey) {
195
+ e.preventDefault();
196
+ handleSend();
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: {
199
+ height: "auto",
200
+ minHeight: "32px",
201
+ }, onInput: (e) => {
202
+ const target = e.target;
203
+ target.style.height = "auto";
204
+ target.style.height = `${Math.min(target.scrollHeight, 160)}px`;
205
+ } })),
206
+ React.createElement("button", { onClick: handleSend, disabled: isLoading || input.trim().length === 0, className: cn("shrink-0 w-10 h-10 rounded-full flex items-center justify-center transition-all", isLoading || input.trim().length === 0
207
+ ? "bg-gray-800 text-gray-500 cursor-not-allowed"
208
+ : "text-white hover:scale-105 active:scale-95"), style: isLoading || input.trim().length === 0
209
+ ? undefined
210
+ : {
211
+ backgroundColor: themeColor,
212
+ boxShadow: `0 10px 25px -10px ${themeColor}4d`,
213
+ }, title: "Send message" }, isLoading ? (React.createElement(MessageLoading, null)) : (React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "text-current" },
214
+ React.createElement("path", { d: "M22 2L11 13", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
215
+ React.createElement("path", { d: "M22 2L15 22L11 13L2 9L22 2Z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })))))))));
216
+ }
217
+ function UserMessage({ message, themeColor, }) {
218
+ var _a;
219
+ return (React.createElement("div", { className: "flex justify-end" },
220
+ React.createElement("div", { className: "max-w-[70%] rounded-2xl px-4 py-3 text-sm text-white shadow-lg", style: { backgroundColor: themeColor } }, (_a = message.parts) === null || _a === void 0 ? void 0 :
221
+ _a.map((part, i) => (React.createElement("div", { key: `${message.id}-${i}`, className: "leading-relaxed" }, part.type === "text" && part.text))),
222
+ (!message.parts || message.parts.length === 0) && message.content && (React.createElement("div", { className: "leading-relaxed" }, message.content)))));
223
+ }
224
+ function AssistantMessage({ message }) {
225
+ var _a;
226
+ const getMessageContent = () => {
227
+ var _a;
228
+ // First try to get content from parts (custom format)
229
+ const partsContent = (_a = message.parts) === null || _a === void 0 ? void 0 : _a.filter((part) => part.type === "text").map((part) => part.text).join("");
230
+ // Fall back to direct content property (useChat format)
231
+ return partsContent || message.content || "";
232
+ };
233
+ const content = getMessageContent();
234
+ // Only render if there's actual content
235
+ if (!content || content.trim() === "") {
236
+ return null;
237
+ }
238
+ return (React.createElement("div", { className: "flex justify-start" },
239
+ React.createElement("div", { className: "max-w-[70%] rounded-2xl px-4 py-3 text-sm bg-muted border border-border" }, (_a = message.parts) === null || _a === void 0 ? void 0 :
240
+ _a.map((part, i) => {
241
+ switch (part.type) {
242
+ case "text":
243
+ return (React.createElement(Markdown, { key: `${message.id}-${i}` }, part.text || ""));
244
+ default:
245
+ return null;
246
+ }
247
+ }),
248
+ (!message.parts || message.parts.length === 0) && content && (React.createElement(Markdown, null, content)))));
249
+ }