kasy-cli 1.32.0 → 1.35.0

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 (169) hide show
  1. package/README.md +1 -1
  2. package/bin/kasy.js +66 -2
  3. package/docs/cli-reference.md +7 -7
  4. package/lib/commands/apple-web.js +222 -0
  5. package/lib/commands/configure.js +3 -91
  6. package/lib/commands/doctor.js +20 -0
  7. package/lib/commands/facebook.js +189 -0
  8. package/lib/commands/new.js +61 -11
  9. package/lib/commands/release-version.js +234 -0
  10. package/lib/commands/update.js +27 -0
  11. package/lib/scaffold/CHANGELOG.json +27 -0
  12. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +24 -0
  13. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api_interface.dart +15 -0
  14. package/lib/scaffold/backends/api/patch/lib/features/authentication/repositories/authentication_repository.dart +27 -0
  15. package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/entities/subscription_entity.dart +1 -0
  16. package/lib/scaffold/backends/firebase/setup-from-scratch.js +199 -21
  17. package/lib/scaffold/backends/patch-base-hashes.json +66 -0
  18. package/lib/scaffold/backends/supabase/deploy.js +92 -0
  19. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +2 -0
  20. package/lib/scaffold/backends/supabase/migrations/20240101000007_welcome_notification.sql +36 -23
  21. package/lib/scaffold/backends/supabase/migrations/20240101000014_subscription_trial_end.sql +6 -0
  22. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +92 -3
  23. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/repositories/authentication_repository.dart +36 -0
  24. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/entities/subscription_entity.dart +1 -0
  25. package/lib/scaffold/generate.js +53 -4
  26. package/lib/scaffold/shared/generator-utils.js +18 -6
  27. package/lib/utils/apple-web.js +147 -0
  28. package/lib/utils/facebook.js +162 -0
  29. package/lib/utils/i18n/messages-en.js +85 -0
  30. package/lib/utils/i18n/messages-es.js +85 -0
  31. package/lib/utils/i18n/messages-pt.js +85 -0
  32. package/package.json +5 -2
  33. package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +73 -0
  34. package/templates/firebase/AGENTS.md +170 -0
  35. package/templates/firebase/CLAUDE.md +16 -0
  36. package/templates/firebase/DESIGN_SYSTEM.md +269 -0
  37. package/templates/firebase/docs/auth-setup.en.md +4 -2
  38. package/templates/firebase/docs/auth-setup.es.md +4 -2
  39. package/templates/firebase/docs/auth-setup.pt.md +4 -2
  40. package/templates/firebase/firebase.json +56 -1
  41. package/templates/firebase/functions/src/authentication/triggers.ts +10 -6
  42. package/templates/firebase/functions/src/core/data/entities/subscription_entity.ts +5 -0
  43. package/templates/firebase/functions/src/core/data/entities/user_entity.ts +9 -1
  44. package/templates/firebase/functions/src/core/data/repositories/user_repository.ts +27 -0
  45. package/templates/firebase/functions/src/subscriptions/models/subscriptions.ts +3 -0
  46. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +4 -0
  47. package/templates/firebase/lib/components/components.dart +1 -0
  48. package/templates/firebase/lib/components/kasy_alert.dart +0 -1
  49. package/templates/firebase/lib/components/kasy_app_bar.dart +35 -17
  50. package/templates/firebase/lib/components/kasy_bottom_sheet.dart +0 -1
  51. package/templates/firebase/lib/components/kasy_date_picker.dart +0 -5
  52. package/templates/firebase/lib/components/kasy_dialog.dart +0 -1
  53. package/templates/firebase/lib/components/kasy_otp_verification_bottom_sheet.dart +1 -1
  54. package/templates/firebase/lib/components/kasy_screen.dart +114 -0
  55. package/templates/firebase/lib/components/kasy_sidebar.dart +189 -120
  56. package/templates/firebase/lib/components/kasy_text_area.dart +0 -1
  57. package/templates/firebase/lib/components/kasy_text_field.dart +0 -1
  58. package/templates/firebase/lib/components/kasy_text_field_otp.dart +0 -1
  59. package/templates/firebase/lib/components/kasy_toast.dart +108 -73
  60. package/templates/firebase/lib/core/app_update/app_update_repository.dart +54 -0
  61. package/templates/firebase/lib/core/app_update/app_update_status.dart +14 -0
  62. package/templates/firebase/lib/core/app_update/update_available_sheet.dart +70 -0
  63. package/templates/firebase/lib/core/bottom_menu/active_tab_notifier.dart +40 -3
  64. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +82 -34
  65. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +6 -6
  66. package/templates/firebase/lib/core/bottom_menu/web_url.dart +20 -0
  67. package/templates/firebase/lib/core/chrome/chrome_visibility.dart +22 -0
  68. package/templates/firebase/lib/core/config/features.dart +5 -0
  69. package/templates/firebase/lib/core/data/api/remote_config_api.dart +38 -1
  70. package/templates/firebase/lib/core/guards/guard.dart +16 -2
  71. package/templates/firebase/lib/core/icons/kasy_icons.dart +3 -0
  72. package/templates/firebase/lib/core/rating/widgets/rate_banner.dart +2 -5
  73. package/templates/firebase/lib/core/rating/widgets/review_popup.dart +48 -124
  74. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +14 -0
  75. package/templates/firebase/lib/core/states/components/maybe_show_update_available.dart +32 -0
  76. package/templates/firebase/lib/core/states/logout_action.dart +5 -1
  77. package/templates/firebase/lib/core/states/user_state_notifier.dart +85 -14
  78. package/templates/firebase/lib/core/theme/responsive_text_theme.dart +69 -0
  79. package/templates/firebase/lib/core/theme/texts.dart +90 -57
  80. package/templates/firebase/lib/core/theme/type_scale.dart +77 -0
  81. package/templates/firebase/lib/core/theme/web_background_sync_web.dart +20 -6
  82. package/templates/firebase/lib/core/utils/image_bytes_loader.dart +12 -0
  83. package/templates/firebase/lib/core/utils/image_bytes_loader_io.dart +28 -0
  84. package/templates/firebase/lib/core/utils/image_bytes_loader_web.dart +56 -0
  85. package/templates/firebase/lib/core/web_screen_width.dart +15 -0
  86. package/templates/firebase/lib/core/web_screen_width_io.dart +3 -0
  87. package/templates/firebase/lib/core/web_screen_width_web.dart +10 -0
  88. package/templates/firebase/lib/core/web_viewport_scale.dart +61 -25
  89. package/templates/firebase/lib/core/widgets/focus_visibility.dart +89 -0
  90. package/templates/firebase/lib/core/widgets/update_bottom_sheet.dart +29 -126
  91. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +11 -8
  92. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_composer.dart +21 -0
  93. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_conversation_view.dart +1 -2
  94. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +2 -3
  95. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +1 -2
  96. package/templates/firebase/lib/features/authentication/api/auth_web_support.dart +12 -0
  97. package/templates/firebase/lib/features/authentication/api/auth_web_support_web.dart +25 -0
  98. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +266 -0
  99. package/templates/firebase/lib/features/authentication/api/authentication_api_interface.dart +22 -0
  100. package/templates/firebase/lib/features/authentication/navigation/post_login_navigation.dart +32 -0
  101. package/templates/firebase/lib/features/authentication/providers/phone_auth_notifier.dart +7 -7
  102. package/templates/firebase/lib/features/authentication/providers/signin_state_provider.dart +34 -10
  103. package/templates/firebase/lib/features/authentication/providers/signup_state_provider.dart +2 -2
  104. package/templates/firebase/lib/features/authentication/repositories/authentication_repository.dart +37 -0
  105. package/templates/firebase/lib/features/authentication/repositories/exceptions/authentication_exceptions.dart +8 -0
  106. package/templates/firebase/lib/features/authentication/ui/components/otp_verification.dart +2 -2
  107. package/templates/firebase/lib/features/authentication/ui/components/phone_input.dart +2 -2
  108. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +80 -15
  109. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +20 -14
  110. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +2 -2
  111. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +2 -2
  112. package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -1
  113. package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +1 -2
  114. package/templates/firebase/lib/features/home/design_system_page.dart +134 -67
  115. package/templates/firebase/lib/features/home/home_components_page.dart +8 -1
  116. package/templates/firebase/lib/features/home/home_components_preview_page.dart +1 -3
  117. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +186 -56
  118. package/templates/firebase/lib/features/home/home_page.dart +4 -0
  119. package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +169 -208
  120. package/templates/firebase/lib/features/notifications/providers/notifications_provider.dart +10 -0
  121. package/templates/firebase/lib/features/notifications/ui/components/notification_settings_sheet.dart +2 -2
  122. package/templates/firebase/lib/features/notifications/ui/components/notification_tile.dart +21 -8
  123. package/templates/firebase/lib/features/notifications/ui/components/push_notification_switcher.dart +2 -2
  124. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +3 -4
  125. package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +84 -128
  126. package/templates/firebase/lib/features/onboarding/providers/onboarding_model.dart +11 -0
  127. package/templates/firebase/lib/features/onboarding/providers/onboarding_provider.dart +30 -3
  128. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +13 -4
  129. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +3 -4
  130. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +2 -4
  131. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +2 -1
  132. package/templates/firebase/lib/features/settings/settings_page.dart +152 -11
  133. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +58 -21
  134. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +12 -12
  135. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +1 -4
  136. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +2 -2
  137. package/templates/firebase/lib/features/settings/ui/components/create_password_sheet.dart +141 -0
  138. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +0 -1
  139. package/templates/firebase/lib/features/settings/ui/widgets/admin_card.dart +42 -38
  140. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +17 -2
  141. package/templates/firebase/lib/features/subscriptions/api/entities/subscription_entity.dart +3 -0
  142. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +12 -11
  143. package/templates/firebase/lib/features/subscriptions/repositories/subscription_repository.dart +25 -2
  144. package/templates/firebase/lib/features/subscriptions/ui/component/active_premium_content.dart +319 -143
  145. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_minimal.dart +1 -5
  146. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_row.dart +1 -5
  147. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_with_switch.dart +2 -5
  148. package/templates/firebase/lib/features/subscriptions/ui/component/premium_content.dart +1 -4
  149. package/templates/firebase/lib/features/subscriptions/ui/widgets/feature_line.dart +1 -2
  150. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_banner.dart +2 -7
  151. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_feature.dart +2 -4
  152. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +1 -4
  153. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +0 -3
  154. package/templates/firebase/lib/i18n/en.i18n.json +54 -7
  155. package/templates/firebase/lib/i18n/es.i18n.json +54 -7
  156. package/templates/firebase/lib/i18n/pt.i18n.json +54 -7
  157. package/templates/firebase/lib/main.dart +11 -2
  158. package/templates/firebase/lib/router.dart +94 -13
  159. package/templates/firebase/pubspec.yaml +1 -1
  160. package/templates/firebase/test/core/data/api/fake_remote_config_api.dart +14 -0
  161. package/templates/firebase/test/core/states/user_state_notifier_test.dart +47 -3
  162. package/templates/firebase/test/core/web_viewport_scale_test.dart +68 -0
  163. package/templates/firebase/test/features/authentication/data/api/auth_api_fake.dart +15 -0
  164. package/templates/firebase/tool/design_check.dart +152 -0
  165. package/templates/firebase/web/index.html +162 -14
  166. package/templates/firebase/assets/images/review.png +0 -0
  167. package/templates/firebase/assets/images/update.png +0 -0
  168. package/templates/firebase/lib/core/guards/user_info_guard.dart +0 -61
  169. package/templates/firebase/lib/features/notifications/ui/components/notifications_header.dart +0 -32
