jevrun
Version:
Run natural-language browser tasks on a Playwright page using Jev decisions.
61 lines • 3.33 kB
JavaScript
// Values are supplied by the user; Jev chooses them and never generates strings.
export function literals(prompt) {
return [...new Set([...prompt.matchAll(/"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|“([^”]*)”/g)]
.map((match) => (match[1] ?? match[2] ?? match[3] ?? "").replace(/\\([\\"'])/g, "$1")))];
}
export function targets(snapshot) {
const result = {};
for (const line of snapshot.split("\n")) {
const match = line.match(/^\s*-\s+.*?\[ref=(e\d+)\]/);
if (match?.[1] && !line.includes("[disabled]"))
result[match[1]] = line.trim();
}
if (Object.keys(result).length > 253)
throw new Error("Page has too many element choices (maximum 253).");
return result;
}
export async function actionsFor(handle, values, timeout) {
const actions = [];
if (!await handle.isVisible() || !await handle.isEnabled())
throw new Error("Selected element is no longer actionable.");
const info = await handle.evaluate((element) => {
const input = element instanceof HTMLInputElement;
const select = element instanceof HTMLSelectElement;
const labels = "labels" in element ? Array.from(element.labels ?? []).map((label) => label.textContent).join(" ") : "";
return {
name: JSON.stringify({ tag: element.tagName.toLowerCase(), type: input ? element.type : undefined,
label: element.getAttribute("aria-label") || labels, text: element.textContent?.slice(0, 250),
placeholder: element.getAttribute("placeholder"), href: element.getAttribute("href"),
value: input && element.type !== "password" ? element.value : undefined }),
checkbox: input && element.type === "checkbox",
radio: input && element.type === "radio",
editable: (input && ["text", "email", "password", "search", "tel", "url", "number", "date", "time", "datetime-local", "month", "week"].includes(element.type))
|| element instanceof HTMLTextAreaElement || (element instanceof HTMLElement && element.isContentEditable),
options: select ? Array.from(element.options).filter((option) => !option.disabled).map((option) => ({ value: option.value, label: option.label })) : null,
};
});
const add = (description, execute) => {
actions.push({ description: `${description} on ${info.name}`, execute: async () => { await execute(); } });
};
if (info.checkbox || info.radio) {
add("Check", () => handle.check({ timeout }));
if (info.checkbox)
add("Uncheck", () => handle.uncheck({ timeout }));
}
else if (info.options) {
for (const option of info.options)
add(`Select ${JSON.stringify(option.label)}`, () => handle.selectOption(option.value, { timeout }));
}
else if (info.editable && await handle.isEditable()) {
for (const value of values)
add(`Fill with ${JSON.stringify(value)}`, () => handle.fill(value, { timeout }));
add("Press Enter", () => handle.press("Enter", { timeout }));
}
else {
add("Click", () => handle.click({ timeout }));
}
if (actions.length > 254)
throw new Error("Too many action choices for this element.");
return actions;
}
//# sourceMappingURL=actions.js.map