@mobileai/react-native 0.9.4 → 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,64 +3,41 @@
|
|
|
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
|
-
* 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.
|
|
16
|
-
*
|
|
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.
|
|
21
|
-
*
|
|
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
|
|
16
|
+
* No hardcoded fiber tag numbers. Detection is purely behavior-based.
|
|
26
17
|
*/
|
|
27
18
|
|
|
28
19
|
import { walkFiberTree } from "../core/FiberTreeWalker.js";
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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,
|
|
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,
|
|
43
25
|
depth: 0
|
|
44
|
-
}
|
|
26
|
+
}];
|
|
45
27
|
while (queue.length > 0) {
|
|
46
28
|
const {
|
|
47
29
|
node,
|
|
48
|
-
depth
|
|
30
|
+
depth
|
|
49
31
|
} = queue.shift();
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
if (props && typeof props.onChange === 'function') {
|
|
53
|
-
return props.onChange;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Add child and sibling
|
|
32
|
+
if (!node || depth > maxDepth) continue;
|
|
33
|
+
if (predicate(node)) return node;
|
|
57
34
|
if (node.child) queue.push({
|
|
58
35
|
node: node.child,
|
|
59
|
-
depth:
|
|
36
|
+
depth: depth + 1
|
|
60
37
|
});
|
|
61
38
|
if (node.sibling) queue.push({
|
|
62
39
|
node: node.sibling,
|
|
63
|
-
depth:
|
|
40
|
+
depth: depth + 1
|
|
64
41
|
});
|
|
65
42
|
}
|
|
66
43
|
return null;
|
|
@@ -91,9 +68,9 @@ export function createTypeTool(context) {
|
|
|
91
68
|
}
|
|
92
69
|
const props = element.props;
|
|
93
70
|
const label = element.label || `[${element.type}]`;
|
|
71
|
+
const fiberNode = element.fiberNode;
|
|
94
72
|
|
|
95
73
|
// ── Strategy 1: controlled via onChangeText ───────────────────────────
|
|
96
|
-
// App developer wired onChangeText={setText} — most reliable.
|
|
97
74
|
if (typeof props.onChangeText === 'function') {
|
|
98
75
|
try {
|
|
99
76
|
props.onChangeText(args.text);
|
|
@@ -104,33 +81,46 @@ export function createTypeTool(context) {
|
|
|
104
81
|
}
|
|
105
82
|
}
|
|
106
83
|
|
|
107
|
-
// ── Strategy 2:
|
|
108
|
-
// For
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
const fiberNode = element.fiberNode;
|
|
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
|
|
113
89
|
if (fiberNode) {
|
|
114
|
-
//
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
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');
|
|
92
|
+
|
|
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) {
|
|
118
98
|
try {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
+
}
|
|
126
116
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
127
117
|
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
128
118
|
} catch (err) {
|
|
129
|
-
return `❌
|
|
119
|
+
return `❌ Type failed: ${err.message}`;
|
|
130
120
|
}
|
|
131
121
|
}
|
|
132
122
|
}
|
|
133
|
-
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No
|
|
123
|
+
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No onChange or native stateNode found in fiber tree.`;
|
|
134
124
|
}
|
|
135
125
|
};
|
|
136
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["walkFiberTree","
|
|
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
|
-
* 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.
|
|
14
|
-
*
|
|
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.
|
|
19
|
-
*
|
|
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
|
|
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,60 +1,33 @@
|
|
|
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
|
-
* 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.
|
|
14
|
-
*
|
|
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.
|
|
19
|
-
*
|
|
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
|
|
14
|
+
* No hardcoded fiber tag numbers. Detection is purely behavior-based.
|
|
24
15
|
*/
|
|
25
16
|
|
|
26
17
|
import { walkFiberTree } from '../core/FiberTreeWalker';
|
|
27
18
|
import type { AgentTool, ToolContext } from './types';
|
|
28
19
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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 }];
|
|
44
24
|
while (queue.length > 0) {
|
|
45
|
-
const { node, depth
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
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 });
|
|
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 });
|
|
56
30
|
}
|
|
57
|
-
|
|
58
31
|
return null;
|
|
59
32
|
}
|
|
60
33
|
|
|
@@ -76,9 +49,9 @@ export function createTypeTool(context: ToolContext): AgentTool {
|
|
|
76
49
|
|
|
77
50
|
const props = element.props;
|
|
78
51
|
const label = element.label || `[${element.type}]`;
|
|
52
|
+
const fiberNode = element.fiberNode;
|
|
79
53
|
|
|
80
54
|
// ── Strategy 1: controlled via onChangeText ───────────────────────────
|
|
81
|
-
// App developer wired onChangeText={setText} — most reliable.
|
|
82
55
|
if (typeof props.onChangeText === 'function') {
|
|
83
56
|
try {
|
|
84
57
|
props.onChangeText(args.text);
|
|
@@ -89,37 +62,52 @@ export function createTypeTool(context: ToolContext): AgentTool {
|
|
|
89
62
|
}
|
|
90
63
|
}
|
|
91
64
|
|
|
92
|
-
// ── Strategy 2:
|
|
93
|
-
// For
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
const fiberNode = element.fiberNode;
|
|
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
|
|
98
70
|
if (fiberNode) {
|
|
99
|
-
//
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
: findNativeOnChange(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');
|
|
104
75
|
|
|
105
|
-
|
|
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) {
|
|
106
85
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
|
|
114
102
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
115
103
|
return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
|
|
116
104
|
} catch (err: any) {
|
|
117
|
-
return `❌
|
|
105
|
+
return `❌ Type failed: ${err.message}`;
|
|
118
106
|
}
|
|
119
107
|
}
|
|
120
108
|
}
|
|
121
109
|
|
|
122
|
-
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No
|
|
110
|
+
return `❌ Element [${args.index}] "${label}" is not a typeable text input. No onChange or native stateNode found in fiber tree.`;
|
|
123
111
|
},
|
|
124
112
|
};
|
|
125
113
|
}
|