11import * as THREE from 'three'
22
3- import { LGraphNode } from '@/lib/litegraph/src/litegraph'
3+ import { LGraphNode , LiteGraph } from '@/lib/litegraph/src/litegraph'
44import { type CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
55
66import { CameraManager } from './CameraManager'
@@ -22,6 +22,7 @@ import {
2222 type MaterialMode ,
2323 type UpDirection
2424} from './interfaces'
25+ import { app } from '@/scripts/app'
2526
2627class Load3d {
2728 renderer : THREE . WebGLRenderer
@@ -51,6 +52,13 @@ class Load3d {
5152 targetAspectRatio : number = 1
5253 isViewerMode : boolean = false
5354
55+ // Context menu tracking
56+ private rightMouseDownX : number = 0
57+ private rightMouseDownY : number = 0
58+ private rightMouseMoved : boolean = false
59+ private readonly dragThreshold : number = 5
60+ private contextMenuAbortController : AbortController | null = null
61+
5462 constructor (
5563 container : Element | HTMLElement ,
5664 options : Load3DOptions = {
@@ -164,6 +172,8 @@ class Load3d {
164172 this . STATUS_MOUSE_ON_SCENE = false
165173 this . STATUS_MOUSE_ON_VIEWER = false
166174
175+ this . initContextMenu ( )
176+
167177 this . handleResize ( )
168178 this . startAnimation ( )
169179
@@ -172,6 +182,65 @@ class Load3d {
172182 } , 100 )
173183 }
174184
185+ /**
186+ * Initialize context menu on the Three.js canvas
187+ * Detects right-click vs right-drag to show menu only on click
188+ */
189+ private initContextMenu ( ) : void {
190+ const canvas = this . renderer . domElement
191+
192+ this . contextMenuAbortController = new AbortController ( )
193+ const { signal } = this . contextMenuAbortController
194+
195+ const mousedownHandler = ( e : MouseEvent ) => {
196+ if ( e . button === 2 ) {
197+ this . rightMouseDownX = e . clientX
198+ this . rightMouseDownY = e . clientY
199+ this . rightMouseMoved = false
200+ }
201+ }
202+
203+ const mousemoveHandler = ( e : MouseEvent ) => {
204+ if ( e . buttons === 2 ) {
205+ const dx = Math . abs ( e . clientX - this . rightMouseDownX )
206+ const dy = Math . abs ( e . clientY - this . rightMouseDownY )
207+
208+ if ( dx > this . dragThreshold || dy > this . dragThreshold ) {
209+ this . rightMouseMoved = true
210+ }
211+ }
212+ }
213+
214+ const contextmenuHandler = ( e : MouseEvent ) => {
215+ const wasDragging = this . rightMouseMoved
216+
217+ this . rightMouseMoved = false
218+
219+ if ( wasDragging ) {
220+ return
221+ }
222+
223+ e . preventDefault ( )
224+ e . stopPropagation ( )
225+
226+ this . showNodeContextMenu ( e )
227+ }
228+
229+ canvas . addEventListener ( 'mousedown' , mousedownHandler , { signal } )
230+ canvas . addEventListener ( 'mousemove' , mousemoveHandler , { signal } )
231+ canvas . addEventListener ( 'contextmenu' , contextmenuHandler , { signal } )
232+ }
233+
234+ private showNodeContextMenu ( event : MouseEvent ) : void {
235+ const menuOptions = app . canvas . getNodeMenuOptions ( this . node )
236+
237+ new LiteGraph . ContextMenu ( menuOptions , {
238+ event,
239+ title : this . node . type ,
240+ extra : this . node
241+ } )
242+ }
243+
175244 getEventManager ( ) : EventManager {
176245 return this . eventManager
177246 }
@@ -621,6 +690,11 @@ class Load3d {
621690 }
622691
623692 public remove ( ) : void {
693+ if ( this . contextMenuAbortController ) {
694+ this . contextMenuAbortController . abort ( )
695+ this . contextMenuAbortController = null
696+ }
697+
624698 this . renderer . forceContextLoss ( )
625699 const canvas = this . renderer . domElement
626700 const event = new Event ( 'webglcontextlost' , {
0 commit comments