kasy-cli 1.39.0 → 1.40.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 (33) hide show
  1. package/lib/scaffold/CHANGELOG.json +23 -0
  2. package/package.json +1 -1
  3. package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +27 -27
  4. package/templates/firebase/AGENTS.md +2 -2
  5. package/templates/firebase/DESIGN_SYSTEM.md +3 -2
  6. package/templates/firebase/lib/components/components.dart +6 -1
  7. package/templates/firebase/lib/components/kasy_app_bar.dart +11 -1
  8. package/templates/firebase/lib/components/kasy_bottom_sheet.dart +25 -7
  9. package/templates/firebase/lib/components/kasy_menu.dart +902 -0
  10. package/templates/firebase/lib/components/kasy_popover.dart +267 -0
  11. package/templates/firebase/lib/components/kasy_sidebar.dart +20 -10
  12. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +23 -7
  13. package/templates/firebase/lib/core/chrome/app_bar_config.dart +1 -1
  14. package/templates/firebase/lib/core/navigation/kasy_route_observer.dart +8 -0
  15. package/templates/firebase/lib/core/rating/widgets/review_popup.dart +96 -14
  16. package/templates/firebase/lib/core/theme/texts.dart +25 -0
  17. package/templates/firebase/lib/core/web_viewport_scale.dart +8 -7
  18. package/templates/firebase/lib/features/home/home_components_page.dart +23 -4
  19. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +363 -13
  20. package/templates/firebase/lib/features/settings/ui/components/admin/admin_home_widgets.dart +4 -0
  21. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +38 -94
  22. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +32 -107
  23. package/templates/firebase/lib/features/subscriptions/ui/widgets/comparison_table.dart +21 -21
  24. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_close_button.dart +35 -27
  25. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +22 -17
  26. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +12 -7
  27. package/templates/firebase/lib/i18n/en.i18n.json +6 -2
  28. package/templates/firebase/lib/i18n/es.i18n.json +6 -2
  29. package/templates/firebase/lib/i18n/pt.i18n.json +6 -2
  30. package/templates/firebase/lib/router.dart +2 -0
  31. package/templates/firebase/pubspec.yaml +1 -1
  32. package/templates/firebase/test/app_bar_config_test.dart +70 -0
  33. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_banner.dart +0 -81
