Merge pull request #260 from caranicas/react-base-nav

React base - Nav, Audio, Display,
This commit is contained in:
cmdr2 2022-09-29 11:58:04 +05:30 committed by GitHub
commit 0fdb49f168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 842 additions and 617 deletions

View File

@ -3,7 +3,10 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/media/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/media/favicon-32x32.png" sizes="32x32">
<meta name="color-scheme" content="dark light" /> <meta name="color-scheme" content="dark light" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<title>Stable Diffusion UI</title> <title>Stable Diffusion UI</title>
</head> </head>
<body> <body>

View File

@ -8,6 +8,7 @@
"name": "react-ts", "name": "react-ts",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@headlessui/react": "^1.7.2",
"@tanstack/react-location": "^3.7.4", "@tanstack/react-location": "^3.7.4",
"@tanstack/react-query": "^4.2.3", "@tanstack/react-query": "^4.2.3",
"@tanstack/react-query-devtools": "^4.2.3", "@tanstack/react-query-devtools": "^4.2.3",
@ -593,6 +594,18 @@
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
"dev": true "dev": true
}, },
"node_modules/@headlessui/react": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.2.tgz",
"integrity": "sha512-snLv2lxwsf2HNTOBNgHYdvoYZ3ChJE8QszPi1d/hl9js8KrFrUulTaQBfSyPbJP5BybVreWh9DxCgz9S0Z6hKQ==",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": "^16 || ^17 || ^18",
"react-dom": "^16 || ^17 || ^18"
}
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.10.4", "version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
@ -5288,6 +5301,12 @@
} }
} }
}, },
"@headlessui/react": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.2.tgz",
"integrity": "sha512-snLv2lxwsf2HNTOBNgHYdvoYZ3ChJE8QszPi1d/hl9js8KrFrUulTaQBfSyPbJP5BybVreWh9DxCgz9S0Z6hKQ==",
"requires": {}
},
"@humanwhocodes/config-array": { "@humanwhocodes/config-array": {
"version": "0.10.4", "version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",

View File

@ -10,6 +10,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@headlessui/react": "^1.7.2",
"@tanstack/react-location": "^3.7.4", "@tanstack/react-location": "^3.7.4",
"@tanstack/react-query": "^4.2.3", "@tanstack/react-query": "^4.2.3",
"@tanstack/react-query-devtools": "^4.2.3", "@tanstack/react-query-devtools": "^4.2.3",

View File

@ -2,8 +2,7 @@ import React, { useState } from "react";
import { ReactLocation, Router } from "@tanstack/react-location"; import { ReactLocation, Router } from "@tanstack/react-location";
import Home from "./pages/Home"; import Home from "./pages/Home";
import Settings from "./pages/Settings"; import Settings from "./pages/Settings";
// @ts-expect-error import { darkTheme, lightTheme } from "./styles/theme/index.css";
import { darkTheme, lightTheme } from "./styles/theme/index.css.ts";
import "./Translation/config"; import "./Translation/config";
const location = new ReactLocation(); const location = new ReactLocation();

View File

@ -2,7 +2,7 @@
* basic server health * basic server health
*/ */
import type { ImageRequest } from "../stores/imageCreateStore"; import type { SAMPLER_OPTIONS } from "../stores/imageCreateStore";
// when we are on dev we want to specifiy 9000 as the port for the backend // when we are on dev we want to specifiy 9000 as the port for the backend
// when we are on prod we want be realtive to the current url // when we are on prod we want be realtive to the current url
@ -57,6 +57,62 @@ export const toggleBetaConfig = async (branch: string) => {
* post a new request for an image * post a new request for an image
*/ */
// TODO; put hese some place better // TODO; put hese some place better
export interface ImageRequest {
session_id: string;
prompt: string;
seed: number;
num_outputs: number;
num_inference_steps: number;
guidance_scale: number;
width:
| 128
| 192
| 256
| 320
| 384
| 448
| 512
| 576
| 640
| 704
| 768
| 832
| 896
| 960
| 1024;
height:
| 128
| 192
| 256
| 320
| 384
| 448
| 512
| 576
| 640
| 704
| 768
| 832
| 896
| 960
| 1024;
// allow_nsfw: boolean
turbo: boolean;
use_cpu: boolean;
use_full_precision: boolean;
save_to_disk_path: null | string;
use_face_correction: null | "GFPGANv1.3";
use_upscale: null | "RealESRGAN_x4plus" | "RealESRGAN_x4plus_anime_6B" | "";
show_only_filtered_image: boolean;
init_image: undefined | string;
prompt_strength: undefined | number;
mask: undefined | string;
sampler: typeof SAMPLER_OPTIONS[number];
stream_progress_updates: true;
stream_image_progress: boolean;
}
export interface ImageOutput { export interface ImageOutput {
data: string; data: string;
path_abs: string | null; path_abs: string | null;
@ -65,9 +121,8 @@ export interface ImageOutput {
export interface ImageReturnType { export interface ImageReturnType {
output: ImageOutput[]; output: ImageOutput[];
request: {}; request: ImageRequest;
status: string; status: string;
session_id: string;
} }
export const MakeImageKey = "MakeImage"; export const MakeImageKey = "MakeImage";
@ -79,9 +134,5 @@ export const doMakeImage = async (reqBody: ImageRequest) => {
}, },
body: JSON.stringify(reqBody), body: JSON.stringify(reqBody),
}); });
console.log('doMakeImage= GOT RESPONSE', res);
// const data = await res.json();
// return data;
return res; return res;
}; };

View File

@ -0,0 +1,34 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
export const PopoverMain = style({
position: 'relative'
});
export const PopoverButtonStyle = style({
backgroundColor: "transparent",
border: "0 none",
cursor: "pointer",
padding: vars.spacing.none,
fontSize: vars.fonts.sizes.Subheadline,
});
globalStyle(`${PopoverButtonStyle} > i`, {
marginRight: vars.spacing.small,
});
export const PopoverPanelMain = style({
position: 'absolute',
top: '100%',
right: '0',
zIndex: '1',
background: vars.colors.backgroundDark,
color: vars.colors.text.normal,
padding: vars.spacing.medium,
borderRadius: vars.trim.smallBorderRadius,
marginBottom: vars.spacing.medium,
});

View File

@ -0,0 +1,18 @@
import { style, } from "@vanilla-extract/css";
//import { vars } from "../../../styles/theme/index.css";
export const SwitchGroupMain = style({
});
export const SwitchMain = style({
});
export const SwitchLabel = style({
});
export const SwitchEnabled = style({
});
export const SwitchPill = style({
});

View File

