lasso persistence in wf

fix
This commit is contained in:
JJ
2025-11-27 15:54:01 +00:00
parent 5835ca3f25
commit ee2e7b22e4
3 changed files with 767 additions and 1557 deletions
File diff suppressed because it is too large Load Diff
+18 -2
View File
@@ -528,6 +528,9 @@ class PrepareRefs:
"required": {
"mask_width": ("INT", {"default": 640, "min": 8, "max": 4096, "step": 8}),
"mask_height": ("INT", {"default": 480, "min": 8, "max": 4096, "step": 8}),
"internal_state": ("STRING", {"default": "{}", "multiline": False}), # Internal canvas state
"export_filename": ("STRING", {"default": "", "multiline": False}), # Export path (hidden in UI)
"ref_layer_data": ("STRING", {"default": "[]", "multiline": False}), # Ref layer data for persistence
},
"optional": {
"bg_image": ("IMAGE", {"forceInput": True}),
@@ -549,7 +552,9 @@ class PrepareRefs:
Can export ref images with lasso shapes as masks.
"""
def prepare(self, mask_width: int, mask_height: int, bg_image: Optional[torch.Tensor] = None,
def prepare(self, mask_width: int, mask_height: int, internal_state: str = "{}",
export_filename: str = "", ref_layer_data: str = "[]",
bg_image: Optional[torch.Tensor] = None,
extra_refs: Optional[torch.Tensor] = None, extra_masks: Optional[torch.Tensor] = None,
unique_id: Optional[str] = None, prompt: Optional[Dict] = None):
"""
@@ -573,7 +578,18 @@ class PrepareRefs:
width, height, output_bg_image, internal_processing_image = self._resolve_base_images(mask_width, mask_height, bg_image)
# Parse and validate ref_layer_data early
raw_ref_data = parse_ref_layer_data_from_prompt(prompt, unique_id)
# ref_layer_data is now a direct parameter (JSON string), parse it
raw_ref_data = []
if ref_layer_data:
try:
import json
raw_ref_data = json.loads(ref_layer_data) if isinstance(ref_layer_data, str) else ref_layer_data
if not isinstance(raw_ref_data, list):
raw_ref_data = []
except (json.JSONDecodeError, ValueError) as e:
print(f"[PrepareRefs WARNING] Failed to parse ref_layer_data: {e}")
raw_ref_data = []
valid_ref_layers = filter_layers_with_shapes(raw_ref_data)
# EARLY GUARD: if no valid layers AND no extra_refs, warn and return empty outputs (no further processing)
+68 -30
View File
@@ -82,9 +82,12 @@ app.registerExtension({
this.uuid = this.properties.uuid || this.uuid || makeUUID();
console.log('[PrepareRefs onNodeCreated] Using UUID:', this.uuid);
// Hide original dimension widgets
// Hide widgets that shouldn't be visible in UI
const widthWidget = this.widgets.find(w => w.name === 'mask_width');
const heightWidget = this.widgets.find(w => w.name === 'mask_height');
const internalStateWidget = this.widgets.find(w => w.name === 'internal_state');
const exportFilenameWidget = this.widgets.find(w => w.name === 'export_filename');
const refLayerDataWidget = this.widgets.find(w => w.name === 'ref_layer_data');
const markUserAdjusted = () => { this.properties.userAdjustedDims = true; };
@@ -96,6 +99,11 @@ app.registerExtension({
}
});
// Hide the persistence widgets (they're managed by the node, not user-editable)
[internalStateWidget, exportFilenameWidget, refLayerDataWidget].forEach(w => {
if (w) hideWidgetForGood(this, w);
});
// Size manager for consistent canvas/node sizing
this.sizeManager = new NodeSizeManager(this, {
spacingAfterCanvas: 36,
@@ -267,23 +275,20 @@ app.registerExtension({
this.refLayers = [];
this.selectedRefLayer = null;
this.addSerializedRefDataWidget();
};
nodeType.prototype.addSerializedRefDataWidget = function () {
const widget = {
type: "custom",
name: "ref_layer_data",
value: [],
serialize: true,
callback: () => { widget.value = this.getRefLayerData(); }
};
this.widgets.push(widget);
// ref_layer_data widget is now created by Python INPUT_TYPES, not here
// Just ensure its callback updates the layer data
const refDataWidget = this.widgets?.find(w => w.name === 'ref_layer_data');
if (refDataWidget) {
const origCallback = refDataWidget.callback;
refDataWidget.callback = () => {
origCallback?.call(refDataWidget);
this.updateRefDataWidget();
};
}
};
nodeType.prototype.getRefLayerData = function () {
return (this.refLayers || [])
.filter(l => l.value?.lassoShape?.additivePaths?.length > 0)
.map(l => ({ ...l.value }));
};
@@ -464,13 +469,15 @@ app.registerExtension({
if (w) {
const layerData = this.getRefLayerData();
console.log('[PrepareRefs updateRefDataWidget] Layer data from getRefLayerData():', layerData);
w.value = layerData;
// Widget is STRING type in Python, so serialize to JSON
w.value = JSON.stringify(layerData);
// Also save to sessionStorage for quick restore on page refresh
try {
const sessionKey = `prepare-refs-layers-${this.uuid}`;
console.log('[PrepareRefs updateRefDataWidget] Saving to session storage with key:', sessionKey);
safeSetSessionItem(sessionKey, JSON.stringify(w.value));
safeSetSessionItem(sessionKey, JSON.stringify(layerData));
console.log('[PrepareRefs updateRefDataWidget] Saved to session storage successfully');
} catch (e) {
console.warn('[PrepareRefs] Failed to save layers to session:', e);
@@ -687,6 +694,8 @@ app.registerExtension({
console.log('[PrepareRefs onConfigure] Starting configuration restore');
console.log('[PrepareRefs onConfigure] Current UUID:', this.uuid);
console.log('[PrepareRefs onConfigure] Properties UUID:', this.properties?.uuid);
console.log('[PrepareRefs onConfigure] Info object:', info);
console.log('[PrepareRefs onConfigure] All widgets:', this.widgets?.map(w => ({name: w.name, value: w.value})));
// CRITICAL: Restore UUID from properties if available
// onConfigure runs AFTER onNodeCreated, so we need to update UUID here
@@ -695,26 +704,54 @@ app.registerExtension({
this.uuid = this.properties.uuid;
}
// Restore ref layers from widget data
// Find the ref_layer_data widget
const refDataWidget = this.widgets?.find(w => w.name === 'ref_layer_data');
console.log('[PrepareRefs onConfigure] ref_layer_data widget:', refDataWidget);
console.log('[PrepareRefs onConfigure] widget value:', refDataWidget?.value);
console.log('[PrepareRefs onConfigure] ref_layer_data widget found:', !!refDataWidget);
if (refDataWidget && Array.isArray(refDataWidget.value) && refDataWidget.value.length > 0) {
console.log('[PrepareRefs onConfigure] Restoring from widget data:', refDataWidget.value.length, 'layers');
this.restoreRefLayers(refDataWidget.value);
// ComfyUI's configure applies widget values from widgets_values array to widgets BEFORE onConfigure runs
// The widget value is a JSON string (STRING type in Python), so parse it
let layerData = null;
if (refDataWidget && refDataWidget.value) {
try {
// Parse JSON string to get layer data array
layerData = typeof refDataWidget.value === 'string' ? JSON.parse(refDataWidget.value) : refDataWidget.value;
console.log('[PrepareRefs onConfigure] Parsed layer data from widget:', layerData);
} catch (e) {
console.warn('[PrepareRefs onConfigure] Failed to parse ref_layer_data:', e);
layerData = [];
}
}
console.log('[PrepareRefs onConfigure] Layer data:', layerData);
// If we have layer data from the workflow (even empty array), use it and DON'T restore from session
// This ensures loading a workflow with no layers doesn't restore old session data
if (layerData !== null && layerData !== undefined && Array.isArray(layerData)) {
if (layerData.length > 0) {
console.log('[PrepareRefs onConfigure] Restoring from workflow data:', layerData.length, 'layers');
this.restoreRefLayers(layerData);
} else {
console.log('[PrepareRefs onConfigure] Workflow has empty layer array - clearing existing layers');
// Clear existing layers since the workflow has none
this.refLayers?.forEach(layer => {
if (layer.domElement?.parentNode) {
layer.domElement.parentNode.removeChild(layer.domElement);
}
});
this.refLayers = [];
this.selectedRefLayer = null;
}
} else {
console.log('[PrepareRefs onConfigure] No widget data, trying session storage...');
// Try to restore from session storage as fallback
// Only restore from session if no workflow data exists (undefined/null)
console.log('[PrepareRefs onConfigure] No workflow layer data, trying session storage...');
const sessionLayers = safeGetSessionItem(`prepare-refs-layers-${this.uuid}`);
console.log('[PrepareRefs onConfigure] Session storage data:', sessionLayers);
if (sessionLayers) {
try {
const layerData = JSON.parse(sessionLayers);
console.log('[PrepareRefs onConfigure] Parsed session data:', layerData);
if (Array.isArray(layerData) && layerData.length > 0) {
console.log('[PrepareRefs onConfigure] Restoring from session:', layerData.length, 'layers');
this.restoreRefLayers(layerData);
const parsedData = JSON.parse(sessionLayers);
console.log('[PrepareRefs onConfigure] Parsed session data:', parsedData);
if (Array.isArray(parsedData) && parsedData.length > 0) {
console.log('[PrepareRefs onConfigure] Restoring from session:', parsedData.length, 'layers');
this.restoreRefLayers(parsedData);
}
} catch (e) {
console.warn('[onConfigure] Failed to restore layers from session:', e);
@@ -763,7 +800,8 @@ app.registerExtension({
if (refWidget) {
const layerData = this.getRefLayerData();
console.log('[PrepareRefs onSerialize] Saving layers:', layerData);
refWidget.value = layerData;
// Widget is STRING type in Python, so serialize to JSON
refWidget.value = JSON.stringify(layerData);
}
o.properties = o.properties || {};