@mobileai/react-native 0.9.3 → 0.9.4
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.
|
@@ -5,37 +5,65 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Strategy chain (tried in order, first success wins):
|
|
7
7
|
*
|
|
8
|
-
* 1. onChangeText
|
|
9
|
-
* → calls props.onChangeText(text) directly.
|
|
8
|
+
* 1. onChangeText — developer's controlled component wired React state.
|
|
9
|
+
* → calls element.props.onChangeText(text) directly.
|
|
10
10
|
*
|
|
11
|
-
* 2.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* 2. Fiber subtree walk — for uncontrolled TextInputs (defaultValue only).
|
|
12
|
+
* React Native's TextInput.js always creates an internal `_onChange` handler
|
|
13
|
+
* and passes it as `onChange` to the native host view (RCTTextInputView /
|
|
14
|
+
* AndroidTextInput). That `_onChange` internally calls `props.onChangeText`
|
|
15
|
+
* and updates native text state.
|
|
14
16
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* → stateNode.focus() + stateNode.setNativeProps({ text })
|
|
17
|
+
* We walk the fiber subtree (behavior-based, no tag numbers) to find any
|
|
18
|
+
* descendant node whose memoizedProps.onChange is a function. Then we call
|
|
19
|
+
* it with a synthetic TextInputChangeEvent. This triggers RN's internal
|
|
20
|
+
* `_onChange` which calls `setLastNativeText` and updates the native view.
|
|
20
21
|
*
|
|
21
|
-
* Why
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* True cross-arch reliability requires native keyboard injection (Detox/Appium).
|
|
22
|
+
* Why this is robust:
|
|
23
|
+
* - No hardcoded fiber tag numbers (host component tags can change across versions)
|
|
24
|
+
* - No dependency on setNativeProps (deprecated in New Architecture / Fabric)
|
|
25
|
+
* - Uses the same code path as a real user typing — RN's own _onChange handler
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import { findNodeHandle } from 'react-native';
|
|
29
28
|
import { walkFiberTree } from "../core/FiberTreeWalker.js";
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Walk a fiber subtree to find the first descendant whose memoizedProps
|
|
31
|
+
* has an `onChange` function. This is behavior-based — we don't check
|
|
32
|
+
* fiber tags, component names, or any React internal that could change.
|
|
33
|
+
* We just look for the prop that RN's TextInput always wires.
|
|
34
|
+
*/
|
|
35
|
+
function findNativeOnChange(fiber, maxDepth = 8) {
|
|
36
|
+
if (!fiber) return null;
|
|
37
|
+
let current = fiber.child;
|
|
38
|
+
|
|
39
|
+
// BFS-style walk through the fiber children
|
|
40
|
+
const queue = [];
|
|
41
|
+
if (current) queue.push({
|
|
42
|
+
node: current,
|
|
43
|
+
depth: 0
|
|
44
|
+
});
|
|
45
|
+
while (queue.length > 0) {
|
|
46
|
+
const {
|
|
47
|
+
node,
|
|
48
|
+
depth: d
|
|
49
|
+
} = queue.shift();
|
|
50
|
+
if (d > maxDepth) continue;
|
|
51
|
+
const props = node.memoizedProps;
|
|
52
|
+
if (props && typeof props.onChange === 'function') {
|
|
53
|
+
return props.onChange;
|
|
37
54
|
}
|
|
38
|
-
|
|
55
|
+
|
|
56
|
+
// Add child and sibling
|
|
57
|
+
if (node.child) queue.push({
|
|
58
|
+
node: node.child,
|
|
59
|
+
depth: d + 1
|
|
60
|
+
});
|
|
61
|
+
if (node.sibling) queue.push({
|
|
62
|
+
node: node.sibling,
|
|
63
|
+
depth: d + 1
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
39
67
|
}
|
|
40
68
|
export function createTypeTool(context) {
|
|
41
69
|
return {
|
|
@@ -62,11 +90,10 @@ export function createTypeTool(context) {
|
|
|
62
90
|
return `❌ Element with index ${args.index} not found.`;
|
|
63
91
|
}
|
|
64
92
|
const props = element.props;
|
|
65
|
-
const stateNode = element.fiberNode?.stateNode;
|
|
66
|
-
const nodeHandle = stateNode ? findNodeHandle(stateNode) ?? null : null;
|
|
67
93
|
const label = element.label || `[${element.type}]`;
|
|
68
94
|
|
|
69
95
|
// ── Strategy 1: controlled via onChangeText ───────────────────────────
|
|
96
|
+
// App developer wired onChangeText={setText} — most reliable.
|
|
70
97
|
if (typeof props.onChangeText === 'function') {
|
|
71
98
|
try {
|
|
72
99
|
props.onChangeText(args.text);
|
|
@@ -77,37 +104,33 @@ export function createTypeTool(context) {
|
|
|
77
104
|
}
|
|
78
105
|
}
|
|
79
106
|
|
|
80
|
-
// ── Strategy 2:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
107
|
+
// ── Strategy 2: fiber subtree walk for native onChange ─────────────────
|
|
108
|
+
// For uncontrolled TextInputs (defaultValue only, no onChangeText).
|
|
109
|
+
// RN's TextInput.js creates _onChange internally and passes it as
|
|
110
|
+
// onChange to the native host view. We find that handler and call it
|
|
111
|
+
// with a synthetic TextInputChangeEvent.
|
|
112
|
+
const fiberNode = element.fiberNode;
|
|
113
|
+
if (fiberNode) {
|
|
114
|
+
// First check the matched fiber itself
|
|
115
|
+
const directOnChange = fiberNode.memoizedProps?.onChange;
|
|
116
|
+
const onChange = typeof directOnChange === 'function' ? directOnChange : findNativeOnChange(fiberNode);
|
|
117
|
+
if (onChange) {
|
|
118
|
+
try {
|
|
119
|
+
onChange({
|
|
120
|
+
nativeEvent: {
|
|
121
|
+
text: args.text,
|
|
122
|
+
eventCount: 0,
|
|
123
|
+
target: 0
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
127
|
+
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
return `❌ onChange failed: ${err.message}`;
|
|
100
130
|
}
|
|
101
|
-
stateNode.setNativeProps({
|
|
102
|
-
text: args.text
|
|
103
|
-
});
|
|
104
|
-
await new Promise(resolve => setTimeout(resolve, 300));
|
|
105
|
-
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
106
|
-
} catch (err) {
|
|
107
|
-
return `❌ setNativeProps failed: ${err.message}`;
|
|
108
131
|
}
|
|
109
132
|
}
|
|
110
|
-
return `❌ Element [${args.index}] "${label}" is not a typeable text input.`;
|
|
133
|
+
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No onChangeText or onChange handler found in fiber tree.`;
|
|
111
134
|
}
|
|
112
135
|
};
|
|
113
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["walkFiberTree","findNativeOnChange","fiber","maxDepth","current","child","queue","push","node","depth","length","d","shift","props","memoizedProps","onChange","sibling","createTypeTool","context","name","description","parameters","index","type","required","text","execute","args","interactives","elements","getRootRef","getWalkConfig","element","find","el","label","onChangeText","Promise","resolve","setTimeout","err","message","fiberNode","directOnChange","nativeEvent","eventCount","target"],"sourceRoot":"../../../src","sources":["tools/typeTool.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Strategy chain (tried in order, first success wins):
|
|
5
5
|
*
|
|
6
|
-
* 1. onChangeText
|
|
7
|
-
* → calls props.onChangeText(text) directly.
|
|
6
|
+
* 1. onChangeText — developer's controlled component wired React state.
|
|
7
|
+
* → calls element.props.onChangeText(text) directly.
|
|
8
8
|
*
|
|
9
|
-
* 2.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* 2. Fiber subtree walk — for uncontrolled TextInputs (defaultValue only).
|
|
10
|
+
* React Native's TextInput.js always creates an internal `_onChange` handler
|
|
11
|
+
* and passes it as `onChange` to the native host view (RCTTextInputView /
|
|
12
|
+
* AndroidTextInput). That `_onChange` internally calls `props.onChangeText`
|
|
13
|
+
* and updates native text state.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* → stateNode.focus() + stateNode.setNativeProps({ text })
|
|
15
|
+
* We walk the fiber subtree (behavior-based, no tag numbers) to find any
|
|
16
|
+
* descendant node whose memoizedProps.onChange is a function. Then we call
|
|
17
|
+
* it with a synthetic TextInputChangeEvent. This triggers RN's internal
|
|
18
|
+
* `_onChange` which calls `setLastNativeText` and updates the native view.
|
|
18
19
|
*
|
|
19
|
-
* Why
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* True cross-arch reliability requires native keyboard injection (Detox/Appium).
|
|
20
|
+
* Why this is robust:
|
|
21
|
+
* - No hardcoded fiber tag numbers (host component tags can change across versions)
|
|
22
|
+
* - No dependency on setNativeProps (deprecated in New Architecture / Fabric)
|
|
23
|
+
* - Uses the same code path as a real user typing — RN's own _onChange handler
|
|
24
24
|
*/
|
|
25
25
|
import type { AgentTool, ToolContext } from './types';
|
|
26
26
|
export declare function createTypeTool(context: ToolContext): AgentTool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/typeTool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"typeTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/typeTool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAkCtD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,CAgE9D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobileai/react-native",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Build autonomous AI agents for React Native and Expo apps. Provides AI-native UI traversal, tool calling, and structured reasoning.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"source": "./src/index.ts",
|
package/src/tools/typeTool.ts
CHANGED
|
@@ -3,39 +3,59 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Strategy chain (tried in order, first success wins):
|
|
5
5
|
*
|
|
6
|
-
* 1. onChangeText
|
|
7
|
-
* → calls props.onChangeText(text) directly.
|
|
6
|
+
* 1. onChangeText — developer's controlled component wired React state.
|
|
7
|
+
* → calls element.props.onChangeText(text) directly.
|
|
8
8
|
*
|
|
9
|
-
* 2.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* 2. Fiber subtree walk — for uncontrolled TextInputs (defaultValue only).
|
|
10
|
+
* React Native's TextInput.js always creates an internal `_onChange` handler
|
|
11
|
+
* and passes it as `onChange` to the native host view (RCTTextInputView /
|
|
12
|
+
* AndroidTextInput). That `_onChange` internally calls `props.onChangeText`
|
|
13
|
+
* and updates native text state.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* → stateNode.focus() + stateNode.setNativeProps({ text })
|
|
15
|
+
* We walk the fiber subtree (behavior-based, no tag numbers) to find any
|
|
16
|
+
* descendant node whose memoizedProps.onChange is a function. Then we call
|
|
17
|
+
* it with a synthetic TextInputChangeEvent. This triggers RN's internal
|
|
18
|
+
* `_onChange` which calls `setLastNativeText` and updates the native view.
|
|
18
19
|
*
|
|
19
|
-
* Why
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* True cross-arch reliability requires native keyboard injection (Detox/Appium).
|
|
20
|
+
* Why this is robust:
|
|
21
|
+
* - No hardcoded fiber tag numbers (host component tags can change across versions)
|
|
22
|
+
* - No dependency on setNativeProps (deprecated in New Architecture / Fabric)
|
|
23
|
+
* - Uses the same code path as a real user typing — RN's own _onChange handler
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { findNodeHandle } from 'react-native';
|
|
27
26
|
import { walkFiberTree } from '../core/FiberTreeWalker';
|
|
28
27
|
import type { AgentTool, ToolContext } from './types';
|
|
29
28
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Walk a fiber subtree to find the first descendant whose memoizedProps
|
|
31
|
+
* has an `onChange` function. This is behavior-based — we don't check
|
|
32
|
+
* fiber tags, component names, or any React internal that could change.
|
|
33
|
+
* We just look for the prop that RN's TextInput always wires.
|
|
34
|
+
*/
|
|
35
|
+
function findNativeOnChange(fiber: any, maxDepth = 8): ((event: any) => void) | null {
|
|
36
|
+
if (!fiber) return null;
|
|
37
|
+
|
|
38
|
+
let current: any = fiber.child;
|
|
39
|
+
|
|
40
|
+
// BFS-style walk through the fiber children
|
|
41
|
+
const queue: { node: any; depth: number }[] = [];
|
|
42
|
+
if (current) queue.push({ node: current, depth: 0 });
|
|
43
|
+
|
|
44
|
+
while (queue.length > 0) {
|
|
45
|
+
const { node, depth: d } = queue.shift()!;
|
|
46
|
+
if (d > maxDepth) continue;
|
|
47
|
+
|
|
48
|
+
const props = node.memoizedProps;
|
|
49
|
+
if (props && typeof props.onChange === 'function') {
|
|
50
|
+
return props.onChange;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add child and sibling
|
|
54
|
+
if (node.child) queue.push({ node: node.child, depth: d + 1 });
|
|
55
|
+
if (node.sibling) queue.push({ node: node.sibling, depth: d + 1 });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return null;
|
|
39
59
|
}
|
|
40
60
|
|
|
41
61
|
export function createTypeTool(context: ToolContext): AgentTool {
|
|
@@ -55,11 +75,10 @@ export function createTypeTool(context: ToolContext): AgentTool {
|
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
const props = element.props;
|
|
58
|
-
const stateNode = element.fiberNode?.stateNode;
|
|
59
|
-
const nodeHandle: number | null = stateNode ? (findNodeHandle(stateNode) ?? null) : null;
|
|
60
78
|
const label = element.label || `[${element.type}]`;
|
|
61
79
|
|
|
62
80
|
// ── Strategy 1: controlled via onChangeText ───────────────────────────
|
|
81
|
+
// App developer wired onChangeText={setText} — most reliable.
|
|
63
82
|
if (typeof props.onChangeText === 'function') {
|
|
64
83
|
try {
|
|
65
84
|
props.onChangeText(args.text);
|
|
@@ -70,36 +89,37 @@ export function createTypeTool(context: ToolContext): AgentTool {
|
|
|
70
89
|
}
|
|
71
90
|
}
|
|
72
91
|
|
|
73
|
-
// ── Strategy 2:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
// ── Strategy 2: fiber subtree walk for native onChange ─────────────────
|
|
93
|
+
// For uncontrolled TextInputs (defaultValue only, no onChangeText).
|
|
94
|
+
// RN's TextInput.js creates _onChange internally and passes it as
|
|
95
|
+
// onChange to the native host view. We find that handler and call it
|
|
96
|
+
// with a synthetic TextInputChangeEvent.
|
|
97
|
+
const fiberNode = element.fiberNode;
|
|
98
|
+
if (fiberNode) {
|
|
99
|
+
// First check the matched fiber itself
|
|
100
|
+
const directOnChange = fiberNode.memoizedProps?.onChange;
|
|
101
|
+
const onChange = typeof directOnChange === 'function'
|
|
102
|
+
? directOnChange
|
|
103
|
+
: findNativeOnChange(fiberNode);
|
|
83
104
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
if (onChange) {
|
|
106
|
+
try {
|
|
107
|
+
onChange({
|
|
108
|
+
nativeEvent: {
|
|
109
|
+
text: args.text,
|
|
110
|
+
eventCount: 0,
|
|
111
|
+
target: 0,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
115
|
+
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
116
|
+
} catch (err: any) {
|
|
117
|
+
return `❌ onChange failed: ${err.message}`;
|
|
93
118
|
}
|
|
94
|
-
stateNode.setNativeProps({ text: args.text });
|
|
95
|
-
await new Promise(resolve => setTimeout(resolve, 300));
|
|
96
|
-
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
97
|
-
} catch (err: any) {
|
|
98
|
-
return `❌ setNativeProps failed: ${err.message}`;
|
|
99
119
|
}
|
|
100
120
|
}
|
|
101
121
|
|
|
102
|
-
return `❌ Element [${args.index}] "${label}" is not a typeable text input.`;
|
|
122
|
+
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No onChangeText or onChange handler found in fiber tree.`;
|
|
103
123
|
},
|
|
104
124
|
};
|
|
105
125
|
}
|