Skip to content

Commit

Permalink
feat: Implement a proper code editor and clean state
Browse files Browse the repository at this point in the history
  • Loading branch information
bracesproul committed Oct 6, 2024
1 parent 828e2c7 commit e6f2626
Show file tree
Hide file tree
Showing 16 changed files with 436 additions and 111 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@assistant-ui/react": "^0.5.71",
"@assistant-ui/react-markdown": "^0.2.18",
"@assistant-ui/react-syntax-highlighter": "^0.0.13",
"@codemirror/lang-javascript": "^6.2.2",
"@langchain/anthropic": "^0.3.1",
"@langchain/core": "^0.3.3",
"@langchain/langgraph": "^0.2.10",
Expand All @@ -30,6 +31,7 @@
"@radix-ui/react-tooltip": "^1.1.2",
"@supabase/supabase-js": "^2.45.4",
"@types/react-syntax-highlighter": "^15.5.13",
"@uiw/react-codemirror": "^4.23.5",
"@vercel/kv": "^2.0.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
67 changes: 58 additions & 9 deletions src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ import {
} from "../types";
import { v4 as uuidv4 } from "uuid";

const defaultInputs = {
selectedArtifactId: undefined,
highlighted: undefined,
next: undefined,
language: undefined,
artifactLength: undefined,
regenerateWithEmojis: undefined,
readingLevel: undefined,
};

