@mobileai/react-native 0.9.3 → 0.9.5
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.
|
@@ -3,39 +3,44 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Type Tool — Reliable multi-strategy text input.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* For uncontrolled TextInputs (defaultValue, no onChangeText):
|
|
7
|
+
* 1. Walk fiber subtree (BFS) to find the native host view whose
|
|
8
|
+
* memoizedProps has `onChange` — this is RN's internal `_onChange`.
|
|
9
|
+
* 2. ALSO walk to find the native stateNode with `setNativeProps` to
|
|
10
|
+
* update the VISUAL text in the native view.
|
|
7
11
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
12
|
+
* Both steps are needed:
|
|
13
|
+
* - `onChange` → updates React's internal lastNativeText state
|
|
14
|
+
* - `setNativeProps` → updates what the user sees on screen
|
|
10
15
|
*
|
|
11
|
-
*
|
|
12
|
-
* onChangeText. RN's TextInput fires onChange before calling onChangeText.
|
|
13
|
-
* → calls props.onChange({ nativeEvent: { text, eventCount: 1 } })
|
|
14
|
-
*
|
|
15
|
-
* 3. setNativeProps (uncontrolled, Old Arch) — for TextInputs with defaultValue
|
|
16
|
-
* only. React doesn't manage the native value after initial render for
|
|
17
|
-
* uncontrolled inputs, so setNativeProps is stable (no re-render wipe).
|
|
18
|
-
* Also focuses the input first so the native layer treats it as user input.
|
|
19
|
-
* → stateNode.focus() + stateNode.setNativeProps({ text })
|
|
20
|
-
*
|
|
21
|
-
* Why no 100% reliable cross-arch pure-JS solution for uncontrolled inputs:
|
|
22
|
-
* React's reconciliation model intentionally prevents external mutation of
|
|
23
|
-
* controlled values. For uncontrolled inputs, setNativeProps is the
|
|
24
|
-
* industry-standard JS approach (used by React Native's own test utils).
|
|
25
|
-
* True cross-arch reliability requires native keyboard injection (Detox/Appium).
|
|
16
|
+
* No hardcoded fiber tag numbers. Detection is purely behavior-based.
|
|
26
17
|
*/
|
|
27
18
|
|
|
28
|
-
import { findNodeHandle } from 'react-native';
|
|
29
19
|
import { walkFiberTree } from "../core/FiberTreeWalker.js";
|
|
30
|
-
/**
|
|
31
|
-
function
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
/** BFS through fiber children. Returns first node where predicate matches. */
|
|
21
|
+
function findFiberNode(rootFiber, predicate, maxDepth = 10) {
|
|
22
|
+
if (!rootFiber?.child) return null;
|
|
23
|
+
const queue = [{
|
|
24
|
+
node: rootFiber.child,
|
|
25
|
+
depth: 0
|
|
26
|
+
}];
|
|
27
|
+
while (queue.length > 0) {
|
|
28
|
+
const {
|
|
29
|
+
node,
|
|
30
|
+
depth
|
|
31
|
+
} = queue.shift();
|
|
32
|
+
if (!node || depth > maxDepth) continue;
|
|
33
|
+
if (predicate(node)) return node;
|
|
34
|
+
if (node.child) queue.push({
|
|
35
|
+
node: node.child,
|
|
36
|
+
depth: depth + 1
|
|
37
|
+
});
|
|
38
|
+
if (node.sibling) queue.push({
|
|
39
|
+
node: node.sibling,
|
|
40
|
+
depth: depth + 1
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
39
44
|
}
|
|
40
45
|
export function createTypeTool(context) {
|
|
41
46
|
return {
|
|
@@ -62,9 +67,8 @@ export function createTypeTool(context) {
|
|
|
62
67
|
return `❌ Element with index ${args.index} not found.`;
|
|
63
68
|
}
|
|
64
69
|
const props = element.props;
|
|
65
|
-
const stateNode = element.fiberNode?.stateNode;
|
|
66
|
-
const nodeHandle = stateNode ? findNodeHandle(stateNode) ?? null : null;
|
|
67
70
|
const label = element.label || `[${element.type}]`;
|
|
71
|
+
const fiberNode = element.fiberNode;
|
|
68
72
|
|
|
69
73
|
// ── Strategy 1: controlled via onChangeText ───────────────────────────
|
|
70
74
|
if (typeof props.onChangeText === 'function') {
|
|
@@ -77,37 +81,46 @@ export function createTypeTool(context) {
|
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
// ── Strategy 2:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
}
|
|
84
|
+
// ── Strategy 2: uncontrolled — find native onChange + setNativeProps ──
|
|
85
|
+
// For TextInputs with defaultValue only.
|
|
86
|
+
// We need BOTH:
|
|
87
|
+
// a) find the onChange handler (RN's internal _onChange) to update React state
|
|
88
|
+
// b) find the native stateNode to call setNativeProps to update visual display
|
|
89
|
+
if (fiberNode) {
|
|
90
|
+
// Find native onChange handler (behavior-based, no tag numbers)
|
|
91
|
+
const nativeOnChangeFiber = fiberNode.memoizedProps?.onChange ? fiberNode : findFiberNode(fiberNode, n => typeof n.memoizedProps?.onChange === 'function');
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
// Find native stateNode (host component with setNativeProps)
|
|
94
|
+
const nativeStateFiber = fiberNode.stateNode?.setNativeProps ? fiberNode : findFiberNode(fiberNode, n => n.stateNode && typeof n.stateNode.setNativeProps === 'function');
|
|
95
|
+
const onChange = nativeOnChangeFiber?.memoizedProps?.onChange;
|
|
96
|
+
const nativeInstance = nativeStateFiber?.stateNode;
|
|
97
|
+
if (onChange || nativeInstance) {
|
|
98
|
+
try {
|
|
99
|
+
// Step 1: Update visual text in native view
|
|
100
|
+
if (nativeInstance && typeof nativeInstance.setNativeProps === 'function') {
|
|
101
|
+
nativeInstance.setNativeProps({
|
|
102
|
+
text: args.text
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Step 2: Notify React's internal onChange so lastNativeText stays in sync
|
|
107
|
+
if (typeof onChange === 'function') {
|
|
108
|
+
onChange({
|
|
109
|
+
nativeEvent: {
|
|
110
|
+
text: args.text,
|
|
111
|
+
eventCount: 1,
|
|
112
|
+
target: 0
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
117
|
+
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return `❌ Type failed: ${err.message}`;
|
|
100
120
|
}
|
|
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
121
|
}
|
|
109
122
|
}
|
|
110
|
-
return `❌ Element [${args.index}] "${label}" is not a typeable text input.`;
|
|
123
|
+
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No onChange or native stateNode found in fiber tree.`;
|
|
111
124
|
}
|
|
112
125
|
};
|
|
113
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["walkFiberTree","findFiberNode","rootFiber","predicate","maxDepth","child","queue","node","depth","length","shift","push","sibling","createTypeTool","context","name","description","parameters","index","type","required","text","execute","args","interactives","elements","getRootRef","getWalkConfig","element","find","el","props","label","fiberNode","onChangeText","Promise","resolve","setTimeout","err","message","nativeOnChangeFiber","memoizedProps","onChange","n","nativeStateFiber","stateNode","setNativeProps","nativeInstance","nativeEvent","eventCount","target"],"sourceRoot":"../../../src","sources":["tools/typeTool.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type Tool — Reliable multi-strategy text input.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* For uncontrolled TextInputs (defaultValue, no onChangeText):
|
|
5
|
+
* 1. Walk fiber subtree (BFS) to find the native host view whose
|
|
6
|
+
* memoizedProps has `onChange` — this is RN's internal `_onChange`.
|
|
7
|
+
* 2. ALSO walk to find the native stateNode with `setNativeProps` to
|
|
8
|
+
* update the VISUAL text in the native view.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
10
|
+
* Both steps are needed:
|
|
11
|
+
* - `onChange` → updates React's internal lastNativeText state
|
|
12
|
+
* - `setNativeProps` → updates what the user sees on screen
|
|
8
13
|
*
|
|
9
|
-
*
|
|
10
|
-
* onChangeText. RN's TextInput fires onChange before calling onChangeText.
|
|
11
|
-
* → calls props.onChange({ nativeEvent: { text, eventCount: 1 } })
|
|
12
|
-
*
|
|
13
|
-
* 3. setNativeProps (uncontrolled, Old Arch) — for TextInputs with defaultValue
|
|
14
|
-
* only. React doesn't manage the native value after initial render for
|
|
15
|
-
* uncontrolled inputs, so setNativeProps is stable (no re-render wipe).
|
|
16
|
-
* Also focuses the input first so the native layer treats it as user input.
|
|
17
|
-
* → stateNode.focus() + stateNode.setNativeProps({ text })
|
|
18
|
-
*
|
|
19
|
-
* Why no 100% reliable cross-arch pure-JS solution for uncontrolled inputs:
|
|
20
|
-
* React's reconciliation model intentionally prevents external mutation of
|
|
21
|
-
* controlled values. For uncontrolled inputs, setNativeProps is the
|
|
22
|
-
* industry-standard JS approach (used by React Native's own test utils).
|
|
23
|
-
* True cross-arch reliability requires native keyboard injection (Detox/Appium).
|
|
14
|
+
* No hardcoded fiber tag numbers. Detection is purely behavior-based.
|
|
24
15
|
*/
|
|
25
16
|
import type { AgentTool, ToolContext } from './types';
|
|
26
17
|
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
|
|
1
|
+
{"version":3,"file":"typeTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/typeTool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAgBtD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,CA+E9D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobileai/react-native",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
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
|
@@ -1,41 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type Tool — Reliable multi-strategy text input.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* For uncontrolled TextInputs (defaultValue, no onChangeText):
|
|
5
|
+
* 1. Walk fiber subtree (BFS) to find the native host view whose
|
|
6
|
+
* memoizedProps has `onChange` — this is RN's internal `_onChange`.
|
|
7
|
+
* 2. ALSO walk to find the native stateNode with `setNativeProps` to
|
|
8
|
+
* update the VISUAL text in the native view.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
10
|
+
* Both steps are needed:
|
|
11
|
+
* - `onChange` → updates React's internal lastNativeText state
|
|
12
|
+
* - `setNativeProps` → updates what the user sees on screen
|
|
8
13
|
*
|
|
9
|
-
*
|
|
10
|
-
* onChangeText. RN's TextInput fires onChange before calling onChangeText.
|
|
11
|
-
* → calls props.onChange({ nativeEvent: { text, eventCount: 1 } })
|
|
12
|
-
*
|
|
13
|
-
* 3. setNativeProps (uncontrolled, Old Arch) — for TextInputs with defaultValue
|
|
14
|
-
* only. React doesn't manage the native value after initial render for
|
|
15
|
-
* uncontrolled inputs, so setNativeProps is stable (no re-render wipe).
|
|
16
|
-
* Also focuses the input first so the native layer treats it as user input.
|
|
17
|
-
* → stateNode.focus() + stateNode.setNativeProps({ text })
|
|
18
|
-
*
|
|
19
|
-
* Why no 100% reliable cross-arch pure-JS solution for uncontrolled inputs:
|
|
20
|
-
* React's reconciliation model intentionally prevents external mutation of
|
|
21
|
-
* controlled values. For uncontrolled inputs, setNativeProps is the
|
|
22
|
-
* industry-standard JS approach (used by React Native's own test utils).
|
|
23
|
-
* True cross-arch reliability requires native keyboard injection (Detox/Appium).
|
|
14
|
+
* No hardcoded fiber tag numbers. Detection is purely behavior-based.
|
|
24
15
|
*/
|
|
25
16
|
|
|
26
|
-
import { findNodeHandle } from 'react-native';
|
|
27
17
|
import { walkFiberTree } from '../core/FiberTreeWalker';
|
|
28
18
|
import type { AgentTool, ToolContext } from './types';
|
|
29
19
|
|
|
30
|
-
/**
|
|
31
|
-
function
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
/** BFS through fiber children. Returns first node where predicate matches. */
|
|
21
|
+
function findFiberNode(rootFiber: any, predicate: (node: any) => boolean, maxDepth = 10): any | null {
|
|
22
|
+
if (!rootFiber?.child) return null;
|
|
23
|
+
const queue: { node: any; depth: number }[] = [{ node: rootFiber.child, depth: 0 }];
|
|
24
|
+
while (queue.length > 0) {
|
|
25
|
+
const { node, depth } = queue.shift()!;
|
|
26
|
+
if (!node || depth > maxDepth) continue;
|
|
27
|
+
if (predicate(node)) return node;
|
|
28
|
+
if (node.child) queue.push({ node: node.child, depth: depth + 1 });
|
|
29
|
+
if (node.sibling) queue.push({ node: node.sibling, depth: depth + 1 });
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
39
32
|
}
|
|
40
33
|
|
|
41
34
|
export function createTypeTool(context: ToolContext): AgentTool {
|
|
@@ -55,9 +48,8 @@ export function createTypeTool(context: ToolContext): AgentTool {
|
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
const props = element.props;
|
|
58
|
-
const stateNode = element.fiberNode?.stateNode;
|
|
59
|
-
const nodeHandle: number | null = stateNode ? (findNodeHandle(stateNode) ?? null) : null;
|
|
60
51
|
const label = element.label || `[${element.type}]`;
|
|
52
|
+
const fiberNode = element.fiberNode;
|
|
61
53
|
|
|
62
54
|
// ── Strategy 1: controlled via onChangeText ───────────────────────────
|
|
63
55
|
if (typeof props.onChangeText === 'function') {
|
|
@@ -70,36 +62,52 @@ export function createTypeTool(context: ToolContext): AgentTool {
|
|
|
70
62
|
}
|
|
71
63
|
}
|
|
72
64
|
|
|
73
|
-
// ── Strategy 2:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
// ── Strategy 2: uncontrolled — find native onChange + setNativeProps ──
|
|
66
|
+
// For TextInputs with defaultValue only.
|
|
67
|
+
// We need BOTH:
|
|
68
|
+
// a) find the onChange handler (RN's internal _onChange) to update React state
|
|
69
|
+
// b) find the native stateNode to call setNativeProps to update visual display
|
|
70
|
+
if (fiberNode) {
|
|
71
|
+
// Find native onChange handler (behavior-based, no tag numbers)
|
|
72
|
+
const nativeOnChangeFiber = fiberNode.memoizedProps?.onChange
|
|
73
|
+
? fiberNode
|
|
74
|
+
: findFiberNode(fiberNode, n => typeof n.memoizedProps?.onChange === 'function');
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
76
|
+
// Find native stateNode (host component with setNativeProps)
|
|
77
|
+
const nativeStateFiber = fiberNode.stateNode?.setNativeProps
|
|
78
|
+
? fiberNode
|
|
79
|
+
: findFiberNode(fiberNode, n => n.stateNode && typeof n.stateNode.setNativeProps === 'function');
|
|
80
|
+
|
|
81
|
+
const onChange = nativeOnChangeFiber?.memoizedProps?.onChange;
|
|
82
|
+
const nativeInstance = nativeStateFiber?.stateNode;
|
|
83
|
+
|
|
84
|
+
if (onChange || nativeInstance) {
|
|
85
|
+
try {
|
|
86
|
+
// Step 1: Update visual text in native view
|
|
87
|
+
if (nativeInstance && typeof nativeInstance.setNativeProps === 'function') {
|
|
88
|
+
nativeInstance.setNativeProps({ text: args.text });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Step 2: Notify React's internal onChange so lastNativeText stays in sync
|
|
92
|
+
if (typeof onChange === 'function') {
|
|
93
|
+
onChange({
|
|
94
|
+
nativeEvent: {
|
|
95
|
+
text: args.text,
|
|
96
|
+
eventCount: 1,
|
|
97
|
+
target: 0,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
103
|
+
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
104
|
+
} catch (err: any) {
|
|
105
|
+
return `❌ Type failed: ${err.message}`;
|
|
93
106
|
}
|
|
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
107
|
}
|
|
100
108
|
}
|
|
101
109
|
|
|
102
|
-
return `❌ Element [${args.index}] "${label}" is not a typeable text input.`;
|
|
110
|
+
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No onChange or native stateNode found in fiber tree.`;
|
|
103
111
|
},
|
|
104
112
|
};
|
|
105
113
|
}
|