@everydaydevopsio/pilot 0.5.2 → 0.6.1
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/.mcp.json +9 -0
- package/AGENTS.md +9 -0
- package/CHANGELOG.md +64 -0
- package/CLAUDE.md +7 -7
- package/README.md +282 -69
- package/dist/browser/browser-manager.d.ts +60 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.js +355 -0
- package/dist/browser/browser-manager.js.map +1 -0
- package/dist/browser/chrome-launcher.d.ts +31 -0
- package/dist/browser/chrome-launcher.d.ts.map +1 -0
- package/dist/browser/chrome-launcher.js +225 -0
- package/dist/browser/chrome-launcher.js.map +1 -0
- package/dist/browser/connection.d.ts +23 -0
- package/dist/browser/connection.d.ts.map +1 -0
- package/dist/browser/connection.js +53 -0
- package/dist/browser/connection.js.map +1 -0
- package/dist/browser/events.d.ts +9 -0
- package/dist/browser/events.d.ts.map +1 -0
- package/dist/browser/events.js +99 -0
- package/dist/browser/events.js.map +1 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/inspect/element-ref.d.ts +23 -0
- package/dist/browser/inspect/element-ref.d.ts.map +1 -0
- package/dist/browser/inspect/element-ref.js +52 -0
- package/dist/browser/inspect/element-ref.js.map +1 -0
- package/dist/browser/inspect/snapshot.d.ts +11 -0
- package/dist/browser/inspect/snapshot.d.ts.map +1 -0
- package/dist/browser/inspect/snapshot.js +282 -0
- package/dist/browser/inspect/snapshot.js.map +1 -0
- package/dist/browser/inspect/styles.d.ts +45 -0
- package/dist/browser/inspect/styles.d.ts.map +1 -0
- package/dist/browser/inspect/styles.js +190 -0
- package/dist/browser/inspect/styles.js.map +1 -0
- package/dist/browser/inspect/types.d.ts +35 -0
- package/dist/browser/inspect/types.d.ts.map +1 -0
- package/dist/browser/inspect/types.js +2 -0
- package/dist/browser/inspect/types.js.map +1 -0
- package/dist/browser/interaction/dialogs.d.ts +18 -0
- package/dist/browser/interaction/dialogs.d.ts.map +1 -0
- package/dist/browser/interaction/dialogs.js +35 -0
- package/dist/browser/interaction/dialogs.js.map +1 -0
- package/dist/browser/interaction/downloads.d.ts +23 -0
- package/dist/browser/interaction/downloads.d.ts.map +1 -0
- package/dist/browser/interaction/downloads.js +87 -0
- package/dist/browser/interaction/downloads.js.map +1 -0
- package/dist/browser/interaction/events.d.ts +17 -0
- package/dist/browser/interaction/events.d.ts.map +1 -0
- package/dist/browser/interaction/events.js +66 -0
- package/dist/browser/interaction/events.js.map +1 -0
- package/dist/browser/interaction/ref-resolver.d.ts +18 -0
- package/dist/browser/interaction/ref-resolver.d.ts.map +1 -0
- package/dist/browser/interaction/ref-resolver.js +32 -0
- package/dist/browser/interaction/ref-resolver.js.map +1 -0
- package/dist/browser/interaction/uploads.d.ts +4 -0
- package/dist/browser/interaction/uploads.d.ts.map +1 -0
- package/dist/browser/interaction/uploads.js +43 -0
- package/dist/browser/interaction/uploads.js.map +1 -0
- package/dist/browser/network/network-buffer.d.ts +19 -0
- package/dist/browser/network/network-buffer.d.ts.map +1 -0
- package/dist/browser/network/network-buffer.js +118 -0
- package/dist/browser/network/network-buffer.js.map +1 -0
- package/dist/browser/network/network-monitor.d.ts +8 -0
- package/dist/browser/network/network-monitor.d.ts.map +1 -0
- package/dist/browser/network/network-monitor.js +85 -0
- package/dist/browser/network/network-monitor.js.map +1 -0
- package/dist/browser/network/redaction.d.ts +3 -0
- package/dist/browser/network/redaction.d.ts.map +1 -0
- package/dist/browser/network/redaction.js +36 -0
- package/dist/browser/network/redaction.js.map +1 -0
- package/dist/browser/network/types.d.ts +45 -0
- package/dist/browser/network/types.d.ts.map +1 -0
- package/dist/browser/network/types.js +2 -0
- package/dist/browser/network/types.js.map +1 -0
- package/dist/browser/performance/analyze.d.ts +5 -0
- package/dist/browser/performance/analyze.d.ts.map +1 -0
- package/dist/browser/performance/analyze.js +230 -0
- package/dist/browser/performance/analyze.js.map +1 -0
- package/dist/browser/performance/trace.d.ts +10 -0
- package/dist/browser/performance/trace.d.ts.map +1 -0
- package/dist/browser/performance/trace.js +38 -0
- package/dist/browser/performance/trace.js.map +1 -0
- package/dist/browser/performance/types.d.ts +31 -0
- package/dist/browser/performance/types.d.ts.map +1 -0
- package/dist/browser/performance/types.js +2 -0
- package/dist/browser/performance/types.js.map +1 -0
- package/dist/browser/preflight.d.ts +20 -0
- package/dist/browser/preflight.d.ts.map +1 -0
- package/dist/browser/preflight.js +105 -0
- package/dist/browser/preflight.js.map +1 -0
- package/dist/browser/security/origins.d.ts +18 -0
- package/dist/browser/security/origins.d.ts.map +1 -0
- package/dist/browser/security/origins.js +84 -0
- package/dist/browser/security/origins.js.map +1 -0
- package/dist/browser/tabs.d.ts +15 -0
- package/dist/browser/tabs.d.ts.map +1 -0
- package/dist/browser/tabs.js +71 -0
- package/dist/browser/tabs.js.map +1 -0
- package/dist/browser/types.d.ts +32 -0
- package/dist/browser/types.d.ts.map +1 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/browser.d.ts +2 -99
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +3 -653
- package/dist/browser.js.map +1 -1
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +33 -48
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/linux-check.d.ts +2 -0
- package/dist/cli/linux-check.d.ts.map +1 -0
- package/dist/cli/linux-check.js +20 -0
- package/dist/cli/linux-check.js.map +1 -0
- package/dist/cli/skill-template.d.ts +1 -1
- package/dist/cli/skill-template.d.ts.map +1 -1
- package/dist/cli/skill-template.js +3 -113
- package/dist/cli/skill-template.js.map +1 -1
- package/dist/commands/check.d.ts +17 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +38 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/click.d.ts +3 -1
- package/dist/commands/click.d.ts.map +1 -1
- package/dist/commands/click.js +16 -4
- package/dist/commands/click.js.map +1 -1
- package/dist/commands/connect.d.ts +14 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +19 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/dialog.d.ts +19 -0
- package/dist/commands/dialog.d.ts.map +1 -0
- package/dist/commands/dialog.js +51 -0
- package/dist/commands/dialog.js.map +1 -0
- package/dist/commands/downloads.d.ts +30 -0
- package/dist/commands/downloads.d.ts.map +1 -0
- package/dist/commands/downloads.js +66 -0
- package/dist/commands/downloads.js.map +1 -0
- package/dist/commands/fill.d.ts +16 -0
- package/dist/commands/fill.d.ts.map +1 -0
- package/dist/commands/fill.js +15 -0
- package/dist/commands/fill.js.map +1 -0
- package/dist/commands/find.d.ts +14 -0
- package/dist/commands/find.d.ts.map +1 -0
- package/dist/commands/find.js +26 -0
- package/dist/commands/find.js.map +1 -0
- package/dist/commands/hover.d.ts +13 -0
- package/dist/commands/hover.d.ts.map +1 -0
- package/dist/commands/hover.js +24 -0
- package/dist/commands/hover.js.map +1 -0
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +13 -0
- package/dist/commands/navigate.js.map +1 -1
- package/dist/commands/network.d.ts +33 -0
- package/dist/commands/network.d.ts.map +1 -0
- package/dist/commands/network.js +84 -0
- package/dist/commands/network.js.map +1 -0
- package/dist/commands/performance.d.ts +12 -0
- package/dist/commands/performance.d.ts.map +1 -0
- package/dist/commands/performance.js +26 -0
- package/dist/commands/performance.js.map +1 -0
- package/dist/commands/press_key.d.ts +10 -0
- package/dist/commands/press_key.d.ts.map +1 -0
- package/dist/commands/press_key.js +103 -0
- package/dist/commands/press_key.js.map +1 -0
- package/dist/commands/scroll.d.ts +21 -0
- package/dist/commands/scroll.d.ts.map +1 -0
- package/dist/commands/scroll.js +54 -0
- package/dist/commands/scroll.js.map +1 -0
- package/dist/commands/select.d.ts +18 -0
- package/dist/commands/select.d.ts.map +1 -0
- package/dist/commands/select.js +53 -0
- package/dist/commands/select.js.map +1 -0
- package/dist/commands/snapshot.d.ts +9 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +7 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/styles.d.ts +13 -0
- package/dist/commands/styles.d.ts.map +1 -0
- package/dist/commands/styles.js +7 -0
- package/dist/commands/styles.js.map +1 -0
- package/dist/commands/type.d.ts +3 -1
- package/dist/commands/type.d.ts.map +1 -1
- package/dist/commands/type.js +11 -2
- package/dist/commands/type.js.map +1 -1
- package/dist/commands/upload.d.ts +12 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +13 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/mcp/console-buffer.d.ts +9 -0
- package/dist/mcp/console-buffer.d.ts.map +1 -1
- package/dist/mcp/console-buffer.js.map +1 -1
- package/dist/mcp/index.js +20 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +63 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/browser.d.ts.map +1 -1
- package/dist/mcp/tools/browser.js +109 -11
- package/dist/mcp/tools/browser.js.map +1 -1
- package/dist/mcp/tools/connect.d.ts +5 -0
- package/dist/mcp/tools/connect.d.ts.map +1 -0
- package/dist/mcp/tools/connect.js +48 -0
- package/dist/mcp/tools/connect.js.map +1 -0
- package/dist/mcp/tools/files.d.ts +4 -0
- package/dist/mcp/tools/files.d.ts.map +1 -0
- package/dist/mcp/tools/files.js +85 -0
- package/dist/mcp/tools/files.js.map +1 -0
- package/dist/mcp/tools/interaction.d.ts +4 -0
- package/dist/mcp/tools/interaction.d.ts.map +1 -0
- package/dist/mcp/tools/interaction.js +176 -0
- package/dist/mcp/tools/interaction.js.map +1 -0
- package/dist/mcp/tools/names.d.ts +1 -1
- package/dist/mcp/tools/names.d.ts.map +1 -1
- package/dist/mcp/tools/names.js +19 -1
- package/dist/mcp/tools/names.js.map +1 -1
- package/dist/mcp/tools/network.d.ts +4 -0
- package/dist/mcp/tools/network.d.ts.map +1 -0
- package/dist/mcp/tools/network.js +68 -0
- package/dist/mcp/tools/network.js.map +1 -0
- package/dist/mcp/tools/performance.d.ts +4 -0
- package/dist/mcp/tools/performance.d.ts.map +1 -0
- package/dist/mcp/tools/performance.js +26 -0
- package/dist/mcp/tools/performance.js.map +1 -0
- package/dist/mcp/tools/runtime.d.ts +4 -0
- package/dist/mcp/tools/runtime.d.ts.map +1 -0
- package/dist/mcp/tools/runtime.js +128 -0
- package/dist/mcp/tools/runtime.js.map +1 -0
- package/dist/mcp/tools/snapshot.d.ts +4 -0
- package/dist/mcp/tools/snapshot.d.ts.map +1 -0
- package/dist/mcp/tools/snapshot.js +42 -0
- package/dist/mcp/tools/snapshot.js.map +1 -0
- package/dist/mcp/tools/styles.d.ts +4 -0
- package/dist/mcp/tools/styles.d.ts.map +1 -0
- package/dist/mcp/tools/styles.js +27 -0
- package/dist/mcp/tools/styles.js.map +1 -0
- package/dist/util/config.d.ts.map +1 -1
- package/dist/util/config.js +3 -2
- package/dist/util/config.js.map +1 -1
- package/package.json +5 -4
- package/skills/pilot/SKILL.md +48 -0
- package/src/browser/browser-manager.ts +465 -0
- package/src/browser/chrome-launcher.ts +282 -0
- package/src/browser/connection.ts +84 -0
- package/src/browser/events.ts +121 -0
- package/src/browser/index.ts +20 -0
- package/src/browser/inspect/element-ref.ts +68 -0
- package/src/browser/inspect/snapshot.ts +348 -0
- package/src/browser/inspect/styles.ts +258 -0
- package/src/browser/inspect/types.ts +37 -0
- package/src/browser/interaction/dialogs.ts +54 -0
- package/src/browser/interaction/downloads.ts +116 -0
- package/src/browser/interaction/events.ts +79 -0
- package/src/browser/interaction/ref-resolver.ts +53 -0
- package/src/browser/interaction/uploads.ts +60 -0
- package/src/browser/network/network-buffer.ts +133 -0
- package/src/browser/network/network-monitor.ts +107 -0
- package/src/browser/network/redaction.ts +42 -0
- package/src/browser/network/types.ts +47 -0
- package/src/browser/performance/analyze.ts +298 -0
- package/src/browser/performance/trace.ts +60 -0
- package/src/browser/performance/types.ts +34 -0
- package/src/browser/preflight.ts +144 -0
- package/src/browser/security/origins.ts +102 -0
- package/src/browser/tabs.ts +110 -0
- package/src/browser/types.ts +32 -0
- package/src/browser.ts +22 -798
- package/src/cli/init.ts +40 -49
- package/src/cli/linux-check.ts +23 -0
- package/src/cli/skill-template.ts +4 -113
- package/src/commands/check.ts +59 -0
- package/src/commands/click.ts +18 -4
- package/src/commands/connect.ts +40 -0
- package/src/commands/dialog.ts +79 -0
- package/src/commands/downloads.ts +111 -0
- package/src/commands/fill.ts +34 -0
- package/src/commands/find.ts +52 -0
- package/src/commands/hover.ts +42 -0
- package/src/commands/navigate.ts +20 -0
- package/src/commands/network.ts +141 -0
- package/src/commands/performance.ts +51 -0
- package/src/commands/press_key.ts +134 -0
- package/src/commands/scroll.ts +79 -0
- package/src/commands/select.ts +78 -0
- package/src/commands/snapshot.ts +18 -0
- package/src/commands/styles.ts +24 -0
- package/src/commands/type.ts +12 -2
- package/src/commands/upload.ts +36 -0
- package/src/mcp/console-buffer.ts +10 -0
- package/src/mcp/index.ts +24 -2
- package/src/mcp/server.ts +74 -2
- package/src/mcp/tools/browser.ts +125 -9
- package/src/mcp/tools/connect.ts +62 -0
- package/src/mcp/tools/files.ts +120 -0
- package/src/mcp/tools/interaction.ts +232 -0
- package/src/mcp/tools/names.ts +19 -1
- package/src/mcp/tools/network.ts +86 -0
- package/src/mcp/tools/performance.ts +40 -0
- package/src/mcp/tools/runtime.ts +164 -0
- package/src/mcp/tools/snapshot.ts +64 -0
- package/src/mcp/tools/styles.ts +41 -0
- package/src/util/config.ts +3 -2
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from './element-ref.js';
|
|
3
|
+
import type { ElementInfo, ElementBounds, SnapshotResult } from './types.js';
|
|
4
|
+
import { getLogger } from '../../util/logger.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Roles that are always included in snapshots when they have
|
|
8
|
+
* a name or are interactive.
|
|
9
|
+
*/
|
|
10
|
+
const INTERACTIVE_ROLES = new Set([
|
|
11
|
+
'button',
|
|
12
|
+
'link',
|
|
13
|
+
'textbox',
|
|
14
|
+
'searchbox',
|
|
15
|
+
'checkbox',
|
|
16
|
+
'radio',
|
|
17
|
+
'combobox',
|
|
18
|
+
'listbox',
|
|
19
|
+
'option',
|
|
20
|
+
'menuitem',
|
|
21
|
+
'menuitemcheckbox',
|
|
22
|
+
'menuitemradio',
|
|
23
|
+
'tab',
|
|
24
|
+
'switch',
|
|
25
|
+
'slider',
|
|
26
|
+
'spinbutton',
|
|
27
|
+
'treeitem'
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Roles included in snapshots when they have a name.
|
|
32
|
+
*/
|
|
33
|
+
const SEMANTIC_ROLES = new Set([
|
|
34
|
+
'heading',
|
|
35
|
+
'img',
|
|
36
|
+
'alert',
|
|
37
|
+
'dialog',
|
|
38
|
+
'alertdialog',
|
|
39
|
+
'navigation',
|
|
40
|
+
'main',
|
|
41
|
+
'complementary',
|
|
42
|
+
'contentinfo',
|
|
43
|
+
'banner',
|
|
44
|
+
'form',
|
|
45
|
+
'region',
|
|
46
|
+
'table',
|
|
47
|
+
'cell',
|
|
48
|
+
'row',
|
|
49
|
+
'columnheader',
|
|
50
|
+
'rowheader'
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
interface AXNode {
|
|
54
|
+
nodeId: string;
|
|
55
|
+
ignored: boolean;
|
|
56
|
+
role?: { type: string; value?: string };
|
|
57
|
+
name?: { type: string; value?: string };
|
|
58
|
+
description?: { type: string; value?: string };
|
|
59
|
+
value?: { type: string; value?: unknown };
|
|
60
|
+
properties?: Array<{
|
|
61
|
+
name: string;
|
|
62
|
+
value: { type: string; value?: unknown };
|
|
63
|
+
}>;
|
|
64
|
+
backendDOMNodeId?: number;
|
|
65
|
+
childIds?: string[];
|
|
66
|
+
parentId?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getAXProperty(node: AXNode, propName: string): unknown | undefined {
|
|
70
|
+
const prop = node.properties?.find((p) => p.name === propName);
|
|
71
|
+
return prop?.value?.value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isNodeVisible(node: AXNode): boolean {
|
|
75
|
+
const hidden = getAXProperty(node, 'hidden');
|
|
76
|
+
if (hidden === true) return false;
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function shouldIncludeNode(node: AXNode): boolean {
|
|
81
|
+
if (node.ignored) return false;
|
|
82
|
+
|
|
83
|
+
const role = node.role?.value;
|
|
84
|
+
if (!role || role === 'none' || role === 'presentation') return false;
|
|
85
|
+
|
|
86
|
+
// Generic containers without names are noise
|
|
87
|
+
if (role === 'generic' || role === 'GenericContainer') {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const name = node.name?.value;
|
|
92
|
+
|
|
93
|
+
// Interactive roles are always included
|
|
94
|
+
if (INTERACTIVE_ROLES.has(role)) return true;
|
|
95
|
+
|
|
96
|
+
// Semantic roles are included when they have a name
|
|
97
|
+
if (SEMANTIC_ROLES.has(role) && name) return true;
|
|
98
|
+
|
|
99
|
+
// Static text nodes with content
|
|
100
|
+
if (role === 'StaticText' || role === 'text') return false;
|
|
101
|
+
|
|
102
|
+
// Include any other node that has an explicit name (ARIA-labeled)
|
|
103
|
+
if (name) return true;
|
|
104
|
+
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function getBoxModelSafe(
|
|
109
|
+
client: Client,
|
|
110
|
+
backendNodeId: number
|
|
111
|
+
): Promise<ElementBounds | undefined> {
|
|
112
|
+
try {
|
|
113
|
+
const { model } = await client.DOM.getBoxModel({ backendNodeId });
|
|
114
|
+
// content quad: [x1,y1, x2,y2, x3,y3, x4,y4]
|
|
115
|
+
const content = model.content;
|
|
116
|
+
const x = Math.min(content[0], content[2], content[4], content[6]);
|
|
117
|
+
const y = Math.min(content[1], content[3], content[5], content[7]);
|
|
118
|
+
const maxX = Math.max(content[0], content[2], content[4], content[6]);
|
|
119
|
+
const maxY = Math.max(content[1], content[3], content[5], content[7]);
|
|
120
|
+
return {
|
|
121
|
+
x: Math.round(x),
|
|
122
|
+
y: Math.round(y),
|
|
123
|
+
width: Math.round(maxX - x),
|
|
124
|
+
height: Math.round(maxY - y)
|
|
125
|
+
};
|
|
126
|
+
} catch {
|
|
127
|
+
// Element may not have a box model (e.g. display:none, zero-size)
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function getNodeTag(
|
|
133
|
+
client: Client,
|
|
134
|
+
backendNodeId: number
|
|
135
|
+
): Promise<{ tag: string; type?: string }> {
|
|
136
|
+
try {
|
|
137
|
+
const { node } = await client.DOM.describeNode({
|
|
138
|
+
backendNodeId,
|
|
139
|
+
depth: 0
|
|
140
|
+
});
|
|
141
|
+
const tag = node.nodeName?.toLowerCase() ?? '';
|
|
142
|
+
// Extract input type from attributes
|
|
143
|
+
let type: string | undefined;
|
|
144
|
+
if (tag === 'input' && node.attributes) {
|
|
145
|
+
for (let i = 0; i < node.attributes.length; i += 2) {
|
|
146
|
+
if (node.attributes[i] === 'type') {
|
|
147
|
+
type = node.attributes[i + 1];
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { tag, type };
|
|
153
|
+
} catch {
|
|
154
|
+
return { tag: '' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function buildSnapshot(
|
|
159
|
+
client: Client,
|
|
160
|
+
refMap: ElementRefMap
|
|
161
|
+
): Promise<SnapshotResult> {
|
|
162
|
+
const logger = getLogger();
|
|
163
|
+
|
|
164
|
+
// Clear refs for the new snapshot without incrementing generation.
|
|
165
|
+
// Generation only advances on navigation (via invalidate()).
|
|
166
|
+
refMap.resetRefs();
|
|
167
|
+
|
|
168
|
+
// Enable required domains
|
|
169
|
+
await Promise.all([client.Accessibility.enable(), client.DOM.enable({})]);
|
|
170
|
+
|
|
171
|
+
// Get the full accessibility tree
|
|
172
|
+
const { nodes } = await client.Accessibility.getFullAXTree({});
|
|
173
|
+
|
|
174
|
+
// Get page info
|
|
175
|
+
const { result: urlResult } = await client.Runtime.evaluate({
|
|
176
|
+
expression: 'document.URL',
|
|
177
|
+
returnByValue: true
|
|
178
|
+
});
|
|
179
|
+
const { result: titleResult } = await client.Runtime.evaluate({
|
|
180
|
+
expression: 'document.title',
|
|
181
|
+
returnByValue: true
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const url = String(urlResult.value ?? '');
|
|
185
|
+
const title = String(titleResult.value ?? '');
|
|
186
|
+
|
|
187
|
+
// Filter to meaningful nodes
|
|
188
|
+
const candidates = (nodes as AXNode[]).filter(shouldIncludeNode);
|
|
189
|
+
|
|
190
|
+
logger.debug(
|
|
191
|
+
{ totalNodes: nodes.length, candidates: candidates.length },
|
|
192
|
+
'Building snapshot'
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// Build element info for each candidate.
|
|
196
|
+
// Assign refs first, then fetch DOM details in parallel per node.
|
|
197
|
+
const validCandidates = candidates.filter((n) => n.backendDOMNodeId);
|
|
198
|
+
const refs = validCandidates.map((axNode) => {
|
|
199
|
+
const ref = refMap.nextRef();
|
|
200
|
+
refMap.set(ref, axNode.backendDOMNodeId!);
|
|
201
|
+
return ref;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const domDetails = await Promise.all(
|
|
205
|
+
validCandidates.map(async (axNode) => {
|
|
206
|
+
const id = axNode.backendDOMNodeId!;
|
|
207
|
+
const [tagInfo, bounds] = await Promise.all([
|
|
208
|
+
getNodeTag(client, id),
|
|
209
|
+
getBoxModelSafe(client, id)
|
|
210
|
+
]);
|
|
211
|
+
return { tagInfo, bounds };
|
|
212
|
+
})
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const elements: ElementInfo[] = [];
|
|
216
|
+
|
|
217
|
+
for (let i = 0; i < validCandidates.length; i++) {
|
|
218
|
+
const axNode = validCandidates[i];
|
|
219
|
+
const ref = refs[i];
|
|
220
|
+
const { tagInfo, bounds } = domDetails[i];
|
|
221
|
+
|
|
222
|
+
const role = axNode.role?.value ?? '';
|
|
223
|
+
const name = axNode.name?.value ?? '';
|
|
224
|
+
|
|
225
|
+
const { tag, type } = tagInfo;
|
|
226
|
+
|
|
227
|
+
// Extract properties
|
|
228
|
+
const checked = getAXProperty(axNode, 'checked');
|
|
229
|
+
const disabled = getAXProperty(axNode, 'disabled');
|
|
230
|
+
const required = getAXProperty(axNode, 'required');
|
|
231
|
+
const selected = getAXProperty(axNode, 'selected');
|
|
232
|
+
const expanded = getAXProperty(axNode, 'expanded');
|
|
233
|
+
const level = getAXProperty(axNode, 'level');
|
|
234
|
+
|
|
235
|
+
// Get value for form controls
|
|
236
|
+
let value: string | undefined;
|
|
237
|
+
if (axNode.value?.value !== undefined && axNode.value?.value !== '') {
|
|
238
|
+
value = String(axNode.value.value);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const visible = isNodeVisible(axNode) && bounds !== undefined;
|
|
242
|
+
const description = axNode.description?.value || undefined;
|
|
243
|
+
|
|
244
|
+
const element: ElementInfo = {
|
|
245
|
+
ref,
|
|
246
|
+
role,
|
|
247
|
+
name,
|
|
248
|
+
tag,
|
|
249
|
+
visible,
|
|
250
|
+
...(type !== undefined && { type }),
|
|
251
|
+
...(value !== undefined && { value }),
|
|
252
|
+
...(checked !== undefined && {
|
|
253
|
+
checked: checked === true || checked === 'true'
|
|
254
|
+
}),
|
|
255
|
+
...(disabled === true && { disabled: true }),
|
|
256
|
+
...(required === true && { required: true }),
|
|
257
|
+
...(selected === true && { selected: true }),
|
|
258
|
+
...(expanded !== undefined && { expanded: expanded === true }),
|
|
259
|
+
...(typeof level === 'number' && { level }),
|
|
260
|
+
...(bounds && { bounds }),
|
|
261
|
+
...(description && { description })
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
elements.push(element);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
url,
|
|
269
|
+
title,
|
|
270
|
+
timestamp: Date.now(),
|
|
271
|
+
elements
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function formatSnapshot(snapshot: SnapshotResult): string {
|
|
276
|
+
const lines: string[] = [];
|
|
277
|
+
lines.push(`Page: ${snapshot.title || snapshot.url}`);
|
|
278
|
+
lines.push(`URL: ${snapshot.url}`);
|
|
279
|
+
lines.push('');
|
|
280
|
+
|
|
281
|
+
if (snapshot.elements.length === 0) {
|
|
282
|
+
lines.push('No interactive elements found.');
|
|
283
|
+
return lines.join('\n');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
lines.push(`Elements (${snapshot.elements.length}):`);
|
|
287
|
+
lines.push('');
|
|
288
|
+
|
|
289
|
+
for (const el of snapshot.elements) {
|
|
290
|
+
const parts: string[] = [`[${el.ref}]`, el.role];
|
|
291
|
+
|
|
292
|
+
if (el.name) {
|
|
293
|
+
parts.push(`"${el.name}"`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const attrs: string[] = [];
|
|
297
|
+
if (el.tag && el.tag !== el.role) attrs.push(el.tag);
|
|
298
|
+
if (el.type) attrs.push(`type=${el.type}`);
|
|
299
|
+
if (el.value !== undefined) attrs.push(`value="${el.value}"`);
|
|
300
|
+
if (el.checked !== undefined)
|
|
301
|
+
attrs.push(el.checked ? 'checked' : 'unchecked');
|
|
302
|
+
if (el.disabled) attrs.push('disabled');
|
|
303
|
+
if (el.required) attrs.push('required');
|
|
304
|
+
if (el.selected) attrs.push('selected');
|
|
305
|
+
if (el.expanded !== undefined)
|
|
306
|
+
attrs.push(el.expanded ? 'expanded' : 'collapsed');
|
|
307
|
+
if (el.level !== undefined) attrs.push(`level=${el.level}`);
|
|
308
|
+
if (!el.visible) attrs.push('hidden');
|
|
309
|
+
|
|
310
|
+
if (attrs.length > 0) {
|
|
311
|
+
parts.push(`(${attrs.join(', ')})`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
lines.push(' ' + parts.join(' '));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return lines.join('\n');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function findElements(
|
|
321
|
+
snapshot: SnapshotResult,
|
|
322
|
+
query: {
|
|
323
|
+
role?: string;
|
|
324
|
+
name?: string;
|
|
325
|
+
text?: string;
|
|
326
|
+
}
|
|
327
|
+
): ElementInfo[] {
|
|
328
|
+
return snapshot.elements.filter((el) => {
|
|
329
|
+
if (query.role && el.role.toLowerCase() !== query.role.toLowerCase()) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
if (query.name) {
|
|
333
|
+
const nameMatch = el.name
|
|
334
|
+
.toLowerCase()
|
|
335
|
+
.includes(query.name.toLowerCase());
|
|
336
|
+
if (!nameMatch) return false;
|
|
337
|
+
}
|
|
338
|
+
if (query.text) {
|
|
339
|
+
const textLower = query.text.toLowerCase();
|
|
340
|
+
const nameMatch = el.name.toLowerCase().includes(textLower);
|
|
341
|
+
const valueMatch = el.value?.toLowerCase().includes(textLower) ?? false;
|
|
342
|
+
const descMatch =
|
|
343
|
+
el.description?.toLowerCase().includes(textLower) ?? false;
|
|
344
|
+
if (!nameMatch && !valueMatch && !descMatch) return false;
|
|
345
|
+
}
|
|
346
|
+
return true;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
import type { ElementRefMap } from './element-ref.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default diagnostic properties returned when no filter is specified.
|
|
6
|
+
* Covers the most common layout/visibility debugging needs.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_PROPERTIES = new Set([
|
|
9
|
+
'display',
|
|
10
|
+
'visibility',
|
|
11
|
+
'opacity',
|
|
12
|
+
'position',
|
|
13
|
+
'width',
|
|
14
|
+
'height',
|
|
15
|
+
'min-width',
|
|
16
|
+
'min-height',
|
|
17
|
+
'max-width',
|
|
18
|
+
'max-height',
|
|
19
|
+
'margin-top',
|
|
20
|
+
'margin-right',
|
|
21
|
+
'margin-bottom',
|
|
22
|
+
'margin-left',
|
|
23
|
+
'padding-top',
|
|
24
|
+
'padding-right',
|
|
25
|
+
'padding-bottom',
|
|
26
|
+
'padding-left',
|
|
27
|
+
'overflow',
|
|
28
|
+
'overflow-x',
|
|
29
|
+
'overflow-y',
|
|
30
|
+
'z-index',
|
|
31
|
+
'transform',
|
|
32
|
+
'color',
|
|
33
|
+
'background-color',
|
|
34
|
+
'font-size',
|
|
35
|
+
'font-weight',
|
|
36
|
+
'text-align',
|
|
37
|
+
'flex-direction',
|
|
38
|
+
'flex-wrap',
|
|
39
|
+
'justify-content',
|
|
40
|
+
'align-items',
|
|
41
|
+
'align-self',
|
|
42
|
+
'flex-grow',
|
|
43
|
+
'flex-shrink',
|
|
44
|
+
'gap',
|
|
45
|
+
'grid-template-columns',
|
|
46
|
+
'grid-template-rows',
|
|
47
|
+
'border',
|
|
48
|
+
'border-radius',
|
|
49
|
+
'box-sizing',
|
|
50
|
+
'pointer-events',
|
|
51
|
+
'cursor'
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
export interface BoxModelInfo {
|
|
55
|
+
content: { x: number; y: number; width: number; height: number };
|
|
56
|
+
padding: { top: number; right: number; bottom: number; left: number };
|
|
57
|
+
border: { top: number; right: number; bottom: number; left: number };
|
|
58
|
+
margin: { top: number; right: number; bottom: number; left: number };
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface MatchedRule {
|
|
64
|
+
selector: string;
|
|
65
|
+
source: string;
|
|
66
|
+
properties: Record<string, string>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface StylesResult {
|
|
70
|
+
ref: string;
|
|
71
|
+
computed: Record<string, string>;
|
|
72
|
+
matched: MatchedRule[];
|
|
73
|
+
boxModel?: BoxModelInfo;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let cssEnabledFor: WeakRef<Client> | null = null;
|
|
77
|
+
|
|
78
|
+
async function ensureCssEnabled(client: Client): Promise<void> {
|
|
79
|
+
if (cssEnabledFor?.deref() === client) return;
|
|
80
|
+
await client.CSS.enable();
|
|
81
|
+
cssEnabledFor = new WeakRef(client);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Reset CSS enabled state (for reconnection)
|
|
85
|
+
export function resetCssState(): void {
|
|
86
|
+
cssEnabledFor = null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parseQuad(quad: number[]): {
|
|
90
|
+
x: number;
|
|
91
|
+
y: number;
|
|
92
|
+
width: number;
|
|
93
|
+
height: number;
|
|
94
|
+
} {
|
|
95
|
+
const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
|
96
|
+
const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
|
97
|
+
const maxX = Math.max(quad[0], quad[2], quad[4], quad[6]);
|
|
98
|
+
const maxY = Math.max(quad[1], quad[3], quad[5], quad[7]);
|
|
99
|
+
return {
|
|
100
|
+
x: Math.round(x),
|
|
101
|
+
y: Math.round(y),
|
|
102
|
+
width: Math.round(maxX - x),
|
|
103
|
+
height: Math.round(maxY - y)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function computeBoxEdges(
|
|
108
|
+
outer: number[],
|
|
109
|
+
inner: number[]
|
|
110
|
+
): { top: number; right: number; bottom: number; left: number } {
|
|
111
|
+
const outerRect = parseQuad(outer);
|
|
112
|
+
const innerRect = parseQuad(inner);
|
|
113
|
+
return {
|
|
114
|
+
top: Math.round(innerRect.y - outerRect.y),
|
|
115
|
+
right: Math.round(
|
|
116
|
+
outerRect.x + outerRect.width - (innerRect.x + innerRect.width)
|
|
117
|
+
),
|
|
118
|
+
bottom: Math.round(
|
|
119
|
+
outerRect.y + outerRect.height - (innerRect.y + innerRect.height)
|
|
120
|
+
),
|
|
121
|
+
left: Math.round(innerRect.x - outerRect.x)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function getStyles(
|
|
126
|
+
client: Client,
|
|
127
|
+
refMap: ElementRefMap,
|
|
128
|
+
ref: string,
|
|
129
|
+
properties?: string[]
|
|
130
|
+
): Promise<StylesResult> {
|
|
131
|
+
const backendNodeId = refMap.resolve(ref);
|
|
132
|
+
|
|
133
|
+
await ensureCssEnabled(client);
|
|
134
|
+
await client.DOM.enable({});
|
|
135
|
+
|
|
136
|
+
// We need a nodeId for CSS APIs — push the backend node to the frontend
|
|
137
|
+
const { nodeIds } = await client.DOM.pushNodesByBackendIdsToFrontend({
|
|
138
|
+
backendNodeIds: [backendNodeId]
|
|
139
|
+
});
|
|
140
|
+
const nodeId = nodeIds[0];
|
|
141
|
+
if (!nodeId) {
|
|
142
|
+
throw new Error(`Could not resolve ref "${ref}" to a DOM node`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get computed styles
|
|
146
|
+
const { computedStyle } = await client.CSS.getComputedStyleForNode({
|
|
147
|
+
nodeId
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const filterSet = properties
|
|
151
|
+
? new Set(properties.map((p) => p.toLowerCase()))
|
|
152
|
+
: DEFAULT_PROPERTIES;
|
|
153
|
+
|
|
154
|
+
const computed: Record<string, string> = {};
|
|
155
|
+
for (const prop of computedStyle) {
|
|
156
|
+
if (filterSet.has(prop.name)) {
|
|
157
|
+
computed[prop.name] = prop.value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Get matched CSS rules
|
|
162
|
+
const matched: MatchedRule[] = [];
|
|
163
|
+
try {
|
|
164
|
+
const { matchedCSSRules } = await client.CSS.getMatchedStylesForNode({
|
|
165
|
+
nodeId
|
|
166
|
+
});
|
|
167
|
+
if (matchedCSSRules) {
|
|
168
|
+
for (const match of matchedCSSRules) {
|
|
169
|
+
const rule = match.rule;
|
|
170
|
+
const selector = rule.selectorList?.text ?? '';
|
|
171
|
+
const source = rule.origin ?? 'regular';
|
|
172
|
+
const ruleProps: Record<string, string> = {};
|
|
173
|
+
for (const prop of rule.style.cssProperties) {
|
|
174
|
+
if (filterSet.has(prop.name) && prop.value) {
|
|
175
|
+
ruleProps[prop.name] = prop.value;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (Object.keys(ruleProps).length > 0) {
|
|
179
|
+
matched.push({ selector, source, properties: ruleProps });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
// getMatchedStylesForNode can fail for some node types
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Get box model
|
|
188
|
+
let boxModel: BoxModelInfo | undefined;
|
|
189
|
+
try {
|
|
190
|
+
const { model } = await client.DOM.getBoxModel({ backendNodeId });
|
|
191
|
+
boxModel = {
|
|
192
|
+
content: parseQuad(model.content),
|
|
193
|
+
padding: computeBoxEdges(model.padding, model.content),
|
|
194
|
+
border: computeBoxEdges(model.border, model.padding),
|
|
195
|
+
margin: computeBoxEdges(model.margin, model.border),
|
|
196
|
+
width: model.width,
|
|
197
|
+
height: model.height
|
|
198
|
+
};
|
|
199
|
+
} catch {
|
|
200
|
+
// Element may not have a box model (display:none)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return { ref, computed, matched, boxModel };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function formatStyles(result: StylesResult): string {
|
|
207
|
+
const lines: string[] = [`Styles for ${result.ref}:\n`];
|
|
208
|
+
|
|
209
|
+
// Computed styles (sorted for stable output)
|
|
210
|
+
lines.push('Computed:');
|
|
211
|
+
const entries = Object.entries(result.computed).sort(([a], [b]) =>
|
|
212
|
+
a.localeCompare(b)
|
|
213
|
+
);
|
|
214
|
+
if (entries.length === 0) {
|
|
215
|
+
lines.push(' (none)');
|
|
216
|
+
} else {
|
|
217
|
+
for (const [name, value] of entries) {
|
|
218
|
+
lines.push(` ${name}: ${value}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Box model
|
|
223
|
+
if (result.boxModel) {
|
|
224
|
+
const bm = result.boxModel;
|
|
225
|
+
lines.push('');
|
|
226
|
+
lines.push('Box Model:');
|
|
227
|
+
lines.push(
|
|
228
|
+
` content: ${bm.content.width}x${bm.content.height} at (${bm.content.x}, ${bm.content.y})`
|
|
229
|
+
);
|
|
230
|
+
lines.push(
|
|
231
|
+
` padding: ${bm.padding.top} ${bm.padding.right} ${bm.padding.bottom} ${bm.padding.left}`
|
|
232
|
+
);
|
|
233
|
+
lines.push(
|
|
234
|
+
` border: ${bm.border.top} ${bm.border.right} ${bm.border.bottom} ${bm.border.left}`
|
|
235
|
+
);
|
|
236
|
+
lines.push(
|
|
237
|
+
` margin: ${bm.margin.top} ${bm.margin.right} ${bm.margin.bottom} ${bm.margin.left}`
|
|
238
|
+
);
|
|
239
|
+
lines.push(` total: ${bm.width}x${bm.height}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Matched rules
|
|
243
|
+
if (result.matched.length > 0) {
|
|
244
|
+
lines.push('');
|
|
245
|
+
lines.push('Matched Rules:');
|
|
246
|
+
for (const rule of result.matched) {
|
|
247
|
+
lines.push(` ${rule.selector} (${rule.source}):`);
|
|
248
|
+
const sortedProps = Object.entries(rule.properties).sort(([a], [b]) =>
|
|
249
|
+
a.localeCompare(b)
|
|
250
|
+
);
|
|
251
|
+
for (const [name, value] of sortedProps) {
|
|
252
|
+
lines.push(` ${name}: ${value}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return lines.join('\n');
|
|
258
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ElementBounds {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ElementInfo {
|
|
9
|
+
ref: string;
|
|
10
|
+
role: string;
|
|
11
|
+
name: string;
|
|
12
|
+
tag: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
checked?: boolean;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
selected?: boolean;
|
|
19
|
+
expanded?: boolean;
|
|
20
|
+
level?: number;
|
|
21
|
+
visible: boolean;
|
|
22
|
+
bounds?: ElementBounds;
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SnapshotResult {
|
|
27
|
+
url: string;
|
|
28
|
+
title: string;
|
|
29
|
+
timestamp: number;
|
|
30
|
+
elements: ElementInfo[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface StaleElementError {
|
|
34
|
+
code: 'STALE_ELEMENT_REFERENCE';
|
|
35
|
+
ref: string;
|
|
36
|
+
message: string;
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Client } from 'chrome-remote-interface';
|
|
2
|
+
|
|
3
|
+
export interface PendingDialog {
|
|
4
|
+
type: string;
|
|
5
|
+
message: string;
|
|
6
|
+
defaultPrompt?: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class DialogQueue {
|
|
11
|
+
private pending: PendingDialog[] = [];
|
|
12
|
+
|
|
13
|
+
push(dialog: PendingDialog): void {
|
|
14
|
+
this.pending.push(dialog);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
list(): PendingDialog[] {
|
|
18
|
+
return [...this.pending];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
shift(): PendingDialog | undefined {
|
|
22
|
+
return this.pending.shift();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
size(): number {
|
|
26
|
+
return this.pending.length;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clear(): void {
|
|
30
|
+
this.pending = [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function attachDialogHandler(client: Client, queue: DialogQueue): void {
|
|
35
|
+
client.Page.javascriptDialogOpening((params) => {
|
|
36
|
+
queue.push({
|
|
37
|
+
type: params.type,
|
|
38
|
+
message: params.message,
|
|
39
|
+
defaultPrompt: params.defaultPrompt ?? undefined,
|
|
40
|
+
timestamp: Date.now()
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function handleDialog(
|
|
46
|
+
client: Client,
|
|
47
|
+
accept: boolean,
|
|
48
|
+
promptText?: string
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
await client.Page.handleJavaScriptDialog({
|
|
51
|
+
accept,
|
|
52
|
+
promptText
|
|
53
|
+
});
|
|
54
|
+
}
|