Merge branch '0.14.0-forceUpdate'

This commit is contained in:
Qing 2022-06-29 21:57:26 +08:00
commit 0f70ab58a7
28 changed files with 447 additions and 45 deletions

View File

@ -1,17 +1,17 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.bdd774f1.chunk.css", "main.css": "/static/css/main.4c6c667e.chunk.css",
"main.js": "/static/js/main.9f4ed6dc.chunk.js", "main.js": "/static/js/main.6b34bb53.chunk.js",
"runtime-main.js": "/static/js/runtime-main.5e86ac81.js", "runtime-main.js": "/static/js/runtime-main.5e86ac81.js",
"static/js/2.1b1d3019.chunk.js": "/static/js/2.1b1d3019.chunk.js", "static/js/2.918f0adb.chunk.js": "/static/js/2.918f0adb.chunk.js",
"index.html": "/index.html", "index.html": "/index.html",
"static/js/2.1b1d3019.chunk.js.LICENSE.txt": "/static/js/2.1b1d3019.chunk.js.LICENSE.txt", "static/js/2.918f0adb.chunk.js.LICENSE.txt": "/static/js/2.918f0adb.chunk.js.LICENSE.txt",
"static/media/_index.scss": "/static/media/WorkSans-SemiBold.1e98db4e.ttf" "static/media/_index.scss": "/static/media/WorkSans-SemiBold.1e98db4e.ttf"
}, },
"entrypoints": [ "entrypoints": [
"static/js/runtime-main.5e86ac81.js", "static/js/runtime-main.5e86ac81.js",
"static/js/2.1b1d3019.chunk.js", "static/js/2.918f0adb.chunk.js",
"static/css/main.bdd774f1.chunk.css", "static/css/main.4c6c667e.chunk.css",
"static/js/main.9f4ed6dc.chunk.js" "static/js/main.6b34bb53.chunk.js"
] ]
} }

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#ffffff"/><title>lama-cleaner - Image inpainting powered by LaMa</title><link href="/static/css/main.bdd774f1.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,a=r[0],f=r[1],i=r[2],p=0,s=[];p<a.length;p++)l=a[p],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var f=t[a];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this["webpackJsonplama-cleaner"]=this["webpackJsonplama-cleaner"]||[],f=a.push.bind(a);a.push=r,a=a.slice();for(var i=0;i<a.length;i++)r(a[i]);var c=f;t()}([])</script><script src="/static/js/2.1b1d3019.chunk.js"></script><script src="/static/js/main.9f4ed6dc.chunk.js"></script></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#ffffff"/><title>lama-cleaner - Image inpainting powered by LaMa</title><link href="/static/css/main.4c6c667e.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,a=r[0],f=r[1],i=r[2],p=0,s=[];p<a.length;p++)l=a[p],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var f=t[a];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this["webpackJsonplama-cleaner"]=this["webpackJsonplama-cleaner"]||[],f=a.push.bind(a);a.push=r,a=a.slice();for(var i=0;i<a.length;i++)r(a[i]);var c=f;t()}([])</script><script src="/static/js/2.918f0adb.chunk.js"></script><script src="/static/js/main.6b34bb53.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,6 +9,7 @@
"@radix-ui/react-select": "0.1.2-rc.27", "@radix-ui/react-select": "0.1.2-rc.27",
"@radix-ui/react-switch": "^0.1.5", "@radix-ui/react-switch": "^0.1.5",
"@radix-ui/react-toast": "^0.1.1", "@radix-ui/react-toast": "^0.1.1",
"@radix-ui/react-tooltip": "^0.1.7",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.1.2", "@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -17,6 +18,7 @@
"@types/react": "^17.0.30", "@types/react": "^17.0.30",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"cross-env": "7.x", "cross-env": "7.x",
"nanoid": "^4.0.0",
"npm-run-all": "4.x", "npm-run-all": "4.x",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",

View File

@ -1,6 +1,7 @@
import React, { useEffect } from 'react' import React, { useEffect, useMemo } from 'react'
import { useKeyPressEvent } from 'react-use' import { useKeyPressEvent } from 'react-use'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { nanoid } from 'nanoid'
import useInputImage from './hooks/useInputImage' import useInputImage from './hooks/useInputImage'
import LandingPage from './components/LandingPage/LandingPage' import LandingPage from './components/LandingPage/LandingPage'
import { themeState } from './components/Header/ThemeChanger' import { themeState } from './components/Header/ThemeChanger'
@ -33,10 +34,14 @@ function App() {
document.body.setAttribute('data-theme', theme) document.body.setAttribute('data-theme', theme)
}, [theme]) }, [theme])
const workspaceId = useMemo(() => {
return nanoid()
}, [file])
return ( return (
<div className="lama-cleaner"> <div className="lama-cleaner">
<Header /> <Header />
{file ? <Workspace file={file} /> : <LandingPage />} {file ? <Workspace file={file} key={workspaceId} /> : <LandingPage />}
</div> </div>
) )
} }

