Merge pull request #277 from 11cafe/gallery-modal

Gallery modal
This commit is contained in:
Weixuan Fu
2024-03-20 21:11:01 +08:00
committed by GitHub
10 changed files with 619 additions and 144 deletions
+7
View File
@@ -156,6 +156,13 @@ export class WorkflowsTable extends TableBase<Workflow> {
throw new Error("Method not allowed.");
}
public async updateTopFields(
id: string,
change: Pick<Partial<Workflow>, "topFieldsConfig">,
): Promise<Workflow | null> {
return this._update(id, change);
}
public async updateMetaInfo(
id: string,
change: Omit<Partial<Workflow>, "id" | "name" | "parentFolderID" | "json">,
+25 -2
View File
@@ -1,3 +1,5 @@
// @ts-expect-error ComfyUI import
import { app } from "/scripts/app.js";
import {
Checkbox,
Flex,
@@ -12,7 +14,7 @@ import {
ModalOverlay,
Text,
} from "@chakra-ui/react";
import { useContext, useEffect, useMemo, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { mediaTable, workflowsTable } from "../db-tables/WorkspaceDB";
import { IconArrowLeft, IconX } from "@tabler/icons-react";
import { WorkspaceContext } from "../WorkspaceContext";
@@ -20,6 +22,8 @@ import { Media } from "../types/dbTypes";
import { MetaDataInfo } from "./components/MetaDataInfo.tsx";
import GalleryMediaItem from "./components/GalleryMediaItem.tsx";
import SearchInput from "../components/SearchInput.tsx";
import { nanoid } from "nanoid";
import { MediaWithMetaData } from "./components/MetaInfoBox.tsx";
export default function GalleryModal({ onclose }: { onclose: () => void }) {
const { curFlowID } = useContext(WorkspaceContext);
@@ -28,7 +32,7 @@ export default function GalleryModal({ onclose }: { onclose: () => void }) {
const [isSelecting, setIsSelecting] = useState(false);
const [coverPath, setCoverPath] = useState("");
const [images, setImages] = useState<Media[]>([]);
const [metaData, setMetaData] = useState<Media>();
const [metaData, setMetaData] = useState<MediaWithMetaData>();
const [searchValue, setSearchValue] = useState("");
const onUpdateSearchValue = (val: string) => {
setSearchValue(val);
@@ -38,6 +42,25 @@ export default function GalleryModal({ onclose }: { onclose: () => void }) {
if (curFlowID == null) return;
const media = await mediaTable?.listByWorkflowID(curFlowID);
setImages(media ?? []);
if (media?.length === 0) {
app.graphToPrompt().then((prompt) => {
setMetaData({
id: nanoid(),
workflowJSON: "",
localPath: "",
createTime: 0,
format: "",
workflowID: "",
metaData: {
prompt: prompt.output,
workflow: prompt.workflow,
},
});
return app.graph._nodes;
});
} else if (Number(media?.length) <= 6 && media?.[0]) {
setMetaData(media[0]);
}
};
useEffect(() => {
@@ -8,83 +8,149 @@ import {
Flex,
} from "@chakra-ui/react";
import { FormItemComponent } from "../FormItem/FormItemComponent.tsx";
import { MetaData } from "../../utils.ts";
import { FormItem } from "../FormItem/types.ts";
import { isInTopField, TopFieldType } from "../MetaBox/MetaBox.tsx";
import { useEffect, useState } from "react";
import { isInTopField } from "../MetaBox/MetaBox.tsx";
import { Fragment, useContext, useEffect, useState } from "react";
import { InputResultItem } from "../MetaBox/utils.ts";
import { MetaBoxContext } from "../MetaBox/metaBoxContext.ts";
export default function AllPromptForm() {
const {
topFields,
updateTopField,
calcInputList,
updateMetaData,
metaData,
showNodeName,
} = useContext(MetaBoxContext);
export default function AllPromptForm({
metaData,
updateMetaData,
topFields,
updateTopField,
}: {
metaData: MetaData;
topFields: TopFieldType[];
updateMetaData: FormItem["updateMetaData"];
updateTopField?: (field: TopFieldType) => void;
}) {
const prompt = metaData.prompt;
const [defaultIndex, setDefaultIndex] = useState<number[]>([]);
const groupInputList =
calcInputList?.reduce<
{
list: InputResultItem[];
class_type: string;
}[]
>((previousValue, currentValue) => {
if (
isInTopField(topFields, {
name: currentValue.name,
promptKey: currentValue.linkId,
classType: currentValue.class_type,
})
) {
return previousValue;
}
const findItem = previousValue.find(
(v) => v.class_type === currentValue.class_type,
);
if (findItem) {
return previousValue.map((item) => {
if (item.class_type === currentValue.class_type) {
return {
...item,
list: [...(item.list ?? []), currentValue],
};
} else {
return item;
}
});
} else {
return [
...previousValue,
{
class_type: currentValue.class_type,
list: [currentValue],
},
];
}
}, []) ?? [];
useEffect(() => {
setDefaultIndex(Object.keys(prompt).map((_, i) => i));
}, [prompt]);
if (showNodeName && groupInputList?.length) {
setDefaultIndex(
Array(groupInputList?.length)
.fill("")
.map((_, i) => i),
);
}
}, [groupInputList?.length, showNodeName]);
if (!showNodeName) {
return (
<>
{groupInputList.map((groupInput) => (
<Fragment
key={`group${groupInput.class_type}${groupInput?.list?.[0]?.linkId}`}
>
{groupInput.list.map((input) => {
const value = metaData.prompt[input.linkId]?.inputs?.[input.name];
return (
<FormItemComponent
key={`form${input.linkId}${input.name}`}
promptKey={input.linkId}
classType={input?.class_type}
value={value}
name={input.name}
updateMetaData={updateMetaData}
updateTopField={updateTopField}
topFields={topFields}
metaData={metaData}
label={input.formLabel}
/>
);
})}
</Fragment>
))}
</>
);
}
return (
<Accordion
index={defaultIndex}
onChange={(val) => setDefaultIndex(val as number[])}
allowMultiple
>
{Object.keys(prompt).map((promptKey) => {
const promptElement = prompt[promptKey];
const promptInputs = promptElement.inputs;
const inputsKeyList = Object.keys(promptInputs).filter(
(v) =>
!Array.isArray(promptInputs[v]) &&
!isInTopField(topFields, {
name: v,
promptKey: promptKey,
classType: promptElement.class_type,
}),
);
if (inputsKeyList.length === 0) return null;
{groupInputList.map((groupInput) => {
return (
<AccordionItem
key={`AccordionItem${promptKey}`}
key={`AccordionItem${groupInput.class_type}${groupInput?.list?.[0]?.linkId}`}
borderWidth={1}
borderRadius={8}
my={2}
>
<AccordionButton>
<Box as="span" flex="1" textAlign="left">
{promptElement.class_type}
{groupInput.class_type}
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
<Flex px={2} gap={1} direction={"column"}>
{inputsKeyList?.map((inputsKey) => {
const value = promptInputs[inputsKey];
{groupInput.list?.map((input) => {
if (
isInTopField(topFields, {
name: inputsKey,
promptKey,
classType: promptElement.class_type,
name: input.name,
promptKey: input.linkId,
classType: input.class_type,
})
) {
return null;
}
const value =
metaData.prompt[input.linkId]?.inputs?.[input.name];
return (
<FormItemComponent
key={`form${inputsKey}`}
promptKey={promptKey}
classType={promptElement?.class_type}
key={`form${input.linkId}${input.name}`}
promptKey={input.linkId}
classType={input?.class_type}
value={value}
name={inputsKey}
name={input.name}
updateMetaData={updateMetaData}
updateTopField={updateTopField}
topFields={topFields}
metaData={metaData}
label={input.formLabel}
/>
);
})}
@@ -95,4 +161,71 @@ export default function AllPromptForm({
})}
</Accordion>
);
// return (
// <Accordion
// index={defaultIndex}
// onChange={(val) => setDefaultIndex(val as number[])}
// allowMultiple
// >
// {Object.keys(prompt).map((promptKey) => {
// const promptElement = prompt[promptKey];
// const promptInputs = promptElement.inputs;
// const inputsKeyList = Object.keys(promptInputs).filter(
// (v) =>
// !Array.isArray(promptInputs[v]) &&
// !isInTopField(topFields, {
// name: v,
// promptKey: promptKey,
// classType: promptElement.class_type,
// }),
// );
// if (inputsKeyList.length === 0) return null;
// return (
// <AccordionItem
// key={`AccordionItem${promptKey}`}
// borderWidth={1}
// borderRadius={8}
// my={2}
// >
// <AccordionButton>
// <Box as="span" flex="1" textAlign="left">
// {promptElement.class_type}
// </Box>
// <AccordionIcon />
// </AccordionButton>
// <AccordionPanel>
// <Flex px={2} gap={1} direction={"column"}>
// {inputsKeyList?.map((inputsKey) => {
// const value = promptInputs[inputsKey];
// if (
// isInTopField(topFields, {
// name: inputsKey,
// promptKey,
// classType: promptElement.class_type,
// })
// ) {
// return null;
// }
// return (
// <FormItemComponent
// key={`form${inputsKey}`}
// promptKey={promptKey}
// classType={promptElement?.class_type}
// value={value}
// name={inputsKey}
// updateMetaData={updateMetaData}
// updateTopField={updateTopField}
// topFields={topFields}
// metaData={metaData}
// />
// );
// })}
// </Flex>
// </AccordionPanel>
// </AccordionItem>
// );
// })}
// </Accordion>
// );
}
+57 -64
View File
@@ -3,8 +3,12 @@ import { Flex } from "@chakra-ui/react";
import { Media } from "../../../types/dbTypes.ts";
import TopForm from "../TopForm/TopForm.tsx";
import AllPromptForm from "../AllPromptForm/AllPromptForm.tsx";
import { useState } from "react";
import { useContext, useEffect, useState } from "react";
import { FormItem } from "../FormItem/types.ts";
import { calcMeta, type InputResultItem } from "./utils.ts";
import { MetaBoxContext } from "./metaBoxContext.ts";
import { workflowsTable } from "../../../db-tables/WorkspaceDB.ts";
import { WorkspaceContext } from "../../../WorkspaceContext.ts";
export type TopFieldType = {
promptKey: string | number;
@@ -12,49 +16,6 @@ export type TopFieldType = {
name: string;
};
// comfyui default workflow
export const DEFAULT_TOP_FIELDS: TopFieldType[] = [
{
promptKey: "4",
name: "ckpt_name",
class_type: "CheckpointLoaderSimple",
},
{
promptKey: "6",
name: "text",
class_type: "CLIPTextEncode",
},
{
promptKey: "7",
name: "text",
class_type: "CLIPTextEncode",
},
{
promptKey: "5",
name: "width",
class_type: "EmptyLatentImage",
},
{
promptKey: "5",
name: "height",
class_type: "EmptyLatentImage",
},
{
promptKey: "3",
name: "steps",
class_type: "KSampler",
},
{
promptKey: "3",
name: "sampler_name",
class_type: "KSampler",
},
{
promptKey: "3",
name: "cfg",
class_type: "KSampler",
},
];
export const isInTopField = (
topFields: TopFieldType[],
item: Pick<FormItem, "name" | "promptKey" | "classType">,
@@ -69,12 +30,22 @@ export const isInTopField = (
export default function MetaBox({
metaData: oriMetaData,
showNodeName,
}: {
metaData: MetaData;
media: Media;
showNodeName: boolean;
}) {
const _metaData = JSON.parse(JSON.stringify(oriMetaData));
const [topFields, setTopFields] = useState(DEFAULT_TOP_FIELDS);
const [calcInputList, setCalcInputList] = useState<InputResultItem[]>([]);
const { curFlowID } = useContext(WorkspaceContext);
useEffect(() => {
const calcInput = calcMeta(_metaData);
setCalcInputList(calcInput);
}, []);
const [topFields, setTopFields] = useState<TopFieldType[]>([]);
const [metaData, setMetaData] = useState<MetaData>(_metaData);
const updateMetaData = ({
promptKey,
@@ -100,6 +71,17 @@ export default function MetaBox({
}));
};
useEffect(() => {
(async () => {
if (curFlowID) {
const topFieldsConfig = (await workflowsTable?.get(curFlowID))
?.topFieldsConfig;
if (topFieldsConfig) {
setTopFields(topFieldsConfig);
}
}
})();
}, [curFlowID]);
const updateTopField = (field: TopFieldType) => {
if (
isInTopField(topFields, {
@@ -108,30 +90,41 @@ export default function MetaBox({
classType: field?.class_type ?? "",
})
) {
setTopFields((pre) =>
pre.filter(
(v) => v.name !== field.name || v.promptKey !== field.promptKey,
),
const topFieldsConfig = topFields.filter(
(v) => v.name !== field.name || v.promptKey !== field.promptKey,
);
setTopFields(topFieldsConfig);
if (curFlowID) {
workflowsTable?.updateTopFields(curFlowID, {
topFieldsConfig,
});
}
} else {
setTopFields((pre) => [...pre, field]);
const topFieldsConfig = [...topFields, field];
setTopFields(topFieldsConfig);
if (curFlowID) {
workflowsTable?.updateTopFields(curFlowID, {
topFieldsConfig,
});
}
}
};
return (
<Flex direction={"column"} align={"stretch"}>
<TopForm
topFields={topFields}
metaData={metaData}
updateMetaData={updateMetaData}
updateTopField={updateTopField}
/>
<AllPromptForm
topFields={topFields}
metaData={metaData}
updateMetaData={updateMetaData}
updateTopField={updateTopField}
/>
</Flex>
<MetaBoxContext.Provider
value={{
topFields,
metaData,
updateMetaData,
showNodeName,
calcInputList,
updateTopField,
}}
>
<Flex direction={"column"} align={"stretch"}>
<TopForm />
<AllPromptForm />
</Flex>
</MetaBoxContext.Provider>
);
}
@@ -0,0 +1,23 @@
import { createContext } from "react";
import { MetaData } from "../../utils.ts";
import { TopFieldType } from "./MetaBox.tsx";
import { FormItem } from "../FormItem/types.ts";
import { InputResultItem } from "./utils.ts";
interface MetaBoxContextProps {
metaData: MetaData;
topFields: TopFieldType[];
updateMetaData: FormItem["updateMetaData"];
updateTopField?: (field: TopFieldType) => void;
calcInputList: InputResultItem[];
showNodeName: boolean;
}
export const MetaBoxContext = createContext<MetaBoxContextProps>({
topFields: [],
calcInputList: [],
metaData: {} as MetaData,
showNodeName: false,
updateMetaData: undefined,
updateTopField(): void {},
});
+222
View File
@@ -0,0 +1,222 @@
import { getNodesInfo } from "../../utils.ts";
type MetaValue = string | number | null | any[];
interface Workflow {
last_node_id: number;
last_link_id: number;
nodes: Node[];
links: Link[];
groups: any[];
config: object;
extra: object;
version: number;
widget_idx_map: WidgetIdxMap;
}
interface Properties {
"Node name for S&R"?: string;
}
interface Node {
id: number;
type: string;
pos: number[];
size: Size;
flags: object;
order: number;
mode: number;
inputs?: Input[];
outputs?: Output[];
properties: Properties;
widgets_values: MetaValue[];
}
interface Link {
id?: number;
type?: string;
pos?: number[];
size?: Size;
flags?: object;
order?: number;
mode?: number;
inputs?: Input[];
outputs?: Output[];
properties?: Properties;
widgets_values?: MetaValue[];
}
interface Size {
0: number;
1: number;
}
interface Input {
name: string;
type: string;
link: number;
label: string;
}
interface Output {
name: string;
type: string;
links: number[];
slot_index: number;
label: string;
}
interface WidgetIdxMap {
[key: string]: {
[key: string]: number;
};
}
interface Meta {
prompt: {
[key: string | number]: {
class_type: string;
inputs: {
[key: string]: MetaValue | any;
};
};
};
workflow: Workflow;
}
export type InputResultItem = {
class_type: string;
name: string;
linkId: string;
value: MetaValue;
path: InputResultItem[];
inputInfo: any;
isTop?: boolean;
formLabel?: string;
};
const nodesInfo = getNodesInfo();
const getInputConfig = (props: { classType: string; name: string }) => {
const nodeData = nodesInfo[props.classType]?.nodeData;
return {
...({
...(nodeData?.input?.required ?? {}),
...(nodeData?.input?.optional ?? {}),
}?.[props.name] ?? {}),
output: nodeData?.output ?? [],
output_name: nodeData?.output_name ?? [],
};
};
function fineRoot(prompt: Meta["prompt"]) {
const keyList = Object.keys(prompt);
const allInputs = keyList.reduce<string[]>((previousValue, currentValue) => {
const inputs = prompt[currentValue].inputs;
const allArrValue = Object.keys(inputs).reduce<string[]>(
(previousValue1, currentValue1) => {
if (Array.isArray(inputs[currentValue1])) {
return [...previousValue1, inputs[currentValue1]?.[0]];
}
return [...previousValue1];
},
[],
);
return [...previousValue, ...allArrValue];
}, []);
return keyList.filter((item) => {
return !allInputs.includes(item);
});
}
function getInputListByLinkId({
oriMeta,
linkId,
parentItem,
}: {
oriMeta: Meta;
linkId: string;
parentItem?: InputResultItem[];
}): InputResultItem[] {
const prompt = oriMeta.prompt;
if (!prompt[linkId]) {
return [];
}
const class_type = prompt[linkId].class_type;
const currentInputKeyList = Object.keys(prompt[linkId].inputs);
const inputList = currentInputKeyList.map((input) => {
const inputInfo = getInputConfig({ classType: class_type, name: input });
let formLabel;
// 判断是否是 正向提示词
if (
parentItem?.some((v) => v.name === "positive") &&
inputInfo?.["0"] === "STRING"
) {
formLabel = `positive-${input}`;
}
if (
parentItem?.some((v) => v.name === "negative") &&
inputInfo?.["0"] === "STRING"
) {
formLabel = `negative-${input}`;
}
return {
name: input,
class_type: class_type,
linkId: linkId,
value: prompt[linkId]?.inputs?.[input],
path: parentItem ?? [],
inputInfo: getInputConfig({ classType: class_type, name: input }),
formLabel,
} as InputResultItem;
});
// 遍历节点所有input
const childrenInputList = inputList.reduce<InputResultItem[]>(
(previousValue, currentValue) => {
// 如果节点为array,则数据为上一节点传入的数据,递归查询上一节点的信息,同时保存当前节点路径,便于后续查找节点的父子关系
if (Array.isArray(currentValue.value)) {
const curList = getInputListByLinkId({
oriMeta,
linkId: currentValue?.value?.[0] ?? "",
parentItem: [...(parentItem ?? []), currentValue],
});
return [
...previousValue,
// 如果之前已经添加过该节点,则跳过,(有的节点数据会提供给多个node)
...curList.filter(
(cur) =>
!previousValue.some(
(value) =>
value.linkId === cur.linkId && value.name === cur.name,
),
),
];
}
return previousValue;
},
[],
);
// 最终输出的是当前节点的信息, 加上递归的节点数据
return [
...inputList.filter((v) => !Array.isArray(v.value)),
...childrenInputList,
];
}
// 递归遍历所有子节点
export function calcMeta(oriMeta: Meta): InputResultItem[] {
// const last_link_id = oriMeta.workflow.last_node_id;
const rootId = fineRoot(oriMeta.prompt);
if (rootId.length) {
return getInputListByLinkId({
oriMeta,
linkId: `${rootId[0]}`,
});
}
return getInputListByLinkId({
oriMeta,
linkId: `${rootId}`,
});
}
+48 -3
View File
@@ -1,8 +1,9 @@
import { Media } from "../../types/dbTypes.ts";
import { FC, useEffect, useState } from "react";
import { Flex } from "@chakra-ui/react";
import { Box, Flex, Grid, Image } from "@chakra-ui/react";
import Carousel from "../../components/Carousel/Carousel.tsx";
import { MetaInfoBox } from "./MetaInfoBox.tsx";
import { isImageFormat } from "../../utils.tsx";
interface MetaDataInfoProps {
media: Media;
@@ -19,7 +20,11 @@ export const MetaDataInfo: FC<MetaDataInfoProps> = ({ mediaList, media }) => {
return (
<Flex gap={3} h={"100%"}>
<Flex flex={1}>
<Grid
gridTemplateRows={mediaList.length <= 6 ? "1fr 20%" : "1fr"}
flex={1}
gap={2}
>
<Carousel
media={mediaList.map((v) => ({
id: v.id,
@@ -30,7 +35,47 @@ export const MetaDataInfo: FC<MetaDataInfoProps> = ({ mediaList, media }) => {
setMediaAct(mediaList?.find((v) => v.id === newMedia.id))
}
/>
</Flex>
{mediaList.length <= 6 && (
<Flex>
{mediaList?.map((media) => (
<Box
display={"inline-block"}
p={2}
borderRadius={"4px"}
key={`image-bottom-${media.id}`}
width={"16.6%"}
cursor={"pointer"}
border={mediaAct?.id === media.id ? "1px solid gray" : ""}
onClick={() => setMediaAct(media)}
>
{isImageFormat(
`/workspace/view_media?filename=${media.localPath}`,
) ? (
<Image
src={`/workspace/view_media?filename=${media.localPath}`}
alt={`image-${media.id}`}
width={"100%"}
height={"100%"}
objectFit="contain"
/>
) : (
<video
style={{ objectFit: "contain" }}
width={"100%"}
height={"100%"}
src={`/workspace/view_media?filename=${media.localPath}`}
loop={true}
autoPlay={true}
muted={true}
>
<track kind="captions" />
</video>
)}
</Box>
))}
</Flex>
)}
</Grid>
<MetaInfoBox media={mediaAct} />
</Flex>
);
+48 -19
View File
@@ -2,6 +2,7 @@ import { getMetadataFromUrl, MetaData } from "../utils.ts";
import { Media } from "../../types/dbTypes.ts";
import { useEffect, useState } from "react";
import {
Checkbox,
Flex,
IconButton,
Link,
@@ -13,7 +14,11 @@ import { IconDownload } from "@tabler/icons-react";
import { formatTimestamp } from "../../utils.tsx";
import MetaBox from "./MetaBox/MetaBox.tsx";
export const MetaInfoBox = ({ media }: { media?: Media }) => {
export type MediaWithMetaData = Media & {
metaData?: MetaData;
};
export const MetaInfoBox = ({ media }: { media?: MediaWithMetaData }) => {
const [showNodeName, setShowNodeName] = useState(true);
const [mediaMetaData, setMediaMetaData] = useState<MetaData>();
const getMetaData = async (curMedia: Media) => {
try {
@@ -27,35 +32,59 @@ export const MetaInfoBox = ({ media }: { media?: Media }) => {
};
useEffect(() => {
if (media) {
getMetaData(media);
if (media?.metaData) {
setMediaMetaData(media?.metaData);
} else {
getMetaData(media);
}
}
}, [media]);
return (
<Flex overflowY={"auto"} mb={4} direction={"column"} gap={2} flex={1}>
<SimpleGrid alignItems={"center"} columns={2} spacing={2}>
<SimpleGrid alignItems={"center"} columns={3} spacing={2}>
<Flex alignItems={"center"} gap={1}>
<Text>{media?.localPath}</Text>
<Tooltip label="Donwload image from gallery">
<Link
href={`/workspace/view_media?filename=${media?.localPath}`}
download={media?.localPath}
>
<IconButton
size={"sm"}
icon={<IconDownload size={19} />}
aria-label="donwload image from gallery"
/>
</Link>
</Tooltip>
{media?.localPath && (
<>
<Text>{media?.localPath}</Text>
<Tooltip label="Donwload image from gallery">
<Link
href={`/workspace/view_media?filename=${media?.localPath}`}
download={media?.localPath}
>
<IconButton
size={"sm"}
icon={<IconDownload size={19} />}
aria-label="donwload image from gallery"
/>
</Link>
</Tooltip>
</>
)}
</Flex>
<Flex gap={1} alignItems={"center"}>
<Text>Create Time:</Text>
<Text>{formatTimestamp(media?.createTime ?? 0, true)}</Text>
{!!media?.createTime && (
<>
<Text>Create Time:</Text>
<Text>{formatTimestamp(media?.createTime ?? 0, true)}</Text>
</>
)}
</Flex>
<Flex>
<Checkbox
isChecked={showNodeName}
onChange={(e) => setShowNodeName(e.target.checked)}
>
show node name
</Checkbox>
</Flex>
</SimpleGrid>
{media && mediaMetaData && (
<MetaBox metaData={mediaMetaData} media={media} />
<MetaBox
showNodeName={showNodeName}
metaData={mediaMetaData}
media={media}
/>
)}
</Flex>
);
+12 -14
View File
@@ -1,20 +1,11 @@
import { MetaData } from "../../utils.ts";
import { TopFieldType } from "../MetaBox/MetaBox.tsx";
import { Flex } from "@chakra-ui/react";
import { FormItemComponent } from "../FormItem/FormItemComponent.tsx";
import { FormItem } from "../FormItem/types.ts";
import { useContext } from "react";
import { MetaBoxContext } from "../MetaBox/metaBoxContext.ts";
export default function TopForm({
metaData,
updateMetaData,
topFields,
updateTopField,
}: {
metaData: MetaData;
topFields: TopFieldType[];
updateMetaData: FormItem["updateMetaData"];
updateTopField?: (field: TopFieldType) => void;
}) {
export default function TopForm() {
const { topFields, updateTopField, calcInputList, updateMetaData, metaData } =
useContext(MetaBoxContext);
if (topFields.length === 0) return null;
const prompt = metaData.prompt;
return (
@@ -26,6 +17,12 @@ export default function TopForm({
const nodeClassType = prompt?.[field.promptKey]?.class_type;
if (nodeClassType !== field.class_type) return null;
const inputValue = nodeInputs?.[field.name];
const label =
calcInputList?.find(
(input) =>
input?.linkId === field?.promptKey &&
input?.name === field?.name,
)?.formLabel ?? undefined;
return (
<FormItemComponent
key={`formTop${field.name}${i}`}
@@ -36,6 +33,7 @@ export default function TopForm({
updateMetaData={updateMetaData}
updateTopField={updateTopField}
topFields={topFields}
label={label}
/>
);
})}
+2
View File
@@ -1,4 +1,5 @@
import type { MODEL_TYPE } from "../model-manager/install-models/util/modelTypes";
import { TopFieldType } from "../gallery/components/MetaBox/MetaBox.tsx";
export interface SortableItem {
name: string;
@@ -21,6 +22,7 @@ export interface Workflow extends SortableItem {
cloudOrigin?: string;
saveLock?: boolean;
latestImage?: string;
topFieldsConfig?: TopFieldType[];
}
export interface TableBaseModel {