Skip to content

Commit

Permalink
Add initial Flux support including txt2img and LORA support
Browse files Browse the repository at this point in the history
  • Loading branch information
ramyma committed Aug 20, 2024
1 parent 6720d6c commit 82ef49f
Show file tree
Hide file tree
Showing 15 changed files with 446 additions and 66 deletions.
33 changes: 30 additions & 3 deletions assets/js/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ import ScrollArea from "./components/ScrollArea";
import useHistoryManager from "./hooks/useHistoryManager";
import useScripts from "./hooks/useScripts";
import { useAppDispatch, useAppSelector } from "./hooks";
import { OptionsState, selectBackend } from "./state/optionsSlice";
import {
OptionsState,
selectBackend,
selectSelectedModel,
} from "./state/optionsSlice";
import useBackend from "./hooks/useBackend";
import useSchedulers from "./hooks/useSchedulers";
import { ActionCreators as UndoActionCreators } from "redux-undo";
import { useCustomEventListener } from "react-custom-events";
import { HistoryItem } from "./state/historySlice";
import Button from "./components/Button";
import BatchImageResults from "./BatchImageResults";
import ClipModelSelect from "./MainForm/ClipModelSelect";

function App() {
const { refetch: refetchOptions } = useOptions({ fetchPolicy: "eager" });
Expand Down Expand Up @@ -76,17 +81,21 @@ function App() {
models,
setModel,
setVae,
setClipModel,
setClipModel2,
selectedModel,
selectedVae,
selectedClipModel,
selectedClipModel2,
fetchData: refetchModels,
fetchVaes: refetchVaes,
vaes,
clipModels,
} = useModels({
fetchPolicy: "eager",
});

useIsConnected();

// const panelRef = useRef<HTMLDivElement>(null);
// const { width } = useResize({ container: panelRef });

Expand Down Expand Up @@ -133,6 +142,20 @@ function App() {
isVaeLoading={isVaeLoading}
isModelLoading={isModelLoading}
/>
{selectedModel?.isFlux && (
<>
<ClipModelSelect
clipModels={clipModels}
setClipModel={setClipModel}
selectedClipModel={selectedClipModel}
/>
<ClipModelSelect
clipModels={clipModels}
setClipModel={setClipModel2}
selectedClipModel={selectedClipModel2}
/>
</>
)}
</div>
<MainForm />
</ScrollArea>
Expand Down Expand Up @@ -244,6 +267,7 @@ const VaeSelect = ({
isModelLoading: boolean;
}) => {
const backend = useAppSelector(selectBackend);
const selectedModel = useAppSelector(selectSelectedModel);

const handleVaeChange: SelectProps["onChange"] = useCallback(
(value) => {
Expand All @@ -261,7 +285,10 @@ const VaeSelect = ({
const title = "Select VAE";
const name = "vae";

const vaeList = useMemo(() => ["Automatic", ...(vaes ?? [])], [vaes]);
const vaeList = useMemo(
() => [...(!selectedModel?.isFlux ? ["Automatic"] : []), ...(vaes ?? [])],
[selectedModel, vaes]
);

return (
<>
Expand Down
42 changes: 42 additions & 0 deletions assets/js/src/MainForm/ClipModelSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useCallback } from "react";
import Select, { SelectProps } from "../components/Select";
import useBackend from "../hooks/useBackend";

type Props = {
clipModels: string[];
selectedClipModel?: string;
setClipModel: (clipModel: string) => void;
};

const ClipModelSelect = ({
clipModels,
selectedClipModel,
setClipModel,
}: Props) => {
const { backend } = useBackend();

const handleClipModelChange: SelectProps["onChange"] = useCallback(
(value) => {
if (value && (selectedClipModel || backend === "comfy"))
setClipModel(value as string);
},
[backend, selectedClipModel, setClipModel]
);

if (backend !== "comfy") return null;

const title = "Select Clip Model";

return (
<Select
id="comfy_vae"
items={clipModels}
name={name}
value={selectedClipModel}
onChange={handleClipModelChange}
title={title}
/>
);
};

export default ClipModelSelect;
135 changes: 86 additions & 49 deletions assets/js/src/MainForm/MainForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ import {
import useScripts from "../hooks/useScripts";
import {
selectBackend,
selectSelectedClipModel,
selectSelectedClipModel2,
selectSelectedModel,
selectSelectedVae,
} from "../state/optionsSlice";
Expand Down Expand Up @@ -171,6 +173,9 @@ const MainForm = () => {
const model = useAppSelector(selectSelectedModel);
const vae = useAppSelector(selectSelectedVae);

const clipModel = useAppSelector(selectSelectedClipModel);
const clipModel2 = useAppSelector(selectSelectedClipModel2);

const isConnected = useAppSelector(selectIsConnected);

const isBatchDisabled =
Expand Down Expand Up @@ -231,14 +236,14 @@ const MainForm = () => {
useEffect(() => {
// Update scale if either width of height go below 512
if (width && height) {
const referenceDim = model.isSdXl ? 1024 : 512;
const referenceDim = model.isSdXl || model.isFlux ? 1024 : 512;

const minDimension = Math.min(+width, +height);

if (minDimension < referenceDim) {
const newScale = Math.min(
10,
(model.isSdXl ? 1024 : 516.5) / minDimension
(model?.isSdXl || model?.isFlux ? 1024 : 516.5) / minDimension
).toFixed(2);
setValue("scale", +newScale);
} else if (
Expand Down Expand Up @@ -341,15 +346,16 @@ const MainForm = () => {
? processedPrompt
: basePrompt,
negative_prompt:
typeof negative_prompt === "string"
typeof (negative_prompt ?? "") === "string"
? prompt
: editorJsonToText((negative_prompt as EditorState).doc.toJSON()),
scheduler,
...rest,
...(!isSeedPinned && { seed: -1 }),
mask: txt2img ? "" : maskDataUrl,
init_images: txt2img
? controlnetArgs.controlnet?.args.some(
? hasControlnet &&
controlnetArgs.controlnet?.args.some(
({ overrideBaseLayer }) => !overrideBaseLayer
)
? [initImageDataUrl]
Expand Down Expand Up @@ -387,9 +393,9 @@ const MainForm = () => {
//_
null,
//tile_width,
model?.isSdXl ? 1024 : 512,
model?.isSdXl || model?.isFlux ? 1024 : 512,
//tile_height
model?.isSdXl ? 1024 : 512,
model?.isSdXl || model?.isFlux ? 1024 : 512,
//mask_blur
8,
// padding,
Expand Down Expand Up @@ -591,6 +597,8 @@ const MainForm = () => {
invert_mask: invertMask,
model: model?.name,
vae,
clip_model: clipModel,
clip_model_2: clipModel2,
...((backend === "comfy" || schedulers?.length > 0) && {
scheduler,
}),
Expand Down Expand Up @@ -781,28 +789,30 @@ const MainForm = () => {
className="flex flex-col p-4 px-6 pt-1 pb-10 gap-8 w-full"
ref={formRef}
>
<div className="flex place-items-center gap-3 justify-between">
<Label htmlFor="clip_skip" className="whitespace-nowrap">
Clip Skip
</Label>
<Controller
name="clip_skip"
control={control}
render={({ field }) => (
<Input
id="clip_skip"
className="text-center max-w-16"
type="number"
step={1}
min={1}
max={10}
{...field}
onChange={(event) => field.onChange(+event.target.value)}
/>
)}
defaultValue={1}
/>
</div>
{!model.isFlux && (
<div className="flex place-items-center gap-3 justify-between">
<Label htmlFor="clip_skip" className="whitespace-nowrap">
Clip Skip
</Label>
<Controller
name="clip_skip"
control={control}
render={({ field }) => (
<Input
id="clip_skip"
className="text-center max-w-16"
type="number"
step={1}
min={1}
max={10}
{...field}
onChange={(event) => field.onChange(+event.target.value)}
/>
)}
defaultValue={1}
/>
</div>
)}

{!isGenerating || !isConnected ? (
<Button
Expand Down Expand Up @@ -835,18 +845,21 @@ const MainForm = () => {
/>
</div>
{hasRegionalPrompting && <RegionalPromptsFields />}
<div className="flex flex-col gap-2">
<Label>Negative Prompt</Label>

<Controller
name="negative_prompt"
control={control}
render={({ field }) => (
<Editor placeholder="Negative Prompt" {...field} />
)}
defaultValue=""
/>
</div>
{backend !== "comfy" ||
(!model.isFlux && (
<div className="flex flex-col gap-2">
<Label>Negative Prompt</Label>

<Controller
name="negative_prompt"
control={control}
render={({ field }) => (
<Editor placeholder="Negative Prompt" {...field} />
)}
defaultValue=""
/>
</div>
))}

<Controller
name="txt2img"
Expand Down Expand Up @@ -951,15 +964,39 @@ const MainForm = () => {
defaultValue={0.7}
/>
)}
<Controller
name="cfg_scale"
control={control}
defaultValue={7}
// rules={{ required: true }}
render={({ field }) => (
<Slider step={0.1} min={1} max={30} label="CFG Scale" {...field} />
)}
/>
{backend !== "comfy" || !model.isFlux ? (
<Controller
name="cfg_scale"
control={control}
defaultValue={7}
// rules={{ required: true }}
render={({ field }) => (
<Slider
step={0.1}
min={1}
max={30}
label="CFG Scale"
{...field}
/>
)}
/>
) : (
<Controller
name="flux_guidance"
control={control}
defaultValue={3.5}
// rules={{ required: true }}
render={({ field }) => (
<Slider
step={0.1}
min={1}
max={30}
label="Flux Guidnace"
{...field}
/>
)}
/>
)}
{/* TODO: Show image_cfg_scale only with ip2p */}
{/* {true && (
<Controller
Expand Down
Loading

0 comments on commit 82ef49f

Please sign in to comment.