Skip to content

feat: add SnackbarToast component for canvas feedback#11731

Closed
dante01yoon wants to merge 3 commits intomainfrom
feat/snackbar-toast-component
Closed

feat: add SnackbarToast component for canvas feedback#11731
dante01yoon wants to merge 3 commits intomainfrom
feat/snackbar-toast-component

Conversation

@dante01yoon
Copy link
Copy Markdown
Collaborator

@dante01yoon dante01yoon commented Apr 28, 2026

figma: https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6826-77784&m=dev
Screenshot 2026-04-29 at 9 05 49 AM

Summary

  • Adds standalone SnackbarToast component (singleton, bottom-center, Teleport to body) plus useSnackbarToast composable and Storybook stories.
  • Component-only — does not wire any commands or stores, by design (see thread below).

Background

Extracted from #11718 per Slack discussion: https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1777399633850579

The team was undecided on whether to surface a snackbar for canvas actions like link-visibility toggle / focus-mode toggle / subgraph unpack ("Sonner for basic actions like this is overkill"). Conclusion: ship the component now, defer integration until UX direction settles.

Behavior

  • 2s default auto-dismiss; hovering pauses the timer.
  • Optional shortcut badge OR action button (mutually exclusive — action only renders when no shortcut is set).
  • Always-present close (X) button.
  • Reka ToolbarRoot / ToolbarButton for arrow-key navigation; role=status / aria-live=polite for SR announcement.

Storybook

Components/Graph/SnackbarToast — Default / WithShortcut / WithUndoAction / Persistent.

Visuals

Matches Figma node 6826:77784 in the Comfy Design System.

Test plan

  • Storybook renders all four stories
  • Auto-dismiss after 2s
  • Hover pauses, mouseleave resumes timer
  • X button dismisses immediately
  • Action button fires onAction callback
  • Keyboard nav across action group works (Reka toolbar)

Refs FE-484

┆Issue is synchronized with this Notion page by Unito

Introduces a singleton snackbar toast component (bottom-center, Teleport
to body) intended for non-blocking canvas feedback. Surfaces an optional
keybinding badge or an action button (e.g. Undo) and supports auto-
dismiss with hover-pause.

