Skip to content

Commit 0c88c11

Browse files
authored
feat: add connect script, Vite plugin, and init command (#10)
1 parent d2f01ae commit 0c88c11

File tree

24 files changed

+939
-27
lines changed

24 files changed

+939
-27
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"agent-react-devtools": minor
3+
---
4+
5+
Zero-config app integration — connect your React app in one line:
6+
7+
- **`agent-react-devtools init`** — CLI command that auto-detects your framework (Vite, Next.js, CRA, React Native) and patches the right config files. Next.js App Router gets a `'use client'` wrapper so the connect code runs in the browser.
8+
- **`agent-react-devtools/connect`** — add `import 'agent-react-devtools/connect'` as the first line of your entry point to connect to the daemon. Skips SSR and production builds automatically, never blocks your app.
9+
- **`agent-react-devtools/vite`** — Vite plugin that injects the connect script automatically, no app code changes needed.

.changeset/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"access": "public",
88
"baseBranch": "main",
99
"updateInternalDependencies": "patch",
10-
"ignore": ["example-react-app"],
10+
"ignore": ["example-vite-app", "example-expo-app"],
1111
"snapshot": {
1212
"useCalculatedVersion": true
1313
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ packages/agent-react-devtools/README.md
1212
# Examples
1313
examples/*/node_modules/
1414
examples/*/dist/
15+
examples/*/.expo/

README.md

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,19 +136,68 @@ agent-react-devtools profile commit <N | #N> [--limit N] # Single commit detail
136136

137137
## Connecting Your App
138138

139-
Install `react-devtools-core` in your app and connect before React renders (e.g. in `src/main.tsx`):
139+
### Quick setup
140+
141+
Run the init command in your project root to auto-configure your framework:
142+
143+
```sh
144+
npx agent-react-devtools init
145+
```
146+
147+
This detects your framework (Vite, Next.js, CRA) and patches the appropriate config file.
148+
149+
### One-line import
150+
151+
Add a single import as the first line of your entry point (e.g. `src/main.tsx`):
140152

141153
```ts
142-
import { initialize, connectToDevTools } from 'react-devtools-core';
143-
import { createRoot } from 'react-dom/client';
144-
import App from './App';
154+
import "agent-react-devtools/connect";
155+
```
145156

146-
initialize();
147-
connectToDevTools({ port: 8097 });
157+
This handles everything: deleting the Vite hook stub, initializing react-devtools-core, and connecting via WebSocket. Your app is never blocked — if the daemon isn't running, it times out after 2 seconds.
148158

149-
createRoot(document.getElementById('root')!).render(<App />);
159+
### Vite plugin
160+
161+
For Vite apps, use the plugin instead — no changes to your app code needed:
162+
163+
```ts
164+
// vite.config.ts
165+
import { defineConfig } from "vite";
166+
import react from "@vitejs/plugin-react";
167+
import { reactDevtools } from "agent-react-devtools/vite";
168+
169+
export default defineConfig({
170+
plugins: [reactDevtools(), react()],
171+
});
150172
```
151173

174+
The plugin only runs in dev mode (`vite dev`), not in production builds.
175+
176+
Options:
177+
178+
```ts
179+
reactDevtools({ port: 8097, host: "localhost" });
180+
```
181+
182+
### React Native
183+
184+
React Native apps connect to DevTools automatically — no code changes needed:
185+
186+
```sh
187+
agent-react-devtools start
188+
npx react-native start
189+
```
190+
191+
For physical devices, forward the port:
192+
193+
```sh
194+
adb reverse tcp:8097 tcp:8097
195+
```
196+
197+
For Expo, the connection works automatically with the Expo dev client.
198+
199+
To use a custom port, set the `REACT_DEVTOOLS_PORT` environment variable.
200+
152201
## Using with AI Agents
153202

154203
Add something like this to your project's `CLAUDE.md` (or equivalent agent instructions):

examples/expo-app/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Expo Example App
2+
3+
A minimal React Native app using Expo to test `agent-react-devtools` integration.
4+
5+
## Setup
6+
7+
```sh
8+
cd examples/expo-app
9+
bun install
10+
```
11+
12+
## Testing the DevTools Connection
13+
14+
React Native apps connect to React DevTools automatically — no code changes needed.
15+
16+
```sh
17+
# Terminal 1: Start the daemon
18+
agent-react-devtools start
19+
20+
# Terminal 2: Start the Expo dev server
21+
cd examples/expo-app
22+
bun start
23+
24+
# Terminal 3: Inspect the app
25+
agent-react-devtools status
26+
agent-react-devtools get tree
27+
```
28+
29+
### Physical devices
30+
31+
Forward the DevTools port over USB:
32+
33+
```sh
34+
adb reverse tcp:8097 tcp:8097
35+
```
36+
37+
### Custom port
38+
39+
Set the `REACT_DEVTOOLS_PORT` environment variable before starting both the daemon and the app.

examples/expo-app/app.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"expo": {
3+
"name": "example-expo-app",
4+
"slug": "example-expo-app",
5+
"version": "1.0.0",
6+
"scheme": "example-expo-app",
7+
"platforms": [
8+
"ios",
9+
"android"
10+
],
11+
"ios": {
12+
"bundleIdentifier": "com.example.agentreactdevtools"
13+
},
14+
"android": {
15+
"package": "com.example.agentreactdevtools"
16+
},
17+
"plugins": [
18+
"expo-router",
19+
"expo-asset",
20+
"expo-font"
21+
]
22+
}
23+
}

examples/expo-app/app/_layout.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Stack } from 'expo-router';
2+
3+
export default function RootLayout() {
4+
return <Stack />;
5+
}

examples/expo-app/app/index.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { useState, useEffect, memo } from 'react';
2+
import { View, Text, TextInput, Pressable, FlatList, StyleSheet } from 'react-native';
3+
import { StatusBar } from 'expo-status-bar';
4+
5+
function LiveClock() {
6+
const [now, setNow] = useState(Date.now());
7+
useEffect(() => {
8+
const id = setInterval(() => setNow(Date.now()), 1000);
9+
return () => clearInterval(id);
10+
}, []);
11+
12+
return (
13+
<View style={styles.row}>
14+
<ClockDisplay timestamp={now} />
15+
<ClockLabel />
16+
</View>
17+
);
18+
}
19+
20+
function ClockDisplay({ timestamp }: { timestamp: number }) {
21+
return <Text style={styles.mono}>{new Date(timestamp).toLocaleTimeString()}</Text>;
22+
}
23+
24+
function ClockLabel() {
25+
return <Text style={styles.muted}> local time</Text>;
26+
}
27+
28+
const ITEMS = Array.from({ length: 50 }, (_, i) => ({
29+
id: String(i),
30+
name: `Item ${i}`,
31+
category: ['A', 'B', 'C'][i % 3],
32+
}));
33+
34+
const ListItem = memo(function ListItem({ name, category }: { name: string; category: string }) {
35+
return (
36+
<View style={styles.listItem}>
37+
<Text>{name}</Text>
38+
<Text style={styles.muted}> ({category})</Text>
39+
</View>
40+
);
41+
});
42+
43+
function ItemList({ filter }: { filter: string }) {
44+
const filtered = ITEMS.filter((item) =>
45+
item.name.toLowerCase().includes(filter.toLowerCase()),
46+
);
47+
48+
return (
49+
<FlatList
50+
data={filtered}
51+
keyExtractor={(item) => item.id}
52+
renderItem={({ item }) => <ListItem name={item.name} category={item.category} />}
53+
style={styles.list}
54+
/>
55+
);
56+
}
57+
58+
function NotificationBanner() {
59+
const [count, setCount] = useState(0);
60+
const [label, setLabel] = useState('No notifications');
61+
62+
useEffect(() => {
63+
setLabel(count === 0 ? 'No notifications' : `${count} notification${count > 1 ? 's' : ''}`);
64+
}, [count]);
65+
66+
return (
67+
<View style={[styles.banner, count > 0 ? styles.bannerWarning : styles.bannerSuccess]}>
68+
<Text>{label}</Text>
69+
<Pressable onPress={() => setCount((n) => n + 1)} style={styles.button}>
70+
<Text>Add</Text>
71+
</Pressable>
72+
<Pressable onPress={() => setCount(0)} style={styles.button}>
73+
<Text>Clear</Text>
74+
</Pressable>
75+
</View>
76+
);
77+
}
78+
79+
export default function HomeScreen() {
80+
const [filter, setFilter] = useState('');
81+
82+
return (
83+
<View style={styles.container}>
84+
<StatusBar style="auto" />
85+
<Text style={styles.title}>Perf Debug App</Text>
86+
<LiveClock />
87+
<NotificationBanner />
88+
<TextInput
89+
placeholder="Filter items..."
90+
value={filter}
91+
onChangeText={setFilter}
92+
style={styles.input}
93+
/>
94+
<ItemList filter={filter} />
95+
</View>
96+
);
97+
}
98+
99+
const styles = StyleSheet.create({
100+
container: { flex: 1, padding: 20, paddingTop: 60, backgroundColor: '#fff' },
101+
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 12 },
102+
row: { flexDirection: 'row', alignItems: 'center', marginVertical: 4 },
103+
mono: { fontFamily: 'monospace' },
104+
muted: { color: '#888' },
105+
banner: { flexDirection: 'row', alignItems: 'center', padding: 8, borderRadius: 4, marginVertical: 8 },
106+
bannerWarning: { backgroundColor: '#ffeeba' },
107+
bannerSuccess: { backgroundColor: '#d4edda' },
108+
button: { marginLeft: 8, paddingHorizontal: 12, paddingVertical: 4, backgroundColor: '#e0e0e0', borderRadius: 4 },
109+
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: 8, marginVertical: 8 },
110+
list: { maxHeight: 300 },
111+
listItem: { flexDirection: 'row', paddingVertical: 4 },
112+
});

examples/expo-app/package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "example-expo-app",
3+
"private": true,
4+
"version": "0.0.0",
5+
"main": "expo-router/entry",
6+
"scripts": {
7+
"start": "expo start",
8+
"android": "expo start --android",
9+
"ios": "expo start --ios"
10+
},
11+
"dependencies": {
12+
"expo": "^54.0.33",
13+
"expo-asset": "~12.0.12",
14+
"expo-constants": "~18.0.13",
15+
"expo-font": "~14.0.11",
16+
"expo-linking": "~8.0.11",
17+
"expo-router": "~6.0.23",
18+
"expo-splash-screen": "~31.0.13",
19+
"expo-status-bar": "~3.0.9",
20+
"react": "19.1.0",
21+
"react-native": "0.81.5",
22+
"react-native-safe-area-context": "~5.6.0",
23+
"react-native-screens": "~4.16.0"
24+
},
25+
"devDependencies": {
26+
"@types/react": "~19.1.10",
27+
"typescript": "^5.5.0"
28+
}
29+
}

examples/expo-app/tsconfig.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "expo/tsconfig.base",
3+
"compilerOptions": {
4+
"strict": true
5+
}
6+
}

0 commit comments

Comments
 (0)