wip: add setting page

This commit is contained in:
Sanster 2022-04-14 20:43:07 +08:00
parent aa411c7524
commit 78d6b1cc3e
11 changed files with 155 additions and 15 deletions

View File

@ -17,6 +17,7 @@
"project": "./tsconfig.json" "project": "./tsconfig.json"
}, },
"rules": { "rules": {
"jsx-a11y/click-events-have-key-events": 0,
"react/jsx-props-no-spreading": 0, "react/jsx-props-no-spreading": 0,
"import/no-unresolved": 0, "import/no-unresolved": 0,
"react/jsx-no-bind": "off", "react/jsx-no-bind": "off",

View File

@ -0,0 +1,7 @@
.hd-setting-block {
.inline-tip {
display: inline;
cursor: pointer;
color: var(--text-color);
}
}

View File

@ -22,6 +22,7 @@ function PixelSizeInputSetting(props: PixelSizeInputProps) {
return ( return (
<SettingBlock <SettingBlock
className="sub-setting-block"
title={title} title={title}
input={ input={
<div <div
@ -54,14 +55,23 @@ function HDSettingBlock() {
} }
const onResizeLimitChange = (value: string) => { const onResizeLimitChange = (value: string) => {
const val = value.length === 0 ? 0 : parseInt(value, 10)
setSettingState(old => { setSettingState(old => {
return { ...old, hdStrategyResizeLimit: value } return { ...old, hdStrategyResizeLimit: val }
}) })
} }
const onCropTriggerSizeChange = (value: string) => { const onCropTriggerSizeChange = (value: string) => {
const val = value.length === 0 ? 0 : parseInt(value, 10)
setSettingState(old => { setSettingState(old => {
return { ...old, hdStrategyCropTrigerSize: value } return { ...old, hdStrategyCropTrigerSize: val }
})
}
const onCropMarginChange = (value: string) => {
const val = value.length === 0 ? 0 : parseInt(value, 10)
setSettingState(old => {
return { ...old, hdStrategyCropMargin: val }
}) })
} }
@ -69,7 +79,16 @@ function HDSettingBlock() {
return ( return (
<div> <div>
Use the original resolution of the picture, suitable for picture size Use the original resolution of the picture, suitable for picture size
below 2K, of course you can try it on higher resolution pictures below 2K. Try{' '}
<div
tabIndex={0}
role="button"
className="inline-tip"
onClick={() => onStrategyChange(HDStrategy.REISIZE)}
>
Resize Strategy
</div>{' '}
if you do not get good results on high resolution images.
</div> </div>
) )
} }
@ -79,7 +98,7 @@ function HDSettingBlock() {
<div> <div>
<div> <div>
Resize the longer side of the image to a specific size(keep ratio), Resize the longer side of the image to a specific size(keep ratio),
then do inpainting on the entire resized image. then do inpainting on the resized image.
</div> </div>
<PixelSizeInputSetting <PixelSizeInputSetting
title="Size limit" title="Size limit"
@ -103,6 +122,11 @@ function HDSettingBlock() {
value={`${setting.hdStrategyCropTrigerSize}`} value={`${setting.hdStrategyCropTrigerSize}`}
onValue={onCropTriggerSizeChange} onValue={onCropTriggerSizeChange}
/> />
<PixelSizeInputSetting
title="Crop margin"
value={`${setting.hdStrategyCropMargin}`}
onValue={onCropMarginChange}
/>
</div> </div>
) )
} }
@ -122,9 +146,11 @@ function HDSettingBlock() {
return ( return (
<SettingBlock <SettingBlock
className="hd-setting-block"
title="High Resolution Strategy" title="High Resolution Strategy"
input={ input={
<Selector <Selector
value={setting.hdStrategy as string}
options={Object.values(HDStrategy)} options={Object.values(HDStrategy)}
onChange={val => onStrategyChange(val as HDStrategy)} onChange={val => onStrategyChange(val as HDStrategy)}
/> />

View File

@ -0,0 +1,4 @@
.model-desc-link {
color: var(--text-color-gray);
text-decoration: none;
}

View File

@ -0,0 +1,87 @@
import React, { ReactNode } from 'react'
import { useRecoilState } from 'recoil'
import { settingState } from '../../store/Atoms'
import Selector from '../shared/Selector'
import SettingBlock from './SettingBlock'
export enum AIModel {
LAMA = 'LaMa',
LDM = 'LDM',
}
function ModelSettingBlock() {
const [setting, setSettingState] = useRecoilState(settingState)
const onModelChange = (value: AIModel) => {
setSettingState(old => {
return { ...old, model: value }
})
}
const renderModelDesc = (
name: string,
paperUrl: string,
githubUrl: string
) => {
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<a
className="model-desc-link"
href={paperUrl}
target="_blank"
rel="noreferrer noopener"
>
{name}
</a>
<br />
<a
className="model-desc-link"
href={githubUrl}
target="_blank"
rel="noreferrer noopener"
style={{ marginTop: '8px' }}
>
{githubUrl}
</a>
</div>
)
}
const renderOptionDesc = (): ReactNode => {
switch (setting.model) {
case AIModel.LAMA:
return renderModelDesc(
'Resolution-robust Large Mask Inpainting with Fourier Convolutions',
'https://arxiv.org/abs/2109.07161',
'https://github.com/saic-mdal/lama'
)
case AIModel.LDM:
return renderModelDesc(
'High-Resolution Image Synthesis with Latent Diffusion Models',
'https://arxiv.org/abs/2112.10752',
'https://github.com/CompVis/latent-diffusion'
)
default:
return <></>
}
}
return (
<SettingBlock
className="model-setting-block"
title="Inpainting Model"
input={
<Selector
value={setting.model as string}
options={Object.values(AIModel)}
onChange={val => onModelChange(val as AIModel)}
/>
}
optionDesc={renderOptionDesc()}
/>
)
}
export default ModelSettingBlock

View File

@ -6,7 +6,7 @@ import SettingBlock from './SettingBlock'
function SavePathSettingBlock() { function SavePathSettingBlock() {
return ( return (
<SettingBlock <SettingBlock
title="Download image at same folder of origin image" title="Download image beside origin image"
input={ input={
<Switch defaultChecked> <Switch defaultChecked>
<SwitchThumb /> <SwitchThumb />

View File

@ -1,12 +1,14 @@
@use '../../styles/Mixins/' as *; @use '../../styles/Mixins/' as *;
@import './SettingBlock.scss'; @import './SettingBlock.scss';
@import './HDSettingBlock.scss';
@import './ModelSettingBlock.scss';
.modal-setting { .modal-setting {
grid-area: main-content; grid-area: main-content;
background-color: var(--modal-bg); background-color: var(--modal-bg);
color: var(--modal-text-color); color: var(--modal-text-color);
box-shadow: 0px 0px 20px rgb(0, 0, 40, 0.2); box-shadow: 0px 0px 20px rgb(0, 0, 40, 0.2);
min-height: 450px; min-height: 600px;
width: 700px; width: 700px;
@include mobile { @include mobile {

View File

@ -1,13 +1,18 @@
.setting-block { .setting-block {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: 12px;
.option-desc { .option-desc {
color: var(--text-color-gray);
margin-top: 12px; margin-top: 12px;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 0.3rem; border-radius: 0.3rem;
padding: 2rem; padding: 2rem;
.sub-setting-block {
margin-top: 8px;
color: var(--text-color);
}
} }
} }

View File

@ -4,6 +4,7 @@ import { useRecoilState } from 'recoil'
import { settingState } from '../../store/Atoms' import { settingState } from '../../store/Atoms'
import Modal from '../shared/Modal' import Modal from '../shared/Modal'
import HDSettingBlock from './HDSettingBlock' import HDSettingBlock from './HDSettingBlock'
import ModelSettingBlock from './ModelSettingBlock'
import SavePathSettingBlock from './SavePathSettingBlock' import SavePathSettingBlock from './SavePathSettingBlock'
export default function SettingModal() { export default function SettingModal() {
@ -23,6 +24,7 @@ export default function SettingModal() {
show={setting.show} show={setting.show}
> >
<SavePathSettingBlock /> <SavePathSettingBlock />
<ModelSettingBlock />
<HDSettingBlock /> <HDSettingBlock />
</Modal> </Modal>
) )

View File

@ -7,6 +7,7 @@ type SelectorChevronDirection = 'up' | 'down'
type SelectorProps = { type SelectorProps = {
minWidth?: number minWidth?: number
chevronDirection?: SelectorChevronDirection chevronDirection?: SelectorChevronDirection
value: string
options: string[] options: string[]
onChange: (value: string) => void onChange: (value: string) => void
} }
@ -17,9 +18,8 @@ const selectorDefaultProps = {
} }
function Selector(props: SelectorProps) { function Selector(props: SelectorProps) {
const { minWidth, chevronDirection, options, onChange } = props const { minWidth, chevronDirection, value, options, onChange } = props
const [showOptions, setShowOptions] = useState<boolean>(false) const [showOptions, setShowOptions] = useState<boolean>(false)
const [index, setIndex] = useState<number>(0)
const selectorRef = useRef<HTMLDivElement | null>(null) const selectorRef = useRef<HTMLDivElement | null>(null)
const showOptionsHandler = () => { const showOptionsHandler = () => {
@ -46,7 +46,6 @@ function Selector(props: SelectorProps) {
const currentRes = e.target.textContent.split('x') const currentRes = e.target.textContent.split('x')
onChange(currentRes[0]) onChange(currentRes[0])
setShowOptions(false) setShowOptions(false)
setIndex(newIndex)
} }
return ( return (
@ -57,7 +56,7 @@ function Selector(props: SelectorProps) {
onClick={showOptionsHandler} onClick={showOptionsHandler}
aria-hidden="true" aria-hidden="true"
> >
<p>{options[index]}</p> <p>{value}</p>
<div className="selector-icon"> <div className="selector-icon">
{chevronDirection === 'up' ? <ChevronUpIcon /> : <ChevronDownIcon />} {chevronDirection === 'up' ? <ChevronUpIcon /> : <ChevronDownIcon />}
</div> </div>

View File

@ -1,5 +1,6 @@
import { atom } from 'recoil' import { atom } from 'recoil'
import { HDStrategy } from '../components/Setting/HDSettingBlock' import { HDStrategy } from '../components/Setting/HDSettingBlock'
import { AIModel } from '../components/Setting/ModelSettingBlock'
export const fileState = atom<File | undefined>({ export const fileState = atom<File | undefined>({
key: 'fileState', key: 'fileState',
@ -13,17 +14,23 @@ export const shortcutsState = atom<boolean>({
export interface Setting { export interface Setting {
show: boolean show: boolean
saveImageBesideOrigin: boolean
model: AIModel
hdStrategy: HDStrategy hdStrategy: HDStrategy
hdStrategyResizeLimit: string hdStrategyResizeLimit: number
hdStrategyCropTrigerSize: string hdStrategyCropTrigerSize: number
hdStrategyCropMargin: number
} }
export const settingState = atom<Setting>({ export const settingState = atom<Setting>({
key: 'settingsState', key: 'settingsState',
default: { default: {
show: false, show: false,
saveImageBesideOrigin: false,
model: AIModel.LAMA,
hdStrategy: HDStrategy.ORIGINAL, hdStrategy: HDStrategy.ORIGINAL,
hdStrategyResizeLimit: '2048', hdStrategyResizeLimit: 2048,
hdStrategyCropTrigerSize: '2048', hdStrategyCropTrigerSize: 2048,
hdStrategyCropMargin: 128,
}, },
}) })