kasy-cli 1.21.9 → 1.23.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/commands/add.js +93 -80
- package/lib/commands/configure.js +100 -32
- package/lib/commands/doctor.js +28 -2
- package/lib/commands/new.js +80 -37
- package/lib/commands/notifications.js +1 -1
- package/lib/commands/remove.js +43 -15
- package/lib/commands/run.js +2 -2
- package/lib/commands/update.js +2 -2
- package/lib/scaffold/CHANGELOG.json +14 -0
- package/lib/scaffold/backends/api/generator.js +14 -14
- package/lib/scaffold/backends/api/patch/README.md +83 -0
- package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +5 -0
- package/lib/scaffold/backends/api/patch/lib/{environnements.dart → environments.dart} +3 -11
- package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_api.dart +108 -0
- package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +51 -0
- 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
- package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +71 -38
- package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +2 -2
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +54 -0
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/models/user_info.dart +16 -16
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +100 -0
- package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
- package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +60 -0
- package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -5
- package/lib/scaffold/backends/firebase/deploy.js +87 -13
- package/lib/scaffold/backends/firebase/generator.js +5 -5
- package/lib/scaffold/backends/firebase/tokens.js +4 -4
- package/lib/scaffold/backends/supabase/deploy.js +63 -11
- package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
- package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
- package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
- package/lib/scaffold/backends/supabase/generator.js +17 -17
- package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
- package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
- package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
- package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
- 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
- package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
- package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
- package/lib/scaffold/backends/supabase/tokens.js +3 -3
- package/lib/scaffold/catalog.js +9 -11
- package/lib/scaffold/generate.js +45 -31
- package/lib/scaffold/shared/generator-utils.js +188 -81
- package/lib/scaffold/shared/sort-imports.js +191 -0
- package/lib/scaffold/shared/template-strings.js +3 -3
- package/lib/utils/checks.js +22 -6
- package/lib/utils/env-tools.js +7 -0
- package/lib/utils/flutter-install.js +114 -0
- package/lib/utils/i18n/messages-en.js +52 -35
- package/lib/utils/i18n/messages-es.js +52 -35
- package/lib/utils/i18n/messages-pt.js +54 -37
- package/lib/utils/updates.js +15 -15
- package/package.json +1 -1
- package/templates/firebase/.env.example +2 -2
- package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
- package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
- package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
- package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
- package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
- package/templates/firebase/firestore.rules +24 -5
- package/templates/firebase/functions/package-lock.json +22 -1
- package/templates/firebase/functions/package.json +2 -1
- package/templates/firebase/functions/src/admin/functions.ts +113 -0
- package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
- package/templates/firebase/functions/src/index.ts +8 -2
- package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
- package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
- package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
- package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
- package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/lib/components/components.dart +4 -1
- package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
- package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
- package/templates/firebase/lib/components/kasy_button.dart +23 -99
- package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
- package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
- package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +702 -425
- package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
- package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
- package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
- package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
- package/templates/firebase/lib/components/kasy_toast.dart +2 -2
- package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
- package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +136 -23
- package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
- package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +54 -0
- package/templates/firebase/lib/core/config/app_env.dart +5 -11
- package/templates/firebase/lib/core/config/features.dart +5 -4
- package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
- package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
- package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
- package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
- package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
- package/templates/firebase/lib/core/data/models/user.dart +11 -0
- package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
- package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
- package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
- package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
- package/templates/firebase/lib/core/states/logout_action.dart +25 -0
- package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
- package/templates/firebase/lib/core/theme/colors.dart +488 -188
- package/templates/firebase/lib/core/theme/radius.dart +22 -11
- package/templates/firebase/lib/core/theme/shadows.dart +66 -0
- package/templates/firebase/lib/core/theme/texts.dart +75 -41
- package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
- package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
- package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
- package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
- package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
- package/templates/firebase/lib/core/widgets/kasy_hover.dart +53 -14
- package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
- package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
- package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
- package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
- package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
- package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
- package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
- package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
- package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
- package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
- package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
- package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
- package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
- package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
- package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
- package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +128 -0
- package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
- package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
- package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
- package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
- package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
- package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
- package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
- package/templates/firebase/lib/features/home/home_feed.dart +289 -0
- package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
- package/templates/firebase/lib/features/home/home_page.dart +11 -250
- package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
- package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
- package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
- package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
- package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
- package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
- package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
- package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
- package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
- package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
- package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
- package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
- package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
- package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
- package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
- package/templates/firebase/lib/i18n/en.i18n.json +171 -46
- package/templates/firebase/lib/i18n/es.i18n.json +175 -50
- package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
- package/templates/firebase/lib/main.dart +6 -3
- package/templates/firebase/lib/router.dart +15 -23
- package/templates/firebase/pubspec.yaml +5 -6
- package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
- package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
- package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
- package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
- package/templates/firebase/test/test_utils.dart +6 -6
- package/templates/firebase/web/index.html +5 -2
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
- package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
- package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
- package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
- package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
- package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
- package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
- package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
- package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
- package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
- /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
- /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/
|
|
7
|
-
import 'package:kasy_kit/features/
|
|
8
|
-
import 'package:kasy_kit/features/
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
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 >
|
|
71
|
+
if (prompt.isEmpty || prompt.length > kAiChatMaxMessageLength) return;
|
|
32
72
|
_controller.clear();
|
|
33
|
-
ref.read(
|
|
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(
|
|
96
|
+
// Scroll to bottom whenever the message list changes (new chunk / message).
|
|
97
|
+
ref.listen(aiChatNotifierProvider, (_, _) => _scrollToBottom());
|
|
58
98
|
|
|
59
|
-
final chatAsync = ref.watch(
|
|
99
|
+
final chatAsync = ref.watch(aiChatNotifierProvider);
|
|
60
100
|
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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,
|
|
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
|
-
|
|
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:
|
|
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
|
|
168
|
-
diameter:
|
|
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.
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
333
|
-
|
|
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
|
+
}
|