diff --git a/lama_cleaner/app/src/adapters/inpainting.ts b/lama_cleaner/app/src/adapters/inpainting.ts index 2cf291a..5f74eb7 100644 --- a/lama_cleaner/app/src/adapters/inpainting.ts +++ b/lama_cleaner/app/src/adapters/inpainting.ts @@ -9,6 +9,7 @@ export default async function inpaint( settings: Settings, croperRect: Rect, prompt?: string, + negativePrompt?: string, sizeLimit?: string, seed?: number ) { @@ -34,6 +35,10 @@ export default async function inpaint( ) fd.append('prompt', prompt === undefined ? '' : prompt) + fd.append( + 'negativePrompt', + negativePrompt === undefined ? '' : negativePrompt + ) fd.append('croperX', croperRect.x.toString()) fd.append('croperY', croperRect.y.toString()) fd.append('croperHeight', croperRect.height.toString()) diff --git a/lama_cleaner/app/src/components/Editor/Editor.tsx b/lama_cleaner/app/src/components/Editor/Editor.tsx index 128e184..da2e445 100644 --- a/lama_cleaner/app/src/components/Editor/Editor.tsx +++ b/lama_cleaner/app/src/components/Editor/Editor.tsx @@ -36,6 +36,7 @@ import { fileState, isInpaintingState, isSDState, + negativePropmtState, propmtState, runManuallyState, seedState, @@ -88,6 +89,7 @@ function mouseXY(ev: SyntheticEvent) { export default function Editor() { const [file, setFile] = useRecoilState(fileState) const promptVal = useRecoilValue(propmtState) + const negativePromptVal = useRecoilValue(negativePropmtState) const settings = useRecoilValue(settingState) const [seedVal, setSeed] = useRecoilState(seedState) const croperRect = useRecoilValue(croperState) @@ -261,6 +263,7 @@ export default function Editor() { settings, croperRect, prompt, + negativePromptVal, sizeLimit.toString(), sdSeed ) @@ -311,6 +314,7 @@ export default function Editor() { croperRect, sizeLimit, promptVal, + negativePromptVal, drawOnCurrentRender, hadDrawSomething, drawLinesOnMask, diff --git a/lama_cleaner/app/src/components/Settings/SettingBlock.scss b/lama_cleaner/app/src/components/Settings/SettingBlock.scss index 9dce547..85befdd 100644 --- a/lama_cleaner/app/src/components/Settings/SettingBlock.scss +++ b/lama_cleaner/app/src/components/Settings/SettingBlock.scss @@ -25,6 +25,14 @@ gap: 12rem; } +.setting-block-content-v { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + gap: 1rem; +} + .setting-block-content-title { display: flex; flex-direction: row; diff --git a/lama_cleaner/app/src/components/Settings/SettingBlock.tsx b/lama_cleaner/app/src/components/Settings/SettingBlock.tsx index 5f3fcc0..0a297bc 100644 --- a/lama_cleaner/app/src/components/Settings/SettingBlock.tsx +++ b/lama_cleaner/app/src/components/Settings/SettingBlock.tsx @@ -8,13 +8,18 @@ interface SettingBlockProps { input: ReactNode optionDesc?: ReactNode className?: string + layout?: string } function SettingBlock(props: SettingBlockProps) { - const { title, titleSuffix, desc, input, optionDesc, className } = props + const { title, titleSuffix, desc, input, optionDesc, className, layout } = + props + const contentClass = + layout === 'h' ? 'setting-block-content' : 'setting-block-content-v' + return (
-
+
{desc ? ( @@ -34,4 +39,8 @@ function SettingBlock(props: SettingBlockProps) { ) } +SettingBlock.defaultProps = { + layout: 'h', +} + export default SettingBlock diff --git a/lama_cleaner/app/src/components/SidePanel/SidePanel.scss b/lama_cleaner/app/src/components/SidePanel/SidePanel.scss index ff54c20..0b87408 100644 --- a/lama_cleaner/app/src/components/SidePanel/SidePanel.scss +++ b/lama_cleaner/app/src/components/SidePanel/SidePanel.scss @@ -55,3 +55,31 @@ // // border-radius: 4px; // } } + +.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; + } +} diff --git a/lama_cleaner/app/src/components/SidePanel/SidePanel.tsx b/lama_cleaner/app/src/components/SidePanel/SidePanel.tsx index fa8fd37..c2fd1a5 100644 --- a/lama_cleaner/app/src/components/SidePanel/SidePanel.tsx +++ b/lama_cleaner/app/src/components/SidePanel/SidePanel.tsx @@ -1,12 +1,13 @@ -import React, { useState } from 'react' +import React, { FormEvent, useState } from 'react' import { useRecoilState } from 'recoil' import * as PopoverPrimitive from '@radix-ui/react-popover' import { useToggle } from 'react-use' -import { SDSampler, settingState } from '../../store/Atoms' +import { negativePropmtState, SDSampler, settingState } from '../../store/Atoms' import NumberInputSetting from '../Settings/NumberInputSetting' import SettingBlock from '../Settings/SettingBlock' import Selector from '../shared/Selector' import { Switch, SwitchThumb } from '../shared/Switch' +import TextAreaInput from '../shared/Textarea' const INPUT_WIDTH = 30 @@ -14,6 +15,15 @@ const INPUT_WIDTH = 30 const SidePanel = () => { const [open, toggleOpen] = useToggle(true) const [setting, setSettingState] = useRecoilState(settingState) + const [negativePrompt, setNegativePrompt] = + useRecoilState(negativePropmtState) + + const handleOnInput = (evt: FormEvent) => { + evt.preventDefault() + evt.stopPropagation() + const target = evt.target as HTMLTextAreaElement + setNegativePrompt(target.value) + } return (
@@ -59,7 +69,7 @@ const SidePanel = () => { title="Steps" width={INPUT_WIDTH} value={`${setting.sdSteps}`} - desc="Large steps result in better result, but more time-consuming" + desc="The number of denoising steps. More denoising steps usually lead to a higher quality image at the expense of slower inference." onValue={value => { const val = value.length === 0 ? 0 : parseInt(value, 10) setSettingState(old => { @@ -88,7 +98,7 @@ const SidePanel = () => { width={INPUT_WIDTH} allowFloat value={`${setting.sdGuidanceScale}`} - desc="TODO" + desc="Higher guidance scale encourages to generate images that are closely linked to the text prompt, usually at the expense of lower image quality." onValue={value => { const val = value.length === 0 ? 0 : parseFloat(value) setSettingState(old => { @@ -101,7 +111,7 @@ const SidePanel = () => { title="Mask Blur" width={INPUT_WIDTH} value={`${setting.sdMaskBlur}`} - desc="TODO" + desc="Blur the edge of mask area. The higher the number the smoother blend with the original image" onValue={value => { const val = value.length === 0 ? 0 : parseInt(value, 10) setSettingState(old => { @@ -167,6 +177,20 @@ const SidePanel = () => {
} /> + + + } + /> diff --git a/lama_cleaner/app/src/components/shared/Input.tsx b/lama_cleaner/app/src/components/shared/Input.tsx index 1537748..37199f8 100644 --- a/lama_cleaner/app/src/components/shared/Input.tsx +++ b/lama_cleaner/app/src/components/shared/Input.tsx @@ -1,5 +1,4 @@ import React, { FocusEvent, InputHTMLAttributes, RefObject } from 'react' -import { useClickAway } from 'react-use' import { useRecoilState } from 'recoil' import { appState } from '../../store/Atoms' diff --git a/lama_cleaner/app/src/components/shared/Textarea.tsx b/lama_cleaner/app/src/components/shared/Textarea.tsx new file mode 100644 index 0000000..94623aa --- /dev/null +++ b/lama_cleaner/app/src/components/shared/Textarea.tsx @@ -0,0 +1,45 @@ +import React, { FocusEvent, TextareaHTMLAttributes } from 'react' +import { useRecoilState } from 'recoil' +import { appState } from '../../store/Atoms' + +const TextAreaInput = React.forwardRef< + HTMLTextAreaElement, + TextareaHTMLAttributes +>((props, ref) => { + const { onFocus, onBlur, ...itemProps } = props + const [_, setAppState] = useRecoilState(appState) + + const handleOnFocus = (evt: FocusEvent) => { + setAppState(old => { + return { ...old, disableShortCuts: true } + }) + onFocus?.(evt) + } + + const handleOnBlur = (evt: FocusEvent) => { + setAppState(old => { + return { ...old, disableShortCuts: false } + }) + onBlur?.(evt) + } + + return ( +