Skip to content

feat: add cloud gtm injection#8311

Merged
benceruleanlu merged 18 commits intomainfrom
feat/cloud-gtm
Jan 27, 2026
Merged

feat: add cloud gtm injection#8311
benceruleanlu merged 18 commits intomainfrom
feat/cloud-gtm

Conversation

@benceruleanlu
Copy link
Member

@benceruleanlu benceruleanlu commented Jan 25, 2026

Summary

Add GTM injection for cloud distribution builds and push SPA page view + signup events.

Changes

  • What: Inject GTM script into head-prepend and noscript iframe into body-prepend for cloud builds
  • What: Push page_view to dataLayer on cloud route changes (page_location + page_title)
  • What: Push sign_up to dataLayer after successful account creation (email/google/github)
  • Dependencies: None

Review Focus

  • Placement order for head-prepend/body-prepend and cloud-only gating
  • Route-change page_view payload shape
  • Signup event emission only for new users

Screenshots (if applicable)

Screenshot 2026-01-26 at 11 38 11 AM Screenshot 2026-01-26 at 11 38 26 AM

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 25, 2026

Warning

Rate limit exceeded

@benceruleanlu has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 41 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This pull request integrates Google Tag Manager (GTM) for analytics tracking across the application. It adds GTM initialization, dataLayer window property, and implements event tracking for page views, user sign-ups, and subscription purchases through both GTM and localStorage-based purchase tracking.

Changes

Cohort / File(s) Summary
GTM Core Infrastructure
global.d.ts, src/platform/telemetry/gtm.ts, src/main.ts
Declares optional dataLayer property on Window interface; introduces new GTM module with initGtm() and pushDataLayerEvent() functions; dynamically loads and initializes GTM module during cloud build startup
Page and Authentication Analytics
src/router.ts, src/stores/firebaseAuthStore.ts
Router tracks page views via pushPageView() after navigation; Firebase auth store tracks sign-ups with trackSignUp() helper that includes optional SHA-256 hashed user IDs; both gated by cloud environment checks
Subscription Purchase Tracking Utilities
src/platform/cloud/subscription/utils/subscriptionPurchaseTracker.ts, src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts
New module exports functions to manage pending subscription purchases in localStorage (startSubscriptionPurchaseTracking, getPendingSubscriptionPurchase, clearPendingSubscriptionPurchase); checkout util initiates purchase tracking when checkout URL is obtained
Subscription Analytics Integration
src/platform/cloud/subscription/composables/useSubscription.ts, src/platform/cloud/subscription/composables/useSubscription.test.ts
Composable now tracks subscription purchases by reading pending purchase data, computing pricing, and emitting GTM purchase events on active subscription; test validates purchase event emission and localStorage cleanup after subscription completion

Sequence Diagrams

sequenceDiagram
    participant Browser
    participant App
    participant GTM as GTM Module
    participant DataLayer
    participant GTMScript as GTM Script

    App->>GTM: initGtm()
    GTM->>DataLayer: Create dataLayer[]
    GTM->>Browser: Inject GTM script tag
    Browser->>GTMScript: Load script
    GTMScript->>DataLayer: Initialize GTM
    GTMScript-->>GTM: Load complete
    GTM-->>App: Promise resolved
Loading
sequenceDiagram
    participant Browser as Browser/User
    participant App as App Logic
    participant Router as Router
    participant DataLayer as GTM DataLayer
    participant SubCheckout as Subscription Checkout
    participant Storage as localStorage
    participant SubStatus as Subscription Status

    Browser->>Router: Navigate
    Router->>Router: pushPageView()
    Router->>DataLayer: page_view event
    
    Browser->>SubCheckout: Initiate checkout
    SubCheckout->>SubCheckout: Get checkout URL
    SubCheckout->>Storage: startSubscriptionPurchaseTracking()
    Storage-->>Storage: Store pending purchase
    SubCheckout->>Browser: Open checkout URL
    
    Browser->>SubStatus: Subscription completes (later)
    SubStatus->>Storage: getPendingSubscriptionPurchase()
    Storage-->>SubStatus: Return pending data
    SubStatus->>DataLayer: purchase event (with price/tier)
    SubStatus->>Storage: clearPendingSubscriptionPurchase()
Loading

Suggested reviewers

  • shinshin86
  • KarryCharon
  • Yorha4D

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

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Jan 25, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/27/2026, 08:26:39 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 25, 2026

🎭 Playwright Tests: ⚠️ Passed with flaky tests

Results: 505 passed, 0 failed, 1 flaky, 8 skipped (Total: 514)

❌ Failed Tests

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

@github-actions
Copy link

github-actions bot commented Jan 25, 2026

Bundle Size Report

Summary

  • Raw size: 22 MB baseline 22 MB — 🔴 +7.08 kB
  • Gzip: 4.59 MB baseline 4.59 MB — 🔴 +2.49 kB
  • Brotli: 3.41 MB baseline 3.41 MB — 🔴 +2.13 kB
  • Bundles: 173 current • 170 baseline • 171 added / 168 removed

Category Glance
Data & Services 🔴 +3.4 kB (3.2 MB) · Other 🔴 +2.43 kB (6.55 MB) · App Entry Points 🔴 +726 B (23.5 kB) · Utilities & Hooks 🔴 +265 B (25.4 kB) · Graph Workspace 🔴 +103 B (960 kB) · Panels & Settings 🔴 +72 B (470 kB) · + 5 more

Per-category breakdown
App Entry Points — 23.5 kB (baseline 22.8 kB) • 🔴 +726 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-DJSyku05.js (new) 23.5 kB 🔴 +23.5 kB 🔴 +7.06 kB 🔴 +6.21 kB
assets/index-Dhd3nWUb.js (removed) 22.8 kB 🟢 -22.8 kB 🟢 -6.84 kB 🟢 -6.05 kB

Status: 1 added / 1 removed

Graph Workspace — 960 kB (baseline 960 kB) • 🔴 +103 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-Bh054fGM.js (new) 960 kB 🔴 +960 kB 🔴 +194 kB 🔴 +147 kB
assets/GraphView-wOADjl4d.js (removed) 960 kB 🟢 -960 kB 🟢 -194 kB 🟢 -147 kB

Status: 1 added / 1 removed

Views & Navigation — 80.7 kB (baseline 80.7 kB) • 🔴 +36 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-CVrPw1S1.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -3.6 kB 🟢 -3.05 kB
assets/CloudSurveyView-DW0fzVi0.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +3.6 kB 🔴 +3.05 kB
assets/CloudLoginView-CET3vsoy.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.09 kB 🟢 -2.72 kB
assets/CloudLoginView-DaLrY4dh.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.09 kB 🔴 +2.72 kB
assets/UserCheckView-BECRVTW9.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.45 kB 🔴 +2.13 kB
assets/UserCheckView-C_KfBeHX.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.45 kB 🟢 -2.13 kB
assets/CloudLayoutView-DUPPDKpg.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.25 kB 🟢 -1.95 kB
assets/CloudLayoutView-wy6xfd5A.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.25 kB 🔴 +1.96 kB
assets/CloudSignupView-ytFlt2-X.js (new) 8.22 kB 🔴 +8.22 kB 🔴 +2.34 kB 🔴 +2.04 kB
assets/CloudSignupView-Bv6K0OgK.js (removed) 8.18 kB 🟢 -8.18 kB 🟢 -2.33 kB 🟢 -2.03 kB
assets/CloudForgotPasswordView-CDSP2j1C.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +1.93 kB 🔴 +1.68 kB
assets/CloudForgotPasswordView-DQ-zCm2W.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -1.92 kB 🟢 -1.69 kB
assets/UserSelectView-CgHCIHED.js (removed) 5.28 kB 🟢 -5.28 kB 🟢 -1.76 kB 🟢 -1.57 kB
assets/UserSelectView-DfCoqMKd.js (new) 5.28 kB 🔴 +5.28 kB 🔴 +1.76 kB 🔴 +1.57 kB
assets/CloudSubscriptionRedirectView-8byNfgSB.js (new) 5.27 kB 🔴 +5.27 kB 🔴 +1.73 kB 🔴 +1.54 kB
assets/CloudSubscriptionRedirectView-BEvnC_sa.js (removed) 5.27 kB 🟢 -5.27 kB 🟢 -1.73 kB 🟢 -1.53 kB
assets/CloudAuthTimeoutView-B2ZGpcRj.js (removed) 5.24 kB 🟢 -5.24 kB 🟢 -1.7 kB 🟢 -1.48 kB
assets/CloudAuthTimeoutView-DPULfVQt.js (new) 5.24 kB 🔴 +5.24 kB 🔴 +1.71 kB 🔴 +1.48 kB
assets/CloudSorryContactSupportView-B5gYo5Lv.js (removed) 1.97 kB 🟢 -1.97 kB 🟢 -702 B 🟢 -625 B
assets/CloudSorryContactSupportView-DfNZyQFB.js (new) 1.97 kB 🔴 +1.97 kB 🔴 +702 B 🔴 +626 B
assets/layout-BJqHw2ua.js (new) 500 B 🔴 +500 B 🔴 +308 B 🔴 +265 B
assets/layout-MvRmLoAK.js (removed) 500 B 🟢 -500 B 🟢 -309 B 🟢 -266 B

