|
|
|
@ -418,7 +418,7 @@ async function fillImageBackground(
|
|
|
|
|
canvas.height = height || image.height
|
|
|
|
|
const ctx = canvas.getContext('2d')
|
|
|
|
|
if (!ctx) {
|
|
|
|
|
toast('Could not create canvas context')
|
|
|
|
|
toast.error('Could not create canvas context')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ctx.fillStyle = color
|
|
|
|
@ -455,14 +455,14 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}, [images.length])
|
|
|
|
|
const [generating, setGenerating] = useState(false)
|
|
|
|
|
const [progress, setProgress] = useState(0)
|
|
|
|
|
const [queuePos, setQueuePos] = useState<number>(0)
|
|
|
|
|
const [queuePos, setQueuePos] = useState(0)
|
|
|
|
|
const [selectedImage, setSelectedImage] = useState(0)
|
|
|
|
|
const [session, setSession] = useRecoilState(Session)
|
|
|
|
|
|
|
|
|
|
const [params, actualSetParams] = useState<any>(
|
|
|
|
|
getModelDefaultParams(ImageGenerationModels.stableDiffusion)
|
|
|
|
|
)
|
|
|
|
|
const [prompt, setPrompt] = useState([''] as string[])
|
|
|
|
|
const [prompt, setPrompt] = useRememberedValue('imagegen-prompt', [''] as string[])
|
|
|
|
|
const [promptLines, setPromptLines] = useState([1] as number[])
|
|
|
|
|
const [negPrompt, setNegPrompt] = useRememberedValue('imagegen-negativeprompt', '')
|
|
|
|
|
|
|
|
|
@ -493,7 +493,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
const didMessageNoAccount = useRef(false)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (session.noAccount && !didMessageNoAccount.current) {
|
|
|
|
|
toast('An account is required to generate images.')
|
|
|
|
|
toast.warn('An account is required to generate images.')
|
|
|
|
|
didMessageNoAccount.current = true
|
|
|
|
|
}
|
|
|
|
|
}, [session])
|
|
|
|
@ -582,7 +582,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (promptTokens.some((t) => t > SD_TOKEN_LIMIT) && lastPromptTokensGood.current) {
|
|
|
|
|
lastPromptTokensGood.current = false
|
|
|
|
|
toast(
|
|
|
|
|
toast.warn(
|
|
|
|
|
'Prompt is too long and will be cut off. Using ' +
|
|
|
|
|
promptTokens +
|
|
|
|
|
' out of ' +
|
|
|
|
@ -624,7 +624,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (negPromptTokens > SD_TOKEN_LIMIT && lastNegPromptTokensGood.current) {
|
|
|
|
|
lastNegPromptTokensGood.current = false
|
|
|
|
|
toast(
|
|
|
|
|
toast.warn(
|
|
|
|
|
'Negative prompt is too long and will be cut off. Using ' +
|
|
|
|
|
negPromptTokens +
|
|
|
|
|
' out of ' +
|
|
|
|
@ -675,6 +675,9 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
logError(error)
|
|
|
|
|
toast.error(`Error: ${error.message} - Unable to reach NovelAI servers. Please wait for a moment and try again`, {
|
|
|
|
|
toastId: 'promptSuggesionError'
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
setSearchingTags(false)
|
|
|
|
@ -761,9 +764,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
if (!prompt[lastFocusedPrompt].slice(cursorPosition)?.trim()) {
|
|
|
|
|
newPrompt += ', '
|
|
|
|
|
}
|
|
|
|
|
setPrompt((v) => {
|
|
|
|
|
return [...v.slice(0, lastFocusedPrompt), newPrompt, ...v.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
|
|
|
|
@ -893,7 +894,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (session.noAccount) {
|
|
|
|
|
toast('An account is required to generate images.')
|
|
|
|
|
toast.warn('An account is required to generate images.')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
@ -946,7 +947,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!validateParameters(tempParams, selectedModel)) {
|
|
|
|
|
toast('Invalid parameters')
|
|
|
|
|
toast.error('Invalid parameters')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1033,7 +1034,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
},
|
|
|
|
|
(error: any) => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
toast(error.message);
|
|
|
|
|
toast.error(error.message);
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
@ -1052,7 +1053,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
toast('Error generating image: ' + error.message ?? error)
|
|
|
|
|
toast.error('Error generating image: ' + error.message ?? error)
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1127,7 +1128,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
},
|
|
|
|
|
(error: any) => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
toast(error.message ?? error)
|
|
|
|
|
toast.error(error.message ?? error)
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
@ -1140,7 +1141,7 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
} catch {
|
|
|
|
|
toast('Error generating image')
|
|
|
|
|
toast.error('Error generating image')
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
reject()
|
|
|
|
|
return
|
|
|
|
@ -1612,10 +1613,10 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
if (!img.data) return
|
|
|
|
|
copyPngToClipboard(img.data)
|
|
|
|
|
.then(() => {
|
|
|
|
|
toast('Image copied to clipboard')
|
|
|
|
|
toast.success('Image copied to clipboard')
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
toast('Error copying image to clipboard: ' + error)
|
|
|
|
|
toast.error('Error copying image to clipboard: ' + error)
|
|
|
|
|
})
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
@ -1843,7 +1844,9 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
return acc + image.length
|
|
|
|
|
}, 0)
|
|
|
|
|
const dupeNames: any = {}
|
|
|
|
|
toast(`Downloading ${imageCount} images...`)
|
|
|
|
|
const loadingToastId = toast.loading(`Downloading ${imageCount} images...`, {
|
|
|
|
|
autoClose: false
|
|
|
|
|
})
|
|
|
|
|
images.forEach((image, i) => {
|
|
|
|
|
image.forEach((img, j) => {
|
|
|
|
|
if (img.isVariationOriginal) return
|
|
|
|
@ -1862,7 +1865,8 @@ function ImageGenContent(): JSX.Element {
|
|
|
|
|
}).then(function (content) {
|
|
|
|
|
saveAs(content, 'images.zip')
|
|
|
|
|
setDownloadingImages(false)
|
|
|
|
|
toast('Images downloaded')
|
|
|
|
|
toast.dismiss(loadingToastId)
|
|
|
|
|
toast.success('Images downloaded')
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3865,7 +3869,7 @@ const GenerateButton = styled.button`
|
|
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding-left: 20px;
|
|
|
|
|
padding-right: 20px;
|
|
|
|
@ -4776,7 +4780,7 @@ function Canvas(props: {
|
|
|
|
|
const newWidth = width + right + left
|
|
|
|
|
const newHeight = height + bottom + top
|
|
|
|
|
if (newWidth < 128 || newHeight < 128) {
|
|
|
|
|
toast('Canvas dimensions must be at least 128x128')
|
|
|
|
|
toast.warn('Canvas dimensions must be at least 128x128')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// transfer old canvas content to new canvas
|
|
|
|
@ -4877,8 +4881,8 @@ function Canvas(props: {
|
|
|
|
|
}, [canvasInternalSize, resizeModalOpen])
|
|
|
|
|
|
|
|
|
|
const [generating, setGenerating] = useState(false)
|
|
|
|
|
const [progress, setProgress] = useState<number>(0)
|
|
|
|
|
const [queuePos, setQueuePos] = useState<number>(0)
|
|
|
|
|
const [progress, setProgress] = useState(0)
|
|
|
|
|
const [queuePos, setQueuePos] = useState(0)
|
|
|
|
|
|
|
|
|
|
// Queue remaining toast
|
|
|
|
|
const queueToastId = React.useRef<any>(null);
|
|
|
|
@ -5166,7 +5170,7 @@ function Canvas(props: {
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
setGenerating(false)
|
|
|
|
|
toast(
|
|
|
|
|
toast.error(
|
|
|
|
|
(error?.message ?? error).replace(/training steps/g, 'Anlas')
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
@ -5908,11 +5912,11 @@ function ImageGenImportOverlay(props: { onFileImport: (file: Buffer) => void })
|
|
|
|
|
const buffer = Buffer.from(result.value)
|
|
|
|
|
props.onFileImport(buffer)
|
|
|
|
|
} else {
|
|
|
|
|
toast('Something went wrong while importing a file.')
|
|
|
|
|
toast.error('Something went wrong while importing a file.')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
toast('Something went wrong while importing a file: ' + error.message ?? error)
|
|
|
|
|
toast.error('Something went wrong while importing a file: ' + error.message ?? error)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|