neoagent 2.3.1-beta.54 → 2.3.1-beta.57

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.
@@ -2,6 +2,7 @@ import 'dart:async';
2
2
  import 'dart:convert';
3
3
  import 'dart:io' show Platform;
4
4
  import 'dart:math' as math;
5
+ import 'dart:ui' show ImageFilter;
5
6
 
6
7
  import 'package:connectivity_plus/connectivity_plus.dart';
7
8
  import 'package:hotkey_manager/hotkey_manager.dart';
@@ -36,6 +37,7 @@ import 'src/oauth_launcher.dart';
36
37
  import 'src/recording_bridge.dart';
37
38
  import 'src/recording_payloads.dart';
38
39
  import 'src/theme/palette.dart';
40
+ import 'src/web_app_update_monitor.dart';
39
41
  import 'src/widget_bridge.dart';
40
42
  import 'src/android_auto_bridge.dart';
41
43
 
@@ -74,106 +74,115 @@ class _BackendSetupViewState extends State<BackendSetupView> {
74
74
  padding: const EdgeInsets.all(24),
75
75
  child: ConstrainedBox(
76
76
  constraints: const BoxConstraints(maxWidth: 560),
77
- child: Container(
78
- decoration: BoxDecoration(
79
- gradient: _panelGradient,
77
+ child: _EntranceMotion(
78
+ child: _GlassSurface(
80
79
  borderRadius: BorderRadius.circular(34),
81
- border: Border.all(color: _borderLight),
80
+ blurSigma: 28,
82
81
  boxShadow: _softPanelShadow,
83
- ),
84
- child: Padding(
85
- padding: const EdgeInsets.fromLTRB(34, 32, 34, 30),
86
- child: Column(
87
- crossAxisAlignment: CrossAxisAlignment.start,
88
- children: <Widget>[
89
- const _BrandLockup(logoSize: 60),
90
- const SizedBox(height: 22),
91
- Text('FIRST-RUN SETUP', style: _sectionEyebrowStyle()),
92
- const SizedBox(height: 10),
93
- Text(
94
- 'Connect this build to your NeoAgent backend',
95
- style: _displayTitleStyle(34),
96
- ),
97
- const SizedBox(height: 12),
98
- Text(
99
- 'This build was not bundled with a backend endpoint. Enter your NeoAgent server URL once and the app will store it locally for future launches.',
100
- style: TextStyle(color: _textSecondary, height: 1.55),
101
- ),
102
- const SizedBox(height: 24),
103
- TextField(
104
- controller: _backendUrlController,
105
- keyboardType: TextInputType.url,
106
- textInputAction: TextInputAction.done,
107
- onSubmitted: (_) => _submit(),
108
- decoration: const InputDecoration(
109
- labelText: 'Backend URL',
110
- hintText: 'https://neoagent.example.com',
111
- prefixIcon: Icon(Icons.cloud_outlined),
82
+ overlayGradient: _panelGradient,
83
+ fillColor: _glassFill,
84
+ child: Padding(
85
+ padding: const EdgeInsets.fromLTRB(34, 32, 34, 30),
86
+ child: Column(
87
+ crossAxisAlignment: CrossAxisAlignment.start,
88
+ children: <Widget>[
89
+ const _BrandLockup(logoSize: 60),
90
+ const SizedBox(height: 22),
91
+ Text(
92
+ 'FIRST-RUN SETUP',
93
+ style: _sectionEyebrowStyle(),
112
94
  ),
113
- ),
114
- const SizedBox(height: 14),
115
- Container(
116
- width: double.infinity,
117
- padding: const EdgeInsets.all(14),
118
- decoration: BoxDecoration(
119
- color: _bgSecondary.withValues(alpha: 0.72),
120
- borderRadius: BorderRadius.circular(18),
121
- border: Border.all(color: _borderLight),
95
+ const SizedBox(height: 10),
96
+ Text(
97
+ 'Connect this build to your NeoAgent backend',
98
+ style: _displayTitleStyle(34),
122
99
  ),
123
- child: Row(
124
- crossAxisAlignment: CrossAxisAlignment.start,
125
- children: <Widget>[
126
- Icon(
127
- Icons.privacy_tip_outlined,
128
- color: _accent,
129
- size: 18,
130
- ),
131
- const SizedBox(width: 10),
132
- Expanded(
133
- child: Text(
134
- 'Use your hosted NeoAgent URL. If you enter a hostname without a scheme, the app will infer `https://` for remote hosts and `http://` for local addresses.',
135
- style: TextStyle(
136
- color: _textSecondary,
137
- height: 1.45,
138
- ),
139
- ),
140
- ),
141
- ],
100
+ const SizedBox(height: 12),
101
+ Text(
102
+ 'This build was not bundled with a backend endpoint. Enter your NeoAgent server URL once and the app will store it locally for future launches.',
103
+ style: TextStyle(
104
+ color: _textSecondary,
105
+ height: 1.55,
106
+ ),
142
107
  ),
143
- ),
144
- if (controller.errorMessage
145
- case final message?) ...<Widget>[
146
- const SizedBox(height: 16),
147
- _InlineError(message: message),
148
- ],
149
- const SizedBox(height: 22),
150
- SizedBox(
151
- width: double.infinity,
152
- child: FilledButton.icon(
153
- onPressed: controller.isSavingBackendUrl
154
- ? null
155
- : _submit,
156
- style: FilledButton.styleFrom(
157
- backgroundColor: _accent,
158
- padding: const EdgeInsets.symmetric(vertical: 16),
108
+ const SizedBox(height: 24),
109
+ TextField(
110
+ controller: _backendUrlController,
111
+ keyboardType: TextInputType.url,
112
+ textInputAction: TextInputAction.done,
113
+ onSubmitted: (_) => _submit(),
114
+ decoration: const InputDecoration(
115
+ labelText: 'Backend URL',
116
+ hintText: 'https://neoagent.example.com',
117
+ prefixIcon: Icon(Icons.cloud_outlined),
118
+ ),
119
+ ),
120
+ const SizedBox(height: 14),
121
+ Container(
122
+ width: double.infinity,
123
+ padding: const EdgeInsets.all(14),
124
+ decoration: BoxDecoration(
125
+ color: _bgSecondary.withValues(alpha: 0.72),
126
+ borderRadius: BorderRadius.circular(18),
127
+ border: Border.all(color: _borderLight),
159
128
  ),
160
- icon: controller.isSavingBackendUrl
161
- ? const SizedBox.square(
162
- dimension: 18,
163
- child: CircularProgressIndicator(
164
- strokeWidth: 2,
165
- color: Colors.white,
129
+ child: Row(
130
+ crossAxisAlignment: CrossAxisAlignment.start,
131
+ children: <Widget>[
132
+ Icon(
133
+ Icons.privacy_tip_outlined,
134
+ color: _accent,
135
+ size: 18,
136
+ ),
137
+ const SizedBox(width: 10),
138
+ Expanded(
139
+ child: Text(
140
+ 'Use your hosted NeoAgent URL. If you enter a hostname without a scheme, the app will infer `https://` for remote hosts and `http://` for local addresses.',
141
+ style: TextStyle(
142
+ color: _textSecondary,
143
+ height: 1.45,
166
144
  ),
167
- )
168
- : const Icon(Icons.arrow_forward_rounded),
169
- label: Text(
170
- controller.isSavingBackendUrl
171
- ? 'Connecting...'
172
- : 'Connect Backend',
145
+ ),
146
+ ),
147
+ ],
173
148
  ),
174
149
  ),
175
- ),
176
- ],
150
+ if (controller.errorMessage
151
+ case final message?) ...<Widget>[
152
+ const SizedBox(height: 16),
153
+ _InlineError(message: message),
154
+ ],
155
+ const SizedBox(height: 22),
156
+ SizedBox(
157
+ width: double.infinity,
158
+ child: FilledButton.icon(
159
+ onPressed: controller.isSavingBackendUrl
160
+ ? null
161
+ : _submit,
162
+ style: FilledButton.styleFrom(
163
+ backgroundColor: _accent,
164
+ padding: const EdgeInsets.symmetric(
165
+ vertical: 16,
166
+ ),
167
+ ),
168
+ icon: controller.isSavingBackendUrl
169
+ ? const SizedBox.square(
170
+ dimension: 18,
171
+ child: CircularProgressIndicator(
172
+ strokeWidth: 2,
173
+ color: Colors.white,
174
+ ),
175
+ )
176
+ : const Icon(Icons.arrow_forward_rounded),
177
+ label: Text(
178
+ controller.isSavingBackendUrl
179
+ ? 'Connecting...'
180
+ : 'Connect Backend',
181
+ ),
182
+ ),
183
+ ),
184
+ ],
185
+ ),
177
186
  ),
178
187
  ),
179
188
  ),
@@ -964,61 +973,62 @@ class _AuthViewState extends State<AuthView> {
964
973
  constraints: BoxConstraints(
965
974
  maxWidth: showQrLogin ? 980 : 468,
966
975
  ),
967
- child: Container(
968
- decoration: BoxDecoration(
969
- gradient: _panelGradient,
976
+ child: _EntranceMotion(
977
+ child: _GlassSurface(
970
978
  borderRadius: BorderRadius.circular(32),
971
- border: Border.all(color: _borderLight),
979
+ blurSigma: 28,
972
980
  boxShadow: _softPanelShadow,
973
- ),
974
- child: Padding(
975
- padding: EdgeInsets.fromLTRB(
976
- viewportConstraints.maxWidth < 480 ? 18 : 34,
977
- viewportConstraints.maxWidth < 480 ? 20 : 30,
978
- viewportConstraints.maxWidth < 480 ? 18 : 34,
979
- viewportConstraints.maxWidth < 480 ? 20 : 30,
980
- ),
981
- child: LayoutBuilder(
982
- builder: (context, panelConstraints) {
983
- final useWideQrLayout =
984
- showQrLogin &&
985
- panelConstraints.maxWidth >= 820;
986
- final formPane = _buildAuthFormPane(
987
- controller: controller,
988
- availableProviders: availableProviders,
989
- awaitingTwoFactor: awaitingTwoFactor,
990
- showRegisterToggle: showRegisterToggle,
991
- title: title,
992
- subtitle: subtitle,
993
- );
994
- if (!showQrLogin) {
995
- return formPane;
996
- }
997
- if (useWideQrLayout) {
998
- return Row(
981
+ overlayGradient: _panelGradient,
982
+ fillColor: _glassFill,
983
+ child: Padding(
984
+ padding: EdgeInsets.fromLTRB(
985
+ viewportConstraints.maxWidth < 480 ? 18 : 34,
986
+ viewportConstraints.maxWidth < 480 ? 20 : 30,
987
+ viewportConstraints.maxWidth < 480 ? 18 : 34,
988
+ viewportConstraints.maxWidth < 480 ? 20 : 30,
989
+ ),
990
+ child: LayoutBuilder(
991
+ builder: (context, panelConstraints) {
992
+ final useWideQrLayout =
993
+ showQrLogin &&
994
+ panelConstraints.maxWidth >= 820;
995
+ final formPane = _buildAuthFormPane(
996
+ controller: controller,
997
+ availableProviders: availableProviders,
998
+ awaitingTwoFactor: awaitingTwoFactor,
999
+ showRegisterToggle: showRegisterToggle,
1000
+ title: title,
1001
+ subtitle: subtitle,
1002
+ );
1003
+ if (!showQrLogin) {
1004
+ return formPane;
1005
+ }
1006
+ if (useWideQrLayout) {
1007
+ return Row(
1008
+ crossAxisAlignment:
1009
+ CrossAxisAlignment.start,
1010
+ children: <Widget>[
1011
+ Expanded(flex: 11, child: formPane),
1012
+ const SizedBox(width: 24),
1013
+ Expanded(
1014
+ flex: 10,
1015
+ child: _buildQrLoginPane(controller),
1016
+ ),
1017
+ ],
1018
+ );
1019
+ }
1020
+ return Column(
1021
+ mainAxisSize: MainAxisSize.min,
999
1022
  crossAxisAlignment:
1000
- CrossAxisAlignment.start,
1023
+ CrossAxisAlignment.stretch,
1001
1024
  children: <Widget>[
1002
- Expanded(flex: 11, child: formPane),
1003
- const SizedBox(width: 24),
1004
- Expanded(
1005
- flex: 10,
1006
- child: _buildQrLoginPane(controller),
1007
- ),
1025
+ formPane,
1026
+ const SizedBox(height: 22),
1027
+ _buildQrLoginPane(controller),
1008
1028
  ],
1009
1029
  );
1010
- }
1011
- return Column(
1012
- mainAxisSize: MainAxisSize.min,
1013
- crossAxisAlignment:
1014
- CrossAxisAlignment.stretch,
1015
- children: <Widget>[
1016
- formPane,
1017
- const SizedBox(height: 22),
1018
- _buildQrLoginPane(controller),
1019
- ],
1020
- );
1021
- },
1030
+ },
1031
+ ),
1022
1032
  ),
1023
1033
  ),
1024
1034
  ),
@@ -1052,18 +1062,40 @@ class _HomeViewState extends State<HomeView> {
1052
1062
  @override
1053
1063
  void initState() {
1054
1064
  super.initState();
1055
-
1065
+
1056
1066
  // Initialize Proactive Context Features for mobile
1057
1067
  if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
1058
1068
  final backendUrl = widget.controller.backendUrl;
1059
- final sessionCookie = widget.controller.sessionCookie ?? '';
1060
-
1061
- LocationService().initialize(context).then((_) {
1062
- LocationService().startGeofenceTracking(backendUrl, sessionCookie);
1063
- });
1064
-
1065
- if (Platform.isAndroid) {
1066
- NotificationInterceptor().initialize(context, backendUrl, sessionCookie);
1069
+ final sessionCookie = widget.controller.sessionCookie?.trim() ?? '';
1070
+ final canInitializeMobileAutomation =
1071
+ backendUrl.trim().isNotEmpty && sessionCookie.isNotEmpty;
1072
+
1073
+ if (canInitializeMobileAutomation) {
1074
+ final locationService = LocationService();
1075
+
1076
+ locationService
1077
+ .initialize(context)
1078
+ .then((_) {
1079
+ if (mounted) {
1080
+ locationService.startGeofenceTracking(
1081
+ backendUrl,
1082
+ sessionCookie,
1083
+ );
1084
+ }
1085
+ })
1086
+ .catchError((error) {
1087
+ if (mounted) {
1088
+ debugPrint('LocationService initialization failed: $error');
1089
+ }
1090
+ });
1091
+
1092
+ if (Platform.isAndroid) {
1093
+ NotificationInterceptor().initialize(
1094
+ context,
1095
+ backendUrl,
1096
+ sessionCookie,
1097
+ );
1098
+ }
1067
1099
  }
1068
1100
  }
1069
1101
 
@@ -1157,13 +1189,12 @@ class _HomeViewState extends State<HomeView> {
1157
1189
  ),
1158
1190
  const SizedBox(width: 14),
1159
1191
  Expanded(
1160
- child: Container(
1161
- decoration: BoxDecoration(
1162
- gradient: _panelGradient,
1163
- borderRadius: BorderRadius.circular(32),
1164
- border: Border.all(color: _borderLight),
1165
- boxShadow: _softPanelShadow,
1166
- ),
1192
+ child: _GlassSurface(
1193
+ borderRadius: BorderRadius.circular(32),
1194
+ blurSigma: 28,
1195
+ boxShadow: _softPanelShadow,
1196
+ overlayGradient: _panelGradient,
1197
+ fillColor: _glassFill,
1167
1198
  child: ClipRRect(
1168
1199
  borderRadius: BorderRadius.circular(32),
1169
1200
  child: AnimatedSwitcher(
@@ -1216,13 +1247,12 @@ class _HomeViewState extends State<HomeView> {
1216
1247
  body: SafeArea(
1217
1248
  child: Padding(
1218
1249
  padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
1219
- child: Container(
1220
- decoration: BoxDecoration(
1221
- gradient: _panelGradient,
1222
- borderRadius: BorderRadius.circular(26),
1223
- border: Border.all(color: _borderLight),
1224
- boxShadow: _softPanelShadow,
1225
- ),
1250
+ child: _GlassSurface(
1251
+ borderRadius: BorderRadius.circular(26),
1252
+ blurSigma: 24,
1253
+ boxShadow: _softPanelShadow,
1254
+ overlayGradient: _panelGradient,
1255
+ fillColor: _glassFill,
1226
1256
  child: ClipRRect(
1227
1257
  borderRadius: BorderRadius.circular(26),
1228
1258
  child: AnimatedSwitcher(
@@ -1288,10 +1318,11 @@ class _HomeViewState extends State<HomeView> {
1288
1318
  child: FilledButton.icon(
1289
1319
  onPressed: () async {
1290
1320
  Navigator.of(dialogContext).pop();
1291
- await widget.controller.allowMessagingSuggestion(
1292
- notice.platform,
1293
- suggestion,
1294
- );
1321
+ await widget.controller
1322
+ .allowMessagingSuggestion(
1323
+ notice.platform,
1324
+ suggestion,
1325
+ );
1295
1326
  },
1296
1327
  icon: Icon(Icons.verified_user_outlined),
1297
1328
  label: Text(suggestion.label),
@@ -1344,20 +1375,19 @@ class _Sidebar extends StatelessWidget {
1344
1375
 
1345
1376
  @override
1346
1377
  Widget build(BuildContext context) {
1347
- return Container(
1378
+ return _GlassSurface(
1348
1379
  width: 254,
1349
- decoration: BoxDecoration(
1350
- gradient: LinearGradient(
1351
- colors: <Color>[
1352
- _bgSecondary.withValues(alpha: 0.96),
1353
- _bgTertiary.withValues(alpha: 0.92),
1354
- ],
1355
- begin: Alignment.topCenter,
1356
- end: Alignment.bottomCenter,
1357
- ),
1358
- borderRadius: BorderRadius.circular(30),
1359
- border: Border.all(color: _borderLight),
1360
- boxShadow: _softPanelShadow,
1380
+ borderRadius: BorderRadius.circular(30),
1381
+ blurSigma: 26,
1382
+ boxShadow: _softPanelShadow,
1383
+ fillColor: _bgSecondary.withValues(alpha: 0.34),
1384
+ overlayGradient: LinearGradient(
1385
+ colors: <Color>[
1386
+ _bgSecondary.withValues(alpha: 0.96),
1387
+ _bgTertiary.withValues(alpha: 0.88),
1388
+ ],
1389
+ begin: Alignment.topCenter,
1390
+ end: Alignment.bottomCenter,
1361
1391
  ),
1362
1392
  child: Column(
1363
1393
  children: <Widget>[
@@ -32,15 +32,13 @@ enum AppSection {
32
32
  health,
33
33
  }
34
34
 
35
- enum SidebarGroup { chat, agents, recordings, activity, automation, settings }
35
+ enum SidebarGroup { chat, recordings, activity, automation, settings }
36
36
 
37
37
  extension SidebarGroupX on SidebarGroup {
38
38
  String get label {
39
39
  switch (this) {
40
40
  case SidebarGroup.chat:
41
41
  return 'Chat';
42
- case SidebarGroup.agents:
43
- return 'Agents';
44
42
  case SidebarGroup.recordings:
45
43
  return 'Recordings';
46
44
  case SidebarGroup.activity:
@@ -56,8 +54,6 @@ extension SidebarGroupX on SidebarGroup {
56
54
  switch (this) {
57
55
  case SidebarGroup.chat:
58
56
  return Icons.chat_bubble_outline;
59
- case SidebarGroup.agents:
60
- return Icons.smart_toy_outlined;
61
57
  case SidebarGroup.recordings:
62
58
  return Icons.fiber_smart_record_outlined;
63
59
  case SidebarGroup.activity:
@@ -171,9 +167,8 @@ extension AppSectionX on AppSection {
171
167
  case AppSection.settings:
172
168
  case AppSection.accountSettings:
173
169
  case AppSection.messaging:
174
- return SidebarGroup.settings;
175
170
  case AppSection.agents:
176
- return SidebarGroup.agents;
171
+ return SidebarGroup.settings;
177
172
  }
178
173
  }
179
174