Status: 11 added / 11 removed

Panels & Settings — 470 kB (baseline 470 kB) • 🔴 +72 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/settings-B0OU6-pB.js (removed) 39.4 kB 🟢 -39.4 kB 🟢 -9.43 kB 🟢 -7.5 kB
assets/settings-DaS3cSXp.js (new) 39.4 kB 🔴 +39.4 kB 🔴 +9.43 kB 🔴 +7.5 kB
assets/settings-CaVpP3Yk.js (removed) 35.2 kB 🟢 -35.2 kB 🟢 -8.11 kB 🟢 -6.8 kB
assets/settings-DaK-NByz.js (new) 35.2 kB 🔴 +35.2 kB 🔴 +8.11 kB 🔴 +6.79 kB
assets/settings-2UNjEj6k.js (new) 32.9 kB 🔴 +32.9 kB 🔴 +7.85 kB 🔴 +6.37 kB
assets/settings-BG8Zhz_I.js (removed) 32.9 kB 🟢 -32.9 kB 🟢 -7.85 kB 🟢 -6.38 kB
assets/settings-DWbMuaAa.js (new) 32 kB 🔴 +32 kB 🔴 +8.25 kB 🔴 +6.86 kB
assets/settings-DWJH5Rx7.js (removed) 32 kB 🟢 -32 kB 🟢 -8.25 kB 🟢 -6.86 kB
assets/settings-B2OMGvh7.js (new) 31.2 kB 🔴 +31.2 kB 🔴 +7.94 kB 🔴 +6.93 kB
assets/settings-DQtYvoMH.js (removed) 31.2 kB 🟢 -31.2 kB 🟢 -7.94 kB 🟢 -6.91 kB
assets/settings-CT-O-PKz.js (removed) 30.4 kB 🟢 -30.4 kB 🟢 -7.79 kB 🟢 -6.79 kB
assets/settings-S7pA60Hj.js (new) 30.4 kB 🔴 +30.4 kB 🔴 +7.79 kB 🔴 +6.79 kB
assets/settings-Bh7k-ZzJ.js (removed) 30.2 kB 🟢 -30.2 kB 🟢 -7.67 kB 🟢 -6.5 kB
assets/settings-CZ62uO3e.js (new) 30.2 kB 🔴 +30.2 kB 🔴 +7.67 kB 🔴 +6.5 kB
assets/WorkspacePanel-1r40jvmd.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +5.89 kB 🔴 +5.13 kB
assets/WorkspacePanel-5NI2yOsz.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -5.89 kB 🟢 -5.14 kB
assets/settings-BcujOfpn.js (new) 29.6 kB 🔴 +29.6 kB 🔴 +7.53 kB 🔴 +6.49 kB
assets/settings-jYq3vZ8H.js (removed) 29.6 kB 🟢 -29.6 kB 🟢 -7.53 kB 🟢 -6.49 kB
assets/settings-BI09_t23.js (new) 29.4 kB 🔴 +29.4 kB 🔴 +7.89 kB 🔴 +6.52 kB
assets/settings-DrFp_k1j.js (removed) 29.4 kB 🟢 -29.4 kB 🟢 -7.89 kB 🟢 -6.52 kB
assets/settings-BlTun9tZ.js (new) 26.4 kB 🔴 +26.4 kB 🔴 +7.78 kB 🔴 +6.27 kB
assets/settings-HIJPcXNb.js (removed) 26.4 kB 🟢 -26.4 kB 🟢 -7.78 kB 🟢 -6.26 kB
assets/settings-BKamuseh.js (new) 25.8 kB 🔴 +25.8 kB 🔴 +7.56 kB 🔴 +5.91 kB
assets/settings-C_BaoiA5.js (removed) 25.8 kB 🟢 -25.8 kB 🟢 -7.56 kB 🟢 -5.9 kB
assets/LegacyCreditsPanel-BX_hmH4x.js (removed) 23.8 kB 🟢 -23.8 kB 🟢 -5.95 kB 🟢 -5.22 kB
assets/LegacyCreditsPanel-Cf3kWqiG.js (new) 23.8 kB 🔴 +23.8 kB 🔴 +5.94 kB 🔴 +5.22 kB
assets/SubscriptionPanel-p9WOWmpk.js (new) 21 kB 🔴 +21 kB 🔴 +5.04 kB 🔴 +4.44 kB
assets/SubscriptionPanel-Bmn2zc8h.js (removed) 20.9 kB 🟢 -20.9 kB 🟢 -5.01 kB 🟢 -4.43 kB
assets/KeybindingPanel-DxivXzm3.js (removed) 14.2 kB 🟢 -14.2 kB 🟢 -3.74 kB 🟢 -3.31 kB
assets/KeybindingPanel-nsOLNQ-v.js (new) 14.2 kB 🔴 +14.2 kB 🔴 +3.74 kB 🔴 +3.31 kB
assets/AboutPanel-C4VKMANe.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.68 kB 🟢 -2.45 kB
assets/AboutPanel-DxvbIOLZ.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.68 kB 🔴 +2.42 kB
assets/ExtensionPanel-B60HlIUO.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.71 kB 🟢 -2.4 kB
assets/ExtensionPanel-DZ88xwGD.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.71 kB 🔴 +2.4 kB
assets/ServerConfigPanel-DLV0wsM-.js (new) 7.26 kB 🔴 +7.26 kB 🔴 +2.18 kB 🔴 +1.95 kB
assets/ServerConfigPanel-D0nM3Lrk.js (removed) 7.23 kB 🟢 -7.23 kB 🟢 -2.17 kB 🟢 -1.94 kB
assets/UserPanel-CzCHr8zf.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +1.9 kB 🔴 +1.67 kB
assets/UserPanel-DmmND9J8.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/config-DCS8f76F.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -610 B 🟢 -533 B
assets/config-DYVzoBHe.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +600 B 🔴 +528 B
assets/refreshRemoteConfig-DtjUOQJo.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +523 B 🔴 +464 B
assets/refreshRemoteConfig-yFdHl0oK.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -521 B 🟢 -453 B
assets/cloudRemoteConfig-C7P9Vu51.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +509 B 🔴 +443 B
assets/cloudRemoteConfig-tIIBFP7M.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -508 B 🟢 -434 B
assets/remoteConfig-DlUK-xIk.js (new) 536 B 🔴 +536 B 🔴 +313 B 🔴 +278 B
assets/remoteConfig-w1E3DdAQ.js (removed) 536 B 🟢 -536 B 🟢 -314 B 🟢 -278 B
assets/refreshRemoteConfig-C_tOnvwT.js (new) 169 B 🔴 +169 B 🔴 +108 B 🔴 +105 B
assets/refreshRemoteConfig-qt8lWdXr.js (removed) 169 B 🟢 -169 B 🟢 -108 B 🟢 -104 B

Status: 24 added / 24 removed

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-CVaNkXA1.js (new) 3.54 kB 🔴 +3.54 kB 🔴 +1.24 kB 🔴 +1.06 kB
assets/auth-DGPzQ3qM.js (removed) 3.54 kB 🟢 -3.54 kB 🟢 -1.24 kB 🟢 -1.06 kB
assets/firebaseAuthStore-C_I2ZZHn.js (removed) 217 B 🟢 -217 B 🟢 -136 B 🟢 -116 B
assets/firebaseAuthStore-D2x1jJYN.js (new) 217 B 🔴 +217 B 🔴 +136 B 🔴 +116 B
assets/auth-C1OM1spT.js (removed) 178 B 🟢 -178 B 🟢 -142 B 🟢 -127 B
assets/auth-FYFDT3kx.js (new) 178 B 🔴 +178 B 🔴 +142 B 🔴 +142 B

Status: 3 added / 3 removed

Editors & Dialogs — 2.88 kB (baseline 2.83 kB) • 🔴 +48 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-BQ2OFvml.js (new) 2.7 kB 🔴 +2.7 kB 🔴 +1.28 kB 🔴 +1.13 kB
assets/useSubscriptionDialog-BWY8z7xL.js (removed) 2.65 kB 🟢 -2.65 kB 🟢 -1.25 kB 🟢 -1.11 kB
assets/useSubscriptionDialog-Bhocs0Th.js (new) 179 B 🔴 +179 B 🔴 +110 B 🔴 +103 B
assets/useSubscriptionDialog-eTAB4-ih.js (removed) 179 B 🟢 -179 B 🟢 -110 B 🟢 -97 B

