mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add page-level drop overlay and paste handler on tool page
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
|||||||
Download,
|
Download,
|
||||||
FileImage,
|
FileImage,
|
||||||
Loader2,
|
Loader2,
|
||||||
|
Upload,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import type { Crop } from "react-image-crop";
|
import type { Crop } from "react-image-crop";
|
||||||
@@ -32,6 +33,7 @@ import { recordRecentTool } from "@/hooks/use-recent-tools";
|
|||||||
import { formatFileSize } from "@/lib/download";
|
import { formatFileSize } from "@/lib/download";
|
||||||
import { format } from "@/lib/format";
|
import { format } from "@/lib/format";
|
||||||
import { ICON_MAP } from "@/lib/icon-map";
|
import { ICON_MAP } from "@/lib/icon-map";
|
||||||
|
import { MULTI_FILE_TOOLS } from "@/lib/tool-display-modes";
|
||||||
import { getToolName } from "@/lib/tool-i18n";
|
import { getToolName } from "@/lib/tool-i18n";
|
||||||
import { getToolRegistryEntry } from "@/lib/tool-registry";
|
import { getToolRegistryEntry } from "@/lib/tool-registry";
|
||||||
import { useBase64Store } from "@/stores/base64-store";
|
import { useBase64Store } from "@/stores/base64-store";
|
||||||
@@ -293,6 +295,11 @@ export function ToolPage() {
|
|||||||
// Center of the painted mask as a 0-100 percentage — used to init the slider at the right spot
|
// Center of the painted mask as a 0-100 percentage — used to init the slider at the right spot
|
||||||
const [eraserSliderInitPos, setEraserSliderInitPos] = useState<number | null>(null);
|
const [eraserSliderInitPos, setEraserSliderInitPos] = useState<number | null>(null);
|
||||||
|
|
||||||
|
// Page-level drag overlay state
|
||||||
|
const [isDraggingOver, setIsDraggingOver] = useState(false);
|
||||||
|
const dragCounter = useRef(0);
|
||||||
|
const isMultiFileTool = toolId ? MULTI_FILE_TOOLS.has(toolId) : false;
|
||||||
|
|
||||||
// biome-ignore lint/correctness/useExhaustiveDependencies: toolId triggers intentional reset on tool navigation
|
// biome-ignore lint/correctness/useExhaustiveDependencies: toolId triggers intentional reset on tool navigation
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
useFileStore.getState().undoProcessing();
|
useFileStore.getState().undoProcessing();
|
||||||
@@ -379,6 +386,72 @@ export function ToolPage() {
|
|||||||
input.click();
|
input.click();
|
||||||
}, [addFiles, toolAccept, toolFileFilter]);
|
}, [addFiles, toolAccept, toolFileFilter]);
|
||||||
|
|
||||||
|
// Page-level drag handlers (active when a file is already loaded)
|
||||||
|
const handleDragEnter = useCallback((e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dragCounter.current++;
|
||||||
|
if (e.dataTransfer.types.includes("Files")) {
|
||||||
|
setIsDraggingOver(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDragOver = useCallback((e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDragLeave = useCallback(() => {
|
||||||
|
dragCounter.current--;
|
||||||
|
if (dragCounter.current === 0) setIsDraggingOver(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePageDrop = useCallback(
|
||||||
|
(e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dragCounter.current = 0;
|
||||||
|
setIsDraggingOver(false);
|
||||||
|
const droppedFiles = Array.from(e.dataTransfer.files);
|
||||||
|
if (droppedFiles.length === 0) return;
|
||||||
|
const validFiles = toolFileFilter ? droppedFiles.filter(toolFileFilter) : droppedFiles;
|
||||||
|
if (validFiles.length === 0) return;
|
||||||
|
if (isMultiFileTool) {
|
||||||
|
addFiles(validFiles);
|
||||||
|
} else {
|
||||||
|
setFiles(validFiles);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toolFileFilter, addFiles, setFiles, isMultiFileTool],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Document-level paste handler (skip for generator tools that don't accept file input)
|
||||||
|
useEffect(() => {
|
||||||
|
if (registryEntry?.displayMode === "no-dropzone") return;
|
||||||
|
|
||||||
|
const handlePaste = (e: ClipboardEvent) => {
|
||||||
|
const pastedFiles: File[] = [];
|
||||||
|
if (e.clipboardData?.files.length) {
|
||||||
|
pastedFiles.push(...Array.from(e.clipboardData.files));
|
||||||
|
} else if (e.clipboardData?.items) {
|
||||||
|
for (const item of Array.from(e.clipboardData.items)) {
|
||||||
|
if (item.kind === "file") {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) pastedFiles.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pastedFiles.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
const filtered = toolFileFilter ? pastedFiles.filter(toolFileFilter) : pastedFiles;
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
if (isMultiFileTool) addFiles(filtered);
|
||||||
|
else setFiles(filtered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("paste", handlePaste);
|
||||||
|
return () => document.removeEventListener("paste", handlePaste);
|
||||||
|
}, [toolFileFilter, isMultiFileTool, addFiles, setFiles, registryEntry?.displayMode]);
|
||||||
|
|
||||||
const handleDownloadAll = useCallback(() => {
|
const handleDownloadAll = useCallback(() => {
|
||||||
if (!batchZipBlob) return;
|
if (!batchZipBlob) return;
|
||||||
const url = URL.createObjectURL(batchZipBlob);
|
const url = URL.createObjectURL(batchZipBlob);
|
||||||
@@ -893,7 +966,29 @@ export function ToolPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLayout breadcrumb={breadcrumb}>
|
<AppLayout breadcrumb={breadcrumb}>
|
||||||
<div className="flex flex-col w-full h-full">
|
<div
|
||||||
|
className="flex flex-col w-full h-full"
|
||||||
|
{...(!isNoDropzone
|
||||||
|
? {
|
||||||
|
onDragEnter: handleDragEnter,
|
||||||
|
onDragOver: handleDragOver,
|
||||||
|
onDragLeave: handleDragLeave,
|
||||||
|
onDrop: handlePageDrop,
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
>
|
||||||
|
{/* Page-level drop overlay */}
|
||||||
|
{isDraggingOver && !isNoDropzone && (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm">
|
||||||
|
<div className="text-center">
|
||||||
|
<Upload className="mx-auto h-12 w-12 text-primary animate-bounce" />
|
||||||
|
<p className="mt-3 text-lg font-medium">
|
||||||
|
{isMultiFileTool ? t.dropzone.dropToAdd : t.dropzone.dropToReplace}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Tool header */}
|
{/* Tool header */}
|
||||||
<div className="flex items-center gap-3 p-4 border-b border-border shrink-0">
|
<div className="flex items-center gap-3 p-4 border-b border-border shrink-0">
|
||||||
<div className="p-2 rounded-lg bg-primary text-primary-foreground">
|
<div className="p-2 rounded-lg bg-primary text-primary-foreground">
|
||||||
@@ -968,7 +1063,29 @@ export function ToolPage() {
|
|||||||
// Desktop layout: side-by-side
|
// Desktop layout: side-by-side
|
||||||
return (
|
return (
|
||||||
<AppLayout breadcrumb={breadcrumb}>
|
<AppLayout breadcrumb={breadcrumb}>
|
||||||
<div className="flex h-full w-full">
|
<div
|
||||||
|
className="flex h-full w-full"
|
||||||
|
{...(!isNoDropzone
|
||||||
|
? {
|
||||||
|
onDragEnter: handleDragEnter,
|
||||||
|
onDragOver: handleDragOver,
|
||||||
|
onDragLeave: handleDragLeave,
|
||||||
|
onDrop: handlePageDrop,
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
>
|
||||||
|
{/* Page-level drop overlay */}
|
||||||
|
{isDraggingOver && !isNoDropzone && (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm">
|
||||||
|
<div className="text-center">
|
||||||
|
<Upload className="mx-auto h-12 w-12 text-primary animate-bounce" />
|
||||||
|
<p className="mt-3 text-lg font-medium">
|
||||||
|
{isMultiFileTool ? t.dropzone.dropToAdd : t.dropzone.dropToReplace}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Tool Settings Panel */}
|
{/* Tool Settings Panel */}
|
||||||
<div className="settings-container settings-slide-in w-72 border-e border-border shrink-0 flex flex-col">
|
<div className="settings-container settings-slide-in w-72 border-e border-border shrink-0 flex flex-col">
|
||||||
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
||||||
|
|||||||
@@ -3230,6 +3230,8 @@ export const ar: TranslationKeys = {
|
|||||||
addUrlButton: "إضافة",
|
addUrlButton: "إضافة",
|
||||||
importMultipleUrls: "استيراد روابط متعددة...",
|
importMultipleUrls: "استيراد روابط متعددة...",
|
||||||
filesSelected: "تم اختيار {count} ملف",
|
filesSelected: "تم اختيار {count} ملف",
|
||||||
|
dropToAdd: "أفلت لإضافة ملفات",
|
||||||
|
dropToReplace: "أفلت لاستبدال الملف",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "النتيجة المعالجة",
|
processedResultAlt: "النتيجة المعالجة",
|
||||||
|
|||||||
@@ -3259,6 +3259,8 @@ export const de: TranslationKeys = {
|
|||||||
addUrlButton: "Hinzufuegen",
|
addUrlButton: "Hinzufuegen",
|
||||||
importMultipleUrls: "Mehrere URLs importieren...",
|
importMultipleUrls: "Mehrere URLs importieren...",
|
||||||
filesSelected: "{count} Dateien ausgewaehlt",
|
filesSelected: "{count} Dateien ausgewaehlt",
|
||||||
|
dropToAdd: "Ablegen, um Dateien hinzuzufuegen",
|
||||||
|
dropToReplace: "Ablegen, um Datei zu ersetzen",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Verarbeitetes Ergebnis",
|
processedResultAlt: "Verarbeitetes Ergebnis",
|
||||||
|
|||||||
@@ -3196,6 +3196,8 @@ export const en = {
|
|||||||
addUrlButton: "Add",
|
addUrlButton: "Add",
|
||||||
importMultipleUrls: "Import multiple URLs...",
|
importMultipleUrls: "Import multiple URLs...",
|
||||||
filesSelected: "{count} files selected",
|
filesSelected: "{count} files selected",
|
||||||
|
dropToAdd: "Drop to add files",
|
||||||
|
dropToReplace: "Drop to replace file",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Processed result",
|
processedResultAlt: "Processed result",
|
||||||
|
|||||||
@@ -3236,6 +3236,8 @@ export const es: TranslationKeys = {
|
|||||||
addUrlButton: "Agregar",
|
addUrlButton: "Agregar",
|
||||||
importMultipleUrls: "Importar multiples URLs...",
|
importMultipleUrls: "Importar multiples URLs...",
|
||||||
filesSelected: "{count} archivos seleccionados",
|
filesSelected: "{count} archivos seleccionados",
|
||||||
|
dropToAdd: "Suelta para agregar archivos",
|
||||||
|
dropToReplace: "Suelta para reemplazar archivo",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Resultado procesado",
|
processedResultAlt: "Resultado procesado",
|
||||||
|
|||||||
@@ -3257,6 +3257,8 @@ export const fr: TranslationKeys = {
|
|||||||
addUrlButton: "Ajouter",
|
addUrlButton: "Ajouter",
|
||||||
importMultipleUrls: "Importer plusieurs URLs...",
|
importMultipleUrls: "Importer plusieurs URLs...",
|
||||||
filesSelected: "{count} fichiers selectionnes",
|
filesSelected: "{count} fichiers selectionnes",
|
||||||
|
dropToAdd: "Deposer pour ajouter des fichiers",
|
||||||
|
dropToReplace: "Deposer pour remplacer le fichier",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Resultat traite",
|
processedResultAlt: "Resultat traite",
|
||||||
|
|||||||
@@ -3227,6 +3227,8 @@ export const hi: TranslationKeys = {
|
|||||||
addUrlButton: "जोड़ें",
|
addUrlButton: "जोड़ें",
|
||||||
importMultipleUrls: "कई URL इम्पोर्ट करें...",
|
importMultipleUrls: "कई URL इम्पोर्ट करें...",
|
||||||
filesSelected: "{count} फाइलें चुनी गईं",
|
filesSelected: "{count} फाइलें चुनी गईं",
|
||||||
|
dropToAdd: "फाइलें जोड़ने के लिए छोड़ें",
|
||||||
|
dropToReplace: "फाइल बदलने के लिए छोड़ें",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "प्रोसेस्ड परिणाम",
|
processedResultAlt: "प्रोसेस्ड परिणाम",
|
||||||
|
|||||||
@@ -3244,6 +3244,8 @@ export const id: TranslationKeys = {
|
|||||||
addUrlButton: "Tambah",
|
addUrlButton: "Tambah",
|
||||||
importMultipleUrls: "Impor beberapa URL...",
|
importMultipleUrls: "Impor beberapa URL...",
|
||||||
filesSelected: "{count} file dipilih",
|
filesSelected: "{count} file dipilih",
|
||||||
|
dropToAdd: "Lepas untuk menambahkan file",
|
||||||
|
dropToReplace: "Lepas untuk mengganti file",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Hasil yang diproses",
|
processedResultAlt: "Hasil yang diproses",
|
||||||
|
|||||||
@@ -3251,6 +3251,8 @@ export const it: TranslationKeys = {
|
|||||||
addUrlButton: "Aggiungi",
|
addUrlButton: "Aggiungi",
|
||||||
importMultipleUrls: "Importa piu URL...",
|
importMultipleUrls: "Importa piu URL...",
|
||||||
filesSelected: "{count} file selezionati",
|
filesSelected: "{count} file selezionati",
|
||||||
|
dropToAdd: "Rilascia per aggiungere file",
|
||||||
|
dropToReplace: "Rilascia per sostituire il file",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Risultato elaborato",
|
processedResultAlt: "Risultato elaborato",
|
||||||
|
|||||||
@@ -3200,6 +3200,8 @@ export const ja: TranslationKeys = {
|
|||||||
addUrlButton: "追加",
|
addUrlButton: "追加",
|
||||||
importMultipleUrls: "複数のURLをインポート...",
|
importMultipleUrls: "複数のURLをインポート...",
|
||||||
filesSelected: "{count}ファイルを選択済み",
|
filesSelected: "{count}ファイルを選択済み",
|
||||||
|
dropToAdd: "ファイルを追加するにはドロップ",
|
||||||
|
dropToReplace: "ファイルを置き換えるにはドロップ",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "処理結果",
|
processedResultAlt: "処理結果",
|
||||||
|
|||||||
@@ -3185,6 +3185,8 @@ export const ko: TranslationKeys = {
|
|||||||
addUrlButton: "추가",
|
addUrlButton: "추가",
|
||||||
importMultipleUrls: "여러 URL 가져오기...",
|
importMultipleUrls: "여러 URL 가져오기...",
|
||||||
filesSelected: "{count}개 파일 선택됨",
|
filesSelected: "{count}개 파일 선택됨",
|
||||||
|
dropToAdd: "파일을 추가하려면 놓기",
|
||||||
|
dropToReplace: "파일을 교체하려면 놓기",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "처리 결과",
|
processedResultAlt: "처리 결과",
|
||||||
|
|||||||
@@ -3247,6 +3247,8 @@ export const nl: TranslationKeys = {
|
|||||||
addUrlButton: "Toevoegen",
|
addUrlButton: "Toevoegen",
|
||||||
importMultipleUrls: "Meerdere URLs importeren...",
|
importMultipleUrls: "Meerdere URLs importeren...",
|
||||||
filesSelected: "{count} bestanden geselecteerd",
|
filesSelected: "{count} bestanden geselecteerd",
|
||||||
|
dropToAdd: "Laat los om bestanden toe te voegen",
|
||||||
|
dropToReplace: "Laat los om bestand te vervangen",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Verwerkt resultaat",
|
processedResultAlt: "Verwerkt resultaat",
|
||||||
|
|||||||
@@ -3254,6 +3254,8 @@ export const pl: TranslationKeys = {
|
|||||||
addUrlButton: "Dodaj",
|
addUrlButton: "Dodaj",
|
||||||
importMultipleUrls: "Importuj wiele adresów URL...",
|
importMultipleUrls: "Importuj wiele adresów URL...",
|
||||||
filesSelected: "Wybrano plików: {count}",
|
filesSelected: "Wybrano plików: {count}",
|
||||||
|
dropToAdd: "Upusc, aby dodac pliki",
|
||||||
|
dropToReplace: "Upusc, aby zastapic plik",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Wynik przetwarzania",
|
processedResultAlt: "Wynik przetwarzania",
|
||||||
|
|||||||
@@ -3247,6 +3247,8 @@ export const ptBR: TranslationKeys = {
|
|||||||
addUrlButton: "Adicionar",
|
addUrlButton: "Adicionar",
|
||||||
importMultipleUrls: "Importar multiplas URLs...",
|
importMultipleUrls: "Importar multiplas URLs...",
|
||||||
filesSelected: "{count} arquivos selecionados",
|
filesSelected: "{count} arquivos selecionados",
|
||||||
|
dropToAdd: "Solte para adicionar arquivos",
|
||||||
|
dropToReplace: "Solte para substituir arquivo",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Resultado processado",
|
processedResultAlt: "Resultado processado",
|
||||||
|
|||||||
@@ -3246,6 +3246,8 @@ export const ru: TranslationKeys = {
|
|||||||
addUrlButton: "Добавить",
|
addUrlButton: "Добавить",
|
||||||
importMultipleUrls: "Импорт нескольких URL...",
|
importMultipleUrls: "Импорт нескольких URL...",
|
||||||
filesSelected: "Выбрано файлов: {count}",
|
filesSelected: "Выбрано файлов: {count}",
|
||||||
|
dropToAdd: "Отпустите, чтобы добавить файлы",
|
||||||
|
dropToReplace: "Отпустите, чтобы заменить файл",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Результат обработки",
|
processedResultAlt: "Результат обработки",
|
||||||
|
|||||||
@@ -3241,6 +3241,8 @@ export const sv: TranslationKeys = {
|
|||||||
addUrlButton: "Lagg till",
|
addUrlButton: "Lagg till",
|
||||||
importMultipleUrls: "Importera flera URL:er...",
|
importMultipleUrls: "Importera flera URL:er...",
|
||||||
filesSelected: "{count} filer valda",
|
filesSelected: "{count} filer valda",
|
||||||
|
dropToAdd: "Slapp for att lagga till filer",
|
||||||
|
dropToReplace: "Slapp for att ersatta fil",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Bearbetat resultat",
|
processedResultAlt: "Bearbetat resultat",
|
||||||
|
|||||||
@@ -3218,6 +3218,8 @@ export const th: TranslationKeys = {
|
|||||||
addUrlButton: "เพิ่ม",
|
addUrlButton: "เพิ่ม",
|
||||||
importMultipleUrls: "นำเข้าหลาย URL...",
|
importMultipleUrls: "นำเข้าหลาย URL...",
|
||||||
filesSelected: "เลือกแล้ว {count} ไฟล์",
|
filesSelected: "เลือกแล้ว {count} ไฟล์",
|
||||||
|
dropToAdd: "วางเพื่อเพิ่มไฟล์",
|
||||||
|
dropToReplace: "วางเพื่อแทนที่ไฟล์",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "ผลลัพธ์ที่ประมวลผลแล้ว",
|
processedResultAlt: "ผลลัพธ์ที่ประมวลผลแล้ว",
|
||||||
|
|||||||
@@ -3251,6 +3251,8 @@ export const tr: TranslationKeys = {
|
|||||||
addUrlButton: "Ekle",
|
addUrlButton: "Ekle",
|
||||||
importMultipleUrls: "Birden fazla URL içe aktar...",
|
importMultipleUrls: "Birden fazla URL içe aktar...",
|
||||||
filesSelected: "{count} dosya seçildi",
|
filesSelected: "{count} dosya seçildi",
|
||||||
|
dropToAdd: "Dosya eklemek icin birakin",
|
||||||
|
dropToReplace: "Dosyayi degistirmek icin birakin",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "İşlenmiş sonuç",
|
processedResultAlt: "İşlenmiş sonuç",
|
||||||
|
|||||||
@@ -3247,6 +3247,8 @@ export const uk: TranslationKeys = {
|
|||||||
addUrlButton: "Додати",
|
addUrlButton: "Додати",
|
||||||
importMultipleUrls: "Імпорт кількох URL...",
|
importMultipleUrls: "Імпорт кількох URL...",
|
||||||
filesSelected: "Обрано файлів: {count}",
|
filesSelected: "Обрано файлів: {count}",
|
||||||
|
dropToAdd: "Відпустіть, щоб додати файли",
|
||||||
|
dropToReplace: "Відпустіть, щоб замінити файл",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Результат обробки",
|
processedResultAlt: "Результат обробки",
|
||||||
|
|||||||
@@ -3241,6 +3241,8 @@ export const vi: TranslationKeys = {
|
|||||||
addUrlButton: "Thêm",
|
addUrlButton: "Thêm",
|
||||||
importMultipleUrls: "Nhập nhiều URL...",
|
importMultipleUrls: "Nhập nhiều URL...",
|
||||||
filesSelected: "Đã chọn {count} tệp",
|
filesSelected: "Đã chọn {count} tệp",
|
||||||
|
dropToAdd: "Tha de them tep",
|
||||||
|
dropToReplace: "Tha de thay the tep",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "Kết quả đã xử lý",
|
processedResultAlt: "Kết quả đã xử lý",
|
||||||
|
|||||||
@@ -3169,6 +3169,8 @@ export const zhCN: TranslationKeys = {
|
|||||||
addUrlButton: "添加",
|
addUrlButton: "添加",
|
||||||
importMultipleUrls: "导入多个 URL...",
|
importMultipleUrls: "导入多个 URL...",
|
||||||
filesSelected: "已选择 {count} 个文件",
|
filesSelected: "已选择 {count} 个文件",
|
||||||
|
dropToAdd: "放下以添加文件",
|
||||||
|
dropToReplace: "放下以替换文件",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "处理结果",
|
processedResultAlt: "处理结果",
|
||||||
|
|||||||
@@ -3167,6 +3167,8 @@ export const zhTW: TranslationKeys = {
|
|||||||
addUrlButton: "加入",
|
addUrlButton: "加入",
|
||||||
importMultipleUrls: "匯入多個URL...",
|
importMultipleUrls: "匯入多個URL...",
|
||||||
filesSelected: "已選取{count}個檔案",
|
filesSelected: "已選取{count}個檔案",
|
||||||
|
dropToAdd: "放下以新增檔案",
|
||||||
|
dropToReplace: "放下以取代檔案",
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
reviewPanel: {
|
||||||
processedResultAlt: "處理結果",
|
processedResultAlt: "處理結果",
|
||||||
|
|||||||
Reference in New Issue
Block a user