Component-only — no app integration in this PR. Wiring to specific
canvas commands (link visibility, focus mode, subgraph unpack) will land
in follow-up PRs once the UX direction is settled (#11718 thread).

Visuals match Figma node 6826:77784 in the Comfy Design System.

Refs FE-484
@dante01yoon dante01yoon requested a review from a team April 28, 2026 23:18
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Apr 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

Adds a typed snackbar toast system: a composable (useSnackbarToast) backed by an injection key, a provider component (SnackbarToastProvider) that manages toast lifecycle, a visual SnackbarToast component, Storybook stories, and unit tests for provider and composable.

Changes

Cohort / File(s) Summary
Composable
src/composables/useSnackbarToast.ts, src/composables/useSnackbarToast.test.ts
Adds useSnackbarToast with SnackbarToastKey and TypeScript interfaces (ShowSnackbarOptions, SnackbarToastItem, SnackbarToastApi); test verifies inject error without provider and access when key is provided.
Provider & Component
src/components/toast/SnackbarToastProvider.vue, src/components/toast/SnackbarToast.vue, src/components/toast/SnackbarToastProvider.test.ts
Adds SnackbarToastProvider that provides SnackbarToastApi (show, dismiss), manages a single-item toasts array, and exposes the API; adds SnackbarToast.vue rendering message, optional shortcut badge, action button, and dismiss handling; tests cover show/dismiss, replacement behavior, action handling (including thrown errors), and id semantics.
Storybook
src/components/toast/SnackbarToast.stories.ts
Adds Storybook stories (Default, WithShortcut, WithUndoAction, Persistent) demonstrating provider usage and various toast options.

Sequence Diagram

sequenceDiagram
    participant Consumer as Consumer / Story
    participant Composable as useSnackbarToast (inject)
    participant Provider as SnackbarToastProvider
    participant ToastComp as SnackbarToast Component
    participant User as End User

    Consumer->>Composable: call show(message, options)
    Composable->>Provider: (injected) show(message, options)
    Provider->>Provider: create id, build SnackbarToastItem
    Provider->>Provider: replace toasts array (single item)
    Provider-->>ToastComp: reactive prop (toast item)
    ToastComp->>User: render message, controls
    User->>ToastComp: click action (if present)
    ToastComp->>Provider: invoke onAction (calls provided callback) and emit dismiss
    Provider->>Provider: dismiss(id) -> remove toast from array
    User->>ToastComp: click close
    ToastComp->>Provider: emit dismiss
    Provider->>Provider: remove toast
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I found a key and gave a shout,
A little toast to pop right out.
Show, dismiss, or press "undo" —
I nibble bugs and ship it through! 🍃✨

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a SnackbarToast component for canvas feedback. It is concise, specific, and directly summarizes the primary contribution of the PR.
Description check ✅ Passed The PR description includes summary, background, behavior details, Storybook organization, visuals, and a comprehensive test plan. It provides substantial context about design decisions and integration deferral. Most required sections are present and well-documented.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
End-To-End Regression Coverage For Fixes ✅ Passed PR title uses feature language ('feat:') and commit subject uses 'refactor', neither containing bug-fix language, so E2E regression test coverage is not required.
Adr Compliance For Entity/Litegraph Changes ✅ Passed PR modifies only toast notification UI components and composables with no changes to src/lib/litegraph/, src/ecs/, or graph entity-related files.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/snackbar-toast-component

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 04/29/2026, 12:46:39 AM UTC

Links

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

🎭 Playwright: ✅ 1423 passed, 0 failed · 1 flaky

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

📦 Bundle: 5.23 MB gzip 🔴 +118 B

Details

Summary

  • Raw size: 24.1 MB baseline 24.1 MB — ⚪ 0 B
  • Gzip: 5.23 MB baseline 5.23 MB — 🔴 +118 B
  • Brotli: 4.05 MB baseline 4.05 MB — 🔴 +225 B
  • Bundles: 257 current • 257 baseline • 118 added / 118 removed

Category Glance
Vendor & Third-Party ⚪ 0 B (9.88 MB) · Other ⚪ 0 B (8.83 MB) · Data & Services ⚪ 0 B (3.04 MB) · Graph Workspace ⚪ 0 B (1.24 MB) · Panels & Settings ⚪ 0 B (484 kB) · Utilities & Hooks ⚪ 0 B (364 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-CbVBQ8gt.js (new) 22.5 kB 🔴 +22.5 kB 🔴 +7.99 kB 🔴 +6.85 kB
assets/index-wXNYevcc.js (removed) 22.5 kB 🟢 -22.5 kB 🟢 -7.98 kB 🟢 -6.87 kB

Status: 1 added / 1 removed

Graph Workspace — 1.24 MB (baseline 1.24 MB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-CDhT0xNw.js (new) 1.24 MB 🔴 +1.24 MB 🔴 +264 kB 🔴 +199 kB
assets/GraphView-D8sgf9IJ.js (removed) 1.24 MB 🟢 -1.24 MB 🟢 -264 kB 🟢 -199 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-Cfqkm4jz.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -3.41 kB 🟢 -2.92 kB
assets/CloudSurveyView-D9A1OzSn.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +3.41 kB 🔴 +2.92 kB
assets/CloudLoginView-2w6We3SJ.js (new) 12.2 kB 🔴 +12.2 kB 🔴 +3.43 kB 🔴 +3.04 kB
assets/CloudLoginView-Aeyo3Ojg.js (removed) 12.2 kB 🟢 -12.2 kB 🟢 -3.43 kB 🟢 -3.04 kB
assets/CloudSignupView-B126mPwq.js (removed) 9.94 kB 🟢 -9.94 kB 🟢 -2.93 kB 🟢 -2.58 kB
assets/CloudSignupView-CPpNHL8C.js (new) 9.94 kB 🔴 +9.94 kB 🔴 +2.93 kB 🔴 +2.58 kB
assets/UserCheckView-CcvmSCuT.js (removed) 9.07 kB 🟢 -9.07 kB 🟢 -2.33 kB 🟢 -2.04 kB
assets/UserCheckView-DkTOUXk6.js (new) 9.07 kB 🔴 +9.07 kB 🔴 +2.33 kB 🔴 +2.05 kB
assets/CloudLayoutView-BFBl8BLT.js (new) 7.7 kB 🔴 +7.7 kB 🔴 +2.43 kB 🔴 +2.13 kB
assets/CloudLayoutView-g5xSkWO-.js (removed) 7.7 kB 🟢 -7.7 kB 🟢 -2.44 kB 🟢 -2.13 kB
assets/CloudForgotPasswordView-Bvl8mafT.js (new) 6.11 kB 🔴 +6.11 kB 🔴 +2.17 kB 🔴 +1.92 kB
assets/CloudForgotPasswordView-DQJlI3Jj.js (removed) 6.11 kB 🟢 -6.11 kB 🟢 -2.17 kB 🟢 -1.9 kB
assets/CloudAuthTimeoutView-BPHPOo26.js (removed) 5.47 kB 🟢 -5.47 kB 🟢 -2 kB 🟢 -1.76 kB
assets/CloudAuthTimeoutView-DvF0nOrT.js (new) 5.47 kB 🔴 +5.47 kB 🔴 +2 kB 🔴 +1.77 kB
assets/CloudSubscriptionRedirectView-DOkOrXYR.js (new) 5.25 kB 🔴 +5.25 kB 🔴 +1.98 kB 🔴 +1.77 kB
assets/CloudSubscriptionRedirectView-vfo8RB4a.js (removed) 5.25 kB 🟢 -5.25 kB 🟢 -1.98 kB 🟢 -1.75 kB
assets/UserSelectView-BmXadlNP.js (new) 4.73 kB 🔴 +4.73 kB 🔴 +1.75 kB 🔴 +1.55 kB
assets/UserSelectView-BWseMvfZ.js (removed) 4.73 kB 🟢 -4.73 kB 🟢 -1.75 kB 🟢 -1.55 kB

Status: 9 added / 9 removed / 2 unchanged

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-BEV-5ol3.js (new) 46.7 kB 🔴 +46.7 kB 🔴 +9.61 kB 🔴 +8.54 kB
assets/KeybindingPanel-CIS29MES.js (removed) 46.7 kB 🟢 -46.7 kB 🟢 -9.61 kB 🟢 -8.53 kB
assets/SecretsPanel-BCFgAtr7.js (new) 22.9 kB 🔴 +22.9 kB 🔴 +5.54 kB 🔴 +4.87 kB
assets/SecretsPanel-BSWXF8yE.js (removed) 22.9 kB 🟢 -22.9 kB 🟢 -5.54 kB 🟢 -4.87 kB
assets/LegacyCreditsPanel-Cp0KV004.js (removed) 21.7 kB 🟢 -21.7 kB 🟢 -5.89 kB 🟢 -5.19 kB
assets/LegacyCreditsPanel-DHHLeFDS.js (new) 21.7 kB 🔴 +21.7 kB 🔴 +5.89 kB 🔴 +5.2 kB
assets/SubscriptionPanel-Bt5WEnqE.js (new) 19.9 kB 🔴 +19.9 kB 🔴 +5.06 kB 🔴 +4.44 kB
assets/SubscriptionPanel-BUS3ybbj.js (removed) 19.9 kB 🟢 -19.9 kB 🟢 -5.06 kB 🟢 -4.46 kB
assets/AboutPanel-BLWjmHAW.js (removed) 12 kB 🟢 -12 kB 🟢 -3.33 kB 🟢 -2.99 kB
assets/AboutPanel-Dirt-80I.js (new) 12 kB 🔴 +12 kB 🔴 +3.33 kB 🔴 +2.99 kB
assets/ExtensionPanel-bpuLEMX5.js (removed) 9.94 kB 🟢 -9.94 kB 🟢 -2.9 kB 🟢 -2.57 kB
assets/ExtensionPanel-cJ3o2KZd.js (new) 9.94 kB 🔴 +9.94 kB 🔴 +2.9 kB 🔴 +2.56 kB
assets/ServerConfigPanel-BgyyRB7o.js (new) 7.02 kB 🔴 +7.02 kB 🔴 +2.35 kB 🔴 +2.09 kB
assets/ServerConfigPanel-DGO74A_i.js (removed) 7.02 kB 🟢 -7.02 kB 🟢 -2.35 kB 🟢 -2.09 kB
assets/UserPanel-0LJGr4Pq.js (new) 6.73 kB 🔴 +6.73 kB 🔴 +2.23 kB 🔴 +1.96 kB
assets/UserPanel-BZ8QNjmi.js (removed) 6.73 kB 🟢 -6.73 kB 🟢 -2.22 kB 🟢 -1.95 kB
assets/cloudRemoteConfig-BXuJDGMo.js (removed) 2.02 kB 🟢 -2.02 kB 🟢 -973 B 🟢 -841 B
assets/cloudRemoteConfig-DgjYDCYx.js (new) 2.02 kB 🔴 +2.02 kB 🔴 +974 B 🔴 +835 B
assets/refreshRemoteConfig-cIshcIRw.js (removed) 1.45 kB 🟢 -1.45 kB 🟢 -646 B 🟢 -549 B
assets/refreshRemoteConfig-D-lMMFhr.js (new) 1.45 kB 🔴 +1.45 kB 🔴 +647 B 🔴 +548 B

Status: 10 added / 10 removed / 11 unchanged

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-C2WwsQkQ.js (removed) 3.57 kB 🟢 -3.57 kB 🟢 -1.26 kB 🟢 -1.07 kB
assets/auth-t5EcKIPE.js (new) 3.57 kB 🔴 +3.57 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/SignUpForm-DntKF-01.js (new) 3.16 kB 🔴 +3.16 kB 🔴 +1.29 kB 🔴 +1.15 kB
assets/SignUpForm-j0PqTHAG.js (removed) 3.16 kB 🟢 -3.16 kB 🟢 -1.29 kB 🟢 -1.15 kB
assets/UpdatePasswordContent-Dxfg6CgH.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.29 kB 🔴 +1.14 kB
assets/UpdatePasswordContent-DXI6i7Eg.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.29 kB 🟢 -1.15 kB
assets/authStore-fzpvXCTx.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -552 B 🟢 -500 B
assets/authStore-GAIwd3XM.js (new) 1.16 kB 🔴 +1.16 kB 🔴 +554 B 🔴 +497 B
assets/auth-Bx1s2MYW.js (removed) 348 B 🟢 -348 B 🟢 -217 B 🟢 -206 B
assets/auth-BzLwXhRk.js (new) 348 B 🔴 +348 B 🔴 +217 B 🔴 +210 B

Status: 5 added / 5 removed / 2 unchanged

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-BotIah8Y.js (removed) 86.9 kB 🟢 -86.9 kB 🟢 -18.8 kB 🟢 -16 kB
assets/ComfyHubPublishDialog-CuM5C2IB.js (new) 86.9 kB 🔴 +86.9 kB 🔴 +18.8 kB 🔴 +16.1 kB
assets/useShareDialog-aHCH5HyC.js (new) 23.7 kB 🔴 +23.7 kB 🔴 +5.77 kB 🔴 +5.11 kB
assets/useShareDialog-e1y9g_Bg.js (removed) 23.7 kB 🟢 -23.7 kB 🟢 -5.77 kB 🟢 -5.11 kB
assets/ComfyHubPublishDialog-DPxF_jxs.js (removed) 1.32 kB 🟢 -1.32 kB 🟢 -613 B 🟢 -556 B
assets/ComfyHubPublishDialog-tt9orhgS.js (new) 1.32 kB 🔴 +1.32 kB 🔴 +615 B 🔴 +550 B
assets/useSubscriptionDialog-CEji6_mh.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +545 B 🔴 +482 B
assets/useSubscriptionDialog-D62PsUHt.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -543 B 🟢 -482 B

Status: 4 added / 4 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-B_lAHOtR.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.79 kB 🟢 -3.38 kB
assets/ComfyQueueButton-B6A3eKKw.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/useTerminalTabs-jKCEZsUh.js (new) 11 kB 🔴 +11 kB 🔴 +3.71 kB 🔴 +3.28 kB
assets/useTerminalTabs-wAbBheK1.js (removed) 11 kB 🟢 -11 kB 🟢 -3.71 kB 🟢 -3.27 kB
assets/SubscribeButton-4mAvUt1Y.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.05 kB 🔴 +947 B
assets/SubscribeButton-Cjx3-_lQ.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.05 kB 🟢 -930 B
assets/cloudFeedbackTopbarButton-Bt3ocrPD.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -930 B 🟢 -815 B
assets/cloudFeedbackTopbarButton-D0zwF5vd.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +932 B 🔴 +818 B
assets/ComfyQueueButton-B69HyJ1c.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -581 B 🟢 -519 B
assets/ComfyQueueButton-BOYdLRkV.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +580 B 🔴 +515 B

Status: 5 added / 5 removed / 8 unchanged

Data & Services — 3.04 MB (baseline 3.04 MB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-Cl5waVzF.js (removed) 1.97 MB 🟢 -1.97 MB 🟢 -454 kB 🟢 -344 kB
assets/dialogService-Cn0Q9eGv.js (new) 1.97 MB 🔴 +1.97 MB 🔴 +454 kB 🔴 +344 kB
assets/api-CkkclUPt.js (removed) 894 kB 🟢 -894 kB 🟢 -214 kB 🟢 -168 kB
assets/api-KmzpQ9wM.js (new) 894 kB 🔴 +894 kB 🔴 +214 kB 🔴 +168 kB
assets/load3dService-BPzjfOOo.js (removed) 114 kB 🟢 -114 kB 🟢 -24.8 kB 🟢 -21 kB
assets/load3dService-D4xey1x-.js (new) 114 kB 🔴 +114 kB 🔴 +24.8 kB 🔴 +21 kB
assets/workflowShareService-B6Qw4OIv.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.89 kB 🟢 -4.33 kB
assets/workflowShareService-BwJh6NBs.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.89 kB 🔴 +4.33 kB
assets/keybindingService-DKioF23-.js (new) 13.8 kB 🔴 +13.8 kB 🔴 +3.67 kB 🔴 +3.22 kB
assets/keybindingService-DL8OOVja.js (removed) 13.8 kB 🟢 -13.8 kB 🟢 -3.67 kB 🟢 -3.22 kB
assets/releaseStore-BD6K6s3v.js (new) 8.12 kB 🔴 +8.12 kB 🔴 +2.28 kB 🔴 +2 kB
assets/releaseStore-DrRfyg9s.js (removed) 8.12 kB 🟢 -8.12 kB 🟢 -2.28 kB 🟢 -2 kB
assets/userStore-BrkCizEG.js (new) 2.24 kB 🔴 +2.24 kB 🔴 +870 B 🔴 +762 B
assets/userStore-BuT_qVEa.js (removed) 2.24 kB 🟢 -2.24 kB 🟢 -870 B 🟢 -760 B
assets/audioService-C0JQBaAr.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +878 B 🔴 +755 B
assets/audioService-DLuuMOpB.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -876 B 🟢 -761 B
assets/releaseStore-CPb82xSY.js (new) 1.16 kB 🔴 +1.16 kB 🔴 +548 B 🔴 +495 B
assets/releaseStore-DlH2PtXb.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -548 B 🟢 -491 B
assets/workflowDraftStore-Dhv0UcK_.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +543 B 🔴 +483 B
assets/workflowDraftStore-DUDf2uNc.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -542 B 🟢 -482 B
assets/dialogService-B_CJ8gJT.js (new) 1.13 kB 🔴 +1.13 kB 🔴 +536 B 🔴 +483 B
assets/dialogService-CbuYMOTR.js (removed) 1.13 kB 🟢 -1.13 kB 🟢 -535 B 🟢 -481 B
assets/settingStore-CKCOB6os.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -537 B 🟢 -481 B
assets/settingStore-DwTlYZJN.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +539 B 🔴 +483 B
assets/assetsStore-CKjzK_ta.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -537 B 🟢 -482 B
assets/assetsStore-CTUb1NTN.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +539 B 🔴 +482 B

Status: 13 added / 13 removed / 4 unchanged

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-DKeZaAIm.js (new) 233 kB 🔴 +233 kB 🔴 +51.8 kB 🔴 +42.3 kB
assets/useConflictDetection-X8WTKdIP.js (removed) 233 kB 🟢 -233 kB 🟢 -51.8 kB 🟢 -42.2 kB
assets/useLoad3d-Bqleb5BE.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -5.09 kB 🟢 -4.51 kB
assets/useLoad3d-CL6AMKMd.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +5.09 kB 🔴 +4.49 kB
assets/useLoad3dViewer-CTKDLl26.js (removed) 20.8 kB 🟢 -20.8 kB 🟢 -4.91 kB 🟢 -4.3 kB
assets/useLoad3dViewer-CYX-JZaR.js (new) 20.8 kB 🔴 +20.8 kB 🔴 +4.91 kB 🔴 +4.31 kB
assets/useFeatureFlags-bh9sQe0Y.js (removed) 5.95 kB 🟢 -5.95 kB 🟢 -1.79 kB 🟢 -1.52 kB
assets/useFeatureFlags-DtWoDBtz.js (new) 5.95 kB 🔴 +5.95 kB 🔴 +1.8 kB 🔴 +1.52 kB
assets/useCopyToClipboard-DohQfmYr.js (new) 5.29 kB 🔴 +5.29 kB 🔴 +1.86 kB 🔴 +1.57 kB
assets/useCopyToClipboard-GY2eRfO0.js (removed) 5.29 kB 🟢 -5.29 kB 🟢 -1.86 kB 🟢 -1.58 kB
assets/useWorkspaceUI-B7PYi1At.js (new) 3.34 kB 🔴 +3.34 kB 🔴 +981 B 🔴 +808 B
assets/useWorkspaceUI-C1ugrsbB.js (removed) 3.34 kB 🟢 -3.34 kB 🟢 -981 B 🟢 -809 B
assets/subscriptionCheckoutUtil-C36s8XOD.js (new) 3.31 kB 🔴 +3.31 kB 🔴 +1.36 kB 🔴 +1.18 kB
assets/subscriptionCheckoutUtil-ZG1MET1L.js (removed) 3.31 kB 🟢 -3.31 kB 🟢 -1.36 kB 🟢 -1.19 kB
assets/assetPreviewUtil-bzOvYkG2.js (new) 2.27 kB 🔴 +2.27 kB 🔴 +961 B 🔴 +839 B
assets/assetPreviewUtil-XnQ-7OZ1.js (removed) 2.27 kB 🟢 -2.27 kB 🟢 -959 B 🟢 -839 B
assets/useUpstreamValue-CNu5a3W8.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -805 B 🟢 -713 B
assets/useUpstreamValue-CVlixEld.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +805 B 🔴 +713 B
assets/useLoad3d-CBdpKWFj.js (removed) 1.3 kB 🟢 -1.3 kB 🟢 -606 B 🟢 -566 B
assets/useLoad3d-DfQi4CPi.js (new) 1.3 kB 🔴 +1.3 kB 🔴 +607 B 🔴 +555 B
assets/useLoad3dViewer-ClZ_ADdn.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +573 B 🔴 +515 B
assets/useLoad3dViewer-DZ9taU2R.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -572 B 🟢 -518 B
assets/useCurrentUser-CE6SVxkD.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +540 B 🔴 +481 B
assets/useCurrentUser-D8h9PwWe.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -538 B 🟢 -482 B
assets/useWorkspaceSwitch-64XForik.js (removed) 747 B 🟢 -747 B 🟢 -384 B 🟢 -329 B
assets/useWorkspaceSwitch-CYIoT5Ov.js (new) 747 B 🔴 +747 B 🔴 +383 B 🔴 +323 B

Status: 13 added / 13 removed / 18 unchanged

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

External libraries and shared vendor chunks

Status: 16 unchanged

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

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-BNa_KfLn.js (new) 76.6 kB 🔴 +76.6 kB 🔴 +19.8 kB 🔴 +16.9 kB
assets/core-BugQIaCy.js (removed) 76.6 kB 🟢 -76.6 kB 🟢 -19.8 kB 🟢 -16.9 kB
assets/groupNode-DZ5GRzsv.js (new) 74.9 kB 🔴 +74.9 kB 🔴 +18.7 kB 🔴 +16.5 kB
assets/groupNode-KJpWtlg6.js (removed) 74.9 kB 🟢 -74.9 kB 🟢 -18.7 kB 🟢 -16.5 kB
assets/WidgetSelect-BRJpxoef.js (new) 67.2 kB 🔴 +67.2 kB 🔴 +14.6 kB 🔴 +12.7 kB
assets/WidgetSelect-DJ-wfw-t.js (removed) 67.2 kB 🟢 -67.2 kB 🟢 -14.6 kB 🟢 -12.6 kB
assets/SubscriptionRequiredDialogContentWorkspace-C8LY6EJA.js (new) 49.3 kB 🔴 +49.3 kB 🔴 +9.42 kB 🔴 +8.04 kB
assets/SubscriptionRequiredDialogContentWorkspace-ToLT91i4.js (removed) 49.3 kB 🟢 -49.3 kB 🟢 -9.42 kB 🟢 -8.04 kB
assets/Load3DControls-BJQEvZG8.js (removed) 46.1 kB 🟢 -46.1 kB 🟢 -7.51 kB 🟢 -6.54 kB
assets/Load3DControls-DF5QZ0o0.js (new) 46.1 kB 🔴 +46.1 kB 🔴 +7.51 kB 🔴 +6.54 kB
assets/WidgetPainter-BAP7lhAG.js (new) 34 kB 🔴 +34 kB 🔴 +8.29 kB 🔴 +7.32 kB
assets/WidgetPainter-C2De9FZl.js (removed) 34 kB 🟢 -34 kB 🟢 -8.29 kB 🟢 -7.35 kB
assets/WorkspacePanelContent-B2-I1UOD.js (removed) 32.8 kB 🟢 -32.8 kB 🟢 -6.99 kB 🟢 -6.18 kB
assets/WorkspacePanelContent-Vdl7WBTo.js (new) 32.8 kB 🔴 +32.8 kB 🔴 +7 kB 🔴 +6.18 kB
assets/Load3dViewerContent-D15X-HYp.js (new) 28 kB 🔴 +28 kB 🔴 +5.85 kB 🔴 +5.07 kB
assets/Load3dViewerContent-E5BoBos6.js (removed) 28 kB 🟢 -28 kB 🟢 -5.85 kB 🟢 -5.07 kB
assets/SubscriptionRequiredDialogContent-BAbH7X8D.js (new) 27.5 kB 🔴 +27.5 kB 🔴 +6.97 kB 🔴 +6.19 kB
assets/SubscriptionRequiredDialogContent-CYmjsCIr.js (removed) 27.5 kB 🟢 -27.5 kB 🟢 -6.96 kB 🟢 -6.17 kB
assets/WidgetImageCrop-d88FXVwP.js (new) 24.3 kB 🔴 +24.3 kB 🔴 +6.18 kB 🔴 +5.43 kB
assets/WidgetImageCrop-DXZaRi61.js (removed) 24.3 kB 🟢 -24.3 kB 🟢 -6.18 kB 🟢 -5.44 kB
assets/SubscriptionPanelContentWorkspace-CoGYXOzn.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.18 kB 🔴 +4.56 kB
assets/SubscriptionPanelContentWorkspace-CrKLK_cv.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.17 kB 🟢 -4.56 kB
assets/SignInContent-BFEqJ4U0.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +5.36 kB 🔴 +4.69 kB
assets/SignInContent-XTQtxWK2.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -5.35 kB 🟢 -4.68 kB
assets/CurrentUserPopoverWorkspace-BL2or4nF.js (removed) 20.5 kB 🟢 -20.5 kB 🟢 -4.89 kB 🟢 -4.39 kB
assets/CurrentUserPopoverWorkspace-BZH41Xya.js (new) 20.5 kB 🔴 +20.5 kB 🔴 +4.9 kB 🔴 +4.38 kB
assets/WidgetInputNumber-e0bMfKQH.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.84 kB 🟢 -4.29 kB
assets/WidgetInputNumber-kCOF2D20.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.84 kB 🔴 +4.29 kB
assets/Load3D-BkeqofDa.js (new) 18.5 kB 🔴 +18.5 kB 🔴 +4.39 kB 🔴 +3.83 kB
assets/Load3D-DjuEufQ-.js (removed) 18.5 kB 🟢 -18.5 kB 🟢 -4.39 kB 🟢 -3.83 kB
assets/WidgetRecordAudio-BavMEMJw.js (new) 17.4 kB 🔴 +17.4 kB 🔴 +5 kB 🔴 +4.47 kB
assets/WidgetRecordAudio-BfHZb9mA.js (removed) 17.4 kB 🟢 -17.4 kB 🟢 -5 kB 🟢 -4.48 kB
assets/WidgetRange-DzqYwnGr.js (removed) 17 kB 🟢 -17 kB 🟢 -4.59 kB 🟢 -4.11 kB
assets/WidgetRange-vBDJb_YO.js (new) 17 kB 🔴 +17 kB 🔴 +4.6 kB 🔴 +4.13 kB
assets/load3d-CmAB61IX.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +4.57 kB 🔴 +3.94 kB
assets/load3d-qoWvG0Ui.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -4.57 kB 🟢 -3.94 kB
assets/WaveAudioPlayer-BiUV7ISW.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.69 kB 🔴 +3.23 kB
assets/WaveAudioPlayer-CGVnZ3bM.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.69 kB 🟢 -3.23 kB
assets/WidgetCurve-DOay-2p1.js (new) 12.1 kB 🔴 +12.1 kB 🔴 +3.91 kB 🔴 +3.54 kB
assets/WidgetCurve-jKWkpZZN.js (removed) 12.1 kB 🟢 -12.1 kB 🟢 -3.91 kB 🟢 -3.55 kB
assets/TeamWorkspacesDialogContent-BBbKxCXO.js (removed) 11.3 kB 🟢 -11.3 kB 🟢 -3.4 kB 🟢 -3.03 kB
assets/TeamWorkspacesDialogContent-DzkrbSzD.js (new) 11.3 kB 🔴 +11.3 kB 🔴 +3.41 kB 🔴 +3.03 kB
assets/nodeTemplates-Clfe15il.js (new) 9.81 kB 🔴 +9.81 kB 🔴 +3.47 kB 🔴 +3.06 kB
assets/nodeTemplates-DtKqkgK1.js (removed) 9.81 kB 🟢 -9.81 kB 🟢 -3.46 kB 🟢 -3.06 kB
assets/NightlySurveyController-bWxk_79i.js (new) 8.99 kB 🔴 +8.99 kB 🔴 +3.15 kB 🔴 +2.78 kB
assets/NightlySurveyController-Ci3xJs0J.js (removed) 8.99 kB 🟢 -8.99 kB 🟢 -3.14 kB 🟢 -2.78 kB
assets/InviteMemberDialogContent-CXWQjmLW.js (removed) 7.91 kB 🟢 -7.91 kB 🟢 -2.51 kB 🟢 -2.21 kB
assets/InviteMemberDialogContent-D3VfAwYJ.js (new) 7.91 kB 🔴 +7.91 kB 🔴 +2.51 kB 🔴 +2.2 kB
assets/Load3DConfiguration-Dg753SNR.js (removed) 7.82 kB 🟢 -7.82 kB 🟢 -2.31 kB 🟢 -2.02 kB
assets/Load3DConfiguration-IfRheat_.js (new) 7.82 kB 🔴 +7.82 kB 🔴 +2.31 kB 🔴 +2.02 kB
assets/onboardingCloudRoutes-CM0x5e9l.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -2.06 kB 🟢 -1.77 kB
assets/onboardingCloudRoutes-DdIYymcS.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +2.05 kB 🔴 +1.77 kB
assets/CreateWorkspaceDialogContent-ByOAVGDo.js (removed) 6.12 kB 🟢 -6.12 kB 🟢 -2.22 kB 🟢 -1.93 kB
assets/CreateWorkspaceDialogContent-Dnkn0Sfg.js (new) 6.12 kB 🔴 +6.12 kB 🔴 +2.22 kB 🔴 +1.94 kB
assets/WidgetWithControl-CxClhFWl.js (new) 6.02 kB 🔴 +6.02 kB 🔴 +2.4 kB 🔴 +2.15 kB
assets/WidgetWithControl-tAqDU4uj.js (removed) 6.02 kB 🟢 -6.02 kB 🟢 -2.4 kB 🟢 -2.13 kB
assets/FreeTierDialogContent-BAMw5vlC.js (new) 5.99 kB 🔴 +5.99 kB 🔴 +2.12 kB 🔴 +1.87 kB
assets/FreeTierDialogContent-DzlOKVRL.js (removed) 5.99 kB 🟢 -5.99 kB 🟢 -2.12 kB 🟢 -1.88 kB
assets/EditWorkspaceDialogContent-BiH9Vwnr.js (new) 5.92 kB 🔴 +5.92 kB 🔴 +2.18 kB 🔴 +1.91 kB
assets/EditWorkspaceDialogContent-uslFHf7A.js (removed) 5.92 kB 🟢 -5.92 kB 🟢 -2.18 kB 🟢 -1.93 kB
assets/WidgetTextarea-BCd0BZ8E.js (removed) 5.73 kB 🟢 -5.73 kB 🟢 -2.26 kB 🟢 -1.99 kB
assets/WidgetTextarea-BufOjzzJ.js (new) 5.73 kB 🔴 +5.73 kB 🔴 +2.26 kB 🔴 +2 kB
assets/Preview3d-C9Wohs8-.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.91 kB 🔴 +1.67 kB
assets/Preview3d-m4nAnLrO.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.91 kB 🟢 -1.68 kB
assets/ValueControlPopover-4iSvRChw.js (removed) 5.5 kB 🟢 -5.5 kB 🟢 -2 kB 🟢 -1.8 kB
assets/ValueControlPopover-Cd7jWU-n.js (new) 5.5 kB 🔴 +5.5 kB 🔴 +2 kB 🔴 +1.79 kB
assets/CancelSubscriptionDialogContent-DqhJlgex.js (removed) 5.46 kB 🟢 -5.46 kB 🟢 -2.04 kB 🟢 -1.79 kB
assets/CancelSubscriptionDialogContent-M7DQOsM5.js (new) 5.46 kB 🔴 +5.46 kB 🔴 +2.04 kB 🔴 +1.79 kB
assets/DeleteWorkspaceDialogContent-B5oVc_Wq.js (new) 4.82 kB 🔴 +4.82 kB 🔴 +1.86 kB 🔴 +1.62 kB
assets/DeleteWorkspaceDialogContent-BFAYlM1J.js (removed) 4.82 kB 🟢 -4.82 kB 🟢 -1.86 kB 🟢 -1.62 kB
assets/LeaveWorkspaceDialogContent-C_qDFsAj.js (new) 4.65 kB 🔴 +4.65 kB 🔴 +1.81 kB 🔴 +1.57 kB
assets/LeaveWorkspaceDialogContent-D_OrJ3dp.js (removed) 4.65 kB 🟢 -4.65 kB 🟢 -1.8 kB 🟢 -1.57 kB
assets/RemoveMemberDialogContent-B8_TQDfH.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.76 kB 🔴 +1.54 kB
assets/RemoveMemberDialogContent-CAEeTk6c.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.76 kB 🟢 -1.54 kB
assets/RevokeInviteDialogContent-Bs9f0azi.js (removed) 4.54 kB 🟢 -4.54 kB 🟢 -1.77 kB 🟢 -1.56 kB
assets/RevokeInviteDialogContent-DWXXAMhb.js (new) 4.54 kB 🔴 +4.54 kB 🔴 +1.77 kB 🔴 +1.55 kB
assets/tierBenefits-CTfFUr_1.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.57 kB 🟢 -1.36 kB
assets/tierBenefits-DvT6LqMU.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.57 kB 🔴 +1.36 kB
assets/InviteMemberUpsellDialogContent-BI8gDTCg.js (new) 4.44 kB 🔴 +4.44 kB 🔴 +1.64 kB 🔴 +1.45 kB
assets/InviteMemberUpsellDialogContent-C8n2LvLU.js (removed) 4.44 kB 🟢 -4.44 kB 🟢 -1.63 kB 🟢 -1.44 kB
assets/cloudSessionCookie-EnHEbrdd.js (new) 4.28 kB 🔴 +4.28 kB 🔴 +1.56 kB 🔴 +1.36 kB
assets/cloudSessionCookie-ko984j3c.js (removed) 4.28 kB 🟢 -4.28 kB 🟢 -1.56 kB 🟢 -1.36 kB
assets/Media3DTop-C38v2K0S.js (removed) 4.01 kB 🟢 -4.01 kB 🟢 -1.7 kB 🟢 -1.5 kB
assets/Media3DTop-DgzV_he1.js (new) 4.01 kB 🔴 +4.01 kB 🔴 +1.7 kB 🔴 +1.5 kB
assets/saveMesh-oZ0RuCWv.js (new) 3.98 kB 🔴 +3.98 kB 🔴 +1.74 kB 🔴 +1.52 kB
assets/saveMesh-WDWDrj55.js (removed) 3.98 kB 🟢 -3.98 kB 🟢 -1.73 kB 🟢 -1.53 kB
assets/GlobalToast-CFnhGQo2.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.11 kB
assets/GlobalToast-DkLWEWn2.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.26 kB 🟢 -1.11 kB
assets/CloudRunButtonWrapper-BbTviYxG.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -1 kB 🟢 -891 B
assets/CloudRunButtonWrapper-DxJWTont.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +1 kB 🔴 +894 B
assets/SubscribeToRun-D-tu_urw.js (removed) 2.13 kB 🟢 -2.13 kB 🟢 -981 B 🟢 -859 B
assets/SubscribeToRun-DFPRN_S3.js (new) 2.13 kB 🔴 +2.13 kB 🔴 +983 B 🔴 +882 B
assets/MediaAudioTop-AbY8i0z_.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -1.01 kB 🟢 -857 B
assets/MediaAudioTop-DBW0gFWI.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +1.01 kB 🔴 +862 B
assets/cloudBadges-CCee09ri.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +962 B 🔴 +833 B
assets/cloudBadges-CoCUy1ea.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -958 B 🟢 -831 B
assets/cloudSubscription-BMEsrcdu.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +884 B 🔴 +763 B
assets/cloudSubscription-s15MvpLL.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -883 B 🟢 -770 B
assets/graphHasMissingNodes-COdq1LQd.js (removed) 1.84 kB 🟢 -1.84 kB 🟢 -861 B 🟢 -775 B
assets/graphHasMissingNodes-DN3Cdxgi.js (new) 1.84 kB 🔴 +1.84 kB 🔴 +860 B 🔴 +756 B
assets/Load3D-Bre6ANsd.js (removed) 1.55 kB 🟢 -1.55 kB 🟢 -696 B 🟢 -623 B
assets/Load3D-D6WMAwN9.js (new) 1.55 kB 🔴 +1.55 kB 🔴 +697 B 🔴 +621 B
assets/previousFullPath-CKuGX6hS.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +694 B 🔴 +598 B
assets/previousFullPath-L-T9T-xi.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -694 B 🟢 -596 B
assets/nightlyBadges-BxqDtlEG.js (new) 1.46 kB 🔴 +1.46 kB 🔴 +730 B 🔴 +667 B
assets/nightlyBadges-CwWFIw59.js (removed) 1.46 kB 🟢 -1.46 kB 🟢 -732 B 🟢 -667 B
assets/Load3dViewerContent-BgffHnVj.js (new) 1.43 kB 🔴 +1.43 kB 🔴 +649 B 🔴 +580 B
assets/Load3dViewerContent-CH0WRDEH.js (removed) 1.43 kB 🟢 -1.43 kB 🟢 -649 B 🟢 -578 B
assets/SubscriptionPanelContentWorkspace-9BrjnFEK.js (new) 1.32 kB 🔴 +1.32 kB 🔴 +603 B 🔴 +529 B
assets/SubscriptionPanelContentWorkspace-BS3S2Zj_.js (removed) 1.32 kB 🟢 -1.32 kB 🟢 -601 B 🟢 -529 B
assets/WidgetLegacy-BQifp30B.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +551 B 🔴 +488 B
assets/WidgetLegacy-D3zR4cRG.js (removed) 1.15 kB 🟢 -1.15 kB 🟢 -549 B 🟢 -487 B
assets/changeTracker-D7AsGeMC.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -538 B 🟢 -473 B
assets/changeTracker-DtCO3k8s.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +540 B 🔴 +471 B

Status: 57 added / 57 removed / 78 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.4 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 48.9 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.7 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.5 MB heap
large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 60.1 MB heap
large-graph-pan: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.3 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.4 MB heap
minimap-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 69.8 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.8 MB heap
subgraph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 63.7 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.6 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 72.5 MB heap
vue-large-graph-idle: · 56.3 avg FPS · 30.1 P5 FPS ❌ (target: ≥52) · 0ms TBT · 165.8 MB heap
vue-large-graph-pan: · 58.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 16ms TBT · 158.0 MB heap
workflow-execution: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.5 MB heap

No regressions detected.

All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms -0% z=-0.9
canvas-idle: p95 frame time 17ms 17ms -1%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 10ms 9ms -14% z=-2.2
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 11 9 -18% z=-3.8
canvas-idle: task duration 434ms 343ms -21% z=-1.6
canvas-idle: script duration 27ms 15ms -43% z=-4.4
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 63.6 MB 67.4 MB +6%
canvas-idle: DOM nodes 22 18 -18% z=-3.6
canvas-idle: event listeners 6 4 -33% z=-1.6
canvas-mouse-sweep: avg frame time 17ms 17ms -0% z=-1.5
canvas-mouse-sweep: p95 frame time 17ms 17ms +0%
canvas-mouse-sweep: layout duration 4ms 3ms -11% z=-0.7
canvas-mouse-sweep: style recalc duration 39ms 46ms +18% z=1.0
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 76 75 -1% z=-1.6
canvas-mouse-sweep: task duration 833ms 818ms -2% z=-0.8
canvas-mouse-sweep: script duration 124ms 133ms +8% z=-0.4
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 45.0 MB 48.9 MB +9%
canvas-mouse-sweep: DOM nodes -261 -258 -1% z=-123.8
canvas-mouse-sweep: event listeners -127 -129 +2% z=-33.0
canvas-zoom-sweep: avg frame time 17ms 17ms -0% z=-1.1
canvas-zoom-sweep: p95 frame time 17ms 17ms +0%
canvas-zoom-sweep: layout duration 1ms 1ms +4% z=1.0
canvas-zoom-sweep: style recalc duration 18ms 18ms -1% z=-1.0
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 31 31 +0% z=-0.6
canvas-zoom-sweep: task duration 317ms 310ms -2% z=-0.8
canvas-zoom-sweep: script duration 29ms 26ms -9% z=-0.3
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 47.3 MB 68.7 MB +45%
canvas-zoom-sweep: DOM nodes 79 78 -1% z=-1.6
canvas-zoom-sweep: event listeners 21 19 -10% z=-0.9
dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
dom-widget-clipping: p95 frame time 17ms 17ms -1%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 8ms 9ms +8% z=-1.3
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 12 13 +8% z=-0.2
dom-widget-clipping: task duration 338ms 345ms +2% z=-1.2
dom-widget-clipping: script duration 59ms 63ms +8% z=-1.5
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 53.2 MB 51.5 MB -3%
dom-widget-clipping: DOM nodes 20 22 +10% z=-0.1
dom-widget-clipping: event listeners 2 0 -100% variance too high
large-graph-idle: avg frame time 17ms 17ms +0% z=-0.2
large-graph-idle: p95 frame time 17ms 17ms +1%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 8ms -14% z=-4.1
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 10 8 -20% z=-11.5
large-graph-idle: task duration 576ms 522ms -9% z=-0.4
large-graph-idle: script duration 100ms 90ms -10% z=-1.2
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 61.9 MB 60.1 MB -3%
large-graph-idle: DOM nodes -259 -264 +2% z=-319.4
large-graph-idle: event listeners -127 -127 +0% z=-25.1
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 18ms 17ms -6% z=-0.6
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 68 67 -1% z=-4.0
large-graph-pan: task duration 1109ms 1115ms +1% z=0.8
large-graph-pan: script duration 390ms 409ms +5% z=0.0
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 62.7 MB 61.3 MB -2%
large-graph-pan: DOM nodes -264 -262 -1% z=-170.3
large-graph-pan: event listeners -125 -125 +0% z=-156.9
large-graph-zoom: avg frame time 17ms 17ms +0%
large-graph-zoom: p95 frame time 17ms 17ms -1%
large-graph-zoom: layout duration 7ms 7ms -3%
large-graph-zoom: style recalc duration 19ms 18ms -6%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 65 -2%
large-graph-zoom: task duration 1291ms 1313ms +2%
large-graph-zoom: script duration 462ms 480ms +4%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 64.3 MB 65.4 MB +2%
large-graph-zoom: DOM nodes -267 -268 +0%
large-graph-zoom: event listeners -123 -123 +0%
minimap-idle: avg frame time 17ms 17ms +0% z=0.1
minimap-idle: p95 frame time 17ms 17ms +0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 8ms 9ms +15% z=-0.5
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 8 9 +13% z=-0.8
minimap-idle: task duration 547ms 521ms -5% z=-0.1
minimap-idle: script duration 94ms 89ms -6% z=-1.0
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 64.5 MB 69.8 MB +8%
minimap-idle: DOM nodes -262 -260 -1% z=-204.1
minimap-idle: event listeners -127 -127 +0% z=-199.3
subgraph-dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
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 11ms +8% z=-1.3
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 339ms 360ms +6% z=-1.0
subgraph-dom-widget-clipping: script duration 112ms 121ms +8% z=-1.1
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 51.8 MB 51.8 MB -0%
subgraph-dom-widget-clipping: DOM nodes 18 20 +11% z=-1.9
subgraph-dom-widget-clipping: event listeners 8 6 -25% z=-1.7
subgraph-idle: avg frame time 17ms 17ms +0% z=0.4
subgraph-idle: p95 frame time 17ms 17ms -0%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 9ms 10ms +12% z=-0.9
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 10 10 +0% z=-1.4
subgraph-idle: task duration 373ms 389ms +4% z=0.6
subgraph-idle: script duration 20ms 19ms -5% z=-0.5
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 64.6 MB 63.7 MB -1%
subgraph-idle: DOM nodes 20 19 -5% z=-1.9
subgraph-idle: event listeners 6 4 -33% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.4
subgraph-mouse-sweep: p95 frame time 17ms 17ms +1%
subgraph-mouse-sweep: layout duration 5ms 5ms -7% z=-0.5
subgraph-mouse-sweep: style recalc duration 40ms 38ms -5% z=-1.4
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 76 77 +1% z=-1.6
subgraph-mouse-sweep: task duration 710ms 688ms -3% z=-1.1
subgraph-mouse-sweep: script duration 92ms 97ms +6% z=-0.5
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 56.6 MB 56.6 MB +0%
subgraph-mouse-sweep: DOM nodes 61 64 +5% z=-1.3
subgraph-mouse-sweep: event listeners 4 4 +0% variance too high
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 52ms 52ms -1%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 250 249 -0%
viewport-pan-sweep: task duration 3709ms 3678ms -1%
viewport-pan-sweep: script duration 1258ms 1249ms -1%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 64.8 MB 72.5 MB +12%
viewport-pan-sweep: DOM nodes -258 -262 +2%
viewport-pan-sweep: event listeners -111 -111 +0%
vue-large-graph-idle: avg frame time 17ms 18ms +3%
vue-large-graph-idle: p95 frame time 17ms 33ms +98%
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 10867ms 12424ms +14%
vue-large-graph-idle: script duration 601ms 602ms +0%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 160.2 MB 165.8 MB +3%
vue-large-graph-idle: DOM nodes -9850 -9850 +0%
vue-large-graph-idle: event listeners -23969 -23957 -0%
vue-large-graph-pan: avg frame time 18ms 17ms -3%
vue-large-graph-pan: p95 frame time 17ms 17ms +0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 16ms 17ms +1%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 64 67 +5%
vue-large-graph-pan: task duration 12480ms 14416ms +16%
vue-large-graph-pan: script duration 865ms 864ms -0%
vue-large-graph-pan: TBT 0ms 16ms
vue-large-graph-pan: heap used 163.7 MB 158.0 MB -4%
vue-large-graph-pan: DOM nodes -9850 -9850 +0%
vue-large-graph-pan: event listeners -23959 -23983 +0%
workflow-execution: avg frame time 17ms 17ms +0% z=0.6
workflow-execution: p95 frame time 17ms 17ms -0%
workflow-execution: layout duration 2ms 1ms -10% z=-1.3
workflow-execution: style recalc duration 21ms 23ms +12% z=-0.4
workflow-execution: layout count 5 5 +0% z=0.1
workflow-execution: style recalc count 16 17 +6% z=-0.4
workflow-execution: task duration 131ms 138ms +5% z=1.4
workflow-execution: script duration 23ms 27ms +18% z=-0.7
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 51.6 MB 51.5 MB -0%
workflow-execution: DOM nodes 156 156 +0% z=-0.7
workflow-execution: event listeners 71 69 -3% z=3.9
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-04-29T00:59:05.157Z",
  "gitSha": "abc86f97c613eb1945285dd16bc7d6b5a4f8e7ec",
  "branch": "feat/snackbar-toast-component",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2013.261999999969,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 7.601,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 455.18100000000004,
      "heapDeltaBytes": 22477796,
      "heapUsedBytes": 70676364,
      "domNodes": 14,
      "jsHeapTotalBytes": 14417920,
      "scriptDurationMs": 25.020999999999997,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-idle",
      "durationMs": 2012.4889999999596,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.046999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 343.3639999999999,
      "heapDeltaBytes": 2005572,
      "heapUsedBytes": 68588780,
      "domNodes": 18,
      "jsHeapTotalBytes": 20709376,
      "scriptDurationMs": 15.315999999999995,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-idle",
      "durationMs": 2003.011000000015,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.889,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 334.517,
      "heapDeltaBytes": 23067696,
      "heapUsedBytes": 71422368,
      "domNodes": 19,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 15.477999999999998,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1931.7990000000123,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 47.416,
      "layouts": 12,
      "layoutDurationMs": 3.7739999999999996,
      "taskDurationMs": 818.0000000000001,
      "heapDeltaBytes": -2138284,
      "heapUsedBytes": 64027316,
      "domNodes": 59,
      "jsHeapTotalBytes": 20971520,
      "scriptDurationMs": 134.712,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1772.803999999951,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 35.422999999999995,
      "layouts": 12,
      "layoutDurationMs": 3.3379999999999996,
      "taskDurationMs": 765.7389999999999,
      "heapDeltaBytes": -14957908,
      "heapUsedBytes": 51300448,
      "domNodes": -261,
      "jsHeapTotalBytes": 20668416,
      "scriptDurationMs": 127.06699999999998,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2001.0389999999916,
      "styleRecalcs": 80,
      "styleRecalcDurationMs": 45.732,
      "layouts": 12,
      "layoutDurationMs": 3.4800000000000004,
      "taskDurationMs": 953.87,
      "heapDeltaBytes": 972476,
      "heapUsedBytes": 48657660,
      "domNodes": -258,
      "jsHeapTotalBytes": 15593472,
      "scriptDurationMs": 133.046,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1754.4599999999946,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 16.701999999999998,
      "layouts": 6,
      "layoutDurationMs": 0.5810000000000002,
      "taskDurationMs": 336.26,
      "heapDeltaBytes": 5959232,
      "heapUsedBytes": 71991360,
      "domNodes": 78,
      "jsHeapTotalBytes": 19230720,
      "scriptDurationMs": 27.764999999999997,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1739.5930000000135,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 17.769,
      "layouts": 6,
      "layoutDurationMs": 0.704,
      "taskDurationMs": 306.343,
      "heapDeltaBytes": 5841532,
      "heapUsedBytes": 71902232,
      "domNodes": 78,
      "jsHeapTotalBytes": 18706432,
      "scriptDurationMs": 26.153999999999996,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1730.660999999941,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 17.586,
      "layouts": 6,
      "layoutDurationMs": 0.6899999999999998,
      "taskDurationMs": 309.503,
      "heapDeltaBytes": 5697548,
      "heapUsedBytes": 72009528,
      "domNodes": 78,
      "jsHeapTotalBytes": 19230720,
      "scriptDurationMs": 22.037000000000003,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 559.8290000000361,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 8.818000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 348.257,
      "heapDeltaBytes": -12249920,
      "heapUsedBytes": 53997424,
      "domNodes": 22,
      "jsHeapTotalBytes": 19755008,
      "scriptDurationMs": 62.96399999999999,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 548.546999999985,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 8.366000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 332.677,
      "heapDeltaBytes": -12546432,
      "heapUsedBytes": 54010956,
      "domNodes": 19,
      "jsHeapTotalBytes": 19230720,
      "scriptDurationMs": 57.329,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.669999999999998,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 563.0199999999377,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 8.876,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 345.14099999999996,
      "heapDeltaBytes": -12103872,
      "heapUsedBytes": 54163080,
      "domNodes": 22,
      "jsHeapTotalBytes": 20279296,
      "scriptDurationMs": 65.153,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2010.5630000000474,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.600999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 520.1450000000001,
      "heapDeltaBytes": 4883816,
      "heapUsedBytes": 63040484,
      "domNodes": -264,
      "jsHeapTotalBytes": 552960,
      "scriptDurationMs": 89.76800000000001,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2017.733000000021,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.911999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 521.8249999999999,
      "heapDeltaBytes": 1603832,
      "heapUsedBytes": 59733844,
      "domNodes": -264,
      "jsHeapTotalBytes": 5009408,
      "scriptDurationMs": 89.046,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2039.5260000000235,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 6.832999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 526.464,
      "heapDeltaBytes": 5332000,
      "heapUsedBytes": 63584464,
      "domNodes": -264,
      "jsHeapTotalBytes": 552960,
      "scriptDurationMs": 92.893,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2139.2910000000143,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 17.784,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1097.413,
      "heapDeltaBytes": 4147788,
      "heapUsedBytes": 63844000,
      "domNodes": -262,
      "jsHeapTotalBytes": 4456448,
      "scriptDurationMs": 393.759,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2120.8819999999946,
      "styleRecalcs": 67,
      "styleRecalcDurationMs": 16.836,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1116.646,
      "heapDeltaBytes": 5172316,
      "heapUsedBytes": 64328528,
      "domNodes": -266,
      "jsHeapTotalBytes": 1282048,
      "scriptDurationMs": 408.583,
      "eventListeners": -125,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2099.796999999967,
      "styleRecalcs": 67,
      "styleRecalcDurationMs": 15.420999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1115.147,
      "heapDeltaBytes": 30421548,
      "heapUsedBytes": 81017424,
      "domNodes": -244,
      "jsHeapTotalBytes": 3350528,
      "scriptDurationMs": 460.4050000000001,
      "eventListeners": -121,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3114.2729999999688,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 18.159,
      "layouts": 60,
      "layoutDurationMs": 7.0889999999999995,
      "taskDurationMs": 1312.8090000000002,
      "heapDeltaBytes": -6821588,
      "heapUsedBytes": 52872060,
      "domNodes": -268,
      "jsHeapTotalBytes": -1081344,
      "scriptDurationMs": 480.05,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3163.618999999983,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 19.644,
      "layouts": 60,
      "layoutDurationMs": 7.684999999999999,
      "taskDurationMs": 1359.175,
      "heapDeltaBytes": 8066000,
      "heapUsedBytes": 68539132,
      "domNodes": -265,
      "jsHeapTotalBytes": 4222976,
      "scriptDurationMs": 496.788,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3130.6409999999687,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 17.927,
      "layouts": 60,
      "layoutDurationMs": 7.246,
      "taskDurationMs": 1291.891,
      "heapDeltaBytes": 9694476,
      "heapUsedBytes": 69139884,
      "domNodes": -269,
      "jsHeapTotalBytes": 6057984,
      "scriptDurationMs": 474.59700000000004,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2053.6789999999883,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.102000000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 537.515,
      "heapDeltaBytes": 14990404,
      "heapUsedBytes": 75058148,
      "domNodes": -255,
      "jsHeapTotalBytes": -233472,
      "scriptDurationMs": 94.63499999999999,
      "eventListeners": -125,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2022.3250000000235,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.843,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 516.4799999999999,
      "heapDeltaBytes": 10424576,
      "heapUsedBytes": 69427316,
      "domNodes": -263,
      "jsHeapTotalBytes": 552960,
      "scriptDurationMs": 85.329,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2052.3230000000012,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.141,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 520.523,
      "heapDeltaBytes": 13968704,
      "heapUsedBytes": 73160576,
      "domNodes": -260,
      "jsHeapTotalBytes": -233472,
      "scriptDurationMs": 88.64800000000001,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 572.9979999999841,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 11.995000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 386.58099999999996,
      "heapDeltaBytes": -12107264,
      "heapUsedBytes": 54327408,
      "domNodes": 21,
      "jsHeapTotalBytes": 20017152,
      "scriptDurationMs": 136.95100000000002,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 529.6959999999444,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 11.380999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 359.7300000000001,
      "heapDeltaBytes": -10456924,
      "heapUsedBytes": 55787092,
      "domNodes": 20,
      "jsHeapTotalBytes": 20709376,
      "scriptDurationMs": 119.976,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 560.6610000000956,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 11.458999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 360.36300000000006,
      "heapDeltaBytes": -12411328,
      "heapUsedBytes": 54326012,
      "domNodes": 20,
      "jsHeapTotalBytes": 19230720,
      "scriptDurationMs": 121.40399999999998,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2020.858999999973,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 12.950999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 388.96,
      "heapDeltaBytes": 971500,
      "heapUsedBytes": 66786536,
      "domNodes": 23,
      "jsHeapTotalBytes": 20017152,
      "scriptDurationMs": 22.999000000000006,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2022.1860000000333,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.440999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 398.36199999999997,
      "heapDeltaBytes": -21976020,
      "heapUsedBytes": 44604556,
      "domNodes": -261,
      "jsHeapTotalBytes": 18571264,
      "scriptDurationMs": 18.873,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2006.6770000000815,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.732000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 340.587,
      "heapDeltaBytes": 1159120,
      "heapUsedBytes": 67767032,
      "domNodes": 19,
      "jsHeapTotalBytes": 18968576,
      "scriptDurationMs": 14.021,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1711.9959999999992,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 37.721000000000004,
      "layouts": 16,
      "layoutDurationMs": 4.554000000000001,
      "taskDurationMs": 688.348,
      "heapDeltaBytes": -7253388,
      "heapUsedBytes": 59355520,
      "domNodes": 64,
      "jsHeapTotalBytes": 20279296,
      "scriptDurationMs": 97.345,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1707.2430000000054,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 38.307,
      "layouts": 16,
      "layoutDurationMs": 4.9350000000000005,
      "taskDurationMs": 681.452,
      "heapDeltaBytes": -5813592,
      "heapUsedBytes": 60788268,
      "domNodes": 64,
      "jsHeapTotalBytes": 19922944,
      "scriptDurationMs": 99.026,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1707.8229999999621,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 37.001,
      "layouts": 16,
      "layoutDurationMs": 4.387,
      "taskDurationMs": 704.499,
      "heapDeltaBytes": -7115544,
      "heapUsedBytes": 59374976,
      "domNodes": 63,
      "jsHeapTotalBytes": 19230720,
      "scriptDurationMs": 96.44800000000001,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8135.9429999999975,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 51.54,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3695.3689999999997,
      "heapDeltaBytes": 17351128,
      "heapUsedBytes": 75983816,
      "domNodes": -262,
      "jsHeapTotalBytes": 4194304,
      "scriptDurationMs": 1258.5249999999999,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8153.731999999991,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 51.634,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3615.44,
      "heapDeltaBytes": 21458032,
      "heapUsedBytes": 79021744,
      "domNodes": -258,
      "jsHeapTotalBytes": 1744896,
      "scriptDurationMs": 1229.6789999999999,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8127.408999999943,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 51.141,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3677.9770000000003,
      "heapDeltaBytes": 17533568,
      "heapUsedBytes": 75815456,
      "domNodes": -263,
      "jsHeapTotalBytes": 1544192,
      "scriptDurationMs": 1249.239,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12433.410000000038,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12423.696999999998,
      "heapDeltaBytes": -46883664,
      "heapUsedBytes": 173812888,
      "domNodes": -9850,
      "jsHeapTotalBytes": 27848704,
      "scriptDurationMs": 576.3999999999999,
      "eventListeners": -23957,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12475.641999999993,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12463.238,
      "heapDeltaBytes": -36866632,
      "heapUsedBytes": 181558292,
      "domNodes": -9850,
      "jsHeapTotalBytes": -15405056,
      "scriptDurationMs": 623.27,
      "eventListeners": -23957,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 33.20000000000073
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12193.06000000006,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12181.995999999997,
      "heapDeltaBytes": -45564040,
      "heapUsedBytes": 173179016,
      "domNodes": -9850,
      "jsHeapTotalBytes": 24702976,
      "scriptDurationMs": 601.595,
      "eventListeners": -23956,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 33.20000000000073
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14673.465999999962,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 16.392000000000017,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14651.616,
      "heapDeltaBytes": -49395900,
      "heapUsedBytes": 165632404,
      "domNodes": -9850,
      "jsHeapTotalBytes": -12783616,
      "scriptDurationMs": 852.417,
      "eventListeners": -23983,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.219999999999953,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14438.173000000006,
      "styleRecalcs": 67,
      "styleRecalcDurationMs": 18.576000000000036,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14416.16,
      "heapDeltaBytes": -62745316,
      "heapUsedBytes": 164948036,
      "domNodes": -9850,
      "jsHeapTotalBytes": -10162176,
      "scriptDurationMs": 863.785,
      "eventListeners": -23983,
      "totalBlockingTimeMs": 71,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14406.810999999947,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 16.508000000000024,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14388.105999999998,
      "heapDeltaBytes": -56369204,
      "heapUsedBytes": 172159996,
      "domNodes": -9850,
      "jsHeapTotalBytes": -20209664,
      "scriptDurationMs": 958.9680000000001,
      "eventListeners": -23955,
      "totalBlockingTimeMs": 16,
      "frameDurationMs": 17.776666666666763,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "workflow-execution",
      "durationMs": 465.1950000000511,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 23.291000000000004,
      "layouts": 5,
      "layoutDurationMs": 1.43,
      "taskDurationMs": 137.67700000000002,
      "heapDeltaBytes": -13411788,
      "heapUsedBytes": 54039932,
      "domNodes": 167,
      "jsHeapTotalBytes": 5242880,
      "scriptDurationMs": 26.871,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "workflow-execution",
      "durationMs": 447.9760000000397,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 25.241,
      "layouts": 5,
      "layoutDurationMs": 1.243,
      "taskDurationMs": 119.05400000000002,
      "heapDeltaBytes": 5089104,
      "heapUsedBytes": 56087868,
      "domNodes": 154,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 23.969000000000005,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "workflow-execution",
      "durationMs": 478.0009999999493,
      "styleRecalcs": 16,
      "styleRecalcDurationMs": 21.935999999999996,
      "layouts": 5,
      "layoutDurationMs": 1.3569999999999998,
      "taskDurationMs": 138.25900000000001,
      "heapDeltaBytes": -14816196,
      "heapUsedBytes": 52662260,
      "domNodes": 156,
      "jsHeapTotalBytes": 4288512,
      "scriptDurationMs": 30.26,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000273
    }
  ]
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/composables/useSnackbarToast.ts (1)

3-11: Use createSharedComposable to wrap module-scoped state.

This composable relies on module-scoped singleton state, which leaks across HMR/tests/SSR boundaries. The codebase already establishes this pattern in other composables (useVueFeatureFlags, useImageLoader, useVueNodeLifecycle, useCoordinateTransform, useBillingContext). Wrap the internal state and timer logic with createSharedComposable to ensure proper lifecycle management.

♻️ Suggested refactor
 import { ref } from 'vue'
+import { createSharedComposable } from '@vueuse/core'
 
-const message = ref('')
-const shortcut = ref('')
-const visible = ref(false)
-const actionLabel = ref('')
-const onAction = ref<(() => void) | null>(null)
-let timeout: ReturnType<typeof setTimeout> | null = null
-let duration = 2000
-
-export function useSnackbarToast() {
+export const useSnackbarToast = createSharedComposable(function useSnackbarToast() {
+  const message = ref('')
+  const shortcut = ref('')
+  const visible = ref(false)
+  const actionLabel = ref('')
+  const onAction = ref<(() => void) | null>(null)
+  let timeout: ReturnType<typeof setTimeout> | null = null
+  let duration = 2000
   function show(
     msg: string,
@@
   return {
@@
   }
-}
+})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/composables/useSnackbarToast.ts` around lines 3 - 11, The module-scoped
state (message, shortcut, visible, actionLabel, onAction, timeout, duration) in
useSnackbarToast must be wrapped with createSharedComposable to avoid singleton
leaks across HMR/tests/SSR; refactor by moving these refs and the timer logic
inside a createSharedComposable call and return the shared composable from
export function useSnackbarToast so useSnackbarToast delegates to the shared
instance, ensuring the timeout is cleared when the shared composable is disposed
and all original methods/values (message, shortcut, visible, actionLabel,
onAction, show/hide logic) are preserved.
src/components/graph/SnackbarToast.stories.ts (1)

32-33: Story files throughout the codebase omit i18n; this refactor is optional.

While guidelines specify vue-i18n for src/**/*.ts, none of the 57+ story files in the repository use i18n. Story files are excluded from test coverage and treated as dev-only fixtures in the build config. If you'd like to standardize this file, you can move the hardcoded strings ('Toast message', 'Links hidden', 'Subgraph unpacked', 'Subgraph repacked', 'Stays open until dismissed', 'Auto-dismiss after 2s...', 'Toast with assigned keybinding badge...', etc.) to src/locales/en/main.json and access them via useI18n() in the story setup blocks. However, this is a low-priority improvement since the project does not enforce i18n for story files.

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

In `@src/components/graph/SnackbarToast.stories.ts` around lines 32 - 33, This
story file uses hardcoded strings (e.g., 'Toast message', 'Links hidden',
'Subgraph unpacked', etc.) instead of i18n; to standardize, add those strings as
keys in your locales (e.g., src/locales/en/main.json) and update the story's
setup to call useI18n(), then replace direct literals used in the story actions
(for example where toast.show(...) is called and any labels/controls in the
story setup) with t('your.key') lookups; ensure keys are unique and descriptive
(e.g., snackbar.toast_message) and update all occurrences in
SnackbarToast.stories.ts including examples that show auto-dismiss text and
keybinding badges.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/graph/SnackbarToast.vue`:
- Line 69: The computed `hasAction` currently only checks `onAction` and
`shortcut`; update it to also require a non-empty `actionLabel` so the action
button isn't shown without a label. Modify the `hasAction` computed (the
function named hasAction that references onAction.value and shortcut.value) to
include a check like `actionLabel.value && actionLabel.value.trim().length > 0`
(or equivalent truthy check) alongside the existing conditions.

In `@src/composables/useSnackbarToast.ts`:
- Around line 31-40: The pause/resume logic currently restarts the full duration
because startTimer always uses duration; fix by tracking remaining milliseconds
and start timestamp: add a numeric variable (e.g., remaining = duration) and a
startTime timestamp; when startTimer runs set startTime = Date.now(), clear any
existing timeout, and set timeout = setTimeout(..., remaining); in pause()
compute remaining = Math.max(0, remaining - (Date.now() - startTime)),
clearTimeout(timeout) but do not reset remaining to the full duration; ensure
startTimer can be called to resume using the remaining value and that
visible.value behavior remains unchanged.

---

Nitpick comments:
In `@src/components/graph/SnackbarToast.stories.ts`:
- Around line 32-33: This story file uses hardcoded strings (e.g., 'Toast
message', 'Links hidden', 'Subgraph unpacked', etc.) instead of i18n; to
standardize, add those strings as keys in your locales (e.g.,
src/locales/en/main.json) and update the story's setup to call useI18n(), then
replace direct literals used in the story actions (for example where
toast.show(...) is called and any labels/controls in the story setup) with
t('your.key') lookups; ensure keys are unique and descriptive (e.g.,
snackbar.toast_message) and update all occurrences in SnackbarToast.stories.ts
including examples that show auto-dismiss text and keybinding badges.

In `@src/composables/useSnackbarToast.ts`:
- Around line 3-11: The module-scoped state (message, shortcut, visible,
actionLabel, onAction, timeout, duration) in useSnackbarToast must be wrapped
with createSharedComposable to avoid singleton leaks across HMR/tests/SSR;
refactor by moving these refs and the timer logic inside a
createSharedComposable call and return the shared composable from export
function useSnackbarToast so useSnackbarToast delegates to the shared instance,
ensuring the timeout is cleared when the shared composable is disposed and all
original methods/values (message, shortcut, visible, actionLabel, onAction,
show/hide logic) are preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ebdeee51-fc9d-43f7-862f-66b497e156ca

📥 Commits

Reviewing files that changed from the base of the PR and between e7640d4 and 719de7c.

📒 Files selected for processing (3)
  • src/components/graph/SnackbarToast.stories.ts
  • src/components/graph/SnackbarToast.vue
  • src/composables/useSnackbarToast.ts

Comment thread src/components/toast/SnackbarToast.vue Outdated
startTimer
} = useSnackbarToast()

const hasAction = computed(() => !!onAction.value && !shortcut.value)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Require both callback and label before rendering the action button.

hasAction should also require a non-empty actionLabel; otherwise an unlabeled action button can render when only onAction is set.

✅ Minimal fix
-const hasAction = computed(() => !!onAction.value && !shortcut.value)
+const hasAction = computed(
+  () => !!onAction.value && !!actionLabel.value && !shortcut.value
+)
📝 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 hasAction = computed(() => !!onAction.value && !shortcut.value)
const hasAction = computed(
() => !!onAction.value && !!actionLabel.value && !shortcut.value
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/graph/SnackbarToast.vue` at line 69, The computed `hasAction`
currently only checks `onAction` and `shortcut`; update it to also require a
non-empty `actionLabel` so the action button isn't shown without a label. Modify
the `hasAction` computed (the function named hasAction that references
onAction.value and shortcut.value) to include a check like `actionLabel.value &&
actionLabel.value.trim().length > 0` (or equivalent truthy check) alongside the
existing conditions.

Comment thread src/composables/useSnackbarToast.ts Outdated
Comment on lines +31 to +40
function startTimer() {
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
visible.value = false
}, duration)
}

function pause() {
if (timeout) clearTimeout(timeout)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hover “pause/resume” currently restarts the full timer.

pause() clears the timeout, and startTimer() restarts with full duration, so hover extends lifetime instead of resuming remaining time. Track remaining milliseconds to match pause/resume behavior.

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

In `@src/composables/useSnackbarToast.ts` around lines 31 - 40, The pause/resume
logic currently restarts the full duration because startTimer always uses
duration; fix by tracking remaining milliseconds and start timestamp: add a
numeric variable (e.g., remaining = duration) and a startTime timestamp; when
startTimer runs set startTime = Date.now(), clear any existing timeout, and set
timeout = setTimeout(..., remaining); in pause() compute remaining = Math.max(0,
remaining - (Date.now() - startTime)), clearTimeout(timeout) but do not reset
remaining to the full duration; ensure startTimer can be called to resume using
the remaining value and that visible.value behavior remains unchanged.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 95.91837% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/components/toast/SnackbarToast.vue 92.00% 2 Missing ⚠️
@@            Coverage Diff             @@
##             main   #11731      +/-   ##
==========================================
+ Coverage   51.90%   52.40%   +0.50%     
==========================================
  Files        1376     1379       +3     
  Lines       70445    70494      +49     
  Branches    18716    18725       +9     
==========================================
+ Hits        36562    36940     +378     
+ Misses      33336    33007     -329     
  Partials      547      547              
Flag Coverage Δ
unit 52.40% <95.91%> (+0.50%) ⬆️

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

Files with missing lines Coverage Δ
src/components/toast/SnackbarToastProvider.vue 100.00% <100.00%> (ø)
src/composables/useSnackbarToast.ts 100.00% <100.00%> (ø)
src/components/toast/SnackbarToast.vue 92.00% <92.00%> (ø)

... and 15 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.

Belongs with the existing toast components (`GlobalToast`,
`ProgressToastItem`, `RerouteMigrationToast`), not under
`components/graph/`. Story title updated to `Components/Toast/SnackbarToast`.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/components/toast/SnackbarToast.vue (1)

69-69: ⚠️ Potential issue | 🟡 Minor

Require a non-empty actionLabel before showing the action button.

hasAction still allows rendering an action button when onAction exists but
the label is empty, which can produce an unlabeled control.

✅ Minimal fix
-const hasAction = computed(() => !!onAction.value && !shortcut.value)
+const hasAction = computed(
+  () => !!onAction.value && !!actionLabel.value?.trim() && !shortcut.value
+)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/toast/SnackbarToast.vue` at line 69, The computed `hasAction`
currently only checks `onAction` and `shortcut`, allowing an action button with
an empty label; update the `hasAction` logic in SnackbarToast.vue to also
require a non-empty, non-whitespace `actionLabel` (e.g., check that
`actionLabel.value` exists and trim().length > 0) alongside the existing
`onAction` and `!shortcut` checks so the action button is only shown when there
is an actual label.
🧹 Nitpick comments (1)
src/components/toast/SnackbarToast.stories.ts (1)

32-107: Move hardcoded user-facing strings to i18n keys.

Story descriptions, button labels, and toast message literals are hardcoded in
English. Please route them through vue-i18n and add entries in
src/locales/en/main.json for consistency with project rules.

As per coding guidelines, "Use vue-i18n for ALL user-facing strings, configured in src/locales/en/main.json" and "Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json."

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

In `@src/components/toast/SnackbarToast.stories.ts` around lines 32 - 107, The
story file contains hardcoded user-facing strings in the story components (see
Story exports WithShortcut, WithUndoAction, Persistent and their inner setup
functions trigger/dismiss using useSnackbarToast, plus Button labels and
paragraph descriptions); replace each literal (toast.show message strings,
paragraph descriptions, Button labels, actionLabel, shortcut badge text) with
vue-i18n lookups via the composition API (import/use the t function in each
render/setup) and add corresponding keys to src/locales/en/main.json (e.g., keys
for toast.messages.subgraphUnpacked, toast.messages.repacked,
toast.messages.autoDismiss, button.show, button.dismiss, toast.undoLabel,
paragraph.descriptions.*) so all user-facing strings are loaded from i18n
instead of hardcoded literals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/components/toast/SnackbarToast.vue`:
- Line 69: The computed `hasAction` currently only checks `onAction` and
`shortcut`, allowing an action button with an empty label; update the
`hasAction` logic in SnackbarToast.vue to also require a non-empty,
non-whitespace `actionLabel` (e.g., check that `actionLabel.value` exists and
trim().length > 0) alongside the existing `onAction` and `!shortcut` checks so
the action button is only shown when there is an actual label.

---

Nitpick comments:
In `@src/components/toast/SnackbarToast.stories.ts`:
- Around line 32-107: The story file contains hardcoded user-facing strings in
the story components (see Story exports WithShortcut, WithUndoAction, Persistent
and their inner setup functions trigger/dismiss using useSnackbarToast, plus
Button labels and paragraph descriptions); replace each literal (toast.show
message strings, paragraph descriptions, Button labels, actionLabel, shortcut
badge text) with vue-i18n lookups via the composition API (import/use the t
function in each render/setup) and add corresponding keys to
src/locales/en/main.json (e.g., keys for toast.messages.subgraphUnpacked,
toast.messages.repacked, toast.messages.autoDismiss, button.show,
button.dismiss, toast.undoLabel, paragraph.descriptions.*) so all user-facing
strings are loaded from i18n instead of hardcoded literals.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a2b159e8-4c72-4a91-8335-04e75dd3594a

📥 Commits

Reviewing files that changed from the base of the PR and between 719de7c and a9695a7.

📒 Files selected for processing (2)
  • src/components/toast/SnackbarToast.stories.ts
  • src/components/toast/SnackbarToast.vue

…ider

Codex adversarial review (PR #11731) flagged the original
`useSnackbarToast` for owning UI state as raw module-level refs and a
manual `setTimeout`, which conflicts with the existing toast stack
(PrimeVue/GlobalToast/HoneyToast) and is unsafe under HMR, multiple
hosts, rapid back-to-back show() calls, and throwing action callbacks.

This rewrites the component on top of Reka's `ToastProvider` /
`ToastRoot` / `ToastAction` / `ToastClose` / `ToastViewport` primitives,
which already handle the queue, duration, hover/focus pause, swipe
dismiss, and SR announcement. State now lives in a `<SnackbarToastProvider>`
component scope, not at module load.

Changes:
- `useSnackbarToast()` is now an inject-based hook returning
  `{ show, dismiss }`. Throws when no Provider is in scope.
- `SnackbarToastProvider.vue` owns the toasts array, provides the API,
  and replaces the previous toast on rapid show() (singleton policy
  preserved). Renders `<ToastViewport>` at the same bottom-center
  position as before.
- `SnackbarToast.vue` is now a single `<ToastRoot>` item renderer,
  driven by a typed `toast` prop. Action handler is wrapped in
  try/finally so a throwing callback still dismisses and is logged.
- Stories wrap each variant in `<SnackbarToastProvider>` with simple
  trigger components.
- Visuals match Figma node 6826:77784 (verified via Figma MCP).

Tests:
- `useSnackbarToast.test.ts` covers no-provider throw and inject
  contract.
- `SnackbarToastProvider.test.ts` covers initial empty state, show
  rendering, singleton replace, shortcut badge vs action exclusivity,
  action click + dismiss, throwing action still dismisses, dismiss(id)
  targeting, and unique-id guarantee.

Refs FE-484
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/toast/SnackbarToastProvider.test.ts (1)

54-56: Restore global spies in the suite cleanup.

If the error-path test fails before Line 137, console.error stays mocked and can hide later failures. Add vi.restoreAllMocks() to the existing afterEach() so cleanup does not depend on the test reaching its last line.

♻️ Suggested cleanup
   afterEach(() => {
     capturedApi = null
+    vi.restoreAllMocks()
   })
@@
-    errSpy.mockRestore()

Also applies to: 122-137

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

In `@src/components/toast/SnackbarToastProvider.test.ts` around lines 54 - 56, The
afterEach cleanup currently only resets capturedApi to null (capturedApi) which
leaves global spies (like console.error mocked in error-path tests) active if a
test fails early; update the existing afterEach() to also call
vi.restoreAllMocks() so all global mocks/spies are restored regardless of test
outcome, ensuring tests like those that mock console.error are cleaned up even
if they fail before their final assertions.
src/composables/useSnackbarToast.ts (1)

4-19: Make shortcut and action states unrepresentable together.

ShowSnackbarOptions currently allows shortcut, actionLabel, and onAction in the same payload even though the UI only honors one branch. A mutually exclusive union would catch those invalid calls at compile time instead of silently dropping the action.

♻️ Suggested type shape
-export interface ShowSnackbarOptions {
-  shortcut?: string
-  duration?: number
-  actionLabel?: string
-  onAction?: () => void
-}
+interface BaseSnackbarOptions {
+  duration?: number
+}
+
+interface ShortcutSnackbarOptions extends BaseSnackbarOptions {
+  shortcut: string
+  actionLabel?: never
+  onAction?: never
+}
+
+interface ActionSnackbarOptions extends BaseSnackbarOptions {
+  shortcut?: never
+  actionLabel: string
+  onAction: () => void
+}
+
+interface PassiveSnackbarOptions extends BaseSnackbarOptions {
+  shortcut?: never
+  actionLabel?: never
+  onAction?: never
+}
+
+export type ShowSnackbarOptions =
+  | PassiveSnackbarOptions
+  | ShortcutSnackbarOptions
+  | ActionSnackbarOptions

As per coding guidelines "Use TypeScript for type safety."

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

In `@src/composables/useSnackbarToast.ts` around lines 4 - 19, ShowSnackbarOptions
currently permits shortcut and actionLabel/onAction together even though the UI
only uses one branch; change ShowSnackbarOptions into a mutually exclusive union
(e.g. { shortcut: string; duration?: number } | { actionLabel: string; onAction:
() => void; duration?: number } | { duration?: number }) so callers cannot pass
both shortcut and action fields; update SnackbarToastItem (which extends
ShowSnackbarOptions) and the SnackbarToastApi.show signature to use the new
union type so type-checking prevents invalid payloads.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/composables/useSnackbarToast.test.ts`:
- Around line 24-41: The test currently only inspects the shape of the injected
object; update it to assert the actual injected instance or that the provided
mocks are invoked: render Provider (which provides SnackbarToastKey with api)
and have the Consumer call useSnackbarToast() and either (a) expose the returned
object so the test can assert strict equality with the provided api, or (b) call
the returned show() and dismiss() inside the Consumer and assert that api.show
and api.dismiss (vi.fn mocks) were called; target symbols: useSnackbarToast,
SnackbarToastKey, Provider, Consumer, and the api mock (show/dismiss).

---

Nitpick comments:
In `@src/components/toast/SnackbarToastProvider.test.ts`:
- Around line 54-56: The afterEach cleanup currently only resets capturedApi to
null (capturedApi) which leaves global spies (like console.error mocked in
error-path tests) active if a test fails early; update the existing afterEach()
to also call vi.restoreAllMocks() so all global mocks/spies are restored
regardless of test outcome, ensuring tests like those that mock console.error
are cleaned up even if they fail before their final assertions.

In `@src/composables/useSnackbarToast.ts`:
- Around line 4-19: ShowSnackbarOptions currently permits shortcut and
actionLabel/onAction together even though the UI only uses one branch; change
ShowSnackbarOptions into a mutually exclusive union (e.g. { shortcut: string;
duration?: number } | { actionLabel: string; onAction: () => void; duration?:
number } | { duration?: number }) so callers cannot pass both shortcut and
action fields; update SnackbarToastItem (which extends ShowSnackbarOptions) and
the SnackbarToastApi.show signature to use the new union type so type-checking
prevents invalid payloads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a2c13df-d5d1-4955-aec2-91d1eb0f21d7

📥 Commits

Reviewing files that changed from the base of the PR and between a9695a7 and 8d71b01.

📒 Files selected for processing (6)
  • src/components/toast/SnackbarToast.stories.ts
  • src/components/toast/SnackbarToast.vue
  • src/components/toast/SnackbarToastProvider.test.ts
  • src/components/toast/SnackbarToastProvider.vue
  • src/composables/useSnackbarToast.test.ts
  • src/composables/useSnackbarToast.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/toast/SnackbarToast.vue

Comment on lines +24 to +41
it('returns the injected api', () => {
const api: SnackbarToastApi = {
show: vi.fn(() => 'id-1'),
dismiss: vi.fn()
}
const Provider = defineComponent({
setup(_, { slots }) {
provide(SnackbarToastKey, api)
return () => slots.default?.()
}
})

render(Provider, {
slots: { default: () => h(Consumer) }
})

expect(screen.getByTestId('has-show').textContent).toBe('function')
expect(screen.getByTestId('has-dismiss').textContent).toBe('function')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assert the injected instance, not just its shape.

This still passes if useSnackbarToast() returns a fresh { show, dismiss } object instead of the provided api. Capture the returned value and compare identity, or invoke the provided mocks through the consumer so the test fails when injection breaks.

As per coding guidelines "Do not write tests that just test the mocks; ensure tests fail when code behaves unexpectedly."

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

In `@src/composables/useSnackbarToast.test.ts` around lines 24 - 41, The test
currently only inspects the shape of the injected object; update it to assert
the actual injected instance or that the provided mocks are invoked: render
Provider (which provides SnackbarToastKey with api) and have the Consumer call
useSnackbarToast() and either (a) expose the returned object so the test can
assert strict equality with the provided api, or (b) call the returned show()
and dismiss() inside the Consumer and assert that api.show and api.dismiss
(vi.fn mocks) were called; target symbols: useSnackbarToast, SnackbarToastKey,
Provider, Consumer, and the api mock (show/dismiss).

@jtydhr88
Copy link
Copy Markdown
Collaborator

jtydhr88 commented Apr 29, 2026

@dante01yoon I don't have access to that figma, just assign to @DrJKL for review

@jtydhr88 jtydhr88 assigned DrJKL and unassigned jtydhr88 Apr 29, 2026
@DrJKL
Copy link
Copy Markdown
Contributor

DrJKL commented May 1, 2026

I don't think we should add this speculatively.

@DrJKL DrJKL assigned dante01yoon and unassigned DrJKL May 1, 2026
@dante01yoon
Copy link
Copy Markdown
Collaborator Author

I don't think we should add this speculatively.

What do you mean?

@DrJKL
Copy link
Copy Markdown
Contributor

DrJKL commented May 1, 2026

I don't think we should add this speculatively.

What do you mean?

We have an existing toast system which I'd love to replace. This adds a secondary component with no immediate usage in response to a problem that we agreed wasn't worth solving in this way.
I also like the sonner style a bit better, but that's a different issue.

@dante01yoon dante01yoon closed this May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants