@mobana/react-native-sdk 0.2.10

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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +249 -0
  3. package/android/build.gradle +50 -0
  4. package/android/src/main/AndroidManifest.xml +6 -0
  5. package/android/src/main/java/ai/mobana/sdk/MobanaModule.kt +67 -0
  6. package/android/src/main/java/ai/mobana/sdk/MobanaPackage.kt +19 -0
  7. package/app.plugin.js +274 -0
  8. package/ios/Mobana.h +11 -0
  9. package/ios/Mobana.m +20 -0
  10. package/lib/commonjs/Mobana.js +676 -0
  11. package/lib/commonjs/Mobana.js.map +1 -0
  12. package/lib/commonjs/NativeMobana.js +53 -0
  13. package/lib/commonjs/NativeMobana.js.map +1 -0
  14. package/lib/commonjs/api.js +201 -0
  15. package/lib/commonjs/api.js.map +1 -0
  16. package/lib/commonjs/bridge/index.js +19 -0
  17. package/lib/commonjs/bridge/index.js.map +1 -0
  18. package/lib/commonjs/bridge/injectBridge.js +528 -0
  19. package/lib/commonjs/bridge/injectBridge.js.map +1 -0
  20. package/lib/commonjs/components/FlowWebView.js +676 -0
  21. package/lib/commonjs/components/FlowWebView.js.map +1 -0
  22. package/lib/commonjs/components/MobanaProvider.js +275 -0
  23. package/lib/commonjs/components/MobanaProvider.js.map +1 -0
  24. package/lib/commonjs/components/index.js +20 -0
  25. package/lib/commonjs/components/index.js.map +1 -0
  26. package/lib/commonjs/device.js +49 -0
  27. package/lib/commonjs/device.js.map +1 -0
  28. package/lib/commonjs/index.js +20 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/package.json +1 -0
  31. package/lib/commonjs/storage.js +277 -0
  32. package/lib/commonjs/storage.js.map +1 -0
  33. package/lib/commonjs/types.js +2 -0
  34. package/lib/commonjs/types.js.map +1 -0
  35. package/lib/module/Mobana.js +673 -0
  36. package/lib/module/Mobana.js.map +1 -0
  37. package/lib/module/NativeMobana.js +49 -0
  38. package/lib/module/NativeMobana.js.map +1 -0
  39. package/lib/module/api.js +194 -0
  40. package/lib/module/api.js.map +1 -0
  41. package/lib/module/bridge/index.js +4 -0
  42. package/lib/module/bridge/index.js.map +1 -0
  43. package/lib/module/bridge/injectBridge.js +523 -0
  44. package/lib/module/bridge/injectBridge.js.map +1 -0
  45. package/lib/module/components/FlowWebView.js +672 -0
  46. package/lib/module/components/FlowWebView.js.map +1 -0
  47. package/lib/module/components/MobanaProvider.js +270 -0
  48. package/lib/module/components/MobanaProvider.js.map +1 -0
  49. package/lib/module/components/index.js +5 -0
  50. package/lib/module/components/index.js.map +1 -0
  51. package/lib/module/device.js +45 -0
  52. package/lib/module/device.js.map +1 -0
  53. package/lib/module/index.js +53 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/storage.js +257 -0
  56. package/lib/module/storage.js.map +1 -0
  57. package/lib/module/types.js +2 -0
  58. package/lib/module/types.js.map +1 -0
  59. package/lib/typescript/Mobana.d.ts +209 -0
  60. package/lib/typescript/Mobana.d.ts.map +1 -0
  61. package/lib/typescript/NativeMobana.d.ts +11 -0
  62. package/lib/typescript/NativeMobana.d.ts.map +1 -0
  63. package/lib/typescript/api.d.ts +34 -0
  64. package/lib/typescript/api.d.ts.map +1 -0
  65. package/lib/typescript/bridge/index.d.ts +3 -0
  66. package/lib/typescript/bridge/index.d.ts.map +1 -0
  67. package/lib/typescript/bridge/injectBridge.d.ts +23 -0
  68. package/lib/typescript/bridge/injectBridge.d.ts.map +1 -0
  69. package/lib/typescript/components/FlowWebView.d.ts +38 -0
  70. package/lib/typescript/components/FlowWebView.d.ts.map +1 -0
  71. package/lib/typescript/components/MobanaProvider.d.ts +65 -0
  72. package/lib/typescript/components/MobanaProvider.d.ts.map +1 -0
  73. package/lib/typescript/components/index.d.ts +5 -0
  74. package/lib/typescript/components/index.d.ts.map +1 -0
  75. package/lib/typescript/device.d.ts +6 -0
  76. package/lib/typescript/device.d.ts.map +1 -0
  77. package/lib/typescript/index.d.ts +46 -0
  78. package/lib/typescript/index.d.ts.map +1 -0
  79. package/lib/typescript/storage.d.ts +68 -0
  80. package/lib/typescript/storage.d.ts.map +1 -0
  81. package/lib/typescript/types.d.ts +298 -0
  82. package/lib/typescript/types.d.ts.map +1 -0
  83. package/mobana.podspec +19 -0
  84. package/package.json +131 -0
  85. package/src/Mobana.ts +742 -0
  86. package/src/NativeMobana.ts +61 -0
  87. package/src/api.ts +259 -0
  88. package/src/bridge/index.ts +2 -0
  89. package/src/bridge/injectBridge.ts +542 -0
  90. package/src/components/FlowWebView.tsx +826 -0
  91. package/src/components/MobanaProvider.tsx +393 -0
  92. package/src/components/index.ts +4 -0
  93. package/src/device.ts +42 -0
  94. package/src/index.ts +66 -0
  95. package/src/storage.ts +262 -0
  96. package/src/types.ts +362 -0
