fix: unify wikilink resolution and disambiguate duplicate titles

- Create resolveEntry() in wikilink.ts: single case-insensitive resolution
  function that handles title, alias, filename stem, path suffix, and
  pipe syntax matching
- Replace findEntryByTarget (case-sensitive) and entryMatchesTarget
  (hardcoded /Laputa/ path) with unified resolveEntry
- Fix attachClickHandlers to insert path|title pipe syntax when multiple
  candidates share the same title (disambiguation)
- Update ai-context.ts resolveTarget to use unified resolution
- Add comprehensive tests for resolveEntry and disambiguation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-03-11 19:12:05 +01:00
parent 1486bae4bd
commit ee94ecb731
9 changed files with 187 additions and 42 deletions

View File

@@ -5,6 +5,7 @@ import type { VaultEntry } from '../types'
import type { FrontmatterValue } from '../components/Inspector'
import { useTabManagement } from './useTabManagement'
import { updateMockFrontmatter, deleteMockFrontmatterProperty } from './mockFrontmatterHelpers'
import { resolveEntry } from '../utils/wikilink'
interface NewEntryParams {
path: string
@@ -123,14 +124,8 @@ export function generateUntitledName(entries: VaultEntry[], type: string, pendin
return title
}
export function entryMatchesTarget(e: VaultEntry, targetLower: string, targetAsWords: string): boolean {
if (e.title.toLowerCase() === targetLower) return true
if (e.aliases.some((a) => a.toLowerCase() === targetLower)) return true
const pathStem = e.path.replace(/^.*\/Laputa\//, '').replace(/\.md$/, '')
if (pathStem.toLowerCase() === targetLower) return true
const fileStem = e.filename.replace(/\.md$/, '')
if (fileStem.toLowerCase() === targetLower.split('/').pop()) return true
return e.title.toLowerCase() === targetAsWords
export function entryMatchesTarget(e: VaultEntry, target: string): boolean {
return resolveEntry([e], target) === e
}
async function invokeFrontmatter(command: string, args: Record<string, unknown>): Promise<string> {
@@ -262,9 +257,7 @@ function openDailyNote(entries: VaultEntry[], selectNote: (e: VaultEntry) => voi
}
function findWikilinkTarget(entries: VaultEntry[], target: string): VaultEntry | undefined {
const targetLower = target.toLowerCase()
const targetAsWords = target.split('/').pop()?.replace(/-/g, ' ').toLowerCase() ?? targetLower
return entries.find((e) => entryMatchesTarget(e, targetLower, targetAsWords))
return resolveEntry(entries, target)
}
/** Navigate to a wikilink target, logging a warning if not found. */