Skip to content

3dgs & ply support#7602

Merged
christian-byrne merged 2 commits intomainfrom
spark-3dgs-support
Dec 20, 2025
Merged

3dgs & ply support#7602
christian-byrne merged 2 commits intomainfrom
spark-3dgs-support

Conversation

@jtydhr88
Copy link
Collaborator

@jtydhr88 jtydhr88 commented Dec 18, 2025

Summary

integrated sparkjs https://sparkjs.dev/, built by world labs to support 3dgs.

  • Add 3D Gaussian Splatting (3DGS) support using @sparkjsdev/spark library
  • Add PLY file format support with multiple rendering engines
  • Support new file formats: .ply, .spz, .splat, .ksplat
  • Add PLY Engine setting with three options: threejs (mesh), fastply (optimized ASCII point clouds), sparkjs (3DGS)
  • Add FastPLYLoader for 4-5x faster ASCII PLY parsing
  • Add original(Advanced) material mode for point cloud rendering with THREE.Points

3dgs generated by https://marble.worldlabs.ai/

test ply file from:

  1. made by https://github.com/PozzettiAndrea/ComfyUI-DepthAnythingV3
  2. threejs offically repo

Screenshots

2025-12-17.20-53-48.mp4
2025-12-17.20-54-45.mp4
2025-12-17.20-55-33.mp4
2025-12-17.20-56-28.mp4

┆Issue is synchronized with this Notion page by Unito

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Dec 18, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

📝 Walkthrough

Walkthrough

Adds PLY/splat support: workspace dependency and PLYEngine setting; new PLY parsers/loaders (FastPLYLoader, ASCII parser, optional Spark SplatMesh); exposes isSplatModel/isPlyModel flags to composables and UI; adds pointCloud material mode; updates loader, scene manager, service, and UI to handle PLY/splat models.

Changes

Cohort / File(s) Change Summary
Dependencies & Workspace
package.json, pnpm-workspace.yaml
Added @sparkjsdev/spark dependency / catalog entry.
Composable State
src/composables/useLoad3d.ts, src/composables/useLoad3dViewer.ts
Added reactive isSplatModel and isPlyModel flags; populated on load and exposed in return objects.
Components & UI
src/components/load3d/Load3D.vue, src/components/load3d/Load3DControls.vue, src/components/load3d/Load3dViewerContent.vue, src/components/load3d/controls/*/ModelControls.vue, src/components/load3d/controls/viewer/ViewerModelControls.vue
Added props isSplatModel/isPlyModel (and hideMaterialMode binding); hide material/light/export controls for splat models; include pointCloud option for PLY models; pass flags to child controls.
Loader Core & Settings
src/extensions/core/load3d.ts, src/schemas/apiSchema.ts, src/extensions/core/load3d/interfaces.ts
Added Comfy.Load3D.PLYEngine setting (threejs
Loader Manager & Helpers
src/extensions/core/load3d/LoaderManager.ts, src/extensions/core/load3d/loader/FastPLYLoader.ts, src/scripts/metadata/ply.ts
Added FastPLYLoader class; ASCII PLY parser (parseASCIIPLY, isPLYAsciiFormat); LoaderManager additions: plyLoader, fastPlyLoader, currentLoadId, fetchModelData, loadPLY, loadSplat; handles sparkjs path, ASCII detection, vertex color handling, Points vs Mesh creation.
Scene Model Management
src/extensions/core/load3d/SceneModelManager.ts
Added containsSplatMesh() and handlePLYModeSwitch(); special-case setup for SplatMesh; recreate models for material mode switches; preserve MainModel naming and emit materialModeChange.
Load3d API
src/extensions/core/load3d/Load3d.ts
Added public methods isSplatModel() and isPlyModel().
Service Layer
src/services/load3dService.ts
copyLoad3dState() now reloads splat models from originalURL instead of cloning; reorganized state/material/up-direction propagation.
Export Guards
src/extensions/core/saveMesh.ts
Skip export menu items when model is a splat model (early return).
Localization
src/locales/en/main.json
Added load3d.materialModes.pointCloud ("Point Cloud").
Tests & Test Setup
tests-ui/tests/composables/useLoad3d.test.ts, tests-ui/tests/scripts/metadata/ply.test.ts, vitest.setup.ts
Added mocks for isSplatModel/isPlyModel; added tests for ASCII PLY parser; mocked @sparkjsdev/spark SplatMesh in Vitest.
Build Chunking
vite.config.mts
vendor-three chunking now groups ids including @sparkjsdev in addition to three.

Sequence Diagram(s)

sequenceDiagram
    participant UI as User/UI
    participant Load3D as Load3D Component
    participant Composable as useLoad3d / useLoad3dViewer
    participant LoaderMgr as LoaderManager
    participant SceneMgr as SceneModelManager
    participant Three as Three.js Scene

    UI->>Load3D: upload/select .ply/.splat/.spz
    Load3D->>Composable: loadModel(path)
    Composable->>LoaderMgr: loadModelInternal(path)

    alt PLYEngine == sparkjs
        LoaderMgr->>LoaderMgr: construct SplatMesh (spark)
        LoaderMgr->>SceneMgr: return SplatMesh group
    else
        LoaderMgr->>LoaderMgr: fetch -> arrayBuffer
        LoaderMgr->>LoaderMgr: isASCII? -> parseASCIIPLY / FastPLY / PLYLoader.parse
        LoaderMgr->>LoaderMgr: compute normals, detect vertex colors
        alt material mode == pointCloud
            LoaderMgr->>LoaderMgr: create Points (point cloud)
        else
            LoaderMgr->>LoaderMgr: create Mesh
        end
        LoaderMgr->>SceneMgr: return Object3D
    end

    SceneMgr->>SceneMgr: containsSplatMesh? -> special setup or handlePLYModeSwitch
    SceneMgr->>Three: add model to scene (fit/camera/materials)
    SceneMgr->>Composable: notify model ready
    Composable->>Composable: set isSplatModel / isPlyModel
    Composable->>Load3D: propagate flags
    Load3D->>UI: adjust controls (hide material mode / enable pointCloud)
Loading
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch spark-3dgs-support

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e2ec32 and 775f784.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (24)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/Load3dViewerContent.vue (2 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/components/load3d/controls/viewer/ViewerModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/composables/useLoad3dViewer.ts (4 hunks)
  • src/extensions/core/load3d.ts (4 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (4 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/extensions/core/saveMesh.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/scripts/metadata/ply.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • tests-ui/tests/scripts/metadata/ply.test.ts (1 hunks)
  • vite.config.mts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3dViewerContent.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useLoad3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
  • src/composables/useLoad3dViewer.ts
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/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
🧠 Learnings (42)
📚 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:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.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:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mock external dependencies in tests

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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 `vitest` for unit testing in this project

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.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:

  • vitest.setup.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests dependent on non-behavioral features like utility classes or styles

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.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} : Write tests for new features

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.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/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.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/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Aim for behavioral coverage of critical and new features

Applied to files:

  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.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/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.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: Check tests-ui/README.md for test guidelines

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.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/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 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: Create browser tests as appropriate after code changes

Applied to files:

  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • package.json
  • pnpm-workspace.yaml
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{services,composables}/**/*.{ts,tsx} : Use `api.apiURL()` for backend endpoints instead of constructing URLs directly

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
🧬 Code graph analysis (6)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
src/scripts/metadata/ply.ts (1)
  • parseASCIIPLY (137-145)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (580-582)
  • isPlyModel (584-586)
tests-ui/tests/scripts/metadata/ply.test.ts (1)
src/scripts/metadata/ply.ts (2)
  • isPLYAsciiFormat (150-153)
  • parseASCIIPLY (137-145)
src/extensions/core/load3d/SceneModelManager.ts (2)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (580-582)
src/extensions/core/load3d/LoaderManager.ts (4)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (10-33)
src/scripts/api.ts (1)
  • api (1306-1306)
src/platform/settings/settingStore.ts (1)
  • useSettingStore (46-252)
src/scripts/metadata/ply.ts (1)
  • isPLYAsciiFormat (150-153)
src/composables/useLoad3dViewer.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (580-582)
  • isPlyModel (584-586)
⏰ 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). (4)
  • GitHub Check: setup
  • GitHub Check: test
  • GitHub Check: lint-and-format
  • GitHub Check: collect
🔇 Additional comments (49)
vite.config.mts (1)

438-438: LGTM!

Appropriately bundles the new SparkJS dependency with three.js in the vendor chunk, as both are related 3D rendering libraries.

src/schemas/apiSchema.ts (1)

511-511: LGTM!

The new PLY engine setting is properly typed using Zod v3's enum schema and will be correctly exposed through the exported Settings type.

pnpm-workspace.yaml (1)

30-30: LGTM!

Standard workspace catalog entry for the new SparkJS dependency. Version constraint follows conventional semver patterns.

package.json (1)

149-149: LGTM!

Correctly references the catalog entry defined in pnpm-workspace.yaml. This ensures consistent versioning across the workspace.

src/extensions/core/saveMesh.ts (1)

57-58: LGTM!

Appropriately hides export menu items for splat models, which use a different format and cannot be exported using the standard mesh export workflow.

src/locales/en/main.json (1)

1640-1640: LGTM!

The locale key "pointCloud" follows consistent naming conventions with other material mode entries and provides clear user-facing text.

Based on learnings, other locale files will be generated automatically on release.

vitest.setup.ts (1)

4-9: No action needed. SplatMesh is not referenced anywhere in the test codebase. The mock's purpose is to prevent WASM module loading errors in Node.js test environments, and the empty constructor is sufficient for this use case. There are no test calls to SplatMesh methods.

Likely an incorrect or invalid review comment.

src/extensions/core/load3d.ts (4)

201-212: LGTM! New PLYEngine setting follows existing patterns.

The setting is properly structured with experimental flag, combo type, and clear tooltip explaining each option. The default value of 'threejs' is a safe choice for backward compatibility.


252-255: LGTM! File extensions properly extended for PLY/splat support.

The accept string now includes .ply, .spz, .splat, and .ksplat formats, aligning with the updated SUPPORTED_EXTENSIONS set in interfaces.ts.


318-319: LGTM! Correct early-exit guard for splat models.

Splat models (3DGS) cannot be exported via the standard mesh export formats, so hiding the export menu items is appropriate behavior.


428-429: LGTM! Consistent early-exit guard in Preview3D context.

This mirrors the same guard in Load3D's getNodeMenuItems, maintaining consistent behavior across both node types.

src/components/load3d/Load3dViewerContent.vue (2)

49-51: LGTM! Props correctly wire viewer state to ModelControls.

The hide-material-mode binding to viewer.isSplatModel.value correctly hides material mode controls for splat models (which have their own rendering pipeline). The is-ply-model prop enables PLY-specific options like pointCloud material mode.


61-69: LGTM! Correct conditional rendering for splat models.

LightControls and ExportControls are appropriately hidden when isSplatModel is true since 3D Gaussian Splatting uses a different rendering approach that doesn't rely on traditional lighting and cannot be exported to standard mesh formats.

src/composables/useLoad3d.ts (2)

66-67: LGTM! New reactive state for model type tracking.

The isSplatModel and isPlyModel refs are properly initialized and will be populated after model loading completes.


568-569: LGTM! New state properly exposed in return object.

The isSplatModel and isPlyModel refs are correctly exposed for consumption by UI components.

src/components/load3d/Load3DControls.vue (3)

90-94: LGTM! Props correctly defined using Vue 3.5+ style.

The destructuring with defaults follows the recommended pattern per coding guidelines, avoiding withDefaults.


50-51: LGTM! Props correctly forwarded to ModelControls.

The hide-material-mode and is-ply-model props enable the child component to conditionally render material mode options and include the pointCloud mode for PLY models.


110-116: LGTM! Categories correctly filtered for splat models.

When isSplatModel is true, the available categories exclude 'light' and 'export' since 3DGS uses its own rendering pipeline without traditional lighting and cannot be exported to standard mesh formats.

src/extensions/core/load3d/interfaces.ts (2)

10-15: LGTM! MaterialMode type properly extended.

The new pointCloud mode is appropriately positioned and enables point-based rendering for PLY models using THREE.Points.


189-199: LGTM! SUPPORTED_EXTENSIONS correctly extended.

The new extensions (.spz, .splat, .ply, .ksplat) align with the PLY engine support and the file input accept string in load3d.ts.

src/components/load3d/controls/viewer/ViewerModelControls.vue (3)

35-38: LGTM! Props follow Vue 3.5 style correctly.

The props declaration uses Vue 3.5+ TypeScript-style reactive destructuring with default values, which aligns with the project's coding guidelines.


13-13: LGTM! Conditional rendering is appropriate.

The material mode panel is correctly hidden for splat models by wrapping it with v-if="!hideMaterialMode", which makes sense since splat models have different rendering requirements.


53-71: LGTM! Point cloud option correctly inserted for PLY models.

The computed property properly inserts the pointCloud material mode at index 1 when isPlyModel is true, maintaining a logical ordering: original → pointCloud (PLY-specific) → normal → wireframe.

src/components/load3d/Load3D.vue (2)

25-26: LGTM! Props correctly forwarded to Load3DControls.

The isSplatModel and isPlyModel flags are properly bound to the child component using kebab-case naming, which is the correct Vue convention for template bindings.


114-115: LGTM! Composable state properly destructured.

The new model type flags are correctly destructured from useLoad3d, following the existing pattern for other state properties.

tests-ui/tests/scripts/metadata/ply.test.ts (1)

1-447: LGTM! Comprehensive test coverage for PLY parsing.

The test suite provides excellent behavioral coverage of the ASCII PLY parsing functionality:

  • Format detection (isPLYAsciiFormat)
  • Basic position-only parsing
  • Position + color parsing with normalization
  • Robustness: non-standard property orders, extra properties, scientific notation, whitespace variations
  • Edge cases: empty buffers, invalid headers, large vertex counts, partial properties
  • Proper null returns for invalid inputs

The tests follow Vitest best practices and focus on behavioral validation rather than implementation details.

Based on learnings: Write tests for all changes, especially new features. Aim for behavioral coverage of critical and new features.

src/composables/useLoad3dViewer.ts (4)

49-50: LGTM! Model type flags properly initialized.

The reactive refs for isSplatModel and isPlyModel are correctly initialized to false, following Vue 3 Composition API patterns.


258-259: LGTM! Flags correctly populated in node mode.

After loading the model configuration in node mode, the flags are properly populated by calling the source Load3d instance's type detection methods.


309-310: LGTM! Flags correctly populated in standalone mode.

After loading the model in standalone mode, the flags are properly populated by calling the Load3d instance's type detection methods.


527-528: LGTM! Public API correctly extended.

The composable properly exposes isSplatModel and isPlyModel in its return object, allowing consumers to access the model type information.

src/extensions/core/load3d/Load3d.ts (2)

580-582: LGTM! Splat model detection correctly delegates to model manager.

The method properly delegates to modelManager.containsSplatMesh() to detect whether the loaded model contains splat mesh content.


584-586: The current implementation correctly handles all PLY loading paths. Both the Three.js PLYLoader and FastPLYLoader return THREE.BufferGeometry, which is correctly stored in originalModel. Splat files (SparkJS engine) are handled separately with a dedicated isSplatModel() method that checks for SplatMesh instances. The isPlyModel() check accurately identifies point cloud PLY files across both supported point cloud engines (threejs and fastply).

Likely an incorrect or invalid review comment.

src/components/load3d/controls/ModelControls.vue (3)

74-77: LGTM! Props follow Vue 3.5 style correctly.

The props declaration uses Vue 3.5+ TypeScript-style reactive destructuring with default values, consistent with the project's coding guidelines and the pattern used in ViewerModelControls.vue.


31-31: LGTM! Conditional rendering is appropriate.

The material mode panel is correctly hidden for splat models by wrapping it with v-if="!hideMaterialMode", which aligns with the splat model rendering requirements.


95-109: LGTM! Point cloud option correctly inserted for PLY models.

The computed property properly inserts the pointCloud material mode at index 1 when isPlyModel is true, using splice to maintain the ordering: original → pointCloud (PLY-specific) → normal → wireframe.

src/extensions/core/load3d/SceneModelManager.ts (3)

1-1: LGTM on the SplatMesh import.

The import from @sparkjsdev/spark is correctly placed and enables 3DGS support.


248-252: LGTM on the PLY early-return routing.

The BufferGeometry check correctly delegates PLY files to handlePLYModeSwitch for special handling since they require geometry recreation for mode changes.


427-440: LGTM on SplatMesh handling in setupModel.

