capNodes function

List<CachedNode> capNodes(
  1. List<CachedNode> nodes,
  2. int maxNodes, {
  3. Set<String> live = const {},
})

Trim a node set to maxNodes (0 = no cap), preserving nodes' order.

A node in live (the current capture) is never evicted: it is on screen and the agent is about to be asked about it. Evicting it meant the next capture re-added it as new, re-dirtied the file and re-uploaded it, on every capture of a large feed (Android's ScreenMerge has the same rule). Victims come from the rest, fewest sightings first, then least recently seen — recurring STRUCTURE outlives one-off DATA rows.

If the capture alone exceeds maxNodes, the first maxNodes live nodes in nodes' order are kept — already-cached ones first, so an identical capture keeps exactly the same set.

Implementation

List<CachedNode> capNodes(
  List<CachedNode> nodes,
  int maxNodes, {
  Set<String> live = const {},
}) {
  if (maxNodes <= 0 || nodes.length <= maxNodes) return nodes;
  final keep = <Object?>{};
  for (final n in nodes) {
    if (keep.length >= maxNodes) break;
    if (live.contains(n['id'])) keep.add(n['id']);
  }
  final room = maxNodes - keep.length;
  if (room > 0) {
    final rest = [
      for (final n in nodes)
        if (!live.contains(n['id'])) n
    ]..sort((a, b) {
        final ac = a['_seenCount'];
        final bc = b['_seenCount'];
        final byCount = (bc is int ? bc : 0).compareTo(ac is int ? ac : 0);
        if (byCount != 0) return byCount;
        final byRecency =
            '${b['_lastSeen'] ?? ''}'.compareTo('${a['_lastSeen'] ?? ''}');
        if (byRecency != 0) return byRecency;
        return '${a['id']}'.compareTo('${b['id']}');
      });
    for (final n in rest.take(room)) {
      keep.add(n['id']);
    }
  }
  return [
    for (final n in nodes)
      if (keep.contains(n['id'])) n
  ];
}