neoagent 2.3.1-beta.57 → 2.3.1-beta.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +569 -0
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +125 -230
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +183 -248
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +3 -5
- package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +140 -107
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +32 -150
- package/flutter_app/lib/main_app_shell.dart +439 -19
- package/flutter_app/lib/main_controller.dart +12 -6
- package/flutter_app/lib/main_devices.dart +204 -109
- package/flutter_app/lib/main_settings.dart +16 -77
- package/flutter_app/lib/main_shared.dart +1 -0
- package/package.json +1 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +75022 -74341
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import 'package:flutter/material.dart';
|
|
2
2
|
import 'package:flutter_animate/flutter_animate.dart';
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import '../../main.dart';
|
|
5
|
+
import 'onboarding_chrome.dart';
|
|
5
6
|
|
|
6
7
|
class OnboardingModelStep extends StatefulWidget {
|
|
7
8
|
const OnboardingModelStep({
|
|
@@ -20,15 +21,16 @@ class OnboardingModelStep extends StatefulWidget {
|
|
|
20
21
|
class _OnboardingModelStepState extends State<OnboardingModelStep> {
|
|
21
22
|
String? _selectedModel;
|
|
22
23
|
|
|
23
|
-
List<ModelMeta> get _models
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
List<ModelMeta> get _models => widget.controller.supportedModels
|
|
25
|
+
.where((m) => m.available && !m.id.startsWith('tool:'))
|
|
26
|
+
.toList();
|
|
26
27
|
|
|
27
28
|
@override
|
|
28
29
|
void initState() {
|
|
29
30
|
super.initState();
|
|
30
31
|
final defaultChatModel = widget.controller.defaultChatModel;
|
|
31
|
-
if (defaultChatModel.isNotEmpty &&
|
|
32
|
+
if (defaultChatModel.isNotEmpty &&
|
|
33
|
+
_models.any((m) => m.id == defaultChatModel)) {
|
|
32
34
|
_selectedModel = defaultChatModel;
|
|
33
35
|
} else if (_models.isNotEmpty) {
|
|
34
36
|
_selectedModel = _models.first.id;
|
|
@@ -36,263 +38,196 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
Color _getColorForModel(ModelMeta model) {
|
|
39
|
-
final
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
final value = model.provider.toLowerCase() + model.id.toLowerCase();
|
|
42
|
+
if (value.contains('google') || value.contains('gemini')) {
|
|
43
|
+
return const Color(0xFF4285F4);
|
|
44
|
+
}
|
|
45
|
+
if (value.contains('openai') || value.contains('gpt')) {
|
|
46
|
+
return const Color(0xFF10A37F);
|
|
47
|
+
}
|
|
48
|
+
if (value.contains('anthropic') || value.contains('claude')) {
|
|
49
|
+
return const Color(0xFFD97757);
|
|
50
|
+
}
|
|
51
|
+
if (value.contains('meta') || value.contains('llama')) {
|
|
52
|
+
return const Color(0xFF0668E1);
|
|
53
|
+
}
|
|
54
|
+
if (value.contains('mistral')) return const Color(0xFFF97316);
|
|
55
|
+
return const Color(0xFF7C8CFF);
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
IconData _getIconForModel(ModelMeta model) {
|
|
49
|
-
final
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
59
|
+
final value = model.provider.toLowerCase() + model.id.toLowerCase();
|
|
60
|
+
if (value.contains('google') || value.contains('gemini')) {
|
|
61
|
+
return Icons.auto_awesome_rounded;
|
|
62
|
+
}
|
|
63
|
+
if (value.contains('openai') || value.contains('gpt')) {
|
|
64
|
+
return Icons.bolt_rounded;
|
|
65
|
+
}
|
|
66
|
+
if (value.contains('anthropic') || value.contains('claude')) {
|
|
67
|
+
return Icons.menu_book_rounded;
|
|
68
|
+
}
|
|
69
|
+
if (value.contains('meta') || value.contains('llama')) {
|
|
70
|
+
return Icons.visibility_rounded;
|
|
71
|
+
}
|
|
54
72
|
return Icons.memory_rounded;
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
@override
|
|
58
76
|
Widget build(BuildContext context) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
fontSize: 18,
|
|
86
|
-
color: Colors.white.withValues(alpha: 0.7),
|
|
87
|
-
height: 1.5,
|
|
77
|
+
return OnboardingScaffold(
|
|
78
|
+
step: 3,
|
|
79
|
+
totalSteps: 4,
|
|
80
|
+
eyebrow: 'INTELLIGENCE',
|
|
81
|
+
title: 'Choose your\ndefault model.',
|
|
82
|
+
description: 'Pick the model NeoOS should use by default.',
|
|
83
|
+
footer: Row(
|
|
84
|
+
mainAxisAlignment: MainAxisAlignment.end,
|
|
85
|
+
children: <Widget>[
|
|
86
|
+
OnboardingPrimaryButton(
|
|
87
|
+
label: 'Finish setup',
|
|
88
|
+
icon: Icons.check_rounded,
|
|
89
|
+
onPressed: widget.onNext,
|
|
90
|
+
),
|
|
91
|
+
],
|
|
92
|
+
),
|
|
93
|
+
child: _models.isEmpty
|
|
94
|
+
? Center(
|
|
95
|
+
child: Text(
|
|
96
|
+
'No available models found.\nYou can configure providers later in Settings.',
|
|
97
|
+
textAlign: TextAlign.center,
|
|
98
|
+
style: TextStyle(
|
|
99
|
+
color: Colors.white.withValues(alpha: 0.6),
|
|
100
|
+
fontSize: 16,
|
|
101
|
+
height: 1.5,
|
|
102
|
+
),
|
|
88
103
|
),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
).animate().fadeIn()
|
|
107
|
-
: ListView.separated(
|
|
108
|
-
itemCount: _models.length,
|
|
109
|
-
separatorBuilder: (context, index) => const SizedBox(height: 16),
|
|
110
|
-
itemBuilder: (context, index) {
|
|
111
|
-
final model = _models[index];
|
|
112
|
-
final isSelected = _selectedModel == model.id;
|
|
113
|
-
|
|
114
|
-
return _ModelCard(
|
|
115
|
-
model: model,
|
|
116
|
-
color: _getColorForModel(model),
|
|
117
|
-
icon: _getIconForModel(model),
|
|
118
|
-
isSelected: isSelected,
|
|
119
|
-
onTap: () async {
|
|
120
|
-
final previousModel = _selectedModel;
|
|
121
|
-
setState(() {
|
|
122
|
-
_selectedModel = model.id;
|
|
123
|
-
});
|
|
124
|
-
try {
|
|
125
|
-
await widget.controller.saveSettingsPayload({'default_chat_model': model.id});
|
|
126
|
-
} catch (e) {
|
|
127
|
-
debugPrint('Failed to save model setting: $e');
|
|
128
|
-
if (context.mounted) {
|
|
129
|
-
ScaffoldMessenger.of(context).showSnackBar(
|
|
130
|
-
SnackBar(
|
|
131
|
-
content: Text('Failed to save selection: $e'),
|
|
132
|
-
backgroundColor: Colors.redAccent,
|
|
133
|
-
),
|
|
104
|
+
)
|
|
105
|
+
: ListView.separated(
|
|
106
|
+
itemCount: _models.length,
|
|
107
|
+
separatorBuilder: (_, __) => const SizedBox(height: 14),
|
|
108
|
+
itemBuilder: (context, index) {
|
|
109
|
+
final model = _models[index];
|
|
110
|
+
final color = _getColorForModel(model);
|
|
111
|
+
final selected = _selectedModel == model.id;
|
|
112
|
+
return OnboardingOptionCard(
|
|
113
|
+
selected: selected,
|
|
114
|
+
accent: color,
|
|
115
|
+
onTap: () async {
|
|
116
|
+
final previousModel = _selectedModel;
|
|
117
|
+
setState(() => _selectedModel = model.id);
|
|
118
|
+
try {
|
|
119
|
+
await widget.controller.saveSettingsPayload(
|
|
120
|
+
<String, Object?>{'default_chat_model': model.id},
|
|
134
121
|
);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if (context.mounted) {
|
|
124
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
125
|
+
SnackBar(
|
|
126
|
+
content: Text('Failed to save selection: $e'),
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
setState(() => _selectedModel = previousModel);
|
|
138
131
|
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
boxShadow: <BoxShadow>[
|
|
155
|
-
BoxShadow(
|
|
156
|
-
color: theme.colorScheme.primary.withValues(alpha: 0.4),
|
|
157
|
-
blurRadius: 30,
|
|
158
|
-
offset: const Offset(0, 10),
|
|
159
|
-
),
|
|
160
|
-
],
|
|
161
|
-
),
|
|
162
|
-
child: FilledButton.icon(
|
|
163
|
-
onPressed: widget.onNext,
|
|
164
|
-
style: FilledButton.styleFrom(
|
|
165
|
-
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
|
166
|
-
shape: RoundedRectangleBorder(
|
|
167
|
-
borderRadius: BorderRadius.circular(20),
|
|
168
|
-
),
|
|
169
|
-
),
|
|
170
|
-
icon: const Icon(Icons.check_rounded),
|
|
171
|
-
label: const Text(
|
|
172
|
-
'Finish Setup',
|
|
173
|
-
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
|
|
174
|
-
),
|
|
175
|
-
),
|
|
176
|
-
).animate().fadeIn(delay: 800.ms).slideY(begin: 0.5, end: 0),
|
|
177
|
-
],
|
|
178
|
-
),
|
|
179
|
-
],
|
|
180
|
-
),
|
|
181
|
-
),
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
class _ModelCard extends StatelessWidget {
|
|
187
|
-
const _ModelCard({
|
|
188
|
-
required this.model,
|
|
189
|
-
required this.color,
|
|
190
|
-
required this.icon,
|
|
191
|
-
required this.isSelected,
|
|
192
|
-
required this.onTap,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
final ModelMeta model;
|
|
196
|
-
final Color color;
|
|
197
|
-
final IconData icon;
|
|
198
|
-
final bool isSelected;
|
|
199
|
-
final VoidCallback onTap;
|
|
200
|
-
|
|
201
|
-
@override
|
|
202
|
-
Widget build(BuildContext context) {
|
|
203
|
-
|
|
204
|
-
return InkWell(
|
|
205
|
-
onTap: onTap,
|
|
206
|
-
borderRadius: BorderRadius.circular(24),
|
|
207
|
-
child: AnimatedContainer(
|
|
208
|
-
duration: const Duration(milliseconds: 300),
|
|
209
|
-
curve: Curves.easeOutCubic,
|
|
210
|
-
padding: const EdgeInsets.all(24),
|
|
211
|
-
decoration: BoxDecoration(
|
|
212
|
-
color: isSelected
|
|
213
|
-
? color.withValues(alpha: 0.15)
|
|
214
|
-
: Colors.white.withValues(alpha: 0.05),
|
|
215
|
-
borderRadius: BorderRadius.circular(24),
|
|
216
|
-
border: Border.all(
|
|
217
|
-
color: isSelected ? color : Colors.white.withValues(alpha: 0.1),
|
|
218
|
-
width: isSelected ? 2 : 1,
|
|
219
|
-
),
|
|
220
|
-
boxShadow: isSelected ? [
|
|
221
|
-
BoxShadow(
|
|
222
|
-
color: color.withValues(alpha: 0.15),
|
|
223
|
-
blurRadius: 20,
|
|
224
|
-
offset: const Offset(0, 8),
|
|
225
|
-
)
|
|
226
|
-
] : [],
|
|
227
|
-
),
|
|
228
|
-
child: Row(
|
|
229
|
-
children: <Widget>[
|
|
230
|
-
Container(
|
|
231
|
-
padding: const EdgeInsets.all(16),
|
|
232
|
-
decoration: BoxDecoration(
|
|
233
|
-
color: color.withValues(alpha: 0.2),
|
|
234
|
-
borderRadius: BorderRadius.circular(16),
|
|
235
|
-
),
|
|
236
|
-
child: Icon(
|
|
237
|
-
icon,
|
|
238
|
-
color: color,
|
|
239
|
-
size: 32,
|
|
240
|
-
),
|
|
241
|
-
),
|
|
242
|
-
const SizedBox(width: 20),
|
|
243
|
-
Expanded(
|
|
244
|
-
child: Column(
|
|
245
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
246
|
-
mainAxisSize: MainAxisSize.min,
|
|
247
|
-
children: [
|
|
248
|
-
Row(
|
|
249
|
-
children: [
|
|
250
|
-
Text(
|
|
251
|
-
model.label,
|
|
252
|
-
style: const TextStyle(
|
|
253
|
-
fontSize: 22,
|
|
254
|
-
fontWeight: FontWeight.w700,
|
|
255
|
-
color: Colors.white,
|
|
256
|
-
),
|
|
257
|
-
),
|
|
258
|
-
const SizedBox(width: 8),
|
|
259
|
-
Container(
|
|
260
|
-
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
261
|
-
decoration: BoxDecoration(
|
|
262
|
-
color: Colors.white.withValues(alpha: 0.1),
|
|
263
|
-
borderRadius: BorderRadius.circular(8),
|
|
264
|
-
),
|
|
265
|
-
child: Text(
|
|
266
|
-
model.provider,
|
|
267
|
-
style: TextStyle(
|
|
268
|
-
fontSize: 12,
|
|
269
|
-
fontWeight: FontWeight.w600,
|
|
270
|
-
color: Colors.white.withValues(alpha: 0.7),
|
|
132
|
+
},
|
|
133
|
+
child: Row(
|
|
134
|
+
children: <Widget>[
|
|
135
|
+
Container(
|
|
136
|
+
width: 58,
|
|
137
|
+
height: 58,
|
|
138
|
+
decoration: BoxDecoration(
|
|
139
|
+
color: color.withValues(alpha: 0.18),
|
|
140
|
+
borderRadius: BorderRadius.circular(18),
|
|
141
|
+
),
|
|
142
|
+
child: Icon(
|
|
143
|
+
_getIconForModel(model),
|
|
144
|
+
color: color,
|
|
145
|
+
size: 30,
|
|
146
|
+
),
|
|
271
147
|
),
|
|
272
|
-
|
|
148
|
+
const SizedBox(width: 18),
|
|
149
|
+
Expanded(
|
|
150
|
+
child: Column(
|
|
151
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
152
|
+
children: <Widget>[
|
|
153
|
+
Wrap(
|
|
154
|
+
spacing: 10,
|
|
155
|
+
runSpacing: 8,
|
|
156
|
+
crossAxisAlignment: WrapCrossAlignment.center,
|
|
157
|
+
children: <Widget>[
|
|
158
|
+
Text(
|
|
159
|
+
model.label,
|
|
160
|
+
style: const TextStyle(
|
|
161
|
+
color: Colors.white,
|
|
162
|
+
fontSize: 20,
|
|
163
|
+
fontWeight: FontWeight.w800,
|
|
164
|
+
),
|
|
165
|
+
),
|
|
166
|
+
Container(
|
|
167
|
+
padding: const EdgeInsets.symmetric(
|
|
168
|
+
horizontal: 10,
|
|
169
|
+
vertical: 5,
|
|
170
|
+
),
|
|
171
|
+
decoration: BoxDecoration(
|
|
172
|
+
color: Colors.white.withValues(
|
|
173
|
+
alpha: 0.08,
|
|
174
|
+
),
|
|
175
|
+
borderRadius: BorderRadius.circular(
|
|
176
|
+
999,
|
|
177
|
+
),
|
|
178
|
+
),
|
|
179
|
+
child: Text(
|
|
180
|
+
model.provider,
|
|
181
|
+
style: TextStyle(
|
|
182
|
+
color: Colors.white.withValues(
|
|
183
|
+
alpha: 0.72,
|
|
184
|
+
),
|
|
185
|
+
fontSize: 12,
|
|
186
|
+
fontWeight: FontWeight.w700,
|
|
187
|
+
),
|
|
188
|
+
),
|
|
189
|
+
),
|
|
190
|
+
],
|
|
191
|
+
),
|
|
192
|
+
const SizedBox(height: 6),
|
|
193
|
+
Text(
|
|
194
|
+
model.purpose,
|
|
195
|
+
style: TextStyle(
|
|
196
|
+
color: Colors.white.withValues(alpha: 0.68),
|
|
197
|
+
fontSize: 14,
|
|
198
|
+
height: 1.45,
|
|
199
|
+
),
|
|
200
|
+
),
|
|
201
|
+
],
|
|
202
|
+
),
|
|
203
|
+
),
|
|
204
|
+
AnimatedSwitcher(
|
|
205
|
+
duration: const Duration(milliseconds: 220),
|
|
206
|
+
child: selected
|
|
207
|
+
? Icon(
|
|
208
|
+
Icons.check_circle_rounded,
|
|
209
|
+
key: ValueKey<String>(model.id),
|
|
210
|
+
color: color,
|
|
211
|
+
size: 28,
|
|
212
|
+
)
|
|
213
|
+
: Icon(
|
|
214
|
+
Icons.radio_button_unchecked_rounded,
|
|
215
|
+
key: ValueKey<String>('idle-${model.id}'),
|
|
216
|
+
color: Colors.white.withValues(alpha: 0.25),
|
|
217
|
+
size: 28,
|
|
218
|
+
),
|
|
219
|
+
),
|
|
220
|
+
],
|
|
273
221
|
),
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
height: 1.4,
|
|
283
|
-
),
|
|
284
|
-
),
|
|
285
|
-
],
|
|
286
|
-
),
|
|
222
|
+
)
|
|
223
|
+
.animate()
|
|
224
|
+
.fadeIn(
|
|
225
|
+
duration: 420.ms,
|
|
226
|
+
delay: (180 + (index.clamp(0, 5) * 70)).ms,
|
|
227
|
+
)
|
|
228
|
+
.slideY(begin: 0.16, end: 0);
|
|
229
|
+
},
|
|
287
230
|
),
|
|
288
|
-
if (isSelected) ...[
|
|
289
|
-
const SizedBox(width: 16),
|
|
290
|
-
Icon(Icons.check_circle_rounded, color: color, size: 28)
|
|
291
|
-
.animate().scale(curve: Curves.elasticOut),
|
|
292
|
-
],
|
|
293
|
-
],
|
|
294
|
-
),
|
|
295
|
-
),
|
|
296
231
|
);
|
|
297
232
|
}
|
|
298
233
|
}
|
|
@@ -40,7 +40,8 @@ class _OnboardingShellState extends State<OnboardingShell> {
|
|
|
40
40
|
backgroundColor: Colors.black, // Deep black for video and transitions
|
|
41
41
|
body: PageView(
|
|
42
42
|
controller: _pageController,
|
|
43
|
-
physics:
|
|
43
|
+
physics:
|
|
44
|
+
const NeverScrollableScrollPhysics(), // Managed programmatically
|
|
44
45
|
children: <Widget>[
|
|
45
46
|
OnboardingVideoStep(onComplete: _nextStep),
|
|
46
47
|
OnboardingWelcomeStep(onNext: _nextStep),
|
|
@@ -48,10 +49,7 @@ class _OnboardingShellState extends State<OnboardingShell> {
|
|
|
48
49
|
onNext: _nextStep,
|
|
49
50
|
controller: widget.controller,
|
|
50
51
|
),
|
|
51
|
-
OnboardingModelStep(
|
|
52
|
-
onNext: _finish,
|
|
53
|
-
controller: widget.controller,
|
|
54
|
-
),
|
|
52
|
+
OnboardingModelStep(onNext: _finish, controller: widget.controller),
|
|
55
53
|
],
|
|
56
54
|
),
|
|
57
55
|
);
|