The early return for SplatMesh models correctly bypasses standard fitting since SplatMesh handles its own rendering. The fixed camera distance of (5, 5, 5) provides a reasonable default view.

src/scripts/metadata/ply.ts (3)

6-24: LGTM on the PLY type definitions.

The PLYHeader and PLYData interfaces are well-structured and provide clear typing for the parser output.


137-153: LGTM on the exported functions.

parseASCIIPLY and isPLYAsciiFormat are well-documented and provide a clean public API. The 500-byte slice for format detection is a reasonable heuristic.


1-5: Test coverage is comprehensive; add NaN/invalid numeric input tests.

Tests exist at tests-ui/tests/scripts/metadata/ply.test.ts and cover empty buffers, malformed headers, and most edge cases. However, NaN values from invalid numeric input (e.g., parsing non-numeric strings as coordinates) are not explicitly tested. Consider adding a test case for this scenario.

src/extensions/core/load3d/LoaderManager.ts (8)

1-21: LGTM on the new imports.

The imports are well-organized and appropriate for PLY/splat loading functionality. Type imports remain properly separated per repository conventions.


29-50: LGTM on the loader initialization and load guard.

The currentLoadId pattern effectively prevents race conditions between concurrent load operations. The new loaders are properly initialized in the constructor.


56-106: LGTM on the load guard implementation.

The loadId pattern correctly handles race conditions where a new load starts before a previous one completes. Both success and error paths properly check if the load is still current.


209-219: LGTM - switch scope leakage addressed.

The splat case now delegates to loadSplat() method, eliminating the scope leakage issue flagged in past reviews where const declarations could leak across switch clauses.


224-232: LGTM - response.ok check and API integration addressed.

Past comments flagged missing response.ok validation and the need to use api.fetchApi for cloud authentication. Both issues are now properly addressed.


234-245: LGTM on loadSplat method.

The extraction addresses the duplication concern from past reviews. Using fileBytes with SplatMesh ensures the data is loaded through the authenticated API.


264-269: LGTM on the PLY engine selection logic.

The conditional correctly chooses between fastPlyLoader (for ASCII when configured) and the standard plyLoader, providing flexibility for performance optimization.


278-313: LGTM on pointCloud rendering path.

The Points-based rendering correctly handles bounding sphere centering/scaling and vertex colors. This provides appropriate visualization for point cloud data.


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

❤️ Share

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

@socket-security
Copy link

socket-security bot commented Dec 18, 2025

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​sparkjsdev/​spark@​0.1.108710010087100

View full report

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d014c0 and af6e004.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/extensions/core/load3d.ts (2 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (3 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/SceneModelManager.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/SceneModelManager.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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
🧠 Learnings (15)
📚 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:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/SceneModelManager.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:

  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.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/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.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/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • pnpm-workspace.yaml
  • package.json
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 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 : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/load3d/Load3DControls.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
🧬 Code graph analysis (2)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
src/extensions/core/load3d/SceneModelManager.ts (3)
src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts (1)
  • mode (80-82)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
🪛 Biome (2.1.2)
src/extensions/core/load3d/LoaderManager.ts

[error] 206-206: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 207-207: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 209-209: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (23)
package.json (1)

149-149: LGTM - Correctly references the catalog entry.

The dependency correctly uses the catalog: reference pattern to pull from the pnpm-workspace.yaml catalog definition. The alphabetical ordering is maintained, and placement in runtime dependencies (rather than devDependencies) is appropriate for this 3D rendering library.

Note: Package verification for version validity and security was already requested in the pnpm-workspace.yaml review.

pnpm-workspace.yaml (1)

30-30: Verify package availability and source before adding to catalog.

The @sparkjsdev/spark package references a legitimate GitHub repository for a 3D Gaussian Splatting renderer, with version 0.1.10 documented in the official repository. However, the package does not appear to be publicly available on the npm registry, making it unclear whether it can be installed as a dependency. Before adding this to the catalog, confirm:

  1. The package is properly published to a registry (npm or otherwise)
  2. Whether it should reference the direct GitHub URL instead of npm
  3. Version 0.1.10 is the intended release (version 0.1.9 is also referenced in the repository)
src/extensions/core/load3d.ts (2)

252-255: LGTM! File format expansion aligns with PLY/splat support.

The expanded file input correctly adds .ply, .spz, .splat, and .ksplat formats, which aligns with the PR's objective to support 3D Gaussian Splatting and PLY files. The format string is properly structured.


201-212: Setting is properly integrated and consumed by LoaderManager.ts

The PLYEngine setting correctly controls the PLY file loader selection. It's defined in the schema with the correct enum values, initialized with 'threejs' as default, and used in LoaderManager.ts:222 to branch between sparkjs for Gaussian Splatting and Three.js/FastPLYLoader for mesh/point cloud files.

src/schemas/apiSchema.ts (1)

511-511: LGTM! PLYEngine setting follows the established pattern.

The new setting integrates cleanly with the existing Load3D configuration schema and uses appropriate Zod v3 syntax.

src/extensions/core/load3d/Load3d.ts (1)

574-576: LGTM! Clean delegation to model manager.

The method follows the established pattern of delegating to specialized managers and provides a clear public API for splat model detection.

src/components/load3d/Load3DControls.vue (3)

89-91: LGTM! Proper Vue 3.5 style prop declaration.

The prop declaration follows Vue 3.5 TypeScript style with reactive destructuring and default value.


108-113: LGTM! Appropriate UI adjustment for splat models.

Excluding the 'light' category for splat models makes sense, as 3D Gaussian Splatting point clouds typically don't use traditional lighting models. The conditional logic is clean and maintainable.


50-50: LGTM! Clean prop binding to child component.

The binding appropriately propagates splat model state to ModelControls for conditional material mode visibility.

src/extensions/core/load3d/interfaces.ts (2)

194-198: LGTM! Extension set properly expanded for PLY and splat formats.

The additions align with the PR's objective to support 3D Gaussian Splatting and PLY files from various sources.


10-15: i18n translation for 'original(Advanced)' is properly defined. The key load3d.materialModes.original(Advanced) with the value "Original (Point Cloud)" already exists in src/locales/en/main.json and is correctly nested alongside other material mode translations. No action required.

src/composables/useLoad3d.ts (3)

66-66: LGTM! Reactive state follows Vue patterns.

The initialization appropriately defaults to false for non-splat models.


494-494: LGTM! Proper state synchronization on model load.

The isSplatModel state is correctly updated when model loading completes, with defensive null-coalescing to handle edge cases.


566-566: LGTM! State properly exposed in public API.

The isSplatModel state is now available to consuming components, completing the propagation chain.

src/components/load3d/controls/ModelControls.vue (3)

31-60: LGTM! Clean conditional rendering for material mode UI.

The v-if directive appropriately hides the material mode section for splat models while preserving the up-direction controls.


74-76: LGTM! Proper Vue 3.5 style prop declaration.

The prop declaration follows Vue 3.5 TypeScript style with reactive destructuring and default value.


94-104: LGTM! Material modes list expanded to include advanced option.

The new 'original(Advanced)' mode is properly integrated into the options list. The computed property pattern is appropriate here for potential future dynamic filtering.

src/components/load3d/Load3D.vue (2)

25-25: LGTM! Proper prop binding to child component.

The isSplatModel state is correctly propagated from the composable to Load3DControls, enabling splat-model-specific UI behavior.


113-113: LGTM! Composable state properly destructured.

The isSplatModel state is correctly extracted from the useLoad3d composable alongside other state properties.

src/extensions/core/load3d/SceneModelManager.ts (4)

102-105: Guard clause logic is correct but could be clarified.

The method returns early if originalModel is not a BufferGeometry, which is the expected behavior for handling PLY files specifically. The implementation is correct.


334-351: Alias handling for original(Advanced) mode is appropriate.

Adding the original(Advanced) case to fall through to original behavior ensures backward compatibility when restoring materials on non-PLY meshes. This is the correct approach.


422-422: Consistent MainModel naming improves lifecycle management.

Tagging models with name = 'MainModel' in both addModelToScene and setupModel enables reliable cleanup in handlePLYModeSwitch. Good pattern for managing scene objects.

Also applies to: 429-429


427-440: SplatMesh early return skips initial material and direction setup—verify if runtime changes are needed.

When isSplatModel is true, setupModel returns early (line 439), skipping setupModelMaterials and setUpDirection initialization. This is intentional since SplatMesh handles its own rendering. However, UI controls allow users to change material mode and up-direction after loading. While setMaterialMode safely skips SplatMesh (traverses only Mesh children), setUpDirection applies rotations directly to currentModel. Consider whether splat models should support these runtime changes or if UI controls should be disabled for splat models to prevent unintended visual behavior.

Comment on lines 9 to 11
parse(arrayBuffer: ArrayBuffer): THREE.BufferGeometry {
const text = new TextDecoder().decode(arrayBuffer)
const lines = text.split('\n')
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing error handling for malformed PLY files.

The parser assumes valid PLY format but doesn't validate the magic number (ply) at the start or handle cases where required properties (x, y, z) are missing. This could lead to silent failures or NaN values in the geometry.

🔎 Suggested improvement:
 parse(arrayBuffer: ArrayBuffer): THREE.BufferGeometry {
   const text = new TextDecoder().decode(arrayBuffer)
   const lines = text.split('\n')
+
+  // Validate PLY magic number
+  if (!lines[0]?.trim().toLowerCase().startsWith('ply')) {
+    throw new Error('Invalid PLY file: missing PLY header')
+  }

   // Parse header
   let vertexCount = 0

Also validate required properties after header parsing:

     } else if (line === 'end_header') {
       headerEndLine = i
       break
     }
   }
+
+  if (xIndex < 0 || yIndex < 0 || zIndex < 0) {
+    throw new Error('Invalid PLY file: missing required position properties (x, y, z)')
+  }
+
+  if (vertexCount === 0) {
+    throw new Error('Invalid PLY file: no vertices defined')
+  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/extensions/core/load3d/loader/FastPLYLoader.ts around lines 9 to 11, the
parser decodes the buffer and splits lines but does not validate the PLY magic
header or ensure required vertex properties (x,y,z) exist; add explicit
validation after decoding: check the first non-empty token equals "ply" (throw
or return a clear error if not), parse the header to confirm vertex element and
that properties include x, y, z, and if any required property is missing throw a
descriptive error; also guard against malformed numeric data by validating
parsed floats (reject or skip vertices with NaN) and ensure the function returns
a predictable failure path instead of producing NaN coordinates.

Comment on lines 50 to 52
// Pre-allocate arrays
const positions = new Float32Array(vertexCount * 3)
const colors = hasColor ? new Float32Array(vertexCount * 3) : null
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Pre-allocated buffer may be larger than needed if vertices are skipped.

The buffer is pre-allocated based on vertexCount, but blank lines and lines with insufficient parts are skipped (lines 63, 66). This could leave trailing zeros in the buffer. Consider trimming the geometry or tracking actual vertex count.

🔎 Suggested approach:

After the parsing loop, trim the buffers if vertexIndex < vertexCount:

   // Create geometry
   const geometry = new THREE.BufferGeometry()
-  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
+  const actualPositions = vertexIndex < vertexCount
+    ? positions.slice(0, vertexIndex * 3)
+    : positions
+  geometry.setAttribute('position', new THREE.BufferAttribute(actualPositions, 3))
   if (hasColor && colors) {
-    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))
+    const actualColors = vertexIndex < vertexCount
+      ? colors.slice(0, vertexIndex * 3)
+      : colors
+    geometry.setAttribute('color', new THREE.BufferAttribute(actualColors, 3))
   }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/extensions/core/load3d/loader/FastPLYLoader.ts around lines 50 to 52, the
positions/colors Float32Array buffers are pre-allocated to vertexCount but some
input lines are skipped, leaving trailing zeros; after the parsing loop detect
if the actual vertexIndex is less than vertexCount and replace/truncate the
buffers to their used length (e.g., create subarrays or new typed arrays of
vertexIndex*3 length for positions and colors when hasColor), then use those
trimmed arrays when creating/setting the geometry attributes and update any
counts/drawRange accordingly so the resulting geometry contains only the parsed
vertices.

Comment on lines 71 to 73
positions[posIndex] = parseFloat(parts[xIndex])
positions[posIndex + 1] = parseFloat(parts[yIndex])
positions[posIndex + 2] = parseFloat(parts[zIndex])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

parseFloat returns NaN silently on invalid input.

If a line contains non-numeric data or the property indices are wrong, parseFloat will return NaN, causing corrupted geometry without any indication. Consider adding validation or at least a check for NaN.

🔎 Consider adding NaN checks:
      // Position
-     positions[posIndex] = parseFloat(parts[xIndex])
-     positions[posIndex + 1] = parseFloat(parts[yIndex])
-     positions[posIndex + 2] = parseFloat(parts[zIndex])
+     const x = parseFloat(parts[xIndex])
+     const y = parseFloat(parts[yIndex])
+     const z = parseFloat(parts[zIndex])
+
+     if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) {
+       continue // Skip malformed vertices
+     }
+
+     positions[posIndex] = x
+     positions[posIndex + 1] = y
+     positions[posIndex + 2] = z
📝 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
positions[posIndex] = parseFloat(parts[xIndex])
positions[posIndex + 1] = parseFloat(parts[yIndex])
positions[posIndex + 2] = parseFloat(parts[zIndex])
// Position
const x = parseFloat(parts[xIndex])
const y = parseFloat(parts[yIndex])
const z = parseFloat(parts[zIndex])
if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) {
continue // Skip malformed vertices
}
positions[posIndex] = x
positions[posIndex + 1] = y
positions[posIndex + 2] = z
🤖 Prompt for AI Agents
In src/extensions/core/load3d/loader/FastPLYLoader.ts around lines 71-73, the
code uses parseFloat without validating results which can silently produce NaN
and corrupt geometry; update the loader to validate each parsed coordinate after
parseFloat (use Number.isFinite or !Number.isNaN) and handle invalid values
explicitly: either throw a descriptive error including the line number/index and
offending property indices so the load fails fast, or skip the malformed vertex
(adjusting the write index accordingly) and log a warning. Ensure you do not
write NaN into the positions buffer (only write after validation) and include
clear context in any thrown/logged message so debugging identifies the exact
line and property.

Comment on lines 203 to 218
case 'spz':
case 'splat':
case 'ksplat':
const splatUrl = path + encodeURIComponent(filename)
const splatMesh = new SplatMesh({ url: splatUrl })
this.modelManager.setOriginalModel(splatMesh)
const splatGroup = new THREE.Group()
splatGroup.add(splatMesh)
model = splatGroup
break
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Wrap switch case declarations in blocks to prevent scope leakage.

Biome correctly flags that splatUrl, splatMesh, and splatGroup declarations can leak to other switch clauses. Wrap them in a block scope.

🔎 Apply this fix:
      case 'spz':
      case 'splat':
