neoagent 2.4.1-beta.30 → 2.4.1-beta.31

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 (27) hide show
  1. package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
  2. package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +391 -382
  3. package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +78 -56
  4. package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +18 -16
  5. package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +18 -17
  6. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +2 -1
  7. package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +16 -13
  8. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +15 -11
  9. package/flutter_app/lib/main_account_settings.dart +1 -1
  10. package/flutter_app/lib/main_admin.dart +1 -1
  11. package/flutter_app/lib/main_app_shell.dart +190 -191
  12. package/flutter_app/lib/main_chat.dart +381 -228
  13. package/flutter_app/lib/main_devices.dart +1 -1
  14. package/flutter_app/lib/main_launcher.dart +1 -1
  15. package/flutter_app/lib/main_operations.dart +3 -3
  16. package/flutter_app/lib/main_shared.dart +336 -340
  17. package/flutter_app/lib/main_spacing.dart +4 -4
  18. package/flutter_app/lib/main_theme.dart +20 -14
  19. package/flutter_app/lib/src/theme/palette.dart +76 -34
  20. package/flutter_app/pubspec.lock +2 -2
  21. package/flutter_app/pubspec.yaml +1 -1
  22. package/package.json +1 -1
  23. package/server/public/.last_build_id +1 -1
  24. package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
  25. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  26. package/server/public/flutter_bootstrap.js +1 -1
  27. package/server/public/main.dart.js +60738 -60521
@@ -6,6 +6,7 @@ import 'package:http/http.dart' as http;
6
6
  import 'package:url_launcher/url_launcher.dart';
7
7
 
8
8
  import '../../main.dart';
9
+ import '../../src/theme/palette.dart';
9
10
  import 'onboarding_chrome.dart';
10
11
 
