Skip to content

Commit 44e4704

Browse files
authored
[feat] carve out path to call asset browser in combo widget (#5464)
* [ci] ignore local browser tests files this is where i have claude put its one off playwright scripts * [feat] carve out path to call asset browser in combo widget * [feat] use buttons on Model Loaders when Asset API setting is on
1 parent ca22044 commit 44e4704

File tree

9 files changed

+423
-56
lines changed

9 files changed

+423
-56
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ tests-ui/workflows/examples
5151
/blob-report/
5252
/playwright/.cache/
5353
browser_tests/**/*-win32.png
54+
browser-tests/local/
5455

5556
.env
5657

src/locales/en/main.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,9 @@
17571757
"copiedTooltip": "Copied",
17581758
"copyTooltip": "Copy message to clipboard"
17591759
},
1760+
"widgets": {
1761+
"selectModel": "Select model"
1762+
},
17601763
"nodeHelpPage": {
17611764
"inputs": "Inputs",
17621765
"outputs": "Outputs",

src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { ref } from 'vue'
22

33
import MultiSelectWidget from '@/components/graph/widgets/MultiSelectWidget.vue'
4+
import { t } from '@/i18n'
45
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
5-
import type { IComboWidget } from '@/lib/litegraph/src/types/widgets'
6+
import type {
7+
IBaseWidget,
8+
IComboWidget
9+
} from '@/lib/litegraph/src/types/widgets'
610
import { transformInputSpecV2ToV1 } from '@/schemas/nodeDef/migration'
711
import {
812
ComboInputSpec,
@@ -18,6 +22,8 @@ import {
1822
type ComfyWidgetConstructorV2,
1923
addValueControlWidgets
2024
} from '@/scripts/widgets'
25+
import { assetService } from '@/services/assetService'
26+
import { useSettingStore } from '@/stores/settingStore'
2127

2228
import { useRemoteWidget } from './useRemoteWidget'
2329

@@ -28,7 +34,10 @@ const getDefaultValue = (inputSpec: ComboInputSpec) => {
2834
return undefined
2935
}
3036

31-
const addMultiSelectWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
37+
const addMultiSelectWidget = (
38+
node: LGraphNode,
39+
inputSpec: ComboInputSpec
40+
): IBaseWidget => {
3241
const widgetValue = ref<string[]>([])
3342
const widget = new ComponentWidgetImpl({
3443
node,
@@ -48,7 +57,36 @@ const addMultiSelectWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
4857
return widget
4958
}
5059

51-
const addComboWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
60+
const addComboWidget = (
61+
node: LGraphNode,
62+
inputSpec: ComboInputSpec
63+
): IBaseWidget => {
64+
const settingStore = useSettingStore()
65+
const isUsingAssetAPI = settingStore.get('Comfy.Assets.UseAssetAPI')
66+
const isEligible = assetService.isAssetBrowserEligible(
67+
inputSpec.name,
68+
node.comfyClass || ''
69+
)
70+
71+
if (isUsingAssetAPI && isEligible) {
72+
// Create button widget for Asset Browser
73+
const currentValue = getDefaultValue(inputSpec)
74+
75+
const widget = node.addWidget(
76+
'button',
77+
inputSpec.name,
78+
t('widgets.selectModel'),
79+
() => {
80+
console.log(
81+
`Asset Browser would open here for:\nNode: ${node.type}\nWidget: ${inputSpec.name}\nCurrent Value:${currentValue}`
82+
)
83+
}
84+
)
85+
86+
return widget
87+
}
88+
89+
// Create normal combo widget
5290
const defaultValue = getDefaultValue(inputSpec)
5391
const comboOptions = inputSpec.options ?? []
5492
const widget = node.addWidget(
@@ -59,14 +97,14 @@ const addComboWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
5997
{
6098
values: comboOptions
6199
}
62-
) as IComboWidget
100+
)
63101

64102
if (inputSpec.remote) {
65103
const remoteWidget = useRemoteWidget({
66104
remoteConfig: inputSpec.remote,
67105
defaultValue,
68106
node,
69-
widget
107+
widget: widget as IComboWidget
70108
})
71109
if (inputSpec.remote.refresh_button) remoteWidget.addRefreshButton()
72110

@@ -84,7 +122,7 @@ const addComboWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
84122
if (inputSpec.control_after_generate) {
85123
widget.linkedWidgets = addValueControlWidgets(
86124
node,
87-
widget,
125+
widget as IComboWidget,
88126
undefined,
89127
undefined,
90128
transformInputSpecV2ToV1(inputSpec)

src/services/assetService.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ import {
77
assetResponseSchema
88
} from '@/schemas/assetSchema'
99
import { api } from '@/scripts/api'
10+
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
1011

1112
const ASSETS_ENDPOINT = '/assets'
1213
const MODELS_TAG = 'models'
1314
const MISSING_TAG = 'missing'
1415

16+
/**
17+
* Input names that are eligible for asset browser
18+
*/
19+
const WHITELISTED_INPUTS = new Set(['ckpt_name', 'lora_name', 'vae_name'])
20+
1521
/**
1622
* Validates asset response data using Zod schema
1723
*/
@@ -102,9 +108,29 @@ function createAssetService() {
102108
)
103109
}
104110

111+
/**
112+
* Checks if a widget input should use the asset browser based on both input name and node comfyClass
113+
*
114+
* @param inputName - The input name (e.g., 'ckpt_name', 'lora_name')
115+
* @param nodeType - The ComfyUI node comfyClass (e.g., 'CheckpointLoaderSimple', 'LoraLoader')
116+
* @returns true if this input should use asset browser
117+
*/
118+
function isAssetBrowserEligible(
119+
inputName: string,
120+
nodeType: string
121+
): boolean {
122+
return (
123+
// Must be an approved input name
124+
WHITELISTED_INPUTS.has(inputName) &&
125+
// Must be a registered node type
126+
useModelToNodeStore().getRegisteredNodeTypes().has(nodeType)
127+
)
128+
}
129+
105130
return {
106131
getAssetModelFolders,
107-
getAssetModels
132+
getAssetModels,
133+
isAssetBrowserEligible
108134
}
109135
}
110136

src/services/litegraphService.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,18 @@ export const useLitegraphService = () => {
484484
) ?? {}
485485

486486
if (widget) {
487-
widget.label = st(nameKey, widget.label ?? inputName)
487+
// Check if this is an Asset Browser button widget
488+
const isAssetBrowserButton =
489+
widget.type === 'button' && widget.value === 'Select model'
490+
491+
if (isAssetBrowserButton) {
492+
// Preserve Asset Browser button label (don't translate)
493+
widget.label = String(widget.value)
494+
} else {
495+
// Apply normal translation for other widgets
496+
widget.label = st(nameKey, widget.label ?? inputName)
497+
}
498+
488499
widget.options ??= {}
489500
Object.assign(widget.options, {
490501
advanced: inputSpec.advanced,

src/stores/modelToNodeStore.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineStore } from 'pinia'
2-
import { ref } from 'vue'
2+
import { computed, ref } from 'vue'
33

44
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
55

@@ -22,6 +22,22 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
2222
const modelToNodeMap = ref<Record<string, ModelNodeProvider[]>>({})
2323
const nodeDefStore = useNodeDefStore()
2424
const haveDefaultsLoaded = ref(false)
25+
26+
/** Internal computed for reactive caching of registered node types */
27+
const registeredNodeTypes = computed(() => {
28+
return new Set(
29+
Object.values(modelToNodeMap.value)
30+
.flat()
31+
.map((provider) => provider.nodeDef.name)
32+
)
33+
})
34+
35+
/** Get set of all registered node types for efficient lookup */
36+
function getRegisteredNodeTypes(): Set<string> {
37+
registerDefaults()
38+
return registeredNodeTypes.value
39+
}
40+
2541
/**
2642
* Get the node provider for the given model type name.
2743
* @param modelType The name of the model type to get the node provider for.
@@ -91,6 +107,7 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
91107

92108
return {
93109
modelToNodeMap,
110+
getRegisteredNodeTypes,
94111
getNodeProvider,
95112
getAllNodeProviders,
96113
registerNodeProvider,

0 commit comments

Comments
 (0)