Phase 2 of ComfyPage refactor:
- Add ToastHelper, DragDropHelper, CommandHelper instances to ComfyPage
- Remove migrated methods from ComfyPage (command, toast, dragDrop, workflow, canvasOps, nodeOps methods)
- Update all test files to use new helper paths
- Keep visibleToasts as passthrough getter for backward compatibility
Amp-Thread-ID: https://ampcode.com/threads/T-019c1666-ed27-773c-976a-07cc805d7a6c
Co-authored-by: Amp <amp@ampcode.com>
- Add inheritAttrs: false and v-bind="$attrs" to TreeExplorer.vue
- Add #folder slot with NodeTreeFolder in NodeLibrarySidebarTab.vue
- Fixes 92 failing tests that couldn't find node-library-tree or folder elements
Amp-Thread-ID: https://ampcode.com/threads/T-019c1636-dca2-77ad-85ad-bcc53aa73e94
Co-authored-by: Amp <amp@ampcode.com>
- nodeSearchBox.spec.ts: Use NodeReference.getOutput() for CLIP output slot position
- rightClickMenu.spec.ts: Use NodeReference.getTitlePosition() for node dragging
- Add getTitlePosition() helper to NodeReference for drag operations
Amp-Thread-ID: https://ampcode.com/threads/T-019c1367-6a59-7752-9329-17cbf152fc62
Co-authored-by: Amp <amp@ampcode.com>
Migrate all test callers to use helper classes directly:
- nodeOps: getGraphNodesCount, getNodeRefById, selectNodes, etc.
- canvasOps: dragAndDrop, zoom, pan, resetView, etc.
- subgraph: rightClickInputSlot, connectToOutput, etc.
Reduces ComfyPage.ts from 1335 to 1085 lines (-250 lines).
Amp-Thread-ID: https://ampcode.com/threads/T-019c1318-58b7-757f-801c-5b676f0d3421
Co-authored-by: Amp <amp@ampcode.com>
- Create browser_tests/fixtures/constants/defaultGraphPositions.ts with all hardcoded default graph positions
- Create browser_tests/fixtures/constants/types.ts with Position and Size interfaces
- Update ComfyPage.ts to import and use DefaultGraphPositions
- Reduces duplication and prepares for further modular extraction
Amp-Thread-ID: https://ampcode.com/threads/T-019c1201-adec-7363-96d5-446d0a98e4cf
Co-authored-by: Amp <amp@ampcode.com>
Per Playwright best practices, assertions belong in tests, not fixtures.
Fixture methods should use waitFor() for preconditions instead.
- vueNodeFixtures.ts: setTitle(), cancelTitleEdit() use waitFor()
- Topbar.ts: closeTopbarMenu() uses waitFor({ state: 'hidden' })
- ComfyPage.ts: ConfirmDialog.click(), closeDialog(), clickDialogButton(),
closeToasts() all converted to waitFor()
- templates.ts: renamed waitForMinimumCardCount to expectMinimumCardCount
to clarify it contains an assertion (uses expect().toPass() pattern)
Amp-Thread-ID: https://ampcode.com/threads/T-019c11f8-acd2-7429-8bec-525ad47a47c4
Co-authored-by: Amp <amp@ampcode.com>
- widget.spec.ts: use expect().toPass() for combo refresh
- subgraph.spec.ts: use comfyPage.setup() after reload
- nodeLibrary.spec.ts: use toHaveCount() for debounced search
- remoteWidgets.spec.ts: use expect().toPass() for backoff test
- litegraphUtils.ts: refactor navigateIntoSubgraph to use toPass()
Amp-Thread-ID: https://ampcode.com/threads/T-019c11f1-e839-772c-8890-b77cfdf70124
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Disables the legacy "node templates" feature on ComfyUI Cloud. This
feature is accessed via right-clicking on the canvas and is distinct
from both subgraphs and workflow templates.
## Problem
The legacy node templates feature presents false-positive errors on
cloud:
- Errors appear when no existing templates exist initially
- After refresh, workflow progress is lost from workflow tabs
- Node templates fail to delete (except the first one)
## Solution
Conditionally load the `nodeTemplates` extension only for non-cloud
builds by wrapping the import in `if (!isCloud)`.
## Testing
- [ ] Verify right-click canvas menu no longer shows "Node Templates"
submenu on cloud
- [ ] Verify right-click canvas menu no longer shows "Save Selected as
Template" on cloud
- [ ] Verify the feature still works on desktop/OSS builds
## Related
- Refs: COM-14105
- Related issue: #4056 (Migrate Node Templates to Workflows)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8462-fix-cloud-disable-legacy-node-templates-feature-on-cloud-2f86d73d36508162948cc897d4c7ef5e)
by [Unito](https://www.unito.io)
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Fix for COM-14110: Preview image does not display new outputs in
vue-nodes.
## Problem
The merge logic in `setOutputsByLocatorId` updated `app.nodeOutputs` but
returned early without updating the reactive `nodeOutputs.value` ref.
This caused Vue components to never receive merged output updates
because only the non-reactive `app.nodeOutputs` was being updated.
## Solution
Added `nodeOutputs.value[nodeLocatorId] = existingOutput` after the
merge loop, before the return statement.
## Testing
- Added 2 unit tests covering the merge behavior
- All 4076 existing unit tests pass
- Typechecks pass
- Lint passes
## Notes
- Related open PRs touching same files: #8143, #8366 - potential minor
conflicts possible
Fixes COM-14110
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8479-fix-update-reactive-ref-after-merge-in-imagePreviewStore-2f86d73d365081f1a145fa5a9782515f)
by [Unito](https://www.unito.io)
Co-authored-by: Amp <amp@ampcode.com>
## Summary
This PR removes unsafe type assertions ("as unknown as Type") from test
files and improves type safety across the codebase.
### Key Changes
#### Type Safety Improvements
- Removed improper `as unknown as Type` patterns from test files in
Group 8 part 8
- Replaced with proper TypeScript patterns using Pinia store testing
patterns
- Fixed parameter shadowing issue in typeGuardUtil.test.ts (constructor
→ nodeConstructor)
- Fixed stale mock values in useConflictDetection.test.ts using getter
functions
- Refactored useManagerState tests to follow proper Pinia store testing
patterns with createTestingPinia
### Files Changed
Test files (Group 8 part 8 - utils and manager composables):
- src/utils/typeGuardUtil.test.ts - Fixed parameter shadowing
- src/utils/graphTraversalUtil.test.ts - Removed unsafe type assertions
- src/utils/litegraphUtil.test.ts - Improved type handling
- src/workbench/extensions/manager/composables/useManagerState.test.ts -
Complete rewrite using Pinia testing patterns
-
src/workbench/extensions/manager/composables/useConflictDetection.test.ts
- Fixed stale mock values with getters
- src/workbench/extensions/manager/composables/useManagerQueue.test.ts -
Type safety improvements
-
src/workbench/extensions/manager/composables/nodePack/useMissingNodes.test.ts
- Removed unsafe casts
-
src/workbench/extensions/manager/composables/nodePack/usePacksSelection.test.ts
- Type improvements
-
src/workbench/extensions/manager/composables/nodePack/usePacksStatus.test.ts
- Type improvements
- src/workbench/extensions/manager/utils/versionUtil.test.ts - Type
safety fixes
Source files (minor type fixes):
- src/utils/fuseUtil.ts - Type improvements
- src/utils/linkFixer.ts - Type safety fixes
- src/utils/syncUtil.ts - Type improvements
-
src/workbench/extensions/manager/composables/nodePack/useWorkflowPacks.ts
- Type fix
-
src/workbench/extensions/manager/composables/useConflictAcknowledgment.ts
- Type fix
### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- All affected test files pass (`pnpm test:unit`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)
Part of the "Road to No Explicit Any" initiative, cleaning up type
casting issues from branch `fix/remove-any-types-part8`.
### Previous PRs in this series:
- Part 2: #7401
- Part 3: #7935
- Part 4: #7970
- Part 5: #8064
- Part 6: #8083
- Part 7: #8092
- Part 8 Group 1: #8253
- Part 8 Group 2: #8258
- Part 8 Group 3: #8304
- Part 8 Group 4: #8314
- Part 8 Group 5: #8329
- Part 8 Group 6: #8344
- Part 8 Group 7: #8459
- Part 8 Group 8: #8496 (this PR)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8496-Road-to-No-explicit-any-Group-8-part-8-test-files-2f86d73d365081f3afdcf8d01fba81e1)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
When bypassing or muting a subgraph the contents are no longer bypassed
along with it. This behaviour was less than ideal because it meant that
toggling the bypass of a single subgraph node twice could change the
behaviour of the node.
It is entirely intended that a subgraph node which is bypassed does not
have it's children execute. As part of testing this behaviour, it was
found that nodes inside of a bypassed subgraph are still considered for
execution even if boundry links are treated as disconnected. The
following example would execute even if the subgraph is muted.
<img width="826" height="476" alt="image"
src="https://github.com/user-attachments/assets/7b282873-e114-494d-b8f1-74c373859151"
/>
To resolve this, the PR does not add the contents of a subgraphNode
which is muted or bypassed to the execution map.
Resolves#8489
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8494-Don-t-bypass-subgraph-contents-with-subgraph-2f86d73d365081aeba8dd2990b6ba0ad)
by [Unito](https://www.unito.io)
## Summary
Add `PR_GH_TOKEN` to ci-lint-format and i18n-update-core workflows to
ensure commits trigger e2e test runs.
## Changes
- Add `token: ${{ secrets.PR_GH_TOKEN }}` to checkout step in
`.github/workflows/ci-lint-format.yaml`
- Add `token: ${{ secrets.PR_GH_TOKEN }}` to checkout step in
`.github/workflows/i18n-update-core.yaml`
## Context
This matches the pattern used consistently across all other workflows in
the repository (release-version-bump, version-bump-desktop-ui,
api-update-registry-api-types, i18n-update-nodes, etc.).
Without this token, commits made by these workflows don't trigger
downstream e2e tests, which can lead to issues being missed.
## Original Work
Original idea & implementation by @drjkl in commits:
- be3a8d61c - fix: use GitHub App token for lint/i18n workflows to
trigger e2e tests
- 50ccb254b - Add github app token action to pinact
This PR simplifies the approach to use the existing `PR_GH_TOKEN` secret
instead of GitHub App tokens, for consistency with the rest of the
repository.
## Test Plan
- [x] Verify workflows can checkout code successfully
- [x] Confirm commits from these workflows trigger e2e test runs
## It works!
<img width="1075" height="402" alt="image"
src="https://github.com/user-attachments/assets/31762d68-a37f-4926-b463-84d184d4309d"
/>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Fixes a bug where socket map data was not properly removed when sockets
are dynamically added/removed via DynamicCombo widgets in Vue mode
(Nodes 2.0).
## Problem
When DynamicCombo widgets (e.g., `should_remesh` on Meshy nodes) change
their selection, inputs are dynamically added/removed. The Vue `v-for`
loop in `NodeSlots.vue` was using array index as the `:key`, causing Vue
to **reuse** slot components instead of properly unmounting them.
This led to:
- Socket map entries leaking (never cleaned up)
- Socket positions becoming desynced
- Stale cached offset data
## Solution
1. **Use slot `name` as Vue key** instead of array index in
`NodeSlots.vue`
- Slot names are unique per node (enforced by ComfyUI backend)
- When a slot is removed, Vue sees the key disappear and properly
unmounts the component
- `onUnmounted` cleanup in `useSlotElementTracking` now runs correctly
2. **Add defensive cleanup** in `useSlotElementTracking.ts`
- Before registering a new slot, check if a stale entry exists with the
same key
- Clean up stale entry to handle any edge cases
## Related
- Fixes COM-12970
- Related to #7837 (fixed LiteGraph version of this bug, but not Vue
mode)
## Testing
- Quality checks pass (typecheck, lint, format)
- Manual testing with DynamicCombo nodes (Meshy, nodes_logic)
recommended
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8469-fix-Vue-mode-socket-map-data-not-cleaned-up-on-dynamic-input-changes-2f86d73d365081e599eadca4f15e6b6e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Improve Template search input performance issue #8134
This was caused by the search logic running too frequently (throttled at
50ms), causing the main thread to block on every few keystrokes.
## Changes
Use debouncing that wait until you stop typing for a specific time
(300ms) before running.
It makes the searching function more smoothly.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8343-Improve-template-search-input-performance-issue-2f56d73d36508144bdf9fa5e0cd76818)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Migrate keybindings domain to `src/platform/keybindings/` following DDD
principles.
## Changes
- **What**: Consolidate keybinding-related code (types, store, service,
defaults, reserved keys) into a single domain module with flat structure
- Extracted `KeyComboImpl` and `KeybindingImpl` classes into separate
files
- Updated all consumers to import from new location
- Colocated tests with source files
- Updated stores/README.md and services/README.md to remove migrated
entries
## Review Focus
- Verify all import paths were updated correctly
- Check that the flat structure is appropriate (vs nested core/data/ui
layers)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8369-refactor-migrate-keybindings-to-DDD-structure-2f66d73d36508120b169dc737075fb45)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>