From 3f37e6c7716e496495f04bb64b9342e81e161440 Mon Sep 17 00:00:00 2001 From: xiaolai Date: Sun, 18 Oct 2026 15:23:40 +0800 Subject: [PATCH] feat(macos): Mark link text in attributed strings Text inside a Link node now carries NSAccessibilityLinkTextAttribute (AXLink) in accessibilityAttributedStringForRange:, whose value is the link's own platform element. This is how AppKit text views expose hypertext, and it lets an assistive technology announce and reach an inline link while reading the surrounding text. --- adapters/macos/src/node.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/adapters/macos/src/node.rs b/adapters/macos/src/node.rs index 7be2e59b6..929f11ee2 100644 --- a/adapters/macos/src/node.rs +++ b/adapters/macos/src/node.rs @@ -853,14 +853,31 @@ declare_class!( #[method_id(accessibilityAttributedStringForRange:)] fn attributed_string_for_range(&self, range: NSRange) -> Option> { - self.resolve(|node| { + self.resolve_with_context(|node, _, context| { if node.supports_text_ranges() { if let Some(range) = from_ns_range(node, range) { + let root = node.id(); let mut result = NSMutableAttributedString::new(); unsafe { result.beginEditing() }; range.traverse_text::<_, ()>(|node, text| { let ns_text = NSString::from_str(text); let mut attrs = NSMutableDictionary::new(); + // Hypertext: text inside a link carries + // NSAccessibilityLinkTextAttribute, whose value + // is the link's own element. + let mut ancestor = node.parent(); + while let Some(a) = ancestor.filter(|a| a.id() != root) { + if a.role() == Role::Link { + attrs.insert_id( + unsafe { NSAccessibilityLinkTextAttribute }, + Id::into_super(Id::into_super(Id::into_super( + context.get_or_create_platform_node(a.id()), + ))), + ); + break; + } + ancestor = a.parent(); + } if let Some(color) = node.background_color() { attrs.insert_id( unsafe { NSAccessibilityBackgroundColorTextAttribute },