fix(chart): transaction mapping fix + add cache for suggestions

- Fix transaction.mapping.map usage - it returns a position, not mapping
- Add 5-minute in-memory cache for chart suggestions (max 50 entries)
- Cache key based on content hash + selection
- Significantly improves UX when re-analyzing same content

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Antigravity
2026-05-23 10:12:11 +00:00
parent 468a2bffc8
commit af3a263a54
2 changed files with 60 additions and 7 deletions

View File

@@ -297,7 +297,7 @@ export const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorPro
try {
console.log('[handleSelectChart] Inserting chart type:', chartContent.split('\n')[0])
const { from, to } = editor.state.selection
const { from } = editor.state.selection
const { tr } = editor.state
const { schema } = editor.state
@@ -313,17 +313,21 @@ export const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorPro
return
}
// Replace selection with the chart node
const transaction = tr.replaceWith(from, to, chartNode)
// Insert the chart node at current position
const transaction = tr.insert(from, chartNode)
// Get the position after the inserted chart
const insertPos = transaction.mapping.map(from)
const chartSize = chartNode.nodeSize
// Add a paragraph after for continued typing
const paragraph = schema.nodes.paragraph.create()
transaction.insert(transaction.mapping.map(to).map(to), paragraph)
transaction.insert(insertPos + chartSize, paragraph)
editor.view.dispatch(transaction)
editor.chain().focus().run()
console.log('[handleSelectChart] Chart inserted')
console.log('[handleSelectChart] Chart inserted successfully')
} catch (error) {
console.error('[handleSelectChart] Failed:', error)
toast.error('Failed to insert chart: ' + (error as Error).message)

View File

@@ -26,12 +26,55 @@ export interface SuggestChartsRequest {
noteId?: string
}
// Simple in-memory cache with 5-minute TTL
const CACHE_TTL = 5 * 60 * 1000 // 5 minutes
const cache = new Map<string, { data: SuggestChartsResponse; timestamp: number }>()
function getCacheKey(content: string, selection: string | null | undefined): string {
// Use content hash + selection as cache key
const textToHash = selection ? content + ':::' + selection : content
// Simple hash function
let hash = 0
for (let i = 0; i < textToHash.length; i++) {
hash = ((hash << 5) - hash) + textToHash.charCodeAt(i)
hash = hash & hash // Convert to 32bit integer
}
return String(hash)
}
function getCached(key: string): SuggestChartsResponse | null {
const entry = cache.get(key)
if (!entry) return null
if (Date.now() - entry.timestamp > CACHE_TTL) {
cache.delete(key)
return null
}
console.log('[chart-suggestion] Cache hit for key:', key)
return entry.data
}
function setCached(key: string, data: SuggestChartsResponse): void {
cache.set(key, { data, timestamp: Date.now() })
// Limit cache size to 50 entries
if (cache.size > 50) {
const firstKey = cache.keys().next().value
cache.delete(firstKey)
}
}
/**
* Call the AI chart suggestions API
* @param request - The request parameters
* @returns Promise with the chart suggestions
*/
export async function suggestCharts(request: SuggestChartsRequest): Promise<SuggestChartsResponse> {
// Check cache first
const cacheKey = getCacheKey(request.content || '', request.selection)
const cached = getCached(cacheKey)
if (cached) {
return cached
}
try {
const response = await fetch('/api/ai/suggest-charts', {
method: 'POST',
@@ -56,8 +99,14 @@ export async function suggestCharts(request: SuggestChartsRequest): Promise<Sugg
}
}
const data = await response.json()
return data as SuggestChartsResponse
const data = await response.json() as SuggestChartsResponse
// Cache successful responses
if (!data.error && !data.quotaExceeded) {
setCached(cacheKey, data)
}
return data
} catch (error) {
console.error('[suggestCharts] Network error:', error)
return {