-     case 'ksplat':
-       const splatUrl = path + encodeURIComponent(filename)
-       const splatMesh = new SplatMesh({ url: splatUrl })
-       this.modelManager.setOriginalModel(splatMesh)
-       const splatGroup = new THREE.Group()
-       splatGroup.add(splatMesh)
-       model = splatGroup
+     case 'ksplat': {
+       const splatUrl = path + encodeURIComponent(filename)
+       const splatMesh = new SplatMesh({ url: splatUrl })
+       this.modelManager.setOriginalModel(splatMesh)
+       const splatGroup = new THREE.Group()
+       splatGroup.add(splatMesh)
+       model = splatGroup
        break
+     }
📝 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
case 'spz':
case 'splat':
case 'ksplat':
const splatUrl = path + encodeURIComponent(filename)
const splatMesh = new SplatMesh({ url: splatUrl })
this.modelManager.setOriginalModel(splatMesh)
const splatGroup = new THREE.Group()
splatGroup.add(splatMesh)
model = splatGroup
break
case 'spz':
case 'splat':
case 'ksplat': {
const splatUrl = path + encodeURIComponent(filename)
const splatMesh = new SplatMesh({ url: splatUrl })
this.modelManager.setOriginalModel(splatMesh)
const splatGroup = new THREE.Group()
splatGroup.add(splatMesh)
model = splatGroup
break
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 206-206: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 207-207: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 209-209: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
In src/extensions/core/load3d/LoaderManager.ts around lines 203 to 212, the
local variables splatUrl, splatMesh, and splatGroup declared in the
'spz'/'splat'/'ksplat' switch case can leak into other cases; wrap the case body
in its own block: add { ... } after the case labels, move the const declarations
and all related statements inside that block, and keep the final model =
splatGroup and break inside the block so the declarations remain scoped to this
case.

@github-actions
Copy link

github-actions bot commented Dec 18, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 12/20/2025, 02:38:27 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Dec 18, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 12/20/2025, 02:46:59 PM UTC

📈 Summary

  • Total Tests: 509
  • Passed: 500 ✅
  • Failed: 0
  • Flaky: 1 ⚠️
  • Skipped: 8 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 488 / ❌ 0 / ⚠️ 1 / ⏭️ 8
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 9 / ❌ 0 / ⚠️ 0 / ⏭️ 0

🎉 Click on the links above to view detailed test results for each browser configuration.

@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Dec 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
src/extensions/core/load3d/SceneModelManager.ts (1)

286-288: Type assertion violates coding guidelines—fix the underlying type issue.

The originalMaterials WeakMap is typed for THREE.Mesh but here stores a THREE.Points object using as unknown as THREE.Mesh. This type coercion hides a design issue.

🔎 Consider these solutions:

Option 1: Widen the WeakMap key type to accept both Mesh and Points:

-  originalMaterials: WeakMap<THREE.Mesh, THREE.Material | THREE.Material[]> =
-    new WeakMap()
+  originalMaterials: WeakMap<THREE.Mesh | THREE.Points, THREE.Material | THREE.Material[]> =
+    new WeakMap()

Then remove the cast:

-      this.modelManager.originalMaterials.set(
-        plyPoints as unknown as THREE.Mesh,
-        pointMaterial
-      )
+      this.modelManager.originalMaterials.set(plyPoints, pointMaterial)

Option 2: Use a union type alias for clarity:

+type RenderableObject = THREE.Mesh | THREE.Points
-  originalMaterials: WeakMap<THREE.Mesh, THREE.Material | THREE.Material[]> =
-    new WeakMap()
+  originalMaterials: WeakMap<RenderableObject, THREE.Material | THREE.Material[]> =
+    new WeakMap()

This change would also require updating related traversal code that checks child instanceof THREE.Mesh to also handle THREE.Points.

As per coding guidelines: avoid as type assertions and fix the underlying type issue.

♻️ Duplicate comments (13)
src/locales/en/main.json (1)

1638-1638: Consider consistent key naming convention.

The key "original(Advanced)" deviates from the naming pattern used by other material mode keys in this section (e.g., normal, wireframe, original). Consider using a more conventional format like "originalAdvanced" or "pointCloud" for better consistency and to avoid special characters in locale keys.

🔎 Suggested alternatives:
-      "original(Advanced)": "Original (Point Cloud)",
+      "originalAdvanced": "Original (Point Cloud)",

Or:

-      "original(Advanced)": "Original (Point Cloud)",
+      "pointCloud": "Original (Point Cloud)",

Note: This change would require updating all references to this key throughout the codebase.

src/services/load3dService.ts (1)

78-102: Add error handling for splat models without originalURL.

The logic correctly differentiates between splat models (which must be reloaded via URL) and regular models (which can be cloned). However, when a splat model lacks an originalURL, the method silently skips model loading, which could lead to confusing behavior or silent failures.

🔎 Consider adding explicit error handling:
 if (source.isSplatModel()) {
   const originalURL = source.modelManager.originalURL
   if (originalURL) {
     await target.loadModel(originalURL)
+  } else {
+    console.warn('Cannot copy splat model: originalURL is missing')
+    // Optionally throw or handle gracefully
   }
 } else {

Additional consideration: Verify whether appliedTexture handling (lines 98-101) should also apply to splat models, or if this is intentionally excluded due to technical limitations.

src/extensions/core/load3d/loader/FastPLYLoader.ts (3)

9-11: Missing error handling for malformed PLY files.

The parser doesn't validate the PLY magic header or required properties. This was flagged in a previous review.


50-52: Pre-allocated buffer may be larger than needed if vertices are skipped.

This was flagged in a previous review - buffers should be trimmed to actual vertex count after parsing.


71-73: parseFloat returns NaN silently on invalid input.

This was flagged in a previous review - add NaN validation to prevent corrupted geometry.

src/extensions/core/load3d/SceneModelManager.ts (3)

111-131: Redundant scene traversal for MainModel removal.

This traversal to find and remove MainModel instances is followed by another identical check at lines 206-212. This was previously flagged as inefficient.


206-212: Redundant double-check removal.

This second traversal duplicates the removal logic from lines 111-131. If the first removal is working correctly, this shouldn't be necessary.


471-480: containsSplatMesh doesn't short-circuit traversal.

This method continues traversing the entire scene graph even after finding a SplatMesh. This was previously identified and a recursive solution was suggested.

src/extensions/core/load3d/LoaderManager.ts (5)

203-212: Wrap switch case declarations in blocks.

Biome correctly flags that splatUrl, splatMesh, and splatGroup can leak to other switch clauses. This was previously identified with a fix provided.


222-222: Use proper TypeScript type instead of as string cast.

The setting value is cast to string, which can hide type issues if the setting is undefined or another type. This was previously flagged with a suggestion to use nullish coalescing.


224-231: Duplicate SplatMesh creation logic.

This SplatMesh creation duplicates lines 206-211. A helper method was previously suggested to eliminate this duplication.


234-236: Missing response.ok check before reading response body.

If the fetch fails with a 4xx/5xx status, response.arrayBuffer() will still execute, potentially parsing an error page as binary data. This was previously identified with a fix provided.


273-293: Material creation logic duplicated with SceneModelManager.

This Points/Mesh creation logic is duplicated in SceneModelManager.handlePLYModeSwitch. This was previously noted as potentially acceptable given the different contexts (initial load vs. mode switching).

Also applies to: 296-316

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af6e004 and 92da937.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/extensions/core/load3d.ts (2 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (3 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/services/load3dService.ts
  • src/composables/useLoad3d.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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
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/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/composables/useLoad3d.test.ts
🧠 Learnings (35)
📚 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:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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 `vitest` for unit testing in this project

Applied to files:

  • vitest.setup.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} : Mock external dependencies in tests

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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} : Write tests for new features

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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 : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/load3d/Load3DControls.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.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/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.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 defineExpose only for imperative operations (such as form.validate(), modal.open())

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • pnpm-workspace.yaml
  • package.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write change detector tests - do not just assert that defaults are certain values

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
🧬 Code graph analysis (3)
src/extensions/core/load3d/SceneModelManager.ts (3)
src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts (1)
  • mode (80-82)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
src/extensions/core/load3d/LoaderManager.ts (1)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (8-102)
🪛 Biome (2.1.2)
src/extensions/core/load3d/LoaderManager.ts

[error] 206-206: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 207-207: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 209-209: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (2)
  • GitHub Check: collect
  • GitHub Check: comment-on-pr-start
🔇 Additional comments (26)
pnpm-workspace.yaml (1)

30-30: LGTM: SparkJS catalog entry added correctly.

The workspace catalog entry for @sparkjsdev/spark follows the established pattern and uses an appropriate version constraint.

package.json (1)

149-149: LGTM: Runtime dependency correctly added.

The SparkJS dependency is properly added using the workspace catalog reference, consistent with other dependencies in the project.

vitest.setup.ts (1)

4-9: LGTM: Appropriate test mock for WASM-dependent library.

The mock correctly bypasses SparkJS's WASM dependencies for the Node.js test environment. The comment clearly explains the reasoning.

As per coding guidelines, leveraging Vitest utilities for mocking.

tests-ui/tests/composables/useLoad3d.test.ts (1)

108-108: LGTM: Mock method added consistently.

The isSplatModel mock follows the existing pattern and provides a reasonable default return value for non-splat model test scenarios.

Consider adding test cases that cover splat model behavior (isSplatModel returning true) to ensure the new code paths are tested.

As per coding guidelines, write tests for new features and aim for behavioral coverage of critical features.

src/extensions/core/load3d.ts (2)

252-255: LGTM: File extensions updated for PLY and splat formats.

The updated file input filter appropriately includes the new supported formats (.ply, .spz, .splat, .ksplat) for 3D Gaussian Splatting and PLY files.


202-212: PLYEngine setting is properly consumed by loader logic.

The Comfy.Load3D.PLYEngine setting is actively used in LoaderManager.ts:222 to select the appropriate loader: Spark.js for Gaussian Splatting files, FastPLYLoader for ASCII point clouds, and Three.js PLYLoader as default.

src/schemas/apiSchema.ts (1)

511-511: LGTM: Schema correctly updated for PLYEngine setting.

The Comfy.Load3D.PLYEngine setting is properly added to the schema with an enum matching the extension definition. The values align with the three supported PLY loading engines.

As per learnings, this follows Zod v3 patterns correctly.

src/extensions/core/load3d/Load3d.ts (1)

574-576: LGTM!

Clean delegation to the model manager. The method is well-placed and provides a clear public API for checking if the current model is a splat mesh.

src/components/load3d/Load3DControls.vue (2)

89-91: LGTM!

Prop declaration follows Vue 3.5+ style with reactive destructuring as per coding guidelines.


108-114: LGTM!

Excluding the light category for splat models is appropriate since 3D Gaussian Splatting models have pre-baked radiance and don't respond to dynamic lighting.

src/composables/useLoad3d.ts (1)

66-66: LGTM!

Proper initialization of the reactive state.

src/extensions/core/load3d/interfaces.ts (2)

10-15: LGTM!

The MaterialMode type correctly extends to include the new point cloud rendering option.


189-199: LGTM!

SUPPORTED_EXTENSIONS correctly expanded to include all PLY and splat-related file formats.

src/components/load3d/controls/ModelControls.vue (2)

74-76: LGTM!

Prop declaration follows Vue 3.5+ style with reactive destructuring as per coding guidelines.


126-128: Remove this comment — no i18n issue exists.

The key load3d.materialModes.original(Advanced) is correctly defined in src/locales/en/main.json as "original(Advanced)": "Original (Point Cloud)". Parentheses are not special characters in vue-i18n keys and pose no runtime issues. The implementation is functioning correctly.

Likely an incorrect or invalid review comment.

src/components/load3d/Load3D.vue (2)

25-25: LGTM!

Prop binding correctly wires isSplatModel state to Load3DControls using kebab-case in template.


113-113: LGTM!

Correctly destructures isSplatModel from the composable's return value.

src/extensions/core/load3d/SceneModelManager.ts (5)

1-1: LGTM: SplatMesh import.

The import is properly structured and necessary for the 3D Gaussian Splatting support.


248-252: LGTM: PLY special handling.

The early return pattern cleanly separates PLY-specific mode switching from the general material mode logic.


335-335: LGTM: Added 'original(Advanced)' case.

Correctly handles the new MaterialMode variant per the interface definition in interfaces.ts.


422-422: LGTM: Consistent model naming.

Setting name = 'MainModel' establishes a clear identifier for scene lifecycle management.


431-440: SplatMesh early return is appropriate.

The early return pattern correctly bypasses standard mesh setup for SplatMesh models, which handle their own rendering. The default camera distance setup is reasonable.

Verify that the hardcoded Vector3(5, 5, 5) provides appropriate framing for typical splat models. If splat models vary significantly in scale, consider computing a size hint from the SplatMesh bounds if available.

src/extensions/core/load3d/LoaderManager.ts (4)

1-1: LGTM: New imports for PLY and splat support.

All imports are properly structured and necessary for the new functionality.

Also applies to: 7-7, 11-11, 19-19


27-28: LGTM: Loader initialization.

The PLY loaders follow the established pattern for other loaders in this class.

Also applies to: 45-46


199-201: LGTM: PLY case delegation.

Cleanly delegates PLY loading to the dedicated method.


238-253: PLY format detection and parsing logic looks sound.

The ASCII format detection and conditional loader selection is appropriate. The plyLoader.setPath(path) at line 248 may be unnecessary since parse(arrayBuffer) is called directly with the buffer rather than loading a file, but this is harmless.

@github-actions
Copy link

github-actions bot commented Dec 18, 2025

Bundle Size Report

Summary

  • Raw size: 17.3 MB baseline 16.6 MB — 🔴 +660 kB
  • Gzip: 3.53 MB baseline 3.37 MB — 🔴 +159 kB
  • Brotli: 2.69 MB baseline 2.59 MB — 🔴 +97.1 kB
  • Bundles: 98 current • 98 baseline • 43 added / 43 removed

Category Glance
Vendor & Third-Party 🔴 +644 kB (9.1 MB) · App Entry Points 🔴 +14.5 kB (3.22 MB) · UI Components 🔴 +843 B (186 kB) · Other ⚪ 0 B (3.44 MB) · Graph Workspace ⚪ 0 B (993 kB) · Panels & Settings ⚪ 0 B (297 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.22 MB (baseline 3.21 MB) • 🔴 +14.5 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-BTdK9q0w.js (new) 2.99 MB 🔴 +2.99 MB 🔴 +631 kB 🔴 +480 kB
assets/index-CkrBk5Nz.js (removed) 2.98 MB 🟢 -2.98 MB 🟢 -628 kB 🟢 -477 kB
assets/index-BWxox_G3.js (new) 228 kB 🔴 +228 kB 🔴 +48.7 kB 🔴 +40 kB
assets/index-wJ4XPLGO.js (removed) 227 kB 🟢 -227 kB 🟢 -48.5 kB 🟢 -39.9 kB
assets/index-B31J6g3I.js (new) 345 B 🔴 +345 B 🔴 +246 B 🔴 +202 B
assets/index-CokVhjgM.js (removed) 345 B 🟢 -345 B 🟢 -243 B 🟢 -201 B

Status: 3 added / 3 removed

Graph Workspace — 993 kB (baseline 993 kB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-Bt5UyvMn.js (removed) 993 kB 🟢 -993 kB 🟢 -194 kB 🟢 -147 kB
assets/GraphView-ippgpfuA.js (new) 993 kB 🔴 +993 kB 🔴 +194 kB 🔴 +147 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-B477QlBQ.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.9 kB
assets/UserSelectView-mcDPXFyd.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.9 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-Ctd8DOti.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.16 kB 🟢 -4.51 kB
assets/LegacyCreditsPanel-UJkeTSzh.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.16 kB 🔴 +4.51 kB
assets/KeybindingPanel-BouDGMid.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3.01 kB
assets/KeybindingPanel-CwojsBWE.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.02 kB
assets/ExtensionPanel-CBoIamt-.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.57 kB 🔴 +2.24 kB
assets/ExtensionPanel-CYQinV-k.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.57 kB 🟢 -2.24 kB
assets/AboutPanel-BKJuA3R_.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.2 kB
assets/AboutPanel-D6H_JqoV.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/ServerConfigPanel-BKtM2dPG.js (new) 7.07 kB 🔴 +7.07 kB 🔴 +1.99 kB 🔴 +1.76 kB
assets/ServerConfigPanel-DenrXFH9.js (removed) 7.07 kB 🟢 -7.07 kB 🟢 -1.98 kB 🟢 -1.76 kB
assets/UserPanel-DHu7F6A7.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.72 kB 🔴 +1.5 kB
assets/UserPanel-DvQdKVSK.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.72 kB 🟢 -1.5 kB
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BvFz-TmQ.js 33.1 kB 33.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BxYnTA2y.js 21.5 kB 21.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C6bMjAne.js 27.1 kB 27.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C7Yhp7H7.js 24 kB 24 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CsnOn8G0.js 25.7 kB 25.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-D5BGNZ7M.js 24.8 kB 24.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-D7k3huHm.js 25 kB 25 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DamkILzl.js 26.4 kB 26.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-s1k0gw1Y.js 20.9 kB 20.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

UI Components — 186 kB (baseline 185 kB) • 🔴 +843 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-MK1YMhhp.js (new) 54.3 kB 🔴 +54.3 kB 🔴 +8.62 kB 🔴 +7.42 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-CstA2wQE.js (removed) 53.5 kB 🟢 -53.5 kB 🟢 -8.45 kB 🟢 -7.25 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-BUX5GCl-.js (removed) 50 kB 🟢 -50 kB 🟢 -11 kB 🟢 -9.63 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-ChXbAbx7.js (new) 50 kB 🔴 +50 kB 🔴 +11 kB 🔴 +9.63 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-CdFVLxak.js (new) 48.8 kB 🔴 +48.8 kB 🔴 +10.5 kB 🔴 +9.13 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-D7FcgoNN.js (removed) 48.8 kB 🟢 -48.8 kB 🟢 -10.5 kB 🟢 -9.13 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-B9uyaUE8.js (removed) 14.3 kB 🟢 -14.3 kB 🟢 -3.73 kB 🟢 -3.29 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-Dn-H6wNX.js (new) 14.3 kB 🔴 +14.3 kB 🔴 +3.74 kB 🔴 +3.29 kB
assets/ComfyQueueButton-BN22pRW0.js (removed) 8.45 kB 🟢 -8.45 kB 🟢 -2.47 kB 🟢 -2.2 kB
assets/ComfyQueueButton-ByTRVjtV.js (new) 8.45 kB 🔴 +8.45 kB 🔴 +2.47 kB 🔴 +2.19 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-CFLlciBb.js (new) 3.68 kB 🔴 +3.68 kB 🔴 +1.45 kB 🔴 +1.31 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-D6Lf_72S.js (removed) 3.68 kB 🟢 -3.68 kB 🟢 -1.45 kB 🟢 -1.31 kB
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-BY8Zq-et.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +889 B 🔴 +768 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-BZFmgyCD.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -890 B 🟢 -770 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-C9Rmxz-u.js (removed) 897 B 🟢 -897 B 🟢 -501 B 🟢 -439 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-RzEWJDS7.js (new) 897 B 🔴 +897 B 🔴 +505 B 🔴 +431 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-DymEwRe0.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-U5ly29Pt.js 2.04 kB 2.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 removed

Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-_ITE3HQP.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/keybindingService-CRAMyOM1.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/audioService-BGjYKxJG.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -964 B 🟢 -821 B
assets/audioService-CW8dfpSS.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +961 B 🔴 +828 B
assets/serverConfigStore-7JI8VFS2.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-AdB5jEpM.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +651 B 🔴 +548 B
assets/audioUtils-DU_P5eEm.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -652 B 🟢 -549 B

Status: 1 added / 1 removed

Vendor & Third-Party — 9.1 MB (baseline 8.46 MB) • 🔴 +644 kB

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-three-DYL0ZbEr.js (new) 2.02 MB 🔴 +2.02 MB 🔴 +416 kB 🔴 +298 kB
assets/vendor-three-C1LfRdh4.js (removed) 1.37 MB 🟢 -1.37 MB 🟢 -260 kB 🟢 -204 kB
assets/vendor-chart-DaR9dw3X.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-Cf7iqkoX.js 3.86 MB 3.86 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-BIgr9yoj.js 1.96 MB 1.96 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-C1NB2Q_L.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-DSfeuQOV.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BF8peZ5_.js 420 kB 420 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

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

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SubscriptionRequiredDialogContent-BDhtDez4.js (removed) 28 kB 🟢 -28 kB 🟢 -6.34 kB 🟢 -5.5 kB
assets/SubscriptionRequiredDialogContent-BZAWx_by.js (new) 28 kB 🔴 +28 kB 🔴 +6.34 kB 🔴 +5.5 kB
assets/WidgetRecordAudio-0XtT0GjG.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.24 kB 🟢 -4.63 kB
assets/WidgetRecordAudio-B8AKEB5S.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.23 kB 🔴 +4.63 kB
assets/AudioPreviewPlayer-CcgrS_i_.js (new) 13.3 kB 🔴 +13.3 kB 🔴 +3.35 kB 🔴 +2.99 kB
assets/AudioPreviewPlayer-CQtIzPMe.js (removed) 13.3 kB 🟢 -13.3 kB 🟢 -3.35 kB 🟢 -2.99 kB
assets/ValueControlPopover-Bm9ljVgS.js (removed) 5.49 kB 🟢 -5.49 kB 🟢 -1.7 kB 🟢 -1.51 kB
assets/ValueControlPopover-BsJPHNLn.js (new) 5.49 kB 🔴 +5.49 kB 🔴 +1.7 kB 🔴 +1.51 kB
assets/WidgetGalleria-BE8cT37h.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.45 kB 🔴 +1.31 kB
assets/WidgetGalleria-C27D1Wnv.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.45 kB 🟢 -1.3 kB
assets/WidgetColorPicker-CLyBCkzm.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-DWGoUK9X.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetTextarea-B1p4jFhC.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.21 kB 🟢 -1.08 kB
assets/WidgetTextarea-CJX9ZBC_.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.21 kB 🔴 +1.08 kB
assets/WidgetMarkdown-BDC4f7Fh.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetMarkdown-CvxScZwY.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.28 kB 🔴 +1.13 kB
assets/WidgetAudioUI-CnL7pLB5.js (removed) 2.89 kB 🟢 -2.89 kB 🟢 -1.16 kB 🟢 -1.06 kB
assets/WidgetAudioUI-DRiHldVm.js (new) 2.89 kB 🔴 +2.89 kB 🔴 +1.17 kB 🔴 +1.06 kB
assets/WidgetInputText-D1iy-2SB.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -917 B 🟢 -842 B
assets/WidgetInputText-DJ0-wToy.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +920 B 🔴 +850 B
assets/WidgetToggleSwitch-DE4cj1EX.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +833 B 🔴 +724 B
assets/WidgetToggleSwitch-n-Lv97Lo.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -833 B 🟢 -732 B
assets/MediaImageBottom-BpAa8MW-.js (new) 1.55 kB 🔴 +1.55 kB 🔴 +736 B 🔴 +641 B
assets/MediaImageBottom-yvuJ88_v.js (removed) 1.55 kB 🟢 -1.55 kB 🟢 -731 B 🟢 -642 B
assets/MediaAudioBottom-DkdG32s-.js (removed) 1.51 kB 🟢 -1.51 kB 🟢 -732 B 🟢 -651 B
assets/MediaAudioBottom-YGEOOrBs.js (new) 1.51 kB 🔴 +1.51 kB 🔴 +735 B 🔴 +649 B
assets/Media3DBottom-DD5VYG0_.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +733 B 🔴 +645 B
assets/Media3DBottom-np8bVwlH.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -731 B 🟢 -644 B
assets/MediaVideoBottom-b3omN2bE.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -730 B 🟢 -650 B
assets/MediaVideoBottom-DYZiXYir.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +732 B 🔴 +645 B
assets/Media3DTop-HwgPbkc8.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -768 B 🟢 -651 B
assets/Media3DTop-JG4sgVbJ.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +770 B 🔴 +653 B
assets/WidgetSelect-Cn55Ssfz.js (new) 733 B 🔴 +733 B 🔴 +362 B 🔴 +305 B
assets/WidgetSelect-COBBOSaQ.js (removed) 733 B 🟢 -733 B 🟢 -360 B 🟢 -324 B
assets/WidgetInputNumber-Bb3iZABm.js (new) 673 B 🔴 +673 B 🔴 +349 B 🔴 +287 B
assets/WidgetInputNumber-DG2ggPPk.js (removed) 673 B 🟢 -673 B 🟢 -347 B 🟢 -286 B
assets/Load3D-9zmCAwMY.js (new) 424 B 🔴 +424 B 🔴 +269 B 🔴 +223 B
assets/Load3D-BD2fhXLe.js (removed) 424 B 🟢 -424 B 🟢 -267 B 🟢 -224 B
assets/WidgetLegacy-BS7YVNcO.js (new) 364 B 🔴 +364 B 🔴 +239 B 🔴 +196 B
assets/WidgetLegacy-Vvyz_eQX.js (removed) 364 B 🟢 -364 B 🟢 -236 B 🟢 -196 B
assets/commands-BWp4HdfU.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CcfGaui5.js 14.4 kB 14.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CisfgZf5.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CkU12Foh.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CoH2DJa6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-COSt-Bjx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DalfIW5f.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DfTl0eCm.js 13.5 kB 13.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwSJL865.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Zxyx15Vd.js 12.8 kB 12.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BDhxs-bB.js 79.9 kB 79.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BtXVDFw6.js 84.7 kB 84.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Buv6RhU4.js 82.4 kB 82.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CgJ6zvJL.js 97.2 kB 97.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CI9qb0E5.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cu4LqtW0.js 81.6 kB 81.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Dm19QY4N.js 92.2 kB 92.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Tihe0h_r.js 71.3 kB 71.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-wPWMutMw.js 70.4 kB 70.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-Dr0afgCA.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-BXa-oKEc.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-BVdg2b8C.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-1Vh3MCrN.js 240 kB 240 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-aW9En70v.js 260 kB 260 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BIckSVgU.js 273 kB 273 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BiYpVi7D.js 263 kB 263 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bw_Jitw_.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CCEXtYfM.js 243 kB 243 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CvmVDWYd.js 323 kB 323 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-D_wreoPJ.js 267 kB 267 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dz-0ZIBN.js 297 kB 297 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-VZsNmhG7.js 264 kB 264 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BU__hen8.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-0zJeINY-.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 20 added / 20 removed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (10)
src/locales/en/main.json (1)

1638-1638: Address the inconsistent key naming flagged in previous review.

The key "original(Advanced)" was previously flagged as inconsistent with other material mode keys in this section. The naming pattern deviates from the camelCase/lowercase convention used by other keys (normal, wireframe, original).

As previously suggested, consider renaming to:

  • "pointCloud" or
  • "originalAdvanced"

Then update all references in the codebase to use the new key name.

src/services/load3dService.ts (1)

78-102: Splat-model copying silently fails without originalURL.

The splat-model branch correctly differentiates splat models from cloneable models. However, when originalURL is missing, the method silently skips model copying entirely, leaving the target in an inconsistent state.

This is a duplicate of a previous review concern. Consider adding explicit error handling or a fallback when originalURL is falsy for splat models.

src/composables/useLoad3d.ts (1)

66-66: isSplatModel state not reset on new model load attempt.

If a splat model is successfully loaded (setting isSplatModel to true) and the next load fails, isSplatModel retains the stale value. The state is set in modelLoadingEnd (line 494) but never cleared in modelLoadingStart.

This is a duplicate of a previous review concern. Consider resetting isSplatModel.value = false in modelLoadingStart to ensure UI consistency during new load attempts.

src/extensions/core/load3d/loader/FastPLYLoader.ts (4)

9-48: Missing validation for PLY file format.

The parser decodes and parses the header without validating:

  1. The PLY magic number at the start of the file
  2. Required vertex properties (x, y, z) are present
  3. Vertex count is non-zero

These omissions can lead to silent failures or corrupted geometry when loading malformed PLY files.

This duplicates a previous review concern. Consider adding validation after line 11 to check the first line starts with "ply", and after header parsing to ensure x/y/z indices are valid and vertexCount > 0.


71-73: parseFloat can silently produce NaN coordinates.

If the PLY file contains non-numeric data or the property indices are incorrect, parseFloat returns NaN without any indication, leading to corrupted geometry.

This duplicates a previous review concern. Consider validating parsed coordinates with Number.isFinite() and either throwing a descriptive error or skipping malformed vertices.


84-86: Missing radix in parseInt calls.

parseInt without an explicit radix can produce unexpected results with certain string formats. Always specify radix 10 for decimal parsing.

This duplicates a previous review concern. Change to parseInt(parts[...], 10) for all three color channel parsings.


50-98: Pre-allocated buffer may contain trailing zeros.

The positions and colors buffers are pre-allocated based on vertexCount, but lines 63 and 66 skip blank lines and lines with insufficient parts. This leaves trailing zeros when fewer vertices are actually parsed.

This duplicates a previous review concern. Consider trimming the buffers after parsing by creating subarrays when vertexIndex < vertexCount before creating the buffer attributes.

src/extensions/core/load3d/SceneModelManager.ts (3)

111-131: Redundant scene traversal for MainModel removal.

The scene is traversed to find and remove MainModel instances, then traversed again at lines 206-212 with a "Double check" comment. This redundancy suggests uncertainty about the cleanup logic and impacts performance.

This duplicates a previous review concern. If the first removal is working correctly, the second should be unnecessary. Consider removing the redundant check or documenting why it's needed.


107-107: Geometry cloning without error handling.

The direct call to this.originalModel.clone() could throw if the PLY geometry is corrupted or malformed, potentially crashing the rendering process.

This duplicates a previous review concern. Consider wrapping the clone operation in a try-catch block or validating the geometry has a position attribute before cloning.


471-480: containsSplatMesh traverses entire scene graph unnecessarily.

The traverse method continues iterating through all children even after finding a SplatMesh, which is inefficient for large models.

This duplicates a previous review concern. Consider using a manual stack-based traversal that returns early when a SplatMesh is found, or use a recursive helper that short-circuits.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92da937 and 8b8b713.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (2 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/extensions/core/load3d.ts (2 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (3 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/services/load3dService.ts
  • src/composables/useLoad3d.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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
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/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/composables/useLoad3d.test.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
🧠 Learnings (37)
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • package.json
  • pnpm-workspace.yaml
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.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:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/ModelControls.vue
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3DControls.vue
  • vitest.setup.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.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/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.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/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
📚 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} : Mock external dependencies in tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • vitest.setup.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} : Write tests for new features

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • vitest.setup.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/useLoad3d.test.ts
  • vitest.setup.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/useLoad3d.test.ts
📚 Learning: 2025-12-05T08:52:50.787Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/parameters/SectionWidgets.vue:41-49
Timestamp: 2025-12-05T08:52:50.787Z
Learning: In Vue 3.4+ SFCs with a build step, kebab-case prop shorthand (e.g., `:is-empty`) automatically resolves to the corresponding camelCase variable in scope (e.g., `isEmpty`). This is valid syntax combining same-name shorthand with automatic kebab-case-to-camelCase conversion and should not be flagged as an error.

Applied to files:

  • src/components/load3d/Load3DControls.vue
📚 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:

  • vitest.setup.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:

  • vitest.setup.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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:

  • vitest.setup.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue,js} : Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see `.prettierrc`

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{js,ts,tsx,vue} : Use TypeScript exclusively - no new JavaScript files

Applied to files:

  • pnpm-workspace.yaml
📚 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/**/*.{js,ts,jsx,tsx} : Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the `pnpm lint:fix` command

Applied to files:

  • pnpm-workspace.yaml
🧬 Code graph analysis (3)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
src/extensions/core/load3d/LoaderManager.ts (3)
src/extensions/core/load3d/interfaces.ts (1)
  • LoaderManagerInterface (177-187)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (8-102)
src/platform/settings/settingStore.ts (1)
  • useSettingStore (46-252)
src/extensions/core/load3d/SceneModelManager.ts (2)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
🪛 Biome (2.1.2)
src/extensions/core/load3d/LoaderManager.ts

[error] 206-206: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 207-207: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 209-209: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (4)
  • GitHub Check: setup
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (33)
src/schemas/apiSchema.ts (1)

511-511: LGTM! Schema entry aligns with extension setting.

The new Comfy.Load3D.PLYEngine setting is properly defined using Zod v3 enum, matching the options declared in the extension (threejs, fastply, sparkjs). The schema integration is correct and follows the existing pattern.

vitest.setup.ts (1)

4-9: LGTM! Properly mocks WASM-dependent library for tests.

The mock for @sparkjsdev/spark correctly bypasses WASM dependencies that don't work in Node.js test environments. The minimal SplatMesh class implementation is appropriate for tests that verify logic without requiring full WASM functionality.

The mock placement and structure follow Vitest best practices.

Based on learnings: Using Vitest's mock utilities as per guidelines.

src/extensions/core/load3d.ts (2)

202-212: LGTM! PLY Engine setting is well-documented and follows conventions.

The new Comfy.Load3D.PLYEngine setting:

  • Properly declares experimental status
  • Provides clear tooltip explaining each engine option (threejs for mesh, fastply for ASCII point clouds, sparkjs for 3DGS)
  • Follows the existing extension setting pattern
  • Has a sensible default (threejs)

252-254: LGTM! File extensions updated to support PLY and splat formats.

The file input now accepts the new formats (.ply, .spz, .splat, .ksplat) in addition to existing 3D formats. This aligns with the PR objectives to add PLY and 3DGS support.

package.json (1)

149-149: LGTM! Dependency addition follows workspace catalog pattern.

The @sparkjsdev/spark dependency correctly uses the catalog: reference, which resolves to version ^0.1.10 defined in pnpm-workspace.yaml. This follows the repository's standard pattern for managing shared dependency versions.

src/components/load3d/Load3DControls.vue (3)

89-91: LGTM! Prop definition follows Vue 3.5+ conventions.

The new isSplatModel optional prop:

  • Uses proper TypeScript typing with optional flag
  • Has a sensible default value (false)
  • Follows camelCase naming convention
  • Uses Vue 3.5+ reactive destructuring style

As per coding guidelines: Vue 3.5+ TypeScript style default prop declaration with reactive destructuring.


108-114: LGTM! Conditional category logic is correct for splat models.

The computed availableCategories properly:

  • Removes the 'light' category when isSplatModel is true (splat models use baked lighting and don't need traditional light controls)
  • Maintains all other categories (scene, model, camera, export)
  • Follows a clean conditional pattern

This aligns well with the 3D Gaussian Splatting rendering approach where lighting is pre-baked into the splat representation.


50-50: LGTM! Prop binding correctly propagates splat model state.

The :hide-material-mode="isSplatModel" binding properly forwards the splat model detection to ModelControls, enabling conditional UI behavior. This creates a clean data flow from the parent component down to the material mode controls.

pnpm-workspace.yaml (1)

26-26: This dependency is acceptable for its specific purpose. @sparkjsdev/spark is an advanced 3D Gaussian Splatting renderer for THREE.js, and version 0.1.10 is the current version in official documentation. The caret constraint allows patch-level updates while maintaining compatibility, which is appropriate for a specialized rendering library serving discrete 3D visualization features in this project.

src/composables/useLoad3d.ts (1)

494-494: Correctly updates splat-model state on load completion.

The fallback to false ensures isSplatModel is always a boolean, even when load3d is unavailable.

src/extensions/core/load3d/Load3d.ts (1)

574-576: LGTM: Clean delegation to model manager.

The isSplatModel() method correctly delegates to modelManager.containsSplatMesh(), providing a clear public API for detecting splat models.

src/extensions/core/load3d/interfaces.ts (2)

10-15: LGTM: MaterialMode extension for point cloud rendering.

The addition of 'original(Advanced)' to the MaterialMode union aligns with the new point cloud rendering capability introduced in this PR.


194-198: LGTM: Extension set updated for PLY/splat support.

The new extensions (.spz, .splat, .ply, .ksplat) correctly reflect the 3DGS and PLY file format support added in this PR.

src/components/load3d/controls/ModelControls.vue (3)

74-76: LGTM: Prop definition follows Vue 3.5 style.

The hideMaterialMode prop is correctly defined using Vue 3.5 destructuring with a default value.


31-31: LGTM: Clean conditional rendering.

The material mode UI is conditionally rendered based on the hideMaterialMode prop, enabling splat-model-specific behavior.


97-97: LGTM: materialModes extended for point cloud rendering.

The addition of 'original(Advanced)' aligns with the new MaterialMode variant and enables point cloud rendering in the UI.

src/components/load3d/Load3D.vue (2)

113-113: LGTM: Clean state extraction from composable.

The isSplatModel state is correctly destructured from useLoad3d for propagation to child components.


25-25: LGTM: State correctly propagated to controls.

The isSplatModel state is properly passed to Load3DControls, enabling splat-model-specific UI behavior.

src/extensions/core/load3d/SceneModelManager.ts (5)

102-239: Complex PLY mode switching logic.

The handlePLYModeSwitch method correctly handles the reconstruction of PLY models for different rendering modes (Points vs Mesh). The logic appropriately:

  • Cleans up old MainModel instances
  • Creates Points geometry for original(Advanced) mode
  • Creates Mesh geometry for other modes
  • Applies proper centering and scaling

However, note the efficiency concerns mentioned in related comments below.


248-252: LGTM: Clean routing for PLY mode changes.

The early return correctly routes PLY BufferGeometry through the specialized handlePLYModeSwitch method, avoiding the general material switching logic.


335-335: LGTM: Material mode restoration includes advanced option.

The addition of the 'original(Advanced)' case ensures proper material restoration for point cloud rendering mode.


431-440: LGTM: Efficient path for splat models.

The early detection and handling of splat models correctly bypasses standard fitting and material setup, as SplatMesh handles its own rendering.


1-1: The @sparkjsdev/spark dependency is already properly configured in package.json under the project's package management system (using the "catalog:" protocol for dependency resolution). The import is correctly used in the codebase with instantiation patterns in LoaderManager.ts and proper type checking in SceneModelManager.ts. Tests mock SplatMesh appropriately in vitest.setup.ts, confirming integration completeness.

src/extensions/core/load3d/LoaderManager.ts (10)

1-1: LGTM - Imports are appropriate.

The new imports properly support PLY and splat format loading with the necessary loaders and dependencies.

Also applies to: 7-7, 11-11, 19-19


27-28: LGTM - Loader fields follow existing patterns.

The new public loader fields are consistent with the existing loader architecture and properly expose the PLY loading capabilities.


45-46: LGTM - Loader initialization is correct.

The new loaders are properly initialized following the established pattern.


199-201: LGTM - PLY case properly delegates to loadPLY.

The switch case cleanly delegates PLY loading to the dedicated method.


218-221: LGTM - Method signature is well-defined.

The private method signature correctly defines the PLY loading interface with appropriate types.


238-241: LGTM - Format detection is appropriate.

The ASCII format detection safely inspects the PLY header without risk of out-of-bounds access.


243-250: LGTM - Loader selection logic is correct.

The conditional selection between FastPLYLoader and standard PLYLoader appropriately handles different formats and user preferences.


252-257: LGTM - Geometry initialization follows best practices.

The geometry is properly registered, normals are computed, and attributes are inspected before rendering decisions.


259-284: LGTM - Points rendering is well-implemented.

The centering, scaling, and Points rendering for the original(Advanced) mode properly handles point cloud visualization with appropriate material configurations.

Also applies to: 290-294


296-317: LGTM - Mesh rendering properly handles materials and geometry.

The mesh rendering path correctly creates appropriate materials based on vertex color presence and applies sensible defaults (DoubleSide, appropriate metalness/roughness).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
src/services/load3dService.ts (2)

9-9: Replace any with proper TypeScript type.

The any type violates coding guidelines. Define the viewer type based on the return type of useLoad3dViewer.

🔎 Apply this diff to use proper typing:
+import type { ReturnType } from 'es-toolkit'
+
+type Load3dViewer = ReturnType<typeof useLoad3dViewer>
+
-const viewerInstances = new Map<NodeId, any>()
+const viewerInstances = new Map<NodeId, Load3dViewer>()

As per coding guidelines: "Never use any type - use proper TypeScript types."


120-120: Replace as any with proper TypeScript type.

The as any type assertion violates coding guidelines. Define a proper type for the Scene Config properties.

🔎 Apply this diff to use proper typing:
+interface SceneConfig {
+  backgroundImage?: string
+  // Add other Scene Config properties as needed
+}
+
-      const sceneConfig = sourceNode?.properties?.['Scene Config'] as any
+      const sceneConfig = sourceNode?.properties?.['Scene Config'] as SceneConfig | undefined
       const backgroundPath = sceneConfig?.backgroundImage

As per coding guidelines: "Never use as any type assertions - fix the underlying type issue."

♻️ Duplicate comments (15)
src/services/load3dService.ts (1)

78-102: Add error handling for missing splat model URL and clarify why splat models don't copy material properties.

The splat model branch (lines 78-82) silently continues if originalURL is missing without warning or error handling. Add error handling to log or handle this case.

Additionally, splat models have embedded rendering properties from their source file and don't support dynamic materialMode, upDirection, or appliedTexture changes like polygon models do. Add a comment clarifying why these properties aren't copied for splat models to prevent future confusion.

Suggested fix:

if (source.isSplatModel()) {
  const originalURL = source.modelManager.originalURL
  if (originalURL) {
    await target.loadModel(originalURL)
+  } else {
+    console.warn('Cannot copy splat model: originalURL is missing')
+  }
+ // Splat models are loaded with embedded rendering properties from their
+ // source file and don't support dynamic material/texture/direction changes
} else {
tests-ui/tests/composables/useLoad3d.test.ts (1)

108-109: Test coverage for splat/PLY model scenarios remains incomplete.

The mocks correctly expose isSplatModel and isPlyModel for the test surface, but both return false, which only tests the non-splat/non-ply code path. Consider adding test cases that verify behavior when these flags return true, such as material mode option changes and UI adjustments specific to splat or PLY models.

Based on learnings: Write tests for all changes, especially new features.

src/composables/useLoad3d.ts (1)

492-497: Reset model type flags at the start of model loading to prevent stale state.

If a splat or PLY model loads successfully and a subsequent load fails, the flags isSplatModel and isPlyModel retain their previous values. This can cause the UI to display incorrect controls. Consider resetting both flags in modelLoadingStart to ensure UI consistency during new load attempts.

🔎 Apply this diff to reset the flags:
 modelLoadingStart: () => {
   loadingMessage.value = t('load3d.loadingModel')
   loading.value = true
+  isSplatModel.value = false
+  isPlyModel.value = false
 },
src/extensions/core/load3d/loader/FastPLYLoader.ts (4)

9-48: Add validation for PLY file format and required properties.

The parser assumes a valid PLY format but doesn't validate the magic number (ply) at the start or verify that required properties (x, y, z) are present. This could lead to silent failures or corrupted geometry with NaN values.

🔎 Add validation after decoding:
 parse(arrayBuffer: ArrayBuffer): THREE.BufferGeometry {
   const text = new TextDecoder().decode(arrayBuffer)
   const lines = text.split('\n')
+
+  // Validate PLY magic number
+  if (!lines[0]?.trim().toLowerCase().startsWith('ply')) {
+    throw new Error('Invalid PLY file: missing PLY header')
+  }

And after header parsing:

     } else if (line === 'end_header') {
       headerEndLine = i
       break
     }
   }
+
+  if (xIndex < 0 || yIndex < 0 || zIndex < 0) {
+    throw new Error('Invalid PLY file: missing required position properties (x, y, z)')
+  }
+
+  if (vertexCount === 0) {
+    throw new Error('Invalid PLY file: no vertices defined')
+  }

50-52: Pre-allocated buffers may contain trailing zeros if vertices are skipped.

The positions and colors buffers are pre-allocated based on vertexCount, but blank lines and lines with insufficient parts are skipped (lines 63, 66). This leaves trailing zeros in the buffer if vertexIndex < vertexCount.

🔎 Trim buffers after parsing:
   // Create geometry
   const geometry = new THREE.BufferGeometry()
-  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
+  const actualPositions = vertexIndex < vertexCount
+    ? positions.slice(0, vertexIndex * 3)
+    : positions
+  geometry.setAttribute('position', new THREE.BufferAttribute(actualPositions, 3))
   if (hasColor && colors) {
-    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))
+    const actualColors = vertexIndex < vertexCount
+      ? colors.slice(0, vertexIndex * 3)
+      : colors
+    geometry.setAttribute('color', new THREE.BufferAttribute(actualColors, 3))
   }

Also applies to: 93-98


71-73: Validate parseFloat results to prevent NaN in geometry.

parseFloat returns NaN silently on invalid input. If a line contains non-numeric data or property indices are wrong, this produces corrupted geometry without any indication.

🔎 Add NaN validation:
      // Position
-     positions[posIndex] = parseFloat(parts[xIndex])
-     positions[posIndex + 1] = parseFloat(parts[yIndex])
-     positions[posIndex + 2] = parseFloat(parts[zIndex])
+     const x = parseFloat(parts[xIndex])
+     const y = parseFloat(parts[yIndex])
+     const z = parseFloat(parts[zIndex])
+
+     if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) {
+       continue // Skip malformed vertices
+     }
+
+     positions[posIndex] = x
+     positions[posIndex + 1] = y
+     positions[posIndex + 2] = z

84-86: Specify radix for parseInt to ensure decimal parsing.

parseInt without a radix argument can produce unexpected results with certain string formats. Always specify radix 10 for decimal parsing.

🔎 Add radix parameter:
-        colors[posIndex] = parseInt(parts[redIndex]) / 255
-        colors[posIndex + 1] = parseInt(parts[greenIndex]) / 255
-        colors[posIndex + 2] = parseInt(parts[blueIndex]) / 255
+        colors[posIndex] = parseInt(parts[redIndex], 10) / 255
+        colors[posIndex + 1] = parseInt(parts[greenIndex], 10) / 255
+        colors[posIndex + 2] = parseInt(parts[blueIndex], 10) / 255
src/extensions/core/load3d/SceneModelManager.ts (3)

102-239: Consolidate redundant MainModel removal logic.

The scene is traversed twice to find and remove MainModel objects (lines 111-131 and 206-212). The second traversal with the "Double check" comment suggests uncertainty about the first cleanup. This is inefficient and may indicate a code smell.

🔎 Remove redundant traversal:

If the first removal is working correctly, the second is unnecessary. If there's a race condition or re-entrancy concern, document it clearly. Otherwise:

-   // Double check: remove any remaining MainModel before adding new one
-   const remainingMainModels: THREE.Object3D[] = []
-   this.scene.traverse((obj) => {
-     if (obj.name === 'MainModel') {
-       remainingMainModels.push(obj)
-     }
-   })
-   remainingMainModels.forEach((obj) => this.scene.remove(obj))

    this.currentModel = newModel
    newModel.name = 'MainModel'

107-107: Add defensive validation before cloning PLY geometry.

If the PLY geometry is corrupted or missing required attributes, clone() could throw. Consider validating the geometry before cloning to provide clearer error messages.

🔎 Add attribute validation:
  private handlePLYModeSwitch(mode: MaterialMode): void {
    if (!(this.originalModel instanceof THREE.BufferGeometry)) {
      return
    }
+   
+   if (!this.originalModel.attributes.position) {
+     console.error('Invalid PLY geometry: missing position attribute')
+     return
+   }

    const plyGeometry = this.originalModel.clone()

471-480: Optimize containsSplatMesh to short-circuit traversal.

THREE.Object3D.traverse doesn't support early exit. Once a SplatMesh is found, the traversal continues unnecessarily through the entire scene graph, which is wasteful for large models.

🔎 Use recursive helper for early exit:
  containsSplatMesh(model?: THREE.Object3D | null): boolean {
    const target = model ?? this.currentModel
    if (!target) return false
    if (target instanceof SplatMesh) return true
-   let found = false
-   target.traverse((child) => {
-     if (child instanceof SplatMesh) found = true
-   })
-   return found
+   // Use a recursive helper that can short-circuit
+   function checkChildren(obj: THREE.Object3D): boolean {
+     for (const child of obj.children) {
+       if (child instanceof SplatMesh) return true
+       if (checkChildren(child)) return true
+     }
+     return false
+   }
+   return checkChildren(target)
  }
src/extensions/core/load3d/LoaderManager.ts (5)

203-212: Wrap switch case declarations in block scope to prevent variable leakage.

The local variables splatUrl, splatMesh, and splatGroup declared in the splat format cases can erroneously leak to other switch clauses. Biome static analysis correctly flags this as a correctness issue.

🔎 Wrap in block scope:
      case 'spz':
      case 'splat':
-     case 'ksplat':
-       const splatUrl = path + encodeURIComponent(filename)
-       const splatMesh = new SplatMesh({ url: splatUrl })
-       this.modelManager.setOriginalModel(splatMesh)
-       const splatGroup = new THREE.Group()
-       splatGroup.add(splatMesh)
-       model = splatGroup
+     case 'ksplat': {
+       const splatUrl = path + encodeURIComponent(filename)
+       const splatMesh = new SplatMesh({ url: splatUrl })
+       this.modelManager.setOriginalModel(splatMesh)
+       const splatGroup = new THREE.Group()
+       splatGroup.add(splatMesh)
+       model = splatGroup
        break
+     }

As per coding guidelines: Avoid using TypeScript any type and fix underlying type issues.


222-222: Replace type assertion with safe default value.

The setting value is cast to string using as string, which can hide undefined or non-string values and violates the coding guideline against as type assertions.

🔎 Use nullish coalescing with default:
- const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') as string
+ const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') ?? 'threejs'

This provides a sensible default if the setting is undefined.

As per coding guidelines: Never use as any type assertions - fix the underlying type issue.


224-231: Extract duplicate SplatMesh creation logic into a helper method.

The sparkjs path in loadPLY (lines 224-231) duplicates the splat case logic (lines 203-212). This code duplication makes maintenance harder and risks divergence.

🔎 Extract helper method:
+ private createSplatModel(url: string): THREE.Group {
+   const splatMesh = new SplatMesh({ url })
+   this.modelManager.setOriginalModel(splatMesh)
+   const group = new THREE.Group()
+   group.add(splatMesh)
+   return group
+ }

Then use in both locations:

  case 'spz':
  case 'splat':
  case 'ksplat': {
    const splatUrl = path + encodeURIComponent(filename)
-   const splatMesh = new SplatMesh({ url: splatUrl })
-   this.modelManager.setOriginalModel(splatMesh)
-   const splatGroup = new THREE.Group()
-   splatGroup.add(splatMesh)
-   model = splatGroup
+   model = this.createSplatModel(splatUrl)
    break
  }
  if (plyEngine === 'sparkjs') {
    const splatUrl = path + encodeURIComponent(filename)
-   const splatMesh = new SplatMesh({ url: splatUrl })
-   this.modelManager.setOriginalModel(splatMesh)
-   const splatGroup = new THREE.Group()
-   splatGroup.add(splatMesh)
-   return splatGroup
+   return this.createSplatModel(splatUrl)
  }

234-236: Validate HTTP response status before reading body.

If fetch fails with a 4xx/5xx status, response.arrayBuffer() will still execute but may return an error page or empty data, leading to confusing parsing errors downstream.

🔎 Add response validation:
  // Use Three.js PLYLoader or FastPLYLoader for point cloud PLY files
  const fullUrl = path + filename
  const response = await fetch(fullUrl)
+ if (!response.ok) {
+   throw new Error(`Failed to load PLY file: ${response.status} ${response.statusText}`)
+ }
  const arrayBuffer = await response.arrayBuffer()

286-289: Type assertion indicates underlying type definition issue.

The as unknown as THREE.Mesh cast when storing plyPoints into originalMaterials suggests that originalMaterials is typed as WeakMap<THREE.Mesh, ...> but needs to store THREE.Points as well.

The proper fix requires widening the WeakMap type in SceneModelManager (where originalMaterials is defined) to accept both THREE.Mesh | THREE.Points. Once that's fixed, remove this cast to maintain type safety.

As per coding guidelines: Never use as any type assertions - fix the underlying type issue.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b8b713 and bafd460.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (2 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/extensions/core/load3d.ts (2 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (3 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/services/load3dService.ts
  • src/composables/useLoad3d.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
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/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/composables/useLoad3d.test.ts
🧠 Learnings (43)
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mock external dependencies in tests

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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 `vitest` for unit testing in this project

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Write tests for new features

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • vitest.setup.ts
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • vitest.setup.ts
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.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:

  • vitest.setup.ts
  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/load3d.ts
  • src/services/load3dService.ts
  • src/schemas/apiSchema.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/Load3d.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.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/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • package.json
  • pnpm-workspace.yaml
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3D.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 : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue,js} : Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see `.prettierrc`

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{js,ts,tsx,vue} : Use TypeScript exclusively - no new JavaScript files

Applied to files:

  • pnpm-workspace.yaml
📚 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/**/*.{js,ts,jsx,tsx} : Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the `pnpm lint:fix` command

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Aim for behavioral coverage of critical and new features

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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: Check tests-ui/README.md for test guidelines

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.test.ts
🧬 Code graph analysis (2)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (574-576)
  • isPlyModel (578-580)
src/extensions/core/load3d/LoaderManager.ts (2)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (8-102)
src/platform/settings/settingStore.ts (1)
  • useSettingStore (46-252)
🪛 Biome (2.1.2)
src/extensions/core/load3d/LoaderManager.ts

[error] 206-206: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 207-207: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 209-209: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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: setup
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (23)
package.json (1)

149-149: LGTM: SparkJS dependency added correctly.

The addition of @sparkjsdev/spark as a runtime dependency follows the established catalog pattern and aligns with the PR's objective to support 3D Gaussian Splatting.

pnpm-workspace.yaml (1)

26-26: LGTM: SparkJS catalog entry added correctly.

The catalog entry for @sparkjsdev/spark at version ^0.1.10 is properly formatted and will be referenced by package.json.

src/extensions/core/load3d.ts (2)

202-212: LGTM: PLY Engine setting configured properly.

The new Comfy.Load3D.PLYEngine setting is well-defined with:

  • Clear tooltip explaining each engine option
  • Appropriate default ('threejs')
  • Proper experimental flag

The three engine options (threejs, fastply, sparkjs) align with the PR's multi-engine PLY support strategy.


252-255: LGTM: File input extended for PLY formats.

The accept attribute now correctly includes .ply, .spz, .splat, and .ksplat extensions, matching the SUPPORTED_EXTENSIONS set defined in interfaces.ts.

vitest.setup.ts (1)

4-9: LGTM: SparkJS mock properly configured for tests.

The mock implementation of SplatMesh with an empty constructor is appropriate for bypassing WASM dependencies during Node.js test execution. The comment clearly explains the rationale.

src/locales/en/main.json (1)

1638-1638: LGTM: Point Cloud translation added with consistent naming.

The new "pointCloud": "Point Cloud" entry follows the established camelCase naming pattern used by other material modes in this section.

src/components/load3d/Load3DControls.vue (3)

90-93: LGTM: Props defined correctly with Vue 3.5 style.

The isSplatModel and isPlyModel props are properly defined using reactive destructuring with appropriate defaults. This follows the project's Vue 3.5+ coding guidelines.


111-116: LGTM: Light controls appropriately hidden for splat models.

The conditional logic to exclude the 'light' category for splat models makes sense, as 3D Gaussian Splatting models have different rendering characteristics that don't require traditional lighting controls.


50-51: ModelControls component correctly accepts the new props.

The component receives :hide-material-mode="isSplatModel" and :is-ply-model="isPlyModel" and ModelControls properly defines both props as hideMaterialMode?: boolean and isPlyModel?: boolean with appropriate defaults and TypeScript types.

src/extensions/core/load3d/interfaces.ts (2)

10-15: LGTM: Point Cloud material mode added to MaterialMode type.

The addition of 'pointCloud' to the MaterialMode union type is consistent with the locale translation added in src/locales/en/main.json and supports the new PLY rendering capabilities.


194-198: LoaderManager properly supports all PLY-related file extensions.

Verified that LoaderManager (lines 199-212) includes case handlers for all four extensions:

  • .ply uses intelligent engine selection (SparkJS, FastPLYLoader, or ThreeJS PLYLoader)
  • .spz, .splat, .ksplat use SplatMesh from SparkJS

All required loaders are imported and initialized. No action needed.

src/schemas/apiSchema.ts (1)

511-511: Enum values match extension setting definition.

The PLY Engine enum values in the schema ('threejs', 'fastply', 'sparkjs') match exactly with the options defined in src/extensions/core/load3d.ts.

src/composables/useLoad3d.ts (2)

66-67: LGTM!

The reactive state variables isSplatModel and isPlyModel are correctly declared and will be properly exposed in the composable's return object.


568-569: LGTM!

The model type flags are correctly exposed in the composable's public API for consumption by UI components.

src/extensions/core/load3d/Load3d.ts (1)

574-580: LGTM!

The new public methods isSplatModel() and isPlyModel() correctly delegate to the model manager and provide clean type detection for downstream consumers. The implementation is straightforward and appropriate.

src/components/load3d/Load3D.vue (1)

25-26: LGTM!

The model type flags are correctly destructured from useLoad3d and passed down to Load3DControls. This enables the child component to adjust its UI based on whether the loaded model is a splat or PLY model.

Also applies to: 114-115

src/components/load3d/controls/ModelControls.vue (2)

31-31: LGTM!

The hideMaterialMode prop correctly controls visibility of the material mode UI, and both props are properly typed and destructured following Vue 3.5 patterns.

Also applies to: 74-77


95-109: LGTM!

The computed materialModes correctly includes the pointCloud option only for PLY models, inserting it at index 1 as intended. The conditional logic is clear and appropriate.

src/extensions/core/load3d/SceneModelManager.ts (3)

1-1: LGTM!

The SplatMesh import from @sparkjsdev/spark is correctly added to support 3D Gaussian Splatting detection and handling.


248-252: LGTM!

The early return to route PLY BufferGeometry models through handlePLYModeSwitch is correct and prevents the standard material mode logic from incorrectly processing PLY models.


431-440: LGTM!

The SplatMesh early-return path correctly skips the standard model fitting and material setup, which is appropriate since SplatMesh handles its own rendering. The default camera distance provides reasonable viewing.

src/extensions/core/load3d/LoaderManager.ts (2)

1-1: LGTM!

The imports and initialization of PLYLoader and FastPLYLoader are correctly added to support the new PLY loading pathways.

Also applies to: 7-7, 11-11, 19-19, 27-28, 45-46


199-201: LGTM!

The 'ply' case correctly delegates to the new loadPLY method, maintaining clean separation of loading logic by file type.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (9)
src/services/load3dService.ts (1)

78-102: Silent failure when splat model lacks originalURL.

The code correctly differentiates between splat models (which must be reloaded via URL) and cloneable models. However, when a splat model lacks an originalURL (line 79-82), the method silently skips model copying entirely, leaving the target without a model. This is a silent failure that could cause confusing behavior for users.

🔎 Recommended fix: Add error handling
 if (source.isSplatModel()) {
   const originalURL = source.modelManager.originalURL
   if (originalURL) {
     await target.loadModel(originalURL)
+  } else {
+    console.error('Cannot copy splat model: originalURL is missing')
+    throw new Error('Cannot copy splat model: originalURL is required for splat models')
   }
 } else {

This ensures the issue is surfaced to the developer/user rather than silently failing. If throwing is too aggressive for your use case, at minimum add the console.error to help with debugging.

src/composables/useLoad3d.ts (1)

488-497: Reset model-type flags at the start of loading to prevent stale state.

If a user loads a splat/PLY model (setting isSplatModel/isPlyModel to true), then attempts to load a different model that fails, these flags retain stale values from the previous successful load. Reset both flags in modelLoadingStart for consistency.

🔎 Suggested fix:
     modelLoadingStart: () => {
       loadingMessage.value = t('load3d.loadingModel')
       loading.value = true
+      isSplatModel.value = false
+      isPlyModel.value = false
     },
tests-ui/tests/composables/useLoad3d.test.ts (1)

108-109: Mock methods correctly added.

The mock properly includes isSplatModel and isPlyModel methods returning false. Consider adding test cases that verify behavior when these return true to ensure complete coverage of splat/PLY model code paths.

src/extensions/core/load3d/SceneModelManager.ts (1)

471-480: Consider short-circuiting the traversal for performance.

As noted in a prior review, traverse continues through the entire scene graph even after finding a SplatMesh. For large models, consider using a manual recursive traversal that can return early.

🔎 Optional optimization:
  containsSplatMesh(model?: THREE.Object3D | null): boolean {
    const target = model ?? this.currentModel
    if (!target) return false
    if (target instanceof SplatMesh) return true
-   let found = false
-   target.traverse((child) => {
-     if (child instanceof SplatMesh) found = true
-   })
-   return found
+   function checkChildren(obj: THREE.Object3D): boolean {
+     for (const child of obj.children) {
+       if (child instanceof SplatMesh) return true
+       if (checkChildren(child)) return true
+     }
+     return false
+   }
+   return checkChildren(target)
  }
src/extensions/core/load3d/LoaderManager.ts (5)

204-213: Wrap switch case declarations in block scope.

The variable declarations in the splat cases can leak to other cases. This issue has been flagged by both Biome linting and in previous review comments.

As suggested in the previous review, wrap the case body in braces:

🔎 Apply this fix
      case 'spz':
      case 'splat':
-     case 'ksplat':
-       const splatUrl = path + encodeURIComponent(filename)
-       const splatMesh = new SplatMesh({ url: splatUrl })
-       this.modelManager.setOriginalModel(splatMesh)
-       const splatGroup = new THREE.Group()
-       splatGroup.add(splatMesh)
-       model = splatGroup
+     case 'ksplat': {
+       const splatUrl = path + encodeURIComponent(filename)
+       const splatMesh = new SplatMesh({ url: splatUrl })
+       this.modelManager.setOriginalModel(splatMesh)
+       const splatGroup = new THREE.Group()
+       splatGroup.add(splatMesh)
+       model = splatGroup
        break
+     }

223-223: Use type guard or default value instead of type assertion.

The as string cast can hide type issues if the setting returns undefined or another type. This was previously flagged.

As suggested in the previous review, use nullish coalescing with a default:

- const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') as string
+ const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') ?? 'threejs'

As per coding guidelines, avoid as type assertions when possible.


225-232: Extract duplicate SplatMesh creation logic.

This code duplicates the splat case logic from lines 204-213. As previously suggested, extract a helper method to reduce duplication.

The previous review suggested creating a createSplatModel helper:

🔎 Recommended refactor
+ private createSplatModel(url: string): THREE.Group {
+   const splatMesh = new SplatMesh({ url })
+   this.modelManager.setOriginalModel(splatMesh)
+   const group = new THREE.Group()
+   group.add(splatMesh)
+   return group
+ }

Then use it in both places to eliminate duplication.


234-237: Add response status validation before reading body.

The code calls response.arrayBuffer() without checking response.ok. If the fetch returns a 4xx/5xx error, this could lead to parsing errors. This was flagged in a previous review.

🔎 Apply this fix
  const fullUrl = path + filename
  const response = await fetch(fullUrl)
+ if (!response.ok) {
+   throw new Error(`Failed to load PLY file: ${response.status} ${response.statusText}`)
+ }
  const arrayBuffer = await response.arrayBuffer()

284-287: Type assertion indicates WeakMap typing issue.

The cast as unknown as THREE.Mesh reveals a type mismatch where originalMaterials expects THREE.Mesh but receives THREE.Points. This was previously flagged and requires updating the WeakMap type definition.

As noted in the previous review, fix the root cause by updating originalMaterials type in SceneModelManager to WeakMap<THREE.Mesh | THREE.Points, MaterialType>, then remove this cast.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bafd460 and bed0d3f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/extensions/core/load3d.ts (2 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (3 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/scripts/metadata/ply.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/SceneModelManager.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/components/load3d/Load3D.vue
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/SceneModelManager.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/services/load3dService.ts
  • src/composables/useLoad3d.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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/Load3DControls.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
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/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/composables/useLoad3d.test.ts
🧠 Learnings (41)
📚 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:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • vitest.setup.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • vitest.setup.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • vitest.setup.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/SceneModelManager.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:

  • src/schemas/apiSchema.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3DControls.vue
  • vitest.setup.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/extensions/core/load3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/scripts/metadata/ply.ts
  • src/components/load3d/Load3D.vue
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/extensions/core/load3d/SceneModelManager.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.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 : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/load3d/Load3DControls.vue
📚 Learning: 2025-12-18T20:39:25.533Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:25.533Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.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/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.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/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mock external dependencies in tests

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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 `vitest` for unit testing in this project

Applied to files:

  • vitest.setup.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Write tests for new features

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • package.json
  • pnpm-workspace.yaml
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Aim for behavioral coverage of critical and new features

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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: Check tests-ui/README.md for test guidelines

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.test.ts
🧬 Code graph analysis (4)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (574-576)
  • isPlyModel (578-580)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
src/scripts/metadata/ply.ts (1)
  • parseASCIIPLY (137-145)
src/extensions/core/load3d/LoaderManager.ts (3)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (10-33)
src/platform/settings/settingStore.ts (1)
  • useSettingStore (46-252)
src/scripts/metadata/ply.ts (1)
  • isPLYAsciiFormat (150-153)
src/extensions/core/load3d/SceneModelManager.ts (2)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (574-576)
🪛 Biome (2.1.2)
src/extensions/core/load3d/LoaderManager.ts

[error] 207-207: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 208-208: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 210-210: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (4)
  • GitHub Check: lint-and-format
  • GitHub Check: setup
  • GitHub Check: collect
  • GitHub Check: test
🔇 Additional comments (25)
package.json (1)

150-150: LGTM - Correctly references workspace catalog.

The addition of "@sparkjsdev/spark": "catalog:" correctly references the catalog entry defined in pnpm-workspace.yaml line 30, following the established pattern for workspace dependencies.

Note: The version verification comment on pnpm-workspace.yaml also applies here.

src/locales/en/main.json (1)

1638-1638: LGTM - Addresses previous naming feedback.

The "pointCloud": "Point Cloud" entry uses a consistent, lowercase key that aligns with the naming pattern of other material modes. This addresses the previous review comment that suggested using "pointCloud" instead of "original(Advanced)".

vitest.setup.ts (1)

4-9: LGTM - Appropriate mock for WASM-dependent library.

The mock for @sparkjsdev/spark with an empty SplatMesh constructor is appropriate for the unit test environment where WASM is not available. This allows the module to be imported and basic type checking to work without requiring the full WASM runtime.

Based on learnings, this follows the repository's testing guidelines to mock external dependencies and keep module mocks contained.

src/extensions/core/load3d.ts (2)

202-212: LGTM - Well-structured setting with clear documentation.

The new Comfy.Load3D.PLYEngine setting is well-designed:

  • Clear tooltip explaining the three engine options
  • Appropriate default value (threejs) for backward compatibility
  • Correctly marked as experimental
  • Consistent category structure

The setting properly aligns with the new PLY/splat support introduced in this PR and matches the schema definition in src/schemas/apiSchema.ts line 511.


252-255: Extension list is correct; clarify that splat formats are not handled by all three PLY engines.

The listed extensions (.ply, .spz, .splat, .ksplat) are accurate and complete—no missing standard formats like .gs or .gsplat exist. However, the premise about engine support needs correction:

  • .spz, .splat, and .ksplat are handled exclusively by the sparkjs engine (SplatMesh from @sparkjsdev/spark)
  • threejs (THREE.js PLYLoader) handles only standard .ply files
  • fastply (FastPLYLoader) handles only ASCII .ply files

These are not interchangeable engines; they serve different purposes and do not all support the same formats.

Likely an incorrect or invalid review comment.

src/schemas/apiSchema.ts (1)

511-511: LGTM - Schema definition matches extension setting.

The 'Comfy.Load3D.PLYEngine' entry correctly matches the extension setting definition in src/extensions/core/load3d.ts lines 202-212:

  • Enum values are identical: ['threejs', 'fastply', 'sparkjs']
  • Positioned logically with other Load3D settings
  • Uses Zod v3 z.enum syntax

As per coding guidelines, this properly expands the exported Settings type via z.infer<typeof zSettings>.

pnpm-workspace.yaml (1)

30-30: The addition of @sparkjsdev/spark at version ^0.1.10 is valid. Version 0.1.10 is the current documented version and the package properly includes WebAssembly components in its build.

src/components/load3d/Load3DControls.vue (1)

90-116: LGTM! Props and computed logic are correctly implemented.

The Vue 3.5 style prop destructuring with defaults is correct, and the availableCategories computed properly filters out the 'light' category for splat models since 3DGS handles its own lighting internally.

src/extensions/core/load3d/interfaces.ts (2)

10-15: LGTM! MaterialMode type extended correctly.

The new 'pointCloud' mode is properly added to the union type for PLY point cloud rendering support.


189-199: LGTM! Supported extensions properly extended.

The new file extensions (.spz, .splat, .ply, .ksplat) are correctly added for PLY and Gaussian splatting format support.

src/composables/useLoad3d.ts (1)

66-67: LGTM! New reactive flags properly declared and exposed.

The isSplatModel and isPlyModel refs are correctly initialized and exposed in the composable's return object, enabling downstream components to reactively respond to model type changes.

Also applies to: 568-569

src/components/load3d/controls/ModelControls.vue (3)

74-77: LGTM! Props correctly defined with Vue 3.5 style.

The hideMaterialMode and isPlyModel props are properly declared with default values using Vue 3.5 style reactive destructuring.


95-109: LGTM! Conditional pointCloud mode insertion is correct.

The computed property correctly inserts 'pointCloud' mode at index 1 (after 'original') only when isPlyModel is true, providing the appropriate UI options for PLY file rendering.


31-60: LGTM! Conditional rendering for material mode section.

The v-if="!hideMaterialMode" correctly hides the material mode controls for splat models, which handle their own rendering internally.

src/extensions/core/load3d/Load3d.ts (1)

574-580: LGTM! Model type detection methods correctly implemented.

Both isSplatModel() and isPlyModel() are properly implemented:

  • isSplatModel() delegates to the model manager's containsSplatMesh() method
  • isPlyModel() correctly identifies PLY models by checking if originalModel is an instance of THREE.BufferGeometry
src/components/load3d/Load3D.vue (1)

25-26: LGTM! Props correctly bound and destructured.

The isSplatModel and isPlyModel refs are properly destructured from useLoad3d and correctly bound to Load3DControls using kebab-case prop syntax.

Also applies to: 114-115

src/extensions/core/load3d/SceneModelManager.ts (4)

1-1: LGTM! SplatMesh import for 3DGS support.

The SplatMesh import from @sparkjsdev/spark enables 3D Gaussian Splatting model detection and handling.


102-239: Well-structured PLY mode switching logic.

The handlePLYModeSwitch method correctly handles the complexity of switching between pointCloud (THREE.Points) and mesh-based (THREE.Mesh) rendering modes for PLY geometries. The method properly:

  • Clones the original geometry to preserve it
  • Handles vertex colors when present
  • Centers and scales point cloud geometry
  • Applies appropriate materials per mode

One consideration from a past review: add defensive validation for the geometry before cloning to handle potentially malformed PLY data.


427-440: LGTM! SplatMesh models correctly bypass standard setup.

The early return for SplatMesh-containing models is appropriate since 3DGS models handle their own rendering internally and don't need standard mesh fitting, material setup, or camera positioning calculations.


334-351: The pointCloud case fallthrough is correct and safe for the code architecture.

The UI properly restricts pointCloud mode to PLY models only (via the isPlyModel prop check in ModelControls.vue). For PLY models, the code enters handlePLYModeSwitch (line 250 check: instanceof THREE.BufferGeometry), which recreates the geometry as a THREE.Points object. For non-PLY models, handlePLYModeSwitch guards against execution and the normal switch/case logic applies, where the pointCloud case falls through to original, gracefully restoring the original material. This design is intentional and prevents runtime errors.

src/scripts/metadata/ply.ts (2)

137-145: LGTM with a note on large file handling.

The implementation decodes the entire buffer to a string and splits it into lines, which is straightforward but could be memory-intensive for very large ASCII PLY files. For the current use case this appears acceptable, especially since binary PLY files would use the standard Three.js loader.

If large ASCII PLY files become common, consider implementing streaming/chunked parsing. For now, verify the expected file sizes in your use cases.


150-153: Simple and effective format detection.

Checking the first 500 bytes for the "format ascii" substring is a practical heuristic for PLY format detection. While theoretically this could match content in comments or data, PLY headers are well-structured and this should work reliably in practice.

src/extensions/core/load3d/LoaderManager.ts (3)

239-252: Well-structured engine selection logic.

The format detection using isPLYAsciiFormat combined with the engine setting provides appropriate fallback behavior. The FastPLYLoader is only used for ASCII files when explicitly configured, with a sensible fallback to the standard Three.js PLYLoader.


257-281: Point cloud normalization and material setup looks good.

The centering and scaling logic using the bounding sphere is appropriate for normalizing point clouds. The conditional material creation based on vertex color availability is well-handled.


294-315: Mesh rendering path is well-implemented.

The mesh mode correctly handles vertex colors when available and falls back to cloning the standard material. Setting DoubleSide for PLY meshes is appropriate to avoid backface culling issues common with mesh-based point cloud data.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (13)
src/composables/useLoad3d.ts (1)

492-497: Reset model type flags at load start to prevent stale state.

If a splat/PLY model loads successfully and then a subsequent model load fails, isSplatModel and isPlyModel retain their previous values. Consider resetting both flags in modelLoadingStart to ensure UI consistency.

🔎 Suggested fix:
     modelLoadingStart: () => {
       loadingMessage.value = t('load3d.loadingModel')
       loading.value = true
+      isSplatModel.value = false
+      isPlyModel.value = false
     },
tests-ui/tests/composables/useLoad3d.test.ts (1)

108-109: Consider adding test coverage for splat and PLY model scenarios.

The mocks currently return false for both isSplatModel and isPlyModel, which tests only the non-splat/non-PLY path. Consider adding test cases that verify behavior when these return true to ensure complete coverage.

Example scenarios:

  • Verify that isSplatModel and isPlyModel flags are correctly populated in the composable's return value when models are loaded
  • Test that UI-related logic (e.g., material mode options) adjusts based on these flags

Based on learnings: Write tests for all changes, especially new features.

src/extensions/core/load3d/loader/FastPLYLoader.ts (1)

11-16: LGTM: Clean delegation to shared PLY parser.

The FastPLYLoader correctly delegates parsing to parseASCIIPLY from the metadata module, which addresses previous concerns about parsing logic separation.

Optionally, consider making the error message more descriptive to aid debugging:

     if (!plyData) {
-      throw new Error('Failed to parse PLY data')
+      throw new Error('Failed to parse PLY data: invalid format or missing required properties (x, y, z)')
     }
src/services/load3dService.ts (1)

78-82: Silent no-op when splat model lacks originalURL.

When source.isSplatModel() is true but originalURL is missing, the function silently skips copying the model entirely without any warning. This could lead to confusing behavior where a user expects model state to be copied but nothing happens.

This was flagged in a previous review. Consider adding a warning log:

🔎 Suggested enhancement
      if (source.isSplatModel()) {
        const originalURL = source.modelManager.originalURL
        if (originalURL) {
          await target.loadModel(originalURL)
+       } else {
+         console.warn('Cannot copy splat model: originalURL is missing')
        }
      } else {
src/scripts/metadata/ply.ts (2)

26-42: Missing PLY magic string validation and bounds checking.

The parser doesn't validate that the file starts with the PLY magic string "ply", and line 42 accesses line.split(/\s+/)[2] without verifying the array has at least 3 elements. If a malformed header contains element vertex without a count, parseInt(undefined) returns NaN.

These issues were flagged in previous reviews.

🔎 Suggested fixes
 function parsePLYHeader(lines: string[]): PLYHeader | null {
+  // Validate PLY magic string
+  if (lines.length === 0 || lines[0].trim() !== 'ply') {
+    return null
+  }
+
   let vertexCount = 0
   let headerEndLine = 0
   // ... existing code ...

     if (line.startsWith('element vertex')) {
-      vertexCount = parseInt(line.split(/\s+/)[2])
+      const parts = line.split(/\s+/)
+      if (parts.length >= 3) {
+        vertexCount = parseInt(parts[2])
+      }
     }

105-107: Missing NaN validation for parsed vertex positions.

parseFloat can return NaN for malformed data, which would populate the positions array with invalid values and potentially create corrupt geometry downstream.

This was flagged in a previous review.

🔎 Suggested fix
     const posIndex = vertexIndex * 3
 
-    positions[posIndex] = parseFloat(parts[xIndex])
-    positions[posIndex + 1] = parseFloat(parts[yIndex])
-    positions[posIndex + 2] = parseFloat(parts[zIndex])
+    const x = parseFloat(parts[xIndex])
+    const y = parseFloat(parts[yIndex])
+    const z = parseFloat(parts[zIndex])
+    
+    if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) {
+      continue // Skip malformed vertex
+    }
+    
+    positions[posIndex] = x
+    positions[posIndex + 1] = y
+    positions[posIndex + 2] = z
src/extensions/core/load3d/SceneModelManager.ts (3)

102-107: Add defensive validation before cloning PLY geometry.

The clone() call on line 107 could fail if originalModel is a malformed or corrupted BufferGeometry. Consider validating that the geometry has the required position attribute before attempting to clone.

This was flagged in a previous review.

🔎 Suggested fix
  private handlePLYModeSwitch(mode: MaterialMode): void {
    if (!(this.originalModel instanceof THREE.BufferGeometry)) {
      return
    }
+   
+   if (!this.originalModel.attributes.position) {
+     console.error('Invalid PLY geometry: missing position attribute')
+     return
+   }

    const plyGeometry = this.originalModel.clone()

471-480: containsSplatMesh continues traversal after finding a match.

Once found is set to true, the traversal continues unnecessarily through the entire scene graph. For models with large hierarchies, this is inefficient.

This was flagged in a previous review.

🔎 Suggested optimization using early-exit recursion
  containsSplatMesh(model?: THREE.Object3D | null): boolean {
    const target = model ?? this.currentModel
    if (!target) return false
    if (target instanceof SplatMesh) return true
-   let found = false
-   target.traverse((child) => {
-     if (child instanceof SplatMesh) found = true
-   })
-   return found
+   // Use recursive helper that can short-circuit
+   function checkChildren(obj: THREE.Object3D): boolean {
+     for (const child of obj.children) {
+       if (child instanceof SplatMesh) return true
+       if (checkChildren(child)) return true
+     }
+     return false
+   }
+   return checkChildren(target)
  }

205-212: Redundant "Double check" traversal remains in the code.

The comment "Double check: remove any remaining MainModel before adding new one" suggests uncertainty about the first cleanup (lines 111-131). If the first removal works correctly, this second traversal is wasteful. If there's a known race condition or edge case, it should be documented.

🔎 Consider consolidating or documenting

Either remove the redundant check if the first cleanup is reliable:

-    // Double check: remove any remaining MainModel before adding new one
-    const remainingMainModels: THREE.Object3D[] = []
-    this.scene.traverse((obj) => {
-      if (obj.name === 'MainModel') {
-        remainingMainModels.push(obj)
-      }
-    })
-    remainingMainModels.forEach((obj) => this.scene.remove(obj))
-
     this.currentModel = newModel
     newModel.name = 'MainModel'

Or add a comment explaining why the double-check is necessary.

src/extensions/core/load3d/LoaderManager.ts (4)

213-222: Wrap switch case declarations in a block to prevent scope leakage.

Biome correctly identifies that splatUrl, splatMesh, and splatGroup declarations can leak to other switch clauses.

This was flagged in a previous review and by static analysis.

🔎 Apply this fix
      case 'spz':
      case 'splat':
-     case 'ksplat':
-       const splatUrl = path + encodeURIComponent(filename)
-       const splatMesh = new SplatMesh({ url: splatUrl })
-       this.modelManager.setOriginalModel(splatMesh)
-       const splatGroup = new THREE.Group()
-       splatGroup.add(splatMesh)
-       model = splatGroup
-       break
+     case 'ksplat': {
+       const splatUrl = path + encodeURIComponent(filename)
+       const splatMesh = new SplatMesh({ url: splatUrl })
+       this.modelManager.setOriginalModel(splatMesh)
+       const splatGroup = new THREE.Group()
+       splatGroup.add(splatMesh)
+       model = splatGroup
+       break
+     }

232-232: Avoid as string type assertion; use nullish coalescing instead.

Per coding guidelines, avoid as type assertions. The setting value could be undefined or a different type, which this cast would hide.

This was flagged in a previous review.

🔎 Suggested safer approach
- const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') as string
+ const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') ?? 'threejs'

244-246: Missing response.ok check before reading the response body.

If the fetch fails with a 4xx/5xx status, response.arrayBuffer() will still execute but may return an error page or empty data, leading to confusing parsing errors downstream.

This was flagged in a previous review.

🔎 Add response validation
    // Use Three.js PLYLoader or FastPLYLoader for point cloud PLY files
    const fullUrl = path + filename
    const response = await fetch(fullUrl)
+   if (!response.ok) {
+     throw new Error(`Failed to load PLY file: ${response.status} ${response.statusText}`)
+   }
    const arrayBuffer = await response.arrayBuffer()

293-296: Unsafe type assertion as unknown as THREE.Mesh violates coding guidelines.

Per coding guidelines: "Never use as any type assertions - fix the underlying type issue." The as unknown as THREE.Mesh pattern is similarly unsafe. The root cause is that originalMaterials WeakMap is typed for THREE.Mesh but needs to store THREE.Points as well.

🔎 Fix by widening the WeakMap type in SceneModelManager

In SceneModelManager.ts, update the type:

- originalMaterials: WeakMap<THREE.Mesh, THREE.Material | THREE.Material[]> =
+ originalMaterials: WeakMap<THREE.Mesh | THREE.Points, THREE.Material | THREE.Material[]> =
    new WeakMap()

Then remove the unsafe cast here:

      const plyPoints = new THREE.Points(plyGeometry, pointMaterial)
-     this.modelManager.originalMaterials.set(
-       plyPoints as unknown as THREE.Mesh,
-       pointMaterial
-     )
+     this.modelManager.originalMaterials.set(plyPoints, pointMaterial)
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bed0d3f and 5e2ec32.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/Load3dViewerContent.vue (2 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/components/load3d/controls/viewer/ViewerModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/composables/useLoad3dViewer.ts (4 hunks)
  • src/extensions/core/load3d.ts (4 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (4 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/extensions/core/saveMesh.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/scripts/metadata/ply.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/components/load3d/controls/ModelControls.vue
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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/components/load3d/controls/ModelControls.vue
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useLoad3dViewer.ts
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3dViewer.ts
  • src/composables/useLoad3d.ts
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/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/composables/useLoad3d.test.ts
🧠 Learnings (40)
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • package.json
  • pnpm-workspace.yaml
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.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 : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/load3d/Load3DControls.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/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.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/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 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:

  • src/components/load3d/Load3DControls.vue
  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • vitest.setup.ts
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
📚 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:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • vitest.setup.ts
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • vitest.setup.ts
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • vitest.setup.ts
  • src/composables/useLoad3dViewer.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/services/load3dService.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mock external dependencies in tests

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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 `vitest` for unit testing in this project

Applied to files:

  • vitest.setup.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Write tests for new features

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • src/components/load3d/Load3dViewerContent.vue
  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Aim for behavioral coverage of critical and new features

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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: Check tests-ui/README.md for test guidelines

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.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/useLoad3d.test.ts
🧬 Code graph analysis (5)
src/extensions/core/load3d/SceneModelManager.ts (2)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (580-582)
src/composables/useLoad3dViewer.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (580-582)
  • isPlyModel (584-586)
src/extensions/core/load3d/LoaderManager.ts (3)
src/extensions/core/load3d/interfaces.ts (1)
  • LoaderManagerInterface (177-187)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (10-33)
src/scripts/metadata/ply.ts (1)
  • isPLYAsciiFormat (150-153)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
src/scripts/metadata/ply.ts (1)
  • parseASCIIPLY (137-145)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (580-582)
  • isPlyModel (584-586)
🪛 Biome (2.1.2)
src/extensions/core/load3d/LoaderManager.ts

[error] 216-216: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 217-217: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 219-219: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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: collect
  • GitHub Check: lint-and-format
  • GitHub Check: setup
  • GitHub Check: test

@dosubot dosubot bot removed the size:XL This PR changes 500-999 lines, ignoring generated files. label Dec 20, 2025
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Dec 20, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
src/extensions/core/load3d/SceneModelManager.ts (1)

334-351: Clarify the pointCloud fallthrough behavior.

The pointCloud case falls through to original, which works for non-PLY models but may be confusing. Consider adding a comment explaining this is intentional for mesh-based models.

          case 'original':
-         case 'pointCloud':
+         case 'pointCloud': // For non-PLY models, pointCloud behaves like original
            const originalMaterial = this.originalMaterials.get(child)
♻️ Duplicate comments (13)
src/services/load3dService.ts (2)

78-82: Add error handling for splat models without originalURL.

When a splat model lacks an originalURL, the method silently skips model copying entirely. This can lead to confusing behavior where the copy operation appears to succeed but the model isn't actually loaded.

🔎 Recommended enhancement
 if (source.isSplatModel()) {
   const originalURL = source.modelManager.originalURL
   if (originalURL) {
     await target.loadModel(originalURL)
+  } else {
+    console.warn('Cannot copy splat model: originalURL is missing')
   }
 } else {

95-102: Remove redundant property assignments.

Lines 95-96 and 98-99 directly assign properties that are then immediately overwritten by setter calls on lines 101-102. The setters likely update the same internal properties, making the direct assignments redundant.

🔎 Proposed refactor
-       target.getModelManager().materialMode =
-         source.getModelManager().materialMode
-
-       target.getModelManager().currentUpDirection =
-         source.getModelManager().currentUpDirection
-
        target.setMaterialMode(source.getModelManager().materialMode)
        target.setUpDirection(source.getModelManager().currentUpDirection)

If the setters don't update internal state, keep the direct assignments and remove the setter calls instead.

src/composables/useLoad3d.ts (1)

488-497: Consider resetting both flags at the start of model loading.

As noted in a previous review, if a splat model loads successfully and then a subsequent non-splat model fails to load, isSplatModel retains a stale true value. The same concern applies to isPlyModel.

🔎 Suggested improvement:
     modelLoadingStart: () => {
       loadingMessage.value = t('load3d.loadingModel')
       loading.value = true
+      isSplatModel.value = false
+      isPlyModel.value = false
     },
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)

1-33: LGTM! Clean refactoring addresses previous concerns.

The FastPLYLoader properly delegates ASCII PLY parsing to the shared parseASCIIPLY function from the metadata module, which addresses the previous review comment about separating concerns. The geometry construction is straightforward and correct.

The error handling is minimal but acceptable, as the underlying parseASCIIPLY function handles detailed validation (header checks, required properties, etc.).

tests-ui/tests/composables/useLoad3d.test.ts (1)

108-109: Add test coverage for splat and PLY model scenarios.

The mock methods isSplatModel and isPlyModel always return false, which only tests the non-splat/non-PLY paths. Consider adding test cases that verify behavior when these methods return true to ensure complete coverage of the new model type detection logic.

Based on learnings: Write tests for all changes, especially new features. Aim for behavioral coverage of critical and new features.

src/extensions/core/load3d/SceneModelManager.ts (3)

102-107: Add defensive validation before cloning geometry.

The clone() call on line 107 could throw if the geometry is malformed. Consider validating the presence of required attributes first.

🔎 Add defensive check:
  private handlePLYModeSwitch(mode: MaterialMode): void {
    if (!(this.originalModel instanceof THREE.BufferGeometry)) {
      return
    }
+   
+   if (!this.originalModel.attributes.position) {
+     console.error('Invalid PLY geometry: missing position attribute')
+     return
+   }

    const plyGeometry = this.originalModel.clone()

471-480: containsSplatMesh traversal doesn't short-circuit.

Once found is set to true, traversal continues through the entire scene graph. For large models, consider a manual recursive check that can exit early.

🔎 Short-circuiting alternative:
  containsSplatMesh(model?: THREE.Object3D | null): boolean {
    const target = model ?? this.currentModel
    if (!target) return false
    if (target instanceof SplatMesh) return true
-   let found = false
-   target.traverse((child) => {
-     if (child instanceof SplatMesh) found = true
-   })
-   return found
+   function checkChildren(obj: THREE.Object3D): boolean {
+     for (const child of obj.children) {
+       if (child instanceof SplatMesh) return true
+       if (checkChildren(child)) return true
+     }
+     return false
+   }
+   return checkChildren(target)
  }

205-212: Redundant "Double check" traversal still present.

The scene is traversed twice to remove MainModel objects (first at lines 111-131, then again here). The first removal should be sufficient unless there's a specific race condition or re-entrancy concern that warrants documenting.

🔎 If the first removal is reliable, remove the redundant check:
-   // Double check: remove any remaining MainModel before adding new one
-   const remainingMainModels: THREE.Object3D[] = []
-   this.scene.traverse((obj) => {
-     if (obj.name === 'MainModel') {
-       remainingMainModels.push(obj)
-     }
-   })
-   remainingMainModels.forEach((obj) => this.scene.remove(obj))
-
    this.currentModel = newModel
    newModel.name = 'MainModel'

If the double-check is actually needed due to a known issue, please add a comment explaining why.

src/scripts/metadata/ply.ts (3)

26-42: Add PLY magic string validation and bounds checking.

The parser should validate that the first line is ply per the PLY specification, and line 42 should verify the split array has sufficient elements before accessing index 2.

🔎 Suggested fixes:
 function parsePLYHeader(lines: string[]): PLYHeader | null {
+  // Validate PLY magic string
+  if (lines.length === 0 || lines[0].trim() !== 'ply') {
+    return null
+  }
+
   let vertexCount = 0
   let headerEndLine = 0
   // ... existing code ...

   for (let i = 0; i < lines.length; i++) {
     const line = lines[i].trim()

     if (line.startsWith('element vertex')) {
-      vertexCount = parseInt(line.split(/\s+/)[2])
+      const parts = line.split(/\s+/)
+      if (parts.length >= 3) {
+        vertexCount = parseInt(parts[2])
+      }
     } else if (line.startsWith('property')) {

105-107: Add NaN validation for parsed vertex positions.

parseFloat returns NaN for malformed input, which would corrupt the geometry. Consider validating parsed values.

🔎 Suggested fix:
     const posIndex = vertexIndex * 3

-    positions[posIndex] = parseFloat(parts[xIndex])
-    positions[posIndex + 1] = parseFloat(parts[yIndex])
-    positions[posIndex + 2] = parseFloat(parts[zIndex])
+    const x = parseFloat(parts[xIndex])
+    const y = parseFloat(parts[yIndex])
+    const z = parseFloat(parts[zIndex])
+    
+    if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
+      console.warn(`Invalid vertex data at line ${i}, skipping`)
+      continue
+    }
+    
+    positions[posIndex] = x
+    positions[posIndex + 1] = y
+    positions[posIndex + 2] = z

117-119: Document the uchar color range assumption.

The implementation assumes color values are in the 0-255 range (uchar in PLY spec). PLY files can also store colors as floats in 0.0-1.0 range, which would render incorrectly.

Consider adding a comment documenting this limitation, or detecting property types during header parsing:

+    // Note: Assumes uchar (0-255) color values per PLY spec.
+    // Float-based colors (0.0-1.0) are not currently supported.
     if (parts.length > Math.max(redIndex, greenIndex, blueIndex)) {
       colors[posIndex] = parseInt(parts[redIndex]) / 255
src/extensions/core/load3d/LoaderManager.ts (2)

251-251: Consider safer setting retrieval.

The as string cast could hide issues if the setting returns undefined. Per coding guidelines, prefer proper TypeScript types over assertions.

🔎 Suggested safer approach:
- const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') as string
+ const plyEngine = useSettingStore().get('Comfy.Load3D.PLYEngine') ?? 'threejs'

305-308: Type assertion issue with originalMaterials WeakMap.

The as unknown as THREE.Mesh cast is needed because originalMaterials is typed for THREE.Mesh but stores THREE.Points. Consider widening the type definition in SceneModelManager.

The proper fix is to update the originalMaterials type in SceneModelManager:

originalMaterials: WeakMap<THREE.Mesh | THREE.Points, THREE.Material | THREE.Material[]>

Then remove this cast.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e2ec32 and 775f784.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (24)
  • package.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/components/load3d/Load3D.vue (2 hunks)
  • src/components/load3d/Load3DControls.vue (3 hunks)
  • src/components/load3d/Load3dViewerContent.vue (2 hunks)
  • src/components/load3d/controls/ModelControls.vue (3 hunks)
  • src/components/load3d/controls/viewer/ViewerModelControls.vue (3 hunks)
  • src/composables/useLoad3d.ts (3 hunks)
  • src/composables/useLoad3dViewer.ts (4 hunks)
  • src/extensions/core/load3d.ts (4 hunks)
  • src/extensions/core/load3d/Load3d.ts (1 hunks)
  • src/extensions/core/load3d/LoaderManager.ts (4 hunks)
  • src/extensions/core/load3d/SceneModelManager.ts (5 hunks)
  • src/extensions/core/load3d/interfaces.ts (2 hunks)
  • src/extensions/core/load3d/loader/FastPLYLoader.ts (1 hunks)
  • src/extensions/core/saveMesh.ts (1 hunks)
  • src/locales/en/main.json (1 hunks)
  • src/schemas/apiSchema.ts (1 hunks)
  • src/scripts/metadata/ply.ts (1 hunks)
  • src/services/load3dService.ts (1 hunks)
  • tests-ui/tests/composables/useLoad3d.test.ts (1 hunks)
  • tests-ui/tests/scripts/metadata/ply.test.ts (1 hunks)
  • vite.config.mts (1 hunks)
  • vitest.setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/extensions/core/saveMesh.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.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

src/**/*.vue: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3dViewerContent.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3dViewerContent.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Vue components must be named in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useLoad3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Composables must follow the naming pattern useXyz.ts

Files:

  • src/composables/useLoad3d.ts
  • src/composables/useLoad3dViewer.ts
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/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
tests-ui/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

tests-ui/**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/Component test files must use pattern **/*.test.ts
Do not write change detector tests - do not just assert that defaults are certain values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing - do not write redundant tests
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
For mocking, leverage Vitest's utilities where possible
Keep module mocks contained - do not use global mutable state within test files, use vi.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

Files:

  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
🧠 Learnings (42)
📚 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:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.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:

  • src/extensions/core/saveMesh.ts
  • vitest.setup.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/composables/useLoad3d.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/components/load3d/controls/ModelControls.vue
  • src/schemas/apiSchema.ts
  • src/extensions/core/load3d/SceneModelManager.ts
  • src/scripts/metadata/ply.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/extensions/core/load3d/LoaderManager.ts
  • src/extensions/core/load3d.ts
  • src/composables/useLoad3dViewer.ts
  • src/services/load3dService.ts
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For mocking, leverage Vitest's utilities where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.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:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files, use `vi.hoisted()` if necessary

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Mock external dependencies in tests

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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 `vitest` for unit testing in this project

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.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:

  • vitest.setup.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} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/composables/useLoad3d.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Do not write tests dependent on non-behavioral features like utility classes or styles

Applied to files:

  • vitest.setup.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • vitest.setup.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:

  • vitest.setup.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:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.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} : Write tests for new features

Applied to files:

  • vitest.setup.ts
  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer `useModel` to prop/emit pairs, do not use `withDefaults`

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/composables/useLoad3dViewer.ts
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.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/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.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/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/load3d/controls/viewer/ViewerModelControls.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/Load3D.vue
  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 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/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/extensions/core/load3d/loader/FastPLYLoader.ts
  • src/services/load3dService.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 : Define proper props and emits definitions in Vue components

Applied to files:

  • src/components/load3d/Load3DControls.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/load3d/Load3D.vue
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Aim for behavioral coverage of critical and new features

Applied to files:

  • tests-ui/tests/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.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/scripts/metadata/ply.test.ts
  • tests-ui/tests/composables/useLoad3d.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: Check tests-ui/README.md for test guidelines

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.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/useLoad3d.test.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to tests-ui/**/*.test.ts : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/tests/composables/useLoad3d.test.ts
  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • src/components/load3d/controls/ModelControls.vue
  • src/components/load3d/Load3dViewerContent.vue
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 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: Create browser tests as appropriate after code changes

Applied to files:

  • src/scripts/metadata/ply.ts
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • package.json
  • pnpm-workspace.yaml
📚 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 : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • package.json
📚 Learning: 2025-12-17T01:01:03.609Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.609Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{services,composables}/**/*.{ts,tsx} : Use `api.apiURL()` for backend endpoints instead of constructing URLs directly

Applied to files:

  • src/extensions/core/load3d/LoaderManager.ts
🧬 Code graph analysis (6)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
src/scripts/metadata/ply.ts (1)
  • parseASCIIPLY (137-145)
src/composables/useLoad3d.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (580-582)
  • isPlyModel (584-586)
tests-ui/tests/scripts/metadata/ply.test.ts (1)
src/scripts/metadata/ply.ts (2)
  • isPLYAsciiFormat (150-153)
  • parseASCIIPLY (137-145)
src/extensions/core/load3d/SceneModelManager.ts (2)
src/extensions/core/load3d/interfaces.ts (1)
  • MaterialMode (10-15)
src/extensions/core/load3d/Load3d.ts (1)
  • isSplatModel (580-582)
src/extensions/core/load3d/LoaderManager.ts (4)
src/extensions/core/load3d/loader/FastPLYLoader.ts (1)
  • FastPLYLoader (10-33)
src/scripts/api.ts (1)
  • api (1306-1306)
src/platform/settings/settingStore.ts (1)
  • useSettingStore (46-252)
src/scripts/metadata/ply.ts (1)
  • isPLYAsciiFormat (150-153)
src/composables/useLoad3dViewer.ts (1)
src/extensions/core/load3d/Load3d.ts (2)
  • isSplatModel (580-582)
  • isPlyModel (584-586)
⏰ 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). (4)
  • GitHub Check: setup
  • GitHub Check: test
  • GitHub Check: lint-and-format
  • GitHub Check: collect

@christian-byrne christian-byrne merged commit 3c4b99e into main Dec 20, 2025
34 checks passed
@christian-byrne christian-byrne deleted the spark-3dgs-support branch December 20, 2025 21:04
Yourz pushed a commit that referenced this pull request Dec 24, 2025
## Summary

integrated sparkjs https://sparkjs.dev/, built by [world labs
](https://www.worldlabs.ai/) to support 3dgs.

- Add 3D Gaussian Splatting (3DGS) support using @sparkjsdev/spark
library
- Add PLY file format support with multiple rendering engines
- Support new file formats: `.ply`, `.spz`, `.splat`, `.ksplat`
- Add PLY Engine setting with three options: `threejs` (mesh), `fastply`
(optimized ASCII point clouds), `sparkjs` (3DGS)
- Add `FastPLYLoader` for 4-5x faster ASCII PLY parsing
- Add `original(Advanced)` material mode for point cloud rendering with
THREE.Points

3dgs generated by https://marble.worldlabs.ai/

test ply file from:
1. made by https://github.com/PozzettiAndrea/ComfyUI-DepthAnythingV3
2. threejs offically repo

## Screenshots


https://github.com/user-attachments/assets/44e64d3e-b58d-4341-9a70-a9aa64801220



https://github.com/user-attachments/assets/76b0dfba-0c12-4f64-91cb-bfc5d672294d



https://github.com/user-attachments/assets/2a8bfe81-1fb2-44c4-8787-dff325369c61



https://github.com/user-attachments/assets/e4beecee-d7a2-40c9-97f7-79b09c60312d

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7602-3dgs-ply-support-2cd6d73d3650814098fcea86cfaf747d)
by [Unito](https://www.unito.io)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants