mirror of
https://github.com/upscayl/upscayl.git
synced 2025-02-15 10:22:37 +01:00
Refactor and Update Code
- Change file names to kebab-caase - Add new useTranslation Hook - Change useLog hook name to useLogger - Update translation hook to provide autocomplete
This commit is contained in:
parent
5e5f60728b
commit
b39d23c2ff
@ -11,11 +11,40 @@ import { atomWithStorage } from "jotai/utils";
|
|||||||
type Translations = typeof en;
|
type Translations = typeof en;
|
||||||
type Locales = "en" | "ru" | "ja" | "zh" | "es" | "fr";
|
type Locales = "en" | "ru" | "ja" | "zh" | "es" | "fr";
|
||||||
|
|
||||||
|
const translations: Record<Locales, Translations> = {
|
||||||
|
en,
|
||||||
|
ru,
|
||||||
|
ja,
|
||||||
|
zh,
|
||||||
|
es,
|
||||||
|
fr,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a type for nested key paths
|
||||||
|
type NestedKeyOf<Object> = Object extends object
|
||||||
|
? {
|
||||||
|
[Key in keyof Object]: Key extends string | number
|
||||||
|
? Key | `${Key}.${NestedKeyOf<Object[Key]>}`
|
||||||
|
: never;
|
||||||
|
}[keyof Object]
|
||||||
|
: never;
|
||||||
|
|
||||||
// Utility function to access nested translation keys
|
// Utility function to access nested translation keys
|
||||||
const getNestedTranslation = (obj: Translations, key: string): string => {
|
const getNestedTranslation = (
|
||||||
return (
|
obj: Translations,
|
||||||
key.split(".").reduce((acc, part) => acc && (acc as any)[part], obj) || key
|
key: NestedKeyOf<Translations>,
|
||||||
);
|
): string => {
|
||||||
|
// Split the key into an array of nested parts
|
||||||
|
const keyParts = key.split(".");
|
||||||
|
|
||||||
|
// Traverse the object using the key parts
|
||||||
|
const result = keyParts.reduce((currentObj, part) => {
|
||||||
|
// If currentObj is falsy or doesn't have the property, return undefined
|
||||||
|
return currentObj && currentObj[part];
|
||||||
|
}, obj);
|
||||||
|
|
||||||
|
// Return the found translation or the original key if not found
|
||||||
|
return result || key;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Atom to store the current locale
|
// Atom to store the current locale
|
||||||
@ -24,16 +53,11 @@ export const localeAtom = atomWithStorage<Locales>("language", "en");
|
|||||||
// Atom to get the translation function based on the current locale
|
// Atom to get the translation function based on the current locale
|
||||||
export const translationAtom = atom((get) => {
|
export const translationAtom = atom((get) => {
|
||||||
const locale = get(localeAtom);
|
const locale = get(localeAtom);
|
||||||
const translations: Record<Locales, Translations> = {
|
|
||||||
en,
|
|
||||||
ru,
|
|
||||||
ja,
|
|
||||||
zh,
|
|
||||||
es,
|
|
||||||
fr,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (key: string, params: Record<string, string> = {}): string => {
|
return (
|
||||||
|
key: NestedKeyOf<Translations>,
|
||||||
|
params: Record<string, string> = {},
|
||||||
|
): string => {
|
||||||
const template = getNestedTranslation(translations[locale], key);
|
const template = getNestedTranslation(translations[locale], key);
|
||||||
|
|
||||||
// Replace placeholders with parameters, e.g., {name} => John
|
// Replace placeholders with parameters, e.g., {name} => John
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FEATURE_FLAGS } from "@common/feature-flags";
|
import { FEATURE_FLAGS } from "@common/feature-flags";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Logo from "./icons/Logo";
|
import UpscaylSVGLogo from "./icons/Logo";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export default function Header({ version }: { version: string }) {
|
|||||||
data-tooltip-content={t("HEADER.GITHUB_BUTTON_TITLE")}
|
data-tooltip-content={t("HEADER.GITHUB_BUTTON_TITLE")}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3 px-5 py-5">
|
<div className="flex items-center gap-3 px-5 py-5">
|
||||||
<Logo className="inline-block h-14 w-14" />
|
<UpscaylSVGLogo className="inline-block h-14 w-14" />
|
||||||
<div className="flex flex-col justify-center">
|
<div className="flex flex-col justify-center">
|
||||||
<h1 className="text-3xl font-bold">
|
<h1 className="text-3xl font-bold">
|
||||||
{t("TITLE")}{" "}
|
{t("TITLE")}{" "}
|
||||||
|
@ -1,27 +1,66 @@
|
|||||||
|
import { newsAtom, showNewsModalAtom } from "@/atoms/newsAtom";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
import { GrayMatterFile } from "gray-matter";
|
import matter, { GrayMatterFile } from "gray-matter";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
|
|
||||||
export const NewsModal = ({
|
export const NewsModal = () => {
|
||||||
show,
|
|
||||||
setShow,
|
|
||||||
news,
|
|
||||||
}: {
|
|
||||||
show: boolean;
|
|
||||||
setShow: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
news: GrayMatterFile<string>;
|
|
||||||
}) => {
|
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
||||||
|
const [news, setNews] = useAtom(newsAtom);
|
||||||
|
const [showNewsModal, setShowNewsModal] = useAtom(showNewsModalAtom);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// TODO: ADD AN ABOUT TAB
|
||||||
|
if (window && window.navigator.onLine === false) return;
|
||||||
|
try {
|
||||||
|
fetch("https://raw.githubusercontent.com/upscayl/upscayl/main/news.md", {
|
||||||
|
cache: "no-cache",
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.text();
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
const newsData = result;
|
||||||
|
if (!newsData) {
|
||||||
|
console.log("📰 Could not fetch news data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const markdownData = matter(newsData);
|
||||||
|
if (!markdownData) return;
|
||||||
|
if (markdownData && markdownData.data.dontShow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
markdownData &&
|
||||||
|
news &&
|
||||||
|
markdownData?.data?.version === news?.data?.version
|
||||||
|
) {
|
||||||
|
console.log("📰 News is up to date");
|
||||||
|
if (showNewsModal === false) {
|
||||||
|
setShowNewsModal(false);
|
||||||
|
}
|
||||||
|
} else if (markdownData) {
|
||||||
|
setNews(matter(newsData));
|
||||||
|
setShowNewsModal(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Could not fetch Upscayl News");
|
||||||
|
}
|
||||||
|
}, [news]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<dialog className={`modal ${show && "modal-open"}`}>
|
<dialog className={`modal ${showNewsModal && "modal-open"}`}>
|
||||||
<div className="modal-box flex flex-col items-center gap-4 text-center">
|
<div className="modal-box flex flex-col items-center gap-4 text-center">
|
||||||
<button
|
<button
|
||||||
className="btn btn-circle absolute right-4 top-2"
|
className="btn btn-circle absolute right-4 top-2"
|
||||||
onClick={() => setShow(false)}
|
onClick={() => {
|
||||||
|
setShowNewsModal(false);
|
||||||
|
setNews((prev) => ({ ...prev, seen: true }));
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -57,7 +96,12 @@ export const NewsModal = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="dialog" className="modal-backdrop">
|
<form method="dialog" className="modal-backdrop">
|
||||||
<button onClick={() => setShow(false)}>
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowNewsModal(false);
|
||||||
|
setNews((prev) => ({ ...prev, seen: true }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t("APP.DIALOG_BOX.CLOSE")}
|
{t("APP.DIALOG_BOX.CLOSE")}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import ELECTRON_COMMANDS from "@common/commands";
|
import ELECTRON_COMMANDS from "@common/commands";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import useLog from "./useLog";
|
import useLogger from "./use-logger";
|
||||||
|
|
||||||
export const initCustomModels = () => {
|
export const initCustomModels = () => {
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const customModelsPath = JSON.parse(
|
const customModelsPath = JSON.parse(
|
||||||
|
@ -3,7 +3,7 @@ import log from "electron-log/renderer";
|
|||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const useLog = () => {
|
const useLogger = () => {
|
||||||
const setLogData = useSetAtom(logAtom);
|
const setLogData = useSetAtom(logAtom);
|
||||||
|
|
||||||
const logit = (...args: any) => {
|
const logit = (...args: any) => {
|
||||||
@ -13,9 +13,7 @@ const useLog = () => {
|
|||||||
setLogData((prevLogData) => [...prevLogData, data]);
|
setLogData((prevLogData) => [...prevLogData, data]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return logit;
|
||||||
logit,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useLog;
|
export default useLogger;
|
9
renderer/components/hooks/use-translation.ts
Normal file
9
renderer/components/hooks/use-translation.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
|
||||||
|
const useTranslation = () => {
|
||||||
|
const t = useAtomValue(translationAtom);
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTranslation;
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const Logo = ({ ...rest }) => {
|
const UpscaylSVGLogo = ({ ...rest }) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 256 256"
|
viewBox="0 0 256 256"
|
||||||
@ -166,4 +166,4 @@ const Logo = ({ ...rest }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Logo;
|
export default UpscaylSVGLogo;
|
||||||
|
28
renderer/components/main-content/image-viewer.tsx
Normal file
28
renderer/components/main-content/image-viewer.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ImageViewSettings from "../upscayl-tab/view/ImageOptions";
|
||||||
|
import { sanitizePath } from "@common/sanitize-path";
|
||||||
|
|
||||||
|
const ImageViewer = ({
|
||||||
|
imagePath,
|
||||||
|
setDimensions,
|
||||||
|
}: {
|
||||||
|
imagePath: string;
|
||||||
|
setDimensions: (dimensions: { width: number; height: number }) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={"file:///" + sanitizePath(imagePath)}
|
||||||
|
onLoad={(e: any) => {
|
||||||
|
setDimensions({
|
||||||
|
width: e.target.naturalWidth,
|
||||||
|
height: e.target.naturalHeight,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
draggable="false"
|
||||||
|
alt=""
|
||||||
|
className="h-full w-full bg-gradient-to-br from-base-300 to-base-100 object-contain"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageViewer;
|
@ -1,5 +1,5 @@
|
|||||||
import useLog from "../hooks/useLog";
|
import useLogger from "../hooks/use-logger";
|
||||||
import { useState, useCallback, useMemo, useRef } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import ELECTRON_COMMANDS from "../../../common/commands";
|
import ELECTRON_COMMANDS from "../../../common/commands";
|
||||||
import { useAtomValue, useSetAtom } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import {
|
import {
|
||||||
@ -12,15 +12,18 @@ import {
|
|||||||
} from "../../atoms/userSettingsAtom";
|
} from "../../atoms/userSettingsAtom";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { sanitizePath } from "@common/sanitize-path";
|
import { sanitizePath } from "@common/sanitize-path";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
|
||||||
import getDirectoryFromPath from "@common/get-directory-from-path";
|
import getDirectoryFromPath from "@common/get-directory-from-path";
|
||||||
import { FEATURE_FLAGS } from "@common/feature-flags";
|
import { FEATURE_FLAGS } from "@common/feature-flags";
|
||||||
import { VALID_IMAGE_FORMATS } from "@/lib/valid-formats";
|
import { VALID_IMAGE_FORMATS } from "@/lib/valid-formats";
|
||||||
import ProgressBar from "../upscayl-tab/view/ProgressBar";
|
import ProgressBar from "./progress-bar";
|
||||||
import RightPaneInfo from "../upscayl-tab/view/RightPaneInfo";
|
import InformationCard from "../upscayl-tab/view/RightPaneInfo";
|
||||||
import ImageOptions from "../upscayl-tab/view/ImageOptions";
|
import ImageViewSettings from "../upscayl-tab/view/ImageOptions";
|
||||||
import { ReactCompareSlider } from "react-compare-slider";
|
|
||||||
import useUpscaylVersion from "../hooks/use-upscayl-version";
|
import useUpscaylVersion from "../hooks/use-upscayl-version";
|
||||||
|
import MacTitlebarDragRegion from "./mac-titlebar-drag-region";
|
||||||
|
import LensViewer from "./lens-view";
|
||||||
|
import ImageViewer from "./image-viewer";
|
||||||
|
import useTranslation from "../hooks/use-translation";
|
||||||
|
import SliderView from "./slider-view";
|
||||||
("use client");
|
("use client");
|
||||||
|
|
||||||
const MainContent = ({
|
const MainContent = ({
|
||||||
@ -53,16 +56,11 @@ const MainContent = ({
|
|||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useTranslation();
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const version = useUpscaylVersion();
|
const version = useUpscaylVersion();
|
||||||
|
|
||||||
const upscaledImageRef = useRef<HTMLImageElement>(null);
|
|
||||||
|
|
||||||
const [backgroundPosition, setBackgroundPosition] = useState("0% 0%");
|
|
||||||
const [lensPosition, setLensPosition] = useState({ x: 0, y: 0 });
|
|
||||||
|
|
||||||
const setOutputPath = useSetAtom(savedOutputPathAtom);
|
const setOutputPath = useSetAtom(savedOutputPathAtom);
|
||||||
const progress = useAtomValue(progressAtom);
|
const progress = useAtomValue(progressAtom);
|
||||||
const batchMode = useAtomValue(batchModeAtom);
|
const batchMode = useAtomValue(batchModeAtom);
|
||||||
@ -77,6 +75,22 @@ const MainContent = ({
|
|||||||
[upscaledImagePath],
|
[upscaledImagePath],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const showInformationCard = useMemo(() => {
|
||||||
|
if (!batchMode) {
|
||||||
|
return imagePath.length === 0 && upscaledImagePath.length === 0;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
batchFolderPath.length === 0 && upscaledBatchFolderPath.length === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
batchMode,
|
||||||
|
imagePath,
|
||||||
|
upscaledImagePath,
|
||||||
|
batchFolderPath,
|
||||||
|
upscaledBatchFolderPath,
|
||||||
|
]);
|
||||||
|
|
||||||
// DRAG AND DROP HANDLERS
|
// DRAG AND DROP HANDLERS
|
||||||
const handleDragEnter = (e) => {
|
const handleDragEnter = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -92,7 +106,7 @@ const MainContent = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openFolderHandler = (e) => {
|
const openFolderHandler = (e) => {
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
logit("📂 OPEN_FOLDER: ", upscaledBatchFolderPath);
|
logit("📂 OPEN_FOLDER: ", upscaledBatchFolderPath);
|
||||||
window.electron.send(
|
window.electron.send(
|
||||||
ELECTRON_COMMANDS.OPEN_FOLDER,
|
ELECTRON_COMMANDS.OPEN_FOLDER,
|
||||||
@ -146,7 +160,7 @@ const MainContent = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePaste = (e) => {
|
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
|
||||||
resetImagePaths();
|
resetImagePaths();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const type = e.clipboardData.items[0].type;
|
const type = e.clipboardData.items[0].type;
|
||||||
@ -171,93 +185,48 @@ const MainContent = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseMove = useCallback((e: any) => {
|
|
||||||
const { left, top, width, height } = e.target.getBoundingClientRect();
|
|
||||||
const x = ((e.pageX - left) / width) * 100;
|
|
||||||
const y = ((e.pageY - top) / height) * 100;
|
|
||||||
setBackgroundPosition(`${x}% ${y}%`);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleMouseMoveCompare = (e: React.MouseEvent) => {
|
|
||||||
if (upscaledImageRef.current) {
|
|
||||||
const { left, top, width, height } =
|
|
||||||
upscaledImageRef.current.getBoundingClientRect();
|
|
||||||
const x = e.clientX - left;
|
|
||||||
const y = e.clientY - top;
|
|
||||||
setLensPosition({
|
|
||||||
x: Math.max(0, Math.min(x - lensSize, width - lensSize * 2)),
|
|
||||||
y: Math.max(0, Math.min(y - lensSize / 2, height - lensSize)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopHandler = () => {
|
|
||||||
window.electron.send(ELECTRON_COMMANDS.STOP);
|
|
||||||
logit("🛑 Stopping Upscayl");
|
|
||||||
resetImagePaths();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="relative flex h-screen w-full flex-col items-center justify-center"
|
className="relative flex h-screen w-full flex-col items-center justify-center"
|
||||||
onDrop={(e) => handleDrop(e)}
|
onDrop={handleDrop}
|
||||||
onDragOver={(e) => handleDragOver(e)}
|
onDragOver={handleDragOver}
|
||||||
onDragEnter={(e) => handleDragEnter(e)}
|
onDragEnter={handleDragEnter}
|
||||||
onDragLeave={(e) => handleDragLeave(e)}
|
onDragLeave={handleDragLeave}
|
||||||
onDoubleClick={() => {
|
onDoubleClick={batchMode ? selectFolderHandler : selectImageHandler}
|
||||||
if (batchMode) {
|
onPaste={handlePaste}
|
||||||
selectFolderHandler();
|
|
||||||
} else {
|
|
||||||
selectImageHandler();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPaste={(e) => handlePaste(e)}
|
|
||||||
>
|
>
|
||||||
{window.electron.platform === "mac" && (
|
<MacTitlebarDragRegion />
|
||||||
<div className="mac-titlebar absolute top-0 h-8 w-full"></div>
|
|
||||||
)}
|
|
||||||
{progress.length > 0 &&
|
{progress.length > 0 &&
|
||||||
upscaledImagePath.length === 0 &&
|
upscaledImagePath.length === 0 &&
|
||||||
upscaledBatchFolderPath.length === 0 ? (
|
upscaledBatchFolderPath.length === 0 && (
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
batchMode={batchMode}
|
batchMode={batchMode}
|
||||||
progress={progress}
|
progress={progress}
|
||||||
doubleUpscaylCounter={doubleUpscaylCounter}
|
doubleUpscaylCounter={doubleUpscaylCounter}
|
||||||
stopHandler={stopHandler}
|
resetImagePaths={resetImagePaths}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
{/* DEFAULT PANE INFO */}
|
|
||||||
{((!batchMode &&
|
|
||||||
imagePath.length === 0 &&
|
|
||||||
upscaledImagePath.length === 0) ||
|
|
||||||
(batchMode &&
|
|
||||||
batchFolderPath.length === 0 &&
|
|
||||||
upscaledBatchFolderPath.length === 0)) && (
|
|
||||||
<RightPaneInfo version={version} batchMode={batchMode} />
|
|
||||||
)}
|
)}
|
||||||
{/* SHOW SELECTED IMAGE */}
|
|
||||||
{!batchMode && upscaledImagePath.length === 0 && imagePath.length > 0 && (
|
{/* DEFAULT PANE INFO */}
|
||||||
<>
|
{showInformationCard && (
|
||||||
<ImageOptions
|
<InformationCard version={version} batchMode={batchMode} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ImageViewSettings
|
||||||
zoomAmount={zoomAmount}
|
zoomAmount={zoomAmount}
|
||||||
setZoomAmount={setZoomAmount}
|
setZoomAmount={setZoomAmount}
|
||||||
resetImagePaths={resetImagePaths}
|
resetImagePaths={resetImagePaths}
|
||||||
hideZoomOptions={true}
|
hideZoomOptions={
|
||||||
|
!batchMode && upscaledImagePath.length === 0 && imagePath.length > 0
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<img
|
|
||||||
src={"file:///" + sanitizePath(imagePath)}
|
{/* SHOW SELECTED IMAGE */}
|
||||||
onLoad={(e: any) => {
|
{!batchMode && upscaledImagePath.length === 0 && imagePath.length > 0 && (
|
||||||
setDimensions({
|
<ImageViewer imagePath={imagePath} setDimensions={setDimensions} />
|
||||||
width: e.target.naturalWidth,
|
|
||||||
height: e.target.naturalHeight,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
draggable="false"
|
|
||||||
alt=""
|
|
||||||
className="h-full w-full bg-gradient-to-br from-base-300 to-base-100 object-contain"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* BATCH UPSCALE SHOW SELECTED FOLDER */}
|
{/* BATCH UPSCALE SHOW SELECTED FOLDER */}
|
||||||
{batchMode &&
|
{batchMode &&
|
||||||
upscaledBatchFolderPath.length === 0 &&
|
upscaledBatchFolderPath.length === 0 &&
|
||||||
@ -270,6 +239,7 @@ const MainContent = ({
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{/* BATCH UPSCALE DONE INFO */}
|
{/* BATCH UPSCALE DONE INFO */}
|
||||||
|
|
||||||
{batchMode && upscaledBatchFolderPath.length > 0 && (
|
{batchMode && upscaledBatchFolderPath.length > 0 && (
|
||||||
<div className="z-50 flex flex-col items-center">
|
<div className="z-50 flex flex-col items-center">
|
||||||
<p className="select-none py-4 font-bold text-base-content">
|
<p className="select-none py-4 font-bold text-base-content">
|
||||||
@ -283,121 +253,26 @@ const MainContent = ({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ImageOptions
|
|
||||||
zoomAmount={zoomAmount}
|
|
||||||
setZoomAmount={setZoomAmount}
|
|
||||||
resetImagePaths={resetImagePaths}
|
|
||||||
/>
|
|
||||||
{!batchMode && viewType === "lens" && upscaledImagePath && imagePath && (
|
{!batchMode && viewType === "lens" && upscaledImagePath && imagePath && (
|
||||||
<div
|
<LensViewer
|
||||||
className="group relative h-full w-full overflow-hidden"
|
zoomAmount={zoomAmount}
|
||||||
onMouseMove={handleMouseMoveCompare}
|
lensSize={lensSize}
|
||||||
>
|
sanitizedImagePath={sanitizedImagePath}
|
||||||
{/* UPSCALED IMAGE */}
|
sanitizedUpscaledImagePath={sanitizedUpscaledImagePath}
|
||||||
<img
|
|
||||||
className="h-full w-full object-contain"
|
|
||||||
src={"file:///" + sanitizedUpscaledImagePath}
|
|
||||||
alt="Upscaled"
|
|
||||||
ref={upscaledImageRef}
|
|
||||||
/>
|
/>
|
||||||
{/* LENS */}
|
|
||||||
<div
|
|
||||||
className="pointer-events-none absolute opacity-0 transition-opacity before:absolute before:left-1/2 before:h-full before:w-[2px] before:bg-white group-hover:opacity-100"
|
|
||||||
style={{
|
|
||||||
left: `${lensPosition.x}px`,
|
|
||||||
top: `${lensPosition.y}px`,
|
|
||||||
width: lensSize * 2,
|
|
||||||
height: lensSize,
|
|
||||||
border: "2px solid white",
|
|
||||||
boxShadow: "0 0 0 9999px rgba(0, 0, 0, 0.5)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex h-full w-full">
|
|
||||||
<div className="h-full w-full overflow-hidden">
|
|
||||||
<img
|
|
||||||
src={"file:///" + sanitizedImagePath}
|
|
||||||
alt="Original"
|
|
||||||
className="h-full w-full"
|
|
||||||
style={{
|
|
||||||
objectFit: "contain",
|
|
||||||
objectPosition: `${-lensPosition.x}px ${-lensPosition.y}px`,
|
|
||||||
transform: `scale(${parseInt(zoomAmount) / 100})`,
|
|
||||||
transformOrigin: "top left",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="h-full w-full overflow-hidden">
|
|
||||||
<img
|
|
||||||
src={"file:///" + sanitizedUpscaledImagePath}
|
|
||||||
alt="Upscaled"
|
|
||||||
className="h-full w-full"
|
|
||||||
style={{
|
|
||||||
objectFit: "contain",
|
|
||||||
objectPosition: `${-lensPosition.x}px ${-lensPosition.y}px`,
|
|
||||||
transform: `scale(${parseInt(zoomAmount) / 100})`,
|
|
||||||
transformOrigin: "top left",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="absolute bottom-0 left-0 flex w-full items-center justify-around bg-black bg-opacity-50 p-1 px-2 text-center text-xs text-white backdrop-blur-sm">
|
|
||||||
<span>Original</span>
|
|
||||||
<span>Upscayl</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* COMPARISON SLIDER */}
|
{/* COMPARISON SLIDER */}
|
||||||
{!batchMode &&
|
{!batchMode &&
|
||||||
viewType === "slider" &&
|
viewType === "slider" &&
|
||||||
imagePath.length > 0 &&
|
imagePath.length > 0 &&
|
||||||
upscaledImagePath.length > 0 && (
|
upscaledImagePath.length > 0 && (
|
||||||
<>
|
<SliderView
|
||||||
<ReactCompareSlider
|
sanitizedImagePath={sanitizedImagePath}
|
||||||
itemOne={
|
sanitizedUpscaledImagePath={sanitizedUpscaledImagePath}
|
||||||
<>
|
zoomAmount={zoomAmount}
|
||||||
<p className="absolute bottom-1 left-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
|
|
||||||
{t("APP.SLIDER.ORIGINAL_TITLE")}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<img
|
|
||||||
/* USE REGEX TO GET THE FILENAME AND ENCODE IT INTO PROPER FORM IN ORDER TO AVOID ERRORS DUE TO SPECIAL CHARACTERS */
|
|
||||||
src={"file:///" + sanitizedImagePath}
|
|
||||||
alt={t("APP.SLIDER.ORIGINAL_TITLE")}
|
|
||||||
onMouseMove={handleMouseMove}
|
|
||||||
style={{
|
|
||||||
objectFit: "contain",
|
|
||||||
backgroundPosition: "0% 0%",
|
|
||||||
transformOrigin: backgroundPosition,
|
|
||||||
}}
|
|
||||||
className={`h-full w-full bg-gradient-to-br from-base-300 to-base-100 transition-transform group-hover:scale-[${zoomAmount}%]`}
|
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
}
|
|
||||||
itemTwo={
|
|
||||||
<>
|
|
||||||
<p className="absolute bottom-1 right-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
|
|
||||||
{t("APP.SLIDER.UPSCAYLED_TITLE")}
|
|
||||||
</p>
|
|
||||||
<img
|
|
||||||
/* USE REGEX TO GET THE FILENAME AND ENCODE IT INTO PROPER FORM IN ORDER TO AVOID ERRORS DUE TO SPECIAL CHARACTERS */
|
|
||||||
src={"file:///" + sanitizedUpscaledImagePath}
|
|
||||||
alt={t("APP.SLIDER.UPSCAYLED_TITLE")}
|
|
||||||
style={{
|
|
||||||
objectFit: "contain",
|
|
||||||
backgroundPosition: "0% 0%",
|
|
||||||
transformOrigin: backgroundPosition,
|
|
||||||
}}
|
|
||||||
onMouseMove={handleMouseMove}
|
|
||||||
className={`h-full w-full bg-gradient-to-br from-base-300 to-base-100 transition-transform group-hover:scale-[${
|
|
||||||
zoomAmount || "100%"
|
|
||||||
}%]`}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
className="group h-screen"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
92
renderer/components/main-content/lens-view.tsx
Normal file
92
renderer/components/main-content/lens-view.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import React, { useRef, useState } from "react";
|
||||||
|
|
||||||
|
const LensViewer = ({
|
||||||
|
zoomAmount,
|
||||||
|
lensSize,
|
||||||
|
sanitizedImagePath,
|
||||||
|
sanitizedUpscaledImagePath,
|
||||||
|
}: {
|
||||||
|
zoomAmount: string;
|
||||||
|
lensSize: number;
|
||||||
|
sanitizedImagePath: string;
|
||||||
|
sanitizedUpscaledImagePath: string;
|
||||||
|
}) => {
|
||||||
|
const upscaledImageRef = useRef<HTMLImageElement>(null);
|
||||||
|
|
||||||
|
const [lensPosition, setLensPosition] = useState({ x: 0, y: 0 });
|
||||||
|
|
||||||
|
const handleMouseMoveCompare = (e: React.MouseEvent) => {
|
||||||
|
if (upscaledImageRef.current) {
|
||||||
|
const { left, top, width, height } =
|
||||||
|
upscaledImageRef.current.getBoundingClientRect();
|
||||||
|
const x = e.clientX - left;
|
||||||
|
const y = e.clientY - top;
|
||||||
|
setLensPosition({
|
||||||
|
x: Math.max(0, Math.min(x - lensSize, width - lensSize * 2)),
|
||||||
|
y: Math.max(0, Math.min(y - lensSize / 2, height - lensSize)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="group relative h-full w-full overflow-hidden"
|
||||||
|
onMouseMove={handleMouseMoveCompare}
|
||||||
|
>
|
||||||
|
{/* UPSCALED IMAGE */}
|
||||||
|
<img
|
||||||
|
className="h-full w-full object-contain"
|
||||||
|
src={"file:///" + sanitizedUpscaledImagePath}
|
||||||
|
alt="Upscaled"
|
||||||
|
ref={upscaledImageRef}
|
||||||
|
/>
|
||||||
|
{/* LENS */}
|
||||||
|
<div
|
||||||
|
className="pointer-events-none absolute opacity-0 transition-opacity before:absolute before:left-1/2 before:h-full before:w-[2px] before:bg-white group-hover:opacity-100"
|
||||||
|
style={{
|
||||||
|
left: `${lensPosition.x}px`,
|
||||||
|
top: `${lensPosition.y}px`,
|
||||||
|
width: lensSize * 2,
|
||||||
|
height: lensSize,
|
||||||
|
border: "2px solid white",
|
||||||
|
boxShadow: "0 0 0 9999px rgba(0, 0, 0, 0.5)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex h-full w-full">
|
||||||
|
<div className="h-full w-full overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={"file:///" + sanitizedImagePath}
|
||||||
|
alt="Original"
|
||||||
|
className="h-full w-full"
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
objectPosition: `${-lensPosition.x}px ${-lensPosition.y}px`,
|
||||||
|
transform: `scale(${parseInt(zoomAmount) / 100})`,
|
||||||
|
transformOrigin: "top left",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="h-full w-full overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={"file:///" + sanitizedUpscaledImagePath}
|
||||||
|
alt="Upscaled"
|
||||||
|
className="h-full w-full"
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
objectPosition: `${-lensPosition.x}px ${-lensPosition.y}px`,
|
||||||
|
transform: `scale(${parseInt(zoomAmount) / 100})`,
|
||||||
|
transformOrigin: "top left",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute bottom-0 left-0 flex w-full items-center justify-around bg-black bg-opacity-50 p-1 px-2 text-center text-xs text-white backdrop-blur-sm">
|
||||||
|
<span>Original</span>
|
||||||
|
<span>Upscayl</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LensViewer;
|
@ -0,0 +1,7 @@
|
|||||||
|
const MacTitlebarDragRegion = () => {
|
||||||
|
return window.electron.platform === "mac" ? (
|
||||||
|
<div className="mac-titlebar absolute top-0 h-8 w-full"></div>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MacTitlebarDragRegion;
|
@ -1,22 +1,24 @@
|
|||||||
import React, { CSSProperties, useEffect, useMemo } from "react";
|
import React, { useEffect } from "react";
|
||||||
import Spinner from "../../icons/Spinner";
|
import UpscaylSVGLogo from "@/components/icons/Logo";
|
||||||
import Logo from "@/components/icons/Logo";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
|
import ELECTRON_COMMANDS from "@common/commands";
|
||||||
|
import useLogger from "../hooks/use-logger";
|
||||||
|
|
||||||
function ProgressBar({
|
function ProgressBar({
|
||||||
progress,
|
progress,
|
||||||
doubleUpscaylCounter,
|
doubleUpscaylCounter,
|
||||||
stopHandler,
|
|
||||||
batchMode,
|
batchMode,
|
||||||
|
resetImagePaths,
|
||||||
}: {
|
}: {
|
||||||
progress: string;
|
progress: string;
|
||||||
doubleUpscaylCounter: number;
|
doubleUpscaylCounter: number;
|
||||||
stopHandler: () => void;
|
|
||||||
batchMode: boolean;
|
batchMode: boolean;
|
||||||
|
resetImagePaths: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [batchProgress, setBatchProgress] = React.useState(0);
|
const [batchProgress, setBatchProgress] = React.useState(0);
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
const logit = useLogger();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const progressString = progress.trim().replace(/\n/g, "");
|
const progressString = progress.trim().replace(/\n/g, "");
|
||||||
@ -26,6 +28,12 @@ function ProgressBar({
|
|||||||
}
|
}
|
||||||
}, [progress]);
|
}, [progress]);
|
||||||
|
|
||||||
|
const stopHandler = () => {
|
||||||
|
window.electron.send(ELECTRON_COMMANDS.STOP);
|
||||||
|
logit("🛑 Stopping Upscayl");
|
||||||
|
resetImagePaths();
|
||||||
|
};
|
||||||
|
|
||||||
// const progressStyle = useMemo(() => {
|
// const progressStyle = useMemo(() => {
|
||||||
// if (progress.includes("%")) {
|
// if (progress.includes("%")) {
|
||||||
// return {
|
// return {
|
||||||
@ -44,11 +52,13 @@ function ProgressBar({
|
|||||||
return (
|
return (
|
||||||
<div className="absolute z-50 flex h-full w-full flex-col items-center justify-center bg-base-300/50 backdrop-blur-lg">
|
<div className="absolute z-50 flex h-full w-full flex-col items-center justify-center bg-base-300/50 backdrop-blur-lg">
|
||||||
<div className="flex flex-col items-center gap-2 rounded-btn bg-base-100/50 p-4 backdrop-blur-lg">
|
<div className="flex flex-col items-center gap-2 rounded-btn bg-base-100/50 p-4 backdrop-blur-lg">
|
||||||
<Logo className="spinner h-12 w-12" />
|
<UpscaylSVGLogo className="spinner h-12 w-12" />
|
||||||
|
|
||||||
<p className="rounded-full px-2 pb-2 font-bold">
|
<p className="rounded-full px-2 pb-2 font-bold">
|
||||||
{batchMode &&
|
{batchMode &&
|
||||||
`${t("APP.PROGRESS_BAR.BATCH_UPSCAYL_IN_PROGRESS_TITLE")} ${batchProgress}`}
|
`${t("APP.PROGRESS_BAR.BATCH_UPSCAYL_IN_PROGRESS_TITLE")} ${batchProgress}`}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-col items-center gap-1">
|
<div className="flex flex-col items-center gap-1">
|
||||||
{progress !== "Hold on..." ? (
|
{progress !== "Hold on..." ? (
|
||||||
<p className="text-sm font-bold">
|
<p className="text-sm font-bold">
|
||||||
@ -60,10 +70,12 @@ function ProgressBar({
|
|||||||
) : (
|
) : (
|
||||||
<p className="text-sm font-bold">{progress}</p>
|
<p className="text-sm font-bold">{progress}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="animate-pulse rounded-full px-2 pb-3 text-xs font-medium text-neutral-content/50">
|
<p className="animate-pulse rounded-full px-2 pb-3 text-xs font-medium text-neutral-content/50">
|
||||||
{t("APP.PROGRESS_BAR.IN_PROGRESS_TITLE")}
|
{t("APP.PROGRESS_BAR.IN_PROGRESS_TITLE")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button onClick={stopHandler} className="btn btn-outline">
|
<button onClick={stopHandler} className="btn btn-outline">
|
||||||
{t("APP.PROGRESS_BAR.STOP_BUTTON_TITLE")}
|
{t("APP.PROGRESS_BAR.STOP_BUTTON_TITLE")}
|
||||||
</button>
|
</button>
|
73
renderer/components/main-content/slider-view.tsx
Normal file
73
renderer/components/main-content/slider-view.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import React, { useCallback, useState } from "react";
|
||||||
|
import { ReactCompareSlider } from "react-compare-slider";
|
||||||
|
import useTranslation from "../hooks/use-translation";
|
||||||
|
|
||||||
|
const SliderView = ({
|
||||||
|
sanitizedImagePath,
|
||||||
|
sanitizedUpscaledImagePath,
|
||||||
|
zoomAmount,
|
||||||
|
}: {
|
||||||
|
sanitizedImagePath: string;
|
||||||
|
sanitizedUpscaledImagePath: string;
|
||||||
|
zoomAmount: string;
|
||||||
|
}) => {
|
||||||
|
const t = useTranslation();
|
||||||
|
|
||||||
|
const [backgroundPosition, setBackgroundPosition] = useState("0% 0%");
|
||||||
|
|
||||||
|
const handleMouseMove = useCallback((e: any) => {
|
||||||
|
const { left, top, width, height } = e.target.getBoundingClientRect();
|
||||||
|
const x = ((e.pageX - left) / width) * 100;
|
||||||
|
const y = ((e.pageY - top) / height) * 100;
|
||||||
|
setBackgroundPosition(`${x}% ${y}%`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactCompareSlider
|
||||||
|
itemOne={
|
||||||
|
<>
|
||||||
|
<p className="absolute bottom-1 left-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
|
||||||
|
{t("APP.SLIDER.ORIGINAL_TITLE")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<img
|
||||||
|
/* USE REGEX TO GET THE FILENAME AND ENCODE IT INTO PROPER FORM IN ORDER TO AVOID ERRORS DUE TO SPECIAL CHARACTERS */
|
||||||
|
src={"file:///" + sanitizedImagePath}
|
||||||
|
alt={t("APP.SLIDER.ORIGINAL_TITLE")}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
backgroundPosition: "0% 0%",
|
||||||
|
transformOrigin: backgroundPosition,
|
||||||
|
}}
|
||||||
|
className={`h-full w-full bg-gradient-to-br from-base-300 to-base-100 transition-transform group-hover:scale-[${zoomAmount}%]`}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
itemTwo={
|
||||||
|
<>
|
||||||
|
<p className="absolute bottom-1 right-1 rounded-md bg-black p-1 text-sm font-medium text-white opacity-30">
|
||||||
|
{t("APP.SLIDER.UPSCAYLED_TITLE")}
|
||||||
|
</p>
|
||||||
|
<img
|
||||||
|
/* USE REGEX TO GET THE FILENAME AND ENCODE IT INTO PROPER FORM IN ORDER TO AVOID ERRORS DUE TO SPECIAL CHARACTERS */
|
||||||
|
src={"file:///" + sanitizedUpscaledImagePath}
|
||||||
|
alt={t("APP.SLIDER.UPSCAYLED_TITLE")}
|
||||||
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
backgroundPosition: "0% 0%",
|
||||||
|
transformOrigin: backgroundPosition,
|
||||||
|
}}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
className={`h-full w-full bg-gradient-to-br from-base-300 to-base-100 transition-transform group-hover:scale-[${
|
||||||
|
zoomAmount || "100%"
|
||||||
|
}%]`}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
className="group h-screen"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SliderView;
|
@ -1,26 +1,26 @@
|
|||||||
import { ThemeSelect } from "./ThemeSelect";
|
import { SelectTheme } from "./select-theme";
|
||||||
import { SaveOutputFolderToggle } from "./SaveOutputFolderToggle";
|
import { SaveOutputFolderToggle } from "./save-output-folder-toggle";
|
||||||
import { GpuIdInput } from "./GpuIdInput";
|
import { InputGpuId } from "./input-gpu-id";
|
||||||
import { CustomModelsFolderSelect } from "./CustomModelsFolderSelect";
|
import { CustomModelsFolderSelect } from "./select-custom-models-folder";
|
||||||
import { LogArea } from "./LogArea";
|
import { LogArea } from "./log-area";
|
||||||
import { ImageScaleSelect } from "./ImageScaleSelect";
|
import { SelectImageScale } from "./select-image-scale";
|
||||||
import { ImageFormatSelect } from "./ImageFormatSelect";
|
import { SelectImageFormat } from "./select-image-format";
|
||||||
import { DonateButton } from "./DonateButton";
|
import { DonateButton } from "./donate-button";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { themeChange } from "theme-change";
|
import { themeChange } from "theme-change";
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import { customModelsPathAtom, scaleAtom } from "../../atoms/userSettingsAtom";
|
import { customModelsPathAtom, scaleAtom } from "../../atoms/userSettingsAtom";
|
||||||
import { modelsListAtom } from "../../atoms/modelsListAtom";
|
import { modelsListAtom } from "../../atoms/modelsListAtom";
|
||||||
import useLog from "../hooks/useLog";
|
import useLogger from "../hooks/use-logger";
|
||||||
import { CompressionInput } from "./CompressionInput";
|
import { InputCompression } from "./input-compression";
|
||||||
import OverwriteToggle from "./OverwriteToggle";
|
import OverwriteToggle from "./overwrite-toggle";
|
||||||
import { UpscaylCloudModal } from "../UpscaylCloudModal";
|
import { UpscaylCloudModal } from "../UpscaylCloudModal";
|
||||||
import { ResetSettings } from "./ResetSettings";
|
import { ResetSettingsButton } from "./reset-settings-button";
|
||||||
import { FEATURE_FLAGS } from "@common/feature-flags";
|
import { FEATURE_FLAGS } from "@common/feature-flags";
|
||||||
import TurnOffNotificationsToggle from "./TurnOffNotificationsToggle";
|
import TurnOffNotificationsToggle from "./turn-off-notifications-toggle";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { CustomResolutionInput } from "./CustomResolutionInput";
|
import { InputCustomResolution } from "./input-custom-resolution";
|
||||||
import { TileSizeInput } from "./TileSizeInput";
|
import { InputTileSize } from "./input-tile-size";
|
||||||
import LanguageSwitcher from "./language-switcher";
|
import LanguageSwitcher from "./language-switcher";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
|
|
||||||
@ -34,7 +34,6 @@ interface IProps {
|
|||||||
gpuId: string;
|
gpuId: string;
|
||||||
setGpuId: React.Dispatch<React.SetStateAction<string>>;
|
setGpuId: React.Dispatch<React.SetStateAction<string>>;
|
||||||
logData: string[];
|
logData: string[];
|
||||||
os: "linux" | "mac" | "win" | undefined;
|
|
||||||
show: boolean;
|
show: boolean;
|
||||||
setShow: React.Dispatch<React.SetStateAction<boolean>>;
|
setShow: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setDontShowCloudModal: React.Dispatch<React.SetStateAction<boolean>>;
|
setDontShowCloudModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
@ -50,19 +49,11 @@ function SettingsTab({
|
|||||||
saveImageAs,
|
saveImageAs,
|
||||||
setSaveImageAs,
|
setSaveImageAs,
|
||||||
logData,
|
logData,
|
||||||
os,
|
|
||||||
show,
|
show,
|
||||||
setShow,
|
setShow,
|
||||||
setDontShowCloudModal,
|
setDontShowCloudModal,
|
||||||
}: IProps) {
|
}: IProps) {
|
||||||
// STATES
|
|
||||||
const [currentModel, setCurrentModel] = useState<{
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}>({
|
|
||||||
label: null,
|
|
||||||
value: null,
|
|
||||||
});
|
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
|
|
||||||
const [customModelsPath, setCustomModelsPath] = useAtom(customModelsPathAtom);
|
const [customModelsPath, setCustomModelsPath] = useAtom(customModelsPathAtom);
|
||||||
@ -72,7 +63,7 @@ function SettingsTab({
|
|||||||
const [timeoutId, setTimeoutId] = useState(null);
|
const [timeoutId, setTimeoutId] = useState(null);
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
themeChange(false);
|
themeChange(false);
|
||||||
@ -90,7 +81,6 @@ function SettingsTab({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!localStorage.getItem("model")) {
|
if (!localStorage.getItem("model")) {
|
||||||
setCurrentModel(modelOptions[0]);
|
|
||||||
setModel(modelOptions[0].value);
|
setModel(modelOptions[0].value);
|
||||||
localStorage.setItem("model", JSON.stringify(modelOptions[0]));
|
localStorage.setItem("model", JSON.stringify(modelOptions[0]));
|
||||||
logit("🔀 Setting model to", modelOptions[0].value);
|
logit("🔀 Setting model to", modelOptions[0].value);
|
||||||
@ -107,7 +97,6 @@ function SettingsTab({
|
|||||||
logit("🔀 Setting model to", modelOptions[0].value);
|
logit("🔀 Setting model to", modelOptions[0].value);
|
||||||
currentlySavedModel = modelOptions[0];
|
currentlySavedModel = modelOptions[0];
|
||||||
}
|
}
|
||||||
setCurrentModel(currentlySavedModel);
|
|
||||||
setModel(currentlySavedModel.value);
|
setModel(currentlySavedModel.value);
|
||||||
logit(
|
logit(
|
||||||
"⚙️ Getting model from localStorage: ",
|
"⚙️ Getting model from localStorage: ",
|
||||||
@ -213,23 +202,23 @@ function SettingsTab({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* THEME SELECTOR */}
|
{/* THEME SELECTOR */}
|
||||||
<ThemeSelect />
|
<SelectTheme />
|
||||||
|
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
|
|
||||||
{/* IMAGE FORMAT BUTTONS */}
|
{/* IMAGE FORMAT BUTTONS */}
|
||||||
<ImageFormatSelect
|
<SelectImageFormat
|
||||||
batchMode={batchMode}
|
batchMode={batchMode}
|
||||||
saveImageAs={saveImageAs}
|
saveImageAs={saveImageAs}
|
||||||
setExportType={setExportType}
|
setExportType={setExportType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* IMAGE SCALE */}
|
{/* IMAGE SCALE */}
|
||||||
<ImageScaleSelect scale={scale} setScale={setScale} />
|
<SelectImageScale scale={scale} setScale={setScale} />
|
||||||
|
|
||||||
<CustomResolutionInput />
|
<InputCustomResolution />
|
||||||
|
|
||||||
<CompressionInput
|
<InputCompression
|
||||||
compression={compression}
|
compression={compression}
|
||||||
handleCompressionChange={handleCompressionChange}
|
handleCompressionChange={handleCompressionChange}
|
||||||
/>
|
/>
|
||||||
@ -240,9 +229,9 @@ function SettingsTab({
|
|||||||
<TurnOffNotificationsToggle />
|
<TurnOffNotificationsToggle />
|
||||||
|
|
||||||
{/* GPU ID INPUT */}
|
{/* GPU ID INPUT */}
|
||||||
<GpuIdInput gpuId={gpuId} handleGpuIdChange={handleGpuIdChange} />
|
<InputGpuId gpuId={gpuId} handleGpuIdChange={handleGpuIdChange} />
|
||||||
|
|
||||||
<TileSizeInput />
|
<InputTileSize />
|
||||||
|
|
||||||
{/* CUSTOM MODEL */}
|
{/* CUSTOM MODEL */}
|
||||||
<CustomModelsFolderSelect
|
<CustomModelsFolderSelect
|
||||||
@ -251,7 +240,7 @@ function SettingsTab({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* RESET SETTINGS */}
|
{/* RESET SETTINGS */}
|
||||||
<ResetSettings />
|
<ResetSettingsButton />
|
||||||
|
|
||||||
{FEATURE_FLAGS.SHOW_UPSCAYL_CLOUD_INFO && (
|
{FEATURE_FLAGS.SHOW_UPSCAYL_CLOUD_INFO && (
|
||||||
<>
|
<>
|
||||||
|
@ -6,7 +6,7 @@ type CompressionInputProps = {
|
|||||||
handleCompressionChange: (arg: any) => void;
|
handleCompressionChange: (arg: any) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CompressionInput({
|
export function InputCompression({
|
||||||
compression,
|
compression,
|
||||||
handleCompressionChange,
|
handleCompressionChange,
|
||||||
}: CompressionInputProps) {
|
}: CompressionInputProps) {
|
@ -3,7 +3,7 @@ import { useAtom, useAtomValue } from "jotai";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
|
|
||||||
export function CustomResolutionInput() {
|
export function InputCustomResolution() {
|
||||||
const [useCustomWidth, setUseCustomWidth] = useAtom(useCustomWidthAtom);
|
const [useCustomWidth, setUseCustomWidth] = useAtom(useCustomWidthAtom);
|
||||||
const [customWidth, setCustomWidth] = useAtom(customWidthAtom);
|
const [customWidth, setCustomWidth] = useAtom(customWidthAtom);
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
@ -7,7 +7,7 @@ type GpuIdInputProps = {
|
|||||||
handleGpuIdChange: (arg: string) => void;
|
handleGpuIdChange: (arg: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function GpuIdInput({ gpuId, handleGpuIdChange }) {
|
export function InputGpuId({ gpuId, handleGpuIdChange }) {
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
||||||
return (
|
return (
|
@ -3,7 +3,7 @@ import { tileSizeAtom } from "@/atoms/userSettingsAtom";
|
|||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export function TileSizeInput() {
|
export function InputTileSize() {
|
||||||
const [tileSize, setTileSize] = useAtom(tileSizeAtom);
|
const [tileSize, setTileSize] = useAtom(tileSizeAtom);
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
@ -2,7 +2,7 @@ import { translationAtom } from "@/atoms/translations-atom";
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export function ResetSettings() {
|
export function ResetSettingsButton() {
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-start gap-2">
|
<div className="flex flex-col items-start gap-2">
|
@ -7,7 +7,7 @@ type ImageFormatSelectProps = {
|
|||||||
setExportType: (arg: string) => void;
|
setExportType: (arg: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ImageFormatSelect({
|
export function SelectImageFormat({
|
||||||
batchMode,
|
batchMode,
|
||||||
saveImageAs,
|
saveImageAs,
|
||||||
setExportType,
|
setExportType,
|
@ -8,7 +8,7 @@ type ImageScaleSelectProps = {
|
|||||||
hideInfo?: boolean;
|
hideInfo?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ImageScaleSelect({
|
export function SelectImageScale({
|
||||||
scale,
|
scale,
|
||||||
setScale,
|
setScale,
|
||||||
hideInfo,
|
hideInfo,
|
@ -1,7 +1,8 @@
|
|||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
export function ThemeSelect() {
|
|
||||||
|
export function SelectTheme() {
|
||||||
const availableThemes = [
|
const availableThemes = [
|
||||||
{ label: "upscayl", value: "upscayl" },
|
{ label: "upscayl", value: "upscayl" },
|
||||||
{ label: "light", value: "light" },
|
{ label: "light", value: "light" },
|
@ -15,27 +15,26 @@ import {
|
|||||||
tileSizeAtom,
|
tileSizeAtom,
|
||||||
showSidebarAtom,
|
showSidebarAtom,
|
||||||
} from "../../atoms/userSettingsAtom";
|
} from "../../atoms/userSettingsAtom";
|
||||||
import useLog from "../../components/hooks/useLog";
|
import useLogger from "../hooks/use-logger";
|
||||||
import {
|
import {
|
||||||
BatchUpscaylPayload,
|
BatchUpscaylPayload,
|
||||||
DoubleUpscaylPayload,
|
DoubleUpscaylPayload,
|
||||||
ImageUpscaylPayload,
|
ImageUpscaylPayload,
|
||||||
} from "@common/types/types";
|
} from "@common/types/types";
|
||||||
import { newsAtom, showNewsModalAtom } from "@/atoms/newsAtom";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import Logo from "@/components/icons/Logo";
|
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
|
||||||
import LeftPaneImageSteps from "../upscayl-tab/config/LeftPaneImageSteps";
|
import LeftPaneImageSteps from "../upscayl-tab/config/LeftPaneImageSteps";
|
||||||
import SettingsTab from "../settings-tab";
|
import SettingsTab from "../settings-tab";
|
||||||
import Footer from "../Footer";
|
import Footer from "../Footer";
|
||||||
import { NewsModal } from "../NewsModal";
|
import { NewsModal } from "../NewsModal";
|
||||||
import Tabs from "../Tabs";
|
import Tabs from "../Tabs";
|
||||||
import Header from "../Header";
|
import Header from "../Header";
|
||||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
import { ChevronLeftIcon } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { logAtom } from "@/atoms/logAtom";
|
import { logAtom } from "@/atoms/logAtom";
|
||||||
import ELECTRON_COMMANDS from "@common/commands";
|
import ELECTRON_COMMANDS from "@common/commands";
|
||||||
import useUpscaylVersion from "../hooks/use-upscayl-version";
|
import useUpscaylVersion from "../hooks/use-upscayl-version";
|
||||||
|
import useTranslation from "../hooks/use-translation";
|
||||||
|
import UpscaylLogo from "./upscayl-logo";
|
||||||
|
import SidebarToggleButton from "./sidebar-button";
|
||||||
|
|
||||||
const Sidebar = ({
|
const Sidebar = ({
|
||||||
setUpscaledImagePath,
|
setUpscaledImagePath,
|
||||||
@ -57,14 +56,13 @@ const Sidebar = ({
|
|||||||
selectImageHandler: () => Promise<void>;
|
selectImageHandler: () => Promise<void>;
|
||||||
selectFolderHandler: () => Promise<void>;
|
selectFolderHandler: () => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useTranslation();
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const version = useUpscaylVersion();
|
const version = useUpscaylVersion();
|
||||||
|
|
||||||
// LOCAL STATES
|
// LOCAL STATES
|
||||||
// TODO: Add electron handler for os
|
// TODO: Add electron handler for os
|
||||||
const [os, setOs] = useState<"linux" | "mac" | "win" | undefined>(undefined);
|
|
||||||
const [model, setModel] = useState("realesrgan-x4plus");
|
const [model, setModel] = useState("realesrgan-x4plus");
|
||||||
const [doubleUpscayl, setDoubleUpscayl] = useState(false);
|
const [doubleUpscayl, setDoubleUpscayl] = useState(false);
|
||||||
const overwrite = useAtomValue(overwriteAtom);
|
const overwrite = useAtomValue(overwriteAtom);
|
||||||
@ -83,8 +81,6 @@ const Sidebar = ({
|
|||||||
const [scale] = useAtom(scaleAtom);
|
const [scale] = useAtom(scaleAtom);
|
||||||
const setDontShowCloudModal = useSetAtom(dontShowCloudModalAtom);
|
const setDontShowCloudModal = useSetAtom(dontShowCloudModalAtom);
|
||||||
const noImageProcessing = useAtomValue(noImageProcessingAtom);
|
const noImageProcessing = useAtomValue(noImageProcessingAtom);
|
||||||
const [news, setNews] = useAtom(newsAtom);
|
|
||||||
const [showNewsModal, setShowNewsModal] = useAtom(showNewsModalAtom);
|
|
||||||
const customWidth = useAtomValue(customWidthAtom);
|
const customWidth = useAtomValue(customWidthAtom);
|
||||||
const useCustomWidth = useAtomValue(useCustomWidthAtom);
|
const useCustomWidth = useAtomValue(useCustomWidthAtom);
|
||||||
const tileSize = useAtomValue(tileSizeAtom);
|
const tileSize = useAtomValue(tileSizeAtom);
|
||||||
@ -174,25 +170,13 @@ const Sidebar = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* TOP LOGO WHEN SIDEBAR IS HIDDEN */}
|
{/* TOP LOGO WHEN SIDEBAR IS HIDDEN */}
|
||||||
{!showSidebar && (
|
{!showSidebar && <UpscaylLogo />}
|
||||||
<div className="fixed right-2 top-2 z-50 flex items-center justify-center gap-2 rounded-[7px] bg-base-300 px-2 py-1 font-medium text-base-content ">
|
|
||||||
<Logo className="w-5" />
|
|
||||||
{t("TITLE")}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* SIDEBAR BUTTON */}
|
<SidebarToggleButton
|
||||||
<button
|
showSidebar={showSidebar}
|
||||||
className={cn(
|
setShowSidebar={setShowSidebar}
|
||||||
"fixed left-0 top-1/2 z-[999] -translate-y-1/2 rounded-r-full bg-base-100 p-4 ",
|
/>
|
||||||
showSidebar ? "hidden" : "",
|
|
||||||
)}
|
|
||||||
onClick={() => setShowSidebar((prev) => !prev)}
|
|
||||||
>
|
|
||||||
<ChevronRightIcon />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* LEFT PANE */}
|
|
||||||
<div
|
<div
|
||||||
className={`relative flex h-screen min-w-[350px] max-w-[350px] flex-col bg-base-100 ${showSidebar ? "" : "hidden"}`}
|
className={`relative flex h-screen min-w-[350px] max-w-[350px] flex-col bg-base-100 ${showSidebar ? "" : "hidden"}`}
|
||||||
>
|
>
|
||||||
@ -203,22 +187,13 @@ const Sidebar = ({
|
|||||||
<ChevronLeftIcon />
|
<ChevronLeftIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* MACOS TITLEBAR */}
|
|
||||||
{window.electron.platform === "mac" && (
|
{window.electron.platform === "mac" && (
|
||||||
<div className="mac-titlebar pt-8"></div>
|
<div className="mac-titlebar pt-8"></div>
|
||||||
)}
|
)}
|
||||||
{/* HEADER */}
|
|
||||||
<Header version={version} />
|
<Header version={version} />
|
||||||
|
|
||||||
{/* NEWS DIALOG */}
|
<NewsModal />
|
||||||
<NewsModal
|
|
||||||
show={showNewsModal}
|
|
||||||
setShow={(val: boolean) => {
|
|
||||||
setShowNewsModal(val);
|
|
||||||
setNews((prev) => ({ ...prev, seen: true }));
|
|
||||||
}}
|
|
||||||
news={news}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Tabs selectedTab={selectedTab} setSelectedTab={setSelectedTab} />
|
<Tabs selectedTab={selectedTab} setSelectedTab={setSelectedTab} />
|
||||||
|
|
||||||
@ -252,13 +227,11 @@ const Sidebar = ({
|
|||||||
saveImageAs={saveImageAs}
|
saveImageAs={saveImageAs}
|
||||||
setSaveImageAs={setSaveImageAs}
|
setSaveImageAs={setSaveImageAs}
|
||||||
logData={logData}
|
logData={logData}
|
||||||
os={os}
|
|
||||||
show={showCloudModal}
|
show={showCloudModal}
|
||||||
setShow={setShowCloudModal}
|
setShow={setShowCloudModal}
|
||||||
setDontShowCloudModal={setDontShowCloudModal}
|
setDontShowCloudModal={setDontShowCloudModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* )} */}
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
25
renderer/components/sidebar/sidebar-button.tsx
Normal file
25
renderer/components/sidebar/sidebar-button.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ChevronRightIcon } from "lucide-react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const SidebarToggleButton = ({
|
||||||
|
showSidebar,
|
||||||
|
setShowSidebar,
|
||||||
|
}: {
|
||||||
|
showSidebar: boolean;
|
||||||
|
setShowSidebar: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
"fixed left-0 top-1/2 z-[999] -translate-y-1/2 rounded-r-full bg-base-100 p-4 ",
|
||||||
|
showSidebar ? "hidden" : "",
|
||||||
|
)}
|
||||||
|
onClick={() => setShowSidebar((prev) => !prev)}
|
||||||
|
>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarToggleButton;
|
15
renderer/components/sidebar/upscayl-logo.tsx
Normal file
15
renderer/components/sidebar/upscayl-logo.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import UpscaylSVGLogo from "../icons/Logo";
|
||||||
|
import useTranslation from "../hooks/use-translation";
|
||||||
|
|
||||||
|
const UpscaylLogo = () => {
|
||||||
|
const t = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed right-2 top-2 z-50 flex items-center justify-center gap-2 rounded-[7px] bg-base-300 px-2 py-1 font-medium text-base-content ">
|
||||||
|
<UpscaylSVGLogo className="w-5" />
|
||||||
|
{t("TITLE")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpscaylLogo;
|
@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useState } from "react";
|
|||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import { themeChange } from "theme-change";
|
import { themeChange } from "theme-change";
|
||||||
import { TModelsList, modelsListAtom } from "../../../atoms/modelsListAtom";
|
import { TModelsList, modelsListAtom } from "../../../atoms/modelsListAtom";
|
||||||
import useLog from "../../hooks/useLog";
|
import useLogger from "../../hooks/use-logger";
|
||||||
import {
|
import {
|
||||||
noImageProcessingAtom,
|
noImageProcessingAtom,
|
||||||
savedOutputPathAtom,
|
savedOutputPathAtom,
|
||||||
@ -76,7 +76,7 @@ function LeftPaneImageSteps({
|
|||||||
const [targetWidth, setTargetWidth] = useState<number>(null);
|
const [targetWidth, setTargetWidth] = useState<number>(null);
|
||||||
const [targetHeight, setTargetHeight] = useState<number>(null);
|
const [targetHeight, setTargetHeight] = useState<number>(null);
|
||||||
|
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { useAtom, useAtomValue } from "jotai";
|
|||||||
import { WrenchIcon } from "lucide-react";
|
import { WrenchIcon } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
const ImageOptions = ({
|
const ImageViewSettings = ({
|
||||||
zoomAmount,
|
zoomAmount,
|
||||||
setZoomAmount,
|
setZoomAmount,
|
||||||
resetImagePaths,
|
resetImagePaths,
|
||||||
@ -113,4 +113,4 @@ const ImageOptions = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ImageOptions;
|
export default ImageViewSettings;
|
||||||
|
@ -2,7 +2,7 @@ import { translationAtom } from "@/atoms/translations-atom";
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
function RightPaneInfo({ version, batchMode }) {
|
function InformationCard({ version, batchMode }) {
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -26,4 +26,4 @@ function RightPaneInfo({ version, batchMode }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RightPaneInfo;
|
export default InformationCard;
|
||||||
|
@ -9,12 +9,12 @@ import {
|
|||||||
progressAtom,
|
progressAtom,
|
||||||
rememberOutputFolderAtom,
|
rememberOutputFolderAtom,
|
||||||
} from "../atoms/userSettingsAtom";
|
} from "../atoms/userSettingsAtom";
|
||||||
import useLog from "../components/hooks/useLog";
|
import useLogger from "../components/hooks/use-logger";
|
||||||
import { newsAtom, showNewsModalAtom } from "@/atoms/newsAtom";
|
import { newsAtom, showNewsModalAtom } from "@/atoms/newsAtom";
|
||||||
import matter from "gray-matter";
|
import matter from "gray-matter";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { ToastAction } from "@/components/ui/toast";
|
import { ToastAction } from "@/components/ui/toast";
|
||||||
import Logo from "@/components/icons/Logo";
|
import UpscaylSVGLogo from "@/components/icons/Logo";
|
||||||
import { translationAtom } from "@/atoms/translations-atom";
|
import { translationAtom } from "@/atoms/translations-atom";
|
||||||
import Sidebar from "@/components/sidebar";
|
import Sidebar from "@/components/sidebar";
|
||||||
import MainContent from "@/components/main-content";
|
import MainContent from "@/components/main-content";
|
||||||
@ -25,7 +25,7 @@ import { initCustomModels } from "@/components/hooks/use-custom-models";
|
|||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const t = useAtomValue(translationAtom);
|
const t = useAtomValue(translationAtom);
|
||||||
const { logit } = useLog();
|
const logit = useLogger();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
initCustomModels();
|
initCustomModels();
|
||||||
@ -279,47 +279,6 @@ const Home = () => {
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// FETCH NEWS
|
|
||||||
useEffect(() => {
|
|
||||||
// TODO: ADD AN ABOUT TAB
|
|
||||||
if (window && window.navigator.onLine === false) return;
|
|
||||||
try {
|
|
||||||
fetch("https://raw.githubusercontent.com/upscayl/upscayl/main/news.md", {
|
|
||||||
cache: "no-cache",
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
return res.text();
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
const newsData = result;
|
|
||||||
if (!newsData) {
|
|
||||||
console.log("📰 Could not fetch news data");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const markdownData = matter(newsData);
|
|
||||||
if (!markdownData) return;
|
|
||||||
if (markdownData && markdownData.data.dontShow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
markdownData &&
|
|
||||||
news &&
|
|
||||||
markdownData?.data?.version === news?.data?.version
|
|
||||||
) {
|
|
||||||
console.log("📰 News is up to date");
|
|
||||||
if (showNewsModal === false) {
|
|
||||||
setShowNewsModal(false);
|
|
||||||
}
|
|
||||||
} else if (markdownData) {
|
|
||||||
setNews(matter(newsData));
|
|
||||||
setShowNewsModal(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Could not fetch Upscayl News");
|
|
||||||
}
|
|
||||||
}, [news]);
|
|
||||||
|
|
||||||
// LOADING STATE
|
// LOADING STATE
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -341,7 +300,7 @@ const Home = () => {
|
|||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Logo className="absolute left-1/2 top-1/2 w-36 -translate-x-1/2 -translate-y-1/2 animate-pulse" />
|
<UpscaylSVGLogo className="absolute left-1/2 top-1/2 w-36 -translate-x-1/2 -translate-y-1/2 animate-pulse" />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user