@@ -1,4 +1,27 @@
1
1
  {
2
+ "1.40.0": {
3
+ "modules": {
4
+ "components": {
5
+ "pt": "Novo componente Kasy Menu (menu de ações) + Popover. É aquele menu suspenso de ações que se adapta sozinho à plataforma: no desktop abre como um balão ancorado no botão (popover), no celular vira bottom sheet. Cada item pode ter ícone, descrição, atalho de teclado e submenu; dá pra agrupar em seções (inclusive uma 'zona de perigo' para ações destrutivas, em vermelho) e usar seleção única ou múltipla. Apresente com showKasyMenu (adapta automático, mas dá pra forçar popover ou sheet). Já está registrado na tela de Componentes com preview.",
6
+ "en": "New Kasy Menu (action menu) + Popover component. It is the dropdown action menu that adapts to the platform on its own: on desktop it opens as a balloon anchored to the button (popover), on mobile it becomes a bottom sheet. Each item can have an icon, description, keyboard shortcut and submenu; you can group items into sections (including a red 'Danger zone' for destructive actions) and use single or multiple selection. Present it with showKasyMenu (adapts automatically, but you can force popover or sheet). Already registered on the Components screen with a preview.",
7
+ "es": "Nuevo componente Kasy Menu (menú de acciones) + Popover. Es ese menú desplegable de acciones que se adapta solo a la plataforma: en escritorio abre como un globo anclado al botón (popover), en el móvil se vuelve bottom sheet. Cada ítem puede tener icono, descripción, atajo de teclado y submenú; puedes agrupar ítems en secciones (incluida una 'zona de peligro' en rojo para acciones destructivas) y usar selección única o múltiple. Preséntalo con showKasyMenu (se adapta automáticamente, pero puedes forzar popover o sheet). Ya está registrado en la pantalla de Componentes con vista previa."
8
+ },
9
+ "core": {
10
+ "pt": "Navegação ganhou um observador de rotas compartilhado (KasyRouteObserver) no navigator raiz: as telas conseguem reagir quando voltam a aparecer depois que algo aberto por cima delas é fechado (via didPopNext). É a base que mantém o novo Kasy Menu se comportando direito ao abrir/fechar; já vem ligado no router.",
11
+ "en": "Navigation got a shared route observer (KasyRouteObserver) on the root navigator: screens can react when they are revealed again after something opened on top of them is dismissed (via didPopNext). It is the foundation that keeps the new Kasy Menu behaving correctly as it opens/closes; it comes wired into the router.",
12
+ "es": "La navegación ganó un observador de rutas compartido (KasyRouteObserver) en el navigator raíz: las pantallas pueden reaccionar cuando vuelven a aparecer luego de que algo abierto encima de ellas se cierra (vía didPopNext). Es la base que mantiene el nuevo Kasy Menu comportándose bien al abrir/cerrar; ya viene conectado en el router."
13
+ }
14
+ }
15
+ },
16
+ "1.39.1": {
17
+ "modules": {
18
+ "core": {
19
+ "pt": "Ajuste fino da proporção da web: a escala passou de 0.95 para 0.93 (a interface fica um pouco menor, mais perto do tamanho do app nativo). É só calibragem do mesmo knob único (kWebViewportScale) em todos os breakpoints; o nativo continua em 1.0 e dá pra desligar tudo numa linha (kWebViewportScaleEnabled).",
20
+ "en": "Fine-tuned the web proportion: the scale went from 0.95 to 0.93 (the interface is a touch smaller, closer to the native app size). It is just calibration of the same single knob (kWebViewportScale) across every breakpoint; native stays at 1.0 and the whole thing can be turned off in one line (kWebViewportScaleEnabled).",
21
+ "es": "Ajuste fino de la proporción web: la escala pasó de 0.95 a 0.93 (la interfaz queda un poco más pequeña, más cerca del tamaño del app nativo). Es solo calibración del mismo knob único (kWebViewportScale) en todos los breakpoints; el nativo se mantiene en 1.0 y todo se puede apagar en una línea (kWebViewportScaleEnabled)."
22
+ }
23
+ }
24
+ },
2
25
  "1.39.0": {
3
26
  "modules": {
4
27
  "core": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasy-cli",
3
- "version": "1.39.0",
3
+ "version": "1.40.0",
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"
@@ -4,7 +4,6 @@ local_notifications.js,1772398909034,0b44b177c4b8a705ad1b0e3d7532537aad7b230e947
4
4
  flutter.js,1777393608000,b42e549a02f5c1428b0ad85dbdc663d400a6d2db10cb8aefcc0fddd592fd6ceb
5
5
  firebase-messaging-sw.js,1772484605561,dba3c939f7c240e62f63d06408d5f70e45f1168d00df98726e6a7a20cbd3b0df
6
6
  favicon.png,1779810782545,c967afd3c3d4dd165ef2e5cf3f203c3982dfff6a95881859ed163d9c38d75512
7
- .last_build_id,1781475668120,4a8e13af757aaae5b1160a761d56555ef4370ca7572732cbfa8d98c989424ab0
8
7
  splash/img/light-4x.png,1780546027751,abf0e55345e532a4645fe14b01c2948b07c78df50cc6da23648837dfb6271c74
9
8
  splash/img/light-3x.png,1780546027699,5f02c94032cb7053807ce89603b109f14ea2740eaa3145dc76c924cbe1d534a9
10
9
  splash/img/light-2x.png,1780546027647,3c073b9da154ac8be530f8beefa3579246f223acd819e856a2db000fdabcb67e
@@ -47,29 +46,30 @@ assets/assets/images/icon_android.png,1779810762467,db0f6c9ff904d4571ab40d461100
47
46
  assets/assets/images/icon.png,1779745964920,194820ecc4eed7bac14b5605916d87093b4571a28f3ca24863ac09c783343488
48
47
  assets/assets/images/favicon.png,1779745964922,194820ecc4eed7bac14b5605916d87093b4571a28f3ca24863ac09c783343488
49
48
  assets/assets/images/empty_notifications.jpg,1772398653078,cbb7d57f7cf305cf33ee8cbd15544eb1020670a1a8100f1328b49ca61d76fb7e
50
- assets/assets/icons/google_play_games.png,1772398653083,533c1d87d7be8690ab173ef42aa92ee8bac3b6e6859c0f97fe30daff2c843501
51
- assets/assets/icons/google.png,1772398653083,f423e7e7be1e06008d45617d07f095f04da7fdcab9a56523f9e0633828e464e0
52
- assets/assets/icons/facebook.png,1772398653083,79ac67e449c1db63319d43329ca91f682b4e0bc6a0883b0dfb2a849e13ae6eb6
53
- assets/assets/icons/apple.png,1772398653083,2a737d8801ca81452b2978bb2e1ac72136acf1a4c338f2de2f77b65e9f6de1fa
54
- version.json,1781481118464,be3307736a7e600c6e90593352859f1c46bc97666d5cf21a16099effd2a0fed6
55
- flutter_service_worker.js,1781481120062,baeeaf9f4b8e6f40d3b0549429ceb70ca01f120db6a7ef2f60d5809233dc2205
56
- assets/FontManifest.json,1781481118640,4d84ab517c27984d36f9a3c8be6f2a72788c0c3985c1d5874297fef0a53407ca
57
- flutter_bootstrap.js,1781481074600,0955c783148666dcda1406de86f477f1f27ec80a4fc8186ba6cd9bd119d242f9
58
- assets/AssetManifest.bin,1781481118640,78bccb08a36307a400711a1d7e6868bd5f89a24e63439fb5b6747660323e4475
59
- index.html,1781481074612,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
60
- assets/AssetManifest.bin.json,1781481118640,50a11b51c3fc4cbed1b5ec3a4f466c6a6b75c481d08d4782c8191f99bdbdba9c
61
- assets/shaders/stretch_effect.frag,1781481118746,1a7d4ac2be40cf0a459dfb390ef08bcd740f37913ffdee8de3c2ea836a18410e
62
- assets/shaders/ink_sparkle.frag,1781481118746,1c8e222328206d1e06754f76fb53947aad38d62180aafad5298a3c6f510b173d
63
- assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1781481119831,12bc6bf55aad4657f62747b6c1c9b5c120a594ed3540db21729b6db3c847340d
64
- assets/fonts/MaterialIcons-Regular.otf,1781481119828,0d07767255068e38fcf81755fd997bb15d94d6ef83bb81bd0149f974bfe359a0
65
- main.dart.js_3.part.js,1781481106966,7dd538a2118493eb8ff1a33ec5c2dd6d355a9a61290f1d8d1e6ad7bf8cc0465f
66
- main.dart.js_1.part.js,1781481106961,a9d49c1a6aff2181d2d352ebe6a64a58c776ece7784ca86546b17be94ffe42b0
67
- assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w100.ttf,1781481119830,c16d95412fe2600bda1f53c2b8dc4bd82496f60f7c5a6ecfa180b0f866128712
68
- assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w400.ttf,1781481119832,edcd01ee13f6ec2285fd18863c0ce0efb0876b290fb427ad5c052f753b3210c8
69
- assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w600.ttf,1781481119834,008f1e974c9ff4e8d40d16221226e0d34d2298a7feb2bdea8c134a9333929c9c
70
- assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w500.ttf,1781481119830,3f68f958d0fa13077968a6767339c38ede8705bcadd0382078db6ec4bac96fa0
71
- assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w300.ttf,1781481119832,aebf5e4f6b1e6de1e61a866f813aa5b08efb4cdd108cc2831d40aad71398c88f
72
- assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w200.ttf,1781481119830,1f798186cfc3e2fcd54b15aa7b8695bc7c29b781c5058029a8d80ecdfa615904
73
- assets/NOTICES,1781481118640,a18efc42c5ed99b56481537d0f229ddd3add671c548a893aaf8766f30c854158
74
- assets/packages/lucide_icons_flutter/assets/lucide.ttf,1781481119830,9b000e47cddf9ada4a6b85fc9577fef7ed2ee261c9279e9a44e4753da28f83d3
75
- main.dart.js,1781481107454,c98fad1aa11ff6a9335379794ac025a501060a6759943e74365f185cc7a5e8ce
49
+ version.json,1781504123531,9c8bda011bf76cc12c7484af634650c1bddbb07450efd617f39b2ba26ee338ec
50
+ flutter_service_worker.js,1781504124974,baeeaf9f4b8e6f40d3b0549429ceb70ca01f120db6a7ef2f60d5809233dc2205
51
+ flutter_bootstrap.js,1781504070895,8f179b4fd4e2445cb3847d5c5d50f3494b9431636e2c741ce4dca87bdef2efcc
52
+ index.html,1781504070910,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
53
+ .last_build_id,1781504125200,fb2a0536078d7023bcc9d41eb14cc00d74532f9465fac7aa9b88dae42e4a249c
54
+ assets/AssetManifest.bin.json,1781504123707,dde7c9126864005877d3ddb3598257c32685e77e36d1586f4441152e19553aef
55
+ assets/FontManifest.json,1781504123706,4d84ab517c27984d36f9a3c8be6f2a72788c0c3985c1d5874297fef0a53407ca
56
+ assets/AssetManifest.bin,1781504123706,b4a445b9a7980358e4e684ecb630fe913953f0ae9d710f1680e6c4cc351c6f50
57
+ assets/shaders/stretch_effect.frag,1781504123803,1a7d4ac2be40cf0a459dfb390ef08bcd740f37913ffdee8de3c2ea836a18410e
58
+ assets/shaders/ink_sparkle.frag,1781504123803,1c8e222328206d1e06754f76fb53947aad38d62180aafad5298a3c6f510b173d
59
+ assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1781504124761,12bc6bf55aad4657f62747b6c1c9b5c120a594ed3540db21729b6db3c847340d
60
+ assets/assets/icons/facebook.svg,1781501956480,3ab79cd57328f232a9aa93936936529716bf4b201897f3b6eae67484af4da871
61
+ assets/assets/icons/google.svg,1781501956480,aad117cb37a3246dda5ad42e20ad27fafa7fbf7e68215abd3a5a32aed09c53d9
62
+ assets/assets/icons/apple_white.svg,1781501956479,3ba907c614ab6feed0ada8c0617ac1fb0673640436e112be09fad8010cb9a977
63
+ assets/assets/icons/apple_black.svg,1781501956479,1831370c613783b8e26217b69701ca0a4a61d081479a1345ae3b13f2f4b39022
64
+ assets/fonts/MaterialIcons-Regular.otf,1781504124775,63e747df6278f4804031880bd30f6837ae2768e3912afb5328b553fb9ed381c7
65
+ main.dart.js_3.part.js,1781504109925,8b5c438ea9b1cffe57a96cba936c34f7b0f2c16045339ea87fb32db6e1463c1f
66
+ main.dart.js_1.part.js,1781504109916,188da12a7134009601f42c62f1d3cf38e9ebeafe0bfcaf5d32aed852e1c841d0
67
+ assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w100.ttf,1781504124774,c16d95412fe2600bda1f53c2b8dc4bd82496f60f7c5a6ecfa180b0f866128712
68
+ assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w600.ttf,1781504124766,008f1e974c9ff4e8d40d16221226e0d34d2298a7feb2bdea8c134a9333929c9c
69
+ assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w400.ttf,1781504124764,edcd01ee13f6ec2285fd18863c0ce0efb0876b290fb427ad5c052f753b3210c8
70
+ assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w500.ttf,1781504124763,3f68f958d0fa13077968a6767339c38ede8705bcadd0382078db6ec4bac96fa0
71
+ assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w300.ttf,1781504124769,aebf5e4f6b1e6de1e61a866f813aa5b08efb4cdd108cc2831d40aad71398c88f
72
+ assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w200.ttf,1781504124774,1f798186cfc3e2fcd54b15aa7b8695bc7c29b781c5058029a8d80ecdfa615904
73
+ assets/NOTICES,1781504123707,6ca6b928af7f7208b55e193e670cc5ff8335ec7ef7fa895fbae99609ba9f769d
74
+ assets/packages/lucide_icons_flutter/assets/lucide.ttf,1781504124772,9b000e47cddf9ada4a6b85fc9577fef7ed2ee261c9279e9a44e4753da28f83d3
75
+ main.dart.js,1781504110701,17f0df6beb2983f6a15b397718bf04a9078c12c4d98d83f71866ae15097f6e38
@@ -64,8 +64,8 @@ Typography collapses `large`+`xlarge` into one "desktop" tier (same sizes); the
64
64
  - **Native mobile** is straightforward: it's the Mobile breakpoint and never
65
65
  scales the viewport.
66
66
  - **Web render scale** (`WebViewportScale`) is a *separate* concern: Flutter web
67
- renders ~10% large, so **desktop only** scales the whole UI to `0.95`. Tablet
68
- and mobile web render at natural size, like native. This is NOT the typography
67
+ renders ~10% large at every width, so the whole web UI (phone, tablet, desktop)
68
+ is scaled to `0.93`; native renders at natural `1.0`. This is NOT the typography
69
69
  scale — don't conflate the two.
70
70
  - **Screen width follows content type** (two tiers): read/form screens (Settings,
71
71
  Notifications) are contained + centred at `kKasyContentMaxWidth` (600) — set
@@ -107,11 +107,12 @@ separate concern from type sizes, sitting in a layer ON TOP of the responsive
107
107
  system — it does NOT replace breakpoints, the type ramp or spacing (turn it off
108
108
  and the app is still fully responsive, just larger). Flutter web renders ~10%
109
109
  larger than an equivalent native/HTML app at 100% zoom, **at every width**. So the
110
- whole web app — phone, tablet and desktop — is scaled to `0.95` (~5%; a true
110
+ whole web app — phone, tablet and desktop — is scaled to `0.93` (~7%; a true
111
111
  viewport scale, so layout, scrolling and hit-testing stay correct), making web
112
112
  read close to the native baseline with **no size jump across breakpoints**. `0.90`
113
113
  fully undoes the 10% but reads small on a monitor (native sizes are tuned for a
114
- phone held close), so `0.95` (the midpoint) is the calibrated default. Two knobs:
114
+ phone held close), so `0.93` (a touch past the midpoint, still short of a full
115
+ undo) is the calibrated default. Two knobs:
115
116
  `kWebViewportScaleEnabled` (master on/off — `false` makes web render at native
116
117
  `1.0`) and `kWebViewportScale` (the value). Two things are never scaled, both at
117
118
  native `1.0`: **native** itself (gated on `kIsWeb`, so iOS/Android/macOS/Windows
@@ -1,11 +1,14 @@
1
1
  /// Kasy UI components — Material-backed wrappers with a stable kit API.
2
2
  ///
3
- /// Includes screen chrome ([KasyAppBar], [KasyOverlayScaffold], [kasyOverlayPaddedSlivers]).
3
+ /// Includes screen chrome ([KasyAppBar], [KasyOverlayScaffold], [kasyOverlayPaddedSlivers])
4
+ /// and its per-screen desktop config ([KasyAppBarConfig], [KasyAppBarConfigurator]).
4
5
  ///
5
6
  /// Import:
6
7
  /// import 'package:kasy_kit/components/components.dart';
7
8
  library;
8
9
 
10
+ export 'package:kasy_kit/core/chrome/app_bar_config.dart';
11
+ export 'package:kasy_kit/core/chrome/app_bar_scope.dart';
9
12
  export 'kasy_accordion.dart';
10
13
  export 'kasy_alert.dart';
11
14
  export 'kasy_app_bar.dart';
@@ -21,7 +24,9 @@ export 'kasy_date_picker.dart';
21
24
  export 'kasy_dialog.dart';
22
25
  export 'kasy_drop_down.dart';
23
26
  export 'kasy_image_viewer.dart';
27
+ export 'kasy_menu.dart';
24
28
  export 'kasy_otp_verification_bottom_sheet.dart';
29
+ export 'kasy_popover.dart';
25
30
  export 'kasy_screen.dart';
26
31
  export 'kasy_sidebar.dart';
27
32
  export 'kasy_skeleton.dart';
@@ -810,6 +810,12 @@ class KasyOverlayScaffold extends StatelessWidget {
810
810
  /// the overlay pattern (bar floats over full-height scroll content).
811
811
  final bool hideAppBarOnScroll;
812
812
 
813
+ /// Desktop only: adapt the shell's application bar for this screen. Receives the
814
+ /// shell default; return e.g. `bar.only(search: true)`. No-op on phone/tablet
815
+ /// (there the page chrome above is what shows). Shortcut for wrapping the body
816
+ /// in a [KasyAppBarConfigurator].
817
+ final KasyAppBarConfigure? applicationBar;
818
+
813
819
  const KasyOverlayScaffold({
814
820
  super.key,
815
821
  required this.title,
@@ -826,6 +832,7 @@ class KasyOverlayScaffold extends StatelessWidget {
826
832
  this.onRefresh,
827
833
  this.refreshIndicatorDisplacement = 48,
828
834
  this.hideAppBarOnScroll = false,
835
+ this.applicationBar,
829
836
  });
830
837
 
831
838
  @override
@@ -856,7 +863,7 @@ class KasyOverlayScaffold extends StatelessWidget {
856
863
  child: scrollView,
857
864
  );
858
865
  }
859
- return Scaffold(
866
+ final Widget scaffold = Scaffold(
860
867
  backgroundColor: bg,
861
868
  body: Stack(
862
869
  fit: StackFit.expand,
@@ -878,6 +885,9 @@ class KasyOverlayScaffold extends StatelessWidget {
878
885
  ],
879
886
  ),
880
887
  );
888
+ final KasyAppBarConfigure? configure = applicationBar;
889
+ if (configure == null) return scaffold;
890
+ return KasyAppBarConfigurator(configure: configure, child: scaffold);
881
891
  }
882
892
  }
883
893
 
@@ -188,13 +188,31 @@ class KasyBottomSheet extends StatelessWidget {
188
188
  ],
189
189
  if (actions.isNotEmpty) ...[
190
190
  const SizedBox(height: KasySpacing.lg),
191
- ...actions.expand(
192
- (w) => [
193
- w,
194
- if (w != actions.last)
195
- const SizedBox(height: KasySpacing.sm),
196
- ],
197
- ),
191
+ // As a desktop dialog, a non-icon form (e.g. edit name,
192
+ // create password) lays its buttons side by side with the
193
+ // primary on the right — the desktop convention — instead of
194
+ // the mobile full-width stack. Icon sheets stay stacked
195
+ // (they read as alerts, where stacked full-width is right).
196
+ // actions are authored primary-first (stack puts it on top),
197
+ // so reverse them here to land cancel-left / confirm-right.
198
+ if (floating && !hasIcon && actions.length >= 2)
199
+ Row(
200
+ children: [
201
+ for (int i = actions.length - 1; i >= 0; i--) ...[
202
+ if (i < actions.length - 1)
203
+ const SizedBox(width: KasySpacing.sm),
204
+ Expanded(child: actions[i]),
205
+ ],
206
+ ],
207
+ )
208
+ else
209
+ ...actions.expand(
210
+ (w) => [
211
+ w,
212
+ if (w != actions.last)
213
+ const SizedBox(height: KasySpacing.sm),
214
+ ],
215
+ ),
198
216
  ],
199
217
  ],
200
218
  ),