View File

@ -35,7 +35,6 @@ export default async function inpaint(
method: 'POST', method: 'POST',
body: fd, body: fd,
}).then(async r => { }).then(async r => {
console.log(r)
if (r.ok) { if (r.ok) {
return r.blob() return r.blob()
} }

View File

@ -26,6 +26,7 @@ import {
isMidClick, isMidClick,
isRightClick, isRightClick,
loadImage, loadImage,
srcToFile,
useImage, useImage,
} from '../../utils' } from '../../utils'
import { settingState, toastState } from '../../store/Atoms' import { settingState, toastState } from '../../store/Atoms'
@ -105,6 +106,11 @@ export default function Editor(props: EditorProps) {
const [sliderPos, setSliderPos] = useState<number>(0) const [sliderPos, setSliderPos] = useState<number>(0)
// redo 相关
const [redoRenders, setRedoRenders] = useState<HTMLImageElement[]>([])
const [redoCurLines, setRedoCurLines] = useState<Line[]>([])
const [redoLineGroups, setRedoLineGroups] = useState<LineGroup[]>([])
const draw = useCallback( const draw = useCallback(
(render: HTMLImageElement, lineGroup: LineGroup) => { (render: HTMLImageElement, lineGroup: LineGroup) => {
if (!context) { if (!context) {
@ -123,7 +129,7 @@ export default function Editor(props: EditorProps) {
[context, original] [context, original]
) )
const drawAllLinesOnMask = (_lineGroups: LineGroup[]) => { const drawLinesOnMask = (_lineGroups: LineGroup[]) => {
if (!context?.canvas.width || !context?.canvas.height) { if (!context?.canvas.width || !context?.canvas.height) {
throw new Error('canvas has invalid size') throw new Error('canvas has invalid size')
} }
@ -148,11 +154,22 @@ export default function Editor(props: EditorProps) {
setCurLineGroup([]) setCurLineGroup([])
setIsDraging(false) setIsDraging(false)
setIsInpaintingLoading(true) setIsInpaintingLoading(true)
drawAllLinesOnMask(newLineGroups) if (settings.graduallyInpainting) {
drawLinesOnMask([curLineGroup])
} else {
drawLinesOnMask(newLineGroups)
}
let targetFile = file
if (settings.graduallyInpainting === true && renders.length > 0) {
console.info('gradually inpainting on last result')
const lastRender = renders[renders.length - 1]
targetFile = await srcToFile(lastRender.currentSrc, file.name, file.type)
}
try { try {
const res = await inpaint( const res = await inpaint(
file, targetFile,
maskCanvas.toDataURL(), maskCanvas.toDataURL(),
settings, settings,
sizeLimit.toString() sizeLimit.toString()
@ -167,6 +184,9 @@ export default function Editor(props: EditorProps) {
draw(newRender, []) draw(newRender, [])
// Only append new LineGroup after inpainting success // Only append new LineGroup after inpainting success
setLineGroups(newLineGroups) setLineGroups(newLineGroups)
// clear redo stack
resetRedoState()
} catch (e: any) { } catch (e: any) {
setToastState({ setToastState({
open: true, open: true,
@ -284,15 +304,29 @@ export default function Editor(props: EditorProps) {
} }
const viewport = viewportRef.current const viewport = viewportRef.current
if (!viewport) { if (!viewport) {
throw new Error('no viewport') return
} }
const offsetX = (windowSize.width - original.width * minScale) / 2 const offsetX = (windowSize.width - original.width * minScale) / 2
const offsetY = (windowSize.height - original.height * minScale) / 2 const offsetY = (windowSize.height - original.height * minScale) / 2
viewport.setTransform(offsetX, offsetY, minScale, 200, 'easeOutQuad') viewport.setTransform(offsetX, offsetY, minScale, 200, 'easeOutQuad')
viewport.state.scale = minScale viewport.state.scale = minScale
setScale(minScale) setScale(minScale)
setPanned(false) setPanned(false)
}, [viewportRef, minScale, original, windowSize]) }, [
viewportRef,
windowSize,
original,
original.width,
windowSize.height,
minScale,
])
const resetRedoState = () => {
setRedoCurLines([])
setRedoLineGroups([])
setRedoRenders([])
}
useEffect(() => { useEffect(() => {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
@ -427,28 +461,43 @@ export default function Editor(props: EditorProps) {
if (curLineGroup.length === 0) { if (curLineGroup.length === 0) {
return return
} }
const newLineGroup = curLineGroup.slice(0, curLineGroup.length - 1)
const lastLine = curLineGroup.pop()!
const newRedoCurLines = [...redoCurLines, lastLine]
setRedoCurLines(newRedoCurLines)
const newLineGroup = [...curLineGroup]
setCurLineGroup(newLineGroup) setCurLineGroup(newLineGroup)
drawOnCurrentRender(newLineGroup) drawOnCurrentRender(newLineGroup)
}, [curLineGroup, drawOnCurrentRender]) }, [curLineGroup, redoCurLines, drawOnCurrentRender])
const undoRender = useCallback(() => { const undoRender = useCallback(() => {
if (!renders.length) { if (!renders.length) {
return return
} }
const groups = lineGroups.slice(0, lineGroups.length - 1) // save line Group
setLineGroups(groups) const lastLineGroup = lineGroups.pop()!
setRedoLineGroups([...redoLineGroups, lastLineGroup])
// If render is undo, clear strokes
setRedoCurLines([])
setLineGroups([...lineGroups])
setCurLineGroup([]) setCurLineGroup([])
setIsDraging(false) setIsDraging(false)
const newRenders = renders.slice(0, renders.length - 1)
// save render
const lastRender = renders.pop()!
setRedoRenders([...redoRenders, lastRender])
const newRenders = [...renders]
setRenders(newRenders) setRenders(newRenders)
if (newRenders.length === 0) { if (newRenders.length === 0) {
draw(original, []) draw(original, [])
} else { } else {
draw(newRenders[newRenders.length - 1], []) draw(newRenders[newRenders.length - 1], [])
} }
}, [draw, renders, lineGroups, original]) }, [draw, renders, redoRenders, redoLineGroups, lineGroups, original])
const undo = () => { const undo = () => {
if (settings.runInpaintingManually && curLineGroup.length !== 0) { if (settings.runInpaintingManually && curLineGroup.length !== 0) {
@ -460,13 +509,15 @@ export default function Editor(props: EditorProps) {
// Handle Cmd+Z // Handle Cmd+Z
const undoPredicate = (event: KeyboardEvent) => { const undoPredicate = (event: KeyboardEvent) => {
const isCmdZ = (event.metaKey || event.ctrlKey) && event.key === 'z' const isCmdZ =
(event.metaKey || event.ctrlKey) && !event.shiftKey && event.key === 'z'
// Handle tab switch // Handle tab switch
if (event.key === 'Tab') { if (event.key === 'Tab') {
event.preventDefault() event.preventDefault()
} }
if (isCmdZ) { if (isCmdZ) {
event.preventDefault() event.preventDefault()
console.log('undo')
return true return true
} }
return false return false
@ -475,6 +526,9 @@ export default function Editor(props: EditorProps) {
useKey(undoPredicate, undo, undefined, [undoStroke, undoRender]) useKey(undoPredicate, undo, undefined, [undoStroke, undoRender])
const disableUndo = () => { const disableUndo = () => {
if (isInpaintingLoading) {
return true
}
if (renders.length > 0) { if (renders.length > 0) {
return false return false
} }
@ -490,6 +544,80 @@ export default function Editor(props: EditorProps) {
return false return false
} }
const redoStroke = useCallback(() => {
if (redoCurLines.length === 0) {
return
}
const line = redoCurLines.pop()!
setRedoCurLines([...redoCurLines])
const newLineGroup = [...curLineGroup, line]
setCurLineGroup(newLineGroup)
drawOnCurrentRender(newLineGroup)
}, [curLineGroup, redoCurLines, drawOnCurrentRender])
const redoRender = useCallback(() => {
if (redoRenders.length === 0) {
return
}
const lineGroup = redoLineGroups.pop()!
setRedoLineGroups([...redoLineGroups])
setLineGroups([...lineGroups, lineGroup])
setCurLineGroup([])
setIsDraging(false)
const render = redoRenders.pop()!
const newRenders = [...renders, render]
setRenders(newRenders)
draw(newRenders[newRenders.length - 1], [])
}, [draw, renders, redoRenders, redoLineGroups, lineGroups, original])
const redo = () => {
if (settings.runInpaintingManually && redoCurLines.length !== 0) {
redoStroke()
} else {
redoRender()
}
}
// Handle Cmd+shift+Z
const redoPredicate = (event: KeyboardEvent) => {
const isCmdZ =
(event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'z'
// Handle tab switch
if (event.key === 'Tab') {
event.preventDefault()
}
if (isCmdZ) {
event.preventDefault()
console.log('redo')
return true
}
return false
}
useKey(redoPredicate, redo, undefined, [redoStroke, redoRender])
const disableRedo = () => {
if (isInpaintingLoading) {
return true
}
if (redoRenders.length > 0) {
return false
}
if (settings.runInpaintingManually) {
if (redoCurLines.length === 0) {
return true
}
} else if (redoRenders.length === 0) {
return true
}
return false
}
useKeyPressEvent( useKeyPressEvent(
'Tab', 'Tab',
ev => { ev => {
@ -763,6 +891,27 @@ export default function Editor(props: EditorProps) {
onClick={undo} onClick={undo}
disabled={disableUndo()} disabled={disableUndo()}
/> />
<Button
toolTip="Redo"
tooltipPosition="top"
icon={
<svg
width="19"
height="9"
viewBox="0 0 19 9"
fill="none"
xmlns="http://www.w3.org/2000/svg"
transform="scale(-1,1)"
>
<path
d="M2 1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1H2ZM1 8H0V9H1V8ZM8 9C8.55228 9 9 8.55229 9 8C9 7.44771 8.55228 7 8 7V9ZM16.5963 7.42809C16.8327 7.92721 17.429 8.14016 17.9281 7.90374C18.4272 7.66731 18.6402 7.07103 18.4037 6.57191L16.5963 7.42809ZM16.9468 5.83205L17.8505 5.40396L16.9468 5.83205ZM0 1V8H2V1H0ZM1 9H8V7H1V9ZM1.66896 8.74329L6.66896 4.24329L5.33104 2.75671L0.331035 7.25671L1.66896 8.74329ZM16.043 6.26014L16.5963 7.42809L18.4037 6.57191L17.8505 5.40396L16.043 6.26014ZM6.65079 4.25926C9.67554 1.66661 14.3376 2.65979 16.043 6.26014L17.8505 5.40396C15.5805 0.61182 9.37523 -0.710131 5.34921 2.74074L6.65079 4.25926Z"
fill="currentColor"
/>
</svg>
}
onClick={redo}
disabled={disableRedo()}
/>
<Button <Button
toolTip="Show Original" toolTip="Show Original"
tooltipPosition="top" tooltipPosition="top"

View File

@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import Selector from '../shared/Selector' import Selector from '../shared/Selector'
const sizes = ['720', '1080', '2000', 'Original'] const sizes = ['720', '1080', '2000', 'Original']

View File

@ -1,5 +1,5 @@
import { ArrowLeftIcon } from '@heroicons/react/outline' import { ArrowLeftIcon, UploadIcon } from '@heroicons/react/outline'
import React from 'react' import React, { useState } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { fileState } from '../../store/Atoms' import { fileState } from '../../store/Atoms'
import Button from '../shared/Button' import Button from '../shared/Button'
@ -11,20 +11,30 @@ import SettingIcon from '../Settings/SettingIcon'
const Header = () => { const Header = () => {
const [file, setFile] = useRecoilState(fileState) const [file, setFile] = useRecoilState(fileState)
const resolution = useResolution() const resolution = useResolution()
const [uploadElemId] = useState(`file-upload-${Math.random().toString()}`)
const renderHeader = () => { const renderHeader = () => {
return ( return (
<header> <header>
<div style={{ visibility: file ? 'visible' : 'hidden' }}> <div style={{ visibility: file ? 'visible' : 'hidden' }}>
<Button <label htmlFor={uploadElemId}>
icon={<ArrowLeftIcon />} <Button icon={<UploadIcon />} style={{ border: 0 }}>
onClick={() => { <input
setFile(undefined) style={{ display: 'none' }}
}} id={uploadElemId}
style={{ border: 0 }} name={uploadElemId}
> type="file"
{resolution === 'desktop' ? 'Start New' : undefined} onChange={ev => {
</Button> const newFile = ev.currentTarget.files?.[0]
if (newFile) {
setFile(newFile)
}
}}
accept="image/png, image/jpeg"
/>
{resolution === 'desktop' ? 'Upload New' : undefined}
</Button>
</label>
</div> </div>
<div className="header-icons-wrapper"> <div className="header-icons-wrapper">

View File

@ -0,0 +1,32 @@
import React from 'react'
import { useRecoilState } from 'recoil'
import { settingState } from '../../store/Atoms'
import { Switch, SwitchThumb } from '../shared/Switch'
import SettingBlock from './SettingBlock'
const GraduallyInpaintingSettingBlock: React.FC = () => {
const [setting, setSettingState] = useRecoilState(settingState)
const onCheckChange = (checked: boolean) => {
setSettingState(old => {
return { ...old, graduallyInpainting: checked }
})
}
return (
<SettingBlock
title="Gradually Inpainting"
desc="If checked, perform inpainting on the last result, otherwise, always run the model on the initial image."
input={
<Switch
checked={setting.graduallyInpainting}
onCheckedChange={onCheckChange}
>
<SwitchThumb />
</Switch>
}
/>
)
}
export default GraduallyInpaintingSettingBlock

View File

@ -25,8 +25,10 @@
.setting-block-content-title { .setting-block-content-title {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
justify-content: space-between; justify-content: center;
align-items: center;
gap: 8px;
} }
.setting-block-desc { .setting-block-desc {

View File

@ -1,4 +1,5 @@
import React, { ReactNode } from 'react' import React, { ReactNode } from 'react'
import Tooltip from '../shared/Tooltip'
interface SettingBlockProps { interface SettingBlockProps {
title: string title: string
@ -15,7 +16,24 @@ function SettingBlock(props: SettingBlockProps) {
<div className="setting-block-content"> <div className="setting-block-content">
<div className="setting-block-content-title"> <div className="setting-block-content-title">
<span>{title}</span> <span>{title}</span>
{desc && <span className="setting-block-desc">{desc}</span>} {desc && (
<Tooltip content={<div style={{ width: 400 }}>{desc}</div>}>
<svg
width="18"
height="18"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.877075 7.49972C0.877075 3.84204 3.84222 0.876892 7.49991 0.876892C11.1576 0.876892 14.1227 3.84204 14.1227 7.49972C14.1227 11.1574 11.1576 14.1226 7.49991 14.1226C3.84222 14.1226 0.877075 11.1574 0.877075 7.49972ZM7.49991 1.82689C4.36689 1.82689 1.82708 4.36671 1.82708 7.49972C1.82708 10.6327 4.36689 13.1726 7.49991 13.1726C10.6329 13.1726 13.1727 10.6327 13.1727 7.49972C13.1727 4.36671 10.6329 1.82689 7.49991 1.82689ZM8.24993 10.5C8.24993 10.9142 7.91414 11.25 7.49993 11.25C7.08571 11.25 6.74993 10.9142 6.74993 10.5C6.74993 10.0858 7.08571 9.75 7.49993 9.75C7.91414 9.75 8.24993 10.0858 8.24993 10.5ZM6.05003 6.25C6.05003 5.57211 6.63511 4.925 7.50003 4.925C8.36496 4.925 8.95003 5.57211 8.95003 6.25C8.95003 6.74118 8.68002 6.99212 8.21447 7.27494C8.16251 7.30651 8.10258 7.34131 8.03847 7.37854L8.03841 7.37858C7.85521 7.48497 7.63788 7.61119 7.47449 7.73849C7.23214 7.92732 6.95003 8.23198 6.95003 8.7C6.95004 9.00376 7.19628 9.25 7.50004 9.25C7.8024 9.25 8.04778 9.00601 8.05002 8.70417L8.05056 8.7033C8.05924 8.6896 8.08493 8.65735 8.15058 8.6062C8.25207 8.52712 8.36508 8.46163 8.51567 8.37436L8.51571 8.37433C8.59422 8.32883 8.68296 8.27741 8.78559 8.21506C9.32004 7.89038 10.05 7.35382 10.05 6.25C10.05 4.92789 8.93511 3.825 7.50003 3.825C6.06496 3.825 4.95003 4.92789 4.95003 6.25C4.95003 6.55376 5.19628 6.8 5.50003 6.8C5.80379 6.8 6.05003 6.55376 6.05003 6.25Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
/>
</svg>
</Tooltip>
)}
</div> </div>
{input} {input}
</div> </div>

View File

@ -6,6 +6,7 @@ import Modal from '../shared/Modal'
import ManualRunInpaintingSettingBlock from './ManualRunInpaintingSettingBlock' import ManualRunInpaintingSettingBlock from './ManualRunInpaintingSettingBlock'
import HDSettingBlock from './HDSettingBlock' import HDSettingBlock from './HDSettingBlock'
import ModelSettingBlock from './ModelSettingBlock' import ModelSettingBlock from './ModelSettingBlock'
import GraduallyInpaintingSettingBlock from './GraduallyInpaintingSettingBlock'
interface SettingModalProps { interface SettingModalProps {
onClose: () => void onClose: () => void
@ -29,6 +30,7 @@ export default function SettingModal(props: SettingModalProps) {
show={setting.show} show={setting.show}
> >
<ManualRunInpaintingSettingBlock /> <ManualRunInpaintingSettingBlock />
<GraduallyInpaintingSettingBlock />
<ModelSettingBlock /> <ModelSettingBlock />
<HDSettingBlock /> <HDSettingBlock />
</Modal> </Modal>

View File

@ -31,3 +31,40 @@ $tooltip-margin: 1.5rem;
margin-top: $tooltip-margin; margin-top: $tooltip-margin;
} }
} }
// radix-ui
.tooltip-trigger {
all: unset;
display: flex;
justify-content: center;
align-items: center;
}
.tooltip-content {
color: var(--tooltip-text-color);
background-color: var(--tooltip-bg);
padding: 10px 15px;
border-radius: 4px;
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px,
hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
@media (prefers-reduced-motion: no-preference) {
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
animation-fill-mode: forwards;
will-change: transform, opacity;
&[data-state='delayed-open'] {
&[data-side='top'] {
animation-name: slideDownAndFade;
}
&[data-side='bottom'] {
animation-name: slideUpAndFade;
}
}
}
}
.tooltip-arrow {
fill: var(--tooltip-bg);
}

View File

@ -0,0 +1,29 @@
import React, { ReactNode } from 'react'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
import { TooltipProps } from '@radix-ui/react-tooltip'
interface MyTooltipProps extends TooltipProps {
content: string | ReactNode
children: ReactNode
}
const Tooltip = (props: MyTooltipProps) => {
const { content, children } = props
return (
<TooltipPrimitive.Root>
<TooltipPrimitive.Provider>
<TooltipPrimitive.Trigger className="tooltip-trigger">
{children}
</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content className="tooltip-content">
{content}
<TooltipPrimitive.Arrow className="tooltip-arrow" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Provider>
</TooltipPrimitive.Root>
)
}
export default Tooltip

View File

@ -32,6 +32,7 @@ export const shortcutsState = atom<boolean>({
export interface Settings { export interface Settings {
show: boolean show: boolean
graduallyInpainting: boolean
runInpaintingManually: boolean runInpaintingManually: boolean
model: AIModel model: AIModel
@ -48,6 +49,7 @@ export interface Settings {
export const settingStateDefault = { export const settingStateDefault = {
show: false, show: false,
graduallyInpainting: true,
runInpaintingManually: false, runInpaintingManually: false,
model: AIModel.LAMA, model: AIModel.LAMA,
ldmSteps: 50, ldmSteps: 50,
@ -65,7 +67,7 @@ const localStorageEffect =
if (savedValue != null) { if (savedValue != null) {
const storageSettings = JSON.parse(savedValue) const storageSettings = JSON.parse(savedValue)
storageSettings.show = false storageSettings.show = false
setSelf(storageSettings) setSelf({ ...settingStateDefault, ...storageSettings })
} }
onSet((newValue: Settings, _: string, isReset: boolean) => onSet((newValue: Settings, _: string, isReset: boolean) =>

View File

@ -55,3 +55,25 @@
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
@keyframes slideUpAndFade {
0% {
opacity: 0;
transform: translateY(2px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDownAndFade {
0% {
opacity: 0;
transform: translateY(-2px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

View File

@ -10,7 +10,7 @@
--border-color: rgb(100, 100, 120); --border-color: rgb(100, 100, 120);
--border-color-light: rgba(100, 100, 120, 0.5); --border-color-light: rgba(100, 100, 120, 0.5);
--tooltip-bg: rgb(230, 230, 234); --tooltip-bg: rgb(230, 230, 234);
--tooltip-text-color: rgb(0, 0,0); --tooltip-text-color: rgb(0, 0, 0);
--error-color: rgb(239, 68, 68); --error-color: rgb(239, 68, 68);
--success-color: rgb(16, 185, 129); --success-color: rgb(16, 185, 129);
@ -44,4 +44,7 @@
--switch-root-background-color: rgb(223, 225, 228); --switch-root-background-color: rgb(223, 225, 228);
--switch-thumb-color: var(--page-bg); --switch-thumb-color: var(--page-bg);
--switch-thumb-checked-color: var(--page-bg); --switch-thumb-checked-color: var(--page-bg);
// tooltip
--tooltip-bg: var(--page-bg);
} }

View File

@ -42,4 +42,7 @@
--switch-root-background-color: rgb(60, 63, 68); --switch-root-background-color: rgb(60, 63, 68);
--switch-thumb-color: rgb(31, 32, 35); --switch-thumb-color: rgb(31, 32, 35);
--switch-thumb-checked-color: white; --switch-thumb-checked-color: white;
// tooltip
--tooltip-bg: hsl(197 6.8% 13.6%);
} }

View File

@ -192,3 +192,13 @@ export function isMidClick(ev: SyntheticEvent) {
const mouseEvent = ev.nativeEvent as MouseEvent const mouseEvent = ev.nativeEvent as MouseEvent
return mouseEvent.button === 1 return mouseEvent.button === 1
} }
export function srcToFile(src: string, fileName: string, mimeType: string) {
return fetch(src)
.then(function (res) {
return res.arrayBuffer()
})
.then(function (buf) {
return new File([buf], fileName, { type: mimeType })
})
}

View File

@ -1551,6 +1551,14 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/popper@0.1.0":
version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/popper/-/popper-0.1.0.tgz#c387a38f31b7799e1ea0d2bb1ca0c91c2931b063"
integrity sha512-uzYeElL3w7SeNMuQpXiFlBhTT+JyaNMCwDfjKkrzugEcYrf5n52PHqncNdQPUtR42hJh8V9FsqyEDbDxkeNjJQ==
dependencies:
"@babel/runtime" "^7.13.10"
csstype "^3.0.4"
"@radix-ui/primitive@0.1.0": "@radix-ui/primitive@0.1.0":
version "0.1.0" version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-0.1.0.tgz#6206b97d379994f0d1929809db035733b337e543" resolved "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-0.1.0.tgz#6206b97d379994f0d1929809db035733b337e543"
@ -1558,6 +1566,14 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-arrow@0.1.4":
version "0.1.4"
resolved "https://registry.npmmirror.com/@radix-ui/react-arrow/-/react-arrow-0.1.4.tgz#a871448a418cd3507d83840fdd47558cb961672b"
integrity sha512-BB6XzAb7Ml7+wwpFdYVtZpK1BlMgqyafSQNGzhIpSZ4uXvXOHPlR5GP8M449JkeQzgQjv9Mp1AsJxFC0KuOtuA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-collection@0.1.5-rc.18": "@radix-ui/react-collection@0.1.5-rc.18":
version "0.1.5-rc.18" version "0.1.5-rc.18"
resolved "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-0.1.5-rc.18.tgz#4dc03a8f464643748c0dad781b472f149d671d5c" resolved "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-0.1.5-rc.18.tgz#4dc03a8f464643748c0dad781b472f149d671d5c"
@ -1706,6 +1722,21 @@
"@radix-ui/react-id" "0.1.6-rc.18" "@radix-ui/react-id" "0.1.6-rc.18"
"@radix-ui/react-primitive" "0.1.5-rc.18" "@radix-ui/react-primitive" "0.1.5-rc.18"
"@radix-ui/react-popper@0.1.4":
version "0.1.4"
resolved "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-0.1.4.tgz#dfc055dcd7dfae6a2eff7a70d333141d15a5d029"
integrity sha512-18gDYof97t8UQa7zwklG1Dr8jIdj3u+rVOQLzPi9f5i1YQak/pVGkaqw8aY+iDUknKKuZniTk/7jbAJUYlKyOw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/popper" "0.1.0"
"@radix-ui/react-arrow" "0.1.4"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-rect" "0.1.1"
"@radix-ui/react-use-size" "0.1.1"
"@radix-ui/rect" "0.1.1"
"@radix-ui/react-portal@0.1.4": "@radix-ui/react-portal@0.1.4":
version "0.1.4" version "0.1.4"
resolved "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-0.1.4.tgz#17bdce3d7f1a9a0b35cb5e935ab8bc562441a7d2" resolved "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-0.1.4.tgz#17bdce3d7f1a9a0b35cb5e935ab8bc562441a7d2"
@ -1833,6 +1864,27 @@
"@radix-ui/react-use-layout-effect" "0.1.0" "@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-visually-hidden" "0.1.4" "@radix-ui/react-visually-hidden" "0.1.4"
"@radix-ui/react-tooltip@^0.1.7":
version "0.1.7"
resolved "https://registry.npmmirror.com/@radix-ui/react-tooltip/-/react-tooltip-0.1.7.tgz#6f8c00d6e489565d14abf209ce0fb8853c8c8ee3"
integrity sha512-eiBUsVOHenZ0JR16tl970bB0DafJBz6mFgSGfIGIVpflFj0LIsIDiLMsYyvYdx1KwwsIUDTEZtxcPm/sWjPzqA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-id" "0.1.5"
"@radix-ui/react-popper" "0.1.4"
"@radix-ui/react-portal" "0.1.4"
"@radix-ui/react-presence" "0.1.2"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-slot" "0.1.2"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-use-escape-keydown" "0.1.0"
"@radix-ui/react-use-previous" "0.1.1"
"@radix-ui/react-use-rect" "0.1.1"
"@radix-ui/react-visually-hidden" "0.1.4"
"@radix-ui/react-use-body-pointer-events@0.1.1": "@radix-ui/react-use-body-pointer-events@0.1.1":
version "0.1.1" version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.1.tgz#63e7fd81ca7ffd30841deb584cd2b7f460df2597" resolved "https://registry.npmmirror.com/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.1.tgz#63e7fd81ca7ffd30841deb584cd2b7f460df2597"
@ -1923,6 +1975,14 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-use-rect@0.1.1":
version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-rect/-/react-use-rect-0.1.1.tgz#6c15384beee59c086e75b89a7e66f3d2e583a856"
integrity sha512-kHNNXAsP3/PeszEmM/nxBBS9Jbo93sO+xuMTcRfwzXsmxT5gDXQzAiKbZQ0EecCPtJIzqvr7dlaQi/aP1PKYqQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/rect" "0.1.1"
"@radix-ui/react-use-size@0.1.1": "@radix-ui/react-use-size@0.1.1":
version "0.1.1" version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-0.1.1.tgz#f6b75272a5d41c3089ca78c8a2e48e5f204ef90f" resolved "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-0.1.1.tgz#f6b75272a5d41c3089ca78c8a2e48e5f204ef90f"
@ -1946,6 +2006,13 @@
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "0.1.5-rc.18" "@radix-ui/react-primitive" "0.1.5-rc.18"
"@radix-ui/rect@0.1.1":
version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/rect/-/rect-0.1.1.tgz#95b5ba51f469bea6b1b841e2d427e17e37d38419"
integrity sha512-g3hnE/UcOg7REdewduRPAK88EPuLZtaq7sA9ouu8S+YEtnyFRI16jgv6GZYe3VMoQLL1T171ebmEPtDjyxWLzw==
dependencies:
"@babel/runtime" "^7.13.10"
"@rollup/plugin-node-resolve@^7.1.1": "@rollup/plugin-node-resolve@^7.1.1":
version "7.1.3" version "7.1.3"
resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz" resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz"
@ -4496,6 +4563,11 @@ csstype@^3.0.2, csstype@^3.0.6:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz" resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz"
integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==
csstype@^3.0.4:
version "3.1.0"
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
cyclist@^1.0.1: cyclist@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz" resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz"
@ -8102,6 +8174,11 @@ nanoid@^3.1.28:
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz"
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
nanoid@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.0.tgz#6e144dee117609232c3f415c34b0e550e64999a5"
integrity sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==
nanomatch@^1.2.9: nanomatch@^1.2.9:
version "1.2.13" version "1.2.13"
resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz"

View File

@ -21,7 +21,7 @@ def load_requirements():
# https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files # https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files
setuptools.setup( setuptools.setup(
name="lama-cleaner", name="lama-cleaner",
version="0.13.0", version="0.14.0",
author="PanicByte", author="PanicByte",
author_email="cwq1913@gmail.com", author_email="cwq1913@gmail.com",
description="Image inpainting tool powered by SOTA AI Model", description="Image inpainting tool powered by SOTA AI Model",