Skip to content

Commit bc880ca

Browse files
authored
Merge pull request Nutlope#15 from Nutlope/add-shadcn
Add shadcn support (feature flagged)
2 parents 003ce16 + 04b24b3 commit bc880ca

22 files changed

Lines changed: 3675 additions & 194 deletions

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,24 @@
2424

2525
## Future Tasks
2626

27+
- [ ] Experiment with a prompt rewriter and launch this as well
28+
- [ ] Make the toast that opens better like a modal for sharability
29+
- [ ] Add sharability to people can take their apps and share them publicly
30+
- [ ] Add the ability to toggle on and off shadcn components and others
31+
- [ ] Launch support for different themes – somehow pass down variables to components
2732
- [ ] Add dynamic OG images to the specific generations & include the prompt
2833
- [ ] Add more dynamic OG images for playwright
2934
- [ ] Address issue of ability to publish the same app repeatedly
3035
- [ ] Try chain of thought reasoning to see if it works better overall
3136
- [ ] Encourage best practices by making the input and textarea & having pills to generate apps w/ good prompts
37+
- [ ] Add more customizability in terms of changing the prompt, temperature, ect...
3238
- [ ] Save previous versions so people can go back and forth between the generated ones
33-
- [ ] Could be nice to show a "featured apps" route on the site on `/featured`. Have a `/id/${prompt}` dynamic route that can display a bunch of nice example apps in the sandbox ready to go
39+
- [ ] Could be nice to show a "featured apps" route on the site on /featured. Have a /id/${prompt} dynamic route that can display a bunch of nice example apps in the sandbox ready to go
3440
- [ ] Support more languages starting with Python, check out E2B
41+
- [ ] Try chain of thought reasoning to see if it works better overall
3542
- [ ] Try finetuning a smaller model on good prompts from 405b or GPT-4/Claude
3643
- [ ] Add dark mode to the site overall, nice design change
44+
- [ ] Surface errors better in codesandbox to the user so people know what is wrong
3745
- [ ] Think about how to have 405B correct itself (sometimes it makes up imports)
3846
- [ ] New route for updateCode that only sends the latest generated code + the modify request
3947
- [ ] Fix bug where if a user edits the code, then does a change, it doesn't use the edited code

app/(main)/page.tsx

Lines changed: 95 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
"use client";
22

