diff --git a/src/components/load3d/Load3D.vue b/src/components/load3d/Load3D.vue
index 23245edc94..200b953b9f 100644
--- a/src/components/load3d/Load3D.vue
+++ b/src/components/load3d/Load3D.vue
@@ -3,69 +3,46 @@
class="relative w-full h-full"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
+ @pointerdown.stop
+ @pointermove.stop
+ @pointerup.stop
>
-
+
-
+
diff --git a/src/components/load3d/Load3DAnimation.vue b/src/components/load3d/Load3DAnimation.vue
deleted file mode 100644
index 9d29c1ed14..0000000000
--- a/src/components/load3d/Load3DAnimation.vue
+++ /dev/null
@@ -1,336 +0,0 @@
-
-
-
-
-
diff --git a/src/components/load3d/Load3DAnimationScene.vue b/src/components/load3d/Load3DAnimationScene.vue
deleted file mode 100644
index 38cbd7169b..0000000000
--- a/src/components/load3d/Load3DAnimationScene.vue
+++ /dev/null
@@ -1,208 +0,0 @@
-
-
-
-
diff --git a/src/components/load3d/Load3DControls.vue b/src/components/load3d/Load3DControls.vue
index eea5208b32..2b41e0a30c 100644
--- a/src/components/load3d/Load3DControls.vue
+++ b/src/components/load3d/Load3DControls.vue
@@ -1,6 +1,10 @@
@@ -28,44 +34,33 @@
-
-
-
diff --git a/src/components/load3d/Load3dViewerContent.vue b/src/components/load3d/Load3dViewerContent.vue
index fa22865d6a..2082b5a108 100644
--- a/src/components/load3d/Load3dViewerContent.vue
+++ b/src/components/load3d/Load3dViewerContent.vue
@@ -11,7 +11,20 @@
ref="containerRef"
class="absolute w-full h-full comfy-load-3d-viewer"
@resize="viewer.handleResize"
+ @dragover.prevent.stop="handleDragOver"
+ @dragleave.stop="handleDragLeave"
+ @drop.prevent.stop="handleDrop"
/>
+
+
+ {{ dragMessage }}
+
+
@@ -77,6 +90,7 @@ import ModelControls from '@/components/load3d/controls/viewer/ViewerModelContro
import SceneControls from '@/components/load3d/controls/viewer/ViewerSceneControls.vue'
import { t } from '@/i18n'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
+import { useToastStore } from '@/platform/updates/common/toastStore'
import { useLoad3dService } from '@/services/load3dService'
import { useDialogStore } from '@/stores/dialogStore'
@@ -84,6 +98,16 @@ const props = defineProps<{
node: LGraphNode
}>()
+// Drag and drop state
+const isDragging = ref(false)
+const dragMessage = ref('')
+const SUPPORTED_EXTENSIONS = ['.gltf', '.glb', '.obj', '.fbx', '.stl']
+
+function isValidModelFile(file: File): boolean {
+ const fileName = file.name.toLowerCase()
+ return SUPPORTED_EXTENSIONS.some((ext) => fileName.endsWith(ext))
+}
+
const viewerContentRef = ref
()
const containerRef = ref()
const mainContentRef = ref()
@@ -92,6 +116,45 @@ const mutationObserver = ref(null)
const viewer = useLoad3dService().getOrCreateViewer(toRaw(props.node))
+function handleDragOver(event: DragEvent) {
+ if (viewer.isPreview.value) return
+
+ if (!event.dataTransfer) return
+
+ const hasFiles = event.dataTransfer.types.includes('Files')
+
+ if (!hasFiles) return
+
+ isDragging.value = true
+
+ event.dataTransfer.dropEffect = 'copy'
+ dragMessage.value = t('load3d.dropToLoad')
+}
+
+function handleDragLeave() {
+ isDragging.value = false
+}
+
+async function handleDrop(event: DragEvent) {
+ isDragging.value = false
+
+ if (viewer.isPreview.value) return
+
+ if (!event.dataTransfer) return
+
+ const files = Array.from(event.dataTransfer.files)
+
+ if (files.length === 0) return
+
+ const modelFile = files.find(isValidModelFile)
+
+ if (modelFile) {
+ await viewer.handleModelDrop(modelFile)
+ } else {
+ useToastStore().addAlert(t('load3d.unsupportedFileType'))
+ }
+}
+
onMounted(async () => {
const source = useLoad3dService().getLoad3d(props.node)
if (source && containerRef.value) {
diff --git a/src/components/load3d/LoadingOverlay.vue b/src/components/load3d/LoadingOverlay.vue
index 361718d9ea..0da3edaeeb 100644
--- a/src/components/load3d/LoadingOverlay.vue
+++ b/src/components/load3d/LoadingOverlay.vue
@@ -1,13 +1,13 @@
- {{ loadingMessage }}
+ {{ props.loadingMessage }}
@@ -15,29 +15,10 @@