kasy-cli 1.36.0 → 1.36.1

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.
@@ -1,4 +1,18 @@
1
1
  {
2
+ "1.36.1": {
3
+ "modules": {
4
+ "onboarding": {
5
+ "pt": "Onboarding mais resistente a falhas: se a escrita no backend falhar (por exemplo, o documento do usuário ainda não foi criado logo após o cadastro), o loader não trava mais. O onboarding sempre conclui e o estado fica salvo localmente, e uma resposta que não salvou é registrada no log sem interromper a experiência.",
6
+ "en": "More resilient onboarding: if a backend write fails (e.g. the user document isn't created yet right after sign-up), the loader no longer gets stuck. Onboarding always completes with the state saved locally, and an answer that failed to save is logged without interrupting the flow.",
7
+ "es": "Onboarding más resistente a fallos: si una escritura en el backend falla (por ejemplo, el documento del usuario aún no se creó justo tras el registro), el loader ya no se queda atascado. El onboarding siempre concluye con el estado guardado localmente, y una respuesta que no se guardó se registra en el log sin interrumpir la experiencia."
8
+ },
9
+ "components": {
10
+ "pt": "Sidebar: a variante \"collapsed rail\" agora fica fina de verdade em tela estreita (mobile) no showcase, em vez de abrir larga. Só a gaveta explícita (isDrawer) força a abertura larga. O app real não muda (a sidebar só aparece de tablet pra cima).",
11
+ "en": "Sidebar: the \"collapsed rail\" variant now actually stays thin on narrow (mobile) widths in the showcase, instead of opening wide. Only the explicit drawer (isDrawer) forces the wide open. The real app is unchanged (the sidebar only shows from tablet up).",
12
+ "es": "Sidebar: la variante \"collapsed rail\" ahora se mantiene fina de verdad en anchos estrechos (móvil) en el showcase, en vez de abrirse ancha. Solo el cajón explícito (isDrawer) fuerza la apertura ancha. La app real no cambia (la sidebar solo aparece de tablet en adelante)."
13
+ }
14
+ }
15
+ },
2
16
  "1.36.0": {
3
17
  "modules": {
4
18
  "components": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasy-cli",
3
- "version": "1.36.0",
3
+ "version": "1.36.1",
4
4
  "description": "CLI for scaffolding production-ready Flutter SaaS apps with Firebase, Supabase, or API REST backends.",
5
5
  "bin": {
6
6
  "kasy": "./bin/kasy.js"
@@ -58,16 +58,16 @@ assets/assets/icons/google_play_games.png,1772398653083,533c1d87d7be8690ab173ef4
58
58
  assets/assets/icons/google.png,1772398653083,f423e7e7be1e06008d45617d07f095f04da7fdcab9a56523f9e0633828e464e0
59
59
  assets/assets/icons/facebook.png,1772398653083,79ac67e449c1db63319d43329ca91f682b4e0bc6a0883b0dfb2a849e13ae6eb6
60
60
  assets/assets/icons/apple.png,1772398653083,2a737d8801ca81452b2978bb2e1ac72136acf1a4c338f2de2f77b65e9f6de1fa
61
- version.json,1781383075783,1c93a56a819f36903677817066dbda0bae5785563a39d3a0a486246cf67107ac
62
- flutter_service_worker.js,1781383076286,baeeaf9f4b8e6f40d3b0549429ceb70ca01f120db6a7ef2f60d5809233dc2205
63
- assets/FontManifest.json,1781383075907,4d84ab517c27984d36f9a3c8be6f2a72788c0c3985c1d5874297fef0a53407ca
64
- assets/AssetManifest.bin.json,1781383075907,50a11b51c3fc4cbed1b5ec3a4f466c6a6b75c481d08d4782c8191f99bdbdba9c
65
- assets/AssetManifest.bin,1781383075907,78bccb08a36307a400711a1d7e6868bd5f89a24e63439fb5b6747660323e4475
66
- assets/shaders/ink_sparkle.frag,1781383075976,1c8e222328206d1e06754f76fb53947aad38d62180aafad5298a3c6f510b173d
67
- assets/shaders/stretch_effect.frag,1781383075975,1a7d4ac2be40cf0a459dfb390ef08bcd740f37913ffdee8de3c2ea836a18410e
68
- index.html,1781383002256,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
69
- flutter_bootstrap.js,1781383002244,fb98f6d7235b26ff8bcd0abce3ccbc26619afbc1956fd2bdceeaa847c25f8a4b
70
- main.dart.js_1.part.js,1781383058280,3d9563d7203157ebfa135f83b8cd2677443bf89b850e8a5adcffe56621b4fc16
71
- main.dart.js_3.part.js,1781383058289,133184cfaa83a56797dcb74c0b7fce45102298ea9e74f51f4a54ef6beca152c9
72
- assets/NOTICES,1781383075907,a18efc42c5ed99b56481537d0f229ddd3add671c548a893aaf8766f30c854158
73
- main.dart.js,1781383059126,b4cd4c8740826a7c4257dc5f49c9a402e5c6338f31eb39de8dd82762fa47be16
61
+ version.json,1781384419657,1c93a56a819f36903677817066dbda0bae5785563a39d3a0a486246cf67107ac
62
+ flutter_service_worker.js,1781384420789,baeeaf9f4b8e6f40d3b0549429ceb70ca01f120db6a7ef2f60d5809233dc2205
63
+ assets/FontManifest.json,1781384419796,4d84ab517c27984d36f9a3c8be6f2a72788c0c3985c1d5874297fef0a53407ca
64
+ assets/AssetManifest.bin,1781384419795,78bccb08a36307a400711a1d7e6868bd5f89a24e63439fb5b6747660323e4475
65
+ assets/AssetManifest.bin.json,1781384419795,50a11b51c3fc4cbed1b5ec3a4f466c6a6b75c481d08d4782c8191f99bdbdba9c
66
+ assets/shaders/stretch_effect.frag,1781384419911,1a7d4ac2be40cf0a459dfb390ef08bcd740f37913ffdee8de3c2ea836a18410e
67
+ assets/shaders/ink_sparkle.frag,1781384419911,1c8e222328206d1e06754f76fb53947aad38d62180aafad5298a3c6f510b173d
68
+ index.html,1781384363218,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
69
+ flutter_bootstrap.js,1781384363207,c9fd7e9f06eccf37ccbe2a39e88047779c1a67c67fe18febf6971479b7d6b64c
70
+ main.dart.js_1.part.js,1781384401644,7be2ce26bfb76e0142df09977ff2bbb058e5bee8c33d1ebf6c49aff2158d94d0
71
+ main.dart.js_3.part.js,1781384401652,6f1d83d415ac91cc4ca02b7ccefeae40d609713c7609b636502b68501dd44a38
72
+ assets/NOTICES,1781384419796,a18efc42c5ed99b56481537d0f229ddd3add671c548a893aaf8766f30c854158
73
+ main.dart.js,1781384402163,46f09db6091efb87263aad9c85327da8097b61c8c528564b1f7359829aef790b
@@ -302,19 +302,15 @@ class _KasySidebarState extends State<KasySidebar> {
302
302
  /// Viewport width below which the sidebar auto-collapses (tablet breakpoint).
303
303
  static const double _kBreakpoint = 1024.0;
304
304
 
305
- /// Below this (mobile), the rail is meant to be a wide drawer: it always opens
306
- /// full width and hides the collapse toggle (thinning makes no sense on a
307
- /// phone-width sheet). The collapse affordance only exists from tablet up.
305
+ /// Below this (mobile), the rail hides its collapse toggle: on a phone the
306
+ /// thin-vs-wide choice is a fixed pre-configuration (set via [isDrawer] for a
307
+ /// wide drawer, or the collapsed default for a thin rail), not a live toggle.
308
+ /// Width does NOT force wide here — a collapsed config stays thin on mobile.
308
309
  static const double _kMobileBreakpoint = 768.0;
309
310
 
310
311
  bool _isMobile(BuildContext context) =>
311
312
  MediaQuery.sizeOf(context).width < _kMobileBreakpoint;
312
313
 
313
- /// Drawer presentation — always wide, no collapse toggle. True on a phone-
314
- /// width viewport or when explicitly opened as a drawer ([isDrawer]).
315
- bool _wideDrawer(BuildContext context) =>
316
- widget.isDrawer || _isMobile(context);
317
-
318
314
  /// True when wired to Bart's navigation (real, tappable screens).
319
315
  bool get _connected =>
320
316
  widget.routes != null &&
@@ -382,9 +378,10 @@ class _KasySidebarState extends State<KasySidebar> {
382
378
 
383
379
  @override
384
380
  Widget build(BuildContext context) {
385
- // A drawer / phone-width rail always opens wide; otherwise honour the user's
386
- // explicit choice, falling back to the tablet auto-collapse.
387
- _collapsed = !_wideDrawer(context) &&
381
+ // Only an explicit drawer ([isDrawer]) forces wide; everything else honours
382
+ // the user's explicit choice, falling back to the auto-collapse on narrow
383
+ // viewports. Mobile is NOT forced wide — a collapsed config stays thin there.
384
+ _collapsed = !widget.isDrawer &&
388
385
  (_collapsePreference ?? _isViewportNarrow(context));
389
386
 
390
387
  final c = _colors;
@@ -622,8 +619,9 @@ class _KasySidebarState extends State<KasySidebar> {
622
619
  MediaQuery.sizeOf(context).width >= _kBreakpoint
623
620
  ? _kTopBandHeight
624
621
  : kasyAppBarBodyTopOverlap(context);
625
- // No collapse toggle on the mobile / drawer rail (it always opens wide).
626
- final bool showToggle = !_wideDrawer(context);
622
+ // No collapse toggle on a drawer (always wide) or on mobile (the thin/wide
623
+ // choice is a fixed pre-configuration there, not a live toggle).
624
+ final bool showToggle = !widget.isDrawer && !_isMobile(context);
627
625
  final bool anchoredLeft = widget.side == KasySidebarSide.left;
628
626
  // Brand wordmark — same artwork as the splash screen.
629
627
  final Widget logo = Image.asset(
@@ -108,8 +108,24 @@ class UserStateNotifier extends _$UserStateNotifier implements OnStartService {
108
108
  state = state.copyWith(user: const User.anonymous(onboarded: true));
109
109
  return;
110
110
  }
111
- final newUser = await _userRepository.setOnboarded(state.user);
112
- state = state.copyWith(user: newUser);
111
+ try {
112
+ final newUser = await _userRepository.setOnboarded(state.user);
113
+ state = state.copyWith(user: newUser);
114
+ } catch (e) {
115
+ // The user document may not exist yet: the backend's onUserRegistration
116
+ // trigger runs asynchronously and can lag a just-created anonymous account
117
+ // (Firestore update() throws on a missing doc). Onboarding is already
118
+ // remembered locally (flag above), so reflect it in state and move on
119
+ // instead of letting this bubble up and trap the onboarding loader.
120
+ _logger.w('setOnboarded failed, keeping local onboarded state: $e');
121
+ state = state.copyWith(
122
+ user: switch (state.user) {
123
+ final AuthenticatedUserData u => u.copyWith(onboarded: true),
124
+ final AnonymousUserData u => u.copyWith(onboarded: true),
125
+ final LoadingUserData _ => state.user,
126
+ },
127
+ );
128
+ }
113
129
  }
114
130
 
115
131
  /// Finish onboarding (or "continue as guest" from the sign-in screen) by
@@ -273,7 +273,9 @@ class _GuestContinueButtonState extends ConsumerState<_GuestContinueButton> {
273
273
  Widget build(BuildContext context) {
274
274
  return KasyButton(
275
275
  label: t.auth.signin.continue_without,
276
- variant: KasyButtonVariant.tertiary,
276
+ // Most subtle variant in the button preview: it's a tertiary action that
277
+ // should sit quietly below sign-in / social, not compete with them.
278
+ variant: KasyButtonVariant.ghost,
277
279
  expand: true,
278
280
  isLoading: _loading,
279
281
  onPressed: _loading ? null : _continue,
@@ -8,6 +8,7 @@ import 'package:kasy_kit/features/notifications/repositories/notifications_repos
8
8
  import 'package:kasy_kit/features/onboarding/models/user_info.dart';
9
9
  import 'package:kasy_kit/features/onboarding/providers/onboarding_model.dart';
10
10
  import 'package:kasy_kit/features/onboarding/repositories/user_infos_repository.dart';
11
+ import 'package:logger/logger.dart';
11
12
  import 'package:riverpod_annotation/riverpod_annotation.dart';
12
13
 
13
14
  part 'onboarding_provider.g.dart';
@@ -83,13 +84,18 @@ class OnboardingNotifier extends _$OnboardingNotifier {
83
84
  await userStateNotifier.continueAsGuest();
84
85
 
85
86
  // Now that the account exists, flush the answers collected during the
86
- // questions (gender, age, …) that had nowhere to go before.
87
+ // questions (gender, age, …) that had nowhere to go before. Best-effort:
88
+ // a failed profile write must never block onboarding from finishing.
87
89
  final userId = ref.read(userStateNotifierProvider).user.idOrNull;
88
90
  final pending = state.pendingUserInfo;
89
91
  if (userId != null && pending.isNotEmpty) {
90
92
  final repository = ref.read(userInfosRepositoryProvider);
91
93
  for (final info in pending) {
92
- await repository.save(userId, info);
94
+ try {
95
+ await repository.save(userId, info);
96
+ } catch (e) {
97
+ Logger().w('Failed to save onboarding answer: $e');
98
+ }
93
99
  }
94
100
  state = state.copyWith(pendingUserInfo: const []);
95
101
  }
@@ -27,10 +27,21 @@ class _OnboardingJournalLoaderState extends ConsumerState<OnboardingLoader> {
27
27
  // Run the real work (which now lazily creates the guest account) while the
28
28
  // loader animation plays, so its network latency is hidden under the
29
29
  // minimum display time instead of being added on top of it.
30
- await Future.wait([
31
- ref.onboardingNotifier.onOnboardingCompleted(),
32
- Future<void>.delayed(const Duration(milliseconds: 3500)),
33
- ]);
30
+ //
31
+ // The completion call MUST always run, even if a backend write fails (e.g.
32
+ // setting the onboarded flag on a user document the backend trigger has not
33
+ // created yet). Otherwise the user is trapped on this loader forever. The
34
+ // account already exists and onboarding is remembered locally, so moving
35
+ // on is safe.
36
+ final minimumDisplay = Future<void>.delayed(
37
+ const Duration(milliseconds: 3500),
38
+ );
39
+ try {
40
+ await ref.onboardingNotifier.onOnboardingCompleted();
41
+ } catch (e) {
42
+ debugPrint('OnboardingLoader: completion error (continuing anyway): $e');
43
+ }
44
+ await minimumDisplay;
34
45
  if (!mounted) return;
35
46
  widget.onCompleted();
36
47
  }
@@ -81,10 +81,10 @@ void main() {
81
81
 
82
82
  // Confirm dialog appears with destructive action.
83
83
  expect(find.text('Delete all notifications?'), findsOneWidget);
84
- expect(find.text('Delete'), findsOneWidget);
84
+ expect(find.text('Yes, delete'), findsOneWidget);
85
85
  expect(find.text('Cancel'), findsOneWidget);
86
86
 
87
- await tester.tap(find.text('Delete'));
87
+ await tester.tap(find.text('Yes, delete'));
88
88
  await tester.pumpAndSettle();
89
89
 
90
90
  // After deletion, no notification tiles remain.