---
name: render_ax_outline
kind: function
lang: py
domain: core
version: "1.0.0"
purity: pure
signature: "def render_ax_outline(nodes: list[dict], max_chars: int = 0) -> str"
description: "Convierte nodos AX tree CDP en un outline indentado jerárquico y legible. Nodos accionables (button, link, textbox, etc.) llevan #ref=nodeId para que el LLM pueda referenciarlos en acciones. Poda nodos ignored y roles sin valor semántico."
tags: [browser, cdp, ax-tree, perception, navegator, pure, llm]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: nodes
    desc: "Lista de AXNode en formato CDP (campos: nodeId, role, name, childIds, parentId, ignored). Devuelto por cdp_get_ax_tree. Pasar trim_ax_tree(nodes) antes para reducir ruido."
  - name: max_chars
    desc: "Si > 0, trunca la salida a ese número de caracteres y añade '…[outline truncado]'. 0 = sin límite (default)."
output: "String multi-línea con el outline indentado. Nodos accionables llevan '  #ref=nodeId' alineado a columna 60. Vacío si nodes está vacío o todos los nodos son ignorados."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/core/render_ax_outline.py"
---

## Ejemplo

```python
from core.render_ax_outline import render_ax_outline

# Nodos de muestra (formato real CDP simplificado)
nodes = [
    {"nodeId": "1", "role": {"value": "RootWebArea"}, "name": {"value": "Gmail"},
     "childIds": ["2", "3"], "ignored": False},
    {"nodeId": "2", "role": {"value": "navigation"}, "name": {"value": ""},
     "childIds": ["4", "5"], "ignored": False},
    {"nodeId": "3", "role": {"value": "main"}, "name": {"value": ""},
     "childIds": ["6"], "ignored": False},
    {"nodeId": "4", "role": {"value": "button"}, "name": {"value": "Redactar"},
     "childIds": [], "ignored": False},
    {"nodeId": "5", "role": {"value": "link"}, "name": {"value": "Recibidos (3)"},
     "childIds": [], "ignored": False},
    {"nodeId": "6", "role": {"value": "textbox"}, "name": {"value": "Buscar correo"},
     "childIds": [], "ignored": False},
]

outline = render_ax_outline(nodes)
print(outline)
# RootWebArea "Gmail"
#   navigation
#     button "Redactar"                              #ref=4
#     link "Recibidos (3)"                           #ref=5
#   main
#     textbox "Buscar correo"                        #ref=6

# Con límite de caracteres para contexto comprimido:
outline_short = render_ax_outline(nodes, max_chars=100)
```

## Cuando usarla

Después de obtener el AX tree con `cdp_get_ax_tree` (y opcionalmente podarlo con `trim_ax_tree`), cuando necesitas dar al LLM una vista compacta de la página para que decida qué elemento accionar. El outline con `#ref` permite al LLM responder "haz clic en #ref=4" sin ambigüedad. Úsala directamente o como parte del pipeline `cdp_perceive_outline_py_pipelines`.

## Gotchas

- Esta función es pura: no llama a Chrome ni tiene I/O. Solo transforma la lista de nodos → string.
- Pasar los nodos crudos de `cdp_get_ax_tree` funciona, pero el outline será más verboso. Usar `trim_ax_tree` antes reduce el ruido considerablemente.
- Nodos con `ignored: true` se saltan silenciosamente (no aparecen en el outline).
- Roles sin valor semántico (`none`, `presentation`) también se saltan; sus hijos se renderizan un nivel arriba.
- Si `max_chars` corta a mitad de un nodo accionable importante, el LLM no verá su `#ref`. Para páginas grandes usar `cdp_perceive_outline` con `max_chars=20000` o chunking via `chunk_ax_tree`.
