Fix focus and add timing information about request timing
This commit is contained in:
@@ -12,6 +12,27 @@ import { Window } from '@tauri-apps/api/window';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
|
|
||||||
|
// Debug timing utility
|
||||||
|
const debugTiming = (operation: string, startTime: number) => {
|
||||||
|
const duration = Date.now() - startTime;
|
||||||
|
const message = `⏱️ ${operation}: ${duration}ms`;
|
||||||
|
console.log(message);
|
||||||
|
return duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enhanced fetch with timing
|
||||||
|
const fetchWithTiming = async (url: string, options: RequestInit, operation: string) => {
|
||||||
|
const startTime = Date.now();
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
debugTiming(operation, startTime);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
debugTiming(`${operation} (FAILED)`, startTime);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
interface Note {
|
interface Note {
|
||||||
id: string;
|
id: string;
|
||||||
epochTime: number;
|
epochTime: number;
|
||||||
@@ -72,9 +93,18 @@ const Index = () => {
|
|||||||
const [isGotoOpen, setIsGotoOpen] = useState(false);
|
const [isGotoOpen, setIsGotoOpen] = useState(false);
|
||||||
const [gotoDateInput, setGotoDateInput] = useState('');
|
const [gotoDateInput, setGotoDateInput] = useState('');
|
||||||
const [cacheMode, setCacheMode] = useState<'global' | 'scoped'>('global');
|
const [cacheMode, setCacheMode] = useState<'global' | 'scoped'>('global');
|
||||||
|
const [debugInfo, setDebugInfo] = useState<string[]>([]);
|
||||||
|
const [showDebugPanel, setShowDebugPanel] = useState(false);
|
||||||
|
|
||||||
const { resolvedTheme, setTheme } = useTheme();
|
const { resolvedTheme, setTheme } = useTheme();
|
||||||
|
|
||||||
|
// Debug logging function that updates UI state
|
||||||
|
const addDebugInfo = (message: string) => {
|
||||||
|
const timestamp = new Date().toLocaleTimeString();
|
||||||
|
const debugMessage = `[${timestamp}] ${message}`;
|
||||||
|
setDebugInfo(prev => [...prev.slice(-19), debugMessage]); // Keep last 20 messages
|
||||||
|
};
|
||||||
|
|
||||||
const previousNoteRef = useRef<HTMLDivElement>(null);
|
const previousNoteRef = useRef<HTMLDivElement>(null);
|
||||||
const currentNoteRef = useRef<HTMLTextAreaElement>(null);
|
const currentNoteRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const scratchRef = useRef<HTMLTextAreaElement>(null);
|
const scratchRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@@ -281,14 +311,14 @@ const Index = () => {
|
|||||||
letterCount: letterCount,
|
letterCount: letterCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(document),
|
body: JSON.stringify(document),
|
||||||
});
|
}, 'Create Note');
|
||||||
|
|
||||||
if (response.status !== 202) {
|
if (response.status !== 202) {
|
||||||
throw new Error('Failed to create note');
|
throw new Error('Failed to create note');
|
||||||
@@ -342,14 +372,14 @@ const Index = () => {
|
|||||||
letterCount: letterCount,
|
letterCount: letterCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(document),
|
body: JSON.stringify(document),
|
||||||
});
|
}, 'Update Note');
|
||||||
|
|
||||||
if (response.status !== 202) {
|
if (response.status !== 202) {
|
||||||
throw new Error('Failed to update note');
|
throw new Error('Failed to update note');
|
||||||
@@ -375,12 +405,12 @@ const Index = () => {
|
|||||||
// Delete a note
|
// Delete a note
|
||||||
const deleteNote = async (id: string) => {
|
const deleteNote = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents/${id}`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
});
|
}, 'Delete Note');
|
||||||
|
|
||||||
if (response.status !== 202) {
|
if (response.status !== 202) {
|
||||||
throw new Error('Failed to delete note');
|
throw new Error('Failed to delete note');
|
||||||
@@ -412,12 +442,14 @@ const Index = () => {
|
|||||||
|
|
||||||
// Load notes from Meilisearch
|
// Load notes from Meilisearch
|
||||||
const loadNotes = async (offset = 0, limit = 500) => {
|
const loadNotes = async (offset = 0, limit = 500) => {
|
||||||
|
const loadStartTime = Date.now();
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (offset === 0) {
|
if (offset === 0) {
|
||||||
setCacheMode('global');
|
setCacheMode('global');
|
||||||
|
addDebugInfo(`📝 Loading notes (offset: ${offset}, limit: ${limit})`);
|
||||||
}
|
}
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -429,7 +461,7 @@ const Index = () => {
|
|||||||
limit,
|
limit,
|
||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Load Notes');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to load notes');
|
throw new Error('Failed to load notes');
|
||||||
@@ -444,11 +476,17 @@ const Index = () => {
|
|||||||
setPreviousNote(notes[0]);
|
setPreviousNote(notes[0]);
|
||||||
setCurrentNoteIndex(0);
|
setCurrentNoteIndex(0);
|
||||||
}
|
}
|
||||||
|
const loadTime = Date.now() - loadStartTime;
|
||||||
|
addDebugInfo(`✅ Loaded ${notes.length} notes in ${loadTime}ms`);
|
||||||
} else {
|
} else {
|
||||||
setNoteCache(prev => [...prev, ...notes]);
|
setNoteCache(prev => [...prev, ...notes]);
|
||||||
|
const loadTime = Date.now() - loadStartTime;
|
||||||
|
addDebugInfo(`✅ Loaded ${notes.length} additional notes in ${loadTime}ms`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading notes:', error);
|
console.error('Error loading notes:', error);
|
||||||
|
const loadTime = Date.now() - loadStartTime;
|
||||||
|
addDebugInfo(`❌ Failed to load notes after ${loadTime}ms`);
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: "Failed to load notes. Please check your connection.",
|
description: "Failed to load notes. Please check your connection.",
|
||||||
@@ -466,7 +504,7 @@ const Index = () => {
|
|||||||
setCacheMode('scoped');
|
setCacheMode('scoped');
|
||||||
|
|
||||||
// Load a larger set to find the target note and surrounding context
|
// Load a larger set to find the target note and surrounding context
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -478,7 +516,7 @@ const Index = () => {
|
|||||||
limit: 1000,
|
limit: 1000,
|
||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Load Notes Around Note');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to load notes');
|
throw new Error('Failed to load notes');
|
||||||
@@ -515,16 +553,16 @@ const Index = () => {
|
|||||||
setCacheMode('scoped');
|
setCacheMode('scoped');
|
||||||
|
|
||||||
const [beforeRes, afterRes] = await Promise.all([
|
const [beforeRes, afterRes] = await Promise.all([
|
||||||
fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: meiliHeaders,
|
headers: meiliHeaders,
|
||||||
body: JSON.stringify({ q: '', filter: `date <= ${targetMs}`, sort: ['date:desc'], limit: beforeLimit }),
|
body: JSON.stringify({ q: '', filter: `date <= ${targetMs}`, sort: ['date:desc'], limit: beforeLimit }),
|
||||||
}),
|
}, 'Load Notes Around Date (Before)'),
|
||||||
fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: meiliHeaders,
|
headers: meiliHeaders,
|
||||||
body: JSON.stringify({ q: '', filter: `date >= ${targetMs}`, sort: ['date:asc'], limit: afterLimit }),
|
body: JSON.stringify({ q: '', filter: `date >= ${targetMs}`, sort: ['date:asc'], limit: afterLimit }),
|
||||||
}),
|
}, 'Load Notes Around Date (After)'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [beforeData, afterData] = await Promise.all([
|
const [beforeData, afterData] = await Promise.all([
|
||||||
@@ -566,8 +604,12 @@ const Index = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const searchStartTime = Date.now();
|
||||||
|
console.log(`🔍 Starting search for: "${query}"`);
|
||||||
|
addDebugInfo(`🔍 Searching for: "${query}"`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -583,7 +625,7 @@ const Index = () => {
|
|||||||
highlightPostTag: '</mark>',
|
highlightPostTag: '</mark>',
|
||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Search Notes');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to search notes');
|
throw new Error('Failed to search notes');
|
||||||
@@ -596,8 +638,15 @@ const Index = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
setSearchResults(results);
|
setSearchResults(results);
|
||||||
|
const searchTime = Date.now() - searchStartTime;
|
||||||
|
debugTiming(`Search Results (${results.length} found)`, searchStartTime);
|
||||||
|
addDebugInfo(`✅ Search complete: ${results.length} results in ${searchTime}ms`);
|
||||||
|
console.log(`✅ Search complete: ${results.length} results found`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error searching notes:', error);
|
console.error('Error searching notes:', error);
|
||||||
|
const searchTime = Date.now() - searchStartTime;
|
||||||
|
debugTiming('Search (FAILED)', searchStartTime);
|
||||||
|
addDebugInfo(`❌ Search failed after ${searchTime}ms`);
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: "Failed to search notes. Please try again.",
|
description: "Failed to search notes. Please try again.",
|
||||||
@@ -656,7 +705,7 @@ const Index = () => {
|
|||||||
const threshold = cleanupThreshold[0];
|
const threshold = cleanupThreshold[0];
|
||||||
const minCount = minLetterCount[0];
|
const minCount = minLetterCount[0];
|
||||||
|
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -667,7 +716,7 @@ const Index = () => {
|
|||||||
filter: `topLetterFrequency > ${threshold} OR letterCount < ${minCount}`,
|
filter: `topLetterFrequency > ${threshold} OR letterCount < ${minCount}`,
|
||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Get Problematic Notes');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to get problematic notes');
|
throw new Error('Failed to get problematic notes');
|
||||||
@@ -836,15 +885,27 @@ const Index = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle window focus to restore focus to the current note field
|
||||||
|
const handleWindowFocus = () => {
|
||||||
|
// Only restore focus if no modal is open and the current note field exists
|
||||||
|
if (!isSearchOpen && !isGotoOpen && !isCleanupOpen && currentNoteRef.current) {
|
||||||
|
currentNoteRef.current.focus();
|
||||||
|
console.log('🎯 Focus restored on window focus');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
window.addEventListener('focus', handleWindowFocus);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('keydown', handleKeyDown);
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
window.removeEventListener('focus', handleWindowFocus);
|
||||||
// Clear any pending save timeout on cleanup
|
// Clear any pending save timeout on cleanup
|
||||||
if (saveTimeoutRef.current) {
|
if (saveTimeoutRef.current) {
|
||||||
clearTimeout(saveTimeoutRef.current);
|
clearTimeout(saveTimeoutRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [currentNote, previousNote, scratchPad, isPreviousNoteModified]);
|
}, [currentNote, previousNote, scratchPad, isPreviousNoteModified, isSearchOpen, isGotoOpen, isCleanupOpen]);
|
||||||
|
|
||||||
// Auto-save functions
|
// Auto-save functions
|
||||||
const handleCurrentNoteBlur = () => {
|
const handleCurrentNoteBlur = () => {
|
||||||
@@ -868,7 +929,7 @@ const Index = () => {
|
|||||||
// Load latest scratch
|
// Load latest scratch
|
||||||
const loadLatestScratch = async () => {
|
const loadLatestScratch = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -879,7 +940,7 @@ const Index = () => {
|
|||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Load Latest Scratch');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to load latest scratch');
|
throw new Error('Failed to load latest scratch');
|
||||||
@@ -914,14 +975,14 @@ const Index = () => {
|
|||||||
content: content,
|
content: content,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/documents`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/documents`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(document),
|
body: JSON.stringify(document),
|
||||||
});
|
}, 'Save Scratch');
|
||||||
|
|
||||||
if (response.status !== 202) {
|
if (response.status !== 202) {
|
||||||
throw new Error('Failed to save scratch');
|
throw new Error('Failed to save scratch');
|
||||||
@@ -957,11 +1018,11 @@ const Index = () => {
|
|||||||
// Check if an index exists
|
// Check if an index exists
|
||||||
const indexExists = async (index: string): Promise<boolean> => {
|
const indexExists = async (index: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${index}`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${index}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
});
|
}, `Check Index ${index}`);
|
||||||
return response.status === 200;
|
return response.status === 200;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error checking if index ${index} exists:`, error);
|
console.error(`Error checking if index ${index} exists:`, error);
|
||||||
@@ -971,10 +1032,15 @@ const Index = () => {
|
|||||||
|
|
||||||
// Initialize Meilisearch indexes
|
// Initialize Meilisearch indexes
|
||||||
const initializeIndexes = async () => {
|
const initializeIndexes = async () => {
|
||||||
|
const initStartTime = Date.now();
|
||||||
try {
|
try {
|
||||||
|
console.log('🚀 Starting initialization...');
|
||||||
|
addDebugInfo('🚀 Starting initialization...');
|
||||||
|
|
||||||
// Initialize notes index
|
// Initialize notes index
|
||||||
if (!(await indexExists(NOTE_INDEX))) {
|
if (!(await indexExists(NOTE_INDEX))) {
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes`, {
|
const indexStart = Date.now();
|
||||||
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -984,41 +1050,42 @@ const Index = () => {
|
|||||||
uid: NOTE_INDEX,
|
uid: NOTE_INDEX,
|
||||||
primaryKey: 'id',
|
primaryKey: 'id',
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Initialize Notes Index');
|
||||||
|
addDebugInfo(`⏱️ Initialize Notes Index: ${Date.now() - indexStart}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure notes index settings
|
// Configure notes index settings
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/settings/sortable-attributes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/settings/sortable-attributes`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(['date']),
|
body: JSON.stringify(['date']),
|
||||||
});
|
}, 'Configure Notes Sortable Attributes');
|
||||||
|
|
||||||
// Set ranking rules to prioritize sorting
|
// Set ranking rules to prioritize sorting
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/settings/ranking-rules`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/settings/ranking-rules`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(['sort', 'words', 'typo', 'proximity', 'attribute', 'exactness']),
|
body: JSON.stringify(['sort', 'words', 'typo', 'proximity', 'attribute', 'exactness']),
|
||||||
});
|
}, 'Configure Notes Ranking Rules');
|
||||||
|
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/settings/filterable-attributes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/settings/filterable-attributes`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(['date', 'topLetter', 'letterCount', 'topLetterFrequency']),
|
body: JSON.stringify(['date', 'topLetter', 'letterCount', 'topLetterFrequency']),
|
||||||
});
|
}, 'Configure Notes Filterable Attributes');
|
||||||
|
|
||||||
// Initialize scratch index
|
// Initialize scratch index
|
||||||
if (!(await indexExists(SCRATCH_INDEX))) {
|
if (!(await indexExists(SCRATCH_INDEX))) {
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -1028,31 +1095,31 @@ const Index = () => {
|
|||||||
uid: SCRATCH_INDEX,
|
uid: SCRATCH_INDEX,
|
||||||
primaryKey: 'id',
|
primaryKey: 'id',
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Initialize Scratch Index');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure scratch index settings
|
// Configure scratch index settings
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/settings/sortable-attributes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/settings/sortable-attributes`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(['date']),
|
body: JSON.stringify(['date']),
|
||||||
});
|
}, 'Configure Scratch Sortable Attributes');
|
||||||
|
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/settings/filterable-attributes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/settings/filterable-attributes`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(['date']),
|
body: JSON.stringify(['date']),
|
||||||
});
|
}, 'Configure Scratch Filterable Attributes');
|
||||||
|
|
||||||
// Initialize settings index
|
// Initialize settings index
|
||||||
if (!(await indexExists(SETTINGS_INDEX))) {
|
if (!(await indexExists(SETTINGS_INDEX))) {
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -1062,26 +1129,33 @@ const Index = () => {
|
|||||||
uid: SETTINGS_INDEX,
|
uid: SETTINGS_INDEX,
|
||||||
primaryKey: 'key',
|
primaryKey: 'key',
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Initialize Settings Index');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure settings index
|
// Configure settings index
|
||||||
await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/settings/filterable-attributes`, {
|
await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/settings/filterable-attributes`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(['key', 'value']),
|
body: JSON.stringify(['key', 'value']),
|
||||||
});
|
}, 'Configure Settings Filterable Attributes');
|
||||||
|
|
||||||
setIsInitialized(true);
|
setIsInitialized(true);
|
||||||
|
const totalTime = Date.now() - initStartTime;
|
||||||
|
debugTiming('Total Initialization', initStartTime);
|
||||||
|
addDebugInfo(`✅ Total Initialization: ${totalTime}ms`);
|
||||||
|
console.log('✅ Initialization complete');
|
||||||
toast({
|
toast({
|
||||||
title: "Initialization complete",
|
title: "Initialization complete",
|
||||||
description: "All indexes have been configured successfully.",
|
description: "All indexes have been configured successfully.",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing indexes:', error);
|
console.error('Error initializing indexes:', error);
|
||||||
|
const totalTime = Date.now() - initStartTime;
|
||||||
|
debugTiming('Initialization (FAILED)', initStartTime);
|
||||||
|
addDebugInfo(`❌ Initialization FAILED: ${totalTime}ms`);
|
||||||
toast({
|
toast({
|
||||||
title: "Initialization failed",
|
title: "Initialization failed",
|
||||||
description: "Failed to initialize indexes. Please check your connection.",
|
description: "Failed to initialize indexes. Please check your connection.",
|
||||||
@@ -1090,6 +1164,17 @@ const Index = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Immediate focus when component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
// Focus the current note field immediately with a small delay to ensure DOM is ready
|
||||||
|
const focusTimer = setTimeout(() => {
|
||||||
|
currentNoteRef.current?.focus();
|
||||||
|
console.log('🎯 Focus set on current note field');
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(focusTimer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initIndexes = async () => {
|
const initIndexes = async () => {
|
||||||
await initializeIndexes();
|
await initializeIndexes();
|
||||||
@@ -1100,9 +1185,20 @@ const Index = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadInitialData = async () => {
|
const loadInitialData = async () => {
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
|
const dataLoadStartTime = Date.now();
|
||||||
|
console.log('📊 Starting data loading...');
|
||||||
|
addDebugInfo('📊 Starting data loading...');
|
||||||
|
|
||||||
await loadNotes();
|
await loadNotes();
|
||||||
await loadLatestScratch();
|
await loadLatestScratch();
|
||||||
await loadFontSizeSetting();
|
await loadFontSizeSetting();
|
||||||
|
|
||||||
|
const totalTime = Date.now() - dataLoadStartTime;
|
||||||
|
debugTiming('Total Data Loading', dataLoadStartTime);
|
||||||
|
addDebugInfo(`✅ Total Data Loading: ${totalTime}ms`);
|
||||||
|
console.log('✅ Data loading complete');
|
||||||
|
|
||||||
|
// Re-focus after data loading to ensure focus is maintained
|
||||||
currentNoteRef.current?.focus();
|
currentNoteRef.current?.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1127,7 +1223,7 @@ const Index = () => {
|
|||||||
// Load font size setting
|
// Load font size setting
|
||||||
const loadFontSizeSetting = async () => {
|
const loadFontSizeSetting = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/search`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/search`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -1138,7 +1234,7 @@ const Index = () => {
|
|||||||
filter: 'key = "fontSize"',
|
filter: 'key = "fontSize"',
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}),
|
}),
|
||||||
});
|
}, 'Load Font Size Setting');
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -1160,14 +1256,14 @@ const Index = () => {
|
|||||||
updatedAt: new Date().getTime(),
|
updatedAt: new Date().getTime(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/documents`, {
|
const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/documents`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(document),
|
body: JSON.stringify(document),
|
||||||
});
|
}, 'Save Font Size Setting');
|
||||||
|
|
||||||
if (response.status !== 202) {
|
if (response.status !== 202) {
|
||||||
throw new Error('Failed to save font size setting');
|
throw new Error('Failed to save font size setting');
|
||||||
@@ -1219,10 +1315,38 @@ const Index = () => {
|
|||||||
<Trash2 className="h-8 w-8" />
|
<Trash2 className="h-8 w-8" />
|
||||||
Cleanup
|
Cleanup
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setShowDebugPanel(!showDebugPanel)}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className={getTextClass('base')}
|
||||||
|
>
|
||||||
|
{showDebugPanel ? 'Hide' : 'Show'} Debug
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{/* Debug Panel */}
|
||||||
|
{showDebugPanel && (
|
||||||
|
<div className="bg-muted border-b border-border p-4 max-h-48 overflow-y-auto">
|
||||||
|
<div className={`${getTextClass('xl')} font-semibold mb-2`}>Debug Information</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{debugInfo.length > 0 ? (
|
||||||
|
debugInfo.map((info, index) => (
|
||||||
|
<div key={index} className={`${getTextClass('base')} font-mono text-xs`}>
|
||||||
|
{info}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className={`${getTextClass('base')} text-muted-foreground`}>
|
||||||
|
No debug information available yet...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="flex-1 flex p-4 gap-4 overflow-hidden">
|
<div className="flex-1 flex p-4 gap-4 overflow-hidden">
|
||||||
{/* Left Panel - 70% */}
|
{/* Left Panel - 70% */}
|
||||||
|
Reference in New Issue
Block a user