Files
recall/src/lib/link-suggestions.ts
Daniel Arroyo ff7223bfea feat: MVP-3 Sprint 4 - Co-usage, metrics, centrality, creation source, feature flags
- Add NoteCoUsage model and co-usage tracking when viewing notes
- Add creationSource field to notes (form/quick/import)
- Add dashboard metrics API (/api/metrics)
- Add centrality calculation (/api/centrality)
- Add feature flags system for toggling features
- Add multiline QuickAdd with smart paste type detection
- Add internal link suggestions while editing notes
- Add type inference for automatic note type detection
- Add comprehensive tests for type-inference and link-suggestions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:50:40 -03:00

73 lines
1.9 KiB
TypeScript

import { prisma } from '@/lib/prisma'
export interface LinkSuggestion {
term: string
noteId: string
noteTitle: string
}
/**
* Find potential wiki-link suggestions in content.
* Returns notes whose titles appear in the content and could be converted to [[links]].
*/
export async function findLinkSuggestions(
content: string,
currentNoteId?: string
): Promise<LinkSuggestion[]> {
if (!content.trim() || content.length < 10) {
return []
}
// Get all note titles except current note
const allNotes = await prisma.note.findMany({
where: currentNoteId ? { id: { not: currentNoteId } } : undefined,
select: { id: true, title: true },
})
if (allNotes.length === 0) {
return []
}
// Find titles that appear in content (case-insensitive)
const suggestions: LinkSuggestion[] = []
const contentLower = content.toLowerCase()
for (const note of allNotes) {
const titleLower = note.title.toLowerCase()
// Check if title appears as a whole word in content
const regex = new RegExp(`\\b${escapeRegex(titleLower)}\\b`, 'i')
if (regex.test(content)) {
suggestions.push({
term: note.title,
noteId: note.id,
noteTitle: note.title,
})
}
}
// Sort by title length (longer titles first - more specific matches)
return suggestions.sort((a, b) => b.noteTitle.length - a.noteTitle.length)
}
/**
* Replace terms in content with wiki-links
*/
export function applyWikiLinks(
content: string,
replacements: { term: string; noteId: string }[]
): string {
let result = content
for (const { term, noteId } of replacements) {
// Replace all occurrences of the term (case-insensitive, whole word only)
const regex = new RegExp(`\\b(${escapeRegex(term)})\\b`, 'gi')
result = result.replace(regex, `[[${term}]]`)
}
return result
}
function escapeRegex(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}