neoagent 3.1.1-beta.9 → 3.2.0
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/.env.example +2 -2
- package/README.md +5 -1
- package/docs/architecture.md +1 -1
- package/docs/benchmarking.md +68 -87
- package/docs/capabilities.md +1 -1
- package/docs/clients-and-devices.md +3 -4
- package/docs/configuration.md +1 -1
- package/docs/devices.md +1 -2
- package/docs/health.md +24 -0
- package/docs/index.md +2 -2
- package/docs/integrations-architecture.md +6 -0
- package/docs/integrations.md +15 -1
- package/docs/models.md +1 -1
- package/docs/persistence.md +1 -1
- package/docs/why-neoagent.md +1 -1
- package/flutter_app/README.md +0 -5
- package/flutter_app/android/app/src/main/AndroidManifest.xml +0 -14
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +0 -98
- package/flutter_app/lib/main.dart +0 -6
- package/flutter_app/lib/main_app_shell.dart +0 -2
- package/flutter_app/lib/main_controller.dart +2 -867
- package/flutter_app/lib/main_devices.dart +0 -73
- package/flutter_app/lib/main_integrations.dart +339 -187
- package/flutter_app/lib/main_launcher.dart +2 -212
- package/flutter_app/lib/main_models.dart +0 -259
- package/flutter_app/lib/main_navigation.dart +2 -14
- package/flutter_app/lib/main_operations.dart +322 -364
- package/flutter_app/lib/main_runtime.dart +9 -208
- package/flutter_app/lib/main_settings.dart +17 -199
- package/flutter_app/lib/main_shared.dart +20 -591
- package/flutter_app/lib/main_timeline.dart +26 -122
- package/flutter_app/lib/main_voice_assistant.dart +0 -5
- package/flutter_app/lib/src/backend_client.dart +0 -102
- package/flutter_app/lib/src/desktop_companion_io.dart +1 -65
- package/flutter_app/lib/src/desktop_companion_stub.dart +0 -12
- package/flutter_app/linux/flutter/generated_plugin_registrant.cc +0 -4
- package/flutter_app/linux/flutter/generated_plugins.cmake +0 -1
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +0 -2
- package/flutter_app/macos/Podfile.lock +56 -5
- package/flutter_app/pubspec.lock +0 -7
- package/flutter_app/pubspec.yaml +0 -2
- package/flutter_app/web/index.html +174 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.cc +0 -3
- package/flutter_app/windows/flutter/generated_plugins.cmake +0 -1
- package/landing/index.html +0 -2
- package/lib/manager.js +1 -1
- package/lib/schema_migrations.js +75 -0
- package/package.json +1 -4
- package/server/db/database.js +1 -133
- package/server/http/middleware.js +2 -16
- package/server/http/routes.js +0 -2
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/NOTICES +0 -25
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/index.html +174 -0
- package/server/public/main.dart.js +88081 -90036
- package/server/routes/auth.js +11 -2
- package/server/routes/settings.js +0 -5
- package/server/routes/voice_assistant.js +1 -128
- package/server/services/account/erasure.js +0 -3
- package/server/services/ai/deliverables/artifact_helpers.js +1 -1
- package/server/services/ai/loop/conversation_loop.js +6 -2
- package/server/services/ai/loop/tool_dispatch.js +0 -3
- package/server/services/ai/messagingFallback.js +1 -1
- package/server/services/ai/settings.js +0 -6
- package/server/services/ai/toolEvidence.js +1 -4
- package/server/services/ai/tools.js +0 -234
- package/server/services/desktop/auth.js +0 -3
- package/server/services/desktop/registry.js +2 -50
- package/server/services/integrations/neoarchive/provider.js +616 -0
- package/server/services/integrations/neorecall/client.js +140 -0
- package/server/services/integrations/neorecall/constants.js +87 -0
- package/server/services/integrations/neorecall/provider.js +163 -0
- package/server/services/integrations/registry.js +4 -2
- package/server/services/manager.js +0 -22
- package/server/services/memory/manager.js +44 -19
- package/server/services/memory/retrieval_reasoning.js +36 -14
- package/server/services/messaging/whatsapp.js +18 -0
- package/server/services/security/tool_categories.js +0 -5
- package/server/services/social_video/service.js +2 -20
- package/server/services/tasks/adapters/index.js +1 -1
- package/server/services/tasks/integration_runtime.js +3 -67
- package/server/services/tasks/runtime.js +1 -6
- package/server/services/tasks/task_repository.js +6 -13
- package/server/services/timeline/service.js +0 -224
- package/server/services/voice/message.js +0 -24
- package/server/services/voice/providers.js +42 -1
- package/server/services/voice/runtimeManager.js +9 -5
- package/server/services/voice/turnRunner.js +0 -22
- package/server/services/wearable/service.js +0 -5
- package/docs/recordings-and-health.md +0 -41
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +0 -21
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +0 -586
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +0 -78
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +0 -104
- package/flutter_app/lib/main_recordings.dart +0 -920
- package/flutter_app/lib/src/desktop_ocr_bridge.dart +0 -2
- package/flutter_app/lib/src/desktop_ocr_bridge_io.dart +0 -125
- package/flutter_app/lib/src/desktop_ocr_bridge_stub.dart +0 -30
- package/flutter_app/lib/src/desktop_passive_history.dart +0 -332
- package/flutter_app/lib/src/recording_bridge.dart +0 -232
- package/flutter_app/lib/src/recording_bridge_io.dart +0 -1027
- package/flutter_app/lib/src/recording_bridge_stub.dart +0 -120
- package/flutter_app/lib/src/recording_bridge_web.dart +0 -702
- package/flutter_app/lib/src/recording_chunk_queue.dart +0 -149
- package/flutter_app/lib/src/recording_chunk_queue_io.dart +0 -182
- package/flutter_app/lib/src/recording_payloads.dart +0 -95
- package/flutter_app/third_party/desktop_audio_capture/LICENSE +0 -21
- package/flutter_app/third_party/desktop_audio_capture/README.md +0 -262
- package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +0 -65
- package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +0 -153
- package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +0 -110
- package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +0 -461
- package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +0 -91
- package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +0 -106
- package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +0 -219
- package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +0 -336
- package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +0 -101
- package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +0 -692
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +0 -35
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +0 -36
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +0 -32
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +0 -32
- package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +0 -878
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +0 -27
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +0 -1172
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +0 -655
- package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +0 -12
- package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +0 -30
- package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +0 -87
- package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +0 -105
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +0 -80
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +0 -31
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +0 -12
- package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +0 -23
- package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +0 -25
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +0 -1117
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +0 -115
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +0 -777
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +0 -87
- package/server/routes/recordings.js +0 -335
- package/server/routes/screenHistory.js +0 -190
- package/server/services/ai/recordingInsights.js +0 -105
- package/server/services/integrations/neomail/provider.js +0 -993
- package/server/services/recordings/deepgram.js +0 -58
- package/server/services/recordings/manager.js +0 -1273
- package/server/services/tasks/adapters/neomail_email_received.js +0 -46
- package/server/services/voice/screenshotContext.js +0 -73
- package/server/services/voice/turnCoordinator.js +0 -51
|
@@ -505,10 +505,8 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
505
505
|
|
|
506
506
|
// Installed tab search & filter state
|
|
507
507
|
String _installedQuery = '';
|
|
508
|
-
String _installedStatusFilter =
|
|
509
|
-
|
|
510
|
-
String _installedSourceFilter =
|
|
511
|
-
'all'; // 'all' | 'built-in' | 'learned' | 'user' | 'store'
|
|
508
|
+
String _installedStatusFilter = 'all'; // 'all' | 'active' | 'draft' | 'disabled'
|
|
509
|
+
String _installedSourceFilter = 'all'; // 'all' | 'built-in' | 'learned' | 'user' | 'store'
|
|
512
510
|
late final TextEditingController _installedSearchController;
|
|
513
511
|
|
|
514
512
|
@override
|
|
@@ -647,36 +645,18 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
647
645
|
final q = _installedQuery;
|
|
648
646
|
if (q.isNotEmpty &&
|
|
649
647
|
!skill.name.toLowerCase().contains(q) &&
|
|
650
|
-
!skill.description.toLowerCase().contains(q))
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
648
|
+
!skill.description.toLowerCase().contains(q)) return false;
|
|
653
649
|
if (_installedStatusFilter != 'all') {
|
|
654
|
-
if (_installedStatusFilter == 'active' &&
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}
|
|
658
|
-
if (_installedStatusFilter == 'draft' && !skill.draft) {
|
|
659
|
-
return false;
|
|
660
|
-
}
|
|
661
|
-
if (_installedStatusFilter == 'disabled' && skill.enabled) {
|
|
662
|
-
return false;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
if (_installedSourceFilter != 'all' &&
|
|
666
|
-
skill.source != _installedSourceFilter) {
|
|
667
|
-
return false;
|
|
650
|
+
if (_installedStatusFilter == 'active' && (!skill.enabled || skill.draft)) return false;
|
|
651
|
+
if (_installedStatusFilter == 'draft' && !skill.draft) return false;
|
|
652
|
+
if (_installedStatusFilter == 'disabled' && skill.enabled) return false;
|
|
668
653
|
}
|
|
654
|
+
if (_installedSourceFilter != 'all' && skill.source != _installedSourceFilter) return false;
|
|
669
655
|
return true;
|
|
670
656
|
}).toList();
|
|
671
657
|
|
|
672
658
|
final statusFilters = <String>['all', 'active', 'draft', 'disabled'];
|
|
673
|
-
final sourceFilters = <String>[
|
|
674
|
-
'all',
|
|
675
|
-
'built-in',
|
|
676
|
-
'learned',
|
|
677
|
-
'user',
|
|
678
|
-
'store',
|
|
679
|
-
];
|
|
659
|
+
final sourceFilters = <String>['all', 'built-in', 'learned', 'user', 'store'];
|
|
680
660
|
|
|
681
661
|
return Card(
|
|
682
662
|
child: Column(
|
|
@@ -777,174 +757,157 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
777
757
|
separatorBuilder: (_, __) => const SizedBox(height: 10),
|
|
778
758
|
itemBuilder: (context, index) {
|
|
779
759
|
final skill = filteredSkills[index];
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
),
|
|
881
|
-
style: TextStyle(
|
|
882
|
-
color: _textSecondary,
|
|
883
|
-
),
|
|
884
|
-
),
|
|
885
|
-
const SizedBox(height: 10),
|
|
886
|
-
Wrap(
|
|
887
|
-
spacing: 8,
|
|
888
|
-
runSpacing: 8,
|
|
889
|
-
children: <Widget>[
|
|
890
|
-
_MetaPill(
|
|
891
|
-
label: skill.category,
|
|
892
|
-
icon: Icons.folder_outlined,
|
|
893
|
-
),
|
|
894
|
-
_MetaPill(
|
|
895
|
-
label: skill.source,
|
|
896
|
-
icon: Icons.source_outlined,
|
|
897
|
-
),
|
|
898
|
-
if (skill.draft)
|
|
899
|
-
const _MetaPill(
|
|
900
|
-
label: 'Draft',
|
|
901
|
-
icon: Icons.edit_note_outlined,
|
|
902
|
-
),
|
|
903
|
-
],
|
|
904
|
-
),
|
|
905
|
-
],
|
|
760
|
+
return LayoutBuilder(
|
|
761
|
+
builder: (context, constraints) {
|
|
762
|
+
final compact = constraints.maxWidth < 760;
|
|
763
|
+
return Container(
|
|
764
|
+
padding: const EdgeInsets.all(14),
|
|
765
|
+
decoration: BoxDecoration(
|
|
766
|
+
color: _bgSecondary,
|
|
767
|
+
borderRadius: BorderRadius.circular(14),
|
|
768
|
+
border: Border.all(color: _border),
|
|
769
|
+
),
|
|
770
|
+
child: compact
|
|
771
|
+
? Column(
|
|
772
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
773
|
+
children: <Widget>[
|
|
774
|
+
Row(
|
|
775
|
+
children: <Widget>[
|
|
776
|
+
Expanded(
|
|
777
|
+
child: Text(
|
|
778
|
+
skill.name,
|
|
779
|
+
style: TextStyle(fontWeight: FontWeight.w700),
|
|
780
|
+
),
|
|
781
|
+
),
|
|
782
|
+
Switch(
|
|
783
|
+
value: skill.enabled,
|
|
784
|
+
onChanged: (value) => controller
|
|
785
|
+
.setSkillEnabled(skill.name, value),
|
|
786
|
+
),
|
|
787
|
+
],
|
|
788
|
+
),
|
|
789
|
+
Text(
|
|
790
|
+
skill.description.ifEmpty('No description'),
|
|
791
|
+
style: TextStyle(color: _textSecondary),
|
|
792
|
+
),
|
|
793
|
+
const SizedBox(height: 10),
|
|
794
|
+
Wrap(
|
|
795
|
+
spacing: 8,
|
|
796
|
+
runSpacing: 8,
|
|
797
|
+
children: <Widget>[
|
|
798
|
+
_MetaPill(
|
|
799
|
+
label: skill.category,
|
|
800
|
+
icon: Icons.folder_outlined,
|
|
801
|
+
),
|
|
802
|
+
_MetaPill(
|
|
803
|
+
label: skill.source,
|
|
804
|
+
icon: Icons.source_outlined,
|
|
805
|
+
),
|
|
806
|
+
if (skill.draft)
|
|
807
|
+
const _MetaPill(
|
|
808
|
+
label: 'Draft',
|
|
809
|
+
icon: Icons.edit_note_outlined,
|
|
810
|
+
),
|
|
811
|
+
],
|
|
812
|
+
),
|
|
813
|
+
const SizedBox(height: 10),
|
|
814
|
+
Row(
|
|
815
|
+
children: <Widget>[
|
|
816
|
+
const Spacer(),
|
|
817
|
+
OutlinedButton(
|
|
818
|
+
onPressed: () =>
|
|
819
|
+
_openSkillEditor(context, skill.name),
|
|
820
|
+
child: Text('Open'),
|
|
821
|
+
),
|
|
822
|
+
const SizedBox(width: 8),
|
|
823
|
+
TextButton.icon(
|
|
824
|
+
onPressed: () =>
|
|
825
|
+
_confirmDeleteSkill(context, skill.name),
|
|
826
|
+
icon: Icon(Icons.delete_outline),
|
|
827
|
+
style: TextButton.styleFrom(
|
|
828
|
+
foregroundColor: _danger,
|
|
829
|
+
),
|
|
830
|
+
label: Text('Delete'),
|
|
831
|
+
),
|
|
832
|
+
],
|
|
833
|
+
),
|
|
834
|
+
],
|
|
835
|
+
)
|
|
836
|
+
: Row(
|
|
837
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
838
|
+
children: <Widget>[
|
|
839
|
+
Expanded(
|
|
840
|
+
child: Column(
|
|
841
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
842
|
+
children: <Widget>[
|
|
843
|
+
Text(
|
|
844
|
+
skill.name,
|
|
845
|
+
style: TextStyle(fontWeight: FontWeight.w700),
|
|
846
|
+
),
|
|
847
|
+
const SizedBox(height: 6),
|
|
848
|
+
Text(
|
|
849
|
+
skill.description.ifEmpty('No description'),
|
|
850
|
+
style: TextStyle(color: _textSecondary),
|
|
851
|
+
),
|
|
852
|
+
const SizedBox(height: 10),
|
|
853
|
+
Wrap(
|
|
854
|
+
spacing: 8,
|
|
855
|
+
runSpacing: 8,
|
|
856
|
+
children: <Widget>[
|
|
857
|
+
_MetaPill(
|
|
858
|
+
label: skill.category,
|
|
859
|
+
icon: Icons.folder_outlined,
|
|
906
860
|
),
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
),
|
|
916
|
-
OutlinedButton(
|
|
917
|
-
onPressed: () => _openSkillEditor(
|
|
918
|
-
context,
|
|
919
|
-
skill.name,
|
|
920
|
-
),
|
|
921
|
-
child: Text('Open'),
|
|
922
|
-
),
|
|
923
|
-
const SizedBox(height: 6),
|
|
924
|
-
TextButton.icon(
|
|
925
|
-
onPressed: () => _confirmDeleteSkill(
|
|
926
|
-
context,
|
|
927
|
-
skill.name,
|
|
928
|
-
),
|
|
929
|
-
icon: Icon(Icons.delete_outline),
|
|
930
|
-
style: TextButton.styleFrom(
|
|
931
|
-
foregroundColor: _danger,
|
|
932
|
-
),
|
|
933
|
-
label: Text('Delete'),
|
|
861
|
+
_MetaPill(
|
|
862
|
+
label: skill.source,
|
|
863
|
+
icon: Icons.source_outlined,
|
|
864
|
+
),
|
|
865
|
+
if (skill.draft)
|
|
866
|
+
const _MetaPill(
|
|
867
|
+
label: 'Draft',
|
|
868
|
+
icon: Icons.edit_note_outlined,
|
|
934
869
|
),
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
870
|
+
],
|
|
871
|
+
),
|
|
872
|
+
],
|
|
873
|
+
),
|
|
874
|
+
),
|
|
875
|
+
const SizedBox(width: 10),
|
|
876
|
+
Column(
|
|
877
|
+
children: <Widget>[
|
|
878
|
+
Switch(
|
|
879
|
+
value: skill.enabled,
|
|
880
|
+
onChanged: (value) => controller
|
|
881
|
+
.setSkillEnabled(skill.name, value),
|
|
938
882
|
),
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
883
|
+
OutlinedButton(
|
|
884
|
+
onPressed: () =>
|
|
885
|
+
_openSkillEditor(context, skill.name),
|
|
886
|
+
child: Text('Open'),
|
|
887
|
+
),
|
|
888
|
+
const SizedBox(height: 6),
|
|
889
|
+
TextButton.icon(
|
|
890
|
+
onPressed: () =>
|
|
891
|
+
_confirmDeleteSkill(context, skill.name),
|
|
892
|
+
icon: Icon(Icons.delete_outline),
|
|
893
|
+
style: TextButton.styleFrom(
|
|
894
|
+
foregroundColor: _danger,
|
|
895
|
+
),
|
|
896
|
+
label: Text('Delete'),
|
|
897
|
+
),
|
|
898
|
+
],
|
|
899
|
+
),
|
|
900
|
+
],
|
|
901
|
+
),
|
|
902
|
+
);
|
|
903
|
+
},
|
|
904
|
+
);
|
|
942
905
|
},
|
|
943
906
|
),
|
|
944
907
|
),
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
908
|
+
],
|
|
909
|
+
),
|
|
910
|
+
);
|
|
948
911
|
}
|
|
949
912
|
|
|
950
913
|
Widget _buildStoreTab(
|
|
@@ -1502,9 +1465,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1502
1465
|
);
|
|
1503
1466
|
if (!mounted) return;
|
|
1504
1467
|
_llmImportController.clear();
|
|
1505
|
-
final warningText =
|
|
1506
|
-
? ''
|
|
1507
|
-
: ' ${result.warnings.join(' ')}';
|
|
1468
|
+
final warningText =
|
|
1469
|
+
result.warnings.isEmpty ? '' : ' ${result.warnings.join(' ')}';
|
|
1508
1470
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
1509
1471
|
SnackBar(
|
|
1510
1472
|
content: Text(
|
|
@@ -1532,7 +1494,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1532
1494
|
: controller.memories;
|
|
1533
1495
|
if (_entityFilter == null) return base;
|
|
1534
1496
|
return base
|
|
1535
|
-
.where(
|
|
1497
|
+
.where(
|
|
1498
|
+
(m) => m.entities.any((e) => e.name == _entityFilter),
|
|
1499
|
+
)
|
|
1536
1500
|
.toList();
|
|
1537
1501
|
}
|
|
1538
1502
|
|
|
@@ -1624,10 +1588,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1624
1588
|
});
|
|
1625
1589
|
}
|
|
1626
1590
|
|
|
1627
|
-
void _openRetrievalInspector(
|
|
1628
|
-
BuildContext context,
|
|
1629
|
-
NeoAgentController controller,
|
|
1630
|
-
) {
|
|
1591
|
+
void _openRetrievalInspector(BuildContext context, NeoAgentController controller) {
|
|
1631
1592
|
Navigator.of(context).push(
|
|
1632
1593
|
MaterialPageRoute(
|
|
1633
1594
|
builder: (context) => RetrievalInspectorView(controller: controller),
|
|
@@ -1642,8 +1603,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1642
1603
|
final memoriesToShow = _visibleMemories;
|
|
1643
1604
|
final selectedIds = _selectedVisibleMemoryIds.toSet();
|
|
1644
1605
|
final selectedCount = selectedIds.length;
|
|
1645
|
-
final allVisibleSelected =
|
|
1646
|
-
memoriesToShow.isNotEmpty &&
|
|
1606
|
+
final allVisibleSelected = memoriesToShow.isNotEmpty &&
|
|
1647
1607
|
memoriesToShow.every((m) => selectedIds.contains(m.id));
|
|
1648
1608
|
final showingSearchResults = controller.memoryRecallResults.isNotEmpty;
|
|
1649
1609
|
final compact = MediaQuery.sizeOf(context).width < 760;
|
|
@@ -1684,7 +1644,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1684
1644
|
padding: const EdgeInsets.all(18),
|
|
1685
1645
|
child: Row(
|
|
1686
1646
|
children: <Widget>[
|
|
1687
|
-
_MemoryConfidenceGauge(
|
|
1647
|
+
_MemoryConfidenceGauge(
|
|
1648
|
+
confidence: stats.averageConfidence,
|
|
1649
|
+
),
|
|
1688
1650
|
const SizedBox(width: 18),
|
|
1689
1651
|
Expanded(
|
|
1690
1652
|
child: Wrap(
|
|
@@ -1743,7 +1705,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1743
1705
|
children: <Widget>[
|
|
1744
1706
|
Row(
|
|
1745
1707
|
children: <Widget>[
|
|
1746
|
-
Expanded(
|
|
1708
|
+
Expanded(
|
|
1709
|
+
child: const _SectionTitle('Knowledge Graph'),
|
|
1710
|
+
),
|
|
1747
1711
|
if (_entityFilter != null)
|
|
1748
1712
|
TextButton.icon(
|
|
1749
1713
|
onPressed: () =>
|
|
@@ -1756,7 +1720,10 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1756
1720
|
const SizedBox(height: 4),
|
|
1757
1721
|
Text(
|
|
1758
1722
|
'Tap an entity to filter memories by it.',
|
|
1759
|
-
style: TextStyle(
|
|
1723
|
+
style: TextStyle(
|
|
1724
|
+
color: _textSecondary,
|
|
1725
|
+
fontSize: 12,
|
|
1726
|
+
),
|
|
1760
1727
|
),
|
|
1761
1728
|
const SizedBox(height: 14),
|
|
1762
1729
|
SizedBox(
|
|
@@ -1890,16 +1857,15 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1890
1857
|
Wrap(
|
|
1891
1858
|
spacing: 8,
|
|
1892
1859
|
runSpacing: 8,
|
|
1893
|
-
crossAxisAlignment:
|
|
1860
|
+
crossAxisAlignment:
|
|
1861
|
+
WrapCrossAlignment.center,
|
|
1894
1862
|
children: <Widget>[
|
|
1895
1863
|
OutlinedButton.icon(
|
|
1896
|
-
onPressed:
|
|
1897
|
-
allVisibleSelected ||
|
|
1864
|
+
onPressed: allVisibleSelected ||
|
|
1898
1865
|
_bulkActionInFlight
|
|
1899
1866
|
? null
|
|
1900
1867
|
: () => _selectAllVisibleMemories(
|
|
1901
|
-
memoriesToShow,
|
|
1902
|
-
),
|
|
1868
|
+
memoriesToShow),
|
|
1903
1869
|
icon: Icon(
|
|
1904
1870
|
Icons.done_all_outlined,
|
|
1905
1871
|
size: 16,
|
|
@@ -1937,7 +1903,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1937
1903
|
Icons.archive_outlined,
|
|
1938
1904
|
size: 16,
|
|
1939
1905
|
),
|
|
1940
|
-
label: Text(
|
|
1906
|
+
label: Text(
|
|
1907
|
+
'Archive ($selectedCount)',
|
|
1908
|
+
),
|
|
1941
1909
|
),
|
|
1942
1910
|
OutlinedButton.icon(
|
|
1943
1911
|
onPressed: _bulkActionInFlight
|
|
@@ -1955,7 +1923,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1955
1923
|
Icons.delete_sweep_outlined,
|
|
1956
1924
|
size: 16,
|
|
1957
1925
|
),
|
|
1958
|
-
label: Text(
|
|
1926
|
+
label: Text(
|
|
1927
|
+
'Delete ($selectedCount)',
|
|
1928
|
+
),
|
|
1959
1929
|
),
|
|
1960
1930
|
],
|
|
1961
1931
|
],
|
|
@@ -1971,9 +1941,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1971
1941
|
)
|
|
1972
1942
|
else
|
|
1973
1943
|
...memoriesToShow.map((memory) {
|
|
1974
|
-
final isSelected =
|
|
1975
|
-
|
|
1976
|
-
);
|
|
1944
|
+
final isSelected =
|
|
1945
|
+
selectedIds.contains(memory.id);
|
|
1977
1946
|
return _MemoryRow(
|
|
1978
1947
|
memory: memory,
|
|
1979
1948
|
isSelected: isSelected,
|
|
@@ -1981,7 +1950,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1981
1950
|
memory.id,
|
|
1982
1951
|
!isSelected,
|
|
1983
1952
|
),
|
|
1984
|
-
onCheck: (value) =>
|
|
1953
|
+
onCheck: (value) =>
|
|
1954
|
+
_toggleMemorySelection(
|
|
1985
1955
|
memory.id,
|
|
1986
1956
|
value ?? false,
|
|
1987
1957
|
),
|
|
@@ -1994,9 +1964,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1994
1964
|
'This memory will be removed permanently.',
|
|
1995
1965
|
onConfirm: () =>
|
|
1996
1966
|
_deleteSingleMemory(
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
1967
|
+
controller,
|
|
1968
|
+
memory.id,
|
|
1969
|
+
),
|
|
2000
1970
|
),
|
|
2001
1971
|
);
|
|
2002
1972
|
}),
|
|
@@ -2015,7 +1985,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2015
1985
|
Expanded(
|
|
2016
1986
|
child: Text(
|
|
2017
1987
|
'Key-value pairs that persist across conversations.',
|
|
2018
|
-
style: TextStyle(
|
|
1988
|
+
style: TextStyle(
|
|
1989
|
+
color: _textSecondary,
|
|
1990
|
+
),
|
|
2019
1991
|
),
|
|
2020
1992
|
),
|
|
2021
1993
|
TextButton.icon(
|
|
@@ -2029,75 +2001,75 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2029
2001
|
],
|
|
2030
2002
|
),
|
|
2031
2003
|
const SizedBox(height: 10),
|
|
2032
|
-
if (controller
|
|
2004
|
+
if (controller
|
|
2005
|
+
.memoryOverview.coreEntries.isEmpty)
|
|
2033
2006
|
Text(
|
|
2034
2007
|
'No core memory entries yet.',
|
|
2035
2008
|
style: TextStyle(color: _textSecondary),
|
|
2036
2009
|
)
|
|
2037
2010
|
else
|
|
2038
|
-
...controller.memoryOverview.coreEntries
|
|
2011
|
+
...controller.memoryOverview.coreEntries
|
|
2012
|
+
.entries
|
|
2039
2013
|
.map((entry) {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2014
|
+
return Container(
|
|
2015
|
+
width: double.infinity,
|
|
2016
|
+
margin: const EdgeInsets.only(bottom: 10),
|
|
2017
|
+
padding: const EdgeInsets.all(12),
|
|
2018
|
+
decoration: BoxDecoration(
|
|
2019
|
+
color: _bgSecondary,
|
|
2020
|
+
borderRadius:
|
|
2021
|
+
BorderRadius.circular(12),
|
|
2022
|
+
border: Border.all(color: _border),
|
|
2023
|
+
),
|
|
2024
|
+
child: Row(
|
|
2025
|
+
crossAxisAlignment:
|
|
2026
|
+
CrossAxisAlignment.start,
|
|
2027
|
+
children: <Widget>[
|
|
2028
|
+
Expanded(
|
|
2029
|
+
child: Column(
|
|
2030
|
+
crossAxisAlignment:
|
|
2031
|
+
CrossAxisAlignment.start,
|
|
2032
|
+
children: <Widget>[
|
|
2033
|
+
Text(
|
|
2034
|
+
entry.key,
|
|
2035
|
+
style: TextStyle(
|
|
2036
|
+
fontWeight: FontWeight.w700,
|
|
2037
|
+
),
|
|
2038
|
+
),
|
|
2039
|
+
const SizedBox(height: 6),
|
|
2040
|
+
Text(
|
|
2041
|
+
entry.value.toString(),
|
|
2042
|
+
),
|
|
2043
|
+
],
|
|
2044
|
+
),
|
|
2044
2045
|
),
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2046
|
+
IconButton(
|
|
2047
|
+
onPressed: () =>
|
|
2048
|
+
_openCoreMemoryEditor(
|
|
2049
|
+
context,
|
|
2050
|
+
controller,
|
|
2051
|
+
keyValue: entry,
|
|
2050
2052
|
),
|
|
2051
|
-
|
|
2053
|
+
icon: Icon(Icons.edit_outlined),
|
|
2052
2054
|
),
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
entry.key,
|
|
2064
|
-
style: TextStyle(
|
|
2065
|
-
fontWeight:
|
|
2066
|
-
FontWeight.w700,
|
|
2067
|
-
),
|
|
2068
|
-
),
|
|
2069
|
-
const SizedBox(height: 6),
|
|
2070
|
-
Text(entry.value.toString()),
|
|
2071
|
-
],
|
|
2072
|
-
),
|
|
2073
|
-
),
|
|
2074
|
-
IconButton(
|
|
2075
|
-
onPressed: () =>
|
|
2076
|
-
_openCoreMemoryEditor(
|
|
2077
|
-
context,
|
|
2078
|
-
controller,
|
|
2079
|
-
keyValue: entry,
|
|
2080
|
-
),
|
|
2081
|
-
icon: Icon(Icons.edit_outlined),
|
|
2082
|
-
),
|
|
2083
|
-
IconButton(
|
|
2084
|
-
onPressed: () => _confirmDelete(
|
|
2085
|
-
context,
|
|
2086
|
-
title:
|
|
2087
|
-
'Delete core memory entry?',
|
|
2088
|
-
message:
|
|
2089
|
-
'Remove "${entry.key}" from core memory.',
|
|
2090
|
-
onConfirm: () =>
|
|
2091
|
-
controller.deleteCoreMemory(
|
|
2092
|
-
entry.key,
|
|
2093
|
-
),
|
|
2094
|
-
),
|
|
2095
|
-
icon: Icon(Icons.delete_outline),
|
|
2055
|
+
IconButton(
|
|
2056
|
+
onPressed: () => _confirmDelete(
|
|
2057
|
+
context,
|
|
2058
|
+
title:
|
|
2059
|
+
'Delete core memory entry?',
|
|
2060
|
+
message:
|
|
2061
|
+
'Remove "${entry.key}" from core memory.',
|
|
2062
|
+
onConfirm: () => controller
|
|
2063
|
+
.deleteCoreMemory(
|
|
2064
|
+
entry.key,
|
|
2096
2065
|
),
|
|
2097
|
-
|
|
2066
|
+
),
|
|
2067
|
+
icon: Icon(Icons.delete_outline),
|
|
2098
2068
|
),
|
|
2099
|
-
|
|
2100
|
-
|
|
2069
|
+
],
|
|
2070
|
+
),
|
|
2071
|
+
);
|
|
2072
|
+
}),
|
|
2101
2073
|
],
|
|
2102
2074
|
),
|
|
2103
2075
|
),
|
|
@@ -2120,8 +2092,11 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2120
2092
|
FilledButton.icon(
|
|
2121
2093
|
onPressed: _llmPromptLoading
|
|
2122
2094
|
? null
|
|
2123
|
-
: () =>
|
|
2124
|
-
|
|
2095
|
+
: () =>
|
|
2096
|
+
_loadLlmPrompt(controller),
|
|
2097
|
+
icon: Icon(
|
|
2098
|
+
Icons.auto_awesome_outlined,
|
|
2099
|
+
),
|
|
2125
2100
|
label: Text(
|
|
2126
2101
|
_llmPromptLoading
|
|
2127
2102
|
? 'Generating...'
|
|
@@ -2130,9 +2105,11 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2130
2105
|
),
|
|
2131
2106
|
OutlinedButton.icon(
|
|
2132
2107
|
onPressed:
|
|
2133
|
-
_llmPromptController.text
|
|
2134
|
-
|
|
2135
|
-
|
|
2108
|
+
_llmPromptController.text
|
|
2109
|
+
.trim()
|
|
2110
|
+
.isEmpty
|
|
2111
|
+
? null
|
|
2112
|
+
: _copyLlmPrompt,
|
|
2136
2113
|
icon: Icon(Icons.copy_all_outlined),
|
|
2137
2114
|
label: Text('Copy Prompt'),
|
|
2138
2115
|
),
|
|
@@ -2145,7 +2122,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2145
2122
|
maxLines: 8,
|
|
2146
2123
|
readOnly: true,
|
|
2147
2124
|
decoration: const InputDecoration(
|
|
2148
|
-
labelText:
|
|
2125
|
+
labelText:
|
|
2126
|
+
'Prompt to paste into another AI',
|
|
2149
2127
|
),
|
|
2150
2128
|
),
|
|
2151
2129
|
const SizedBox(height: 16),
|
|
@@ -2155,7 +2133,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2155
2133
|
onChanged: _llmImporting
|
|
2156
2134
|
? null
|
|
2157
2135
|
: (value) => setState(
|
|
2158
|
-
() =>
|
|
2136
|
+
() =>
|
|
2137
|
+
_llmApplyBehaviorNotes = value,
|
|
2159
2138
|
),
|
|
2160
2139
|
title: Text('Apply behavior notes'),
|
|
2161
2140
|
subtitle: Text(
|
|
@@ -2168,7 +2147,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2168
2147
|
onChanged: _llmImporting
|
|
2169
2148
|
? null
|
|
2170
2149
|
: (value) => setState(
|
|
2171
|
-
() =>
|
|
2150
|
+
() =>
|
|
2151
|
+
_llmApplyCoreMemory = value,
|
|
2172
2152
|
),
|
|
2173
2153
|
title: Text('Apply core memory'),
|
|
2174
2154
|
subtitle: Text(
|
|
@@ -2188,10 +2168,13 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2188
2168
|
FilledButton.icon(
|
|
2189
2169
|
onPressed: _llmImporting
|
|
2190
2170
|
? null
|
|
2191
|
-
: () =>
|
|
2171
|
+
: () =>
|
|
2172
|
+
_importLlmMemories(controller),
|
|
2192
2173
|
icon: Icon(Icons.file_download_outlined),
|
|
2193
2174
|
label: Text(
|
|
2194
|
-
_llmImporting
|
|
2175
|
+
_llmImporting
|
|
2176
|
+
? 'Importing...'
|
|
2177
|
+
: 'Import',
|
|
2195
2178
|
),
|
|
2196
2179
|
),
|
|
2197
2180
|
],
|
|
@@ -2470,10 +2453,9 @@ class _MemoryConfidenceGaugeState extends State<_MemoryConfidenceGauge>
|
|
|
2470
2453
|
vsync: this,
|
|
2471
2454
|
duration: const Duration(milliseconds: 1200),
|
|
2472
2455
|
);
|
|
2473
|
-
_progress = Tween<double>(
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic));
|
|
2456
|
+
_progress = Tween<double>(begin: 0, end: widget.confidence).animate(
|
|
2457
|
+
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
|
2458
|
+
);
|
|
2477
2459
|
_controller.forward();
|
|
2478
2460
|
}
|
|
2479
2461
|
|
|
@@ -2481,10 +2463,12 @@ class _MemoryConfidenceGaugeState extends State<_MemoryConfidenceGauge>
|
|
|
2481
2463
|
void didUpdateWidget(_MemoryConfidenceGauge oldWidget) {
|
|
2482
2464
|
super.didUpdateWidget(oldWidget);
|
|
2483
2465
|
if (oldWidget.confidence != widget.confidence) {
|
|
2484
|
-
_progress = Tween<double>(
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2466
|
+
_progress = Tween<double>(
|
|
2467
|
+
begin: _progress.value,
|
|
2468
|
+
end: widget.confidence,
|
|
2469
|
+
).animate(
|
|
2470
|
+
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
|
2471
|
+
);
|
|
2488
2472
|
_controller
|
|
2489
2473
|
..reset()
|
|
2490
2474
|
..forward();
|
|
@@ -2674,7 +2658,10 @@ class _MemoryRow extends StatelessWidget {
|
|
|
2674
2658
|
if (onDelete != null)
|
|
2675
2659
|
IconButton(
|
|
2676
2660
|
onPressed: onDelete,
|
|
2677
|
-
icon: Icon(
|
|
2661
|
+
icon: Icon(
|
|
2662
|
+
Icons.delete_outline,
|
|
2663
|
+
size: 18,
|
|
2664
|
+
),
|
|
2678
2665
|
),
|
|
2679
2666
|
],
|
|
2680
2667
|
),
|
|
@@ -2699,7 +2686,10 @@ class _MemoryRow extends StatelessWidget {
|
|
|
2699
2686
|
const SizedBox(height: 6),
|
|
2700
2687
|
Text(
|
|
2701
2688
|
memory.createdAtLabel,
|
|
2702
|
-
style: TextStyle(
|
|
2689
|
+
style: TextStyle(
|
|
2690
|
+
fontSize: 11,
|
|
2691
|
+
color: _textMuted,
|
|
2692
|
+
),
|
|
2703
2693
|
),
|
|
2704
2694
|
],
|
|
2705
2695
|
),
|
|
@@ -2788,31 +2778,27 @@ class _EntityGraphViewState extends State<_EntityGraphView>
|
|
|
2788
2778
|
for (int i = 0; i < entities.length; i++) {
|
|
2789
2779
|
final entity = entities[i];
|
|
2790
2780
|
final sizeFactor = 0.4 + 0.6 * (entity.mentionCount / maxMention);
|
|
2791
|
-
_nodes.add(
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
),
|
|
2801
|
-
);
|
|
2781
|
+
_nodes.add(_GraphNode(
|
|
2782
|
+
id: entity.name,
|
|
2783
|
+
label: entity.name,
|
|
2784
|
+
radius: 18 + 20 * sizeFactor,
|
|
2785
|
+
color: _kindColors[entity.kind] ?? _kindColors['concept']!,
|
|
2786
|
+
kind: entity.kind,
|
|
2787
|
+
isReflection: false,
|
|
2788
|
+
offsetPhase: i * 0.7,
|
|
2789
|
+
));
|
|
2802
2790
|
}
|
|
2803
2791
|
|
|
2804
2792
|
for (int i = 0; i < views.length && i < 6; i++) {
|
|
2805
|
-
_nodes.add(
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
),
|
|
2815
|
-
);
|
|
2793
|
+
_nodes.add(_GraphNode(
|
|
2794
|
+
id: 'kv_${views[i].title}',
|
|
2795
|
+
label: views[i].title,
|
|
2796
|
+
radius: 14,
|
|
2797
|
+
color: const Color(0xFF8B7EC8),
|
|
2798
|
+
kind: views[i].viewType,
|
|
2799
|
+
isReflection: true,
|
|
2800
|
+
offsetPhase: (entities.length + i) * 0.9,
|
|
2801
|
+
));
|
|
2816
2802
|
}
|
|
2817
2803
|
|
|
2818
2804
|
_layoutDone = false;
|
|
@@ -2953,12 +2939,10 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
2953
2939
|
void paint(Canvas canvas, Size size) {
|
|
2954
2940
|
if (nodes.isEmpty) return;
|
|
2955
2941
|
|
|
2956
|
-
final entityNodes =
|
|
2957
|
-
.where((n) => !n.isReflection)
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
.where((n) => n.isReflection)
|
|
2961
|
-
.toList(growable: false);
|
|
2942
|
+
final entityNodes =
|
|
2943
|
+
nodes.where((n) => !n.isReflection).toList(growable: false);
|
|
2944
|
+
final reflectionNodes =
|
|
2945
|
+
nodes.where((n) => n.isReflection).toList(growable: false);
|
|
2962
2946
|
|
|
2963
2947
|
// Draw connections between entity nodes (subtle web)
|
|
2964
2948
|
final linePaint = Paint()
|
|
@@ -2990,8 +2974,8 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
2990
2974
|
var closest = entityNodes.first;
|
|
2991
2975
|
var minDist = double.infinity;
|
|
2992
2976
|
for (final en in entityNodes) {
|
|
2993
|
-
final d =
|
|
2994
|
-
(en.
|
|
2977
|
+
final d = (en.x - rn.x) * (en.x - rn.x) +
|
|
2978
|
+
(en.y - rn.y) * (en.y - rn.y);
|
|
2995
2979
|
if (d < minDist) {
|
|
2996
2980
|
minDist = d;
|
|
2997
2981
|
closest = en;
|
|
@@ -3018,9 +3002,8 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
3018
3002
|
// Glow
|
|
3019
3003
|
if (isSelected || isHovered) {
|
|
3020
3004
|
final glowPaint = Paint()
|
|
3021
|
-
..color = (isSelected ? accentColor : node.color)
|
|
3022
|
-
|
|
3023
|
-
)
|
|
3005
|
+
..color = (isSelected ? accentColor : node.color)
|
|
3006
|
+
.withValues(alpha: 0.22)
|
|
3024
3007
|
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12);
|
|
3025
3008
|
canvas.drawCircle(Offset(cx, cy), r + 6, glowPaint);
|
|
3026
3009
|
}
|
|
@@ -3040,8 +3023,8 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
3040
3023
|
..color = isSelected
|
|
3041
3024
|
? accentColor
|
|
3042
3025
|
: (isHovered
|
|
3043
|
-
|
|
3044
|
-
|
|
3026
|
+
? node.color.withValues(alpha: 0.8)
|
|
3027
|
+
: node.color.withValues(alpha: 0.35))
|
|
3045
3028
|
..style = PaintingStyle.stroke
|
|
3046
3029
|
..strokeWidth = isSelected ? 2.5 : 1.5;
|
|
3047
3030
|
canvas.drawCircle(Offset(cx, cy), r, borderPaint);
|
|
@@ -3058,7 +3041,10 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
3058
3041
|
maxLines: 1,
|
|
3059
3042
|
ellipsis: '…',
|
|
3060
3043
|
)..layout(maxWidth: r * 3);
|
|
3061
|
-
tp.paint(
|
|
3044
|
+
tp.paint(
|
|
3045
|
+
canvas,
|
|
3046
|
+
Offset(cx - tp.width / 2, cy + r + 5),
|
|
3047
|
+
);
|
|
3062
3048
|
}
|
|
3063
3049
|
}
|
|
3064
3050
|
|
|
@@ -5039,15 +5025,6 @@ const List<_TaskTriggerOption> _taskTriggerOptions = <_TaskTriggerOption>[
|
|
|
5039
5025
|
providerKey: 'google_workspace',
|
|
5040
5026
|
appKey: 'gmail',
|
|
5041
5027
|
),
|
|
5042
|
-
_TaskTriggerOption(
|
|
5043
|
-
type: 'neomail_email_received',
|
|
5044
|
-
section: 'Email',
|
|
5045
|
-
label: 'NeoMail Email Received',
|
|
5046
|
-
description: 'Run when a matching message is synced into NeoMail.',
|
|
5047
|
-
icon: Icons.mark_email_unread_rounded,
|
|
5048
|
-
providerKey: 'neomail',
|
|
5049
|
-
appKey: 'mailbox',
|
|
5050
|
-
),
|
|
5051
5028
|
_TaskTriggerOption(
|
|
5052
5029
|
type: 'outlook_email_received',
|
|
5053
5030
|
section: 'Email',
|
|
@@ -5110,6 +5087,7 @@ _TaskTriggerOption _taskTriggerOptionForType(String type) {
|
|
|
5110
5087
|
);
|
|
5111
5088
|
}
|
|
5112
5089
|
|
|
5090
|
+
|
|
5113
5091
|
Future<String?> _pickTaskTriggerType(
|
|
5114
5092
|
BuildContext context,
|
|
5115
5093
|
String selectedType,
|
|
@@ -6289,7 +6267,9 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6289
6267
|
final selectedConnectionId = ValueNotifier<int?>(
|
|
6290
6268
|
task?.triggerConfig['connectionId'] is int
|
|
6291
6269
|
? task!.triggerConfig['connectionId'] as int
|
|
6292
|
-
: int.tryParse(
|
|
6270
|
+
: int.tryParse(
|
|
6271
|
+
task?.triggerConfig['connectionId']?.toString() ?? '',
|
|
6272
|
+
),
|
|
6293
6273
|
);
|
|
6294
6274
|
final selectedDeliveryTarget = ValueNotifier<TaskDeliveryTarget?>(
|
|
6295
6275
|
_taskDeliveryTargetFromTask(task),
|
|
@@ -6312,8 +6292,6 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6312
6292
|
);
|
|
6313
6293
|
final channelController = TextEditingController(
|
|
6314
6294
|
text:
|
|
6315
|
-
task?.triggerConfig['folder']?.toString() ??
|
|
6316
|
-
task?.triggerConfig['folderId']?.toString() ??
|
|
6317
6295
|
task?.triggerConfig['channel']?.toString() ??
|
|
6318
6296
|
task?.triggerConfig['chatId']?.toString() ??
|
|
6319
6297
|
'',
|
|
@@ -6534,8 +6512,6 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6534
6512
|
],
|
|
6535
6513
|
if (selectedTriggerType ==
|
|
6536
6514
|
'gmail_message_received' ||
|
|
6537
|
-
selectedTriggerType ==
|
|
6538
|
-
'neomail_email_received' ||
|
|
6539
6515
|
selectedTriggerType ==
|
|
6540
6516
|
'outlook_email_received') ...<Widget>[
|
|
6541
6517
|
TextField(
|
|
@@ -6553,18 +6529,6 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6553
6529
|
setLocalState(() => unreadOnly = value),
|
|
6554
6530
|
),
|
|
6555
6531
|
],
|
|
6556
|
-
if (selectedTriggerType ==
|
|
6557
|
-
'neomail_email_received') ...<Widget>[
|
|
6558
|
-
TextField(
|
|
6559
|
-
controller: channelController,
|
|
6560
|
-
decoration: const InputDecoration(
|
|
6561
|
-
labelText: 'Folder (optional)',
|
|
6562
|
-
helperText:
|
|
6563
|
-
'Example: INBOX, archive, or a custom folder path.',
|
|
6564
|
-
),
|
|
6565
|
-
),
|
|
6566
|
-
const SizedBox(height: 12),
|
|
6567
|
-
],
|
|
6568
6532
|
if (selectedTriggerType ==
|
|
6569
6533
|
'outlook_email_received') ...<Widget>[
|
|
6570
6534
|
TextField(
|
|
@@ -6918,17 +6882,11 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6918
6882
|
triggerConfig['eventTypes'] = eventTypes;
|
|
6919
6883
|
}
|
|
6920
6884
|
if (selectedTriggerType == 'gmail_message_received' ||
|
|
6921
|
-
selectedTriggerType == 'neomail_email_received' ||
|
|
6922
6885
|
selectedTriggerType == 'outlook_email_received') {
|
|
6923
6886
|
if (queryController.text.trim().isNotEmpty) {
|
|
6924
6887
|
triggerConfig['query'] = queryController.text.trim();
|
|
6925
6888
|
}
|
|
6926
6889
|
triggerConfig['unreadOnly'] = unreadOnly;
|
|
6927
|
-
if (selectedTriggerType == 'neomail_email_received' &&
|
|
6928
|
-
channelController.text.trim().isNotEmpty) {
|
|
6929
|
-
triggerConfig['folder'] = channelController.text
|
|
6930
|
-
.trim();
|
|
6931
|
-
}
|
|
6932
6890
|
if (selectedTriggerType == 'outlook_email_received' &&
|
|
6933
6891
|
channelController.text.trim().isNotEmpty) {
|
|
6934
6892
|
triggerConfig['folderId'] = channelController.text
|