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 @@ 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 @@