Skip to content

Commit 1714e86

Browse files
committed
v0.3.0-alpha: Full LayerCache architecture with instant preview refresh
Fixes #1, Fixes #2 Architecture: - Full LayerCache migration replaces legacy 9-cache system - Unified layer storage: upstream, additions, subtractions - "Additions win" formula: combined = max(upstream - subtractions, additions) - Added get_preview_masks() for unified preview selection - Removed obsolete delta mode from preview.py - Simplified node.py (-400 lines), api.py (-150 lines), preview.py (-50 lines) Features: - Instant preview refresh when changing mask_output or editor_target widgets - New get-preview API endpoint for fetching preview from LayerCache state - VS Code debug configurations for ComfyUI development Fixes: - Stale cache bugs when switching editor_target modes (#2) - Widget switch preview bug - no longer requires re-running workflow (#1) - MaskEditor Cancel now correctly restores preview from LayerCache - GitHub workflow names (was "Fit Mask to Image", now "Preview Bridge Extended") - Issue template project names Files changed: 17 files, +619/-911 lines (net reduction of ~300 lines)
1 parent 762abaf commit 1714e86

17 files changed

Lines changed: 619 additions & 911 deletions

.github/ISSUE_TEMPLATE/bug-report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: Bug report
3-
about: Create a report to help improve Fit Mask to Image
3+
about: Create a report to help improve Preview Bridge Extended
44
title: "[BUG] "
55
labels: bug
66
assignees: ''

.github/ISSUE_TEMPLATE/feature-request.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: Feature request
3-
about: Suggest an idea for Fit Mask to Image
3+
about: Suggest an idea for Preview Bridge Extended
44
title: "[FEATURE] "
55
labels: enhancement
66
assignees: ''

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Fit Mask to Image CI
1+
name: Preview Bridge Extended CI
22

33
on:
44
push:

.github/workflows/publish-to-registry.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
jobs:
99
publish:
1010
runs-on: ubuntu-latest
11-
name: Publish Fit Mask to Image to Registry
11+
name: Publish Preview Bridge Extended to Registry
1212

1313
steps:
1414
- name: Check Out Repo
@@ -35,6 +35,6 @@ jobs:
3535
# 1. Update MAJOR, MINOR, PATCH in version.py (git hooks will update __version__)
3636
# 2. Commit and push to main branch
3737
# 3. Create a git tag: git tag v0.1.0 && git push origin v0.1.0
38-
# 4. Go to: https://github.com/DazzleNodes/fit-mask-to-image/actions/workflows/publish-to-registry.yml
38+
# 4. Go to: https://github.com/DazzleNodes/ComfyUI-PreviewBridgeExtended/actions/workflows/publish-to-registry.yml
3939
# 5. Click "Run workflow" → Select branch → Run workflow
4040
# 6. This manual step prevents accidental publishing of every commit

