[refactor] remove node as dependency in 3d node (#6707)

## Summary

This PR refactors the Load3d 3D rendering system to remove its direct
dependency on LGraphNode, making it a more decoupled and reusable
component. The core rendering engine is now framework-agnostic and can
be used in any context, not just within LiteGraph nodes.

## Changes

1. Decoupled Load3d from LGraphNode
  - Before: Load3d directly accessed node.widgets and node.properties
- After: Load3d accepts optional parameters and callbacks, delegating
node integration to the calling code

2. Event-Driven State Management
  - Removed internal storage from Load3d core components
- Camera, controls, and view helper managers now emit cameraChanged
events instead of directly storing state
- External code (e.g., useLoad3d) listens to events and handles
persistence to node.properties

3. Reactive Dimension Updates

- Introduced getDimensions callback to support reactive dimension
updates
- Fixes the issue where dimension changes in vueNodes mode required a
refresh
- The callback is invoked on every render to get fresh width/height
values

4. Improved Configuration System

- Load3DConfiguration now accepts properties: Dictionary<NodeProperty |
undefined> instead of custom storage
  interface
  - Uses official LiteGraph type definitions (Dictionary, NodeProperty)
  - More semantic parameter naming: storage → properties

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6707-refactor-remove-node-as-dependency-in-3d-node-2ab6d73d365081ffac1cdce354781ce8)
by [Unito](https://www.unito.io)
This commit is contained in:
Terry Jia
2025-11-15 06:36:36 -05:00
committed by GitHub
parent ba768c32f3
commit 7a11dc59b6
13 changed files with 187 additions and 234 deletions

View File

@@ -25,7 +25,9 @@ vi.mock('@/platform/updates/common/toastStore', () => ({
vi.mock('@/scripts/api', () => ({
api: {
apiURL: vi.fn()
apiURL: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn()
}
}))
@@ -157,9 +159,15 @@ describe('useLoad3d', () => {
await composable.initializeLoad3d(containerRef)
expect(Load3d).toHaveBeenCalledWith(containerRef, {
node: mockNode
})
expect(Load3d).toHaveBeenCalledWith(
containerRef,
expect.objectContaining({
width: 512,
height: 512,
getDimensions: expect.any(Function),
onContextMenu: expect.any(Function)
})
)
expect(nodeToLoad3dMap.has(mockNode)).toBe(true)
})

View File

@@ -161,9 +161,10 @@ describe('useLoad3dViewer', () => {
await viewer.initializeViewer(containerRef, mockSourceLoad3d)
expect(Load3d).toHaveBeenCalledWith(containerRef, {
disablePreview: true,
isViewerMode: true,
node: mockNode
width: undefined,
height: undefined,
getDimensions: undefined,
isViewerMode: true
})
expect(mockLoad3dService.copyLoad3dState).toHaveBeenCalledWith(