@@ -1,3 +1,4 @@
1
+ import 'package:bart/bart/bart_model.dart';
1
2
  import 'package:flutter/foundation.dart' show kIsWeb;
2
3
  import 'package:flutter/material.dart';
3
4
  import 'package:flutter/services.dart';
@@ -82,6 +83,10 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
82
83
  label: 'Subpage Actions',
83
84
  builder: _buildAppBarSubpageActionsVariant,
84
85
  ),
86
+ ComponentPreviewVariant(
87
+ label: 'Menu opens sidebar',
88
+ builder: _buildAppBarMenuVariant,
89
+ ),
85
90
  ],
86
91
  );
87
92
  case 'Web Header':
@@ -397,6 +402,10 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
397
402
  label: 'With Label & Description',
398
403
  builder: _buildTextAreaWithLabelDescription,
399
404
  ),
405
+ ComponentPreviewVariant(
406
+ label: 'Variants',
407
+ builder: _buildTextAreaVariants,
408
+ ),
400
409
  ComponentPreviewVariant(
401
410
  label: 'States',
402
411
  builder: _buildTextAreaStates,
@@ -701,6 +710,96 @@ class _SidebarPreview extends StatelessWidget {
701
710
  }
702
711
  }
703
712
 
713
+ /// A [KasySidebar] wired to the live Home configuration (real pages) with
714
+ /// highlight-only navigation, so the showcase mirrors the actual app. Shared by
715
+ /// the sidebar previews and the app-bar drawer demo.
716
+ class _DemoSidebar extends StatefulWidget {
717
+ const _DemoSidebar({this.isDrawer = false});
718
+
719
+ final bool isDrawer;
720
+
721
+ @override
722
+ State<_DemoSidebar> createState() => _DemoSidebarState();
723
+ }
724
+
725
+ class _DemoSidebarState extends State<_DemoSidebar> {
726
+ final ValueNotifier<int> _current = ValueNotifier<int>(0);
727
+
728
+ // The same four tabs the live app wires into the sidebar (Home / Support /
729
+ // Notifications / Settings); pages are stubs since this is a visual demo.
730
+ late final List<BartMenuRoute> _routes = <BartMenuRoute>[
731
+ BartMenuRoute.bottomBar(
732
+ label: 'Home', icon: KasyIcons.home, path: 'home', pageBuilder: _stub),
733
+ BartMenuRoute.bottomBar(
734
+ label: 'Support',
735
+ icon: KasyIcons.help,
736
+ path: 'support',
737
+ pageBuilder: _stub),
738
+ BartMenuRoute.bottomBar(
739
+ label: 'Notifications',
740
+ icon: KasyIcons.notification,
741
+ path: 'notifications',
742
+ pageBuilder: _stub),
743
+ BartMenuRoute.bottomBar(
744
+ label: 'Settings',
745
+ icon: KasyIcons.settings,
746
+ path: 'settings',
747
+ pageBuilder: _stub),
748
+ ];
749
+
750
+ static Widget _stub(BuildContext _, BuildContext _, RouteSettings? _) =>
751
+ const SizedBox.shrink();
752
+
753
+ @override
754
+ void dispose() {
755
+ _current.dispose();
756
+ super.dispose();
757
+ }
758
+
759
+ @override
760
+ Widget build(BuildContext context) {
761
+ return KasySidebar(
762
+ routes: _routes,
763
+ currentItem: _current,
764
+ onTapItem: (i) => _current.value = i, // highlight only
765
+ isDrawer: widget.isDrawer,
766
+ );
767
+ }
768
+ }
769
+
770
+ /// Slides [child] in as a full-height drawer from the left ([fromEnd] = right),
771
+ /// over a dim barrier — the mobile navigation-drawer pattern.
772
+ void _openSidebarDrawer(
773
+ BuildContext context, {
774
+ required bool fromEnd,
775
+ required Widget child,
776
+ }) {
777
+ showGeneralDialog<void>(
778
+ context: context,
779
+ barrierDismissible: true,
780
+ barrierLabel: 'Dismiss',
781
+ barrierColor: Colors.black.withValues(alpha: 0.42),
782
+ transitionDuration: const Duration(milliseconds: 290),
783
+ pageBuilder: (ctx, anim, secAnim) => Align(
784
+ alignment: fromEnd ? Alignment.centerRight : Alignment.centerLeft,
785
+ child: SizedBox(height: double.infinity, child: child),
786
+ ),
787
+ transitionBuilder: (ctx, anim, secAnim, c) {
788
+ final curved = CurvedAnimation(parent: anim, curve: Curves.easeOutCubic);
789
+ return SlideTransition(
790
+ position: Tween<Offset>(
791
+ begin: Offset(fromEnd ? 1.0 : -1.0, 0),
792
+ end: Offset.zero,
793
+ ).animate(curved),
794
+ child: FadeTransition(
795
+ opacity: Tween<double>(begin: 0.85, end: 1.0).animate(curved),
796
+ child: c,
797
+ ),
798
+ );
799
+ },
800
+ );
801
+ }
802
+
704
803
  // ─────────────────────────────────────────────────────────────────────────────