.vscode/launch.json

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,51 @@
1313
"justMyCode": true
1414
},
1515
{
16-
"name": "Python: Module",
16+
"name": "Python: Current File with Arguments",
1717
"type": "python",
1818
"request": "launch",
19-
"module": "ComfyUI-ImageMask-Fix",
19+
"program": "${file}",
2020
"console": "integratedTerminal",
21+
"args": [
22+
// Add your arguments here, or use VS Code's input picker at runtime
23+
"${command:pickArgs}"
24+
],
2125
"justMyCode": true
2226
},
2327
{
24-
"name": "Python: Current File with Arguments",
28+
"name": "ComfyUI: Debug This Node",
2529
"type": "python",
2630
"request": "launch",
27-
"program": "${file}",
31+
"program": "${env:COMFYUI_PATH}/main.py",
2832
"console": "integratedTerminal",
33+
"justMyCode": false,
34+
"cwd": "${env:COMFYUI_PATH}",
35+
"env": {
36+
"COMFY_DEBUG_PREVIEW_BRIDGE_EXT": "true"
37+
},
2938
"args": [
30-
// Add your arguments here
31-
// Examples:
32-
// "--input", "${workspaceFolder}/data/input.txt",
33-
// "--output", "${workspaceFolder}/data/output.txt",
34-
// Use VS Code's input picker to add arguments at runtime
35-
"${command:pickArgs}"
39+
// Common ComfyUI arguments (uncomment as needed):
40+
// "--listen", "127.0.0.1",
41+
// "--port", "8188",
42+
// "--auto-launch",
43+
// "--disable-xformers",
3644
]
37-
}
38-
],
39-
"compounds": [
45+
},
4046
{
41-
"name": "Server/Client",
42-
"configurations": ["Python: Module", "Python: Current File"]
47+
"name": "ComfyUI: Attach to Running",
48+
"type": "python",
49+
"request": "attach",
50+
"connect": {
51+
"host": "localhost",
52+
"port": 5678
53+
},
54+
"justMyCode": false,
55+
"pathMappings": [
56+
{
57+
"localRoot": "${workspaceFolder}",
58+
"remoteRoot": "${env:COMFYUI_PATH}/custom_nodes/ComfyUI-PreviewBridgeExtended"
59+
}
60+
]
4361
}
4462
]
4563
}

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.3.0-alpha] - 2026-01-09
11+
12+
### Added
13+
- **Instant Preview Refresh**: Preview now updates immediately when changing `mask_output` or `editor_target` widgets (fixes #1)
14+
- No longer requires re-running workflow to see preview changes
15+
- Widget change listeners call `get-preview` API to regenerate preview from LayerCache state
16+
- **get-preview API**: New endpoint `/preview-bridge-extended/get-preview` for fetching current preview without clipspace decomposition
17+
- Used by Cancel handler and widget change listeners
18+
- Returns preview based on current LayerCache state and widget values
19+
- **VS Code Debug Configs**: Added ComfyUI debugging configurations to `.vscode/launch.json`
20+
- "ComfyUI: Debug This Node" - Launch ComfyUI with debugger attached
21+
- "ComfyUI: Attach to Running" - Attach to running ComfyUI instance
22+
- Uses `COMFYUI_PATH` environment variable for portability
23+
24+
### Changed
25+
- **Full LayerCache Migration**: Removed all legacy dual-cache code
26+
- LayerCache is now the exclusive storage system
27+
- Removed `_preview_bridge_input_mask_cache` and `_preview_bridge_editor_mask_cache`
28+
- Simplified `node.py`, `api.py`, and `preview.py` to use LayerCache exclusively
29+
- **Removed Delta Mode**: Eliminated obsolete delta computation from `preview.py`
30+
- Delta mode was incompatible with LayerCache architecture
31+
- Preview now uses direct layer display (input_mask as red, editor_mask as orange)
32+
- Simplified `apply_mask_overlays()` function
33+
34+
### Fixed
35+
- **Widget Switch Preview Bug**: Fixed preview not updating when switching between modes
36+
- Root cause: Widget changes didn't trigger preview refresh (only workflow execution did)
37+
- Solution: Added widget callbacks that call `getPreview` API
38+
- **Cancel Handler**: MaskEditor Cancel now correctly restores preview from LayerCache state
39+
- Previously could show stale preview after cancelling
40+
- **GitHub Workflows**: Fixed workflow names from "Fit Mask to Image" to "Preview Bridge Extended"
41+
- Updated `main.yml` CI workflow name
42+
- Updated `publish-to-registry.yml` workflow name and repo URL
43+
- **Issue Templates**: Fixed project name in bug report and feature request templates
44+
45+
### Technical Details
46+
- See `private/claude/2026-01-09__06-05-13__widget-switch-preview-refresh.md` for widget listener implementation
47+
- See `private/claude/2026-01-09__05-51-13__preview-delta-mode-bug.md` for delta mode removal analysis
48+
1049
## [0.2.0-alpha] - 2026-01-09
1150

1251
### Added

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Since Impact-Pack maintains a conservative approach to major changes, this repo
2929
- **Optional Mask Input**: Accept masks from upstream nodes alongside MaskEditor drawings
3030
- **Configurable Mask Output**: Choose between combined, input_mask, or mask_editor modes
3131
- **Two-Layer Visualization**: Red tint for input mask, orange tint for editor mask
32+
- **Instant Preview Refresh**: Preview updates immediately when changing display mode widgets
3233
- **Mask Restoration**: Persist masks across image changes (never, always, if_same_size)
3334
- **Smart Empty Detection**: Detects placeholder masks (64x64) and all-zero masks
3435
- **Mask Combination**: OR (union) combination of multiple mask sources
@@ -148,6 +149,15 @@ Set `mask_output` to "mask_editor":
148149

149150
## Technical Details
150151

152+
### LayerCache Architecture
153+
154+
The node uses a unified `LayerCache` system for mask storage with three explicit layers:
155+
- **upstream**: Original mask from `mask_opt` input (immutable reference)
156+
- **additions**: User additions from MaskEditor
157+
- **subtractions**: User erasures/subtractions from original mask
158+
159+
The "additions win" formula ensures predictable output: `combined = max(upstream - subtractions, additions)`
160+
151161
### Empty Mask Detection
152162

153163
Masks are considered empty if:
@@ -162,6 +172,10 @@ combined = torch.sum(torch.stack(masks_to_combine, dim=0), dim=0)
162172
combined = torch.clamp(combined, 0, 1)
163173
```
164174

175+
### Instant Preview Refresh
176+
177+
When `mask_output` or `editor_target` widgets change, JavaScript listeners trigger an API call to regenerate the preview from the current LayerCache state. This provides WYSIWYG behavior without re-running the workflow.
178+
165179
## Development
166180

167181
This project uses Git-RepoKit hooks for automatic version tracking.

__init__.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Part of the DazzleNodes collection - standalone ComfyUI custom nodes.
66
"""
77

8-
from .py import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, generate_preview_for_api, prepare_for_editing
8+
from .py import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, generate_preview_for_api, get_preview_for_api, prepare_for_editing
99
from .version import __version__
1010

1111
# Tell ComfyUI where to find our JavaScript files
@@ -109,6 +109,53 @@ async def prepare_for_edit_api(request):
109109

110110
print("[PreviewBridgeExtended] Registered API endpoint: /preview-bridge-extended/prepare-for-edit")
111111

112+
@server.PromptServer.instance.routes.post("/preview-bridge-extended/get-preview")
113+
async def get_preview_api(request):
114+
"""
115+
API endpoint to get current preview from LayerCache state.
116+
117+
Used by Cancel handler to restore correct preview after MaskEditor closes
118+
without saving. Unlike refresh-preview, this doesn't decompose a new clipspace.
119+
120+
POST body: {
121+
"node_id": "123",
122+
"mask_output": "combined", # optional - current widget value
123+
"editor_target": "combined" # optional - current widget value
124+
}
125+
Returns: {"success": true, "image_data": "data:image/png;base64,..."}
126+
"""
127+
try:
128+
data = await request.json()
129+
node_id = data.get('node_id')
130+
mask_output = data.get('mask_output')
131+
editor_target = data.get('editor_target')
132+
133+
if not node_id:
134+
return web.json_response({
135+
'success': False,
136+
'error': 'No node_id provided'
137+
}, status=400)
138+
139+
# Generate preview from existing LayerCache state
140+
result = get_preview_for_api(
141+
str(node_id),
142+
mask_output_override=mask_output,
143+
editor_target_override=editor_target
144+
)
145+
146+
if result.get('success'):
147+
return web.json_response(result)
148+
else:
149+
return web.json_response(result, status=400)
150+
151+
except Exception as e:
152+
return web.json_response({
153+
'success': False,
154+
'error': str(e)
155+
}, status=500)
156+
157+
print("[PreviewBridgeExtended] Registered API endpoint: /preview-bridge-extended/get-preview")
158+
112159
except Exception as e:
113160
print(f"[PreviewBridgeExtended] Warning: Could not register API endpoint: {e}")
114161

py/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# Re-export API functions for route handlers
2222
from .api import (
2323
generate_preview_for_api,
24+
get_preview_for_api,
2425
prepare_for_editing,
2526
)
2627

@@ -48,6 +49,7 @@
4849
delete_layer_cache,
4950
decompose_and_store,
5051
get_output_mask,
52+
get_preview_masks,
5153
)
5254

5355
__all__ = [
@@ -57,6 +59,7 @@
5759
'NODE_DISPLAY_NAME_MAPPINGS',
5860
# API
5961
'generate_preview_for_api',
62+
'get_preview_for_api',
6063
'prepare_for_editing',
6164
# Utils
6265
'load_mask_from_clipspace',
@@ -75,4 +78,5 @@
7578
'delete_layer_cache',
7679
'decompose_and_store',
7780
'get_output_mask',
81+
'get_preview_masks',
7882
]

0 commit comments

Comments
 (0)