Skip to content

Commit b0521df

Browse files
committed
Extract duplicated display-priority label sets and ordering logic into shared module
Consolidated identical hidden-label and low-priority-label sets and their associated priority-ranking functions from snapshot_ui.ts and structured-output-envelope.ts into a new shared module (element-priority.ts). This eliminates duplication risk and ensures atomic updates to label sets across both next-step suggestion ordering and compact JSON target ordering.
1 parent 04b0552 commit b0521df

3 files changed

Lines changed: 67 additions & 88 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
export const HIDDEN_LABELS = new Set(['sheet grabber']);
2+
3+
export const LOW_PRIORITY_LABELS = new Set([
4+
'close',
5+
'clear search',
6+
'remove',
7+
'delete',
8+
'clear',
9+
'c',
10+
'ac',
11+
'±',
12+
'%',
13+
'÷',
14+
'×',
15+
'-',
16+
'+',
17+
'=',
18+
]);
19+
20+
export function compactText(value: string | undefined): string {
21+
return (value ?? '').replace(/\s+/g, ' ').trim();
22+
}
23+
24+
export function isHiddenLabel(label: string | undefined): boolean {
25+
return HIDDEN_LABELS.has(compactText(label).toLowerCase());
26+
}
27+
28+
export function isLowPriorityLabel(label: string | undefined): boolean {
29+
return LOW_PRIORITY_LABELS.has(compactText(label).toLowerCase());
30+
}
31+
32+
export function isContentRichElement(element: { label?: string; identifier?: string }): boolean {
33+
const label = compactText(element.label);
34+
const identifier = compactText(element.identifier);
35+
return label.includes(',') || label.length >= 24 || /card$/i.test(identifier);
36+
}
37+
38+
export function isAlreadySelectedElement(element: {
39+
state?: { selected?: boolean };
40+
value?: string;
41+
}): boolean {
42+
return (
43+
element.state?.selected === true || compactText(element.value).toLowerCase() === 'selected'
44+
);
45+
}

src/mcp/tools/ui-automation/snapshot_ui.ts

Lines changed: 10 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ import {
2727
parseRuntimeSnapshotResponse,
2828
RuntimeSnapshotParseError,
2929
} from './shared/runtime-snapshot.ts';
30+
import {
31+
isHiddenLabel,
32+
isLowPriorityLabel,
33+
isContentRichElement,
34+
isAlreadySelectedElement,
35+
} from './shared/element-priority.ts';
3036

