@10play/expo-air 0.1.0

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.
Files changed (98) hide show
  1. package/README.md +156 -0
  2. package/android/build.gradle +43 -0
  3. package/android/src/main/AndroidManifest.xml +2 -0
  4. package/android/src/main/java/expo/modules/expoair/ExpoAirModule.kt +50 -0
  5. package/android/src/main/java/expo/modules/expoair/ExpoAirView.kt +30 -0
  6. package/build/ExpoAir.types.d.ts +25 -0
  7. package/build/ExpoAir.types.d.ts.map +1 -0
  8. package/build/ExpoAir.types.js +2 -0
  9. package/build/ExpoAir.types.js.map +1 -0
  10. package/build/ExpoAirModule.d.ts +17 -0
  11. package/build/ExpoAirModule.d.ts.map +1 -0
  12. package/build/ExpoAirModule.js +4 -0
  13. package/build/ExpoAirModule.js.map +1 -0
  14. package/build/ExpoAirModule.web.d.ts +10 -0
  15. package/build/ExpoAirModule.web.d.ts.map +1 -0
  16. package/build/ExpoAirModule.web.js +12 -0
  17. package/build/ExpoAirModule.web.js.map +1 -0
  18. package/build/ExpoAirView.d.ts +4 -0
  19. package/build/ExpoAirView.d.ts.map +1 -0
  20. package/build/ExpoAirView.js +7 -0
  21. package/build/ExpoAirView.js.map +1 -0
  22. package/build/ExpoAirView.web.d.ts +4 -0
  23. package/build/ExpoAirView.web.d.ts.map +1 -0
  24. package/build/ExpoAirView.web.js +7 -0
  25. package/build/ExpoAirView.web.js.map +1 -0
  26. package/build/index.d.ts +4 -0
  27. package/build/index.d.ts.map +1 -0
  28. package/build/index.js +6 -0
  29. package/build/index.js.map +1 -0
  30. package/cli/dist/bin/expo-air.d.ts +3 -0
  31. package/cli/dist/bin/expo-air.d.ts.map +1 -0
  32. package/cli/dist/bin/expo-air.js +51 -0
  33. package/cli/dist/bin/expo-air.js.map +1 -0
  34. package/cli/dist/commands/fly.d.ts +11 -0
  35. package/cli/dist/commands/fly.d.ts.map +1 -0
  36. package/cli/dist/commands/fly.js +324 -0
  37. package/cli/dist/commands/fly.js.map +1 -0
  38. package/cli/dist/commands/init.d.ts +7 -0
  39. package/cli/dist/commands/init.d.ts.map +1 -0
  40. package/cli/dist/commands/init.js +126 -0
  41. package/cli/dist/commands/init.js.map +1 -0
  42. package/cli/dist/commands/server.d.ts +7 -0
  43. package/cli/dist/commands/server.d.ts.map +1 -0
  44. package/cli/dist/commands/server.js +40 -0
  45. package/cli/dist/commands/server.js.map +1 -0
  46. package/cli/dist/commands/start.d.ts +12 -0
  47. package/cli/dist/commands/start.d.ts.map +1 -0
  48. package/cli/dist/commands/start.js +266 -0
  49. package/cli/dist/commands/start.js.map +1 -0
  50. package/cli/dist/server/promptServer.d.ts +38 -0
  51. package/cli/dist/server/promptServer.d.ts.map +1 -0
  52. package/cli/dist/server/promptServer.js +591 -0
  53. package/cli/dist/server/promptServer.js.map +1 -0
  54. package/cli/dist/tsconfig.tsbuildinfo +1 -0
  55. package/cli/dist/tunnel/bore.d.ts +24 -0
  56. package/cli/dist/tunnel/bore.d.ts.map +1 -0
  57. package/cli/dist/tunnel/bore.js +157 -0
  58. package/cli/dist/tunnel/bore.js.map +1 -0
  59. package/cli/dist/tunnel/cloudflare.d.ts +17 -0
  60. package/cli/dist/tunnel/cloudflare.d.ts.map +1 -0
  61. package/cli/dist/tunnel/cloudflare.js +159 -0
  62. package/cli/dist/tunnel/cloudflare.js.map +1 -0
  63. package/cli/dist/tunnel/localhost-run.d.ts +13 -0
  64. package/cli/dist/tunnel/localhost-run.d.ts.map +1 -0
  65. package/cli/dist/tunnel/localhost-run.js +65 -0
  66. package/cli/dist/tunnel/localhost-run.js.map +1 -0
  67. package/cli/dist/tunnel/localtunnel.d.ts +12 -0
  68. package/cli/dist/tunnel/localtunnel.d.ts.map +1 -0
  69. package/cli/dist/tunnel/localtunnel.js +23 -0
  70. package/cli/dist/tunnel/localtunnel.js.map +1 -0
  71. package/cli/dist/types/messages.d.ts +90 -0
  72. package/cli/dist/types/messages.d.ts.map +1 -0
  73. package/cli/dist/types/messages.js +8 -0
  74. package/cli/dist/types/messages.js.map +1 -0
  75. package/expo-module.config.json +10 -0
  76. package/ios/ExpoAir.podspec +34 -0
  77. package/ios/ExpoAirAppDelegateSubscriber.swift +85 -0
  78. package/ios/ExpoAirModule.swift +88 -0
  79. package/ios/ExpoAirView.swift +38 -0
  80. package/ios/FloatingBubbleManager.swift +483 -0
  81. package/ios/WidgetBridge.h +4 -0
  82. package/ios/WidgetBridge.mm +64 -0
  83. package/ios/WidgetRuntime.h +11 -0
  84. package/ios/WidgetRuntime.mm +153 -0
  85. package/package.json +90 -0
  86. package/plugin/build/index.d.ts +3 -0
  87. package/plugin/build/index.js +152 -0
  88. package/widget/BubbleContent.tsx +474 -0
  89. package/widget/app.json +8 -0
  90. package/widget/babel.config.js +6 -0
  91. package/widget/components/GitChangesTab.tsx +148 -0
  92. package/widget/components/PromptInput.tsx +150 -0
  93. package/widget/components/ResponseArea.tsx +253 -0
  94. package/widget/index.ts +5 -0
  95. package/widget/metro.config.js +28 -0
  96. package/widget/package.json +16 -0
  97. package/widget/services/websocket.ts +316 -0
  98. package/widget/tsconfig.json +4 -0
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # @10play/expo-air
2
+
3
+ Vibe Coding for React-Native - AI-powered on-device development with Claude.
4
+
5
+ ## Features
6
+
7
+ - Floating widget overlay on your iOS device
8
+ - Send prompts to Claude directly from your phone
9
+ - Real-time code changes via Expo Metro
10
+ - Git status monitoring
11
+ - Tunnel support for remote development
12
+
13
+ ## Requirements
14
+
15
+ - Expo SDK 54+
16
+ - iOS 15.1+ (iOS only in v0)
17
+ - Node.js 18+
18
+
19
+ ## Installation
20
+
21
+ ### 1. Install the package
22
+
23
+ ```bash
24
+ npm install @10play/expo-air
25
+ ```
26
+
27
+ ### 2. Initialize in your project
28
+
29
+ ```bash
30
+ npx expo-air init
31
+ ```
32
+
33
+ This will:
34
+ - Create `.expo-air.json` configuration file
35
+ - Add the plugin to your `app.json`
36
+ - Update `.gitignore`
37
+ - Run `expo prebuild` to generate native iOS code
38
+
39
+ ### 3. Start development
40
+
41
+ ```bash
42
+ npx expo-air start
43
+ ```
44
+
45
+ This starts:
46
+ - Widget Metro server (port 8082)
47
+ - Prompt server (port 3847)
48
+ - App Metro server (port 8081)
49
+ - Cloudflare tunnels for remote access (optional)
50
+
51
+ The widget will appear automatically when your app launches in DEBUG mode.
52
+
53
+ ## Usage
54
+
55
+ ### CLI Commands
56
+
57
+ ```bash
58
+ # Initialize expo-air in your project
59
+ npx expo-air init
60
+ npx expo-air init --force # Overwrite existing config
61
+ npx expo-air init --skip-prebuild # Skip running expo prebuild
62
+
63
+ # Start the development environment
64
+ npx expo-air start
65
+ npx expo-air start --no-tunnel # Skip tunnel (local network only)
66
+ npx expo-air start --no-build # Skip building the app
67
+ npx expo-air start --no-server # Skip starting the WebSocket server
68
+
69
+ # Start only the WebSocket server
70
+ npx expo-air server
71
+ ```
72
+
73
+ ### Port Options
74
+
75
+ ```bash
76
+ npx expo-air start \
77
+ --port 3847 \ # Prompt server port
78
+ --widget-port 8082 \ # Widget Metro port
79
+ --metro-port 8081 # App Metro port
80
+ ```
81
+
82
+ ## Configuration
83
+
84
+ ### .expo-air.json
85
+
86
+ Configuration file created by `expo-air init`:
87
+
88
+ ```json
89
+ {
90
+ "autoShow": true,
91
+ "ui": {
92
+ "bubbleSize": 60,
93
+ "bubbleColor": "#007AFF"
94
+ }
95
+ }
96
+ ```
97
+
98
+ | Option | Type | Default | Description |
99
+ |--------|------|---------|-------------|
100
+ | `autoShow` | boolean | `true` | Auto-show widget on app launch |
101
+ | `ui.bubbleSize` | number | `60` | Size of the floating bubble |
102
+ | `ui.bubbleColor` | string | `"#007AFF"` | Color of the floating bubble |
103
+
104
+ ### .expo-air.local.json (gitignored)
105
+
106
+ Auto-generated file containing tunnel URLs for the current session:
107
+
108
+ ```json
109
+ {
110
+ "serverUrl": "wss://...",
111
+ "widgetMetroUrl": "https://...",
112
+ "appMetroUrl": "https://..."
113
+ }
114
+ ```
115
+
116
+ ## How It Works
117
+
118
+ 1. **App launches** - `ExpoAirAppDelegateSubscriber` triggers (DEBUG builds only)
119
+ 2. **Config loaded** - Settings read from `Info.plist` (set by plugin during prebuild)
120
+ 3. **Widget loads** - `FloatingBubbleManager` loads widget bundle from Metro server
121
+ 4. **Connection established** - Widget connects to prompt server via WebSocket
122
+ 5. **Ready to vibe** - Send prompts to Claude from your device
123
+
124
+ ## Development Mode Only
125
+
126
+ The widget is designed for development only and will **never appear in production builds**. This is enforced via:
127
+
128
+ - `#if DEBUG` guards in native Swift code
129
+ - Widget loads from Metro dev server (no bundled JS)
130
+ - No impact on release builds
131
+
132
+ ## Troubleshooting
133
+
134
+ ### Widget not appearing
135
+
136
+ 1. Ensure you're running a DEBUG build (not release/production)
137
+ 2. Check that Metro servers are running (`npx expo-air start`)
138
+ 3. Verify `autoShow: true` in `.expo-air.json`
139
+ 4. Check Xcode console for `[expo-air]` logs
140
+
141
+ ### Connection issues
142
+
143
+ 1. For local development, ensure device is on same WiFi as your computer
144
+ 2. For remote development, use tunnels (`npx expo-air start` enables by default)
145
+ 3. Check that ports 3847, 8081, 8082 are not blocked
146
+
147
+ ### Prebuild issues
148
+
149
+ If prebuild fails, try:
150
+ ```bash
151
+ npx expo prebuild --platform ios --clean
152
+ ```
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,43 @@
1
+ apply plugin: 'com.android.library'
2
+
3
+ group = 'expo.modules.expoair'
4
+ version = '0.1.0'
5
+
6
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
+ apply from: expoModulesCorePlugin
8
+ applyKotlinExpoModulesCorePlugin()
9
+ useCoreDependencies()
10
+ useExpoPublishing()
11
+
12
+ // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
13
+ // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
14
+ // Most of the time, you may like to manage the Android SDK versions yourself.
15
+ def useManagedAndroidSdkVersions = false
16
+ if (useManagedAndroidSdkVersions) {
17
+ useDefaultAndroidSdkVersions()
18
+ } else {
19
+ buildscript {
20
+ // Simple helper that allows the root project to override versions declared by this library.
21
+ ext.safeExtGet = { prop, fallback ->
22
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
23
+ }
24
+ }
25
+ project.android {
26
+ compileSdkVersion safeExtGet("compileSdkVersion", 36)
27
+ defaultConfig {
28
+ minSdkVersion safeExtGet("minSdkVersion", 24)
29
+ targetSdkVersion safeExtGet("targetSdkVersion", 36)
30
+ }
31
+ }
32
+ }
33
+
34
+ android {
35
+ namespace "expo.modules.expoair"
36
+ defaultConfig {
37
+ versionCode 1
38
+ versionName "0.1.0"
39
+ }
40
+ lintOptions {
41
+ abortOnError false
42
+ }
43
+ }
@@ -0,0 +1,2 @@
1
+ <manifest>
2
+ </manifest>
@@ -0,0 +1,50 @@
1
+ package expo.modules.expoair
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+ import expo.modules.kotlin.modules.ModuleDefinition
5
+ import java.net.URL
6
+
7
+ class ExpoAirModule : Module() {
8
+ // Each module class must implement the definition function. The definition consists of components
9
+ // that describes the module's functionality and behavior.
10
+ // See https://docs.expo.dev/modules/module-api for more details about available components.
11
+ override fun definition() = ModuleDefinition {
12
+ // Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
13
+ // Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
14
+ // The module will be accessible from `requireNativeModule('ExpoAir')` in JavaScript.
15
+ Name("ExpoAir")
16
+
17
+ // Defines constant property on the module.
18
+ Constant("PI") {
19
+ Math.PI
20
+ }
21
+
22
+ // Defines event names that the module can send to JavaScript.
23
+ Events("onChange")
24
+
25
+ // Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.
26
+ Function("hello") {
27
+ "Hello world! 👋"
28
+ }
29
+
30
+ // Defines a JavaScript function that always returns a Promise and whose native code
31
+ // is by default dispatched on the different thread than the JavaScript runtime runs on.
32
+ AsyncFunction("setValueAsync") { value: String ->
33
+ // Send an event to JavaScript.
34
+ sendEvent("onChange", mapOf(
35
+ "value" to value
36
+ ))
37
+ }
38
+
39
+ // Enables the module to be used as a native view. Definition components that are accepted as part of
40
+ // the view definition: Prop, Events.
41
+ View(ExpoAirView::class) {
42
+ // Defines a setter for the `url` prop.
43
+ Prop("url") { view: ExpoAirView, url: URL ->
44
+ view.webView.loadUrl(url.toString())
45
+ }
46
+ // Defines an event that the view can send to JavaScript.
47
+ Events("onLoad")
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,30 @@
1
+ package expo.modules.expoair
2
+
3
+ import android.content.Context
4
+ import android.webkit.WebView
5
+ import android.webkit.WebViewClient
6
+ import expo.modules.kotlin.AppContext
7
+ import expo.modules.kotlin.viewevent.EventDispatcher
8
+ import expo.modules.kotlin.views.ExpoView
9
+
10
+ class ExpoAirView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
11
+ // Creates and initializes an event dispatcher for the `onLoad` event.
12
+ // The name of the event is inferred from the value and needs to match the event name defined in the module.
13
+ private val onLoad by EventDispatcher()
14
+
15
+ // Defines a WebView that will be used as the root subview.
16
+ internal val webView = WebView(context).apply {
17
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
18
+ webViewClient = object : WebViewClient() {
19
+ override fun onPageFinished(view: WebView, url: String) {
20
+ // Sends an event to JavaScript. Triggers a callback defined on the view component in JavaScript.
21
+ onLoad(mapOf("url" to url))
22
+ }
23
+ }
24
+ }
25
+
26
+ init {
27
+ // Adds the WebView to the view hierarchy.
28
+ addView(webView)
29
+ }
30
+ }
@@ -0,0 +1,25 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ export type OnLoadEventPayload = {
3
+ url: string;
4
+ };
5
+ export type ExpoAirModuleEvents = {
6
+ onChange: (params: ChangeEventPayload) => void;
7
+ onPress: () => void;
8
+ onExpand: () => void;
9
+ onCollapse: () => void;
10
+ onDragEnd: (params: {
11
+ x: number;
12
+ y: number;
13
+ }) => void;
14
+ };
15
+ export type ChangeEventPayload = {
16
+ value: string;
17
+ };
18
+ export type ExpoAirViewProps = {
19
+ url: string;
20
+ onLoad: (event: {
21
+ nativeEvent: OnLoadEventPayload;
22
+ }) => void;
23
+ style?: StyleProp<ViewStyle>;
24
+ };
25
+ //# sourceMappingURL=ExpoAir.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAir.types.d.ts","sourceRoot":"","sources":["../src/ExpoAir.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC/C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,EAAE,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7D,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ExpoAir.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAir.types.js","sourceRoot":"","sources":["../src/ExpoAir.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { StyleProp, ViewStyle } from 'react-native';\n\nexport type OnLoadEventPayload = {\n url: string;\n};\n\nexport type ExpoAirModuleEvents = {\n onChange: (params: ChangeEventPayload) => void;\n onPress: () => void;\n onExpand: () => void;\n onCollapse: () => void;\n onDragEnd: (params: { x: number; y: number }) => void;\n};\n\nexport type ChangeEventPayload = {\n value: string;\n};\n\nexport type ExpoAirViewProps = {\n url: string;\n onLoad: (event: { nativeEvent: OnLoadEventPayload }) => void;\n style?: StyleProp<ViewStyle>;\n};\n"]}
@@ -0,0 +1,17 @@
1
+ import { NativeModule } from 'expo';
2
+ import { ExpoAirModuleEvents } from './ExpoAir.types';
3
+ declare class ExpoAirModule extends NativeModule<ExpoAirModuleEvents> {
4
+ PI: number;
5
+ hello(): string;
6
+ setValueAsync(value: string): Promise<void>;
7
+ show(options?: {
8
+ size?: number;
9
+ color?: string;
10
+ }): void;
11
+ hide(): void;
12
+ expand(): void;
13
+ collapse(): void;
14
+ }
15
+ declare const _default: ExpoAirModule;
16
+ export default _default;
17
+ //# sourceMappingURL=ExpoAirModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirModule.d.ts","sourceRoot":"","sources":["../src/ExpoAirModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,OAAO,aAAc,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,IAAI,MAAM;IACf,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3C,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IACvD,IAAI,IAAI,IAAI;IACZ,MAAM,IAAI,IAAI;IACd,QAAQ,IAAI,IAAI;CACjB;;AAGD,wBAA6D"}
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo';
2
+ // This call loads the native module object from the JSI.
3
+ export default requireNativeModule('ExpoAir');
4
+ //# sourceMappingURL=ExpoAirModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirModule.js","sourceRoot":"","sources":["../src/ExpoAirModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAczD,yDAAyD;AACzD,eAAe,mBAAmB,CAAgB,SAAS,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport { ExpoAirModuleEvents } from './ExpoAir.types';\n\ndeclare class ExpoAirModule extends NativeModule<ExpoAirModuleEvents> {\n PI: number;\n hello(): string;\n setValueAsync(value: string): Promise<void>;\n show(options?: { size?: number; color?: string }): void;\n hide(): void;\n expand(): void;\n collapse(): void;\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<ExpoAirModule>('ExpoAir');\n"]}
@@ -0,0 +1,10 @@
1
+ import { NativeModule } from 'expo';
2
+ import { ExpoAirModuleEvents } from './ExpoAir.types';
3
+ declare class ExpoAirModule extends NativeModule<ExpoAirModuleEvents> {
4
+ PI: number;
5
+ setValueAsync(value: string): Promise<void>;
6
+ hello(): string;
7
+ }
8
+ declare const _default: typeof ExpoAirModule;
9
+ export default _default;
10
+ //# sourceMappingURL=ExpoAirModule.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoAirModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,cAAM,aAAc,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IAC3D,EAAE,SAAW;IACP,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAGjD,KAAK;CAGN;;AAED,wBAAiE"}
@@ -0,0 +1,12 @@
1
+ import { registerWebModule, NativeModule } from 'expo';
2
+ class ExpoAirModule extends NativeModule {
3
+ PI = Math.PI;
4
+ async setValueAsync(value) {
5
+ this.emit('onChange', { value });
6
+ }
7
+ hello() {
8
+ return 'Hello world! 👋';
9
+ }
10
+ }
11
+ export default registerWebModule(ExpoAirModule, 'ExpoAirModule');
12
+ //# sourceMappingURL=ExpoAirModule.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirModule.web.js","sourceRoot":"","sources":["../src/ExpoAirModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAIvD,MAAM,aAAc,SAAQ,YAAiC;IAC3D,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;IACb,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,KAAK;QACH,OAAO,iBAAiB,CAAC;IAC3B,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC","sourcesContent":["import { registerWebModule, NativeModule } from 'expo';\n\nimport { ExpoAirModuleEvents } from './ExpoAir.types';\n\nclass ExpoAirModule extends NativeModule<ExpoAirModuleEvents> {\n PI = Math.PI;\n async setValueAsync(value: string): Promise<void> {\n this.emit('onChange', { value });\n }\n hello() {\n return 'Hello world! 👋';\n }\n}\n\nexport default registerWebModule(ExpoAirModule, 'ExpoAirModule');\n"]}
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+ import { ExpoAirViewProps } from './ExpoAir.types';
3
+ export default function ExpoAirView(props: ExpoAirViewProps): React.JSX.Element;
4
+ //# sourceMappingURL=ExpoAirView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirView.d.ts","sourceRoot":"","sources":["../src/ExpoAirView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKnD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,KAAK,EAAE,gBAAgB,qBAE1D"}
@@ -0,0 +1,7 @@
1
+ import { requireNativeView } from 'expo';
2
+ import * as React from 'react';
3
+ const NativeView = requireNativeView('ExpoAir');
4
+ export default function ExpoAirView(props) {
5
+ return <NativeView {...props}/>;
6
+ }
7
+ //# sourceMappingURL=ExpoAirView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirView.js","sourceRoot":"","sources":["../src/ExpoAirView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,UAAU,GACd,iBAAiB,CAAC,SAAS,CAAC,CAAC;AAE/B,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,KAAuB;IACzD,OAAO,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACnC,CAAC","sourcesContent":["import { requireNativeView } from 'expo';\nimport * as React from 'react';\n\nimport { ExpoAirViewProps } from './ExpoAir.types';\n\nconst NativeView: React.ComponentType<ExpoAirViewProps> =\n requireNativeView('ExpoAir');\n\nexport default function ExpoAirView(props: ExpoAirViewProps) {\n return <NativeView {...props} />;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+ import { ExpoAirViewProps } from './ExpoAir.types';
3
+ export default function ExpoAirView(props: ExpoAirViewProps): React.JSX.Element;
4
+ //# sourceMappingURL=ExpoAirView.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirView.web.d.ts","sourceRoot":"","sources":["../src/ExpoAirView.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,KAAK,EAAE,gBAAgB,qBAU1D"}
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ export default function ExpoAirView(props) {
3
+ return (<div>
4
+ <iframe style={{ flex: 1 }} src={props.url} onLoad={() => props.onLoad({ nativeEvent: { url: props.url } })}/>
5
+ </div>);
6
+ }
7
+ //# sourceMappingURL=ExpoAirView.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoAirView.web.js","sourceRoot":"","sources":["../src/ExpoAirView.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,KAAuB;IACzD,OAAO,CACL,CAAC,GAAG,CACF;MAAA,CAAC,MAAM,CACL,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CACnB,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CACf,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAEpE;IAAA,EAAE,GAAG,CAAC,CACP,CAAC;AACJ,CAAC","sourcesContent":["import * as React from 'react';\n\nimport { ExpoAirViewProps } from './ExpoAir.types';\n\nexport default function ExpoAirView(props: ExpoAirViewProps) {\n return (\n <div>\n <iframe\n style={{ flex: 1 }}\n src={props.url}\n onLoad={() => props.onLoad({ nativeEvent: { url: props.url } })}\n />\n </div>\n );\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export { default } from './ExpoAirModule';
2
+ export { default as ExpoAirView } from './ExpoAirView';
3
+ export * from './ExpoAir.types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,cAAc,iBAAiB,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // Reexport the native module. On web, it will be resolved to ExpoAirModule.web.ts
2
+ // and on native platforms to ExpoAirModule.ts
3
+ export { default } from './ExpoAirModule';
4
+ export { default as ExpoAirView } from './ExpoAirView';
5
+ export * from './ExpoAir.types';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,8CAA8C;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,cAAc,iBAAiB,CAAC","sourcesContent":["// Reexport the native module. On web, it will be resolved to ExpoAirModule.web.ts\n// and on native platforms to ExpoAirModule.ts\nexport { default } from './ExpoAirModule';\nexport { default as ExpoAirView } from './ExpoAirView';\nexport * from './ExpoAir.types';\n"]}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=expo-air.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-air.d.ts","sourceRoot":"","sources":["../../bin/expo-air.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { startCommand } from "../commands/start.js";
4
+ import { serverCommand } from "../commands/server.js";
5
+ import { initCommand } from "../commands/init.js";
6
+ import { flyCommand } from "../commands/fly.js";
7
+ const program = new Command();
8
+ program
9
+ .name("expo-air")
10
+ .description("Vibe Coding for React-Native - Mobile assistant for Claude Code")
11
+ .version("0.1.0");
12
+ program
13
+ .command("init")
14
+ .description("Initialize expo-air in your Expo project")
15
+ .option("-f, --force", "Overwrite existing configuration")
16
+ .option("--skip-prebuild", "Skip running expo prebuild")
17
+ .action(initCommand);
18
+ program
19
+ .command("start")
20
+ .description("Start the development environment")
21
+ .option("-p, --port <port>", "Port for prompt server", "3847")
22
+ .option("-w, --widget-port <port>", "Port for widget Metro server", "8082")
23
+ .option("-m, --metro-port <port>", "Port for main app Metro server", "8081")
24
+ .option("--project <path>", "Path to Expo project (where Claude makes changes)")
25
+ .option("--no-tunnel", "Skip tunnel (local network only)")
26
+ .option("--no-build", "Skip building and installing the app")
27
+ .option("--no-server", "Skip starting the WebSocket server")
28
+ .action(startCommand);
29
+ program
30
+ .command("server")
31
+ .description("Start only the WebSocket server (for dev mode with watch)")
32
+ .option("-p, --port <port>", "Port for prompt server", "3847")
33
+ .option("--project <path>", "Path to Expo project (where Claude makes changes)")
34
+ .action(serverCommand);
35
+ program
36
+ .command("fly")
37
+ .description("✈️ Start everything + build and install on a real iOS device")
38
+ .option("-p, --port <port>", "Port for prompt server", "3847")
39
+ .option("-w, --widget-port <port>", "Port for widget Metro server", "8082")
40
+ .option("-m, --metro-port <port>", "Port for main app Metro server", "8081")
41
+ .option("--project <path>", "Path to Expo project")
42
+ .option("--device <id>", "Device UDID or name to use")
43
+ .option("--no-tunnel", "Skip tunnel (local network only)")
44
+ .action(flyCommand);
45
+ // Default command (just running `expo-air` starts everything)
46
+ program
47
+ .action(() => {
48
+ program.commands.find((cmd) => cmd.name() === "start")?.parse();
49
+ });
50
+ program.parse();
51
+ //# sourceMappingURL=expo-air.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-air.js","sourceRoot":"","sources":["../../bin/expo-air.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,iEAAiE,CAAC;KAC9E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,aAAa,EAAE,kCAAkC,CAAC;KACzD,MAAM,CAAC,iBAAiB,EAAE,4BAA4B,CAAC;KACvD,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,CAAC;KAC7D,MAAM,CAAC,0BAA0B,EAAE,8BAA8B,EAAE,MAAM,CAAC;KAC1E,MAAM,CAAC,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,CAAC;KAC3E,MAAM,CAAC,kBAAkB,EAAE,mDAAmD,CAAC;KAC/E,MAAM,CAAC,aAAa,EAAE,kCAAkC,CAAC;KACzD,MAAM,CAAC,YAAY,EAAE,sCAAsC,CAAC;KAC5D,MAAM,CAAC,aAAa,EAAE,oCAAoC,CAAC;KAC3D,MAAM,CAAC,YAAY,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,2DAA2D,CAAC;KACxE,MAAM,CAAC,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,CAAC;KAC7D,MAAM,CAAC,kBAAkB,EAAE,mDAAmD,CAAC;KAC/E,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,+DAA+D,CAAC;KAC5E,MAAM,CAAC,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,CAAC;KAC7D,MAAM,CAAC,0BAA0B,EAAE,8BAA8B,EAAE,MAAM,CAAC;KAC1E,MAAM,CAAC,yBAAyB,EAAE,gCAAgC,EAAE,MAAM,CAAC;KAC3E,MAAM,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;KAClD,MAAM,CAAC,eAAe,EAAE,4BAA4B,CAAC;KACrD,MAAM,CAAC,aAAa,EAAE,kCAAkC,CAAC;KACzD,MAAM,CAAC,UAAU,CAAC,CAAC;AAEtB,8DAA8D;AAC9D,OAAO;KACJ,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;AAClE,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,11 @@
1
+ interface FlyOptions {
2
+ port: string;
3
+ tunnel: boolean;
4
+ widgetPort?: string;
5
+ metroPort?: string;
6
+ project?: string;
7
+ device?: string;
8
+ }
9
+ export declare function flyCommand(options: FlyOptions): Promise<void>;
10
+ export {};
11
+ //# sourceMappingURL=fly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../commands/fly.ts"],"names":[],"mappings":"AA2IA,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA8OnE"}