neoagent 2.4.1-beta.8 → 2.4.1

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.
Files changed (248) hide show
  1. package/.env.example +33 -3
  2. package/LICENSE +111 -56
  3. package/README.md +8 -3
  4. package/docs/configuration.md +8 -0
  5. package/docs/getting-started.md +9 -3
  6. package/docs/index.md +4 -0
  7. package/extensions/chrome-browser/background.mjs +45 -7
  8. package/extensions/chrome-browser/icons/icon128.png +0 -0
  9. package/extensions/chrome-browser/icons/icon16.png +0 -0
  10. package/extensions/chrome-browser/icons/icon48.png +0 -0
  11. package/extensions/chrome-browser/icons/logo.svg +39 -8
  12. package/extensions/chrome-browser/manifest.json +3 -2
  13. package/extensions/chrome-browser/popup.html +5 -1
  14. package/extensions/chrome-browser/popup.js +15 -2
  15. package/flutter_app/android/app/src/main/AndroidManifest.xml +2 -1
  16. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  17. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  18. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  19. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  20. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  21. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  22. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  23. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  24. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  25. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  26. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  27. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  28. package/flutter_app/assets/branding/app_icon_light_1024.png +0 -0
  29. package/flutter_app/assets/branding/app_icon_light_128.png +0 -0
  30. package/flutter_app/assets/branding/app_icon_light_192.png +0 -0
  31. package/flutter_app/assets/branding/app_icon_light_256.png +0 -0
  32. package/flutter_app/assets/branding/app_icon_light_32.png +0 -0
  33. package/flutter_app/assets/branding/app_icon_light_512.png +0 -0
  34. package/flutter_app/assets/branding/app_icon_light_64.png +0 -0
  35. package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
  36. package/flutter_app/assets/branding/tray_icon_light_template.png +0 -0
  37. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  38. package/flutter_app/lib/features/location/location_service.dart +3 -0
  39. package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +391 -382
  40. package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +743 -0
  41. package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +18 -16
  42. package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +19 -18
  43. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +8 -1
  44. package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +16 -13
  45. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +17 -13
  46. package/flutter_app/lib/main.dart +3 -0
  47. package/flutter_app/lib/main_account_settings.dart +10 -34
  48. package/flutter_app/lib/main_admin.dart +14 -1
  49. package/flutter_app/lib/main_app_shell.dart +377 -340
  50. package/flutter_app/lib/main_chat.dart +707 -227
  51. package/flutter_app/lib/main_controller.dart +338 -46
  52. package/flutter_app/lib/main_devices.dart +851 -122
  53. package/flutter_app/lib/main_integrations.dart +255 -6
  54. package/flutter_app/lib/main_launcher.dart +1 -1
  55. package/flutter_app/lib/main_model_picker.dart +685 -0
  56. package/flutter_app/lib/main_models.dart +212 -0
  57. package/flutter_app/lib/main_navigation.dart +1 -9
  58. package/flutter_app/lib/main_operations.dart +1417 -614
  59. package/flutter_app/lib/main_settings.dart +764 -887
  60. package/flutter_app/lib/main_shared.dart +971 -443
  61. package/flutter_app/lib/main_spacing.dart +4 -4
  62. package/flutter_app/lib/main_theme.dart +22 -14
  63. package/flutter_app/lib/main_unified.dart +5 -72
  64. package/flutter_app/lib/src/android_apk_drop_zone.dart +24 -0
  65. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +13 -0
  66. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +219 -0
  67. package/flutter_app/lib/src/backend_client.dart +79 -0
  68. package/flutter_app/lib/src/desktop_companion_actions.dart +56 -7
  69. package/flutter_app/lib/src/desktop_companion_io.dart +77 -1
  70. package/flutter_app/lib/src/desktop_native_bridge.dart +13 -0
  71. package/flutter_app/lib/src/security/password_strength.dart +84 -0
  72. package/flutter_app/lib/src/stream_renderer.dart +205 -35
  73. package/flutter_app/lib/src/theme/palette.dart +76 -34
  74. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  75. package/flutter_app/macos/Runner/AppDelegate.swift +44 -0
  76. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  77. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  78. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  79. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  80. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  81. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  82. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  83. package/flutter_app/pubspec.lock +2 -2
  84. package/flutter_app/pubspec.yaml +7 -1
  85. package/flutter_app/tool/branding_source/neoagent-icon-1024.png +0 -0
  86. package/flutter_app/tool/branding_source/neoagent-icon-128.png +0 -0
  87. package/flutter_app/tool/branding_source/neoagent-icon-16.png +0 -0
  88. package/flutter_app/tool/branding_source/neoagent-icon-180.png +0 -0
  89. package/flutter_app/tool/branding_source/neoagent-icon-192.png +0 -0
  90. package/flutter_app/tool/branding_source/neoagent-icon-256.png +0 -0
  91. package/flutter_app/tool/branding_source/neoagent-icon-32.png +0 -0
  92. package/flutter_app/tool/branding_source/neoagent-icon-48.png +0 -0
  93. package/flutter_app/tool/branding_source/neoagent-icon-512.png +0 -0
  94. package/flutter_app/tool/branding_source/neoagent-icon-64.png +0 -0
  95. package/flutter_app/tool/branding_source/neoagent-icon.svg +43 -0
  96. package/flutter_app/tool/generate_desktop_branding.py +154 -152
  97. package/flutter_app/web/favicon.png +0 -0
  98. package/flutter_app/web/favicon.svg +40 -9
  99. package/flutter_app/web/favicon_light.svg +43 -0
  100. package/flutter_app/web/icons/Icon-192-light.png +0 -0
  101. package/flutter_app/web/icons/Icon-192.png +0 -0
  102. package/flutter_app/web/icons/Icon-512-light.png +0 -0
  103. package/flutter_app/web/icons/Icon-512.png +0 -0
  104. package/flutter_app/web/icons/Icon-maskable-192-light.png +0 -0
  105. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  106. package/flutter_app/web/icons/Icon-maskable-512-light.png +0 -0
  107. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  108. package/flutter_app/windows/runner/main.cpp +7 -1
  109. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  110. package/lib/manager.js +445 -83
  111. package/package.json +17 -3
  112. package/runtime/paths.js +3 -1
  113. package/server/admin/access.js +198 -0
  114. package/server/admin/admin.css +268 -0
  115. package/server/admin/admin.js +348 -0
  116. package/server/admin/analytics.js +128 -0
  117. package/server/admin/index.html +1015 -0
  118. package/server/admin/login.html +290 -0
  119. package/server/admin/logo.svg +43 -0
  120. package/server/admin/sql.js +134 -0
  121. package/server/admin/users.js +147 -0
  122. package/server/config/origins.js +3 -1
  123. package/server/db/database.js +92 -0
  124. package/server/db/ftsQuery.js +27 -0
  125. package/server/http/routes.js +1 -0
  126. package/server/http/static.js +23 -6
  127. package/server/index.js +1 -1
  128. package/server/middleware/adminAuth.js +48 -0
  129. package/server/middleware/auth.js +1 -40
  130. package/server/public/.last_build_id +1 -1
  131. package/server/public/app_icon.png +0 -0
  132. package/server/public/assets/AssetManifest.bin +1 -1
  133. package/server/public/assets/AssetManifest.bin.json +1 -1
  134. package/server/public/assets/assets/branding/app_icon_1024.png +0 -0
  135. package/server/public/assets/assets/branding/app_icon_256.png +0 -0
  136. package/server/public/assets/assets/branding/app_icon_512.png +0 -0
  137. package/server/public/assets/assets/branding/app_icon_light_1024.png +0 -0
  138. package/server/public/assets/assets/branding/app_icon_light_256.png +0 -0
  139. package/server/public/assets/assets/branding/app_icon_light_512.png +0 -0
  140. package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
  141. package/server/public/assets/assets/branding/tray_icon_light_template.png +0 -0
  142. package/server/public/assets/assets/branding/tray_icon_template.png +0 -0
  143. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  144. package/server/public/assets/web/icons/Icon-192.png +0 -0
  145. package/server/public/favicon.png +0 -0
  146. package/server/public/favicon.svg +40 -9
  147. package/server/public/favicon_light.svg +43 -0
  148. package/server/public/flutter_bootstrap.js +2 -2
  149. package/server/public/icons/Icon-192-light.png +0 -0
  150. package/server/public/icons/Icon-192.png +0 -0
  151. package/server/public/icons/Icon-512-light.png +0 -0
  152. package/server/public/icons/Icon-512.png +0 -0
  153. package/server/public/icons/Icon-maskable-192-light.png +0 -0
  154. package/server/public/icons/Icon-maskable-192.png +0 -0
  155. package/server/public/icons/Icon-maskable-512-light.png +0 -0
  156. package/server/public/icons/Icon-maskable-512.png +0 -0
  157. package/server/public/main.dart.js +88944 -85720
  158. package/server/routes/admin.js +632 -0
  159. package/server/routes/agent_profiles.js +3 -0
  160. package/server/routes/agents.js +1 -1
  161. package/server/routes/auth.js +3 -1
  162. package/server/routes/browser.js +14 -0
  163. package/server/routes/browser_extension.js +21 -4
  164. package/server/routes/desktop.js +10 -0
  165. package/server/routes/mcp.js +29 -13
  166. package/server/routes/memory.js +23 -1
  167. package/server/routes/messaging.js +2 -0
  168. package/server/routes/screenHistory.js +14 -8
  169. package/server/routes/settings.js +14 -2
  170. package/server/routes/stream.js +12 -3
  171. package/server/routes/triggers.js +4 -4
  172. package/server/routes/voice_assistant.js +36 -1
  173. package/server/routes/workspace.js +86 -0
  174. package/server/services/account/admin_two_factor.js +132 -0
  175. package/server/services/account/password_policy.js +6 -1
  176. package/server/services/ai/completion.js +44 -0
  177. package/server/services/ai/engine.js +321 -500
  178. package/server/services/ai/imageAnalysis.js +9 -5
  179. package/server/services/ai/logFormat.js +46 -0
  180. package/server/services/ai/loopPolicy.js +11 -0
  181. package/server/services/ai/messagingFallback.js +228 -0
  182. package/server/services/ai/models.js +194 -55
  183. package/server/services/ai/providerRetry.js +169 -0
  184. package/server/services/ai/providers/anthropic.js +15 -4
  185. package/server/services/ai/providers/google.js +38 -11
  186. package/server/services/ai/providers/grok.js +19 -68
  187. package/server/services/ai/providers/grokOauth.js +141 -0
  188. package/server/services/ai/providers/nvidia.js +154 -0
  189. package/server/services/ai/providers/ollama.js +76 -39
  190. package/server/services/ai/providers/openai.js +20 -31
  191. package/server/services/ai/providers/openaiCodex.js +6 -2
  192. package/server/services/ai/providers/openaiCompatible.js +70 -0
  193. package/server/services/ai/providers/openrouter.js +162 -0
  194. package/server/services/ai/settings.js +30 -0
  195. package/server/services/ai/systemPrompt.js +51 -29
  196. package/server/services/ai/taskAnalysis.js +60 -1
  197. package/server/services/ai/toolEvidence.js +207 -0
  198. package/server/services/ai/toolSelector.js +8 -1
  199. package/server/services/ai/tools.js +80 -15
  200. package/server/services/android/controller.js +36 -1
  201. package/server/services/browser/controller.js +18 -0
  202. package/server/services/browser/extension/manifest.js +33 -0
  203. package/server/services/browser/extension/provider.js +12 -6
  204. package/server/services/browser/extension/registry.js +229 -18
  205. package/server/services/desktop/protocol.js +1 -0
  206. package/server/services/desktop/provider.js +4 -0
  207. package/server/services/desktop/registry.js +104 -1
  208. package/server/services/desktop/screenRecorder.js +208 -98
  209. package/server/services/desktop/screen_recorder_support.js +46 -0
  210. package/server/services/integrations/google/provider.js +13 -0
  211. package/server/services/integrations/home_assistant/constants.js +88 -0
  212. package/server/services/integrations/home_assistant/network.js +207 -0
  213. package/server/services/integrations/home_assistant/provider.js +249 -0
  214. package/server/services/integrations/home_assistant/snapshot.js +98 -0
  215. package/server/services/integrations/home_assistant/tools.js +101 -0
  216. package/server/services/integrations/manager.js +5 -0
  217. package/server/services/integrations/registry.js +2 -0
  218. package/server/services/integrations/trello/provider.js +8 -2
  219. package/server/services/manager.js +51 -47
  220. package/server/services/mcp/client.js +208 -268
  221. package/server/services/mcp/client_support.js +172 -0
  222. package/server/services/mcp/recovery.js +116 -0
  223. package/server/services/mcp/tool_operations.js +123 -0
  224. package/server/services/memory/ingestion.js +185 -370
  225. package/server/services/memory/ingestion_coverage.js +129 -0
  226. package/server/services/memory/ingestion_documents.js +123 -0
  227. package/server/services/memory/ingestion_support.js +171 -0
  228. package/server/services/memory/intelligence.js +181 -0
  229. package/server/services/memory/manager.js +476 -32
  230. package/server/services/messaging/automation.js +71 -134
  231. package/server/services/messaging/formatting_guides.js +26 -1
  232. package/server/services/messaging/inbound_queue.js +111 -0
  233. package/server/services/messaging/manager.js +25 -8
  234. package/server/services/messaging/typing_keepalive.js +110 -0
  235. package/server/services/runtime/manager.js +7 -3
  236. package/server/services/runtime/settings.js +17 -0
  237. package/server/services/streaming/android-stream.js +296 -32
  238. package/server/services/streaming/stream-hub.js +1 -1
  239. package/server/services/tasks/integration_runtime.js +4 -1
  240. package/server/services/tasks/runtime.js +415 -56
  241. package/server/services/tasks/task_repository.js +48 -6
  242. package/server/services/websocket.js +8 -2
  243. package/server/services/widgets/service.js +17 -1
  244. package/server/services/workspace/manager.js +116 -0
  245. package/server/utils/logger.js +44 -6
  246. package/server/utils/security.js +3 -0
  247. package/server/utils/version.js +29 -19
  248. package/server/services/memory/openhuman_uplift.test.js +0 -98
