@@ -75,22 +75,23 @@ export class TemplateChoiceEngine extends TemplateEngine {
7575 const format = this . choice . fileNameFormat . enabled
7676 ? this . choice . fileNameFormat . format
7777 : VALUE_SYNTAX ;
78- const formattedName = this . stripDuplicateFolderPrefix (
79- await this . formatter . formatFileName (
80- format ,
81- this . choice . name ,
82- ) ,
78+ const formattedName = await this . formatter . formatFileName (
79+ format ,
80+ this . choice . name ,
81+ ) ;
82+ const { fileName, strippedPrefix } = this . stripDuplicateFolderPrefix (
83+ formattedName ,
8384 folderPath ,
8485 ) ;
8586 const treatAsVaultRelativePath =
8687 await this . shouldTreatFormattedNameAsVaultRelativePath (
8788 formattedName ,
88- folderPath ,
89+ strippedPrefix ,
8990 ) ;
9091
9192 let filePath = this . normalizeTemplateFilePath (
9293 treatAsVaultRelativePath ? "" : folderPath ,
93- formattedName ,
94+ fileName ,
9495 this . choice . templatePath ,
9596 ) ;
9697
@@ -311,37 +312,42 @@ export class TemplateChoiceEngine extends TemplateEngine {
311312 } ) ;
312313 }
313314
314- private stripDuplicateFolderPrefix ( fileName : string , folderPath : string ) : string {
315+ private stripDuplicateFolderPrefix (
316+ fileName : string ,
317+ folderPath : string ,
318+ ) : { fileName : string ; strippedPrefix : boolean } {
315319 const normalizedFolder = this . stripLeadingSlash ( folderPath ) ;
316320 const normalizedFileName = this . stripLeadingSlash ( fileName ) ;
317321
318- if ( ! normalizedFolder ) return normalizedFileName ;
322+ if ( ! normalizedFolder ) {
323+ return { fileName : normalizedFileName , strippedPrefix : false } ;
324+ }
319325 if ( ! normalizedFileName . startsWith ( `${ normalizedFolder } /` ) ) {
320- return normalizedFileName ;
326+ return { fileName : normalizedFileName , strippedPrefix : false } ;
321327 }
322328
323- return normalizedFileName . slice ( normalizedFolder . length + 1 ) ;
329+ return {
330+ fileName : normalizedFileName . slice ( normalizedFolder . length + 1 ) ,
331+ strippedPrefix : true ,
332+ } ;
324333 }
325334
326335 private async shouldTreatFormattedNameAsVaultRelativePath (
327336 formattedName : string ,
328- folderPath : string ,
337+ strippedPrefix : boolean ,
329338 ) : Promise < boolean > {
330339 if ( this . choice . folder . enabled ) return false ;
340+ if ( strippedPrefix ) return false ;
331341
332- const normalizedFileName = this . stripLeadingSlash ( formattedName ) ;
342+ const normalizedFileName = formattedName . trim ( ) ;
333343 if ( ! normalizedFileName . includes ( "/" ) ) return false ;
344+ if ( normalizedFileName . startsWith ( "./" ) ) return false ;
345+ if ( normalizedFileName . startsWith ( "/" ) ) return true ;
334346
335- const normalizedFolder = this . stripLeadingSlash ( folderPath ) ;
336- if ( ! normalizedFolder ) return true ;
337-
338- const [ firstSegment ] = normalizedFileName . split ( "/" ) ;
339- if ( ! firstSegment ) return false ;
340-
341- // Preserve legacy "relative subpath under default note location" behavior.
342- // Only skip default folder prefixing when the first path segment exists at
343- // vault root, which indicates an explicit vault-relative destination.
344- return await this . app . vault . adapter . exists ( firstSegment ) ;
347+ const slashCount = normalizedFileName . split ( "/" ) . length - 1 ;
348+ // Keep one-level subpaths (e.g. "tasks/note") relative to Obsidian's
349+ // default folder, while treating deeper paths as vault-relative.
350+ return slashCount >= 2 ;
345351 }
346352
347353 private getCurrentFolderSuggestion ( ) :
0 commit comments