// =============================================================================
// Text Extraction
// =============================================================================
///|
fn get_text_content(node : @aom.AccessibilityNode) -> String {
// Use aggregated text if available
match node.text {
Some(text) => {
if should_defer_to_children_text(node, text) {
let buf = StringBuilder::new()
for child in node.children {
collect_text(child, buf)
}
let collected = buf.to_string()
if collected.length() > text.length() {
return collected
}
}
text
}
None => {
// Otherwise collect from children
let buf = StringBuilder::new()
collect_text(node, buf)
buf.to_string()
}
}
}
///|
fn should_defer_to_children_text(
node : @aom.AccessibilityNode,
text : String,
) -> Bool {
if node.children.length() == 0 {
return false
}
let text_len = text.length()
if text_len == 0 {
return true
}
if text_len > 800 {
return false
}
let share_penalty = calculate_share_penalty(text)
let nav_penalty = calculate_navigation_penalty(node, text)
let caption_penalty = calculate_caption_penalty(text)
if share_penalty <= 0.4 || nav_penalty <= 0.4 || caption_penalty <= 0.4 {
return true
}
false
}
///|
fn collect_text(node : @aom.AccessibilityNode, buf : StringBuilder) -> Unit {
if is_navigation(node) {
return
}
match node.text {
Some(text) if !text.is_empty() => {
if should_skip_navigation_text(node, text) {
return
}
buf.write_string(text)
buf.write_string(" ")
return
}
_ => ()
}
match node.name {
Some(name) if !name.is_empty() => {
if should_skip_navigation_text(node, name) {
return
}
buf.write_string(name)
buf.write_string(" ")
}
_ => ()
}
for child in node.children {
collect_text(child, buf)
}
}
///|
fn navigation_label_patterns() -> FixedArray[String] {
[
"previous post", "next post", "older", "newer", "read more", "continue reading",
"next", "previous", "newsletter", "subscribe", "sign in", "sign-in", "signin",
"login", "privacy", "terms", "policy", "cookie", "share", "facebook", "twitter",
"whatsapp", "pinterest", "linkedin", "instagram", "telegram", "email", "compartilhar",
"compartilhe", "enviar", "pint", "baixar", "download", "filed under", "comments",
"comment", "replies", "reply", "vocĂȘ pode gostar", "voce pode gostar",
]
}
///|
fn should_skip_navigation_text(
node : @aom.AccessibilityNode,
text : String,
) -> Bool {
let trimmed = text.trim()
if trimmed.is_empty() {
return false
}
if trimmed.length() > 40 {
return false
}
let lower = trimmed.to_lower().to_owned()
if !contains_any(lower, navigation_label_patterns()) {
return false
}
match node.role {
@aom.Link
| @aom.ListItem
| @aom.List
| @aom.Button
| @aom.Generic
| @aom.Group => true
_ => false
}
}