plugins add dropdown menu; realesrgan support 2x 4x
This commit is contained in:
@@ -153,12 +153,16 @@ export function modelDownloaded(name: string) {
|
||||
export async function runPlugin(
|
||||
name: string,
|
||||
imageFile: File,
|
||||
upscale?: number,
|
||||
maskFile?: File | null,
|
||||
clicks?: number[][]
|
||||
) {
|
||||
const fd = new FormData()
|
||||
fd.append('name', name)
|
||||
fd.append('image', imageFile)
|
||||
if (upscale) {
|
||||
fd.append('upscale', upscale.toString())
|
||||
}
|
||||
if (clicks) {
|
||||
fd.append('clicks', JSON.stringify(clicks))
|
||||
}
|
||||
|
||||
@@ -562,7 +562,7 @@ export default function Editor() {
|
||||
})
|
||||
|
||||
const runRenderablePlugin = useCallback(
|
||||
async (name: string) => {
|
||||
async (name: string, data?: any) => {
|
||||
if (isProcessing) {
|
||||
return
|
||||
}
|
||||
@@ -570,7 +570,7 @@ export default function Editor() {
|
||||
// TODO 要不要加 undoCurrentLine??
|
||||
setIsPluginRunning(true)
|
||||
const targetFile = await getCurrentRender()
|
||||
const res = await runPlugin(name, targetFile)
|
||||
const res = await runPlugin(name, targetFile, data?.upscale)
|
||||
if (!res) {
|
||||
throw new Error('Something went wrong on server side.')
|
||||
}
|
||||
@@ -603,8 +603,8 @@ export default function Editor() {
|
||||
}, [runRenderablePlugin])
|
||||
|
||||
useEffect(() => {
|
||||
emitter.on(PluginName.RealESRGAN, () => {
|
||||
runRenderablePlugin(PluginName.RealESRGAN)
|
||||
emitter.on(PluginName.RealESRGAN, (data: any) => {
|
||||
runRenderablePlugin(PluginName.RealESRGAN, data)
|
||||
})
|
||||
return () => {
|
||||
emitter.off(PluginName.RealESRGAN)
|
||||
@@ -845,8 +845,9 @@ export default function Editor() {
|
||||
|
||||
try {
|
||||
const res = await runPlugin(
|
||||
PluginName.InteractiveSeg.toString(),
|
||||
PluginName.InteractiveSeg,
|
||||
targetFile,
|
||||
undefined,
|
||||
prevMask,
|
||||
newClicks
|
||||
)
|
||||
@@ -938,7 +939,7 @@ export default function Editor() {
|
||||
}
|
||||
|
||||
const onMouseDown = (ev: SyntheticEvent) => {
|
||||
if (isInteractiveSeg) {
|
||||
if (isProcessing) {
|
||||
return
|
||||
}
|
||||
if (isChangingBrushSizeByMouse) {
|
||||
@@ -954,9 +955,6 @@ export default function Editor() {
|
||||
if (!canvas) {
|
||||
return
|
||||
}
|
||||
if (isInpainting) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isRightClick(ev)) {
|
||||
return
|
||||
|
||||
@@ -3,90 +3,131 @@
|
||||
.plugins {
|
||||
position: absolute;
|
||||
top: 68px;
|
||||
left: 1rem;
|
||||
padding: 0.1rem 0.3rem;
|
||||
left: 24px;
|
||||
z-index: 4;
|
||||
|
||||
border-radius: 0.8rem;
|
||||
border-style: solid;
|
||||
border-color: var(--border-color);
|
||||
border-width: 1px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.plugins-trigger {
|
||||
font-family: 'WorkSans', sans-serif;
|
||||
font-size: 16px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.plugins-content {
|
||||
outline: none;
|
||||
position: relative;
|
||||
font-family: 'WorkSans', sans-serif;
|
||||
font-size: 14px;
|
||||
top: 8px;
|
||||
left: 1rem;
|
||||
padding: 0.8rem 0.5rem;
|
||||
z-index: 9;
|
||||
|
||||
// backdrop-filter: blur(12px);
|
||||
color: var(--text-color);
|
||||
.DropdownMenuContent,
|
||||
.DropdownMenuSubContent {
|
||||
border: 1px solid var(--border-color);
|
||||
min-width: 80px;
|
||||
background-color: var(--page-bg);
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2);
|
||||
animation-duration: 400ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
.DropdownMenuContent[data-side='top'],
|
||||
.DropdownMenuSubContent[data-side='top'] {
|
||||
animation-name: slideDownAndFade;
|
||||
}
|
||||
.DropdownMenuContent[data-side='right'],
|
||||
.DropdownMenuSubContent[data-side='right'] {
|
||||
animation-name: slideLeftAndFade;
|
||||
}
|
||||
.DropdownMenuContent[data-side='bottom'],
|
||||
.DropdownMenuSubContent[data-side='bottom'] {
|
||||
animation-name: slideUpAndFade;
|
||||
}
|
||||
.DropdownMenuContent[data-side='left'],
|
||||
.DropdownMenuSubContent[data-side='left'] {
|
||||
animation-name: slideRightAndFade;
|
||||
}
|
||||
|
||||
border-radius: 0.8rem;
|
||||
border-style: solid;
|
||||
border-color: var(--border-color);
|
||||
border-width: 1px;
|
||||
|
||||
.DropdownMenuItem,
|
||||
.DropdownMenuCheckboxItem,
|
||||
.DropdownMenuRadioItem,
|
||||
.DropdownMenuSubTrigger {
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
color: var(--btn-text-color);
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.setting-block-content {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
// input {
|
||||
// height: 24px;
|
||||
// // border-radius: 4px;
|
||||
// }
|
||||
|
||||
// button {
|
||||
// height: 28px;
|
||||
// // border-radius: 4px;
|
||||
// }
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
padding: 0 5px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
gap: 8px;
|
||||
}
|
||||
.DropdownMenuSubTrigger[data-state='open'] {
|
||||
background-color: var(--page-bg);
|
||||
color: var(--btn-text-color);
|
||||
}
|
||||
.DropdownMenuItem[data-disabled],
|
||||
.DropdownMenuCheckboxItem[data-disabled],
|
||||
.DropdownMenuRadioItem[data-disabled],
|
||||
.DropdownMenuSubTrigger[data-disabled] {
|
||||
color: var(--text-color-disabled);
|
||||
pointer-events: none;
|
||||
}
|
||||
.DropdownMenuItem[data-highlighted],
|
||||
.DropdownMenuCheckboxItem[data-highlighted],
|
||||
.DropdownMenuRadioItem[data-highlighted],
|
||||
.DropdownMenuSubTrigger[data-highlighted] {
|
||||
background-color: var(--yellow-accent);
|
||||
color: var(--btn-text-hover-color);
|
||||
}
|
||||
|
||||
.negative-prompt {
|
||||
all: unset;
|
||||
border-width: 0;
|
||||
border-radius: 0.5rem;
|
||||
min-height: 150px;
|
||||
max-width: 200px;
|
||||
width: 100%;
|
||||
padding: 12px 0.8rem;
|
||||
outline: 1px solid var(--border-color);
|
||||
|
||||
&:focus-visible {
|
||||
border-width: 0;
|
||||
outline: 1px solid var(--yellow-accent);
|
||||
}
|
||||
|
||||
&:-webkit-input-placeholder {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
&:-moz-input-placeholder {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
&:-ms-input-placeholder {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.RightSlot {
|
||||
margin-left: auto;
|
||||
padding-left: 10px;
|
||||
color: var(--btn-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.resize-title-tile {
|
||||
width: 86px;
|
||||
font-size: 0.5rem;
|
||||
[data-highlighted] > .RightSlot {
|
||||
color: var(--btn-text-hover-color);
|
||||
}
|
||||
[data-disabled] .RightSlot {
|
||||
color: var(--text-color-gray);
|
||||
}
|
||||
|
||||
@keyframes slideUpAndFade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideRightAndFade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDownAndFade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideLeftAndFade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
import React, { FormEvent } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { CursorArrowRaysIcon, GifIcon } from '@heroicons/react/24/outline'
|
||||
import { BoxModelIcon, MarginIcon, HobbyKnifeIcon } from '@radix-ui/react-icons'
|
||||
import {
|
||||
BoxModelIcon,
|
||||
ChevronRightIcon,
|
||||
HobbyKnifeIcon,
|
||||
MixIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import { useToggle } from 'react-use'
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
import {
|
||||
fileState,
|
||||
isInpaintingState,
|
||||
isPluginRunningState,
|
||||
isProcessingState,
|
||||
serverConfigState,
|
||||
} from '../../store/Atoms'
|
||||
@@ -41,50 +44,97 @@ const pluginMap = {
|
||||
}
|
||||
|
||||
const Plugins = () => {
|
||||
const [open, toggleOpen] = useToggle(true)
|
||||
// const [open, toggleOpen] = useToggle(true)
|
||||
const serverConfig = useRecoilValue(serverConfigState)
|
||||
const file = useRecoilValue(fileState)
|
||||
const isProcessing = useRecoilValue(isProcessingState)
|
||||
const disabled = !file || isProcessing
|
||||
|
||||
const onPluginClick = (pluginName: string) => {
|
||||
if (isProcessing) {
|
||||
return
|
||||
if (!disabled) {
|
||||
emitter.emit(pluginName)
|
||||
}
|
||||
emitter.emit(pluginName)
|
||||
}
|
||||
|
||||
const onRealESRGANClick = (upscale: number) => {
|
||||
if (!disabled) {
|
||||
emitter.emit(PluginName.RealESRGAN, { upscale })
|
||||
}
|
||||
}
|
||||
|
||||
const renderRealESRGANPlugin = () => {
|
||||
return (
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger
|
||||
className="DropdownMenuSubTrigger"
|
||||
disabled={disabled}
|
||||
>
|
||||
<BoxModelIcon />
|
||||
RealESRGAN
|
||||
<div className="RightSlot">
|
||||
<ChevronRightIcon />
|
||||
</div>
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.SubContent className="DropdownMenuSubContent">
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => onRealESRGANClick(2)}
|
||||
>
|
||||
upscale 2x
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => onRealESRGANClick(4)}
|
||||
disabled={disabled}
|
||||
>
|
||||
upscale 4x
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Sub>
|
||||
)
|
||||
}
|
||||
|
||||
const renderPlugins = () => {
|
||||
return serverConfig.plugins.map((plugin: string) => {
|
||||
const { IconClass } = pluginMap[plugin as PluginName]
|
||||
if (plugin === PluginName.RealESRGAN) {
|
||||
return renderRealESRGANPlugin()
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
style={{ gap: 6 }}
|
||||
icon={<IconClass style={{ width: 15 }} />}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => onPluginClick(plugin)}
|
||||
disabled={!file || isProcessing}
|
||||
disabled={disabled}
|
||||
>
|
||||
{pluginMap[plugin as PluginName].showName}
|
||||
</Button>
|
||||
<IconClass style={{ width: 15 }} />
|
||||
{plugin}
|
||||
</DropdownMenu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
if (serverConfig.plugins.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="plugins">
|
||||
<PopoverPrimitive.Root open={open}>
|
||||
<PopoverPrimitive.Trigger
|
||||
className="btn-primary plugins-trigger"
|
||||
onClick={() => toggleOpen()}
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger className="plugins">
|
||||
<Button icon={<MixIcon />} />
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="DropdownMenuContent"
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={5}
|
||||
>
|
||||
Plugins
|
||||
</PopoverPrimitive.Trigger>
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content className="plugins-content">
|
||||
{renderPlugins()}
|
||||
</PopoverPrimitive.Content>
|
||||
</PopoverPrimitive.Portal>
|
||||
</PopoverPrimitive.Root>
|
||||
</div>
|
||||
{renderPlugins()}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
// Text
|
||||
--text-color: #040404;
|
||||
--text-color-gray: rgb(107, 111, 118);
|
||||
--text-color-disabled: rgb(107, 111, 118);
|
||||
|
||||
// Shared
|
||||
--btn-text-color: var(--text-color);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
// Text
|
||||
--text-color: white;
|
||||
--text-color-gray: rgb(195, 196, 198);
|
||||
--text-color-disabled: rgb(107, 111, 118);
|
||||
|
||||
// Shared
|
||||
--btn-text-color: var(--text-color);
|
||||
|
||||
Reference in New Issue
Block a user