3-
import { Toaster, toast } from "sonner";
3+
import CodeViewer from "@/components/code-viewer";
44
import Footer from "@/components/Footer";
55
import Header from "@/components/Header";
66
import { useScrollTo } from "@/hooks/use-scroll-to";
7-
import { Sandpack } from "@codesandbox/sandpack-react";
8-
import { dracula as draculaTheme } from "@codesandbox/sandpack-themes";
7+
import { domain } from "@/utils/domain";
98
import { CheckIcon } from "@heroicons/react/16/solid";
10-
import {
11-
ArrowLongRightIcon,
12-
ChevronDownIcon,
13-
ArrowUpOnSquareIcon,
14-
} from "@heroicons/react/20/solid";
15-
// import { ArrowUpOnSquareIcon } from "@heroicons/react/24/outline";
9+
import { ArrowLongRightIcon, ChevronDownIcon } from "@heroicons/react/20/solid";
10+
import { ArrowUpOnSquareIcon } from "@heroicons/react/24/outline";
1611
import * as Select from "@radix-ui/react-select";
12+
import * as Switch from "@radix-ui/react-switch";
1713
import * as Tooltip from "@radix-ui/react-tooltip";
1814
import {
1915
createParser,
@@ -22,16 +18,23 @@ import {
2218
} from "eventsource-parser";
2319
import { AnimatePresence, motion } from "framer-motion";
2420
import { FormEvent, useEffect, useState } from "react";
21+
import { toast, Toaster } from "sonner";
2522
import LoadingDots from "../../components/loading-dots";
2623
import { shareApp } from "./actions";
27-
import { domain } from "@/utils/domain";
24+
25+
const FEATURES = {
26+
shadcn: false,
27+
};
2828

2929
export default function Home() {
3030
let [status, setStatus] = useState<
3131
"initial" | "creating" | "created" | "updating" | "updated"
3232
>("initial");
3333
let [generatedCode, setGeneratedCode] = useState("");
34-
let [modelUsedForInitialCode, setModelUsedForInitialCode] = useState("");
34+
let [initialAppConfig, setInitialAppConfig] = useState({
35+
model: "",
36+
shadcn: false,
37+
});
3538
let [ref, scrollTo] = useScrollTo();
3639
let [messages, setMessages] = useState<{ role: string; content: string }[]>(
3740
[],
@@ -53,6 +56,7 @@ export default function Home() {
5356
let formData = new FormData(e.currentTarget);
5457
let model = formData.get("model");
5558
let prompt = formData.get("prompt");
59+
let shadcn = !!formData.get("shadcn");
5660
if (typeof prompt !== "string" || typeof model !== "string") {
5761
return;
5862
}
@@ -66,6 +70,7 @@ export default function Home() {
6670
body: JSON.stringify({
6771
messages: newMessages,
6872
model,
73+
shadcn,
6974
}),
7075
});
7176
if (!chatRes.ok) {
@@ -107,7 +112,7 @@ export default function Home() {
107112
{ role: "assistant", content: generatedCode },
108113
];
109114

110-
setModelUsedForInitialCode(model);
115+
setInitialAppConfig({ model, shadcn });
111116
setMessages(newMessages);
112117
setStatus("created");
113118
}
@@ -132,7 +137,8 @@ export default function Home() {
132137
},
133138
body: JSON.stringify({
134139
messages: newMessages,
135-
model: modelUsedForInitialCode,
140+
model: initialAppConfig.model,
141+
shadcn: initialAppConfig.shadcn,
136142
}),
137143
});
138144
if (!chatRes.ok) {
@@ -232,59 +238,77 @@ export default function Home() {
232238
</button>
233239
</div>
234240
</div>
235-
<div className="mt-6 flex items-center justify-center gap-3">
236-
<p className="text-xs text-gray-500">Model:</p>
237-
<Select.Root
238-
name="model"
239-
defaultValue="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo"
240-
disabled={loading}
241-
>
242-
<Select.Trigger className="group flex w-full max-w-xs items-center rounded-2xl border-[6px] border-gray-300 bg-white px-4 py-2 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500">
243-
<Select.Value />
244-
<Select.Icon className="ml-auto">
245-
<ChevronDownIcon className="size-6 text-gray-300 group-focus-visible:text-gray-500 group-enabled:group-hover:text-gray-500" />
246-
</Select.Icon>
247-
</Select.Trigger>
248-
<Select.Portal>
249-
<Select.Content className="overflow-hidden rounded-md bg-white shadow-lg">
250-
<Select.Viewport className="p-2">
251-
{[
252-
{
253-
label: "Llama 3.1 405B",
254-
value:
255-
"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
256-
},
257-
{
258-
label: "Llama 3.1 70B",
259-
value: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
260-
},
261-
{
262-
label: "Gemma 2 27B",
263-
value: "google/gemma-2-27b-it",
264-
},
265-
].map((model) => (
266-
<Select.Item
267-
key={model.value}
268-
value={model.value}
269-
className="flex cursor-pointer items-center rounded-md px-3 py-2 text-sm data-[highlighted]:bg-gray-100 data-[highlighted]:outline-none"
270-
>
271-
<Select.ItemText asChild>
272-
<span className="inline-flex items-center gap-2 text-gray-500">
273-
<div className="size-2 rounded-full bg-green-500" />
274-
{model.label}
275-
</span>
276-
</Select.ItemText>
277-
<Select.ItemIndicator className="ml-auto">
278-
<CheckIcon className="size-5 text-blue-600" />
279-
</Select.ItemIndicator>
280-
</Select.Item>
281-
))}
282-
</Select.Viewport>
283-
<Select.ScrollDownButton />
284-
<Select.Arrow />
285-
</Select.Content>
286-
</Select.Portal>
287-
</Select.Root>
241+
<div className="mt-6 flex flex-col justify-center gap-4 sm:flex-row sm:items-center sm:gap-8">
242+
<div className="flex items-center justify-between gap-3 sm:justify-center">
243+
<p className="text-gray-500 sm:text-xs">Model:</p>
244+
<Select.Root
245+
name="model"
246+
defaultValue="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo"
247+
disabled={loading}
248+
>
249+
<Select.Trigger className="group flex w-60 max-w-xs items-center rounded-2xl border-[6px] border-gray-300 bg-white px-4 py-2 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500">
250+
<Select.Value />
251+
<Select.Icon className="ml-auto">
252+
<ChevronDownIcon className="size-6 text-gray-300 group-focus-visible:text-gray-500 group-enabled:group-hover:text-gray-500" />
253+
</Select.Icon>
254+
</Select.Trigger>
255+
<Select.Portal>
256+
<Select.Content className="overflow-hidden rounded-md bg-white shadow-lg">
257+
<Select.Viewport className="p-2">
258+
{[
259+
{
260+
label: "Llama 3.1 405B",
261+
value:
262+
"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
263+
},
264+
{
265+
label: "Llama 3.1 70B",
266+
value:
267+
"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
268+
},
269+
{
270+
label: "Gemma 2 27B",
271+
value: "google/gemma-2-27b-it",
272+
},
273+
].map((model) => (
274+
<Select.Item
275+
key={model.value}
276+
value={model.value}
277+
className="flex cursor-pointer items-center rounded-md px-3 py-2 text-sm data-[highlighted]:bg-gray-100 data-[highlighted]:outline-none"
278+
>
279+
<Select.ItemText asChild>
280+
<span className="inline-flex items-center gap-2 text-gray-500">
281+
<div className="size-2 rounded-full bg-green-500" />
282+
{model.label}
283+
</span>
284+
</Select.ItemText>
285+
<Select.ItemIndicator className="ml-auto">
286+
<CheckIcon className="size-5 text-blue-600" />
287+
</Select.ItemIndicator>
288+
</Select.Item>
289+
))}
290+
</Select.Viewport>
291+
<Select.ScrollDownButton />
292+
<Select.Arrow />
293+
</Select.Content>
294+
</Select.Portal>
295+
</Select.Root>
296+
</div>
297+
298+
{FEATURES.shadcn && (
299+
<div className="flex h-full items-center justify-between gap-3 sm:justify-center">
300+
<label className="text-gray-500 sm:text-xs" htmlFor="shadcn">
301+
shadcn/ui:
302+
</label>
303+
<Switch.Root
304+
className="group flex w-20 max-w-xs items-center rounded-2xl border-[6px] border-gray-300 bg-white p-1.5 text-sm shadow-inner transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500 data-[state=checked]:bg-blue-500"
305+
id="shadcn"
306+
name="shadcn"
307+
>
308+
<Switch.Thumb className="size-7 rounded-lg bg-gray-200 shadow-[0_1px_2px] shadow-gray-400 transition data-[state=checked]:translate-x-7 data-[state=checked]:bg-white data-[state=checked]:shadow-gray-600" />
309+
</Switch.Root>
310+
</div>
311+
)}
288312
</div>
289313
</fieldset>
290314
</form>
@@ -335,7 +359,7 @@ export default function Home() {
335359
<div>
336360
<Toaster invert={true} />
337361
<Tooltip.Provider>
338-
<Tooltip.Root delayDuration={0}>
362+
<Tooltip.Root>
339363
<Tooltip.Trigger asChild>
340364
<button
341365
disabled={loading || isPublishing}
@@ -351,7 +375,7 @@ export default function Home() {
351375
shareApp({
352376
generatedCode,
353377
prompt,
354-
model: modelUsedForInitialCode,
378+
model: initialAppConfig.model,
355379
}),
356380
1000,
357381
);
@@ -363,7 +387,7 @@ export default function Home() {
363387
`${domain}/share/${appId}`,
364388
);
365389
}}
366-
className="inline-flex h-[68px] w-40 items-center justify-center gap-2 rounded-3xl bg-blue-500 transition disabled:grayscale"
390+
className="inline-flex h-[68px] w-40 items-center justify-center gap-2 rounded-3xl bg-blue-500 transition enabled:hover:bg-blue-600 disabled:grayscale"
367391
>
368392
<span className="relative">
369393
{isPublishing && (
@@ -387,7 +411,7 @@ export default function Home() {
387411
className="select-none rounded bg-white px-4 py-2.5 text-sm leading-none shadow-md shadow-black/20"
388412
sideOffset={5}
389413
>
390-
Publishes your app to the internet
414+
Publish your app to the internet.
391415
<Tooltip.Arrow className="fill-white" />
392416
</Tooltip.Content>
393417
</Tooltip.Portal>
@@ -397,40 +421,7 @@ export default function Home() {
397421
</div>
398422
<div className="relative mt-8 w-full overflow-hidden">
399423
<div className="isolate">
400-
<Sandpack
401-
theme={draculaTheme}
402-
options={{
403-
showNavigator: true,
404-
externalResources: [
405-
"https://unpkg.com/@tailwindcss/ui/dist/tailwind-ui.min.css",
406-
],
407-
editorHeight: "80vh",
408-
showTabs: false,
409-
}}
410-
files={{
411-
"App.tsx": generatedCode,
412-
"/public/index.html": `<!DOCTYPE html>
413-
<html lang="en">
414-
<head>
415-
<meta charset="UTF-8">
416-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
417-
<title>Document</title>
418-
<script src="https://cdn.tailwindcss.com"></script>
419-
</head>
420-
<body>
421-
<div id="root"></div>
422-
</body>
423-
</html>`,
424-
}}
425-
template="react-ts"
426-
customSetup={{
427-
dependencies: {
428-
"lucide-react": "latest",
429-
recharts: "2.9.0",
430-
"react-router-dom": "latest",
431-
},
432-
}}
433-
/>
424+
<CodeViewer code={generatedCode} showEditor />
434425
</div>
435426

436427
<AnimatePresence>

0 commit comments

Comments
 (0)