Moving from a node to the panel crossed empty canvas or other nodes and
changed what it showed, so Lock and the editors were out of reach without
Sticky. Hover now holds the node while the pointer heads for the panel,
until it rests, and only reads the canvas, DOM widgets and Vue nodes, so
the sidebar no longer exposes nodes behind it. With Follow Cursor the
glass holds still on the way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Over a DOM widget such as a prompt text box, node_over is empty and the
Inspector falls back to its own hit test. That divided the offset by the
scale, so it missed at any zoom but 100%. It now uses LiteGraph's
ds.convertCanvasToOffset, and the try/catch that hid this is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Load Image listed its internal $$canvas-image-preview widget as a row in
the Inspector and the pop-out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run ids name other nodes in another workflow tab. The run state now says
whether the tab on screen is the one that ran, and is sent again with the
info a tab switch changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The frontend wraps canvas.onDrawForeground again whenever its DOM widgets
mount, and the wrapper calls the highlighter's hook, which then called the
wrapper. The hook now keeps every function it replaced and goes one level
down per call, so each runs once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Encoding each frame to PNG and posting it over the channel lagged the glass
and capped it at 30 fps. The viewer is a same-origin popup, so the glass now
draws each rendered frame into it in the same animation frame. The viewer
hands its window back through opener.postMessage after a ComfyUI reload.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Listen for the viewer once ComfyUI is set up, so a viewer left open over a reload connects fully
- A duplicated tab starts its own session instead of sharing the viewer
- Ignore requests from a viewer that is not connected
- Stop the render loop while the glass is hidden and the viewer shows no frames
- Do not place the panel again on the 250 ms refresh
- Build output URLs from filename, subfolder and type only
- Viewer: skip undecodable frames, tolerate showPicker and blocked storage, keep the channel open on unload, shorten "Go to node…"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each slot lists the nodes it links to as chips that jump to them, and flags
required inputs with nothing connected. Links to a subgraph's own input or
output show as plain chips.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shown node's live previews or last results appear as thumbnails under the
header, and the Output view shows one in place of the frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Forward ComfyUI's execution events as a throttled run message while the
viewer is connected: a run bar, node badges, an error banner and marked rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pop-out can set the glass zoom, switch between Split, Glass and Info,
and resize the Inspector by dragging or with the arrow keys; the layout is kept per browser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the Inspector's header, nav row, breadcrumb and searchable node picker,
run panel actions through UIManager.runAction, and bring the editors, copy
buttons and viewer keys in line with the Inspector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Send a snapshot of the palette tokens instead of a theme name, and draw the
viewer flat like the Inspector with SVG icons, switches and a stacked layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frames are encoded with toBlob one at a time, stale decodes are dropped,
and fit and fullscreen use object-fit: contain so they no longer crop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
InfoPanel builds the payload, applies the Inspector's edit guards and refreshes
the viewer on a 250 ms tick while connected, also when the glass is off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each ComfyUI tab talks to its viewer on a session channel. Pings run only while
connected, status follows pings, and a hidden glass keeps feeding the viewer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the viewer from import.meta.url, open it as a popup window, and
render data:image values and node ids without raw HTML or inline handlers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Paint Vue nodes under the glass from the DOM, since the canvas capture
has none of them.
- Resolve the Inspector's hovered node from the Vue node element under
the cursor, falling back to the canvas hit test in pan mode.
- Fit the node highlight to collapsed nodes in both modes.
- Draw Node Title Emphasis on the title bar instead of the first slot
row, and skip nodes without a title bar such as Reroute.
- Fill the glass with the page background so it is not half-empty at
the canvas edge or wrong at zoom 1.5 and above.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restyle the panel on ComfyUI palette tokens and drop the built-in themes.
Add a header with Back/Forward, Prev/Next, Focus, Lock and a searchable
Go to node picker, a subgraph/group breadcrumb, and simplified modes:
Follow glass or Float, plus Sticky, Follow selection and Lock to node
(replaces Hold).
Fix widget edits (step2 steps, INT/FLOAT, commit on Enter/blur, undo),
subgraph lookups, orphaned dropdown menus, global arrow-key capture,
the pop-out editor, and remove Google Fonts loading and dead code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Make the sidebar tab wrapper fill the panel, and let the content keep its
natural height when the parent has none, so the settings no longer collapse
to a 16px scroller.
- Only measure DOM images and videos under the glass, so per-frame work scales
with what is under the glass instead of all media on the page.
- Cap Virtual Zoom near 60 fps again, so high-refresh displays do not run two
full graph draws every frame.
- Cull nodes with LiteGraph's boundingRect and keep collapsed nodes, so title
bars and collapsed nodes in front of a preview stay on top in the glass.
- Drop the self-min on the title width and the dead pos/size guards.
- Remove the stale web/shared/settings.js build output.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Updated `sanitizeElement` to strictly remove whitespace and control characters from URI attributes before validation.
This prevents bypasses like `java\tscript:` in `href`, `src`, `action`, and `formaction` attributes.
Added comprehensive unit tests covering these bypass vectors.
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
Added `sanitizeElement` utility to recursively remove inline event handlers (on*) and javascript: URIs from HTML elements.
Applied sanitization in `MagnifyGlass.renderHtmlOverlays` when cloning nodes for the magnifying glass view.
Added unit tests to verify sanitization logic.
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
Added `sanitizeElement` utility to recursively remove inline event handlers (on*) from HTML elements.
Applied sanitization in `MagnifyGlass.renderHtmlOverlays` when cloning nodes for the magnifying glass view.
Added unit tests to verify sanitization logic.
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
- Replaced read-only hex code span with an editable text input in `createColorPicker`
- Implemented bidirectional sync between text input and color picker
- Added validation for hex codes and auto-correction on change/blur
- Ensured keydown propagation is stopped to prevent triggering global shortcuts
- Added aria-label for accessibility
- Updated unit tests to verify sync logic and event handling
- Added CSS styles for `magnify-color-preview` input to match UI design
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
Reduced redundant iterations over `app.graph._nodes` in `OffscreenRenderer`.
Implemented `getVisibleNodes` to calculate visible nodes once per frame (O(N)) and reused this list for text, title, and image preview rendering passes (O(M)).
This improves performance for large graphs by replacing multiple O(N) traversals with a single O(N) traversal followed by O(M) traversals where M << N.
Added unit tests in `tests/unit/OffscreenRenderer.test.ts` to verify node visibility culling logic.
Updated `.jules/bolt.md` with performance learnings.
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
- Replaced read-only hex code span with an editable text input in `createColorPicker`
- Implemented bidirectional sync between text input and color picker
- Added validation for hex codes and auto-correction on blur
- Ensured keydown propagation is stopped to prevent triggering global shortcuts
- Added aria-label for accessibility
- Updated unit tests to verify sync logic and event handling
- Added CSS styles for `magnify-color-preview` input to match UI design
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
Removed the `isHtml` flag support in `UIManager`, which allowed bypassing HTML escaping.
Replaced it with a `specialType` mechanism (specifically 'focus-node') to render trusted
HTML structures internally while ensuring dynamic values are always escaped.
Updated `Security.test.ts` to verify the fix and prevent regression.
Fixes: Potential XSS vulnerability via unchecked HTML injection.
- Added 'Obsidian', 'Obsidian Dark', and 'Milk White' themes to Info Panel and PopOut viewer.
- Improved theme detection in 'StateManager' to support new themes.
- Implemented button widget support in PopOut viewer with 'button-click' event handling.
- Enhanced widget options handling in 'PopOutManager' for combo widgets.
- Updated dropdown accessibility tests.
- Refactor `showWebGLError` to use `document.createElement` instead of `innerHTML`
- Replace inline `onclick` handler with `addEventListener` for CSP compliance
- Add unit test to verify XSS prevention and button functionality
- Moves `litegraphCanvas.getBoundingClientRect()` call outside the widget loop in `renderHtmlOverlays`.
- Reuses the `dpr` calculated at the top level.
- Adds regression test `tests/unit/MagnifyGlassRenderOptim.test.ts`.
Improves rendering performance by reducing forced reflows during magnification.
Add validation for all InfoPanel settings loaded in StateManager to prevent
injection of invalid values (e.g. out-of-bounds numbers, invalid colors).
This mitigates risks of degraded stability or potential injection via
manipulated settings storage.
- Add validateNumber, validateColor, validateStringOption helpers
- Update StateManager.loadSettings to validate all keys
- Add unit tests for validation logic
- Export form creation helpers for testing
- Add unique ID generation for sliders, selects, and color pickers
- Associate labels with inputs using `htmlFor`
- Add unit tests to verify accessibility attributes
- Pass event object to handleToggle from keydown listener
- Ensure stopPropagation() is called for keyboard events
- Maintain consistency between mouse and keyboard interaction behavior
- Update Require Alt Key toggle sync to include aria-checked attribute
- Update Reset Position button to clear aria-checked on Follow Cursor toggle
- Add regression tests for sidebar interaction state synchronization
- Add `role="switch"` and `aria-checked` to toggle elements
- Add `tabIndex="0"` for keyboard focus
- Add keyboard event listeners for Enter and Space keys
- Add `aria-labelledby` to associate toggles with their labels
- Enable toggling by clicking the label text
- Export `createToggle` for testing
- Add unit tests for accessibility and interaction logic
- Implement `validateNumber` and `validateColor` in `ConfigManager`.
- Clamp numeric settings (zoom, size, borders) to safe ranges.
- Validate color settings against hex regex.
- Update `ConfigManager` unit tests to cover validation logic.
This enhancement prevents invalid configuration states and potential stability issues caused by malformed or extreme setting values.