|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
import Head from 'next/head'
|
|
|
|
|
import React, { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
|
|
|
import { cssTransition, toast, ToastContainer } from 'react-toastify'
|
|
|
|
|
import 'react-toastify/dist/ReactToastify.css';
|
|
|
|
|
import 'react-toastify/dist/ReactToastify.css'
|
|
|
|
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
|
|
|
import styled from 'styled-components'
|
|
|
|
|
import { useRouter } from 'next/router'
|
|
|
|
@ -64,9 +64,7 @@ import Checkbox from '../components/controls/checkbox'
|
|
|
|
|
import { getStorage } from '../data/storage/storage'
|
|
|
|
|
import { getUserSetting } from '../data/user/settings'
|
|
|
|
|
import { useReload } from '../hooks/useReload'
|
|
|
|
|
import {
|
|
|
|
|
BackendURLTagSearch,
|
|
|
|
|
} from '../globals/constants'
|
|
|
|
|
import { BackendURLTagSearch } from '../globals/constants'
|
|
|
|
|
import Tooltip from '../components/tooltip'
|
|
|
|
|
import { WorkerInterface } from '../tokenizer/interface'
|
|
|
|
|
import { EncoderType } from '../tokenizer/encoder'
|
|
|
|
@ -98,7 +96,7 @@ export const ToastArea = styled.div`
|
|
|
|
|
--toastify-color-progress-light: ${(props) => props.theme.colors.textHeadings};
|
|
|
|
|
--toastify-text-color-light: ${(props) => props.theme.colors.textMain};
|
|
|
|
|
--toastify-font-family: ${(props) => props.theme.fonts.default};
|
|
|
|
|
`;
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const MOBILE_WIDTH = 900
|
|
|
|
|
|
|
|
|
@ -144,7 +142,7 @@ const parsePrompt = (prompt: string[]): string => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const maxSamplesForSize = (width: number, height: number, max?: number): number => {
|
|
|
|
|
let limit = 100;
|
|
|
|
|
const limit = 100
|
|
|
|
|
if (max) return Math.min(limit, max)
|
|
|
|
|
return limit
|
|
|
|
|
}
|
|
|
|
@ -487,24 +485,27 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}, [savedPrompt])
|
|
|
|
|
|
|
|
|
|
// Queue remaining toast
|
|
|
|
|
const queueToastId = React.useRef<any>(null);
|
|
|
|
|
const queueToastId = React.useRef<any>(null)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const msg = `Task is lining up, ${queuePos} task${queuePos > 1 ? 's' : ''} remaining ahead`;
|
|
|
|
|
if (queuePos === 0 && queueToastId.current) { // hide toast
|
|
|
|
|
const msg = `Task is lining up, ${queuePos} task${queuePos > 1 ? 's' : ''} remaining ahead`
|
|
|
|
|
if (queuePos === 0 && queueToastId.current) {
|
|
|
|
|
// hide toast
|
|
|
|
|
toast.dismiss(queueToastId.current)
|
|
|
|
|
queueToastId.current = null
|
|
|
|
|
} else if (queuePos > 0) {
|
|
|
|
|
if (queueToastId.current) { // update toast
|
|
|
|
|
if (queueToastId.current) {
|
|
|
|
|
// update toast
|
|
|
|
|
toast.update(queueToastId.current, {
|
|
|
|
|
render: msg
|
|
|
|
|
render: msg,
|
|
|
|
|
})
|
|
|
|
|
} else { // show toast
|
|
|
|
|
} else {
|
|
|
|
|
// show toast
|
|
|
|
|
queueToastId.current = toast.loading(msg, {
|
|
|
|
|
autoClose: false,
|
|
|
|
|
draggable: false,
|
|
|
|
|
closeButton: false,
|
|
|
|
|
toastId: 'queueToast',
|
|
|
|
|
position: toast.POSITION.TOP_CENTER
|
|
|
|
|
position: toast.POSITION.TOP_CENTER,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -560,8 +561,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
|
|
|
|
|
const [purchaseModalOpen, setPurchaseModalOpen] = useState(false)
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
}, [
|
|
|
|
|
useEffect(() => {}, [
|
|
|
|
|
params,
|
|
|
|
|
selectedModel,
|
|
|
|
|
session,
|
|
|
|
@ -674,9 +674,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
fetch(
|
|
|
|
|
BackendURLTagSearch +
|
|
|
|
|
//`?model=${encodeURIComponent(selectedModel.toString())}&prompt=${encodeURIComponent(
|
|
|
|
|
`?prompt=${encodeURIComponent(
|
|
|
|
|
prompt.trim()
|
|
|
|
|
)}`,
|
|
|
|
|
`?prompt=${encodeURIComponent(prompt.trim())}`,
|
|
|
|
|
{
|
|
|
|
|
mode: 'cors',
|
|
|
|
|
cache: 'default',
|
|
|
|
@ -695,9 +693,12 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
logError(error)
|
|
|
|
|
toast.error(`Error: ${error.message} - Unable to reach Naifu servers. Please wait for a moment and try again`, {
|
|
|
|
|
toastId: 'promptSuggesionError'
|
|
|
|
|
})
|
|
|
|
|
toast.error(
|
|
|
|
|
`Error: ${error.message} - Unable to reach Naifu servers. Please wait for a moment and try again`,
|
|
|
|
|
{
|
|
|
|
|
toastId: 'promptSuggesionError',
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
setSearchingTags(false)
|
|
|
|
@ -784,7 +785,11 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
if (!prompt[lastFocusedPrompt].slice(cursorPosition)?.trim()) {
|
|
|
|
|
newPrompt += ', '
|
|
|
|
|
}
|
|
|
|
|
setPrompt([...prompt.slice(0, lastFocusedPrompt), newPrompt, ...prompt.slice(lastFocusedPrompt + 1)])
|
|
|
|
|
setPrompt([
|
|
|
|
|
...prompt.slice(0, lastFocusedPrompt),
|
|
|
|
|
newPrompt,
|
|
|
|
|
...prompt.slice(lastFocusedPrompt + 1),
|
|
|
|
|
])
|
|
|
|
|
if (promptid !== undefined) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const input = document.querySelector(`#prompt-input-${promptid}`) as HTMLInputElement
|
|
|
|
@ -1054,7 +1059,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
},
|
|
|
|
|
(error: any) => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
toast.error(error.message);
|
|
|
|
|
toast.error(error.message)
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
@ -1075,11 +1080,10 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
body: 'Image generation completed',
|
|
|
|
|
icon: newImages[0].url,
|
|
|
|
|
image: newImages[0].url,
|
|
|
|
|
tag: 'imagegen-complete'
|
|
|
|
|
})
|
|
|
|
|
notification.onclick = () => {
|
|
|
|
|
notification.addEventListener('click', () => {
|
|
|
|
|
window.focus()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1174,11 +1178,10 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
body: 'Image generation completed',
|
|
|
|
|
icon: newImages[0].url,
|
|
|
|
|
image: newImages[0].url,
|
|
|
|
|
tag: 'imagegen-complete'
|
|
|
|
|
})
|
|
|
|
|
notification.onclick = () => {
|
|
|
|
|
notification.addEventListener('click', () => {
|
|
|
|
|
window.focus()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
resolve({ images: newImages, seeds: masks.map((m) => m.seed) })
|
|
|
|
@ -1425,9 +1428,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
</InputLabel>
|
|
|
|
|
<EnhanceButton
|
|
|
|
|
disabled={
|
|
|
|
|
generating ||
|
|
|
|
|
prompt.some((p) => !p) ||
|
|
|
|
|
!validateParameters(params, selectedModel)
|
|
|
|
|
generating || prompt.some((p) => !p) || !validateParameters(params, selectedModel)
|
|
|
|
|
}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setEnhanceBoxVisible(false)
|
|
|
|
@ -1787,10 +1788,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
<PenIcon style={{ width: 16, height: 16 }} />
|
|
|
|
|
</OverlayButton>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
<Tooltip
|
|
|
|
|
tooltip={`Generate Variations`}
|
|
|
|
|
delay={0}
|
|
|
|
|
>
|
|
|
|
|
<Tooltip tooltip={`Generate Variations`} delay={0}>
|
|
|
|
|
<OverlayButton
|
|
|
|
|
disabled={generating}
|
|
|
|
|
style={{
|
|
|
|
@ -1889,7 +1887,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}, 0)
|
|
|
|
|
const dupeNames: any = {}
|
|
|
|
|
const loadingToastId = toast.loading(`Downloading ${imageCount} images...`, {
|
|
|
|
|
autoClose: false
|
|
|
|
|
autoClose: false,
|
|
|
|
|
})
|
|
|
|
|
images.forEach((image, i) => {
|
|
|
|
|
image.forEach((img, j) => {
|
|
|
|
@ -2324,7 +2322,8 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
SD_TOKEN_LIMIT
|
|
|
|
|
}
|
|
|
|
|
style={{
|
|
|
|
|
height: `${((promptTokens[promptid] ?? 0) /
|
|
|
|
|
height: `${
|
|
|
|
|
((promptTokens[promptid] ?? 0) /
|
|
|
|
|
SD_TOKEN_LIMIT) *
|
|
|
|
|
100
|
|
|
|
|
}%`,
|
|
|
|
@ -2779,13 +2778,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{!downloadingImages ? (
|
|
|
|
|
'Download ZIP'
|
|
|
|
|
) : (
|
|
|
|
|
<span>
|
|
|
|
|
Downloading...
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{!downloadingImages ? 'Download ZIP' : <span>Downloading...</span>}
|
|
|
|
|
</SubtleButton>
|
|
|
|
|
</HistoryBar>
|
|
|
|
|
</Sidebar>
|
|
|
|
@ -3058,7 +3051,7 @@ const ExpandedImageContainer = styled(motion.div)`
|
|
|
|
|
align-items: center;
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const HistoryButton = styled(SubtleButton) <{ selected: boolean; img: string }>`
|
|
|
|
|
const HistoryButton = styled(SubtleButton)<{ selected: boolean; img: string }>`
|
|
|
|
|
border: ${(props) =>
|
|
|
|
|
props.selected ? `2px solid ${props.theme.colors.textHeadings}` : `2px solid transparent`};
|
|
|
|
|
border-radius: 3px;
|
|
|
|
@ -3298,7 +3291,7 @@ function GenerationOptions(props: {
|
|
|
|
|
}, [props.notiEnabled])
|
|
|
|
|
|
|
|
|
|
const setNotiEnabled = useCallback((b: boolean) => {
|
|
|
|
|
if (typeof window !== "undefined") {
|
|
|
|
|
if (typeof window !== 'undefined') {
|
|
|
|
|
if (window.Notification.permission === 'denied') {
|
|
|
|
|
toast.warn("You've disabled Notification in browser settings.")
|
|
|
|
|
} else {
|
|
|
|
@ -3330,7 +3323,7 @@ function GenerationOptions(props: {
|
|
|
|
|
onMouseDown: (e: any) => {
|
|
|
|
|
if (e.target !== document.activeElement) {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
; (e.target as HTMLInputElement).select()
|
|
|
|
|
;(e.target as HTMLInputElement).select()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
@ -3895,7 +3888,7 @@ const Input = styled.input<{ warn?: boolean }>`
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const LargeInput = styled(TextareaAutosize) <{ warn?: boolean }>`
|
|
|
|
|
const LargeInput = styled(TextareaAutosize)<{ warn?: boolean }>`
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
border: 1px solid ${(props) => (props.warn ? props.theme.colors.warning : props.theme.colors.bg3)};
|
|
|
|
|
border-radius: 3px;
|
|
|
|
@ -3938,7 +3931,7 @@ const GenerateButton = styled.button`
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
flex: .1 0 auto;
|
|
|
|
|
flex: 0.1 0 auto;
|
|
|
|
|
border: 1px solid ${(props) => props.theme.colors.textHeadings};
|
|
|
|
|
&:disabled {
|
|
|
|
|
color: ${(props) => props.theme.colors.textHeadings};
|
|
|
|
@ -4914,7 +4907,7 @@ function Canvas(props: {
|
|
|
|
|
onMouseDown: (e: any) => {
|
|
|
|
|
if (e.target !== document.activeElement) {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
; (e.target as HTMLInputElement).select()
|
|
|
|
|
;(e.target as HTMLInputElement).select()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
@ -4973,24 +4966,27 @@ function Canvas(props: {
|
|
|
|
|
const [queuePos, setQueuePos] = useState(0)
|
|
|
|
|
|
|
|
|
|
// Queue remaining toast
|
|
|
|
|
const queueToastId = React.useRef<any>(null);
|
|
|
|
|
const queueToastId = React.useRef<any>(null)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const msg = `Task is lining up, ${queuePos} task${queuePos > 1 ? 's' : ''} remaining ahead`;
|
|
|
|
|
if (queuePos === 0 && queueToastId.current) { // hide toast
|
|
|
|
|
const msg = `Task is lining up, ${queuePos} task${queuePos > 1 ? 's' : ''} remaining ahead`
|
|
|
|
|
if (queuePos === 0 && queueToastId.current) {
|
|
|
|
|
// hide toast
|
|
|
|
|
toast.dismiss(queueToastId.current)
|
|
|
|
|
queueToastId.current = null
|
|
|
|
|
} else if (queuePos > 0) {
|
|
|
|
|
if (queueToastId.current) { // update toast
|
|
|
|
|
if (queueToastId.current) {
|
|
|
|
|
// update toast
|
|
|
|
|
toast.update(queueToastId.current, {
|
|
|
|
|
render: msg
|
|
|
|
|
render: msg,
|
|
|
|
|
})
|
|
|
|
|
} else { // show toast
|
|
|
|
|
} else {
|
|
|
|
|
// show toast
|
|
|
|
|
queueToastId.current = toast.loading(msg, {
|
|
|
|
|
autoClose: false,
|
|
|
|
|
draggable: false,
|
|
|
|
|
closeButton: false,
|
|
|
|
|
toastId: 'queueToast',
|
|
|
|
|
position: toast.POSITION.TOP_CENTER
|
|
|
|
|
position: toast.POSITION.TOP_CENTER,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -5886,7 +5882,7 @@ const Filler = styled.div`
|
|
|
|
|
height: 30px;
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const CanvasControlButton = styled(SubtleButton) <{ selected?: boolean }>`
|
|
|
|
|
const CanvasControlButton = styled(SubtleButton)<{ selected?: boolean }>`
|
|
|
|
|
padding: 8px;
|
|
|
|
|
height: 44px;
|
|
|
|
|
min-width: 58px;
|
|
|
|
|