neoagent 2.3.1-beta.62 → 2.3.1-beta.64
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 +9 -0
- package/docs/configuration.md +11 -0
- package/flutter_app/lib/main.dart +2 -0
- package/flutter_app/lib/main_account_settings.dart +50 -22
- package/flutter_app/lib/main_admin.dart +24 -10
- package/flutter_app/lib/main_app_shell.dart +10 -9
- package/flutter_app/lib/main_chat.dart +433 -309
- package/flutter_app/lib/main_controller.dart +164 -6
- package/flutter_app/lib/main_integrations.dart +15 -7
- package/flutter_app/lib/main_models.dart +116 -7
- package/flutter_app/lib/main_navigation.dart +27 -18
- package/flutter_app/lib/main_operations.dart +162 -91
- package/flutter_app/lib/main_runtime.dart +22 -0
- package/flutter_app/lib/main_settings.dart +287 -75
- package/flutter_app/lib/main_shared.dart +165 -6
- package/flutter_app/lib/main_unified.dart +388 -0
- package/flutter_app/lib/src/analytics_service.dart +294 -0
- package/flutter_app/lib/src/backend_client.dart +4 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +2 -0
- package/flutter_app/pubspec.lock +8 -0
- package/flutter_app/pubspec.yaml +1 -0
- package/flutter_app/web/index.html +1 -0
- package/package.json +1 -1
- package/server/config/analytics.js +30 -0
- package/server/db/database.js +52 -0
- package/server/http/routes.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/NOTICES +183 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/assets/packages/mixpanel_flutter/assets/mixpanel.js +3 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/index.html +1 -0
- package/server/public/main.dart.js +83685 -82046
- package/server/routes/agents.js +2 -1
- package/server/routes/memory.js +75 -3
- package/server/routes/runtime.js +14 -0
- package/server/routes/widgets.js +4 -4
- package/server/services/ai/engine.js +86 -0
- package/server/services/ai/runEvents.js +100 -0
- package/server/services/memory/manager.js +242 -26
- package/server/services/websocket.js +3 -1
- package/server/services/widgets/focus_widget.js +126 -0
- package/server/services/widgets/service.js +130 -2
|
@@ -32,7 +32,7 @@ enum AppSection {
|
|
|
32
32
|
health,
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
enum SidebarGroup { chat, recordings,
|
|
35
|
+
enum SidebarGroup { chat, recordings, automation, settings }
|
|
36
36
|
|
|
37
37
|
extension SidebarGroupX on SidebarGroup {
|
|
38
38
|
String get label {
|
|
@@ -41,8 +41,6 @@ extension SidebarGroupX on SidebarGroup {
|
|
|
41
41
|
return 'Chat';
|
|
42
42
|
case SidebarGroup.recordings:
|
|
43
43
|
return 'Recordings';
|
|
44
|
-
case SidebarGroup.activity:
|
|
45
|
-
return 'Activity';
|
|
46
44
|
case SidebarGroup.automation:
|
|
47
45
|
return 'Automation';
|
|
48
46
|
case SidebarGroup.settings:
|
|
@@ -56,8 +54,6 @@ extension SidebarGroupX on SidebarGroup {
|
|
|
56
54
|
return Icons.chat_bubble_outline;
|
|
57
55
|
case SidebarGroup.recordings:
|
|
58
56
|
return Icons.fiber_smart_record_outlined;
|
|
59
|
-
case SidebarGroup.activity:
|
|
60
|
-
return Icons.insights_outlined;
|
|
61
57
|
case SidebarGroup.automation:
|
|
62
58
|
return Icons.auto_awesome_outlined;
|
|
63
59
|
case SidebarGroup.settings:
|
|
@@ -80,7 +76,7 @@ extension AppSectionX on AppSection {
|
|
|
80
76
|
case AppSection.messaging:
|
|
81
77
|
return 'Messaging';
|
|
82
78
|
case AppSection.runs:
|
|
83
|
-
return 'Runs';
|
|
79
|
+
return 'Runs & Logs';
|
|
84
80
|
case AppSection.settings:
|
|
85
81
|
return 'Settings';
|
|
86
82
|
case AppSection.accountSettings:
|
|
@@ -92,7 +88,7 @@ extension AppSectionX on AppSection {
|
|
|
92
88
|
case AppSection.agents:
|
|
93
89
|
return 'Agents';
|
|
94
90
|
case AppSection.integrations:
|
|
95
|
-
return '
|
|
91
|
+
return 'Tools';
|
|
96
92
|
case AppSection.memory:
|
|
97
93
|
return 'Memory';
|
|
98
94
|
case AppSection.tasks:
|
|
@@ -119,7 +115,7 @@ extension AppSectionX on AppSection {
|
|
|
119
115
|
case AppSection.messaging:
|
|
120
116
|
return Icons.forum_outlined;
|
|
121
117
|
case AppSection.runs:
|
|
122
|
-
return Icons.
|
|
118
|
+
return Icons.monitor_heart_outlined;
|
|
123
119
|
case AppSection.settings:
|
|
124
120
|
return Icons.tune;
|
|
125
121
|
case AppSection.accountSettings:
|
|
@@ -131,7 +127,7 @@ extension AppSectionX on AppSection {
|
|
|
131
127
|
case AppSection.agents:
|
|
132
128
|
return Icons.smart_toy_outlined;
|
|
133
129
|
case AppSection.integrations:
|
|
134
|
-
return Icons.
|
|
130
|
+
return Icons.handyman_outlined;
|
|
135
131
|
case AppSection.memory:
|
|
136
132
|
return Icons.psychology_outlined;
|
|
137
133
|
case AppSection.tasks:
|
|
@@ -152,9 +148,6 @@ extension AppSectionX on AppSection {
|
|
|
152
148
|
return SidebarGroup.chat;
|
|
153
149
|
case AppSection.recordings:
|
|
154
150
|
return SidebarGroup.recordings;
|
|
155
|
-
case AppSection.runs:
|
|
156
|
-
case AppSection.logs:
|
|
157
|
-
return SidebarGroup.activity;
|
|
158
151
|
case AppSection.devices:
|
|
159
152
|
case AppSection.skills:
|
|
160
153
|
case AppSection.integrations:
|
|
@@ -164,6 +157,8 @@ extension AppSectionX on AppSection {
|
|
|
164
157
|
case AppSection.mcp:
|
|
165
158
|
case AppSection.health:
|
|
166
159
|
return SidebarGroup.automation;
|
|
160
|
+
case AppSection.runs:
|
|
161
|
+
case AppSection.logs:
|
|
167
162
|
case AppSection.settings:
|
|
168
163
|
case AppSection.accountSettings:
|
|
169
164
|
case AppSection.messaging:
|
|
@@ -172,17 +167,31 @@ extension AppSectionX on AppSection {
|
|
|
172
167
|
}
|
|
173
168
|
}
|
|
174
169
|
|
|
170
|
+
AppSection get canonicalSection {
|
|
171
|
+
switch (this) {
|
|
172
|
+
case AppSection.logs:
|
|
173
|
+
return AppSection.runs;
|
|
174
|
+
case AppSection.skills:
|
|
175
|
+
case AppSection.mcp:
|
|
176
|
+
return AppSection.integrations;
|
|
177
|
+
default:
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
175
182
|
String get navigationTitle {
|
|
176
|
-
final
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
final effectiveSection = canonicalSection;
|
|
184
|
+
final groupLabel = effectiveSection.group.label;
|
|
185
|
+
if (effectiveSection == AppSection.voiceAssistant) {
|
|
186
|
+
return effectiveSection.label;
|
|
179
187
|
}
|
|
180
|
-
if (group == SidebarGroup.chat ||
|
|
188
|
+
if (effectiveSection.group == SidebarGroup.chat ||
|
|
189
|
+
effectiveSection.group == SidebarGroup.recordings) {
|
|
181
190
|
return groupLabel;
|
|
182
191
|
}
|
|
183
|
-
if (groupLabel == label) {
|
|
192
|
+
if (groupLabel == effectiveSection.label) {
|
|
184
193
|
return groupLabel;
|
|
185
194
|
}
|
|
186
|
-
return '$groupLabel · $label';
|
|
195
|
+
return '$groupLabel · ${effectiveSection.label}';
|
|
187
196
|
}
|
|
188
197
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
part of 'main.dart';
|
|
2
2
|
|
|
3
3
|
class LogsPanel extends StatefulWidget {
|
|
4
|
-
const LogsPanel({super.key, required this.controller});
|
|
4
|
+
const LogsPanel({super.key, required this.controller, this.embedded = false});
|
|
5
5
|
|
|
6
6
|
final NeoAgentController controller;
|
|
7
|
+
final bool embedded;
|
|
7
8
|
|
|
8
9
|
@override
|
|
9
10
|
State<LogsPanel> createState() => _LogsPanelState();
|
|
@@ -350,46 +351,89 @@ class _LogsPanelState extends State<LogsPanel> {
|
|
|
350
351
|
@override
|
|
351
352
|
Widget build(BuildContext context) {
|
|
352
353
|
return ListView(
|
|
353
|
-
padding: _pagePadding(context),
|
|
354
|
+
padding: widget.embedded ? EdgeInsets.zero : _pagePadding(context),
|
|
354
355
|
children: <Widget>[
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
356
|
+
if (!widget.embedded)
|
|
357
|
+
_PageTitle(
|
|
358
|
+
title: 'Logs',
|
|
359
|
+
subtitle:
|
|
360
|
+
'Merged server and Flutter runtime logs for this app session.',
|
|
361
|
+
trailing: Wrap(
|
|
362
|
+
spacing: 12,
|
|
363
|
+
runSpacing: 12,
|
|
364
|
+
children: <Widget>[
|
|
365
|
+
OutlinedButton.icon(
|
|
366
|
+
onPressed: _isExportingRecentMessages
|
|
367
|
+
? null
|
|
368
|
+
: _exportRecentMessages,
|
|
369
|
+
icon: _isExportingRecentMessages
|
|
370
|
+
? const SizedBox.square(
|
|
371
|
+
dimension: 16,
|
|
372
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
373
|
+
)
|
|
374
|
+
: Icon(Icons.ios_share_outlined),
|
|
375
|
+
label: Text('Export last 5 messages'),
|
|
376
|
+
),
|
|
377
|
+
OutlinedButton.icon(
|
|
378
|
+
onPressed: _copyDebugInfo,
|
|
379
|
+
icon: Icon(Icons.bug_report_outlined),
|
|
380
|
+
label: Text('Copy debug info'),
|
|
381
|
+
),
|
|
382
|
+
OutlinedButton.icon(
|
|
383
|
+
onPressed: widget.controller.logs.isEmpty ? null : _copyLogs,
|
|
384
|
+
icon: Icon(Icons.copy_all_outlined),
|
|
385
|
+
label: Text('Copy logs'),
|
|
386
|
+
),
|
|
387
|
+
OutlinedButton.icon(
|
|
388
|
+
onPressed: widget.controller.clearLogs,
|
|
389
|
+
icon: Icon(Icons.clear_all),
|
|
390
|
+
label: Text('Clear'),
|
|
391
|
+
),
|
|
392
|
+
],
|
|
393
|
+
),
|
|
394
|
+
)
|
|
395
|
+
else
|
|
396
|
+
Align(
|
|
397
|
+
alignment: Alignment.centerRight,
|
|
398
|
+
child: Padding(
|
|
399
|
+
padding: const EdgeInsets.only(bottom: 12),
|
|
400
|
+
child: Wrap(
|
|
401
|
+
spacing: 12,
|
|
402
|
+
runSpacing: 12,
|
|
403
|
+
children: <Widget>[
|
|
404
|
+
OutlinedButton.icon(
|
|
405
|
+
onPressed: _isExportingRecentMessages
|
|
406
|
+
? null
|
|
407
|
+
: _exportRecentMessages,
|
|
408
|
+
icon: _isExportingRecentMessages
|
|
409
|
+
? const SizedBox.square(
|
|
410
|
+
dimension: 16,
|
|
411
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
412
|
+
)
|
|
413
|
+
: const Icon(Icons.ios_share_outlined),
|
|
414
|
+
label: const Text('Export last 5 messages'),
|
|
415
|
+
),
|
|
416
|
+
OutlinedButton.icon(
|
|
417
|
+
onPressed: _copyDebugInfo,
|
|
418
|
+
icon: const Icon(Icons.bug_report_outlined),
|
|
419
|
+
label: const Text('Copy debug info'),
|
|
420
|
+
),
|
|
421
|
+
OutlinedButton.icon(
|
|
422
|
+
onPressed: widget.controller.logs.isEmpty
|
|
423
|
+
? null
|
|
424
|
+
: _copyLogs,
|
|
425
|
+
icon: const Icon(Icons.copy_all_outlined),
|
|
426
|
+
label: const Text('Copy logs'),
|
|
427
|
+
),
|
|
428
|
+
OutlinedButton.icon(
|
|
429
|
+
onPressed: widget.controller.clearLogs,
|
|
430
|
+
icon: const Icon(Icons.clear_all),
|
|
431
|
+
label: const Text('Clear'),
|
|
432
|
+
),
|
|
433
|
+
],
|
|
389
434
|
),
|
|
390
|
-
|
|
435
|
+
),
|
|
391
436
|
),
|
|
392
|
-
),
|
|
393
437
|
Card(
|
|
394
438
|
child: Padding(
|
|
395
439
|
padding: const EdgeInsets.all(16),
|
|
@@ -440,9 +484,14 @@ class _LogsPanelState extends State<LogsPanel> {
|
|
|
440
484
|
}
|
|
441
485
|
|
|
442
486
|
class SkillsPanel extends StatefulWidget {
|
|
443
|
-
const SkillsPanel({
|
|
487
|
+
const SkillsPanel({
|
|
488
|
+
super.key,
|
|
489
|
+
required this.controller,
|
|
490
|
+
this.embedded = false,
|
|
491
|
+
});
|
|
444
492
|
|
|
445
493
|
final NeoAgentController controller;
|
|
494
|
+
final bool embedded;
|
|
446
495
|
|
|
447
496
|
@override
|
|
448
497
|
State<SkillsPanel> createState() => _SkillsPanelState();
|
|
@@ -493,10 +542,9 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
493
542
|
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
|
494
543
|
});
|
|
495
544
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
children: <Widget>[
|
|
545
|
+
final body = Column(
|
|
546
|
+
children: <Widget>[
|
|
547
|
+
if (!widget.embedded)
|
|
500
548
|
_PageTitle(
|
|
501
549
|
title: 'Skills',
|
|
502
550
|
subtitle:
|
|
@@ -506,38 +554,53 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
506
554
|
icon: Icon(Icons.add),
|
|
507
555
|
label: Text('New Skill'),
|
|
508
556
|
),
|
|
509
|
-
)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
indicatorSize: TabBarIndicatorSize.tab,
|
|
521
|
-
labelStyle: TextStyle(fontWeight: FontWeight.w700),
|
|
522
|
-
tabs: <Widget>[
|
|
523
|
-
Tab(text: 'Installed Skills (${controller.skills.length})'),
|
|
524
|
-
Tab(text: 'Store (${filteredStore.length})'),
|
|
525
|
-
],
|
|
557
|
+
)
|
|
558
|
+
else
|
|
559
|
+
Align(
|
|
560
|
+
alignment: Alignment.centerRight,
|
|
561
|
+
child: Padding(
|
|
562
|
+
padding: const EdgeInsets.only(bottom: 12),
|
|
563
|
+
child: FilledButton.icon(
|
|
564
|
+
onPressed: () => _openCreateSkill(context),
|
|
565
|
+
icon: const Icon(Icons.add),
|
|
566
|
+
label: const Text('New Skill'),
|
|
567
|
+
),
|
|
526
568
|
),
|
|
527
569
|
),
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
_buildStoreTab(controller, categories, filteredStore),
|
|
535
|
-
],
|
|
536
|
-
),
|
|
570
|
+
if (!widget.embedded) const SizedBox(height: 12),
|
|
571
|
+
Container(
|
|
572
|
+
decoration: BoxDecoration(
|
|
573
|
+
color: _bgSecondary,
|
|
574
|
+
borderRadius: BorderRadius.circular(14),
|
|
575
|
+
border: Border.all(color: _border),
|
|
537
576
|
),
|
|
538
|
-
|
|
539
|
-
|
|
577
|
+
child: TabBar(
|
|
578
|
+
controller: _tabController,
|
|
579
|
+
dividerColor: Colors.transparent,
|
|
580
|
+
indicatorSize: TabBarIndicatorSize.tab,
|
|
581
|
+
labelStyle: TextStyle(fontWeight: FontWeight.w700),
|
|
582
|
+
tabs: <Widget>[
|
|
583
|
+
Tab(text: 'Installed Skills (${controller.skills.length})'),
|
|
584
|
+
Tab(text: 'Store (${filteredStore.length})'),
|
|
585
|
+
],
|
|
586
|
+
),
|
|
587
|
+
),
|
|
588
|
+
const SizedBox(height: 12),
|
|
589
|
+
Expanded(
|
|
590
|
+
child: TabBarView(
|
|
591
|
+
controller: _tabController,
|
|
592
|
+
children: <Widget>[
|
|
593
|
+
_buildInstalledTab(controller),
|
|
594
|
+
_buildStoreTab(controller, categories, filteredStore),
|
|
595
|
+
],
|
|
596
|
+
),
|
|
597
|
+
),
|
|
598
|
+
],
|
|
540
599
|
);
|
|
600
|
+
if (widget.embedded) {
|
|
601
|
+
return body;
|
|
602
|
+
}
|
|
603
|
+
return Padding(padding: _pagePadding(context), child: body);
|
|
541
604
|
}
|
|
542
605
|
|
|
543
606
|
Widget _buildInstalledTab(NeoAgentController controller) {
|
|
@@ -1985,16 +2048,18 @@ class WidgetsPanel extends StatelessWidget {
|
|
|
1985
2048
|
spacing: 10,
|
|
1986
2049
|
runSpacing: 10,
|
|
1987
2050
|
children: <Widget>[
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2051
|
+
if (!item.isSystem)
|
|
2052
|
+
OutlinedButton(
|
|
2053
|
+
onPressed: () =>
|
|
2054
|
+
controller.openWidgetEditFlow(item),
|
|
2055
|
+
child: Text('Edit With AI'),
|
|
2056
|
+
),
|
|
2057
|
+
if (!item.isSystem)
|
|
2058
|
+
OutlinedButton(
|
|
2059
|
+
onPressed: () =>
|
|
2060
|
+
controller.toggleWidgetEnabled(item),
|
|
2061
|
+
child: Text(item.enabled ? 'Pause' : 'Enable'),
|
|
2062
|
+
),
|
|
1998
2063
|
FilledButton(
|
|
1999
2064
|
onPressed: remaining > 0
|
|
2000
2065
|
? null
|
|
@@ -2003,16 +2068,18 @@ class WidgetsPanel extends StatelessWidget {
|
|
|
2003
2068
|
_manualRunButtonLabel('Run Now', remaining),
|
|
2004
2069
|
),
|
|
2005
2070
|
),
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2071
|
+
if (!item.isSystem)
|
|
2072
|
+
OutlinedButton(
|
|
2073
|
+
onPressed: () => _confirmDelete(
|
|
2074
|
+
context,
|
|
2075
|
+
title: 'Delete widget?',
|
|
2076
|
+
message:
|
|
2077
|
+
'This removes "${item.name}" and its refresh job.',
|
|
2078
|
+
onConfirm: () =>
|
|
2079
|
+
controller.deleteWidget(item.id),
|
|
2080
|
+
),
|
|
2081
|
+
child: Text('Delete'),
|
|
2013
2082
|
),
|
|
2014
|
-
child: Text('Delete'),
|
|
2015
|
-
),
|
|
2016
2083
|
],
|
|
2017
2084
|
),
|
|
2018
2085
|
),
|
|
@@ -2185,7 +2252,11 @@ class _AiWidgetCardState extends State<_AiWidgetCard> {
|
|
|
2185
2252
|
),
|
|
2186
2253
|
const SizedBox(width: 12),
|
|
2187
2254
|
_StatusPill(
|
|
2188
|
-
label: item.
|
|
2255
|
+
label: item.isSystem
|
|
2256
|
+
? (item.enabled
|
|
2257
|
+
? 'System live'
|
|
2258
|
+
: 'System paused')
|
|
2259
|
+
: (item.enabled ? 'Live' : 'Paused'),
|
|
2189
2260
|
color: item.enabled ? _success : _textSecondary,
|
|
2190
2261
|
),
|
|
2191
2262
|
],
|
|
@@ -627,6 +627,28 @@ class _NeoAgentAppState extends State<NeoAgentApp>
|
|
|
627
627
|
),
|
|
628
628
|
),
|
|
629
629
|
),
|
|
630
|
+
if (!_desktopToolbarWindowMode &&
|
|
631
|
+
!_desktopAssistantPopupWindowMode &&
|
|
632
|
+
_controller.showAnalyticsConsentBanner)
|
|
633
|
+
Positioned(
|
|
634
|
+
left: 0,
|
|
635
|
+
right: 0,
|
|
636
|
+
bottom: 0,
|
|
637
|
+
child: SafeArea(
|
|
638
|
+
top: false,
|
|
639
|
+
child: Padding(
|
|
640
|
+
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
|
|
641
|
+
child: Center(
|
|
642
|
+
child: ConstrainedBox(
|
|
643
|
+
constraints: const BoxConstraints(maxWidth: 980),
|
|
644
|
+
child: _GlobalAnalyticsConsentBanner(
|
|
645
|
+
controller: _controller,
|
|
646
|
+
),
|
|
647
|
+
),
|
|
648
|
+
),
|
|
649
|
+
),
|
|
650
|
+
),
|
|
651
|
+
),
|
|
630
652
|
if (_supportsDesktopShell &&
|
|
631
653
|
!_desktopToolbarWindowMode &&
|
|
632
654
|
!_desktopAssistantPopupWindowMode)
|