705
804
  // ─────────────────────────────────────────────────────────────────────────────
706
805
  // Tabs — interactive demos
@@ -1777,6 +1876,13 @@ Widget _buildAppBarSubpageActionsVariant(BuildContext context) {
1777
1876
  return const _AppBarPreview(variant: KasyAppBarStyle.subpageActions);
1778
1877
  }
1779
1878
 
1879
+ Widget _buildAppBarMenuVariant(BuildContext context) {
1880
+ return const _AppBarPreview(
1881
+ variant: KasyAppBarStyle.rootTab,
1882
+ withMenu: true,
1883
+ );
1884
+ }
1885
+
1780
1886
  Widget _buildWebHeaderDefaultVariant(BuildContext context) {
1781
1887
  return const _WebHeaderPreview();
1782
1888
  }
@@ -2785,6 +2891,34 @@ Widget _buildTextAreaWithLabelDescription(BuildContext context) {
2785
2891
  return const _TextAreaWithLabelDescriptionContent();
2786
2892
  }
2787
2893
 
2894
+ Widget _buildTextAreaVariants(BuildContext context) {
2895
+ return const Column(
2896
+ mainAxisSize: MainAxisSize.min,
2897
+ crossAxisAlignment: CrossAxisAlignment.stretch,
2898
+ children: [
2899
+ KasyTextArea(
2900
+ label: 'Primary Variant',
2901
+ hint: 'Primary style text area',
2902
+ description: 'Default variant with primary styling',
2903
+ ),
2904
+ SizedBox(height: KasySpacing.lg),
2905
+ KasyTextArea(
2906
+ label: 'Secondary Variant',
2907
+ hint: 'Secondary style text area',
2908
+ description: 'Secondary variant for surfaces',
2909
+ variant: KasyTextFieldVariant.secondary,
2910
+ ),
2911
+ SizedBox(height: KasySpacing.lg),
2912
+ KasyTextArea(
2913
+ label: 'Flat Variant',
2914
+ hint: 'Flat style text area',
2915
+ description: 'Primary fill, hairline border, no shadow',
2916
+ variant: KasyTextFieldVariant.flat,
2917
+ ),
2918
+ ],
2919
+ );
2920
+ }
2921
+
2788
2922
  Widget _buildTextAreaStates(BuildContext context) {
2789
2923
  return const Column(
2790
2924
  mainAxisSize: MainAxisSize.min,
@@ -3308,7 +3442,6 @@ class _ToastVariantsPreview extends StatelessWidget {
3308
3442
  title: 'Join a team',
3309
3443
  message:
3310
3444
  'You have been invited to join the team!',
3311
- icon: _logoChip(context),
3312
3445
  ),
3313
3446
  ),
3314
3447
  const SizedBox(height: KasySpacing.sm),
@@ -3381,16 +3514,6 @@ class _ToastVariantsPreview extends StatelessWidget {
3381
3514
  ),
3382
3515
  );