@@ -4,9 +4,9 @@ part of 'main.dart';
4
4
  // surface palette to create visual contrast during the auth flow. Named here
5
5
  // so they can be updated in one place rather than hunted across the widget tree.
6
6
  const Color _qrPanelGradientStart = Color(0xFF0A1D2E);
7
- const Color _qrPanelGradientEnd = Color(0xFF112B43);
8
- const Color _qrPanelGlowBlue = Color(0xFF6EDBFF);
9
- const Color _qrPanelGlowGreen = Color(0xFF58E0A2);
7
+ const Color _qrPanelGradientEnd = Color(0xFF112B43);
8
+ const Color _qrPanelGlowBlue = Color(0xFF6EDBFF);
9
+ const Color _qrPanelGlowGreen = Color(0xFF58E0A2);
10
10
 
11
11
  class SplashView extends StatelessWidget {
12
12
  const SplashView({super.key});
@@ -37,7 +37,7 @@ class SplashView extends StatelessWidget {
37
37
  ),
38
38
  ),
39
39
  const SizedBox(height: 14),
40
- const Text('Loading NeoOS'),
40
+ const Text('Loading NeoAgent'),
41
41
  ],
42
42
  ),
43
43
  ),
@@ -274,7 +274,7 @@ class _AuthViewState extends State<AuthView> {
274
274
  crossAxisAlignment: CrossAxisAlignment.stretch,
275
275
  children: <Widget>[
276
276
  Text(
277
- 'Enter your username or account email. NeoOS will send a reset link if it can match the account.',
277
+ 'Enter your username or account email. NeoAgent will send a reset link if it can match the account.',
278
278
  style: TextStyle(color: _textSecondary, height: 1.45),
279
279
  ),
280
280
  const SizedBox(height: 16),
@@ -480,7 +480,7 @@ class _AuthViewState extends State<AuthView> {
480
480
  const SizedBox(height: 8),
481
481
  Text(
482
482
  awaitingTwoFactor
483
- ? 'Open your authenticator app and enter the current NeoOS code.'
483
+ ? 'Open your authenticator app and enter the current NeoAgent code.'
484
484
  : subtitle,
485
485
  style: TextStyle(color: _textSecondary, height: 1.5),
486
486
  ),
@@ -767,9 +767,9 @@ class _AuthViewState extends State<AuthView> {
767
767
  crossAxisAlignment: contentAlignment,
768
768
  children: <Widget>[
769
769
  Text(
770
- 'Scan with NeoOS on your phone',
770
+ 'Scan with NeoAgent on your phone',
771
771
  textAlign: titleAlignment,
772
- style: GoogleFonts.spaceGrotesk(
772
+ style: GoogleFonts.geist(
773
773
  fontSize: titleSize,
774
774
  fontWeight: FontWeight.w700,
775
775
  letterSpacing: compact ? -0.3 : -0.6,
@@ -948,9 +948,9 @@ class _AuthViewState extends State<AuthView> {
948
948
  : 'Sign in';
949
949
  final subtitle = _registerMode
950
950
  ? (controller.hasUser
951
- ? 'Create another NeoOS account.'
952
- : 'This account will unlock NeoOS on this machine.')
953
- : 'Enter your NeoOS account details.';
951
+ ? 'Create another NeoAgent account.'
952
+ : 'This account will unlock NeoAgent on this machine.')
953
+ : 'Enter your NeoAgent account details.';
954
954
  final awaitingTwoFactor = controller.isAwaitingTwoFactor;
955
955
  final showRegisterToggle =
956
956
  controller.registrationOpen && controller.hasUser;
@@ -982,7 +982,9 @@ class _AuthViewState extends State<AuthView> {
982
982
  ),
983
983
  child: Padding(
984
984
  padding: EdgeInsets.all(
985
- viewportConstraints.maxWidth < AppBreakpoints.mobile ? 14 : 24,
985
+ viewportConstraints.maxWidth < AppBreakpoints.mobile
986
+ ? 14
987
+ : 24,
986
988
  ),
987
989
  child: Center(
988
990
  child: ConstrainedBox(
@@ -998,10 +1000,22 @@ class _AuthViewState extends State<AuthView> {
998
1000
  fillColor: _glassFill,
999
1001
  child: Padding(
1000
1002
  padding: EdgeInsets.fromLTRB(
1001
- viewportConstraints.maxWidth < AppBreakpoints.mobile ? 18 : 34,
1002
- viewportConstraints.maxWidth < AppBreakpoints.mobile ? 20 : 30,
1003
- viewportConstraints.maxWidth < AppBreakpoints.mobile ? 18 : 34,
1004
- viewportConstraints.maxWidth < AppBreakpoints.mobile ? 20 : 30,
1003
+ viewportConstraints.maxWidth <
1004
+ AppBreakpoints.mobile
1005
+ ? 18
1006
+ : 34,
1007
+ viewportConstraints.maxWidth <
1008
+ AppBreakpoints.mobile
1009
+ ? 20
1010
+ : 30,
1011
+ viewportConstraints.maxWidth <
1012
+ AppBreakpoints.mobile
1013
+ ? 18
1014
+ : 34,
1015
+ viewportConstraints.maxWidth <
1016
+ AppBreakpoints.mobile
1017
+ ? 20
1018
+ : 30,
1005
1019
  ),
1006
1020
  child: LayoutBuilder(
1007
1021
  builder: (context, panelConstraints) {
@@ -1188,59 +1202,54 @@ class _HomeViewState extends State<HomeView> {
1188
1202
  final wide = MediaQuery.sizeOf(context).width >= 1080;
1189
1203
 
1190
1204
  if (wide) {
1191
- return _AmbientBackdrop(
1205
+ return _ControlSurfaceBackdrop(
1192
1206
  child: Scaffold(
1193
1207
  backgroundColor: Colors.transparent,
1194
- body: SafeArea(
1195
- child: Padding(
1196
- padding: const EdgeInsets.all(14),
1197
- child: Row(
1198
- children: <Widget>[
1199
- _Sidebar(
1200
- controller: controller,
1201
- expandedGroup: _expandedSidebarGroup,
1202
- onToggleGroup: _toggleSidebarGroup,
1203
- ),
1204
- const SizedBox(width: 14),
1205
- Expanded(
1206
- child: _GlassSurface(
1207
- borderRadius: BorderRadius.circular(32),
1208
- blurSigma: 28,
1209
- boxShadow: _softPanelShadow,
1210
- overlayGradient: _panelGradient,
1211
- fillColor: _glassFill,
1212
- child: ClipRRect(
1213
- borderRadius: BorderRadius.circular(32),
1214
- child: AnimatedSwitcher(
1215
- duration: const Duration(milliseconds: 260),
1216
- switchInCurve: Curves.easeOutCubic,
1217
- switchOutCurve: Curves.easeInCubic,
1218
- transitionBuilder: (child, animation) {
1219
- final offset = Tween<Offset>(
1220
- begin: const Offset(0.015, 0.02),
1221
- end: Offset.zero,
1222
- ).animate(animation);
1223
- return FadeTransition(
1224
- opacity: animation,
1225
- child: SlideTransition(
1226
- position: offset,
1227
- child: child,
1228
- ),
1229
- );
1230
- },
1231
- child: KeyedSubtree(
1232
- key: ValueKey<AppSection>(
1233
- controller.selectedSection,
1234
- ),
1235
- child: _SectionBody(controller: controller, devicesPanelKey: _devicesPanelKey),
1208
+ body: Row(
1209
+ children: <Widget>[
1210
+ _Sidebar(
1211
+ controller: controller,
1212
+ expandedGroup: _expandedSidebarGroup,
1213
+ onToggleGroup: _toggleSidebarGroup,
1214
+ ),
1215
+ Expanded(
1216
+ child: ClipRect(
1217
+ child: AnimatedSwitcher(
1218
+ duration: const Duration(milliseconds: 320),
1219
+ switchInCurve: Curves.easeOutBack,
1220
+ switchOutCurve: Curves.easeInCubic,
1221
+ transitionBuilder: (child, animation) {
1222
+ final offset = Tween<Offset>(
1223
+ begin: const Offset(0.018, 0.026),
1224
+ end: Offset.zero,
1225
+ ).animate(animation);
1226
+ final scale = Tween<double>(
1227
+ begin: 0.992,
1228
+ end: 1,
1229
+ ).animate(animation);
1230
+ return ScaleTransition(
1231
+ scale: scale,
1232
+ alignment: Alignment.topCenter,
1233
+ child: FadeTransition(
1234
+ opacity: animation,
1235
+ child: SlideTransition(
1236
+ position: offset,
1237
+ child: child,
1236
1238
  ),
1237
1239
  ),
1240
+ );
1241
+ },
1242
+ child: KeyedSubtree(
1243
+ key: ValueKey<AppSection>(controller.selectedSection),
1244
+ child: _SectionBody(
1245
+ controller: controller,
1246
+ devicesPanelKey: _devicesPanelKey,
1238
1247
  ),
1239
1248
  ),
1240
1249
  ),
1241
- ],
1250
+ ),
1242
1251
  ),
1243
- ),
1252
+ ],
1244
1253
  ),
1245
1254
  ),
1246
1255
  );
@@ -1268,21 +1277,38 @@ class _HomeViewState extends State<HomeView> {
1268
1277
  body: SafeArea(
1269
1278
  child: Padding(
1270
1279
  padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
1271
- child: _GlassSurface(
1272
- borderRadius: BorderRadius.circular(26),
1273
- blurSigma: 24,
1274
- boxShadow: _softPanelShadow,
1275
- overlayGradient: _panelGradient,
1276
- fillColor: _glassFill,
1280
+ child: Container(
1281
+ decoration: BoxDecoration(
1282
+ color: _bgPrimary,
1283
+ borderRadius: BorderRadius.circular(22),
1284
+ border: Border.all(color: _border),
1285
+ boxShadow: _softPanelShadow,
1286
+ ),
1287
+ clipBehavior: Clip.antiAlias,
1277
1288
  child: ClipRRect(
1278
- borderRadius: BorderRadius.circular(26),
1289
+ borderRadius: BorderRadius.circular(22),
1279
1290
  child: AnimatedSwitcher(
1280
- duration: const Duration(milliseconds: 240),
1281
- switchInCurve: Curves.easeOutCubic,
1291
+ duration: const Duration(milliseconds: 280),
1292
+ switchInCurve: Curves.easeOutBack,
1282
1293
  switchOutCurve: Curves.easeInCubic,
1294
+ transitionBuilder: (child, animation) {
1295
+ return FadeTransition(
1296
+ opacity: animation,
1297
+ child: SlideTransition(
1298
+ position: Tween<Offset>(
1299
+ begin: const Offset(0, 0.018),
1300
+ end: Offset.zero,
1301
+ ).animate(animation),
1302
+ child: child,
1303
+ ),
1304
+ );
1305
+ },
1283
1306
  child: KeyedSubtree(
1284
1307
  key: ValueKey<AppSection>(controller.selectedSection),
1285
- child: _SectionBody(controller: controller, devicesPanelKey: _devicesPanelKey),
1308
+ child: _SectionBody(
1309
+ controller: controller,
1310
+ devicesPanelKey: _devicesPanelKey,
1311
+ ),
1286
1312
  ),
1287
1313
  ),
1288
1314
  ),
@@ -1396,55 +1422,58 @@ class _Sidebar extends StatelessWidget {
1396
1422
 
1397
1423
  @override
1398
1424
  Widget build(BuildContext context) {
1399
- return _GlassSurface(
1400
- width: 254,
1401
- borderRadius: BorderRadius.circular(30),
1402
- blurSigma: 26,
1403
- boxShadow: _softPanelShadow,
1404
- fillColor: _bgSecondary.withValues(alpha: 0.34),
1405
- overlayGradient: LinearGradient(
1406
- colors: <Color>[
1407
- _bgSecondary.withValues(alpha: 0.96),
1408
- _bgTertiary.withValues(alpha: 0.88),
1409
- ],
1410
- begin: Alignment.topCenter,
1411
- end: Alignment.bottomCenter,
1425
+ return Container(
1426
+ width: 268,
1427
+ decoration: BoxDecoration(
1428
+ color: _bgSecondary,
1429
+ border: Border(right: BorderSide(color: _border)),
1412
1430
  ),
1413
1431
  child: Column(
1414
1432
  children: <Widget>[
1415
- Container(
1416
- padding: const EdgeInsets.fromLTRB(18, 20, 18, 18),
1417
- decoration: BoxDecoration(
1418
- border: Border(bottom: BorderSide(color: _border)),
1419
- ),
1420
- child: Column(
1421
- crossAxisAlignment: CrossAxisAlignment.start,
1433
+ Padding(
1434
+ padding: const EdgeInsets.fromLTRB(20, 18, 18, 12),
1435
+ child: Row(
1422
1436
  children: <Widget>[
1423
- Row(
1424
- children: <Widget>[
1425
- Expanded(
1426
- child: const _BrandLockup(
1427
- logoSize: 34,
1428
- titleFontSize: 18,
1429
- direction: Axis.horizontal,
1430
- spacing: 12,
1431
- alignment: CrossAxisAlignment.start,
1437
+ const _LogoBadge(size: 38),
1438
+ const SizedBox(width: 11),
1439
+ Expanded(
1440
+ child: Column(
1441
+ crossAxisAlignment: CrossAxisAlignment.start,
1442
+ mainAxisSize: MainAxisSize.min,
1443
+ children: <Widget>[
1444
+ Text(
1445
+ 'NeoAgent',
1446
+ style: GoogleFonts.geist(
1447
+ fontSize: 17,
1448
+ fontWeight: FontWeight.w600,
1449
+ color: _textPrimary,
1450
+ letterSpacing: -0.3,
1451
+ ),
1432
1452
  ),
1433
- ),
1434
- ],
1453
+ const SizedBox(height: 2),
1454
+ Text(
1455
+ 'CONTROL SURFACE',
1456
+ style: GoogleFonts.geistMono(
1457
+ fontSize: 9.5,
1458
+ fontWeight: FontWeight.w600,
1459
+ color: _textMuted,
1460
+ letterSpacing: 1.8,
1461
+ ),
1462
+ ),
1463
+ ],
1464
+ ),
1435
1465
  ),
1436
1466
  ],
1437
1467
  ),
1438
1468
  ),
1439
- if (controller.agentProfiles.isNotEmpty) ...<Widget>[
1469
+ if (controller.agentProfiles.isNotEmpty)
1440
1470
  Padding(
1441
- padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
1471
+ padding: const EdgeInsets.fromLTRB(14, 2, 14, 14),
1442
1472
  child: _AgentSwitcher(controller: controller),
1443
1473
  ),
1444
- ],
1445
1474
  Expanded(
1446
1475
  child: ListView(
1447
- padding: const EdgeInsets.all(8),
1476
+ padding: const EdgeInsets.fromLTRB(12, 0, 12, 8),
1448
1477
  children: _buildSidebarItems(
1449
1478
  controller,
1450
1479
  onSelect: controller.setSelectedSection,
@@ -1454,40 +1483,42 @@ class _Sidebar extends StatelessWidget {
1454
1483
  ),
1455
1484
  ),
1456
1485
  Container(
1457
- padding: const EdgeInsets.all(10),
1486
+ padding: const EdgeInsets.fromLTRB(16, 12, 12, 14),
1458
1487
  decoration: BoxDecoration(
1459
1488
  border: Border(top: BorderSide(color: _border)),
1460
1489
  ),
1461
- child: Column(
1490
+ child: Row(
1462
1491
  children: <Widget>[
1463
- Row(
1464
- children: <Widget>[
1465
- Expanded(
1466
- child: Text(
1467
- controller.accountLabel,
1468
- maxLines: 1,
1469
- overflow: TextOverflow.ellipsis,
1470
- style: TextStyle(
1471
- color: _textSecondary,
1472
- fontSize: 12,
1473
- fontWeight: FontWeight.w600,
1474
- ),
1475
- ),
1476
- ),
1477
- const SizedBox(width: 8),
1478
- _ProfileSettingsButton(
1479
- controller: controller,
1480
- onTap: () => controller.setSelectedSection(
1481
- AppSection.accountSettings,
1482
- ),
1483
- ),
1484
- const SizedBox(width: 8),
1485
- _SidebarIconButton(
1486
- tooltip: 'Logout',
1487
- icon: Icons.logout,
1488
- onTap: controller.logout,
1492
+ _SidebarAccountAvatar(
1493
+ controller: controller,
1494
+ onTap: () =>
1495
+ controller.setSelectedSection(AppSection.accountSettings),
1496
+ ),
1497
+ const SizedBox(width: 10),
1498
+ Expanded(
1499
+ child: Text(
1500
+ controller.accountLabel,
1501
+ maxLines: 1,
1502
+ overflow: TextOverflow.ellipsis,
1503
+ style: GoogleFonts.geist(
1504
+ color: _textSecondary,
1505
+ fontSize: 12.5,
1506
+ fontWeight: FontWeight.w500,
1489
1507
  ),
1490
- ],
1508
+ ),
1509
+ ),
1510
+ const SizedBox(width: 8),
1511
+ _SidebarIconButton(
1512
+ tooltip: 'Settings',
1513
+ icon: Icons.settings_outlined,
1514
+ onTap: () =>
1515
+ controller.setSelectedSection(AppSection.accountSettings),
1516
+ ),
1517
+ const SizedBox(width: 2),
1518
+ _SidebarIconButton(
1519
+ tooltip: 'Logout',
1520
+ icon: Icons.logout,
1521
+ onTap: controller.logout,
1491
1522
  ),
1492
1523
  ],
1493
1524
  ),
@@ -1557,27 +1588,14 @@ class _AgentSwitcherState extends State<_AgentSwitcher> {
1557
1588
  menuChildren: <Widget>[
1558
1589
  SizedBox(
1559
1590
  width: 320,
1560
- child: _GlassSurface(
1561
- borderRadius: BorderRadius.circular(24),
1562
- blurSigma: 28,
1563
- fillColor: _bgCard.withValues(alpha: 0.9),
1564
- overlayGradient: LinearGradient(
1565
- colors: <Color>[
1566
- Colors.white.withValues(alpha: 0.1),
1567
- _bgSecondary.withValues(alpha: 0.92),
1568
- _bgPrimary.withValues(alpha: 0.94),
1569
- ],
1570
- begin: Alignment.topLeft,
1571
- end: Alignment.bottomRight,
1591
+ child: Container(
1592
+ decoration: BoxDecoration(
1593
+ color: _bgCard,
1594
+ borderRadius: BorderRadius.circular(18),
1595
+ border: Border.all(color: _border),
1596
+ boxShadow: _softPanelShadow,
1572
1597
  ),
1573
- boxShadow: <BoxShadow>[
1574
- ..._softPanelShadow,
1575
- BoxShadow(
1576
- color: Colors.black.withValues(alpha: 0.22),
1577
- blurRadius: 28,
1578
- offset: const Offset(0, 16),
1579
- ),
1580
- ],
1598
+ clipBehavior: Clip.antiAlias,
1581
1599
  child: Padding(
1582
1600
  padding: const EdgeInsets.all(10),
1583
1601
  child: Column(
@@ -1605,106 +1623,96 @@ class _AgentSwitcherState extends State<_AgentSwitcher> {
1605
1623
  child: Tooltip(
1606
1624
  message: 'Switch agent',
1607
1625
  child: InkWell(
1608
- borderRadius: BorderRadius.circular(24),
1609
- onTap: _toggleMenu,
1610
- child: AnimatedContainer(
1611
- duration: const Duration(milliseconds: 180),
1612
- curve: Curves.easeOutCubic,
1613
- padding: const EdgeInsets.fromLTRB(12, 9, 12, 9),
1614
- decoration: BoxDecoration(
1615
- borderRadius: BorderRadius.circular(24),
1616
- gradient: LinearGradient(
1617
- colors: <Color>[
1618
- Colors.white.withValues(alpha: isMenuOpen ? 0.13 : 0.08),
1619
- _accentMuted.withValues(alpha: isMenuOpen ? 0.24 : 0.14),
1620
- _bgSecondary.withValues(alpha: isMenuOpen ? 0.92 : 0.84),
1626
+ borderRadius: BorderRadius.circular(14),
1627
+ onTap: _toggleMenu,
1628
+ child: AnimatedContainer(
1629
+ duration: const Duration(milliseconds: 180),
1630
+ curve: Curves.easeOutCubic,
1631
+ padding: const EdgeInsets.all(12),
1632
+ decoration: BoxDecoration(
1633
+ borderRadius: BorderRadius.circular(14),
1634
+ color: _bgCard,
1635
+ border: Border.all(
1636
+ color: isMenuOpen
1637
+ ? _accent.withValues(alpha: 0.45)
1638
+ : _borderLight,
1639
+ ),
1640
+ boxShadow: <BoxShadow>[
1641
+ BoxShadow(
1642
+ color: Colors.black.withValues(alpha: 0.05),
1643
+ blurRadius: 8,
1644
+ offset: const Offset(0, 1),
1645
+ ),
1621
1646
  ],
1622
- begin: Alignment.topLeft,
1623
- end: Alignment.bottomRight,
1624
1647
  ),
1625
- border: Border.all(
1626
- color: isMenuOpen
1627
- ? _accent.withValues(alpha: 0.65)
1628
- : _borderLight,
1629
- ),
1630
- boxShadow: isMenuOpen
1631
- ? <BoxShadow>[
1632
- BoxShadow(
1633
- color: _accent.withValues(alpha: 0.16),
1634
- blurRadius: 24,
1635
- offset: const Offset(0, 10),
1636
- ),
1637
- ]
1638
- : null,
1639
- ),
1640
- child: Row(
1641
- children: <Widget>[
1642
- _AgentGlyph(
1643
- agent: selectedAgent,
1644
- selected: true,
1645
- compact: true,
1646
- ),
1647
- const SizedBox(width: 10),
1648
- Expanded(
1649
- child: Column(
1650
- crossAxisAlignment: CrossAxisAlignment.start,
1651
- mainAxisSize: MainAxisSize.min,
1652
- children: <Widget>[
1653
- Row(
1654
- children: <Widget>[
1655
- Flexible(
1656
- child: Text(
1657
- selectedAgent.displayName,
1658
- maxLines: 1,
1659
- overflow: TextOverflow.ellipsis,
1660
- style: const TextStyle(
1661
- fontSize: 13.5,
1662
- fontWeight: FontWeight.w700,
1663
- letterSpacing: -0.15,
1648
+ child: Row(
1649
+ children: <Widget>[
1650
+ _AgentGlyph(
1651
+ agent: selectedAgent,
1652
+ selected: true,
1653
+ compact: true,
1654
+ ),
1655
+ const SizedBox(width: 10),
1656
+ Expanded(
1657
+ child: Column(
1658
+ crossAxisAlignment: CrossAxisAlignment.start,
1659
+ mainAxisSize: MainAxisSize.min,
1660
+ children: <Widget>[
1661
+ Row(
1662
+ children: <Widget>[
1663
+ Flexible(
1664
+ child: Text(
1665
+ selectedAgent.displayName,
1666
+ maxLines: 1,
1667
+ overflow: TextOverflow.ellipsis,
1668
+ style: const TextStyle(
1669
+ fontSize: 13.5,
1670
+ fontWeight: FontWeight.w700,
1671
+ letterSpacing: -0.15,
1672
+ ),
1664
1673
  ),
1665
1674
  ),
1666
- ),
1667
- if (selectedAgent.isDefault) ...<Widget>[
1668
- const SizedBox(width: 8),
1669
- _AgentTag(
1670
- label: 'DEFAULT',
1671
- color: _accent,
1672
- foreground: _accentHover,
1673
- ),
1675
+ if (selectedAgent.isDefault) ...<Widget>[
1676
+ const SizedBox(width: 8),
1677
+ _AgentTag(
1678
+ label: 'DEFAULT',
1679
+ color: _accent,
1680
+ foreground: _accentHover,
1681
+ ),
1682
+ ],
1674
1683
  ],
1675
- ],
1676
- ),
1677
- const SizedBox(height: 2),
1678
- Text(
1679
- _agentSwitcherSubtitle(selectedAgent),
1680
- maxLines: 1,
1681
- overflow: TextOverflow.ellipsis,
1682
- style: TextStyle(
1683
- color: _textSecondary,
1684
- fontSize: 12,
1685
- fontWeight: FontWeight.w600,
1686
- height: 1.2,
1687
1684
  ),
1688
- ),
1689
- ],
1685
+ const SizedBox(height: 2),
1686
+ Text(
1687
+ _agentSwitcherSubtitle(selectedAgent),
1688
+ maxLines: 1,
1689
+ overflow: TextOverflow.ellipsis,
1690
+ style: TextStyle(
1691
+ color: _textSecondary,
1692
+ fontSize: 12,
1693
+ fontWeight: FontWeight.w600,
1694
+ height: 1.2,
1695
+ ),
1696
+ ),
1697
+ ],
1698
+ ),
1690
1699
  ),
1691
- ),
1692
- const SizedBox(width: 8),
1693
- AnimatedRotation(
1694
- turns: isMenuOpen ? 0.5 : 0,
1695
- duration: const Duration(milliseconds: 180),
1696
- curve: Curves.easeOutCubic,
1697
- child: Icon(
1698
- Icons.keyboard_arrow_down_rounded,
1699
- size: 20,
1700
- color: isMenuOpen ? _accentHover : _textSecondary,
1700
+ const SizedBox(width: 8),
1701
+ AnimatedRotation(
1702
+ turns: isMenuOpen ? 0.5 : 0,
1703
+ duration: const Duration(milliseconds: 180),
1704
+ curve: Curves.easeOutCubic,
1705
+ child: Icon(
1706
+ Icons.keyboard_arrow_down_rounded,
1707
+ size: 20,
1708
+ color: isMenuOpen ? _accentHover : _textSecondary,
1709
+ ),
1701
1710
  ),
1702
- ),
1703
- ],
1711
+ ],
1712
+ ),
1704
1713
  ),
1705
1714
  ),
1706
1715
  ),
1707
- ),
1708
1716
  );
1709
1717
  },
1710
1718
  );
@@ -1741,105 +1749,97 @@ class _AgentSwitcherMenuItem extends StatelessWidget {
1741
1749
  child: Tooltip(
1742
1750
  message: agent.displayName,
1743
1751
  child: InkWell(
1744
- borderRadius: BorderRadius.circular(18),
1745
- onTap: onTap,
1746
- child: Ink(
1747
- decoration: BoxDecoration(
1748
- borderRadius: BorderRadius.circular(18),
1749
- gradient: selected
1750
- ? LinearGradient(
1751
- colors: <Color>[
1752
- _accent.withValues(alpha: 0.18),
1753
- _accentMuted.withValues(alpha: 0.3),
1754
- ],
1755
- begin: Alignment.topLeft,
1756
- end: Alignment.bottomRight,
1757
- )
1758
- : null,
1759
- color: selected ? null : Colors.white.withValues(alpha: 0.025),
1760
- border: Border.all(
1761
- color: selected ? _accent.withValues(alpha: 0.5) : _borderLight,
1752
+ borderRadius: BorderRadius.circular(14),
1753
+ onTap: onTap,
1754
+ child: Ink(
1755
+ decoration: BoxDecoration(
1756
+ borderRadius: BorderRadius.circular(14),
1757
+ color: selected ? _accent.withValues(alpha: 0.10) : _bgSecondary,
1758
+ border: Border.all(
1759
+ color: selected
1760
+ ? _accent.withValues(alpha: 0.55)
1761
+ : _borderLight,
1762
+ ),
1762
1763
  ),
1763
- ),
1764
- child: Padding(
1765
- padding: const EdgeInsets.fromLTRB(12, 12, 12, 12),
1766
- child: Row(
1767
- crossAxisAlignment: CrossAxisAlignment.start,
1768
- children: <Widget>[
1769
- _AgentGlyph(agent: agent, selected: selected, compact: true),
1770
- const SizedBox(width: 12),
1771
- Expanded(
1772
- child: Column(
1773
- crossAxisAlignment: CrossAxisAlignment.start,
1774
- children: <Widget>[
1775
- Row(
1776
- children: <Widget>[
1777
- Expanded(
1778
- child: Text(
1779
- agent.displayName,
1780
- maxLines: 1,
1781
- overflow: TextOverflow.ellipsis,
1782
- style: TextStyle(
1783
- color: _textPrimary,
1784
- fontSize: 13.5,
1785
- fontWeight: selected
1786
- ? FontWeight.w700
1787
- : FontWeight.w600,
1788
- letterSpacing: -0.1,
1764
+ child: Padding(
1765
+ padding: const EdgeInsets.fromLTRB(12, 12, 12, 12),
1766
+ child: Row(
1767
+ crossAxisAlignment: CrossAxisAlignment.start,
1768
+ children: <Widget>[
1769
+ _AgentGlyph(agent: agent, selected: selected, compact: true),
1770
+ const SizedBox(width: 12),
1771
+ Expanded(
1772
+ child: Column(
1773
+ crossAxisAlignment: CrossAxisAlignment.start,
1774
+ children: <Widget>[
1775
+ Row(
1776
+ children: <Widget>[
1777
+ Expanded(
1778
+ child: Text(
1779
+ agent.displayName,
1780
+ maxLines: 1,
1781
+ overflow: TextOverflow.ellipsis,
1782
+ style: TextStyle(
1783
+ color: _textPrimary,
1784
+ fontSize: 13.5,
1785
+ fontWeight: selected
1786
+ ? FontWeight.w700
1787
+ : FontWeight.w600,
1788
+ letterSpacing: -0.1,
1789
+ ),
1789
1790
  ),
1790
1791
  ),
1791
- ),
1792
- if (agent.isDefault) ...<Widget>[
1793
- const SizedBox(width: 8),
1794
- _AgentTag(
1795
- label: 'DEFAULT',
1796
- color: _accent,
1797
- foreground: _accentHover,
1798
- ),
1792
+ if (agent.isDefault) ...<Widget>[
1793
+ const SizedBox(width: 8),
1794
+ _AgentTag(
1795
+ label: 'DEFAULT',
1796
+ color: _accent,
1797
+ foreground: _accentHover,
1798
+ ),
1799
+ ],
1799
1800
  ],
1800
- ],
1801
- ),
1802
- const SizedBox(height: 4),
1803
- Text(
1804
- _agentSwitcherSubtitle(agent),
1805
- maxLines: 2,
1806
- overflow: TextOverflow.ellipsis,
1807
- style: TextStyle(
1808
- color: _textSecondary,
1809
- fontSize: 12,
1810
- height: 1.3,
1811
- fontWeight: FontWeight.w600,
1812
1801
  ),
1813
- ),
1814
- ],
1802
+ const SizedBox(height: 4),
1803
+ Text(
1804
+ _agentSwitcherSubtitle(agent),
1805
+ maxLines: 2,
1806
+ overflow: TextOverflow.ellipsis,
1807
+ style: TextStyle(
1808
+ color: _textSecondary,
1809
+ fontSize: 12,
1810
+ height: 1.3,
1811
+ fontWeight: FontWeight.w600,
1812
+ ),
1813
+ ),
1814
+ ],
1815
+ ),
1815
1816
  ),
1816
- ),
1817
- const SizedBox(width: 10),
1818
- AnimatedOpacity(
1819
- duration: const Duration(milliseconds: 140),
1820
- opacity: selected ? 1 : 0,
1821
- child: Container(
1822
- width: 24,
1823
- height: 24,
1824
- decoration: BoxDecoration(
1825
- shape: BoxShape.circle,
1826
- color: _accent.withValues(alpha: 0.2),
1827
- border: Border.all(
1828
- color: _accent.withValues(alpha: 0.45),
1817
+ const SizedBox(width: 10),
1818
+ AnimatedOpacity(
1819
+ duration: const Duration(milliseconds: 140),
1820
+ opacity: selected ? 1 : 0,
1821
+ child: Container(
1822
+ width: 24,
1823
+ height: 24,
1824
+ decoration: BoxDecoration(
1825
+ shape: BoxShape.circle,
1826
+ color: _accent.withValues(alpha: 0.2),
1827
+ border: Border.all(
1828
+ color: _accent.withValues(alpha: 0.45),
1829
+ ),
1830
+ ),
1831
+ child: Icon(
1832
+ Icons.check_rounded,
1833
+ size: 15,
1834
+ color: _accentHover,
1829
1835
  ),
1830
- ),
1831
- child: Icon(
1832
- Icons.check_rounded,
1833
- size: 15,
1834
- color: _accentHover,
1835
1836
  ),
1836
1837
  ),
1837
- ),
1838
- ],
1838
+ ],
1839
+ ),
1839
1840
  ),
1840
1841
  ),
1841
1842
  ),
1842
- ),
1843
1843
  ),
1844
1844
  );
1845
1845
  }
@@ -1860,12 +1860,12 @@ class _AgentGlyph extends StatelessWidget {
1860
1860
  Widget build(BuildContext context) {
1861
1861
  final baseColor = agent.isDefault ? _accent : _accentAlt;
1862
1862
  final initials = _agentInitials(agent.displayName);
1863
- final size = compact ? 42.0 : 44.0;
1863
+ final size = compact ? 38.0 : 42.0;
1864
1864
  return Container(
1865
1865
  width: size,
1866
1866
  height: size,
1867
1867
  decoration: BoxDecoration(
1868
- shape: BoxShape.circle,
1868
+ borderRadius: BorderRadius.circular(compact ? 11 : 12),
1869
1869
  gradient: LinearGradient(
1870
1870
  colors: <Color>[
1871
1871
  baseColor.withValues(alpha: selected ? 0.85 : 0.65),
@@ -1926,9 +1926,9 @@ class _AgentTag extends StatelessWidget {
1926
1926
  @override
1927
1927
  Widget build(BuildContext context) {
1928
1928
  return Container(
1929
- padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 4),
1929
+ padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
1930
1930
  decoration: BoxDecoration(
1931
- borderRadius: BorderRadius.circular(999),
1931
+ borderRadius: BorderRadius.circular(5),
1932
1932
  color: color.withValues(alpha: 0.14),
1933
1933
  border: Border.all(color: color.withValues(alpha: 0.3)),
1934
1934
  ),
@@ -1936,9 +1936,9 @@ class _AgentTag extends StatelessWidget {
1936
1936
  label,
1937
1937
  style: TextStyle(
1938
1938
  color: foreground,
1939
- fontSize: 9.5,
1940
- fontWeight: FontWeight.w800,
1941
- letterSpacing: 0.6,
1939
+ fontSize: 9,
1940
+ fontWeight: FontWeight.w600,
1941
+ letterSpacing: 1,
1942
1942
  ),
1943
1943
  ),
1944
1944
  );
@@ -2021,6 +2021,45 @@ class _ProfileSettingsButton extends StatelessWidget {
2021
2021
  }
2022
2022
  }
2023
2023
 
2024
+ class _SidebarAccountAvatar extends StatelessWidget {
2025
+ const _SidebarAccountAvatar({required this.controller, required this.onTap});
2026
+
2027
+ final NeoAgentController controller;
2028
+ final VoidCallback onTap;
2029
+
2030
+ @override
2031
+ Widget build(BuildContext context) {
2032
+ final label = controller.accountLabel.trim();
2033
+ final initial = label.isEmpty ? 'N' : label.characters.first.toUpperCase();
2034
+ final active = controller.selectedSection == AppSection.accountSettings;
2035
+ return Tooltip(
2036
+ message: 'Account settings',
2037
+ child: InkWell(
2038
+ borderRadius: BorderRadius.circular(999),
2039
+ onTap: onTap,
2040
+ child: Container(
2041
+ width: 30,
2042
+ height: 30,
2043
+ decoration: BoxDecoration(
2044
+ shape: BoxShape.circle,
2045
+ color: active ? _accentMuted : _bgCard,
2046
+ border: Border.all(color: active ? _accent : _borderLight),
2047
+ ),
2048
+ alignment: Alignment.center,
2049
+ child: Text(
2050
+ initial,
2051
+ style: TextStyle(
2052
+ color: active ? _accentHover : _textPrimary,
2053
+ fontWeight: FontWeight.w700,
2054
+ fontSize: 11,
2055
+ ),
2056
+ ),
2057
+ ),
2058
+ ),
2059
+ );
2060
+ }
2061
+ }
2062
+
2024
2063
  class _MobileDrawer extends StatelessWidget {
2025
2064
  const _MobileDrawer({
2026
2065
  required this.controller,
@@ -2137,8 +2176,6 @@ class _SectionBody extends StatelessWidget {
2137
2176
  return SettingsWorkspacePanel(controller: controller);
2138
2177
  case AppSection.accountSettings:
2139
2178
  return SettingsWorkspacePanel(controller: controller);
2140
- case AppSection.logs:
2141
- return RunsAndLogsPanel(controller: controller);
2142
2179
  case AppSection.skills:
2143
2180
  return ToolsPanel(controller: controller);
2144
2181
  case AppSection.agents: