simplify widget registration

This commit is contained in:
bymyself
2025-09-04 21:11:30 -07:00
parent 85fa2f4559
commit 8e098fc325
4 changed files with 224 additions and 241 deletions

View File

@@ -50,10 +50,10 @@ import { LODLevel } from '@/renderer/extensions/vueNodes/lod/useLOD'
// Import widget components directly
import WidgetInputText from '@/renderer/extensions/vueNodes/widgets/components/WidgetInputText.vue'
import {
ESSENTIAL_WIDGET_TYPES,
useWidgetRenderer
} from '@/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer'
import { widgetTypeToComponent } from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
getComponent,
isEssential,
shouldRenderAsVue
} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
import InputSlot from './InputSlot.vue'
@@ -67,9 +67,6 @@ interface NodeWidgetsProps {
const props = defineProps<NodeWidgetsProps>()
// Use widget renderer composable
const { getWidgetComponent, shouldRenderAsVue } = useWidgetRenderer()
// Error boundary implementation
const renderError = ref<string | null>(null)
@@ -110,14 +107,9 @@ const processedWidgets = computed((): ProcessedWidget[] => {
if (!widget.type) continue
if (!shouldRenderAsVue(widget)) continue
if (
lodLevel === LODLevel.REDUCED &&
!ESSENTIAL_WIDGET_TYPES.has(widget.type)
)
continue
if (lodLevel === LODLevel.REDUCED && !isEssential(widget.type)) continue
const componentName = getWidgetComponent(widget.type)
const vueComponent = widgetTypeToComponent[componentName] || WidgetInputText
const vueComponent = getComponent(widget.type) || WidgetInputText
const simplified: SimplifiedWidget = {
name: widget.name,

View File

@@ -1,118 +0,0 @@
/**
* Widget renderer composable for Vue node system
* Maps LiteGraph widget types to Vue components
*/
import { WidgetType, widgetTypeToComponent } from '../registry/widgetRegistry'
/**
* Static mapping of LiteGraph widget types to Vue widget component names
*/
const TYPE_TO_ENUM_MAP: Record<string, string> = {
// Number inputs
number: WidgetType.NUMBER,
slider: WidgetType.SLIDER,
INT: WidgetType.INT,
FLOAT: WidgetType.FLOAT,
// Text inputs
text: WidgetType.STRING,
string: WidgetType.STRING,
STRING: WidgetType.STRING,
// Selection
combo: WidgetType.COMBO,
COMBO: WidgetType.COMBO,
selectbutton: WidgetType.SELECTBUTTON,
SELECTBUTTON: WidgetType.SELECTBUTTON,
multiselect: WidgetType.MULTISELECT,
MULTISELECT: WidgetType.MULTISELECT,
treeselect: WidgetType.TREESELECT,
TREESELECT: WidgetType.TREESELECT,
// Boolean
toggle: WidgetType.TOGGLESWITCH,
boolean: WidgetType.BOOLEAN,
BOOLEAN: WidgetType.BOOLEAN,
// Multiline text
multiline: WidgetType.TEXTAREA,
textarea: WidgetType.TEXTAREA,
TEXTAREA: WidgetType.TEXTAREA,
customtext: WidgetType.TEXTAREA,
MARKDOWN: WidgetType.MARKDOWN,
// Advanced widgets
color: WidgetType.COLOR,
COLOR: WidgetType.COLOR,
imagecompare: WidgetType.IMAGECOMPARE,
IMAGECOMPARE: WidgetType.IMAGECOMPARE,
galleria: WidgetType.GALLERIA,
GALLERIA: WidgetType.GALLERIA,
file: WidgetType.FILEUPLOAD,
fileupload: WidgetType.FILEUPLOAD,
FILEUPLOAD: WidgetType.FILEUPLOAD,
// Button widget
button: WidgetType.BUTTON,
BUTTON: WidgetType.BUTTON,
// Chart widget
chart: WidgetType.CHART,
CHART: WidgetType.CHART
} as const
/**
* Pre-computed widget support map for O(1) lookups
* Maps widget type directly to boolean for fast shouldRenderAsVue checks
*/
const WIDGET_SUPPORT_MAP = new Map(
Object.entries(TYPE_TO_ENUM_MAP).map(([type, enumValue]) => [
type,
widgetTypeToComponent[enumValue] !== undefined
])
)
export const ESSENTIAL_WIDGET_TYPES = new Set([
'combo',
'COMBO',
'select',
'toggle',
'boolean',
'BOOLEAN',
'slider',
'number',
'INT',
'FLOAT'
])
export const useWidgetRenderer = () => {
const getWidgetComponent = (widgetType: string): string => {
const enumKey = TYPE_TO_ENUM_MAP[widgetType]
if (enumKey && widgetTypeToComponent[enumKey]) {
return enumKey
}
return WidgetType.STRING
}
const shouldRenderAsVue = (widget: {
type?: string
options?: Record<string, unknown>
}): boolean => {
if (widget.options?.canvasOnly) return false
if (!widget.type) return false
// Check if widget type is explicitly supported
const isSupported = WIDGET_SUPPORT_MAP.get(widget.type)
if (isSupported !== undefined) return isSupported
// Fallback: unknown types are rendered as STRING widget
return widgetTypeToComponent[WidgetType.STRING] !== undefined
}
return {
getWidgetComponent,
shouldRenderAsVue
}
}

View File

@@ -3,7 +3,6 @@
*/
import type { Component } from 'vue'
// Component imports
import WidgetButton from '../components/WidgetButton.vue'
import WidgetChart from '../components/WidgetChart.vue'
import WidgetColorPicker from '../components/WidgetColorPicker.vue'
@@ -20,54 +19,132 @@ import WidgetTextarea from '../components/WidgetTextarea.vue'
import WidgetToggleSwitch from '../components/WidgetToggleSwitch.vue'
import WidgetTreeSelect from '../components/WidgetTreeSelect.vue'
/**
* Enum of all available widget types
*/
export enum WidgetType {
BUTTON = 'BUTTON',
STRING = 'STRING',
INT = 'INT',
FLOAT = 'FLOAT',
NUMBER = 'NUMBER',
BOOLEAN = 'BOOLEAN',
COMBO = 'COMBO',
COLOR = 'COLOR',
MULTISELECT = 'MULTISELECT',
SELECTBUTTON = 'SELECTBUTTON',
SLIDER = 'SLIDER',
TEXTAREA = 'TEXTAREA',
TOGGLESWITCH = 'TOGGLESWITCH',
CHART = 'CHART',
IMAGECOMPARE = 'IMAGECOMPARE',
GALLERIA = 'GALLERIA',
FILEUPLOAD = 'FILEUPLOAD',
TREESELECT = 'TREESELECT',
MARKDOWN = 'MARKDOWN'
interface WidgetDefinition {
component: Component
aliases: string[]
essential: boolean
}
/**
* Maps widget types to their corresponding Vue components
* Components will be added as they are implemented
*/
export const widgetTypeToComponent: Record<string, Component> = {
// Components will be uncommented as they are implemented
[WidgetType.BUTTON]: WidgetButton,
[WidgetType.STRING]: WidgetInputText,
[WidgetType.INT]: WidgetSlider,
[WidgetType.FLOAT]: WidgetSlider,
[WidgetType.NUMBER]: WidgetSlider, // For compatibility
[WidgetType.BOOLEAN]: WidgetToggleSwitch,
[WidgetType.COMBO]: WidgetSelect,
[WidgetType.COLOR]: WidgetColorPicker,
[WidgetType.MULTISELECT]: WidgetMultiSelect,
[WidgetType.SELECTBUTTON]: WidgetSelectButton,
[WidgetType.SLIDER]: WidgetSlider,
[WidgetType.TEXTAREA]: WidgetTextarea,
[WidgetType.TOGGLESWITCH]: WidgetToggleSwitch,
[WidgetType.CHART]: WidgetChart,
[WidgetType.IMAGECOMPARE]: WidgetImageCompare,
[WidgetType.GALLERIA]: WidgetGalleria,
[WidgetType.FILEUPLOAD]: WidgetFileUpload,
[WidgetType.TREESELECT]: WidgetTreeSelect,
[WidgetType.MARKDOWN]: WidgetMarkdown
const coreWidgetDefinitions: Array<[string, WidgetDefinition]> = [
[
'button',
{ component: WidgetButton, aliases: ['BUTTON'], essential: false }
],
[
'string',
{
component: WidgetInputText,
aliases: ['STRING', 'text'],
essential: false
}
],
['int', { component: WidgetSlider, aliases: ['INT'], essential: true }],
[
'float',
{
component: WidgetSlider,
aliases: ['FLOAT', 'number', 'slider'],
essential: true
}
],
[
'boolean',
{
component: WidgetToggleSwitch,
aliases: ['BOOLEAN', 'toggle'],
essential: true
}
],
['combo', { component: WidgetSelect, aliases: ['COMBO'], essential: true }],
[
'color',
{ component: WidgetColorPicker, aliases: ['COLOR'], essential: false }
],
[
'multiselect',
{ component: WidgetMultiSelect, aliases: ['MULTISELECT'], essential: false }
],
[
'selectbutton',
{
component: WidgetSelectButton,
aliases: ['SELECTBUTTON'],
essential: false
}
],
[
'textarea',
{
component: WidgetTextarea,
aliases: ['TEXTAREA', 'multiline', 'customtext'],
essential: false
}
],
['chart', { component: WidgetChart, aliases: ['CHART'], essential: false }],
[
'imagecompare',
{
component: WidgetImageCompare,
aliases: ['IMAGECOMPARE'],
essential: false
}
],
[
'galleria',
{ component: WidgetGalleria, aliases: ['GALLERIA'], essential: false }
],
[
'fileupload',
{
component: WidgetFileUpload,
aliases: ['FILEUPLOAD', 'file'],
essential: false
}
],
[
'treeselect',
{ component: WidgetTreeSelect, aliases: ['TREESELECT'], essential: false }
],
[
'markdown',
{ component: WidgetMarkdown, aliases: ['MARKDOWN'], essential: false }
]
]
// Build lookup maps
const widgets = new Map<string, WidgetDefinition>()
const aliasMap = new Map<string, string>()
for (const [type, def] of coreWidgetDefinitions) {
widgets.set(type, def)
for (const alias of def.aliases) {
aliasMap.set(alias, type)
}
}
// Utility functions
const getCanonicalType = (type: string): string => aliasMap.get(type) || type
export const getComponent = (type: string): Component | null => {
const canonicalType = getCanonicalType(type)
return widgets.get(canonicalType)?.component || null
}
export const isSupported = (type: string): boolean => {
const canonicalType = getCanonicalType(type)
return widgets.has(canonicalType)
}
export const isEssential = (type: string): boolean => {
const canonicalType = getCanonicalType(type)
return widgets.get(canonicalType)?.essential || false
}
export const shouldRenderAsVue = (widget: {
type?: string
options?: Record<string, unknown>
}): boolean => {
if (widget.options?.canvasOnly) return false
if (!widget.type) return false
return isSupported(widget.type)
}