🎨 完整的 IOPaint 项目更新
## 主要更新 - ✨ 更新所有依赖到最新稳定版本 - 📝 添加详细的项目文档和模型推荐 - 🔧 配置 VSCode Cloud Studio 预览功能 - 🐛 修复 PyTorch API 弃用警告 ## 依赖更新 - diffusers: 0.27.2 → 0.35.2 - gradio: 4.21.0 → 5.46.0 - peft: 0.7.1 → 0.18.0 - Pillow: 9.5.0 → 11.3.0 - fastapi: 0.108.0 → 0.116.2 ## 新增文件 - CLAUDE.md - 项目架构和开发指南 - UPGRADE_NOTES.md - 详细的升级说明 - .vscode/preview.yml - 预览配置 - .vscode/LAUNCH_GUIDE.md - 启动指南 - .gitignore - 更新的忽略规则 ## 代码修复 - 修复 iopaint/model/ldm.py 中的 torch.cuda.amp.autocast() 弃用警告 ## 文档更新 - README.md - 添加模型推荐和使用指南 - 完整的项目源码(iopaint/) - Web 前端源码(web_app/) 🤖 Generated with Claude Code
This commit is contained in:
167
web_app/src/App.tsx
Normal file
167
web_app/src/App.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
|
||||
import useInputImage from "@/hooks/useInputImage"
|
||||
import { keepGUIAlive } from "@/lib/utils"
|
||||
import { getServerConfig } from "@/lib/api"
|
||||
import Header from "@/components/Header"
|
||||
import Workspace from "@/components/Workspace"
|
||||
import FileSelect from "@/components/FileSelect"
|
||||
import { Toaster } from "./components/ui/toaster"
|
||||
import { useStore } from "./lib/states"
|
||||
import { useWindowSize } from "react-use"
|
||||
|
||||
const SUPPORTED_FILE_TYPE = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/webp",
|
||||
"image/bmp",
|
||||
"image/tiff",
|
||||
]
|
||||
function Home() {
|
||||
const [file, updateAppState, setServerConfig, setFile] = useStore((state) => [
|
||||
state.file,
|
||||
state.updateAppState,
|
||||
state.setServerConfig,
|
||||
state.setFile,
|
||||
])
|
||||
|
||||
const userInputImage = useInputImage()
|
||||
|
||||
const windowSize = useWindowSize()
|
||||
|
||||
useEffect(() => {
|
||||
if (userInputImage) {
|
||||
setFile(userInputImage)
|
||||
}
|
||||
}, [userInputImage, setFile])
|
||||
|
||||
useEffect(() => {
|
||||
updateAppState({ windowSize })
|
||||
}, [windowSize])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchServerConfig = async () => {
|
||||
const serverConfig = await getServerConfig()
|
||||
setServerConfig(serverConfig)
|
||||
if (serverConfig.isDesktop) {
|
||||
// Keeping GUI Window Open
|
||||
keepGUIAlive()
|
||||
}
|
||||
}
|
||||
fetchServerConfig()
|
||||
}, [])
|
||||
|
||||
const dragCounter = useRef(0)
|
||||
|
||||
const handleDrag = useCallback((event: any) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}, [])
|
||||
|
||||
const handleDragIn = useCallback((event: any) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
dragCounter.current += 1
|
||||
}, [])
|
||||
|
||||
const handleDragOut = useCallback((event: any) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
dragCounter.current -= 1
|
||||
if (dragCounter.current > 0) return
|
||||
}, [])
|
||||
|
||||
const handleDrop = useCallback((event: any) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
if (event.dataTransfer.files && event.dataTransfer.files.length > 0) {
|
||||
if (event.dataTransfer.files.length > 1) {
|
||||
// setToastState({
|
||||
// open: true,
|
||||
// desc: "Please drag and drop only one file",
|
||||
// state: "error",
|
||||
// duration: 3000,
|
||||
// })
|
||||
} else {
|
||||
const dragFile = event.dataTransfer.files[0]
|
||||
const fileType = dragFile.type
|
||||
if (SUPPORTED_FILE_TYPE.includes(fileType)) {
|
||||
setFile(dragFile)
|
||||
} else {
|
||||
// setToastState({
|
||||
// open: true,
|
||||
// desc: "Please drag and drop an image file",
|
||||
// state: "error",
|
||||
// duration: 3000,
|
||||
// })
|
||||
}
|
||||
}
|
||||
event.dataTransfer.clearData()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const onPaste = useCallback((event: any) => {
|
||||
// TODO: when sd side panel open, ctrl+v not work
|
||||
// https://htmldom.dev/paste-an-image-from-the-clipboard/
|
||||
if (!event.clipboardData) {
|
||||
return
|
||||
}
|
||||
const clipboardItems = event.clipboardData.items
|
||||
const items: DataTransferItem[] = [].slice
|
||||
.call(clipboardItems)
|
||||
.filter((item: DataTransferItem) => {
|
||||
// Filter the image items only
|
||||
return item.type.indexOf("image") !== -1
|
||||
})
|
||||
|
||||
if (items.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
// TODO: add confirm dialog
|
||||
|
||||
const item = items[0]
|
||||
// Get the blob of image
|
||||
const blob = item.getAsFile()
|
||||
if (blob) {
|
||||
setFile(blob)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("dragenter", handleDragIn)
|
||||
window.addEventListener("dragleave", handleDragOut)
|
||||
window.addEventListener("dragover", handleDrag)
|
||||
window.addEventListener("drop", handleDrop)
|
||||
window.addEventListener("paste", onPaste)
|
||||
return function cleanUp() {
|
||||
window.removeEventListener("dragenter", handleDragIn)
|
||||
window.removeEventListener("dragleave", handleDragOut)
|
||||
window.removeEventListener("dragover", handleDrag)
|
||||
window.removeEventListener("drop", handleDrop)
|
||||
window.removeEventListener("paste", onPaste)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between w-full bg-[radial-gradient(circle_at_1px_1px,_#8e8e8e8e_1px,_transparent_0)] [background-size:20px_20px] bg-repeat">
|
||||
<Toaster />
|
||||
<Header />
|
||||
<Workspace />
|
||||
{!file ? (
|
||||
<FileSelect
|
||||
onSelection={async (f) => {
|
||||
setFile(f)
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
Reference in New Issue
Block a user