mirror of
https://github.com/upscayl/upscayl.git
synced 2024-11-24 07:30:19 +01:00
Converted to Typescript
This commit is contained in:
parent
3e622c2158
commit
a2e7cc16a7
@ -4,15 +4,15 @@
|
|||||||
We're putting resources/{os}/bin from project inside resources/bin of electron. Same for the models directory as well.
|
We're putting resources/{os}/bin from project inside resources/bin of electron. Same for the models directory as well.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { join, dirname, resolve } = require("path");
|
import { join, dirname, resolve } from "path";
|
||||||
const { getPlatform } = require("./getPlatform");
|
import { getPlatform } from "./getPlatform";
|
||||||
const isDev = require("electron-is-dev");
|
import isDev from "electron-is-dev";
|
||||||
const { app } = require("electron");
|
import { app } from "electron";
|
||||||
|
|
||||||
const appRootDir = app.getAppPath();
|
const appRootDir = app.getAppPath();
|
||||||
|
|
||||||
const binariesPath = isDev
|
const binariesPath = isDev
|
||||||
? join(appRootDir, "resources", getPlatform(), "bin")
|
? join(appRootDir, "resources", getPlatform()!, "bin")
|
||||||
: join(dirname(appRootDir), "bin");
|
: join(dirname(appRootDir), "bin");
|
||||||
|
|
||||||
const execPath = (execName) =>
|
const execPath = (execName) =>
|
||||||
@ -22,4 +22,4 @@ const modelsPath = isDev
|
|||||||
? resolve(join(appRootDir, "resources", "models"))
|
? resolve(join(appRootDir, "resources", "models"))
|
||||||
: resolve(join(dirname(appRootDir), "models"));
|
: resolve(join(dirname(appRootDir), "models"));
|
||||||
|
|
||||||
module.exports = { execPath, modelsPath };
|
export { execPath, modelsPath };
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -14,4 +14,4 @@ const commands = {
|
|||||||
OPEN_FOLDER: "Open Folder",
|
OPEN_FOLDER: "Open Folder",
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = commands;
|
export default commands;
|
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { platform } = require("os");
|
import { platform } from "os";
|
||||||
|
|
||||||
const getPlatform = () => {
|
const getPlatform = () => {
|
||||||
switch (platform()) {
|
switch (platform()) {
|
||||||
@ -18,4 +18,4 @@ const getPlatform = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { getPlatform };
|
export { getPlatform };
|
@ -1,27 +1,28 @@
|
|||||||
// Native
|
// Native
|
||||||
const { join, parse } = require("path");
|
import { join, parse } from "path";
|
||||||
const { format } = require("url");
|
import { format } from "url";
|
||||||
const { spawn } = require("child_process");
|
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
|
||||||
const fs = require("fs");
|
import fs from "fs";
|
||||||
const sizeOf = require("image-size");
|
import sizeOf from "image-size";
|
||||||
const { autoUpdater } = require("electron-updater");
|
import { autoUpdater } from "electron-updater";
|
||||||
const { getPlatform } = require("./getPlatform");
|
import { getPlatform } from "./getPlatform";
|
||||||
|
|
||||||
const { execPath, modelsPath } = require("./binaries");
|
import { execPath, modelsPath } from "./binaries";
|
||||||
|
|
||||||
// Packages
|
// Packages
|
||||||
const {
|
import {
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
app,
|
app,
|
||||||
ipcMain,
|
ipcMain,
|
||||||
dialog,
|
dialog,
|
||||||
ipcRenderer,
|
ipcRenderer,
|
||||||
shell,
|
shell,
|
||||||
} = require("electron");
|
MessageBoxOptions,
|
||||||
|
} from "electron";
|
||||||
|
|
||||||
const isDev = require("electron-is-dev");
|
import isDev from "electron-is-dev";
|
||||||
const prepareNext = require("electron-next");
|
import prepareNext from "electron-next";
|
||||||
const commands = require("./commands");
|
import commands from "./commands";
|
||||||
|
|
||||||
// Prepare the renderer once the app is ready
|
// Prepare the renderer once the app is ready
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
@ -42,7 +43,6 @@ app.on("ready", async () => {
|
|||||||
show: false,
|
show: false,
|
||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
autoHideMenuBar: true,
|
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
webSecurity: false,
|
webSecurity: false,
|
||||||
preload: join(__dirname, "preload.js"),
|
preload: join(__dirname, "preload.js"),
|
||||||
@ -141,7 +141,7 @@ ipcMain.on(commands.DOUBLE_UPSCAYL, async (event, payload) => {
|
|||||||
model,
|
model,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: null,
|
cwd: undefined,
|
||||||
detached: false,
|
detached: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -180,7 +180,7 @@ ipcMain.on(commands.DOUBLE_UPSCAYL, async (event, payload) => {
|
|||||||
execPath("realesrgan"),
|
execPath("realesrgan"),
|
||||||
["-i", outFile, "-o", outFile, "-s", 4, "-m", modelsPath, "-n", model],
|
["-i", outFile, "-o", outFile, "-s", 4, "-m", modelsPath, "-n", model],
|
||||||
{
|
{
|
||||||
cwd: null,
|
cwd: undefined,
|
||||||
detached: false,
|
detached: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -251,7 +251,7 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
|
|||||||
// If already upscayled, just output that file
|
// If already upscayled, just output that file
|
||||||
mainWindow.webContents.send(commands.UPSCAYL_DONE, outFile);
|
mainWindow.webContents.send(commands.UPSCAYL_DONE, outFile);
|
||||||
} else {
|
} else {
|
||||||
let upscayl = null;
|
let upscayl: ChildProcessWithoutNullStreams | null = null;
|
||||||
switch (model) {
|
switch (model) {
|
||||||
case "realesrgan-x4plus":
|
case "realesrgan-x4plus":
|
||||||
case "realesrgan-x4plus-anime":
|
case "realesrgan-x4plus-anime":
|
||||||
@ -270,7 +270,7 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
|
|||||||
model,
|
model,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: null,
|
cwd: undefined,
|
||||||
detached: false,
|
detached: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -290,7 +290,7 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
|
|||||||
modelsPath + "/" + model,
|
modelsPath + "/" + model,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: null,
|
cwd: undefined,
|
||||||
detached: false,
|
detached: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -298,7 +298,7 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let failed = false;
|
let failed = false;
|
||||||
upscayl.stderr.on("data", (data) => {
|
upscayl?.stderr.on("data", (data) => {
|
||||||
console.log(
|
console.log(
|
||||||
"🚀 => upscayl.stderr.on => stderr.toString()",
|
"🚀 => upscayl.stderr.on => stderr.toString()",
|
||||||
data.toString()
|
data.toString()
|
||||||
@ -310,14 +310,14 @@ ipcMain.on(commands.UPSCAYL, async (event, payload) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
upscayl.on("error", (data) => {
|
upscayl?.on("error", (data) => {
|
||||||
mainWindow.webContents.send(commands.UPSCAYL_PROGRESS, data.toString());
|
mainWindow.webContents.send(commands.UPSCAYL_PROGRESS, data.toString());
|
||||||
failed = true;
|
failed = true;
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send done comamnd when
|
// Send done comamnd when
|
||||||
upscayl.on("close", (code) => {
|
upscayl?.on("close", (code) => {
|
||||||
if (failed !== true) {
|
if (failed !== true) {
|
||||||
console.log("Done upscaling");
|
console.log("Done upscaling");
|
||||||
mainWindow.webContents.send(commands.UPSCAYL_DONE, outFile);
|
mainWindow.webContents.send(commands.UPSCAYL_DONE, outFile);
|
||||||
@ -338,7 +338,7 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
|||||||
fs.mkdirSync(outputDir, { recursive: true });
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
}
|
}
|
||||||
// UPSCALE
|
// UPSCALE
|
||||||
let upscayl = null;
|
let upscayl: ChildProcessWithoutNullStreams | null = null;
|
||||||
switch (model) {
|
switch (model) {
|
||||||
case "realesrgan-x4plus":
|
case "realesrgan-x4plus":
|
||||||
case "realesrgan-x4plus-anime":
|
case "realesrgan-x4plus-anime":
|
||||||
@ -357,7 +357,7 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
|||||||
model,
|
model,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: null,
|
cwd: undefined,
|
||||||
detached: false,
|
detached: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -377,7 +377,7 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
|||||||
modelsPath + "/" + model,
|
modelsPath + "/" + model,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: null,
|
cwd: undefined,
|
||||||
detached: false,
|
detached: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -385,7 +385,7 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let failed = false;
|
let failed = false;
|
||||||
upscayl.stderr.on("data", (data) => {
|
upscayl?.stderr.on("data", (data) => {
|
||||||
console.log(
|
console.log(
|
||||||
"🚀 => upscayl.stderr.on => stderr.toString()",
|
"🚀 => upscayl.stderr.on => stderr.toString()",
|
||||||
data.toString()
|
data.toString()
|
||||||
@ -400,7 +400,7 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
upscayl.on("error", (data) => {
|
upscayl?.on("error", (data) => {
|
||||||
mainWindow.webContents.send(
|
mainWindow.webContents.send(
|
||||||
commands.FOLDER_UPSCAYL_PROGRESS,
|
commands.FOLDER_UPSCAYL_PROGRESS,
|
||||||
data.toString()
|
data.toString()
|
||||||
@ -410,7 +410,7 @@ ipcMain.on(commands.FOLDER_UPSCAYL, async (event, payload) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Send done comamnd when
|
// Send done comamnd when
|
||||||
upscayl.on("close", (code) => {
|
upscayl?.on("close", (code) => {
|
||||||
if (failed !== true) {
|
if (failed !== true) {
|
||||||
console.log("Done upscaling");
|
console.log("Done upscaling");
|
||||||
mainWindow.webContents.send(commands.FOLDER_UPSCAYL_DONE, outputDir);
|
mainWindow.webContents.send(commands.FOLDER_UPSCAYL_DONE, outputDir);
|
||||||
@ -425,22 +425,29 @@ ipcMain.on(commands.OPEN_FOLDER, async (event, payload) => {
|
|||||||
|
|
||||||
//------------------------Auto-Update Code-----------------------------//
|
//------------------------Auto-Update Code-----------------------------//
|
||||||
// ! AUTO UPDATE STUFF
|
// ! AUTO UPDATE STUFF
|
||||||
autoUpdater.on("update-available", (_event, releaseNotes, releaseName) => {
|
autoUpdater.on("update-available", ({ releaseNotes, releaseName }) => {
|
||||||
const dialogOpts = {
|
const dialogOpts = {
|
||||||
type: "info",
|
type: "info",
|
||||||
buttons: ["Ok"],
|
buttons: ["Ok"],
|
||||||
title: "Application Update",
|
title: "Application Update",
|
||||||
message: process.platform === "win32" ? releaseNotes : releaseName,
|
message:
|
||||||
|
process.platform === "win32"
|
||||||
|
? (releaseNotes as string)
|
||||||
|
: (releaseName as string),
|
||||||
detail: "A new version is being downloaded.",
|
detail: "A new version is being downloaded.",
|
||||||
};
|
};
|
||||||
dialog.showMessageBox(dialogOpts, (response) => {});
|
dialog.showMessageBox(dialogOpts).then((returnValue) => {});
|
||||||
});
|
});
|
||||||
autoUpdater.on("update-downloaded", (_event, releaseNotes, releaseName) => {
|
|
||||||
const dialogOpts = {
|
autoUpdater.on("update-downloaded", (event) => {
|
||||||
|
const dialogOpts: MessageBoxOptions = {
|
||||||
type: "info",
|
type: "info",
|
||||||
buttons: ["Restart", "Later"],
|
buttons: ["Restart", "Later"],
|
||||||
title: "Application Update",
|
title: "Application Update",
|
||||||
message: process.platform === "win32" ? releaseNotes : releaseName,
|
message:
|
||||||
|
process.platform === "win32"
|
||||||
|
? (event.releaseNotes as string)
|
||||||
|
: (event.releaseName as string),
|
||||||
detail:
|
detail:
|
||||||
"A new version has been downloaded. Restart the application to apply the updates.",
|
"A new version has been downloaded. Restart the application to apply the updates.",
|
||||||
};
|
};
|
@ -1,4 +1,4 @@
|
|||||||
const { ipcRenderer, contextBridge } = require("electron");
|
import { ipcRenderer, contextBridge } from "electron";
|
||||||
|
|
||||||
// 'ipcRenderer' will be available in index.js with the method 'window.electron'
|
// 'ipcRenderer' will be available in index.js with the method 'window.electron'
|
||||||
contextBridge.exposeInMainWorld("electron", {
|
contextBridge.exposeInMainWorld("electron", {
|
31
electron/utils.ts
Normal file
31
electron/utils.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { spawn } from "child_process";
|
||||||
|
import { execPath } from "./binaries";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} inputFile
|
||||||
|
* @param {*} outFile
|
||||||
|
* @param {*} modelsPath
|
||||||
|
* @param {*} model
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function upscaylImage(
|
||||||
|
inputFile: string,
|
||||||
|
outFile: string,
|
||||||
|
modelsPath: string,
|
||||||
|
model: string
|
||||||
|
) {
|
||||||
|
// UPSCALE
|
||||||
|
let upscayl = spawn(
|
||||||
|
execPath("realesrgan"),
|
||||||
|
["-i", inputFile, "-o", outFile, "-s", "4", "-m", modelsPath, "-n", model],
|
||||||
|
{
|
||||||
|
cwd: undefined,
|
||||||
|
detached: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return upscayl;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { upscaylImage };
|
@ -1,25 +0,0 @@
|
|||||||
const { spawn } = require("child_process");
|
|
||||||
const { execPath } = require("./binaries");
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} inputFile
|
|
||||||
* @param {*} outFile
|
|
||||||
* @param {*} modelsPath
|
|
||||||
* @param {*} model
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function upscaylImage(inputFile, outFile, modelsPath, model) {
|
|
||||||
// UPSCALE
|
|
||||||
let upscayl = spawn(
|
|
||||||
execPath("realesrgan"),
|
|
||||||
["-i", inputFile, "-o", outFile, "-s", 4, "-m", modelsPath, "-n", model],
|
|
||||||
{
|
|
||||||
cwd: null,
|
|
||||||
detached: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return upscayl;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { upscaylImage };
|
|
@ -1,5 +1,11 @@
|
|||||||
module.exports = {
|
/**
|
||||||
|
* @type {import('next').NextConfig}
|
||||||
|
**/
|
||||||
|
|
||||||
|
const nextConfig = {
|
||||||
images: {
|
images: {
|
||||||
unoptimized: true,
|
unoptimized: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = nextConfig;
|
||||||
|
36
package.json
36
package.json
@ -34,6 +34,7 @@
|
|||||||
"clean": "rimraf dist renderer/.next renderer/out",
|
"clean": "rimraf dist renderer/.next renderer/out",
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
"build": "next build renderer && next export renderer",
|
"build": "next build renderer && next export renderer",
|
||||||
|
"tsc": "tsc",
|
||||||
"pack-app": "npm run build && electron-builder --dir",
|
"pack-app": "npm run build && electron-builder --dir",
|
||||||
"dist": "npm run build && DEBUG=* electron-builder",
|
"dist": "npm run build && DEBUG=* electron-builder",
|
||||||
"dist:appimage": "npm run build && DEBUG=* electron-builder build -l AppImage",
|
"dist:appimage": "npm run build && DEBUG=* electron-builder build -l AppImage",
|
||||||
@ -123,30 +124,37 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.8",
|
"@types/electron": "^1.6.10",
|
||||||
"electron": "^20.0.2",
|
"@types/node": "^18.11.9",
|
||||||
"electron-builder": "^23.3.3",
|
"@types/react": "^18.0.25",
|
||||||
"next": "^12.3.1",
|
"@types/react-dom": "^18.0.8",
|
||||||
"postcss": "^8.4.16",
|
"autoprefixer": "^10.4.13",
|
||||||
|
"electron": "^21.2.2",
|
||||||
|
"electron-builder": "^23.6.0",
|
||||||
|
"next": "^13.0.2",
|
||||||
|
"postcss": "^8.4.18",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||||
"react": "^17.0.2",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.2.0",
|
||||||
"tailwindcss": "^3.1.8"
|
"tailwindcss": "^3.2.2",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-root-dir": "^1.0.2",
|
"app-root-dir": "^1.0.2",
|
||||||
"electron-is-dev": "^2.0.0",
|
"electron-is-dev": "^2.0.0",
|
||||||
"electron-is-packaged": "^1.0.2",
|
"electron-is-packaged": "^1.0.2",
|
||||||
"electron-next": "^3.1.5",
|
"electron-next": "^3.1.5",
|
||||||
"electron-root-path": "^1.0.16",
|
"electron-root-path": "^1.1.0",
|
||||||
"electron-updater": "^5.2.1",
|
"electron-updater": "^5.3.0",
|
||||||
"image-size": "^1.0.2",
|
"image-size": "^1.0.2",
|
||||||
"react-compare-slider": "^2.2.0",
|
"react-compare-slider": "^2.2.0",
|
||||||
"react-dropzone": "^14.2.2",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-select": "^5.4.0",
|
"react-image-zoom": "^1.3.1",
|
||||||
"react-tooltip": "^4.2.21",
|
"react-magnifier": "^3.0.4",
|
||||||
"tailwind-scrollbar": "^1.3.1"
|
"react-select": "^5.6.0",
|
||||||
|
"react-tooltip": "^4.4.3",
|
||||||
|
"tailwind-scrollbar": "^2.0.1"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "16.17.0"
|
"node": "16.17.0"
|
||||||
|
38
renderer/components/Footer.tsx
Normal file
38
renderer/components/Footer.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function Footer() {
|
||||||
|
return (
|
||||||
|
<div className="p-2 text-center text-sm text-neutral-500">
|
||||||
|
<p>
|
||||||
|
Copyright © 2022 -{" "}
|
||||||
|
<a
|
||||||
|
className="font-bold"
|
||||||
|
href="https://github.com/upscayl/upscayl"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Upscayl
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
By{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/TGS963"
|
||||||
|
className="font-bold"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
TGS963
|
||||||
|
</a>{" "}
|
||||||
|
and{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/NayamAmarshe"
|
||||||
|
className="font-bold"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Nayam Amarshe
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer;
|
24
renderer/components/Header.tsx
Normal file
24
renderer/components/Header.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="https://github.com/upscayl/upscayl"
|
||||||
|
target="_blank"
|
||||||
|
className="outline-none focus-visible:ring-2"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3 px-5 py-5">
|
||||||
|
<img
|
||||||
|
src="icon.png"
|
||||||
|
className="inline-block w-14"
|
||||||
|
alt="Upscayl Logo"
|
||||||
|
data-tip="Star us on GitHub 😁"
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col justify-center">
|
||||||
|
<h1 className="text-3xl font-bold text-neutral-50">Upscayl</h1>
|
||||||
|
<p className="text-neutral-400">AI Image Upscaler</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
225
renderer/components/LeftPaneSteps.tsx
Normal file
225
renderer/components/LeftPaneSteps.tsx
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Select from "react-select";
|
||||||
|
import ReactTooltip from "react-tooltip";
|
||||||
|
|
||||||
|
function LeftPaneSteps(props) {
|
||||||
|
const handleBatchMode = () => {
|
||||||
|
props.setBatchMode((oldValue) => !oldValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const customStyles = {
|
||||||
|
option: (provided, state) => ({
|
||||||
|
...provided,
|
||||||
|
borderBottom: "1px dotted pink",
|
||||||
|
color: state.isSelected ? "red" : "blue",
|
||||||
|
padding: 20,
|
||||||
|
}),
|
||||||
|
control: () => ({
|
||||||
|
// none of react-select's styles are passed to <Control />
|
||||||
|
width: 200,
|
||||||
|
}),
|
||||||
|
singleValue: (provided, state) => {
|
||||||
|
const opacity = state.isDisabled ? 0.5 : 1;
|
||||||
|
const transition = "opacity 300ms";
|
||||||
|
|
||||||
|
return { ...provided, opacity, transition };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const modelOptions = [
|
||||||
|
{ label: "General Photo", value: "realesrgan-x4plus" },
|
||||||
|
{ label: "Digital Art", value: "realesrgan-x4plus-anime" },
|
||||||
|
{ label: "Sharpen Image", value: "models-DF2K" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="animate-step-in animate flex h-screen flex-col gap-7 overflow-auto p-5">
|
||||||
|
{/* BATCH OPTION */}
|
||||||
|
<div className="flex flex-row items-end">
|
||||||
|
<p
|
||||||
|
className="mr-1 inline-block cursor-help text-sm text-white/70"
|
||||||
|
data-tip="This will let you upscale all files in a folder at once"
|
||||||
|
>
|
||||||
|
Batch Upscale:
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className={`animate relative inline-block h-5 w-8 cursor-pointer rounded-full outline-none focus-visible:shadow-lg focus-visible:shadow-purple-500 ${
|
||||||
|
props.batchMode ? "bg-gradient-purple" : "bg-neutral-500"
|
||||||
|
}`}
|
||||||
|
onClick={handleBatchMode}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
props.batchMode ? "translate-x-4" : "translate-x-1"
|
||||||
|
} animate absolute top-1/2 h-3 w-3 -translate-y-1/2 rounded-full bg-neutral-100`}
|
||||||
|
></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ADVANCED OPTION */}
|
||||||
|
<div className="flex flex-row items-end">
|
||||||
|
<p
|
||||||
|
className="mr-1 inline-block cursor-help text-sm text-white/70"
|
||||||
|
data-tip="This will let you upscale all files in a folder at once"
|
||||||
|
>
|
||||||
|
Advanced Options:
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className={`animate relative inline-block h-5 w-8 cursor-pointer rounded-full outline-none focus-visible:shadow-lg focus-visible:shadow-purple-500 ${
|
||||||
|
props.batchMode ? "bg-gradient-purple" : "bg-neutral-500"
|
||||||
|
}`}
|
||||||
|
onClick={handleBatchMode}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
props.batchMode ? "translate-x-4" : "translate-x-1"
|
||||||
|
} animate absolute top-1/2 h-3 w-3 -translate-y-1/2 rounded-full bg-neutral-100`}
|
||||||
|
></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* STEP 1 */}
|
||||||
|
<div data-tip={props.imagePath}>
|
||||||
|
<p className="step-heading">Step 1</p>
|
||||||
|
<button
|
||||||
|
className="animate bg-gradient-red rounded-lg p-3 font-medium text-white/90 outline-none transition-colors focus-visible:shadow-lg focus-visible:shadow-red-500"
|
||||||
|
onClick={
|
||||||
|
!props.batchMode
|
||||||
|
? props.selectImageHandler
|
||||||
|
: props.selectFolderHandler
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Select {props.batchMode ? "Folder" : "Image"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* STEP 2 */}
|
||||||
|
<div className="animate-step-in">
|
||||||
|
<p className="step-heading">Step 2</p>
|
||||||
|
<p className="mb-2 text-sm text-white/60">Select Upscaling Type</p>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
options={modelOptions}
|
||||||
|
components={{
|
||||||
|
IndicatorSeparator: () => null,
|
||||||
|
DropdownIndicator: () => null,
|
||||||
|
}}
|
||||||
|
onChange={props.handleModelChange}
|
||||||
|
className="react-select-container"
|
||||||
|
classNamePrefix="react-select"
|
||||||
|
defaultValue={modelOptions[0]}
|
||||||
|
theme={(theme) => ({
|
||||||
|
...theme,
|
||||||
|
colors: {
|
||||||
|
...theme.colors,
|
||||||
|
primary: "rgb(71 85 105)",
|
||||||
|
primary25: "#f5f5f5",
|
||||||
|
primary50: "#f5f5f5",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
styles={{
|
||||||
|
input: (provided, state) => ({
|
||||||
|
...provided,
|
||||||
|
color: "rgb(100 100 100)",
|
||||||
|
}),
|
||||||
|
dropdownIndicator: (provided, state) => ({
|
||||||
|
...provided,
|
||||||
|
color: "rgb(15 15 15)",
|
||||||
|
}),
|
||||||
|
placeholder: (provided, state) => ({
|
||||||
|
...provided,
|
||||||
|
color: "rgb(15 15 15)",
|
||||||
|
fontWeight: "500",
|
||||||
|
}),
|
||||||
|
singleValue: (provided, state) => ({
|
||||||
|
...provided,
|
||||||
|
color: "rgb(15 15 15)",
|
||||||
|
fontWeight: "500",
|
||||||
|
}),
|
||||||
|
menu: (provided, state) => ({
|
||||||
|
...provided,
|
||||||
|
background: "#f5f5f5",
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* <select
|
||||||
|
name="select-model"
|
||||||
|
onDrop={(e) => props.handleDrop(e)}
|
||||||
|
className="animate bg-gradient-white block cursor-pointer rounded-lg p-3 font-medium text-black/90 outline-none hover:bg-slate-200 focus-visible:ring-2 focus-visible:ring-slate-400"
|
||||||
|
onChange={props.handleModelChange}
|
||||||
|
>
|
||||||
|
<option value="realesrgan-x4plus">General Photo</option>
|
||||||
|
<option value="realesrgan-x4plus-anime">Digital Art</option>
|
||||||
|
<option value="models-DF2K">Sharpen Image</option>
|
||||||
|
</select> */}
|
||||||
|
|
||||||
|
{props.model !== "models-DF2K" && !props.batchMode && (
|
||||||
|
<div className="mt-2 flex items-center gap-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="checked:bg-gradient-white h-4 w-4 cursor-pointer appearance-none rounded-full bg-white/30 transition duration-200 focus:outline-none focus-visible:border focus-visible:shadow-lg focus-visible:shadow-white/40"
|
||||||
|
checked={props.doubleUpscayl}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
props.setDoubleUpscayl(true);
|
||||||
|
} else {
|
||||||
|
props.setDoubleUpscayl(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
className={`inline-block cursor-pointer select-none rounded-full text-sm font-medium ${
|
||||||
|
props.doubleUpscayl
|
||||||
|
? "bg-gradient-white px-2 text-black/90"
|
||||||
|
: "text-white/50"
|
||||||
|
}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
props.setDoubleUpscayl(!props.doubleUpscayl);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Double Upscayl
|
||||||
|
</p>
|
||||||
|
<span
|
||||||
|
className="cursor-help rounded-full bg-white/20 px-3 text-center font-bold text-white/40"
|
||||||
|
data-tip="Enable this option to get an 8x upscayl. Note that this may not always work properly with all images, for example, images with really large resolutions."
|
||||||
|
>
|
||||||
|
i
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* STEP 3 */}
|
||||||
|
<div className="animate-step-in" data-tip={props.outputPath}>
|
||||||
|
<p className="step-heading">Step 3</p>
|
||||||
|
<p className="mb-2 text-sm text-white/60">Defaults to Image's path</p>
|
||||||
|
<button
|
||||||
|
className="animate bg-gradient mt-1 rounded-lg p-3 font-medium text-black/90 outline-none transition-colors focus-visible:shadow-lg focus-visible:shadow-green-500"
|
||||||
|
onClick={props.outputHandler}
|
||||||
|
>
|
||||||
|
Set Output Folder
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* STEP 4 */}
|
||||||
|
<div className="animate-step-in">
|
||||||
|
<p className="step-heading">Step 4</p>
|
||||||
|
<button
|
||||||
|
className="animate bg-gradient-upscayl rounded-lg p-3 font-medium text-white/90 outline-none transition-colors focus-visible:shadow-lg focus-visible:shadow-violet-500"
|
||||||
|
onClick={props.upscaylHandler}
|
||||||
|
disabled={props.progress.length > 0}
|
||||||
|
>
|
||||||
|
{props.progress.length > 0 ? "Upscayling⏳" : "Upscayl"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ReactTooltip
|
||||||
|
className="max-w-md break-words bg-neutral-900 text-neutral-50"
|
||||||
|
place="top"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LeftPaneSteps;
|
20
renderer/components/ProgressBar.tsx
Normal file
20
renderer/components/ProgressBar.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Animated from "../public/loading.svg";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
function ProgressBar(props) {
|
||||||
|
console.log(props.sharpening);
|
||||||
|
return (
|
||||||
|
<div className="absolute flex h-full w-full flex-col items-center justify-center bg-black/50 backdrop-blur-lg">
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<Image src={Animated} alt="Progress Bar" />
|
||||||
|
<p className="font-bold text-neutral-50">{props.progress}</p>
|
||||||
|
<p className="text-sm font-medium text-neutral-200">
|
||||||
|
Doing the Upscayl magic...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProgressBar;
|
14
renderer/components/ResetButton.tsx
Normal file
14
renderer/components/ResetButton.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function ResetButton(props) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="animate bg-gradient-blue absolute top-1 right-1 z-10 rounded-full py-2 px-4 text-white opacity-30 hover:opacity-100"
|
||||||
|
onClick={props.resetImagePaths}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ResetButton;
|
20
renderer/components/RightPaneInfo.tsx
Normal file
20
renderer/components/RightPaneInfo.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function RightPaneInfo({ version, batchMode }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="p-5 pb-0 text-lg font-medium text-neutral-400">
|
||||||
|
Select {batchMode ? "a Folder" : "an Image"} to Upscale
|
||||||
|
</p>
|
||||||
|
{batchMode && (
|
||||||
|
<p className="w-full py-5 text-center text-neutral-500 md:w-96">
|
||||||
|
Make sure that the folder doesn't contain anything except PNG, JPG,
|
||||||
|
JPEG & WEBP images.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<p className="text-neutral-600">Upscayl v{version}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RightPaneInfo;
|
5
renderer/next-env.d.ts
vendored
Normal file
5
renderer/next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
16
renderer/pages/_app.tsx
Normal file
16
renderer/pages/_app.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import "../styles/globals.css";
|
||||||
|
import Head from "next/head";
|
||||||
|
import { AppProps } from "next/app";
|
||||||
|
|
||||||
|
const MyApp = ({ Component, pageProps }: AppProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Upscayl</title>
|
||||||
|
</Head>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MyApp;
|
411
renderer/pages/index.tsx
Normal file
411
renderer/pages/index.tsx
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
import { useState, useEffect, useRef, useCallback } from "react";
|
||||||
|
import commands from "../../electron/commands";
|
||||||
|
import {
|
||||||
|
ReactCompareSlider,
|
||||||
|
ReactCompareSliderImage,
|
||||||
|
} from "react-compare-slider";
|
||||||
|
import Header from "../components/Header";
|
||||||
|
import Footer from "../components/Footer";
|
||||||
|
import ProgressBar from "../components/ProgressBar";
|
||||||
|
import ResetButton from "../components/ResetButton";
|
||||||
|
import LeftPaneSteps from "../components/LeftPaneSteps";
|
||||||
|
import RightPaneInfo from "../components/RightPaneInfo";
|
||||||
|
|
||||||
|
const Home = () => {
|
||||||
|
const [imagePath, SetImagePath] = useState("");
|
||||||
|
const [upscaledImagePath, setUpscaledImagePath] = useState("");
|
||||||
|
const [outputPath, SetOutputPath] = useState("");
|
||||||
|
const [scaleFactor, setScaleFactor] = useState(4);
|
||||||
|
const [progress, setProgress] = useState("");
|
||||||
|
const [model, setModel] = useState("realesrgan-x4plus");
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
const [version, setVersion] = useState("");
|
||||||
|
const [batchMode, setBatchMode] = useState(false);
|
||||||
|
const [batchFolderPath, setBatchFolderPath] = useState("");
|
||||||
|
const [upscaledBatchFolderPath, setUpscaledBatchFolderPath] = useState("");
|
||||||
|
const [doubleUpscayl, setDoubleUpscayl] = useState(false);
|
||||||
|
|
||||||
|
const resetImagePaths = () => {
|
||||||
|
setProgress("");
|
||||||
|
|
||||||
|
SetImagePath("");
|
||||||
|
setUpscaledImagePath("");
|
||||||
|
|
||||||
|
setBatchFolderPath("");
|
||||||
|
setUpscaledBatchFolderPath("");
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoaded(true);
|
||||||
|
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
|
||||||
|
setVersion(navigator?.userAgent?.match(/Upscayl\/([\d\.]+\d+)/)[1]);
|
||||||
|
|
||||||
|
const handleErrors = (data) => {
|
||||||
|
if (data.includes("invalid gpu")) {
|
||||||
|
alert(
|
||||||
|
"Error. Please make sure you have a Vulkan compatible GPU (Most modern GPUs support Vulkan). Upscayl does not work with CPU or iGPU sadly."
|
||||||
|
);
|
||||||
|
resetImagePaths();
|
||||||
|
} else if (data.includes("failed")) {
|
||||||
|
if (batchMode) return;
|
||||||
|
alert(
|
||||||
|
data.includes("encode")
|
||||||
|
? "ENCODING ERROR => "
|
||||||
|
: "DECODING ERROR => " +
|
||||||
|
"This image is possibly corrupt or not supported by Upscayl. You could try converting the image into another format and upscaling again. Otherwise, make sure that the output path is correct and you have the proper write permissions for the directory. If not, then unfortuantely this image is not supported by Upscayl, sorry."
|
||||||
|
);
|
||||||
|
resetImagePaths();
|
||||||
|
} else if (data.includes("uncaughtException")) {
|
||||||
|
alert(
|
||||||
|
"Upscayl encountered an error. Possibly, the upscayl binary failed to execute the commands properly. Try launching Upscayl using commandline through Terminal and see if you get any information. You can post an issue on Upscayl's GitHub repository for more help."
|
||||||
|
);
|
||||||
|
resetImagePaths();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.electron.on(commands.UPSCAYL_PROGRESS, (_, data) => {
|
||||||
|
console.log(
|
||||||
|
"🚀 => file: index.jsx => line 61 => window.electron.on => data",
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.length > 0 && data.length < 10) {
|
||||||
|
setProgress(data);
|
||||||
|
}
|
||||||
|
handleErrors(data);
|
||||||
|
});
|
||||||
|
window.electron.on(commands.FOLDER_UPSCAYL_PROGRESS, (_, data) => {
|
||||||
|
if (data.length > 0 && data.length < 10) {
|
||||||
|
setProgress(data);
|
||||||
|
}
|
||||||
|
handleErrors(data);
|
||||||
|
});
|
||||||
|
window.electron.on(commands.DOUBLE_UPSCAYL_PROGRESS, (_, data) => {
|
||||||
|
if (data.length > 0 && data.length < 10) {
|
||||||
|
setProgress(data);
|
||||||
|
}
|
||||||
|
handleErrors(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electron.on(commands.UPSCAYL_DONE, (_, data) => {
|
||||||
|
setProgress("");
|
||||||
|
setUpscaledImagePath(data);
|
||||||
|
});
|
||||||
|
window.electron.on(commands.FOLDER_UPSCAYL_DONE, (_, data) => {
|
||||||
|
setProgress("");
|
||||||
|
setUpscaledBatchFolderPath(data);
|
||||||
|
});
|
||||||
|
window.electron.on(commands.DOUBLE_UPSCAYL_DONE, (_, data) => {
|
||||||
|
setUpscaledImagePath(data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setProgress("");
|
||||||
|
}, [batchMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (imagePath.length > 0) {
|
||||||
|
const filePath = imagePath;
|
||||||
|
console.log(
|
||||||
|
"🚀 => file: index.jsx => line 109 => useEffect => filePath",
|
||||||
|
filePath
|
||||||
|
);
|
||||||
|
|
||||||
|
const extension = imagePath.toLocaleLowerCase().split(".").pop();
|
||||||
|
console.log(
|
||||||
|
"🚀 => file: index.jsx => line 111 => useEffect => extension",
|
||||||
|
extension
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!allowedFileTypes.includes(extension.toLowerCase())) {
|
||||||
|
alert("Please select an image");
|
||||||
|
resetImagePaths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [imagePath]);
|
||||||
|
|
||||||
|
const selectImageHandler = async () => {
|
||||||
|
resetImagePaths();
|
||||||
|
|
||||||
|
var path = await window.electron.invoke(commands.SELECT_FILE);
|
||||||
|
|
||||||
|
if (path !== "cancelled") {
|
||||||
|
SetImagePath(path);
|
||||||
|
var dirname = path.match(/(.*)[\/\\]/)[1] || "";
|
||||||
|
SetOutputPath(dirname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectFolderHandler = async () => {
|
||||||
|
resetImagePaths();
|
||||||
|
|
||||||
|
var path = await window.electron.invoke(commands.SELECT_FOLDER);
|
||||||
|
|
||||||
|
if (path !== "cancelled") {
|
||||||
|
setBatchFolderPath(path);
|
||||||
|
SetOutputPath(path + "_upscayled");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModelChange = (e) => {
|
||||||
|
setModel(e.value);
|
||||||
|
if (e.value === "models-DF2K") {
|
||||||
|
setDoubleUpscayl(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragEnter = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("drag enter");
|
||||||
|
};
|
||||||
|
const handleDragLeave = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("drag leave");
|
||||||
|
};
|
||||||
|
const handleDragOver = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("drag over");
|
||||||
|
};
|
||||||
|
const openFolderHandler = (e) => {
|
||||||
|
window.electron.send(commands.OPEN_FOLDER, upscaledBatchFolderPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowedFileTypes = ["png", "jpg", "jpeg", "webp"];
|
||||||
|
|
||||||
|
const handleDrop = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
resetImagePaths();
|
||||||
|
|
||||||
|
const type = e.dataTransfer.items[0].type;
|
||||||
|
console.log("🚀 => handleDrop => type", type);
|
||||||
|
const filePath = e.dataTransfer.files[0].path;
|
||||||
|
console.log("🚀 => handleDrop => filePath", filePath);
|
||||||
|
const extension = e.dataTransfer.files[0].name.split(".").at(-1);
|
||||||
|
console.log("🚀 => handleDrop => extension", extension);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!type.includes("image") ||
|
||||||
|
!allowedFileTypes.includes(extension.toLowerCase())
|
||||||
|
) {
|
||||||
|
alert("Please drag and drop an image");
|
||||||
|
} else {
|
||||||
|
SetImagePath(filePath);
|
||||||
|
var dirname = filePath.match(/(.*)[\/\\]/)[1] || "";
|
||||||
|
console.log("🚀 => handleDrop => dirname", dirname);
|
||||||
|
SetOutputPath(dirname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePaste = (e) => {
|
||||||
|
console.log(e);
|
||||||
|
resetImagePaths();
|
||||||
|
e.preventDefault();
|
||||||
|
const type = e.clipboardData.items[0].type;
|
||||||
|
const filePath = e.clipboardData.files[0].path;
|
||||||
|
const extension = e.clipboardData.files[0].name.split(".").at(-1);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!type.includes("image") &&
|
||||||
|
!allowedFileTypes.includes(extension.toLowerCase())
|
||||||
|
) {
|
||||||
|
alert("Please drag and drop an image");
|
||||||
|
} else {
|
||||||
|
SetImagePath(filePath);
|
||||||
|
var dirname = filePath.match(/(.*)[\/\\]/)[1] || "";
|
||||||
|
SetOutputPath(dirname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const outputHandler = async () => {
|
||||||
|
var path = await window.electron.invoke(commands.SELECT_FOLDER);
|
||||||
|
if (path !== "cancelled") {
|
||||||
|
SetOutputPath(path);
|
||||||
|
} else {
|
||||||
|
console.log("Getting output path from input file");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const upscaylHandler = async () => {
|
||||||
|
setUpscaledImagePath("");
|
||||||
|
if (imagePath !== "" || batchFolderPath !== "") {
|
||||||
|
setProgress("Hold on...");
|
||||||
|
if (model === "models-DF2K") {
|
||||||
|
setDoubleUpscayl(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doubleUpscayl) {
|
||||||
|
await window.electron.send(commands.DOUBLE_UPSCAYL, {
|
||||||
|
imagePath,
|
||||||
|
outputPath,
|
||||||
|
model,
|
||||||
|
});
|
||||||
|
} else if (batchMode) {
|
||||||
|
setDoubleUpscayl(false);
|
||||||
|
await window.electron.send(commands.FOLDER_UPSCAYL, {
|
||||||
|
scaleFactor,
|
||||||
|
batchFolderPath,
|
||||||
|
outputPath,
|
||||||
|
model,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await window.electron.send(commands.UPSCAYL, {
|
||||||
|
scaleFactor,
|
||||||
|
imagePath,
|
||||||
|
outputPath,
|
||||||
|
model,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Please select an image to upscale");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-screen flex-row overflow-hidden bg-[#1d1c23]">
|
||||||
|
<div className="flex h-screen w-96 flex-col bg-[#26222c]">
|
||||||
|
{((!batchMode && imagePath.length > 0) ||
|
||||||
|
(batchMode && batchFolderPath.length > 0)) && (
|
||||||
|
<ResetButton resetImagePaths={resetImagePaths} />
|
||||||
|
)}
|
||||||
|
{/* HEADER */}
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{/* LEFT PANE */}
|
||||||
|
<LeftPaneSteps
|
||||||
|
progress={progress}
|
||||||
|
selectImageHandler={selectImageHandler}
|
||||||
|
selectFolderHandler={selectFolderHandler}
|
||||||
|
handleModelChange={handleModelChange}
|
||||||
|
handleDrop={handleDrop}
|
||||||
|
outputHandler={outputHandler}
|
||||||
|
upscaylHandler={upscaylHandler}
|
||||||
|
batchMode={batchMode}
|
||||||
|
setBatchMode={setBatchMode}
|
||||||
|
imagePath={imagePath}
|
||||||
|
outputPath={outputPath}
|
||||||
|
doubleUpscayl={doubleUpscayl}
|
||||||
|
setDoubleUpscayl={setDoubleUpscayl}
|
||||||
|
model={model}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* RIGHT PANE */}
|
||||||
|
<div
|
||||||
|
className="relative flex h-screen w-full flex-col items-center justify-center"
|
||||||
|
onDrop={(e) => handleDrop(e)}
|
||||||
|
onDragOver={(e) => handleDragOver(e)}
|
||||||
|
onDragEnter={(e) => handleDragEnter(e)}
|
||||||
|
onDragLeave={(e) => handleDragLeave(e)}
|
||||||
|
onPaste={(e) => handlePaste(e)}
|
||||||
|
>
|
||||||
|
{progress.length > 0 &&
|
||||||
|
upscaledImagePath.length === 0 &&
|
||||||
|
upscaledBatchFolderPath.length === 0 ? (
|
||||||
|
<ProgressBar progress={progress} />
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{((!batchMode &&
|
||||||
|
imagePath.length === 0 &&
|
||||||
|
upscaledImagePath.length === 0) ||
|
||||||
|
(batchMode &&
|
||||||
|
batchFolderPath.length === 0 &&
|
||||||
|
upscaledBatchFolderPath.length === 0)) && (
|
||||||
|
<RightPaneInfo version={version} batchMode={batchMode} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!batchMode &&
|
||||||
|
upscaledImagePath.length === 0 &&
|
||||||
|
imagePath.length > 0 && (
|
||||||
|
<img
|
||||||
|
className="h-full w-full object-contain"
|
||||||
|
src={
|
||||||
|
"file://" +
|
||||||
|
`${upscaledImagePath ? upscaledImagePath : imagePath}`
|
||||||
|
}
|
||||||
|
draggable="false"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{batchMode &&
|
||||||
|
upscaledBatchFolderPath.length === 0 &&
|
||||||
|
batchFolderPath.length > 0 && (
|
||||||
|
<p className="select-none font-bold text-neutral-50">
|
||||||
|
Selected folder: {batchFolderPath}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{batchMode && upscaledBatchFolderPath.length > 0 && (
|
||||||
|
<>
|
||||||
|
<p className="select-none py-4 font-bold text-neutral-50">
|
||||||
|
All done!
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className="bg-gradient-blue rounded-lg p-3 font-medium text-white/90 transition-colors"
|
||||||
|
onClick={openFolderHandler}
|
||||||
|
>
|
||||||
|
Open Upscayled Folder
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!batchMode && imagePath.length > 0 && upscaledImagePath.length > 0 && (
|
||||||
|
<ReactCompareSlider
|
||||||
|
itemOne={
|
||||||
|
<>
|
||||||
|
<p className="absolute bottom-1 left-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
|
||||||
|
Original
|
||||||
|
</p>
|
||||||
|
<ReactCompareSliderImage
|
||||||
|
src={"file://" + imagePath}
|
||||||
|
alt="Original"
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
}}
|
||||||
|
className="bg-[#1d1c23]"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
itemTwo={
|
||||||
|
<>
|
||||||
|
<p className="absolute bottom-1 right-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
|
||||||
|
Upscayled
|
||||||
|
</p>
|
||||||
|
<ReactCompareSliderImage
|
||||||
|
src={"file://" + upscaledImagePath}
|
||||||
|
alt="Upscayl"
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
}}
|
||||||
|
className="origin-bottom scale-[200%] bg-[#1d1c23]"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
className="h-screen"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* {imagePath.length === 0 && batchFolderPath.length === 0 ? (
|
||||||
|
<RightPaneInfo version={version} />
|
||||||
|
) : upscaledImagePath.length === 0 &&
|
||||||
|
upscaledBatchFolderPath.length === 0 ? (
|
||||||
|
!batchMode ? (
|
||||||
|
|
||||||
|
) : (
|
||||||
|
|
||||||
|
)
|
||||||
|
) : !batchMode ? (
|
||||||
|
|
||||||
|
) : (
|
||||||
|
|
||||||
|
)} */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
13
renderer/renderer.d.ts
vendored
Normal file
13
renderer/renderer.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { IpcRenderer } from "electron";
|
||||||
|
|
||||||
|
export interface IElectronAPI {
|
||||||
|
on: (command, func?) => IpcRenderer;
|
||||||
|
send: (command, func?) => IpcRenderer;
|
||||||
|
invoke: (command, func?) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electron: IElectronAPI;
|
||||||
|
}
|
||||||
|
}
|
20
renderer/tsconfig.json
Normal file
20
renderer/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"incremental": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve"
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "../app.module.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
107
tsconfig.json
Normal file
107
tsconfig.json
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
"jsx": "preserve" /* Specify what JSX code is generated. */,
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs" /* Specify what module code is generated. */,
|
||||||
|
"rootDir": "./electron" /* Specify the root folder within your source files. */,
|
||||||
|
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
"rootDirs": [
|
||||||
|
"./electron",
|
||||||
|
"./renderer"
|
||||||
|
] /* Allow multiple folders to be treated as one when resolving modules. */,
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
"outDir": "./main" /* Specify an output folder for all emitted files. */,
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
|
"noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "public", "renderer"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user