3137
const snapshotUiSchema = z.object({
3238
simulatorId: z.uuid({ message: 'Invalid Simulator UUID format' }),
@@ -42,69 +48,19 @@ type SnapshotUiResult = CaptureResultDomainResult;
4248

4349
const LOG_PREFIX = '[AXe]';
4450

45-
const HIDDEN_TAP_NEXT_STEP_LABELS = new Set(['sheet grabber']);
46-
47-
const LOW_PRIORITY_TAP_NEXT_STEP_LABELS = new Set([
48-
'close',
49-
'clear search',
50-
'remove',
51-
'delete',
52-
'clear',
53-
'c',
54-
'ac',
55-
'±',
56-
'%',
57-
'÷',
58-
'×',
59-
'-',
60-
'+',
61-
'=',
62-
]);
63-
64-
function compactTapNextStepText(value: string | undefined): string {
65-
return (value ?? '').replace(/\s+/g, ' ').trim();
66-
}
67-
68-
function isHiddenTapNextStepElement(label: string | undefined): boolean {
69-
return HIDDEN_TAP_NEXT_STEP_LABELS.has(compactTapNextStepText(label).toLowerCase());
70-
}
71-
72-
function isLowPriorityTapNextStepElement(label: string | undefined): boolean {
73-
return LOW_PRIORITY_TAP_NEXT_STEP_LABELS.has(compactTapNextStepText(label).toLowerCase());
74-
}
75-
76-
function isContentRichTapNextStepElement(element: {
77-
label?: string;
78-
identifier?: string;
79-
}): boolean {
80-
const label = compactTapNextStepText(element.label);
81-
const identifier = compactTapNextStepText(element.identifier);
82-
return label.includes(',') || label.length >= 24 || /card$/i.test(identifier);
83-
}
84-
85-
function isAlreadySelectedTapNextStepElement(element: {
86-
state?: { selected?: boolean };
87-
value?: string;
88-
}): boolean {
89-
return (
90-
element.state?.selected === true ||
91-
compactTapNextStepText(element.value).toLowerCase() === 'selected'
92-
);
93-
}
94-
9551
function getTapNextStepElementPriority(element: {
9652
label?: string;
9753
identifier?: string;
9854
state?: { selected?: boolean };
9955
value?: string;
10056
}): number {
101-
if (isLowPriorityTapNextStepElement(element.label)) {
57+
if (isLowPriorityLabel(element.label)) {
10258
return 90;
10359
}
104-
if (isAlreadySelectedTapNextStepElement(element)) {
60+
if (isAlreadySelectedElement(element)) {
10561
return 70;
10662
}
107-
if (isContentRichTapNextStepElement(element)) {
63+
if (isContentRichElement(element)) {
10864
return 0;
10965
}
11066
return 20;
@@ -212,7 +168,7 @@ export async function snapshot_uiLogic(
212168
({ element }) =>
213169
element.actions.includes('tap') &&
214170
!element.actions.includes('typeText') &&
215-
!isHiddenTapNextStepElement(element.label),
171+
!isHiddenLabel(element.label),
216172
)
217173
.sort((left, right) => {
218174
const priorityDelta =

src/utils/structured-output-envelope.ts

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import type {
66
RuntimeSnapshotUnchangedV1,
77
RuntimeSnapshotV1,
88
} from '../types/ui-snapshot.ts';
9+
import {
10+
isHiddenLabel,
11+
isLowPriorityLabel,
12+
isContentRichElement,
13+
isAlreadySelectedElement,
14+
compactText,
15+
} from '../mcp/tools/ui-automation/shared/element-priority.ts';
916

1017
type DomainResultData<TResult extends ToolDomainResult> = Omit<
1118
TResult,
@@ -38,56 +45,27 @@ type RuntimeSnapshotUnchangedCompactCapture = {
3845
udid: string;
3946
};
4047

41-
const HIDDEN_RUNTIME_TARGET_LABELS = new Set(['sheet grabber']);
42-
43-
const LOW_PRIORITY_RUNTIME_TARGET_LABELS = new Set([
44-
'sheet grabber',
45-
'close',
46-
'clear search',
47-
'remove',
48-
'delete',
49-
'clear',
50-
'c',
51-
'ac',
52-
'±',
53-
'%',
54-
'÷',
55-
'×',
56-
'-',
57-
'+',
58-
'=',
59-
]);
60-
6148
function compactRuntimeSnapshotText(value: string | undefined): string {
62-
return (value ?? '').replace(/\s+/g, ' ').replace(/\|/g, '/').trim();
63-
}
64-
65-
function normalizedRuntimeSnapshotText(value: string | undefined): string {
66-
return compactRuntimeSnapshotText(value).toLocaleLowerCase();
49+
return compactText(value).replace(/\|/g, '/');
6750
}
6851

6952
function isHiddenRuntimeTarget(element: RuntimeElementV1): boolean {
70-
return HIDDEN_RUNTIME_TARGET_LABELS.has(normalizedRuntimeSnapshotText(element.label));
53+
return isHiddenLabel(element.label);
7154
}
7255

7356
function isLowPriorityRuntimeTarget(element: RuntimeElementV1): boolean {
74-
return LOW_PRIORITY_RUNTIME_TARGET_LABELS.has(normalizedRuntimeSnapshotText(element.label));
57+
return isLowPriorityLabel(element.label);
7558
}
7659

7760
function isContentRichTapTarget(element: RuntimeElementV1): boolean {
7861
if (!element.actions.includes('tap')) {
7962
return false;
8063
}
81-
82-
const label = compactRuntimeSnapshotText(element.label);
83-
const identifier = compactRuntimeSnapshotText(element.identifier);
84-
return label.includes(',') || label.length >= 24 || /card$/i.test(identifier);
64+
return isContentRichElement(element);
8565
}
8666

8767
function isAlreadySelectedRuntimeTarget(element: RuntimeElementV1): boolean {
88-
return (
89-
element.state?.selected === true || normalizedRuntimeSnapshotText(element.value) === 'selected'
90-
);
68+
return isAlreadySelectedElement(element);
9169
}
9270

9371
function getRuntimeTargetDisplayPriority(element: RuntimeElementV1): number {

0 commit comments

Comments
 (0)