@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.
- package/dist/components/guardian/app-layout-no-sidebar.js +8 -0
- package/dist/components/guardian/ask-ai-view.js +249 -0
- package/dist/components/guardian/code-focus-section.d.ts +41 -0
- package/dist/components/guardian/code-focus-section.js +174 -0
- package/dist/components/guardian/context/guardian-context.js +94 -0
- package/dist/components/guardian/context/vm-context.js +28 -0
- package/dist/components/guardian/default-guide-view.js +34 -0
- package/dist/components/guardian/demo/guardian-demo.js +35 -0
- package/dist/components/guardian/demo/left-view/toggle.js +28 -0
- package/dist/components/guardian/demo/left-view.js +49 -0
- package/dist/components/guardian/guardian-component.js +79 -0
- package/dist/components/guardian/guardian-demo.js +35 -0
- package/dist/components/guardian/guardian-home.d.ts +4 -0
- package/dist/components/guardian/guardian-home.js +61 -0
- package/dist/components/guardian/guardian-playground.js +45 -0
- package/dist/components/guardian/guardian-style-wrapper.js +29 -0
- package/dist/components/guardian/guardian-upload-spec.d.ts +14 -0
- package/dist/components/guardian/guardian-upload-spec.js +160 -0
- package/dist/components/guardian/header/glassmorphic-combobox.d.ts +15 -0
- package/dist/components/guardian/header/glassmorphic-combobox.js +30 -0
- package/dist/components/guardian/header.js +61 -0
- package/dist/components/guardian/hooks/use-frame-messages.js +65 -0
- package/dist/components/guardian/hooks/use-frame-params.js +44 -0
- package/dist/components/guardian/hooks/use-sandbox-url-loader.js +101 -0
- package/dist/components/guardian/ide/browser.js +538 -0
- package/dist/components/guardian/index.js +8 -0
- package/dist/components/guardian/layout/app-layout-no-sidebar.js +8 -0
- package/dist/components/guardian/layout/header/glassmorphic-combobox.js +48 -0
- package/dist/components/guardian/layout/header.js +63 -0
- package/dist/components/guardian/right-view/code-view.js +56 -0
- package/dist/components/guardian/right-view/pill-file-selector.js +233 -0
- package/dist/components/guardian/right-view/preview-control-bar.js +25 -0
- package/dist/components/guardian/right-view/right-panel-view.js +38 -0
- package/dist/components/guardian/right-view/right-top-down-view.js +289 -0
- package/dist/components/guardian/right-view/right-view.js +28 -0
- package/dist/components/guardian/right-view/simplified-editor.js +234 -0
- package/dist/components/guardian/types/ide-types.js +162 -0
- package/dist/components/guardian/types.js +3 -0
- package/dist/components/guardian/ui/ai-loader.js +48 -0
- package/dist/components/guardian/ui/badge.js +24 -0
- package/dist/components/guardian/ui/button.js +45 -0
- package/dist/components/guardian/ui/command.js +63 -0
- package/dist/components/guardian/ui/console-with-app.js +17 -0
- package/dist/components/guardian/ui/dialog.js +57 -0
- package/dist/components/guardian/ui/dropdown-menu.js +82 -0
- package/dist/components/guardian/ui/markdown.js +57 -0
- package/dist/components/guardian/ui/popover.js +25 -0
- package/dist/components/guardian/ui/tooltip.js +25 -0
- package/dist/components/guardian/utils.js +88 -0
- package/dist/components/guardian/zip-to-codebase.js +246 -0
- package/dist/components/guardian/zip-to-filetree.js +284 -0
- package/dist/components/icons.js +22 -0
- package/dist/components/sandbox/Sandbox.js +87 -0
- package/dist/components/sandbox/SandboxHome.js +141 -0
- package/dist/components/sandbox/api.js +108 -0
- package/dist/components/sandbox/guardian/app-layout-no-sidebar.js +8 -0
- package/dist/components/sandbox/guardian/ask-ai-view.js +249 -0
- package/dist/components/sandbox/guardian/code-focus-section.js +174 -0
- package/dist/components/sandbox/guardian/context/guardian-context.js +94 -0
- package/dist/components/sandbox/guardian/context/vm-context.js +28 -0
- package/dist/components/sandbox/guardian/default-guide-view.js +34 -0
- package/dist/components/sandbox/guardian/demo/guardian-demo.js +35 -0
- package/dist/components/sandbox/guardian/demo/left-view/toggle.js +28 -0
- package/dist/components/sandbox/guardian/demo/left-view.js +58 -0
- package/dist/components/sandbox/guardian/guardian-component.js +97 -0
- package/dist/components/sandbox/guardian/guardian-demo.js +35 -0
- package/dist/components/sandbox/guardian/guardian-home.d.ts +4 -0
- package/dist/components/sandbox/guardian/guardian-home.js +61 -0
- package/dist/components/sandbox/guardian/guardian-playground.js +45 -0
- package/dist/components/sandbox/guardian/guardian-style-wrapper.js +33 -0
- package/dist/components/sandbox/guardian/guardian-upload-spec.d.ts +14 -0
- package/dist/components/sandbox/guardian/guardian-upload-spec.js +160 -0
- package/dist/components/sandbox/guardian/header/glassmorphic-combobox.js +30 -0
- package/dist/components/sandbox/guardian/header.js +61 -0
- package/dist/components/sandbox/guardian/hooks/use-frame-messages.js +65 -0
- package/dist/components/sandbox/guardian/hooks/use-frame-params.js +44 -0
- package/dist/components/sandbox/guardian/hooks/use-sandbox-url-loader.js +145 -0
- package/dist/components/sandbox/guardian/ide/browser.js +538 -0
- package/dist/components/sandbox/guardian/index.js +8 -0
- package/dist/components/sandbox/guardian/right-view/code-view.js +60 -0
- package/dist/components/sandbox/guardian/right-view/pill-file-selector.js +233 -0
- package/dist/components/sandbox/guardian/right-view/preview-control-bar.js +25 -0
- package/dist/components/sandbox/guardian/right-view/right-panel-view.js +38 -0
- package/dist/components/sandbox/guardian/right-view/right-top-down-view.js +289 -0
- package/dist/components/sandbox/guardian/right-view/right-view.js +28 -0
- package/dist/components/sandbox/guardian/right-view/simplified-editor.js +234 -0
- package/dist/components/sandbox/guardian/types/ide-types.js +162 -0
- package/dist/components/sandbox/guardian/types.js +3 -0
- package/dist/components/sandbox/guardian/ui/ai-loader.js +48 -0
- package/dist/components/sandbox/guardian/ui/badge.js +24 -0
- package/dist/components/sandbox/guardian/ui/button.js +45 -0
- package/dist/components/sandbox/guardian/ui/command.js +63 -0
- package/dist/components/sandbox/guardian/ui/console-with-app.js +17 -0
- package/dist/components/sandbox/guardian/ui/dialog.js +57 -0
- package/dist/components/sandbox/guardian/ui/dropdown-menu.js +82 -0
- package/dist/components/sandbox/guardian/ui/markdown/accordion-group/accordion.js +62 -0
- package/dist/components/sandbox/guardian/ui/markdown/accordion-group.js +23 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-check.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-error.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-info.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-note.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-tip.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/callout-warning.js +4 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/shared/callout.js +9 -0
- package/dist/components/sandbox/guardian/ui/markdown/callout/shared/types.js +1 -0
- package/dist/components/sandbox/guardian/ui/markdown/card-group/card.js +18 -0
- package/dist/components/sandbox/guardian/ui/markdown/card-group.js +25 -0
- package/dist/components/sandbox/guardian/ui/markdown/code-group/code-block.js +87 -0
- package/dist/components/sandbox/guardian/ui/markdown/code-group.js +101 -0
- package/dist/components/sandbox/guardian/ui/markdown/icon.js +31 -0
- package/dist/components/sandbox/guardian/ui/markdown.js +786 -0
- package/dist/components/sandbox/guardian/ui/popover.js +25 -0
- package/dist/components/sandbox/guardian/ui/tooltip.js +25 -0
- package/dist/components/sandbox/guardian/utils.js +88 -0
- package/dist/components/sandbox/guardian/zip-to-codebase.js +259 -0
- package/dist/components/sandbox/guardian/zip-to-filetree.js +284 -0
- package/dist/components/sandbox/index.js +4 -0
- package/dist/components/sandbox/sandbox-control-bar.js +91 -0
- package/dist/components/sandbox/sandbox-header.js +52 -0
- package/dist/components/sandbox/sandbox-home/SandboxCard.js +59 -0
- package/dist/components/sandbox/sandbox-home/SandboxHome.js +174 -0
- package/dist/components/sandbox/sandbox-home/SearchBar.js +12 -0
- package/dist/components/sandbox/sandbox-home/index.js +3 -0
- package/dist/components/sandbox/sandbox-left-panel.js +248 -0
- package/dist/components/sandbox/sandbox-loading.js +48 -0
- package/dist/components/sandbox/sandbox-right-panel.js +247 -0
- package/dist/components/sandbox/types.js +1 -0
- package/dist/components/sandbox.js +32 -0
- package/dist/components/tailwind-example.js +46 -0
- package/dist/index.d.ts +336 -1
- package/dist/index.es.js +90131 -421
- package/dist/index.js +13 -2
- package/dist/index.standalone.js +61 -53
- package/dist/index.standalone.umd.js +17 -24
- package/dist/lib/api-client.example.js +60 -0
- package/dist/lib/api-client.js +98 -0
- package/dist/lib/generated-css.js +4 -0
- package/dist/lib/inject-styles.js +42 -0
- package/dist/lib/shadow-dom-wrapper.js +42 -0
- package/dist/lib/utils.js +5 -0
- package/dist/sdk.css +1 -1
- package/dist/tailwind.css +1 -0
- 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
|
+
}
|