kasy-cli 1.37.0 → 1.38.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.
- package/lib/scaffold/CHANGELOG.json +9 -0
- package/lib/scaffold/backends/api/patch/lib/core/data/api/user_api.dart +18 -0
- package/lib/scaffold/backends/patch-base-hashes.json +4 -4
- package/lib/scaffold/backends/supabase/patch/lib/core/data/api/user_api.dart +8 -0
- package/package.json +1 -1
- package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +23 -23
- package/templates/firebase/AGENTS.md +20 -10
- package/templates/firebase/DESIGN_SYSTEM.md +13 -0
- package/templates/firebase/README.en.md +1 -1
- package/templates/firebase/README.es.md +1 -1
- package/templates/firebase/README.md +1 -1
- package/templates/firebase/lib/components/kasy_app_bar.dart +57 -16
- package/templates/firebase/lib/components/kasy_bottom_sheet.dart +283 -66
- package/templates/firebase/lib/components/kasy_date_picker.dart +61 -46
- package/templates/firebase/lib/components/kasy_sidebar.dart +397 -28
- package/templates/firebase/lib/components/kasy_web_header.dart +11 -3
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +2 -213
- package/templates/firebase/lib/core/bottom_menu/sidebar_focus.dart +224 -0
- package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +12 -4
- package/templates/firebase/lib/core/chrome/web_header_scope.dart +20 -0
- package/templates/firebase/lib/core/data/api/user_api.dart +4 -0
- package/templates/firebase/lib/core/data/repositories/user_repository.dart +5 -0
- package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +525 -65
- package/templates/firebase/lib/core/dev_inspector/dev_inspector_info.dart +47 -0
- package/templates/firebase/lib/core/icons/kasy_icons.dart +16 -1
- package/templates/firebase/lib/core/states/user_state_notifier.dart +41 -0
- package/templates/firebase/lib/core/theme/universal_theme.dart +9 -0
- package/templates/firebase/lib/core/web_device_preview/png_clipboard.dart +14 -0
- package/templates/firebase/lib/core/web_device_preview/png_clipboard_io.dart +9 -0
- package/templates/firebase/lib/core/web_device_preview/png_clipboard_web.dart +36 -0
- package/templates/firebase/lib/core/web_device_preview/png_export_result.dart +2 -0
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +498 -466
- package/templates/firebase/lib/core/widgets/responsive_layout.dart +8 -0
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +11 -7
- package/templates/firebase/lib/features/home/home_components_page.dart +16 -6
- package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +7 -0
- package/templates/firebase/lib/features/notifications/ui/components/notification_settings_sheet.dart +23 -13
- package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +3 -0
- package/templates/firebase/lib/features/notifications/ui/widgets/web_notifications_bell.dart +262 -0
- package/templates/firebase/lib/features/settings/settings_page.dart +51 -38
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +549 -376
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +14 -4
- package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +266 -141
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +23 -30
- package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +30 -49
- package/templates/firebase/lib/i18n/en.i18n.json +23 -9
- package/templates/firebase/lib/i18n/es.i18n.json +23 -9
- package/templates/firebase/lib/i18n/pt.i18n.json +23 -9
- package/templates/firebase/lib/router.dart +43 -25
- package/templates/firebase/pubspec.yaml +1 -1
- package/templates/firebase/test/admin_shell_chrome_test.dart +104 -0
- package/templates/firebase/test/features/authentication/data/api/user_api_fake.dart +3 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +0 -53
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
+
"1.38.0": {
|
|
3
|
+
"modules": {
|
|
4
|
+
"components": {
|
|
5
|
+
"pt": "Sidebar agora suporta submenus expansíveis (grupos com sub-itens, como o menu suspenso de Configurações na Home): clicar no grupo abre/fecha os filhos, o item ativo do submenu fica em negrito forte (sem pílula) e o submenu fecha sozinho ao navegar para outra tela.",
|
|
6
|
+
"en": "Sidebar now supports expandable submenus (groups with sub-items, like the Settings dropdown on Home): tapping the group opens/closes its children, the active sub-item shows in strong bold (no pill) and the submenu closes on its own when you navigate to another screen.",
|
|
7
|
+
"es": "La sidebar ahora admite submenús expansibles (grupos con subítems, como el menú desplegable de Ajustes en la Home): tocar el grupo abre/cierra sus hijos, el ítem activo del submenú se muestra en negrita fuerte (sin píldora) y el submenú se cierra solo al navegar a otra pantalla."
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
},
|
|
2
11
|
"1.37.0": {
|
|
3
12
|
"modules": {
|
|
4
13
|
"components": {
|
|
@@ -44,6 +44,24 @@ class UserApi {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/// Watches the user's role so an admin promotion/demotion is picked up at
|
|
48
|
+
/// runtime (the shared user state refreshes when it changes). A REST backend
|
|
49
|
+
/// has no realtime channel, so this polls: it emits the current role at once
|
|
50
|
+
/// (mirroring the immediate snapshot of the Firebase/Supabase streams, which
|
|
51
|
+
/// the state notifier skips) and then re-checks periodically. The notifier
|
|
52
|
+
/// cancels the subscription on logout/dispose, which ends this generator.
|
|
53
|
+
Stream<String?> watchRole(String id) async* {
|
|
54
|
+
while (true) {
|
|
55
|
+
try {
|
|
56
|
+
final user = await get(id);
|
|
57
|
+
yield user?.role;
|
|
58
|
+
} catch (_) {
|
|
59
|
+
// Ignore transient network errors and keep polling.
|
|
60
|
+
}
|
|
61
|
+
await Future<void>.delayed(const Duration(seconds: 30));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
47
65
|
Future<void> update(UserEntity user) async {
|
|
48
66
|
try {
|
|
49
67
|
final data = user.toJson()..removeWhere((_, v) => v == null);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"api/android/app/src/main/AndroidManifest.xml": "0579c57068ee50d43837ef9c7bee884b6232e92975301c8305ce389282f68291",
|
|
3
3
|
"api/ios/Runner/AppDelegate.swift": "9f1027a03ad1fef2dab45464fbd7e98364b6a9cb6d2a6abe770ee9c45fa19122",
|
|
4
4
|
"api/lib/core/data/api/storage_api.dart": "d77fecc6923ea9544b6ec20571dcab5c8f3d38be81ec618b0cf4f1b08be7bbae",
|
|
5
|
-
"api/lib/core/data/api/user_api.dart": "
|
|
5
|
+
"api/lib/core/data/api/user_api.dart": "0e7d984dceada025b7e998c217c39da9b781de430fd0f4d2267aaa6439391d5f",
|
|
6
6
|
"api/lib/core/data/entities/json_converters.dart": "92a5efd595a03a862fa03e77ab3ca3d556523d0c9e0daca6e296e35d24b760a8",
|
|
7
7
|
"api/lib/core/data/entities/user_entity.dart": "5c1e56e4be3ba2ed95cdcd276112a02b526f2a3fa7ee5bba08a3ec2b51153688",
|
|
8
8
|
"api/lib/environments.dart": "c41f0843a4d627b717579eec5607b7ff5c16369bdd4b2a82e081058a20ffb7a5",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"api/lib/features/subscriptions/api/entities/subscription_entity.dart": "20c1d75ed9d88acb96e94a592dcb4de0f63c792302ea07df53cebbdeb6d0cf7e",
|
|
31
31
|
"api/lib/features/subscriptions/api/stripe_backend_api.dart": "e370bd05211462f2fc94c69af1749eb5330f997e9ec5e73e5c4e119999bf666f",
|
|
32
32
|
"api/lib/features/subscriptions/api/subscription_api.dart": "c7484c9301d16245748025a3d420a89397e9e956b2211b7ed001c073ff2e4449",
|
|
33
|
-
"api/README.md": "
|
|
33
|
+
"api/README.md": "3c3e4b7731ac6eb86f8fdce202ed7b7d681246bcf76fdb87b389e4c9dfdeac08",
|
|
34
34
|
"supabase/android/app/src/main/AndroidManifest.xml": "0579c57068ee50d43837ef9c7bee884b6232e92975301c8305ce389282f68291",
|
|
35
35
|
"supabase/ios/Runner/AppDelegate.swift": "9f1027a03ad1fef2dab45464fbd7e98364b6a9cb6d2a6abe770ee9c45fa19122",
|
|
36
36
|
"supabase/lib/core/data/api/storage_api.dart": "d77fecc6923ea9544b6ec20571dcab5c8f3d38be81ec618b0cf4f1b08be7bbae",
|
|
37
|
-
"supabase/lib/core/data/api/user_api.dart": "
|
|
37
|
+
"supabase/lib/core/data/api/user_api.dart": "0e7d984dceada025b7e998c217c39da9b781de430fd0f4d2267aaa6439391d5f",
|
|
38
38
|
"supabase/lib/core/data/entities/json_converters.dart": "92a5efd595a03a862fa03e77ab3ca3d556523d0c9e0daca6e296e35d24b760a8",
|
|
39
39
|
"supabase/lib/core/data/entities/user_entity.dart": "5c1e56e4be3ba2ed95cdcd276112a02b526f2a3fa7ee5bba08a3ec2b51153688",
|
|
40
40
|
"supabase/lib/environments.dart": "c41f0843a4d627b717579eec5607b7ff5c16369bdd4b2a82e081058a20ffb7a5",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"supabase/lib/features/subscriptions/api/stripe_backend_api.dart": "e370bd05211462f2fc94c69af1749eb5330f997e9ec5e73e5c4e119999bf666f",
|
|
63
63
|
"supabase/lib/features/subscriptions/api/subscription_api.dart": "c7484c9301d16245748025a3d420a89397e9e956b2211b7ed001c073ff2e4449",
|
|
64
64
|
"supabase/lib/google_auth_options.dart": "9d3ab9be5928f3100b9b217864d916edd1223d186e60ed130c35628812cace66",
|
|
65
|
-
"supabase/README.md": "
|
|
65
|
+
"supabase/README.md": "3c3e4b7731ac6eb86f8fdce202ed7b7d681246bcf76fdb87b389e4c9dfdeac08"
|
|
66
66
|
}
|
|
@@ -40,6 +40,14 @@ class UserApi {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
Stream<String?> watchRole(String id) {
|
|
44
|
+
return _client
|
|
45
|
+
.from('users')
|
|
46
|
+
.stream(primaryKey: ['id'])
|
|
47
|
+
.eq('id', id)
|
|
48
|
+
.map((rows) => rows.isEmpty ? null : rows.first['role'] as String?);
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
Future<void> update(UserEntity user) async {
|
|
44
52
|
try {
|
|
45
53
|
final data = user.toJson()..removeWhere((_, v) => v == null);
|
package/package.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
stripe_success.html,1781195415353,677b2fb430839866f32a33a751aae85b4c4819123be820f3f5b2f689b8eed00e
|
|
2
2
|
manifest.json,1779810782774,1b929ac7267ff738d05646106a0fac3591082b6f365051928a62518af30b3fd5
|
|
3
3
|
local_notifications.js,1772398909034,0b44b177c4b8a705ad1b0e3d7532537aad7b230e9472f93c820742b7548797b8
|
|
4
|
-
index.html,1781388256310,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
|
|
5
|
-
flutter_bootstrap.js,1781388256056,25f49550b20f234b84194d9e067725abdc33590343251086f6c7d147325dfb41
|
|
6
4
|
flutter.js,1777393608000,b42e549a02f5c1428b0ad85dbdc663d400a6d2db10cb8aefcc0fddd592fd6ceb
|
|
7
5
|
firebase-messaging-sw.js,1772484605561,dba3c939f7c240e62f63d06408d5f70e45f1168d00df98726e6a7a20cbd3b0df
|
|
8
6
|
favicon.png,1779810782545,c967afd3c3d4dd165ef2e5cf3f203c3982dfff6a95881859ed163d9c38d75512
|
|
9
|
-
.last_build_id,
|
|
7
|
+
.last_build_id,1781393235997,3217b88fca927fa7fc5a6d9c24dee562336c5b14ad2c6e8b9b7b1bacff2e4a42
|
|
10
8
|
splash/img/light-4x.png,1780546027751,abf0e55345e532a4645fe14b01c2948b07c78df50cc6da23648837dfb6271c74
|
|
11
9
|
splash/img/light-3x.png,1780546027699,5f02c94032cb7053807ce89603b109f14ea2740eaa3145dc76c924cbe1d534a9
|
|
12
10
|
splash/img/light-2x.png,1780546027647,3c073b9da154ac8be530f8beefa3579246f223acd819e856a2db000fdabcb67e
|
|
@@ -36,6 +34,15 @@ canvaskit/chromium/canvaskit.js.symbols,1777393830000,60722421e56edacbeb674cc3ef
|
|
|
36
34
|
canvaskit/chromium/canvaskit.js,1777393830000,10ea3badcf26e29aba851699c03e09bc35fea65cdc440a035bac263d6d02d665
|
|
37
35
|
assets/.env,1781370223435,777e85eb4de226724b28db8664404d0b73ae37f63fd66c955ac90c89cd491e7e
|
|
38
36
|
assets/packages/mixpanel_flutter/assets/mixpanel.js,1779806094332,1915455df317dd1a9f42c6f3ea5bbe3ee357614f89d994f157a2e821f37956c3
|
|
37
|
+
assets/packages/lucide_icons_flutter/assets/lucide.ttf,1779809590216,3813e3eb113e8be4c36c2082397dc3397d0ea7cd3a3b43eaecd61100b0256513
|
|
38
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w600.ttf,1779809590200,095ae41c43f72ec2997fa59fd91a0c83a36b345915e705f292f414cae6240dab
|
|
39
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w500.ttf,1779809590211,59d94414e1a3dd1732ff0b3ea50389c3e8b3915fe5a9b6163ba256a825e1e05b
|
|
40
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w400.ttf,1779809590202,9342632d6521314eb2ab7e902a8a6fe6c3d62a110f682bcc123e244e12c4a695
|
|
41
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w300.ttf,1779809590205,3bd1b359793f74027dada0e359d7d9dc712afd721db5ef30b095592e2e03e865
|
|
42
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w200.ttf,1779809590198,8026f9e7a751a47ace3ec60fdb7c6843a1d8051699fb28eb0a9fb1bd53904a66
|
|
43
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w100.ttf,1779809590208,1d81fb594eb42c3e8e43a36b89120a0a8a4c2dd858dec1a0c94de0e5b2208388
|
|
44
|
+
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1779806141382,010d647b6cab4e1a9b4398957353f43589903ce8dc760d05d894944b664e29bd
|
|
45
|
+
assets/fonts/MaterialIcons-Regular.otf,1662569016000,3a631b0761788d7cf2a71e8732a5b31288d6e7aa996c671c70d53a251bbd3848
|
|
39
46
|
assets/assets/images/splash_logo_light_android12.png,1779867497377,da2caee4f16230230dbb04d84913f0a9df2eae6bcb8f9d1ce83ed556cee6d094
|
|
40
47
|
assets/assets/images/splash_logo_light.png,1779867497286,643a1f45d6ef9458c4c0eed9f4889cc2adbdf1db616913215be66673a05a0066
|
|
41
48
|
assets/assets/images/splash_logo_dark_android12.png,1779867497464,b237d9f9035d419525a10b32889c30d44c706d62990b30bdc6bef911fe9352d6
|
|
@@ -53,23 +60,16 @@ assets/assets/icons/google_play_games.png,1772398653083,533c1d87d7be8690ab173ef4
|
|
|
53
60
|
assets/assets/icons/google.png,1772398653083,f423e7e7be1e06008d45617d07f095f04da7fdcab9a56523f9e0633828e464e0
|
|
54
61
|
assets/assets/icons/facebook.png,1772398653083,79ac67e449c1db63319d43329ca91f682b4e0bc6a0883b0dfb2a849e13ae6eb6
|
|
55
62
|
assets/assets/icons/apple.png,1772398653083,2a737d8801ca81452b2978bb2e1ac72136acf1a4c338f2de2f77b65e9f6de1fa
|
|
56
|
-
version.json,
|
|
57
|
-
flutter_service_worker.js,
|
|
58
|
-
assets/
|
|
59
|
-
|
|
60
|
-
assets/
|
|
61
|
-
assets/
|
|
62
|
-
assets/shaders/stretch_effect.frag,
|
|
63
|
-
assets/
|
|
64
|
-
|
|
65
|
-
main.dart.js_3.part.js,
|
|
66
|
-
main.dart.js_1.part.js,
|
|
67
|
-
assets/
|
|
68
|
-
|
|
69
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w600.ttf,1781388385468,008f1e974c9ff4e8d40d16221226e0d34d2298a7feb2bdea8c134a9333929c9c
|
|
70
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w100.ttf,1781388385446,c16d95412fe2600bda1f53c2b8dc4bd82496f60f7c5a6ecfa180b0f866128712
|
|
71
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w500.ttf,1781388385464,3f68f958d0fa13077968a6767339c38ede8705bcadd0382078db6ec4bac96fa0
|
|
72
|
-
assets/packages/lucide_icons_flutter/assets/lucide.ttf,1781388385448,9b000e47cddf9ada4a6b85fc9577fef7ed2ee261c9279e9a44e4753da28f83d3
|
|
73
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w200.ttf,1781388385451,1f798186cfc3e2fcd54b15aa7b8695bc7c29b781c5058029a8d80ecdfa615904
|
|
74
|
-
assets/NOTICES,1781388383326,a18efc42c5ed99b56481537d0f229ddd3add671c548a893aaf8766f30c854158
|
|
75
|
-
main.dart.js,1781388361400,b625f1b346dc2f1475ee99ebb1d698654e74183d737c91e1bbac020869096a84
|
|
63
|
+
version.json,1781400658272,495754f8591b89616bdad18717ffd8aa9275a8605889167170e6bc98ced40489
|
|
64
|
+
flutter_service_worker.js,1781400658740,baeeaf9f4b8e6f40d3b0549429ceb70ca01f120db6a7ef2f60d5809233dc2205
|
|
65
|
+
assets/FontManifest.json,1781400658381,4d84ab517c27984d36f9a3c8be6f2a72788c0c3985c1d5874297fef0a53407ca
|
|
66
|
+
index.html,1781400619641,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
|
|
67
|
+
assets/AssetManifest.bin,1781400658381,78bccb08a36307a400711a1d7e6868bd5f89a24e63439fb5b6747660323e4475
|
|
68
|
+
assets/AssetManifest.bin.json,1781400658381,50a11b51c3fc4cbed1b5ec3a4f466c6a6b75c481d08d4782c8191f99bdbdba9c
|
|
69
|
+
assets/shaders/stretch_effect.frag,1781400658449,1a7d4ac2be40cf0a459dfb390ef08bcd740f37913ffdee8de3c2ea836a18410e
|
|
70
|
+
assets/shaders/ink_sparkle.frag,1781400658449,1c8e222328206d1e06754f76fb53947aad38d62180aafad5298a3c6f510b173d
|
|
71
|
+
flutter_bootstrap.js,1781400619636,371abfca6d227535d1d7bf43ada40ba5256e04ae0aed40e29ccae33f964fa7f7
|
|
72
|
+
main.dart.js_3.part.js,1781400646444,6f52d11c8e140736cd046ce6b541ad8d8bf20e6ef709f39d8ef4eb3dddbece8b
|
|
73
|
+
main.dart.js_1.part.js,1781400646439,42136799a40b5acb14b5acf0a8cef39bbef13b4dc99ce808a7bc8a1f8cdd8b09
|
|
74
|
+
assets/NOTICES,1781400658382,a18efc42c5ed99b56481537d0f229ddd3add671c548a893aaf8766f30c854158
|
|
75
|
+
main.dart.js,1781400646862,c0154549b8162d55ff32593e679d1f3461e6f4ff744d9730cd87953b0244d8c9
|
|
@@ -41,15 +41,19 @@ colours, typography, spacing and dozens of ready widgets (`KasyButton`,
|
|
|
41
41
|
`showKasyConfirmDialog`, …). Reuse one before writing a new widget. Full token
|
|
42
42
|
reference: **`DESIGN_SYSTEM.md`**.
|
|
43
43
|
|
|
44
|
-
**Responsiveness is deliberate, not improvised.**
|
|
45
|
-
|
|
46
|
-
oscillates or guesses:
|
|
44
|
+
**Responsiveness is deliberate, not improvised.** The responsive system
|
|
45
|
+
(`DeviceType` in `lib/core/widgets/responsive_layout.dart`) has four tiers and the
|
|
46
|
+
type sizes are pinned per breakpoint, so the UI never oscillates or guesses:
|
|
47
47
|
|
|
48
|
-
|
|
|
49
|
-
| ---------- |
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
48
|
+
| DeviceType | Width | Notes |
|
|
49
|
+
| ---------- | ------------ | ------------------------------------------------ |
|
|
50
|
+
| `small` | `< 768` | native iOS/Android + phone-width web |
|
|
51
|
+
| `medium` | `768–1023` | tablet |
|
|
52
|
+
| `large` | `1024–1279` | desktop, authored baseline |
|
|
53
|
+
| `xlarge` | `>= 1280` | wide desktop (web-scale design target, 1280) |
|
|
54
|
+
|
|
55
|
+
Typography collapses `large`+`xlarge` into one "desktop" tier (same sizes); the
|
|
56
|
+
`1280` boundary matters for the web render scale (its design target), not type.
|
|
53
57
|
|
|
54
58
|
- **Typography** (`KasyTypeScale`): each role has an **explicit size per
|
|
55
59
|
breakpoint** — headings are largest on desktop and step down on smaller
|
|
@@ -63,6 +67,11 @@ oscillates or guesses:
|
|
|
63
67
|
renders ~10% large, so **desktop only** scales the whole UI to `0.95`. Tablet
|
|
64
68
|
and mobile web render at natural size, like native. This is NOT the typography
|
|
65
69
|
scale — don't conflate the two.
|
|
70
|
+
- **Screen width follows content type** (two tiers): read/form screens (Settings,
|
|
71
|
+
Notifications) are contained + centred at `kKasyContentMaxWidth` (600) — set
|
|
72
|
+
`KasyOverlayScaffold(maxContentWidth: kKasyContentMaxWidth)` or use `KasyScreen`;
|
|
73
|
+
feed/dashboard screens (Home) go full width + the 16 gutter. Mirror
|
|
74
|
+
Settings/Notifications, or Home. Full rule: **`DESIGN_SYSTEM.md`** → Internal-screen contract.
|
|
66
75
|
|
|
67
76
|
To re-tune sizes, edit `KasyTypeScale` in `lib/core/theme/type_scale.dart` —
|
|
68
77
|
nothing else. The live ramp (tabs per breakpoint) is under **Design System →
|
|
@@ -95,7 +104,8 @@ lib/
|
|
|
95
104
|
|
|
96
105
|
1. Read `DESIGN_SYSTEM.md` (tokens, typography roles, components).
|
|
97
106
|
2. Reuse a kit component before building one. Wrap new pages in `KasyScreen`
|
|
98
|
-
(or mirror an existing page's scaffold).
|
|
107
|
+
(or mirror an existing page's scaffold). Pick the width tier — contained
|
|
108
|
+
(mirror Settings/Notifications) vs full-width (mirror Home); see `DESIGN_SYSTEM.md`.
|
|
99
109
|
3. Use semantic typography roles (`pageTitle`, `sectionTitle`, `rowTitle`, …)
|
|
100
110
|
and tokens for **every** size, colour and spacing.
|
|
101
111
|
4. Strings via `context.t.*` (add keys to the i18n JSON, never inline text).
|
|
@@ -139,7 +149,7 @@ Recommended flow when the user wants newer kit code:
|
|
|
139
149
|
2. Run the relevant command:
|
|
140
150
|
- `kasy update` — shows what's new for the features in use
|
|
141
151
|
- `kasy update core` / `kasy update components` — refreshes core files / UI components
|
|
142
|
-
- `kasy update <feature>` — e.g. `revenuecat`, `ai_chat`, `
|
|
152
|
+
- `kasy update <feature>` — e.g. `revenuecat`, `ai_chat`, `widget` (re-applies that feature)
|
|
143
153
|
3. The command overwrites the files and prints which ones changed.
|
|
144
154
|
4. **Merge:** if the user had customized any overwritten file, reconcile their version
|
|
145
155
|
(in the previous git commit) with the new kit version. As the in-project agent,
|
|
@@ -222,6 +222,19 @@ New internal screens follow the sidebar / Home ruler:
|
|
|
222
222
|
- Haptics only on native (`if (!kIsWeb)`); "hide chrome on scroll" only on the
|
|
223
223
|
phone breakpoint (`width < 768`).
|
|
224
224
|
|
|
225
|
+
**Content width — two tiers (width follows content type):**
|
|
226
|
+
- *Read / form screens* (Settings, Notifications, Reminder) → **contained + centred**
|
|
227
|
+
at `kKasyContentMaxWidth` (600, in `core/widgets/responsive_layout.dart`). With the
|
|
228
|
+
overlay pattern, set `KasyOverlayScaffold(maxContentWidth: kKasyContentMaxWidth)`
|
|
229
|
+
(centres the column on desktop, keeps the 16 gutter on mobile); with `KasyScreen`
|
|
230
|
+
it is the default. Long lines and lone controls read badly stretched, so cap them.
|
|
231
|
+
The Settings desktop master/detail centres the **nav + detail as one unit**.
|
|
232
|
+
- *Browse / feed / dashboard screens* (Home) → **full width + the 16 gutter, on
|
|
233
|
+
purpose** — width helps a grid/feed. Do **not** contain these.
|
|
234
|
+
- Every tier shares the same family (16 gutter, `KasyCard`, sidebar + web header) —
|
|
235
|
+
only the max width changes. To mirror: copy **Settings / Notifications** for a
|
|
236
|
+
contained screen, **Home** for a full-width one.
|
|
237
|
+
|
|
225
238
|
---
|
|
226
239
|
|
|
227
240
|
## Keeping it consistent — the guard-rail (optional)
|
|
@@ -74,7 +74,7 @@ Force manually: `kasy run --rc=test` or `kasy run --rc=prod`. `--rc=auto` (defau
|
|
|
74
74
|
To update, edit `.vscode/launch.json` or pass via command line:
|
|
75
75
|
|
|
76
76
|
```sh
|
|
77
|
-
flutter run --dart-define=
|
|
77
|
+
flutter run --dart-define=RC_ANDROID_PROD_KEY=your_key
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
---
|
|
@@ -74,7 +74,7 @@ Forzar manualmente: `kasy run --rc=test` o `kasy run --rc=prod`. `--rc=auto` (po
|
|
|
74
74
|
Para actualizar, edita `.vscode/launch.json` o pasa por línea de comandos:
|
|
75
75
|
|
|
76
76
|
```sh
|
|
77
|
-
flutter run --dart-define=
|
|
77
|
+
flutter run --dart-define=RC_ANDROID_PROD_KEY=tu_clave
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
---
|
|
@@ -74,7 +74,7 @@ Forçar manualmente: `kasy run --rc=test` ou `kasy run --rc=prod`. `--rc=auto` (
|
|
|
74
74
|
Para atualizar, edite o `.vscode/launch.json` ou passe via linha de comando:
|
|
75
75
|
|
|
76
76
|
```sh
|
|
77
|
-
flutter run --dart-define=
|
|
77
|
+
flutter run --dart-define=RC_ANDROID_PROD_KEY=sua_chave
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
---
|
|
@@ -27,6 +27,7 @@ import 'package:flutter/foundation.dart' show kIsWeb;
|
|
|
27
27
|
import 'package:flutter/material.dart';
|
|
28
28
|
import 'package:flutter/services.dart' show SystemUiOverlayStyle;
|
|
29
29
|
import 'package:kasy_kit/core/chrome/chrome_visibility.dart';
|
|
30
|
+
import 'package:kasy_kit/core/chrome/web_header_scope.dart';
|
|
30
31
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
31
32
|
import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
|
|
32
33
|
import 'package:kasy_kit/core/widgets/responsive_layout.dart';
|
|
@@ -56,8 +57,11 @@ enum KasyAppBarScrollTreatment { overlay, intrinsicScroll }
|
|
|
56
57
|
|
|
57
58
|
/// Vertical inset for scroll/viewport when using [KasyAppBarScrollTreatment.overlay].
|
|
58
59
|
double kasyAppBarBodyTopOverlap(BuildContext context) {
|
|
59
|
-
// On desktop the app bar hides
|
|
60
|
-
|
|
60
|
+
// On desktop the app bar hides only inside the web-header scope (the shell), so
|
|
61
|
+
// no overlap there. Outside it (a full-screen pushed route) the bar is visible,
|
|
62
|
+
// so reserve its height like on phone/tablet.
|
|
63
|
+
if (MediaQuery.sizeOf(context).width >= DeviceType.large.breakpoint &&
|
|
64
|
+
KasyWebHeaderScope.of(context)) {
|
|
61
65
|
return 0;
|
|
62
66
|
}
|
|
63
67
|
return MediaQuery.paddingOf(context).top +
|
|
@@ -74,23 +78,47 @@ List<Widget> kasyOverlayPaddedSlivers(
|
|
|
74
78
|
BuildContext context, {
|
|
75
79
|
required List<Widget> slivers,
|
|
76
80
|
EdgeInsetsGeometry? contentPadding,
|
|
81
|
+
double? maxContentWidth,
|
|
77
82
|
}) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
const double topPad = KasySpacing.belowChromeContentGap;
|
|
84
|
+
final double bottomPad =
|
|
85
|
+
MediaQuery.paddingOf(context).bottom + KasySpacing.pageVerticalGutter;
|
|
86
|
+
const double gutter = KasySpacing.pageHorizontalGutter;
|
|
87
|
+
|
|
88
|
+
final Widget body = SliverMainAxisGroup(slivers: slivers);
|
|
89
|
+
|
|
90
|
+
// An explicit [contentPadding] takes full control; otherwise use the page
|
|
91
|
+
// gutters, optionally centering content within [maxContentWidth] on wide
|
|
92
|
+
// viewports (so lists never stretch edge-to-edge on desktop). Centering is
|
|
93
|
+
// computed from the real content width (SliverLayoutBuilder) so it stays
|
|
94
|
+
// correct inside the desktop shell where the sidebar already took space.
|
|
95
|
+
final Widget paddedGroup;
|
|
96
|
+
if (contentPadding != null) {
|
|
97
|
+
paddedGroup = SliverPadding(padding: contentPadding, sliver: body);
|
|
98
|
+
} else if (maxContentWidth == null) {
|
|
99
|
+
paddedGroup = SliverPadding(
|
|
100
|
+
padding: EdgeInsets.fromLTRB(gutter, topPad, gutter, bottomPad),
|
|
101
|
+
sliver: body,
|
|
102
|
+
);
|
|
103
|
+
} else {
|
|
104
|
+
paddedGroup = SliverLayoutBuilder(
|
|
105
|
+
builder: (context, constraints) {
|
|
106
|
+
final double available = constraints.crossAxisExtent;
|
|
107
|
+
final double horizontal = available - 2 * gutter > maxContentWidth
|
|
108
|
+
? (available - maxContentWidth) / 2
|
|
109
|
+
: gutter;
|
|
110
|
+
return SliverPadding(
|
|
111
|
+
padding: EdgeInsets.fromLTRB(horizontal, topPad, horizontal, bottomPad),
|
|
112
|
+
sliver: body,
|
|
113
|
+
);
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
}
|
|
86
117
|
return <Widget>[
|
|
87
118
|
SliverToBoxAdapter(
|
|
88
119
|
child: SizedBox(height: kasyAppBarBodyTopOverlap(context)),
|
|
89
120
|
),
|
|
90
|
-
|
|
91
|
-
padding: pad,
|
|
92
|
-
sliver: SliverMainAxisGroup(slivers: slivers),
|
|
93
|
-
),
|
|
121
|
+
paddedGroup,
|
|
94
122
|
];
|
|
95
123
|
}
|
|
96
124
|
|
|
@@ -285,8 +313,12 @@ class KasyAppBar extends StatelessWidget {
|
|
|
285
313
|
|
|
286
314
|
@override
|
|
287
315
|
Widget build(BuildContext context) {
|
|
288
|
-
// On desktop the web header owns the top chrome, so the page app bar hides
|
|
289
|
-
|
|
316
|
+
// On desktop the web header owns the top chrome, so the page app bar hides —
|
|
317
|
+
// but ONLY when there actually is a web header above (i.e. inside the shell's
|
|
318
|
+
// KasyWebHeaderScope). A full-screen route pushed over the shell has no web
|
|
319
|
+
// header, so the bar stays visible and its back button is never lost.
|
|
320
|
+
if (MediaQuery.sizeOf(context).width >= DeviceType.large.breakpoint &&
|
|
321
|
+
KasyWebHeaderScope.of(context)) {
|
|
290
322
|
return const SizedBox.shrink();
|
|
291
323
|
}
|
|
292
324
|
final Color orbFg = context.colors.onSurface;
|
|
@@ -461,6 +493,13 @@ class KasyOverlayScaffold extends StatelessWidget {
|
|
|
461
493
|
final Widget? trailing;
|
|
462
494
|
final List<Widget> slivers;
|
|
463
495
|
final EdgeInsetsGeometry? contentPadding;
|
|
496
|
+
|
|
497
|
+
/// When set, content is centered within this max width on wide viewports
|
|
498
|
+
/// (desktop) instead of stretching edge-to-edge. Use [kKasyContentMaxWidth]
|
|
499
|
+
/// for the standard single-column internal page. Ignored when
|
|
500
|
+
/// [contentPadding] is provided (that takes full control of the insets).
|
|
501
|
+
final double? maxContentWidth;
|
|
502
|
+
|
|
464
503
|
final ScrollController? scrollController;
|
|
465
504
|
final ScrollPhysics? physics;
|
|
466
505
|
final Color? backgroundColor;
|
|
@@ -480,6 +519,7 @@ class KasyOverlayScaffold extends StatelessWidget {
|
|
|
480
519
|
this.trailing,
|
|
481
520
|
required this.slivers,
|
|
482
521
|
this.contentPadding,
|
|
522
|
+
this.maxContentWidth,
|
|
483
523
|
this.scrollController,
|
|
484
524
|
this.physics,
|
|
485
525
|
this.backgroundColor,
|
|
@@ -504,6 +544,7 @@ class KasyOverlayScaffold extends StatelessWidget {
|
|
|
504
544
|
slivers: kasyOverlayPaddedSlivers(
|
|
505
545
|
context,
|
|
506
546
|
contentPadding: contentPadding,
|
|
547
|
+
maxContentWidth: maxContentWidth,
|
|
507
548
|
slivers: slivers,
|
|
508
549
|
),
|
|
509
550
|
);
|