Skip to content

Commit 8034a29

Browse files
committed
review changes
1 parent 0e5f914 commit 8034a29

1 file changed

Lines changed: 39 additions & 18 deletions

File tree

src/tools/input.ts

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import type {ElementHandle} from 'puppeteer-core';
88
import z from 'zod';
99

10-
import type {McpContext} from '../McpContext.js';
10+
import type {McpContext, TextSnapshotNode} from '../McpContext.js';
1111

1212
import {ToolCategories} from './categories.js';
1313
import {defineTool} from './ToolDefinition.js';
@@ -80,32 +80,53 @@ export const hover = defineTool({
8080
},
8181
});
8282

83-
async function fillFormElement(
84-
uid: string,
83+
// The AXNode for an option doesn't contain its `value`. We set text content of the option as value.
84+
// If the form is a combobox, we need to find the correct option by its text value.
85+
// To do that, loop through the children while checking which child's text matches the requested value (requested value is actually the text content).
86+
// When the correct option is found, use the element handle to get the real value.
87+
async function selectOption(
88+
handle: ElementHandle,
89+
aXNode: TextSnapshotNode,
8590
value: string,
86-
context: McpContext,
8791
) {
88-
const handle = await context.getElementByUid(uid);
89-
try {
90-
// The AXNode for an option doesn't contain its `value`. We set text content of the option as value.
91-
// If the form is a combobox, we need to find the correct option by its text value.
92-
// To do that, loop through the children while checking which child's text matches the requested value (requested value is actually the text content).
93-
// When the correct option is found, use the element handle to get the real value.
94-
const aXNode = context.getAXNodeByUid(uid);
95-
if (aXNode && aXNode.role === 'combobox' && aXNode.children) {
96-
for (const child of aXNode.children) {
97-
if (child.role === 'option' && child.name === value && child.value) {
98-
const childHandle = await child.elementHandle();
99-
if (childHandle) {
100-
const childValueHandle = await childHandle.getProperty('value');
92+
let optionFound = false;
93+
for (const child of aXNode.children) {
94+
if (child.role === 'option' && child.name === value && child.value) {
95+
optionFound = true;
96+
const childHandle = await child.elementHandle();
97+
if (childHandle) {
98+
try {
99+
const childValueHandle = await childHandle.getProperty('value');
100+
try {
101101
const childValue = await childValueHandle.jsonValue();
102102
if (childValue) {
103103
await handle.asLocator().fill(childValue.toString());
104104
}
105-
break;
105+
} finally {
106+
void childValueHandle.dispose();
106107
}
108+
break;
109+
} finally {
110+
void childHandle.dispose();
107111
}
108112
}
113+
}
114+
}
115+
if (!optionFound) {
116+
throw new Error(`Could not find option with text "${value}"`);
117+
}
118+
}
119+
120+
async function fillFormElement(
121+
uid: string,
122+
value: string,
123+
context: McpContext,
124+
) {
125+
const handle = await context.getElementByUid(uid);
126+
try {
127+
const aXNode = context.getAXNodeByUid(uid);
128+
if (aXNode && aXNode.role === 'combobox') {
129+
await selectOption(handle, aXNode, value);
109130
} else {
110131
await handle.asLocator().fill(value);
111132
}

0 commit comments

Comments
 (0)