1111 ref =" containerRef"
1212 class =" absolute w-full h-full comfy-load-3d-viewer"
1313 @resize =" viewer.handleResize"
14+ @dragover.prevent.stop =" handleDragOver"
15+ @dragleave.stop =" handleDragLeave"
16+ @drop.prevent.stop =" handleDrop"
1417 />
18+ <div
19+ v-if =" isDragging"
20+ class =" absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm pointer-events-none"
21+ >
22+ <div
23+ class =" px-6 py-4 bg-blue-500/20 border-2 border-dashed border-blue-400 rounded-lg text-blue-100 text-lg font-medium"
24+ >
25+ {{ dragMessage }}
26+ </div >
27+ </div >
1528 </div >
1629
1730 <div class =" w-72 flex flex-col" >
@@ -77,13 +90,24 @@ import ModelControls from '@/components/load3d/controls/viewer/ViewerModelContro
7790import SceneControls from ' @/components/load3d/controls/viewer/ViewerSceneControls.vue'
7891import { t } from ' @/i18n'
7992import type { LGraphNode } from ' @/lib/litegraph/src/LGraphNode'
93+ import { useToastStore } from ' @/platform/updates/common/toastStore'
8094import { useLoad3dService } from ' @/services/load3dService'
8195import { useDialogStore } from ' @/stores/dialogStore'
8296
8397const props = defineProps <{
8498 node: LGraphNode
8599}>()
86100
101+ // Drag and drop state
102+ const isDragging = ref (false )
103+ const dragMessage = ref (' ' )
104+ const SUPPORTED_EXTENSIONS = [' .gltf' , ' .glb' , ' .obj' , ' .fbx' , ' .stl' ]
105+
106+ function isValidModelFile(file : File ): boolean {
107+ const fileName = file .name .toLowerCase ()
108+ return SUPPORTED_EXTENSIONS .some ((ext ) => fileName .endsWith (ext ))
109+ }
110+
87111const viewerContentRef = ref <HTMLDivElement >()
88112const containerRef = ref <HTMLDivElement >()
89113const mainContentRef = ref <HTMLDivElement >()
@@ -92,6 +116,41 @@ const mutationObserver = ref<MutationObserver | null>(null)
92116
93117const viewer = useLoad3dService ().getOrCreateViewer (toRaw (props .node ))
94118
119+ function handleDragOver(event : DragEvent ) {
120+ if (! event .dataTransfer ) return
121+
122+ const hasFiles = event .dataTransfer .types .includes (' Files' )
123+
124+ if (! hasFiles ) return
125+
126+ isDragging .value = true
127+
128+ event .dataTransfer .dropEffect = ' copy'
129+ dragMessage .value = t (' load3d.dropToLoad' )
130+ }
131+
132+ function handleDragLeave() {
133+ isDragging .value = false
134+ }
135+
136+ async function handleDrop(event : DragEvent ) {
137+ isDragging .value = false
138+
139+ if (! event .dataTransfer ) return
140+
141+ const files = Array .from (event .dataTransfer .files )
142+
143+ if (files .length === 0 ) return
144+
145+ const modelFile = files .find (isValidModelFile )
146+
147+ if (modelFile ) {
148+ await viewer .handleModelDrop (modelFile )
149+ } else {
150+ useToastStore ().addAlert (t (' load3d.unsupportedFileType' ))
151+ }
152+ }
153+
95154onMounted (async () => {
96155 const source = useLoad3dService ().getLoad3d (props .node )
97156 if (source && containerRef .value ) {
0 commit comments