@@ -81,7 +81,7 @@ export function findImagePathMentions(
8181 // Keep a lone trailing \r on CRLF out of the scan window.
8282 const contentEnd =
8383 lineEnd > lineStart && text [ lineEnd - 1 ] === "\r" ? lineEnd - 1 : lineEnd ;
84- scanImagePathLine ( text , lineStart , contentEnd , cwd , push ) ;
84+ scanImagePathLine ( text , lineStart , contentEnd , cwd , push , false ) ;
8585 if ( lineEnd === text . length ) break ;
8686 lineStart = lineEnd + 1 ;
8787 }
@@ -98,6 +98,7 @@ function scanImagePathLine(
9898 end : number ,
9999 cwd : string ,
100100 push : ( raw : string , path : string | undefined ) => void ,
101+ inQuotedProse : boolean ,
101102) : void {
102103 let i = start ;
103104 while ( i < end ) {
@@ -120,7 +121,7 @@ function scanImagePathLine(
120121 }
121122 // Balanced quotes around prose are not path wrappers. Search inside
122123 // so an absolute path can still be found, then resume after the closer.
123- scanImagePathLine ( text , i + 1 , close , cwd , push ) ;
124+ scanImagePathLine ( text , i + 1 , close , cwd , push , true ) ;
124125 i = close + 1 ;
125126 continue ;
126127 }
@@ -129,7 +130,12 @@ function scanImagePathLine(
129130 continue ;
130131 }
131132
132- if ( canStartUnquotedPath ( text , i , end ) ) {
133+ // Inside prose a candidate must open a new token, or the separator in
134+ // `notes/plan.png` mints a root-level `/plan.png` mention.
135+ if (
136+ canStartUnquotedPath ( text , i , end ) &&
137+ ( ! inQuotedProse || i === start || ! isPathTokenContinuation ( text [ i - 1 ] ) )
138+ ) {
133139 const match = UNQUOTED_AT . exec ( text . slice ( i , end ) ) ;
134140 if ( match ?. [ 0 ] !== undefined ) {
135141 const raw = trimTrailingPunctuation ( match [ 0 ] ) ;
@@ -151,6 +157,28 @@ function isWordChar(ch: string | undefined): boolean {
151157 ) ;
152158}
153159
160+ // True when `ch` continues the current token (word, `/\:.~`, or quote-glue)
161+ // so a mid-token `/` inside prose must not open a root path.
162+ function isPathTokenContinuation ( ch : string | undefined ) : boolean {
163+ if ( ch === undefined ) return false ;
164+ if ( isWordChar ( ch ) ) return true ;
165+ switch ( ch ) {
166+ case "/" :
167+ case "\\" :
168+ case "." :
169+ case "~" :
170+ case ":" :
171+ case "_" :
172+ case "-" :
173+ case "'" :
174+ case '"' :
175+ case "`" :
176+ return true ;
177+ default :
178+ return false ;
179+ }
180+ }
181+
154182function looksLikeQuotedImagePath ( inner : string ) : boolean {
155183 if ( inner . startsWith ( "file://" ) ) return true ;
156184 if ( inner === "~" || inner . startsWith ( "~/" ) || inner . startsWith ( "~\\" ) )
0 commit comments