Status: 2 added / 2 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-BI0ZKXy2.js (new) 9.52 kB 🔴 +9.52 kB 🔴 +2.69 kB 🔴 +2.42 kB
assets/ComfyQueueButton-CZ-EZI_2.js (removed) 9.52 kB 🟢 -9.52 kB 🟢 -2.69 kB 🟢 -2.43 kB
assets/TopbarBadge-DHZYSmi1.js (removed) 8.36 kB 🟢 -8.36 kB 🟢 -1.98 kB 🟢 -1.77 kB
assets/TopbarBadge-p_Pm0np-.js (new) 8.36 kB 🔴 +8.36 kB 🔴 +1.97 kB 🔴 +1.75 kB
assets/SubscribeButton-BXhPgXdP.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.56 kB 🔴 +1.39 kB
assets/SubscribeButton-CplyUOkr.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.57 kB 🟢 -1.39 kB
assets/Button-B9mYP1x0.js (removed) 3.82 kB 🟢 -3.82 kB 🟢 -1.39 kB 🟢 -1.23 kB
assets/Button-gARAYOLj.js (new) 3.82 kB 🔴 +3.82 kB 🔴 +1.39 kB 🔴 +1.23 kB
assets/WidgetButton-BFtcBv-z.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -980 B 🟢 -885 B
assets/WidgetButton-DNeeragT.js (new) 2.41 kB 🔴 +2.41 kB 🔴 +980 B 🔴 +894 B
assets/CloudBadge-DW4VhKa7.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +721 B 🔴 +642 B
assets/CloudBadge-DWHNRPHI.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -723 B 🟢 -648 B
assets/UserAvatar-CIuPULbC.js (removed) 1.73 kB 🟢 -1.73 kB 🟢 -726 B 🟢 -637 B
assets/UserAvatar-CndbQENT.js (new) 1.73 kB 🔴 +1.73 kB 🔴 +725 B 🔴 +635 B
assets/cloudFeedbackTopbarButton-B8M_wweO.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -675 B 🟢 -578 B
assets/cloudFeedbackTopbarButton-C1cTrgYB.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +677 B 🔴 +577 B
assets/ComfyQueueButton-DEu6ovjP.js (removed) 181 B 🟢 -181 B 🟢 -118 B 🟢 -112 B
assets/ComfyQueueButton-DNffLGqr.js (new) 181 B 🔴 +181 B 🔴 +118 B 🔴 +122 B

Status: 9 added / 9 removed

Data & Services — 3.2 MB (baseline 3.19 MB) • 🔴 +3.4 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-Dd6A9aW0.js (new) 2.01 MB 🔴 +2.01 MB 🔴 +424 kB 🔴 +324 kB
assets/dialogService-Cv-Ao3ol.js (removed) 2 MB 🟢 -2 MB 🟢 -423 kB 🟢 -323 kB
assets/api-C4kluqLB.js (removed) 1.17 MB 🟢 -1.17 MB 🟢 -244 kB 🟢 -189 kB
assets/api-DeTdLqJH.js (new) 1.17 MB 🔴 +1.17 MB 🔴 +243 kB 🔴 +189 kB
assets/releaseStore-DCnknBja.js (new) 8.94 kB 🔴 +8.94 kB 🔴 +2.41 kB 🔴 +2.13 kB
assets/releaseStore-CAau93PM.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.4 kB 🟢 -2.12 kB
assets/keybindingService-DPXYWrxF.js (new) 6.78 kB 🔴 +6.78 kB 🔴 +1.74 kB 🔴 +1.52 kB
assets/keybindingService-hlELq80E.js (removed) 6.78 kB 🟢 -6.78 kB 🟢 -1.74 kB 🟢 -1.52 kB
assets/serverConfigStore-B2LzN8g1.js (removed) 2.64 kB 🟢 -2.64 kB 🟢 -879 B 🟢 -778 B
assets/serverConfigStore-rKMIA1W-.js (new) 2.64 kB 🔴 +2.64 kB 🔴 +877 B 🔴 +778 B
assets/userStore-BPZD4dCU.js (removed) 2.16 kB 🟢 -2.16 kB 🟢 -814 B 🟢 -724 B
assets/userStore-D4u4OPoL.js (new) 2.16 kB 🔴 +2.16 kB 🔴 +812 B 🔴 +726 B
assets/audioService-C2dTJzkh.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -931 B 🟢 -822 B
assets/audioService-CJ_L5emh.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +932 B 🔴 +822 B
assets/teamWorkspaceStore-CglBEYtV.js (new) 165 B 🔴 +165 B 🔴 +123 B 🔴 +109 B
assets/teamWorkspaceStore-CPW-4azI.js (removed) 165 B 🟢 -165 B 🟢 -123 B 🟢 -108 B
assets/releaseStore-BGdu5M4S.js (new) 140 B 🔴 +140 B 🔴 +106 B 🔴 +107 B
assets/releaseStore-CIvrjVhW.js (removed) 140 B 🟢 -140 B 🟢 -106 B 🟢 -106 B

Status: 9 added / 9 removed

Utilities & Hooks — 25.4 kB (baseline 25.2 kB) • 🔴 +265 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/colorUtil-8brfHtOx.js (removed) 7.2 kB 🟢 -7.2 kB 🟢 -2.22 kB 🟢 -1.96 kB
assets/colorUtil-CXqecD32.js (new) 7.2 kB 🔴 +7.2 kB 🔴 +2.22 kB 🔴 +1.95 kB
assets/useErrorHandling-B9iNRsI-.js (new) 5.11 kB 🔴 +5.11 kB 🔴 +1.51 kB 🔴 +1.32 kB
assets/useErrorHandling-CHz5Ype7.js (removed) 5.08 kB 🟢 -5.08 kB 🟢 -1.5 kB 🟢 -1.3 kB
assets/useWorkspaceUI-BIMxGArc.js (new) 3.42 kB 🔴 +3.42 kB 🔴 +974 B 🔴 +833 B
assets/useWorkspaceUI-D-BA4WXF.js (removed) 3.42 kB 🟢 -3.42 kB 🟢 -974 B 🟢 -843 B
assets/subscriptionCheckoutUtil-DKm4mS8F.js (new) 2.22 kB 🔴 +2.22 kB 🔴 +914 B 🔴 +808 B
assets/useSubscriptionActions-Dw9dBXqr.js (removed) 2.22 kB 🟢 -2.22 kB 🟢 -868 B 🟢 -760 B
assets/useSubscriptionActions-wB0aHGt0.js (new) 2.22 kB 🔴 +2.22 kB 🔴 +866 B 🔴 +758 B
assets/subscriptionCheckoutUtil-gUv5S-SX.js (removed) 2 kB 🟢 -2 kB 🟢 -862 B 🟢 -751 B
assets/markdownRendererUtil-BiHkcU4x.js (new) 1.78 kB 🔴 +1.78 kB 🔴 +886 B 🔴 +774 B
assets/markdownRendererUtil-DBMaRy6q.js (removed) 1.78 kB 🟢 -1.78 kB 🟢 -886 B 🟢 -773 B
assets/useSubscriptionCredits-B9k6Yh9K.js (new) 1.39 kB 🔴 +1.39 kB 🔴 +600 B 🔴 +551 B
assets/useSubscriptionCredits-Bb2RN7mh.js (removed) 1.39 kB 🟢 -1.39 kB 🟢 -597 B 🟢 -530 B
assets/audioUtils-CEaPXanL.js (removed) 970 B 🟢 -970 B 🟢 -546 B 🟢 -457 B
assets/audioUtils-TpRYeAxY.js (new) 970 B 🔴 +970 B 🔴 +547 B 🔴 +484 B
assets/tailwindUtil-Di4nI2Ni.js (new) 488 B 🔴 +488 B 🔴 +297 B 🔴 +272 B
assets/tailwindUtil-Hy0jY5OA.js (removed) 488 B 🟢 -488 B 🟢 -298 B 🟢 -267 B
assets/_plugin-vue_export-helper-CLv7oIby.js (new) 467 B 🔴 +467 B 🔴 +270 B 🔴 +243 B
assets/_plugin-vue_export-helper-DLRTaeJK.js (removed) 467 B 🟢 -467 B 🟢 -270 B 🟢 -243 B
assets/useCurrentUser-BpX8Cb_R.js (removed) 145 B 🟢 -145 B 🟢 -114 B 🟢 -102 B
assets/useCurrentUser-DdrKQtck.js (new) 145 B 🔴 +145 B 🔴 +114 B 🔴 +99 B

