mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-11 01:28:03 +00:00
bdf47aebca4fa187f5bdaf2a9c90a0b3a7e2ae1f
34 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b4ae6344d7 |
Brand local node IDs (#13085)
## Summary Adds a branded local `NodeId` helper and starts separating local node identity from serialized workflow IDs. ## Changes - **What**: Adds central `NodeId` parsing/branding helpers, migrates nearby widget identity types, keeps queue results at the serialized boundary, and removes misleading workflow `NodeId` usage from execution error maps. ## Review Focus Check that the first migration slice keeps serialized/API IDs as raw `number | string` while local UI/store IDs use the branded string type. ## Caveat `SUBGRAPH_INPUT_ID` and `SUBGRAPH_OUTPUT_ID` are now branded local `NodeId` string values internally instead of numeric sentinels. Reviewers should double-check extension compatibility for callers that import `Constants` and compare those values numerically. ## Screenshots (if applicable) N/A --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: AustinMroz <austin@comfy.org> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
caabebe145 |
Redesign missing model detection contract for promoted subgraph widgets (#13059)
## Summary Redesign missing-model detection for ADR 0009 promoted subgraph widgets so candidates are created from the widget value the user can actually edit, while still using the concrete interior widget as the schema/options source. ## Why This PR Exists This PR comes from the follow-up missing-model detection work for the ADR 0009 / 1.46 subgraph widget changes introduced by [#12197](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12197). [#12197](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12197) intentionally changed promoted subgraph widgets to be represented through subgraph input links. After that change, the promoted widget on the host `SubgraphNode` is the editable value owner, and linked interior widgets are no longer guaranteed to mirror that value. The old missing-model contract still treated the concrete interior node widget as the effective source of truth in subgraph cases: - recursive scans entered the subgraph and scanned the interior widget value; - candidates were keyed by the interior node/widget identity; - the parent subgraph host mostly received propagated highlight/navigation behavior; - subgraph container widgets were not treated as first-class candidate sources. That contract breaks after ADR 0009. A user can resolve a missing model by changing the promoted host widget to an installed model, while the linked interior widget can still hold the old stale value. If detection keeps scanning the linked interior value, entering the subgraph or reloading the workflow can re-create a false missing-model error that no longer corresponds to the value the user can edit. ADR 0009 also means the same subgraph definition can be reused by multiple `SubgraphNode` hosts. Once missing-model detection moves from the interior definition widget to the promoted host widget, the selected value is no longer a property of the shared definition alone. It is a property of a specific host instance. That makes the old interior-node identity insufficient for mode changes, removal handling, and re-scan behavior: a single interior leaf definition can be reachable through multiple host execution paths, and only the affected host path should add, remove, or restore a candidate. This PR also builds on [#12990](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12990), which narrowed workflow-level `models[]` and embedded model data to metadata enrichment only. Together, the intended boundary is: - live widgets create missing-model candidates; - workflow/root-level `models[]` and node metadata only enrich candidates that already came from a live widget; - promoted widget values are read from the editable host widget, not inferred from stale interior `widgets_values`. ## Changes - **What**: - Introduces promoted-widget scan targets that split: - host promoted widget value and candidate identity; - concrete leaf widget/node definition data. - Scans the outermost unlinked promoted widget on a `SubgraphNode` host as the selected value owner. - Skips linked interior widgets as candidate sources, preventing stale linked widget values from producing duplicate or false missing-model candidates. - Resolves the concrete leaf widget for combo options, asset-widget support, node type, directory lookup, and embedded metadata enrichment. - Keys promoted missing-model candidates by the host execution id and host promoted widget name. - Adds `sourceExecutionId` to promoted candidates so liveness still follows the concrete source execution path, including nested inactive subgraph containers. - Uses source-scope activity for pipeline filtering and async verification, while keeping highlight/store/clearing identity host-keyed. - Removes host-keyed promoted candidates when their source execution scope is removed or bypassed. - Re-scans ancestor subgraph hosts when an interior source path is un-bypassed, so host-keyed promoted errors can reappear correctly. - Handles shared subgraph definitions by deriving promoted source paths from the concrete host instance path, rather than treating the shared definition node id as globally unique. - Shares promoted source resolution between Vue node processing and the right-side panel to avoid drift. - Aligns missing-model clearing across Vue node widgets, legacy canvas widgets, and right-side panel Parameters/Nodes section widgets. - Adds unit coverage for scan identity, source-scope liveness, dynamic mode changes, source-scope removal, shared-definition host isolation, and right-side panel clearing. - Adds nested promoted-widget E2E coverage for OSS and Cloud flows across Vue, Parameters tab, and legacy widget surfaces. - **Breaking**: None expected. - **Dependencies**: None. ## New Detection Contract A missing-model candidate is created from an unlinked final editable value owner. That value owner can be: - a normal node model widget; or - the outermost promoted model widget displayed on a `SubgraphNode` host. For promoted widgets: - the host promoted widget supplies the selected value; - the host execution id and host widget name are the candidate identity; - the concrete leaf widget supplies definition data such as combo options and asset-browser support; - the concrete source execution path is retained as `sourceExecutionId` for liveness only; - linked interior widgets are skipped as candidate sources because their values are not authoritative when driven by a promoted input. For a nested chain: `Outer promoted widget A -> inner promoted widget B -> concrete widget C` only `A` creates the candidate. `B` and `C` are linked along the promoted-input path and are skipped as selected-value sources, while `C` still provides the concrete widget definition used to evaluate `A`. ## Shared Definition And Source-Scope Liveness ADR 0009 promoted widgets make subgraphs behave more like reusable definitions with host-owned inputs. Two host `SubgraphNode`s can point at the same interior subgraph definition while carrying different promoted widget values. In that shape, the missing-model candidate must be keyed to the editable host surface, but the activity check cannot use the host id alone. For example, if two outer hosts share the same nested subgraph definition, one host can select a valid model while the other still selects a missing model. The result should be one missing-model reference, not a single definition-level error and not two errors after one host is fixed. Likewise, bypassing or un-bypassing an interior nested container should affect only the host execution paths that actually pass through that container. This PR therefore separates two concepts: - **candidate identity**: host execution id + host promoted widget name, used for storage, highlight, navigation, and clearing; - **candidate liveness**: concrete source execution path, used for scan-time activity checks, pipeline filtering, async verification, source-scope removal, and re-exposure after mode changes. That separation is the reason this PR updates more than the scan itself. Moving the detection target to the subgraph host also requires the mode-change and removal paths to understand that a host-keyed candidate can be invalidated by a descendant source path, and can need to be restored by re-scanning an ancestor host when an interior source path becomes active again. ## Review Focus Please review the identity split carefully: - candidate/store/highlight/clearing identity should remain host-keyed for promoted widgets; - liveness should use `sourceExecutionId` when present, falling back to `nodeId` for normal candidates; - scan-time activity checks should account for the source node itself and all ancestor subgraph containers; - source-scope removal should remove host-keyed candidates whose concrete source path was removed or bypassed; - un-bypassing an interior source path should re-scan affected ancestor subgraph hosts so host-keyed candidates can reappear; - shared subgraph definitions should not merge errors across different host instances; - linked interior widgets should not produce their own missing-model candidates; - asset-browser eligibility should be resolved from the concrete leaf node type and widget name, not the synthetic subgraph host type; - right-side panel edits should clear host missing-model errors and source validation errors consistently. The E2E matrix intentionally keeps nested promoted workflows only. Nested promoted widgets cover the same editable host path as single promoted widgets while also exercising the `A -> B -> C` chain that can break source-scope liveness and re-scan behavior. The nested fixture also includes multiple host instances that share the same subgraph definition, so it verifies that fixing one host does not accidentally clear or suppress another host's missing-model candidate. Direct/single promoted behavior is still covered at the unit level. ## Non-Goals - This PR does not reintroduce workflow-level `models[]` candidate creation. - This PR does not infer selected model values from `widgets_values`. - This PR does not synchronize linked interior widget values back from promoted host widgets. - This PR does not redesign missing-media scanning; missing media still skips subgraph containers and remains keyed by concrete interior paths. The shared async post-verification active-scope filter is intentionally stricter, so a pending missing-media candidate is no longer surfaced if its own node is bypassed or removed while verification is in flight. ## Validation - `pnpm exec vitest run src/components/rightSidePanel/parameters/SectionWidgets.test.ts src/platform/missingModel/missingModelScan.test.ts src/composables/graph/useErrorClearingHooks.test.ts src/platform/missingModel/missingModelPipeline.test.ts src/platform/missingModel/missingModelStore.test.ts src/utils/graphTraversalUtil.test.ts src/composables/graph/useGraphNodeManager.test.ts src/renderer/extensions/vueNodes/composables/useProcessedWidgets.test.ts --reporter=dot` - 8 files passed, 294 tests passed. - `pnpm exec vitest run src/platform/missingModel/missingModelScan.test.ts src/core/graph/subgraph/resolveConcretePromotedWidget.test.ts src/components/rightSidePanel/parameters/SectionWidgets.test.ts` - 3 files passed, 71 tests passed. - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm format:check` - targeted ESLint for changed production/unit/E2E files - `git diff --check` - `pnpm build` - `pnpm build:cloud` - OSS affected E2E on the 8188 build: - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabModeAware.spec.ts --project=chromium --grep "Changing an OSS .*promoted|Refreshing a resolved promoted|Reloading a resolved nested"` - 5 passed. - Cloud affected E2E on the 8188 cloud build: - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --project=cloud --grep "Changing a Cloud .*promoted"` - 2 passed; the Cloud legacy promoted asset-modal case still fails until [#13075](https://github.com/Comfy-Org/ComfyUI_frontend/pull/13075) is merged. - Full OSS `errorsTabModeAware.spec.ts` on the 8188 build: - 23 passed; 3 existing paste/clipboard cases failed before the promoted subgraph section with node count remaining at 1 after `clipboard.paste()`. - Commit hooks ran `oxfmt`, `oxlint`, `eslint`, `pnpm typecheck`, and browser typecheck where applicable. - Pre-push hook ran `pnpm knip --cache`. ## Screenshots Before https://github.com/user-attachments/assets/6380c1da-1d92-4b70-888e-3ade572c4b5b After https://github.com/user-attachments/assets/4cfc24d6-3dc3-4e36-9b31-72fea6b3d9d5 |
||
|
|
8d82944441 |
fix: limit workflow models to metadata enrichment (#12990)
## Summary This PR intentionally narrows workflow-embedded model metadata handling so root-level `models[]` and node-level embedded model metadata can enrich existing missing-model candidates, but can no longer create new candidates by themselves. ## Why this PR exists ADR 0009, **Subgraph promoted widgets use linked inputs**, changes promoted value ownership for subgraphs. That design was implemented by [#12197](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12197), **Subgraph Link Only Promotion (ADR 0009)**. Under ADR 0009, a promoted widget is represented as a standard linked `SubgraphInput` on the host `SubgraphNode`. The host boundary owns the promoted value identity through the host node locator plus `SubgraphInput.name`. The interior source widget remains the provider of schema, type, options, tooltip, defaults, diagnostics, and migration metadata, but it is not the persistence owner of the promoted value. This PR is a preparatory cleanup discovered while working on the missing model detection follow-up required by that ADR 0009 / [#12197](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12197) behavior. The follow-up needs missing model detection to respect the new subgraph promoted-widget ownership model. While reviewing that path, we found that the existing embedded model metadata fallback in `enrichWithEmbeddedMetadata` was doing more than metadata enrichment. The important finding was that this fallback was not just attaching metadata to candidates that had already been detected from live node widgets. It could also synthesize brand-new `MissingModelCandidate` entries from workflow JSON metadata, including root-level `models[]` entries, when no live candidate existed. That behavior is inaccurate for the missing model system for two reasons. First, the normal missing model lifecycle is anchored to a real node/widget binding. A candidate found from a COMBO or asset widget has a concrete `nodeId + widgetName` reference. That reference is what lets the UI surface the error, cache it as a pending warning, and later clear or resolve it when the underlying node/widget value is fixed. A root-level `models[]` entry does not reliably provide that anchor. If metadata-only fallback creates a candidate without a real live widget reference, the resulting error can be detected but cannot reliably travel through the existing clearing path. In practice, that can become an effectively unremovable missing model warning unless the user downloads exactly the same model referenced by the stale metadata. Second, a missing model error is meant to mean that a model-selecting widget on an active node references a value that is not available. Workflow JSON metadata by itself is not the same source of truth. If a model only appears in root workflow metadata, or appears in node metadata that is not represented by an active COMBO or asset widget candidate, that is a different kind of state from the existing missing model error model. Treating that metadata as a candidate creates a second, less reliable detector that is not aligned with the scan/clear lifecycle. This is especially important before the ADR 0009 missing-model follow-up. With linked-input promoted widgets, the host promoted value is the value that matters. The interior source widget may still carry stale or default metadata, and it must not become a second source of truth for missing model errors. A detection path that can create candidates directly from workflow metadata would make it harder to reason about which value actually produced the warning. For those reasons, this PR removes metadata-only candidate synthesis and keeps embedded metadata in the role it can perform safely: metadata enrichment. If the live widget/asset scan produces a candidate, embedded metadata may fill in `directory`, `url`, `hash`, and `hashType`. If no live candidate exists, the metadata is not enough to create a missing model warning. This PR is intended to land before the child PR that updates runtime missing model detection for ADR 0009 linked-input promoted widgets. ## Changes - **What**: Restrict `enrichWithEmbeddedMetadata` to enriching existing candidates instead of creating fallback candidates from unmatched root `models[]` or embedded model metadata. - **What**: Remove the now-unused installed-model check callback and asset-support callback from `enrichWithEmbeddedMetadata`. - **What**: Remove the now-unnecessary `modelStore.loadModelFolders()` path from the missing model pipeline, since embedded metadata no longer performs installed-model fallback detection. - **What**: Remove dead source-tracking metadata (`EmbeddedModelWithSource`, source node/widget fields, and widget-name lookup) that only existed to support metadata-only synthesis. - **What**: Update missing model tests so they assert the new contract: metadata enriches live candidates, but does not create candidates without a live scan result. - **What**: Delete obsolete fixtures that only covered the removed metadata-only synthesis path. - **Breaking**: None expected. This is an intentional narrowing of an inaccurate fallback detector, not a public API change. - **Dependencies**: None. ## Review Focus Please focus on whether the candidate lifecycle now has a single source of truth: live COMBO/asset widget scanning creates candidates, while workflow metadata only enriches those candidates. The intended behavioral change is that a model present only in workflow-level metadata, with no active node widget candidate referencing it, no longer appears as a missing model. This avoids surfacing warnings that cannot be cleared through the normal `nodeId + widgetName` path. The expected retained behavior is that active widget-referenced missing models are still detected by `scanAllModelCandidates`, and metadata from root `models[]` or node `properties.models` still supplies download-related fields for those live candidates. ## Screenshots (if applicable) Not applicable. This is a detection/pipeline behavior change covered by unit tests. ## Validation - `pnpm test:unit src/platform/missingModel/missingModelScan.test.ts src/platform/missingModel/missingModelPipeline.test.ts` - `pnpm exec eslint src/platform/missingModel/missingModelScan.ts src/platform/missingModel/missingModelScan.test.ts src/platform/missingModel/missingModelPipeline.ts src/platform/missingModel/missingModelPipeline.test.ts src/platform/missingModel/types.ts` - `pnpm exec oxfmt --check src/platform/missingModel/missingModelScan.ts src/platform/missingModel/missingModelScan.test.ts src/platform/missingModel/missingModelPipeline.ts src/platform/missingModel/missingModelPipeline.test.ts src/platform/missingModel/types.ts` - `pnpm typecheck` - pre-push hook: `knip --cache` |
||
|
|
4c870d84ed |
Fix disabling of linked widgets in props panel (#12896)
| Before | After | | ------ | ----- | | <img width="360" alt="before" src="https://github.com/user-attachments/assets/9d602ee3-ff10-48b9-95ca-4c7f5ca57a45" /> | <img width="360" alt="after" src="https://github.com/user-attachments/assets/36e96aff-60ec-4f8c-b7c9-b4d68e03884c" />| Making reactivity function is a little bit clunkier than I would like, but it'll get simplified in the future by east coast swing |
||
|
|
6d43320b93 |
Simplify missing model error presentation (#12793)
## Summary Simplifies the Missing Models error card as the fifth slice of the catalog-driven error-tab redesign. This PR is intentionally larger than the previous slices because Missing Models is the only remaining error type where the card UI, OSS download flow, Cloud import flow, and shared model-import dialog all have to move together to preserve the resolution path. The high-level goal is to make Missing Models behave like the other simplified error cards: show the exact missing item, show the affected nodes, keep locate actions predictable, and only expose actions that can actually resolve the problem. This follows the staged error-tab cleanup plan: 1. #12683 refined validation, runtime, and prompt error presentation. 2. #12705 simplified missing media error presentation. 3. #12735 simplified missing node pack error presentation. 4. #12768 simplified swap node error presentation. 5. This PR simplifies missing model presentation and the model-import handoff. ## Why This PR Is Larger Missing Models has more resolution paths than the previous error groups: - OSS can refresh model state, download individual models, and download all available models. - Cloud cannot download directly from the panel; it resolves supported rows through the model import dialog. - Some Cloud rows cannot be resolved through import at all because the node/widget cannot consume imported model assets. - Importing from Cloud needs to know the originating missing-model row so it can lock the expected model type and apply the imported model back to the affected widgets. - Already-imported files can still be unusable if they were imported under a different model type than the missing node expects. Because of those constraints, splitting the card layout from the dialog handoff would leave either a misleading Import button or an import dialog that does not know what it is resolving. This PR keeps that behavior in one reviewable unit. ## User-Facing Behavior ### Shared Missing Models Card - Replaces the older grouped presentation with compact model rows. - Shows each missing model as the primary row label. - Shows model metadata as a smaller sublabel instead of using large section headers. - Keeps locate-node controls visually consistent with the other simplified error cards. - Keeps rows expandable when multiple nodes reference the same missing model. - Shows the affected node rows under expanded models. - Allows single-reference rows to locate the affected node without rendering an extra duplicate child row. - Keeps unknown rows visible, including their affected nodes, instead of silently hiding them. - Removes the old library-select UI from the Missing Models card. ### OSS Behavior - Keeps the refresh action available from the Missing Models group header. - Keeps individual Download actions for downloadable models. - Moves file size out of the Download button label and into the row sublabel. - Keeps Download all when multiple downloadable models are available. - Places Download all at the bottom of the card rather than competing with the group header. - Leaves rows without a download URL as non-downloadable instead of rendering a broken action. ### Cloud Behavior - Shows Import only for missing models that can be resolved by importing a model asset of the required type. - Separates models that cannot be resolved through Cloud import into an Import Not Supported section. - Gives unsupported rows a direct explanation: nodes referencing those models do not support imported models, so users need to open the node and choose a supported built-in model or replace the node with a supported loader. - Treats unknown model type/directory as unsupported for Cloud import, because the import dialog cannot lock a valid model type and the node cannot safely consume the imported asset. - Keeps affected nodes visible in the unsupported section so users still have a path to locate and replace the node manually. ## Cloud Import Dialog Changes The shared model import dialog now accepts missing-model context when opened from the Missing Models card. When that context is present: - The dialog shows which missing model will be replaced. - The dialog lists the affected node/widget references that will be updated. - The model type selector is locked to the required model directory/type. - The Back/import-another path is disabled when it would break the targeted missing-model flow. - Import progress can be associated with the originating missing-model row. - After import completion, matching missing-model references are applied automatically where possible. - If the selected file is already imported under an incompatible model type, the dialog shows a targeted failure state explaining why this import cannot resolve the missing model. This keeps the generic import dialog reusable while adding only the context-specific behavior needed for Missing Models. ## Implementation Notes - `MissingModelCard.vue` owns the card-level grouping and OSS/Cloud section decisions. - `MissingModelRow.vue` owns per-model row rendering, expansion, locate actions, import/download actions, and row-level progress states. - `useMissingModelInteractions.ts` remains the interaction layer for locating nodes and applying resolved model selections. - `UploadModelDialog.vue`, `UploadModelConfirmation.vue`, `UploadModelFooter.vue`, `UploadModelProgress.vue`, and `useUploadModelWizard.ts` receive the missing-model context needed by the Cloud import handoff. - `MissingModelLibrarySelect.vue` is removed because the simplified card no longer exposes that inline selection path. - Locale and selector changes are limited to the new simplified row/section states and removed unused Missing Models strings. ## Tests Added / Updated - Unit coverage for Missing Models card grouping and row states. - Unit coverage for importable vs unsupported Cloud rows. - Unit coverage for model row expansion, locate actions, progress display, and action availability. - Unit coverage for upload confirmation/footer/progress behavior when a missing-model context is present. - Unit coverage for incompatible already-imported model handling. - E2E coverage for OSS Missing Models presentation. - E2E coverage for mode-aware Missing Models interactions. - Cloud E2E coverage for importable rows vs Import Not Supported rows. - Cloud E2E coverage for opening the import dialog with missing-model replacement context. ## Review Focus - Cloud import eligibility: unsupported or unknown model rows should not expose Import as if the row can be resolved automatically. - Missing-model context in the import dialog: the required model type should be locked, and the affected node/widget references should be clear. - OSS parity: OSS should keep refresh, individual Download, and Download all while visually matching the simplified Cloud card where possible. - Narrow side panel behavior: row labels may wrap, but link, primary action, and locate controls should not overlap. - Scope boundaries: this PR intentionally does not redesign Missing Node Pack / Swap Node / Missing Media again; visual parity issues shared across those cards can be handled in a follow-up unification pass if needed. ## Validation - `pnpm format` - `pnpm lint` - `pnpm typecheck` - Related unit tests: 8 files / 84 tests passed - `pnpm build` - OSS Missing Models E2E: `errorsTabMissingModels.spec.ts` passed, 8/8 - Mode-aware Missing Models E2E subset passed, 11/11, excluding unrelated local paste clipboard cases - `pnpm build:cloud` - Cloud Missing Models E2E: `errorsTabCloudMissingModels.spec.ts` passed, 3/3 - Final Claude review: no Blocker or Major findings ## Breaking / Dependencies - Breaking: none. - Dependencies: none. ## Screenshots OSS <img width="575" height="393" alt="스크린샷 2026-06-12 오전 12 25 27" src="https://github.com/user-attachments/assets/f5c44f95-711a-4d3d-99bd-f39ac2bb2012" /> <img width="659" height="351" alt="스크린샷 2026-06-12 오전 12 24 37" src="https://github.com/user-attachments/assets/4bb65a47-c1aa-408b-836b-a1998412f815" /> Cloud <img width="688" height="357" alt="스크린샷 2026-06-12 오전 12 23 59" src="https://github.com/user-attachments/assets/9330a7e7-9f22-420f-82b3-dde0fb2b3dd1" /> <img width="531" height="437" alt="스크린샷 2026-06-12 오전 12 21 13" src="https://github.com/user-attachments/assets/734bd911-f6f7-4872-8868-bb927ddeedd8" /> New import model flow https://github.com/user-attachments/assets/c094c670-62b9-47ce-bfe1-2d09f4f7359d |
||
|
|
1b14f4df8a |
Simplify missing node pack error presentation (#12735)
## Summary Simplify the Missing Node Packs error card so it follows the new error-tab item-row direction, with clearer pack rows, predictable locate behavior, and focused E2E coverage. This is the third PR in the staged error-tab simplification plan: 1. Merged: execution/prompt/validation error presentation and catalog grouping in #12683. 2. Merged: missing media presentation simplification in #12705. 3. This PR: missing node pack presentation simplification. 4. Planned next: swap-node presentation simplification. 5. Planned later: missing model presentation and action-flow simplification. ## Changes - **What**: Refactors Missing Node Packs rows so pack-level and node-level actions are easier to scan and more consistent with the rest of the refreshed Errors tab. - **What**: Removes the node-id badge from missing node pack rows, matching the simplified item-row direction. - **What**: Makes a single-node known pack row directly locatable from the pack label, rather than rendering an extra child row. - **What**: Keeps multi-node packs collapsed by default, with both the chevron and pack title toggling the child node list. - **What**: Keeps unknown packs expanded by default, including the single-node unknown-pack case, so users can still see the unresolved node type immediately. - **What**: Keeps per-node child rows clickable for locate-on-canvas behavior when a pack contains multiple affected nodes. - **What**: Replaces missing-node-pack action labels with shared `g.install` and `g.search` copy and removes now-unused English locale keys. - **What**: Adds targeted Playwright coverage for the simplified missing-node-pack card, including unknown-pack default rows, row-label locate behavior, and chevron/title expansion behavior. - **Breaking**: None. - **Dependencies**: None. ## Review Focus Please focus on the missing-node-pack row behavior: - Single known pack with one affected node should stay compact and locate the node from the pack label or locate icon. - Known packs with multiple affected nodes should show a count, start collapsed, and expand/collapse from either the chevron or title. - Unknown packs should expose the affected node rows immediately, including when there is only one affected node. - Locate actions should remain attached to the affected node rows, not to the parent pack when there are multiple nodes. - The E2E fixture intentionally uses two missing nodes with the same `cnr_id` and node sizes of `[400, 200]` to follow browser-test asset guidance. ## Validation - `pnpm format:check` - `pnpm lint` - `pnpm typecheck` - `pnpm knip --cache` via pre-push hook - `pnpm test:unit src/components/rightSidePanel/errors/MissingPackGroupRow.test.ts src/components/rightSidePanel/errors/MissingNodeCard.test.ts --run` - `pnpm test:browser:local browser_tests/tests/propertiesPanel/errorsTabMissingNodes.spec.ts --project=chromium` - Pre-commit hook: staged formatting, linting, `pnpm typecheck`, and `pnpm typecheck:browser` ## Screenshots This PR <img width="531" height="598" alt="스크린샷 2026-06-10 오전 1 54 31" src="https://github.com/user-attachments/assets/9c0addeb-92d2-4cef-a4f3-35a87bbad308" /> old (Main) <img width="509" height="807" alt="스크린샷 2026-06-10 오전 1 53 51" src="https://github.com/user-attachments/assets/b8488f73-d8ed-4356-bd4c-fc678ea205f7" /> |
||
|
|
14f8fdebdd |
fix: refresh promoted combo host options after missing model reload (#12692)
## Summary Fixes a Vue node subgraph case where the missing-model refresh flow clears the missing-model error, but the promoted combo widget remains in an invalid visual state because its hosted options snapshot is stale. ## Changes - **What**: After `reloadNodeDefs()` refreshes combo option lists and extension `refreshComboInNodes` hooks run, resync hosted options snapshots for promoted combo widgets so Vue-rendered subgraph nodes see the newly available model option. - **What**: Adds a focused E2E regression for the missing-model refresh path on a subgraph with a promoted `ckpt_name` widget. - **What**: Hardens the E2E by cleaning up its `/object_info` route override and asserting the widget's `aria-invalid` state rather than a Tailwind implementation class. - **Breaking**: None. - **Dependencies**: None. ## Review Focus This PR is intentionally a minimal patch for the missing-model refresh path, not a broader subgraph architecture change. Root cause: `reloadNodeDefs()` updates the live LiteGraph combo widget options, including widgets inside subgraphs. However, Vue nodes render promoted widgets from a hosted `WidgetState.options` snapshot. When a missing model is downloaded and the missing-model refresh button reloads node definitions, the source combo receives the updated model list, but the promoted host snapshot can still contain the old option list. The missing-model error and node-level state are cleared, while the Vue combo still computes itself as invalid from stale options. The fix keeps the existing host snapshot model intact. It simply resyncs promoted combo host options after the normal combo refresh pipeline finishes. This avoids changing promoted-widget ownership, `useProcessedWidgets` merge precedence, or broader subgraph internals while addressing the reported stale invalid state. Why the helper is in `app.ts`: this sync is currently a single-call-site post-step of `reloadNodeDefs()`, and the ordering is load-bearing. It must run after the core combo refresh loop and after extension `refreshComboInNodes` hooks so it captures both built-in and extension-provided option changes. Keeping the small private helper next to the refresh orchestration makes that sequence explicit and avoids adding a new public subgraph helper or introducing a more visible dependency cycle through `promotionUtils` for a narrow patch. This is stacked on `jaeone/fe-942-bug-error-indicators-persist-after-resolving-missing-model`, so it is opened as a draft until the base PR lands. ## Test Plan - `pnpm knip` - `pnpm exec oxfmt --check src/scripts/app.ts browser_tests/tests/propertiesPanel/errorsTabModeAware.spec.ts` - `pnpm exec eslint --cache --no-warn-ignored src/scripts/app.ts browser_tests/tests/propertiesPanel/errorsTabModeAware.spec.ts` - `pnpm typecheck` - `pnpm test:browser:local --grep "Refreshing a resolved promoted missing model clears the combo invalid state"` - First local run hit a `beforeEach` timeout while the dev server was still warming custom-node/conflict-detection output. - Re-running against the warmed dev server passed. ## Screenshots (if applicable) N/A. The behavioral E2E covers the visible invalid-state regression. |
||
|
|
1b90696459 |
Simplify missing media error presentation (#12705)
## Summary This is the second PR in the planned Workflow Overview error-tab modernization stack. It follows the merged execution-error slice in #12683 and focuses only on Missing Media / Missing Inputs. The intent is to simplify missing-media errors from media-type/file-resolution cards into compact node-level guidance. Missing media is now presented as a flat list of affected node inputs, matching the new catalog-driven item-row direction introduced by the first PR while intentionally avoiding the larger missing-node, swap-node, and missing-model redesigns. This PR also removes the upload/library selection workflow from the error tab. Missing media rows now help the user locate the affected node/input directly, rather than attempting to resolve the missing file inside the error card. ## Changes - **What**: Simplifies the Missing Inputs card into a flat list of affected node/widget rows. - **What**: Displays each missing media row with a catalog-resolved item label in the form `{nodeName} - {inputName}`. - **What**: Makes both the row label and right-aligned locate icon navigate to the affected node on canvas. - **What**: Adds item-specific accessible labels for locate icon buttons and an explicit focus-visible state for clickable row labels. - **What**: Uses the existing `list-scale` transition pattern so missing media rows animate consistently when they enter or leave. - **What**: Removes the old image/video/audio sub-sections from the missing-media card. - **What**: Removes missing-media upload/dropzone/library-selection/status/confirm/cancel UI, because resolution is no longer handled inside this card. - **What**: Removes stale E2E selectors and tests tied to the deleted upload/library controls. - **What**: Removes stale English missing-media locale keys for the deleted controls; translated locale files are intentionally left to the localization sync process. - **What**: Removes stale missing-media store interaction state (`expandState`, `uploadState`, `pendingSelection`) and the now-unused name-based removal API that only supported the deleted upload/library flow. - **What**: Keeps the remaining widget/node/prefix candidate-removal APIs used by the missing-media lifecycle. - **What**: Carries `nodeType` through missing-media referencing nodes so fallback labels remain correct when the same missing filename is referenced by multiple node types. - **Breaking**: None expected. This changes the missing-media error-tab UX, but does not change the workflow schema or backend APIs. - **Dependencies**: None. ## Planned stack This PR is part of the same staged redesign plan described in #12683. The goal is to keep each error category reviewable and stable before moving to the next slice. 1. Execution errors, merged in #12683: validation, runtime, and prompt error grouping/presentation. 2. Missing media, this PR: simplify image/video/audio missing-media cards into node-level catalog item rows with locate actions. 3. Missing node and swap node: align missing-pack rows, nested node references, install/replace actions, and locate behavior. 4. Missing model: unify OSS and Cloud presentation, simplify download/import actions, and improve import/download progress behavior. This PR intentionally does not change missing node packs, swap nodes, missing models, runtime errors, validation errors, or prompt errors. ## Review Focus - Missing media rows should now be node-level rows, not file-level rows and not image/video/audio sections. - If the same missing media filename is referenced by multiple nodes, each affected node/input should render as its own row. - Clicking either the row label or locate icon should focus the corresponding affected node. - Removed upload/library controls are intentional and should not be restored in this PR. - The missing-media store cleanup should not remove any state or API still used by the remaining missing-media lifecycle. - Only `rightSidePanel.missingMedia.missingMediaTitle` should remain in the English locale for the old missing-media UI namespace; new row copy lives in the error catalog. - This PR should stay scoped to Missing Media / Missing Inputs and should not include the larger missing model/node/swap redesign work. ## Screenshots (if applicable) This PR <img width="483" height="339" alt="스크린샷 2026-06-08 오후 8 26 43" src="https://github.com/user-attachments/assets/e029411e-b1db-41cd-aa6e-2bd5d86762f6" /> Before (Main) <img width="484" height="802" alt="스크린샷 2026-06-08 오후 8 32 19" src="https://github.com/user-attachments/assets/c183c18d-babe-4f4e-b4e0-706ce41298de" /> ## Validation - `pnpm format` - `pnpm test:unit src/platform/missingMedia/missingMediaStore.test.ts src/components/rightSidePanel/errors/TabErrors.test.ts src/platform/errorCatalog/errorMessageResolver.test.ts src/platform/missingMedia/missingMediaScan.test.ts src/platform/missingMedia/missingMediaAssetResolver.test.ts` - `pnpm typecheck` - `pnpm knip --cache` - `pnpm test:browser:local browser_tests/tests/propertiesPanel/errorsTabMissingMedia.spec.ts --project=chromium` - Pre-push hook: `pnpm knip --cache` ## Review notes Claude review was run after the main implementation and again after cleanup. No blocker or major issues remained. The only final minor finding was stale inline i18n fixture data in `TabErrors.test.ts`, which was removed in the final commit. |
||
|
|
74b9f16b62 |
Refine execution error presentation (#12683)
## Summary This is the first PR in a planned stack to modernize the Workflow Overview error tab. It focuses only on execution-style errors: validation errors, runtime errors, and known prompt errors. The intent is to establish the catalog-driven presentation model before touching the larger missing-resource cards. Validation and prompt errors are known product states, so this PR makes them read more like structured guidance instead of generic reportable failures. Runtime errors remain reportable, but their details are reorganized so the error log is easier to scan and copy. ## What changed - Groups validation errors by error catalog id instead of node class/type. - Adds an `unknown_validation_error` fallback catalog id so validation grouping can follow one rule without special-case missing catalog ids. - Shows validation group title and message once, then lists each affected input as a compact item row. - Adds per-item validation detail disclosure so detailed validation text is still available without repeating the group title/message for every item. - Keeps locate-node behavior available from validation rows, including keyboard/ARIA disclosure wiring. - Removes GitHub, copy, and help actions from validation/prompt errors because these are known, cataloged errors where the UI copy should guide the user directly. - Refines runtime error cards so the error log is visible by default, has its own header, and keeps copy/report actions inside the log area. - Removes the special full-panel singleton runtime layout so runtime errors keep the same fixed card rhythm as the other error groups. - Keeps runtime errors reportable via Get Help and Find on GitHub, because these can still represent unexpected execution failures. - Updates prompt error detail styling to match the darker runtime error-log treatment. - Restores display-message semantics for grouped execution messages: `displayMessage ?? message` is used for user-facing dedupe instead of raw backend-only messages. - Adds focused unit coverage for catalog grouping, fallback validation catalog ids, display-message grouping, runtime detail behavior, and the updated prompt/validation action model. ## Planned stack This PR intentionally keeps the first slice narrow. The broader redesign is planned as a sequence of follow-up PRs rather than one large change: 1. Execution errors, this PR: validation, runtime, and prompt error grouping/presentation. 2. Missing media: simplify image/video/audio missing-media cards around catalog item labels and locate actions. 3. Missing node and swap node: align missing-pack rows, nested node references, install/replace actions, and locate behavior. 4. Missing model: unify OSS and Cloud presentation, simplify download/import actions, and improve import/download progress behavior. The goal is to review and stabilize each slice before stacking the next one. This is especially important because later missing-model changes are much larger and should not obscure the catalog/error-card behavior introduced here. ## Review focus - Validation errors should now group by catalog id, not by node class. - Validation groups intentionally show one message per group, with individual affected inputs rendered as rows. - Prompt and validation errors intentionally no longer show report/copy/help actions. - Runtime errors intentionally still show report actions, but only inside the error-log panel. - Node id badges are intentionally not shown in these execution error rows; the follow-up missing-resource PRs will handle their own row treatments separately. - This PR does not change missing media, missing model, missing node pack, or swap node cards. ## Screenshots ### This PR Validation error <img width="457" height="362" alt="스크린샷 2026-06-07 오전 4 26 19" src="https://github.com/user-attachments/assets/4c35b9f3-57dd-4dae-b44a-6d2fd8547b7c" /> Runtime error <img width="454" height="545" alt="스크린샷 2026-06-07 오전 4 24 24" src="https://github.com/user-attachments/assets/b7d4482f-b35b-4ed2-90f2-0a62dafa3519" /> Prompt / Service error <img width="456" height="192" alt="스크린샷 2026-06-07 오전 4 27 58" src="https://github.com/user-attachments/assets/aeec0978-b47f-40c7-ab71-0a0d18ceb054" /> ### Old (main) Validation error <img width="457" height="853" alt="스크린샷 2026-06-07 오전 4 25 09" src="https://github.com/user-attachments/assets/185dd573-430d-4041-8b31-a8eb6346f1ff" /> Runtime error <img width="455" height="554" alt="스크린샷 2026-06-07 오전 4 24 58" src="https://github.com/user-attachments/assets/deb1c09d-ea58-4d6a-9ac6-d2a3a9832fbe" /> Prompt / Service error <img width="455" height="297" alt="스크린샷 2026-06-07 오전 4 28 14" src="https://github.com/user-attachments/assets/c68eef7c-6525-4a5b-858c-6482fe76ad27" /> ## Validation - `pnpm format:check` - `pnpm test:unit src/components/rightSidePanel/errors/TabErrors.test.ts src/components/rightSidePanel/errors/ErrorNodeCard.test.ts src/components/rightSidePanel/errors/useErrorGroups.test.ts src/platform/errorCatalog/errorMessageResolver.test.ts` - `pnpm typecheck` - `pnpm lint` - `pnpm knip` - `pnpm build` |
||
|
|
dbeb9cc10d |
fix: clear missing model on promoted widget change (#12677)
## Summary Fixes FE-942 by clearing missing model indicators when promoted subgraph widgets are changed through the legacy canvas path. ## Changes - **What**: Resolves promoted widget error-clearing targets in `useErrorClearingHooks`, including legacy canvas path events from interior widgets. - **Dependencies**: None. ## Review Focus - Promoted widget validation errors clear by resolved interior widget name, while missing model/media state clears by source widget name. - Same-named promoted widgets are value-gated so changing one promoted model widget does not clear unchanged sibling indicators. - Core promoted widget event emission remains unchanged; the fix is scoped to the error-clearing hook. ## Validation - `pnpm test:unit src/composables/graph/useErrorClearingHooks.test.ts` - `pnpm test:unit src/core/graph/subgraph/promotedWidgetView.test.ts src/lib/litegraph/src/subgraph/SubgraphWidgetPromotion.test.ts` - `pnpm exec oxfmt --check src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts src/stores/executionErrorStore.ts` - `pnpm exec oxlint src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts src/stores/executionErrorStore.ts --type-aware` - `pnpm exec eslint src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts src/stores/executionErrorStore.ts` - `pnpm typecheck` - `pnpm test:browser:local browser_tests/tests/propertiesPanel/errorsTabModeAware.spec.ts --project=chromium --grep Subgraph` - pre-push `knip --cache` ## Screenshots (if applicable) N/A |
||
|
|
874b486640 |
fix: resolve missing resource error messages (#12646)
## Summary Resolve missing resource error groups through the error catalog so missing nodes, replaceable nodes, missing models, and missing media use consistent panel and single-error overlay copy. ## Changes - **What**: Adds missing-resource resolvers for `missing_node`, `swap_nodes`, `missing_model`, and `missing_media` that provide `displayMessage`, `toastTitle`, and `toastMessage` alongside the existing group titles. The Errors tab now renders a group-level `displayMessage` under non-execution group headers, which gives grouped missing-resource cards the same explanatory message path used by validation/runtime errors without adding per-row detail fields that these grouped cards do not need. - **What**: Moves missing node and swap node explanatory copy out of card-local hardcoded text and into `errorCatalog.missingErrors.*` keys. `MissingNodeCard` and `SwapNodesCard` now focus on rendering their grouped rows and actions, while the shared error group header owns the explanatory copy. - **What**: Adds environment-aware copy for missing node packs and missing models. Cloud messages explain unsupported resources and replacement/import paths without suggesting local execution, while OSS messages point users toward installing or downloading the missing resources. - **What**: Adds single-error overlay/toast copy for missing resources. Missing media uses a concise input-focused title/message, missing models distinguish Cloud unsupported models from OSS missing files, and missing nodes/swap nodes use node-type-aware copy. - **What**: Deduplicates missing node and swap node toast decisions by distinct node type so repeated instances of the same missing/replaceable node do not accidentally switch the single-error copy to plural copy. - **What**: Preserves representative missing media candidate metadata so missing media toast copy can use a human-readable node name such as `Load Image is missing a required media file.` - **What**: Removes unused missing-resource resolver fields such as grouped `displayDetails`, grouped `displayItemLabel`, and the unused `mediaTypes` source parameter after deciding those fields do not fit grouped missing-resource cards. - **Breaking**: None. - **Dependencies**: None. ## Review Focus - Missing resource groups are still grouped cards. This PR intentionally gives them group-level display and toast copy, but does not split missing resources into one error item per underlying candidate. - Missing resource count semantics are intentionally not normalized here. Error overlay totals, store counts, and grouped card counts still follow the existing behavior; a follow-up PR can define those count units separately. - The Cloud/OSS message variants remain explicit in the resolver instead of being abstracted into a generic variant helper. That keeps this PR focused on the messaging behavior and avoids a broader resolver refactor. - Only `src/locales/en/main.json` is updated directly. Other locales should be synced by the existing localization flow. ## Screenshots (if applicable) <img width="668" height="245" alt="스크린샷 2026-06-05 오전 3 16 49" src="https://github.com/user-attachments/assets/98b50ac3-67e1-438d-8c37-e06c7bf465ee" /> <img width="666" height="195" alt="스크린샷 2026-06-05 오전 3 16 58" src="https://github.com/user-attachments/assets/92da95b1-03d6-4739-97e6-c573982bfec9" /> <img width="505" height="358" alt="스크린샷 2026-06-05 오전 3 17 27" src="https://github.com/user-attachments/assets/4d0e1a6e-13b9-4097-9fb5-19fe0c5331dc" /> <img width="507" height="324" alt="스크린샷 2026-06-05 오전 3 17 44" src="https://github.com/user-attachments/assets/054e42f8-0d0c-44b5-8a67-e467fc04f1fc" /> ## Validation - `pnpm format` - `pnpm lint` - `pnpm test:unit src/platform/errorCatalog/errorMessageResolver.test.ts src/components/rightSidePanel/errors/useErrorGroups.test.ts src/components/rightSidePanel/errors/TabErrors.test.ts` - `pnpm typecheck` - push hook: `knip --cache` |
||
|
|
8a819fa2be |
refactor(assets): read content hash from the canonical hash field (#12638)
## Summary The assets API exposes an asset's content hash as `hash`. An older `asset_hash` field was a deprecated alias carrying the same value. This PR moves the frontend fully onto `hash` and removes `asset_hash` from the frontend entirely. ## Changes - Read `asset.hash` (no `?? asset_hash` fallback) across the asset consumers: - `useMediaAssetActions` — widget-value variants + cloud-mode stored-filename resolution - `assetsStore` — input-asset-by-filename map - `assetMetadataUtils.getAssetUrlFilename` - `missingMedia` resolver/scan and `missingModel` scan hash matching - `useComboWidget` / `useWidgetSelectItems` - `assetPreviewUtil.findOutputAsset` now queries `/assets?hash=` instead of the deprecated `?asset_hash=` param and matches on `a.hash`. - Removed `asset_hash` from the zod asset schema and the local `AssetRecord` type. Responses that still include the alias parse cleanly — zod strips unknown keys — so the declared field protected nothing once the reads were gone. - Purged `asset_hash` from all test fixtures/mocks; tests key on the canonical `hash`. ## Safety / rollout The API currently emits **both** `hash` and `asset_hash` with identical values, so reading `hash` is safe today. This is the frontend half of retiring the alias; the backend stops emitting `asset_hash` only after this ships and old bundles age out, so there is no window where the field the UI reads is absent. ## Verification - `pnpm typecheck`: clean. - Affected unit tests pass (asset utils, store, media/model scans, widget composables). - `grep -rn asset_hash src/`: zero matches. |
||
|
|
4bfb0c36be |
Fix Cloud media input defaults (#12562)
## Summary Fix Cloud media loader widgets so `LoadImage`, `LoadVideo`, and `LoadAudio` resolve their default values from Cloud input assets instead of blindly accepting backend `object_info` combo options. When no matching Cloud input asset exists, the widgets now start empty instead of selecting a server-only value that immediately trips missing-input detection. ## Changes - **What**: Cloud media input widgets now derive their available values from `assetsStore.inputAssets`, filtered to the node's media type and to assets with a valid `asset_hash`. - **What**: Cloud media defaults now prefer an explicit default only when it matches an available Cloud input asset hash or name, then fall back to the first matching Cloud input asset, and otherwise use an empty value. - **What**: The media path keeps the existing model-widget implementation style by resolving Cloud asset state through the store internally, while preserving media-specific hash/name matching because media widgets submit Cloud `asset_hash` values. - **What**: Added regression coverage for adding empty Cloud `LoadImage`, `LoadVideo`, and `LoadAudio` nodes when backend `object_info` advertises server-only media options. - **What**: Expanded media widget unit coverage for image/video/audio inputs, empty defaults, first-asset fallback, default-by-hash matching, default-by-name matching, hashless asset filtering, unrelated media filtering, dynamic values, option labels, lazy input loading, and `control_after_generate` wiring. - **What**: Kept existing OSS and Cloud runtime missing-media E2E coverage, with Cloud fixtures consistently using the local setup backend for `object_info` so tests do not depend on live Cloud backend startup details. - **Breaking**: None. - **Dependencies**: None. ## Review Focus - Cloud media loader defaults should no longer be sourced from backend file lists unless the value also corresponds to a Cloud input asset. - Empty Cloud input-asset libraries should produce empty media widget values, not missing-input errors at node creation time. - Model asset-browser behavior is intentionally unchanged. The media path mirrors the store-access style, but media defaults still resolve to asset hashes because those are the values submitted by Cloud media widgets. - The Cloud E2E fixture stubs bootstrap endpoints and routes `object_info` through the local setup backend. This keeps the test focused on frontend behavior while still using realistic node definitions. ## Testing - `pnpm exec oxfmt browser_tests/tests/propertiesPanel/errorsTabMissingMediaRuntime.spec.ts src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.test.ts src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts` - `pnpm lint` - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm exec vitest run src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.test.ts` - `PLAYWRIGHT_TEST_URL=http://127.0.0.1:5174 PLAYWRIGHT_SETUP_API_URL=http://127.0.0.1:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabMissingMediaRuntime.spec.ts --project=chromium --workers=1` - `PLAYWRIGHT_TEST_URL=http://127.0.0.1:5175 PLAYWRIGHT_SETUP_API_URL=http://127.0.0.1:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabMissingMediaRuntime.spec.ts --project=cloud --workers=1` - `git diff --check` - `pnpm knip` - `.claude/skills/reviewing-unit-tests/SKILL.md` red-flag review ## Screenshots Before https://github.com/user-attachments/assets/5df04036-d15c-4f94-bdcd-df8b26a29329 After https://github.com/user-attachments/assets/abe7caf5-a83b-4960-aa6f-65a377424a85 |
||
|
|
06e09df673 |
test: replace jobs mock fixture with typed route mocks (#12267)
## Summary Replace the merged stateful jobs API browser mock fixture with a small declarative typed route-mock foundation. ## Changes - **What**: Removes `JobsApiMock`, `jobsApiMockFixture`, and the old shared `jobFixtures` helper. - **What**: Adds a generic `RouteMocker` primitive for explicit typed JSON route responses. - **What**: Adds `jobsRouteFixture`, which registers explicit `/api/jobs` list/detail responses without filtering, mutation handling, or hidden in-memory backend behavior. - **What**: Migrates the current queue overlay and missing-media runtime specs onto the new jobs route fixture. - **What**: Keeps `./browser_tests/tsconfig.json` in the ESLint TypeScript resolver config. - **Dependencies**: None. ## Review Focus This is intended to be the foundation PR for the test-strategy reset: old stateful helper out, typed declarative route mocks in. It intentionally does not add the full asset sidebar, job history sidebar, or floating QPO coverage suite; those should stack on top after this fixture shape is accepted. The boundary this PR is trying to preserve: route mocks may describe frontend-visible API responses, but should not implement Core queue/history mutation semantics. Context: https://www.notion.so/comfy-org/E2E-Test-Strategy-for-Assets-Job-History-and-Queue-Progress-Overlay-35f6d73d365081209bc5f10e6c7eb8de ## Screenshots (if applicable) Not applicable. |
||
|
|
ad63f7cb9b |
test: cover missing media runtime sources (#12126)
## Summary Adds browser coverage for the missing-media runtime paths introduced by #12069 and #12111: - OSS: annotated `[output]` media is resolved from job history. - Cloud: compact `[output]` media is resolved from output assets. - OSS and Cloud: dropped video uploads do not surface a missing-media error while the upload is still in progress. This PR is now rebased directly onto `main`; the parent fix PRs have been squash-merged, so this branch only contains the E2E coverage commit. ## Test Fixtures - Adds workflow fixtures for OSS spaced output annotations and Cloud compact output annotations. - Adds a small plain MP4 fixture for video drag/drop upload coverage. ## Validation - `pnpm exec oxfmt --check browser_tests/tests/propertiesPanel/errorsTabMissingMediaRuntime.spec.ts browser_tests/assets/missing/missing_media_cloud_output_annotation.json browser_tests/assets/missing/missing_media_output_annotations.json` - `pnpm typecheck:browser` - `pnpm exec oxlint browser_tests/tests/propertiesPanel/errorsTabMissingMediaRuntime.spec.ts --type-aware` - `git diff --check origin/main..HEAD` Note: before the rebase, the Cloud project target for this spec passed locally. Local OSS project execution against the currently running Cloud dist did not reach the test body because `ComfyPage.waitForAppReady` timed out in `beforeEach`. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12126-test-cover-missing-media-runtime-sources-35d6d73d365081f0a981c02f33c0ff84) by [Unito](https://www.unito.io) |
||
|
|
21406dceb1 |
fix: skip nested subgraph containers in replay scan (#11908)
## Summary Fixes the Cloud-only nested subgraph missing-model false positive covered by the stacked regression test in #11907. When returning from an outer subgraph to the root graph, the Vue graph node manager replays `onNodeAdded` for existing graph nodes. The realtime error-clearing hook handled a subgraph container by recursively scanning all interior nodes. For nested subgraphs, that also scanned the nested subgraph container itself. Nested subgraph container widgets are promoted synthetic views of interior widgets. Scanning them as real model-loader nodes is wrong: the container node type is the subgraph UUID, not `UNETLoader`, so Cloud asset resolution can classify an installed promoted model as missing. ## Changes - Skip nested subgraph container nodes during parent subgraph replay scans. - Keep scanning real active interior leaf nodes. - Add unit coverage proving the replay scan visits the `UNETLoader` leaf but not the nested subgraph container. - Remove the `test.fail()` annotation from the Cloud E2E regression test added in #11907. ## Stacked PR This PR is stacked on #11907. After #11907 lands, this branch should be rebased or retargeted onto `main`. ## Verification - `pnpm exec vitest run src/composables/graph/useErrorClearingHooks.test.ts -t "skips nested subgraph containers during parent subgraph replay scan"` - `pnpm exec oxfmt --check src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm exec eslint src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm exec oxlint src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --type-aware` - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm build:cloud` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --project=cloud` - commit hook: `pnpm typecheck`, `pnpm typecheck:browser` - push hook: `pnpm knip` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11908-fix-skip-nested-subgraph-containers-in-replay-scan-3566d73d3650819c8687d6ab74add1b9) by [Unito](https://www.unito.io) |
||
|
|
14320a131f |
test: add Playwright regression test for nested subgraph Cloud missing model (#11907)
## Summary Adds a Cloud Playwright regression test for the nested subgraph case where an installed Lotus diffusion model is incorrectly surfaced as missing after returning to the root graph. The fixture keeps the reproduction small: root graph -> subgraph node -> nested subgraph node -> `UNETLoader` using `lotus-depth-d-v1-1.safetensors`. The test stubs `/api/assets` through the shared asset API fixture so that model is explicitly present as a `diffusion_models` asset. This test is intentionally written as an XFAIL regression guard. Its setup and precondition checks are outside the XFAIL section: initial workflow load must not show the error overlay, the Errors tab must initially stay hidden, subgraph entry must succeed, root return must succeed, and the replay scan must run. Only the final `Errors` tab visibility assertion is expected to fail on current Cloud behavior. ## What a green run means A green CI run for this PR means the Cloud-only bug was reproduced at the intended point. The test reaches the root-return replay scan, verifies that the replay scan ran, and then current Cloud behavior makes the Errors tab visible even though the Lotus model exists in `/api/assets`. If any earlier setup or navigation step fails, or if the root-return replay scan does not run, the test fails normally because those checks happen before `test.fail()` is applied. Locally, removing `test.fail()` produces the expected red result after the replay-scan precondition passes, with `panel-tab-errors` visible. The intended post-fix contract is that the replay scan still runs, but the Errors tab remains hidden. ## Why this is XFAIL This PR intentionally ships only the regression test, not the production fix. The final behavioral assertion is annotated with `test.fail()` because the current Cloud replay path still treats the nested subgraph promoted model widget as missing. When the follow-up fix lands, Playwright will report this test as an unexpected pass until the `test.fail()` annotation is removed. That is the handoff point for converting this regression guard into a normal passing E2E test. ## Follow-up The stacked fix PR is #11908. It updates the replay scan so nested subgraph container nodes are skipped, then removes the `test.fail()` annotation from this test. ## Verification - `pnpm exec oxfmt --check browser_tests/fixtures/assetApiFixture.ts browser_tests/tests/cloud-asset-default.spec.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm exec oxlint browser_tests/fixtures/assetApiFixture.ts browser_tests/tests/cloud-asset-default.spec.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --type-aware` - `pnpm exec eslint browser_tests/fixtures/assetApiFixture.ts browser_tests/tests/cloud-asset-default.spec.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm typecheck:browser` - `pnpm typecheck` - `pnpm lint` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts browser_tests/tests/cloud-asset-default.spec.ts --project=cloud` - Temporarily removed `test.fail()` locally and verified the test fails only after the replay-scan precondition passes, with `panel-tab-errors` visible ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11907-test-add-Playwright-regression-test-for-nested-subgraph-Cloud-missing-model-3566d73d3650810b86d4de916c2852f9) by [Unito](https://www.unito.io) |
||
|
|
3c5695fd42 |
test: add Vue node error/validation ring e2e coverage (#11727)
## Summary Add additional test coverage for vue node errors ## Changes - **What**: - add tests for showing error on missing node, execution error, validation failure & resolved on fix - move ErrorsTabHelper to fixtures dir & update refs - add SLOW_MO env var for headed local tests ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11727-test-add-Vue-node-error-validation-ring-e2e-coverage-3506d73d365081069ff8f70f7970dd55) by [Unito](https://www.unito.io) |
||
|
|
967f1eb562 |
test: extract title editor test component (#11605)
## Summary Extract shared TitleEditor component and update tests to use it ## Changes - **What**: - add title editor helper - update locations that used `TestIds.node.titleInput` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11605-test-extract-title-editor-test-component-34c6d73d3650811da6b0ec493b190c3f) by [Unito](https://www.unito.io) |
||
|
|
963a7bf178 |
refactor: consolidate browser_tests/helpers/ into fixtures/ (#11411)
*PR Created by the Glary-Bot Agent* --- ## Summary - Eliminates the confusing dual-helpers structure where `browser_tests/helpers/` and `browser_tests/fixtures/helpers/` coexisted one tier apart with overlapping purposes - Routes each file to its natural home based on what it actually *is*: page objects → `components/`, standalone utils → `utils/`, domain helper classes stay in `helpers/` - Adds an ESLint guard (`no-restricted-imports`) to prevent re-creating `browser_tests/helpers/` ## File Moves | File | From | To | Reason | |---|---|---|---| | `actionbar.ts` | `helpers/` | `fixtures/components/Actionbar.ts` | Page object class imported by ComfyPage | | `templates.ts` | `helpers/` | `fixtures/components/Templates.ts` | Page object class imported by ComfyPage | | `boundsUtils.ts` | `fixtures/helpers/` | `fixtures/utils/` | Pure function, not a helper class | | `mimeTypeUtil.ts` | `fixtures/helpers/` | `fixtures/utils/` | Pure function, not a helper class | | `builderTestUtils.ts` | `helpers/` | `fixtures/utils/` | Shared test setup functions | | `clipboardSpy.ts` | `helpers/` | `fixtures/utils/` | Page injection utility | | `fitToView.ts` | `helpers/` | `fixtures/utils/` | Canvas utility function | | `manageGroupNode.ts` | `helpers/` | `fixtures/utils/` | Litegraph interaction helper | | `painter.ts` | `helpers/` | `fixtures/utils/` | Test helper functions | | `perfReporter.ts` | `helpers/` | `fixtures/utils/` | Test infrastructure | | `promotedWidgets.ts` | `helpers/` | `fixtures/utils/` | Query helpers for specs | ## What Changed Beyond File Moves - **28 import statements** updated across test specs, fixtures, and infra files - **AGENTS.md** — directory tree diagram and architectural separation descriptions updated - **README.md** — "Leverage Existing Fixtures and Helpers" section updated - **`.claude/skills/perf-fix-with-proof/SKILL.md`** — perfReporter path reference updated - **`eslint.config.ts`** — added `@e2e/helpers/*` restricted import pattern to both spec and non-spec browser_tests rules ## Verification - `pnpm typecheck` — clean - `pnpm typecheck:browser` — clean - `pnpm lint` — 0 errors, 0 warnings - `pnpm format:check` — all files formatted - `pnpm knip` — clean - Pre-commit hooks passed full pipeline (oxfmt, oxlint, eslint, typecheck, typecheck:browser) ## Config Audit No changes needed to: `tsconfig.json` (`@e2e/*` alias covers all subdirs), `playwright.config.ts`, `vite.config.mts`, `knip.config.ts`, `.oxlintrc.json`, `nx.json` ## Manual Verification Note This is a pure structural refactoring (file moves + import updates) with zero behavioral or visual changes. The typecheck and lint passes confirm all imports resolve correctly. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11411-refactor-consolidate-browser_tests-helpers-into-fixtures-3476d73d3650816cb671ef7fa8433f66) by [Unito](https://www.unito.io) --------- Co-authored-by: glary-bot <glary-bot@comfy.org> Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: DrJKL <DrJKL0424@gmail.com> Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
b4d209b5f6 |
feat: refresh missing models through pipeline (#11661)
## Summary Follow-up to the closed earlier attempt in #11646. This PR keeps the same user-facing goal, but changes the implementation to reuse the existing missing model pipeline for refresh instead of maintaining a separate candidate-only recheck path. Adds a missing model refresh action in the Errors tab by reusing the existing missing model pipeline, so users can re-check models after downloading or manually placing files without reloading the workflow. ## Changes - **What**: - Adds `app.refreshMissingModels()` as a reusable refresh entry point for the current root graph. - Splits node definition reloading into `app.reloadNodeDefs()` so missing-model refresh can pull fresh `object_info` without showing the generic combo refresh success flow. - Reuses the existing missing model pipeline instead of adding a separate candidate-only checker. The refresh path serializes the current graph, reuses active workflow model metadata when available, falls back to current missing-model metadata, and then reruns the same candidate discovery/enrichment/surfacing flow used during workflow load. - Adds missing model refresh state and error handling to `missingModelStore`. - Adds a Refresh button next to Download all in the missing model card action bar. - Moves Download all from the Errors tab header into the missing model card, so the Download all and Refresh actions render or hide together. - Changes Download all visibility from “more than one downloadable model” to “at least one downloadable model.” - Keeps the action bar hidden when there are no downloadable missing models; Cloud still does not render this action area. - Normalizes active workflow `pendingWarnings` updates so resolved missing model warnings do not get revived by stale empty warning objects. - Adds test IDs and coverage for the new action bar, refresh state, refresh delegation, pending warning sync, and E2E refresh behavior. - **Breaking**: None. - **Dependencies**: None. ## Review Focus The main design choice is intentionally reusing the missing model pipeline for refresh instead of implementing a smaller candidate-only recheck. The earlier candidate-only approach was cheaper, but it created a separate source of truth for missing-model resolution and made edge cases harder to reason about. In particular, it could diverge from the behavior used when a workflow is loaded, and it did not naturally handle the case where a model becomes missing after the workflow is already open. This version pays the cost of refreshing node definitions and rerunning the missing-model scan for the current graph, but keeps the refresh behavior aligned with workflow load semantics. Expected behavior by environment: - OSS browser: - The action bar appears when at least one missing model has a downloadable URL and directory. - Download all uses the existing browser download path. - Refresh reloads `object_info`, refreshes node definitions/combo values, reruns missing-model detection for the current graph, and clears the error if the selected model is now available. - OSS desktop: - The same action bar appears under the same downloadable-model condition. - Download all uses the existing Electron DownloadManager path. - Refresh uses the same missing-model pipeline as browser, so manually placed files or desktop-downloaded files can be rechecked without reloading the workflow. - Cloud: - The action bar remains hidden because model download/import is not supported in this section for Cloud. A few boundaries are intentional: - This PR does not add automatic filesystem watching. Browser OSS cannot reliably observe local model folder changes, so the user-triggered Refresh button remains the cross-environment mechanism. - This PR does not redesign the public `refreshComboInNodes` API beyond extracting `reloadNodeDefs()` for reuse. Further cleanup of toast behavior or a more explicit object-info reload API can be follow-up work. - This PR keeps refresh scoped to missing-model validation; missing media and missing nodes continue to use their existing flows. Linear: FE-417 ## Screenshots (if applicable) https://github.com/user-attachments/assets/2e02799f-1374-4377-b7b3-172241517772 ## Validation - `pnpm format` - `pnpm lint` (passes; existing unrelated warning remains in `src/platform/workspace/composables/useWorkspaceBilling.test.ts`) - `pnpm typecheck` - `pnpm test:unit` - `pnpm test:browser:local -- --project=chromium browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts` - `pnpm build` - `NX_SKIP_NX_CACHE=true DISTRIBUTION=desktop USE_PROD_CONFIG=true NODE_OPTIONS='--max-old-space-size=8192' pnpm exec nx build` - Manual desktop verification through `~/Projects/desktop` after copying the desktop build into `assets/ComfyUI/web_custom_versions/desktop_app`: - confirmed the FE bundle is built with `DISTRIBUTION = "desktop"` - confirmed missing model Download uses the desktop download path instead of browser download - confirmed Refresh can clear the missing model error after the model is available - Push hook: `pnpm knip --cache` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11661-feat-refresh-missing-models-through-pipeline-34f6d73d3650811488defee54a7a6667) by [Unito](https://www.unito.io) |
||
|
|
693b8383d6 |
fix: missing-asset correctness follow-ups from #10856 (#11233)
Follow-up to #10856. Four correctness issues and their regression tests. ## Bugs fixed ### 1. ErrorOverlay model count reflected node selection `useErrorGroups` exposed `filteredMissingModelGroups` under the public name `missingModelGroups`. `ErrorOverlay.vue` read that alias to compute its model count label, so selecting a node shrank the overlay total. The overlay must always show the whole workflow's errors. Exposed both shapes explicitly: `missingModelGroups` / `missingMediaGroups` (unfiltered totals) and `filteredMissingModelGroups` / `filteredMissingMediaGroups` (selection-scoped). `TabErrors.vue` destructures the filtered variant with an alias. Before https://github.com/user-attachments/assets/eb848c5f-d092-4a4f-b86f-d22bb4408003 After https://github.com/user-attachments/assets/75e67819-c9f2-45ec-9241-74023eca6120 ### 2. Bypass → un-bypass dropped url/hash metadata Realtime `scanNodeModelCandidates` only reads widget values, so un-bypass produced a fresh candidate without the url that `enrichWithEmbeddedMetadata` had previously attached from `graphData.models`. `MissingModelRow`'s download/copy-url buttons disappeared after a bypass/un-bypass cycle. Added `enrichCandidateFromNodeProperties` that copies `url`/`hash`/`directory` from the node's own `properties.models` — which persists across mode toggles — into each scanned candidate. Applied to every call site of the per-node scan. A later fix in the same branch also enforces directory agreement to prevent a same-name / different-directory collision from stamping the wrong metadata. Before https://github.com/user-attachments/assets/39039d83-4d55-41a9-9d01-dec40843741b After https://github.com/user-attachments/assets/047a603b-fb52-4320-886d-dfeed457d833 ### 3. Initial full scan surfaced interior errors of a muted/bypassed subgraph container `scanAllModelCandidates`, `scanAllMediaCandidates`, and the JSON-based missing-node scan only check each node's own mode. Interior nodes whose parent container was bypassed passed the filter. Added `isAncestorPathActive(rootGraph, executionId)` to `graphTraversalUtil` and post-filter the three pipelines in `app.ts` after the live rootGraph is configured. The filter uses the execution-ID path (`"65:63"` → check node 65's mode) so it handles both live-scan-produced and JSON-enrichment-produced candidates. Before https://github.com/user-attachments/assets/3032d46b-81cd-420e-ab8e-f58392267602 After https://github.com/user-attachments/assets/02a01931-951d-4a48-986c-06424044fbf8 ### 4. Bypassed subgraph entry re-surfaced interior errors `useGraphNodeManager` replays `graph.onNodeAdded` for each existing interior node when the Vue node manager initializes on subgraph entry. That chain reached `scanSingleNodeErrors` via `installErrorClearingHooks`' `onNodeAdded` override. Each interior node's own mode was active, so the caller guards passed and the scan re-introduced the error that the initial pipeline had correctly suppressed. Added an ancestor-activity gate at the top of `scanSingleNodeErrors`, the single entry point shared by paste, un-bypass, subgraph entry, and subgraph container activation. A later commit also hardens this guard against detached nodes (null execution ID → skip) and applies the same ancestor check to `isCandidateStillActive` in the realtime verification callback. Before https://github.com/user-attachments/assets/fe44862d-f1d6-41ed-982d-614a7e83d441 After https://github.com/user-attachments/assets/497a76ce-3caa-479f-9024-4cd0f7bd20a4 ## Tests - 6 unit tests for `isAncestorPathActive` (root, active, immediate-bypass, deep-nested mute, unresolvable ancestor, null rootGraph) - 4 unit tests for `enrichCandidateFromNodeProperties` (enrichment, no-overwrite, name mismatch, directory mismatch) - 1 unit test for `scanSingleNodeErrors` ancestor guard (subgraph entry replaying onNodeAdded) - 2 unit tests for `useErrorGroups` dual export + ErrorOverlay contract - 4 E2E tests: - ErrorOverlay model count stays constant when a node is selected (new fixture `missing_models_distinct.json`) - Bypass/un-bypass cycle preserves Copy URL button (uses `missing_models_from_node_properties`) - Loading a workflow with bypassed subgraph suppresses interior missing model error (new fixture `missing_models_in_bypassed_subgraph.json`) - Entering a bypassed subgraph does not resurface interior missing model error (shares the above fixture) `pnpm typecheck`, `pnpm lint`, 206 related unit tests passing. ## Follow-up Several items raised by code review are deferred as pre-existing tech debt or scope-avoided refactors. Tracked via comments on #11215 and #11216. --- Follows up on #10856. |
||
|
|
5899a9392e |
test: Simplify vue node/menu test setup (#11184)
## Summary Simplifies test setup for common settings ## Changes - **What**: - add vue-nodes tag to auto enable nodes 2.0 - remove UseNewMenu Top as this is default ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11184-test-Simplify-vue-node-menu-test-setup-3416d73d3650815487e0c357d28761fe) by [Unito](https://www.unito.io) |
||
|
|
521019d173 |
fix: exclude muted/bypassed nodes from missing asset detection (#10856)
## Summary Muted and bypassed nodes are excluded from execution but were still triggering missing model/media/node warnings. This PR makes the error system mode-aware: muted/bypassed nodes no longer produce missing asset errors, and all error lifecycle events (mode toggle, deletion, paste, undo, tab switch) are handled consistently. - Fixes Comfy-Org/ComfyUI#13256 ## Behavioral notes - **Tab switch overlay suppression (intentional)**: Switching back to a workflow with missing assets no longer re-shows the error overlay. This reverses the behavior introduced in #10190. The error state is still restored silently in the errors tab — users can access it via the properties panel without being interrupted by the overlay on every tab switch. ## Changes ### 1. Scan filtering - `scanAllModelCandidates`, `scanAllMediaCandidates`, `scanMissingNodes`: skip nodes with `mode === NEVER || BYPASS` - `collectMissingNodes` (serialized data): skip error reporting for muted/bypassed nodes while still calling `sanitizeNodeName` for safe `configure()` - `collectEmbeddedModelsWithSource`: skip muted/bypassed nodes; workflow-level `graphData.models` only create candidates when active nodes exist - `enrichWithEmbeddedMetadata`: filter unmatched workflow-level models when all referencing nodes are inactive ### 2. Realtime mode change handling - `useErrorClearingHooks.ts` chains `graph.onTrigger` to detect `node:property:changed` (mode) - Deactivation (active → muted/bypassed): remove missing model/media/node errors for the node - Activation (muted/bypassed → active): scan the node and add confirmed errors, show overlay - Subgraph container deactivation: remove all interior node errors (execution ID prefix match) - Subgraph container activation: scan all active interior nodes recursively - Subgraph interior mode change: resolve node via `localGraph.getNodeById()` then compute execution ID from root graph ### 3. Node deletion - `graph.onNodeRemoved`: remove missing model/media/node errors for the deleted node - Handle `node.graph === null` at callback time by using `String(node.id)` for root-level nodes ### 4. Node paste/duplicate - `graph.onNodeAdded`: scan via `queueMicrotask` (deferred until after `node.configure()` restores widget values) - Guard: skip during `ChangeTracker.isLoadingGraph` (undo/redo/tab switch handled by pipeline) - Guard: skip muted/bypassed nodes ### 5. Workflow tab switch optimization - `skipAssetScans` option in `loadGraphData`: skip full pipeline on tab switch - Cache missing model/media/node state per workflow via `PendingWarnings` - `beforeLoadNewGraph`: save current store state to outgoing workflow's `pendingWarnings` - `showPendingWarnings`: restore cached errors silently (no overlay), always sync missing nodes store (even when null) - Preserve UI state (`fileSizes`, `urlInputs`) on tab switch by using `setMissingModels([])` instead of `clearMissingModels()` - `MissingModelRow.vue`: fetch file size on mount via `fetchModelMetadata` memory cache ### 6. Undo/redo overlay suppression - `silentAssetErrors` option propagated through pipeline → `surfaceMissingModels`/`surfaceMissingMedia` `{ silent }` option - `showPendingWarnings` `{ silent }` option for missing nodes overlay - `changeTracker.ts`: pass `silentAssetErrors: true` on undo/redo ### 7. Error tab node filtering - Selected node filters missing model/media card contents (not just group visibility) - `isAssetErrorInSelection`: resolve execution ID → graph node for selection matching - Missing nodes intentionally unfiltered (pack-level scope) - `hasMissingMediaSelected` added to `RightSidePanel.vue` error tab visibility - Download All button: show only when 2+ downloadable models exist ### 8. New store functions - `missingModelStore`: `addMissingModels`, `removeMissingModelsByNodeId` - `missingMediaStore`: `addMissingMedia`, `removeMissingMediaByNodeId` - `missingNodesErrorStore`: `removeMissingNodesByNodeId` - `missingModelScan`: `scanNodeModelCandidates` (extracted single-node scan) - `missingMediaScan`: `scanNodeMediaCandidates` (extracted single-node scan) ### 9. Test infrastructure improvements - `data-testid` on `RightSidePanel.vue` tabs (`panel-tab-{value}`) - Error-related TestIds moved from `dialogs` to `errorsTab` namespace in `selectors.ts` - Removed unused `TestIdValue` type - Extracted `cleanupFakeModel` to shared `ErrorsTabHelper.ts` - Renamed `openErrorsTabViaSeeErrors` → `loadWorkflowAndOpenErrorsTab` - Added `aria-label` to pencil edit button and subgraph toggle button ## Test plan ### Unit tests (41 new) - Store functions: `addMissing*`, `removeMissing*ByNodeId` - `executionErrorStore`: `surfaceMissing*` silent option - Scan functions: muted/bypassed filtering, `scanNodeModelCandidates`, `scanNodeMediaCandidates` - `workflowService`: `showPendingWarnings` silent, `beforeLoadNewGraph` caching ### E2E tests (17 new in `errorsTabModeAware.spec.ts`) **Missing nodes** - [x] Deleting a missing node removes its error from the errors tab - [x] Undo after bypass restores error without showing overlay **Missing models** - [x] Loading a workflow with all nodes bypassed shows no errors - [x] Bypassing a node hides its error, un-bypassing restores it - [x] Deleting a node with missing model removes its error - [x] Undo after bypass restores error without showing overlay - [x] Pasting a node with missing model increases referencing node count - [x] Pasting a bypassed node does not add a new error - [x] Selecting a node filters errors tab to only that node **Missing media** - [x] Loading a workflow with all nodes bypassed shows no errors - [x] Bypassing a node hides its error, un-bypassing restores it - [x] Pasting a bypassed node does not add a new error - [x] Selecting a node filters errors tab to only that node **Subgraph** - [x] Bypassing a subgraph hides interior errors, un-bypassing restores them - [x] Bypassing a node inside a subgraph hides its error, un-bypassing restores it **Workflow switching** - [x] Does not resurface error overlay when switching back to workflow with missing nodes - [x] Restores missing nodes in errors tab when switching back to workflow # Screenshots https://github.com/user-attachments/assets/e0a5bcb8-69ba-4120-ab7f-5c83e4cfc3c5 ## Follow-up work - Extract error-detection computed properties from `RightSidePanel.vue` into a composable (e.g. `useErrorsTabVisibility`) --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
8c9328c1b2 |
feat: add eslint-plugin-playwright via oxlint JS plugins (#11136)
## Summary Add eslint-plugin-playwright as an oxlint JS plugin scoped to browser_tests/, enforcing Playwright best practices at lint time. ## Changes - **What**: Configure eslint-plugin-playwright@2.10.1 via oxlint's alpha `jsPlugins` field (`.oxlintrc.json` override scoped to `browser_tests/**/*.ts`). 18 recommended rules + `prefer-native-locators` + `require-to-pass-timeout` at error severity. All 173 initial violations resolved (config, auto-fix, manual fixes). `no-force-option` set to off — 28 violations need triage (canvas overlay workarounds vs unnecessary force) in a dedicated PR. - **Dependencies**: `eslint-plugin-playwright@^2.10.1` (devDependency, required by oxlint jsPlugins at runtime) ## Review Focus - `.oxlintrc.json` override structure — this is the first use of oxlint's JS plugins alpha feature in this repo - Manual fixes in spec files: `waitForSelector` → `locator.waitFor`, deprecated page methods → locator equivalents, `toPass()` timeout additions - Compound CSS selectors replaced with `.and()` (Playwright native locator composition) to avoid `prefer-native-locators` suppressions - Lint script changes in `package.json` to include `browser_tests/` in oxlint targets --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
0132c77c7d |
test: harden 82 Playwright specs for deterministic CI runs (#10967)
## Summary
Harden 98 E2E spec files and 8 fixtures/helpers for deterministic CI
runs by replacing race-prone patterns with retry-safe alternatives.
No source code changes -- only `browser_tests/` is touched.
## Changes
- **E2E spec hardening** (98 spec files, 6 fixtures, 2 helpers):
| Fix class | Sites | Examples |
|-----------|-------|---------:|
| `expect(await ...)` -> `expect.poll()` | ~153 | interaction,
defaultKeybindings, workflows, featureFlags |
| `const x = await loc.count(); expect(x)` -> `toHaveCount()` | ~19 |
menu, linkInteraction, assets, bottomPanelShortcuts |
| `nextFrame()` -> `waitForHidden()` after menu clicks | ~22 |
contextMenu, rightClickMenu, subgraphHelper |
| Redundant `nextFrame()` removed | many | defaultKeybindings, minimap,
builderSaveFlow |
| `expect(async () => { ... }).toPass()` retry blocks | 5 | interaction
(graphdialog dismiss guard) |
| `force:true` removed from `BaseDialog.close()` | 1 | BaseDialog
fixture |
| ContextMenu `waitForHidden` simplified (check-then-act race removed) |
1 | ContextMenu fixture |
| Non-deterministic node order -> proximity-based selection | 1 |
interaction (toggle dom widget) |
| Tight poll timeout (250ms) -> >=2000ms | 2 | templates |
- **Helper improvements**: Exposed locator getters on
`ComfyPage.domWidgets`, `ToastHelper.toastErrors`, and
`WorkflowsSidebarTab.activeWorkflowLabel` so callers can use retrying
assertions (`toHaveCount()`, `toHaveText()`) directly.
- **Flake pattern catalog**: Added section 7 table to
`browser_tests/FLAKE_PREVENTION_RULES.md` documenting 8 pattern classes
for reviewers and future authors.
- **Docs**: Fixed bad examples in `browser_tests/README.md` to use
`expect.poll()`.
- **Breaking**: None
- **Dependencies**: None
## Review Focus
- All fixes follow the rules in
`browser_tests/FLAKE_PREVENTION_RULES.md`
- No behavioral changes to tests -- only timing/retry strategy is
updated
- The `ContextMenu.waitForHidden` simplification removes a
swallowed-error anti-pattern; both locators now use direct `waitFor({
state: 'hidden' })`
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
|
||
|
|
3b78dfbe1c |
test: migrate browser_tests/ to @e2e/ path alias and add lint rule (#10958)
## Summary Complete the @e2e/ path alias migration started in #10735 by converting all 354 remaining relative imports and adding a lint rule to prevent backsliding. ## Changes - **What**: Migrate all relative imports in browser_tests/ to use `@e2e/` (intra-directory) and `@/` (src/ imports) path aliases. Add `no-restricted-imports` ESLint rule banning `./` and `../` imports in `browser_tests/**/*.ts`. Suppress pre-existing oxlint `no-eval` and `no-console` warnings exposed by touching those files. ## Review Focus - ESLint flat-config merging: the `@playwright/test` ban and relative-import ban are in two separate blocks to avoid last-match-wins collision with the `useI18n`/`useVirtualList` blocks higher in the config. - The `['./**', '../**']` glob patterns (not `['./*', '../*']`) are needed to catch multi-level relative paths like `../../../src/foo`. Follows up on #10735 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10958-test-migrate-browser_tests-to-e2e-path-alias-and-add-lint-rule-33c6d73d365081649d1be771eac986fd) by [Unito](https://www.unito.io) Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
3f375bea9c |
test: comprehensive E2E tests for error dialog, overlay, and errors tab (#10848)
## Summary Comprehensive Playwright E2E tests for the error systems: ErrorDialog, ErrorOverlay, and the errors tab (missing nodes, models, media, execution errors). ## Changes - **What**: - **ErrorDialog** (`errorDialog.spec.ts`, 7 tests): configure/prompt error triggers, Show Report, Copy to Clipboard, Find Issues on GitHub, Contact Support - **ErrorOverlay** (`errorOverlay.spec.ts`, 12 tests): error count labels, per-type button labels (missing nodes/models/media/multiple), See Errors flow (open panel, dismiss, close), undo/redo persistence - **Errors tab — common** (`errorsTab.spec.ts`, 3 tests): tab visibility, search/filter execution errors - **Errors tab — Missing nodes** (`errorsTabMissingNodes.spec.ts`, 5 tests): MissingNodeCard, packs group, expand/collapse, locate button - **Errors tab — Missing models** (`errorsTabMissingModels.spec.ts`, 6 tests): group display, model name, expand/referencing nodes, clipboard copy, OSS Copy URL/Download buttons - **Errors tab — Missing media** (`errorsTabMissingMedia.spec.ts`, 7 tests): migrated from `missingMedia.spec.ts` with detection, upload/library/cancel flows, locate - **Errors tab — Execution** (`errorsTabExecution.spec.ts`, 2 tests): Find on GitHub/Copy buttons, runtime error panel - **Shared helpers**: `ErrorsTabHelper.ts` (openErrorsTabViaSeeErrors), `clipboardSpy.ts` (interceptClipboardWrite/getClipboardText) - **Component changes**: added `data-testid` to `ErrorDialogContent.vue`, `FindIssueButton.vue`, `MissingModelRow.vue`, `MissingModelCard.vue` - **Selectors**: registered all new test IDs in `selectors.ts` - **Test assets**: `missing_nodes_and_media.json` (compound errors), `missing_models_with_nodes.json` (expand/locate) - **Migrations**: error tests from `dialog.spec.ts` → dedicated files, `errorOverlaySeeErrors.spec.ts` → `errorOverlay.spec.ts`, `missingMedia.spec.ts` → `errorsTabMissingMedia.spec.ts` ## Review Focus - OSS tests (`@oss` tag) verify Download/Copy URL buttons appear for models with embedded URLs. - The `missing_models.json` fixture must remain without nodes — adding `CheckpointLoaderSimple` nodes causes directory mismatch in `enrichWithEmbeddedMetadata` that prevents URL enrichment. A separate `missing_models_with_nodes.json` fixture is used for expand/locate tests. ## Cloud tests deferred Missing model cloud environment tests (`@cloud` tag — hidden buttons, import-unsupported notice) are deferred to a follow-up PR. The `comfyPage` fixture cannot bypass the Firebase auth guard in cloud builds, causing `window.app` initialization timeout. A separate infra PR is needed to add cloud auth bypass to the fixture. ## Bug Discovery During testing, a bug was found where the **Locate button for missing nodes in subgraphs fails on initial workflow load**. `collectMissingNodes` in `loadGraphData` captures execution IDs using pre-`configure()` JSON node IDs, but `configure()` triggers subgraph node ID deduplication (PR #8762, always-on since PR #9510) which remaps colliding IDs. This will be addressed in a follow-up PR. - Fixes #10847 (tracked, fix pending in separate PR) ## Testing - 42 new/migrated E2E tests across 8 spec files - All OSS tests pass locally and in CI ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10848-test-comprehensive-E2E-tests-for-error-dialog-overlay-and-errors-tab-3386d73d36508137a5e4cec8b12fa2fa) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
dee236cd60 |
test: comprehensive properties panel E2E tests (PNL-01) (#10548)
## Summary Comprehensive Playwright E2E tests for the properties panel (right sidebar). Part of the **Test Coverage Q2 Overhaul** initiative (Phase 2: PNL-01). ## What's included - **PropertiesPanelHelper** page object in `browser_tests/helpers/` — locators + action methods for all panel elements - **35 test cases** covering: - Open/close via actionbar toggle - Workflow Overview (no selection): tabs, title, nodes list, global settings - Single node selection: title, parameters, info tab, widgets display - Multi-node selection: item count, node listing, hidden Info tab - Title editing: pencil icon, edit mode, rename, visibility rules - Search filtering: query, clear, empty state - Settings tab: Normal/Bypass/Mute state, color swatches, pinned toggle - Selection transitions: no-selection ↔ single ↔ multi - Nodes tab: list all, search filter - Tab label changes based on selection count - **Errors tab scaffold** (for @jaeone94 ADD-03) ## Testing - All tests use Vue nodes with new menu enabled - Zero flaky tests (proper waits, no sleeps) - Screenshots scoped to panel elements ## Unblocks - **ADD-03** (error systems by @jaeone94) — errors tab scaffold ready to extend ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10548-test-comprehensive-properties-panel-E2E-tests-PNL-01-32f6d73d36508199a216fd8d953d8e18) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> |
||
|
|
6ee3803770 |
feat: implement NodeLibrarySidebarTabV2 with Reka UI components (#8548)
## Summary Implement a redesigned Node Library sidebar using Reka UI components with virtualized tree rendering and improved UX. ## Changes - **What**: - Add three-tab structure (Essential, All, Custom) using Reka UI Tabs - Implement TreeExplorerV2 with virtualized tree using TreeRoot/TreeVirtualizer for performance - Add node hover preview with teleport to show NodePreview component - Implement context menu for toggling favorites on nodes - Add search functionality that auto-expands matching folders - Create panel components: EssentialNodesPanel, AllNodesPanel, CustomNodesPanel - Add 'Open Manager' button in CustomNodesPanel - Use custom icons: comfy--node for nodes, ph--folder-fill for folders - New node preview component: `NodePreviewCard` - Api node folder icon - Node drag preview - **Feature Flag**: Enabled via URL parameter `?nodeRedesign=true` ## Review Focus - TreeExplorerV2.vue uses `[...expandedKeys]` to prevent internal mutation by Reka UI TreeRoot - Context menu injection key is exported from TreeExplorerV2Node.vue and imported by TreeExplorerV2.vue - Hover preview uses teleport to `#node-library-node-preview-container-v2` ## Screenshots (if applicable) | Feature | Screenshot | |---|---| | All nodes tab |<img width="323" height="761" alt="image" src="https://github.com/user-attachments/assets/1976222b-83dc-4a1b-838a-2d49aedea3b8" />| | Custom nodes tab | <img width="308" height="748" alt="image" src="https://github.com/user-attachments/assets/2c23bffb-bdaa-4c6c-8cac-7610fb7f3fb7" />| |Api nodes icon | <img width="299" height="523" alt="image" src="https://github.com/user-attachments/assets/e9ca05b0-1143-44cf-b227-6462173c7cd0" />| | node preview|<img width="499" height="544" alt="image" src="https://github.com/user-attachments/assets/8961a7b4-77ae-4e57-99cf-62d9e4e17088" />| | node drag preview | <img width="434" height="289" alt="image" src="https://github.com/user-attachments/assets/b5838c90-65d4-4bee-b2b3-c41b57870da8" />| Test by adding `?nodeRedesign=true` to the URL ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8548-WIP-feat-implement-NodeLibrarySidebarTabV2-with-Reka-UI-components-2fb6d73d36508134b7e0f75a2c9b976a) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: bymyself <cbyrne@comfy.org> |
||
|
|
f2d5bfab73 |
test(browser): refactor browser tests for reliability and maintainability (#8510)
## Summary Major refactoring of browser tests to improve reliability, maintainability, and type safety. ## Changes ### Test Infrastructure Decomposition - Decomposed `ComfyPage.ts` (~1000 lines) into focused helpers: - `CanvasHelper`, `DebugHelper`, `SubgraphHelper`, `NodeOperationsHelper` - `SettingsHelper`, `WorkflowHelper`, `ClipboardHelper`, `KeyboardHelper` - Created `ContextMenu` page object, `BaseDialog` base class, and `BottomPanel` page object - Extracted `DefaultGraphPositions` constants ### Locator Stability - Added `data-testid` attributes to Vue components (sidebar, dialogs, node library) - Created centralized `selectors.ts` with test ID constants - Replaced fragile CSS selectors (`.nth()`, `:nth-child()`) with `getByTestId`/`getByRole` ### Performance & Reliability - Removed `setTimeout` anti-patterns (replaced with `waitForFunction`) - Replaced `waitForTimeout` with retrying assertions - Replaced hardcoded coordinates with computed `NodeReference` positions - Enforced LF line endings for all text files ### Type Safety - Enabled `no-explicit-any` lint rule for browser_tests via oxlint - Purged `as any` casts from browser_tests - Added Window type augmentation for standardized window access - Added proper type annotations throughout ### Bug Fixes - Restored `ExtensionManager` API contract - Removed test-only settings from production schema - Fixed flaky selectors and missing test setup ## Testing - All browser tests pass - Typecheck passes <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Overhauled browser E2E test infrastructure with many new helpers/fixtures, updated test APIs, and CI test container image bumped for consistency. * **Chores** * Standardized line endings and applied stricter lint rules for browser tests; workspace dependency version updated. * **Documentation** * Updated Playwright and TypeScript testing guidance and test-run commands. * **UI** * Added stable data-testids to multiple components to improve testability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
b1b2fd8a4f |
feat: right side panel favorites, no selection state, and more... (#7812)
Most of the features in this pull request are completed and can be
reviewed and merged.
## TODO
- [x] no selection panel
- [x] group selected panel
- [x] tabs
- [x] favorites tab
- [x] global settings tab
- [x] nodes tab
- [x] widget actions menu
- [x] [Bug]: style bugs
- [x] button zoom to the node on canvas.
- [x] rename widgets on widget actions
- [ ] [Bug]: the canvas has not been updated after renaming.
- [x] global settings
- [ ] setting item: "show advanced parameters"
- blocked by other things. skip for now.
- [x] setting item: show toolbox on selection
- [x] setting item: nodes 2.0
- [ ] setting item: "background color"
- blocked by other things. skip for now.
- [x] setting item: grid spacing
- [x] setting item: snap nodes to grid
- [x] setting item: link shape
- [x] setting item: show connected links
- [x] form style reuses the form style of node widgets
- [x] group node cases
- [x] group node settings
- [x] show all nodes in group
- [x] show frame name on nodes when multiple selections are made
- [x] group multiple selections
- [x] [Bug]: nodes without widgets cannot display the location and their
group
- [x] [Bug]: labels layout
- [x] favorites
- [x] the indicator on widgets
- [x] favorite and unfavorite buttons on widgets
- [x] [Bug]: show node name in favorite widgets + improve labels layout
- [ ] [Bug]: After canceling the like, the like list will not be updated
immediately.
- [x] [Bug]: The favorite function does not work for the project on
Subgraph.
- [x] subgraph
- [x] add the node name from where this parameter comes from when node
is subgraph
- [x] show and hide directly on Inputs
- [x] some bugs need to be fixed.
- [x] advanced widgets
- [x] button: show advanced inputs
- Clicking button expands the "Advanced Inputs" section on the right
side panel, regardless of whether the panel is open or not
- [x] [Bug]: style bugs
- [x] advanced inputs section when node is subgraph
- [x] inputs tab rearranging
- [x] favorited inputs rearranging
- [x] subgraph inputs rearranging
- [ ] review and reconstruction to improve complexity and architecture
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7812-feat-right-side-panel-favorites-no-selection-state-and-more-2da6d73d36508134b503d676f9b3d248)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
|
||
|
|
a87bd0eb37 |
feat: position properties panel opposite to sidebar (#7647)
## Problem When sidebar is positioned on the right, the properties panel also appears on the right, causing both panels to compete for space and creating a poor layout. ## Solution Properties panel now dynamically positions itself opposite to the sidebar: - Sidebar left → Properties panel right (default) - Sidebar right → Properties panel left ## Changes - Modified `LiteGraphCanvasSplitterOverlay.vue` to conditionally render properties panel based on sidebar location - Updated splitter refresh key to recalculate layout when sidebar position changes - Added dynamic close button icon in `RightSidePanel.vue` that points in the correct direction ## Testing - Created E2E tests to verify positioning behavior - Manually verified visual behavior in browser ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7647-feat-position-properties-panel-opposite-to-sidebar-2ce6d73d365081049683e74c8d03dbdd) by [Unito](https://www.unito.io) |
||
|
|
f74c176423 |
Cleanup: Properties Panel (#7137)
## Summary - Code cleanup - Copy, padding, color, alignment of components - Subgraph Edit mode changes - Partial fix for the Node Info location (need to do context menu still) - Editing node title ### Still to-do - Bi-directionality in values ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7137-WIP-Cleanup-Properties-Panel-2be6d73d3650813e9430f6bcb09dfb4d) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> |