diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index f97bb5b..dcdda17 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -12,6 +12,27 @@ import { Window } from '@tauri-apps/api/window'; import { Switch } from '@/components/ui/switch'; 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 { id: string; epochTime: number; @@ -72,9 +93,18 @@ const Index = () => { const [isGotoOpen, setIsGotoOpen] = useState(false); const [gotoDateInput, setGotoDateInput] = useState(''); const [cacheMode, setCacheMode] = useState<'global' | 'scoped'>('global'); + const [debugInfo, setDebugInfo] = useState([]); + const [showDebugPanel, setShowDebugPanel] = useState(false); 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(null); const currentNoteRef = useRef(null); const scratchRef = useRef(null); @@ -281,14 +311,14 @@ const Index = () => { letterCount: letterCount, }; - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(document), - }); + }, 'Create Note'); if (response.status !== 202) { throw new Error('Failed to create note'); @@ -342,14 +372,14 @@ const Index = () => { letterCount: letterCount, }; - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/documents`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(document), - }); + }, 'Update Note'); if (response.status !== 202) { throw new Error('Failed to update note'); @@ -375,12 +405,12 @@ const Index = () => { // Delete a note const deleteNote = async (id: string) => { 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', headers: { 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, - }); + }, 'Delete Note'); if (response.status !== 202) { throw new Error('Failed to delete note'); @@ -412,12 +442,14 @@ const Index = () => { // Load notes from Meilisearch const loadNotes = async (offset = 0, limit = 500) => { + const loadStartTime = Date.now(); try { setIsLoading(true); if (offset === 0) { 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', headers: { 'Content-Type': 'application/json', @@ -429,7 +461,7 @@ const Index = () => { limit, sort: ['date:desc'], }), - }); + }, 'Load Notes'); if (!response.ok) { throw new Error('Failed to load notes'); @@ -444,11 +476,17 @@ const Index = () => { setPreviousNote(notes[0]); setCurrentNoteIndex(0); } + const loadTime = Date.now() - loadStartTime; + addDebugInfo(`✅ Loaded ${notes.length} notes in ${loadTime}ms`); } else { setNoteCache(prev => [...prev, ...notes]); + const loadTime = Date.now() - loadStartTime; + addDebugInfo(`✅ Loaded ${notes.length} additional notes in ${loadTime}ms`); } } catch (error) { console.error('Error loading notes:', error); + const loadTime = Date.now() - loadStartTime; + addDebugInfo(`❌ Failed to load notes after ${loadTime}ms`); toast({ title: "Error", description: "Failed to load notes. Please check your connection.", @@ -466,7 +504,7 @@ const Index = () => { setCacheMode('scoped'); // 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', headers: { 'Content-Type': 'application/json', @@ -478,7 +516,7 @@ const Index = () => { limit: 1000, sort: ['date:desc'], }), - }); + }, 'Load Notes Around Note'); if (!response.ok) { throw new Error('Failed to load notes'); @@ -515,16 +553,16 @@ const Index = () => { setCacheMode('scoped'); const [beforeRes, afterRes] = await Promise.all([ - fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, { + fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, { method: 'POST', headers: meiliHeaders, body: JSON.stringify({ q: '', filter: `date <= ${targetMs}`, sort: ['date:desc'], limit: beforeLimit }), - }), - fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, { + }, 'Load Notes Around Date (Before)'), + fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, { method: 'POST', headers: meiliHeaders, body: JSON.stringify({ q: '', filter: `date >= ${targetMs}`, sort: ['date:asc'], limit: afterLimit }), - }), + }, 'Load Notes Around Date (After)'), ]); const [beforeData, afterData] = await Promise.all([ @@ -566,8 +604,12 @@ const Index = () => { return; } + const searchStartTime = Date.now(); + console.log(`🔍 Starting search for: "${query}"`); + addDebugInfo(`🔍 Searching for: "${query}"`); + try { - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -583,7 +625,7 @@ const Index = () => { highlightPostTag: '', sort: ['date:desc'], }), - }); + }, 'Search Notes'); if (!response.ok) { throw new Error('Failed to search notes'); @@ -596,8 +638,15 @@ const Index = () => { })); 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) { console.error('Error searching notes:', error); + const searchTime = Date.now() - searchStartTime; + debugTiming('Search (FAILED)', searchStartTime); + addDebugInfo(`❌ Search failed after ${searchTime}ms`); toast({ title: "Error", description: "Failed to search notes. Please try again.", @@ -656,7 +705,7 @@ const Index = () => { const threshold = cleanupThreshold[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', headers: { 'Content-Type': 'application/json', @@ -667,7 +716,7 @@ const Index = () => { filter: `topLetterFrequency > ${threshold} OR letterCount < ${minCount}`, sort: ['date:desc'], }), - }); + }, 'Get Problematic Notes'); if (!response.ok) { 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); + window.addEventListener('focus', handleWindowFocus); + return () => { document.removeEventListener('keydown', handleKeyDown); + window.removeEventListener('focus', handleWindowFocus); // Clear any pending save timeout on cleanup if (saveTimeoutRef.current) { clearTimeout(saveTimeoutRef.current); } }; - }, [currentNote, previousNote, scratchPad, isPreviousNoteModified]); + }, [currentNote, previousNote, scratchPad, isPreviousNoteModified, isSearchOpen, isGotoOpen, isCleanupOpen]); // Auto-save functions const handleCurrentNoteBlur = () => { @@ -868,7 +929,7 @@ const Index = () => { // Load latest scratch const loadLatestScratch = async () => { try { - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/search`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -879,7 +940,7 @@ const Index = () => { sort: ['date:desc'], limit: 1, }), - }); + }, 'Load Latest Scratch'); if (!response.ok) { throw new Error('Failed to load latest scratch'); @@ -914,14 +975,14 @@ const Index = () => { content: content, }; - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/documents`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SCRATCH_INDEX}/documents`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(document), - }); + }, 'Save Scratch'); if (response.status !== 202) { throw new Error('Failed to save scratch'); @@ -957,11 +1018,11 @@ const Index = () => { // Check if an index exists const indexExists = async (index: string): Promise => { try { - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${index}`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${index}`, { headers: { 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, - }); + }, `Check Index ${index}`); return response.status === 200; } catch (error) { console.error(`Error checking if index ${index} exists:`, error); @@ -971,10 +1032,15 @@ const Index = () => { // Initialize Meilisearch indexes const initializeIndexes = async () => { + const initStartTime = Date.now(); try { + console.log('🚀 Starting initialization...'); + addDebugInfo('🚀 Starting initialization...'); + // Initialize notes index if (!(await indexExists(NOTE_INDEX))) { - await fetch(`${MEILISEARCH_ENDPOINT}/indexes`, { + const indexStart = Date.now(); + await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -984,41 +1050,42 @@ const Index = () => { uid: NOTE_INDEX, primaryKey: 'id', }), - }); + }, 'Initialize Notes Index'); + addDebugInfo(`⏱️ Initialize Notes Index: ${Date.now() - indexStart}ms`); } // 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(['date']), - }); + }, 'Configure Notes Sortable Attributes'); // 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(['date', 'topLetter', 'letterCount', 'topLetterFrequency']), - }); + }, 'Configure Notes Filterable Attributes'); // Initialize scratch index if (!(await indexExists(SCRATCH_INDEX))) { - await fetch(`${MEILISEARCH_ENDPOINT}/indexes`, { + await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -1028,31 +1095,31 @@ const Index = () => { uid: SCRATCH_INDEX, primaryKey: 'id', }), - }); + }, 'Initialize Scratch Index'); } // 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(['date']), - }); + }, 'Configure Scratch Filterable Attributes'); // Initialize settings index if (!(await indexExists(SETTINGS_INDEX))) { - await fetch(`${MEILISEARCH_ENDPOINT}/indexes`, { + await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -1062,26 +1129,33 @@ const Index = () => { uid: SETTINGS_INDEX, primaryKey: 'key', }), - }); + }, 'Initialize 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(['key', 'value']), - }); + }, 'Configure Settings Filterable Attributes'); setIsInitialized(true); + const totalTime = Date.now() - initStartTime; + debugTiming('Total Initialization', initStartTime); + addDebugInfo(`✅ Total Initialization: ${totalTime}ms`); + console.log('✅ Initialization complete'); toast({ title: "Initialization complete", description: "All indexes have been configured successfully.", }); } catch (error) { console.error('Error initializing indexes:', error); + const totalTime = Date.now() - initStartTime; + debugTiming('Initialization (FAILED)', initStartTime); + addDebugInfo(`❌ Initialization FAILED: ${totalTime}ms`); toast({ title: "Initialization failed", 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(() => { const initIndexes = async () => { await initializeIndexes(); @@ -1100,9 +1185,20 @@ const Index = () => { useEffect(() => { const loadInitialData = async () => { if (isInitialized) { + const dataLoadStartTime = Date.now(); + console.log('📊 Starting data loading...'); + addDebugInfo('📊 Starting data loading...'); + await loadNotes(); await loadLatestScratch(); 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(); } }; @@ -1127,7 +1223,7 @@ const Index = () => { // Load font size setting const loadFontSizeSetting = async () => { try { - const response = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/search`, { + const response = await fetchWithTiming(`${MEILISEARCH_ENDPOINT}/indexes/${SETTINGS_INDEX}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -1138,7 +1234,7 @@ const Index = () => { filter: 'key = "fontSize"', limit: 1, }), - }); + }, 'Load Font Size Setting'); if (response.ok) { const data = await response.json(); @@ -1160,14 +1256,14 @@ const Index = () => { 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', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${MEILISEARCH_API_KEY}`, }, body: JSON.stringify(document), - }); + }, 'Save Font Size Setting'); if (response.status !== 202) { throw new Error('Failed to save font size setting'); @@ -1219,10 +1315,38 @@ const Index = () => { Cleanup + + {/* Debug Panel */} + {showDebugPanel && ( +
+
Debug Information
+
+ {debugInfo.length > 0 ? ( + debugInfo.map((info, index) => ( +
+ {info} +
+ )) + ) : ( +
+ No debug information available yet... +
+ )} +
+
+ )} + {/* Main Content */}
{/* Left Panel - 70% */}