feat(Index.tsx): enhance tag generation and search functionality
This commit is contained in:
@@ -97,6 +97,7 @@ const Index = () => {
|
|||||||
const [showDebugPanel, setShowDebugPanel] = useState(false);
|
const [showDebugPanel, setShowDebugPanel] = useState(false);
|
||||||
const [autoGenerateTags, setAutoGenerateTags] = useState(true);
|
const [autoGenerateTags, setAutoGenerateTags] = useState(true);
|
||||||
const [ollamaStatus, setOllamaStatus] = useState<'unknown' | 'online' | 'offline'>('unknown');
|
const [ollamaStatus, setOllamaStatus] = useState<'unknown' | 'online' | 'offline'>('unknown');
|
||||||
|
const [includeTagsInSearch, setIncludeTagsInSearch] = useState(true);
|
||||||
|
|
||||||
const { resolvedTheme, setTheme } = useTheme();
|
const { resolvedTheme, setTheme } = useTheme();
|
||||||
|
|
||||||
@@ -240,7 +241,7 @@ const Index = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Generate tags using Ollama
|
// Generate tags using Ollama
|
||||||
const generateTags = async (content: string): Promise<string[]> => {
|
const generateTags = async (content: string, noteIndex?: number): Promise<string[]> => {
|
||||||
try {
|
try {
|
||||||
const systemPrompt = `You are a helpful assistant that generates searchable tags for journal entries.
|
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.`;
|
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:
|
const userPrompt = `Generate tags for this journal entry:
|
||||||
|
|
||||||
${content}`;
|
${content}${context}`;
|
||||||
|
|
||||||
const response = await fetchWithTiming(`${OLLAMA_ENDPOINT}/api/generate`, {
|
const response = await fetchWithTiming(`${OLLAMA_ENDPOINT}/api/generate`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -448,7 +469,7 @@ ${content}`;
|
|||||||
let tags: string[] = currentNoteTags;
|
let tags: string[] = currentNoteTags;
|
||||||
if (autoGenerateTags && tags.length === 0) {
|
if (autoGenerateTags && tags.length === 0) {
|
||||||
addDebugInfo('Generating tags for new note...');
|
addDebugInfo('Generating tags for new note...');
|
||||||
tags = await generateTags(trimmedContent);
|
tags = await generateTags(trimmedContent, 0); // New note will be at index 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const document = {
|
const document = {
|
||||||
@@ -519,7 +540,8 @@ ${content}`;
|
|||||||
let tags = note.tags || [];
|
let tags = note.tags || [];
|
||||||
if (autoGenerateTags && trimmedContent !== note.content) {
|
if (autoGenerateTags && trimmedContent !== note.content) {
|
||||||
addDebugInfo('Content changed, regenerating tags...');
|
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 = {
|
const document = {
|
||||||
@@ -767,7 +789,7 @@ ${content}`;
|
|||||||
|
|
||||||
const searchStartTime = Date.now();
|
const searchStartTime = Date.now();
|
||||||
console.log(`Starting search for: "${query}"`);
|
console.log(`Starting search for: "${query}"`);
|
||||||
addDebugInfo(`Searching for: "${query}"`);
|
addDebugInfo(`Searching for: "${query}"${includeTagsInSearch ? ' (including tags)' : ' (content only)'}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
@@ -780,7 +802,7 @@ ${content}`;
|
|||||||
q: query,
|
q: query,
|
||||||
matchingStrategy: 'all',
|
matchingStrategy: 'all',
|
||||||
limit: 50,
|
limit: 50,
|
||||||
attributesToHighlight: ['content', 'tags'],
|
attributesToHighlight: includeTagsInSearch ? ['content', 'tags'] : ['content'],
|
||||||
showRankingScore: true,
|
showRankingScore: true,
|
||||||
highlightPreTag: '<mark class="bg-yellow-200">',
|
highlightPreTag: '<mark class="bg-yellow-200">',
|
||||||
highlightPostTag: '</mark>',
|
highlightPostTag: '</mark>',
|
||||||
@@ -1034,9 +1056,10 @@ ${content}`;
|
|||||||
const notes: Note[] = data.hits.map((hit: any) => mapHitToNote(hit));
|
const notes: Note[] = data.hits.map((hit: any) => mapHitToNote(hit));
|
||||||
|
|
||||||
let updatedCount = 0;
|
let updatedCount = 0;
|
||||||
for (const note of notes) {
|
for (let i = 0; i < notes.length; i++) {
|
||||||
|
const note = notes[i];
|
||||||
try {
|
try {
|
||||||
const newTags = await generateTags(note.content);
|
const newTags = await generateTags(note.content, i);
|
||||||
if (JSON.stringify(newTags) !== JSON.stringify(note.tags || [])) {
|
if (JSON.stringify(newTags) !== JSON.stringify(note.tags || [])) {
|
||||||
const updatedNote = { ...note, tags: newTags };
|
const updatedNote = { ...note, tags: newTags };
|
||||||
await updateNote(updatedNote);
|
await updateNote(updatedNote);
|
||||||
@@ -1731,7 +1754,7 @@ ${content}`;
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (previousNote) {
|
if (previousNote) {
|
||||||
addDebugInfo('Manually generating tags...');
|
addDebugInfo('Manually generating tags...');
|
||||||
const tags = await generateTags(previousNote.content);
|
const tags = await generateTags(previousNote.content, currentNoteIndex);
|
||||||
const updatedNote = { ...previousNote, tags };
|
const updatedNote = { ...previousNote, tags };
|
||||||
setPreviousNote(updatedNote);
|
setPreviousNote(updatedNote);
|
||||||
setIsPreviousNoteModified(true);
|
setIsPreviousNoteModified(true);
|
||||||
@@ -1794,7 +1817,7 @@ ${content}`;
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (currentNote.trim()) {
|
if (currentNote.trim()) {
|
||||||
addDebugInfo('Manually generating tags for current note...');
|
addDebugInfo('Manually generating tags for current note...');
|
||||||
const tags = await generateTags(currentNote);
|
const tags = await generateTags(currentNote, 0); // Current note context
|
||||||
setCurrentNoteTags(tags);
|
setCurrentNoteTags(tags);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -1840,14 +1863,24 @@ ${content}`;
|
|||||||
<DialogTitle className={`${getTextClass('4xl')}`}>Search Notes</DialogTitle>
|
<DialogTitle className={`${getTextClass('4xl')}`}>Search Notes</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
<Input
|
<Input
|
||||||
ref={searchInputRef}
|
ref={searchInputRef}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search your notes..."
|
placeholder="Search your notes..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
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">
|
<div className="overflow-auto max-h-[50vh] space-y-4">
|
||||||
{searchResults.length > 0 ? (
|
{searchResults.length > 0 ? (
|
||||||
searchResults.map((note) => (
|
searchResults.map((note) => (
|
||||||
|
Reference in New Issue
Block a user