add file manager
This commit is contained in:
@@ -30,7 +30,8 @@
|
||||
"react-dom": "^17.0.2",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-hotkeys-hook": "^3.4.7",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-photo-album": "^2.0.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-use": "^17.3.1",
|
||||
"react-zoom-pan-pinch": "^2.1.3",
|
||||
"recoil": "^0.6.1",
|
||||
@@ -38,7 +39,7 @@
|
||||
"typescript": "4.x"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"start": "cross-env GENERATE_SOURCEMAP=false react-scripts start",
|
||||
"build": "cross-env GENERATE_SOURCEMAP=false react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
@@ -65,8 +66,8 @@
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-react": "^7.27.1",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"prettier": "^2.4.1",
|
||||
"sass": "^1.49.9"
|
||||
}
|
||||
|
||||
@@ -164,3 +164,16 @@ export async function postInteractiveSeg(
|
||||
throw new Error(`Something went wrong: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMediaFile(filename: string) {
|
||||
const res = await fetch(`${API_ENDPOINT}/media/${filename}`, {
|
||||
method: 'GET',
|
||||
})
|
||||
if (res.ok) {
|
||||
const blob = await res.blob()
|
||||
const file = new File([blob], filename)
|
||||
return file
|
||||
}
|
||||
const errMsg = await res.text()
|
||||
throw new Error(errMsg)
|
||||
}
|
||||
|
||||
27
lama_cleaner/app/src/components/FileManager/FileManager.scss
Normal file
27
lama_cleaner/app/src/components/FileManager/FileManager.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
.file-manager-modal {
|
||||
color: var(--text-color);
|
||||
height: 90%;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.file-manager {
|
||||
overflow: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.react-photo-album.react-photo-album--columns {
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.react-photo-album--photo {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
|
||||
// transform-origin: 0 0;
|
||||
transition: transform 0.25s, visibility 0.25s ease-in;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid var(--border-color);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
}
|
||||
90
lama_cleaner/app/src/components/FileManager/FileManager.tsx
Normal file
90
lama_cleaner/app/src/components/FileManager/FileManager.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React, { ReactNode, useEffect, useMemo, useState } from 'react'
|
||||
import PhotoAlbum, { RenderPhoto } from 'react-photo-album'
|
||||
import Modal from '../shared/Modal'
|
||||
|
||||
interface Photo {
|
||||
src: string
|
||||
height: number
|
||||
width: number
|
||||
}
|
||||
|
||||
interface Filename {
|
||||
name: string
|
||||
height: number
|
||||
width: number
|
||||
}
|
||||
|
||||
const renderPhoto: RenderPhoto = ({
|
||||
layout,
|
||||
layoutOptions,
|
||||
imageProps: { alt, style, ...restImageProps },
|
||||
}) => (
|
||||
<div
|
||||
style={{
|
||||
boxSizing: 'content-box',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={alt}
|
||||
style={{ ...style, width: '100%', padding: 0 }}
|
||||
{...restImageProps}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
onClose: () => void
|
||||
onPhotoClick: (filename: string) => void
|
||||
}
|
||||
|
||||
export default function FileManager(props: Props) {
|
||||
const { show, onClose, onPhotoClick } = props
|
||||
const [filenames, setFileNames] = useState<Filename[]>([])
|
||||
|
||||
const onClick = ({ index }: { index: number }) => {
|
||||
onPhotoClick(filenames[index].name)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const res = await fetch('/medias')
|
||||
if (res.ok) {
|
||||
const newFilenames = await res.json()
|
||||
setFileNames(newFilenames)
|
||||
}
|
||||
}
|
||||
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const photos = useMemo(() => {
|
||||
return filenames.map((filename: Filename) => {
|
||||
const width = 256
|
||||
const height = filename.height * (width / filename.width)
|
||||
const src = `/media_thumbnail/${filename.name}?width=${width}&height=${height}`
|
||||
return { src, height, width }
|
||||
})
|
||||
}, [filenames])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={onClose}
|
||||
title="Files"
|
||||
className="file-manager-modal"
|
||||
show={show}
|
||||
>
|
||||
<div className="file-manager">
|
||||
<PhotoAlbum
|
||||
layout="columns"
|
||||
photos={photos}
|
||||
renderPhoto={renderPhoto}
|
||||
spacing={6}
|
||||
padding={4}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArrowUpTrayIcon } from '@heroicons/react/24/outline'
|
||||
import { FolderIcon, PhotoIcon } from '@heroicons/react/24/outline'
|
||||
import { PlayIcon } from '@radix-ui/react-icons'
|
||||
import React, { useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
isSDState,
|
||||
maskState,
|
||||
runManuallyState,
|
||||
showFileManagerState,
|
||||
} from '../../store/Atoms'
|
||||
import Button from '../shared/Button'
|
||||
import Shortcuts from '../Shortcuts/Shortcuts'
|
||||
@@ -18,6 +19,7 @@ import PromptInput from './PromptInput'
|
||||
import CoffeeIcon from '../CoffeeIcon/CoffeeIcon'
|
||||
import emitter, { EVENT_CUSTOM_MASK } from '../../event'
|
||||
import { useImage } from '../../utils'
|
||||
import useHotKey from '../../hooks/useHotkey'
|
||||
|
||||
const Header = () => {
|
||||
const isInpainting = useRecoilValue(isInpaintingState)
|
||||
@@ -29,6 +31,17 @@ const Header = () => {
|
||||
const isSD = useRecoilValue(isSDState)
|
||||
const runManually = useRecoilValue(runManuallyState)
|
||||
const [openMaskPopover, setOpenMaskPopover] = useState(false)
|
||||
const [showFileManager, setShowFileManager] =
|
||||
useRecoilState(showFileManagerState)
|
||||
|
||||
useHotKey(
|
||||
'f',
|
||||
() => {
|
||||
setShowFileManager(!showFileManager)
|
||||
},
|
||||
{},
|
||||
[showFileManager]
|
||||
)
|
||||
|
||||
const renderHeader = () => {
|
||||
return (
|
||||
@@ -41,10 +54,20 @@ const Header = () => {
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
icon={<FolderIcon />}
|
||||
style={{ border: 0 }}
|
||||
toolTip="Open File Manager"
|
||||
tooltipPosition="bottom"
|
||||
onClick={() => {
|
||||
setShowFileManager(true)
|
||||
}}
|
||||
/>
|
||||
|
||||
<label htmlFor={uploadElemId}>
|
||||
<Button
|
||||
icon={<ArrowUpTrayIcon />}
|
||||
style={{ border: 0 }}
|
||||
icon={<PhotoIcon />}
|
||||
style={{ border: 0, gap: 0 }}
|
||||
disabled={isInpainting}
|
||||
toolTip="Upload image"
|
||||
tooltipPosition="bottom"
|
||||
@@ -62,7 +85,6 @@ const Header = () => {
|
||||
}}
|
||||
accept="image/png, image/jpeg"
|
||||
/>
|
||||
Image
|
||||
</Button>
|
||||
</label>
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ export default function ShortcutsModal() {
|
||||
<ShortCut content="Toggle Dark Mode" keys={['Shift', 'D']} />
|
||||
<ShortCut content="Toggle Hotkeys Dialog" keys={['H']} />
|
||||
<ShortCut content="Toggle Settings Dialog" keys={['S']} />
|
||||
<ShortCut content="Toggle File Manager" keys={['F']} />
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import Editor from './Editor/Editor'
|
||||
import ShortcutsModal from './Shortcuts/ShortcutsModal'
|
||||
@@ -10,15 +10,18 @@ import {
|
||||
isPaintByExampleState,
|
||||
isSDState,
|
||||
settingState,
|
||||
showFileManagerState,
|
||||
toastState,
|
||||
} from '../store/Atoms'
|
||||
import {
|
||||
currentModel,
|
||||
getMediaFile,
|
||||
modelDownloaded,
|
||||
switchModel,
|
||||
} from '../adapters/inpainting'
|
||||
import SidePanel from './SidePanel/SidePanel'
|
||||
import PESidePanel from './SidePanel/PESidePanel'
|
||||
import FileManager from './FileManager/FileManager'
|
||||
|
||||
const Workspace = () => {
|
||||
const [file, setFile] = useRecoilState(fileState)
|
||||
@@ -27,6 +30,9 @@ const Workspace = () => {
|
||||
const isSD = useRecoilValue(isSDState)
|
||||
const isPaintByExample = useRecoilValue(isPaintByExampleState)
|
||||
|
||||
const [showFileManager, setShowFileManager] =
|
||||
useRecoilState(showFileManagerState)
|
||||
|
||||
const onSettingClose = async () => {
|
||||
const curModel = await currentModel().then(res => res.text())
|
||||
if (curModel === settings.model) {
|
||||
@@ -92,6 +98,17 @@ const Workspace = () => {
|
||||
<>
|
||||
{isSD ? <SidePanel /> : <></>}
|
||||
{isPaintByExample ? <PESidePanel /> : <></>}
|
||||
<FileManager
|
||||
show={showFileManager}
|
||||
onClose={() => {
|
||||
setShowFileManager(false)
|
||||
}}
|
||||
onPhotoClick={async (filename: string) => {
|
||||
const newFile = await getMediaFile(filename)
|
||||
setFile(newFile)
|
||||
setShowFileManager(false)
|
||||
}}
|
||||
/>
|
||||
<Editor />
|
||||
<SettingModal onClose={onSettingClose} />
|
||||
<ShortcutsModal />
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
grid-auto-rows: max-content;
|
||||
row-gap: 1rem;
|
||||
place-self: center;
|
||||
padding: 2rem;
|
||||
padding: 25px;
|
||||
border-radius: 0.95rem;
|
||||
|
||||
&:focus {
|
||||
|
||||
@@ -41,6 +41,7 @@ interface AppState {
|
||||
isInteractiveSeg: boolean
|
||||
isInteractiveSegRunning: boolean
|
||||
interactiveSegClicks: number[][]
|
||||
showFileManager: boolean
|
||||
}
|
||||
|
||||
export const appState = atom<AppState>({
|
||||
@@ -53,6 +54,7 @@ export const appState = atom<AppState>({
|
||||
isInteractiveSeg: false,
|
||||
isInteractiveSegRunning: false,
|
||||
interactiveSegClicks: [],
|
||||
showFileManager: false,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -78,6 +80,18 @@ export const isInpaintingState = selector({
|
||||
},
|
||||
})
|
||||
|
||||
export const showFileManagerState = selector({
|
||||
key: 'showFileManager',
|
||||
get: ({ get }) => {
|
||||
const app = get(appState)
|
||||
return app.showFileManager
|
||||
},
|
||||
set: ({ get, set }, newValue: any) => {
|
||||
const app = get(appState)
|
||||
set(appState, { ...app, showFileManager: newValue })
|
||||
},
|
||||
})
|
||||
|
||||
export const fileState = selector({
|
||||
key: 'fileState',
|
||||
get: ({ get }) => {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// App
|
||||
@use './App';
|
||||
@use '../components/Editor/Editor';
|
||||
@use '../components/FileManager/FileManager';
|
||||
@use '../components/LandingPage/LandingPage';
|
||||
@use '../components/Header/Header';
|
||||
@use '../components/Header/PromptInput';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user