@flightdev/mobile 0.2.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.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/config.d.ts +243 -0
- package/dist/config.js +37 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/platform-Cq72g4Qx.d.ts +68 -0
- package/dist/plugins/index.d.ts +52 -0
- package/dist/plugins/index.js +104 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/react/index.d.ts +259 -0
- package/dist/react/index.js +332 -0
- package/dist/react/index.js.map +1 -0
- package/dist/vue/index.d.ts +82 -0
- package/dist/vue/index.js +208 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +98 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Flight Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# @flight-framework/mobile
|
|
2
|
+
|
|
3
|
+
Mobile app support for Flight Framework via Capacitor. Build iOS and Android apps with the same codebase.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**Flight doesn't impose** - install only the Capacitor plugins you need. All plugins are optional peer dependencies. Hooks gracefully degrade on web.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Cross-platform** - iOS, Android, and Web from one codebase
|
|
12
|
+
- **React hooks** - usePlatform, useCamera, useGeolocation, and more
|
|
13
|
+
- **Vue composables** - Reactive wrappers with automatic cleanup
|
|
14
|
+
- **Plugin freedom** - Install only what you need
|
|
15
|
+
- **Web fallback** - Graceful degradation, no errors
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Core package
|
|
21
|
+
npm install @flight-framework/mobile
|
|
22
|
+
|
|
23
|
+
# Install Capacitor (required for native)
|
|
24
|
+
npm install @capacitor/core @capacitor/cli
|
|
25
|
+
|
|
26
|
+
# Install only the plugins you need
|
|
27
|
+
npm install @capacitor/camera # Optional
|
|
28
|
+
npm install @capacitor/geolocation # Optional
|
|
29
|
+
npm install @capacitor/push-notifications # Optional
|
|
30
|
+
npm install @capacitor/haptics # Optional
|
|
31
|
+
npm install @capacitor/preferences # Optional
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### React
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { usePlatform, useCamera, useGeolocation } from '@flight-framework/mobile/react';
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
const { isNative, isIOS, isAndroid } = usePlatform();
|
|
43
|
+
const { takePhoto, available: cameraAvailable } = useCamera();
|
|
44
|
+
const { position, getCurrentPosition } = useGeolocation();
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div>
|
|
48
|
+
<p>Platform: {isIOS ? 'iOS' : isAndroid ? 'Android' : 'Web'}</p>
|
|
49
|
+
|
|
50
|
+
{cameraAvailable && (
|
|
51
|
+
<button onClick={() => takePhoto()}>Take Photo</button>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
<button onClick={getCurrentPosition}>Get Location</button>
|
|
55
|
+
{position && <p>Lat: {position.coords.latitude}</p>}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Vue
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<script setup>
|
|
65
|
+
import { usePlatform, useCamera, useGeolocation } from '@flight-framework/mobile/vue';
|
|
66
|
+
|
|
67
|
+
const { isNative, isIOS } = usePlatform();
|
|
68
|
+
const { takePhoto, available } = useCamera();
|
|
69
|
+
const { position, getCurrentPosition, loading } = useGeolocation();
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<p>Platform: {{ isIOS ? 'iOS' : 'Web' }}</p>
|
|
74
|
+
<button v-if="available" @click="takePhoto">Take Photo</button>
|
|
75
|
+
<button @click="getCurrentPosition">Get Location</button>
|
|
76
|
+
</template>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Hooks
|
|
80
|
+
|
|
81
|
+
### usePlatform
|
|
82
|
+
|
|
83
|
+
Detect current platform.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const { platform, isNative, isIOS, isAndroid, isWeb } = usePlatform();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### useCamera
|
|
90
|
+
|
|
91
|
+
Take photos and pick images.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const { takePhoto, pickImage, available } = useCamera();
|
|
95
|
+
|
|
96
|
+
const photo = await takePhoto({ quality: 90 });
|
|
97
|
+
const image = await pickImage();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### useGeolocation
|
|
101
|
+
|
|
102
|
+
Get device location.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const { position, error, loading, getCurrentPosition, watchPosition } = useGeolocation();
|
|
106
|
+
|
|
107
|
+
await getCurrentPosition();
|
|
108
|
+
console.log(position.coords.latitude, position.coords.longitude);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### usePushNotifications
|
|
112
|
+
|
|
113
|
+
Handle push notifications.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const { token, requestPermission, onNotificationReceived } = usePushNotifications();
|
|
117
|
+
|
|
118
|
+
await requestPermission();
|
|
119
|
+
console.log('FCM Token:', token);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### useHaptics
|
|
123
|
+
|
|
124
|
+
Haptic feedback.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const { impact, notification, vibrate } = useHaptics();
|
|
128
|
+
|
|
129
|
+
await impact('medium');
|
|
130
|
+
await notification('success');
|
|
131
|
+
await vibrate(300);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### useStorage
|
|
135
|
+
|
|
136
|
+
Secure key-value storage.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const { get, set, remove } = useStorage();
|
|
140
|
+
|
|
141
|
+
await set('theme', 'dark');
|
|
142
|
+
const theme = await get('theme');
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
Create `capacitor.config.ts`:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { defineCapacitorConfig } from '@flight-framework/mobile/config';
|
|
151
|
+
|
|
152
|
+
export default defineCapacitorConfig({
|
|
153
|
+
appId: 'com.example.myapp',
|
|
154
|
+
appName: 'My App',
|
|
155
|
+
webDir: 'dist',
|
|
156
|
+
plugins: {
|
|
157
|
+
PushNotifications: {
|
|
158
|
+
presentationOptions: ['badge', 'sound', 'alert'],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Build Commands
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Add platforms
|
|
168
|
+
npx cap add ios
|
|
169
|
+
npx cap add android
|
|
170
|
+
|
|
171
|
+
# Build web assets
|
|
172
|
+
npm run build
|
|
173
|
+
|
|
174
|
+
# Sync to native projects
|
|
175
|
+
npx cap sync
|
|
176
|
+
|
|
177
|
+
# Open native IDE
|
|
178
|
+
npx cap open ios
|
|
179
|
+
npx cap open android
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capacitor configuration helper.
|
|
3
|
+
* Creates type-safe configuration for mobile builds.
|
|
4
|
+
*/
|
|
5
|
+
interface MobileConfig {
|
|
6
|
+
/** Bundle identifier (com.example.app) */
|
|
7
|
+
appId: string;
|
|
8
|
+
/** Display name of the app */
|
|
9
|
+
appName: string;
|
|
10
|
+
/** Directory containing the web assets (default: dist) */
|
|
11
|
+
webDir?: string;
|
|
12
|
+
/** Server configuration for live reload */
|
|
13
|
+
server?: {
|
|
14
|
+
url?: string;
|
|
15
|
+
cleartext?: boolean;
|
|
16
|
+
};
|
|
17
|
+
/** iOS-specific configuration */
|
|
18
|
+
ios?: {
|
|
19
|
+
/** Path to iOS project (default: ios) */
|
|
20
|
+
path?: string;
|
|
21
|
+
/** Scheme for deep links */
|
|
22
|
+
scheme?: string;
|
|
23
|
+
/** Content inset mode */
|
|
24
|
+
contentInset?: 'always' | 'scrollable' | 'automatic' | 'never';
|
|
25
|
+
/** Background color of web view */
|
|
26
|
+
backgroundColor?: string;
|
|
27
|
+
/** Preferred content mode */
|
|
28
|
+
preferredContentMode?: 'mobile' | 'desktop' | 'recommended';
|
|
29
|
+
};
|
|
30
|
+
/** Android-specific configuration */
|
|
31
|
+
android?: {
|
|
32
|
+
/** Path to Android project (default: android) */
|
|
33
|
+
path?: string;
|
|
34
|
+
/** Flavor for build variants */
|
|
35
|
+
flavor?: string;
|
|
36
|
+
/** Background color of web view */
|
|
37
|
+
backgroundColor?: string;
|
|
38
|
+
/** Allow mixed content */
|
|
39
|
+
allowMixedContent?: boolean;
|
|
40
|
+
/** Initial focus on web view */
|
|
41
|
+
initialFocus?: boolean;
|
|
42
|
+
};
|
|
43
|
+
/** Plugin configurations */
|
|
44
|
+
plugins?: {
|
|
45
|
+
/** Push notifications config */
|
|
46
|
+
PushNotifications?: {
|
|
47
|
+
presentationOptions?: ('badge' | 'sound' | 'alert')[];
|
|
48
|
+
};
|
|
49
|
+
/** Splash screen config */
|
|
50
|
+
SplashScreen?: {
|
|
51
|
+
launchShowDuration?: number;
|
|
52
|
+
launchAutoHide?: boolean;
|
|
53
|
+
launchFadeOutDuration?: number;
|
|
54
|
+
backgroundColor?: string;
|
|
55
|
+
androidSplashResourceName?: string;
|
|
56
|
+
androidScaleType?: 'CENTER' | 'CENTER_CROP' | 'CENTER_INSIDE' | 'FIT_CENTER' | 'FIT_XY';
|
|
57
|
+
showSpinner?: boolean;
|
|
58
|
+
spinnerStyle?: 'horizontal' | 'small' | 'large';
|
|
59
|
+
spinnerColor?: string;
|
|
60
|
+
};
|
|
61
|
+
/** Local notifications config */
|
|
62
|
+
LocalNotifications?: {
|
|
63
|
+
smallIcon?: string;
|
|
64
|
+
iconColor?: string;
|
|
65
|
+
sound?: string;
|
|
66
|
+
};
|
|
67
|
+
/** Keyboard config */
|
|
68
|
+
Keyboard?: {
|
|
69
|
+
resize?: 'body' | 'ionic' | 'native' | 'none';
|
|
70
|
+
style?: 'dark' | 'light' | 'default';
|
|
71
|
+
resizeOnFullScreen?: boolean;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a Capacitor configuration.
|
|
77
|
+
*
|
|
78
|
+
* Use this in your capacitor.config.ts file:
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* // capacitor.config.ts
|
|
83
|
+
* import { defineCapacitorConfig } from '@flightdev/mobile/config';
|
|
84
|
+
*
|
|
85
|
+
* export default defineCapacitorConfig({
|
|
86
|
+
* appId: 'com.example.myapp',
|
|
87
|
+
* appName: 'My App',
|
|
88
|
+
* webDir: 'dist',
|
|
89
|
+
* plugins: {
|
|
90
|
+
* PushNotifications: {
|
|
91
|
+
* presentationOptions: ['badge', 'sound', 'alert'],
|
|
92
|
+
* },
|
|
93
|
+
* },
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare function defineCapacitorConfig(config: MobileConfig): {
|
|
98
|
+
appId: string;
|
|
99
|
+
appName: string;
|
|
100
|
+
webDir: string;
|
|
101
|
+
server: {
|
|
102
|
+
url?: string;
|
|
103
|
+
cleartext?: boolean;
|
|
104
|
+
} | undefined;
|
|
105
|
+
ios: {
|
|
106
|
+
path: string;
|
|
107
|
+
scheme?: string;
|
|
108
|
+
contentInset?: "always" | "scrollable" | "automatic" | "never";
|
|
109
|
+
backgroundColor?: string;
|
|
110
|
+
preferredContentMode?: "mobile" | "desktop" | "recommended";
|
|
111
|
+
};
|
|
112
|
+
android: {
|
|
113
|
+
path: string;
|
|
114
|
+
flavor?: string;
|
|
115
|
+
backgroundColor?: string;
|
|
116
|
+
allowMixedContent?: boolean;
|
|
117
|
+
initialFocus?: boolean;
|
|
118
|
+
};
|
|
119
|
+
plugins: {
|
|
120
|
+
/** Push notifications config */
|
|
121
|
+
PushNotifications?: {
|
|
122
|
+
presentationOptions?: ("badge" | "sound" | "alert")[];
|
|
123
|
+
};
|
|
124
|
+
/** Splash screen config */
|
|
125
|
+
SplashScreen?: {
|
|
126
|
+
launchShowDuration?: number;
|
|
127
|
+
launchAutoHide?: boolean;
|
|
128
|
+
launchFadeOutDuration?: number;
|
|
129
|
+
backgroundColor?: string;
|
|
130
|
+
androidSplashResourceName?: string;
|
|
131
|
+
androidScaleType?: "CENTER" | "CENTER_CROP" | "CENTER_INSIDE" | "FIT_CENTER" | "FIT_XY";
|
|
132
|
+
showSpinner?: boolean;
|
|
133
|
+
spinnerStyle?: "horizontal" | "small" | "large";
|
|
134
|
+
spinnerColor?: string;
|
|
135
|
+
};
|
|
136
|
+
/** Local notifications config */
|
|
137
|
+
LocalNotifications?: {
|
|
138
|
+
smallIcon?: string;
|
|
139
|
+
iconColor?: string;
|
|
140
|
+
sound?: string;
|
|
141
|
+
};
|
|
142
|
+
/** Keyboard config */
|
|
143
|
+
Keyboard?: {
|
|
144
|
+
resize?: "body" | "ionic" | "native" | "none";
|
|
145
|
+
style?: "dark" | "light" | "default";
|
|
146
|
+
resizeOnFullScreen?: boolean;
|
|
147
|
+
};
|
|
148
|
+
} | undefined;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Create Flight-specific mobile configuration.
|
|
152
|
+
* Extends Capacitor config with Flight-specific options.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* // flight.config.ts
|
|
157
|
+
* import { defineMobileConfig } from '@flightdev/mobile/config';
|
|
158
|
+
*
|
|
159
|
+
* export default {
|
|
160
|
+
* mobile: defineMobileConfig({
|
|
161
|
+
* appId: 'com.example.myapp',
|
|
162
|
+
* appName: 'My App',
|
|
163
|
+
* }),
|
|
164
|
+
* };
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
declare function defineMobileConfig(config: MobileConfig): {
|
|
168
|
+
_flight: true;
|
|
169
|
+
/** Bundle identifier (com.example.app) */
|
|
170
|
+
appId: string;
|
|
171
|
+
/** Display name of the app */
|
|
172
|
+
appName: string;
|
|
173
|
+
/** Directory containing the web assets (default: dist) */
|
|
174
|
+
webDir?: string;
|
|
175
|
+
/** Server configuration for live reload */
|
|
176
|
+
server?: {
|
|
177
|
+
url?: string;
|
|
178
|
+
cleartext?: boolean;
|
|
179
|
+
};
|
|
180
|
+
/** iOS-specific configuration */
|
|
181
|
+
ios?: {
|
|
182
|
+
/** Path to iOS project (default: ios) */
|
|
183
|
+
path?: string;
|
|
184
|
+
/** Scheme for deep links */
|
|
185
|
+
scheme?: string;
|
|
186
|
+
/** Content inset mode */
|
|
187
|
+
contentInset?: "always" | "scrollable" | "automatic" | "never";
|
|
188
|
+
/** Background color of web view */
|
|
189
|
+
backgroundColor?: string;
|
|
190
|
+
/** Preferred content mode */
|
|
191
|
+
preferredContentMode?: "mobile" | "desktop" | "recommended";
|
|
192
|
+
};
|
|
193
|
+
/** Android-specific configuration */
|
|
194
|
+
android?: {
|
|
195
|
+
/** Path to Android project (default: android) */
|
|
196
|
+
path?: string;
|
|
197
|
+
/** Flavor for build variants */
|
|
198
|
+
flavor?: string;
|
|
199
|
+
/** Background color of web view */
|
|
200
|
+
backgroundColor?: string;
|
|
201
|
+
/** Allow mixed content */
|
|
202
|
+
allowMixedContent?: boolean;
|
|
203
|
+
/** Initial focus on web view */
|
|
204
|
+
initialFocus?: boolean;
|
|
205
|
+
};
|
|
206
|
+
/** Plugin configurations */
|
|
207
|
+
plugins?: {
|
|
208
|
+
/** Push notifications config */
|
|
209
|
+
PushNotifications?: {
|
|
210
|
+
presentationOptions?: ("badge" | "sound" | "alert")[];
|
|
211
|
+
};
|
|
212
|
+
/** Splash screen config */
|
|
213
|
+
SplashScreen?: {
|
|
214
|
+
launchShowDuration?: number;
|
|
215
|
+
launchAutoHide?: boolean;
|
|
216
|
+
launchFadeOutDuration?: number;
|
|
217
|
+
backgroundColor?: string;
|
|
218
|
+
androidSplashResourceName?: string;
|
|
219
|
+
androidScaleType?: "CENTER" | "CENTER_CROP" | "CENTER_INSIDE" | "FIT_CENTER" | "FIT_XY";
|
|
220
|
+
showSpinner?: boolean;
|
|
221
|
+
spinnerStyle?: "horizontal" | "small" | "large";
|
|
222
|
+
spinnerColor?: string;
|
|
223
|
+
};
|
|
224
|
+
/** Local notifications config */
|
|
225
|
+
LocalNotifications?: {
|
|
226
|
+
smallIcon?: string;
|
|
227
|
+
iconColor?: string;
|
|
228
|
+
sound?: string;
|
|
229
|
+
};
|
|
230
|
+
/** Keyboard config */
|
|
231
|
+
Keyboard?: {
|
|
232
|
+
resize?: "body" | "ionic" | "native" | "none";
|
|
233
|
+
style?: "dark" | "light" | "default";
|
|
234
|
+
resizeOnFullScreen?: boolean;
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Generate capacitor.config.ts content from Flight config.
|
|
240
|
+
*/
|
|
241
|
+
declare function generateCapacitorConfig(config: MobileConfig): string;
|
|
242
|
+
|
|
243
|
+
export { type MobileConfig, defineCapacitorConfig, defineMobileConfig, generateCapacitorConfig };
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
function defineCapacitorConfig(config) {
|
|
3
|
+
return {
|
|
4
|
+
appId: config.appId,
|
|
5
|
+
appName: config.appName,
|
|
6
|
+
webDir: config.webDir ?? "dist",
|
|
7
|
+
server: config.server,
|
|
8
|
+
ios: {
|
|
9
|
+
path: "ios",
|
|
10
|
+
...config.ios
|
|
11
|
+
},
|
|
12
|
+
android: {
|
|
13
|
+
path: "android",
|
|
14
|
+
...config.android
|
|
15
|
+
},
|
|
16
|
+
plugins: config.plugins
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function defineMobileConfig(config) {
|
|
20
|
+
return {
|
|
21
|
+
...config,
|
|
22
|
+
_flight: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function generateCapacitorConfig(config) {
|
|
26
|
+
const configObj = defineCapacitorConfig(config);
|
|
27
|
+
return `import type { CapacitorConfig } from '@capacitor/cli';
|
|
28
|
+
|
|
29
|
+
const config: CapacitorConfig = ${JSON.stringify(configObj, null, 2)};
|
|
30
|
+
|
|
31
|
+
export default config;
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { defineCapacitorConfig, defineMobileConfig, generateCapacitorConfig };
|
|
36
|
+
//# sourceMappingURL=config.js.map
|
|
37
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"names":[],"mappings":";AA0GO,SAAS,sBAAsB,MAAA,EAAsB;AACxD,EAAA,OAAO;AAAA,IACH,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,MAAA,EAAQ,OAAO,MAAA,IAAU,MAAA;AAAA,IACzB,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,GAAA,EAAK;AAAA,MACD,IAAA,EAAM,KAAA;AAAA,MACN,GAAG,MAAA,CAAO;AAAA,KACd;AAAA,IACA,OAAA,EAAS;AAAA,MACL,IAAA,EAAM,SAAA;AAAA,MACN,GAAG,MAAA,CAAO;AAAA,KACd;AAAA,IACA,SAAS,MAAA,CAAO;AAAA,GACpB;AACJ;AAmBO,SAAS,mBAAmB,MAAA,EAAsB;AACrD,EAAA,OAAO;AAAA,IACH,GAAG,MAAA;AAAA,IACH,OAAA,EAAS;AAAA,GACb;AACJ;AASO,SAAS,wBAAwB,MAAA,EAA8B;AAClE,EAAA,MAAM,SAAA,GAAY,sBAAsB,MAAM,CAAA;AAE9C,EAAA,OAAO,CAAA;;AAAA,gCAAA,EAEuB,IAAA,CAAK,SAAA,CAAU,SAAA,EAAW,IAAA,EAAM,CAAC,CAAC,CAAA;;AAAA;AAAA,CAAA;AAIpE","file":"config.js","sourcesContent":["/**\r\n * Capacitor configuration helper.\r\n * Creates type-safe configuration for mobile builds.\r\n */\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface MobileConfig {\r\n /** Bundle identifier (com.example.app) */\r\n appId: string;\r\n /** Display name of the app */\r\n appName: string;\r\n /** Directory containing the web assets (default: dist) */\r\n webDir?: string;\r\n /** Server configuration for live reload */\r\n server?: {\r\n url?: string;\r\n cleartext?: boolean;\r\n };\r\n /** iOS-specific configuration */\r\n ios?: {\r\n /** Path to iOS project (default: ios) */\r\n path?: string;\r\n /** Scheme for deep links */\r\n scheme?: string;\r\n /** Content inset mode */\r\n contentInset?: 'always' | 'scrollable' | 'automatic' | 'never';\r\n /** Background color of web view */\r\n backgroundColor?: string;\r\n /** Preferred content mode */\r\n preferredContentMode?: 'mobile' | 'desktop' | 'recommended';\r\n };\r\n /** Android-specific configuration */\r\n android?: {\r\n /** Path to Android project (default: android) */\r\n path?: string;\r\n /** Flavor for build variants */\r\n flavor?: string;\r\n /** Background color of web view */\r\n backgroundColor?: string;\r\n /** Allow mixed content */\r\n allowMixedContent?: boolean;\r\n /** Initial focus on web view */\r\n initialFocus?: boolean;\r\n };\r\n /** Plugin configurations */\r\n plugins?: {\r\n /** Push notifications config */\r\n PushNotifications?: {\r\n presentationOptions?: ('badge' | 'sound' | 'alert')[];\r\n };\r\n /** Splash screen config */\r\n SplashScreen?: {\r\n launchShowDuration?: number;\r\n launchAutoHide?: boolean;\r\n launchFadeOutDuration?: number;\r\n backgroundColor?: string;\r\n androidSplashResourceName?: string;\r\n androidScaleType?: 'CENTER' | 'CENTER_CROP' | 'CENTER_INSIDE' | 'FIT_CENTER' | 'FIT_XY';\r\n showSpinner?: boolean;\r\n spinnerStyle?: 'horizontal' | 'small' | 'large';\r\n spinnerColor?: string;\r\n };\r\n /** Local notifications config */\r\n LocalNotifications?: {\r\n smallIcon?: string;\r\n iconColor?: string;\r\n sound?: string;\r\n };\r\n /** Keyboard config */\r\n Keyboard?: {\r\n resize?: 'body' | 'ionic' | 'native' | 'none';\r\n style?: 'dark' | 'light' | 'default';\r\n resizeOnFullScreen?: boolean;\r\n };\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Factory\r\n// =============================================================================\r\n\r\n/**\r\n * Create a Capacitor configuration.\r\n * \r\n * Use this in your capacitor.config.ts file:\r\n * \r\n * @example\r\n * ```typescript\r\n * // capacitor.config.ts\r\n * import { defineCapacitorConfig } from '@flightdev/mobile/config';\r\n * \r\n * export default defineCapacitorConfig({\r\n * appId: 'com.example.myapp',\r\n * appName: 'My App',\r\n * webDir: 'dist',\r\n * plugins: {\r\n * PushNotifications: {\r\n * presentationOptions: ['badge', 'sound', 'alert'],\r\n * },\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport function defineCapacitorConfig(config: MobileConfig) {\r\n return {\r\n appId: config.appId,\r\n appName: config.appName,\r\n webDir: config.webDir ?? 'dist',\r\n server: config.server,\r\n ios: {\r\n path: 'ios',\r\n ...config.ios,\r\n },\r\n android: {\r\n path: 'android',\r\n ...config.android,\r\n },\r\n plugins: config.plugins,\r\n };\r\n}\r\n\r\n/**\r\n * Create Flight-specific mobile configuration.\r\n * Extends Capacitor config with Flight-specific options.\r\n * \r\n * @example\r\n * ```typescript\r\n * // flight.config.ts\r\n * import { defineMobileConfig } from '@flightdev/mobile/config';\r\n * \r\n * export default {\r\n * mobile: defineMobileConfig({\r\n * appId: 'com.example.myapp',\r\n * appName: 'My App',\r\n * }),\r\n * };\r\n * ```\r\n */\r\nexport function defineMobileConfig(config: MobileConfig) {\r\n return {\r\n ...config,\r\n _flight: true as const,\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Utilities\r\n// =============================================================================\r\n\r\n/**\r\n * Generate capacitor.config.ts content from Flight config.\r\n */\r\nexport function generateCapacitorConfig(config: MobileConfig): string {\r\n const configObj = defineCapacitorConfig(config);\r\n\r\n return `import type { CapacitorConfig } from '@capacitor/cli';\r\n\r\nconst config: CapacitorConfig = ${JSON.stringify(configObj, null, 2)};\r\n\r\nexport default config;\r\n`;\r\n}\r\n"]}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/platform.ts
|
|
2
|
+
function getCapacitor() {
|
|
3
|
+
try {
|
|
4
|
+
const Capacitor = globalThis.Capacitor;
|
|
5
|
+
return Capacitor || null;
|
|
6
|
+
} catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function getPlatform() {
|
|
11
|
+
const Capacitor = getCapacitor();
|
|
12
|
+
if (!Capacitor) {
|
|
13
|
+
return {
|
|
14
|
+
platform: "web",
|
|
15
|
+
isNative: false,
|
|
16
|
+
isIOS: false,
|
|
17
|
+
isAndroid: false,
|
|
18
|
+
isWeb: true,
|
|
19
|
+
hasCapacitor: false
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const platform = Capacitor.getPlatform();
|
|
23
|
+
const isNative = Capacitor.isNativePlatform();
|
|
24
|
+
return {
|
|
25
|
+
platform,
|
|
26
|
+
isNative,
|
|
27
|
+
isIOS: platform === "ios",
|
|
28
|
+
isAndroid: platform === "android",
|
|
29
|
+
isWeb: platform === "web",
|
|
30
|
+
hasCapacitor: true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function isPluginAvailable(pluginName) {
|
|
34
|
+
const Capacitor = getCapacitor();
|
|
35
|
+
if (!Capacitor || !Capacitor.isPluginAvailable) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return Capacitor.isPluginAvailable(pluginName);
|
|
39
|
+
}
|
|
40
|
+
function getSafeAreaInsets() {
|
|
41
|
+
if (typeof window !== "undefined" && typeof window.getComputedStyle === "function") {
|
|
42
|
+
const style = getComputedStyle(document.documentElement);
|
|
43
|
+
return {
|
|
44
|
+
top: parseInt(style.getPropertyValue("--sat") || "0", 10),
|
|
45
|
+
bottom: parseInt(style.getPropertyValue("--sab") || "0", 10),
|
|
46
|
+
left: parseInt(style.getPropertyValue("--sal") || "0", 10),
|
|
47
|
+
right: parseInt(style.getPropertyValue("--sar") || "0", 10)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { top: 0, bottom: 0, left: 0, right: 0 };
|
|
51
|
+
}
|
|
52
|
+
function isPWA() {
|
|
53
|
+
if (typeof window === "undefined") return false;
|
|
54
|
+
return window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/config.ts
|
|
58
|
+
function defineCapacitorConfig(config) {
|
|
59
|
+
return {
|
|
60
|
+
appId: config.appId,
|
|
61
|
+
appName: config.appName,
|
|
62
|
+
webDir: config.webDir ?? "dist",
|
|
63
|
+
server: config.server,
|
|
64
|
+
ios: {
|
|
65
|
+
path: "ios",
|
|
66
|
+
...config.ios
|
|
67
|
+
},
|
|
68
|
+
android: {
|
|
69
|
+
path: "android",
|
|
70
|
+
...config.android
|
|
71
|
+
},
|
|
72
|
+
plugins: config.plugins
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function defineMobileConfig(config) {
|
|
76
|
+
return {
|
|
77
|
+
...config,
|
|
78
|
+
_flight: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function generateCapacitorConfig(config) {
|
|
82
|
+
const configObj = defineCapacitorConfig(config);
|
|
83
|
+
return `import type { CapacitorConfig } from '@capacitor/cli';
|
|
84
|
+
|
|
85
|
+
const config: CapacitorConfig = ${JSON.stringify(configObj, null, 2)};
|
|
86
|
+
|
|
87
|
+
export default config;
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { defineCapacitorConfig, defineMobileConfig, generateCapacitorConfig, getPlatform, getSafeAreaInsets, isPWA, isPluginAvailable };
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
93
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platform.ts","../src/config.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Platform detection utilities.\r\n * Works without Capacitor installed - gracefully degrades.\r\n */\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport type Platform = 'web' | 'ios' | 'android';\r\n\r\nexport interface PlatformInfo {\r\n /** Current platform: 'web', 'ios', or 'android' */\r\n platform: Platform;\r\n /** True if running as a native app (iOS or Android) */\r\n isNative: boolean;\r\n /** True if running on iOS */\r\n isIOS: boolean;\r\n /** True if running on Android */\r\n isAndroid: boolean;\r\n /** True if running as a web app */\r\n isWeb: boolean;\r\n /** True if Capacitor is available */\r\n hasCapacitor: boolean;\r\n}\r\n\r\n// =============================================================================\r\n// Detection\r\n// =============================================================================\r\n\r\n/**\r\n * Get Capacitor instance if available.\r\n * Returns null if Capacitor is not installed.\r\n */\r\nfunction getCapacitor(): any {\r\n try {\r\n // Dynamic import to avoid errors when Capacitor is not installed\r\n const Capacitor = (globalThis as any).Capacitor;\r\n return Capacitor || null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Detect the current platform.\r\n * \r\n * @returns Platform information object\r\n * \r\n * @example\r\n * ```typescript\r\n * const { isNative, isIOS, platform } = getPlatform();\r\n * \r\n * if (isNative) {\r\n * // Use native features\r\n * }\r\n * \r\n * if (isIOS) {\r\n * // iOS-specific code\r\n * }\r\n * ```\r\n */\r\nexport function getPlatform(): PlatformInfo {\r\n const Capacitor = getCapacitor();\r\n\r\n if (!Capacitor) {\r\n return {\r\n platform: 'web',\r\n isNative: false,\r\n isIOS: false,\r\n isAndroid: false,\r\n isWeb: true,\r\n hasCapacitor: false,\r\n };\r\n }\r\n\r\n const platform = Capacitor.getPlatform() as Platform;\r\n const isNative = Capacitor.isNativePlatform();\r\n\r\n return {\r\n platform,\r\n isNative,\r\n isIOS: platform === 'ios',\r\n isAndroid: platform === 'android',\r\n isWeb: platform === 'web',\r\n hasCapacitor: true,\r\n };\r\n}\r\n\r\n/**\r\n * Check if a Capacitor plugin is available.\r\n * \r\n * @param pluginName - Name of the plugin to check\r\n * @returns True if the plugin is registered\r\n * \r\n * @example\r\n * ```typescript\r\n * if (isPluginAvailable('Camera')) {\r\n * // Camera plugin is installed\r\n * }\r\n * ```\r\n */\r\nexport function isPluginAvailable(pluginName: string): boolean {\r\n const Capacitor = getCapacitor();\r\n\r\n if (!Capacitor || !Capacitor.isPluginAvailable) {\r\n return false;\r\n }\r\n\r\n return Capacitor.isPluginAvailable(pluginName);\r\n}\r\n\r\n/**\r\n * Get safe area insets for notched devices.\r\n * Returns zeros on web or when not available.\r\n */\r\nexport function getSafeAreaInsets(): {\r\n top: number;\r\n bottom: number;\r\n left: number;\r\n right: number;\r\n} {\r\n // Try to get from CSS env variables\r\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\r\n const style = getComputedStyle(document.documentElement);\r\n return {\r\n top: parseInt(style.getPropertyValue('--sat') || '0', 10),\r\n bottom: parseInt(style.getPropertyValue('--sab') || '0', 10),\r\n left: parseInt(style.getPropertyValue('--sal') || '0', 10),\r\n right: parseInt(style.getPropertyValue('--sar') || '0', 10),\r\n };\r\n }\r\n\r\n return { top: 0, bottom: 0, left: 0, right: 0 };\r\n}\r\n\r\n/**\r\n * Check if running in a PWA installed context.\r\n */\r\nexport function isPWA(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n\r\n return window.matchMedia('(display-mode: standalone)').matches ||\r\n (window.navigator as any).standalone === true;\r\n}\r\n","/**\r\n * Capacitor configuration helper.\r\n * Creates type-safe configuration for mobile builds.\r\n */\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface MobileConfig {\r\n /** Bundle identifier (com.example.app) */\r\n appId: string;\r\n /** Display name of the app */\r\n appName: string;\r\n /** Directory containing the web assets (default: dist) */\r\n webDir?: string;\r\n /** Server configuration for live reload */\r\n server?: {\r\n url?: string;\r\n cleartext?: boolean;\r\n };\r\n /** iOS-specific configuration */\r\n ios?: {\r\n /** Path to iOS project (default: ios) */\r\n path?: string;\r\n /** Scheme for deep links */\r\n scheme?: string;\r\n /** Content inset mode */\r\n contentInset?: 'always' | 'scrollable' | 'automatic' | 'never';\r\n /** Background color of web view */\r\n backgroundColor?: string;\r\n /** Preferred content mode */\r\n preferredContentMode?: 'mobile' | 'desktop' | 'recommended';\r\n };\r\n /** Android-specific configuration */\r\n android?: {\r\n /** Path to Android project (default: android) */\r\n path?: string;\r\n /** Flavor for build variants */\r\n flavor?: string;\r\n /** Background color of web view */\r\n backgroundColor?: string;\r\n /** Allow mixed content */\r\n allowMixedContent?: boolean;\r\n /** Initial focus on web view */\r\n initialFocus?: boolean;\r\n };\r\n /** Plugin configurations */\r\n plugins?: {\r\n /** Push notifications config */\r\n PushNotifications?: {\r\n presentationOptions?: ('badge' | 'sound' | 'alert')[];\r\n };\r\n /** Splash screen config */\r\n SplashScreen?: {\r\n launchShowDuration?: number;\r\n launchAutoHide?: boolean;\r\n launchFadeOutDuration?: number;\r\n backgroundColor?: string;\r\n androidSplashResourceName?: string;\r\n androidScaleType?: 'CENTER' | 'CENTER_CROP' | 'CENTER_INSIDE' | 'FIT_CENTER' | 'FIT_XY';\r\n showSpinner?: boolean;\r\n spinnerStyle?: 'horizontal' | 'small' | 'large';\r\n spinnerColor?: string;\r\n };\r\n /** Local notifications config */\r\n LocalNotifications?: {\r\n smallIcon?: string;\r\n iconColor?: string;\r\n sound?: string;\r\n };\r\n /** Keyboard config */\r\n Keyboard?: {\r\n resize?: 'body' | 'ionic' | 'native' | 'none';\r\n style?: 'dark' | 'light' | 'default';\r\n resizeOnFullScreen?: boolean;\r\n };\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Factory\r\n// =============================================================================\r\n\r\n/**\r\n * Create a Capacitor configuration.\r\n * \r\n * Use this in your capacitor.config.ts file:\r\n * \r\n * @example\r\n * ```typescript\r\n * // capacitor.config.ts\r\n * import { defineCapacitorConfig } from '@flightdev/mobile/config';\r\n * \r\n * export default defineCapacitorConfig({\r\n * appId: 'com.example.myapp',\r\n * appName: 'My App',\r\n * webDir: 'dist',\r\n * plugins: {\r\n * PushNotifications: {\r\n * presentationOptions: ['badge', 'sound', 'alert'],\r\n * },\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport function defineCapacitorConfig(config: MobileConfig) {\r\n return {\r\n appId: config.appId,\r\n appName: config.appName,\r\n webDir: config.webDir ?? 'dist',\r\n server: config.server,\r\n ios: {\r\n path: 'ios',\r\n ...config.ios,\r\n },\r\n android: {\r\n path: 'android',\r\n ...config.android,\r\n },\r\n plugins: config.plugins,\r\n };\r\n}\r\n\r\n/**\r\n * Create Flight-specific mobile configuration.\r\n * Extends Capacitor config with Flight-specific options.\r\n * \r\n * @example\r\n * ```typescript\r\n * // flight.config.ts\r\n * import { defineMobileConfig } from '@flightdev/mobile/config';\r\n * \r\n * export default {\r\n * mobile: defineMobileConfig({\r\n * appId: 'com.example.myapp',\r\n * appName: 'My App',\r\n * }),\r\n * };\r\n * ```\r\n */\r\nexport function defineMobileConfig(config: MobileConfig) {\r\n return {\r\n ...config,\r\n _flight: true as const,\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Utilities\r\n// =============================================================================\r\n\r\n/**\r\n * Generate capacitor.config.ts content from Flight config.\r\n */\r\nexport function generateCapacitorConfig(config: MobileConfig): string {\r\n const configObj = defineCapacitorConfig(config);\r\n\r\n return `import type { CapacitorConfig } from '@capacitor/cli';\r\n\r\nconst config: CapacitorConfig = ${JSON.stringify(configObj, null, 2)};\r\n\r\nexport default config;\r\n`;\r\n}\r\n"]}
|