From 4f068a2920a053648a12f029bb51b31aaacd0ce6 Mon Sep 17 00:00:00 2001 From: wil Date: Tue, 17 Sep 2024 16:42:13 -0400 Subject: [PATCH] Feature/ AI Stylize 2 (#18) * setup ai stylize dialog to manage more properties * integrated switches and sliders * all options for ai stylize is available except for IPAdapter --------- Co-authored-by: wil --- src/KonvaApp/Engine.ts | 11 +- .../SignaledToolbarMain.tsx | 7 + .../DialogAiStylize/DialogAiStylize.tsx | 137 +++++++++++++ .../SubPanelAdvance/StyleOptionSwitches.tsx | 180 ++++++++++++++++++ .../SubPanelAdvance/StyleStrengthSlider.tsx | 40 ++++ .../SubPanelAdvance/SubPanelAdvance.tsx | 44 +++++ .../DialogAiStylize/SubPanelAdvance/index.tsx | 1 + .../{ => SubPanelBasic}/ArtStyleItem.tsx | 28 +-- .../{ => SubPanelBasic}/ArtStyleSelector.tsx | 9 +- .../{ => SubPanelBasic}/Prompts.tsx | 4 +- .../SubPanelBasic/SubPanelBaisc.tsx | 55 ++++++ .../DialogAiStylize/SubPanelBasic/index.tsx | 1 + .../features/DialogAiStylize/enums.ts | 5 + .../features/DialogAiStylize/index.tsx | 111 +---------- .../features/DialogAiStylize/utilities.ts | 30 ++- src/components/ui/Button.tsx | 5 +- src/components/ui/InputNumber.tsx | 81 ++++++++ src/components/ui/Slider.tsx | 131 +++++++++++++ src/components/ui/index.ts | 2 + src/signals/uiAccess/loadingBar.ts | 7 +- .../uiAccess/toolbarMain/loadingBar.ts | 8 +- src/signals/uiEvents/aiStylize.ts | 52 +++-- src/signals/uiEvents/index.ts | 6 +- 23 files changed, 788 insertions(+), 167 deletions(-) create mode 100644 src/components/features/DialogAiStylize/DialogAiStylize.tsx create mode 100644 src/components/features/DialogAiStylize/SubPanelAdvance/StyleOptionSwitches.tsx create mode 100644 src/components/features/DialogAiStylize/SubPanelAdvance/StyleStrengthSlider.tsx create mode 100644 src/components/features/DialogAiStylize/SubPanelAdvance/SubPanelAdvance.tsx create mode 100644 src/components/features/DialogAiStylize/SubPanelAdvance/index.tsx rename src/components/features/DialogAiStylize/{ => SubPanelBasic}/ArtStyleItem.tsx (76%) rename src/components/features/DialogAiStylize/{ => SubPanelBasic}/ArtStyleSelector.tsx (76%) rename src/components/features/DialogAiStylize/{ => SubPanelBasic}/Prompts.tsx (97%) create mode 100644 src/components/features/DialogAiStylize/SubPanelBasic/SubPanelBaisc.tsx create mode 100644 src/components/features/DialogAiStylize/SubPanelBasic/index.tsx create mode 100644 src/components/ui/InputNumber.tsx create mode 100644 src/components/ui/Slider.tsx diff --git a/src/KonvaApp/Engine.ts b/src/KonvaApp/Engine.ts index c763cb6a31..9e0464d06d 100644 --- a/src/KonvaApp/Engine.ts +++ b/src/KonvaApp/Engine.ts @@ -15,7 +15,6 @@ import { ResponseType } from "./WorkerPrimitives/SharedWorkerBase"; import * as ort from "onnxruntime-web"; - export class Engine { private canvasReference: HTMLDivElement; private stage: Konva.Stage; @@ -168,8 +167,14 @@ export class Engine { this.addVideo(video.url); }); - uiEvents.onRequestAiStylize((data) => { + uiEvents.aiStylize.onRequest((data) => { console.log("Engine heard AI Stylize request: ", data); + console.log( + "you can also get initial values via uiEvents.aiStylize.getInitialValues", + ); + console.log( + "if you missed the event, the current values can be accessed via uiEvents.aiStylize.getCurrentValues", + ); }); // TODO: You may listen to all the image toolbar events here uiEvents.toolbarImage.MOVE.onClick(() => { @@ -185,7 +190,7 @@ export class Engine { //this.onRenderingSystemReceived(undefined); }); - uiEvents.onRequestAiStylize(async () => { + uiEvents.aiStylize.onRequest(async () => { // uiAccess.toolbarMain.changeButtonState( // ToolbarMainButtonNames.AI_STYLIZE, // { disabled: true }, diff --git a/src/KonvaRootComponent/SignaledToolbarMain.tsx b/src/KonvaRootComponent/SignaledToolbarMain.tsx index 8c84150171..5d1a94e450 100644 --- a/src/KonvaRootComponent/SignaledToolbarMain.tsx +++ b/src/KonvaRootComponent/SignaledToolbarMain.tsx @@ -99,6 +99,13 @@ export const SignaledToolbarMain = () => { /> { + const { selectedArtStyle: artstyle, ...rest } = data; + dispatchUiEvents.aiStylize.dispatchRequest({ + artstyle, + ...rest, + }); + }} closeCallback={closeAll} /> diff --git a/src/components/features/DialogAiStylize/DialogAiStylize.tsx b/src/components/features/DialogAiStylize/DialogAiStylize.tsx new file mode 100644 index 0000000000..a6100bdd84 --- /dev/null +++ b/src/components/features/DialogAiStylize/DialogAiStylize.tsx @@ -0,0 +1,137 @@ +import { useCallback, useEffect, useState } from "react"; +import { twMerge } from "tailwind-merge"; +import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react"; +import { + faWandSparkles, + faChevronLeft, +} from "@fortawesome/pro-solid-svg-icons"; + +import { Button } from "~/components/ui"; +import { + dialogBackgroundStyles, + paperWrapperStyles, +} from "~/components/styles"; + +import { ArtStyleNames, SubPanelNames } from "./enums"; +import { + generateRandomTextPositive, + generateRandomTextNegative, + AIStylizeProps, + initialValues, +} from "./utilities"; + +import { SubPanelBasic } from "./SubPanelBasic"; +import { SubPanelAdvance } from "./SubPanelAdvance"; + +export const DialogAiStylize = ({ + isOpen, + closeCallback, + onRequestAIStylize, +}: { + isOpen: boolean; + closeCallback: () => void; + onRequestAIStylize: (data: AIStylizeProps) => void; +}) => { + const [state, setState] = useState< + AIStylizeProps & { panelState: SubPanelNames } + >({ + ...initialValues, + panelState: SubPanelNames.BASIC, + }); + const { panelState, ...aiStylizeProps } = state; + const { selectedArtStyle, positivePrompt, negativePrompt } = aiStylizeProps; + + function handleGenerate() { + onRequestAIStylize(aiStylizeProps); + closeCallback(); + } + const onChangePanel = useCallback((newP: SubPanelNames) => { + setState((curr) => ({ ...curr, panelState: newP })); + }, []); + const setStylizeOptions = useCallback( + (newOptions: Partial) => { + console.log("setStylizeOptions", newOptions); + setState((curr) => ({ ...curr, ...newOptions })); + }, + [], + ); + const onSelectedArtStyle = useCallback((newArtstyle: ArtStyleNames) => { + setStylizeOptions({ + selectedArtStyle: newArtstyle, + positivePrompt: generateRandomTextPositive(newArtstyle), + negativePrompt: generateRandomTextNegative(newArtstyle), + }); + }, []); + const onChangeNegativePrompt = useCallback((newPrompt: string) => { + setStylizeOptions({ negativePrompt: newPrompt }); + }, []); + const onChangePositivePrompt = useCallback((newPrompt: string) => { + setStylizeOptions({ positivePrompt: newPrompt }); + }, []); + + useEffect(() => { + if (!isOpen) { + setState({ ...initialValues, panelState: SubPanelNames.BASIC }); + } + }, [isOpen]); + + return ( + +
+ + {panelState === SubPanelNames.BASIC && ( + <> + + Use AI to Stylize{" "} + + + + )} + {panelState === SubPanelNames.ADVANCED && ( + <> + + + + )} +
+ + + +
+
+
+
+ ); +}; diff --git a/src/components/features/DialogAiStylize/SubPanelAdvance/StyleOptionSwitches.tsx b/src/components/features/DialogAiStylize/SubPanelAdvance/StyleOptionSwitches.tsx new file mode 100644 index 0000000000..3869ef1fe6 --- /dev/null +++ b/src/components/features/DialogAiStylize/SubPanelAdvance/StyleOptionSwitches.tsx @@ -0,0 +1,180 @@ +import { Field, Label, Switch } from "@headlessui/react"; +import { twMerge } from "tailwind-merge"; + +import { AIStylizeProps } from "../utilities"; + +export const StyleOptionSwitches = ({ + faceDetail, + upscale, + lipSync, + cinematic, + enginePreProcessing, + onStylizeOptionsChanged, +}: { + cinematic: boolean; + enginePreProcessing: boolean; + faceDetail: boolean; + lipSync: boolean; + upscale: boolean; + onStylizeOptionsChanged: (newOptions: Partial) => void; +}) => { + const handleCinematicChange = () => { + onStylizeOptionsChanged({ + cinematic: !cinematic, + upscale: !cinematic === true ? false : upscale, + }); + }; + + const enginePreProcessingChange = () => { + onStylizeOptionsChanged({ + enginePreProcessing: !enginePreProcessing, + }); + }; + + const handleUpscaleChange = () => { + onStylizeOptionsChanged({ + upscale: !upscale, + cinematic: !upscale === true ? false : cinematic, + }); + }; + + const handleLipsyncChange = () => { + onStylizeOptionsChanged({ + lipSync: !lipSync, + }); + }; + + const handleFaceDetailerChange = () => { + onStylizeOptionsChanged({ + faceDetail: !faceDetail, + }); + }; + + const switchContainerBaseStyle = + "relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-0 focus:ring-indigo-500 focus:ring-offset-0"; + const switchContainerStateStyle = (state: boolean) => + state ? "bg-primary hover:bg-primary-400" : "bg-gray-500 hover:bg-gray-400"; + const switchButtonBaseStyle = + "inline-block h-4 w-4 transform rounded-full bg-white transition-transform"; + const switchButtonStateStyle = (state: boolean) => + state ? "translate-x-6" : "translate-x-1"; + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; diff --git a/src/components/features/DialogAiStylize/SubPanelAdvance/StyleStrengthSlider.tsx b/src/components/features/DialogAiStylize/SubPanelAdvance/StyleStrengthSlider.tsx new file mode 100644 index 0000000000..adb46aabb2 --- /dev/null +++ b/src/components/features/DialogAiStylize/SubPanelAdvance/StyleStrengthSlider.tsx @@ -0,0 +1,40 @@ +import { InputNumber, Slider } from "~/components/ui"; +import { AIStylizeProps } from "../utilities"; + +export function StyleStrengthSlider({ + styleStrength, + onStylizeOptionsChanged, +}: { + styleStrength: number; + onStylizeOptionsChanged: (newOptions: Partial) => void; +}) { + const onChanged = (value: number | number[]) => { + onStylizeOptionsChanged({ + styleStrength: (value as number) / 100, + }); + }; + + return ( +
+ +

+ (The higher the value the more the style will be applied, the lower the + value the closer to source.) +

+
+ + +
+
+ ); +} diff --git a/src/components/features/DialogAiStylize/SubPanelAdvance/SubPanelAdvance.tsx b/src/components/features/DialogAiStylize/SubPanelAdvance/SubPanelAdvance.tsx new file mode 100644 index 0000000000..308d8a695f --- /dev/null +++ b/src/components/features/DialogAiStylize/SubPanelAdvance/SubPanelAdvance.tsx @@ -0,0 +1,44 @@ +import { StyleOptionSwitches } from "./StyleOptionSwitches"; +import { AIStylizeProps } from "../utilities"; +import { StyleStrengthSlider } from "./StyleStrengthSlider"; + +export const SubPanelAdvance = ({ + aiStylizeProps, + onStylizeOptionsChanged, +}: { + aiStylizeProps: AIStylizeProps; + onStylizeOptionsChanged: (newOptions: Partial) => void; +}) => { + const { + cinematic, + enginePreProcessing, + faceDetail, + lipSync, + upscale, + styleStrength, + } = aiStylizeProps; + + return ( +
+
+
IP Adapter
+
+
+

Advanced Options

+ +
+ +
+
+ ); +}; diff --git a/src/components/features/DialogAiStylize/SubPanelAdvance/index.tsx b/src/components/features/DialogAiStylize/SubPanelAdvance/index.tsx new file mode 100644 index 0000000000..76e8f0c2a9 --- /dev/null +++ b/src/components/features/DialogAiStylize/SubPanelAdvance/index.tsx @@ -0,0 +1 @@ +export * from "./SubPanelAdvance"; diff --git a/src/components/features/DialogAiStylize/ArtStyleItem.tsx b/src/components/features/DialogAiStylize/SubPanelBasic/ArtStyleItem.tsx similarity index 76% rename from src/components/features/DialogAiStylize/ArtStyleItem.tsx rename to src/components/features/DialogAiStylize/SubPanelBasic/ArtStyleItem.tsx index 3d1d1eb8a7..b2f9951a6f 100644 --- a/src/components/features/DialogAiStylize/ArtStyleItem.tsx +++ b/src/components/features/DialogAiStylize/SubPanelBasic/ArtStyleItem.tsx @@ -1,6 +1,7 @@ import { ImgHTMLAttributes, useEffect, useState } from "react"; import { twMerge } from "tailwind-merge"; -import { ArtStyleNames } from "./enums"; +import { ArtStyleNames } from "../enums"; +import { transitionTimingStyles } from "~/components/styles"; interface ArtStyleItemProps extends ImgHTMLAttributes { label: string; @@ -18,8 +19,6 @@ export const ArtStyleItem = ({ defaultImg = "/resources/placeholders/style_placeholder.png", src = defaultImg, onSelected, - // width, - // height, className, ...imgProps }: ArtStyleItemProps) => { @@ -39,18 +38,13 @@ export const ArtStyleItem = ({ return ( + + + ); +}; diff --git a/src/components/features/DialogAiStylize/SubPanelBasic/index.tsx b/src/components/features/DialogAiStylize/SubPanelBasic/index.tsx new file mode 100644 index 0000000000..738035fe20 --- /dev/null +++ b/src/components/features/DialogAiStylize/SubPanelBasic/index.tsx @@ -0,0 +1 @@ +export * from "./SubPanelBaisc"; diff --git a/src/components/features/DialogAiStylize/enums.ts b/src/components/features/DialogAiStylize/enums.ts index a6f61b9940..1cabe075d2 100644 --- a/src/components/features/DialogAiStylize/enums.ts +++ b/src/components/features/DialogAiStylize/enums.ts @@ -39,3 +39,8 @@ export enum Visibility { Hidden = "hidden", Private = "private", } + +export enum SubPanelNames { + BASIC = "basic", + ADVANCED = "advanced", +} diff --git a/src/components/features/DialogAiStylize/index.tsx b/src/components/features/DialogAiStylize/index.tsx index eb6629af60..de3a1cdb82 100644 --- a/src/components/features/DialogAiStylize/index.tsx +++ b/src/components/features/DialogAiStylize/index.tsx @@ -1,110 +1 @@ -import { useCallback, useState } from "react"; -import { twMerge } from "tailwind-merge"; -import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react"; -import { faWandSparkles } from "@fortawesome/pro-solid-svg-icons"; - -import { Button } from "~/components/ui"; -import { - dialogBackgroundStyles, - paperWrapperStyles, -} from "~/components/styles"; - -import { ArtStyleNames } from "./enums"; -import { - generateRandomTextPositive, - generateRandomTextNegative, -} from "./utilities"; - -import { Prompts } from "./Prompts"; -import { ArtStyleSelector } from "./ArtStyleSelector"; -import { dispatchUiEvents } from "~/signals"; - -export const DialogAiStylize = ({ - isOpen, - closeCallback, -}: { - isOpen: boolean; - closeCallback: () => void; -}) => { - const [{ selectedArtStyle, positivePrompt, negativePrompt }, setState] = - useState<{ - selectedArtStyle: ArtStyleNames; - positivePrompt: string; - negativePrompt: string; - }>({ - selectedArtStyle: ArtStyleNames.Anime2DFlat, - positivePrompt: generateRandomTextPositive(ArtStyleNames.Anime2DFlat), - negativePrompt: generateRandomTextNegative(ArtStyleNames.Anime2DFlat), - }); - - function handleClose() { - closeCallback(); - } - - function handleGenerate() { - dispatchUiEvents.requestAiStylize({ - artstyle: selectedArtStyle, - positivePrompt: positivePrompt, - negativePrompt: negativePrompt, - }); - handleClose(); - } - const onSelectedArtStyle = useCallback((newArtstyle: ArtStyleNames) => { - setState({ - selectedArtStyle: newArtstyle, - positivePrompt: generateRandomTextPositive(newArtstyle), - negativePrompt: generateRandomTextNegative(newArtstyle), - }); - }, []); - const onChangeNegativePrompt = useCallback((newPrompt: string) => { - setState((curr) => ({ ...curr, negativePrompt: newPrompt })); - }, []); - const onChangePositivePrompt = useCallback((newPrompt: string) => { - setState((curr) => ({ ...curr, positivePrompt: newPrompt })); - }, []); - - return ( - -
- - Use AI to Stylize -
-
-

Pick a Style

- -
-
- -
-
-
- - -
-
-
-
- ); -}; +export * from "./DialogAiStylize"; diff --git a/src/components/features/DialogAiStylize/utilities.ts b/src/components/features/DialogAiStylize/utilities.ts index ae196c60b4..fbf00398da 100644 --- a/src/components/features/DialogAiStylize/utilities.ts +++ b/src/components/features/DialogAiStylize/utilities.ts @@ -4,18 +4,40 @@ import { RandomTextsNegative, } from "./data/randomTextList"; -export const generateRandomTextPositive = (artStyleName: ArtStyleNames) => { +export type AIStylizeProps = { + selectedArtStyle: ArtStyleNames; + positivePrompt: string; + negativePrompt: string; + cinematic: boolean; + enginePreProcessing: boolean; + faceDetail: boolean; + lipSync: boolean; + upscale: boolean; + styleStrength: number; +}; +export const initialValues = { + selectedArtStyle: ArtStyleNames.Anime2DFlat, + positivePrompt: generateRandomTextPositive(ArtStyleNames.Anime2DFlat), + negativePrompt: generateRandomTextNegative(ArtStyleNames.Anime2DFlat), + cinematic: false, + enginePreProcessing: false, + faceDetail: false, + lipSync: false, + upscale: false, + styleStrength: 0.8, +}; +export function generateRandomTextPositive(artStyleName: ArtStyleNames) { const randomIndex = Math.floor( Math.random() * RandomTextsPositive[artStyleName].length, ); const randomText = RandomTextsPositive[artStyleName][randomIndex]; return randomText; -}; +} -export const generateRandomTextNegative = (artStyleName: ArtStyleNames) => { +export function generateRandomTextNegative(artStyleName: ArtStyleNames) { const randomIndex = Math.floor( Math.random() * RandomTextsNegative[artStyleName].length, ); const randomText = RandomTextsNegative[artStyleName][randomIndex]; return randomText; -}; +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index 41fa7befa8..c32650a972 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -9,7 +9,7 @@ export interface ButtonProps extends ButtonHTMLAttributes { iconClassName?: string; iconFlip?: boolean; htmlFor?: string; - variant?: "primary" | "secondary" | "action"; + variant?: "primary" | "secondary" | "tertiary" | "action"; loading?: boolean; } @@ -27,6 +27,9 @@ export const Button = ({ }: ButtonProps) => { function getVariantClassNames(variant: string) { switch (variant) { + case "tertiary": { + return "bg-tertiary hover:bg-tertiary-400 text-white focus-visible:outline-tertiary-600"; + } case "secondary": { return "bg-secondary hover:bg-secondary-900 text-white focus-visible:outline-secondary"; } diff --git a/src/components/ui/InputNumber.tsx b/src/components/ui/InputNumber.tsx new file mode 100644 index 0000000000..7451e437a5 --- /dev/null +++ b/src/components/ui/InputNumber.tsx @@ -0,0 +1,81 @@ +import React, { useEffect, useState } from "react"; +import { twMerge } from "tailwind-merge"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronDown, faChevronUp } from "@fortawesome/pro-solid-svg-icons"; + +import { Input } from "./Input"; + +interface NumberInputProps { + value: number; + onChange: (value: number) => void; +} + +export const InputNumber = ({ value, onChange }: NumberInputProps) => { + const [inputValue, setInputValue] = useState(value.toString()); + + useEffect(() => { + setInputValue(Math.round(value * 100) / 100 + ""); + }, [value]); + + const handleChange = (event: React.ChangeEvent) => { + const newValue = event.target.value; + if ( + newValue === "" || + (!isNaN(Number(newValue)) && Number(newValue) <= 100) + ) { + setInputValue(newValue); + const numericValue = parseFloat(newValue); + if (newValue !== "") { + onChange(Math.round(numericValue * 100) / 100); + } + } + }; + + const handleBlur = () => { + if (inputValue === "") { + setInputValue(Math.round(value * 100) / 100 + ""); + } + }; + + const handleIncrement = () => { + const newValue = Math.min(Number(inputValue) + 1, 100); + setInputValue(Math.round(newValue * 100) / 100 + ""); + onChange(Math.round(newValue * 100) / 100); + }; + + const handleDecrement = () => { + const newValue = Math.max(Number(inputValue) - 1, 0); + setInputValue(Math.round(newValue * 100) / 100 + ""); + onChange(Math.round(newValue * 100) / 100); + }; + + const incrementButtonStyle = + "flex h-4 w-6 items-center justify-center text-xs text-white bg-ui-border hover:bg-secondary-500 outline-none focus:outline-none transition-all active:outline-none"; + + return ( +
+ + +
+ + +
+
+ ); +}; diff --git a/src/components/ui/Slider.tsx b/src/components/ui/Slider.tsx new file mode 100644 index 0000000000..5b756b81dc --- /dev/null +++ b/src/components/ui/Slider.tsx @@ -0,0 +1,131 @@ +import { Transition } from "@headlessui/react"; +import React, { useState, useRef, useEffect, ReactNode } from "react"; +import { twMerge } from "tailwind-merge"; + +interface SliderProps { + min: number; + max: number; + value: number; + onChange: (value: number) => void; + step: number; + className?: string; + showTooltip?: boolean; + tooltipContent?: ReactNode; + suffix?: string; +} + +export const Slider = ({ + min, + max, + value, + onChange, + step, + className, + showTooltip, + tooltipContent, + suffix, +}: SliderProps) => { + const [localValue, setLocalValue] = useState(value); + const sliderRef = useRef(null); + const [isDragging, setIsDragging] = useState(false); + const [showHandleTooltip, setShowHandleTooltip] = useState(false); + + useEffect(() => { + setLocalValue(value); + }, [value]); + + const updatePosition = (x: number) => { + if (!sliderRef.current) return; + const rect = sliderRef.current.getBoundingClientRect(); + const fraction = (x - rect.left) / rect.width; + const newValue = fraction * (max - min) + min; + const roundedValue = Math.round(newValue / step) * step; + const clampedValue = Math.min(Math.max(roundedValue, min), max); + setLocalValue(clampedValue); + onChange(clampedValue); + }; + + const handleMouseDown = (e: React.MouseEvent) => { + e.preventDefault(); + setIsDragging(true); + setShowHandleTooltip(true); + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + updatePosition(e.clientX); + }; + + const handleMouseMove = (e: MouseEvent) => { + e.preventDefault(); + updatePosition(e.clientX); + }; + + const handleMouseUp = (e: MouseEvent) => { + e.preventDefault(); + setIsDragging(false); + setShowHandleTooltip(false); + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + + const handleMouseEnter = () => { + if (!isDragging) { + setShowHandleTooltip(true); + } + }; + + const handleMouseLeave = () => { + if (!isDragging) { + setShowHandleTooltip(false); + } + }; + + const percentage = ((localValue - min) / (max - min)) * 100; + const displayValue = + localValue.toFixed(step.toString().split(".")[1]?.length || 0) + + (suffix || ""); + + return ( +
+
+
+ {showTooltip && ( + +
+ {tooltipContent ? tooltipContent : displayValue} +
+
+ )} +
+ ); +}; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index e31ee1be40..8c75db6f91 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -1,9 +1,11 @@ export * from "./Button"; export * from "./Input"; +export * from "./InputNumber"; export * from "./LoadingBar"; export * from "./LoadingSpinner"; export * from "./PanelBottom"; export * from "./PanelRight"; +export * from "./Slider"; export * from "./Spinner"; export * from "./TextArea"; export * from "./Tooltip"; diff --git a/src/signals/uiAccess/loadingBar.ts b/src/signals/uiAccess/loadingBar.ts index 70ecc62e56..585b7e6285 100644 --- a/src/signals/uiAccess/loadingBar.ts +++ b/src/signals/uiAccess/loadingBar.ts @@ -53,7 +53,7 @@ export const loadingBar = { status, }; }, - show(props: Omit) { + show(props?: Omit) { if (loadingBarSignal.value.isShowing) { if (import.meta.env.DEV) { console.warn( @@ -63,8 +63,11 @@ export const loadingBar = { } return; } + const mergedProps = props + ? { ...props, ...loadingBarSignal.value } + : loadingBarSignal.value; loadingBarSignal.value = { - ...props, + ...mergedProps, isShowing: true, }; }, diff --git a/src/signals/uiAccess/toolbarMain/loadingBar.ts b/src/signals/uiAccess/toolbarMain/loadingBar.ts index f55eb5623a..3250fde95c 100644 --- a/src/signals/uiAccess/toolbarMain/loadingBar.ts +++ b/src/signals/uiAccess/toolbarMain/loadingBar.ts @@ -38,10 +38,12 @@ export const loadingBar = { status, }; }, - show: (props: Omit) => { + show: (props?: Omit) => { + const mergedProps = props + ? { ...props, ...loadingBarSignal.value } + : loadingBarSignal.value; loadingBarSignal.value = { - ...loadingBarSignal.value, - ...props, + ...mergedProps, isShowing: true, }; }, diff --git a/src/signals/uiEvents/aiStylize.ts b/src/signals/uiEvents/aiStylize.ts index 55a75e1421..aa5bd68ae5 100644 --- a/src/signals/uiEvents/aiStylize.ts +++ b/src/signals/uiEvents/aiStylize.ts @@ -1,31 +1,55 @@ import { signal, effect } from "@preact/signals-react"; +import { + RandomTextsNegative, + RandomTextsPositive, +} from "~/components/features/DialogAiStylize/data/randomTextList"; import { ArtStyleNames } from "~/components/features/DialogAiStylize/enums"; -const stagedAiStylizeRequest = signal<{ +interface AiStylizeSingalType { artstyle: ArtStyleNames; positivePrompt: string; negativePrompt: string; -} | null>(null); + cinematic: boolean; + enginePreProcessing: boolean; + faceDetail: boolean; + lipSync: boolean; + upscale: boolean; + styleStrength: number; +} +const initialValues = { + artstyle: ArtStyleNames.Anime2DFlat, + positivePrompt: RandomTextsPositive[ArtStyleNames.Anime2DFlat][0], + negativePrompt: RandomTextsNegative[ArtStyleNames.Anime2DFlat][0], + cinematic: false, + enginePreProcessing: false, + faceDetail: false, + lipSync: false, + upscale: false, + styleStrength: 0.8, +}; +const stagedAiStylizeRequest = signal(initialValues); -export const requestAiStylize = (data: { - artstyle: ArtStyleNames; - positivePrompt: string; - negativePrompt: string; -}) => { +const dispatchRequest = (data: AiStylizeSingalType) => { stagedAiStylizeRequest.value = data; }; -export const onRequestAiStylize = ( - callback: (data: { - artstyle: ArtStyleNames; - positivePrompt: string; - negativePrompt: string; - }) => void, -) => { +const onRequest = (callback: (data: AiStylizeSingalType) => void) => { effect(() => { if (stagedAiStylizeRequest.value) { callback(stagedAiStylizeRequest.value); } }); }; + +const getCurrentValues = stagedAiStylizeRequest.value; +const getInitialValues = initialValues; + +export const aiStylizeDispatchers = { + dispatchRequest, +}; +export const aiStylizeEvents = { + onRequest, + getCurrentValues, + getInitialValues, +}; diff --git a/src/signals/uiEvents/index.ts b/src/signals/uiEvents/index.ts index dbe1ccc10b..9b41bd6149 100644 --- a/src/signals/uiEvents/index.ts +++ b/src/signals/uiEvents/index.ts @@ -22,11 +22,11 @@ import { dispatcher as buttonRetryDispatcher, eventsHandler as buttonRetryEvent, } from "./buttonRetry"; -import { requestAiStylize, onRequestAiStylize } from "./aiStylize"; +import { aiStylizeDispatchers, aiStylizeEvents } from "./aiStylize"; export const uiEvents = { ...addMediaEvents, - onRequestAiStylize, + aiStylize: aiStylizeEvents, buttonRetry: buttonRetryEvent, buttonTest: buttonTestEvent, toolbarImage: toolbarImageEvents, @@ -35,7 +35,7 @@ export const uiEvents = { }; export const dispatchUiEvents = { ...addMediaDispatchers, - requestAiStylize, + aiStylize: aiStylizeDispatchers, buttonRetry: buttonRetryDispatcher, buttonTest: buttonTestDispatcher, toolbarImage: toolbarImageDispatchers,