feat: implement diagram history functionality with history dialog in ChatInput and ChatPanel

This commit is contained in:
dayuan.jiang
2025-03-23 13:54:21 +00:00
parent 1f69cf590b
commit 6819a92921
3 changed files with 134 additions and 1 deletions

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>