fix: prevent browser crash during long streaming sessions (#262)

- Debounce streaming diagram updates (150ms) to reduce handleDisplayChart calls by 93%
- Debounce localStorage writes (1s) to prevent blocking main thread
- Limit diagramHistory to 20 entries to prevent unbounded memory growth
- Clean up debounce timeout on component unmount to prevent memory leaks
- Add console timing markers for performance profiling

Fixes #78
This commit is contained in:
Dayuan Jiang
2025-12-14 21:23:14 +09:00
committed by GitHub
parent 55821301dd
commit 78a77e102d
4 changed files with 205 additions and 23 deletions

View File

@@ -86,16 +86,20 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
chart: string,
skipValidation?: boolean,
): string | null => {
console.time("perf:loadDiagram")
let xmlToLoad = chart
// Validate XML structure before loading (unless skipped for internal use)
if (!skipValidation) {
console.time("perf:loadDiagram-validation")
const validation = validateAndFixXml(chart)
console.timeEnd("perf:loadDiagram-validation")
if (!validation.valid) {
console.warn(
"[loadDiagram] Validation error:",
validation.error,
)
console.timeEnd("perf:loadDiagram")
return validation.error
}
// Use fixed XML if auto-fix was applied
@@ -112,11 +116,14 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
setChartXML(xmlToLoad)
if (drawioRef.current) {
console.time("perf:drawio-iframe-load")
drawioRef.current.load({
xml: xmlToLoad,
})
console.timeEnd("perf:drawio-iframe-load")
}
console.timeEnd("perf:loadDiagram")
return null
}
@@ -138,14 +145,20 @@ export function DiagramProvider({ children }: { children: React.ReactNode }) {
setLatestSvg(data.data)
// Only add to history if this was a user-initiated export
// Limit to 20 entries to prevent memory leaks during long sessions
const MAX_HISTORY_SIZE = 20
if (expectHistoryExportRef.current) {
setDiagramHistory((prev) => [
...prev,
{
svg: data.data,
xml: extractedXML,
},
])
setDiagramHistory((prev) => {
const newHistory = [
...prev,
{
svg: data.data,
xml: extractedXML,
},
]
// Keep only the last MAX_HISTORY_SIZE entries (circular buffer)
return newHistory.slice(-MAX_HISTORY_SIZE)
})
expectHistoryExportRef.current = false
}