fix: improve note interactions and markdown LaTeX support

## Bug Fixes

### Note Card Actions
- Fix broken size change functionality (missing state declaration)
- Implement React 19 useOptimistic for instant UI feedback
- Add startTransition for non-blocking updates
- Ensure smooth animations without page refresh
- All note actions now work: pin, archive, color, size, checklist

### Markdown LaTeX Rendering
- Add remark-math and rehype-katex plugins
- Support inline equations with dollar sign syntax
- Support block equations with double dollar sign syntax
- Import KaTeX CSS for proper styling
- Equations now render correctly instead of showing raw LaTeX

## Technical Details

- Replace undefined currentNote references with optimistic state
- Add optimistic updates before server actions for instant feedback
- Use router.refresh() in transitions for smart cache invalidation
- Install remark-math, rehype-katex, and katex packages

## Testing

- Build passes successfully with no TypeScript errors
- Dev server hot-reloads changes correctly
This commit is contained in:
2026-01-09 22:13:49 +01:00
parent 3c4b9d6176
commit 640fcb26f7
218 changed files with 51363 additions and 902 deletions

View File

@@ -20,12 +20,18 @@ export async function fetchLinkMetadata(url: string): Promise<LinkMetadata | nul
const response = await fetch(targetUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; Memento/1.0; +http://localhost:3000)',
// Use a real browser User-Agent to avoid 403 Forbidden from strict sites
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5'
},
next: { revalidate: 3600 } // Cache for 1 hour
});
if (!response.ok) return null;
if (!response.ok) {
console.warn(`[Scrape] Failed to fetch ${targetUrl}: ${response.status} ${response.statusText}`);
return null;
}
const html = await response.text();
const $ = cheerio.load(html);
@@ -34,21 +40,21 @@ export async function fetchLinkMetadata(url: string): Promise<LinkMetadata | nul
$(`meta[property="${prop}"]`).attr('content') ||
$(`meta[name="${prop}"]`).attr('content');
const title = getMeta('og:title') || $('title').text() || '';
const description = getMeta('og:description') || getMeta('description') || '';
const imageUrl = getMeta('og:image');
const siteName = getMeta('og:site_name');
// Robust extraction with fallbacks
const title = getMeta('og:title') || $('title').text() || getMeta('twitter:title') || url;
const description = getMeta('og:description') || getMeta('description') || getMeta('twitter:description') || '';
const imageUrl = getMeta('og:image') || getMeta('twitter:image') || $('link[rel="image_src"]').attr('href');
const siteName = getMeta('og:site_name') || '';
return {
url: targetUrl,
title: title.substring(0, 100), // Truncate if too long
title: title.substring(0, 100),
description: description.substring(0, 200),
imageUrl,
siteName
};
} catch (error) {
console.error('Error fetching link metadata:', error);
console.error(`[Scrape] Error fetching ${url}:`, error);
return null;
}
}