register static method

String register({
  1. required Rect rect,
  2. required Element element,
  3. required String groupId,
  4. required bool isTextField,
  5. SemanticsNode? node,
  6. RenderObject? renderObject,
})

Mints (or returns the cached) token for the supplied tree references.

When node is supplied AND a token has already been issued for the same node.id, the same token is returned and its underlying entry is refreshed in place (so action tools always see the latest element / rect for that node, AND the entry's current groupId becomes the supplied groupId — the latest snapshot owns the disposal lifecycle).

When node is null (e.g. the find_by_text walk does not have a SemanticsNode handle), a fresh token is always minted.

Tokens look like e1, e2, … to mirror Playwright MCP's [ref=e<N>] shape.

Implementation

static String register({
  required Rect rect,
  required Element element,
  required String groupId,
  required bool isTextField,
  SemanticsNode? node,
  RenderObject? renderObject,
}) {
  // 1. Cache hit — only when a SemanticsNode is supplied. Find an
  //    existing token for node.id and refresh its payload so downstream
  //    action tools see the latest element/rect/renderObject. The
  //    entry's groupId is updated to the LATEST caller's groupId so
  //    older snapshots' disposeGroup calls never strand a still-live
  //    token.
  if (node != null) {
    final String? existing = _byNodeId[node.id];
    if (existing != null) {
      _entries[existing] = RefEntry(
        rect: rect,
        element: element,
        groupId: groupId,
        isTextField: isTextField,
        node: node,
        renderObject: renderObject,
      );
      return existing;
    }

    // 2. Mint a new token, store the entry, and remember the node.id
    //    mapping for future cache hits.
    _counter += 1;
    final String token = 'e$_counter';
    _entries[token] = RefEntry(
      rect: rect,
      element: element,
      groupId: groupId,
      isTextField: isTextField,
      node: node,
      renderObject: renderObject,
    );
    _byNodeId[node.id] = token;
    return token;
  }

  // 3. No SemanticsNode → mint fresh every call. The entry still
  //    carries the supplied groupId so disposeGroup cleans it up.
  _counter += 1;
  final String token = 'e$_counter';
  _entries[token] = RefEntry(
    rect: rect,
    element: element,
    groupId: groupId,
    isTextField: isTextField,
    node: null,
    renderObject: renderObject,
  );
  return token;
}