kasy-cli 1.21.9 → 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 (267) 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 +80 -37
  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/generator.js +5 -5
  31. package/lib/scaffold/backends/firebase/tokens.js +4 -4
  32. package/lib/scaffold/backends/supabase/deploy.js +63 -11
  33. package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
  34. package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
  35. package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
  36. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
  37. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
  38. package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
  39. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
  40. package/lib/scaffold/backends/supabase/generator.js +17 -17
  41. package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
  42. package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
  43. package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
  44. package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
  45. package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
  46. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
  47. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
  48. 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
  49. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
  50. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
  51. package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
  52. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
  53. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  54. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  55. package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
  56. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  57. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
  58. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  59. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
  60. package/lib/scaffold/backends/supabase/tokens.js +3 -3
  61. package/lib/scaffold/catalog.js +9 -11
  62. package/lib/scaffold/generate.js +45 -31
  63. package/lib/scaffold/shared/generator-utils.js +188 -81
  64. package/lib/scaffold/shared/sort-imports.js +191 -0
  65. package/lib/scaffold/shared/template-strings.js +3 -3
  66. package/lib/utils/checks.js +2 -2
  67. package/lib/utils/i18n/messages-en.js +50 -35
  68. package/lib/utils/i18n/messages-es.js +50 -35
  69. package/lib/utils/i18n/messages-pt.js +52 -37
  70. package/lib/utils/updates.js +15 -15
  71. package/package.json +1 -1
  72. package/templates/firebase/.env.example +2 -2
  73. package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
  74. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  75. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  76. package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
  78. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
  79. package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
  80. package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
  81. package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
  82. package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
  83. package/templates/firebase/firestore.rules +24 -5
  84. package/templates/firebase/functions/package-lock.json +22 -1
  85. package/templates/firebase/functions/package.json +2 -1
  86. package/templates/firebase/functions/src/admin/functions.ts +113 -0
  87. package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
  88. package/templates/firebase/functions/src/index.ts +8 -2
  89. package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
  90. package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
  91. package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
  92. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
  93. package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
  94. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
  95. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  96. package/templates/firebase/lib/components/components.dart +4 -1
  97. package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
  98. package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
  99. package/templates/firebase/lib/components/kasy_button.dart +23 -99
  100. package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
  101. package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
  102. package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
  103. package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
  104. package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
  105. package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
  106. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
  107. package/templates/firebase/lib/components/kasy_toast.dart +2 -2
  108. package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
  109. package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
  110. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
  111. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
  112. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
  113. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
  114. package/templates/firebase/lib/core/config/app_env.dart +5 -11
  115. package/templates/firebase/lib/core/config/features.dart +5 -4
  116. package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
  117. package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
  118. package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
  119. package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
  120. package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
  121. package/templates/firebase/lib/core/data/models/user.dart +11 -0
  122. package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
  123. package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
  124. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
  125. package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
  126. package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
  127. package/templates/firebase/lib/core/states/logout_action.dart +25 -0
  128. package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
  129. package/templates/firebase/lib/core/theme/colors.dart +488 -188
  130. package/templates/firebase/lib/core/theme/radius.dart +22 -11
  131. package/templates/firebase/lib/core/theme/shadows.dart +66 -0
  132. package/templates/firebase/lib/core/theme/texts.dart +75 -41
  133. package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
  134. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
  135. package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
  136. package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
  137. package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
  138. package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
  139. package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
  140. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
  141. package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
  142. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
  143. package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
  144. package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
  145. package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  146. package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
  147. package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
  148. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
  149. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
  150. package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
  151. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
  152. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
  153. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
  154. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
  155. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
  156. package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
  157. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
  158. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
  159. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
  160. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
  161. package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
  162. package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
  163. package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
  164. package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
  165. package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
  166. package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
  167. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
  168. package/templates/firebase/lib/features/home/home_feed.dart +289 -0
  169. package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
  170. package/templates/firebase/lib/features/home/home_page.dart +11 -250
  171. package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
  172. package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
  173. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
  174. package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
  175. package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
  176. package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
  177. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  178. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
  179. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
  180. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
  181. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
  182. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
  183. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
  184. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
  185. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
  186. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
  187. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
  188. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  189. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
  190. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
  191. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
  192. package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
  193. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
  194. package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
  195. package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
  196. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
  197. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
  198. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
  199. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
  200. package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
  201. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
  202. package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
  203. package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
  204. package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
  205. package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
  206. package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
  207. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
  208. package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  209. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
  210. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
  211. package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
  212. package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
  213. package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
  214. package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
  215. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
  216. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
  217. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
  218. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
  219. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
  220. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
  221. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
  222. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
  223. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
  224. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
  225. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
  226. package/templates/firebase/lib/i18n/en.i18n.json +171 -46
  227. package/templates/firebase/lib/i18n/es.i18n.json +175 -50
  228. package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
  229. package/templates/firebase/lib/main.dart +6 -3
  230. package/templates/firebase/lib/router.dart +15 -23
  231. package/templates/firebase/pubspec.yaml +4 -5
  232. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
  233. package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
  234. package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
  235. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
  236. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
  237. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
  238. package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
  239. package/templates/firebase/test/test_utils.dart +6 -6
  240. package/templates/firebase/web/index.html +5 -2
  241. package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
  242. package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
  243. package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
  244. package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
  245. package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
  246. package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
  247. package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
  248. package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
  249. package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
  250. package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
  251. package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
  252. package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
  253. package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
  254. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
  255. package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
  256. /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
  257. /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
  258. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
  259. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
  260. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
  261. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
  262. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
  263. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
  264. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
  265. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
  266. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
  267. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
