Skip to content

fix(PreviewBridge): ComfyUI v1.34+ compatibility - widget.value non-configurable#1172

Open
djdarcy wants to merge 2 commits into
ltdrdata:Mainfrom
djdarcy:minimal-fix-v2
Open

fix(PreviewBridge): ComfyUI v1.34+ compatibility - widget.value non-configurable#1172
djdarcy wants to merge 2 commits into
ltdrdata:Mainfrom
djdarcy:minimal-fix-v2

Conversation

@djdarcy

@djdarcy djdarcy commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

After ComfyUI updated to v1.34+ with the Vue-based frontend, the ImpactPack PreviewBridge node broke in two ways:

  1. MaskEditor showed wrong image: Widget value retained old clipspace paths, causing MaskEditor to display the previous image instead of the newly generated one
  2. restore_mask settings ignored: Mask restoration ("never", "if_same_size", "always") stopped working entirely

Debugging revealed a few causes...

ComfyUI v1.34+ made widget.value non-configurable, causing Object.defineProperty() to fail silently (error swallowed by extension system).

The original JavaScript code relied on intercepting widget.value changes to:

  1. Register clipspace paths via API call to /impact/set/pb_id_image
  2. Track image state for proper display

Without this registration, PreviewBridge.load_image() couldn't find clipspace paths in preview_bridge_image_id_map, returning empty 64x64 placeholder masks instead of actual user-drawn masks.

Solution

JavaScript (impact-image-util.js)

  • Replace Object.defineProperty(w, 'value', ...) with node.onExecuted hook
  • Register preview images after each execution via existing API
  • Add null checks for widgets
  • Add configurable check for ImageReceiver to prevent silent failures

Python (bridge_nodes.py)

  • Add load_mask_from_clipspace() to read masks directly from disk (bypasses broken registration)
  • Clipspace paths now take priority over cache (user's latest edit preserved)
  • Apply same fix to PreviewBridgeLatent

Demo Video

Test-Demo-Image

Demonstrates:

  • block setting: "if_empty_mask" and "never" both work
  • restore_mask modes: "never", "if_same_size", "always" all function correctly
  • Edited masks persist across workflow runs

Testing Summary

restore_mask Image Changed Expected Result
never Yes Mask cleared
if_same_size Yes (same size) Mask preserved
if_same_size Yes (diff size) Mask cleared
always Yes (any size) Mask preserved

Backwards Compatibility

No breaking API changes. Existing workflows unaffected. Fix specifically targets ComfyUI v1.34+ frontend compatibility while maintaining support for older versions.

Related

…onfigurable

Breaking Change in ComfyUI v1.34+:
- Vue frontend makes widget.value non-configurable
- Object.defineProperty() fails silently (error swallowed)

JavaScript Fix:
- Replace Object.defineProperty(w, 'value') with node.onExecuted hook
- Add null checks for widgets
- Add configurable check for ImageReceiver

Python Fix:
- Add load_mask_from_clipspace() for direct disk loading
- Clipspace paths now take priority over cache (user edits preserved)
- Apply same fix to PreviewBridgeLatent

All restore_mask modes tested: never, if_same_size, always
@djdarcy

djdarcy commented Jan 2, 2026

Copy link
Copy Markdown
Contributor Author

FYI ... while working on this fix, a few ideas came up that could possibly improve long-term maintainability:

  1. Widget Compatibility Layer - Abstract widget interaction into a helper that handles both old (Object.defineProperty) and new (onExecuted hook) approaches. Single place to update when ComfyUI changes again.

  2. Centralized Clipspace Handling - Move is_clipspace_path(), load_mask_from_clipspace(), etc. to a dedicated module to avoid duplication and make testing easier.

  3. Optional Debug Logging - Environment variable like IMPACT_PREVIEW_BRIDGE_DEBUG=true could enable verbose logging without code changes, helpful for diagnosing future issues.

  4. Monitor window.comfyAPI - ComfyUI is developing stable extension APIs. Migrating to those when available would reduce reliance on internal implementation details.


I also developed some additional enhancements during debugging that weren't included in this minimal fix (content-based cache invalidation via IS_CHANGED, tensor fingerprinting, verbose debug logging). Code is available here if useful: https://gist.github.com/djdarcy/f7aaf10d36f2c9f207e948e6f39e8ad7

These aren't blockers by any means, but could be valuable follow-up work. Happy to discuss or create separate issues if interested.

The gist link provides the full code snippets for:

  • IS_CHANGED classmethod (content fingerprinting)
  • _images_match / _latents_match (content comparison)
  • Debug logging patterns

Detect when Python generates a new temp file (image changed) vs returning
existing file (same image). Only update widget value when image actually
changed, preserving clipspace paths that contain user-drawn masks.

Fixes stale clipspace image loading after restart with different input.
@djdarcy

djdarcy commented Jan 4, 2026

Copy link
Copy Markdown
Contributor Author

Added 072a5e9 - fix for stale clipspace image after restart/image change

The original fix had an issue: when a user drew a mask and then ran the workflow with a different input image, MaskEditor would still load the old clipspace image instead of the new one.

This commit detects when Python generates a new temp file (indicating image changed) by checking output.images[0] metadata. Clipspace paths are only preserved when the image hasn't changed.

Tested scenarios:

  • Same image, mask persists
  • Different image, MaskEditor shows new image
  • All restore_mask modes still work

@djdarcy

djdarcy commented Jan 13, 2026

Copy link
Copy Markdown
Contributor Author

Following up on this PR - I've created an extended version of Preview Bridge as a standalone node: https://github.com/DazzleNodes/ComfyUI-PreviewBridgeExtended.

It builds on the fixes here and adds:

  • Optional MASK input (mask_opt) - Accept masks from upstream nodes (LoadImage, SAM, detection nodes, etc.)
  • LayerCache architecture - Explicit additions/subtractions tracking for reliable mask persistence across mode switches
  • Two-layer editing - Red tint for input mask, orange tint for editor additions, with visual preview
  • mask_output widget - Choose what goes to output: combined (OR union), input_mask only, or mask_editor only
  • editor_target widget - Control which layer MaskEditor affects: edit both together, or lock one layer while editing the other
  • Additional block modes - if_empty_editor (block until user draws something) and always (debugging backstop)
  • Instant preview refresh - Widget changes update preview without re-running workflow

The repo also has PBE_DEBUG=1 env var if you want to see the internals without digging through code.

Feel free to borrow any ideas, incorporate changes into Impact Pack, or just know it exists as an alternative. Happy to discuss architecture decisions if useful. Happy new year!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant