neoagent 2.3.1-beta.57 → 2.3.1-beta.59

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.
@@ -443,6 +443,7 @@ class _DevicesPanelState extends State<DevicesPanel> {
443
443
  if (_isDesktop) ...<Widget>[
444
444
  const SizedBox(height: 14),
445
445
  DropdownButtonFormField<String>(
446
+ isExpanded: true,
446
447
  initialValue: selectedDesktopDevice?['deviceId']
447
448
  ?.toString(),
448
449
  decoration: const InputDecoration(
@@ -464,7 +465,12 @@ class _DevicesPanelState extends State<DevicesPanel> {
464
465
  : 'offline';
465
466
  return DropdownMenuItem<String>(
466
467
  value: deviceId,
467
- child: Text('$label · $os · $state'),
468
+ child: Text(
469
+ '$label · $os · $state',
470
+ maxLines: 1,
471
+ overflow: TextOverflow.ellipsis,
472
+ softWrap: false,
473
+ ),
468
474
  );
469
475
  }).toList(),
470
476
  onChanged: controller.isRunningDeviceAction
@@ -652,94 +658,96 @@ class _DeviceSurfaceHeader extends StatelessWidget {
652
658
 
653
659
  @override
654
660
  Widget build(BuildContext context) {
655
- final androidStarting = androidRuntime['starting'] == true;
656
- final androidVersion = _androidRuntimeVersionLabel(androidRuntime);
657
- final selectedDesktop = desktopDevices
658
- .where((device) => device['deviceId'] == selectedDesktopDeviceId)
659
- .cast<Map<String, dynamic>?>()
660
- .firstWhere((device) => device != null, orElse: () => null);
661
- final desktopOnlineCount = desktopDevices
662
- .where((device) => device['online'] == true)
663
- .length;
664
- final title = switch (surface) {
665
- _DeviceSurface.browser =>
666
- (browserPageInfo['title']?.toString().trim().isNotEmpty ?? false)
667
- ? browserPageInfo['title'].toString()
668
- : 'Live Browser',
669
- _DeviceSurface.android => 'Android Phone',
670
- _DeviceSurface.desktop =>
671
- selectedDesktop?['label']?.toString().trim().isNotEmpty == true
672
- ? selectedDesktop!['label'].toString()
673
- : 'Desktop Companion',
674
- };
675
- final subtitle = switch (surface) {
676
- _DeviceSurface.browser =>
677
- browserExtensionPreferred && !browserExtensionActive
678
- ? 'No extension device is active. Using the $browserFallbackLabel browser fallback.'
679
- : (browserPageInfo['url']?.toString() ?? 'Ready for navigation'),
680
- _DeviceSurface.android =>
681
- androidOnline
682
- ? androidVersion == null
683
- ? 'Tap and swipe directly on the preview.'
684
- : '$androidVersion · Tap and swipe directly on the preview.'
685
- : androidStarting
686
- ? (androidRuntime['startupPhase']?.toString().trim().isNotEmpty ??
661
+ return LayoutBuilder(
662
+ builder: (context, constraints) {
663
+ final compact = constraints.maxWidth < 720;
664
+ final androidStarting = androidRuntime['starting'] == true;
665
+ final androidVersion = _androidRuntimeVersionLabel(androidRuntime);
666
+ final selectedDesktop = desktopDevices
667
+ .where((device) => device['deviceId'] == selectedDesktopDeviceId)
668
+ .cast<Map<String, dynamic>?>()
669
+ .firstWhere((device) => device != null, orElse: () => null);
670
+ final desktopOnlineCount = desktopDevices
671
+ .where((device) => device['online'] == true)
672
+ .length;
673
+ final title = switch (surface) {
674
+ _DeviceSurface.browser =>
675
+ (browserPageInfo['title']?.toString().trim().isNotEmpty ?? false)
676
+ ? browserPageInfo['title'].toString()
677
+ : 'Live Browser',
678
+ _DeviceSurface.android => 'Android Phone',
679
+ _DeviceSurface.desktop =>
680
+ selectedDesktop?['label']?.toString().trim().isNotEmpty == true
681
+ ? selectedDesktop!['label'].toString()
682
+ : 'Desktop Companion',
683
+ };
684
+ final subtitle = switch (surface) {
685
+ _DeviceSurface.browser =>
686
+ browserExtensionPreferred && !browserExtensionActive
687
+ ? 'No extension device is active. Using the $browserFallbackLabel browser fallback.'
688
+ : (browserPageInfo['url']?.toString() ??
689
+ 'Ready for navigation'),
690
+ _DeviceSurface.android =>
691
+ androidOnline
692
+ ? androidVersion == null
693
+ ? 'Tap and swipe directly on the preview.'
694
+ : '$androidVersion · Tap and swipe directly on the preview.'
695
+ : androidStarting
696
+ ? (androidRuntime['startupPhase']
697
+ ?.toString()
698
+ .trim()
699
+ .isNotEmpty ??
700
+ false)
701
+ ? androidRuntime['startupPhase'].toString()
702
+ : 'Starting the phone. This can take a little while.'
703
+ : (androidRuntime['lastLogLine']
704
+ ?.toString()
705
+ .trim()
706
+ .isNotEmpty ??
687
707
  false)
688
- ? androidRuntime['startupPhase'].toString()
689
- : 'Starting the phone. This can take a little while.'
690
- : (androidRuntime['lastLogLine']?.toString().trim().isNotEmpty ??
691
- false)
692
- ? androidRuntime['lastLogLine'].toString()
693
- : androidVersion != null
694
- ? '$androidVersion selected. Phone is offline.'
695
- : 'Phone is offline. Open or start it from below.',
696
- _DeviceSurface.desktop =>
697
- selectedDesktop == null
698
- ? desktopOnlineCount > 1
699
- ? 'Multiple desktop companions are online. Pick the machine you want to control.'
700
- : desktopOnlineCount == 1
701
- ? 'One desktop companion is online. Open the surface to fetch the latest frame.'
702
- : 'No desktop companion is online. Enable Companion Mode on a signed-in desktop app.'
703
- : '${selectedDesktop['platform'] ?? 'desktop'} · ${selectedDesktop['hostname'] ?? 'unknown host'}',
704
- };
705
- final statusLabel = surface == _DeviceSurface.browser
706
- ? browserExtensionPreferred && !browserExtensionActive
707
- ? 'Fallback'
708
- : browserExtensionActive
709
- ? 'Extension'
710
- : (browserStatus['launched'] == true ? 'Live' : 'Sleeping')
711
- : surface == _DeviceSurface.desktop
712
- ? selectedDesktop == null
713
- ? (desktopOnlineCount > 0 ? 'Select Device' : 'Offline')
714
- : (selectedDesktop['paused'] == true
715
- ? 'Paused'
716
- : (selectedDesktop['online'] == true ? 'Live' : 'Offline'))
717
- : (androidOnline
718
- ? 'Live'
719
- : androidStarting
720
- ? 'Starting'
721
- : 'Offline');
722
- final statusColor = surface == _DeviceSurface.browser
723
- ? (browserStatus['launched'] == true ? _success : _warning)
724
- : surface == _DeviceSurface.desktop
725
- ? (selectedDesktop?['paused'] == true
726
- ? _warning
727
- : (selectedDesktop?['online'] == true ? _success : _warning))
728
- : (androidOnline ? _success : (androidStarting ? _accent : _warning));
729
-
730
- return Row(
731
- children: <Widget>[
732
- Container(
733
- width: 48,
734
- height: 48,
735
- decoration: BoxDecoration(
736
- color: _accentMuted,
737
- borderRadius: BorderRadius.circular(14),
738
- ),
739
- child: Icon(surface.icon, color: _textPrimary),
740
- ),
741
- const SizedBox(width: 14),
742
- Expanded(
708
+ ? androidRuntime['lastLogLine'].toString()
709
+ : androidVersion != null
710
+ ? '$androidVersion selected. Phone is offline.'
711
+ : 'Phone is offline. Open or start it from below.',
712
+ _DeviceSurface.desktop =>
713
+ selectedDesktop == null
714
+ ? desktopOnlineCount > 1
715
+ ? 'Multiple desktop companions are online. Pick the machine you want to control.'
716
+ : desktopOnlineCount == 1
717
+ ? 'One desktop companion is online. Open the surface to fetch the latest frame.'
718
+ : 'No desktop companion is online. Enable Companion Mode on a signed-in desktop app.'
719
+ : '${selectedDesktop['platform'] ?? 'desktop'} · ${selectedDesktop['hostname'] ?? 'unknown host'}',
720
+ };
721
+ final statusLabel = surface == _DeviceSurface.browser
722
+ ? browserExtensionPreferred && !browserExtensionActive
723
+ ? 'Fallback'
724
+ : browserExtensionActive
725
+ ? 'Extension'
726
+ : (browserStatus['launched'] == true ? 'Live' : 'Sleeping')
727
+ : surface == _DeviceSurface.desktop
728
+ ? selectedDesktop == null
729
+ ? (desktopOnlineCount > 0 ? 'Select Device' : 'Offline')
730
+ : (selectedDesktop['paused'] == true
731
+ ? 'Paused'
732
+ : (selectedDesktop['online'] == true
733
+ ? 'Live'
734
+ : 'Offline'))
735
+ : (androidOnline
736
+ ? 'Live'
737
+ : androidStarting
738
+ ? 'Starting'
739
+ : 'Offline');
740
+ final statusColor = surface == _DeviceSurface.browser
741
+ ? (browserStatus['launched'] == true ? _success : _warning)
742
+ : surface == _DeviceSurface.desktop
743
+ ? (selectedDesktop?['paused'] == true
744
+ ? _warning
745
+ : (selectedDesktop?['online'] == true ? _success : _warning))
746
+ : (androidOnline
747
+ ? _success
748
+ : (androidStarting ? _accent : _warning));
749
+
750
+ final textColumn = Expanded(
743
751
  child: Column(
744
752
  crossAxisAlignment: CrossAxisAlignment.start,
745
753
  children: <Widget>[
@@ -758,10 +766,59 @@ class _DeviceSurfaceHeader extends StatelessWidget {
758
766
  ),
759
767
  ],
760
768
  ),
761
- ),
762
- const SizedBox(width: 12),
763
- _DotStatus(label: statusLabel, color: statusColor),
764
- ],
769
+ );
770
+
771
+ final statusChip = Flexible(
772
+ child: Align(
773
+ alignment: compact ? Alignment.centerLeft : Alignment.centerRight,
774
+ child: _DotStatus(label: statusLabel, color: statusColor),
775
+ ),
776
+ );
777
+
778
+ if (compact) {
779
+ return Column(
780
+ crossAxisAlignment: CrossAxisAlignment.start,
781
+ children: <Widget>[
782
+ Row(
783
+ crossAxisAlignment: CrossAxisAlignment.start,
784
+ children: <Widget>[
785
+ Container(
786
+ width: 48,
787
+ height: 48,
788
+ decoration: BoxDecoration(
789
+ color: _accentMuted,
790
+ borderRadius: BorderRadius.circular(14),
791
+ ),
792
+ child: Icon(surface.icon, color: _textPrimary),
793
+ ),
794
+ const SizedBox(width: 14),
795
+ textColumn,
796
+ ],
797
+ ),
798
+ const SizedBox(height: 12),
799
+ statusChip,
800
+ ],
801
+ );
802
+ }
803
+
804
+ return Row(
805
+ children: <Widget>[
806
+ Container(
807
+ width: 48,
808
+ height: 48,
809
+ decoration: BoxDecoration(
810
+ color: _accentMuted,
811
+ borderRadius: BorderRadius.circular(14),
812
+ ),
813
+ child: Icon(surface.icon, color: _textPrimary),
814
+ ),
815
+ const SizedBox(width: 14),
816
+ textColumn,
817
+ const SizedBox(width: 12),
818
+ statusChip,
819
+ ],
820
+ );
821
+ },
765
822
  );
766
823
  }
767
824
  }
@@ -1047,31 +1104,69 @@ class _SurfaceSwitcher extends StatelessWidget {
1047
1104
 
1048
1105
  @override
1049
1106
  Widget build(BuildContext context) {
1050
- return Row(
1051
- mainAxisAlignment: MainAxisAlignment.center,
1052
- children: <Widget>[
1053
- IconButton.filledTonal(
1054
- onPressed: onPrevious,
1055
- icon: Icon(Icons.arrow_back_ios_new_rounded),
1056
- ),
1057
- const SizedBox(width: 14),
1058
- Column(
1107
+ return LayoutBuilder(
1108
+ builder: (context, constraints) {
1109
+ final compact = constraints.maxWidth < 520;
1110
+ final labelColumn = Column(
1059
1111
  mainAxisSize: MainAxisSize.min,
1060
1112
  children: <Widget>[
1061
1113
  Text(
1062
1114
  surface.label,
1115
+ textAlign: TextAlign.center,
1063
1116
  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w800),
1064
1117
  ),
1065
1118
  const SizedBox(height: 4),
1066
- Text(surface.helper, style: TextStyle(color: _textSecondary)),
1119
+ Text(
1120
+ surface.helper,
1121
+ textAlign: TextAlign.center,
1122
+ maxLines: compact ? 3 : 2,
1123
+ overflow: TextOverflow.ellipsis,
1124
+ style: TextStyle(color: _textSecondary),
1125
+ ),
1067
1126
  ],
1068
- ),
1069
- const SizedBox(width: 14),
1070
- IconButton.filledTonal(
1071
- onPressed: onNext,
1072
- icon: Icon(Icons.arrow_forward_ios_rounded),
1073
- ),
1074
- ],
1127
+ );
1128
+
1129
+ if (compact) {
1130
+ return Column(
1131
+ mainAxisSize: MainAxisSize.min,
1132
+ children: <Widget>[
1133
+ Row(
1134
+ mainAxisAlignment: MainAxisAlignment.center,
1135
+ children: <Widget>[
1136
+ IconButton.filledTonal(
1137
+ onPressed: onPrevious,
1138
+ icon: Icon(Icons.arrow_back_ios_new_rounded),
1139
+ ),
1140
+ const SizedBox(width: 14),
1141
+ Flexible(child: labelColumn),
1142
+ const SizedBox(width: 14),
1143
+ IconButton.filledTonal(
1144
+ onPressed: onNext,
1145
+ icon: Icon(Icons.arrow_forward_ios_rounded),
1146
+ ),
1147
+ ],
1148
+ ),
1149
+ ],
1150
+ );
1151
+ }
1152
+
1153
+ return Row(
1154
+ mainAxisAlignment: MainAxisAlignment.center,
1155
+ children: <Widget>[
1156
+ IconButton.filledTonal(
1157
+ onPressed: onPrevious,
1158
+ icon: Icon(Icons.arrow_back_ios_new_rounded),
1159
+ ),
1160
+ const SizedBox(width: 14),
1161
+ labelColumn,
1162
+ const SizedBox(width: 14),
1163
+ IconButton.filledTonal(
1164
+ onPressed: onNext,
1165
+ icon: Icon(Icons.arrow_forward_ios_rounded),
1166
+ ),
1167
+ ],
1168
+ );
1169
+ },
1075
1170
  );
1076
1171
  }
1077
1172
  }
@@ -265,8 +265,6 @@ class _SettingsPanelState extends State<SettingsPanel> {
265
265
  ],
266
266
  _buildSettingsOverview(controller, availableModels.length),
267
267
  const SizedBox(height: 16),
268
- _buildManagementSection(controller),
269
- const SizedBox(height: 16),
270
268
  _buildWorkspaceSection(controller),
271
269
  const SizedBox(height: 16),
272
270
  _buildModelsSection(
@@ -344,6 +342,22 @@ class _SettingsPanelState extends State<SettingsPanel> {
344
342
  ),
345
343
  ],
346
344
  ),
345
+ const SizedBox(height: 14),
346
+ Align(
347
+ alignment: Alignment.centerLeft,
348
+ child: OutlinedButton.icon(
349
+ onPressed: controller.reopenOnboarding,
350
+ style: OutlinedButton.styleFrom(
351
+ visualDensity: VisualDensity.compact,
352
+ padding: const EdgeInsets.symmetric(
353
+ horizontal: 12,
354
+ vertical: 10,
355
+ ),
356
+ ),
357
+ icon: const Icon(Icons.replay_rounded, size: 18),
358
+ label: const Text('Redo onboarding'),
359
+ ),
360
+ ),
347
361
  ],
348
362
  ),
349
363
  ),
@@ -453,81 +467,6 @@ class _SettingsPanelState extends State<SettingsPanel> {
453
467
  );
454
468
  }
