neoagent 3.1.1-beta.2 → 3.1.1-beta.4
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/extensions/chrome-browser/manifest.json +1 -1
- package/extensions/chrome-browser/protocol.mjs +36 -0
- package/flutter_app/lib/main_app_shell.dart +9 -3
- package/flutter_app/lib/main_controller.dart +49 -0
- package/flutter_app/lib/main_models.dart +55 -0
- package/flutter_app/lib/main_operations.dart +530 -2
- package/flutter_app/lib/main_settings.dart +301 -1
- package/flutter_app/lib/main_shared.dart +7 -2
- package/flutter_app/lib/main_timeline.dart +82 -11
- package/flutter_app/lib/src/backend_client.dart +50 -0
- package/landing/index.html +3 -0
- package/landing/status.html +178 -0
- package/lib/schema_migrations.js +23 -0
- package/package.json +1 -1
- package/server/db/database.js +6 -0
- package/server/http/routes.js +2 -0
- package/server/http/static.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +76806 -75964
- package/server/routes/public_status.js +12 -0
- package/server/routes/settings.js +14 -0
- package/server/routes/social_reach.js +88 -0
- package/server/routes/tasks.js +17 -0
- package/server/services/ai/tools.js +60 -1
- package/server/services/browser/extension/protocol.js +1 -0
- package/server/services/manager.js +14 -0
- package/server/services/memory/consolidation.js +2 -0
- package/server/services/memory/manager.js +70 -8
- package/server/services/memory/retention.js +66 -0
- package/server/services/memory/routing.js +68 -0
- package/server/services/messaging/access_policy.js +39 -2
- package/server/services/messaging/manager.js +9 -4
- package/server/services/public_status.js +103 -0
- package/server/services/runtime/settings.js +6 -0
- package/server/services/social_reach/channels/base.js +56 -0
- package/server/services/social_reach/channels/github.js +75 -0
- package/server/services/social_reach/channels/index.js +34 -0
- package/server/services/social_reach/channels/linkedin.js +34 -0
- package/server/services/social_reach/channels/rss.js +58 -0
- package/server/services/social_reach/channels/unsupported.js +49 -0
- package/server/services/social_reach/channels/v2ex.js +85 -0
- package/server/services/social_reach/channels/web.js +40 -0
- package/server/services/social_reach/channels/xueqiu.js +101 -0
- package/server/services/social_reach/channels/youtube.js +42 -0
- package/server/services/social_reach/index.js +7 -0
- package/server/services/social_reach/platforms.js +162 -0
- package/server/services/social_reach/service.js +163 -0
- package/server/services/social_reach/store.js +89 -0
- package/server/services/social_reach/utils.js +95 -0
- package/server/services/tasks/delivery_targets.js +260 -0
- package/server/utils/whatsapp.js +33 -1
|
@@ -117,6 +117,22 @@ const _modelsSettingsSection = _SettingsSection('models', <String>[
|
|
|
117
117
|
'smart selector',
|
|
118
118
|
]);
|
|
119
119
|
|
|
120
|
+
const _socialReachSettingsSection = _SettingsSection('social reach', <String>[
|
|
121
|
+
'social',
|
|
122
|
+
'reach',
|
|
123
|
+
'web',
|
|
124
|
+
'rss',
|
|
125
|
+
'github',
|
|
126
|
+
'youtube',
|
|
127
|
+
'linkedin',
|
|
128
|
+
'xueqiu',
|
|
129
|
+
'twitter',
|
|
130
|
+
'reddit',
|
|
131
|
+
'instagram',
|
|
132
|
+
'facebook',
|
|
133
|
+
'cookies',
|
|
134
|
+
]);
|
|
135
|
+
|
|
120
136
|
const _voiceRecordingSettingsSection = _SettingsSection(
|
|
121
137
|
'voice recording',
|
|
122
138
|
<String>[
|
|
@@ -164,6 +180,7 @@ const _securitySettingsSection = _SettingsSection('security', <String>[
|
|
|
164
180
|
const List<_SettingsSection> _settingsSearchSections = <_SettingsSection>[
|
|
165
181
|
_overviewSettingsSection,
|
|
166
182
|
_workspaceSettingsSection,
|
|
183
|
+
_socialReachSettingsSection,
|
|
167
184
|
_modelsSettingsSection,
|
|
168
185
|
_voiceRecordingSettingsSection,
|
|
169
186
|
_desktopSettingsSection,
|
|
@@ -201,6 +218,9 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
201
218
|
Map<String, dynamic>? _extensionTestResult;
|
|
202
219
|
bool _desktopTestRunning = false;
|
|
203
220
|
Map<String, dynamic>? _desktopTestResult;
|
|
221
|
+
bool _socialReachRefreshing = false;
|
|
222
|
+
String? _socialReachBusyPlatform;
|
|
223
|
+
Map<String, dynamic>? _socialReachActionResult;
|
|
204
224
|
|
|
205
225
|
@override
|
|
206
226
|
void initState() {
|
|
@@ -399,6 +419,13 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
399
419
|
_buildWorkspaceSection(controller),
|
|
400
420
|
const SizedBox(height: 16),
|
|
401
421
|
],
|
|
422
|
+
if (_matchesSettingsSection(
|
|
423
|
+
searchQuery,
|
|
424
|
+
_socialReachSettingsSection,
|
|
425
|
+
)) ...<Widget>[
|
|
426
|
+
_buildSocialReachSection(controller),
|
|
427
|
+
const SizedBox(height: 16),
|
|
428
|
+
],
|
|
402
429
|
if (_matchesSettingsSection(
|
|
403
430
|
searchQuery,
|
|
404
431
|
_modelsSettingsSection,
|
|
@@ -920,6 +947,278 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
920
947
|
);
|
|
921
948
|
}
|
|
922
949
|
|
|
950
|
+
List<Map<String, dynamic>> _socialReachPlatforms(
|
|
951
|
+
NeoAgentController controller,
|
|
952
|
+
) {
|
|
953
|
+
final raw = controller.socialReachStatus['platforms'];
|
|
954
|
+
if (raw is! List) return const <Map<String, dynamic>>[];
|
|
955
|
+
return raw
|
|
956
|
+
.whereType<Map>()
|
|
957
|
+
.map((item) => Map<String, dynamic>.from(item))
|
|
958
|
+
.toList();
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
Color _socialReachStatusColor(Map<String, dynamic> platform) {
|
|
962
|
+
final status = platform['status']?.toString().toLowerCase() ?? '';
|
|
963
|
+
if (platform['ready'] == true || status == 'ok') return _success;
|
|
964
|
+
if (status == 'warn') return _warning;
|
|
965
|
+
if (status == 'error') return _danger;
|
|
966
|
+
return _textSecondary;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
Widget _buildSocialReachSection(NeoAgentController controller) {
|
|
970
|
+
final platforms = _socialReachPlatforms(controller);
|
|
971
|
+
final ready = platforms
|
|
972
|
+
.where((item) => item['ready'] == true || item['status'] == 'ok')
|
|
973
|
+
.length;
|
|
974
|
+
final unsupported = platforms
|
|
975
|
+
.where((item) => item['setupKind'] == 'unsupported_node_only')
|
|
976
|
+
.length;
|
|
977
|
+
return Card(
|
|
978
|
+
child: Padding(
|
|
979
|
+
padding: const EdgeInsets.all(20),
|
|
980
|
+
child: Column(
|
|
981
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
982
|
+
children: <Widget>[
|
|
983
|
+
Row(
|
|
984
|
+
children: <Widget>[
|
|
985
|
+
const Expanded(child: _SectionTitle('Social Reach')),
|
|
986
|
+
IconButton(
|
|
987
|
+
tooltip: 'Refresh',
|
|
988
|
+
onPressed: _socialReachRefreshing
|
|
989
|
+
? null
|
|
990
|
+
: () async {
|
|
991
|
+
setState(() {
|
|
992
|
+
_socialReachRefreshing = true;
|
|
993
|
+
_socialReachActionResult = null;
|
|
994
|
+
});
|
|
995
|
+
try {
|
|
996
|
+
await controller.refreshSocialReachStatus();
|
|
997
|
+
} catch (e) {
|
|
998
|
+
if (mounted) {
|
|
999
|
+
setState(
|
|
1000
|
+
() => _socialReachActionResult =
|
|
1001
|
+
<String, dynamic>{'error': e.toString()},
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
} finally {
|
|
1005
|
+
if (mounted) {
|
|
1006
|
+
setState(() => _socialReachRefreshing = false);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
icon: _socialReachRefreshing
|
|
1011
|
+
? const SizedBox.square(
|
|
1012
|
+
dimension: 18,
|
|
1013
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
1014
|
+
)
|
|
1015
|
+
: const Icon(Icons.refresh_rounded),
|
|
1016
|
+
),
|
|
1017
|
+
],
|
|
1018
|
+
),
|
|
1019
|
+
const SizedBox(height: 10),
|
|
1020
|
+
Text(
|
|
1021
|
+
'Node-native web and social content access for agents.',
|
|
1022
|
+
style: TextStyle(color: _textSecondary, height: 1.45),
|
|
1023
|
+
),
|
|
1024
|
+
const SizedBox(height: 12),
|
|
1025
|
+
Wrap(
|
|
1026
|
+
spacing: 8,
|
|
1027
|
+
runSpacing: 8,
|
|
1028
|
+
children: <Widget>[
|
|
1029
|
+
_MetaPill(
|
|
1030
|
+
icon: Icons.check_circle_outline,
|
|
1031
|
+
label: '$ready ready',
|
|
1032
|
+
color: _success,
|
|
1033
|
+
),
|
|
1034
|
+
_MetaPill(
|
|
1035
|
+
icon: Icons.code_outlined,
|
|
1036
|
+
label: 'Node-only mode',
|
|
1037
|
+
color: _info,
|
|
1038
|
+
),
|
|
1039
|
+
_MetaPill(
|
|
1040
|
+
icon: Icons.block_outlined,
|
|
1041
|
+
label: '$unsupported unavailable',
|
|
1042
|
+
color: _warning,
|
|
1043
|
+
),
|
|
1044
|
+
],
|
|
1045
|
+
),
|
|
1046
|
+
if (_socialReachActionResult != null) ...<Widget>[
|
|
1047
|
+
const SizedBox(height: 12),
|
|
1048
|
+
Container(
|
|
1049
|
+
width: double.infinity,
|
|
1050
|
+
padding: const EdgeInsets.all(10),
|
|
1051
|
+
decoration: BoxDecoration(
|
|
1052
|
+
color:
|
|
1053
|
+
(_socialReachActionResult!['error'] == null
|
|
1054
|
+
? _success
|
|
1055
|
+
: _danger)
|
|
1056
|
+
.withValues(alpha: 0.10),
|
|
1057
|
+
borderRadius: BorderRadius.circular(8),
|
|
1058
|
+
border: Border.all(
|
|
1059
|
+
color:
|
|
1060
|
+
(_socialReachActionResult!['error'] == null
|
|
1061
|
+
? _success
|
|
1062
|
+
: _danger)
|
|
1063
|
+
.withValues(alpha: 0.30),
|
|
1064
|
+
),
|
|
1065
|
+
),
|
|
1066
|
+
child: Text(
|
|
1067
|
+
_socialReachActionResult!['error']?.toString() ??
|
|
1068
|
+
'Social Reach updated.',
|
|
1069
|
+
style: TextStyle(
|
|
1070
|
+
color: _socialReachActionResult!['error'] == null
|
|
1071
|
+
? _success
|
|
1072
|
+
: _danger,
|
|
1073
|
+
),
|
|
1074
|
+
),
|
|
1075
|
+
),
|
|
1076
|
+
],
|
|
1077
|
+
const SizedBox(height: 16),
|
|
1078
|
+
if (platforms.isEmpty)
|
|
1079
|
+
Text(
|
|
1080
|
+
'Status is not loaded yet.',
|
|
1081
|
+
style: TextStyle(color: _textSecondary),
|
|
1082
|
+
)
|
|
1083
|
+
else
|
|
1084
|
+
...platforms.map(
|
|
1085
|
+
(platform) => Padding(
|
|
1086
|
+
padding: const EdgeInsets.only(bottom: 10),
|
|
1087
|
+
child: _buildSocialReachPlatformRow(controller, platform),
|
|
1088
|
+
),
|
|
1089
|
+
),
|
|
1090
|
+
],
|
|
1091
|
+
),
|
|
1092
|
+
),
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
Widget _buildSocialReachPlatformRow(
|
|
1097
|
+
NeoAgentController controller,
|
|
1098
|
+
Map<String, dynamic> platform,
|
|
1099
|
+
) {
|
|
1100
|
+
final id = platform['platform']?.toString() ?? '';
|
|
1101
|
+
final label = platform['label']?.toString() ?? id;
|
|
1102
|
+
final setupKind = platform['setupKind']?.toString() ?? '';
|
|
1103
|
+
final status = platform['status']?.toString() ?? 'off';
|
|
1104
|
+
final message = platform['message']?.toString() ?? '';
|
|
1105
|
+
final cookie = platform['cookie'] is Map
|
|
1106
|
+
? Map<String, dynamic>.from(platform['cookie'] as Map)
|
|
1107
|
+
: const <String, dynamic>{};
|
|
1108
|
+
final busy = _socialReachBusyPlatform == id;
|
|
1109
|
+
final canImport = setupKind == 'cookies';
|
|
1110
|
+
return Container(
|
|
1111
|
+
padding: const EdgeInsets.all(14),
|
|
1112
|
+
decoration: BoxDecoration(
|
|
1113
|
+
color: _bgSecondary,
|
|
1114
|
+
borderRadius: BorderRadius.circular(8),
|
|
1115
|
+
border: Border.all(color: _border),
|
|
1116
|
+
),
|
|
1117
|
+
child: Column(
|
|
1118
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
1119
|
+
children: <Widget>[
|
|
1120
|
+
Row(
|
|
1121
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
1122
|
+
children: <Widget>[
|
|
1123
|
+
Expanded(
|
|
1124
|
+
child: Text(
|
|
1125
|
+
label,
|
|
1126
|
+
style: TextStyle(
|
|
1127
|
+
color: _textPrimary,
|
|
1128
|
+
fontWeight: FontWeight.w800,
|
|
1129
|
+
),
|
|
1130
|
+
),
|
|
1131
|
+
),
|
|
1132
|
+
_StatusPill(
|
|
1133
|
+
label: status,
|
|
1134
|
+
color: _socialReachStatusColor(platform),
|
|
1135
|
+
),
|
|
1136
|
+
],
|
|
1137
|
+
),
|
|
1138
|
+
const SizedBox(height: 6),
|
|
1139
|
+
Text(message, style: TextStyle(color: _textSecondary, height: 1.35)),
|
|
1140
|
+
if (cookie.isNotEmpty) ...<Widget>[
|
|
1141
|
+
const SizedBox(height: 8),
|
|
1142
|
+
Text(
|
|
1143
|
+
cookie['configured'] == true
|
|
1144
|
+
? '${cookie['count'] ?? 0} cookies imported'
|
|
1145
|
+
: 'Cookies not configured',
|
|
1146
|
+
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
1147
|
+
),
|
|
1148
|
+
],
|
|
1149
|
+
if (canImport) ...<Widget>[
|
|
1150
|
+
const SizedBox(height: 10),
|
|
1151
|
+
Wrap(
|
|
1152
|
+
spacing: 8,
|
|
1153
|
+
runSpacing: 8,
|
|
1154
|
+
children: <Widget>[
|
|
1155
|
+
FilledButton.icon(
|
|
1156
|
+
onPressed: busy
|
|
1157
|
+
? null
|
|
1158
|
+
: () => _runSocialReachAction(
|
|
1159
|
+
controller,
|
|
1160
|
+
id,
|
|
1161
|
+
() => controller.importSocialReachCookies(id),
|
|
1162
|
+
),
|
|
1163
|
+
icon: busy
|
|
1164
|
+
? const SizedBox.square(
|
|
1165
|
+
dimension: 16,
|
|
1166
|
+
child: CircularProgressIndicator(
|
|
1167
|
+
strokeWidth: 2,
|
|
1168
|
+
color: Colors.white,
|
|
1169
|
+
),
|
|
1170
|
+
)
|
|
1171
|
+
: const Icon(Icons.extension_outlined, size: 18),
|
|
1172
|
+
label: const Text('Import from extension'),
|
|
1173
|
+
),
|
|
1174
|
+
OutlinedButton.icon(
|
|
1175
|
+
onPressed: busy
|
|
1176
|
+
? null
|
|
1177
|
+
: () => _runSocialReachAction(
|
|
1178
|
+
controller,
|
|
1179
|
+
id,
|
|
1180
|
+
() => controller.clearSocialReachCookies(id),
|
|
1181
|
+
),
|
|
1182
|
+
icon: const Icon(Icons.delete_outline, size: 18),
|
|
1183
|
+
label: const Text('Clear'),
|
|
1184
|
+
),
|
|
1185
|
+
],
|
|
1186
|
+
),
|
|
1187
|
+
],
|
|
1188
|
+
],
|
|
1189
|
+
),
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
Future<void> _runSocialReachAction(
|
|
1194
|
+
NeoAgentController controller,
|
|
1195
|
+
String platform,
|
|
1196
|
+
Future<Map<String, dynamic>> Function() action,
|
|
1197
|
+
) async {
|
|
1198
|
+
setState(() {
|
|
1199
|
+
_socialReachBusyPlatform = platform;
|
|
1200
|
+
_socialReachActionResult = null;
|
|
1201
|
+
});
|
|
1202
|
+
try {
|
|
1203
|
+
final result = await action();
|
|
1204
|
+
if (mounted) {
|
|
1205
|
+
setState(() => _socialReachActionResult = result);
|
|
1206
|
+
}
|
|
1207
|
+
} catch (e) {
|
|
1208
|
+
if (mounted) {
|
|
1209
|
+
setState(
|
|
1210
|
+
() => _socialReachActionResult = <String, dynamic>{
|
|
1211
|
+
'error': e.toString(),
|
|
1212
|
+
},
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
} finally {
|
|
1216
|
+
if (mounted) {
|
|
1217
|
+
setState(() => _socialReachBusyPlatform = null);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
923
1222
|
Widget _buildModelsSection({
|
|
924
1223
|
required BuildContext context,
|
|
925
1224
|
required NeoAgentController controller,
|
|
@@ -1075,11 +1374,12 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
1075
1374
|
selectedIds: _enabledModels,
|
|
1076
1375
|
),
|
|
1077
1376
|
);
|
|
1078
|
-
if (result != null)
|
|
1377
|
+
if (result != null) {
|
|
1079
1378
|
setState(() {
|
|
1080
1379
|
_enabledModels = result;
|
|
1081
1380
|
_hasUnsavedChanges = true;
|
|
1082
1381
|
});
|
|
1382
|
+
}
|
|
1083
1383
|
},
|
|
1084
1384
|
),
|
|
1085
1385
|
],
|
|
@@ -588,7 +588,7 @@ class _PageTitle extends StatelessWidget {
|
|
|
588
588
|
Widget build(BuildContext context) {
|
|
589
589
|
final compact = MediaQuery.sizeOf(context).width < 760;
|
|
590
590
|
final titleStyle = compact
|
|
591
|
-
? _displayTitleStyle(
|
|
591
|
+
? _displayTitleStyle(22)
|
|
592
592
|
: _displayTitleStyle(32);
|
|
593
593
|
final subtitleStyle = TextStyle(
|
|
594
594
|
color: _textSecondary,
|
|
@@ -603,7 +603,12 @@ class _PageTitle extends StatelessWidget {
|
|
|
603
603
|
children: <Widget>[
|
|
604
604
|
Text('CONTROL SURFACE', style: _sectionEyebrowStyle()),
|
|
605
605
|
const SizedBox(height: 6),
|
|
606
|
-
Text(
|
|
606
|
+
Text(
|
|
607
|
+
title,
|
|
608
|
+
maxLines: 1,
|
|
609
|
+
overflow: TextOverflow.ellipsis,
|
|
610
|
+
style: titleStyle,
|
|
611
|
+
),
|
|
607
612
|
const SizedBox(height: 8),
|
|
608
613
|
ConstrainedBox(
|
|
609
614
|
constraints: const BoxConstraints(maxWidth: 720),
|
|
@@ -51,6 +51,67 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
51
51
|
return items.first;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
Future<void> _showMobileEventDetails(
|
|
55
|
+
List<TimelineEventItem> items,
|
|
56
|
+
TimelineEventItem initialEvent,
|
|
57
|
+
) async {
|
|
58
|
+
_selectEvent(initialEvent);
|
|
59
|
+
var selectedIndex = items.indexWhere((item) => item.id == initialEvent.id);
|
|
60
|
+
if (selectedIndex < 0) {
|
|
61
|
+
selectedIndex = 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await showModalBottomSheet<void>(
|
|
65
|
+
context: context,
|
|
66
|
+
isScrollControlled: true,
|
|
67
|
+
useSafeArea: true,
|
|
68
|
+
backgroundColor: Colors.transparent,
|
|
69
|
+
builder: (context) {
|
|
70
|
+
return StatefulBuilder(
|
|
71
|
+
builder: (context, setSheetState) {
|
|
72
|
+
final selectedEvent = items[selectedIndex];
|
|
73
|
+
void selectOffset(int offset) {
|
|
74
|
+
final nextIndex = (selectedIndex + offset).clamp(
|
|
75
|
+
0,
|
|
76
|
+
items.length - 1,
|
|
77
|
+
);
|
|
78
|
+
if (nextIndex == selectedIndex) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
setSheetState(() {
|
|
82
|
+
selectedIndex = nextIndex;
|
|
83
|
+
});
|
|
84
|
+
_selectEvent(items[nextIndex]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return FractionallySizedBox(
|
|
88
|
+
heightFactor: 0.9,
|
|
89
|
+
child: Padding(
|
|
90
|
+
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
|
|
91
|
+
child: _TimelineDetailPane(
|
|
92
|
+
items: items,
|
|
93
|
+
selectedEvent: selectedEvent,
|
|
94
|
+
selectedIndex: selectedIndex,
|
|
95
|
+
onSelectPrevious: selectedIndex > 0
|
|
96
|
+
? () => selectOffset(-1)
|
|
97
|
+
: null,
|
|
98
|
+
onSelectNext: selectedIndex < items.length - 1
|
|
99
|
+
? () => selectOffset(1)
|
|
100
|
+
: null,
|
|
101
|
+
onOpenRun: selectedEvent.runId.isNotEmpty
|
|
102
|
+
? () => unawaited(
|
|
103
|
+
widget.controller.openRunDetails(selectedEvent.runId),
|
|
104
|
+
)
|
|
105
|
+
: null,
|
|
106
|
+
),
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
54
115
|
@override
|
|
55
116
|
Widget build(BuildContext context) {
|
|
56
117
|
final items = _sortedTimelineEvents(widget.controller.timelineItems);
|
|
@@ -87,10 +148,15 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
87
148
|
: LayoutBuilder(
|
|
88
149
|
builder: (context, constraints) {
|
|
89
150
|
final isWide = constraints.maxWidth >= 1180;
|
|
151
|
+
final isCompact = constraints.maxWidth < 760;
|
|
90
152
|
final feedPane = _TimelineFeedPane(
|
|
91
153
|
groups: groups,
|
|
92
154
|
selectedEventId: selectedEvent?.id,
|
|
93
|
-
onSelectEvent:
|
|
155
|
+
onSelectEvent: isCompact
|
|
156
|
+
? (item) => unawaited(
|
|
157
|
+
_showMobileEventDetails(items, item),
|
|
158
|
+
)
|
|
159
|
+
: _selectEvent,
|
|
94
160
|
);
|
|
95
161
|
final detailPane = _TimelineDetailPane(
|
|
96
162
|
items: items,
|
|
@@ -126,6 +192,10 @@ class _TimelinePanelState extends State<TimelinePanel> {
|
|
|
126
192
|
);
|
|
127
193
|
}
|
|
128
194
|
|
|
195
|
+
if (isCompact) {
|
|
196
|
+
return feedPane;
|
|
197
|
+
}
|
|
198
|
+
|
|
129
199
|
return Column(
|
|
130
200
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
131
201
|
children: <Widget>[
|
|
@@ -156,13 +226,14 @@ class _TimelineHeroHeader extends StatelessWidget {
|
|
|
156
226
|
|
|
157
227
|
@override
|
|
158
228
|
Widget build(BuildContext context) {
|
|
229
|
+
final compact = MediaQuery.sizeOf(context).width < 760;
|
|
159
230
|
final focusedDay =
|
|
160
231
|
selectedEvent?.occurredAt ??
|
|
161
232
|
(items.isEmpty ? null : items.first.occurredAt);
|
|
162
233
|
|
|
163
234
|
return Container(
|
|
164
235
|
width: double.infinity,
|
|
165
|
-
padding:
|
|
236
|
+
padding: EdgeInsets.all(compact ? 16 : 22),
|
|
166
237
|
decoration: BoxDecoration(
|
|
167
238
|
gradient: LinearGradient(
|
|
168
239
|
colors: <Color>[
|
|
@@ -172,7 +243,7 @@ class _TimelineHeroHeader extends StatelessWidget {
|
|
|
172
243
|
begin: Alignment.topLeft,
|
|
173
244
|
end: Alignment.bottomRight,
|
|
174
245
|
),
|
|
175
|
-
borderRadius: BorderRadius.circular(28),
|
|
246
|
+
borderRadius: BorderRadius.circular(compact ? 20 : 28),
|
|
176
247
|
border: Border.all(color: _borderLight),
|
|
177
248
|
boxShadow: <BoxShadow>[
|
|
178
249
|
BoxShadow(
|
|
@@ -195,27 +266,27 @@ class _TimelineHeroHeader extends StatelessWidget {
|
|
|
195
266
|
'ACTIVITY FEED',
|
|
196
267
|
style: TextStyle(
|
|
197
268
|
color: _accentHover,
|
|
198
|
-
fontSize: 13,
|
|
269
|
+
fontSize: compact ? 11 : 13,
|
|
199
270
|
fontWeight: FontWeight.w700,
|
|
200
|
-
letterSpacing: 4.2,
|
|
271
|
+
letterSpacing: compact ? 2.4 : 4.2,
|
|
201
272
|
),
|
|
202
273
|
),
|
|
203
|
-
|
|
204
|
-
|
|
274
|
+
SizedBox(height: compact ? 8 : 14),
|
|
275
|
+
Text(
|
|
205
276
|
'Timeline',
|
|
206
277
|
style: TextStyle(
|
|
207
|
-
fontSize: 40,
|
|
278
|
+
fontSize: compact ? 28 : 40,
|
|
208
279
|
fontWeight: FontWeight.w800,
|
|
209
280
|
height: 1,
|
|
210
281
|
),
|
|
211
282
|
),
|
|
212
|
-
|
|
283
|
+
SizedBox(height: compact ? 8 : 14),
|
|
213
284
|
Text(
|
|
214
285
|
'Emails, AI actions, recordings, tasks and run activity in one chronological feed.',
|
|
215
286
|
style: TextStyle(
|
|
216
287
|
color: _textSecondary,
|
|
217
|
-
fontSize: 16.5,
|
|
218
|
-
height: 1.35,
|
|
288
|
+
fontSize: compact ? 13.5 : 16.5,
|
|
289
|
+
height: compact ? 1.28 : 1.35,
|
|
219
290
|
),
|
|
220
291
|
),
|
|
221
292
|
],
|
|
@@ -524,6 +524,36 @@ class BackendClient {
|
|
|
524
524
|
);
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
Future<Map<String, dynamic>> fetchSocialReachStatus(String baseUrl) async {
|
|
528
|
+
return getMap(baseUrl, '/api/social-reach/status');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
Future<Map<String, dynamic>> importSocialReachCookies(
|
|
532
|
+
String baseUrl,
|
|
533
|
+
String platform, {
|
|
534
|
+
String? tokenId,
|
|
535
|
+
}) async {
|
|
536
|
+
return postMap(
|
|
537
|
+
baseUrl,
|
|
538
|
+
'/api/social-reach/cookies/import',
|
|
539
|
+
<String, dynamic>{
|
|
540
|
+
'platform': platform,
|
|
541
|
+
if (tokenId != null && tokenId.trim().isNotEmpty)
|
|
542
|
+
'tokenId': tokenId.trim(),
|
|
543
|
+
},
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
Future<Map<String, dynamic>> clearSocialReachCookies(
|
|
548
|
+
String baseUrl,
|
|
549
|
+
String platform,
|
|
550
|
+
) async {
|
|
551
|
+
return deleteMap(
|
|
552
|
+
baseUrl,
|
|
553
|
+
'/api/social-reach/cookies/${Uri.encodeComponent(platform)}',
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
527
557
|
Future<Map<String, dynamic>> fetchUpdateStatus(String baseUrl) async {
|
|
528
558
|
return getMap(baseUrl, '/api/settings/update/status');
|
|
529
559
|
}
|
|
@@ -1660,6 +1690,26 @@ class BackendClient {
|
|
|
1660
1690
|
return getList(baseUrl, _withAgentQuery('/api/tasks', agentId));
|
|
1661
1691
|
}
|
|
1662
1692
|
|
|
1693
|
+
Future<List<Map<String, dynamic>>> fetchTaskDeliveryTargets(
|
|
1694
|
+
String baseUrl, {
|
|
1695
|
+
String? query,
|
|
1696
|
+
String? platform,
|
|
1697
|
+
String? agentId,
|
|
1698
|
+
}) async {
|
|
1699
|
+
final params = <String>[
|
|
1700
|
+
if (query != null && query.trim().isNotEmpty)
|
|
1701
|
+
'q=${Uri.encodeQueryComponent(query.trim())}',
|
|
1702
|
+
if (platform != null && platform.trim().isNotEmpty)
|
|
1703
|
+
'platform=${Uri.encodeQueryComponent(platform.trim())}',
|
|
1704
|
+
if (agentId != null && agentId.trim().isNotEmpty)
|
|
1705
|
+
'agentId=${Uri.encodeQueryComponent(agentId.trim())}',
|
|
1706
|
+
];
|
|
1707
|
+
final path = params.isEmpty
|
|
1708
|
+
? _withAgentQuery('/api/tasks/delivery-targets', agentId)
|
|
1709
|
+
: '/api/tasks/delivery-targets?${params.join('&')}';
|
|
1710
|
+
return getList(baseUrl, path);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1663
1713
|
Future<Map<String, dynamic>> saveTask(
|
|
1664
1714
|
String baseUrl, {
|
|
1665
1715
|
int? id,
|
package/landing/index.html
CHANGED
|
@@ -887,6 +887,7 @@ p { margin: 0; text-wrap: pretty; }
|
|
|
887
887
|
<a href="#features">Features</a>
|
|
888
888
|
<a href="#channels">Channels</a>
|
|
889
889
|
<a href="#how">How it works</a>
|
|
890
|
+
<a href="/status">Status</a>
|
|
890
891
|
</nav>
|
|
891
892
|
<div class="nav-right">
|
|
892
893
|
<a class="signin" href="/app">Sign in</a>
|
|
@@ -899,6 +900,7 @@ p { margin: 0; text-wrap: pretty; }
|
|
|
899
900
|
<a href="#features">Features</a>
|
|
900
901
|
<a href="#channels">Channels</a>
|
|
901
902
|
<a href="#how">How it works</a>
|
|
903
|
+
<a href="/status">Status</a>
|
|
902
904
|
</div>
|
|
903
905
|
</header>
|
|
904
906
|
|
|
@@ -1230,6 +1232,7 @@ p { margin: 0; text-wrap: pretty; }
|
|
|
1230
1232
|
<a href="#features">Features</a>
|
|
1231
1233
|
<a href="#channels">Channels</a>
|
|
1232
1234
|
<a href="#how">How it works</a>
|
|
1235
|
+
<a href="/status">Status</a>
|
|
1233
1236
|
<a href="https://neolabs-systems.github.io/NeoAgent/">Documentation</a>
|
|
1234
1237
|
</nav>
|
|
1235
1238
|
<nav class="footer-legal">
|