From bf064161341191da3b54b664c91049879e1ccbe9 Mon Sep 17 00:00:00 2001 From: abhishek-gaonkar Date: Sat, 5 Oct 2024 15:23:35 +0530 Subject: [PATCH 1/6] Modified handlePaste functionality to save in temp folder --- common/electron-commands.ts | 3 + common/get-directory-from-path.ts | 9 +- electron/commands/batch-upscayl.ts | 2 +- electron/commands/custom-models-select.ts | 2 +- electron/commands/double-upscayl.ts | 2 +- electron/commands/get-models-list.ts | 2 +- electron/commands/image-upscayl.ts | 2 +- electron/commands/paste-image.ts | 63 +++++++++++ electron/index.ts | 3 + electron/utils/logit.ts | 2 +- renderer/components/main-content/index.tsx | 126 +++++++++++++++------ renderer/components/sidebar/index.tsx | 6 +- renderer/locales/en.json | 5 +- renderer/locales/es.json | 5 +- renderer/locales/fr.json | 5 +- renderer/locales/ja.json | 5 +- renderer/locales/ru.json | 5 +- renderer/locales/zh.json | 5 +- renderer/next-env.d.ts | 2 +- 19 files changed, 197 insertions(+), 57 deletions(-) create mode 100644 electron/commands/paste-image.ts diff --git a/common/electron-commands.ts b/common/electron-commands.ts index f1ebcb7..7eaba07 100644 --- a/common/electron-commands.ts +++ b/common/electron-commands.ts @@ -25,6 +25,9 @@ const ELECTRON_COMMANDS = { OS: "Get OS", SCALING_AND_CONVERTING: "Adding some finishing touches", UPSCAYL_ERROR: "Upscaling Error", + PASTE_IMAGE: "Paste Image from clipboard", + PASTE_IMAGE_SAVE_SUCCESS: "Clipboard Image saved successfully", + PASTE_IMAGE_SAVE_ERROR: "Clipboard Image save failed", } as const; export { ELECTRON_COMMANDS }; diff --git a/common/get-directory-from-path.ts b/common/get-directory-from-path.ts index bc27318..2cb4481 100644 --- a/common/get-directory-from-path.ts +++ b/common/get-directory-from-path.ts @@ -1,12 +1,15 @@ -export default function getDirectoryFromPath(filePath: string): string { +export default function getDirectoryFromPath( + filePath: string, + popFileName: boolean = true, +): string { // Define the path separator based on the operating system const separator = filePath.includes("/") ? "/" : "\\"; // Split the file path by the path separator const pathParts = filePath.split(separator); - // Remove the last element to get the directory - pathParts.pop(); + // Remove the last element to get the directory if popFileName is true + if (popFileName) pathParts.pop(); // Join the remaining parts back together to form the directory path const directoryPath = pathParts.join(separator); diff --git a/electron/commands/batch-upscayl.ts b/electron/commands/batch-upscayl.ts index 584552f..30646f2 100644 --- a/electron/commands/batch-upscayl.ts +++ b/electron/commands/batch-upscayl.ts @@ -11,7 +11,7 @@ import { spawnUpscayl } from "../utils/spawn-upscayl"; import { getBatchArguments } from "../utils/get-arguments"; import slash from "../utils/slash"; import { modelsPath } from "../utils/get-resource-paths"; -import { ELECTRON_COMMANDS } from "@common/electron-commands"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; import { BatchUpscaylPayload } from "../../common/types/types"; import showNotification from "../utils/show-notification"; import { DEFAULT_MODELS } from "../../common/models-list"; diff --git a/electron/commands/custom-models-select.ts b/electron/commands/custom-models-select.ts index a0ab7d0..ae0ef0e 100644 --- a/electron/commands/custom-models-select.ts +++ b/electron/commands/custom-models-select.ts @@ -5,7 +5,7 @@ import { } from "../utils/config-variables"; import logit from "../utils/logit"; import slash from "../utils/slash"; -import { ELECTRON_COMMANDS } from "@common/electron-commands"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; import getModels from "../utils/get-models"; import { getMainWindow } from "../main-window"; import settings from "electron-settings"; diff --git a/electron/commands/double-upscayl.ts b/electron/commands/double-upscayl.ts index 34a81e3..b38377c 100644 --- a/electron/commands/double-upscayl.ts +++ b/electron/commands/double-upscayl.ts @@ -14,7 +14,7 @@ import { } from "../utils/get-arguments"; import { modelsPath } from "../utils/get-resource-paths"; import logit from "../utils/logit"; -import { ELECTRON_COMMANDS } from "@common/electron-commands"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; import { DoubleUpscaylPayload } from "../../common/types/types"; import { ImageFormat } from "../types/types"; import showNotification from "../utils/show-notification"; diff --git a/electron/commands/get-models-list.ts b/electron/commands/get-models-list.ts index cce3e37..29499e9 100644 --- a/electron/commands/get-models-list.ts +++ b/electron/commands/get-models-list.ts @@ -1,4 +1,4 @@ -import { ELECTRON_COMMANDS } from "@common/electron-commands"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; import { getMainWindow } from "../main-window"; import { savedCustomModelsPath, diff --git a/electron/commands/image-upscayl.ts b/electron/commands/image-upscayl.ts index 9a7ce50..983b0e8 100644 --- a/electron/commands/image-upscayl.ts +++ b/electron/commands/image-upscayl.ts @@ -1,6 +1,6 @@ import fs from "fs"; import { modelsPath } from "../utils/get-resource-paths"; -import { ELECTRON_COMMANDS } from "@common/electron-commands"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; import { savedCustomModelsPath, setChildProcesses, diff --git a/electron/commands/paste-image.ts b/electron/commands/paste-image.ts new file mode 100644 index 0000000..27aa9e5 --- /dev/null +++ b/electron/commands/paste-image.ts @@ -0,0 +1,63 @@ +import { getMainWindow } from "../main-window"; +import logit from "../utils/logit"; +import fs from "fs"; +import { tmpdir, homedir } from "os"; +import path from "path"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; + +interface IClipboardFileParameters { + name: string; + extension: string; + size: number; + type: string; + encodedBuffer: string; +} + +const createTempFileFromClipboard = ( + appFolderPrefix: string, + inputFileParams: IClipboardFileParameters, + onSuccessCallback: (inputFilePath: string, outputFilePath: string) => void, + onErrorCallback: (error: Error) => void, +) => { + let tempDirectory = fs.mkdtempSync(path.join(tmpdir(), appFolderPrefix)); + let tempFilePath = path.join(tempDirectory, inputFileParams.name); + + fs.writeFile( + tempFilePath, + Buffer.from(inputFileParams.encodedBuffer, "base64"), + (err) => { + if (err) { + onErrorCallback(new Error("No permission to temp folder")); + return; + } + let homeDirectoryAsOutputFolder = homedir(); + onSuccessCallback(tempFilePath, homeDirectoryAsOutputFolder); + }, + ); +}; + +const pasteImage = (event, file: IClipboardFileParameters) => { + const mainWindow = getMainWindow(); + if (!mainWindow) return; + if (!file || !file.name || !file.encodedBuffer) return; + const appFolderPrefix = "upscayl"; + createTempFileFromClipboard( + appFolderPrefix, + file, + (imageFilePath, homeDirectory) => { + mainWindow.webContents.send(ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, [ + imageFilePath, + homeDirectory, + ]); + }, + (error) => { + logit(error.message); + mainWindow.webContents.send( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, + error.message, + ); + }, + ); +}; + +export default pasteImage; diff --git a/electron/index.ts b/electron/index.ts index 0bc3ebf..6f2ffd9 100644 --- a/electron/index.ts +++ b/electron/index.ts @@ -19,6 +19,7 @@ import doubleUpscayl from "./commands/double-upscayl"; import autoUpdate from "./commands/auto-update"; import { FEATURE_FLAGS } from "../common/feature-flags"; import settings from "electron-settings"; +import pasteImage from "./commands/paste-image"; // INITIALIZATION log.initialize({ preload: true }); @@ -94,6 +95,8 @@ ipcMain.on(ELECTRON_COMMANDS.FOLDER_UPSCAYL, batchUpscayl); ipcMain.on(ELECTRON_COMMANDS.DOUBLE_UPSCAYL, doubleUpscayl); +ipcMain.on(ELECTRON_COMMANDS.PASTE_IMAGE, pasteImage); + if (!FEATURE_FLAGS.APP_STORE_BUILD) { autoUpdater.on("update-downloaded", autoUpdate); } diff --git a/electron/utils/logit.ts b/electron/utils/logit.ts index b80a8f7..9ad1396 100644 --- a/electron/utils/logit.ts +++ b/electron/utils/logit.ts @@ -1,5 +1,5 @@ import log from "electron-log"; -import { ELECTRON_COMMANDS } from "@common/electron-commands"; +import { ELECTRON_COMMANDS } from "../../common/electron-commands"; import { getMainWindow } from "../main-window"; const logit = (...args: any) => { diff --git a/renderer/components/main-content/index.tsx b/renderer/components/main-content/index.tsx index 44298b9..e3fcdfa 100644 --- a/renderer/components/main-content/index.tsx +++ b/renderer/components/main-content/index.tsx @@ -1,6 +1,6 @@ "use client"; import useLogger from "../hooks/use-logger"; -import { useState, useMemo } from "react"; +import { useState, useMemo, useEffect } from "react"; import { ELECTRON_COMMANDS } from "@common/electron-commands"; import { useAtomValue, useSetAtom } from "jotai"; import { @@ -165,40 +165,103 @@ const MainContent = ({ const handlePaste = (e: React.ClipboardEvent) => { resetImagePaths(); e.preventDefault(); - const items = e.clipboardData.items; - const files = e.clipboardData.files; - if (items.length === 0 || files.length === 0) { - toast({ - title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), - description: t("ERRORS.INVALID_IMAGE_ERROR.ADDITIONAL_DESCRIPTION"), - }); - return; - } - const type = items[0].type; - const filePath = files[0].path; - const extension = files[0].name.split(".").at(-1); - logit("📋 Pasted file: ", JSON.stringify({ type, filePath, extension })); - if ( - !type.includes("image") && - !VALID_IMAGE_FORMATS.includes(extension.toLowerCase()) - ) { - toast({ - title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), - description: t("ERRORS.INVALID_IMAGE_ERROR.ADDITIONAL_DESCRIPTION"), - }); - } else { - setImagePath(filePath); - const dirname = getDirectoryFromPath(filePath); - logit("🗂 Setting output path: ", dirname); - if (!FEATURE_FLAGS.APP_STORE_BUILD) { - if (!rememberOutputFolder) { - setOutputPath(dirname); - } + if (e.clipboardData.files.length) { + const fileObject = e.clipboardData.files[0]; + const currentDate = new Date(Date.now()); + const currentTime = `${currentDate.getHours()}-${currentDate.getMinutes()}-${currentDate.getSeconds()}`; + const fileName = `${currentTime}-${fileObject.name}`; + const file = { + name: fileName, + extension: fileName.split(".").pop(), + size: fileObject.size, + type: fileObject.type.split("/")[0], + encodedBuffer: "", + }; + + logit( + "📋 Pasted file: ", + JSON.stringify({ + type: file.type, + name: file.name, + extension: file.extension, + }), + ); + + if ( + file.type === "image" && + VALID_IMAGE_FORMATS.includes(file.extension) + ) { + const reader = new FileReader(); + reader.onload = async (event) => { + const result = event.target?.result; + if (typeof result === "string") { + file.encodedBuffer = Buffer.from(result, "utf-8").toString( + "base64", + ); + } else if (result instanceof ArrayBuffer) { + file.encodedBuffer = Buffer.from(new Uint8Array(result)).toString( + "base64", + ); + } else { + logit("🚫 Invalid file pasted"); + toast({ + title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), + description: t( + "ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION", + ), + }); + } + window.electron.send(ELECTRON_COMMANDS.PASTE_IMAGE, file); + }; + reader.readAsArrayBuffer(fileObject); + } else { + logit("🚫 Invalid file pasted"); + toast({ + title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), + description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"), + }); } - validateImagePath(filePath); + } else { + logit("🚫 Invalid file pasted"); + toast({ + title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), + description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"), + }); } }; + useEffect(() => { + const handlePasteEvent = (e) => handlePaste(e); + window.addEventListener("paste", handlePasteEvent); + window.electron.on( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, + (_: any, output: string[]) => { + let [imageFilePath, homeDirectory] = output; + setImagePath(imageFilePath); + var dirname = getDirectoryFromPath(homeDirectory, false); + logit("🗂 Setting output path: ", dirname); + if (!FEATURE_FLAGS.APP_STORE_BUILD) { + if (!rememberOutputFolder) { + setOutputPath(dirname); + } + } + validateImagePath(imageFilePath); + }, + ); + window.electron.on( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, + (_: any, error: string) => { + toast({ + title: t("ERRORS.NO_IMAGE_ERROR.TITLE"), + description: error, + }); + }, + ); + return () => { + window.removeEventListener("paste", handlePasteEvent); + }; + }, []); + return (
diff --git a/renderer/components/sidebar/index.tsx b/renderer/components/sidebar/index.tsx index ecd62a3..fa61194 100644 --- a/renderer/components/sidebar/index.tsx +++ b/renderer/components/sidebar/index.tsx @@ -24,10 +24,10 @@ import { import { useToast } from "@/components/ui/use-toast"; import UpscaylSteps from "./upscayl-tab/upscayl-steps"; import SettingsTab from "./settings-tab"; -import Footer from "../footer"; +import Footer from "../Footer"; import { NewsModal } from "../news-modal"; -import Tabs from "../tabs"; -import Header from "../header"; +import Tabs from "../Tabs"; +import Header from "../Header"; import { ChevronLeftIcon } from "lucide-react"; import { logAtom } from "@/atoms/log-atom"; import { ELECTRON_COMMANDS } from "@common/electron-commands"; diff --git a/renderer/locales/en.json b/renderer/locales/en.json index 5041fa4..0694083 100644 --- a/renderer/locales/en.json +++ b/renderer/locales/en.json @@ -194,8 +194,9 @@ }, "INVALID_IMAGE_ERROR": { "TITLE": "Invalid Image", - "DESCRIPTION": "Please select an image with a valid extension like PNG, JPG, JPEG, JFIF or WEBP.", - "ADDITIONAL_DESCRIPTION": "Please drag and drop an image" + "DESCRIPTION": "Please select/paste an image with a valid extension like PNG, JPG, JPEG, JFIF or WEBP.", + "ADDITIONAL_DESCRIPTION": "Please drag and drop an image", + "CLIPBOARD_DESCRIPTION": "No Image file found in Clipboard to paste!" }, "NO_IMAGE_ERROR": { "TITLE": "No image selected", diff --git a/renderer/locales/es.json b/renderer/locales/es.json index 911d6ba..080701a 100644 --- a/renderer/locales/es.json +++ b/renderer/locales/es.json @@ -194,8 +194,9 @@ }, "INVALID_IMAGE_ERROR": { "TITLE": "Imagen inválida", - "DESCRIPTION": "Por favor, selecciona una imagen con una extensión válida como PNG, JPG, JPEG, JFIF o WEBP.", - "ADDITIONAL_DESCRIPTION": "Por favor, arrastra y suelta una imagen" + "DESCRIPTION": "Por favor, selecciona/pega una imagen con una extensión válida como PNG, JPG, JPEG, JFIF o WEBP.", + "ADDITIONAL_DESCRIPTION": "Por favor, arrastra y suelta una imagen", + "CLIPBOARD_DESCRIPTION": "¡No se encontró ningún archivo de imagen en el portapapeles para pegar!" }, "NO_IMAGE_ERROR": { "TITLE": "No se ha seleccionado imagen", diff --git a/renderer/locales/fr.json b/renderer/locales/fr.json index eb50172..37d8d0f 100644 --- a/renderer/locales/fr.json +++ b/renderer/locales/fr.json @@ -194,8 +194,9 @@ }, "INVALID_IMAGE_ERROR": { "TITLE": "Image invalide", - "DESCRIPTION": "Veuillez sélectionner une image avec une extension valide comme PNG, JPG, JPEG, JFIF ou WEBP.", - "ADDITIONAL_DESCRIPTION": "Veuillez glisser-déposer une image" + "DESCRIPTION": "Veuillez sélectionner/coller une image avec une extension valide comme PNG, JPG, JPEG, JFIF ou WEBP.", + "ADDITIONAL_DESCRIPTION": "Veuillez faire glisser et déposer une image", + "CLIPBOARD_DESCRIPTION": "Aucun fichier image trouvé dans le presse-papiers à coller !" }, "NO_IMAGE_ERROR": { "TITLE": "Aucune image sélectionnée", diff --git a/renderer/locales/ja.json b/renderer/locales/ja.json index b05d8fc..0fdea19 100644 --- a/renderer/locales/ja.json +++ b/renderer/locales/ja.json @@ -194,8 +194,9 @@ }, "INVALID_IMAGE_ERROR": { "TITLE": "無効な画像", - "DESCRIPTION": "PNG、JPG、JPEG、JFIF、またはWEBPなどの有効な拡張子を持つ画像を選択してください。", - "ADDITIONAL_DESCRIPTION": "画像をドラッグアンドドロップしてください" + "DESCRIPTION": "PNG、JPG、JPEG、JFIF、または WEBP のような有効な拡張子の画像を選択/貼り付けてください。", + "ADDITIONAL_DESCRIPTION": "画像をドラッグアンドドロップしてください", + "CLIPBOARD_DESCRIPTION": "クリップボードに貼り付ける画像ファイルが見つかりません!" }, "NO_IMAGE_ERROR": { "TITLE": "画像が選択されていません", diff --git a/renderer/locales/ru.json b/renderer/locales/ru.json index 00e58ce..90ecbdb 100644 --- a/renderer/locales/ru.json +++ b/renderer/locales/ru.json @@ -194,8 +194,9 @@ }, "INVALID_IMAGE_ERROR": { "TITLE": "Неверное изображение", - "DESCRIPTION": "Пожалуйста, выберите изображение с правильным расширением, таким как PNG, JPG, JPEG, JFIF или WEBP.", - "ADDITIONAL_DESCRIPTION": "Пожалуйста, перетащите изображение" + "DESCRIPTION": "Пожалуйста, выберите/вставьте изображение с допустимым расширением, таким как PNG, JPG, JPEG, JFIF или WEBP.", + "ADDITIONAL_DESCRIPTION": "Пожалуйста, перетащите и отпустите изображение", + "CLIPBOARD_DESCRIPTION": "Файл изображения не найден в буфере обмена для вставки!" }, "NO_IMAGE_ERROR": { "TITLE": "Изображение не выбрано", diff --git a/renderer/locales/zh.json b/renderer/locales/zh.json index 4cb4d7d..bc99ec9 100644 --- a/renderer/locales/zh.json +++ b/renderer/locales/zh.json @@ -194,8 +194,9 @@ }, "INVALID_IMAGE_ERROR": { "TITLE": "图片无效", - "DESCRIPTION": "请选择一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片", - "ADDITIONAL_DESCRIPTION": "请拖放图片" + "DESCRIPTION": "请选择/粘貼一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片", + "ADDITIONAL_DESCRIPTION": "請拖放一個圖像", + "CLIPBOARD_DESCRIPTION": "剪貼板中未找到可粘貼的圖像文件!" }, "NO_IMAGE_ERROR": { "TITLE": "未选择图片", diff --git a/renderer/next-env.d.ts b/renderer/next-env.d.ts index 4f11a03..a4a7b3f 100644 --- a/renderer/next-env.d.ts +++ b/renderer/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. From ebe8751c381efa24e0c2fb0ee8bab704c65d087a Mon Sep 17 00:00:00 2001 From: abhishek-gaonkar Date: Sat, 5 Oct 2024 16:29:45 +0530 Subject: [PATCH 2/6] fix: useEffect re-render param; right-pane add ctrl+v info --- renderer/components/main-content/index.tsx | 2 +- renderer/components/main-content/instructions-card.tsx | 2 ++ renderer/locales/en.json | 3 ++- renderer/locales/es.json | 3 ++- renderer/locales/fr.json | 3 ++- renderer/locales/ja.json | 3 ++- renderer/locales/ru.json | 3 ++- renderer/locales/zh.json | 3 ++- 8 files changed, 15 insertions(+), 7 deletions(-) diff --git a/renderer/components/main-content/index.tsx b/renderer/components/main-content/index.tsx index e3fcdfa..42c4720 100644 --- a/renderer/components/main-content/index.tsx +++ b/renderer/components/main-content/index.tsx @@ -260,7 +260,7 @@ const MainContent = ({ return () => { window.removeEventListener("paste", handlePasteEvent); }; - }, []); + }, [t]); return (
{t("APP.RIGHT_PANE_INFO.SELECT_IMAGES_DESCRIPTION")} +
+ {t("APP.RIGHT_PANE_INFO.PASTE_IMAGE_DESCRIPTION")}

)}

