Spaces:
Configuration error
Configuration error
import { FolderIcon, PhotoIcon } from '@heroicons/react/24/outline' | |
import { PlayIcon, ReloadIcon } from '@radix-ui/react-icons' | |
import React, { useCallback, useState } from 'react' | |
import { useRecoilState, useRecoilValue } from 'recoil' | |
import * as PopoverPrimitive from '@radix-ui/react-popover' | |
import { | |
enableFileManagerState, | |
fileState, | |
isInpaintingState, | |
isPix2PixState, | |
isSDState, | |
maskState, | |
runManuallyState, | |
showFileManagerState, | |
} from '../../store/Atoms' | |
import Button from '../shared/Button' | |
import Shortcuts from '../Shortcuts/Shortcuts' | |
import { ThemeChanger } from './ThemeChanger' | |
import SettingIcon from '../Settings/SettingIcon' | |
import PromptInput from './PromptInput' | |
import CoffeeIcon from '../CoffeeIcon/CoffeeIcon' | |
import emitter, { EVENT_CUSTOM_MASK, RERUN_LAST_MASK } from '../../event' | |
import { useImage } from '../../utils' | |
import useHotKey from '../../hooks/useHotkey' | |
const Header = () => { | |
const isInpainting = useRecoilValue(isInpaintingState) | |
const [file, setFile] = useRecoilState(fileState) | |
const [mask, setMask] = useRecoilState(maskState) | |
const [maskImage, maskImageLoaded] = useImage(mask) | |
const [uploadElemId] = useState(`file-upload-${Math.random().toString()}`) | |
const [maskUploadElemId] = useState(`mask-upload-${Math.random().toString()}`) | |
const isSD = useRecoilValue(isSDState) | |
const isPix2Pix = useRecoilValue(isPix2PixState) | |
const runManually = useRecoilValue(runManuallyState) | |
const [openMaskPopover, setOpenMaskPopover] = useState(false) | |
const [showFileManager, setShowFileManager] = | |
useRecoilState(showFileManagerState) | |
const enableFileManager = useRecoilValue(enableFileManagerState) | |
useHotKey( | |
'f', | |
() => { | |
if (enableFileManager && !isInpainting) { | |
setShowFileManager(!showFileManager) | |
} | |
}, | |
{}, | |
[showFileManager, enableFileManager, isInpainting] | |
) | |
const handleRerunLastMask = useCallback(() => { | |
emitter.emit(RERUN_LAST_MASK) | |
}, []) | |
useHotKey( | |
'r', | |
() => { | |
if (!isInpainting) { | |
handleRerunLastMask() | |
} | |
}, | |
{}, | |
[isInpainting, handleRerunLastMask] | |
) | |
const renderHeader = () => { | |
return ( | |
<header> | |
<div | |
style={{ | |
display: 'flex', | |
justifyContent: 'center', | |
alignItems: 'center', | |
gap: 4, | |
}} | |
> | |
{enableFileManager ? ( | |
<Button | |
icon={<FolderIcon />} | |
style={{ border: 0 }} | |
toolTip="Open File Manager" | |
disabled={isInpainting} | |
onClick={() => { | |
setShowFileManager(true) | |
}} | |
/> | |
) : ( | |
<></> | |
)} | |
<label htmlFor={uploadElemId}> | |
<Button | |
icon={<PhotoIcon />} | |
style={{ border: 0, gap: 0 }} | |
disabled={isInpainting} | |
toolTip="Upload image" | |
> | |
<input | |
style={{ display: 'none' }} | |
id={uploadElemId} | |
name={uploadElemId} | |
type="file" | |
onChange={ev => { | |
const newFile = ev.currentTarget.files?.[0] | |
if (newFile) { | |
setFile(newFile) | |
} | |
}} | |
accept="image/png, image/jpeg" | |
/> | |
</Button> | |
</label> | |
<div | |
style={{ | |
visibility: file ? 'visible' : 'hidden', | |
display: 'flex', | |
justifyContent: 'center', | |
alignItems: 'center', | |
}} | |
> | |
<label htmlFor={maskUploadElemId}> | |
<Button | |
style={{ border: 0 }} | |
disabled={isInpainting} | |
toolTip="Upload custom mask" | |
> | |
<input | |
style={{ display: 'none' }} | |
id={maskUploadElemId} | |
name={maskUploadElemId} | |
type="file" | |
onClick={e => { | |
const element = e.target as HTMLInputElement | |
element.value = '' | |
}} | |
onChange={ev => { | |
const newFile = ev.currentTarget.files?.[0] | |
if (newFile) { | |
setMask(newFile) | |
console.info('Send custom mask') | |
if (!runManually) { | |
emitter.emit(EVENT_CUSTOM_MASK, { mask: newFile }) | |
} | |
} | |
}} | |
accept="image/png, image/jpeg" | |
/> | |
Mask | |
</Button> | |
</label> | |
{mask ? ( | |
<PopoverPrimitive.Root open={openMaskPopover}> | |
<PopoverPrimitive.Trigger | |
className="btn-primary side-panel-trigger" | |
onMouseEnter={() => setOpenMaskPopover(true)} | |
onMouseLeave={() => setOpenMaskPopover(false)} | |
style={{ | |
visibility: mask ? 'visible' : 'hidden', | |
outline: 'none', | |
}} | |
onClick={() => { | |
if (mask) { | |
emitter.emit(EVENT_CUSTOM_MASK, { mask }) | |
} | |
}} | |
> | |
<PlayIcon /> | |
</PopoverPrimitive.Trigger> | |
<PopoverPrimitive.Portal> | |
<PopoverPrimitive.Content | |
style={{ | |
outline: 'none', | |
}} | |
> | |
{maskImageLoaded ? ( | |
<img | |
src={maskImage.src} | |
alt="mask" | |
className="mask-preview" | |
/> | |
) : ( | |
<></> | |
)} | |
</PopoverPrimitive.Content> | |
</PopoverPrimitive.Portal> | |
</PopoverPrimitive.Root> | |
) : ( | |
<></> | |
)} | |
<Button | |
icon={<ReloadIcon style={{ height: 16, width: 16 }} />} | |
style={{ border: 0, gap: 0 }} | |
disabled={isInpainting} | |
toolTip="Rerun last mask [r]" | |
onClick={handleRerunLastMask} | |
/> | |
</div> | |
</div> | |
{(isSD || isPix2Pix) && file ? <PromptInput /> : <></>} | |
<div className="header-icons-wrapper"> | |
<CoffeeIcon /> | |
<ThemeChanger /> | |
<div className="header-icons"> | |
<Shortcuts /> | |
<SettingIcon /> | |
</div> | |
</div> | |
</header> | |
) | |
} | |
return renderHeader() | |
} | |
export default Header | |