mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
e6f5142e48
The --lm-vscrollbar-width inset from 634ea7f2 was only refreshed on input
events, mount and mode changes. Programmatic value updates (widget.setValue
from "send lora to workflow", external value-change events) change the
textarea content without an input event, leaving the corner clear (x)
button overlapping a freshly appeared classic scrollbar until the next
keystroke. Mount-time pending value replay was already covered.
- onExternalValueChange and widget.onSetValue now call
updateVScrollbarWidth() alongside the hasText update
- tests: cover both paths by overriding textarea metrics to an overflowing
state and asserting the 15px gutter lands in the CSS var
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