diff --git a/components/chat-input.tsx b/components/chat-input.tsx index e59dc32..dec7238 100644 --- a/components/chat-input.tsx +++ b/components/chat-input.tsx @@ -4,12 +4,14 @@ import React, { useCallback, useRef, useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { ResetWarningModal } from "@/components/reset-warning-modal"; +import { SaveDialog } from "@/components/save-dialog"; import { Loader2, Send, RotateCcw, Image as ImageIcon, History, + Download, } from "lucide-react"; import { ButtonWithTooltip } from "@/components/button-with-tooltip"; import { FilePreviewList } from "./file-preview-list"; @@ -39,11 +41,12 @@ export function ChatInput({ showHistory = false, onToggleHistory = () => {}, }: ChatInputProps) { - const { diagramHistory } = useDiagram(); + const { diagramHistory, saveDiagramToFile } = useDiagram(); const textareaRef = useRef(null); const fileInputRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [showClearDialog, setShowClearDialog] = useState(false); + const [showSaveDialog, setShowSaveDialog] = useState(false); // Debug: Log status changes const isDisabled = status === "streaming" || status === "submitted"; @@ -166,6 +169,7 @@ export function ChatInput({ setShowClearDialog(false); }; + return (
+ {/* Save Diagram Button */} + setShowSaveDialog(true)} + disabled={isDisabled} + tooltipContent="Save diagram to local file" + > + + + + + + + + + + ); +} diff --git a/contexts/diagram-context.tsx b/contexts/diagram-context.tsx index 46edfb8..d8a3362 100644 --- a/contexts/diagram-context.tsx +++ b/contexts/diagram-context.tsx @@ -14,6 +14,7 @@ interface DiagramContextType { drawioRef: React.Ref; handleDiagramExport: (data: any) => void; clearDiagram: () => void; + saveDiagramToFile: (filename: string) => void; } const DiagramContext = createContext(undefined); @@ -28,6 +29,8 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { const resolverRef = useRef<((value: string) => void) | null>(null); // Track if we're expecting an export for history (user-initiated) const expectHistoryExportRef = useRef(false); + // Track if we're expecting an export for file save + const saveResolverRef = useRef<((xml: string) => void) | null>(null); const handleExport = () => { if (drawioRef.current) { @@ -68,6 +71,12 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { resolverRef.current(extractedXML); resolverRef.current = null; } + + // Handle save to file if requested + if (saveResolverRef.current) { + saveResolverRef.current(extractedXML); + saveResolverRef.current = null; + } }; const clearDiagram = () => { @@ -78,6 +87,35 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { setDiagramHistory([]); }; + const saveDiagramToFile = (filename: string) => { + if (!drawioRef.current) { + console.warn("Draw.io editor not ready"); + return; + } + + // Export diagram and save when export completes + drawioRef.current.exportDiagram({ format: "xmlsvg" }); + saveResolverRef.current = (xml: string) => { + // Wrap in proper .drawio format + let fileContent = xml; + if (!xml.includes("${xml}`; + } + + const blob = new Blob([fileContent], { type: "application/xml" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + // Add .drawio extension if not present + a.download = filename.endsWith(".drawio") ? filename : `${filename}.drawio`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + // Delay URL revocation to ensure download completes + setTimeout(() => URL.revokeObjectURL(url), 100); + }; + }; + return ( {children}