Status: 11 added / 11 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-other-D-057CYB.js (new) 4.1 MB 🔴 +4.1 MB 🔴 +867 kB 🔴 +673 kB
assets/vendor-other-Dk4vQph5.js (removed) 4.1 MB 🟢 -4.1 MB 🟢 -867 kB 🟢 -673 kB
assets/vendor-primevue-BK91gQps.js (removed) 3.04 MB 🟢 -3.04 MB 🟢 -560 kB 🟢 -329 kB
assets/vendor-primevue-bkmKwsUf.js (new) 3.04 MB 🔴 +3.04 MB 🔴 +560 kB 🔴 +329 kB
assets/vendor-three-BKpliY5_.js (removed) 1.83 MB 🟢 -1.83 MB 🟢 -391 kB 🟢 -283 kB
assets/vendor-three-CERwhPwK.js (new) 1.83 MB 🔴 +1.83 MB 🔴 +391 kB 🔴 +283 kB
assets/vendor-tiptap-BFRBT3RT.js (removed) 650 kB 🟢 -650 kB 🟢 -152 kB 🟢 -122 kB
assets/vendor-tiptap-BxrEVL6s.js (new) 650 kB 🔴 +650 kB 🔴 +152 kB 🔴 +122 kB
assets/vendor-chart-BD4LLnJB.js (new) 408 kB 🔴 +408 kB 🔴 +98.6 kB 🔴 +80.6 kB
assets/vendor-chart-DdBDBwvF.js (removed) 408 kB 🟢 -408 kB 🟢 -98.6 kB 🟢 -80.6 kB
assets/vendor-xterm-IX6P8SWv.js (new) 398 kB 🔴 +398 kB 🔴 +79.3 kB 🔴 +63.5 kB
assets/vendor-xterm-PDw3y6Aq.js (removed) 398 kB 🟢 -398 kB 🟢 -79.3 kB 🟢 -63.5 kB
assets/vendor-reka-ui-B1HjWS0V.js (new) 256 kB 🔴 +256 kB 🔴 +50.6 kB 🔴 +42.1 kB
assets/vendor-reka-ui-Bh_PdEOO.js (removed) 256 kB 🟢 -256 kB 🟢 -50.6 kB 🟢 -42.1 kB
assets/vendor-vue-BiXzLAAm.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +4.38 kB 🔴 +3.92 kB
assets/vendor-vue-DMjbEcx7.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -4.38 kB 🟢 -3.92 kB

Status: 8 added / 8 removed

