diff --git a/src/utils/noteListHelpers.test.ts b/src/utils/noteListHelpers.test.ts index 4118bd48..a665776b 100644 --- a/src/utils/noteListHelpers.test.ts +++ b/src/utils/noteListHelpers.test.ts @@ -250,6 +250,27 @@ describe('buildRelationshipGroups', () => { expect(groups).toEqual([]) }) + it('keeps stale entity views usable when a rapidly switched entry lacks filename metadata', () => { + const staleEntity = { + ...makeEntry({ + path: 'C:\\Users\\luca\\Laputa\\project\\alpha.md', + title: 'Alpha project', + isA: 'Project', + }), + filename: undefined, + } as unknown as ReturnType + const backlink = makeEntry({ + path: '/Users/luca/Laputa/note/backlink.md', + filename: 'backlink.md', + title: 'Backlink', + outgoingLinks: ['project/alpha'], + }) + + const groups = buildRelationshipGroups(staleEntity, [staleEntity, backlink]) + + expect(groups.find((group) => group.label === 'Backlinks')?.entries).toEqual([backlink]) + }) + it('allows the same note to appear in multiple relationship groups', () => { const parent = makeEntry({ path: '/vault/parent.md', diff --git a/src/utils/noteListHelpers.ts b/src/utils/noteListHelpers.ts index 7e0d18a4..edfaefc6 100644 --- a/src/utils/noteListHelpers.ts +++ b/src/utils/noteListHelpers.ts @@ -266,10 +266,31 @@ export function clearListSortFromLocalStorage(): void { } catch { /* ignore */ } } +function stringField(value: unknown): string { + return typeof value === 'string' ? value : '' +} + +function filenameStemFromEntry(entry: VaultEntry): string { + const filename = stringField(entry.filename) + if (filename) return filename.replace(/\.md$/, '') + + const pathLeaf = stringField(entry.path).split(/[\\/]/u).pop() ?? '' + return pathLeaf.replace(/\.md$/, '') +} + +function relativePathStemFromEntry(entry: VaultEntry): string { + const normalizedPath = stringField(entry.path).replaceAll('\\', '/') + return normalizedPath.replace(/^.*\/Laputa\//, '').replace(/\.md$/, '') +} + +function linkTargetsForEntry(entry: VaultEntry): Set { + const title = stringField(entry.title) + const aliases = Array.isArray(entry.aliases) ? entry.aliases : [] + return new Set([title, ...aliases, filenameStemFromEntry(entry), relativePathStemFromEntry(entry)].filter(Boolean)) +} + function findBacklinks(entity: VaultEntry, allEntries: VaultEntry[]): VaultEntry[] { - const stem = entity.filename.replace(/\.md$/, '') - const pathStem = entity.path.replace(/^.*\/Laputa\//, '').replace(/\.md$/, '') - const targets = new Set([entity.title, ...entity.aliases, stem, pathStem]) + const targets = linkTargetsForEntry(entity) return allEntries.filter((e) => { if (e.path === entity.path) return false