onairos 2.0.4 → 2.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onairos",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "dependencies": {
5
5
  "@anthropic-ai/sdk": "^0.24.3",
6
6
  "@google/generative-ai": "^0.15.0",
@@ -1,43 +1,160 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ const platforms = [
4
+ { name: 'YouTube', icon: '📺', color: 'bg-red-500' },
5
+ { name: 'Reddit', icon: '🔥', color: 'bg-orange-500' },
6
+ { name: 'Instagram', icon: '📷', color: 'bg-pink-500' },
7
+ { name: 'Pinterest', icon: '📌', color: 'bg-red-600' },
8
+ { name: 'TikTok', icon: '🎵', color: 'bg-black' },
9
+ { name: 'Twitter', icon: '🐦', color: 'bg-blue-500' },
10
+ { name: 'LinkedIn', icon: '💼', color: 'bg-blue-700' },
11
+ { name: 'Facebook', icon: '👥', color: 'bg-blue-600' }
12
+ ];
2
13
 
3
14
  /**
4
15
  * UniversalOnboarding Component
5
16
  * Displays an onboarding screen for applications requesting Onairos data
6
17
  */
7
- const UniversalOnboarding = ({ appIcon, appName }) => {
18
+ export default function UniversalOnboarding({ onComplete, appIcon, appName = 'App' }) {
19
+ const [connectedAccounts, setConnectedAccounts] = useState({});
20
+ const [isConnecting, setIsConnecting] = useState(false);
21
+
22
+ const handleToggle = async (platformName) => {
23
+ if (isConnecting) return;
24
+
25
+ setIsConnecting(true);
26
+
27
+ // Simulate connection delay
28
+ await new Promise(resolve => setTimeout(resolve, 1000));
29
+
30
+ setConnectedAccounts(prev => ({
31
+ ...prev,
32
+ [platformName]: !prev[platformName]
33
+ }));
34
+
35
+ setIsConnecting(false);
36
+ };
37
+
38
+ const handleContinue = () => {
39
+ const connected = Object.entries(connectedAccounts)
40
+ .filter(([platform, isConnected]) => isConnected)
41
+ .map(([platform]) => platform);
42
+
43
+ onComplete({
44
+ connectedAccounts: connected,
45
+ totalConnections: connected.length
46
+ });
47
+ };
48
+
49
+ const connectedCount = Object.values(connectedAccounts).filter(Boolean).length;
50
+
8
51
  return (
9
- <div className="flex flex-col items-center justify-center min-h-screen p-4">
10
- <div className="max-w-md w-full bg-white rounded-lg shadow-md p-6 space-y-6">
11
- {appIcon && (
12
- <div className="flex justify-center">
13
- <img src={appIcon} alt={`${appName} icon`} className="w-20 h-20" />
52
+ <div className="max-w-md mx-auto bg-white p-6 min-h-[500px]">
53
+ {/* Header with App Logo and Arrow to Onairos */}
54
+ <div className="flex items-center justify-center mb-6">
55
+ <div className="flex items-center space-x-3">
56
+ <img
57
+ src={appIcon || "https://onairos.sirv.com/Images/OnairosBlack.png"}
58
+ alt={appName}
59
+ className="w-10 h-10 rounded-lg"
60
+ />
61
+ <div className="flex items-center text-gray-400">
62
+ <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
63
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
64
+ </svg>
14
65
  </div>
15
- )}
16
-
17
- <div className="text-center space-y-2">
18
- <h1 className="text-xl font-bold text-black">Connect with {appName}</h1>
19
- <p className="text-sm text-gray-600">
20
- To share your data with {appName}, you need to create an Onairos Personality model first.
21
- </p>
22
- </div>
23
-
24
- <div className="border-t border-gray-200 pt-4">
25
- <a
26
- href="https://onairos.uk/connections"
27
- target="_blank"
28
- rel="noopener noreferrer"
29
- className="block w-full bg-black text-white rounded-md py-2 text-center font-medium hover:bg-black/90 transition-colors"
30
- >
31
- Create Personality Model
32
- </a>
66
+ <img
67
+ src="https://onairos.sirv.com/Images/OnairosBlack.png"
68
+ alt="Onairos"
69
+ className="w-10 h-10 rounded-lg"
70
+ />
33
71
  </div>
34
-
35
- <p className="text-xs text-center text-gray-500">
36
- Your data will be securely shared with {appName} only after your explicit consent.
72
+ </div>
73
+
74
+ <div className="text-center mb-6">
75
+ <h2 className="text-xl font-bold text-gray-900 mb-2">Connect Your Accounts</h2>
76
+ <p className="text-gray-600 text-sm">
77
+ Choose which accounts to connect for a personalized experience
78
+ </p>
79
+ </div>
80
+
81
+ {/* Privacy Notice */}
82
+ <div className="mb-6 p-3 bg-blue-50 border border-blue-200 rounded-lg">
83
+ <p className="text-blue-800 text-sm">
84
+ 🔒 Your data is never shared with anyone. It's only used to train your personal model and is stored securely.
37
85
  </p>
38
86
  </div>
87
+
88
+ {/* Platform List - Vertical Layout with Toggles */}
89
+ <div className="space-y-3 mb-6">
90
+ {platforms.map((platform) => {
91
+ const isConnected = connectedAccounts[platform.name] || false;
92
+
93
+ return (
94
+ <div
95
+ key={platform.name}
96
+ className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50 transition-colors"
97
+ >
98
+ <div className="flex items-center space-x-3">
99
+ <div className={`w-10 h-10 rounded-lg ${platform.color} flex items-center justify-center text-white text-lg`}>
100
+ {platform.icon}
101
+ </div>
102
+ <div>
103
+ <h3 className="font-medium text-gray-900">{platform.name}</h3>
104
+ <p className="text-sm text-gray-500">
105
+ {isConnected ? 'Connected' : 'Not connected'}
106
+ </p>
107
+ </div>
108
+ </div>
109
+
110
+ {/* Toggle Switch */}
111
+ <button
112
+ onClick={() => handleToggle(platform.name)}
113
+ disabled={isConnecting}
114
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
115
+ isConnected ? 'bg-blue-600' : 'bg-gray-200'
116
+ } ${isConnecting ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
117
+ >
118
+ <span
119
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
120
+ isConnected ? 'translate-x-6' : 'translate-x-1'
121
+ }`}
122
+ />
123
+ </button>
124
+ </div>
125
+ );
126
+ })}
127
+ </div>
128
+
129
+ {/* Connection Status */}
130
+ {connectedCount > 0 && (
131
+ <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
132
+ <p className="text-green-800 text-sm">
133
+ ✅ {connectedCount} account{connectedCount > 1 ? 's' : ''} connected
134
+ </p>
135
+ </div>
136
+ )}
137
+
138
+ {/* Continue Button */}
139
+ <button
140
+ onClick={handleContinue}
141
+ disabled={connectedCount === 0}
142
+ className={`w-full py-3 px-4 rounded-lg font-semibold transition-colors ${
143
+ connectedCount > 0
144
+ ? 'bg-blue-600 text-white hover:bg-blue-700'
145
+ : 'bg-gray-300 text-gray-500 cursor-not-allowed'
146
+ }`}
147
+ >
148
+ Continue {connectedCount > 0 ? `with ${connectedCount} account${connectedCount > 1 ? 's' : ''}` : ''}
149
+ </button>
150
+
151
+ {/* Skip Option */}
152
+ <button
153
+ onClick={() => onComplete({ connectedAccounts: [], totalConnections: 0 })}
154
+ className="w-full mt-2 py-2 text-gray-500 hover:text-gray-700 text-sm"
155
+ >
156
+ Skip for now
157
+ </button>
39
158
  </div>
40
159
  );
41
- };
42
-
43
- export default UniversalOnboarding;
160
+ }
@@ -1,15 +1,8 @@
1
- import React, { useEffect, useState, useRef } from 'react';
2
- import { rsaEncrypt } from './RSA.jsx';
1
+ import React, { useEffect, useState } from 'react';
3
2
  import EmailAuth from './components/EmailAuth.js';
4
3
  import UniversalOnboarding from './components/UniversalOnboarding.js';
5
4
  import PinSetup from './components/PinSetup.js';
6
5
  import DataRequest from './components/DataRequest.js';
7
- import {
8
- openDataRequestPopup,
9
- closeDataRequestPopup,
10
- sendDataToPopup,
11
- listenForPopupMessages
12
- } from './iframe/dataRequestHandler.js';
13
6
 
14
7
  export function OnairosButton({
15
8
  requestData,
@@ -25,16 +18,13 @@ export function OnairosButton({
25
18
  loginReturn = null,
26
19
  loginType = 'signIn',
27
20
  visualType = 'full',
28
- appIcon = null, // App icon URL to display in the data request screen
21
+ appIcon = null,
29
22
  }) {
30
23
 
31
24
  const [showOverlay, setShowOverlay] = useState(false);
32
25
  const [currentFlow, setCurrentFlow] = useState('email'); // 'email' | 'onboarding' | 'pin' | 'dataRequest'
33
26
  const [userData, setUserData] = useState(null);
34
- const [isLoading, setIsLoading] = useState(false);
35
27
  const [error, setError] = useState(null);
36
- const [popupWindow, setPopupWindow] = useState(null);
37
- const messageCleanup = useRef(null);
38
28
 
39
29
  // Check for existing user session
40
30
  useEffect(() => {
@@ -62,84 +52,15 @@ export function OnairosButton({
62
52
  checkExistingSession();
63
53
  }, []);
64
54
 
65
- // Cleanup popup and message listeners on unmount
66
- useEffect(() => {
67
- return () => {
68
- if (popupWindow) {
69
- closeDataRequestPopup(popupWindow);
70
- }
71
- if (messageCleanup.current) {
72
- messageCleanup.current();
73
- }
74
- };
75
- }, [popupWindow]);
76
-
77
55
  const openTerminal = async () => {
78
56
  try {
79
57
  console.log('🔥 openTerminal called');
80
-
81
- // If user has completed all steps, open popup directly for data request
82
- if (userData?.onboardingComplete && userData?.pinCreated) {
83
- openDataRequestDirectly();
84
- } else {
85
- // Otherwise show the overlay for onboarding flow
86
- setShowOverlay(true);
87
- }
58
+ setShowOverlay(true);
88
59
  } catch (error) {
89
60
  console.error('Error in openTerminal:', error);
90
61
  }
91
62
  };
92
63
 
93
- const openDataRequestDirectly = () => {
94
- try {
95
- // Open popup for data request
96
- const popup = openDataRequestPopup({
97
- requestData,
98
- webpageName,
99
- userData,
100
- autoFetch,
101
- proofMode,
102
- appIcon // Pass the app icon to the data request popup
103
- });
104
-
105
- if (popup) {
106
- setPopupWindow(popup);
107
-
108
- // Set up message listener for popup communication
109
- const cleanup = listenForPopupMessages(
110
- handlePopupMessage,
111
- {
112
- autoFetch,
113
- onApiResponse: handleApiResponse
114
- }
115
- );
116
- messageCleanup.current = cleanup;
117
- }
118
- } catch (error) {
119
- console.error('Error opening data request popup:', error);
120
- setError('Failed to open data request popup');
121
- }
122
- };
123
-
124
- const handlePopupMessage = (data) => {
125
- console.log('🔥 OnairosButton received popup message:', data);
126
-
127
- if (data.type === 'dataRequestComplete') {
128
- handleDataRequestComplete(data);
129
- } else if (data.type === 'popupClosed') {
130
- setPopupWindow(null);
131
- if (messageCleanup.current) {
132
- messageCleanup.current();
133
- messageCleanup.current = null;
134
- }
135
- }
136
- };
137
-
138
- const handleApiResponse = (apiResponse) => {
139
- console.log('🔥 API Response received:', apiResponse);
140
- // Additional handling for API response if needed
141
- };
142
-
143
64
  const handleCloseOverlay = () => {
144
65
  setShowOverlay(false);
145
66
  setError(null);
@@ -187,11 +108,8 @@ export function OnairosButton({
187
108
  setUserData(updatedUserData);
188
109
  localStorage.setItem('onairosUser', JSON.stringify(updatedUserData));
189
110
 
190
- // Close overlay and open popup for data request
191
- setShowOverlay(false);
192
- setTimeout(() => {
193
- openDataRequestDirectly();
194
- }, 300);
111
+ // Move to data request flow within the same overlay
112
+ setCurrentFlow('dataRequest');
195
113
  };
196
114
 
197
115
  const handleDataRequestComplete = (requestResult) => {
@@ -205,11 +123,8 @@ export function OnairosButton({
205
123
  setUserData(updatedUserData);
206
124
  localStorage.setItem('onairosUser', JSON.stringify(updatedUserData));
207
125
 
208
- // Close popup
209
- if (popupWindow) {
210
- closeDataRequestPopup(popupWindow);
211
- setPopupWindow(null);
212
- }
126
+ // Close overlay
127
+ setShowOverlay(false);
213
128
 
214
129
  // Call onComplete callback if provided
215
130
  console.log('🔥 Calling onComplete callback with:', requestResult);
@@ -239,7 +154,7 @@ export function OnairosButton({
239
154
  return (
240
155
  <UniversalOnboarding
241
156
  onComplete={handleOnboardingComplete}
242
- appIcon="https://onairos.sirv.com/Images/OnairosBlack.png"
157
+ appIcon={appIcon || "https://onairos.sirv.com/Images/OnairosBlack.png"}
243
158
  appName={webpageName}
244
159
  />
245
160
  );
@@ -260,6 +175,7 @@ export function OnairosButton({
260
175
  requestData={requestData}
261
176
  appName={webpageName}
262
177
  autoFetch={autoFetch}
178
+ appIcon={appIcon}
263
179
  />
264
180
  );
265
181
 
@@ -300,60 +216,60 @@ export function OnairosButton({
300
216
  case 'signOut':
301
217
  return 'Sign Out of Onairos';
302
218
  default:
303
- return 'Sign In with Onairos';
304
- }
305
- };
306
-
307
- return (
308
- <>
309
- <button
310
- className={buttonClass}
311
- onClick={openTerminal}
312
- style={buttonStyle}
313
- >
314
- {(visualType === 'full' || visualType === 'icon') && (
315
- <img
316
- src={login ? "https://onairos.sirv.com/Images/OnairosWhite.png" : "https://onairos.sirv.com/Images/OnairosBlack.png"}
317
- alt="Onairos Logo"
318
- style={logoStyle}
319
- />
320
- )}
321
- {visualType !== 'icon' && (
322
- <span className={`${textColor === 'black' ? 'text-black' : 'text-white'} ${visualType === 'icon' ? 'sr-only' : ''} ${textLayout === 'right' ? 'ml-2' : textLayout === 'left' ? 'mr-2' : ''}`}>
323
- {getText()}
324
- </span>
325
- )}
326
- </button>
327
-
328
- {/* Modal Overlay */}
329
- {showOverlay && (
330
- <div
331
- className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
332
- onClick={handleBackdropClick}
333
- >
334
- <div
335
- className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-hidden relative"
336
- onClick={(e) => e.stopPropagation()}
337
- >
338
- {/* Close button */}
339
- <button
340
- onClick={handleCloseOverlay}
341
- className="absolute top-4 right-4 text-gray-400 hover:text-gray-600 z-10"
342
- >
343
- <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
344
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
345
- </svg>
346
- </button>
347
-
348
- {/* Content */}
349
- <div className="overflow-y-auto max-h-[90vh]">
350
- {renderCurrentFlow()}
351
- </div>
352
- </div>
353
- </div>
354
- )}
355
- </>
356
- );
357
- }
358
-
219
+ return 'Sign In with Onairos';
220
+ }
221
+ };
222
+
223
+ return (
224
+ <>
225
+ <button
226
+ className={buttonClass}
227
+ onClick={openTerminal}
228
+ style={buttonStyle}
229
+ >
230
+ {(visualType === 'full' || visualType === 'icon') && (
231
+ <img
232
+ src={login ? "https://onairos.sirv.com/Images/OnairosWhite.png" : "https://onairos.sirv.com/Images/OnairosBlack.png"}
233
+ alt="Onairos Logo"
234
+ style={logoStyle}
235
+ />
236
+ )}
237
+ {visualType !== 'icon' && (
238
+ <span className={`${textColor === 'black' ? 'text-black' : 'text-white'} ${visualType === 'icon' ? 'sr-only' : ''} ${textLayout === 'right' ? 'ml-2' : textLayout === 'left' ? 'mr-2' : ''}`}>
239
+ {getText()}
240
+ </span>
241
+ )}
242
+ </button>
243
+
244
+ {/* Full-Screen Overlay (Plaid/SendPay Style) */}
245
+ {showOverlay && (
246
+ <div
247
+ className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
248
+ onClick={handleBackdropClick}
249
+ >
250
+ <div
251
+ className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-hidden relative"
252
+ onClick={(e) => e.stopPropagation()}
253
+ >
254
+ {/* Close button */}
255
+ <button
256
+ onClick={handleCloseOverlay}
257
+ className="absolute top-4 right-4 text-gray-400 hover:text-gray-600 z-10"
258
+ >
259
+ <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
260
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
261
+ </svg>
262
+ </button>
263
+
264
+ {/* Content */}
265
+ <div className="overflow-y-auto max-h-[90vh]">
266
+ {renderCurrentFlow()}
267
+ </div>
268
+ </div>
269
+ </div>
270
+ )}
271
+ </>
272
+ );
273
+ }
274
+
359
275
  export default OnairosButton;