kasy-cli 1.21.8 → 1.22.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 (269) hide show
  1. package/lib/commands/add.js +93 -80
  2. package/lib/commands/configure.js +100 -32
  3. package/lib/commands/doctor.js +28 -2
  4. package/lib/commands/new.js +86 -38
  5. package/lib/commands/notifications.js +1 -1
  6. package/lib/commands/remove.js +43 -15
  7. package/lib/commands/run.js +2 -2
  8. package/lib/commands/update.js +2 -2
  9. package/lib/scaffold/CHANGELOG.json +14 -0
  10. package/lib/scaffold/backends/api/generator.js +14 -14
  11. package/lib/scaffold/backends/api/patch/README.md +83 -0
  12. package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
  13. package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +5 -0
  14. package/lib/scaffold/backends/api/patch/lib/{environnements.dart → environments.dart} +3 -11
  15. package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_api.dart +108 -0
  16. package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +51 -0
  17. package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +5 -5
  18. package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +71 -38
  19. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +2 -2
  20. package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +54 -0
  21. package/lib/scaffold/backends/api/patch/lib/features/onboarding/models/user_info.dart +16 -16
  22. package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  23. package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  24. package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +100 -0
  25. package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  26. package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +60 -0
  27. package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  28. package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -5
  29. package/lib/scaffold/backends/firebase/deploy.js +87 -13
  30. package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +14 -6
  31. package/lib/scaffold/backends/firebase/generator.js +5 -5
  32. package/lib/scaffold/backends/firebase/setup-from-scratch.js +69 -45
  33. package/lib/scaffold/backends/firebase/tokens.js +4 -4
  34. package/lib/scaffold/backends/supabase/deploy.js +63 -11
  35. package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
  36. package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
  37. package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
  38. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
  39. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
  40. package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
  41. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
  42. package/lib/scaffold/backends/supabase/generator.js +17 -17
  43. package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
  44. package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
  45. package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
  46. package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
  47. package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
  48. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
  49. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
  50. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  51. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
  52. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
  53. package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
  54. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
  55. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  56. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  57. package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
  58. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  59. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
  60. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  61. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
  62. package/lib/scaffold/backends/supabase/tokens.js +3 -3
  63. package/lib/scaffold/catalog.js +9 -11
  64. package/lib/scaffold/generate.js +45 -31
  65. package/lib/scaffold/shared/generator-utils.js +188 -81
  66. package/lib/scaffold/shared/sort-imports.js +191 -0
  67. package/lib/scaffold/shared/template-strings.js +3 -3
  68. package/lib/utils/checks.js +2 -2
  69. package/lib/utils/i18n/messages-en.js +50 -35
  70. package/lib/utils/i18n/messages-es.js +50 -35
  71. package/lib/utils/i18n/messages-pt.js +52 -37
  72. package/lib/utils/updates.js +15 -15
  73. package/package.json +1 -1
  74. package/templates/firebase/.env.example +2 -2
  75. package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
  76. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  78. package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
  79. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
  80. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
  81. package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
  82. package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
  83. package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
  84. package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
  85. package/templates/firebase/firestore.rules +24 -5
  86. package/templates/firebase/functions/package-lock.json +22 -1
  87. package/templates/firebase/functions/package.json +2 -1
  88. package/templates/firebase/functions/src/admin/functions.ts +113 -0
  89. package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
  90. package/templates/firebase/functions/src/index.ts +8 -2
  91. package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
  92. package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
  93. package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
  94. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
  95. package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
  96. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
  97. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  98. package/templates/firebase/lib/components/components.dart +4 -1
  99. package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
  100. package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
  101. package/templates/firebase/lib/components/kasy_button.dart +23 -99
  102. package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
  103. package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
  104. package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
  105. package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
  106. package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
  107. package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
  108. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
  109. package/templates/firebase/lib/components/kasy_toast.dart +2 -2
  110. package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
  111. package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
  112. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
  113. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
  114. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
  115. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
  116. package/templates/firebase/lib/core/config/app_env.dart +5 -11
  117. package/templates/firebase/lib/core/config/features.dart +5 -4
  118. package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
  119. package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
  120. package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
  121. package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
  122. package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
  123. package/templates/firebase/lib/core/data/models/user.dart +11 -0
  124. package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
  125. package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
  126. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
  127. package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
  128. package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
  129. package/templates/firebase/lib/core/states/logout_action.dart +25 -0
  130. package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
  131. package/templates/firebase/lib/core/theme/colors.dart +488 -188
  132. package/templates/firebase/lib/core/theme/radius.dart +22 -11
  133. package/templates/firebase/lib/core/theme/shadows.dart +66 -0
  134. package/templates/firebase/lib/core/theme/texts.dart +75 -41
  135. package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
  136. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
  137. package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
  138. package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
  139. package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
  140. package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
  141. package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
  142. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
  143. package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
  144. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
  145. package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
  146. package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
  147. package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  148. package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
  149. package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
  150. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
  151. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
  152. package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
  153. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
  154. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
  155. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
  156. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
  157. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
  158. package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
  159. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
  160. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
  161. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
  162. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
  163. package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
  164. package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
  165. package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
  166. package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
  167. package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
  168. package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
  169. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
  170. package/templates/firebase/lib/features/home/home_feed.dart +289 -0
  171. package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
  172. package/templates/firebase/lib/features/home/home_page.dart +11 -250
  173. package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
  174. package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
  175. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
  176. package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
  177. package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
  178. package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
  179. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  180. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
  181. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
  182. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
  183. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
  184. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
  185. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
  186. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
  187. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
  188. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
  189. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
  190. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  191. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
  192. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
  193. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
  194. package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
  195. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
  196. package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
  197. package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
  198. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
  199. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
  200. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
  201. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
  202. package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
  203. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
  204. package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
  205. package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
  206. package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
  207. package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
  208. package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
  209. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
  210. package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  211. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
  212. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
  213. package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
  214. package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
  215. package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
  216. package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
  217. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
  218. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
  219. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
  220. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
  221. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
  222. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
  223. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
  224. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
  225. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
  226. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
  227. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
  228. package/templates/firebase/lib/i18n/en.i18n.json +171 -46
  229. package/templates/firebase/lib/i18n/es.i18n.json +175 -50
  230. package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
  231. package/templates/firebase/lib/main.dart +6 -3
  232. package/templates/firebase/lib/router.dart +15 -23
  233. package/templates/firebase/pubspec.yaml +4 -5
  234. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
  235. package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
  236. package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
  237. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
  238. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
  239. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
  240. package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
  241. package/templates/firebase/test/test_utils.dart +6 -6
  242. package/templates/firebase/web/index.html +5 -2
  243. package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
  244. package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
  245. package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
  246. package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
  247. package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
  248. package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
  249. package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
  250. package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
  251. package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
  252. package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
  253. package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
  254. package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
  255. package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
  256. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
  257. package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
  258. /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
  259. /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
  260. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
  261. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
  262. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
  263. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
  264. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
  265. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
  266. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
  267. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
  268. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
  269. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