@@ -0,0 +1,528 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildFlowHtml = buildFlowHtml;
7
+ exports.generateBridgeScript = generateBridgeScript;
8
+ /**
9
+ * Bridge context passed from native to WebView
10
+ */
11
+
12
+ /**
13
+ * Generate JavaScript code to inject into WebView
14
+ * Creates the window.Mobana bridge object
15
+ */
16
+ function generateBridgeScript(context) {
17
+ const contextJson = JSON.stringify(context);
18
+
19
+ // This JavaScript runs inside the WebView
20
+ return `
21
+ (function() {
22
+ 'use strict';
23
+
24
+ // Bridge context from native
25
+ var __context = ${contextJson};
26
+ var __localData = __context.localData || {};
27
+
28
+ // Pending async requests (requestId -> { resolve, reject })
29
+ var __pendingRequests = {};
30
+ var __requestId = 0;
31
+
32
+ // Send message to native
33
+ function postMessage(type, payload, requestId) {
34
+ var message = {
35
+ type: type,
36
+ payload: payload,
37
+ requestId: requestId
38
+ };
39
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
40
+ }
41
+
42
+ // Make an async request to native and wait for response
43
+ function asyncRequest(type, payload) {
44
+ return new Promise(function(resolve, reject) {
45
+ var id = ++__requestId;
46
+ __pendingRequests[id] = { resolve: resolve, reject: reject };
47
+ postMessage(type, payload, id);
48
+ });
49
+ }
50
+
51
+ // Handle response from native (called via injectJavaScript)
52
+ window.__mobanaBridgeResponse = function(requestId, success, result) {
53
+ var pending = __pendingRequests[requestId];
54
+ if (pending) {
55
+ delete __pendingRequests[requestId];
56
+ if (success) {
57
+ pending.resolve(result);
58
+ } else {
59
+ pending.reject(new Error(result || 'Request failed'));
60
+ }
61
+ }
62
+ };
63
+
64
+ // Mobana bridge object
65
+ window.Mobana = {
66
+ // ============================================
67
+ // Data Access
68
+ // ============================================
69
+
70
+ /**
71
+ * Get attribution data for this install
72
+ * @returns {Object|null} Attribution object or null if not matched
73
+ */
74
+ getAttribution: function() {
75
+ return __context.attribution;
76
+ },
77
+
78
+ /**
79
+ * Get custom parameters passed to startFlow()
80
+ * @returns {Object} Parameters object
81
+ */
82
+ getParams: function() {
83
+ return __context.params || {};
84
+ },
85
+
86
+ /**
87
+ * Get the install ID
88
+ * @returns {string} Unique install identifier
89
+ */
90
+ getInstallId: function() {
91
+ return __context.installId;
92
+ },
93
+
94
+ /**
95
+ * Get the current platform
96
+ * @returns {string} 'ios' or 'android'
97
+ */
98
+ getPlatform: function() {
99
+ return __context.platform;
100
+ },
101
+
102
+ /**
103
+ * Get safe area insets for the device screen
104
+ * @returns {Object} { top, bottom, left, right, width, height }
105
+ */
106
+ getSafeArea: function() {
107
+ return __context.safeArea;
108
+ },
109
+
110
+ /**
111
+ * Get the device color scheme (light/dark mode)
112
+ * @returns {string} 'light' or 'dark'
113
+ */
114
+ getColorScheme: function() {
115
+ return __context.colorScheme;
116
+ },
117
+
118
+ /**
119
+ * Store data locally on device (persists across app sessions)
120
+ * @param {string} key - Data key
121
+ * @param {*} value - Data value
122
+ */
123
+ setLocalData: function(key, value) {
124
+ __localData[key] = value;
125
+ postMessage('setLocalData', { key: key, value: value });
126
+ },
127
+
128
+ /**
129
+ * Retrieve locally stored data
130
+ * @param {string} key - Data key
131
+ * @returns {*} Data value or undefined
132
+ */
133
+ getLocalData: function(key) {
134
+ return __localData[key];
135
+ },
136
+
137
+ // ============================================
138
+ // Flow Control
139
+ // ============================================
140
+
141
+ /**
142
+ * Complete the flow with optional data
143
+ * @param {Object} data - Optional data to return to the app
144
+ */
145
+ complete: function(data) {
146
+ postMessage('complete', { data: data });
147
+ },
148
+
149
+ /**
150
+ * Dismiss the flow
151
+ */
152
+ dismiss: function() {
153
+ postMessage('dismiss', {});
154
+ },
155
+
156
+ /**
157
+ * Track a custom event
158
+ * @param {string} name - Event name (snake_case, e.g., 'welcome_viewed')
159
+ */
160
+ trackEvent: function(name) {
161
+ postMessage('trackEvent', { name: name });
162
+ },
163
+
164
+ /**
165
+ * Request the app to perform an async action and return a result.
166
+ * The flow stays open while the app processes the request.
167
+ * Requires onCallback to be provided when starting the flow.
168
+ *
169
+ * @param {Object} data - Arbitrary data to send to the app's onCallback handler
170
+ * @param {Object} options - Optional configuration
171
+ * @param {number} options.timeout - Timeout in seconds (default: 300)
172
+ * @returns {Promise<Object>} Result returned by the app's onCallback handler
173
+ *
174
+ * @example
175
+ * // Request a purchase
176
+ * try {
177
+ * var result = await Mobana.requestCallback(
178
+ * { action: 'purchase', planId: 'premium' },
179
+ * { timeout: 120 }
180
+ * );
181
+ * if (result.success) {
182
+ * Mobana.complete({ purchased: true });
183
+ * }
184
+ * } catch (error) {
185
+ * // Timeout, no handler, or handler threw an error
186
+ * }
187
+ */
188
+ requestCallback: function(data, options) {
189
+ var opts = options || {};
190
+ var timeout = typeof opts.timeout === 'number' ? opts.timeout : 300;
191
+
192
+ var promise = asyncRequest('requestCallback', { data: data || {} });
193
+
194
+ // Wrap with timeout
195
+ var timeoutMs = timeout * 1000;
196
+ var timer;
197
+ var timeoutPromise = new Promise(function(_, reject) {
198
+ timer = setTimeout(function() {
199
+ reject(new Error('requestCallback timed out after ' + timeout + 's'));
200
+ }, timeoutMs);
201
+ });
202
+
203
+ return Promise.race([promise, timeoutPromise]).then(
204
+ function(result) { clearTimeout(timer); return result; },
205
+ function(error) { clearTimeout(timer); throw error; }
206
+ );
207
+ },
208
+
209
+ // ============================================
210
+ // Permissions
211
+ // ============================================
212
+
213
+ /**
214
+ * Request notification permission
215
+ * @returns {Promise<boolean>} True if granted
216
+ */
217
+ requestNotificationPermission: function() {
218
+ return asyncRequest('requestNotificationPermission', {});
219
+ },
220
+
221
+ /**
222
+ * Check notification permission status without requesting
223
+ * @returns {Promise<Object>} { status: string, granted: boolean, settings?: Object }
224
+ */
225
+ checkNotificationPermission: function() {
226
+ return asyncRequest('checkNotificationPermission', {});
227
+ },
228
+
229
+ /**
230
+ * Request App Tracking Transparency permission (iOS only)
231
+ * @returns {Promise<string>} 'authorized', 'denied', 'not-determined', or 'restricted'
232
+ */
233
+ requestATTPermission: function() {
234
+ return asyncRequest('requestATTPermission', {});
235
+ },
236
+
237
+ /**
238
+ * Check App Tracking Transparency status without requesting (iOS only)
239
+ * @returns {Promise<string>} 'authorized', 'denied', 'not-determined', or 'restricted'
240
+ */
241
+ checkATTPermission: function() {
242
+ return asyncRequest('checkATTPermission', {});
243
+ },
244
+
245
+ /**
246
+ * Request location permission
247
+ * @param {Object} options - Optional configuration
248
+ * @param {string} options.precision - 'precise' (default) or 'coarse'. On Android, this determines
249
+ * whether to request ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION. On iOS, precision is
250
+ * controlled by the user in the permission dialog.
251
+ * @returns {Promise<string>} Permission result ('granted', 'denied', 'blocked', 'unavailable')
252
+ */
253
+ requestLocationPermission: function(options) {
254
+ var opts = options || {};
255
+ return asyncRequest('requestLocationPermission', { precision: opts.precision || 'precise' });
256
+ },
257
+
258
+ /**
259
+ * Request background location permission
260
+ * @returns {Promise<string>} Permission result ('granted', 'denied', 'blocked', 'unavailable')
261
+ */
262
+ requestBackgroundLocationPermission: function() {
263
+ return asyncRequest('requestBackgroundLocationPermission', {});
264
+ },
265
+
266
+ /**
267
+ * Get current location permission status
268
+ * @returns {Promise<Object>} Location permission status object:
269
+ * - foreground: 'granted' | 'denied' | 'blocked' | 'not_requested'
270
+ * - background: 'granted' | 'denied' | 'blocked' | 'not_requested'
271
+ * - precision: 'precise' | 'coarse' | 'unknown'
272
+ */
273
+ getLocationPermissionStatus: function() {
274
+ return asyncRequest('getLocationPermissionStatus', {});
275
+ },
276
+
277
+ /**
278
+ * Get current location
279
+ * @returns {Promise<Object>} Location coordinates
280
+ */
281
+ getCurrentLocation: function() {
282
+ return asyncRequest('getCurrentLocation', {});
283
+ },
284
+
285
+ // ============================================
286
+ // Native Utilities
287
+ // ============================================
288
+
289
+ /**
290
+ * Request app store review
291
+ * Note: This will complete the flow and show the review dialog after the flow closes.
292
+ * Due to iOS StoreKit limitations, reviews cannot be shown while a modal is visible.
293
+ * Use this as the final action in your flow.
294
+ */
295
+ requestAppReview: function() {
296
+ postMessage('requestAppReview', {});
297
+ },
298
+
299
+ /**
300
+ * Trigger haptic feedback
301
+ * @param {string} style - 'light', 'medium', 'heavy', 'success', 'warning', 'error', 'selection'
302
+ */
303
+ haptic: function(style) {
304
+ postMessage('haptic', { style: style || 'medium' });
305
+ },
306
+
307
+ /**
308
+ * Open a URL in the browser
309
+ * @param {string} url - URL to open
310
+ */
311
+ openURL: function(url) {
312
+ postMessage('openURL', { url: url });
313
+ },
314
+
315
+ /**
316
+ * Open app settings
317
+ */
318
+ openSettings: function() {
319
+ postMessage('openSettings', {});
320
+ },
321
+
322
+ /**
323
+ * Play a sound from a URL (external or base64 data URL)
324
+ * @param {string} url - Sound URL (https:// or data:audio/...)
325
+ * @param {Object} options - Optional playback options
326
+ * @param {number} options.volume - Volume level (0.0 - 1.0, default 1.0)
327
+ * @param {boolean} options.loop - Whether to loop the sound (default false)
328
+ * @param {function} options.onEnd - Callback when sound finishes playing
329
+ * @param {function} options.onError - Callback when an error occurs
330
+ * @returns {Object} Controller with { isPlaying, stop() }
331
+ */
332
+ playSound: function(url, options) {
333
+ var opts = options || {};
334
+ var volume = typeof opts.volume === 'number' ? Math.max(0, Math.min(1, opts.volume)) : 1.0;
335
+ var loop = opts.loop === true;
336
+ var onEnd = typeof opts.onEnd === 'function' ? opts.onEnd : null;
337
+ var onError = typeof opts.onError === 'function' ? opts.onError : null;
338
+
339
+ var controller = {
340
+ isPlaying: false,
341
+ stop: function() {}
342
+ };
343
+
344
+ try {
345
+ var audio = new Audio(url);
346
+ audio.volume = volume;
347
+ audio.loop = loop;
348
+
349
+ audio.onplay = function() {
350
+ controller.isPlaying = true;
351
+ };
352
+
353
+ audio.onended = function() {
354
+ controller.isPlaying = false;
355
+ if (onEnd) {
356
+ try { onEnd(); } catch (e) { console.warn('playSound onEnd error:', e); }
357
+ }
358
+ };
359
+
360
+ audio.onerror = function(e) {
361
+ controller.isPlaying = false;
362
+ console.warn('playSound error: Failed to load or play sound');
363
+ if (onError) {
364
+ try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
365
+ }
366
+ };
367
+
368
+ audio.onpause = function() {
369
+ if (!audio.ended) {
370
+ controller.isPlaying = false;
371
+ }
372
+ };
373
+
374
+ controller.stop = function() {
375
+ try {
376
+ audio.pause();
377
+ audio.currentTime = 0;
378
+ controller.isPlaying = false;
379
+ } catch (e) {
380
+ // Audio may have been garbage collected
381
+ }
382
+ };
383
+
384
+ audio.play().catch(function(e) {
385
+ controller.isPlaying = false;
386
+ console.warn('playSound error: ' + e.message);
387
+ if (onError) {
388
+ try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
389
+ }
390
+ });
391
+
392
+ controller.isPlaying = true;
393
+ } catch (e) {
394
+ console.warn('playSound error: ' + e.message);
395
+ if (onError) {
396
+ try { onError(e); } catch (err) { console.warn('playSound onError callback error:', err); }
397
+ }
398
+ }
399
+
400
+ return controller;
401
+ }
402
+ };
403
+
404
+ // Mark bridge as ready
405
+ window.__mobanaBridgeReady = true;
406
+
407
+ // Dispatch ready event for flows that want to wait
408
+ if (typeof document !== 'undefined') {
409
+ document.dispatchEvent(new Event('mobana:ready'));
410
+ }
411
+ })();
412
+ `;
413
+ }
414
+
415
+ /**
416
+ * Build complete HTML with injected bridge, styles, and safe area CSS variables
417
+ */
418
+ function buildFlowHtml(html, css, js, bridgeScript, safeArea, colorScheme) {
419
+ let fullHtml = html;
420
+
421
+ // 0. Ensure viewport meta tag has viewport-fit=cover (required for edge-to-edge rendering on iOS)
422
+ const viewportMetaRegex = /<meta\s+[^>]*name=["']viewport["'][^>]*>/i;
423
+ const viewportMatch = fullHtml.match(viewportMetaRegex);
424
+ if (viewportMatch) {
425
+ const existingTag = viewportMatch[0];
426
+ if (!existingTag.includes('viewport-fit=cover')) {
427
+ // Append viewport-fit=cover to existing content attribute
428
+ const updatedTag = existingTag.replace(/content=["']([^"']*)["']/i, (match, content) => `content="${content}, viewport-fit=cover"`);
429
+ fullHtml = fullHtml.replace(existingTag, updatedTag);
430
+ }
431
+ } else {
432
+ // No viewport meta tag — inject a sensible default
433
+ const defaultViewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">';
434
+ if (fullHtml.includes('</head>')) {
435
+ fullHtml = fullHtml.replace(/<head([^>]*)>/i, `<head$1>${defaultViewport}`);
436
+ } else if (fullHtml.includes('<body')) {
437
+ fullHtml = fullHtml.replace('<body', `<head>${defaultViewport}</head><body`);
438
+ } else {
439
+ fullHtml = `<head>${defaultViewport}</head>` + fullHtml;
440
+ }
441
+ }
442
+
443
+ // 1. Inject SDK base resets FIRST (before user CSS, so flows can override)
444
+ const resetStyle = `<style data-mobana="reset">
445
+ /* Mobana SDK base resets — flows can override any of these */
446
+ *, *::before, *::after {
447
+ margin: 0;
448
+ padding: 0;
449
+ box-sizing: border-box;
450
+ -webkit-tap-highlight-color: transparent;
451
+ }
452
+ body {
453
+ -webkit-font-smoothing: antialiased;
454
+ -webkit-user-select: none;
455
+ user-select: none;
456
+ -webkit-touch-callout: none;
457
+ overflow: hidden;
458
+ }
459
+ </style>`;
460
+ if (fullHtml.includes('</head>')) {
461
+ // Insert at the START of <head> so it comes before any flow styles
462
+ fullHtml = fullHtml.replace(/<head([^>]*)>/i, `<head$1>${resetStyle}`);
463
+ } else if (fullHtml.includes('<body')) {
464
+ fullHtml = fullHtml.replace('<body', `<head>${resetStyle}</head><body`);
465
+ } else {
466
+ fullHtml = resetStyle + fullHtml;
467
+ }
468
+
469
+ // 2. Inject user CSS (if separate) — after resets, before env vars
470
+ if (css) {
471
+ const styleTag = `<style>${css}</style>`;
472
+ if (fullHtml.includes('</head>')) {
473
+ fullHtml = fullHtml.replace('</head>', `${styleTag}</head>`);
474
+ } else if (fullHtml.includes('<body')) {
475
+ fullHtml = fullHtml.replace('<body', `<head>${styleTag}</head><body`);
476
+ } else {
477
+ fullHtml = styleTag + fullHtml;
478
+ }
479
+ }
480
+
481
+ // 3. Inject CSS environment variables AFTER user CSS (SDK values take precedence)
482
+ // Note: We use values from react-native-safe-area-context (not CSS env()) for reliable
483
+ // cross-platform insets. Step 0 ensures viewport-fit=cover for edge-to-edge rendering.
484
+ const envVarsStyle = `<style data-mobana="env">
485
+ :root {
486
+ /* Color scheme - enables light-dark() CSS function */
487
+ color-scheme: ${colorScheme || 'light'};
488
+ --color-scheme: ${colorScheme || 'light'};
489
+ /* Safe area insets - from react-native-safe-area-context */
490
+ --safe-area-top: ${safeArea?.top ?? 0}px;
491
+ --safe-area-right: ${safeArea?.right ?? 0}px;
492
+ --safe-area-bottom: ${safeArea?.bottom ?? 0}px;
493
+ --safe-area-left: ${safeArea?.left ?? 0}px;
494
+ /* Screen dimensions */
495
+ --screen-width: ${safeArea?.width ?? 0}px;
496
+ --screen-height: ${safeArea?.height ?? 0}px;
497
+ }
498
+ </style>`;
499
+ if (fullHtml.includes('</head>')) {
500
+ fullHtml = fullHtml.replace('</head>', `${envVarsStyle}</head>`);
501
+ } else if (fullHtml.includes('<body')) {
502
+ fullHtml = fullHtml.replace('<body', `<head>${envVarsStyle}</head><body`);
503
+ } else {
504
+ fullHtml = envVarsStyle + fullHtml;
505
+ }
506
+
507
+ // Inject JS if separate
508
+ if (js) {
509
+ const scriptTag = `<script>${js}</script>`;
510
+ if (fullHtml.includes('</body>')) {
511
+ fullHtml = fullHtml.replace('</body>', `${scriptTag}</body>`);
512
+ } else {
513
+ fullHtml = fullHtml + scriptTag;
514
+ }
515
+ }
516
+
517
+ // Inject bridge script (must come before user JS)
518
+ const bridgeTag = `<script>${bridgeScript}</script>`;
519
+ if (fullHtml.includes('</head>')) {
520
+ fullHtml = fullHtml.replace('</head>', `${bridgeTag}</head>`);
521
+ } else if (fullHtml.includes('<body')) {
522
+ fullHtml = fullHtml.replace('<body', `<head>${bridgeTag}</head><body`);
523
+ } else {
524
+ fullHtml = bridgeTag + fullHtml;
525
+ }
526
+ return fullHtml;
527
+ }
528
+ //# sourceMappingURL=injectBridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["generateBridgeScript","context","contextJson","JSON","stringify","buildFlowHtml","html","css","js","bridgeScript","safeArea","colorScheme","fullHtml","viewportMetaRegex","viewportMatch","match","existingTag","includes","updatedTag","replace","content","defaultViewport","resetStyle","styleTag","envVarsStyle","top","right","bottom","left","width","height","scriptTag","bridgeTag"],"sourceRoot":"../../../src","sources":["bridge/injectBridge.ts"],"mappings":";;;;;;;AAEA;AACA;AACA;;AAWA;AACA;AACA;AACA;AACO,SAASA,oBAAoBA,CAACC,OAAsB,EAAU;EACnE,MAAMC,WAAW,GAAGC,IAAI,CAACC,SAAS,CAACH,OAAO,CAAC;;EAE3C;EACA,OAAO;AACT;AACA;AACA;AACA;AACA,oBAAoBC,WAAW;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;;AAEA;AACA;AACA;AACO,SAASG,aAAaA,CAC3BC,IAAY,EACZC,GAAuB,EACvBC,EAAsB,EACtBC,YAAoB,EACpBC,QAAmB,EACnBC,WAAyB,EACjB;EACR,IAAIC,QAAQ,GAAGN,IAAI;;EAEnB;EACA,MAAMO,iBAAiB,GAAG,2CAA2C;EACrE,MAAMC,aAAa,GAAGF,QAAQ,CAACG,KAAK,CAACF,iBAAiB,CAAC;EACvD,IAAIC,aAAa,EAAE;IACjB,M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","ignoreList":[]}