@@ -6,8 +6,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
6
6
  import 'package:kasy_kit/core/config/app_env.dart';
7
7
  import 'package:kasy_kit/core/states/translations.dart';
8
8
  import 'package:kasy_kit/core/states/user_state_notifier.dart';
9
- import 'package:kasy_kit/features/llm_chat/api/llm_chat_api.dart';
10
- import 'package:kasy_kit/features/llm_chat/api/llm_chat_message_entity.dart';
9
+ import 'package:kasy_kit/features/ai_chat/api/ai_chat_api.dart';
10
+ import 'package:kasy_kit/features/ai_chat/api/ai_chat_message_entity.dart';
11
+ import 'package:kasy_kit/features/ai_chat/providers/ai_conversations_notifier.dart';
11
12
  import 'package:logger/logger.dart';
12
13
 
13
14
  /// Maximum number of recent messages sent to the AI as context.
@@ -34,39 +35,39 @@ class ChatMessage {
34
35
  factory ChatMessage.assistant(String content) =>
35
36
  ChatMessage(role: 'assistant', content: content);
36
37
 
37
- factory ChatMessage.fromEntity(LlmChatMessageEntity entity) =>
38
+ factory ChatMessage.fromEntity(AiChatMessageEntity entity) =>
38
39
  ChatMessage(role: entity.role, content: entity.content);
39
40
 
40
- LlmChatMessageEntity toEntity() => LlmChatMessageEntity(
41
+ AiChatMessageEntity toEntity() => AiChatMessageEntity(
41
42
  role: role,
42
43
  content: content,
43
44
  createdAt: DateTime.now(),
44
45
  );
45
46
  }
46
47
 
47
- /// UI state for the LLM chat screen.
48
- class LlmChatState {
48
+ /// UI state for the AI chat screen.
49
+ class AiChatState {
49
50
  final List<ChatMessage> messages;
50
51
 
51
- /// True while the HTTP request to the LLM backend is in-flight.
52
+ /// True while the HTTP request to the AI backend is in-flight.
52
53
  final bool isReplying;
53
54
 
54
55
  /// True once the first SSE chunk has been received.
55
56
  /// While true the last message in [messages] is the partial assistant reply.
56
57
  final bool streamingStarted;
57
58
 
58
- const LlmChatState({
59
+ const AiChatState({
59
60
  required this.messages,
60
61
  this.isReplying = false,
61
62
  this.streamingStarted = false,
62
63
  });
63
64
 
64
- LlmChatState copyWith({
65
+ AiChatState copyWith({
65
66
  List<ChatMessage>? messages,
66
67
  bool? isReplying,
67
68
  bool? streamingStarted,
68
69
  }) {
69
- return LlmChatState(
70
+ return AiChatState(
70
71
  messages: messages ?? this.messages,
71
72
  isReplying: isReplying ?? this.isReplying,
72
73
  streamingStarted: streamingStarted ?? this.streamingStarted,
@@ -74,39 +75,56 @@ class LlmChatState {
74
75
  }
75
76
  }
76
77
 
77
- /// Manages LLM chat state: loads history from the backend on init,
78
+ /// Manages AI chat state for the conversation currently selected
79
+ /// ([selectedConversationIdProvider]): loads that conversation's history,
78
80
  /// persists each message, and streams the assistant reply word-by-word via SSE.
79
- final llmChatNotifierProvider =
80
- AsyncNotifierProvider<LlmChatNotifier, LlmChatState>(LlmChatNotifier.new);
81
+ /// Re-runs [build] whenever the selected conversation changes.
82
+ final aiChatNotifierProvider =
83
+ AsyncNotifierProvider<AiChatNotifier, AiChatState>(AiChatNotifier.new);
81
84
 
82
- class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
85
+ class AiChatNotifier extends AsyncNotifier<AiChatState> {
83
86
  final Logger _logger = Logger();
84
87
 
88
+ /// The conversation a reply is currently streaming into. Used to ignore UI
89
+ /// updates if the user switches conversations mid-stream (the reply is still
90
+ /// persisted to the right conversation, just not shown on the wrong one).
91
+ String? _streamingConversationId;
92
+
93
+ bool get _streamStillActive =>
94
+ ref.read(selectedConversationIdProvider) == _streamingConversationId;
95
+
85
96
  @override
86
- Future<LlmChatState> build() async {
97
+ Future<AiChatState> build() async {
98
+ final conversationId = ref.watch(selectedConversationIdProvider);
87
99
  final userId = ref.read(userStateNotifierProvider).user.idOrNull;
88
- if (userId == null) return const LlmChatState(messages: []);
100
+ if (conversationId == null || userId == null) {
101
+ return const AiChatState(messages: []);
102
+ }
89
103
 
90
104
  try {
91
- final entities = await ref.read(llmChatApiProvider).loadMessages(userId);
92
- return LlmChatState(
105
+ final entities = await ref
106
+ .read(aiChatApiProvider)
107
+ .loadMessages(userId, conversationId);
108
+ return AiChatState(
93
109
  messages: entities.map(ChatMessage.fromEntity).toList(),
94
110
  );
95
111
  } catch (e) {
96
- _logger.e('Failed to load LLM chat history: $e');
112
+ _logger.e('Failed to load AI chat history: $e');
97
113
  // Graceful fallback: start fresh if the backend is unavailable.
98
- return const LlmChatState(messages: []);
114
+ return const AiChatState(messages: []);
99
115
  }
100
116
  }
101
117
 
102
118
  /// Adds the user message to the conversation, persists it, then streams
103
119
  /// the assistant reply chunk-by-chunk via SSE, updating state on each token.
104
120
  Future<void> sendMessage(String prompt) async {
121
+ final conversationId = ref.read(selectedConversationIdProvider);
105
122
  final current = switch (state) {
106
123
  AsyncData(:final value) => value,
107
124
  _ => null,
108
125
  };
109
- if (current == null || current.isReplying) return;
126
+ if (conversationId == null || current == null || current.isReplying) return;
127
+ _streamingConversationId = conversationId;
110
128
 
111
129
  final userMsg = ChatMessage.user(prompt);
112
130
  state = AsyncData(
@@ -117,26 +135,29 @@ class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
117
135
  ),
118
136
  );
119
137
 
120
- _persistMessage(userMsg);
138
+ _persistMessage(userMsg, conversationId);
121
139
 
122
140
  // Pass only the last _kMaxContextMessages messages to the AI.
123
141
  // The full history is still stored in the DB for the user to read.
124
- final allMessages = (state as AsyncData<LlmChatState>).value.messages;
142
+ final allMessages = (state as AsyncData<AiChatState>).value.messages;
125
143
  final history = allMessages.length > _kMaxContextMessages
126
144
  ? allMessages.sublist(allMessages.length - _kMaxContextMessages)
127
145
  : allMessages;
128
- await _requestReplyStream(history);
146
+ await _requestReplyStream(history, conversationId);
129
147
  }
130
148
 
131
149
  // ---------------------------------------------------------------------------
132
150
  // SSE streaming
133
151
  // ---------------------------------------------------------------------------
134
152
 
135
- Future<void> _requestReplyStream(List<ChatMessage> history) async {
136
- final t = ref.read(translationsProvider).llm_chat;
137
- final String llmChatEndpoint = AppEnv.llmChatEndpoint;
153
+ Future<void> _requestReplyStream(
154
+ List<ChatMessage> history,
155
+ String conversationId,
156
+ ) async {
157
+ final t = ref.read(translationsProvider).ai_chat;
158
+ final String aiChatEndpoint = AppEnv.aiChatEndpoint;
138
159
 
139
- if (llmChatEndpoint.isEmpty) {
160
+ if (aiChatEndpoint.isEmpty) {
140
161
  _finalizeAssistantMessage(t.error_not_configured);
141
162
  return;
142
163
  }
@@ -158,7 +179,7 @@ class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
158
179
 
159
180
  try {
160
181
  final response = await dio.post<ResponseBody>(
161
- llmChatEndpoint,
182
+ aiChatEndpoint,
162
183
  data: {
163
184
  'message': history.last.content,
164
185
  'history': history
@@ -199,7 +220,7 @@ class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
199
220
  // Persist the complete assistant reply once the stream ends.
200
221
  final fullContent = contentBuffer.toString();
201
222
  if (fullContent.isNotEmpty) {
202
- _persistMessage(ChatMessage.assistant(fullContent));
223
+ _persistMessage(ChatMessage.assistant(fullContent), conversationId);
203
224
  } else {
204
225
  // Stream ended with no content (e.g. error event)
205
226
  _finalizeAssistantMessage(t.error_no_reply);
@@ -211,19 +232,21 @@ class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
211
232
  AsyncData(:final value) => value,
212
233
  _ => null,
213
234
  };
214
- if (latest != null) {
235
+ if (latest != null && _streamStillActive) {
215
236
  state = AsyncData(
216
237
  latest.copyWith(isReplying: false, streamingStarted: false),
217
238
  );
218
239
  }
219
240
  } catch (error) {
220
- _logger.e('LLM chat stream failed: $error');
241
+ _logger.e('AI chat stream failed: $error');
221
242
  _finalizeAssistantMessage(t.error_network);
222
243
  }
223
244
  }
224
245
 
225
246
  /// Called on every incoming SSE token to update the last assistant bubble.
226
247
  void _appendStreamingChunk(String partialContent) {
248
+ // Ignore chunks if the user navigated to another conversation mid-stream.
249
+ if (!_streamStillActive) return;
227
250
  final current = switch (state) {
228
251
  AsyncData(:final value) => value,
229
252
  _ => null,
@@ -241,22 +264,24 @@ class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
241
264
  : [...msgs, ChatMessage.assistant(partialContent)];
242
265
 
243
266
  state = AsyncData(
244
- LlmChatState(messages: newMsgs, isReplying: true, streamingStarted: true),
267
+ AiChatState(messages: newMsgs, isReplying: true, streamingStarted: true),
245
268
  );
246
269
  }
247
270
 
248
271
  /// Replaces (or appends) the assistant bubble with [content] and marks
249
272
  /// the reply as finished. Used for error messages and empty-stream fallback.
250
273
  void _finalizeAssistantMessage(String content) {
274
+ // Ignore if the user navigated to another conversation mid-stream.
275
+ if (!_streamStillActive) return;
251
276
  final current = switch (state) {
252
277
  AsyncData(:final value) => value,
253
- _ => const LlmChatState(messages: []),
278
+ _ => const AiChatState(messages: []),
254
279
  };
255
280
  final msgs = current.messages;
256
281
  final newMsgs = current.streamingStarted
257
282
  ? [...msgs.sublist(0, msgs.length - 1), ChatMessage.assistant(content)]
258
283
  : [...msgs, ChatMessage.assistant(content)];
259
- state = AsyncData(LlmChatState(messages: newMsgs));
284
+ state = AsyncData(AiChatState(messages: newMsgs));
260
285
  }
261
286
 
262
287
  // ---------------------------------------------------------------------------
@@ -302,14 +327,24 @@ class LlmChatNotifier extends AsyncNotifier<LlmChatState> {
302
327
  // Persistence
303
328
  // ---------------------------------------------------------------------------
304
329
 
305
- void _persistMessage(ChatMessage message) {
330
+ void _persistMessage(ChatMessage message, String conversationId) {
306
331
  final userId = ref.read(userStateNotifierProvider).user.idOrNull;
307
332
  if (userId == null) return;
333
+ final entity = message.toEntity();
308
334
  ref
309
- .read(llmChatApiProvider)
310
- .saveMessage(userId, message.toEntity())
335
+ .read(aiChatApiProvider)
336
+ .saveMessage(userId, conversationId, entity)
311
337
  .catchError((e) {
312
338
  _logger.e('Failed to persist message: $e');
313
339
  });
340
+ // Keep the conversation list preview + ordering in sync.
341
+ ref
342
+ .read(aiConversationsNotifierProvider.notifier)
343
+ .touch(
344
+ conversationId,
345
+ role: message.role,
346
+ content: message.content,
347
+ at: entity.createdAt,
348
+ );
314
349
  }
315
350
  }
@@ -0,0 +1,103 @@
1
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
2
+ import 'package:flutter_riverpod/legacy.dart';
3
+ import 'package:kasy_kit/core/states/user_state_notifier.dart';
4
+ import 'package:kasy_kit/features/ai_chat/api/ai_chat_api.dart';
5
+ import 'package:kasy_kit/features/ai_chat/api/ai_chat_conversation_entity.dart';
6
+ import 'package:logger/logger.dart';
7
+
8
+ /// The conversation currently open in the detail pane (null = none selected).
9
+ ///
10
+ /// On wide layouts a null value shows the "pick a conversation" placeholder;
11
+ /// on phones a null value means the list is showing (no conversation pushed).
12
+ final selectedConversationIdProvider = StateProvider<String?>((ref) => null);
13
+
14
+ /// Loads and mutates the user's conversation list. Backend-agnostic: it only
15
+ /// talks to [aiChatApiProvider] and [AiChatConversationEntity], so this single
16
+ /// file is shared across the Firebase / Supabase / API templates.
17
+ final aiConversationsNotifierProvider =
18
+ AsyncNotifierProvider<
19
+ AiConversationsNotifier,
20
+ List<AiChatConversationEntity>
21
+ >(AiConversationsNotifier.new);
22
+
23
+ class AiConversationsNotifier
24
+ extends AsyncNotifier<List<AiChatConversationEntity>> {
25
+ final Logger _logger = Logger();
26
+
27
+ String? get _userId => ref.read(userStateNotifierProvider).user.idOrNull;
28
+
29
+ List<AiChatConversationEntity>? get _current => switch (state) {
30
+ AsyncData(:final value) => value,
31
+ _ => null,
32
+ };
33
+
34
+ @override
35
+ Future<List<AiChatConversationEntity>> build() async {
36
+ final userId = _userId;
37
+ if (userId == null) return const [];
38
+ try {
39
+ return await ref.read(aiChatApiProvider).loadConversations(userId);
40
+ } catch (e) {
41
+ _logger.e('Failed to load conversations: $e');
42
+ // Graceful fallback: start with an empty list if the backend is down.
43
+ return const [];
44
+ }
45
+ }
46
+
47
+ /// Creates an empty conversation, prepends it to the list, and returns its id.
48
+ Future<String?> create() async {
49
+ final userId = _userId;
50
+ if (userId == null) return null;
51
+ try {
52
+ final conversation = await ref
53
+ .read(aiChatApiProvider)
54
+ .createConversation(userId);
55
+ state = AsyncData([conversation, ...?_current]);
56
+ return conversation.id;
57
+ } catch (e) {
58
+ _logger.e('Failed to create conversation: $e');
59
+ return null;
60
+ }
61
+ }
62
+
63
+ /// Deletes a conversation (and its messages) and removes it from the list.
64
+ Future<void> delete(String conversationId) async {
65
+ final userId = _userId;
66
+ if (userId == null) return;
67
+ // Optimistic removal so the UI feels instant.
68
+ state = AsyncData(
69
+ (_current ?? const []).where((c) => c.id != conversationId).toList(),
70
+ );
71
+ if (ref.read(selectedConversationIdProvider) == conversationId) {
72
+ ref.read(selectedConversationIdProvider.notifier).state = null;
73
+ }
74
+ try {
75
+ await ref
76
+ .read(aiChatApiProvider)
77
+ .deleteConversation(userId, conversationId);
78
+ } catch (e) {
79
+ _logger.e('Failed to delete conversation: $e');
80
+ }
81
+ }
82
+
83
+ /// Updates the denormalized last-message fields and moves the conversation to
84
+ /// the top of the list. Called by the chat notifier after each message.
85
+ void touch(
86
+ String conversationId, {
87
+ required String role,
88
+ required String content,
89
+ required DateTime at,
90
+ }) {
91
+ final current = _current;
92
+ if (current == null) return;
93
+ final index = current.indexWhere((c) => c.id == conversationId);
94
+ if (index == -1) return;
95
+ final updated = current[index].copyWith(
96
+ lastMessageRole: role,
97
+ lastMessageContent: content,
98
+ updatedAt: at,
99
+ );
100
+ final rest = [...current]..removeAt(index);
101
+ state = AsyncData([updated, ...rest]);
102
+ }
103
+ }
@@ -8,19 +8,19 @@ import 'package:kasy_kit/features/settings/ui/widgets/avatar_utils.dart';
8
8
  import 'package:kasy_kit/i18n/translations.g.dart';
9
9
 
10
10
  /// Diameter used beside chat bubbles.
11
- const double kLlmChatAvatarDiameter = 32;
11
+ const double kAiChatAvatarDiameter = 32;
12
12
 
13
13
  /// Diameter shown in the empty state hero.
14
- const double kLlmChatEmptyAvatarDiameter = 64;
14
+ const double kAiChatEmptyAvatarDiameter = 64;
15
15
 
16
16
  /// Bundled launcher icon ([flutter_launcher_icons] → `assets/images/icon.png`).
17
- const String kLlmChatAppIconAsset = 'assets/images/icon.png';
17
+ const String kAiChatAppIconAsset = 'assets/images/icon.png';
18
18
 
19
19
  /// Project / assistant avatar — app icon when bundled, [KasyAvatar] fallback otherwise.
20
- class LlmChatAssistantAvatar extends StatelessWidget {
21
- const LlmChatAssistantAvatar({
20
+ class AiChatAssistantAvatar extends StatelessWidget {
21
+ const AiChatAssistantAvatar({
22
22
  super.key,
23
- this.diameter = kLlmChatAvatarDiameter,
23
+ this.diameter = kAiChatAvatarDiameter,
24
24
  this.showShadow = false,
25
25
  });
26
26
 
@@ -31,7 +31,7 @@ class LlmChatAssistantAvatar extends StatelessWidget {
31
31
  Widget build(BuildContext context) {
32
32
  return KasyAvatar(
33
33
  diameter: diameter,
34
- image: const AssetImage(kLlmChatAppIconAsset),
34
+ image: const AssetImage(kAiChatAppIconAsset),
35
35
  icon: KasyIcons.packageOutline,
36
36
  fallbackSurface: KasyAvatarFallbackSurface.soft,
37
37
  showShadow: showShadow,
@@ -41,10 +41,10 @@ class LlmChatAssistantAvatar extends StatelessWidget {
41
41
  }
42
42
 
43
43
  /// User avatar resolved from profile photo, initials, or [KasyAvatar] fallback.
44
- class LlmChatUserAvatar extends ConsumerWidget {
45
- const LlmChatUserAvatar({
44
+ class AiChatUserAvatar extends ConsumerWidget {
45
+ const AiChatUserAvatar({
46
46
  super.key,
47
- this.diameter = kLlmChatAvatarDiameter,
47
+ this.diameter = kAiChatAvatarDiameter,
48
48
  this.showShadow = false,
49
49
  });
50
50
 
@@ -61,7 +61,7 @@ class LlmChatUserAvatar extends ConsumerWidget {
61
61
  :final email,
62
62
  :final avatarPath,
63
63
  ) =>
64
- _LlmChatUserAvatarContent(
64
+ _AiChatUserAvatarContent(
65
65
  diameter: diameter,
66
66
  userId: id,
67
67
  avatarPath: avatarPath,
@@ -91,8 +91,8 @@ class LlmChatUserAvatar extends ConsumerWidget {
91
91
  }
92
92
  }
93
93
 
94
- class _LlmChatUserAvatarContent extends StatelessWidget {
95
- const _LlmChatUserAvatarContent({
94
+ class _AiChatUserAvatarContent extends StatelessWidget {
95
+ const _AiChatUserAvatarContent({
96
96
  required this.diameter,
97
97
  required this.userId,
98
98
  required this.avatarPath,
@@ -6,11 +6,11 @@ import 'package:kasy_kit/core/theme/theme.dart';
6
6
  import 'package:kasy_kit/i18n/translations.g.dart';
7
7
 
8
8
  /// Maximum characters allowed in a single chat message.
9
- const int kLlmChatMaxMessageLength = 2000;
9
+ const int kAiChatMaxMessageLength = 2000;
10
10
 
11
11
  /// Unified chat bar: primary [KasyTextField] shell (surface, border, shadow) + send orb inside.
12
- class LlmChatComposer extends StatefulWidget {
13
- const LlmChatComposer({
12
+ class AiChatComposer extends StatefulWidget {
13
+ const AiChatComposer({
14
14
  super.key,
15
15
  required this.controller,
16
16
  required this.onSend,
@@ -24,10 +24,10 @@ class LlmChatComposer extends StatefulWidget {
24
24
  static const int _maxLines = 5;
25
25
 
26
26
  @override
27
- State<LlmChatComposer> createState() => _LlmChatComposerState();
27
+ State<AiChatComposer> createState() => _AiChatComposerState();
28
28
  }
29
29
 
30
- class _LlmChatComposerState extends State<LlmChatComposer> {
30
+ class _AiChatComposerState extends State<AiChatComposer> {
31
31
  bool _canSend = false;
32
32
 
33
33
  @override
@@ -45,7 +45,7 @@ class _LlmChatComposerState extends State<LlmChatComposer> {
45
45
 
46
46
  bool _hasSendableText(String value) {
47
47
  final String trimmed = value.trim();
48
- return trimmed.isNotEmpty && trimmed.length <= kLlmChatMaxMessageLength;
48
+ return trimmed.isNotEmpty && trimmed.length <= kAiChatMaxMessageLength;
49
49
  }
50
50
 
51
51
  void _onTextChanged() {
@@ -90,13 +90,13 @@ class _LlmChatComposerState extends State<LlmChatComposer> {
90
90
  controller: widget.controller,
91
91
  enabled: enabled,
92
92
  variant: KasyTextFieldVariant.embedded,
93
- hint: t.llm_chat.hint,
93
+ hint: t.ai_chat.hint,
94
94
  minLines: 1,
95
- maxLines: LlmChatComposer._maxLines,
95
+ maxLines: AiChatComposer._maxLines,
96
96
  textCapitalization: TextCapitalization.sentences,
97
97
  textInputAction: TextInputAction.newline,
98
98
  inputFormatters: [
99
- LengthLimitingTextInputFormatter(kLlmChatMaxMessageLength),
99
+ LengthLimitingTextInputFormatter(kAiChatMaxMessageLength),
100
100
  ],
101
101
  onSubmitted: (_) => _handleSend(),
102
102
  ),
@@ -112,7 +112,7 @@ class _LlmChatComposerState extends State<LlmChatComposer> {
112
112
  size: KasyButtonSize.small,
113
113
  iconOnlyLayoutExtent: 36,
114
114
  iconGlyphSize: 18,
115
- semanticLabel: t.llm_chat.hint,
115
+ semanticLabel: t.ai_chat.hint,
116
116
  ),
117
117
  ),
118
118
  ],