perf: add gpu hint and transform settle to prevent rasterizing while zooming (scale transform)#7417
Conversation
📝 WalkthroughWalkthroughThis PR introduces a new Changes
Sequence DiagramsequenceDiagram
participant User
participant Canvas as Canvas Element
participant useTS as useTransformSettling
participant TP as TransformPane Component
participant CSS as CSS Engine
User->>Canvas: wheel event
Canvas->>useTS: dispatch wheel event (capture phase)
activate useTS
useTS->>useTS: set isTransforming = true
useTS->>useTS: reset settle timer
useTS->>TP: reactive update: isTransforming = true
deactivate useTS
activate TP
TP->>TP: compute dynamic class<br/>"transform-pane--interacting"
TP->>CSS: apply class
deactivate TP
activate CSS
CSS->>CSS: disable transitions on .lg-node
CSS->>CSS: enable will-change: transform
CSS->>CSS: render optimized transform
deactivate CSS
Note over User,CSS: User continues wheel interactions...
User->>Canvas: (no more wheel events)
activate useTS
useTS->>useTS: settle timer fires after 256ms
useTS->>useTS: set isTransforming = false
useTS->>TP: reactive update: isTransforming = false
deactivate useTS
activate TP
TP->>TP: compute dynamic class<br/>"" (empty)
TP->>CSS: remove class
deactivate TP
activate CSS
CSS->>CSS: re-enable transitions
CSS->>CSS: disable will-change
CSS->>CSS: render normal
deactivate CSS
Possibly related PRs
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
🎭 Playwright Test Results⏰ Completed at: 12/12/2025, 02:20:27 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 12/12/2025, 02:12:10 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.24 MB (baseline 3.24 MB) • ⚪ 0 BMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 984 kB (baseline 983 kB) • 🔴 +1.17 kBGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 178 kB (baseline 178 kB) • ⚪ 0 BReusable component library chunks
Status: 7 added / 7 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 3.18 kB (baseline 3.18 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 3.81 MB (baseline 3.81 MB) • ⚪ 0 BBundles that do not match a named category
Status: 18 added / 18 removed |
There was a problem hiding this comment.
Actionable comments posted: 5
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
packages/design-system/src/css/style.css(1 hunks)src/renderer/core/layout/__tests__/TransformPane.test.ts(2 hunks)src/renderer/core/layout/transform/TransformPane.vue(4 hunks)src/renderer/core/layout/transform/useTransformSettling.ts(1 hunks)tests-ui/tests/composables/graph/useTransformSettling.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
tests-ui/**/*.test.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)
tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks
Files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
**/*.{ts,tsx,js,jsx,vue,json}
📄 CodeRabbit inference engine (AGENTS.md)
Code style: Use 2-space indentation, single quotes, no trailing semicolons, and 80-character line width (see
.prettierrc)
Files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,vue}: Imports must be sorted and grouped by plugin; runpnpm formatbefore committing
Use TypeScript for type safety; never useanytype - use proper TypeScript types
Never useas anytype assertions; fix the underlying type issue instead
Use es-toolkit for utility functions
Write code that is expressive and self-documenting; avoid comments unless absolutely necessary; do not add or retain redundant comments
Keep functions short and functional
Minimize nesting in code (e.g., deeply nestediforforstatements); apply the Arrow Anti-Pattern principle
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions, especially testable ones
Files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/component test files must be named**/*.test.tsor intests-ui/directory
Do not write change detector tests that just assert default values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing; do not write redundant tests; see composable tests approach
Follow 'Don't Mock What You Don't Own' principle - avoid mocking external dependencies
Do not write tests that just test the mocks; ensure tests fail when code behaves unexpectedly
Leverage Vitest's mocking utilities where possible for test mocking
Keep module mocks contained in test files; do not use global mutable state within test files; usevi.hoisted()if necessary
For Component testing, use Vue Test Utils and follow advice about making components easy to test
Aim for behavioral coverage of critical and new features in unit tests
Files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
src/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions
Files:
src/renderer/core/layout/transform/TransformPane.vue
src/**/*.{vue,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
Files:
src/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase
Files:
src/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
**/*.vue: Use Vue 3 SFCs (Single File Components) with Composition API only; do not use Options API
Vue components must use<script setup lang="ts">for component logic
Use Vue 3.5 TypeScript style for default prop declaration with reactive props destructuring; do not usewithDefaultsor runtime props declaration
PreferuseModelto separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using<style>blocks in Vue components
Use semantic Tailwind values fromstyle.csstheme instead of thedark:variant; for example, usebg-node-component-surfaceinstead ofdark:prefixes
Always usecn()utility from@/utils/tailwindUtilto merge Tailwind class names; do not use:class="[]"syntax
Usereffor reactive state in Vue Composition API components
Implement computed properties withcomputed()from Vue; avoid using arefwith awatchif acomputedwould work instead
UsewatchandwatchEffectfor side effects in Vue components
Implement lifecycle hooks usingonMounted,onUpdated, and other Vue lifecycle functions
Useprovide/injectfor dependency injection; do not use dependency injection if a Store or shared composable would be simpler
Do not import Vue macros unnecessarily; only use when needed
Be judicious with addition of new refs or other state: prefer props, avoid redundantcomputed, and prefercomputedoverwatch
Use VueUse functions for performance-enhancing styles
In Vue Components, implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Implement proper error handling in Vue components
Follow Vue 3 style guide and naming conventions
Use vue-i18n in composition API for any string literals; place new translation entries insrc/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead
Files:
src/renderer/core/layout/transform/TransformPane.vue
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safetyMinimize the surface area (exported values) of each module and composable
Files:
src/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
🧠 Learnings (33)
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Be parsimonious in testing; do not write redundant tests; see composable tests approach
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilities
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test user workflows in browser tests
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebase
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test across multiple viewports
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `test` instead of `it` for defining test cases in vitest
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Prefer specific selectors in browser tests
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `vitest` for unit testing in this project
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Leverage Vitest's mocking utilities where possible for test mocking
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.
Applied to files:
tests-ui/tests/composables/graph/useTransformSettling.test.tssrc/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.tssrc/renderer/core/layout/__tests__/TransformPane.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Applied to files:
src/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Utilize Vue 3's Teleport component when needed
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize Vue 3's Teleport component when needed
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles
Applied to files:
src/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.ts
📚 Learning: 2025-12-04T21:43:49.363Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/parameters/TabParameters.vue:10-0
Timestamp: 2025-12-04T21:43:49.363Z
Learning: Vue 3.5+ supports reactive props destructure in <script setup>. Destructuring props directly (e.g., `const { nodes } = defineProps<{ nodes: LGraphNode[] }>()`) maintains reactivity through compiler transformation. This is the recommended modern approach and does not require using `props.x` or `toRef`/`toRefs`.
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use setup() function in Vue 3 Composition API
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Vue components must use `<script setup lang="ts">` for component logic
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement computed() for derived state in Vue 3 Composition API
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use VueUse functions for performance-enhancing styles
Applied to files:
src/renderer/core/layout/transform/TransformPane.vuesrc/renderer/core/layout/transform/useTransformSettling.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use `ref` for reactive state in Vue Composition API components
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.
Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.
Applied to files:
src/renderer/core/layout/transform/TransformPane.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to browser_tests/**/*.spec.ts : Follow Playwright best practices described in the official documentation for E2E tests
Applied to files:
src/renderer/core/layout/__tests__/TransformPane.test.ts
🧬 Code graph analysis (2)
tests-ui/tests/composables/graph/useTransformSettling.test.ts (1)
src/renderer/core/layout/transform/useTransformSettling.ts (1)
useTransformSettling(45-84)
packages/design-system/src/css/style.css (1)
src/lib/litegraph/src/LGraph.ts (1)
change(1261-1264)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: deploy-and-comment
- GitHub Check: test
- GitHub Check: lint-and-format
- GitHub Check: collect
- GitHub Check: setup
🔇 Additional comments (1)
src/renderer/core/layout/transform/TransformPane.vue (1)
4-9: Use Tailwindwill-change-*utilities instead of<style>blocks; remove scoped CSS.The repo guideline requires using Tailwind utilities for styling and avoiding
<style>blocks in Vue components. Tailwind 4 provides the exact utilities needed here:
- Replace
will-change-autowith thewill-change-autoutility- Add
will-change-transformto the interacting stateRemove the
<style scoped>block entirely and apply the utility class conditionally viacn()when interacting. Also verify the import path forLGraphCanvasmatches the module export structure.
| /* Performance optimization during canvas interaction */ | ||
| .transform-pane--interacting .lg-node * { | ||
| transition: none !important; | ||
| } | ||
|
|
||
| .transform-pane--interacting .lg-node { | ||
| will-change: transform; | ||
| } |
There was a problem hiding this comment.
Over-broad * { transition: none !important; } risks unintended UI regressions; prefer targeting known transitioning elements.
Right now this disables all descendant transitions under nodes during zoom, not just the LiteGraph node transition surfaces. Given you already define transitions on .lg-node .lg-slot / .lg-node .lg-widget (Line 1324-1329), consider scoping the override to those instead of *.
/* Performance optimization during canvas interaction */
-.transform-pane--interacting .lg-node * {
- transition: none !important;
-}
+.transform-pane--interacting .lg-node .lg-slot,
+.transform-pane--interacting .lg-node .lg-widget {
+ transition: none !important;
+}
.transform-pane--interacting .lg-node {
will-change: transform;
}🤖 Prompt for AI Agents
In packages/design-system/src/css/style.css around lines 1331 to 1338, the rule
using the universal selector to disable transitions is too broad and can cause
regressions; narrow the selector to only the known transition targets (e.g.,
.lg-node .lg-slot and .lg-node .lg-widget or any other specific transitioning
descendants) so that during .transform-pane--interacting only those elements
receive transition: none !important, leaving unrelated descendants unaffected.
| describe('canvas event listeners', () => { | ||
| it('should add event listeners to canvas on mount', async () => { | ||
| const mockCanvas = createMockCanvas() | ||
| mount(TransformPane, { | ||
| props: { | ||
| canvas: mockCanvas | ||
| } | ||
| }) | ||
|
|
||
| await nextTick() | ||
|
|
||
| expect(mockCanvas.canvas.addEventListener).toHaveBeenCalledWith( | ||
| 'wheel', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith( | ||
| 'pointerdown', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith( | ||
| 'pointerup', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith( | ||
| 'pointercancel', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| }) | ||
|
|
||
| it('should remove event listeners on unmount', async () => { | ||
| const mockCanvas = createMockCanvas() | ||
| const wrapper = mount(TransformPane, { | ||
| props: { | ||
| canvas: mockCanvas | ||
| } | ||
| }) | ||
|
|
||
| await nextTick() | ||
| wrapper.unmount() | ||
|
|
||
| expect(mockCanvas.canvas.removeEventListener).toHaveBeenCalledWith( | ||
| 'wheel', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith( | ||
| 'pointerdown', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith( | ||
| 'pointerup', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith( | ||
| 'pointercancel', | ||
| expect.any(Function), | ||
| expect.any(Object) | ||
| ) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Event-listener tests should assert handler identity (and avoid brittle “no pointer listeners” assertions).
removeEventListener requires the same function reference and compatible options; expect.any(Function) can pass even if the component leaks the real handler. Also, “not called with pointer*” will break if pointer listeners are legitimately added later.
Consider tightening to “wheel is registered with capture/passive” and “wheel is removed with the same handler”:
it('should add event listeners to canvas on mount', async () => {
const mockCanvas = createMockCanvas()
mount(TransformPane, {
props: {
canvas: mockCanvas
}
})
await nextTick()
- expect(mockCanvas.canvas.addEventListener).toHaveBeenCalledWith(
- 'wheel',
- expect.any(Function),
- expect.any(Object)
- )
- expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith(
- 'pointerdown',
- expect.any(Function),
- expect.any(Object)
- )
- expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith(
- 'pointerup',
- expect.any(Function),
- expect.any(Object)
- )
- expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith(
- 'pointercancel',
- expect.any(Function),
- expect.any(Object)
- )
+ const [type, handler, options] =
+ (mockCanvas.canvas.addEventListener as unknown as { mock: { calls: unknown[][] } })
+ .mock.calls[0]
+
+ expect(type).toBe('wheel')
+ expect(handler).toEqual(expect.any(Function))
+ expect(options).toEqual(expect.objectContaining({ capture: true }))
})
it('should remove event listeners on unmount', async () => {
const mockCanvas = createMockCanvas()
const wrapper = mount(TransformPane, {
props: {
canvas: mockCanvas
}
})
await nextTick()
+ const [, handler, options] =
+ (mockCanvas.canvas.addEventListener as unknown as { mock: { calls: unknown[][] } })
+ .mock.calls[0]
wrapper.unmount()
- expect(mockCanvas.canvas.removeEventListener).toHaveBeenCalledWith(
- 'wheel',
- expect.any(Function),
- expect.any(Object)
- )
- expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith(
- 'pointerdown',
- expect.any(Function),
- expect.any(Object)
- )
- expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith(
- 'pointerup',
- expect.any(Function),
- expect.any(Object)
- )
- expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith(
- 'pointercancel',
- expect.any(Function),
- expect.any(Object)
- )
+ expect(mockCanvas.canvas.removeEventListener).toHaveBeenCalledWith(
+ 'wheel',
+ handler as EventListener,
+ options as AddEventListenerOptions
+ )
})(If you’d rather avoid mocking intricacies altogether, consider using a real HTMLCanvasElement and vi.spyOn(canvasEl, 'addEventListener').)
Based on learnings, prefer assertions that reflect behavior over internal wiring where possible.
🤖 Prompt for AI Agents
In src/renderer/core/layout/__tests__/TransformPane.test.ts around lines 120 to
185, the tests currently assert add/remove calls using expect.any(Function) and
negative assertions for pointer listeners which is brittle; update the tests to
(1) capture the actual handler reference returned/attached when addEventListener
is spied or when using a real HTMLCanvasElement so you can assert the same
function is used in removeEventListener, (2) assert the wheel listener was
registered with the expected options (e.g. passive/capture) rather than broadly
checking any function, and (3) remove the
“not.toHaveBeenCalledWith('pointer...')” negative assertions or replace them
with behavior-based assertions; alternatively switch to a real canvas element
and use vi.spyOn(canvasEl, 'addEventListener') so you can compare handler
identity and options precisely and then assert removeEventListener was called
with that exact handler and matching options.
| expect(transformPane.classes()).not.toContain( | ||
| 'transform-pane--interacting' | ||
| ) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Interaction class test only asserts the initial state; consider validating the “interacting” state is reachable.
Right now it only checks the class is absent initially. If feasible, dispatch a wheel event on a real canvas element (and advance fake timers) to assert transform-pane--interacting toggles on/off.
🤖 Prompt for AI Agents
In src/renderer/core/layout/__tests__/TransformPane.test.ts around lines 201 to
203, the test only asserts the initial absence of the
'transform-pane--interacting' class; extend it to actually trigger and verify
the interacting state by dispatching a real 'wheel' event on the canvas element
and advancing fake timers to observe the class toggle. Specifically, get the
actual canvas DOM element from the mounted wrapper, dispatch a WheelEvent with a
non-zero delta (e.g., deltaY), assert the wrapper now contains
'transform-pane--interacting', then advance timers by the interaction timeout
duration and assert the class is removed; ensure
jest.useFakeTimers/jest.advanceTimersByTime (or equivalent) are used and timers
are restored/cleared after the test.
| interface TransformSettlingOptions { | ||
| /** | ||
| * Delay in ms before transform is considered "settled" after last interaction | ||
| * @default 200 | ||
| */ | ||
| settleDelay?: number | ||
| /** | ||
| * Whether to use passive event listeners (better performance but can't preventDefault) | ||
| * @default true | ||
| */ | ||
| passive?: boolean | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/renderer/core/layout/transform/useTransformSettling.tsRepository: Comfy-Org/ComfyUI_frontend
Length of output: 2905
🌐 Web query:
VueUse useEventListener return type documentation
💡 Result:
The VueUse docs show the TypeScript declarations for useEventListener (including its return type). In short: useEventListener returns a cleanup function you can call to unregister the listener, and the library also exports a type alias UseEventListenerReturn = ReturnType. See the Type Declarations in the docs for details and overloads. [1][2]
Sources:
[1] VueUse useEventListener (Type declarations). https://vueuse.org/core/useeventlistener/
[2] VueUse docs (versioned/type alias showing UseEventListenerReturn = ReturnType). https://v7-7-1.vueuse.org/core/useeventlistener/
🏁 Script executed:
rg "useTransformSettling" --type ts --type vue -A 3 -B 1Repository: Comfy-Org/ComfyUI_frontend
Length of output: 97
🏁 Script executed:
rg "useTransformSettling" -A 3 -B 1Repository: Comfy-Org/ComfyUI_frontend
Length of output: 5818
🏁 Script executed:
cat -n tests-ui/tests/composables/graph/useTransformSettling.test.tsRepository: Comfy-Org/ComfyUI_frontend
Length of output: 6338
Fix documentation mismatch for default settleDelay value.
The JSDoc @default 200 at line 8 disagrees with the actual implementation at line 49 which uses settleDelay = 256. Please align the documentation to match the actual default value of 256.
Additionally, consider whether returning the stop() function from useEventListener would improve API flexibility. While the composable currently works correctly via automatic cleanup in component setup contexts (as verified by the test suite), exposing the stop function would allow explicit cleanup if the composable is ever used outside a Vue effect scope.
🤖 Prompt for AI Agents
In src/renderer/core/layout/transform/useTransformSettling.ts around lines 5 to
16, update the JSDoc for TransformSettlingOptions to state the correct default
settleDelay of 256 (replace @default 200 with @default 256) so docs match the
implementation, and modify the composable to expose the stop() cleanup function
returned by useEventListener (either return stop from useTransformSettling or
include it in the composable's returned object) so callers can explicitly
teardown listeners when used outside a Vue effect scope.
| it('should use capture phase for events', async () => { | ||
| const captureHandler = vi.fn() | ||
| const bubbleHandler = vi.fn() | ||
|
|
||
| // Add handlers to verify capture phase | ||
| element.addEventListener('wheel', captureHandler, true) | ||
| element.addEventListener('wheel', bubbleHandler, false) | ||
|
|
||
| const { isTransforming } = useTransformSettling(element) | ||
|
|
||
| // Create child element | ||
| const child = document.createElement('div') | ||
| element.appendChild(child) | ||
|
|
||
| // Dispatch event on child | ||
| child.dispatchEvent(new WheelEvent('wheel', { bubbles: true })) | ||
| await nextTick() | ||
|
|
||
| // Capture handler should be called before bubble handler | ||
| expect(captureHandler).toHaveBeenCalled() | ||
| expect(isTransforming.value).toBe(true) | ||
|
|
||
| element.removeEventListener('wheel', captureHandler, true) | ||
| element.removeEventListener('wheel', bubbleHandler, false) | ||
| }) | ||
|
|
There was a problem hiding this comment.
Strengthen or drop implementation-detail tests (capture/passive) to avoid false confidence.
- The “capture phase” test doesn’t validate that
useTransformSettlingregistered with{ capture: true }; it only shows a capture listener existed. - If you want this as a contract, it’s clearer to
vi.spyOn(element, 'addEventListener')and assert the options passed include{ capture: true, passive: true }(similar to the passive test), or remove the capture test and rely on the options assertion alone.
Also applies to: 157-170
…zooming (scale transform) (Comfy-Org#7417) ## Summary Ensures the nodes get their own compositing layers during scale transform (tracked via mouse wheel events), which prevents rasterization during transform. Adds forced reflow at end of transform to ensure layers are always at correct resolution (fixes blurriness and some readability issues). Videos show testing this branch first then testing main - doing layer visualization, paint (include paint operations calculations and actual raster) visualizations, and cpu usage monitoring. https://github.com/user-attachments/assets/c5fab219-0b32-4822-9238-c4572f0d6a44 https://github.com/user-attachments/assets/7e172e8d-cc5b-4dcd-aa07-1dfc3eb65bac
…zooming (scale transform) (#7417) ## Summary Ensures the nodes get their own compositing layers during scale transform (tracked via mouse wheel events), which prevents rasterization during transform. Adds forced reflow at end of transform to ensure layers are always at correct resolution (fixes blurriness and some readability issues). Videos show testing this branch first then testing main - doing layer visualization, paint (include paint operations calculations and actual raster) visualizations, and cpu usage monitoring. https://github.com/user-attachments/assets/c5fab219-0b32-4822-9238-c4572f0d6a44 https://github.com/user-attachments/assets/7e172e8d-cc5b-4dcd-aa07-1dfc3eb65bac
Summary
Ensures the nodes get their own compositing layers during scale transform (tracked via mouse wheel events), which prevents rasterization during transform. Adds forced reflow at end of transform to ensure layers are always at correct resolution (fixes blurriness and some readability issues).
Videos show testing this branch first then testing main - doing layer visualization, paint (include paint operations calculations and actual raster) visualizations, and cpu usage monitoring.
VID_20251212_042140430.mp4
VID_20251212_035211733.mp4