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

@@ -182,32 +182,37 @@ describe('generateUntitledName', () => {
describe('entryMatchesTarget', () => {
it('matches by exact title (case-insensitive)', () => {
const entry = makeEntry({ title: 'My Project' })
expect(entryMatchesTarget(entry, 'my project', 'my project')).toBe(true)
expect(entryMatchesTarget(entry, 'my project')).toBe(true)
})
it('matches by alias', () => {
const entry = makeEntry({ aliases: ['MP', 'TheProject'] })
expect(entryMatchesTarget(entry, 'mp', 'mp')).toBe(true)
expect(entryMatchesTarget(entry, 'mp')).toBe(true)
})
it('matches by path stem (relative to Laputa)', () => {
it('matches by path suffix (type/slug)', () => {
const entry = makeEntry({ path: '/Users/luca/Laputa/project/my-project.md' })
expect(entryMatchesTarget(entry, 'project/my-project', 'project/my-project')).toBe(true)
expect(entryMatchesTarget(entry, 'project/my-project')).toBe(true)
})
it('matches by filename stem', () => {
const entry = makeEntry({ filename: 'my-project.md' })
expect(entryMatchesTarget(entry, 'my-project', 'my-project')).toBe(true)
expect(entryMatchesTarget(entry, 'my-project')).toBe(true)
})
it('matches when target as words matches title', () => {
const entry = makeEntry({ title: 'my project' })
expect(entryMatchesTarget(entry, 'project/my-project', 'my project')).toBe(true)
expect(entryMatchesTarget(entry, 'my-project')).toBe(true)
})
it('returns false when nothing matches', () => {
const entry = makeEntry({ title: 'Something Else', aliases: [], filename: 'else.md' })
expect(entryMatchesTarget(entry, 'nonexistent', 'nonexistent')).toBe(false)
expect(entryMatchesTarget(entry, 'nonexistent')).toBe(false)
})
it('handles pipe syntax targets', () => {
const entry = makeEntry({ path: '/vault/project/alpha.md', filename: 'alpha.md', title: 'Alpha' })
expect(entryMatchesTarget(entry, 'project/alpha|Alpha Project')).toBe(true)
})
})