@nexface/tools 0.1.13-alpha.1 → 0.1.13-alpha.3
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/dist/browser/actions.js +0 -1
- package/dist/browser/controller.js +0 -1
- package/dist/browser/diagnostics.js +0 -1
- package/dist/browser/dom.js +0 -1
- package/dist/browser/feedback.js +0 -1
- package/dist/browser/focus.js +0 -1
- package/dist/browser/locator.js +0 -1
- package/dist/browser/prompt.generated.js +0 -1
- package/dist/browser/prompt.js +0 -1
- package/dist/browser/snapshot.js +0 -1
- package/dist/browser/toolset.js +0 -1
- package/dist/browser/types.js +0 -1
- package/dist/browser/worker-runner.js +0 -1
- package/dist/definition.js +0 -1
- package/dist/index.browser.js +0 -1
- package/dist/index.js +0 -1
- package/dist/internal-react.js +0 -1
- package/dist/internal-types.js +0 -1
- package/dist/navigation.js +0 -1
- package/dist/operation-lock.js +0 -1
- package/dist/operation-runtime.js +0 -1
- package/dist/operation-surface.js +0 -1
- package/dist/operation-types.js +0 -1
- package/dist/operation-view.js +0 -1
- package/dist/react.js +0 -1
- package/dist/registration.js +0 -1
- package/dist/runtime.d.ts +3 -1
- package/dist/runtime.js +40 -3
- package/dist/shared/definition.js +0 -1
- package/dist/shared/errors.js +0 -1
- package/dist/shared/page-protocol.js +0 -1
- package/dist/shared/page-tools.js +0 -1
- package/dist/shared/runtime-controller.d.ts +1 -0
- package/dist/shared/runtime-controller.js +0 -1
- package/dist/shared/runtime-execution.js +0 -1
- package/dist/shared/schema.js +0 -1
- package/dist/tool-search.d.ts +11 -0
- package/dist/tool-search.js +91 -0
- package/dist/types.js +0 -1
- package/dist/webmcp.js +0 -1
- package/package.json +6 -2
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/controller.js.map +0 -1
- package/dist/browser/diagnostics.js.map +0 -1
- package/dist/browser/dom.js.map +0 -1
- package/dist/browser/feedback.js.map +0 -1
- package/dist/browser/focus.js.map +0 -1
- package/dist/browser/locator.js.map +0 -1
- package/dist/browser/prompt.generated.js.map +0 -1
- package/dist/browser/prompt.js.map +0 -1
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/browser/toolset.js.map +0 -1
- package/dist/browser/types.js.map +0 -1
- package/dist/browser/worker-runner.js.map +0 -1
- package/dist/definition.js.map +0 -1
- package/dist/index.browser.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal-react.js.map +0 -1
- package/dist/internal-types.js.map +0 -1
- package/dist/navigation.js.map +0 -1
- package/dist/operation-lock.js.map +0 -1
- package/dist/operation-runtime.js.map +0 -1
- package/dist/operation-surface.js.map +0 -1
- package/dist/operation-types.js.map +0 -1
- package/dist/operation-view.js.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/registration.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/shared/definition.js.map +0 -1
- package/dist/shared/errors.js.map +0 -1
- package/dist/shared/page-protocol.js.map +0 -1
- package/dist/shared/page-tools.js.map +0 -1
- package/dist/shared/runtime-controller.js.map +0 -1
- package/dist/shared/runtime-execution.js.map +0 -1
- package/dist/shared/schema.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/webmcp.js.map +0 -1
package/dist/browser/actions.js
CHANGED
package/dist/browser/dom.js
CHANGED
package/dist/browser/feedback.js
CHANGED
package/dist/browser/focus.js
CHANGED
package/dist/browser/locator.js
CHANGED
|
@@ -54,4 +54,3 @@ Execution policy:
|
|
|
54
54
|
- Return JSON-serializable data when reading information from the page.
|
|
55
55
|
- A successful Page action receipt proves only that the action ran. Confirm business success through visible feedback, field readback, or an application tool result.
|
|
56
56
|
- nxf_executeAction automatically returns a best-effort viewport snapshot. If refsExpired, postSnapshotTimedOut, or snapshotUnavailable is true, or the expected page state is absent, call nxf_getSnapshot before another write or nxf_finish.`;
|
|
57
|
-
//# sourceMappingURL=prompt.generated.js.map
|
package/dist/browser/prompt.js
CHANGED
package/dist/browser/snapshot.js
CHANGED
package/dist/browser/toolset.js
CHANGED
package/dist/browser/types.js
CHANGED
package/dist/definition.js
CHANGED
package/dist/index.browser.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,4 +4,3 @@ export { AgentOperationSurface } from "./operation-surface.js";
|
|
|
4
4
|
export { createToolRuntime, isToolRuntimeError, } from "./runtime.js";
|
|
5
5
|
export { AgentContext, ToolsProvider, AgentUIAction, useAgentContext, useAgentNavigation, useAgentTool, } from "./react.js";
|
|
6
6
|
export { registerAgentTool } from "./registration.js";
|
|
7
|
-
//# sourceMappingURL=index.js.map
|
package/dist/internal-react.js
CHANGED
package/dist/internal-types.js
CHANGED
package/dist/navigation.js
CHANGED
package/dist/operation-lock.js
CHANGED
|
@@ -157,4 +157,3 @@ function OperationSurfaceContent({ toolRuntime, element }) {
|
|
|
157
157
|
const entries = !cycle || history.scope === cycle.id ? history.entries : [];
|
|
158
158
|
return activated || executing || entries.length ? React.createElement(OperationOverlayView, { key: cycle?.id ?? history.scope, operation: operation, progressHistory: entries, running: running, humanHasControl: waiting, cancelling: stopping, ...(feedback ? { feedback } : {}), ...(cycle?.canCancel ? { onCancel: () => runtime.requestOperationCancel(cycle.id) } : {}), resolveSurface: resolveSurface }) : null;
|
|
159
159
|
}
|
|
160
|
-
//# sourceMappingURL=operation-surface.js.map
|
package/dist/operation-types.js
CHANGED
package/dist/operation-view.js
CHANGED
|
@@ -209,4 +209,3 @@ export function OperationOverlayView({ operation, feedback, progressHistory = []
|
|
|
209
209
|
(running || humanHasControl) && operation.status !== "cancelled" ? React.createElement("span", { className: "agent-operation__compact-status", title: compactStatusLabel }, compactStatusLabel) : null,
|
|
210
210
|
cancel && (running || humanHasControl) && operation.status !== "cancelled" ? React.createElement("button", { ref: cancelButtonRef, className: "agent-operation__stop", type: "button", disabled: cancelling, onClick: cancel }, cancelling ? "正在终止" : "终止任务") : null) : null), document.body);
|
|
211
211
|
}
|
|
212
|
-
//# sourceMappingURL=operation-view.js.map
|
package/dist/react.js
CHANGED
package/dist/registration.js
CHANGED
package/dist/runtime.d.ts
CHANGED
|
@@ -7,10 +7,11 @@ import type { AgentContextEntry, AgentContextInput, AgentContextRegistration, Ag
|
|
|
7
7
|
import type { AgentToolExecutionEvent } from "./internal-types.js";
|
|
8
8
|
export interface ToolRuntimeOptions {
|
|
9
9
|
readonly pageAutomation?: boolean;
|
|
10
|
+
readonly routeSearch?: boolean;
|
|
10
11
|
}
|
|
11
12
|
export interface ToolRuntimeToolChangeEvent {
|
|
12
13
|
readonly toolName: string;
|
|
13
|
-
readonly change: "registered" | "unregistered";
|
|
14
|
+
readonly change: "registered" | "unregistered" | "visibility";
|
|
14
15
|
}
|
|
15
16
|
declare const CONTEXT_ERROR_CODES: readonly ["CONTEXT_DISPOSED", "CONTEXT_NAME_MISMATCH", "DUPLICATE_CONTEXT", "INVALID_CONTEXT"];
|
|
16
17
|
type AgentContextErrorCode = typeof CONTEXT_ERROR_CODES[number];
|
|
@@ -170,6 +171,7 @@ export declare class ToolRuntimeImplementation extends EventTarget implements To
|
|
|
170
171
|
getContexts(options?: {
|
|
171
172
|
signal?: AbortSignal;
|
|
172
173
|
}): Promise<readonly AgentContextEntry[]>;
|
|
174
|
+
getRegisteredTools(): readonly RuntimeAgentTool[];
|
|
173
175
|
getWebMcpTools(): readonly RuntimeAgentTool[];
|
|
174
176
|
isRegistered(name: string): boolean;
|
|
175
177
|
getTools(): readonly RuntimeAgentTool[];
|
package/dist/runtime.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { searchNavigationTools, searchToolDefinition } from "./tool-search.js";
|
|
1
2
|
import { pageDecisionSymbol } from "./shared/runtime-execution.js";
|
|
2
3
|
import { PageCommandError } from "./browser/locator.js";
|
|
3
4
|
import { measurePagePhase } from "./browser/diagnostics.js";
|
|
@@ -123,6 +124,8 @@ export function getInternalToolRuntime(runtime) {
|
|
|
123
124
|
export class ToolRuntimeImplementation extends EventTarget {
|
|
124
125
|
[toolRuntimeBrand] = true;
|
|
125
126
|
#bindings = new Map();
|
|
127
|
+
#routeSearch;
|
|
128
|
+
#visibleNavigation = new Set();
|
|
126
129
|
#contexts = new Map();
|
|
127
130
|
#contextRevision = 0;
|
|
128
131
|
#browserToolset;
|
|
@@ -157,6 +160,33 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
157
160
|
typeof options.pageAutomation !== "boolean") {
|
|
158
161
|
throw new AgentToolError("INVALID_INPUT", "pageAutomation must be a boolean.");
|
|
159
162
|
}
|
|
163
|
+
if (options.routeSearch !== undefined && typeof options.routeSearch !== "boolean") {
|
|
164
|
+
throw new AgentToolError("INVALID_INPUT", "routeSearch must be a boolean.");
|
|
165
|
+
}
|
|
166
|
+
this.#routeSearch = options.routeSearch ?? false;
|
|
167
|
+
if (this.#routeSearch)
|
|
168
|
+
this.register(searchToolDefinition, ({ query, queries }) => {
|
|
169
|
+
if (query !== undefined && queries !== undefined)
|
|
170
|
+
throw new AgentToolError("INVALID_INPUT", "Use either query or queries, not both.");
|
|
171
|
+
const searchQueries = queries === undefined ? (query === undefined ? undefined : [query]) : [...queries];
|
|
172
|
+
if (searchQueries?.some((phrase) => typeof phrase !== "string" || !phrase.trim()))
|
|
173
|
+
throw new AgentToolError("INVALID_INPUT", "Search queries must contain nonblank strings.");
|
|
174
|
+
const navigation = this.getRegisteredTools().filter(({ definition }) => definition.effect === "navigate");
|
|
175
|
+
const candidates = searchQueries === undefined ? navigation : searchNavigationTools(navigation, searchQueries.map((phrase) => phrase.trim()));
|
|
176
|
+
if (searchQueries !== undefined) {
|
|
177
|
+
const previous = this.#visibleNavigation;
|
|
178
|
+
this.#visibleNavigation = new Set(candidates);
|
|
179
|
+
for (const tool of new Set([...previous, ...candidates])) {
|
|
180
|
+
if (previous.has(tool) === this.#visibleNavigation.has(tool))
|
|
181
|
+
continue;
|
|
182
|
+
this.dispatchEvent(new DetailEvent("toolchange", Object.freeze({
|
|
183
|
+
toolName: tool.definition.name,
|
|
184
|
+
change: "visibility",
|
|
185
|
+
})));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { tools: candidates.map(({ definition: { name, description } }) => ({ name, description })) };
|
|
189
|
+
});
|
|
160
190
|
this.#browserToolset = options.pageAutomation ? createBrowserToolset() : undefined;
|
|
161
191
|
runtimeInternals.set(this, this);
|
|
162
192
|
const controller = Object.freeze({
|
|
@@ -165,6 +195,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
165
195
|
setTaskProgress: this.setTaskProgress,
|
|
166
196
|
getOperationSnapshot: () => this.getActivitySnapshot().operation,
|
|
167
197
|
subscribeOperation: this.subscribeActivity,
|
|
198
|
+
getRegisteredTools: () => this.getRegisteredTools(),
|
|
168
199
|
});
|
|
169
200
|
Object.defineProperty(this, runtimeControllerSymbol, { value: controller });
|
|
170
201
|
}
|
|
@@ -224,6 +255,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
224
255
|
disposed = true;
|
|
225
256
|
options.signal?.removeEventListener("abort", dispose);
|
|
226
257
|
this.#bindings.delete(definition.name);
|
|
258
|
+
this.#visibleNavigation.delete(runtimeTool);
|
|
227
259
|
lifecycleController.abort();
|
|
228
260
|
this.dispatchEvent(new DetailEvent("toolchange", Object.freeze({
|
|
229
261
|
toolName: definition.name,
|
|
@@ -337,17 +369,23 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
337
369
|
revision: this.#contextRevision,
|
|
338
370
|
}));
|
|
339
371
|
}
|
|
372
|
+
getRegisteredTools() {
|
|
373
|
+
return [...this.#bindings.values()]
|
|
374
|
+
.map(({ runtimeTool }) => runtimeTool)
|
|
375
|
+
.filter(({ definition }) => !this.#routeSearch || definition !== searchToolDefinition)
|
|
376
|
+
.sort((left, right) => compareStrings(left.definition.name, right.definition.name));
|
|
377
|
+
}
|
|
340
378
|
getWebMcpTools() {
|
|
341
379
|
return [...this.#bindings.values()]
|
|
342
380
|
.map(({ runtimeTool }) => runtimeTool)
|
|
381
|
+
.filter((tool) => !this.#routeSearch || tool.definition.effect !== "navigate" || this.#visibleNavigation.has(tool))
|
|
343
382
|
.sort((left, right) => compareStrings(left.definition.name, right.definition.name));
|
|
344
383
|
}
|
|
345
384
|
isRegistered(name) {
|
|
346
385
|
return this.#bindings.has(name);
|
|
347
386
|
}
|
|
348
387
|
getTools() {
|
|
349
|
-
const applicationTools =
|
|
350
|
-
.map(({ runtimeTool }) => runtimeTool);
|
|
388
|
+
const applicationTools = this.getWebMcpTools();
|
|
351
389
|
const browserTools = (this.#browserToolset?.definitions ?? [])
|
|
352
390
|
.map((definition) => this.#getBrowserRuntimeTool(definition));
|
|
353
391
|
return [...applicationTools, ...browserTools]
|
|
@@ -599,4 +637,3 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
599
637
|
return runtimeTool;
|
|
600
638
|
}
|
|
601
639
|
}
|
|
602
|
-
//# sourceMappingURL=runtime.js.map
|
package/dist/shared/errors.js
CHANGED
|
@@ -12,6 +12,7 @@ interface OperationSnapshot {
|
|
|
12
12
|
readonly taskProgress?: TaskProgress;
|
|
13
13
|
}
|
|
14
14
|
export interface RuntimeController {
|
|
15
|
+
getRegisteredTools(): ReturnType<ToolRuntime["getTools"]>;
|
|
15
16
|
beginInternalOperation(options?: {
|
|
16
17
|
signal?: AbortSignal;
|
|
17
18
|
onCancel?: () => void;
|
package/dist/shared/schema.js
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RuntimeAgentTool } from "./runtime.js";
|
|
2
|
+
export declare const searchToolDefinition: import("./types.js").AgentToolDefinition<{
|
|
3
|
+
query?: string;
|
|
4
|
+
queries?: string[];
|
|
5
|
+
}, {
|
|
6
|
+
tools: Array<{
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}>;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function searchNavigationTools(tools: readonly RuntimeAgentTool[], query: string | readonly string[]): RuntimeAgentTool[];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createAgentToolDefinition } from "./shared/definition.js";
|
|
2
|
+
export const searchToolDefinition = createAgentToolDefinition({
|
|
3
|
+
name: "nxf_searchTool",
|
|
4
|
+
description: "Search registered navigation tools when opening a page with no suitable callable navigation tool. Use query for one phrase or queries for alternative phrasings of the same task. Results replace visible candidates (max 5). Call a match from the refreshed catalog, then use the page's tools. If none fits, call {} for the directory, then query an exact name.",
|
|
5
|
+
effect: "read",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
query: { type: "string", description: "Page name, purpose, or exact navigation tool name." },
|
|
10
|
+
queries: { type: "array", description: "Alternative search phrases for the same navigation task. Use instead of query.", minItems: 1, maxItems: 3, items: { type: "string", minLength: 1 } },
|
|
11
|
+
},
|
|
12
|
+
additionalProperties: false,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
function tokenize(text, segmenter) {
|
|
16
|
+
const normalized = text.normalize("NFKC").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase();
|
|
17
|
+
if (segmenter) {
|
|
18
|
+
return [...segmenter.segment(normalized)].filter((part) => part.isWordLike).map((part) => part.segment);
|
|
19
|
+
}
|
|
20
|
+
return (normalized.match(/\p{Script=Han}+|[\p{L}\p{N}]+/gu) ?? []).flatMap((word) => {
|
|
21
|
+
if (!/^\p{Script=Han}+$/u.test(word))
|
|
22
|
+
return [word];
|
|
23
|
+
const characters = [...word];
|
|
24
|
+
return characters.length < 2 ? characters : characters.slice(1).map((character, index) => characters[index] + character);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function schemaText(value) {
|
|
28
|
+
if (!value || typeof value !== "object")
|
|
29
|
+
return [];
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return value.flatMap(schemaText);
|
|
32
|
+
const schema = value;
|
|
33
|
+
return Object.entries(schema).flatMap(([key, child]) => {
|
|
34
|
+
if (key === "description" && typeof child === "string")
|
|
35
|
+
return [child];
|
|
36
|
+
if (key === "properties" && child && typeof child === "object") {
|
|
37
|
+
return Object.entries(child).flatMap(([name, property]) => [name, ...schemaText(property)]);
|
|
38
|
+
}
|
|
39
|
+
return child && typeof child === "object" ? schemaText(child) : [];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export function searchNavigationTools(tools, query) {
|
|
43
|
+
if (typeof query !== "string") {
|
|
44
|
+
const queries = [...new Set(query)];
|
|
45
|
+
if (queries.length === 1)
|
|
46
|
+
return searchNavigationTools(tools, queries[0]);
|
|
47
|
+
// Fuse ranks rather than comparing BM25 scores across different queries.
|
|
48
|
+
const scores = new Map();
|
|
49
|
+
for (const phrase of queries) {
|
|
50
|
+
searchNavigationTools(tools, phrase).forEach((tool, index) => {
|
|
51
|
+
scores.set(tool, (scores.get(tool) ?? 0) + 1 / (60 + index + 1));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return [...scores].sort(([left, a], [right, b]) => {
|
|
55
|
+
const x = left.definition.name;
|
|
56
|
+
const y = right.definition.name;
|
|
57
|
+
return b - a || (x < y ? -1 : x > y ? 1 : 0);
|
|
58
|
+
}).slice(0, 5).map(([tool]) => tool);
|
|
59
|
+
}
|
|
60
|
+
const exact = tools.find((tool) => tool.definition.name === query);
|
|
61
|
+
if (exact)
|
|
62
|
+
return [exact];
|
|
63
|
+
const segmenter = typeof Intl.Segmenter === "function" ? new Intl.Segmenter("zh", { granularity: "word" }) : undefined;
|
|
64
|
+
const terms = [...new Set(tokenize(query, segmenter))];
|
|
65
|
+
if (!terms.length || !tools.length)
|
|
66
|
+
return [];
|
|
67
|
+
const documents = tools.map((tool) => {
|
|
68
|
+
const { name, description, inputSchema } = tool.definition;
|
|
69
|
+
const tokens = tokenize([name, description, ...schemaText(inputSchema)].join(" "), segmenter);
|
|
70
|
+
const frequencies = new Map();
|
|
71
|
+
for (const token of tokens)
|
|
72
|
+
frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
73
|
+
return { tool, length: tokens.length, frequencies };
|
|
74
|
+
});
|
|
75
|
+
const averageLength = documents.reduce((sum, document) => sum + document.length, 0) / documents.length || 1;
|
|
76
|
+
const idf = new Map(terms.map((term) => {
|
|
77
|
+
const count = documents.filter((document) => document.frequencies.has(term)).length;
|
|
78
|
+
return [term, Math.log(1 + (documents.length - count + 0.5) / (count + 0.5))];
|
|
79
|
+
}));
|
|
80
|
+
return documents.map((document) => {
|
|
81
|
+
const score = terms.reduce((sum, term) => {
|
|
82
|
+
const frequency = document.frequencies.get(term) ?? 0;
|
|
83
|
+
return sum + idf.get(term) * frequency * 2.2 / (frequency + 1.2 * (0.25 + 0.75 * document.length / averageLength));
|
|
84
|
+
}, 0);
|
|
85
|
+
return { tool: document.tool, score };
|
|
86
|
+
}).filter(({ score }) => score > 0).sort((left, right) => {
|
|
87
|
+
const a = left.tool.definition.name;
|
|
88
|
+
const b = right.tool.definition.name;
|
|
89
|
+
return right.score - left.score || (a < b ? -1 : a > b ? 1 : 0);
|
|
90
|
+
}).slice(0, 5).map(({ tool }) => tool);
|
|
91
|
+
}
|
package/dist/types.js
CHANGED
package/dist/webmcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexface/tools",
|
|
3
|
-
"version": "0.1.13-alpha.
|
|
3
|
+
"version": "0.1.13-alpha.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -27,5 +27,9 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist"
|
|
30
|
-
]
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/fishear/nexface.git"
|
|
34
|
+
}
|
|
31
35
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/browser/actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PageActionCandidate, PageCommand, SemanticState } from \"./types.js\";\nimport { computeName } from \"./dom.js\";\n\nexport interface ObservedAction {\n candidate: PageActionCandidate;\n command: Pick<PageCommand, \"target\" | \"method\" | \"args\">;\n optionElement?: HTMLOptionElement;\n}\n\nexport function collectElementActions(\n actions: Map<string, ObservedAction>,\n ref: string,\n element: Element,\n role: string,\n label: string,\n state: SemanticState,\n scrollable: boolean,\n): void {\n const target = { type: \"ref\", ref } as const;\n const add = (operation: PageActionCandidate[\"operation\"], method: PageCommand[\"method\"], args: unknown[], option?: string, optionElement?: HTMLOptionElement): void => {\n if (actions.size >= 240) return;\n const id = `a${actions.size + 1}`;\n actions.set(id, {\n candidate: { id, operation, ref, role, label: (optionElement ? `${label} / ${option}` : label).slice(0, 500), state, ...(option === undefined ? {} : { option: option.slice(0, 500) }) },\n command: { target, method, args },\n ...(optionElement ? { optionElement } : {}),\n });\n };\n if (state.disabled || element.closest('[inert],[aria-disabled=\"true\"]') || element.matches(\":disabled\")) return;\n if (scrollable) {\n if (element.scrollTop > 0) add(\"scrollUp\", \"scrollBy\", [{ y: -560 }]);\n if (element.scrollTop + element.clientHeight < element.scrollHeight - 1) add(\"scrollDown\", \"scrollBy\", [{ y: 560 }]);\n return;\n }\n if (element instanceof HTMLInputElement && [\"password\", \"file\", \"hidden\"].includes(element.type)) return;\n if (element instanceof HTMLOptionElement) return;\n if (element instanceof HTMLSelectElement) {\n const counts = new Map<string, number>();\n for (const option of element.options) counts.set(option.value, (counts.get(option.value) ?? 0) + 1);\n for (const option of element.options) {\n if (element.multiple) {\n if (counts.get(option.value) === 1 && !option.disabled && !option.closest(\"optgroup[disabled]\")) {\n add(option.selected ? \"uncheck\" : \"check\", \"setOptionSelected\", [option.value, !option.selected, option.label], option.label, option);\n }\n continue;\n }\n if (counts.get(option.value) === 1 && !(element.required && option.value === \"\") && !option.disabled && !option.selected && !option.closest(\"optgroup[disabled]\")) add(\"select\", \"selectOption\", [option.value], option.label);\n }\n return;\n }\n if (role === \"option\" && element.closest('[role=\"listbox\"]')?.getAttribute(\"aria-multiselectable\") === \"true\"\n && [\"true\", \"false\"].includes(element.getAttribute(\"aria-selected\") ?? \"\")) {\n const field = computeName(element.closest('[role=\"listbox\"]')!);\n label = field ? `${field} / ${label}` : label;\n add(state.selected ? \"uncheck\" : \"check\", state.selected ? \"uncheck\" : \"check\", []);\n return;\n }\n if (role === \"checkbox\" || role === \"switch\" || role === \"radio\") {\n if (state.checked !== true) add(\"check\", \"check\", []);\n else if (role !== \"radio\") add(\"uncheck\", \"uncheck\", []);\n return;\n }\n const editable = element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || (element instanceof HTMLElement && element.isContentEditable);\n if (editable && !(\"readOnly\" in element && element.readOnly) && element.getAttribute(\"aria-readonly\") !== \"true\" && [\"textbox\", \"searchbox\", \"combobox\", \"spinbutton\"].includes(role)) {\n add(\"fill\", \"fill\", []);\n }\n add(\"click\", \"click\", []);\n}\n\nexport function collectPageActions(actions: Map<string, ObservedAction>): void {\n const add = (id: string, operation: PageActionCandidate[\"operation\"], method: PageCommand[\"method\"], args: unknown[]): void => {\n actions.set(id, { candidate: { id, operation, label: operation, role: \"page\", state: {} }, command: { target: { type: \"page\" }, method, args } });\n };\n if (window.scrollY > 0) add(\"pageUp\", \"scrollUp\", \"scrollBy\", [{ y: -560 }]);\n if (window.scrollY + window.innerHeight < document.documentElement.scrollHeight - 1) add(\"pageDown\", \"scrollDown\", \"scrollBy\", [{ y: 560 }]);\n add(\"wait\", \"wait\", \"waitForTimeout\", [100]);\n}\n"]}
|