11
12
  class OnboardingCompanionStep extends StatefulWidget {
@@ -189,12 +190,13 @@ class _OnboardingCompanionStepState extends State<OnboardingCompanionStep> {
189
190
  }
190
191
 
191
192
  Widget _buildChannelSelector() {
193
+ final p = paletteOf(context);
192
194
  return Container(
193
195
  padding: const EdgeInsets.all(4),
194
196
  decoration: BoxDecoration(
195
- color: Colors.white.withValues(alpha: 0.05),
196
- borderRadius: BorderRadius.circular(16),
197
- border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
197
+ color: p.bgSecondary,
198
+ borderRadius: BorderRadius.circular(14),
199
+ border: Border.all(color: p.border),
198
200
  ),
199
201
  child: Row(
200
202
  mainAxisSize: MainAxisSize.min,
@@ -234,7 +236,9 @@ class _OnboardingCompanionStepState extends State<OnboardingCompanionStep> {
234
236
  child: Text(
235
237
  label,
236
238
  style: TextStyle(
237
- color: isSelected ? Colors.white : Colors.white.withValues(alpha: 0.6),
239
+ color: isSelected
240
+ ? paletteOf(context).textPrimary
241
+ : paletteOf(context).textMuted,
238
242
  fontSize: 14,
239
243
  fontWeight: FontWeight.w700,
240
244
  ),
@@ -248,7 +252,6 @@ class _OnboardingCompanionStepState extends State<OnboardingCompanionStep> {
248
252
  Widget build(BuildContext context) {
249
253
  final width = MediaQuery.sizeOf(context).width;
250
254
  final useGrid = width >= 700;
251
- final columns = width >= 1050 ? 3 : (useGrid ? 2 : 1);
252
255
 
253
256
  return AnimatedBuilder(
254
257
  animation: widget.controller,
@@ -333,12 +336,14 @@ class _OnboardingCompanionStepState extends State<OnboardingCompanionStep> {
333
336
  _buildChannelSelector(),
334
337
  if (_isLoadingReleases) ...[
335
338
  const SizedBox(width: 12),
336
- const SizedBox(
339
+ SizedBox(
337
340
  width: 14,
338
341
  height: 14,
339
342
  child: CircularProgressIndicator(
340
343
  strokeWidth: 2,
341
- valueColor: AlwaysStoppedAnimation<Color>(Colors.white54),
344
+ valueColor: AlwaysStoppedAnimation<Color>(
345
+ paletteOf(context).textMuted,
346
+ ),
342
347
  ),
343
348
  ),
344
349
  ],
@@ -348,11 +353,12 @@ class _OnboardingCompanionStepState extends State<OnboardingCompanionStep> {
348
353
  Expanded(
349
354
  child: useGrid
350
355
  ? GridView.builder(
351
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
352
- crossAxisCount: columns,
356
+ gridDelegate:
357
+ const SliverGridDelegateWithMaxCrossAxisExtent(
358
+ maxCrossAxisExtent: 360,
353
359
  crossAxisSpacing: 14,
354
360
  mainAxisSpacing: 14,
355
- childAspectRatio: width >= 1050 ? 1.42 : 1.25,
361
+ mainAxisExtent: 264,
356
362
  ),
357
363
  itemCount: items.length,
358
364
  itemBuilder: (context, index) {
@@ -442,6 +448,7 @@ class _CompanionCard extends StatelessWidget {
442
448
 
443
449
  @override
444
450
  Widget build(BuildContext context) {
451
+ final p = paletteOf(context);
445
452
  final shellSize = compact ? 48.0 : 58.0;
446
453
  final iconSize = compact ? 24.0 : 30.0;
447
454
 
@@ -473,26 +480,24 @@ class _CompanionCard extends StatelessWidget {
473
480
  item.title,
474
481
  maxLines: 1,
475
482
  overflow: TextOverflow.ellipsis,
476
- style: const TextStyle(
477
- color: Colors.white,
478
- fontSize: 18,
479
- fontWeight: FontWeight.w800,
483
+ style: TextStyle(
484
+ color: p.textPrimary,
485
+ fontSize: 16,
486
+ fontWeight: FontWeight.w700,
480
487
  ),
481
488
  ),
482
489
  const SizedBox(height: 6),
483
- Expanded(
484
- child: Text(
485
- item.subtitle,
486
- maxLines: 3,
487
- overflow: TextOverflow.ellipsis,
488
- style: TextStyle(
489
- color: Colors.white.withValues(alpha: 0.68),
490
- fontSize: 13,
491
- height: 1.35,
492
- ),
490
+ Text(
491
+ item.subtitle,
492
+ maxLines: 2,
493
+ overflow: TextOverflow.ellipsis,
494
+ style: TextStyle(
495
+ color: p.textMuted,
496
+ fontSize: 13,
497
+ height: 1.4,
493
498
  ),
494
499
  ),
495
- const SizedBox(height: 12),
500
+ const Spacer(),
496
501
  if (item.id == 'desktop' && !item.connected)
497
502
  _PlatformSelector(
498
503
  selectedPlatform: selectedDesktopPlatform,
@@ -525,17 +530,17 @@ class _CompanionCard extends StatelessWidget {
525
530
  children: <Widget>[
526
531
  Text(
527
532
  item.title,
528
- style: const TextStyle(
529
- color: Colors.white,
530
- fontSize: 20,
531
- fontWeight: FontWeight.w800,
533
+ style: TextStyle(
534
+ color: p.textPrimary,
535
+ fontSize: 18,
536
+ fontWeight: FontWeight.w700,
532
537
  ),
533
538
  ),
534
539
  const SizedBox(height: 5),
535
540
  Text(
536
541
  item.subtitle,
537
542
  style: TextStyle(
538
- color: Colors.white.withValues(alpha: 0.68),
543
+ color: p.textMuted,
539
544
  fontSize: 14,
540
545
  height: 1.45,
541
546
  ),
@@ -585,7 +590,7 @@ class _StatusIndicator extends StatelessWidget {
585
590
  : Icon(
586
591
  Icons.arrow_circle_down_rounded,
587
592
  key: const ValueKey<String>('downloadable'),
588
- color: Colors.white.withValues(alpha: 0.26),
593
+ color: paletteOf(context).textMuted.withValues(alpha: 0.5),
589
594
  size: 28,
590
595
  ),
591
596
  );
@@ -600,8 +605,10 @@ class _DownloadButton extends StatelessWidget {
600
605
 
601
606
  @override
602
607
  Widget build(BuildContext context) {
608
+ final p = paletteOf(context);
603
609
  if (item.connected) {
604
610
  return Container(
611
+ width: double.infinity,
605
612
  padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
606
613
  decoration: BoxDecoration(
607
614
  color: item.accentColor.withValues(alpha: 0.16),
@@ -610,15 +617,20 @@ class _DownloadButton extends StatelessWidget {
610
617
  ),
611
618
  child: Row(
612
619
  mainAxisSize: MainAxisSize.min,
620
+ mainAxisAlignment: MainAxisAlignment.center,
613
621
  children: <Widget>[
614
622
  Icon(Icons.done_all_rounded, size: 14, color: item.accentColor),
615
623
  const SizedBox(width: 6),
616
- Text(
617
- 'Connected',
618
- style: TextStyle(
619
- color: item.accentColor,
620
- fontSize: 12,
621
- fontWeight: FontWeight.w700,
624
+ Flexible(
625
+ child: Text(
626
+ 'Connected',
627
+ maxLines: 1,
628
+ overflow: TextOverflow.ellipsis,
629
+ style: TextStyle(
630
+ color: item.accentColor,
631
+ fontSize: 12,
632
+ fontWeight: FontWeight.w700,
633
+ ),
622
634
  ),
623
635
  ),
624
636
  ],
@@ -627,33 +639,39 @@ class _DownloadButton extends StatelessWidget {
627
639
  }
628
640
 
629
641
  return Container(
642
+ width: double.infinity,
630
643
  padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
631
644
  decoration: BoxDecoration(
632
645
  color: isClicked
633
- ? Colors.white.withValues(alpha: 0.08)
646
+ ? p.bgSecondary
634
647
  : item.accentColor.withValues(alpha: 0.12),
635
648
  borderRadius: BorderRadius.circular(12),
636
649
  border: Border.all(
637
650
  color: isClicked
638
- ? Colors.white.withValues(alpha: 0.2)
651
+ ? p.borderLight
639
652
  : item.accentColor.withValues(alpha: 0.25),
640
653
  ),
641
654
  ),
642
655
  child: Row(
643
656
  mainAxisSize: MainAxisSize.min,
657
+ mainAxisAlignment: MainAxisAlignment.center,
644
658
  children: <Widget>[
645
659
  Icon(
646
660
  isClicked ? Icons.hourglass_empty_rounded : Icons.open_in_new_rounded,
647
661
  size: 14,
648
- color: isClicked ? Colors.white70 : item.accentColor,
662
+ color: isClicked ? p.textMuted : item.accentColor,
649
663
  ),
650
664
  const SizedBox(width: 6),
651
- Text(
652
- isClicked ? 'Waiting for pairing...' : item.buttonText,
653
- style: TextStyle(
654
- color: isClicked ? Colors.white70 : Colors.white,
655
- fontSize: 12,
656
- fontWeight: FontWeight.w700,
665
+ Flexible(
666
+ child: Text(
667
+ isClicked ? 'Waiting for pairing...' : item.buttonText,
668
+ maxLines: 1,
669
+ overflow: TextOverflow.ellipsis,
670
+ style: TextStyle(
671
+ color: isClicked ? p.textMuted : item.accentColor,
672
+ fontSize: 12,
673
+ fontWeight: FontWeight.w700,
674
+ ),
657
675
  ),
658
676
  ),
659
677
  ],
@@ -673,26 +691,27 @@ class _PlatformSelector extends StatelessWidget {
673
691
 
674
692
  @override
675
693
  Widget build(BuildContext context) {
694
+ final p = paletteOf(context);
676
695
  return Container(
677
696
  margin: const EdgeInsets.only(bottom: 12),
678
697
  padding: const EdgeInsets.all(2),
679
698
  decoration: BoxDecoration(
680
- color: Colors.white.withValues(alpha: 0.04),
699
+ color: p.bgSecondary,
681
700
  borderRadius: BorderRadius.circular(10),
682
- border: Border.all(color: Colors.white.withValues(alpha: 0.06)),
701
+ border: Border.all(color: p.border),
683
702
  ),
684
703
  child: Row(
685
- mainAxisSize: MainAxisSize.min,
686
704
  children: <Widget>[
687
- _buildTab(TargetPlatform.macOS, 'macOS'),
688
- _buildTab(TargetPlatform.windows, 'Windows'),
689
- _buildTab(TargetPlatform.linux, 'Linux'),
705
+ Expanded(child: _buildTab(context, TargetPlatform.macOS, 'macOS')),
706
+ Expanded(child: _buildTab(context, TargetPlatform.windows, 'Windows')),
707
+ Expanded(child: _buildTab(context, TargetPlatform.linux, 'Linux')),
690
708
  ],
691
709
  ),
692
710
  );
693
711
  }
694
712
 
695
- Widget _buildTab(TargetPlatform platform, String label) {
713
+ Widget _buildTab(BuildContext context, TargetPlatform platform, String label) {
714
+ final p = paletteOf(context);
696
715
  final isSelected = selectedPlatform == platform;
697
716
  return Material(
698
717
  color: Colors.transparent,
@@ -701,15 +720,18 @@ class _PlatformSelector extends StatelessWidget {
701
720
  borderRadius: BorderRadius.circular(8),
702
721
  child: AnimatedContainer(
703
722
  duration: const Duration(milliseconds: 180),
704
- padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
723
+ padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
705
724
  decoration: BoxDecoration(
706
- color: isSelected ? Colors.white.withValues(alpha: 0.08) : Colors.transparent,
725
+ color: isSelected ? p.bgCard : Colors.transparent,
707
726
  borderRadius: BorderRadius.circular(8),
708
727
  ),
709
728
  child: Text(
710
729
  label,
730
+ textAlign: TextAlign.center,
731
+ maxLines: 1,
732
+ overflow: TextOverflow.ellipsis,
711
733
  style: TextStyle(
712
- color: isSelected ? Colors.white : Colors.white.withValues(alpha: 0.5),
734
+ color: isSelected ? p.textPrimary : p.textMuted,
713
735
  fontSize: 11,
714
736
  fontWeight: FontWeight.w700,
715
737
  ),
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
2
  import 'package:flutter_animate/flutter_animate.dart';
3
3
 
4
4
  import '../../main.dart';
5
+ import '../../src/theme/palette.dart';
5
6
  import 'onboarding_chrome.dart';
6
7
 
7
8
  class OnboardingMessagingStep extends StatefulWidget {
@@ -29,7 +30,6 @@ class _OnboardingMessagingStepState extends State<OnboardingMessagingStep> {
29
30
  : messagingPlatforms;
30
31
  final width = MediaQuery.sizeOf(context).width;
31
32
  final useGrid = width >= 700;
32
- final columns = width >= 1050 ? 3 : (useGrid ? 2 : 1);
33
33
 
34
34
  return OnboardingScaffold(
35
35
  step: 2,
@@ -79,11 +79,11 @@ class _OnboardingMessagingStepState extends State<OnboardingMessagingStep> {
79
79
  ),
80
80
  child: useGrid
81
81
  ? GridView.builder(
82
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
83
- crossAxisCount: columns,
82
+ gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
83
+ maxCrossAxisExtent: 360,
84
84
  crossAxisSpacing: 14,
85
85
  mainAxisSpacing: 14,
86
- childAspectRatio: width >= 1050 ? 1.5 : 1.35,
86
+ mainAxisExtent: 160,
87
87
  ),
88
88
  itemCount: platforms.length,
89
89
  itemBuilder: (context, index) {
@@ -133,6 +133,7 @@ class _MessagingPlatformCard extends StatelessWidget {
133
133
 
134
134
  @override
135
135
  Widget build(BuildContext context) {
136
+ final p = paletteOf(context);
136
137
  final iconSize = compact ? 24.0 : 30.0;
137
138
  final shellSize = compact ? 48.0 : 58.0;
138
139
  return OnboardingOptionCard(
@@ -172,10 +173,10 @@ class _MessagingPlatformCard extends StatelessWidget {
172
173
  platform.label,
173
174
  maxLines: 1,
174
175
  overflow: TextOverflow.ellipsis,
175
- style: const TextStyle(
176
- color: Colors.white,
177
- fontSize: 18,
178
- fontWeight: FontWeight.w800,
176
+ style: TextStyle(
177
+ color: p.textPrimary,
178
+ fontSize: 16,
179
+ fontWeight: FontWeight.w700,
179
180
  ),
180
181
  ),
181
182
  const SizedBox(height: 4),
@@ -184,9 +185,9 @@ class _MessagingPlatformCard extends StatelessWidget {
184
185
  maxLines: 2,
185
186
  overflow: TextOverflow.ellipsis,
186
187
  style: TextStyle(
187
- color: Colors.white.withValues(alpha: 0.68),
188
+ color: p.textMuted,
188
189
  fontSize: 13,
189
- height: 1.35,
190
+ height: 1.4,
190
191
  ),
191
192
  ),
192
193
  ],
@@ -213,17 +214,17 @@ class _MessagingPlatformCard extends StatelessWidget {
213
214
  children: <Widget>[
214
215
  Text(
215
216
  platform.label,
216
- style: const TextStyle(
217
- color: Colors.white,
218
- fontSize: 20,
219
- fontWeight: FontWeight.w800,
217
+ style: TextStyle(
218
+ color: p.textPrimary,
219
+ fontSize: 18,
220
+ fontWeight: FontWeight.w700,
220
221
  ),
221
222
  ),
222
223
  const SizedBox(height: 5),
223
224
  Text(
224
225
  platform.subtitle,
225
226
  style: TextStyle(
226
- color: Colors.white.withValues(alpha: 0.68),
227
+ color: p.textMuted,
227
228
  fontSize: 14,
228
229
  height: 1.45,
229
230
  ),
@@ -255,6 +256,7 @@ class _SelectionIcon extends StatelessWidget {
255
256
 
256
257
  @override
257
258
  Widget build(BuildContext context) {
259
+ final p = paletteOf(context);
258
260
  return AnimatedSwitcher(
259
261
  duration: const Duration(milliseconds: 220),
260
262
  child: selected
@@ -267,7 +269,7 @@ class _SelectionIcon extends StatelessWidget {
267
269
  : Icon(
268
270
  Icons.add_circle_outline_rounded,
269
271
  key: ValueKey<String>('idle-$id'),
270
- color: Colors.white.withValues(alpha: 0.26),
272
+ color: p.textMuted.withValues(alpha: 0.5),
271
273
  size: 28,
272
274
  ),
273
275
  );
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
2
  import 'package:flutter_animate/flutter_animate.dart';
3
3
 
4
4
  import '../../main.dart';
5
+ import '../../src/theme/palette.dart';
5
6
  import 'onboarding_chrome.dart';
6
7
 
7
8
  class OnboardingModelStep extends StatefulWidget {
@@ -76,7 +77,6 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
76
77
  Widget build(BuildContext context) {
77
78
  final width = MediaQuery.sizeOf(context).width;
78
79
  final useGrid = width >= 720;
79
- final columns = width >= 1150 ? 3 : (useGrid ? 2 : 1);
80
80
 
81
81
  return OnboardingScaffold(
82
82
  step: 3,
@@ -100,7 +100,7 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
100
100
  'No available models found.\nYou can configure providers later in Settings.',
101
101
  textAlign: TextAlign.center,
102
102
  style: TextStyle(
103
- color: Colors.white.withValues(alpha: 0.6),
103
+ color: paletteOf(context).textMuted,
104
104
  fontSize: 16,
105
105
  height: 1.5,
106
106
  ),
@@ -108,11 +108,11 @@ class _OnboardingModelStepState extends State<OnboardingModelStep> {
108
108
  )
109
109
  : useGrid
110
110
  ? GridView.builder(
111
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
112
- crossAxisCount: columns,
111
+ gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
112
+ maxCrossAxisExtent: 380,
113
113
  crossAxisSpacing: 14,
114
114
  mainAxisSpacing: 14,
115
- childAspectRatio: width >= 1150 ? 1.38 : 1.18,
115
+ mainAxisExtent: 214,
116
116
  ),
117
117
  itemCount: _models.length,
118
118
  itemBuilder: (context, index) {
@@ -193,6 +193,7 @@ class _ModelChoiceCard extends StatelessWidget {
193
193
 
194
194
  @override
195
195
  Widget build(BuildContext context) {
196
+ final p = paletteOf(context);
196
197
  final shellSize = compact ? 48.0 : 58.0;
197
198
  final iconSize = compact ? 24.0 : 30.0;
198
199
  final titleSize = compact ? 17.0 : 20.0;
@@ -231,7 +232,7 @@ class _ModelChoiceCard extends StatelessWidget {
231
232
  : Icon(
232
233
  Icons.radio_button_unchecked_rounded,
233
234
  key: ValueKey<String>('idle-${model.id}'),
234
- color: Colors.white.withValues(alpha: 0.25),
235
+ color: p.textMuted.withValues(alpha: 0.5),
235
236
  size: 28,
236
237
  ),
237
238
  ),
@@ -243,9 +244,9 @@ class _ModelChoiceCard extends StatelessWidget {
243
244
  maxLines: 1,
244
245
  overflow: TextOverflow.ellipsis,
245
246
  style: TextStyle(
246
- color: Colors.white,
247
+ color: p.textPrimary,
247
248
  fontSize: titleSize,
248
- fontWeight: FontWeight.w800,
249
+ fontWeight: FontWeight.w700,
249
250
  ),
250
251
  ),
251
252
  const SizedBox(height: 6),
@@ -255,7 +256,7 @@ class _ModelChoiceCard extends StatelessWidget {
255
256
  vertical: 5,
256
257
  ),
257
258
  decoration: BoxDecoration(
258
- color: Colors.white.withValues(alpha: 0.08),
259
+ color: p.bgSecondary,
259
260
  borderRadius: BorderRadius.circular(999),
260
261
  ),
261
262
  child: Text(
@@ -263,7 +264,7 @@ class _ModelChoiceCard extends StatelessWidget {
263
264
  maxLines: 1,
264
265
  overflow: TextOverflow.ellipsis,
265
266
  style: TextStyle(
266
- color: Colors.white.withValues(alpha: 0.72),
267
+ color: p.textMuted,
267
268
  fontSize: 11,
268
269
  fontWeight: FontWeight.w700,
269
270
  ),
@@ -275,7 +276,7 @@ class _ModelChoiceCard extends StatelessWidget {
275
276
  maxLines: 3,
276
277
  overflow: TextOverflow.ellipsis,
277
278
  style: TextStyle(
278
- color: Colors.white.withValues(alpha: 0.68),
279
+ color: p.textMuted,
279
280
  fontSize: purposeSize,
280
281
  height: 1.35,
281
282
  ),
@@ -306,9 +307,9 @@ class _ModelChoiceCard extends StatelessWidget {
306
307
  Text(
307
308
  model.label,
308
309
  style: TextStyle(
309
- color: Colors.white,
310
+ color: p.textPrimary,
310
311
  fontSize: titleSize,
311
- fontWeight: FontWeight.w800,
312
+ fontWeight: FontWeight.w700,
312
313
  ),
313
314
  ),
314
315
  Container(
@@ -317,13 +318,13 @@ class _ModelChoiceCard extends StatelessWidget {
317
318
  vertical: 5,
318
319
  ),
319
320
  decoration: BoxDecoration(
320
- color: Colors.white.withValues(alpha: 0.08),
321
+ color: p.bgSecondary,
321
322
  borderRadius: BorderRadius.circular(999),
322
323
  ),
323
324
  child: Text(
324
325
  model.provider,
325
326
  style: TextStyle(
326
- color: Colors.white.withValues(alpha: 0.72),
327
+ color: p.textMuted,
327
328
  fontSize: 12,
328
329
  fontWeight: FontWeight.w700,
329
330
  ),
@@ -335,7 +336,7 @@ class _ModelChoiceCard extends StatelessWidget {
335
336
  Text(
336
337
  model.purpose,
337
338
  style: TextStyle(
338
- color: Colors.white.withValues(alpha: 0.68),
339
+ color: p.textMuted,
339
340
  fontSize: purposeSize,
340
341
  height: 1.45,
341
342
  ),
@@ -355,7 +356,7 @@ class _ModelChoiceCard extends StatelessWidget {
355
356
  : Icon(
356
357
  Icons.radio_button_unchecked_rounded,
357
358
  key: ValueKey<String>('idle-${model.id}'),
358
- color: Colors.white.withValues(alpha: 0.25),
359
+ color: p.textMuted.withValues(alpha: 0.5),
359
360
  size: 28,
360
361
  ),
361
362
  ),
@@ -1,5 +1,6 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import '../../main.dart';
3
+ import '../../src/theme/palette.dart';
3
4
  import 'onboarding_video_step.dart';
4
5
  import 'onboarding_welcome_step.dart';
5
6
  import 'onboarding_companion_step.dart';
@@ -38,7 +39,7 @@ class _OnboardingShellState extends State<OnboardingShell> {
38
39
  @override
39
40
  Widget build(BuildContext context) {
40
41
  return Scaffold(
41
- backgroundColor: Colors.black, // Deep black for video and transitions
42
+ backgroundColor: paletteOf(context).bgPrimary,
42
43
  body: PageView(
43
44
  controller: _pageController,
44
45
  physics:
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
5
5
  import 'package:flutter_animate/flutter_animate.dart';
6
6
  import 'package:video_player/video_player.dart';
7
7
 
8
+ import '../../src/theme/palette.dart';
8
9
  import 'onboarding_chrome.dart';
9
10
 
10
11
  class OnboardingVideoStep extends StatefulWidget {
@@ -75,26 +76,27 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
75
76
  final orientation = MediaQuery.orientationOf(context);
76
77
 
77
78
  if (_hasError) {
79
+ final p = paletteOf(context);
78
80
  return Scaffold(
79
- backgroundColor: Colors.black,
81
+ backgroundColor: p.bgPrimary,
80
82
  body: Center(
81
83
  child: Padding(
82
84
  padding: const EdgeInsets.all(32),
83
85
  child: Column(
84
86
  mainAxisSize: MainAxisSize.min,
85
87
  children: <Widget>[
86
- const Icon(
88
+ Icon(
87
89
  Icons.play_circle_outline_rounded,
88
- color: Colors.white70,
90
+ color: p.accent,
89
91
  size: 56,
90
92
  ),
91
93
  const SizedBox(height: 18),
92
- const Text(
94
+ Text(
93
95
  'Continue to setup',
94
96
  style: TextStyle(
95
- color: Colors.white,
97
+ color: p.textPrimary,
96
98
  fontSize: 26,
97
- fontWeight: FontWeight.w800,
99
+ fontWeight: FontWeight.w700,
98
100
  ),
99
101
  ),
100
102
  const SizedBox(height: 12),
@@ -102,7 +104,7 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
102
104
  'The intro is not available on this device.',
103
105
  textAlign: TextAlign.center,
104
106
  style: TextStyle(
105
- color: Colors.white.withValues(alpha: 0.68),
107
+ color: p.textMuted,
106
108
  fontSize: 15,
107
109
  height: 1.5,
108
110
  ),
@@ -135,7 +137,7 @@ class _OnboardingVideoStepState extends State<OnboardingVideoStep> {
135
137
  }
136
138
 
137
139
  return Scaffold(
138
- backgroundColor: Colors.black,
140
+ backgroundColor: portrait ? paletteOf(context).bgPrimary : Colors.black,
139
141
  body: portrait
140
142
  ? const _RotatePrompt()
141
143
  : Stack(
@@ -183,21 +185,22 @@ class _RotatePrompt extends StatelessWidget {
183
185
 
184
186
  @override
185
187
  Widget build(BuildContext context) {
188
+ final p = paletteOf(context);
186
189
  return Center(
187
190
  child: Padding(
188
191
  padding: const EdgeInsets.all(32),
189
192
  child: Column(
190
193
  mainAxisSize: MainAxisSize.min,
191
194
  children: <Widget>[
192
- const Icon(Icons.screen_rotation, color: Colors.white, size: 54),
195
+ Icon(Icons.screen_rotation, color: p.accent, size: 54),
193
196
  const SizedBox(height: 18),
194
- const Text(
197
+ Text(
195
198
  'Rotate to continue',
196
199
  textAlign: TextAlign.center,
197
200
  style: TextStyle(
198
- color: Colors.white,
201
+ color: p.textPrimary,
199
202
  fontSize: 24,
200
- fontWeight: FontWeight.w800,
203
+ fontWeight: FontWeight.w700,
201
204
  ),
202
205
  ),
203
206
  const SizedBox(height: 10),
@@ -205,7 +208,7 @@ class _RotatePrompt extends StatelessWidget {
205
208
  'This intro is designed for full-screen landscape playback.',
206
209
  textAlign: TextAlign.center,
207
210
  style: TextStyle(
208
- color: Colors.white.withValues(alpha: 0.7),
211
+ color: p.textMuted,
209
212
  fontSize: 15,
210
213
  height: 1.5,
211
214
  ),