Prior to #6014, I had identified and started on #6015 to resolve what I
believed to be a larger class of issues. While the issue I was solving
produced the same error log on the same line, I misunderstood the
circumstances in which the reported issue would occur. As proxyWidgets
do not have their own value, only linked widgets should have their value
restored from `widgets_values`. This PR adds an additional check both
that the property entry is within bounds, and that the property entry is
for a linked widget.
See #6014
A potential additional issue is still being investigated.
- Additional issue seems unrelated to fix here. Will leave issue open,
but recommend merging this as is to not block v1.28.7
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6054-Add-additional-check-when-restoring-widgets_values-28c6d73d365081489d29fc9723132db0)
by [Unito](https://www.unito.io)
## Summary
- Enhanced video control visibility logic for better UX
- Added fullscreen gallery view with zoom-in button
- Fixed hover interaction issues with overlays
## Changes
### Video Controls
- **Before**: Controls hidden when not hovering
- **After**: Controls always visible when not playing, hover-based
during playback
### Overlay Behavior
- **Before**: All overlays hidden during video playback
- **After**: All overlays (actions, tags, layers) show on hover even
during playback
### Gallery View
- Added zoom-in button to top-right corner (all media types except 3D)
- Integrated with existing ResultGallery component
- Gallery closes when clicking dimmed background area
### Bug Fixes
- Fixed hover flicker issue by proper event handling on overlay elements
## Test Plan
- [x] Test video controls visibility (paused vs playing)
- [x] Test overlay hover behavior during video playback
- [x] Test zoom-in button opens gallery view
- [x] Test gallery closes on background click
- [x] Test 3D assets don't show zoom button
- [x] Test in Storybook with various media types
🤖 Generated with [Claude Code](https://claude.ai/code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6065-feat-Improve-MediaAssetCard-video-controls-and-add-gallery-view-28d6d73d3650818c90cfc5d0d00e4826)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude <noreply@anthropic.com>
This pull request refactors how top bar badges are handled in the
application, making badge rendering extensible and moving cloud badge
logic into the extension system. The main changes include replacing the
old `CloudBetaBadge` component with a new, generic badge system,
introducing a Pinia store for badge management, and updating the
extension API to support top bar badges.
**Badge System Refactor and Extensibility**
* Replaced the hardcoded `CloudBetaBadge` with a new `TopbarBadges`
component, which dynamically renders badges from the store instead of
relying on the `isCloud` flag in `TopMenubar.vue`.
[[1]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL9-R9)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL43-R48)
* Renamed and refactored `CloudBetaBadge.vue` to `TopbarBadge.vue`,
making it accept a generic `badge` prop and removing i18n logic from the
component.
* Added a new `TopbarBadges.vue` component to render all badges from the
`topbarBadgeStore`.
**Badge Data Management**
* Introduced a new Pinia store `topbarBadgeStore` that aggregates top
bar badges from all extensions, enabling dynamic badge management.
**Extension System Integration**
* Updated the extension API (`ComfyExtension` interface) to support a
new `topbarBadges` property, allowing extensions to contribute badges to
the top bar.
* Added a core extension (`cloudBadge.ts`) that registers a "Comfy
Cloud" beta badge when running in a cloud environment, using the new
badge system.
[[1]](diffhunk://#diff-b7818ca9daae2411d56695777160b8132507f2a3ff4f700d2510453c8833ca75R1-R16)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R2)
**Type Definitions**
* Added a new `TopbarBadge` type to `comfy.ts` to define the structure
for top bar badges, supporting optional labels.
This pull request introduces a new system for displaying
environment-specific badges in the application's top bar, with a focus
on supporting a "Comfy Cloud" badge in production environments. The
changes include new badge types, extension support, UI components, and
environment detection logic to ensure badges are only shown in
appropriate contexts.
**Topbar Badge System**
* Added a new `TopbarBadge` type and support for topbar badges in the
`ComfyExtension` interface to allow extensions to specify badges for the
top bar.
[[1]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R24-R31)
[[2]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R85-R88)
* Created a Pinia store `topbarBadgeStore` to aggregate topbar badges
from all registered extensions for display.
**UI Integration**
* Added a new `TopbarBadges.vue` component to render topbar badges and
integrated it into the top menu bar UI.
[[1]](diffhunk://#diff-6f460b1398fd033a2059daca1f991c74ce572cef86046a3726d1b1a70a3a4325R1-R32)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL5-R14)
* Updated CSS variables and menu styling to support the new badge
visuals.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R88-R89)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL5-R14)
**Environment Detection and Extension Registration**
* Added a runtime environment detection utility to determine if the app
is running in production or staging, replacing the previous build-time
constant approach.
* Registered a new `cloudBadge` extension that conditionally adds a
"Comfy Cloud" badge with a "BETA" label when running in production.
[[1]](diffhunk://#diff-b7818ca9daae2411d56695777160b8132507f2a3ff4f700d2510453c8833ca75R1-R15)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R2)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6048-Badge-for-cloud-environment-28c6d73d365081188050ece527c3c8f3)
by [Unito](https://www.unito.io)
<img width="996" height="897" alt="Screenshot 2025-10-14 at 20 02 40"
src="https://github.com/user-attachments/assets/5a3258c5-87fc-46ae-ad23-7669696cb8b6"
/>
## Summary
Revise some traditional Chinese to simplified Chinese.
<!-- One sentence describing what changed and why. -->
## Changes
- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->
## Review Focus
<!-- Critical design decisions or edge cases that need attention -->
<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->
## Screenshots (if applicable)
<!-- Add screenshots or video recording to help explain your changes -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6039-Fix-Simplified-Chinese-Translation-28b6d73d365081aebba5f60893b75cb3)
by [Unito](https://www.unito.io)
## Summary
After #5292, at certain browser zoom levels, the xterm terminal did not
fill horizontal space properly, showing a gap with mismatched background
colors (`#171717` viewport vs `black` terminal content).
Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6049
## Problem
The hardcoded `#171717` terminal theme background only affected the
xterm viewport, while terminal content remained black, causing a visible
color mismatch at low zoom levels where the gap was more apparent.
Fixed by keeping original theme when not on desktop distribution.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6056-fix-terminal-style-28c6d73d36508132b521e5767f41d540)
by [Unito](https://www.unito.io)
## Summary
Implements Ctrl+Alt+click batch disconnect functionality for Vue node
output slots to match LiteGraph behavior.
## Changes
- **Feature**: Add Ctrl+Alt+click handler in `useSlotLinkInteraction.ts`
to disconnect all links from output slots
- **Test**: Add test case in `linkInteraction.spec.ts` to verify batch
disconnect behavior
- Follows existing pattern from input slot disconnect implementation
## Implementation Details
The implementation:
- Checks for Ctrl+Alt+click on output slots with existing links
- Calls `resolvedNode.disconnectOutput(index)` to batch disconnect all
links
- Marks canvas as dirty and prevents event propagation
- Matches LiteGraph canvas behavior (`LGraphCanvas.ts:2727-2731`)
- Follows same pattern as existing input slot disconnect (lines 591-611)
Note: Test currently uses `dispatchEvent` for pointerdown with modifiers
and is failing. The feature implementation is correct and matches the
existing codebase patterns, but the test interaction needs debugging.
Under some infrequent circumstances, `audioWidget.value` is not a
string. Presumably if a workflow is loaded with a saved file choice that
does not exist and the value is set to undefined instead.
Instead of a cast, a proper type guard is used and the widget is not
updated if the new value is not a string.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6041-Use-type-check-instead-of-cast-28b6d73d365081249353f4f905769f89)
by [Unito](https://www.unito.io)
## Summary
Establishes distribution-specific code pattern using compile-time
constants and dead code elimination. Demonstrates with Help Center by
hiding extension manager and update buttons in cloud distribution.
Below commentary makes assumption that minifcation and tree-shaking is
enabled (which isn't true yet, but will be eventually).
## Changes
- **What**: Added `src/platform/distribution/types.ts` with distribution
detection via `__DISTRIBUTION__` variable
- **Build**: Vite replaces `__DISTRIBUTION__` at build time using
environment variables
- **Tree-shaking**: All code not relevant to target distribution is
DCR'd and eliminated from bundle
- **Example**: Help Center hides "Manager Extension" menu item and
"Update" buttons in cloud builds
## Pattern
This PR defines a `__DISTRIBUTION__` variable which gets replaced at
build time by Vite using environment variables. All code not relevant to
the given distribution is then DCR'd and tree-shaken.
For simple cases (like this Help Center PR), import `isCloud` and use
compile-time conditionals:
```typescript
import { isCloud } from '@/platform/distribution/types'
if (!isCloud) {
items.push({
key: 'manager',
action: async () => {
await useManagerState().openManager({ ... })
}
})
}
```
The code is DCR'd at build time so there's zero runtime overhead - we
don't even incur the `if (isCloud)` cost because Terser eliminates it.
For complex services later, we'll add interfaces and use an index.ts
that exports different implementations under the same alias per
distribution. It will resemble a DI container but simpler since we don't
need runtime discovery like backend devs do. This guarantees types and
makes testing easier.
Example for services:
```typescript
// src/platform/storage/index.ts
import { isCloud } from '@/platform/distribution/types'
if (isCloud) {
export { CloudStorage as StorageService } from './cloud'
} else {
export { LocalStorage as StorageService } from './local'
}
```
Example for component variants:
```typescript
// src/components/downloads/index.ts
import { isCloud } from '@/platform/distribution/types'
if (isCloud) {
export { default as DownloadButton } from './DownloadButton.cloud.vue'
} else {
export { default as DownloadButton } from './DownloadButton.desktop.vue'
}
```
## Implementation Details
Distribution types (`src/platform/distribution/types.ts`):
```typescript
type Distribution = 'desktop' | 'localhost' | 'cloud'
declare global {
const __DISTRIBUTION__: Distribution
}
const DISTRIBUTION: Distribution = __DISTRIBUTION__
export const isCloud = DISTRIBUTION === 'cloud'
```
Vite configuration adds the define:
```typescript
const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
| 'desktop'
| 'localhost'
| 'cloud'
export default defineConfig({
define: {
__DISTRIBUTION__: JSON.stringify(DISTRIBUTION)
}
})
```
## Build Commands
```bash
pnpm build # localhost (default)
DISTRIBUTION=cloud pnpm build # cloud
DISTRIBUTION=desktop pnpm build # desktop
```
## Future Applications
This pattern can be used with auth or telemetry services - which will
guarantee all the telemetry code, for example, is not even in the code
distributed in OSS Comfy whatsoever while still being able to develop
off `main`.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6028-Add-distribution-detection-pattern-28a6d73d365081b08767d395472cd1bc)
by [Unito](https://www.unito.io)
## Summary
Fixed Vue node opacity calculation to properly combine global opacity
setting with muted/bypassed state opacity.
**Root Cause**: When global opacity setting was added as inline style
(481aa8252), it began overriding CSS `opacity-50` classes due to higher
specificity.
**Solution**: Modified `nodeOpacity` computed property to calculate
effective opacity as `globalOpacity * 0.5` for muted/bypassed states,
removing conflicting CSS classes.
## Changes
- **What**: Fixed [CSS specificity
conflict](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity)
where inline `opacity` style overrode `opacity-50` classes for
muted/bypassed nodes
- **Breaking**: None - restores intended opacity behavior
## Review Focus
Multiplicative opacity calculation ensuring muted/bypassed nodes apply
0.5 opacity on top of global opacity setting rather than being
overridden by it.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6022-fix-Vue-node-opacity-conditions-user-node-opacity-bypass-state-muted-state-2896d73d365081c290f1da37c195c2f5)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
Makes the litegraph `node.widgets` array `shallowReactive` and makes the
`nodeData.widgets` a `reactiveComputed` derived from the litegraph
widget data.

Making changes to the structure of litegraph items is somewhat
dangerous, but code search verifies that there are no custom nodes using
`defineProperty` on `node.widgets`
This fixes display of promoted widgets on subgraph node and any custom
nodes that dynamically add or remove widgets.
TODO:
- Investigate occasional dropped widgets.
- Some of this was confusion with `canvasOnly` widgets and widgets not
implemented in vue. Will keep investigating, but I'm not terribly
concerned with actual test cases and it being an objective improvement.
Known Issue:
- Node does not grow/shrink to fit changed widgets
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6019-Make-nodeData-widgets-reactive-2896d73d3650815691b6ee370a86a22c)
by [Unito](https://www.unito.io)
Cherry picked over from #6025, should've been made to target main to
begin with
## Summary
Fixes the browser tab progress and favicon remaining at ~14% after
workflow completion on `main` by resetting execution state when a run
ends (success, error, or interruption).
## Changes
- Add `execution_success` listener in the execution store
- Centralize terminal-state cleanup in `resetExecutionState()`
- Clear `nodeProgressStates`, queued prompt entry,
`_executingNodeProgress`, and set `activePromptId` to `null`
- Ensures `isIdle` becomes `true` post-run so tab title and favicon no
longer freeze mid-progress
resolves#6024
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6026-fix-execution-reset-progress-state-after-runs-to-unfreeze-tab-title-favicon-main-28a6d73d365081f188ebc2e69d936dd9)
by [Unito](https://www.unito.io)
## Summary
Implements a comprehensive media asset card component system for the
Asset Manager sidebar, enabling display and interaction with various
media types (images, videos, audio, and 3D models).
## Changes
### New Components
- **MediaAssetCard**: Main card component for displaying media assets
- **Media type-specific components**: Specialized display logic for each
media type
- MediaImageTop/Bottom
- MediaVideoTop/Bottom
- MediaAudioTop/Bottom
- Media3DTop/Bottom
- **MediaAssetActions**: Top-left action buttons (delete, download, more
options)
- **MediaAssetMoreMenu**: Dropdown menu for additional actions
- **SquareChip**: Chip component for displaying duration and file format
with dark/light variants
- **MediaAssetButtonDivider**: Visual separator for button groups
### Features
- **Video playback**: Autoplay with native video controls
- Dynamic duration chip positioning based on control visibility
- Hides overlays when video is playing
- **Audio playback**: Audio icon with HTML5 audio element
- Duration chip with consistent positioning
- **3D model support**: Icon display for 3D assets
- **Selection state**: Proper hover and selected state handling with CSS
priority fixes
### Architecture Improvements
- **Domain-Driven Design structure**: Organized under
`src/platform/mediaAsset/` following DDD principles
- **Provide/Inject pattern**: Eliminates props drilling with
MediaAssetKey InjectionKey
- **Composable pattern**: `useMediaAssetActions` manages all action
handlers
- **Type safety**: Comprehensive TypeScript types for media assets and
actions
### UI/UX Enhancements
- **CardTop component**: Added custom class props for slot positioning
- **SquareChip component**: Backdrop blur effects with variant system
- **Lazy loading**: Image optimization with LazyImage component
- **Responsive states**: Loading, selected, and hover states
### Utilities
- **formatDuration**: Converts milliseconds to human-readable format
(45s, 1m 23s, 1h 2m)
## Testing
- Comprehensive Storybook stories for all media types
- Grid layout examples
- Loading and selected state demonstrations
## File Structure
```
src/platform/assets/
├── components/
│ ├── MediaAssetCard.vue
│ ├── MediaAssetCard.stories.ts
│ ├── MediaAssetActions.vue
│ ├── MediaAssetMoreMenu.vue
│ ├── MediaAssetButtonDivider.vue
│ ├── MediaImageTop.vue
│ ├── MediaImageBottom.vue
│ ├── MediaVideoTop.vue
│ ├── MediaVideoBottom.vue
│ ├── MediaAudioTop.vue
│ ├── MediaAudioBottom.vue
│ ├── Media3DTop.vue
│ └── Media3DBottom.vue
├── composables/
│ └── useMediaAssetActions.ts
└── schemas/
└── mediaAssetSchema.ts
```
## Screenshots
[media_asset_record.webm](https://github.com/user-attachments/assets/d13b5cc0-a262-4850-bb81-ca1daa0dd969)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Fixes issue where node size changes are not serialized by routing
DOM-driven node bounds updates through a single CRDT operation so Vue
node geometry stays synchronized with LiteGraph.
## Changes
- **What**: Added `BatchUpdateBoundsOperation` to the layout store,
applied it via the existing Yjs pipeline, notified link sync to
recompute touched nodes, and covered the path with a regression test
## Review Focus
Correctness of the new batch operation when multiple nodes update
simultaneously, especially remote replay/undo scenarios and link
geometry recomputation.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5939-fix-emit-layout-change-for-batch-node-bounds-2846d73d365081db8f8cca5bf7b85308)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>

Did testing on about a dozen custom nodes. Most just work.
- Some custom nodes have copy/pasted the `addDOMWidget` call with types
like `customtext` and get converted to textareas -> Not feasible to fix
here. Can open PRs into custom nodes if complaints arise.
- Only the KJNodes spline editor had mouse issues -> Can
investigate/open PR into KJNodes later.
- Many nodes don't resize gracefully. Probably best handled in a future
PR.
- Some expect to be handled like textareas. These currently have minsize
and don't scale.
- Others, like VHS previews, scale self properly, but don't update
height inside a drag operation -> node height can be set to less than
fit.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6006-Implement-DOMWidget-for-vue-2886d73d3650817ca497c15d87d70f4f)
by [Unito](https://www.unito.io)
This pull request introduces a new audio playback widget for node UIs
and integrates it into the node widget system. The main changes include
the implementation of the `WidgetAudioUI` component, its registration in
the widget registry, and updates to pass node data to the new widget.
Additionally, some logging was added for debugging purposes.
**Audio Widget Implementation and Integration:**
* Added a new `WidgetAudioUI.vue` component that provides audio playback
controls (play/pause, progress slider, volume, options) and loads audio
files from the server based on node data.
* Registered the new `WidgetAudioUI` component in the widget registry by
importing it and adding an entry for the `audioUI` type.
[[1]](diffhunk://#diff-c2a60954f7fdf638716fa1f83e437774d5250e9c99f3aa83c84a1c0e9cc5769bR21)
[[2]](diffhunk://#diff-c2a60954f7fdf638716fa1f83e437774d5250e9c99f3aa83c84a1c0e9cc5769bR112-R115)
* Updated `NodeWidgets.vue` to pass `nodeInfo` as the `node-data` prop
to widgets of type `audioUI`, enabling the widget to access
node-specific audio file information.
**Debugging and Logging:**
* Added logging of `nodeData` in `LGraphNode.vue` and
`WidgetAudioUI.vue` to help with debugging and understanding the data
structure.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R188-R189)
[[2]](diffhunk://#diff-71cce190d74c6b5359288857ab9917caededb8cdf1a7e6377578b78aa32be2fcR1-R284)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5627-Vuenodes-audio-widgets-2716d73d365081fbbc06c1e6cf4ebf4d)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Arjan Singh <1598641+arjansingh@users.noreply.github.com>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: Robin Huang <robin.j.huang@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
Implements droponcanvas functionality and a linkconnectoradapter
refactor.
- Drop on canvas (Shift and default) integrated via LinkConnector
‘dropped-on-canvas’ with proper CanvasPointerEvent.
- LinkConnector adapter: now wraps the live canvas linkConnector (no
duplicate state); added dropOnCanvas() helper.
- Tests: Playwright scenarios for Shift-drop context menu/searchbox,
pinned endpoint, type prefilter, and post-selection auto-connect
(browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts).
There are some followup PRs that will fix/refactor some more noncritical
things, like the terrible slotid, the number/string nodeid confusion,
etc.
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5780 (snapping) <--
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5898 (drop on canvas
+ linkconnectoradapter refactor) <--
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5903 (fix reroute
snapping)
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
This pull request refactors the node selection logic in the Vue nodes
event handler composable to simplify the function signature and improve
single vs. multi-selection behavior. The main change is the removal of
the `wasDragging` parameter from the `handleNodeSelect` function, with
selection logic now determined by the current selection state. Related
test code is updated to match the new function signature.
**Node selection logic improvements:**
* Refactored the `handleNodeSelect` function in
`useNodeEventHandlersIndividual` to remove the `wasDragging` parameter,
making the function signature simpler and relying on selection state to
handle single vs. multi-selection.
* Updated the selection logic to check if multiple nodes are already
selected using `isLGraphNode`, and only perform single selection if not.
**Code and test updates:**
* Updated all calls to `handleNodeSelect` in the composable to remove
the `wasDragging` argument, ensuring consistent usage throughout the
codebase.
[[1]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L125-R123)
[[2]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L146-R144)
[[3]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L173-R171)
* Updated all related test cases to use the new `handleNodeSelect`
signature without the third parameter.
[[1]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL105-R105)
[[2]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL125-R125)
[[3]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL144-R144)
[[4]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL162-R162)
[[5]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL174-R174)
[[6]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL187-R187)
**Utility import:**
* Added an import for `isLGraphNode` from `@/utils/litegraphUtil` to
support the updated selection logic.## Summary
<!-- One sentence describing what changed and why. -->
## Screenshots (if applicable)
https://github.com/user-attachments/assets/71e856d3-afc2-497d-826e-5b485066e7fe
---------
Co-authored-by: github-actions <github-actions@github.com>
This pull request introduces a new extension API for context menu
customization, allowing extensions to contribute items to both canvas
and node right-click menus. It adds two collection methods to the
`ComfyApp` class to aggregate these menu items from all registered
extensions, and updates the extension interface accordingly.
Comprehensive unit tests are included to verify the correct aggregation
behavior and error handling.
**Extension API for Context Menus:**
* Added optional `getCanvasMenuItems` and `getNodeMenuItems` methods to
the `ComfyExtension` interface, enabling extensions to provide context
menu items for canvas and node right-click menus (`src/types/comfy.ts`).
* Updated type imports to support the new API, including
`IContextMenuValue`, `LGraphCanvas`, and `LGraphNode`
(`src/types/comfy.ts`, `src/scripts/app.ts`).
[[1]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66L1-R5)
[[2]](diffhunk://#diff-bde0dce9fe2403685d27b0e94a938c3d72824d02d01d1fd6167a0dddc6e585ddR10)
**Core Implementation:**
* Implemented `collectCanvasMenuItems` and `collectNodeMenuItems`
methods in the `ComfyApp` class to gather menu items from all
extensions, with robust error handling and logging for extension
failures (`src/scripts/app.ts`).
**Testing:**
* Added a comprehensive test suite for the new context menu extension
API, covering aggregation logic, error handling, and integration
scenarios (`tests-ui/tests/extensions/contextMenuExtension.test.ts`).
This is PR 1 of the 3 PRs in the Contextmenu standardizations.
-https://github.com/Comfy-Org/ComfyUI_frontend/pull/5992
-https://github.com/Comfy-Org/ComfyUI_frontend/pull/5993
## Problem
The `update-playwright-expectations.yaml` workflow was failing with:
```
error: argument --front-end-root: The path '../dist' does not exist.
```
This was happening because the workflow was trying to launch the ComfyUI
server with `--front-end-root ../dist` before building the frontend.
## Root Cause
The workflow was missing the frontend build step entirely. It went
directly from checkout → setup server with `launch_server: true` → run
tests, skipping the crucial frontend build.
## Solution
1. Remove `launch_server: true` from `setup-comfyui-server` action call
2. Add `setup-frontend` action with `include_build_step: true` to build
the frontend
3. Add separate "Launch ComfyUI Server" step that runs AFTER frontend is
built
This ensures the `dist/` directory exists before the server tries to use
it.
## Testing
This fixes errors seen on PR #5863 and any PR using the
`/update-playwright` comment trigger.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6005-bugfix-Fix-update-playwright-expectations-workflow-missing-frontend-build-2876d73d36508182bb1af1123f3b2a87)
by [Unito](https://www.unito.io)