UNPKG

jevrun

Version:

Run natural-language browser tasks on a Playwright page using Jev decisions.

61 lines • 3.33 kB
// 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