Skip to content

feat: add gated Hugging Face model access hints#13742

Open
jaeone94 wants to merge 11 commits into
mainfrom
jaeone/fe-1173-gated-hf-download-hint-action
Open

feat: add gated Hugging Face model access hints#13742
jaeone94 wants to merge 11 commits into
mainfrom
jaeone/fe-1173-gated-hf-download-hint-action

Conversation

@jaeone94

@jaeone94 jaeone94 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds an explicit access hint for gated Hugging Face models in the missing-model UI without taking the normal download action away from the user.

When metadata prefetch receives an authorization-related response (401, 403, or 451) with Hugging Face's explicit X-Error-Code: GatedRepo marker, the UI keeps the derived repository URL and shows a lock action beside Download. The lock opens the repository page so the user can sign in and accept any required access terms. Download continues to use the existing OSS or Desktop download path.

This gives gated models a practical recovery path without adding a Hugging Face OAuth flow, storing an access token, or integrating another provider API.

Changes

  • What: Preserve gated Hugging Face repository URLs discovered during missing-model metadata prefetch.
  • What: Show a lock icon beside affected models and open the corresponding repository page when it is clicked.
  • What: Keep the existing Download and Download all behavior unchanged, including the normal browser anchor download path.
  • What: Centralize missing-model metadata prefetch, download dispatch, and model-access-page dispatch in useMissingModelDownload() so Row and Card use the same platform-level behavior.
  • What: Add an optional openModelAccessPage() Desktop bridge contract and retain the browser anchor fallback for OSS and older Desktop hosts.
  • What: Add regression coverage for explicit gated-response classification, authorization-status false positives, exact Hugging Face host matching, store and pipeline propagation, component rendering, browser navigation, and Desktop bridge delegation.
  • Breaking: None. The new Desktop bridge method is optional.
  • Dependencies: No new runtime dependencies.

Why Download Remains Available

A failed cross-origin HEAD request can tell us that a Hugging Face URL appears to require authorization, but it cannot reliably tell us whether the user's browser already has a valid Hugging Face login cookie and access grant. Gated responses also do not expose a usable content-length, so the missing-model UI cannot show the model size in this state.

Treating the gated result as a hard redirect would therefore remove a working one-click download from users who are already authorized. This PR treats it as an access hint instead:

  • the lock is the route to sign in or accept repository terms;
  • Download remains the route that attempts the actual file download;
  • after authorization, the browser or Desktop download session can use its cookie normally.

The lock may remain visible after access is granted because it represents metadata observed during the current missing-model scan, not a live authentication-state check.

User Flow

  1. The user clicks Download for a gated model.
  2. Without access, OSS may open the Hugging Face authorization response instead of downloading the file, while Desktop reports a failed download.
  3. The user clicks the lock icon beside that model.
  4. ComfyUI opens the actual Hugging Face repository page.
  5. The user signs in and accepts the repository's access terms.
  6. The user returns to ComfyUI and clicks Download again.
  7. The existing one-click download path succeeds using the now-authorized browser or Desktop session.

Desktop same-session navigation is implemented by the paired Comfy-Org/Comfy-Desktop#1275.

Intended Limitations

  • This does not determine whether the current user is already authenticated or authorized on Hugging Face.
  • Gated models do not show a file size while the metadata request cannot read content-length.
  • Granting access does not automatically retry the download; the user returns to ComfyUI and clicks Download again.
  • Network failures, authorization statuses without Hugging Face's explicit GatedRepo error code, and other HTTP failures are not classified as gated repositories.
  • This does not add Hugging Face OAuth, token storage, provider API integration, or a broader download action-state redesign.

Review Focus

  • Confirm that gated metadata remains a non-blocking hint and never replaces Download.
  • Confirm that the shared composable is the only component-facing entry point for missing-model download and access-page actions.
  • Confirm that the optional Desktop bridge preserves the OSS fallback and compatibility with older hosts.

Validation

  • pnpm format:check
  • pnpm lint
  • pnpm typecheck
  • pnpm test:unit
  • pnpm test:browser
  • pnpm knip
  • pnpm build
  • Manually verified the full gated-model access and retry flow with the built frontend hosted in Comfy Desktop.

Playwright E2E tests cover the deterministic browser-visible flow: a mocked Hugging Face 403 response carrying X-Error-Code: GatedRepo classifies a model as gated, renders the lock and guidance while keeping Download available, and opens the derived repository URL through the browser fallback.

The provider-authenticated flow itself is not automated because it depends on a real third-party Hugging Face account, a gated repository access grant, cross-origin cookies, and the host browser or Electron session. That full sign-in, grant, return, and retry flow was verified manually in Comfy Desktop.

Screenshots

스크린샷 2026-07-17 오후 6 25 30
2026-07-17.2.31.33.mov

@jaeone94
jaeone94 requested a review from a team July 17, 2026 05:08
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

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

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

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 07/24/2026, 05:29:51 AM UTC

Links

📦 Bundle: 8.08 MB gzip 🔴 +1.12 kB

Details

Summary

  • Raw size: 33.9 MB baseline 33.9 MB — 🔴 +5.17 kB
  • Gzip: 8.08 MB baseline 8.08 MB — 🔴 +1.12 kB
  • Brotli: 5.58 MB baseline 5.58 MB — 🔴 +793 B
  • Bundles: 417 current • 417 baseline • 150 added / 150 removed

