Skip to content

refactor(assets): delete isAssetAPIEnabled and Comfy.Assets.UseAssetAPI (FE-729)#12322

Open
dante01yoon wants to merge 17 commits into
mainfrom
jaewon/fe-729-delete-is-asset-api-enabled
Open

refactor(assets): delete isAssetAPIEnabled and Comfy.Assets.UseAssetAPI (FE-729)#12322
dante01yoon wants to merge 17 commits into
mainfrom
jaewon/fe-729-delete-is-asset-api-enabled

Conversation

@dante01yoon

@dante01yoon dante01yoon commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

FE-729 (L1.2 — L1 central). Asset API will be always available post-BE-786 (--enable-assets removed from OSS). Delete the gating function, the user-facing setting, the experimental warning, and the toggle command. Simplify caller sites to assume the asset API is available.

Draft — do not merge until BE-786 lands. This PR assumes OSS asset routes are unconditionally registered (which is BE-786's deliverable).

⚠️ MUST REVERT BEFORE MERGE
Commit e48dcd11f temporarily adds --enable-assets to .github/actions/start-comfyui-server/action.yaml so Playwright e2e can validate FE-729's intent (modelStore routes through assetService; legacy /api/models fallback is removed and 503s without the flag).
Once BE-786 lands and the CI ComfyUI image picks up "assets always-on" by default, revert commit e48dcd11f before flipping this PR Ready-for-review. This change must NOT ship to main.

Scope clarification

This PR is the routing-layer half of bringing the Asset Browser dialog to OSS:

  • Routing (sidebarTabStore.ts:81) — Model Library sidebar tab → Asset Browser dialog (always, not gated on a setting)
  • Central gate (assetService.ts:385) — isAssetAPIEnabled() deleted; shouldUseAssetBrowser simplified
  • Settings / commands cleanupComfy.Assets.UseAssetAPI setting + Comfy.ToggleAssetAPI command + EXPERIMENTAL_WARNING removed
  • Dialog content (modal data fetch, filter bar, delete actions): FE-732 (L1.5)
  • Store fetcher (assetsStore.ts:121, 324 Cloud-OSS fork): FE-730 (L1.3)

After this PR + BE-786 land, an OSS user sees the dialog when they click the Model Library sidebar tab, but model content will not be correct until FE-730 + FE-732 + BE-933 also merge. See the scope clarification comment on FE-729 for the full breakdown.

This PR maps directly to the Notion v3 Setting retirement plan (sites #2#5 + EXPERIMENTAL_WARNING rewording) — see Asset FE Divergence Survey M1 Scope.

What OSS users gain immediately after BE-786 + this PR

Once OSS asset routes are unconditionally registered (BE-786) and this PR merges, OSS users see — without any opt-in — the same baseline asset surface Cloud has today:

  • Asset list + browse — Model Library sidebar tab opens the Asset Browser dialog (GET /api/assets)
  • Asset detail / metadata view — info panel for selected models (GET /api/assets/{id})
  • Asset delete + rename + metadata update — sidebar / context-menu / info panel (PUT /api/assets/{id}, DELETE /api/assets/{id})
  • Multipart upload — local file → asset registration (POST /api/assets)
  • Tag management — add / remove tags on assets (POST / DELETE /api/assets/{id}/tags)
  • OSS-only filesystem ops — local directory scanner (/api/assets/seed), prune, content streaming for local files

Cloud-specific features remain absent on OSS and are not delivered by this PR:

  • URL-based import (Civitai / Hugging Face) — Cloud uses /api/assets/download, /api/assets/remote-metadata, /api/assets/import-url; OSS has skeleton only
  • Server-side bulk export (ZIP) — Cloud uses /api/assets/export; OSS lacks the task system for backgrounded archiving
  • Model upload wizard UI — Cloud-only product surface (tag system + async upload + preview generation)

These three Cloud-only capabilities are described in Notion v3 §L2 as asset_url_import_enabled, asset_bulk_export_enabled, asset_model_wizard_enabled and tracked separately — they depend on additional BE work outside this PR's scope.

Changes

Two commits:

1. 7f6d354a8 — delete isAssetAPIEnabled + Comfy.Assets.UseAssetAPI

  • Delete isAssetAPIEnabled() from assetService.ts; simplify shouldUseAssetBrowser() to call isAssetBrowserEligible() directly.
  • Remove the EXPERIMENTAL_WARNING prefix from four asset-load error messages.
  • Drop the Comfy.Assets.UseAssetAPI setting (coreSettings.ts, apiSchema.ts).
  • Remove the Comfy.ToggleAssetAPI command outright. Keep Comfy.BrowseModelAssets but drop its setting-check / setting-set preamble and the Experimental: label prefix; the command now just opens the asset browser dialog.
  • modelStore, sidebarTabStore, assetPreviewUtil, WidgetSelect.vue caller-site simplifications.
  • Test mocks / expectations updated; assetService.test.ts drops the two now-meaningless cloud/setting branch cases.

2. b34026527 — remove isAssetPreviewSupported wrapper + simplify callers

  • Delete isAssetPreviewSupported() from assetPreviewUtil.ts (was a return true wrapper after commit 1).
  • Simplify 5 caller sites: Media3DTop.vue, saveMesh.ts (two blocks), FormDropdownMenuItem.vue, useLoad3d.ts.
  • Clean up 5 test files; remove now-meaningless "asset preview unsupported" branch tests.

Auto-fixed unrelated tailwind class-order lint errors in several files via pnpm lint:fix across both commits to keep CI green.

Review Focus

  • The decision to delete (not flag) was confirmed in Slack thread (2026-05-19) — Matt + Jacob aligned, no --enable-assets, no asset_api_enabled flag.
  • Comfy.BrowseModelAssets is not fully removed: it survives as the entry point that sidebarTabStore invokes when the model-library sidebar tab is clicked, and as a Command Palette entry. Removing it would require inlining the dialog-open logic into sidebarTabStore (couples a store to UI logic). Kept as a small command.
  • useCoreCommands.ts:1287 and :1330-1331 are the setting-check lines named in the FE-729 description.
  • The isAssetPreviewSupported cleanup (commit 2) is follow-up tightening that was originally listed as a follow-up PR; rolled into this PR since the wrapper was newly dead and the caller surface was small.

Done when (status)

  • isAssetAPIEnabled() function deleted
  • Comfy.Assets.UseAssetAPI setting + schema entry removed
  • Comfy.ToggleAssetAPI command removed; Comfy.BrowseModelAssets body simplified
  • EXPERIMENTAL_WARNING removed + 4 caller-site prefixes
  • All isAssetAPIEnabled / Comfy.Assets.UseAssetAPI caller sites simplified
  • Unit / component tests no longer mock the function or setting
  • Cloud-side smoke verified manually (Model Library tab → Asset Browser dialog opens, no regression) — see comment
  • OSS-side smoke — gated on BE-786 merge
  • Revert commit e48dcd11f (--enable-assets in CI action) before merge

Dependencies

  • Blocks-merge: BE-786 — OSS --enable-assets removal. Asset routes must be unconditionally registered before this PR can merge.
  • Coordinates with: BE-933 / BE-934 (L3 — unified asset response shape; required for FE-732's dialog content layer to behave correctly).

Refs

Screenshots (if applicable)

User-visible behavior changes after BE-786 + this PR merge:

  • Browse Model Assets command label no longer says "Experimental:".
  • The Comfy.Assets.UseAssetAPI hidden setting and the Comfy.ToggleAssetAPI command are no longer present in the command palette.
  • On OSS (post BE-786), clicking the Model Library sidebar tab opens the Asset Browser dialog instead of toggling a legacy sidebar panel.

See attached cloud-side verification screenshot in the comment thread.

FE-729 (L1.2 — L1 central). Asset API will be always available
post-BE-786 (`--enable-assets` removed from OSS). Delete the gating
function, the user-facing setting, the experimental warning, and the
toggle command. Simplify all caller sites to assume the asset API
is available.

Changes:
- Delete isAssetAPIEnabled() from assetService.ts; simplify
  shouldUseAssetBrowser() to call isAssetBrowserEligible() directly.
- Remove the EXPERIMENTAL_WARNING prefix from four asset-load error
  messages.
- Drop the Comfy.Assets.UseAssetAPI setting from coreSettings.ts and
  its schema entry in apiSchema.ts.
- Remove the Comfy.ToggleAssetAPI command outright. Keep
  Comfy.BrowseModelAssets but drop its setting-check / setting-set
  preamble and the "Experimental:" label prefix; the command now
  just opens the asset browser dialog.
- modelStore: drop the useAssetAPI fork in createGetModelsFunc and
  loadModelFolders; always route through assetService.
- sidebarTabStore: model-library tab click always routes through
  Comfy.BrowseModelAssets (no more setting check).
- assetPreviewUtil.isAssetPreviewSupported() returns true
  unconditionally; caller-site simplification deferred.
- WidgetSelect.vue: simplify isAssetMode to drop the
  isAssetAPIEnabled() arm of the disjunction.
- Test mocks/expectations updated.

Blocked-merge by BE-786 (OSS `--enable-assets` removal). PR opened
as draft.

Also auto-fixed unrelated tailwind class-order lint errors in four
files (VirtualGrid, RightSidePanel, Textarea, ModelInfoPanel,
WidgetSelectDefault) to keep CI green.
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png is excluded by !**/*.png

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 22d91dcc-e5e7-462e-b6ea-32509396d1f9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The Comfy.Assets.UseAssetAPI feature flag is fully removed: the toggle command, hidden setting, cloud/environment checks, and all conditional branching are deleted. The asset service, model store, sidebar tab handler, WidgetSelect, isAssetPreviewSupported, useLoad3d, saveMesh, Media3DTop, and FormDropdownMenuItem now behave unconditionally as if the asset API is always enabled.

Changes

Asset API Toggle Deprecation

Layer / File(s) Summary
Command and setting removal
src/composables/useCoreCommands.ts, src/platform/settings/constants/coreSettings.ts, src/schemas/apiSchema.ts
Comfy.ToggleAssetAPI command deleted; Comfy.BrowseModelAssets label de-experimentalized and preflight removed; Comfy.Assets.UseAssetAPI removed from CORE_SETTINGS and zSettings.
Asset service contract simplification
src/platform/assets/services/assetService.ts
Cloud/env imports, EXPERIMENTAL_WARNING constant, and isAssetAPIEnabled helper removed from assetService; shouldUseAssetBrowser reduced to eligibility-only; error messages drop the experimental prefix.
Asset service test simplification
src/platform/assets/services/assetService.test.ts
Distribution/settings store mocks removed; cloud/setting-enabled test cases for shouldUseAssetBrowser deleted; eligibility-only cases retained.
Model store always-asset-service migration
src/stores/modelStore.ts
useSettingStore import and settingStore init removed; createGetModelsFunc and loadModelFolders unconditionally use assetService.getAssetModels / getAssetModelFolders.
Model store test suite overhaul
src/stores/modelStore.test.ts
enableMocks helper refactored; all assertions migrated from api.getModels / api.getModelFolders to assetService.getAssetModels / assetService.getAssetModelFolders.
Sidebar tab handler always opens browser
src/stores/workspace/sidebarTabStore.ts
settingStore initialization and UseAssetAPI conditional removed from registerSidebarTab; model-library tab always dispatches Comfy.BrowseModelAssets.
WidgetSelect asset-mode logic update
src/renderer/extensions/vueNodes/widgets/components/WidgetSelect.vue, WidgetSelect.test.ts, WidgetSelect.asset-mode.test.ts
isAssetMode drops isAssetAPIEnabled() gate; now uses shouldUseAssetBrowser(...) OR widget.type === 'asset'; test mocks remove isAssetAPIEnabled.
Asset preview support always enabled
src/platform/assets/utils/assetPreviewUtil.ts, assetPreviewUtil.test.ts
isAssetPreviewSupported() returns true unconditionally; server-feature and isAssetAPIEnabled checks deleted from both implementation and test mocks.
useLoad3d thumbnail capture unconditional
src/composables/useLoad3d.ts, useLoad3d.test.ts
isAssetPreviewSupported import and guard removed; thumbnail capture/persist runs whenever load3d exists; tests consolidated accordingly.
saveMesh thumbnail capture unconditional
src/extensions/core/saveMesh.ts, saveMesh.test.ts
isAssetPreviewSupported import and guards removed from both applySaveGLBOutput and node.onExecuted; capture/persist chains run unconditionally.
Media3DTop server preview unconditional
src/platform/assets/components/Media3DTop.vue, Media3DTop.test.ts
isAssetPreviewSupported import and gate removed; server preview always attempted when asset.name exists; skip-when-unsupported test deleted.
FormDropdownMenuItem mesh preview unconditional
src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuItem.vue, FormDropdownMenuItem.test.ts
isAssetPreviewSupported removed; resolveMeshPreview always calls findServerPreviewUrl; import path updated; tests drop the support-check mock and related case.
CI docs and E2E test adjustments
.github/actions/start-comfyui-server/action.yaml, browser_tests/tests/defaultKeybindings.spec.ts, browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts, browser_tests/fixtures/helpers/BuilderSelectHelper.ts
TODO comment added for --enable-assets flag; model-library keybinding entry and OSS-specific refresh assertion removed; selectInputWidget updated with two-path locator strategy for asset-mode widgets.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • christian-byrne
  • AustinMroz

Poem

🐇 Hippity-hop, the toggle is gone,
No setting to flip, no flag to turn on!
The asset browser opens with glee,
Unconditional and finally free.
One bunny less config, more carrot-y code —
We've lightened the stack on the feature-flag road! 🥕

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main changes: removing isAssetAPIEnabled and Comfy.Assets.UseAssetAPI, with the associated issue reference.
Description check ✅ Passed The description is comprehensive, well-structured, and includes all required sections: summary, changes overview, review focus, dependencies, references, and context about the blocker and temporary commit.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
End-To-End Regression Coverage For Fixes ✅ Passed PR uses "refactor" and "delete/remove" language, not bug-fix language (fix/fixed/fixes/bugfix/hotfix). Check only applies to bug fixes, not refactoring PRs.
Adr Compliance For Entity/Litegraph Changes ✅ Passed No files in src/lib/litegraph/, src/ecs/, or core graph entity infrastructure were modified. PR scope is asset API gating and preview utilities only; no ADR 0003 (command pattern) or ADR 0008 (god-...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jaewon/fe-729-delete-is-asset-api-enabled

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/composables/useCoreCommands.ts`:
- Line 1286: The label "Browse Model Assets" in the command definition inside
useCoreCommands.ts should be localized: import and use the composition API
useI18n() and replace the raw label value with a call to
t('main.browseModelAssets') (or similar key) in the command object where label
is defined, and add the corresponding "browseModelAssets" entry under the main
namespace in src/locales/en/main.json; ensure the key name matches exactly
between the t(...) call and the JSON entry.

In `@src/platform/assets/services/assetService.ts`:
- Line 229: Replace direct schema.parse()/throw of Zod error details with
schema.safeParse() in the asset service locations that currently throw "Invalid
asset response against zod schema" (the occurrences at the existing throw sites
around the message at lines 229 and 448); on failure, log the full Zod parse
error to your internal logger (e.g., processLogger or the module logger) for
debugging, but throw a generic, user-safe error message (e.g., "Invalid asset
response" or a localized equivalent) instead. Ensure the code path that
previously used schema.parse() now checks result.success and uses result.error
for logging only, and update any callers of those parse sites to expect the same
generic Error type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8c90d715-72a7-4165-b3d1-83259c1091ea

📥 Commits

Reviewing files that changed from the base of the PR and between 448ad73 and 7f6d354.

📒 Files selected for processing (18)
  • src/components/common/VirtualGrid.vue
  • src/components/rightSidePanel/RightSidePanel.vue
  • src/components/ui/textarea/Textarea.vue
  • src/composables/useCoreCommands.ts
  • src/platform/assets/components/modelInfo/ModelInfoPanel.vue
  • src/platform/assets/services/assetService.test.ts
  • src/platform/assets/services/assetService.ts
  • src/platform/assets/utils/assetPreviewUtil.test.ts
  • src/platform/assets/utils/assetPreviewUtil.ts
  • src/platform/settings/constants/coreSettings.ts
  • src/renderer/extensions/vueNodes/widgets/components/WidgetSelect.asset-mode.test.ts
  • src/renderer/extensions/vueNodes/widgets/components/WidgetSelect.test.ts
  • src/renderer/extensions/vueNodes/widgets/components/WidgetSelect.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue
  • src/schemas/apiSchema.ts
  • src/stores/modelStore.test.ts
  • src/stores/modelStore.ts
  • src/stores/workspace/sidebarTabStore.ts
💤 Files with no reviewable changes (2)
  • src/schemas/apiSchema.ts
  • src/platform/settings/constants/coreSettings.ts

id: 'Comfy.BrowseModelAssets',
icon: 'pi pi-folder-open',
label: 'Experimental: Browse Model Assets',
label: 'Browse Model Assets',

@coderabbitai coderabbitai Bot May 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize this command label via vue-i18n.

This user-facing label is a raw string and should go through i18n.

💡 Suggested change
-      label: 'Browse Model Assets',
+      label: () => t('commands.browseModelAssets'),
As per coding guidelines, "Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/composables/useCoreCommands.ts` at line 1286, The label "Browse Model
Assets" in the command definition inside useCoreCommands.ts should be localized:
import and use the composition API useI18n() and replace the raw label value
with a call to t('main.browseModelAssets') (or similar key) in the command
object where label is defined, and add the corresponding "browseModelAssets"
entry under the main namespace in src/locales/en/main.json; ensure the key name
matches exactly between the t(...) call and the JSON entry.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid finding, but the raw-string label is a pre-existing pattern on main — this PR only removed the Experimental: prefix, not the i18n gap. Full i18n migration for command labels is out of scope for FE-729 (which targets isAssetAPIEnabled() / Comfy.Assets.UseAssetAPI removal). Tracking as a follow-up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description image shows the ComfyUI Model Library dialog — a dark-themed modal with a left sidebar listing model categories (BEN, BiRefNet, CogVideo, etc.) and a main grid displaying LORA model cards with thumbnails, file names, dates, and "Use" buttons, along with search and filter controls.


@dante01yoon Fair enough — the i18n gap is pre-existing and FE-729 is rightly scoped to the isAssetAPIEnabled / Comfy.Assets.UseAssetAPI removal. Would you like me to open a follow-up GitHub issue to track the full i18n migration for command labels in useCoreCommands.ts?


🧠 Learnings used
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8248
File: src/platform/assets/schemas/assetSchema.ts:109-118
Timestamp: 2026-01-22T23:44:59.888Z
Learning: Do not export declarations (e.g., schemas, constants, types) unless they are actually used elsewhere in the codebase. Keep the public API surface minimal by exporting only items with concrete usage. This reduces coupling and keeps imports explicit. In src/platform/assets/schemas/assetSchema.ts, ensure only necessary exports are exposed; remove unused exports or switch to non-exported/internal where appropriate.

Learnt from: JemiloII
Repo: Comfy-Org/ComfyUI_frontend PR: 8282
File: src/composables/usePaste.test.ts:372-383
Timestamp: 2026-01-23T21:06:15.240Z
Learning: In paste/clipboard handling code, do not rely on ephemeral DataTransfer data across async boundaries. If an operation awaits async tasks (such as node creation) during a paste, clone the DataTransfer (e.g., using cloneDataTransfer) to persist files and string data before the paste event completes. This ensures clipboard contents remain available for subsequent processing.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8722
File: src/composables/maskeditor/useBrushDrawing.ts:5-5
Timestamp: 2026-02-08T03:19:12.009Z
Learning: Treat unresolved import warnings caused by pnpm workspace catalog mappings (pnpm-workspace.yaml) and unplugin-typegpu in vite.config.mts as false positives. Do not flag these in PR reviews; they are expected in sandboxes without node_modules and are not indicative of actual missing dependencies in the TS codebase.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/load3d.ts:427-430
Timestamp: 2026-02-19T02:06:23.468Z
Learning: In TypeScript, you can use typeof SomeClass in type annotation positions (e.g., param: typeof LGraphNode) even when SomeClass is imported with import type. This is a type query that only exists at compile time and is erased in runtime, so it is safe to combine with type-only imports. Apply this pattern to TS files broadly when you need a type that references the shape of a class or constructor function without importing the value at runtime.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/uploadAudio.ts:91-94
Timestamp: 2026-02-19T02:06:38.395Z
Learning: In TypeScript files, you can use a type annotation like 'nodeType: typeof MyClass' even if MyClass is imported via 'import type'. Both the type-only import and 'typeof' operate at the type level and are erased at compile time. This pattern is commonly used for constructor types (e.g., 'nodeType: typeof LGraphNode'). Apply this pattern across TypeScript files in the repository (src/**/*.ts) as appropriate, ensuring the imported symbol is a type-only import when possible for clarity and to avoid runtime imports.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8992
File: src/lib/litegraph/src/widgets/GradientSliderWidget.ts:18-18
Timestamp: 2026-02-20T21:08:19.814Z
Learning: When drawing with CanvasRenderingContext2D in TypeScript/JavaScript, wrap the drawing logic with ctx.save() at the start and ctx.restore() at the end to preserve and restore the canvas state. Do not manually destructure and restore individual properties (e.g., fillStyle, strokeStyle); rely on save/restore to manage state changes in a scoped manner. This should be applied to all TS files that perform canvas drawing.

Learnt from: dante01yoon
Repo: Comfy-Org/ComfyUI_frontend PR: 9075
File: src/scripts/api.featureFlags.test.ts:237-268
Timestamp: 2026-02-22T04:27:33.379Z
Learning: In Vite/Vitest, import.meta.env.DEV is true for any mode that is not 'production' (i.e., DEV is the opposite of PROD, and can be true in 'test', 'development', etc.). Do not assume DEV implies only 'development' mode. When reviewing code and tests, treat DEV as a non-production flag and verify environment-specific logic accordingly. Reference: https://vite.dev/guide/env-and-mode#modes

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9427
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuFilter.vue:33-33
Timestamp: 2026-03-06T00:53:28.835Z
Learning: When reviewing code, note that the enforce-canonical-classes (better-tailwindcss) rule may auto-fix Tailwind v3 !class-name syntax by converting it to v4 class-name! syntax. Do not treat these auto-fixed class-name! instances as newly introduced issues; the perceived change is in syntax placement, not in usage or intent. This guidance applies across all .vue and .ts files in the repository.

Learnt from: sonnybox
Repo: Comfy-Org/ComfyUI_frontend PR: 9446
File: src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue:45-45
Timestamp: 2026-03-06T01:55:00.013Z
Learning: Treat wrap-break-word as a valid Tailwind CSS utility for overflow-wrap: break-word in Tailwind v4+ projects. Do not flag this class as invalid in any Vue (.vue) or TypeScript (.ts/.tsx) files within the repository (e.g., Comfy-Org/ComfyUI_frontend) or other Tailwind v4+ projects. When reviewing, verify that the class is used to enable word breaking in long text content and reference the Tailwind docs: https://tailwindcss.com/docs/overflow-wrap.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 11420
File: src/composables/useCanvasScheduler.ts:20-20
Timestamp: 2026-04-19T22:12:36.981Z
Learning: In the ComfyUI frontend Vue/TypeScript codebase, when implementing singleton composables inside `src/composables/**`, prefer `createSharedComposable` from VueUse over module-scoped `let` variables. Module-scoped `let` can cause SSR cross-request state leakage, unpredictable HMR behavior, and requires manual lifecycle/cleanup management for watchers/computeds/effects. `createSharedComposable` ties the singleton to Vue lifecycle: it disposes watchers/computeds and effect-scope reactivity automatically when the last consumer unmounts, improving testability. Only use the module-scoped `let` pattern if the singleton must intentionally outlive all Vue component trees or if there is a clearly documented reason why `createSharedComposable` is inferior for that specific composable.

Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 11531
File: src/composables/maskeditor/brushDrawingUtils.ts:14-14
Timestamp: 2026-04-22T04:06:01.353Z
Learning: In the Comfy-Org/ComfyUI_frontend codebase, `knip` is used to detect unused exports. When reviewing TypeScript/TSX code, avoid recommending or introducing exported types/functions/constants that have no concrete external consumers (e.g., they’re only used within the same module). Only recommend exporting when there is an actual external usage outside the module. If something might be needed externally later, keep it non-exported for now and defer exporting until an external consumer is added.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 12197
File: src/renderer/extensions/linearMode/AppInput.vue:23-23
Timestamp: 2026-05-14T21:10:45.382Z
Learning: In the ComfyUI_frontend repo, `WidgetEntityId` is a branded template-literal type (e.g., `${UUID}:${NodeId}:${string}`) and should be treated as structurally non-empty when valid—so an empty string is invalid and must be treated as “absent”. When handling optional `entityId?: WidgetEntityId` values (e.g., for Vue props or TS function parameters), guard missing/invalid IDs with a truthy check (e.g., `if (!entityId) return`) rather than `if (entityId === undefined)`. Rationale: `=== undefined` would allow empty-string values through into stores such as `appModeStore.selectedInputs`, creating rows that can’t resolve back to a widget.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 12197
File: src/core/schemas/parseNodePropertyArray.ts:1-1
Timestamp: 2026-05-18T21:34:41.153Z
Learning: In TypeScript, it is correct to use `import type { z } from 'zod'` when the imported symbol `z` is used exclusively in type-annotation/type-only positions (e.g., `schema: z.ZodType<T[]>`, `z.infer<typeof schema>`, `type X = z.AnyZodObject`, etc.). Because `import type` is fully erased at compile time and requires no runtime value, code reviewers should not flag `import type { z }` as an error (e.g., as an unused value import or incorrect runtime import) in TypeScript/React projects when `z` is only referenced in type positions. If `z` is referenced in a value/runtime position, it should instead be imported with a normal `import { z } from 'zod'`.

throw new Error(
`${EXPERIMENTAL_WARNING}Invalid asset response against zod schema:\n${error}`
)
throw new Error(`Invalid asset response against zod schema:\n${error}`)

@coderabbitai coderabbitai Bot May 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid throwing raw schema validation details to callers.

Line 229 and Line 448 include full Zod validation output in thrown errors. Keep detailed parse errors in logs, but throw a generic/localized user-safe message.

💡 Suggested change
-  const error = fromZodError(result.error)
-  throw new Error(`Invalid asset response against zod schema:\n${error}`)
+  const error = fromZodError(result.error)
+  console.error('Invalid asset response:', error)
+  throw new Error(
+    st(
+      'assetBrowser.errorInvalidAssetResponse',
+      'Received invalid asset data from the server. Please try again.'
+    )
+  )
-    const error = result.error
-      ? fromZodError(result.error)
-      : 'Unknown validation error'
-    throw new Error(`Invalid asset response against zod schema:\n${error}`)
+    const error = result.error
+      ? fromZodError(result.error)
+      : 'Unknown validation error'
+    console.error(`Invalid asset response for ${id}:`, error)
+    throw new Error(
+      st(
+        'assetBrowser.errorInvalidAssetResponse',
+        'Received invalid asset data from the server. Please try again.'
+      )
+    )
Based on learnings, "In asset service files under src/platform/assets/services, prefer using schema.safeParse() over schema.parse() for API response validation... while throwing user-friendly errors that do not reveal internal schema details."

Also applies to: 448-448

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/assets/services/assetService.ts` at line 229, Replace direct
schema.parse()/throw of Zod error details with schema.safeParse() in the asset
service locations that currently throw "Invalid asset response against zod
schema" (the occurrences at the existing throw sites around the message at lines
229 and 448); on failure, log the full Zod parse error to your internal logger
(e.g., processLogger or the module logger) for debugging, but throw a generic,
user-safe error message (e.g., "Invalid asset response" or a localized
equivalent) instead. Ensure the code path that previously used schema.parse()
now checks result.success and uses result.error for logging only, and update any
callers of those parse sites to expect the same generic Error type.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid finding, but the fromZodError(...) throw pattern is pre-existing on main — this PR only removed the EXPERIMENTAL_WARNING prefix from the messages, not the parse / log / throw shape. Sanitizing zod detail at the throw site (log full detail, throw a user-safe message) is a worthwhile improvement but out of scope for FE-729. Tracking as a follow-up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dante01yoon, understood — the diff only strips the EXPERIMENTAL_WARNING prefix and doesn't touch the parse/log/throw shape, which is indeed pre-existing. Happy to track the sanitization improvement separately.

Would you like me to open a follow-up GitHub issue for: "Sanitize Zod validation error details at throw sites in assetService — log full detail, throw user-safe message"? I can link back to this PR and comment automatically.


🧠 Learnings used
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/services/assetService.ts:484-491
Timestamp: 2026-01-06T19:20:52.000Z
Learning: In asset service files under src/platform/assets/services, prefer using schema.safeParse() over schema.parse() for API response validation. Follow the established pattern: call safeParse(), check result.success, and if failed, convert the error with fromZodError(result.error) for logging while throwing user-friendly errors that do not reveal internal schema details.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8248
File: src/platform/assets/schemas/assetSchema.ts:109-118
Timestamp: 2026-01-22T23:44:59.888Z
Learning: Do not export declarations (e.g., schemas, constants, types) unless they are actually used elsewhere in the codebase. Keep the public API surface minimal by exporting only items with concrete usage. This reduces coupling and keeps imports explicit. In src/platform/assets/schemas/assetSchema.ts, ensure only necessary exports are exposed; remove unused exports or switch to non-exported/internal where appropriate.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8722
File: src/composables/maskeditor/useBrushDrawing.ts:5-5
Timestamp: 2026-02-08T03:19:12.009Z
Learning: Treat unresolved import warnings caused by pnpm workspace catalog mappings (pnpm-workspace.yaml) and unplugin-typegpu in vite.config.mts as false positives. Do not flag these in PR reviews; they are expected in sandboxes without node_modules and are not indicative of actual missing dependencies in the TS codebase.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/load3d.ts:427-430
Timestamp: 2026-02-19T02:06:23.468Z
Learning: In TypeScript, you can use typeof SomeClass in type annotation positions (e.g., param: typeof LGraphNode) even when SomeClass is imported with import type. This is a type query that only exists at compile time and is erased in runtime, so it is safe to combine with type-only imports. Apply this pattern to TS files broadly when you need a type that references the shape of a class or constructor function without importing the value at runtime.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 8966
File: src/extensions/core/uploadAudio.ts:91-94
Timestamp: 2026-02-19T02:06:38.395Z
Learning: In TypeScript files, you can use a type annotation like 'nodeType: typeof MyClass' even if MyClass is imported via 'import type'. Both the type-only import and 'typeof' operate at the type level and are erased at compile time. This pattern is commonly used for constructor types (e.g., 'nodeType: typeof LGraphNode'). Apply this pattern across TypeScript files in the repository (src/**/*.ts) as appropriate, ensuring the imported symbol is a type-only import when possible for clarity and to avoid runtime imports.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 8992
File: src/lib/litegraph/src/widgets/GradientSliderWidget.ts:18-18
Timestamp: 2026-02-20T21:08:19.814Z
Learning: When drawing with CanvasRenderingContext2D in TypeScript/JavaScript, wrap the drawing logic with ctx.save() at the start and ctx.restore() at the end to preserve and restore the canvas state. Do not manually destructure and restore individual properties (e.g., fillStyle, strokeStyle); rely on save/restore to manage state changes in a scoped manner. This should be applied to all TS files that perform canvas drawing.

Learnt from: dante01yoon
Repo: Comfy-Org/ComfyUI_frontend PR: 9075
File: src/scripts/api.featureFlags.test.ts:237-268
Timestamp: 2026-02-22T04:27:33.379Z
Learning: In Vite/Vitest, import.meta.env.DEV is true for any mode that is not 'production' (i.e., DEV is the opposite of PROD, and can be true in 'test', 'development', etc.). Do not assume DEV implies only 'development' mode. When reviewing code and tests, treat DEV as a non-production flag and verify environment-specific logic accordingly. Reference: https://vite.dev/guide/env-and-mode#modes

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9427
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuFilter.vue:33-33
Timestamp: 2026-03-06T00:53:28.835Z
Learning: When reviewing code, note that the enforce-canonical-classes (better-tailwindcss) rule may auto-fix Tailwind v3 !class-name syntax by converting it to v4 class-name! syntax. Do not treat these auto-fixed class-name! instances as newly introduced issues; the perceived change is in syntax placement, not in usage or intent. This guidance applies across all .vue and .ts files in the repository.

Learnt from: sonnybox
Repo: Comfy-Org/ComfyUI_frontend PR: 9446
File: src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue:45-45
Timestamp: 2026-03-06T01:55:00.013Z
Learning: Treat wrap-break-word as a valid Tailwind CSS utility for overflow-wrap: break-word in Tailwind v4+ projects. Do not flag this class as invalid in any Vue (.vue) or TypeScript (.ts/.tsx) files within the repository (e.g., Comfy-Org/ComfyUI_frontend) or other Tailwind v4+ projects. When reviewing, verify that the class is used to enable word breaking in long text content and reference the Tailwind docs: https://tailwindcss.com/docs/overflow-wrap.

Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 11531
File: src/composables/maskeditor/brushDrawingUtils.ts:14-14
Timestamp: 2026-04-22T04:06:01.353Z
Learning: In the Comfy-Org/ComfyUI_frontend codebase, `knip` is used to detect unused exports. When reviewing TypeScript/TSX code, avoid recommending or introducing exported types/functions/constants that have no concrete external consumers (e.g., they’re only used within the same module). Only recommend exporting when there is an actual external usage outside the module. If something might be needed externally later, keep it non-exported for now and defer exporting until an external consumer is added.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 12197
File: src/renderer/extensions/linearMode/AppInput.vue:23-23
Timestamp: 2026-05-14T21:10:45.382Z
Learning: In the ComfyUI_frontend repo, `WidgetEntityId` is a branded template-literal type (e.g., `${UUID}:${NodeId}:${string}`) and should be treated as structurally non-empty when valid—so an empty string is invalid and must be treated as “absent”. When handling optional `entityId?: WidgetEntityId` values (e.g., for Vue props or TS function parameters), guard missing/invalid IDs with a truthy check (e.g., `if (!entityId) return`) rather than `if (entityId === undefined)`. Rationale: `=== undefined` would allow empty-string values through into stores such as `appModeStore.selectedInputs`, creating rows that can’t resolve back to a widget.

Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 12197
File: src/core/schemas/parseNodePropertyArray.ts:1-1
Timestamp: 2026-05-18T21:34:41.153Z
Learning: In TypeScript, it is correct to use `import type { z } from 'zod'` when the imported symbol `z` is used exclusively in type-annotation/type-only positions (e.g., `schema: z.ZodType<T[]>`, `z.infer<typeof schema>`, `type X = z.AnyZodObject`, etc.). Because `import type` is fully erased at compile time and requires no runtime value, code reviewers should not flag `import type { z }` as an error (e.g., as an unused value import or incorrect runtime import) in TypeScript/React projects when `z` is only referenced in type positions. If `z` is referenced in a value/runtime position, it should instead be imported with a normal `import { z } from 'zod'`.

@codecov

codecov Bot commented May 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.46154% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/extensions/core/saveMesh.ts 83.33% 2 Missing ⚠️
src/platform/assets/services/assetService.ts 66.66% 1 Missing ⚠️
@@             Coverage Diff             @@
##             main   #12322       +/-   ##
===========================================
- Coverage   76.76%   63.03%   -13.73%     
===========================================
  Files        1573     1461      -112     
  Lines      107148    74602    -32546     
  Branches    33135    20983    -12152     
===========================================
- Hits        82248    47025    -35223     
- Misses      23982    27231     +3249     
+ Partials      918      346      -572     
Flag Coverage Δ
e2e ?
unit 63.03% <88.46%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/composables/useCoreCommands.ts 25.18% <ø> (-38.02%) ⬇️
src/composables/useLoad3d.ts 87.97% <100.00%> (-0.02%) ⬇️
src/platform/assets/components/Media3DTop.vue 88.09% <100.00%> (ø)
src/platform/assets/utils/assetPreviewUtil.ts 100.00% <ø> (+9.09%) ⬆️
src/platform/settings/constants/coreSettings.ts 9.37% <ø> (-59.86%) ⬇️
...sions/vueNodes/widgets/components/WidgetSelect.vue 93.47% <100.00%> (-4.44%) ⬇️
.../components/form/dropdown/FormDropdownMenuItem.vue 81.66% <ø> (-4.05%) ⬇️
src/schemas/apiSchema.ts 100.00% <ø> (ø)
src/stores/modelStore.ts 92.00% <100.00%> (-2.90%) ⬇️
src/stores/workspace/sidebarTabStore.ts 67.12% <100.00%> (+0.90%) ⬆️
... and 2 more

... and 1167 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dante01yoon dante01yoon marked this pull request as ready for review May 19, 2026 01:12
@dante01yoon dante01yoon requested a review from a team May 19, 2026 01:12
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label May 19, 2026
… callers

Follow-up to the previous FE-729 commit. After deleting
isAssetAPIEnabled, isAssetPreviewSupported() became a wrapper that
always returned true. Remove the function and simplify all callers.

Changes:
- Delete isAssetPreviewSupported() from assetPreviewUtil.ts.
- Media3DTop.vue: drop the isAssetPreviewSupported() arm of the
  loadThumbnail guard (asset.name check is still required).
- saveMesh.ts: unwrap two `if (isAssetPreviewSupported()) { ... }`
  blocks in applySaveGLBOutput and the SaveGLB beforeRegisterNodeDef
  extension callback.
- FormDropdownMenuItem.vue: drop the early return from
  resolveMeshPreview.
- useLoad3d.ts: drop the isAssetPreviewSupported() arm of the
  modelReady guard.
- Tests: remove the dead "asset preview is unsupported" branches
  (useLoad3d, Media3DTop, FormDropdownMenuItem) and clean up the
  associated mocks and hoisted state.

Auto-fixed unrelated tailwind class-order lint errors in five files
(VirtualGrid, RightSidePanel, Textarea, ModelInfoPanel,
WidgetSelectDefault) to keep CI green.
@dante01yoon dante01yoon requested a review from jtydhr88 as a code owner May 19, 2026 01:27
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels May 19, 2026
@dante01yoon

Copy link
Copy Markdown
Collaborator Author

2026-05-19 — Cloud verification

Verified locally against pnpm dev:cloud-prod (Vite HTTPS dev server with DEV_SERVER_COMFYUI_URL=https://cloud.comfy.org/, attached to authenticated Cloud session via Chrome --remote-debugging-port=9222).

Smoke result on Cloud:

  • ✅ Model Library sidebar tab click → Asset Browser dialog opens (routing layer working as before)
  • ✅ Dialog renders heading "Model Library", All Models / Imported nav, search input, Import button, close button — no regression
  • ✅ Page snapshot confirms dialog modal rooted at the expected accessibility node

Cloud-side shouldUseAssetBrowser caller path manually verified. OSS-side smoke still pending BE-786 merge.

(Screenshot attached separately — see comments.)

@dante01yoon dante01yoon changed the title refactor(assets): delete isAssetAPIEnabled and Comfy.Assets.UseAssetAPI refactor(assets): delete isAssetAPIEnabled and Comfy.Assets.UseAssetAPI (FE-729) May 19, 2026
Temporary unblock for FE-729 e2e while BE-786 is in flight. FE-729
removes the legacy /api/models fallback in modelStore, so the assets
endpoint must be reachable in CI. BE-786 will make assets always-on
in OSS core; once that ships in the CI ComfyUI image, this flag (and
this commit) MUST be reverted before merging FE-729 to main.
After FE-729 the model-library sidebar tab routes to the Asset Browser
dialog and never renders ModelLibrarySidebarTab.vue. The legacy
sidebar-tree tests now validate dead code paths:

- delete browser_tests/tests/sidebar/modelLibrary.spec.ts entirely
  (tab/folders/search/refresh/empty-state — all dead)
- drop the model-library entry from the defaultKeybindings sidebar
  toggle table (KeyM no longer toggles a panel)
- remove the Refresh-clears-resolved-missing-model case from
  errorsTabMissingModels.spec.ts — it waited for the legacy
  /experiment/models endpoint that modelStore no longer hits

Dialog-flow coverage for the new entry point belongs to FE-732.
FE-729 routes model widgets (ckpt_name, lora_name, etc.) through
WidgetSelectDropdown on OSS as well as Cloud. That dropdown's trigger
button has no aria-label — the widget name lives in a sibling label
div ([data-testid="widget-layout-field-label"]). Grid-mode widgets
(WidgetSelectDefault) still carry aria-label on the input directly.

Update the helper to .or() the two strategies so existing aria-label
lookups keep working while asset-mode dropdowns are reached via the
visible label text + sibling trigger button.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@browser_tests/fixtures/helpers/BuilderSelectHelper.ts`:
- Around line 131-133: The fallback label selector uses filter({ hasText:
widgetName }) which does substring matching and can pick the wrong label; change
that call on the locator returned by getByTestId("widget-layout-field-label") to
use an anchored regular expression that matches the entire widgetName (e.g., new
RegExp('^' + escapeRegExp(widgetName) + '$')) so the match is exact; ensure you
add or use an escapeRegExp helper to safely escape any regex metacharacters in
widgetName and keep the final .locator('..') chain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 42a1c44a-0c3e-4976-a9ef-f8d23e3a9c05

📥 Commits

Reviewing files that changed from the base of the PR and between ffe8d0f and 0797b7a.

📒 Files selected for processing (1)
  • browser_tests/fixtures/helpers/BuilderSelectHelper.ts

Comment on lines +131 to +133
.getByTestId("widget-layout-field-label")
.filter({ hasText: widgetName })
.locator('..')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use exact text matching in the fallback label selector

hasText: widgetName is substring-based and can match the wrong field label when names overlap, causing flaky/misdirected clicks. Please make this match exact.

Suggested fix
+function escapeRegExp(value: string): string {
+  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+}
+
   const widgetLocator = node
     .getByLabel(widgetName, { exact: true })
     .or(
       node
         .getByTestId("widget-layout-field-label")
-        .filter({ hasText: widgetName })
+        .filter({ hasText: new RegExp(`^${escapeRegExp(widgetName)}$`) })
         .locator('..')
         .locator('button')
         .first()
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.getByTestId("widget-layout-field-label")
.filter({ hasText: widgetName })
.locator('..')
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
const widgetLocator = node
.getByLabel(widgetName, { exact: true })
.or(
node
.getByTestId("widget-layout-field-label")
.filter({ hasText: new RegExp(`^${escapeRegExp(widgetName)}$`) })
.locator('..')
.locator('button')
.first()
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser_tests/fixtures/helpers/BuilderSelectHelper.ts` around lines 131 -
133, The fallback label selector uses filter({ hasText: widgetName }) which does
substring matching and can pick the wrong label; change that call on the locator
returned by getByTestId("widget-layout-field-label") to use an anchored regular
expression that matches the entire widgetName (e.g., new RegExp('^' +
escapeRegExp(widgetName) + '$')) so the match is exact; ensure you add or use an
escapeRegExp helper to safely escape any regex metacharacters in widgetName and
keep the final .locator('..') chain unchanged.

…Widget

Previous .or() implementation acted as a union — both branches resolved
for number widgets (e.g. 'seed' has aria-label on wrapper + a Decrement
button under widget-layout-field-label), causing Playwright strict mode
violations on tests that previously passed.

Switch to a count-based check: prefer the aria-label match when it
exists, otherwise fall back to the dropdown-trigger button under the
visible label row. This restores grid/number widget behaviour while
keeping asset-mode dropdown support.
dante01yoon added a commit that referenced this pull request May 26, 2026
Align with the L1 stack pattern (FE-729 #12322 / FE-730 #12335 / FE-731 #12375
/ FE-732 #12417): the Generated tab now resolves to `useFlatOutputAssetsGrouped()`
unconditionally. OSS reaches the same asset-API path as cloud once BE-786 lands
and `_ASSETS_ENABLED` is no longer a gate; no transitional fallback is kept, in
line with FE-730's "no temporary isCloud fallback" stance.

FE-740
dante01yoon added a commit that referenced this pull request Jun 2, 2026
…ed feature-flag test infra

Per review (DrJKL): mockFeatureFlags and the exported FeatureFlags type have no live consumer in the open stack — FE-729~732 (#12322/#12335/#12375/#12417) don't use them, and FE-780/781 (#12485/#12486) still hand-roll inline vi.hoisted mocks. Shipping them here adds a public surface with no caller, so split them out; they'll be reintroduced bundled with the first PR that actually adopts the util.

This PR is now scoped to the getAssetStoredFilename extraction, which is consumed at its two call sites in useMediaAssetActions.

Retained-code review fixes:
- M3: spread importOriginal in the distribution/types mock so isDesktop and isNightly survive the wholesale replacement.
- L2: trim getAssetStoredFilename JSDoc; demote the BE-933/934 collapse to a one-line TODO.

Removed: src/test-utils/mockFeatureFlags.ts, the FeatureFlags type export, and its knip ignore entry. Pre-split state preserved on local branch jaewon/fe-733-presplit-snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dante01yoon added a commit that referenced this pull request Jun 2, 2026
…ed feature-flag test infra

Per review (DrJKL): mockFeatureFlags and the exported FeatureFlags type have no live consumer in the open stack — FE-729~732 (#12322/#12335/#12375/#12417) don't use them, and FE-780/781 (#12485/#12486) still hand-roll inline vi.hoisted mocks. Shipping them here adds a public surface with no caller, so split them out; they'll be reintroduced bundled with the first PR that actually adopts the util.

This PR is now scoped to the getAssetStoredFilename extraction, which is consumed at its two call sites in useMediaAssetActions.

Retained-code review fixes:
- M3: spread importOriginal in the distribution/types mock so isDesktop and isNightly survive the wholesale replacement.
- L2: trim getAssetStoredFilename JSDoc; demote the BE-933/934 collapse to a one-line TODO.

Removed: src/test-utils/mockFeatureFlags.ts, the FeatureFlags type export, and its knip ignore entry. Pre-split state preserved on local branch jaewon/fe-733-presplit-snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mathtone pushed a commit to sketchyplans/ComfyUI-frontend-fork1 that referenced this pull request Jun 17, 2026
…fy-Org#12287)

## Summary

L1 prerequisite cleanup, scoped to a single type-preserving refactor:
extract `getAssetStoredFilename(asset)` to collapse the duplicated
`isCloud && asset.asset_hash ? asset.asset_hash : asset.name` branch
from `useMediaAssetActions.ts` into one helper in
`assetMetadataUtils.ts`. No behavior change.

Once BE-933/934 emit `file_path` and the cloud spec sync brings the
field into generated types, only the helper internals change (collapse
to `asset.file_path ?? asset.name`).

## Scope change (per review)

The `mockFeatureFlags` test util and the exported `FeatureFlags` type
that this PR originally also added have been **split out**. They had no
live consumer in the open stack — FE-729~732 (Comfy-Org#12322 / Comfy-Org#12335 / Comfy-Org#12375 /
Comfy-Org#12417) don't use them, and FE-780 / FE-781 (Comfy-Org#12485 / Comfy-Org#12486) still
hand-roll inline `vi.hoisted` mocks — so shipping them here would add a
public surface with no caller. They will be reintroduced bundled with
the first PR that actually adopts the util, where `featureFlag`'s return
type and the "all flags off vs. production defaults" semantics can be
validated against a real consumer.

## Review fixes carried in this PR

- Mock `@/platform/distribution/types` via an `importOriginal` spread so
`isDesktop` / `isNightly` survive the wholesale replacement (only
`isCloud` was re-hoisted before).
- Trimmed the `getAssetStoredFilename` JSDoc; the BE-933/934
future-collapse is now a one-line `TODO` rather than a design-doc
paragraph.

## Review Focus

- The helper is intentionally named `getAssetStoredFilename` to
disambiguate from the existing `getAssetFilename` (which targets
`user_metadata.filename` / `metadata.filename` for serialized-identifier
contexts — missing-model matching, filename schema validation) and
`getAssetDisplayFilename` (UI labels). Folding the `isCloud &&
asset_hash` fallback into either of those would regress
display/identifier sites where the cloud hash is never meant to surface.

- Fixes FE-733
- Parent: FE-601 (L1 umbrella)
- RFC: [Asset Identity
Semantics](https://www.notion.so/comfy-org/RFC-Asset-Identity-Semantics-35a6d73d365080e59d59c98cebae779b)
- Survey: [Asset FE Divergence Survey M1
Scope](https://www.notion.so/comfy-org/Assets-FE-Divergence-Survey-M1-Scope-3616d73d365080d0a9cbf5f2394c12f8)
- Slack thread:
https://comfy-organization.slack.com/archives/C0AUUTS2RQV/p1778815571949519

## Screenshots (if applicable)

N/A — no UI change.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12287-refactor-assets-extract-getAssetStoredFilename-helper-add-mockFeatureFlags-test-util-3616d73d365081c9a1c6e1982728a38a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Matt Miller <matt@miller-media.com>
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

🎭 Playwright: ❌ 1653 passed, 1 failed · 3 flaky

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 1632 / ❌ 1 / ⚠️ 3 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 18 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 06/20/2026, 12:00:24 AM UTC

Links

📦 Bundle: 7.45 MB gzip 🟢 -660 B

Details

Summary

  • Raw size: 31.3 MB baseline 31.3 MB — 🟢 -2.4 kB
  • Gzip: 7.45 MB baseline 7.45 MB — 🟢 -660 B
  • Brotli: 5.1 MB baseline 5.1 MB — 🟢 -726 B
  • Bundles: 279 current • 279 baseline • 125 added / 125 removed

Category Glance
Graph Workspace 🟢 -1.07 kB (1.25 MB) · Utilities & Hooks 🟢 -1 kB (3.32 MB) · Other 🟢 -286 B (10.4 MB) · Data & Services 🟢 -44 B (268 kB) · Vendor & Third-Party ⚪ 0 B (15.3 MB) · Panels & Settings ⚪ 0 B (525 kB) · + 5 more

App Entry Points — 46.7 kB (baseline 46.7 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-CCQBAlTY.js (new) 46.7 kB 🔴 +46.7 kB 🔴 +13.7 kB 🔴 +11.8 kB
assets/index-DyBAKgcv.js (removed) 46.7 kB 🟢 -46.7 kB 🟢 -13.7 kB 🟢 -11.8 kB

Status: 1 added / 1 removed

Graph Workspace — 1.25 MB (baseline 1.25 MB) • 🟢 -1.07 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-TF-UPE56.js (removed) 1.25 MB 🟢 -1.25 MB 🟢 -266 kB 🟢 -200 kB
assets/GraphView-BEQ78PgC.js (new) 1.25 MB 🔴 +1.25 MB 🔴 +266 kB 🔴 +200 kB

Status: 1 added / 1 removed

Views & Navigation — 95.3 kB (baseline 95.3 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-CgS9A5QF.js (removed) 19.5 kB 🟢 -19.5 kB 🟢 -5.06 kB 🟢 -4.49 kB
assets/CloudSurveyView-Dvp2uNWG.js (new) 19.5 kB 🔴 +19.5 kB 🔴 +5.06 kB 🔴 +4.49 kB
assets/CloudLoginView-C__qHr_U.js (new) 11.4 kB 🔴 +11.4 kB 🔴 +3.06 kB 🔴 +2.68 kB
assets/CloudLoginView-ds5Pc5Dw.js (removed) 11.4 kB 🟢 -11.4 kB 🟢 -3.06 kB 🟢 -2.67 kB
assets/CloudSignupView-CSa6bwk7.js (new) 9.7 kB 🔴 +9.7 kB 🔴 +2.71 kB 🔴 +2.37 kB
assets/CloudSignupView-sTK6yTeu.js (removed) 9.7 kB 🟢 -9.7 kB 🟢 -2.71 kB 🟢 -2.38 kB
assets/CloudLayoutView-BKbxuwc9.js (removed) 9.36 kB 🟢 -9.36 kB 🟢 -2.34 kB 🟢 -2.03 kB
assets/CloudLayoutView-Bkoz_ymb.js (new) 9.36 kB 🔴 +9.36 kB 🔴 +2.34 kB 🔴 +2.02 kB
assets/UserCheckView-BEtHerdS.js (removed) 8.8 kB 🟢 -8.8 kB 🟢 -2.22 kB 🟢 -1.92 kB
assets/UserCheckView-DjMiKpyo.js (new) 8.8 kB 🔴 +8.8 kB 🔴 +2.22 kB 🔴 +1.93 kB
assets/UserSelectView-B9yh6V2a.js (removed) 6 kB 🟢 -6 kB 🟢 -2.15 kB 🟢 -1.88 kB
assets/UserSelectView-BsiGN9jN.js (new) 6 kB 🔴 +6 kB 🔴 +2.15 kB 🔴 +1.88 kB
assets/CloudForgotPasswordView-D_UBRg9E.js (new) 5.15 kB 🔴 +5.15 kB 🔴 +1.75 kB 🔴 +1.53 kB
assets/CloudForgotPasswordView-Vt8k_-D9.js (removed) 5.15 kB 🟢 -5.15 kB 🟢 -1.75 kB 🟢 -1.53 kB
assets/CloudAuthTimeoutView-CwYTL1Ie.js (new) 4.49 kB 🔴 +4.49 kB 🔴 +1.57 kB 🔴 +1.37 kB
assets/CloudAuthTimeoutView-Dec0tliv.js (removed) 4.49 kB 🟢 -4.49 kB 🟢 -1.57 kB 🟢 -1.36 kB
assets/CloudSubscriptionRedirectView-BBgKjpgo.js (removed) 4.3 kB 🟢 -4.3 kB 🟢 -1.57 kB 🟢 -1.39 kB
assets/CloudSubscriptionRedirectView-Bfdf5Uud.js (new) 4.3 kB 🔴 +4.3 kB 🔴 +1.57 kB 🔴 +1.38 kB

Status: 9 added / 9 removed / 3 unchanged

Panels & Settings — 525 kB (baseline 525 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-DJRy46cy.js (removed) 49.4 kB 🟢 -49.4 kB 🟢 -9.96 kB 🟢 -8.81 kB
assets/KeybindingPanel-kR68wgO8.js (new) 49.4 kB 🔴 +49.4 kB 🔴 +9.96 kB 🔴 +8.81 kB
assets/SecretsPanel-DtiIsXfo.js (removed) 24.2 kB 🟢 -24.2 kB 🟢 -5.76 kB 🟢 -5.06 kB
assets/SecretsPanel-yXh0cjyz.js (new) 24.2 kB 🔴 +24.2 kB 🔴 +5.77 kB 🔴 +5.06 kB
assets/LegacyCreditsPanel-BwWbpoSY.js (new) 20.9 kB 🔴 +20.9 kB 🔴 +5.52 kB 🔴 +4.86 kB
assets/LegacyCreditsPanel-DQWOqCeI.js (removed) 20.9 kB 🟢 -20.9 kB 🟢 -5.52 kB 🟢 -4.85 kB
assets/SubscriptionPanel-Cfp7_Tgm.js (removed) 19.2 kB 🟢 -19.2 kB 🟢 -5.04 kB 🟢 -4.42 kB
assets/SubscriptionPanel-DcnK26F5.js (new) 19.2 kB 🔴 +19.2 kB 🔴 +5.04 kB 🔴 +4.42 kB
assets/AboutPanel-C32Ed8b-.js (new) 11.7 kB 🔴 +11.7 kB 🔴 +3.22 kB 🔴 +2.89 kB
assets/AboutPanel-CYVTuvYv.js (removed) 11.7 kB 🟢 -11.7 kB 🟢 -3.22 kB 🟢 -2.89 kB
assets/ExtensionPanel-_gYBBgzV.js (new) 9.03 kB 🔴 +9.03 kB 🔴 +2.49 kB 🔴 +2.2 kB
assets/ExtensionPanel-ByD9oAOS.js (removed) 9.03 kB 🟢 -9.03 kB 🟢 -2.49 kB 🟢 -2.2 kB
assets/ServerConfigPanel-1JHoEQG5.js (new) 6.15 kB 🔴 +6.15 kB 🔴 +1.98 kB 🔴 +1.77 kB
assets/ServerConfigPanel-BLkkAF6l.js (removed) 6.15 kB 🟢 -6.15 kB 🟢 -1.98 kB 🟢 -1.76 kB
assets/UserPanel-C1axuD0C.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.82 kB 🔴 +1.58 kB
assets/UserPanel-CQmG6_m0.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.82 kB 🟢 -1.58 kB
assets/refreshRemoteConfig-1Cu85nva.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.06 kB 🟢 -944 B
assets/refreshRemoteConfig-s0CXstGn.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.06 kB 🔴 +942 B
assets/cloudRemoteConfig-4S-C6UhR.js (new) 990 B 🔴 +990 B 🔴 +538 B 🔴 +440 B
assets/cloudRemoteConfig-CR2SZfHz.js (removed) 990 B 🟢 -990 B 🟢 -539 B 🟢 -464 B
assets/refreshRemoteConfig-BM-6Dhcl.js (removed) 110 B 🟢 -110 B 🟢 -89 B 🟢 -83 B
assets/refreshRemoteConfig-C9qpjVrj.js (new) 110 B 🔴 +110 B 🔴 +89 B 🔴 +81 B

Status: 11 added / 11 removed / 15 unchanged

User & Accounts — 19.9 kB (baseline 19.9 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-CFMMYcQS.js (removed) 3.69 kB 🟢 -3.69 kB 🟢 -1.31 kB 🟢 -1.14 kB
assets/auth-J5zs-jDO.js (new) 3.69 kB 🔴 +3.69 kB 🔴 +1.31 kB 🔴 +1.13 kB
assets/usePostAuthRedirect-BwgpaQWI.js (new) 3.33 kB 🔴 +3.33 kB 🔴 +1.28 kB 🔴 +1.11 kB
assets/usePostAuthRedirect-C95yx_ZU.js (removed) 3.33 kB 🟢 -3.33 kB 🟢 -1.28 kB 🟢 -1.11 kB
assets/SignUpForm-B9iwQG4f.js (removed) 3.19 kB 🟢 -3.19 kB 🟢 -1.29 kB 🟢 -1.15 kB
assets/SignUpForm-CKyeQ25i.js (new) 3.19 kB 🔴 +3.19 kB 🔴 +1.29 kB 🔴 +1.16 kB
assets/UpdatePasswordContent-DmcC7df_.js (removed) 1.92 kB 🟢 -1.92 kB 🟢 -876 B 🟢 -765 B
assets/UpdatePasswordContent-DTGv4KfQ.js (new) 1.92 kB 🔴 +1.92 kB 🔴 +876 B 🔴 +767 B
assets/authStore-BpfDsQ9j.js (new) 130 B 🔴 +130 B 🔴 +109 B 🔴 +112 B
assets/authStore-DMtf3DM3.js (removed) 130 B 🟢 -130 B 🟢 -109 B 🟢 -108 B
assets/auth--sLfzbH1.js (removed) 105 B 🟢 -105 B 🟢 -96 B 🟢 -88 B
assets/auth-B183AoZB.js (new) 105 B 🔴 +105 B 🔴 +96 B 🔴 +85 B

Status: 6 added / 6 removed / 3 unchanged

Editors & Dialogs — 112 kB (baseline 112 kB) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-BkmN7D0z.js (new) 86 kB 🔴 +86 kB 🔴 +18.6 kB 🔴 +15.8 kB
assets/ComfyHubPublishDialog-CGdLzMl0.js (removed) 86 kB 🟢 -86 kB 🟢 -18.6 kB 🟢 -15.9 kB
assets/useShareDialog-BmCVyUfD.js (new) 23.4 kB 🔴 +23.4 kB 🔴 +5.54 kB 🔴 +4.91 kB
assets/useShareDialog-DJA6qP_l.js (removed) 23.4 kB 🟢 -23.4 kB 🟢 -5.54 kB 🟢 -4.94 kB
assets/ComfyHubPublishDialog-BvOs2osT.js (new) 143 B 🔴 +143 B 🔴 +105 B 🔴 +90 B
assets/ComfyHubPublishDialog-CIosrzGV.js (removed) 143 B 🟢 -143 B 🟢 -105 B 🟢 -90 B
assets/useSubscriptionDialog-CqbIc9IW.js (removed) 110 B 🟢 -110 B 🟢 -102 B 🟢 -99 B
assets/useSubscriptionDialog-DlpaYQnb.js (new) 110 B 🔴 +110 B 🔴 +102 B 🔴 +93 B

Status: 4 added / 4 removed / 1 unchanged

UI Components — 57.2 kB (baseline 57.2 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-D_yrY5GX.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.83 kB 🟢 -3.42 kB
assets/ComfyQueueButton-DOPEHzL_.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.82 kB 🔴 +3.42 kB
assets/useTerminalTabs-C36Fo3uf.js (new) 12.1 kB 🔴 +12.1 kB 🔴 +3.84 kB 🔴 +3.39 kB
assets/useTerminalTabs-Dx69qWfd.js (removed) 12.1 kB 🟢 -12.1 kB 🟢 -3.84 kB 🟢 -3.38 kB
assets/SubscribeButton-CABKWgQ7.js (new) 2.44 kB 🔴 +2.44 kB 🔴 +1.05 kB 🔴 +929 B
assets/SubscribeButton-Cj9txs22.js (removed) 2.44 kB 🟢 -2.44 kB 🟢 -1.05 kB 🟢 -950 B
assets/cloudFeedbackTopbarButton-BOGpRvn1.js (new) 829 B 🔴 +829 B 🔴 +496 B 🔴 +453 B
assets/cloudFeedbackTopbarButton-C3y4UiLk.js (removed) 829 B 🟢 -829 B 🟢 -497 B 🟢 -451 B
assets/ComfyQueueButton-CgJSjxuM.js (removed) 128 B 🟢 -128 B 🟢 -99 B 🟢 -90 B
assets/ComfyQueueButton-DYjPqQZj.js (new) 128 B 🔴 +128 B 🔴 +99 B 🔴 +92 B

Status: 5 added / 5 removed / 8 unchanged

Data & Services — 268 kB (baseline 268 kB) • 🟢 -44 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/load3dService-CAnOAvzJ.js (new) 127 kB 🔴 +127 kB 🔴 +27.9 kB 🔴 +23.6 kB
assets/load3dService-DVbsBY8w.js (removed) 127 kB 🟢 -127 kB 🟢 -27.9 kB 🟢 -23.6 kB
assets/api-CBIO03nf.js (removed) 83.9 kB 🟢 -83.9 kB 🟢 -22.5 kB 🟢 -19.4 kB
assets/api-Q1OqLFO7.js (new) 83.9 kB 🔴 +83.9 kB 🔴 +22.5 kB 🔴 +19.4 kB
assets/workflowShareService-D4BY1O9D.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.91 kB 🔴 +4.35 kB
assets/workflowShareService-nTA_fnB8.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.91 kB 🟢 -4.35 kB
assets/keybindingService-4ubTTXlu.js (removed) 13.8 kB 🟢 -13.8 kB 🟢 -3.67 kB 🟢 -3.23 kB
assets/keybindingService-CJ7FU9zp.js (new) 13.8 kB 🔴 +13.8 kB 🔴 +3.67 kB 🔴 +3.23 kB
assets/releaseStore-CgoxR-in.js (removed) 8.29 kB 🟢 -8.29 kB 🟢 -2.33 kB 🟢 -2.04 kB
assets/releaseStore-vbg1IQag.js (new) 8.29 kB 🔴 +8.29 kB 🔴 +2.34 kB 🔴 +2.04 kB
assets/extensionStore-B1Wk1VOg.js (new) 5.29 kB 🔴 +5.29 kB 🔴 +1.87 kB 🔴 +1.58 kB
assets/extensionStore-C1UFm5y2.js (removed) 5.29 kB 🟢 -5.29 kB 🟢 -1.86 kB 🟢 -1.58 kB
assets/userStore-Bh6jPw8-.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -929 B 🟢 -824 B
assets/userStore-Cb4Zp79b.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +933 B 🔴 +823 B
assets/audioService-DKCs0pUP.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -862 B 🟢 -748 B
assets/audioService-Dz1zeMw5.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +863 B 🔴 +749 B
assets/dialogService-B5wQFilb.js (removed) 100 B 🟢 -100 B 🟢 -99 B 🟢 -98 B
assets/dialogService-CkntFeuc.js (new) 100 B 🔴 +100 B 🔴 +99 B 🔴 +95 B
assets/settingStore--6RXat30.js (removed) 98 B 🟢 -98 B 🟢 -98 B 🟢 -96 B
assets/settingStore-Br3Tl5td.js (new) 98 B 🔴 +98 B 🔴 +98 B 🔴 +93 B
assets/assetsStore-BjPKvxFQ.js (removed) 96 B 🟢 -96 B 🟢 -97 B 🟢 -87 B
assets/assetsStore-Bpw3mQcO.js (new) 96 B 🔴 +96 B 🔴 +97 B 🔴 +100 B
assets/releaseStore-DuE6pjsN.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +83 B
assets/releaseStore-X4-WbwTb.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -86 B
assets/api-BKaadS-F.js (removed) 62 B 🟢 -62 B 🟢 -74 B 🟢 -66 B
assets/api-BY-j-SeS.js (new) 62 B 🔴 +62 B 🔴 +74 B 🔴 +66 B

Status: 13 added / 13 removed / 3 unchanged

Utilities & Hooks — 3.32 MB (baseline 3.32 MB) • 🟢 -1 kB

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/promotionUtils-DGYi9Bmt.js (removed) 2.97 MB 🟢 -2.97 MB 🟢 -686 kB 🟢 -517 kB
assets/promotionUtils-Cu4-oC2I.js (new) 2.97 MB 🔴 +2.97 MB 🔴 +686 kB 🔴 +517 kB
assets/useConflictDetection-3rRS5QSG.js (new) 233 kB 🔴 +233 kB 🔴 +52 kB 🔴 +42.4 kB
assets/useConflictDetection-CYYuuVk-.js (removed) 233 kB 🟢 -233 kB 🟢 -52 kB 🟢 -42.3 kB
assets/useLoad3d-DYR29UZB.js (removed) 25.5 kB 🟢 -25.5 kB 🟢 -5.76 kB 🟢 -5.1 kB
assets/useLoad3d-9LVY_MMz.js (new) 25.4 kB 🔴 +25.4 kB 🔴 +5.74 kB 🔴 +5.08 kB
assets/useLoad3dViewer-CvPqkO2b.js (new) 21.1 kB 🔴 +21.1 kB 🔴 +4.98 kB 🔴 +4.35 kB
assets/useLoad3dViewer-Cxhl1JkN.js (removed) 21.1 kB 🟢 -21.1 kB 🟢 -4.98 kB 🟢 -4.36 kB
assets/useFeatureFlags-B0koheD4.js (new) 5.37 kB 🔴 +5.37 kB 🔴 +1.66 kB 🔴 +1.41 kB
assets/useFeatureFlags-WqaT-QBK.js (removed) 5.37 kB 🟢 -5.37 kB 🟢 -1.65 kB 🟢 -1.4 kB
assets/useSessionCookie-BS16qjse.js (removed) 3.33 kB 🟢 -3.33 kB 🟢 -1.15 kB 🟢 -978 B
assets/useSessionCookie-DrzMCI63.js (new) 3.33 kB 🔴 +3.33 kB 🔴 +1.15 kB 🔴 +980 B
assets/subscriptionCheckoutUtil-C_925QTA.js (removed) 3.31 kB 🟢 -3.31 kB 🟢 -1.37 kB 🟢 -1.19 kB
assets/subscriptionCheckoutUtil-DY9vzq7X.js (new) 3.31 kB 🔴 +3.31 kB 🔴 +1.36 kB 🔴 +1.19 kB
assets/assetPreviewUtil-BnYfyBG-.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -1.01 kB 🟢 -877 B
assets/assetPreviewUtil-CXFR6hFJ.js (new) 2.26 kB 🔴 +2.26 kB 🔴 +952 B 🔴 +831 B
assets/useUpstreamValue-B3MjbD2Y.js (removed) 2.04 kB 🟢 -2.04 kB 🟢 -794 B 🟢 -704 B
assets/useUpstreamValue-BZTc2amX.js (new) 2.04 kB 🔴 +2.04 kB 🔴 +793 B 🔴 +714 B
assets/useWorkspaceSwitch-2iV4_Wpm.js (removed) 748 B 🟢 -748 B 🟢 -387 B 🟢 -336 B
assets/useWorkspaceSwitch-pJ5UO5rc.js (new) 748 B 🔴 +748 B 🔴 +384 B 🔴 +336 B
assets/useLoad3d-D85xoJXk.js (new) 311 B 🔴 +311 B 🔴 +165 B 🔴 +147 B
assets/useLoad3d-Ds-99E9n.js (removed) 311 B 🟢 -311 B 🟢 -165 B 🟢 -151 B
assets/useSessionCookie-CfX2rHAx.js (new) 101 B 🔴 +101 B 🔴 +86 B 🔴 +79 B
assets/useSessionCookie-LfMmiDhX.js (removed) 101 B 🟢 -101 B 🟢 -86 B 🟢 -78 B
assets/useLoad3dViewer-C0p0PZ88.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +82 B
assets/useLoad3dViewer-C7JBacBl.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -87 B
assets/useCurrentUser-CLFVsw2k.js (removed) 96 B 🟢 -96 B 🟢 -97 B 🟢 -93 B
assets/useCurrentUser-NKHngUj3.js (new) 96 B 🔴 +96 B 🔴 +97 B 🔴 +89 B

Status: 14 added / 14 removed / 16 unchanged

Vendor & Third-Party — 15.3 MB (baseline 15.3 MB) • ⚪ 0 B

External libraries and shared vendor chunks

Status: 16 unchanged

Other — 10.4 MB (baseline 10.4 MB) • 🟢 -286 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-B21JwCQs.js (new) 118 kB 🔴 +118 kB 🔴 +30.4 kB 🔴 +25.7 kB
assets/core-tKpNtiAQ.js (removed) 118 kB 🟢 -118 kB 🟢 -30.4 kB 🟢 -25.7 kB
assets/WidgetSelect-C4rE9Xuk.js (removed) 82.6 kB 🟢 -82.6 kB 🟢 -18.1 kB 🟢 -15.7 kB
assets/WidgetSelect-BSXepcU6.js (new) 82.5 kB 🔴 +82.5 kB 🔴 +18 kB 🔴 +15.7 kB
assets/SubscriptionRequiredDialogContentWorkspace-B1QU4C9f.js (removed) 47.8 kB 🟢 -47.8 kB 🟢 -9.08 kB 🟢 -7.84 kB
assets/SubscriptionRequiredDialogContentWorkspace-CKCIFzpo.js (new) 47.8 kB 🔴 +47.8 kB 🔴 +9.08 kB 🔴 +7.83 kB
assets/Load3DControls-C9ZXcqZ1.js (new) 46.8 kB 🔴 +46.8 kB 🔴 +7.57 kB 🔴 +6.61 kB
assets/Load3DControls-Ck01CUzL.js (removed) 46.8 kB 🟢 -46.8 kB 🟢 -7.57 kB 🟢 -6.62 kB
assets/WorkspacePanelContent-BREeaz6L.js (new) 33.3 kB 🔴 +33.3 kB 🔴 +6.97 kB 🔴 +6.17 kB
assets/WorkspacePanelContent-tNOc6xCR.js (removed) 33.3 kB 🟢 -33.3 kB 🟢 -6.96 kB 🟢 -6.18 kB
assets/WidgetPainter-Blxkso8u.js (removed) 32.6 kB 🟢 -32.6 kB 🟢 -7.86 kB 🟢 -7 kB
assets/WidgetPainter-D85ijuSa.js (new) 32.6 kB 🔴 +32.6 kB 🔴 +7.86 kB 🔴 +6.98 kB
assets/Load3dViewerContent-DCPFAYfo.js (new) 30.9 kB 🔴 +30.9 kB 🔴 +6.31 kB 🔴 +5.47 kB
assets/Load3dViewerContent-mN9uyLjS.js (removed) 30.9 kB 🟢 -30.9 kB 🟢 -6.31 kB 🟢 -5.47 kB
assets/SubscriptionRequiredDialogContent-DqZByOLR.js (removed) 26.6 kB 🟢 -26.6 kB 🟢 -6.56 kB 🟢 -5.8 kB
assets/SubscriptionRequiredDialogContent-fUP3-3gj.js (new) 26.6 kB 🔴 +26.6 kB 🔴 +6.56 kB 🔴 +5.8 kB
assets/WidgetImageCrop-BxTH9seI.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.75 kB 🟢 -5.04 kB
assets/WidgetImageCrop-CnVobrYr.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.74 kB 🔴 +5.04 kB
assets/SubscriptionPanelContentWorkspace-CNvN7Sgo.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -5.2 kB 🟢 -4.58 kB
assets/SubscriptionPanelContentWorkspace-D86leHZs.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +5.2 kB 🔴 +4.58 kB
assets/load3d-BTHNo90_.js (removed) 21.2 kB 🟢 -21.2 kB 🟢 -5.18 kB 🟢 -4.5 kB
assets/load3d-DsxtnY9l.js (new) 21.2 kB 🔴 +21.2 kB 🔴 +5.18 kB 🔴 +4.5 kB
assets/CurrentUserPopoverWorkspace-Bf7SCi1C.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -4.7 kB 🟢 -4.2 kB
assets/CurrentUserPopoverWorkspace-Cg-ug_qj.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +4.7 kB 🔴 +4.2 kB
assets/SignInContent-DKUlUYSV.js (new) 19.9 kB 🔴 +19.9 kB 🔴 +5 kB 🔴 +4.36 kB
assets/SignInContent-zG8sjXpD.js (removed) 19.9 kB 🟢 -19.9 kB 🟢 -5 kB 🟢 -4.36 kB
assets/Load3D-ChewAli4.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.52 kB 🔴 +3.94 kB
assets/Load3D-DgVM4Yq1.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.52 kB 🟢 -3.94 kB
assets/WidgetInputNumber-dmgAKca5.js (removed) 19 kB 🟢 -19 kB 🟢 -4.79 kB 🟢 -4.25 kB
assets/WidgetInputNumber-uYOPDadO.js (new) 19 kB 🔴 +19 kB 🔴 +4.79 kB 🔴 +4.25 kB
assets/WidgetRecordAudio-_dXVex2R.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.63 kB 🔴 +4.14 kB
assets/WidgetRecordAudio-B1JWeQFT.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.63 kB 🟢 -4.14 kB
assets/WidgetRange-B8uGwnI9.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +4.17 kB 🔴 +3.73 kB
assets/WidgetRange-qkWYwf13.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -4.17 kB 🟢 -3.73 kB
assets/WaveAudioPlayer-CdTJV1-U.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.48 kB 🟢 -3.05 kB
assets/WaveAudioPlayer-CPN3CaGz.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.49 kB 🔴 +3.06 kB
assets/WidgetCurve-BEJZvatV.js (new) 11.3 kB 🔴 +11.3 kB 🔴 +3.51 kB 🔴 +3.17 kB
assets/WidgetCurve-wizNnqOs.js (removed) 11.3 kB 🟢 -11.3 kB 🟢 -3.51 kB 🟢 -3.17 kB
assets/TeamWorkspacesDialogContent-CtaV_ROh.js (new) 10.4 kB 🔴 +10.4 kB 🔴 +3.01 kB 🔴 +2.67 kB
assets/TeamWorkspacesDialogContent-Tk7fr49I.js (removed) 10.4 kB 🟢 -10.4 kB 🟢 -3.01 kB 🟢 -2.68 kB
assets/Load3DConfiguration-BP19gSgW.js (new) 9.02 kB 🔴 +9.02 kB 🔴 +2.67 kB 🔴 +2.35 kB
assets/Load3DConfiguration-kT03vmwG.js (removed) 9.02 kB 🟢 -9.02 kB 🟢 -2.66 kB 🟢 -2.35 kB
assets/nodeTemplates-CBNxE3do.js (new) 8.33 kB 🔴 +8.33 kB 🔴 +2.88 kB 🔴 +2.55 kB
assets/nodeTemplates-CRlsYrLa.js (removed) 8.33 kB 🟢 -8.33 kB 🟢 -2.88 kB 🟢 -2.55 kB
assets/onboardingCloudRoutes-By_BRhzS.js (new) 8.2 kB 🔴 +8.2 kB 🔴 +2.54 kB 🔴 +2.18 kB
assets/onboardingCloudRoutes-pwY2j868.js (removed) 8.2 kB 🟢 -8.2 kB 🟢 -2.54 kB 🟢 -2.19 kB
assets/NightlySurveyController-SI3ZEUOl.js (new) 7.95 kB 🔴 +7.95 kB 🔴 +2.7 kB 🔴 +2.37 kB
assets/NightlySurveyController-t_OL77k4.js (removed) 7.95 kB 🟢 -7.95 kB 🟢 -2.7 kB 🟢 -2.4 kB
assets/InviteMemberDialogContent-7g-KdClD.js (removed) 7.03 kB 🟢 -7.03 kB 🟢 -2.14 kB 🟢 -1.85 kB
assets/InviteMemberDialogContent-DlIsgDAX.js (new) 7.03 kB 🔴 +7.03 kB 🔴 +2.13 kB 🔴 +1.85 kB
assets/WidgetWithControl-CiN0X32p.js (new) 6.3 kB 🔴 +6.3 kB 🔴 +2.54 kB 🔴 +2.27 kB
assets/WidgetWithControl-DP8JiFSl.js (removed) 6.3 kB 🟢 -6.3 kB 🟢 -2.54 kB 🟢 -2.23 kB
assets/load3dPreviewExtensions-DHieEUtH.js (new) 5.38 kB 🔴 +5.38 kB 🔴 +1.76 kB 🔴 +1.55 kB
assets/load3dPreviewExtensions-DPWMLzRF.js (removed) 5.38 kB 🟢 -5.38 kB 🟢 -1.76 kB 🟢 -1.55 kB
assets/CreateWorkspaceDialogContent-BEjSs7Wb.js (removed) 5.19 kB 🟢 -5.19 kB 🟢 -1.83 kB 🟢 -1.59 kB
assets/CreateWorkspaceDialogContent-BJ50MDbO.js (new) 5.19 kB 🔴 +5.19 kB 🔴 +1.83 kB 🔴 +1.59 kB
assets/missingModelDownload-BzbQZr5n.js (removed) 5.07 kB 🟢 -5.07 kB 🟢 -1.98 kB 🟢 -1.72 kB
assets/missingModelDownload-DGmFsb_x.js (new) 5.07 kB 🔴 +5.07 kB 🔴 +1.98 kB 🔴 +1.72 kB
assets/FreeTierDialogContent-CMtourfQ.js (removed) 5.02 kB 🟢 -5.02 kB 🟢 -1.69 kB 🟢 -1.49 kB
assets/FreeTierDialogContent-DvDRthyk.js (new) 5.02 kB 🔴 +5.02 kB 🔴 +1.69 kB 🔴 +1.49 kB
assets/EditWorkspaceDialogContent-C5PkENe-.js (new) 5 kB 🔴 +5 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/EditWorkspaceDialogContent-DfmgjxQo.js (removed) 5 kB 🟢 -5 kB 🟢 -1.79 kB 🟢 -1.56 kB
assets/WidgetTextarea-DkE8XtrF.js (removed) 4.86 kB 🟢 -4.86 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/WidgetTextarea-UOth-rJa.js (new) 4.86 kB 🔴 +4.86 kB 🔴 +1.89 kB 🔴 +1.66 kB
assets/saveMesh-CiEhJYiD.js (removed) 4.79 kB 🟢 -4.79 kB 🟢 -1.55 kB 🟢 -1.37 kB
assets/saveMesh-DKKDrHQW.js (new) 4.67 kB 🔴 +4.67 kB 🔴 +1.51 kB 🔴 +1.34 kB
assets/Preview3d-BYJkJapv.js (removed) 4.59 kB 🟢 -4.59 kB 🟢 -1.43 kB 🟢 -1.23 kB
assets/Preview3d-JMYFXdYq.js (new) 4.59 kB 🔴 +4.59 kB 🔴 +1.44 kB 🔴 +1.24 kB
assets/ValueControlPopover-BYXX3xlj.js (removed) 4.55 kB 🟢 -4.55 kB 🟢 -1.59 kB 🟢 -1.41 kB
assets/ValueControlPopover-IYmJ0Lkh.js (new) 4.55 kB 🔴 +4.55 kB 🔴 +1.59 kB 🔴 +1.41 kB
assets/CancelSubscriptionDialogContent-DDBeY5VZ.js (new) 4.54 kB 🔴 +4.54 kB 🔴 +1.65 kB 🔴 +1.44 kB
assets/CancelSubscriptionDialogContent-Dn8WEGzi.js (removed) 4.54 kB 🟢 -4.54 kB 🟢 -1.66 kB 🟢 -1.44 kB
assets/tierBenefits-cX5eYlim.js (new) 4.46 kB 🔴 +4.46 kB 🔴 +1.58 kB 🔴 +1.37 kB
assets/tierBenefits-D-uohxWO.js (removed) 4.46 kB 🟢 -4.46 kB 🟢 -1.58 kB 🟢 -1.37 kB
assets/DeleteWorkspaceDialogContent-CI2bmnUo.js (new) 3.91 kB 🔴 +3.91 kB 🔴 +1.47 kB 🔴 +1.26 kB
assets/DeleteWorkspaceDialogContent-NAfad63N.js (removed) 3.91 kB 🟢 -3.91 kB 🟢 -1.47 kB 🟢 -1.27 kB
assets/LeaveWorkspaceDialogContent-8LmwXvAo.js (new) 3.73 kB 🔴 +3.73 kB 🔴 +1.42 kB 🔴 +1.22 kB
assets/LeaveWorkspaceDialogContent-B12gYXbT.js (removed) 3.73 kB 🟢 -3.73 kB 🟢 -1.42 kB 🟢 -1.22 kB
assets/RemoveMemberDialogContent-DK3NgU2D.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.37 kB 🔴 +1.19 kB
assets/RemoveMemberDialogContent-Ofgtr1Pa.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.37 kB 🟢 -1.19 kB
assets/RevokeInviteDialogContent-0-_GcGCn.js (new) 3.63 kB 🔴 +3.63 kB 🔴 +1.38 kB 🔴 +1.21 kB
assets/RevokeInviteDialogContent-Ctx8kUWH.js (removed) 3.63 kB 🟢 -3.63 kB 🟢 -1.38 kB 🟢 -1.2 kB
assets/InviteMemberUpsellDialogContent-BGcPzvs1.js (new) 3.48 kB 🔴 +3.48 kB 🔴 +1.23 kB 🔴 +1.07 kB
assets/InviteMemberUpsellDialogContent-biwNO-rB.js (removed) 3.48 kB 🟢 -3.48 kB 🟢 -1.24 kB 🟢 -1.08 kB
assets/Media3DTop-BS4sG6mG.js (removed) 3.26 kB 🟢 -3.26 kB 🟢 -1.3 kB 🟢 -1.13 kB
assets/Media3DTop-CgngZCi9.js (new) 3.2 kB 🔴 +3.2 kB 🔴 +1.28 kB 🔴 +1.11 kB
assets/GlobalToast-CKq1rJ5H.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.26 kB 🟢 -1.08 kB
assets/GlobalToast-fiQPRx4O.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.08 kB
assets/load3dAdvanced-CTQtsPXp.js (new) 2.85 kB 🔴 +2.85 kB 🔴 +1.12 kB 🔴 +975 B
assets/load3dAdvanced-Dz1o5xuI.js (removed) 2.85 kB 🟢 -2.85 kB 🟢 -1.12 kB 🟢 -977 B
assets/SubscribeToRun-2HZ2PjDW.js (removed) 2.53 kB 🟢 -2.53 kB 🟢 -1.1 kB 🟢 -973 B
assets/SubscribeToRun-CtbQ1nAd.js (new) 2.53 kB 🔴 +2.53 kB 🔴 +1.1 kB 🔴 +972 B
assets/graphHasMissingNodes-C_NJSiXR.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -908 B 🟢 -794 B
assets/graphHasMissingNodes-DFZgegNw.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +907 B 🔴 +797 B
assets/MediaAudioTop-BwICIpyD.js (removed) 1.67 kB 🟢 -1.67 kB 🟢 -838 B 🟢 -693 B
assets/MediaAudioTop-Dk46SDa0.js (new) 1.67 kB 🔴 +1.67 kB 🔴 +838 B 🔴 +693 B
assets/CloudRunButtonWrapper-BtWW5qCG.js (new) 1.13 kB 🔴 +1.13 kB 🔴 +548 B 🔴 +492 B
assets/CloudRunButtonWrapper-DC146jm1.js (removed) 1.13 kB 🟢 -1.13 kB 🟢 -552 B 🟢 -516 B
assets/cloudSessionCookie-Dpgmx_pa.js (removed) 991 B 🟢 -991 B 🟢 -467 B 🟢 -412 B
assets/cloudSessionCookie-r60AjfB_.js (new) 991 B 🔴 +991 B 🔴 +466 B 🔴 +398 B
assets/cloudBadges-DAaGRT2R.js (removed) 973 B 🟢 -973 B 🟢 -551 B 🟢 -495 B
assets/cloudBadges-DRXwhf3k.js (new) 973 B 🔴 +973 B 🔴 +550 B 🔴 +468 B
assets/Load3DAdvanced-CdKnrbM7.js (new) 813 B 🔴 +813 B 🔴 +453 B 🔴 +407 B
assets/Load3DAdvanced-FxKqTCWG.js (removed) 813 B 🟢 -813 B 🟢 -458 B 🟢 -410 B
assets/nightlyBadges-BQd-r6h_.js (new) 464 B 🔴 +464 B 🔴 +306 B 🔴 +304 B
assets/nightlyBadges-DBE8CgHt.js (removed) 464 B 🟢 -464 B 🟢 -305 B 🟢 -252 B
assets/missingModelDownload-ClDFSYoE.js (new) 228 B 🔴 +228 B 🔴 +149 B 🔴 +129 B
assets/missingModelDownload-Wkcc-z-V.js (removed) 228 B 🟢 -228 B 🟢 -150 B 🟢 -129 B
assets/SubscriptionPanelContentWorkspace-BQOSRY7W.js (removed) 179 B 🟢 -179 B 🟢 -117 B 🟢 -103 B
assets/SubscriptionPanelContentWorkspace-DUN0y2-n.js (new) 179 B 🔴 +179 B 🔴 +117 B 🔴 +105 B
assets/Load3dViewerContent-CTqENSpX.js (removed) 137 B 🟢 -137 B 🟢 -103 B 🟢 -102 B
assets/Load3dViewerContent-mb_2jDg9.js (new) 137 B 🔴 +137 B 🔴 +103 B 🔴 +92 B
assets/Load3DAdvanced-BGveGlXU.js (removed) 122 B 🟢 -122 B 🟢 -97 B 🟢 -88 B
assets/Load3DAdvanced-BSo6IW3A.js (new) 122 B 🔴 +122 B 🔴 +97 B 🔴 +90 B
assets/WidgetLegacy-D-xF1IpM.js (new) 119 B 🔴 +119 B 🔴 +108 B 🔴 +98 B
assets/WidgetLegacy-OKKb2cjI.js (removed) 119 B 🟢 -119 B 🟢 -108 B 🟢 -101 B
assets/workflowDraftStoreV2-BiGfDOB2.js (removed) 113 B 🟢 -113 B 🟢 -105 B 🟢 -116 B
assets/workflowDraftStoreV2-Kym3go0u.js (new) 113 B 🔴 +113 B 🔴 +105 B 🔴 +112 B
assets/Load3D-D2_JfPew.js (removed) 98 B 🟢 -98 B 🟢 -89 B 🟢 -86 B
assets/Load3D-e1DCVWah.js (new) 98 B 🔴 +98 B 🔴 +89 B 🔴 +93 B
assets/changeTracker-CWlRRZ2W.js (new) 93 B 🔴 +93 B 🔴 +95 B 🔴 +81 B
assets/changeTracker-saRoOczJ.js (removed) 93 B 🟢 -93 B 🟢 -95 B 🟢 -81 B

Status: 61 added / 61 removed / 89 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 54.0 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.2 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.8 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.9 MB heap
large-graph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 59.5 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 79.5 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.5 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 59.4 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 63.3 MB heap
subgraph-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 60.8 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 45.7 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 153ms TBT · 76.1 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 70.6 MB heap
vue-large-graph-idle: · 59.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 162.2 MB heap
vue-large-graph-pan: · 58.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 49ms TBT · 155.2 MB heap
workflow-execution: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.4 MB heap

✅ No regressions detected.

All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms +0% z=0.4
canvas-idle: p95 frame time 17ms 17ms +0%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 10ms 9ms -14% z=-2.1
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 10 9 -10% z=-3.8
canvas-idle: task duration 361ms 372ms +3% z=-0.7
canvas-idle: script duration 22ms 20ms -8% z=-2.4
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 53.9 MB 54.0 MB +0%
canvas-idle: DOM nodes 18 18 +0% z=-3.6
canvas-idle: event listeners 4 4 +0% z=-1.6
canvas-mouse-sweep: avg frame time 17ms 17ms -0% z=-0.9
canvas-mouse-sweep: p95 frame time 17ms 17ms +0%
canvas-mouse-sweep: layout duration 3ms 3ms +4% z=-1.4
canvas-mouse-sweep: style recalc duration 36ms 41ms +14% z=-0.6
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 75 73 -3% z=-2.6
canvas-mouse-sweep: task duration 732ms 785ms +7% z=-1.4
canvas-mouse-sweep: script duration 117ms 113ms -3% z=-3.4
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 48.9 MB 53.2 MB +9%
canvas-mouse-sweep: DOM nodes 58 -85 -246% z=-56.8
canvas-mouse-sweep: event listeners 4 -95 -2463% z=-24.7
canvas-zoom-sweep: avg frame time 17ms 17ms +0% z=0.5
canvas-zoom-sweep: p95 frame time 17ms 17ms -0%
canvas-zoom-sweep: layout duration 1ms 1ms +15% z=0.2
canvas-zoom-sweep: style recalc duration 15ms 17ms +12% z=-1.3
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 31 31 +0% z=-0.6
canvas-zoom-sweep: task duration 289ms 349ms +21% z=0.9
canvas-zoom-sweep: script duration 22ms 29ms +32% z=0.6
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 57.4 MB 62.8 MB +9%
canvas-zoom-sweep: DOM nodes 77 78 +1% z=-1.6
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: avg frame time 17ms 17ms -0% z=-0.2
dom-widget-clipping: p95 frame time 17ms 17ms -0%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 8ms 8ms -2% z=-2.6
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 12 12 +0% z=-2.2
dom-widget-clipping: task duration 321ms 342ms +7% z=-1.3
dom-widget-clipping: script duration 55ms 57ms +5% z=-3.2
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 52.5 MB 62.9 MB +20%
dom-widget-clipping: DOM nodes 20 20 +0% z=-1.5
dom-widget-clipping: event listeners 0 0 +0% variance too high
large-graph-idle: avg frame time 17ms 17ms +0% z=-0.2
large-graph-idle: p95 frame time 17ms 17ms +0%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 9ms -3% z=-3.5
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 10 10 +0% z=-5.1
large-graph-idle: task duration 476ms 532ms +12% z=-0.2
large-graph-idle: script duration 91ms 95ms +4% z=-0.8
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 59.3 MB 59.5 MB +0%
large-graph-idle: DOM nodes 20 20 +0% z=-5.2
large-graph-idle: event listeners 4 4 +0% z=-3.7
large-graph-pan: avg frame time 17ms 17ms +0% z=0.8
large-graph-pan: p95 frame time 17ms 17ms -0%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 20ms 18ms -10% z=0.7
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 70 68 -4% z=-3.2
large-graph-pan: task duration 1013ms 1097ms +8% z=0.3
large-graph-pan: script duration 383ms 396ms +4% z=-0.6
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 79.3 MB 79.5 MB +0%
large-graph-pan: DOM nodes 20 12 -40% z=-4.2
large-graph-pan: event listeners 6 5 -17% z=0.1
large-graph-zoom: avg frame time 17ms 17ms -0%
large-graph-zoom: p95 frame time 17ms 17ms +1%
large-graph-zoom: layout duration 7ms 8ms +13%
large-graph-zoom: style recalc duration 18ms 20ms +8%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 66 -1%
large-graph-zoom: task duration 1264ms 1315ms +4%
large-graph-zoom: script duration 472ms 471ms -0%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 65.2 MB 65.5 MB +0%
large-graph-zoom: DOM nodes 14 13 -7%
large-graph-zoom: event listeners 8 8 +0%
minimap-idle: avg frame time 17ms 17ms -0% z=-0.4
minimap-idle: p95 frame time 17ms 17ms -0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 8ms 7ms -10% z=-3.3
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 9 8 -17% z=-3.0
minimap-idle: task duration 467ms 545ms +17% z=0.4
minimap-idle: script duration 86ms 94ms +8% z=-0.5
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 56.9 MB 59.4 MB +4%
minimap-idle: DOM nodes -273 15 -105% z=-3.0
minimap-idle: event listeners -193 4 -102% z=-0.9
subgraph-dom-widget-clipping: avg frame time 17ms 17ms +0% z=-0.4
subgraph-dom-widget-clipping: p95 frame time 17ms 17ms -0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 11ms 11ms +5% z=-1.8
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 48 48 -1% z=-0.8
subgraph-dom-widget-clipping: task duration 339ms 368ms +9% z=-0.5
subgraph-dom-widget-clipping: script duration 116ms 123ms +6% z=-0.9
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 63.6 MB 63.3 MB -1%
subgraph-dom-widget-clipping: DOM nodes 22 21 -5% z=-1.1
subgraph-dom-widget-clipping: event listeners 6 6 +0% z=-1.7
subgraph-idle: avg frame time 17ms 17ms +0% z=-0.2
subgraph-idle: p95 frame time 17ms 17ms -1%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 9ms 10ms +17% z=-0.4
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 10 10 -5% z=-2.1
subgraph-idle: task duration 331ms 429ms +30% z=1.9
subgraph-idle: script duration 12ms 18ms +49% z=-0.7
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 53.6 MB 60.8 MB +13%
subgraph-idle: DOM nodes 20 -150 -850% z=-114.9
subgraph-idle: event listeners 4 -98 -2538% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.4
subgraph-mouse-sweep: p95 frame time 17ms 17ms +1%
subgraph-mouse-sweep: layout duration 4ms 4ms +0% z=-1.5
subgraph-mouse-sweep: style recalc duration 35ms 35ms +1% z=-2.1
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 77 77 -1% z=-1.9
subgraph-mouse-sweep: task duration 636ms 690ms +9% z=-1.1
subgraph-mouse-sweep: script duration 90ms 86ms -5% z=-2.3
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 45.9 MB 45.7 MB -0%
subgraph-mouse-sweep: DOM nodes 65 64 -2% z=-1.5
subgraph-mouse-sweep: event listeners 4 4 +0% variance too high
subgraph-transition-enter: avg frame time 17ms 17ms -0%
subgraph-transition-enter: p95 frame time 17ms 17ms +0%
subgraph-transition-enter: layout duration 14ms 12ms -16%
subgraph-transition-enter: style recalc duration 27ms 25ms -9%
subgraph-transition-enter: layout count 4 4 +0%
subgraph-transition-enter: style recalc count 16 17 +6%
subgraph-transition-enter: task duration 696ms 714ms +3%
subgraph-transition-enter: script duration 25ms 29ms +13%
subgraph-transition-enter: TBT 153ms 153ms +0%
subgraph-transition-enter: heap used 76.2 MB 76.1 MB -0%
subgraph-transition-enter: DOM nodes 13833 13833 +0%
subgraph-transition-enter: event listeners 2527 2527 +0%
viewport-pan-sweep: avg frame time 17ms 17ms -0%
viewport-pan-sweep: p95 frame time 17ms 17ms +0%
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 53ms 58ms +10%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 251 252 +0%
viewport-pan-sweep: task duration 3472ms 3974ms +14%
viewport-pan-sweep: script duration 1196ms 1308ms +9%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 63.9 MB 70.6 MB +10%
viewport-pan-sweep: DOM nodes 22 -119 -641%
viewport-pan-sweep: event listeners 20 -47 -335%
vue-large-graph-idle: avg frame time 17ms 17ms +2%
vue-large-graph-idle: p95 frame time 17ms 17ms -0%
vue-large-graph-idle: layout duration 0ms 0ms +0%
vue-large-graph-idle: style recalc duration 0ms 0ms +0%
vue-large-graph-idle: layout count 0 0 +0%
vue-large-graph-idle: style recalc count 0 0 +0%
vue-large-graph-idle: task duration 11935ms 11469ms -4%
vue-large-graph-idle: script duration 562ms 545ms -3%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 158.5 MB 162.2 MB +2%
vue-large-graph-idle: DOM nodes -3310 -3305 -0%
vue-large-graph-idle: event listeners -16474 -16471 -0%
vue-large-graph-pan: avg frame time 17ms 17ms -0%
vue-large-graph-pan: p95 frame time 17ms 17ms +0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 16ms 20ms +23%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 66 66 -1%
vue-large-graph-pan: task duration 14128ms 13869ms -2%
vue-large-graph-pan: script duration 842ms 925ms +10%
vue-large-graph-pan: TBT 0ms 49ms
vue-large-graph-pan: heap used 170.7 MB 155.2 MB -9%
vue-large-graph-pan: DOM nodes -3306 -3307 +0%
vue-large-graph-pan: event listeners -16471 -16470 -0%
workflow-execution: avg frame time 17ms 17ms -0% z=0.6
workflow-execution: p95 frame time 17ms 17ms -0%
workflow-execution: layout duration 1ms 2ms +47% z=1.8
workflow-execution: style recalc duration 22ms 27ms +21% z=1.0
workflow-execution: layout count 5 6 +20% z=1.9
workflow-execution: style recalc count 18 19 +6% z=0.6
workflow-execution: task duration 112ms 129ms +15% z=0.6
workflow-execution: script duration 20ms 21ms +6% z=-2.6
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 61.9 MB 62.4 MB +1%
workflow-execution: DOM nodes 157 164 +4% z=0.4
workflow-execution: event listeners 69 69 +0% z=3.9
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-06-20T00:09:13.893Z",
  "gitSha": "545635ae1c7ea0b6539cebb8deb31c230caa0d51",
  "branch": "jaewon/fe-729-delete-is-asset-api-enabled",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2018.0959999999857,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 6.255999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 373.24999999999994,
      "heapDeltaBytes": -2381580,
      "heapUsedBytes": 56705284,
      "domNodes": 14,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 18.733000000000004,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-idle",
      "durationMs": 2024.3470000000343,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.591,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 371.71700000000004,
      "heapDeltaBytes": -2219596,
      "heapUsedBytes": 56636700,
      "domNodes": 22,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 21.153,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1840.2949999999976,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 44.43,
      "layouts": 12,
      "layoutDurationMs": 3.284,
      "taskDurationMs": 812.696,
      "heapDeltaBytes": -6966940,
      "heapUsedBytes": 51749596,
      "domNodes": 60,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 116.729,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1791.0469999999918,
      "styleRecalcs": 71,
      "styleRecalcDurationMs": 37.029999999999994,
      "layouts": 12,
      "layoutDurationMs": 3.371,
      "taskDurationMs": 756.722,
      "heapDeltaBytes": -9310380,
      "heapUsedBytes": 59725068,
      "domNodes": -229,
      "jsHeapTotalBytes": 16429056,
      "scriptDurationMs": 109.85099999999998,
      "eventListeners": -193,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1741.6190000000142,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 15.953000000000001,
      "layouts": 6,
      "layoutDurationMs": 0.5549999999999999,
      "taskDurationMs": 358.56499999999994,
      "heapDeltaBytes": 1859444,
      "heapUsedBytes": 60608348,
      "domNodes": 78,
      "jsHeapTotalBytes": 26214400,
      "scriptDurationMs": 27.278999999999996,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1722.8709999999978,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 18.369,
      "layouts": 6,
      "layoutDurationMs": 0.7470000000000002,
      "taskDurationMs": 338.805,
      "heapDeltaBytes": 7394904,
      "heapUsedBytes": 71041856,
      "domNodes": 78,
      "jsHeapTotalBytes": 15466496,
      "scriptDurationMs": 30.518000000000004,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 574.9250000000075,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 8.325,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 351.178,
      "heapDeltaBytes": 7390156,
      "heapUsedBytes": 66022388,
      "domNodes": 20,
      "jsHeapTotalBytes": 19136512,
      "scriptDurationMs": 59.459,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 527.4779999999737,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 7.378000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 333.59499999999997,
      "heapDeltaBytes": 7262412,
      "heapUsedBytes": 65903432,
      "domNodes": 20,
      "jsHeapTotalBytes": 19136512,
      "scriptDurationMs": 55.36800000000001,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666682,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 1993.471999999997,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.818000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 521.6750000000001,
      "heapDeltaBytes": -8027944,
      "heapUsedBytes": 62103384,
      "domNodes": 20,
      "jsHeapTotalBytes": 10891264,
      "scriptDurationMs": 89.44,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2020.5060000000117,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.243000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 541.823,
      "heapDeltaBytes": -7996560,
      "heapUsedBytes": 62638712,
      "domNodes": 20,
      "jsHeapTotalBytes": 10366976,
      "scriptDurationMs": 99.683,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2100.611000000015,
      "styleRecalcs": 70,
      "styleRecalcDurationMs": 20.365000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1097.379,
      "heapDeltaBytes": 9927812,
      "heapUsedBytes": 83079868,
      "domNodes": 18,
      "jsHeapTotalBytes": 9232384,
      "scriptDurationMs": 397.253,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2071.673999999973,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 15.280999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1096.002,
      "heapDeltaBytes": 27984460,
      "heapUsedBytes": 83607788,
      "domNodes": 6,
      "jsHeapTotalBytes": 2301952,
      "scriptDurationMs": 395.326,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3113.4739999999965,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 19.636,
      "layouts": 60,
      "layoutDurationMs": 8.02,
      "taskDurationMs": 1282.201,
      "heapDeltaBytes": 12929076,
      "heapUsedBytes": 68475636,
      "domNodes": 14,
      "jsHeapTotalBytes": 5767168,
      "scriptDurationMs": 453.288,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3170.1189999999997,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 19.654999999999998,
      "layouts": 60,
      "layoutDurationMs": 8.084000000000001,
      "taskDurationMs": 1347.702,
      "heapDeltaBytes": 13129704,
      "heapUsedBytes": 68848828,
      "domNodes": 12,
      "jsHeapTotalBytes": 6029312,
      "scriptDurationMs": 489.057,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "minimap-idle",
      "durationMs": 2002.6860000000397,
      "styleRecalcs": 5,
      "styleRecalcDurationMs": 4.598000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 545.534,
      "heapDeltaBytes": 6308720,
      "heapUsedBytes": 60189764,
      "domNodes": 10,
      "jsHeapTotalBytes": 786432,
      "scriptDurationMs": 95.92500000000001,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "minimap-idle",
      "durationMs": 2002.016000000026,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.053000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 545.2060000000001,
      "heapDeltaBytes": -7951808,
      "heapUsedBytes": 64355076,
      "domNodes": 20,
      "jsHeapTotalBytes": 11415552,
      "scriptDurationMs": 91.09500000000001,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 579.7460000000001,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 10.916,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 373.595,
      "heapDeltaBytes": 7616660,
      "heapUsedBytes": 66444888,
      "domNodes": 20,
      "jsHeapTotalBytes": 19922944,
      "scriptDurationMs": 122.291,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 553.9589999999635,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 11.184000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 362.61199999999997,
      "heapDeltaBytes": 7281872,
      "heapUsedBytes": 66222856,
      "domNodes": 22,
      "jsHeapTotalBytes": 18087936,
      "scriptDurationMs": 122.94699999999999,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2051.7329999999847,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 11.621,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 487.208,
      "heapDeltaBytes": 16108324,
      "heapUsedBytes": 70801688,
      "domNodes": -320,
      "jsHeapTotalBytes": 15380480,
      "scriptDurationMs": 17.013,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2026.7049999999927,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.552000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 371.614,
      "heapDeltaBytes": -2435452,
      "heapUsedBytes": 56637528,
      "domNodes": 20,
      "jsHeapTotalBytes": 25427968,
      "scriptDurationMs": 19.814,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1705.5890000000034,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 34.995,
      "layouts": 16,
      "layoutDurationMs": 4.361000000000001,
      "taskDurationMs": 695.0849999999999,
      "heapDeltaBytes": -10780304,
      "heapUsedBytes": 48285760,
      "domNodes": 65,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 84.782,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1691.7440000000283,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 35.867000000000004,
      "layouts": 16,
      "layoutDurationMs": 4.086,
      "taskDurationMs": 684.1440000000001,
      "heapDeltaBytes": -11323916,
      "heapUsedBytes": 47591648,
      "domNodes": 62,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 86.77499999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 948.3589999999822,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 25.054999999999993,
      "layouts": 4,
      "layoutDurationMs": 11.822,
      "taskDurationMs": 713.9710000000001,
      "heapDeltaBytes": 4388132,
      "heapUsedBytes": 79826672,
      "domNodes": 13833,
      "jsHeapTotalBytes": 17825792,
      "scriptDurationMs": 28.695999999999998,
      "eventListeners": 2527,
      "totalBlockingTimeMs": 153,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8173.467999999957,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 57.861,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3848.8,
      "heapDeltaBytes": 12693340,
      "heapUsedBytes": 75297496,
      "domNodes": -260,
      "jsHeapTotalBytes": 6250496,
      "scriptDurationMs": 1229.8870000000002,
      "eventListeners": -114,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8168.706000000043,
      "styleRecalcs": 252,
      "styleRecalcDurationMs": 58.06400000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4100.1539999999995,
      "heapDeltaBytes": 2353540,
      "heapUsedBytes": 72745884,
      "domNodes": 22,
      "jsHeapTotalBytes": 22339584,
      "scriptDurationMs": 1385.8110000000001,
      "eventListeners": 20,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 11124.616999999944,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 11108.032,
      "heapDeltaBytes": -21918112,
      "heapUsedBytes": 169897336,
      "domNodes": -3302,
      "jsHeapTotalBytes": 19922944,
      "scriptDurationMs": 539.626,
      "eventListeners": -16467,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333326,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 11847.328000000061,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 11830.583999999999,
      "heapDeltaBytes": -36559664,
      "heapUsedBytes": 170343600,
      "domNodes": -3308,
      "jsHeapTotalBytes": 22515712,
      "scriptDurationMs": 549.67,
      "eventListeners": -16474,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.220000000000073,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14410.539000000028,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 19.712000000000007,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14389.104,
      "heapDeltaBytes": -55354144,
      "heapUsedBytes": 154891192,
      "domNodes": -3308,
      "jsHeapTotalBytes": 15437824,
      "scriptDurationMs": 910.1109999999999,
      "eventListeners": -16470,
      "totalBlockingTimeMs": 69,
      "frameDurationMs": 17.219999999999953,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 13368.910000000029,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 19.95099999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 13348.078999999998,
      "heapDeltaBytes": -33280172,
      "heapUsedBytes": 170612916,
      "domNodes": -3306,
      "jsHeapTotalBytes": 19632128,
      "scriptDurationMs": 940.0090000000001,
      "eventListeners": -16470,
      "totalBlockingTimeMs": 29,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "workflow-execution",
      "durationMs": 451.0200000000282,
      "styleRecalcs": 20,
      "styleRecalcDurationMs": 28.351000000000003,
      "layouts": 6,
      "layoutDurationMs": 2.02,
      "taskDurationMs": 134.957,
      "heapDeltaBytes": 5474692,
      "heapUsedBytes": 65594264,
      "domNodes": 170,
      "jsHeapTotalBytes": 3407872,
      "scriptDurationMs": 21.540999999999997,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "workflow-execution",
      "durationMs": 453.9839999999913,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 24.715,
      "layouts": 6,
      "layoutDurationMs": 1.6090000000000002,
      "taskDurationMs": 122.89399999999999,
      "heapDeltaBytes": 5248064,
      "heapUsedBytes": 65335612,
      "domNodes": 157,
      "jsHeapTotalBytes": 3407872,
      "scriptDurationMs": 21.426000000000002,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    }
  ]
}

@dante01yoon

Copy link
Copy Markdown
Collaborator Author

Blocker status — 2026-06-18

BE-786 ("assets always-on", the merge gate for this PR) is confirmed still required — not done elsewhere (Matt checked: Slack). 3 of its 4 blockers are now cleared; the only one left is BE-933 (Core: emit file_path) = Simon's Comfy-Org/ComfyUI#14005, currently In Review. Once #14005 merges, BE-786 unblocks — Matt is now assigned and will pick it up, and this PR is ready for review.

Until then the red e2e is expected: it is the temporary --enable-assets golden mismatch described in the body (verified systematic over two runs — the vue-node @screenshot set; subgraphSeed was a flake that passed on rerun), not a regression.

icon: 'pi pi-database',
label: () =>
`Experimental: ${
useSettingStore().get('Comfy.Assets.UseAssetAPI')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: (non-blocking) Any user with a persisted keybinding to Comfy.ToggleAssetAPI will hit an unhandled rejection on keypress -- commandStore.execute throws "Command not found" and the keybinding service has no try/catch around execute(). A silent no-op would be preferable; consider whether the keybinding service should guard missing commands gracefully, or add a one-time startup migration that drops orphaned bindings pointing to unregistered command IDs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed the path: keybindHandler is attached via useEventListener(window, "keydown", ...) in GraphView.vue, and commandStore.execute throws Command ... not found for an unregistered id, so a floating rejection is possible. Scope is narrow though — no default keybinding ever pointed at Comfy.ToggleAssetAPI (KeyM maps to Workspace.ToggleSidebarTab.model-library), so only users who manually bound a key are affected.

The clean fix is a graceful guard in the keybinding service (skip + warn on unregistered command ids), which also covers future removed commands and orphaned extension bindings. That touches shared keybinding infra beyond this delete-only PR, so I'll handle it as a separate follow-up rather than widen this one.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as FE-1100 — the keybinding service will skip unregistered command ids gracefully (warn + no-op), covering this removal and any future ones plus orphaned extension bindings. https://linear.app/comfyorg/issue/FE-1100


if (
tab.id === 'model-library' &&
settingStore.get('Comfy.Assets.UseAssetAPI')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: (non-blocking) Previously, if Comfy.Assets.UseAssetAPI was false the if-block was skipped and normal tab toggle behavior continued. Now if (tab.id === 'model-library') always fires and ?.function?.() silently no-ops if Comfy.BrowseModelAssets is not yet registered (load-ordering race). The tab becomes a dead click with no feedback. Worth adding a fallback or asserting the command exists before relying on optional chaining.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bc5d7a3. If Comfy.BrowseModelAssets isn't registered we now fall back to toggleSidebarTab(tab.id) instead of a silent no-op, so the click is never dead. Added regression coverage in sidebarTabStore.test.ts: command-present opens the browser; command-absent toggles the tab.

cd /ComfyUI && python3 main.py --cpu --multi-user --front-end-root "${{ inputs.front_end_root }}" &
# TODO(FE-729): remove --enable-assets once BE-786 lands in the CI ComfyUI image
# (BE-786 removes the gate so /api/assets is always on). Until then, FE-729
# routes modelStore through assetService, which 503s without this flag.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: The TODO comment is clear, but there is no machine enforcement that catches if --enable-assets survives into a non-draft merge. Consider a CI lint step (a grep gate) or a note in the BE-786 ticket so the revert is verified automatically rather than relying on reviewer memory.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed it shouldn't rely on reviewer memory. The --enable-assets line lives in commit e48dcd1, which is itself reverted before this PR leaves draft-gating on BE-786 (PR checklist item). I'll add a note on BE-786 so the CI image flip and this revert are verified together.

A standing grep gate isn't durable here since the whole temp commit is removed at merge, but I can add a transient CI check if you'd prefer belt-and-suspenders.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted the coordination note on BE-786 so the CI image flip and the e48dcd11f revert get verified together when it lands.

@@ -1,236 +0,0 @@
import { expect } from '@playwright/test'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: (non-blocking) All 5 describe blocks covering the model-library sidebar (open/close, folder expand, search, refresh, empty state -- roughly 15 test cases) are removed and the tab now opens the Asset Browser dialog. No equivalent E2E tests for that dialog appear in this PR. Is Asset Browser dialog coverage tracked as a follow-up (FE-732?), or is there an existing suite I'm missing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those blocks exercised the legacy model-library sidebar panel, which no longer renders — the tab now opens the Asset Browser dialog. The dialog's content/fetch and its E2E are the next layers: FE-730 (store fetcher) and FE-732 (dialog content). They're out of this routing-only PR, and on OSS the dialog data path is gated on BE-786, so there's no green dialog suite to add here yet. New-behavior coverage is owned by FE-732.

@@ -27,7 +27,6 @@ test.describe('Default Keybindings', { tag: '@keyboard' }, () => {
const sidebarTabs = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: The KeyM -> model-library keybinding test entry is removed, but I don't see the keybinding definition file in the diff. Does the keybinding still exist with changed behavior (in which case it should be re-tested under the new behavior), or was the binding itself removed entirely?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The binding still exists — defaults.ts keeps KeyM -> Workspace.ToggleSidebarTab.model-library. What changed is its behavior: that command now opens the Asset Browser dialog instead of toggling the sidebar panel. I dropped the entry from this data-driven Sidebar Toggle Shortcuts test because its assertion (.model-library-tab-button.side-bar-button-selected shows/hides) no longer holds — there's no selectable sidebar button to toggle. A test for the new behavior (KeyM opens the dialog) belongs with the dialog work in FE-732, same as the modelLibrary.spec.ts removal above.

const node = this.comfyPage.vueNodes.getNodeLocator(String(nodeRef.id))
// Grid-mode widgets (WidgetSelectDefault) and number widgets expose
// aria-label on a wrapper/input. Asset-mode widgets (WidgetSelectDropdown)
// do not — the widget name lives in a sibling

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: (non-blocking) byAriaLabel.count() resolves against the current DOM immediately with no wait -- if the widget is still mounting it returns 0 falsely and routes to the fallback even when the aria-label element is about to appear. Consider waitFor({ state: 'attached' }) wrapped in a try/catch before branching.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid — count() is a point-in-time read. The preceding await nodeRef.centerOnNode() settles a frame first so the window is small, but not airtight. I held off on a blanket waitFor({ state: "attached" }) because asset-mode widgets legitimately have no aria-label, so every asset-widget selection would eat the full timeout on the fallback branch.

The durable fix is the data-testid on the dropdown trigger you suggest in the next two comments — that removes the aria-label/fallback branch and the race with it. Tracking that as a follow-up; happy to drop in a short-timeout waitFor here in the meantime if you'd rather.

(await byAriaLabel.count()) > 0
? byAriaLabel
: node
.getByTestId('widget-layout-field-label')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: (non-blocking) .locator('button').first() picks by DOM order. If the widget row has multiple buttons (clear, expand, trigger), this may click the wrong one. A data-testid on the dropdown trigger button would be more reliable than positional selection.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — positional .first() is fragile. A data-testid on the dropdown trigger is the right call; it supersedes both this positional select and the .. traversal below. Tracking as a follow-up with the widget-helper cleanup.

const widgetLocator =
(await byAriaLabel.count()) > 0
? byAriaLabel
: node

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: (non-blocking) locator('..') is XPath parent-axis traversal -- Playwright docs discourage it since any added wrapper div silently breaks the path. A shared data-testid on the row ancestor would be more maintainable.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — XPath parent-axis is brittle to wrapper changes. Same fix as the previous comment: a data-testid on the row/trigger removes the .. hop. Folding both into the follow-up.

@christian-byrne

Copy link
Copy Markdown
Contributor

suggestion: (non-blocking) The locale files (e.g. src/locales/en/commands.json) likely still have Comfy_BrowseModelAssets.label set to "Experimental: Browse Model Assets" and an orphaned Comfy_ToggleAssetAPI key. If the command palette resolves labels via st()/stRaw() using the command id as a locale key, the i18n entry overrides the hardcoded change in useCoreCommands.ts and users in all locales continue to see the old "Experimental:" prefix. Both keys need updating/removing across all shipped locale files.

…istered (FE-729)

If Comfy.BrowseModelAssets is not yet registered (load-ordering race), the
model-library tab click was a silent no-op via optional chaining. Fall back to
toggleSidebarTab so the click is never dead, and add regression coverage.
@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
comfyui-frontend-node-search-preview Error Error Jun 19, 2026 12:22am

@github-actions

Copy link
Copy Markdown

Updating Playwright Expectations

@github-actions github-actions Bot removed the New Browser Test Expectations New browser test screenshot should be set by github action label Jun 19, 2026
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

Resource is limited - try again in 1 day (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/uy-tieu-s-projects?upgradeToPro=build-rate-limit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants