fix: use chart.applyOptions() for theme changes instead of re-creating chart

Remove resolvedTheme from the chart initialization useEffect dependency
array so theme changes no longer destroy and re-create the entire chart.
Add a separate useEffect that calls chartRef.current.applyOptions() with
the new layout, grid, timeScale and rightPriceScale colors whenever
resolvedTheme changes, avoiding the memory leak and flicker caused by
full chart reconstruction on every theme toggle.

Closes task 8.2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marko Djordjevic 2026-02-18 15:19:44 +01:00
parent 0cd7a34c99
commit 131d2912d5
2 changed files with 14 additions and 2 deletions

View file

@ -71,7 +71,7 @@
## 8. Frontend — Memory Leaks & Performance
- [x] 8.1 `[opus]` Fix SpanAnnotationManager preview primitive memory leak: replace `useState` with `useRef` for preview primitive, add cleanup on unmount (`src/components/SpanAnnotationManager.tsx:265-324`)
- [ ] 8.2 `[sonnet]` Use `chart.applyOptions()` for theme changes instead of re-creating chart (`src/components/CandleChart.tsx:333`)
- [x] 8.2 `[sonnet]` Use `chart.applyOptions()` for theme changes instead of re-creating chart (`src/components/CandleChart.tsx:333`)
- [ ] 8.3 `[sonnet]` Remove `fitContent()` from reconciliation effect, only call on initial load (`src/components/SpanAnnotationManager.tsx:160`)
- [ ] 8.4 `[opus]` Implement incremental primitive updates in SpanAnnotationManager: update only selection state on selection change, full reconciliation only on annotation list changes (`src/components/SpanAnnotationManager.tsx:104-161`)
- [ ] 8.5 `[sonnet]` Add bounded prediction cache (max 100 entries, FIFO eviction) to `predictionCacheRef` (`src/app/page.tsx:195-199`)

View file

@ -345,7 +345,19 @@ const CandleChart = forwardRef<CandleChartHandle, CandleChartProps>(
histogramSeriesRef.current = null;
chart.remove();
};
}, [isEmpty, mounted, resolvedTheme]);
}, [isEmpty, mounted]);
// Apply theme changes without re-creating the chart
useEffect(() => {
if (!chartRef.current) return;
const colors = getChartColors();
chartRef.current.applyOptions({
layout: colors.layout,
grid: colors.grid,
timeScale: { borderColor: colors.timeScale.borderColor },
rightPriceScale: { borderColor: colors.rightPriceScale.borderColor },
});
}, [resolvedTheme]);
// Load candle data into chart
useEffect(() => {