Category Glance
Graph Workspace 🔴 +3.8 kB (1.25 MB) · Other 🔴 +1.06 kB (12.4 MB) · Data & Services 🔴 +304 B (3.37 MB) · Vendor & Third-Party ⚪ 0 B (15.7 MB) · Panels & Settings ⚪ 0 B (551 kB) · Utilities & Hooks ⚪ 0 B (357 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-CMmkc3Tz.js (new) 3.64 kB 🔴 +3.64 kB 🔴 +1.82 kB 🔴 +1.58 kB
assets/index-Lw9Zs0nQ.js (removed) 3.64 kB 🟢 -3.64 kB 🟢 -1.82 kB 🟢 -1.6 kB

Status: 1 added / 1 removed

Graph Workspace — 1.25 MB (baseline 1.25 MB) • 🔴 +3.8 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-CqJu2WEn.js (new) 1.25 MB 🔴 +1.25 MB 🔴 +267 kB 🔴 +201 kB
assets/GraphView-DkYA-fC8.js (removed) 1.25 MB 🟢 -1.25 MB 🟢 -266 kB 🟢 -200 kB

Status: 1 added / 1 removed / 1 unchanged

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-C7JiIF92.js (removed) 25 kB 🟢 -25 kB 🟢 -6.25 kB 🟢 -5.54 kB
assets/CloudSurveyView-D9WWgJF1.js (new) 25 kB 🔴 +25 kB 🔴 +6.25 kB 🔴 +5.53 kB
assets/CloudLoginView-CondeYRr.js (removed) 11.6 kB 🟢 -11.6 kB 🟢 -3.14 kB 🟢 -2.76 kB
assets/CloudLoginView-SCJa4GqS.js (new) 11.6 kB 🔴 +11.6 kB 🔴 +3.14 kB 🔴 +2.75 kB
assets/CloudSignupView-BlRgEhmT.js (removed) 9.96 kB 🟢 -9.96 kB 🟢 -2.81 kB 🟢 -2.46 kB
assets/CloudSignupView-CaDRZHVe.js (new) 9.96 kB 🔴 +9.96 kB 🔴 +2.81 kB 🔴 +2.47 kB
assets/CloudLayoutView-CNwaTgc6.js (new) 9.54 kB 🔴 +9.54 kB 🔴 +2.35 kB 🔴 +2.04 kB
assets/CloudLayoutView-Dp6jA-n4.js (removed) 9.54 kB 🟢 -9.54 kB 🟢 -2.35 kB 🟢 -2.03 kB
assets/UserCheckView-Beycd1FE.js (removed) 8.8 kB 🟢 -8.8 kB 🟢 -2.22 kB 🟢 -1.93 kB
assets/UserCheckView-BTxP3E7z.js (new) 8.8 kB 🔴 +8.8 kB 🔴 +2.22 kB 🔴 +1.93 kB
assets/WidgetTextPreview-CCosih0e.js (removed) 6.13 kB 🟢 -6.13 kB 🟢 -2.16 kB 🟢 -1.92 kB
assets/WidgetTextPreview-CwYC_vd-.js (new) 6.13 kB 🔴 +6.13 kB 🔴 +2.16 kB 🔴 +1.92 kB
assets/CloudSubscriptionRedirectView-CEDp5CyA.js (new) 6.06 kB 🔴 +6.06 kB 🔴 +2.27 kB 🔴 +1.97 kB
assets/CloudSubscriptionRedirectView-DM5sc-EC.js (removed) 6.06 kB 🟢 -6.06 kB 🟢 -2.27 kB 🟢 -1.97 kB
assets/UserSelectView-BKzdUjij.js (removed) 5.54 kB 🟢 -5.54 kB 🟢 -1.99 kB 🟢 -1.74 kB
assets/UserSelectView-DXHv62K_.js (new) 5.54 kB 🔴 +5.54 kB 🔴 +1.99 kB 🔴 +1.74 kB
assets/CloudForgotPasswordView-C_dOXWRR.js (new) 5.15 kB 🔴 +5.15 kB 🔴 +1.76 kB 🔴 +1.55 kB
assets/CloudForgotPasswordView-C11Fnj3B.js (removed) 5.15 kB 🟢 -5.15 kB 🟢 -1.76 kB 🟢 -1.54 kB
assets/CloudAuthTimeoutView-CSbwn159.js (removed) 4.49 kB 🟢 -4.49 kB 🟢 -1.57 kB 🟢 -1.37 kB
assets/CloudAuthTimeoutView-XbDkBT0Y.js (new) 4.49 kB 🔴 +4.49 kB 🔴 +1.57 kB 🔴 +1.37 kB
assets/OAuthLayoutView-BagRv-Mj.js (removed) 1.36 kB 🟢 -1.36 kB 🟢 -731 B 🟢 -621 B
assets/OAuthLayoutView-I1W7U29c.js (new) 1.36 kB 🔴 +1.36 kB 🔴 +732 B 🔴 +622 B
assets/WidgetTextPreview-2WwTxZfX.js (new) 131 B 🔴 +131 B 🔴 +100 B 🔴 +93 B
assets/WidgetTextPreview-vyhj4bT_.js (removed) 131 B 🟢 -131 B 🟢 -100 B 🟢 -94 B

Status: 12 added / 12 removed / 4 unchanged

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-C-_G63KF.js (removed) 49.5 kB 🟢 -49.5 kB 🟢 -9.97 kB 🟢 -8.82 kB
assets/KeybindingPanel-SzZp7gmZ.js (new) 49.5 kB 🔴 +49.5 kB 🔴 +9.97 kB 🔴 +8.84 kB
assets/SecretsPanel-ByfygCe5.js (removed) 31 kB 🟢 -31 kB 🟢 -7.51 kB 🟢 -6.57 kB
assets/SecretsPanel-w3nC4Kb3.js (new) 31 kB 🔴 +31 kB 🔴 +7.51 kB 🔴 +6.57 kB
assets/CreditsPanel-C-DYM0hi.js (removed) 16 kB 🟢 -16 kB 🟢 -4.66 kB 🟢 -4.1 kB
assets/CreditsPanel-W09IJqR0.js (new) 16 kB 🔴 +16 kB 🔴 +4.66 kB 🔴 +4.09 kB
assets/SubscriptionPanel-BZfLVdHR.js (removed) 11.5 kB 🟢 -11.5 kB 🟢 -3.65 kB 🟢 -3.21 kB
assets/SubscriptionPanel-ep6laWgC.js (new) 11.5 kB 🔴 +11.5 kB 🔴 +3.65 kB 🔴 +3.21 kB
assets/AboutPanel-DvXr_Iyb.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.06 kB 🔴 +2.75 kB
assets/AboutPanel-YkpGRRBq.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.06 kB 🟢 -2.74 kB
assets/ExtensionPanel-BteuOcGe.js (new) 9.24 kB 🔴 +9.24 kB 🔴 +2.54 kB 🔴 +2.25 kB
assets/ExtensionPanel-DtzAFCU3.js (removed) 9.24 kB 🟢 -9.24 kB 🟢 -2.54 kB 🟢 -2.25 kB
assets/ServerConfigPanel-91Piova-.js (removed) 6.15 kB 🟢 -6.15 kB 🟢 -1.97 kB 🟢 -1.75 kB
assets/ServerConfigPanel-Bv5V-hAJ.js (new) 6.15 kB 🔴 +6.15 kB 🔴 +1.97 kB 🔴 +1.75 kB
assets/UserPanel-BTtmwV2r.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.82 kB 🟢 -1.57 kB
assets/UserPanel-CkcwcRE4.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.81 kB 🔴 +1.57 kB
assets/refreshRemoteConfig-2mKaIrIS.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +1.26 kB 🔴 +1.11 kB
assets/refreshRemoteConfig-CSaVtKNw.js (removed) 2.97 kB 🟢 -2.97 kB 🟢 -1.26 kB 🟢 -1.11 kB
assets/cloudRemoteConfig-CTxBAjGe.js (new) 988 B 🔴 +988 B 🔴 +535 B 🔴 +450 B
assets/cloudRemoteConfig-DRrI_sGJ.js (removed) 988 B 🟢 -988 B 🟢 -537 B 🟢 -457 B
assets/refreshRemoteConfig-CCDjvxMO.js (new) 110 B 🔴 +110 B 🔴 +89 B 🔴 +88 B
assets/refreshRemoteConfig-DpffNe4M.js (removed) 110 B 🟢 -110 B 🟢 -89 B 🟢 -91 B

Status: 11 added / 11 removed / 15 unchanged

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SignUpForm-DBHMJBqP.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +4.19 kB 🔴 +3.65 kB
assets/SignUpForm-T6eRq_zH.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -4.18 kB 🟢 -3.65 kB
assets/auth-Cp2Gmg_U.js (new) 3.69 kB 🔴 +3.69 kB 🔴 +1.3 kB 🔴 +1.12 kB
assets/auth-UGaecmPd.js (removed) 3.69 kB 🟢 -3.69 kB 🟢 -1.3 kB 🟢 -1.13 kB
assets/usePostAuthRedirect-CjRRN4TR.js (new) 3.33 kB 🔴 +3.33 kB 🔴 +1.27 kB 🔴 +1.1 kB
assets/usePostAuthRedirect-CYACaA6p.js (removed) 3.33 kB 🟢 -3.33 kB 🟢 -1.27 kB 🟢 -1.1 kB
assets/UpdatePasswordContent-BsNiILxo.js (new) 1.91 kB 🔴 +1.91 kB 🔴 +872 B 🔴 +759 B
assets/UpdatePasswordContent-CC7Ldj-U.js (removed) 1.91 kB 🟢 -1.91 kB 🟢 -874 B 🟢 -760 B
assets/authStore-ChoqxYv7.js (new) 128 B 🔴 +128 B 🔴 +107 B 🔴 +114 B
assets/authStore-D8eg7mLI.js (removed) 128 B 🟢 -128 B 🟢 -107 B 🟢 -101 B
assets/workspaceAuthStore-3urK2-ev.js (removed) 108 B 🟢 -108 B 🟢 -99 B 🟢 -106 B
assets/workspaceAuthStore-BQaCmNvW.js (new) 108 B 🔴 +108 B 🔴 +99 B 🔴 +112 B
assets/auth-CcGQBq2A.js (removed) 105 B 🟢 -105 B 🟢 -96 B 🟢 -93 B
assets/auth-CS82wAjR.js (new) 105 B 🔴 +105 B 🔴 +96 B 🔴 +86 B

Status: 7 added / 7 removed / 3 unchanged

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-BijnrrGQ.js (removed) 90.5 kB 🟢 -90.5 kB 🟢 -19.3 kB 🟢 -16.5 kB
assets/ComfyHubPublishDialog-Dpwfza1D.js (new) 90.5 kB 🔴 +90.5 kB 🔴 +19.3 kB 🔴 +16.5 kB
assets/useShareDialog-BOEId5Nq.js (new) 23.7 kB 🔴 +23.7 kB 🔴 +5.59 kB 🔴 +4.97 kB
assets/useShareDialog-ydHmQITD.js (removed) 23.7 kB 🟢 -23.7 kB 🟢 -5.59 kB 🟢 -4.97 kB
assets/feedbackDialog-CL2zxWPY.js (removed) 4.5 kB 🟢 -4.5 kB 🟢 -1.9 kB 🟢 -1.62 kB
assets/feedbackDialog-DmgGmebF.js (new) 4.5 kB 🔴 +4.5 kB 🔴 +1.9 kB 🔴 +1.62 kB
assets/ComfyHubPublishDialog-CGhzzrny.js (new) 143 B 🔴 +143 B 🔴 +105 B 🔴 +89 B
assets/ComfyHubPublishDialog-jL3DA7oP.js (removed) 143 B 🟢 -143 B 🟢 -105 B 🟢 -91 B
assets/useSubscriptionDialog-2XWWhFJN.js (removed) 108 B 🟢 -108 B 🟢 -102 B 🟢 -98 B
assets/useSubscriptionDialog-DMPOV6_o.js (new) 108 B 🔴 +108 B 🔴 +102 B 🔴 +91 B

Status: 5 added / 5 removed / 1 unchanged

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-BERTvBU_.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.8 kB 🟢 -3.38 kB
assets/ComfyQueueButton-CLThDWkk.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.8 kB 🔴 +3.38 kB
assets/useTerminalTabs-C-NJeK5y.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.72 kB 🔴 +3.29 kB
assets/useTerminalTabs-CAO6JLaO.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.72 kB 🟢 -3.29 kB
assets/InviteMembersForm-Bo9AouFo.js (removed) 7.85 kB 🟢 -7.85 kB 🟢 -2.65 kB 🟢 -2.37 kB
assets/InviteMembersForm-CWxgT9rd.js (new) 7.85 kB 🔴 +7.85 kB 🔴 +2.65 kB 🔴 +2.35 kB
assets/SubscribeButton-BtHwLjE1.js (new) 2.18 kB 🔴 +2.18 kB 🔴 +988 B 🔴 +864 B
assets/SubscribeButton-CK-Wb_Mq.js (removed) 2.18 kB 🟢 -2.18 kB 🟢 -989 B 🟢 -862 B
assets/cloudFeedbackTopbarButton-DmftVf3B.js (new) 768 B 🔴 +768 B 🔴 +452 B 🔴 +419 B
assets/cloudFeedbackTopbarButton-DRaTej76.js (removed) 768 B 🟢 -768 B 🟢 -455 B 🟢 -414 B
assets/ComfyQueueButton-1tkH9als.js (removed) 128 B 🟢 -128 B 🟢 -99 B 🟢 -92 B
assets/ComfyQueueButton-Ohpi--7M.js (new) 128 B 🔴 +128 B 🔴 +99 B 🔴 +92 B

Status: 6 added / 6 removed / 8 unchanged

Data & Services — 3.37 MB (baseline 3.37 MB) • 🔴 +304 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/settingStore-CxNqC3Vj.js (new) 3.1 MB 🔴 +3.1 MB 🔴 +715 kB 🔴 +539 kB
assets/settingStore-AZB1zRfW.js (removed) 3.1 MB 🟢 -3.1 MB 🟢 -715 kB 🟢 -539 kB
assets/load3dService-CoF1RYxt.js (new) 132 kB 🔴 +132 kB 🔴 +29.2 kB 🔴 +24.6 kB
assets/load3dService-CUwrspty.js (removed) 132 kB 🟢 -132 kB 🟢 -29.2 kB 🟢 -24.6 kB
assets/api-CHG35mnQ.js (new) 91.3 kB 🔴 +91.3 kB 🔴 +25.3 kB 🔴 +21.7 kB
assets/api-lCOxcOV2.js (removed) 91.3 kB 🟢 -91.3 kB 🟢 -25.3 kB 🟢 -21.7 kB
assets/workflowShareService-_gtwE0ij.js (removed) 17 kB 🟢 -17 kB 🟢 -5 kB 🟢 -4.43 kB
assets/workflowShareService-jTN5HW8P.js (new) 17 kB 🔴 +17 kB 🔴 +5 kB 🔴 +4.43 kB
assets/keybindingService-Disokucm.js (removed) 7.28 kB 🟢 -7.28 kB 🟢 -1.89 kB 🟢 -1.62 kB
assets/keybindingService-DNP7Syo-.js (new) 7.28 kB 🔴 +7.28 kB 🔴 +1.9 kB 🔴 +1.62 kB
assets/releaseStore-CLccLTU5.js (new) 6.77 kB 🔴 +6.77 kB 🔴 +2.06 kB 🔴 +1.8 kB
assets/releaseStore-CRnv9dra.js (removed) 6.77 kB 🟢 -6.77 kB 🟢 -2.06 kB 🟢 -1.8 kB
assets/systemStatsStore-BwBOoDZ5.js (removed) 4.99 kB 🟢 -4.99 kB 🟢 -1.77 kB 🟢 -1.5 kB
assets/systemStatsStore-D9IrWTU_.js (new) 4.99 kB 🔴 +4.99 kB 🔴 +1.77 kB 🔴 +1.5 kB
assets/userStore-CsYWmwrV.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -929 B 🟢 -819 B
assets/userStore-DGkvC_LV.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +931 B 🔴 +819 B
assets/audioService-ChFwtv6r.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +862 B 🔴 +746 B
assets/audioService-De7U1PkA.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -861 B 🟢 -746 B
assets/dialogService-D9AFba30.js (removed) 98 B 🟢 -98 B 🟢 -97 B 🟢 -81 B
assets/dialogService-DplnCrb2.js (new) 98 B 🔴 +98 B 🔴 +97 B 🔴 +81 B
assets/releaseStore-Cl8Qnkfg.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +93 B
assets/releaseStore-GcRlTtzP.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -88 B
assets/settingStore-DltZMZn0.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +86 B
assets/settingStore-fUQYbOGf.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -81 B
assets/assetsStore-BpTh5z1H.js (new) 94 B 🔴 +94 B 🔴 +92 B 🔴 +86 B
assets/assetsStore-CZycwhFJ.js (removed) 94 B 🟢 -94 B 🟢 -92 B 🟢 -85 B
assets/api-CQ9oboSW.js (removed) 62 B 🟢 -62 B 🟢 -74 B 🟢 -66 B
assets/api-DOsjaLN9.js (new) 62 B 🔴 +62 B 🔴 +74 B 🔴 +66 B

Status: 14 added / 14 removed / 3 unchanged

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-BHU2Xoa1.js (removed) 235 kB 🟢 -235 kB 🟢 -52.5 kB 🟢 -42.7 kB
assets/useConflictDetection-CMFcdV3e.js (new) 235 kB 🔴 +235 kB 🔴 +52.5 kB 🔴 +42.7 kB
assets/useLoad3d-BF-70FwB.js (removed) 25.8 kB 🟢 -25.8 kB 🟢 -5.82 kB 🟢 -5.16 kB
assets/useLoad3d-DCNyosWb.js (new) 25.8 kB 🔴 +25.8 kB 🔴 +5.83 kB 🔴 +5.16 kB
assets/useLoad3dViewer-CcO7i1Uy.js (removed) 21.3 kB 🟢 -21.3 kB 🟢 -5 kB 🟢 -4.4 kB
assets/useLoad3dViewer-HK-SiFKK.js (new) 21.3 kB 🔴 +21.3 kB 🔴 +5.01 kB 🔴 +4.38 kB
assets/useFeatureFlags-BDcxDMP7.js (new) 7.08 kB 🔴 +7.08 kB 🔴 +2.13 kB 🔴 +1.79 kB
assets/useFeatureFlags-DMwKoCVF.js (removed) 7.08 kB 🟢 -7.08 kB 🟢 -2.13 kB 🟢 -1.79 kB
assets/useDowngradeToPersonal-DcBXnFsz.js (new) 3.51 kB 🔴 +3.51 kB 🔴 +1.26 kB 🔴 +1.12 kB
assets/useDowngradeToPersonal-qYWXixko.js (removed) 3.51 kB 🟢 -3.51 kB 🟢 -1.27 kB 🟢 -1.1 kB
assets/useExternalLink-C_cLZhIy.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.09 kB 🔴 +947 B
assets/useExternalLink-CF2wAQjM.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.09 kB 🟢 -954 B
assets/downloadUtil--uKfoHiH.js (new) 2.45 kB 🔴 +2.45 kB 🔴 +1.11 kB 🔴 +899 B
assets/downloadUtil-BDO4RGhY.js (removed) 2.45 kB 🟢 -2.45 kB 🟢 -1.12 kB 🟢 -900 B
assets/assetPreviewUtil-BfpIL8Nj.js (new) 2.41 kB 🔴 +2.41 kB 🔴 +1 kB 🔴 +873 B
assets/assetPreviewUtil-DxmuZYFO.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -1 kB 🟢 -874 B
assets/useUpstreamValue-CIa3w7JG.js (new) 2.04 kB 🔴 +2.04 kB 🔴 +789 B 🔴 +701 B
assets/useUpstreamValue-Cq_nyt_6.js (removed) 2.04 kB 🟢 -2.04 kB 🟢 -789 B 🟢 -700 B
assets/useWorkspaceTierLabel-CPcT5tIC.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -849 B 🟢 -728 B
assets/useWorkspaceTierLabel-DXPTpmBS.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +850 B 🔴 +729 B
assets/useErrorHandling-BQRle7p9.js (removed) 1.54 kB 🟢 -1.54 kB 🟢 -645 B 🟢 -547 B
assets/useErrorHandling-CbCSmxLv.js (new) 1.54 kB 🔴 +1.54 kB 🔴 +644 B 🔴 +549 B
assets/subscriptionCheckoutUtil-CFp756Y5.js (new) 903 B 🔴 +903 B 🔴 +536 B 🔴 +458 B
assets/subscriptionCheckoutUtil-ZU3Bo4lx.js (removed) 903 B 🟢 -903 B 🟢 -537 B 🟢 -456 B
assets/useSessionCookie-Ev87f8J7.js (removed) 746 B 🟢 -746 B 🟢 -393 B 🟢 -339 B
assets/useSessionCookie-wKpeVuK4.js (new) 746 B 🔴 +746 B 🔴 +390 B 🔴 +336 B
assets/useLoad3d-BuyM8TU_.js (removed) 311 B 🟢 -311 B 🟢 -165 B 🟢 -152 B
assets/useLoad3d-CjGk9oeh.js (new) 311 B 🔴 +311 B 🔴 +162 B 🔴 +147 B
assets/useSessionCookie-4U-ku9l7.js (removed) 101 B 🟢 -101 B 🟢 -86 B 🟢 -81 B
assets/useSessionCookie-DqDTh3TD.js (new) 101 B 🔴 +101 B 🔴 +86 B 🔴 +79 B
assets/useFeatureFlags-CoGWS1Zp.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +81 B
assets/useFeatureFlags-Cuah_DKU.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -75 B
assets/useLoad3dViewer-BqQU84Hp.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +84 B
assets/useLoad3dViewer-DSklXaOz.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -95 B
assets/useCurrentUser-BiyTx2zE.js (new) 94 B 🔴 +94 B 🔴 +95 B 🔴 +84 B
assets/useCurrentUser-DonefNfC.js (removed) 94 B 🟢 -94 B 🟢 -95 B 🟢 -91 B

Status: 18 added / 18 removed / 17 unchanged

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

External libraries and shared vendor chunks

Status: 16 unchanged

Other — 12.4 MB (baseline 12.4 MB) • 🔴 +1.06 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/main-CFaqOl9d.js (new) 190 kB 🔴 +190 kB 🔴 +55.6 kB 🔴 +46.7 kB
assets/main-B4nPMuDj.js (removed) 190 kB 🟢 -190 kB 🟢 -55.5 kB 🟢 -46.6 kB
assets/core-BYXNrBlc.js (new) 111 kB 🔴 +111 kB 🔴 +28.7 kB 🔴 +24.3 kB
assets/core-FyxXSkNT.js (removed) 111 kB 🟢 -111 kB 🟢 -28.7 kB 🟢 -24.3 kB
assets/WidgetSelect-CNQeiXbF.js (new) 88 kB 🔴 +88 kB 🔴 +20 kB 🔴 +17.1 kB
assets/WidgetSelect-CRTvhpf6.js (removed) 88 kB 🟢 -88 kB 🟢 -19.9 kB 🟢 -17.1 kB
assets/Load3D-B0ChDon1.js (removed) 68.8 kB 🟢 -68.8 kB 🟢 -11 kB 🟢 -9.49 kB
assets/Load3D-DEDMsZTq.js (new) 68.8 kB 🔴 +68.8 kB 🔴 +11 kB 🔴 +9.49 kB
assets/Preview3d-C6odh89w.js (removed) 50.9 kB 🟢 -50.9 kB 🟢 -8.34 kB 🟢 -7.29 kB
assets/Preview3d-CS0A2U3T.js (new) 50.9 kB 🔴 +50.9 kB 🔴 +8.34 kB 🔴 +7.28 kB
assets/main-D9a5JDs6.js (removed) 46.3 kB 🟢 -46.3 kB 🟢 -13.7 kB 🟢 -11.9 kB
assets/main-orS7_uDA.js (new) 46.3 kB 🔴 +46.3 kB 🔴 +13.7 kB 🔴 +11.9 kB
assets/SubscriptionTransitionPreviewWorkspace-B5PweZ-o.js (new) 43.5 kB 🔴 +43.5 kB 🔴 +9.35 kB 🔴 +8.21 kB
assets/SubscriptionTransitionPreviewWorkspace-BvzgscUf.js (removed) 43.5 kB 🟢 -43.5 kB 🟢 -9.35 kB 🟢 -8.21 kB
assets/SubscriptionRequiredDialogContentUnified-B6Rkvcc4.js (removed) 42.1 kB 🟢 -42.1 kB 🟢 -9.37 kB 🟢 -8.15 kB
assets/SubscriptionRequiredDialogContentUnified-Cpzep4CG.js (new) 42.1 kB 🔴 +42.1 kB 🔴 +9.38 kB 🔴 +8.16 kB
assets/WorkspacePanelContent-ChtGleov.js (removed) 42.1 kB 🟢 -42.1 kB 🟢 -8.78 kB 🟢 -7.78 kB
assets/WorkspacePanelContent-CRmz1XcS.js (new) 42.1 kB 🔴 +42.1 kB 🔴 +8.78 kB 🔴 +7.77 kB
assets/WidgetBoundingBoxes-45d8FVQp.js (new) 33.8 kB 🔴 +33.8 kB 🔴 +9.2 kB 🔴 +8.15 kB
assets/WidgetBoundingBoxes-CACI_1Vt.js (removed) 33.8 kB 🟢 -33.8 kB 🟢 -9.2 kB 🟢 -8.16 kB
assets/WidgetPainter-BguOklGN.js (new) 32.7 kB 🔴 +32.7 kB 🔴 +7.9 kB 🔴 +6.99 kB
assets/WidgetPainter-DprS-xDR.js (removed) 32.7 kB 🟢 -32.7 kB 🟢 -7.9 kB 🟢 -7 kB
assets/Load3dViewerContent-Bxc4DXf1.js (new) 30.8 kB 🔴 +30.8 kB 🔴 +6.29 kB 🔴 +5.46 kB
assets/Load3dViewerContent-Dqz1MjFi.js (removed) 30.8 kB 🟢 -30.8 kB 🟢 -6.29 kB 🟢 -5.45 kB
assets/HdrViewerContent-BtFIJAhX.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +8.28 kB 🔴 +7.31 kB
assets/HdrViewerContent-DmW_ADPf.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -8.28 kB 🟢 -7.31 kB
assets/SubscriptionPanelContentWorkspace-Be75e8Ie.js (removed) 25.9 kB 🟢 -25.9 kB 🟢 -6.04 kB 🟢 -5.32 kB
assets/SubscriptionPanelContentWorkspace-CYxVgsFt.js (new) 25.9 kB 🔴 +25.9 kB 🔴 +6.04 kB 🔴 +5.33 kB
assets/SubscriptionRequiredDialogContentWorkspace-BdkdJeM_.js (removed) 24.9 kB 🟢 -24.9 kB 🟢 -5.77 kB 🟢 -5.07 kB
assets/SubscriptionRequiredDialogContentWorkspace-SdKPakGO.js (new) 24.9 kB 🔴 +24.9 kB 🔴 +5.77 kB 🔴 +5.06 kB
assets/SubscriptionRequiredDialogContent-DeQ6mO-4.js (new) 24.4 kB 🔴 +24.4 kB 🔴 +6 kB 🔴 +5.3 kB
assets/SubscriptionRequiredDialogContent-DkLaQU0h.js (removed) 24.4 kB 🟢 -24.4 kB 🟢 -6 kB 🟢 -5.29 kB
assets/WidgetImageCrop-C4wye1RY.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.75 kB 🔴 +5.04 kB
assets/WidgetImageCrop-jKayk7aw.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.75 kB 🟢 -5.04 kB
assets/load3d-D0JxK4eP.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.43 kB 🔴 +4.69 kB
assets/load3d-DDeq1NqA.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.43 kB 🟢 -4.69 kB
assets/CurrentUserPopoverWorkspace-BJrQIIX-.js (removed) 20.5 kB 🟢 -20.5 kB 🟢 -4.71 kB 🟢 -4.2 kB
assets/CurrentUserPopoverWorkspace-CofAYbHY.js (new) 20.5 kB 🔴 +20.5 kB 🔴 +4.71 kB 🔴 +4.19 kB
assets/SignInContent-BSHlCT81.js (removed) 20.3 kB 🟢 -20.3 kB 🟢 -5.13 kB 🟢 -4.48 kB
assets/SignInContent-xljIegq6.js (new) 20.3 kB 🔴 +20.3 kB 🔴 +5.13 kB 🔴 +4.5 kB
assets/WidgetInputNumber-BPaZa1-I.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.83 kB 🟢 -4.29 kB
assets/WidgetInputNumber-iYVpEZoJ.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.83 kB 🔴 +4.28 kB
assets/CreditsTile-BvcTxC5A.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -4.57 kB 🟢 -4.04 kB
assets/CreditsTile-rwh0QESX.js (new) 17.2 kB 🔴 +17.2 kB 🔴 +4.57 kB 🔴 +4.03 kB
assets/WidgetRecordAudio-B7VC7cqR.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.63 kB 🔴 +4.14 kB
assets/WidgetRecordAudio-DAtk-QHj.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.63 kB 🟢 -4.14 kB
assets/WidgetRange-DS5vXUjU.js (new) 16.1 kB 🔴 +16.1 kB 🔴 +4.17 kB 🔴 +3.72 kB
assets/WidgetRange-DyGlwDVp.js (removed) 16.1 kB 🟢 -16.1 kB 🟢 -4.17 kB 🟢 -3.72 kB
assets/WaveAudioPlayer-B320fDyZ.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.49 kB 🟢 -3.07 kB
assets/WaveAudioPlayer-D62ixq4g.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.49 kB 🔴 +3.06 kB
assets/i18n-BBshrXIv.js (new) 12.6 kB 🔴 +12.6 kB 🔴 +3.33 kB 🔴 +2.81 kB
assets/i18n-CpI-pKfV.js (removed) 12.6 kB 🟢 -12.6 kB 🟢 -3.34 kB 🟢 -2.81 kB
assets/WidgetCurve-DW6nsW59.js (removed) 11.3 kB 🟢 -11.3 kB 🟢 -3.51 kB 🟢 -3.17 kB
assets/WidgetCurve-DY07dq8a.js (new) 11.3 kB 🔴 +11.3 kB 🔴 +3.51 kB 🔴 +3.16 kB
assets/AudioPreviewPlayer-Dn6YSbRY.js (removed) 10.6 kB 🟢 -10.6 kB 🟢 -3.08 kB 🟢 -2.75 kB
assets/AudioPreviewPlayer-DyaD57l0.js (new) 10.6 kB 🔴 +10.6 kB 🔴 +3.08 kB 🔴 +2.75 kB
assets/TeamWorkspacesDialogContent-BoRKZ1Wd.js (new) 10.3 kB 🔴 +10.3 kB 🔴 +3 kB 🔴 +2.65 kB
assets/TeamWorkspacesDialogContent-utdLeXCd.js (removed) 10.3 kB 🟢 -10.3 kB 🟢 -3 kB 🟢 -2.65 kB
assets/onboardingCloudRoutes-CkPobi6K.js (new) 9.03 kB 🔴 +9.03 kB 🔴 +2.76 kB 🔴 +2.39 kB
assets/onboardingCloudRoutes-pipl_mXY.js (removed) 9.03 kB 🟢 -9.03 kB 🟢 -2.75 kB 🟢 -2.38 kB
assets/Load3DConfiguration-BaURdoRr.js (new) 8.97 kB 🔴 +8.97 kB 🔴 +2.63 kB 🔴 +2.32 kB
assets/Load3DConfiguration-MqOjXdap.js (removed) 8.97 kB 🟢 -8.97 kB 🟢 -2.63 kB 🟢 -2.32 kB
assets/nodeTemplates-CiqolG_c.js (new) 8.33 kB 🔴 +8.33 kB 🔴 +2.87 kB 🔴 +2.54 kB
assets/nodeTemplates-DWRinuOs.js (removed) 8.33 kB 🟢 -8.33 kB 🟢 -2.87 kB 🟢 -2.54 kB
assets/NightlySurveyController-cmKNOqih.js (removed) 7.56 kB 🟢 -7.56 kB 🟢 -2.59 kB 🟢 -2.29 kB
assets/NightlySurveyController-DUvRtwfW.js (new) 7.56 kB 🔴 +7.56 kB 🔴 +2.59 kB 🔴 +2.3 kB
assets/WidgetWithControl-DG1wZ2Ux.js (new) 6.38 kB 🔴 +6.38 kB 🔴 +2.59 kB 🔴 +2.3 kB
assets/WidgetWithControl-u4nWzCHk.js (removed) 6.38 kB 🟢 -6.38 kB 🟢 -2.59 kB 🟢 -2.31 kB
assets/tierBenefits-BCLA7Anu.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +2 kB 🔴 +1.75 kB
assets/tierBenefits-DJVgkb5Z.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -2.01 kB 🟢 -1.75 kB
assets/CancelSubscriptionDialogContent-CzJXXjav.js (removed) 6.01 kB 🟢 -6.01 kB 🟢 -2.05 kB 🟢 -1.8 kB
assets/CancelSubscriptionDialogContent-vc4qwSOT.js (new) 6.01 kB 🔴 +6.01 kB 🔴 +2.05 kB 🔴 +1.8 kB
assets/load3dPreviewExtensions-75YNG3Xf.js (removed) 5.94 kB 🟢 -5.94 kB 🟢 -1.84 kB 🟢 -1.63 kB
assets/load3dPreviewExtensions-DE5rXNns.js (new) 5.94 kB 🔴 +5.94 kB 🔴 +1.84 kB 🔴 +1.62 kB
assets/main-e1g6CiM8.js (new) 5.84 kB 🔴 +5.84 kB 🔴 +1.86 kB 🔴 +1.59 kB
assets/main-yoNYAfVI.js (removed) 5.84 kB 🟢 -5.84 kB 🟢 -1.86 kB 🟢 -1.58 kB
assets/missingModelDownload-BZX-EaoA.js (new) 5.59 kB 🔴 +5.59 kB 🔴 +2.04 kB 🔴 +1.79 kB
assets/FreeTierDialogContent-BrQVPbmf.js (new) 5.47 kB 🔴 +5.47 kB 🔴 +1.83 kB 🔴 +1.61 kB
assets/FreeTierDialogContent-Coadan97.js (removed) 5.47 kB 🟢 -5.47 kB 🟢 -1.83 kB 🟢 -1.61 kB
assets/CreateWorkspaceDialogContent-1kQJv2gU.js (removed) 5.19 kB 🟢 -5.19 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/CreateWorkspaceDialogContent-Dsx42ggA.js (new) 5.19 kB 🔴 +5.19 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/ChangeMemberRoleDialogContent-C5pg7a5J.js (removed) 5.04 kB 🟢 -5.04 kB 🟢 -1.67 kB 🟢 -1.46 kB
assets/ChangeMemberRoleDialogContent-jfoS2O47.js (new) 5.04 kB 🔴 +5.04 kB 🔴 +1.67 kB 🔴 +1.46 kB
assets/missingModelDownload-Cq3M7aCV.js (removed) 5.03 kB 🟢 -5.03 kB 🟢 -1.96 kB 🟢 -1.7 kB
assets/EditWorkspaceDialogContent-D4tLEcb7.js (new) 5 kB 🔴 +5 kB 🔴 +1.79 kB 🔴 +1.56 kB
assets/EditWorkspaceDialogContent-DJJdPp4e.js (removed) 5 kB 🟢 -5 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/WidgetTextarea-B8cyJVIr.js (removed) 4.86 kB 🟢 -4.86 kB 🟢 -1.9 kB 🟢 -1.66 kB
assets/WidgetTextarea-D5D3e5lv.js (new) 4.86 kB 🔴 +4.86 kB 🔴 +1.9 kB 🔴 +1.66 kB
assets/saveMesh-6oz99sW3.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.55 kB 🔴 +1.37 kB
assets/saveMesh-DwpO6EMC.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.55 kB 🟢 -1.37 kB
assets/InviteMemberDialogContent-k-aW-8Me.js (removed) 4.75 kB 🟢 -4.75 kB 🟢 -1.61 kB 🟢 -1.4 kB
assets/InviteMemberDialogContent-QYRmDfRi.js (new) 4.75 kB 🔴 +4.75 kB 🔴 +1.61 kB 🔴 +1.4 kB
assets/ValueControlPopover-Bv0Gn-96.js (new) 4.54 kB 🔴 +4.54 kB 🔴 +1.58 kB 🔴 +1.42 kB
assets/ValueControlPopover-Cw37L88m.js (removed) 4.54 kB 🟢 -4.54 kB 🟢 -1.58 kB 🟢 -1.41 kB
assets/ApiNodesSignInContent-B-2oeXSb.js (removed) 4.14 kB 🟢 -4.14 kB 🟢 -1.34 kB 🟢 -1.18 kB
assets/ApiNodesSignInContent-DRJ-xcbn.js (new) 4.14 kB 🔴 +4.14 kB 🔴 +1.34 kB 🔴 +1.18 kB
assets/DeleteWorkspaceDialogContent-CH36hmPb.js (new) 3.91 kB 🔴 +3.91 kB 🔴 +1.47 kB 🔴 +1.27 kB
assets/DeleteWorkspaceDialogContent-CT7oM3na.js (removed) 3.91 kB 🟢 -3.91 kB 🟢 -1.47 kB 🟢 -1.27 kB
assets/LeaveWorkspaceDialogContent-C4clcFGp.js (new) 3.73 kB 🔴 +3.73 kB 🔴 +1.42 kB 🔴 +1.22 kB
assets/LeaveWorkspaceDialogContent-Jiicp7_j.js (removed) 3.73 kB 🟢 -3.73 kB 🟢 -1.42 kB 🟢 -1.22 kB
assets/RemoveMemberDialogContent-DEMcL8ZB.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.37 kB 🔴 +1.18 kB
assets/RemoveMemberDialogContent-DUtW990K.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.37 kB 🟢 -1.18 kB
assets/RevokeInviteDialogContent-CEniZxZe.js (new) 3.62 kB 🔴 +3.62 kB 🔴 +1.38 kB 🔴 +1.21 kB
assets/RevokeInviteDialogContent-DyOLmFij.js (removed) 3.62 kB 🟢 -3.62 kB 🟢 -1.38 kB 🟢 -1.2 kB
assets/InviteMemberUpsellDialogContent-BhfsQ-ux.js (removed) 3.51 kB 🟢 -3.51 kB 🟢 -1.26 kB 🟢 -1.09 kB
assets/InviteMemberUpsellDialogContent-CXMCdkC8.js (new) 3.51 kB 🔴 +3.51 kB 🔴 +1.26 kB 🔴 +1.09 kB
assets/workspaceCheckoutTelemetry--EJLe1YQ.js (removed) 3.46 kB 🟢 -3.46 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/workspaceCheckoutTelemetry-DYMU5IHy.js (new) 3.46 kB 🔴 +3.46 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/Media3DTop-C25htsb5.js (removed) 3.26 kB 🟢 -3.26 kB 🟢 -1.3 kB 🟢 -1.13 kB
assets/Media3DTop-DQaGdYDm.js (new) 3.26 kB 🔴 +3.26 kB 🔴 +1.3 kB 🔴 +1.13 kB
assets/GlobalToast-BmF-yrHf.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.25 kB 🔴 +1.08 kB
assets/GlobalToast-xmIj1jlZ.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.25 kB 🟢 -1.08 kB
assets/load3dAdvanced-B4-EGwrf.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.12 kB 🔴 +976 B
assets/load3dAdvanced-D5JnA1n_.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.13 kB 🟢 -980 B
assets/SubscribeToRun-bJ3lU_aW.js (new) 2.45 kB 🔴 +2.45 kB 🔴 +1.07 kB 🔴 +940 B
assets/SubscribeToRun-ByGsQ7hT.js (removed) 2.45 kB 🟢 -2.45 kB 🟢 -1.07 kB 🟢 -927 B
assets/graphHasMissingNodes-BRlGALYO.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +902 B 🔴 +806 B
assets/graphHasMissingNodes-SuNRtLV_.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -902 B 🟢 -787 B
assets/MediaAudioTop-C8dN9IHB.js (new) 1.67 kB 🔴 +1.67 kB 🔴 +836 B 🔴 +698 B
assets/MediaAudioTop-DBmDc5qz.js (removed) 1.67 kB 🟢 -1.67 kB 🟢 -837 B 🟢 -697 B
assets/signInSchema-BBcgu4Ky.js (new) 1.6 kB 🔴 +1.6 kB 🔴 +584 B 🔴 +514 B
assets/signInSchema-Ssp05v5D.js (removed) 1.6 kB 🟢 -1.6 kB 🟢 -582 B 🟢 -557 B
assets/CloudRunButtonWrapper-BGo9Pr26.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -548 B 🟢 -478 B
assets/CloudRunButtonWrapper-cUG7HfTw.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +547 B 🔴 +502 B
assets/cloudSessionCookie-DK03vtmI.js (removed) 989 B 🟢 -989 B 🟢 -467 B 🟢 -428 B
assets/cloudSessionCookie-PU8uMIhR.js (new) 989 B 🔴 +989 B 🔴 +464 B 🔴 +412 B
assets/cloudBadges-9IPcGdNm.js (new) 971 B 🔴 +971 B 🔴 +544 B 🔴 +489 B
assets/cloudBadges-d4W71y32.js (removed) 971 B 🟢 -971 B 🟢 -547 B 🟢 -490 B
assets/Load3DAdvanced-BAdAAJvh.js (new) 813 B 🔴 +813 B 🔴 +454 B 🔴 +413 B
assets/Load3DAdvanced-BIkoCs7K.js (removed) 813 B 🟢 -813 B 🟢 -453 B 🟢 -407 B
assets/nightlyBadges-Bje8E3kG.js (removed) 462 B 🟢 -462 B 🟢 -305 B 🟢 -250 B
assets/nightlyBadges-WJaT6ihA.js (new) 462 B 🔴 +462 B 🔴 +305 B 🔴 +250 B
assets/missingModelDownload-D3T81p_C.js (new) 326 B 🔴 +326 B 🔴 +188 B 🔴 +171 B
assets/missingModelDownload-CWoEN2qs.js (removed) 228 B 🟢 -228 B 🟢 -149 B 🟢 -129 B
assets/SubscriptionPanelContentWorkspace-C7KpptmE.js (removed) 179 B 🟢 -179 B 🟢 -117 B 🟢 -92 B
assets/SubscriptionPanelContentWorkspace-CRby5z1g.js (new) 179 B 🔴 +179 B 🔴 +117 B 🔴 +92 B
assets/Load3dViewerContent-0x49vSuJ.js (removed) 137 B 🟢 -137 B 🟢 -103 B 🟢 -90 B
assets/Load3dViewerContent-D3NDRGPE.js (new) 137 B 🔴 +137 B 🔴 +103 B 🔴 +91 B
assets/Load3DAdvanced-CbyM2JwX.js (new) 122 B 🔴 +122 B 🔴 +97 B 🔴 +91 B
assets/Load3DAdvanced-CDeoa2mQ.js (removed) 122 B 🟢 -122 B 🟢 -97 B 🟢 -88 B
assets/WidgetLegacy-Bo7ttTUt.js (removed) 117 B 🟢 -117 B 🟢 -106 B 🟢 -102 B
assets/WidgetLegacy-gNPCpu1D.js (new) 117 B 🔴 +117 B 🔴 +106 B 🔴 +101 B
assets/workflowDraftStoreV2-Bp518J6L.js (new) 112 B 🔴 +112 B 🔴 +101 B 🔴 +108 B
assets/workflowDraftStoreV2-CuL0Tz8t.js (removed) 112 B 🟢 -112 B 🟢 -101 B 🟢 -116 B
assets/Load3D-DLob-j1r.js (new) 98 B 🔴 +98 B 🔴 +89 B 🔴 +85 B
assets/Load3D-nfct3ZNR.js (removed) 98 B 🟢 -98 B 🟢 -89 B 🟢 -81 B
assets/i18n-C4KpE7zZ.js (removed) 97 B 🟢 -97 B 🟢 -92 B 🟢 -85 B
assets/i18n-Crx8F5uL.js (new) 97 B 🔴 +97 B 🔴 +92 B 🔴 +87 B
assets/changeTracker-CysZvG88.js (new) 91 B 🔴 +91 B 🔴 +93 B 🔴 +82 B
assets/changeTracker-DmYLjWRe.js (removed) 91 B 🟢 -91 B 🟢 -93 B 🟢 -84 B

Status: 75 added / 75 removed / 199 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.7 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 48.0 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 71.2 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 52.9 MB heap
large-graph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.1 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 60.4 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.4 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.5 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.2 MB heap
subgraph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.3 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.8 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 153ms TBT · 73.8 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.6 MB heap
vue-large-graph-idle: · 55.4 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 158.7 MB heap
vue-large-graph-pan: · 55.4 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 152ms TBT · 153.3 MB heap
workflow-execution: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 48.3 MB heap

⚠️ 6 regressions detected

Show regressions
Metric Baseline PR (median) Δ Sig
canvas-idle: task duration 532ms 538ms +1% ⚠️ z=4.6
canvas-zoom-sweep: task duration 402ms 377ms -6% ⚠️ z=2.2
large-graph-idle: task duration 802ms 667ms -17% ⚠️ z=2.3
large-graph-pan: task duration 1374ms 1292ms -6% ⚠️ z=4.9
minimap-idle: task duration 787ms 654ms -17% ⚠️ z=2.7
subgraph-idle: task duration 537ms 489ms -9% ⚠️ z=3.8
All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms +0% z=-0.1
canvas-idle: p95 frame time 17ms 17ms +1%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 9ms 9ms -1% z=-1.9
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 10 10 +0% z=-2.1
canvas-idle: task duration 532ms 538ms +1% ⚠️ z=4.6
canvas-idle: script duration 23ms 20ms -13% z=-2.3
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 67.8 MB 67.7 MB -0%
canvas-idle: DOM nodes 20 20 +0% z=-2.0
canvas-idle: event listeners 4 5 +25% z=-1.4
canvas-mouse-sweep: avg frame time 17ms 17ms +0% z=-0.4
canvas-mouse-sweep: p95 frame time 17ms 17ms -0%
canvas-mouse-sweep: layout duration 4ms 4ms -3% z=-0.4
canvas-mouse-sweep: style recalc duration 44ms 38ms -13% z=-1.5
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 76 74 -3% z=-2.0
canvas-mouse-sweep: task duration 938ms 889ms -5% z=0.4
canvas-mouse-sweep: script duration 133ms 128ms -4% z=-1.2
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 50.6 MB 48.0 MB -5%
canvas-mouse-sweep: DOM nodes -266 -269 +1% z=-127.9
canvas-mouse-sweep: event listeners -133 -133 +0% z=-33.9
canvas-zoom-sweep: avg frame time 17ms 17ms +0% z=-0.3
canvas-zoom-sweep: p95 frame time 17ms 17ms +0%
canvas-zoom-sweep: layout duration 1ms 1ms -7% z=-1.7
canvas-zoom-sweep: style recalc duration 18ms 17ms -6% z=-1.7
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 32 31 -3% z=-0.6
canvas-zoom-sweep: task duration 402ms 377ms -6% ⚠️ z=2.2
canvas-zoom-sweep: script duration 23ms 18ms -23% z=-3.1
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 71.1 MB 71.2 MB +0%
canvas-zoom-sweep: DOM nodes 78 76 -3% z=-4.1
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
dom-widget-clipping: p95 frame time 17ms 17ms -0%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 8ms 8ms -11% z=-2.9
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 11 11 +0% z=-4.2
dom-widget-clipping: task duration 392ms 379ms -3% z=0.9
dom-widget-clipping: script duration 61ms 60ms -1% z=-2.4
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 52.9 MB 52.9 MB +0%
dom-widget-clipping: DOM nodes 18 18 +0% z=-2.9
dom-widget-clipping: event listeners 0 1 variance too high
large-graph-idle: avg frame time 17ms 17ms -0% z=-0.6
large-graph-idle: p95 frame time 17ms 17ms +0%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 7ms -14% z=-4.5
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 8 8 +0% z=-11.5
large-graph-idle: task duration 802ms 667ms -17% ⚠️ z=2.3
large-graph-idle: script duration 127ms 106ms -16% z=0.4
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 62.8 MB 62.1 MB -1%
large-graph-idle: DOM nodes -268 -269 +0% z=-324.4
large-graph-idle: event listeners -127 -129 +2% z=-25.4
large-graph-pan: avg frame time 17ms 17ms +0% z=0.3
large-graph-pan: p95 frame time 17ms 17ms -0%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 14ms 14ms -2% z=-4.4
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 68 69 +1% z=-1.7
large-graph-pan: task duration 1374ms 1292ms -6% ⚠️ z=4.9
large-graph-pan: script duration 454ms 438ms -4% z=1.5
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 62.4 MB 60.4 MB -3%
large-graph-pan: DOM nodes -269 -271 +1% z=-175.8
large-graph-pan: event listeners -129 -128 -1% z=-160.5
large-graph-zoom: avg frame time 17ms 17ms +0%
large-graph-zoom: p95 frame time 17ms 17ms +0%
large-graph-zoom: layout duration 8ms 7ms -3%
large-graph-zoom: style recalc duration 16ms 15ms -6%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 65 -2%
large-graph-zoom: task duration 1620ms 1570ms -3%
large-graph-zoom: script duration 566ms 556ms -2%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 61.3 MB 62.4 MB +2%
large-graph-zoom: DOM nodes -271 -273 +1%
large-graph-zoom: event listeners -133 -133 +0%
minimap-idle: avg frame time 17ms 17ms -0% z=-0.4
minimap-idle: p95 frame time 17ms 17ms -0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 8ms 7ms -14% z=-2.8
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 8 8 +0% z=-2.3
minimap-idle: task duration 787ms 654ms -17% ⚠️ z=2.7
minimap-idle: script duration 129ms 100ms -22% z=0.2
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 63.8 MB 64.5 MB +1%
minimap-idle: DOM nodes -268 -269 +0% z=-210.7
minimap-idle: event listeners -129 -128 -1% z=-200.8
subgraph-dom-widget-clipping: avg frame time 17ms 17ms -0% z=-0.4
subgraph-dom-widget-clipping: p95 frame time 17ms 17ms -1%
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 11ms 10ms -8% z=-2.7
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 46 47 +2% z=-1.6
subgraph-dom-widget-clipping: task duration 425ms 404ms -5% z=1.4
subgraph-dom-widget-clipping: script duration 123ms 118ms -4% z=-1.6
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 53.4 MB 53.2 MB -0%
subgraph-dom-widget-clipping: DOM nodes 18 20 +11% z=-1.9
subgraph-dom-widget-clipping: event listeners 8 7 -13% z=-1.6
subgraph-idle: avg frame time 17ms 17ms +0% z=0.9
subgraph-idle: p95 frame time 17ms 17ms +1%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 9ms 9ms -2% z=-2.2
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 10 10 +0% z=-1.4
subgraph-idle: task duration 537ms 489ms -9% ⚠️ z=3.8
subgraph-idle: script duration 19ms 15ms -22% z=-2.0
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 44.1 MB 56.3 MB +28%
subgraph-idle: DOM nodes -264 -123 -54% z=-96.5
subgraph-idle: event listeners -133 -65 -52% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms -0% z=-0.5
subgraph-mouse-sweep: p95 frame time 17ms 17ms +0%
subgraph-mouse-sweep: layout duration 4ms 4ms -0% z=-1.1
subgraph-mouse-sweep: style recalc duration 38ms 37ms -3% z=-1.7
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 76 77 +1% z=-1.9
subgraph-mouse-sweep: task duration 864ms 782ms -9% z=0.2
subgraph-mouse-sweep: script duration 99ms 91ms -7% z=-1.4
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 44.4 MB 51.8 MB +17%
subgraph-mouse-sweep: DOM nodes -265 -101 -62% z=-74.9
subgraph-mouse-sweep: event listeners -133 -65 -52% variance too high
subgraph-transition-enter: avg frame time 17ms 17ms +0%
subgraph-transition-enter: p95 frame time 17ms 17ms +1%
subgraph-transition-enter: layout duration 13ms 15ms +9%
subgraph-transition-enter: style recalc duration 32ms 31ms -2%
subgraph-transition-enter: layout count 14 14 +0%
subgraph-transition-enter: style recalc count 18 18 +0%
subgraph-transition-enter: task duration 1017ms 933ms -8%
subgraph-transition-enter: script duration 39ms 34ms -14%
subgraph-transition-enter: TBT 138ms 153ms +11%
subgraph-transition-enter: heap used 93.2 MB 73.8 MB -21%
subgraph-transition-enter: DOM nodes 13673 13673 +0%
subgraph-transition-enter: event listeners 2371 2371 +0%
viewport-pan-sweep: avg frame time 17ms 17ms -0%
viewport-pan-sweep: p95 frame time 17ms 17ms -0%
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 39ms 37ms -6%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 250 250 +0%
viewport-pan-sweep: task duration 4822ms 4459ms -8%
viewport-pan-sweep: script duration 1476ms 1382ms -6%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 62.7 MB 61.6 MB -2%
viewport-pan-sweep: DOM nodes -266 -266 +0%
viewport-pan-sweep: event listeners -113 -113 +0%
vue-large-graph-idle: avg frame time 18ms 18ms +2%
vue-large-graph-idle: p95 frame time 17ms 17ms -0%
vue-large-graph-idle: layout duration 0ms 0ms +0%
vue-large-graph-idle: style recalc duration 0ms 0ms +0%
vue-large-graph-idle: layout count 0 0 +0%
vue-large-graph-idle: style recalc count 0 0 +0%
vue-large-graph-idle: task duration 17218ms 16753ms -3%
vue-large-graph-idle: script duration 588ms 584ms -1%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 159.2 MB 158.7 MB -0%
vue-large-graph-idle: DOM nodes -8311 -8311 +0%
vue-large-graph-idle: event listeners -16384 -16382 -0%
vue-large-graph-pan: avg frame time 18ms 18ms +2%
vue-large-graph-pan: p95 frame time 17ms 17ms -0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 18ms 17ms -7%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 142 140 -2%
vue-large-graph-pan: task duration 20660ms 20427ms -1%
vue-large-graph-pan: script duration 882ms 873ms -1%
vue-large-graph-pan: TBT 317ms 152ms -52%
vue-large-graph-pan: heap used 158.6 MB 153.3 MB -3%
vue-large-graph-pan: DOM nodes -8311 -8311 +0%
vue-large-graph-pan: event listeners -16378 -16377 -0%
workflow-execution: avg frame time 17ms 17ms -0% z=0.1
workflow-execution: p95 frame time 17ms 17ms -0%
workflow-execution: layout duration 0ms 1ms +150% z=-4.1
workflow-execution: style recalc duration 20ms 21ms +4% z=-1.5
workflow-execution: layout count 2 3 +50% z=-3.6
workflow-execution: style recalc count 13 17 +31% z=-0.4
workflow-execution: task duration 118ms 119ms +1% z=-0.3
workflow-execution: script duration 10ms 10ms -7% z=-6.5
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 48.2 MB 48.3 MB +0%
workflow-execution: DOM nodes 121 136 +12% z=-3.6
workflow-execution: event listeners 65 66 +2% z=3.2
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-07-24T05:40:45.092Z",
  "gitSha": "3435fc826558fd4b1084590da75b0c3706d850e1",
  "branch": "jaeone/fe-1173-gated-hf-download-hint-action",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2057.6620000000503,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.248999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 573.302,
      "heapDeltaBytes": 3551412,
      "heapUsedBytes": 71072128,
      "domNodes": 22,
      "jsHeapTotalBytes": 21110784,
      "scriptDurationMs": 21.984,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-idle",
      "durationMs": 2024.6480000000702,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 6.928000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 502.7579999999999,
      "heapDeltaBytes": 3517548,
      "heapUsedBytes": 70987856,
      "domNodes": 18,
      "jsHeapTotalBytes": 20848640,
      "scriptDurationMs": 18.443,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1818.3340000000499,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 37.273,
      "layouts": 12,
      "layoutDurationMs": 3.5669999999999997,
      "taskDurationMs": 890.2270000000001,
      "heapDeltaBytes": -19005904,
      "heapUsedBytes": 48612812,
      "domNodes": -268,
      "jsHeapTotalBytes": 20975616,
      "scriptDurationMs": 127.05,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1887.5419999999394,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 38.751,
      "layouts": 12,
      "layoutDurationMs": 3.5120000000000005,
      "taskDurationMs": 888.5880000000001,
      "heapDeltaBytes": -15243956,
      "heapUsedBytes": 52086148,
      "domNodes": -269,
      "jsHeapTotalBytes": 20975616,
      "scriptDurationMs": 128.775,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1737.2440000000324,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 17.411999999999995,
      "layouts": 6,
      "layoutDurationMs": 0.591,
      "taskDurationMs": 372.822,
      "heapDeltaBytes": 7039428,
      "heapUsedBytes": 74730232,
      "domNodes": 76,
      "jsHeapTotalBytes": 20848640,
      "scriptDurationMs": 17.496000000000002,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1706.770000000006,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 15.793000000000001,
      "layouts": 6,
      "layoutDurationMs": 0.5370000000000001,
      "taskDurationMs": 380.525,
      "heapDeltaBytes": 7285240,
      "heapUsedBytes": 74582548,
      "domNodes": 76,
      "jsHeapTotalBytes": 20586496,
      "scriptDurationMs": 18.298999999999996,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 570.8050000000071,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 6.995999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 379.37600000000003,
      "heapDeltaBytes": -11924292,
      "heapUsedBytes": 55689312,
      "domNodes": 16,
      "jsHeapTotalBytes": 21110784,
      "scriptDurationMs": 59.447,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 575.5249999999705,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 8.103000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 377.65999999999997,
      "heapDeltaBytes": -12286696,
      "heapUsedBytes": 55241908,
      "domNodes": 20,
      "jsHeapTotalBytes": 21897216,
      "scriptDurationMs": 60.451,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2020.2330000000188,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.861,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 666.8009999999999,
      "heapDeltaBytes": 6302164,
      "heapUsedBytes": 65765288,
      "domNodes": -269,
      "jsHeapTotalBytes": 5316608,
      "scriptDurationMs": 108.99599999999998,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2013.311999999928,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.108,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 667.78,
      "heapDeltaBytes": 5848996,
      "heapUsedBytes": 64506432,
      "domNodes": -268,
      "jsHeapTotalBytes": 4792320,
      "scriptDurationMs": 103.42800000000003,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2153.626000000031,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 13.784999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1310.3229999999999,
      "heapDeltaBytes": 1832328,
      "heapUsedBytes": 61601936,
      "domNodes": -271,
      "jsHeapTotalBytes": 5521408,
      "scriptDurationMs": 435.791,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2188.9660000000504,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 13.852999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1273.1650000000002,
      "heapDeltaBytes": 5487848,
      "heapUsedBytes": 65053420,
      "domNodes": -271,
      "jsHeapTotalBytes": 4997120,
      "scriptDurationMs": 439.683,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3349.2500000000405,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 13.889999999999995,
      "layouts": 60,
      "layoutDurationMs": 7.236000000000001,
      "taskDurationMs": 1604.368,
      "heapDeltaBytes": 5928724,
      "heapUsedBytes": 67880932,
      "domNodes": -275,
      "jsHeapTotalBytes": 8462336,
      "scriptDurationMs": 579.45,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3232.161000000019,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 16.282000000000004,
      "layouts": 60,
      "layoutDurationMs": 7.475,
      "taskDurationMs": 1534.918,
      "heapDeltaBytes": -791236,
      "heapUsedBytes": 62917548,
      "domNodes": -271,
      "jsHeapTotalBytes": 7675904,
      "scriptDurationMs": 532.1859999999999,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "minimap-idle",
      "durationMs": 2022.8040000000078,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.0600000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 647.609,
      "heapDeltaBytes": 6319696,
      "heapUsedBytes": 66337208,
      "domNodes": -269,
      "jsHeapTotalBytes": 4530176,
      "scriptDurationMs": 101.093,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2033.0860000000257,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.515999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 659.53,
      "heapDeltaBytes": 6741568,
      "heapUsedBytes": 69016168,
      "domNodes": -269,
      "jsHeapTotalBytes": 5054464,
      "scriptDurationMs": 98.94600000000001,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 564.6199999999908,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 10.524,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 398.84899999999993,
      "heapDeltaBytes": -11758236,
      "heapUsedBytes": 55622484,
      "domNodes": 20,
      "jsHeapTotalBytes": 22421504,
      "scriptDurationMs": 119.38300000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 603.5000000000537,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 9.718,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 408.319,
      "heapDeltaBytes": -11788420,
      "heapUsedBytes": 56027620,
      "domNodes": 20,
      "jsHeapTotalBytes": 21897216,
      "scriptDurationMs": 117.015,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2047.9119999999966,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.059999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 524.378,
      "heapDeltaBytes": -20604856,
      "heapUsedBytes": 46901124,
      "domNodes": -265,
      "jsHeapTotalBytes": 20189184,
      "scriptDurationMs": 16.7,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2007.3859999999968,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.396,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 452.674,
      "heapDeltaBytes": 3493084,
      "heapUsedBytes": 71191436,
      "domNodes": 20,
      "jsHeapTotalBytes": 20848640,
      "scriptDurationMs": 12.855000000000002,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1694.735000000037,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 37.068999999999996,
      "layouts": 16,
      "layoutDurationMs": 4.38,
      "taskDurationMs": 751.331,
      "heapDeltaBytes": -5515248,
      "heapUsedBytes": 61809876,
      "domNodes": 64,
      "jsHeapTotalBytes": 21110784,
      "scriptDurationMs": 90.054,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1741.1429999999655,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 36.619,
      "layouts": 16,
      "layoutDurationMs": 4.327,
      "taskDurationMs": 813.286,
      "heapDeltaBytes": -20622536,
      "heapUsedBytes": 46890876,
      "domNodes": -265,
      "jsHeapTotalBytes": 20713472,
      "scriptDurationMs": 92.913,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1400.4429999999957,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 31.166999999999994,
      "layouts": 14,
      "layoutDurationMs": 14.606000000000002,
      "taskDurationMs": 933.3790000000001,
      "heapDeltaBytes": 2076524,
      "heapUsedBytes": 77350704,
      "domNodes": 13673,
      "jsHeapTotalBytes": 14155776,
      "scriptDurationMs": 33.749,
      "eventListeners": 2371,
      "totalBlockingTimeMs": 153,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8282.869999999946,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 36.678000000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4466.706999999999,
      "heapDeltaBytes": 1287996,
      "heapUsedBytes": 59791992,
      "domNodes": -266,
      "jsHeapTotalBytes": 5840896,
      "scriptDurationMs": 1397.865,
      "eventListeners": -113,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8226.3650000001,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 36.367999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4451.107,
      "heapDeltaBytes": 10613240,
      "heapUsedBytes": 69491128,
      "domNodes": -266,
      "jsHeapTotalBytes": 7589888,
      "scriptDurationMs": 1365.6589999999999,
      "eventListeners": -113,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 16726.572999999975,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16689.594,
      "heapDeltaBytes": -58708580,
      "heapUsedBytes": 166406600,
      "domNodes": -8311,
      "jsHeapTotalBytes": -12783616,
      "scriptDurationMs": 584.004,
      "eventListeners": -16382,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.77333333333336,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 16836.236999999983,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16816.385000000002,
      "heapDeltaBytes": -55075880,
      "heapUsedBytes": 166499608,
      "domNodes": -8311,
      "jsHeapTotalBytes": -13045760,
      "scriptDurationMs": 583.6119999999999,
      "eventListeners": -16382,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.333333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 20621.865000000013,
      "styleRecalcs": 140,
      "styleRecalcDurationMs": 17.80599999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20579.033,
      "heapDeltaBytes": -51532508,
      "heapUsedBytes": 155403584,
      "domNodes": -8311,
      "jsHeapTotalBytes": -13570048,
      "scriptDurationMs": 882.838,
      "eventListeners": -16376,
      "totalBlockingTimeMs": 166,
      "frameDurationMs": 18.329999999999927,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 20307.67000000003,
      "styleRecalcs": 139,
      "styleRecalcDurationMs": 16.37500000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20275.414,
      "heapDeltaBytes": -42201328,
      "heapUsedBytes": 166017532,
      "domNodes": -8311,
      "jsHeapTotalBytes": -13832192,
      "scriptDurationMs": 862.3040000000001,
      "eventListeners": -16378,
      "totalBlockingTimeMs": 137,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "workflow-execution",
      "durationMs": 479.99000000004344,
      "styleRecalcs": 21,
      "styleRecalcDurationMs": 23.592999999999996,
      "layouts": 3,
      "layoutDurationMs": 1.33,
      "taskDurationMs": 131.33,
      "heapDeltaBytes": -15682176,
      "heapUsedBytes": 50941372,
      "domNodes": 152,
      "jsHeapTotalBytes": 7741440,
      "scriptDurationMs": 10.508,
      "eventListeners": 67,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "workflow-execution",
      "durationMs": 458.59199999995326,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 18.307,
      "layouts": 3,
      "layoutDurationMs": 0.5730000000000002,
      "taskDurationMs": 107.651,
      "heapDeltaBytes": -16226616,
      "heapUsedBytes": 50431936,
      "domNodes": 119,
      "jsHeapTotalBytes": 7741440,
      "scriptDurationMs": 8.978000000000002,
      "eventListeners": 65,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    }
  ]
}

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

