mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +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>("");
|
const [chartXML, setChartXML] = useState<string>("");
|
||||||
// Add a ref to store the resolver function
|
// Add a ref to store the resolver function
|
||||||
const resolverRef = useRef<((value: string) => void) | null>(null);
|
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 = () => {
|
const handleExport = () => {
|
||||||
if (drawioRef.current) {
|
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 (
|
return (
|
||||||
<div className="flex h-screen bg-gray-100">
|
<div className="flex h-screen bg-gray-100">
|
||||||
<div className="w-2/3 p-1">
|
<div className="w-2/3 p-1">
|
||||||
@@ -36,6 +52,8 @@ export default function Home() {
|
|||||||
onExport={(data) => {
|
onExport={(data) => {
|
||||||
const extractedXML = extractDiagramXML(data.data);
|
const extractedXML = extractDiagramXML(data.data);
|
||||||
setChartXML(extractedXML);
|
setChartXML(extractedXML);
|
||||||
|
// Store the latest SVG data
|
||||||
|
setLatestSvg(data.data);
|
||||||
// If there's a pending resolver, resolve it with the fresh XML
|
// If there's a pending resolver, resolve it with the fresh XML
|
||||||
if (resolverRef.current) {
|
if (resolverRef.current) {
|
||||||
resolverRef.current(extractedXML);
|
resolverRef.current(extractedXML);
|
||||||
@@ -61,6 +79,8 @@ export default function Home() {
|
|||||||
handleExport();
|
handleExport();
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
diagramHistory={diagramHistory}
|
||||||
|
onAddToHistory={addToHistory}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,14 @@
|
|||||||
import React, { useCallback, useRef, useEffect, useState } from "react";
|
import React, { useCallback, useRef, useEffect, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
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 {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -30,6 +37,10 @@ interface ChatInputProps {
|
|||||||
onDisplayChart: (xml: string) => void;
|
onDisplayChart: (xml: string) => void;
|
||||||
files?: FileList;
|
files?: FileList;
|
||||||
onFileChange?: (files: FileList | undefined) => void;
|
onFileChange?: (files: FileList | undefined) => void;
|
||||||
|
diagramHistory?: { svg: string; xml: string }[];
|
||||||
|
onSelectHistoryItem?: (xml: string) => void;
|
||||||
|
showHistory?: boolean;
|
||||||
|
setShowHistory?: (show: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatInput({
|
export function ChatInput({
|
||||||
@@ -41,6 +52,10 @@ export function ChatInput({
|
|||||||
onDisplayChart,
|
onDisplayChart,
|
||||||
files,
|
files,
|
||||||
onFileChange,
|
onFileChange,
|
||||||
|
diagramHistory = [],
|
||||||
|
onSelectHistoryItem = () => {},
|
||||||
|
showHistory = false,
|
||||||
|
setShowHistory = () => {},
|
||||||
}: ChatInputProps) {
|
}: ChatInputProps) {
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -250,8 +265,87 @@ export function ChatInput({
|
|||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</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>
|
||||||
<div className="flex gap-2">
|
<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
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -19,17 +19,23 @@ import { convertToLegalXml } from "@/lib/utils";
|
|||||||
interface ChatPanelProps {
|
interface ChatPanelProps {
|
||||||
onDisplayChart: (xml: string) => void;
|
onDisplayChart: (xml: string) => void;
|
||||||
onFetchChart: () => Promise<string>;
|
onFetchChart: () => Promise<string>;
|
||||||
|
diagramHistory?: { svg: string; xml: string }[];
|
||||||
|
onAddToHistory?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ChatPanel({
|
export default function ChatPanel({
|
||||||
onDisplayChart,
|
onDisplayChart,
|
||||||
onFetchChart,
|
onFetchChart,
|
||||||
|
diagramHistory = [],
|
||||||
|
onAddToHistory = () => {},
|
||||||
}: ChatPanelProps) {
|
}: ChatPanelProps) {
|
||||||
// Add a step counter to track updates
|
// Add a step counter to track updates
|
||||||
const stepCounterRef = useRef<number>(0);
|
const stepCounterRef = useRef<number>(0);
|
||||||
// Add state for file attachments
|
// Add state for file attachments
|
||||||
const [files, setFiles] = useState<FileList | undefined>(undefined);
|
const [files, setFiles] = useState<FileList | undefined>(undefined);
|
||||||
// Add state to control visibility of prompt examples panel
|
// 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
|
// Remove the currentXmlRef and related useEffect
|
||||||
const {
|
const {
|
||||||
@@ -93,6 +99,9 @@ export default function ChatPanel({
|
|||||||
experimental_attachments: files,
|
experimental_attachments: files,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add current diagram to history after submission
|
||||||
|
onAddToHistory();
|
||||||
|
|
||||||
// Clear files after submission
|
// Clear files after submission
|
||||||
setFiles(undefined);
|
setFiles(undefined);
|
||||||
} catch (error) {
|
} 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
|
// Helper function to render tool invocations
|
||||||
const renderToolInvocation = (toolInvocation: any) => {
|
const renderToolInvocation = (toolInvocation: any) => {
|
||||||
const callId = toolInvocation.toolCallId;
|
const callId = toolInvocation.toolCallId;
|
||||||
@@ -356,6 +371,10 @@ export default function ChatPanel({
|
|||||||
onDisplayChart={onDisplayChart}
|
onDisplayChart={onDisplayChart}
|
||||||
files={files}
|
files={files}
|
||||||
onFileChange={handleFileChange}
|
onFileChange={handleFileChange}
|
||||||
|
diagramHistory={diagramHistory}
|
||||||
|
onSelectHistoryItem={handleSelectHistoryItem}
|
||||||
|
showHistory={showHistory}
|
||||||
|
setShowHistory={setShowHistory}
|
||||||
/>
|
/>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user