mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## Summary Add asset browser dialog integration for combo widgets with full animation support and proper state management. (Thank you Claude from saving me me from merge conflict hell on this one.) ## Changes - Widget integration: combo widgets now use AssetBrowserModal for eligible asset types - Dialog animations: added animateHide() for smooth close transitions - Async operations: proper sequencing of widget updates and dialog animations - Service layer: added getAssetsForNodeType() and getAssetDetails() methods - Type safety: comprehensive TypeScript types and error handling - Test coverage: unit tests for all new functionality - Bonus: fixed the hardcoded labels in AssetFilterBar Widget behavior: - Shows asset browser button for eligible widgets when asset API enabled - Handles asset selection with proper callback sequencing - Maintains widget value updates and litegraph notification ## Review Focus I will call out some stuff inline. ## Screenshots https://github.com/user-attachments/assets/9d3a72cf-d2b0-445f-8022-4c49daa04637 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5629-feat-integrate-asset-browser-with-widget-system-2726d73d365081a9a98be9a2307aee0b) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com>
180 lines
4.5 KiB
TypeScript
180 lines
4.5 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
|
|
import AssetBrowserModal from '@/platform/assets/components/AssetBrowserModal.vue'
|
|
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
|
|
import {
|
|
createMockAssets,
|
|
mockAssets
|
|
} from '@/platform/assets/fixtures/ui-mock-assets'
|
|
|
|
// Story arguments interface
|
|
interface StoryArgs {
|
|
nodeType: string
|
|
inputName: string
|
|
currentValue: string
|
|
showLeftPanel?: boolean
|
|
}
|
|
|
|
const meta: Meta<StoryArgs> = {
|
|
title: 'Platform/Assets/AssetBrowserModal',
|
|
component: AssetBrowserModal,
|
|
parameters: {
|
|
layout: 'fullscreen'
|
|
},
|
|
argTypes: {
|
|
nodeType: {
|
|
control: 'select',
|
|
options: ['CheckpointLoaderSimple', 'VAELoader', 'ControlNetLoader'],
|
|
description: 'ComfyUI node type for context'
|
|
},
|
|
inputName: {
|
|
control: 'select',
|
|
options: ['ckpt_name', 'vae_name', 'control_net_name'],
|
|
description: 'Widget input name'
|
|
},
|
|
currentValue: {
|
|
control: 'text',
|
|
description: 'Current selected asset value'
|
|
},
|
|
showLeftPanel: {
|
|
control: 'boolean',
|
|
description: 'Whether to show the left panel with categories'
|
|
}
|
|
}
|
|
}
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
// Modal Layout Stories
|
|
export const Default: Story = {
|
|
args: {
|
|
nodeType: 'CheckpointLoaderSimple',
|
|
inputName: 'ckpt_name',
|
|
currentValue: '',
|
|
showLeftPanel: false
|
|
},
|
|
render: (args) => ({
|
|
components: { AssetBrowserModal },
|
|
setup() {
|
|
const onAssetSelect = (asset: AssetDisplayItem) => {
|
|
console.log('Selected asset:', asset)
|
|
}
|
|
const onClose = () => {
|
|
console.log('Modal closed')
|
|
}
|
|
|
|
return {
|
|
...args,
|
|
onAssetSelect,
|
|
onClose,
|
|
assets: mockAssets
|
|
}
|
|
},
|
|
template: `
|
|
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
|
|
<AssetBrowserModal
|
|
:node-type="nodeType"
|
|
:input-name="inputName"
|
|
:show-left-panel="showLeftPanel"
|
|
:assets="assets"
|
|
@asset-select="onAssetSelect"
|
|
@close="onClose"
|
|
/>
|
|
</div>
|
|
`
|
|
})
|
|
}
|
|
|
|
// Story demonstrating single asset type (auto-hides left panel)
|
|
export const SingleAssetType: Story = {
|
|
args: {
|
|
nodeType: 'CheckpointLoaderSimple',
|
|
inputName: 'ckpt_name',
|
|
currentValue: '',
|
|
showLeftPanel: false
|
|
},
|
|
render: (args) => ({
|
|
components: { AssetBrowserModal },
|
|
setup() {
|
|
const onAssetSelect = (asset: AssetDisplayItem) => {
|
|
console.log('Selected asset:', asset)
|
|
}
|
|
const onClose = () => {
|
|
console.log('Modal closed')
|
|
}
|
|
|
|
// Create assets with only one type (checkpoints)
|
|
const singleTypeAssets = createMockAssets(15).map((asset) => ({
|
|
...asset,
|
|
type: 'checkpoint'
|
|
}))
|
|
|
|
return { ...args, onAssetSelect, onClose, assets: singleTypeAssets }
|
|
},
|
|
template: `
|
|
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
|
|
<AssetBrowserModal
|
|
:node-type="nodeType"
|
|
:input-name="inputName"
|
|
:show-left-panel="showLeftPanel"
|
|
:assets="assets"
|
|
@asset-select="onAssetSelect"
|
|
@close="onClose"
|
|
/>
|
|
</div>
|
|
`
|
|
}),
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
'Modal with assets of only one type (checkpoint) - left panel auto-hidden.'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Story with left panel explicitly hidden
|
|
export const NoLeftPanel: Story = {
|
|
args: {
|
|
nodeType: 'CheckpointLoaderSimple',
|
|
inputName: 'ckpt_name',
|
|
currentValue: '',
|
|
showLeftPanel: false
|
|
},
|
|
render: (args) => ({
|
|
components: { AssetBrowserModal },
|
|
setup() {
|
|
const onAssetSelect = (asset: AssetDisplayItem) => {
|
|
console.log('Selected asset:', asset)
|
|
}
|
|
const onClose = () => {
|
|
console.log('Modal closed')
|
|
}
|
|
|
|
return { ...args, onAssetSelect, onClose, assets: mockAssets }
|
|
},
|
|
template: `
|
|
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
|
|
<AssetBrowserModal
|
|
:node-type="nodeType"
|
|
:input-name="inputName"
|
|
:show-left-panel="showLeftPanel"
|
|
:assets="assets"
|
|
@asset-select="onAssetSelect"
|
|
@close="onClose"
|
|
/>
|
|
</div>
|
|
`
|
|
}),
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
'Modal with left panel explicitly disabled via showLeftPanel=false.'
|
|
}
|
|
}
|
|
}
|
|
}
|