🌐 Website E2E

Tip

All tests passed.

Status ✅ Passed
Report View Report

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Missing-model handling now detects gated Hugging Face repositories, caches access URLs, exposes gated access controls and hints, supports desktop-bridge delegation with browser fallback, and validates the behavior through unit and browser tests.

Changes

Missing model access flow

Layer / File(s) Summary
Gated metadata detection and storage
src/platform/missingModel/missingModelDownload.ts, src/platform/missingModel/missingModelPipeline.ts, src/platform/missingModel/missingModelStore.ts, src/platform/missingModel/*test.ts
Hugging Face gated responses are identified using hostname, status, and error-code checks; gated repository URLs are cached in the missing-model store and cleared with missing-model state.
Download and access action composable
src/platform/missingModel/composables/useMissingModelDownload.ts, packages/comfyui-desktop-bridge-types/*
A composable centralizes metadata prefetching, downloads, and desktop-bridge access with browser fallback; the bridge package declares the optional access method and updates its version.
Missing-model row gated action
src/platform/missingModel/components/MissingModelRow.vue, src/platform/missingModel/components/MissingModelRow.test.ts, src/locales/en/main.json
Rows prefetch metadata, show gated access controls and tooltips, preserve download actions, and test bridge success, fallback, and rejection behavior.
Missing-model card download integration
src/platform/missingModel/components/MissingModelCard.vue, src/platform/missingModel/components/MissingModelCard.test.ts
Cards display gated-model guidance only for downloadable OSS models and route “Download all” through the shared download composable.
Gated-model browser validation
browser_tests/assets/missing/*, browser_tests/fixtures/selectors.ts, browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts
A gated workflow fixture and Playwright coverage validate gated indicators, download availability, and repository navigation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MissingModelRow
  participant useMissingModelDownload
  participant ComfyDesktop2Bridge
  participant openGatedRepoPage
  MissingModelRow->>useMissingModelDownload: openModelAccessPage(repoUrl)
  useMissingModelDownload->>ComfyDesktop2Bridge: request model access
  ComfyDesktop2Bridge-->>useMissingModelDownload: resolve true, false, or reject
  useMissingModelDownload->>openGatedRepoPage: open repo when bridge does not own request
Loading

Suggested reviewers: austinmroz


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
End-To-End Regression Coverage For Fixes ❓ Inconclusive Commit subjects aren’t available here, so I can’t verify the required bug-fix signal despite the PR adding browser_tests coverage. Please provide the PR title and commit subjects so I can check for bug-fix language and confirm whether the browser_tests coverage satisfies the rule.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Adr Compliance For Entity/Litegraph Changes ✅ Passed No changed files touch src/lib/litegraph/, src/ecs/, or graph entity-related code, so ADR 0003/0008 guidance doesn’t apply.
Title check ✅ Passed The title is concise and accurately captures the main change: gated Hugging Face model access hints.
Description check ✅ Passed The description matches the template well and includes summary, changes, review focus, validation, and screenshots.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jaeone/fe-1173-gated-hf-download-hint-action

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@src/platform/missingModel/components/MissingModelCard.test.ts`:
- Around line 311-327: Update the “Download all” test around MissingModelCard
and the shared missing-model download handler to exercise the real download
composable instead of asserting only mockDownloadModel. Use multiple
downloadable models and verify the observable browser anchor interaction or
Desktop bridge effect, preserving the expected “all models” behavior.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 4d23b4a7-6c94-41b8-9e2d-ea45c9fa25ff

📥 Commits

Reviewing files that changed from the base of the PR and between 3be998a and 9a80859.

📒 Files selected for processing (15)
  • packages/comfyui-desktop-bridge-types/comfyDesktopBridge.d.ts
  • packages/comfyui-desktop-bridge-types/package.json
  • src/locales/en/main.json
  • src/locales/ko/main.json
  • src/platform/missingModel/components/MissingModelCard.test.ts
  • src/platform/missingModel/components/MissingModelCard.vue
  • src/platform/missingModel/components/MissingModelRow.test.ts
  • src/platform/missingModel/components/MissingModelRow.vue
  • src/platform/missingModel/composables/useMissingModelDownload.ts
  • src/platform/missingModel/missingModelDownload.test.ts
  • src/platform/missingModel/missingModelDownload.ts
  • src/platform/missingModel/missingModelPipeline.test.ts
  • src/platform/missingModel/missingModelPipeline.ts
  • src/platform/missingModel/missingModelStore.test.ts
  • src/platform/missingModel/missingModelStore.ts

Comment thread src/platform/missingModel/components/MissingModelCard.test.ts
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
src/platform/missingModel/missingModelDownload.ts 94.73% 2 Missing ⚠️
...atform/missingModel/components/MissingModelRow.vue 96.15% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main   #13742      +/-   ##
==========================================
- Coverage   78.57%   77.75%   -0.83%     
==========================================
  Files        1698     1699       +1     
  Lines      113608    99643   -13965     
  Branches    36753    35368    -1385     
==========================================
- Hits        89269    77477   -11792     
+ Misses      23712    21703    -2009     
+ Partials      627      463     -164     
Flag Coverage Δ
unit 68.11% <96.96%> (+0.04%) ⬆️

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

Files with missing lines Coverage Δ
...tform/missingModel/components/MissingModelCard.vue 90.00% <100.00%> (+8.62%) ⬆️
...issingModel/composables/useMissingModelDownload.ts 100.00% <100.00%> (ø)
src/platform/missingModel/missingModelPipeline.ts 81.45% <100.00%> (-4.88%) ⬇️
src/platform/missingModel/missingModelStore.ts 95.97% <100.00%> (-0.50%) ⬇️
...atform/missingModel/components/MissingModelRow.vue 85.97% <96.15%> (+4.19%) ⬆️
src/platform/missingModel/missingModelDownload.ts 80.00% <94.73%> (-9.93%) ⬇️

... and 283 files with indirect coverage changes

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

@jaeone94
jaeone94 force-pushed the jaeone/fe-1173-gated-hf-download-hint-action branch from 9a80859 to c24ec68 Compare July 17, 2026 09:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@src/locales/en/main.json`:
- Around line 3994-3995: Update the gatedModelsHint and gatedModelTooltip
translations to describe completing the model’s access requirements rather than
always accepting a license agreement, while preserving the sign-in guidance and
distinct hint/tooltip context.

In `@src/platform/missingModel/components/MissingModelCard.test.ts`:
- Around line 19-26: Update the partial mock for the missingModelDownload module
in MissingModelCard tests to also replace fetchModelMetadata with a default
resolved response of { fileSize: null, gatedRepoUrl: null }. Keep the existing
downloadModel mock and all other actual exports unchanged, ensuring
MissingModelRow mounts do not trigger network requests.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: ab2b917e-53e0-4e40-a50c-8c1dbf7f45c1

📥 Commits

Reviewing files that changed from the base of the PR and between 9a80859 and c24ec68.

📒 Files selected for processing (14)
  • packages/comfyui-desktop-bridge-types/comfyDesktopBridge.d.ts
  • packages/comfyui-desktop-bridge-types/package.json
  • src/locales/en/main.json
  • src/platform/missingModel/components/MissingModelCard.test.ts
  • src/platform/missingModel/components/MissingModelCard.vue
  • src/platform/missingModel/components/MissingModelRow.test.ts
  • src/platform/missingModel/components/MissingModelRow.vue
  • src/platform/missingModel/composables/useMissingModelDownload.ts
  • src/platform/missingModel/missingModelDownload.test.ts
  • src/platform/missingModel/missingModelDownload.ts
  • src/platform/missingModel/missingModelPipeline.test.ts
  • src/platform/missingModel/missingModelPipeline.ts
  • src/platform/missingModel/missingModelStore.test.ts
  • src/platform/missingModel/missingModelStore.ts

Comment thread src/locales/en/main.json
Comment thread src/platform/missingModel/components/MissingModelCard.test.ts
The bridge contract resolves `false` when the host declines to open a model
access page, but the caller only branched on the method's presence and handled
rejections, so a declined request left the shield button doing nothing. Await
the result and fall through to the anchor fallback on both `false` and a thrown
error.

Align the card's gated banner with the row's shield action. The banner keyed off
`gatedRepoUrls` alone while the shield also requires `isModelDownloadable`, and
the pipeline populates `gatedRepoUrls` without checking allowed suffixes. A gated
`.bin` or `.gguf` therefore rendered guidance to accept a license with no shield
or download button on any row. Derive the banner from `downloadableModels` so
both conditions share one authority.

Also:
- Use a polite live region for the gated guidance instead of an assertive one.
- Document that `isRemote()` reports the backend server rather than the user's
  location, and why the access-page bridge is used regardless of it.
- Document that the metadata HEAD is deliberately uncredentialed, so a gated
  result cannot be cleared by a later re-check.
- Extract the duplicated anchor-click code behind one helper.
- Drop the unreachable catch in `prefetchModelMetadata`.
- Cover both 404 and 500 in the not-gated test, and drop the `setGatedRepoUrl`
  test that restated its own implementation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@src/platform/missingModel/composables/useMissingModelDownload.ts`:
- Around line 31-34: Update the gated access flow in useMissingModelDownload to
invoke openModelAccessPage directly through window.__comfyDesktop2 instead of
extracting it into openInHost, preserving the bridge object's this context while
retaining the existing await and early-return behavior.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: c5786042-d75c-410d-a380-f5d014f84000

📥 Commits

Reviewing files that changed from the base of the PR and between c24ec68 and 41f35c2.

📒 Files selected for processing (9)
  • packages/comfyui-desktop-bridge-types/comfyDesktopBridge.d.ts
  • src/platform/missingModel/components/MissingModelCard.test.ts
  • src/platform/missingModel/components/MissingModelCard.vue
  • src/platform/missingModel/components/MissingModelRow.test.ts
  • src/platform/missingModel/components/MissingModelRow.vue
  • src/platform/missingModel/composables/useMissingModelDownload.ts
  • src/platform/missingModel/missingModelDownload.test.ts
  • src/platform/missingModel/missingModelDownload.ts
  • src/platform/missingModel/missingModelStore.test.ts
💤 Files with no reviewable changes (1)
  • src/platform/missingModel/missingModelStore.test.ts

Comment thread src/platform/missingModel/composables/useMissingModelDownload.ts Outdated
The gated flow had no coverage above unit level. The classification is driven
purely by a HEAD response status and the shield, banner, and fallback are
ordinary DOM, so the parts this feature adds are reachable from Playwright even
though a real access grant is not.

Add three tests to the OSS missing-models spec:
- a 403 on the metadata HEAD classifies the model as gated and renders both the
  shield action and the card banner;
- Download stays visible and enabled on a gated row, alongside the shield;
- clicking the shield with no Desktop bridge opens the derived repository URL
  through the anchor fallback.

The stubbed 403 carries `Access-Control-Allow-Origin` because the HEAD is
cross-origin: without it the browser rejects the response, the classification
never runs, and the tests would pass or fail for reasons unrelated to the code
under test. Routing is registered on the browser context rather than the page so
the fallback popup's navigation is stubbed too.

Add a workflow fixture with a HuggingFace URL, since no existing one has a
download source that survives `isModelDownloadable()`, and register the two new
test IDs in the shared selectors.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts`:
- Line 223: Replace the synchronous accessPage.url() equality assertion with
Playwright’s retrying await expect(accessPage).toHaveURL(...) assertion,
preserving GATED_MODEL_REPO_URL as the expected destination.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: fb1303ff-ef3f-42d5-9baa-2872085b2d10

📥 Commits

Reviewing files that changed from the base of the PR and between 41f35c2 and 5a9db5c.

📒 Files selected for processing (3)
  • browser_tests/assets/missing/missing_models_gated.json
  • browser_tests/fixtures/selectors.ts
  • browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts

Comment thread browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts Outdated
jaeone94 added 2 commits July 17, 2026 20:40
The card's gated banner is derived from downloadable models, while the pipeline
records gated repository URLs for any candidate that has a url and a directory.
Nothing above unit level pinned that difference, so a regression would restore
guidance to accept a license on rows that offer no way to act on it.

Add an OSS case for a gated model whose extension is excluded from download:
the row is listed as missing, the HuggingFace HEAD is still answered with 403,
and neither the banner, the shield, nor Download appears. Waiting on the HEAD
count before the absence assertions keeps them from passing merely because
classification had not run yet.
The comment defending the bridge call justified skipping the isRemote() check
by claiming the host session is reused by downloads. It is not: downloadModel
only takes the bridge when isRemote() is false, so the rationale was false in
the one branch it existed to defend. Skipping the check is still right, for a
different reason, and the comment now gives that reason.

The exclusion test polled a counter incremented when the route handler was
entered, which is before the response is sent, before the store is written, and
before a render. It caught the regression only because an earlier assertion
happened to leave enough time. Serve the gated response first and gate a
downloadable companion's metadata behind it, so a rendered size proves the
gated response was already consumed before the absence assertions run.

Await the popup URL instead of reading it off a page that may not have
navigated yet, and require an explicit true from the bridge rather than any
truthy value. Document what false obligates the caller to do, since the
contract only described the accepting case.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@jaeone94

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@src/platform/missingModel/composables/useMissingModelDownload.ts`:
- Around line 32-36: Update openModelAccessPage to invoke
window.__comfyDesktop2.openModelAccessPage directly rather than storing it in
openInHost, preserving the bridge object's this context while keeping the
existing result check and fallback flow unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 11a5ccdb-470a-46b5-a5f5-cd455b03d8b8

📥 Commits

Reviewing files that changed from the base of the PR and between 5a9db5c and 9e502a9.

📒 Files selected for processing (4)
  • browser_tests/assets/missing/missing_models_gated_unsupported.json
  • browser_tests/tests/propertiesPanel/errorsTabMissingModels.spec.ts
  • packages/comfyui-desktop-bridge-types/comfyDesktopBridge.d.ts
  • src/platform/missingModel/composables/useMissingModelDownload.ts

Comment thread src/platform/missingModel/composables/useMissingModelDownload.ts Outdated
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Request timed out after 900000ms (requestId=637e9953-bd70-4d79-ab9b-4805d478bbb1)

jaeone94 added 3 commits July 18, 2026 00:45
Extracting openModelAccessPage into a local detached it from its receiver, so a
host that reads this on the method would throw and the flow would fall to the
system-browser anchor, stranding provider cookies. Call it on the bridge object.

The Download all test drove a single downloadable model, so it could not tell
"downloads all" from "downloads the first". Drive two models and assert each is
dispatched once with its own arguments; reducing the component to the first
model now fails the call count.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 17, 2026
@jaeone94
jaeone94 requested a review from christian-byrne July 18, 2026 02:55
@jaeone94 jaeone94 added the needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch label Jul 18, 2026
@jaeone94 jaeone94 added core/1.47 Backport PRs for core 1.47 cloud/1.47 Backport PRs for cloud 1.47 labels Jul 18, 2026
size="sm"
class="shrink-0 focus-visible:ring-inset"
:aria-label="`${t('g.download')} ${model.name}`"
:title="gatedModelTooltip"

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.

test

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Could you clarify what this comment was pointing to? Component-level coverage already existed. If you meant that the composable itself was missing direct unit tests, I addressed that in 7caff34 by adding useMissingModelDownload.test.ts with coverage for the metadata guard, download dispatch, Desktop bridge behavior, browser fallback, and URL validation.

GATED_STATUS_CODES.has(response.status) &&
response.headers.get('x-error-code') === HUGGING_FACE_GATED_ERROR_CODE
) {
return { fileSize: null, gatedRepoUrl: downloadUrlToHfRepoUrl(url) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: The isComplete function below returns false when a URL fetches successfully but omits content-length -- producing { fileSize: null, gatedRepoUrl: null } which is never cached. fetchModelMetadata will re-fire a HEAD request on every call for these URLs (e.g. HF URLs that return 200 with no size header). Same root affects prefetchModelMetadata in the composable: the guard store.fileSizes[url] !== undefined || store.gatedRepoUrls[url] fails to skip for non-gated URLs with no content-length, so onMounted refetches on every component mount. Consider caching all results unconditionally, or adding a fetched: true sentinel field to ModelMetadata to distinguish "fetched but unknown" from "not fetched".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 7caff34. fetchHeadMetadata now returns metadata together with an explicit cacheability outcome. A successful HEAD response without content-length is cached, while thrown/network failures and non-OK responses remain uncached so they can be retried. The existing Civitai path is intentionally unchanged. I added regression coverage for successful empty-size caching and failed/non-OK retries.

size="sm"
class="shrink-0 focus-visible:ring-inset"
:aria-label="`${t('g.download')} ${model.name}`"
:title="gatedModelTooltip"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: (non-blocking) When a model is gated, the Download button receives gatedModelTooltip ("This model is gated and requires you to be logged in...") as its :title. Since Download is still functional, this is contradictory -- the tooltip implies access is blocked while the button implies it is available. Consider removing gatedModelTooltip from the Download button's :title, or replacing it with a softer hint like "Download may require signing in to Hugging Face first".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated in 7caff34. The Download button now uses the softer i18n-backed hint, “Download may require signing in to Hugging Face first.” The stronger gated/license message remains on the dedicated lock action.

}

function openUrlInNewTab(url: string, downloadAs?: string): void {
const link = document.createElement('a')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: openUrlInNewTab sets link.href = url and calls link.click() with no scheme validation. Browsers execute javascript: URIs assigned to anchor .href when clicked programmatically. The current callers only pass https://huggingface.co/... URLs derived from downloadUrlToHfRepoUrl, so the path is safe today. But the function has no defense in depth -- a future caller or the catch { return url } fallback in downloadUrlToHfRepoUrl could introduce a javascript: URI. Suggest validating the scheme before navigation: if (new URL(url).protocol !== 'https:') return.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 7caff34 at the gated-navigation boundary. openGatedRepoPage now rejects any URL whose parsed origin is not exactly https://huggingface.co, with tests covering javascript: and non-Hugging-Face URLs. I kept the generic openUrlInNewTab helper unchanged because it also serves ordinary model download URLs; the stricter trust rule belongs to the gated repository action.

const bridge = window.__comfyDesktop2
if (bridge?.openModelAccessPage) {
try {
if ((await bridge.openModelAccessPage(repoUrl)) === true) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: bridge.openModelAccessPage(repoUrl) passes the URL to Electron IPC without origin validation. If the Desktop-side implementation calls shell.openExternal(), a file:// or javascript: URI could access local files or execute code on the host. The renderer should validate the origin before delegating: if (!repoUrl.startsWith('https://huggingface.co/')) { openGatedRepoPage(repoUrl); return } (or reject entirely). Renderer-side validation should not rely on the host to sanitize.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 7caff34. The composable now validates the parsed URL origin with isTrustedHuggingFaceUrl before invoking either the Desktop bridge or the browser fallback. Only the exact https://huggingface.co origin is accepted; lookalike hosts, alternate schemes, and alternate ports are rejected. The Desktop side still performs its own validation as defense in depth.

<div
v-if="showGatedModelsHint"
data-testid="missing-model-gated-hint"
role="status"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: role="status" is an ARIA live region (aria-live="polite"). On a banner rendered as static content when the component mounts, this causes screen readers to announce the gated-hint text as a dynamic status update on every render or re-render -- interrupting the user's current focus unexpectedly. For a persistent informational message, role="note" (no live behavior) or no role is appropriate. role="status" should be reserved for content that genuinely changes in response to user action. WCAG 4.1.3.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 7caff34. The persistent banner now uses role="note", avoiding repeated polite live-region announcements when the Errors tab remounts. The component test was updated accordingly.

variant="secondary"
size="sm"
class="shrink-0 focus-visible:ring-inset"
:aria-label="`${t('g.download')} ${model.name}`"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: (non-blocking) When a model is gated, the Download button's aria-label is still just "Download [model name]" -- keyboard-only users (who don't receive title attribute values) get no indication that sign-in is required before the download will succeed. Consider adding context to the aria-label for gated models, e.g. Download ${model.name} (login required), or pointing to the hint banner via aria-describedby. WCAG 2.4.6.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 7caff34 using aria-describedby. The Download button keeps the concise accessible name Download [model name] and references a unique hidden description containing the sign-in hint when the model is gated. This preserves a clear command name while supplying the gated context to assistive technology.

<i
aria-hidden="true"
class="mt-0.5 icon-[lucide--lock] size-4 shrink-0 text-warning-background"
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: (non-blocking) The gated-hint banner conveys its warning state through color alone (text-warning-background, border-warning-background). Users with color vision deficiencies may not distinguish it from ordinary informational content. A short text prefix ("Note:" or similar) or a non-color visual indicator alongside the lock icon would satisfy WCAG 1.4.1 Use of Color.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The banner already has two non-color indicators: a visible lock icon and explicit text stating that some models are gated and require Hugging Face sign-in/license acceptance. Color is only supplemental styling here, so I kept the current copy rather than adding a generic “Note:” prefix.


it('returns gatedRepoUrl for gated HuggingFace HEAD requests (403)', async () => {
fetchMock.mockResolvedValueOnce({ ok: false, status: 403 })
it.for([401, 403, 451])(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: (non-blocking) metadataCache is a module-level Map in missingModelDownload.ts. vi.resetAllMocks() in beforeEach resets mock functions but does not re-import modules or clear module state, so the cache persists across tests. The testId counter prevents same-URL collisions within this file, but any future test using a hardcoded URL could read stale cached state. Consider exporting a clearMetadataCache() test hook, or using vi.resetModules() + dynamic import in beforeEach to guarantee isolation.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I kept the module cache encapsulated rather than adding a production export solely for tests or converting the suite to dynamic imports. The existing testId strategy gives every cache-sensitive test a unique URL, and the new successful-empty and retry tests follow that same isolation rule. I agree this would need revisiting if future tests start sharing fixed cache keys.

import type { ModelWithUrl } from '@/platform/missingModel/missingModelDownload'
import { useMissingModelStore } from '@/platform/missingModel/missingModelStore'

export function useMissingModelDownload() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: (non-blocking) useMissingModelDownload.ts is a new file with three functions but no unit test file. The openModelAccessPage bridge/fallback paths are tested via component integration tests, but composable-level behavior -- especially the prefetchModelMetadata guard when gatedRepoUrls[url] is already set, and the openModelAccessPage fallback when the bridge returns false -- is not covered at the unit level. Worth adding a useMissingModelDownload.test.ts alongside the other composable tests.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added direct composable coverage in 7caff34. The new useMissingModelDownload.test.ts covers file and gated metadata storage, the existing-store guard, download dispatch, Desktop bridge receiver/URL handling, remote Desktop behavior, bridge false and rejection fallbacks, and untrusted URL rejection. The component tests now retain only the UI wiring responsibility.

/** Opens a model provider access page in the hosted frontend's browser session.
* Resolves `true` when the host has taken ownership of the request.
* On `false` or rejection the frontend falls back to opening a new tab. */
openModelAccessPage?: (url: string) => Promise<boolean>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: (non-blocking) The downloadUrlToHfRepoUrl utility used to derive gatedRepoUrl has a catch block that returns the raw url argument unchanged on parse failure. Since isHuggingFaceRepoUrl uses new URL() too and would fail on the same malformed input, this path is unreachable via fetchHeadMetadata. But the function's own contract is broken: it claims to return an HF repo URL but can return anything. A safer fallback would be return 'https://huggingface.co/' so future callers that skip the hostname check don't receive arbitrary values.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I left the raw fallback unchanged because it is a pre-existing utility-contract issue and remains unreachable from the gated flow. In 7caff34, both the composable and openGatedRepoPage independently require the exact https://huggingface.co origin, so a malformed/raw fallback cannot reach browser navigation or Electron IPC. I would prefer to clean up the broader conversion utility contract in a focused follow-up if it gains additional callers.

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

Labels

cloud/1.47 Backport PRs for cloud 1.47 core/1.47 Backport PRs for core 1.47 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants