From 5fd7d4364fe18e4f1dc7b9cba90c81b59b16c776 Mon Sep 17 00:00:00 2001 From: "dayuan.jiang" Date: Sun, 4 Jan 2026 12:56:50 +0900 Subject: [PATCH] feat: show success toast after diagram download completes - Add optional successMessage parameter to saveDiagramToFile() - Show toast after file download is initiated, not when dialog opens - Add i18n strings for success message (en/ja/zh) This is the correct implementation for issue #479 - showing feedback after the actual save completes rather than when the save dialog opens. --- components/chat-input.tsx | 7 ++++++- contexts/diagram-context.tsx | 11 +++++++++++ lib/i18n/dictionaries/en.json | 3 ++- lib/i18n/dictionaries/ja.json | 3 ++- lib/i18n/dictionaries/zh.json | 3 ++- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/components/chat-input.tsx b/components/chat-input.tsx index 407e782..6a718a8 100644 --- a/components/chat-input.tsx +++ b/components/chat-input.tsx @@ -432,7 +432,12 @@ export function ChatInput({ open={showSaveDialog} onOpenChange={setShowSaveDialog} onSave={(filename, format) => - saveDiagramToFile(filename, format, sessionId) + saveDiagramToFile( + filename, + format, + sessionId, + dict.save.savedSuccessfully, + ) } defaultFilename={`diagram-${new Date() .toISOString() diff --git a/contexts/diagram-context.tsx b/contexts/diagram-context.tsx index dd3af3b..c530de5 100644 --- a/contexts/diagram-context.tsx +++ b/contexts/diagram-context.tsx @@ -3,6 +3,7 @@ import type React from "react" import { createContext, useContext, useEffect, useRef, useState } from "react" import type { DrawIoEmbedRef } from "react-drawio" +import { toast } from "sonner" import type { ExportFormat } from "@/components/save-dialog" import { getApiEndpoint } from "@/lib/base-path" import { @@ -27,6 +28,7 @@ interface DiagramContextType { filename: string, format: ExportFormat, sessionId?: string, + successMessage?: string, ) => void getThumbnailSvg: () => Promise isDrawioReady: boolean @@ -236,6 +238,7 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { filename: string, format: ExportFormat, sessionId?: string, + successMessage?: string, ) => { if (!drawioRef.current) { console.warn("Draw.io editor not ready") @@ -297,6 +300,14 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) { a.click() document.body.removeChild(a) + // Show success toast after download is initiated + if (successMessage) { + toast.success(successMessage, { + position: "bottom-left", + duration: 2500, + }) + } + // Delay URL revocation to ensure download completes if (!url.startsWith("data:")) { setTimeout(() => URL.revokeObjectURL(url), 100) diff --git a/lib/i18n/dictionaries/en.json b/lib/i18n/dictionaries/en.json index 8583de2..dba4a9c 100644 --- a/lib/i18n/dictionaries/en.json +++ b/lib/i18n/dictionaries/en.json @@ -117,7 +117,8 @@ "drawio": "Draw.io XML", "png": "PNG Image", "svg": "SVG Image" - } + }, + "savedSuccessfully": "Saved successfully!" }, "history": { "title": "Diagram History", diff --git a/lib/i18n/dictionaries/ja.json b/lib/i18n/dictionaries/ja.json index a001fd5..0a6cd0e 100644 --- a/lib/i18n/dictionaries/ja.json +++ b/lib/i18n/dictionaries/ja.json @@ -117,7 +117,8 @@ "drawio": "Draw.io XML", "png": "PNG 画像", "svg": "SVG 画像" - } + }, + "savedSuccessfully": "保存完了!" }, "history": { "title": "ダイアグラム履歴", diff --git a/lib/i18n/dictionaries/zh.json b/lib/i18n/dictionaries/zh.json index 1d66bb6..9fca336 100644 --- a/lib/i18n/dictionaries/zh.json +++ b/lib/i18n/dictionaries/zh.json @@ -117,7 +117,8 @@ "drawio": "Draw.io XML", "png": "PNG 图片", "svg": "SVG 图片" - } + }, + "savedSuccessfully": "保存成功!" }, "history": { "title": "图表历史",