Other — 6.55 MB (baseline 6.55 MB) • 🔴 +2.43 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/nodeDefs-Bg1UEeRw.js (new) 448 kB 🔴 +448 kB 🔴 +69.9 kB 🔴 +45.2 kB
assets/nodeDefs-C3-BImCp.js (removed) 448 kB 🟢 -448 kB 🟢 -69.9 kB 🟢 -45.2 kB
assets/nodeDefs-B68z80AD.js (new) 413 kB 🔴 +413 kB 🔴 +63.4 kB 🔴 +41.8 kB
assets/nodeDefs-ChGhVGtO.js (removed) 413 kB 🟢 -413 kB 🟢 -63.4 kB 🟢 -41.8 kB
assets/nodeDefs-75iqoHK1.js (removed) 412 kB 🟢 -412 kB 🟢 -65.4 kB 🟢 -43.1 kB
assets/nodeDefs-qCfy7PeV.js (new) 412 kB 🔴 +412 kB 🔴 +65.4 kB 🔴 +43.1 kB
assets/nodeDefs-BgIXRnZk.js (removed) 383 kB 🟢 -383 kB 🟢 -63.8 kB 🟢 -41.6 kB
assets/nodeDefs-DxR-7ogK.js (new) 383 kB 🔴 +383 kB 🔴 +63.8 kB 🔴 +41.6 kB
assets/nodeDefs-CeaH9-p6.js (removed) 373 kB 🟢 -373 kB 🟢 -62.5 kB 🟢 -41.3 kB
assets/nodeDefs-CinCueZ7.js (new) 373 kB 🔴 +373 kB 🔴 +62.5 kB 🔴 +41.2 kB
assets/nodeDefs-ClsEmImE.js (removed) 369 kB 🟢 -369 kB 🟢 -60.9 kB 🟢 -41.7 kB
assets/nodeDefs-xDY-sSQw.js (new) 369 kB 🔴 +369 kB 🔴 +60.9 kB 🔴 +41.7 kB
assets/nodeDefs-CUgTHcSh.js (removed) 369 kB 🟢 -369 kB 🟢 -62.2 kB 🟢 -42.6 kB
assets/nodeDefs-DKHHBXVW.js (new) 369 kB 🔴 +369 kB 🔴 +62.2 kB 🔴 +42.6 kB
assets/nodeDefs-CCnfIwZD.js (removed) 366 kB 🟢 -366 kB 🟢 -59.8 kB 🟢 -41 kB
assets/nodeDefs-DgzjkU5p.js (new) 366 kB 🔴 +366 kB 🔴 +59.8 kB 🔴 +41 kB
assets/nodeDefs-B0UaQKt6.js (new) 363 kB 🔴 +363 kB 🔴 +59 kB 🔴 +40.3 kB
assets/nodeDefs-DaayC8qD.js (removed) 363 kB 🟢 -363 kB 🟢 -59 kB 🟢 -40.3 kB
assets/nodeDefs-BkQR1m9D.js (removed) 342 kB 🟢 -342 kB 🟢 -61.2 kB 🟢 -40.3 kB
assets/nodeDefs-Dn1Haq99.js (new) 342 kB 🔴 +342 kB 🔴 +61.2 kB 🔴 +40.3 kB
assets/nodeDefs-CC3d1IX6.js (removed) 339 kB 🟢 -339 kB 🟢 -60.2 kB 🟢 -39 kB
assets/nodeDefs-CicFSATk.js (new) 339 kB 🔴 +339 kB 🔴 +60.2 kB 🔴 +39 kB
assets/core-BP-fPALf.js (removed) 180 kB 🟢 -180 kB 🟢 -43.3 kB 🟢 -36.2 kB
assets/core-B2vIeg6z.js (new) 180 kB 🔴 +180 kB 🔴 +43.3 kB 🔴 +36.2 kB
assets/main-d4ZcTV7L.js (removed) 172 kB 🟢 -172 kB 🟢 -45.5 kB 🟢 -36.1 kB
assets/main-DVGct6t3.js (new) 172 kB 🔴 +172 kB 🔴 +45.5 kB 🔴 +36 kB
assets/main-mdvCJJpq.js (new) 154 kB 🔴 +154 kB 🔴 +41 kB 🔴 +33.1 kB
assets/main-sT768wiI.js (removed) 154 kB 🟢 -154 kB 🟢 -41 kB 🟢 -33.2 kB
assets/main-Do-oawDh.js (new) 150 kB 🔴 +150 kB 🔴 +41.1 kB 🔴 +32.8 kB
assets/main-oCqMFbo3.js (removed) 150 kB 🟢 -150 kB 🟢 -41.1 kB 🟢 -32.8 kB
assets/main-akhLOzys.js (removed) 143 kB 🟢 -143 kB 🟢 -40.9 kB 🟢 -32.7 kB
assets/main-DpM2tcEq.js (new) 143 kB 🔴 +143 kB 🔴 +40.9 kB 🔴 +32.7 kB
assets/main-Cu50a8UC.js (removed) 132 kB 🟢 -132 kB 🟢 -39.6 kB 🟢 -33.2 kB
assets/main-DcPG2fUX.js (new) 132 kB 🔴 +132 kB 🔴 +39.6 kB 🔴 +33.2 kB
assets/main-B-fN60cC.js (new) 129 kB 🔴 +129 kB 🔴 +38.9 kB 🔴 +31.4 kB
assets/main-DvfWsSBh.js (removed) 129 kB 🟢 -129 kB 🟢 -38.9 kB 🟢 -31.4 kB
assets/main-DQY0Poqn.js (new) 128 kB 🔴 +128 kB 🔴 +38.4 kB 🔴 +31.8 kB
assets/main-RPBRc45Y.js (removed) 128 kB 🟢 -128 kB 🟢 -38.4 kB 🟢 -31.8 kB
assets/main-Dyymoku4.js (removed) 125 kB 🟢 -125 kB 🟢 -38.6 kB 🟢 -32.3 kB
assets/main-Fbu5slCi.js (new) 125 kB 🔴 +125 kB 🔴 +38.6 kB 🔴 +32.3 kB
assets/main-_Vi60AGa.js (new) 125 kB 🔴 +125 kB 🔴 +38 kB 🔴 +31.9 kB
assets/main-DhCVcHpP.js (removed) 125 kB 🟢 -125 kB 🟢 -38 kB 🟢 -31.9 kB
assets/main-3xQXroMq.js (new) 112 kB 🔴 +112 kB 🔴 +38.1 kB 🔴 +30.3 kB
assets/main-rcgwfJJa.js (removed) 112 kB 🟢 -112 kB 🟢 -38.1 kB 🟢 -30.3 kB
assets/main-B7nu0wu4.js (new) 111 kB 🔴 +111 kB 🔴 +38.1 kB 🔴 +30 kB
assets/main-BAD0AZuf.js (removed) 111 kB 🟢 -111 kB 🟢 -38.1 kB 🟢 -30.1 kB
assets/WidgetSelect-9T6LZGb1.js (new) 51 kB 🔴 +51 kB 🔴 +11.3 kB 🔴 +9.8 kB
assets/WidgetSelect-unofi1zO.js (removed) 51 kB 🟢 -51 kB 🟢 -11.2 kB 🟢 -9.79 kB
assets/Load3DControls-Cn0pcsEv.js (new) 35.9 kB 🔴 +35.9 kB 🔴 +5.87 kB 🔴 +5.09 kB
assets/Load3DControls-DxWEPuGD.js (removed) 35.9 kB 🟢 -35.9 kB 🟢 -5.87 kB 🟢 -5.08 kB
assets/SubscriptionRequiredDialogContent-BLGMw6eZ.js (new) 28.7 kB 🔴 +28.7 kB 🔴 +6.79 kB 🔴 +5.92 kB
assets/SubscriptionRequiredDialogContent-DM6QbCXs.js (removed) 28.7 kB 🟢 -28.7 kB 🟢 -6.78 kB 🟢 -5.92 kB
assets/CurrentUserPopoverWorkspace-BIbBWGE8.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +4.99 kB 🔴 +4.42 kB
assets/CurrentUserPopoverWorkspace-Fd_vlGrK.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5 kB 🟢 -4.43 kB
assets/Load3D-_Bv8_C79.js (removed) 20.9 kB 🟢 -20.9 kB 🟢 -4.59 kB 🟢 -4.01 kB
assets/Load3D-DF5qLCfs.js (new) 20.9 kB 🔴 +20.9 kB 🔴 +4.59 kB 🔴 +4.03 kB
assets/commands-Dmj47WTl.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -3.96 kB 🟢 -3.07 kB
assets/commands-DXauvccL.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +3.96 kB 🔴 +3.09 kB
assets/commands-BC_Q8we6.js (removed) 19.3 kB 🟢 -19.3 kB 🟢 -3.68 kB 🟢 -2.83 kB
assets/commands-C6piRza5.js (new) 19.3 kB 🔴 +19.3 kB 🔴 +3.68 kB 🔴 +2.87 kB
assets/commands-CuidUnsD.js (removed) 19.3 kB 🟢 -19.3 kB 🟢 -3.69 kB 🟢 -2.9 kB
assets/commands-D1595tOr.js (new) 19.3 kB 🔴 +19.3 kB 🔴 +3.69 kB 🔴 +2.9 kB
assets/commands-B52_zgXW.js (removed) 18.8 kB 🟢 -18.8 kB 🟢 -3.79 kB 🟢 -2.93 kB
assets/commands-Cf8Zq1td.js (new) 18.8 kB 🔴 +18.8 kB 🔴 +3.79 kB 🔴 +2.93 kB
assets/commands-BEw5ErI4.js (new) 18.5 kB 🔴 +18.5 kB 🔴 +3.52 kB 🔴 +2.92 kB
assets/commands-D5OfZ3bv.js (removed) 18.5 kB 🟢 -18.5 kB 🟢 -3.52 kB 🟢 -2.91 kB
assets/WidgetInputNumber-BhnSgdtR.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.51 kB 🔴 +4.01 kB
assets/WidgetInputNumber-rTVc86e_.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.51 kB 🟢 -4.02 kB
assets/WidgetRecordAudio-BxsjpLOP.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.96 kB 🟢 -4.43 kB
assets/WidgetRecordAudio-ZBdm61Wi.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.96 kB 🔴 +4.43 kB
assets/SubscriptionPanelContentWorkspace-BQ6Wb-NQ.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.47 kB 🟢 -3.91 kB
assets/SubscriptionPanelContentWorkspace-Da_veZ_6.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.47 kB 🔴 +3.9 kB
assets/commands-MVcGSIbM.js (removed) 18 kB 🟢 -18 kB 🟢 -3.51 kB 🟢 -2.9 kB
assets/commands-P5QCEfZc.js (new) 18 kB 🔴 +18 kB 🔴 +3.51 kB 🔴 +2.88 kB
assets/commands-CiziP3Xs.js (new) 18 kB 🔴 +18 kB 🔴 +3.39 kB 🔴 +2.78 kB
assets/commands-K3soE7da.js (removed) 18 kB 🟢 -18 kB 🟢 -3.4 kB 🟢 -2.78 kB
assets/commands-BGeHkplA.js (new) 17.9 kB 🔴 +17.9 kB 🔴 +3.35 kB 🔴 +2.77 kB
assets/commands-CgVf6wUK.js (removed) 17.9 kB 🟢 -17.9 kB 🟢 -3.36 kB 🟢 -2.77 kB
assets/commands-BZlNQPg2.js (removed) 17.8 kB 🟢 -17.8 kB 🟢 -3.65 kB 🟢 -2.84 kB
assets/commands-C_Y3D6Cn.js (new) 17.8 kB 🔴 +17.8 kB 🔴 +3.65 kB 🔴 +2.84 kB
assets/commands-BV0l36Iz.js (new) 17.2 kB 🔴 +17.2 kB 🔴 +3.57 kB 🔴 +2.73 kB
assets/commands-DUK2nLuH.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -3.57 kB 🟢 -2.73 kB
assets/WidgetImageCrop-BH3Bi5Vz.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +4.14 kB 🔴 +3.63 kB
assets/WidgetImageCrop-C-YElMXh.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -4.14 kB 🟢 -3.63 kB
assets/commands-27PChCGl.js (removed) 17 kB 🟢 -17 kB 🟢 -3.54 kB 🟢 -2.64 kB
assets/commands-WbYP_D61.js (new) 17 kB 🔴 +17 kB 🔴 +3.54 kB 🔴 +2.64 kB
assets/PanelTemplate-B9amhbM9.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +5.46 kB 🔴 +4.8 kB
assets/PanelTemplate-XqkCNxJ4.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -5.45 kB 🟢 -4.79 kB
assets/LazyImage-CRvudoAP.js (new) 14.1 kB 🔴 +14.1 kB 🔴 +4 kB 🔴 +3.54 kB
assets/LazyImage-rWQpxwnM.js (removed) 14.1 kB 🟢 -14.1 kB 🟢 -4 kB 🟢 -3.54 kB
assets/AudioPreviewPlayer-CBKdeBF0.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.97 kB 🔴 +2.65 kB
assets/AudioPreviewPlayer-GF2k-UuD.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.65 kB
assets/InviteMemberDialogContent-7nfudT0M.js (new) 8.36 kB 🔴 +8.36 kB 🔴 +2.51 kB 🔴 +2.16 kB
assets/InviteMemberDialogContent-DDVIiF-v.js (removed) 8.36 kB 🟢 -8.36 kB 🟢 -2.5 kB 🟢 -2.17 kB
assets/WidgetWithControl-E2HdljjM.js (new) 8.07 kB 🔴 +8.07 kB 🔴 +2.68 kB 🔴 +2.42 kB
assets/WidgetWithControl-D4qilL5P.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.65 kB 🟢 -2.38 kB
assets/CreateWorkspaceDialogContent-BvDRVJsb.js (removed) 5.93 kB 🟢 -5.93 kB 🟢 -1.93 kB 🟢 -1.68 kB
assets/CreateWorkspaceDialogContent-ChceZ3nt.js (new) 5.93 kB 🔴 +5.93 kB 🔴 +1.93 kB 🔴 +1.68 kB
assets/EditWorkspaceDialogContent-CcMoQJC_.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.88 kB 🔴 +1.65 kB
assets/EditWorkspaceDialogContent-CZtMaIFc.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.88 kB 🟢 -1.64 kB
assets/ValueControlPopover-Dykk_Rt1.js (removed) 5.17 kB 🟢 -5.17 kB 🟢 -1.69 kB 🟢 -1.5 kB
assets/ValueControlPopover-ys0Fox_4.js (new) 5.17 kB 🔴 +5.17 kB 🔴 +1.69 kB 🔴 +1.5 kB
assets/WidgetBoundingBox-CgNPbPqq.js (removed) 4.71 kB 🟢 -4.71 kB 🟢 -1.13 kB 🟢 -1.02 kB
assets/WidgetBoundingBox-DClY9LmH.js (new) 4.71 kB 🔴 +4.71 kB 🔴 +1.13 kB 🔴 +1.01 kB
assets/DeleteWorkspaceDialogContent-DabNszJ_.js (new) 4.59 kB 🔴 +4.59 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/DeleteWorkspaceDialogContent-DOdlCe3Z.js (removed) 4.59 kB 🟢 -4.59 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/WidgetGalleria-BtIaO6AP.js (new) 4.57 kB 🔴 +4.57 kB 🔴 +1.57 kB 🔴 +1.42 kB
assets/WidgetGalleria-f72WaoQY.js (removed) 4.57 kB 🟢 -4.57 kB 🟢 -1.57 kB 🟢 -1.42 kB
assets/LeaveWorkspaceDialogContent-aFiAkTcC.js (new) 4.41 kB 🔴 +4.41 kB 🔴 +1.51 kB 🔴 +1.3 kB
assets/LeaveWorkspaceDialogContent-Cfri96Vc.js (removed) 4.41 kB 🟢 -4.41 kB 🟢 -1.5 kB 🟢 -1.3 kB
assets/RemoveMemberDialogContent-B6ffSlhL.js (new) 4.38 kB 🔴 +4.38 kB 🔴 +1.45 kB 🔴 +1.27 kB
assets/RemoveMemberDialogContent-Bu_wwy8Q.js (removed) 4.38 kB 🟢 -4.38 kB 🟢 -1.45 kB 🟢 -1.26 kB
assets/RevokeInviteDialogContent-BgdBcdtX.js (removed) 4.29 kB 🟢 -4.29 kB 🟢 -1.47 kB 🟢 -1.29 kB
assets/RevokeInviteDialogContent-BS_Yq8uW.js (new) 4.29 kB 🔴 +4.29 kB 🔴 +1.47 kB 🔴 +1.29 kB
assets/Slider-BAEfKuro.js (removed) 4.21 kB 🟢 -4.21 kB 🟢 -1.52 kB 🟢 -1.34 kB
assets/Slider-BMusiptx.js (new) 4.21 kB 🔴 +4.21 kB 🔴 +1.52 kB 🔴 +1.34 kB
assets/WidgetImageCompare-D2qzVTaR.js (new) 3.79 kB 🔴 +3.79 kB 🔴 +1.28 kB 🔴 +1.12 kB
assets/WidgetImageCompare-Dd0p7l7V.js (removed) 3.79 kB 🟢 -3.79 kB 🟢 -1.28 kB 🟢 -1.12 kB
assets/WidgetColorPicker-BaC4Zuu3.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.38 kB 🔴 +1.25 kB
assets/WidgetColorPicker-x8L_rIAb.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.38 kB 🟢 -1.25 kB
assets/WidgetTextarea-BxkGsnJX.js (new) 3.52 kB 🔴 +3.52 kB 🔴 +1.33 kB 🔴 +1.17 kB
assets/WidgetTextarea-DwdEJ3mA.js (removed) 3.52 kB 🟢 -3.52 kB 🟢 -1.33 kB 🟢 -1.17 kB
assets/WidgetMarkdown-bP5F2-qx.js (removed) 3.22 kB 🟢 -3.22 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetMarkdown-DMzHdmk2.js (new) 3.22 kB 🔴 +3.22 kB 🔴 +1.28 kB 🔴 +1.13 kB
assets/WidgetToggleSwitch-1Qf74Gsu.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.19 kB 🔴 +1.06 kB
assets/WidgetToggleSwitch-BIK7gguT.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.19 kB 🟢 -1.06 kB
assets/GlobalToast--KouBvz1.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.1 kB 🔴 +939 B
assets/GlobalToast-x5Aleg_4.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.09 kB 🟢 -942 B
assets/cloudSessionCookie-Zj7TWx5c.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +944 B 🔴 +813 B
assets/SubscribeToRun-BPyuUoXg.js (removed) 2.96 kB 🟢 -2.96 kB 🟢 -1.15 kB 🟢 -1.04 kB
assets/SubscribeToRun-C5av9Alj.js (new) 2.96 kB 🔴 +2.96 kB 🔴 +1.15 kB 🔴 +1.01 kB
assets/cloudSessionCookie-BjT2vcls.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -933 B 🟢 -808 B
assets/MediaVideoTop-WHbgZ4CI.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.05 kB 🔴 +910 B
assets/MediaVideoTop-yhq80asg.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.05 kB 🟢 -910 B
assets/WidgetChart-3mC7A_va.js (removed) 2.79 kB 🟢 -2.79 kB 🟢 -1.06 kB 🟢 -962 B
assets/WidgetChart-c8c4iM6B.js (new) 2.79 kB 🔴 +2.79 kB 🔴 +1.06 kB 🔴 +971 B
assets/WidgetLayoutField-C8vq_J5o.js (new) 2.61 kB 🔴 +2.61 kB 🔴 +1.01 kB 🔴 +888 B
assets/WidgetLayoutField-CZXFB71F.js (removed) 2.61 kB 🟢 -2.61 kB 🟢 -1.01 kB 🟢 -892 B
assets/WidgetInputText-Db81LDvm.js (new) 2.58 kB 🔴 +2.58 kB 🔴 +1.01 kB 🔴 +949 B
assets/WidgetInputText-DOFSeOZG.js (removed) 2.58 kB 🟢 -2.58 kB 🟢 -1.01 kB 🟢 -912 B
assets/BaseViewTemplate-C4Vu5BMi.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -940 B
assets/BaseViewTemplate-CSScw_IG.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +931 B
assets/Media3DTop-8_T22Isd.js (removed) 2.38 kB 🟢 -2.38 kB 🟢 -1.02 kB 🟢 -876 B
assets/Media3DTop-CUbzOuTJ.js (new) 2.38 kB 🔴 +2.38 kB 🔴 +1.02 kB 🔴 +873 B
assets/MediaImageTop-1THG_DJM.js (removed) 2.34 kB 🟢 -2.34 kB 🟢 -1 kB 🟢 -869 B
assets/MediaImageTop-BAA-rCud.js (new) 2.34 kB 🔴 +2.34 kB 🔴 +1 kB 🔴 +886 B
assets/MediaAudioTop-BYIydMOt.js (new) 2 kB 🔴 +2 kB 🔴 +882 B 🔴 +760 B
assets/MediaAudioTop-CBP8th-j.js (removed) 2 kB 🟢 -2 kB 🟢 -881 B 🟢 -759 B
assets/CloudRunButtonWrapper-D4SGhktf.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +645 B 🔴 +566 B
assets/CloudRunButtonWrapper-r1XjwG70.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -644 B 🟢 -561 B
assets/auto-BmypP-XQ.js (removed) 1.73 kB 🟢 -1.73 kB 🟢 -636 B 🟢 -580 B
assets/auto-RHVQpwL9.js (new) 1.73 kB 🔴 +1.73 kB 🔴 +637 B 🔴 +565 B
assets/widgetPropFilter-DOe9Bb1I.js (removed) 1.31 kB 🟢 -1.31 kB 🟢 -568 B 🟢 -508 B
assets/widgetPropFilter-ERx8czR8.js (new) 1.31 kB 🔴 +1.31 kB 🔴 +569 B 🔴 +506 B
assets/gtm-BJ3bgvnV.js (new) 1.3 kB 🔴 +1.3 kB 🔴 +628 B 🔴 +500 B
assets/cloudBadges-BQaypftn.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +539 B 🔴 +482 B
assets/cloudBadges-j2IwjFNm.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -539 B 🟢 -504 B
assets/graphHasMissingNodes-B7iKXUmA.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +460 B 🔴 +421 B
assets/graphHasMissingNodes-BB8Cw-Nb.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -460 B 🟢 -421 B
assets/cloudSubscription-Cc9ZvTLw.js (removed) 976 B 🟢 -976 B 🟢 -460 B 🟢 -401 B
assets/cloudSubscription-CpcuLF43.js (new) 976 B 🔴 +976 B 🔴 +461 B 🔴 +398 B
assets/types-9RyZweiA.js (new) 909 B 🔴 +909 B 🔴 +458 B 🔴 +384 B
assets/previousFullPath-B-XG7lU7.js (removed) 838 B 🟢 -838 B 🟢 -415 B 🟢 -346 B
assets/previousFullPath-CmezY7As.js (new) 838 B 🔴 +838 B 🔴 +414 B 🔴 +380 B
assets/nightlyBadges-0Dark3-5.js (new) 594 B 🔴 +594 B 🔴 +358 B 🔴 +311 B
assets/nightlyBadges-CG9e-Pr-.js (removed) 594 B 🟢 -594 B 🟢 -356 B 🟢 -311 B
assets/widget-C_FImE7Q.js (new) 518 B 🔴 +518 B 🔴 +344 B 🔴 +290 B
assets/widget-CD3JnB1i.js (removed) 518 B 🟢 -518 B 🟢 -343 B 🟢 -289 B
assets/SubscriptionPanelContentWorkspace-_pI_ldtL.js (new) 266 B 🔴 +266 B 🔴 +136 B 🔴 +117 B
assets/SubscriptionPanelContentWorkspace-Bunez9yk.js (removed) 266 B 🟢 -266 B 🟢 -136 B 🟢 -115 B
assets/WidgetBoundingBox-Bp7B7z1s.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -114 B
assets/WidgetBoundingBox-BTNRImOa.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +114 B
assets/WidgetInputNumber-D7s4zaCo.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +111 B
assets/WidgetInputNumber-DxC6RxdT.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -112 B
assets/WidgetLegacy-Dfq0wRyg.js (new) 164 B 🔴 +164 B 🔴 +125 B 🔴 +110 B
assets/WidgetLegacy-DWAvmBBW.js (removed) 164 B 🟢 -164 B 🟢 -125 B 🟢 -111 B
assets/mixpanel.module-BiPjOPVW.js (removed) 143 B 🟢 -143 B 🟢 -125 B 🟢 -107 B
assets/mixpanel.module-Bk2WCk7z.js (new) 143 B 🔴 +143 B 🔴 +125 B 🔴 +114 B
assets/Load3D-B2N_G7UJ.js (new) 131 B 🔴 +131 B 🔴 +107 B 🔴 +105 B
assets/Load3D-D0KSk_Gf.js (removed) 131 B 🟢 -131 B 🟢 -107 B 🟢 -107 B
assets/gtm-hxMW470c.js (new) 122 B 🔴 +122 B 🔴 +116 B 🔴 +113 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-cVp-94Rc.js 1.96 kB 1.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 92 added / 89 removed

