diff --git a/package-lock.json b/package-lock.json
index 827700b78..b84776b8a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,6 +29,7 @@
"@mui/joy": "5.0.0-beta.24",
"@mui/material": "5.15.6",
"@next/mdx": "14.1.0",
+ "@playwright/test": "^1.41.2",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
@@ -5085,6 +5086,21 @@
"url": "https://opencollective.com/unts"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.41.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz",
+ "integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==",
+ "dev": true,
+ "dependencies": {
+ "playwright": "1.41.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/@pnpm/config.env-replace": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
@@ -20938,6 +20954,50 @@
"node": ">=4"
}
},
+ "node_modules/playwright": {
+ "version": "1.41.2",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz",
+ "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.41.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.41.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz",
+ "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/playwright/node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
diff --git a/package.json b/package.json
index 31dbca1e0..f427bbcd0 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"build": "nextron build",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"dev": "nextron",
- "eslint": "eslint \"{renderer,main}/{**/*,*}{.,.config.,.test.,.e2e.}{js,mjs,ts,tsx}\"",
+ "eslint": "eslint \"src/{**/*,*}.{ts,tsx}\"",
"postinstall": "electron-builder install-app-deps",
"ncu": "npx npm-check-updates@latest -u",
"ncu:check": "npx npm-check-updates@latest",
@@ -21,7 +21,9 @@
"toc": "npx markdown-toc README.md -i",
"tsc:noEmit": "tsc --noEmit",
"caption:test": "ts-node-esm main/captions/misc.ts",
- "test:unit": "jest --runInBand --config jest.config.unit.mjs --verbose"
+ "test:unit": "jest --runInBand --config jest.config.unit.mjs --verbose",
+ "pretest:e2e": "nextron build --no-pack",
+ "test:e2e": "npx playwright test"
},
"dependencies": {
"electron-context-menu": "3.6.1",
@@ -43,6 +45,7 @@
"@mui/joy": "5.0.0-beta.24",
"@mui/material": "5.15.6",
"@next/mdx": "14.1.0",
+ "@playwright/test": "^1.41.2",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
diff --git a/playwright-report/index.html b/playwright-report/index.html
new file mode 100644
index 000000000..76188316c
--- /dev/null
+++ b/playwright-report/index.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+ Playwright Test Report
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 000000000..f8805f1b4
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,10 @@
+import type { PlaywrightTestConfig } from "@playwright/test";
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+const config: PlaywrightTestConfig = {
+ testDir: "./playwright",
+};
+
+export default config;
diff --git a/playwright/index.test.ts b/playwright/index.test.ts
new file mode 100644
index 000000000..b8e9357e6
--- /dev/null
+++ b/playwright/index.test.ts
@@ -0,0 +1,33 @@
+import type { ElectronApplication, Page } from "@playwright/test";
+import { test, expect } from "@playwright/test";
+import { _electron as electron } from "playwright";
+
+let electronApp: ElectronApplication;
+let page: Page;
+
+test.beforeAll(async () => {
+ // Use package.main
+ electronApp = await electron.launch({ args: ["."] });
+ const isPackaged = await electronApp.evaluate(async ({ app }) => app.isPackaged);
+
+ expect(isPackaged).toBe(false);
+});
+
+test.afterAll(async () => {
+ await electronApp.close();
+});
+
+test("Renders the first page", async () => {
+ page = await electronApp.firstWindow();
+ const title = await page.title();
+ expect(title).toBe("Blibla");
+});
+
+test("Allows switching the language", async () => {
+ page = await electronApp.firstWindow();
+
+ await expect(page.getByTestId("language-selector-list")).toBeVisible();
+ await expect(page.getByText("Deutsch")).toBeVisible();
+ await page.getByText("Deutsch").click();
+ await expect(page.getByText("Sprache")).toBeVisible();
+});
diff --git a/src/client/organisms/delete-confirm/index.tsx b/src/client/organisms/delete-confirm/index.tsx
deleted file mode 100644
index f9e02bbea..000000000
--- a/src/client/organisms/delete-confirm/index.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import CancelIcon from "@mui/icons-material/Cancel";
-import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
-import Button from "@mui/joy/Button";
-import IconButton from "@mui/joy/IconButton";
-import Sheet from "@mui/joy/Sheet";
-import { useAtom } from "jotai";
-import { useTranslation } from "next-i18next";
-import { useState } from "react";
-
-import { datasetsAtom } from "@/ions/atoms";
-
-export function DeleteConfirm({ projectId }: { projectId: string }) {
- const [confirm, setConfirm] = useState(false);
- const [, setDatasets] = useAtom(datasetsAtom);
- const { t } = useTranslation(["common"]);
- return confirm ? (
-
- }
- onClick={() => {
- setConfirm(false);
- }}
- >
- {t("common:cancel")}
-
- }
- onClick={async () => {
- await window.ipc.deleteDataset(projectId);
- await window.ipc.getDatasets().then((datasets_: any) => {
- setDatasets(datasets_);
- });
- }}
- >
- {t("common:delete")}
-
-
- ) : (
- {
- setConfirm(true);
- }}
- >
-
-
- );
-}
diff --git a/src/client/organisms/folder-drop/index.tsx b/src/client/organisms/folder-drop/index.tsx
deleted file mode 100644
index d849617cd..000000000
--- a/src/client/organisms/folder-drop/index.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import Box from "@mui/joy/Box";
-import type { ReactNode } from "react";
-import { useState } from "react";
-
-export function FolderDrop({
- children,
- onDrop,
-}: {
- children?: ReactNode;
- onDrop?(path: string): void;
-}) {
- const [dragCounter, setDragCounter] = useState(0);
-
- const isDragOver = dragCounter > 0;
-
- return (
- {
- event.preventDefault();
- }}
- onDragEnter={event => {
- event.preventDefault();
- setDragCounter(previousState => previousState + 1);
- }}
- onDragLeave={event => {
- event.preventDefault();
- setDragCounter(previousState => previousState - 1);
- }}
- onDrop={event => {
- event.preventDefault();
- setDragCounter(0);
- const folder = event.dataTransfer.items[0];
- if (folder?.kind === "file" && folder.webkitGetAsEntry()?.isDirectory && onDrop) {
- const file = folder.getAsFile();
- if (file?.path) {
- onDrop(file.path);
- }
- }
- }}
- >
- {children}
-
- );
-}
diff --git a/src/client/organisms/folder-field/index.tsx b/src/client/organisms/folder-field/index.tsx
deleted file mode 100644
index 2bc380560..000000000
--- a/src/client/organisms/folder-field/index.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import FolderIcon from "@mui/icons-material/Folder";
-import FormControl from "@mui/joy/FormControl";
-import FormHelperText from "@mui/joy/FormHelperText";
-import FormLabel from "@mui/joy/FormLabel";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import type { InputProps } from "@mui/joy/Input";
-import { useTranslation } from "next-i18next";
-import type { ReactNode } from "react";
-import type { Except } from "type-fest";
-
-export function FolderField({
- helpText,
- error,
- label,
- onSelect,
- ...properties
-}: Except & {
- helpText?: ReactNode;
- label?: ReactNode;
- error?: string;
- onSelect?(value: string): Promise | void;
-}) {
- const { t } = useTranslation(["common"]);
- return (
-
- {label && {label}}
- {
- try {
- const directory_ = await window.ipc.getDirectory();
- if (directory_ && onSelect) {
- onSelect(directory_);
- }
- } catch (error) {
- console.log(error);
- }
- }}
- >
-
-
- }
- />
- {(error || helpText) && {error || helpText}}
-
- );
-}
diff --git a/src/client/organisms/live-painting/drawing-canvas.tsx b/src/client/organisms/live-painting/drawing-canvas.tsx
deleted file mode 100644
index 44eb119f7..000000000
--- a/src/client/organisms/live-painting/drawing-canvas.tsx
+++ /dev/null
@@ -1,196 +0,0 @@
-import Brush from "@mui/icons-material/Brush";
-import Clear from "@mui/icons-material/Clear";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import Stack from "@mui/joy/Stack";
-import { Box } from "@mui/material";
-import type { MouseEvent } from "react";
-import { useRef, useEffect, useState } from "react";
-
-interface DrawingCanvasProperties {
- width?: number;
- height?: number;
-}
-
-export function DrawingCanvas({ width = 512, height = 512 }: DrawingCanvasProperties) {
- const canvasReference = useRef(null);
- const [isDrawing, setIsDrawing] = useState(false);
- const [strokeColor, setStrokeColor] = useState("#00ff00");
- const [brushSize, setBrushSize] = useState(30);
- const [backgroundColor] = useState("#ffffff");
- const brushReference = useRef(null);
-
- function startDrawing({ nativeEvent }: MouseEvent) {
- const context = canvasReference.current?.getContext("2d");
- if (context) {
- const { offsetX, offsetY } = nativeEvent;
- context.strokeStyle = strokeColor;
- context.lineWidth = brushSize;
- context.beginPath();
- context.moveTo(offsetX, offsetY);
- setIsDrawing(true);
- }
- }
-
- function finishDrawing() {
- const context = canvasReference.current?.getContext("2d");
- if (context) {
- context.closePath();
- setIsDrawing(false);
- }
- }
-
- function draw({ nativeEvent }: MouseEvent) {
- const { offsetX, offsetY } = nativeEvent;
- if (brushReference.current) {
- brushReference.current.style.transform = `translate3d(calc(${offsetX}px - 50%), calc(${offsetY}px - 50%), 0)`;
- }
-
- if (!isDrawing) {
- return;
- }
-
- const context = canvasReference.current?.getContext("2d");
- if (context) {
- context.strokeStyle = strokeColor;
- context.lineWidth = brushSize;
- context.lineTo(offsetX, offsetY);
- context.stroke();
- }
- }
-
- function clearCanvas() {
- if (canvasReference.current) {
- const canvas = canvasReference.current;
- const context = canvas?.getContext("2d");
-
- if (context) {
- context.clearRect(0, 0, canvas.width, canvas.height);
- context.fillStyle = backgroundColor;
- context.fillRect(0, 0, canvas.width, canvas.height);
- }
- }
- }
-
- useEffect(() => {
- // Keep this function local to this side effect
- // it simplifies the usage and reduces the number of required hooks
- let animationFrameId: number;
- async function captureAndSendCanvasData() {
- const drawingCanvas = canvasReference.current;
-
- if (drawingCanvas) {
- const data = drawingCanvas.toDataURL();
- window.ipc.send("live-painting:input", data);
- }
-
- animationFrameId = requestAnimationFrame(captureAndSendCanvasData);
- }
-
- if (canvasReference.current) {
- const dpr = window.devicePixelRatio || 1;
- const canvas = canvasReference.current;
-
- canvas.width = width * dpr;
- canvas.height = height * dpr;
-
- const context = canvas.getContext("2d");
- if (context) {
- context.scale(dpr, dpr);
- }
-
- animationFrameId = requestAnimationFrame(captureAndSendCanvasData);
- }
-
- return () => {
- cancelAnimationFrame(animationFrameId);
- };
- }, [height, width]);
-
- // Only handle adjustments here
- // Never paint in this side effect
- useEffect(() => {
- if (brushReference.current) {
- brushReference.current.style.width = `${brushSize}px`;
- brushReference.current.style.height = `${brushSize}px`;
- }
-
- if (canvasReference.current) {
- const canvas = canvasReference.current;
-
- const context = canvas.getContext("2d");
- if (context) {
- context.fillStyle = backgroundColor;
- context.lineCap = "round";
- context.lineJoin = "round"; // This makes the corner round
- context.strokeStyle = strokeColor;
- context.lineWidth = brushSize;
- }
- }
- }, [backgroundColor, brushSize, strokeColor]);
-
- return (
-
-
- {
- setStrokeColor(event.target.value);
- }}
- />
-
- }
- slotProps={{ input: { min: 0 } }}
- onChange={event => {
- setBrushSize(Number.parseInt(event.target.value, 10));
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/client/organisms/live-painting/output-canvas.tsx b/src/client/organisms/live-painting/output-canvas.tsx
deleted file mode 100644
index f307e2e27..000000000
--- a/src/client/organisms/live-painting/output-canvas.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { useRef, useEffect } from "react";
-
-interface OutputCanvasProperties {
- width?: number;
- height?: number;
-}
-
-export function OutputCanvas({ width = 512, height = 512 }: OutputCanvasProperties) {
- const canvasReference = useRef(null);
-
- useEffect(() => {
- if (canvasReference.current) {
- const dpr = window.devicePixelRatio || 1;
- const canvas = canvasReference.current;
-
- canvas.width = width * dpr;
- canvas.height = height * dpr;
- canvas.style.width = `${width}px`;
- canvas.style.height = `${height}px`;
- }
- }, [height, width]);
-
- useEffect(() => {
- const image = new Image();
-
- if (!canvasReference.current) {
- return;
- }
-
- const canvas = canvasReference.current;
- const context = canvas.getContext("2d");
-
- function handleLoad() {
- context!.drawImage(image, 0, 0, canvas.width, canvas.height);
- }
-
- image.addEventListener("load", handleLoad);
-
- function handleImageGenerated(base64Image: string) {
- if (!base64Image.trim() || image.src === base64Image) {
- return;
- }
-
- console.log("tick");
- image.src = `${base64Image}`;
- }
-
- const unsubscribe = window.ipc.on("image-generated", handleImageGenerated);
-
- return () => {
- unsubscribe();
- image.removeEventListener("load", handleLoad);
- };
- }, []);
-
- return ;
-}
diff --git a/src/client/organisms/live-painting/update-properties.tsx b/src/client/organisms/live-painting/update-properties.tsx
deleted file mode 100644
index 37a46d267..000000000
--- a/src/client/organisms/live-painting/update-properties.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import Recycling from "@mui/icons-material/Recycling";
-import FormControl from "@mui/joy/FormControl";
-import FormLabel from "@mui/joy/FormLabel";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import Stack from "@mui/joy/Stack";
-import type { ChangeEvent } from "react";
-import { useEffect, useState } from "react";
-
-export function UpdateProperties() {
- const [prompt, setPrompt] = useState("");
- const [seed, setSeed] = useState("0");
- const [size, setSize] = useState({ width: 512, height: 512 });
- const [strength, setStrength] = useState("1");
- const [guidance, setGuidance] = useState("0");
-
- // Handle input changes
- function handlePromptChange(event: ChangeEvent) {
- setPrompt(event.target.value);
- }
-
- function handleSeedChange(event: ChangeEvent) {
- setSeed(event.target.value);
- }
-
- function handleSizeChange(dimension: "height" | "width", event: ChangeEvent) {
- setSize(previousSize => ({
- ...previousSize,
- [dimension]: Number.parseInt(event.target.value, 10),
- }));
- }
-
- function handleStrengthChange(event: ChangeEvent) {
- setStrength(event.target.value);
- }
-
- function handleGuidanceChange(event: ChangeEvent) {
- setGuidance(event.target.value);
- }
-
- function randomSeed() {
- setSeed(Math.floor(Math.random() * 100_000_000 + 1).toString());
- }
-
- // Use effect to send updates
- useEffect(() => {
- const properties = {
- prompt,
- seed: seed === "" ? 0 : Number.parseInt(seed, 10),
- size,
- strength: Number.parseFloat(strength),
- guidance_scale: Number.parseFloat(guidance),
- };
- window.ipc.send("live-painting:update-properties", properties);
- }, [prompt, seed, size, strength, guidance]);
-
- return (
-
-
- Prompt
-
-
-
-
- Strength
- {
- handleStrengthChange(event);
- }}
- />
-
-
- {/*
- Guidance
- {
- handleGuidanceChange(event);
- }}
- />
- */}
-
-
- Seed
-
-
-
- }
- onChange={handleSeedChange}
- />
-
-
- Width
- {
- handleSizeChange("width", event);
- }}
- />
-
-
- Height
- {
- handleSizeChange("height", event);
- }}
- />
-
-
- );
-}
diff --git a/src/client/organisms/modals/add-dataset.tsx b/src/client/organisms/modals/add-dataset.tsx
deleted file mode 100644
index c072d8940..000000000
--- a/src/client/organisms/modals/add-dataset.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
-import FolderIcon from "@mui/icons-material/Folder";
-import RefreshIcon from "@mui/icons-material/Refresh";
-import Button from "@mui/joy/Button";
-import CircularProgress from "@mui/joy/CircularProgress";
-import FormControl from "@mui/joy/FormControl";
-import FormHelperText from "@mui/joy/FormHelperText";
-import FormLabel from "@mui/joy/FormLabel";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import Modal from "@mui/joy/Modal";
-import type { ModalProps } from "@mui/joy/Modal";
-import ModalClose from "@mui/joy/ModalClose";
-import ModalDialog from "@mui/joy/ModalDialog";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
-import { useAtom } from "jotai";
-import { useTranslation } from "next-i18next";
-import { useEffect, useState } from "react";
-import { useForm } from "react-hook-form";
-import type { Except } from "type-fest";
-
-import { directoryAtom, datasetsAtom } from "@/ions/atoms";
-import { adjectives, colors, generateRandomName, nouns } from "@/ions/utils/get-random-name";
-
-function AddDatasetForm({ onClose }: { onClose: ModalProps["onClose"] }) {
- const [, setDatasets] = useAtom(datasetsAtom);
- const [directory, setDirectory] = useAtom(directoryAtom);
-
- const [loading, setLoading] = useState(false);
- const [, setError] = useState(null);
-
- const { t } = useTranslation(["common"]);
-
- const { register, formState, handleSubmit, setValue, clearErrors } = useForm({
- defaultValues: {
- name: generateRandomName([adjectives, colors, nouns], {
- humanize: true,
- separator: " ",
- }),
- directory,
- },
- });
-
- useEffect(
- () => () => {
- setDirectory("");
- },
- [setDirectory]
- );
-
- useEffect(() => {
- clearErrors("directory");
- }, [clearErrors]);
-
- return (
-
- );
-}
-
-export function AddDatasetModal({ onClose, ...properties }: Except) {
- const { t } = useTranslation(["common"]);
-
- return (
-
-
-
-
-
-
- );
-}
diff --git a/src/client/organisms/modals/caption/batch-edit.tsx b/src/client/organisms/modals/caption/batch-edit.tsx
deleted file mode 100644
index 0974aaa3f..000000000
--- a/src/client/organisms/modals/caption/batch-edit.tsx
+++ /dev/null
@@ -1,177 +0,0 @@
-import FindReplaceIcon from "@mui/icons-material/FindReplace";
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import DialogActions from "@mui/joy/DialogActions";
-import FormControl from "@mui/joy/FormControl";
-import FormLabel from "@mui/joy/FormLabel";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import Modal from "@mui/joy/Modal";
-import ModalClose from "@mui/joy/ModalClose";
-import ModalDialog from "@mui/joy/ModalDialog";
-import Stack from "@mui/joy/Stack";
-import SvgIcon from "@mui/joy/SvgIcon";
-import Tooltip from "@mui/joy/Tooltip";
-import Typography from "@mui/joy/Typography";
-import { useAtom } from "jotai/index";
-import { useTranslation } from "next-i18next";
-import React, { useId, useState } from "react";
-import { useForm } from "react-hook-form";
-
-import { editCaptionScopeAtom, imagesAtom } from "@/ions/atoms";
-import { EditCaptionScope } from "@/organisms/modals/caption/index";
-
-export function RegexIcon() {
- return (
-
-
-
- );
-}
-
-export function TextSearchIcon() {
- return (
-
-
-
- );
-}
-
-export function SuffixIcon() {
- return (
-
-
-
- );
-}
-
-export function PrefixIcon() {
- return (
-
-
-
- );
-}
-
-export function BatchEditModal({
- open,
- onClose,
-}: {
- onClose(): void | Promise;
- open: boolean;
-}) {
- const FindReplaceId = useId();
- const { t } = useTranslation(["common"]);
- const [regex, setRegex] = useState(false);
- const [images, setImages] = useAtom(imagesAtom);
- const [editCaptionScope] = useAtom(editCaptionScopeAtom);
- const { register, reset, handleSubmit } = useForm({
- defaultValues: {
- find: "",
- replace: "",
- prefix: "",
- suffix: "",
- },
- });
-
- return (
-
-
-
-
- {t("common:batchEdit")}:
- {
- const newData = images.map(image => {
- if (editCaptionScope === "empty" && image.caption) {
- return image;
- }
-
- if (editCaptionScope === "selected" && !image.selected) {
- return image;
- }
-
- if (data.find) {
- const pattern = regex ? new RegExp(data.find, "ig") : data.find;
- image.caption = image.caption.replace(pattern, data.replace);
- }
-
- image.caption = data.prefix + image.caption + data.suffix;
- return image;
- });
- setImages(newData);
- window.ipc.batchEditCaption(newData);
- reset();
- onClose();
- })}
- >
-
-
-
- {t("common:searchAndReplace")}
-
-
- }
- endDecorator={
-
- {
- setRegex(previousState => !previousState);
- }}
- >
-
-
-
- }
- />
- }
- />
-
-
-
- {t("common:prefix")}
- } />
-
-
- {t("common:suffix")}
- } />
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/client/organisms/modals/caption/index.tsx b/src/client/organisms/modals/caption/index.tsx
deleted file mode 100644
index 517536e73..000000000
--- a/src/client/organisms/modals/caption/index.tsx
+++ /dev/null
@@ -1,568 +0,0 @@
-import CloudDownloadIcon from "@mui/icons-material/CloudDownload";
-import CheckAllIcon from "@mui/icons-material/DoneAll";
-import ErrorIcon from "@mui/icons-material/Error";
-import RuleIcon from "@mui/icons-material/Rule";
-import StyleIcon from "@mui/icons-material/Style";
-import VisibilityIcon from "@mui/icons-material/Visibility";
-import WarningIcon from "@mui/icons-material/Warning";
-import Alert from "@mui/joy/Alert";
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import FormControl from "@mui/joy/FormControl";
-import FormLabel from "@mui/joy/FormLabel";
-import Link from "@mui/joy/Link";
-import Modal from "@mui/joy/Modal";
-import ModalClose from "@mui/joy/ModalClose";
-import ModalDialog from "@mui/joy/ModalDialog";
-import Option from "@mui/joy/Option";
-import Select from "@mui/joy/Select";
-import Slider from "@mui/joy/Slider";
-import Stack from "@mui/joy/Stack";
-import { styled } from "@mui/joy/styles";
-import SvgIcon from "@mui/joy/SvgIcon";
-import Tab from "@mui/joy/Tab";
-import TabList from "@mui/joy/TabList";
-import TabPanel from "@mui/joy/TabPanel";
-import Tabs from "@mui/joy/Tabs";
-import Textarea from "@mui/joy/Textarea";
-import ToggleButtonGroup from "@mui/joy/ToggleButtonGroup";
-import Typography from "@mui/joy/Typography";
-import { useAtom } from "jotai/index";
-import dynamic from "next/dynamic";
-import { Trans, useTranslation } from "next-i18next";
-import React, { useEffect, useMemo, useState } from "react";
-import useSWR from "swr";
-
-import {
- CAPTIONS,
- DOWNLOADS,
- GPT_VISION_OPTIONS,
- OPENAI_API_KEY,
-} from "../../../../main/helpers/constants";
-
-import { captioningErrorAtom, imagesAtom, editCaptionScopeAtom } from "@/ions/atoms";
-import { PasswordField } from "@/organisms/password-field";
-
-export const CodeMirror = dynamic(
- () => import("react-codemirror2").then(module_ => module_.Controlled),
- { ssr: false }
-);
-export const StyledEditor = styled(CodeMirror)({
- height: "100%",
- ">.CodeMirror": {
- height: "100%",
- },
-});
-
-export const defaultGptOptions = {
- batchSize: 4,
- parallel: true,
- guidelines: `Please caption these images, separate groups by comma, ensure logical groups: "black torn wide pants, red stained sweater" instead of "black, torn, wide pants and red, stained sweater"`,
- exampleResponse: `[
- "a photo of a young man, red hair, blue torn overalls with brass buttons, orange t-shirt with holes, white background",
- "a watercolor painting of an elderly woman, grey hair, yellow and blue floral print sundress with puffy sleeves, pink high heels, looking at a castle in the distance"
-]`,
-};
-
-export function EmptyCaptionIcon() {
- return (
-
-
-
- );
-}
-
-export function useFilteredImages() {
- const [filterScope] = useAtom(editCaptionScopeAtom);
- const [images] = useAtom(imagesAtom);
- return useMemo(() => {
- switch (filterScope) {
- case "empty": {
- return images.filter(image => !image.caption);
- }
-
- case "selected": {
- return images.filter(image => image.selected);
- }
-
- case "all": {
- return images;
- }
-
- default: {
- return [];
- }
- }
- }, [filterScope, images]);
-}
-
-export function EditCaptionScope() {
- const { t } = useTranslation(["common"]);
- const [value, setValue] = useAtom(editCaptionScopeAtom);
-
- return (
-
- {t("common:scopeForEditing")}
- {
- if (newValue) {
- setValue(newValue);
- }
- }}
- >
- }>
- {t("common:all")}
-
- }>
- {t("common:empty")}
-
- }>
- {t("common:selected")}
-
-
-
- );
-}
-
-export function GPTVCaptionModal({
- onClose,
- onStart,
-}: {
- onClose(): void | Promise;
- onStart(): void | Promise;
- onDone(): void | Promise;
-}) {
- const [openAiApiKey, setOpenAiApiKey] = useState("");
- const [gptVisionOptions, setGptVisionOptions] = useState(defaultGptOptions);
- const [confirmGpt, setConfirmGpt] = useState(false);
- const { t } = useTranslation(["common"]);
- const [, setCaptioningError] = useAtom(captioningErrorAtom);
- const { data: openApiKeyData } = useSWR(OPENAI_API_KEY);
- const { data: gptVisionData } = useSWR(GPT_VISION_OPTIONS);
- const filteredImages = useFilteredImages();
-
- useEffect(() => {
- if (openApiKeyData) {
- setOpenAiApiKey(openApiKeyData);
- }
- }, [openApiKeyData]);
-
- useEffect(() => {
- if (gptVisionData) {
- setGptVisionOptions(
- gptVisionData as {
- batchSize: number;
- guidelines: string;
- exampleResponse: string;
- parallel: boolean;
- }
- );
- }
- }, [gptVisionData]);
-
- return (
-
- }
- sx={{ flex: 1 }}
- onClick={async () => {
- setConfirmGpt(!confirmGpt);
- }}
- >
- {t("common:pages.dataset.customCaptionsWithGPTVision")}
-
-
- {confirmGpt && (
-
- }>
-
-
- ),
- }}
- />
-
-
- {!openAiApiKey && (
- }>
-
- {t("common:pages.dataset.enterKeyToUseGPTVision")}{" "}
-
- {t("common:getApiKey")}
-
-
-
- )}
- }
- sx={{ flex: 1 }}
- onClick={async () => {
- onStart();
- onClose();
- try {
- await window.ipc.handleRunGPTV(
- filteredImages.map(image => image.image),
- {
- ...gptVisionOptions,
- exampleResponse: JSON.parse(
- gptVisionOptions.exampleResponse
- ),
- }
- );
- } catch (error) {
- console.log(".... error");
- console.log(error);
- setCaptioningError((error as Error).message);
- }
- }}
- >
- {t("common:pages.dataset.proceedWithGPTVision")}
-
-
- )}
-
- {
- setOpenAiApiKey(event.target.value);
- }}
- onBlur={event => {
- window.ipc.fetch(OPENAI_API_KEY, {
- method: "POST",
- data: event.target.value,
- });
- }}
- />
-
-
-
-
-
- {t("common:batch")}
- {
- setGptVisionOptions(previousState => ({
- ...previousState,
- batchSize: value as number,
- }));
- }}
- onChangeCommitted={(_event, value) => {
- window.ipc.fetch(GPT_VISION_OPTIONS, {
- method: "POST",
- data: {
- ...gptVisionOptions,
- batchSize: value,
- },
- });
- }}
- />
-
- {t("common:guideline")}
-
- {
- setGptVisionOptions({
- ...gptVisionOptions,
- guidelines: value,
- });
- window.ipc.fetch(GPT_VISION_OPTIONS, {
- method: "POST",
- data: {
- ...gptVisionOptions,
- guidelines: value,
- },
- });
- }}
- />
-
- {t("common:exampleResponse")}
-
- {
- setGptVisionOptions({
- ...gptVisionOptions,
- exampleResponse: value,
- });
- window.ipc.fetch(GPT_VISION_OPTIONS, {
- method: "POST",
- data: {
- ...gptVisionOptions,
- exampleResponse: value,
- },
- });
- }}
- />
-
-
-
- );
-}
-
-export function WD14CaptionModal({
- onClose,
- onStart,
-}: {
- onClose(): void | Promise;
- onStart(): void | Promise;
- onDone(): void | Promise;
-}) {
- const [options, setOptions] = useState({ batchSize: 10, model: "", exclude: "" });
- const { t } = useTranslation(["common"]);
- const [, setCaptioningError] = useAtom(captioningErrorAtom);
- const { data: loadingModel } = useSWR("SmilingWolf/wd-v1-4-convnextv2-tagger-v2/model");
- const { data: loadingCSV } = useSWR("SmilingWolf/wd-v1-4-convnextv2-tagger-v2/selected_tags");
- const { data: checkpointsData } = useSWR(CAPTIONS, () => window.ipc.getModels("captions"));
- const isInstalled = Boolean(checkpointsData?.length);
- const model =
- "https://huggingface.co/SmilingWolf/wd-v1-4-convnextv2-tagger-v2/resolve/main/model.onnx";
- const csv =
- "https://huggingface.co/SmilingWolf/wd-v1-4-convnextv2-tagger-v2/resolve/main/selected_tags.csv";
-
- useEffect(() => {
- if (checkpointsData) {
- setOptions(previousState => ({ ...previousState, model: checkpointsData[0] }));
- }
- }, [checkpointsData]);
-
- const filteredImages = useFilteredImages();
-
- return (
-
- {!isInstalled && (
- }
- endDecorator={
- }
- onClick={async () => {
- const storeKey = `${DOWNLOADS}.SmilingWolf/wd-v1-4-convnextv2-tagger-v2.model.onnx`;
-
- await window.ipc.downloadModel("wd14", model, {
- id: "SmilingWolf/wd-v1-4-convnextv2-tagger-v2",
- storeKey,
- });
- await window.ipc.downloadModel("wd14", csv, {
- id: "SmilingWolf/wd-v1-4-convnextv2-tagger-v2",
- storeKey,
- });
- }}
- >
- {t("common:download")}
-
- }
- sx={{
- ".MuiAlert-endDecorator": {
- alignSelf: "flex-start",
- mt: -0.5,
- mr: -0.5,
- },
- }}
- >
- {t("common:pages.dataset.oneTimeDownloadNote")}
-
- )}
- }
- sx={{ flex: 1 }}
- onClick={async () => {
- onStart();
- onClose();
- try {
- await window.ipc.handleRunWd14(
- filteredImages.map(image => image.image),
- {
- ...options,
- exclude: options.exclude.split(",").map(entry => entry.trim()),
- }
- );
- } catch (error) {
- setCaptioningError((error as Error).message);
- }
- }}
- >
- {t("common:pages.dataset.generateTagsWithWD14")}
-
-
-
-
- {t("common:model")}
-
-
-
- {t("common:excludedTags")}
-
-
- {t("common:batch")}
- {
- setOptions(previousState => ({
- ...previousState,
- batchSize: value as number,
- }));
- }}
- />
-
-
-
- );
-}
-
-export function CaptionModal({
- open,
- onClose,
- onStart,
- onDone,
-}: {
- onClose(): void | Promise;
- onStart(): void | Promise;
- onDone(): void | Promise;
- open: boolean;
-}) {
- const { t } = useTranslation(["common"]);
-
- return (
-
-
-
-
- {t("common:pages.dataset.chooseCaptioningMethod")}:
-
-
- WD14
- GPT Vision
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/client/organisms/model-card/index.tsx b/src/client/organisms/model-card/index.tsx
deleted file mode 100644
index d5729e481..000000000
--- a/src/client/organisms/model-card/index.tsx
+++ /dev/null
@@ -1,309 +0,0 @@
-import { ClickAwayListener } from "@mui/base";
-import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
-import CheckIcon from "@mui/icons-material/Check";
-import DownloadIcon from "@mui/icons-material/Download";
-import LinkIcon from "@mui/icons-material/Link";
-import WorkspacePremiumIcon from "@mui/icons-material/WorkspacePremium";
-import Badge from "@mui/joy/Badge";
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import ButtonGroup from "@mui/joy/ButtonGroup";
-import Card from "@mui/joy/Card";
-import CardContent from "@mui/joy/CardContent";
-import CircularProgress from "@mui/joy/CircularProgress";
-import IconButton from "@mui/joy/IconButton";
-import Menu from "@mui/joy/Menu";
-import MenuItem from "@mui/joy/MenuItem";
-import Tooltip from "@mui/joy/Tooltip";
-import Typography from "@mui/joy/Typography";
-import { useAtom } from "jotai";
-import { useTranslation } from "next-i18next";
-import type { MouseEvent as ReactMouseEvent } from "react";
-import { useEffect, useRef, useState } from "react";
-import useSWR from "swr";
-
-import { DOWNLOADS } from "../../../main/helpers/constants";
-
-import { captionsAtom, checkpointsAtom, lorasAtom } from "@/ions/atoms";
-import { fetcher } from "@/ions/swr/fetcher";
-
-export const architectureMap = {
- "sd-2-1": "SD 2.1",
- "sd-xl-turbo": "SDXL Turbo",
- "sd-2-1-turbo": "SD Turbo",
- "sd-1-5": "SD 1.5",
- "sd-xl-1-0": "SDXL",
-};
-
-const modelAtoms = {
- loras: lorasAtom,
- checkpoints: checkpointsAtom,
- wd14: captionsAtom,
-};
-
-// TODO this component has too much business logic.
-export function ModelCard({
- id,
- title,
- author,
- caption,
- files,
- type,
- license,
- architecture,
- link,
- image,
-}: {
- id: string;
- author: string;
- license: string;
- link: string;
- type: "loras" | "checkpoints" | "wd14";
- caption?: string;
- architecture: string;
- files: Array<{ filename: string; variant?: string; required?: boolean }>;
- title: string;
- image?: string;
-}) {
- const [isDownloading, setIsDownloading] = useState(false);
- const { t } = useTranslation(["common"]);
- const [isDownloadOptionsOpen, setIsDownloadOptionsOpen] = useState(false);
-
- const anchorReference = useRef(null);
- const [selectedIndex, setSelectedIndex] = useState(0);
- const [checkpoints] = useAtom(modelAtoms[type]);
-
- const selectedFile = files[selectedIndex];
- const installed =
- type === "wd14"
- ? Boolean(selectedFile) && checkpoints.includes([id, selectedFile.filename].join("/"))
- : Boolean(selectedFile) && checkpoints.includes(selectedFile.filename);
-
- const hasVersion =
- type === "wd14"
- ? files.some(({ filename }) => checkpoints.includes([id, filename].join("/")))
- : files.some(({ filename }) => checkpoints.includes(filename));
-
- const hasMultipleVersions = files.filter(item => !item.required).length > 1;
-
- function handleMenuItemClick(event: ReactMouseEvent, index: number) {
- setSelectedIndex(index);
- setIsDownloadOptionsOpen(false);
- }
-
- const storeKey = `${DOWNLOADS}.${id}.${selectedFile.filename}`;
- const { data } = useSWR(storeKey, fetcher, {
- refreshInterval: 1000,
- });
-
- useEffect(() => {
- setIsDownloading(Boolean(data));
- }, [data]);
-
- let buttonText = t("common:download");
-
- if (isDownloading) {
- buttonText = t("common:downloading");
- } else if (installed) {
- buttonText = t("common:installed");
- }
-
- return (
-
- <>
-
- }
- sx={{ display: "block" }}
- anchorOrigin={{
- vertical: "top",
- horizontal: "left",
- }}
- slotProps={{
- badge: {
- sx: {
- mt: -1,
- borderRadius: 0,
- boxShadow: "none",
- },
- },
- }}
- >
-
- {title}
-
-
- {author}
-
-
-
-
-
-
-
- {image && (
-
- )}
- {caption && (
-
- {caption}
-
- )}
-
-
-
- }>
- {license}
-
-
- {
- setIsDownloadOptionsOpen(false);
- }}
- >
-
-
-
- :
- }
- onClick={async () => {
- setIsDownloading(true);
- try {
- await window.ipc.fetch(storeKey, {
- method: "POST",
- data: true,
- });
-
- if (files.every(file => file.required)) {
- console.log("all required");
- for (const file of files) {
- await window.ipc.downloadModel(
- type,
- `${link}/resolve/main/${file.filename}?download=true`,
- { id, storeKey }
- );
- }
- } else {
- await window.ipc.downloadModel(
- type,
- `${link}/resolve/main/${selectedFile.filename}?download=true`,
- { id, storeKey }
- );
- }
- } catch (error) {
- console.log(error);
- } finally {
- setIsDownloading(false);
- }
- }}
- >
- {buttonText}
-
-
- {hasMultipleVersions && (
- {
- setIsDownloadOptionsOpen(!isDownloadOptionsOpen);
- }}
- >
-
-
- )}
-
-
-
-
-
- >
-
- );
-}
diff --git a/src/client/organisms/password-field/index.tsx b/src/client/organisms/password-field/index.tsx
deleted file mode 100644
index 0225d1763..000000000
--- a/src/client/organisms/password-field/index.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import VisibilityIcon from "@mui/icons-material/Visibility";
-import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
-import FormControl from "@mui/joy/FormControl";
-import FormHelperText from "@mui/joy/FormHelperText";
-import FormLabel from "@mui/joy/FormLabel";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import type { InputProps } from "@mui/joy/Input";
-import { useTranslation } from "next-i18next";
-import type { ReactNode } from "react";
-import { useState } from "react";
-import type { Except } from "type-fest";
-
-export function PasswordField({
- helpText,
- error,
- label,
- ...properties
-}: Except & {
- helpText?: ReactNode;
- label?: ReactNode;
- error?: string;
-}) {
- const [showKey, setShowKey] = useState(false);
- const { t } = useTranslation(["common"]);
- return (
-
- {label && {label}}
- {
- setShowKey(!showKey);
- }}
- >
- {showKey ? : }
-
- }
- />
- {(error || helpText) && {error || helpText}}
-
- );
-}
diff --git a/src/client/organisms/text-field/index.tsx b/src/client/organisms/text-field/index.tsx
deleted file mode 100644
index d44847260..000000000
--- a/src/client/organisms/text-field/index.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import FormControl from "@mui/joy/FormControl";
-import FormHelperText from "@mui/joy/FormHelperText";
-import FormLabel from "@mui/joy/FormLabel";
-import Input from "@mui/joy/Input";
-import type { InputProps } from "@mui/joy/Input";
-import type { ReactNode } from "react";
-
-export function TextField({
- helpText,
- error,
- label,
- ...properties
-}: InputProps & {
- helpText?: ReactNode;
- label?: ReactNode;
- error?: string;
-}) {
- return (
-
- {label && {label}}
-
- {(error || helpText) && {error || helpText}}
-
- );
-}
diff --git a/src/client/organisms/zoomable-image-stage/index.tsx b/src/client/organisms/zoomable-image-stage/index.tsx
deleted file mode 100644
index 8f1954a5a..000000000
--- a/src/client/organisms/zoomable-image-stage/index.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
-import ChevronRightIcon from "@mui/icons-material/ChevronRight";
-import SearchOffIcon from "@mui/icons-material/SearchOff";
-import ZoomInIcon from "@mui/icons-material/ZoomIn";
-import ZoomOutIcon from "@mui/icons-material/ZoomOut";
-import Box from "@mui/joy/Box";
-import ButtonGroup from "@mui/joy/ButtonGroup";
-import IconButton from "@mui/joy/IconButton";
-import Sheet from "@mui/joy/Sheet";
-import { useAtom } from "jotai/index";
-import { useTranslation } from "next-i18next";
-import React from "react";
-import {
- type ReactZoomPanPinchContentRef,
- TransformComponent,
- TransformWrapper,
-} from "react-zoom-pan-pinch";
-
-import { imagesAtom, selectedImageAtom } from "@/ions/atoms";
-
-export function ZoomControls({ zoomIn, zoomOut, resetTransform }: ReactZoomPanPinchContentRef) {
- const { t } = useTranslation(["common"]);
- return (
-
-
- {
- zoomIn();
- }}
- >
-
-
- {
- zoomOut();
- }}
- >
-
-
- {
- resetTransform();
- }}
- >
-
-
-
-
- );
-}
-
-export function ZoomImageStage() {
- const [images] = useAtom(imagesAtom);
- const [selectedImage, setSelectedImage] = useAtom(selectedImageAtom);
- const { t } = useTranslation(["common"]);
- return (
-
- {images[selectedImage] && (
-
-
- {utils => (
-
-
-
- {
- setSelectedImage(
- (images.length + selectedImage - 1) % images.length
- );
- utils.resetTransform();
- }}
- >
-
-
- {
- setSelectedImage((selectedImage + 1) % images.length);
- utils.resetTransform();
- }}
- >
-
-
-
-
-
-
-
- )}
-
-
- )}
-
- );
-}
diff --git a/src/client/pages/[locale]/dataset.tsx b/src/client/pages/[locale]/dataset.tsx
deleted file mode 100644
index c0d92cc07..000000000
--- a/src/client/pages/[locale]/dataset.tsx
+++ /dev/null
@@ -1,548 +0,0 @@
-import ChecklistIcon from "@mui/icons-material/Checklist";
-import DeselectIcon from "@mui/icons-material/Deselect";
-import EditIcon from "@mui/icons-material/Edit";
-import EditNoteIcon from "@mui/icons-material/EditNote";
-import ErrorIcon from "@mui/icons-material/Error";
-import FolderOpenIcon from "@mui/icons-material/FolderOpen";
-import PhotoFilterIcon from "@mui/icons-material/PhotoFilter";
-import SelectAllIcon from "@mui/icons-material/SelectAll";
-import Badge from "@mui/joy/Badge";
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import Checkbox from "@mui/joy/Checkbox";
-import CircularProgress from "@mui/joy/CircularProgress";
-import FormControl from "@mui/joy/FormControl";
-import FormLabel from "@mui/joy/FormLabel";
-import Grid from "@mui/joy/Grid";
-import IconButton from "@mui/joy/IconButton";
-import Input from "@mui/joy/Input";
-import Sheet from "@mui/joy/Sheet";
-import Snackbar from "@mui/joy/Snackbar";
-import Stack from "@mui/joy/Stack";
-import Textarea from "@mui/joy/Textarea";
-import Tooltip from "@mui/joy/Tooltip";
-import { useAtom } from "jotai";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import { useRouter } from "next/router";
-import { useTranslation } from "next-i18next";
-import type { CSSProperties } from "react";
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import AutoSizer from "react-virtualized-auto-sizer";
-import { FixedSizeGrid } from "react-window";
-import useSWR from "swr";
-
-import { CAPTION, CAPTION_RUNNING, DATASET, FOLDER } from "../../../main/helpers/constants";
-
-import { ScreenReaderOnly } from "@/atoms/screen-reader-only";
-import {
- canSelectImagesAtom,
- captioningErrorAtom,
- captionRunningAtom,
- directoryAtom,
- imagesAtom,
- projectAtom,
- selectedImageAtom,
-} from "@/ions/atoms";
-import { useColumns } from "@/ions/hooks/columns";
-import { useKeyboardControlledImagesNavigation } from "@/ions/hooks/keyboard-controlled-images-navigation";
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { CustomScrollbarsVirtualList } from "@/organisms/custom-scrollbars";
-import { CaptionModal, useFilteredImages } from "@/organisms/modals/caption";
-import { BatchEditModal } from "@/organisms/modals/caption/batch-edit";
-import { ZoomImageStage } from "@/organisms/zoomable-image-stage";
-
-export function ImageGridCell({
- columnIndex,
- rowIndex,
- style,
-}: {
- columnIndex: number;
- rowIndex: number;
- style: CSSProperties;
-}) {
- const reference = useRef(null);
- const [images, setImages] = useAtom(imagesAtom);
- const [selectedImage, setSelectedImage] = useAtom(selectedImageAtom);
- const columnCount = useColumns({ xs: 2, sm: 3, md: 4, lg: 6 });
- const { t } = useTranslation(["common"]);
- const [canSelectImages] = useAtom(canSelectImagesAtom);
-
- const index = rowIndex * columnCount + columnIndex;
- const image = images[index];
-
- return (
- image && (
-
- {canSelectImages && image && (
- ({
- position: "absolute",
- top: 0,
- left: 0,
- zIndex: theme.zIndex.badge + 1,
- })}
- >
- {
- setImages(previousState =>
- previousState.map(image_ =>
- image_ === image
- ? {
- ...image_,
- selected: event.target.checked,
- }
- : image_
- )
- );
- }}
- />
-
- )}
-
-
-
-
- )
- );
-}
-
-export function CaptioningError() {
- const [captioningError, setCaptioningError] = useAtom(captioningErrorAtom);
-
- if (!captioningError) {
- return null;
- }
-
- return (
- }
- sx={{
- maxWidth: 600,
- ".MuiSnackbar-startDecorator": { alignSelf: "flex-start" },
- ".MuiSnackbar-endDecorator": { alignSelf: "flex-end" },
- }}
- endDecorator={
-
- }
- onClose={() => {
- setCaptioningError(false);
- }}
- >
- {captioningError}
-
- );
-}
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const { query } = useRouter();
- const id = query.id as string | undefined;
- const [images, setImages] = useAtom(imagesAtom);
- const [selectedImage, setSelectedImage] = useAtom(selectedImageAtom);
- const [dataset, setDataset] = useAtom(projectAtom);
- const columnCount = useColumns({ xs: 2, sm: 3, md: 4, lg: 6 });
- const [caption, setCaption] = useState("");
- const [name, setName] = useState("");
- const { t } = useTranslation(["common"]);
- const [batchModalOpen, setBatchModalOpen] = useState(false);
- const [captionModalOpen, setCaptionModalOpen] = useState(false);
- const [progress, setProgress] = useState(0);
- const [progressCount, setProgressCount] = useState("");
- const [, setDirectory] = useAtom(directoryAtom);
- const [captionRunning, setCaptionRunning] = useAtom(captionRunningAtom);
- const [canSelectImages, setCanSelectImages] = useAtom(canSelectImagesAtom);
-
- const { data: captionRunningData } = useSWR(CAPTION_RUNNING);
-
- const { data: datasetData } = useSWR(captionRunningData ? DATASET : undefined, () => {
- if (id) {
- return window.ipc.getDataset(id);
- }
- });
- const filteredImages = useFilteredImages();
- const saveCaptionToFile = useCallback(async () => {
- const image = images[selectedImage];
- if (image) {
- setImages(
- images.map(image_ =>
- image_.image === image.image ? { ...image_, caption } : image_
- )
- );
- await window.ipc.saveCaption({ ...image, caption });
- }
- }, [images, selectedImage, caption, setImages]);
-
- useKeyboardControlledImagesNavigation({ onBeforeChange: saveCaptionToFile });
-
- useEffect(() => {
- console.log({ captionRunningData });
- if (typeof captionRunningData === "boolean") {
- setCaptionRunning(captionRunningData);
- }
- }, [captionRunningData, setCaptionRunning]);
-
- useEffect(() => {
- if (datasetData) {
- setDataset(datasetData.dataset);
- setImages(datasetData.images);
- setName(datasetData.dataset.name);
- setDirectory(datasetData.dataset.files);
- }
- // When datasetData is undefined (initial call) we manually get it from IPC
- // Check if id has is available from query
- else if (id) {
- window.ipc.getDataset(id).then(datasetData_ => {
- setDataset(datasetData_.dataset);
- setImages(datasetData_.images);
- setName(datasetData_.dataset.name);
- setDirectory(datasetData_.dataset.files);
- });
- }
- }, [datasetData, setDataset, setImages, setDirectory, id]);
-
- // TODO: probably needs adjustment to new mechanism
- // Track progress of potential captioning progress
- useEffect(() => {
- const unsubscribe = window.ipc.on(
- `${CAPTION}:updated`,
- ({
- progress: progress_,
- counter,
- totalCount,
- done,
- }: {
- progress: number;
- counter: number;
- totalCount: number;
- done: boolean;
- }) => {
- setProgress(progress_);
- setProgressCount(`${counter}/${totalCount}`);
- if (done) {
- setCaptionRunning(false);
- }
- }
- );
- return () => {
- unsubscribe();
- };
- }, [setCaptionRunning]);
-
- // Set initially selected image to 0
- useEffect(() => {
- setSelectedImage(0);
- }, [setSelectedImage]);
-
- // Set caption when an image changes
- useEffect(() => {
- const image = images[selectedImage];
- if (image) {
- setCaption(image.caption ?? "");
- }
- }, [selectedImage, images]);
-
- return (
- <>
-
- {`Captain | ${t("common:dataset")}`}
-
-
- {
- setCaptionRunning(true);
- setProgress(0);
- setProgressCount(`0/${filteredImages.length}`);
- window.ipc.fetch(CAPTION_RUNNING, { method: "POST", data: true });
- }}
- onDone={async () => {
- console.log("done");
- }}
- onClose={() => {
- setCaptionModalOpen(false);
- }}
- />
- {
- setBatchModalOpen(false);
- }}
- />
-
-
-
- {dataset && (
- }
- onChange={event => {
- setName(event.target.value);
- }}
- onBlur={async event => {
- await window.ipc.updateDataset(dataset.id, {
- name: event.target.value,
- });
- }}
- />
- )}
-
-
- }
- sx={{
- width: { xs: 36, lg: "auto" },
- px: 1.5,
- whiteSpace: "nowrap",
- justifyContent: "flex-start",
- overflow: "hidden",
- }}
- onClick={() => {
- setCanSelectImages(previousState => !previousState);
- }}
- >
- {t("common:selectImages")}
-
-
-
- }
- sx={{
- width: { xs: 36, lg: "auto" },
- px: 1.5,
- whiteSpace: "nowrap",
- justifyContent: "flex-start",
- overflow: "hidden",
- }}
- onClick={() => {
- setBatchModalOpen(true);
- }}
- >
- {t("common:batchEdit")}
-
-
-
- }
- sx={{
- width: { xs: 36, lg: "auto" },
- px: 1.5,
- whiteSpace: "nowrap",
- justifyContent: "flex-start",
- overflow: "hidden",
- }}
- onClick={() => {
- if (dataset) {
- window.ipc.send(`${FOLDER}:open`, dataset.files);
- }
- }}
- >
- {t("common:openFolder")}
-
-
-
-
- ) : (
-
- )
- }
- sx={{
- position: "relative",
- width: { xs: 36, md: "auto" },
- px: 1.5,
- whiteSpace: "nowrap",
- justifyContent: "flex-start",
- overflow: "hidden",
- "&.Mui-disabled": {
- color: captionRunning ? "common.white" : undefined,
- },
- ".MuiButton-startDecorator": { position: "relative", zIndex: 1 },
- }}
- onClick={() => {
- if (!captionRunning) {
- setCaptionModalOpen(true);
- }
- }}
- >
- {captionRunning && (
-
- )}
-
- {captionRunning ? progressCount : t("common:autoCaption")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {t("common:caption")}
-
-
- {canSelectImages && (
-
-
- {
- setImages(previousState =>
- previousState.map(image_ => ({
- ...image_,
- selected: true,
- }))
- );
- }}
- >
-
-
-
-
- {
- setImages(previousState =>
- previousState.map(image_ => ({
- ...image_,
- selected: false,
- }))
- );
- }}
- >
-
-
-
-
- )}
-
-
- {({ height, width }) => {
- const columnWidth = width / columnCount;
-
- return (
-
- {ImageGridCell}
-
- );
- }}
-
-
-
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";
diff --git a/src/client/pages/[locale]/feedback.tsx b/src/client/pages/[locale]/feedback.tsx
deleted file mode 100644
index 91e89d2d0..000000000
--- a/src/client/pages/[locale]/feedback.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import Container from "@mui/joy/Container";
-import FormControl from "@mui/joy/FormControl";
-import FormLabel from "@mui/joy/FormLabel";
-import Sheet from "@mui/joy/Sheet";
-import Textarea from "@mui/joy/Textarea";
-import Typography from "@mui/joy/Typography";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import { useTranslation } from "next-i18next";
-import { useForm } from "react-hook-form";
-
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { CustomScrollbars } from "@/organisms/custom-scrollbars";
-import { Lottie } from "@/organisms/lottie";
-
-function FeedbackForm() {
- const {
- register,
- handleSubmit,
- formState: { errors },
- } = useForm({
- defaultValues: { body: "" },
- });
- const { t } = useTranslation(["common"]);
-
- return (
- {
- await window.ipc.sendFeedback(data);
- })}
- >
-
- {t("common:pages.feedback.giveFeedback")}
-
-
-
-
-
-
- );
-}
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const { t } = useTranslation(["common"]);
- return (
- <>
-
- {`Captain | ${t("common:feedback")}`}
-
-
-
-
-
- {t("common:feedback")}
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";
diff --git a/src/client/pages/[locale]/home.tsx b/src/client/pages/[locale]/home.tsx
deleted file mode 100644
index d1194e4e6..000000000
--- a/src/client/pages/[locale]/home.tsx
+++ /dev/null
@@ -1,247 +0,0 @@
-import AddToPhotosIcon from "@mui/icons-material/AddToPhotos";
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import Grid from "@mui/joy/Grid";
-import LinearProgress from "@mui/joy/LinearProgress";
-import Sheet from "@mui/joy/Sheet";
-import Tooltip from "@mui/joy/Tooltip";
-import Typography from "@mui/joy/Typography";
-import { useAtom } from "jotai";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import Link from "next/link";
-import { useTranslation } from "next-i18next";
-import { useEffect, useState } from "react";
-import useSWR from "swr";
-
-import { APP, INSTALLING_PYTHON } from "../../../main/helpers/constants";
-
-import { StyledImage } from "@/atoms/image/styled";
-import { directoryAtom, imagesAtom, datasetsAtom } from "@/ions/atoms";
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { CustomScrollbars } from "@/organisms/custom-scrollbars";
-import { DeleteConfirm } from "@/organisms/delete-confirm";
-import { FolderDrop } from "@/organisms/folder-drop";
-import { Lottie } from "@/organisms/lottie";
-import { AddDatasetModal } from "@/organisms/modals/add-dataset";
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const [datasets, setDatasets] = useAtom(datasetsAtom);
- const [, setImages] = useAtom(imagesAtom);
- const [, setDirectory] = useAtom(directoryAtom);
- const {
- t,
- i18n: { language: locale },
- } = useTranslation(["common"]);
- const [newDatasetOpen, setNewDatasetOpen] = useState(false);
- const [appReady, setAppReady] = useState(false);
-
- const { data: pythonInstallingData } = useSWR(INSTALLING_PYTHON);
-
- function handleCloseNewDataset() {
- setNewDatasetOpen(false);
- }
-
- function handleOpenNewDataset() {
- setNewDatasetOpen(true);
- }
-
- useEffect(() => {
- window.ipc.getDatasets().then(datasets_ => {
- setDatasets(datasets_);
- });
- }, [setDatasets]);
-
- useEffect(() => {
- setImages([]);
- }, [setImages]);
-
- useEffect(() => {
- if (typeof pythonInstallingData === "boolean") {
- setAppReady(!pythonInstallingData);
- }
- }, [pythonInstallingData]);
-
- useEffect(() => {
- const unsubscribe = window.ipc.on(`${APP}:ready`, () => {
- console.log("APP IS READY");
- setAppReady(true);
- });
- return () => {
- unsubscribe();
- };
- }, []);
-
- return (
- <>
-
- {`Captain | ${t("common:datasets")}`}
-
-
-
-
- {t("common:datasets")}
-
-
- }
- onClick={handleOpenNewDataset}
- >
- {t("common:new")}
-
-
-
-
-
- {
- if (appReady) {
- setDirectory(path);
- handleOpenNewDataset();
- }
- }}
- >
- {datasets.length > 0 && appReady ? (
-
-
- {datasets?.map(dataset => (
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
- ) : (
-
- {appReady ? (
-
-
-
- {t("common:noDatasets")}
-
-
- {t("common:pages.datasets.dropToAdd")}
-
-
- ) : (
-
-
-
-
- {t("common:initialSetup")}
-
-
- {t("common:settingUpApp")}
-
-
- )}
-
- )}
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";
diff --git a/src/client/pages/[locale]/inventory.tsx b/src/client/pages/[locale]/inventory.tsx
deleted file mode 100644
index 618e1f003..000000000
--- a/src/client/pages/[locale]/inventory.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import Box from "@mui/joy/Box";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import { useTranslation } from "next-i18next";
-
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { Lottie } from "@/organisms/lottie";
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const { t } = useTranslation(["common"]);
- return (
- <>
-
- {`Captain | ${t("common:inventory")}`}
-
-
-
-
-
- {t("common:inventory")}
-
-
-
-
-
-
- {t("common:comingSoon")}
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";
diff --git a/src/client/pages/[locale]/livepainting.tsx b/src/client/pages/[locale]/livepainting.tsx
deleted file mode 100644
index 4ba76778a..000000000
--- a/src/client/pages/[locale]/livepainting.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import Grid from "@mui/joy/Grid";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import { useTranslation } from "next-i18next";
-
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { DrawingCanvas } from "@/organisms/live-painting/drawing-canvas";
-import { OutputCanvas } from "@/organisms/live-painting/output-canvas";
-import { UpdateProperties } from "@/organisms/live-painting/update-properties";
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const { t } = useTranslation(["common"]);
- return (
- <>
-
- {`Captain | ${t("common:livepainting")}`}
-
-
-
-
-
- {t("common:livepainting")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";
diff --git a/src/client/pages/[locale]/marketplace.tsx b/src/client/pages/[locale]/marketplace.tsx
deleted file mode 100644
index d6945725c..000000000
--- a/src/client/pages/[locale]/marketplace.tsx
+++ /dev/null
@@ -1,595 +0,0 @@
-import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
-import ChevronRightIcon from "@mui/icons-material/ChevronRight";
-import CloudDownloadIcon from "@mui/icons-material/CloudDownload";
-import Box from "@mui/joy/Box";
-import Button from "@mui/joy/Button";
-import CircularProgress from "@mui/joy/CircularProgress";
-import Container from "@mui/joy/Container";
-import IconButton from "@mui/joy/IconButton";
-import Sheet from "@mui/joy/Sheet";
-import Typography from "@mui/joy/Typography";
-import { useAtom } from "jotai";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import { useTranslation } from "next-i18next";
-import { useEffect, useRef, useState } from "react";
-import useSWR from "swr";
-
-export const DEFAULT_MARKETPLACE_URL =
- "https://gist.githubusercontent.com/pixelass/004948226b2ce96b0c6be1ae459f3f42/raw/695bf222fee0c109135ea5b91902046b401175ba/captain-marketplace-test.json";
-
-import {
- CAPTIONS,
- CHECKPOINTS,
- LORAS,
- MARKETPLACE_INDEX,
- MARKETPLACE_INDEX_DATA,
-} from "../../../main/helpers/constants";
-
-import { captionsAtom, checkpointsAtom, lorasAtom } from "@/ions/atoms";
-import { useScrollPosition } from "@/ions/hooks/scroll-position";
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { CustomScrollbars } from "@/organisms/custom-scrollbars";
-import { Lottie } from "@/organisms/lottie";
-import { ModelCard } from "@/organisms/model-card";
-
-interface NestedObjectWithOptionalId {
- [key: string]: NestedObjectWithOptionalId | string | boolean | number | null | undefined;
- id?: string;
-}
-
-// Define a type for objects that definitely have an id property
-interface ObjectWithId {
- id: string;
- [key: string]: any;
-}
-
-interface ModelInformation {
- previews: Preview[];
- info: ModelInfo;
- id: string;
-}
-
-interface Preview {
- type: string;
- content: string;
-}
-
-interface ModelInfo {
- type: string;
- architecture: string;
- title: string;
- author: string;
- link: string;
- license: string;
- files: FileInfo[];
-}
-
-interface FileInfo {
- filename: string;
- variant?: string;
- required?: boolean;
-}
-
-function findAllParentObjectsWithId(object: NestedObjectWithOptionalId, parents: T[] = []): T[] {
- if (object && typeof object === "object" && "id" in object) {
- // Use a type guard to check if the object has a string id
- const objectWithId: ObjectWithId | undefined =
- typeof object.id === "string" ? { ...object, id: object.id } : undefined;
- if (objectWithId) {
- parents.push(objectWithId as T);
- }
- }
-
- if (object && typeof object === "object") {
- for (const key in object) {
- if (Object.hasOwn(object, key)) {
- const child = object[key];
- // Check if child is a non-null object but not an array (as arrays might not have id properties in the same sense)
- if (child && typeof child === "object" && !Array.isArray(child)) {
- findAllParentObjectsWithId(child, parents);
- }
- }
- }
- }
-
- return parents;
-}
-
-export function CheckpointsSection() {
- const { t } = useTranslation(["common"]);
- const [, setCheckpoints] = useAtom(checkpointsAtom);
- // TODO create a type for the models
- const [stableDiffusionModels, setStableDiffusionModels] = useState([]);
- const scrollReference = useRef(null);
- const scrollPosition = useScrollPosition(scrollReference);
-
- const { data: marketPlaceData } = useSWR(MARKETPLACE_INDEX_DATA);
- const { data: checkpointsData } = useSWR(CHECKPOINTS, () =>
- window.ipc.getModels("checkpoints")
- );
-
- useEffect(() => {
- if (marketPlaceData && marketPlaceData["stable-diffusion"]?.checkpoint) {
- setStableDiffusionModels(
- findAllParentObjectsWithId(
- marketPlaceData["stable-diffusion"]?.checkpoint
- )
- );
- }
- }, [marketPlaceData]);
-
- useEffect(() => {
- if (checkpointsData) {
- setCheckpoints(checkpointsData);
- }
- }, [checkpointsData, setCheckpoints]);
-
- return (
-
-
-
- {t("common:pages.settings.stableDiffusionCheckpoints")}
-
-
-
-
- {
- if (scrollReference.current) {
- scrollReference.current.scrollBy({
- left: -scrollReference.current.clientWidth,
- behavior: "smooth",
- });
- }
- }}
- >
-
-
- {
- if (scrollReference.current) {
- // Scroll one full width of the scrollRef Element to the right
- scrollReference.current.scrollBy({
- left: scrollReference.current.clientWidth,
- behavior: "smooth",
- });
- }
- }}
- >
-
-
-
-
-
- {stableDiffusionModels.map(stableDiffusionModel => (
-
- item.type === "image"
- )?.content
- }
- />
-
- ))}
-
-
-
- );
-}
-
-export function LorasSection() {
- const { t } = useTranslation(["common"]);
- const [, setLoras] = useAtom(lorasAtom);
- // TODO create a type for the models
- const [stableDiffusionModels, setStableDiffusionModels] = useState([]);
- const scrollReference = useRef(null);
- const scrollPosition = useScrollPosition(scrollReference);
-
- const { data: marketPlaceData } = useSWR(MARKETPLACE_INDEX_DATA);
- const { data: loasData } = useSWR(LORAS, () => window.ipc.getModels("loras"));
-
- useEffect(() => {
- if (marketPlaceData && marketPlaceData["stable-diffusion"]?.lora) {
- setStableDiffusionModels(
- findAllParentObjectsWithId(
- marketPlaceData["stable-diffusion"]?.lora
- )
- );
- }
- }, [marketPlaceData]);
-
- useEffect(() => {
- if (loasData) {
- setLoras(loasData);
- }
- }, [loasData, setLoras]);
-
- return (
-
-
-
- {t("common:pages.settings.stableDiffusionLoras")}
-
-
-
-
- {
- if (scrollReference.current) {
- scrollReference.current.scrollBy({
- left: -scrollReference.current.clientWidth,
- behavior: "smooth",
- });
- }
- }}
- >
-
-
- {
- if (scrollReference.current) {
- // Scroll one full width of the scrollRef Element to the right
- scrollReference.current.scrollBy({
- left: scrollReference.current.clientWidth,
- behavior: "smooth",
- });
- }
- }}
- >
-
-
-
-
-
- {stableDiffusionModels.map(stableDiffusionModel => (
-
- item.type === "image"
- )?.content
- }
- />
-
- ))}
-
-
-
- );
-}
-
-export function CaptionsSection() {
- const { t } = useTranslation(["common"]);
- // TODO create a type for the models
- const [, setInstalledCaptionModels] = useAtom(captionsAtom);
- const [captionModels, setCaptionModels] = useState([]);
- const scrollReference = useRef(null);
- const scrollPosition = useScrollPosition(scrollReference);
-
- const { data: marketPlaceData } = useSWR(MARKETPLACE_INDEX_DATA);
- const { data: checkpointsData } = useSWR(CAPTIONS, () => window.ipc.getModels("captions"));
-
- useEffect(() => {
- if (marketPlaceData?.caption) {
- setCaptionModels(findAllParentObjectsWithId(marketPlaceData.caption));
- }
- }, [marketPlaceData]);
- useEffect(() => {
- if (checkpointsData) {
- setInstalledCaptionModels(checkpointsData);
- }
- }, [checkpointsData, setInstalledCaptionModels]);
-
- return (
-
-
- {t("common:caption")}
-
-
-
- {
- if (scrollReference.current) {
- scrollReference.current.scrollBy({
- left: -scrollReference.current.clientWidth,
- behavior: "smooth",
- });
- }
- }}
- >
-
-
- {
- if (scrollReference.current) {
- // Scroll one full width of the scrollRef Element to the right
- scrollReference.current.scrollBy({
- left: scrollReference.current.clientWidth,
- behavior: "smooth",
- });
- }
- }}
- >
-
-
-
-
-
- {captionModels.map(captionModel => (
-
- item.type === "text")
- ?.content
- }
- />
-
- ))}
-
-
-
- );
-}
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const { t } = useTranslation(["common"]);
- const [marketplaceDownloading, setMarketplaceDownloading] = useState(false);
-
- useEffect(() => {
- const unsubscribe = window.ipc.on(`${MARKETPLACE_INDEX}:updated`, (_event, data) => {
- setMarketplaceDownloading(false);
- });
- return () => {
- unsubscribe();
- };
- }, []);
-
- return (
- <>
-
- {`Captain | ${t("common:marketplace")}`}
-
-
-
-
- {t("common:marketplace")}
-
-
- :
- }
- onClick={() => {
- setMarketplaceDownloading(true);
- window.ipc.downloadMarketplace(DEFAULT_MARKETPLACE_URL);
- }}
- >
- {t("common:updateMarketplace")}
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";
diff --git a/src/client/pages/[locale]/settings.tsx b/src/client/pages/[locale]/settings.tsx
index fdc0c9f36..793ba8e16 100644
--- a/src/client/pages/[locale]/settings.tsx
+++ b/src/client/pages/[locale]/settings.tsx
@@ -12,14 +12,11 @@ import Typography from "@mui/joy/Typography";
import type { InferGetStaticPropsType } from "next";
import Head from "next/head";
import { useTranslation } from "next-i18next";
-import { useState } from "react";
import { makeStaticProperties } from "@/ions/i18n/get-static";
import { ColorModeSelector } from "@/organisms/color-mode-selector";
import { CustomScrollbars } from "@/organisms/custom-scrollbars";
-import { FolderField } from "@/organisms/folder-field";
import { LanguageSelect } from "@/organisms/language-select";
-import { PasswordField } from "@/organisms/password-field";
export function UserPreferences() {
const { t } = useTranslation(["common"]);
@@ -60,158 +57,6 @@ export function UserPreferences() {
);
}
-export function OpenAISettings() {
- const { t } = useTranslation(["common"]);
-
- return (
-
- {t("common:pages.settings.openAiSettings")}
-
-
-
-
- {t("common:openAiApiKey")}
-
- {t("common:pages.settings.openAiApiKeyDescription")}
-
-
-
- {
- setOpenAiApiKey(event.target.value);
- }}
- onBlur={event => {
- window.ipc.fetch(OPENAI_API_KEY, {
- method: "POST",
- data: event.target.value,
- });
- }} */
- />
-
-
-
-
-
- );
-}
-
-export function StableDiffusionSettings() {
- const { t } = useTranslation(["common"]);
- const [sdSettings, setSdSettings] = useState({ checkpoints: "", loras: "" });
-
- return (
-
- {t("common:pages.settings.stableDiffusionSettings")}
-
-
-
-
-
- {t("common:pages.settings.stableDiffusionCheckpoints")}
-
-
- {t("common:pages.settings.stableDiffusionCheckpointsDescription")}
-
-
-
- {
- setSdSettings(previousValue => ({
- ...previousValue,
- checkpoints: event.target.value,
- }));
- }}
- /* //
- onSelect={async value => {
- await window.ipc.fetch(STABLE_DIFFUSION_SETTINGS, {
- method: "PATCH",
- data: { checkpoints: value },
- });
- setSdSettings(previousValue => ({
- ...previousValue,
- checkpoints: value,
- }));
- }}
- onBlur={event => {
- window.ipc.fetch(STABLE_DIFFUSION_SETTINGS, {
- method: "PATCH",
- data: { checkpoints: event.target.value },
- });
- }} */
- />
-
-
-
-
-
- {t("common:pages.settings.stableDiffusionLoras")}
-
-
- {t("common:pages.settings.stableDiffusionLorasDescription")}
-
-
-
- {
- setSdSettings(previousValue => ({
- ...previousValue,
- loras: event.target.value,
- }));
- }}
- /* //
- onSelect={async value => {
- await window.ipc.fetch(STABLE_DIFFUSION_SETTINGS, {
- method: "PATCH",
- data: { loras: value },
- });
- setSdSettings(previousValue => ({
- ...previousValue,
- loras: value,
- }));
- }}
- onBlur={event => {
- window.ipc.fetch(STABLE_DIFFUSION_SETTINGS, {
- method: "PATCH",
- data: { loras: event.target.value },
- });
- }} */
- />
-
-
-
-
-
- );
-}
-
-export function RunPodSettings() {
- const { t } = useTranslation(["common"]);
- return (
-
- {t("common:pages.settings.runPodSettings")}
-
-
-
-
- {t("common:comingSoon")}
-
-
-
-
-
- );
-}
-
export default function Page(_properties: InferGetStaticPropsType) {
const { t } = useTranslation(["common"]);
return (
diff --git a/src/client/pages/[locale]/training.tsx b/src/client/pages/[locale]/training.tsx
deleted file mode 100644
index f1f6e4708..000000000
--- a/src/client/pages/[locale]/training.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import Box from "@mui/joy/Box";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
-import type { InferGetStaticPropsType } from "next";
-import Head from "next/head";
-import { useTranslation } from "next-i18next";
-
-import { makeStaticProperties } from "@/ions/i18n/get-static";
-import { Lottie } from "@/organisms/lottie";
-
-export default function Page(_properties: InferGetStaticPropsType) {
- const { t } = useTranslation(["common"]);
- return (
- <>
-
- {`Captain | ${t("common:training")}`}
-
-
-
-
-
- {t("common:training")}
-
-
-
-
-
-
- {t("common:comingSoon")}
-
-
-
-
- >
- );
-}
-
-export const getStaticProps = makeStaticProperties(["common"]);
-
-export { getStaticPaths } from "@/ions/i18n/get-static";