mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
refactor(lora-pool-widget): make ComponentWidget generic and remove legacy config
- Add generic type parameter to ComponentWidget<T> for type-safe callbacks - Remove LegacyLoraPoolConfig interface and migrateConfig function - Update LoraPoolWidget to use ComponentWidget<LoraPoolConfig> - Clean up type imports across widget files
This commit is contained in:
@@ -76,11 +76,11 @@ import TagsModal from './lora-pool/modals/TagsModal.vue'
|
||||
import FoldersModal from './lora-pool/modals/FoldersModal.vue'
|
||||
import { useLoraPoolState } from '../composables/useLoraPoolState'
|
||||
import { useModalState, type ModalType } from '../composables/useModalState'
|
||||
import type { ComponentWidget, LoraPoolConfig, LegacyLoraPoolConfig } from '../composables/types'
|
||||
import type { ComponentWidget, LoraPoolConfig } from '../composables/types'
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
widget: ComponentWidget
|
||||
widget: ComponentWidget<LoraPoolConfig>
|
||||
node: { id: number }
|
||||
}>()
|
||||
|
||||
@@ -99,7 +99,7 @@ onMounted(async () => {
|
||||
// ComfyUI calls this automatically after setValue() in domWidget.ts
|
||||
// NOTE: callback should NOT call refreshPreview() to avoid infinite loops:
|
||||
// watch(filters) → refreshPreview() → buildConfig() → widget.value = v → callback → refreshPreview() → ...
|
||||
props.widget.callback = (v: LoraPoolConfig | LegacyLoraPoolConfig) => {
|
||||
props.widget.callback = (v: LoraPoolConfig) => {
|
||||
if (v) {
|
||||
console.log('[LoraPoolWidget] Restoring config from callback')
|
||||
state.restoreFromConfig(v)
|
||||
@@ -110,7 +110,7 @@ onMounted(async () => {
|
||||
// Restore from saved value if workflow was already loaded
|
||||
if (props.widget.value) {
|
||||
console.log('[LoraPoolWidget] Restoring from initial value')
|
||||
state.restoreFromConfig(props.widget.value as LoraPoolConfig | LegacyLoraPoolConfig)
|
||||
state.restoreFromConfig(props.widget.value as LoraPoolConfig)
|
||||
}
|
||||
|
||||
// Fetch filter options
|
||||
|
||||
@@ -37,22 +37,6 @@ export interface FolderTreeNode {
|
||||
children?: FolderTreeNode[]
|
||||
}
|
||||
|
||||
// Legacy config for migration (v1)
|
||||
export interface LegacyLoraPoolConfig {
|
||||
version: 1
|
||||
filters: {
|
||||
baseModels: string[]
|
||||
tags: { include: string[]; exclude: string[] }
|
||||
folder: { path: string | null; recursive: boolean }
|
||||
favoritesOnly: boolean
|
||||
license: {
|
||||
noCreditRequired: boolean | null
|
||||
allowSellingGeneratedContent: boolean | null
|
||||
}
|
||||
}
|
||||
preview: { matchCount: number; lastUpdated: number }
|
||||
}
|
||||
|
||||
// Randomizer config
|
||||
export interface RandomizerConfig {
|
||||
count_mode: 'fixed' | 'range'
|
||||
@@ -98,14 +82,17 @@ export interface CyclerConfig {
|
||||
next_index?: number | null // Index for display after execution
|
||||
}
|
||||
|
||||
export interface ComponentWidget {
|
||||
// Widget config union type
|
||||
export type WidgetConfig = LoraPoolConfig | RandomizerConfig | CyclerConfig
|
||||
|
||||
export interface ComponentWidget<T = WidgetConfig> {
|
||||
/** @deprecated Use callback instead. Kept for backward compatibility with other widgets. */
|
||||
serializeValue?: () => Promise<LoraPoolConfig | RandomizerConfig | CyclerConfig>
|
||||
value?: LoraPoolConfig | LegacyLoraPoolConfig | RandomizerConfig | CyclerConfig
|
||||
serializeValue?: () => Promise<T>
|
||||
value?: T
|
||||
/** @deprecated Use callback instead. Kept for backward compatibility with other widgets. */
|
||||
onSetValue?: (v: LoraPoolConfig | LegacyLoraPoolConfig | RandomizerConfig | CyclerConfig) => void
|
||||
onSetValue?: (v: T) => void
|
||||
/** @deprecated Directly set widget.value instead. Kept for backward compatibility with other widgets. */
|
||||
updateConfig?: (v: LoraPoolConfig | RandomizerConfig | CyclerConfig) => void
|
||||
updateConfig?: (v: T) => void
|
||||
/** Called by ComfyUI automatically after setValue() - use this for UI sync */
|
||||
callback?: (v: LoraPoolConfig | LegacyLoraPoolConfig | RandomizerConfig | CyclerConfig) => void
|
||||
callback?: (v: T) => void
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import type {
|
||||
LoraPoolConfig,
|
||||
LegacyLoraPoolConfig,
|
||||
BaseModelOption,
|
||||
TagOption,
|
||||
FolderTreeNode,
|
||||
@@ -10,7 +9,7 @@ import type {
|
||||
} from './types'
|
||||
import { useLoraPoolApi } from './useLoraPoolApi'
|
||||
|
||||
export function useLoraPoolState(widget: ComponentWidget) {
|
||||
export function useLoraPoolState(widget: ComponentWidget<LoraPoolConfig>) {
|
||||
const api = useLoraPoolApi()
|
||||
|
||||
// Flag to prevent infinite loops during config restoration
|
||||
@@ -70,41 +69,13 @@ export function useLoraPoolState(widget: ComponentWidget) {
|
||||
return config
|
||||
}
|
||||
|
||||
// Migrate legacy config (v1) to current format (v2)
|
||||
const migrateConfig = (legacy: LegacyLoraPoolConfig): LoraPoolConfig => {
|
||||
return {
|
||||
version: 2,
|
||||
filters: {
|
||||
baseModels: legacy.filters.baseModels || [],
|
||||
tags: {
|
||||
include: legacy.filters.tags?.include || [],
|
||||
exclude: legacy.filters.tags?.exclude || []
|
||||
},
|
||||
folders: {
|
||||
include: legacy.filters.folder?.path ? [legacy.filters.folder.path] : [],
|
||||
exclude: []
|
||||
},
|
||||
license: {
|
||||
noCreditRequired: legacy.filters.license?.noCreditRequired ?? false,
|
||||
allowSelling: legacy.filters.license?.allowSellingGeneratedContent ?? false
|
||||
}
|
||||
},
|
||||
preview: legacy.preview || { matchCount: 0, lastUpdated: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// Restore state from config
|
||||
const restoreFromConfig = (rawConfig: LoraPoolConfig | LegacyLoraPoolConfig) => {
|
||||
const restoreFromConfig = (config: LoraPoolConfig) => {
|
||||
// Set flag to prevent buildConfig from triggering widget.value updates during restoration
|
||||
// This breaks the infinite loop: callback → restoreFromConfig → watch → refreshPreview → buildConfig → widget.value = config → callback
|
||||
isRestoring = true
|
||||
|
||||
try {
|
||||
// Migrate if needed
|
||||
const config = rawConfig.version === 1
|
||||
? migrateConfig(rawConfig as LegacyLoraPoolConfig)
|
||||
: rawConfig as LoraPoolConfig
|
||||
|
||||
if (!config?.filters) return
|
||||
|
||||
const { filters, preview } = config
|
||||
|
||||
@@ -5,7 +5,7 @@ import LoraRandomizerWidget from '@/components/LoraRandomizerWidget.vue'
|
||||
import LoraCyclerWidget from '@/components/LoraCyclerWidget.vue'
|
||||
import JsonDisplayWidget from '@/components/JsonDisplayWidget.vue'
|
||||
import AutocompleteTextWidget from '@/components/AutocompleteTextWidget.vue'
|
||||
import type { LoraPoolConfig, LegacyLoraPoolConfig, RandomizerConfig, CyclerConfig } from './composables/types'
|
||||
import type { LoraPoolConfig, RandomizerConfig, CyclerConfig } from './composables/types'
|
||||
import {
|
||||
setupModeChangeHandler,
|
||||
createModeChangeCallback,
|
||||
@@ -78,7 +78,7 @@ function createLoraPoolWidget(node) {
|
||||
|
||||
forwardMiddleMouseToCanvas(container)
|
||||
|
||||
let internalValue: LoraPoolConfig | LegacyLoraPoolConfig | undefined
|
||||
let internalValue: LoraPoolConfig | undefined
|
||||
|
||||
const widget = node.addDOMWidget(
|
||||
'pool_config',
|
||||
@@ -88,7 +88,7 @@ function createLoraPoolWidget(node) {
|
||||
getValue() {
|
||||
return internalValue
|
||||
},
|
||||
setValue(v: LoraPoolConfig | LegacyLoraPoolConfig) {
|
||||
setValue(v: LoraPoolConfig) {
|
||||
internalValue = v
|
||||
// ComfyUI automatically calls widget.callback after setValue
|
||||
// No need for custom onSetValue mechanism
|
||||
|
||||
@@ -970,7 +970,7 @@ to { transform: rotate(360deg);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.lora-pool-widget[data-v-1cc8816c] {
|
||||
.lora-pool-widget[data-v-4456abba] {
|
||||
padding: 12px;
|
||||
background: rgba(40, 44, 52, 0.6);
|
||||
border-radius: 4px;
|
||||
@@ -11378,33 +11378,10 @@ function useLoraPoolState(widget) {
|
||||
}
|
||||
return config;
|
||||
};
|
||||
const migrateConfig = (legacy) => {
|
||||
var _a2, _b, _c, _d, _e2;
|
||||
return {
|
||||
version: 2,
|
||||
filters: {
|
||||
baseModels: legacy.filters.baseModels || [],
|
||||
tags: {
|
||||
include: ((_a2 = legacy.filters.tags) == null ? void 0 : _a2.include) || [],
|
||||
exclude: ((_b = legacy.filters.tags) == null ? void 0 : _b.exclude) || []
|
||||
},
|
||||
folders: {
|
||||
include: ((_c = legacy.filters.folder) == null ? void 0 : _c.path) ? [legacy.filters.folder.path] : [],
|
||||
exclude: []
|
||||
},
|
||||
license: {
|
||||
noCreditRequired: ((_d = legacy.filters.license) == null ? void 0 : _d.noCreditRequired) ?? false,
|
||||
allowSelling: ((_e2 = legacy.filters.license) == null ? void 0 : _e2.allowSellingGeneratedContent) ?? false
|
||||
}
|
||||
},
|
||||
preview: legacy.preview || { matchCount: 0, lastUpdated: 0 }
|
||||
};
|
||||
};
|
||||
const restoreFromConfig = (rawConfig) => {
|
||||
const restoreFromConfig = (config) => {
|
||||
var _a2, _b, _c, _d, _e2, _f;
|
||||
isRestoring = true;
|
||||
try {
|
||||
const config = rawConfig.version === 1 ? migrateConfig(rawConfig) : rawConfig;
|
||||
if (!(config == null ? void 0 : config.filters)) return;
|
||||
const { filters, preview } = config;
|
||||
const updateIfChanged = (refValue, newValue) => {
|
||||
@@ -11601,7 +11578,7 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
|
||||
};
|
||||
}
|
||||
});
|
||||
const LoraPoolWidget = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["__scopeId", "data-v-1cc8816c"]]);
|
||||
const LoraPoolWidget = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["__scopeId", "data-v-4456abba"]]);
|
||||
const _hoisted_1$8 = { class: "last-used-preview" };
|
||||
const _hoisted_2$5 = { class: "last-used-preview__content" };
|
||||
const _hoisted_3$3 = ["src", "onError"];
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user