@benceruleanlu benceruleanlu marked this pull request as ready for review January 26, 2026 19:39
@benceruleanlu benceruleanlu requested a review from a team as a code owner January 26, 2026 19:39
Copilot AI review requested due to automatic review settings January 26, 2026 19:39
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jan 26, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds Google Tag Manager (GTM) integration for cloud distribution builds to track user analytics. The implementation injects GTM scripts during the build process and tracks page views on route changes and signup events for new users.

Changes:

  • GTM script injection in vite.config.mts for cloud builds with configurable enablement via environment variable
  • Page view tracking in router.ts that pushes location and title data on route changes
  • Signup event tracking in firebaseAuthStore.ts with privacy-preserving SHA-256 hashed user IDs for email, Google, and GitHub authentication methods

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
vite.config.mts Adds GTM script and noscript iframe injection via Vite plugin for cloud builds, with ENABLE_GTM flag for manual override
src/stores/firebaseAuthStore.ts Implements signup event tracking with SHA-256 hashed user IDs and dataLayer helper functions
src/router.ts Adds page_view event tracking on route changes using router.afterEach hook
global.d.ts Adds TypeScript type definition for window.dataLayer array

Comment on lines 84 to 86
const pushDataLayerEvent = (event: Record<string, unknown>) => {
if (!isCloud || typeof window === 'undefined') return
const dataLayer = window.dataLayer ?? (window.dataLayer = [])
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The dataLayer initialization pattern window.dataLayer ?? (window.dataLayer = []) is duplicated between this file and src/router.ts:42. Consider extracting this into a shared utility function to maintain consistency and reduce duplication. For example, create a helper function like getDataLayer() that both files can import.

Suggested change
const pushDataLayerEvent = (event: Record<string, unknown>) => {
if (!isCloud || typeof window === 'undefined') return
const dataLayer = window.dataLayer ?? (window.dataLayer = [])
const getDataLayer = (): unknown[] | undefined => {
if (!isCloud || typeof window === 'undefined') return
// Initialize dataLayer if it does not exist yet
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dataLayer = (window as any).dataLayer ?? ((window as any).dataLayer = [])
return dataLayer
}
const pushDataLayerEvent = (event: Record<string, unknown>) => {
const dataLayer = getDataLayer()
if (!dataLayer) return

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's fine for now just two callers

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb0abe7417

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

src/router.ts Outdated
Comment on lines 42 to 46
const dataLayer = window.dataLayer ?? (window.dataLayer = [])
dataLayer.push({
event: 'page_view',
page_location: window.location.href,
page_title: document.title

Choose a reason for hiding this comment

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

P2 Badge Strip invite tokens from page_location before GTM push

Because pushPageView sends window.location.href as page_location, any invite tokens in the query string are forwarded to GTM. The cloud invite flow explicitly accepts /?invite=TOKEN and only removes it later in useInviteUrlLoader (so the token is still present on the first navigation), which means the raw invite token will be sent to third‑party analytics on the initial page_view. To avoid leaking sensitive invite codes, consider sanitizing page_location (e.g., remove invite and other preserved query params) before pushing to dataLayer.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/router.ts`:
- Around line 39-48: pushPageView may read document.title too early when called
from router.afterEach causing the old title to be pushed; change pushPageView
(and its caller in router.afterEach) to prefer a provided title param (e.g.,
to.meta.title) and/or defer reading document.title via Vue.nextTick before
building the dataLayer object so the new page title is available. Update the
pushPageView signature to accept an optional title and in router.afterEach pass
to.meta.title if present, otherwise call pushPageView inside nextTick to read
document.title.

Comment on lines 39 to 48
function pushPageView(): void {
if (!isCloud) return

const dataLayer = window.dataLayer ?? (window.dataLayer = [])
dataLayer.push({
event: 'page_view',
page_location: window.location.href,
page_title: document.title
})
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 26, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential timing issue with document.title.

When pushPageView is called in router.afterEach, document.title may still reflect the previous page's title since Vue Router's navigation completes before the new component is mounted and updates the title.

Consider using to.meta.title if available, or deferring the push with nextTick to allow the DOM to update:

🔧 Suggested fix using nextTick
+import { nextTick } from 'vue'
+
 function pushPageView(): void {
   if (!isCloud) return

-  const dataLayer = window.dataLayer ?? (window.dataLayer = [])
-  dataLayer.push({
-    event: 'page_view',
-    page_location: window.location.href,
-    page_title: document.title
+  nextTick(() => {
+    const dataLayer = window.dataLayer ?? (window.dataLayer = [])
+    dataLayer.push({
+      event: 'page_view',
+      page_location: window.location.href,
+      page_title: document.title
+    })
   })
 }
📝 Committable suggestion

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

Suggested change
function pushPageView(): void {
if (!isCloud) return
const dataLayer = window.dataLayer ?? (window.dataLayer = [])
dataLayer.push({
event: 'page_view',
page_location: window.location.href,
page_title: document.title
})
}
import { nextTick } from 'vue'
function pushPageView(): void {
if (!isCloud) return
nextTick(() => {
const dataLayer = window.dataLayer ?? (window.dataLayer = [])
dataLayer.push({
event: 'page_view',
page_location: window.location.href,
page_title: document.title
})
})
}
🤖 Prompt for AI Agents
In `@src/router.ts` around lines 39 - 48, pushPageView may read document.title too
early when called from router.afterEach causing the old title to be pushed;
change pushPageView (and its caller in router.afterEach) to prefer a provided
title param (e.g., to.meta.title) and/or defer reading document.title via
Vue.nextTick before building the dataLayer object so the new page title is
available. Update the pushPageView signature to accept an optional title and in
router.afterEach pass to.meta.title if present, otherwise call pushPageView
inside nextTick to read document.title.

Copy link
Member Author

Choose a reason for hiding this comment

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

ahh, we'll see what marketing/gtm wants. we pagetitle is practically only ever going to be ComfyUI because we only change it for workflow name, unsaved indicator, and node/progress status.

Copy link
Contributor

Choose a reason for hiding this comment

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

@benceruleanlu, understood! That makes sense—if the title is mostly static, the timing issue becomes less critical. It's good to verify with your marketing/GTM team whether they need route-specific titles or if the current approach works for their tracking needs. Feel free to follow up if they have specific requirements! 👍


✏️ Learnings added
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 8311
File: src/router.ts:39-48
Timestamp: 2026-01-26T20:01:11.087Z
Learning: In ComfyUI, the page title (document.title) is mostly static and remains "ComfyUI" throughout the application. It only changes for workflow name, unsaved indicator, and node/progress status, not for different routes or pages.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@benceruleanlu
Copy link
Member Author

During the frontend standup, the team (Christian and Jacob) wanted that no line of this Google related analytics code to be available on the local build, and proposed injecting this during build time cloud-side. Or just making sure it's fully tree shaken for local builds.

@benceruleanlu benceruleanlu marked this pull request as draft January 26, 2026 22:22
@benceruleanlu
Copy link
Member Author

Because iscloud is gating, this won't be available in the code in local builds, but will still be present in sorucemaps. I don't think there's much we can do about its presence in sourcemaps.

@benceruleanlu benceruleanlu marked this pull request as ready for review January 27, 2026 19:36
@dosubot dosubot bot removed the size:M This PR changes 30-99 lines, ignoring generated files. label Jan 27, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb0abe7417

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +44 to +46
event: 'page_view',
page_location: window.location.href,
page_title: document.title

Choose a reason for hiding this comment

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

P2 Badge Avoid sending user workflow titles to GTM

pushPageView forwards document.title as page_title. In this app, document.title is derived from the active workflow filename and node execution text via useBrowserTabTitle (see src/composables/useBrowserTabTitle.ts), which are user-provided values. On cloud builds this will send workflow names and progress text to GTM/GA on every route change, which can leak sensitive user content into analytics. Consider omitting page_title or replacing it with a sanitized/static value such as the route name before pushing to dataLayer.

Useful? React with 👍 / 👎.

@benceruleanlu benceruleanlu added needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch cloud/1.37 Backport PRs for cloud 1.37 labels Jan 27, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@src/platform/cloud/subscription/composables/useSubscription.test.ts`:
- Around line 214-221: Replace the hardcoded string
'pending_subscription_purchase' in the test with the exported STORAGE_KEY from
subscriptionPurchaseTracker to avoid key drift; import STORAGE_KEY from
"src/platform/cloud/subscription/utils/subscriptionPurchaseTracker" at the top
of useSubscription.test.ts and use STORAGE_KEY in the localStorage.setItem call
(the test code block around localStorage.setItem(...) and the STORAGE_KEY symbol
identify where to change).

In `@src/platform/cloud/subscription/composables/useSubscription.ts`:
- Around line 104-145: The three utilities—buildApiUrl, pushDataLayerEvent, and
trackSubscriptionPurchase—are currently declared as const arrow functions;
change each to a standard function declaration (e.g., function buildApiUrl(path:
string) { ... }) to follow the project's TypeScript guideline against function
expressions; preserve all current logic, parameter types, and surrounding scope
(including use of getComfyApiBaseUrl, __GTM_ENABLED__, window/dataLayer access,
t, getTierPrice, getPendingSubscriptionPurchase,
clearPendingSubscriptionPurchase, and CloudSubscriptionStatusResponse) so
behavior and references remain identical.

In `@src/platform/cloud/subscription/utils/subscriptionPurchaseTracker.ts`:
- Around line 32-68: Move the localStorage.getItem(STORAGE_KEY) call inside the
existing try block in getPendingSubscriptionPurchase and wrap both getItem and
JSON.parse with a try/catch so any storage exceptions are handled; create a
small helper function safeRemove() that calls
localStorage.removeItem(STORAGE_KEY) inside a try/catch and use safeRemove in
all places that currently call localStorage.removeItem (including the catch in
getPendingSubscriptionPurchase and inside clearPendingSubscriptionPurchase);
keep all existing validation logic (VALID_TIERS, VALID_CYCLES, MAX_AGE_MS) and
return behavior unchanged, only adding the guarded reads/removals to prevent
exceptions in restricted storage modes.

In `@src/stores/firebaseAuthStore.ts`:
- Around line 84-107: The GTM tracking helpers (pushDataLayerEvent, hashSha256,
trackSignUp) must be converted to function declarations and made best-effort so
they never block auth: add an early guard in trackSignUp that returns
immediately if !__GTM_ENABLED__ or typeof window === 'undefined', wrap the body
of trackSignUp in try-catch to swallow and log any errors so failures don’t
reject login/register promises, add a TextEncoder existence check in hashSha256
(return undefined if missing) and ensure pushDataLayerEvent similarly guards for
__GTM_ENABLED__ and window before accessing window.dataLayer; update references
to these functions accordingly.

In `@vite.config.mts`:
- Around line 54-63: Change ENABLE_GTM so GTM can only be active for cloud
builds and never in local/dev; compute it as true only when DISTRIBUTION ===
'cloud' and !IS_DEV, and allow explicit opt‑out via process.env.ENABLE_GTM ===
'false' but do not allow process.env.ENABLE_GTM === 'true' to force GTM on
non‑cloud builds—update the ENABLE_GTM expression (which currently uses
ENABLE_GTM, DISTRIBUTION, IS_DEV and process.env.ENABLE_GTM) to enforce
"cloud-only" and "no GA in dev" semantics while still honoring an explicit
opt‑out.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/platform/cloud/subscription/composables/useSubscription.test.ts (1)

76-92: Add cleanup for window.dataLayer and localStorage in beforeEach.

The test at line 210 sets window.dataLayer = [], but beforeEach doesn't reset it. This could cause test pollution if tests run in different orders or if previous tests leave data behind.

♻️ Proposed fix
   beforeEach(() => {
     vi.clearAllMocks()
     mockIsLoggedIn.value = false
     mockTelemetry.trackSubscription.mockReset()
     mockTelemetry.trackMonthlySubscriptionCancelled.mockReset()
+    window.dataLayer = undefined
+    localStorage.removeItem('pending_subscription_purchase')
     window.__CONFIG__ = {
       subscription_required: true
     } as typeof window.__CONFIG__
🤖 Fix all issues with AI agents
In `@src/platform/cloud/subscription/composables/useSubscription.ts`:
- Line 238: The call to trackSubscriptionPurchase(statusData) should be wrapped
in a try-catch so analytics errors cannot interrupt the subscription status
flow; locate the invocation of trackSubscriptionPurchase in useSubscription.ts
and surround it with a try { await trackSubscriptionPurchase(statusData) } catch
(err) { /* log error via existing logger or console.error without rethrowing */
} ensuring you await it if it returns a promise and do not let the catch rethrow
so the main fetch flow continues.

In `@src/platform/telemetry/gtm.ts`:
- Around line 25-32: The current finalize handler marks isInitialized = true for
both load and error events and swallows failures; change this so the 'load'
event sets isInitialized = true and resolves, while the 'error' event uses a
separate handler that logs the failure (e.g., via console.warn or your
telemetry/logger) including the Event/error details and then resolves (still
best-effort). Update the event wiring from using finalize for both events to use
finalize (or onLoad) for 'load' and onError (or finalizeError) for 'error',
ensuring you still resolve the initialization promise but do not falsely mark
success without logging; reference isInitialized, finalize, and the
script.addEventListener(...) calls to locate where to change.

Comment on lines +25 to +32
const finalize = () => {
isInitialized = true
resolve()
}

script.addEventListener('load', finalize, { once: true })
script.addEventListener('error', finalize, { once: true })
document.head?.appendChild(script)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Script load errors are silently swallowed.

When GTM script fails to load (e.g., network error, blocked by ad-blocker), finalize still sets isInitialized = true. This silently succeeds, which is reasonable for best-effort analytics, but consider logging the error for observability.

♻️ Optional: Add error logging
       const finalize = () => {
         isInitialized = true
         resolve()
       }

       script.addEventListener('load', finalize, { once: true })
-      script.addEventListener('error', finalize, { once: true })
+      script.addEventListener('error', (e) => {
+        console.warn('GTM script failed to load:', e)
+        finalize()
+      }, { once: true })
       document.head?.appendChild(script)
📝 Committable suggestion

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

Suggested change
const finalize = () => {
isInitialized = true
resolve()
}
script.addEventListener('load', finalize, { once: true })
script.addEventListener('error', finalize, { once: true })
document.head?.appendChild(script)
const finalize = () => {
isInitialized = true
resolve()
}
script.addEventListener('load', finalize, { once: true })
script.addEventListener('error', (e) => {
console.warn('GTM script failed to load:', e)
finalize()
}, { once: true })
document.head?.appendChild(script)
🤖 Prompt for AI Agents
In `@src/platform/telemetry/gtm.ts` around lines 25 - 32, The current finalize
handler marks isInitialized = true for both load and error events and swallows
failures; change this so the 'load' event sets isInitialized = true and
resolves, while the 'error' event uses a separate handler that logs the failure
(e.g., via console.warn or your telemetry/logger) including the Event/error
details and then resolves (still best-effort). Update the event wiring from
using finalize for both events to use finalize (or onLoad) for 'load' and
onError (or finalizeError) for 'error', ensuring you still resolve the
initialization promise but do not falsely mark success without logging;
reference isInitialized, finalize, and the script.addEventListener(...) calls to
locate where to change.

Copy link
Contributor

@Myestery Myestery left a comment

Choose a reason for hiding this comment

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

LGTM

await import('@/platform/remoteConfig/refreshRemoteConfig')
await refreshRemoteConfig({ useAuth: false })

const { initGtm } = await import('@/platform/telemetry/gtm')
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@benceruleanlu
Copy link
Member Author

I think the failing tests is part of the CI being broken on main? The snapshot seems to actually be wrong, and the actual version is right.

image

Going to merge

@benceruleanlu
Copy link
Member Author

Well, it passed on retry, but it's definitely a bad snapshot... We can fix it later

@benceruleanlu benceruleanlu merged commit 788f508 into main Jan 27, 2026
41 of 42 checks passed
@benceruleanlu benceruleanlu deleted the feat/cloud-gtm branch January 27, 2026 20:44
@github-actions
Copy link

⚠️ Backport to cloud/1.37 failed

Reason: Merge conflicts detected during cherry-pick of 788f508

📄 Conflicting files
src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts
🤖 Prompt for AI Agents
Backport PR #8311 (https://github.com/Comfy-Org/ComfyUI_frontend/pull/8311) to cloud/1.37.
Cherry-pick merge commit 788f50834ce618ede5e4cdc7a5164f210ce458a6 onto new branch
backport-8311-to-cloud-1.37 from origin/cloud/1.37.
Resolve conflicts in: src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts .
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport cloud/1.37] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.

cc @benceruleanlu

@benceruleanlu benceruleanlu added preview preview-cpu Creates a preview ephemeral environment for this PR (CPU only) labels Jan 28, 2026
@github-actions
Copy link

⚠️ Backport to cloud/1.37 failed

Reason: Merge conflicts detected during cherry-pick of 788f508

📄 Conflicting files
src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts
🤖 Prompt for AI Agents
Backport PR #8311 (https://github.com/Comfy-Org/ComfyUI_frontend/pull/8311) to cloud/1.37.
Cherry-pick merge commit 788f50834ce618ede5e4cdc7a5164f210ce458a6 onto new branch
backport-8311-to-cloud-1.37 from origin/cloud/1.37.
Resolve conflicts in: src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts .
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport cloud/1.37] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.

cc @benceruleanlu

1 similar comment
@github-actions
Copy link

⚠️ Backport to cloud/1.37 failed

Reason: Merge conflicts detected during cherry-pick of 788f508

📄 Conflicting files
src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts
🤖 Prompt for AI Agents
Backport PR #8311 (https://github.com/Comfy-Org/ComfyUI_frontend/pull/8311) to cloud/1.37.
Cherry-pick merge commit 788f50834ce618ede5e4cdc7a5164f210ce458a6 onto new branch
backport-8311-to-cloud-1.37 from origin/cloud/1.37.
Resolve conflicts in: src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.ts .
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport cloud/1.37] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.

cc @benceruleanlu

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

Labels

cloud/1.37 Backport PRs for cloud 1.37 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch preview preview-cpu Creates a preview ephemeral environment for this PR (CPU only) size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants