feat: add save diagram to local file button (#60)

- Add save button in chat input area with download icon
- Create SaveDialog component for filename input
- Export current diagram as .drawio file format
- Support custom filename with default timestamp-based name

Closes #53
This commit is contained in:
Dayuan Jiang
2025-12-03 21:02:26 +09:00
committed by GitHub
parent a61d37c818
commit 45f74df349
3 changed files with 143 additions and 1 deletions

View File

@@ -14,6 +14,7 @@ interface DiagramContextType {
drawioRef: React.Ref<DrawIoEmbedRef | null>;
handleDiagramExport: (data: any) => void;
clearDiagram: () => void;
saveDiagramToFile: (filename: string) => void;
}
const DiagramContext = createContext<DiagramContextType | undefined>(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<boolean>(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("<mxfile")) {
fileContent = `<mxfile><diagram name="Page-1" id="page-1">${xml}</diagram></mxfile>`;
}
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 (
<DiagramContext.Provider
value={{
@@ -90,6 +128,7 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
drawioRef,
handleDiagramExport,
clearDiagram,
saveDiagramToFile,
}}
>
{children}