mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 22:52:12 -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 FoldersModal from './lora-pool/modals/FoldersModal.vue'
|
||||||
import { useLoraPoolState } from '../composables/useLoraPoolState'
|
import { useLoraPoolState } from '../composables/useLoraPoolState'
|
||||||
import { useModalState, type ModalType } from '../composables/useModalState'
|
import { useModalState, type ModalType } from '../composables/useModalState'
|
||||||
import type { ComponentWidget, LoraPoolConfig, LegacyLoraPoolConfig } from '../composables/types'
|
import type { ComponentWidget, LoraPoolConfig } from '../composables/types'
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
widget: ComponentWidget
|
widget: ComponentWidget<LoraPoolConfig>
|
||||||
node: { id: number }
|
node: { id: number }
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ onMounted(async () => {
|
|||||||
// ComfyUI calls this automatically after setValue() in domWidget.ts
|
// ComfyUI calls this automatically after setValue() in domWidget.ts
|
||||||
// NOTE: callback should NOT call refreshPreview() to avoid infinite loops:
|
// NOTE: callback should NOT call refreshPreview() to avoid infinite loops:
|
||||||
// watch(filters) → refreshPreview() → buildConfig() → widget.value = v → callback → refreshPreview() → ...
|
// watch(filters) → refreshPreview() → buildConfig() → widget.value = v → callback → refreshPreview() → ...
|
||||||
props.widget.callback = (v: LoraPoolConfig | LegacyLoraPoolConfig) => {
|
props.widget.callback = (v: LoraPoolConfig) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
console.log('[LoraPoolWidget] Restoring config from callback')
|
console.log('[LoraPoolWidget] Restoring config from callback')
|
||||||
state.restoreFromConfig(v)
|
state.restoreFromConfig(v)
|
||||||
@@ -110,7 +110,7 @@ onMounted(async () => {
|
|||||||
// Restore from saved value if workflow was already loaded
|
// Restore from saved value if workflow was already loaded
|
||||||
if (props.widget.value) {
|
if (props.widget.value) {
|
||||||
console.log('[LoraPoolWidget] Restoring from initial 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
|
// Fetch filter options
|
||||||
|
|||||||
@@ -37,22 +37,6 @@ export interface FolderTreeNode {
|
|||||||
children?: 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
|
// Randomizer config
|
||||||
export interface RandomizerConfig {
|
export interface RandomizerConfig {
|
||||||
count_mode: 'fixed' | 'range'
|
count_mode: 'fixed' | 'range'
|
||||||
@@ -98,14 +82,17 @@ export interface CyclerConfig {
|
|||||||
next_index?: number | null // Index for display after execution
|
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. */
|
/** @deprecated Use callback instead. Kept for backward compatibility with other widgets. */
|
||||||
serializeValue?: () => Promise<LoraPoolConfig | RandomizerConfig | CyclerConfig>
|
serializeValue?: () => Promise<T>
|
||||||
value?: LoraPoolConfig | LegacyLoraPoolConfig | RandomizerConfig | CyclerConfig
|
value?: T
|
||||||
/** @deprecated Use callback instead. Kept for backward compatibility with other widgets. */
|
/** @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. */
|
/** @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 */
|
/** 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 { ref, computed, watch } from 'vue'
|
||||||
import type {
|
import type {
|
||||||
LoraPoolConfig,
|
LoraPoolConfig,
|
||||||
LegacyLoraPoolConfig,
|
|
||||||
BaseModelOption,
|
BaseModelOption,
|
||||||
TagOption,
|
TagOption,
|
||||||
FolderTreeNode,
|
FolderTreeNode,
|
||||||
@@ -10,7 +9,7 @@ import type {
|
|||||||
} from './types'
|
} from './types'
|
||||||
import { useLoraPoolApi } from './useLoraPoolApi'
|
import { useLoraPoolApi } from './useLoraPoolApi'
|
||||||
|
|
||||||
export function useLoraPoolState(widget: ComponentWidget) {
|
export function useLoraPoolState(widget: ComponentWidget<LoraPoolConfig>) {
|
||||||
const api = useLoraPoolApi()
|
const api = useLoraPoolApi()
|
||||||
|
|
||||||
// Flag to prevent infinite loops during config restoration
|
// Flag to prevent infinite loops during config restoration
|
||||||
@@ -70,41 +69,13 @@ export function useLoraPoolState(widget: ComponentWidget) {
|
|||||||
return config
|
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
|
// 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
|
// 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
|
// This breaks the infinite loop: callback → restoreFromConfig → watch → refreshPreview → buildConfig → widget.value = config → callback
|
||||||
isRestoring = true
|
isRestoring = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Migrate if needed
|
|
||||||
const config = rawConfig.version === 1
|
|
||||||
? migrateConfig(rawConfig as LegacyLoraPoolConfig)
|
|
||||||
: rawConfig as LoraPoolConfig
|
|
||||||
|
|
||||||
if (!config?.filters) return
|
if (!config?.filters) return
|
||||||
|
|
||||||
const { filters, preview } = config
|
const { filters, preview } = config
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import LoraRandomizerWidget from '@/components/LoraRandomizerWidget.vue'
|
|||||||
import LoraCyclerWidget from '@/components/LoraCyclerWidget.vue'
|
import LoraCyclerWidget from '@/components/LoraCyclerWidget.vue'
|
||||||
import JsonDisplayWidget from '@/components/JsonDisplayWidget.vue'
|
import JsonDisplayWidget from '@/components/JsonDisplayWidget.vue'
|
||||||
import AutocompleteTextWidget from '@/components/AutocompleteTextWidget.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 {
|
import {
|
||||||
setupModeChangeHandler,
|
setupModeChangeHandler,
|
||||||
createModeChangeCallback,
|
createModeChangeCallback,
|
||||||
@@ -78,7 +78,7 @@ function createLoraPoolWidget(node) {
|
|||||||
|
|
||||||
forwardMiddleMouseToCanvas(container)
|
forwardMiddleMouseToCanvas(container)
|
||||||
|
|
||||||
let internalValue: LoraPoolConfig | LegacyLoraPoolConfig | undefined
|
let internalValue: LoraPoolConfig | undefined
|
||||||
|
|
||||||
const widget = node.addDOMWidget(
|
const widget = node.addDOMWidget(
|
||||||
'pool_config',
|
'pool_config',
|
||||||
@@ -88,7 +88,7 @@ function createLoraPoolWidget(node) {
|
|||||||
getValue() {
|
getValue() {
|
||||||
return internalValue
|
return internalValue
|
||||||
},
|
},
|
||||||
setValue(v: LoraPoolConfig | LegacyLoraPoolConfig) {
|
setValue(v: LoraPoolConfig) {
|
||||||
internalValue = v
|
internalValue = v
|
||||||
// ComfyUI automatically calls widget.callback after setValue
|
// ComfyUI automatically calls widget.callback after setValue
|
||||||
// No need for custom onSetValue mechanism
|
// No need for custom onSetValue mechanism
|
||||||
|
|||||||
@@ -970,7 +970,7 @@ to { transform: rotate(360deg);
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lora-pool-widget[data-v-1cc8816c] {
|
.lora-pool-widget[data-v-4456abba] {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
background: rgba(40, 44, 52, 0.6);
|
background: rgba(40, 44, 52, 0.6);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -11378,33 +11378,10 @@ function useLoraPoolState(widget) {
|
|||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
const migrateConfig = (legacy) => {
|
const restoreFromConfig = (config) => {
|
||||||
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) => {
|
|
||||||
var _a2, _b, _c, _d, _e2, _f;
|
var _a2, _b, _c, _d, _e2, _f;
|
||||||
isRestoring = true;
|
isRestoring = true;
|
||||||
try {
|
try {
|
||||||
const config = rawConfig.version === 1 ? migrateConfig(rawConfig) : rawConfig;
|
|
||||||
if (!(config == null ? void 0 : config.filters)) return;
|
if (!(config == null ? void 0 : config.filters)) return;
|
||||||
const { filters, preview } = config;
|
const { filters, preview } = config;
|
||||||
const updateIfChanged = (refValue, newValue) => {
|
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_1$8 = { class: "last-used-preview" };
|
||||||
const _hoisted_2$5 = { class: "last-used-preview__content" };
|
const _hoisted_2$5 = { class: "last-used-preview__content" };
|
||||||
const _hoisted_3$3 = ["src", "onError"];
|
const _hoisted_3$3 = ["src", "onError"];
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user