mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 02:32:18 +00:00
[Manager] Preview the individual nodes for packs on the registry (#3408)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -4,7 +4,10 @@
|
|||||||
<div class="top-0 z-10 px-6 pt-6 w-full">
|
<div class="top-0 z-10 px-6 pt-6 w-full">
|
||||||
<InfoPanelHeader :node-packs="[nodePack]" />
|
<InfoPanelHeader :node-packs="[nodePack]" />
|
||||||
</div>
|
</div>
|
||||||
<div class="p-6 pt-2 overflow-y-auto flex-1 text-sm hidden-scrollbar">
|
<div
|
||||||
|
ref="scrollContainer"
|
||||||
|
class="p-6 pt-2 overflow-y-auto flex-1 text-sm hidden-scrollbar"
|
||||||
|
>
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<MetadataRow
|
<MetadataRow
|
||||||
v-if="isPackInstalled(nodePack.id)"
|
v-if="isPackInstalled(nodePack.id)"
|
||||||
@@ -46,7 +49,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { whenever } from '@vueuse/core'
|
import { useScroll, whenever } from '@vueuse/core'
|
||||||
import { computed, provide, ref } from 'vue'
|
import { computed, provide, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
@@ -70,6 +73,8 @@ const { nodePack } = defineProps<{
|
|||||||
nodePack: components['schemas']['Node']
|
nodePack: components['schemas']['Node']
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const scrollContainer = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
const managerStore = useComfyManagerStore()
|
const managerStore = useComfyManagerStore()
|
||||||
const isInstalled = computed(() => managerStore.isPackInstalled(nodePack.id))
|
const isInstalled = computed(() => managerStore.isPackInstalled(nodePack.id))
|
||||||
const isInstalling = ref(false)
|
const isInstalling = ref(false)
|
||||||
@@ -103,6 +108,17 @@ const infoItems = computed<InfoItem[]>(() => [
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const { y } = useScroll(scrollContainer, {
|
||||||
|
eventListenerOptions: {
|
||||||
|
passive: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const onNodePackChange = () => {
|
||||||
|
y.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
whenever(() => nodePack, onNodePackChange, { immediate: true, deep: true })
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.hidden-scrollbar {
|
.hidden-scrollbar {
|
||||||
|
|||||||
@@ -31,28 +31,29 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAsyncState } from '@vueuse/core'
|
import { useAsyncState } from '@vueuse/core'
|
||||||
import { computed } from 'vue'
|
import { computed, onUnmounted } from 'vue'
|
||||||
|
|
||||||
import PackStatusMessage from '@/components/dialog/content/manager/PackStatusMessage.vue'
|
import PackStatusMessage from '@/components/dialog/content/manager/PackStatusMessage.vue'
|
||||||
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
|
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
|
||||||
import InfoPanelHeader from '@/components/dialog/content/manager/infoPanel/InfoPanelHeader.vue'
|
import InfoPanelHeader from '@/components/dialog/content/manager/infoPanel/InfoPanelHeader.vue'
|
||||||
import MetadataRow from '@/components/dialog/content/manager/infoPanel/MetadataRow.vue'
|
import MetadataRow from '@/components/dialog/content/manager/infoPanel/MetadataRow.vue'
|
||||||
import PackIconStacked from '@/components/dialog/content/manager/packIcon/PackIconStacked.vue'
|
import PackIconStacked from '@/components/dialog/content/manager/packIcon/PackIconStacked.vue'
|
||||||
import { useComfyRegistryService } from '@/services/comfyRegistryService'
|
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||||
import { components } from '@/types/comfyRegistryTypes'
|
import { components } from '@/types/comfyRegistryTypes'
|
||||||
|
|
||||||
const { nodePacks } = defineProps<{
|
const { nodePacks } = defineProps<{
|
||||||
nodePacks: components['schemas']['Node'][]
|
nodePacks: components['schemas']['Node'][]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const comfyRegistryService = useComfyRegistryService()
|
const { getNodeDefs } = useComfyRegistryStore()
|
||||||
|
|
||||||
const getPackNodes = async (pack: components['schemas']['Node']) => {
|
const getPackNodes = async (pack: components['schemas']['Node']) => {
|
||||||
if (!comfyRegistryService.packNodesAvailable(pack)) return []
|
if (!pack.latest_version?.version) return []
|
||||||
return comfyRegistryService.getNodeDefs({
|
const nodeDefs = await getNodeDefs.call({
|
||||||
packId: pack.id,
|
packId: pack.id,
|
||||||
versionId: pack.latest_version?.id
|
version: pack.latest_version?.version
|
||||||
})
|
})
|
||||||
|
return nodeDefs?.comfy_nodes ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
const { state: allNodeDefs } = useAsyncState(
|
const { state: allNodeDefs } = useAsyncState(
|
||||||
@@ -69,4 +70,8 @@ const totalNodesCount = computed(() =>
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
getNodeDefs.cancel()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<DescriptionTabPanel :node-pack="nodePack" />
|
<DescriptionTabPanel :node-pack="nodePack" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="nodes">
|
<TabPanel value="nodes">
|
||||||
<NodesTabPanel :node-pack="nodePack" />
|
<NodesTabPanel :node-pack="nodePack" :node-names="nodeNames" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@@ -27,15 +27,21 @@ import TabList from 'primevue/tablist'
|
|||||||
import TabPanel from 'primevue/tabpanel'
|
import TabPanel from 'primevue/tabpanel'
|
||||||
import TabPanels from 'primevue/tabpanels'
|
import TabPanels from 'primevue/tabpanels'
|
||||||
import Tabs from 'primevue/tabs'
|
import Tabs from 'primevue/tabs'
|
||||||
import { ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
import DescriptionTabPanel from '@/components/dialog/content/manager/infoPanel/tabs/DescriptionTabPanel.vue'
|
import DescriptionTabPanel from '@/components/dialog/content/manager/infoPanel/tabs/DescriptionTabPanel.vue'
|
||||||
import NodesTabPanel from '@/components/dialog/content/manager/infoPanel/tabs/NodesTabPanel.vue'
|
import NodesTabPanel from '@/components/dialog/content/manager/infoPanel/tabs/NodesTabPanel.vue'
|
||||||
import { components } from '@/types/comfyRegistryTypes'
|
import { components } from '@/types/comfyRegistryTypes'
|
||||||
|
|
||||||
defineProps<{
|
const { nodePack } = defineProps<{
|
||||||
nodePack: components['schemas']['Node']
|
nodePack: components['schemas']['Node']
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const nodeNames = computed(() => {
|
||||||
|
// @ts-expect-error comfy_nodes is an Algolia-specific field
|
||||||
|
const { comfy_nodes } = nodePack
|
||||||
|
return comfy_nodes ?? []
|
||||||
|
})
|
||||||
|
|
||||||
const activeTab = ref('description')
|
const activeTab = ref('description')
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,47 +1,92 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-4 mt-4 overflow-auto text-sm">
|
<div class="flex flex-col gap-4 mt-4 text-sm">
|
||||||
<div v-if="nodeDefs?.length">
|
<template v-if="mappedNodeDefs?.length">
|
||||||
<!-- TODO: when registry returns node defs, use them here -->
|
<div
|
||||||
</div>
|
v-for="nodeDef in mappedNodeDefs"
|
||||||
<div
|
:key="createNodeDefKey(nodeDef)"
|
||||||
v-for="i in 3"
|
class="border border-surface-border rounded-lg p-4"
|
||||||
v-else
|
>
|
||||||
:key="i"
|
<NodePreview :node-def="nodeDef" class="!text-[.625rem] !min-w-full" />
|
||||||
class="border border-surface-border rounded-lg p-4"
|
</div>
|
||||||
>
|
</template>
|
||||||
<NodePreview
|
<template v-else-if="isLoading">
|
||||||
:node-def="placeholderNodeDef"
|
<ProgressSpinner />
|
||||||
class="!text-[.625rem] !min-w-full"
|
</template>
|
||||||
|
<template v-else-if="nodeNames.length">
|
||||||
|
<div v-for="node in nodeNames" :key="node" class="text-muted truncate">
|
||||||
|
{{ node }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<NoResultsPlaceholder
|
||||||
|
:title="$t('manager.noNodesFound')"
|
||||||
|
:message="$t('manager.noNodesFoundDescription')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NodePreview from '@/components/node/NodePreview.vue'
|
import { whenever } from '@vueuse/core'
|
||||||
import { ComfyNodeDef } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
import ProgressSpinner from 'primevue/progressspinner'
|
||||||
import { components } from '@/types/comfyRegistryTypes'
|
import { computed, ref, shallowRef, useId } from 'vue'
|
||||||
|
|
||||||
defineProps<{
|
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||||
|
import NodePreview from '@/components/node/NodePreview.vue'
|
||||||
|
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||||
|
import { components, operations } from '@/types/comfyRegistryTypes'
|
||||||
|
import { registryToFrontendV2NodeDef } from '@/utils/mapperUtil'
|
||||||
|
|
||||||
|
type ListComfyNodesResponse =
|
||||||
|
operations['ListComfyNodes']['responses'][200]['content']['application/json']['comfy_nodes']
|
||||||
|
|
||||||
|
const { nodePack, nodeNames } = defineProps<{
|
||||||
nodePack: components['schemas']['Node']
|
nodePack: components['schemas']['Node']
|
||||||
nodeDefs?: components['schemas']['ComfyNode'][]
|
nodeNames: string[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// TODO: when registry returns node defs, use them here
|
const { getNodeDefs } = useComfyRegistryStore()
|
||||||
const placeholderNodeDef: ComfyNodeDef = {
|
|
||||||
name: 'Sample Node',
|
const isLoading = ref(false)
|
||||||
display_name: 'Sample Node',
|
const registryNodeDefs = shallowRef<ListComfyNodesResponse | null>(null)
|
||||||
description: 'This is a sample node for preview purposes',
|
|
||||||
inputs: {
|
const fetchNodeDefs = async () => {
|
||||||
input1: { name: 'Input 1', type: 'IMAGE' },
|
isLoading.value = true
|
||||||
input2: { name: 'Input 2', type: 'CONDITIONING' }
|
|
||||||
},
|
const { id: packId } = nodePack
|
||||||
outputs: [
|
const version = nodePack.latest_version?.version
|
||||||
{ name: 'Output 1', type: 'IMAGE', index: 0, is_list: false },
|
|
||||||
{ name: 'Output 2', type: 'MASK', index: 1, is_list: false }
|
if (!packId || !version) {
|
||||||
],
|
registryNodeDefs.value = null
|
||||||
category: 'Utility',
|
} else {
|
||||||
output_node: false,
|
const response = await getNodeDefs.call({
|
||||||
python_module: 'nodes'
|
packId,
|
||||||
|
version,
|
||||||
|
page: 1,
|
||||||
|
limit: 256
|
||||||
|
})
|
||||||
|
registryNodeDefs.value = response?.comfy_nodes ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
whenever(() => nodePack, fetchNodeDefs, { immediate: true, deep: true })
|
||||||
|
|
||||||
|
const toFrontendNodeDef = (nodeDef: components['schemas']['ComfyNode']) => {
|
||||||
|
try {
|
||||||
|
return registryToFrontendV2NodeDef(nodeDef, nodePack)
|
||||||
|
} catch (error) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const mappedNodeDefs = computed(() => {
|
||||||
|
if (!registryNodeDefs.value) return null
|
||||||
|
return registryNodeDefs.value
|
||||||
|
.map(toFrontendNodeDef)
|
||||||
|
.filter((nodeDef) => nodeDef !== null)
|
||||||
|
})
|
||||||
|
|
||||||
|
const createNodeDefKey = (nodeDef: components['schemas']['ComfyNode']) =>
|
||||||
|
`${nodeDef.category}${nodeDef.comfy_node_name ?? useId()}`
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -111,6 +111,8 @@
|
|||||||
"manager": {
|
"manager": {
|
||||||
"title": "Custom Nodes Manager",
|
"title": "Custom Nodes Manager",
|
||||||
"failed": "Failed ({count})",
|
"failed": "Failed ({count})",
|
||||||
|
"noNodesFound": "No nodes found",
|
||||||
|
"noNodesFoundDescription": "The pack's nodes either could not be parsed, or the pack is a frontend extension only and doesn't have any nodes.",
|
||||||
"installationQueue": "Installation Queue",
|
"installationQueue": "Installation Queue",
|
||||||
"changingVersion": "Changing version from {from} to {to}",
|
"changingVersion": "Changing version from {from} to {to}",
|
||||||
"dependencies": "Dependencies",
|
"dependencies": "Dependencies",
|
||||||
|
|||||||
@@ -428,6 +428,8 @@
|
|||||||
"loadingVersions": "Cargando versiones...",
|
"loadingVersions": "Cargando versiones...",
|
||||||
"nightlyVersion": "Nocturna",
|
"nightlyVersion": "Nocturna",
|
||||||
"noDescription": "No hay descripción disponible",
|
"noDescription": "No hay descripción disponible",
|
||||||
|
"noNodesFound": "No se encontraron nodos",
|
||||||
|
"noNodesFoundDescription": "Los nodos del paquete no se pudieron analizar, o el paquete es solo una extensión de frontend y no tiene ningún nodo.",
|
||||||
"noResultsFound": "No se encontraron resultados que coincidan con tu búsqueda.",
|
"noResultsFound": "No se encontraron resultados que coincidan con tu búsqueda.",
|
||||||
"nodePack": "Paquete de Nodos",
|
"nodePack": "Paquete de Nodos",
|
||||||
"packsSelected": "Paquetes Seleccionados",
|
"packsSelected": "Paquetes Seleccionados",
|
||||||
|
|||||||
@@ -428,6 +428,8 @@
|
|||||||
"loadingVersions": "Chargement des versions...",
|
"loadingVersions": "Chargement des versions...",
|
||||||
"nightlyVersion": "Nocturne",
|
"nightlyVersion": "Nocturne",
|
||||||
"noDescription": "Aucune description disponible",
|
"noDescription": "Aucune description disponible",
|
||||||
|
"noNodesFound": "Aucun nœud trouvé",
|
||||||
|
"noNodesFoundDescription": "Les nœuds du pack n'ont pas pu être analysés, ou le pack est une extension frontend uniquement et n'a pas de nœuds.",
|
||||||
"noResultsFound": "Aucun résultat trouvé correspondant à votre recherche.",
|
"noResultsFound": "Aucun résultat trouvé correspondant à votre recherche.",
|
||||||
"nodePack": "Pack de Nœuds",
|
"nodePack": "Pack de Nœuds",
|
||||||
"packsSelected": "Packs sélectionnés",
|
"packsSelected": "Packs sélectionnés",
|
||||||
|
|||||||
@@ -428,6 +428,8 @@
|
|||||||
"loadingVersions": "バージョンを読み込んでいます...",
|
"loadingVersions": "バージョンを読み込んでいます...",
|
||||||
"nightlyVersion": "ナイトリー",
|
"nightlyVersion": "ナイトリー",
|
||||||
"noDescription": "説明はありません",
|
"noDescription": "説明はありません",
|
||||||
|
"noNodesFound": "ノードが見つかりません",
|
||||||
|
"noNodesFoundDescription": "パックのノードは解析できなかったか、パックがフロントエンドの拡張機能のみでノードがない可能性があります。",
|
||||||
"noResultsFound": "検索に一致する結果が見つかりませんでした。",
|
"noResultsFound": "検索に一致する結果が見つかりませんでした。",
|
||||||
"nodePack": "ノードパック",
|
"nodePack": "ノードパック",
|
||||||
"packsSelected": "選択したパック",
|
"packsSelected": "選択したパック",
|
||||||
|
|||||||
@@ -428,6 +428,8 @@
|
|||||||
"loadingVersions": "버전 로딩 중...",
|
"loadingVersions": "버전 로딩 중...",
|
||||||
"nightlyVersion": "최신 테스트 버전(nightly)",
|
"nightlyVersion": "최신 테스트 버전(nightly)",
|
||||||
"noDescription": "설명이 없습니다",
|
"noDescription": "설명이 없습니다",
|
||||||
|
"noNodesFound": "노드를 찾을 수 없습니다",
|
||||||
|
"noNodesFoundDescription": "팩의 노드를 파싱할 수 없거나, 팩이 프론트엔드 확장만을 가지고 있어서 노드가 없습니다.",
|
||||||
"noResultsFound": "검색과 일치하는 결과가 없습니다.",
|
"noResultsFound": "검색과 일치하는 결과가 없습니다.",
|
||||||
"nodePack": "노드 팩",
|
"nodePack": "노드 팩",
|
||||||
"packsSelected": "선택한 노드 팩",
|
"packsSelected": "선택한 노드 팩",
|
||||||
|
|||||||
@@ -428,6 +428,8 @@
|
|||||||
"loadingVersions": "Загрузка версий...",
|
"loadingVersions": "Загрузка версий...",
|
||||||
"nightlyVersion": "Ночная",
|
"nightlyVersion": "Ночная",
|
||||||
"noDescription": "Описание отсутствует",
|
"noDescription": "Описание отсутствует",
|
||||||
|
"noNodesFound": "Узлы не найдены",
|
||||||
|
"noNodesFoundDescription": "Узлы пакета не могут быть проанализированы, или пакет является только расширением интерфейса и не имеет узлов.",
|
||||||
"noResultsFound": "По вашему запросу ничего не найдено.",
|
"noResultsFound": "По вашему запросу ничего не найдено.",
|
||||||
"nodePack": "Пакет Узлов",
|
"nodePack": "Пакет Узлов",
|
||||||
"packsSelected": "Выбрано пакетов",
|
"packsSelected": "Выбрано пакетов",
|
||||||
|
|||||||
@@ -428,6 +428,8 @@
|
|||||||
"loadingVersions": "正在加载版本...",
|
"loadingVersions": "正在加载版本...",
|
||||||
"nightlyVersion": "每夜",
|
"nightlyVersion": "每夜",
|
||||||
"noDescription": "无可用描述",
|
"noDescription": "无可用描述",
|
||||||
|
"noNodesFound": "未找到节点",
|
||||||
|
"noNodesFoundDescription": "无法解析包的节点,或者该包仅为前端扩展,没有任何节点。",
|
||||||
"noResultsFound": "未找到符合您搜索的结果。",
|
"noResultsFound": "未找到符合您搜索的结果。",
|
||||||
"nodePack": "节点包",
|
"nodePack": "节点包",
|
||||||
"packsSelected": "选定的包",
|
"packsSelected": "选定的包",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import axios, { AxiosError, AxiosResponse } from 'axios'
|
import axios, { AxiosError, AxiosResponse } from 'axios'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
|
||||||
import type { components, operations } from '@/types/comfyRegistryTypes'
|
import type { components, operations } from '@/types/comfyRegistryTypes'
|
||||||
import { isAbortError } from '@/utils/typeGuardUtil'
|
import { isAbortError } from '@/utils/typeGuardUtil'
|
||||||
|
|
||||||
@@ -25,29 +24,6 @@ export const useComfyRegistryService = () => {
|
|||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
|
|
||||||
const nodeDefStore = useNodeDefStore()
|
|
||||||
|
|
||||||
const isLocalNodePack = (nodePackId: string) =>
|
|
||||||
!!nodeDefStore.nodeDefsByName[nodePackId]
|
|
||||||
|
|
||||||
const isLocalNode = (nodeName: string, nodePackId: string) => {
|
|
||||||
if (!nodeDefStore.nodeDefsByName[nodeName]) return false
|
|
||||||
return (
|
|
||||||
nodeDefStore.nodeDefsByName[nodeName].python_module.toLowerCase() ===
|
|
||||||
nodePackId.toLowerCase()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the node definitions for the pack are available
|
|
||||||
*/
|
|
||||||
const packNodesAvailable = (node: components['schemas']['Node']) => {
|
|
||||||
if (node.id && isLocalNodePack(node.id)) return true
|
|
||||||
if (node.latest_version?.comfy_node_extract_status !== 'success')
|
|
||||||
return false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleApiError = (
|
const handleApiError = (
|
||||||
err: unknown,
|
err: unknown,
|
||||||
context: string,
|
context: string,
|
||||||
@@ -125,11 +101,11 @@ export const useComfyRegistryService = () => {
|
|||||||
const getNodeDefs = async (
|
const getNodeDefs = async (
|
||||||
params: {
|
params: {
|
||||||
packId: components['schemas']['Node']['id']
|
packId: components['schemas']['Node']['id']
|
||||||
versionId: components['schemas']['NodeVersion']['id']
|
version: components['schemas']['NodeVersion']['version']
|
||||||
},
|
} & operations['ListComfyNodes']['parameters']['query'],
|
||||||
signal?: AbortSignal
|
signal?: AbortSignal
|
||||||
) => {
|
) => {
|
||||||
const { packId, versionId } = params
|
const { packId, version: versionId, ...queryParams } = params
|
||||||
if (!packId || !versionId) return null
|
if (!packId || !versionId) return null
|
||||||
|
|
||||||
const endpoint = `/nodes/${packId}/versions/${versionId}/comfy-nodes`
|
const endpoint = `/nodes/${packId}/versions/${versionId}/comfy-nodes`
|
||||||
@@ -141,7 +117,10 @@ export const useComfyRegistryService = () => {
|
|||||||
|
|
||||||
return executeApiRequest(
|
return executeApiRequest(
|
||||||
() =>
|
() =>
|
||||||
registryApiClient.get<components['schemas']['ComfyNode'][]>(endpoint, {
|
registryApiClient.get<
|
||||||
|
operations['ListComfyNodes']['responses'][200]['content']['application/json']
|
||||||
|
>(endpoint, {
|
||||||
|
params: queryParams,
|
||||||
signal
|
signal
|
||||||
}),
|
}),
|
||||||
errorContext,
|
errorContext,
|
||||||
@@ -149,33 +128,6 @@ export const useComfyRegistryService = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a Comfy Node definition for a specific node in a specific version of a node pack
|
|
||||||
* @param packId - The ID of the node pack
|
|
||||||
* @param versionId - The version of the node pack
|
|
||||||
* @param comfyNodeName - The name of the comfy node (corresponds to `ComfyNodeDef#name`)
|
|
||||||
* @returns The node definition or null if not found or an error occurred
|
|
||||||
*/
|
|
||||||
const getNodeDef = async (
|
|
||||||
params: {
|
|
||||||
packId: components['schemas']['Node']['id']
|
|
||||||
versionId: components['schemas']['NodeVersion']['id']
|
|
||||||
comfyNodeName: components['schemas']['ComfyNode']['comfy_node_name']
|
|
||||||
},
|
|
||||||
signal?: AbortSignal
|
|
||||||
) => {
|
|
||||||
const { packId, versionId, comfyNodeName } = params
|
|
||||||
if (!comfyNodeName || !packId || !versionId) return null
|
|
||||||
if (isLocalNode(comfyNodeName, packId))
|
|
||||||
return nodeDefStore.nodeDefsByName[comfyNodeName]
|
|
||||||
|
|
||||||
const nodeDefs = await getNodeDefs({ packId, versionId }, signal)
|
|
||||||
return (
|
|
||||||
nodeDefs?.find((nodeDef) => nodeDef.comfy_node_name === comfyNodeName) ||
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a paginated list of packs matching specific criteria.
|
* Get a paginated list of packs matching specific criteria.
|
||||||
* Search packs using `search` param. Search individual nodes using `comfy_node_search` param.
|
* Search packs using `search` param. Search individual nodes using `comfy_node_search` param.
|
||||||
@@ -377,9 +329,7 @@ export const useComfyRegistryService = () => {
|
|||||||
getPackByVersion,
|
getPackByVersion,
|
||||||
getPublisherById,
|
getPublisherById,
|
||||||
listPacksForPublisher,
|
listPacksForPublisher,
|
||||||
getNodeDef,
|
|
||||||
getNodeDefs,
|
getNodeDefs,
|
||||||
postPackReview,
|
postPackReview
|
||||||
packNodesAvailable
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ type NodePack = components['schemas']['Node']
|
|||||||
type ListPacksParams = operations['listAllNodes']['parameters']['query']
|
type ListPacksParams = operations['listAllNodes']['parameters']['query']
|
||||||
type ListPacksResult =
|
type ListPacksResult =
|
||||||
operations['listAllNodes']['responses'][200]['content']['application/json']
|
operations['listAllNodes']['responses'][200]['content']['application/json']
|
||||||
type ComfyNode = components['schemas']['ComfyNode']
|
type GetNodeDefsParams = operations['ListComfyNodes']['parameters']['query'] & {
|
||||||
|
packId: components['schemas']['Node']['id']
|
||||||
|
version: components['schemas']['NodeVersion']['version']
|
||||||
|
}
|
||||||
type GetPackByIdPath = operations['getNode']['parameters']['path']['nodeId']
|
type GetPackByIdPath = operations['getNode']['parameters']['path']['nodeId']
|
||||||
|
|
||||||
const isNodePack = (pack: NodePack | undefined): pack is NodePack => {
|
const isNodePack = (pack: NodePack | undefined): pack is NodePack => {
|
||||||
@@ -89,8 +92,8 @@ export const useComfyRegistryStore = defineStore('comfyRegistry', () => {
|
|||||||
* Get the node definitions for a pack
|
* Get the node definitions for a pack
|
||||||
*/
|
*/
|
||||||
const getNodeDefs = useCachedRequest<
|
const getNodeDefs = useCachedRequest<
|
||||||
{ packId: string; versionId: string },
|
GetNodeDefsParams,
|
||||||
ComfyNode[]
|
operations['ListComfyNodes']['responses'][200]['content']['application/json']
|
||||||
>(registryService.getNodeDefs, { maxSize: PACK_BY_ID_CACHE_SIZE })
|
>(registryService.getNodeDefs, { maxSize: PACK_BY_ID_CACHE_SIZE })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
77
src/utils/mapperUtil.ts
Normal file
77
src/utils/mapperUtil.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { transformInputSpecV1ToV2 } from '@/schemas/nodeDef/migration'
|
||||||
|
import {
|
||||||
|
ComfyNodeDef as ComfyNodeDefV2,
|
||||||
|
InputSpec
|
||||||
|
} from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
|
import { ComfyNodeDef as ComfyNodeDefV1 } from '@/schemas/nodeDefSchema'
|
||||||
|
import { components } from '@/types/comfyRegistryTypes'
|
||||||
|
|
||||||
|
const registryToFrontendV2NodeOutputs = (
|
||||||
|
registryDef: components['schemas']['ComfyNode']
|
||||||
|
): ComfyNodeDefV2['outputs'] => {
|
||||||
|
const returnTypes = JSON.parse(registryDef.return_types ?? '{}')
|
||||||
|
if (!returnTypes.length) return []
|
||||||
|
|
||||||
|
const returnNames = JSON.parse(registryDef.return_names ?? '{}')
|
||||||
|
const outputsIsList = registryDef.output_is_list ?? []
|
||||||
|
|
||||||
|
const outputs = []
|
||||||
|
for (let i = 0; i < returnTypes.length; i++) {
|
||||||
|
outputs.push({
|
||||||
|
type: returnTypes[i],
|
||||||
|
name: returnNames[i] || returnTypes[i],
|
||||||
|
is_list: outputsIsList[i] ?? false,
|
||||||
|
index: i
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputs
|
||||||
|
}
|
||||||
|
|
||||||
|
const registryToFrontendV2NodeInputs = (
|
||||||
|
registryDef: components['schemas']['ComfyNode']
|
||||||
|
): ComfyNodeDefV2['inputs'] => {
|
||||||
|
const inputTypes = JSON.parse(
|
||||||
|
registryDef.input_types ?? '{}'
|
||||||
|
) as ComfyNodeDefV1['input']
|
||||||
|
if (!inputTypes || !Object.keys(inputTypes).length) return {}
|
||||||
|
|
||||||
|
const inputsV2: Record<string, InputSpec> = {}
|
||||||
|
|
||||||
|
if (inputTypes.required) {
|
||||||
|
Object.entries(inputTypes.required).forEach(([name, inputSpecV1]) => {
|
||||||
|
inputsV2[name] = transformInputSpecV1ToV2(inputSpecV1, {
|
||||||
|
name,
|
||||||
|
isOptional: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputTypes.optional) {
|
||||||
|
Object.entries(inputTypes.optional).forEach(([name, inputSpecV1]) => {
|
||||||
|
inputsV2[name] = transformInputSpecV1ToV2(inputSpecV1, {
|
||||||
|
name,
|
||||||
|
isOptional: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputsV2
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registryToFrontendV2NodeDef = (
|
||||||
|
nodeDef: components['schemas']['ComfyNode'],
|
||||||
|
nodePack: components['schemas']['Node']
|
||||||
|
): ComfyNodeDefV2 => {
|
||||||
|
const name = nodeDef.comfy_node_name ?? 'Node Name'
|
||||||
|
return {
|
||||||
|
category: nodeDef.category ?? 'unknown',
|
||||||
|
description: nodeDef.description ?? '',
|
||||||
|
display_name: name,
|
||||||
|
name,
|
||||||
|
inputs: registryToFrontendV2NodeInputs(nodeDef),
|
||||||
|
outputs: registryToFrontendV2NodeOutputs(nodeDef),
|
||||||
|
output_node: false,
|
||||||
|
python_module: nodePack.name ?? nodePack.id ?? 'unknown'
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user