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,8 +1,12 @@
|
|
|
1
1
|
import 'dart:io' show Platform;
|
|
2
|
+
|
|
2
3
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
3
4
|
import 'package:flutter/material.dart';
|
|
5
|
+
import 'package:flutter_animate/flutter_animate.dart';
|
|
4
6
|
import 'package:video_player/video_player.dart';
|
|
5
7
|
|
|
8
|
+
import 'onboarding_chrome.dart';
|
|
9
|
+
|
|
6
10
|
class OnboardingVideoStep extends StatefulWidget {
|
|
7
11
|
const OnboardingVideoStep({super.key, required this.onComplete});
|
|
8
12
|
|
|
@@ -16,7 +20,6 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
|
|
|
16
20
|
VideoPlayerController? _controller;
|
|
17
21
|
bool _isInitialized = false;
|
|
18
22
|
bool _hasError = false;
|
|
19
|
-
|
|
20
23
|
bool _hasCompleted = false;
|
|
21
24
|
|
|
22
25
|
@override
|
|
@@ -26,11 +29,8 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
Future<void> _initVideo() async {
|
|
29
|
-
// video_player doesn't support Windows/Linux out of the box
|
|
30
32
|
if (!kIsWeb && (Platform.isWindows || Platform.isLinux)) {
|
|
31
|
-
setState(()
|
|
32
|
-
_hasError = true;
|
|
33
|
-
});
|
|
33
|
+
setState(() => _hasError = true);
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -40,26 +40,22 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
|
|
|
40
40
|
);
|
|
41
41
|
await _controller!.initialize();
|
|
42
42
|
if (!mounted) return;
|
|
43
|
-
_controller
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
_controller!
|
|
44
|
+
..setLooping(false)
|
|
45
|
+
..setVolume(1)
|
|
46
|
+
..addListener(_videoListener);
|
|
47
|
+
setState(() => _isInitialized = true);
|
|
47
48
|
await _controller!.play();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
setState(() {
|
|
53
|
-
_hasError = true;
|
|
54
|
-
});
|
|
49
|
+
} catch (_) {
|
|
50
|
+
if (mounted) {
|
|
51
|
+
setState(() => _hasError = true);
|
|
52
|
+
}
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
void _videoListener() {
|
|
59
57
|
if (!mounted || _hasCompleted) return;
|
|
60
58
|
if (_controller == null || !_controller!.value.isInitialized) return;
|
|
61
|
-
|
|
62
|
-
// When video reaches the end
|
|
63
59
|
if (_controller!.value.position >= _controller!.value.duration) {
|
|
64
60
|
_hasCompleted = true;
|
|
65
61
|
_controller!.removeListener(_videoListener);
|
|
@@ -76,110 +72,147 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
|
|
|
76
72
|
|
|
77
73
|
@override
|
|
78
74
|
Widget build(BuildContext context) {
|
|
75
|
+
final orientation = MediaQuery.orientationOf(context);
|
|
76
|
+
|
|
79
77
|
if (_hasError) {
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
return Scaffold(
|
|
79
|
+
backgroundColor: Colors.black,
|
|
80
|
+
body: Center(
|
|
81
|
+
child: Padding(
|
|
82
|
+
padding: const EdgeInsets.all(32),
|
|
83
|
+
child: Column(
|
|
84
|
+
mainAxisSize: MainAxisSize.min,
|
|
85
|
+
children: <Widget>[
|
|
86
|
+
const Icon(
|
|
87
|
+
Icons.play_circle_outline_rounded,
|
|
88
|
+
color: Colors.white70,
|
|
89
|
+
size: 56,
|
|
90
|
+
),
|
|
91
|
+
const SizedBox(height: 18),
|
|
92
|
+
const Text(
|
|
93
|
+
'Continue to setup',
|
|
94
|
+
style: TextStyle(
|
|
95
|
+
color: Colors.white,
|
|
96
|
+
fontSize: 26,
|
|
97
|
+
fontWeight: FontWeight.w800,
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
const SizedBox(height: 12),
|
|
101
|
+
Text(
|
|
102
|
+
'The intro is not available on this device.',
|
|
103
|
+
textAlign: TextAlign.center,
|
|
104
|
+
style: TextStyle(
|
|
105
|
+
color: Colors.white.withValues(alpha: 0.68),
|
|
106
|
+
fontSize: 15,
|
|
107
|
+
height: 1.5,
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
const SizedBox(height: 28),
|
|
111
|
+
OnboardingPrimaryButton(
|
|
112
|
+
label: 'Continue',
|
|
113
|
+
icon: Icons.arrow_forward_rounded,
|
|
114
|
+
onPressed: widget.onComplete,
|
|
115
|
+
),
|
|
116
|
+
],
|
|
94
117
|
),
|
|
95
|
-
|
|
118
|
+
),
|
|
96
119
|
),
|
|
97
120
|
);
|
|
98
121
|
}
|
|
99
122
|
|
|
100
|
-
if (!_isInitialized) {
|
|
101
|
-
return const
|
|
123
|
+
if (!_isInitialized || _controller == null) {
|
|
124
|
+
return const Scaffold(
|
|
125
|
+
backgroundColor: Colors.black,
|
|
126
|
+
body: Center(child: CircularProgressIndicator(color: Colors.white)),
|
|
127
|
+
);
|
|
102
128
|
}
|
|
103
129
|
|
|
104
|
-
final
|
|
130
|
+
final portrait = orientation == Orientation.portrait;
|
|
131
|
+
if (portrait && _controller!.value.isPlaying) {
|
|
132
|
+
_controller!.pause();
|
|
133
|
+
} else if (!portrait && !_controller!.value.isPlaying) {
|
|
134
|
+
_controller!.play();
|
|
135
|
+
}
|
|
105
136
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
'Please rotate your device',
|
|
121
|
-
textAlign: TextAlign.center,
|
|
122
|
-
style: TextStyle(
|
|
123
|
-
color: Colors.white,
|
|
124
|
-
fontSize: 24,
|
|
125
|
-
fontWeight: FontWeight.w700,
|
|
137
|
+
return Scaffold(
|
|
138
|
+
backgroundColor: Colors.black,
|
|
139
|
+
body: portrait
|
|
140
|
+
? const _RotatePrompt()
|
|
141
|
+
: Stack(
|
|
142
|
+
fit: StackFit.expand,
|
|
143
|
+
children: <Widget>[
|
|
144
|
+
FittedBox(
|
|
145
|
+
fit: BoxFit.cover,
|
|
146
|
+
child: SizedBox(
|
|
147
|
+
width: _controller!.value.size.width,
|
|
148
|
+
height: _controller!.value.size.height,
|
|
149
|
+
child: VideoPlayer(_controller!),
|
|
150
|
+
),
|
|
126
151
|
),
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
Positioned.fill(
|
|
153
|
+
child: DecoratedBox(
|
|
154
|
+
decoration: BoxDecoration(
|
|
155
|
+
gradient: LinearGradient(
|
|
156
|
+
colors: <Color>[
|
|
157
|
+
Colors.transparent,
|
|
158
|
+
Colors.transparent,
|
|
159
|
+
Colors.black.withValues(alpha: 0.22),
|
|
160
|
+
],
|
|
161
|
+
begin: Alignment.topCenter,
|
|
162
|
+
end: Alignment.bottomCenter,
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
),
|
|
136
166
|
),
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
style: FilledButton.styleFrom(
|
|
145
|
-
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
|
167
|
+
Positioned(
|
|
168
|
+
top: 28,
|
|
169
|
+
right: 28,
|
|
170
|
+
child: OnboardingGhostButton(
|
|
171
|
+
label: 'Skip',
|
|
172
|
+
onPressed: widget.onComplete,
|
|
173
|
+
).animate().fadeIn(duration: 280.ms, delay: 220.ms),
|
|
146
174
|
),
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
);
|
|
153
|
-
} else {
|
|
154
|
-
// Ensure it resumes if they rotated back
|
|
155
|
-
if (!_controller!.value.isPlaying && _isInitialized) {
|
|
156
|
-
_controller!.play();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
175
|
+
],
|
|
176
|
+
),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
159
180
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
181
|
+
class _RotatePrompt extends StatelessWidget {
|
|
182
|
+
const _RotatePrompt();
|
|
183
|
+
|
|
184
|
+
@override
|
|
185
|
+
Widget build(BuildContext context) {
|
|
186
|
+
return Center(
|
|
187
|
+
child: Padding(
|
|
188
|
+
padding: const EdgeInsets.all(32),
|
|
189
|
+
child: Column(
|
|
190
|
+
mainAxisSize: MainAxisSize.min,
|
|
191
|
+
children: <Widget>[
|
|
192
|
+
const Icon(Icons.screen_rotation, color: Colors.white, size: 54),
|
|
193
|
+
const SizedBox(height: 18),
|
|
194
|
+
const Text(
|
|
195
|
+
'Rotate to continue',
|
|
196
|
+
textAlign: TextAlign.center,
|
|
197
|
+
style: TextStyle(
|
|
198
|
+
color: Colors.white,
|
|
199
|
+
fontSize: 24,
|
|
200
|
+
fontWeight: FontWeight.w800,
|
|
201
|
+
),
|
|
178
202
|
),
|
|
179
|
-
|
|
180
|
-
|
|
203
|
+
const SizedBox(height: 10),
|
|
204
|
+
Text(
|
|
205
|
+
'This intro is designed for full-screen landscape playback.',
|
|
206
|
+
textAlign: TextAlign.center,
|
|
207
|
+
style: TextStyle(
|
|
208
|
+
color: Colors.white.withValues(alpha: 0.7),
|
|
209
|
+
fontSize: 15,
|
|
210
|
+
height: 1.5,
|
|
211
|
+
),
|
|
212
|
+
),
|
|
213
|
+
],
|
|
181
214
|
),
|
|
182
|
-
|
|
215
|
+
),
|
|
183
216
|
);
|
|
184
217
|
}
|
|
185
218
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import 'package:flutter/material.dart';
|
|
2
2
|
import 'package:flutter_animate/flutter_animate.dart';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import 'onboarding_chrome.dart';
|
|
4
5
|
|
|
5
6
|
class OnboardingWelcomeStep extends StatelessWidget {
|
|
6
7
|
const OnboardingWelcomeStep({super.key, required this.onNext});
|
|
@@ -9,156 +10,37 @@ class OnboardingWelcomeStep extends StatelessWidget {
|
|
|
9
10
|
|
|
10
11
|
@override
|
|
11
12
|
Widget build(BuildContext context) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
41
|
-
mainAxisAlignment: MainAxisAlignment.center,
|
|
42
|
-
children: <Widget>[
|
|
43
|
-
const Spacer(flex: 3),
|
|
44
|
-
|
|
45
|
-
// Welcome text
|
|
46
|
-
Text(
|
|
47
|
-
'Welcome to\nNeoOS',
|
|
48
|
-
style: GoogleFonts.spaceGrotesk(
|
|
49
|
-
fontSize: 72,
|
|
50
|
-
fontWeight: FontWeight.w800,
|
|
51
|
-
height: 1.05,
|
|
52
|
-
letterSpacing: -2.5,
|
|
53
|
-
color: Colors.white,
|
|
54
|
-
),
|
|
55
|
-
).animate()
|
|
56
|
-
.fadeIn(duration: 800.ms, delay: 200.ms)
|
|
57
|
-
.slideY(begin: 0.2, end: 0, curve: Curves.easeOutCubic),
|
|
58
|
-
|
|
59
|
-
const SizedBox(height: 24),
|
|
60
|
-
|
|
61
|
-
Text(
|
|
62
|
-
'A new era of intelligent interaction.\nPrivate, powerful, and deeply integrated into your workflow.',
|
|
63
|
-
style: TextStyle(
|
|
64
|
-
fontSize: 20,
|
|
65
|
-
color: Colors.white.withValues(alpha: 0.7),
|
|
66
|
-
height: 1.5,
|
|
67
|
-
),
|
|
68
|
-
).animate()
|
|
69
|
-
.fadeIn(duration: 800.ms, delay: 400.ms)
|
|
70
|
-
.slideY(begin: 0.2, end: 0, curve: Curves.easeOutCubic),
|
|
71
|
-
|
|
72
|
-
const Spacer(flex: 4),
|
|
73
|
-
|
|
74
|
-
// Action Button
|
|
75
|
-
Align(
|
|
76
|
-
alignment: Alignment.bottomRight,
|
|
77
|
-
child: Container(
|
|
78
|
-
decoration: BoxDecoration(
|
|
79
|
-
boxShadow: <BoxShadow>[
|
|
80
|
-
BoxShadow(
|
|
81
|
-
color: accent.withValues(alpha: 0.4),
|
|
82
|
-
blurRadius: 30,
|
|
83
|
-
offset: const Offset(0, 10),
|
|
84
|
-
),
|
|
85
|
-
],
|
|
86
|
-
),
|
|
87
|
-
child: FilledButton.icon(
|
|
88
|
-
onPressed: onNext,
|
|
89
|
-
style: FilledButton.styleFrom(
|
|
90
|
-
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20),
|
|
91
|
-
shape: RoundedRectangleBorder(
|
|
92
|
-
borderRadius: BorderRadius.circular(24),
|
|
93
|
-
),
|
|
94
|
-
),
|
|
95
|
-
icon: const Icon(Icons.arrow_forward_rounded),
|
|
96
|
-
label: const Text(
|
|
97
|
-
'Get Started',
|
|
98
|
-
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
|
|
99
|
-
),
|
|
100
|
-
),
|
|
101
|
-
).animate()
|
|
102
|
-
.fadeIn(duration: 800.ms, delay: 800.ms)
|
|
103
|
-
.slideX(begin: 0.2, end: 0, curve: Curves.easeOutCubic),
|
|
104
|
-
),
|
|
105
|
-
],
|
|
106
|
-
),
|
|
13
|
+
return OnboardingScaffold(
|
|
14
|
+
step: 1,
|
|
15
|
+
totalSteps: 4,
|
|
16
|
+
eyebrow: 'WELCOME',
|
|
17
|
+
title: 'Welcome to\nNeoOS',
|
|
18
|
+
description: 'Your assistant layer for capture, context, and action.',
|
|
19
|
+
footer: Row(
|
|
20
|
+
mainAxisAlignment: MainAxisAlignment.end,
|
|
21
|
+
children: <Widget>[
|
|
22
|
+
OnboardingPrimaryButton(
|
|
23
|
+
label: 'Continue',
|
|
24
|
+
icon: Icons.arrow_forward_rounded,
|
|
25
|
+
onPressed: onNext,
|
|
26
|
+
)
|
|
27
|
+
.animate()
|
|
28
|
+
.fadeIn(duration: 600.ms, delay: 600.ms)
|
|
29
|
+
.slideY(begin: 0.2),
|
|
30
|
+
],
|
|
31
|
+
),
|
|
32
|
+
child: Align(
|
|
33
|
+
alignment: Alignment.topLeft,
|
|
34
|
+
child: Text(
|
|
35
|
+
'Set up your workspace in a few steps and start using NeoOS immediately.',
|
|
36
|
+
style: TextStyle(
|
|
37
|
+
color: Colors.white.withValues(alpha: 0.72),
|
|
38
|
+
fontSize: 18,
|
|
39
|
+
height: 1.55,
|
|
40
|
+
fontWeight: FontWeight.w500,
|
|
107
41
|
),
|
|
108
|
-
),
|
|
109
|
-
|
|
42
|
+
).animate().fadeIn(duration: 600.ms, delay: 380.ms),
|
|
43
|
+
),
|
|
110
44
|
);
|
|
111
45
|
}
|
|
112
46
|
}
|
|
113
|
-
|
|
114
|
-
class _AbstractBackgroundPainter extends CustomPainter {
|
|
115
|
-
_AbstractBackgroundPainter({required this.accentColor});
|
|
116
|
-
final Color accentColor;
|
|
117
|
-
|
|
118
|
-
@override
|
|
119
|
-
void paint(Canvas canvas, Size size) {
|
|
120
|
-
final paint = Paint()
|
|
121
|
-
..color = accentColor.withValues(alpha: 0.15)
|
|
122
|
-
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 100);
|
|
123
|
-
|
|
124
|
-
canvas.drawCircle(
|
|
125
|
-
Offset(size.width * 0.8, size.height * 0.2),
|
|
126
|
-
size.width * 0.4,
|
|
127
|
-
paint,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
paint.color = const Color(0xFF6EDBFF).withValues(alpha: 0.1);
|
|
131
|
-
canvas.drawCircle(
|
|
132
|
-
Offset(size.width * 0.2, size.height * 0.8),
|
|
133
|
-
size.width * 0.5,
|
|
134
|
-
paint,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
@override
|
|
139
|
-
bool shouldRepaint(covariant _AbstractBackgroundPainter oldDelegate) {
|
|
140
|
-
return oldDelegate.accentColor != accentColor;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
class _GridPainter extends CustomPainter {
|
|
145
|
-
@override
|
|
146
|
-
void paint(Canvas canvas, Size size) {
|
|
147
|
-
final paint = Paint()
|
|
148
|
-
..color = Colors.white
|
|
149
|
-
..strokeWidth = 1
|
|
150
|
-
..style = PaintingStyle.stroke;
|
|
151
|
-
|
|
152
|
-
const double spacing = 40.0;
|
|
153
|
-
|
|
154
|
-
for (double i = 0; i < size.width; i += spacing) {
|
|
155
|
-
canvas.drawLine(Offset(i, 0), Offset(i, size.height), paint);
|
|
156
|
-
}
|
|
157
|
-
for (double i = 0; i < size.height; i += spacing) {
|
|
158
|
-
canvas.drawLine(Offset(0, i), Offset(size.width, i), paint);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@override
|
|
163
|
-
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
164
|
-
}
|