@ -9,8 +9,7 @@ import { API_URL } from "../../../api";
import { import {
ModifierTagMain, ModifierTagMain,
tagPreview tagPreview
// @ts-expect-error } from "./modifierTags.css";
} from "./modifierTags.css.ts";
interface ModifierTagProps { interface ModifierTagProps {
name: string; name: string;

View File

@ -13,3 +13,4 @@ const AudioDing = React.forwardRef((props, ref) => (
AudioDing.displayName = "AudioDing"; AudioDing.displayName = "AudioDing";
export default AudioDing; export default AudioDing;

View File

@ -52,7 +52,7 @@ export default function BetaMode() {
if (toggleStatus === "success") { if (toggleStatus === "success") {
if (toggleData[0] === "OK") { if (toggleData[0] === "OK") {
// force a refetch of the config // force a refetch of the config
queryClient.invalidateQueries([KEY_CONFIG]); void queryClient.invalidateQueries([KEY_CONFIG])
} }
setShouldSetConfig(false); setShouldSetConfig(false);
} }
@ -66,7 +66,7 @@ export default function BetaMode() {
onChange={(e) => { onChange={(e) => {
setShouldSetConfig(true); setShouldSetConfig(true);
}} }}
/> />🔥
{t("advanced-settings.beta")} {t("advanced-settings.beta-disc")} {t("advanced-settings.beta")} {t("advanced-settings.beta-disc")}
</label> </label>
); );

View File

@ -1,15 +1,14 @@
// @ts-nocheck
import React, { useRef, useState, useEffect } from "react"; import React, { useRef, useState, useEffect } from "react";
import { import {
DrawImageMain, // @ts-expect-error DrawImageMain,
} from "./drawImage.css.ts"; } from "./drawImage.css";
// https://github.com/embiem/react-canvas-draw // https://github.com/embiem/react-canvas-draw
interface DrawImageProps { interface DrawImageProps {
imageData: string; imageData: string;
brushSize: string; brushSize: number;
brushShape: string; brushShape: string;
brushColor: string; brushColor: string;
@ -46,16 +45,19 @@ export default function DrawImage({
// drawn pixels to the new color // drawn pixels to the new color
if (drawingRef.current != null) { if (drawingRef.current != null) {
const ctx = drawingRef.current.getContext("2d"); const ctx = drawingRef.current.getContext("2d");
const imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); if (ctx != null) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) { const imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
if (data[i + 3] > 0) { const data = imageData.data;
data[i] = parseInt(brushColor, 16); for (let i = 0; i < data.length; i += 4) {
data[i + 1] = parseInt(brushColor, 16); if (data[i + 3] > 0) {
data[i + 2] = parseInt(brushColor, 16); data[i] = parseInt(brushColor, 16);
data[i + 1] = parseInt(brushColor, 16);
data[i + 2] = parseInt(brushColor, 16);
}
} }
ctx.putImageData(imageData, 0, 0);
} }
ctx.putImageData(imageData, 0, 0);
} }
}, [brushColor]); }, [brushColor]);
@ -80,23 +82,25 @@ export default function DrawImage({
} }
}; };
const _drawCanvas = (x, y, brushSize, brushShape, brushColor) => { const _drawCanvas = (x: number, y: number, brushSize: number, brushShape: string, brushColor: string | CanvasGradient | CanvasPattern) => {
const canvas = drawingRef.current; const canvas = drawingRef.current;
if (canvas != null) { if (canvas != null) {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
if (isErasing) { if (ctx != null) {
// stack overflow https://stackoverflow.com/questions/10396991/clearing-circular-regions-from-html5-canvas if (isErasing) {
// stack overflow https://stackoverflow.com/questions/10396991/clearing-circular-regions-from-html5-canvas
const offset = brushSize / 2; const offset = brushSize / 2;
ctx.clearRect(x - offset, y - offset, brushSize, brushSize); ctx.clearRect(x - offset, y - offset, brushSize, brushSize);
} else { } else {
ctx.beginPath(); ctx.beginPath();
ctx.lineWidth = brushSize; ctx.lineWidth = brushSize;
ctx.lineCap = brushShape; // @ts-expect-error
ctx.strokeStyle = brushColor; ctx.lineCap = brushShape;
ctx.moveTo(x, y); ctx.strokeStyle = brushColor;
ctx.lineTo(x, y); ctx.moveTo(x, y);
ctx.stroke(); ctx.lineTo(x, y);
ctx.stroke();
}
} }
} }
}; };
@ -111,29 +115,35 @@ export default function DrawImage({
const canvas = cursorRef.current; const canvas = cursorRef.current;
if (canvas != null) { if (canvas != null) {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
ctx.beginPath(); if (ctx != null) {
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (isErasing) { if (isErasing) {
const offset = brushSize / 2; const offset = brushSize / 2;
// draw a quare // draw a quare
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.lineCap = "butt"; ctx.lineCap = "butt";
ctx.strokeStyle = brushColor; ctx.strokeStyle = brushColor;
ctx.moveTo(x - offset, y - offset); ctx.moveTo(x - offset, y - offset);
ctx.lineTo(x + offset, y - offset); ctx.lineTo(x + offset, y - offset);
ctx.lineTo(x + offset, y + offset); ctx.lineTo(x + offset, y + offset);
ctx.lineTo(x - offset, y + offset); ctx.lineTo(x - offset, y + offset);
ctx.lineTo(x - offset, y - offset); ctx.lineTo(x - offset, y - offset);
ctx.stroke(); ctx.stroke();
} else { } else {
ctx.lineWidth = brushSize; ctx.lineWidth = brushSize;
ctx.lineCap = brushShape; // @ts-expect-error
ctx.strokeStyle = brushColor; ctx.lineCap = brushShape;
ctx.moveTo(x, y); ctx.strokeStyle = brushColor;
ctx.lineTo(x, y); ctx.moveTo(x, y);
ctx.stroke(); ctx.lineTo(x, y);
ctx.stroke();
}
} }
} }
}; };
@ -156,8 +166,10 @@ export default function DrawImage({
const canvas = drawingRef.current; const canvas = drawingRef.current;
if (canvas != null) { if (canvas != null) {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
ctx.fillStyle = brushColor; if (ctx != null) {
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = brushColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
} }
}; };

View File

@ -1,20 +1,11 @@
import { style } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
export const generatedImageMain = style({ export const generatedImageMain = style({
position: "relative", position: "relative",
}); });
// export const imageContain = style({ globalStyle(`${generatedImageMain} img`, {
// width: "512px", width: "100%",
// height: "512px", height: "100%",
// backgroundColor: "black", objectFit: "contain",
// display: "flex",
// justifyContent: "center",
// alignItems: "center",
// });
export const image = style({
// width: "512px",
// height: "512px",
// objectFit: "contain",
}); });

View File

