@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
- * Strategy chain (tried in order, first success wins):
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
- * 1. onChangeText (controlled) — developer wired up React state. Most common.
9
- * calls props.onChangeText(text) directly.
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
- * 2. onChange (controlled, lower-level) some forms use onChange instead of
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
- /** Build a synthetic RN-style text change event object */
31
- function makeSyntheticChangeEvent(text, nodeHandle) {
32
- return {
33
- nativeEvent: {
34
- text,
35
- eventCount: 1,
36
- target: nodeHandle ?? 0
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: controlled via onChange (lower-level event) ───────────
81
- if (typeof props.onChange === 'function') {
82
- try {
83
- props.onChange(makeSyntheticChangeEvent(args.text, nodeHandle));
84
- await new Promise(resolve => setTimeout(resolve, 300));
85
- return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
86
- } catch (err) {
87
- return `❌ onChange failed: ${err.message}`;
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
- // ── Strategy 3: uncontrolled focus + setNativeProps ─────────────────
92
- // For defaultValue-only TextInputs, React doesn't own the native value
93
- // after initial render, so setNativeProps is stable across re-renders.
94
- if (stateNode && typeof stateNode.setNativeProps === 'function') {
95
- try {
96
- // Focus first so the native layer treats this as active input
97
- if (typeof stateNode.focus === 'function') {
98
- stateNode.focus();
99
- await new Promise(resolve => setTimeout(resolve, 200));
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":["findNodeHandle","walkFiberTree","makeSyntheticChangeEvent","text","nodeHandle","nativeEvent","eventCount","target","createTypeTool","context","name","description","parameters","index","type","required","execute","args","interactives","elements","getRootRef","getWalkConfig","element","find","el","props","stateNode","fiberNode","label","onChangeText","Promise","resolve","setTimeout","err","message","onChange","setNativeProps","focus"],"sourceRoot":"../../../src","sources":["tools/typeTool.ts"],"mappings":"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","ignoreList":[]}
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
- * Strategy chain (tried in order, first success wins):
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
- * 1. onChangeText (controlled) — developer wired up React state. Most common.
7
- * calls props.onChangeText(text) directly.
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
- * 2. onChange (controlled, lower-level) some forms use onChange instead of
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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAatD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,CAgE9D"}
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",
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",
@@ -1,41 +1,34 @@
1
1
  /**
2
2
  * Type Tool — Reliable multi-strategy text input.
3
3
  *
4
- * Strategy chain (tried in order, first success wins):
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
- * 1. onChangeText (controlled) — developer wired up React state. Most common.
7
- * calls props.onChangeText(text) directly.
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
- * 2. onChange (controlled, lower-level) some forms use onChange instead of
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
- /** Build a synthetic RN-style text change event object */
31
- function makeSyntheticChangeEvent(text: string, nodeHandle: number | null) {
32
- return {
33
- nativeEvent: {
34
- text,
35
- eventCount: 1,
36
- target: nodeHandle ?? 0,
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: controlled via onChange (lower-level event) ───────────
74
- if (typeof props.onChange === 'function') {
75
- try {
76
- props.onChange(makeSyntheticChangeEvent(args.text, nodeHandle));
77
- await new Promise(resolve => setTimeout(resolve, 300));
78
- return `✅ Typed "${args.text}" into [${args.index}] "${label}"`;
79
- } catch (err: any) {
80
- return `❌ onChange failed: ${err.message}`;
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
- // ── Strategy 3: uncontrolled focus + setNativeProps ─────────────────
85
- // For defaultValue-only TextInputs, React doesn't own the native value
86
- // after initial render, so setNativeProps is stable across re-renders.
87
- if (stateNode && typeof stateNode.setNativeProps === 'function') {
88
- try {
89
- // Focus first so the native layer treats this as active input
90
- if (typeof stateNode.focus === 'function') {
91
- stateNode.focus();
92
- await new Promise(resolve => setTimeout(resolve, 200));
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
  }