Skip to content

feat(templates): migrate cloud template dialog to hub API#10675

Open
dante01yoon wants to merge 10 commits intomainfrom
feat/hub-template-api-migration
Open

feat(templates): migrate cloud template dialog to hub API#10675
dante01yoon wants to merge 10 commits intomainfrom
feat/hub-template-api-migration

Conversation

@dante01yoon
Copy link
Copy Markdown
Collaborator

@dante01yoon dante01yoon commented Mar 28, 2026

Summary

  • Replace static index.json template loading on cloud with the hub workflows API (GET /api/hub/workflows for listing, GET /api/hub/workflows/{share_id} for workflow JSON)
  • Add adapter layer to convert HubWorkflowSummaryTemplateInfo, preserving all existing client-side filtering/sorting/pagination
  • Local installations remain completely unchanged

Changes

File Change
src/scripts/api.ts Add listHubWorkflows(), listAllHubWorkflows(), getHubWorkflowDetail() with Zod validation
src/platform/workflow/templates/adapters/hubTemplateAdapter.ts New — adapter HubWorkflowSummaryTemplateInfo + category wrapper
src/platform/workflow/templates/types/template.ts Add thumbnailUrl, thumbnailComparisonUrl, shareId, profile to TemplateInfo
src/platform/workflow/templates/repositories/workflowTemplatesStore.ts Branch on isCloud in fetchCoreTemplates(), add getTemplateByShareId()
src/platform/workflow/templates/composables/useTemplateWorkflows.ts Hub thumbnail URLs + detail API for workflow JSON
src/platform/workflow/templates/composables/useTemplateUrlLoader.ts shareId-based fallback lookup for cloud

Test plan

  • TypeScript typecheck passes
  • All 7302+ unit tests pass (including 41 template-related tests)
  • 8 new adapter unit tests covering field mapping, LabelRef extraction, metadata extraction, defaults
  • Manual: Open template dialog on cloud build → templates load from hub API
  • Manual: Thumbnails render correctly (absolute URLs)
  • Manual: Click template → workflow loads via detail API
  • Manual: Search/filter/sort work with hub data
  • Manual: Local build behavior unchanged

┆Issue is synchronized with this Notion page by Unito

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 03/29/2026, 05:23:06 AM UTC

Links

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request integrates hub workflow templates into the cloud environment. It introduces adapter functions to transform hub workflow summaries into template format, extends the API with hub workflow fetching capabilities, and modifies store and composable logic to conditionally load hub workflows when running in cloud mode.

Changes

Cohort / File(s) Summary
Dependency & Type Definitions
package.json, src/platform/workflow/templates/types/template.ts
Added @comfyorg/ingest-types workspace dependency. Extended TemplateInfo interface with optional thumbnailUrl, thumbnailComparisonUrl, shareId, and profile fields to support hub workflow metadata.
Hub Adapter Layer
src/platform/workflow/templates/adapters/hubTemplateAdapter.ts
Created adapter module with adaptHubWorkflowToTemplate and adaptHubWorkflowsToCategories functions to map HubWorkflowSummary objects to TemplateInfo shape, including thumbnail variant mapping, metadata extraction, and category wrapping.
Hub Adapter Tests
src/platform/workflow/templates/adapters/hubTemplateAdapter.test.ts
Added comprehensive test suite covering both adapter functions, validating field mapping, metadata extraction, default behavior for missing fields, and category wrapping logic.
API Layer
src/scripts/api.ts
Added hub workflow retrieval methods: listAllHubWorkflows() for paginated workflow summary fetching and getHubWorkflowDetail(shareId) for detailed workflow JSON retrieval, including pagination helper and Zod validation.
Store Enhancement
src/platform/workflow/templates/repositories/workflowTemplatesStore.ts
Extended EnhancedTemplate with optional shareId field. Added getTemplateByShareId() lookup method. Modified fetchCoreTemplates() to conditionally fetch hub workflows via API when in cloud mode, adapting and caching them as core templates.
Composable Updates
src/platform/workflow/templates/composables/useTemplateUrlLoader.ts, src/platform/workflow/templates/composables/useTemplateWorkflows.ts
Updated useTemplateUrlLoader to perform cloud-specific template lookup via store when initial load fails. Modified useTemplateWorkflows to prefer absolute hub URLs, remap sourceModule from 'hub' to 'default', and fetch hub workflow JSON via API when template has shareId.
Composable Tests
src/platform/workflow/templates/composables/useTemplateUrlLoader.test.ts, src/platform/workflow/templates/composables/useTemplateWorkflows.test.ts
Extended mocks to stub getTemplateByShareId and getTemplateByName methods on the workflow templates store for improved test isolation.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/<br>useTemplateWorkflows
    participant Store as workflowTemplatesStore
    participant API as ComfyApi
    participant Adapter as hubTemplateAdapter
    participant Backend as Backend

    Client->>Store: getTemplateByName(name)
    alt Template found with shareId
        Store-->>Client: EnhancedTemplate{shareId}
        Client->>API: getHubWorkflowDetail(shareId)
        API->>Backend: GET /hub/workflows/{shareId}
        Backend-->>API: HubWorkflowDetail
        API-->>Client: workflow_json
    else No shareId or not found
        Client->>API: fetchTemplateJson(sourceModule)
        API-->>Client: workflow_json (default path)
    end

    Note over Store,Backend: First-time hub template load
    Client->>Store: fetchCoreTemplates()
    Store->>API: listAllHubWorkflows()
    API->>Backend: GET /hub/workflows (paginated)
    Backend-->>API: HubWorkflowSummary[]
    API-->>Store: HubWorkflowSummary[]
    Store->>Adapter: adaptHubWorkflowsToCategories(summaries)
    Adapter-->>Store: WorkflowTemplates[] (hub category)
    Store-->>Client: coreTemplates cached
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hub workflows hop into view,
Cloud templates fresh and new!
Adapters weave the data thread,
From hub summaries well-fed,
Pagination hops ahead! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective: migrating cloud template loading from static JSON to the hub API, which is the core focus of all changes in this PR.
Description check ✅ Passed The description comprehensively covers the template requirements: a clear one-sentence summary explaining what changed and why, detailed breakdown of changes across files, and a test plan. All required sections are present and well-populated.
End-To-End Regression Coverage For Fixes ✅ Passed PR title uses 'feat:' prefix (feature development language), not bug-fix language ('fix', 'bugfix', 'hotfix'). Criterion 1 satisfied.
Adr Compliance For Entity/Litegraph Changes ✅ Passed PR does not modify files under src/lib/litegraph/, src/ecs/, or graph entity-related files. Changes are limited to workflow templates.

✏️ 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 feat/hub-template-api-migration

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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

🎭 Playwright: ✅ 771 passed, 0 failed · 3 flaky

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

📦 Bundle: 5.1 MB gzip 🔴 +6.37 kB

Details

Summary

  • Raw size: 23.5 MB baseline 23.5 MB — 🔴 +47.7 kB
  • Gzip: 5.1 MB baseline 5.09 MB — 🔴 +6.37 kB
  • Brotli: 3.95 MB baseline 3.94 MB — 🔴 +5.25 kB
  • Bundles: 249 current • 249 baseline • 116 added / 116 removed