455
469
 
456
- Widget _buildManagementSection(NeoAgentController controller) {
457
- return Card(
458
- child: Padding(
459
- padding: const EdgeInsets.all(20),
460
- child: Column(
461
- crossAxisAlignment: CrossAxisAlignment.start,
462
- children: <Widget>[
463
- const _SectionTitle('Subcategories'),
464
- const SizedBox(height: 10),
465
- Text(
466
- 'Open settings areas that have their own dedicated management screens.',
467
- style: TextStyle(color: _textSecondary, height: 1.45),
468
- ),
469
- const SizedBox(height: 16),
470
- InkWell(
471
- onTap: () => controller.setSelectedSection(AppSection.agents),
472
- borderRadius: BorderRadius.circular(20),
473
- child: Ink(
474
- decoration: BoxDecoration(
475
- color: _bgSecondary.withValues(alpha: 0.7),
476
- borderRadius: BorderRadius.circular(20),
477
- border: Border.all(color: _border),
478
- ),
479
- child: Padding(
480
- padding: const EdgeInsets.all(16),
481
- child: Row(
482
- children: <Widget>[
483
- Container(
484
- width: 44,
485
- height: 44,
486
- decoration: BoxDecoration(
487
- color: _accentMuted,
488
- borderRadius: BorderRadius.circular(14),
489
- ),
490
- child: Icon(
491
- Icons.smart_toy_outlined,
492
- color: _accentHover,
493
- ),
494
- ),
495
- const SizedBox(width: 14),
496
- Expanded(
497
- child: Column(
498
- crossAxisAlignment: CrossAxisAlignment.start,
499
- children: <Widget>[
500
- Text(
501
- 'Agents',
502
- style: TextStyle(
503
- fontWeight: FontWeight.w700,
504
- color: _textPrimary,
505
- ),
506
- ),
507
- const SizedBox(height: 4),
508
- Text(
509
- 'Manage specialist agents, routing roles, memory separation, and account assignment.',
510
- style: TextStyle(
511
- color: _textSecondary,
512
- height: 1.4,
513
- ),
514
- ),
515
- ],
516
- ),
517
- ),
518
- const SizedBox(width: 12),
519
- Icon(Icons.chevron_right_rounded, color: _textSecondary),
520
- ],
521
- ),
522
- ),
523
- ),
524
- ),
525
- ],
526
- ),
527
- ),
528
- );
529
- }
530
-
531
470
  Widget _buildModelsSection({
532
471
  required NeoAgentController controller,
533
472
  required List<DropdownMenuItem<String>> modelChoices,
@@ -242,6 +242,7 @@ List<AppSection> _mainSections(NeoAgentController controller) {
242
242
  AppSection.memory,
243
243
  if (controller.showHealthSection) AppSection.health,
244
244
  AppSection.settings,
245
+ AppSection.agents,
245
246
  AppSection.messaging,
246
247
  ];
247
248
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.57",
3
+ "version": "2.3.1-beta.59",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -1 +1 @@
1
- 4c06defd54f49c9d84625740934d71be
1
+ 154775e7dc374e782403c776404afd0b
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d9
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "1045065206" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "3080522326" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });