using System.Linq;
namespace AxCopilot.Views;
public partial class ChatWindow
{
/// 최대 캐시 보유 수. 화면 표시 항목 + 여유분.
private const int TranscriptCacheRetentionCount = 120;
private void PruneTranscriptElementCache(IReadOnlyCollection visibleKeys)
{
// 캐시가 보유 한도의 1.5배 이상일 때만 정리 (빈번한 정리 방지)
if (_elementCache.Count <= TranscriptCacheRetentionCount * 3 / 2)
return;
var keep = new HashSet(visibleKeys, StringComparer.Ordinal);
// 최근 렌더링된 키 중 보유 한도만큼만 유지
var retainFromRendered = Math.Min(TranscriptCacheRetentionCount, _lastRenderedTimelineKeys.Count);
foreach (var key in _lastRenderedTimelineKeys.TakeLast(retainFromRendered))
keep.Add(key);
var removable = _elementCache.Keys
.Where(key => !keep.Contains(key))
.ToList();
foreach (var key in removable)
_elementCache.Remove(key);
// _transcriptElementMap도 동기 정리 — 더 이상 transcript에 없는 element 참조 제거
PruneTranscriptElementMap();
}
///
/// _transcriptElementMap에서 현재 _transcriptElements에 없는 항목을 제거합니다.
/// ObservableCollection 변경 시 자동으로 정리되지 않는 stale 참조를 회수합니다.
///
private void PruneTranscriptElementMap()
{
if (_transcriptElementMap.Count <= _transcriptElements.Count * 2)
return; // 맵이 실제 요소의 2배 미만이면 정리 불필요
var activeElements = new HashSet(
_transcriptElements
.Where(item => item.IsMaterialized && item.Element != null)
.Select(item => item.Element!));
var staleKeys = _transcriptElementMap.Keys
.Where(key => !activeElements.Contains(key))
.ToList();
foreach (var key in staleKeys)
_transcriptElementMap.Remove(key);
}
}