- Add AG Native DOM path: #conversation + .leading-relaxed.select-text - Keep Cascade path: [data-testid=conversation-view] + [data-step-index] - Register #632 in known-issues.md (SDK+DOM both blocked for AG Native) - Bump version 0.5.50 → 0.5.51 - Add DOM analysis helper scripts
161 lines
5.2 KiB
Python
161 lines
5.2 KiB
Python
"""Analyze AG Native DOM structure to find AI response containers."""
|
|
import json, os, sys
|
|
|
|
def load_dump():
|
|
bridge = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity', 'bridge')
|
|
# Try deep-inspect result first, then dump_html
|
|
for fname in ['deep-inspect-result.json', 'dump_html.json']:
|
|
fpath = os.path.join(bridge, fname)
|
|
if os.path.exists(fpath):
|
|
print(f"Loading: {fname} ({os.path.getsize(fpath)} bytes)")
|
|
with open(fpath, 'r', encoding='utf-8-sig') as f:
|
|
return json.load(f), fname
|
|
return None, None
|
|
|
|
def find_text_containers(node, path="", depth=0, results=None):
|
|
"""Recursively find nodes with substantial text content (potential AI response containers)."""
|
|
if results is None:
|
|
results = []
|
|
if not isinstance(node, dict):
|
|
return results
|
|
|
|
tag = node.get('tag', '')
|
|
cls = node.get('cls', '')
|
|
text = node.get('text', '')
|
|
attrs = node.get('attrs', {})
|
|
children = node.get('children', [])
|
|
|
|
cur_path = f"{path}/{tag}"
|
|
if cls:
|
|
short_cls = cls[:60]
|
|
cur_path += f".{short_cls}"
|
|
|
|
# Look for nodes with long text (potential AI responses)
|
|
if text and len(text) > 50:
|
|
results.append({
|
|
'path': cur_path,
|
|
'depth': depth,
|
|
'tag': tag,
|
|
'cls': cls[:100],
|
|
'text_len': len(text),
|
|
'text_preview': text[:120],
|
|
'attrs': {k:v for k,v in attrs.items() if k not in ('style',)}
|
|
})
|
|
|
|
for child in children:
|
|
find_text_containers(child, cur_path, depth+1, results)
|
|
|
|
return results
|
|
|
|
def find_by_class_pattern(node, patterns, path="", depth=0, results=None):
|
|
"""Find nodes matching class patterns."""
|
|
if results is None:
|
|
results = []
|
|
if not isinstance(node, dict):
|
|
return results
|
|
|
|
tag = node.get('tag', '')
|
|
cls = node.get('cls', '')
|
|
attrs = node.get('attrs', {})
|
|
children = node.get('children', [])
|
|
text = node.get('text', '')
|
|
|
|
cur_path = f"{path}/{tag}"
|
|
|
|
for pattern in patterns:
|
|
if pattern.lower() in cls.lower() or pattern.lower() in str(attrs).lower():
|
|
child_count = len(children)
|
|
results.append({
|
|
'path': cur_path,
|
|
'depth': depth,
|
|
'tag': tag,
|
|
'cls': cls[:150],
|
|
'pattern': pattern,
|
|
'text_preview': text[:80] if text else '',
|
|
'child_count': child_count,
|
|
'attrs': {k:v[:50] for k,v in attrs.items() if k != 'style'}
|
|
})
|
|
|
|
for child in children:
|
|
find_by_class_pattern(child, patterns, cur_path, depth+1, results)
|
|
|
|
return results
|
|
|
|
def analyze_chat_structure(node, path="", depth=0):
|
|
"""Find the chat/conversation area by looking at the main layout."""
|
|
if not isinstance(node, dict):
|
|
return
|
|
tag = node.get('tag', '')
|
|
cls = node.get('cls', '')
|
|
children = node.get('children', [])
|
|
text = node.get('text', '')
|
|
attrs = node.get('attrs', {})
|
|
|
|
# Print interesting structural nodes at shallow depths
|
|
if depth <= 6:
|
|
child_count = len(children)
|
|
has_text = bool(text and len(text) > 10)
|
|
info = f"{' '*depth}{tag}"
|
|
if cls:
|
|
info += f" .{cls[:80]}"
|
|
if attrs:
|
|
attr_str = ' '.join(f'{k}={v[:30]}' for k,v in attrs.items() if k not in ('style','class'))
|
|
if attr_str:
|
|
info += f" [{attr_str}]"
|
|
info += f" children={child_count}"
|
|
if has_text:
|
|
info += f" text=\"{text[:50]}...\""
|
|
print(info)
|
|
|
|
for child in children:
|
|
analyze_chat_structure(child, f"{path}/{tag}", depth+1)
|
|
|
|
data, fname = load_dump()
|
|
if not data:
|
|
print("No dump file found!")
|
|
sys.exit(1)
|
|
|
|
# Handle both dump formats
|
|
body = data.get('body', data)
|
|
qi = data.get('quickInfo', {})
|
|
|
|
print("=" * 60)
|
|
print("QUICK INFO")
|
|
print("=" * 60)
|
|
if qi:
|
|
for k, v in qi.items():
|
|
if k == 'buttons':
|
|
print(f"buttons ({len(v)}):")
|
|
for b in v[:15]:
|
|
print(f" [{b.get('tag')}] \"{b.get('text','')[:50]}\" visible={b.get('visible')} cls={b.get('cls','')[:60]}")
|
|
elif k == 'dataAttrs':
|
|
print(f"dataAttrs: {v[:30]}")
|
|
else:
|
|
print(f"{k}: {v}")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("CHAT-RELATED CLASS PATTERNS")
|
|
print("=" * 60)
|
|
patterns = ['chat', 'message', 'conversation', 'response', 'answer', 'reply',
|
|
'markdown', 'prose', 'content', 'panel', 'agent', 'assistant',
|
|
'planner', 'step', 'trajectory', 'bot', 'ai-', 'turn']
|
|
matches = find_by_class_pattern(body, patterns)
|
|
for m in matches:
|
|
print(f" [{m['tag']}] cls=\"{m['cls']}\" pattern={m['pattern']} children={m['child_count']} {m.get('attrs',{})}")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("LONG TEXT NODES (potential AI responses)")
|
|
print("=" * 60)
|
|
texts = find_text_containers(body)
|
|
texts.sort(key=lambda x: x['text_len'], reverse=True)
|
|
for t in texts[:20]:
|
|
print(f" [{t['tag']}] depth={t['depth']} len={t['text_len']} cls=\"{t['cls'][:60]}\"")
|
|
print(f" text: \"{t['text_preview']}\"")
|
|
if t['attrs']:
|
|
print(f" attrs: {t['attrs']}")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("DOM TREE (depth<=6)")
|
|
print("=" * 60)
|
|
analyze_chat_structure(body)
|