Skip to content

Commit

Permalink
Add auto fix dialog (Nutlope#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
samselikoff authored Jan 17, 2025
1 parent cd5c035 commit 5cb686c
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 35 deletions.
12 changes: 7 additions & 5 deletions app/(main)/chats/[id]/chat-log.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import type { Chat, Message } from "./page";
import ArrowLeftIcon from "@/components/icons/arrow-left";
import { splitByFirstCodeFence } from "@/lib/utils";
import { Fragment } from "react";
import Markdown from "react-markdown";
import { StickToBottom } from "use-stick-to-bottom";

Expand All @@ -29,7 +30,7 @@ export default function ChatLog({
<UserMessage content={chat.prompt} />

{chat.messages.slice(2).map((message) => (
<div key={message.id}>
<Fragment key={message.id}>
{message.role === "user" ? (
<UserMessage content={message.content} />
) : (
Expand All @@ -43,7 +44,7 @@ export default function ChatLog({
onMessageClick={onMessageClick}
/>
)}
</div>
</Fragment>
))}

{streamText && (
Expand All @@ -60,9 +61,10 @@ export default function ChatLog({

function UserMessage({ content }: { content: string }) {
return (
<div className="inline-flex items-center gap-3">
<div className="size-4 rotate-45 rounded-sm bg-gray-700" />
<div className="italic text-gray-600">{content}</div>
<div className="relative inline-flex w-4/5 items-end gap-3 self-end">
<div className="whitespace-pre-wrap rounded bg-white px-4 py-2 text-gray-600 shadow">
{content}
</div>
</div>
);
}
Expand Down
16 changes: 14 additions & 2 deletions app/(main)/chats/[id]/code-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default function CodeViewer({
activeTab,
onTabChange,
onClose,
onRequestFix,
}: {
chat: Chat;
streamText: string;
Expand All @@ -28,6 +29,7 @@ export default function CodeViewer({
activeTab: string;
onTabChange: (v: "code" | "preview") => void;
onClose: () => void;
onRequestFix: (e: string) => void;
}) {
const app = message ? extractFirstCodeBlock(message.content) : undefined;
const streamAppParts = splitByFirstCodeFence(streamText);
Expand Down Expand Up @@ -113,7 +115,12 @@ export default function CodeViewer({
<>
{language && (
<div className="flex h-full items-center justify-center">
<CodeRunner language={language} code={code} key={refresh} />
<CodeRunner
onRequestFix={onRequestFix}
language={language}
code={code}
key={refresh}
/>
</div>
)}
</>
Expand All @@ -128,7 +135,12 @@ export default function CodeViewer({
<div className="border-t border-gray-300 px-4 py-4">Output</div>
<div className="flex grow items-center justify-center border-t">
{!streamAppIsGenerating && (
<CodeRunner language={language} code={code} key={refresh} />
<CodeRunner
onRequestFix={onRequestFix}
language={language}
code={code}
key={refresh}
/>
)}
</div>
</div>
Expand Down
23 changes: 22 additions & 1 deletion app/(main)/chats/[id]/page.client.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"use client";

import { createMessage } from "@/app/(main)/actions";
import {
createMessage,
getNextCompletionStreamPromise,
} from "@/app/(main)/actions";
import LogoSmall from "@/components/icons/logo-small";
import { splitByFirstCodeFence } from "@/lib/utils";
import Link from "next/link";
Expand Down Expand Up @@ -141,6 +144,24 @@ export default function PageClient({ chat }: { chat: Chat }) {
setActiveMessage(undefined);
setIsShowingCodeViewer(false);
}}
onRequestFix={(error: string) => {
startTransition(async () => {
let newMessageText = `The code is not working. Can you fix it? Here's the error:\n\n`;
newMessageText += error.trimStart();
const message = await createMessage(
chat.id,
newMessageText,
"user",
);
const { streamPromise } =
await getNextCompletionStreamPromise(
message.id,
chat.model,
);
setStreamPromise(streamPromise);
router.refresh();
});
}}
/>
)}
</CodeViewerLayout>
Expand Down
62 changes: 60 additions & 2 deletions components/code-runner-react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@ import * as shadcnComponents from "@/lib/shadcn";
import {
SandpackPreview,
SandpackProvider,
useSandpack,
} from "@codesandbox/sandpack-react/unstyled";
import dedent from "dedent";
import { CheckIcon, CopyIcon } from "lucide-react";
import { useState } from "react";

export default function ReactCodeRunner({ code }: { code: string }) {
export default function ReactCodeRunner({
code,
onRequestFix,
}: {
code: string;
onRequestFix?: (e: string) => void;
}) {
return (
<SandpackProvider
key={code}
template="react-ts"
className="h-full w-full [&_.sp-preview-container]:flex [&_.sp-preview-container]:h-full [&_.sp-preview-container]:w-full [&_.sp-preview-container]:grow [&_.sp-preview-container]:flex-col [&_.sp-preview-container]:justify-center [&_.sp-preview-iframe]:grow"
className="relative h-full w-full [&_.sp-preview-container]:flex [&_.sp-preview-container]:h-full [&_.sp-preview-container]:w-full [&_.sp-preview-container]:grow [&_.sp-preview-container]:flex-col [&_.sp-preview-container]:justify-center [&_.sp-preview-iframe]:grow"
files={{
"App.tsx": code,
...shadcnFiles,
Expand Down Expand Up @@ -52,10 +61,59 @@ export default function ReactCodeRunner({ code }: { code: string }) {
showOpenNewtab={false}
className="h-full w-full"
/>
{onRequestFix && <ErrorMessage onRequestFix={onRequestFix} />}
</SandpackProvider>
);
}

function ErrorMessage({ onRequestFix }: { onRequestFix: (e: string) => void }) {
const { sandpack } = useSandpack();
const [didCopy, setDidCopy] = useState(false);
console.log("error:");
console.log(sandpack.error);

if (!sandpack.error) return null;

return (
<div className="absolute inset-0 flex flex-col items-center justify-center gap-4 bg-white/5 text-base backdrop-blur-sm">
<div className="max-w-[400px] rounded-md bg-red-500 p-4 text-white shadow-xl shadow-black/20">
<p className="text-lg font-medium">Error</p>

<p className="mt-4 line-clamp-[10] overflow-x-auto whitespace-pre font-mono text-xs">
{sandpack.error.message}
</p>

<div className="mt-8 flex justify-between gap-4">
<button
onClick={async () => {
if (!sandpack.error) return;

setDidCopy(true);
await window.navigator.clipboard.writeText(
sandpack.error.message,
);
await new Promise((resolve) => setTimeout(resolve, 2000));
setDidCopy(false);
}}
className="rounded border-red-300 px-2.5 py-1.5 text-sm font-semibold text-red-50"
>
{didCopy ? <CheckIcon size={18} /> : <CopyIcon size={18} />}
</button>
<button
onClick={() => {
if (!sandpack.error) return;
onRequestFix(sandpack.error.message);
}}
className="rounded bg-white px-2.5 py-1.5 text-sm font-medium text-black"
>
Try to fix
</button>
</div>
</div>
</div>
);
}

const shadcnFiles = {
"/lib/utils.ts": shadcnComponents.utils,
"/components/ui/accordion.tsx": shadcnComponents.accordian,
Expand Down
52 changes: 27 additions & 25 deletions components/code-runner.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
import {
runJavaScriptCode,
runPythonCode,
} from "@/components/code-runner-actions";
import CodeRunnerServerAction from "@/components/code-runner-server-action";
// import {
// runJavaScriptCode,
// runPythonCode,
// } from "@/components/code-runner-actions";
// import CodeRunnerServerAction from "@/components/code-runner-server-action";
import CodeRunnerReact from "./code-runner-react";

export default function CodeRunner({
language,
code,
onRequestFix,
}: {
language: string;
code: string;
onRequestFix?: (e: string) => void;
}) {
return <CodeRunnerReact code={code} />;
return <CodeRunnerReact code={code} onRequestFix={onRequestFix} />;

return (
<>
{language === "python" ? (
<CodeRunnerServerAction
code={code}
runCodeAction={runPythonCode}
key={code}
/>
) : ["ts", "js", "javascript", "typescript"].includes(language) ? (
<CodeRunnerServerAction
code={code}
runCodeAction={runJavaScriptCode}
key={code}
/>
) : (
<CodeRunnerReact code={code} />
)}
</>
);
// return (
// <>
// {language === "python" ? (
// <CodeRunnerServerAction
// code={code}
// runCodeAction={runPythonCode}
// key={code}
// />
// ) : ["ts", "js", "javascript", "typescript"].includes(language) ? (
// <CodeRunnerServerAction
// code={code}
// runCodeAction={runJavaScriptCode}
// key={code}
// />
// ) : (
// <CodeRunnerReact code={code} />
// )}
// </>
// );
}

0 comments on commit 5cb686c

Please sign in to comment.