From 155b28206679f5287c8f4c30311ddbc7c4b556e0 Mon Sep 17 00:00:00 2001 From: ss707494 Date: Thu, 14 Mar 2024 22:38:46 +0800 Subject: [PATCH 1/6] feat(gallery): gallery-table --- ui/src/gallery/GalleryModal.tsx | 7 +++- ui/src/gallery/components/MetaBox/MetaBox.tsx | 1 + ui/src/gallery/components/MetaDataInfo.tsx | 42 +++++++++++++++++-- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/ui/src/gallery/GalleryModal.tsx b/ui/src/gallery/GalleryModal.tsx index ce96e31..1a9c93b 100644 --- a/ui/src/gallery/GalleryModal.tsx +++ b/ui/src/gallery/GalleryModal.tsx @@ -12,7 +12,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"; @@ -38,6 +38,9 @@ export default function GalleryModal({ onclose }: { onclose: () => void }) { if (curFlowID == null) return; const media = await mediaTable?.listByWorkflowID(curFlowID); setImages(media ?? []); + if (Number(media?.length) <= 6 && media?.[0]) { + setMetaData(media[0]); + } }; useEffect(() => { @@ -83,7 +86,7 @@ export default function GalleryModal({ onclose }: { onclose: () => void }) { - {!!metaData && ( + {!!metaData && images.length > 6 && ( setMetaData(undefined)} variant={"ghost"} diff --git a/ui/src/gallery/components/MetaBox/MetaBox.tsx b/ui/src/gallery/components/MetaBox/MetaBox.tsx index 74024b8..0ed5802 100644 --- a/ui/src/gallery/components/MetaBox/MetaBox.tsx +++ b/ui/src/gallery/components/MetaBox/MetaBox.tsx @@ -55,6 +55,7 @@ export const DEFAULT_TOP_FIELDS: TopFieldType[] = [ class_type: "KSampler", }, ]; + export const isInTopField = ( topFields: TopFieldType[], item: Pick, diff --git a/ui/src/gallery/components/MetaDataInfo.tsx b/ui/src/gallery/components/MetaDataInfo.tsx index 4f647df..7ee5ad9 100644 --- a/ui/src/gallery/components/MetaDataInfo.tsx +++ b/ui/src/gallery/components/MetaDataInfo.tsx @@ -1,6 +1,14 @@ import { Media } from "../../types/dbTypes.ts"; import { FC, useEffect, useState } from "react"; -import { Flex } from "@chakra-ui/react"; +import { + Box, + Flex, + Grid, + HStack, + Image, + SimpleGrid, + VStack, +} from "@chakra-ui/react"; import Carousel from "../../components/Carousel/Carousel.tsx"; import { MetaInfoBox } from "./MetaInfoBox.tsx"; @@ -19,7 +27,11 @@ export const MetaDataInfo: FC = ({ mediaList, media }) => { return ( - + ({ id: v.id, @@ -30,7 +42,31 @@ export const MetaDataInfo: FC = ({ mediaList, media }) => { setMediaAct(mediaList?.find((v) => v.id === newMedia.id)) } /> - + {mediaList.length <= 6 && ( + + {mediaList?.map((media) => ( + setMediaAct(media)} + > + {`image-bottom-${media.id}`} + + ))} + + )} + ); From 75a5d73412283d2784a6323b043a806d4994cfda Mon Sep 17 00:00:00 2001 From: ss707494 Date: Fri, 15 Mar 2024 15:32:59 +0800 Subject: [PATCH 2/6] feat(gallery): gallery-table/Query node information by the root node --- .../AllPromptForm/AllPromptForm.tsx | 208 ++++++++++-- ui/src/gallery/components/MetaBox/MetaBox.tsx | 171 +++++++--- ui/src/gallery/components/MetaBox/utils.ts | 303 ++++++++++++++++++ ui/src/gallery/components/MetaDataInfo.tsx | 10 +- ui/src/gallery/components/MetaInfoBox.tsx | 18 +- ui/src/gallery/components/TopForm/TopForm.tsx | 19 +- 6 files changed, 629 insertions(+), 100 deletions(-) create mode 100644 ui/src/gallery/components/MetaBox/utils.ts diff --git a/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx b/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx index fc9255f..f816d72 100644 --- a/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx +++ b/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx @@ -11,80 +11,153 @@ 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 { Fragment, useEffect, useState } from "react"; +import { InputResultItem } from "../MetaBox/utils.ts"; +export type FormBoxProps = { + metaData: MetaData; + topFields: TopFieldType[]; + updateMetaData: FormItem["updateMetaData"]; + updateTopField?: (field: TopFieldType) => void; + calcInputList: InputResultItem[]; + showNodeName: boolean; +}; export default function AllPromptForm({ metaData, updateMetaData, topFields, updateTopField, -}: { - metaData: MetaData; - topFields: TopFieldType[]; - updateMetaData: FormItem["updateMetaData"]; - updateTopField?: (field: TopFieldType) => void; -}) { - const prompt = metaData.prompt; + calcInputList, + showNodeName, +}: FormBoxProps) { const [defaultIndex, setDefaultIndex] = useState([]); + + 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) => ( + + {groupInput.list.map((input) => { + const value = metaData.prompt[input.linkId]?.inputs?.[input.name]; + return ( + + ); + })} + + ))} + + ); + } return ( 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 ( - {promptElement.class_type} + {groupInput.class_type} - {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 ( ); })} @@ -95,4 +168,71 @@ export default function AllPromptForm({ })} ); + + // return ( + // 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 ( + // + // + // + // {promptElement.class_type} + // + // + // + // + // + // {inputsKeyList?.map((inputsKey) => { + // const value = promptInputs[inputsKey]; + // if ( + // isInTopField(topFields, { + // name: inputsKey, + // promptKey, + // classType: promptElement.class_type, + // }) + // ) { + // return null; + // } + // return ( + // + // ); + // })} + // + // + // + // ); + // })} + // + // ); } diff --git a/ui/src/gallery/components/MetaBox/MetaBox.tsx b/ui/src/gallery/components/MetaBox/MetaBox.tsx index 0ed5802..fc79e93 100644 --- a/ui/src/gallery/components/MetaBox/MetaBox.tsx +++ b/ui/src/gallery/components/MetaBox/MetaBox.tsx @@ -3,8 +3,9 @@ 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 { useEffect, useState } from "react"; import { FormItem } from "../FormItem/types.ts"; +import { calcMeta, type InputResultItem } from "./utils.ts"; export type TopFieldType = { promptKey: string | number; @@ -12,50 +13,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, @@ -70,12 +27,30 @@ 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([]); + + useEffect(() => { + const calcInput = calcMeta(_metaData); + setCalcInputList(calcInput); + setTopFields( + calcInput + ?.filter((input: InputResultItem) => input.isTop) + ?.map((input: InputResultItem) => ({ + name: input.name, + promptKey: input.linkId, + class_type: input.class_type, + })), + ); + }, []); + + const [topFields, setTopFields] = useState([]); const [metaData, setMetaData] = useState(_metaData); const updateMetaData = ({ promptKey, @@ -122,17 +97,121 @@ export default function MetaBox({ return ( ); } + +const t = { + "3": { + inputs: { + seed: 561349015796654, + steps: 25, + cfg: 2.5, + sampler_name: "euler_ancestral", + scheduler: "karras", + denoise: 1, + model: ["14", 0], + positive: ["12", 0], + negative: ["12", 1], + latent_image: ["12", 2], + }, + class_type: "KSampler", + }, + "8": { + inputs: { + samples: ["3", 0], + vae: ["15", 2], + }, + class_type: "VAEDecode", + }, + "12": { + inputs: { + width: 576, + height: 1024, + video_frames: 25, + motion_bucket_id: 120, + fps: 8, + augmentation_level: 0.07, + clip_vision: ["15", 1], + init_image: ["57", 0], + vae: ["15", 2], + }, + class_type: "SVD_img2vid_Conditioning", + }, + "14": { + inputs: { + min_cfg: 1, + model: ["15", 0], + }, + class_type: "VideoLinearCFGGuidance", + }, + "15": { + inputs: { + ckpt_name: "stableVideoDiffusion_img2vid.safetensors", + }, + class_type: "ImageOnlyCheckpointLoader", + }, + "24": { + inputs: { + frame_rate: 10, + loop_count: 0, + filename_prefix: "comfyUI", + format: "video/h265-mp4", + pix_fmt: "yuv420p10le", + crf: 20, + save_metadata: true, + pingpong: false, + save_output: true, + images: ["8", 0], + }, + class_type: "VHS_VideoCombine", + }, + "57": { + inputs: { + image: + "00352-596241437-c4tt4stic,(own hands together_1.3),a cartoon cat wearing a jacket,(pray_1.1),_lora_c4tt4stic_0.85_,pitiful,i beg you,cupping han - 副本 (1).png", + upload: "image", + }, + class_type: "LoadImage", + is_changed: [ + "22bc50d33b258d1d8446a4f1fce10d568ee9fc24952428bf0f66ccab583f7fed", + ], + }, +}; + +function fineRoot(t) { + const keyList = Object.keys(t); + const allInputs = keyList.reduce((previousValue, currentValue) => { + const inputs = t[currentValue].inputs; + const allArrValue = Object.keys(inputs).reduce( + (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); + }); +} + +console.log(fineRoot(t)); diff --git a/ui/src/gallery/components/MetaBox/utils.ts b/ui/src/gallery/components/MetaBox/utils.ts new file mode 100644 index 0000000..c3f2a96 --- /dev/null +++ b/ui/src/gallery/components/MetaBox/utils.ts @@ -0,0 +1,303 @@ +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 getTopInfo({ input }: { input: InputResultItem }) { + if ( + input.name === "width" && + input.inputInfo?.output?.some((out: string) => out === "LATENT") + ) { + return { + ...input, + isTop: true, + }; + } + if ( + input.name === "cfg" && + input.inputInfo?.output?.some((out: string) => out === "LATENT") + ) { + return { + ...input, + isTop: true, + }; + } + + if ( + input.name === "steps" && + input.inputInfo?.output?.some((out: string) => out === "LATENT") + ) { + return { + ...input, + isTop: true, + }; + } + if ( + input.name === "sampler_name" && + input.inputInfo?.output?.some((out: string) => out === "LATENT") + ) { + return { + ...input, + isTop: true, + }; + } + + if ( + input.name === "height" && + input.inputInfo?.output?.some((out: string) => out === "LATENT") + ) { + return { + ...input, + isTop: true, + }; + } + + if ( + input.path.some((v) => v.name === "model") && + input.inputInfo?.output?.some((out: string) => out === "MODEL") + ) { + return { + ...input, + isTop: true, + }; + } + + if ( + input.path.some((v) => v.name === "positive") && + input.inputInfo?.output?.some((out: string) => + ["CONDITIONING", "STRING"].indexOf(out), + ) && + input.inputInfo?.["0"] === "STRING" + ) { + return { + ...input, + formLabel: `positive-${input.name}`, + isTop: true, + }; + } + if ( + input.path.some((v) => v.name === "negative") && + input.inputInfo?.output?.some((out: string) => + ["CONDITIONING", "STRING"].indexOf(out), + ) && + input.inputInfo?.["0"] === "STRING" + ) { + return { + ...input, + formLabel: `negative-${input.name}`, + isTop: true, + }; + } + return { + ...input, + }; +} + +function fineRoot(prompt: Meta["prompt"]) { + const keyList = Object.keys(prompt); + const allInputs = keyList.reduce((previousValue, currentValue) => { + const inputs = prompt[currentValue].inputs; + const allArrValue = Object.keys(inputs).reduce( + (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 inputList = Object.keys(prompt[linkId].inputs).map( + (input) => + getTopInfo({ + input: { + name: input, + class_type: class_type, + linkId: linkId, + value: prompt[linkId]?.inputs?.[input], + path: parentItem ?? [], + inputInfo: getInputConfig({ classType: class_type, name: input }), + }, + }) as InputResultItem, + ); + + const childrenInputList = inputList.reduce( + (previousValue, currentValue) => { + if (Array.isArray(currentValue.value)) { + const curList = getInputListByLinkId({ + oriMeta, + linkId: currentValue?.value?.[0] ?? "", + parentItem: [...(parentItem ?? []), currentValue], + }); + return [ + ...previousValue, + ...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); + console.log(432, rootId); + + console.log( + 465, + getInputListByLinkId({ + oriMeta, + linkId: `${rootId[0]}`, + }), + oriMeta.prompt, + oriMeta.workflow, + ); + if (rootId.length) { + return getInputListByLinkId({ + oriMeta, + linkId: `${rootId[0]}`, + }); + } + return getInputListByLinkId({ + oriMeta, + linkId: `${rootId}`, + }); +} diff --git a/ui/src/gallery/components/MetaDataInfo.tsx b/ui/src/gallery/components/MetaDataInfo.tsx index 7ee5ad9..01eceb2 100644 --- a/ui/src/gallery/components/MetaDataInfo.tsx +++ b/ui/src/gallery/components/MetaDataInfo.tsx @@ -1,14 +1,6 @@ import { Media } from "../../types/dbTypes.ts"; import { FC, useEffect, useState } from "react"; -import { - Box, - Flex, - Grid, - HStack, - Image, - SimpleGrid, - VStack, -} 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"; diff --git a/ui/src/gallery/components/MetaInfoBox.tsx b/ui/src/gallery/components/MetaInfoBox.tsx index fb28242..a0bcde2 100644 --- a/ui/src/gallery/components/MetaInfoBox.tsx +++ b/ui/src/gallery/components/MetaInfoBox.tsx @@ -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, @@ -14,6 +15,7 @@ import { formatTimestamp } from "../../utils.tsx"; import MetaBox from "./MetaBox/MetaBox.tsx"; export const MetaInfoBox = ({ media }: { media?: Media }) => { + const [showNodeName, setShowNodeName] = useState(true); const [mediaMetaData, setMediaMetaData] = useState(); const getMetaData = async (curMedia: Media) => { try { @@ -33,7 +35,7 @@ export const MetaInfoBox = ({ media }: { media?: Media }) => { return ( - + {media?.localPath} @@ -53,9 +55,21 @@ export const MetaInfoBox = ({ media }: { media?: Media }) => { Create Time: {formatTimestamp(media?.createTime ?? 0, true)} + + setShowNodeName(e.target.checked)} + > + show node name + + {media && mediaMetaData && ( - + )} ); diff --git a/ui/src/gallery/components/TopForm/TopForm.tsx b/ui/src/gallery/components/TopForm/TopForm.tsx index f9a34ea..7ed3020 100644 --- a/ui/src/gallery/components/TopForm/TopForm.tsx +++ b/ui/src/gallery/components/TopForm/TopForm.tsx @@ -1,20 +1,14 @@ -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 { FormBoxProps } from "../AllPromptForm/AllPromptForm.tsx"; export default function TopForm({ metaData, updateMetaData, topFields, updateTopField, -}: { - metaData: MetaData; - topFields: TopFieldType[]; - updateMetaData: FormItem["updateMetaData"]; - updateTopField?: (field: TopFieldType) => void; -}) { + calcInputList, +}: FormBoxProps) { if (topFields.length === 0) return null; const prompt = metaData.prompt; return ( @@ -26,6 +20,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 ( ); })} From 99b4f8e8326b3ee387bc1b798dcbd29996202890 Mon Sep 17 00:00:00 2001 From: ss707494 Date: Fri, 15 Mar 2024 15:34:35 +0800 Subject: [PATCH 3/6] feat(gallery): gallery-table/Query node information by the root node --- ui/src/gallery/components/MetaBox/MetaBox.tsx | 100 ------------------ 1 file changed, 100 deletions(-) diff --git a/ui/src/gallery/components/MetaBox/MetaBox.tsx b/ui/src/gallery/components/MetaBox/MetaBox.tsx index fc79e93..ae3bea6 100644 --- a/ui/src/gallery/components/MetaBox/MetaBox.tsx +++ b/ui/src/gallery/components/MetaBox/MetaBox.tsx @@ -115,103 +115,3 @@ export default function MetaBox({ ); } - -const t = { - "3": { - inputs: { - seed: 561349015796654, - steps: 25, - cfg: 2.5, - sampler_name: "euler_ancestral", - scheduler: "karras", - denoise: 1, - model: ["14", 0], - positive: ["12", 0], - negative: ["12", 1], - latent_image: ["12", 2], - }, - class_type: "KSampler", - }, - "8": { - inputs: { - samples: ["3", 0], - vae: ["15", 2], - }, - class_type: "VAEDecode", - }, - "12": { - inputs: { - width: 576, - height: 1024, - video_frames: 25, - motion_bucket_id: 120, - fps: 8, - augmentation_level: 0.07, - clip_vision: ["15", 1], - init_image: ["57", 0], - vae: ["15", 2], - }, - class_type: "SVD_img2vid_Conditioning", - }, - "14": { - inputs: { - min_cfg: 1, - model: ["15", 0], - }, - class_type: "VideoLinearCFGGuidance", - }, - "15": { - inputs: { - ckpt_name: "stableVideoDiffusion_img2vid.safetensors", - }, - class_type: "ImageOnlyCheckpointLoader", - }, - "24": { - inputs: { - frame_rate: 10, - loop_count: 0, - filename_prefix: "comfyUI", - format: "video/h265-mp4", - pix_fmt: "yuv420p10le", - crf: 20, - save_metadata: true, - pingpong: false, - save_output: true, - images: ["8", 0], - }, - class_type: "VHS_VideoCombine", - }, - "57": { - inputs: { - image: - "00352-596241437-c4tt4stic,(own hands together_1.3),a cartoon cat wearing a jacket,(pray_1.1),_lora_c4tt4stic_0.85_,pitiful,i beg you,cupping han - 副本 (1).png", - upload: "image", - }, - class_type: "LoadImage", - is_changed: [ - "22bc50d33b258d1d8446a4f1fce10d568ee9fc24952428bf0f66ccab583f7fed", - ], - }, -}; - -function fineRoot(t) { - const keyList = Object.keys(t); - const allInputs = keyList.reduce((previousValue, currentValue) => { - const inputs = t[currentValue].inputs; - const allArrValue = Object.keys(inputs).reduce( - (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); - }); -} - -console.log(fineRoot(t)); From 76345c2173f60bc8ff8b92ceb76912dc7ffd17be Mon Sep 17 00:00:00 2001 From: ss707494 Date: Sun, 17 Mar 2024 11:24:53 +0800 Subject: [PATCH 4/6] feat(gallery): top field config save to database/add context --- ui/src/db-tables/WorkflowsTable.ts | 7 ++ ui/src/gallery/GalleryModal.tsx | 26 +++++- .../AllPromptForm/AllPromptForm.tsx | 33 +++----- ui/src/gallery/components/MetaBox/MetaBox.tsx | 84 +++++++++++-------- .../components/MetaBox/metaBoxContext.ts | 23 +++++ ui/src/gallery/components/MetaBox/utils.ts | 41 ++++----- ui/src/gallery/components/MetaDataInfo.tsx | 31 +++++-- ui/src/gallery/components/MetaInfoBox.tsx | 49 +++++++---- ui/src/gallery/components/TopForm/TopForm.tsx | 13 ++- ui/src/types/dbTypes.ts | 2 + 10 files changed, 196 insertions(+), 113 deletions(-) create mode 100644 ui/src/gallery/components/MetaBox/metaBoxContext.ts diff --git a/ui/src/db-tables/WorkflowsTable.ts b/ui/src/db-tables/WorkflowsTable.ts index 89c7c90..d59b67f 100644 --- a/ui/src/db-tables/WorkflowsTable.ts +++ b/ui/src/db-tables/WorkflowsTable.ts @@ -157,6 +157,13 @@ export class WorkflowsTable extends TableBase { throw new Error("Method not allowed."); } + public async updateTopFields( + id: string, + change: Pick, "topFieldsConfig">, + ): Promise { + return this._update(id, change); + } + public async updateMetaInfo( id: string, change: Omit, "id" | "name" | "parentFolderID" | "json">, diff --git a/ui/src/gallery/GalleryModal.tsx b/ui/src/gallery/GalleryModal.tsx index 1a9c93b..947c242 100644 --- a/ui/src/gallery/GalleryModal.tsx +++ b/ui/src/gallery/GalleryModal.tsx @@ -1,3 +1,5 @@ +// @ts-expect-error ComfyUI import +import { app } from "/scripts/app.js"; import { Checkbox, Flex, @@ -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([]); - const [metaData, setMetaData] = useState(); + const [metaData, setMetaData] = useState(); const [searchValue, setSearchValue] = useState(""); const onUpdateSearchValue = (val: string) => { setSearchValue(val); @@ -38,7 +42,23 @@ export default function GalleryModal({ onclose }: { onclose: () => void }) { if (curFlowID == null) return; const media = await mediaTable?.listByWorkflowID(curFlowID); setImages(media ?? []); - if (Number(media?.length) <= 6 && media?.[0]) { + 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]); } }; @@ -86,7 +106,7 @@ export default function GalleryModal({ onclose }: { onclose: () => void }) { - {!!metaData && images.length > 6 && ( + {!!metaData && ( setMetaData(undefined)} variant={"ghost"} diff --git a/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx b/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx index f816d72..ecab4d3 100644 --- a/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx +++ b/ui/src/gallery/components/AllPromptForm/AllPromptForm.tsx @@ -8,28 +8,21 @@ 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 { Fragment, 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 type FormBoxProps = { - metaData: MetaData; - topFields: TopFieldType[]; - updateMetaData: FormItem["updateMetaData"]; - updateTopField?: (field: TopFieldType) => void; - calcInputList: InputResultItem[]; - showNodeName: boolean; -}; -export default function AllPromptForm({ - metaData, - updateMetaData, - topFields, - updateTopField, - calcInputList, - showNodeName, -}: FormBoxProps) { const [defaultIndex, setDefaultIndex] = useState([]); const groupInputList = diff --git a/ui/src/gallery/components/MetaBox/MetaBox.tsx b/ui/src/gallery/components/MetaBox/MetaBox.tsx index ae3bea6..c604c8a 100644 --- a/ui/src/gallery/components/MetaBox/MetaBox.tsx +++ b/ui/src/gallery/components/MetaBox/MetaBox.tsx @@ -3,9 +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 { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { FormItem } from "../FormItem/types.ts"; -import { calcMeta, type InputResultItem } from "./utils.ts"; +import { calcMeta, getNodeTypeByPath, 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; @@ -35,19 +38,12 @@ export default function MetaBox({ }) { const _metaData = JSON.parse(JSON.stringify(oriMetaData)); const [calcInputList, setCalcInputList] = useState([]); + const { curFlowID } = useContext(WorkspaceContext); useEffect(() => { - const calcInput = calcMeta(_metaData); + const allInputList = calcMeta(_metaData); + const calcInput = allInputList.map((input) => getNodeTypeByPath({ input })); setCalcInputList(calcInput); - setTopFields( - calcInput - ?.filter((input: InputResultItem) => input.isTop) - ?.map((input: InputResultItem) => ({ - name: input.name, - promptKey: input.linkId, - class_type: input.class_type, - })), - ); }, []); const [topFields, setTopFields] = useState([]); @@ -76,6 +72,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, { @@ -84,34 +91,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 ( - - - - + + + + + + ); } diff --git a/ui/src/gallery/components/MetaBox/metaBoxContext.ts b/ui/src/gallery/components/MetaBox/metaBoxContext.ts new file mode 100644 index 0000000..9aa75f8 --- /dev/null +++ b/ui/src/gallery/components/MetaBox/metaBoxContext.ts @@ -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({ + topFields: [], + calcInputList: [], + metaData: {} as MetaData, + showNodeName: false, + updateMetaData: undefined, + updateTopField(): void {}, +}); diff --git a/ui/src/gallery/components/MetaBox/utils.ts b/ui/src/gallery/components/MetaBox/utils.ts index c3f2a96..304fbc6 100644 --- a/ui/src/gallery/components/MetaBox/utils.ts +++ b/ui/src/gallery/components/MetaBox/utils.ts @@ -108,7 +108,8 @@ const getInputConfig = (props: { classType: string; name: string }) => { }; }; -function getTopInfo({ input }: { input: InputResultItem }) { +// 根据节点路径信息猜测节点类型(模型,正向提示词,方向提示词) +export function getNodeTypeByPath({ input }: { input: InputResultItem }) { if ( input.name === "width" && input.inputInfo?.output?.some((out: string) => out === "LATENT") @@ -170,7 +171,7 @@ function getTopInfo({ input }: { input: InputResultItem }) { if ( input.path.some((v) => v.name === "positive") && input.inputInfo?.output?.some((out: string) => - ["CONDITIONING", "STRING"].indexOf(out), + ["CONDITIONING", "STRING"].includes(out), ) && input.inputInfo?.["0"] === "STRING" ) { @@ -183,7 +184,7 @@ function getTopInfo({ input }: { input: InputResultItem }) { if ( input.path.some((v) => v.name === "negative") && input.inputInfo?.output?.some((out: string) => - ["CONDITIONING", "STRING"].indexOf(out), + ["CONDITIONING", "STRING"].includes(out), ) && input.inputInfo?.["0"] === "STRING" ) { @@ -232,22 +233,23 @@ function getInputListByLinkId({ return []; } const class_type = prompt[linkId].class_type; - const inputList = Object.keys(prompt[linkId].inputs).map( + const currentInputKeyList = Object.keys(prompt[linkId].inputs); + const inputList = currentInputKeyList.map( (input) => - getTopInfo({ - input: { - name: input, - class_type: class_type, - linkId: linkId, - value: prompt[linkId]?.inputs?.[input], - path: parentItem ?? [], - inputInfo: getInputConfig({ classType: class_type, name: input }), - }, + ({ + name: input, + class_type: class_type, + linkId: linkId, + value: prompt[linkId]?.inputs?.[input], + path: parentItem ?? [], + inputInfo: getInputConfig({ classType: class_type, name: input }), }) as InputResultItem, ); + // 遍历节点所有input const childrenInputList = inputList.reduce( (previousValue, currentValue) => { + // 如果节点为array,则数据为上一节点传入的数据,递归查询上一节点的信息,同时保存当前节点路径,便于后续查找节点的父子关系 if (Array.isArray(currentValue.value)) { const curList = getInputListByLinkId({ oriMeta, @@ -256,6 +258,7 @@ function getInputListByLinkId({ }); return [ ...previousValue, + // 如果之前已经添加过该节点,则跳过,(有的节点数据会提供给多个node) ...curList.filter( (cur) => !previousValue.some( @@ -270,26 +273,18 @@ function getInputListByLinkId({ [], ); + // 最终输出的是当前节点的信息, 加上递归的节点数据 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); - console.log(432, rootId); - console.log( - 465, - getInputListByLinkId({ - oriMeta, - linkId: `${rootId[0]}`, - }), - oriMeta.prompt, - oriMeta.workflow, - ); if (rootId.length) { return getInputListByLinkId({ oriMeta, diff --git a/ui/src/gallery/components/MetaDataInfo.tsx b/ui/src/gallery/components/MetaDataInfo.tsx index 01eceb2..0c556a4 100644 --- a/ui/src/gallery/components/MetaDataInfo.tsx +++ b/ui/src/gallery/components/MetaDataInfo.tsx @@ -3,6 +3,7 @@ import { FC, useEffect, useState } from "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; @@ -47,13 +48,29 @@ export const MetaDataInfo: FC = ({ mediaList, media }) => { border={mediaAct?.id === media.id ? "1px solid gray" : ""} onClick={() => setMediaAct(media)} > - {`image-bottom-${media.id}`} + {isImageFormat( + `/workspace/view_media?filename=${media.localPath}`, + ) ? ( + {`image-${media.id}`} + ) : ( + + )} ))} diff --git a/ui/src/gallery/components/MetaInfoBox.tsx b/ui/src/gallery/components/MetaInfoBox.tsx index a0bcde2..fe435e6 100644 --- a/ui/src/gallery/components/MetaInfoBox.tsx +++ b/ui/src/gallery/components/MetaInfoBox.tsx @@ -14,7 +14,10 @@ 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(); const getMetaData = async (curMedia: Media) => { @@ -29,7 +32,11 @@ export const MetaInfoBox = ({ media }: { media?: Media }) => { }; useEffect(() => { if (media) { - getMetaData(media); + if (media?.metaData) { + setMediaMetaData(media?.metaData); + } else { + getMetaData(media); + } } }, [media]); @@ -37,23 +44,31 @@ export const MetaInfoBox = ({ media }: { media?: Media }) => { - {media?.localPath} - - - } - aria-label="donwload image from gallery" - /> - - + {media?.localPath && ( + <> + {media?.localPath} + + + } + aria-label="donwload image from gallery" + /> + + + + )} - Create Time: - {formatTimestamp(media?.createTime ?? 0, true)} + {!!media?.createTime && ( + <> + Create Time: + {formatTimestamp(media?.createTime ?? 0, true)} + + )} Date: Sun, 17 Mar 2024 16:59:27 +0800 Subject: [PATCH 5/6] feat(gallery): delete getNodeTypeByPath --- ui/src/gallery/components/MetaBox/MetaBox.tsx | 3 +- ui/src/gallery/components/MetaBox/utils.ts | 128 ++++-------------- 2 files changed, 27 insertions(+), 104 deletions(-) diff --git a/ui/src/gallery/components/MetaBox/MetaBox.tsx b/ui/src/gallery/components/MetaBox/MetaBox.tsx index c604c8a..fcbd60e 100644 --- a/ui/src/gallery/components/MetaBox/MetaBox.tsx +++ b/ui/src/gallery/components/MetaBox/MetaBox.tsx @@ -41,8 +41,7 @@ export default function MetaBox({ const { curFlowID } = useContext(WorkspaceContext); useEffect(() => { - const allInputList = calcMeta(_metaData); - const calcInput = allInputList.map((input) => getNodeTypeByPath({ input })); + const calcInput = calcMeta(_metaData); setCalcInputList(calcInput); }, []); diff --git a/ui/src/gallery/components/MetaBox/utils.ts b/ui/src/gallery/components/MetaBox/utils.ts index 304fbc6..dd09ddc 100644 --- a/ui/src/gallery/components/MetaBox/utils.ts +++ b/ui/src/gallery/components/MetaBox/utils.ts @@ -108,97 +108,6 @@ const getInputConfig = (props: { classType: string; name: string }) => { }; }; -// 根据节点路径信息猜测节点类型(模型,正向提示词,方向提示词) -export function getNodeTypeByPath({ input }: { input: InputResultItem }) { - if ( - input.name === "width" && - input.inputInfo?.output?.some((out: string) => out === "LATENT") - ) { - return { - ...input, - isTop: true, - }; - } - if ( - input.name === "cfg" && - input.inputInfo?.output?.some((out: string) => out === "LATENT") - ) { - return { - ...input, - isTop: true, - }; - } - - if ( - input.name === "steps" && - input.inputInfo?.output?.some((out: string) => out === "LATENT") - ) { - return { - ...input, - isTop: true, - }; - } - if ( - input.name === "sampler_name" && - input.inputInfo?.output?.some((out: string) => out === "LATENT") - ) { - return { - ...input, - isTop: true, - }; - } - - if ( - input.name === "height" && - input.inputInfo?.output?.some((out: string) => out === "LATENT") - ) { - return { - ...input, - isTop: true, - }; - } - - if ( - input.path.some((v) => v.name === "model") && - input.inputInfo?.output?.some((out: string) => out === "MODEL") - ) { - return { - ...input, - isTop: true, - }; - } - - if ( - input.path.some((v) => v.name === "positive") && - input.inputInfo?.output?.some((out: string) => - ["CONDITIONING", "STRING"].includes(out), - ) && - input.inputInfo?.["0"] === "STRING" - ) { - return { - ...input, - formLabel: `positive-${input.name}`, - isTop: true, - }; - } - if ( - input.path.some((v) => v.name === "negative") && - input.inputInfo?.output?.some((out: string) => - ["CONDITIONING", "STRING"].includes(out), - ) && - input.inputInfo?.["0"] === "STRING" - ) { - return { - ...input, - formLabel: `negative-${input.name}`, - isTop: true, - }; - } - return { - ...input, - }; -} - function fineRoot(prompt: Meta["prompt"]) { const keyList = Object.keys(prompt); const allInputs = keyList.reduce((previousValue, currentValue) => { @@ -234,17 +143,32 @@ function getInputListByLinkId({ } const class_type = prompt[linkId].class_type; const currentInputKeyList = Object.keys(prompt[linkId].inputs); - const inputList = currentInputKeyList.map( - (input) => - ({ - name: input, - class_type: class_type, - linkId: linkId, - value: prompt[linkId]?.inputs?.[input], - path: parentItem ?? [], - inputInfo: getInputConfig({ classType: class_type, name: input }), - }) as InputResultItem, - ); + 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( From b9790c6c75a6649f23adcb26a1926805ffc11f74 Mon Sep 17 00:00:00 2001 From: ss707494 Date: Sun, 17 Mar 2024 16:59:42 +0800 Subject: [PATCH 6/6] feat(gallery): delete getNodeTypeByPath --- ui/src/gallery/components/MetaBox/MetaBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/gallery/components/MetaBox/MetaBox.tsx b/ui/src/gallery/components/MetaBox/MetaBox.tsx index fcbd60e..02c1604 100644 --- a/ui/src/gallery/components/MetaBox/MetaBox.tsx +++ b/ui/src/gallery/components/MetaBox/MetaBox.tsx @@ -5,7 +5,7 @@ import TopForm from "../TopForm/TopForm.tsx"; import AllPromptForm from "../AllPromptForm/AllPromptForm.tsx"; import { useContext, useEffect, useState } from "react"; import { FormItem } from "../FormItem/types.ts"; -import { calcMeta, getNodeTypeByPath, type InputResultItem } from "./utils.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";