@ -1,17 +1,15 @@
import React from "react"; import React from "react";
import { ImageRequest } from "../../../stores/imageCreateStore"; import { ImageRequest } from "../../../api";
import { import {
generatedImageMain, generatedImageMain,
image, // @ts-expect-error } from "./generatedImage.css";
} from "./generatedImage.css.ts";
interface GeneretaedImageProps { interface GeneretaedImageProps {
imageData: string | undefined; imageData: string | undefined;
metadata: ImageRequest | undefined; metadata: ImageRequest | undefined;
className?: string; className?: string;
// children: never[];
} }
export default function GeneratedImage({ export default function GeneratedImage({
@ -21,7 +19,7 @@ export default function GeneratedImage({
}: GeneretaedImageProps) { }: GeneretaedImageProps) {
return ( return (
<div className={[generatedImageMain, className].join(" ")}> <div className={[generatedImageMain, className].join(" ")}>
<img className={image} src={imageData} alt={metadata!.prompt} /> <img src={imageData} alt={metadata!.prompt} />
</div> </div>
); );
} }

View File

@ -1,12 +1,9 @@
import { style, globalStyle } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../../styles/theme/index.css";
import { vars } from "../../../../styles/theme/index.css.ts";
// import { PanelBox } from "../../../../styles/shared.css.ts";
export const AdvancedSettingsList = style({ export const AdvancedSettingsList = style({
// marginBottom: vars.spacing.small,
paddingLeft: 0, paddingLeft: 0,
listStyleType: "none", listStyleType: "none",
}); });
@ -14,19 +11,3 @@ export const AdvancedSettingsList = style({
export const AdvancedSettingGrouping = style({ export const AdvancedSettingGrouping = style({
marginTop: vars.spacing.medium, marginTop: vars.spacing.medium,
}); });
export const MenuButton = style({
display: "block",
width: "100%",
textAlign: "left",
backgroundColor: "transparent",
color: vars.colors.text.normal,
border: "0 none",
cursor: "pointer",
padding: "0",
marginBottom: vars.spacing.medium,
});
globalStyle(`${MenuButton}> h4`, {
color: "#e7ba71",
});

View File

@ -1,74 +0,0 @@
import React from "react";
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useCreateUI } from "../../creationPanelUIStore";
import {
SettingItem, // @ts-expect-error
} from "../../../../../styles/shared.css.ts";
import {
MenuButton, // @ts-expect-error
} from "../advancedsettings.css.ts";
import { useTranslation } from "react-i18next";
export default function GpuSettings() {
const { t } = useTranslation();
const turbo = useImageCreate((state) => state.getValueForRequestKey("turbo"));
const useCpu = useImageCreate((state) =>
state.getValueForRequestKey("use_cpu")
);
const useFullPrecision = useImageCreate((state) =>
state.getValueForRequestKey("use_full_precision")
);
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const gpuOpen = useCreateUI((state) => state.isOpenAdvGPUSettings);
const toggleGpuOpen = useCreateUI((state) => state.toggleAdvGPUSettings);
return (
<div>
<button type="button" className={MenuButton} onClick={toggleGpuOpen}>
<h4>GPU Settings</h4>
</button>
{gpuOpen && (
<>
<div className={SettingItem}>
<label>
<input
checked={turbo}
onChange={(e) => setRequestOption("turbo", e.target.checked)}
type="checkbox"
/>
{t("advanced-settings.turbo")} {t("advanced-settings.turbo-disc")}
</label>
</div>
<div className={SettingItem}>
<label>
<input
type="checkbox"
checked={useCpu}
onChange={(e) => setRequestOption("use_cpu", e.target.checked)}
/>
{t("advanced-settings.cpu")} {t("advanced-settings.cpu-disc")}
</label>
</div>
<div className={SettingItem}>
<label>
<input
checked={useFullPrecision}
onChange={(e) =>
setRequestOption("use_full_precision", e.target.checked)
}
type="checkbox"
/>
{t("advanced-settings.gpu")} {t("advanced-settings.gpu-disc")}
</label>
</div>
</>
)}
</div>
);
}

View File

@ -4,11 +4,9 @@ import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useCreateUI } from "../../creationPanelUIStore"; import { useCreateUI } from "../../creationPanelUIStore";
import { import {
SettingItem, // @ts-expect-error SettingItem,
} from "../../../../../styles/shared.css.ts"; MenuButton,
import { } from "../../../../../styles/shared.css";
MenuButton, // @ts-expect-error
} from "../advancedsettings.css.ts";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@ -1,20 +1,16 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useCreateUI } from "../creationPanelUIStore"; import { useCreateUI } from "../creationPanelUIStore";
// @ts-expect-error import { PanelBox } from "../../../../styles/shared.css";
import { PanelBox } from "../../../../styles/shared.css.ts";
import { import {
AdvancedSettingsList, AdvancedSettingsList,
AdvancedSettingGrouping, // @ts-expect-error AdvancedSettingGrouping,
} from "./advancedsettings.css.ts"; } from "./advancedsettings.css";
import ImprovementSettings from "./improvementSettings"; import ImprovementSettings from "./improvementSettings";
import PropertySettings from "./propertySettings"; import PropertySettings from "./propertySettings";
import WorkflowSettings from "./workflowSettings"; import WorkflowSettings from "./workflowSettings";
import GpuSettings from "./gpuSettings";
// import BetaMode from "../../../molecules/betaMode";
function SettingsList() { function SettingsList() {
return ( return (
@ -28,13 +24,6 @@ function SettingsList() {
<li className={AdvancedSettingGrouping}> <li className={AdvancedSettingGrouping}>
<WorkflowSettings /> <WorkflowSettings />
</li> </li>
<li className={AdvancedSettingGrouping}>
<GpuSettings />
</li>
{/* <li className={AdvancedSettingGrouping}>
<BetaMode />
</li> */}
</ul> </ul>
); );
} }

View File

@ -3,12 +3,11 @@ import { useImageCreate, SAMPLER_OPTIONS } from "../../../../../stores/imageCrea
import { useCreateUI } from "../../creationPanelUIStore"; import { useCreateUI } from "../../creationPanelUIStore";
import { import {
SettingItem, // @ts-expect-error SettingItem,
} from "../../../../../styles/shared.css.ts"; MenuButton
} from "../../../../../styles/shared.css";
import {
MenuButton, // @ts-expect-error
} from "../advancedsettings.css.ts";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@ -4,12 +4,10 @@ import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useCreateUI } from "../../creationPanelUIStore"; import { useCreateUI } from "../../creationPanelUIStore";
import { import {
SettingItem, // @ts-expect-error SettingItem,
} from "../../../../../styles/shared.css.ts"; MenuButton,
} from "../../../../../styles/shared.css";
import {
MenuButton, // @ts-expect-error
} from "../advancedsettings.css.ts";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -20,21 +18,10 @@ export default function WorkflowSettings() {
state.getValueForRequestKey("num_outputs") state.getValueForRequestKey("num_outputs")
); );
const parallelCount = useImageCreate((state) => state.parallelCount); const parallelCount = useImageCreate((state) => state.parallelCount);
const isUseAutoSave = useImageCreate((state) => state.isUseAutoSave());
const diskPath = useImageCreate((state) =>
state.getValueForRequestKey("save_to_disk_path")
);
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
const setRequestOption = useImageCreate((state) => state.setRequestOptions); const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const setParallelCount = useImageCreate((state) => state.setParallelCount); const setParallelCount = useImageCreate((state) => state.setParallelCount);
const shouldStreamImages = useImageCreate((state) => state.getValueForRequestKey("stream_image_progress")); const shouldStreamImages = useImageCreate((state) => state.getValueForRequestKey("stream_image_progress"));
const toggleUseAutoSave = useImageCreate((state) => state.toggleUseAutoSave);
const toggleSoundEnabled = useImageCreate(
(state) => state.toggleSoundEnabled
);
const workflowOpen = useCreateUI((state) => state.isOpenAdvWorkflowSettings); const workflowOpen = useCreateUI((state) => state.isOpenAdvWorkflowSettings);
const toggleWorkflowOpen = useCreateUI( const toggleWorkflowOpen = useCreateUI(
@ -85,40 +72,6 @@ export default function WorkflowSettings() {
/> />
</label> </label>
</div> </div>
<div className={SettingItem}>
<label>
<input
checked={isUseAutoSave}
onChange={(e) => toggleUseAutoSave()}
type="checkbox"
/>
{t("storage.ast")}{" "}
</label>
<label>
<input
value={diskPath}
onChange={(e) =>
setRequestOption("save_to_disk_path", e.target.value)
}
size={40}
disabled={!isUseAutoSave}
/>
<span className="visually-hidden">
Path on disk where images will be saved
</span>
</label>
</div>
<div className={SettingItem}>
<label>
<input
checked={isSoundEnabled}
onChange={(e) => toggleSoundEnabled()}
type="checkbox"
/>
{t("advanced-settings.sound")}
</label>
</div>
</> </>
)} )}
</div> </div>

View File

