mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
CDP/MCP: improve Stagehand compatibility for semantic tree
This commit is contained in:
@@ -150,9 +150,9 @@ fn dump(self: Self, node: *Node, jw: *std.json.Stringify, parent_xpath: []const
|
|||||||
try jw.beginObject();
|
try jw.beginObject();
|
||||||
|
|
||||||
try jw.objectField("nodeId");
|
try jw.objectField("nodeId");
|
||||||
try jw.write(cdp_node.id);
|
try jw.write(try std.fmt.allocPrint(self.arena, "{d}", .{cdp_node.id}));
|
||||||
|
|
||||||
try jw.objectField("backendNodeId");
|
try jw.objectField("backendDOMNodeId");
|
||||||
try jw.write(cdp_node.id);
|
try jw.write(cdp_node.id);
|
||||||
|
|
||||||
try jw.objectField("nodeName");
|
try jw.objectField("nodeName");
|
||||||
@@ -171,6 +171,26 @@ fn dump(self: Self, node: *Node, jw: *std.json.Stringify, parent_xpath: []const
|
|||||||
try jw.objectField("role");
|
try jw.objectField("role");
|
||||||
try jw.write(role);
|
try jw.write(role);
|
||||||
|
|
||||||
|
// Add accessible name (e.g. button label, aria-label, etc.)
|
||||||
|
if (try axn.getName(self.page, self.arena)) |name| {
|
||||||
|
if (name.len > 0) {
|
||||||
|
try jw.objectField("name");
|
||||||
|
try jw.write(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add value for input elements
|
||||||
|
if (el.is(Element.Html.Input)) |input| {
|
||||||
|
try jw.objectField("value");
|
||||||
|
try jw.write(input.getValue());
|
||||||
|
} else if (el.is(Element.Html.TextArea)) |textarea| {
|
||||||
|
try jw.objectField("value");
|
||||||
|
try jw.write(textarea.getValue());
|
||||||
|
} else if (el.is(Element.Html.Select)) |select| {
|
||||||
|
try jw.objectField("value");
|
||||||
|
try jw.write(select.getValue(self.page));
|
||||||
|
}
|
||||||
|
|
||||||
if (el._attributes) |attrs| {
|
if (el._attributes) |attrs| {
|
||||||
try jw.objectField("attributes");
|
try jw.objectField("attributes");
|
||||||
try jw.beginObject();
|
try jw.beginObject();
|
||||||
|
|||||||
@@ -756,6 +756,74 @@ const AXSource = enum(u8) {
|
|||||||
value, // input value
|
value, // input value
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn getName(self: AXNode, page: *Page, allocator: std.mem.Allocator) !?[]const u8 {
|
||||||
|
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||||
|
defer aw.deinit();
|
||||||
|
|
||||||
|
// We need to bypass the strict JSON writer used in writeName
|
||||||
|
// We'll create a dummy writer that just writes to our buffer
|
||||||
|
const DummyWriter = struct {
|
||||||
|
aw: *std.Io.Writer.Allocating,
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
|
||||||
|
pub fn write(w: @This(), val: anytype) !void {
|
||||||
|
const T = @TypeOf(val);
|
||||||
|
if (T == []const u8 or T == [:0]const u8 or T == *const [val.len]u8) {
|
||||||
|
try w.aw.writer.writeAll(val);
|
||||||
|
} else if (comptime std.meta.hasMethod(T, "format")) {
|
||||||
|
try std.fmt.format(w.aw.writer, "{s}", .{val});
|
||||||
|
} else {
|
||||||
|
// Ignore unexpected types to avoid garbage output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn beginWriteRaw(w: @This()) !void {
|
||||||
|
_ = w;
|
||||||
|
}
|
||||||
|
pub fn endWriteRaw(w: @This()) void {
|
||||||
|
_ = w;
|
||||||
|
}
|
||||||
|
pub fn writeByte(w: @This(), b: u8) !void {
|
||||||
|
try w.aw.writer.writeByte(b);
|
||||||
|
}
|
||||||
|
pub fn writeAll(w: @This(), s: []const u8) !void {
|
||||||
|
try w.aw.writer.writeAll(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock object methods
|
||||||
|
pub fn objectField(w: @This(), name: []const u8) !void {
|
||||||
|
_ = w;
|
||||||
|
_ = name;
|
||||||
|
}
|
||||||
|
pub fn beginObject(w: @This()) !void {
|
||||||
|
_ = w;
|
||||||
|
}
|
||||||
|
pub fn endObject(w: @This()) !void {
|
||||||
|
_ = w;
|
||||||
|
}
|
||||||
|
pub fn beginArray(w: @This()) !void {
|
||||||
|
_ = w;
|
||||||
|
}
|
||||||
|
pub fn endArray(w: @This()) !void {
|
||||||
|
_ = w;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const w = DummyWriter{ .aw = &aw, .writer = &aw.writer };
|
||||||
|
|
||||||
|
const source = try self.writeName(w, page);
|
||||||
|
if (source != null) {
|
||||||
|
var str = aw.written();
|
||||||
|
// writeString manually injects literal quotes for JSON, we need to strip them
|
||||||
|
if (str.len >= 2 and str[0] == '"' and str[str.len - 1] == '"') {
|
||||||
|
str = str[1 .. str.len - 1];
|
||||||
|
}
|
||||||
|
return try allocator.dupe(u8, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource {
|
fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource {
|
||||||
const node = axnode.dom;
|
const node = axnode.dom;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user