Upscayl v{version}

diff --git a/renderer/locales/en.json b/renderer/locales/en.json index 0694083..02e6bd0 100644 --- a/renderer/locales/en.json +++ b/renderer/locales/en.json @@ -147,7 +147,8 @@ "SELECT_FOLDER": "Select a Folder to Upscayl", "SELECT_IMAGE": "Select an Image to Upscayl", "SELECT_FOLDER_DESCRIPTION": "Make sure that the folder doesn't contain anything except PNG, JPG, JPEG & WEBP images.", - "SELECT_IMAGES_DESCRIPTION": "Select or drag and drop a PNG, JPG, JPEG or WEBP image." + "SELECT_IMAGES_DESCRIPTION": "Select or drag and drop a PNG, JPG, JPEG or WEBP image.", + "PASTE_IMAGE_DESCRIPTION": "Hit Ctrl + V or Cmd + V to Paste image from Clipboard" }, "PROGRESS": { "PROCESSING_TITLE": "Processing the image...", diff --git a/renderer/locales/es.json b/renderer/locales/es.json index 080701a..ae20c8c 100644 --- a/renderer/locales/es.json +++ b/renderer/locales/es.json @@ -147,7 +147,8 @@ "SELECT_FOLDER": "Selecciona una carpeta para aumentar", "SELECT_IMAGE": "Selecciona una imagen para aumentar", "SELECT_FOLDER_DESCRIPTION": "Asegúrate de que la carpeta no contenga nada excepto imágenes PNG, JPG, JPEG y WEBP.", - "SELECT_IMAGES_DESCRIPTION": "Selecciona o arrastra y suelta una imagen PNG, JPG, JPEG o WEBP." + "SELECT_IMAGES_DESCRIPTION": "Selecciona o arrastra y suelta una imagen PNG, JPG, JPEG o WEBP.", + "PASTE_IMAGE_DESCRIPTION": "Presiona Ctrl + V o Cmd + V para pegar la imagen desde el portapapeles" }, "PROGRESS": { "PROCESSING_TITLE": "Procesando la imagen...", diff --git a/renderer/locales/fr.json b/renderer/locales/fr.json index 37d8d0f..d1f75d1 100644 --- a/renderer/locales/fr.json +++ b/renderer/locales/fr.json @@ -147,7 +147,8 @@ "SELECT_FOLDER": "Sélectionnez un dossier à suréchantillonner", "SELECT_IMAGE": "Sélectionnez une image à suréchantillonner", "SELECT_FOLDER_DESCRIPTION": "Assurez-vous que le dossier ne contient rien d'autre que des images PNG, JPG, JPEG et WEBP.", - "SELECT_IMAGES_DESCRIPTION": "Sélectionnez ou glissez-déposez une image PNG, JPG, JPEG ou WEBP." + "SELECT_IMAGES_DESCRIPTION": "Sélectionnez ou glissez-déposez une image PNG, JPG, JPEG ou WEBP.", + "PASTE_IMAGE_DESCRIPTION": "Appuyez sur Ctrl + V ou Cmd + V pour coller l'image depuis le presse-papiers" }, "PROGRESS": { "PROCESSING_TITLE": "Traitement de l'image...", diff --git a/renderer/locales/ja.json b/renderer/locales/ja.json index 0fdea19..f60bb87 100644 --- a/renderer/locales/ja.json +++ b/renderer/locales/ja.json @@ -147,7 +147,8 @@ "SELECT_FOLDER": "Upscaylするフォルダを選択", "SELECT_IMAGE": "Upscaylする画像を選択", "SELECT_FOLDER_DESCRIPTION": "フォルダにはPNG、JPG、JPEG、WEBPの画像以外のものが含まれていないことを確認してください。", - "SELECT_IMAGES_DESCRIPTION": "PNG、JPG、JPEG、またはWEBP画像を選択するか、ドラッグアンドドロップしてください。" + "SELECT_IMAGES_DESCRIPTION": "PNG、JPG、JPEG、またはWEBP画像を選択するか、ドラッグアンドドロップしてください。", + "PASTE_IMAGE_DESCRIPTION": "Ctrl + V または Cmd + V を押してクリップボードから画像を貼り付けます" }, "PROGRESS": { "PROCESSING_TITLE": "画像を処理中...", diff --git a/renderer/locales/ru.json b/renderer/locales/ru.json index 90ecbdb..ecce345 100644 --- a/renderer/locales/ru.json +++ b/renderer/locales/ru.json @@ -147,7 +147,8 @@ "SELECT_FOLDER": "Выберите папку для увеличения", "SELECT_IMAGE": "Выберите изображение для увеличения", "SELECT_FOLDER_DESCRIPTION": "Убедитесь, что в папке нет ничего, кроме изображений PNG, JPG, JPEG и WEBP.", - "SELECT_IMAGES_DESCRIPTION": "Выберите или перетащите изображение в формате PNG, JPG, JPEG или WEBP." + "SELECT_IMAGES_DESCRIPTION": "Выберите или перетащите изображение в формате PNG, JPG, JPEG или WEBP.", + "PASTE_IMAGE_DESCRIPTION": "Нажмите Ctrl + V или Cmd + V, чтобы вставить изображение из буфера обмена" }, "PROGRESS": { "PROCESSING_TITLE": "Обработка изображения...", diff --git a/renderer/locales/zh.json b/renderer/locales/zh.json index bc99ec9..b8ea7b6 100644 --- a/renderer/locales/zh.json +++ b/renderer/locales/zh.json @@ -147,7 +147,8 @@ "SELECT_FOLDER": "选择要增强的文件夹", "SELECT_IMAGE": "选择要增强的图片", "SELECT_FOLDER_DESCRIPTION": "文件夹中只支持 PNG、JPG、JPEG 和 WEBP 图片。", - "SELECT_IMAGES_DESCRIPTION": "选择或拖放 PNG、JPG、JPEG 或 WEBP 图片。" + "SELECT_IMAGES_DESCRIPTION": "选择或拖放 PNG、JPG、JPEG 或 WEBP 图片。", + "PASTE_IMAGE_DESCRIPTION": "按 Ctrl + V 或 Cmd + V 以從剪貼簿貼上圖像" }, "PROGRESS": { "PROCESSING_TITLE": "正在处理图片...", From 177b555a55504b1a0399b540871d5a7ff9806dff Mon Sep 17 00:00:00 2001 From: abhishek-gaonkar Date: Thu, 24 Oct 2024 17:38:51 +0530 Subject: [PATCH 3/6] MOD: Refactor few codes; Suggestions --- electron/commands/paste-image.ts | 60 +++------ renderer/components/main-content/index.tsx | 149 +++++++++++---------- renderer/locales/en.json | 4 + renderer/locales/es.json | 4 + renderer/locales/fr.json | 4 + renderer/locales/ja.json | 4 + renderer/locales/ru.json | 4 + renderer/locales/zh.json | 4 + 8 files changed, 121 insertions(+), 112 deletions(-) diff --git a/electron/commands/paste-image.ts b/electron/commands/paste-image.ts index 27aa9e5..1107647 100644 --- a/electron/commands/paste-image.ts +++ b/electron/commands/paste-image.ts @@ -1,63 +1,45 @@ import { getMainWindow } from "../main-window"; import logit from "../utils/logit"; import fs from "fs"; -import { tmpdir, homedir } from "os"; import path from "path"; import { ELECTRON_COMMANDS } from "../../common/electron-commands"; interface IClipboardFileParameters { name: string; + path: string; extension: string; size: number; type: string; encodedBuffer: string; } -const createTempFileFromClipboard = ( - appFolderPrefix: string, +const createTempFileFromClipboard = async ( inputFileParams: IClipboardFileParameters, - onSuccessCallback: (inputFilePath: string, outputFilePath: string) => void, - onErrorCallback: (error: Error) => void, -) => { - let tempDirectory = fs.mkdtempSync(path.join(tmpdir(), appFolderPrefix)); - let tempFilePath = path.join(tempDirectory, inputFileParams.name); +): Promise => { + const tempFilePath = path.join(inputFileParams.path, inputFileParams.name); + const buffer = Buffer.from(inputFileParams.encodedBuffer, "base64"); - fs.writeFile( - tempFilePath, - Buffer.from(inputFileParams.encodedBuffer, "base64"), - (err) => { - if (err) { - onErrorCallback(new Error("No permission to temp folder")); - return; - } - let homeDirectoryAsOutputFolder = homedir(); - onSuccessCallback(tempFilePath, homeDirectoryAsOutputFolder); - }, - ); + await fs.promises.writeFile(tempFilePath, buffer); + return tempFilePath; }; -const pasteImage = (event, file: IClipboardFileParameters) => { +const pasteImage = async (event, file: IClipboardFileParameters) => { const mainWindow = getMainWindow(); if (!mainWindow) return; if (!file || !file.name || !file.encodedBuffer) return; - const appFolderPrefix = "upscayl"; - createTempFileFromClipboard( - appFolderPrefix, - file, - (imageFilePath, homeDirectory) => { - mainWindow.webContents.send(ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, [ - imageFilePath, - homeDirectory, - ]); - }, - (error) => { - logit(error.message); - mainWindow.webContents.send( - ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, - error.message, - ); - }, - ); + try { + const imageFilePath = await createTempFileFromClipboard(file); + mainWindow.webContents.send( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, + imageFilePath, + ); + } catch (error: any) { + logit(error.message); + mainWindow.webContents.send( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, + error.message, + ); + } }; export default pasteImage; diff --git a/renderer/components/main-content/index.tsx b/renderer/components/main-content/index.tsx index 216a689..e158abf 100644 --- a/renderer/components/main-content/index.tsx +++ b/renderer/components/main-content/index.tsx @@ -2,7 +2,7 @@ import useLogger from "../hooks/use-logger"; import { useState, useMemo, useEffect } from "react"; import { ELECTRON_COMMANDS } from "@common/electron-commands"; -import { useAtomValue, useSetAtom } from "jotai"; +import { useAtom, useAtomValue } from "jotai"; import { batchModeAtom, lensSizeAtom, @@ -63,7 +63,7 @@ const MainContent = ({ const { toast } = useToast(); const version = useUpscaylVersion(); - const setOutputPath = useSetAtom(savedOutputPathAtom); + const [outputPath, setOutputPath] = useAtom(savedOutputPathAtom); const progress = useAtomValue(progressAtom); const batchMode = useAtomValue(batchModeAtom); @@ -163,57 +163,66 @@ const MainContent = ({ }; const handlePaste = (e: React.ClipboardEvent) => { - resetImagePaths(); e.preventDefault(); - if (e.clipboardData.files.length) { - const fileObject = e.clipboardData.files[0]; - const currentDate = new Date(Date.now()); - const currentTime = `${currentDate.getHours()}-${currentDate.getMinutes()}-${currentDate.getSeconds()}`; - const fileName = `${currentTime}-${fileObject.name}`; - const file = { - name: fileName, - extension: fileName.split(".").pop() as ImageFormat, - size: fileObject.size, - type: fileObject.type.split("/")[0], - encodedBuffer: "", - }; - - logit( - "📋 Pasted file: ", - JSON.stringify({ - type: file.type, - name: file.name, - extension: file.extension, - }), - ); - - if ( - file.type === "image" && - VALID_IMAGE_FORMATS.includes(file.extension) - ) { - const reader = new FileReader(); - reader.onload = async (event) => { - const result = event.target?.result; - if (typeof result === "string") { - file.encodedBuffer = Buffer.from(result, "utf-8").toString( - "base64", - ); - } else if (result instanceof ArrayBuffer) { - file.encodedBuffer = Buffer.from(new Uint8Array(result)).toString( - "base64", - ); - } else { - logit("🚫 Invalid file pasted"); - toast({ - title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), - description: t( - "ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION", - ), - }); - } - window.electron.send(ELECTRON_COMMANDS.PASTE_IMAGE, file); + if (outputPath) { + resetImagePaths(); + if (e.clipboardData.files.length) { + const fileObject = e.clipboardData.files[0]; + const currentDate = new Date(Date.now()); + const currentTime = `${currentDate.getHours()}-${currentDate.getMinutes()}-${currentDate.getSeconds()}`; + const fileName = `.temp-${currentTime}-${fileObject.name || "image"}`; + const file = { + name: fileName, + path: outputPath, + extension: fileName.split(".").pop() as ImageFormat, + size: fileObject.size, + type: fileObject.type.split("/")[0], + encodedBuffer: "", }; - reader.readAsArrayBuffer(fileObject); + + logit( + "📋 Pasted file: ", + JSON.stringify({ + name: file.name, + path: file.path, + extension: file.extension, + }), + ); + + if ( + file.type === "image" && + VALID_IMAGE_FORMATS.includes(file.extension) + ) { + const reader = new FileReader(); + reader.onload = async (event) => { + const result = event.target?.result; + if (typeof result === "string") { + file.encodedBuffer = Buffer.from(result, "utf-8").toString( + "base64", + ); + } else if (result instanceof ArrayBuffer) { + file.encodedBuffer = Buffer.from(new Uint8Array(result)).toString( + "base64", + ); + } else { + logit("🚫 Invalid file pasted"); + toast({ + title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), + description: t( + "ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION", + ), + }); + } + window.electron.send(ELECTRON_COMMANDS.PASTE_IMAGE, file); + }; + reader.readAsArrayBuffer(fileObject); + } else { + logit("🚫 Invalid file pasted"); + toast({ + title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), + description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"), + }); + } } else { logit("🚫 Invalid file pasted"); toast({ @@ -222,45 +231,39 @@ const MainContent = ({ }); } } else { - logit("🚫 Invalid file pasted"); toast({ - title: t("ERRORS.INVALID_IMAGE_ERROR.TITLE"), - description: t("ERRORS.INVALID_IMAGE_ERROR.CLIPBOARD_DESCRIPTION"), + title: t("ERRORS.NO_OUTPUT_FOLDER_ERROR.TITLE"), + description: t("ERRORS.NO_OUTPUT_FOLDER_ERROR.DESCRIPTION"), }); } }; useEffect(() => { + // Events const handlePasteEvent = (e) => handlePaste(e); + const handlePasteImageSaveSuccess = (_: any, imageFilePath: string) => { + setImagePath(imageFilePath); + validateImagePath(imageFilePath); + }; + const handlePasteImageSaveError = (_: any, error: string) => { + toast({ + title: t("ERRORS.NO_IMAGE_ERROR.TITLE"), + description: error, + }); + }; window.addEventListener("paste", handlePasteEvent); window.electron.on( ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, - (_: any, output: string[]) => { - let [imageFilePath, homeDirectory] = output; - setImagePath(imageFilePath); - var dirname = getDirectoryFromPath(homeDirectory, false); - logit("🗂 Setting output path: ", dirname); - if (!FEATURE_FLAGS.APP_STORE_BUILD) { - if (!rememberOutputFolder) { - setOutputPath(dirname); - } - } - validateImagePath(imageFilePath); - }, + handlePasteImageSaveSuccess, ); window.electron.on( ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, - (_: any, error: string) => { - toast({ - title: t("ERRORS.NO_IMAGE_ERROR.TITLE"), - description: error, - }); - }, + handlePasteImageSaveError, ); return () => { window.removeEventListener("paste", handlePasteEvent); }; - }, [t]); + }, [t, outputPath]); return (
Date: Thu, 24 Oct 2024 22:29:41 +0530 Subject: [PATCH 4/6] ADD: File Extension check in Backend --- electron/commands/paste-image.ts | 37 ++++++++++++++++++++++---------- electron/types/types.d.ts | 4 +++- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/electron/commands/paste-image.ts b/electron/commands/paste-image.ts index 1107647..323c9a0 100644 --- a/electron/commands/paste-image.ts +++ b/electron/commands/paste-image.ts @@ -3,16 +3,21 @@ import logit from "../utils/logit"; import fs from "fs"; import path from "path"; import { ELECTRON_COMMANDS } from "../../common/electron-commands"; +import { ImageFormat, imageFormats } from "../types/types"; interface IClipboardFileParameters { name: string; path: string; - extension: string; + extension: ImageFormat; size: number; type: string; encodedBuffer: string; } +const isImageFormatValid = (format: string): format is ImageFormat => { + return (imageFormats as readonly string[]).includes(format); +}; + const createTempFileFromClipboard = async ( inputFileParams: IClipboardFileParameters, ): Promise => { @@ -23,21 +28,31 @@ const createTempFileFromClipboard = async ( return tempFilePath; }; -const pasteImage = async (event, file: IClipboardFileParameters) => { +const pasteImage = async ( + event: Electron.IpcMainEvent, + file: IClipboardFileParameters, +) => { const mainWindow = getMainWindow(); if (!mainWindow) return; if (!file || !file.name || !file.encodedBuffer) return; - try { - const imageFilePath = await createTempFileFromClipboard(file); - mainWindow.webContents.send( - ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, - imageFilePath, - ); - } catch (error: any) { - logit(error.message); + if (isImageFormatValid(file.extension)) { + try { + const imageFilePath = await createTempFileFromClipboard(file); + mainWindow.webContents.send( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_SUCCESS, + imageFilePath, + ); + } catch (error: any) { + logit(error.message); + mainWindow.webContents.send( + ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, + error.message, + ); + } + } else { mainWindow.webContents.send( ELECTRON_COMMANDS.PASTE_IMAGE_SAVE_ERROR, - error.message, + "Unsupported Image Format", ); } }; diff --git a/electron/types/types.d.ts b/electron/types/types.d.ts index 8a7f4c0..77c1da5 100644 --- a/electron/types/types.d.ts +++ b/electron/types/types.d.ts @@ -1 +1,3 @@ -export type ImageFormat = "png" | "jpg" | "webp"; +export const imageFormats = ["png", "jpg", "jpeg", "webp"] as const; + +export type ImageFormat = (typeof imageFormats)[number]; From 5a05883058f69f40fb9802373dfb2cded425321c Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Fri, 1 Nov 2024 14:46:19 +0000 Subject: [PATCH 5/6] make han characters simplified --- renderer/locales/zh.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/renderer/locales/zh.json b/renderer/locales/zh.json index c2b715b..9bbe3f6 100644 --- a/renderer/locales/zh.json +++ b/renderer/locales/zh.json @@ -148,7 +148,7 @@ "SELECT_IMAGE": "选择要增强的图片", "SELECT_FOLDER_DESCRIPTION": "文件夹中只支持 PNG、JPG、JPEG 和 WEBP 图片。", "SELECT_IMAGES_DESCRIPTION": "选择或拖放 PNG、JPG、JPEG 或 WEBP 图片。", - "PASTE_IMAGE_DESCRIPTION": "按 Ctrl + V 或 Cmd + V 以從剪貼簿貼上圖像" + "PASTE_IMAGE_DESCRIPTION": "按 Ctrl + V 或 Cmd + V 来粘贴图像" }, "PROGRESS": { "PROCESSING_TITLE": "正在处理图片...", @@ -200,8 +200,8 @@ "INVALID_IMAGE_ERROR": { "TITLE": "图片无效", "DESCRIPTION": "请选择/粘貼一个扩展名为 PNG、JPG、JPEG、JFIF 或 WEBP 的有效图片", - "ADDITIONAL_DESCRIPTION": "請拖放一個圖像", - "CLIPBOARD_DESCRIPTION": "剪貼板中未找到可粘貼的圖像文件!" + "ADDITIONAL_DESCRIPTION": "请拖放一张图片", + "CLIPBOARD_DESCRIPTION": "剪贴板中未找到可粘贴的图像文件!" }, "NO_IMAGE_ERROR": { "TITLE": "未选择图片", From cb7b45ab59420e118156293e0439f7cd455dad6d Mon Sep 17 00:00:00 2001 From: abhishek-gaonkar Date: Sun, 10 Nov 2024 14:57:04 +0530 Subject: [PATCH 6/6] FIX: types issue during runtime --- common/image-formats.ts | 1 + electron/commands/paste-image.ts | 3 ++- electron/types/types.d.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 common/image-formats.ts diff --git a/common/image-formats.ts b/common/image-formats.ts new file mode 100644 index 0000000..4d489d3 --- /dev/null +++ b/common/image-formats.ts @@ -0,0 +1 @@ +export const imageFormats = ["png", "jpg", "jpeg", "webp"] as const; diff --git a/electron/commands/paste-image.ts b/electron/commands/paste-image.ts index 323c9a0..fe0ea1d 100644 --- a/electron/commands/paste-image.ts +++ b/electron/commands/paste-image.ts @@ -3,7 +3,8 @@ import logit from "../utils/logit"; import fs from "fs"; import path from "path"; import { ELECTRON_COMMANDS } from "../../common/electron-commands"; -import { ImageFormat, imageFormats } from "../types/types"; +import { ImageFormat } from "../types/types"; +import { imageFormats } from "../../common/image-formats"; interface IClipboardFileParameters { name: string; diff --git a/electron/types/types.d.ts b/electron/types/types.d.ts index 77c1da5..3883004 100644 --- a/electron/types/types.d.ts +++ b/electron/types/types.d.ts @@ -1,3 +1,3 @@ -export const imageFormats = ["png", "jpg", "jpeg", "webp"] as const; +import { imageFormats } from "../../common/image-formats"; export type ImageFormat = (typeof imageFormats)[number];