11import type { App } from 'obsidian' ;
2- import { TFile , TFolder } from 'obsidian' ;
2+ import { Notice , TFile , TFolder } from 'obsidian' ;
33
44import { t } from '@/i18n/i18n' ;
55import type { MentionInsertReference } from '@/shared/mention/types' ;
66
7+ import { imageMediaTypeForFilename } from './image-context' ;
8+
79/** A vault file or folder reference extracted from an Obsidian drag payload. */
810export interface VaultDropReference {
911 path : string ;
@@ -21,7 +23,8 @@ interface DragManagerHost {
2123
2224/**
2325 * Accepts Obsidian file-explorer drags on the composer and inserts them as
24- * `@path` / `@path/ ` mention tokens at the caret position.
26+ * `@path` / `@path/ ` mention tokens at the caret position. Notes, folders,
27+ * and images are accepted; anything else is ignored.
2528 *
2629 * Must be attached before ImageContextManager so vault drags can be claimed
2730 * via stopImmediatePropagation before the image drop handlers run. The drop
@@ -55,20 +58,20 @@ export class VaultDropController {
5558 }
5659
5760 private readonly handleDragEnter = ( event : DragEvent ) : void => {
58- if ( this . getDraggedReferences ( ) . length === 0 ) return ;
61+ if ( ! this . hasClaimableDrag ( ) ) return ;
5962 event . preventDefault ( ) ;
6063 event . stopImmediatePropagation ( ) ;
6164 this . dropOverlayEl . addClass ( 'visible' ) ;
6265 } ;
6366
6467 private readonly handleDragOver = ( event : DragEvent ) : void => {
65- if ( this . getDraggedReferences ( ) . length === 0 ) return ;
68+ if ( ! this . hasClaimableDrag ( ) ) return ;
6669 event . preventDefault ( ) ;
6770 event . stopImmediatePropagation ( ) ;
6871 } ;
6972
7073 private readonly handleDragLeave = ( event : DragEvent ) : void => {
71- if ( this . getDraggedReferences ( ) . length === 0 ) return ;
74+ if ( ! this . hasClaimableDrag ( ) ) return ;
7275 event . stopImmediatePropagation ( ) ;
7376
7477 const rect = this . inputWrapperEl . getBoundingClientRect ( ) ;
@@ -83,7 +86,7 @@ export class VaultDropController {
8386 } ;
8487
8588 private readonly handleDrop = ( event : DragEvent ) : void => {
86- const references = this . getDraggedReferences ( ) ;
89+ const { references, ignoredCount } = this . collectDragged ( ) ;
8790 if ( references . length === 0 ) return ;
8891 event . preventDefault ( ) ;
8992 event . stopImmediatePropagation ( ) ;
@@ -101,9 +104,18 @@ export class VaultDropController {
101104 }
102105 this . inputEl . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
103106 }
107+ // Mixed drags are claimed wholesale, so surface the items we dropped.
108+ if ( ignoredCount > 0 ) {
109+ new Notice ( t ( 'chat.drop.ignored' , { count : ignoredCount } ) ) ;
110+ }
104111 this . inputEl . focus ( ) ;
105112 } ;
106113
114+ private hasClaimableDrag ( ) : boolean {
115+ const { references } = this . collectDragged ( ) ;
116+ return references . length > 0 ;
117+ } ;
118+
107119 private getDraggedItems ( ) : unknown [ ] {
108120 const host = this . app as unknown as DragManagerHost ;
109121 const dragManager = host . dragManager ;
@@ -123,21 +135,28 @@ export class VaultDropController {
123135 return typeof value === 'object' && value !== null ;
124136 }
125137
126- private getDraggedReferences ( ) : VaultDropReference [ ] {
138+ private collectDragged ( ) : { references : VaultDropReference [ ] ; ignoredCount : number } {
127139 const references : VaultDropReference [ ] = [ ] ;
128140 const seenPaths = new Set < string > ( ) ;
141+ let ignoredCount = 0 ;
129142 for ( const item of this . getDraggedItems ( ) ) {
130143 const reference =
131144 item instanceof TFolder && item . path !== '/' && item . path !== ''
132145 ? { path : item . path , kind : 'folder' as const }
133- : item instanceof TFile && item . extension . toLowerCase ( ) === 'md'
146+ : item instanceof TFile &&
147+ ( item . extension . toLowerCase ( ) === 'md' ||
148+ imageMediaTypeForFilename ( item . name ) !== null )
134149 ? { path : item . path , kind : 'file' as const }
135150 : null ;
136- if ( ! reference || seenPaths . has ( reference . path ) ) continue ;
151+ if ( ! reference ) {
152+ ignoredCount += 1 ;
153+ continue ;
154+ }
155+ if ( seenPaths . has ( reference . path ) ) continue ;
137156 seenPaths . add ( reference . path ) ;
138157 references . push ( reference ) ;
139158 }
140- return references ;
159+ return { references, ignoredCount } ;
141160 }
142161
143162 private mentionToken ( reference : VaultDropReference ) : string {
0 commit comments