Spaces:
Runtime error
Runtime error
| "use client" | |
| import { useState } from "react" | |
| import { useLocalStorage } from 'usehooks-ts' | |
| import { Button } from "@/components/ui/button" | |
| import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" | |
| import { | |
| Select, | |
| SelectContent, | |
| SelectItem, | |
| SelectTrigger, | |
| SelectValue, | |
| } from "@/components/ui/select" | |
| import { RenderingModelVendor } from "@/types" | |
| import { Input } from "@/components/ui/input" | |
| import { Label } from "./label" | |
| import { Field } from "./field" | |
| import { localStorageKeys } from "./localStorageKeys" | |
| import { defaultSettings } from "./defaultSettings" | |
| import { useDynamicConfig } from "@/lib/useDynamicConfig" | |
| import { Slider } from "@/components/ui/slider" | |
| import { fonts } from "@/lib/fonts" | |
| export function SettingsDialog() { | |
| const [isOpen, setOpen] = useState(false) | |
| const [renderingModelVendor, setRenderingModelVendor] = useLocalStorage<RenderingModelVendor>( | |
| localStorageKeys.renderingModelVendor, | |
| defaultSettings.renderingModelVendor | |
| ) | |
| const [renderingUseTurbo, setRenderingUseTurbo] = useLocalStorage<boolean>( | |
| localStorageKeys.renderingUseTurbo, | |
| defaultSettings.renderingUseTurbo | |
| ) | |
| const [huggingfaceApiKey, setHuggingfaceApiKey] = useLocalStorage<string>( | |
| localStorageKeys.huggingfaceApiKey, | |
| defaultSettings.huggingfaceApiKey | |
| ) | |
| const [huggingfaceInferenceApiModel, setHuggingfaceInferenceApiModel] = useLocalStorage<string>( | |
| localStorageKeys.huggingfaceInferenceApiModel, | |
| defaultSettings.huggingfaceInferenceApiModel | |
| ) | |
| const [huggingfaceInferenceApiModelTrigger, setHuggingfaceInferenceApiModelTrigger] = useLocalStorage<string>( | |
| localStorageKeys.huggingfaceInferenceApiModelTrigger, | |
| defaultSettings.huggingfaceInferenceApiModelTrigger | |
| ) | |
| const [huggingfaceInferenceApiFileType, setHuggingfaceInferenceApiFileType] = useLocalStorage<string>( | |
| localStorageKeys.huggingfaceInferenceApiFileType, | |
| defaultSettings.huggingfaceInferenceApiFileType | |
| ) | |
| const [replicateApiKey, setReplicateApiKey] = useLocalStorage<string>( | |
| localStorageKeys.replicateApiKey, | |
| defaultSettings.replicateApiKey | |
| ) | |
| const [replicateApiModel, setReplicateApiModel] = useLocalStorage<string>( | |
| localStorageKeys.replicateApiModel, | |
| defaultSettings.replicateApiModel | |
| ) | |
| const [replicateApiModelVersion, setReplicateApiModelVersion] = useLocalStorage<string>( | |
| localStorageKeys.replicateApiModelVersion, | |
| defaultSettings.replicateApiModelVersion | |
| ) | |
| const [replicateApiModelTrigger, setReplicateApiModelTrigger] = useLocalStorage<string>( | |
| localStorageKeys.replicateApiModelTrigger, | |
| defaultSettings.replicateApiModelTrigger | |
| ) | |
| const [openaiApiKey, setOpenaiApiKey] = useLocalStorage<string>( | |
| localStorageKeys.openaiApiKey, | |
| defaultSettings.openaiApiKey | |
| ) | |
| const [openaiApiModel, setOpenaiApiModel] = useLocalStorage<string>( | |
| localStorageKeys.openaiApiModel, | |
| defaultSettings.openaiApiModel | |
| ) | |
| const [userDefinedMaxNumberOfPages, setUserDefinedMaxNumberOfPages] = useLocalStorage<number>( | |
| localStorageKeys.userDefinedMaxNumberOfPages, | |
| defaultSettings.userDefinedMaxNumberOfPages | |
| ) | |
| const { config: { maxNbPages }, isConfigReady } = useDynamicConfig() | |
| return ( | |
| <Dialog open={isOpen} onOpenChange={setOpen}> | |
| <DialogTrigger asChild> | |
| <Button className="space-x-1 md:space-x-2"> | |
| <div> | |
| <span className="hidden md:inline">Settings</span> | |
| </div> | |
| </Button> | |
| </DialogTrigger> | |
| <DialogContent className="w-full sm:max-w-[500px] md:max-w-[700px]"> | |
| <DialogHeader> | |
| <DialogDescription className="w-full text-center text-lg font-bold text-stone-800"> | |
| Settings | |
| </DialogDescription> | |
| </DialogHeader> | |
| <div className="overflow-y-scroll h-[75vh] md:h-[70vh]"> | |
| {isConfigReady && <Field> | |
| <Label>(new!) Control the number of pages: {userDefinedMaxNumberOfPages}</Label> | |
| <Slider | |
| min={1} | |
| max={maxNbPages} | |
| step={1} | |
| onValueChange={(value: any) => { | |
| let numericValue = Number(value[0]) | |
| numericValue = !isNaN(value[0]) && isFinite(value[0]) ? numericValue : 0 | |
| numericValue = Math.min(maxNbPages, Math.max(1, numericValue)) | |
| setUserDefinedMaxNumberOfPages(numericValue) | |
| }} | |
| defaultValue={[userDefinedMaxNumberOfPages]} | |
| value={[userDefinedMaxNumberOfPages]} | |
| /> | |
| </Field> | |
| } | |
| <div className="grid gap-4 pt-8 pb-1 space-y-1 text-stone-800"> | |
| <Field> | |
| <Label>Image rendering provider:</Label> | |
| <p className="pt-2 pb-3 text-base italic text-zinc-600"> | |
| ℹ️ Some API vendors have a delay for rarely used models.<br/> | |
| 👉 In case of trouble, try again after 5-10 minutes. | |
| </p> | |
| <Select | |
| onValueChange={(value: string) => { | |
| setRenderingModelVendor(value as RenderingModelVendor) | |
| }} | |
| defaultValue={renderingModelVendor}> | |
| <SelectTrigger className=""> | |
| <SelectValue placeholder="Theme" /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="SERVER">Use server settings (default)</SelectItem> | |
| <SelectItem value="HUGGINGFACE">Custom Hugging Face model (recommended)</SelectItem> | |
| <SelectItem value="REPLICATE">Custom Replicate model (will use your own account)</SelectItem> | |
| <SelectItem value="OPENAI">DALL·E 3 by OpenAI (partial support, will use your own account)</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </Field> | |
| { | |
| // renderingModelVendor === "SERVER" && <> | |
| // <Field> | |
| // <Label>Quality over performance ratio (beta, deprecated):</Label> | |
| // <div className="flex flex-row space-x-2 text-zinc-500"> | |
| // <Switch | |
| // // checked={renderingUseTurbo} | |
| // // onCheckedChange={setRenderingUseTurbo} | |
| // checked={false} | |
| // disabled | |
| // className="opacity-30 pointer-events-none" | |
| // /> | |
| // {/* | |
| // <span | |
| // onClick={() => setRenderingUseTurbo(!renderingUseTurbo)} | |
| // className={cn("cursor-pointer", { "text-zinc-800": renderingUseTurbo })}> | |
| // Use a faster, but lower quality model (you are warned!) | |
| // </span> | |
| // */} | |
| // <span className="text-zinc-500 italic"> | |
| // Following feedbacks from users (low rendering quality on comics) the fast renderer has been disabled. | |
| // </span> | |
| // </div> | |
| // </Field> | |
| // </> | |
| } | |
| {renderingModelVendor === "HUGGINGFACE" && <> | |
| <Field> | |
| <Label>Hugging Face API Token (<a className="text-stone-600 underline" href="https://huggingface.co/subscribe/pro" target="_blank">PRO account</a> recommended for higher rate limit):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| type="password" | |
| placeholder="Enter your private api token" | |
| onChange={(x) => { | |
| setHuggingfaceApiKey(x.target.value) | |
| }} | |
| value={huggingfaceApiKey} | |
| /> | |
| </Field> | |
| <Field> | |
| <Label>Inference API model (custom SDXL or SDXL LoRA):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder="Name of the Inference API model" | |
| onChange={(x) => { | |
| setHuggingfaceInferenceApiModel(x.target.value) | |
| }} | |
| value={huggingfaceInferenceApiModel} | |
| /> | |
| </Field> | |
| <Field> | |
| <Label>The file type supported by the model (jpg, webp..):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder="Inference API file type" | |
| onChange={(x) => { | |
| setHuggingfaceInferenceApiFileType(x.target.value) | |
| }} | |
| value={huggingfaceInferenceApiFileType} | |
| /> | |
| </Field> | |
| <p className="text-sm text-zinc-700"> | |
| Using a LoRA? Don't forget the trigger keyword! Also you will want to use the "Neutral" style. | |
| </p> | |
| <Field> | |
| <Label>LoRA model trigger (optional):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder="Trigger keyword (if you use a LoRA)" | |
| onChange={(x) => { | |
| setHuggingfaceInferenceApiModelTrigger(x.target.value) | |
| }} | |
| value={huggingfaceInferenceApiModelTrigger} | |
| /> | |
| </Field> | |
| </>} | |
| {renderingModelVendor === "OPENAI" && <> | |
| <Field> | |
| <Label>OpenAI API Token (you will be billed based on OpenAI pricing):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| type="password" | |
| placeholder="Enter your private api token" | |
| onChange={(x) => { | |
| setOpenaiApiKey(x.target.value) | |
| }} | |
| value={openaiApiKey} | |
| /> | |
| </Field> | |
| <Field> | |
| <Label>OpenAI image model:</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder="OpenAI image model" | |
| onChange={(x) => { | |
| setOpenaiApiModel(x.target.value) | |
| }} | |
| value={openaiApiModel} | |
| /> | |
| </Field> | |
| </>} | |
| {renderingModelVendor === "REPLICATE" && <> | |
| <Field> | |
| <Label>Replicate API Token (you will be billed based on Replicate pricing):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| type="password" | |
| placeholder="Enter your private api token" | |
| onChange={(x) => { | |
| setReplicateApiKey(x.target.value) | |
| }} | |
| value={replicateApiKey} | |
| /> | |
| </Field> | |
| <Field> | |
| <Label>Replicate model name:</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder="Name of the Replicate model" | |
| onChange={(x) => { | |
| setReplicateApiModel(x.target.value) | |
| }} | |
| value={replicateApiModel} | |
| /> | |
| </Field> | |
| <Field> | |
| <Label>Model version:</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder="Version of the Replicate model" | |
| onChange={(x) => { | |
| setReplicateApiModelVersion(x.target.value) | |
| }} | |
| value={replicateApiModelVersion} | |
| /> | |
| </Field> | |
| <p className="text-sm text-zinc-700"> | |
| Using a LoRA? Don't forget the trigger keyword! Also you will want to use the "Neutral" style. | |
| </p> | |
| <Field> | |
| <Label>LoRA model trigger (optional):</Label> | |
| <Input | |
| className={fonts.actionman.className} | |
| placeholder={'Eg. "In the style of TOK" etc'} | |
| onChange={(x) => { | |
| setReplicateApiModelTrigger(x.target.value) | |
| }} | |
| value={replicateApiModelTrigger} | |
| /> | |
| </Field> | |
| </>} | |
| <p className="text-sm text-zinc-700 italic"> | |
| 🔒 Settings such as API keys are stored inside your browser and aren't kept on our servers. | |
| </p> | |
| </div> | |
| </div> | |
| <DialogFooter> | |
| <Button type="submit" onClick={() => setOpen(false)}>Close</Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| ) | |
| } |