wip: add setting page
This commit is contained in:
parent
aa411c7524
commit
78d6b1cc3e
@ -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",
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
.hd-setting-block {
|
||||||
|
.inline-tip {
|
||||||
|
display: inline;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
}
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
.model-desc-link {
|
||||||
|
color: var(--text-color-gray);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
@ -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
|
@ -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 />
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user