@ -6,8 +6,6 @@ import ModifierTag from "../../../../atoms/modifierTag";
export default function ActiveTags() { export default function ActiveTags() {
const selectedtags = useImageCreate((state) => state.selectedTags()); const selectedtags = useImageCreate((state) => state.selectedTags());
console.log("ActiveTags", selectedtags);
return ( return (
<div className="selected-tags"> <div className="selected-tags">
<p>Active Tags</p> <p>Active Tags</p>

View File

@ -3,8 +3,8 @@ import { useImageCreate } from "../../../../stores/imageCreateStore";
import { import {
CreationBasicMain, CreationBasicMain,
PromptDisplay, // @ts-expect-error PromptDisplay,
} from "./basicCreation.css.ts"; } from "./basicCreation.css";
import SeedImage from "./seedImage"; import SeedImage from "./seedImage";
import ActiveTags from "./activeTags"; import ActiveTags from "./activeTags";

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import React, { useEffect } from "react"; import React, { useEffect, useRef } from "react";
import { useImageCreate, ImageRequest } from "../../../../../stores/imageCreateStore"; import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useImageQueue } from "../../../../../stores/imageQueueStore"; import { useImageQueue } from "../../../../../stores/imageQueueStore";
import { import {
FetchingStates, FetchingStates,
@ -13,18 +13,27 @@ import { useImageDisplay } from "../../../../../stores/imageDisplayStore";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { useRandomSeed } from "../../../../../utils"; import { useRandomSeed } from "../../../../../utils";
import { doMakeImage } from "../../../../../api";
import { import {
MakeButtonStyle, // @ts-expect-error ImageRequest,
} from "./makeButton.css.ts"; ImageReturnType,
ImageOutput,
doMakeImage,
} from "../../../../../api";
import {
MakeButtonStyle,
} from "./makeButton.css";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import AudioDing from "../../../../molecules/audioDing"; import AudioDing from "../../../../molecules/audioDing";
const idDelim = "_batch";
export default function MakeButton() { export default function MakeButton() {
const { t } = useTranslation(); const { t } = useTranslation();
const dingRef = useRef<HTMLAudioElement>();
const parallelCount = useImageCreate((state) => state.parallelCount); const parallelCount = useImageCreate((state) => state.parallelCount);
const builtRequest = useImageCreate((state) => state.builtRequest); const builtRequest = useImageCreate((state) => state.builtRequest);
const isRandomSeed = useImageCreate((state) => state.isRandomSeed()); const isRandomSeed = useImageCreate((state) => state.isRandomSeed());
@ -47,45 +56,22 @@ export default function MakeButton() {
const updateDisplay = useImageDisplay((state) => state.updateDisplay); const updateDisplay = useImageDisplay((state) => state.updateDisplay);
const hackJson = (jsonStr: string) => { const hackJson = (jsonStr: string, id: string) => {
// DONES't seem to be needed for the updated progress implementation
// if (jsonStr !== undefined && jsonStr.indexOf('}{') !== -1) {
// // hack for a middleman buffering all the streaming updates, and unleashing them
// // on the poor browser in one shot.
// // this results in having to parse JSON like {"step": 1}{"step": 2}...{"status": "succeeded"..}
// // which is obviously invalid.
// // So we need to just extract the last {} section, starting from "status" to the end of the response
// const lastChunkIdx = jsonStr.lastIndexOf('}{')
// if (lastChunkIdx !== -1) {
// const remaining = jsonStr.substring(lastChunkIdx)
// jsonStr = remaining.substring(1)
// }
// }
try { try {
// todo - used zod or something to validate this const parsed = JSON.parse(jsonStr);
interface jsonResponseType { const { status, request, output: outputs } = parsed as ImageReturnType;
status: string;
request: ImageRequest;
output: []
}
const { status, request, output: outputs }: jsonResponseType = JSON.parse(jsonStr);
if (status === 'succeeded') { if (status === 'succeeded') {
outputs.forEach((output: any) => { outputs.forEach((output: any, index: number) => {
const { data, seed } = output;
const { data, seed } = output as ImageOutput;
const seedReq = { const seedReq = {
...request, ...request,
seed, seed,
}; };
const batchId = `${id}${idDelim}-${seed}-${index}`;
updateDisplay(data, seedReq); updateDisplay(batchId, data, seedReq);
}); });
} }
@ -100,18 +86,17 @@ export default function MakeButton() {
} }
const parseRequest = async (id: string, reader: ReadableStreamDefaultReader<Uint8Array>) => { const parseRequest = async (id: string, reader: ReadableStreamDefaultReader<Uint8Array>) => {
console.log('parseRequest');
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let finalJSON = ''; let finalJSON = '';
console.log('id', id);
while (true) { while (true) {
const { done, value } = await reader.read(); const { done, value } = await reader.read();
const jsonStr = decoder.decode(value); const jsonStr = decoder.decode(value);
if (done) { if (done) {
removeFirstInQueue(); removeFirstInQueue();
setStatus(FetchingStates.COMPLETE); setStatus(FetchingStates.COMPLETE);
hackJson(finalJSON) hackJson(finalJSON, id);
void dingRef.current?.play();
break; break;
} }
@ -163,10 +148,7 @@ export default function MakeButton() {
} }
const startStream = async (id: string, req: ImageRequest) => { const startStream = async (id: string, req: ImageRequest) => {
// const streamReq = {
// ...req,
// stream_image_progress: true,
// };
try { try {
resetForFetching(); resetForFetching();
@ -262,14 +244,17 @@ export default function MakeButton() {
}, [hasQueue, status, id, options, startStream]); }, [hasQueue, status, id, options, startStream]);
return ( return (
<button <>
className={MakeButtonStyle} <button
onClick={() => { className={MakeButtonStyle}
void makeImageQueue(); onClick={() => {
}} void makeImageQueue();
disabled={hasQueue} }}
> disabled={hasQueue}
{t("home.make-img-btn")} >
</button> {t("home.make-img-btn")}
</button>
<AudioDing ref={dingRef}></AudioDing>
</>
); );
} }

View File

@ -1,31 +1,10 @@
import { style } from "@vanilla-extract/css"; import { style } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../../../styles/theme/index.css";
import { vars } from "../../../../../styles/theme/index.css.ts";
export const MakeButtonStyle = style({ import { BrandedButton } from "../../../../../styles/shared.css";
export const MakeButtonStyle = style([BrandedButton, {
width: "100%", width: "100%",
backgroundColor: vars.colors.brand,
fontSize: vars.fonts.sizes.Headline, fontSize: vars.fonts.sizes.Headline,
fontWeight: "bold", }]);
color: vars.colors.text.normal,
padding: vars.spacing.small,
borderRadius: vars.trim.smallBorderRadius,
":hover": {
backgroundColor: vars.colors.brandHover,
},
":active": {
backgroundColor: vars.colors.brandActive,
},
":disabled": {
backgroundColor: vars.colors.brandDimmed,
color: vars.colors.text.dimmed,
},
":focus": {
outline: "none",
},
});

View File

@ -6,8 +6,8 @@ import {
ImageInput, ImageInput,
ImageInputButton, ImageInputButton,
ImageFixer, ImageFixer,
XButton, // @ts-expect-error XButton,
} from "./seedImage.css.ts"; } from "./seedImage.css";
import { useImageCreate } from "../../../../../stores/imageCreateStore"; import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@ -1,7 +1,8 @@
import { style } from "@vanilla-extract/css"; import { style } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../../../styles/theme/index.css";
import { vars } from "../../../../../styles/theme/index.css.ts";
import { BrandedButton } from "../../../../../styles/shared.css";
export const ImageInputDisplay = style({ export const ImageInputDisplay = style({
display: "flex", display: "flex",
@ -16,27 +17,7 @@ export const ImageInput = style({
display: "none", display: "none",
}); });
export const ImageInputButton = style({ export const ImageInputButton = style([BrandedButton]);
backgroundColor: vars.colors.brand,
fontSize: vars.fonts.sizes.Subheadline,
fontWeight: "bold",
color: vars.colors.text.normal,
padding: vars.spacing.small,
borderRadius: vars.trim.smallBorderRadius,
":hover": {
backgroundColor: vars.colors.brandHover,
},
":active": {
backgroundColor: vars.colors.brandActive,
},
":disabled": {
backgroundColor: vars.colors.brandDimmed,
color: vars.colors.text.dimmed,
},
});
// this is needed to fix an issue with the image input text // this is needed to fix an issue with the image input text
// when that is a drag an drop we can remove this // when that is a drag an drop we can remove this

View File

@ -7,16 +7,12 @@ export interface ImageCreationUIOptions {
isOpenAdvImprovementSettings: boolean; isOpenAdvImprovementSettings: boolean;
isOpenAdvPropertySettings: boolean; isOpenAdvPropertySettings: boolean;
isOpenAdvWorkflowSettings: boolean; isOpenAdvWorkflowSettings: boolean;
isOpenAdvGPUSettings: boolean;
isOpenImageModifier: boolean; isOpenImageModifier: boolean;
imageMofidiersMap: object;
toggleAdvancedSettings: () => void; toggleAdvancedSettings: () => void;
toggleAdvImprovementSettings: () => void; toggleAdvImprovementSettings: () => void;
toggleAdvPropertySettings: () => void; toggleAdvPropertySettings: () => void;
toggleAdvWorkflowSettings: () => void; toggleAdvWorkflowSettings: () => void;
toggleAdvGPUSettings: () => void;
toggleImageModifier: () => void; toggleImageModifier: () => void;
// addImageModifier: (modifier: string) => void; // addImageModifier: (modifier: string) => void;
@ -30,9 +26,7 @@ export const useCreateUI = create<ImageCreationUIOptions>(
isOpenAdvImprovementSettings: false, isOpenAdvImprovementSettings: false,
isOpenAdvPropertySettings: false, isOpenAdvPropertySettings: false,
isOpenAdvWorkflowSettings: false, isOpenAdvWorkflowSettings: false,
isOpenAdvGPUSettings: false,
isOpenImageModifier: false, isOpenImageModifier: false,
imageMofidiersMap: {},
toggleAdvancedSettings: () => { toggleAdvancedSettings: () => {
set( set(
@ -67,14 +61,6 @@ export const useCreateUI = create<ImageCreationUIOptions>(
); );
}, },
toggleAdvGPUSettings: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.isOpenAdvGPUSettings = !state.isOpenAdvGPUSettings;
})
);
},
toggleImageModifier: () => { toggleImageModifier: () => {
set( set(
produce((state: ImageCreationUIOptions) => { produce((state: ImageCreationUIOptions) => {

View File

@ -1,6 +1,5 @@
import { style, globalStyle } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../../styles/theme/index.css";
import { vars } from "../../../../styles/theme/index.css.ts";
export const ImagerModifierGroups = style({ export const ImagerModifierGroups = style({
// marginBottom: vars.spacing.small, // marginBottom: vars.spacing.small,
@ -16,24 +15,8 @@ export const ImageModifierGrouping = style({
marginTop: vars.spacing.medium, marginTop: vars.spacing.medium,
}); });
export const MenuButton = style({
display: "block",
width: "100%",
textAlign: "left",
backgroundColor: "transparent",
color: vars.colors.text.normal,
border: "0 none",
cursor: "pointer",
padding: "0",
marginBottom: vars.spacing.medium,
});
globalStyle(`${MenuButton}> h4`, {
color: "#e7ba71",
});
export const ModifierListStyle = style({ export const ModifierListStyle = style({
// marginBottom: vars.spacing.small,
paddingLeft: 0, paddingLeft: 0,
listStyleType: "none", listStyleType: "none",
display: "flex", display: "flex",

View File

@ -1,14 +1,16 @@
import React, { useState } from "react"; import React, { useState } from "react";
// @ts-expect-error
import { PanelBox } from "../../../../styles/shared.css.ts"; import {
PanelBox,
MenuButton,
} from "../../../../styles/shared.css";
import { import {
ImagerModifierGroups, ImagerModifierGroups,
ImageModifierGrouping, ImageModifierGrouping,
MenuButton, ModifierListStyle,
ModifierListStyle, //@ts-expect-error } from "./imageModifiers.css";
} from "./imageModifiers.css.ts";
import { ModifierObject, useImageCreate } from "../../../../stores/imageCreateStore"; import { ModifierObject, useImageCreate } from "../../../../stores/imageCreateStore";
import { useCreateUI } from "../creationPanelUIStore"; import { useCreateUI } from "../creationPanelUIStore";

View File

@ -13,8 +13,8 @@ import "./creationPanel.css";
import { import {
CreationPaneMain, CreationPaneMain,
InpaintingSlider, // @ts-expect-error InpaintingSlider,
} from "./creationpane.css.ts"; } from "./creationpane.css";
import BasicCreation from "./basicCreation"; import BasicCreation from "./basicCreation";

View File

@ -6,8 +6,8 @@ import { useImageCreate } from "../../../../stores/imageCreateStore";
import { import {
InpaintingPanelMain, InpaintingPanelMain,
InpaintingControls, InpaintingControls,
InpaintingControlRow, // @ts-expect-error InpaintingControlRow,
} from "./inpaintingPanel.css.ts"; } from "./inpaintingPanel.css";
export default function InpaintingPanel() { export default function InpaintingPanel() {
// no idea if this is the right typing // no idea if this is the right typing
@ -47,6 +47,7 @@ export default function InpaintingPanel() {
<DrawImage <DrawImage
// ref={drawingRef} // ref={drawingRef}
imageData={initImage} imageData={initImage}
// @ts-expect-error
brushSize={brushSize} brushSize={brushSize}
brushShape={brushShape} brushShape={brushShape}
brushColor={brushColor} brushColor={brushColor}

View File

@ -1,7 +1,6 @@
import { style, globalStyle } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../../styles/theme/index.css";
import { vars } from "../../../../styles/theme/index.css.ts";
export const completedImagesMain = style({ export const completedImagesMain = style({
height: "100%", height: "100%",
@ -56,5 +55,5 @@ export const RemoveButton = style({
border: "0 none", border: "0 none",
padding: vars.spacing.small, padding: vars.spacing.small,
cursor: "pointer", cursor: "pointer",
borderRadius: vars.trim.borderRadiusSmall, borderRadius: vars.trim.smallBorderRadius,
}); });

View File

@ -8,8 +8,7 @@ import {
completedImagesList, completedImagesList,
imageContain, imageContain,
RemoveButton, RemoveButton,
// @ts-expect-error } from "./completedImages.css";
} from "./completedImages.css.ts";

View File

@ -0,0 +1,12 @@
import { style } from '@vanilla-extract/css'
// handles all 3
export const currentDisplayMain = style({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
padding: '0 0 0 0',
})

View File

@ -0,0 +1,41 @@
import { style, globalStyle } from '@vanilla-extract/css'
import { vars } from '../../../../../styles/theme/index.css'
export const imageDisplayMain = style({
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
});
export const imageDisplayContainer = style({
height: '100%',
width: '80%',
display: 'flex',
justifyContent: 'center',
});
export const imageDisplayCenter = style({
width: '100%',
maxWidth: '1000px',
position: 'relative',
});
export const imageDisplayContent = style({
display: 'flex',
flexDirection: 'column',
});
globalStyle(`${imageDisplayContent} > div`, {
marginBottom: vars.spacing.medium,
});
globalStyle(`${imageDisplayContent} p`, {
marginBottom: vars.spacing.small,
});
globalStyle(`${imageDisplayContent} button`, {
marginRight: vars.spacing.medium,
});

View File

@ -0,0 +1,90 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React from "react";
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { CompletedImagesType } from "../../../../../stores/imageDisplayStore";
import GeneratedImage from "../../../../molecules/generatedImage";
import {
imageDisplayMain,
imageDisplayContainer,
imageDisplayCenter,
imageDisplayContent,
} from './imageDisplay.css';
import {
BrandedButton
} from '../../../../../styles/shared.css'
export default function ImageDisplay({ info, data }: CompletedImagesType) {
const createFileName = () => {
const {
prompt,
seed,
num_inference_steps,
guidance_scale,
use_face_correction,
use_upscale,
width,
height,
} = info;
// Most important information is the prompt
let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, "_");
underscoreName = underscoreName.substring(0, 100);
// name and the top level metadata
let fileName = `${underscoreName}_Seed-${seed}_Steps-${num_inference_steps}_Guidance-${guidance_scale}`;
// Add the face correction and upscale
if (typeof use_face_correction == "string") {
fileName += `_FaceCorrection-${use_face_correction}`;
}
if (typeof use_upscale == "string") {
fileName += `_Upscale-${use_upscale}`;
}
// Add the width and height
fileName += `_${width}x${height}`;
// add the file extension
fileName += ".png";
// return fileName
return fileName;
};
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const _handleSave = () => {
const link = document.createElement("a");
link.download = createFileName();
link.href = data ?? "";
link.click();
};
const _handleUseAsInput = () => {
setRequestOption("init_image", data);
};
return (
<div className={imageDisplayMain}>
<div className={imageDisplayContainer}>
<div className={imageDisplayCenter}>
<div className={imageDisplayContent}>
<div>
<p> {info?.prompt}</p>
<div>
<button className={BrandedButton} onClick={_handleSave}>Save</button>
<button className={BrandedButton} onClick={_handleUseAsInput}>Use as Input</button>
</div>
</div>
<GeneratedImage imageData={data} metadata={info}></GeneratedImage>
</div>
</div>
</div>
</div>
);
};

View File

@ -1,14 +1,15 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import GeneratedImage from "../../../molecules/generatedImage";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import { FetchingStates, useImageFetching } from "../../../../stores/imageFetchingStore"; import { FetchingStates, useImageFetching } from "../../../../stores/imageFetchingStore";
import { CompletedImagesType, useImageDisplay } from "../../../../stores/imageDisplayStore"; import { useImageDisplay } from "../../../../stores/imageDisplayStore";
import { API_URL } from "../../../../api"; import { API_URL } from "../../../../api";
import { isGeneratorFunction } from "util/types";
import {
currentDisplayMain,
} from './currentDisplay.css';
import ImageDisplay from "./imageDisplay";
const IdleDisplay = () => { const IdleDisplay = () => {
return ( return (
@ -65,64 +66,6 @@ const LoadingDisplay = () => {
); );
}; };
const ImageDisplay = ({ info, data }: CompletedImagesType) => {
const createFileName = () => {
const {
prompt,
seed,
num_inference_steps,
guidance_scale,
use_face_correction,
use_upscale,
width,
height,
} = info;
// Most important information is the prompt
let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, "_");
underscoreName = underscoreName.substring(0, 100);
// name and the top level metadata
let fileName = `${underscoreName}_Seed-${seed}_Steps-${num_inference_steps}_Guidance-${guidance_scale}`;
// Add the face correction and upscale
if (typeof use_face_correction == "string") {
fileName += `_FaceCorrection-${use_face_correction}`;
}
if (typeof use_upscale == "string") {
fileName += `_Upscale-${use_upscale}`;
}
// Add the width and height
fileName += `_${width}x${height}`;
// add the file extension
fileName += ".png";
// return fileName
return fileName;
};
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const _handleSave = () => {
const link = document.createElement("a");
link.download = createFileName();
link.href = data ?? "";
link.click();
};
const _handleUseAsInput = () => {
setRequestOption("init_image", data);
};
return (
<div className="imageDisplay">
<p> {info?.prompt}</p>
<GeneratedImage imageData={data} metadata={info}></GeneratedImage>
<div>
<button onClick={_handleSave}>Save</button>
<button onClick={_handleUseAsInput}>Use as Input</button>
</div>
</div>
);
};
export default function CurrentDisplay() { export default function CurrentDisplay() {
@ -130,7 +73,7 @@ export default function CurrentDisplay() {
const currentImage = useImageDisplay((state) => state.currentImage); const currentImage = useImageDisplay((state) => state.currentImage);
return ( return (
<div className="current-display"> <div className={currentDisplayMain}>
{status === FetchingStates.IDLE && <IdleDisplay />} {status === FetchingStates.IDLE && <IdleDisplay />}

View File

@ -1,17 +1,18 @@
import { style } from "@vanilla-extract/css"; import { style } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
export const displayPanel = style({ export const displayPanel = style({
height: "100%", height: "100%",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
paddingRight: vars.spacing.medium,
}); });
export const displayContainer = style({ export const displayContainer = style({
flexGrow: 1, flexGrow: 1,
display: "flex", overflow: 'auto',
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}); });
export const previousImages = style({}); export const previousImages = style({
minHeight: '250px',
});

View File

@ -8,34 +8,13 @@ import {
displayPanel, displayPanel,
displayContainer, displayContainer,
previousImages, previousImages,
// @ts-expect-error } from "./displayPanel.css";
} from "./displayPanel.css.ts";
const idDelim = "_batch";
export default function DisplayPanel() { export default function DisplayPanel() {
// if (completedQueries.length > 0) {
// // map the completedImagesto a new array
// // and then set the state
// const temp = completedQueries
// .map((query, index) => {
// if (void 0 !== query) {
// return query.output.map((data: ImageOutput, index: number) => {
// return {
// id: `${completedIds[index]}${idDelim}-${data.seed}-${index}`,
// data: data.data,
// info: { ...query.request, seed: data.seed },
// };
// });
// }
// })
// .flat()
// .reverse()
// .filter((item) => void 0 !== item) as CompletedImagesType[]; // remove undefined items
return ( return (
<div className={displayPanel}> <div className={displayPanel}>
<div className={displayContainer}> <div className={displayContainer}>
<CurrentDisplay <CurrentDisplay
></CurrentDisplay> ></CurrentDisplay>

View File

@ -1,7 +1,6 @@
import { style, globalStyle } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../styles/theme/index.css";
import { vars } from "../../../styles/theme/index.css.ts";
export const FooterDisplayMain = style({ export const FooterDisplayMain = style({
color: vars.colors.text.normal, color: vars.colors.text.normal,

View File

@ -2,8 +2,8 @@ import React from "react";
import { import {
FooterDisplayMain, FooterDisplayMain,
CoffeeButton, // @ts-expect-error CoffeeButton,
} from "./footerDisplay.css.ts"; } from "./footerDisplay.css";
import { API_URL } from "../../../api"; import { API_URL } from "../../../api";

View File

@ -1,12 +1,10 @@
import { style, globalStyle } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
//@ts-expect-error import { vars } from "../../../styles/theme/index.css";
import { vars } from "../../../styles/theme/index.css.ts";
export const HeaderDisplayMain = style({ export const HeaderDisplayMain = style({
color: vars.colors.text.normal, color: vars.colors.text.normal,
display: "flex", display: "flex",
alignItems: "center", justifyContent: "space-between"
justifyContent: "center",
}); });
globalStyle(`${HeaderDisplayMain} > h1`, { globalStyle(`${HeaderDisplayMain} > h1`, {
@ -14,3 +12,17 @@ globalStyle(`${HeaderDisplayMain} > h1`, {
fontWeight: "bold", fontWeight: "bold",
marginRight: vars.spacing.medium, marginRight: vars.spacing.medium,
}); });
export const HeaderTitle = style({
marginLeft: vars.spacing.large,
});
export const HeaderLinks = style({
display: "flex",
alignItems: "center",
flexGrow: 1,
justifyContent: "space-between",
maxWidth: "300px",
marginRight: vars.spacing.large,
});

View File

@ -0,0 +1,5 @@
import { style } from "@vanilla-extract/css";
export const HelpContent = style({
width: '300px',
});

View File

@ -0,0 +1,57 @@
import React from "react";
import { Popover } from '@headlessui/react';
// import { useTranslation } from "react-i18next";
import {
PopoverMain,
PopoverButtonStyle,
PopoverPanelMain,
} from "../../../_headless/popover/index.css";
import {
IconFont,
SettingItem
} from "../../../../styles/shared.css";
import {
HelpContent
} from "./helpOptions.css";
export default function HelpOptions() {
return (
<Popover className={PopoverMain}>
<Popover.Button className={PopoverButtonStyle}>
<i className={[IconFont, 'fa-solid', 'fa-comments'].join(" ")}></i>
Help & Community
</Popover.Button>
<Popover.Panel className={PopoverPanelMain}>
<div className={HelpContent}>
<ul>
<li className={SettingItem}>
<a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md" target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-solid', 'fa-circle-question'].join(" ")}></i> Usual Problems and Solutions
</a>
</li>
<li className={SettingItem}>
<a href="https://discord.com/invite/u9yhsFmEkB" target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-brands', 'fa-discord'].join(" ")}></i> Discord user Community
</a>
</li>
<li className={SettingItem}>
<a href="https://old.reddit.com/r/StableDiffusionUI/" target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-brands', 'fa-reddit'].join(" ")}></i> Reddit Community
</a>
</li>
<li className={SettingItem}>
<a href="https://github.com/cmdr2/stable-diffusion-ui " target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-brands', 'fa-github'].join(" ")}></i> Source Code on Github
</a>
</li>
</ul>
</div>
</Popover.Panel>
</Popover>
);
};

View File

@ -5,11 +5,16 @@ import { KEY_CONFIG, getConfig } from "../../../api";
import StatusDisplay from "./statusDisplay"; import StatusDisplay from "./statusDisplay";
import HelpOptions from "./helpOptions";
import SystemSettings from "./systemSettings";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
HeaderDisplayMain, // @ts-expect-error HeaderDisplayMain,
} from "./headerDisplay.css.ts"; HeaderTitle,
HeaderLinks,
} from "./headerDisplay.css";
// import LanguageDropdown from "./languageDropdown"; // import LanguageDropdown from "./languageDropdown";
@ -41,11 +46,16 @@ export default function HeaderDisplay() {
return ( return (
<div className={HeaderDisplayMain}> <div className={HeaderDisplayMain}>
<h1> <div className={HeaderTitle}>
{t("title")} {version} {release}{" "} <h1>
</h1> {t("title")} {version} {release}{" "}
<StatusDisplay className="status-display"></StatusDisplay> </h1>
<StatusDisplay className="status-display"></StatusDisplay>
</div>
<div className={HeaderLinks}>
<HelpOptions></HelpOptions>
<SystemSettings></SystemSettings>
</div>
{/* <LanguageDropdown></LanguageDropdown> */} {/* <LanguageDropdown></LanguageDropdown> */}
</div> </div>
); );

View File

@ -1,8 +1,10 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState, useRef } from "react";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { healthPing, HEALTH_PING_INTERVAL } from "../../../../api"; import { healthPing, HEALTH_PING_INTERVAL } from "../../../../api";
import AudioDing from "../../../molecules/audioDing";
import { import {
StartingStatus, StartingStatus,
ErrorStatus, ErrorStatus,
@ -17,6 +19,8 @@ export default function StatusDisplay({ className }: { className?: string }) {
const [statusMessage, setStatusMessage] = useState(startingMessage); const [statusMessage, setStatusMessage] = useState(startingMessage);
const [statusClass, setStatusClass] = useState(StartingStatus); const [statusClass, setStatusClass] = useState(StartingStatus);
const dingRef = useRef<HTMLAudioElement>();
// but this will be moved to the status display when it is created // but this will be moved to the status display when it is created
const { status, data } = useQuery(["health"], healthPing, { const { status, data } = useQuery(["health"], healthPing, {
refetchInterval: HEALTH_PING_INTERVAL, refetchInterval: HEALTH_PING_INTERVAL,
@ -33,16 +37,20 @@ export default function StatusDisplay({ className }: { className?: string }) {
if (data[0] === "OK") { if (data[0] === "OK") {
setStatusMessage(successMessage); setStatusMessage(successMessage);
setStatusClass(SuccessStatus); setStatusClass(SuccessStatus);
// catch an auto play error
dingRef.current?.play().catch((e) => {
console.log('DING!')
});
} else { } else {
setStatusMessage(errorMessage); setStatusMessage(errorMessage);
setStatusClass(ErrorStatus); setStatusClass(ErrorStatus);
} }
} }
}, [status, data]); }, [status, data, dingRef]);
return ( return (
<> <>
{/* alittle hacky but joins the class names, will probably need a better css in js solution or tailwinds */} <AudioDing ref={dingRef}></AudioDing>
<p className={[statusClass, className].join(" ")}>{statusMessage}</p> <p className={[statusClass, className].join(" ")}>{statusMessage}</p>
</> </>
); );

View File

@ -1,7 +1,6 @@
import { style } from "@vanilla-extract/css"; import { style } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../../../styles/theme/index.css";
import { vars } from "../../../../styles/theme/index.css.ts";
export const StartingStatus = style({ export const StartingStatus = style({
color: vars.colors.warning, color: vars.colors.warning,

View File

@ -0,0 +1,162 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import React from "react";
import { Popover } from '@headlessui/react';
import { useTranslation } from "react-i18next";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import BetaMode from "../../../molecules/betaMode";
import {
IconFont,
SettingItem
} from "../../../../styles/shared.css";
import {
PopoverMain,
PopoverButtonStyle,
PopoverPanelMain,
} from "../../../_headless/popover/index.css";
import {
SettingContent
} from "./systemSettings.css";
// import {
// SwitchGroupMain,
// SwitchMain,
// SwitchLabel,
// SwitchEnabled,
// SwitchPill,
// } from "../../../_headless/switch/index.css";
export default function SystemSettings() {
const { t } = useTranslation();
const isUseAutoSave = useImageCreate((state) => state.isUseAutoSave());
const diskPath = useImageCreate((state) =>
state.getValueForRequestKey("save_to_disk_path")
);
const turbo = useImageCreate((state) => state.getValueForRequestKey("turbo"));
const useCpu = useImageCreate((state) =>
state.getValueForRequestKey("use_cpu")
);
const useFullPrecision = useImageCreate((state) =>
state.getValueForRequestKey("use_full_precision")
);
const isSoundEnabled = true; //useImageCreate((state) => state.isSoundEnabled());
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const toggleUseAutoSave = useImageCreate((state) => state.toggleUseAutoSave);
const toggleSoundEnabled = useImageCreate(
(state) => state.toggleSoundEnabled
);
return (
<Popover className={PopoverMain}>
<Popover.Button className={PopoverButtonStyle}>
<i className={[IconFont, 'fa-solid', 'fa-gear'].join(" ")}></i>
Settings
</Popover.Button>
<Popover.Panel className={PopoverPanelMain}>
<div className={SettingContent}>
<h4>System Settings</h4>
<ul>
<li className={SettingItem}>
<label>
<input
checked={isUseAutoSave}
onChange={(e) => toggleUseAutoSave()}
type="checkbox"
/>
{t("storage.ast")}{" "}
</label>
<label>
<input
value={diskPath}
onChange={(e) =>
setRequestOption("save_to_disk_path", e.target.value)
}
size={40}
disabled={!isUseAutoSave}
/>
<span className="visually-hidden">
Path on disk where images will be saved
</span>
</label>
</li>
<li className={SettingItem}>
<label>
<input
checked={isSoundEnabled}
onChange={(e) => toggleSoundEnabled()}
type="checkbox"
/>
{t("advanced-settings.sound")}
</label>
{/* <Switch.Group>
<Switch.Label passive> <>{t("advanced-settings.sound")}</> </Switch.Label>
<Switch checked={isSoundEnabled} onChange={toggleSoundEnabled} className={SwitchMain}>
<span
className={SwitchPill}
/>
</Switch>
</Switch.Group> */}
</li>
<li className={SettingItem}>
<label>
<input
checked={turbo}
onChange={(e) => setRequestOption("turbo", e.target.checked)}
type="checkbox"
/>
{t("advanced-settings.turbo")} {t("advanced-settings.turbo-disc")}
</label>
</li>
<li className={SettingItem}>
<label>
<input
type="checkbox"
checked={useCpu}
onChange={(e) => setRequestOption("use_cpu", e.target.checked)}
/>
{t("advanced-settings.cpu")} {t("advanced-settings.cpu-disc")}
</label>
</li>
<li className={SettingItem}>
<label>
<input
checked={useFullPrecision}
onChange={(e) =>
setRequestOption("use_full_precision", e.target.checked)
}
type="checkbox"
/>
{t("advanced-settings.gpu")} {t("advanced-settings.gpu-disc")}
</label>
</li>
<li className={SettingItem}>
<BetaMode />
</li>
</ul>
</div>
</Popover.Panel>
</Popover>
);
}

View File

@ -0,0 +1,5 @@
import { style } from "@vanilla-extract/css";
export const SettingContent = style({
width: '480px',
});

View File

@ -1,6 +1,5 @@
import { style } from "@vanilla-extract/css"; import { style } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "../../styles/theme/index.css";
import { vars } from "../../styles/theme/index.css.ts";
export const AppLayout = style({ export const AppLayout = style({
position: "relative", position: "relative",

View File

@ -5,8 +5,8 @@ import {
HeaderLayout, HeaderLayout,
CreateLayout, CreateLayout,
DisplayLayout, DisplayLayout,
FooterLayout, // @ts-expect-error FooterLayout,
} from "./home.css.ts"; } from "./home.css";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { getSaveDirectory, loadModifications } from "../../api"; import { getSaveDirectory, loadModifications } from "../../api";

View File

@ -5,6 +5,8 @@ import { devtools } from "zustand/middleware";
import { useRandomSeed } from "../utils"; import { useRandomSeed } from "../utils";
import { ImageRequest } from "../api";
export interface ImageCreationUiOptions { export interface ImageCreationUiOptions {
isUseRandomSeed: boolean; isUseRandomSeed: boolean;
isUseAutoSave: boolean; isUseAutoSave: boolean;
@ -22,62 +24,6 @@ export const SAMPLER_OPTIONS = [
'lms', 'lms',
] as const; ] as const;
export interface ImageRequest {
session_id: string;
prompt: string;
seed: number;
num_outputs: number;
num_inference_steps: number;
guidance_scale: number;
width:
| 128
| 192
| 256
| 320
| 384
| 448
| 512
| 576
| 640
| 704
| 768
| 832
| 896
| 960
| 1024;
height:
| 128
| 192
| 256
| 320
| 384
| 448
| 512
| 576
| 640
| 704
| 768
| 832
| 896
| 960
| 1024;
// allow_nsfw: boolean
turbo: boolean;
use_cpu: boolean;
use_full_precision: boolean;
save_to_disk_path: null | string;
use_face_correction: null | "GFPGANv1.3";
use_upscale: null | "RealESRGAN_x4plus" | "RealESRGAN_x4plus_anime_6B" | "";
show_only_filtered_image: boolean;
init_image: undefined | string;
prompt_strength: undefined | number;
mask: undefined | string;
sampler: typeof SAMPLER_OPTIONS[number];
stream_progress_updates: true;
stream_image_progress: boolean;
}
export interface ModifierPreview { export interface ModifierPreview {
name: string; name: string;
path: string; path: string;

View File

@ -1,7 +1,7 @@
import create from "zustand"; import create from "zustand";
import produce from "immer"; import produce from "immer";
import { ImageRequest } from "./imageCreateStore"; import { ImageRequest } from "../api";
export interface CompletedImagesType { export interface CompletedImagesType {
id?: string; id?: string;
@ -13,11 +13,9 @@ interface ImageDisplayState {
// imageOptions: Map<string, any>; // imageOptions: Map<string, any>;
images: CompletedImagesType[] images: CompletedImagesType[]
currentImage: CompletedImagesType | null currentImage: CompletedImagesType | null
updateDisplay: (ImageData: string, imageOptions: any) => void; updateDisplay: (id: string, ImageData: string, imageOptions: any) => void;
setCurrentImage: (image: CompletedImagesType) => void; setCurrentImage: (image: CompletedImagesType) => void;
clearDisplay: () => void; clearDisplay: () => void;
// getCurrentImage: () => {};
} }
export const useImageDisplay = create<ImageDisplayState>((set, get) => ({ export const useImageDisplay = create<ImageDisplayState>((set, get) => ({
@ -26,13 +24,11 @@ export const useImageDisplay = create<ImageDisplayState>((set, get) => ({
currentImage: null, currentImage: null,
// use produce to make sure we don't mutate state // use produce to make sure we don't mutate state
// imageOptions: any // imageOptions: any
updateDisplay: (ImageData: string, imageOptions) => { updateDisplay: (id: string, ImageData: string, imageOptions) => {
set( set(
produce((state) => { produce((state) => {
// options: imageOptions state.currentImage = { id, display: ImageData, info: imageOptions };
// state.currentImage = { display: ImageData, imageOptions }; state.images.unshift({ id, data: ImageData, info: imageOptions });
// imageOptions
state.images.unshift({ data: ImageData, info: imageOptions });
state.currentImage = state.images[0]; state.currentImage = state.images[0];
}) })
); );

View File

@ -2,7 +2,7 @@ import create from "zustand";
import produce from "immer"; import produce from "immer";
import { useRandomSeed } from "../utils"; import { useRandomSeed } from "../utils";
import { ImageRequest } from "./imageCreateStore"; import { ImageRequest } from "../api";
interface QueueItem { interface QueueItem {
id?: string; id?: string;

View File

@ -1,6 +1,5 @@
import { globalStyle } from "@vanilla-extract/css"; import { globalStyle } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "./theme/index.css";
import { vars } from "./theme/index.css.ts";
// baisc body style // baisc body style
globalStyle("body", { globalStyle("body", {
@ -28,31 +27,40 @@ globalStyle(`button`, {
fontSize: vars.fonts.sizes.Body, fontSize: vars.fonts.sizes.Body,
}); });
globalStyle(`.visually-hidden`, {
visibility: "hidden",
position: "absolute",
});
/** RESETS */ /** RESETS */
globalStyle(`p, h1, h2, h3, h4, h5, h6, ul`, { globalStyle(`h1, h2, h3, h4, h5, h6, p, label, ul, textarea`, {
margin: 0, margin: 0,
padding: 0,
fontFamily: vars.fonts.body,
}); });
globalStyle(`h3`, { globalStyle(`h3`, {
fontSize: vars.fonts.sizes.Subheadline, fontSize: vars.fonts.sizes.Subheadline,
fontFamily: vars.fonts.body, marginBottom: vars.spacing.small,
}); });
globalStyle(`h4, h5`, { globalStyle(`h4, h5`, {
fontSize: vars.fonts.sizes.SubSubheadline, fontSize: vars.fonts.sizes.SubSubheadline,
fontFamily: vars.fonts.body, marginBottom: vars.spacing.medium,
}); });
globalStyle(`p, label`, { globalStyle(`p, label`, {
fontSize: vars.fonts.sizes.Body, fontSize: vars.fonts.sizes.Body,
fontFamily: vars.fonts.body,
}); });
globalStyle(`textarea`, { globalStyle(`textarea`, {
margin: 0,
padding: 0, padding: 0,
border: "none", border: "none",
fontSize: vars.fonts.sizes.Body, fontSize: vars.fonts.sizes.Body,
fontWeight: "bold", fontWeight: "bold",
fontFamily: vars.fonts.body,
}); });
globalStyle(`a`, {
color: vars.colors.link,
textDecoration: "none",
});

View File

@ -1,6 +1,5 @@
import { style, globalStyle } from "@vanilla-extract/css"; import { style, globalStyle } from "@vanilla-extract/css";
// @ts-expect-error import { vars } from "./theme/index.css";
import { vars } from "./theme/index.css.ts";
export const PanelBox = style({ export const PanelBox = style({
background: vars.colors.backgroundAlt, background: vars.colors.backgroundAlt,
@ -24,6 +23,60 @@ globalStyle(`${PanelBox} .panel-box-toggle-btn`, {
padding: "0", padding: "0",
}); });
//TODO this should probably just be for all li elements
export const SettingItem = style({ export const SettingItem = style({
marginBottom: vars.spacing.medium, marginBottom: vars.spacing.medium,
selectors: {
"&:last-of-type": {
marginBottom: vars.spacing.none,
},
},
});
export const IconFont = style({
// reliant on font-awesome cdn
fontFamily: "Font Awesome 6 Free"
});
export const MenuButton = style({
display: "block",
width: "100%",
textAlign: "left",
backgroundColor: "transparent",
color: vars.colors.text.normal,
border: "0 none",
cursor: "pointer",
padding: "0",
marginBottom: vars.spacing.medium,
});
globalStyle(`${MenuButton}> h4`, {
color: "#e7ba71",
});
export const BrandedButton = style({
backgroundColor: vars.colors.brand,
fontSize: vars.fonts.sizes.Subheadline,
fontWeight: "bold",
color: vars.colors.text.normal,
padding: vars.spacing.small,
borderRadius: vars.trim.smallBorderRadius,
":hover": {
backgroundColor: vars.colors.brandHover,
},
":active": {
backgroundColor: vars.colors.brandActive,
},
":disabled": {
backgroundColor: vars.colors.brandDimmed,
color: vars.colors.text.dimmed,
},
}); });

View File

@ -11,7 +11,6 @@ import {
* *
* Lots of these arent used yet, but once they are defined and useable then they can be set. * Lots of these arent used yet, but once they are defined and useable then they can be set.
*/ */
const colors = createThemeContract({ const colors = createThemeContract({
brand: null, brand: null,
brandDimmed: null, brandDimmed: null,
@ -33,6 +32,8 @@ const colors = createThemeContract({
backgroundAccent: null, backgroundAccent: null,
backgroundAlt: null, backgroundAlt: null,
backgroundAltAccent: null, backgroundAltAccent: null,
backgroundDark: null,
backgroundDarkAccent: null,
text: { text: {
normal: null, normal: null,
@ -66,6 +67,7 @@ const app = createGlobalTheme(":root", {
fonts: { fonts: {
body: "Arial, Helvetica, sans-serif;", body: "Arial, Helvetica, sans-serif;",
// IconFont is a shared class for now
sizes: { sizes: {
Title: "2em", Title: "2em",
Headline: "1.5em", Headline: "1.5em",
@ -99,9 +101,13 @@ export const darkTheme = createTheme(colors, {
background: "#202124", // dark grey background: "#202124", // dark grey
backgroundAccent: " #383838", // lighter grey backgroundAccent: " #383838", // lighter grey
backgroundAlt: "#2c2d30", // med grey backgroundAlt: "#2c2d30", // med grey
backgroundAltAccent: "#383838", // lighter grey backgroundAltAccent: "#383838", // lighter grey
backgroundDark: "#121213", // darker grey
backgroundDarkAccent: "#383838", // lighter grey
text: { text: {
normal: "#ffffff", // white normal: "#ffffff", // white
dimmed: "#d1d5db", // off white dimmed: "#d1d5db", // off white
@ -141,6 +147,9 @@ export const lightTheme = createTheme(colors, {
backgroundAccent: "#EFF6FF", backgroundAccent: "#EFF6FF",
backgroundAlt: "#EFF6FF", backgroundAlt: "#EFF6FF",
backgroundAltAccent: "#EFF6FF", backgroundAltAccent: "#EFF6FF",
backgroundDark: "#EFF6FF",
backgroundDarkAccent: "#EFF6FF",
text: { text: {
normal: "#1F2937", normal: "#1F2937",
dimmed: "#6B7280", dimmed: "#6B7280",

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,10 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/media/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/media/favicon-32x32.png" sizes="32x32">
<meta name="color-scheme" content="dark light" /> <meta name="color-scheme" content="dark light" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<title>Stable Diffusion UI</title> <title>Stable Diffusion UI</title>
<script type="module" crossorigin src="/index.js"></script> <script type="module" crossorigin src="/index.js"></script>
<link rel="stylesheet" href="/index.css"> <link rel="stylesheet" href="/index.css">

File diff suppressed because one or more lines are too long