Category Glance
Data & Services 🔴 +46.9 kB (3.01 MB) · Vendor & Third-Party 🔴 +435 B (9.8 MB) · Graph Workspace 🔴 +391 B (1.14 MB) · Other ⚪ 0 B (8.45 MB) · Panels & Settings ⚪ 0 B (484 kB) · Utilities & Hooks ⚪ 0 B (334 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-C9_ATtlI.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +7.93 kB 🔴 +6.78 kB
assets/index-ZvLU3twH.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -7.93 kB 🟢 -6.8 kB

Status: 1 added / 1 removed

Graph Workspace — 1.14 MB (baseline 1.14 MB) • 🔴 +391 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-Cmm2m9mm.js (new) 1.14 MB 🔴 +1.14 MB 🔴 +244 kB 🔴 +184 kB
assets/GraphView-Drr_TyCA.js (removed) 1.14 MB 🟢 -1.14 MB 🟢 -244 kB 🟢 -184 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-Ck8SLhsN.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -3.4 kB 🟢 -2.9 kB
assets/CloudSurveyView-DlFcpl7w.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +3.4 kB 🔴 +2.91 kB
assets/CloudLoginView-DsNtK6kd.js (removed) 12 kB 🟢 -12 kB 🟢 -3.36 kB 🟢 -2.96 kB
assets/CloudLoginView-DYLtY5OJ.js (new) 12 kB 🔴 +12 kB 🔴 +3.36 kB 🔴 +2.96 kB
assets/CloudSignupView-BvxDq3eb.js (new) 9.78 kB 🔴 +9.78 kB 🔴 +2.85 kB 🔴 +2.51 kB
assets/CloudSignupView-HEEVFec7.js (removed) 9.78 kB 🟢 -9.78 kB 🟢 -2.85 kB 🟢 -2.51 kB
assets/UserCheckView-CN3egJep.js (new) 9.04 kB 🔴 +9.04 kB 🔴 +2.33 kB 🔴 +2.03 kB
assets/UserCheckView-DLJ5kMjT.js (removed) 9.04 kB 🟢 -9.04 kB 🟢 -2.33 kB 🟢 -2.03 kB
assets/CloudLayoutView-BCe6EBh6.js (removed) 7.54 kB 🟢 -7.54 kB 🟢 -2.37 kB 🟢 -2.07 kB
assets/CloudLayoutView-Dunjw-eb.js (new) 7.54 kB 🔴 +7.54 kB 🔴 +2.36 kB 🔴 +2.05 kB
assets/CloudForgotPasswordView-BDcMF8zG.js (new) 5.94 kB 🔴 +5.94 kB 🔴 +2.09 kB 🔴 +1.85 kB
assets/CloudForgotPasswordView-C0Dq33jt.js (removed) 5.94 kB 🟢 -5.94 kB 🟢 -2.1 kB 🟢 -1.85 kB
assets/CloudAuthTimeoutView-B_vr9EFS.js (new) 5.31 kB 🔴 +5.31 kB 🔴 +1.93 kB 🔴 +1.68 kB
assets/CloudAuthTimeoutView-DDpoGT6-.js (removed) 5.31 kB 🟢 -5.31 kB 🟢 -1.93 kB 🟢 -1.71 kB
assets/CloudSubscriptionRedirectView-CGZ0xVBT.js (new) 5.08 kB 🔴 +5.08 kB 🔴 +1.91 kB 🔴 +1.69 kB
assets/CloudSubscriptionRedirectView-CQhMuyJg.js (removed) 5.08 kB 🟢 -5.08 kB 🟢 -1.91 kB 🟢 -1.69 kB
assets/UserSelectView-BzWgPrlS.js (new) 4.71 kB 🔴 +4.71 kB 🔴 +1.74 kB 🔴 +1.54 kB
assets/UserSelectView-CX2V24jx.js (removed) 4.71 kB 🟢 -4.71 kB 🟢 -1.74 kB 🟢 -1.54 kB

Status: 9 added / 9 removed / 2 unchanged

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-4VIjsHoc.js (removed) 46.6 kB 🟢 -46.6 kB 🟢 -9.53 kB 🟢 -8.47 kB
assets/KeybindingPanel-CKRL4esW.js (new) 46.6 kB 🔴 +46.6 kB 🔴 +9.52 kB 🔴 +8.47 kB
assets/SecretsPanel-CfuQDE81.js (new) 22.4 kB 🔴 +22.4 kB 🔴 +5.42 kB 🔴 +4.76 kB
assets/SecretsPanel-DNeBQ8p5.js (removed) 22.4 kB 🟢 -22.4 kB 🟢 -5.42 kB 🟢 -4.76 kB
assets/LegacyCreditsPanel-CQUiNlGt.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -5.82 kB 🟢 -5.12 kB
assets/LegacyCreditsPanel-EnI71CJW.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +5.82 kB 🔴 +5.13 kB
assets/SubscriptionPanel-BdRJ3gWx.js (new) 19.7 kB 🔴 +19.7 kB 🔴 +5 kB 🔴 +4.41 kB
assets/SubscriptionPanel-BH5hbXtU.js (removed) 19.7 kB 🟢 -19.7 kB 🟢 -5.01 kB 🟢 -4.4 kB
assets/AboutPanel-DyLrPa6h.js (removed) 12 kB 🟢 -12 kB 🟢 -3.33 kB 🟢 -2.98 kB
assets/AboutPanel-NgsDsyM-.js (new) 12 kB 🔴 +12 kB 🔴 +3.32 kB 🔴 +2.98 kB
assets/ExtensionPanel-1vfOVtm-.js (removed) 9.78 kB 🟢 -9.78 kB 🟢 -2.82 kB 🟢 -2.51 kB
assets/ExtensionPanel-FeFwX5j_.js (new) 9.78 kB 🔴 +9.78 kB 🔴 +2.82 kB 🔴 +2.51 kB
assets/ServerConfigPanel-Cg-z9XEd.js (new) 6.85 kB 🔴 +6.85 kB 🔴 +2.27 kB 🔴 +2.03 kB
assets/ServerConfigPanel-E9ssqKNy.js (removed) 6.85 kB 🟢 -6.85 kB 🟢 -2.28 kB 🟢 -2.03 kB
assets/UserPanel-A0zSvjxi.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +2.15 kB 🔴 +1.89 kB
assets/UserPanel-DukGa32C.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -2.16 kB 🟢 -1.89 kB
assets/cloudRemoteConfig-B_lwXpXs.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +902 B 🔴 +795 B
assets/cloudRemoteConfig-DrjLcZ_u.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -903 B 🟢 -793 B
assets/refreshRemoteConfig-BsUFZRFV.js (new) 1.45 kB 🔴 +1.45 kB 🔴 +648 B 🔴 +553 B
assets/refreshRemoteConfig-DR0mixOR.js (removed) 1.45 kB 🟢 -1.45 kB 🟢 -651 B 🟢 -557 B

Status: 10 added / 10 removed / 12 unchanged

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-B8Ky6sT0.js (new) 3.57 kB 🔴 +3.57 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/auth-CbtwWjDq.js (removed) 3.57 kB 🟢 -3.57 kB 🟢 -1.26 kB 🟢 -1.07 kB
assets/SignUpForm-B2DV3g_e.js (removed) 3.16 kB 🟢 -3.16 kB 🟢 -1.28 kB 🟢 -1.14 kB
assets/SignUpForm-D5aTJyPZ.js (new) 3.16 kB 🔴 +3.16 kB 🔴 +1.29 kB 🔴 +1.15 kB
assets/UpdatePasswordContent-CJgnxm00.js (new) 2.66 kB 🔴 +2.66 kB 🔴 +1.19 kB 🔴 +1.05 kB
assets/UpdatePasswordContent-pvpOVNer.js (removed) 2.66 kB 🟢 -2.66 kB 🟢 -1.2 kB 🟢 -1.06 kB
assets/authStore-DlZqilQk.js (new) 989 B 🔴 +989 B 🔴 +484 B 🔴 +433 B
assets/authStore-mwI5h_s3.js (removed) 989 B 🟢 -989 B 🟢 -484 B 🟢 -429 B
assets/auth-BqwL_Ity.js (removed) 348 B 🟢 -348 B 🟢 -214 B 🟢 -189 B
assets/auth-e8VjOZVp.js (new) 348 B 🔴 +348 B 🔴 +214 B 🔴 +186 B

Status: 5 added / 5 removed / 2 unchanged

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useShareDialog-7gHD2kJQ.js (removed) 108 kB 🟢 -108 kB 🟢 -22.4 kB 🟢 -18.9 kB
assets/useShareDialog-DMHOJPwr.js (new) 108 kB 🔴 +108 kB 🔴 +22.4 kB 🔴 +18.9 kB
assets/useSubscriptionDialog-BLRtzAt0.js (new) 969 B 🔴 +969 B 🔴 +475 B 🔴 +417 B
assets/useSubscriptionDialog-Bt_5nC5L.js (removed) 969 B 🟢 -969 B 🟢 -478 B 🟢 -415 B

Status: 2 added / 2 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-Be0g8aB3.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/ComfyQueueButton-DWvqTQgs.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.79 kB 🟢 -3.37 kB
assets/useTerminalTabs-CfG_TonK.js (new) 10.7 kB 🔴 +10.7 kB 🔴 +3.6 kB 🔴 +3.16 kB
assets/useTerminalTabs-sGTyl74M.js (removed) 10.7 kB 🟢 -10.7 kB 🟢 -3.6 kB 🟢 -3.17 kB
assets/SubscribeButton-CcUe0zdc.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +912 B
assets/SubscribeButton-DgldcoWd.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -914 B
assets/cloudFeedbackTopbarButton-C2Twaj_c.js (removed) 1.66 kB 🟢 -1.66 kB 🟢 -847 B 🟢 -762 B
assets/cloudFeedbackTopbarButton-LuLDmzQu.js (new) 1.66 kB 🔴 +1.66 kB 🔴 +845 B 🔴 +753 B
assets/ComfyQueueButton-D75NTqqy.js (new) 1.03 kB 🔴 +1.03 kB 🔴 +488 B 🔴 +438 B
assets/ComfyQueueButton-DTqp23ti.js (removed) 1.03 kB 🟢 -1.03 kB 🟢 -491 B 🟢 -442 B

Status: 5 added / 5 removed / 8 unchanged

Data & Services — 3.01 MB (baseline 2.96 MB) • 🔴 +46.9 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-j9YjscTF.js (new) 1.93 MB 🔴 +1.93 MB 🔴 +444 kB 🔴 +337 kB
assets/dialogService-DSO1k9_w.js (removed) 1.92 MB 🟢 -1.92 MB 🟢 -443 kB 🟢 -336 kB
assets/api-aZszJx1y.js (new) 929 kB 🔴 +929 kB 🔴 +217 kB 🔴 +171 kB
assets/api-IQdz5qpQ.js (removed) 885 kB 🟢 -885 kB 🟢 -211 kB 🟢 -167 kB
assets/load3dService-BtxvpR6J.js (removed) 92.5 kB 🟢 -92.5 kB 🟢 -19.7 kB 🟢 -17 kB
assets/load3dService-CYXqUN0l.js (new) 92.5 kB 🔴 +92.5 kB 🔴 +19.7 kB 🔴 +17 kB
assets/workflowShareService-BC3fky4Y.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.88 kB 🔴 +4.32 kB
assets/workflowShareService-D0-IVAmy.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.88 kB 🟢 -4.32 kB
assets/keybindingService-CEq4tAc-.js (removed) 13.8 kB 🟢 -13.8 kB 🟢 -3.67 kB 🟢 -3.21 kB
assets/keybindingService-cPzbEndB.js (new) 13.8 kB 🔴 +13.8 kB 🔴 +3.67 kB 🔴 +3.21 kB
assets/releaseStore-ByeZUE_W.js (removed) 8.12 kB 🟢 -8.12 kB 🟢 -2.28 kB 🟢 -2 kB
assets/releaseStore-DNUZGlN0.js (new) 8.12 kB 🔴 +8.12 kB 🔴 +2.28 kB 🔴 +2 kB
assets/userStore-2xbqxUpH.js (new) 2.24 kB 🔴 +2.24 kB 🔴 +869 B 🔴 +769 B
assets/userStore-CAboWs0U.js (removed) 2.24 kB 🟢 -2.24 kB 🟢 -869 B 🟢 -762 B
assets/audioService-BiO-AwXw.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -879 B 🟢 -757 B
assets/audioService-jE6DiKcn.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +878 B 🔴 +754 B
assets/releaseStore-C7fFwSD5.js (removed) 993 B 🟢 -993 B 🟢 -483 B 🟢 -429 B
assets/releaseStore-CVbZ79um.js (new) 993 B 🔴 +993 B 🔴 +480 B 🔴 +430 B
assets/workflowDraftStore-C07F5o6u.js (new) 969 B 🔴 +969 B 🔴 +472 B 🔴 +424 B
assets/workflowDraftStore-ClEfJcBW.js (removed) 969 B 🟢 -969 B 🟢 -477 B 🟢 -423 B
assets/dialogService-BSBjL6vj.js (new) 958 B 🔴 +958 B 🔴 +466 B 🔴 +417 B
assets/dialogService-I_TXLPHR.js (removed) 958 B 🟢 -958 B 🟢 -469 B 🟢 -414 B
assets/settingStore-DTWg1mNB.js (new) 956 B 🔴 +956 B 🔴 +469 B 🔴 +416 B
assets/settingStore-HRCl1MtE.js (removed) 956 B 🟢 -956 B 🟢 -472 B 🟢 -416 B
assets/assetsStore-BkEvLwnb.js (new) 955 B 🔴 +955 B 🔴 +469 B 🔴 +417 B
assets/assetsStore-CqqP4vs9.js (removed) 955 B 🟢 -955 B 🟢 -472 B 🟢 -415 B

Status: 13 added / 13 removed / 4 unchanged

Utilities & Hooks — 334 kB (baseline 334 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-B6sy91Ms.js (new) 232 kB 🔴 +232 kB 🔴 +51.3 kB 🔴 +41.8 kB
assets/useConflictDetection-BeU__kH2.js (removed) 232 kB 🟢 -232 kB 🟢 -51.3 kB 🟢 -41.8 kB
assets/useLoad3dViewer-68MLK5yT.js (removed) 18.7 kB 🟢 -18.7 kB 🟢 -4.44 kB 🟢 -3.87 kB
assets/useLoad3dViewer-BkpJ5eU3.js (new) 18.7 kB 🔴 +18.7 kB 🔴 +4.44 kB 🔴 +3.91 kB
assets/useLoad3d-DNQxljnS.js (new) 15 kB 🔴 +15 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/useLoad3d-WuBvl7Ca.js (removed) 15 kB 🟢 -15 kB 🟢 -3.79 kB 🟢 -3.35 kB
assets/useFeatureFlags-NJSV7ARC.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.75 kB 🟢 -1.48 kB
assets/useFeatureFlags-uQ6L_1hu.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.75 kB 🔴 +1.48 kB
assets/useCopyToClipboard-BXEHZiwm.js (new) 5.29 kB 🔴 +5.29 kB 🔴 +1.86 kB 🔴 +1.57 kB
assets/useCopyToClipboard-EY7AZTI_.js (removed) 5.29 kB 🟢 -5.29 kB 🟢 -1.86 kB 🟢 -1.57 kB
assets/useWorkspaceUI-D2jOliph.js (new) 3.34 kB 🔴 +3.34 kB 🔴 +980 B 🔴 +810 B
assets/useWorkspaceUI-HOXQhcuL.js (removed) 3.34 kB 🟢 -3.34 kB 🟢 -981 B 🟢 -810 B
assets/subscriptionCheckoutUtil-C4C7JtMB.js (removed) 2.97 kB 🟢 -2.97 kB 🟢 -1.31 kB 🟢 -1.14 kB
assets/subscriptionCheckoutUtil-wvClrsjd.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +1.3 kB 🔴 +1.14 kB
assets/assetPreviewUtil-D6MoyJ1J.js (new) 2.27 kB 🔴 +2.27 kB 🔴 +958 B 🔴 +831 B
assets/assetPreviewUtil-jo1jXqR3.js (removed) 2.27 kB 🟢 -2.27 kB 🟢 -960 B 🟢 -834 B
assets/useUpstreamValue-Bkwt8xJk.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -807 B 🟢 -710 B
assets/useUpstreamValue-C-QBU_G-.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +805 B 🔴 +722 B
assets/useLoad3d-C7oSVcGj.js (new) 1.13 kB 🔴 +1.13 kB 🔴 +538 B 🔴 +486 B
assets/useLoad3d-QfYUbqo0.js (removed) 1.13 kB 🟢 -1.13 kB 🟢 -539 B 🟢 -483 B
assets/useLoad3dViewer-C_Uhv121.js (new) 1.07 kB 🔴 +1.07 kB 🔴 +505 B 🔴 +458 B
assets/useLoad3dViewer-rnU81EOA.js (removed) 1.07 kB 🟢 -1.07 kB 🟢 -507 B 🟢 -456 B
assets/useCurrentUser-C-5MV5Lk.js (new) 955 B 🔴 +955 B 🔴 +470 B 🔴 +414 B
assets/useCurrentUser-CxPfJ9V-.js (removed) 955 B 🟢 -955 B 🟢 -472 B 🟢 -416 B
assets/useWorkspaceSwitch-B1UxF5vL.js (new) 747 B 🔴 +747 B 🔴 +386 B 🔴 +326 B
assets/useWorkspaceSwitch-DavZ1Bpo.js (removed) 747 B 🟢 -747 B 🟢 -385 B 🟢 -336 B

Status: 13 added / 13 removed / 12 unchanged

Vendor & Third-Party — 9.8 MB (baseline 9.8 MB) • 🔴 +435 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-zod-CLwkGt0g.js (new) 105 kB 🔴 +105 kB 🔴 +18.8 kB 🔴 +16 kB
assets/vendor-zod-Dg3yaIzQ.js (removed) 105 kB 🟢 -105 kB 🟢 -18.7 kB 🟢 -15.9 kB

Status: 1 added / 1 removed / 15 unchanged

Other — 8.45 MB (baseline 8.45 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-B-LGS1Ih.js (new) 76.8 kB 🔴 +76.8 kB 🔴 +19.9 kB 🔴 +17 kB
assets/core-DDb5UjbX.js (removed) 76.8 kB 🟢 -76.8 kB 🟢 -19.9 kB 🟢 -16.9 kB
assets/groupNode-D9PtTSVL.js (new) 74 kB 🔴 +74 kB 🔴 +18.5 kB 🔴 +16.3 kB
assets/groupNode-DV8ogRYQ.js (removed) 74 kB 🟢 -74 kB 🟢 -18.5 kB 🟢 -16.3 kB
assets/WidgetSelect-CZlalHm8.js (removed) 64.6 kB 🟢 -64.6 kB 🟢 -14.1 kB 🟢 -12.2 kB
assets/WidgetSelect-Davm9x7A.js (new) 64.6 kB 🔴 +64.6 kB 🔴 +14.1 kB 🔴 +12.2 kB
assets/SubscriptionRequiredDialogContentWorkspace-58VdscwM.js (new) 48.9 kB 🔴 +48.9 kB 🔴 +9.28 kB 🔴 +7.94 kB
assets/SubscriptionRequiredDialogContentWorkspace-og5LNkRX.js (removed) 48.9 kB 🟢 -48.9 kB 🟢 -9.3 kB 🟢 -7.97 kB
assets/WidgetPainter-BkJbb8Di.js (removed) 33.3 kB 🟢 -33.3 kB 🟢 -8.11 kB 🟢 -7.19 kB
assets/WidgetPainter-DTcnTdIG.js (new) 33.3 kB 🔴 +33.3 kB 🔴 +8.11 kB 🔴 +7.18 kB
assets/Load3DControls-Cpa13Idj.js (new) 32.1 kB 🔴 +32.1 kB 🔴 +5.47 kB 🔴 +4.76 kB
assets/Load3DControls-U9FspAzZ.js (removed) 32.1 kB 🟢 -32.1 kB 🟢 -5.47 kB 🟢 -4.77 kB
assets/WorkspacePanelContent-CgXsiKdM.js (new) 29.9 kB 🔴 +29.9 kB 🔴 +6.33 kB 🔴 +5.56 kB
assets/WorkspacePanelContent-CM9pq1fz.js (removed) 29.9 kB 🟢 -29.9 kB 🟢 -6.33 kB 🟢 -5.55 kB
assets/SubscriptionRequiredDialogContent-Cj5Hj79b.js (removed) 28.2 kB 🟢 -28.2 kB 🟢 -7.17 kB 🟢 -6.31 kB
assets/SubscriptionRequiredDialogContent-mslL8lgJ.js (new) 28.2 kB 🔴 +28.2 kB 🔴 +7.16 kB 🔴 +6.29 kB
assets/Load3dViewerContent-Bj9LKFzQ.js (new) 24.3 kB 🔴 +24.3 kB 🔴 +5.32 kB 🔴 +4.63 kB
assets/Load3dViewerContent-Mfxn5MHf.js (removed) 24.3 kB 🟢 -24.3 kB 🟢 -5.32 kB 🟢 -4.64 kB
assets/WidgetImageCrop-CJCbgIPZ.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.83 kB 🟢 -5.14 kB
assets/WidgetImageCrop-vWFWrMlz.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.83 kB 🔴 +5.14 kB
assets/SubscriptionPanelContentWorkspace-BG1r9zjG.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.17 kB 🔴 +4.55 kB
assets/SubscriptionPanelContentWorkspace-CQdbaknN.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.18 kB 🟢 -4.56 kB
assets/SignInContent-BCD3rgnc.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.29 kB 🟢 -4.62 kB
assets/SignInContent-mBttbu9g.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.29 kB 🔴 +4.62 kB
assets/CurrentUserPopoverWorkspace-tKFTx6Yw.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +4.83 kB 🔴 +4.34 kB
assets/CurrentUserPopoverWorkspace-z_n5sFvT.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -4.83 kB 🟢 -4.33 kB
assets/WidgetInputNumber-CM2FE92Q.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.84 kB 🟢 -4.3 kB
assets/WidgetInputNumber-CR0Um2sf.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.84 kB 🔴 +4.3 kB
assets/WidgetRecordAudio-BU_NKTCU.js (removed) 18.1 kB 🟢 -18.1 kB 🟢 -5.18 kB 🟢 -4.64 kB
assets/WidgetRecordAudio-D7XrSOfP.js (new) 18.1 kB 🔴 +18.1 kB 🔴 +5.18 kB 🔴 +4.64 kB
assets/Load3D-Ba8Nl9mO.js (removed) 16.9 kB 🟢 -16.9 kB 🟢 -4.12 kB 🟢 -3.59 kB
assets/Load3D-Cf4TSMZr.js (new) 16.9 kB 🔴 +16.9 kB 🔴 +4.12 kB 🔴 +3.59 kB
assets/WidgetCurve-BRPH6XUv.js (new) 16.1 kB 🔴 +16.1 kB 🔴 +4.97 kB 🔴 +4.45 kB
assets/WidgetCurve-CtRG2qfF.js (removed) 16.1 kB 🟢 -16.1 kB 🟢 -4.97 kB 🟢 -4.45 kB
assets/load3d-B5uwPh1c.js (removed) 15 kB 🟢 -15 kB 🟢 -4.33 kB 🟢 -3.74 kB
assets/load3d-DxlxEWoJ.js (new) 15 kB 🔴 +15 kB 🔴 +4.32 kB 🔴 +3.74 kB
assets/WaveAudioPlayer-CiR5iW4m.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.68 kB 🟢 -3.23 kB
assets/WaveAudioPlayer-DZSnBaRx.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.68 kB 🔴 +3.22 kB
assets/TeamWorkspacesDialogContent-BdK_5Wrx.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -3.34 kB 🟢 -2.98 kB
assets/TeamWorkspacesDialogContent-BLJgX1vi.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +3.33 kB 🔴 +2.97 kB
assets/nodeTemplates-DIPoulCT.js (removed) 9.58 kB 🟢 -9.58 kB 🟢 -3.37 kB 🟢 -2.97 kB
assets/nodeTemplates-oiQosOxZ.js (new) 9.58 kB 🔴 +9.58 kB 🔴 +3.37 kB 🔴 +2.97 kB
assets/InviteMemberDialogContent-B9vqaHsm.js (new) 7.77 kB 🔴 +7.77 kB 🔴 +2.45 kB 🔴 +2.14 kB
assets/InviteMemberDialogContent-DlPGX-1G.js (removed) 7.77 kB 🟢 -7.77 kB 🟢 -2.45 kB 🟢 -2.14 kB
assets/Load3DConfiguration-D9qZg0wx.js (removed) 6.6 kB 🟢 -6.6 kB 🟢 -2.04 kB 🟢 -1.78 kB
assets/Load3DConfiguration-Q479Rh_V.js (new) 6.6 kB 🔴 +6.6 kB 🔴 +2.04 kB 🔴 +1.78 kB
assets/onboardingCloudRoutes-BIZPsz9V.js (removed) 6.53 kB 🟢 -6.53 kB 🟢 -2.04 kB 🟢 -1.75 kB
assets/onboardingCloudRoutes-C5Zal7bZ.js (new) 6.53 kB 🔴 +6.53 kB 🔴 +2.03 kB 🔴 +1.75 kB
assets/WidgetWithControl-BFkNX4nk.js (removed) 5.99 kB 🟢 -5.99 kB 🟢 -2.38 kB 🟢 -2.12 kB
assets/WidgetWithControl-Bmpo2EBs.js (new) 5.99 kB 🔴 +5.99 kB 🔴 +2.38 kB 🔴 +2.12 kB
assets/CreateWorkspaceDialogContent-C6LzwpA5.js (removed) 5.95 kB 🟢 -5.95 kB 🟢 -2.15 kB 🟢 -1.88 kB
assets/CreateWorkspaceDialogContent-mPPsjK9R.js (new) 5.95 kB 🔴 +5.95 kB 🔴 +2.15 kB 🔴 +1.88 kB
assets/FreeTierDialogContent-CQ7wxvwu.js (removed) 5.82 kB 🟢 -5.82 kB 🟢 -2.05 kB 🟢 -1.81 kB
assets/FreeTierDialogContent-ouLCvzyH.js (new) 5.82 kB 🔴 +5.82 kB 🔴 +2.05 kB 🔴 +1.8 kB
assets/EditWorkspaceDialogContent-CBZR65b9.js (removed) 5.75 kB 🟢 -5.75 kB 🟢 -2.12 kB 🟢 -1.85 kB
assets/EditWorkspaceDialogContent-DPX24_2E.js (new) 5.75 kB 🔴 +5.75 kB 🔴 +2.11 kB 🔴 +1.85 kB
assets/WidgetTextarea-3yxvnpZS.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +2.17 kB 🔴 +1.92 kB
assets/WidgetTextarea-o_FiYeMt.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -2.17 kB 🟢 -1.93 kB
assets/Preview3d--Hyfeurb.js (new) 5.36 kB 🔴 +5.36 kB 🔴 +1.79 kB 🔴 +1.56 kB
assets/Preview3d-rocXBvns.js (removed) 5.36 kB 🟢 -5.36 kB 🟢 -1.8 kB 🟢 -1.56 kB
assets/ValueControlPopover-C9FyRQ6n.js (new) 5.33 kB 🔴 +5.33 kB 🔴 +1.93 kB 🔴 +1.72 kB
assets/ValueControlPopover-Du_m6DWZ.js (removed) 5.33 kB 🟢 -5.33 kB 🟢 -1.93 kB 🟢 -1.72 kB
assets/CancelSubscriptionDialogContent-B4GOiTUk.js (removed) 5.22 kB 🟢 -5.22 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/CancelSubscriptionDialogContent-B9RrxSMY.js (new) 5.22 kB 🔴 +5.22 kB 🔴 +1.95 kB 🔴 +1.71 kB
assets/DeleteWorkspaceDialogContent-lykF0SZ7.js (removed) 4.65 kB 🟢 -4.65 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/DeleteWorkspaceDialogContent-MVLX4vhM.js (new) 4.65 kB 🔴 +4.65 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/LeaveWorkspaceDialogContent-BKQoTfQY.js (new) 4.48 kB 🔴 +4.48 kB 🔴 +1.74 kB 🔴 +1.51 kB
assets/LeaveWorkspaceDialogContent-Ct8sMMA-.js (removed) 4.48 kB 🟢 -4.48 kB 🟢 -1.74 kB 🟢 -1.51 kB
assets/RemoveMemberDialogContent-B0biRWQD.js (removed) 4.46 kB 🟢 -4.46 kB 🟢 -1.69 kB 🟢 -1.47 kB
assets/RemoveMemberDialogContent-De3Sl1Rf.js (new) 4.46 kB 🔴 +4.46 kB 🔴 +1.69 kB 🔴 +1.47 kB
assets/tierBenefits-Cb3ngaey.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.57 kB 🟢 -1.36 kB
assets/tierBenefits-DzpcHwFF.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.57 kB 🔴 +1.36 kB
assets/RevokeInviteDialogContent-B3sA0ftz.js (new) 4.37 kB 🔴 +4.37 kB 🔴 +1.7 kB 🔴 +1.49 kB
assets/RevokeInviteDialogContent-Do75tldb.js (removed) 4.37 kB 🟢 -4.37 kB 🟢 -1.7 kB 🟢 -1.49 kB
assets/InviteMemberUpsellDialogContent-BYRMkBn_.js (new) 4.27 kB 🔴 +4.27 kB 🔴 +1.56 kB 🔴 +1.37 kB
assets/InviteMemberUpsellDialogContent-VUbtnFle.js (removed) 4.27 kB 🟢 -4.27 kB 🟢 -1.57 kB 🟢 -1.37 kB
assets/cloudSessionCookie-D5dnsK4t.js (new) 4.12 kB 🔴 +4.12 kB 🔴 +1.49 kB 🔴 +1.29 kB
assets/cloudSessionCookie-DgQLGYJi.js (removed) 4.12 kB 🟢 -4.12 kB 🟢 -1.49 kB 🟢 -1.3 kB
assets/saveMesh-CdGnQPLS.js (new) 3.92 kB 🔴 +3.92 kB 🔴 +1.68 kB 🔴 +1.48 kB
assets/saveMesh-DNG2St4T.js (removed) 3.92 kB 🟢 -3.92 kB 🟢 -1.69 kB 🟢 -1.49 kB
assets/Media3DTop-B0kpDGIC.js (new) 3.85 kB 🔴 +3.85 kB 🔴 +1.62 kB 🔴 +1.42 kB
assets/Media3DTop-PHfeqae7.js (removed) 3.85 kB 🟢 -3.85 kB 🟢 -1.63 kB 🟢 -1.43 kB
assets/GlobalToast-a8rwg-UO.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.25 kB 🟢 -1.1 kB
assets/GlobalToast-Dmf9SQjM.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/SubscribeToRun-CcI_v6jK.js (new) 2.13 kB 🔴 +2.13 kB 🔴 +984 B 🔴 +868 B
assets/SubscribeToRun-CK2RmYGB.js (removed) 2.13 kB 🟢 -2.13 kB 🟢 -982 B 🟢 -866 B
assets/MediaAudioTop-wZa91Q0o.js (removed) 2.02 kB 🟢 -2.02 kB 🟢 -981 B 🟢 -833 B
assets/MediaAudioTop-XqK6j2O8.js (new) 2.02 kB 🔴 +2.02 kB 🔴 +982 B 🔴 +831 B
assets/CloudRunButtonWrapper-CjB3ytCk.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +912 B 🔴 +809 B
assets/CloudRunButtonWrapper-YWmmOi47.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -915 B 🟢 -814 B
assets/graphHasMissingNodes-Cx74_fc1.js (new) 1.83 kB 🔴 +1.83 kB 🔴 +861 B 🔴 +752 B
assets/graphHasMissingNodes-l4nix4ep.js (removed) 1.83 kB 🟢 -1.83 kB 🟢 -860 B 🟢 -752 B
assets/cloudBadges-BVkF_IOT.js (new) 1.77 kB 🔴 +1.77 kB 🔴 +893 B 🔴 +776 B
assets/cloudBadges-jW6IBwwi.js (removed) 1.77 kB 🟢 -1.77 kB 🟢 -896 B 🟢 -777 B
assets/cloudSubscription-Cl2hwgUX.js (removed) 1.68 kB 🟢 -1.68 kB 🟢 -818 B 🟢 -714 B
assets/cloudSubscription-CZueaujO.js (new) 1.68 kB 🔴 +1.68 kB 🔴 +812 B 🔴 +701 B
assets/signInSchema-BNDmjJjL.js (removed) 1.6 kB 🟢 -1.6 kB 🟢 -585 B 🟢 -510 B
assets/signInSchema-waZb4GWH.js (new) 1.6 kB 🔴 +1.6 kB 🔴 +586 B 🔴 +553 B
assets/previousFullPath-B5t2D_hR.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +695 B 🔴 +601 B
assets/previousFullPath-Dnrx7opu.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -695 B 🟢 -602 B
assets/Load3D-CWis04ag.js (removed) 1.34 kB 🟢 -1.34 kB 🟢 -619 B 🟢 -557 B
assets/Load3D-DDOwB02U.js (new) 1.34 kB 🔴 +1.34 kB 🔴 +615 B 🔴 +545 B
assets/nightlyBadges-DEjqrVM7.js (removed) 1.29 kB 🟢 -1.29 kB 🟢 -660 B 🟢 -584 B
assets/nightlyBadges-GoBv-Bwg.js (new) 1.29 kB 🔴 +1.29 kB 🔴 +656 B 🔴 +575 B
assets/Load3dViewerContent-Ch_zgpA6.js (removed) 1.23 kB 🟢 -1.23 kB 🟢 -570 B 🟢 -503 B
assets/Load3dViewerContent-lIC2XUj6.js (new) 1.23 kB 🔴 +1.23 kB 🔴 +568 B 🔴 +502 B
assets/SubscriptionPanelContentWorkspace-BcsePlJ_.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +534 B 🔴 +467 B
assets/SubscriptionPanelContentWorkspace-D9V5v4jl.js (removed) 1.15 kB 🟢 -1.15 kB 🟢 -536 B 🟢 -467 B
assets/WidgetLegacy-BJl7KHE1.js (new) 978 B 🔴 +978 B 🔴 +480 B 🔴 +425 B
assets/WidgetLegacy-D-15wOrc.js (removed) 978 B 🟢 -978 B 🟢 -485 B 🟢 -423 B
assets/changeTracker-D7jESMVY.js (removed) 952 B 🟢 -952 B 🟢 -472 B 🟢 -415 B
assets/changeTracker-DdUiaoa3.js (new) 952 B 🔴 +952 B 🔴 +469 B 🔴 +415 B

Status: 56 added / 56 removed / 78 unchanged

⚡ Performance Report

⚠️ 7 regressions detected

Metric Baseline PR (median) Δ Sig
canvas-mouse-sweep: style recalc count 76 84 +11% ⚠️ z=2.3
canvas-mouse-sweep: task duration 745ms 1004ms +35% ⚠️ z=2.4
canvas-zoom-sweep: frame duration 17ms 17ms -0% ⚠️ z=2.2
large-graph-pan: task duration 1066ms 1215ms +14% ⚠️ z=3.1
subgraph-mouse-sweep: task duration 695ms 931ms +34% ⚠️ z=2.4
subgraph-mouse-sweep: DOM nodes 66 72 +9% ⚠️ z=2.3
workflow-execution: event listeners 37 71 +92% ⚠️ z=4.4
All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 9ms 10ms +16% z=-0.6
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 11 10 -9% z=-2.1
canvas-idle: task duration 370ms 426ms +15% z=1.0
canvas-idle: script duration 25ms 29ms +15% z=1.5
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: frame duration 17ms 17ms +0% z=0.8
canvas-idle: heap used 60.0 MB 57.9 MB -4%
canvas-idle: DOM nodes 22 20 -9% z=-2.0
canvas-idle: event listeners 6 6 +0% z=-1.2
canvas-mouse-sweep: layout duration 3ms 3ms -1% z=-1.0
canvas-mouse-sweep: style recalc duration 37ms 45ms +19% z=0.6
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 76 84 +11% ⚠️ z=2.3
canvas-mouse-sweep: task duration 745ms 1004ms +35% ⚠️ z=2.4
canvas-mouse-sweep: script duration 119ms 147ms +23% z=1.7
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: frame duration 17ms 17ms +0% z=-2.6
canvas-mouse-sweep: heap used 56.1 MB 53.0 MB -5%
canvas-mouse-sweep: DOM nodes 60 67 +12% z=1.8
canvas-mouse-sweep: event listeners 4 6 +50% z=-0.6
canvas-zoom-sweep: layout duration 1ms 1ms +20% z=1.6
canvas-zoom-sweep: style recalc duration 18ms 21ms +16% z=0.9
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 30 31 +3% z=-0.6
canvas-zoom-sweep: task duration 301ms 351ms +17% z=1.1
canvas-zoom-sweep: script duration 24ms 32ms +34% z=1.6
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: frame duration 17ms 17ms -0% ⚠️ z=2.2
canvas-zoom-sweep: heap used 64.3 MB 46.0 MB -28%
canvas-zoom-sweep: DOM nodes 79 79 +0% z=-0.3
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 11ms 10ms -2% z=0.6
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 13 13 +0% z=-0.2
dom-widget-clipping: task duration 352ms 372ms +6% z=0.5
dom-widget-clipping: script duration 61ms 69ms +14% z=0.5
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: frame duration 17ms 17ms +0% z=0.9
dom-widget-clipping: heap used 47.0 MB 53.9 MB +15%
dom-widget-clipping: DOM nodes 22 22 +0% z=-0.1
dom-widget-clipping: event listeners 2 2 +0% variance too high
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 10ms +17% z=-2.1
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 11 10 -9% z=-5.1
large-graph-idle: task duration 548ms 625ms +14% z=1.5
large-graph-idle: script duration 90ms 112ms +24% z=0.9
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: frame duration 17ms 17ms +0% z=0.6
large-graph-idle: heap used 52.3 MB 63.0 MB +20%
large-graph-idle: DOM nodes -256 -258 +1% z=-312.8
large-graph-idle: event listeners -125 -129 +3% z=-25.4
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 17ms 17ms +2% z=-0.4
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 70 69 -1% z=-0.9
large-graph-pan: task duration 1066ms 1215ms +14% ⚠️ z=3.1
large-graph-pan: script duration 372ms 439ms +18% z=1.5
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: frame duration 17ms 17ms -0% z=-1.8
large-graph-pan: heap used 64.9 MB 68.5 MB +6%
large-graph-pan: DOM nodes -259 -261 +1% z=-169.7
large-graph-pan: event listeners -125 -127 +2% z=-159.3
large-graph-zoom: layout duration 8ms 8ms +2%
large-graph-zoom: style recalc duration 17ms 17ms -3%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 65 -2%
large-graph-zoom: task duration 1343ms 1441ms +7%
large-graph-zoom: script duration 483ms 549ms +14%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: frame duration 17ms 17ms -0%
large-graph-zoom: heap used 61.1 MB 60.0 MB -2%
large-graph-zoom: DOM nodes -262 -267 +2%
large-graph-zoom: event listeners -123 -123 +0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 9ms 9ms -3% z=-1.0
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 10 8 -20% z=-2.3
minimap-idle: task duration 555ms 616ms +11% z=1.9
minimap-idle: script duration 95ms 107ms +13% z=0.9
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: frame duration 17ms 17ms +0% z=-2.0
minimap-idle: heap used 54.1 MB 52.9 MB -2%
minimap-idle: DOM nodes -258 -263 +2% z=-206.3
minimap-idle: event listeners -127 -127 +0% z=-199.3
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 12ms 12ms +6% z=-0.3
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 48 48 +0% z=0.1
subgraph-dom-widget-clipping: task duration 350ms 383ms +10% z=0.3
subgraph-dom-widget-clipping: script duration 117ms 131ms +12% z=0.4
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: frame duration 17ms 17ms +0% z=1.2
subgraph-dom-widget-clipping: heap used 47.1 MB 44.1 MB -6%
subgraph-dom-widget-clipping: DOM nodes 22 21 -5% z=-1.1
subgraph-dom-widget-clipping: event listeners 8 8 +0% z=-1.4
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 9ms 11ms +20% z=1.1
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 11 11 +0% z=0.2
subgraph-idle: task duration 348ms 414ms +19% z=1.4
subgraph-idle: script duration 19ms 23ms +25% z=1.1
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: frame duration 17ms 17ms +0% z=1.5
subgraph-idle: heap used 59.8 MB 41.8 MB -30%
subgraph-idle: DOM nodes 22 22 +0% z=0.1
subgraph-idle: event listeners 6 6 +0% variance too high
subgraph-mouse-sweep: layout duration 5ms 5ms -3% z=-0.3
subgraph-mouse-sweep: style recalc duration 46ms 48ms +5% z=1.6
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 78 83 +6% z=1.5
subgraph-mouse-sweep: task duration 695ms 931ms +34% ⚠️ z=2.4
subgraph-mouse-sweep: script duration 92ms 109ms +18% z=1.2
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: frame duration 17ms 17ms +0% z=1.2
subgraph-mouse-sweep: heap used 52.2 MB 59.8 MB +15%
subgraph-mouse-sweep: DOM nodes 66 72 +9% ⚠️ z=2.3
subgraph-mouse-sweep: event listeners 4 6 +50% variance too high
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 47ms 45ms -4%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 252 252 +0%
viewport-pan-sweep: task duration 3919ms 3893ms -1%
viewport-pan-sweep: script duration 1281ms 1287ms +0%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: frame duration 17ms 17ms +0%
viewport-pan-sweep: heap used 67.4 MB 67.7 MB +0%
viewport-pan-sweep: DOM nodes -256 -256 +0%
viewport-pan-sweep: event listeners -109 -111 +2%
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 10590ms 12597ms +19%
vue-large-graph-idle: script duration 557ms 639ms +15%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: frame duration 18ms 18ms +0%
vue-large-graph-idle: heap used 157.8 MB 155.0 MB -2%
vue-large-graph-idle: DOM nodes -8331 -8331 +0%
vue-large-graph-idle: event listeners -16468 -16462 -0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 15ms 15ms -0%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 67 66 -1%
vue-large-graph-pan: task duration 12657ms 14551ms +15%
vue-large-graph-pan: script duration 807ms 870ms +8%
vue-large-graph-pan: TBT 33ms 0ms -100%
vue-large-graph-pan: frame duration 18ms 18ms -0%
vue-large-graph-pan: heap used 141.7 MB 163.9 MB +16%
vue-large-graph-pan: DOM nodes -8331 -8331 +0%
vue-large-graph-pan: event listeners -16470 -16458 -0%
workflow-execution: layout duration 1ms 1ms -5% z=-1.1
workflow-execution: style recalc duration 18ms 24ms +35% z=-0.1
workflow-execution: layout count 5 5 +0% z=0.1
workflow-execution: style recalc count 11 18 +64% z=0.1
workflow-execution: task duration 93ms 131ms +42% z=0.8
workflow-execution: script duration 24ms 29ms +23% z=0.0
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: frame duration 17ms 17ms +0% z=1.6
workflow-execution: heap used 46.2 MB 48.3 MB +5%
workflow-execution: DOM nodes 158 158 +0% z=-0.4
workflow-execution: event listeners 37 71 +92% ⚠️ z=4.4
Historical variance (last 15 runs)
Metric μ σ CV
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: frame duration 17ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
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: frame duration 17ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
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: frame duration 17ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
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: frame duration 17ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
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: frame duration 17ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
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: frame duration 17ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
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: frame duration 17ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
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: frame duration 17ms 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: 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: frame duration 17ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
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: frame duration 17ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
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: frame duration 17ms 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: 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: frame duration ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
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: frame duration ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
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: frame duration ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
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: frame duration ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
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: frame duration ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
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: frame duration ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
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: frame duration ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
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: frame duration ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
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: frame duration ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
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: frame duration ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
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: frame duration ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-03-29T05:34:05.078Z",
  "gitSha": "9e60e858c4480373d200ced92693da0c16ccf8c2",
  "branch": "feat/hub-template-api-migration",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2026.5210000000025,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.969999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 450.66,
      "heapDeltaBytes": 10373836,
      "heapUsedBytes": 60694464,
      "domNodes": 19,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 24.761,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-idle",
      "durationMs": 2055.4030000000125,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.288,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 425.38399999999996,
      "heapDeltaBytes": 10744160,
      "heapUsedBytes": 60744332,
      "domNodes": 22,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 28.514000000000003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-idle",
      "durationMs": 2034.7850000000562,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.31,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 425.77099999999996,
      "heapDeltaBytes": 10680124,
      "heapUsedBytes": 60689812,
      "domNodes": 20,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 30.662000000000003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2138.231000000019,
      "styleRecalcs": 84,
      "styleRecalcDurationMs": 50.281000000000006,
      "layouts": 12,
      "layoutDurationMs": 3.7300000000000004,
      "taskDurationMs": 1143.557,
      "heapDeltaBytes": 5713044,
      "heapUsedBytes": 55829064,
      "domNodes": 67,
      "jsHeapTotalBytes": 26214400,
      "scriptDurationMs": 154.88299999999998,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1805.4750000000013,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 37.591,
      "layouts": 12,
      "layoutDurationMs": 3.4070000000000005,
      "taskDurationMs": 779.2869999999999,
      "heapDeltaBytes": 5399604,
      "heapUsedBytes": 55529792,
      "domNodes": 60,
      "jsHeapTotalBytes": 25952256,
      "scriptDurationMs": 138.294,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2056.4729999999827,
      "styleRecalcs": 84,
      "styleRecalcDurationMs": 44.505,
      "layouts": 12,
      "layoutDurationMs": 3.384,
      "taskDurationMs": 1003.6160000000001,
      "heapDeltaBytes": 5770708,
      "heapUsedBytes": 55563836,
      "domNodes": 67,
      "jsHeapTotalBytes": 26214400,
      "scriptDurationMs": 146.55400000000003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1734.6889999999462,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 20.552,
      "layouts": 6,
      "layoutDurationMs": 0.717,
      "taskDurationMs": 379.91,
      "heapDeltaBytes": -153484,
      "heapUsedBytes": 48215936,
      "domNodes": 80,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 33.912,
      "eventListeners": 23,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1744.1579999999703,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 21.261000000000003,
      "layouts": 6,
      "layoutDurationMs": 0.811,
      "taskDurationMs": 351.30499999999995,
      "heapDeltaBytes": -178000,
      "heapUsedBytes": 48238360,
      "domNodes": 79,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 31.834,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1725.5790000000388,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 18.433999999999997,
      "layouts": 6,
      "layoutDurationMs": 0.5889999999999999,
      "taskDurationMs": 339.998,
      "heapDeltaBytes": 14552288,
      "heapUsedBytes": 64486020,
      "domNodes": 78,
      "jsHeapTotalBytes": 19398656,
      "scriptDurationMs": 28.433999999999994,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 696.5859999999964,
      "styleRecalcs": 14,
      "styleRecalcDurationMs": 11.242000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 425.705,
      "heapDeltaBytes": -2998384,
      "heapUsedBytes": 46961936,
      "domNodes": 23,
      "jsHeapTotalBytes": 15728640,
      "scriptDurationMs": 76.271,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 586.4090000000033,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 10.347999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 372.439,
      "heapDeltaBytes": 8045920,
      "heapUsedBytes": 56525120,
      "domNodes": 20,
      "jsHeapTotalBytes": 13631488,
      "scriptDurationMs": 69.47,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 570.6710000000612,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 9.833,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 353.272,
      "heapDeltaBytes": 8443924,
      "heapUsedBytes": 56979028,
      "domNodes": 22,
      "jsHeapTotalBytes": 14155776,
      "scriptDurationMs": 69.063,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2035.1330000000019,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.954,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 638.4590000000001,
      "heapDeltaBytes": 13981128,
      "heapUsedBytes": 66060752,
      "domNodes": -255,
      "jsHeapTotalBytes": 15462400,
      "scriptDurationMs": 112.049,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.680000000000017
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2030.1939999999945,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.020999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 624.91,
      "heapDeltaBytes": 16960868,
      "heapUsedBytes": 68630584,
      "domNodes": -258,
      "jsHeapTotalBytes": 14675968,
      "scriptDurationMs": 114.67,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2024.2230000000063,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 11.124000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 589.5129999999999,
      "heapDeltaBytes": 3815172,
      "heapUsedBytes": 54312624,
      "domNodes": -258,
      "jsHeapTotalBytes": 16453632,
      "scriptDurationMs": 110.41899999999998,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2183.2990000000054,
      "styleRecalcs": 71,
      "styleRecalcDurationMs": 18.450000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1277.107,
      "heapDeltaBytes": 14512136,
      "heapUsedBytes": 75547928,
      "domNodes": -258,
      "jsHeapTotalBytes": 18087936,
      "scriptDurationMs": 473.873,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2154.988000000003,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 16.945999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1202.2309999999998,
      "heapDeltaBytes": 4025224,
      "heapUsedBytes": 66916304,
      "domNodes": -261,
      "jsHeapTotalBytes": 15990784,
      "scriptDurationMs": 428.82399999999996,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2134.149999999977,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 15.515999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1215.215,
      "heapDeltaBytes": 18445420,
      "heapUsedBytes": 71819452,
      "domNodes": -263,
      "jsHeapTotalBytes": 19542016,
      "scriptDurationMs": 438.516,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3181.0549999999866,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 18.155,
      "layouts": 60,
      "layoutDurationMs": 7.804,
      "taskDurationMs": 1481.873,
      "heapDeltaBytes": 7881048,
      "heapUsedBytes": 62931188,
      "domNodes": -266,
      "jsHeapTotalBytes": 17240064,
      "scriptDurationMs": 570.999,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3181.9110000000137,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 16.712,
      "layouts": 60,
      "layoutDurationMs": 7.337000000000001,
      "taskDurationMs": 1392.5510000000002,
      "heapDeltaBytes": 4648876,
      "heapUsedBytes": 59629796,
      "domNodes": -267,
      "jsHeapTotalBytes": 17502208,
      "scriptDurationMs": 509.271,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3231.4210000000685,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 15.82,
      "layouts": 60,
      "layoutDurationMs": 7.713,
      "taskDurationMs": 1441.455,
      "heapDeltaBytes": 8182360,
      "heapUsedBytes": 63008796,
      "domNodes": -267,
      "jsHeapTotalBytes": 17764352,
      "scriptDurationMs": 549.388,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "minimap-idle",
      "durationMs": 2012.2460000000046,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 9.423999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 670.4570000000001,
      "heapDeltaBytes": 1734804,
      "heapUsedBytes": 55481184,
      "domNodes": -264,
      "jsHeapTotalBytes": 16191488,
      "scriptDurationMs": 109.79,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "minimap-idle",
      "durationMs": 2034.8829999999793,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.76,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 615.9989999999999,
      "heapDeltaBytes": -7646720,
      "heapUsedBytes": 47481452,
      "domNodes": -263,
      "jsHeapTotalBytes": 16248832,
      "scriptDurationMs": 104.879,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "minimap-idle",
      "durationMs": 2030.285000000049,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.357999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 612.6320000000001,
      "heapDeltaBytes": 2472180,
      "heapUsedBytes": 55780428,
      "domNodes": -263,
      "jsHeapTotalBytes": 15667200,
      "scriptDurationMs": 107.496,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 564.910999999995,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 11.92,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 379.023,
      "heapDeltaBytes": -5117808,
      "heapUsedBytes": 45056384,
      "domNodes": 20,
      "jsHeapTotalBytes": 16515072,
      "scriptDurationMs": 131.31400000000002,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 587.5679999999761,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 12.437999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 387.60999999999996,
      "heapDeltaBytes": 8000584,
      "heapUsedBytes": 56844976,
      "domNodes": 21,
      "jsHeapTotalBytes": 14417920,
      "scriptDurationMs": 131.076,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 579.979000000094,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 19.461000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 383.15600000000006,
      "heapDeltaBytes": -3848672,
      "heapUsedBytes": 46251760,
      "domNodes": 23,
      "jsHeapTotalBytes": 15728640,
      "scriptDurationMs": 123.668,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1992.761999999999,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 11.262999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 447.84600000000006,
      "heapDeltaBytes": 9934648,
      "heapUsedBytes": 59976056,
      "domNodes": 23,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 26.841000000000005,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2004.1059999999788,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.804,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 392.26199999999994,
      "heapDeltaBytes": -4949852,
      "heapUsedBytes": 43824968,
      "domNodes": 22,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 21.941,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2014.4730000000663,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 14.516000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 414.312,
      "heapDeltaBytes": -5000912,
      "heapUsedBytes": 43866244,
      "domNodes": 22,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 23.107,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1705.365999999998,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 39.754,
      "layouts": 16,
      "layoutDurationMs": 4.266,
      "taskDurationMs": 730.989,
      "heapDeltaBytes": 14555556,
      "heapUsedBytes": 63306392,
      "domNodes": 63,
      "jsHeapTotalBytes": 25427968,
      "scriptDurationMs": 108.534,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1985.7340000000363,
      "styleRecalcs": 83,
      "styleRecalcDurationMs": 47.769,
      "layouts": 16,
      "layoutDurationMs": 4.595,
      "taskDurationMs": 947.125,
      "heapDeltaBytes": 13795388,
      "heapUsedBytes": 62599032,
      "domNodes": 72,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 106.726,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1975.6360000000086,
      "styleRecalcs": 84,
      "styleRecalcDurationMs": 49.719,
      "layouts": 16,
      "layoutDurationMs": 4.88,
      "taskDurationMs": 930.8499999999999,
      "heapDeltaBytes": 13845924,
      "heapUsedBytes": 62712532,
      "domNodes": 73,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 109.77199999999999,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8265.70399999997,
      "styleRecalcs": 253,
      "styleRecalcDurationMs": 47.20300000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4066.979,
      "heapDeltaBytes": 7543548,
      "heapUsedBytes": 67544456,
      "domNodes": -256,
      "jsHeapTotalBytes": 19398656,
      "scriptDurationMs": 1330.179,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8188.961000000007,
      "styleRecalcs": 252,
      "styleRecalcDurationMs": 44.661,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3893.228,
      "heapDeltaBytes": 13673800,
      "heapUsedBytes": 73435032,
      "domNodes": -253,
      "jsHeapTotalBytes": 19398656,
      "scriptDurationMs": 1287.1809999999998,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8162.9500000000235,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 44.001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3856.905,
      "heapDeltaBytes": 20435924,
      "heapUsedBytes": 70979240,
      "domNodes": -256,
      "jsHeapTotalBytes": 19542016,
      "scriptDurationMs": 1252.166,
      "eventListeners": -107,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999989
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12721.116999999993,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12706.222999999998,
      "heapDeltaBytes": -44554588,
      "heapUsedBytes": 161521392,
      "domNodes": -8331,
      "jsHeapTotalBytes": 23945216,
      "scriptDurationMs": 639.0590000000001,
      "eventListeners": -16460,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.329999999999927
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12316.63199999997,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12302.782000000001,
      "heapDeltaBytes": -44175896,
      "heapUsedBytes": 162507620,
      "domNodes": -8331,
      "jsHeapTotalBytes": 24469504,
      "scriptDurationMs": 596.968,
      "eventListeners": -16462,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 19.990000000000144
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12611.362999999983,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12597.179,
      "heapDeltaBytes": -40938764,
      "heapUsedBytes": 163411732,
      "domNodes": -8331,
      "jsHeapTotalBytes": 24207360,
      "scriptDurationMs": 652.6110000000001,
      "eventListeners": -16462,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.33000000000029
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14527.308000000005,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 14.786999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14497.023,
      "heapDeltaBytes": -23944112,
      "heapUsedBytes": 173000596,
      "domNodes": -8331,
      "jsHeapTotalBytes": 25169920,
      "scriptDurationMs": 870.171,
      "eventListeners": -16458,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.33000000000029
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14603.364,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 14.790999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14581.658999999998,
      "heapDeltaBytes": -33232700,
      "heapUsedBytes": 171909660,
      "domNodes": -5048,
      "jsHeapTotalBytes": -4714496,
      "scriptDurationMs": 952.9700000000001,
      "eventListeners": -16456,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 19.98999999999978
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14572.066000000064,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 15.245000000000008,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14550.971000000001,
      "heapDeltaBytes": -57401280,
      "heapUsedBytes": 148247524,
      "domNodes": -8333,
      "jsHeapTotalBytes": 614400,
      "scriptDurationMs": 867.629,
      "eventListeners": -16490,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.329999999999927
    },
    {
      "name": "workflow-execution",
      "durationMs": 453.69300000004387,
      "styleRecalcs": 22,
      "styleRecalcDurationMs": 28.458000000000002,
      "layouts": 5,
      "layoutDurationMs": 1.408,
      "taskDurationMs": 133.71699999999998,
      "heapDeltaBytes": 4677148,
      "heapUsedBytes": 50621428,
      "domNodes": 170,
      "jsHeapTotalBytes": 262144,
      "scriptDurationMs": 26.429000000000002,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "workflow-execution",
      "durationMs": 443.70199999991655,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 23.976,
      "layouts": 5,
      "layoutDurationMs": 1.227,
      "taskDurationMs": 121.697,
      "heapDeltaBytes": 4478932,
      "heapUsedBytes": 53254232,
      "domNodes": 158,
      "jsHeapTotalBytes": 262144,
      "scriptDurationMs": 29.067000000000004,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "workflow-execution",
      "durationMs": 455.5480000000216,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 23.056,
      "layouts": 5,
      "layoutDurationMs": 1.39,
      "taskDurationMs": 131.01200000000003,
      "heapDeltaBytes": -5212992,
      "heapUsedBytes": 46209084,
      "domNodes": 158,
      "jsHeapTotalBytes": 1835008,
      "scriptDurationMs": 30.299,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    }
  ]
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/platform/workflow/templates/adapters/hubTemplateAdapter.ts`:
- Around line 52-59: The adapter currently hardcodes mediaType: 'image' and
mediaSubtype: 'webp' which breaks video previews; change it to use the thumbnail
type/subtype from the incoming summary instead: set mediaType to
summary.thumbnail_type, and set mediaSubtype to summary.thumbnail_subtype if
present, otherwise default to 'mp4' when summary.thumbnail_type === 'video' and
'webp' for images; keep using mapThumbnailVariant(summary.thumbnail_type) for
thumbnailVariant and return thumbnailUrl/thumbnailComparisonUrl as-is so video
thumbnails follow the correct rendering path.
- Around line 74-80: The adapter adaptHubWorkflowsToCategories currently
hardcodes the category title 'All'; change it to accept a localized title or use
the shared i18n helper instead of English text. Update the function signature
(adaptHubWorkflowsToCategories) to take an extra parameter like allTitle: string
(or import/use the project's i18n get/t helper) and replace the literal 'All'
with that parameter/helper, and then update all call sites to pass in the
localized string (from vue-i18n or src/locales/en/main.json) so no user-facing
text is hardcoded in the adapter.

In `@src/platform/workflow/templates/composables/useTemplateUrlLoader.test.ts`:
- Around line 38-51: The tests pin isCloud to false and always return undefined
from useWorkflowTemplatesStore().getTemplateByShareId, so the new cloud/shareId
path in useTemplateUrlLoader is never exercised; update the test suite to
include at least two cloud-mode cases: one where the distribution flag (isCloud)
is set to true and useWorkflowTemplatesStore().getTemplateByShareId resolves a
valid template to verify successful cloud shareId loading, and another where
getTemplateByShareId throws/rejects to assert that a real load failure surfaces
as an error (not treated as a “not found” fallback). Implement these by making
the mocks for '@/platform/distribution/types' (isCloud) and
'@/platform/workflow/templates/repositories/workflowTemplatesStore' configurable
per test (override the mock or use vi.doMock/vi.mocked and set return values for
getTemplateByShareId) and add test cases that call useTemplateUrlLoader (the
composable under test) to assert the resolved template and the thrown error
behavior.

In `@src/platform/workflow/templates/composables/useTemplateUrlLoader.ts`:
- Around line 118-128: The code is retrying every failed load as a shareId
lookup which can repeat the same failing call because loadWorkflowTemplate
returns only false for all errors; update the logic so you only retry by shareId
when the original failure is a genuine "not found" (not any error) or when the
store mapping yields a different name than the original templateParam to avoid
re-invoking the same call: either (A) change loadWorkflowTemplate to return a
richer result/error enum (e.g., SUCCESS / NOT_FOUND / ERROR) and only call
templateWorkflows.loadWorkflowTemplate(templateByShareId.name, ...) when the
result was NOT_FOUND, or (B) at minimum guard the retry with
templateByShareId.name !== templateParam so you don’t repeat the identical call;
adjust callers in useTemplateUrlLoader, loadWorkflowTemplate, and
useWorkflowTemplatesStore/getTemplateByShareId accordingly.

In `@src/platform/workflow/templates/composables/useTemplateWorkflows.ts`:
- Around line 136-139: When mapping hub templates you currently overwrite
sourceModule ('hub' -> 'default') but lose the matched template so the workflow
name falls back to id; keep a reference to the matched template (e.g., save
matchedTemplate or template variable) when you detect sourceModule === 'hub' and
pass matchedTemplate.title into the code path that normalizes or loads the fetch
source (the call that builds the fetch source / normalizeFetchSource or the
loadWorkflowFromSource invocation) so the resulting workflow uses template.title
as its name instead of the id.

In `@src/platform/workflow/templates/repositories/workflowTemplatesStore.ts`:
- Around line 486-497: When handling the cloud branch in workflowTemplatesStore
(inside the isCloud block where api.listAllHubWorkflows() and
adaptHubWorkflowsToCategories are used), ensure hub-sourced categories are
treated as core by mapping moduleName 'hub' to the core source before setting
coreTemplates.value; e.g., after adaptHubWorkflowsToCategories(summaries)
iterate the resulting categories and replace any category.source/moduleName ===
'hub' with the core identifier used elsewhere (e.g., 'default') so
groupedTemplates and navGroupedTemplates recognize them as core and avoid
creating an Extensions > hub entry; also keep existing knownTemplateNames
handling.

In `@src/scripts/api.ts`:
- Around line 869-876: The do/while in listAllHubWorkflows() can loop forever if
the API returns a repeated next_cursor; modify listAllHubWorkflows to track seen
cursors (e.g., a Set) and/or enforce a maximum page count, and break with an
error or return when a repeated cursor or max pages is reached; reference the
variables cursor and res.next_cursor and the method listHubWorkflows to locate
where to add the guard and the early-exit logic.
🪄 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: 9e9ab91b-4390-4fe3-8247-5af775092716

📥 Commits

Reviewing files that changed from the base of the PR and between 82242f1 and c440395.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • package.json
  • src/platform/workflow/templates/adapters/hubTemplateAdapter.test.ts
  • src/platform/workflow/templates/adapters/hubTemplateAdapter.ts
  • src/platform/workflow/templates/composables/useTemplateUrlLoader.test.ts
  • src/platform/workflow/templates/composables/useTemplateUrlLoader.ts
  • src/platform/workflow/templates/composables/useTemplateWorkflows.test.ts
  • src/platform/workflow/templates/composables/useTemplateWorkflows.ts
  • src/platform/workflow/templates/repositories/workflowTemplatesStore.ts
  • src/platform/workflow/templates/types/template.ts
  • src/scripts/api.ts

Comment on lines +38 to +51
// Mock the workflow templates store
vi.mock(
'@/platform/workflow/templates/repositories/workflowTemplatesStore',
() => ({
useWorkflowTemplatesStore: vi.fn(() => ({
getTemplateByShareId: vi.fn().mockReturnValue(undefined)
}))
})
)

// Mock distribution (non-cloud for tests)
vi.mock('@/platform/distribution/types', () => ({
isCloud: false
}))
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

This suite still never exercises the new cloud/shareId path.

isCloud is pinned to false and getTemplateByShareId() always returns undefined, so the behavior added in this PR is fully mocked out here. Please add at least one cloud-mode case that resolves a template by shareId, plus one case that proves a real load failure is not treated like a not-found fallback. As per coding guidelines, "Do not write tests that just test the mocks in Vitest; ensure tests fail when code behaves unexpectedly."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/workflow/templates/composables/useTemplateUrlLoader.test.ts`
around lines 38 - 51, The tests pin isCloud to false and always return undefined
from useWorkflowTemplatesStore().getTemplateByShareId, so the new cloud/shareId
path in useTemplateUrlLoader is never exercised; update the test suite to
include at least two cloud-mode cases: one where the distribution flag (isCloud)
is set to true and useWorkflowTemplatesStore().getTemplateByShareId resolves a
valid template to verify successful cloud shareId loading, and another where
getTemplateByShareId throws/rejects to assert that a real load failure surfaces
as an error (not treated as a “not found” fallback). Implement these by making
the mocks for '@/platform/distribution/types' (isCloud) and
'@/platform/workflow/templates/repositories/workflowTemplatesStore' configurable
per test (override the mock or use vi.doMock/vi.mocked and set return values for
getTemplateByShareId) and add test cases that call useTemplateUrlLoader (the
composable under test) to assert the resolved template and the thrown error
behavior.

Comment on lines +136 to +139
// Hub templates use sourceModule 'hub'
if (sourceModule === 'hub') {
sourceModule = 'default'
}
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

Use the hub template title when this path loads a workflow.

After introducing sourceModule === 'hub', the later load still derives the workflow name from id. For hub templates that id is the share_id, so the loaded workflow gets an ID-derived name instead of the human-readable template.title. Keep the matched template around and pass its title through when you normalize the fetch source.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/workflow/templates/composables/useTemplateWorkflows.ts` around
lines 136 - 139, When mapping hub templates you currently overwrite sourceModule
('hub' -> 'default') but lose the matched template so the workflow name falls
back to id; keep a reference to the matched template (e.g., save matchedTemplate
or template variable) when you detect sourceModule === 'hub' and pass
matchedTemplate.title into the code path that normalizes or loads the fetch
source (the call that builds the fetch source / normalizeFetchSource or the
loadWorkflowFromSource invocation) so the resulting workflow uses template.title
as its name instead of the id.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/scripts/api.ts (1)

865-872: ⚠️ Potential issue | 🟠 Major

Guard listAllHubWorkflows() against repeated cursors.

If /hub/workflows ever returns the same next_cursor twice, this loop never terminates and the dialog keeps issuing requests. Track seen cursors or cap page count before advancing.

🛡️ Suggested guard
   async listAllHubWorkflows(): Promise<HubWorkflowSummary[]> {
     const all: HubWorkflowSummary[] = []
+    const seenCursors = new Set<string>()
     let cursor: string | undefined
     do {
+      if (cursor && seenCursors.has(cursor)) {
+        throw new Error(
+          `Hub workflow pagination loop detected at cursor: ${cursor}`
+        )
+      }
+      if (cursor) {
+        seenCursors.add(cursor)
+      }
       const page = await this.fetchHubWorkflowPage(100, cursor)
       all.push(...(page.workflows as HubWorkflowSummary[]))
       cursor = page.next_cursor || undefined
     } while (cursor)
     return all
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/scripts/api.ts` around lines 865 - 872, The loop in listAllHubWorkflows
can hang if fetchHubWorkflowPage returns the same page.next_cursor repeatedly;
update the function to guard against repeated cursors by tracking seen cursors
(e.g., a Set) and breaking if next_cursor is already seen, or impose a hard
page/iteration cap before advancing; reference the cursor variable,
page.next_cursor and fetchHubWorkflowPage to locate where to add the check and
exit the loop safely.
🧹 Nitpick comments (1)
src/scripts/api.ts (1)

851-853: Include request context in these thrown errors.

Both branches only surface the status code, so upstream logs can’t tell which page request or share ID failed. Include statusText plus the request context before rethrowing.

🛠️ Suggested tweak
     if (!res.ok) {
-      throw new Error(`Failed to list hub workflows: ${res.status}`)
+      throw new Error(
+        `Failed to list hub workflows (limit=${limit}, cursor=${
+          cursor ?? 'initial'
+        }): ${res.status} ${res.statusText}`
+      )
     }
@@
     if (!res.ok) {
-      throw new Error(`Failed to get hub workflow detail: ${res.status}`)
+      throw new Error(
+        `Failed to get hub workflow detail for shareId=${shareId}: ${res.status} ${res.statusText}`
+      )
     }
As per coding guidelines "Provide user-friendly and actionable error messages" and "Implement proper error propagation".

Also applies to: 883-885

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/scripts/api.ts` around lines 851 - 853, The thrown errors only include
res.status; update the error messages where they say "Failed to list hub
workflows" (and the similar throw at 883-885) to include res.statusText plus
contextual request information (e.g., page, shareId, or the full request
URL/endpoint/params) so logs show which page/share failed; locate the throws
around the "Failed to list hub workflows" message in src/scripts/api.ts and
change the Error construction to combine a descriptive message, res.status,
res.statusText, and the relevant local variables (page, shareId or url) before
rethrowing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/scripts/api.ts`:
- Around line 865-872: The loop in listAllHubWorkflows can hang if
fetchHubWorkflowPage returns the same page.next_cursor repeatedly; update the
function to guard against repeated cursors by tracking seen cursors (e.g., a
Set) and breaking if next_cursor is already seen, or impose a hard
page/iteration cap before advancing; reference the cursor variable,
page.next_cursor and fetchHubWorkflowPage to locate where to add the check and
exit the loop safely.

---

Nitpick comments:
In `@src/scripts/api.ts`:
- Around line 851-853: The thrown errors only include res.status; update the
error messages where they say "Failed to list hub workflows" (and the similar
throw at 883-885) to include res.statusText plus contextual request information
(e.g., page, shareId, or the full request URL/endpoint/params) so logs show
which page/share failed; locate the throws around the "Failed to list hub
workflows" message in src/scripts/api.ts and change the Error construction to
combine a descriptive message, res.status, res.statusText, and the relevant
local variables (page, shareId or url) before rethrowing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a417a318-1991-41f4-9046-808a7e076614

📥 Commits

Reviewing files that changed from the base of the PR and between c440395 and 2fef25f.

📒 Files selected for processing (1)
  • src/scripts/api.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@src/composables/useTemplateFiltering.ts`:
- Around line 127-130: The watcher on debouncedSearchQuery can cause
out-of-order hub results because multiple searchHubWorkflows calls may resolve
in any order; update the logic to sequence or cancel in-flight requests before
committing results: either add an AbortController signal param to
workflowTemplatesStore.searchHubWorkflows and abort the previous controller when
starting a new watch invocation, or implement a monotonically increasing
requestId (e.g., lastHubSearchId) inside workflowTemplatesStore that
searchHubWorkflows captures and only commits results if the id matches the
latest; ensure this also coordinates with loadHubNextPage appends so that only
the current search's next-page results are applied to the same active requestId.
- Around line 126-131: The cloud path currently derives facets and counts from
the partially-populated templatesArray causing incomplete
availableModels/availableUseCases and wrong filteredCount/totalCount; update the
cloud branch (isCloud) to either (A) fetch the full hub result set before
deriving client-side filters by implementing a paginated fetchAllHubWorkflows on
workflowTemplatesStore and only computing facets/counts after that completes, or
(B) delegate facet and count computation server-side and consume server-returned
availableModels/availableUseCases/filteredCount/totalCount from
workflowTemplatesStore.searchHubWorkflows; modify the watch on
debouncedSearchQuery and any usages of templatesArray, availableModels,
availableUseCases, filteredCount, and totalCount so they use the chosen approach
(fetchAllHubWorkflows completion or server-provided values) instead of the
incremental templatesArray.
🪄 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: 2cf790d4-ca77-43da-a73a-4383b22af945

📥 Commits

Reviewing files that changed from the base of the PR and between 62008df and fea2659.

📒 Files selected for processing (4)
  • src/components/custom/widget/WorkflowTemplateSelectorDialog.vue
  • src/composables/useTemplateFiltering.ts
  • src/platform/workflow/templates/repositories/workflowTemplatesStore.ts
  • src/scripts/api.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/scripts/api.ts
  • src/platform/workflow/templates/repositories/workflowTemplatesStore.ts

Comment on lines +126 to +131
// On cloud, delegate search to the hub API instead of Fuse.js
if (isCloud) {
const workflowTemplatesStore = useWorkflowTemplatesStore()
watch(debouncedSearchQuery, (query) => {
void workflowTemplatesStore.searchHubWorkflows(query.trim())
})
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

Cloud filtering is now running on an incomplete dataset.

In cloud mode, templatesArray only contains the hub pages fetched so far, but the rest of this composable still derives facets and counts from it as if it were the full result set. That makes availableModels, availableUseCases, filteredCount/totalCount, and downstream empty-state behavior incomplete until the user scrolls through every page, so valid hub templates can be missing from filters/results. Either load the full hub result set before applying client-side filters, or move those filters/counts server-side.

Also applies to: 139-142

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useTemplateFiltering.ts` around lines 126 - 131, The cloud
path currently derives facets and counts from the partially-populated
templatesArray causing incomplete availableModels/availableUseCases and wrong
filteredCount/totalCount; update the cloud branch (isCloud) to either (A) fetch
the full hub result set before deriving client-side filters by implementing a
paginated fetchAllHubWorkflows on workflowTemplatesStore and only computing
facets/counts after that completes, or (B) delegate facet and count computation
server-side and consume server-returned
availableModels/availableUseCases/filteredCount/totalCount from
workflowTemplatesStore.searchHubWorkflows; modify the watch on
debouncedSearchQuery and any usages of templatesArray, availableModels,
availableUseCases, filteredCount, and totalCount so they use the chosen approach
(fetchAllHubWorkflows completion or server-provided values) instead of the
incremental templatesArray.

Comment on lines +127 to +130
if (isCloud) {
const workflowTemplatesStore = useWorkflowTemplatesStore()
watch(debouncedSearchQuery, (query) => {
void workflowTemplatesStore.searchHubWorkflows(query.trim())
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

Guard hub search against out-of-order responses.

This watcher can start a new searchHubWorkflows() before the previous request finishes. Since the store replaces shared template state when each response resolves, a slower older query can overwrite a newer one, and it can also interleave with loadHubNextPage() appends from the dialog. Please add request sequencing or cancellation before committing hub search results.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useTemplateFiltering.ts` around lines 127 - 130, The watcher
on debouncedSearchQuery can cause out-of-order hub results because multiple
searchHubWorkflows calls may resolve in any order; update the logic to sequence
or cancel in-flight requests before committing results: either add an
AbortController signal param to workflowTemplatesStore.searchHubWorkflows and
abort the previous controller when starting a new watch invocation, or implement
a monotonically increasing requestId (e.g., lastHubSearchId) inside
workflowTemplatesStore that searchHubWorkflows captures and only commits results
if the id matches the latest; ensure this also coordinates with loadHubNextPage
appends so that only the current search's next-page results are applied to the
same active requestId.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/scripts/api.ts (1)

870-874: ⚠️ Potential issue | 🟠 Major

Guard pagination against repeated cursors.

Line 870 loops while cursor is truthy. If the backend repeats next_cursor, this can become an infinite loop. Add repeated-cursor protection (and optionally a page cap) to fail fast.

🛡️ Suggested guard
   async listAllHubWorkflows(): Promise<HubWorkflowSummary[]> {
     const all: HubWorkflowSummary[] = []
+    const seenCursors = new Set<string>()
     let cursor: string | undefined
     do {
+      if (cursor && seenCursors.has(cursor)) {
+        throw new Error(
+          `Hub workflow pagination loop detected at cursor: ${cursor}`
+        )
+      }
+      if (cursor) {
+        seenCursors.add(cursor)
+      }
       const page = await this.fetchHubWorkflowPage(100, cursor)
       all.push(...(page.workflows as HubWorkflowSummary[]))
       cursor = page.next_cursor || undefined
     } while (cursor)
     return all
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/scripts/api.ts` around lines 870 - 874, The pagination loop calling
this.fetchHubWorkflowPage(100, cursor) can infinite-loop if the backend returns
the same next_cursor repeatedly; modify the loop to track seen cursors (e.g., a
Set of previous cursor values) and detect repeats before assigning cursor =
page.next_cursor, and either throw an error or break when a repeat is detected;
additionally add an optional maxPages counter (e.g., maxPages = 1000) and
increment per iteration to fail fast if exceeded. Ensure the change is applied
around the do/while loop that pushes page.workflows and references next_cursor
so the guard prevents repeated-cursor progression and caps total pages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/scripts/api.ts`:
- Around line 849-850: The default status filter currently uses
query.set('status', 'pending,approved,rejected,deprecated') which exposes
non-production workflows; change it to only 'approved' by default (replace the
value passed to query.set) and move the broader list behind a dev/test flag or
environment check (e.g., use a feature flag or NODE_ENV check) so only
development/test runs set 'pending,approved,rejected,deprecated'; ensure this
logic is implemented where query.set('status', ...) is called so production
always receives only 'approved'.

---

Duplicate comments:
In `@src/scripts/api.ts`:
- Around line 870-874: The pagination loop calling
this.fetchHubWorkflowPage(100, cursor) can infinite-loop if the backend returns
the same next_cursor repeatedly; modify the loop to track seen cursors (e.g., a
Set of previous cursor values) and detect repeats before assigning cursor =
page.next_cursor, and either throw an error or break when a repeat is detected;
additionally add an optional maxPages counter (e.g., maxPages = 1000) and
increment per iteration to fail fast if exceeded. Ensure the change is applied
around the do/while loop that pushes page.workflows and references next_cursor
so the guard prevents repeated-cursor progression and caps total pages.
🪄 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: b885a9ff-e3be-436e-8e81-caae02702615

📥 Commits

Reviewing files that changed from the base of the PR and between fea2659 and 5a5678c.

📒 Files selected for processing (2)
  • src/platform/workflow/templates/repositories/workflowTemplatesStore.ts
  • src/scripts/api.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/platform/workflow/templates/repositories/workflowTemplatesStore.ts

Comment on lines +849 to +850
// TODO: Remove after production has approved data — fetch all statuses for testing
query.set('status', 'pending,approved,rejected,deprecated')
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

Default status filter includes non-approved workflows.

Line 849 currently requests pending, rejected, and deprecated in addition to approved. That can expose non-production templates in the cloud dialog if downstream filtering changes or regresses. Default this to approved and gate broader status sets behind a dev/test flag.

🔧 Suggested change
-    // TODO: Remove after production has approved data — fetch all statuses for testing
-    query.set('status', 'pending,approved,rejected,deprecated')
+    const includeAllStatusesForTesting =
+      getDevOverride<boolean>('hubTemplates.includeAllStatuses') === true
+    query.set(
+      'status',
+      includeAllStatusesForTesting
+        ? 'pending,approved,rejected,deprecated'
+        : 'approved'
+    )

As per coding guidelines, "Validate trusted sources before processing".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/scripts/api.ts` around lines 849 - 850, The default status filter
currently uses query.set('status', 'pending,approved,rejected,deprecated') which
exposes non-production workflows; change it to only 'approved' by default
(replace the value passed to query.set) and move the broader list behind a
dev/test flag or environment check (e.g., use a feature flag or NODE_ENV check)
so only development/test runs set 'pending,approved,rejected,deprecated'; ensure
this logic is implemented where query.set('status', ...) is called so production
always receives only 'approved'.

Replace static index.json template loading on cloud with the hub
workflows API (GET /api/hub/workflows for listing, GET
/api/hub/workflows/{share_id} for workflow JSON).

- Add listHubWorkflows, listAllHubWorkflows, getHubWorkflowDetail to api.ts
- Create adapter to convert HubWorkflowSummary to TemplateInfo
- Branch on isCloud in workflowTemplatesStore.fetchCoreTemplates()
- Update thumbnail URL resolution for absolute hub URLs
- Update workflow JSON loading to use detail API via shareId
- Add shareId-based fallback in URL template loader
…etch

Replace the generic listHubWorkflows method (which accepted search/tag
params) with a private fetchHubWorkflowPage that only takes limit and
cursor. This prevents accidental filtering when loading all templates.
Production has no approved data yet — temporarily include all statuses
(pending, approved, rejected, deprecated) so the template dialog can
be tested locally.
Replace loading all hub workflows at once with cursor-based pagination.
The store now loads one page at a time and fetches more on scroll via
the intersection observer.

Search on cloud delegates to the API search param instead of client-side
Fuse.js, resetting loaded data with server-filtered results.
Revert server-side pagination and search (Phase 2) since the backend
does not yet support model filter, runs-on filter, or sort params.

Phase 1 approach: load all hub workflows upfront via listAllHubWorkflows,
keep all existing client-side filtering/sorting/search (Fuse.js) intact.
This preserves the current UX while switching data source to the hub API.
- Remove temporary status override for testing
- Add 6 unit tests for workflowTemplatesStore cloud/local paths
  (hub loading, field adaptation, shareId lookup, error handling)
- Add E2E regression tests for template dialog: open/cards, filters,
  search, template loading, navigation, reopen behavior
@dante01yoon dante01yoon force-pushed the feat/hub-template-api-migration branch from 5f5cef9 to b48ed9b Compare March 28, 2026 13:54
- Derive mediaType/mediaSubtype from thumbnail_type (video support)
- Accept localized title in adaptHubWorkflowsToCategories instead of
  hardcoded 'All'
- URL loader: resolve template by name or shareId before loading
  instead of retrying on every failure
- Guard listAllHubWorkflows against cursor pagination loops
Unit tests:
- Video thumbnail type → mediaType/mediaSubtype mapping
- Image thumbnail type defaults
- Hub absolute thumbnail URL resolution (index 1 and 2)
- Static fallback when thumbnailUrl is absent

E2E tests:
- Remove duplicates with existing templates.spec.ts
- Add sort dropdown options test
- Add navigation switching test
- Add thumbnail rendering verification
- Add hub API mock test for cloud build validation
- Add MIT license to @comfyorg/ingest-types package.json (fixes
  validate-licenses CI check)
- Fix search input selector: ComboboxInput renders role="combobox",
  not role="searchbox". Use getByPlaceholder(/search/i) instead.
- Increase debounce wait from 300ms to 500ms for CI stability.
@dante01yoon dante01yoon marked this pull request as ready for review March 28, 2026 14:28
@dante01yoon dante01yoon requested a review from a team March 28, 2026 14:28
@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Mar 28, 2026
Add E2E test that confirms local (non-cloud) builds never call
/api/hub/workflows and load templates from static index.json instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedvite@​8.0.0941008299100

View full report

@socket-security
Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm vite is 91.0% likely obfuscated

Confidence: 0.91

Location: Package overview

From: apps/desktop-ui/package.jsonnpm/vite@8.0.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/vite@8.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Comment on lines +5 to +12
/**
* Regression tests for the template dialog hub API migration.
*
* These verify behavior that is NOT covered by the existing templates.spec.ts,
* focusing on the hub API data path and the adapter integration.
*/
test.describe(
'Template Hub Migration — Regression',
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.

I think it would still make sense to just put these in the existing template test files/modules/folders instead of distinguishing them as separate.

await expect(comfyPage.templates.content).toBeVisible()
await comfyPage.templates.expectMinimumCardCount(1)

const dialog = comfyPage.page.getByRole('dialog')
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.

Shouldn't the dialog and buttons be page objects like comfyPage.templates.popularBtn? Same goes for other test cases in this file

expect(hubRequests).toHaveLength(0)
})

test('hub API mock: dialog renders hub workflow data', async ({
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.

We might want to put this data in the fixtures/data and potentially the mock routing in the templates page object or helper if appropriate.

Comment on lines +196 to +204
// The hub API is only called when isCloud is true.
// This test verifies the route interception works for when the
// cloud build is running. On local builds, the template dialog
// uses static files instead, so this mock won't be hit.
// The test still validates that the mock setup and route interception
// pattern works correctly for cloud E2E testing.
await comfyPage.command.executeCommand('Comfy.BrowseTemplates')
await expect(comfyPage.templates.content).toBeVisible()
await comfyPage.templates.expectMinimumCardCount(1)
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.

You can now write tests targeting cloud after #10546

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

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants