@wenathlan/extension 1.1.37 → 1.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/index.js +440 -8
- package/dist/index.js.map +2 -2
- package/dist/memory.d.ts +83 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +25 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +46 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +209 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1540 -14
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +199 -2
- package/extension/dist/pagebridge.js.map +4 -4
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +41 -2
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +340 -2
- package/extension/dist/sidepanel.js.map +4 -4
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
|
|
4
4
|
"name": "Devthink",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.39",
|
|
6
6
|
"description": "A consent-first bridge for reviewed browser-agent tasks.",
|
|
7
7
|
"permissions": [
|
|
8
8
|
"activeTab",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
3
|
// policy.ts
|
|
4
|
-
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword"]);
|
|
4
|
+
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts"]);
|
|
5
5
|
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
|
|
6
|
-
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha"]);
|
|
6
|
+
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures"]);
|
|
7
7
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
8
8
|
function parseoptions(step) {
|
|
9
9
|
if (step.options === void 0) return {};
|
|
@@ -2934,6 +2934,196 @@
|
|
|
2934
2934
|
}
|
|
2935
2935
|
}
|
|
2936
2936
|
|
|
2937
|
+
// extension/pagedata.ts
|
|
2938
|
+
function normalizeheader(label) {
|
|
2939
|
+
const slug = label.trim().toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "");
|
|
2940
|
+
return slug || "column";
|
|
2941
|
+
}
|
|
2942
|
+
function columnspecof(label, used = /* @__PURE__ */ new Set()) {
|
|
2943
|
+
const base = normalizeheader(label);
|
|
2944
|
+
let key = base;
|
|
2945
|
+
let suffix = 2;
|
|
2946
|
+
while (used.has(key)) {
|
|
2947
|
+
key = `${base}${suffix}`;
|
|
2948
|
+
suffix += 1;
|
|
2949
|
+
}
|
|
2950
|
+
used.add(key);
|
|
2951
|
+
return { key, label: label.trim(), kind: "text", normalized: label.trim().toLowerCase() };
|
|
2952
|
+
}
|
|
2953
|
+
function classifycolumn(values) {
|
|
2954
|
+
const present = values.filter((value) => value.trim().length > 0);
|
|
2955
|
+
if (present.length === 0) return "text";
|
|
2956
|
+
return present.every((value) => Number.isFinite(Number(value.replace(/,/g, ".")))) ? "number" : "text";
|
|
2957
|
+
}
|
|
2958
|
+
function expandspans(rows) {
|
|
2959
|
+
const filled = [];
|
|
2960
|
+
const pending = /* @__PURE__ */ new Map();
|
|
2961
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
2962
|
+
const row = filled[index] ?? (filled[index] = []);
|
|
2963
|
+
let column = 0;
|
|
2964
|
+
for (const cell of rows[index].cells) {
|
|
2965
|
+
while (row[column] !== void 0 || pending.has(`${index},${column}`)) {
|
|
2966
|
+
if (row[column] === void 0) row[column] = pending.get(`${index},${column}`);
|
|
2967
|
+
column += 1;
|
|
2968
|
+
}
|
|
2969
|
+
row[column] = cell.text;
|
|
2970
|
+
for (let spanrow = 0; spanrow < Math.max(1, cell.rowspan); spanrow += 1) {
|
|
2971
|
+
for (let spancol = 0; spancol < Math.max(1, cell.colspan); spancol += 1) {
|
|
2972
|
+
if (spanrow === 0 && spancol === 0) continue;
|
|
2973
|
+
pending.set(`${index + spanrow},${column + spancol}`, cell.text);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
column += Math.max(1, cell.colspan);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
for (const [key, value] of pending) {
|
|
2980
|
+
const [rowpart, columnpart] = key.split(",");
|
|
2981
|
+
const rowindex = Number.parseInt(rowpart ?? "0", 10);
|
|
2982
|
+
const columnindex = Number.parseInt(columnpart ?? "0", 10);
|
|
2983
|
+
const target = filled[rowindex] ?? (filled[rowindex] = []);
|
|
2984
|
+
if (target[columnindex] === void 0) target[columnindex] = value;
|
|
2985
|
+
}
|
|
2986
|
+
const width = filled.reduce((largest, row) => Math.max(largest, row.length), 0);
|
|
2987
|
+
return filled.map((row) => Array.from({ length: width }, (_, column) => row[column] ?? ""));
|
|
2988
|
+
}
|
|
2989
|
+
function readgrid(rows) {
|
|
2990
|
+
const headerindex = rows.findIndex((row) => row.header || (row.cells[0]?.header ?? false));
|
|
2991
|
+
const useheader = headerindex !== -1 ? headerindex : 0;
|
|
2992
|
+
const hasheader = headerindex !== -1 || rows.length > 0;
|
|
2993
|
+
const grid = expandspans(rows);
|
|
2994
|
+
const headercells = hasheader ? grid[useheader] ?? [] : [];
|
|
2995
|
+
const used = /* @__PURE__ */ new Set();
|
|
2996
|
+
const columns = Array.from({ length: headercells.length || (grid[0]?.length ?? 0) }, (_, index) => columnspecof(headercells[index] ?? `column${index + 1}`, used));
|
|
2997
|
+
const bodyrows = grid.filter((_, index) => hasheader ? index !== useheader : true).filter((line) => line.some((value) => value.trim().length > 0)).map((line) => {
|
|
2998
|
+
const row = {};
|
|
2999
|
+
columns.forEach((column, index) => {
|
|
3000
|
+
row[column.key] = line[index] ?? "";
|
|
3001
|
+
});
|
|
3002
|
+
return row;
|
|
3003
|
+
});
|
|
3004
|
+
for (const column of columns) column.kind = classifycolumn(bodyrows.map((row) => row[column.key] ?? ""));
|
|
3005
|
+
const children = [];
|
|
3006
|
+
rows.forEach((row, rowindex) => {
|
|
3007
|
+
if (hasheader && rowindex === useheader) return;
|
|
3008
|
+
const parentrow = hasheader && rowindex > useheader ? rowindex - 1 : rowindex;
|
|
3009
|
+
row.cells.forEach((cell) => {
|
|
3010
|
+
if (!cell.nested) return;
|
|
3011
|
+
const child = readgrid(cell.nested.rows);
|
|
3012
|
+
children.push({ parentrow, selector: cell.nested.selector, columns: child.columns, rows: child.rows });
|
|
3013
|
+
});
|
|
3014
|
+
});
|
|
3015
|
+
return { columns, rows: bodyrows, children };
|
|
3016
|
+
}
|
|
3017
|
+
function rowsfresh(previous, current) {
|
|
3018
|
+
if (current.length === 0) return false;
|
|
3019
|
+
const known = new Set(previous.map((row) => JSON.stringify(row)));
|
|
3020
|
+
return current.some((row) => !known.has(JSON.stringify(row)));
|
|
3021
|
+
}
|
|
3022
|
+
function mergedatasets(datasets) {
|
|
3023
|
+
const columns = [];
|
|
3024
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3025
|
+
for (const dataset of datasets) {
|
|
3026
|
+
for (const column of dataset.columns) {
|
|
3027
|
+
if (seen.has(column.key)) continue;
|
|
3028
|
+
seen.add(column.key);
|
|
3029
|
+
columns.push(column);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
const rows = datasets.flatMap((dataset) => dataset.rows.map((row) => {
|
|
3033
|
+
const merged = {};
|
|
3034
|
+
for (const column of columns) merged[column.key] = row[column.key] ?? "";
|
|
3035
|
+
return merged;
|
|
3036
|
+
}));
|
|
3037
|
+
return { columns, rows };
|
|
3038
|
+
}
|
|
3039
|
+
function collectrowshapes(table) {
|
|
3040
|
+
return [...table.rows].map((row) => ({
|
|
3041
|
+
header: [...row.cells].every((cell) => cell.tagName === "TH") && row.cells.length > 0,
|
|
3042
|
+
cells: [...row.cells].map((cell) => {
|
|
3043
|
+
const nested = cell.querySelector("table");
|
|
3044
|
+
return {
|
|
3045
|
+
text: clean(nested ? `${nested.rows.length} rows` : cell.textContent ?? ""),
|
|
3046
|
+
header: cell.tagName === "TH",
|
|
3047
|
+
rowspan: cell.rowSpan,
|
|
3048
|
+
colspan: cell.colSpan,
|
|
3049
|
+
...nested instanceof HTMLTableElement ? { nested: { selector: elementselector(nested), rows: collectrowshapes(nested) } } : {}
|
|
3050
|
+
};
|
|
3051
|
+
})
|
|
3052
|
+
}));
|
|
3053
|
+
}
|
|
3054
|
+
function readtable(target, root, fallbackselector) {
|
|
3055
|
+
const table = target instanceof HTMLTableElement ? target : fallbackselector ? root.querySelector(fallbackselector) : root.querySelector("table");
|
|
3056
|
+
if (!table) return null;
|
|
3057
|
+
return readgrid(collectrowshapes(table));
|
|
3058
|
+
}
|
|
3059
|
+
async function runpagedata(step, target, root = document) {
|
|
3060
|
+
let options = {};
|
|
3061
|
+
try {
|
|
3062
|
+
options = parseoptions(step);
|
|
3063
|
+
} catch {
|
|
3064
|
+
options = {};
|
|
3065
|
+
}
|
|
3066
|
+
if (step.kind === "scrapetable") {
|
|
3067
|
+
const grid = readtable(target, root, step.target);
|
|
3068
|
+
if (!grid) return { ok: false, summary: "The reviewed table selector matches no table element." };
|
|
3069
|
+
const rowlimit = typeof options.rowlimit === "number" && Number.isInteger(options.rowlimit) && options.rowlimit > 0 ? options.rowlimit : grid.rows.length;
|
|
3070
|
+
const limited = grid.rows.slice(0, rowlimit);
|
|
3071
|
+
return {
|
|
3072
|
+
ok: true,
|
|
3073
|
+
summary: `Scraped ${limited.length} row${limited.length === 1 ? "" : "s"} into ${grid.columns.length} normalized column${grid.columns.length === 1 ? "" : "s"}${grid.children.length > 0 ? ` with ${grid.children.length} nested child table${grid.children.length === 1 ? "" : "s"}` : ""}.`,
|
|
3074
|
+
details: { grid: { columns: grid.columns, rows: limited, children: grid.children }, rows: limited.length, columns: grid.columns.length }
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
if (step.kind === "paginateextract") {
|
|
3078
|
+
const nextselector = typeof options.next === "string" ? options.next : "";
|
|
3079
|
+
const pages = typeof options.pages === "number" && Number.isInteger(options.pages) && options.pages > 0 ? options.pages : 1;
|
|
3080
|
+
const wait5 = typeof options.wait === "number" && Number.isFinite(options.wait) && options.wait > 0 ? options.wait : 0;
|
|
3081
|
+
const cursor = typeof options.cursor === "number" && Number.isInteger(options.cursor) && options.cursor > 0 ? options.cursor : 0;
|
|
3082
|
+
const grids = [];
|
|
3083
|
+
let previous = [];
|
|
3084
|
+
for (let page = 0; page < pages + cursor; page += 1) {
|
|
3085
|
+
if (page < cursor) {
|
|
3086
|
+
const control = root.querySelector(nextselector);
|
|
3087
|
+
if (!control) break;
|
|
3088
|
+
control.click();
|
|
3089
|
+
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
|
3090
|
+
continue;
|
|
3091
|
+
}
|
|
3092
|
+
if (page > cursor) {
|
|
3093
|
+
const control = root.querySelector(nextselector);
|
|
3094
|
+
if (!control) break;
|
|
3095
|
+
control.click();
|
|
3096
|
+
const deadline = Date.now() + wait5;
|
|
3097
|
+
let fresh = false;
|
|
3098
|
+
while (!fresh && Date.now() < deadline) {
|
|
3099
|
+
await new Promise((resolve) => window.setTimeout(resolve, Math.min(100, Math.max(16, deadline - Date.now()))));
|
|
3100
|
+
const probe = readtable(target, root, step.target);
|
|
3101
|
+
fresh = probe !== null && rowsfresh(previous, probe.rows);
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
const grid = readtable(target, root, step.target);
|
|
3105
|
+
if (!grid) return { ok: false, summary: "The reviewed table selector matches no table element." };
|
|
3106
|
+
grids.push(grid);
|
|
3107
|
+
previous = grid.rows;
|
|
3108
|
+
}
|
|
3109
|
+
const merged = mergedatasets(grids);
|
|
3110
|
+
const hasnext = Boolean(root.querySelector(nextselector));
|
|
3111
|
+
return {
|
|
3112
|
+
ok: grids.length > 0,
|
|
3113
|
+
summary: grids.length > 0 ? `Followed ${grids.length} page${grids.length === 1 ? "" : "s"} of the reviewed table into ${merged.rows.length} row${merged.rows.length === 1 ? "" : "s"}${hasnext ? "; a next control remains" : ""}.` : "No page of the reviewed table was extracted.",
|
|
3114
|
+
details: { grid: { columns: merged.columns, rows: merged.rows, children: grids.flatMap((grid) => grid.children) }, pages: grids.length, rows: merged.rows.length, next: hasnext }
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
return { ok: false, summary: "Unsupported data step." };
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
// extension/datacommand.ts
|
|
3121
|
+
function checksum(value) {
|
|
3122
|
+
let hash = 5381;
|
|
3123
|
+
for (let index = 0; index < value.length; index += 1) hash = (hash * 33 ^ value.charCodeAt(index)) >>> 0;
|
|
3124
|
+
return `fnv1a-${hash.toString(16)}`;
|
|
3125
|
+
}
|
|
3126
|
+
|
|
2937
3127
|
// extension/pagebridge.ts
|
|
2938
3128
|
function stepoptions3(step) {
|
|
2939
3129
|
if (!step.options) return {};
|
|
@@ -3043,6 +3233,7 @@
|
|
|
3043
3233
|
var navstepkinds = /* @__PURE__ */ new Set(["waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "stopnav", "prefetch", "preconnect", "printpdf"]);
|
|
3044
3234
|
var formkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "asksubmit", "submitform", "consentpassword", "attachfile"]);
|
|
3045
3235
|
var wizardkinds = /* @__PURE__ */ new Set(["runwizard", "selectchain", "picktypeahead", "pickdate", "fillcard", "fillcode"]);
|
|
3236
|
+
var datastepkinds = /* @__PURE__ */ new Set(["scrapetable", "paginateextract"]);
|
|
3046
3237
|
async function performstep(step, expectedorigin, rootdocument = document) {
|
|
3047
3238
|
if (location.origin !== expectedorigin) return { ok: false, summary: "Page origin changed before action." };
|
|
3048
3239
|
if (step.kind === "observe") return { ok: true, summary: "Observation completed." };
|
|
@@ -3070,6 +3261,11 @@
|
|
|
3070
3261
|
return { ok: true, summary: "History forward requested." };
|
|
3071
3262
|
}
|
|
3072
3263
|
if (navstepkinds.has(step.kind)) return await runpagenav(step, rootdocument);
|
|
3264
|
+
if (step.kind === "writeclipboard") {
|
|
3265
|
+
const text = step.value ?? "";
|
|
3266
|
+
await navigator.clipboard.writeText(text);
|
|
3267
|
+
return { ok: true, summary: `Wrote ${text.length} reviewed character${text.length === 1 ? "" : "s"} to the clipboard with payload hash ${checksum(text)}.`, details: { length: text.length, hash: checksum(text), destination: "clipboard" } };
|
|
3268
|
+
}
|
|
3073
3269
|
if (step.kind === "scrollpage") {
|
|
3074
3270
|
const options = stepoptions3(step);
|
|
3075
3271
|
window.scrollBy({ left: typeof options.x === "number" ? options.x : 0, top: typeof options.y === "number" ? options.y : 600, behavior: "auto" });
|
|
@@ -3091,6 +3287,7 @@
|
|
|
3091
3287
|
let result;
|
|
3092
3288
|
if (formkinds.has(step.kind)) result = runpageform(step, element, rootdocument);
|
|
3093
3289
|
else if (wizardkinds.has(step.kind)) result = runpagewizard(step, element, rootdocument);
|
|
3290
|
+
else if (datastepkinds.has(step.kind)) result = runpagedata(step, element, rootdocument);
|
|
3094
3291
|
else if (readkinds.has(step.kind)) result = runpageread(step, element, rootdocument);
|
|
3095
3292
|
else if (controlkinds.has(step.kind)) result = runpagecontrol(step, element, rootdocument);
|
|
3096
3293
|
else if (interactkinds.has(step.kind)) return await runinteractstep(step, expectedorigin, performstep);
|