mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 14:22:28 +08:00
feat: implement diagram history functionality with history dialog in ChatInput and ChatPanel
This commit is contained in:
20
app/page.tsx
20
app/page.tsx
@@ -11,6 +11,12 @@ export default function Home() {
|
||||
const [chartXML, setChartXML] = useState<string>("");
|
||||
// Add a ref to store the resolver function
|
||||
const resolverRef = useRef<((value: string) => void) | null>(null);
|
||||
// Add state for diagram history
|
||||
const [diagramHistory, setDiagramHistory] = useState<
|
||||
{ svg: string; xml: string }[]
|
||||
>([]);
|
||||
// Add state for latest SVG
|
||||
const [latestSvg, setLatestSvg] = useState<string>("");
|
||||
|
||||
const handleExport = () => {
|
||||
if (drawioRef.current) {
|
||||
@@ -28,6 +34,16 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
// Add function to add current diagram to history
|
||||
const addToHistory = () => {
|
||||
if (latestSvg && chartXML) {
|
||||
setDiagramHistory((prev) => [
|
||||
...prev,
|
||||
{ svg: latestSvg, xml: chartXML },
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-100">
|
||||
<div className="w-2/3 p-1">
|
||||
@@ -36,6 +52,8 @@ export default function Home() {
|
||||
onExport={(data) => {
|
||||
const extractedXML = extractDiagramXML(data.data);
|
||||
setChartXML(extractedXML);
|
||||
// Store the latest SVG data
|
||||
setLatestSvg(data.data);
|
||||
// If there's a pending resolver, resolve it with the fresh XML
|
||||
if (resolverRef.current) {
|
||||
resolverRef.current(extractedXML);
|
||||
@@ -61,6 +79,8 @@ export default function Home() {
|
||||
handleExport();
|
||||
});
|
||||
}}
|
||||
diagramHistory={diagramHistory}
|
||||
onAddToHistory={addToHistory}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
import React, { useCallback, useRef, useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Loader2, Send, RotateCcw, Image as ImageIcon, X } from "lucide-react";
|
||||
import {
|
||||
Loader2,
|
||||
Send,
|
||||
RotateCcw,
|
||||
Image as ImageIcon,
|
||||
X,
|
||||
History,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -30,6 +37,10 @@ interface ChatInputProps {
|
||||
onDisplayChart: (xml: string) => void;
|
||||
files?: FileList;
|
||||
onFileChange?: (files: FileList | undefined) => void;
|
||||
diagramHistory?: { svg: string; xml: string }[];
|
||||
onSelectHistoryItem?: (xml: string) => void;
|
||||
showHistory?: boolean;
|
||||
setShowHistory?: (show: boolean) => void;
|
||||
}
|
||||
|
||||
export function ChatInput({
|
||||
@@ -41,6 +52,10 @@ export function ChatInput({
|
||||
onDisplayChart,
|
||||
files,
|
||||
onFileChange,
|
||||
diagramHistory = [],
|
||||
onSelectHistoryItem = () => {},
|
||||
showHistory = false,
|
||||
setShowHistory = () => {},
|
||||
}: ChatInputProps) {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -250,8 +265,87 @@ export function ChatInput({
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* History Dialog */}
|
||||
<Dialog open={showHistory} onOpenChange={setShowHistory}>
|
||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Diagram History</DialogTitle>
|
||||
<DialogDescription>
|
||||
Click on a diagram to restore it
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{diagramHistory.length === 0 ? (
|
||||
<div className="text-center p-4 text-gray-500">
|
||||
No history available yet. Send messages to
|
||||
create diagram history.
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 py-4">
|
||||
{diagramHistory.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border rounded-md p-2 cursor-pointer hover:border-primary transition-colors"
|
||||
onClick={() =>
|
||||
onSelectHistoryItem(item.xml)
|
||||
}
|
||||
>
|
||||
<div className="aspect-video bg-white rounded overflow-hidden flex items-center justify-center">
|
||||
<Image
|
||||
src={item.svg}
|
||||
alt={`Diagram version ${
|
||||
index + 1
|
||||
}`}
|
||||
width={200}
|
||||
height={100}
|
||||
className="object-cover w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs text-center mt-1 text-gray-500">
|
||||
Version {index + 1}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShowHistory(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{/* History Button */}
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => setShowHistory(true)}
|
||||
disabled={
|
||||
status === "streaming" ||
|
||||
diagramHistory.length === 0
|
||||
}
|
||||
title="Diagram History"
|
||||
>
|
||||
<History className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
View diagram history
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
|
||||
@@ -19,17 +19,23 @@ import { convertToLegalXml } from "@/lib/utils";
|
||||
interface ChatPanelProps {
|
||||
onDisplayChart: (xml: string) => void;
|
||||
onFetchChart: () => Promise<string>;
|
||||
diagramHistory?: { svg: string; xml: string }[];
|
||||
onAddToHistory?: () => void;
|
||||
}
|
||||
|
||||
export default function ChatPanel({
|
||||
onDisplayChart,
|
||||
onFetchChart,
|
||||
diagramHistory = [],
|
||||
onAddToHistory = () => {},
|
||||
}: ChatPanelProps) {
|
||||
// Add a step counter to track updates
|
||||
const stepCounterRef = useRef<number>(0);
|
||||
// Add state for file attachments
|
||||
const [files, setFiles] = useState<FileList | undefined>(undefined);
|
||||
// Add state to control visibility of prompt examples panel
|
||||
// Add state for showing the history dialog
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
|
||||
// Remove the currentXmlRef and related useEffect
|
||||
const {
|
||||
@@ -93,6 +99,9 @@ export default function ChatPanel({
|
||||
experimental_attachments: files,
|
||||
});
|
||||
|
||||
// Add current diagram to history after submission
|
||||
onAddToHistory();
|
||||
|
||||
// Clear files after submission
|
||||
setFiles(undefined);
|
||||
} catch (error) {
|
||||
@@ -130,6 +139,12 @@ export default function ChatPanel({
|
||||
}
|
||||
};
|
||||
|
||||
// Function to handle history item selection
|
||||
const handleSelectHistoryItem = (xml: string) => {
|
||||
onDisplayChart(xml);
|
||||
setShowHistory(false);
|
||||
};
|
||||
|
||||
// Helper function to render tool invocations
|
||||
const renderToolInvocation = (toolInvocation: any) => {
|
||||
const callId = toolInvocation.toolCallId;
|
||||
@@ -356,6 +371,10 @@ export default function ChatPanel({
|
||||
onDisplayChart={onDisplayChart}
|
||||
files={files}
|
||||
onFileChange={handleFileChange}
|
||||
diagramHistory={diagramHistory}
|
||||
onSelectHistoryItem={handleSelectHistoryItem}
|
||||
showHistory={showHistory}
|
||||
setShowHistory={setShowHistory}
|
||||
/>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user