Jon Taylor
added waiting message
2ae2b29
raw
history blame
7.6 kB
"use client";
import {
Accordion,
ActionIcon,
LoadingOverlay,
SegmentedControl,
Textarea,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import {
IconArrowDownRight,
IconMinus,
IconPlus,
IconRobot,
IconRotateClockwise,
} from "@tabler/icons-react";
import React, { useCallback, useEffect, useRef } from "react";
import useDebounce from "../hooks/debounce";
import FieldSet from "./FieldSet";
import Label from "./Label";
import SelectInput from "./SelectInput";
import SlideInput from "./SliderInput";
import TextInput from "./TextInput";
export const Controls = React.memo(({ remoteParams, onData }) => {
const init = useRef(false);
const form = useForm({
initialValues: {
prompt: "",
negative_prompt: "",
steps: 1,
guidance: 0.1,
seed: 45678,
resolution: "720",
},
});
const debounce = useDebounce();
const handleChange = useCallback((v) => onData(v), [onData]);
useEffect(() => {
if (!remoteParams) return;
if (!init.current) {
form.setValues(remoteParams);
init.current = true;
}
}, [form, remoteParams]);
useEffect(() => {
if (!form) return;
form.isDirty() && debounce(() => handleChange(form.values), 500);
}, [form, debounce, handleChange]);
return (
<div className="relative flex flex-col gap-4">
{!remoteParams && (
<LoadingOverlay
visible={true}
zIndex={1000}
overlayProps={{ blur: 2 }}
loaderProps={{
children: (
<div className="animate-pulse font-medium text-center">
<IconRobot
size={42}
stroke={1}
className="mx-auto mb-2 text-indigo-600"
/>
<div>Waiting for bot to initialize...</div>
<div className="text-sm font-normal text-zinc-600">
It may take a few minutes
</div>
</div>
),
}}
className="h-screen"
/>
)}
<Accordion
defaultValue="prompt"
classNames={{
chevron: "w-[20px] text-indigo-500",
label: "font-medium text-zinc-900",
control: "hover:bg-zinc-50",
item: "border-zinc-200",
}}
chevron={<IconArrowDownRight size={20} stroke={2} className="block" />}
>
<Accordion.Item value="prompt" key="prompt">
<Accordion.Control value="prompt">Prompt</Accordion.Control>
<Accordion.Panel>
<FieldSet>
<div className="flex flex-col">
<div className="flex flex-row items-center gap-2 mb-2">
<IconPlus size={20} stroke={1} className="text-emerald-500" />
<span className="font-mono uppercase text-xs tracking-wider text-emerald-600">
Positive Prompt
</span>
</div>
<Textarea
autosize
className="rounded-md"
minRows={3}
defaultValue={form.getInputProps("prompt").value}
onChange={(e) => form.setFieldValue("prompt", e.target.value)}
classNames={{
input:
"p-3 font-mono text-emerald-800 bg-emerald-600/[0.07] border-emerald-500/30 focus:border-emerald-500 focus:ring-1 focus:ring-inset focus:ring-emerald-500",
}}
/>
</div>
<div className="flex flex-col">
<div className="flex flex-row items-center gap-2 mb-2">
<IconMinus size={20} stroke={1} className="text-rose-500" />
<span className="font-mono uppercase text-xs tracking-wider text-rose-600">
Negative Prompt
</span>
</div>
<Textarea
autosize
className="rounded-md"
minRows={3}
defaultValue={form.getInputProps("negative_prompt").value}
onChange={(e) =>
form.setFieldValue("negative_prompt", e.target.value)
}
classNames={{
input:
"p-3 font-mono text-rose-800 bg-rose-600/[0.07] border-rose-500/30 focus:border-rose-500 focus:ring-1 focus:ring-inset focus:ring-rose-500",
}}
/>
</div>
</FieldSet>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value="camera" key="camera">
<Accordion.Control value="camera">Camera</Accordion.Control>
<Accordion.Panel>
<FieldSet>
<SelectInput label="Device" />
<div>
<Label>Resolution</Label>
<SegmentedControl
fullWidth
size="sm"
radius="xl"
defaultValue={"720"}
data={[
{ label: "480P", value: "480" },
{ label: "720P", value: "720" },
{ label: "1080P", value: "1080" },
]}
onChange={(v) => form.setFieldValue("resolution", v)}
/>
</div>
</FieldSet>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value="diffusion" key="diffusion">
<Accordion.Control value="inference">Diffusion</Accordion.Control>
<Accordion.Panel>
<div className="flex flex-col gap-4">
<TextInput
label="Seed"
type="number"
fieldProps={form.getInputProps("seed")}
>
<ActionIcon
radius="sm"
variant="subtle"
onClick={() =>
form.setFieldValue(
"seed",
Math.floor(Math.random() * 100000001)
)
}
>
<IconRotateClockwise
style={{ width: "70%", height: "70%" }}
stroke={2}
/>
</ActionIcon>
</TextInput>
<SlideInput
label="Steps"
fieldProps={form.getInputProps("steps")}
step={1}
min={1}
max={15}
marks={[
{ value: 1, label: "Low" },
{ value: 15, label: "High" },
]}
/>
<SlideInput
label="Guidance"
fieldProps={form.getInputProps("guidance")}
step={0.001}
min={0}
max={30}
marks={[
{ value: 0, label: "None" },
{ value: 2.5, label: "Low" },
{ value: 7.5, label: "Normal" },
{ value: 12.5, label: "Strict" },
{ value: 17.5, label: "Very Strict" },
{ value: 30, label: "Max" },
]}
/>
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value="cn" key="cn">
<Accordion.Control value="cn">Control Net</Accordion.Control>
<Accordion.Panel>Control net</Accordion.Panel>
</Accordion.Item>
</Accordion>
</div>
);
});
Controls.displayName = "Controls";
export default Controls;