interface Highlight {
/**
* The id of the artifact the highlighted text belongs to
Expand All @@ -48,12 +58,18 @@ const GraphAnnotation = Annotation.Root({
/**
* The ID of the artifact to perform some action on.
*/
selectedArtifactId: Annotation<string>,
selectedArtifactId: Annotation<string | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
/**
* The part of the artifact the user highlighted. Use the `selectedArtifactId`
* to determine which artifact the highlight belongs to.
*/
highlighted: Annotation<Highlight | undefined>,
highlighted: Annotation<Highlight | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
/**
* The artifacts that have been generated in the conversation.
*/
Expand All @@ -64,23 +80,38 @@ const GraphAnnotation = Annotation.Root({
/**
* The next node to route to. Only used for the first routing node/conditional edge.
*/
next: Annotation<string | undefined>,
next: Annotation<string | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
/**
* The language to translate the artifact to.
*/
language: Annotation<LanguageOptions | undefined>,
language: Annotation<LanguageOptions | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
/**
* The length of the artifact to regenerate to.
*/
artifactLength: Annotation<ArtifactLengthOptions | undefined>,
artifactLength: Annotation<ArtifactLengthOptions | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
/**
* Whether or not to regenerate with emojis.
*/
regenerateWithEmojis: Annotation<boolean | undefined>,
regenerateWithEmojis: Annotation<boolean | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
/**
* The reading level to adjust the artifact to.
*/
readingLevel: Annotation<ReadingLevelOptions | undefined>,
readingLevel: Annotation<ReadingLevelOptions | undefined>({
reducer: (_state, update) => update,
default: () => undefined,
}),
});

type GraphReturnType = Partial<typeof GraphAnnotation.State>;
Expand Down Expand Up @@ -384,6 +415,14 @@ const generateArtifact = async (
{
name: "generate_artifact",
schema: z.object({
type: z
.enum(["code", "text"])
.describe("The content type of the artifact generated."),
language: z
.string()
.describe(
"The language of the artifact to generate. If generating code, it should be the programming language."
),
artifact: z
.string()
.describe("The content of the artifact to generate."),
Expand All @@ -406,6 +445,8 @@ const generateArtifact = async (
id: response.id ?? uuidv4(),
content: response.tool_calls?.[0]?.args.artifact,
title: response.tool_calls?.[0]?.args.title,
type: response.tool_calls?.[0]?.args.type,
language: response.tool_calls?.[0]?.args.language,
};

return {
Expand Down Expand Up @@ -539,6 +580,12 @@ const routeNode = (state: typeof GraphAnnotation.State) => {
});
};

const cleanState = (_: typeof GraphAnnotation.State) => {
return {
...defaultInputs,
};
};

const builder = new StateGraph(GraphAnnotation)
.addNode("generatePath", generatePath)
.addEdge(START, "generatePath")
Expand All @@ -549,11 +596,13 @@ const builder = new StateGraph(GraphAnnotation)
.addNode("updateArtifact", updateArtifact)
.addNode("generateArtifact", generateArtifact)
.addNode("generateFollowup", generateFollowup)
.addNode("cleanState", cleanState)
.addEdge("generateArtifact", "generateFollowup")
.addEdge("updateArtifact", "generateFollowup")
.addEdge("rewriteArtifact", "generateFollowup")
.addEdge("rewriteArtifactTheme", "generateFollowup")
.addEdge("respondToQuery", END)
.addEdge("generateFollowup", END);
.addEdge("respondToQuery", "cleanState")
.addEdge("generateFollowup", "cleanState")
.addEdge("cleanState", END);

export const graph = builder.compile();
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { ArtifactRenderer } from "@/components/ArtifactRenderer";
import { ArtifactRenderer } from "@/components/artifacts/ArtifactRenderer";
import { ContentComposerChatInterface } from "@/components/ContentComposer";
import { useGraph } from "@/hooks/useGraph";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { v4 as uuidv4 } from "uuid";
import Markdown from "react-markdown";
import { useState, useEffect, useCallback, useRef } from "react";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import { CircleArrowUp } from "lucide-react";
import { cn } from "@/lib/utils";
import { Artifact } from "@/types";
Expand All @@ -11,6 +10,8 @@ import { BaseMessage, HumanMessage } from "@langchain/core/messages";
import { convertToOpenAIFormat } from "@/lib/convert_messages";
import { X } from "lucide-react";
import { ActionsToolbar } from "./actions_toolbar";
import { TextRenderer } from "./TextRenderer";
import { CodeRenderer } from "./CodeRenderer";

export interface ArtifactRendererProps {
artifact: Artifact | undefined;
Expand Down Expand Up @@ -108,7 +109,7 @@ export function ArtifactRenderer(props: ArtifactRendererProps) {
messages: [convertToOpenAIFormat(humanMessage)],
highlighted: {
id: props.artifact.id,
startCharIndex: startIndex,
startCharIndex: startIndex === -1 ? 0 : startIndex,
endCharIndex: endIndex,
},
});
Expand Down Expand Up @@ -180,12 +181,28 @@ export function ArtifactRenderer(props: ArtifactRendererProps) {
<h1 className="text-xl font-medium">{props.artifact.title}</h1>
</div>

<div ref={contentRef} className="flex justify-center h-full pt-[10%]">
<div className="max-w-3xl w-full px-4 relative">
<div
ref={contentRef}
className={cn(
"flex justify-center h-full",
props.artifact.type === "code" ? "pt-[10px]" : "pt-[10%]"
)}
>
<div
className={cn(
"relative",
props.artifact.type === "code"
? "min-w-full min-h-full"
: "max-w-3xl w-full px-4"
)}
>
<div ref={markdownRef}>
<Markdown className="text-left leading-relaxed overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100">
{props.artifact.content}
</Markdown>
{props.artifact.type === "text" ? (
<TextRenderer artifact={props.artifact} />
) : null}
{props.artifact.type === "code" ? (
<CodeRenderer artifact={props.artifact} />
) : null}
</div>
<div
ref={highlightLayerRef}
Expand Down
42 changes: 42 additions & 0 deletions src/components/artifacts/CodeRenderer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* .codeMirrorCustom :global(.cm-focused) {
outline: none !important;
}
.codeMirrorCustom :global(.cm-editor) {
border: none !important;
}
.codeMirrorCustom :global(.cm-focused) {
outline: none !important;
}
.codeMirrorCustom :global(.cm-editor) {
border: none !important;
}
.codeMirrorCustom :global(.cm-gutters) {
border-right: none !important;
} */

.codeMirrorCustom {
height: 100vh !important;
overflow: hidden;
}

.codeMirrorCustom :global(.cm-editor) {
height: 100% !important;
border: none !important;
}

.codeMirrorCustom :global(.cm-scroller) {
overflow: auto;
}

.codeMirrorCustom :global(.cm-gutters) {
height: 100% !important;
border-right: none !important;
}

.codeMirrorCustom :global(.cm-focused) {
outline: none !important;
}
29 changes: 29 additions & 0 deletions src/components/artifacts/CodeRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Artifact } from "@/types";
import { useState } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";

// Add this import for styling
import styles from "./CodeRenderer.module.css";

export interface CodeRendererProps {
artifact: Artifact;
}

export function CodeRenderer(props: CodeRendererProps) {
const [code, setCode] = useState("");

if (!props.artifact.content) {
return null;
}

return (
<CodeMirror
className={`w-full min-h-full ${styles.codeMirrorCustom}`}
value={props.artifact.content}
height="800px"
extensions={[javascript({ jsx: true })]}
onChange={(c) => setCode(c)}
/>
);
}
14 changes: 14 additions & 0 deletions src/components/artifacts/TextRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Markdown from "react-markdown";
import { Artifact } from "@/types";

export interface TextRendererProps {
artifact: Artifact;
}

export function TextRenderer(props: TextRendererProps) {
return (
<Markdown className="text-left leading-relaxed overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100">
{props.artifact.content}
</Markdown>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { cn } from "@/lib/utils";
import { Slider } from "../ui/slider";
import { useState } from "react";
import { GraphInput } from "@/hooks/useGraph";
import { ArtifactLengthOptions } from "@/types";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../ui/tooltip";
import { GraphInput } from "@/hooks/useGraph";
import { ArtifactLengthOptions } from "@/types";
} from "@/components/ui/tooltip";
import { Slider } from "@/components/ui/slider";

export interface LengthOptionsProps {
selectedArtifactId: string | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
School,
Swords,
} from "lucide-react";
import { TooltipIconButton } from "../ui/assistant-ui/tooltip-icon-button";
import { GraphInput } from "@/hooks/useGraph";
import { ReadingLevelOptions as ReadingLevelOptionsType } from "@/types";
import { TooltipIconButton } from "@/components/ui/assistant-ui/tooltip-icon-button";

export interface ReadingLevelOptionsProps {
selectedArtifactId: string | undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { GraphInput } from "@/hooks/useGraph";
import {
UsaFlag,
ChinaFlag,
IndiaFlag,
SpanishFlag,
FrenchFlag,
} from "../icons/flags";
import { TooltipIconButton } from "../ui/assistant-ui/tooltip-icon-button";
} from "@/components/icons/flags";
import { TooltipIconButton } from "@/components/ui/assistant-ui/tooltip-icon-button";
import { GraphInput } from "@/hooks/useGraph";
import { LanguageOptions } from "@/types";

export interface TranslateOptionsProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
import { cn } from "@/lib/utils";
import { ReadingLevelOptions } from "./ReadingLevelOptions";
import { TranslateOptions } from "./TranslateOptions";
import { TooltipIconButton } from "../ui/assistant-ui/tooltip-icon-button";
import { LengthOptions } from "./LengthOptions";
import { GraphInput } from "@/hooks/useGraph";
import { TooltipIconButton } from "@/components/ui/assistant-ui/tooltip-icon-button";

type SharedComponentProps = ActionsToolbarProps & { handleClose: () => void };

Expand Down
Loading

0 comments on commit e6f2626

Please sign in to comment.