@@ -1,21 +1,61 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:flutter_riverpod/flutter_riverpod.dart';
3
3
  import 'package:kasy_kit/components/components.dart';
4
+ import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
4
5
  import 'package:kasy_kit/core/theme/theme.dart';
5
6
  import 'package:kasy_kit/core/widgets/kasy_scroll_behavior.dart';
6
- import 'package:kasy_kit/features/llm_chat/providers/llm_chat_notifier.dart';
7
- import 'package:kasy_kit/features/llm_chat/ui/widgets/llm_chat_avatars.dart';
8
- import 'package:kasy_kit/features/llm_chat/ui/widgets/llm_chat_composer.dart';
7
+ import 'package:kasy_kit/features/ai_chat/providers/ai_chat_notifier.dart';
8
+ import 'package:kasy_kit/features/ai_chat/providers/ai_conversations_notifier.dart';
9
+ import 'package:kasy_kit/features/ai_chat/ui/widgets/ai_chat_avatars.dart';
10
+ import 'package:kasy_kit/features/ai_chat/ui/widgets/ai_chat_composer.dart';
9
11
  import 'package:kasy_kit/i18n/translations.g.dart';
10
12
 
11
- class LlmChatPage extends ConsumerStatefulWidget {
12
- const LlmChatPage({super.key});
13
+ /// Full-screen conversation page used on phones. Pushed on the root navigator
14
+ /// so the bottom navigation bar is hidden — the composer needs the bottom edge.
15
+ /// Clears the selected conversation when dismissed (back button or system pop).
16
+ class AiChatConversationPage extends ConsumerWidget {
17
+ const AiChatConversationPage({super.key});
13
18
 
14
19
  @override
15
- ConsumerState<LlmChatPage> createState() => _LlmChatPageState();
20
+ Widget build(BuildContext context, WidgetRef ref) {
21
+ return PopScope(
22
+ onPopInvokedWithResult: (didPop, _) {
23
+ if (didPop) {
24
+ ref.read(selectedConversationIdProvider.notifier).state = null;
25
+ }
26
+ },
27
+ child: Scaffold(
28
+ backgroundColor: context.colors.background,
29
+ body: Column(
30
+ children: [
31
+ KasyAppBar(
32
+ title: t.ai_chat.title,
33
+ onThemeToggle: () {
34
+ KasyHaptics.light(context);
35
+ ThemeProvider.of(context).toggle();
36
+ },
37
+ ),
38
+ const Expanded(child: AiChatConversationView()),
39
+ ],
40
+ ),
41
+ ),
42
+ );
43
+ }
44
+ }
45
+
46
+ /// The chat thread for the currently selected conversation: message list +
47
+ /// composer. Has no chrome of its own — it scrolls *inside* whatever container
48
+ /// hosts it (a rounded pane on desktop, a full page below the app bar on phone).
49
+ class AiChatConversationView extends ConsumerStatefulWidget {
50
+ const AiChatConversationView({super.key});
51
+
52
+ @override
53
+ ConsumerState<AiChatConversationView> createState() =>
54
+ _AiChatConversationViewState();
16
55
  }
17
56
 
18
- class _LlmChatPageState extends ConsumerState<LlmChatPage> {
57
+ class _AiChatConversationViewState
58
+ extends ConsumerState<AiChatConversationView> {
19
59
  final TextEditingController _controller = TextEditingController();
20
60
  final ScrollController _scrollController = ScrollController();
21
61
 
@@ -28,9 +68,9 @@ class _LlmChatPageState extends ConsumerState<LlmChatPage> {
28
68
 
29
69
  void _sendMessage() {
30
70
  final prompt = _controller.text.trim();
31
- if (prompt.isEmpty || prompt.length > kLlmChatMaxMessageLength) return;
71
+ if (prompt.isEmpty || prompt.length > kAiChatMaxMessageLength) return;
32
72
  _controller.clear();
33
- ref.read(llmChatNotifierProvider.notifier).sendMessage(prompt);
73
+ ref.read(aiChatNotifierProvider.notifier).sendMessage(prompt);
34
74
  }
35
75
 
36
76
  void _scrollToBottom() {
@@ -53,46 +93,19 @@ class _LlmChatPageState extends ConsumerState<LlmChatPage> {
53
93
 
54
94
  @override
55
95
  Widget build(BuildContext context) {
56
- // Scroll to bottom whenever the message list changes.
57
- ref.listen(llmChatNotifierProvider, (_, _) => _scrollToBottom());
96
+ // Scroll to bottom whenever the message list changes (new chunk / message).
97
+ ref.listen(aiChatNotifierProvider, (_, _) => _scrollToBottom());
58
98
 
59
- final chatAsync = ref.watch(llmChatNotifierProvider);
99
+ final chatAsync = ref.watch(aiChatNotifierProvider);
60
100
 
61
- return Scaffold(
62
- backgroundColor: context.colors.background,
63
- body: Stack(
64
- fit: StackFit.expand,
65
- children: [
66
- Positioned.fill(
67
- child: Column(
68
- crossAxisAlignment: CrossAxisAlignment.stretch,
69
- children: [
70
- Expanded(
71
- child: chatAsync.when(
72
- loading: () =>
73
- const Center(child: CircularProgressIndicator()),
74
- error: (_, _) =>
75
- Center(child: Text(t.llm_chat.error_network)),
76
- data: (chatState) => _buildBody(context, chatState),
77
- ),
78
- ),
79
- ],
80
- ),
81
- ),
82
- Positioned(
83
- top: 0,
84
- left: 0,
85
- right: 0,
86
- child: KasyAppBar(
87
- title: t.llm_chat.title,
88
- ),
89
- ),
90
- ],
91
- ),
101
+ return chatAsync.when(
102
+ loading: () => const Center(child: CircularProgressIndicator()),
103
+ error: (_, _) => Center(child: Text(t.ai_chat.error_network)),
104
+ data: (chatState) => _buildBody(context, chatState),
92
105
  );
93
106
  }
94
107
 
95
- Widget _buildBody(BuildContext context, LlmChatState chatState) {
108
+ Widget _buildBody(BuildContext context, AiChatState chatState) {
96
109
  final messages = chatState.messages;
97
110
  final isReplying = chatState.isReplying;
98
111
 
@@ -104,21 +117,19 @@ class _LlmChatPageState extends ConsumerState<LlmChatPage> {
104
117
  children: [
105
118
  Expanded(
106
119
  child: messages.isEmpty && !isReplying
107
- ? _EmptyState()
120
+ ? const _EmptyState()
108
121
  : ScrollConfiguration(
109
122
  behavior: const KasyKitScrollBehavior(),
110
123
  child: ListView.builder(
111
124
  controller: _scrollController,
112
- padding: EdgeInsets.fromLTRB(
125
+ padding: const EdgeInsets.fromLTRB(
113
126
  KasySpacing.pageHorizontalGutter,
114
- kasyAppBarBodyTopOverlap(context) +
115
- KasySpacing.belowChromeContentGap,
127
+ KasySpacing.md,
116
128
  KasySpacing.pageHorizontalGutter,
117
129
  KasySpacing.md,
118
130
  ),
119
131
  // +1 for the typing indicator shown before the first SSE chunk
120
- itemCount:
121
- messages.length + (showTypingIndicator ? 1 : 0),
132
+ itemCount: messages.length + (showTypingIndicator ? 1 : 0),
122
133
  itemBuilder: (context, index) {
123
134
  if (showTypingIndicator && index == messages.length) {
124
135
  return const _TypingIndicatorRow();
@@ -141,7 +152,7 @@ class _LlmChatPageState extends ConsumerState<LlmChatPage> {
141
152
  KasySpacing.pageHorizontalGutter,
142
153
  KasySpacing.md,
143
154
  ),
144
- child: LlmChatComposer(
155
+ child: AiChatComposer(
145
156
  controller: _controller,
146
157
  isReplying: isReplying,
147
158
  onSend: _sendMessage,
@@ -154,6 +165,8 @@ class _LlmChatPageState extends ConsumerState<LlmChatPage> {
154
165
  }
155
166
 
156
167
  class _EmptyState extends StatelessWidget {
168
+ const _EmptyState();
169
+
157
170
  @override
158
171
  Widget build(BuildContext context) {
159
172
  return Center(
@@ -164,8 +177,8 @@ class _EmptyState extends StatelessWidget {
164
177
  child: Column(
165
178
  mainAxisSize: MainAxisSize.min,
166
179
  children: [
167
- const LlmChatAssistantAvatar(
168
- diameter: kLlmChatEmptyAvatarDiameter,
180
+ const AiChatAssistantAvatar(
181
+ diameter: kAiChatEmptyAvatarDiameter,
169
182
  showShadow: true,
170
183
  ),
171
184
  const SizedBox(height: KasySpacing.md),
@@ -179,7 +192,7 @@ class _EmptyState extends StatelessWidget {
179
192
  ),
180
193
  const SizedBox(height: KasySpacing.xs),
181
194
  Text(
182
- t.llm_chat.empty_state,
195
+ t.ai_chat.empty_state,
183
196
  textAlign: TextAlign.center,
184
197
  style: context.textTheme.bodyLarge?.copyWith(
185
198
  color: context.colors.muted,
@@ -213,17 +226,18 @@ class _ChatBubble extends StatelessWidget {
213
226
  borderRadius: BorderRadius.only(
214
227
  topLeft: const Radius.circular(16),
215
228
  topRight: const Radius.circular(16),
216
- bottomLeft:
217
- isUser ? const Radius.circular(16) : const Radius.circular(4),
218
- bottomRight:
219
- isUser ? const Radius.circular(4) : const Radius.circular(16),
229
+ bottomLeft: isUser
230
+ ? const Radius.circular(16)
231
+ : const Radius.circular(4),
232
+ bottomRight: isUser
233
+ ? const Radius.circular(4)
234
+ : const Radius.circular(16),
220
235
  ),
221
236
  ),
222
237
  child: Text(
223
238
  message.content,
224
239
  style: context.textTheme.bodyMedium?.copyWith(
225
- color:
226
- isUser ? context.colors.onPrimary : context.colors.onBackground,
240
+ color: isUser ? context.colors.onPrimary : context.colors.onBackground,
227
241
  height: 1.4,
228
242
  ),
229
243
  ),
@@ -238,7 +252,7 @@ class _ChatBubble extends StatelessWidget {
238
252
  children: [
239
253
  Flexible(child: bubble),
240
254
  const SizedBox(width: KasySpacing.xs),
241
- const LlmChatUserAvatar(),
255
+ const AiChatUserAvatar(),
242
256
  ],
243
257
  ),
244
258
  );
@@ -249,7 +263,7 @@ class _ChatBubble extends StatelessWidget {
249
263
  child: Row(
250
264
  crossAxisAlignment: CrossAxisAlignment.start,
251
265
  children: [
252
- const LlmChatAssistantAvatar(),
266
+ const AiChatAssistantAvatar(),
253
267
  const SizedBox(width: KasySpacing.xs),
254
268
  Flexible(child: bubble),
255
269
  ],
@@ -268,7 +282,7 @@ class _TypingIndicatorRow extends StatelessWidget {
268
282
  child: Row(
269
283
  crossAxisAlignment: CrossAxisAlignment.start,
270
284
  children: [
271
- LlmChatAssistantAvatar(),
285
+ AiChatAssistantAvatar(),
272
286
  SizedBox(width: KasySpacing.xs),
273
287
  _TypingIndicator(),
274
288
  ],
@@ -328,10 +342,9 @@ class _TypingIndicatorState extends State<_TypingIndicator>
328
342
  animation: _controller,
329
343
  builder: (context, _) {
330
344
  // Each dot bounces at a slightly different phase.
331
- final double phase =
332
- (_controller.value - i * 0.2).clamp(0.0, 1.0);
333
- final double scale = 1.0 +
334
- 0.4 * (phase < 0.5 ? phase * 2 : (1.0 - phase) * 2);
345
+ final double phase = (_controller.value - i * 0.2).clamp(0.0, 1.0);
346
+ final double scale =
347
+ 1.0 + 0.4 * (phase < 0.5 ? phase * 2 : (1.0 - phase) * 2);
335
348
  return Transform.scale(
336
349
  scale: scale,
337
350
  child: Container(
@@ -0,0 +1,285 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+ import 'package:kasy_kit/components/components.dart';
4
+ import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
5
+ import 'package:kasy_kit/core/theme/theme.dart';
6
+ import 'package:kasy_kit/core/widgets/kasy_scroll_behavior.dart';
7
+ import 'package:kasy_kit/features/ai_chat/api/ai_chat_conversation_entity.dart';
8
+ import 'package:kasy_kit/features/ai_chat/providers/ai_conversations_notifier.dart';
9
+ import 'package:kasy_kit/features/ai_chat/ui/widgets/ai_chat_conversation_view.dart';
10
+ import 'package:kasy_kit/features/ai_chat/ui/widgets/ai_conversation_tile.dart';
11
+ import 'package:kasy_kit/i18n/translations.g.dart';
12
+
13
+ /// The conversation list pane (Figma `mail` list). Creating a new conversation
14
+ /// has a single entry point per layout to avoid duplication:
15
+ /// - wide: a round "+" button in the header (the app bar / web header own the
16
+ /// top chrome elsewhere, so the title lives here).
17
+ /// - phone: a full-width button pinned at the bottom (Figma "New Email"); the
18
+ /// title is shown by the page app bar, so there is no header here.
19
+ ///
20
+ /// Selecting a tile updates [selectedConversationIdProvider]; the parent shell
21
+ /// reacts (open the thread on phones, swap the detail pane on wide layouts).
22
+ class AiConversationList extends ConsumerWidget {
23
+ const AiConversationList({
24
+ super.key,
25
+ this.swipeToDelete = false,
26
+ this.isPhone = false,
27
+ });
28
+
29
+ /// When true (phones), each row can be swiped to reveal a delete action and
30
+ /// the create action becomes a pinned bottom button instead of a header orb.
31
+ final bool swipeToDelete;
32
+ final bool isPhone;
33
+
34
+ @override
35
+ Widget build(BuildContext context, WidgetRef ref) {
36
+ final conversationsAsync = ref.watch(aiConversationsNotifierProvider);
37
+ final selectedId = ref.watch(selectedConversationIdProvider);
38
+
39
+ return Column(
40
+ crossAxisAlignment: CrossAxisAlignment.stretch,
41
+ children: [
42
+ if (!isPhone) _Header(onCreate: () => _createConversation(context, ref)),
43
+ Expanded(
44
+ child: conversationsAsync.when(
45
+ loading: () => const Center(child: CircularProgressIndicator()),
46
+ error: (_, _) => Center(child: Text(t.ai_chat.error_network)),
47
+ data: (conversations) {
48
+ if (conversations.isEmpty) return const _EmptyConversations();
49
+ return ScrollConfiguration(
50
+ behavior: const KasyKitScrollBehavior(),
51
+ child: ListView.separated(
52
+ padding: const EdgeInsets.fromLTRB(
53
+ KasySpacing.smd,
54
+ KasySpacing.smd,
55
+ KasySpacing.smd,
56
+ KasySpacing.md,
57
+ ),
58
+ itemCount: conversations.length,
59
+ separatorBuilder: (_, _) => isPhone
60
+ // Horizontal hairline between rows (sidebar/header line).
61
+ ? Divider(
62
+ height: KasySpacing.xs,
63
+ thickness: 0.5,
64
+ color: context.colors.border,
65
+ )
66
+ : const SizedBox(height: KasySpacing.xs),
67
+ itemBuilder: (context, index) {
68
+ final conversation = conversations[index];
69
+ return _buildRow(
70
+ context,
71
+ ref,
72
+ conversation,
73
+ selected: conversation.id == selectedId,
74
+ );
75
+ },
76
+ ),
77
+ );
78
+ },
79
+ ),
80
+ ),
81
+ if (isPhone) _BottomCreateButton(onCreate: () => _createConversation(context, ref)),
82
+ ],
83
+ );
84
+ }
85
+
86
+ Widget _buildRow(
87
+ BuildContext context,
88
+ WidgetRef ref,
89
+ AiChatConversationEntity conversation, {
90
+ required bool selected,
91
+ }) {
92
+ final tile = AiConversationTile(
93
+ conversation: conversation,
94
+ selected: selected,
95
+ onTap: () {
96
+ KasyHaptics.light(context);
97
+ _open(context, ref, conversation.id);
98
+ },
99
+ onDelete: () => _confirmDelete(context, ref, conversation),
100
+ );
101
+
102
+ if (!swipeToDelete) return tile;
103
+
104
+ return Dismissible(
105
+ key: ValueKey(conversation.id),
106
+ direction: DismissDirection.endToStart,
107
+ background: _swipeDeleteBackground(context),
108
+ confirmDismiss: (_) async {
109
+ final bool confirmed = await _askDelete(context);
110
+ if (confirmed) {
111
+ await ref
112
+ .read(aiConversationsNotifierProvider.notifier)
113
+ .delete(conversation.id);
114
+ }
115
+ // The list rebuilds from provider state after delete, so we never let
116
+ // Dismissible remove the row itself (avoids a stale-widget assertion).
117
+ return false;
118
+ },
119
+ child: tile,
120
+ );
121
+ }
122
+
123
+ Widget _swipeDeleteBackground(BuildContext context) {
124
+ return Container(
125
+ alignment: Alignment.centerRight,
126
+ padding: const EdgeInsets.symmetric(horizontal: KasySpacing.lg),
127
+ decoration: BoxDecoration(
128
+ color: context.colors.surfaceErrorSoft,
129
+ borderRadius: BorderRadius.circular(16),
130
+ ),
131
+ child: Icon(KasyIcons.trash, color: context.colors.error),
132
+ );
133
+ }
134
+
135
+ /// Selects a conversation; on phones it also pushes the full-screen thread
136
+ /// (a root-navigator route, so the bottom nav bar is hidden).
137
+ void _open(BuildContext context, WidgetRef ref, String conversationId) {
138
+ ref.read(selectedConversationIdProvider.notifier).state = conversationId;
139
+ if (isPhone) {
140
+ Navigator.of(context, rootNavigator: true).push(
141
+ MaterialPageRoute(builder: (_) => const AiChatConversationPage()),
142
+ );
143
+ }
144
+ }
145
+
146
+ Future<void> _createConversation(BuildContext context, WidgetRef ref) async {
147
+ KasyHaptics.light(context);
148
+ final id = await ref.read(aiConversationsNotifierProvider.notifier).create();
149
+ if (id != null && context.mounted) {
150
+ _open(context, ref, id);
151
+ }
152
+ }
153
+
154
+ Future<void> _confirmDelete(
155
+ BuildContext context,
156
+ WidgetRef ref,
157
+ AiChatConversationEntity conversation,
158
+ ) async {
159
+ final bool confirmed = await _askDelete(context);
160
+ if (confirmed) {
161
+ await ref
162
+ .read(aiConversationsNotifierProvider.notifier)
163
+ .delete(conversation.id);
164
+ }
165
+ }
166
+
167
+ Future<bool> _askDelete(BuildContext context) async {
168
+ bool confirmed = false;
169
+ await showKasyConfirmDialog(
170
+ context,
171
+ title: t.ai_chat.delete_title,
172
+ message: t.ai_chat.delete_message,
173
+ cancelLabel: t.ai_chat.delete_cancel,
174
+ confirmLabel: t.ai_chat.delete_confirm,
175
+ destructive: true,
176
+ onConfirm: () => confirmed = true,
177
+ );
178
+ return confirmed;
179
+ }
180
+ }
181
+
182
+ class _Header extends StatelessWidget {
183
+ const _Header({required this.onCreate});
184
+
185
+ final VoidCallback onCreate;
186
+
187
+ @override
188
+ Widget build(BuildContext context) {
189
+ return DecoratedBox(
190
+ // Horizontal hairline matching the sidebar/web-header bottom border.
191
+ decoration: BoxDecoration(
192
+ border: Border(
193
+ bottom: BorderSide(color: context.colors.border, width: 0.5),
194
+ ),
195
+ ),
196
+ child: Padding(
197
+ padding: const EdgeInsets.fromLTRB(
198
+ KasySpacing.lg,
199
+ KasySpacing.md,
200
+ KasySpacing.smd,
201
+ KasySpacing.md,
202
+ ),
203
+ child: Row(
204
+ children: [
205
+ Expanded(
206
+ child: Text(
207
+ t.ai_chat.title,
208
+ maxLines: 1,
209
+ overflow: TextOverflow.ellipsis,
210
+ style: context.textTheme.titleLarge?.copyWith(
211
+ fontWeight: FontWeight.w800,
212
+ color: context.colors.onBackground,
213
+ ),
214
+ ),
215
+ ),
216
+ KasyButton.iconOnly(
217
+ icon: KasyIcons.add,
218
+ onPressed: onCreate,
219
+ size: KasyButtonSize.small,
220
+ iconOnlyLayoutExtent: 36,
221
+ iconGlyphSize: 20,
222
+ semanticLabel: t.ai_chat.new_conversation,
223
+ ),
224
+ ],
225
+ ),
226
+ ),
227
+ );
228
+ }
229
+ }
230
+
231
+ class _BottomCreateButton extends StatelessWidget {
232
+ const _BottomCreateButton({required this.onCreate});
233
+
234
+ final VoidCallback onCreate;
235
+
236
+ @override
237
+ Widget build(BuildContext context) {
238
+ return DecoratedBox(
239
+ // Horizontal hairline above the pinned action (matches sidebar footer).
240
+ decoration: BoxDecoration(
241
+ border: Border(
242
+ top: BorderSide(color: context.colors.border, width: 0.5),
243
+ ),
244
+ ),
245
+ child: SafeArea(
246
+ top: false,
247
+ child: Padding(
248
+ padding: const EdgeInsets.fromLTRB(
249
+ KasySpacing.md,
250
+ KasySpacing.sm,
251
+ KasySpacing.md,
252
+ KasySpacing.sm,
253
+ ),
254
+ child: KasyButton(
255
+ label: t.ai_chat.new_conversation,
256
+ icon: KasyIcons.add,
257
+ expand: true,
258
+ onPressed: onCreate,
259
+ ),
260
+ ),
261
+ ),
262
+ );
263
+ }
264
+ }
265
+
266
+ class _EmptyConversations extends StatelessWidget {
267
+ const _EmptyConversations();
268
+
269
+ @override
270
+ Widget build(BuildContext context) {
271
+ return Center(
272
+ child: Padding(
273
+ padding: const EdgeInsets.all(KasySpacing.lg),
274
+ child: Text(
275
+ t.ai_chat.conversations_empty,
276
+ textAlign: TextAlign.center,
277
+ style: context.textTheme.bodyLarge?.copyWith(
278
+ color: context.colors.muted,
279
+ height: 1.4,
280
+ ),
281
+ ),
282
+ ),
283
+ );
284
+ }
285
+ }