Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JACoB PR for Issue Enhance Artifact Component with Collapsible File Menu #395

Merged
merged 4 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
JACoB PR for Issue Enhance Artifact Component with Collapsible File…
… Menu
  • Loading branch information
jacob-ai-bot[bot] committed Nov 22, 2024
commit 134129a0faae15ac44a1fa5e2473dcc19ae4324e
272 changes: 147 additions & 125 deletions src/app/dashboard/[org]/[repo]/chat/components/Artifact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { toast } from "react-toastify";
import { useTheme } from "next-themes";
import MarkdownRenderer from "../../components/MarkdownRenderer";
import { type CodeFile } from "./Chat";
import FileMenu from "./FileMenu";

interface ArtifactProps {
content: string;
Expand All @@ -26,6 +27,8 @@ export function Artifact({
}: ArtifactProps) {
const [activeTab, setActiveTab] = useState<"view" | "edit" | "diff">("view");
const [originalContent, setOriginalContent] = useState<string | null>(null);
const [selectedFilePath, setSelectedFilePath] = useState<string>(filePath);
const [selectedContent, setSelectedContent] = useState<string>(content);
const { resolvedTheme } = useTheme();

const copyToClipboard = async (text: string) => {
Expand All @@ -37,13 +40,25 @@ export function Artifact({
if (codeFiles.length > 0) {
console.log("looking for found content...");
const foundContent =
codeFiles.find((file) => file.path === filePath)?.content ?? null;
codeFiles.find((file) => file.path === selectedFilePath)?.content ??
null;
console.log("foundContent", foundContent);
setOriginalContent(
foundContent && foundContent.trim() !== "" ? foundContent : null,
);
if (foundContent) {
setSelectedContent(foundContent);
}
}
}, [filePath, codeFiles]);
}, [selectedFilePath, codeFiles]);

const handleFileSelect = (path: string) => {
setSelectedFilePath(path);
const selectedFile = codeFiles.find((file) => file.path === path);
if (selectedFile?.content) {
setSelectedContent(selectedFile.content);
}
};

const handleSave = async () => {
try {
Expand Down Expand Up @@ -93,138 +108,145 @@ export function Artifact({
const showDiffTab = originalContent !== null && originalContent.trim() !== "";

return (
<motion.div
initial={{ opacity: 0, x: 100 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 100 }}
className="flex h-full w-1/2 flex-col overflow-hidden rounded-lg bg-white shadow-md dark:bg-slate-800"
>
<div className="border-b border-aurora-200 bg-aurora-50/50 shadow-sm dark:border-gray-700 dark:bg-slate-800">
<div className="flex items-center justify-between px-4 py-2">
<h2 className="truncate text-sm font-medium text-gray-700 dark:text-gray-200">
{fileName}
</h2>
<div className="flex">
<button
onClick={() => setActiveTab("view")}
className={`px-3 py-2 text-sm font-medium ${
activeTab === "view"
? "border-b-2 border-aurora-500 text-aurora-600"
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}`}
>
View
</button>
<button
onClick={() => setActiveTab("edit")}
className={`px-3 py-2 text-sm font-medium ${
activeTab === "edit"
? "border-b-2 border-aurora-500 text-aurora-600"
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}`}
>
Edit
</button>
{showDiffTab && (
<div className="flex h-full w-full">
<FileMenu
codeFiles={codeFiles}
selectedFilePath={selectedFilePath}
onFileSelect={handleFileSelect}
/>
<motion.div
initial={{ opacity: 0, x: 100 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 100 }}
className="flex h-full flex-1 flex-col overflow-hidden rounded-lg bg-white shadow-md dark:bg-slate-800"
>
<div className="border-b border-aurora-200 bg-aurora-50/50 shadow-sm dark:border-gray-700 dark:bg-slate-800">
<div className="flex items-center justify-between px-4 py-2">
<h2 className="truncate text-sm font-medium text-gray-700 dark:text-gray-200">
{fileName}
</h2>
<div className="flex">
<button
onClick={() => setActiveTab("diff")}
onClick={() => setActiveTab("view")}
className={`px-3 py-2 text-sm font-medium ${
activeTab === "diff"
activeTab === "view"
? "border-b-2 border-aurora-500 text-aurora-600"
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}`}
>
Diff
View
</button>
)}
<button
onClick={() => setActiveTab("edit")}
className={`px-3 py-2 text-sm font-medium ${
activeTab === "edit"
? "border-b-2 border-aurora-500 text-aurora-600"
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}`}
>
Edit
</button>
{showDiffTab && (
<button
onClick={() => setActiveTab("diff")}
className={`px-3 py-2 text-sm font-medium ${
activeTab === "diff"
? "border-b-2 border-aurora-500 text-aurora-600"
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}`}
>
Diff
</button>
)}
</div>
</div>
</div>
</div>
<div className="flex-grow overflow-hidden">
<AnimatePresence mode="wait">
{activeTab === "view" ? (
<motion.div
key="view"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full overflow-auto bg-gray-50 dark:bg-[#282c34]"
>
<MarkdownRenderer className={`markdown-chat`}>
{`\`\`\`${language}\n${content ?? originalContent}\n\`\`\``}
</MarkdownRenderer>
</motion.div>
) : activeTab === "diff" && showDiffTab ? (
<motion.div
key="diff"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full"
>
<DiffEditor
original={originalContent ?? ""}
modified={content}
language={language}
theme={resolvedTheme === "dark" ? "vs-dark" : "light"}
options={{
readOnly: true,
minimap: { enabled: false },
lineNumbers: "on",
scrollBeyondLastLine: false,
automaticLayout: true,
selectionHighlight: false,
scrollbar: {
vertical: "hidden",
horizontal: "hidden",
},
}}
/>
</motion.div>
) : (
<motion.div
key="edit"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full"
>
<Editor
value={content}
language={language}
theme={resolvedTheme === "dark" ? "vs-dark" : "light"}
options={{
minimap: { enabled: false },
lineNumbers: "on",
scrollBeyondLastLine: false,
automaticLayout: true,
selectionHighlight: false,
scrollbar: {
vertical: "hidden",
horizontal: "hidden",
},
}}
/>
</motion.div>
)}
</AnimatePresence>
</div>
<div className="flex items-center justify-end space-x-2 border-t border-gray-200 bg-white p-2 dark:border-gray-700 dark:bg-slate-800">
<button
onClick={() => copyToClipboard(content)}
className="rounded p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
title="Copy to clipboard"
>
<FontAwesomeIcon icon={faCopy} />
</button>
<button
onClick={handleSave}
className="rounded bg-aurora-500 px-3 py-1 text-sm font-medium text-white transition-colors hover:bg-aurora-600"
>
Save
</button>
</div>
</motion.div>
<div className="flex-grow overflow-hidden">
<AnimatePresence mode="wait">
{activeTab === "view" ? (
<motion.div
key="view"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full overflow-auto bg-gray-50 dark:bg-[#282c34]"
>
<MarkdownRenderer className={`markdown-chat`}>
{`\`\`\`${language}\n${selectedContent ?? originalContent}\n\`\`\``}
</MarkdownRenderer>
</motion.div>
) : activeTab === "diff" && showDiffTab ? (
<motion.div
key="diff"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full"
>
<DiffEditor
original={originalContent ?? ""}
modified={selectedContent}
language={language}
theme={resolvedTheme === "dark" ? "vs-dark" : "light"}
options={{
readOnly: true,
minimap: { enabled: false },
lineNumbers: "on",
scrollBeyondLastLine: false,
automaticLayout: true,
selectionHighlight: false,
scrollbar: {
vertical: "hidden",
horizontal: "hidden",
},
}}
/>
</motion.div>
) : (
<motion.div
key="edit"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full"
>
<Editor
value={selectedContent}
language={language}
theme={resolvedTheme === "dark" ? "vs-dark" : "light"}
options={{
minimap: { enabled: false },
lineNumbers: "on",
scrollBeyondLastLine: false,
automaticLayout: true,
selectionHighlight: false,
scrollbar: {
vertical: "hidden",
horizontal: "hidden",
},
}}
/>
</motion.div>
)}
</AnimatePresence>
</div>
<div className="flex items-center justify-end space-x-2 border-t border-gray-200 bg-white p-2 dark:border-gray-700 dark:bg-slate-800">
<button
onClick={() => copyToClipboard(selectedContent)}
className="rounded p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
title="Copy to clipboard"
>
<FontAwesomeIcon icon={faCopy} />
</button>
<button
onClick={handleSave}
className="rounded bg-aurora-500 px-3 py-1 text-sm font-medium text-white transition-colors hover:bg-aurora-600"
>
Save
</button>
</div>
</motion.div>
</div>
);
}

Expand Down
Loading
Loading