mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Full Asset Selection Experience (Assets API) (#5900)
## Summary Full Integration of Asset Browsing and Selection when Assets API is enabled. ## Changes 1. Replace Model Left Side Tab with experience 2. Configurable titles for the Asset Browser Modal 3. Refactors to simplify callback code 4. Refactor to make modal filters reactive (they change their values based on assets displayed) 5. Add `browse()` mode with ability to create node directly from the Asset Browser Modal (in `browse()` mode) ## Screenshots Demo of many different types of Nodes getting configured by the Modal https://github.com/user-attachments/assets/34f9c964-cdf2-4c5d-86a9-a8e7126a7de9 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5900-Feat-asset-selection-cloud-integration-2816d73d365081ccb4aeecdc14b0e5d3) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -11,14 +11,11 @@ import { api } from '@/scripts/api'
|
||||
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
|
||||
|
||||
const ASSETS_ENDPOINT = '/assets'
|
||||
const MODELS_TAG = 'models'
|
||||
const MISSING_TAG = 'missing'
|
||||
const EXPERIMENTAL_WARNING = `EXPERIMENTAL: If you are seeing this please make sure "Comfy.Assets.UseAssetAPI" is set to "false" in your ComfyUI Settings.\n`
|
||||
const DEFAULT_LIMIT = 300
|
||||
|
||||
/**
|
||||
* Input names that are eligible for asset browser
|
||||
*/
|
||||
const WHITELISTED_INPUTS = new Set(['ckpt_name', 'lora_name', 'vae_name'])
|
||||
export const MODELS_TAG = 'models'
|
||||
export const MISSING_TAG = 'missing'
|
||||
|
||||
/**
|
||||
* Validates asset response data using Zod schema
|
||||
@@ -66,7 +63,7 @@ function createAssetService() {
|
||||
*/
|
||||
async function getAssetModelFolders(): Promise<ModelFolder[]> {
|
||||
const data = await handleAssetRequest(
|
||||
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG}`,
|
||||
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG}&limit=${DEFAULT_LIMIT}`,
|
||||
'model folders'
|
||||
)
|
||||
|
||||
@@ -95,7 +92,7 @@ function createAssetService() {
|
||||
*/
|
||||
async function getAssetModels(folder: string): Promise<ModelFile[]> {
|
||||
const data = await handleAssetRequest(
|
||||
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG},${folder}`,
|
||||
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG},${folder}&limit=${DEFAULT_LIMIT}`,
|
||||
`models for ${folder}`
|
||||
)
|
||||
|
||||
@@ -115,19 +112,12 @@ function createAssetService() {
|
||||
/**
|
||||
* Checks if a widget input should use the asset browser based on both input name and node comfyClass
|
||||
*
|
||||
* @param inputName - The input name (e.g., 'ckpt_name', 'lora_name')
|
||||
* @param nodeType - The ComfyUI node comfyClass (e.g., 'CheckpointLoaderSimple', 'LoraLoader')
|
||||
* @returns true if this input should use asset browser
|
||||
*/
|
||||
function isAssetBrowserEligible(
|
||||
inputName: string,
|
||||
nodeType: string
|
||||
): boolean {
|
||||
function isAssetBrowserEligible(nodeType: string = ''): boolean {
|
||||
return (
|
||||
// Must be an approved input name
|
||||
WHITELISTED_INPUTS.has(inputName) &&
|
||||
// Must be a registered node type
|
||||
useModelToNodeStore().getRegisteredNodeTypes().has(nodeType)
|
||||
!!nodeType && useModelToNodeStore().getRegisteredNodeTypes().has(nodeType)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -153,7 +143,7 @@ function createAssetService() {
|
||||
|
||||
// Fetch assets for this category using same API pattern as getAssetModels
|
||||
const data = await handleAssetRequest(
|
||||
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG},${category}`,
|
||||
`${ASSETS_ENDPOINT}?include_tags=${MODELS_TAG},${category}&limit=${DEFAULT_LIMIT}`,
|
||||
`assets for ${nodeType}`
|
||||
)
|
||||
|
||||
@@ -196,12 +186,30 @@ function createAssetService() {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets assets filtered by a specific tag
|
||||
*
|
||||
* @param tag - The tag to filter by (e.g., 'models')
|
||||
* @returns Promise<AssetItem[]> - Full asset objects filtered by tag, excluding missing assets
|
||||
*/
|
||||
async function getAssetsByTag(tag: string): Promise<AssetItem[]> {
|
||||
const data = await handleAssetRequest(
|
||||
`${ASSETS_ENDPOINT}?include_tags=${tag}&limit=${DEFAULT_LIMIT}`,
|
||||
`assets for tag ${tag}`
|
||||
)
|
||||
|
||||
return (
|
||||
data?.assets?.filter((asset) => !asset.tags.includes(MISSING_TAG)) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
getAssetModelFolders,
|
||||
getAssetModels,
|
||||
isAssetBrowserEligible,
|
||||
getAssetsForNodeType,
|
||||
getAssetDetails
|
||||
getAssetDetails,
|
||||
getAssetsByTag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user