feat(Index.tsx): enhance tag generation and search functionality

This commit is contained in:
2025-08-29 11:00:56 +02:00
parent b25103f74b
commit 5fbe98630b

View File

@@ -97,6 +97,7 @@ const Index = () => {
const [showDebugPanel, setShowDebugPanel] = useState(false);
const [autoGenerateTags, setAutoGenerateTags] = useState(true);
const [ollamaStatus, setOllamaStatus] = useState<'unknown' | 'online' | 'offline'>('unknown');
const [includeTagsInSearch, setIncludeTagsInSearch] = useState(true);
const { resolvedTheme, setTheme } = useTheme();
@@ -240,7 +241,7 @@ const Index = () => {
};
// Generate tags using Ollama
const generateTags = async (content: string): Promise<string[]> => {
const generateTags = async (content: string, noteIndex?: number): Promise<string[]> => {
try {
const systemPrompt = `You are a helpful assistant that generates searchable tags for journal entries.
@@ -256,9 +257,29 @@ Return ONLY a comma-separated list of tags, no other text. Example: golang, test
Keep tags concise, use lowercase, and separate words with hyphens if needed.`;
// Get context from surrounding notes
let context = '';
if (noteIndex !== undefined && noteCache.length > 0) {
const contextNotes = [];
const start = Math.max(0, noteIndex - 2);
const end = Math.min(noteCache.length, noteIndex + 3);
for (let i = start; i < end; i++) {
if (i !== noteIndex) {
const note = noteCache[i];
const date = new Date(note.epochTime).toLocaleDateString();
contextNotes.push(`[${date}] ${note.content.substring(0, 200)}${note.content.length > 200 ? '...' : ''}`);
}
}
if (contextNotes.length > 0) {
context = `\n\nContext from surrounding notes:\n${contextNotes.join('\n\n')}`;
}
}
const userPrompt = `Generate tags for this journal entry:
${content}`;
${content}${context}`;
const response = await fetchWithTiming(`${OLLAMA_ENDPOINT}/api/generate`, {
method: 'POST',
@@ -448,7 +469,7 @@ ${content}`;
let tags: string[] = currentNoteTags;
if (autoGenerateTags && tags.length === 0) {
addDebugInfo('Generating tags for new note...');
tags = await generateTags(trimmedContent);
tags = await generateTags(trimmedContent, 0); // New note will be at index 0
}
const document = {
@@ -519,7 +540,8 @@ ${content}`;
let tags = note.tags || [];
if (autoGenerateTags && trimmedContent !== note.content) {
addDebugInfo('Content changed, regenerating tags...');
tags = await generateTags(trimmedContent);
const noteIndex = noteCache.findIndex(n => n.id === note.id);
tags = await generateTags(trimmedContent, noteIndex);
}
const document = {
@@ -767,7 +789,7 @@ ${content}`;
const searchStartTime = Date.now();
console.log(`Starting search for: "${query}"`);
addDebugInfo(`Searching for: "${query}"`);
addDebugInfo(`Searching for: "${query}"${includeTagsInSearch ? ' (including tags)' : ' (content only)'}`);
try {
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
@@ -780,7 +802,7 @@ ${content}`;
q: query,
matchingStrategy: 'all',
limit: 50,
attributesToHighlight: ['content', 'tags'],
attributesToHighlight: includeTagsInSearch ? ['content', 'tags'] : ['content'],
showRankingScore: true,
highlightPreTag: '<mark class="bg-yellow-200">',
highlightPostTag: '</mark>',
@@ -1034,9 +1056,10 @@ ${content}`;
const notes: Note[] = data.hits.map((hit: any) => mapHitToNote(hit));
let updatedCount = 0;
for (const note of notes) {
for (let i = 0; i < notes.length; i++) {
const note = notes[i];
try {
const newTags = await generateTags(note.content);
const newTags = await generateTags(note.content, i);
if (JSON.stringify(newTags) !== JSON.stringify(note.tags || [])) {
const updatedNote = { ...note, tags: newTags };
await updateNote(updatedNote);
@@ -1731,7 +1754,7 @@ ${content}`;
onClick={async () => {
if (previousNote) {
addDebugInfo('Manually generating tags...');
const tags = await generateTags(previousNote.content);
const tags = await generateTags(previousNote.content, currentNoteIndex);
const updatedNote = { ...previousNote, tags };
setPreviousNote(updatedNote);
setIsPreviousNoteModified(true);
@@ -1794,7 +1817,7 @@ ${content}`;
onClick={async () => {
if (currentNote.trim()) {
addDebugInfo('Manually generating tags for current note...');
const tags = await generateTags(currentNote);
const tags = await generateTags(currentNote, 0); // Current note context
setCurrentNoteTags(tags);
}
}}
@@ -1840,14 +1863,24 @@ ${content}`;
<DialogTitle className={`${getTextClass('4xl')}`}>Search Notes</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div className="flex items-center gap-4">
<Input
ref={searchInputRef}
type="text"
placeholder="Search your notes..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={`${getTextClass('2xl')} bg-secondary border-input text-secondary-foreground`}
className={`flex-1 ${getTextClass('2xl')} bg-secondary border-input text-secondary-foreground`}
/>
<div className="flex items-center gap-2">
<span className={`${getTextClass('base')} text-muted-foreground`}>Include tags</span>
<Switch
checked={includeTagsInSearch}
onCheckedChange={setIncludeTagsInSearch}
aria-label="Toggle tag search"
/>
</div>
</div>
<div className="overflow-auto max-h-[50vh] space-y-4">
{searchResults.length > 0 ? (
searchResults.map((note) => (