3383
3516
  }
3384
-
3385
- Widget _logoChip(BuildContext context) => Container(
3386
- width: 26,
3387
- height: 26,
3388
- decoration: BoxDecoration(
3389
- color: context.colors.onSurface,
3390
- borderRadius: BorderRadius.circular(KasyRadius.xs),
3391
- ),
3392
- child: Icon(KasyIcons.widgets, size: 15, color: context.colors.surface),
3393
- );
3394
3517
  }
3395
3518
 
3396
3519
  // — Toast: static cards preview (all 5 tones, close buttons functional) —
@@ -3405,29 +3528,16 @@ class _ToastStaticPreview extends StatefulWidget {
3405
3528
  class _ToastStaticPreviewState extends State<_ToastStaticPreview> {
3406
3529
  final Set<int> _dismissed = {};
3407
3530
 
3408
- Widget _logoChip() => Builder(
3409
- builder: (ctx) => Container(
3410
- width: 26,
3411
- height: 26,
3412
- decoration: BoxDecoration(
3413
- color: ctx.colors.onSurface,
3414
- borderRadius: BorderRadius.circular(KasyRadius.xs),
3415
- ),
3416
- child:
3417
- Icon(KasyIcons.widgets, size: 15, color: ctx.colors.surface),
3418
- ),
3419
- );
3420
-
3421
3531
  @override
3422
3532
  Widget build(BuildContext context) {
3423
3533
  final items = <(int, KasyToast)>[
3424
3534
  (
3425
3535
  0,
3536
+ // Default (neutral) tone renders the app brand logo automatically.
3426
3537
  KasyToast(
3427
3538
  title: 'Join a team',
3428
3539
  message:
3429
3540
  'You have been invited to join the team!',
3430
- icon: _logoChip(),
3431
3541
  onClose: () => setState(() => _dismissed.add(0)),
3432
3542
  ),
3433
3543
  ),
@@ -3869,7 +3979,11 @@ class _DesktopMockContent extends StatelessWidget {
3869
3979
  class _AppBarPreview extends StatelessWidget {
3870
3980
  final KasyAppBarStyle variant;
3871
3981
 
3872
- const _AppBarPreview({required this.variant});
3982
+ /// Shows a hamburger leading that opens a [KasySidebar] drawer from the left —
3983
+ /// the mobile "menu opens navigation" pattern.
3984
+ final bool withMenu;
3985
+
3986
+ const _AppBarPreview({required this.variant, this.withMenu = false});
3873
3987
 
3874
3988
  @override
3875
3989
  Widget build(BuildContext context) {
@@ -3881,6 +3995,20 @@ class _AppBarPreview extends StatelessWidget {
3881
3995
  tooltip: 'More',
3882
3996
  onPressed: () {},
3883
3997
  );
3998
+ final Widget? leading = withMenu
3999
+ ? KasyChromeOrbIconButton(
4000
+ icon: KasyIcons.menu,
4001
+ iconSize: 20,
4002
+ foregroundColor: context.colors.onSurface,
4003
+ fillColor: kasyChromeOrbFillColor(context),
4004
+ tooltip: 'Menu',
4005
+ onPressed: () => _openSidebarDrawer(
4006
+ context,
4007
+ fromEnd: false,
4008
+ child: const _DemoSidebar(isDrawer: true),
4009
+ ),
4010
+ )
4011
+ : null;
3884
4012
  final KasyColors c = context.colors;
3885
4013
  // KasyAppBar hides itself at desktop width; the preview must always show it,
3886
4014
  // so pin a phone-sized MediaQuery just for the bar's own width check.
@@ -3909,9 +4037,10 @@ class _AppBarPreview extends StatelessWidget {
3909
4037
  right: 0,
3910
4038
  child: KasyAppBar(
3911
4039
  useSafeArea: false,
3912
- title: 'Preview',
4040
+ title: withMenu ? 'Home' : 'Preview',
3913
4041
  style: variant,
3914
4042
  onBack: () {},
4043
+ leading: leading,
3915
4044
  trailing: variant == KasyAppBarStyle.subpageActions
3916
4045
  ? trailing
3917
4046
  : null,
@@ -7593,15 +7722,15 @@ class _RealCardContent extends StatelessWidget {
7593
7722
  children: [
7594
7723
  Text(
7595
7724
  'Alex Johnson',
7596
- style: TextStyle(
7597
- fontSize: 14,
7598
- fontWeight: FontWeight.w600,
7725
+ style: context.kasyTextTheme.cardTitle.copyWith(
7599
7726
  color: c.onSurface,
7600
7727
  ),
7601
7728
  ),
7602
7729
  Text(
7603
7730
  '@alexjohnson · 2h ago',
7604
- style: TextStyle(fontSize: 12, color: c.muted),
7731
+ style: context.kasyTextTheme.cardSubtitle.copyWith(
7732
+ color: c.muted,
7733
+ ),
7605
7734
  ),
7606
7735
  ],
7607
7736
  ),
@@ -7650,8 +7779,7 @@ class _RealCardContent extends StatelessWidget {
7650
7779
  'Golden hour light made every single step worth it.',
7651
7780
  maxLines: 3,
7652
7781
  overflow: TextOverflow.ellipsis,
7653
- style: TextStyle(
7654
- fontSize: 13,
7782
+ style: context.textTheme.bodySmall?.copyWith(
7655
7783
  color: c.onSurface,
7656
7784
  height: 1.5,
7657
7785
  ),
@@ -7716,9 +7844,7 @@ class _SkeletonControls extends StatelessWidget {
7716
7844
  // Label
7717
7845
  Text(
7718
7846
  'Animation',
7719
- style: TextStyle(
7720
- fontSize: 12,
7721
- fontWeight: FontWeight.w600,
7847
+ style: context.kasyTextTheme.sectionLabel.copyWith(
7722
7848
  color: c.muted,
7723
7849
  letterSpacing: 0.4,
7724
7850
  ),
@@ -7783,7 +7909,9 @@ class _SkeletonControls extends StatelessWidget {
7783
7909
  ),
7784
7910
  Text(
7785
7911
  'Toggle skeleton loading state',
7786
- style: TextStyle(fontSize: 11, color: c.muted),
7912
+ style: context.kasyTextTheme.caption.copyWith(
7913
+ color: c.muted,
7914
+ ),
7787
7915
  ),
7788
7916
  ],
7789
7917
  ),
@@ -7933,15 +8061,15 @@ class _SkeletonListPreviewState extends State<_SkeletonListPreview> {
7933
8061
  children: [
7934
8062
  Text(
7935
8063
  item.title,
7936
- style: TextStyle(
7937
- fontSize: 14,
7938
- fontWeight: FontWeight.w600,
8064
+ style: context.kasyTextTheme.rowTitle.copyWith(
7939
8065
  color: c.onSurface,
7940
8066
  ),
7941
8067
  ),
7942
8068
  Text(
7943
8069
  item.subtitle,
7944
- style: TextStyle(fontSize: 12, color: c.muted),
8070
+ style: context.kasyTextTheme.cardSubtitle.copyWith(
8071
+ color: c.muted,
8072
+ ),
7945
8073
  ),
7946
8074
  ],
7947
8075
  ),
@@ -8086,8 +8214,7 @@ class _SkeletonTextPreviewState extends State<_SkeletonTextPreview> {
8086
8214
  Widget _buildRealText(BuildContext context) {
8087
8215
  return Text(
8088
8216
  _kParagraph,
8089
- style: TextStyle(
8090
- fontSize: 16,
8217
+ style: context.textTheme.bodyLarge?.copyWith(
8091
8218
  height: 1.6,
8092
8219
  color: context.colors.onSurface,
8093
8220
  ),
@@ -8192,9 +8319,7 @@ class _SkeletonProfilePreviewState extends State<_SkeletonProfilePreview> {
8192
8319
  // Name
8193
8320
  Text(
8194
8321
  'Alex Johnson',
8195
- style: TextStyle(
8196
- fontSize: 20,
8197
- fontWeight: FontWeight.w700,
8322
+ style: context.textTheme.headlineSmall?.copyWith(
8198
8323
  color: c.onSurface,
8199
8324
  ),
8200
8325
  ),
@@ -8202,14 +8327,19 @@ class _SkeletonProfilePreviewState extends State<_SkeletonProfilePreview> {
8202
8327
  // Handle
8203
8328
  Text(
8204
8329
  '@alexjohnson',
8205
- style: TextStyle(fontSize: 13, color: c.muted),
8330
+ style: context.kasyTextTheme.cardSubtitle.copyWith(
8331
+ color: c.muted,
8332
+ ),
8206
8333
  ),
8207
8334
  const SizedBox(height: KasySpacing.smd),
8208
8335
  // Bio
8209
8336
  Text(
8210
8337
  'Product designer & outdoor enthusiast. Building tools that help people do more with less.',
8211
8338
  textAlign: TextAlign.center,
8212
- style: TextStyle(fontSize: 13, color: c.onSurface, height: 1.5),
8339
+ style: context.textTheme.bodySmall?.copyWith(
8340
+ color: c.onSurface,
8341
+ height: 1.5,
8342
+ ),
8213
8343
  ),
8214
8344
  const SizedBox(height: KasySpacing.md),
8215
8345
  // Stats row
@@ -8263,15 +8393,15 @@ class _StatCell extends StatelessWidget {
8263
8393
  children: [
8264
8394
  Text(
8265
8395
  value,
8266
- style: TextStyle(
8267
- fontSize: 17,
8268
- fontWeight: FontWeight.w700,
8396
+ style: context.textTheme.titleMedium?.copyWith(
8269
8397
  color: c.onSurface,
8270
8398
  ),
8271
8399
  ),
8272
8400
  Text(
8273
8401
  label,
8274
- style: TextStyle(fontSize: 11, color: c.muted),
8402
+ style: context.kasyTextTheme.caption.copyWith(
8403
+ color: c.muted,
8404
+ ),
8275
8405
  ),
8276
8406
  ],
8277
8407
  ),
@@ -8497,9 +8627,7 @@ class _SkeletonGridPreviewState extends State<_SkeletonGridPreview> {
8497
8627
  children: [
8498
8628
  Text(
8499
8629
  item.title,
8500
- style: TextStyle(
8501
- fontSize: 13,
8502
- fontWeight: FontWeight.w600,
8630
+ style: context.kasyTextTheme.cardTitle.copyWith(
8503
8631
  color: c.onSurface,
8504
8632
  ),
8505
8633
  maxLines: 1,
@@ -8508,7 +8636,9 @@ class _SkeletonGridPreviewState extends State<_SkeletonGridPreview> {
8508
8636
  const SizedBox(height: 2),
8509
8637
  Text(
8510
8638
  item.subtitle,
8511
- style: TextStyle(fontSize: 11, color: c.muted),
8639
+ style: context.kasyTextTheme.cardSubtitle.copyWith(
8640
+ color: c.muted,
8641
+ ),
8512
8642
  maxLines: 1,
8513
8643
  overflow: TextOverflow.ellipsis,
8514
8644
  ),
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
3
  import 'package:kasy_kit/components/kasy_app_bar.dart';
4
4
  import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
5
5
  import 'package:kasy_kit/core/states/components/maybe_ask_rating.dart';
6
+ import 'package:kasy_kit/core/states/components/maybe_show_update_available.dart';
6
7
  import 'package:kasy_kit/core/states/components/maybe_show_update_bottom_sheet.dart';
7
8
  import 'package:kasy_kit/core/states/components/maybeshow_component.dart';
8
9
  import 'package:kasy_kit/core/theme/theme.dart';
@@ -21,6 +22,9 @@ class HomePage extends ConsumerWidget {
21
22
  Widget build(BuildContext context, WidgetRef ref) {
22
23
  return ConditionalWidgetsEvents(
23
24
  eventWidgets: [
25
+ // A required/optional store update pre-empts every other start-up
26
+ // prompt — if the user is behind, fix that before anything else.
27
+ MaybeShowUpdateAvailable(),
24
28
  MaybeAskForReview(),
25
29
  MaybeAskForRating(),
26
30
  MaybeShowPremiumPage(),