mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-08-06 22:10:14 -03:00
In ComfyUI Vue render mode, WidgetDOM.vue reuses its component instance during undo/redo without re-calling mountWidgetElement(), leaving newly created widget containers detached from the DOM. - AutocompleteTextWidget: scan for empty containers by ID prefix and reuse - Loras widget: scan for empty .lm-loras-container elements and reuse - Prevent duplicate event listeners by guarding listener setup on new containers only - Keep container in DOM on cleanup (clearChildren instead of remove) so it can be found and reused by the next factory invocation
Vue Widgets for ComfyUI LoRA Manager
This directory contains the source code for Vue 3 + PrimeVue custom widgets for ComfyUI LoRA Manager.
Structure
vue-widgets/
├── src/ # TypeScript/Vue source code
│ ├── main.ts # Main entry point that registers extensions
│ └── components/ # Vue components
│ └── DemoWidget.vue # Example demo widget
├── package.json # Dependencies and build scripts
├── vite.config.mts # Vite build configuration
├── tsconfig.json # TypeScript configuration
└── README.md # This file
Development
Install Dependencies
cd vue-widgets
npm install
Build for Production
npm run build
This compiles the TypeScript/Vue code and outputs to ../web/comfyui/vue-widgets/.
Development Mode (Watch)
npm run dev
This builds the widgets in watch mode, automatically rebuilding when files change.
Type Checking
npm run typecheck
Creating a New Widget
1. Create the Python Node
Create a new node file in /py/nodes/your_node.py:
class YourCustomNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"your_widget_name": ("YOUR_WIDGET_TYPE", {}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "process"
CATEGORY = "loramanager"
def process(self, your_widget_name):
# Process widget data
return (str(your_widget_name),)
NODE_CLASS_MAPPINGS = {
"YourCustomNode": YourCustomNode
}
2. Create the Vue Component
Create a new component in src/components/YourWidget.vue:
<template>
<div class="your-widget-container">
<!-- Your UI here using PrimeVue components -->
<Button label="Click me" @click="handleClick" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import Button from 'primevue/button'
const props = defineProps<{
widget: { serializeValue?: (node: unknown, index: number) => Promise<unknown> }
node: { id: number }
}>()
onMounted(() => {
// Serialize widget data when workflow is saved
props.widget.serializeValue = async () => {
return { /* your data */ }
}
})
</script>
<style scoped>
/* Your styles */
</style>
3. Register the Widget
In src/main.ts, add your widget registration:
import YourWidget from '@/components/YourWidget.vue'
// In getCustomWidgets()
YOUR_WIDGET_TYPE(node) {
return createVueWidget(node, YourWidget, 'your-widget-name')
}
// In nodeCreated()
if (node.constructor?.comfyClass !== 'YourCustomNode') return
4. Register the Node
Add your node to __init__.py:
from .py.nodes.your_node import YourCustomNode
NODE_CLASS_MAPPINGS = {
# ...
"YourCustomNode": YourCustomNode
}
5. Build and Test
npm run build
Then restart ComfyUI and test your new widget!
Available PrimeVue Components
This project uses PrimeVue 4.x. Popular components include:
Button- Buttons with icons and variantsInputText- Text input fieldsInputNumber- Number input with increment/decrementDropdown- Select dropdownsCard- Card containersDataTable- Data tables with sorting/filteringDialog- Modal dialogsTree- Tree view components- And many more! See PrimeVue Docs
Notes
- Build output goes to
../web/comfyui/vue-widgets/(gitignored) - The widget type name in Python (e.g., "YOUR_WIDGET_TYPE") must match the key in
getCustomWidgets() - Widget data is serialized when the workflow is saved/executed via
serializeValue() - ComfyUI's app.js is marked as external and not bundled