mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
add mermaid buttons (#4547)
* add mermaid buttons * feat: Add Modal, TabButton, and ZoomControls components * feat: Add error handling messages for image operations and file opening * mermaid: Add drag functionality and support contious zooming * add active color for tabbutton * refactor zoom controls * refactor: Remove unused svgToPng prop and simplify handleCopy function * Move zoom to constants and increase max zoom * feat: add save image functionality and refactor image handling * feat: add translations --------- Co-authored-by: Daniel Riccio <ricciodaniel98@gmail.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
This commit is contained in:
parent
85fd86e675
commit
dfcf8fe760
45 changed files with 1360 additions and 59 deletions
|
|
@ -17,7 +17,8 @@ import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage
|
|||
import { checkExistKey } from "../../shared/checkExistApiConfig"
|
||||
import { experimentDefault } from "../../shared/experiments"
|
||||
import { Terminal } from "../../integrations/terminal/Terminal"
|
||||
import { openFile, openImage } from "../../integrations/misc/open-file"
|
||||
import { openFile } from "../../integrations/misc/open-file"
|
||||
import { openImage, saveImage } from "../../integrations/misc/image-handler"
|
||||
import { selectImages } from "../../integrations/misc/process-images"
|
||||
import { getTheme } from "../../integrations/theme/getTheme"
|
||||
import { discoverChromeHostUrl, tryChromeHostUrl } from "../../services/browser/browserDiscovery"
|
||||
|
|
@ -423,7 +424,10 @@ export const webviewMessageHandler = async (
|
|||
provider.postMessageToWebview({ type: "vsCodeLmModels", vsCodeLmModels })
|
||||
break
|
||||
case "openImage":
|
||||
openImage(message.text!)
|
||||
openImage(message.text!, { values: message.values })
|
||||
break
|
||||
case "saveImage":
|
||||
saveImage(message.dataUri!)
|
||||
break
|
||||
case "openFile":
|
||||
openFile(message.text!, message.values as { create?: boolean; content?: string; line?: number })
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Format d'URI de dades no vàlid",
|
||||
"error_copying_image": "Error copiant la imatge: {{errorMessage}}",
|
||||
"error_saving_image": "Error desant la imatge: {{errorMessage}}",
|
||||
"error_opening_image": "Error obrint la imatge: {{error}}",
|
||||
"could_not_open_file": "No s'ha pogut obrir el fitxer: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "No s'ha pogut obrir el fitxer!",
|
||||
"checkpoint_timeout": "S'ha esgotat el temps en intentar restaurar el punt de control.",
|
||||
"checkpoint_failed": "Ha fallat la restauració del punt de control.",
|
||||
"no_workspace": "Si us plau, obre primer una carpeta de projecte",
|
||||
|
|
@ -71,7 +76,9 @@
|
|||
"custom_storage_path_set": "Ruta d'emmagatzematge personalitzada establerta: {{path}}",
|
||||
"default_storage_path": "S'ha reprès l'ús de la ruta d'emmagatzematge predeterminada",
|
||||
"settings_imported": "Configuració importada correctament.",
|
||||
"share_link_copied": "Enllaç de compartició copiat al portapapers"
|
||||
"share_link_copied": "Enllaç de compartició copiat al portapapers",
|
||||
"image_copied_to_clipboard": "URI de dades de la imatge copiada al portapapers",
|
||||
"image_saved": "Imatge desada a {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Sí",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Ungültiges Daten-URI-Format",
|
||||
"error_copying_image": "Fehler beim Kopieren des Bildes: {{errorMessage}}",
|
||||
"error_saving_image": "Fehler beim Speichern des Bildes: {{errorMessage}}",
|
||||
"error_opening_image": "Fehler beim Öffnen des Bildes: {{error}}",
|
||||
"could_not_open_file": "Datei konnte nicht geöffnet werden: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Datei konnte nicht geöffnet werden!",
|
||||
"checkpoint_timeout": "Zeitüberschreitung beim Versuch, den Checkpoint wiederherzustellen.",
|
||||
"checkpoint_failed": "Fehler beim Wiederherstellen des Checkpoints.",
|
||||
"no_workspace": "Bitte öffne zuerst einen Projektordner",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Benutzerdefinierter Speicherpfad festgelegt: {{path}}",
|
||||
"default_storage_path": "Auf Standardspeicherpfad zurückgesetzt",
|
||||
"settings_imported": "Einstellungen erfolgreich importiert.",
|
||||
"share_link_copied": "Share-Link in die Zwischenablage kopiert"
|
||||
"share_link_copied": "Share-Link in die Zwischenablage kopiert",
|
||||
"image_copied_to_clipboard": "Bild-Daten-URI in die Zwischenablage kopiert",
|
||||
"image_saved": "Bild gespeichert unter {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Ja",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Invalid data URI format",
|
||||
"error_copying_image": "Error copying image: {{errorMessage}}",
|
||||
"error_opening_image": "Error opening image: {{error}}",
|
||||
"error_saving_image": "Error saving image: {{errorMessage}}",
|
||||
"could_not_open_file": "Could not open file: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Could not open file!",
|
||||
"checkpoint_timeout": "Timed out when attempting to restore checkpoint.",
|
||||
"checkpoint_failed": "Failed to restore checkpoint.",
|
||||
"no_workspace": "Please open a project folder first",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Custom storage path set: {{path}}",
|
||||
"default_storage_path": "Reverted to using default storage path",
|
||||
"settings_imported": "Settings imported successfully.",
|
||||
"share_link_copied": "Share link copied to clipboard"
|
||||
"share_link_copied": "Share link copied to clipboard",
|
||||
"image_copied_to_clipboard": "Image data URI copied to clipboard",
|
||||
"image_saved": "Image saved to {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Yes",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Formato de URI de datos no válido",
|
||||
"error_copying_image": "Error copiando la imagen: {{errorMessage}}",
|
||||
"error_saving_image": "Error guardando la imagen: {{errorMessage}}",
|
||||
"error_opening_image": "Error abriendo la imagen: {{error}}",
|
||||
"could_not_open_file": "No se pudo abrir el archivo: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "¡No se pudo abrir el archivo!",
|
||||
"checkpoint_timeout": "Se agotó el tiempo al intentar restaurar el punto de control.",
|
||||
"checkpoint_failed": "Error al restaurar el punto de control.",
|
||||
"no_workspace": "Por favor, abre primero una carpeta de proyecto",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Ruta de almacenamiento personalizada establecida: {{path}}",
|
||||
"default_storage_path": "Se ha vuelto a usar la ruta de almacenamiento predeterminada",
|
||||
"settings_imported": "Configuración importada correctamente.",
|
||||
"share_link_copied": "Enlace de compartir copiado al portapapeles"
|
||||
"share_link_copied": "Enlace de compartir copiado al portapapeles",
|
||||
"image_copied_to_clipboard": "URI de datos de imagen copiada al portapapeles",
|
||||
"image_saved": "Imagen guardada en {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Sí",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Format d'URI de données invalide",
|
||||
"error_copying_image": "Erreur lors de la copie de l'image : {{errorMessage}}",
|
||||
"error_saving_image": "Erreur lors de l'enregistrement de l'image : {{errorMessage}}",
|
||||
"error_opening_image": "Erreur lors de l'ouverture de l'image : {{error}}",
|
||||
"could_not_open_file": "Impossible d'ouvrir le fichier : {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Impossible d'ouvrir le fichier !",
|
||||
"checkpoint_timeout": "Expiration du délai lors de la tentative de rétablissement du checkpoint.",
|
||||
"checkpoint_failed": "Échec du rétablissement du checkpoint.",
|
||||
"no_workspace": "Veuillez d'abord ouvrir un espace de travail",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Chemin de stockage personnalisé défini : {{path}}",
|
||||
"default_storage_path": "Retour au chemin de stockage par défaut",
|
||||
"settings_imported": "Paramètres importés avec succès.",
|
||||
"share_link_copied": "Lien de partage copié dans le presse-papiers"
|
||||
"share_link_copied": "Lien de partage copié dans le presse-papiers",
|
||||
"image_copied_to_clipboard": "URI de données d'image copiée dans le presse-papiers",
|
||||
"image_saved": "Image enregistrée dans {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Oui",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "अमान्य डेटा URI फॉर्मेट",
|
||||
"error_copying_image": "छवि कॉपी करने में त्रुटि: {{errorMessage}}",
|
||||
"error_saving_image": "छवि सहेजने में त्रुटि: {{errorMessage}}",
|
||||
"error_opening_image": "छवि खोलने में त्रुटि: {{error}}",
|
||||
"could_not_open_file": "फ़ाइल नहीं खोली जा सकी: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "फ़ाइल नहीं खोली जा सकी!",
|
||||
"checkpoint_timeout": "चेकपॉइंट को पुनर्स्थापित करने का प्रयास करते समय टाइमआउट हो गया।",
|
||||
"checkpoint_failed": "चेकपॉइंट पुनर्स्थापित करने में विफल।",
|
||||
"no_workspace": "कृपया पहले प्रोजेक्ट फ़ोल्डर खोलें",
|
||||
|
|
@ -66,8 +71,10 @@
|
|||
"history_cleanup": "इतिहास से गायब फाइलों वाले {{count}} टास्क साफ किए गए।",
|
||||
"custom_storage_path_set": "कस्टम स्टोरेज पाथ सेट किया गया: {{path}}",
|
||||
"default_storage_path": "डिफ़ॉल्ट स्टोरेज पाथ का उपयोग पुनः शुरू किया गया",
|
||||
"settings_imported": "सेटिंग्स सफलतापूर्वक इम्पोर्ट की गईं.",
|
||||
"share_link_copied": "साझा लिंक क्लिपबोर्ड पर कॉपी किया गया"
|
||||
"settings_imported": "सेटिंग्स सफलतापूर्वक इम्पोर्ट की गईं।",
|
||||
"share_link_copied": "साझा लिंक क्लिपबोर्ड पर कॉपी किया गया",
|
||||
"image_copied_to_clipboard": "छवि डेटा URI क्लिपबोर्ड में कॉपी की गई",
|
||||
"image_saved": "छवि {{path}} में सहेजी गई"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "हां",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Formato URI dati non valido",
|
||||
"error_copying_image": "Errore durante la copia dell'immagine: {{errorMessage}}",
|
||||
"error_saving_image": "Errore durante il salvataggio dell'immagine: {{errorMessage}}",
|
||||
"error_opening_image": "Errore durante l'apertura dell'immagine: {{error}}",
|
||||
"could_not_open_file": "Impossibile aprire il file: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Impossibile aprire il file!",
|
||||
"checkpoint_timeout": "Timeout durante il tentativo di ripristinare il checkpoint.",
|
||||
"checkpoint_failed": "Impossibile ripristinare il checkpoint.",
|
||||
"no_workspace": "Per favore, apri prima una cartella di progetto",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Percorso di archiviazione personalizzato impostato: {{path}}",
|
||||
"default_storage_path": "Tornato al percorso di archiviazione predefinito",
|
||||
"settings_imported": "Impostazioni importate con successo.",
|
||||
"share_link_copied": "Link di condivisione copiato negli appunti"
|
||||
"share_link_copied": "Link di condivisione copiato negli appunti",
|
||||
"image_copied_to_clipboard": "URI dati dell'immagine copiato negli appunti",
|
||||
"image_saved": "Immagine salvata in {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Sì",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "データURIフォーマットが無効です",
|
||||
"error_copying_image": "画像のコピー中にエラーが発生しました:{{errorMessage}}",
|
||||
"error_saving_image": "画像の保存中にエラーが発生しました:{{errorMessage}}",
|
||||
"error_opening_image": "画像を開く際にエラーが発生しました:{{error}}",
|
||||
"could_not_open_file": "ファイルを開けませんでした:{{errorMessage}}",
|
||||
"could_not_open_file_generic": "ファイルを開けませんでした!",
|
||||
"checkpoint_timeout": "チェックポイントの復元を試みる際にタイムアウトしました。",
|
||||
"checkpoint_failed": "チェックポイントの復元に失敗しました。",
|
||||
"no_workspace": "まずプロジェクトフォルダを開いてください",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "カスタムストレージパスが設定されました:{{path}}",
|
||||
"default_storage_path": "デフォルトのストレージパスに戻りました",
|
||||
"settings_imported": "設定が正常にインポートされました。",
|
||||
"share_link_copied": "共有リンクがクリップボードにコピーされました"
|
||||
"share_link_copied": "共有リンクがクリップボードにコピーされました",
|
||||
"image_copied_to_clipboard": "画像データURIがクリップボードにコピーされました",
|
||||
"image_saved": "画像を{{path}}に保存しました"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "はい",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "잘못된 데이터 URI 형식",
|
||||
"error_copying_image": "이미지 복사 중 오류 발생: {{errorMessage}}",
|
||||
"error_saving_image": "이미지 저장 중 오류 발생: {{errorMessage}}",
|
||||
"error_opening_image": "이미지 열기 중 오류 발생: {{error}}",
|
||||
"could_not_open_file": "파일을 열 수 없습니다: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "파일을 열 수 없습니다!",
|
||||
"checkpoint_timeout": "체크포인트 복원을 시도하는 중 시간 초과되었습니다.",
|
||||
"checkpoint_failed": "체크포인트 복원에 실패했습니다.",
|
||||
"no_workspace": "먼저 프로젝트 폴더를 열어주세요",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "사용자 지정 저장 경로 설정됨: {{path}}",
|
||||
"default_storage_path": "기본 저장 경로로 되돌아갔습니다",
|
||||
"settings_imported": "설정이 성공적으로 가져와졌습니다.",
|
||||
"share_link_copied": "공유 링크가 클립보드에 복사되었습니다"
|
||||
"share_link_copied": "공유 링크가 클립보드에 복사되었습니다",
|
||||
"image_copied_to_clipboard": "이미지 데이터 URI가 클립보드에 복사되었습니다",
|
||||
"image_saved": "이미지가 {{path}}에 저장되었습니다"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "예",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Ongeldig data-URI-formaat",
|
||||
"error_copying_image": "Fout bij kopiëren van afbeelding: {{errorMessage}}",
|
||||
"error_saving_image": "Fout bij opslaan van afbeelding: {{errorMessage}}",
|
||||
"error_opening_image": "Fout bij openen van afbeelding: {{error}}",
|
||||
"could_not_open_file": "Kon bestand niet openen: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Kon bestand niet openen!",
|
||||
"checkpoint_timeout": "Time-out bij het herstellen van checkpoint.",
|
||||
"checkpoint_failed": "Herstellen van checkpoint mislukt.",
|
||||
"no_workspace": "Open eerst een projectmap",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Aangepast opslagpad ingesteld: {{path}}",
|
||||
"default_storage_path": "Terug naar standaard opslagpad",
|
||||
"settings_imported": "Instellingen succesvol geïmporteerd.",
|
||||
"share_link_copied": "Deellink gekopieerd naar klembord"
|
||||
"share_link_copied": "Deellink gekopieerd naar klembord",
|
||||
"image_copied_to_clipboard": "Afbeelding data-URI gekopieerd naar klembord",
|
||||
"image_saved": "Afbeelding opgeslagen naar {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Ja",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Nieprawidłowy format URI danych",
|
||||
"error_copying_image": "Błąd kopiowania obrazu: {{errorMessage}}",
|
||||
"error_saving_image": "Błąd zapisywania obrazu: {{errorMessage}}",
|
||||
"error_opening_image": "Błąd otwierania obrazu: {{error}}",
|
||||
"could_not_open_file": "Nie można otworzyć pliku: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Nie można otworzyć pliku!",
|
||||
"checkpoint_timeout": "Upłynął limit czasu podczas próby przywrócenia punktu kontrolnego.",
|
||||
"checkpoint_failed": "Nie udało się przywrócić punktu kontrolnego.",
|
||||
"no_workspace": "Najpierw otwórz folder projektu",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Ustawiono niestandardową ścieżkę przechowywania: {{path}}",
|
||||
"default_storage_path": "Wznowiono używanie domyślnej ścieżki przechowywania",
|
||||
"settings_imported": "Ustawienia zaimportowane pomyślnie.",
|
||||
"share_link_copied": "Link udostępniania skopiowany do schowka"
|
||||
"share_link_copied": "Link udostępniania skopiowany do schowka",
|
||||
"image_copied_to_clipboard": "URI danych obrazu skopiowane do schowka",
|
||||
"image_saved": "Obraz zapisany w {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Tak",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Formato de URI de dados inválido",
|
||||
"error_copying_image": "Erro ao copiar imagem: {{errorMessage}}",
|
||||
"error_saving_image": "Erro ao salvar imagem: {{errorMessage}}",
|
||||
"error_opening_image": "Erro ao abrir imagem: {{error}}",
|
||||
"could_not_open_file": "Não foi possível abrir o arquivo: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Não foi possível abrir o arquivo!",
|
||||
"checkpoint_timeout": "Tempo esgotado ao tentar restaurar o ponto de verificação.",
|
||||
"checkpoint_failed": "Falha ao restaurar o ponto de verificação.",
|
||||
"no_workspace": "Por favor, abra primeiro uma pasta de projeto",
|
||||
|
|
@ -71,7 +76,9 @@
|
|||
"custom_storage_path_set": "Caminho de armazenamento personalizado definido: {{path}}",
|
||||
"default_storage_path": "Retornado ao caminho de armazenamento padrão",
|
||||
"settings_imported": "Configurações importadas com sucesso.",
|
||||
"share_link_copied": "Link de compartilhamento copiado para a área de transferência"
|
||||
"share_link_copied": "Link de compartilhamento copiado para a área de transferência",
|
||||
"image_copied_to_clipboard": "URI de dados da imagem copiada para a área de transferência",
|
||||
"image_saved": "Imagem salva em {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Sim",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Неверный формат URI данных",
|
||||
"error_copying_image": "Ошибка копирования изображения: {{errorMessage}}",
|
||||
"error_saving_image": "Ошибка сохранения изображения: {{errorMessage}}",
|
||||
"error_opening_image": "Ошибка открытия изображения: {{error}}",
|
||||
"could_not_open_file": "Не удалось открыть файл: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Не удалось открыть файл!",
|
||||
"checkpoint_timeout": "Превышено время ожидания при попытке восстановления контрольной точки.",
|
||||
"checkpoint_failed": "Не удалось восстановить контрольную точку.",
|
||||
"no_workspace": "Пожалуйста, сначала откройте папку проекта",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Установлен пользовательский путь хранения: {{path}}",
|
||||
"default_storage_path": "Возвращено использование пути хранения по умолчанию",
|
||||
"settings_imported": "Настройки успешно импортированы.",
|
||||
"share_link_copied": "Ссылка для совместного использования скопирована в буфер обмена"
|
||||
"share_link_copied": "Ссылка для совместного использования скопирована в буфер обмена",
|
||||
"image_copied_to_clipboard": "URI данных изображения скопирован в буфер обмена",
|
||||
"image_saved": "Изображение сохранено в {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Да",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Geçersiz veri URI formatı",
|
||||
"error_copying_image": "Resim kopyalanırken hata oluştu: {{errorMessage}}",
|
||||
"error_saving_image": "Resim kaydedilirken hata oluştu: {{errorMessage}}",
|
||||
"error_opening_image": "Resim açılırken hata oluştu: {{error}}",
|
||||
"could_not_open_file": "Dosya açılamadı: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Dosya açılamadı!",
|
||||
"checkpoint_timeout": "Kontrol noktasını geri yüklemeye çalışırken zaman aşımına uğradı.",
|
||||
"checkpoint_failed": "Kontrol noktası geri yüklenemedi.",
|
||||
"no_workspace": "Lütfen önce bir proje klasörü açın",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Özel depolama yolu ayarlandı: {{path}}",
|
||||
"default_storage_path": "Varsayılan depolama yoluna geri dönüldü",
|
||||
"settings_imported": "Ayarlar başarıyla içe aktarıldı.",
|
||||
"share_link_copied": "Paylaşım bağlantısı panoya kopyalandı"
|
||||
"share_link_copied": "Paylaşım bağlantısı panoya kopyalandı",
|
||||
"image_copied_to_clipboard": "Resim veri URI'si panoya kopyalandı",
|
||||
"image_saved": "Resim {{path}} konumuna kaydedildi"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Evet",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Định dạng URI dữ liệu không hợp lệ",
|
||||
"error_copying_image": "Lỗi khi sao chép hình ảnh: {{errorMessage}}",
|
||||
"error_saving_image": "Lỗi khi lưu hình ảnh: {{errorMessage}}",
|
||||
"error_opening_image": "Lỗi khi mở hình ảnh: {{error}}",
|
||||
"could_not_open_file": "Không thể mở tệp: {{errorMessage}}",
|
||||
"could_not_open_file_generic": "Không thể mở tệp!",
|
||||
"checkpoint_timeout": "Đã hết thời gian khi cố gắng khôi phục điểm kiểm tra.",
|
||||
"checkpoint_failed": "Không thể khôi phục điểm kiểm tra.",
|
||||
"no_workspace": "Vui lòng mở thư mục dự án trước",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "Đã thiết lập đường dẫn lưu trữ tùy chỉnh: {{path}}",
|
||||
"default_storage_path": "Đã quay lại sử dụng đường dẫn lưu trữ mặc định",
|
||||
"settings_imported": "Cài đặt đã được nhập thành công.",
|
||||
"share_link_copied": "Liên kết chia sẻ đã được sao chép vào clipboard"
|
||||
"share_link_copied": "Liên kết chia sẻ đã được sao chép vào clipboard",
|
||||
"image_copied_to_clipboard": "URI dữ liệu hình ảnh đã được sao chép vào clipboard",
|
||||
"image_saved": "Hình ảnh đã được lưu vào {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "Có",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,17 @@
|
|||
"this_and_subsequent": "此消息及所有后续消息"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "数据URI格式无效",
|
||||
"invalid_mcp_config": "项目MCP配置格式无效",
|
||||
"invalid_mcp_settings_format": "MCP设置JSON格式无效。请确保您的设置遵循正确的JSON格式。",
|
||||
"invalid_mcp_settings_syntax": "MCP设置JSON格式无效。请检查您的设置文件是否有语法错误。",
|
||||
"invalid_mcp_settings_validation": "MCP设置格式无效:{{errorMessages}}",
|
||||
"failed_initialize_project_mcp": "初始化项目MCP服务器失败:{{error}}",
|
||||
"invalid_data_uri": "数据 URI 格式无效",
|
||||
"error_copying_image": "复制图片时出错:{{errorMessage}}",
|
||||
"error_saving_image": "保存图片时出错:{{errorMessage}}",
|
||||
"error_opening_image": "打开图片时出错:{{error}}",
|
||||
"could_not_open_file": "无法打开文件:{{errorMessage}}",
|
||||
"could_not_open_file_generic": "无法打开文件!",
|
||||
"checkpoint_timeout": "尝试恢复检查点时超时。",
|
||||
"checkpoint_failed": "恢复检查点失败。",
|
||||
"no_workspace": "请先打开项目文件夹",
|
||||
|
|
@ -67,7 +77,9 @@
|
|||
"custom_storage_path_set": "自定义存储路径已设置:{{path}}",
|
||||
"default_storage_path": "已恢复使用默认存储路径",
|
||||
"settings_imported": "设置已成功导入。",
|
||||
"share_link_copied": "分享链接已复制到剪贴板"
|
||||
"share_link_copied": "分享链接已复制到剪贴板",
|
||||
"image_copied_to_clipboard": "图片数据 URI 已复制到剪贴板",
|
||||
"image_saved": "图片已保存到 {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "是",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "資料 URI 格式無效",
|
||||
"error_copying_image": "複製圖片時發生錯誤:{{errorMessage}}",
|
||||
"error_saving_image": "儲存圖片時發生錯誤:{{errorMessage}}",
|
||||
"error_opening_image": "開啟圖片時發生錯誤:{{error}}",
|
||||
"could_not_open_file": "無法開啟檔案:{{errorMessage}}",
|
||||
"could_not_open_file_generic": "無法開啟檔案!",
|
||||
"checkpoint_timeout": "嘗試恢復檢查點時超時。",
|
||||
"checkpoint_failed": "恢復檢查點失敗。",
|
||||
"no_workspace": "請先開啟專案資料夾",
|
||||
|
|
@ -67,7 +72,9 @@
|
|||
"custom_storage_path_set": "自訂儲存路徑已設定:{{path}}",
|
||||
"default_storage_path": "已恢復使用預設儲存路徑",
|
||||
"settings_imported": "設定已成功匯入。",
|
||||
"share_link_copied": "分享連結已複製到剪貼簿"
|
||||
"share_link_copied": "分享連結已複製到剪貼簿",
|
||||
"image_copied_to_clipboard": "圖片資料 URI 已複製到剪貼簿",
|
||||
"image_saved": "圖片已儲存至 {{path}}"
|
||||
},
|
||||
"answers": {
|
||||
"yes": "是",
|
||||
|
|
|
|||
92
src/integrations/misc/image-handler.ts
Normal file
92
src/integrations/misc/image-handler.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import * as path from "path"
|
||||
import * as os from "os"
|
||||
import * as vscode from "vscode"
|
||||
import { getWorkspacePath } from "../../utils/path"
|
||||
import { t } from "../../i18n"
|
||||
|
||||
export async function openImage(dataUri: string, options?: { values?: { action?: string } }) {
|
||||
const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/)
|
||||
if (!matches) {
|
||||
vscode.window.showErrorMessage(t("common:errors.invalid_data_uri"))
|
||||
return
|
||||
}
|
||||
const [, format, base64Data] = matches
|
||||
const imageBuffer = Buffer.from(base64Data, "base64")
|
||||
|
||||
// Default behavior: open the image
|
||||
const tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)
|
||||
try {
|
||||
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)
|
||||
// Check if this is a copy action
|
||||
if (options?.values?.action === "copy") {
|
||||
try {
|
||||
// Read the image file
|
||||
const imageData = await vscode.workspace.fs.readFile(vscode.Uri.file(tempFilePath))
|
||||
|
||||
// Convert to base64 for clipboard
|
||||
const base64Image = Buffer.from(imageData).toString("base64")
|
||||
const dataUri = `data:image/${format};base64,${base64Image}`
|
||||
|
||||
// Use vscode.env.clipboard to copy the data URI
|
||||
// Note: VSCode doesn't support copying binary image data directly to clipboard
|
||||
// So we copy the data URI which can be pasted in many applications
|
||||
await vscode.env.clipboard.writeText(dataUri)
|
||||
|
||||
vscode.window.showInformationMessage(t("common:info.image_copied_to_clipboard"))
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
vscode.window.showErrorMessage(t("common:errors.error_copying_image", { errorMessage }))
|
||||
} finally {
|
||||
// Clean up temp file
|
||||
try {
|
||||
await vscode.workspace.fs.delete(vscode.Uri.file(tempFilePath))
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(tempFilePath))
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(t("common:errors.error_opening_image", { error }))
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveImage(dataUri: string) {
|
||||
const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/)
|
||||
if (!matches) {
|
||||
vscode.window.showErrorMessage(t("common:errors.invalid_data_uri"))
|
||||
return
|
||||
}
|
||||
const [, format, base64Data] = matches
|
||||
const imageBuffer = Buffer.from(base64Data, "base64")
|
||||
|
||||
// Get workspace path or fallback to home directory
|
||||
const workspacePath = getWorkspacePath()
|
||||
const defaultPath = workspacePath || os.homedir()
|
||||
const defaultFileName = `mermaid_diagram_${Date.now()}.${format}`
|
||||
const defaultUri = vscode.Uri.file(path.join(defaultPath, defaultFileName))
|
||||
|
||||
// Show save dialog
|
||||
const saveUri = await vscode.window.showSaveDialog({
|
||||
filters: {
|
||||
Images: [format],
|
||||
"All Files": ["*"],
|
||||
},
|
||||
defaultUri: defaultUri,
|
||||
})
|
||||
|
||||
if (!saveUri) {
|
||||
// User cancelled the save dialog
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Write the image to the selected location
|
||||
await vscode.workspace.fs.writeFile(saveUri, imageBuffer)
|
||||
vscode.window.showInformationMessage(t("common:info.image_saved", { path: saveUri.fsPath }))
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
vscode.window.showErrorMessage(t("common:errors.error_saving_image", { errorMessage }))
|
||||
}
|
||||
}
|
||||
|
|
@ -2,23 +2,7 @@ import * as path from "path"
|
|||
import * as os from "os"
|
||||
import * as vscode from "vscode"
|
||||
import { arePathsEqual, getWorkspacePath } from "../../utils/path"
|
||||
|
||||
export async function openImage(dataUri: string) {
|
||||
const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/)
|
||||
if (!matches) {
|
||||
vscode.window.showErrorMessage("Invalid data URI format")
|
||||
return
|
||||
}
|
||||
const [, format, base64Data] = matches
|
||||
const imageBuffer = Buffer.from(base64Data, "base64")
|
||||
const tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)
|
||||
try {
|
||||
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)
|
||||
await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(tempFilePath))
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(`Error opening image: ${error}`)
|
||||
}
|
||||
}
|
||||
import { t } from "../../i18n"
|
||||
|
||||
interface OpenFileOptions {
|
||||
create?: boolean
|
||||
|
|
@ -151,9 +135,9 @@ export async function openFile(filePath: string, options: OpenFileOptions = {})
|
|||
})
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
vscode.window.showErrorMessage(`Could not open file: ${error.message}`)
|
||||
vscode.window.showErrorMessage(t("common:errors.could_not_open_file", { errorMessage: error.message }))
|
||||
} else {
|
||||
vscode.window.showErrorMessage(`Could not open file!`)
|
||||
vscode.window.showErrorMessage(t("common:errors.could_not_open_file_generic"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export interface WebviewMessage {
|
|||
| "requestLmStudioModels"
|
||||
| "requestVsCodeLmModels"
|
||||
| "openImage"
|
||||
| "saveImage"
|
||||
| "openFile"
|
||||
| "openMention"
|
||||
| "cancelTask"
|
||||
|
|
@ -164,6 +165,7 @@ export interface WebviewMessage {
|
|||
text?: string
|
||||
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
|
||||
disabled?: boolean
|
||||
dataUri?: string
|
||||
askResponse?: ClineAskResponse
|
||||
apiConfiguration?: ProviderSettings
|
||||
images?: string[]
|
||||
|
|
|
|||
45
webview-ui/src/components/common/IconButton.tsx
Normal file
45
webview-ui/src/components/common/IconButton.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
interface IconButtonProps {
|
||||
icon: string
|
||||
onClick?: (e: React.MouseEvent) => void
|
||||
onMouseDown?: (e: React.MouseEvent) => void
|
||||
onMouseUp?: (e: React.MouseEvent) => void
|
||||
onMouseLeave?: (e: React.MouseEvent) => void
|
||||
title?: string
|
||||
size?: "small" | "medium"
|
||||
variant?: "default" | "transparent"
|
||||
}
|
||||
|
||||
export function IconButton({
|
||||
icon,
|
||||
onClick,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onMouseLeave,
|
||||
title,
|
||||
size = "medium",
|
||||
variant = "default",
|
||||
}: IconButtonProps) {
|
||||
const sizeClasses = {
|
||||
small: "w-6 h-6",
|
||||
medium: "w-7 h-7",
|
||||
}
|
||||
|
||||
const variantClasses = {
|
||||
default: "bg-transparent hover:bg-vscode-toolbar-hoverBackground",
|
||||
transparent: "bg-transparent hover:bg-vscode-toolbar-hoverBackground",
|
||||
}
|
||||
|
||||
const handleClick = onClick || ((_event: React.MouseEvent) => {})
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${sizeClasses[size]} flex items-center justify-center border-none text-vscode-editor-foreground cursor-pointer rounded-[3px] ${variantClasses[variant]}`}
|
||||
onClick={handleClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseUp={onMouseUp}
|
||||
onMouseLeave={onMouseLeave}
|
||||
title={title}>
|
||||
<span className={`codicon codicon-${icon}`}></span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
80
webview-ui/src/components/common/MermaidActionButtons.tsx
Normal file
80
webview-ui/src/components/common/MermaidActionButtons.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import React from "react"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { IconButton } from "./IconButton"
|
||||
import { ZoomControls } from "./ZoomControls"
|
||||
|
||||
interface MermaidActionButtonsProps {
|
||||
onZoom?: (e: React.MouseEvent) => void
|
||||
onZoomIn?: () => void
|
||||
onZoomOut?: () => void
|
||||
onCopy: (e: React.MouseEvent) => void
|
||||
onSave?: (e: React.MouseEvent) => void
|
||||
onViewCode: () => void
|
||||
onClose?: () => void
|
||||
copyFeedback: boolean
|
||||
showZoomControls?: boolean
|
||||
zoomLevel?: number
|
||||
}
|
||||
|
||||
export const MermaidActionButtons: React.FC<MermaidActionButtonsProps> = ({
|
||||
onZoom,
|
||||
onZoomIn,
|
||||
onZoomOut,
|
||||
onCopy,
|
||||
onSave,
|
||||
onViewCode,
|
||||
onClose,
|
||||
copyFeedback,
|
||||
showZoomControls = false,
|
||||
zoomLevel,
|
||||
}) => {
|
||||
const { t } = useAppTranslation()
|
||||
|
||||
if (showZoomControls && onZoomOut && onZoomIn && zoomLevel !== undefined) {
|
||||
return (
|
||||
<>
|
||||
<ZoomControls
|
||||
zoomLevel={zoomLevel}
|
||||
onZoomIn={onZoomIn}
|
||||
onZoomOut={onZoomOut}
|
||||
zoomInTitle={t("common:mermaid.buttons.zoomIn")}
|
||||
zoomOutTitle={t("common:mermaid.buttons.zoomOut")}
|
||||
/>
|
||||
<IconButton
|
||||
icon="code"
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
onViewCode()
|
||||
}}
|
||||
title={t("common:mermaid.buttons.viewCode")}
|
||||
/>
|
||||
<IconButton
|
||||
icon={copyFeedback ? "check" : "copy"}
|
||||
onClick={onCopy}
|
||||
title={t("common:mermaid.buttons.copy")}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{onZoom && <IconButton icon="zoom-in" onClick={onZoom} title={t("common:mermaid.buttons.zoom")} />}
|
||||
<IconButton
|
||||
icon="code"
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
onViewCode()
|
||||
}}
|
||||
title={t("common:mermaid.buttons.viewCode")}
|
||||
/>
|
||||
<IconButton
|
||||
icon={copyFeedback ? "check" : "copy"}
|
||||
onClick={onCopy}
|
||||
title={t("common:mermaid.buttons.copy")}
|
||||
/>
|
||||
{onSave && <IconButton icon="save" onClick={onSave} title={t("common:mermaid.buttons.save")} />}
|
||||
{onClose && <IconButton icon="close" onClick={onClose} title={t("common:mermaid.buttons.close")} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import { vscode } from "@src/utils/vscode"
|
|||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { useCopyToClipboard } from "@src/utils/clipboard"
|
||||
import CodeBlock from "./CodeBlock"
|
||||
import { MermaidButton } from "@/components/common/MermaidButton"
|
||||
|
||||
// Removed previous attempts at static imports for individual diagram types
|
||||
// as the paths were incorrect for Mermaid v11.4.1 and caused errors.
|
||||
|
|
@ -213,7 +214,9 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
|
|||
)}
|
||||
</div>
|
||||
) : (
|
||||
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading} />
|
||||
<MermaidButton containerRef={containerRef} code={code} isLoading={isLoading} svgToPng={svgToPng}>
|
||||
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading}></SvgContainer>
|
||||
</MermaidButton>
|
||||
)}
|
||||
</MermaidBlockContainer>
|
||||
)
|
||||
|
|
@ -243,10 +246,16 @@ async function svgToPng(svgEl: SVGElement): Promise<string> {
|
|||
|
||||
const serializer = new XMLSerializer()
|
||||
const svgString = serializer.serializeToString(svgClone)
|
||||
const svgDataUrl = "data:image/svg+xml;base64," + btoa(decodeURIComponent(encodeURIComponent(svgString)))
|
||||
|
||||
// Create a data URL directly
|
||||
// First, ensure the SVG string is properly encoded
|
||||
const encodedSvg = encodeURIComponent(svgString).replace(/'/g, "%27").replace(/"/g, "%22")
|
||||
|
||||
const svgDataUrl = `data:image/svg+xml;charset=utf-8,${encodedSvg}`
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement("canvas")
|
||||
canvas.width = editorWidth
|
||||
|
|
|
|||
246
webview-ui/src/components/common/MermaidButton.tsx
Normal file
246
webview-ui/src/components/common/MermaidButton.tsx
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import { useState, useCallback } from "react"
|
||||
import { useCopyToClipboard } from "@src/utils/clipboard"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { MermaidActionButtons } from "./MermaidActionButtons"
|
||||
import { Modal } from "./Modal"
|
||||
import { TabButton } from "./TabButton"
|
||||
import { IconButton } from "./IconButton"
|
||||
import { ZoomControls } from "./ZoomControls"
|
||||
|
||||
const MIN_ZOOM = 0.5
|
||||
const MAX_ZOOM = 20
|
||||
|
||||
export interface MermaidButtonProps {
|
||||
containerRef: React.RefObject<HTMLDivElement>
|
||||
code: string
|
||||
isLoading: boolean
|
||||
svgToPng: (svgEl: SVGElement) => Promise<string>
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export function MermaidButton({ containerRef, code, isLoading, svgToPng, children }: MermaidButtonProps) {
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [zoomLevel, setZoomLevel] = useState(1)
|
||||
const [copyFeedback, setCopyFeedback] = useState(false)
|
||||
const [isHovering, setIsHovering] = useState(false)
|
||||
const [modalViewMode, setModalViewMode] = useState<"diagram" | "code">("diagram")
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [dragPosition, setDragPosition] = useState({ x: 0, y: 0 })
|
||||
const { copyWithFeedback } = useCopyToClipboard()
|
||||
const { t } = useAppTranslation()
|
||||
|
||||
/**
|
||||
* Opens a modal with the diagram for zooming
|
||||
*/
|
||||
const handleZoom = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
setShowModal(true)
|
||||
setZoomLevel(1)
|
||||
setModalViewMode("diagram")
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the diagram text to clipboard
|
||||
*/
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
|
||||
try {
|
||||
await copyWithFeedback(code, e)
|
||||
|
||||
// Show feedback
|
||||
setCopyFeedback(true)
|
||||
setTimeout(() => setCopyFeedback(false), 2000)
|
||||
} catch (err) {
|
||||
console.error("Error copying text:", err instanceof Error ? err.message : String(err))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the diagram as an image file
|
||||
*/
|
||||
const handleSave = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
|
||||
// Get the SVG element from the container
|
||||
const svgEl = containerRef.current?.querySelector("svg")
|
||||
if (!svgEl) {
|
||||
console.error("SVG element not found")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert SVG to PNG
|
||||
const pngDataUrl = await svgToPng(svgEl)
|
||||
|
||||
// Send message to VSCode to save the image
|
||||
vscode.postMessage({
|
||||
type: "saveImage",
|
||||
dataUri: pngDataUrl,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error saving image:", error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust zoom level in the modal
|
||||
*/
|
||||
const adjustZoom = (amount: number) => {
|
||||
setZoomLevel((prev) => {
|
||||
const newZoom = prev + amount
|
||||
return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, newZoom))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle wheel event for zooming with scroll wheel
|
||||
*/
|
||||
const handleWheel = useCallback((e: React.WheelEvent) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
// Determine zoom direction and amount
|
||||
// Negative deltaY means scrolling up (zoom in), positive means scrolling down (zoom out)
|
||||
const delta = e.deltaY > 0 ? -0.2 : 0.2
|
||||
adjustZoom(delta)
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Handle mouse enter event for diagram container
|
||||
*/
|
||||
const handleMouseEnter = () => {
|
||||
setIsHovering(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse leave event for diagram container
|
||||
*/
|
||||
const handleMouseLeave = () => {
|
||||
setIsHovering(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative w-full" onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||
{children}
|
||||
{!isLoading && isHovering && (
|
||||
<div className="absolute bottom-2 right-2 flex gap-1 bg-black/70 rounded p-0.5 z-10 opacity-100 transition-opacity duration-200 ease-in-out">
|
||||
<MermaidActionButtons
|
||||
onZoom={handleZoom}
|
||||
onCopy={handleCopy}
|
||||
onSave={handleSave}
|
||||
onViewCode={() => {
|
||||
setShowModal(true)
|
||||
setModalViewMode("code")
|
||||
setZoomLevel(1)
|
||||
}}
|
||||
copyFeedback={copyFeedback}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Modal isOpen={showModal} onClose={() => setShowModal(false)}>
|
||||
<div className="flex justify-between items-center border-b border-vscode-editorGroup-border">
|
||||
<div className="flex gap-0">
|
||||
<TabButton
|
||||
icon="graph"
|
||||
label={t("common:mermaid.tabs.diagram")}
|
||||
isActive={modalViewMode === "diagram"}
|
||||
onClick={() => setModalViewMode("diagram")}
|
||||
/>
|
||||
<TabButton
|
||||
icon="code"
|
||||
label={t("common:mermaid.tabs.code")}
|
||||
isActive={modalViewMode === "code"}
|
||||
onClick={() => setModalViewMode("code")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pr-3">
|
||||
<IconButton
|
||||
icon="close"
|
||||
onClick={() => setShowModal(false)}
|
||||
title={t("common:mermaid.buttons.close")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex-1 p-4 pb-[60px] overflow-auto flex items-center justify-center"
|
||||
onWheel={modalViewMode === "diagram" ? handleWheel : undefined}>
|
||||
{modalViewMode === "diagram" ? (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
transform: `scale(${zoomLevel}) translate(${dragPosition.x}px, ${dragPosition.y}px)`,
|
||||
transformOrigin: "center center",
|
||||
transition: isDragging ? "none" : "transform 0.1s ease",
|
||||
cursor: isDragging ? "grabbing" : "grab",
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
setIsDragging(true)
|
||||
e.preventDefault()
|
||||
}}
|
||||
onMouseMove={(e) => {
|
||||
if (isDragging) {
|
||||
setDragPosition((prev) => ({
|
||||
x: prev.x + e.movementX / zoomLevel,
|
||||
y: prev.y + e.movementY / zoomLevel,
|
||||
}))
|
||||
}
|
||||
}}
|
||||
onMouseUp={() => setIsDragging(false)}
|
||||
onMouseLeave={() => setIsDragging(false)}>
|
||||
{containerRef.current && containerRef.current.innerHTML && (
|
||||
<div dangerouslySetInnerHTML={{ __html: containerRef.current.innerHTML }} />
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute bottom-4 left-4 bg-vscode-editor-background border border-vscode-editorGroup-border rounded px-2 py-1 text-xs text-vscode-descriptionForeground pointer-events-none opacity-80">
|
||||
{Math.round(zoomLevel * 100)}%
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<textarea
|
||||
className="w-full min-h-[200px] bg-vscode-editor-background text-vscode-editor-foreground border border-vscode-editorGroup-border rounded-[3px] p-2 font-mono resize-y outline-none"
|
||||
readOnly
|
||||
value={code}
|
||||
style={{ height: "100%", minHeight: "unset", fontSize: "var(--vscode-editor-font-size)" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute bottom-0 right-0 left-0 p-3 flex items-center justify-end gap-2 bg-vscode-editor-background border-t border-vscode-editorGroup-border rounded-b">
|
||||
{modalViewMode === "diagram" ? (
|
||||
<>
|
||||
<ZoomControls
|
||||
zoomLevel={zoomLevel}
|
||||
zoomInTitle={t("common:mermaid.buttons.zoomIn")}
|
||||
zoomOutTitle={t("common:mermaid.buttons.zoomOut")}
|
||||
useContinuousZoom={true}
|
||||
adjustZoom={adjustZoom}
|
||||
zoomInStep={0.2}
|
||||
zoomOutStep={-0.2}
|
||||
/>
|
||||
<IconButton
|
||||
icon={copyFeedback ? "check" : "copy"}
|
||||
onClick={handleCopy}
|
||||
title={t("common:mermaid.buttons.copy")}
|
||||
/>
|
||||
<IconButton icon="save" onClick={handleSave} title={t("common:mermaid.buttons.save")} />
|
||||
</>
|
||||
) : (
|
||||
<IconButton
|
||||
icon={copyFeedback ? "check" : "copy"}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
copyWithFeedback(code, e)
|
||||
}}
|
||||
title={t("common:mermaid.buttons.copy")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
20
webview-ui/src/components/common/Modal.tsx
Normal file
20
webview-ui/src/components/common/Modal.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
interface ModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function Modal({ isOpen, onClose, children, className = "" }: ModalProps) {
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-[1000]" onClick={onClose}>
|
||||
<div
|
||||
className={`bg-vscode-editor-background rounded w-[90%] h-[90%] max-w-[1200px] flex flex-col shadow-[0_5px_15px_rgba(0,0,0,0.5)] border border-vscode-editorGroup-border relative ${className}`}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
26
webview-ui/src/components/common/TabButton.tsx
Normal file
26
webview-ui/src/components/common/TabButton.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
interface TabButtonProps {
|
||||
icon: string
|
||||
label: string
|
||||
isActive: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export function TabButton({ icon, label, isActive, onClick }: TabButtonProps) {
|
||||
const activeClasses =
|
||||
"bg-vscode-editor-background border-b-2 border-vscode-focusBorder text-vscode-editor-foreground"
|
||||
const inactiveClasses =
|
||||
"bg-transparent border-b-2 border-transparent text-vscode-descriptionForeground hover:text-vscode-editor-foreground hover:bg-vscode-toolbar-hoverBackground"
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`px-4 py-2 border-none cursor-pointer flex items-center gap-1.5 text-[13px] transition-all duration-200 ease-in-out ${
|
||||
isActive ? activeClasses : inactiveClasses
|
||||
}`}
|
||||
onClick={onClick}>
|
||||
<span
|
||||
className={`codicon codicon-${icon} text-sm`}
|
||||
style={isActive ? { color: "var(--vscode-focusBorder)" } : undefined}></span>
|
||||
{label}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
91
webview-ui/src/components/common/ZoomControls.tsx
Normal file
91
webview-ui/src/components/common/ZoomControls.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { IconButton } from "./IconButton"
|
||||
import { useRef, useEffect } from "react"
|
||||
|
||||
interface ZoomControlsProps {
|
||||
zoomLevel: number
|
||||
zoomInTitle?: string
|
||||
zoomOutTitle?: string
|
||||
useContinuousZoom?: boolean
|
||||
adjustZoom?: (amount: number) => void
|
||||
zoomInStep?: number
|
||||
zoomOutStep?: number
|
||||
onZoomIn?: () => void
|
||||
onZoomOut?: () => void
|
||||
}
|
||||
|
||||
export function ZoomControls({
|
||||
zoomLevel,
|
||||
zoomInTitle,
|
||||
zoomOutTitle,
|
||||
useContinuousZoom = false,
|
||||
adjustZoom,
|
||||
zoomInStep = 0.1,
|
||||
zoomOutStep = -0.1,
|
||||
onZoomIn,
|
||||
onZoomOut,
|
||||
}: ZoomControlsProps) {
|
||||
const zoomIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
/**
|
||||
* Start continuous zoom on mouse down
|
||||
*/
|
||||
const startContinuousZoom = (amount: number) => {
|
||||
if (!useContinuousZoom || !adjustZoom) return
|
||||
|
||||
// Clear any existing interval first
|
||||
if (zoomIntervalRef.current) {
|
||||
clearInterval(zoomIntervalRef.current)
|
||||
}
|
||||
|
||||
// Immediately apply first zoom adjustment
|
||||
adjustZoom(amount)
|
||||
|
||||
// Set up interval for continuous zooming
|
||||
zoomIntervalRef.current = setInterval(() => {
|
||||
adjustZoom(amount)
|
||||
}, 150) // Adjust every 150ms while button is held down
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop continuous zoom on mouse up or mouse leave
|
||||
*/
|
||||
const stopContinuousZoom = () => {
|
||||
if (zoomIntervalRef.current) {
|
||||
clearInterval(zoomIntervalRef.current)
|
||||
zoomIntervalRef.current = null
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up interval on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (zoomIntervalRef.current) {
|
||||
clearInterval(zoomIntervalRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<IconButton
|
||||
icon="zoom-out"
|
||||
title={zoomOutTitle}
|
||||
onClick={!useContinuousZoom ? onZoomOut || (() => adjustZoom?.(zoomOutStep)) : undefined}
|
||||
onMouseDown={useContinuousZoom && adjustZoom ? () => startContinuousZoom(zoomOutStep) : undefined}
|
||||
onMouseUp={useContinuousZoom && adjustZoom ? stopContinuousZoom : undefined}
|
||||
onMouseLeave={useContinuousZoom && adjustZoom ? stopContinuousZoom : undefined}
|
||||
/>
|
||||
<div className="text-sm text-vscode-editor-foreground min-w-[50px] text-center">
|
||||
{Math.round(zoomLevel * 100)}%
|
||||
</div>
|
||||
<IconButton
|
||||
icon="zoom-in"
|
||||
title={zoomInTitle}
|
||||
onClick={!useContinuousZoom ? onZoomIn || (() => adjustZoom?.(zoomInStep)) : undefined}
|
||||
onMouseDown={useContinuousZoom && adjustZoom ? () => startContinuousZoom(zoomInStep) : undefined}
|
||||
onMouseUp={useContinuousZoom && adjustZoom ? stopContinuousZoom : undefined}
|
||||
onMouseLeave={useContinuousZoom && adjustZoom ? stopContinuousZoom : undefined}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Generant diagrama mermaid...",
|
||||
"render_error": "No es pot renderitzar el diagrama"
|
||||
"render_error": "No es pot renderitzar el diagrama",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Ampliar",
|
||||
"zoomOut": "Reduir",
|
||||
"copy": "Copiar",
|
||||
"save": "Desar imatge",
|
||||
"viewCode": "Veure codi",
|
||||
"viewDiagram": "Veure diagrama",
|
||||
"close": "Tancar"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Codi Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagrama",
|
||||
"code": "Codi"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Imatge copiada al porta-retalls",
|
||||
"copyError": "Error copiant la imatge"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Format d'URI de dades no vàlid",
|
||||
"copyingImage": "Error copiant la imatge: {{error}}",
|
||||
"openingImage": "Error obrint la imatge: {{error}}",
|
||||
"pathNotExists": "El camí no existeix: {{path}}",
|
||||
"couldNotOpen": "No s'ha pogut obrir el fitxer: {{error}}",
|
||||
"couldNotOpenGeneric": "No s'ha pogut obrir el fitxer!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI de dades de la imatge copiada al porta-retalls"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Mermaid-Diagramm wird generiert...",
|
||||
"render_error": "Diagramm kann nicht gerendert werden"
|
||||
"render_error": "Diagramm kann nicht gerendert werden",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Vergrößern",
|
||||
"zoomOut": "Verkleinern",
|
||||
"copy": "Kopieren",
|
||||
"save": "Bild speichern",
|
||||
"viewCode": "Code anzeigen",
|
||||
"viewDiagram": "Diagramm anzeigen",
|
||||
"close": "Schließen"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaid-Code"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagramm",
|
||||
"code": "Code"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Bild in die Zwischenablage kopiert",
|
||||
"copyError": "Fehler beim Kopieren des Bildes"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Ungültiges Daten-URI-Format",
|
||||
"copyingImage": "Fehler beim Kopieren des Bildes: {{error}}",
|
||||
"openingImage": "Fehler beim Öffnen des Bildes: {{error}}",
|
||||
"pathNotExists": "Pfad existiert nicht: {{path}}",
|
||||
"couldNotOpen": "Datei konnte nicht geöffnet werden: {{error}}",
|
||||
"couldNotOpenGeneric": "Datei konnte nicht geöffnet werden!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "Bild-Daten-URI in die Zwischenablage kopiert"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Generating mermaid diagram...",
|
||||
"render_error": "Unable to Render Diagram"
|
||||
"render_error": "Unable to Render Diagram",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Zoom In",
|
||||
"zoomOut": "Zoom Out",
|
||||
"copy": "Copy",
|
||||
"save": "Save Image",
|
||||
"viewCode": "View Code",
|
||||
"viewDiagram": "View Diagram",
|
||||
"close": "Close"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaid Code"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagram",
|
||||
"code": "Code"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Image copied to clipboard",
|
||||
"copyError": "Error copying image"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Invalid data URI format",
|
||||
"copyingImage": "Error copying image: {{error}}",
|
||||
"openingImage": "Error opening image: {{error}}",
|
||||
"pathNotExists": "Path does not exist: {{path}}",
|
||||
"couldNotOpen": "Could not open file: {{error}}",
|
||||
"couldNotOpenGeneric": "Could not open file!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "Image data URI copied to clipboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Generando diagrama mermaid...",
|
||||
"render_error": "No se puede renderizar el diagrama"
|
||||
"render_error": "No se puede renderizar el diagrama",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Ampliar",
|
||||
"zoomOut": "Reducir",
|
||||
"copy": "Copiar",
|
||||
"save": "Guardar imagen",
|
||||
"viewCode": "Ver código",
|
||||
"viewDiagram": "Ver diagrama",
|
||||
"close": "Cerrar"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Código Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagrama",
|
||||
"code": "Código"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Imagen copiada al portapapeles",
|
||||
"copyError": "Error copiando la imagen"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Formato de URI de datos inválido",
|
||||
"copyingImage": "Error copiando la imagen: {{error}}",
|
||||
"openingImage": "Error abriendo la imagen: {{error}}",
|
||||
"pathNotExists": "La ruta no existe: {{path}}",
|
||||
"couldNotOpen": "No se pudo abrir el archivo: {{error}}",
|
||||
"couldNotOpenGeneric": "¡No se pudo abrir el archivo!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI de datos de imagen copiada al portapapeles"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Génération du diagramme mermaid...",
|
||||
"render_error": "Impossible de rendre le diagramme"
|
||||
"render_error": "Impossible de rendre le diagramme",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Agrandir",
|
||||
"zoomOut": "Réduire",
|
||||
"copy": "Copier",
|
||||
"save": "Enregistrer l'image",
|
||||
"viewCode": "Voir le code",
|
||||
"viewDiagram": "Voir le diagramme",
|
||||
"close": "Fermer"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Code Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagramme",
|
||||
"code": "Code"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Image copiée dans le presse-papiers",
|
||||
"copyError": "Erreur lors de la copie de l'image"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Format d'URI de données invalide",
|
||||
"copyingImage": "Erreur lors de la copie de l'image : {{error}}",
|
||||
"openingImage": "Erreur lors de l'ouverture de l'image : {{error}}",
|
||||
"pathNotExists": "Le chemin n'existe pas : {{path}}",
|
||||
"couldNotOpen": "Impossible d'ouvrir le fichier : {{error}}",
|
||||
"couldNotOpenGeneric": "Impossible d'ouvrir le fichier !"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI de données d'image copiée dans le presse-papiers"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "मरमेड डायग्राम जनरेट हो रहा है...",
|
||||
"render_error": "डायग्राम रेंडर नहीं किया जा सकता"
|
||||
"render_error": "डायग्राम रेंडर नहीं किया जा सकता",
|
||||
"buttons": {
|
||||
"zoom": "ज़ूम",
|
||||
"zoomIn": "बड़ा करें",
|
||||
"zoomOut": "छोटा करें",
|
||||
"copy": "कॉपी करें",
|
||||
"save": "छवि सहेजें",
|
||||
"viewCode": "कोड देखें",
|
||||
"viewDiagram": "डायग्राम देखें",
|
||||
"close": "बंद करें"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "मरमेड कोड"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "डायग्राम",
|
||||
"code": "कोड"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "इमेज क्लिपबोर्ड में कॉपी हो गई",
|
||||
"copyError": "इमेज कॉपी करने में त्रुटि"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "अमान्य डेटा URI फॉर्मेट",
|
||||
"copyingImage": "इमेज कॉपी करने में त्रुटि: {{error}}",
|
||||
"openingImage": "इमेज खोलने में त्रुटि: {{error}}",
|
||||
"pathNotExists": "पथ मौजूद नहीं है: {{path}}",
|
||||
"couldNotOpen": "फ़ाइल नहीं खोली जा सकी: {{error}}",
|
||||
"couldNotOpenGeneric": "फ़ाइल नहीं खोली जा सकी!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "इमेज डेटा URI क्लिपबोर्ड में कॉपी हो गया"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Generazione del diagramma mermaid...",
|
||||
"render_error": "Impossibile renderizzare il diagramma"
|
||||
"render_error": "Impossibile renderizzare il diagramma",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Ingrandisci",
|
||||
"zoomOut": "Riduci",
|
||||
"copy": "Copia",
|
||||
"save": "Salva immagine",
|
||||
"viewCode": "Visualizza codice",
|
||||
"viewDiagram": "Visualizza diagramma",
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Codice Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagramma",
|
||||
"code": "Codice"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Immagine copiata negli appunti",
|
||||
"copyError": "Errore nella copia dell'immagine"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Formato URI dati non valido",
|
||||
"copyingImage": "Errore nella copia dell'immagine: {{error}}",
|
||||
"openingImage": "Errore nell'apertura dell'immagine: {{error}}",
|
||||
"pathNotExists": "Il percorso non esiste: {{path}}",
|
||||
"couldNotOpen": "Impossibile aprire il file: {{error}}",
|
||||
"couldNotOpenGeneric": "Impossibile aprire il file!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI dati immagine copiato negli appunti"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Mermaidダイアグラムを生成中...",
|
||||
"render_error": "ダイアグラムをレンダリングできません"
|
||||
"render_error": "ダイアグラムをレンダリングできません",
|
||||
"buttons": {
|
||||
"zoom": "ズーム",
|
||||
"zoomIn": "拡大",
|
||||
"zoomOut": "縮小",
|
||||
"copy": "コピー",
|
||||
"save": "画像を保存",
|
||||
"viewCode": "コードを表示",
|
||||
"viewDiagram": "ダイアグラムを表示",
|
||||
"close": "閉じる"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaidコード"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "ダイアグラム",
|
||||
"code": "コード"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "画像をクリップボードにコピーしました",
|
||||
"copyError": "画像のコピーエラー"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "無効なデータURI形式",
|
||||
"copyingImage": "画像のコピーエラー: {{error}}",
|
||||
"openingImage": "画像を開く際のエラー: {{error}}",
|
||||
"pathNotExists": "パスが存在しません: {{path}}",
|
||||
"couldNotOpen": "ファイルを開けませんでした: {{error}}",
|
||||
"couldNotOpenGeneric": "ファイルを開けませんでした!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "画像データURIをクリップボードにコピーしました"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "머메이드 다이어그램 생성 중...",
|
||||
"render_error": "다이어그램을 렌더링할 수 없음"
|
||||
"render_error": "다이어그램을 렌더링할 수 없음",
|
||||
"buttons": {
|
||||
"zoom": "줌",
|
||||
"zoomIn": "확대",
|
||||
"zoomOut": "축소",
|
||||
"copy": "복사",
|
||||
"save": "이미지 저장",
|
||||
"viewCode": "코드 보기",
|
||||
"viewDiagram": "다이어그램 보기",
|
||||
"close": "닫기"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "머메이드 코드"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "다이어그램",
|
||||
"code": "코드"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "이미지가 클립보드에 복사됨",
|
||||
"copyError": "이미지 복사 오류"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "잘못된 데이터 URI 형식",
|
||||
"copyingImage": "이미지 복사 오류: {{error}}",
|
||||
"openingImage": "이미지 열기 오류: {{error}}",
|
||||
"pathNotExists": "경로가 존재하지 않음: {{path}}",
|
||||
"couldNotOpen": "파일을 열 수 없음: {{error}}",
|
||||
"couldNotOpenGeneric": "파일을 열 수 없습니다!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "이미지 데이터 URI가 클립보드에 복사됨"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Mermaid-diagram genereren...",
|
||||
"render_error": "Kan diagram niet weergeven"
|
||||
"render_error": "Kan diagram niet weergeven",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Inzoomen",
|
||||
"zoomOut": "Uitzoomen",
|
||||
"copy": "Kopiëren",
|
||||
"save": "Afbeelding opslaan",
|
||||
"viewCode": "Code bekijken",
|
||||
"viewDiagram": "Diagram bekijken",
|
||||
"close": "Sluiten"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaid-code"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagram",
|
||||
"code": "Code"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Afbeelding gekopieerd naar klembord",
|
||||
"copyError": "Fout bij kopiëren van afbeelding"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Ongeldig data-URI-formaat",
|
||||
"copyingImage": "Fout bij kopiëren van afbeelding: {{error}}",
|
||||
"openingImage": "Fout bij openen van afbeelding: {{error}}",
|
||||
"pathNotExists": "Pad bestaat niet: {{path}}",
|
||||
"couldNotOpen": "Kon bestand niet openen: {{error}}",
|
||||
"couldNotOpenGeneric": "Kon bestand niet openen!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "Afbeelding data-URI gekopieerd naar klembord"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Generowanie diagramu mermaid...",
|
||||
"render_error": "Nie można renderować diagramu"
|
||||
"render_error": "Nie można renderować diagramu",
|
||||
"buttons": {
|
||||
"zoom": "Powiększenie",
|
||||
"zoomIn": "Powiększ",
|
||||
"zoomOut": "Pomniejsz",
|
||||
"copy": "Kopiuj",
|
||||
"save": "Zapisz obraz",
|
||||
"viewCode": "Zobacz kod",
|
||||
"viewDiagram": "Zobacz diagram",
|
||||
"close": "Zamknij"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Kod Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagram",
|
||||
"code": "Kod"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Obraz skopiowany do schowka",
|
||||
"copyError": "Błąd kopiowania obrazu"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Nieprawidłowy format URI danych",
|
||||
"copyingImage": "Błąd kopiowania obrazu: {{error}}",
|
||||
"openingImage": "Błąd otwierania obrazu: {{error}}",
|
||||
"pathNotExists": "Ścieżka nie istnieje: {{path}}",
|
||||
"couldNotOpen": "Nie można otworzyć pliku: {{error}}",
|
||||
"couldNotOpenGeneric": "Nie można otworzyć pliku!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI danych obrazu skopiowane do schowka"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Gerando diagrama mermaid...",
|
||||
"render_error": "Não foi possível renderizar o diagrama"
|
||||
"render_error": "Não foi possível renderizar o diagrama",
|
||||
"buttons": {
|
||||
"zoom": "Zoom",
|
||||
"zoomIn": "Ampliar",
|
||||
"zoomOut": "Reduzir",
|
||||
"copy": "Copiar",
|
||||
"save": "Salvar imagem",
|
||||
"viewCode": "Ver código",
|
||||
"viewDiagram": "Ver diagrama",
|
||||
"close": "Fechar"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Código Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diagrama",
|
||||
"code": "Código"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Imagem copiada para a área de transferência",
|
||||
"copyError": "Erro ao copiar imagem"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Formato de URI de dados inválido",
|
||||
"copyingImage": "Erro ao copiar imagem: {{error}}",
|
||||
"openingImage": "Erro ao abrir imagem: {{error}}",
|
||||
"pathNotExists": "Caminho não existe: {{path}}",
|
||||
"couldNotOpen": "Não foi possível abrir o arquivo: {{error}}",
|
||||
"couldNotOpenGeneric": "Não foi possível abrir o arquivo!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI de dados da imagem copiada para a área de transferência"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Создание диаграммы mermaid...",
|
||||
"render_error": "Не удалось отобразить диаграмму"
|
||||
"render_error": "Не удалось отобразить диаграмму",
|
||||
"buttons": {
|
||||
"zoom": "Масштаб",
|
||||
"zoomIn": "Увеличить",
|
||||
"zoomOut": "Уменьшить",
|
||||
"copy": "Копировать",
|
||||
"save": "Сохранить изображение",
|
||||
"viewCode": "Посмотреть код",
|
||||
"viewDiagram": "Посмотреть диаграмму",
|
||||
"close": "Закрыть"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Код Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Диаграмма",
|
||||
"code": "Код"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Изображение скопировано в буфер обмена",
|
||||
"copyError": "Ошибка копирования изображения"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Неверный формат URI данных",
|
||||
"copyingImage": "Ошибка копирования изображения: {{error}}",
|
||||
"openingImage": "Ошибка открытия изображения: {{error}}",
|
||||
"pathNotExists": "Путь не существует: {{path}}",
|
||||
"couldNotOpen": "Не удалось открыть файл: {{error}}",
|
||||
"couldNotOpenGeneric": "Не удалось открыть файл!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI данных изображения скопирован в буфер обмена"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Mermaid diyagramı oluşturuluyor...",
|
||||
"render_error": "Diyagram render edilemiyor"
|
||||
"render_error": "Diyagram render edilemiyor",
|
||||
"buttons": {
|
||||
"zoom": "Yakınlaştır",
|
||||
"zoomIn": "Büyüt",
|
||||
"zoomOut": "Küçült",
|
||||
"copy": "Kopyala",
|
||||
"save": "Resmi kaydet",
|
||||
"viewCode": "Kodu görüntüle",
|
||||
"viewDiagram": "Diyagramı görüntüle",
|
||||
"close": "Kapat"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaid Kodu"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Diyagram",
|
||||
"code": "Kod"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Görsel panoya kopyalandı",
|
||||
"copyError": "Görsel kopyalama hatası"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Geçersiz veri URI formatı",
|
||||
"copyingImage": "Görsel kopyalama hatası: {{error}}",
|
||||
"openingImage": "Görsel açma hatası: {{error}}",
|
||||
"pathNotExists": "Yol mevcut değil: {{path}}",
|
||||
"couldNotOpen": "Dosya açılamadı: {{error}}",
|
||||
"couldNotOpenGeneric": "Dosya açılamadı!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "Görsel veri URI'si panoya kopyalandı"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "Đang tạo biểu đồ mermaid...",
|
||||
"render_error": "Không thể hiển thị biểu đồ"
|
||||
"render_error": "Không thể hiển thị biểu đồ",
|
||||
"buttons": {
|
||||
"zoom": "Thu phóng",
|
||||
"zoomIn": "Phóng to",
|
||||
"zoomOut": "Thu nhỏ",
|
||||
"copy": "Sao chép",
|
||||
"save": "Lưu hình ảnh",
|
||||
"viewCode": "Xem mã",
|
||||
"viewDiagram": "Xem biểu đồ",
|
||||
"close": "Đóng"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mã Mermaid"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "Biểu đồ",
|
||||
"code": "Mã"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "Hình ảnh đã được sao chép vào clipboard",
|
||||
"copyError": "Lỗi sao chép hình ảnh"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "Định dạng URI dữ liệu không hợp lệ",
|
||||
"copyingImage": "Lỗi sao chép hình ảnh: {{error}}",
|
||||
"openingImage": "Lỗi mở hình ảnh: {{error}}",
|
||||
"pathNotExists": "Đường dẫn không tồn tại: {{path}}",
|
||||
"couldNotOpen": "Không thể mở tệp: {{error}}",
|
||||
"couldNotOpenGeneric": "Không thể mở tệp!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "URI dữ liệu hình ảnh đã được sao chép vào clipboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "生成 Mermaid 图表中...",
|
||||
"render_error": "无法渲染图表"
|
||||
"render_error": "无法渲染图表",
|
||||
"buttons": {
|
||||
"zoom": "缩放",
|
||||
"zoomIn": "放大",
|
||||
"zoomOut": "缩小",
|
||||
"copy": "复制",
|
||||
"save": "保存图片",
|
||||
"viewCode": "查看代码",
|
||||
"viewDiagram": "查看图表",
|
||||
"close": "关闭"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaid 代码"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "图表",
|
||||
"code": "代码"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "图片已复制到剪贴板",
|
||||
"copyError": "复制图片时出错"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "无效的数据 URI 格式",
|
||||
"copyingImage": "复制图片时出错: {{error}}",
|
||||
"openingImage": "打开图片时出错: {{error}}",
|
||||
"pathNotExists": "路径不存在: {{path}}",
|
||||
"couldNotOpen": "无法打开文件: {{error}}",
|
||||
"couldNotOpenGeneric": "无法打开文件!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "图片数据 URI 已复制到剪贴板"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
},
|
||||
"mermaid": {
|
||||
"loading": "產生 Mermaid 圖表中...",
|
||||
"render_error": "無法渲染圖表"
|
||||
"render_error": "無法渲染圖表",
|
||||
"buttons": {
|
||||
"zoom": "縮放",
|
||||
"zoomIn": "放大",
|
||||
"zoomOut": "縮小",
|
||||
"copy": "複製",
|
||||
"save": "儲存圖片",
|
||||
"viewCode": "檢視程式碼",
|
||||
"viewDiagram": "檢視圖表",
|
||||
"close": "關閉"
|
||||
},
|
||||
"modal": {
|
||||
"codeTitle": "Mermaid 程式碼"
|
||||
},
|
||||
"tabs": {
|
||||
"diagram": "圖表",
|
||||
"code": "程式碼"
|
||||
},
|
||||
"feedback": {
|
||||
"imageCopied": "圖片已複製到剪貼簿",
|
||||
"copyError": "複製圖片時發生錯誤"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"invalidDataUri": "無效的資料 URI 格式",
|
||||
"copyingImage": "複製圖片時發生錯誤: {{error}}",
|
||||
"openingImage": "開啟圖片時發生錯誤: {{error}}",
|
||||
"pathNotExists": "路徑不存在: {{path}}",
|
||||
"couldNotOpen": "無法開啟檔案: {{error}}",
|
||||
"couldNotOpenGeneric": "無法開啟檔案!"
|
||||
},
|
||||
"success": {
|
||||
"imageDataUriCopied": "圖片資料 URI 已複製到剪貼簿"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue