From 833c68a1892843504baf8e56e2a1c62440ddf4c7 Mon Sep 17 00:00:00 2001 From: yc Date: Mon, 27 Jul 2026 14:42:23 +0200 Subject: [PATCH] initial commit --- .gitignore | 45 + .metadata | 45 + CLAUDE.md | 161 +++ README.md | 17 + analysis_options.yaml | 36 + android/.gitignore | 14 + android/app/build.gradle.kts | 44 + android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 + .../com/example/manager/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/styles.xml | 18 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle.kts | 24 + android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle.kts | 26 + assets/fonts/Inter-OFL.txt | 93 ++ assets/fonts/Inter-Variable.ttf | Bin 0 -> 876576 bytes assets/fonts/JetBrainsMono-OFL.txt | 93 ++ assets/fonts/JetBrainsMono-Variable.ttf | Bin 0 -> 187208 bytes build.yaml | 6 + devtools_options.yaml | 3 + ios/.gitignore | 34 + ios/Flutter/AppFrameworkInfo.plist | 24 + ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Runner.xcodeproj/project.pbxproj | 620 ++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + ios/Runner/AppDelegate.swift | 16 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 + ios/Runner/Base.lproj/Main.storyboard | 26 + ios/Runner/Info.plist | 70 ++ ios/Runner/Runner-Bridging-Header.h | 1 + ios/Runner/SceneDelegate.swift | 6 + ios/RunnerTests/RunnerTests.swift | 12 + lib/app.dart | 25 + lib/core/connections/connection.dart | 19 + lib/core/connections/connection.freezed.dart | 277 ++++++ lib/core/connections/connection.g.dart | 20 + .../connections/connection_providers.dart | 75 ++ .../connections/connection_providers.g.dart | 232 +++++ lib/core/connections/connection_storage.dart | 41 + lib/core/models/aggregated_node_status.dart | 25 + .../aggregated_node_status.freezed.dart | 301 ++++++ lib/core/models/aggregated_node_status.g.dart | 27 + lib/core/models/backup_config.dart | 25 + lib/core/models/backup_config.freezed.dart | 295 ++++++ lib/core/models/backup_config.g.dart | 37 + lib/core/models/backup_execution.dart | 19 + lib/core/models/backup_execution.freezed.dart | 286 ++++++ lib/core/models/backup_execution.g.dart | 27 + lib/core/models/backup_method.dart | 11 + lib/core/models/backup_target.dart | 24 + lib/core/models/backup_target.freezed.dart | 298 ++++++ lib/core/models/backup_target.g.dart | 45 + lib/core/models/models.dart | 12 + lib/core/models/node_info.dart | 30 + lib/core/models/node_info.freezed.dart | 310 ++++++ lib/core/models/node_info.g.dart | 29 + lib/core/models/remote_node.dart | 21 + lib/core/models/remote_node.freezed.dart | 295 ++++++ lib/core/models/remote_node.g.dart | 32 + lib/core/models/run_state.dart | 24 + lib/core/models/run_state.freezed.dart | 289 ++++++ lib/core/models/run_state.g.dart | 35 + lib/core/models/scan_result.dart | 20 + lib/core/models/scan_result.freezed.dart | 307 ++++++ lib/core/models/scan_result.g.dart | 38 + lib/core/models/service.dart | 29 + lib/core/models/service.freezed.dart | 322 ++++++ lib/core/models/service.g.dart | 37 + lib/core/models/service_scan_info.dart | 18 + .../models/service_scan_info.freezed.dart | 280 ++++++ lib/core/models/service_scan_info.g.dart | 23 + lib/core/models/severity_mapping.dart | 16 + lib/core/models/update_record.dart | 18 + lib/core/models/update_record.freezed.dart | 289 ++++++ lib/core/models/update_record.g.dart | 29 + lib/core/network/api_client_provider.dart | 41 + lib/core/network/api_client_provider.g.dart | 73 ++ lib/core/network/api_exception.dart | 44 + lib/core/network/api_exception.freezed.dart | 528 ++++++++++ lib/core/network/dio_error_mapper.dart | 39 + lib/core/network/node_master_api_client.dart | 261 +++++ lib/core/polling/polling_async_notifier.dart | 37 + lib/core/routing/app_router.dart | 72 ++ lib/core/routing/app_router.g.dart | 51 + lib/core/routing/route_paths.dart | 17 + lib/core/theme/app_colors.dart | 38 + lib/core/theme/app_data_styles.dart | 66 ++ lib/core/theme/app_theme.dart | 146 +++ lib/core/theme/spacing.dart | 16 + lib/core/theme/status_colors.dart | 82 ++ lib/core/theme/theme_mode_provider.dart | 26 + lib/core/theme/theme_mode_provider.g.dart | 64 ++ lib/core/theme/theme_x.dart | 15 + lib/core/utils/relative_time.dart | 12 + .../utils/shared_preferences_provider.dart | 12 + .../widgets/backup_target_form_dialog.dart | 146 +++ lib/core/widgets/backup_target_row.dart | 190 ++++ lib/core/widgets/backup_targets_list.dart | 61 ++ lib/core/widgets/data_table_scaffold.dart | 56 ++ lib/core/widgets/empty_state.dart | 50 + lib/core/widgets/error_banner.dart | 38 + lib/core/widgets/mono_text.dart | 34 + lib/core/widgets/nav_rail_shell.dart | 295 ++++++ lib/core/widgets/slide_over_panel.dart | 131 +++ lib/core/widgets/sparkline.dart | 106 ++ lib/core/widgets/stat_tile.dart | 88 ++ lib/core/widgets/status_pill.dart | 57 ++ lib/data/repositories/node_repository.dart | 79 ++ lib/data/repositories/node_repository.g.dart | 216 +++++ lib/data/repositories/nodes_repository.dart | 60 ++ lib/data/repositories/nodes_repository.g.dart | 180 ++++ .../repositories/services_repository.dart | 57 ++ .../repositories/services_repository.g.dart | 118 +++ lib/features/backups/backups_screen.dart | 267 +++++ .../widgets/backup_config_form_dialog.dart | 101 ++ .../backups/widgets/backup_history_table.dart | 73 ++ lib/features/dashboard/dashboard_screen.dart | 175 ++++ .../providers/dashboard_summary.dart | 14 + .../providers/dashboard_summary.freezed.dart | 292 ++++++ .../providers/dashboard_summary_provider.dart | 39 + .../dashboard_summary_provider.g.dart | 75 ++ .../dashboard/widgets/activity_feed.dart | 133 +++ .../dashboard/widgets/quick_actions_row.dart | 85 ++ lib/features/fleet/fleet_screen.dart | 110 +++ .../fleet/widgets/fleet_card_grid.dart | 134 +++ lib/features/fleet/widgets/fleet_table.dart | 120 +++ .../fleet/widgets/node_form_dialog.dart | 164 ++++ lib/features/services/services_screen.dart | 196 ++++ .../services/widgets/service_detail_body.dart | 172 ++++ .../services/widgets/service_form_dialog.dart | 161 +++ .../services/widgets/services_table.dart | 88 ++ lib/features/settings/settings_screen.dart | 124 +++ .../widgets/connection_form_dialog.dart | 128 +++ .../settings/widgets/connections_list.dart | 104 ++ .../settings/widgets/no_auth_callout.dart | 46 + .../settings/widgets/node_identity_form.dart | 159 +++ .../settings/widgets/theme_toggle.dart | 32 + lib/features/updates/updates_screen.dart | 84 ++ .../updates/widgets/updates_table.dart | 101 ++ lib/main.dart | 28 + linux/.gitignore | 1 + linux/CMakeLists.txt | 128 +++ linux/flutter/CMakeLists.txt | 88 ++ linux/flutter/generated_plugin_registrant.cc | 11 + linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 23 + linux/runner/CMakeLists.txt | 26 + linux/runner/main.cc | 6 + linux/runner/my_application.cc | 148 +++ linux/runner/my_application.h | 21 + macos/.gitignore | 7 + macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 12 + macos/Runner.xcodeproj/project.pbxproj | 705 ++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++ macos/Runner/Configs/AppInfo.xcconfig | 14 + macos/Runner/Configs/Debug.xcconfig | 2 + macos/Runner/Configs/Release.xcconfig | 2 + macos/Runner/Configs/Warnings.xcconfig | 13 + macos/Runner/DebugProfile.entitlements | 12 + macos/Runner/Info.plist | 32 + macos/Runner/MainFlutterWindow.swift | 15 + macos/Runner/Release.entitlements | 8 + macos/RunnerTests/RunnerTests.swift | 12 + pubspec.lock | 914 ++++++++++++++++++ pubspec.yaml | 100 ++ test/core/models/service_test.dart | 70 ++ test/core/models/severity_mapping_test.dart | 28 + test/core/network/dio_error_mapper_test.dart | 92 ++ .../widgets/data_table_scaffold_test.dart | 48 + test/core/widgets/empty_state_test.dart | 44 + test/core/widgets/error_banner_test.dart | 37 + test/core/widgets/mono_text_test.dart | 29 + test/core/widgets/slide_over_panel_test.dart | 51 + test/core/widgets/sparkline_test.dart | 23 + test/core/widgets/stat_tile_test.dart | 50 + test/core/widgets/status_pill_test.dart | 33 + test/fixtures/service_full.json | 38 + test/fixtures/service_minimal.json | 6 + test/widget_test.dart | 57 ++ web/favicon.png | Bin 0 -> 917 bytes web/icons/Icon-192.png | Bin 0 -> 5292 bytes web/icons/Icon-512.png | Bin 0 -> 8252 bytes web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes web/index.html | 46 + web/manifest.json | 35 + windows/.gitignore | 17 + windows/CMakeLists.txt | 108 +++ windows/flutter/CMakeLists.txt | 109 +++ .../flutter/generated_plugin_registrant.cc | 11 + windows/flutter/generated_plugin_registrant.h | 15 + windows/flutter/generated_plugins.cmake | 23 + windows/runner/CMakeLists.txt | 40 + windows/runner/Runner.rc | 121 +++ windows/runner/flutter_window.cpp | 71 ++ windows/runner/flutter_window.h | 33 + windows/runner/main.cpp | 43 + windows/runner/resource.h | 16 + windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes windows/runner/runner.exe.manifest | 14 + windows/runner/utils.cpp | 65 ++ windows/runner/utils.h | 19 + windows/runner/win32_window.cpp | 288 ++++++ windows/runner/win32_window.h | 102 ++ 257 files changed, 17947 insertions(+) create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/example/manager/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle.kts create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle.kts create mode 100644 assets/fonts/Inter-OFL.txt create mode 100644 assets/fonts/Inter-Variable.ttf create mode 100644 assets/fonts/JetBrainsMono-OFL.txt create mode 100644 assets/fonts/JetBrainsMono-Variable.ttf create mode 100644 build.yaml create mode 100644 devtools_options.yaml create mode 100644 ios/.gitignore create mode 100644 ios/Flutter/AppFrameworkInfo.plist create mode 100644 ios/Flutter/Debug.xcconfig create mode 100644 ios/Flutter/Release.xcconfig create mode 100644 ios/Runner.xcodeproj/project.pbxproj create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner/AppDelegate.swift create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 ios/Runner/Base.lproj/Main.storyboard create mode 100644 ios/Runner/Info.plist create mode 100644 ios/Runner/Runner-Bridging-Header.h create mode 100644 ios/Runner/SceneDelegate.swift create mode 100644 ios/RunnerTests/RunnerTests.swift create mode 100644 lib/app.dart create mode 100644 lib/core/connections/connection.dart create mode 100644 lib/core/connections/connection.freezed.dart create mode 100644 lib/core/connections/connection.g.dart create mode 100644 lib/core/connections/connection_providers.dart create mode 100644 lib/core/connections/connection_providers.g.dart create mode 100644 lib/core/connections/connection_storage.dart create mode 100644 lib/core/models/aggregated_node_status.dart create mode 100644 lib/core/models/aggregated_node_status.freezed.dart create mode 100644 lib/core/models/aggregated_node_status.g.dart create mode 100644 lib/core/models/backup_config.dart create mode 100644 lib/core/models/backup_config.freezed.dart create mode 100644 lib/core/models/backup_config.g.dart create mode 100644 lib/core/models/backup_execution.dart create mode 100644 lib/core/models/backup_execution.freezed.dart create mode 100644 lib/core/models/backup_execution.g.dart create mode 100644 lib/core/models/backup_method.dart create mode 100644 lib/core/models/backup_target.dart create mode 100644 lib/core/models/backup_target.freezed.dart create mode 100644 lib/core/models/backup_target.g.dart create mode 100644 lib/core/models/models.dart create mode 100644 lib/core/models/node_info.dart create mode 100644 lib/core/models/node_info.freezed.dart create mode 100644 lib/core/models/node_info.g.dart create mode 100644 lib/core/models/remote_node.dart create mode 100644 lib/core/models/remote_node.freezed.dart create mode 100644 lib/core/models/remote_node.g.dart create mode 100644 lib/core/models/run_state.dart create mode 100644 lib/core/models/run_state.freezed.dart create mode 100644 lib/core/models/run_state.g.dart create mode 100644 lib/core/models/scan_result.dart create mode 100644 lib/core/models/scan_result.freezed.dart create mode 100644 lib/core/models/scan_result.g.dart create mode 100644 lib/core/models/service.dart create mode 100644 lib/core/models/service.freezed.dart create mode 100644 lib/core/models/service.g.dart create mode 100644 lib/core/models/service_scan_info.dart create mode 100644 lib/core/models/service_scan_info.freezed.dart create mode 100644 lib/core/models/service_scan_info.g.dart create mode 100644 lib/core/models/severity_mapping.dart create mode 100644 lib/core/models/update_record.dart create mode 100644 lib/core/models/update_record.freezed.dart create mode 100644 lib/core/models/update_record.g.dart create mode 100644 lib/core/network/api_client_provider.dart create mode 100644 lib/core/network/api_client_provider.g.dart create mode 100644 lib/core/network/api_exception.dart create mode 100644 lib/core/network/api_exception.freezed.dart create mode 100644 lib/core/network/dio_error_mapper.dart create mode 100644 lib/core/network/node_master_api_client.dart create mode 100644 lib/core/polling/polling_async_notifier.dart create mode 100644 lib/core/routing/app_router.dart create mode 100644 lib/core/routing/app_router.g.dart create mode 100644 lib/core/routing/route_paths.dart create mode 100644 lib/core/theme/app_colors.dart create mode 100644 lib/core/theme/app_data_styles.dart create mode 100644 lib/core/theme/app_theme.dart create mode 100644 lib/core/theme/spacing.dart create mode 100644 lib/core/theme/status_colors.dart create mode 100644 lib/core/theme/theme_mode_provider.dart create mode 100644 lib/core/theme/theme_mode_provider.g.dart create mode 100644 lib/core/theme/theme_x.dart create mode 100644 lib/core/utils/relative_time.dart create mode 100644 lib/core/utils/shared_preferences_provider.dart create mode 100644 lib/core/widgets/backup_target_form_dialog.dart create mode 100644 lib/core/widgets/backup_target_row.dart create mode 100644 lib/core/widgets/backup_targets_list.dart create mode 100644 lib/core/widgets/data_table_scaffold.dart create mode 100644 lib/core/widgets/empty_state.dart create mode 100644 lib/core/widgets/error_banner.dart create mode 100644 lib/core/widgets/mono_text.dart create mode 100644 lib/core/widgets/nav_rail_shell.dart create mode 100644 lib/core/widgets/slide_over_panel.dart create mode 100644 lib/core/widgets/sparkline.dart create mode 100644 lib/core/widgets/stat_tile.dart create mode 100644 lib/core/widgets/status_pill.dart create mode 100644 lib/data/repositories/node_repository.dart create mode 100644 lib/data/repositories/node_repository.g.dart create mode 100644 lib/data/repositories/nodes_repository.dart create mode 100644 lib/data/repositories/nodes_repository.g.dart create mode 100644 lib/data/repositories/services_repository.dart create mode 100644 lib/data/repositories/services_repository.g.dart create mode 100644 lib/features/backups/backups_screen.dart create mode 100644 lib/features/backups/widgets/backup_config_form_dialog.dart create mode 100644 lib/features/backups/widgets/backup_history_table.dart create mode 100644 lib/features/dashboard/dashboard_screen.dart create mode 100644 lib/features/dashboard/providers/dashboard_summary.dart create mode 100644 lib/features/dashboard/providers/dashboard_summary.freezed.dart create mode 100644 lib/features/dashboard/providers/dashboard_summary_provider.dart create mode 100644 lib/features/dashboard/providers/dashboard_summary_provider.g.dart create mode 100644 lib/features/dashboard/widgets/activity_feed.dart create mode 100644 lib/features/dashboard/widgets/quick_actions_row.dart create mode 100644 lib/features/fleet/fleet_screen.dart create mode 100644 lib/features/fleet/widgets/fleet_card_grid.dart create mode 100644 lib/features/fleet/widgets/fleet_table.dart create mode 100644 lib/features/fleet/widgets/node_form_dialog.dart create mode 100644 lib/features/services/services_screen.dart create mode 100644 lib/features/services/widgets/service_detail_body.dart create mode 100644 lib/features/services/widgets/service_form_dialog.dart create mode 100644 lib/features/services/widgets/services_table.dart create mode 100644 lib/features/settings/settings_screen.dart create mode 100644 lib/features/settings/widgets/connection_form_dialog.dart create mode 100644 lib/features/settings/widgets/connections_list.dart create mode 100644 lib/features/settings/widgets/no_auth_callout.dart create mode 100644 lib/features/settings/widgets/node_identity_form.dart create mode 100644 lib/features/settings/widgets/theme_toggle.dart create mode 100644 lib/features/updates/updates_screen.dart create mode 100644 lib/features/updates/widgets/updates_table.dart create mode 100644 lib/main.dart create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/runner/CMakeLists.txt create mode 100644 linux/runner/main.cc create mode 100644 linux/runner/my_application.cc create mode 100644 linux/runner/my_application.h create mode 100644 macos/.gitignore create mode 100644 macos/Flutter/Flutter-Debug.xcconfig create mode 100644 macos/Flutter/Flutter-Release.xcconfig create mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 macos/Runner.xcodeproj/project.pbxproj create mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner/AppDelegate.swift create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 macos/Runner/Base.lproj/MainMenu.xib create mode 100644 macos/Runner/Configs/AppInfo.xcconfig create mode 100644 macos/Runner/Configs/Debug.xcconfig create mode 100644 macos/Runner/Configs/Release.xcconfig create mode 100644 macos/Runner/Configs/Warnings.xcconfig create mode 100644 macos/Runner/DebugProfile.entitlements create mode 100644 macos/Runner/Info.plist create mode 100644 macos/Runner/MainFlutterWindow.swift create mode 100644 macos/Runner/Release.entitlements create mode 100644 macos/RunnerTests/RunnerTests.swift create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 test/core/models/service_test.dart create mode 100644 test/core/models/severity_mapping_test.dart create mode 100644 test/core/network/dio_error_mapper_test.dart create mode 100644 test/core/widgets/data_table_scaffold_test.dart create mode 100644 test/core/widgets/empty_state_test.dart create mode 100644 test/core/widgets/error_banner_test.dart create mode 100644 test/core/widgets/mono_text_test.dart create mode 100644 test/core/widgets/slide_over_panel_test.dart create mode 100644 test/core/widgets/sparkline_test.dart create mode 100644 test/core/widgets/stat_tile_test.dart create mode 100644 test/core/widgets/status_pill_test.dart create mode 100644 test/fixtures/service_full.json create mode 100644 test/fixtures/service_minimal.json create mode 100644 test/widget_test.dart create mode 100644 web/favicon.png create mode 100644 web/icons/Icon-192.png create mode 100644 web/icons/Icon-512.png create mode 100644 web/icons/Icon-maskable-192.png create mode 100644 web/icons/Icon-maskable-512.png create mode 100644 web/index.html create mode 100644 web/manifest.json create mode 100644 windows/.gitignore create mode 100644 windows/CMakeLists.txt create mode 100644 windows/flutter/CMakeLists.txt create mode 100644 windows/flutter/generated_plugin_registrant.cc create mode 100644 windows/flutter/generated_plugin_registrant.h create mode 100644 windows/flutter/generated_plugins.cmake create mode 100644 windows/runner/CMakeLists.txt create mode 100644 windows/runner/Runner.rc create mode 100644 windows/runner/flutter_window.cpp create mode 100644 windows/runner/flutter_window.h create mode 100644 windows/runner/main.cpp create mode 100644 windows/runner/resource.h create mode 100644 windows/runner/resources/app_icon.ico create mode 100644 windows/runner/runner.exe.manifest create mode 100644 windows/runner/utils.cpp create mode 100644 windows/runner/utils.h create mode 100644 windows/runner/win32_window.cpp create mode 100644 windows/runner/win32_window.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..89db5de --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ff37bef603469fb030f2b72995ab929ccfc227f0" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: android + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: ios + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: linux + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: macos + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: web + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + - platform: windows + create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4468b12 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,161 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +NodeMaster Manager — a Flutter GUI client for the NodeMaster API, a Go/Beego +backend that lives in the sibling repo `../nodemaster` (i.e. +`/home/yves/Documents/code/PROJECTS/nodemaster/nodemaster` if this repo is +checked out at `/home/yves/Documents/code/PROJECTS/nodemaster/manager`). +NodeMaster manages a homelab-style host: docker-compose "services" (start/ +stop/backup), restic/rsync backup targets, OS update history, and a fleet of +other NodeMaster nodes. This app is the multi-platform (Linux/macOS/Windows/ +web/Android/iOS) desktop-first UI over that API's REST endpoints under `/v1`. + +**The Go API is the source of truth for wire shapes.** Every Dart model in +`lib/core/models/` mirrors a Go struct in `../nodemaster/models/`, field for +field, including `@JsonKey` renames for snake_case. When the API changes, +re-read the actual Go source (`models/*.go`, `controllers/*.go`) rather than +trusting existing Dart models or prior assumptions — they can drift, and +swagger docs in that repo have been observed stale relative to the +controllers. Neither repo currently has authentication; the API is meant to +sit behind a VPN/reverse proxy (see the `NoAuthCallout` banner on Settings). + +## Commands + +```bash +flutter pub get # install deps +dart run build_runner build --delete-conflicting-outputs # regenerate *.freezed.dart / *.g.dart +dart run build_runner watch --delete-conflicting-outputs # regenerate on save, for long edit sessions +flutter analyze # static analysis (must be clean) +flutter test # full test suite +flutter test test/core/models/service_test.dart # single test file +flutter test --plain-name "parses a fully-populated service" # single test by name +flutter run -d chrome # run as web app +flutter run -d linux # run as Linux desktop app +flutter build linux --debug # build the Linux bundle (build/linux/x64/debug/bundle/manager) +``` + +**Any change to a `@freezed` or `@riverpod`/`@Riverpod` class requires a +`build_runner build` afterward** — the `.freezed.dart`/`.g.dart` companion +files are committed generated code, not build artifacts recreated from +scratch each time, so a source edit without regeneration leaves the app +compiling against stale generated code (or plain compile errors if fields +changed). If a diagnostics pass immediately after a `build_runner` run shows +errors referencing symbols you didn't touch, it's very likely IDE/analyzer +staleness from the file swap — rerun `flutter analyze` fresh before +concluding something is actually broken. + +To run the app against a live API, start the sibling Go repo first +(`cd ../nodemaster && go run main.go`, default port `8080`, config at +`~/.nodemaster.conf`), then add a connection in Settings pointing at it (or a +pre-seeded `SharedPreferences` `connections` value in tests — see +`test/widget_test.dart`). + +## Architecture + +### Layering: `core/network` → `data/repositories` → `features/*/providers` → screens + +- **`lib/core/network/node_master_api_client.dart`** — one `NodeMasterApiClient` + class, one method per REST endpoint, thin (dio call in, typed model out). + Every method funnels through `_guard`, which enforces a hard 20s request + budget (`Future.timeout`, not just Dio's own timeouts — needed because + Dio's web/browser adapter has no connect-phase timeout hook) and maps every + failure mode to a typed `ApiException` via `dio_error_mapper.dart`. This is + the *only* place that touches `Dio`/`DioException` directly. +- **`lib/data/repositories/*_repository.dart`** — one repository per REST + resource group (`NodeRepository`, `ServicesRepository`, `NodesRepository`), + each a thin pass-through over `NodeMasterApiClient`. Each repository has a + paired `@riverpod` provider (e.g. `nodeRepositoryProvider`) that returns + **null when there's no active connection** — this null-propagation pattern + is used consistently up through `apiClientProvider` so every downstream + provider gets the same "no connection" signal without re-deriving it. +- **`lib/features//`** — one folder per nav destination (`dashboard`, + `services`, `backups`, `fleet`, `updates`, `settings`), each with a + top-level `_screen.dart`, a `widgets/` subfolder for + feature-local dialogs/rows, and a `providers/` subfolder where the screen + needs bespoke async state beyond a bare repository call. + +### State management: Riverpod 3 with code generation + +All providers use `@riverpod`/`@Riverpod(...)` annotations + generated `.g.dart` +companions (not the older manual `Provider`/`StateNotifierProvider` syntax). +`main.dart` disables Riverpod 3's container-wide auto-retry +(`retry: (retryCount, error) => null`) because the app already has its own +explicit refresh model (manual pull-to-refresh + `PollingMixin` for two +screens) — a blanket retry-with-backoff would silently fight that. Widget +tests must pass the same `retry: null` override or a deliberately-unreachable +test connection won't settle into `AsyncError` deterministically. + +`lib/core/polling/polling_async_notifier.dart`'s `PollingMixin` is mixed +into an `AutoDisposeAsyncNotifier` to re-fetch on a timer while the provider +has a listener (used by Dashboard's summary and Fleet's aggregated view — +the API has no push/websocket, so this is a deliberate "poll while visible" +model, not a live stream). It never emits an intermediate `AsyncLoading` on +refresh ticks, so a background poll never flashes the UI back to a spinner. +Other list-backed screens (Services, node backup config, updates) are +manual-refresh only, on purpose — a silent background refresh could yank +state out from under an in-progress edit dialog. + +### Connections (multi-host support) + +`lib/core/connections/` models a list of saved API endpoints +(`Connection { id, name, baseUrl }`), persisted as plain JSON in +`SharedPreferences` via `ConnectionStorage` (explicitly *not* a secret store +— the API has no auth, so a saved base URL isn't sensitive by itself). +`activeConnectionProvider` (in `connection_providers.dart`) is the single +source everything downstream derives from: `apiClientProvider` rebuilds +(never mutates) a fresh `Dio`/`NodeMasterApiClient` whenever it changes, which +cascades through every repository and feature provider automatically. + +### Routing + +`lib/core/routing/app_router.dart` uses `go_router` with a single +`@Riverpod(keepAlive: true)` `GoRouter`. A `_ConnectionRefreshListenable` +bridges `activeConnectionProvider` into go_router's `refreshListenable` so +removing the active connection redirects to Settings immediately rather than +waiting for the next navigation. All feature screens sit inside one +`ShellRoute` wrapping `NavRailShell` (the persistent side nav). + +### Models (`lib/core/models/`) + +`@freezed` sealed classes + `json_serializable` (`explicit_to_json: true` in +`build.yaml`), one file per Go struct, with `part 'x.freezed.dart'` / +`part 'x.g.dart'`. Field renames to match the API's snake_case use +`@JsonKey(name: '...')`. Write-only fields the API never echoes back (e.g. a +password) should default to `null`/omitted-on-null (`includeIfNull: false`) +rather than round-tripping a value the server will never actually send. +`lib/core/models/models.dart` is the barrel export — import that, not +individual model files, from feature code. + +### Theming + +`lib/core/theme/` defines two custom `ThemeExtension`s — `StatusColors` +(good/warning/serious/critical, reserved exclusively for status semantics, +never reused as a brand color) and `AppDataStyles` (monospace styles for +numeric/data display). Access via the `BuildContext` extension in +`theme_x.dart`: `context.status.good`, `context.dataStyles.dataMono`, +`context.colors`, `context.text` — prefer these over raw +`Theme.of(context).extension<...>()!` calls. `StatusPill` (`core/widgets/`) +is the shared good/warning/serious/critical/neutral badge — reuse it rather +than building ad hoc colored chips. + +### Error handling + +`ApiException` (`core/network/api_exception.dart`) is a sealed/freezed-style +type with variants for timeout, network, cancelled, server (with status code ++ message), parse, and unknown. UI code catches `ApiException` and reads +`.userMessage` for display; it should never need to inspect `DioException` or +raw status codes directly. + +## Testing conventions + +Widget tests spin up a real `ProviderScope` with `retry: null` and +`SharedPreferences.setMockInitialValues(...)` to seed connection state (see +`test/widget_test.dart`) rather than mocking providers individually — this +exercises the actual router-redirect and provider-null-propagation logic. A +deliberately unreachable connection (`http://127.0.0.1:1`) is used to get a +fast, deterministic connection-refused error without depending on real +network access. `mocktail`/`fake_async` are available as dev dependencies for +narrower unit tests (e.g. `dio_error_mapper_test.dart`). diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a5c8cf --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# manager + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..bd28abc --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,36 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +analyzer: + errors: + # False positive on every freezed class that puts @JsonKey on a factory + # constructor parameter (the standard, documented pattern) — freezed + # forwards the annotation to the generated field correctly; the analyzer + # just doesn't see that at the constructor-parameter site. + invalid_annotation_target: ignore + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..51518a9 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.manager" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.manager" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..43af087 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/manager/MainActivity.kt b/android/app/src/main/kotlin/com/example/manager/MainActivity.kt new file mode 100644 index 0000000..b07acfa --- /dev/null +++ b/android/app/src/main/kotlin/com/example/manager/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.manager + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..fbee1d8 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e4ef43f --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..ca7fe06 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") diff --git a/assets/fonts/Inter-OFL.txt b/assets/fonts/Inter-OFL.txt new file mode 100644 index 0000000..21f6aff --- /dev/null +++ b/assets/fonts/Inter-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/Inter-Variable.ttf b/assets/fonts/Inter-Variable.ttf new file mode 100644 index 0000000000000000000000000000000000000000..047c92f6e2212473dc436020afed689527076d44 GIT binary patch literal 876576 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

Ek&TdifBe7Pd^mmY)(962bN_SP%pQaf-0_4xU_4 zrq2qpIh6h`)Bi3QOH?N;l$%)7C1MkcM}Uh(aVi>HgrHsM6{hkJc$Q#q8uD*lBBEC{ zE)z8@4o8Jg>yj#}vrvp+lWj>B|AtR*x#-BmOH`N3zP-EGnfvwhE3oG$MLam02dC!> zT$V2M3V2}79?0OHz5E3K@ey|AF!_~-vubjX5-j9EsxW&73)!8=1v=9oVrPF45IImZ zQAVe#p}Jy0WlgejaRzr*l-JcYwk*eLcx`z#@W<`eCA@+2ab$|aO@dDHg@P%PFBD{v ze6b-7uxQePl90=m_%In__tKJkSxs#V&M%|KCaWqMmy48zY7a9b!5K=$rtL^+IGRya zTVCJDR_>7mdVU&Mu-p&hN%~4eZWXOxKpnH7AgtFU(f0(g3f(I9mI+={K)k2a z=Rsm68mE>@dM`oN$;O4X4XoQXw?H8+QjUs6mq?V-01x45N5$ewBr4VeB2lqu0-Jg+ zDd0$Rg?Pdrd`f&T)(x;jQt<6)St1J?3|Nbx0RBoQ_5~0d7i%$q1?LHqIu2(ecyeK( zRF8zk5dhdC*;27=5y=8SQJuhHER;0fLO!*5vE?~-E9GL)2 z`3rq8V9Hvv!JM#n6farHcwD`RS!N{ zf%M^fki?OKd!`hTevBSmRtPT@>mI(4^#XPtqS|s>j>k>b2;#6SCzE3ve zv|!CXEMf?*DsmN^k=P=6rMZ*;T~3fo`JOg5?6F}LBN;Z*kqf)<$c6oMAUR-Dc8i5Yf{QJIpvy?avh#>9g%WPO}VC}T+>so87bGylxtSXH9O^+lX4xI zavha&%}u%H`K}B>mZH*J`%DOM`7BHLXVUCSKtdT!(K5K6+y91BCw)V+V}iJFm&#NS ztq2Ns3`!^6F~}p;F$fNH4F8i<$MB=1I|ljq9mD@3)iKB;*fIQ1NcTpd=okc~ItIbi zG5n}vR!6m`iJgFYEQIEq_ehG{!|x+Leti9_pA#SG(Pan@bQwQ}-(|YR;9Z7%f?dY< z3UnFr>fU9@lSu*CeI4PmhAbZd{bS-K!lDI1fNxNxkXU{oBtH->5-tjai0!U^YJ5{{ zv?tGrcE|?e#saBe`jHNSJQxH-F?e|~-m(7bmluO42cSG;>m|%TE4?7%M}v+OJs)8` zebuJGCwzMOK7s}D1h`i!Mm7i2!Qu;FH_DUklS+hCsNAuJ`C;H z6w9koC;%MtLOBYvO8iL;2SxkD!ORfGfrzNQ|5Ydzf&&n|i76x$<00AUD*%Eq`L<_2 zrpQ?oqfC@3E77eO+35rPfmBL42~a9wQO4}RL2|#$*bX18sh~vqEFy}Tl|D2a_==JY z9PmvCmIbR24c1&xGo_D%z3FdaL2Si>-^4^Q1P%m+QUW;1%M!?%+6^Jp6zMXs)1HBx z%7Qpebjv9iP4pVl~i1aZmEOAuvX8)`70f?#R|!PLaQ_QI^$|Ey6s z&if-3s4$%#wm$k?rPB)u%x9+$QUwFkZ3NaJ za;82mQqRKUBBWg5_(>me3Zzn+Zg!kv5@pOzpX>?5>{jMTu$U21Os1x0J*l=^wxUQl z4wJ4Hj<;}t&c<+nDYuExu}d>psHn&)bxeZ@Q4C2w5d0t(#3$C#{B$vi3Q}?`*ak%X zS&jcFJ34+E(d^_xAic66))U=w3PuxyAQd$~Sk(M<(%=HY)Cz({EeIC1AXq)@F;7>o zAec0i2Z4}aJ_SUj>7CMkL{Mq4J7T|dASB(r5iQ+O0R;Oq_D=iXgcH~qod&SAIRFU! zWTkc~2f~ViCC5ha^jAee3>As=vR3?~$-p0i=!Z^-C6S%p=^IEAcc!pF(fQdq&;ZB| zb8@gpxhn1+p|KWEN9Jvw2E~r~)VI9dgQ09w zI}hcfDz3%}0_n!adENkx#ey^z3ra}1^&yZzBKS=rK$qAR9*9PRgeZY0sGQe~U(6AR z7##Xy@if7qbZMf(MyxcQ14xrLfZ#Vodz5XOmP6QM%`@$`wUhhoxhIl)?uF$;c5{(W z>s%wADmCsSNe|@!(m2Id#$ZU0M|lYmEi1Ji&(rf)Cs?mY3*gL&?0JmtvUp);5(;~i zu33Ce+70$Ob|8>ygF(H;QW4W|V^pe272cQQ3ww)=18l1k+d9A_VBv^m%hahN@6^sI z4hp3{cBc3ci%d|@^pt^odIb_AFWxW_qK97p6Ltel3WNq^53ejh%Ter+VHM?RMseRu z8VHDf(8Z;R6hNAEaLAsp%n z)mCbg2KVU)eXNicIBl`D6Yk#&(V(%#s!hr(hp)iEpZnBR@_0S?xhZ(&{llYG#MsL( zd1?X2kNPhmdw#=H59XKpCLOZpH>u^Jf8>)6p>GEGWfRA7f*Ji=kpGp4?SIR^suZ1M zz`q0$V)o*TQ{6uLpF=1oPE$0;=YtiW@CS$wHwN)$5D}wkstSbzQcno$g6U{9#}@<} z4hU2sI`~@1h9VzG^ARK&NQLJ3A{CnB`+z70=d-CfKJ^cpGXBeax+pcF_vh1TAAbU0 z^v@}N5tifl#v<%@L?uhu_57%#cb)dp$>;iYb$MWqygS~9lJGx ze-JLgL1DJ@N8w^65_@au zJF)Wv@dTyCsDs6bf@0!?HjX0r^`!EMB{0;RzVlC5i&Kd37nB+krw)M!`c9np#ZeB? zfn!9GF{1F8*!vGWP<-0cPfLQRzh7Va-oIT?EKxG?THphh_%Oy9qw_;CaekNy+2l{< z6Zeat{*<1ec;H9mA0_!=i`i9qJk@AvnbOcIoZ=ueHl~UKM+@mg zDrgjUuCViOem1^cVDRL^8csryA63| z0OlpY$vZ&`QM!LCUg}v*xTK*0!klnGf_AtMmkn+1WNVbi#GPwuts%UKn zhh;d%(Py#qCM+-mn0IQ}yvDF;y_?Rtghi)>KPOt)ymP?jjSri5D%fJ(I4s%@FvlEh zjyc#qTcmshJBA>{nFKcE@c1Rg;t(hI%`Wu26n{7PfJ>)K-?W@d|9%5({4o7Gw%X1G}T6 zd~qh4087V}jc5n(YCfU|SZ`4z1B=Vart&&A;4RJ$0ub<7+0ah}zl?|_`%pwI>BAQN zGD1hXB6O)-#2?`4a=8fID}YHAPcK4;iX(K$IO5-~=-(CMUkXP@yd!iwRK&l=iTGiJ z`g_DLpDznh|BukAVfo^^p6BmoqZGmyRARgUnL6;J>$!p8T!vw}PoiqbqGO7ny$UH=s&F(pc3OO$Sah>AU#p{Us51DnQH+TjCj3U7@#?KFZyg~3dh zLjHWDf&NmoR-6uA$LZj8oNii+)4}Vw*qMa*{`Z^;0rPZW`}v91juX|yiCW@xXgW@Z zrsH&II!;#;#r<;8osV(479~!1WyXoh;zVU}qOQ1LlUPQgdeA}NI34tj(?Q=j$sBQ- z`o)PV<3#mwnizz<5aW5MPmH)5oQ&sj>Wk2&UlBT28zJE}B1#>KBuaC91x8StlfjiS zq$|q6m`Jyq)K&KJqjyuVq!LLhWDxaVRVSHTYr->wIw$pETKn^_o~{3yaclbfbm?0w zd!!1?H93J;>Eg9k_DmOqqVd54-8iqc(iVzYOjt8`${2-Nq%02b?FI}KVT2}Z6RoU4 zJRfU(>oaDmEFuoDLF@w1Esy}5U8t?94uob34+_q%gtXOEUQydn)ymT9$@Y7RcLP!} z*!~L02groQ=m7(ph{a@%c|ak$r%sTm5z~duA`#u zx}+RaQ5N6#fWD!rk$Jf6E(7{LqYgqS6M9%cwhs_KQ!qu&6eQzAV5C9_;<=Ab}a22ymkv%RvBs#Iy*;!k|G5NMy{% zS`LgeN4B7llp59Lv<&=$zSHj3$_!Qv9v1-t0QC89n#6zcY_04i%FAB|VR<8^?%7v1 z;5-xX&GX?ge4jM!`#wA^iqkhuRl!dK-ofwtq+>H;_-7iyqc7*2ak+Ov4Q=FY5Ex{RSS{tFcScG<{ zg0IqDWF-sO{xJf?z1)4pMP&RkM#%4ehuHhyy%#hkm$&rZBaHQgO!8aN#BOw?9B^U+ z^8!CQ2NsAmmY(V8wUrAPzUe}=g|%2vSb-bSnkWr%2VT~aq<{Q^@c<##Vw(xO=#l}r z{1iK13BZZZ2nX2VNTyC)fEYk@BCD>v345E_nOl}+HXRk_`$w^g!&8NBCy!;}vTq;< z;a8mE=$wNtXyAM5FrR^-xHQba9A= z!~%2=!P@+$%H+iDLl#}oRpL9Q`i|MYquh5SeMh73SmDs;xb`fAA223{kcAcjOTije zHR~CcPcNBbqaQJtY&70tc0?iwGj>LB4;IexPac`f1M7!k1*Zq^VvL+9SJ%%5!>= zy`UL)&9%TsjBQvO07m$sKR+OAf#|F3f>DSKIl<1#?xsmH&&7?nh`ttg05Xc?y8Fro zNtWc@yYb>D9>d0)qWn*NU6M3MQluO4yB{GB%6%nS?vEGgWxPj!EzIlXHkhB{&30Md zp-hluWs=U-?H+CvJvJx%DXT>Qoe@yt+Eqlmx}m!*ZL@!N2^sZtJPMR z%hi=IPgGBWd5U@!%xhG9g_oJ}6?O2p>}wC7`4W`!jHgw zLVFeFo7xtb-)UQ6Zqt5&xn0{1^EX|RWW4N)^58AsOqf0N9x(BmFU&rAKbU*zxiE+6 z!(bk)9}F{355tV=C?{U(ErK~pKNRL+dI`*l`b3zM^~o@&>QiA(*Jr?-tIvgbw0<;9 zyuS+*FYm&-$HA;Hnqe+Amczu` zrZDlUDa?zEOJL%?QkZudEVXBh=V7il*2Babq%hww-hlbG@ixr&jgMh&GB&~7YWyOp zreYeBY|b&~z-%`fy=ASVCGXIiP zyd$|#lJN>7^1=&+U%+g)zJvb{RtMa_TE9v%ULDMWnQdoFDtk{5ZoDT5bCA71%prJ3 zP-d?P!i?I$GkY}<=Akz5jQ0Y^!NiM!FcWqH=0tlU%){-&VNSJybG#Th9p+4XCd{L4 z;2iG&Ho-i}J`Ls>_SrDcwNXdB?RPKCM{UpvUfcuC+wa;R!2HDi1m-6D8<<-i&@En} zYnD{JAa@eX3!QeDolYmroen4*ucv7+2f71c4t0mZ9OjOIImR6$sd(uOC1)?2!JOhw zkrcdZhH~O$&G&aQC{GUViVxOIsNt-wEy6|{80&S`jPW-O*I%D z`$%2A?&J2~cRN|RdErQ6`h2_>&AheMx8Jv}{hN;8b}Jc!hmSrqF@D zz6U}XHWmETb<(=T`mXiT`mt@DZBlz~hx41!C3PvR(KH~$_}i46&YX_nZ3$`mgGX+` zTi7CZwJoQ^>Bv#K*b96)9eDSd`74ro>Zuq0`oNawH*IpaZ2BRqqhtG)?e=f(KH2;1 zbHJ8FV*Y%*)+}ObtCc%8KJd^N@8PMxgSL$Nrt_QJ?|cG>}PFKgC(IXV?UA(Y-=0dCT(c%?D+QA>>s+6-=(Y_lD_lhH+SyRc6|yyo+WSHEz7%Q zW4H2)swiEG+NGGFXi4earS{&fnX+aM(ZAC4eRuYgw&qFiC4jZWI%zEBu1eZD%a@g{z)&R#oM$oUG2^+?=Z_H_b zyUqE){Q+of8@{n^c;_eWZJ*2X=FL4e??1df$7#>a{pOE5w>kZ{<@E2!Y3s-VvU7gQ zFn;aX@3)Nae|NGv+uAxE=MUT7E%%q!Wk?;JQfH^^{@`?WX8iVd?;n56X=~5vK-1;m zZ-6ZClhHBc^WnqWJ4bx>{h=?mCDyH*uzh>&)mQUTRzpo#ldBu=$n`!t=$&8MJ2&K@ z2zYX_v$L};*Xe8rYshJD-`QmjmpYMGPG^VJ)#oQ?BTAOr(dOjlZ0zxI!Jj#8UdI6g zzU;!Q+~|0es&@KrW!%LNA9MIk_q2>$)9F<`eC_ggo`>J-83*jYG1Bh5`_ag+Km65| z+aB?XzTFi5?z>H)@4w#^{-(oe&*^+PVRx=Q^6SpEbK5VWO!>p(?2bEWp-8>Non z?ErG5&YVuEYqu=zhU|b6OS$m#Iy$w-!z$id_e+9yy2cs(Qn9B z7|<%nbiKTx%>j*$iEQW`Te$BB#;)xjo-usn$D2lsY+F4%ANl08 z<+N}7{L`GS&y~$No8KHXYx8DjQ2W-ebGQ7t&pzK?F<{`gn+NRNdPYv|)?W|!ZripI zeL6OR4myYDbo`m)Y`3%iz)#-3&qn3emf-A6Q)mpP?|q~wW{v=Q#E(}VPy9I z(H#2ZcBd1ya&kKz(4vzwygjSEGtxT?q=};R+q$)1zdzkw{r+}$4A|P)Z*X?UCU|!a z&uzP-ptJOgQ3t*J@|gBW=a|khZHJC%`wH2tBUUN7g0;82dfeKZ+oX<- z?YZrp?PGF~hJ&^n)6uyqXAD|sc&Frabm__tM`8tGE!)j-spAmTlYb>Zi-T8^jE_5-^@G+p^y^TMxKv?Del7p0n4gJ6hG@tJ*gX_f~Bk zzI*J=chvX!^dtRCFNA7)Z)Qrr#@9mlqdG~KBmUrF%Rq?J}53Eb{-S$BHsGU0>>@pr2 zx8}7nXUZBNdia`-4##=u?ZXE@_wX0{4&D05lY_FnN1oX~%X{pd5&La>;?5_EN36a4 z!~q|yy=!gJ@U>4LYsJ=ncCa(z$;ac?m?yv4vhTzvw{F>c;!~T4{h>dV+u@C2#> zC3YUVZsXJ;cdYxeK>cQ2$F8&TpZ?*V!2S5E|=b5pmw~zWc z{&Z(#+mxp}NBuwYz63s}EB*i6``&xsC98;tAWcysB6-*3ec$Yh?28&zqpC*L7&XQi z)d-`;sH&>zq^hc^s;W_A$QU(ht7@v&lvo-`5D^g(5fPC@y!Zb-=S_&M4b{%f@Bg09 zea`njXT9e<&pGEV=Y1~3P!hYf$$Bn!tL(`&f_PCfv~ervDK~BvJ>46(oiIo~lsvxe zM2teW?X-`ra@%PG|M<2uK6Q@Usyo{%x1aD=`%}_?`w6YO@AlLFV*l;sT^!xEJD!YA z`r?COkClJ1#r#&^UIBK(y=d(g>@|c z^750H9^bimdXV?dLcPX$XLIzK(4AEJzq-`Z@pA6rFelI4BNf>(d5x>DQ$oyLLM2 z&AZBbtImG&ox!2}o8$c)YriQ9R;#}`6)dVK0kVLc3Fg$hzYTFewL3qmPP_Z0wXxIg z^2eLCyQ?0Ps&?1<)dudCG-mUjqQ08CJvW@L_t~SV?R;^ML*zB(-Vm`)nH2wO8{Z&r_)udlLy}!=4%ICmW2D#6H6TO`K z960IccJ@F?(50>iN{2P74xDk|IvhCP_1fbHE_S`*PDv+9d?;bOJ_jy#2dYj$eg3}0~>mhdHbohE0LT`kecRqY0 z?7ZV)Ipn<4VMou4E{C06FS{Ieb43aeAU6;X*Yhq%vPRcHF?)l29>_~acK53DKe9j8 z<?d&#L?5|u5>+G)?4Xu^z1nVoIMY6{@jJG zN9)}Ydfj?KLy5ytXkN)t*?P|5*vDf!I3N3GEN?wlsI{Fvc41(phaYCSO_+M+5{1@4=3+>h_lT?{(D`}s!4<9Q+HIv+1G$u7rF`D@CL-_U@l z#fx5rn^Vd?3b&_}dKBh)^^yw@$K$jxpxnE#DC%6N!lD7^+zL;bE_f84G2s*hD3}rp z0?r1PcPcz*I^$7Dhu6Y}k;K1MPL4=xq*_#qMgrQzgD#C`D^u*Tr2wK*C2bJ zzjCeUz<8ho6F|NVIr~`AVbdXH(b2%Oor;dq6vw+>?NoG}CIi9g3zUp)Xec_-^>U}8 z6EuTU@ns!~PCIpYqUf~gtb5Vfz_VQ`=~i?$2)UdMKHHHJ1caXNRCJD_b0J7^F63;7 zqKYo(yA+)dKJ8v~J`|BJ1fTUNs_JyMdr?(q>rAEK7F=RaY@iAwitD8k zpWeZg}pDKnn<>ghA?lyo|A zAQ|Xv*y#=@E_6E6`NSoUGt!Amohb=C)A2+DZU!lFIMLVUctN74g&;L7dK= z@uH;b$vtBmlqV02t&=DL8V7P{Y>o0{vDKzIc`~k%KUrdJQd6QiSrSS~$_@9EW!8Gl z$x3HQJQ=XM)a_*2!BV$VbP_&wDx_X<>TLfLULcK~PC1=D-QiT9^Z!mP*)^=ht)!&H zC%mM@=HpRP)~!TKiLRugTd}U>a<>wX5|3gm+B?_t)g`{Wf#T+uxSmG7=F_Dfr-Dy6 zr*}mST%_*l;XU;U}x}Wj#zTkSs`;4qQ`{8f+(`Q#2D%59J z23Lq@iw)e_v&C`8#j_|0gsrf90FuPUvo zqN`~TZ@N+|D8M=zH$m=LAXa(jV4doXVZCagdui3hqrO+0FBV&m)m|(ec)HKU3+lsl zlw7@dN&W5B>O4O;q54Ql<>%Gk4XUoIE{?3+M9JsXB|$}X)g_VTpI4vlp%N~A@k&&O zOFLrrK6a^6bM(rk3+h9)lw7$~tpNf7as;`gK5*qytp=oCsZ?L;c$0uX zrAO^1uM*GNtzN~RwR?xTovJ<6wM0?NU90X!N%t#sF?OY@wBMyGmx@J?E2ze$D>YtX zW!=|CxvB1&N~pbh^^$|b)vH%QG*u2hS2fiR-u1bfIN$ob`cM$}IDLH;8nPY>KHQg8 zx!2cMC&?%4FNu3=aF2p}6*^U7DeR7$^=u7YAD5QmmPu23?Tf*(`r3E83oh4AKL&EH z^95&0T&`6nBj7^kb1v7e6mxFZl;sYphOJ>XRl}hOo3`PUkIJLr$`z$k!}Ti7mB!1W zqoVQpb&wlP^^Y}jO|{;X05yY%P1TPz(bZv7&3Vb8>8f|L)I=|TXQ;Sz!y zm9A1J!FDK6aCLGSAD6jhvI*=)^V3H@YjY%d(B*aa)P!1F{QuSynd5 zOgH~X+44nL$88=O1vDwVB)_bw>v&Ff1Kxhc+eKf?xNuBky9 zrR#O5W|^yix;F~}SDLSjhf=m27V{?_Xb`VNUsi}h({-hE`nmlVrKWn7O=@aTHA{lB zxlvi2w68{48@sPgDa-0c73aO_GH(CtuBgQ>*RE<-mg;>q>z=Ceq@;^xTXLNj?w_l2 z(c!*OG7Y_Bb%%(EP0m#srLyrN6AF5NR;qDQ)?KG}X%HYhbJ4gv)n3uycB)>ZUzxXC zqu1{#z`U-nawvKRVtCfYz1qQ{!_}*fdkoxV$Iz;8R8EI_Ur{?9^1Y&TrlXXz)by|G zF6Vx||A5QoXF+_OuUEJpiKwY@Eg5MiS6%7C%hlbf>Rg9SVGR`^ii!@tP4x%eWbS&U zo2N_FnT}stZd~f9xpei0JMQuJx_>)>*SZ%#a3Q+61rX-V+3t{QJT1W!2zPG`EbbUn zFxG^w%yZY*Z_eqYtU8_VLGKYg=uM)BTX6@sE(em_I(n&`E}iIV=~PtIt=vENsCRBb zDefMhaKGfODb36A_I58V@b>mDE%oW-edU&c|Z{O|GxIzBI>|hW6`q zqS?8Beb2pL=nL?aM~?=~)%#ZE73iUq=#2WjD)Y|3@ajbHfs8kPe7j_uVtO#`jEMdE?l@% z)tStD=Wkw~T*L>nYzngP!wti}msM9r{*-s3T1^&#UL>{Oe%nV)-Z?y5Q9(`|edC4~ zIeILs`8rvDY)PdjS$S+Z=&WNaDtnUy$3Eqb$P)@meY=sFg))%W^E|J4dCGpWvQ;<4h+x zjQiyq$2-a6$-X!FU&s?U=QmySvXTpWJM%F)gZ%v+wL*5|qTapMUGB}ry?f179!m1x zyV_aqNQ#$Kgv%kMIJ?$S9!8F2*SfuW0jurRI=~8^Ihbp$kWIDQsig2&V}bX**y<=6sa#P z_m+c5Zc*^#awf?uF7uHcxy5++DUTq_N-ny|zbDhmT|3HuAg@$9x}m&Nmn&uDd6ar% z$gr!N1JZTm&+FlJaw_U6Cy)`{8)M|ja=mwG6^TJ_wt1psTWNcT8~FQ3=4brIY&$7G zK@O^?!0*F#^mxivZmS{nQ-|0pZ2P!4TcoX8?oKMyL*Vz}B1D7jnyrDH%m~CI={i#R zLNDrbZqIg;^|tTGmW&A5Vk_XDo{$0U!;14cSKr^!Z= z`NE5Gy6j8xGah69v5cCU1Yq*S2 zD)}i~$Aag6AwMnmAjM-lQ=bz?y2!tjy~z0)H{@Y*FLLq)2qFB^7hI{&NzXzy;cK2% z!4Jl{zJ7$r8KJ~Ob1^hv&333J$?5jy0%)cxS*{~VT!t9?rz zJE~3|%RMfS8eJ=o<9f=^{rVF8KJqj0r*Ix}>TfRMX*hkEa`OqJZJ_MObtD_Q*rv#n z$luB92HVRRS>7kb(YCqJ*j41aeztkGzjDLLtCMW++uq}1$?RmgldLDzWNEbA)mBBC zNbY30r)@p=G}%8=_J?4WlCM(b0NYybdGh&F47@@9367jfR**l9Vc-)Af+6kIBqy2z zyh>kX#rPFYz9es%a`iyDgij{q#yfan z#5l7C5)Go8$ue)G5ZgAB>}Ffd{f--!TL1dYn%Kd^kqRn?B+OzUIC>B5ZhS7&ukyt|p zk|k*Vp=2c9IZh-QWF~ow2(fV~gUKX(|1zFT!N>70k}3E|Z34+9;`0;#Fp2e@SJbU8B!6bBn*d#otNrc9KlSA>q9m zNA}}T11U%zIYv%`&(kw*C|=a{z?uCDC{+V15UK#C&$QVqJiN10k?lkg6E)a}Y)?&3 z0UeZ{3W_*VOYD|3cz;6Q8*&LfklGck!o|A}&Ia`#SxLCKg4ANrlj-2ZRmK&7*2dvI zb6ozDcv~J<@C<4jKl%8d~%4k6V68= zr+9akLK(NIt-G0%eU-!|4kWS2*Ehi}Aw0n=kt7_6FO9EE@Ji^BkV##9ZNkFD6*$+% zHzf>8;1f0^jA70VDcr!nY^1iFHX0iR%-K;Wi~UB`rzX1fRNN zEv}mR)yb2R5)zA(V-u@ybEQe65-Z@~!}Wj@61OK-B(FE73oQ>JUjpZSONRl9%k$L)^#gAwJNjG7;e)XoTUUgoBIMU~^G zwOb}-DZ}qxdEDYnlbFIfEI?Fk}*==KXvA<@M-&R;oFhCIAe^Qo-zJ*?(Jvto{UMibL?lB z6Ep6`Z{<;Wkk4eyXg$B>XO`Z=6lKhAJ!fg!&WSDS1bK5teQO-%duPm(1Cjp-La=Pd zFXJtF#MH5^ad4+YpQ+$CbxteqPM?+4ntyi2BFx{}ZR6mPz|EiBYpT!9699Q~#&Y|4 zW=bm`H!-+H$XF%UXRK}IQC}XE@xx=5Zw78K{xAMAcKlpdg@;n?CqJSBA82iazQ6O= zTA%jv?~K>>T%MV+>(2EdJ!gk0D%({I@%g?w{N?d>h7>RIT#bmT6uI_X#0~(Gb-Dj-wXdxUGA0U zzkzq>TDo=4pzYjt+fipjKRzbpQZMICHRJv*ylsEV=cnR{24rT-gO_n<8swGKnv0w}bzm!|zPxzq zkhbUFhvkt|v4h~z{7|P+wl-sW+ci^rOK3ZHv^ULvKHh`fH@1CN^z+sId+KPtwa)yx z>T{<&|6Jw%uazU0PTO^>&iB99qH*NSQzzVt`>4^h+tz-MI`>DN-%r~XkG8h-ezNWF z|0ms$^QY4HM3}SMw!kfnIW+Uu*$<=t3vJhS&G5rl{C_J(>$4@hJluM|@}F22$W>Eu z7krPO_tdO=aOo@DpZ$0H{P43Ef8PS-AG{;JZ_+jaQ|~h>*rQ-e`N)c4zUjN7hT+pg=|*5zJxpzj^q z(-!TCbFchc%Wqq!d&T=HczEVm+unR1@82)vKf{+#{`hij&6yr;bCG9c zc58e7sPVntX~_kdzK^<`hsh812p}ZW*mgd9n0#8(+&OQ`ms&$0Z&jJrTW9T?2V3LI zlQSb*@x%F$WiDyWNnV+`tQEJ{N1B!2A2)N&_lG}x=)GiCF3nv3@Hsx3l=s_%@n~{?uw1YX z+WsIZ{Jlme%!qdDNcAwU(mG@K41$cky*IEW4)3oiGmu|&xKWck_ac8Rg>*lne zyW-yQt;U_Huxcv-ZzBF~0+g$#4uzYyhn4hDza_+S84_gk#lu37{y>nb_8xGydXnX#BSl&Es2J)Z% zeR2MO3zPGvWj~5*mg}d@dldO#-SO6=$>)c1nYQT87(ccxTG!PyU*xs?&isGg^Om*g z&s*ptDXs0A>XBslKb%S1HP!#{WjwN~%NN=?*FUlnA0Q(xVl4f?8_m0*#Ez-m6L1zY z&PY&R^OWXk{6h_i7>)zBV2BbHW-duJ%0{(|<^#=VuxfGvJ9l6kBLXX~h@m(};lPX< zF$Kp=9P^M~fYlSMx2eSwBEsg$Ux||0OK~Jl5yK)zM@)#wh?o^IKVnhDvWV3Y8zQzx z?2gE%rHLoRlj135lj?vfPX!w_4`e^3)6E)?2t3P-Sc(Jpz!6(<;Mo-OcfrRq1$JQd z9Z`k@HH~N>BofyhiF?dQ+%HFBl^*HC&S3+9@a+GF9Pl?!D)JHenk*#Wkn`koPQ$%U zieZ`QV{SP230KWc=4!YmZo8sUah=;EsD;7Yelb>zbZX`#0h?`DnyM_*H}$@ef53 z`oOrI@Zx$oJ7NUm77|`cl(UU%rF8jLX-jBchB zypQ3}mR2Ljc@my481YX~VvKwy!c9z&Ya=|xw~^Lcx(wQjh=K!Rj8&Q#wJ?f1PvcnF z$pIyz^tv1tUL&oRH-*=uv>am^N~3f$rL>RbE#bxDNk&gmioY5KRUKe7kI_Sv5+^a7 zknoLS5~Eq5TakKC_->@8^c1C%2K=28Y6aSvQsf6d@}o45(L=DFlaJU&mwxx-0&bVzl}h;NA=?DFq*L1-gat?Q}6@#7Vv?x#C?uFXr3M zi-&FVfqKhZAbAuaKFWCMX6Sjy5qPw}(B2`CmMnR*^fvNXj65XNWQ({L2l@>;yrjGV zUq+dd;$8V;qx9<8Z9Uu@`0L$stsu zkb4Mn4@V|wBj9uAG z{B!P0$Y-XCAeIA4Ta1{WBIbO={1h?gBj$XRxDF+LirDiJ`%}c8Ph3%_nTQ=H?ni4u z){!DkEXUQW&(hwh3ULfkP#i7FyhU*WPdx}x; zqfvGY`~f)PaU|eK#F2#XWcVovPlNvn(yfX=B98{$WZQ0Slas~EbT2P zV=$`!jZA=zy}{)7{22aKGM!(_e?+pGy}f0yx0gdcQY65x-fCt`@9)f(-ey?Q%OcwZ zy`U$Dgu$?WmoHotE|SBdm*_=~h}}hRa+KMn`;N9EDPR`q3YkT^BB`&`mlR9=q<-Xt z6d(nVlafWUl2g(!X&5;z4VQ+KQt3JAIdVoCC%s6@m^HX^Sb*cn1?BIRzb947KPdk| zE-I%er;uvppOmx6CFNS>T5?&Lqs$?-$_>g5J*WB==cJmh`ZMRM`dIY|=c!t!TE}%& z{Y&*PuA6F$Y75t0wN15+^H!s)xX09vFu&siYjAzJp49dm*GoNH{VMl_`fc?)Tp#s^ z>JK?T^=ImhTtD?@^=7WWdYgJX7ogsu-oa_9MK?~T&Q~AdjOu!IJr}G&=W}Mx5;EaV zXWj)4c^GFt7(y&Q=0bCsx!T-d;Vq694~tK0PS(=foyG6gxh33^U>Rf?ZW&{lXvwt9 zwk)t1E#cPEJGtO24$<7(S(b+{0O`tW}mZls=3L8ECDyJIlsabj!Ew2ywr^ zv+T7T3Goi`ww$E&4(A(kw+qEp4GW3GRcBZ#Sa0bqwIQ=C%~r|kYV``4Z|!9bu$ryW z))eayxRKUz*2&fx*16U!YqoWzHOIQynrqFo79a(kb$UGYmR!HO+f0<7CH=V9tR$cGa9_nA&bCS1hfoe#yA`yEn|iiE5NVu zC!ODr@5l?}I7}i=;i52d{EeHz%^)YZRopk^Bv$}SoikC#!KV* z2x+1;k&mR~4IiZxl@cFEEl%?|Q@+Nh!v^IVejpu(_$QU0DL>-}DYq!M@V`)QQ*Pr2E5B5J$v>t1TKP3U1ePc@ z{L^$a;-6J@QFY^grRt^f<6lsjRAKyhRjewGe@T_3`URgsM<#xjYLsdWKS%X@)yw=F zs@bafe3t5M)jRzAs>Q0M{8H6QRSy4&>NC}6e2(gK)fWDrsxMSu@EcS+RXh35R9~xh z@f%fpRQvc%stc+M{1#ZT?8R@TmMi&fbll>u3paP z(s7IbnvPriF7;RHTzp2(Bom_>z*eT>Xl|^nCHdh^f2^?{_m!Ta)W@^A5Jd=~#PKZwqK z{AkR5>-iTTyWj8=sqFHzsqFG|sl{1-9m*Of3Y#YHX+wO!Z2z zLa!o)P-@{-7y=8gzZafUDwLA&JE|MPAD|l(g~`fE%9n*H%Bjjf3e%w#bA>-a6aFE* zpX|rj>YGw9E&5V9E-oAax6X%Ij$1NP+P6yShbhhOMDqtTDyrE)IO^?mCnfG zAE|tcGpKxvGpQX`@lVv=syGX>{(JGy>Q~jTigVPjsb3RcQ@^8rSDZ`bUVKCSzWRM} zzWM|82jZLRW$I<(0%*bt@h|F)>WyL+)d%ryst@8qst@8K=)-PtF}2JpE}@oL#ig*! zdP4kAeM)^wTn0TkD}F@vMEpd3QGHQdsjgMmihomIRbLfXsjsOU#ZT2Y)HlR68eYST z|ImmUNnEQ@X;fkk)e!NYR71q|(2lop;Hd@Eif5SRfpgTo#l5 zgVcU9R|B~p$%pf!;GTk1zrhb-vOa^!`Ya~v3#naY{v9UepD`)_oJsi>CgppXl%J+j zuIR_4+`^Vq(v>BAIiU0GN_tt$(ErPh^&cZE7=$YS~r)xyV2PgaPMs1ZJ4 z+OnEy%ipP`Wnm30Eq4;uik-#I!a8V;`a=3b>`3h=i|*8Zve=2*PZm2<`^lmwwVW(=p*E96 zFRG>DW7KA{=tFHLiy_ozvKU5fCX3^v<}vi#R=3} zvUreMOO_1OPO@aAS|Ige<*Fbxc}Dbxit*>X@_~I@TzCOtni|MYT)%6xyYd zR#U6U(pqW>Sz1ReAxlitq<>LOlQvN6$I@ri;<2=mFEkwau}-o5 z)@Yk_g3ejzsp~;w>-=d5T(2K?x=3A;Zm@2IZme#SZn|y`O`&^Bw}hqiGCne=E77gc zt<`PP?bPkp9i#c`N^a$@t2BBUPW~9Dt1}L{x6^BlLv%L1O7EucMq}uEQ;wnH$2q;# zp5AEwan4X{sMW_(`Y_Ho*;x8uPM>BBfSXI5eyH7lc>29Lm<`Q#rytdd9?Ump8ME}` zDSb#M8R7I(D7~N4&qN57@{#Q-V}^d7ah!gkeyM(ye!YIHewY5BzEEGLuhushc!Q(C z!&qSOG58sba7Q51;f4gmAj5FO7{f$EreU^Wfnl*>xnYfAqhW_(Z|m4X{o6Gkx_g`E z-K`ldvfiTMx5;vgB(;$X*4wSUe0LwTv`t${F1<%I|F ztX?b8n&WUr0iUh16-?%^UCGc9`#S!DY8Qt@o|-+h;0m24)k?UcqH;=P$|)F0;l_r=4&2?@i6M zh*5*1&3vTI*A}ysm}97KUi`!E)?7sAAzdU*LFX8ja<93KvyQ&q(OC$bjN6?h`rghw zyRD;f%nKf!!(7GYLpm4gR?vKPYj5SQ+XQ^%ZbxS~^T~TV-7!nnyp<#) z8`?VSuv^ZOeQ&4lX35r7>gwp+NMq3X(UNWE@8KNHKg#KQ+tXW8evC7FP^Z`0eZLX+ za+VamRc|%>n0@rIls=5JlXLPKhw`NFE=l@E;CoOb^3Yc3hT1_J9BO8tYoge-673t>uStZTd8kd zO>5fftlq8YmapGs^`%aK(C$Ax{k|N`;rc>zxZPp?EQ4g=#ezk`Oj>P6cTiW$eI1>9 zEnXHcgCnJPxL%gKoH@bn3?8lM!F<}g2A?*4+#&<*<(LN<{LF*khMR{QjFh%IOTFEh z$53YoxBK^|h6L~@-sKixZ7~S$&K1W!)*zOZ<^_fwmR!qbx)!nIShB3{58=G659Zp_ zTA|hla`yF+WiH%|o37qcg>_M_rNDgBFUZgwyvrz=H(Iicu5`A?`l=V!!DhcmtXt@+ zWhB-nbj@O4t+cLFa_wsrW0kSqL`)i!yUE++>z8EGn?g--rh%qereUVhrU|ADp!udn zre&tprVXa;rroA|Q?aSsRAXui63lypcbOLiIR|+L^$7A03JQu0N($N;G&pEP(Ac0! zLDPfg1iclsBxps@+MrEAI|GA)_6HpcDha9#std9OtAgEvy9M_SUK*?owg$%rrv(oU z9u+)3cuMfh;CaCdgO>(|2CoWUAG{Uoi5=#>*3p;;k6;GEXn}E{8Cv8Djp)_7B4R#U z?N}$=T9cSJ+E*2=D+8L3eI~xgylS8KT4p&qquo7!wa!O$7P8MiY*yKEbM?@6KEbH& zH{#|Pv*cjqvB0v~l51XUDYg1qs&2)@*uCcad%_Z62|zoKwwTejStxyo zr4)0^ILl;6?p)~LFmN#+qnwq{Nt6Okp5F*d0pg)FTAroeO8f?61{`eFKx4hZrCJq= zdez$t+<jSqkQ@jvQz{)u8Ty4R?jpG-I@b zw8OPy;sI#`Hhz}2StsdS<7c%x-AamdUbq&Acb$X57UGELwSFhKH#&3w2momQPb2vm8RsQ{ zCTue7H0(DVGn5#|8%N=+(okox8C5jI=w|F@>}}Kq&c}N-O;!!-I2QZ-IN*+8Vlk38Ow~->3*#)x!%}d;*;x5jwTP2 zPjY>$GYvBNnG#G!Q#j}#%8hRday4y?Z;Ed+tugJ0FE>pz4F?{BW1=b3G@FGip!AkA zEjBG@XXU0HrZtF(^hnzjB$@V_j+joGDonMeW*Q?%qWLrA8swEK1oa9E2r>sn1Es__ zrI4T@I7SAI3z{4>BZVY*;%shER*EJlJ7^_n4vx(!8tSMYl$+|G>K~Mcqktm#r9oA8 zln|K^85|cJ8dM)lf(Hg`g54!ExXZ#4#+bA$W9JL&AvQ3Beh` zvx4UbFG?7Z&Id0GUY*WUml_njA$WUmxw%L1?%@2?Ae3io3PV7DPd5>!hGS^TEO16y#)D2_ zcqY(1%R-9n&ayPYMxAAqWj*!NhufW{(6ZIC%W}|CNY4|yQI|5y?pw<2{!JQd9d4<% z4ze^P_O|lYF;>UKSgVKC$Lg0D+v==iD6$%@;noDk>y5KP*5TGMX>Qg9)``~H)=V6W zaV!8`p0a^D>l*7u>kjyPtw*dUtrgaa#3^={;-2DetxcR_ZN`ge*Tg9yUUq&+cKXJU zULjea0Ssq{m_wpNQbLA=j07(`WL(JPkQo%=n2Yl)94kX|LN=!lN*@$bfFn0l!*Cuw zYwPU%^g&6>?c9*kkgAaSP?A3A&XDwp=@UcU;k-kA(9GaiFDs)lkEa3S#7KJVgU5)UAp&KZ@WzqBdgW#QFnQ<8>9BqR+F zZ;0R{93#RbJaF!lG#u0qhY=ivMhuD=9x)~|8vKdWMNEvyjF=sFh{bSg zA~r_sh}au(B;sU5MMQ1H^1C?Aky4~=E-_@?w7kcGULBm8t$|tJ>otb0RmliMKutE;2VVm-=no*0cw6yVBqy z^C-RDMHWPs+BuO`c1K(O_MWo$Nm^kPY3-l1vIldiYpIKJk8)2fiSka}6y*!2j|!zO zr5G;1)umRZO-WNx7ZnFLFlt!T=%@)un~_?VvM6d+iY97)d}!37C1xhv zyqJYCOJi2WtdH3mvn%FcOkqq}Om$2{EFbF_>jCEz>lbT`4UbKT9TYn}c1-NV*v#1F zv1?*y$1aFn44>jPu^VG|#O{qfl3az=T~qAI*oxTN*yaJ!0M`Lt19}a}84xhQJRo{N z%77sQMh+M^VDf+&1Lh9M8jw9;<$#>Pkpnj4$iJw{PU({2NBMvP-EPiylPyB?W9k6>lD}H|bqWER;tK&B$ z?Mz^%d*Z6(kr;>9+eh5Qt%m`~CV3?JU=%VYg(oE>4Z^rG24hKP((I%KDYKFmr_4&ES-uL%cN*vt4lwm2OQzoQjr21pln4jjBvM6O)%4*Ee z+f#N!--=T!Q)*Jmanz(Vr3zG=Qk}6<@=Wc)FxEk7)v1xGNf_5gq>fFUlsY|iPU>5T zu_Sc`)96j9J5%?k9!o7rAD&v7T9;~r-nylAOY5DcO|z!OqGi+4hNg{58xNhHneLG` zGi_el!t~*33)7Zjj9ZT}jE+VJS*w&`b>4uLnj^hS!8+KO9-f|nRvex_2J>=e`t0-t z>5J2sr>{xhn7$)@Z~75@pUlm~Cm_TJR7D&ac4o8(s4H;;bt4K;foMS0ga>sZ4xq3P z4XPpiKwXFgs$?;p2x0tgpw7gb(L_cQ7!3f`5K5g0Ez6aoIlB;7P-j*jXQF2O98gCV zN6qS{A_6!9QGpWjIH;1vmxu_gB1%RnRx*w&F#~g~Zm#_Mz^>dG7RR3dS{9RD1@?x3 zI`Xd=YKXw_08pd`RkOBoCOTl) zlm~^)ZBPxnUI*5O&V+6R(hz@8SH3@sL)%2d@^ouyXIic+{~joBzasKEunXxx`5bLM zO-pU*)oEA_B_aTKAbz0Otputjof)N9=gMyeb!J!R!rI)0wV^Bj2KXxCMdNU^ubo-@ zxbn2Go!J$+5L!b>0plctIuHRAdznC8`7A~kfoj;b!v}^Pa!^7#GTH@HV71~{{yb|j zSH3U!4h-|G-(9&P#-U|7vTG!)ZiLX7xF%3%LS;!{_0cfd&@ibc#1niKYYjrWvzV!% zE-YuH0M-yAC?R%1zx`Mj$d7#d$<|3=JhDnsbS_%7S5GpYVbmjj7EV7u6Es{X{ z(X~aEb^vQgZ_44ffI5>vhH1Y@EFTvp8!qfBIx$R`?uf(*aa62sE<^wpS^pi%48(WzO3^49iH3bg?;wVV z5yVl95~E26?DR<^9mR3tIMPiVFODbO#R=jB;w}DO{5^RL`xf5B-imj{cZt8aMEsES z7e5j|A_nneaV0T|tHf2rEUp$;6N|V`%pq3nX!sWi#jb{JBwYML{DQ>NeGOy)b~YR$ z@z~RFjHF{D1(qE-4vPybKT0~Y$i>3F- z8tFsnBeGWdL|RGyDdk8x}0Z#o!HG3L%za3 zrV(Tx-OEJws}w4M9L7G0L*%IHu<9r&QoE`w+Cw;n;H)fj)~wpGA?*Vl;a0G4xy<2^QnUcyQ<*wk~24c7qw{7i8e1h$)DZDyE`8 z($L2`*2ikr$7?zB^wXDZi zi7H6R7L>JB+)Dhg|79C6l@$eKWiN5YPMCezMTFfjkkmXe51ensZ^1bTNzyP$a$u6A zWRfI8lFEoEmP4K#Ax{;ghj?B*PkM@#VkPheNLWuu*kxQ}omhubu#X1#x@g1xFgWbp z!d@C+?4%(cbT19*fZa5X;5$i9#2342oQV@85_*o^H60L#%4G-0WjE~U><;NPLOP>~ zB*jRvqz}H%8$hC^I4K^S1Syd`E+t9Hz$sD+3CB*lRAQCVq;zlwLZdI z8BzxEmZnOXz|*8@z|*Daz<-qfNW!EU(hOpeUV-%Ukp8)#uVW`6=go~TA^ZEp@LR~0%PY8 zal&rD7$PtY5wMS_i71uVmCZ!Nj-ngH8M}&X#6c-5W#Ww8MHt(e{%D{-`-vmnYebZ) zZ&e40s5%6VQbMB+6HnDq=#>b)>OeGVH?Q2BO&^i~Ubt2O` z1=Bhe(>hnCb(mks5~g(ue0}`_abdb=WV+|Tbgw_tJr|~Xik5NFj2(#zrf=923VrLu z^v$P@zWFkJ^Te*m0SLXNbpiB?O>n5z`7o{XWm@OiM(ZA9<0!{8jTh&LuR-RhzKP=N z;_JXv>v(a#I3Jkmo+xICS0Sqsj=>$6zI9;wrfj2K3Z`BC z#D8HV=AdI+ktfwIe>NU-Ovl7FI_Ai9%&CozIkwR;N2X&c=vW!{GoBUCB2TJgPE5zT zF&*p9bgUcGvF=RAc(GQzf|yj(MDeP46`1N9FE(I|w`(0Q%9sxAx+hA!!~;_e*;bTk>6;e;^(lan^R5NYt$+R(wX`>(0 z#;!~o)pu#5nrWj+8YB%Oou$FjU{I=)0@Fq>ria2^dMGeG6qp|BnI3vb^QAYT^M8@v zB7K=Qc99lJ3o&Z6)ya3IcS#`AMmMI7J(xCP|0{ODDwIx27wDm@vIF6nHaakEbYR-( zz_d}xv{9jaTKO~yVmhf{I;mnhDYel_$2L0Y*hVLvm`1S`IpAyqgk?E%c(@z(sp9-S5u|ZmBsvuaba2i5P zWejZrDxv7c9;KJ5n4ulYQd0p7?T*kR6p=b0>)I99j+~np_zy`5a9~00w~gyf>o?x zCRVZBDN6Yhj2;HXzz*7(t=iRW9js*QR3%%HAKTPXvVZR~TWOXn-_fT||uIS!hQfTs`Vn1T>l=atZN98rEY?vJbrBlqW%lj6+$x z9K|f-V3QBx?FAZW@<7^mfKp6Y#c(>H1QQRGW6L-C8yjdmAYWrOjR(}rSVrRk`50+k zUXvT~)~*M5|3=z;qc=(mkZbX^LT?~PAP-|Vpltc7(bEX|!=Cf2Mt39RPo!7rN^7O~ zJ6gljsBPi7Q=a(O)=+V-eAQ5G90}fQgcvGWisghelpAarYboLyf$TZ&MV*%!)*zlUP_|(?;=Mq&kzzl+REkP#%l-Hqc&* z6l+kcDvAV1r+zC%1T%zTf8bv5jGUlfYbZer%|PgC7H>4~YbeuW#u3uNTh4gt+;aU= z17;F_33xcrTW5t4`bB{l&7>i`uRhDL38nSr-_tLEyekw71hamgo=WGJidp*I+6thn zLMMDdfLVj{0dKYzv!9}~@R@$5eh!cX-Yo47#B%_eq1{N++Vb`Dwa^9rs{8`JN=Tt; z!AsC$WL02}(8ubNXj<^%v~*;30E*UPWL0LNrs1@v%0F-hd@>SA^HXH%L-oj$>PGqTX0NWtVl`6LjN z1FU9=&c;H!@)f!!hP;97acGbpp)Yi7loAqw*eE5gKpD^*r0oaZK^9LBw4Wj&0f>!KWGN6Er3BPow}Ykd z0Af;te>)&LXe5(5LAOD-8TsKZa>3Z33#Ul&F_4v^`9Mr2c$B0=DJSSy&kD57fx%!m|WtzdFv2x|qCGea0nOd5*lTEQeSv>1rV36Ci70ga|e@lT+U4DA7eG=TRR&`^f90S#uz6=>j1ltK}9;S+5FMZ7;y zEDJ^Z8BuTKG684^MZ#(zTouqxpfrZ)b+G;rHUh;lo*F3nCPI55=M3hDHb@0*!q!YLhQ) z!GYSqAf$K>X;~jB#@glw`m$V}LW*7ty#wUK&_tka6tVo!hNx*Kc6yZ5E}Ekp{}zK%d1K#qNRAk6mxD&Gd6e;^lMU#$Q6L5f-Zn|$f|e+hVAzF7a$ za{E{L9t01$^sn&U1(apW??1^G^9z>;G{Kk70_%Xr`K|(5Ys=T}^2I8cG=VqVcOiH= zH!k!a+J6MC6?nsZXM*<;&=B7#;NcFW{~+J-v^1oB%Xbt)w}BVyI~3?M@Mih4wz^T@ zKe~S?y-Fbe{`!{u0(`ZIw-71(e0u|V0QL6m285MK{~o?>K>t84(Y`8rmEgJev4OV) zJYyd!*(kTa)Ta_WnhW2j1QG`M_Gc2IF!Hy^VhrL(p^oidJ4A zlNUP6`xi3a`#{GSg3kCiGxRTLDANx*UimW(z!Sihk&w=!T_@bkk7y@t>r ziiAk;{233W`};D4I{UK`Nt^@Z!+4XBLcqj*iV{E_i{U#yj5v`#-);R&JO%-wAjihN6LxF~~{GI^98B)+u&y$5Jz(ekc=L(*N zA`Vv_ATc!AX6&b@h~PT<1+aJ`kl#(zn<9P(A^i&(g5>mLIrHd&ei$neIsgc{Bj;cs zN5*>=NJSC7qwXg#USA*p#G6WpUo%5!13#3FTp(9|bqu`-R6~&h>H3dlJhYP^>q~wn z5bH~RqAlL9(60pfP5ytxy$g6$)!DbdPczvQLNYl`LdXdS0YV6fh=_oQ5CTF}L`xAV zA|R+mOBEEAA|fKDh}1(wi)aujDo|9kphyv^BBdG?53i+Gt)&zZ5h+?q{=es*VC~`U z+rIzryS{6#-1+5MYp=tzp0)OxojrSGdyjl&I-(D1v{WxswxW0Gf$eVP+kkpnYCWl? zQtPlOlDcG}wo9d(iu^^XOR&8orQ=ne&MuXQv5k_Akq$nQnv1QgQj_ibz7$^b2TScD zRp;M|@utklaeM|sVBr9MlU4@yn> zHfM-L$6A%_(TTFnBA?Eel`~D*$aMOOBW6p{BX@>>N#*47Ql)+=+a9HAM;_5rL-ikr zM%A(!-a@U6}x zyUa)J>O8cIy_?$-?&{pXQo9T97b#OKy-HhUw}h)IgG#+`QYnKww>`05>OiEi)A`nF z*`{|+@1ZpbTMaf#9cU`(Ak`mrniMgiC($%^A|~{tnMR%Sl+RbcI(7S6C0C~^)wWbu z5&TG%dbH54P3c)1jhz=*KJTWK8J*{Kt}`Xue7$e>{rjZmc31oSj1=96uJ_HRW^|rj zHKN(J;KT_@)p@e6A^G0R+D#3kUMpoZC10jvC!c8Nrpt8fFguTXKTr#5>?)Ad;EDm= zbtaUW(YbdQ^SDwgs&suKsmgS<#?*Zk1(o-sCU(xReBV^~Rzh&x}N>{v=E^-_hVXw_r7wqa^| zr-izvg00is?iLXaKeETM$2aEKFGb*Nb zQAA*C>|T&m72~?kK@C1((+P??Qa7AX+^p7ipN^VQ(XUb-C-wdlb|+Oux9-)b!TLN! zf3@mJ%7TvDx_!{Bo;|_h>WJExUbzT0z2i!*KNLqAI}lC&V^Yaz%9O%u!3Zh57L1lk zdhKqhq}NtU;Whm~uCYU@^*5rPcff1G7gG6VL*NN~-RK-O5lCJ6%(;4>hCHRh=7?sV5xkCg0l;`fThl)O{!Ht&oSj@2cbx)ONL0 ztttPfQbSQMOI4$uk{W<|PO2X&F4g;M#X3bDi@JS99fMDp*Hu1sf1!NEmd~q{Dlnyd z(@&V(?Xc8V^&`!$0--{?3S_FoxUM5m6FYuYp)r?jM#sa7*<|W~Zi*}N^|Gr#QulSJ z>a<*~pA>0q|1PbXDz(d0uu7^CwMA+tO5@R9YrRs}OKn5Nq_on?W{P#$>L`DsTSw{` zT)w?)T>ZF4sVgj>-(Kn=Q_45J{4pB^f0m+YM3v^MKTESBxi)Q9DR*`qt7D})_U^h( zb@)pC+G`!VcU82zXUSGYd0KNiR#3;iI-kM+=oog|-fRoBQ#0G(s?k+7sOeQ_ch!!=j;N}(_1O<1jaA7QkEbkX z|3=p;*-UM(T80|jesfh?vu%CVG1PtSAJf`k`DS!jVedQqc=%TP`VJ4FcC}yFVJWE# zIxMN$ftuLi?v4YSZHud(kvH-qjb&r4zd@-KYhq-K6zhFN>s1+X8W|@=oJJ-~CFArh zsbrkSQ>M2s)q6PouS7Oxs8^?G>QOaPzOPgJy4m{WVul78Qpxk_Z91Zq zcSBNLF72{HN0=JNx$L%-`?{R3*>AQP<;Qs6aa*LZ{3~m{_hYHUs6`r&gI(T}O{z;p z<(y_!+U0!IjPiHweTTPLM-Zpp-O9ICI~QMSM(btvzQfaMQBEY1Zv!R&Ib}gvVP$uv z{yAlOSyq?TsKI4n5Cp+H~G8A6I_2M(Z<`x2p4oq$(ewjV-qFTJ4EUO>bA%r7dbkdH)XP z@eT4&pAJJ&)7$Oq5=cF*+NvtICROFO&X=NQl$UnUv7{DOn#Z43z4_(EsOgnUD_>G< z+$(RytyOxOZDV;FO6RTe5Y20mly5+u)q9C-{qiQ5 zQk#!d_LRC@ZSIy=iLGb*%1X^{%UAJkUKzHFD_*TEMUAi6R%zp5sn6!wC|sg?*X5O? z{IUai&8cPk^5j#ccBr_svu3wcnUw z|BZzCd1BNr+mtZBm~C^SKB>yqCM*uBb;M(3mhXaKZ0VJhd_go(wmdQ4Y?0U76ei4T z7pRoG6LqLWn=mmVsY;I}#-Tct9_nm8zaa8i*;NVa`2{H#m%iWG=HLY><4bqx{g9QC zC>xVZje4b966Q78N}ox{FK&G_-toP}bg8?e@v`2D%cZKL@lHJwzyX#+U%|g z#|KOb{xRB}CDjPM#WFRc-5c%RMNRM6uhX4s!N)pcht=zR8TqW; zOSwHvsgA9=HO*>sZoj0e=#i^YRle6NDst6x%U4mFt1(9#kBphNc(lx{hS zCV#eiS(!5)<+tmYb0t3gtX)}-jk)=`G-qH^by$?6o@2{uPW9WR=g2SKt!jNVXO7fF z`OnFg@4bn^*pg^ln~jRt;-fi1vpSe<>;1&Y>&1JrZOkooclP_J4#jVDv{6;Gx80V_ z3`n`Scyq_KW=k1gyuPEwxQ=MMIornE)bi}*rXruUUDnp-g4rHwyHct?7^~EMres@| zeLc!=dw;gg1*Mi(XYZ7{LirYEZ)1{@VqCUH##H&0S=Mt?ld`M@oz$b6>@2A?)iE&pd>uO=GP!O4@{v-t zvh~RxAeBgI*|t~PfvB|dZf*NB`Yqd5wAFZM1k;K>Xj`dLbcam)g;~!^-L7Mcv!mFw zPG-$DrB(0Btoml#q%2znG?wkhWQ}W9BeE0+vUO-*)4^J=)Hb`bMmMW>v(7dZ`K-;Z zEcM*#D6GtSD4AN+GpnFkRc0lcRokrMq-wJ&E2mkl$Vx?ZC<=7;PL@}nskz|w4#u|r zAZvuwX{uvErhM;LN4>HwnR})BD|LI8=AdkevelUyb5m_5XKrX#6EYPMviWVsWo}X_ zU8TlkK8f-QXJsyzQYmd}GxwX)u|qRwB~{sk%<(9{O`ps;&8fXI%`g4cmL8eCn)CI^ zwD$Fnyk6Elvlg3I)-h8N;ipHPHd$?Kq;EQEP z2VTZLJFvFQHJh6*HJjACq^>vRzbSPMO7Aerc*mzZj2a7)`v_eFC>=+tc9E?PnDX{3 zHMw(pP3@yc-Xx9rs0@pjN%DPdMx#_~oz*JhZEXf9N`_@9UZfIb12R^dQoddptCVk{ z@`V{+Nojr*EX=qcl_;2-G0#-w^@5oh8gsv&*1uONS$BN3+mZ2-QY*DyHD*{ey(_gd z!=h=b)OIV`A1bviW0`E9=!nNM?v}bqwr4Wxq`JyhXC(*U3dXi7$hbzfiAHfIO+m_%{?@FoJ*yc&$Q}0^sF{feE{-9*)*XmM~t|^pE zLMdO#cvJ3+k@ri+mZ&!OlgRt|OG-4S-FqURl#D60c=n@bmW(3x>BuMXNu~XiI#afx zC7SW>>c}VgQ%ftPDx&X{3@A}NyFb&h14}i}onJ&(m-H`LC$&BLVM(8o0aC9-KP*04 zqB$eAx}-;mMbj*mQdOd8cc0U-J$L(9(yQG_W$i1()XR-R`KJE6+ z7+6v$HB+{Z#Z^+P-M)FlNqNi}HO`JXy9SvTZ0HEA^yoyNX9jsh35K#Y3gua*q}5 zEc#gLMXBvY?@9eqYFkmG)J~}_MK4Ls_bZFG7Ma)Tq&63s*JeuXEHbYxmfBvlOX^2b zD~qge^COK#tQ)S@wIbFW+uIf`u;`4Ot$g!})VIiFsX0Z~x0yG~M#~r$kOHnpULwrq4_?YQJc8SPL#Ku8^|wm>-c5 zQp}IYBIRTKi)e)_8p#n4NS%$kU22J`sIG_<4Z!wPqO5KmTMfsjV>0N#yqZPSZDmhxJ zw{RY*T9FFzLG(STWG|=b*rZ=hm+eaO&5`Y@<45SuR{PvkY_^Sx)ijP2jZa|>YJqGc zP&%%#H|l1oo+uqr*!{TDj?+p}YEI?Xsw2wU7DuVP!Zehwh7^VFg!ezo;>*>pB+y{)w(U01NH ziyB)Wk?k_6ovrsv>56mqv#mFvsyIX7R)794M!j7$hCMrWQQgdLU|W>vgRsNG+0Wb!%Ju?pD4Lt+l4< zDo1tJd!*_rM^>Y?MOQDXvv#&VyIE~5(5mWxrqm7eZG-BqYTaGOnkp-JU+QU<7q%`= zs@BH}#EX^hQJvGJ`|YL9{{ zQ%bEU>?o!8ubD?uYD`5MGxw*MhfIBx(%)1tHuGJpSMOElwmOzlDcgl=!QPa%TH!T< ztqvF1IndO;R$5ifI65>g(zJ-s+*~Ce{0>YD+{l`ckhKq(v=qI>{DBm&ulhzE=>mk~d7X`l^+AL+xvI zxjxM#r9H`b-M_5ew)&ADRZRNTN*x&;+pLD_dwpb6zW$WlRW0ZhwVs=*iuRG}uOm7} zt>>nSqvkd3v0LRt7f5xMEgsD=C0jIVb4%AnS}l*JHLE51e3)JNXl%7epKD2~d65tB z+ThHze6>n@y7w}@e4S5B#gF9cT%;LQ9lz94Z7vj?pZ}_C`(>M#FJJj@$>Y!Dn>S`k zJ(<5wHqDCs`}0>yX)eTjrPiobY57^!H`(R~>baGYu_t(2CHIXq=6w~sA*J&~-r-=o>Q#Ap2ZPPBy(6_> zcY)beleu|&gU6Jr>+g9VCuffovF2@7d7Q`W8g&Ne9nZ>3u31oE!? zsMMywVy%-rZ8)mu+!iv1aXC zsBf>RO;8k*u;MC zsOpf%TTb&1O7&B|kG=Ui;v7PpVuz<#^F~Fz#1>C+uY6u&lc%_Mzl?gpNN>DS z^?oU_)*E3;sVh7?e@u*eExUQPI%;2*sP}qHog;Op*G=jZsYPD7r3Pcu4trNhy&P#w z%)x6K-?Tm67}>f6WBtQcvOie1cfA_fCaG2W77QtUx+XEk>mhZy)F`jQB2j9jS1OyX zE2XXW5>kqiv{jzg7ylHg6`rToXR9{$40i5LtEb)#Qk-W<#p8a4^6BEEO1Vrb%anhw z@=Y`4d=m8&folLTg{fGT%7w}?k?2$+(uG0x6;_PI2zU5?XLOs zL+uQiwf2cB=Z)N#)Plp&MD8=WoAjBXJ7?Ro2u>2S`GkI^(@i?=u7G3U{cwqx>S(#s zC+NR}r9n*aDfqb47R$Hc3^9|>9>tw1EEgG_x6rQ|y$7&-3%(3%;F+R6O$R+JgJD0| z1-<}(3GEGUoR-I(Qq%4zc#Y8;55F(^eDW#o-bLzRW3s2-W6}{mvmf`0NKGg86nMLs z@a!{imAk5++}3l@n(0fCC_^FR`{rx>Eyv&m=F`hTOOm6aLk_xTASmXDxKpb z<7XWC%`=&fwJJe52`5>C#dbDr%cHbxe4Fib#2$fJVvbV))1am1IGx07yq(Rb<#L;~ zy?4oBlyk5o_*`AW39viR;zVa7Xc47L`)I2%&a*@^VGgq?$7w}sFKAJkO)s*O@$CE_ zOAe_vQaSiMlXp?scqZ;#i2Y6x+Z@NnB8Qge;PV_>mDyac&Dq@McxH~f*V(b&%|>?v z{26@2*c@FpvvQiF!j8&jynH8+P7we4^rUnSD??f`u3U?&xa%7o=CwQ4v^P%l@qFB~ zxQ%-jx0z0o$vAvm7VSbT)n#RHu0}rve+bVrI=>KOd{#B?B}cgx_MeNH)RO7+!IF&I z6zK%*PcWuAJV_>}dF4#5!1&X>-i~sAWb~%PYOzK0O2UYHH<0HQxJhiuiV=5n&Eiaj zzcP9jdvWJeqq`Gs#PSz(a)qZBW@y}9j{QmUB**9>bR9fLOmhPCjWEOL+S(QOt`S=# zXP>(Z%LMoxI0yEF7Q=DRVzs5i?Dl#}hYqpfnXlsBO=3DLXBuOj-h8f{ORB9Zakm!T z0e!F0SqYDdKJn%^N5x>XdzTqq^79Gf%;vqs6pq>_W+&%@JAnLYkdbwtHSN9#ZKaC4 zvy9H0=o&P!;Vr~6TFj*Vao741cjjTYUdJ8tRoq*E#m-7`cLKyS&JyWtW>(yLMA~m& z$&)R)kDSBBFd2dFTr7pK7+TE4-3wqlF|&Dp@(j9EOt2a!h@1qTPcU{loD*{#Jdk`g zku!$;7F`L)*3}%IOqJvGm(FW$Te4p(WXZ+n*-kPV?d%h8K3lctSc{e%&a*j=je90- z$ZW3NVjzcAF^3V%aRMyKvzVP#^_&lAy~uR}w4ICN=(uAMm0-P1pmXR&4kMf0yb4v3 ze?P2;_|3UhI*p^^&UWmRjNZBETKHR|I}*(bmKtI!1JNCJnr2B zFM>AL(w#2oZ$V>`!9>~=#}U_SyaO5<(>4m5gp7K^(JAU$<&lTPO* z`M67}H-gmpu-xczM)u-p8|%22?AJ=^?B-*)ncaO!%uIUBWfZ(K$Zs(j_ma_@^zdm| zENpnIfi>JAC#PaVI&OCSvET>RgW|5?u3hW^#Y!tirwyo@R7^j=l%p36p*Q z5viH*YIr5IxfXYxgzLpLq9X1R6>c(ae@bd{MB18mOJI9A43>&5*}rAud7oWS8mnTO zlcm&5o=2C->Kb>4a@0@YH27Vk^K1A%d;|U$v^Z?p?5oGHaIBLY3!8UwhuPsIOa9a> z?je{Q@q^?!5mKXr2OQ>+b1pgWfJsjVroH4yF^k<_k@}37=HN-^H)e5OhwsAOuo1ok zlio|t{c0=&jcyeE03>>yAEWPs%S4}uis03_b2fJKb|${fOukP<$DL621b$9%R1Vi4 za@beL@mpqceQ+(JvXgV%yV1&Ydm5el(GNjl&&@XNL1(UMC%MYyOJ_Hq7kZf8P0r09 zkmp4))yXsM*-T7puJ?d+3)&g?Sk1ii$dmNybLhcF_XVS4`Qy&hZ~^uya1WNVATi^< zCY_e-k#iBc1*tP&Z}>2L9?myGg*tnnbGYhi@s=AjJwGZXIxxrbcjIrQA>5~OqZVA zUAx~W|EbX8Ftd4&QDqkIOzhU9xZ4ZMK%?`Cn922)xcj+T++^JDH0`Z`9kI`W$+?ej z-Bws;lBcWaC(i=TpRrS;vmAY{n3=pj;LSIS!-|m{Q|A@(ltJRtC7xYk(oK4=h@&hr zW6iUPxOLycZZVUdw0nv z>173_&G{7dp>l+)l7FWjXiCu}qANRC#HZ@y?#`SF&8 z*3!QlztjVXm~UyYI7(4s;v(4xva6RP%W0Ou^)SdN00YQW$Zt$V@K4h zf2R#r20agF+PJ6$Xuq3(b|+Pfk*aH^$~>NDW;iXJW@9J26g8*XZ{?CLV|zufaE`So zwO7HRaEZNAmmhzHi^B!_IofRRm3e;Za`kI@?)svul6f-UJDzvQp43^Xv($I7`PD1d zBev-O#_M>U_S@FT)LwsmX&myCk!>+!anWx4-<-|t9P^Fy3Hh6kuo3*`%f>dj9oPkb{=Ss zKzgAZ{QcK@lRdE>*64h#vi|zAqmzH>m~xHOcE^p~(==lE`}&M}TGO!MdgfoG#&L!( zbKKx}`n}X~y)Di;!?*GK#o_Dp`*qouE8p*$HV&WbxNgj;8~&K=PbuF_+4m`3<#61U z*ar=t>UjR0P4&aK>8P>8cQ~H+tTSl%9Pj6%o}BF8pkrqHv&4CNc6vAEToepc`Li6) zeYk1L@cHfvqtbdS?W)L;rVYaCt}TmC@5o>%Zp@H;o%U zSx1ZXR8PzIo>u-@-Y4Q*Z@;)8nj$W80&@2zcW<@-!r}exm|<@?L6F&$KfGViw`tAr zUbJj#tdm+a-U+NL5V@VD>- zW9T%k9Uj*%{bgs+u#c>V!yd984qItG9JX3MlHG~Y+WywsVau$w!g zov72kY09wm&Pn34PET=*(@T8GIYr#*^cLT6qVD}VeuMjCqx>^=*fxz^yJ1fT-Q|%T z^3v7AHmfXa(Fj@;r$q@`RA@dQ)|PgSa8kVW%C*>g0jk|+?v0cvz0RH^I&7Xj|7=*D z9dYJiC#E}G>dx$~cr&ht&%sUb1-Qc)UJNG~ofhWXGwaN^XHJg%x@q>X9+BPfH&Ekn z=2RPpGiRrKWtQ-la3_4t=%lIcL1!LT-NxtOCb$Dy%LiD?&-}<*e&%j#`I#Tl@>;b< zM}-rNPD@X9pSjNeE&Kp}2tP6ge}wyt5fA!Mu{HGB@D9`AVq@$jquvcKaGarOZcn54 z3-}WJ6>Nl>yGnfnzG?JRje1VA7+zwG^);$}*3&e4nnq94bdQl)G)AKJH_gU#=qT&& z(66k&Ll0Shhkj-K9Xf&jrqN%Or}n)vWSZJ1s(r?n;ICjKd>y_4-!!UyqS_~_eWKbY zI_c)Wq0=m$hfFswO3RC)yeP_xL#LS+MR{@PH1nb;FN*Twkn7EhL#LS+hfXsu4ppiC zv8IhfX8TRXz=1CGpbrBWfr=O9)O->(pN7n)?DFt#)8ReFWLt;qu(sB0x3)^Ft)kj0 zs;xtISX)K4b;u5DtEje$YHQ7_*480AtgSz!1-3pyqOmfgw2-14D9j?LskH^Qx@~HPsfWHLu#bP!m|ZN-JJP#jB`z z6&0_d;#E|^YC}@Ap9JD0gu2YW7L5jjKHWdr6o*<8OHDpqm%3X)EPAR18*IC0zM6&fg9jP zsB@xnJ`XoTogZb{3SWfV;4AQ##z;$JI37-fli*ULld$-xPE#w+sa~K~7@vSo!)M?I zxDh@JpNE^_&*4`1BHRXFfxk4W6{1>UoCqhurA8-V=j}nyYAo7Sf1t52-U4rhb?^!J zJp3Iz2tS8kz$389sIf45Fao1SjfF8CW*9XVqI~gE)BV+x-Iw8Z_)1e<^>}v&{3YB8 ze+6HKuNi~RmOnTJo@)&I!P6`?><>>j#_lkC>=U>jehLp5)e4d0&*34XljpwO zv~l2O_Z|2y{4e+({58~WLwSA!wdau5oN^4wI#P{${EYu`{4cX$8afp04{?M!iQi3)c#P%YJVs`VhsAg)8Rlk(wOos zV|XI$VYEG*cr(1qNLw}zJX>usYNSMsl(-Z=0GGiB;X|+iJ`7jDM~rHV@pNcjI6D|= zR9i%~#n{8Bwis`QcNz8VG2d$%d&UIK7vnAPR#*q0fX_pnC6wnN{2YD(kH99Q=8MsT z5g3KnHsud^Nqaw0b4PhJcZ?ZE%^lInw|E(_$l^s*yoibyQSl-wUPQ%P=J6)&RVWxyia%?`NRqDER#BPwbJEV8H(6*U7EIr(-zJ^d;> z_n!WSoqJDzL;l)0;4%5jcniE0*1;#>^U&&lO#U){4!?j$V3Se)GV07PMqtz^9~skO zhEYBe<)hrDvHkZbc8vGKAH${a0k{l42p@tC@L{+DK4P?ae>%JA(>ExN%yK#$2uB(f zLB<|NMUe4kc$bkDjq5*IEi&E@e+-wx2jDXJAbbcmK)rX;QF`wrK4MfWjHknaaHLVu zEUGQW9!9mrcr&~U>YY@7&uL{b(!a){LsWE#ijMv@8oT=bM>TfFTi~s*4n6^&hrfdd z;pgxR*kshG89f+*QKLr9m<}_H7G?cwHFna;cAd7?+9j%8qS|%ZTD5CUzujt=QTqo` z`v+0`2T}V6QTqo``v+0`2T}V6QTqo`JBNO|)h?rU4x)ArqIM4bcB@@R?Hoky9Qy6H zcAd6f?UHs1%wMPF)2@8ll~23!E!IzKv{)Av>!M;^RIH1Nby2Y{D%M5Cx~Nze73-p6 zT~w@})~MDQ73-p6T~w@})@ZRVD%M5C`e}`{uGk{}v__}3?ZW%*vzgfUfbDFhwX+qq zvlX?o6^}J-?02^9sr#L6duq{zs!dvZYB7MSSz0xVn%AP{wWz&uzkRkh?zhkO#-jGd zqE@}Whiz}%Z=darMXeD1&bEDbzq4(h+;5-L&i2WD=i5H0Z>sH+`_8xBkf_~|sNImL z-H@o=kf_~|sNImL-H@o=kf_~|sNImL-H@o=kf_~|sJ*wSy|<{Hk*J-KsC~GoeYmK7 zxTt-&sC{_f`L+-5JKuIsedpV5yzhM5Ly6i$iP}Sn+MkQsN%fs?yY#;Eop$Ph(|3z{ zV0;2<-`;ns?b}7|+ePiImHG_a05`&Cp?3Akqg}oDJeJMy=TJL**|(x!gxlaNP&2#O@TR~l!lD)Mtl;55mul z@}KcqI1}po&Sbd(&VoOHv*C?!4!jA@g*U@_@D@1VDE}GP!#}`7M)}W}2KC*g(iyN7 zEP$=yVxz@V@9pxXw0vo#CGw?dS|VRc#|oW6r#8x?rq6&Ez_D-|TnV2xIzx3m(do5E z|1~xSP|cH$Le(rC!Wh*1Pgz<(MS!$MQB)j=iUTnYGhr6YhB+`7CSV@Shl(B@tLPCG zJz^m&g2hmCP4-f#87JKqwu5D`JuHVEU`N;qYKH2l&ae`8fmN_8>;_MO-QkI_2RsSt zoxI998TNvwz~1mw*azx8xpMY{r@{V4U+?Y3BgPP)hxk0i=OI21@p*{PV^>(t*llpN z(K*lYn(BJ#(@!vf5g3IjFoZFf3R}Q5*b=703>b%*FbihG9GD9eFc0R#RZ6`VWV}MgD`dPv#w%pJLdGk0g_R$>4X!q}NVe)i@8PD6J$KrEq~~~FyNsSYeeE*D zAHh4J-dD+@J%Xq`f>;mlg-f8``N^(#exly_iA&)Fa2b3MJ_Pm7PkEL@+TgE1KLS_6 zpTJe{QTP~q9Il3I;9B@oxDIM3r}DM8Qoppf61BGywYL(rw>oK??X6DQW_v49y%x2% z61BGywYL(rw-U9t5;Z=e#z)lNs^@syTlE}odn-|UD^dA-?zCNI&!x7@6mNmrWlC$8 zDO%f>+AdT49n>yUTDwe9yG&8LOi?ZDxzu)kDwGDYn&MeQ!MD4Id?XX1cute>!MD4Id?XX1cu*5UrFjx!Ef@i~X z;YfHMJRgpR+JUQtG4MjD-MB2@f#cvsa6G&iPJmAugDc@2coUopZ-(>WEpR@(6)u3< z-Kc~*_(Ql5-VPVRJK$pYBX}pg3*HUyf%WiSxEB5tu7giNy^Bzd&%mF-4R9mWyGErx z2RFgz;b!=AxCOodx55|UHuwwp68sqc9`1qr;ivEb{0tt1pF`~s)rK$OSMbm9DEtc~ z(gGqaLUculB0YIS$3}>y2+XJ&xD%Ived$};Mwq8 zI1-)*HJg=lG`s+6Hp`;fERKcWftt;-Xf})Ep=Pu61o))UX0td4YBo#Hg*U@_@D`}q ztkhefX0!BdP_tS3hj1ah9WH`*z{T)K@J@Ia)NIzVn$2Q8)NGbs3pL`>8gWr0E^5R@ zjku`UENa|Ejk~CE7ZvZKVpdcPh>8JGF(7V%n$6OR2T}1LDjq~dpQza^ehhyPHJfGG z4?l$m;Aik4{2U&En$61p75p>QY?ei{StQbIHdk)5*(?%8Hk+l1CA;?2(ZL*mD_Z+;tBoqaU5ra zUZ*-EoiX}rre1@bTb(+m+Ii5~rmGq+J3E{!o!6Y#ovWN(&b!VG=RM~`=X&R3XOA=2 z*{jzrZcJB4=ers1K<8Gs+P&I&-ks&naz1tMb02XIxUaZxIDc_>xu5C_vH$E9xNW>b zuh>1^Yv-Ns4)jKO-*&I@F7U>=GrdXP&F*aP7VlPfnRlC4=QenYygS^7y`|p6?jznx z@0acx?^oU~_f2oNx7+=-_rCX`yW9Jn_qqFl_l0-V{meV2k6eAJ$1FzNqkha!)k9m- z{2b5o6Midw-(_pR#>?=B`e%E6{Sp2MZ=gTY|CTq%|F%ED8{%K<&-BjoZ}4yMzUBYG zzsnn?Z=PT4UE#0upY(3^-}K+~>ij+aAG{y>NBkptYHeze>MaV=f;8`rAU(+R76)B| zD(~)~ThL9<|2;A2;oTeb40?O_1$~3Q-m;*7Fu;2-7#IxkmIuRw;ohoXWN@DMXmE9K zwfA_Sm$y1t6FlLq37!hJdrtYm5Yu9Pc-gL?ln& zz0xXD;2n$$ z-qFbAk;}cmM5acjddKXW?Y$=ZPJ7?6Z?pIL9(&)Oc=%&I=jnmS1AZjZ5NYtE`i}Y) zeoACzWTl=CwJNg8PmQdNto2*yo9dtN(<0ACp7qo1+v)Z68-2sPo)7m)H^)2xczg09P8uJV6JK_Bz`!;yLIGP_V^h=_>qP_gG z=&0xnzrB5PyFbXjx!oTe{Xz65zsA0;-5;J3rlk63r0yRHHOvWf{42vmnD2itY#mnkSA{2r zJ^Z=hX<@ZLFRTqO^cRNT2`BqYb-(m={>t#iu+HBQ)`t)H+rkIKhy9)5Ps4To>*15( zQ~sOb#&DzmR=6qL;=dif7;f`_9ljjC?EfZwC49wy-@b|6|84lk@Q?lnF)tSQAI8F1 z3;*L-%UHUqVuSooW9P<3`Uhec#4hj;#;%B6>3<%Z z7MtdO5xXHa$3GmqId-#uG{`(vbA01%5_W-Auq*5aPk`OwiLeJe3HF31!(Q+d*c+Y-`@p`iA3P2Aho{4#@Ju)i4u`ey zEO<6N2abT}!jbSics~3V90k7(N5c!?7)$!HeK{crly+C&Eea5;z%7ftSL| z;N|eUa4Nh4UJ1Vkr@`;TtKijeI-CKof!D&B@H%)syaCRFKY+90jc^XU3C@K#!^QAN z@J@Iayc^yF>*2j{3A_*Lp4YNx{e)>h59Y&GumHA(g|G+~!xC5u+rYMvi1mqBpNRE| zSf7aXiCCYA^@&)Yi1mqBpNRE|Sf7aXiCCYA^@&)Yi1mqBpNRE)Qt!#I7d!>_hNnUz z)+b_pBGxBjeInK;V*LT+JOd7dgJ3lr3~S&JNQC=DuTS&_L~L-rr3T-Equ{sUXm|l6 zVgn*JAYua|HXvdHA~qmm10pscVgn*JAYua|HXvdHA~qmm10pscVgn*JAYua|Hh7BK zUxwS^C&q{ivmlWWArc~tbx4mwdKA*5kRFBfD5OUrJqqbd$czZ5S!p3NB4kE{S7VtD zXTW-RFI)oegZIN9!;SEHxY-zE)MAWUY(19e;3oJ2+zMZW^f&fv(=8ZH_fqF{XONy? zJpi5o2f{(H8V-gva3nkro)5nTN5OBy(eMH|23`oq!tcOw@FJ)?1Jwro?^V?QUPav{ zDC#aj@e(*0PJx%g%i!hkyKpMJ0$vHf2dBaB!>i!ca5|g;uYuRX>)>2?Gn@x+f%D<5 z@HY4$dpQZYv5X_`!nSS-JdDy{!CH-Nf)1j`cJyF z?${J{$ENr*sQWghb>F7=EY$zgrS<=GQTK3)x`$KzIotwYfLq~woLw zYp@Z%4tK$~;M?#WxCibvx*0GIGhr6YhB+_+^I$$~1q)znSO|+?F)V?lunlYr+rcu} z9+txnup{gQD`00>3A?~5*cEnzC&2FTMA!qK1W$&&;3=>-JQenVePKU%8te~ez-!>O za3&-s-Rsdez}fIdI0xPYiCdSrb%|S-xOItJm$-Eokdx?j>(EP#-T*id4uaKiFsy+? zAfM75Y*57-U9x%m|Me;V~mTBF!Vx zJYvjym;C<)--Ew~zk%<=-@*^zhwvl#G5kH;1OEW`!au@&@DsQnehLr4FW_PLCwK&Y z3BQ7WhDVM5a5x<@;`+>o+3}~3KYje^<4=Dfsd&|2guVkVHU=@63e#aF>;j3;fcOlE z&w$7bh{k}Z3mEs{4DvJX0plJp;=xet!yqk>Fn1!96QLYEXIL$XFgj6YWr}N-6k

us)Vd6A*)Kr zsuJdrCm*(kg^)IftR!IxmQvUTwuS9rXNZSGJRDYG841sW-+|-cMQ{qd6kY}|hY!GI z@Im+xY=F#;@L}{C_$0(*G0$|&hXIVh6c|EcBZik^J+UyCW3)ELN)nre{YE$k-UONB zF=8@Shh-ro4rBMCx5HQ9AK+g2N0@9U;}~P?TGX1o#aTx8KHGidt+m~Ucm^B@6@#)U z21T`3R3wUOwJ0Bp=fiKoQSjSvG`s+gffqu>nvT-Qh#DDDBO{K77sCl~BAf&-fs>)e zQ28~6qQ+3v7>eJ8Q{ffxO87lE4SpY91+Rvh{W?~&U%Uoh3$KH7;mvR!yaj4Zl>b(E z8&uTD@(^r*%i+Ut1$+dqgg=3+;G^&{_&8h*6>BO}vswHpTnC?kib2_*g6rYa@EQ0s zxB+g2&%)>6Cipzu41W%{z!%_F_#)f}6=kaN6}SVw1{>k)a2I?Fz75}jd!V8+Z>{Y< zM6DBICe%71t#v}ofeDxg^I3AMVYF--o}2AHt8|$ME-X5Bvk%3;ziB!B60R_$fRDzkr9~ zpWqSrCHxBh86GwI4s_vgI2{rRwkuH{{A{}tY5Z)v66u8yZ`-a!mOJ2L_#=2HybIn9 z?}7F3UdZVC_o45HKZcB;&j|XApw9^UjG)g5`i!8@2>L5H>Ji9j`i!Q}X!?w%&uIFL zrq5{ljHb_M`ak8^b?^zJ?XARAm<}sp7s%|hy_NDX`)qF|eJbn^iE7(h$xc+;-b%U# z4u!)Y{*OdV+YU>lG}~cG_kpz2c3639ZHFZ?e{F{)eLAE@+hNHv7Vd+eKw`#rSW5j2 z9)zDmqQ!Ptd24NlCDLo#VM)hfCd`7_Fb5`J9?XZWVIic=w!>06C9o8>fo)+s*cswy z+hHkx6&wlAgWrMU;6-o>ycAvrFNbsC&2S#P1=c(?$48?1vwUAPSWASCKSqAn!r zY+t6f5kKLR=zT`pmx(?MAY&R!L5GmIvVECy;vd_cNe_nf)OKgG%z`(G4!;Yh!YkmF@Oy9?{64%2UJa*1t%WM*8h9d>TFj ze+D!tBZ^5_WJ8%zFY^K-R z9!=CrBW6OaG}2mW#2lD_c`zTgf(1~kj&f>NiCT3;tvX@}EQM`gTi6bk!S=8mc7PpW zCs+YH!%C?2NhMdouCNcrxq-Pl3JRsjv_13;V&-V1GCRUIVX%Ga)f) zdo;D@1~?nu2W-a3={2@HlO6~M!D=`d*1#c< za%^{|{KUWQ&ZK_`Z--0a18^B+j@a%@IhiB2JCj}snGv=xlZ6@KJ%(mR*uG45W`ym_ zq-{nF(EUY5n-K$af06MSxB+g27Wo5of01!Bv}hlo`-_Yg;RAGkk# z5%?wi3jP@$g~!Ai-5(|D{wSjl0~mo(m;yr>gDqehYzfn02DF)5qq`lAHgjuqx1-T! zZjJ7CG!BEq#ai7ZWtOwx+3;LA5}pUohoj*Ia16W@P}|Ayd5rrcfiH)NwLQEhUvAoHx$hSwLvB90-5!;H z-qq;-E~Cx68r|Pz+yre7*698&qs_q@-QQ(=0d9phA8T}fm+=?yCHOJ48B`POfqR_` z)BETyFSC3K55UjhLHIfRf}DrppP%K2z zRFBl`sk^^STU6BO?k{6YaX>`hdXnBp_kfut8|K1F%1%jwp*7a4z0#oyB;c9y|s2dRJ3|)?Ruz~4_m`RSOkk< z2`q(eU|ZM@c7~O(3#@`8;d#*JW^HJ5vo^H3S*y=m$|t(R%{U28!G0;c3|J6kHeosda673EdO?~HfDXS)m?mM*#-ZA zWiR|AOnQLGwD%3^x^Ks*r^Nn#wtZh}AjX_jbO2#i@>o~2X zvw7aS?xK{=asn6;^UyKJm3AGo#GG6)-8lz~l^i-5Vk)&Hzq2)!r*fzA+~b*; zmG8swoOEf2Z-&rqP12szQgpGWJEx${67r1izzL-FRA<;;jC(&5Gn3yj=B>hRwWRZu z-aPu4;JaQNr?c!?l$=FNvM49ZNyBcnXE{+Zpm!l22=QTn-Qhc0(w&o)C+3ui9;tfj zH99V)Q)6m#E$07RJgi$3r2}-#v9ZW&_Msh{i&s79IQg=u2GON$5&YQ_yW5=8MlGZ? z-S8%z?(`C4_&J?#wMnH$k6y%_fK=;U1dH26&z@6*E=}G2|KfH0xrhJb@A;X%Q~RW~ zP3;pajg@8|&Yl-r8y=1o#L|zyQV;#(D{Xa)I{l=ky^(yiJkX*nmZR6d^X$(519?)b zzWIukXC6-9mw7nzaK`MCwYfVA=a|m#mESA#@bO;!C%@AsrB6<;h#mgsOSh*szcLzg z^sV&CSN4I7#_R+3I&S&L-?3T$^tCwlf8f<(+W+pmRs%fI}cjF#BD|J$f(F)DR;i&3K8wEc}2wyd=I_WQqk zrDj=5OzTZDqW;RV(TVp`*T>3>m&D2|Mx;%$*&FU|vC7t+Sb0WciK$&j0Y$ za{f0Wpj5GI@oTTejJwf(D&gW5AIDbY&15bmBUmxp{90_k<^2`m0lg0E_tDrmd$Mn= zd#rogxdp3qFSXbDPWy}3;^yC1wdfbqPwL{-#o}yUdd6`6vDl@tJ@!+&H{LPUJwB)W z866QOUzzo(>vSWa zA2{bwqkp^=ddB8C$uzUue=+nlV?7nmo`EAq{Jk@BYcWL|pzyQT)U?P~eA zK3bx@(MY2#uj^-*`l06#L|bSy^W^itNr|SCvQa5(oao=wx>jvIUUO95pZ&F8f73KY zb#0WN#_CDP-x!x~cxR)N@^>w0P+bjbMFXu!u~y438|9PPkuLhZN8`7bF|60IuS&h9 zz9>>dzo|Y9)Z4gzGUdS(i(KWHq8w9{Iz_2dlsZMJQ}jILzb`rIn>zE26YA;54I1e+ z8gs>T(?&h-xIs@lZU}ek$6_+X%in9NJ3b=j>4={E5gFQ4hsQ&Gj%1CWuW~Hr9#-4+ z^fUE+ifWjRS7NGVifWmnTE6y`YMP>&rl_VVs%eT7(=#OM|HezdlP~nd)oAqC7hjK8 z>c7nOO`o=~=hxa(uuXiuWD=TJE<>_2JFSmz$+TK%lj z&uZr$x0inU>ZhN6ZgcMO^!!i#H~1c>g`3utW}fxWl_&J937a$OcYWmd()*gaMh?lJ z$D=RpZ{p?O@kPsj+M1sKyhVDV*dSV!j&EMk2>ePve~ow>k-vKKuQ9$cMb8Y;Q`|P{ zS-*cjhI)!)^9t|}FIvQ_$GYbBr68`P}ZqV52il#oBJjh1l z>%M2vh61(YlBRlnx};taS?_CQ^?%>AQESIotsNG_U4ym9zR=3jmDO&IM(j6DBO)IV zv11hxbxxKdz9G<4UxKCj(eqsbJ>4bHvt0r`*(FeX2P^cWr?~`rmP?=~xdeKSOQ5H? z*f}BGDxds9KQGBIIsddxcWJiV)715U)wW#XZkYQ0E&Ytv&qex~pr6V5xmiCJlX|X@ z#ic$O@;}C=KHE^I=g{i;MS3!1ot_I>r&z7i6He>g2lTT{KM(5XA^kMy=VAS<(9a_( zT~F!>^psnjbL#XY(>gu#u})8XWKGc19_wOvHJ4nklIvA+y-KcE$@MC^UM1J7l(WmE53`8&q1$N_8kN3ArLR%xYgGCgmCo5qrLR%xYgGCgmA*!$uTkl1RQei~zDA|5`Ty8E z6ZkBO^N-KoyGaNk*ZU?U5CIhd0TB_oM1+Wl=!w(02(s-jr>n){-Ry^<` zh-j5qtB9zmkc1r6wjLCz2i~YuDf|DE=xX*pHeO;z*dyX<4;Old1sTMy7Cv<=`Bz z+j7aKF57*j#_AHyUbUUuH99mLr{m6GjYoVnYUF4Mev+05mA|qOPS$Hnc+&A;Yw9~yDB{kD_GJ7 zmb8E+t*lyDo3cs)_OGP%s|K15cdWFvDs5m%3s_R`l-94L?JH^dO4_|bM<}xwiOJIL zm9%;#ZC;Uk1I)sb!hZr3EmP9Jss7xVy_tEPqM6UnaSk zTGDpaio{V$>OG(z_PmLhcQMCeJN14srPK?=aOg`NtqZlR}x41jeDJph6r z3uJ>FkPEtit{@NW0lM*UfV?d7awmYJz|kNCuu9<`Lm~HTQ`>GukCE+*HRHOO#K3O? ztQvEzm2$0>a;>#I!?fP^B@NpCF53BS@D}agQXdS&#(yam{!6u$0BsoC`t!9aPz`E8 zEm+GugOTX5!h*#IvzpKAV|%w)Q?)2mTG#1N3}m;@Ql^vzdu!GZRZr zUkU@C&%Xkcn#WMO5nvRW#i0N_hFN+xv-E6tF&f5ofE5X5?AgrNv%NVSqrG@&E8eHb zKLekGFThvGz6S6KF-Y3Ql~!@F?<%59UVk0`O7H>r0(=Rut)hR;-(H>wE(TaEF|b!+ zTn=V}-vb^K8CQTS!ByaDa1FQ?TnA?H#M>$85Dr&U;)57jTpilS;Tx_guYdTzEy<2 zRfN7(guYdTzEy<2RfN7(guYdTHdlncRYd=m(Z6N%ZyEhqL_ZeMk45xj5&c+1KNiuC zMf774{a8dl7SWGI^kWhISVTV-(T_#+V-fvWL_Zdx`xnXe8Tzq|8NLX8t_Xduh(0Yt zrz=9AD?*jSBETkLX zg73f<@IS6zx#H4DdyVpalPliJQ5uxeWZj$a-wy5oXyUP&t+#~ZXTfuT^$KxF>_{T* za!Pxg(i&$SL%SV67C8;#z2H6oFEij} z2E5F80LZi-0uKYYnE^L5;ARHg%z&F2Pk@Dhx!S<8rtu`es;0rb-69iX)@+U`O3Ree zE~Se;AZ=1giV}bzZJXVS_b+tHplrIYm7D*+yj`&G~S@l?gM`Y z_k;Q1LBQJu8t)eHUS3GPWmh+&F5#GeSW~NmO=s7T8W&z5yx}Y>SN$5!vREmXHpHhn(Y?jrRuwft;aF7a>+t zLUl7W_R8gK?d_xV`jB@G`#YJv$LRExPG9NI0q$X)dszPfaF6I;0`w*wy~#KcTnsJ& zmx9Z|Oz?Y946XoIf~x@iVbC82{bA4_2K`~4&(%$z!}yELkJxXbKe-0^kGnDpeak}M zva&!n=nOc=x(VC{o&}W8S_;a*^WbH$9DE2q2CVP0)>CG4P1~gDd`G{Z2`HCKxm?QS zq1$<*z@gw|PynWb1>i4!S;Br`0N2TcpVQqU?agkNuDP9eR#Ycdci1#W)dJ>^0_Kkb z=8ppAj{@e80_Kkb=8ppAj{@e80_Kkb=8ppAj{@e8LgtS`=8r<{4e%ylMN(r$Qd_!?{i-+*tycVIL4FW|rE7^(A_ zp9)!kQ1gV^(5e2m@&%vA-oq`Cl2~a z3HnJ1^H@IfSU&SuzI&Ko?2ZMC)k;nQb6NrVN(u8?A@f=R^IAUhT0S~U2|7y&I!g(2 zTp@E@A#+?Ib6g=hO9?tl2|7!OD57V!d2NZFDW+$N>6v1Brj(v3rDsa%nNoVDl%6T2 zXG-aTLVBQ(9w?*-3h9ADdZ3UVD5M7p>48FeppYIYqz4M=fkJwqkRB+c2MXzdLVBQ( z`L&Gs6`N0hwHaoFGUFeBbr}PGY`g>B1@8eyDdyiY=HD{r-!kUkGUneh=HD{r-!kUk zGUneh=HD{r-!kUkGUneh=HD{r-!kUkGUneh=HD{r-!kUkGJ3U;UM;34i|NVwb+uzS zW^Ii*xXj97gbA`5o&{joc4suq$eJ5HTTIWE(zB)XY$-ijNRJlMqlL`ZW$w-358xKS zjLY0z#@t=T++9Ks%ib-fcT4EqLVCB5dAy8yyo`CgjCs6_9xkSbi|OHFdbpSsaMq2m zu+CE-fVMBt_Jw?ohPE#}08(s-*7?YzoobCu8LDxb|5P|6rk${0|} z7*NU>P|6rk${0|}7*NU>P|6rk${0|Jef3g>Fk?W?hSB_jeEwL7( zy{%-w3Y7bTXGDTEihi)&ez4ts*j_Ki@_H$D*X4SpWLo`repkZtyAqxy@%RVOzu00g z#S(idcGyc@`WGAQu~*-0#*I?O4av&-xvM~h-;ZZdf@e@dU1jIOW#pB1BYU0`sjF=v zezaAttl3;yv$?WnW5c}^dxVGMjsr)4@!&`>0UQO61|cvJz_iesz7CV^wYao~7x0yq(z1WpF0fXSc${0d9~r-IYK>0m1OHJAp@0B3?ia2EIt zI2-&HoC9b{?L2Tkm<}!gGe8lz5X=O>2iv%wuPxx5N5G@tG4MEe0xSfjU=er{ECx@3 zCE#iB4EPIp7CZ-*f->+t_$zn;{0+PaUINR&%V0Tp1*`zCg4dXlUk5Aw0@jRQK{tH` zee@Od(O1w%UqK&z1%31t^wC!suL~Hj3mC5p7>^4Wj|&)&3mA_J7>^4Wj|&)&3mA_J z7>^4Wj|&)&3mA_Jj4uKIj>dRgz<6B1cwE4ET)=o-z<6B1cwAs6ac8BdyK5+SSAp42 z%;5j_=9`1qABg-QWQ-8%jvMZGXXcg9L1vqW5zcjEa-A>(7pSrOd^LU-nDV)6j$hz> z%w5s}WB^7lcTcbv*cpFL3EEmp<|YkYoLD@rrjf{_vPz;Bk+5+#l+0yodb~X832pbD#pycHEr> z+?@s7odx1UY~`21<4Sb2Kb@6E_*@Bmt^|wuWrhJvfM#ddzyU7sKq5dtHqeC)^lk&a z+eiVOKq^QB>EH-39!vm7fuq4OU=lbM90yJU=+VY0U@}0THhu-BfK$O~;B+t*+)ml< z0L*Ui%M$oy3H-7Iepv#)EP-E^z%NVSmnHDa68L2a{IbO0sTcgR1b$g!JOHRO{IUdo zSpvT-fnS!uFH7K;CGg7<_+<(FvIKrv0>3PQUzWfxON^%gvnh7=%S`xy2_G=82JjRU zo?^mN%vs=i0B^X0Dl87f|tND@G@8qUI8n>tKc<&)?=dem}osFT95e| z_#Auzz65AE=GR~|_%HY#`~c8!OsU~;y_j4t*t;*||0GJVcwdIS`!b8`0$cZGSh_E> zGC(F64o2}#!x;95gYo#OKkMv87UgChOJW{NVjdgHJT{bhY$&U?#jM&Evuaz+s%0% z*w`<_!hV@o1Ah$o#V{e>I>IVh*cR~Nyv;C_uVZiI?V-nbn_&`f7rib{!1`~MI8jT` zx{H&wk=XeyQg(hX(SC=W-$$|YyFe_~9@SnFFKVx1?RTwK!+P=tW#_jyc7FS4ef7S2 zKW(tSpFT+&q92Fl-vTWEo}m3oKUJTqO;MJArz*?8S1ZfE*D1@t*Xy_Iw`(_I|Mvl{ z1pB`)YPajlumb!zR)A}@XZ649?`kjU@9XbtuVDrF1MPMFKl+E-N`0fgQF~MW0=vL( zDT}}rSOm7UDrpf|`@6IVto=h;1lHC|i@@54$|7*4vIv~3ECP2?7J<7N7aAApeU)9{ ze#$QJe#$QJ0A&~WaAgv_;5*IU zW^XJ4?~7gFKPtPx_m~Ho2kQ5l2bn{aUEo9X`R1YKX#GJm-yEkutgHh+ZXRQvi(TOJ z&FNSHo?%|0ziG}f=jd;nbIrN>Kg@aNJpG^MpUsE#f0+xg8vJjp2CvsY$7=9LMpyGw zOEbD*DcCa(v^rQFjp0_Rm2Ql*0#?8{)XKK9jnP()m1B&Nwu6m)X*<{$YxS~v8HY=& z!NxdixHa53!pgVujq%o4>u}>pYnC<3ILf-oy2&`&y4AYX2w8VpcN!C|Io2HG7;COI z*O-JY;KjzV*0a`MjpMABt>wlk)=KLQqrh5aRTxvO8mq>bYW>T4*Z8&dzV*IwrnMeR z!G+d`)+feql)d0{txeV@<2+llHRF8SwjE=-on$8&Gq4EU*(kER+Fgx{odcW$j7yw@ zoP&%@ong)}<1(!MerjCqeCd2?{NCAw&EH~W^Y=>Udu;w*?Hbtpz0vJ}&EMOV&ELD+ zT(_%nx7)+*ZOp;u?@;4@cep#&cnq7r#~FWdPsH-?i&*|GG+uFki{;;PEdSnuCEwez z{QJ2(AIra6l;vOFecFB6)THHKQ0aygS&OE@o9*e+(yj#6n%^}_$ z*aaTy&A~44!OAZ1NM#pz6n23hGY?gEfya4Eyd~y%tOGx19;vJYhm>{TNy<9#$zFw5 zVHS8bUXA%HZ=?6AImP?Z`^r2mA&?L-rzY%|Fu?qE;_$@b<{8Rr@R`bLaG|yk3k4zU z6NIo%5MoU;#5!IG+fgB`5`?fx5MqQ0@q93ZHG+^h9-IJ91Sf%$!6{%eC;-0#Q^2W! z|2f1eWeA&;AuLjcutyof8f8eF2@1hk;5XoG@LO;WI2W7;&Ii-M1z-j!0vCecfs4S! z;8JikxCUGct^>2c^+3MCAV? z2w^!ggjIkLRwF}L1PBS02y2lcM&*#;-y{Y9C5e^Dkl;N`EJTK|4;d2tn;{hJz8{U@#K=4qOB-2A2T(kZqFWNfWGA$`w*6oh_C8Hd{ZCdi~7bZ{qp@qxzZp1 zIXc&ndZ)4BN`LtM#@OfS(Km49b9LT(jI8vJk?+pMzKO%P_92~Xitp@0d}SZv8~c#{ zPw+4BE_e^T57vQygY{qo_z(CHd;~rQpMZ_vQ}7x19BcyLfN#NfU^DnH_#XTKwx}l! zW3bp5GA;!4KVOcA_-;JJSK}eR84qC%DTFPgkU=|R2PuRVq>ynNPbWN(0Fpr;urJ`c zx&^Q1BD`QErqZQ6mm${IRua{7J=lGk;&Ku3h}*hh_8)9 z4(-5~#v#5l4mn?euK{hqH^m{oC=Ox$C&bspA-*LJx!k*aM;ziS;t<~uhxmdx#P`D? zz8(&_ok0#De{B4Ou<#Rd^S~aU8|V&t1KI(bJ|Qgngm^+TnI|-p-AVY51-}AQz^Neo z)JDAn8}rm=j9W<9S>QL|Y`{|-zV!{c_klly`@wv`b&Y+V5Y~A@*yahj)R%8|LwvCt z;(Of?U+adj#}nd9-H=B=@nvp^r$LkXHa8^S^y2H>5H=D+e2p8z0#C@h0$d5M0@o0a z`;4z}Lwti9!tPGUqwjfwG#RUiA-<#y@f~f*`v`mtJ^?a++L-TUL)g~|@x?4}@vj}k zd?o&K+dp0qmou9*w{@xH8FkE-P&qc{sCDl-mAp~0joU?)JOkg6Th)C#GBq)#a#E}5 zOg1@SIfxm7wZ{3hqSy;$=d6UimXmyWh zc$2_Z1oAx2@8vIF-9KK`XdPsjzp;vUFxs$9u9_7aD_;A>hPTJ>udAtwJ5j z(Nxw*GI@2I=ZEI5&9;sK{?auK?wx2!{6%ZtuJf0zdCz}&&4%c4bkDgPqkg%MJtr#n z%hr4q_1Ev$Y>xWu-)BmRV6yn#R`S{_GHCtn2 z#A`np-tsk9xn9MQZPKOkV}ETW@8N|v{NM7mTx6@}x#@d92VYMkDp|-w^J!^SumU3^b?WXqc)%H|% zT)XC!hUdwnS~FJWe^kwU64}yg`;x|x=%LzosU6rv)*wRtfxoJDBy-c*^^&kpT{}(c z&^T7OPyBz^mWwf~r#B6aCs}<_JZbCYrTrRjlMzt=sJQX<5taYs_jmu~OSpXn98(>C zE^lY5a-3`DsXOGf*y~J=Z$EZ;3KZ zt-iXwXKw#_aoTUp>RYxe-MrNc{dZPZioDfpWjboE&0GBr^T1to>E^ATTj!5>lx|+S z`hJnVy5Twk={1QP)yMKy*Xsm%tCy&FomMYZ`!TCul>6|!uOv!eP;(F07~FORWqb#` z84j&?RUBD|RyI{P;`p;G)O_1Y^wxtTZB`d?azuww?N=v_cvi2c>~-~T?!H0CiZkbv zM8fJVvFi;=x9d`#hptkm^VR2Ad$aT_9YX3#rK{B#8LhvHM|$<~x5Zbk%h>)yE#3bMdc&tOWp%r6K=0_%lpLEV{27KLcM-Y%00_Z@e3-}`73J6v4RnI zQ|Gk6L1XV7nSjc_ta3#(F~2)9$pN?~TH9nDT8ksYWgWK8E>|2v>d}qM5KTj_N3^!7 zHk(^My0v&JJX9Y`wJYscFKL_4pHj}76Xmo0m&=P4FX~tRfPYr`IKQxbkxaoaEUXdrJi zo~!#LQrFmct@|r7eY^7P#_{~)D!WKoy!DI5ad*VAzpDKCmeTR_%a^qn9;Xh?#gC4$ z;qZ#gXgt4B`}ymtM@7SS^uF2ky&auVgY;^e+%wH_{syP*l7oh8L+!~j_MUD1{MsuU z#;KhZd#u)DB5O9W^0xTnt!+0~Hh*3DYw^=8Ull9g8U57K{M4`xVcjaWJgmtyYCjp; za2+?g=GE}LU-nURy1*~F*72|H@~YT6D?P8U{Owqo^!rOI#y6HVKK5sa*Zce!w&Tw( zf3I=8*yD!RZ*0h}_cvIt+y&YDl{IxbNo;oU#*kS3T*j4pc)YfYcW!Lj>Rj|7 zio>$q?zXf*{O1#_-H(V(#s9XZ32*Q>){Kjd)A*c*VU3RDdUD*Yx$?w~+$NzvqkKb~ zBJRXU4co6q@t*kEwG8hcSmWP;2sHiZUq#Blw(6nSFh6qaN7D4C zR7{k0kIG}?Ht@&HPf{-1uR-|F<)CLJnnYw9QuAKa`-|B(>Zk3km%p~Uf73K#-oa}c zSSRtnsaaHau&thEcy9diY-{4btoYHfqw$>F;CVY$_RA^?{Gk=I{D~D)TWf*Fv* z6B$ur&#dVqkD~dKgvZU{S?oO=BRBsl(QHD(aqLY>mzz(3RFU*?z6XX(_yMyT*&F>Jf(n#OhM<&Xcm1 z=7i?X*)4B>3v8>770#*R=Jx0KOE|N>{_ShO`ZnNs2k&LHyvb)G{>wFW?`1?|w`G3f z+-IWk8|<5#pW0BK2I=gAgJ$c{V%{|k-pPp9el4CJ6WEq9FD5!;STo;0#YSqYU#%Op zHS&&$x2^S!m#e?3;*NOoCbDL(!zS@s5ctb0mPdnS+2KVs{(BX3qB6MFebUdjnincr zdWGT?^4&ec9#^tvoVs&Vc*Q@PMC($qI2=^*tlY=)HuZj;EKjkDA?NiM!oH@itb>16 z#mc534J9%yzqDpADQ$hOtlGvkfC}1!bZI}iZ?Gv29H;&bB604DiaM^>FsS0g@NsnC zFtoX&=Ihu(I-wnS|20>zT`F&(uJOtdQ~&1MFW!k^nO|8^_dG*MdRFjEqvEsX&!FFn zE4jZS=X1^Pl1-H>eojs55I8V9 zS6Xk6`W#~4m#TZeyre!{h|0LnuVU*E8@nZc>*Z=^Ked!;By3ySFPfaR3*axU9K?5( zo9n*8oL!k34VQcQ&T8X&`Lip7Dy&$#P2Y=&zUL}K>$l4OQGZ=o2UPY5%gBFdG?YB$ z`{jHS8l9i&Lz?Th_tvQQ1q%>gU4BnXz$N_lN5s+brB}P3ztyjW(|vRSs)% zZgYahb}D|Bc0QV`Ts!4$vGdVV(rx@st)(Qwb}K5eg1P3a@HfI-YqKk-#GWJH3vayM zK%8kZPIK40I;ZjX8eJ-{j;7n-Jf)YD|IlbC>2kNK@2+EH^`Q-YvJP9fR8DUw4Ig2P zBcdyjUK!6hS5{smL)v{mQHOAvwAseWTjJ%}MAnA;$)8*MfP-SW~)EH=D3#QY+-lK4v zweRejt=n{7)UME&%B6MZomuB=-V=@8;CoG?N7IXaPEjBCMQq{4-Ly%SOX3E^3fpi^ z#zt?qzqRLm(K^JoS!`zU+E2=^tbDO9TC`21Al6?OA>wH%ta02p$Bng!!*mzA4M;zgek>Dth_a zomp=G-<8bL(XD-UbJW?`%?YQ{w)enWvYQhvP4oLTYF#cG9?OZNvX=I-I!a6N+7=dT zH>deC>NsMYC_m?G{pVcvI9*XM+EqGGeU6QMHR+86;lsG^qO2JZH|}m>yIUakx^8^$ zs`Z>UaL;&|N8fFT4QrEbQ5<_lGyZt`a5GVNmAG-+ZBM>_>6#J#@-?F+*NwKLziQ3k zXkSJ5+m#3F0IhH2AG3a;>=NWNXGgwmYw3JhhnB))!~BwF#(>yJZSaTNtPK%mlo&fy zEw|^IM&FRNG5y`qXn|U;miBCGj&9Fc;d9m;=YQJJhK|2(%{j5@aWCE0;CStt(_)XK zey*n(QGa{)jq?&+)-*eM$+$Alc=<-_5I1b&n&N2rNaK>IKi#caig=&*R&2PHf(> ze!nFAeS)+-*?5!bM#CG2ZSQfsy2qy3xcy?oqW;#>h{lWE%X!?dUc|MomN~z*IIV@pmQDKk%ll^6E@b}gm{gWJX-k<})^&GwDR)i!{-P=~no~>9~Tx}{}*HB(yDfH^p;v~ zJHrN4joi)@cG>BSVcXSyb^qh@6Zv1EmKyG-a6h#7e}!7AS8HMZM^!ibG5=4|TKruV z-jZ$;TZWz6Z&h<*^V;?Ps{6t=<*M%6>3I)n+cV#tUXC9lgVx?*`Z3D1JI^>R(A2JN zoJcK-V#kZNaLN`F+X;a`#WL-LYVNWmcZ6QP%L@B5&*+!V>3`1MxJgx$~rU`wzK4o>lDpLwqMjAZ{OT` z@!~cw0oc=0{9~!m!6F)R8js13_rFgr;BJCCP7H7RJ$a;H{KaM@i-M#aZ zZ-L$ad$&`%s6FZ!`nv;nN(=a3R(EMR;p!gY@aVqf*gu)jy{gr>!P{G& z_;%EOJiBh&Tc6$M|J+)jdRV(l>{MUf?sJ=*SbakiQ4>K;v+poAiP}i8Gyg}bQC7PT z+tdQpW3eGWp-oYDN7@A~P(5iEWU@PpCM{5XQWH@-1kJ5A#3m)z0NeBKLTnnl{oB_9 z@%pK;On!S4Ylk(OV{`A}#-1Nt=Z(tbzTVzOG^O_KqiMG9?6#e?t?%4!Z<+qz-4A`M zr?jmn;>D_-7EikKWYuTysI=Si?O43L{3+Gb8_WH(sxN9R`=uOj@BQ>kX@^}?kLsDb zCZC46`0LiTvL@3o{{Q=8D%DpJvJYbv0G7CG!ibzwl>Ne;X@nt3TelVmw(})BE|vtKPC* zWgS!Fs`JOx4A`c0qW0uMHF|^dqUkjdMh;raV@yqQ{VA>3iHg3P6spNn=R}X?!3^nO zr)5b^wupvHQOp+oACwqTL$CY>rY&D0H?Gf(;s1qem{{aUrWqSQ+I}*)rEhLp2>ZFs z3(;pKkrw&Uce|$jn){ZlW<+cQ_}|uyjy;zC=KhnttcHBlL*o-tE|?wwYWrdz<3NiQHBlgua{?=kx|5;_R~j`)rW$ z&-$R@|9$?fs}fUq|NWSQo#6FfZaVh!U#ZLDXI1lBeECmXs%%}hbcYYCS@qMdLDSMT zUUO+0qxO!bvL8?4KUDMfkC*%IoEx{m4w*L_C%OZUcg#AhwC`K<-VP|vPb!J_{-4^e zszh7u8||v{{5bj4n*PSxgob(7rig^v%sN@m+8pU8o?4ys->dD}R{gOq4e3d*?MoU% z8pyiT4s0T85TX9SUsXG@{%G5M?JD!OXA}R7-Roj)vY8#f@#>{N<~-2S_0v*Ved&KZ zpZ`%S|CNwYJ6@G8qjutsDkbYfC&W!FqqZQfyd_~q?Rj;<8MRYm{AbnqoAbzX6KgLN z8MT)+7bPsOy|Q6=?X1{ixn>Z%ZV(&Rnm?YN@Ke8LTCP*=%`JyVLw{O&eY8}+tbOgA zpF(})bMNT0Z~5O&a*KQ2m)71FjTep;-)${U-0-FG(~{D*>U)0Q+J~A?rM`|W#c3_f zkGxsuzt>vK9TeVk2c@%Ue?d)eJ{?giK+fz^Z-`MRr@BiyL z{-WCD@#+poSs72h<7DCg0&19EJXw8=9r4_vKD8go7pkrKk#gRt+4`GXNxow%;jIMw zpVt1faTI?;?YhP?b(~%MVO)6&LjT>`&*BDmsNEDR_rI?FKK5Ap{dd>0Ca~6SEZ=<` zZUO&|wMo0%V%u84|IgagZ98Lk>>r~A{P))ecXhry;(OS-cTcj{_G)+en>p8CzP5k6 z)BK5?_)Dn6PoycgsTlqz+Z6qm9oZ9lu@EWZKwKeGaXrxuH%0F*T`dteSqLTRjjM(I z+Q0F46e%iH!%Y&&xUMj9lf{v^LYT!uB#42yrZ8|l(L+fF;OZirazIIBUam;PP0{{^ z+d)`Lo~7Jxx_@nWx-;;wFe8xJg3h;)<@g$s!%M zgUTyG48xxgsoky0KUBp@Qm(9Birxb^L3G6JAY@GwM1pcjHS+*2iwV_^%-x3+P zrb;tag(eHxFDcr36)MXaP@zJ|yfRfUpjqHgQnkobEt913N{-|uOPeCfa2?fpmb&)1 zrjWR*2rxmC)o#Pw7wC8@lU#UV(9y1I1Lv&rfjPF8VJ^gUH*AKYYh z{aC8yDIF3wToN+ zUu3HCCyTyFQZ#vGb`Z(RzX&%$wFb4s@2R}J@HL>zYc*N5yb!W|Je8xX`Y1))fW%a1 zrRaZEF^|Sg(ci%Bpw4ntNrdQxL`2#|)+kww#7zvhgFYXBiZ1Wa#K_evd%;%qP0@># zT$NT_fS*{nu8L`^(z+2>-a(EEO%a3fCr48F6Mikyda@)b+Cu3HS!P$2GgxMr;$dSZSqeQaU4}W5~P4pM=cT{bXAO@+Jax6*~;XB&I z@2V0eM|!yj5}`s})k-Padq_N$TCy0ULM>cJ^_Q!zpTuwp^?x8w(ci}Hp!&CCq&?qJ z^4ZG$J8nYcIyo4BM^(qf@YSX7t3qXKcTnd#s#OzI*&LNis%oEv$jC28r(|&et{1s_ z|A=2i?k`ziTbQ`IYO4g*J_#xXSKT9?%3V_}kRbXIvy-|zQuMj_6GRW(6#WcbSG7q; zRUTK!d^@UiI;y@-(eIQowGVJRsFHZ$J5-;BBtaacdtFx9nfMXqpp?R8XnB?=RNiZ1UpUHOHO za}#48uBY0{QazHQFGk`5@<=Y{W+Ve;l zjg%|rl%z;$|E454xGDOZ%AbPUAv`i@)%ZO%t9mNm6itrL$&qUp&?Co=Q1Izk~QG z`ZDF7jq9o2Nzo5fk~vBuM?ayiEBYV5C1fjEsyr$BQ2dFim2`0zJ$*NE|14g^zfycG zUeFS>D)FvXqaUk1svpm3Sc1ntqLbfPS5Ry?(5IqkfZqg8m2nPlV0W z-_p<2SL?O>VG%J8B;kv!}y)C zw{fv?sWI5N+*o1^HL8ri8y6V=Fg7FIV!FoNW`db)JYc4nDaIpaZ?nJgs5!tKYAiB` zo5PKz=16m#QDz=#PB2!O6U_^Z*UTcb$oSZtYtA)3G3S}{jE&}>&4-Ln%?0LD#@FW4 z=6b_7H(2RrKP%J9HVdpAtBZNMwYRmmInC;4?Ps204YUTDXIX=-!RBwQq1FiVZ0itf zlzE<&ZyjzrwM=tJGR#&bAg?PnmP9XRW`QbFIHwe>3m5 zUbdE-^R1Ouh54XWW7U{XSpTx#H5Xd%Tko4sTI;P1=3?tZ>l5>7>oaS!`JDBGZJEn$ z*G@K9+bMP@v&IhCx#n8Cr@gQFuHDZbV18g9V2?CEvJbUKo8Q<++DDq-*&#b*ZnlrH zk1_viA8TJ`esBNYzRK!mUu(~@_O=(zLb>m*sdojj+9)!#Y5Ilwx=ImkK48srRf z4z&(+j(1M54t5Hh0_zaxW9MUQl(W&5Yq(>rwEVFcbWBryWD-<7VaBvg>AcQ+-kdn`-!{JPC@MycB-d)$#$BT z;sxwHFUQNVdwO|Zp1qI9&+g?7@&?(xy^-EXyN@@<8)NV59p)Wo_w^=vlk9%p$==EK z0I$F+u=n@Q_0F>gdegn>_8{+a?;86+?>eu<9_jtT`-7eD-Rj+HALiZe-EEKc{^-rI zkM!>G=GjMi^S$|Y$a~Ox%%12y;VraJ^p|j(iT@DoLSbsZ(=Nw9Q~N#sVvSO1H)uDC46Q^f!GDwXN0Fw@ z(dOX)lQy4p9@HMjU7)=xJnc1YrO4IZ&?-bXtx~HM>DpRtgGkms(Kh0Ku6>UCh4#J3 z)_%~lg`;=YI}1b4(YxXAt{)aVaNKeFR3yLFe=YWxqZ0n>^y@?)eU?6p z(ChW<@!z1|g#QovA8>EcZ$)yO{wHG2(;q6-ZvyUlTd{>-tLKzoEZ@ z3LzHU2L4V5EKz_kCl%=xTgme209qv6(nqjQ`=^YN8LBo|zytGto>G z`=6aD}eqeqe zjxslx9}?#y^CRMaY0_rP39&-zcIffbhF8|WHHnT+tMse z=$397qN`A;3}49!4Rw@D~b3WtPY}+m27pypJJtm-c~286QQXV zoZL#Y((tES=_1L>url!ntRV6%t26O)tQ?VGxxuIM4>TKq$; zVYtJs;bMq2!Wtp+nRgEnhg+kpQDV3?#u|e>-^v%KS%+DNiDRt8t;3Owv&Iqg2kYB5^``Zf=xeRARv}q!twvIARfwKaW5AEb zAofIKSTFLd53CPJVS}}S_#avyijmew)<@zH>tpL<{GV8#;Q!3}l3IUdeND_w)+X}$ z#`*^Tx7K&aH(Q(W|6py!@7w5~wq|Q0%hqijzhRpql}pCP@7kUiY$w=>;s86zP9jbR zyMs8)PPUUp544jM{GIGh#0l5|;sou^qQ9ME=ZG3ofUlk<%*oNt|PMUwNK^PRAr&CX`z-#g!nJ)Iw%Eh5kPpYuO4z}f0-75h5A zmwDgKa5L~{xY3}~+K+(}Xz#T3;cZ54aEG)nIi800kDPNJum=B0_TUWS)}i+(JQkeadRC^ci^4EKhM9Q0#!CvTK@DCeRd zBcJF^B+X;IW5l7}BySQDbY()(m2uDY&J~t-o=2NYjTy=1-b~_L;a!D$wRbh;yvDnR zT(0x36Y1V84~@&a!Mg$XM(;+>E%DF>y_-GsE~#6KT<h2>qA$ zFXSJ2vM)aMzM#xsdSBxI%G*Rq(B_3EwRw>zwRw>xwRy3R)aJ$hQkxep+WZ8Ok$6<% zQE1mXTB|OGG0_f3vv-tcpMho{5?#bZekMBpG59BmQ}9m~1tL@Y3XR`E-=BqhJzD-i zwEVlU19LaOfOt$i&MMUt{5pt*{0yb{=L)Iu=PF%4S84gVO3Tkx`hBj_>|Hebpa^PN zS~t;E>#lW27axgE@1fI=Mvot(jS;74`C1YF3(@i&rRDcV%b$tl_vre2DqTOIbp6AX zt{*_xpN$6pM>PHnrSYeu@jom&pzALXuJ(xbh?uNBs=b7NnYKdgr@aaXa1{sWr8q!O zI6xKbq6Q|A0~7dMBq{B`yVCx3rTy=V_TPoJ>`H6y3kT=}ALy(1!`%-)VCbWCu3foG zA@*bq;$(5EeyV=D$W(klldBY>2dfk};+E(oq;r#glQ>NAfy3Yfw;;b2R-nTQZWDXx zx9hhfxdVo<2Ml38@(17v20Y;*BoD(A_EbDUS3JQ}JfV-`30@3O@Dxwz15a2cJjD?D zz!2a+`fGLEz*F3ykKzU%+@OZgTD?|eD^`%LSb+{Jco)ffxWS%v++YL#&tV9IA`C%t z108O_ilCvv5cYr}*hn115qq-k0*O{^L1(ok4S718!Gkkoq3dTG+2U-YvwK{*VzOor&;=9`J`_NM{l(BEdM`IGz+vFit=s8HKJGMS@}!DT+~Kz$h*u$4lWA zd%`O&$3N4UDfTsf54*5o7k5(5yWkinDUNX#9AgoZCyghOEUsf3i7`wgQ8A6Z71P*T zF^#=o8Xu7U2ID^>(fH8#kaB)xe1~6hj~Xu#STj>(I>nMogoC^{Ob-_!X;G6U7L{H+sQ0m?_QU;2Rwk-^fsW;|#?&PE&j% zMKKLc@rurhS9DgqqO;-^ofWUJ%!TGcacK;@(BKu%z!~Zpg{BzAxr$MAQj8)IF0nzR znE!!GBq%O1NO6h1;1ZuvqtDIHVG&=LUx;k;OY=)4U%@M~;T7ND{}y&(!!B6KF#l`* z7ytL>_xOJ>f55-R+(MfFGyjKVtGN}44-e554@powL{mIuFU3O!DIQ`d9-_lT9QcE4 z@hsO$uy{IQCBjAyRBXgkY(#^NbV8B}1JM)%(OKKeL=vz9NP_T?ec>TloSSWBBav(* zQL&NJ6&o3;*vL7sk$&P#t3RCNNVy7#ek_^E`HGnwp_s{7#Z2~5%w&vWCPynya)II` zW2~`olCg@7j8SalXvIc~6dU=CTs1^rXVnmwRYP3KMp6|UNmFbjRk4vg#YR#U8%a}a zB-N5^B+rsOBwz86V-*j{S3Klc#Y09Z9&)JSA)^!zIaKkGQHqBgs(8o{>mlnQ@?|9w zmz79d$wMx*BoEogl02lp;vuQs~X3}ii=q_5&6sfv>vq&Ue$#Yv7)oMfWnB*!RDlBPIGs^TPh zij$-&PLifLNvh%`X^NAiDo&E8I7zDFBx!IGp1UeeGFowxLlh^;Q=H@k#Yu)JPI7|c zB%>84IYe=ip^B62qd3WLVI%v%ReIUI#00xHoa6w-Nv0`I5`vBN7n9&2`-wt(0BmFe zJY*1YBm)U426CQaAk!5C`JFw~9x5(UJmh4>LoQZ4ix`%?Q-{F0w!DQ1$Tm`Rr6BvV<_ zO@N0aIy{SDWw#st?oJQVUB|poCt@}6o=`d zILsc3!{~~`c#6aHfy0~uFN(64LRgSwFFNeyBF?=S_L893OG*rTF%)}ARP1GM#a{MO z>?KjLmrTW848>kN*vq}5hdUn*V=E5hC=Qbw!(nn_*o&svi>}y<=Dq}b(G`2?9K&8Z zH(@UaDfZG&v6sGzz4TM;C10_ZRK;HUvQGaAHI?k;FvVWF!CrI`MCo8f$z$nA1Qe4= zhsii18zz&0hMfqXNl<(y1wO;GZpCEw_fox7FZ-?Vz`a2xJ?4wrdV`<+gw4(uY%$9 zRt%?$VmKWX!^!ck_gLvx45teWrv&c7a|22uxlJ#g8{95NC~nh5ahnc`+axJglM};g zauln{;dui56K=DBGC$%yN}D_ex5-xAMvLJ#iHh53irXY8Zjdr_Q(MqiHh{+{+B+WTgm%S%tu zgLnH@m+Ncw z_snkkhv>SSt((bMS9GsBU_STkrGjZQknIMX=KxX36rW*N7jRo-VT zFcull8ZR2J8Rck@@1ZAtVQe-TLRfX)=hRcro?)JJx^$X=Ioo;-8?)0hW zm}i~GfhjJKWwS3RJZGAjbioDv_cK#*2be+J{mm}81I-?|2bjHZ2bukG4>Ski4mO8f zP;}Y_=BNvb&bz=IJ42>6;gVlZKNszCG)oL4g>mR1lk&{ljy!67p1EP-QAg%kLsb82 zIqM~n5L)o@@Ug`Tj6C)jx#Zrx0j|nEWtm(+Mvqz^KDMTY_sNe&?A-rK3+y~DdUGTb>YvcD_5XSGYo-OR{- zLS%ntq#luUC`Y(n8^Yn%m63cS`=qUr@;w{gvzD*Q8{J#agyVM@&EBOP1HyaruE>5i z@sXR)hT|pnitJfUk>yUlB(nc1ytgh3?>Rr5&#>@u;GOW^jl@gjsgOM1jg&Kyl^1#3 zk(DdCPZ=5BXUXz#9PM8Vrl*dX%hY~qWIsK;cOvbP5Gkio9G0{46^=h8ytk)Cg~=w(xitL%ws8&iYSKWQ!cugz{MP z=^^$KJw-3kN44LWjw?F8-SM*&JtZ@xSH~VHBU0Y!v?XnP$3ZC*Q#Pfhr%mfPHf36; zgw#H1#U0I#C#77{@$8Q29cQN8+$k@0bjMpd-k)-Rr$MQwq|HfrrqlS;8J$i`ElFFF z`as&M^rJg2>2y)*^JyQZpPl|F;!UAa!GUPWl7sTT)hbTH2{R&Fr)x zEhl|Y8m0Gr$|ie`w~eTktOsxFQIiDfl0;pS1uNdXA(bl9s8>*AQLoO_ESI`=hR18qoC%i`IJ(6UdWHMQ(a@wRe12rX*^EsX2T zMZ$G2kF&W8vetp`0b>H^YFY9OQpl=d&$@8dXMi=#td;6m&-xgNo}I&L)(!d%_lHe#^nBg?gMfI#H zIkU6g%bAVuwH(%=WKVXUMapZbfqVKF z?)03S_mj2C5pLG9tYI_DzSCal-0f5&-GX$2RbU@&Ut#~zUhK?vYLI@9^a$%@`!LwtE%qbMoz5E7 zPI_=qkUkD>p)jSlCOgEB9Z}0 zq6P1u?b2181CeOCgGhOKBu-e;Lui5TBa(x{XJs+ZXO)M4gvlJb%Cm#ag8}Bc;5a24 z9Ep9Iu(R%u{sM2Rusx{pK#{0l&wB?q7&jUv#!be}#vjn#Ze^uV&*E)UR=sD7KZ-fx zPhzgPN6ZuViu>Hd37a93^u_wKiHEbsx6`Wc>fh@>@V?ajl;q~X*3NnRFrH>k528@V`w=Tg zbh78$584mg3+zYj$L%NVQkede_A~Zh?C0!f?WgUh>?J}EY$Dzbc8Ptn{RjJ2`*!;d z`!4%#d$xU#J&#`$kCabSVv$O5aZAmkmw|Q6%_=L59Po2+L1^5E?|CRH# zd`1cDhw(c;uMqst+3NBlpR2h#{Qn30&-PO1zYbTkmJTvNCJ2Ba$O7C*0B0? z1b5Yu+$|?tr&yD@XAa{I8_%6{5%<~U+&{N)x82EI^oaeKy^wq9PVT(Bxue!NRZg|M zuYzAN+k7lO5gWy);xqBN_(FUsz7k)HP2wBzt@uuC7XKCBiyy=m@jtOu_zb$5rn8i8 z@_K~LBV1SW+;Q$wQo4b+dAjR8^gZ>x^u1v>`|xgLZ{CUAS3Z+~>+Giw(D&B|@_ytX z{Xm${LHZDVs6I>|u8+_U)<^1x=-u5p?w{Pb)Q^F(e$#U6{e*RCaMZQIQC@?iJsKQ! zYjD)P!BLL}M|(CnlCyc;rpcMOZqt~Z>o;wmdOz)Prr3wKgPu1kj8}|jjF*k4jpvNF zjK9FQ-Y{0fyP_DS~1_9^ycyTJaHJ;grNKFvPeo@)Qvo@Sq6pJ^A`XW758 z&$fSSpJShEpJ$)XTG$NxLi=~DgI&t{*GyKwitQ`ytL&@oYwTI9f?Y@238cNZT+w4a zuQ%&>`|?f!D|qrf0{Iq!T)SiCZZIo$Ls+RB#tPjCR^~?9hp;9$#?I$GfpPW`tjtZY zk78wRBI|OKSdWwIa(d^xMBnYb(wZL?44!>=`9ciXnK{TgFg*8lvBki|39J=AY%SoO zl}CAF<#ASjU$kDbmdW*B>lJH-^{Vxn^}4mr`mgmpE3sRw|5;n*S}ZHEx?F)}{WXa- z#yy;Fa*fg1)7i_}+v(}-4)Na8jTka9Utm;D*3ifpY@W0~ZD^3Cs*!8Mqe7 z&4JqjcLnAI<^}E#JS4Opx)|PSI<2GsuusHBcU}@lmz_P%Kz{Rj|?el-2pqtuL%E<@!9U^WVr-d26$s z*wVFNe`$YZ^8{#**U0}U>u4)vO|*`&CRxW?$63c)CoslOwSH}#X%$-2tTT94@*AF! zoXgXa-&*JKoa9&56zf#$H0yM0x^)3uvdFs7`ki%=b+L7cb*Xikb-6XuDz>iRS;|$` z)z&rgX$sF$u9weHc!KhKo}b)o{lU6LXbg;_a(e`p1#^NY1m6j6%$X8cku@RseP=hY zGI&>3L2ydex4EYTRt4`5cFS6rJs|6xoEL)&gFUm{VBhSqS(&+a1S*3AgJ)*-$QqDy z3vK>3TI9{a^Rk}}4hb&JdNJ@$@S^O5+&2U3f-8a}(XYE?t{ohzIxovWOy zook$Ho$H)g&h^d>&W%oqbCYwk^9Sb^=T_%7?%O-+X1_l=bDTdpbNSul%yaH_?sNX^ z%y%AWbacP7z@oOhk~opp_l-g7oM|8f59tam<8z7L&`(5kfV=#^d3ABy!Szp789tv$W3lA$v3? z9pVIgaQ9sjJf%KXkld2c9fRSO8JV9J90p)!!GSUHdZHq2LY5aJLvIhj3WG^?GCeC( zDp7&EV)8Y)^Z5nNLw+T4Sp$=CLH?+HU`Cy69plEjz$9eTA~LNT>?@a5!N9YDXZMh; z#;XRo<+D=eL#k4>-8|eb#uqT$PTlQj7A55M5*2yeJO!dno zz07ryoUOp5@GtX4q?L8j5sbV1G8Z9T7Ll5{Bjhg+Mw~lBXnV*jKxyi^aNXRVxjpx| zwk|hKm7MogGdD;dF;yRDmhZu}l|xU+eMD+xmh)RqPGgbOOP4ZxEI~TBf%JjQ2Qn8T zl^RntuAVp|krHvfCijta^~}?<9!EL=Dg7FeYFVrrX2R1m$0w%NN$t$>(O(8_m$97K zvUYan!00dIKIDt*r(1TFXdfCh-#%AzV^!DMVlNwZ%SRh zS{`NY{IM`HUdVVs`iZStD&wKN{)E7J!@h{z$hbM<=DZ*wq%ZdoxtUR-e#MlfUmX zo=fL0P2Vbg5xJATCFaNVpB}ybo%D4vzX^m);T+jUPWo#xzafN-kAzU4?B&ABULO8+ z?N*nMo<6gyOX#D*NS_(@bB=65BYkT6)GnV9b_-=wzDS6bJ|*(w9!y({G+JsaJwNgr zNr=1#>O=aZhkr2lv_&x?M%otjwxX8yrqHFo?i?d+UD~=XtQD|cROgSB!A|>U-H-b* zZBk5%cG`<|KYBS$j)u{4*l7>c{T31ydlxuqvtoXi5z2{mXF6$T#r&oUE%n0&q2tr) zf5#D8UKgr$nI;M{Z^*nQ^RCRfnfGTd$Xpogmbrwx^F=hfH__|X1}6r4q1mlRuloY6 zZcA`LKo1THxM+3h=yZ8#bbZk01~aCNMwdGpP41N7CBbWhw*}_~9}PYeTozmvd^@-S z-R?~Ex*35>g8xU}m%!I_ynjD4C->ZwlPe;&Aj{oyX)LuhR8=jp)K*p0lA@}LlG^v8 zYKbi()~c$;QhSw%2qJ=58cM1tL4+z1k=m-3d*1Jyn-jGC^|$^1@8|P=-kf}&ne&`^ z=FB`Zch1anX6CrdSpp&h`jnlBzTGVJ>=vV6w-Wua&FGEo3b-9GD4;R=7A?yjLXYeu z`Ud68W|yl}E*ww%JoLvNpf@I3WWRti7`uzH&c460bNsixeDCo8cBJaD@BQ6)wA6_)>pItQaUtKizf7<^k>T@WsW9+Vvx$)i{b`_5u=g*@~ zF}}C9rhLV$D2l4cn16s7(wKRGSqDl9#a}6jnFpnm(%z)J?#-04N&sd`W0o}LNC)Em zdrPI2(pqVwBztou$XgQg5EQFo^Y&8gibDxi!jwvwmGFX6+1rE}300M9-m+3%sUhYi zh*=4klTc3ySL!M+D|Ij@;T6nIXn^?$4V6Yp6U<16P?{>u#B2o2MR-GLr@X1OSKh*$ zfX@nZ_Jx>XjrrAYE1i|DN;f4E^Ax_sd--=TN1>LtK2ioLLzI!qaLiU1p^WnO!F+`=%2;oUGG3X0SqmR46TOQm zlax=DPrQAFMGX3!tV~y?DASZ^WvVhunW4=53+>Lv%!WBitP+R04fB-w$^tR30rL>j zF$>`*<&1Jx$x_ZKnV4g63^NZ-VBW!LtXxrkR<0_)D0h{I$^+$za$osPd8FJ^ephZO1(oSY}tBBmc{*FEm@;nZ-_GWl&Ddje=JQ;SxQk)%ToH( zQrXT$|H<>~ZyP>ID#1Ef#H6;@72Tk-6*B9zq&5Pfs8a&kI>i#OQl zkAV&ZmB1BiCJXN__*mP*@b-3|X#wUTBE@+7$1#WekyJ+bJ>jvwgN2*|&VYyMAncKF zStEquAxA=vgB};x5ieG~g`5hx03JdJ%Yxuy4X2>rihQ6xbw|2PA?YETLy|&LL)MWw zBsb)2NKQyv$gz+^A$Z3E$s<@1MVNi~gE+&iWHASsq))J_QJhPN1@C2H>u3T$W@zGl zCEnKKehH>T-KLcWi-ypY0=EDbj3<~S=Pk-#=fPG1>)#Mq&sSjFeNg8$Ftvj{a2V|A zt1uEjr1Kh^-hy>-G_33EFfPaH0>szU)DhOgaj>>;#5nz^&TC=n4BO#E*xtXzs9oe; zLE75Gwm1^D`862TA3>^78X>Blez69C^to>0T$IW*3J0I?WAY(tj{Y`5pRjV z{5^_aqpMw2mcx!-n2jVS4lDq?PSup2)J7&43@&j1o`W@!1pWr9?k8*h` zZ5?G@Xl-bXv|6mLkgSlqD6904=#Zr$y+cNX)DDRV2?%jvTAn2Yy;{6onEsdVA@KGA z?;yn6h%fOzVi)-bcMa|x92Gn&cvA4J;Dy1sr5suHd4e{o z{KvakZ(ASRWSgI@yv=E=W~*y!Y-?@nWb0|`ZyRPCZ;Q6Y*_PT?+cw*hZToGEfDWztJ~_?B5ZAKoo&5r18gH~6KykW^KHv)Ypmt1c4(-kwVt)HwG?tB>8p}S zUytmiul5r86gBc&v=rO$D}5Rw`CaAK(Ybgz`F!_lsgBwwJ#8T0xCHfpr!q z`ANwBh?UqX-gK2mObjy<=Fu0-k5ynVvPmost1zzf`%*FK9VtrsRGKYX7_=|ni&@R4 zOw$jjIag3icgWYV9*ARgcSqh8r4h+{;_hNax(|M((YT9v%kHbUqJCK4ai09KrsENn z)7Mjk>#HYT)_=JX_$9|tGkukHXRNROiMn9D!!PuXzOJFazM=t73(>+`+XMI|Svvup zfv!L#&>iRr^a3!7LWwYIU!XsLv7QwzyLAvS1Q-U405GDqq8+hfOlBPqOavwYlYyx~ zGyt2Lbrui;E`1xNrAfg~Uq z*af5jsX!XAA4mt#vRaP-8Ne~%IFJdP1Wo~GfU`grZ~@2$a)2wqRp1(s3*-U$Kml+Y zxC`6|9sm!4$AFudjQ|dK0WzQfDqsP80YAVWCu4l5Li4p>4Tsoh<>=n*D9s>iKv+ ziZ|LD@st#H?m2i?3d{0Q@x=T$>K^t*`g41zKQpZQGsCGrGeWU?X$0n*aO-%~eU8?6 z2IvgbcaC-pEiRsLOF@^Sc5}3!Ye3haMsu`pc)M;*KrNQ6$!NK;BGkGc?e}rG$IyzO zg?k2VIc%uV$2HVK(Ia`o)EPaJZl-Qn6W_xWi@6Raw2)baAxt71+RBHhk0wFUI$Kch zOoG}1Y{95sCP8gsXhp-}2es8lyN$k$plDS)*t*)#8liRVj#|~17>+XL*ucBeheULEHydlh?4dtG}2dxX6u z&KugB*<0J&+dJF4+k4}@tG%bak9~lBh<%iOJkE#N$Ji&@r`l)P80Hv5s^g3!+cCf~ z#4*Y--Z9xR!x8IP2<}wJEJvJUv16HIrDL6Avm?>53-ud)44tmf>6bcPrqh)={fc~^ zmvN*y4mplF(56B5l1>wJnyAwxL8VJ%d3qN^!f_VqLOX}P%rev0x{J4Ws9_i@mHLo%({R)*6UsR zFXVmx_1`XfVXtCk(Ho{Wu)<`zc{*0J97J!u26F3N*w;@g?Csx?g$^pC57ijE$aKVN z(P>x#x(xjZv42%I_N+2tg=8zNgY1B{jib@~IEfkLm$2T@{SW(kUqU;c9(7sALez-W z&}yP1#i+GUYwOcm>ZzSn_KzGar!$0oWV|~ zGmI*@8n~Ld+PXTqy1V+gqFf_fP&ZLI5VARoEMx|oVm^d=Y8knP#&s;`i7PYEgxzPbwTc~^I<53 z%AuA}|ImQY;8161SZMXo@X-39jYC_6whiqN+BLLiXrIslp+iDPg^mxM96BQutvbng ztGxj=bC2mTb~(wxug2f>yKKVvMC@B(#ptgF=BbKscu$USV)V%MF@y;=%C@Le?a+q% znz~|yiaww@7pwQV`Gz-sW9B@}V&>+XSOvq)`QBcjx3Ct5n+ve2pJQH2CD5)i=7C{; zOBiS*)|7F~bEyK_9s68x%y+2<+6!ySxcOJCo#B}IQWO5$-U{d)>9P?u8f`1RIY&d4fybS++>>a={SEe5PzhMUfZhnAuHym?kUI8759WglO z(7X!zk=y`u5O&1im`~GCjFFU|Krbj~Krbn0L35NW&@0M0&~>IhpzBQ^fbKC3Ld+Ug z?r=;7E)D+^%;4r`H+CrCiWhb(;ELq!2g6)EMeIo7B~Lc3 zk>A1!2rqexX|3GB)DONS`AbtzLJrjGEB$K1VzavM`8_$Oc% z-y*rK>23HYVm{wuS@ao97;`K)^}rr$cq(IVQ=#aNKt-GYR?p}K9Zq{3O zAH8+=*IW0Jdh2f0TX*r>5cUP*w^3+Wf%U!>o*LqL;iJdsix^i@as1lWBfQw(rYvg8 z9tzM$-{r(?6RM!E3Jo&l;rADedswhP4CabZJ^2adi=cLrUCx!!e^=jBVO>&ts6#$E#`xffKLhWj@GAw4`v$s1o@!v?_>9| zgF1aGgI4uHZ{MfB5Bl{!?R?sSzVGus=s+J~S!47nVP7Nc{m=vZJj})}ZU?YWLvifS z;AB-`PaBDy7baqVh0n2tob(ZEVU;^^V6QyiWPIyJeq0o zhZHUPA1#~zBuyMUb{MuEJ8V6Td2gzS9?KMJsapd3>w8*!h&?T~&;aa6v7KUJ3rV5* zu!S6;B`A}_v>aBD?`Z|>9~Wp1N+_Qau{Y;qI)IYuKU06Gm-UBv3dg7(9xn3Pj<=Gcv4LyI)ESqKC8|yoL`TO2=yrcL)tZO{X zS9yQ$eHC}+WCieO#0Urb$_Z`Kd|0Ng(+<@5>6C@NL{_j!>=KyIhKb!jSc>$!^oSkC zo^c&m23F|T;{m8G-Fdiau4yjs4BPWk-bJic=kJL%+Pt^COWw`jm-onf_y_V{IgR&~ z56OpkfBA@flz%86mrwJL#7<0nq`9`a4j+Zp%?HKD8cF07qAh8_7Bb${ME^bh)tKS8sw8tJvaSW*A+9rPfhpjB9r{%Jq* z=?phvJrq4kF#~VwUs#`vS$cmn6VI?uF@m+ULvZ)tzQIF+#|2LfjtyQCyfS!Ga8htu za7OT{;OyYs;Jd-@5GBMvBoLO`>LK-DvuzvFIiy#}09bG*hRg_=AF?cDO~@A5A=6>K zKl`6qGyiOB_}A==7+IFWc(M{kk@YclY=;qJZ+jF*i<2-$T!@k3I(q{8*N5zx=t*C- z7ocD56-itYZmAQk&2hPQ!TT6h=_F=mWc*3i`i+ zPM5PfdbttKw&>yZat=W6b|U(w^U+&fZnK7Y@L*qj?geHcjh8_t$8G0e~TIlW2$1d6B=PK`Vx~jSAx*EG$ zqlewo)!#MDHQp8NigPV>t#)k=3Ja?~t~Cc)wp2TMzQ&^p+B zl3{B(5|jz6PY$dtw}T$S?xP0#KWin2U!oVbhL)rMwT`|*Z)+pPqd)a6t-za`8MIPZmuMC2OLORJ*qGvI z4Q4SOqP4=>MC)L0I!YTbkMTHd)Zf8u(rxMA=+^el`c42__4f@4cC z(IsJz4r0N~#VW!sU6a*fjaetwh4o;)*=X3Qr?P1*n#HksY(87a7O^kca<-9eV~K1# zOJY0O9+t||*%9_VR%oAKKd_T5i(O#Z>^8f{G<2waxP=$vzF5mpt!UjsGu|94wIlgE z`~x1vM`MNdOgvj+`69laZ{Qm-Mo!?{#Ir?z3-S}sLXEi2Z}2>5HoW}mp7o>|)j&xc2MY<^!czJo5u@6oXc0zvI zeu}=3c*k|Ia5nSLrmK2;m?fW=FUwcu>+&t{18OO(e6OxfQ=`@C=tst=o6u9-sqRv* zsW;R-^`?3Y{lQ<=+v;5(6U-WcqMzquLqD%7-nn8l_)klC>ED!Y$)b`K<=W`ElJ%5p z?ElqrHA?k_UZS4zG)nW2W$7u&0ne1=Cxs;`R&f3I<=E(%_7%~6s?Gn@sJAr8{_MgQ5z@`gk^ull^=pO7{%JJ)13c5u1qelfltbCy8?O+8K zgWu0M*XJAH8v$mF*-baiV$6Qi468*UeQ1X50)9asnqk=heSsv5KRoUexES*zgxD#z z050NzCJ+y_fq2mOhOF6T#wcCCjxVXeEr12lOSq<+NQoFTi36*s&DaA@59e_o>gP`o zuQ(TXa~8^b+$Z|^6T~OZpWxkoRU_Ua-@`(mh{bbN#3Ou=E942E!85`kHq%42155`J zhd+;ByuZgx?mt{HILL*5?RmMUyr=o~M(!BeP7mo676in3M5o{BG()FHb$U#v-|O_aPEY7G zQ>T~>2HBH3#j1PwPwDiuPGQdm2exfcShYdV=`>5H=XH8Prx$g4NvAnF&DQBvo#L4b z*~>b;qSK#sdQGR-bvg)ipn0%PVdsV)c5YDExj|v)1|6Z(kvbivQ`ly~hvge|tWL-2 z6qavrVEG1xk}PwNBURbiGdTW&=-ayxGvHXvM{uXth0VB3$et2fn!C@ri35pK(>R z9v+@(Jq(|5RkRNto@fOPAFg6t&DA)J?6?}kd_d>0_why|mj?(whYr;ke1}5N0?^N; zdg7|-b>TO45tLpN9BKf1lc5gCxeRpxu^om#pW#;^-(slM;GkBkv$;)#;a1R_9Ccou z!^PEDsjc8kZ-|f*>b#sUq0Y;I2UN49+USYy7?7?ab#;6EG5qL-J$3n$b z<`R5XQSf;);pfeDe+v-?Gp)F~7&b<6bvrl#5*JdEBtlAFg3tUwud@>1#4>+z6-z$^ zzm$HLo;v4U&>O55!pvju!Jp51>tWLLtNZk;`}M2hUisKdSlsa^(CZw(Ts4;CmkT}M zmkU0AxjufEnes!~c^;`Yw7X9j0aTR;}aCH&(X%;2Y zT7;xF@Xx^rUT8^ZHI_Q*^lhEfS(icy`uI^-_^(qp_&Ke>)j70M54lPYiF%9pQE#Dh z)N0ku(3+v1pf!UAj)CT~@5NPi95jZV5aHRcI{&uLxdR%@?t;!?_w=jx#eKO*;UX?l z{yEo6pNn+P;q5?UxX4Q^N2)+0bd%%C4qbz*57s?K`0%fPhc$q|aFBx=> zmsy1OQbc%)6&#uiIu}+9p_6$c2H15Gp3oXVV+%oVvhu>uDu}CMRDv9##;TvQAaHK6 z5D^dy@c^#?TvwM(5X zT>y=dE($3r8~!-vw@XVpM+*^5GoRTnX^8t~s>HH9pz1^+Ep zTgWo}jv$LVfZvVDHdR_7rQc`jFZ%BSZN-6>WEy-WMV@X}AzaIWWT%98g09_9PZL*^hLoLAumngm+3qK!aF}kYaO$6caUV5knnAIz@ehMMOw3)HI|Jb-*W>p#?zr zTi`EddO0$bBXTNA?FK`sA*9guJeC80J`?48lj$YP7T`T_F5+8Ak{XLVptO*~$09Dw zy%aS7C4|%?cI2}F^p^Cih)cQ+f4-CldQ(FAK{g*Wjwg$dd?#ow-z8*u0_bd>D8lpI zB0S#Bh`PHUbRORe8q3qb&*S^xf6TXm-lp%sxj`AAxpWkqIdl+oHuhgcJoG*2O*#(F zTsi@|7`8*C0bhDToUkGyo%93zv2+stJo*v-$8-ex3V@waQfDJp)F|8mH41*zDBR&a z(Ak7~g}C2`e=cm5BGwN;7gJx*SlBK_+$bsBXDIyn)DJX|P}aDEDCTd zS8s5=CUCtba1q;Vj`{#ih?+2$BRqZ)2#==;>I3vK8dq=eG4RjhW8u%|;}BaV{xRqa zd^~6vp8#5!i}d7ik)FpK=|LMf3^a!i2ff5cAPnatK?7(Q@;wK69j?1KXoPT)i;?p^qb2E;apZ3LaoHi`7GZ$NLd&EU*s--5=mEuatBR?x*P z6<2RDQMU7#DF0Zt1DrgD+JROAwZlhds2x553^fA3FSK2#UrRu9rOy#&jx-;1wzL48 zd}%4@P3a48=1R*zrG)QYj8YJWtX+TcZdlGW$QU(_-C`%;lIgR z!atX_0)4;&=;hdpkY!i(8`hxS7RkR z=wiu<@Oe@w{EsCU=&w>mgsCP~f?Pv5u$FjSPk5|E zDm?duPb>@%n}W`VMWN93goibu@Z1v~mIVDAc7#IL6CO5%!gEh}*bfTNJ>g+H(9dBx zD0DsHVKpc`_k@SV0K0`77B0h=P{d0t^6e?|r55=NS)nbDtk9R?Gh~I%44xq?^yc9S z%^5yJR#=)0o*@g%OcBqJ6a$clPu@C;c|S3NvK&%(xLq!|{lqN|21EMP@EL)OqU(o`gC=vnX# zSwqi)XUH0Q7Ir-2zOaB5-owzdxN6+j(6boNdvqmy9$De@$O@n5zQX5`6+X{>g$2Nn zHS{cahOD7y!82qHJqw$nAuHMfp+CVhWDPwFo*`@KS*%(#WDPwFo*`@0XTdXMjruI? zmWHgMXTdXM4Lu8}8hREyL)Or<;2E-po`s#;xUZpS z!82qHJqw;8i}dT|0X-JU8hREyL)Or<;2E-pp1~=SHS{cahOD7y!Sl#Mvhcpb=Sj2h zdGsuN9$De@=vnv-SwqjbYmuy>XTdXM4Lu8+Gm0c;jN_MsEn%T9p>t-{tn>g--^oe;P zUK97Wk-L1d`)&37q{Wf9`+T}_jC1?syCZU2MyE$U=rDb6pOOb=U5&VPbx!iIC!6P( zwER|cr;Yl_HFweQTdn5q80%Ln?qT`+ZRhP6>sw>~nWjF+7ku5<@%@4w9V%ul_&&m% zwQyRW1{sTz+q;e}K2`f##NsmjZkAqL`|V3DKKtpFip`gN)7v*;Nos}s)X%>dU83jb z2b+bR|NN&qKQ~%hwpC)2FSa!*Q}2sy5oPKu+wqp|*s_#n_Ew1#ApR%5P81~6_EWhvNpA&Zt~u1&uiBa`JxqWR zyXHSM+IXqzd7n+k+GN(;bhcrp{hL|C_cY!-d4$|}^YRhLt8dO~kx_lix@IS9Y)N^4 zWyLN2XBJl3`o+kjRkv;(dbDc7SN+YW6TTXJvv$IkKDVnUBz4Xxk&s>`^TmW?{WGd0 zoPIBeC*;+}>AjNoEZdTL%Sqc(2JzL~_P+LAbt zf5&O**A7-p-aIVbmAtDbNJ=Egv9jrE@`;MMC6Y61XO>SsI|{sOEW2c+z$ATKlpsncfPrQNSJ{H6TvRxj>*F=g?9GvFy@>U<&9<`_Sku&`hGmrS$6I@4=d(qLOyL-~{W5SoY+g|d{519im2mNqnz)ruD zX9sNPaPoY>?{A$+ELnzr+TL%^FK08fN`YA^fhYYgtRHwJ@8V}2&J??t6L|V@_J;n; zuVg2^R_gogQ}*S#*=H&xR?W^T1723OodCqHM_xqId3dx1-zT+NACl6dtu-(UQ%?JQgT z(X}6Ytdp*%*Zj^uci!ln(7cQ9Bv-yw;Fz2D>-mpYoWH*zxRUv|%UTe7@O?w&0sa)_ z_irQ0TzI_xLx0=jW39csp5zSJapcL{O(n@aE1Yw8cEFt{+NFTokJygZzNakb%iVeC zyY;<=cT49qxp}nKqTr*CU;<;$K0vh2d1W59*50vn*w$9-{{7q6n`vy2d z@7=TknylniZz>7;KL zZPbydrQ@{GpTB5sqm4e<=tfy>^tsW9bL^5K(i_^?TP5zirHxDNcbRJw)=sFjMVqkc zqwG+;(h+Im`eO|bHTUL;?uiXSX<|d#^U3+2Kc|_W@Pl(Gep1eb#`MXoq{}&M#GEBd z7SQ2&(|v2uqM6OX&1x3qwsb=d*KnO3)%uZ=D9Qz|X%e8&1*7 z_8+=S$5*Yr9!v$GzRU!Ao7S$Ke!31#U;A0ma!OqncPg9`)_)cdK=JGM{#cd1T>s;b z<>;IB=YD*NW^O$ALk(KGX~UV9m}^s5W_#N9&F42N(WK4s*gW~*w+p^+PS?K8IANoM zTfX|)k7jP2>HLzmZe8S6oVIWM=KC77ZR?4nRq0_ubU^{dB&^P;LNgNbGOYCDwoiV) zMGLk~`K~gp-8LQc^tR7#-O{Or(i3Op=hNiGg;Pp`;#Z9 zyJ&Lq4AAK0IGrvBotV4^bdpY2?Ob#qlxFQ(z28ZbcWnTjDkz(@>oNSxcTtu~pY8r~ zpM$3FUc1l12Bj=av(xI7#h~+3;z6gTtOK2))5$5D;Ll2Nr`fe3Ded7uzGueo^=SW| zgx^X~dTMm4l`f`!Mb#)ebzQ12=q7L`rfva^Nli}8X8x&YY-mzyR<>2^jINbcTdC7b zL8&WfTjr~CqEb8IYOmDp;BVf$TziE+*}HX52p!*>c+W)h_p<%IT9q_4C73ey#hlHe zMf=w74x)tp@s+O9)cs%QH(~rhg84m)Nzc4igT6YrWG183gDZZ!Lth?Fxml5}W+dkZ z(wU63_2uZ;(e>9G(W39qT|_Bl`p3jj!l_MXIn6xHQ{9wwW>%?a+JElzz;d)QYg5)W za$lG_#!e?M#!DR;Wk-*)($wse@)1hSwoaTV=+ud{{nA|5PD;&5za-P%8@sQnY;j&D zI_lU2K4 z);iG4v;OH?M~a=3S6}NwU#+QDS&O8rX)7bNcWBPN6-~76wDi#9J#dzsxbM;iP}cnU z?S!*retT^oCCA@ttVL1!iJ!f+k7!BKO{X@9cE_k0+A!LbaQlpKvTkqIhSQ$&zkaQa zpbG_8)@h?@K94)r)8IxY_pO|ZESVOd}&Z!yxS|7SN^-KoBZV}A#OpSefQfo-R#67;N zy+!xt-R!5eqSFiHmPq%U1(#cCEh%YP&ULLi^ZU}Lsn(jE`O>isxnHw9AYAK3yH-?E z-8X2(s&h7XE{j=nrmohBEnRC`uYE|5)}F8Dj^e%7oeI|~vD(6EKugwduC2XKt=2o* zXd_vP^<{t80@(W-t~_zS$H#8`p_cY4X&Yavpf#rCjV%?F#LmE))&p$mEuTeuY`;$ zD4wMW$Es_?S@Uh*mD9%3^=)XguEY7RvO9%ECmspY6gEmY7B)^e2xbLx#h4X}c0SAdU3Fm$Kz~ihC&IH;coH1zi4pq`5 zHgCtlikgYd5ssNn6HX8-w&P$~tsceiXnSA#h`k}0gXugRx0_}qr@J(NHcmKB)>AlP ztg~<$vr@w8#U=@-H^m5NI5kNQERIY^i?jEr-_8S}NK!}P^rgi+wNO;)S8(ReWaD=2 zcOuR{!s$gH?s|9))mA?%iw1I^13Bs^oRKs|IHPH>a6V=Oh2zhrrl4W8qY;G@NkfD) zhz1B}6dg%P3)9-stUZI@buZ_wQ_;8)+aR1WY^rbq+3M8PL%4f?an^u!NKGY=6RfpG zlL_VkW=X$a6UjYs(CZO?opFc!B{;_pQJ~h0@(!oDwAR%B$Z=n-6XhH^m7={(UmUTw z6P5i)ds+K{?tEAN3)KiJE+m;I=rp!gaSx^;CszoK+z9>`9m4#xp!sjSZ{0*`F74X#rq+$Qdg9Hf z_|nTW<8AkN_eNH)#|ZZl_qVKCs|elg)TF*UQ)@(S|TAF#4`rdF@4HciPcLT6M%TIwueA2Xw|SLaXUcpi2`f zYc;{|mYu8B)+*CQxr+9Z=10G^tSVd<(l}Ii`*nDv)zMsZx#Z_`%StrauQk_nz9DnnY{fdrf;wD@~V1w-he(9^6>#gz&9jtEY9-Ca{6; zH`02zvuV|kj#>}2szbZ?(%#oXX!*cSS|3!=f@=XzH1 zZ2`^H#`4|TTDaBW?$mD3x&sHaeELHBcp3~ov`qUrTK2fpVO?SkwRTIq@TeDGR4cM0 zxnI_rG7D`nx!=(0WAetm%I;36Dd*`{DR*z}Q(8~cg58%j&K#6j*?rrc#QM>m(VFB= z#oN)8_L{GI7hapLZ?1c$(2CbIyVjkx)VhyC%_u=PY4$j+p8F=^+1_1ifHK@ob6TRX z12BVw=w9jdc`$V7?uN|fN znq!l;9TvJMZRiWgbT_s@8~UlXPrFGKv{zl)ajHj9a)@?POQ7D`_&hCJo5+YJS;o1&F89iO7nBib({3< z+g^6>bdSQMnVYP*d#Cm?`$>C~rn^sTHuf%Bpt4W9E#tL!@neZauWbsd(M&8~pGJ#NVR~cw zOL0v6v?B{YL5Z(XO@t028564TLtyoh*-;dM7ClhQ)Vk6J?E)=C^~I;;eTKtON%#cK ze|waW{rzM=lb_j7u~f0tEQ+sWq3@7e2dgaazh)Ua*9T_*DKh zR&^qOq#tM1p>S2HwB#>U{PV%jtCg??O3EY}$74ez(KOAq%H7$+gno2oKF^f&9;JGR|_(a*C4lh4^ z9_)YSsE*H$iKE(ob-3_BGZ$~9Qx}#4hwu<}p`gzlo>0|9DAiIJ7Jh$K;r=S00#2Cz zK{P@Cv*Sm9U(WfPyc6e^`d%gYk6tDGeJw9@JGZOt1%2k|#e3p2Yo4PnKDO3^hs&Mh zzC}FaS~wrf2P-~;{tL%Y?m2{vMKSTAYG1W)A#bQ+!Q$vY9!0ULWB6cofVhYHa?zct zs^ZgQ)6|dDviM9~Rdtr2`Z1U{#Yf1VIgp2^NBA>-ovWC!RJ(NH;VAMMoPWg?AFuNq ze-qlv;#XE-D9dY4OBlXtm?KztNM^}w_@8r+<0Fi|e{)!{T}MaWM*S2=A60yw?IYd) zsXD396<0px?Zu&ndfY;)mnJp=Xo(4VPfPZn`oy*Yo&z7{dkM0j|LDNBS4GF4!-#Fc z#Nq4p$3qVB9NZ}v!!bto)m`EL>!TvJq^c+vLr3N>|FG;nLaiy4;$C=4`Qx+bL--;~ zT}xdr?xk4#EIyV}meTk{fv>vYj~p!0U#Kc71H$<@9wEuHx9X?*NUH3V?Yuj0hFB-a z6(tL{zly>K9^L?=`Io%2T#7HZG{@FG6L~(K{(e$LY@d5j3d6_mrb^}c1NlR2fV_lv z`wxzHWeYw$i{7%A=>R0ODJJgo=9AsN5L>%&HwSrm>AM@56BTIvuvR2_#+1x@(8Y&p3cHm4~s zRpR||3|D8UPPH^PVe8C?^YL;8ppsmMf24k_dZ}I2uGm&V<5q6MZ|f?b$F0~7W1QLv z8!hDF=@`Nj`91XwwXs@?r>i~qREw`XL~X0q<@e-jY6QQ59u)d=_{``u%WHgqS{xrb zG|P3RlA@m{e~iynV%HE!<-cLWhI)J;zA^d%9|-jh+`f93ZJB`$2alKd>=M(>MKcH z=B;sL;RBY9)uwV={zM+YbLASmAvWi-0mZQyM+xkfhoHO_zGdnI34icQ@vhuYosTog zOO;ixQk==9<3iIaO+d==lDwp>;1j34u@Q{NZGg|0zRK(Ss=nU7KL7i7@GmCS zm8xSCks!$_g-YI16&xj`a?&f3k7U!`I@nSqTx{yCc*&Bl>}&q_B`hylD*F|;yl81+ zsqJsEv`~v#UiT|usp#ipsb;CR7!rMu*^LrZ_x7i>a;it&#@Wgdwjdx3tQ9gH+X;P8giduBdiwqh)!!9 z*kDJvQkg$UT;ZO>xTo%_HgRCo>5iN2uh5Xo*xrolvFg%5#t-rhz4U7$!d&VpBtly z-hYVxG%f7gqpxl+d~z4{f%Tqpb9JET|BAk?-iv+O+Z6p)S7D#AsHf=Z`#s+WkVm37 zU)a~udsli-N_3Z=8|Mt>+|#cv>?JhqnG{jaK~C;o|Un?o-aZEzL`NVPlvTn<-1P+#I*)DT^J2ly(r9iNM5 z#t5}CG*J&v`v>x1zW~0=QdRDNQeBPXbDgf{F$iZ<%Bm&QifRM?wd&xLR4X>$tZ!** zsf7<9HHRQsv^>Z`m5|5|RRzM{S<`=GB=4*jm(N=fxSe0pK9T%V`# z1$-s8k&5sfvJ>_C5o-H1^oqmOl4#Ih!f&A??Np{KC)BE!Dy3}Afm_L?>V`0fCUPHE`zw|pswKGH?UhSk-Ku-(xxUd%V z(Z^GIO^`!)6Mjdn=COgvHn}A)Q#cAvlWpo-{1Y`uZ6!Ct$3VKF4;!x6n9`!o_?6Wh3SA0uj?{Jt8fc=Iv%>4)w72Bq52`u zR6EFT!eX*P?afp0f%Mm*<2at7w&AggKR+i`RbRjskQsar-y_fBr~Lf={P`NS7d~U% zM(xG7$sefiVvDaqMaLIBp5Ii*;vR2dHs$}?A2tWWp^jDut1Z;>*iLr_o}k`-f!L0# zITp(PKlEp5fUQqYsddya{K`kkub>s^E`HzQH!u9?A9NQvQk#q30~X22*aUbi$-1du z)-&p(dh@#Cnpl_xjs@mbg?ufq;za~r4`68(x)S;!&V347m5F}kL@UC7QRA?susA09 z7Z)Er=R5TFKn(6sxO{k2Z*eZc)z3%u{Xpzg?~%dRH{ip% zJ)y3KIG+7WrPOCmJy!)a_)j@d+w)Vb(uAOP&LbWliFIkSPE?C;iBR|^6Vxu|9+v>c z+P04d4IRVk5{>+z?=Xhz{f50efHfrSOpFOI^{5%O$1b7$v8r?urgbdAD}qEy!)o2L zbX89~XD_2T3pK#%>AP!*JP~_Ezk|nLAMXN;Ft3Vv&$=wJ1{6@_e+{V=d`Msgckd#9 zb-bQ2_%)d4tHfef1;;9C!dhhGS}p7zVEAkE&PD#0o(U5!S&R5}u%cYd%n&g(f=|dk zrI_pdoSx0bndPX@lyXbTs;)lvIkjq4U#XXZki_~*9Hmf2EJCNcR)l7(o=Am$MXp4u z^*dujGUSu=aDrdG(2v}*8XmvMEncoMWXwiHa^o0h@sWyrx3RyoLdZ zzynMYuLq0+@DnuM$Iq)SFa|)XWH;urHw5Mbr|?q=0J;K80mS8v`+N5S5VtqNsvQ9A zV(;SxL;{;cy8$!?CIS12ie(|wNRuz*d{+Wjv6&n0S$qbNhXQE}Aiff)dnJ&M5^3U< z6p)Vj^Rs{q{0tCjsRl%4{D5`TDI08;_ThsZ!P0O$A~ z9NAKc9I*iI;XFpF|* zNusuufbqa)qBl6u9)Q1{A20|wPV}Y=7zZQ~wMUrt4FTw^eJasgNXJ`9$6L|BA)*cn z&8pb+y~f2)WsiwzYFf$ zRRvlCGk^<3-D(54)@?OWqzgd)BT z6ZJ$qJyGX+LTB$1hygN*qI`jN zSpI_h4?0UUxIBRL42}hm$H8}rhCl~H7XyfI803dRei+g+4D!SMf!4rO0A)5ZhiDA) zG!{AF3DY(y+hQLGs@<;+r#rag6PfZ0L5>2ZIAVJfT0h~wUJbD(8K{VY0Anns} zJ{@VBp#t>*=w?P1(M%VBe9S~XXFPoNvSVHk@z6^+Z3QH2~Sf z45IB80O#9h060&=c~Ua~c}PkI9un=S2Ou9ikdGaAiIT&C5kMkPK(xyWK(v1rwx`|<+$Kt|4Iut>giA-b zgSh`eoF7aAaDE6lTm?WH52Ick@dbJVxc;32v;>f!?=pxokhYBOL`OFPNb9kh0Mc*_ zaUa9IzOP1f0_i?609a1+gFn%aaDPO7I28c22T%`A;rw(N0C_u&w4OdhbjAzl0K@{g z*H5_jPZ0pp_0ui@={$@3os9z40VvCJcAzJ)6gWeaRSH0yS#iKIqVp=zMTEVG`(@+) z*@*v&AJ7_z29k-cqF!GW?tP+P5dSZefE3^%(Y0`32!Q-v%O$!F-CZ96Yyt9#a>D?W zQ7-b7dx7W%?r{V6xPd&}Kpygthdh*j9^%YHoOy`zCgQw_IBz1(Tj1YHBl;D%i*(1Dr+gYAKL~VM7aIrY*$G z_)eCJ?_H@6i22~Vc=%cs^VtmG`+XJ{(3hC+eqzP_i1|ate+jWtJpsg1W*h+7GIxoU ztqu$TRs%W20`Pr~fUW?(6%vq1tQ_RawFaUAtTRO4g_Umzi~;b~t?~uLDpUb*zX~gX zY+`|kGq4wcGzOj}7F3E@@CAIO^$_Xb60~EVQU}5~+X1tH%0Cd#&6tO0_casPp1~^760_lxt2228QkEVVA z!Z(F3o1P)o4Ebw@yfi~znjx*t5xzOXH%Iv9DZoQwEm{J|%j=B+k}pZ>_V5wFv||14w@x=%_6gzO=nYtX&ka61YO_O()PDSWK*ae`0Sn1ZDu~ z#5!=G0kMvCiFJx6*0~-q21o{O6YEkFfd0Cq0!U-m+Qhoqfk8kDu}BNC?#NsB_COo} zUG(q;kj@^+dk^TUCv?#hdFYAjy^!|zk)96}jQVizen?Bd@c{1EAL;%OaSwP*EDAas z+=*X{iYcJ z{x>PWLt>i|*Ji}Ec>@4_e(M580BeYCjVHDZ`QF}{SQ65)qbsqUVZ?TI0wAA)c=m24 zmX<+me=vYF>|X|4Aa=kGpj-~%`~b=!-Ae3W6=H{Sh#iLiFv1>Q2%I8z1Ufj<5||9^ zC-xoklo1HvK1UJP_kD>SPX!(mJArhZ7zS(s@`+_4J(+y~q%{*^e<%+??uR(w7_pNo zfIOd^2&4cHi2Ya_K%Rd@o_|E1Pa)5zpf5bf*eS$u8gZP#ea_%MKOG`=)(dC|i~|yZ z+r-XQ0w8m44RD267V?x82`mIMiJkW)b^&R)aGcmh3xNDxj0W}-%O;>cFa}5hkfuvW z(ENlAlw~nI?{!8l z^B^Li;uRI=Atg06W75>jOv`$D)YQz%tkX?X~w_d+p(@1HR>jBvgQJ#at39 zaetKq0QxGVQ-$klx76+POkw+Np!}!^Bce+5?v5e%4G(y zov@k<;M_HaL@YOmZi|3ZB)ab1@W%%9!#+-Rwne@Nnk3xO>pj$8|zB{2v* zLefbLMVg_IDRdQpe4%d=(Km@<1|S-!CUF$x9|ieGfo2ra7(D`b4~QkKumNEJ?td8f zKWrv(%rIavfcwYb{zs;hIDQ<76Jkhw+!L5c;^b{W28kGhicjyx3!Di6->C}$=wxak ziPL%kbAXK`KKllVGjN=dPU1|+JQFg{gv>J`^DM|b3o_3N2av`rr1>0pJ@*~}8E1C2QUHn5I9WYJQ)ZAknefO_p6a4&MznNwGa|tuO{)0Kwv%)MdE^2NnD8Qx1Rx4 z0FdczGl`3a0nqcJ?LZ-k@ALxZ02_gH5*Pad$oFF8dvO9*ubqI=Ksbr-Zza*Vg+vqd zxMULWDR7FgF9jF}ECmjcxD0YGgG|c|Kn!6I5&#~{k&orL{{!UfgQtP7feaEq>zw`%YleiW%UqQaF@r!2O$X&yO1b$nw)v61OIk__HU8+n~27l=1csB<_Gdb|#UyyAO$by-56B z2Y`PJ_{E?M4mgqc$5Ijx&LQ#8ToMm2BJoHiiN~HGF&^@qK>3|4C-D@Hr=m#wE1tvz zK9p~nk5Nlc9+@nS5Am!e6$oKIpV=&$y`bS3hdH-p6M&j71PEC6l6 zViJqylUTBm#46c3VZdTwCy+-{Cq%LD1U{YC1MIEN;G>@aEC=EM zGfAC?0&f7@0MPjR-;8cLlDZ+!0Z#yHNa_yVb>Bx)&uo%(`Z}{*fdN zz&-aF0Q^1>X%9p?gOJAkNO#C|l7@!>;4wT3fDT7=08j=aAk&D$Bs~~G(nEs*@OUVm zq@Z2^n0{2TqVQzAG>X zKpKxC%}1vIpqT*L32T8Ak{M*dXgp%0~P_Ge|!M221q1nk_!++(&SkrJrPLK zlaS#_z5rzLF#p?vFT1(ozDz z2XkZ6hZ9Kp2>d?|BWWel{p2i3pJkA=3i5xxm!#F8`2spw16_QH`_|%~bx7;q(IkB{ zf~55WN!kGY{s-8I^NrOcMMjde32FQQeQcIV`Z18CEs*giUm%^NpCQvPkS}ToR=z;_q&xMw~99{XmH6g``y{q-cpK&As*Ns840_%8MkNq?ZM{#XEP22x0ha{@*J zppV-KfaV}%J_udG7D@-#0?7M8@H>QZIRyTPRsrC3sFI{VdjL}b=;_Z`0Qo;qe;p{zOv?$lq~}I?h1J4%mSc;9ME5tfnmUWU=slS<+=e#FBh`qZU=Hn zx(1%tAkVcA0qEo!@}JiS0Ixjo$^)Kwq*BK-#54flo=Qs3xgy49Vg`Aem%o7_b(oC0U*d zd`hy?1Hie$&f`c{4+Hq!t`9H^SOc6QS%=-ky1~F)lI?o|(*e-f=aB3$62N_q{s3s4 zCIR@(*$qIN&a;7)Kr8?`oQs=>3+P>@00v+ShudP3-Sz>H$-N&i57u`VEf8Pd@2jO}!WFL%t4Muu{*8xX?N|J|k1(3fXka5U10QU|Buc3j!ECBf) zdV*xELCFt<0l4phYLbWHIP4I~!}0y_@c`}{0r^MZ`w`H`gL6q9=>+rv5&`7rp%9XT zAagMIhCt?!NRmTE0C^5OO!8=?KSm{aEclMw03h9Q`6NHGoaFJy%lJZ)C&(l}z7PPv zNvlYnj5MAc2t<=S1v+|aHpx#vP4YhmlZ-VU8EZT8Keq#BlBeSSX)eH8lBfFu8%ch4 zJdjKBj5kQ0=?Uxvs!4v%3job*e*k=EBhA@c0O;p=@OVB5cnv@vo=+lq4)ibwWi@9W z@HKFX;p^(ptBd_fO3*w3IHJYOCJKr%S*`XTs<%ehyZp2 zkn?58{W4^J`8^;C$Rru-J@P!rI1e(;gN*Ya<13Kym65<30KR)Ao#g)+06>=c;6J~V zI0|)~a0GohhlHYUy zMgXq@8-PTT7s$Y1;6-2^Z~`!s{1#++YZkB?I1E&iys#HA6<7(x0+8|Tt^j0wdpWQV z$R~M`A21PsjEf-SBFOj-WPArQVhu`uXDa}`EXH?>k(YPh1Gba=ULMKsL#OXQ0Ym_x zeIK+2FJLsV5J3J6xX%~>%mI)OV;affxF>uP0NKLhNj9lK2mszD$YrV}IRg1y0vRx7 zk(VHStUt-i>PcQclH?BzB!9S>yW>7 zr%3*{69E1Gdj)Wqv0Qj%p3qWVzjsO+{(ABq< zBySiDECwLYe{{eYU@eeK^2V;f4B%@Zh2-yifhPcz$#)4PM>+t{0BZo~_It?p{RH4c z0A;ob{4mavH@ycSy-i5(hta@t0O@V^044#*-)7|RM}J^CupU5qTRef;zy=_VT0Q}&?$uXmW4}rq~t`7_a-TXD8ad9*~4BAg{f#70CZjTChTDR$+gI0TU5m_UjXc0##e zzn*InDQ-A=d<`U$(gqLYw_Q$3yOE^$Y$2tC7Z3}SlHz9|MUNN;oyP#-q;vuAt{X`S zm=ECI?yr*4b15mkkw#x{Qu^nRf-$awIYT9I04amPcPQ>1=1?M>QpWoMPXmWYnSgsHP9^2>N>ZL!N6M6mq&#($l&6u#KgN^t&jmm_ zDcBRDOq&aA2XaZ7K9rPaJCHIXgp`@c$8-IF6jEj*?b)+|4L}+x&m*np!Si{@_54l% zeCPB4kmidbFoBf0OG$b8Yf|PVlJY9}&j;P>P5^ZEI`a8C_`hx@<&7X@~r{?HAwqQ@cR-vUpt|qc zGWcZ$5Kl@}B!E0%d0W{&5SR}{17=co;Ccu0u;UOZJCWy|;Infz06x0{fGGgv*tHLU z9J_k}$j9ykQucTO(8(U$zX!kVg}(M~1VHRz{XyjC5a|B|??1u&&j?bEdH|KA97CC7ok%%85m-se2?Hr7UnJ$!B4962 zO3Gh-fN)YyuOKDS2?zqf|IArZ&MqY79P)Gy=SetE!twlk0J=!_2Brg_0?@^UsQ`GU zJOLDwlDeFfG<=^{OUlK;0CbQ(8;B?6QZL{`0QY6g0uGaM8GJ9FASKfuKw6oJq+~(1 ztfi!6y8yU12l8EwAtkp1Dc2H6$=gaw{&-Rfpr69^q!i_oQoMx}j1iSG6=hZt&sZ~j-2c2ansls|vMKh^#IH_tRVIwlI4oD%@J^+X!)!{`Thg7H4 zq&goZ)pY`pM5^0(QayB}w!yjA98%kj20kU#dpdyod_eD$Mr!*AU@MSLYKKFlcHBa$ z-*QqrZ6Z}4O={<4QvLHu?RtvT0HoJ_J^)#If=|zQ0P^+f2SBFYF9PMH_A!v!cK~92 zAdP+*r1oD+>VSzrJ*oHQkvb6k2KfT70ozCo#Cc!{umCtr>ixx}4%tiU1IYKV4@n)~ z$BL(Mgq$O)NyU7h`XFc@OeQ?J3qbCXIDcpjsliSFWC-3$YDgTZq0mDpZnK`Vq>e-Sn443_gYHo;U?~83CqRzJIsi$ePW+VA$3Z_S5I~xf zA;XjSZOV31pN3BU0X_V4DydVE=Ct{wPERBiwpN{qbY{&a^|@zAeLjlR7Z#EFVgdl2 z%mu%fA@9pbcOLk?@+q(zNF()C4@OzOLHNPQ3V z?`4zvz7r4y;M|}Go&ca1<2X{o`v8@sn$`m+05ho(0|4-h*g)zM$h8DAE`hF>Y$A0j z^0Ukv0FPx6Kn|(PA?pXw+lNU1!=0plgzr8cN$SdcQa|ZH>ZhX#PaTrFiU9Cmy_D21 zkoFqz`7(&qwVQ!bQon*uzJhMQS^A z0RN2(Nd4|LAcNFM@QH-{kl8%HS8I9pwb91j@K%N9qp94IfzDJ&V*m zxbL?BQujf|-&X_Gq((zl_^WC(X!b+4{Y!x)Qe%*A41PZl1VFX}g`~z#B=wJ}q{iX5 zxM4v=6r_D(wKJkvGDze`DlS!mo=6}=P~AkM$q*z*+5VD2A#V!6Jp)MwxrB2O5U5SY zwF~DiW~8U5qV23i=Xi$|7|}XxNsz+yg116 zrgGszY%G0cj0h221Si@fj2#<7zX=|Ki$R*0oIKGa)W!zmhl@(;+{cEEdN-6lpm3T) z<-){?!H7Y$%n(H9jS<1-QuPiQ4!=Sy)v3B(s;KbxCMWH#bLY>W!CmJH3eK5$ z?j7_*cE~;6m5ieZ;8_Df5Cz9w!bmcRXOW#H2B8nsa~>yn4^k03)PmuXyKs;=hZN4? zm0LJu-p&E@l{d>UkaI9I4&hb-j&lJnFmkQ@TFk+HoVu8&-`|#g8E4>dd-}KOpn`Ka z#5uUyIP~Qlf*Lv4p$^@uMT1y`Is;v~aPn(5@*SMK@O+ zjzma-W93PRv6ORRt%b?K%7MwD)1w~Xe|M$^q#D375=_6MCgAFKmJJ-!Z{(xJk~qWl zSjrXQY*U1VE5hS;Jv!(cYl1;!*?=BdE|xHXSW`J~G>9w<_@3q9ZgsKMCD~#g5M_|H zhf6#U-EDc0xeQgUkip8qj&pFaaq#3EDq6u|ESEtm2ZNAqT`P3^|2_xz##}V7+cQmcTiU2w=cc-~ zH0svlX5A_%F9qerEVzR5V)2-bD69x0lp2ABXLuJz6b$~rA!uUtLBo2VrY-HsU1(3i zRAl)Ik4Xlb>(SP7NjO|2AOh}LC&A9z){vqDs9DeBfE2Os4l9TK>3gGrx|<}ocj!l) zT{DR_$Iw_3aDLn*)vlszIP)|~9)FM@&fIKu1KgaOaAV$xRNP79_K=C(g2-gh7z!rQ z=QJ1=gE;jDL)j#Wr^&_E8@Pa*id{c(p~VQAag9Pz;aO% z1qqL9EQ9DTAs-3E;0kO<>uHkJGTFGgAjgedA7ot3rB<$;G_HF!u0ga)V0>S)@=ehA z9^iam(bL;^vMqb$E=j1r8vymMSqQd%0SEN^kB;)Z$66XJ`l8L=#HdBmbg+U%%h`7NWC zcE$MD?8t@P+04iVdSSMLwZlfsGfJ}+VVbS5*lY#$3$pYw1PKbNMlP&HtY&^JCMjnh zu_kM0(tHjD9Pib7w&*Fex`wsLq17WWN639^FD)16Fe7Y)5tiL5m=!j{3QILU+zcCG zhUHo>W`~Wi!?K4vGsH$1Voei_BeR^gexHpez>yhZBO0RMR$;ZoMwm^>=K*etjWESG zn93$u#Cz{uFvwt|Y&USk8VJC~<$Qz^j0n(SR%^`I23xu#HaEA&j4>?=qIH@rdK}57 znu77Z?CY8}iorD!7(I%aW$Wmr`4k?s46<5goaB%xGIv za7H<>R%0t!=Fv%?B`mXP5Aa=*UJf*WsG@a9Jm zm3$O&g}aJ+^wZkpL52lF#cr)@L`%+5At6XANZ&0~%ke&qI!Yqvde;fGudKrZmk zXh3jwA^`zb@eku+#8}PP)Aoe@35QePo;Y_p-iRPbcB*6luR3de+v%1Lg^~m(kYw( zjs31cG_NG5()Rn+W9m_PklG*|FgjG5D|OVZ8>MI9Cm-KV-8%JbqmDxEb+pN2S^c#KSc;*Z5VSip_e-5xeyFI-SncGcd6HT=d?(dykxsk3aP%P7YjY8|mv792|U~k6UEqfYIZEf`S~X zBPkOc-F)1A`BjMJ74{K7_&q%sQ;*?d#-FQ0EDP92SeJo&4AcS1xVCTC z&DY)mp%9&YTxDYjtz#eOS&!ULDJdy|fq`C`2RDUS=CY5eXGTU+b`d)7#OR|(W6u;6 z6y)b#%1f!mN%F~*yu3g6p32Y9zjQngO~d3|SuRvo%l4vbFV(1aMy3NF(cEA*o5NMf zPFDpTnR>dpsZIorIa!5;mo8qpa;3`2)whQyPKq4;eSJO5Md0Y+;Y;=4SXwNU7S~CN ztFGdDt!QimR(4m;ol8$oM^JA!Pxm@}Vf6=X-keU)$2ztdF3(GRhN~--%;vOWp@k)a z-FcycaFvH-Zl0B3&ow@+;AYPz$xw|Ca&efxg;cJ=Dj(b1DEi!Iaeb_%=~0H4Ru%T2;(3f@P-zj+uH z^&3kcyNzp)9Xs~hf7S-$%^H2|_MS>aO<#rnwhu7ZD9TfNg)48r{r1b#(^c@Gda4(^tk*pRcR>Qi(H42;{rBJh_$V^b zCSe@D*i0YGQ%8;*IV+ep3**oSWE;gzktyL}mYLe;Wy>PkbdRm-{=~$@OL>`@*K<*k z@1cc>L5bJGm71EGoU=i#Yi&|(ZSAfg%lH9qZe`vB2M)Z4#wQM|zZkAuzka>2d{`?R zqE@FB)I;WJ-)xI7G2@%b{&Zk}w7H6UBe%u(PB95-JdHHwp{_IQ{TK<$ed&yF7>QYqY=_0CY&n> z$2^4*2~u@UxLxkK4D=Sg{u#pTFNMp`^`x`^4zaw)K6a7k8yXt)y}TowmFn~pCr(_h zS2_hxo-#4m@`mL-CGrfp4}3R-cCe4~-hHm-gztha?^_nAlcvA={1EDbRH`r|Kr#e+0>6fL1B?ejgYR9<-Gk?G^RMsz=P1~n|>(wRfC(ZB88 z^XrbCyP^-JUWK*IO*<65d)JPrJ$v`<|ARg=cGKx<%gIrJk#3M3ot^E?6|f$XGnLi5 zABA)X?<$GwuJtLduC6+YSo*;S zAFNrw>%XcsLw@fyskyBx6crV6E?5phll}}}SD1vne0UGM2L}hXI(VDsI;=?Zd_7=+eh%dN7cCgi%ukbQxig@|Zfq@T5I1Mw%V=L zZE;DfEk5hP|AA6|rD+ddCOX4*xkzP2*K@DtWMpP#UoUg;^7Qm<=TLV2N_OU@oSbX< z#jFRH-cJ=l3q@u6VS*p=c;?KRA)dndA69?*nci0_E=CPC$~D!bvkSNL>C+jVM$-4zUR(YqnR-8O?a%F| z_JmQ(NXm_H_Ukk1si&R_>ES15CY(GIOux|g%KH<$c$8x3q#1L0Q;BE50Kc-Ty1F{Qp6%}B#cAtN#mwdF z!v1{5{^T1ug)izrpdPc|dWn3|(gXDZ))__YO27x<@CEo8E~yt&FK1?EUQA0(PfN;( zF{stmbrwO^U97=5WT# z+<@Vc&$Vl9PlaK4^0fAO+wnlv_3Px+ZSdg1-F>uSv$S<9iAIczwPKN9_Nu?~c?d6* zaM@z6Z!j-+xsr9|5?uGJ>?@hsc^PpARi{$~OIe#-$Pv?$aUS1vi+O==rp<|aG zJ$iK02L!meQ7;IElJROw`PboPh0Ard@NSlC{YV&eF%#`!5!yo-bTNV8(L%>JMsM(| zQq!3xGA~`c2yI+M`e~_2Nf{Yf$7(uY!^90!5kW^f-KWffy|N`b`se?wS+ge8@-Mui z?`6xoc59C2=H{N-^w}ep*YSz4EVR5}nXY|i;N^Z8jET|Fm?>O%(-^E-l{Oi1jcpup z(PeBU`4w-#TO)_*_4<%Uv905P=Gz`c=#2t0Yo8*z%*I&a8*gBiFTR;^)g4QrJ3O?D zxmYU6NlQz+l9hEOla)#O<*Y1}OBNKBeI+js>WMQp9XDx3pa_K0s;>UjHa_yVj*o7X zUi)TBujz=W<&tc1E~`97^9W?#Bdl0VEcV+9tE4rV(X;g+FB*L zQ*j)pMrZ(Ro8q);BW+7F5w$UF4#Trpz#PU|kk=1t5YMc@1-54bYqh>5dGlff1Z#;e7CMt~N)!|l z0^)=tBuwvR+ZEMZs)zaH+MH&jXK|IANn3EZ&_I$kRr@9yX0w&{(6B*> zL4EJeWG?0t&=^&+t2p`{2cZ{g=n^YEo3&v(vb1$S&||(9Gf6S4GzQprReT5>a1_(P z0>?N3JeC@$R}e*D0%3`5zTOplT*1d1ZQd1pT(S8DZ#&WcsTm!UJd-@n8YG>aYqe1g zjEg)L`PW5*v?zM;QlqeS66=y4!MxunZ#Y)7gz>vEnQK%tT=OdO#2SNCZ7=AIs#}+U z-T?z98>IBQi~h#Dc&v|})#~R!y2$dqioDt37}c;t2~D=u&lc7Q-|eE_G|arzieuW)*gR?vrV`e!(OD5w98(^s~FzL3)gaQbgK zeFLZO)e8C!oPH#y|BlnUa(ahG`sQlBK^#tJSvf7@#D$zVnGx%O#+w(w7m{I_q^(2lY%vQCq^EA z_uL2bwEw}=4&iC<<7rnlrro0Kt)y?s+S#9F)~@6tvv%venY9~O!>rx2lWo@SR!V#8 z?oIErLU$7EFEYDc>&@)CV~uc}4OAy%L^a4dGwf)!S8SGsy9vGHlRf@Tg&Zi}Pe#-_oMq z>bTwqaJ?Vrde7&2_iTmU{W!e~r?+r=Ym2XK1w9nc_I;Lc`gNRsAW#3<|6l3*-zGoX zd&9=)jwpZI3a&wN^FjGrcluScW~c4+lm4>-YT*0)5Hzd?r);0!)6P&w<32yO9C2Xlz14G`e8F{y6=VtBKj7|GBTW{K?x!$yVk2%&Hi{7-lrUd=skL@4ue82_0 z>C&+HMMibr;Wa1MoIyv5rGlN~(487ppFF(srwSdZuF8V$H*`SX9(}u@BlTChq#HF` zsz3*~&vTzOMi`Mj{PAXgYS^F2*6|MW8`#c^n|8JQQA}T;t%hO0+N;7CxDb!vimm(~ zuq=c?*bB|JwK%(yiZg6$sdA-pIJ2|1ZEXb!JZ&pT%omaNB+y5=r@Kiy>W6I|Y>ysR z`)MZ5utU!~pWJcgX%ev>A4CS6H{GMhcKq46F-svGNuYn@9xU|7`q4Ky!?uu_N!U7} zZO^lElOdy?W*B)t)BHZVH?*A&V>DhtmJw{9gT+VV7exEno&mURjAsyiuI!2MO_Bo&mE95|uG4CqzDW(T zalEnD>TlLc<=)$dzH!uZ(++~a>y`dpudtO~oo$y)TUZ}wl#4TlSPJ*$l?*w_DQUMs z)OG4^R02;%9*-=8r*?J%-1vt=(N_bOGDC=E7P(m#8I_FsytKRwgIHa1&8WBy3J48& z(;%k1mRbGxq0sG6w9$#My@+B52-N=k`J{R(z$7V~0=bxaF(X5klaidAl9Fh*(V#XE zEHep4MbIYa!!b3`sZ;mv5$*f;zi-%x`>^OA85x=8>W{ONCnF<|ACHWrGSGdp3d^}a z9x*C^U15{@;mV>#XU|TU5Yfhi@Wu~qqJ|y#KlR&hxBhv4>sISznH<~@-6Ve`gyQY% zA7UP0neOe^XjVXJX$@Yy&hy;<+O@nxv1cGZW~HuXUCF|1Z+%`~T3TKnWk3#zu+C2w zqhiq(cEYhpRZ?>8T15MH?VKtl1#?mX0Ri2+c&l!1g9ZfzgoXwL(3?hAi;r)|jvc${ zi;MkzTwGl2!j<%NSJyHG8ojeO+;+F-7mS1s;8Snl2S-SPD;*eHN9uPf^Q7$=TQX4Q z)t4_Al>-5f^>_?rzO>JFqww%fHVqR6pv-B#rI?l)4RUs+T!}K*xwbPZ&xbrV@G+FR ze~GiT%%4P=PeYl%irO^=WzOcr{R@jQ`H^w8thUZflIZ5uMps{R70aEeDVc=@H9DQW zEQd=~g%G=YeZ3uK@w5fcx{8vLasb zq2XHM@fmZUAKR|{#0D&Ru0K-X@z9hh_nkc(A0L0=8picTS=CWZZMa>2+7&@i`c1bL z{FC04x79j-YQMRnSMT0CcJ%7x6yfYxlZJKAYmRNZJn-TUV!qYWK~g<_gi zTlkb0_jfJW9Bf%+S!BPguyFL~(L+0+w=!bl&1`2EuGW=Zzk2o0Pr|VCl~0o{5dW0X zh(6-8b@uQ0Bwe^Ld2)}oazxL>)7bftapC0QKjUKe$3$)UdGDbk7qIYoB_}=c%#po& z_e4j>#>O5?qz%UII$d3bb?sAfbaizSs`y%ESp({py{wwevMg5BV@pqEaTTh2<+bdq zCDrwH7n9Skl@!+7WAW3)QD`VgPcO*KtgNiGh%mNBxw=Ypa}MtyNtT+L+Dg9uSzlPo zcv3?}g^Wq(dOjE0l6UQvAqXU#C>Acj!*>^YlTf^Y?b1(@ga%%V-$dPb4|U@!)QttG z8?U2o40LdCu#*)$F=$Cn%7eVEuPZ}JEDdZ4SP*%eD`|DzdEk*p9%*y=R7`B_{vA6G z96x^i9_o^-a9?{Z)feZ?o;~}q`-FS0Tp^Zc*~fQ%R!x_npn={x|A!uW=)bOT;?+-% zhYK|&VU{;7Z?W2FStKs|aAE)P#JD-=gQ1*M$-A3d#T4Qj`idW&8BAZ z3Bf2dY8X=|v^^ZKh0|tJZ>3jIo3&wPCZ~7f^xHUnI-?J7nVNZA8a0{`8|n4n)Z00A zF;6|Ck={1DaPwTI7q|T7oY;jE=W*gRMttYq@Ga{1fD#zTF@NA^|hqA z8}x%YeHf>o#Oc>@`V)=x&%%x%vJNeT9hnU~G7Y-xXQ^ztkGWdu=s0D{l;Q0fR69FG zft|`S_P(RFC@U>1bFs5D)YC@R<3#i`TUzdIoQ6O3)YPd{A8cFuz!Ou?or{mhcK%jb z1!-fJt1F(&5ufR(809If_la=MFSu*h^KM|f+;4-i6td9e(4j*Ik0fU%CKeTq9*teQ zt!?*qZ`%F-d&*zu3yg9q>xUvb{Sn`CCv+ix*RBxk;=#x9qpb(`?fUKa-wzzX=JDQl zZ04>m&d#MZlA@!@tXqocm!4bQe3#^`)`(@Psc6Ni$tlHGuU@`fQGvaMw)X7XOuyuc zJ+5`d*nTQcXZ@4aQ);ny%F=qLQmuD#(t4+#wBD(~);qnATJ;fX)pw{>5vWz~qE?N( zwRgJLS~mQi`lwrL-N;+}se7%3|6OO*T(xYr)B1Z=^>6lEx7FbOw{~CuRgE5gPaWB< zHGJf)o!PzC_?CLK*0+FL`=uM!Rn4;YYo*K{N5NR&;DAPqLmxD@yY2JI=Jal`ZlKu4 z>C-s9xfS#toW2LA-_Gd^IemH~ee*q=H%vBCE$758oH&;gr!e9>_iTSR?;%`v7oL0q zPdBBhvBu>AU)5kZ`+hzvY*!=c&wxrDD%%r{8Z*3$6>uizZ_Kk}y zG+?EYMi^-;w#|zxX02d516&L=!AMa?H6t%QH$CsD0evUcVUaDMR970{Z9wvr$L9uZ z_VU)}Zj1La>)5^!f7w%kF6_$fD6^q%-uQcGZI|!WYFZH$vNHRHJqycLK8~_O7@NEP z-=S}Q{h2-Q%GRIn=c)h7QV-{8S2m_?%lEB2;T!g{HG+YK0@f+Exv+H#=_p$xsPPuU z?jp4V&$Wf8dxGb>lBaCGd&(#Gvb7cOLP1Z$W)}gk7AZR_96%X(3nO^SY%beRFH?7Y zb931V35OGpCLZ2kzsxs58sZOa9*n|(e$jrn{56bAanI55`!6Yb3M8{F19-cVq;c?@}VNYjix1kTS zDIaz*ZXD)%3Y(eh5#r)Z!iW(OY!mYPt}DLZ^X-ZiD`w4_)%oJ#ueWa1w#q2%ixpbq z2sfZ)2&HMALNPU)D$LPxe9I=57{fVxopaZU6ht$uE)EXxgNpE2^%%@tR9Tb z{@KEBf^xD*=O%2qT zU~C9~#%Qf%P%Sf4Cv24r3S)L^fVGlovW^pwZ5S+BV)t}6Y7~!%=lwX2&-q7 z_jPOj$nf*aNWI=*Qm!5T2FYqwO|Vuu`xPs&;VaDbTHL2ranuPD9(&N8ydY1Tp8cYt zOsH%|q2E8l=wtd;`+ThavtL3&zus*lI@MNVQZX$TgPPo2cFe7jYfCU!rel*{4ODAX zb$0HqR99z0#WT88+k3Wa*Urr94>*^^l&xGn{bn&pWV8fur&SSs;s;#Lv@s{2E?D$@`R8+|RxUfG**&nPS zY30OL@NS)>7coa4WMQi?VK>%}*c11SYey$>#n+D5x(Z)Aa>bc#?TB%0y4s^X<&U+a zAj|*YF1~t%*<S+|tY->lia??B{tR10Q;V!;08jdn;GIcI0p6%GZvvtXx-vE7LEyV&y3Kj$HZ5k!?kauN<*u7y&C%eC3F(>#>zC zwsOQ)#K09RN3P9qy>;cNd9K>Z5oX9WtzzXUi1RGclbw~Pt$doUK8iO=dx%Mj_hUT> zB069_Nn71ohbzQ%=$21x{R}Z3ob=QcXH8=|u=;Zo$EGE2*`imK1G^R<{K9g!41_(zd?E^8LTx zb`)^Eef9TFZJ4bV+`0-c?LiQ0*4(sb`Hv?1VYP-m%P)_$Pok%AiYNKKyRbH*J;~p9 z0BdqLp5(ukBit&i)<*Lt$DYiVV-x()!=Ya`$um1A5laV;@!L4!DSKx(*6eP1LfsX; z0gHaZ__2qRUeeY<`+%4|$3Ma{URw#pZdR>6?-$=?OSi{0-of-at=F2L_7K0Qu=aO* zlz>L=&sm<$2`mH#pNK&zSOt^5u2k=7zL{#CWCN&YSVCG@Mt-=guSg@~;H{z15c z_KQkc^R)7}l_t}^ZI?!q`Z2^L$EUCsYUC|c9M81A4wpG5x!VBNMvZ8rvXd)oq((GS zt>rHjU##L*j;zGvtFzE58xT0<2jmNLrHtNB~ zWxOA~bswkZ(+Rs#d*&J8mnCDYhC@GCZ5-lhH9HK(bF_CE)#~`_Ul}Dcf{WCT9jdWE z83!jCeAjVS9j7;Q`eaUD!RhUr&_fR_#*S^Tg%({2kM^@?dMze#pNxEr*UlxsBASb{ zHYQ0b(Bdqi56$<2ep!#gbrAsTa3`K7rG_bNU!gU(D&dHqtlW zk{TdCYTIL+xHl(`<-{qBSZjqX%5LUq_vGYpoLuDOa%0*p(zjCcEz*y^Nq3*{^pErO zg;u0r#OVXL{QEh5DyM(474#Q5eS1!y$LTL|dY4A}mf8#&FlH+Pv^Mj*)@GD=He2C1 z*fv|CpzYq?`)_0}dnEhwKKrBj^4*NwgKwTc`(>|skJmG zcqh!;Y&!49b_CF&AWMHXpDrAPpT*)dBI+e#DC0z|rIYO7$p-n{M{1GF9vhcaHZE)u zP2*Cs}lcbdAr%$6Kti?4~V^QTT{f!0`tTi{2 zY&?!}9wm)DT9jm;O_CZL7Ys(ZPGru-sTExQcgjb!$zs*4LHNxk%SkSaeJf=7$;PG9 zmZPgSE*CkMX|3S$m5s}F8y8O-m&=@sS0k6E?XYP(){KD{QKhvVUgPahusg}TN7rDR z_sIIx+oG#}hiMygBHC+rlZajWL6%^gBergL=J&99Q}|I{`IraM{HO=?vSW&YX9b%(}vn3d})aDA3@ygKZ#*=mN(CLDF z_@o-oIz85qt+wHgWsZot@OpwQj1CcX=duzi!8!aW%>J?X8StaL_4E&%HT9!dncw7@ zH7)ZS9GNGzMw753=;29uYr=!GMk#YW#(LjWEKB;7_p8 zx*pl0rtirwX@?y3M6Wdzb7}3MrQUkM(Lq4u1jJ`z{~fMey?8N`Ew5hMZLl+&>uRu9 z+-#&acI`%t7=Zv1$#z4Yd+xb2XU{Vvhy@hF_VCj(@dX-WG%B7w?!R9@B?R-n_y|+{jY6-s5f0Z7k|#eWr?|K%&0r^r zik%{2{*cfeBfubMM7=kuD3f^%)lpnS#kXLf@g|yVWOpdnK>OoEHkiC`AvJatp7t~q+Jq5 z71Pi{UNkDNo;mgB!6Qb&Qx`7g@2@oqVet{_v}uz^44!{9E4V|yO{ z3J2d}K4**}2&tlx$vNh>LuMI;pZ0|ZQH(Gt4`0qiydJOI+^gAFaxdrSUuWz1SM%~N zUn?%4Y)E_8a_@K2FFoD71sWZ#Gq($)^Pb+ndzDWPbG=KG(tu{dXA z(V4S&oGryQ&X3+I0~}re8z@n`5Kpndub&N$>>4rMy zk(`{6keqz#)T#5wj~ze%*Qrz3H1)Ayu|yzvh@ao!!J9T+d+;+n$E|)k{PLzvh?8p& zUVF_X96f5p8oBV$LlH{5c1cM;{~SyvG6^G(h0uP=rvpNKKkXaToSjAt(y$%DBs~7O zMzni(ghzRKetvnm+3eT0bC=F-JK~uhZzL-S5gtB1dcBWN|NeEp0o4J%V*md5#USM5 zARe`wkyN8lQgX*Sah8s-s^Eyi_#bu>hr&3`Dgy#6u!ARdYVmg68~V5p`WOd&^ky~_ z`e@5qTGGXfDM@D#ocCNpLK-enr@|d;Y9tY{%3*vxE^3!*uRP2;O%P z`R5D4fog^N#o7i3sB9NF&^Pprd?Z7Vib4@ya+c*0%M{qSV9R*Ra}kgdk4I%?<>VDy zzjp0-(XWI%+SxT=XOjibm{g;&Ro6tcb#udxhBj@yd^@)H@oBHud*TukMa|sG zYdN!aED{>qxpku#g9akd)?Z|QwR2SL2qUUBw|BOc9C!VVl^hu%-$W51l1k$+-C>jh zH>K4Y@PL!HoI~2Kp89ol$ZN7&ys*oi>fFjjblI=#jSBe66+hi_S1E;px`=-pob>o0ihv|%_E1_3Rru==X25tQX*^G$|N*xE-~{-*s3s_ zpRld^8Kj1;NZ25Fv4`_RMi z;<(JT$#Oqr`6-bZilRv{6kL{;Xba*5bWAn1LT4=>ACn?!tv_bhF_n~okqIsn&(hcIIB7DT0p{z| z)4*W~IIISTX|N0I`G@`)8AlQm&m|?DIh*iTLgMMvxHvq(#{Qi4?gNdCPFeyz9kguU@@;r1JbclcTe%UCp)3%**)*vSmc1GNJSeHdhr|oSY+s zz`!8O9LL)F>f-AK7OE>RDhZ+wI_=x{@WT&}4hs$%8S>aO&rXdVwDya&-~IXA8{asX!i0`OJ1V0;=`&%`qA>b`PQw-KEOa!o zU;NRvoTsm}Up}OLbY1WiIx_1NBy?~bG$drqxQ7G$yYw757{RV6mSUX{N9k%_{`Jy| z%Ce$@Q2N-CNf8t-y|du0cSj@A@KRs}EEzrqEUJcw2`Rz>VJd<4NkrNRs$ zR!A|0nhVUOmTs0;q#g0`QK5L=A<%3w7Z~xqg)BoY3#Fa$@moV-?K+x^%moq9dQD;B z^}M`-{Nj@0!lEl?Gagf9|E?Dm7v|+vF~72~xHK~}D-%LeI<)T9)?F>e(|3ZirhG#*YJ=%4wmmFMNToo4=#DQi1+IYIV zI5@P!Go&ugjt(9kZ9LkvX@fT>Fy=jj#)pNC9o~&IFRiqws++5gxxEu&lnS+A&g*fD zUTcqynho>g3*>^f6paYf>CDEsAf+ZAN3$!c)tr7Pr_bW_I!<5NNN;P$%$7B^M->~H zJ-T1pg_|*tc^Z!C%pBGD**p!K6})+$<4M$ONH~)xJA@}&#*>v;vRdm^(0&xOA7*Zm~R?uPC)zevUIYv!>6Og-kfDV6@grqTq~$B%T;JX zMMX6=xrLX~Gn1}fy@r_1bOE_^cWlT!R^ZaH^8k~5se`je-v+UvyfmVto1444#p2>t zRa}V&NbM0e+1AQ3KP)@&@(;wpWms>$!$>|^C^uKX>hbH1T z_Q5tEu4%h3`+>Fl@!bUYA-g-XcJF+FwfhDiTf1+`VzmE(c@1+l%BMBTT^E?saMi~) zx@d_q>j&mDh}{JE3{jm0g@nErn9J~(4_pRK+s~k7zJ-<+P=7CmpzrO$ z74B#lZV$K6lBMW-d-#PrT8`TTGPGn$ZmJ>gH?1N2_eW>uoH=~>aCCI^frEb>yo)Wr zhZ^#)f4#o=z1J5kSdf#GdUh5kdTP_29o~B4 zUnfq)#2^0i++W(DJ}Ct+bxA#&kbDGvfYIK?30?V_!m>&ilYPRatkO0^{G^JaB~)T< zpVIpa-*`5R4D==YSl)BeLZd`(*>ND|x0n+_^gW`5bu$`6g?2(0vd~XjPri>D1P{T- z6bh%jGy3}1wO)S#`u~S46P7r>iNamGe&dfZClCFqZE4)f;{LKv1SZ^!;@fX82!=DU zR{G{mn0Dh>^C@!{9Q=-2cize3WiB-*m{^N{N^nB-RCN6qPIiMMFn>u$ILh#ZOQovX zy6Yv{{J{0x%yX$%@{4n8&_NjOOUu!#_w{ggC^gv+=-knz;89bI6m|!_*i%S zHt#k6z}#eP9W#Oue=e{;K6mt*??ho68?!xIjbq~j8k48nqf~*T*qLp%|Ac*W zA(g&kM;~7-WmzR1g&aSI98Uw6(de(kNFaxzYngbuJo8#nWkYzIhRULndPPy{ONt7! z5r-z5#h&R@UvKYB<<&4R2-)pyUym1@BODQX`KQAHRP?=$O{p+!`Vlk;)9?w z>^qUV(VrEzrv$@9PlDbL=C3o#)W!9qT(bq<8vg4-Hvgs)096lvfgu z`n+6xfDVV@StlN2=56r^J-}W}9cAi#`aFA`M*8{F@dpt+=3xBkzYhKW`|pSTI-Qt? z$TDe(2&>RZr>iL=Cj_cy!MMw6bZ%}A2w+r+h%yxjLhI;e>ReW%#q+2s%V)CzSMtjW zGcR1YkXcw(B_fiHScQ2uqg(+d4&iMbz@vgNC`GWHQt-f=4s@>8clovGadzM_A1e5Y z$SGtLtuUL%e8>U!yLsE5oW73JyWT{9s*%2VZ+k6|mr>7&4{+kXJn^R(vDUl4g!=R^ z)F;-v&qIBB4)v*HSy_eZ)fU5P{!g@+YwlLXa*T4mjCn^>PrV+MOqLtU^6y&b0!vCt z1-CZ#VPRo-{B zPmbNSIe|=xonb0 zXrsSd<>wwii?u(L*WOON0&e6La5Jxfe>T?M7U@Gcy*sBr&FK?3ePt`?RZf3})Bnop z9XNfTR?rXS^bd3T$((*Ir;l$1eKx0W!|8wF^l6;l+zR@+oPH#ykK^EFOb*uzCgZAJbiPTzxPVH>AE!s+|pOwVS_n$EUqGbOH!KAck@0hr!V33ms>&q zcjdnmeReC-AHwrLn#(`w|4{cGa8Vs=yytA+r7gX~(m_xW8?hkG5;ciwCNaI=MDI;C z#pLF4SYmotqo$cejZqVOZ%C|IK)Uqads%kbg?;}yvj{3>z2&{%W0hT$|ID13Z@&5F zt91P9==d-E_wd7MPB4AY)&v)_(AMm9BHEg^4Mba0R@twu8J2F_=*T_k$P4JmFVKty z#55mr7$M|=e8|JDNNhE)^Ry+kClgS=KAya#BWw?E8WQ9)+DS(7-W2}}#c!eb(`Mk? zDBhFe3n=~q#n+mFcc6GZ#S_OTUXJ9)e|Z2uEG<{_w9Hq)+!@c)@_L?@_xIEC(0qJt z>@_}aAg3_+_6c$f<9#eShw=UZIfyAAANmeeN-j;je~uIRw1Iqs`IdU2*AX8V6Pm-kfKTw}jJxK~IfuqU_xp2_@sZXB$HfQ{D*P#Mh>9tsk=pR(j3b_c z9R3x4ho;s4h z@hk2sWTU31@6fHz<69-hUDE0zzSS^Os|Wa2yG*T~81UtcRly-`q*kBdTa7ohdLQ3v zxT)1`e5-Y)R=1&5lQ$>AnlJv_t!DDRk|cvPiQxGat(yEfWh&;Bsa1R&kUvxdmE#fw zdLqBXAn{8f3)gUCB;&+3Gw@Z*CVYsURmrO8o}Z63CN(Ud!~JIg?mzPwma)d&vUIcy zPbsG!{Zbt;W5(vqquyu=IpH~!7rupij8kLdk|noradF~=frW*Ew|HF%AoDQl-DCqM zV$|D60FtdLN4bU7d z3Rxm4v?Z5{41CRLsZ1u7+Sq8dNiNO|OMLg}UwnYK68)0abSO~kwWB85_)kgA<;`Dw zv9V?@YNP*w;3Q9DEdI_jF6Q9g)Z52N-+JrqhkQ!6z^{of$75;Vl>?PiuGaOHSH{ne zhfmz^{3}r!@3(a6>C;P>#xUQ(N&cB<_;=xBk1-Q4(ryi@IA^LYCi=F4v47rFbqr}1 zo-#fqT)1%me#AR5KPLGd$f?4^*OXp3fBr;q3oN)zS=+8$$<38W%HV%lR;JUzO&n!M zr3Q^QkyHTd))_>65ptH@4owPWV))i42*sjb)b`Mwr>v)j(+U1$7Xu^ zC=_{Hku-fdc?lA>1>jHnY%bSdbttEE%liIhP2$IkA~_Ab;}PI6T$TdbUr=f_henLBsfL_gFg^os~X z%BesgI-1&b4Jew9f`Lpg9D8bNYE1IQaC>z8d0rlLq}8!5VYHB^DyWy4AKDyE9yC(? zc#7Xa@usp`76bSxSf41Y&s?m}6s*q_tPlJF$k#Ayl+1lFEOc{Gc)e>}F8T^}AHRw- zz5#tVit;6(LeF&-`G%hLK_&UFFM_^+(K4;9j1A;pVy23zV>0mCAbEA(%$YOiy%x*- zLJIQ1L$i?^qRRQO3luQ86yFV-%!vJDG;OlMh4_Zva6*`+4ncTSIXHBCpeW<8>lcVB zvx5qh55JZ}pI+z5b>KMp=1;z*kZ*z9!0lkLEH7n{SLYn5ugOc_=e-cnL1bm&eK`z? z*@f)wOgvFY%uj+qMYbV-%eWI%^f;*KO;FKYprVCn$BynobELcvMwjwJp3Cdx|}?CR_VmJDkMS}5hwZx1V+8SkA?Z7 z&uC;iJDXa1?d);#O4wf3(23d`EGK7qqfs9tlB*+s`6W1bT#&n1ZtDbY;^Jsy!M{DEt)`HKANymBz`_c76Z-_v_b^-c@hB zv3BkHACga=A+E3&)301Od1C$gwQK!rx1q}Du4dmk@vxTd;9p60aUQvA*RF+M#gdr2 zm%jb>BN1@+PPDA4kzfQ0D@jjBhb~dr16NC2nwXuUS3M0SNHw^SyhQKk7{>l9n%RlE z;DJwZ@~2*S^UXI`yvyp^)se}``}c4E{n+Vq>G_!Pt7&IXpWM2A|NhjXbYv_(mzA1U z3@NE1`{*J32vc27eeKDUC(jfXsWpvRn+94VSwL?GtzF9kT3TwM)1x3uN1Y)*AC;ltU?FB~a&j6P%F38J z2qtoCnLq#$i&RY!%M@~aA%fuykXD|)AycEHqfehc zT`cpR@ie3^#2HDilf0%($=k~HOJ3Zzd{VZPM<#A ztuEuxN|XG>AyXuAORUm#LQ?9Lc2-xHwKX6gV`E!cef_2El3ZjFQ`>d7YvG!p(_xYs zy;jqWj~rx4;+I(1Bkb<(mWeBTeO$_S?N=k52YrE@9 z%0K`JJ<^RLlg+Q=WikbxAHrBMo&az47z*C((5^`)8`@f7f%(SoRa3AV1q&%yPry{) zq#EYVW=;Dprtsqw?oZ+K2KpZMKJ^sElXzZ?;xmfBLh;>Z;4LWLhvJV=d>O?rFaz&K z@%|K_Oz|>`?-{@kryrEm5&fY185X+kBN0SDD7!%PgMD`W`oUpzwb7V%Q+$Lw4QBR7 zEX0u~Fn|O;LPEA1gP;)-ebpGq%@GnedPk3|BfgHe@nd%(A8z|n zeOWwa{~pZ#Lzw+|%ziv(-?ubBKQ9+fqd7UbusRo&;j6SbAKBz`ax;jEAdA_>CEDXe z#UI@D^?C#1q*z>&rHSB&KCw5XGpB_aW8skt1Ra%1BKVm9-;(ZBG?B ziyk#DU@JPJjZZ)XhkLiZy}!y)YCj=17JM>~^||vEoJ)zUvvVTr>Z+D)4-8$nbm`Kk z{~pbJXtCVJYF6FGZ@>L^_2t0%Jg3}_$cg1l9@D`54A1VLnVkvItOb{-w71u^w6xac zY$tNMH5FiWC8g!nNRL-v*G!ZGjZ7K_ zhtmvyP%%j?776g=25`_sa8UAB?W;sw1im0OxH>vI+v7rOqqKt%p>%Wg@`g^pgBd?s z8$3^o!g|DEJr-a+qOcxOSPvh%8%Ep<9}K$}d@jpmm;L<Td3e(aLB-g@hSsnZuNYCrWIoQS?V-5JAt&wMXl z_2u7QMyWjExpVRHBU8QUSw^J!4~C_AA4>Bh99ZN6Ix@vC!wkQK8U7G6{32#}DQ0+5 zOJ(+z{Is*j(^|T_Q5HdA)4`-8j`Q^C^t^(+ywa+Q^z@5o&z}8dLr%T>7#ny8R##!# zA_M}*8B3PLyWq_1(dyu*#b^+|$Os^V>5G$-ORys#YTCM4u}oK4e!Zx-SD+3o(zpbV zS-tA}!$mrmfPjE-AFW8{>+5UPRCs>Nx9bCg73Ej{1Yt)TS6FiUQk&~jpvw8>1a#!+ zPFItccVW|qAAb1L*|IuW*z_6EkpYepoarLSedFJK_uXeAJ-o#wms0lr^6SaUdaXb$ z8f%$%{0tIgdi-lic#!b$M<0E(BQw3Z2tHw1dXK25Ut0FdR-_CD z>ga-^*49pJDQ}402#NYqj-9KmK@2Av9U2(`9WV=oR+bi3Mz;vt{74X)0{clY>O79>vZ$@fw>_Zm9%0NQsa?b~kjzEQ}4 zWFqjP@Y59DOW_^^Pd=OkBt1tYAdgclB%m|A)S){_Bp}Df`z0VUXC(fdj9Q^GRd0sC zNk$#be4ju&UPC&@Jx0K$9b{j|fIXPwl$=YO@7j@{Wb@$Z@@zL+09Vi6Mt z+8A061AU+Cm%}{B6!3AX!ZTzVs2m0!a}Xa%q^cvCjsZDLjOV)9^1-J)KSi`L5#$@6 zQ2B9=O7MgqF2hW`KbRk0ttiK3>(GyX6t@Qjf$|MnZFNO$YfDqBRwTp*(VojbbR3ev zl`B_u3P#tg>8QY%!JvA3MfLe*T?mG8v+ZS3!b)0_oLsCY*J7qur`IrWW7TPUuzg+P zmn@m#C{RzT5&PnHEy&O5v5A{McI;TEj`G9b|2QVh!%&-h0JrR;$rpcJ1-X*j2kZ!8k_>XEf`}*O{>t@1J>sg9qmhQz9>~Gm0AiU zN?WOnC;)1YZr*&VF=^7mNx{gXzv$%`W(MhmV1a=u|LLKAK7nJ$hxrGA1xDNz|Jb7N z@bIX4G2T`#-V?*7-wA4Rfs3t!)1>HdrLeE9ytWrtdX`m9n-*x9=)?-dcHoNw8*2gE z3qmN|^6|$<>-#z3G!?@<3|b z-iRG4aC-C2DQ>qt3%Xf0v17`Vj*PFq`YNM?WzUxp2WUQ)Ab})rWY1M6qK>rm_XCX) zGu@f)#8~D_@{-))-Ut!5B+mGl@fq@B*JdU8Ls+Y?$9eAS;}5e8JX945@)Q3R>I)$g zh7l`nrC8hAsu!_I0{G;9Ab`GNB@yV|-0r#O)zAeGKk&c<_uq35ep-m)4h!+Yu^gf$ zk`3S>N0sl^zfIJ4%4MQNE1+SI0#YR;ZSWkOa1~-twZkgdfwv*ckC-H{fs~M5n_G4e z<+q8I-D~r@kW&dlBm+d@%lD8}lRbVP1o5Z%zhV(liwQ-I{#HUIBKEbMNL8=wQ6vb9 znPP^XTSK2aCMIudVt=nCF!zC{3c?vBdFx^nqn$e)v^GsH-vHctcz6tj&m!>ad%ynN zF&%H!6g-ZC{V2F)^uCR>Z%+!(pzr_+ZyV@)xO2zASLAW$&z-dU$jv)gNzR>iMgO@o z9MbRpMUW{kq^4$Np&~hNXCZ9cEi4(@EoivljN=H_p*xk%=!MAvys{mMma? zV_ldGt+u|JMx&#rZf;Ia4h~S$O`-M-3Akx<=d==z zEh*uc5|iB4zfVYQdr}c&&_1ax$gkX%Ae0iHfa>4B4?CM6e4BZjVXR0<(YNf$2d<4% zlvP%qcPT-rV|0utG$_DkZi29cDWNnZ~&&hf3 zy%_S(!sEw5<`zU1JaguYue|flJKzNg0%(K53y>oPCUayfc}ndnmw?wo8S6iFB5Ebt zTQxTN`$IL0`c9mZ=yg$&h42iK1bSU9@}+1wq-J$(tive*{^TDt$=lA(uUDn2uU9I2 zda#+4L^<2h5jhPac=$LdXNx(uxESH=ruA{eY2vLCF;190NANKv!^*LxKwIQXB4)(o zU0ljDHC$4_)EKKk!-mvBb0e~EXt2LFA7b|97j^x-E~%f_p(P?Tp!Orr?MQ+WEhqE* zIOBPqf%rRn06jul!+BvM9ZWv!55TR6KrBlnaGjx`@o zcNcs9>~iz;f{Y~U=`r-`Ihg@Q5Dc`>zzOxeh90VUkHmb;#e6)0`Iw0LAQFO|Em=~Q zPjb^gE4KMr`oLQ5@qlN`9AYZz!OhAQCV53wI61#>^SDBLC|*GOw>AUcL-Fkt z-*_EfV+P)f;-e@&oZ|P>{@cyKr&7Fv;yOSr%*77D8!I*M(_JS zUHL9L_HNq!X4<{+rm+wE?Eeq&*X8W!ym|blxD?BQneQL{;C1I$E}Eh#-I08R^!c;t z^TTzlpK|(J7`%3T3w5*wZ#sXUP`oLw>VX0La1tjr9<(cs7g>LXq`ShAEGQ&Wu64v{ zQZ61mh+&&}WDSp$6_UCshKT;UDZ}+ywVhT0`0gCS!zz;Wa5F2L8n}6od_-D4^XG?xE;%0qO@Gvf&(4FK{|puidUL}@1gj1if^QN0mW+u@WamiZhr3V2&I0OM<~^1 z4WU$dSU;tPKpu&PJVNX(p^!(W;(WBi<#~{RW?4E&3Q0~R-U}uPO`eyBdq-{q>;Xgv zKwkf{`r||f1E~mpeQUO$0;bGz@nZOg4k@q6=gjHp32@XyB@o9TIT&6)SuF`@`}6b9 zKSw<4J(E$=iYe#)N5thL3mMmqnH5`>4iYbt6RwlokSJ8C97d{8aq5xf2~UPWZbuO_ z9`ohN2=JF>Y|2Y>iV6!WatewHE3z|5#zkp+7s-Us)xMH-v5q#rDQ|4Knw)uf)9?PD zAa)}GsV@^_k$|BYRnPYCk2TKiYwp9ZruM|+Zuk#WJ6+3sE_&#W`(LSuVTxF1Xk>G_ z#27Y$NfAyTJ9bJ8lfp)LATZxSU9PLwdp?@V`J0c|FlN- zM*Js#GLn4C#N&G~)hA-?w8Bw7hWLI0`wEDIglC}kbPIXQAa7K%>ZdspD>*nXkbWPb zIq5pk90ND4TtaouErjZ-TKcK(4)pzZ^t}SuJ1~ojFpEK@rCA79zj_rh{`vVOxrGIV z1qDcdhg8EkIc0_U1^9EdAP3<=iE@Oi<0dB5wzYDDKC7il7im$EQK3v?y)f(VDv-4! z^+H~5*5yIPd>YJQslJhvo6Lk7n!S~riT{Z5f{z8FIc|f^l} zHjTEsv!`3vZ6N9WC83wb91P zG+oV%vB#pnvG6Y z#+IYAFdKN{=|FCtv$5x9W6$}N78R9b=AFNIHLtL+t`R#8d1}jwYAf@x1=B9(Wf6YB z8gT1`3d7RF!^&3K!?8jGCzrs3s0@NulNyWNwVcVjMvM8#FZX^X z-afs)xuK_v<*Wr7eQRf5FYcl8ULm7HQ{o;xCbDbP)^@hEN;tMxjA>BF;TI>jwCG9l zY47UlF!mVq$d9W*hKW{#Uf2nT)n@j`@pzBnFUwA%jr{v%yIQpwznUw_T)#_v7B zNn05ExHwxw3&%W>1V+^p3%%y=%qq$9XO=$|6?!=qTs)h}2DP2R?{DO!-V4<}rJrdQ`Ct>>pK332VdJ2xiX2f?9LJ#rWdH zi%&N&J=!?q^Txj!?>FAb{KYYUsfnV!wzR$$Dcu{I8|&*Enwlyqn9CedS&N`S@9j$z z>3X!XPo@Ffw})Yj^L~-pFG3G2O&w$e3mty+uN7vPg;p@lKYI1^AB;tJz z)&Lp0UmJQxb)THk3 zbx+;eU;mJ=1H^`-1=Q@p_*(G^HPaD#uED2K*TGc%u#fI2S^tg_&>hLYQR?_ldD`%? z@TDY0L=3apxSNlK{~H8eJ{I1NjDelj%&Pu(^f|Q6-&9$5CY_Y``X`0wFA0Jj{M-;e zJsGq)1H0^DaGA;AGLx}Nt_Kf>ga!o$hsP}_Emf)Z@7cF|=jN|JRH;bf5V@U)qm(s@ z5wnAvZeyFl$m-$ekYwfJ^29?k$NP_)vnVj|;K3NwX;~)v^5^Y8!l?I#aPM9w3)n4B zJ~Th>rRDHh<>U{(|ILmgUwpI@(l1+C8Et%3w(?|IRnFmU&CLs#Ul`=@W(CX{Ufe#! z)Zvhal{A3_Ss|D*1|B{yvupo|H@<*;cd&=TBFb!u!7qQszHo0mr0*X{I57f>U3I#K zc7>x{)ZO0MX>055(A&jY`h+<;G7^kOlU?2;780)+POb>`miF?`mE`K+u;uq3zTNiQ z`qf(P!GkUy5iw&!LxPh@W#R<+obvMGqTHfl7)Ip>MHa5UA%4& z;Pkh_f~>>`0r(qvt3b=|QyLkz1$){;0BxbIzXe~~0$+A`loq74g%a9AOMeSdv;|i) z#$a{}!=1fOH0w_mi?g>Cb3iVJ-V$;#yqMa5_6~=QDkUBna|o8GIe6?@3GvC;pW5$} zaX;vpWJ~=3^n5?)`F_xIh>wqI&ituxX;Z0GenA02Bhj}6&cX=DVL2r$Lg*2Ec7~>R z&sno#<}ZvvV!ge4_cnS*#F#<!J5n$gW+Rzx?4RxUC>q?c}lD8%Ee`QlqOIYG(zgIHfO&8CXl15%(V9grv6R^*6>I zlBa}M@Jz@e6*!Pv*0lFDl-iS6ucEF~y$ z)eBB)Bh*{EyW2?+VXtjz&qlm5GwPyE~i8t{mI&MJrB#Hl0qd({#4A)Yr7)3aRZ9LX!GW0H;*Wx>wv;+t?!q z{W!U}N(4!^uf6>8t1mrwXQYLNucc*JSjc^EN8|qR)@$&5SwWyON0MaxKEtwbMdRsKKtya z;}9HF0s)eyp5J#ezoY<(q{l};{_3l*F8|x25V;*v)-n{wev%R5XXDyE zRd8$x^t4y%;CETuVdrU4Q7%V{ija_i5D$q&+J<_KvpjnsRVsqBvDo zSC0{F3sK3s9$j~bPLB?CLWhuf6QB+7jj*}IIU&hN7m~4bbu{<(ba(6YdWJRPgPQLD ze)(&#r7b})0aS%^T}TAO1Obc5#hZ>A&8*NbmAi+=W>Tz}VoNAiOtFsFVF|CgzTe9P zn@ExQ6se#{^0c&93+x|~)A{NM4sZ`8_EAYq{FqRA9~Ka=$z-$nJc3+H<8X<-2V{UO#4m0ZpEh2e&=87cjy)C z8d$O1ffXwoSh0x%D;9Utirtu7DM4V~Scaaz|2lT+jdL(`zqix*yN%A@PP%TT6x)7n z{)Wa{P;3pw_ED@A#rBSZ?W5Rsiq%o9X{Ys!f@LYzlVU3<)|vL(bq(7u1rzSYj#Zt<_V2%{?R)8^`MUkTZ~JrS?tRBgj8dPwUxLA(W7MJ*h>d#hb_niYLl*u5 z^J21)FkeZYoMuyae9z0Il=RQi<>OnAABGe=n=fPc39}xGl5bhB`(^_QO$%PXF`UH$ zPj^}7wr$(a_hSBCwOWCN!dj$hXz6OH@96S+0EtZ=o<3#4yFS_#^Xe<9w+M?2g#I}w zC^U3Jgny_X`S0MV(?Z9LMby<8KMI!iC7}H!Z&;w8gmb%!ID*cxosln zB$Fu=7IL}5+D<5u%cWwG5xH#Saw-S)-yW~847Zuw7J0SxJo3qoe2e1fO=jqwWWM}& z^BHd5nRWm>=?HexS?r{}xIyj54Ju()a}#b*Wf_I~i@E2IC!e{Lg>b^`)Z~;@L|Uhu zJ99GSe0Jv9Et_|pJX26gDp=?$%W;7!E3ZZk=d7&s%Z>v#E?eKO?9z&o2E7=;U*49M z)}r>Jtiqa_nvV7g{4RVJ((gs%JI!kh_)M*^aXaIor6eR2sJM-h0lc zRxr}8va;s1-G{1Kg{Tw7D=agAz}0K#JQl#a@qlUw{4eI|b*1ZO>ts?mD&oM7~Xj}<@R+Lrw8uGU9CQ^+5)jIVHR!a z2wdAMnm<0~y1U%vUw6c^GN>|737+#8sO9CES8#btzfvWLLmssIjB|`nSZ=tWnf3&} zZtFU~V`U61mybDl`a9O1%xx||y?OUvsb`z4i6?sSgcnU$H_L~|bROM;ufsQ7{oagb z*2tfXd8WR+prC}Q;{0*DL*#(a$=~09vgHvdUJv%z=7<&{1=34*PI@xd1s9&;4KPXw z=1#Ov^k~d4M4_G1a)Wow^ZbR+qGHf>PZo<>#I_um5Y;| zs1IQ&LOTbhUhD1`F;=N(WR^A#6K8}&Q0bLn&I_8_aPu>08amLWNaO^CK_0m(Dy;NJ z|0rd&UAp`uhfobRz+h-?lX&|n_4v}X!l|!OYtdfW363D}xG%;TkvOu>j1$pJ!q`W= zK)W`|Kzz#BYr0 zsyXw)+3u(Ib~!2@UoyqEk6FkCcXTwj^|||!k!u=I3%awV*R8Ql2gcr6Uw1UC4ID*g zYkfN=I@oc{SdTVnQF>amWbhr$9oqg&=p<6|HRY&GbLC29OLC$mR=CjXsRP?#t zy?pud#j3u--#=5U_MeBJMoz)So!@@5am!CXz5DKl4O@QwW_?)SNwAjW-Y`_o`U&N& z$?HeKV@y0JQ7CjB0uQG|p|zr=MJ=+hXwxWYk?Yq&ncUT}#=n!7Xnir`w=|mhK^<&u z)s1=3D(z-JJku{Ubjp;lFuz$#qU~ILus!|U?SyTJkn0d58Mu{5)YZyZ%fT))PSw?^ z9jvS-1&b^#1$u$34}{a!EwEHts)NV5+2DY)at*(IhKsGIr0`P{gF!E-J4o0H3qZBKm*g}d`Qmn%$*aH-6q}W`Fb);C=YuKT0%SUSgruq#L zTpRJxAF7VRbTmTs8%jGgMD-gEo@nek{l?8Z&b+Y6GjA+IuiJke8++p% z4Bc-8oxeyre}B+*YoOS+Yx6fWwv%G3DOOLhE);7R1zSV0Z4|4aSkr#fje@PGSPzOV zrC49uZ|61aa9WO9zJ_yHG7Bw7I$u%2&WUI_Jjac!}iV4rP%QlE2UW5QLuRwtD@M06nlzdwf{q` zX_fqFzq@F^CuzR{qx9QI$J|JUHUt z&%*Y3=2H4!DHkqWIGK{3eI+$DH9Zq4JH;YVd3HyK+T-d~O&tWE)SSvrc`q) zAo1iDm)CSa;<-|GY18^GTS(5+^&8WwGtTdWNoU{rMx6`ND>-W$i;hB2Ih>ENB9;5t zrAyTwt5;`{C~Zv#W2Jz^^Lt^R&jXNn?sn~~*zp;hE5AUU?%mJ?{K#x!wy@{U&Eb^^ zKQccvyO=+jRmwMC_y@k;ajM$%!3Q5~taO?a4-aqN#{y=k zVkZwlht4DdE%jqczUvo(R_8LIuMrU_K$?3zAHK{aJ4f5qqZtyJDl#(CM*e+?r|af(WFv6lhu@0rkls3_%2v*40~iJ2@$p zE-v04J&h8t5ZIc6Jp9IwCuzOH{k*;40pRU}Bn0C2c7>fzr?&L-6YC%cA=|f{g$QKt zEU2%A2&6Fx#KIeVItK3@-k@B+0bWoE@OtF8PY)#tCyW=HDGZ&MJUW(uCYC@BP5@1i z3eXQh4h}0Qy>!WZx`01|1W9wvp^dPL;IF?npFWtS9R8R^B*Ka3jK=>0r9>N-!?3#i zmI)@LsFbg2ZSZ;e@#e}LwwCGJF^TkW>;TNgu$i7Q^(;JFO)zi|NwB6?nMi(eCU0H!$ z5Z;3w1l`S0R<`!wzpH9%px11&cM_1yK}yP?pj)m9!tt4E?{CL>xO-6xxvxL%gf8#-g|5~R$#(-Z&#w9awfrn ztVkK^<`y(_{1{y2aMlp+W!>7U_Ev>Rc>Q=LVYW)kNYVp4~DP^#7`B6@6Ig2G)TAH?t?)4DWXlc7}Hw!q3>QDeze5sd+ew zt!V?XOL_7V+b&*W^Kl^(n{i6N#5Nq#aRc$|n$9Ed<&o!Ih*wwLlzy+SVNTWFqEw_n z{V$)YB;)x1;8X>@{@*%PkLI)?RqB85RFyY0+ac!<$%Pf*ZD^LMLP4J)UI7y(Ae~l3 zM1YS9{*OL>OeCetwl=k8NQiW_Q*{yOkVJny3$D1xq#X*nlu2se6l7k?DJi;g`po&X zD_1V%7eFIaaP?C9#Z$+RpS*arAdC5d<9vJDs;^###`wgxwO^&2xtN}rnOW6RU08$Y zLme{AM~wHfN(yRX9iReoV4HLud3!bljGY)HZ>l*AZP4MGCRtF#*npq+iZ#ouN!F&vV=L3&}0iIV2a8$Vt70TOb)LrS@z z=*6Gy64xd1yiMi{@H9^H&O2{BI?++24(g~Y!!(rDbrk0`*oOGncU4w&l#?{yWfdKj z)dpApVB3bgQpl~GZ;w{b5<$t%yL<2YToNkTQd6(DB86wPy1-}W3$Ior1vz@4z>Ct= z!_iWq^ze0Xw70W!R#?I-%hJ->-r3XJBQU_--jw2>qL7c z6$U3aXgw_i9W{-O?cD|wC%Mlg*Sv@oe9$D9HZExFYdRsTCn8X8$rGR5p=O<*%J63tp#!Lxr^L^FSwzrt!Wmzd}EuV94{ zo`z#w$h?jfehre#)JuYC4=h-4``vdhj*5oy=Cqlsy@a#b?$wACzL_~WnQ*VGX#dK* zO)PZCFWNC52lcb2tbWfSE!j)(0Keb(x1bGX(nYlKX>Rh8XB=@H_cG5t4Fk)fI~STs zCE~N40Wtr4XCCTFY=XV#du;Y~$kVls`N;9_qY;ngb?g9nOgX`CG9eOj_r@DvK?vv@ zs+20U?CB*_v*0&Mx`9i`WP4-W-)tqi*sE|ZYtOAC`lY(->_b;F z`7$RN>036P$w^I3PDU!$Gn=-hUrfibn|2|+qy*AHaS?PSu#5EDhw5M-QWNV?`5^01 zRV%d)wRH2=q3-7VHV>pPT)WoACD6U2IIBwH;o%{w$U0A~LtC!54zVWd&{%emb?5;f zY8~1~twWb~uKDKI%|HJ5?z`WAzxn5{*Nrp&1tm;M-?%5^nGGiE&`*NLAw~~ahiv+- zLtR5zhrTQ6w+`7_^VT67yE%_U2aOvS842Y@)RI^`S0ZZpy4wkeb*LQyJcF%6S4`HS z$zujthgvn%Iy9bIhg`zv&v1A4@(K)8shr0wTxjp?j7x@#i@jI^>yT6mt;HbgkWTF9 zr$(hgJH;UDP;LD+>ySc4Gj`mh8OfrWk!Y$J$)lQ)XQ*bxV?Zm;IZGax5FS1y)O_0WBuOT1YzB%44Ol8iqQyvs6f}ps?u5y4V^yjN zPUh35CrMD*);3T*Xuy&=8m+!=qP34qwDvN%VkGVlXHT6zoSb}O-I}d%X))hA?R}$q zWq{U3wpK^t!At-A?$ggd|Cmeo6bVAjw_e-as9_nPwvo));mGG)pqla|{5%%VejLe_ zZ`r&(gwjL~rO7o?Gt=lD2{qG%NfV#A z+=DO7!@?radP6H`BS+Zz)C+{eoIiIC z`iboHw96OIp^(nG(da-nAmnhatPx5H#)!x;BTa(FijmVFO1?g|nG%p)qr(`mcpPwN z8f}rZwm17YeCQrEPMRpcd+U`;g%^$;J9gQ83KGd=wWm+T80Q=3Tjr&p48X~9#rR;~ z?zPdzxoqd^7^A)MEy>ZNN7KvOWdjUqBvKR4akvQ4$#foCK6Ottf_BD*hKCwXeFr&J zbTLb#S{?_LF?oHgx@Dr|{B>>*UcTJt6%!NVt>~>N%t=46F5|Yz{)kFaXj_?Ue9X)v zbJn=9V0vbbA2*ipwDIBLVZi}@!69S9!r?;}J{l#<2IM!+)yRtA{>Z{(jb#j2R>UGf zeG5gDfGlvo>(59B>MRd8BE^Bdd3v}*-{lSGA2%m^TPJ51H!mL_AFt8qUp659jXa(K zCOE=zOLqo|mX}~>koX0nXO^i*266#zF|`oh-;aU6<0D)Q>;hXj{M$4l8{*xX2#447 z%tTMEP+hO5E-~q;gQ%VwM915_fsNxe)y|`8syld1wKK18F5&gfQ+Z9b{Y{$c8#Bqd z^P1|(1W7!wd29)__YaE-Z3wK0Bp9*QOkNi1;Y=-;YV{lLU4H6K#}}4wISp@dibF zG^)GeXBUPfO=72*ekPd!UPBASO7%I`>u=y>FQ5hSp+g*Z;zSHAC2z97{z}|YpAQ<1 zd-XFftPICFXQZa4q?~HiYMB86X{76#xU^I#%+8*T`~gppTEHX{HP~F{1c^$x{jFDD z<%CN<{QUDov(7p3>#vuW|MQ=-nNp^d{gnL_$%Kk;iN&ywV-_>G1m=-gR5<>J@gE$s zDi&9oubHnCZu#cNMci}Gv}sG0I6B^Q&uCnvpNaT!oB(zv&aTe_?tSL5j7ffeixxRL z-hH>N?L!ZZ#vb~)1JZp6eKm;dwX2kgM2uO?fZxj`7YKDNdguRoK(Y=A-tdI&ku zJtVIJg|18x#C!21iGOi<05rNXfteD`e3?MV)})zOi?+T)Tgjl)Z$YQ4(blZ}f9yM* znvE1&`Ipn6IfsKOz6y}95pUTU85dGgMm}^c25A-!avn-cQlD9FXYJt#-vH4CR8&2A zvPUF@jX)tvR3c-cdBp7S>B{wg6&pQpXKz1sMkIRj$@==;yJMlhTx$H+#MO|?_hqA; zi@m$K1dbX*?v*>>fqt~j5I7%I;~U?7~*n- zE5}Rmai&K=z`1j;zy8)+e4SGmZND3Xa$|9d_H#e1*>HuBi7Nk0_Rig5 z7N?Q~3+i8sGML;#168*u|!3{s;sa-17EaJ!q^D;r4OvTg|AEGseeYTMg2hD0R_-Ku(W*fe2cfpH zp{PF5Dx|do#c+_2U!-ep)~S_!TGVUn_v9VnK)$vIZ;txSI){TPfz4@DCkUhSa5`~J z13u<;;e_ItIDCA@7pD|;i}eV{Nrn31gX}-|VJAL{op>*HBA;vH_~~qHzv9fZg{4>E z<&>P9a_Q3PlV>kyLL$yMi)u<2&!0*@bLu#{<^21)YKyL1i~6XkX{<-d1sWZ~(CCoJ zAnT-%P7$%+O|DMTwp?Hp1V3oM^u(j6!xF38T1U;&^XMk=mlM1zt_1 z#dW&cs7)*jA1)l4N9B$M@N46Fh6I~u#7*9-c(`r{lr zXq-coBdU#r_=T!q!gF-@5a=yI;bX@5dwTi>`FRuPZWo`>umB(b;IQ#Q-ejDp^o;C# zeTkklv;bKkJK9Vk2u&mlaA!k(b9b*{q<8|8ywZayg}9K5R`Djoknv{A|3qYl2f>TU zO_$hki3M;8c=1>~dFd@s!TV-X!C1pGyUh;%lcr6Z78z&{#*8P*%bW4;yNd(t#ksrj z^;eVqZ4W*9MBdeQYf>1v+5GPi1hE8~M-et;QcP4)KN?l2%_obo)25Y| z-vVh6>)(G&8{Slw^{2G4jnc+NN*k@$Xk%zO*o$JFC^nyBJ1N#@6l^)g)>G_ZiXBI> z4~&9!pxA{J`v}DzrP!_^v4{nPL=8Cw!ZRM11l=f;z zvGP%{pHb{X6g!_{CsJ&{DA@mGrlwpsQyR*s*lX2Pn3IVwc>6?Z2x? z`B|=^*gzgjYEDtKD@9w~gqDKm49v7WMK7Q|KTNU5C|2`d!4BuWRKVZOD(YF>&Gv+o zyV>MQ_AJ9b7T$k18x9Hg(IAG^*R$|(w+|t_1qoFJ_Pn@Eu^6lpqBbk`woj-?W^dGn-)!Sr=&+;%bK4kCFz0v@m!+?JO-%L^_Lar5HUQ_1uWSeyxOva1=F;B#{M zYGp-!R&HTweqk9VHV7(-OZinKW0Zx5Z5!On3UaPiLhdTB=*U5=^SN^;4j%q^{oWLH zh^9IRc883L?&dBDMEHn^%b$O~x4K(A@j-me_3X~rwRLO61Q$J7BmeG>9##MW%PH8c z>(r@n;}ng%KmGL6-F;K@nj?|a>8_dgML8hfa$w|L5IGl&ckjw3R_zBLoDc(Vl6BVa z#yU4{g+RITk9P0KC;x@2>f)^2S_PI2$F@+|qctRYIs^vTs8of88NKsSICGw~WlZ<> z#Kgp}_FTGB)@e|OXf+1hSS5xAgRui%7L}C^9UTqT4LaVLzNWI48lrI#7YKXB?B3jkwR8FzxUvG@1x+heEY7S_WXrO!qp_fx-KjE@ZrO0 zd8GwuX=(M{Np?|DQND)O^qo7i($fnH;LpMw#jp9;7;%S_E8bcGAHF5GCs|?zb9KJs zm#uj9#mD|mvYl-wuLR~XEs>e|w_9#Lt2d(~Cq z=t~A*<$$PEQMabC9)58;W0Do|I(TjK!0V$CFOPlUi)rq}QXm#Hk&7)_{*Mm;JVV{h!AX=fRNNu=Xj?oiR5) zGhci}rqK}bclTz5h(Bk1PW180m0x`oOO#K>f1sgfxCD0DGVZ2{h+*D`nnjA~EHVa9 zel;3yA#c6>%xiH_`oTY&*`2_=$R)%Omn3eOd2y1Sx6^eVUoqc+{Z3G&48ZtKM z5E`hP=EwTwm&M}AlP5>r_3%rtEdM9!7_JomMh(&z=a()_Ba; zOw(*Z{mEEt-AXQpL(oRzFmvU!dv|j)oK-EW^AE3IfBf*_)2E3)JCf}VIX%iZ-;9dF z=1*&L3wq|&7oS@EY#h{7a3yCpLc2HG%pjFT^UYM#KFrKGEm+{?bu;xmH}s_Nd^#L+ zavSDkHs&N86f_=l;)H4>XLjw|x_0%>9e?ahh7+t_uU0cVxCDDjILA{mukf3NfgUzh zZ3UKBe(Kl*!t_Qu_#7}7pu|4^sVn(pFnChDCzdmb}oIH-#*MTj#;LbL6cU( zj+y=>BH}+s?#(x0kol4pD})xmiD^n;miO-_y)GU)rPt_g60H9V)cgh_KHg9}AsoIV z|9Hxkto;X)|H?#$2faQtlsSP;QN$2C%cyT?#?I0hQST9a$B6{w-|^7nFZ|=)hhJQV zVk9%g;-DZ>0hp&6g;n;n?3sn>q{$aS{{qyr@#zb~wxjGo^YYE#iQ z|J*|I)`#w|f9`%XwXXC4{ZIS`%i_Z`1d#{+i8o8|L;SNJ&*W$e#8ax@vU8m^j6DVL z3&bAwi17~Wq@(OaVh;;pM7%xhF(l-qEf8O+H!=4limcyPYHD_VdRi5$?d{0E3bzQ7 zNFgH)5fOZTgpa-4gLuI9p4 zXJ2RMHEY)F&B-bk;@?D-S!cHp=eo1lHk@Zii(%*I@8joG{elJ7br81;;?adzw*i}a~(T63MwEZG}iXGn6fr-in-HV znH4LRcxy6NnW7cN>#u5@?_HsuDlRXoFFt+dz$LK*Ow)c*kI%6t`7KLzws!H4#s*H9 z0JYR)m7Rwl6u-W1cA|FTpC@A6IB&7o#a*;$$z+H?O7YmS_r*er6!yrJGAKB&oNaIC z?DaObVoy&eD@!XQN#|g&bn*67PYtqgk8pSKb+Qj}a&~t1n{@Xb|#{Y=f#Z0s9U8vsVS$&kKC*q zy{2xJ48L*3@%HIcK;*Z)5lnebCULwA*PsZv|7d5(z`=fImYna~Qlj0`fcC z!?35~SP{eC=E0UT;NxTXqkx=ygKxXn&Q3uk^CLMin!zCPmd(r|Z)3nW@OB3|zW6h9 zXlx3_dQfa8#TqEqdlYOd#ZIEwT#B`(SfhE^#T3g@tb}6OQLz8X^Q!2W-DtmEwBJ_R zuk$GVI#KK*ihYw}4^nKyHS8jAzdNwTB%+DLq}>7T7noC2SzTRSQCyInaU~7mX$6J3 zd62rVX6Ihc&dw<-yL9Q&& zgws}FN3YV|-QCKd>21!}18wOc%$A#9~;%5?vb` z#A3U72$Q|r%~-t?DVd9~D~=y`w3Q@TTJ;peepYMY=sW9jh$o48@}KJ$)q6yWZFarrOrlR;`2+A=y_?uSrH+j@5e&tG5cPw;ZcS;;g4` z%4l*32@bJBnm|L3(xNoY+~u@Rz4P{9Wa+oIv$w_2?~Y~;2c(*}#8YiN!a^mbmyVq{ zadh|YqgVE1nz!b;oVD2BCijm&{@A_W)7{n86}}JOGh%ZH!LvbXC6`+J-??<@qj3}O zdTM^$zeO2ObF$j=r9HTq^=aY}a`i6FC@=W6xc1gbW@`AvAUm0jwHxyDO`jR62$q>g zL!O+qqO#En86o{u-abgP))H-87N9VXu9Sy<_&V{Mqg#Ie?z`XfYD)83yUn9C0jCt6 zDe8sE)zPz~4YKK_?}WENdqiIm0oq#t+KT|~MS%8PXmIk$l;cPDAA#Q|v`|?Y*||B) zCXTtNmR-4W8iEykL^;W&Z19SlT(t`gKHdi*EBVy<^XC^WTIAeWc;FYNjbj$6W!~PT z3T~fOFejOMC-}j$_oz{Hh7T;B4?_9=|4{cH@KIG+{P=y-XHsU;J0ygJ5E6O`Jqe*n z72B?bRjj+Xy1Kf!uDi~giNK1k1ym3V0%8MHL`0+p2qXjo>5cT>CzD=ge&74m?87MVk4p+>BAvJ@io}uwTC#~phaHi@ zkggLM>P0+v>YYW{WT zsl)E9tPJ0R5JsQFT;#gWXwYZ$m21D{L5nI1`Fv)2Pp zuK-UpHk*2^!>(OLHMx|w&I(`%jRLONz*|805nn>KCw;|5-gJ|VV< zL~=NL+_-FgSd+1;p`kt@J{u$gl4!9(I7LMj6+nQBb^{Svx`T+(9i$&IsVu~V#4U+F z%vDiuHaFd@sRJqDK!JIAPy&<7A9(~gRUu{D92$m_H*+F*FYIuba@>WI)x7e)#U)=TdMA)WKLWSRIUBjPJnq?_&YqGm7ZX0$$+4BXqp$9BJc?t*1-pNE z)X|Ru@4upkKvJ6#6VUue3I+OR-|An zG!tk@671tJ_gFa0z2Gn?Y(he>Q!7ewl&C0xCJ{YDyW>!<{l zn1trGyvl6muBHm|K$6})G4T}8ck2=2=7VVG$F7Au67LEFiDWG^I)J!Bu`3*nOgWA&1S zQn1Guw4?n7GRG;hOG{pW-l9>roO-)gwBQbdx8&v3=_HNyqLx*QQB8nV_xTYO#KL${ zScHFUXh@Jd_Prg{g?gC1h2kX!h0j=j7|Yy8hlEUkonrFbxpU`C0o&(Q9A|E6g0@g! z)2er@sA$8=^mMnIC8)F^;faVBIja@1q5gh8uqF8V`FSG26$}PNOD0a7JZlQpEjSQ$ zzje$caq7Ra4(r$!EP#(iFh%(xmaT6rdy=F$Xlho zx zi!Z*&>2aO2hRSjJPLSCp|q*jOOL~17!&n>rYHz_A z(pXzpT?OP96i|mypus>xKp}5ptDHJ@=G^(yzo66F@iPh6EUSk_jrDMLCYr9+=0-e6 zjU(#9(@@IVtc1NW=6|AIgm9@oJ)32M`uEmbP|~#lMMYRsx~TU)h`QF#Jo86Ft1T-6 zqVKh=@ei9pDSi_c`un(}LW-O7m{D#@UaXn>=s%ajN198Fmg@}pQ1VLD=+UF2NH^)$ zMv@Egcc|6jItkYOhqo59lE3UXXX@5TRq~=AZovk(E_Cxa2Zy#cgR!;=YuH`h)7jeI z)z#UfmY6t-29?s@0Z8!hAZ?hqT1^;kx(OVx-Q7^K@mb1sG+R)m+B~kI!Q5P!n^sHz zFm(CSK#^4HI%n$SAWvY%(GLDK)W3kc!im>9qe~S-TyrfgJKfSElIZN}OWQ|V(5SJ* z4?PP!QoqA?;PF}DaU<|Jy`&rkyoZCzg3CYunqN?mn_o)K@l$Dj_HgjZSu(gqa;4t; z>l_#gMu6qF;7ft#Dv#Z|b<113QF=d!CTS0ic3x#o4l@1UU*-hUikkQyorrJhK--a#uu^i<$dGd z>J_~;^UL@pt*SQ7z#&GkXcLVQbjalprPu9ajncC@a|Ii@mW^y=Be%1W)kBYb{!5y3 z=4Igg375NxLxy5cE_a4-qkOn|OxCf^`L-;%FKIouyXl;xAV>(a{yCRcbSw903z^VPA zU+gM(3_jdv>Bxy>2VS`6NF>SY`z#6}Yj2s!K>LMOBnOuQG#wBx9qcJO*(YVcr??%q zlfkyIziwu*ZS0e52v{?NZDfBvr{B1SURXP<-tCzqJ+y}nPsSld#J>hNU=fP>vvCt4 zLJ@njm?>sGp`(?rztc?A2H-jx_pS*^EH89v5_Sbm~)op9P_DaUeCH2BuW10K+okC)j&~IuLeMZDIuc2EQ zuV*Ffa}wRe7D;6A)&6f*Y;nI8`;N{0Dx0~fcjkkSThCzq*k5Ne*qiLrx*=fm7_2k< zYZ-$rVxR1KVQo-4-S8|j*q;t=*>?rF35yJt$c1n7^jcI6#?5;B7~Hz==ozDA8QEOt zF`;s&u&h#ARIv$BgQ0M$Z z@mt{bgJYoDgeTP!Q><3(9D=9Pl4`s)6=L>UG8S(=M3|OXrsF!t%zQ-0`~ z3{Hc?KmBe$H->lS&=sH&Uv`vNQyrYeFUVIi28d--La zOGt?2KAIV;sjfC1`PooMh30G5vu?tGBXSQ5!RzSV+)gT-?1ps3`5|>gh$o zd6CJ{k>`5gyYFFpY!RMl8J_3~JW(W`XaSzc9~P9z^!uI zvQt~OeV;pJAQq&+g7vJh>Sa0*64+r5=mam*fqvw0ouR+NE&Ndha6m0kedX^LuaT4TNiHCV8>#mLP4)#x9-67^s2mqJQ(xS({CX8 z2n#35s;}pjMMcyC99K{orxz6!H6o-{WyZo|`o*miuF3tR_AEoP+8b zB&76oLqkO|-GSH%JZIVa8aFpPZx2^@KR-9O(Ozz@bO(wMe2S==SR!USFc42P44$GK zu*3d8%T^d|S!34i{re;sDfPnO;7X7=JeRA%VMd&1}kNsq`j~- zzlu`nY*1+^xN0c4s$0%YFdBHG&Kx`pFGX^$Wi`aqJj*LNi}6*W+~Rh+5$qHGrQPqo z`|+or?*7iQT=>hfWL{~tm*OuaE9LM+tP}+L1L)uI0?H%p7W_hQL2`m8NqqS-rYNR1 zP+n=ZYIZbjaVsyuWMK9uuFkOG8_Fz;FQ!&YUTL*ScDHSDlW^c~5bVPyEg0wH6Na|} zcEq$MB(LnN3CS!qL3r}D{t|LO@E(jOU+K@eI1?I2CIV}My+Mk1c<-Egv#_Y}W@Z|4 zXKv=^=H#Z{yav|~+G+4|kwl>sOC_8hg3R6CJqo|j(9l3>Ve+v9D0_zD_mZ-#pjd{gm)CNHk-@qsV!3-p*s;pa1;l2j_*z5oo^!CtR1pttu@q zFRym=y2S`t%OTCM72~o)aNw-F@BhP-Pd&M8VKnKY--&#DCo zYHs@ZgoI0Rmo8mOypnhkaffWB7h|P=4;p&_G`1Kky%;nWl$AqUX@JNwv$FBT2z$!N z$uBC&znPblft42gprGJ8)wkQ`T<;#n=M9-PMWFxre(Ou;hgx7*~yh zoyN)8)f2QKxH7T7?|2hId!=U^l{BsPS-C~ucRb5EBGF=lc>%W?7TOQFd$~)5>paUf z191=|N##(d@-g95w-}2oY^#*99uLZT82mt4LXMm37}u(0e>{(JEo$Tg6EyeoEE;J& zXs(Q?jY3ljcnX;^sVKSy*+NOd@hb0yw{@m>33iSE!On40uyaVMor4pFSfv44r;1uS z9YM)P$I?Py&=|$1HpI> zWDp5lpYpz7JcpC9fNjXUvg=pk_a3HQ=fny{eJiibEkn~xKQERwp8SO;Zz8~5&hPl}z5jHgAdTb?cGyA7Ej#EdxCh^at^c2R-7b2Kybf=< z$a^)kRiQK(O&0qO)u`<8wA4nS!qckov|^twQUD~F z^`fh-;L00?_rwu$6S6;vi=O^LBi?a(af_5R2=9blL+_@--R%3_O)mVgSuZZGC+`aP z5^3zgleb!KHi>kzoB7Qqj|%TBd&qJ(N>Q{HyD&FDZ{#b|xrrTALghF!+>M`b%Sj~1 zCdWCs>V>Hl6cg85?zReddjWHM19y7>ccXdlVQ``2$Icg(lou^p#6AhXv$GvL@dokN zZKS5W=u|>0iAQvXO4;7ruH#HS|G6Fgst-?;!h_z{+)&=Alj7agW9VHgR0!ifN{m8I z=U*i^+wi?5aa3~n;^DWDr~PLUe;TX&EI(@H%H=ahISb{}e8x64InRIQnP-*-o6(wS zAFp#d!0&Y=il`_}ZdU zXgmY_HI28C{GgoOYSDB&XyNRh7S8^pz}ZVFXV>&_L-O^#7QVipGGr5WFW0Isir5cr@WmDx-)O@84EUO;qBuWVD5~!r$~5+K|}HOy1kUQ7wo6JJ^6LY+XL$<4(|!F@^&eB!0n#X zV|*-ix8c`wz3)PJb^=*7AO~;_iw##Irj}Q6VMflkTwEtF4%wU0tOYY8m#GdE>8?eh zJ})k;E>F=C8G>It-Dhf%EW>!(ef|X8M* z!pw~1OvJur)z=l5-Aqr*M>0)80Tktoqgt=H($b8}kW#Odm#1EcizAm3&UJF7nmk%yZ+NpKh2&!+p{_IC=?VhRGm{(6LM0z zyIV_3>)Y{aG<5TFE)<0~Hq3VAW0=DL4JFT_k2I89x?h-lf#hlP5R*J+ZHO4<-b)O`4G zP6u8a4Lb6944TwHzsL1g?vo=tc{>ILCfFA*aQl;Dy}br3nGA*fEGy?<>S*sUkqH`2s8f)ShnJ_@SVf?# zqlO-!8V#Dwsq7pP{^SB~~;O=fZ9uVIS3&!G2v3Y^<$q30{eo>p2)6ydqprE7Ww}K~g})uY zQe0e&&V?0)14yc-2H)1=A4|(Bs-byP@+vE@q?$Ow+HZ~77a{0YcMH{Rk%Qve+dI0j zz1X{MQ|w%C$7Y;$B?co%2z2K+B692(aw|f}?a1i!0Sia^E9jF}qNE(j*N1JdKDLC2 z4}m91aKQSc-OMk$nVX+`@e<9LXQFX7jAuD{`A4O3BtO5llb5vS<<%ot)=c8C&+tFp zW*06jUb1ZI!s*i{dwRLJdieOd`7(+5*YXx$FeI?0)OObh(5AqX935pMSQC)1?2MYZ z5(l+}mzy(Ci1M@eT3+sygm+&jq{K#1dU6p}wHfOxO3JHKEAmm1_R<9*x>Jb6l|8dE zv=dt>n(-m|-+vdW0v$pGrTl05bYCSx{_NJR3zFkiqt=nfVi2+`fReYlCsy<1uQA}* z?2C7}N2bS4sI4t2&Mhl1DK6{ks5ex@3POKC0F@Q6BNWkob5#}Pr63Vdgifr2Ge{@l z?Cm8Gyg8j*DhHo7>tUOx9;)R2cEvY;5A?v}0y_TT3(Ewb6IYO^gRU3>-fM9z-*DsO0PqIF|h?3-5$y{!WOVw07-voHips_GYJHBAZ`CuHpJGFuwW!3geS#59$$N zee-azJ_#*O;f@~x<~NTB^HzDYU(A(g5>62anpBl~L(4e`8v&n7K&yCh0(~)wb8u)g zgPzWy8?4YAgYM{sw#u7U8;VuZ?m9|63sY)2xB+UZEuQ)olBmSl<8PHXtx@8)sppAWi)h{a0~DPKR*5E( zyh80oIrOVaA$gldRon~dgl2VZ0%xFEUF!fDl8}U|M@YIny!%tg2&aWcv4gKWGkdGJ zUbD9?JcN{atf2w>330#ta_+)~xD;x792_sqK;9crEa{)c*PGQPxle=ADl1v8CdAQK zVMN(4l;ZjTan|6~kS^|mad;SMUCgrH8!y~pGl`ETbhS0ZC;+|IDJT$fvZ&Q|LKic` z_l5=!NZ#v9=xS?5b~aK#M~|6;YS*L1wiCLTxn9+0uD?S@mld;==l5bZKK^1&V;=HO zXe*VZ#3bAKdRkeJfK;J*Iuvh-P0ZeV@BR1vZY4~>z%Dm^>HW4btChX;K1!K?bBUKh zc*H%|4w{#%NJ`4C>?%fLW;DaD__;^nr`EaeXI8g5D-;b#a@258KlXZgWrJ+rR-9SvbJkntyKmJ$~+g=>BmH@$a^o16C{{dyr2+ub)T85cpAd zbXMGn=SOt385x5>`VFNFm=W8IjrGRnmNuG^)McBf?&Ouw+c+>HlyZ36DECk)6|n8v ztL*ILpiQwvDz~F~E4C@_&ch8(eu5p!%31z$D`&~SSy5C}ELYLqi%?Sqzv$p+MnCmc z$N{%;c8A#ePYMs8GJDP>rX=Lprlx+H>1~} z=AAOW>0wb^l3h@c|Ld^?rW^?m|rB_}t-6F40=|zxO z+J}IxX5%(7*kU$rFoRt(1nf8l8^&N$8SDiHTQCG{D}#+-u%!%EHE_;WN!VKd$SMgN z4+t45O^}4SqF1OK?2tp{;4W{g9BhRgC`0=k5b{JQB+U~MV1Le`cG5EMq3on32dJI2 zs)>W0H1`#1Ck@ZxU?*MWZMBn9KMwVNT6@l5Vvr{f?1!7;VH!=>Q zH#vD^KYjJ~Au+rq&Tba<hfW0hXuXl%iEzy$cI3rlw`&{Pgp$zy0>xkt0cG&Yn4* z4{nh`-siRUkl{06ajB>(zCkTm4V5S^k(t>Li_&Y6=I882ja~Zin!E3N;DHBHQeb6O zWuzSWP3A2;eDfoZub-LKMw()xT>o0G5_0>&(c|BH*eIEP4{(Wx^*hq>e-Vx+E1Rnnv)AFVQw}Ybi7}Wp*;HS_jV#iKY--Iy)qGN;LBQGDi}& z9%Tg|nIOse@@>7duc-h?D=_)azHj~d_4m#8E!Y{2)InV0odv!R#6a%;Rk(JG9zcyK zR5nI}F=y6E(XtJ1qfjR49mykpvIcmY(Du;$vgF%(ncpLk2y#%h7Z6$o{(L^4bbpImY8{u*?DPS?a>I~lBhKiKYmu+*DMVX1GHX?11> z+W=VjA@tPOL`$8CoG~;?MBxjr_YTj1hMvM5{)IcxzGP2>hNdPbr~R;X%a8c^=9_O0 zesd^kNTT|T*CZw-UAY2ta{P%Cap!67{g7lfwZ6W#uD-qw1}vo2AGvy?dPus<SCpOjb z5m4$>gQ4Daek9~}59BiYj`nIDl5~st$<3{h3t^sv@tv4a!mD*iTAn(3^u$#UFAG2c zFQkzBPY50}Bylg`K@Zp&h)!tc?X!yEM6UcVs6BvD-6%$}KQWR^WYli#rMJP)b$_U;)}6sFVX#jy*b@x4eF)e%2J6gV&oWpOgAE%3 zb{T`6&tOv-Y!`!d8Ul8B&r78Og+HQ`{Kbg&5qev|h%4eY??hG(PU`h^XuL+zu{lvC zCqm4U2t5~X$<47wH_=!f2k-^0cr#rvmYcJlE*g&WD7tER713oAvT`C(=7BDr@WD2B zjOe%a@3A$=W^14vVhxHI>^Qc5XBcc1gMDNO*g6JlX0SO7Ry@45uVt_;4AzIi+A~-z z-W@r5BZCcPuooCCqQNLf8 zW_**u-ej=9G1zGg_Obsz!A>4HXPTpSdwy~$B}tYiSxU*0s2bBjY ziSi`oPO_4v`lyB_b{CU5Jhnv>DW)%D$pXCC<|F~E`sgSI`xAprWU$5|V3QfFFN3XR zuyqX9y%%;D*5D&ZN#8?C`WW1BC%ECl5lcx|uO(l*mYjV3dTJ_~RbNvfbn4pGs|g8L zFo!|Jrx|ya(&4zLS~mUDMGgSALimgm(B0o%jIbz!jW z?0IY1xT;>*)u5);pvxyfm#aaSv}$!wa`KfcLn_5Pd2wNJap919aFCDBnKMJGz8Spu z>a=NBhtzl7YisYiYed@Zbe>kEh4E$B_Lm@op`~R=MYfU;L@#1)NZmDP^yt9AA=T9w z6sE)7hNYqYuQ}I22?$uc z_}sbL+OcEldz_-Yw5?s%DV6E$5X||=_)b~+7l@$Zx&QnpRx1hC zVXkb3bb9PZ*_MZyW!!9EKR*UhokGSuc}M!_{Pojk6-AO=lIc^Y&vMZ^n8gZ{L_+lX z1NkP!`Y8A+wc1Fc)k&tr!&n_+sHUktg=G!KjEuVa8dy)Ot7;lDGSbk>R)V9cr;{h0dRG@LA6-!dfeKREB z4ou!R=;umS4l0JMvf!Hf#VD>{1}~u=_qE_9sQ;Rk6`$VGasApQq=Us@O>XJAiGYgh z=i^Yd@?6~Y^cygyT~9@0J#A22Ty}PA6=bT!j8gO@mq?1!jYiVlU6xVYSO!;TW`1#n zv0D#s*wCu89NXY+k&2W za7}zlK9y_?Bn7)IO<^P-WReMgjU=Dyg9-~PD_d@!PLV16BO)RKRPxM|8HVa=nQL%( zc=$Lsnb36)t*ktGU0_w!g&RP>(vdW_U|m9Izz2q&F!rL>MmSqd3Q?EHUXRMS?(Ttp zowc%2<58w^oUgp9Rx`%aGjP(RprD}1lLJSO@<-{)05sy(IgcMN@4%gE%ZQSLD5r8X z7$c(MsA}Mlvg&q7bu=*gdssmOZ0#<(ytcIb6ck^&DdzKHydZ-YjJ1p#m zH6OQM=I@VM7@u6qPds-B#?wRR60$FT`u@isml`z4oK!cKrXNL->mw;hoDpkFCngOK z&C>YL2<)+?Lg%q>$srUSJwf&mz2s$dLwt290ws1Mkoz?7|1FK9QI0YEi0sH88po`Q zLEAF6+20neB??}&;dD6yw?>Tf@$gC{4HxfcX|#50>oAcYhEU7m;Szwq<<`Tef|o!A zv_A6Jpn|`G3jP8rhyV}kS7Bke6tRb&Ik57=aH%5#o4@+_mmlujxntX#AHwrBd|q%{ zqn=?>4fqkNXD=VDc*FmKao8yDFk;%8K~I#*il*AyIu~~lj+EijnJ2FvSQuiMG#7#M zrC-g6;nLsC+uG|4iy_^?N)m+w1ZR?&m57lG@~x-}yc1uZ1jD)MeD3Z+oHT0A7@(hu6wX&Xl&u**fUi25zfQ2iHR9;`@Xx8nge`gr(FN~ ztHhkkm(N~LqTO5(w@r&Mr03tnl=!hmV|p=oG}m03A+%O&&CZUCLvBh%aX$RQH_94| zGsA5qljCNU*l%yqmL;I-3W175pDp#@yN!zl7aPc*fvp#iB_^z{4d1d@q7K-6F| z|5>uTfCO%&r(a0K#EIjgAC7i%qyaPSYUd>n&I_D8IRaU6WANkSEHc2=*#xr~Y8yE? z%m~Fgv@{Y&B~HN`7pkgww1vf?r!e98Yit$aDAb1pj~eAV?yi-Syw#vf2hYiO>~c z_YZdQ@IWDGPfr)A47LWj9Qfg#4F*SQ!r+votQNQKkdGg)w-=-H4E0S_R*O7bnC?vZ z9_@D6#4;e7H?X=>gi|_ORtfWhYcP#VydC5FkxbN#MG(Ne*f?? zIJ1XOMYr*|;gVGZh)1q+pW^QyzG%keQP^w4CbQdmhz*DAnABxGZ?}N%dixQ?CqR5a zyu-nryU!u&;ZjN}5~qZIjRz8Qu3n9cOG=_`9B*scxR)PI%X2g|aJ_NLeO#qd;3~W; zm`w@VU09h*X@kIAB94}WiR|3D^Zm_R-rcd|{V$r@1uoNCpWEmY?B}<0r>}3Yx3Ms- zObX^BE=jvgnax*}*{ErukH;SCEA+wl@*UcF~*6Ke)wqnel%-*?X^97zTUU}V_W?iV|aI3EM)ZPzG5N0tjQ6q zNiPArYE=!wpgzvGFsO*BlfuHnX3m_s^3g|T&73)Jilc+TsWi@uADSOHVZzLrVPSy_ zAB=SN3P4A#Kp$r@WmrZj{OEp&dNixmedR~`SXT;Im)_OEVS0$9kAI=1nf@P z#P>3|sv64R7^Q`S!_jWZOF(H8z~Lr9vU4jeOiN2j{O0TLc7O1}_K$ac4~;E@yvJ*u z;-G}nXxg)960d+5v-7USx=|5q?{{aeoqvDZw*OoxxO#*JBz?r|`9`yPg$>^^_IsPYJ-w;3lyGLGJ1V#%ziyWXGfpus$-Hscy8kI$I@Mc$0X3~Or}8a+JSf+kD|3m)f$IP_|y;_K8E6$Y5RJG&Yi(1@uhzc$uQ zqw)6k^qLeCv*y109{v42a{_1@;|lz#MkB`=&@I(a)w;T%+_EAxQ+_R0gl2@T#EPuM ziUbX;dt+mTSVDkr-$sLAd)tZy4J?ggV+C(3m1AQ`SpgkuJ%xS+bVM75z1+9e*SXXB z2bGtA_0To9l-{v{kK0P|41%DbfyH@jAndkcJvK2o(Bh4n&4>o51_eVLv+!D`NFo4$!IGsVveG1V09CCr|m(keO+}_wI^YauJBc5H@2ce-#Kq%Eq(4OThwn5;fMaFZY_)?pG3Dr?!F35P#NXZZ4&1e-Q}> zHtp@Gu@kmfF*bn64!pJp%}~0~buvw>O1OfMH{?HEj3=%~V4)gSaWWgEY-hN1==`}O zM-b!7i~Oc-h@sX^Tsjh~P%5PGnsrOPQIll9BM2q8)gRB@wJM)oI1d9 z=I$jZr9_FIUVkueB->)`Q07HM?YLM2i3-yxt*)%8tg1e!RW`S@w&Jns8;C139Q%Nn zh~Y zeg-}3V+T9yy+T3`9<*^o-{IW1%y`hVfAj5UzZcdCKZBnCn{A!{ z15XbYck01TkFo6ZFtO94i=G}@!r*aZx>kimK} zSdU&>Bz(*X-2zXh@jBTKvbn^giiMr`*4YfqG6;N6sIx_9L$eHo`5bDXSq4=rtXT##fN?4- zP>wPc`-*n&1eLbZnEuR$oSeGSl9H0D*38V}lFUq$)}%6YTwZ26WGDPXT4pwK)p)MB zI2L=Wlv-(1rNYiK?okE9g-fhp}3_sP6%df`std4jNV+i=EWz1{KeXLkoID zZ#c+1y}TS9yu3&dAnJ`Rh_3~|X*C)UGQo3&h0K036RR^Ft1}O)Gaailovlu0W@{Cm zzO*hUr-3^Aa$8G^+3ING;&6kintIeLKx?zQnhIWA7#CNl$MWpk*Vuq{sjtt;v8>em zsIR`p^5`7jdFP$8MckArQ*h{{7uE8_g#V~xM_3Ryzf7!7s@sAE^J1`GD_3m)=%qhn zfppO{44=GX_s~=&X0d$v@&#c|Jo?^{7Cn}zs_MigtWDDS^73<8v=HHmD@no<=^TYp zOpwWFVxw599I&&UY&BRj=k93o%6(cxpg`?3X7z3M0%Rq0hQ*Rz^v^YFpfX| z(=!UBUY3WkPE_Yuk9As)bqY_*xRF|T^V+49D=1cY{6ZS-0C(loFGtSC9sK6Z566Cm z=j`&4pMLuO`|polAlrF9MD86f5z+ptbq&=OCB;SgS=XVYo&Nld?=K<$;YMmY^`@4V zl{LkN=hvgG#OHg?6n4X=qG~d^L5tNG3eSJNcfXfodBG{#MD*;XZ+7k4_047SEADM> zH>4ha_uY3-)+naTo;_po7;oCMRqg3AWdn*+MtPX7C&ph%D=2S?l`Qlwy@Z8-kG!qf zaE}*gwxqc-1Kz(IJ;6(<4e0>+Oj1zQ9sWo(;>~bz8<#D{_WF(oIjnE(_qrMv==3wJNfq8Z(pdepD=6I{K#1Wb`rfdtgfW2vZkU< zL<>fE$m)vFWxLv}8arWZ@c580JA!DfR<(y8&hcP(_^2nSRIZ-t@Uc|k_VelJGMNiY zstk1HD(jLn5CGIzU#pPH6qMw$>7+-s54B{6we$KRz$>T@Q znmQg9%dn}FCcw)UKAueBCxC(*ZeA}#i)@9YyT@eeY;7^NV?XuCTzqNAFq9gC&?S|r zoRu9+F5!WH9{Aq}{Of>!9q_*}3mwA?v$L~Le|PX4@RyvLJwl3bqGq4)FhL0C%;-BS zSB`{879J^u@drES-8bHN^PTtJd-uaHM@Tl9Kv}Y(QLq8+-nElWU<8yTRECYl%HR?` zyW!8Y49Vw0Y1j{Cn;!a~&{?p-5cF8&1CRX)gtqC`e~`{*!$>G^VPhTRd@4U*IW)YE zmIg91TAVMKi!_3{$czYes01UT&V>idAmSjS$E9259pvX1(sFc!^g18y;Yx1Cdw~-K z2aO3JGJEyNDfn-oV49QtIVgA&DEL)M!KI}L*y#O1$*0m0QZS@s5%(4mkBDq@ONz2m zbFQ6BIq<=`ytdH`*N&9isdoKm+Xrww|NH&?9G$rI=+2Ch@ITofRxg~tcqMu#zDxFy zBy!m9m3fZY=PzXaPv})Xe(Lf^|MHi={4J7bx#@vA&(L+N$Bc+#aqyP)`zakE#fs%} zspzdR<;cj?&(DjhEqy;>BP3IF1Q(0`jm9)#S4Kph?1ZHfHjSH- zO7TXVI(SjA7^cZ!6UGT3Ma`_vGyg$!2BU}G6<0E3kd0sG&KTf)XI zX5-FaA}#YUB}QA2x29SAFQnh~95uo?#Y7K4=wu#w~qZ7;M{qo6fl`zrV5 z(+6f6g$I3TL8B07&J2Cx@!#vi+`XhZ zm%sVogAexqcIhxmlAOwE^YZ=l(?bcl1`T3_T$(x*etv%4IVs=m!JIwZe5j=BJ!bNZ z*|Vo8YEHq$b1c2FwMVCJDJ#fGich?5@`!>I_}CP^BnB-Z5B!SBIX_}&GM}o zI$UPKHGSXI-z^@6XR-626}@2AtO@oN7a#=wbV?sqRa+=?83$YDq(D^%^cJ&&@AyD; z{SDG|Rh5^QH*|1bP#QF{^2!RL;X`XmVR1B8l@y|_agPtuC++NlCuqD~sp&H~Xo6fW zQ`Xiu!V8?4tPfL+lcL5dDrX0b^FX&K@6n+F9%BMMU3`O_6liE8m2ff$`zn#i73tl4 z=rUiJ(>jgFsfBO7TO!A7+G;N5H9{_syQoxBiAd6I>cmq>taAT!VEzez3HgVAuYTm^ zNkp6!H=_10YL|m02&N#E+!T8j78X!J@J2FR&?)Iz*}1S)-MpEdaTpbUc~{N^q;n`- zilpZbPTV7s$R$W`LH4g-M`iZWk3RZnOSDk0{bdJOoprHW;^ViRgVX+Cr1>Frd_4bC zC$c!Xk~0Sn0$03N6h2y|vy(ad$PG>1hKkCXHk~V?-;S4iuU@?xh|5#XdK}u+?RZMDd)ssTuN`TC^%VoocMM1DJhOF4IVMS;cjv<%X_eH}ugbU%3 zpJ2qg=5C#xxUHy3?Bw7oAvF!UXwVNX`v)dVpaek$cD;H2vuN{U^y1#!AB!a^g1l~M zFu>hYUxV^B^A|KD3Z*(n&~2%3HAcCFLRIB)ZNidQ2QVLG{1n+0r0ks^6~U= zb47T7i--3N+c1K4CkEL18?g0nU@HduG=^{xmoNl1Rsuf~0b}f!h?YrZ*bAWDl5?`s z)6;ICczsrGW_C_?c5ZHd!Ofd_c{lTOb25)0X_?dEuX#~Vw@K$D7Io9i)owV?I0(&h zFgW{z2Z2awb=h&~y1FGWlRqw8e}q#1YJgU&)qPWtmXYz}iHeFA0C1hnSdVr&jQ?D9 z`a$w7PbTr)TnB7fNtn0DP+nY`doBKQ+Koz`r-$#J_kq~2uV+?wa;;@0HRdt%qNrU# zPvoz>0@N0VJiP{({X)2YCds5kxNg9r{t0Rf(QB*U{SM=&O>S{?Rybo=( zh>|b?!v_@R|M(A#aR#kDVo|X$To+^*RG5hC2eXb zuWPAEc|RKc1#pq0`T&G86_!_`@MHr@;a8TSxL`#^8A=V7lt9KTE-SA}L-3CorKwFk zErqIc>M@}Nj2x8(De_HNOcH=n1R8hL!S@~Q2aBMamxsHDhnt(TlZ%V1tBbRnyVsN< z@4;k=AkaY&_%-dI6a+d50>5^mWnYrwp>Kcn&5_?O6c&<0yf`Z>zgDMcZ|;=9!BU%} zcNz<+GCn=&o8A15%f;fcW62esclhaJ?T0%**lv>9DeE)N=}Ek`>-IMLXaD@q7yo&0 zfMZE~JXwlZ;KfjZ1d|0VcP&%WZqAyCdME0M%gHWiHhVcV<{`M4q~f-2$WoHXm7>m; zHfL`yy%SZ910&`xU%hDKAC{rqB8kEu%@0(o)sBvB1d_JK%9W^3cs`zQF`h3R&qsX) zZY?ccU21rjy71bSkyTV}gxXNR%i|Ig;|4xwl=(UHbE4MPrfBoC^b+mZ_wge0CNQ5h z<_*#hf4I@qgy81Yyq0_RS>3?rkD_6Qo8i|4#m9RMl8|$|gotF6@yTq1!JP#^IVSfOXJLaVSstFS^rC;;T>RB+;2X~x+-Kitf% zu(4tuoZ#!oJ?iZI(`B$<>0m3U$jQksuQ>Jf$&^J{v)N`Z^Lo*Vt9ip+SZDd?U+mkr z@BOVN)0Xc+_I6~Dg;tzAd8MTJ!hs)8Mq_#3As>j>c#E@#x%?XQtFPc(z9&qnjB$d; zp$#e4cfd!Go11H8na8{kXaAY^FY)qG+rW==R}bZU0n4hZmrsXxzKslmub}z!C%cY` z-tfT6W%sXr>Yim`Ht^@ng-wJ_aNToebL2#nl?&F2=CawNd?o7Q;zv?n+Le?p z$r8mS5@KA0kiS+u_fuMNH#Q2Xh$I)a^jZF+#gU9TYb6Tju22u;ypeVy37#l@^bg31 zBejHaSz145lfQ(#=r4vRUPK}Y$Feh+d#vxQg}6jV_pYp^=&Hc^UshULS=(T!t*I_8 zqH`*6!f|Yph|D4sVnM+$xx|c8EFv~h>90j?eW+7x|1}@Rl*GN;sFV?E=D3Vek>*Xx z&p!TU=bLZ7x!Y)rqM?kO6{ln`k6V_WI~!$QVqRjOc6#^j-KX+p6UL4*!tJ<-YkV)t ztTw+SM`ewq{CY_@aybNygNJRA+~)wSFScNP9|RrV5yt)JpMBxk`O>@t4sNp^i@_Po zI>TS#Dk`GrpZ3y28t-p5w|hNs^=gkF8W)UGb{FTSCm-8+BVu|W$X?mjU`7W^vz0o$ z9aWS#!0la}oShw2a;eJB-r0@fKYA_j$vpC59klWmaJUWo zYGG+3MDX^O`btP^W%UiU6~Govx-2RxtbwT9++=KNYO1ZOt*0S8*ne1~w)STDJrUeo zTUyX1q5JS6&j+^GmfuXliF@(P*|S$57^i0B6;>L-KDszL(uqWfj4Bov4)K#log_$*VHL>f2Ou{nUZop9PH-=Oi8bcQm#wF1t5RX1Dku$S~`>cXd(b z*Uit#`5%+gEBZ)9v-8r83uT$VeYtn%&p-cs_C_yB9Oe1(kUx5$28hH;jb_@T;FN2; zOht#FvwU*wQY+%hTorXSumraioZ+2L{|52v?ek~PA=mlJrPO-(h`|$9k`!_r+eHwN zQLO1GKAX_cB7zI!HvV-b*7IkQg5_SqZ4|DDx!If#h~*~{Lamk{K;&1FMb1DPKSTD( z{xn?ziK&;?J_fD5!L5IKIWilPNeAiXJh(-|6~W1^1jdPI-6CR^NX0s7(YLX&+jQ1q zMV}WCmj|gwAD8B(S#~wB@|W7XI^u!t+`V{>|NVI4waXv4f5nOw>lQ@?N~}b<&{+ny zDO4=-7#-;4tXA8jbe~eG(zv6_El%o>k&I0#a z`0zc;QTgev6)|(?EmZ5NJNsVw})tTika5A_GQ!yGa zm8lcpEpYEhP0Y>B6?Mra-2!7}yM>-YbFo{Zuv@6y6-G~?>gv+c>+n96R)ec4w4O3i_Tzl)SYj+lr zyr@WILo7nlkytU9DC9*&9~G+V)%4Aq zg0Kxs=X{_NFLQwZC$aJ4tmCh>j<06p_ra_DZ=b&~f$#zt8zH+}Z(*|DLdk9+p;KUMV10&x zp8rsK3(|%)ptClB_^Hj()q&b96-%u)OIy(E3J^Cy+!%BeSgink(t$<}G%dA84%mYB zE(iUr)7$qwJ=OEVfx(3q zek164@$r?Fx9PG!46f(7?cZNlN1Hq*W?w(_`5QZv%H=5P6$>6Sh`JF4c~&cmiRtZ~ z_qP3W&%gfC>-Pt0P>&RSNr%wrIy*BnZ&P$-7;6v>!7Vx(`H=$^-e~->Ee&8T)`DiQ zK8CfhYILe2M}8_O{$bnaKY#UJr1=Fe{_w%$HkM~tN*zQ2S6@G4ev<;-ik>&G@!HWS zcDAvE!_wf?NRGUiabWBA&t89F)0FPA--cY{k*aN|(NUDInGs;;HEGR4XIF&{Bn(e& zi-?$@_M896x}}TmUh&xKMdNHBX;^Ao9}lDPR{eR{74idRRrR36H?(LiM%zx~^GqUR zFVT3R(OOFEA#1llnLp}zFb)Rq`wzU997h=Mzs&w%Ufl@x!<;D^&Rp>kHAfxJ9n^H)U>11~X|M8+&$o^XUJc!z$_i;Id-cSV|djn*KFW&zi-7ne_HhB-5e z*4vhq=3UQkBAXBbt9CN++#)!85!;IE-`u)Ty-2MNkM+#FPVK(1%+;f0j!;$IXw1z` zN7}pu&IY*xoswMOL~wOe=^?cenclOkpcPgMa4k1SM;h^sER_zWshark$joVJYDY^d zS~ds4D0;E0YiBo;j3^yiJ)2U{1EnV&mI5SaprpfLo_L?j$jHY+`}Fj+;>8L@2^Vl$ z)bSJ`qi=z%z%1CVxeJg>fe~7nbpghU!FYky)rh=eKemvQ%=Rg8sIJr$8T{ zK=>CBsMKg@3uU=Hxl2bT@Cu(9cim+Jc~hslquxO@*+Vb+`en9}DP_ovMA#tNszcfw{S`OtBwZNG{;tw1MTo%F6N9A6p0yL7A~P6t(RN#U1b-20X0vhu6?w z3{3%e@aNp!1djuxFoaNpr*QB{NZ>niE6hOnS6T|XT3m|1hDu$@2nNeIsfsgoLR;=I z#dUR{GA&i%dw3Eg?2?~zThtM(zI|>*adEvLG`Yo#XOES)mR&w_;K1I!dY$6<+3(OIAcLwhY; zK?Z{&Bxd=F$6sG0=pZyuJd*6;b(}x4@3$gwpI%$Ib^bhgc@fOPg664n92*rC6**@* zwDjq7BBP>^7gSx>P*+=z4iz1pO{iIAXsD~H&eJMIGB^X|VA66Onm5BZAm#^IEbKWf zmd4vn?O-oMprlfTg%rsg9o@ZEgWiLk1oME2DC~$;*b#Gpi8jYSo?$$EsqYS#_Ht7COe3?OyVj~wb*mef% zF$8QWgPp@*Pcztq47O_s*wqYnIfHFsuq_O>dkENC2Ky3&{T+k7!C=n}0Xwpl?!Q(> zHm)5Tx0{XIGrT$fzXjVg*qlvl&OJk{)dB{~C`ygl-EV8$>MyCxS!tj$XU!%ma|Y*9nRB&|RpuOg?^{-xV^2GuJ*|vAZC&rv zT4DR|UmvzocMGsW^pLwd(F&E9EC*s7WSsNayH)&liXcgI6smWIbwc_IW1Ykg?>8Ov3 zt7{foS2owh#npGb;nCq{X?K3Uu0w{7f3*M8wJGdw3^WxAT zSDD_Ut~8%%6&00r2BV->boI1hopqetnOC^jQ@gp`-Ua?U`5PW;sf=k9jtWN?N6ZZ2 zN*-Rs5B(`yTRJ7YLfq31DWtte%!?%`FUB+qD@F9D#r|ifh-LOz&Q2%Xl4%FjYe!|F zby!*IwWG3-50!;l2~7@!EY#iA+SDirLzU&V5Fo@<6dG)a1u^J&dS#y&EBKh(R&5X(Fy4h)()IxCySLMI=GE4sb7k#7^)EDhSx)h+3X;kz{s3jJkRA9-p zvPcCK#S9;V zruk#|n2S>{PtS3FI418T=MHcD!ROvzqOi=}B2k!o=RP>>@W!=;U!!o+O%qN!mci%1 zPCELyTHAH5`%CqyY_%;?ec=!@?te?gQKUkHu)Aphk;3Gp{kX*ez@v zBbEHygB&AcZZTQdI8mnbS}bfZvi`UqD5=C#k;Ipz;Dr+jPbKJOLPcSx-bigL%?&*9 zjq&uvpO(Vm#7EBOf9Q$tAXLTgzAe7I|H$KrFmfmxIn_Gy1y(1wuyrC!q_qf9m$||47Tbv*#2j6f6dIuDBhx(_3T->jA^)JiAsfXezdbC1%VIMIJJ+{n$n(Inq2%sphVxnKD4^ku|J zpE`H$N>*h$=8l|XF5wUjr+X6D-?>8x_)4!nbSPTtV5dyIaW!Mt#EHTqYEV_$S(THt7_ zgrV`-9=FF!32wxsGb`Q)aTXqiKWf;efZOOtrURhq09{1Bv=h92cV}oQa`ns~qCvRN->EO&G3uce?nKD*s z{$qCe?~}8tI=C96*wt@4?NuLb`qq?aUdrv;7j0f`x?%d3r{7#N-z9vr(v)oaR=pM(!4Xw?E9i;%17Z@ta(u*p}i;BytYAUMfZVH)=b&}x0(p$Hf;9yP? z94fkXOBfQYmwCFV?46v{DksnQz!AYC#zak=8Zl~g)R?*R7c6*e+Qai7nYVnwqqAmA zpEect=;(=KFyQgDsUe;$>okxyHOP%+U2IMHSu5JwDy=?!d3O5pF84_*o3vx9ZKDKc z>9N%rN0~EQElGAG&(yrte*AshtrrwZ$ zK_66+^MX}6V5nDMRLqg6y@#%(olGjL*E@;7_Hh=r)G}SwSt(0wjKC~pMv>#p;|T6P z0I=FZE)L&}AV+N99Abts3-yyM-&xLB9RZ^J{`*NliaT0PTfRdEq%?9)nl~UcGG^+W zhvot9Dm4xs7BOn<)P*REw}Uyh8%PMdm!DfUcjoZH{x0@aHa@Vf7JamN!-lVPy#O;& z!nYeXe6tRJ>1P0}v<+!~{vgRX4&c*PWDX0eC`vCb%g-(^EhuWqZD^{kZf&Wlw>H<- zH5==zD{HF|SygP~gWQbH@NjExE+@&&><$m_$5pB|3UlAWTZ~460Gp9{l z@X+j;vm=K`M1)KjHGJ5J;E17PqsC8|IBC-4sZ(e7@ zR46={l8Sgf)DC>J}Bn-Adu&S_xka{{prXGPhZN@OL_W;e)QNJ-Q(AW$fIs?l8+9c(r{=U^gAZoR_jxd z23bg=FbVpk;r*ZfX5ZCE^H)E~U;Q`!>LdDHeFCmN3A1P>X3=;^(^yDTpMt`|f{ZMb zgU`t+&&j6<(}J9`;^M5V>sc8TBVeC{+!WV~5!>HceEq_Os!C4MR&*sX@k&u!ys#U& zL}RSWUC!*?y{oaNv2fhZUw+vYol()$x$nq{n=#g>wZIN*nQOy2$(EDlt*zxJw{T2U z7AJXgzQ0Q4KmW~mVdmwej2N<0x?;inIUzwqf{G`>e1Y85WREm2C_E%|&b%3q#E_r0 zF+_)!7XQLYW=-=Fi#?~!;+Q|4;UtGoHOXX6rw+&a0QxI;=~6g*0;3lwcLo|?Hq=|m zI?9BNF2ol*Qi)Cyq^Kz=sZj)ROs9#HT(4I9_^7L|$NNaTn;bMUd(vems&34zAo8k= zo-UA8D*3e=iW=XD^)UxNsp!KVaT~*7^>S zlfR#z|B$H*7b}y-h752ft?litm8oY>$R0|}Lu8OvvUTg>pFjWb!w+?i-7O7kN$8wK z&-~*b{{ZIIV&|&kNT}ZadeG8$*1r1q!)w+&9BwI0k46;&*(8C?(Qk-YJY+7PZDS8J zp)7{%7sQhH(dPT_zx2;{kY7_LunQOxV~y5Ql={YmN!EAhfLY%Xq4~N(sDOt!{EL1- zw@@Nt1X9=~S*xj=h(wjllpFBN(SK7^iU_=w0Tl{T7sMD?v5MComZ+(osw%b-X~Od=Lxs9_zr zsm2R`{Bgl3;RL?c)&y_gRt4MK<>er?SGtA`MKu$1J7XtM44FG2h{#Y@SfX~4x{PZE zT}w({UP=q-dWy<*K*>Bvr#109Z40l{I@ultlT^j)w71YQKUY^^L}+Z(M;H+r%}jII z3^|{borjz(sVONpcmDfu($&kS9yCT-T=<377K*TF?`m(wsj;%)!h=R_NbG{go|@+6 zBx4&<<+rgZ>*BEojiUfOHe|v?1XP9(oPwRq*L)%x@}c}uU-DNYEUq|txhi1(bCVFo zKtmzF6s&o64Nda!1Cn0c_^-$RV@@mh=#L5=(`lRn5a23Xy2Ce6H+bDg|1Dz-v2#=y zOX>`_(krXIg4J@b0EY*$$~jK!&NgNuP~sLm8#onMJ;`4&&ycp7s$1vJCpTABwJWWi z4fRd!4`L?^o~F6eqH~n0YiYQ3c~uMfTmFg#hs1zGmV-kkfJ0)yAsQOe{2-p#%;_IE zaWy-3#Kt~o(DRUhc;!m@Ukz_8!Yk6{sj2_{JZ6u4@x_tk-r5M;*yid;SNtT+<(G?!r?~q1UFWUMzxEkh@KAfhRvk$sZ(< z8hQFq{@y$LuI|p0-}sNK->W?o@YI8O>eN2!6Fhb5-%x9KY9F4uvX45Qr#^Ux`mc8Q z<+dGuwrz*MxYtYBZQJ3u$@+BK`}cV=e`PQJ%9_3_xA9lbymRIIQ#bL{CZ77yKI&d6 ze(DbO{dK0zR7c9Q=}a5x#}@h~>(iO;f5r3s6&?61w)S07%-?U_oh$ZH|G(vt|8~{t zyZXI6GL66Y@%+6@`tDuG-+S7fd!N#)g)Ho+g)l!s$$!h^^TX){7pYAvHRH_rvsZJf z$_j5?Il1F&v@A)voqGPzmAZrfVc-%M-8_vfyI1ty#XaIqL+Z_6evc6;r1D!CDVbY_ zM2`tzs&A*@rX;49|FL$}lPdrwUGv0<0Ru)nv4#^2_7o??l2ha>;i~6Wyc0+MjqUGw zsbNCgql=b5`NYZ?azLDU?D!4Y)MYVbi`J%*G}2(r)iANG5qx>3o1;du*M*;-JP9rS zHS2Sn;OvEBsvc;)%4*AurUv4zX1m)O#Ehk-BCE2ssafi4k1ml&y3LK9t(nj7%x{BcgXvjgbE zT=yScDsdPIZ9pryM(MSfcQI%I;!k0t6x8w!QLk117U&*=qHJv~EZ|#k;#;7WAGDz3 z>8tM2-=DuKc>Z$b`D<1me;IiGdgBg%1>pHZuwn+``2%qE09@U^rlzha&fTVIQuygz-fi2Pb1n{IQ#6`*&I2mw?|!)!m_N2>fGF{ z#=8lfz!&&*=cErZL4W&ikE0KGiQz(wPi~zYFtmmIw0_#ZV?Bzp2m<};snFGTy%Wz zNNDdQT&)?V2U@eCyuGzGmS%tv8qqzm=pMqvA@|0Y^w8o~1s4N?bYyWH?Jw(_9N9dH z_82_`4|iVFBkMk@3f=8K14XE!2L&k_m5%R43GGFDg*ipdpUGL#YClFk`jz>JrmSIk|jpE^1k;M}_-K|!Mko%MS3?PxpQY<^HMIA0>u(taR{kRbraK5|4bz|o?2 zygNqb&bF3T7%42A8WkojX2fW-ayunz+umh;#^j(AB1TrIS7PwwVu=j#@m7mfurBEC z_>G7f9v&6<(6XmpTwYkUWc0|$s0rgHju@s12){c`m?D*qRe5Wi#X2QM*3mB{%o#1U zw+k>CDCYz5a0eYkHUA0d8p)?$06V^ZJp!*^M`u6jKn)(B-5vH`fxu^!cZSpzAW0%& zWu$MoAt2n&w+tFIXvdBnKYso74_mkXxc8o!!3zvNXT0ieK@!qWfH2k*aj<~kuv@ZZ z$%MejhaP(@+NJ+6+NO(e$r20~% z#15YcUUj!j2@Uh;ih1@PD?Y(zQ;@7}xW=cxzq)OLSE zeXEc97EhhaQ%CdEOaC8H|JjDz8yiK+@YvLbth~g)hFm&_`qKuRs6S1-46DAa$NFgh z5uEB)zzh)9wd&%)c=BU{K?9T^L2EVIWx#`>gASXpDpASRvA zEm%F86zOcE&uo?{NoyxinVqdfDKodWcJ>efO)Mu9lAexMebBgZ+qN~d$&h(Y-qui&4ir*) zMT6t`IdkTWcWk)64Z$}*U8QC0lg5rs;#jGibRdq$)InqtLSq--XZKVS*GJ;8z~EpP zdXO6AdV62*x_kFaFUc(in1T!zxohOinKL6@dvT_G}w!M(9KeJD93T98K#_J`{{4mnXw;JlA%t6(o_du`q4!*9QUHnu?;W;3Cwk zyLJ&7pf6raOG!jZ!^D)d^n9>bemY`&^nOyQsfE~ykrV_r))tdgt(FUkr5)Ya)we$onN9|n6psTFrNo0>bDYs3;F6CkCAI3yr6AS7U> z*q?M4!UAmLUOBEU$F*tpdO5C*Vh_kmL9fhamt-1_X$fMdPR!gM*`OtF>zI|F*k*%D z8)IFmW2*{l3|F+I6VX-8!D@FcDb(?;K7v+Pqt$h2bsk!s2QCagch0FN<@aBH`8~PY z>D;-KCl96+<=2&`{QAkMQ=k5tUQ?T2lztEuZ0O9;DopdDTrEAUp#JK93Fkudrdb;s zJDi4D1*!G+?hcF@xZQY0rGi&hvUcu>p+iSJyau1K3>6nox8%c_y9l<-51=(pw<;~= z%(ZLNEfrR!bv87~5AZLGtge=PVR13dz^~X6=Gs|N<>{qt$a#hnBhlRIRgXPKT@^=( zfvI(kwfb47OSD_JfBq3vt9T|QB^H|46QYmKr=*`Ma3b>Oey(B@bcpz z-i&I5e=s2-)4`JG;UN18EciKq)s3W17#9b;g%-OTnL_LzH*L@U)asHL>r>ohnnV!C zQ`RR1ySB#1Sl0>@{L){Jg>UFX=G`|YLn2VqE{^zt9Ua?GRLP+nRiE09gw1BQDm(9b zdX;Jr@++yzZ{EnuE=Py7Z&E_A=T%XE`T#5I}?Loz@8o?lSHaAT9{lPI(VQj#E|d6 z!G}&J!kLwL^3dQzg`tZVFAgm{G+1~Xk+a8zBS#{0WC=G=sg$wA+zya*OS>7Gno{}g zx8EMAAf7W{#A~J}sW|ejLt~?Z-Zw8Z1wtq#GjHKzE1!AhnU#+%%vgZ<<-340@k zY!f!w+c)V$vD~2#@Lo|)^WfmUxT4GekA$JDqPVOQi(X|Jgi1L{LLc0D^gEq_722u~ z#h;j|cz1Vm_wew*yU^W%I=xg5?n1c|f?Fv^DrOpzWotwzCS5)Fxv-~;h><6WHp0(X z>TRBYahis4>eGY$Yyr(F>Dh&)6{WaYj7y%OC@Ygz3eC(kkRvFwDU``k(@7+f%4I@M z#-iX3{X@nk_yvCV-FKV6-*wO{co^Iok-z=+TZYX0z@@Ih!0?}c4kyVVPATicj$I@Y zc9o@YvcxMiWb^BNR}vG*fjD?;aL8YoJ9TkkV|fyiKGF$^L%Dz?y*kox;K0#;{p%>L zovApwKy8 zI7LrKdnbYcx;xuDdN`T6ga4z$oZzQMUYwxd0B=_Zr3x_%_-oLx!6E?`1H0g0J2IRS zBUFJB;{;;o05{sVZUN2-HrYB~L&)Rk)79wHCE)$V;C)XIYNTNpp~QwIuzj`_O2=0o zhQdO+<;IlA$;&A$L?+3STv~xE@3_|8lviuAAbpgnHqS&lyRbyagbeBGB!gUCA9-X_ zDCUBTqPwv)W2z z(G>ixr5TqJ5;)P0Qw^-ok~qY`;t(zIVe3!si1)gA(`34R2~#4wUg{PcyYR^;pL}6) zWaNq!40A4yuISeHg*)rrCr^SNV~e6?UxAPq45JQwSYJz4y}D?Kvs%XLB&1)H-^Qd{`PkNFez1 z(<_~Hc@wfIryzwJ!)eO<@1raM9LFdPC=g2(v|5HlLKNsLH@CqNJ}@epdvr47L1iR9 z5rf^_IF-iN%R$zIoV-192QObuJU@r*Fo*0g-u>26BG_ldiuOFV&Xe_mP1Et2R)1mi z{=2UsZacn>{JSC#zmK#GbAVMAADi|Nm-}uK9BCW$&6#1Uukwur9}y2kM2* zzKE96l4`wBkVzKd({;Ttpbko@mN@H$Piv5{MN8EE?y!GrHap~D+S)7lRkU7M;|m!^ zpIPXcNnXXLygRM2df{XZ8Q8naQ=$J#8(CFmIpZOhBnR*dyo2Htn;BwKre z)tOM00ZTxOfb1b`_Gx@Zn`1S^`%i7sF}d63JX@Rc-Zq!p+8k$VlY+HrtEIiI{)kqe z;agpyA(I~1>dUrPgL_+DX=@eW0X#WXnrN$8y{&$PR_F1p4$<7fUiLlzz4!W{R)4d# zYVB=RhgN6stp;oESZXN;(ea_Z`k$jcS}$z%1^7ryYW0FC^d7&%=L)?rf<{7ViKAXP zNka{%q~lNHeNTJ+$Gf+^ZoOa?ZSOK@Gxb8>I#P~LNG;*7AuK-iaX7U@_stq=7k~yz zgaC@b)h76UA_jXce%xv6!csK14}Uf2g(1GQf|IaBh%~<1J5_6xXiI@-?VmYaxZ^vX zwJGF-Z8lNf=D^#mAeAyV2j*r222<|lz}@WAD=B+(U~iiY|8tAjrW4`e-+1=sz}{?4 z6XkCXqs)3%Q3i)U>TZjyo}C;=S-cmW+o$U*F&fy7WADijx-U$%F4FdLd3>DpS>kJb z8GqaOJYKjub26UX#^~|LjB2eTzi4@0k7vhfMuXY^)Ve)dSM|0Yi z&-8LUy=!p1;4z-%>7CQ`VZYj#9_3&3?26@->v?o^-@V`4`pdmDG#C#~TVDy|1f8SN zwmt#p3+D2y&$s>&%_UOIqP^^be0$c0Bfpiw|t@n*1 z?f1T%`u#X#w4QD4rt^TqJP=NCqHTVMKPr$2HHyvz4l{wB6icT9hpBK6Z`{-7J-pG^ zX189rDw?)=8Pu72);E;S1`e}<_4lLGfx~pLwb|#D>BHVq8K@PsK@)~SOo9a*r(~?J zI4Qt(I9ae#@m8|o;QkjheD3z%%rx$9R3k;o2%EtNXIfa1CgKpd8H!2_c~CD3pk@>m z=43(XD8SbExSK$P4Wm>@l?f5yj)<5=b8XnR{p1t7sUG#eB2E16 zDwcifDZ0bjOmvE;o_cEDP!DN+ZZZ?I83ud`QanJk z!U#bcAosJZ6SztI@2snY2lA^rbpms9x!%oCTxck-sI4u9o>5#3}%|Dn(Cc2QhU)TY#yQxTzgfNEPYjyR-o z)1ByHX6|a*OZ4DKyh;w+PrJZh8RUViMP~725|6KK|IECJ=!L`aN`s-PwZ6I{H8V4# zzAJ)mQP&G+u#T%{!vpXwvV9!KaT1>1?HHvm;R!(CFs}1Cr=5yxzlA}1+jhJsh-XbY zHhZUG<82*HQ#BQtA^CrL1wVT!KA;Z8!y4-A5guEMG%9UvO%080ZBV^BI@;QioVC53 zuGKPA7Y!Tk>O#tO)mi+>Oy>sb31-fq#g{R|83p9W=XA!icw(Gr&FEe) z#zVNPSHVqdAl>d%#x($3m4!MTw{!9eV4s3UU6@~z*|+7+$5yi-KaXzW#0^cfthH4q zvGiDax;vX2IF-d_O{KYa&D~wy7CkvP-5LnqS&r1-p0;Cz^=Wy+ahSm>ZlB$~y>1$; zZLeFOwm#lFt5!=BzTLfh_uidf({b%NY~BV*_5AQ@_PvWhuX z)#;7jeE&84EMI;9^~OZN{vMja%q2ODjG4&Hw;iFRf}EDTi)^0mzGGLf-E#m$M5A z`|n!g_ZVOnGExIitwu;R5X!?ejJ*mXo^C0@GdKi7l_3|yoR^oIn^{~|R(Mn^lGwaw zoCpQb!9BzyGV;KI?b|Ko=V7uxS7zBR@Q8|v@(^r?f~u3PU5hd<*V7B?x;ZIEcR{PwUa%8Fs5~d~@d+A;tZDol^B;7pKaX=y&S>1&zS*Xm-dFETN` zmVIK5g_-?Hov6$}?fV=I{76zjP5c73qNvxRNqsTYUqF2^z5W8ihQXea!F|A%`#F(p zoF7Nf+dpuwD#;`#an9)}0luMEH;cs3jYA6y&_ZA3Nwqzm5--$|;>S>6pNc{z$;lU! zFJDYaOuTsEYq6;dd4u7U5}PEXtG$z`eSOtfZamz5PMky>`g=G9Df z^<-pbzF`Ya$%3p#iJ>@~weefw8`E9Q*t1i^Bkz`gyINp&SdLwn-m|XGHd6`2 z3`g8J=DY9Woy5q$+|gbx5Sxj&Qe{=Pu|&lGNGRm!Z@atWoeYL6SDKF-Zs(+ABpHvV zXQyV7-S|-lhh$*TFl|4wtvFO8r0c`ID6}iU^fmO681L*2- z0fSKeVw;p!bcyVOp_U)D9KF4lf{Mp!|?c0Hu)FH_l2hb4{9Uaf?*+aE* z9Ww!W3s0c}&l%?GVB0s~u!H#Nym_{t9yt<^3)Ixe8Ku3Fk=4{dDPE765f9T_aB#DO zPos~6DL5Ftr(@J=j)*x1UUvTZf^oNDxZA;azF^#KFz(hdCp$Zb-Z9EiWuzCurc4fS z!tQRC)iGu>M|x~bAyaJHgmOeXcV=2+;SyLN-Zc8^&Yff%C(O!v^idsy04eNGm?xi% zm(5tde8!$V@4g#&HV(7o5=m!|kAC-EEXhfn@R?_J?$j|$mvY!DF&j6=JA(Zy>C;r8 z6i`DkjJeSEp_7@-I^+`PP(6^cfTJ5KgVI{it|)__;31RUczp{VsQy*klznLHR*iqvsSUOm95BNKXy*v+i1(?kuBxtvUADTsw6wa)kdswK%iNX~ zRMk{hBM#jGYBu%o!f|UB2pm0~y1I09f-qfO@jftkdw9U^>EYt!Bn#)G0<|ELDs}NXKycS|vvU@mqL_jb{M3>oYait zOP9vSucdDB4@HYT@{_>C+9|w02Xohf8LPJ?*CV?Gi&S0foT(a8i~pN}d~Zb&g2uX9 z^az!xhyk(J*AhFiBYQO}qn_(ag_J59&~)TPpdj~Fr5yNg2^29{!h+bM-&o0pLezm92vkTF)^9iyVc@pgJp-IpUK3>@C_nsgLJR8akWhlU9ZU*kI z)grW@99LCgPGMDBWxgi^7ZzUlHC*1^0vdQE6V(u?{MN9&36jDO-a z!QApU?6YZF2}*;+UdYFW<{DK!vh zH7O@NPuwg;U60b6Cp^VX*x5IUX`WAViyJ7BAZ3-5rIVbMl8zpU1Rfv2kwg~w>Wvl= z=@O`oYPC@xdMul|4?44tEgs{Jdv+hQ_}Jp%3Ur=g_~K)QB2?2B6*3Ic=R%fK(Zz}Q zQQ%x4#VkNy1vrq#f-JlW8i_+dNCLlx_06*Wcwns{i=3wu$`dmZi+wz}(w0ZM8ul)H z9Yn_U&NJAMNPvbV0pJEdjexijMj@x1fZYJq4dhTD`aa82lTt6IT~0cADp3cu5|WgY z%kKPEDD&OBFJx16gzA@FTJkza5;(z!9};2akhf09pt#S^zips3%HJX%Kw(-6tIkiD z7aw4D+;fS#wz~G(_BMtz=ujF1D-I{uyf|jS*nqKPCQq6K`!YxTIcDrwOb|$kLA`Q# z818-$czFW1^;}b-d&O$hg<6z{(p^S|UE#0%{ zNY&=euf7T&DSQ2TYDqgM^O?VL<;qxB%eCtXuBcj$`@jlS0rzZC5z1K>R#lNJba1H$ zjiH4+gbeAiB)AUpMI|O^E5QK)8nwN>8NOk6cVvgV_owS_TK+WV_uBfh$zD3RN_a?d z`h-WNRoQZ~V9P(ecf|XmIAof8FuzQ}F-Aq0fhCN9V11@ulK?$adWC5wj8h2Sd2Z|L z@sa&|oC3kJvtcJFD9opt1#~8Zf%bU6Cl&Bd+EkSc2p3Q~G1A(Uq@)`)+qXwX(jHGt zOv-KLWNi-7(a{l}-yKX$5cv$4@W>;N(1IU0BssawV_tprnGrS>PzdildVS=`p}ulX zR=W4cAAdYkBN#n0!Ik2^s;aQBC@m?Y3IbFp+zWcW-{`~LPDj5-=7QA^-@`5jeIVQ| zI2fhm(Chd7^qt{?-d>NwT#3S(GzHH-7TiYj!8tl$eqavtzCbAuIH1Bw8OpW;9G9J! zpH-Zid%^+u1H_n$0nUNIBqDgIyo$5W{QB#!r~^6Hw=?^~>C=}BzxysM%-47Sepr*) zOP8+XwQ-7e*`S!1m?2(Uj$C?~V_sg%k+phd*wC4%=>pH}vC2yGbY4q-cQVXIWP{}F z+04wvi{Un4eSJM7Eu7-Uub)Dz*pX2i;EM!Hp8?+k_3BBz2=S+Q=^OzIvr_@5B~>E9 zZ(SX9$r_GUe}tq^0Aq_6kaC3ShURI)4p;Bh)I*mCI8mGNPe_F42#MF#rO`;G(0HjJ z?CijO(@@9ej`r6Fn$kEXt%xHo_O)ydP@ef*zL4uC#nL>2dY_iSThQz;Xv=p<1|8-(RO-XHO{Y`4;mgvOlU}=!V zXRub_PyBhL-Th@G-RohZTp{amqKJ3??|RrW@U;&a-8*BbE;1H(Ofl|M7a5Cj_fAdC z&dw+_6e9UuK~Z6Lc5-rN9%cac3mM42rkcrbbd0t2tFYE4D} z_01O5s8Kt2s=}sDof_q@!oGlm)RevsWZdb?xpn4v83y=Nc8$llapUy%nVC;N?NuU+ zS-yOEw6tXJ-dNaGb*-(YhPnm}Ok;gRYpcc5-URVf-_mZv#q|=fr-<4U;$@IbC^AG>cD=VoT1 z#7_-o@rhmT?mlFQuM&eLcJlQnp73*U(h5v-82ik4RzOKqJ+0NS)bx%v#ox*`f2wN0 zBx3XVcJJmBkSaKZnWgA{8n^s1kt5feP+D&LcDoQDJ;wOiT_mZT zH*hwoBy>a^XhB|9ZhqeF>lxWaMfsT-u_krFagu!J$A>L4jSAuh1d{NC=BM! zOotVCsdXvYCs?LLLWjTPamTub(&Ey}vJ&L@uB`yNtg1Fi3mYMfs*KKtFrr*dNGP^G z-X8Am0D>XlG@M z4Gt@>fKScW(NysR_Lw@mD_5eT$bH$^W=8o0TY<#HSkzhj#J>Jeo+FaXJLDeHY8Ng% z^!X&(u{i$8ZaGwsjit`+{r91#{ACww=%b*nj-kH9P*PD*QHqUNRdaJqIanSBi+UIV z67J9E4r;ZxH~nAcD(M7ci9)AxcCN1CHq70IZn2CAaB-utu#f_3{!^gXxPB@6cP0Aw zZS?O_^e;t54MMsbC|N}Sc-J(`XsrZSS#wPpyl)jK_1xGjptv?ArUaZkja;bNY!ONo z3bCaVy1iIMn!A8@lQIDNv~>|!;Y~;YCNZ-;J*-|EW^7_THD7-5&8|%16#L|pPsTct z+q=I0;!BOEps|sd%E8;^rmI&YBCu!UByM4T;G3)+z4#_}%WV>}G7jn#4#~1GK)ejM zt8_9`Cw!x1oYKJ(V|6DQ1Lbv?vlr~FxYXT0ka>UHEa1~{O$+%HgE{viy+sgKxul^c06T$U$pJ; z;j0xLIvEdl{(bV~-}Qr%Q!}6<7Z>K|WhNi}4KTUijwWa3!;_nvlYKia`OJYo{`lkI z*)wP0Jp7UiWm!Q_XIopB4bW)6i5Xv!IqD#p5`_(nay5RFdGMKp<7g;$d~xyL}Z9 zkeYSshaY}8m7QKzmS=~El<;u7+^=Y6CXEIm1zHNVa0Hb5sqq2QLU_=je~%6g@KMIY z6QM`!nqdF_#Z!UF{sUe^di+5?R4g@{9h}?XGHsR%=&tq{lV27?PH8FDvo{pM{)6M` z)t*r1fM?xxG`-dm&nT68pW5Q$nmhyQEEeYjl$doXxvZ=#E2E;;(puLJ;11^qg_;6Q zsfLV?7E5PyM?BRfeFSxNrWPSeGP12EqcOr>?&alW?>0)%)p0W&ssrZ;6}`BqrUNx4 zjYUO8jcqj*27us!7{ET5NC-owfjO0`TX7Ft_d z>+5+5F14f5hGj`t8z7nt4K%qUlTepYAwy80O@a?->`-9&R&+EnOhabs6DuB{;c3j@ zk8qRy`9{wfkF9tj^>#fcI(Kg1Kq|p;b<%0lNvdue!1Q6udSy4eGKAdT(ks>Nc4R?> zXu{li39b}%7Q!G^Y!>;&KC*CbERvwSE83RaE(mzDH||8VCV;fw*cJ zjKp)NjvqVv$H7B~PhHHT;ZBu#7f&5Id~pBKV<%3Zzi}fYgO}fk%)z<_WWN|dRhjjk z)en_3+d6ufUin>TwrznsA&2W58xivYE<&Cbg+gs-sw*xmDyT0{zjCXl25mupv+ZbD zrt|CWXz%XsFjBk)qaK+GLzqP?T5o8o<8l|ag?4rKbptJN&PZ}lO55%d4? z%F5{@M~)mZeCUvH)b^aX;K_NBo(|w7L@)8-O1oPkAD@EYjJ<%Y&|`zkqM?fGJ9g|i zXQ*iM_Kut|!NXqZhPmKuHaBCGQtDCR6*Jh>)$J7Et<S69CN35I^yX_a65*qV%g!=>5pM(_rSzC9Srj zgVd0VcCUsgO2Cc?hC<-eq+SG+Rz5Lg(u_!GjKd#$cP^HdB$8u0@<{>CsVGdZ2fF$k zxec&ME7{FZGd5smj_QEW@PQr~akmoZ51k>Y3vRxo-?~IR5OG~+N?Zyp21#Tv%rr&}J zm3ccO zL=xNdd@2-rh#jg;K|P_dwGa(*nHZKf>}Cm^WLOvEU}c3JOEDMv=%64crLR)$hx}y$ zfx#gd?6A;*12j^2jRdWz6x1oik6rvUgNFpdTIB5?7%~_YsYjwi#*B>YA%oaKA;FNA zoMRJ82lc-h5i8KK)*dKFoS)Iy+(o)sERax_L`(zEs_Vs4CPyN#F*4b3gaR@8A)6c#!;I*Qtim8F>KX)dg;(a(Ay> zmHeEJR6yQA&N{h7T$Y%aIDY(iUz0u#lHvr}B*1)_38Tjbh{F@`2j`J?dN5)}EF2XY zI%_5>X`La3%phi^?T8@N@W|{Du9+3$IT@*1>8~qEnsyolDLuXcXTC?)qsrZ)0lFAi z%Q?8k1lTg`WL-^-iHQ>@#x}-Jvhny_>zjh}=BUZ=;_zJlop{wa4-Xe5tCJ6pjG8!c zB6eO`X8mZTt3QIF2Kf2<`>4U0BSyfAZ|ChFHiYUJ8l}S8+SDOpC30mqc#Bc`dwUOr zbqFSu5SHy|A#w%cuFTF+4^xljv~USVNQ7iI-54>krM0Q4t(##vxv8NY!-T=I+UrNx zwOC=7VG*L>s=`;z&5%we0kMZ))5(EtGc{Eh%7s`ZjUwE^$g#u1!Uhf$iHICe+gxvv zD#SW(pDB-ac1k4Sj!+_uB1g8hhjJ+*=Ouz3i%#AEL??<&cXqls^ElJR;L#_*qpyKS z7lTI^fJei+J7^U{r4m@siZ+^N4=+t*kVn95B;^jZZOU%f4qE)ccIB z=Kjx!&S+RxRi3Kj~C;RIcFQ!+vSpzWEBLkJR|72o&hwEaFGk^fJw};?FNtR@Szdb#S~=hfaLh;p55Zrhoq%;L!AwhkySSFt^=DuinVW z&ds`d`SO*kSI-^Zw{PFUqsIW(15fl#+S|R8){gBzX)(J|ehsr*AI3Cy!LjAy;^g6B zFP3z*U@hxl9qqx60jxlxbn|kN(Af0wOx1L&fx4=?8_aeBjC>2yOCbHV zKqsg8M}P!^g4Uy<*17@Uj{$@s4h;>| zO%K(!w4%gttDz9$Th`PFO#u(o+N@_TU)IYe1~u%_YCqq#Yd_V+1@PCtDNH(mssJ%q zo>uB6^Zq(4cxQkWA7ve-7nC+WK8q zqw%A5Vw>qs!1Vh6cw-&|Hv{ks0>7e)QX1=rS5Zl6MP*rKSrIIa2yH4U%7>ZnxE~`D zVw26#h5$tsAiNzXE7R3Yqus6DgxD*G%p0cg(m$HG4Ar5K|1d+Wt%6Bi;^gB!xq zI(y_^^N?3_rjxw;BEsXYSYZYm67jHoJ#6(*p(L=UIGMAu9A+$<%hcbzkVmkleeuN~ z^^Vamy!qyv%fcjuzkH!5q%y|3LjAd$o1_{`{LPf&%e!~&-TUn%V5p<5@z!w;TvPN5 zFTAj9bU@p=Uw--JbaMbe+Z>cbdVC~Uh~D$9{rEh(#N169tDx+u#!Q*AWK8LAjZR}? zM}>trF*XmP-P2=2yt21!*>Y9l69y0N2YSEa+yWXDidS)AL2gcdPJS^g#fXYS09bi- zWs;w@yN7zN@t+k)Xu!D`oER&f0~S$6MEo4&O2|-55TLY`a+#gX&cVeEwk1cEgR_f= z>%hNlfuBFmV2q!|Yf}kkB<-vsrTK9?Jmo&>gu_Y`Kc_#U)ECJp1%*;+3!UtrY`UemVKUi zt-U4hS{{%~C?0DSSb>6e33O>})G<--#FDM#3pxDnbG?OS`>uifIdX0tB?I~p{c2<(`vN=17^Sfgnp7uQ@u=fGLK005i$qL0M6e2L*_jH=If#7 z!^aeso0TGX-vPt5?<#NYY$(bb6MowNoi+1=#qhy!O1aX~+6JRTRpKVSej?{+J^Rab zDviy^iGc|j_Rx4MN31mU#yY{%!yS61?EUxC4H$U{jE68bIDLcO!byhb!AXUy@lxde zv}HW4Jx@E3r%mQ*JO73@@qV;op4RT_gwWPe^u{0l=tuD0-n~&({APO_$p8P(BIHr z;%Ql)wveY4@wB7G4xEcK^*~fwE0s3!4ALaMR_O>7c3OqABn6zX!@a^d2bt^tAxa`!fn0 zV?h}Rzl^3FL|dRNgmV$)A=(0Eq6fD?*@(74`G~ea8Hu()If=GFS*f>$`}6R5Gys9( z0eYNnf*hv~P~$WR#1E1(E7DoS0A=I{ z-gyUq^=SU;+xV*&@mG)iyQ@>-O0Q0(E4?}uuk`9vzTzB91?&T_-Ycyb{_2DIt6TZ2 zcmLhh`?!}<)2mZ*dUZ-quTC52z4}Ywz1JbLzJ>(o677o-lCwKjUB<#G!ousadm@ybOorYcH=} z_S__Vw;tQ~ZydwL$=EFwN#o)|#Tjsqv;;5aOK#&Ih5wp|E(FqS5%cdlol9%}^)pEs z*BUDtU>g)*LmMz^@L(w1)WiItV(y@JAOoNZR-XQ8G7KDi{-1XQTbH~<)s>z40RR8& zZ|Ys|-`z5*Y?f41S6}1S)n$qH;(h6X}ifd}pQJCIr<)naB8_ia+ zn+pmFxd*jNCDtAehL}#Xh>V8L;p*Y+O7n8KBJO}V!tMmbcYvs#AXS4C%}VG1fTW8P zpknq8&VYXd$?OW_JlbYOI=K~qosQNXt4JnV=VR(XC&AQ5<|Qcx()KjN;%!!DCLh`d41MoSuH=3Nm~u+5Xj+*aW9x{mU=y9yVIujVoj#g*_2i~dC#0l$2XD7cueH+jEPdw{-JS(-ay^d#97Zlj?%FxhF z_>D_SPSJ`=a z{NhW?XHkonF>mi)VaeG;hYr;SkC67%mmqCLiP7vl<(X%mnd)LLJpeD{!7>CEtEEyq zWT%lRq4~B89KGxWx&Y|vk53pP%DItIkYCnBBuYE!h7CvSWPvjfsy01PR)2JZerSk& zE$+J3J|x7y^D5nVnWGhoSyLW=`|Y>iTK&)07tVVcpPq>cX+I7(*@=#jrIQhbdB}Eb z5-cNg@vLG84?n;mYN`#{`BkMx?6Ftbj(1cWem`Dr4j4kKDqwHmRIP{;%1=odIWpW=C3R5f0UT!>8h>n zO#@e5+a=MDadHL-LG1w7gtbEe5bl6L#>0d08;BXE3G{bg{K6DE*Ib+(?O{*BPS2G_ z3Anog6v$r@0Of+BDr0k_P-?p(oYW#U;s@GJZTz+5eplpofz+-`qb%3<^4x3SxzT%0 zX5i^NQZ!oc8?t@4r9VQ`%gf|F3sno?ZzPFBXzgr_i|9C_H{0d#gkc zGAoX5jL16Cb0Z&r9R5SjzpIP&3UGlj6|Z!}Q zbpUsCsIUvRbh&=Ch(X{()y*S`^-`%5D!`abj7;w0?W*zjvcG-wKoY#6wHBe$FMR00 zU^juerVb7-*v7=%NK7e5XE>8#8iK)$Rl9p*r|0SC&)7Rig+iqxThm5&k?k!_$(h(} z!UhjZu24TZB-j&t1hXz#f+v)_dAK?{0{xBKY-$38UL<1Lt6E*%V4LWy%ekK4imp}2RQ9+br4=De z>b}fN_t)og!68uHMq=1-+QfTd2jb!Imm99g>vOBYkyNXoD2g|FrC>jf{N$6QWH98Z z{ae5O;){e2)^F-#$cx8!eZ4**;fpW6+;Q%zjU_+%1Yo}BxKZ~qrP;=l+1Jw2vg`Wu zWa{nPX=xP|{kU?Bp;ij692PxTa^_6hiBCTHwOt3l8Xsl_u%a&>({3pinmHikq z!c|z*%a}&v$cMp~;{vS7M~=Mu%93Hdd|6`W88+j|S6@AH@q+=R_zdRM;)^@d*ra65c*`I2rax zqlLAITmu7ryru0ursmf69$`=r;@L(?rD|7aQ&|bmmyYf})Bqua^|re|Tgp6rk+{;@ zk1@xL83M$WjWM5iLKz0WoIcby5LuX>oHDeRF9$@7^$(u3Vnt-+kdUw_8)rW81bL0) zT>Eoojg2v7c51bRLriclW2zkk0uVVxT@QV%Y4h>ZefED^jkVNFe4f6|p>JaPwuIwX z<+Z)5GR1Y#Rhddi=Uaxn+huv_S%ukG4( zd@|{JQf^7grKBsTcjF%tXrg!I$hHZ}ZmCaOixJ|nuDP=b!V)4=>SEX2)DEVEP^>J= z2P-CD+p^;n1m^XO;_H`hmFk^~$^<@|@3;Pbtx_>!#0bxBqgZ?bfr{_?f4XDGj?+0O z#A2f~Fff1=TtBpDP^e2wVe&qhA3xT8i^t=TIKx_Wbvs7>q`?{tfeH5X^0hOXID1D& zNXCwU8DPd`!|jz~omi;y67`53^{xY1Lu&G>7nV&O;fmjsN!*RbXCIpTQZMVd{3GsJ zqtU$rY|8}AdTH&Iiv{w)s2Ah-8cYAY&?HBE33V9~Q@TPzljxVd8?vYaKuEEXYh zc6!U%M(h`ID@{VV7hGJ?k3P&xO9y+#-_H}w=~V88c=?N0K>nmQSI3j*wwf zMmc&6iHaCD3ZR);^M+Ar8S3WRTGbBEsYuY>)C7qMx#{TW>ft7bv;?CwRweGjUWN3C zIx&>~oVw0FX{iH;y$Ol;uU?6_3LNHmG8vZGtgKs?kM5>P=eFkMeH3SX9@x6qSb*lg z*$tSoTVCE5-+uGWH$UyVh=@sesBfLyyZtNtYaHtREwVm_J%&yC)gj0GwQL)wng5E> zDk{>_Zr@Jr$7)XbZ4O?Z8#nH`o=#ZoH_9T;rNxVq=t zjnMujjk(wN{rKZgpK8CN0x+zl#%N63j8Q#dknybM;VCn=nIQo0u-eLSqp_P6-eop6 z!nrOplWuK3_ggb{fD9Qz+SLmdET204srTM{@7cKJ3l^vg&z(Eh?mFW=nzrS02~dt^ zuR&2y^(J{^pNyVJ1z}iAwb3Y!voW0NulQ|$5iT#p<@-2pOplS5RD_J zPLG=~UabzF`S9qN+4JYmU-|rW2tr*b{A6|%6EEnl`74%FdC)w6j(3^PCI^26&;1IX zI|iQH1gW+eJQuwE*qJ}}eD%Se1BdtR{9)6!?LTb$zsb3uxag#$gMVDlOWn7A3x@LC zg~SU<*yjEZB%kDt=tIU3(fTc0e%koyOzQ%8`X7O{d_J8mH36OmE{pM38B_%nz!GojzcLH+~xrHj5dx&1}&Py-R82dWXjiUjdvPcP| zMwDjb|4FC{sZfn!)SNBeSQUSyflMA}sfKO8v+J7=;iCNCiQ8zkxz)-pTq3YoSf#hO zliJPceJc(3?OS zO#ustV!d+h^7Eg(eojT)Hc+X~K&AcIV!IEXS zrcE9Ff3#Bz#$s>KB_>W7A*A$E|F$SPIyxaPz?k@dvwL4%*1gEfy8XPYo5ahyt-P#D zUzT;RPP^l3v~fIb^mS(J)%w3B(-B7T&Io;LV0?PTnAe~SWU zX_SzfjHkUbibh&7Ok@&bDZBh4AvQ=Y6$wMDNuFk%y)OUft=sqia-zsqhvX?h6I7J~ zqfTHl2*l!cN3XaaxwKL#@@aiOH*id;WF+oPiV8>>n*iAXrIa69m=qEmW*m)@KoZHJ zLv7W&0R{V0bAVs{Nkp#O8DUUCRhQYn;+I1wJ!5Aq&l;05_2I{!eioJzh5{hfD%G>G z`MbY@PxRma{_X-G#KI9W*w<{mke3f5$$|fv)_iImm!Z;homl%pbMu1_&c=rR4Px28 zCYR{dsk)fkGDsK^^;R}@KP+$s_5rinM=QOE68z-dPrv?b^~x;7biTwJNEpc{u}Pyw zPg<4*M>pV1$wfaEZwa;8U<#?Ktm=@clzEQDWA#U^LSw4@koRi z1Q$i(ha^$~1_QEX#K=TQK6oK)R;{Q#c&y!8@6o7ra8iZ_2EvFKqz`~K5ecPJaDRb8 zs#O{m%?-LJWph(|4^A{jeWBY4FlR1|PAV1*c_k-cMoA>cO*p-VP~oUx5@lajdMxMg z!Mz6%91YNe!b?RZ#neUy;TDRVoc#P>A*AM>KEp`ziV#IsTw)GhzdkYXhwt}nI(+!( ziPNV~BWpXTw{qWl;Hyx#m}QZXB*WOJPGlsL7r_kt&~4^)m}NHs3*<)FdA|T4(-#=u zZxI!@jYo$nc>nXA#tcv|swXnj0w2H)=>->;RYbzO&cfTx|v(^m4dcAi#y9onn* zJM_H3D7fZWN%(&6y`tYke82MR(AM#^EKj?Mzvr_&?ey!=p5#1K}QW4}^WL-2!1C(gNWi(gI;2e5wf#kroIOUAqOsMx+J8N2CS9 zNTdbANu&kBO4n|IFcWEka1&{PuoG!vcrFM-4YhD}d8h{sV4LxR9v>Yb$A=pI<3j~v zey92qULxBZ>7DFzq<6B>k>1HphffvR>aN-Q(7r+Qy^rU6r}^F+uh;u97ZGaGJ0T~% z6ME7+X<(@L(Kyp?hLrL+q?FN+Qf`c*4SFJ=2uOS#yr=M_a&G~tVrI1>y&EsPy_}?% z72Ee`lcNeChXT~a5CZJOD|GpA4#ziikt zZP^kY4qS#9_{0-WBv!?iu9k5rz|Brpi%5}a4gpEp$IcP*-meMWsog08cHBF2b&YcGw7Qkf*Gs4Cn;ggl_!ye?Gpf%5Z*TO zfaF1umQBSYqMdso;-28V$Yc`nAWCfa4~o_BsOSM>3JL{-nq@v@kpoZ$sc-7wEZi$CN}e|OIOmZwd+4sA0}ix^JQul0&?M!n8!a?}N$R>9L&^0Zxizfu1~ zdnHy;EKfU=qfMi-vw7;RJay~;P+!T9N}f86@414f?c-^$w}yteqnoF_fu|i>2c6gH zcR$~+n5PZmY3Ka0-{sil7Gn*OY{$hoZx$1)b7SF_p9`JZ{f%7}=Z@~)yy2H0Kg?pE zK-S-8_I37IzZIW-v-RgA=Sqt!PM*oluYh~)btXGvdq|(Xn-zNd;6rM5sq1B7*r0_9 zY(Ih0p3|Gj>F^M)SLm&$Es0~s`ukIkvRbc7&{uuw0UHdy|AK{j+vT9BfBG? zl;0(V8RA5D-1)@w4?Wa1fHTRfjsJe*$M3CMhh0}D6Lwa-bkAh26t6{De5!gdB0fx1 z?o7LP4)P^-!vI$e1KfY9x9Jyv4Y3j#5dT)+I4-cgxM1wWiBr=um)wP*kL)yR9n{W& zybYO9gP#XB^*$~9n5)0BLOOQI}fQW&B{Jn<>BZp;|r9{|9qLEVux-<~ZfXr_JCKz~>2 zs6e$J;FPlC{P&i1b@#e-1PE865T?ay^})IhH&j3mTy)xGl=xMDfUYlJxP8OkOD#^rivwXtxy}&qDBP% z!#8J4L9Wjz@%hg)P#W}Q_NBO$$3?Jv3(#s@9Lt_Rj4b5bONE8?`5QKDaKxqs!7)aO zVJx{+*<}uJ4ip{!wX)wU_n)+Q@zj`6H{G-R3C8g7`(v|n=az$Ca@$9A7~!O?*j zo@_z0M`+f#dy%TwL{8hfl9LCKRR0QunKw{=>z}|4C$`t;mETDDtjT(nAk2OZapE6u{5|7o&$s07Y9M?0_j*nv}UBW|4{TiA6?65D?~| zu;|3p@k!wU0f^-pGdVgsHr8lFg~G^4W0(I8!N9YJa^|qqDTaRac@nU(TJVqh`gB-i zw$TFv9I^;jIv_BJNY&Lf>xnF^YjUliED}GJfC5?`sbGV*IgNc@`ZiF&km5&NU4HL; zyWzl|cL;!3TicN%)b|*EZM?FhBVlZ)RIE@-Xp}GPplAlr=(=({+y)Ri8*3agWnW8P zYb(O$*t$9x`t&}ZOwrJgY(!j-pSG*lM>RA6j+g4=Pr%vr>$&q?&dM{z#TRo=At~(Fllet}@;sAY2nfCG7<+jhM9Fidwhpq|>?J)t z$m?vWu5GenJrq~f+PenL@sLV`y^V$YQ1Eik>5H{_8`rL1-|7lZNl7vEHkIuM2IBs5 z>L`<~@b&bAxB9w#a+yg_CqMB7_1=4%tGv;7VC~)#?5*AQo*9rXxrNTKv3ER;*Q;|N zoNh)Hg7w&LADE;|+4!iLoAFsbB_y zDiZLOR4wQuWgZj9gXNG}E5d<Rlr-7-}d?pdXwJ6f?c`#nJ69m{F z2Pyp2NcyJL!QfH+lyI5;7I52bpeHDsw}9Jj0kh$B&S=HSEXcyuYDM(SG==uf9U0vmmH@&>X4INWCPjx!I~% z@NmWq!fg+Xl~#ODcJj?dok8XhoI&=|FfT%bi)*}LGarBB)u(>g_N!ePeNPs+j8ta$ zh5E1jo@j??=JMssso6|6-7Phvm;??WlcfFpx9_f8g2b1<|%{Nyy!q#LrAF z!RzxwEs59?~hxURA z{UZl{0uXEg7X-Es5aU)Kr5J{#R{?(V$XjjWK!yQlx_vD(rbGRnW4# z7s9zqxQ+mLAQ-}jAlX2P#t>-q^Yx;=n!3TjU>JIp5vj@iNls?(X3X9bn7!dDlq2#A zD*1`qvL*QPj2%6QD7J$~^YV`F*#l6wL#Hs9$?FO86Q3rM|5v9PSNccUxQ4>7zJLkCy^`nh}YFgMCg@ z=o3#o_uQRvgO}FB-~Y{}!MJ75J@@33OD2Q_YNZyn(MK||YJA3Lo_WrAM;2`6{B(UG zyu(=Y8G*aSrT(nOXiUny9WZjYPo*Aakg!g9ksgRS<7NzVwyD9Zzl$Js;u0;>dmA>T zlmC-_|NZ9eN6y)iZaSg|$2H^(w)RVEYl^oUTZ6s7?$)y=?6fSfwD z+!RK5cEBU?*CS+EuakIS5CDj)wH?V)GV_S2coL-)5*|Eu?4e+I=J5)R8VNBWH5Eu* zA;vglFUK0GR0fA#UmvYjn8FZf(bn8e`vs6(L=D~D($-d9-mM8zxcUZ%bnax#BEf5% zk69dpH8=)q&`@H^vVc;fX)Ql}noL=BE3!O$-9n97cEm~q z)&AA1hYD&7H|+XhC5dCbpM8ejfCKFfMl5eHJ_cWs^10`Btv3e z=0^A~zNd1y>jy4Y=L_T+uM_-Z29<_$4q4pVG2H0F$&4|kXCq~f8+ek9EReHav!R~= zT6hpYf$g%ldq5X5E=f-L81aCAXb3zvN|(!EG)hELxxmwBHq3}Zn1Wc0I3Be+c=Y&j znUm+ufn)%bMr~9Z65-m~X2fE^G@1cgI5bLPjMD$cYHegJ46JGR@&fr%LB51D{OY*+ zo-E?+hf8=1UJdB584vOnJOgO?J$bk{qQi+XC&VXeB9tbcjwN&^o(ecW*WAhb_#PMY zl;80^DtOw#>(I9Fw0fR)8c%zHr>(yZ?U2p?O4=ctzvMc!EZ^^3zF#@tZw=qC`#Q8v zo^~2f+r-n>@wB?@&|bCQq2~ok|24;1%J;kcihhB>Kqgpm9a=k2JLDhP#ozN8p7w_8 z(4OOI<$S+gJgtPMjk^x*kWc4!p4NWFID4-{o6Gkb!P8oKS|{Ic$p6q@Y0EJ3w6}4z zKx@h7sdw|#j{l*)(w1?pH574G+Tk^X)DOOA34hmVq~D=+)O1zK-{J~(@suGvWf*_Q z3;8?#t=IdoPmoOJ53zFZ$I87AD>nrSY-MR-K8jDBKly7ul8SIHct*|(@l@k1vO22r zb50>)BL6&(;6fB?#o4pDaLnhHwy_dRm~T+6?Jg*&Z^!mcJkc(%r>njgk;ka2ZHYn7 z!s3~UQGqxcg5p!dh=DycYQzX(?RLcD9dw7K&VD`vC8Kc3pPd>aYl8HGnmE}>C@O|c z9soZyXQviPbdh?m`;iASgIVhi5YW%EYf_6*R3=Z>_9&}ix_^H30|n@*)~;jWwL z@pPafdVmxZZeW#x)j9`>Q*CwjhlUEE<=U-v9epIay{W;`Kq{FMV1I>Jz~X^@p57j(*(h*2sv12$ zuTbFXmU40u-QV7d(pP;0Fb|}}#>OV5PMACcrFrILPEHz0PTsgV%iMveWi@I#8saXg+tB=b2V2m*!0jlgGlw-6*S7WbcGOh2541P+6EUoeetMLjI} zp1V+1-{ocWV2p;U`VKoJ-~xL`eO03i;hq**3l)IL(^D-sA!hS7b|yQOyJjJ3?NPP` z?`K47j~zRMB)+?Y1tyCx$lN0t$V{I?J(jEOJSSaCE+NN^FcOkFjz+RG;;80jXiGe zXjh5cJ#M)QFfb~Gdq@DQ>*+z5ZyS>!ld&G3n1#sP?*kG)E0YnCOYQ4yfvDTj(&3_@7c{Q0QzLd33qpG;~F zlgNB9mJOmwgL<%~o=9Q!ErV*kAJx*rO9#_2`{|hd#h85(2Q(S8A6{MW02gC5p*(kC zF#&di+6)1(x*ALNCx!`d*PhDBxzIpzHC)by3pqKbYEiGFwA7;0gpc68J-;+*R&T9> z1t)80y*$mnB3Z4`z>2X3BC=@U;#Nk!^%qiC;NL&|vXv2Sy*MBetrENI^Px*;aGCw< z1W(7h(^05sqwsDh8MA@KO|E{bmO2kSd-6Z$F-@q-DQLfRiV>Wm@6Q}wt%Cb$@|#QT zZ-v{+i4KS52&CE8W`enfp+)eB&DPpVii8Rzky9D@*bo_69@%b9>VN?l@1hFkf zM?oWnfQmPKZE#z{J%=Ch3;q)&x1valmCaUC1}ySw1UgsO+HBU6($bRBGL&(vtf<(R z9ptjt6r9XCT?9L6YiX;qF%O!{i9EZD5j$J$?Jg=*=#pTW8RL@If4dI8%wK(D0YZwo zlumwX_K@x2Q;4na`wRVG3=14Jb!@mcG*M$5NhEfuNMs0=$Z3>&*F=O!s?fl(vZF`K zj^Udsi6KzV_zif(lCVI`VEp`r3l}De%rc!E@yW%7#TB!$@4kl#*i0_*vl;$y#%A!9 z+GG!o^}>QwW-popbKn=4Uid+AnUs${FflM{eEQ-CGN`BF5fw=Vt(`O@a#Z$eGKV7O z=`;ii3DSn+=QA~3MK&0cni~Z&v5C4VlPabs(h~u#w#YIruaKavlpqbE0tuYWRTqx! z*|X>H;ln?D`w0-bKUlM7-It#uvwvg z60ziatVbClNWBo@J#HV+|4riN3e>-9vglM=ge_}P>n$WK5=;{4uQWxV#9(-ko^X*; z3%p_y%@q4ISQ+bq3n8l2C$S+ z$101>qD>`Nv8(`kcPKj1%U5UlCuR=P-Fgh*ifp=sdm#KQM34V#TltN;ho>ILQ%~fn z*9}v*U5C2hYSe|p)avU{OL^*t_`XGa-#-ph;{+PIbE1I^`xuAs+{Dw~$m!DdG1vScPpm!naat)8TAJ=yp1-BPB=0m-|HQc-I3i?0ZIWc?f!`%sD zc7YtJ;U5C1br+$v7uKB~5i!M|DK5B&2z9hAavxTss z?j!LTDTH6}*-jW{WwyY^j~#xaCM9xL73)hZSZ_U+8~T`?Ka2+k~RqM;Io}D z54jJXdq^9EeTLh>PCSg{Fnk}}4&1X3SP&_Mhe#V_8c7>G7m+py8x6Ms9{gh)7>_@@ zkKe|FFw^haAQh`+;HKgGKpTXeR%TNvLk#u%Tj5!X+zR0-zPj3}LdN zHhyoH_-%a+?MW$U0n4Nv4PZyJ4RiQ!yUL%81lbVD9grQ7+yU7V$sO=}B7UPLo8r(N zTw`yE`X>#L{`nS2|D=hb{xiW>#BO;ncp?+) zER#qHRaG!WA*sC%)m$NXA=4W9uU1F{r6hU1xE#2tj2K4<5?dJ+s-?jm$bE5nflcf3 zvc45T`#NCIA9M4IVXHR22kC0dzT-rKJ5fKEUF_>*b&$FjaM$bEX>i*y;=<3?Zgbzl zs9LtH{PaVM#q$2A@1E&m#PxjLjq=}sbcD&9`bPHV3(IHE zp1olC=(&)pFH%;@37BgcE#sWXRSYB4PnZKdzRzJ2RV{qv#u@307A=}OW9e&|fDVA$ zNcifFss5Jex;nU8kxgFJ;2;Q@cya)cTWwa9i^J*NTu+2MF)I*3HDpw7DI39G%at+TU{sLNJcV`o=?Z);;~<&hmnh$re4syUy9$a*R__sk`z&m~20 zIjoe>VmWR~Vva$PEh!r?efn56f|+~a+#ZB43TY|51RK|d;>E5(^Q5*uE+8!;e$3<> z#*Ujh_0*|yO`7%+K;1Wf@X-(Z(ROZuqeYO4F&yo-o%;bA_4ZN};vu%%!R8x`eYTpW z?mm}~iH!nwz)itO*)6GuPT_X<_OY_hKHFpW2F-i;;fHUIlhp3`%GWm)c;UrYUw+|*mtS}if4_8MP{(d`wxdBa`OQpTu%lK8GEsPr zQ3>>;0Vuj9 z=L--LUvR#pq?kkx6kaHywldjjcpwl;faTueVh6n_YlzH-zINCbfVEIkOtd0%Tw>xt z2YAd{-%<)y`9y(zi`{R*f(5bKEn8H{_uqei(!dsAcA6C-aR~~6r`JvJVsvl6{r1^G z9l$W*wo;A19YOo%&=5}bxZ(@ZN`P4?^viAV#~;V~rY}c}^8;H^x?R}1Ze1%xdA6H< zlWs^hDdbM6tKZ$(C36ih@)uv6BnPnj@oe(2$R35mx7^xBe1kpRP2FuMMAz00XvQX} zpRHX@C~j|7c)c!(PA6wwfLGBWG(spO)!vXF;c~NhaC4ki8y$;nEN$Fq;)~bC#)K$1 z#9#$VL52X*-xwW2DbbKxE%C|asCI;Eo4+*#ol36oL$nP)FQl3`*{xMr+e1}-MVt~Q zEHAd!33Flc7nZJ1ETLXDQS;I$Bs0uHE~!aak2=AW+awGO)*7gKzB*B06ki!!hqm-l z0<;PLCsE>%cmVqzNu(7+??JoboJ*u-@sd)~wxAQO3*MbK)IJ@GL*-SZSq_mPq!XQG z)17XUco2XiT7v-gDH9)CFnro6hwfj=Nn|aRR2?~G#*Rh}Bz(4Y1A~$HBC~~tc2cl7 zTNvpfB~hr_AuWQ$C``gQ+<|sTZ6q#6ffyRF&bqph4&2b$2}E+_|9?S&So1smi$KGZ~(0N?>MGW zbBWX{K7O2glW(U_CvOW?OhBA3QpW@{`ulH0kXIoFP)!ZecTxka58vZAg8u&LX`H1b z56u`D)a&Km(y+Bd{0rg=j0ytKDlc^{$GDSvG9)W}Irw)u)=dV!}JCB~n(z$Rq3L-4Rxmct%>{`m0| zr(w^A)4#RR*4Wz8+2{6}QyMxYL7^YMcc8%!`yyTK=T|KPpt!{P^A}$l!^BP1c?W@K zbzsMijoY?uJ5-9qyJVkFj3cN3hKA#HGSn;rYylfZ;8Vwv0}93ybx?>=MT>mh9sPqo zvnaX0sI(#-NANegn-jVbchEOJC}_MKDO=tIWKMlet(O!PR0@(ZpUH%UgVqbT^ zTcR}h`77x`bBf#DSXf}Q^>}4C9-S2}Efp?o#Y(onuCA;U_(-Zy1TPG>wgTuQK2!>a zX0kxgUUcqUCmvEM!?GbSXI}?)dY`|)iiLT(yR);U-HV#$BtGAqA_Ty-&`%qtB1jre z*3XX}fCF0S2Twjp&r+(CI0R57T!T|UDWx<_vPjfXeC}K?k?o)`2?SWJ!a)}f6U+tVw7g z9M5{Wc6Vt_;l)BLse}!W(+Lw8s)3=mZL@HNyc=s#GyDap8Dlq`&X+n6z941HDA zE$tUJ>kP8C%F}zdZR5s{3|y4WR#Z~$fPPb3R>iA4b#|ECo0}V}TYE8-XP9s!7H*K- z=kwIpi|7F-Dr2jRMr!8F7@(RPjTYM9A17bWz^%&z3(}iKz7=c0u z=b9dwF9M&(CuzM@)Fh7@8KzMqGnbnQGDjRI5(}>LEPd4W%ls(G%A)M{DO1d96=ymp;wo8FR4$RxUUy_rHS|=f zg06Zh6GQ(RLKF675AmmH8#QJJsWgQ#t!?XSt0nN@P#|FM&zA6`7|BR5qA@W>qZyW5 z0Eg+qqr-KnYf>x9*+kVYf1zXl6l5cU#_8!P1x(hzU=J*3QccM zH&iuQA z%`AHP<>toT6^z8$Sb7QdM$)N`)JAMY894ejQs0W6nmLzxipkF8B*N7={2q?BVw*;c zl%T)NzCw>XfsP1&lu+n&3WP$$Swl9?#+f2f2ZkF-99e`>wL&izW18hqa@1-ODix^$ z6H?)B=l%`>^n+ZyS#ZD#et#k>W7vZGoB!R1N5m3%3ipqXVB8iyoahZ7{~McerKHot z7fzR>xFpt84%XCHgiOfekg8#+;8&FxJ(DOO2}YZ&NilXthT1~HdW!IckPUrHg)o2c z5M0?~F{sePKR|~bZyfHip8FYTVj+L%QG7*@dq_7Z6~IvcB45suFGB5x8ArJL$iL?=S3f#dva3Ye5}~{(_?fR9F;EcJiT@OiGFQZ>6@%vSMpdO|vw| zQNu-M$vW0dJG4IW)WVOm1rJUxzrxIf%y^of>~iF+H?lESNyZ)dHQ$osML zLyrc8e;HRexu~e728Xr+$T*ab$SFAgq_jw0<8ajEFgS}CD#si?cJ`9nZd-ca{Y&67 z1LzAoZQ7wjB+vfTsf7zMz#R2wYuBw?_tOPNa={@#G;!jgca#$&y9x{IJgQz)5znCZ zkW2JxeEePTTay!-oId!Ku%rajqv-B-(XYSy(n~Krww#eHPw1X=^yr*-!t0O=X|376 z?+naph(D;eqq;{ka_kYiS&R8p48V#ZAa&OuaZA|20ff*C-U*Kn0X&nieb5I`eIQXK z>h(c@G}UUu!cYY(iBV`YQg+m6L`qMa!boC5G~$*P@jF+XCRawv5V2+jHmkR@@vvZr zMBFewh*X0QBGmAFufoVHF!FvFd1573P_$UYafJv>VBMR|sp_JG8Q&z@v!PCBAeVAK z*(m;oIe*n8!W>)AWJ6;Y1p?wH0R_l;ot>;F5+8i!dTIwdlb}F+MR*@r+eg0m5ApY{ zpm`KDBrf7M+zW{b9*H^yFjLjlRb9$E2jTi$-leKbC(nQ%&YZkd)$D-Y?`W#3x^xML z*ze*Z&U0}QI-FiDmGvD}$MLE{XmAU?N_IIOm;^;EVbj^^+?9^+A7$J4pok+!j(pAs zMYJ^>{N$5Q4#1H=929Xo9~9x{f+7HPhoA@@iU4g!P{fkIqz5fQA2i->?fRjcuJ_P`Fr_H_C*TIs5hG8G36ssfm( z45(BxrB0?(i7>(&?eHfB7gs>WfxRCPAL1SST1NnX*Vs;bKTqLOpxPF$$KR!~)m4NnYR zNz~bIk~9@xtmwf}tFfWPP)?4G>IMj4pUmIvSKA`gP=d;WN}NF=86A{^#66iHc(j3aL71RorL;PnvWM-p}avj%eP_U(J?#w=fEp$?d&siLBjryHbt zv!BRSb?VfaoqM+f5P8qOLu1mY&jrQbH>FWO3dZbxVGiu|Um)}NeF#Rlgb$8Jc8gi@ z`oECavt)d9V1NqUTafi-l1QmODnuo)#3)cN#Go(+YKeBH4fQvm{8pep7Cx+?Ft93= zR4gNIZ|@t_Dv&)AG%7V-jo5EZ#LSr^XU>eEG{%S!rL*0fMd4IJ+1G3;JN)_Ap1uc` zyyTe+64Wv+$~>gaoi%G@B;v|5p9VUOSs4)yn``{2aiet9trlvZSsXVnb7X4hHP5v| z>IB(3f+&)bOYg(E#^ItXfc=VJ7obWLzoQJF;pCM1O}Z_nj*>qML+n1NcY(SF-%!m& zwhgAn{DJn5d)Zz>`vP)U@NtOx8~Lz8WXFewO27w+07(HajRr3fDUoEZkB*|Lz%mk= zfMr-lFp1&wsI0BU-o1|rK$Z+R#%6^tq!L?<%?7d264>S8Luq!mRX933OU_nJow{om z4pw1ms-lxobmk$1{owJ5W0F%>i5|jf0BMQnshbpkdng4))R^%ZyLOR+NdL|T?2qvL z`TTN5QLbLHWXYV&L#Oi2uL^7+n;Eo_27=mav(?#aYGC#|O+-%Qf-8uX8y*+0*XLWM zQB%;fcqyEmo$8jKN7eV4dBNPy{xHPIrv1G_*`TLRoO?o1xZVZ$^{5lxP?{;hn#!rIoA^g*o`{-(lPOPB&C|bJ=_3vo=X&Ps4zz7&rxseZ! z`tyqQLpnK(e8n|I)rHpGq8v9aMwGZd@Iw(P)x0TN03)M`%AE~y!cJ{w8xbt{el~!% z$lHY4^|dXw)^jAyS%TX2!G7U-<7kr95_+xL^@R}A@sw56N93vN@YEdtwZg=TUmGvy zp%A-b3b+H!c1L#)IHJ z`}6+Vd_&vOk`rQ#Dmy6 zkAy{GNAp$3G7Nt!mRf6bfvK8XYbAtb2Y>kChl6EOl<|WaC+q#wF&)mABJz+d3K1Tc z^QFqE51AF|P)I{z3tLAkV6GC3qXrqMM1miw!lBifjybs3ezg>1hrS7SeXru|+b5Nb3;77@zRQS4xaaG;R75g#Z7aS$JF5pdS{ z6Ky2c%=9P`wIB*1C|EPoqiB5qXVBtLHr}*_qcz}AcMlw=$DBy*>~dNWgljE6qrm>| z#b(#i((htKIaF&86wAoGyh7ESIdfF-H5CG6`*40C+3^n@8Z&0mA{2lqWwH@(yLv~h z9}=<;95_%?Ze1ZHh3r>Rs>dEP_G6FjH;To^JBC~a$UPg{{Ka?OWiSkVPpXz@5AFNP zyQc=>9snVGYpP*|@Z?Q5om>?lJfC+a?>w7}Q|dyl6&@99^Ijql2oZK{Fes#qXeOms z&&2)K)Hp8SHZM3bGh5*k%c*H4$3Re!9%4jjD6t4>2%WnhW_~OmAEH@=Wxc&+ynMb4 zBX}QnooBG?EW-#c!>*Hb@pR6Ki0lJt2{`ZULPUigkvn@xk>vM`4WwTkW=7iis$E#@lhpWx+ zZ_PdL!Aa>kpS$&a)9Mcmf~uT@xx2pshV(bPgVO;P_!5N1J? zojWpkv>~Z!^?UEX`|bI5lbl!1+S|2Sb5iW2nMsoNZf|JnxY!935lcOBLhRUa(L%s6 zB&AJ>O`bSu(xi#W#t|coR3JvFu1t92LkTVv8kK6*p5_Ml(`dS6?e2j{e>nHOw8!a2 zoIT2#C&VPg83T0VQFl2OE<-#dT!=BLQ>INfgpEiZ6%!f({OO1=F(W{w6vjA+Y7ecg zZBD5_7$YKZ&`|}KbDLAH4>V{bCb3xLL*-cp3jdI;btz_?;Bh>K87Fugw_(Od;#|cm zC+E=dvp5FM9zV2iD;#E9_Z`~t5|5Rb3)dqqVE*)XpD&{Bc)qRh@dmgb6BJRqi7{r5&0-{}88KBUM+-PbCKdEkf!+$L0(^ z^*}@t5AH?cfq7_DptQLVtkT)#5^7+v2@X^X1A=fg1?z=9U67f&`zR-4jwPD(Z+bS% zB_42T2e4jqSqB=upLrw~-K<@-D0NaYM9AbxsiP+&-F5Ql)TwFYH8quFipR$%QA=>Y zy)qe~A-KE)lYv*XxvYa`l6Bx{8Ppj3czJFj=5!+FbPncp7^gC%rm^vK35167OAg1y zMxtt;uBmEltfY=Gj1EyVWhI4h;zH0wy+!OB3~jTS13onxFXY2!mY?r%R0a`$N#&=o z=ZJ!XOoUE@l%cdp7CZuH?dV9oh42+(Sp?_L-;f1lsh_!Pr93MvEZhK&Qfr8^ltViZ z@N$xaL+4`HB2B;kL zOEwo|^ms`kwAa&bfvd$Fg52h$aLh<(jGtee3Z@Tjk|9W?BB(|j1;S>C*SfK>uczHY zV*}z(dt#HAi_y9dqeUdGxmW?QxLK~IDAEONa6N9^xZ}jdGA@A1-iXK(V17csFdN)W zr6m=x`&GaNlUIOm$_kO&%m}bc5~F17^yz}2S$E!f=gc5M+pjVWdE^a#R|kY+%!*WK2X1w!f5c zt&me?P-zCWV|_lkzt#{Q6GPS44|!Z?V=mG#7g?B#X_$*?n2U5)$2EvClgUrgd6;Y(L0(>{@mPJ2ayl%uu4bsjt@6p0vq!)A>C2hjRQu1 z4$fF4FypIk!|v}kZCd-$7Y7e!WPI}tX5uIlaGO1+DKK$70@e;6K2%EU2YUQxnP^Nw zz>EC{zxj64@uvRAAMX#p?a3#feEjie(n)>=y?;M1_+r2_m;lw1$3`24c|g1vH*W0A zDMmwd$_Nub(BUCn=XdPbv1eehLa|ty^V80qr&~0l}t^*hO_emVc_DzFO|&z(4Y_}IC!io%m8^Ygh= zeRy;K1NH(*+$E8H5DIDrHbJAsd>s9a&X7Wco(qvx8nSbwig9yiYyE*ByLG>z#4EFlL zkhnx$OR3fE1`-Q7&;JCLL5uU<=}LI$MqybUh2xwxCtV56Fz98a`sm1{i4!L#MMvwE za;YdGJ}f{tZrtR_IL~2Ob#nL&5d@z>FL$n&&21ej`_m?``T3fq`e?h?eRm_;_ zw=vTvuzt*#=@ppiS>?rWP*^MS&z>zUhaEldT!}!{i9P#b-suy^@Y=I&-^T5`PaZjW z{OJClk6bD%0NTshGpBP;L3lk^P)S&D$4`ZP7kU53-QBiO|13FE2M$ZX`X42U2x_KC~xRnem9YgU~lPIH;SH8Pw%+VlZ7aWlRVXnP=5ElX|}nWUw~YLsjVM zX*#_7B1DqT9+cI^OX)8U)2ZAvLW3kSy1k)|=*j&9PBLx1ZS@q@j0qi-gpL?DVM20p zm@ZU{x@?kxwjLRWV<{0!-Tfj|9z)I);0W;HuaOF+QYDfcl>l*+3N5oFDanXq(r5z$ zSTQ&;F0g6mwrc!F8xUj!=2PUTl-Nm;M!h^VeB8{$kx`)pZpjcB#KntGx(Pv&M#9>5 zyF?{pCbqR%8%0hQyO+DUh4PYegbKAEKsNmmbFLAHyvUgoNqtVhP7U9K1Tj_I-ev&D zM@NaMF;PM)0307c(F*yHYZLmO9EPjGuFft;eT5JfJe&t^H++Gd!waG`EtJ>y$E=&N zcamZI7u2&1?2-bcPbw9c{{Y`aQCnd{6@LA%t)FF3A0Y_hL>l`KIJ8!pxN6&s+%qWg zNTd>foe@QROteJ8$i*I^lSVj}CWL|AlAb!1;qLY!-0e!NPZC{6BH$mw`W(weNP3)} zGHWN~M_0RTz|)G;uA&0snYGf^)J~F0Z6&3(4XA8hTT@0IV5F_BHLb1Xjg3s4udlI+ z6vE!O>8JcQ><+EXNFgmmM0Bf%!h8!AvMD1p7U?HP`}xyMd;E8!=;rNtDmB>8S z1E3R+$LEnMNOYS^q!&^uIA6oU^eUA;ST7NflQ%d_*2PAToIK7z!(*G$-w%r>;w_M+ z*gr@UcvsX3^y};Mcm%#aSX^73%5HlP;Zx(1nFfHYjulIY-B2bDjxyj=q16p^B5BzW z8Hu`F!Ny=c3BXgM_F8CQNO*LdQ4jf4)7wk;_DUc`4GM$^em8|1MkO0j{mkR-=|TW} zhc~pfjg-;uZE0>F@DB056x=k7R6^|kVsH}!?Fo`6F|t{R)ec`Eiq6C@5M;pdU@WL& z!Gq(#gHfQ00+qJDvBFVVYb_}(Ej^omsR*)AwkjuQU?4vix&X9;3l?qsn6(YHsmbAi zKy*F!{YGju1G(l__Ey>;Hwj^`h_}!?NmWa6X6D)4Ml}f&|;v=W)06k+8WzqU4m zOsGRxu1P2s_xnsj483*8fAu_`=p{T6L1K6gPxKs~Xv+EXfV#66<(FSLb@)QZzyLx~ z6@ER`MIbZ44qjS;5PwJA#f#@)mR$dJadX%xKeSlyz-Ubx6p0X}Y`EdByRw3@1G}BB zj;=wf-{V54C3YeYN&(g2T$j*ZT_`f)`fAIY!GQwvD76}H=l^alkoCDY6`XjJ!O^UKwqXYt11EydjY)W69 zs%?^uo_<5xlz4*#OG||FB<1zjUw?3lF;ZNUm$Q5QhGX_-r@$b=u?FARx5fPnJ=rPXx`!i3j-^|8jW#V+%PA<_)H{G4~W;)KbcO?*J@0v-C zW2EE;P#|#LToUmL6p4*PN7`!T{7BcOoI{(x*><|D%OvVJwP_OolK7eeZ)o;aO2^Kc zwJ0k=>lY)(0+`a*mzS4c)zVSlJ2nHu&q^8b$0e*iX)?& zi#LAq6Wk!>6(|}i6b%lFn3UENn>HP6#869QQk_;JP)VgiiX4XGn6$KLkhxi5XG0H5 zDI*gC2ZbUFN{Y>!7vLW^VRYKu+tMR~q7gXZM}=vV5hOTy+W6FQ(`Mca69rXj5y3Z}Qw z)&+-cYTATAy~N3%K6ilg?f~aK4$iv+oOcH}FX`k-o9(Yj*O08u@u8tXN~)(3X8p2X zcW$Y));D9jXXR8sjnI$)LvYxF`BO%Ono+GOH}|i}){v{s@iN(85vJxUzARH;9pVfQ8y>akEsBx6_8KQuo0EjXhK9c;RYRgS$J59- z85HCYS1txHPUyEhh zDfKWMq3*NIhR}^#_`HAmiTag+4&>?_6iLOV(8lB2&NN{6RI7wg4uG+X8o(~M2aDSW z{Bhi-IT@h}Qzpd41PZ!Z%5rnkXB#?efBxdD?K=ttiDUKp-cX>|oj7r#!mS4^DPjcT zKU}ry$2_r=V3UdrvD0oN2EtK--28JtfAb*)Sja7xphna(LJmt_Syxxu-db7E88%|p zBk8d4t`;51m5jMP3vgs4I%fm*t>X1GF+d^&Hi%V-eTnYue+)yL6u22$(V)v?3YF=@ zM8vk<)9ZyO4qRUp^_5BxAtVwoLKGKlFf$?YYHMRtw^xWrAf>?N0v4K5P9wu0N)N*? zZ9;~pkj7vGpay9;us{zr?P;s^iP1EwY)TDb{zB*Pn?9=19Od`CB=E!EqeMX95F#>O zfHxgzKn{WsNdl0#5kr8lw9_cVOwt7cBkck>jX44)a zuw^A;p$9OIBFLXCWq@NQK&yji69{yE{(;bt3_(E&SNl_bEt2fATd@{z#abMWn+%0v z%z-EufXI|v9Tk-|Fhe4a5^u!4AhVZv?OBtWWyw0Dgl;D#a1WS8TeeJ}?jqpQkIJ(@ zT(L8FI&-w!NveGRla7esJ_QjGR2usx+JMVHzZpU8o`H1sO{6DoIGe`G=nsn1p*bYb zn|ssPXUyrujw0Bzhs{F0xnd~GA5n}TxpWVr)0F=h=aWhU1IZiv7YxT@9fic^Q6^fU z%j94Hp*0Jfhh9vhcF=-zWa~R(4s|#xDiAFLG?{v9g~MKp4F!Rgcq4v>0m~3JHmR|X zV*yZJiH!0|OU^At>J1T0yI1Pjo&D6WosdLmhgkazOg_jCZnI|E1XWPHzbfbsRl?lc z7r&f^U15N`P8eQZyf`afb^9&1FHTQDkYhsn;$^o}vl#^FNrEVcgNn3dL7(Q6{}+fX z#lF~=W&@^}zrLnc(oMYp)9m+2BqmB@9!ZW$vRLb^bx6#2G{TzG=%};T)xr{QudBmK zuC1-ZW(o_w;6N<)W((UWg~?X}lTvVi1? zV`txU&pq&J{_7=6(CxQ`MzzN$satn__~C~o>d+*0=(JmJz4e(a*!nzFCzP6v;yZ3> z@2CFBi0gLjsH46%35D&a|1$&xZIUS@9k%w`ZJPuPed>@UrVhehb!}};HRQc=aBeOB z!BJ*!Kw@JVKddo?=KU6KS6W+YEw%ofEtG1I5G?s` z(kA03IQ(S zB-k%l8>~$n`jPK>I95=Bo$+_kOzG?mY#7X&Px}rciiwWH?(;K4;+QlfpA?@Hsn;1k zHiP8SWkrdoo6$lf?md#C{WK61L=Y6nPD5Ury6W;8e!?ppv zo1y@1+qnha$ju!eepv62LhF^)H*zPETq4=JmAt7946=?VKL^Jo6bIvs$>W#2^2#gJ z-&ywW&nT78m`OYcmxa#>DMVZ$FIoi%_hZyC`9uG_XRNoe(KqhC*^m(;sJ-0#C?h+v z@dKCDy8Xj`&@2U zUr#UF@9p>Y{Nca(u_gIk#OIZckx9hJBtl5hm&0UVQc_!c;Swr9mSFEh!25niNOGVo zB7szjiK2z9B^^f!ptf;tBSeB0i!)f7^=Curk3Ys@p%3O|utB~vYyy_fC`Pz1C1sz5 zzHwO^)k3S)41H6T1yevCe`qi;9HfkrKXrliAjL2PLy%7u2_9bfLIypJo=4wF|N5AT zD$ih_VgC-*l|G;ZUO!CHO-(g5UF{^H9eY@NJ1kH_lJ;Z~`CKmWBJHIu`apnIhJ^U} zMMs1JvkN;Dgo}j#{X1^AAu*bZFe1yq=fk<*A;i^3-pK#1Chy{+nu>~&S}US76+o*sto=`qt~G@-AN6iwECK1>ejC-^7D&BEUC6@ILXcrm`YP#MR}^j)vMx zFgv{~)fmK($ip-iVDx?YC3$Nin0I2fZ9P(U%G%qBau?au=kOeqmQEX;n>*T)L2GCg zwU3@ZKZE)O_I{0pE-6W;mAHmK-AB{$CspE#$8VlC`(K%`E1ST|z1eBNC0;?Uk$?g^p)FDT=WtCsI+dovM z^n=czat^{B!2NGgNl5|zmtQ$gVFbRw2>gffDWqX$$3Dh-}6_piba2i&Y z15yb#k20jHlXoT56l)~{D6o5%S60>5A|{~>Q3_Dx4N<|woM^&H1UZ{Rp%sBhN`4~{ z00u)#L{}ES25<&6WS@^+F`IoITGs=dLq;L|Vf!x|=i)by!41O)SEsTQ#NX|N33YuM zyO7?!JDpv4`SW%0=NO#tzna5_K@$n#uBGhjf*&tdWC9;{GPc3TaXJVY*(04m)TZdD$6becU9Y80L3rR1%qNfnHuoMAyNN;-M4Q}(pX9hB&3K1&w#_Mm) zhkSeh@Uc_rS=^plA#-DTMOrK$9VCW?SfdC;8(wj*j zq-fOGT{npuqwcPC?Q35SLz?DwSBgb;cidS_?@15Ej@=gg3p zHLJ<)`~UyGZw@m9hjY%;?x)@NbzgTRqfSm>KE`m2eT-!7i59cjFCax>fl#!0x)zvmqa8yHGZ50qx&5iJ)kPQs9nwp$wrxXz7pux~=nBE8I z8Ha;mcrDfxA_k{L0)GxB)D@HUtWy;bpwsES_1+XFruWfM0JF|ltM#RCJ$0_qd%+^= z@9(3>%Er&n-xuDg|4CvlIZcF|J^?v>404(XIZcF|M!5SazAhGBK7Z<1R?a27va*Wv zs;euS!JdLG8H_#e$;}o~$P};c+NxZm+@r6jU zIzus+CP4x`iJcqfeh-A*6p2&XTOV5i(s zi=p6Wwb0cJGaA?5iQq@(fq^D4medqBx_F~$E$Ec?n(TIu;Lv!Zyj!96jJ31&-tJTy z#>M^8>6}gvZGUGU7N*#!N_6TWb3}vzi$h|d4;VLf*2C(!DY5hcl_d@B1*K(m!y^gR zq|!O~FKD4h&_YYmLZccRHJT1HrRi$yL4PMAR_o&>vkf#-3vg*oNBaP!F6B~0{T*QD ziZPf>1`z2eiDdx0C^7_;MXv+?^Qysaq84KfG+=|HB9y6eiOm>0VnjhfyA^^MpdPX? z_U62E%btGnHs9X!n-KeGLsq-z><1qlm!A*rtdeHZ$&2XI;d3D)1A{5jw(@$05yw3; z$GvNQz>l=-oa5Rx%ePxb$HeU4KYD~JRR<8n6y*R7(Ali?9yx8ng4v0P-u4Ff z^dvOietK|kkJYqq7obq`RUI8be@qP`y5Th8PLA!NWT*$0z|6nRg?i*};dqVB@DxRFUp%sjV&qJyEpF=WsfVla5^(R?bz`>JH9`dkyQfKX?;Uw-sP-= z2M-)QnwFN9okuqDF(!k-gm9x4qNLGA?IK_aR7^j#|C|Tuv7>$E9E{jdr1sln#@HiA zy1M#0!GXc{HPa3`^fW8PQe~{4cBiWDrJhezajg_A3vK14w1!f zF?f|dMJCUKVqFjeY~nu<|MB+QAGsqu^-7gxT&9@Cs>>N^M-Lr5uy_9tKOW5}qg2#Q zlB0|yTuJ4rr7mC0m-y>2Oh+D9#SP#cg)O?qr-D3u7X(|#u>Gmdrld2O4G zI^;1{0IS>0W>ZyZQDu`;4LMMf7E@_iMMZmk-kE&Th7$DQ!Sa-VAp~t2vRPdMKn7Sm z332Sj;+mpnmlb|7TK_SO1+Sv@7o+tBOkQiNm)8xqd^9yv-(cfM%4BJ2H`?&-QQ)Rp zUI!ie=>M$M-O2q2O&?QG@cs8U-0acRwtRyvA9MI{XXg#Kx`>*P`5S3^*G#q<}f(7VoE55#FfK3R9#U}P*DdD zFpzt?RO6Ku>E3n`$AGdF;ra-cBQhBaa_u2I6mc+q0O*co3b`e_x3#*VB7N7MJ>$pk z*|U9r+NFxZ?7W9D6!>>l(4k;YMOVPWM;?15ue3)Z?y?F4hP6v9=_y4JBme3pOR=DP zZ0X#M8!uigEv*EJ)cF!3Vj`~$56h!S!Ij}*YcYtw!^Z}sxCaxq>2igUcq?=APM+=> zee1%7J9aEwIDh_ForqP3&w?pfY!?!C{+9b=Yw7boxQug zwT>oQP}|yJL9}2C?>qCyAK{wNnXs5nMz6j0)fEjIJCJg zW$agk7vDFfueaYetdNC{pEZWn0$JBk+0XzrED%^#wXLnS8$! zE_v~QyhK7?q9HH729|hGy&vI_1j|ZHrrf5Y>VqjFqz1QKaIFcikdUog|M}18XqbUz zrup+tr3ne8Zz5LVn>=~b2!xzl^Mwb*#l^?P7s4fG9drDCG5j-5pN2YQId%ohlW8(k zHzz_eZQ*K>mIn`um6vF>h8FFhB1Ke*kf(yq<|6VqNH``CX^f}CCvj^%iA~)2P zTm*6JnUh%nYGba>%{ZBveeV4EGZ`7DGAVG^LXzgdCgm5fi6xbUocwX)^GJXLDrDcD9moi-9P&_&|qQ2zF zM#L`xjnNy<*VtwYLh{-BJDMVNY>Jwbdw_s1BytQK6DHiwQwG2fnBR}&Z!%ou>?}~H znM%tWyTEgaFe4pQ#v40r&Y^w_bPgbnw)M0?Gf>mX$en;W^mjDC=w+(u10D^=l8b4Y zV-V)=Pi9@HMfFsj$@~odHQwuXZQGW9t`gojHJ7ta{D}B4KO60oZ>e60NUaSxH-pTJ9SGK3v!bESw`oM6Ezuy@<2v4;;QCCwS#eevMo!-o?R4j=yEz`;XDFBhIalYKh# zOlcncB0qe!n0(4=s^)zwx}3fpJ*!5|xaAO3k4 z%zE!U?lU2gd<}}ql9JLc{X{re;}7yRlaXL={6AlvS z@2mHQB1EV63(y7x2KxI41_t1O8PY%I-`*w6Dbvw*_hXH`40Fn`H&C!C7qi2K!omVD zITxAAFqr4$LZxvA?BM6XF~q|ulffhm;s8ksP+sU8;6KKh6=c+0n5zszo2QWpgZztUdqICyUSaRVw%baDhnLW%J3|b%X zV1ufx_x7&e<*wosfvH!Z8x=1Mw%LM5t_U`DVw`}_6vBZuH=F8VUFq(G>H<7$Rtubu zc{wdBVqK${Dd65kp+QFt;a(A}HH^dy?m)r8@DhSAXh@g=z%v?4>fuWmZw5D!JwxI!+rs6Tp$N!$3ocvK*GcT*HgJ#g&Qdhf5 z4Qp+6eKTc}Z)pZw72-w?!dwoG4RA0p!|J@@Hs$FT;IAEh&Hr2_7o+6EtZQPF92g~R zdOwC8lg%Y-@Wm`b;@SKPSn%n`FrP|1?t2n7_Bv`T9{0r)#@ENS%tDPo$&2wt7->*- zJ{1)J>l6d4W2!1CuC6L8!KhPLf6xbg2FoY_y%7ipnqd(msDTL?vBlSS%UY*%twgeJ zU0x&b1CqYVywf{B|2#U{zb9`OmL2b474jy#p`as4P<#H9e4GsG9yo!~cZvaS4V9I} zd3l%eF%XoQU_hU+`1ik`z;>S7uzGb$a&n51$fra`PI=;i2cGEbOC(_9RNSg+!9>r+ z%>Uinf`7EQqEa+*{#|oPDyI3pc&`-QJO87PsIAe6I$6mx;ou<>j!9$R_E)d(Ruq~h*2q{{@wM$SfW7dP~I~I#u7L084->4 zgGw!#a7I!6SnA485{#lrG7uaklTAiZVH#|U$uR4S4w^`;@S96Q=-!>8eqYA3sOE<1 z@nj_>3%Xmu__?(Cr<1wI0{;qEQH|B$;2-w>b*AyXcH3? z$N6hFZ;qe8Fd-p9Vckq>_*H7Hzs{cnV~n}3wyUF$_gcAfiYhIITenZmsjYLIPt(H~Yc^+P z!HQc4GW5>8qi4>fT?W*-t*NS|bP&H3Unp&9$vl|T2IrP@Eimqg!pqCUjq1K$rq9aB zS&1Bhx1>C+6o1x%{^3ZGh@>(KhB3x=c4GCTm8b|F-B1fE>I!In`c;~MC_R2LE5Rh< z=`iEbpg{oHiU2e>s=GUx?#>~RSR=Ev4kEw;o++&_D8QBkIy9|bN1aq%cCg3L<}aho zhk3E7{ZwCEY_*z9EL?eA-#6a=AKy^ZPm7OlXyCZRhljs!LJY`6o)Wc+CK50YgMB#l z#>8Uem@%VA!%*Qd{QX~w$j3J)BGa0h&YUrsjvpWXz6o*p_=Y4!R9ENY!;9nMgzuQR zxSNogk8ez1T%*KXNX%SF%srS7=3|tY3yBGJYx*&&B50tkx2>ZGKHhTD)6rZGriAil zQqCh(r&vY??HDWUg9MEEeSJf~7~(Q95SV?&(Be+2&{vv^PkxO(=2s_*TPXXoyruZ` z7Wh_fIgR)LJSUbCn?1$bZX<|)uzK~jN;2{xfD3;=l2mS6Ex=%~*oB&jPU&=Bq90sz z`}y;4&%x%bm>iJ2HJ)p2*g3TU;)Sr9*b~uLFI@#8nhOx~JfO3L6Qy`_9I?@fkcv-nU`0Vo6D0ea3bpKQ}zQs^Oo~1DZCBGj}I3}k>7l>_#Vl_JRF_wcizt!HL36??C5Ci>`VhmT#5+5(olM;ySoeYy9ky+ z-IgqJmxgy9@^CBiuoQWiH{AY5j#Rb?Woa(WKCpZDv13P1=a<#N;8?vZ8*Je-}LF>Tu)Ux zR3$&1E>5JtQW4mZ5iVZ4{$Q;tIBfD#H2fp8CQJwvr^uB7h7n;g@%*Y)sbHTGH=l!U zp@{fRdibT6wr@Yu>XrCr5@kZcv5I*N#+zOo@00hF!E2Zd?nCh%hUSbPHd5@QFh7V_ z>^yn0v=_5gOa8?Z`}a|2Y|uT?s6*z)cEEDltIDh3b)1z|P*c-xgGg{a#bz^%j+k15 z2Z3XvFi$|WMBc%{0Sfd9kgajb{z|y~q=v@DY5MvpX3jrqQo`K1)27XsG&X=no>4?C zo-+rK-Xs*VdxVdVA>0!T$vpyM9fjo7dWHA+`EoqN)fb2HYm|-a=UjL7Uo!QCdB&F! z5oW~gOeA5p{>0P}raTE}gmuFUXvOEyiZssT3uwh>(TgW{HCI*EmR!y$>$I{!uMc!! zLNi-AY7a2i=AJJqtwnHDQza}cOh2X^!0HD2dPoUB(Pr~bT)K3+lIB&UR!LDGm|=$c zx?#~%DhEq5GmCoB*fO>oa6;$`Y1t z*d+L-d_q2xoXaW}$IN?k4p~nR(W}dszcm+fBm4!({k?$H@SOLwMT?%M{B0YBW2MJ~ zQ}mT5PMm;xHn1aSPbzST!toBb;n1NA?M7vHrO6CmH?`i^LzFtX7*TmnUC1-lmy}n- z^S!wSJMzLJSX{tW(a>3!d*O6;d24fT7yegSP*6j6OYJ7$X(n=9>xs>qPxr#rQfBYz z9e}nEBa2w!!3|(Mk&K!#BU%M%vpPJO0mSkk{qR*oC?iX8vrJ?cN$s6&6b8_1Lkxv} zOB+Ufy&DIF51$aP3=^4ynOpT_t=tz>IXaxQ3h=`0qvMfUgnuY4*^&Kgq#F1jt-I5F)MX#Z4SL&(bk5} z2h_Z;FA3lojSrO5S@M{{HFoh#KeJ5dHi6DAN=RcTo{zaDu5#x&gJV&5#jfu zWOS8G5#iLr6=mZk-D45>s2Ty6B! zsiP>i&neSDc@Pj#yY=t9S}H9nvQuwzz0O0*q(+@7?$_wwOI`cjnL0WRimLKX96vBJ zblbM?KKlFTU+@0rAD9zA{OS-mWU;(Mi16eu9Y9}pP^za+*V5+ibrv}hkFKGs7slD=i<*~G)_R!Fl_KxiH#dSRhXXfZA2PTmB>MTBUrnFxw zl`__@21*Cm#d+}RAt0J6D15xJwR^ys8r9nGIl|9NC$)AoRAV_?oL}G!4ONBp_066( zEvA0Qw?_-9s;vPwOK+R4t*WM`!;ZnoY3*vGG?F70kLH#@8wvB~YdiXFg<)YOO*KQo)22@u z53S_*@z^&f!)qhu##(qxO-)EhuF2^e^wHXN`*YiL@TyfdmS&wk5fi<8_x{g6`+CQL z?>2zn=AYl5q)`x<2z?>TV>WA)+fpnm?6Y=ZTf0P7p!7KDot@{2Y|!hg8qWEP<+IL{DW zcgB&jlt_$aqUqQe&nsEE^5gH*($X?6Re(Za)vAqI6;=a`iV;#%_QupzM&Vap=%zNp9G~OX?$ieKs*hN)Nvg3Og`e7<|-! z8mH?yjyZl`A@Pa>zW;sWq#;jP3-VNDVTO90J_a8dwzUlxFBg|qnH{Xps8ORvsD>oo zuvd9YZQZq{MWvOsW|(p($YdR*^$IU7C2(TAMvRbP`%L;SUaSJ&0dOR85Q%Fv9tyb_ zW=kv<7)roloCL>0IR*u})&n~Ni9)Lqw+{|>clSvwd6_v~Xh098Oh(O<@cEF)fUreYpXPok=JC6gI^@*F@3F$nVs&gW9)p)oOSZ8K)Xkx1Op17JNFNR^N9_J-Xfax__h zlOS24Cx!WX6>_oyIr$1XS%sXeKu%_tRZ{3+b07G~@NKGe$#>epgC}z!=LqjEIOS0_ zAC@l#fbmejsH#fn01>^Vu&9IqbapmOMNT3?Rog7xE+-igLwYPWF=~pslfdi8Mpc5a zA0s-W6$=w5WL65e3v?af8stzElzM*#zhX!u;%d(G{=gH}%MsbEuLMM_T( zk#*40VeYlFWX+l#C2jVw88c>t+1pBXtjWlz^DvBG0>$`}@dl5&45MzQ0h`G4y|EYt`u0YMpg@x@1HhOv1ttzH&| z&0O|ke0*R};o)`3$;s;u7xo0j8)p~hAz$Y&<`)(ogCQX&uTbC&Z>Iwi8l?guCuk$( zUq-{=bs69Eh1jt9umu$rqL#knXG6SU34#{N$$1bbbyR~pr<#N0ve-(9Wmt7$xPsal zd;$z31M4tB;MjhQyZsKQ-6=NCcJ^7(L2M##Z!4~{^f{FZtQ6%fEkiU87bkVO62Vs; zhJG=^(E=AN_>3aTIYvsx0T(0G#ekKF85`q-qc<>uvD67t>O=$%2~b-EY>(I)3*loR zEFxeKOZ^JjCiiz|wYYnmECJ45Z9 z)m6n-(vIpt=qN8k7Wm^nX!WM`LJ z4U?9?4NsuQZi!G-XB)Yhezh0!sFM2&{mV14rOh~#e<}UQ562-H4cbw6f-Q159@WR{ z*Y24R)Rha}b(uQ$4yZ>26{qO0_h8%xhmrq`q@+J2VYH{m+7WXRao&m@MrWg`k435b zYc3Zy_Ss|QWi7~ z)wRelZtIF;aNA*u8-T9GDH}H~R0PvUdwXY-8JlTMctBs9 z*(S4C25dw9on}x+b#`=F`YgstYKv<}EiY%haYOlWmHQ7kE&jB6xZ>Y*5EUKaM z8&H>tY*qwaxe=XNC|W8MEj0$?Kqy*jBw9*aUEO@K3~`@S0jjpD{CuzqB`f}B+*&KA z^S{-y63(WQ?y3%zN(N_sXKE_^|B@BYzOwk9-~S%_i>msSDrhy&9Xbv85V%XRp+GN5 zCJ*t+^AI8L7H5oejB}cE8V6fJUEfM=RaH?}Jt8sxu=R)h>b5oz&m}8fd}ncRKaFH4 zQ?Arnt!)-FqPqFm+uK?g?36)M2dq6icGT4k4dv$_KVE=!azk@FT-SVTwxESemnP_Ni8ye?xCv2FQ6qI& zUC3Lb$BvF0W6*OpV|Y!AC}8B@R{b-*iVcm9j<%l8luG~jM_=DvyVk6E?_+p#@27Re z>!dQ9t>$~|IDc##2)`YHAzEO!#14g*JqbcH2Rq2YO>R8NI|Gf&H)Or`?c2wCPEopxb61vGL56SZ87f-&tQ! z2@AEaP_y5FE}Uf(q1rw;toCqeJetR>)U>r_o@#B4i=)qhdD^Y-aBDkErc_OQyjVAR za$ViPKyx!@@-W~DB)kkV=_DdnTtN4u6j9br3W2j?B~aDUT2=3` zOHr|Ew%@LT_qfX5-wlp~vifdB<&naLSgh0r8~7kxVgP~BNN-QM6I&n_KqZ77i~s|^ ziXAXUbufBNj+X69PMex)3iApxPk`R!YpjOWTo7k3r2G+O)$OhIh1k;<*0-7we^_cWqh7G;@bVh!t2OaKW^-Ewk}9k) z^@?C)abjOw3xh>nXLqkt?%sXSmZedP{)m>nf4F4_SQYIfoxS~@p#D?$^umYJRA(OS z6+%??4mv^8?W3acmXI1VWa)IJW~P9`infkE25g27qz05JV$XEHT74n{y7P-Ko}~hnmwWC4@Rk=Z(pLTat zV=Y=OG^x$j-X>PKx{vorbf1_ogPQJ|7{oje21k037G4|uu2so=v?#UBJ%kpewz(wq z)li$g56XH>Q#KA;Ska)3@qpu&2V?7}P218B;48JwNw2ld*#P6A#yJisBO9!9cJFcM z0Auy`hBJrN^FLQ=TM70!c9GFpiiT_KPR{cpn7G|=YR|2p2LG+ zu1A1t80gmF>CeGpXAI@+?O4K@9UP+9hldBKIH?XL)JZwUigDfw6oX)-6JKkj)2c`> zHPV@TiAw8X^aCOoI}naSnO)y9=!Su;=XiQP1rNb`1CA>{v(c%vq_Z0vq3%wiRk>|+ zzG#CGw1EL_;EOiE+KWJqIW$yP*VP4D-4Q6;N|&8Z9T2b&jQ?R_;dk6I3#XHr4?Zx$ zhtOrLQ*YlsfBq}4z+ddxu`K#up$?2!Y<7r4N&7osdoo!ua<|fDw^K(%SS+5NaN<&{ z?ZWA+w!7=tiwJ_WSou`8TllONKBI73DqBfE|E-;guo~goVBzze@EI-KN2~kS?h6pE z9VdJq6F!~7eS^PopO0|uXyG$k`1BL*ll;bgp~AJ3gwG7&Qz_g>E9ckp882L`6+Wwk z&rab!({J1tCS2yyr()JiM?)`_0}zy!ZI^5gvmw;sPb6uA0L1llVa?L~R^BG6ti#1a~Dmd4nvZU=2j$e0jE7t9stf!FLmuoBw0 zFFHCz(Ju)PH+Xpm`+oT)E}{bjf6YEbN(YJe-uoD){1=?>C_er4oA37T-}Ct6^kQMW z;IFqM(bJ&=ZdN`Dcz%_fD;bagtcc!l&8|dG2NI1&q>|VeCRWdh5QdvxblsiFbw`#7 zg7i-lKI?=}@MvOWsrikb@XHaRU%2nLMhH$w!$bHyE_{v`?gM@8ulDpCxQ|0i!f6EW zE0%V^B8{nJx)Bg!@~M#+j3mGanotrd{4Qu!>0O*~i15kc%^)ZP?()EU3*HjEBk>ll zhMx;!;$oOU8Zulh!_~}!1;%93XkZ1ZHBO@C4$KK)w|oL54tqCOm}cE)pKXIN-f>B_H%D*W}|#cRr?jqQF8%KHX?! z49G};-pI(1kx(D}UFCj;_&Oy|Ajv2yt+WGpr{R6Z=+ZvZdZM+%q2yQUNoq_@O+}ri zl4*Fi;w|KEDXv|Ls~*R-OK}%PDPpN5yrLqo$f6t9=*Uq~p`j5`!{7!Mr@+ zAD}aBH{=1iOHbZ^_uYnes_ZVT%DemSY151`=HtirAG&9HAZ*D`KlRxCz4cWMW~bBI zkhS}^yXMaI@(KSmUyGteS4C4VVxDW9!Qxy#Cw1_z5ARc3xAP(-?f{`<*+zyc~78d5c~W zb||9n@4N4NBb*6^S22w_T7{KtQAuerRc{WTFQr?QA{v@FFV86R#jqfP0-oVzV3h`4 zuw3WMdt*xo#wVMtx!DYr1qX8$b<#A(VBdtQY{zE7YQ=^TkdOW@YkzMiJ&FHy^;4js zgR^#Yh{Z;zey%*b-wJyT!VXKXJ{$IM?07lwHVV)F_ibHJ?8-9L(jvq&eZh;N(p@xo zHqbL*swgNcD?nS9nwrdJOMhP<)P?5GZkpjPc=@#V^bCTLor18aZm-LN#x}<6F?mJ7 z4`s9=E>2o_6#gmuckYRcGtg5}UZy>JzNe>-7E1S=IkN|CZEmQl@4bKd(@!&uJaGCw zhxQ*o?nLy&ogN|K!Cqc-=iPnV?yLr@bMCxj$Bi+|V>@!G7g}juU8OYXCA^+qURPI2 zPvv&RE?@o(J(VP_#L4AGX?wdUWJ3Ht7y&^k&C5b21k~P!=Ep)9TN?dKi}G^o5Mz1g z_qz@SH|Ermo!{SgzcD5z$|dMw=mp`CG}ogMeId-g4x&qRiY#>B?P80Egc)ZAr* z29zIx7I)HRFA<3lOUco1Zf?eg!4CA90~Sk;Gsft(3beaOWXGle`c}NW^l~}1u8Tkh zu16d$yfkXL-e%Jq0a0QGy1Q)>a7SHzHeyCVRW5}O`L)j$+WNX<<=g1>i}5~(|6}uU87!U$-u7N#UHwY)D}pKI?@e$(mwr}y~^DOKEbdjD_Tck}J{ z|5^EHyn>&v4Ou~6c%cD4`qyRvvw2gquzC2JZYfbwux^ZA1Iz|IevbxLehHbM^bPvR;wH84vDzE0rB7$bRqDQGF%)&EFtL+aX75Fqf{xTsyn6j@l!#(rY^HU04=UrdF(P zGc!txOw}h&!P5?)l)^%o*OUtoTtZ8p=YXr+_QBn6mSyesTLSe-|HixvcvQ?z< zH|QD45)O2O(h$V*5;kUfAmZMDr(7l1dMcqIlqx+askm6H92^`w$H0nYsophCUnsVR zD(h<1BZ1j87o=~%0w4)4Z0|bXbG{Q5n0cui=m1G)>4~rY@sEFO{Cri>E-duFFE%3- ztncXWzB*o2*3GKP3Yry*Irq@PV<)QQAtM&N{PN2WPYaJ2GfK}>0O#tkr)EV2z%DEC z8W9>DH&TCRtin>c2Y}FZ|M+UCG$7$vcRJWm^+H`-#_IfBlsg zUrZpMc$WWI+y->wy7nJ$1OL6MK;63V>G{db=v?&E>fb6`AaDQp-bqD|T7}CIk<)oZFXW?!>|# z0mK?Jat8T`!K2#>ZC1MI?5J0V;>h(Rw*I%m{7Yj6(rx)UVQ4EsW$43u_GY=7rD==gE`#bu*Jyn3GfQ;LR zV+XWh-uA9WQ=5}U0T?18i>c1ROB@BK>z#0439$Bps>=c&`T)ztj7;OBkrCacv*r2u0LtZ-<)1@;K1a6khU5_Ke|&HM~m7bn{DkCfV zn%bEe|J_Dy!iDt}B3pE{4LTH2r1crVdDuh%DlbTu3AM9qF5Fy&ST^K6p>{+TmbD0M z!0Z<%YE#(S6JJo(jSij}i1mz)f|$>+CHEoRyNnAAnhCKBlI3E`17YN5~LpZji;f7$?je}vLj=eZBV5nzE=L^z7yxK3EIB}sJ`%}~y3x>?YYgbkVwzLy9#k#|Ep_kT`c1Go)k#2y zA*oA;y)hm1-ChItdsH{|`iJ<${th9Y7I?v@wJ${@)dx?1U`G6?$@hbVE`R@rbHGlC zL-gVPe9j0YhT2gJqW7}0fiZV1qa=(w$R6>aMKxv7pB5rc9*CB4Yz5qJI~owNOvx_E zlJPLPjbB0y%ijsd3bg3~UZIQf)oB&lmfYQ7tQMH;ov(0TW@a`zQ+zM63F^+{aH4Oz)vzOD( zdWZPxBp_LnNC$B~U`(_J#?(PX8SP^Ry8yQ9A{~6}l$M50yREdep%szCh7=${Kwvr` zuN(S@h}uKkQ#a+B4k<_q-HlT{CzUn%u&l8G7{gX1Gsn>F-W-;-1I+UnrM|_fd#SW_ zz}wC(ZS)zxkXJlIkk(^YrS*Uyt>3z&wc}c8eF}Lzif2E zEmOvf8*h+FY&dTP12qabNO|?p!7$j_skHWdus!Qy*1qqA5#U$>1}RGcK<`Hm9ol&~ zE9dO~4Lyi8d67nTy?pWfX`>hjhRyqSqW?x=VerMN3h~6bvv>cHSya&ruR#ysazJX} zUvdDzngb<+=oo`kSfx9=M);~4Yg#UUi`BxR4!d2cNYM$?Gq*H*Cmf z3os-4^PMp^|t z7$7b=T`5^Ah%TYxDmNEHht}8W6MxTK0-jspSj#;zV}>7-;$s=o1qTG{Rei-~rLyDf z*7x3fZ|m6(r65hF@^){`fISUmM>cQUd0^lE%(9{yz;s&k(=TEIX>Lc=wl`DimDQx`h0l0ewR=&3;xHuA1WrxO7<(Le~n=`^P3CB2ss$+yMa z)mEHaT+vip-`v#Lo1)K8KW##?y{(Y-)?OQsf;K9w***bUj*Ey4@K$1M=>v(LSOZ!2 z_Lqx6uWi!=W%u{j)HlMO-&kMM@77v`8wl9!T%K}q29|^y7z9$JOjyD!!mx^t4sfPg zx?5YjYj}VL5sKN|XCo7UU`A+q5Y9}EWb2OpEqSGfY@nqshq}9$q~~O#*D59-Jlue= z?o(wA8IX(&pn)`gufKX%^yq2hgGMiU`st?=)I5WcNu&x4g9AG+dHUHW|Ma@wyF!3Q z@w*tL_Wga1a~VBEf8R$3DJ)Wa)Q&c+re2)7^ zdO8NH0FAo0+tV|0{ER!|sauz+%*DBvJ-Yc}9$y5)KVKN-23U zjpfwV)6-VHQpU(AajeW;r^6R2BoaA@NP#9&0g)v44v0jfVU7~U585KH894GW#t*lJ zU1*Tuo9Q0_IwD0ZAQTX=76btALeo-bFQq524@yEN96fsUhn+h=0p50FMvY?X{DgzY zE|k|;73hcU9DqzJ)zQCus7%Lk!EqA{zdn{WPt)r6_7)bN&Z#TfhZTGpc-d756KJWg zJb+Fu`Nt<;e7d>`wm5Llh*NB~F^gW9LpISv^unSsHY2&+4XE9LW=TiiGzrHE@;>*% z!{a<;N#r!z2mBMN=kE(dzZ~Ein=qOtp(l4@URlXJ0vf0-^pL*$$Sh2mv(P`$O1VD( z^Z&N^+w>~OXheJ(MwWjpdTQ1ze`7#bugasNqvS%y54pJyJ(R0dHe_u#8jaht8k9zb z*$kI#J6&N_^D?Iu>$*;>lR%K6h#vr(tVEHXerV^mFTU8e^N_3lM`P@z(Zk{}b`CT9 z>kc3Oao3Kow(P{5`~B9hcI-clLHi9}3xd&p^gd>wtF;>VhnhMgr}q|_TN2?N`4X1` zy#5;Mt^6F<0_TS>cx}umdlSP*!zaafHP?dpCDHztxx_r2_p|eFOoIVcb|K5 z>(;GgES4b%M~Qf}3g{Gco;=grXY`avl%75#0`*wGdj*W}QA>yv*9}+(d5NXdN+eqJ zD2>|aNf#hKV<%IT&V;FBeS$}kJL&Vp3@;HYrKGnNO$#5@t0j&^iN~SDk(jZl9amSJ zi)r@kh0Dbk&z*ulLiR;>iK~En9$+x&^xFoyni{%{o^ipHBiTPXZtu=dF|@DWi;^aL zRMl$6OpcJTY_lV3OqeoNb~bG{%C$S~>;e??Jy&7z`94{bObaVfD;xw|8$5z=O30u! z)w8J+GSb)6-qdBGaQyx*T2_fC;_g{HEj*ScRw7rXdWQG~f!Qk1FJ#JuF#rt3PN7AW zSi1*^R4L}gj2QOM;e7|?HBwi4<%Hx`>N z=c_4P8`}nYb15hc^kMqAI~gyK%L=0tEu9yhPj_yJIOJkA6lxHDLx(T9(?IEjRJcQi z;dD?kAtg>%IGh%fmf%WGA~6dIQXk%-6iJM)kf>-tie!aUA3>@#n%xSdN;#MxfrQ3_ z(~7<-%cyM(b8J;bITRl7>T$g)5wQp_8jqvA9Nr%{XBR~{I|H;G0lNOv8#a9RHC~$! zwQV@vucIdoK|yVYHq(>eZ3qeyi^)m;<}9P|pa%sx!%Sa8l12jqsvzLQsJP{LNWUV-doOzC69L=8L3;>kxlv61+WKr!}7bA@pPaw!M9!gJ@&=I39gr*bY{ID7UyJ(F`O|7>pVSwSjp z#)2BVr9c*pZQTg(kt1#A_99-`_OG^#9Em8wBSr+v3bt(dnx5Kw&bH;)u`PmB+>8ws z=mphdiYh=&FO`>+R0TgZkHP|lV>9_yUQ$v{|5c@UY`(CD*)sfJ_Nm}1+{?)2_!Q*f zKeDgJ_Vsij;sRC_092a0u^oa7XImRx#P)V~_4M@#ydK@=HgJLTK^|_-!WtVAsMlcG zp^{<|=NkY8VsLPXUN1yS(dYv~KTXdB`07JAE(G%MAJ|xrVw5<6QKA^~a0H{oQH&C^ zYx`_wtQWz_ZSAo6VXLn1Gm8srb^5>noeGXk0&Y}?Wu+Q|3E#BtmA4qPw&TKve6g0g{~ zl^-1KMrz?R@4XlCR_#N502ZKnjwFK7`=xk^q-Z;~=%@Ci9`%^Bbm`Jrel`c&a189F z8xL2_iCaffAe|j z#os^i*LUB2`<`19qp{Kpic6TgTYlUwn?3lAg!vO9mtGlx&x9y}Ah?NVtEY zU&WEbB`738J5W`t$z+znPc(9r6fV}P&}GY(&Fuc6qA3Tuha;CdIiIQb_~YCI_bsa} zJ$GvH;(1<8N56!3$l>bdf%wJ7*}a9Cxe%$mOnC10lt3?0Bhe0o3}RoLQB`yS2=@A- zi$zsukEf^Of2MA06;Rj(P0b~j^NQe2QB)k*IMm%|X)Mk^SA4dz0E;VxyFQZxWp?^O zuT;-Bp-g2tok9doJh*wGL>|C<*y5j^CbQub zd`>|tO0?KqD!2g$Q2-_@R``z&OhI6K|HvS@adx}g7*^`DB@!|u(i#F-RB}zT@7;^1Ffdf?|LV}8GKB5`TcDo|`y&wuc)J^W zv9s0Q``5{qW&l(y{UCfK4u8mgDPUs;TsjKaIaOVot|1F(PQv- zM^C3~aUckIEId=OhM^x)`f(@k);H6;s6OT2-38xj%JNCY?VI-x=8?e9Hzb01M)R2^TI* z0DIrLbCZm^lA9NrKsqMCmNjhO@+_5QFLg*fxRD( z*vg0{qsZ?5MAT3pAFa+?Km1K1uDu|cYH}$*=`#k7EH_-?Ck+l_;XMhjc!)k3 zpS{>1EQY|=ju*Obd1WR2DM#1>`crunvg0d6mw}@_rS+sA?r-cP@(arm8LYm#yf8oi z;u#DCXD;UF_k_;4<(AOy`~w2-G#vO#e!Zs5>YtYxarBCX zGVAplHjsV%DyFhhD0Z??XoRC{5(=%=0?Uj-3s1TZvPmc)TUDLLHdYh%a8N!wJBzRp z10n2({)DZrN@L*)2r^6q%Sef<=I||ypi<;#c*IyG0=rYSag``*_UtTt`G5GKKfVBf zbK(opv7}dVyTh0PH*VLJ1FXqdXqwqJt(BdgPS`QIR`y5|VKFeV*yFPLP6R>#`b;Rt zf0F|ix&H+=Z5Y1ho+GFoEz;n!qfr!_1dcfv*O;_q2KW+`MkE|e#PS3<&@C_9Ks7rX z+`ogKgxwIaJkj}(V-Js8c*c;x__=c)geT9ylcz%J+^oEaP>lFfc){v~4^>)PI>v_d z6R@R zX`v6yXJ(w51O3V*#~z`{PvM`m$wwLz=(q4BiaS*OtVJ+ophY;wl*|)$>C*R%2~e6P zgAZ^at#q7avFIOk96U&7@t$MGM0u0eV(cjA&p(V&8%4rN9FvW4)Y6Yf>s>)&&!{HewoN$$x z=6f9S9d~WM(e!9Pw7;UPu&fZ%4sy z`|#oU^O5V~R^lBshUVKy$e$BQ18HDheO35Y|9R=H2?+_eE`8O*<5ik(z+^$uiik%< zE?uptZ>ev=IA2q5N?(C`N>O+Rcn450thY8yIB~V5Jdkf{gQk7Q)ly>GCdfGpTPtlT zd?3*FzqZx&`ZZ6N8zESAeq&U`8+>#2EX6hA$ zrWmzLS3PtLySl;x42J^Qgmmyl(*Yh$`1lXfprxVZr!_G=PC_-uBO-WeDxZMD~sxxwKTXV6%(p(3;jk~uA8o;Gn~6? z@A*X{SFZoXx)}>YJaU$+#*iJJq|=WuKJ~x4=VtPyMcJty*UNB43rV;=TR-~@O6nJQ zvIsvw?bahy_3O!C8zDe;ylUh`7(ZA>F^f8s9}`h5P%>v%_z?vy0ZBKC8;+Lrg1&$N zH__tWZ_t!Hev#6i&%XXyN@Besb^p(jp(8p`P)62N7&sZ448qd=ESYm9qASS=bw;(e zLMmuWE#wEWVu-lBGUGIs{A4D6jN;(9jeE!icTwPzN!_15MweR0Bd9M~Z{$|3T>m99 zHIfH9gBB3iYmgL78m^3|PuY%NCJ#U5DDwuN%obC`NitBLZNL7Eblf`r7th$hEWtAh z!T615grH@9mJb!$#RD}q%nZ&d&f^)kAbG_;{F1;k+1WL90g3voaek%Y=fX*tETc3$9_MT=J8aZH&a!TCIuN({j(;C4o*!)f9~@QxiL z>r=$w3!@Urq^GYCH)QuPG`lT`Ss>){`n7*^HAmNjo(5>}k;`l5XH)6nWJW{?qf#Xx z@X8L7O83NMtdTH5A(VAzE5t2>he(rSM%L7DI+?-RP3tp?e!KFgFc6GR>*Vc1MMsENCo|iK9YSTIYAVLzeqwNrY&3zj{H^hxyy*~tE;TEH_>{eV~~5K zcP-OUj|eS@48O*dq9!o{rGnve>AY1Xo3?59OD`+heJ5MVSU;lU3;qcfr zT!+HgRE2=K2#iZO{20YuhE)dua3J^N$0#nyc=R5`1;qY@>@$k15vCR4Si#;7PJXeD zQjy3~B^9d17d%glQC&>Kq!kvI!0o>TnN-T<-hRLcLP4*zG$q7y^ zztUBDy7!Tx^uNFm;&~8Dwj8_86~dF~U)`jRRz0%^b%H75nvqHF&JS&a6k27}y)y-E z0Ga>7m_plt`gf+F(A2*(1;tAq_V1*l8}-cm#WR>dR(J+s63CaXhcMzb!b2FQL3jvb zEpoTy|ISlr{SJ>qSLPR43rhTmP$ZGQ4O?xOuPEEl0pa=*6HKa6q*XxFsmG`&Km+tA zbH!(LSOuh5U*27T>&kKG2KViMcim1G zn7BR9T`T|l<)M2OI<{)jW9S}>j;+#z#Rqrg?811NEHXj&otiQSF=hYcdI$O-AE#LqdWfmD+{?WyB(PjUZJt4ISiC^Yb){4;JU_%DI|` zm?OgONbjo`fTXEd=+UhnjfZj?sU%AbL@`?Nvs7Sy!EWeEDq<>kNQKJX@Txck&vz|` zoQoY`rDR?q3Ct)OIc7BdNEjY_{+oLPknSkd;7Ft!fEo-y4Jw;n#?bn~cD^k;JtqWR6hF-f#}Gjgr;_ushDvZMK#Z1CYGI46)FjG8A(OX<%O^KQR= z-V;wC>s)j5i!Wv;knQ3H^;2e!p8{%E5@pmhpqgn-L;G7V)U+BF8nIm_aze2KWwTM^ zqt((M6bA+JL2>@|3Zd4AOBDI@64BD2NT?ua7F;D78e%}Xgz}(NR-=TL21TNA0Vqq* zIu0jLA~6c&Z}|abkrd>e%YLSLhfzaIgCZe-?jU|eiNq*Sb9NL8BAzmS=9KycD4bD4 z3xg8XR98blspzOt9w2E!WC{*yk<19x4@+ACabM8_h!d!s7~_c|F< zDOGTBreag^5pIC7qhH;fL6>j9?)N4XQdJL$?>UYE1C+*Ddv zYAP>8&yjFs(7`zdQ{g3j?9efd`VfRlIyx$^c=Z3F?L7e7sW% ztY-D{e_M{ZgbVlm_xp6dj(gM?n#bFsSIlwwxbU0z6;OP!QmQQ;Z!DErV)5gC7EO%(sMES;mjjhz@l2;dB@7fZGE%w zy3_x5wOB8{MN{+@HYes+sApI3$Rqvz#b^wqV-_#pwr$(;Viq2=idW$A3XCP|?yj!8 zKmO#BlMlco-+AeiXr#Pq^W}>1tj$&B5jw+)c*v-Al-!ulXC&Eiib?#YsMbVfs1Wf` zs1)QTd)k%nO;VqD8%R_^0lp=E04eDiJH=3tVf`b0wcHpZN|6pR>VxWoCA*us&Fl8h zNc2-&h@~xUPp)G8+P;$7&f+e*h2la2Sw^hqxttU=DKbMnAr&4_*ooxcY92T?b<4^kDOuwet94lKGW>PwVot2p>&WMa%{p98PTfE=iy6UEzZd!d0O)YVaStgWO87Y6% zXYTC2bK|D9=+k-C{2Ok+>!~Lu-xDXjk<0I`DY@$E*$Z#H{qCopAbdR`Sq>P)(|=q8 z^ugcKe=p8@;f2kW0CYz^#+H_r{)xI?)VC>Hbp1x8pxv@!#f%^V<);qqdFP#XKCPoQ zBgRoQk~3RO0A*XTMPSP}V8gSR5mp<;o^hUsO0*o*{AE_7+J6#Nq#+=Yn@1=Kct$Ki zX{2(6Oe)D%X34WvD(oQ$ry&TMt;|MzO@U2i+W+B)7)RXU5dgPsIh=Lh?rws9p<$_U8+V06U=M-$hdmu zp-=ZW_n8?b6_N?velmA?bG?TF1^uEEyZrFr#S@H`8#n&!dJT#P=PRzFsREa6a7>Ao ze+i#&MAv=iJ)3X4YR*-a*WPmL&woiMpLe>y2(=;}c;M+LpM3KE`|n?y0#NnLDbh*$ z*l8NqXf%u%MuvvXCZhp_w4C2Ag;N`C+}VV5$WV(XC>hzMq%x^klAR@&fmX_CKk}*c z`haAFZTds6aV>Fq!X3wenhgz$Xxrz>OVhMhdO@%BHUEG6R&w&UtpBs$`tQC)e9k|5 zg%}A&W&g2X`k#J>2l`EY=RZB-wOWdcvo{l(efLZ7)DsF)s!4wuq5RZ0r}T0y_MkN7 zzyCcv{vXl(|H`=jd%hr68OAmKe?Y@hadjgCd zHZ7XAe0fbxr&0+U2!*~T-?_H4^I8Zce)siu1RiF;u=Y}f+EmM33@5v{{_-u z^5;+~2+AU^7W(PY?R*wKvtmsWAcAS>D>W*O;mKFoPj)P$aB=JY^WrXFAfH^I?9*9w zlmHqdt`Pd`h3)(Vm7q=^XC^3gZ6H^-Ub_D7$yWt~v!?v21-Q0STw4Ktw*c2xK%wd) z0i=u!4-SkB=?92rM)pbgO z?+}yz%r^h}J3K}CHvntHy?F8TX-cKiGESb{AvWR|F__FFqefzaamkhgGg}LbX$MMk z%H>j7R<=|j&&JBT;?4AY&&2i4#Pu%2_0Gig&cyX9&?6qYk$%L0Hc-IOj0_DSlC+)6 zf_{XO{90}T%3G4j+M$?TVo_eNw3N(l9Z=hj+SCKBh=OhBUN@_Us^~ha<(}=Tpn7Iq zw}Y{21G0CRo63RB-@(|hYCSTyUPE=z9n@j`zZ;L=+rE;xp15cG%c=hI>$KeK%KNGE z{|4uu3dTPFTX6bdPTD^i65k***4V0zrw zVBFVY+}C2`hixZUGWX6)@U z9zSmE+MA(tcy6yKTtiFTtfliDLZPj4Da-wbt#hwiNzsgl9zuo{XUW{Hf1n=Ms#-@I z_mq|D`|ey+Fn4ajqC5NarDb~>M_cI}gTXUA5)K=T9>9GJod)BjOQWVvgMm7$RR#Hy zRFW0vvbs*+L$C&&+G zCitlI>)|Id@r1*WCNF!YAY-U|HEimB4U4)}!^D3^UM`i*1L7jBfRH;JME6Xu#oWQ6 z!GVURzV6O_%>bqk>YMf%TTxw%qz&6v0a7pML8S{8ZDX8kTrc`JfSA{2T}|C zZp`5Z%wZAMdlAKuWKxZH>!_>Yg1>o%`T_J93_%M(i4 z{-SEl2#gf(go{$HZrQc#cB zDOI4}osbm|X&Cu=xXQ_YlXq7zUEXl-;9#$z)eFp`(K~u22LjKR4Rk(y&II}Iqpsjb z*gt6|Bs3tW&&IejmknOLs86`Eir1_u&N_dYjuP~ZuDteIxcT@??!TWoNxtl?sW~Di zAP19BE{iQ5Bc4}Afw>+gP=jSLAzBu!#U7o`;XulORoAe0?=^&8dAa0k1a zmdNt2Wa>hs1VrLgAb?Z|qysw~8CU*5n#-qQo@P+&bXSUL)MP~o_0T{cDO58w)ZY&P z6N;ih4T`SjCTMpc!TRaHejjdQN}E7hi@+GzEt*=jP5nn%%g z&uBEGln(ZMvf-b{hgg4wZh@*$YDKn+6hKiZluDqRvZdJQ$$x6v_db*!+d~-JV;I|$ z6pMNYV|xg?&0?6vBk+5WJ2*o0CfvCRN|?aD9D@VcrSJqF7(`;v;6NXoB%lFY(KTAY z3*bnFJcTSLM}iX5B!Y-T$Y4}M?!H1LUPZSc?Sh^jbR0#lqmYUHgUM3F(RZh$m=kB{ zdUa!A@+>@&kufk3NVQL>ZNAJ*06_Y+SjB8@i3k4T@hBycDP$5L>~RPL$Pz8j6R_M! z_whygv78&Rn+=9QkhoeSUategM^^+vhE&Go z@{%u6D_&g;Lwy9k4sr+QhY{2o=^&tD0?z9(nSn0G6MVpAvRX}MWUZSpf$*i*gK7*o z@o{Db)L9(p)lTzMgpA2xDpd_=+$$98)y1&!QM*w=?%*1cXW))Wu*XuVOwP^91_&8X z@Bw5bsj{+FaGPNA;fgQ+fB6HGyI&qSq5@KB5u{QcI3kZ?WQx&JChapL;7uk|45sfN z85$vXy}F}XD1Fs%VGQ&4lXV$yJOWE9O;Ef%}&0#bjO z)oe5(!a%c{c(P%s*^hK}rK1 z-o6r@2&*}5ZOLRy%Zr?|ltCgf{23PqczXIUj#|Jr>F|ruWV4t@tppxj4JQzWnS2p; zAwj7Bxsa^$$wRJCf)CQ+76hCgkdvG>>LmX znW;DiAnza-aA55Z4k8AH+kwGJq4yf;Bb?NxU8`EGbjX62xVAvOT`*|TYFb+vjJ7r+ z1^fEihDa{-P}|^O+nFRnaWy>S?-9|k^ZlNKhdwOiUs=8Oc+cuVn2btNCd@^ z4f%@YhyzOwwH%-&Wnmdk3fUh!MViuUK6=06HQ>QD;K5lxp!8sxA3@pyY%$Q$ z2%(1w`THhG{e~2CO|B70W*oSQ@A!_?#gn8qnNUUs{g1l4`Miju6;y6A#WIBG{73bI zPW%3%L=zUALatIxgQGb&X+v~i6wc-y-x0iclHj*3M(fFyn;v_5+k#~`Zh92D)t1|C zUB2M)=N?6sF;M)~9Qec8NH}2z&1OBk!cIyKQ{w6wGlUF!)(O<< z+pgDqR}DcS2NmJofDD>c4T*vx$Ih1y*}}fZ4hR<4*AdC572`vY&rl^exJ*ikCXo1e zTKgax2)SY$tn4IDsnIBtLF>?vHJF^Yc3!gSSW_bVrVUjKO67dMymUd;26L$5fnPsR z5yDyU)%3U1w`5||vzr}HH+|-opzc8lH+X4)t6s2HEy+G~vb7)Wc>SjS)|1)F{(bMi zzt5?p|-GT`-P{E|G}OJwApADpZ9LvpL}Jt-D}OGRBa-J~(I?@i@n< z@Q05C6OJG(rdSDBd-zx= z8m1RS*b*!%+mJZHL(!8wSNp}!Kg<$(`@0NXM?U!A1En%oZK#^aG#OO^^5dGnApovyl$oAEKH3mcC+%E={lrP$(d7ir#Wa|Hrk zr`2J%IbAM~&lkAt1G%28GVsjx;2F}{=6diaHqQzH4eI?C97Aj zE{?cIkG=cuyGM=GfVL; zp3E$&MF0BAqD*%q(hV-SGb=Yf{P4pYE2(^K5l}Tq+C=9k{1*yE!aMI=surdCk4k{K%B3CFvr2+udjgLF*lto*F0+I|ChqSw7!3oC|Wn^Ru zSus-GFdAnGB^h+FI!A&$SPAC25R*};RI7+|%T}q?c}PsjS5uYRB9F)AL`@tdo5Buq zIX#%Nn8%m~fQ))j`{gp-Ni~ZyNH>D|%hNDXlMpxoB9mZrq2}et0Hhj%nJrp25wY0R zWE#zvUs_^VJTXfLiluizgQrEMW$f`mjB{|DTSikdy?*W5%p{h1JaZaW!8B?!K4UP% zB&DlXt(qoD7~sdA`VroU8Bz%0K5V=YVGN2d&4#!^@UB_QPQNml(W1tcR^m!YW{4Ub zq6SP?#3WH#B1<8RMI%^kP{fES_zHI4Y7W#pHWNPWefu;TQrP|ASNr#o$C{M6c^{TM zRY9XfJg1t$m1sG$u0t^MI+{}6aKp&Rs#WXOu3UE`+Rp0rLzJrGM(R<5Bx6RSiE2iC zT+11}xOeZ~dRhcAhD(PN;dr#YpiWu>LDRjH>^JE`aY#9ubtSl>7> ze7%pV{u3o{M!xceMv{$up%IT;S^%SCM5Ax~=gEo-P+p*99u=KLD-r~X2Gq<4H80~Z z5NBs#i(<04!*EXxjO?qXxYcPaCyS7mMa^G>&eZ>oTBo>W?q0BS2lae4B|maRrI`sZ z*-VWJOo$!@1iDxKDlUs_fV0ayvcH;&R&!-=2xUv>k;jv}#7nq>16c8Gpu$U-&%a^C zuQ2EuF6ixT!=&MM`bgW8=*E4%2T%2=-Kwj%>45wB-eL8JW8=i$gi$O1-s_VOkw>Ft zi;60ku~#y{Dr_d&GYdJXc(R&h>Lvw%QZI@bcp5`nUPN#)32#dxl3c0q?Ol!uHB7y& zYoaTuck>^-3M@Zo_0q))A@d^E-s1-fOOvB#-hA`T*T2xMN-a)3t=x6+;K9${`um$- zHum)N4A}SUWv~JTCy~V}Tq3xDq5~I|o2V8oN+v|v=6(3)#el;pxz5Csu!I_QbbQR_ zh&& zE3#4@N9wk2S}O6LI(4dhq|I;c0)SR$by$y}zSU8i^rpqtAJkgJ*Is+=RnwO#i>_X< zV8PWIb>7N{pLpVlO|v6X*U67Q-+TP*@vP#t_ins#?doMTwi3nF%T_)38|jJ zfev0hc`@uvIW#(4jF1s!rfU!)R7s~VDC%|FOkM<^Qg5fF$3Co^OISDESU3ByZuXIN zGcXLF0ni8lqYU=<=}0GNpr?C#`(bbO>jrfF#N;Bbnc+d54sb>N2o~0GA5l|*T+|I^ z4dH78eL7uNH(Da2ymJ5G&}4J(FoFocuwXX=XM+8Riytm)7E1WBcu;Eb3^F?fI1c^; z1A!PB1+^Y8!@8#+<;llc94(Kp&PQd=T)K4W zU8|`-fg#^vzVO7e#FO*Ryg%N7muF^DzTO7P(*_R&Yj%k+$sRXJ)~{bbS4PR^;-1&% zVkf0@R*3O_o0n!TyYq$B;P5x7?Yv)XykSA%%saM{riI^9t?9pSDO#}gXG^A+%(`LY z-M=GVI$H4Sjmt`cn3Z73DtInv>922FHajOPGvXW%WaU;YyA9QwX$bpkHf{a=dKmd{ zB9wO=i80>-LH~Ea5ue|>>6%jbUH53Yn>MYxX2II0S5n(o{Q8dTs-Rn5n@zbIfYm>C z_Uy60Zh!UY+4Hq{*5IPDuiZqcSKt#>%jRTL;n8z-c>SE6vY*3!?RkrqFP*(?E#6;| zgL=&<7ljSk0$h&CVzoJ-;5uwpyWIkd*J3l5uz z6kju2Z8jHlJiF6jwU|bcKfVIhU=WD`oCnIGXgObq7ECq7h|9`pI^!nY3_4^#w@9(jG zw_^Qn#e$iy2ac7PkMJfDe^T0fhc5|k6GCQK`dFmtg*%ik?qxLSp>}1!+Xtfvb|n{< zWi>nPL#3e*X|qyEAX8zLGN?GT;sk>Q%a}vw7+KR7F1=1YG9LA@q{)j%c0vJuchA{Y zdz>gs{+^SsRHZgg{$;#U+icHYwrrWCr=I#)%NZLx`^ud=C+oj~w%7Kl;BOp`~TzVo6e5dCe`){BFgzr*HWg3jIww z>Hfri_SQvnvZ~pP%)$*%u0>6o$(p8LP;Uz#U6JQU@U>@923CJrE6U4TSfx0F0_bo5 z%}#R^)62_+@oGjiq7};X^WJ*9#$dMdZo8bl%w0EjHanyFZF)Z7WN8BzZZP5qOM4D1 z@ZqHK4%MQtRmSQMm;emqp*E(br9hU8rrWm3wkC%XGC#_)x<>! zX1G#f^n5_h()KZYXT*D!b}qnkMyzGzIKvMCK1=hSB3=3`QzECz|K~q5sWp{?dlqTX zaSnQ0DY&N;+#~Pn8yZ4Oq`v-sXi4x_^^nA*C<+8pTrP6=;8Q~iJ@GW8m1W#-NZp>A zow^QM%T4KmpSb|kNAW*D#StcxFFj9n29vLk{z{u6N{^nX*m!UgLq;8A+{Y+}I>=_+ zLTyKP+3k#57_V)IWJ^82ol$_SJsDFgw^~s*$^kpaf)1qMUhSdeOdSJo?J>uo(FCzxzLTgy1U0kymt`gfU& zf45l6_~jR?0m@lUea`%P$Jbi6QRf&r!$hxW_>lRz@e*eIcpOPQBs*?7&b}OHUyHLZ z2Q{w)H46arHMR}4q)jEX(Q0Wp)8EwH*$xxFt&_T_&2zggkdH9qY_`FExGqMW7Bm{r z5B6CdZo0r^i!zf;gUv@;lL5H#^~eAEqNA_pQygDWA8PZ!S12)4U0wajSHHT3*x0=1 zfBT0&{NeE@_xHu<>=MaHSDUV({Xvk{xKhbEtTi>j*tE3 zH_t!+{1XrD83dClGG8IbLF2zI_0k1^80$TwzBKOMY;(e(wFI1F<8ae?#*l-WCzt1z zm1$6vPOUC0M}6&r?A$^)M$4w>DCKm4J1EYSGZ}6JDYbTF-zV?B{>JCE?cLb1`d!rL zS_BJ~YK;cv-o(@$bE`!OfDLd&0(FM0%QmG3R9y(FE(BFIpsEH`mFUf*efj~2FUUDW zWzK35>w+!;olL-DA*XBa_0*qIb~Jvykdmd|LV8Ea8}I-s!o2|p>5GiV9$Q6Spe}4jV1@+07>x(DRO8CjD&)fA zmX;U?hO9<2mONyG$wZPllQbg%?ns0Zi4ZN3i^M8lu$%@?FQ@(pGQ(%tXnxEa9LpzZ&K(#n_R)MYFgUQZKMS7@*1@ zUJWQN{y5LxoKN-t6RDSUmI4<7ZH?@glNUvvqvSGvO28KktW0fYzt+#6_1GE|HUAp_ zVbc8x8JdiaKUJmfL2-9ncLRm|(ZrjTj3z8Aam-;FnOcpKP-D z6Y)g|9ptEU!<{VxG;%m;5=#!2AB!#b38=o=mw=^yIt=^Y|b4JP`SYMD`r z_cAL(w*U0X)Gsq?zm$wFbw%EL_wOOC z<$Gf8YUoTCsBamwv4PZRq5nM#hS;vfe`I1k`?74rU_)sKtHC^C8MQm?qgdJ1%m{(< zajD457>62#9#pbysZx=Xo1;L|)A!z<Q2{>n#nuOrU9ZWyQqI@ueRGw!-hd<-5n@13*+s0?I-(>pT%*m z-ewyb8nru}23;GPP)w;zIMQzDUxSv`KN z>}=jd9sHWd5sE??N-00&aE*_(DZ*sP<%NX>l8YDP1#_>OIb)hE zX}<{lAznQz6%CL^9Cl9e;ydn(dB>0eaM!)}-V0w`twb+CFy!rHOK1x5Q1QNay7n&mw zrpBVNV1&s{2q9D;ho;HJXhE8zRKq{QVu`T;uSWUpAUwh0D8&ke;pcZC7sCO?#uvbj z0wo6yPSpNQ#G=7)BoL!#n{BvuqjAgz%#S}X?h3dR|f(izY~yYcoD1<2sA}lh%h5VH5t>xzZ?w&ES4auzq5I)M8IvC#7nNn{IAFS zugCna$NaCy{Fl_}O#>zqX4Ry3nopcOv}ezrgLO?cXs~gtNr!Dh2dShnnVLE}pnBNM zW*eru=498X4H4enuJ+TXPhaX9u+k;p9{K!(k3Y`K`{<(&ch?*^b`a+|aMa4hroypa z_~esM8o<>Wu{gV=L@qZY4&}y-nrtaqmiW*iE0u$W>$#Na5M6Tr6Hjb=?6F8>+qTEH zJpSOVH*MUw@s`^)NyOlinpKZJ`sgZZqgE4*j*X880z_dUxmcw|+zD@V{rX(4x1piI z%`I9_m*i7H=biJ;SH5E&ASF)vYs`*ACjB)map zGU<=A!S3vMFiDs6HFYD>;Bp~{%G%m%B_$?p7Iz5#ez@83!~qY9#^b>@31=+KaFQlG zIN(dhJPt>~XN4>NTpK8taL8FpFdVoNjwj_-`9W#r<4 zFY=3XQ|5*@-gu+IoXjaM*n9Az9x7H501|vg)CbCYq}SfGU;zq^Wm68gL7SQnpYzHT zG_zd6s?K96$`@_8^A4{!rJixiv(G+z%M4}Q?Y`~S_19qa6irM(>Ih{ZyCgK!n6A?r z=g9!WTexslr93u47xLkrf)SMBCmC{0v67ua2}IaFycAbmT$l-;Y_YZo^d*)Q4o;-+ z;y9pq3{GMK8*_k%7R0_dU2JxbW8He(2%wD`P*mSyfw`d{wpiU>r^)HGfbMXE2mC(Y zI3fo&S2!c)g~l-IO<`_=4xKX+3tq|h>%PzTef=g--PqfAaNn_Szumw8+i&X**EXGm zoqV!M4-e_kB;VVNT_A=Ep`_VJW&U=VclClZAHp(}#z{l0Oqy~P)IOwxY?ax9lh>4zN7B|vUc7% z=~k4_sy#7G>9i%FnJ5+VfN1`dprF8HatT(fSW%vG<|S~>0iBs6rJ03NR&^m;QL=b# zRaF)2q=5le@xr^1xANqg0=C1vcEwz3t+oV=7v*LW;DIn1^>_*jawuq@HeQxj1efJ> zjU*bR%aU;t<4vHREa}z4VDM2W9sW721$IVaEK{bGrYI4t;#4Gz@HUx;NLZ^ta46ln zHsVhP@pVRVRy;+QIT2rUI>*QD{R3%D9-*Ir+eOlGhV8`R<@7m#ohm^>C}NhFv9Vy1 z@?b8;@kOvn$Vs9>)**LdVscI@F((8iQi(aa8go*CImyPXb@g95y1!?zd(ezXOnW<@ z4+~RaGq)Z&a-_cL{Ap58_JpnO=+Qbl8+PVs)3LYT_~Ofb`#yX5<(EJF5J1JhYZY=$ z91GQAU9tq7B+4Xm_o`KH8u?Ih#f z(Dqb7)!we6L6cO{P+TtL1Ti2A^A4 zSd=Bi2;_1o$x*E$%#LBfdeBxGe5P*-gLNFF7A__>t3 z_X4)Q(}$1t>5N1^^ym)lJ^J<62%a3iaOuRcW5?<)4M92#0?u6tl|-cWxIw6JM5k+o zy6GO%ohH%(u7J9_*tm1t;V=($oIihFH!$L$=NMvHd8%(}dW=!n*QL1(0UKyt8J7~U zq4IYs)VnbNRGo0*_A~_3P#8=iZG&ER92>jE~U<5ILzQ-*XXF*9cO11 zojhq`=G}PXjd@J_Nm{PSqujwe*DoxRKo}YfmH@>@mpHcH=|!hu#0PJhJx>Psm~;)3-Yg;DU^wb%*hs&EiRi*8e5ktRfQT2dMFWzlMPE*gs?nUq~<}sr$Sl{2ftx9 zheQIfyh*P|dtWv?X19;|i4aNnZFYK&SMN+tctT@7JJ>y#WCZON@c*b~0+VXD6H-S; zunRcNBSaeYpyo~`G^>=yL3ZX43z`9bI!h5B(PI|T9mwHe#==pdki&#_6!kJ?!pU_& z&{Ffj3-iDWQ)np_(T8ldww{(VNB4aE@yCFD9y{M@f{!-)DA@$+>PT78hK92?C_~tZ z&(f-oKFZ76wd<2l4($0CKK*%VDPk=bK0?SYyNIYeEEWed7GiSc^3sK9(> z>ZBq8he;{4*=dwFgAu^UfX%}v9_nQlycA{N5iJ3X;@Sl)<=m3RTaU>@T6igthV$GQ(vJ3v$%O5`Q#0|Yq)N?wC(T&8q^S%46!m1G!#a*&D97rb64KIaaqXlo(-L@x0jlsw@_hB;oEMTMhC!acf0Zo4T&9 z-$4q+fT`?mGAdMqo*b1_f@_C$oW4m%Jtd!fa#$Z1u+Zx+n0@h!FX|+2VPR1b>eLU9 zW4!GXRBjm7#tr$%VdM(J;W-*uuSIiKprY#v>OrjG6B8U6$OJkOO7%|FUnv8vW?>D?02?_(VjtK z7@Qo`)l&PknFHEK%>T{z&OeU z+K2CUfn_w?`dTivw47@=^X;b}zxn4s{~2=oy*CQ z_<2B>OeW-UJ*O?lQW+X*0!N+ib-R1dlLrtVjYOkA*I=_xgL@qB{ zeCw^ZUQ;NSYZl?bygazbB9Tqo?%(q0BPh(i_3j5Bf9mn4fAylv0qS()0eVI<8DUeX70B%?{R zWfK8P0+Gw*^6(!^82%#A8HE|kv4%Y!0;Y5AX^-!{u@h zpml}GI5uwd+X>JMa-J+l6Osc*pwqalz$Y6Z>&vm5M8MHg8otkKx=GhJ2=95|(T zx);W1Bh{)ckV?hylo1=0^!pJ@Yin((%t~0Im6DXTmS&bp8Pz!qY3ah%cy0B<(j@e1 zi>P$j129C_QERjX*x^uXp4gu6Eszyfs{@<uZTq3O#ic?v7wCQ2&jRNQKxC%*+rLU>aWH9{D+k(Z9 za+=`ONk0pmFTo&Q`!C*>sTsJ!?uVg&^qacxtpAz=wXptwX!qM}Xr=(!J!SomTCHSG zzHj$`l%HRq*{u2VfXXzaX4H~JF)(iu>Rrm zu-otXq1}&c53dJwo3j38=+3OV((Z?$UpD7It^bmFC9wW~X!l2>i0@%yrmTN7003uS zVfVw(clcA^S^r^+1J?f!?S9DXU>Mv!W&KAOD0ul_?fweLm*%bgbsvMzTX5)XKs0Xa|dZv*two891NM9kWP-X~f)_B{?KfW_wt{Cv|LcigdJzMRgAL>_r$+tzz-y7lg#|NPeF*WOMxv|noF z*w9ibDT3_TvTti}-u9sYokc1PppJkGD>H; zU}x!v?QuWJOck-+WCQBe%E52J;5eSxT-+r5uV&+v&T$_`eIG{s07iWuM*RRrUD4S; z?4EE9G+#IlOY)mD9mLPkcIL#%-MhZ3J$|&< zpIm0hHh5y^&Yef<5AKBg{qzjATRTfCjXQf=>v1|`R8U$zeOkT@-aR){qA8z;@Oc40 zh%~1Gy$#9Dbj93jXG`2Z#mpiJR82*ZoDZ@{%k$-n9^bm6c-CsT*jF#P`EIDXo35iC z)2c$DlW)c4GXRWNiIM@Y2ir3d9QUB0D_oOiIB^3kg)*;XMxlZq578B326U|mm8r_d zk&}r?MkK-(=NIG`6%{Ci=p!0uWMr%1v&~bWKIJS1gO^#N77_!2!y{1~-vlbw!~#CU zSbDxfp#)qm8GJT^wiRRbHN8K-tAdq8$}T!k7{JJ5W4`?5>c6~x+B-q)oq&G=*>Dbz zAN!KZ@4xmjbgYAJ9T;~L0Exq4gNBZn)IBnT4Fqz#++(2$&lq5L5b=|GA@QQj!CX&y zQ7SRlY0C@qn-1629X~Es=Sv9ny z0~z)?Q~P;RH0I-v|M_vvp%cVTDQVfdG?iSA>lwKYX@^?^XN>uwK%zG> z11&<@nqgtU#6XiZ8%*cEzMcW6MMtb>M2VbZgmIuA1d)FN{UI5gLWY-sCrHF_8rFXa z)_)0jLel7UG}qUkM^Np^yKfw$Xss9m&pyFds|7wg2ti8PKnL^?8RU0r?S|U!c zg(9(7m9NSznXaPBv|>oLaLA$8r+kK7?swWu7MIsIK6B?s`B;HAtFE`FyK5&x-f9q|`aceLbsha%H6FcotgEZ$?|3Aa zuR;L$Mb5A8x#ym{pIQZ^55mFpG^h8G1@q@sR$evx>g4U%gbR!E@>I45Hhc5(3kr)W zD`(G{`*Z3xZ7Hbt_$}8=pMLGVkK@seOQug>@cXLU6-b)Ws^uK{}6SS(5< zgP!2Tm@^t=QOO9L$Zr1x6(2T_`NI=orbyzaJlaxFy19LHXvp2$j7K_D%^2-!A^bE4 zky)WokyAP+N>7W2#!^70`>BZ_j|G5NZf3Sbk|&eoGTB@npM^F`LO;x=Qp}2R!WWAv z-9|i$!?&KW!~Y%*mlwp*|8Gn922Y?DS)g$+zu^!Im>oVlm4#po6>eOL(@v`dfAN``>^cw-*2n zsXcl0$RR^pdtbNo&9~oqJF9(Q*mCsLiN+n&+uD5jxutIRsn!6OYinyf<#sPVhsl`D zN8_2wRj@gQf(-7nH{W>sZ58tuEn2Mj<8Pk%z5JSmGs-vKy>|W6xWpH=`GFhGs#FWF zO2UlQR4i1f&fgpe3^_G(=FGHXmo8N16e#4G>cVL`3zt;RsjNUGqjFA3zN|c3DOOOc zwfWJq4p@1vu7(CzG6>jRS6MX5%b9=u^<`PSC|#)UY%&k%IvN`c`tFPMXKGrykDqMm z7)YB)R?DdDb^7IhoYZ^S0FUIU>l@FXyVP&WgaoM7 z1p)&{Apyo?a>SeSXhpJOx~9Bxx;&D6X6>4V3l=Syw@|v8(xREpe{lbBTUj2P!UaUG zTV6SRdWj+o`GCBfjItGw4{8+!$?TyE7$u6l5{*hGOfZEa9zVMyyd1eR1dI6gX>3PQpOImBp6(gnc_<}p)Gugm2CMAzr<(CK=*F0?iEhE);< zYDni{_l%|z2>~`&x6>R9dR&kz_8}YDswbsOA*foSnMCvFYnVXwqdJb$xo@9Nw`*5> z``d3*fF0W+up#T}zDC}hjZPPXB!Dm`iz5=P%HF?!W$F>efjYjtf89EF<*{SbcV+ij z8BM~QS5mteH{Hc8x~MX(4xkwf$^zlrsNxw4$$aVzC>K$CDv5#?=|(Z+EhdxajH8j_ z1@91#gOIJkJ>~r&HDay-53JI#s0HAGYrq3JU1XCR>1sse=t5U3uvV>ID5=^9SdUg^ zHbasT=d{gfHX5BaGp5~Qa~SEI=5e4r-OXRnG`*+Uf}|Kr^S98<4pQ%F0rBLtvt|R8lPQ3TD$G3MY~@Q%s^zz$%E~bd3h^b^tXBwY(H+ zicU-fqA%vOqsh`pPy0m-@KSp>EZ^?-i%=Rb0bB_^57-8>U7}op)fx!L;ogjgC#;jR zJB3oa@q25g;HT&nG#@=rc>bqoW^y^PVt$HlHABW)&<+r>AEzCM;qlYFHPZ;S)@->< zFaDM_pcsGFng__T!5`=T8|N?G1flebHZPE-o}ADxuwg?n3nZ*Hvosr0455fF_(?*F z)zY6Prj`|h(}FJeDUx0?_MHG(^L~n~K5uMF9x1_#q~=u)XnVQl6(u-%^8ERx&Q2rL zdmdCOl$#z-IlglNL2Gmnp9^9@qHK*iq8tpJ8 zlFY2E7$rlT4PkkXfP)I4r%xl1t+%%u`8hn|CrL8lAxZm0OUN3=m6YI0zSDC$J9`@Y z5#3Qphnkw3fyfuzK7Id$J(0j`fFRmvCNc@NLJsg;fRnxU8t^`1`St5>{n@5XU_q>O zIV}+&pq?QRiz#`o1|3C`oQO(eFpsvC{-vqa4<@EsGnmnq%s5oV4%d&X;(@8 zRx6ZxKx?4n=yWZY8ZLEpMTfiLlqbIu_Iue#nMYv64deB){=O1pn92zMZs*kLE}cJz zIC6JSXHU=IU}twdg8j!&4JJA|zWfr+*Ya(y!QQUE5!5xXbe=lZg@tY%Xus4o#u-J} zh*sz3CKSbpY4W^%wY78SW_lapO#9b27mce>3i?;9h6W_f<;!c@oV=`@=?Fk;c!5}Jwjbr~z zc*k60B+if^jVQr=9&b3}(YK=Z5%$k$)Y>-;$u%AVxENJ|qoyIib_Y_k;3Bk=F@)`? zV8IfSK9fPfmLLz33`B7hGMC#uMd8h$Fu|N{2A4bp*|{0Ab9T?AZi{VrsHvj`WNzsL z6u1Yo)Y{f=7`2#nEx@@T*4Wb3bpcNFv#r!;+N)7@37rBXkU|Cvi9f*tF`FE4Z5exd z`iZD@!_PJ5aC=7WHgNguK_^G4diUMhz6j!w{%#(x+k=qngzm&nxX`&aUHx8I8=rsq z5~;NxcgqOC#jk?|7OhCt?Sm^|IkAZZCpe>OdwmV zycshVAks4{KLgwj?_|K#3pc6G?T!jDdl^Z8kaX1IiG|piQG7$}Che85uDN+uVq0K=BPhdp7WTzvgTnQp677an3$Eu||-tiVnhgGbRj!ufF!R1 zITsv9a1`_&OQUshVHsl55ExE>k{B9E@G_|=QfmB8UobQ&C#En@S7gd|Hg}tC`o5;t z2C}{MA33sTkK4U_H-4@GGjOs6>RFN59FZxN3Cy`;oRr3~A(jc@=5^bhzEE(?OcxJ~ zah0lf-mL9UVU}%;fxw(Oa{0#}W4V2ddHtpp^NR2*nb$6)jr2N2*#CyttzBz2FJsw9 z&7KG!g0FaH4xk2)Jhr?X8J`CEbz(8rV&wk&ue)x=?VC4m-Y^#us3;KN7S5P4A9Gic zD+b$Xj-NOom!DVgB`P@=b<61O8dk#ViNqNwtB)fplNA<@jTHgs6orweM$y1bZBZgI zjy=_boi|RA4B(6i1Z|Wd5N8M&VedqOE(QoM5;W*tHU|WzeFSzoYQFdNi9|dXAOHp@ zTxCTeApcAzlI1eyfcMzchg7a`m~qKvGup!`Ac7}-6Kbry>6*zXF3CTgu0cmN=uh~U zR-=T2Eoa-ikek-n^tMof#A;54O(M0S(6>xBv$;9`{`)V!7>|cSFTF(lNh_J#ZZ_Y2 zcYAmDTzR&Wi0RAcA|YLLIZN4ADV}@|XuKC56>8+4eWq~+%mO(jj47`wW?xfWT&`Iz zj6=0xCZj-o;Wy%OGwSUQ44gQD{<^(gT?pz(Y{_s~sSHJ@3QWs`D{2a`%=N=V69OLRo1-x`$5b`U45b|TbvG6s;(~Fk#k)VQ6#N3o5 z9_KK8z7r?(s0Y#AjS+~+2-4TjgL~rg_1}njAhj@l1_^j0=HW(Mf9cutjTh@rpFMr< zI1+r%HXl83yylb7KmY2)v9m|N+4}{KS|DkPQTrv3N`)csoXCg_fbjsTj~vMaS+X30s%k# zHXbi&g`H6hpBIz4Y*`Ub9)!0hczJa^h*^0Mv+@XL-8{?9+r47Q`GX4iY~zVp@_ufOslQjqptGGZ$o zsz3DJU*G=p6xx$ikWC@#=pVrL<7bIP<+tB{`_c?MsnTQ^wU2S-H8uJGX^fGZaJSXa zs#S|Lnc!CS{G}VVKK~mwr(ngc+kX4o-#xP$39WyoURC`2bI(1uX$8DR!dWZt{`Cvb zJ@o+fyfzE&!6+9lM*JAZ=XC1ep+0WVW)o-<3Xa4BD_0f@0@wna9C6-Cx-wI#U}N`U zip3d1=6gGMa`OYLA~gced@TPxhQZRHj>p z7*c<4Pg6@zZ(kE6d_^b}!y*ZWaGDsYc?$QpT$-Ps4RtZ#ZOn=b zgK$}qM2K`l7fByXrx4O*Lv|J>nj2Yi>4bMock1xhdyr0juxW_2YxfVG2dJnXrBnfh z&K6FX&L4ok-{Ts^PB&<9#xvx{jtzRz=X@sLH+YN|E?t<5Nfik_>u)CZ$`0yfbo^HR_nooL+BRH zvteC<+%}raApipglaqOk{Lp_iaO7*uRdQg6bh>16{rV@V>$SYV=`&~K@>2owS+ZLI zuQQ#RHER|p%AlgqRG~)&0?>GiEx}dNKImLy-2_3y;NreLB<%@Vcf-=gg zShDWHr=NcMu_vG0_QW&KJoE7SB?Z`_ifKZ9X|m^#P$K##UkOp z7*M{1IIqC7kTqaoU<50^l*WbCM~_)Nc;jAwf)GEi(PQ+bocl5 z17-{VKcEO*eFK=?p}r1?qE57HT7>iXP{k(**##*>&Yws`1CfbnC>#j^!Q^rwA)QE{ zfY${9m9DGAN+G9iQcL^3eY6>TmbQ;tyM~O2H*~hPTsU*)LcP)LB3Ld{*O{hEXRr^Q zYHDe$J6d<{LOTq7b00RMtC7J7bbLRgbUV-(jksJBj3KAl?lkL10Cj`GWFtAH*ot60 z(X(3(RJQv4cfW2mVqtOxqD)dvPRSWLd;Z}2*opqt)Z^e}@?yj7wMf$;ThY~7SzL&{ zE{L%@r$mwk6(L(HirWn)i*a-!v#>C$sc9t6F9X~ok2BUp&%Sn+Bt`xD=}il#XF{}d z{7xsS@ynH{au@#`+t4qnt|><@p0IGyt&m-}U>~~L=c5!kIkSP0TVDpG1`!K>UyLix z!iFODxyF2R=N7R7Cde0SOgfjIEzU%u0Y{+7;LG!K)f$%DOJ+1C%;T};*of2_VtHX{ zAsVzXF)65AqP?2v04S=9fIRH91tqwi1fGY$EKE3dkf;sO7fG;)<%H}VdiDU)nQR?x zJ!2sfZ60PwcpmVH%@<4sN61zLW*O+$br1H!QP9&vtVm#+0N)(Pvk_RPL$F!`2^L@_ zA^&K<2{FnDDhOex0td&UVc%sQnA(kQy}TRU1X)wor|-q~bLyx=cj@54#u0;o7->ia zB*w03s0a0hddO}BALM|+*xyKSi*~Yuz%T$p)@nt}6EX_P23ESv&|QmpJ9_>_AFrw1 z9Oq?{a$-WC;nJ>MyDl0cK$nHdZe%ta#~AW?Ye|iyj4XHw^OPAer+NAEeC{}YZ=932 zoaSA#bQ5--M;~2DZO@xN%k8xK`Fxb#U`nPhT>tRH58tq`5bL#w>_u{Ujy&Nr353=Db^Ps1QWsWZ_1sR0y+=GFdRCAm+6t zux~gC_s~!@3XO=^1_7TpND`fpWQ8w~M|4?hUk8{&XTNm(&2WMpv7qNN^oY(L9Yjq} zj}CiL5B4HuDCBg;64Ek)Vn}eH42eX7WH6-VGty!OpvxVTYm?w$Nw=?Z%u6}eW;y0X z*4^EDw&wUn)Xr+IKVH+=4WFCD7iA15vEOX zXqix$Ay+BHF!sfn3YA2N0ImdenH)Z!eGD{;c|6esEgK)V4e16^)WB%74(bf!RJ&FZ ziiJq=U{1)TH~D5!MC&A_O0gTX(A-Jkg)3RV>0yc6mxbe;jWJ>qb~Err4$nj)=A~s5PU7ZqO@spg zju^+nWL&<{VONNX`TgEt6u7LZb(Dem`iWjm9cR1F?q*MTV?m0Yx9hWAhu9+v&Z#MO z;P(4(zk^*mxEAOzl?vwcCQhhn=>yR@B+AMjC=yzt+g+tgv|-p!7ACva|5Yxu_@ z_PO~ZI5%^G`M@3QwS%PudB$h|KkR)6d=%HU_nn#TMY~${UXTO`QBALsC^i^3+;GEj zFL`zx$01If*t;50+~T;zcHDcx7&lBYV9OL^iYby1LUq*}l2DgcyF1_i%)pJ3!vc-eF#pY>T~zO;Be2DvNzMEKdCys zonEExel1ud@`oE*iYuXL!FKny@LgSuU&F28=a#Wd_`I>P*ZC$0x&%(mH=ktOyD+%@ z6K_!uK!M_b1)g2gc6YnGMFq>NE-yxV;pktD&l{?1g^{^T zrS)hLNwXO`6-+39qx=o3uwd3AdZ5f(R--2=j))rNVEp3)VI-_ivmIdN;&dgQV(66X z$hMPZ{O~DWqF275(=s}x(CN^5Q@la1Y^9T2M#A4u@sF4B0%clAr-gJf(&@-~YxM)Y z@;sexrV~XNhl$JiTK-phMQ+DcbTyJr2hW@0aeC!@I^9X9AUf?CCIx@GKLAM|bZ>QJ zXX6^NVb1Qqf%oOmF*_*HFO>Ju=(FD+=g7g&#j=>d`+*&6#I6%()eyqW6aq!{waE;mQM0mY4zQnC&l~p%4c-CjZP*yq1^MN zK&Saq$h8WfTa1SDq@ZpG3mGSwHqNE1^HbBGlVWI($mIPU$-mrzLWad`s^=KqsC~ADlPEtMtlVIxV78DxJ>6$Dy@)hF5=Wp#SuH#~QoJ z{p8c^Nyi$8aN&jQLdP1_qD2`QkQ=O#FR!=)moGWiDD>8^Uqk+P$X0JM+8M#|*w=yC zgB_s6>A=m@as6Ol!3Clt!0k4$UTib_nBLvdAvtK9$T<*DAfnWToo!!f4mP&mmUe($ z)=|0_thv2d3PBIt)mRJ;NoixB^x=m;HEQ9TLf81yhhjvE1>5_8lz9jpG$+LdW{oXn z7Hp?xu%!-1h}jZ^4RVmh#OBP2hb!2M3XdgWju@dqxaw}WyTw+{tkGf{2_M7Q&!{2j z3j?zYnzH?fmcy$|COsn=Y&X-H#E8a5@EDpJVdhlc)ChJll&HWuZfHQ1cOwkJnd}IG zxg-a#hVm|gjH#5-?ANIwul{~Lv=|S>O}$4KBnDv~m}Yqy-`lTIb&4@3x>5D+6WhQm{N}_dFhx$C*a?Q>uA_C;`=K)u1*v{x z8Q|q??2^{6KhlVJ#3|~=!|OZROH0L=L?gCk#>AOdUw!qAL<<^aNt}AgC6`PY6&yYh zwA&E@lVZRv)S_dVnIkOluuMC@ULTn$EOy66AIY?TWd=zIPk44GC#H6+gHxY96=;0 zhiDL@STD3ez#>G}4#@8NdwG?@Wex)Nh5~zu%NzuZ3j)UJ;TgW_WMiWZdx|V5Ls;F} z`j=i(si0R8K~9(Q9IDZyXCBKF^%q_URxzrLuwpD0{<44m^S$oUboQ`z-;bC!q9Ll%9anE#O0yRx`Cad5q8k%fw1J5q|x- zubRx}EY`5v5)hzw^=hED9H8^Q{Wi$49g)9HF==T~Q}{kFFB{4GPu0WDx!J4JXJ%pv zU^nLGZvT464!|KWE-u?52wbq$)6-K@+zWjIEJE$-g_lf)t7|CCu7<66U27{$VU4YI zRXshx9lKGZiW(7xfIC*0;wm^e3-z9ddQ*(JS*Z6+)H@npk~t`u1b7;YNlevC_|~sZ z$j^r)rq*E(*TKl7t_!*j4L|)vB1&(=@qH-SK^H ztsD|wa5`BJl5gEybh3*$)O>q;F+||CrC-U=?cwvHEj`dgX=&*f1S?Q6gkx`S!C2`vZI58-O$PIlWeGZY;1fuFs#4ZIxjgH zV6!>wrk-$PP^1RZ`yLoCWa(5UbwJ8A@Hc#G%U=M7BzIf^7?RwPtV71NI*Tgnit>)N z^nx+i&;$a}hmqP*dlYWls!kMEIh#>Pb6s66aJ`MyJJQ_l@OjIYC1M2jYiQtr1>V;q zfJ4-rn_J!uC>!)(W07hjoWqLaitGCIrZ@h!y|@o-+qztxF1HI8ZO1mf_o3O)U6%L# zmyklQ`{<*$-+%x8J&3-L2G%;@a{^+9pQ`(zHT1)tofW4@BIPcIhdFcuBxNuyZ-zLb zOA^QR^;aD{niQk)o&?MCI7!Fqz=>?=Y!8Vzd?2^FtuF*Zmq4Bm!rF0fY}7a1B8X#y8Z7ZVZ~7(#(`1EMZSPKb<+hAVj+$T~vnfU2v^te@-$ z)e=0@Am`5J4xI6v(+j|-;KalbO_q@}$C%U_jiK$7M^fmG-x6&hdYwEkEkds+wiM-; z*A*N%R8M9EmBkI)w$;{lbrlpGK3rK3R#!tScm%<2cl5$#%f_OoLn20}Lbwx%&82~MT$~JIVha<4VaD0p)N~RZ0Lg3_nB4`i{B`L0@;pGW zms9y*KLR5z86z$ku=jhQ46Lj)tJSumB8$ZcLaI`+ZU;AYoCAa=AvJX>n>=B4pxK;O zQKA3%54x1fIg)vxngAyKF={^7fC4608)`RtvyKL6^gb??Fq?z11T7fpnDUtf230R*?l znvKcH$-%1Dyu8%Z!-oqYwJED_L|`p3X-s%e?a4VaCZxs!$5W<{88^jMd*Ou<7&jOH z_Q6}=lWgfMHqVjB^VXD!6VoP6oR}CHh&52jb_|BPx}2OESiMEX#{_A3F-dRG!DENj zqN7L`A!ac_nv_>+qV26$4T(Ht<1#)|3oBlpN40^3_E>w-xPG>h% z%TO-}k$6{6cT-6o1TsJFtMPi9J*A~#AtA=xci#IJi-BVWV9CHp>J&U?^nfYR%axkb&=C!g#u?F1Hhork~pL>wEg z?Sh}1=5}UDm@;EZWuq%%#Nx#ZCtY>R%@@NPQDT@D>j6F}V$AIHMWa|UW+Ht^Oq7|H z2bO*a?f})j<>e)|v|y7O=v*xB_pG_%i&`K_g*Y&23OXnPbU7e7q@PR%4Q5d&X#;R(gRWOhXzepmx7W-s zF6-|+RcG;@lqp?3NmpD1C*L!YA~BmpCQX_#W5y&l-vN`q&OU2II3$;|W=(;} zdGeS5?}Q1daaO>=8a;Y~n9v20e0O(1Ohr-7=ACfH2*7sq^cR0wE`gv=9=#(3i+${JYHAVn#DG91*y6Q!AuzB!&O!kB5I{ZzkPpE)Lr^8AN4yjA z(N&K)adtjH5Wwxr)|ozpk00HkBhwROP+r0%=^8hy&-wYs!~vEu^`i`A^Drp&`%^HECMF<=vQNJL(yKS zL2vo`>u+G~x_wV!1E!iZb;Dt-R`MG-EdHg2eCR~xVP`fCSl`(Ump=_P4R8%t2&#a! zGu=V8p}DhP`r?bcnoex|J8Sa3*tM&sO9RBxbk*z5u}zQd8&gW#2L_M#Pd!#0&4;!X&XzS3G_xIg`+}M z;74A5j^esqG!F3we&}bNj&SUP1rDCkp(?B+mF<2g?MON}wRFJlS5Skq0QDz4#n~hX z$V;9>-GVryy`cqo+ujWyI%rc<6TAkYZ~H1MTc{^HVc7+AhvPD^51QM7fsiGiLN(f1 zH31kZ$y${J?VUbY#u9Z=OCDJ^0q2n}Nv{W0L3JfB(+30~HUt_4vLB$}Iq9?90z4;u z|676QvObG!D_c`h-`LgL0dEf)n8V<&qov6lrlN5tbwCdbBHvbE_Q?*+C}TYyX!3ce zW?i6$hG&=hgLq)*98k)(Q+$ z)#Q~Q`5d##H^qp=Vq~qQ1&Fe~{vf6m$Cy6!$$s#OzVCvgGpw8gRPLt2-27v^w;kz; zieS5U=QT_5z&&Gcc$r0SNv22M!OYsqDT*G$`FjoFrzloUjh49~lJ`g%M@M`&YX z8*EMLp_mHaPdyzS*bcz}T_+}X)E8i4sqnn1Rn@hc;Tr(GtnMg<`fq864l4s4rWq~v zr{yhd;w_HQC)Kt-Y#^eDQ+~T ztI=^_MuE38-=U3*8=VBIoa6|Fn^*Ats!z6C;ZvnkB8|2HzNcw62Hd8FZJ1?>M^25jt^VEEa|VYPAZpCqjIKPU+OpIZUi*uEpF^ zS$b;U=2u?-$#bd|xi&VmVQb&kPz$xQ8k%X~N2!$)G`$WqIs`zAkBU@zySgw7W8HyS zxXU-o2gMK7g9wV}eEI}x6(svuaA3aTB~>?ckeDj$M@w#n>buZA5bU2E(~%smN@`DS zW>BH?b|P3?h$WEulOYNtcDo#fRZ&)te$4{n0+VI|{2VBn1!}E{_qZ5;FtC&l-^uuq z?Gb#-D8{?`I^Y(D@m{wJn{Qt(2GESbdK<<8{RfxIrSgK_WT{4O9l6!1+8~upWUA`P z)sw?jvpuop3;^(Ub*PLcg8^y~F2Ut-z|LQ(T?+CYtiO|tITxY63J>kweGt&jJBXN^ zWeD155zJ%-*s=FM-^bkgsp>(r`4?2MI`~VqT-`;0al1t15|2*QLhiR0b4+J1!W#TvsJQI zQhzIXkcOFHM*|SGsh3*isdIJ+fzZBVI)l*G0v(}f8`*<}NAKABDO&dV4r-OPv(c?L z!aZPKy-Ke$S;fGb%0ggVWld5Fw6sQ!OrlnCdIJLJK=~43Ij|k(#6Y($fGD!t!x8^h z>!DT&;c)?;j&4C%3EJ1p`*`iYkYWUKZS4aUq zEJ%n4S2P}bTN!J?dJ%+Azx?o>bm=OUyA5hTDpgX!rXm&h)$XM_t8*ue$`1sJpl>|m~J#9FTr z4rvMtOW^4gPRz8NY&M102GxrqI#_|@0l;x+=P>n#&0l{8NiCa;e^DJdZFfFIX+l?f zdzYx{?0EEQxhlf`$jJSoTGI-v?!6A7NT(|j)rQ(_F6aXbsa|iYuY}2H`81R;6(x{; z@f4I0zH`TJXvISF{K)=2d$8HryZ-h_`~h4A{+t;Z@|-CVGF!|S!E8hbunx*~Xuv#_)QYilHHII;!)*1ySb-6w|C*2a#d zzokp(h4+K*dc#v;XpkwlA+(ff68Vt1S6nd)XdOFw$=!F~eM9=#b}_uY2MY`Q!4wJJ z*AS|qkx;K0tnyGegEPS}3<_&l8+cWS*lscgm<*!HB{YLESXOrQ=&_ovEG?pY!qBe* zBBMtfq4Oq>jSh#$(ZDe5z9b1Ng^nI==nNl>$VXukU^E&qA|Hig03++sqj~vcbA9wE z8h!Wx1nmLjV;!BoO7Yvf~ot>(WQhlMMTA-u~rAKAwO_fWp zK*B*~L8L)t$=W|1=S@{fsrD+V5Oac3Y3UK_AdhGDuVs-RcaDXEf`RI;i-?|D;&P^wBL)qEvYGd+UF)Op%fM6VoF zQo(OKK~RPU3OrA$8cLO`q?)Fr>ZC{2=S}tB*)EByQ=!x_T&ZC@JvtXYsTtvutXfj4 zq>4~dVTcUX#5;RTdg&GCkh0`dP>VPZ48NmPUn;5kl(G`(5tOd}g;f9G96P!8hu~r7 z*u8seJfScn4E5CPJ$sH71ieZ2P;Al*!_2WEAvP0v2-TQuAwOe|6_V@fk_YBka-u$X zj@_cuZ5fzjyGD%Y8kl3}1O_5Nsthl!j~dZRRg3upD^>L9U(w~jY0QtHeYARD2XPBh zsK+fPC1zFjG{dL<7&>E0>WRM8JCxKON}1z{bO2%n2?8Wr43umT4Z5Qmlir!+u2vh z1=mxM6H=!A22+>=nXR%5$5$|VLpX3N6mnK8@Srs}<_UmY*PwV>ph28Y1N z=ObS&uYv*>)p7wi>w{`ZJA)xP@&V7HmcPflyzwnu-Js-_p3dG!ZrhdI_AO#pQ*JwF zv8n#t&Rf5mef67cPxsPjU1eve-x}ojzOQ~upuYeY*5Z zwDK0paa+1H8#&g?Ir3o-vUK(r1&S8vc_H_8c#H3U6wg=rbM^PlkiO_Yb^ z7=_4deY$ip@=`E`kG%nTy&~tuk0Q)oL+@zQncdDV!=)M;;Dh_(bUXbcB%R(@Mu@*J z<+J)M1`^lorE zdlh$^Dc8eyVl*Si90`Jk_kQz;j@Z+~%SZbLA|-{u8_aY3ihXC)&X3P>pAbIZg4*FNxsMm&8j~hbnXnbe4@4CCG2ruyXz>&D zR)0Es&JLZ#p*=G!B+arhI*ndE*ttLT5j(wFO z&Gq&JgAnj->}org_8S1xo=yvs%GGw&@fE*@)-Y7*4%~V*B7ClqvHUaSy9xQehkRc} zzUz>0+-IMEgy0cxeE8XCpKgLbjhxLJKKledYrg;ZvoBzQ^!it~NmHe12%wnkJI3LD zsx(=eEnVn4=1NoWWRi4|@0ftQ=~9yKm?;NI{+;wY)V0Q;g{!81Ls5e(%j9Y(GQdKt zy1L0wXPqWsYcbB$S)5fqEcmL`GGHA z%EnGfT=nvoTd@sq+5F|lw@WBRK0ZPD%MdE}Zr^bSLO@QJVkG_P!zjh#$vlLCR1OLc zIo=D#F&)xG?&~iyq;x40b#8LZ8$UidHj&rCQARJ$iLuG!DWWYuVd>JP6L`e8g%> z4rLr{xS&4;hmX)Er1jC?9cPbW)ckAX%p{p5wYOh6V5g68l9EBP0q>O~STf>?4w$bT z8r%gylSw`#>-<3jb=$cI>b##dP|q`vRgr&pBtQ1=8_C{1-g0jTF?!{r-CKqy>%1?X zK3?{If+q(hPC2~$akqVe_X*$e?I2R{@V`z9;{F?H#a(C83g%=#A-4AWzGEZ8b~{-o zXW`;}hn02U$q}~2cO1c88)s4u)}@kZO*)Aa&Zai={t;?395pijhscq}M~-erqdq~? zKlU9Tu=RNI13O_DaTRVy?Z)P^V=#PL4#E0Tm)_2&mw5&A$Y2vN%0;7 z{rZt-^fq4BO*H;^xJNh<3D^9le7G@6!RaTr@sb0 zeHirgZYFThGve<0A3;xV^HzGRyv5$5zM}~Dm0qW}9^^GQU+UFk4$A!2Y$Iaue_Z{6W`X`*n{?!SGxmYu+ zV@3Udo3aq8T9DriL|I4nCI?>+D=a(f zO=oW+|1TYU7?mgNN{3w`9|@kg%&DLfi0e&UpVK9<)#={DDB*Tr2}0jS1VfPX=V#o7 z-POV{gzwmyK60R9iLh6tbO8q4) zOlJpCLaV=o+=bBd9#nz`C7fl=4gMm@c|O7^T`|ueNB3iVav$>Q83U-s0IGif&$>@O z|LW6KYrs(=se^XQmhIc=BMR>uQl3kfe#IRtN|zP`QSNd;<4BU|BMQG|&A&tsawC${ z*=|&`kQ_XRhto%dibi2YLw(L(W_4(rn>pw}L;GCcR2vjAdgQe$*aA{&B2QO+em_?K zOk31w3;7K2+p4O8gNoCNbSZ|~&zY&|d+8K5NbYsE)%XYWiY&okK9`acZ$@w^&}Rld({nYX30uQiqy%Plj)XJkO9;9b*x_xL*h zhnZTN+uEBNnp>KiT2B?5JRA=VJmrJRMj#NTM;|d70=Ch*UTCKE>LQ@-6%oimxq%Bz zfO2dCVrY-pxN*mU6VSOhabU;Bb6qQAksy5*rjhRy#U%p`VqE^&cT!{<{xm~vyEoB0 zr|2ZhypPZoOu}VI6Aql!pXt*5cn5S#KGU`03SyZfduTaBu^^OQq{4`R;j+V(Dh-xZc*N=v23q$Q?>)!Sm|v+ctVw=wRw%QD!9Y`;3NW&WLM zshQXk!Uwx)uWe#2efY#g9;$0{F@n0Up{q?_>0rMj41Ur+P`tajyG_^MpsB13Fc`q; zmV2M@<*eR^ER}lUPju$)BN*+X(_ks~*`s+Iy|RZ+O{cH!_2C#vW&F}ClDN^lB{<8E z7l}}fhZT@8ZqDZY{_9shzAj#5Q4SU*3eF06%Y|AE=0d?57!m5bR_gB0)$f5x&h?8g z)~y!>BBr7cobbYDqCm7$gjq47m$Ki90#Qp*@D!i$iUQF{QK(5?(1<6XjLNthHs8-a z|M+8nzN*+~-|*)B4_F9XY?LT(0%{fd1pYJJ4NSJWED zPY)Qm`0`Pr)<^oAMeTKCCa#F8ZH^JOKEm(u&670hDaI;|Uy88`EkR%_&u!YmZT>n# zdPK_OCLG}oy~Q0KjgT@tH)95u5XYsC;u@M{yop5mDe)?N;G++1857T$pr*$`lN2sH zL8y%-r6nb06=krkC^>H2zi;16UMBT*y935dsx5UQ zrc^}IL}>3{5FM?7W@LFTJk94q zC3rd#-cWqdCd+lQBMAqzPg|}-qplpTlA-N0Q=40d2*zj6L#dI&*5 zyZ*?2n0I1^597&Y*eR7p)I%6kQRQ^PU4ooYC%rv+a1uLF2mjl4wx52x#sN6` zpA*K~Ar3|TG4~CfDTmglm(mWLbaYE6k~%}3 zqP(aO5uwTZpBxnW1UXnHTgZZf&7`VlBGCs7!Z{3HUUC?`xE2JM>Ou)Tb@JrgTduqA z=+O(_TDJ~z$IV;5-%Ec#?%KZbGgy6n@%dNZRACu2x$4^&=SvHega0clc4TFE4|orV z8Qg4cwtd8${QNl}8v!wmJ%S3}?D`sGECTn&>$zsvl>6@cuPH9`vDZbGx_x8hbfroHd}U*2}(eAxF-o4?|B^VwSYSi?Wel2IDl z!FGrlm=c%TN7QLFb%+!d7FJf$1O?Tm(z37!gKP2yBf`p#VQd^L3ro5{jCIyl)FIde z0>Q&;DlClfS4S?N5f~7E`=BG_FoEPiCmIl8h|TLT07fud@q!k!SZiotSYT*)L^xv5 z!RQypc@be`t$^?#_#6FqlFOK-^-&6vL6Hnu)*Ip}+%-h+9#|IOs~6MTDm%9xOaD1^ zKa-{4i|~@Pj{Mqar12z6pt&7*@i+9M2dg-IVQ)!aqz!^>Mqp}t85fj(`1$-R%*g4| z-E?Mj&7jnWO{H&k_SblbrW4-^e@rMN0D*c?Mgz(?PWNA7XN0eA=+!AD4qiXfs?I-? z%ye=V#z`gIQpW8K<#sbJ?=7wk_)^W)DYzenw$dCjEndK!$m7FVkpzDljh4RPCR$~I zdu;@)Dua-+yuw*t_AS;6*-Xk}ToxPo!w-87Ue1Bc;CAKv#~kP|({Qm()Lx+^YTG-%Ms*yCw|JNI!IX-8zOE8T5T`}2((xO zZHWUvDeasQ&)Bt0#ja-pn~iqP2Bz5pF{#pM=HO0~zOWYwgx}K=#F>Ej)u~eTA|gXmWQWVv`%3#7`G{bG(HRCEhAMEZrzwF3pj~Nb!y(Qarxr;QmHDze}1S z3En*K_iVkG#jeVLO#V^Zruxp(0vK-Z#uPhwWP%}R@`?EUfIfaQXlVj`@ThFb0WDx|se`YU3p0gcqR$}q;Flgo4@Nw`?-Y1Mn zoi=~|e8gVbo0sR}1^)vpRSjyG74G)9J5FJC zr{Ef7C1ti5tbs;Q6fwqP4G7Asv`v{fJ$(!e7VtMFefq@7G5tk(L720$!d>6&JIUO^ zVH6k(W=)8qsp70Da~G<7^!48i`wfe|-+NE|Y}jx38L;2rgZ)}o`A@-q_1}SgWmV;{ zu&+EX>=E}0zz)u<3K|af!7}XK-|PqMgF<1(Nd4B?)I1dS#Px^--X{R>DV9k%@ZM15 zEGmHW`TPPDuY->ukY3D7qTph#kJ6=Bkf~Vqkx0K>x@*_0SsMYJEPE!Z_1D!mF{F$ zt-26&Hdb1M_0iQ(cY08|hgli$r6oX2Af-JV zt1V-o)nGHr6 zsdTN6thur|&GqRp3c-iFUX!iWY@9V~*RD%lU}9xJ^H1lH9NY2bhb~V`)n#^idX<$1Wj|hSaHP$GcL86f#{7cz> zqV0SUqZ-?_15}d8auJOKkmbTMDwN1F+~l36k_L?qZGeHN4#B4bWGXrIjnh=}oGl^( zTwisr7QH98XmmOjllZXFXSImgGtrtQ1o|wt&)=ebf2Tz>(G-@RuSI5Rk@;LLA`gSh z(MP{PA590|U_-`Y;N~8G2oYCWc=X`Gk3L0QmtEiC`T*D|P{@wL8W~r+wtjW=;GUeE zs`}l#b2fkV!8;#s!Ve-`-gS2`_I~KyCnZY1m7bRFm2QH5=@e=Au`vtSKAE1r%%0@#`u%)xsk*qy+$GtL`FQ+2 zgQlM9uAA;#iSR~mvlFbE3*D5?vbDcoHhJ2ecl_#)6!Zv(9UCN)0cA>mphNTc%P;NS zzaA+42kxT!Oeqp7u`AS1t$p_Omse*>D?x{6i(&*PHI-C#!$BCIk^tIf4Cv`+@aUz1 z^UW;8!*O}l282=J`SK#z8taU4NzoRb%XUQV+4Jt!j>zEDNil-ZSPM^O!PdkPkrp`J zLF-^F8cy5Ivan(9b_8tue#htQwt~ZR@Sv{`c5eFkoe#d+oU?m(eN|4*o`Xlf+PVwfzh2x!~eVOj5mEglhl({ivN|K6eQ>nRT(RxZmHk z0IT+w<(X}tY0R;Q{_FM?H~iwRU!!wY1w_60;)`#t-}H5L-M+V9xNVPg)@G;$1L%%0j<-Z7#Tj+jXcv_uZbxs20bU4P zu2nHP2x|7l2d}^J`T9+}b`}-v*zxW&FRuUKCrwO$e-7FeF?a5b%TifwQ{j;+k2fq@ zQ1!I5mzB}R-#1VBV7}Z3bBgdBBLnx~)^(rn_TB1oedd+7-hcbG7oHk(YyrSH5Bkn`OTXp*wr0L`iDZP(>8ormDoO^a=7rZbe!JoQ zj|pG^xkY`)OK_q50k2_sp1j)g!P+;I@QkLegE#-c>*J z@UL#W`w!&ee=XZhJ~Ufenp*{%vA3%gF^_+OADU`sPkRg49gv6v%Yp5Df4A8nNZy1L zFn;>FDvGPyyi#^dPELP+jOM2g*1!17yE}Fi6z$x#Y5nJKy#B!#=mm5eUw` zAUwa7621GpA5z>h8P&aCL0i-)UBa#T+XDP}Hy}Sxnr1pS=BXE6d;9&jUU>$cvkH3! znSyBl_^UhaoHlvc??GL-O`(}kAvnR_Rz0}#zMJlv4+i2IqDGK0fGq9Cv6eld{=?n3 z{p#U|GTFNTeJvNh1zn+e;-3}1+~je-y}DK$8J6V&~5H zHWaXkkWmw&dA_0A&5=VqkT7`>nXFS79s$#DL`o9G9?+3;w#QV7jvi}0Cj(to8 zPPqqDA%1|VpsbGv7R-JggUQEZ@U4~+AyHXA53n3ITshpf_pGI}yoAeQ%dX~zU2LC& zK{2pNwwz{A3|(nw`4)wN1B>yY{EH1DR3FAKL@y5JUMy6&7pGU^3kR*lALfs;+(n>t zLznLhmE}7(JiBA9ctIW;n?H!vaeC#RKbGaC^$%Lr53oSe#3{618nO+S7xePN@{C32 zY1W9mJr5bo8W~vTXN3(}i4Jf~cyY=o>&UdhyLNf~k98`_`vHh(6zZVfttrBF{C`p26GI+=^IDqth>J_R)p^#mu$;i^#3u; z9ETZeCTQkj(9Ch5nd3k+LzEqipVw1`08H|#A6_46A9DuBN8Zi6r&*52U{Lv3Kiu(Y z^VtUJIG8{i*fcRn+1s4K20FbT;OvZeSc6zX+_Zi3rR(86WFyW)b_m1qh(eUzkDpUi zT3A&t?-+uVJx(>MX zE8r6OyO&p-AncGmG@h*PYN0smaA#X`^gu~LJ_RqM^&+h3RHsTxp460-)RXV~pT!Rr zVD7eLF*l8iV@0r&va{FNhfw)A2xYG&tPtN@asL{gKg+(u<|>(+#+HcoOW0a209J-i zVQKRyWYbS#=k+CCI6#LQOs30jxM3mKl2725HTV{}>!EKG;Na~#`$8#2x)jT)`=mcg zzviBPHdDGm3Ii)46Rd;fBiq|D{S-pi90)*F43tnTR)@LU(MmQ%79H>{2g!wFtj|iVdI0p5cCT zeT=m}rUS*s zv7;wU9|8B(aJf8U`h?MGAszT#+4;`Ln)D8giCfVZi_jN~h`t8XB2*Sy4_c3^gPkQ6 z#l;Q7#M_BlM-C?+H)j~< z#n36tFYt|>K@{u^>Q+i)>SQB^6!8!p8o*W;o|N>spSby{-Q(({BrAT*MxY^?nS1Wl zkzX`CH5a9Ax?V(-FKSR zFYi9)`oyERO0nJ(5RPuh){SM?jT)IYVf;vkx)a;z`c6mU$ng`>Mvl6UjlG_mm^LD1 z!t!fx2cA4|`?bp_q>M2?6Za*vhPB<7w{z$#YZNsmAtZqOya?EV^ef39c$D z05#6TBe{F@5QFv9(Xi*T2S{+%X}X-P!ze8)Z3S`2o-zobqTzZqIXXCW+?W}c-*tbm zb>!Tc6>UKyN6du3Ui=LpA>o}NJbKsuCUa84s0mSGNDTsRmvn?ool{-U8mi}v^%R#E z9;p%aQ4>ZbBnx@FqQiW6cr)p zkv@LRnDNmZ{FiajW=Qki?u^(lV1~#jO-J0;J@Jb6dQ+c;0jQr1@xj$9p>*tX1 z28Bq6KmYvm!%|2`N64s|^XAQ)IV$9wGIu*9ii+5Mb>LvJ`~0M?vL9d01b*SjvUys{ ze)<;TrGbrTu1GK*v=Iep0dazGc|~m_T(33OLcd|q7IYV2JY)+BMjo6%x?EXsd`w313)%qk zZu3luKH-i7xbvtFcUBPYltPQ{*q*IhX@HcLRn=h}*HuArq^!Jz;-i*9vV*^^TYtiP zrKP|fJG`wcY>qZJVTww{OHx)a(1${dAili^P97D8h-ww*xoinko>H*~*o_6*DsZmX z!zlA8D-AkS;fCW;cg3OH+(SFweDcXB-`sI%F)PJy-{PIO@$RdvoJ-~Y(-Kb5t3 zvV2Gr`0|fsT}fD~oMvl4Ah|kwd=;?bq+6q|sKDB)vh)P}_MIpNRjRD0hLTlvWhEY0 zRH!wb6;8YXO((l;D1!pAzBw){7(MGopE3gYfXvboWVw9tC{1g7Q&n$JkjX=mveXZdljG7id6HobWIJMfoEhK z13Hkzb%R?uDDgm76PFGwc@(+V|Gxb{0Vez~eThySwkQ9QcmESOq6Pj9*6LHVhv6D& zjEsA%313inH{uJf-vgD0UCJ8!oN0rU*GK^wX}grPEy_dVzmV3qwm)aNsY($;>uggV zb}QxD&|VuxTNwJthLL9jjn+C(l;9ukG1cV3U!@hb!Qa)}w$;P;qY#lHbrWWpcUMGqShLQ5JJJwvGzZa*SCIu{iI=k z+F$<(XUQ>?Oo7)m2&_aIWb13@TDRKf)_ns@GR<`?f9!Q*^RxAFPZj>M;EAYv@esxG z{H5@X^=p3HrM#%Gb5=K@4vp-9L(|7p)PQV#m^C;G)rf?h*Xf6HJ^UV)2m6asAruGv zbgfo(ZYhAT%GE;X0}H?yT8$@8Y?*iC+Zt?RNq*l(!{<>}qU2GqMag5q{m3Jm@~Es< z@~{Q}ojgjNGy$$web@dSe6Ow+ZVSH+c|49MRZb$_t5rc(BD1TNJ;U(z@a0iozgf;B zCFR$?JfO%V=K*!4fjr16k1QWZ|DEjqtTH~7lpQOpLvyltO_xUG0hdfEK`LNR!jTx3 zSu4|}Ft$?U0jG54;TJqELo17Ar9(?G3rJC2*;IJ~jn3j-Mi)>b*cN5eqw&hUQhO?y zV#X0B4wPJ0a;%Oz18=&FD7j!uhLivoveYqI+}gG2tO4aNc!0`%T1Gk`U}<8bfH*VaFm|URjw>>sGGYkWJv9IyGHV z*#W?;3~2&8l&wNnR9B~SNp|XwtPFNYnvktUuauRY1W>a$LwB|oof8lcO=~B*9cU%N zH)2RDoux$$MN}TNbqykqzRHvyWFJZo%0L+XcE;@*z6N0RxvREaaIf}=7#Lg?Tm}5 z5wpxjlMTMO;h!qZW-=OA!S1oQ7a>AU*=4o(ER9+zHH-IhlKcqUV)VHRHB_MwRAk}8 zLO5rRL;hIf5tIWMNpbyx(E=-40L^=RxBxQ?CLV_GUoc}MUsGFC?JOxSDlW;fn_P$y zsZwjys9--FmkkY634}w1bUfAn7nf3%HK^EJO?49ef*#(ze-nwetBGe z^}=m0Wo2cFFKxT34eswML9pwBqEm%^GBe2YLYB9E1otaL~ocvx)Q7#@BuSSR+Fth?A-Rnry$-e^A6JFLk3yD3H4U2Y|K~n9YQ6zOrOL?kh zHk^`z_%9a)Dkf996$ErAZ$%;5vJ(ULxhUsShY;iN)Hw!AtYmuCj* zFaveSL>)+9WCmzRf;={AoWuqZ~6E)N*+xs?L2FOp}v_3ZQ$c|Qu{C(`1Y@D8^ z6Rv*Ap3he5TpaZ2TxV#7WF!3V;8Owt#%i=usLNr$0Wk55H1e%7#+CqnD}W*Q z0YjDmeoGjaG=c@nvl48<=$roJuOZhXU{qLH0j^FN7&|ftjm!*(Ugy~L*ojkO2{EYj zoFs8@#D*`2Vc?pvzl26FhIP#1XjZ-Xwbx$TTs?mL(WB+b38kVjA-TL%z8ir%Az?&$ zsrZcLl~*@avnV-96sz9!s%XUXgo#IwPE1ZHE*Fi-<;C(H(v$;Ds5}kU24s3s`K|$t@HnV7K>oA+?4k2?bqJWbHg^e4ZXl5Oy2oX@)!OV%{ zMI)Yf;H~BkypB7w*>{K6M}VbB@Q@)RLxXH5aWUu`Kmqdy0FeL~hhBz<3=M~2;F^tq zfn@;kv}_1th<^*XT68=3I{s(nr1UdIl9+>jz8L*Xkt7gFJ2HX=o~w5wV1Y!H9;Q#T z&e@O8XwTbm&;JA6q(eZi^K{Ge=j((B7;#bUhXH-oxxgJ^9SqpP*YW2}BhS~%0N1NP zf5`4?8Q{7M^k+1nM*ro+Oa=?9O1`hIu4!y(!Xm<1?JPQ8SahO@q_?7qh6JA#%1Iss z&*2zERIWakfY?R|B<_w&1SpT50@wR(Vw9lV0z)peZsuu z?4xg8Gp@q+#KAqVe3+csm%8P1U7Ozcv*XVnq)W?mA3SS+>b)IO`f?HNCrdhz1GFCu zQkWFrzNDnMAomznbj}iIZ9{!+T`fsXMMN9|b;t^pV9$*fGK5^!Xf>Wba8BKQ6z!bj zRpMwMKm8ML*BW3lVKIw)lgX&z)#jjJE5a>CgolQOl6?2)<*1B`(dfM-^j;ErZ!~&u zG&&$;xZWGoQyL#^v(N67Y(M<}p?6T=+29%k8jTM$*&y*}Lt#*hProQ*rk}(3_xMt0 z?_Z2(t^8>RmI~?-59;CD;c7Juw)i9s!5+ERiS@s_U*e(P!k)+k2YV$qmA{Z}&h-8$ zt%c(B<19Pf+ryu@^UiecBL2=hPvB03I^^d^>=i679YlK4GVyWM!XEg^urc7fs1sJT@LHO>KJ`9Oh_WiUk4T(qm#CQ(clT#0hm`6r4C zKhvX1z<0Mw$BfuZ(Hk_y^7h648p8DFc4z=RN%#JG;#*ku(XwT5sBIsTjKPbLuK{d7 zTgTlrCj<9`n_H3^wgxF=&+FRiG!XhP^KO1 zQnczqwCeN@R#_PU&{S=~Tq-v$??eF_m;V_=`)C}m5nyPKK*g4T({Ib08G$>@mipU? z-dIQhag??~Zu+sjy0(+7^9S|=H<#6LG5q~DJQk-Jj6z9f6!UzrR8?k#4nlEKhi*9btIJPO4S00IH*`Wa{sGY84whzW}XK%ys} z@v8!rhXIwtfJ!Z(@-XPf!;G6fi=zng@QJ5FJJIK06Da+vnAnrjf{IpFApE=PkuLMb&0}%qYnhl>UA%!t4i#`NQ5~FF#>-_dJc-36~r&)(=N4 z`-3>ekDdDCkGZEb-ulV)>epWTB5!hB-WLbM(=NOH_RG?Qih~v5X}8~=7EV9Z=Y8=S z|Kn7)U_OSE+!_MCQg3|IpYJig>5^KjbcP@|k{Yg+8;@pNPF5V+x${uP$(<(;?L5?L z2+o9Z(gV^TOfPTB+4OSG%kRRa`X&fNAEC$4GzvEK{iBru(_4 zm2dv(oUhZR2e{zuc|2!|O}+4nWg9kZE$Cuh1z!2Qb;Gh{S6rAHYv#?dseEdz3ABfvp?1R=`oUxAVY;Wf#TqQ-q6_ZD6x7zY=;thz-=hEGwf9zDi4F^r?g`ITR^? zKhOxNEQN#;ioi4*BV03(1yU@;>V&ivxWTac0P3L8SqQcW?15<8p#cJUfgV|A33!Aj z5UQzEZZ-?EFl`P4gM%>@V=lG^;J}~RY%&E02f<1mqP1Y`^dVxinN0!s*-UeGfC-GJ zf4*F0%+aos_?|=f{v~LWlen#F%s{+GCu%n^ulrIjw0Bg?@C4NIJyCljOS|5i2E_`9 zw&5)ypOmnch}vp^$w}N+H6|Ia3vOMhaSXx^z)J zYuc1)v*w6!81D6+t^#|&!LX3p#{cr3UuLjv9JUQ!?~OMOs?GzDpgu6TQbtI~sa8tG zM0HhNLtRycXr!&=>Dr0vGIVTNiEl4FEMzc;DnbE&Q?!@5y=;RFnN2Wg+mtTdE^58k zk7GBEBgn9|h&7W&N=ch!_BUpCf{Y)mV1O$U1pgBmGIs$o_X0Ba0y6i24&H-x6j;0T z*VrzO?x`>T0e^XAHC>z+QBtcKkAL^wcRO}`|3g&`{qj?FG0@C?5{sTyV;dT(%PUIq z^Ye2LAJ~+Z(pS`Npo@Kh)PlHKn1csRAk}`MLQNbzF2z%mFtvAr(>T!b2w{ zBI4@w(7w9;IpZdzn0s6Epqnsu?D#PWoq^M)O}ijDsCdhkEk%^bp2I8_tFEW&3lk{O%xH%At8P_bXs|6nCp02hUkyaf z450x8DX6pa)TxG^3og*NmpE%`8@hE-7s$H&0G&>cu-N(lT`0K-(Ho>@L}BgYH68;3 zrY0sPCSz#fhtO~{3A@cQ@DN73yRWae7rfSSf@V zcK7ym_KQL|2npWxOpSPpsn%TPMv4cFFocY$1J1y5&7ts=3bRb(_~%NWd- zr&p2WdIs}|22{&g<;=0GNef!TbXs|?!7o}w!QR|B(cMU4Lxi=6M!;5a7xE5nvsZ-j z=t??Zv=1dZ4C|FxG$NZ)%1G9;#-h<0WHnpk{r57yQca@Htcg4k@PbB8@66$m-od%3 zCYUB@8nJ8K6B{?f%ZnQNfGbfRb8z0x4vxQYja{ig#z1AVHFATH4;tj)G^mIfHL=F~ z?-goBJkIH;$r8ZC$M1#P4w{)z9oXa23K! z)gARLV&NStMR1(cp;|WSkb_BrUs9kG2k4{$IvIdY9Oz0M<1FwRdKxYO4<9fHC>6C8Ie>`4 z2NA8{WstMreOjYnm4f$0{S$S7${h>1NgO19YJdSiNM}!T8J%p{)(dET12!d)*PWy3 z)a|!$yOKn?F20tR7nSFr*#=*;`#cB`I;hPSgC+iqpOrR8pv@D}=Gka-1lkgYj9EX6-j>{ovy#ixT2GnhT?Fn@K4s`uObbU4yj*f^=1guG6u1@}i>hnhFrJY?I5S!Pt?onRam+FMQ7tZ%$-=P)zflX~*d0|N|0 zMnr<*1cGZoMOJrRv+Me}x@%fjT~miaT;wr5x|j(fW(5HSBuUO7VRGonnaO#k@B2Mf z-IJIe+-KjYP)j&N-E|4J)Jm{`aVr4M_F;^9~1lKE_cT^L(+x zDT*u3&MvMj$SEuig!3INcgvl6au>=}TP<&~A15;h1 zPB{f-g=HuNMHOfBLZ~XJ5mYkMpicU)+So?c_SMGaQvH{6dGtS83IF17Jse3h1Y3dDqg2i9zIP1pGJXCqrfNZ9aL15T~U!;=E^NX5T^(W zI%&X3!)wHu6gYi)dit(Ki*`*)M3TfwMa+OC#3iZ`)y%2-`fHmlAr(iMs91tLM)kGE zp~0D6V<3G=@&u$NZc)uRvruM!epzN_S+=VrAAz%cwhKv!2~;i4DOjTc0T!LkVlamU zgqX4IWbEK1qGi#(S2TvAL(B4@feKnplQ=b>%nM;wih1{HY>DVT(llc`tZC>F0~yw^f0Z-6k@iR1LJ#t5uCyK zJe)HYPyriY;Xln57)%8<*n;WxcCD8PIF(It0}FVN^Oj$V&`0 z7X8cZvb~oU-X8=8#0QcMib(mU1x^cB!%5fm(Si!pOYjdj6sYwx2(?=O9LBtYI1hvZ3i{`tIgSNay4rmG8*1@mEnG($>bmD1zV zle7{I%)lM-u}ZtrFDFNDm-J6yi(V(fR@^>;P>~5R1`x@qw4rq}mJL-&%2FZ0 z&)BqOMb*VsuBu};jam(2bTX|aC@MJ8967%CmtN_y0#lmdF_2;~54GpB&$}@4zMUku zi;rv5+MyLV-ch@J`HB_GVX)W@H?mDv|NQgSa}FGsc*aQMC z?A}%Le`4C|vT7IX+k+&>YHFd{C`*(XBlKf>f9b_79Joaf!i@$^6*%z>y2C}dbur9D zK+sj1Jo#m%2d7Pp@FBdgb>{1@znn}cfTC%31BCmh`SX8T1#Fz!96nS@xTrCW0a525OXN4w%l;AKt(LSBbE_CX-tYm%iFa$k6Y0de zI0a9yKBqo)g~U$JWYU?-aJHN#Vo~ig5sMyFzRq56fbvn*vMHOH-c<@8ps=F2>D*SR>Ui@r2oGPHezN{GY*aTr2gkNlJ?beZv4cpM| zTy~}(ek5JjpQaY_Di*^)LwfPE;q+G(#u`y&Xh<)9{eHF4o|#6_PY*sCp%?U4jD>43 ziA04l78)D0!#_g65KTaO^8@M%;4axEnSP_)0N?yTv?}Tur$Zj5As^})7g(i0GtUQB zl#)V!WNF$1kf$44-5_JK;E|i%AMC(av>Woo+-6e*wIlqE{?o%0I{WvJzwr3)XNMkK z|AzC8^#?Hk`Psqh*|$IT!eh73u0HtL>wHT;SZZ3BvPo_b;sF`y1bzo&pY zF`!Ni?YD>x2ze)jI#T5oX$DRel^97;*fB1v>%k}(kd%9vlgRFtX^Dk9to(`>UU=cf zE6T%;05Rtgr;RC=Vnbt-)9E-GQ9c8Id|^g;#8DLgD!@#Eb-5HN$gK+;4Tpa2=-%}B zHafUAo8k!BwZWsWa;6zLLAu4@irbD&7Ii3|I3_8F{Ueuj1?oxjy=Zg=@g-amOY8jz zN%=KtXp{y4a{TX`HEv^J&ZRhpfNp7Y<6k52MsvxAh6tu~8oG7U&7W>vxcP?AQ<4C> z718Yqx+UV~8)60}>7kpBZb5V-Sl2lt@vsm)iHg!mbhFZ}c~}TuRi3?z z5?@9)%dimfl!RBmgWjU)HhdYQDM<+3D(MzQw^H1E*L)@=89}!;y4BO|7;e5HcvX2p zyD0HUy5;nPAZZ&YC4g9pL@kt(LpM5?3dtKVcj(5OA>DjK zaDkdli9ezn=RVzhL+~<^DDl7Orli}iD1#XAtIOb0LX8w#FXP5_3N99tujwGWMjE2WF0+giGDWkl8Y z4Z+2hx7B=t;9na(5GvR)G&{B5{8Tcat#DUDVO%$`iFhx<2G5|;=YZ(uj>+1`H5ClsX{3#cXzMR{OfeJ_-^H`Kx=Rl`z)=Z`;geI4ms zO2xY1w+cI;##0}PQzlte_%Gs=w^^l-V5U;&$YCIJ-D~Kl4L0;)L@F<`^+STm@{Js1 z?+4F5*r^$8^9O4>k6k|4?ze;r%Nd6B0NY>r^-ayXiyQ!@vE{~%1H6E=!Crt;mU=F7 z1fsD+I>y5>b)QNRjtt5L*7P6lMyG-MN)D69KVjTSfRCVT)AmR&!wt~DNgx*i^T@R1 z9*}o1&?Xtk1>U^-b0LOAtJcGX3H`V*0i3(ZDuv=i6JR%(4G34@-(X&-6mGZTB4#k< ziHQ?Wyldf4Ju&>TN+6vXCFd+a_htP_L-hA8Yt)JVUfDlNmRPPM%x(gZTEU`^2v`e zpuHMferVW8a02ZF_o~7C7+4c-OI}6Qewl9kE8V=*y_i}$N@t)O*~G&0Pz?*g%}6pr zSP9*@i86f8%vr#DX6i9S8STTC!LxT$Vh+aZfsSC9dO+>l7$Gin7@J%b8J-?l$#M~w!9p)P%p3-rGbxx_Ns(Z*MtJs<`V%48t*zxN)^ziZ|c&=4KAB_fdl2_RsKEC`aQ0C9bC%FS2Ra zGC2RZ)${Lrn`lO!rtY<2hORki>!~!idM=}{L_gm(=YkhO>5A#bzYb5`|L1M~|Hs=r zK|JTGh)iYQQo@xc7xeUADajiaf@_kQl$iQ5(W2^xh2TQS<&SzA5s4eVMe*#pRy#-# zesrT2;JY4Nm&~Gchv~*WkKx<-R7%I2F84O63YYM+c^Ut)x7mS$eAk0lmFu;)2_a4c zbiczuaKb7n@yB%IUqgq5`1id{UTKaCr!EDQcrWd{9=tc_I+62y5_XNDt4Es~cWK4p_L(O)J$HYs({y@pfal|Pe zSFpbdApQ-r?}ml#!6`HVs$=WuF`2zY4<+`F;twL9@A@{e^^~jwXPe?Nhdo0Nf1JLK zM-Pj{!#8Xfdy|rtvz7Fi!=9ptTAT!A1etmvYeLa$jFGua7$=t7h>Hpj z50At-p-#(Ha~0{Byi`_OEBlSU_S$Q$QvT+zKWcYc7IcofD`noI#f$&&^wUq@Jk^X% z3d62`>`|xXr+uZ^m$0x9``P3i$Fhp!nOWG3vD0Y@iyskJ+cRSF|>ipuKR+UirMPMyxCJ*;yI^2$APMrdCd;z$3T ztfU_B;cqzI-KnU?VYx~2pPf=SIP$HX{lkf~NgM4LZG$*&uwy)hbH#h2wjU_#*`0VY~6(EKg?meGkG*BKJ9@AZirIGMyaJ_`C0i{-N!p~ z@oVnRk13uz1bv7$bcNSel|y7pFSN8&Rn=Bh)LZZ=lrp9nTzkooj2{8NN*cRG) zs;su9ySt^fjE!0I@dxj}|NeVxSAX%v7i-pkxn>PEi1_fsk5;Wda%ktyorjJb#jdSK z!L|6YW37=qG>MU5EGP&UESivRN*W<%y@Lbet{w!F4z#Liv2 zNrLw7-MMqmo}adC*|KfN0qoPcZx7IV)*jxDjRCqkyD^8W2Xw4zz*Yg>9oQ8^@-zbN2AuDM9(i3-iyOI<^1PPd`1?6gF?(JX6W; z&v!W^@49O;4ll$fKfi87>O&7blr+KsMU)-LlI@zZZCh4-^y8`&Y}x)>`M0Wn;uN2^ z7lP$?$}{-p7U&i!3ex#nzj_10y}mVI@o7~7t=r5}7RNq)p>YHjPn`l$;ST0pm+?v82`)=$aZW!1D3 zguAD+9SHjQ2Vgk`K7M|Z7Dwr*{E1k>K_;RTlDD;TvFL`D&MU8KY-~j1YG@a9qLIv0 z(23TQ#0xqFVsAY0=~ZH;oqP5{)DG;}vSrVnoqPB00;SA4okpq9U?+;u$Ve3qOa>-v z)5qSJI@_;wFJx?IRz+7A37ttgxdx~BZ5zBiA)7wCy;zM77T3f5D&#w9B zx;3vZbDCQk>*#c<)}Efm#+Lf}=4vw*oAtOmu~|fQSy?x<2T(NX=tKmip8*synnR4V z{S*#+)*Cpn+8e5CDnOyCax9X~?M7Sb?rd$t;s~MhZv(a72enp!TJu1yhpkeabuhI& z#Ovz;=WtftOZ>9QPxFPs&*8Ucurcln5W0)WJKZ{%yo0|(zTF)-t?EU^`uE>l zjE&{5lq1~Lbjcy|6n-k$VmeigUw^`;^P}DE5xyCe1fAxSMTR~W{VFadV^96>l+R$p zwQJbtY$wad%v)Tn5bytuZ44K(wY}Hp>;&tM@XhulsM%o|KkOf!RzL4cU~OBkt>f*zqzZI% zN%C#@k3LL(7#X22{}}%Z?9xB};S~KDmUiKddNgV3R1Z#MK^Z-{(hO-M`7X+{3T$d> zljd5SHf5s`C$-x#bpqCWAS|d1e9_@|*#!ICj_G(dWl$qLK|7l_Y@RLs^4#B-r>TFQ z6Z`T^8ki^bzU0gH_bIsvX550EB5YXM&5x6xz!8M?trgBgC%LGimHW&=BXT$;av1B) z!B6B+_bYNxh#aU=a3Vy7fCE(q52}a<2WV4nh3LRTY@CSy+zOG9frUxN_at<1fgmwf zh)AY7DVIr&e$qUFKf&s(VkHy7dMp?zv;YJE1xB+XZn5lP(e|a1IzwuqU#qYhUU)Fx zvT0Plf7yUX*^`Sypn8>_s3SjLbiWrV7K7aE89NnGUgnwiw@ZV`(4Ni4>hvE zBM4axlERB<86)5c9Y7E&cn}(R5DJ4NXfPK64q9BDN3{4PoM_?SL9{U3;-!U8%q>4~ z%&%rmRMu+o(2FRMj$HA9?dKxQ16>?`oZ#a3BA|4E@qm(x0!o*FH*DqbvIsLA7oK~o zh@QBK4wrzJ1pEvEzfr)u1$=8Cye}yNI4C#c0#R;b08!3;m?+m8eo4yd1m&7=fD$N| z$0pIET?AhI*o;eJ-X-AO0zOl~w+MLs(C{>ujrtAV{$v54DB$-JJT|`}BvgIS{p+o_ z_URk|quz;`RPSArP;a<^2- z9BjjWI*Yt68W-uWbI?vv_QFoUO1S>TbdviFfhrdFmh5f2r1AUOE+F=z?ONudl`dR> zSRf|#rP-u^-QmQHm$wIWOSxvOeFY(f{e>>{Wh2Sy{$@|Yu^El-D9P$S*q)+|_2%K95 z&IW;VmB6{A4`;F)2gmVo6gXrXtpE|vT~x>KSUf#s(R&)%7;lYzmL65frEjzdZSN5Q zY7K?9v8^)&w}+QLLnYOF#6hCiu4z8!ps7*c(w`0?D8wK1ip$AkVkC7r^ ze?a8WB?9)*eek~Iz<8MGVck#kIKt`Cyp`w?bocP|Fsjg1dFWxK!nsCM9w7t-J(>v# zL60Cpq>mm$O9mw-$%qv-Im4C^OGhy?JsPk&-$Mz#wJ#>tPomKeP!1NSG=f*=%o1Ym zC^J>2&Vb5Xf{u!>IWKD1aVqaCF-D^hG9Exwy##!VfNvA<>jk_l;EjFozW9LR!H1gT z#D}lW5bBCtLOo!bmk&N$#IG#5Km36HxcOqt9RhE^KD_%wYy0=+Jp#IXK)tmB9s>tn zy7{Vi2e0=iUhg+}y$!tHn%;W*g#4Z;HY$iH_C_XA%kb^B(K#1X}?)lgMv7NCIaN{dg5U21?pR zIe6?RM1Cj-KW`2}B8Q^B9K4jf2!C!vsaAk;%n#)g?Sd{4?TRj38h_z69%gnbeIiOf zPxzaJ&0j;# z1Q&BnH%<(BeouZr*&KX0JfDWcMcIDPvxCPT$$n@1ljO;igS3wa`%wceYOsuQ>0~G9 z5rS~grE>Yvo6AXWE(&ihZ6X(n>|ZJu97!W^>GbAO<7HYh$1s}QiU0=8Jdh6>o0URbY$dm{sSF~TJ&*K_Uu1Nb4O zUqjpo2?96J%g-fC!r9k8ywN2ZAJcduQ@Yhs2tynx1X~M1gcLWjdHm^5jLrUy} zAIe8*SWTtJ?V-{)bL%)doMfS!T+AWqrG|bdGvy+U>&Ex)GFfJjwvKHX71bTr+E2BJ1db^)51R|JBDm=o74sE@dEOO zcEcui`aK9k@b0$SS#J-DrTTT9!gB<)u;5Y^2WWtz359RkV zHbbJ!@Vt@8fae~{2G8R}h?g(c8D8#@@5>eQICk0&X1l016w<=GM8RY(H~s5kfSwcM z9aRL1x$=k_1razFT;bJCS&%n|&RQvj9feHWlvc_M3(Y)HrT&sY(KWqC>&jn|Bm_H1 z5;Fe|Nr1lH4LI!E>R=P`;-LlCVQ58zEz5vCut$^S`{cROLYB<_B`uLQBeJtc>OjON zR%*Uj>s}n&8-0{Ycl}kA?uqTm2@pjA?e)*GyCNS^610%xi54QlHhM1T$Nf>}N@6w} zr-v@huK1e&PEj_w8f@wFNY&5^gU4wcIEWfdxSU1=Y*HOGz;gO73#Xw2+tCfwMMR4M zwb@Lxyu1drVRZl&^*N4G5v%O9_SR?Uw$&zj&soAqD-rY3bYfmwd+)ro{(S7`3rlUh z_q?K!C|U9hZ_(M*qT3_AExNx%^~EVm8rZvb(K<@Tvvj=1qoM5G4+SP#$Akb@fma$n zyWJ1nQ*Y}Sx)z1$_&l-i=H5(6B86wobri3s|Fo)n5XZs(~V? z3V0Cuco6D%5ZVJd=e0e&`mo_$;@tRjqTc@LM7@9tqMqM8FZF!Uz2RdH$>VYRXLHCR z4msBgIdolX1imc--zb5vpTIY$7vG^_gG7%RNoIphQOQ$d2nU9aO|gyw>ch~uLX3J{ zEoLyf#0F`Q{i)c$trq8&M8ygUViUq`c#Q`!v#59xfQu&-1pPt?jb8d0tV4Tt z(iJqQYLk9|5wr+$BSk>zy-ji?i;1OvG_h1@VV`+S5_!~m^Kg6f(26`Tk<6P1QDg{z zOyXIvE+7G#ux8*uR5AGzRn!Z;R8a$(ud9IG_4cL?_9^wA+z68T89_1`LBq&S6T#UO zMgiF^%{Ky~-R`r@FL+fOpLpG@3->frX1bP4uu z*MQop1#ELKtgqU-d2MI#+BWmrN`tILFXUkBzkKR0uXd5h!*@)m(Jj@a^W*H_66?@`GUc*zY(>~$(Rq=`yy3iOsto06lWq4fKc;;YmOC2M^wZ`Q@=5 zSOvg|Ux5dRUu8E?$q9T`gYpmFl84slJ8D2_VSTBlpgR}VSMFJK>B9Ol`y-Sftgnzr zjGOmbUqk1IV{*OurT68R-t`heP6)W9uF$q<3T;ZJ*X1yLG=vfLG`yKBwp8v>qzo4Um}d%JSOcP?X7En@2Su3 zNBXs1?`h&(whp#Jd>Xb~NFsCWC4AG7WBE0P7KACrBt+*(>_(q~I};k`XMhnEHXC;{&xc!z+N`=AGNPj3~o(>Sm+HH~PIPQI+9 zqX;?plR~IFMcHcbVK^&7CY#t3N|9KMhkHY}kAeMpr?93yR)o73R&Ry%Wfl9(^s}ac z*QdbCLY{U5UgDjS?6IkH@D)Cz6ayv>>?H@b&tF&m(6|q@Cp@@&>AX452-g344pwv_j~7Preo*lfjgHf7(%0#s z!1l79mPItDedI|e_FF$9DZcA!*WHR$B%}ah2r-LjotQ<`CT0MDDXiBliS5cvO)VYkq@d z(wDpzcu7Ph_()>BkPE5T!XLWqATfwu&VHuSI`}$O2(z2Mb#h0%zhyFTg|1Ev*3^?l z?(hcQ`E(*38;=YKl59P1Z>?j!v0n;rfx^KQ-@>z<0$h03P=pIl1BJMTi2Zt{fN5)kJ2=i%LOu)wT>kJH0dul)}aWLn2OK5+GT7t9-8T|#NN=7h` zDMLriDTas5C%P=g2mw|eSU;ZH6S9`I42{W}FE z$M$Ofq3K>Lu$?WiJvd;s@!Vc)hlbrQVATRPOu#M>H648k*s`H;HVB-rxCGAsNqsko zaw7z-q6DpO5aq`9mg{XH{q-bN6xPL_!FmuZi)F7lar+j({TF_~qS#J6uqL)%jD?dg zMlFoAoTq)z{*DT1-$vvDZK{~DJ{RXj*=@<}TlQ@dTAre%rxDhm;%wdqOcLYmbQE$K zgIp#cmpHUjIt#|MZ{OZs^x3x$>sB{!hnW7&Zo`3s-F5X2B~}79EH#IRt2(gK>cWf} z>({R@Py=6^>d@g6=j$EG@)PnC%KA2)Co4_v*$nxwK+OEdVIJ;jpG!;&E`J3P^;^&{T;jp*eZvnX`$h+a5@MNH0LMr6H3B9 zy$)n(!_MEL-4S*F^IWJWA|82li62EjljXm;D{M*bJ4j#TZbiKCWnz(Zhb>9LbxC6h z*N$vMl6((PG<)@l3AsgBRV>!3#Ofdgvsn3QxU?ivbzuMZ`@hFwPRfmJW4)c}=Pdl< zhoqzp8z2YltX(>bF($Aj4JZ3bn2`c02}Tc`heJKW*+G_%7=hIEv|U|M*>$0{M2T|* zG|1!Z@%;4Dr?a!!mv;7ByYjbe>eMtrotuHlZK&5@AT>7wlbcbmv17)JnRNN|g*bS9 z@~EH&ECjz$SJ_i!l(Ac?%gM9!D@|O zZ_#x%w+F-Q&m>EktJS2eV`MZnH zXX3cKjtSS_lY%qRzGNHJoh^P>Ec&l&u`c*$eywL8V)5H|LU1LcF`41)9*EA5c0*u7 zSRl^8syh9hEmeMyUw>15c;G;}?C>{e>Wa&XtDE!*f$Zt0)5hYA!Xw9byp46pQ(YGQ}3 zz7Dtq2IA1Rko_4wI1+aK`U}{Dpg42?dc0FJRZ6sORsH!BhYuy=RG}4;+l{sF z-(vCU0tZ)Ys$`tD`9unPhgd}l6HNNnu7ZFRjph2qu1<_mgXziglk$_`q%%b8ZU>!u zdK6eYs?cbA@D3&5P@2pdjYDS+1aZ5&6$)nv&Q-;UjyQf5)UClWtayi(z_Ej*Zp;o{ z_;@Iw($ba|kJcdXL5CLV^&Skuv*sL?i|a2*Q|-Y-g?v=}QsS<_f9!{}0H>%0;r|!- zj}z9g1GA?+`%E3@N$uA^HT7IxyT*&7v|xdg_9DaSnoh1@r7ym~3-l{?8^&L{ol-&q zo?iVrY#woOgY)q7^N|P9s=uZuJcv|;2a|9Xo|6y}l)1P%(FG0k|S%6DV= z@BQ-KPDSeOl}U22lwXu0FP4|ex9Sc@9FF+uv(J8Fii&6c41Y}XsyI#8{^xn zSHDgF0}O@$5ALIYdkk=o@qs%>9LTqHYU_bJAx{4Zb&kA~al})1Q{?>@733Yr1NXx} z0ec)DOQ=^olf*ViH;hVV-vfPMe~$wAd(nsG!qG1{R-CQ^_FMbV2kzCVL-zT@1sr`3 z{sF*W=pV9kz2g8B4O2tb?LadPD(qIe!(U9Zqff0``_sOYGu7;6kI8MY*uckSJ^_q%Vu{pOo*zx?tmT#UtCe)-0Yqerh< zQP9q9Cf;J4J|8wX5=GJ@fz4v#?$>Xu*fvT;x zwzfm7jh*Xc(T5JrpWo2%=%W+D&6dbOU1#U_-#6$Y#wNs%o^kb@WR}L3Dz0Hk_7V9b zN9NOifB)c!*x1-Ovw2o(YHHGK9D<7-S*-p})0^+S^WH}ve)Q1??|$$RK5LWYBy(+W zZSc-ryLRv1wRi8&xR}_sTvQhVN>g7Hk}~*ESKX-E8tmp$QGq>OYHKevHoAhXXRK#} zLc*iNf~^xL;>_H!6L5Mb*H^T^3$?+1JF{R5b#bDuy&4e_1ml8<~e$!UV~#-#pi7dC#u_|!x5}LiS3f^$(D-C zrLFmrDN6FdDu4~?ItBB6at~Qifj6UYuj6ts&PGQZIaOQm0AN1@M0keVok z5H+V2ric=?5TYh}v3VZ8P|3uX0aPZ>1(3OYWad2D(2q0!uI}?<**_XZ{O4H0&jgl` z0ghz0Kr*s-+>B1Cqh4+#=<7M94wj7E-eFh5>TI_&$&O80u(={;Yy~FCi`C8moL3CX zZ?PQ{Lsh26HESB3eO`zmY#v+3R?1oIGdwHptdfqe)iRq72RmYO5*^dw^e($fj%sO% za`Im#$=~1mYqw5^CPQ{pEbtl)yv74BD_Zp!CNWJ%2Yl34I+jui6V30y$pZ&YWMrH; zuyfnColdY+c_8p#|ANz`3vF#R8ka2tcl|<&LqfvL=CBaercElR0b|;v8|i?AdF%uB z9(zxk#!f5lXN!|rxs;^$kM|F3Z4JCWglYWr`kQwCe>H}`g>V8;LQ}fYB+sUuK$?n(PiXBc$OWV6kqN7VZl0f!Ye@TGX2K_%5e0iVb zKuRK!P|+)L@U*vRD+3k8Ig7ENKpbF%Z9wv_?WjI~C_f`>kCr;HRjbx8|Cuv4Z%#;9 zw{FCQRofw7#&MaMjUQQ_##;mNYGwC@ls`Vb@ z%S&H=?Uh$vqnb10hh@`#Tsb3b=H`TTxkmYe-+64&- z3#h|neH!WCuc4Ql)Xs>Tv7DQzA_NgfQx>?gc-6FPR;}XI@{gVQ)mAS@iJQblFGoGc zdJ?2U6Eo!>cY3R+BR=x_`PUpd+2-XZ@sfDx<)_o%u&y}K%THn?G0@9QkIeq>IO**r zg2BlMs{%cRq}6ac45+4OHWZ)Oy@T%CL1(BMN2}AH^a8Jmk&qrTKp36lV7mgSgS(*k$ zt@5uVu20Zyu~%k;02c+~-0by_WBMNLD-~>nd*f3Ka)U0jrnjXU@eIO8JHP*heWhgL>;d=FOWn zegFNY@4nlBYunCkJ7M`Y)U+_UE_2fc9A(mu13z241IAA)%X2MWeDY*U%C21);TaJg zK5ELGIdi5(MX9mjVE!+M4jnp+tqQ}{n>VZJzmtvdYp-wZY^cc2e(SAYDmt_}mD(~Q z>W>d3eD&41ap%w9a*K1s(WB=}>gwt$PM!=xs9@p<9OXKG`t*-b&9E4h;0dYM+C!`9;D?WY zpean&T7#maBO;<=!h?fDA|gVm_v)#kdRkhl z;SzQOANbOVjWp7|gF$3dP>(h=>}6tvzzs_criV@}MLQ>!qT#6Dez0B--5bxbDZ<6h ztfdgH|3X5(6mb(}Qzyop;`!2z{$&pw_lXf}&cpe*9Y0e7+s>S!3`SjIXVze`Giw|h zO{q;{h^ko(T}JubnN`bRnWN*VB%pdRU=>1(O$lQ@-W1IjNA<_JzfEIPj1^;p4QxLF zjS#Rd3c}bFm+>(#->^}Fs3l@@$|NxrqecwZ#$jsza9oNKD->E`_2ee$hK`i{j8@A2_qBmFCoBUQFX$Js-HTQ;)yV3AHQmD%60c6z}(Pq z|3l9_;S9*jtE5d4{qyq1jveb)>PV=Df zJ1C{{>hwMhdN%8|oWDrG|!PP>j_#fLIQub78_#@*LIr{2)Z3=xRq{1(<7%(KYMol`WZpB zI3>O=WXAd#^Y41(kw@lZ^rF-5&_Dh3+O?l-IFjXRwCn%*&*iUg$WbDK=w!;Tzn(Qq z9J>AG{vHgD_3Zx=Zk6=qYYXHD>f-j5W*tOftHX!bEG-I&pc+} z9|!az{l}nbQ!~CYv!3^wk^=geX6z{Tiq15VHG8zePR>GB%g+yAfTYwP`+V_Yz5dI^i$8xaRbIlcCzS7h&OWz0w2iK;Bi|wb znoQd#8)IYbF)=a9oDHwq?M{}=Y|N&4U?PTqCjKc23;s@@uEU};n{@j0Lj37VM5*c8 zfMGxz+PuT=Fq#u*UHiu;pM3J8j}jBHx>ZwMT@y3s`X_jt-RWOeTI4D!%P+_;E3YU! z=)l&y>MjM&U{_-qhb1(~9ApVF1z5tuEn^1!Z2%woTQ9$sl8=Fse_<;2CffUBpyVUa z7&9u$iXoKc=z5B=A8S=thf>+uDl7c;t<{+)Po6wiSXEhBRqZ-=?p%heQZ}ktYgb1- z?6K+H-EOT(58S(%+^C>o#^#Iz2eLb{?XtETu@sOT(G;zpzp@?uqH#8qV`#T?MrMTu z`~Qv{KXIZJ`o!4gI=kUtZ$(6hcH|!T5O(p}U#gZZtIGOj#flXl>^NT^K7P#G?|rc! zW2tt(o|f7RO+9ei0)i}ljyO89-FhqbH@{`z?nEw^Y`;jXHzIe@u|V5vPlpJb9#v#^7}UTI589 zwbfSCw?e};l;-DmXgu*2@_WegpqTsszX#K&zO<+DEmdA2&xgB00UpbP_S?N1P)`8r zcaY5ALNZ5HS67f|s?`~pnZ-5LZ3-&By}UT%#Iaur3JPHxUFdKqTdLqAC14O%r*+d% zY-e`|x=M{dYwCjbS9dod5ZuKeogFO*OA``yzyJQm0@e7%7-YFAzB_aM ztIOUTW7Rd+cefOyTPx|rgb2Gq8#FR;)28v`6I(t{!PdUGRNqvwh+9)2Ik>cUPCzG6 znp@*gb#^eFCQTzZ=Kpf+*s*!?U**4I!oW(lQuRP|SI%20@?CVP-zn&cTJ%gZbn|Bc zXOA7rs_W5=n?Ha4)F3!_c7sA6nQ+T3Sy|~3&!s}6(6vHwwd&wJY$hxe{sWr--d|o` z-PGk!X|-TmTH^WhnR%eoPd{Ow{=9SNkL8l0 zQn}kZ6u7%JDlG`q*~I+SAXt~m-(*7ThG1!p3^L_R3^8gAkfpjh%jnUumOx!Qy40?K zu%Pf!iKDe@LttcVqz>#iMzAisJ;vSH-l0;___!YJy1BC3pXUEGw|8}P$V#O{%Mbc3@ZhnN=g%BFb^i3x zLr0GuKl#h)^?p6=?M$hXweW;xw;eHM#!RuxogZZ=qL)1T?6XUPDzHw4#>9^t zVZCa|l)@hJIrq18y+pg`DnS$U$}^-US{$0v?j>7>`9c zW*9^`90~u4jY?eR^z7{1yn@{HUovyf{(>m$ddwU3cgtXuR#qw9?d?5qIa+0{(c~Xu zO-xLjhr!$r*ayn#=w6bEE`HtJoUjgdsW836tyI)!XXTfc zyeY&6smpNLOR-;T=16eD=>|Y{vbZa>tIDGm~jk za#dvH5x>V$*cWuEUc5}5|4uSYt9xvSmITb79*iPXrr3ngt`@f&?6G@TrHfg;dd=RR zk*P1H3TCobrF-W)&6t&jqcN)LTP{>LwbWOal1%bu7ojdHgmLJAr;Gig@h^&K^Z6>V|XUK`KSzAkKUDOb*MGDAef<>Mrb zTbeEd+;U8w`dri9rHi}l_s>(8N!KUrOX<-qG)G|XY>%q-d-lFr5fS>jd?@FmurzIU zZQ;XD+%c*4$TunS-|14XJc-C(yF;VwuCDFTbz{6nUmB1QKjQMy2m__CSD>+-s%Nf` zkk>h*3!xhGi%TjH04%GhEYHI%G@MFNQi!2f*THBR^3~JXi9;5c3`4&Y#s*}XY4B?o zi^dlGrhwoO%v^VA^R#WbUtH#$)cCtd6ZG9QRo`DOx)Fv$ky_R{PsfYH?h4J># z4jS83b{eq7sf`_XDs|1+Qpd)AhlwlrgM(qnb3n=ctxucu8A`sBN*gVLjB#Rn!7MTJ zIs#MV1@wI=*}+uoZKh=RL89+NFFzf_FIhQxc?IafTp1aeP!~D*g@svJ;15JO3n$>v zUkVEHbF;HfpN3tOpN=q&vQ1*LGhD{d1;n@u-e5;}o2=KXAmAk8c4`%`DEtD|X-2z! z>8BYj=20VIcc#dX<9fpMd2w}@H7?F>kBbX!EzMjlet%qtBvGL~C5PUn&K@V$E7z{w zg+mbTLKtF(T^U$e-{}Ze>y#Z|eA-d;6;#Zp+XF}c{PXqqd&FP2&|R+4l;;;WIwZds zM-p2v-Byq$z3>9u0^L(Lj1BFoLS<6ODq!!Lp15jUXz4G%yzl~!oY0P){*d^6r4C8T zn{}3y$L7Jwm?6zzZ?U%|oenb@;9e9Vc5^^ctMhm0pqz}Q#m3n`d0YZ{oCSHD1$m6l zaFynK^~2U5zW@IFEkFLSVZ-|M-)_Pb%pW&?`xSf3F6q=f_R!UhlK|87m@V1W)s3+> z$*FuVAb!Pf zIfi>&we4N-@uha79j2(eryD&?njVY|=xMGjEiLuLUQ$8lMWFK^K<8A@ITdt{gINIi z%FN8l&CR2>PJLG)v~NK!X@tyd1c*tP+;)?jns=95twF3p3k%PoQmWB^rKwMzZ@{#c z6ilqa75>3V-HrGxSvYszoH_ zc4^X|J&jNK=Si!U&!dT>xS(XPA&^6LYSpB%foX9iyqQ&0l$VqgQ~RR6rvSZjVF|Uu ziZXTx*`Wisn-tWf)QYZ77}gyy?i{KfIB-&0TnKMR;o)H+A;Hvus0%Wgf`S4=sF{UZ z*w{;DA!L3&cyTXy@kj7tK6o*oJT2mb3!xJ_Z!9A{8>dO-<`bTIIHJW>=z{-610f+0 zb4F;YiTnsq;_x1=7BenL88{KXLrev8u0EbG|AT8;=vfH=D?cR5kIIkg+!uO)Xyft2 zo^&Gu$e2Rd77gJab|nsP=|uR`?WeQ*>mnjP{`li91cker;n1jsKW+#Ul&DfS-BpBXnTX$BO!_<`m4yew)3GhLL86*~#1v zv+VB9Ip@lxuuP&~;mIHVJ4HkqG2NuHGSuV*B`PXh2+N?kAnZ|ATwGRCMABJYhTjWZ z@USSf6@o^=+z?Jo2!MI#rqh=?I_xS^ijs2_=KNw}URXF9LPSKQB{63|{a&nFz zIg^jj3F~dm?OTq^9o_JjQ{_MM>q*o4 z?48H!>Njuy@fW2`VQcSSp3;0-uxtC-dUXB|*l{{^GP;|;X+O+IlECIIcJ-En-@Nk5 zE2;SP;l96^8+-pkUG5$7olb;tlEJC(jPWBA6D|5sBN_IoY#qOrn;w7cy78kT6UJH$ zfgKH)SHmvl>*UBWCmsb%2LzaNt} zxv;RZ5+*S*wvhTi3^tdSmDPyfAn)|SPvxbh&21NououS{TZxU4Z{9m}sOE zU&7)eos$CtBO-$E0h2L^OzaO2vsj|VZx~dj5B(Gx8b3BRh>g2chP-!L0Ny_W-cw9u z0b1$;YN=?CF1S>J_xU;5S?L+Dqx166c3~E$r=QQjc%m~%>TK_l;fiT=q|iy9D;!!7 zTB&gNU{bssEk{cS52nhG;QFillI9JX{P{LQ$bXb?l$`G@Ld(5XPLi)f_jR+pK#p{` zic5}^uaO_dc+4)l(=hSdT|0IbMzlrU_}F|wbhb^3ll~~FHBL=;TU2=B+$3kRWWaUL zJ*g}c*AIweCJH}dB4*)_~6)VDZhE=mJ*x6CiBz{el=1NyfAsFC!1yR_R zWVi)u)Xz`IK5}HmJoK@D6y}X;=~u@Ks~y_PQ%8=}Pq}WQa}-*tt4y?8wA|9N@~TSA zb1W`KtA@*8RaISy;ZOiZY{@m@*(xJa@IIhl7XsTw5Xt-#)p_Z`7 z2uvji4o0(vkq9#}0u%Bsm5XS_wCZ6#I7#CyUalGnB)qBiHyRJ`=y zgUMLeb6jDyq80tqZda$uUm^yZA_urv%8S|84uvu{by-+(PEK~NtEAWkGrhR9w4}J0 zNX+&DRVC{N@mlKIdz|n>mxTozjRrq+NU+)8-xM4g8WJ2#bi@MSi;{8~zXg=}!m48H ztV;HrRl#n7!Ir=WR3HZw`9Y?*pk&eQ(2YKx7lksR09p_Vudpy9qo4rZt_$j#B3^kg zF7l}%<25fMBYP*pm2P_onJ8pTbTG{1f|N2DPK3iq{;-S{Gium{EcnNtkAMHh8*hBM z`^SCTjzcpX`ugV|=OHF=HNXBA^z)CZnKNew?)s@Z3`1a@wY8@{NTHyD{G4I+&)atW zn1Kk|nYR{DpN9)4=Ha?aS`OYkZPy+-a`eP`S8b;gy0$3u zcJ8)q+e;(HVkVp;;&~l&o5v>2orKTaxnm7YE$oS>t{EF%vfaso7tX)?!MpCd>)zkZ zUpNiX%mnNB`3vVEx>Ufg6(bkUm*KC=*UyT}|N85%%OWDKzVrV3?|<~ZS=WyWNAkpL z9)Elu`_z;C3+aA{z*4&wp~x$zPMH$!G=@!{H08?q&@Szh9`q6>S^_n-q6&9urGd9zr3eu^pbm>31f$6C4xkPp~|s^6rw1En^##` zSO{O-1rvl~yu~n`ilC11hB5H6eb8wNdkBv%K^1G6TdkJ6I*EUh1E->cp)eI?HA@JD zI)>&onIa+r1L2_KbTB`YDG>8?f&+ul37hd691&p|HC&EHa{s+qing2piMp;_l==ViLK2X%MDK2T8~f+!h)ST{C3 z?l@hUa2q~TRNcF12=q?@=w+pztgYXE`|Wq%@6YL=)ojlwYWD8^`Nv}&Fh(7S zgr(7?x^?W>_&|q#{H?cjw8mU>%{6=NY_F3U3l@at?8B$jy5Iqdb*&HRZ1$ga)x7)u zfzSQ(=M*GU2divuW`DEm?z-#l$&G~#2N2KXKBMa6BS*@*9eS;@q3{fx?##RF>@H_e zK}kLuR6$8eIof|wS#gEyV33Neo^CQMa9TRTuh4S?2m-PQf|x=A5KT70-GC7kWC;#8 zj~M{mBY?SH2nCgC*5(Lk%urA{6dKbATBc{_X1EGzZIq2^9Zb7SJ?l*C%eEa!@{KBk z_ATt%4r}j-BZ(bbrZ_ckUMky)ekO%|qbxggCFDB|f0iG_6zvC>8BvR>ijqoKK?y86 z8|!eej%7w*7a1NBVK#;EEiCzug70cz|M}53@Tte2F^VDT$qKRSP7@zF(XU?BLd-gnxaKu5-C6JYugAQG-Mmu40 znjb_=cH{g{H^ryyeoCw@g5e3j8$##22EFwZT+`$k{JK)UNB`Qo9f*=2J$iEcb#P0@ zVx&DzPLRjpjjjZFx_r0p_4juGji3J;O&8wdZ!-#9CAB>&M^sB&?9IRX%}94y>4SIt z<}!?dRy z*AgU~BANJw-G<@L{*3sZP4UDN7!X~}a?rs)W7mAtQe3JY;qMG8JzG5@;kx^7offrn z?V+NaZKb3BD#Gb+s-K!0dj9R@&V=#{)zpz!pj*d)Ha=z57pm!8VHoX*WT1y4^R5)# zyt<1_bt%nHcFPhL@M#nj1;tn*Oj$TDh!n})oz8@?*eL4L!_mWI5F8&%R4kVA#6-b> zN8kee7@2pW=<$DDhC*hhfa?!ISNs{eVhW^q3X{UHsY>rhiAXMbHz)&z!jheH_I%b^ zbXGapWa4F~|y1Q^m|6I33V!;Fq1FI?Xoj%lDig*S0s6sJkf^X!V5o*;(RdP+EU%zP=MuT^4`sy|GSy&o3J2;i?V{6z#b|m11za+*?@H>a#$XVlr z)aO$%8K16YrbQFRj~+dG{Dg@(@OkP)_9UWIHoJO$FuT97(4mOE=`Rb=HSJ+8ejQ`) zng4S02=@F7&pi1cx|0W=eEPW;oR+fE+UoLJsQJ<|vfwJKt4hJ)6PTc-hPqV40yiqP z27^IKBY_%*kZVj-7~<)cPzyL_iHM2}1p_DgO3yXRo#4seJuUH0@Z@f2mPwGH{M?+p z>~p72pT$7;xpV0mSviQHXJqCe$^j=h8!^h9+?*`5lXRNYL+ak4l4S%a5TtO+jxZUg zj-n|-!4o$XGCC)%Rt?Wx3xXu53VwS1TG-l4~RCbphmII1@6>H9_}+C)He4^Nap;?KkA~{o-JwxthvaXXhwRg6u^@X@ zcTHfy?t|zWjPuZEyeI-6iZ!_nX&`4|aY<=$8D_xO(kxpV6e&fJy|SVd0VH_RW#n6x zl$KK?#F3+_BW4u`#~QAoIyOdIRMigH9J3VQ}`rDojyu%XMOW zl61WqGD7inudcUau4S5M)OsGhS?D&&E+Ccw;$}en4G@bVRkX@I;dEP1&f)ZQm+Sob z!>|g^ojHm~-;vYj4jnjg;>7*~=d#Yz{E7@@jH5-1&o?#MWB2aO%4)4ReE9Iu^pa)? zON&d>i|R^nBymQ5QF&AM|6%XF1EVOn_wjdTYj#sMWz!o8Dbx_87fD5mG!a46YXcDw z3!(xd?(A-8ie5!j5ET?GNU?zQme4{81d@=RP1z)*lU_Ev-{;H*FJARt@8_@IAHN-# z-R#cH&c5$?&wI{uo^xJ9WSZ&fH(Ph^jEwyH>#e)4m)}S|2q8I`TGQzh6&2;xR#R{S ze;>=k>?24d0~aqg_;22vL0qy{#CcV0 zVGeK?FOG*zxC?)MiFfz0k2G&AdJ&H|z01D=uVPo3_k;vUz;h6_^l=|AWYTOO}WReOXY!J zY6mH$uAx}pKFmYf)dV08fn-l=n8+bcP6q$mx6j^yiQ(YP8d{`|jv7b44eQO{u`2m4 z9tpn@#byV87<@pJ1H2l`te(M0cMS6I2@eO4CnC~E>jA}}^+JEi8XFy)@7yss_UWVO zluD&FX6C>(3XbkxeD!T`DqAhQOk%X_?}yNh1z=Y6l%u`lo zvB^~qz+aIGDU(UCpt04$tPFE}7xs*KW)JHtsl4>n`yXt}EQ1VHIyw$Q->ihI5V-Q8 zP_?49pkVCCUe3yvva%yzVX5?TN5=t^CQZ687J6X~TPuHcVn5d!EFkN=I=B~ZF?`b0 zfk%bC*Uf^`nDq6?GllOi8#pm1hU;1gwjw8uuiVmGPc{Z~`)?Zx( zz~y>!@~%X2W#pG_&%u*@OS$RClc$emiio_Utdan{55x zceh3Z`E%21wQjB&cMlI&_!q7&cn{dDyPK=ClZ*I{Tj*bXch|pvw_OGa1)>W^4T##4o?Usxy>jM-CSn`vm}HoU{a!pnZYmD z)_>%;Tfe`Y+exI#POd5A$ktDG4UcHZy86#}tRpRMlK0ct4ne+d-Itk}S4#9=?VMjx zmlFN%6xgTFG`JEABa8g9x2`kz4)M;sy6~mvr;myD1}d-g8aV2Kr(arlHPdy_tVASe z+DURhvk&+=>=l9JF-bCG>{uUztiz&ikt%@d*QTca5C`xWn`}#Zi#lonmftaiQqP&o z&FDeV)+1N3tlV5sXcAFb%qC?hCQ3gHP8DZ_MF(HdnPc5p(i=?xyAP#0Rw zhz1_{haR30{G%p0{`x{eSX2&ADq3icZ-@qWGdhss)eqqjST~03E zL0-1PBfBC4$G?c)ejzci?_jU17fzizb;D+0F`Sor@!|<_k1l-dg;lFoy)~1qN6adP z<%94b@7biBKbOGG;>L3ia3i>AxB`)!7yrZ)|CoyOy&rLKE~ntado~R+I=fi~%VNLq zZ~X(;*9$_NXmBJ+XGIP7a}=O_^)8ye!-of`lnMv6Iv^x4e5}CT_@qa2xX*J1r808( zsIgaHHNQ1VB{*I!+2?=y7LNm0;S31D8uz2Sfy`g_?{pnE4}GdQ zaQgJnp<)(i=bZ)*xz*@8K;6Ve^$BojqMK;)jEZ`g5Fl86iWgt3tE=}P^IQTt&%Ws; zuTEu4U~ec&=O7rijyCBqo}GJ#a?-@ntT?MwsV-blD)q7^Fx#~nV;GbbbTPa)F^EAg zVH857O;Fr;MGrHD#S|QPLIjuy$j>7+)vcFwcVmPTAddzxhPeah0jxt4;k)Lr?wD_7n5fzaWe68QRs*?i;~w`_07}&!cabqi^S;Zy!b9 z4uBV*oky&^f*icO4xc^;{yZ8JbR0k^1-ZAC5AYF@zv~{;;qRF_&olvP{i#hJID2BSV4?!a(?C?&piV;amKhP zfI3doCZXWYm+SybhL3j``@<}qE&$IZP2lJFcId+z+Erl2h7TQiW6RO>to+h$RZwhf zY@qe~eCjD@gud6WKTu*AK=mZdr6dR7Bv&`Ur>}rN4{-oF2Y^~>`R)4FuF}TJYE+&; zz8BQ1Ehz*F3$vTG%Yr6^%|ww7j`a>lS*QT5Zy<2m-VufYety2*T6g+3FL?GS(*ei9 z!yV!Q>JGp__m29Kob;QO7I#n7{ExL?+4E1YJTqFa?B-!DwzYNiR5t1lDOiI!VKe50 z{*Z$HjMI9MuTNS88ij|BA#YzM_sAy?^9DpBzc4p5BQw9yl$ZOnhg#O%2```Hx`7Nr zv8fbraglU0$@U<&$+mb~f^8!HC)lPSWV1r~GdWhbe%ZE`Vj+{ogPwITO41 zS{~h|k3+T%Hck&MKM?--Mz0w=J_Ukf=;+4RT;4HdIPu%uhfjHiSXXv4Z>{VZC+Be! zXUE|kY?9d%$2l1oTRNHZ6}J%R>0#V-Y@@jWoEN(#Te>tPByQydcnl|5yZtEEj@R4x zRf(aK*>-#Yz6rLic>RUwf$m~o0P|T2!efBv`;(tvym;}#3L=~YPJaJ~0^Cx;5ATnM zfjGt%ByMAEFG;s-*}QRG4*^%QX5;2929GjofI7%WSxI?yO*M$k=JGU;mM)1LNP*2J z0T|iQ*3#Mmu*AcI9K}HNxQCl3inMsRX|$dL|NPkhpar(mu&<)sAljAnyb{lN9et&P z7WfcaU?uv01+>8EoU^B|L6YGK+`O4*%DHhB4qsYMSt)|G`KH|b;*z|}Am%1tId(1m z^s(Co$Ie_lf8xOY!^aLB2IwdB)`t6abulGxDy;*)UQ^p`O1}b3CpG!(M>|h~MxJ)7 ztiDUHsHu`V@GRG)mvnS>gLl(b*CL0z)3`|KpBn6vB}B7-Mk zFMf;vCYHlaL5rA#K&jNbZy)!lM6NB1n~M0>gX*S%T|eTon-8UDn%nhWphH`j^urH7 zKGV%6%<`5&N4k0J`|3l(=xU3t3CIILrKYCp^6HLm;ApKCWo^!^AnRgUm94#%Lf&#q zhq<)5Bd?;Psiv-t$t)efO`3&~N@aakR{foZ$|{SExXo_zCP!Zt$jz3L-KVRpxD@)x z$}8*Zd1+NeZN1h7EX3C4#^!p%Xpg9&y>W_M?&$9BBPfnWVg9mW=Wclm*D<(e9$XghGAK ze0G;zNE|=P!n5brO2zYxI{qfk1l6w?=i^siZr3+8*3|(2sc&g+Llj5WT5zQlI3&&2Cc4pmN}lHW1D!hl#tHnwp$^u|ooXuCczM#ljh!a!l>6p`oFH zP95pHzD+?~%Izq-m2)Q#;N|{f*RH9?j~^d+mcnwfXP(h&DM9Z!+jA=6>jR*B{ruAz z6wrvU*}7XQkmLqiy|U?rmb^(dymj<;8=5Y+S9x;ZDUm%FqB zL_4Puqy6t(yLPQ6U{qA(pi!~F;C4xUJVWB<$5CNA_O52x<8%8rVq$NNh~v1_)UvYi zBG;dwmz^N3803Ixmc2l<%^gB-6euWbl)45$Qtp5B5$PGF6t;j>JLIvKUV7;PAD6nr zTefU*BA5jhpP<&k#m&2MG399Tt=op-KkohcIHEu&4<9~!=-0!iP8|B>z~K|H0}lQ4 z^DjT_*syWm`(Kq5@|3-;a3O_)k+#`O3Z-k`}2S2xKTV_2wBA9>~nObOi+$4_>zg20G-QG&rT3I4G2exJMsV zD3B*+OSC0=?A=>ea~z1ore87}eEWxeu;s@K7#)IACbP&TE%jyd1#ZO(gq>WgA9>`F z2g5pR4?=GC6eGn#Zd6D6RYQmG_$WE~%$az$2^)AzlE)tNekuX8}x6ezxrR`$?+8M!%(QE5_G{qwH+G6zDP|ytWi;JsF zaDNJco#7dQ5PD(ia+MWczK$0qI3PcloTM!DX-;kqcoS~P$>&jK_ToiYfEJ>2;9>AX z8bzR1YVB&MZn#5LXzS`a7;q6QiJ4KBwRi8k)zzRB*|I25FVi0`lxe*wp1;)gvgX}= zC-b1@ORxUC^GX|hkRvg+$GH#Ij|XjODe!J;_-AczNH%`@Pa_3xXX%g-e;>~t95SS2 z`SRkQkizzhxvj0T9_>}1oq6qI%9-SAWv$lUhzOu$ZeF*{cW7Mv*kNH3gUolzl;_*p z5^OODdm>MTZIV7WbD)P?9NU3ylXm6v(;|UxyK4g@o#k*y`^}Se%mX^NMf`jRO3c5# zJRbJP=Ro-n>QHD~knG9L?Mue~%6=~WF#k`AbaHZ4;ZBrpK7lZdBZEEUmRd9{m~igx zcPewzQd85)C5EBkqm&gDWtrfAfgxqWGmr*x9G%;LtfZxZtQj0oR!&}8RegO;MR^5L zK{OU;XSdEyh=!E-MtB(f5M?T<2B50Z!h^=gZf*(&n1+yaK-^}ykVnDnyvnQ!@P(|qOr8L!V$FH{U%Gd!C&ft${dIvfQ~G| zgp7|{nmejXtJ}Ik?6A*mGgJ7+kKE8DmC0Wt-GMKvW zegLb-FbVX)m%z1Ij)t6v@ah}r<`-?RNc4KWZoJ6GS$wywEC22`jH1bcz}Mb5FN7Bs zlZ}BGN2R&gR9u*S9YgTsmr46CK>eiO%*;f~W1g!^Qhk^r0~_P3ufE#0EylJ;x&}qw zfbgu6t@{%3|E-A-#YM3A2P1Gako)@6X}GzCwg-Nr7f(VE={{SixZQ_Rki@2p7X+^0 zGh9pv->?BxmVX|ukoLRpzNkSVzG?`7i?@69GfS2%iS@ExI(z!u^}Ld%B$=im9Z`ok zC?0Iwim9=%!ah?!_~3(2y*zKmm@#9ftf0im6wC)9oG<^_{N+=TF_Xi2(#9%awAnZ} zu8+9&L&Ebhm~3rm3I`8wLF1ch ziwICB4TSPQU%(`|Q_gG9CJ~<9cMyo|bnzWBddRSmBgXh?pgrV45d%QL8R#issU(4S z^7R+Or5Z#9RW8nsh=a>arMK@Op{=H}_F5KBZ>XzrRy#T&wCk7@Jt>e3IOy?UkI=rL zy3Cj~Dh6i2Fz0nJNjV!c?7zv zA>LeTOH(_X^KQAjzlcd#ElRmk#XQ}VNp`#k3na5%khL(#ng?VJo}d$g_xC&;L8>wp zmfH6xnbgRnN#I~fE?>LfKNdRiU#}iw(~Z*UxCjr2=}C#J%NN&UZBN|uYN_MTgjMrD zN-fx!Bba5Tk^=ianOTg?lH};5)oR?F?E4V=d-gk(;ZFaEcZg623SbIsJnc~2PeA#7 zJKm9qGakZkBi4K(zBdu$D5S6eXe=~GNfB~3a!g3ADJ;lG?1RvbLSknDkk&vU10O}Q z65%-#AXr8P`imZbJ#7ZH=Id)gX!n@|_^!P~MV#;4i3C+k_R(MP*!%rn4gKyX08)U{ zt$_APFLIl?3J|g4_19k?-qxm6=^gq!G}BEz_^GF#t`Jy-K|Lnk{T6s{CyV^zAE(5! zUCv5g*=t1n+=UAlE_!@o{FqpFik*^#g#nUQLdz6?|5ZyN*!m)BCEa`OTon%zjI`ye z58mChX_J@0ybK{FrGVKnMHM2*Sy5p^##ITTop=DAC?QJ&XtW0E&t|dMLCT*NcdD&tUY%W5H7PRImh< zm3jQ**Tm7(q@QgZvlf zvSCA6+5gfp>I9Yt-q|D}^0wY9Z09sG@s@~T8ni5-Q0LN|fAXf~JJ zEf2PJYE_5grfVUCu-Xu*b}YI{DD>`j@bvNZ@e68e^Hts{ zEi_eBH&DiRH#n^=r(l6xXnx{}W&`svn@bC`Z(K`Dd;Rq{-dMR3Am6GblLq+n&CRj! z3H7Q)|b7NsxX_(#pg+GK-MIg6C@bvT>{eNP|3j43!AIPORLekhX3&(QH}}gx?8d4 z-YlNlV+hi5(R(?hK6bytWnl&7a~!NUzcB2@Gvje4MaX7CAKlF^2`MNk`Aq>879#GN zLwTt19iXDXt|=*jK2maIl_K_MOVR*qB~9MdO`{gG9B0&gedWr&@As1GIht*uU=n49 z&UZN0B^f|UDljdUXtzjmhBgX{h}gS#Ux9+1I$ecnLXN=OIixUb_~;ai9vjIbB8-}N zlW9c!V~e179!nfE(2ZBdQkdQ0+4!Jh(oiKqD2s!Fw@5H}MNpNN;<%+RKc(*KKn950 z@ZrGUMPS|G`6%^M_sh#zX{j-!w6O3u)dO3t$W&1dx+DS#P)^{_6c$1gDQ!0BSHzUL zBn_}gF};NP22nCZ4no{vmEBc5;IF%AkeiCQr8o#csd09O9{SIh5c`0llrzx>|3Dx7 zD@vJ@^H%{%Q)PJxh5~g0-5+9iQqJi1TZ9rf*l@d1yhAP8f|rB;fy9N(13&KF|I5S& znhyMs2*ycIgwnQJaisb2$B!RJKd7p!=g*%v%*kWsLw)oLN5p8RVr~H99faPyYGA_IeJ%O1}2Oh3|VN z3e}ct_1nKq>k42#KE}|Jii*FARGPAL3o&M>TS)dGZ^)N&`z=%n;nR2_atY}Rb@TH2 zt4O6rsdC1kqMrFLR}uSWI{M~u^v(0=8!{uNL(@er>=)$gDyLO)&RDtJjZACrG}!TH#_)ffORe4XLB2g42!!AQ^l(uc+tanL*s%<6gX&0Q9oc{fDH-`$;lZR zb#=)sqtqz<>Z`~|I3};jE?zu5JYg4dwfOI_O1n`qXi#CHzzscZj1^smix(3(M{YDK zKz+=<1CuL`o#1}t82o@r9K3t?)Tsh@FxTipw4eI+sw%|rTd?qZIzDuCY(#l4eo5;JrzxQ<;-`ji7o`KPBZp@rh z-0jG>6rTL{%WKz5x|FOeBd>-F8HpvHX*QSlzwz&YzRZ1H2-gta?Xy|=UzG395t7DP6drqBoq0kZ-<|oZVX4nYXpx8PwO)$p!q%J`Kv|Tg>g(oI8!p9j*%4rgD`eCRF3$?OIQyt*+MY4$XkD zuxO_ayIc^{c0n*=u)+5*ZOIaWEh6$UVGz@6vWq=$nXX;GapP9@&8(d4v}>0yU%i@k zBP;8BomR}^Lt=;xC!Pky2rBC}AFw6?aqe3D4$ z^s)Ka0>HkG6}Lxi%caK6Kl}_yTPz45{(rX>(BJN&j-ZE85m52g8*d28c?u^Fe^(fu zdd|{8)xH)l-jl!uaZb#PwBOy*SKx}iO3{%cvXv|2P|ie0*R>hLo#gd>Rhjiw`^LN=x%EUTs9_%P}y07g*@k~K;1eiRG7 z`thK>$>kU{ZC2M>#Cmw%JLa*YhhK#qz4Ey}wXO~O*XUz0O0U4pW71J0hD1ap06)0~ zN_;B&r)tXZK*A1!hb4e7oy_v+BUwbmpfRJKo&p5#aL=Y^3EY$1lR)r(5flC$5hHy? zNEGe9wV0-0|M81wh3I8%og!a~or(n>wS=3qG64hm6}!(W`#P^hJ^oEtJjilb$^BSu z^eZVw1YK-N(K{_JAp%TUNj-qQYhYH$cs-g=P@EOQMm&*lIQDS~>t_Y9=E&)zY z9;vGvF<(5~f)Fr|`cEf{J-!S*z8#wSI5hRk!919;92=QO)&}|!1W4Qv862=P2-qe1 z8u6*A^{{?$=g35s!-Ip3f`{H2 zA6TuF(1#c|tX(8Uyp2s~n`R5d765Y8D(U*45Qe2HgS4%7_~hW_RD@wuFMRhPwEbI{ zG=_nsJyzU?0C{@=`u{B+^oj%!s|F(5Zi;Q0O!o^Qj=h*ikowUr4Chy_TsFnav;(}F zobsBQMgo|dt1W)RhWYa=dES56Fh5ImGg5`S8*6IHvvAlBlh>4GD-DxGLAk@JIcIDx zoD55C{lY%y7cE*ep6bS6yNYayPnc2i+L9Mu_y_V?E+JQvr0Y1#;vA5GaUZsRAXzC{ z7g!g&XhYToy)R`o5V)=6SYZ2z?Sn{tF6cy9Si13eX=`GSTOpep-nZ`~ef5f_Qqyg1 zq&L*oN}=Wxrp8!6~F<< z6JNyZ;d=CZ72po>yKH)Ad?0?GT!PpiSdwWTe-lt%xOn@S$zbuz~=O=FE~pX zN^YG_u5E>94<4b%<+5&9706owIXS1dL3lp7B5NA?3e?Yhj(GAv@o)#VzP z=vjW@xO&DY9}~Hj<-Nu|9}61BE;l8w9W-M6;{<*^nK&@iiHBPw898#olDlH?kG?)F z*HAd52meYKb0Ae6kGrSwDsAl&x z(W=Oer_tDLH6mGpGeT<{dZX(ov)1lrg1qGkw1wdNVgKza_MW*9J+lHc z%e$Ca?t?{m9~K|?!7NWQDbD#Moiwx1dpz%BD!7$??OHlAI|@zNSy`gSL3}sw=6<=g zn<4j?7aRfBux=<25*Dce1vje?OoS47(B=tN32jomdeHW^5P~ z>(+gCqEb+V%vrwtjpq`5*D_*{j+hLF{s~sY#c?Zowm6JxtOLg{9>6~g%aFEH82H!e zO|UPILSfLB#tz|=6Zqr?JI(fRixO4bB8s1QojAQ6l2k9fQtklU(fVzwb{8@-EA#3`PT~zZrmu%H?cWjO|8MHiD*bH z87FQZOW%CW3&VkR7#vNNi0#-($)<};u9qP4i(E5}H|ogSS;m^k-`=*%>p1+npYZGU z;n$7BuN%j>Ucn@;qJWADl$0s{Lw-qsc^=7H!C9ufyxiQpEbxX9G|0%zxN#j=9%4#Y zu7b*wk+EM1w3g<^PMcn01E^^rqz9}C1K$Z0H`cn_x|=_G^z$j!E^M7Jb;50))(UH? zHQicdEyKh%*S4D9`uXQiTIV3uzYW_t>w4=N>)X~9*5%e$t*=;@Szo|s^eOEhwQjJU zwVt9O94V*>b!=%lv0JA@&e=-XDJ!{_Mm=AXb|RL>3%8_? zQNDa7SUij1f*)W9xXoFpXJlEEWHcH-*=%63#4+QSEgSnFE5>GmNnC}M!Q5a0M`P0> z;dA$?9xsK>VpDT?s3nW*#}G2MrLt zBBP!{F9F(O!M++T64$i8J{}aK@%Qr&4DJQWA|9c^!GZq%(f{#uyIf6$Tv3MU3MOHb zAy?C2Z-pWJRaEx%l9Y!u-dd21`q zIG~?|km*yfy`Mo(&w@4e6b2qPsJ_`yJe$|SbO`+ChnqgI%G3^Z*HYNLc`T%#R{XMO zoCop_PlV1pkL?gVhl3Bhz8=4z>+P zc5oCb9!$rpUND;jU_AT7Dt_GdsBIa)<=0;m@TVsX<;QI+B_K0=K>WcsZ4XE{ZMt-c z#aPXNhWZJy1j#w16f`&9scYfd+VvW#th4s^DU6~MZGGo2STG$yARJ<3F-R78>eXqa z1X&X(#_g;baFRP0$c2fU8>__x_q}B0({tt|a7xaFmE-U2Ji-|9@S)DIBGxW__0_p^ zaizBfuD(`KZ{A#64)Sy!N`rS8q%s#juULXlusPiCng=NND%-r-=viq(CIB)Kk#<^) zsxL5QDxuX=VH~n9k#hz=1EUm?0$Z2h>8*iz>E-K-q*GUvbb-m@4Q1cw&yTi`%+ctv zDHxg4F*1jv$A)8M`j%H-OZoDfPd33b-}K2hUw^%2%l7YJG<>)HOLPP1WL0&^$sk76 zH@7x-=vB$d7@Zwf3&w~+a%td0XD7mmorek_(|+4#+vagaMRDk!M{V<@+u=xUrx5DX z7%vQKPXK233#&jH1~yiK*B^6HQE{9T zpoov9%fJF!7K69m2Eb4N6t7fJJdJe;V$$it zD4$tZwrqs7!B11Xv+Wy%WHy1Svl_F}`t?++>lND?jQ`bA-G?83xbXpCI&b1bI|*bd z6vBI5T?F(jL?b>X2z|{N+1aPl=?4@Sn=8snOAGA0juT(5-G52sb==BEwlE54)e91$ zk{P6@N|0FElL6_v^M_p*(WzGsoh6n-)~~z3x_t@O|3Yce&F^>V<3*kMjBOd0v@rpQ z9Icr7q-`&}mhLLimqge}Ko43i-fcBZu|3vpSr;wbAoMRtHPJboN48 zetD~af{|>oL7HGg4=fb5mS3xrT{`-d;hQtnsX2wUO6Meuli5xb- zKLAeyhK(BAH+*z#)TFpkNL3gYKW@a(p+ghm`^#kaKY)Y)f%EGxgxkL=EW}&o8Gp3nB;kO*3SsM#!JD6*T~ba(ZpzDuU_qW25=>B}EwA9DrxnTyCLk4EM~#Ed zE@a7oAte{6R6g7=pd?Z-_Nc&38!HFbr?{my<>*yRqUZOup}@@&Tb#J92s~3e9NzrVn#xnqHS125JB?0BsR`;o>CHu~wcWjc+ⅅz?iuH&b8I;Pc4b>A5fi= zVqhMLv6CNq_~D0VO^Hn$5rvvJQ6pm$-;QHcIVwBk?ZjATWVO4Dh^zpEH%F@-G#SS} zHhsjDVf}FIu<1*dB*0n_4`w^~C#OJN83o0lL4$@5=^gB1P`mUBA2K`&iljVzLA)Ky zVCz&1dYjKro;*IZH5Ih0{9czZjGb!t$k2Jcew8l7uMxi;xoJ7sfHl^m7#z>7 zaVEG>&dV(1rI6lbYo@SrA<^#do{mpTT(2Xw&l|87qTm=;zg00XnVoo zqb-Ij4GkA`o8Keq?5vfs}x*hblA z$l#f5$!IVrlySEz-vkF}JQ$3$6ADz2t2Tu8+ z+%S&#Fm?h5p6Ml$YQfeLOQxYTYza_VDhPXzKtKmU7eUOaU|y+XDbm@&%LTor5}?m| zNVwIu#+jh_PHUHx1Os5&1_=}sx84c52!BhvL;%-j7)MBtof(2>dWmFQEW;+2S_7`< zhJ{F|SS-QMcONcblOV-UET!k}2)?0paUx)x+`u@wgmEHZoPe3d z#-YjE2_ANN5*#9W}s^|OvFJnw9jxhstU|<}A0TBAXEAtPN%>N%pnM@pIQ2x8{E6smz zF#L^?#XS{n7zf!0C*U68_}zfGXFUAH*mAmeaAC}#_QRi#kDkHg^!veN7zgnOiQw*l z!1M3M#63geFNT-Xz2ghR=6}5b5uppixQPvxvT3kq{>68*l6ztQNK zs`v#dF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..6d6cd3e --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = manager + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.manager + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..fc0e6e3 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,914 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d + url: "https://pub.dev" + source: hosted + version: "91.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: a40a0cee526a7e1f387c6847bd8a5ccbf510a75952ef8a28338e989558072cb0 + url: "https://pub.dev" + source: hosted + version: "8.4.0" + analyzer_buffer: + dependency: transitive + description: + name: analyzer_buffer + sha256: aba2f75e63b3135fd1efaa8b6abefe1aa6e41b6bd9806221620fa48f98156033 + url: "https://pub.dev" + source: hosted + version: "0.1.11" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: "08cfefa90b4f4dd3b447bda831cecf644029f9f8e22820f6ee310213ebe2dd53" + url: "https://pub.dev" + source: hosted + version: "0.13.10" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: "7174c5d84b0fed00a1f5e7543597b35d67560465ae3d909f0889b8b20419d5e3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: fd754058c342243718d5171a95f352cfc9fcf0cba8cfa26df67cb13a5836db78 + url: "https://pub.dev" + source: hosted + version: "4.1.2" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "82730bf3d9043366ba8c02e4add05842a10739899520a6a22ddbd22d333bd5bb" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "32c6b3d172f1f46b7c4df6bc4a47b8d88afb9e505dd4ace4af80b3c37e89832b" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "4b188774b369104ad96c0e4ca2471e5162f0566ce277771b179bed5eabf2d048" + url: "https://pub.dev" + source: hosted + version: "9.2.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56" + url: "https://pub.dev" + source: hosted + version: "8.12.6" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" + url: "https://pub.dev" + source: hosted + version: "4.11.1" + collection: + dependency: "direct main" + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d" + url: "https://pub.dev" + source: hosted + version: "1.15.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.dev" + source: hosted + version: "1.0.9" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "85b339346154d5646952d44d682965dfe9e12cae5febd706f0db3aa5010d6423" + url: "https://pub.dev" + source: hosted + version: "0.8.1" + custom_lint_visitor: + dependency: transitive + description: + name: custom_lint_visitor + sha256: "91f2a81e9f0abb4b9f3bb529f78b6227ce6050300d1ae5b1e2c69c66c7a566d8" + url: "https://pub.dev" + source: hosted + version: "1.0.0+8.4.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b + url: "https://pub.dev" + source: hosted + version: "3.1.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "0df44ebba85e503958eb75d07eedd3c86275a58c1d3eda2f2ce8f0a2c3abbb3c" + url: "https://pub.dev" + source: hosted + version: "5.11.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "0786d0b7295a373de356fc0af4f6f1d0ab2844ed31b19dfc5e7556b70e24212c" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + fake_async: + dependency: "direct dev" + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "9e2d6907f12cc7d23a846847615941bddee8709bf2bfd274acdf5e80bcf22fde" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" + url: "https://pub.dev" + source: hosted + version: "3.2.3" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 + url: "https://pub.dev" + source: hosted + version: "14.8.1" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" + url: "https://pub.dev" + source: hosted + version: "0.20.3" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 + url: "https://pub.dev" + source: hosted + version: "6.11.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mockito: + dependency: transitive + description: + name: mockito + sha256: eff30d002f0c8bf073b6f929df4483b543133fcafce056870163587b03f1d422 + url: "https://pub.dev" + source: hosted + version: "5.6.4" + mocktail: + dependency: "direct dev" + description: + name: mocktail + sha256: "5e1bf53cc7baa8062a33b84424deb61513858ea05c601b8509e683815b5914aa" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: c406de02bff19d920b832bddfb8283548bfa05ce41c59afba57ce643e116aa59 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: a0f68adb078b790faa3c655110a017f9a7b7b079a57bbd40f540e80dce5fcd29 + url: "https://pub.dev" + source: hosted + version: "1.0.0-dev.7" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: "7230014155777fc31ba3351bc2cb5a3b5717b11bfafe52b1553cb47d385f8897" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + sha256: "49894543a42cf7a9954fc4e7366b6d3cb2e6ec0fa07775f660afcdd92d097702" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 + url: "https://pub.dev" + source: hosted + version: "2.4.23" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + url: "https://pub.dev" + source: hosted + version: "1.3.8" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: transitive + description: + name: test + sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" + url: "https://pub.dev" + source: hosted + version: "1.30.0" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + test_core: + dependency: transitive + description: + name: test_core + sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" + url: "https://pub.dev" + source: hosted + version: "0.6.16" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" + url: "https://pub.dev" + source: hosted + version: "4.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.11.1 <4.0.0" + flutter: ">=3.38.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..da56684 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,100 @@ +name: manager +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.11.1 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + + flutter_riverpod: ^3.0.0 + riverpod_annotation: ^3.0.3 + go_router: ^14.8.1 + dio: ^5.7.0 + freezed_annotation: ^3.0.0 + json_annotation: ^4.9.0 + shared_preferences: ^2.3.4 + intl: ^0.20.2 + collection: ^1.19.1 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^6.0.0 + + build_runner: ^2.4.13 + riverpod_generator: ^3.0.0 + freezed: ^3.0.0 + json_serializable: ^6.11.2 + mocktail: ^1.0.4 + fake_async: ^1.3.1 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + fonts: + # Variable fonts: the same file is listed once per weight so Skia + # renders the correct named instance from the font's wght axis + # instead of synthetically bolding a single static weight. + - family: Inter + fonts: + - asset: assets/fonts/Inter-Variable.ttf + weight: 400 + - asset: assets/fonts/Inter-Variable.ttf + weight: 500 + - asset: assets/fonts/Inter-Variable.ttf + weight: 600 + - asset: assets/fonts/Inter-Variable.ttf + weight: 700 + - asset: assets/fonts/Inter-Variable.ttf + weight: 800 + - family: JetBrains Mono + fonts: + - asset: assets/fonts/JetBrainsMono-Variable.ttf + weight: 400 + - asset: assets/fonts/JetBrainsMono-Variable.ttf + weight: 500 + - asset: assets/fonts/JetBrainsMono-Variable.ttf + weight: 600 diff --git a/test/core/models/service_test.dart b/test/core/models/service_test.dart new file mode 100644 index 0000000..5f01881 --- /dev/null +++ b/test/core/models/service_test.dart @@ -0,0 +1,70 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/models/models.dart'; + +Map _loadFixture(String name) { + final raw = File('test/fixtures/$name').readAsStringSync(); + return jsonDecode(raw) as Map; +} + +void main() { + group('Service.fromJson', () { + test('parses a fully-populated service including nested backup config', () { + final service = Service.fromJson(_loadFixture('service_full.json')); + + expect(service.id, 'svc_123'); + expect(service.name, 'postgres'); + expect(service.composeFile, '/opt/compose/postgres/docker-compose.yml'); + expect(service.composeType, 'docker compose'); + expect(service.backupFolder, '/opt/data/containers/postgres'); + expect(service.status, 'up'); + expect(service.stopOnBackup, isTrue); + expect(service.externalBackupStopMinutes, 5); + expect(service.externalBackupStartMinutes, 2); + + final backup = service.backup; + expect(backup, isNotNull); + expect(backup!.sourcePath, '/opt/data/containers/postgres'); + expect(backup.nextRun, DateTime.parse('2026-07-27T02:00:00Z')); + expect(backup.lastRun, DateTime.parse('2026-07-26T02:00:00Z')); + + expect(backup.targets, hasLength(1)); + final target = backup.targets.single; + expect(target.id, 't1'); + expect(target.method, BackupMethod.restic); + expect(target.remote, 'sftp:nas-cold:/backups/edge-01'); + expect(target.params, {'transfers': '4'}); + expect(target.schedule, '0 2 * * *'); + expect(target.lastRun, DateTime.parse('2026-07-26T02:00:00Z')); + expect(target.nextRun, DateTime.parse('2026-07-27T02:00:00Z')); + + expect(backup.history, hasLength(1)); + final execution = backup.history.single; + expect(execution.success, isTrue); + expect(execution.durationSeconds, 12); + expect(execution.targetId, 't1'); + }); + + test('fills in defaults for every omitted/nullable field', () { + final service = Service.fromJson(_loadFixture('service_minimal.json')); + + expect(service.id, 'svc_456'); + expect(service.name, 'n8n'); + expect(service.status, 'down'); + expect(service.composeType, isNull); + expect(service.backupFolder, isNull); + expect(service.stopOnBackup, isFalse); + expect(service.externalBackupStopMinutes, 0); + expect(service.externalBackupStartMinutes, 0); + expect(service.backup, isNull); + }); + + test('round-trips through toJson/fromJson without losing data', () { + final original = Service.fromJson(_loadFixture('service_full.json')); + final roundTripped = Service.fromJson(original.toJson()); + expect(roundTripped, original); + }); + }); +} diff --git a/test/core/models/severity_mapping_test.dart b/test/core/models/severity_mapping_test.dart new file mode 100644 index 0000000..289cf10 --- /dev/null +++ b/test/core/models/severity_mapping_test.dart @@ -0,0 +1,28 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/models/severity_mapping.dart'; +import 'package:manager/core/theme/status_colors.dart'; + +void main() { + group('severityForServiceStatus', () { + test('maps up/down to good/critical and anything else to warning', () { + expect(severityForServiceStatus('up'), Severity.good); + expect(severityForServiceStatus('down'), Severity.critical); + expect(severityForServiceStatus('unknown'), Severity.warning); + expect(severityForServiceStatus('anything-else'), Severity.warning); + }); + }); + + group('severityForSuccess', () { + test('maps true/false to good/critical', () { + expect(severityForSuccess(true), Severity.good); + expect(severityForSuccess(false), Severity.critical); + }); + }); + + group('severityForNodeReachable', () { + test('maps true/false to good/critical', () { + expect(severityForNodeReachable(true), Severity.good); + expect(severityForNodeReachable(false), Severity.critical); + }); + }); +} diff --git a/test/core/network/dio_error_mapper_test.dart b/test/core/network/dio_error_mapper_test.dart new file mode 100644 index 0000000..9336542 --- /dev/null +++ b/test/core/network/dio_error_mapper_test.dart @@ -0,0 +1,92 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/network/api_exception.dart'; +import 'package:manager/core/network/dio_error_mapper.dart'; + +RequestOptions _options() => RequestOptions(path: '/v1/services/'); + +void main() { + group('mapDioException', () { + test('maps timeout variants to ApiException.timeout', () { + for (final type in [ + DioExceptionType.connectionTimeout, + DioExceptionType.sendTimeout, + DioExceptionType.receiveTimeout, + ]) { + final result = mapDioException(DioException(requestOptions: _options(), type: type)); + expect(result, isA()); + } + }); + + test('maps connectionError to ApiException.network', () { + final result = mapDioException( + DioException( + requestOptions: _options(), + type: DioExceptionType.connectionError, + message: 'Failed host lookup', + ), + ); + expect(result, isA()); + expect((result as ApiNetworkException).message, contains('Failed host lookup')); + }); + + test('maps cancel to ApiException.cancelled', () { + final result = mapDioException( + DioException(requestOptions: _options(), type: DioExceptionType.cancel), + ); + expect(result, isA()); + }); + + test('maps badResponse with a {"error": msg} body to ApiException.server, extracting the message', () { + final response = Response( + requestOptions: _options(), + statusCode: 404, + data: {'error': 'not found'}, + ); + final result = mapDioException( + DioException(requestOptions: _options(), type: DioExceptionType.badResponse, response: response), + ); + expect(result, isA()); + final server = result as ApiServerException; + expect(server.statusCode, 404); + expect(server.message, 'not found'); + }); + + test('maps badResponse without a parseable body to the status message', () { + final response = Response( + requestOptions: _options(), + statusCode: 500, + statusMessage: 'Internal Server Error', + data: null, + ); + final result = mapDioException( + DioException(requestOptions: _options(), type: DioExceptionType.badResponse, response: response), + ); + expect(result, isA()); + expect((result as ApiServerException).message, 'Internal Server Error'); + }); + + test('passes an already-typed ApiException through unchanged', () { + const original = ApiException.timeout(); + expect(mapDioException(original), same(original)); + }); + + test('maps a non-Dio, non-ApiException error to ApiException.unknown', () { + final result = mapDioException(FormatException('bad json')); + expect(result, isA()); + }); + }); + + group('ApiExceptionUserMessage', () { + test('server(404) gets a generic "not found" message regardless of body text', () { + const exception = ApiException.server(404, 'service not found'); + expect(exception.userMessage, 'Not found.'); + }); + + test('server(non-404) includes the status code and body message', () { + const exception = ApiException.server(400, 'no backup targets configured'); + expect(exception.userMessage, contains('400')); + expect(exception.userMessage, contains('no backup targets configured')); + }); + }); +} diff --git a/test/core/widgets/data_table_scaffold_test.dart b/test/core/widgets/data_table_scaffold_test.dart new file mode 100644 index 0000000..28d9da1 --- /dev/null +++ b/test/core/widgets/data_table_scaffold_test.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/widgets/data_table_scaffold.dart'; +import 'package:manager/core/widgets/empty_state.dart'; + +void main() { + const columns = [DataColumn(label: Text('Name')), DataColumn(label: Text('Status'))]; + + testWidgets('renders the given rows', (tester) async { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: Scaffold( + body: DataTableScaffold( + columns: columns, + rows: const [ + DataRow(cells: [DataCell(Text('postgres')), DataCell(Text('up'))]), + DataRow(cells: [DataCell(Text('grafana')), DataCell(Text('up'))]), + ], + ), + ), + ), + ); + + expect(find.text('postgres'), findsOneWidget); + expect(find.text('grafana'), findsOneWidget); + expect(find.byType(DataTable), findsOneWidget); + }); + + testWidgets('shows the empty-state fallback instead of an empty table', (tester) async { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: Scaffold( + body: DataTableScaffold( + columns: columns, + rows: const [], + empty: const EmptyState(icon: Icons.layers_outlined, title: 'No services yet'), + ), + ), + ), + ); + + expect(find.text('No services yet'), findsOneWidget); + expect(find.byType(DataTable), findsNothing); + }); +} diff --git a/test/core/widgets/empty_state_test.dart b/test/core/widgets/empty_state_test.dart new file mode 100644 index 0000000..070f78a --- /dev/null +++ b/test/core/widgets/empty_state_test.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/widgets/empty_state.dart'; + +void main() { + testWidgets('renders title/message and invokes the action callback', (tester) async { + var tapped = false; + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: Scaffold( + body: EmptyState( + icon: Icons.layers_outlined, + title: 'No services yet', + message: 'Scan a compose folder to discover services.', + actionLabel: 'Scan now', + onAction: () => tapped = true, + ), + ), + ), + ); + + expect(find.text('No services yet'), findsOneWidget); + expect(find.text('Scan a compose folder to discover services.'), findsOneWidget); + + await tester.tap(find.widgetWithText(OutlinedButton, 'Scan now')); + await tester.pump(); + expect(tapped, isTrue); + }); + + testWidgets('omits the action button when none is given', (tester) async { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: const Scaffold( + body: EmptyState(icon: Icons.inbox_outlined, title: 'Nothing here'), + ), + ), + ); + + expect(find.byType(OutlinedButton), findsNothing); + }); +} diff --git a/test/core/widgets/error_banner_test.dart b/test/core/widgets/error_banner_test.dart new file mode 100644 index 0000000..ea4edfc --- /dev/null +++ b/test/core/widgets/error_banner_test.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/widgets/error_banner.dart'; + +void main() { + testWidgets('renders the message and invokes onRetry', (tester) async { + var retried = false; + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: Scaffold( + body: ErrorBanner( + message: "Can't reach this node.", + onRetry: () => retried = true, + ), + ), + ), + ); + + expect(find.text("Can't reach this node."), findsOneWidget); + await tester.tap(find.text('Retry')); + await tester.pump(); + expect(retried, isTrue); + }); + + testWidgets('omits the retry button when onRetry is null', (tester) async { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: const Scaffold(body: ErrorBanner(message: 'Something broke.')), + ), + ); + + expect(find.text('Retry'), findsNothing); + }); +} diff --git a/test/core/widgets/mono_text_test.dart b/test/core/widgets/mono_text_test.dart new file mode 100644 index 0000000..a2d393c --- /dev/null +++ b/test/core/widgets/mono_text_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/widgets/mono_text.dart'; + +void main() { + testWidgets('renders in the JetBrains Mono family, small variant uses a smaller size', (tester) async { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: const Scaffold( + body: Column( + children: [ + MonoText('/opt/compose/postgres/docker-compose.yml'), + MonoText('svc_123', small: true), + ], + ), + ), + ), + ); + + final regular = tester.widget(find.text('/opt/compose/postgres/docker-compose.yml')); + final small = tester.widget(find.text('svc_123')); + + expect(regular.style?.fontFamily, 'JetBrains Mono'); + expect(small.style?.fontFamily, 'JetBrains Mono'); + expect(small.style!.fontSize!, lessThan(regular.style!.fontSize!)); + }); +} diff --git a/test/core/widgets/slide_over_panel_test.dart b/test/core/widgets/slide_over_panel_test.dart new file mode 100644 index 0000000..3a87ae7 --- /dev/null +++ b/test/core/widgets/slide_over_panel_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/widgets/slide_over_panel.dart'; + +void main() { + Widget wrap({required bool open, required VoidCallback onClose}) { + return MaterialApp( + theme: AppTheme.light(), + home: Scaffold( + body: Stack( + children: [ + const Center(child: Text('List content')), + SlideOverPanel( + open: open, + onClose: onClose, + title: 'vaultwarden', + body: const Text('Detail body'), + actions: [OutlinedButton(onPressed: () {}, child: const Text('Stop'))], + ), + ], + ), + ), + ); + } + + testWidgets('closed panel ignores taps on its (invisible) close button', (tester) async { + var closed = false; + await tester.pumpWidget(wrap(open: false, onClose: () => closed = true)); + await tester.pumpAndSettle(); + + // IgnorePointer(ignoring: true) means the close icon exists in the tree + // but isn't hit-testable — tapping it must not fire onClose. + await tester.tap(find.byIcon(Icons.close), warnIfMissed: false); + await tester.pump(); + expect(closed, isFalse); + }); + + testWidgets('open panel responds to the close button and the scrim tap', (tester) async { + var closed = false; + await tester.pumpWidget(wrap(open: true, onClose: () => closed = true)); + await tester.pumpAndSettle(); + + expect(find.text('vaultwarden'), findsOneWidget); + expect(find.text('Detail body'), findsOneWidget); + expect(find.text('Stop'), findsOneWidget); + + await tester.tap(find.byIcon(Icons.close)); + expect(closed, isTrue); + }); +} diff --git a/test/core/widgets/sparkline_test.dart b/test/core/widgets/sparkline_test.dart new file mode 100644 index 0000000..53a222e --- /dev/null +++ b/test/core/widgets/sparkline_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/widgets/sparkline.dart'; + +void main() { + testWidgets('paints without error for multiple points, one point, and empty data', (tester) async { + for (final values in [ + [12.4, 16.0, 4.1, 19.8, 9.2], + [12.4], + [], + ]) { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: Scaffold(body: Center(child: Sparkline(values: values))), + ), + ); + expect(tester.takeException(), isNull); + expect(find.byType(CustomPaint), findsWidgets); + } + }); +} diff --git a/test/core/widgets/stat_tile_test.dart b/test/core/widgets/stat_tile_test.dart new file mode 100644 index 0000000..1887915 --- /dev/null +++ b/test/core/widgets/stat_tile_test.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/theme/status_colors.dart'; +import 'package:manager/core/widgets/stat_tile.dart'; + +void main() { + testWidgets('renders label, value, qualifier and caption', (tester) async { + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: const Scaffold( + body: StatTile( + label: 'Services', + value: '7', + valueQualifier: '/ 9 up', + caption: '1 down · 1 unknown', + severity: Severity.critical, + ), + ), + ), + ); + + expect(find.text('Services'), findsOneWidget); + expect(find.textContaining('7'), findsOneWidget); + expect(find.textContaining('/ 9 up'), findsOneWidget); + expect(find.text('1 down · 1 unknown'), findsOneWidget); + }); + + testWidgets('severity stripe color matches the resolved status color', (tester) async { + final key = GlobalKey(); + await tester.pumpWidget( + MaterialApp( + theme: AppTheme.light(), + home: Scaffold( + body: StatTile(key: key, label: 'X', value: '1', severity: Severity.critical), + ), + ), + ); + + final context = key.currentContext!; + final statusColors = Theme.of(context).extension()!; + + final stripe = tester.widgetList(find.byType(Container)).firstWhere( + (c) => c.color == statusColors.critical, + orElse: () => throw StateError('No stripe container with critical color found'), + ); + expect(stripe.color, statusColors.critical); + }); +} diff --git a/test/core/widgets/status_pill_test.dart b/test/core/widgets/status_pill_test.dart new file mode 100644 index 0000000..32ad23c --- /dev/null +++ b/test/core/widgets/status_pill_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/core/theme/app_theme.dart'; +import 'package:manager/core/theme/status_colors.dart'; +import 'package:manager/core/widgets/status_pill.dart'; + +Widget _wrap(Widget child) => MaterialApp(theme: AppTheme.light(), home: Scaffold(body: Center(child: child))); + +void main() { + testWidgets('renders its label', (tester) async { + await tester.pumpWidget(_wrap(const StatusPill(label: 'up', severity: Severity.good))); + expect(find.text('up'), findsOneWidget); + }); + + testWidgets('good/critical/neutral each resolve to a distinct pill color', (tester) async { + Future pillColorFor(Severity severity) async { + await tester.pumpWidget(_wrap(StatusPill(label: severity.name, severity: severity))); + final container = tester.widget( + find.descendant(of: find.byType(StatusPill), matching: find.byType(Container)).first, + ); + final decoration = container.decoration as BoxDecoration; + return decoration.color!; + } + + final good = await pillColorFor(Severity.good); + final critical = await pillColorFor(Severity.critical); + final neutral = await pillColorFor(Severity.neutral); + + expect(good, isNot(equals(critical))); + expect(good, isNot(equals(neutral))); + expect(critical, isNot(equals(neutral))); + }); +} diff --git a/test/fixtures/service_full.json b/test/fixtures/service_full.json new file mode 100644 index 0000000..459faa0 --- /dev/null +++ b/test/fixtures/service_full.json @@ -0,0 +1,38 @@ +{ + "id": "svc_123", + "name": "postgres", + "compose_file": "/opt/compose/postgres/docker-compose.yml", + "compose_type": "docker compose", + "backup_folder": "/opt/data/containers/postgres", + "status": "up", + "stop_on_backup": true, + "external_backup_stop_minutes": 5, + "external_backup_start_minutes": 2, + "backup": { + "source_path": "/opt/data/containers/postgres", + "targets": [ + { + "id": "t1", + "name": "nas-cold", + "method": "restic", + "remote": "sftp:nas-cold:/backups/edge-01", + "params": { "transfers": "4" }, + "schedule": "0 2 * * *", + "last_run": "2026-07-26T02:00:00Z", + "next_run": "2026-07-27T02:00:00Z" + } + ], + "next_run": "2026-07-27T02:00:00Z", + "last_run": "2026-07-26T02:00:00Z", + "history": [ + { + "id": "bex_1", + "timestamp": "2026-07-26T02:00:00Z", + "success": true, + "duration_seconds": 12, + "message": "snapshot 4f2a9c1e created", + "target_id": "t1" + } + ] + } +} diff --git a/test/fixtures/service_minimal.json b/test/fixtures/service_minimal.json new file mode 100644 index 0000000..2ba6310 --- /dev/null +++ b/test/fixtures/service_minimal.json @@ -0,0 +1,6 @@ +{ + "id": "svc_456", + "name": "n8n", + "compose_file": "/opt/compose/n8n/docker-compose.yml", + "status": "down" +} diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..4f3450b --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:manager/app.dart'; +import 'package:manager/core/utils/shared_preferences_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +Future _pumpApp(WidgetTester tester, Map prefsValues) async { + // Wide enough that NavRailShell renders its expanded (non-narrow) rail, + // which is what shows the "NodeMaster" wordmark asserted on below. + tester.view.physicalSize = const Size(1280, 800); + tester.view.devicePixelRatio = 1.0; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + SharedPreferences.setMockInitialValues(prefsValues); + final prefs = await SharedPreferences.getInstance(); + + await tester.pumpWidget( + ProviderScope( + // Matches main.dart: no container-wide auto-retry, so a deliberately + // unreachable test connection settles into AsyncError deterministically + // instead of retrying with backoff mid-test. + retry: (retryCount, error) => null, + overrides: [sharedPreferencesProvider.overrideWithValue(prefs)], + child: const App(), + ), + ); + await tester.pumpAndSettle(); +} + +void main() { + testWidgets('redirects to Settings when no connection is configured', (tester) async { + await _pumpApp(tester, {}); + + expect(find.text('NodeMaster'), findsOneWidget); + // The router redirect lands on Settings — its "Connections" block + // header (unique to that screen) proves we're actually there, not + // just that the nav rail lists the label. + expect(find.text('Connections'), findsOneWidget); + }); + + testWidgets('launches on the Dashboard when a connection is configured', (tester) async { + await _pumpApp(tester, { + // Port 1 refuses immediately rather than hanging until a timeout, + // so the test doesn't depend on real network access or reachability + // — only on routing/shell behavior once a connection exists. + 'connections': '[{"id":"conn_test","name":"Test","baseUrl":"http://127.0.0.1:1"}]', + 'active_connection_id': 'conn_test', + }); + + expect(find.text('NodeMaster'), findsOneWidget); + expect(find.text('Dashboard'), findsWidgets); + expect(find.text('Services'), findsOneWidget); + expect(find.text('Settings'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 zcmdT|`#%%j|KDb2V@0DPm$^(Lx5}lO%Yv(=e*7hl@QqKS50#~#^IQPxBmuh|i9sXnt4ch@VT0F7% zMtrs@KWIOo+QV@lSs66A>2pz6-`9Jk=0vv&u?)^F@HZ)-6HT=B7LF;rdj zskUyBfbojcX#CS>WrIWo9D=DIwcXM8=I5D{SGf$~=gh-$LwY?*)cD%38%sCc?5OsX z-XfkyL-1`VavZ?>(pI-xp-kYq=1hsnyP^TLb%0vKRSo^~r{x?ISLY1i7KjSp z*0h&jG(Rkkq2+G_6eS>n&6>&Xk+ngOMcYrk<8KrukQHzfx675^^s$~<@d$9X{VBbg z2Fd4Z%g`!-P}d#`?B4#S-9x*eNlOVRnDrn#jY@~$jfQ-~3Od;A;x-BI1BEDdvr`pI z#D)d)!2_`GiZOUu1crb!hqH=ezs0qk<_xDm_Kkw?r*?0C3|Io6>$!kyDl;eH=aqg$B zsH_|ZD?jP2dc=)|L>DZmGyYKa06~5?C2Lc0#D%62p(YS;%_DRCB1k(+eLGXVMe+=4 zkKiJ%!N6^mxqM=wq`0+yoE#VHF%R<{mMamR9o_1JH8jfnJ?NPLs$9U!9!dq8 z0B{dI2!M|sYGH&9TAY34OlpIsQ4i5bnbG>?cWwat1I13|r|_inLE?FS@Hxdxn_YZN z3jfUO*X9Q@?HZ>Q{W0z60!bbGh557XIKu1?)u|cf%go`pwo}CD=0tau-}t@R2OrSH zQzZr%JfYa`>2!g??76=GJ$%ECbQh7Q2wLRp9QoyiRHP7VE^>JHm>9EqR3<$Y=Z1K^SHuwxCy-5@z3 zVM{XNNm}yM*pRdLKp??+_2&!bp#`=(Lh1vR{~j%n;cJv~9lXeMv)@}Odta)RnK|6* zC+IVSWumLo%{6bLDpn)Gz>6r&;Qs0^+Sz_yx_KNz9Dlt^ax`4>;EWrIT#(lJ_40<= z750fHZ7hI{}%%5`;lwkI4<_FJw@!U^vW;igL0k+mK)-j zYuCK#mCDK3F|SC}tC2>m$ZCqNB7ac-0UFBJ|8RxmG@4a4qdjvMzzS&h9pQmu^x&*= zGvapd1#K%Da&)8f?<9WN`2H^qpd@{7In6DNM&916TRqtF4;3`R|Nhwbw=(4|^Io@T zIjoR?tB8d*sO>PX4vaIHF|W;WVl6L1JvSmStgnRQq zTX4(>1f^5QOAH{=18Q2Vc1JI{V=yOr7yZJf4Vpfo zeHXdhBe{PyY;)yF;=ycMW@Kb>t;yE>;f79~AlJ8k`xWucCxJfsXf2P72bAavWL1G#W z;o%kdH(mYCM{$~yw4({KatNGim49O2HY6O07$B`*K7}MvgI=4x=SKdKVb8C$eJseA$tmSFOztFd*3W`J`yIB_~}k%Sd_bPBK8LxH)?8#jM{^%J_0|L z!gFI|68)G}ex5`Xh{5pB%GtlJ{Z5em*e0sH+sU1UVl7<5%Bq+YrHWL7?X?3LBi1R@_)F-_OqI1Zv`L zb6^Lq#H^2@d_(Z4E6xA9Z4o3kvf78ZDz!5W1#Mp|E;rvJz&4qj2pXVxKB8Vg0}ek%4erou@QM&2t7Cn5GwYqy%{>jI z)4;3SAgqVi#b{kqX#$Mt6L8NhZYgonb7>+r#BHje)bvaZ2c0nAvrN3gez+dNXaV;A zmyR0z@9h4@6~rJik-=2M-T+d`t&@YWhsoP_XP-NsVO}wmo!nR~QVWU?nVlQjNfgcTzE-PkfIX5G z1?&MwaeuzhF=u)X%Vpg_e@>d2yZwxl6-r3OMqDn8_6m^4z3zG##cK0Fsgq8fcvmhu z{73jseR%X%$85H^jRAcrhd&k!i^xL9FrS7qw2$&gwAS8AfAk#g_E_tP;x66fS`Mn@SNVrcn_N;EQm z`Mt3Z%rw%hDqTH-s~6SrIL$hIPKL5^7ejkLTBr46;pHTQDdoErS(B>``t;+1+M zvU&Se9@T_BeK;A^p|n^krIR+6rH~BjvRIugf`&EuX9u69`9C?9ANVL8l(rY6#mu^i z=*5Q)-%o*tWl`#b8p*ZH0I}hn#gV%|jt6V_JanDGuekR*-wF`u;amTCpGG|1;4A5$ zYbHF{?G1vv5;8Ph5%kEW)t|am2_4ik!`7q{ymfHoe^Z99c|$;FAL+NbxE-_zheYbV z3hb0`uZGTsgA5TG(X|GVDSJyJxsyR7V5PS_WSnYgwc_D60m7u*x4b2D79r5UgtL18 zcCHWk+K6N1Pg2c;0#r-)XpwGX?|Iv)^CLWqwF=a}fXUSM?n6E;cCeW5ER^om#{)Jr zJR81pkK?VoFm@N-s%hd7@hBS0xuCD0-UDVLDDkl7Ck=BAj*^ps`393}AJ+Ruq@fl9 z%R(&?5Nc3lnEKGaYMLmRzKXow1+Gh|O-LG7XiNxkG^uyv zpAtLINwMK}IWK65hOw&O>~EJ}x@lDBtB`yKeV1%GtY4PzT%@~wa1VgZn7QRwc7C)_ zpEF~upeDRg_<#w=dLQ)E?AzXUQpbKXYxkp>;c@aOr6A|dHA?KaZkL0svwB^U#zmx0 zzW4^&G!w7YeRxt<9;d@8H=u(j{6+Uj5AuTluvZZD4b+#+6Rp?(yJ`BC9EW9!b&KdPvzJYe5l7 zMJ9aC@S;sA0{F0XyVY{}FzW0Vh)0mPf_BX82E+CD&)wf2!x@{RO~XBYu80TONl3e+ zA7W$ra6LcDW_j4s-`3tI^VhG*sa5lLc+V6ONf=hO@q4|p`CinYqk1Ko*MbZ6_M05k zSwSwkvu;`|I*_Vl=zPd|dVD0lh&Ha)CSJJvV{AEdF{^Kn_Yfsd!{Pc1GNgw}(^~%)jk5~0L~ms|Rez1fiK~s5t(p1ci5Gq$JC#^JrXf?8 z-Y-Zi_Hvi>oBzV8DSRG!7dm|%IlZg3^0{5~;>)8-+Nk&EhAd(}s^7%MuU}lphNW9Q zT)DPo(ob{tB7_?u;4-qGDo!sh&7gHaJfkh43QwL|bbFVi@+oy;i;M zM&CP^v~lx1U`pi9PmSr&Mc<%HAq0DGH?Ft95)WY`P?~7O z`O^Nr{Py9M#Ls4Y7OM?e%Y*Mvrme%=DwQaye^Qut_1pOMrg^!5u(f9p(D%MR%1K>% zRGw%=dYvw@)o}Fw@tOtPjz`45mfpn;OT&V(;z75J*<$52{sB65$gDjwX3Xa!x_wE- z!#RpwHM#WrO*|~f7z}(}o7US(+0FYLM}6de>gQdtPazXz?OcNv4R^oYLJ_BQOd_l172oSK$6!1r@g+B@0ofJ4*{>_AIxfe-#xp>(1 z@Y3Nfd>fmqvjL;?+DmZk*KsfXJf<%~(gcLwEez%>1c6XSboURUh&k=B)MS>6kw9bY z{7vdev7;A}5fy*ZE23DS{J?8at~xwVk`pEwP5^k?XMQ7u64;KmFJ#POzdG#np~F&H ze-BUh@g54)dsS%nkBb}+GuUEKU~pHcYIg4vSo$J(J|U36bs0Use+3A&IMcR%6@jv$ z=+QI+@wW@?iu}Hpyzlvj-EYeop{f65GX0O%>w#0t|V z1-svWk`hU~m`|O$kw5?Yn5UhI%9P-<45A(v0ld1n+%Ziq&TVpBcV9n}L9Tus-TI)f zd_(g+nYCDR@+wYNQm1GwxhUN4tGMLCzDzPqY$~`l<47{+l<{FZ$L6(>J)|}!bi<)| zE35dl{a2)&leQ@LlDxLQOfUDS`;+ZQ4ozrleQwaR-K|@9T{#hB5Z^t#8 zC-d_G;B4;F#8A2EBL58s$zF-=SCr`P#z zNCTnHF&|X@q>SkAoYu>&s9v@zCpv9lLSH-UZzfhJh`EZA{X#%nqw@@aW^vPcfQrlPs(qQxmC|4tp^&sHy!H!2FH5eC{M@g;ElWNzlb-+ zxpfc0m4<}L){4|RZ>KReag2j%Ot_UKkgpJN!7Y_y3;Ssz{9 z!K3isRtaFtQII5^6}cm9RZd5nTp9psk&u1C(BY`(_tolBwzV_@0F*m%3G%Y?2utyS zY`xM0iDRT)yTyYukFeGQ&W@ReM+ADG1xu@ruq&^GK35`+2r}b^V!m1(VgH|QhIPDE X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..69db1ab --- /dev/null +++ b/web/index.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + manager + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..7cd1d06 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "manager", + "short_name": "manager", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 0000000..90b7800 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(manager LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "manager") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..8b6d468 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..b93c4c3 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 0000000..59df082 --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "manager" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "manager" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "manager.exe" "\0" + VALUE "ProductName", "manager" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 0000000..609d5ea --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"manager", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..153653e --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 0000000..3a0b465 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_

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�`t1>ioQcDTIG6x2X6$P-d@w| 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 literal 0 HcmV?d00001 diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..7cb5117 --- /dev/null +++ b/build.yaml @@ -0,0 +1,6 @@ +targets: + $default: + builders: + json_serializable: + options: + explicit_to_json: true diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..391a902 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..459649a --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,620 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..e3773d4 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..c30b367 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,16 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..1aa6c62 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,70 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Manager + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + manager + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/Runner/SceneDelegate.swift b/ios/Runner/SceneDelegate.swift new file mode 100644 index 0000000..b9ce8ea --- /dev/null +++ b/ios/Runner/SceneDelegate.swift @@ -0,0 +1,6 @@ +import Flutter +import UIKit + +class SceneDelegate: FlutterSceneDelegate { + +} diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 0000000..ed47e7c --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'core/routing/app_router.dart'; +import 'core/theme/app_theme.dart'; +import 'core/theme/theme_mode_provider.dart'; + +class App extends ConsumerWidget { + const App({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final router = ref.watch(appRouterProvider); + final themeMode = ref.watch(themeModeControllerProvider); + + return MaterialApp.router( + title: 'NodeMaster Manager', + debugShowCheckedModeBanner: false, + theme: AppTheme.light(), + darkTheme: AppTheme.dark(), + themeMode: themeMode, + routerConfig: router, + ); + } +} diff --git a/lib/core/connections/connection.dart b/lib/core/connections/connection.dart new file mode 100644 index 0000000..cd05e34 --- /dev/null +++ b/lib/core/connections/connection.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'connection.freezed.dart'; +part 'connection.g.dart'; + +/// A saved NodeMaster API endpoint. Client-side only — has no server-side +/// counterpart. `baseUrl` excludes the `/v1` suffix (added by +/// [NodeMasterApiClient]) and any trailing slash, e.g. +/// `https://edge-01.local:8080`. +@freezed +sealed class Connection with _$Connection { + const factory Connection({ + required String id, + required String name, + required String baseUrl, + }) = _Connection; + + factory Connection.fromJson(Map json) => _$ConnectionFromJson(json); +} diff --git a/lib/core/connections/connection.freezed.dart b/lib/core/connections/connection.freezed.dart new file mode 100644 index 0000000..27f69d1 --- /dev/null +++ b/lib/core/connections/connection.freezed.dart @@ -0,0 +1,277 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'connection.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$Connection { + + String get id; String get name; String get baseUrl; +/// Create a copy of Connection +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ConnectionCopyWith get copyWith => _$ConnectionCopyWithImpl(this as Connection, _$identity); + + /// Serializes this Connection to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Connection&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.baseUrl, baseUrl) || other.baseUrl == baseUrl)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,baseUrl); + +@override +String toString() { + return 'Connection(id: $id, name: $name, baseUrl: $baseUrl)'; +} + + +} + +/// @nodoc +abstract mixin class $ConnectionCopyWith<$Res> { + factory $ConnectionCopyWith(Connection value, $Res Function(Connection) _then) = _$ConnectionCopyWithImpl; +@useResult +$Res call({ + String id, String name, String baseUrl +}); + + + + +} +/// @nodoc +class _$ConnectionCopyWithImpl<$Res> + implements $ConnectionCopyWith<$Res> { + _$ConnectionCopyWithImpl(this._self, this._then); + + final Connection _self; + final $Res Function(Connection) _then; + +/// Create a copy of Connection +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? baseUrl = null,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,baseUrl: null == baseUrl ? _self.baseUrl : baseUrl // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [Connection]. +extension ConnectionPatterns on Connection { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Connection value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Connection() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Connection value) $default,){ +final _that = this; +switch (_that) { +case _Connection(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Connection value)? $default,){ +final _that = this; +switch (_that) { +case _Connection() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, String baseUrl)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Connection() when $default != null: +return $default(_that.id,_that.name,_that.baseUrl);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String name, String baseUrl) $default,) {final _that = this; +switch (_that) { +case _Connection(): +return $default(_that.id,_that.name,_that.baseUrl);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, String baseUrl)? $default,) {final _that = this; +switch (_that) { +case _Connection() when $default != null: +return $default(_that.id,_that.name,_that.baseUrl);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _Connection implements Connection { + const _Connection({required this.id, required this.name, required this.baseUrl}); + factory _Connection.fromJson(Map json) => _$ConnectionFromJson(json); + +@override final String id; +@override final String name; +@override final String baseUrl; + +/// Create a copy of Connection +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ConnectionCopyWith<_Connection> get copyWith => __$ConnectionCopyWithImpl<_Connection>(this, _$identity); + +@override +Map toJson() { + return _$ConnectionToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Connection&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.baseUrl, baseUrl) || other.baseUrl == baseUrl)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,baseUrl); + +@override +String toString() { + return 'Connection(id: $id, name: $name, baseUrl: $baseUrl)'; +} + + +} + +/// @nodoc +abstract mixin class _$ConnectionCopyWith<$Res> implements $ConnectionCopyWith<$Res> { + factory _$ConnectionCopyWith(_Connection value, $Res Function(_Connection) _then) = __$ConnectionCopyWithImpl; +@override @useResult +$Res call({ + String id, String name, String baseUrl +}); + + + + +} +/// @nodoc +class __$ConnectionCopyWithImpl<$Res> + implements _$ConnectionCopyWith<$Res> { + __$ConnectionCopyWithImpl(this._self, this._then); + + final _Connection _self; + final $Res Function(_Connection) _then; + +/// Create a copy of Connection +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? baseUrl = null,}) { + return _then(_Connection( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,baseUrl: null == baseUrl ? _self.baseUrl : baseUrl // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/core/connections/connection.g.dart b/lib/core/connections/connection.g.dart new file mode 100644 index 0000000..d058cd6 --- /dev/null +++ b/lib/core/connections/connection.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'connection.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_Connection _$ConnectionFromJson(Map json) => _Connection( + id: json['id'] as String, + name: json['name'] as String, + baseUrl: json['baseUrl'] as String, +); + +Map _$ConnectionToJson(_Connection instance) => + { + 'id': instance.id, + 'name': instance.name, + 'baseUrl': instance.baseUrl, + }; diff --git a/lib/core/connections/connection_providers.dart b/lib/core/connections/connection_providers.dart new file mode 100644 index 0000000..b3c0fb4 --- /dev/null +++ b/lib/core/connections/connection_providers.dart @@ -0,0 +1,75 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../utils/shared_preferences_provider.dart'; +import 'connection.dart'; +import 'connection_storage.dart'; + +part 'connection_providers.g.dart'; + +@Riverpod(keepAlive: true) +ConnectionStorage connectionStorage(Ref ref) { + return ConnectionStorage(ref.watch(sharedPreferencesProvider)); +} + +/// The full list of saved connections. Mutations persist immediately. +@Riverpod(keepAlive: true) +class SavedConnections extends _$SavedConnections { + @override + List build() => ref.watch(connectionStorageProvider).readConnections(); + + Future add(Connection connection) async { + state = [...state, connection]; + await ref.read(connectionStorageProvider).writeConnections(state); + } + + Future update(Connection connection) async { + state = [ + for (final c in state) + if (c.id == connection.id) connection else c, + ]; + await ref.read(connectionStorageProvider).writeConnections(state); + } + + Future remove(String id) async { + state = state.where((c) => c.id != id).toList(growable: false); + await ref.read(connectionStorageProvider).writeConnections(state); + final activeId = ref.read(activeConnectionIdProvider); + if (activeId == id) { + await ref.read(activeConnectionIdProvider.notifier).setActive( + state.isEmpty ? null : state.first.id, + ); + } + } +} + +/// The id of the currently active connection, or null if none is selected +/// (e.g. first launch with nothing configured yet). +@Riverpod(keepAlive: true) +class ActiveConnectionId extends _$ActiveConnectionId { + @override + String? build() { + final stored = ref.watch(connectionStorageProvider).readActiveConnectionId(); + final connections = ref.watch(savedConnectionsProvider); + if (stored != null && connections.any((c) => c.id == stored)) return stored; + return connections.isEmpty ? null : connections.first.id; + } + + Future setActive(String? id) async { + state = id; + await ref.read(connectionStorageProvider).writeActiveConnectionId(id); + } +} + +/// The currently active [Connection], or null if none is configured. +/// Everything in `core/network` derives from this — switching it rebuilds +/// the API client and, transitively, every provider that watches it. +@riverpod +Connection? activeConnection(Ref ref) { + final id = ref.watch(activeConnectionIdProvider); + if (id == null) return null; + final connections = ref.watch(savedConnectionsProvider); + for (final c in connections) { + if (c.id == id) return c; + } + return null; +} diff --git a/lib/core/connections/connection_providers.g.dart b/lib/core/connections/connection_providers.g.dart new file mode 100644 index 0000000..2a6364b --- /dev/null +++ b/lib/core/connections/connection_providers.g.dart @@ -0,0 +1,232 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'connection_providers.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(connectionStorage) +const connectionStorageProvider = ConnectionStorageProvider._(); + +final class ConnectionStorageProvider + extends + $FunctionalProvider< + ConnectionStorage, + ConnectionStorage, + ConnectionStorage + > + with $Provider { + const ConnectionStorageProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'connectionStorageProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$connectionStorageHash(); + + @$internal + @override + $ProviderElement $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + ConnectionStorage create(Ref ref) { + return connectionStorage(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(ConnectionStorage value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$connectionStorageHash() => r'0faf909e3978260785f2f8ce4bc41f101e27ad66'; + +/// The full list of saved connections. Mutations persist immediately. + +@ProviderFor(SavedConnections) +const savedConnectionsProvider = SavedConnectionsProvider._(); + +/// The full list of saved connections. Mutations persist immediately. +final class SavedConnectionsProvider + extends $NotifierProvider> { + /// The full list of saved connections. Mutations persist immediately. + const SavedConnectionsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'savedConnectionsProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$savedConnectionsHash(); + + @$internal + @override + SavedConnections create() => SavedConnections(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>(value), + ); + } +} + +String _$savedConnectionsHash() => r'2595280b9ef1f5835ecce70b791a901a51aaf8e8'; + +/// The full list of saved connections. Mutations persist immediately. + +abstract class _$SavedConnections extends $Notifier> { + List build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref, List>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, List>, + List, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} + +/// The id of the currently active connection, or null if none is selected +/// (e.g. first launch with nothing configured yet). + +@ProviderFor(ActiveConnectionId) +const activeConnectionIdProvider = ActiveConnectionIdProvider._(); + +/// The id of the currently active connection, or null if none is selected +/// (e.g. first launch with nothing configured yet). +final class ActiveConnectionIdProvider + extends $NotifierProvider { + /// The id of the currently active connection, or null if none is selected + /// (e.g. first launch with nothing configured yet). + const ActiveConnectionIdProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'activeConnectionIdProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$activeConnectionIdHash(); + + @$internal + @override + ActiveConnectionId create() => ActiveConnectionId(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$activeConnectionIdHash() => + r'ac8b7d48bc04e6f9245b23c83a15e11a539e1fc8'; + +/// The id of the currently active connection, or null if none is selected +/// (e.g. first launch with nothing configured yet). + +abstract class _$ActiveConnectionId extends $Notifier { + String? build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + String?, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} + +/// The currently active [Connection], or null if none is configured. +/// Everything in `core/network` derives from this — switching it rebuilds +/// the API client and, transitively, every provider that watches it. + +@ProviderFor(activeConnection) +const activeConnectionProvider = ActiveConnectionProvider._(); + +/// The currently active [Connection], or null if none is configured. +/// Everything in `core/network` derives from this — switching it rebuilds +/// the API client and, transitively, every provider that watches it. + +final class ActiveConnectionProvider + extends $FunctionalProvider + with $Provider { + /// The currently active [Connection], or null if none is configured. + /// Everything in `core/network` derives from this — switching it rebuilds + /// the API client and, transitively, every provider that watches it. + const ActiveConnectionProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'activeConnectionProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$activeConnectionHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + Connection? create(Ref ref) { + return activeConnection(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Connection? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$activeConnectionHash() => r'544cf6f795b35636cbfc497e6379a84b1e8f5b88'; diff --git a/lib/core/connections/connection_storage.dart b/lib/core/connections/connection_storage.dart new file mode 100644 index 0000000..41b47e7 --- /dev/null +++ b/lib/core/connections/connection_storage.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; + +import 'connection.dart'; + +const _connectionsKey = 'connections'; +const _activeConnectionIdKey = 'active_connection_id'; + +/// Reads/writes the saved connection list and active-connection id to +/// [SharedPreferences] as a plain JSON-encoded array. Not a secret store — +/// the API has no auth, so there's nothing sensitive in a saved base URL. +class ConnectionStorage { + const ConnectionStorage(this._prefs); + + final SharedPreferences _prefs; + + List readConnections() { + final raw = _prefs.getString(_connectionsKey); + if (raw == null || raw.isEmpty) return const []; + final decoded = jsonDecode(raw) as List; + return decoded + .map((e) => Connection.fromJson(e as Map)) + .toList(growable: false); + } + + Future writeConnections(List connections) async { + final encoded = jsonEncode(connections.map((c) => c.toJson()).toList()); + await _prefs.setString(_connectionsKey, encoded); + } + + String? readActiveConnectionId() => _prefs.getString(_activeConnectionIdKey); + + Future writeActiveConnectionId(String? id) async { + if (id == null) { + await _prefs.remove(_activeConnectionIdKey); + } else { + await _prefs.setString(_activeConnectionIdKey, id); + } + } +} diff --git a/lib/core/models/aggregated_node_status.dart b/lib/core/models/aggregated_node_status.dart new file mode 100644 index 0000000..cb8ec36 --- /dev/null +++ b/lib/core/models/aggregated_node_status.dart @@ -0,0 +1,25 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'remote_node.dart'; +import 'service.dart'; + +part 'aggregated_node_status.freezed.dart'; +part 'aggregated_node_status.g.dart'; + +/// Mirrors the anonymous `nodeServices` struct returned by +/// `GET /nodes/aggregated`: `{node, services?, error?}`. `error` is the +/// *local* API reporting that fetching *this remote* failed (e.g. +/// unreachable) — a per-entry condition, distinct from the outer call +/// itself failing. Render `error` inline per-card; never conflate it with +/// an [ApiException] on the aggregated fetch as a whole. +@freezed +sealed class AggregatedNodeStatus with _$AggregatedNodeStatus { + const factory AggregatedNodeStatus({ + required RemoteNode node, + @Default([]) List services, + String? error, + }) = _AggregatedNodeStatus; + + factory AggregatedNodeStatus.fromJson(Map json) => + _$AggregatedNodeStatusFromJson(json); +} diff --git a/lib/core/models/aggregated_node_status.freezed.dart b/lib/core/models/aggregated_node_status.freezed.dart new file mode 100644 index 0000000..f5feba3 --- /dev/null +++ b/lib/core/models/aggregated_node_status.freezed.dart @@ -0,0 +1,301 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'aggregated_node_status.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$AggregatedNodeStatus { + + RemoteNode get node; List get services; String? get error; +/// Create a copy of AggregatedNodeStatus +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AggregatedNodeStatusCopyWith get copyWith => _$AggregatedNodeStatusCopyWithImpl(this as AggregatedNodeStatus, _$identity); + + /// Serializes this AggregatedNodeStatus to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AggregatedNodeStatus&&(identical(other.node, node) || other.node == node)&&const DeepCollectionEquality().equals(other.services, services)&&(identical(other.error, error) || other.error == error)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,node,const DeepCollectionEquality().hash(services),error); + +@override +String toString() { + return 'AggregatedNodeStatus(node: $node, services: $services, error: $error)'; +} + + +} + +/// @nodoc +abstract mixin class $AggregatedNodeStatusCopyWith<$Res> { + factory $AggregatedNodeStatusCopyWith(AggregatedNodeStatus value, $Res Function(AggregatedNodeStatus) _then) = _$AggregatedNodeStatusCopyWithImpl; +@useResult +$Res call({ + RemoteNode node, List services, String? error +}); + + +$RemoteNodeCopyWith<$Res> get node; + +} +/// @nodoc +class _$AggregatedNodeStatusCopyWithImpl<$Res> + implements $AggregatedNodeStatusCopyWith<$Res> { + _$AggregatedNodeStatusCopyWithImpl(this._self, this._then); + + final AggregatedNodeStatus _self; + final $Res Function(AggregatedNodeStatus) _then; + +/// Create a copy of AggregatedNodeStatus +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? node = null,Object? services = null,Object? error = freezed,}) { + return _then(_self.copyWith( +node: null == node ? _self.node : node // ignore: cast_nullable_to_non_nullable +as RemoteNode,services: null == services ? _self.services : services // ignore: cast_nullable_to_non_nullable +as List,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as String?, + )); +} +/// Create a copy of AggregatedNodeStatus +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$RemoteNodeCopyWith<$Res> get node { + + return $RemoteNodeCopyWith<$Res>(_self.node, (value) { + return _then(_self.copyWith(node: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [AggregatedNodeStatus]. +extension AggregatedNodeStatusPatterns on AggregatedNodeStatus { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AggregatedNodeStatus value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AggregatedNodeStatus() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AggregatedNodeStatus value) $default,){ +final _that = this; +switch (_that) { +case _AggregatedNodeStatus(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AggregatedNodeStatus value)? $default,){ +final _that = this; +switch (_that) { +case _AggregatedNodeStatus() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( RemoteNode node, List services, String? error)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AggregatedNodeStatus() when $default != null: +return $default(_that.node,_that.services,_that.error);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( RemoteNode node, List services, String? error) $default,) {final _that = this; +switch (_that) { +case _AggregatedNodeStatus(): +return $default(_that.node,_that.services,_that.error);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( RemoteNode node, List services, String? error)? $default,) {final _that = this; +switch (_that) { +case _AggregatedNodeStatus() when $default != null: +return $default(_that.node,_that.services,_that.error);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _AggregatedNodeStatus implements AggregatedNodeStatus { + const _AggregatedNodeStatus({required this.node, final List services = const [], this.error}): _services = services; + factory _AggregatedNodeStatus.fromJson(Map json) => _$AggregatedNodeStatusFromJson(json); + +@override final RemoteNode node; + final List _services; +@override@JsonKey() List get services { + if (_services is EqualUnmodifiableListView) return _services; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_services); +} + +@override final String? error; + +/// Create a copy of AggregatedNodeStatus +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AggregatedNodeStatusCopyWith<_AggregatedNodeStatus> get copyWith => __$AggregatedNodeStatusCopyWithImpl<_AggregatedNodeStatus>(this, _$identity); + +@override +Map toJson() { + return _$AggregatedNodeStatusToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AggregatedNodeStatus&&(identical(other.node, node) || other.node == node)&&const DeepCollectionEquality().equals(other._services, _services)&&(identical(other.error, error) || other.error == error)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,node,const DeepCollectionEquality().hash(_services),error); + +@override +String toString() { + return 'AggregatedNodeStatus(node: $node, services: $services, error: $error)'; +} + + +} + +/// @nodoc +abstract mixin class _$AggregatedNodeStatusCopyWith<$Res> implements $AggregatedNodeStatusCopyWith<$Res> { + factory _$AggregatedNodeStatusCopyWith(_AggregatedNodeStatus value, $Res Function(_AggregatedNodeStatus) _then) = __$AggregatedNodeStatusCopyWithImpl; +@override @useResult +$Res call({ + RemoteNode node, List services, String? error +}); + + +@override $RemoteNodeCopyWith<$Res> get node; + +} +/// @nodoc +class __$AggregatedNodeStatusCopyWithImpl<$Res> + implements _$AggregatedNodeStatusCopyWith<$Res> { + __$AggregatedNodeStatusCopyWithImpl(this._self, this._then); + + final _AggregatedNodeStatus _self; + final $Res Function(_AggregatedNodeStatus) _then; + +/// Create a copy of AggregatedNodeStatus +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? node = null,Object? services = null,Object? error = freezed,}) { + return _then(_AggregatedNodeStatus( +node: null == node ? _self.node : node // ignore: cast_nullable_to_non_nullable +as RemoteNode,services: null == services ? _self._services : services // ignore: cast_nullable_to_non_nullable +as List,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +/// Create a copy of AggregatedNodeStatus +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$RemoteNodeCopyWith<$Res> get node { + + return $RemoteNodeCopyWith<$Res>(_self.node, (value) { + return _then(_self.copyWith(node: value)); + }); +} +} + +// dart format on diff --git a/lib/core/models/aggregated_node_status.g.dart b/lib/core/models/aggregated_node_status.g.dart new file mode 100644 index 0000000..e6ca9c1 --- /dev/null +++ b/lib/core/models/aggregated_node_status.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'aggregated_node_status.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_AggregatedNodeStatus _$AggregatedNodeStatusFromJson( + Map json, +) => _AggregatedNodeStatus( + node: RemoteNode.fromJson(json['node'] as Map), + services: + (json['services'] as List?) + ?.map((e) => Service.fromJson(e as Map)) + .toList() ?? + const [], + error: json['error'] as String?, +); + +Map _$AggregatedNodeStatusToJson( + _AggregatedNodeStatus instance, +) => { + 'node': instance.node.toJson(), + 'services': instance.services.map((e) => e.toJson()).toList(), + 'error': instance.error, +}; diff --git a/lib/core/models/backup_config.dart b/lib/core/models/backup_config.dart new file mode 100644 index 0000000..507be3c --- /dev/null +++ b/lib/core/models/backup_config.dart @@ -0,0 +1,25 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'backup_execution.dart'; +import 'backup_target.dart'; + +part 'backup_config.freezed.dart'; +part 'backup_config.g.dart'; + +/// Mirrors `models.BackupConfig`. Used both at node level (`GET /node/backup`) +/// and per-service (`Service.backup`). `nextRun`/`lastRun` here are +/// aggregates (earliest/latest) across `targets` — each target now tracks +/// its own `schedule`/`lastRun`/`nextRun` too. There's no `frequency` field +/// on the Go side anymore; scheduling moved to a per-target cron `schedule`. +@freezed +sealed class BackupConfig with _$BackupConfig { + const factory BackupConfig({ + @JsonKey(name: 'source_path') String? sourcePath, + @Default([]) List targets, + @JsonKey(name: 'next_run') DateTime? nextRun, + @JsonKey(name: 'last_run') DateTime? lastRun, + @Default([]) List history, + }) = _BackupConfig; + + factory BackupConfig.fromJson(Map json) => _$BackupConfigFromJson(json); +} diff --git a/lib/core/models/backup_config.freezed.dart b/lib/core/models/backup_config.freezed.dart new file mode 100644 index 0000000..b0f4349 --- /dev/null +++ b/lib/core/models/backup_config.freezed.dart @@ -0,0 +1,295 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'backup_config.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$BackupConfig { + +@JsonKey(name: 'source_path') String? get sourcePath; List get targets;@JsonKey(name: 'next_run') DateTime? get nextRun;@JsonKey(name: 'last_run') DateTime? get lastRun; List get history; +/// Create a copy of BackupConfig +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$BackupConfigCopyWith get copyWith => _$BackupConfigCopyWithImpl(this as BackupConfig, _$identity); + + /// Serializes this BackupConfig to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is BackupConfig&&(identical(other.sourcePath, sourcePath) || other.sourcePath == sourcePath)&&const DeepCollectionEquality().equals(other.targets, targets)&&(identical(other.nextRun, nextRun) || other.nextRun == nextRun)&&(identical(other.lastRun, lastRun) || other.lastRun == lastRun)&&const DeepCollectionEquality().equals(other.history, history)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,sourcePath,const DeepCollectionEquality().hash(targets),nextRun,lastRun,const DeepCollectionEquality().hash(history)); + +@override +String toString() { + return 'BackupConfig(sourcePath: $sourcePath, targets: $targets, nextRun: $nextRun, lastRun: $lastRun, history: $history)'; +} + + +} + +/// @nodoc +abstract mixin class $BackupConfigCopyWith<$Res> { + factory $BackupConfigCopyWith(BackupConfig value, $Res Function(BackupConfig) _then) = _$BackupConfigCopyWithImpl; +@useResult +$Res call({ +@JsonKey(name: 'source_path') String? sourcePath, List targets,@JsonKey(name: 'next_run') DateTime? nextRun,@JsonKey(name: 'last_run') DateTime? lastRun, List history +}); + + + + +} +/// @nodoc +class _$BackupConfigCopyWithImpl<$Res> + implements $BackupConfigCopyWith<$Res> { + _$BackupConfigCopyWithImpl(this._self, this._then); + + final BackupConfig _self; + final $Res Function(BackupConfig) _then; + +/// Create a copy of BackupConfig +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? sourcePath = freezed,Object? targets = null,Object? nextRun = freezed,Object? lastRun = freezed,Object? history = null,}) { + return _then(_self.copyWith( +sourcePath: freezed == sourcePath ? _self.sourcePath : sourcePath // ignore: cast_nullable_to_non_nullable +as String?,targets: null == targets ? _self.targets : targets // ignore: cast_nullable_to_non_nullable +as List,nextRun: freezed == nextRun ? _self.nextRun : nextRun // ignore: cast_nullable_to_non_nullable +as DateTime?,lastRun: freezed == lastRun ? _self.lastRun : lastRun // ignore: cast_nullable_to_non_nullable +as DateTime?,history: null == history ? _self.history : history // ignore: cast_nullable_to_non_nullable +as List, + )); +} + +} + + +/// Adds pattern-matching-related methods to [BackupConfig]. +extension BackupConfigPatterns on BackupConfig { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _BackupConfig value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _BackupConfig() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _BackupConfig value) $default,){ +final _that = this; +switch (_that) { +case _BackupConfig(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _BackupConfig value)? $default,){ +final _that = this; +switch (_that) { +case _BackupConfig() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function(@JsonKey(name: 'source_path') String? sourcePath, List targets, @JsonKey(name: 'next_run') DateTime? nextRun, @JsonKey(name: 'last_run') DateTime? lastRun, List history)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _BackupConfig() when $default != null: +return $default(_that.sourcePath,_that.targets,_that.nextRun,_that.lastRun,_that.history);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function(@JsonKey(name: 'source_path') String? sourcePath, List targets, @JsonKey(name: 'next_run') DateTime? nextRun, @JsonKey(name: 'last_run') DateTime? lastRun, List history) $default,) {final _that = this; +switch (_that) { +case _BackupConfig(): +return $default(_that.sourcePath,_that.targets,_that.nextRun,_that.lastRun,_that.history);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function(@JsonKey(name: 'source_path') String? sourcePath, List targets, @JsonKey(name: 'next_run') DateTime? nextRun, @JsonKey(name: 'last_run') DateTime? lastRun, List history)? $default,) {final _that = this; +switch (_that) { +case _BackupConfig() when $default != null: +return $default(_that.sourcePath,_that.targets,_that.nextRun,_that.lastRun,_that.history);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _BackupConfig implements BackupConfig { + const _BackupConfig({@JsonKey(name: 'source_path') this.sourcePath, final List targets = const [], @JsonKey(name: 'next_run') this.nextRun, @JsonKey(name: 'last_run') this.lastRun, final List history = const []}): _targets = targets,_history = history; + factory _BackupConfig.fromJson(Map json) => _$BackupConfigFromJson(json); + +@override@JsonKey(name: 'source_path') final String? sourcePath; + final List _targets; +@override@JsonKey() List get targets { + if (_targets is EqualUnmodifiableListView) return _targets; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_targets); +} + +@override@JsonKey(name: 'next_run') final DateTime? nextRun; +@override@JsonKey(name: 'last_run') final DateTime? lastRun; + final List _history; +@override@JsonKey() List get history { + if (_history is EqualUnmodifiableListView) return _history; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_history); +} + + +/// Create a copy of BackupConfig +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$BackupConfigCopyWith<_BackupConfig> get copyWith => __$BackupConfigCopyWithImpl<_BackupConfig>(this, _$identity); + +@override +Map toJson() { + return _$BackupConfigToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _BackupConfig&&(identical(other.sourcePath, sourcePath) || other.sourcePath == sourcePath)&&const DeepCollectionEquality().equals(other._targets, _targets)&&(identical(other.nextRun, nextRun) || other.nextRun == nextRun)&&(identical(other.lastRun, lastRun) || other.lastRun == lastRun)&&const DeepCollectionEquality().equals(other._history, _history)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,sourcePath,const DeepCollectionEquality().hash(_targets),nextRun,lastRun,const DeepCollectionEquality().hash(_history)); + +@override +String toString() { + return 'BackupConfig(sourcePath: $sourcePath, targets: $targets, nextRun: $nextRun, lastRun: $lastRun, history: $history)'; +} + + +} + +/// @nodoc +abstract mixin class _$BackupConfigCopyWith<$Res> implements $BackupConfigCopyWith<$Res> { + factory _$BackupConfigCopyWith(_BackupConfig value, $Res Function(_BackupConfig) _then) = __$BackupConfigCopyWithImpl; +@override @useResult +$Res call({ +@JsonKey(name: 'source_path') String? sourcePath, List targets,@JsonKey(name: 'next_run') DateTime? nextRun,@JsonKey(name: 'last_run') DateTime? lastRun, List history +}); + + + + +} +/// @nodoc +class __$BackupConfigCopyWithImpl<$Res> + implements _$BackupConfigCopyWith<$Res> { + __$BackupConfigCopyWithImpl(this._self, this._then); + + final _BackupConfig _self; + final $Res Function(_BackupConfig) _then; + +/// Create a copy of BackupConfig +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? sourcePath = freezed,Object? targets = null,Object? nextRun = freezed,Object? lastRun = freezed,Object? history = null,}) { + return _then(_BackupConfig( +sourcePath: freezed == sourcePath ? _self.sourcePath : sourcePath // ignore: cast_nullable_to_non_nullable +as String?,targets: null == targets ? _self._targets : targets // ignore: cast_nullable_to_non_nullable +as List,nextRun: freezed == nextRun ? _self.nextRun : nextRun // ignore: cast_nullable_to_non_nullable +as DateTime?,lastRun: freezed == lastRun ? _self.lastRun : lastRun // ignore: cast_nullable_to_non_nullable +as DateTime?,history: null == history ? _self._history : history // ignore: cast_nullable_to_non_nullable +as List, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/backup_config.g.dart b/lib/core/models/backup_config.g.dart new file mode 100644 index 0000000..15c1a68 --- /dev/null +++ b/lib/core/models/backup_config.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'backup_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_BackupConfig _$BackupConfigFromJson(Map json) => + _BackupConfig( + sourcePath: json['source_path'] as String?, + targets: + (json['targets'] as List?) + ?.map((e) => BackupTarget.fromJson(e as Map)) + .toList() ?? + const [], + nextRun: json['next_run'] == null + ? null + : DateTime.parse(json['next_run'] as String), + lastRun: json['last_run'] == null + ? null + : DateTime.parse(json['last_run'] as String), + history: + (json['history'] as List?) + ?.map((e) => BackupExecution.fromJson(e as Map)) + .toList() ?? + const [], + ); + +Map _$BackupConfigToJson(_BackupConfig instance) => + { + 'source_path': instance.sourcePath, + 'targets': instance.targets.map((e) => e.toJson()).toList(), + 'next_run': instance.nextRun?.toIso8601String(), + 'last_run': instance.lastRun?.toIso8601String(), + 'history': instance.history.map((e) => e.toJson()).toList(), + }; diff --git a/lib/core/models/backup_execution.dart b/lib/core/models/backup_execution.dart new file mode 100644 index 0000000..ef50cec --- /dev/null +++ b/lib/core/models/backup_execution.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'backup_execution.freezed.dart'; +part 'backup_execution.g.dart'; + +/// Mirrors `models.BackupExecution`. +@freezed +sealed class BackupExecution with _$BackupExecution { + const factory BackupExecution({ + required String id, + required DateTime timestamp, + required bool success, + @JsonKey(name: 'duration_seconds') required int durationSeconds, + @Default('') String message, + @JsonKey(name: 'target_id') String? targetId, + }) = _BackupExecution; + + factory BackupExecution.fromJson(Map json) => _$BackupExecutionFromJson(json); +} diff --git a/lib/core/models/backup_execution.freezed.dart b/lib/core/models/backup_execution.freezed.dart new file mode 100644 index 0000000..3440c6c --- /dev/null +++ b/lib/core/models/backup_execution.freezed.dart @@ -0,0 +1,286 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'backup_execution.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$BackupExecution { + + String get id; DateTime get timestamp; bool get success;@JsonKey(name: 'duration_seconds') int get durationSeconds; String get message;@JsonKey(name: 'target_id') String? get targetId; +/// Create a copy of BackupExecution +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$BackupExecutionCopyWith get copyWith => _$BackupExecutionCopyWithImpl(this as BackupExecution, _$identity); + + /// Serializes this BackupExecution to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is BackupExecution&&(identical(other.id, id) || other.id == id)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.success, success) || other.success == success)&&(identical(other.durationSeconds, durationSeconds) || other.durationSeconds == durationSeconds)&&(identical(other.message, message) || other.message == message)&&(identical(other.targetId, targetId) || other.targetId == targetId)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,timestamp,success,durationSeconds,message,targetId); + +@override +String toString() { + return 'BackupExecution(id: $id, timestamp: $timestamp, success: $success, durationSeconds: $durationSeconds, message: $message, targetId: $targetId)'; +} + + +} + +/// @nodoc +abstract mixin class $BackupExecutionCopyWith<$Res> { + factory $BackupExecutionCopyWith(BackupExecution value, $Res Function(BackupExecution) _then) = _$BackupExecutionCopyWithImpl; +@useResult +$Res call({ + String id, DateTime timestamp, bool success,@JsonKey(name: 'duration_seconds') int durationSeconds, String message,@JsonKey(name: 'target_id') String? targetId +}); + + + + +} +/// @nodoc +class _$BackupExecutionCopyWithImpl<$Res> + implements $BackupExecutionCopyWith<$Res> { + _$BackupExecutionCopyWithImpl(this._self, this._then); + + final BackupExecution _self; + final $Res Function(BackupExecution) _then; + +/// Create a copy of BackupExecution +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? timestamp = null,Object? success = null,Object? durationSeconds = null,Object? message = null,Object? targetId = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,timestamp: null == timestamp ? _self.timestamp : timestamp // ignore: cast_nullable_to_non_nullable +as DateTime,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool,durationSeconds: null == durationSeconds ? _self.durationSeconds : durationSeconds // ignore: cast_nullable_to_non_nullable +as int,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String,targetId: freezed == targetId ? _self.targetId : targetId // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [BackupExecution]. +extension BackupExecutionPatterns on BackupExecution { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _BackupExecution value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _BackupExecution() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _BackupExecution value) $default,){ +final _that = this; +switch (_that) { +case _BackupExecution(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _BackupExecution value)? $default,){ +final _that = this; +switch (_that) { +case _BackupExecution() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, DateTime timestamp, bool success, @JsonKey(name: 'duration_seconds') int durationSeconds, String message, @JsonKey(name: 'target_id') String? targetId)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _BackupExecution() when $default != null: +return $default(_that.id,_that.timestamp,_that.success,_that.durationSeconds,_that.message,_that.targetId);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, DateTime timestamp, bool success, @JsonKey(name: 'duration_seconds') int durationSeconds, String message, @JsonKey(name: 'target_id') String? targetId) $default,) {final _that = this; +switch (_that) { +case _BackupExecution(): +return $default(_that.id,_that.timestamp,_that.success,_that.durationSeconds,_that.message,_that.targetId);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, DateTime timestamp, bool success, @JsonKey(name: 'duration_seconds') int durationSeconds, String message, @JsonKey(name: 'target_id') String? targetId)? $default,) {final _that = this; +switch (_that) { +case _BackupExecution() when $default != null: +return $default(_that.id,_that.timestamp,_that.success,_that.durationSeconds,_that.message,_that.targetId);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _BackupExecution implements BackupExecution { + const _BackupExecution({required this.id, required this.timestamp, required this.success, @JsonKey(name: 'duration_seconds') required this.durationSeconds, this.message = '', @JsonKey(name: 'target_id') this.targetId}); + factory _BackupExecution.fromJson(Map json) => _$BackupExecutionFromJson(json); + +@override final String id; +@override final DateTime timestamp; +@override final bool success; +@override@JsonKey(name: 'duration_seconds') final int durationSeconds; +@override@JsonKey() final String message; +@override@JsonKey(name: 'target_id') final String? targetId; + +/// Create a copy of BackupExecution +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$BackupExecutionCopyWith<_BackupExecution> get copyWith => __$BackupExecutionCopyWithImpl<_BackupExecution>(this, _$identity); + +@override +Map toJson() { + return _$BackupExecutionToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _BackupExecution&&(identical(other.id, id) || other.id == id)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.success, success) || other.success == success)&&(identical(other.durationSeconds, durationSeconds) || other.durationSeconds == durationSeconds)&&(identical(other.message, message) || other.message == message)&&(identical(other.targetId, targetId) || other.targetId == targetId)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,timestamp,success,durationSeconds,message,targetId); + +@override +String toString() { + return 'BackupExecution(id: $id, timestamp: $timestamp, success: $success, durationSeconds: $durationSeconds, message: $message, targetId: $targetId)'; +} + + +} + +/// @nodoc +abstract mixin class _$BackupExecutionCopyWith<$Res> implements $BackupExecutionCopyWith<$Res> { + factory _$BackupExecutionCopyWith(_BackupExecution value, $Res Function(_BackupExecution) _then) = __$BackupExecutionCopyWithImpl; +@override @useResult +$Res call({ + String id, DateTime timestamp, bool success,@JsonKey(name: 'duration_seconds') int durationSeconds, String message,@JsonKey(name: 'target_id') String? targetId +}); + + + + +} +/// @nodoc +class __$BackupExecutionCopyWithImpl<$Res> + implements _$BackupExecutionCopyWith<$Res> { + __$BackupExecutionCopyWithImpl(this._self, this._then); + + final _BackupExecution _self; + final $Res Function(_BackupExecution) _then; + +/// Create a copy of BackupExecution +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? timestamp = null,Object? success = null,Object? durationSeconds = null,Object? message = null,Object? targetId = freezed,}) { + return _then(_BackupExecution( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,timestamp: null == timestamp ? _self.timestamp : timestamp // ignore: cast_nullable_to_non_nullable +as DateTime,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool,durationSeconds: null == durationSeconds ? _self.durationSeconds : durationSeconds // ignore: cast_nullable_to_non_nullable +as int,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String,targetId: freezed == targetId ? _self.targetId : targetId // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/backup_execution.g.dart b/lib/core/models/backup_execution.g.dart new file mode 100644 index 0000000..e99cb13 --- /dev/null +++ b/lib/core/models/backup_execution.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'backup_execution.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_BackupExecution _$BackupExecutionFromJson(Map json) => + _BackupExecution( + id: json['id'] as String, + timestamp: DateTime.parse(json['timestamp'] as String), + success: json['success'] as bool, + durationSeconds: (json['duration_seconds'] as num).toInt(), + message: json['message'] as String? ?? '', + targetId: json['target_id'] as String?, + ); + +Map _$BackupExecutionToJson(_BackupExecution instance) => + { + 'id': instance.id, + 'timestamp': instance.timestamp.toIso8601String(), + 'success': instance.success, + 'duration_seconds': instance.durationSeconds, + 'message': instance.message, + 'target_id': instance.targetId, + }; diff --git a/lib/core/models/backup_method.dart b/lib/core/models/backup_method.dart new file mode 100644 index 0000000..1df051a --- /dev/null +++ b/lib/core/models/backup_method.dart @@ -0,0 +1,11 @@ +import 'package:json_annotation/json_annotation.dart'; + +/// Mirrors `models.BackupMethod` in `nodemaster/models/types.go`. +enum BackupMethod { + @JsonValue('rsync') + rsync, + @JsonValue('restic') + restic, + @JsonValue('external') + external, +} diff --git a/lib/core/models/backup_target.dart b/lib/core/models/backup_target.dart new file mode 100644 index 0000000..d5c850c --- /dev/null +++ b/lib/core/models/backup_target.dart @@ -0,0 +1,24 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'backup_method.dart'; + +part 'backup_target.freezed.dart'; +part 'backup_target.g.dart'; + +/// Mirrors `models.BackupTarget`. `schedule` is a standard 5-field cron +/// expression; empty means manual-only (no automatic scheduling). +@freezed +sealed class BackupTarget with _$BackupTarget { + const factory BackupTarget({ + @Default('') String id, + required String name, + required BackupMethod method, + required String remote, + @Default({}) Map params, + String? schedule, + @JsonKey(name: 'last_run') DateTime? lastRun, + @JsonKey(name: 'next_run') DateTime? nextRun, + }) = _BackupTarget; + + factory BackupTarget.fromJson(Map json) => _$BackupTargetFromJson(json); +} diff --git a/lib/core/models/backup_target.freezed.dart b/lib/core/models/backup_target.freezed.dart new file mode 100644 index 0000000..6054d84 --- /dev/null +++ b/lib/core/models/backup_target.freezed.dart @@ -0,0 +1,298 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'backup_target.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$BackupTarget { + + String get id; String get name; BackupMethod get method; String get remote; Map get params; String? get schedule;@JsonKey(name: 'last_run') DateTime? get lastRun;@JsonKey(name: 'next_run') DateTime? get nextRun; +/// Create a copy of BackupTarget +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$BackupTargetCopyWith get copyWith => _$BackupTargetCopyWithImpl(this as BackupTarget, _$identity); + + /// Serializes this BackupTarget to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is BackupTarget&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.method, method) || other.method == method)&&(identical(other.remote, remote) || other.remote == remote)&&const DeepCollectionEquality().equals(other.params, params)&&(identical(other.schedule, schedule) || other.schedule == schedule)&&(identical(other.lastRun, lastRun) || other.lastRun == lastRun)&&(identical(other.nextRun, nextRun) || other.nextRun == nextRun)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,method,remote,const DeepCollectionEquality().hash(params),schedule,lastRun,nextRun); + +@override +String toString() { + return 'BackupTarget(id: $id, name: $name, method: $method, remote: $remote, params: $params, schedule: $schedule, lastRun: $lastRun, nextRun: $nextRun)'; +} + + +} + +/// @nodoc +abstract mixin class $BackupTargetCopyWith<$Res> { + factory $BackupTargetCopyWith(BackupTarget value, $Res Function(BackupTarget) _then) = _$BackupTargetCopyWithImpl; +@useResult +$Res call({ + String id, String name, BackupMethod method, String remote, Map params, String? schedule,@JsonKey(name: 'last_run') DateTime? lastRun,@JsonKey(name: 'next_run') DateTime? nextRun +}); + + + + +} +/// @nodoc +class _$BackupTargetCopyWithImpl<$Res> + implements $BackupTargetCopyWith<$Res> { + _$BackupTargetCopyWithImpl(this._self, this._then); + + final BackupTarget _self; + final $Res Function(BackupTarget) _then; + +/// Create a copy of BackupTarget +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? method = null,Object? remote = null,Object? params = null,Object? schedule = freezed,Object? lastRun = freezed,Object? nextRun = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,method: null == method ? _self.method : method // ignore: cast_nullable_to_non_nullable +as BackupMethod,remote: null == remote ? _self.remote : remote // ignore: cast_nullable_to_non_nullable +as String,params: null == params ? _self.params : params // ignore: cast_nullable_to_non_nullable +as Map,schedule: freezed == schedule ? _self.schedule : schedule // ignore: cast_nullable_to_non_nullable +as String?,lastRun: freezed == lastRun ? _self.lastRun : lastRun // ignore: cast_nullable_to_non_nullable +as DateTime?,nextRun: freezed == nextRun ? _self.nextRun : nextRun // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [BackupTarget]. +extension BackupTargetPatterns on BackupTarget { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _BackupTarget value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _BackupTarget() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _BackupTarget value) $default,){ +final _that = this; +switch (_that) { +case _BackupTarget(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _BackupTarget value)? $default,){ +final _that = this; +switch (_that) { +case _BackupTarget() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, BackupMethod method, String remote, Map params, String? schedule, @JsonKey(name: 'last_run') DateTime? lastRun, @JsonKey(name: 'next_run') DateTime? nextRun)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _BackupTarget() when $default != null: +return $default(_that.id,_that.name,_that.method,_that.remote,_that.params,_that.schedule,_that.lastRun,_that.nextRun);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String name, BackupMethod method, String remote, Map params, String? schedule, @JsonKey(name: 'last_run') DateTime? lastRun, @JsonKey(name: 'next_run') DateTime? nextRun) $default,) {final _that = this; +switch (_that) { +case _BackupTarget(): +return $default(_that.id,_that.name,_that.method,_that.remote,_that.params,_that.schedule,_that.lastRun,_that.nextRun);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, BackupMethod method, String remote, Map params, String? schedule, @JsonKey(name: 'last_run') DateTime? lastRun, @JsonKey(name: 'next_run') DateTime? nextRun)? $default,) {final _that = this; +switch (_that) { +case _BackupTarget() when $default != null: +return $default(_that.id,_that.name,_that.method,_that.remote,_that.params,_that.schedule,_that.lastRun,_that.nextRun);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _BackupTarget implements BackupTarget { + const _BackupTarget({this.id = '', required this.name, required this.method, required this.remote, final Map params = const {}, this.schedule, @JsonKey(name: 'last_run') this.lastRun, @JsonKey(name: 'next_run') this.nextRun}): _params = params; + factory _BackupTarget.fromJson(Map json) => _$BackupTargetFromJson(json); + +@override@JsonKey() final String id; +@override final String name; +@override final BackupMethod method; +@override final String remote; + final Map _params; +@override@JsonKey() Map get params { + if (_params is EqualUnmodifiableMapView) return _params; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_params); +} + +@override final String? schedule; +@override@JsonKey(name: 'last_run') final DateTime? lastRun; +@override@JsonKey(name: 'next_run') final DateTime? nextRun; + +/// Create a copy of BackupTarget +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$BackupTargetCopyWith<_BackupTarget> get copyWith => __$BackupTargetCopyWithImpl<_BackupTarget>(this, _$identity); + +@override +Map toJson() { + return _$BackupTargetToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _BackupTarget&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.method, method) || other.method == method)&&(identical(other.remote, remote) || other.remote == remote)&&const DeepCollectionEquality().equals(other._params, _params)&&(identical(other.schedule, schedule) || other.schedule == schedule)&&(identical(other.lastRun, lastRun) || other.lastRun == lastRun)&&(identical(other.nextRun, nextRun) || other.nextRun == nextRun)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,method,remote,const DeepCollectionEquality().hash(_params),schedule,lastRun,nextRun); + +@override +String toString() { + return 'BackupTarget(id: $id, name: $name, method: $method, remote: $remote, params: $params, schedule: $schedule, lastRun: $lastRun, nextRun: $nextRun)'; +} + + +} + +/// @nodoc +abstract mixin class _$BackupTargetCopyWith<$Res> implements $BackupTargetCopyWith<$Res> { + factory _$BackupTargetCopyWith(_BackupTarget value, $Res Function(_BackupTarget) _then) = __$BackupTargetCopyWithImpl; +@override @useResult +$Res call({ + String id, String name, BackupMethod method, String remote, Map params, String? schedule,@JsonKey(name: 'last_run') DateTime? lastRun,@JsonKey(name: 'next_run') DateTime? nextRun +}); + + + + +} +/// @nodoc +class __$BackupTargetCopyWithImpl<$Res> + implements _$BackupTargetCopyWith<$Res> { + __$BackupTargetCopyWithImpl(this._self, this._then); + + final _BackupTarget _self; + final $Res Function(_BackupTarget) _then; + +/// Create a copy of BackupTarget +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? method = null,Object? remote = null,Object? params = null,Object? schedule = freezed,Object? lastRun = freezed,Object? nextRun = freezed,}) { + return _then(_BackupTarget( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,method: null == method ? _self.method : method // ignore: cast_nullable_to_non_nullable +as BackupMethod,remote: null == remote ? _self.remote : remote // ignore: cast_nullable_to_non_nullable +as String,params: null == params ? _self._params : params // ignore: cast_nullable_to_non_nullable +as Map,schedule: freezed == schedule ? _self.schedule : schedule // ignore: cast_nullable_to_non_nullable +as String?,lastRun: freezed == lastRun ? _self.lastRun : lastRun // ignore: cast_nullable_to_non_nullable +as DateTime?,nextRun: freezed == nextRun ? _self.nextRun : nextRun // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/backup_target.g.dart b/lib/core/models/backup_target.g.dart new file mode 100644 index 0000000..37a4228 --- /dev/null +++ b/lib/core/models/backup_target.g.dart @@ -0,0 +1,45 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'backup_target.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_BackupTarget _$BackupTargetFromJson(Map json) => + _BackupTarget( + id: json['id'] as String? ?? '', + name: json['name'] as String, + method: $enumDecode(_$BackupMethodEnumMap, json['method']), + remote: json['remote'] as String, + params: + (json['params'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ) ?? + const {}, + schedule: json['schedule'] as String?, + lastRun: json['last_run'] == null + ? null + : DateTime.parse(json['last_run'] as String), + nextRun: json['next_run'] == null + ? null + : DateTime.parse(json['next_run'] as String), + ); + +Map _$BackupTargetToJson(_BackupTarget instance) => + { + 'id': instance.id, + 'name': instance.name, + 'method': _$BackupMethodEnumMap[instance.method]!, + 'remote': instance.remote, + 'params': instance.params, + 'schedule': instance.schedule, + 'last_run': instance.lastRun?.toIso8601String(), + 'next_run': instance.nextRun?.toIso8601String(), + }; + +const _$BackupMethodEnumMap = { + BackupMethod.rsync: 'rsync', + BackupMethod.restic: 'restic', + BackupMethod.external: 'external', +}; diff --git a/lib/core/models/models.dart b/lib/core/models/models.dart new file mode 100644 index 0000000..3b4c5e9 --- /dev/null +++ b/lib/core/models/models.dart @@ -0,0 +1,12 @@ +export 'aggregated_node_status.dart'; +export 'backup_config.dart'; +export 'backup_execution.dart'; +export 'backup_method.dart'; +export 'backup_target.dart'; +export 'node_info.dart'; +export 'remote_node.dart'; +export 'run_state.dart'; +export 'scan_result.dart'; +export 'service.dart'; +export 'service_scan_info.dart'; +export 'update_record.dart'; diff --git a/lib/core/models/node_info.dart b/lib/core/models/node_info.dart new file mode 100644 index 0000000..e40b733 --- /dev/null +++ b/lib/core/models/node_info.dart @@ -0,0 +1,30 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'backup_config.dart'; +import 'update_record.dart'; + +part 'node_info.freezed.dart'; +part 'node_info.g.dart'; + +/// Mirrors `models.NodeInfo` — the local/current host's identity + backup +/// config + update log, as served by `GET /node/`. +/// +/// `resticPassword` is write-only: the API never echoes the real password +/// back (`GET /node/` sends `restic_password_set` instead, see +/// `controllers/node.go`), so this stays `null` unless the user is actively +/// setting/changing it in the identity form, and is omitted from the PUT +/// body entirely when `null` so an untouched form never clobbers the +/// existing password (mirrors the Go side's "empty string = don't change"). +@freezed +sealed class NodeInfo with _$NodeInfo { + const factory NodeInfo({ + required String hostname, + String? description, + @JsonKey(name: 'update_history') @Default([]) List updateHistory, + required BackupConfig backup, + @JsonKey(name: 'restic_password_set') @Default(false) bool resticPasswordSet, + @JsonKey(name: 'restic_password', includeIfNull: false) String? resticPassword, + }) = _NodeInfo; + + factory NodeInfo.fromJson(Map json) => _$NodeInfoFromJson(json); +} diff --git a/lib/core/models/node_info.freezed.dart b/lib/core/models/node_info.freezed.dart new file mode 100644 index 0000000..f0a28d0 --- /dev/null +++ b/lib/core/models/node_info.freezed.dart @@ -0,0 +1,310 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'node_info.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$NodeInfo { + + String get hostname; String? get description;@JsonKey(name: 'update_history') List get updateHistory; BackupConfig get backup;@JsonKey(name: 'restic_password_set') bool get resticPasswordSet;@JsonKey(name: 'restic_password', includeIfNull: false) String? get resticPassword; +/// Create a copy of NodeInfo +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NodeInfoCopyWith get copyWith => _$NodeInfoCopyWithImpl(this as NodeInfo, _$identity); + + /// Serializes this NodeInfo to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NodeInfo&&(identical(other.hostname, hostname) || other.hostname == hostname)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.updateHistory, updateHistory)&&(identical(other.backup, backup) || other.backup == backup)&&(identical(other.resticPasswordSet, resticPasswordSet) || other.resticPasswordSet == resticPasswordSet)&&(identical(other.resticPassword, resticPassword) || other.resticPassword == resticPassword)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,hostname,description,const DeepCollectionEquality().hash(updateHistory),backup,resticPasswordSet,resticPassword); + +@override +String toString() { + return 'NodeInfo(hostname: $hostname, description: $description, updateHistory: $updateHistory, backup: $backup, resticPasswordSet: $resticPasswordSet, resticPassword: $resticPassword)'; +} + + +} + +/// @nodoc +abstract mixin class $NodeInfoCopyWith<$Res> { + factory $NodeInfoCopyWith(NodeInfo value, $Res Function(NodeInfo) _then) = _$NodeInfoCopyWithImpl; +@useResult +$Res call({ + String hostname, String? description,@JsonKey(name: 'update_history') List updateHistory, BackupConfig backup,@JsonKey(name: 'restic_password_set') bool resticPasswordSet,@JsonKey(name: 'restic_password', includeIfNull: false) String? resticPassword +}); + + +$BackupConfigCopyWith<$Res> get backup; + +} +/// @nodoc +class _$NodeInfoCopyWithImpl<$Res> + implements $NodeInfoCopyWith<$Res> { + _$NodeInfoCopyWithImpl(this._self, this._then); + + final NodeInfo _self; + final $Res Function(NodeInfo) _then; + +/// Create a copy of NodeInfo +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? hostname = null,Object? description = freezed,Object? updateHistory = null,Object? backup = null,Object? resticPasswordSet = null,Object? resticPassword = freezed,}) { + return _then(_self.copyWith( +hostname: null == hostname ? _self.hostname : hostname // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,updateHistory: null == updateHistory ? _self.updateHistory : updateHistory // ignore: cast_nullable_to_non_nullable +as List,backup: null == backup ? _self.backup : backup // ignore: cast_nullable_to_non_nullable +as BackupConfig,resticPasswordSet: null == resticPasswordSet ? _self.resticPasswordSet : resticPasswordSet // ignore: cast_nullable_to_non_nullable +as bool,resticPassword: freezed == resticPassword ? _self.resticPassword : resticPassword // ignore: cast_nullable_to_non_nullable +as String?, + )); +} +/// Create a copy of NodeInfo +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$BackupConfigCopyWith<$Res> get backup { + + return $BackupConfigCopyWith<$Res>(_self.backup, (value) { + return _then(_self.copyWith(backup: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [NodeInfo]. +extension NodeInfoPatterns on NodeInfo { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NodeInfo value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NodeInfo() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NodeInfo value) $default,){ +final _that = this; +switch (_that) { +case _NodeInfo(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NodeInfo value)? $default,){ +final _that = this; +switch (_that) { +case _NodeInfo() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String hostname, String? description, @JsonKey(name: 'update_history') List updateHistory, BackupConfig backup, @JsonKey(name: 'restic_password_set') bool resticPasswordSet, @JsonKey(name: 'restic_password', includeIfNull: false) String? resticPassword)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NodeInfo() when $default != null: +return $default(_that.hostname,_that.description,_that.updateHistory,_that.backup,_that.resticPasswordSet,_that.resticPassword);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String hostname, String? description, @JsonKey(name: 'update_history') List updateHistory, BackupConfig backup, @JsonKey(name: 'restic_password_set') bool resticPasswordSet, @JsonKey(name: 'restic_password', includeIfNull: false) String? resticPassword) $default,) {final _that = this; +switch (_that) { +case _NodeInfo(): +return $default(_that.hostname,_that.description,_that.updateHistory,_that.backup,_that.resticPasswordSet,_that.resticPassword);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String hostname, String? description, @JsonKey(name: 'update_history') List updateHistory, BackupConfig backup, @JsonKey(name: 'restic_password_set') bool resticPasswordSet, @JsonKey(name: 'restic_password', includeIfNull: false) String? resticPassword)? $default,) {final _that = this; +switch (_that) { +case _NodeInfo() when $default != null: +return $default(_that.hostname,_that.description,_that.updateHistory,_that.backup,_that.resticPasswordSet,_that.resticPassword);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _NodeInfo implements NodeInfo { + const _NodeInfo({required this.hostname, this.description, @JsonKey(name: 'update_history') final List updateHistory = const [], required this.backup, @JsonKey(name: 'restic_password_set') this.resticPasswordSet = false, @JsonKey(name: 'restic_password', includeIfNull: false) this.resticPassword}): _updateHistory = updateHistory; + factory _NodeInfo.fromJson(Map json) => _$NodeInfoFromJson(json); + +@override final String hostname; +@override final String? description; + final List _updateHistory; +@override@JsonKey(name: 'update_history') List get updateHistory { + if (_updateHistory is EqualUnmodifiableListView) return _updateHistory; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_updateHistory); +} + +@override final BackupConfig backup; +@override@JsonKey(name: 'restic_password_set') final bool resticPasswordSet; +@override@JsonKey(name: 'restic_password', includeIfNull: false) final String? resticPassword; + +/// Create a copy of NodeInfo +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NodeInfoCopyWith<_NodeInfo> get copyWith => __$NodeInfoCopyWithImpl<_NodeInfo>(this, _$identity); + +@override +Map toJson() { + return _$NodeInfoToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NodeInfo&&(identical(other.hostname, hostname) || other.hostname == hostname)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other._updateHistory, _updateHistory)&&(identical(other.backup, backup) || other.backup == backup)&&(identical(other.resticPasswordSet, resticPasswordSet) || other.resticPasswordSet == resticPasswordSet)&&(identical(other.resticPassword, resticPassword) || other.resticPassword == resticPassword)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,hostname,description,const DeepCollectionEquality().hash(_updateHistory),backup,resticPasswordSet,resticPassword); + +@override +String toString() { + return 'NodeInfo(hostname: $hostname, description: $description, updateHistory: $updateHistory, backup: $backup, resticPasswordSet: $resticPasswordSet, resticPassword: $resticPassword)'; +} + + +} + +/// @nodoc +abstract mixin class _$NodeInfoCopyWith<$Res> implements $NodeInfoCopyWith<$Res> { + factory _$NodeInfoCopyWith(_NodeInfo value, $Res Function(_NodeInfo) _then) = __$NodeInfoCopyWithImpl; +@override @useResult +$Res call({ + String hostname, String? description,@JsonKey(name: 'update_history') List updateHistory, BackupConfig backup,@JsonKey(name: 'restic_password_set') bool resticPasswordSet,@JsonKey(name: 'restic_password', includeIfNull: false) String? resticPassword +}); + + +@override $BackupConfigCopyWith<$Res> get backup; + +} +/// @nodoc +class __$NodeInfoCopyWithImpl<$Res> + implements _$NodeInfoCopyWith<$Res> { + __$NodeInfoCopyWithImpl(this._self, this._then); + + final _NodeInfo _self; + final $Res Function(_NodeInfo) _then; + +/// Create a copy of NodeInfo +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? hostname = null,Object? description = freezed,Object? updateHistory = null,Object? backup = null,Object? resticPasswordSet = null,Object? resticPassword = freezed,}) { + return _then(_NodeInfo( +hostname: null == hostname ? _self.hostname : hostname // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,updateHistory: null == updateHistory ? _self._updateHistory : updateHistory // ignore: cast_nullable_to_non_nullable +as List,backup: null == backup ? _self.backup : backup // ignore: cast_nullable_to_non_nullable +as BackupConfig,resticPasswordSet: null == resticPasswordSet ? _self.resticPasswordSet : resticPasswordSet // ignore: cast_nullable_to_non_nullable +as bool,resticPassword: freezed == resticPassword ? _self.resticPassword : resticPassword // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +/// Create a copy of NodeInfo +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$BackupConfigCopyWith<$Res> get backup { + + return $BackupConfigCopyWith<$Res>(_self.backup, (value) { + return _then(_self.copyWith(backup: value)); + }); +} +} + +// dart format on diff --git a/lib/core/models/node_info.g.dart b/lib/core/models/node_info.g.dart new file mode 100644 index 0000000..7b7779e --- /dev/null +++ b/lib/core/models/node_info.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'node_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_NodeInfo _$NodeInfoFromJson(Map json) => _NodeInfo( + hostname: json['hostname'] as String, + description: json['description'] as String?, + updateHistory: + (json['update_history'] as List?) + ?.map((e) => UpdateRecord.fromJson(e as Map)) + .toList() ?? + const [], + backup: BackupConfig.fromJson(json['backup'] as Map), + resticPasswordSet: json['restic_password_set'] as bool? ?? false, + resticPassword: json['restic_password'] as String?, +); + +Map _$NodeInfoToJson(_NodeInfo instance) => { + 'hostname': instance.hostname, + 'description': instance.description, + 'update_history': instance.updateHistory.map((e) => e.toJson()).toList(), + 'backup': instance.backup.toJson(), + 'restic_password_set': instance.resticPasswordSet, + 'restic_password': ?instance.resticPassword, +}; diff --git a/lib/core/models/remote_node.dart b/lib/core/models/remote_node.dart new file mode 100644 index 0000000..29585c7 --- /dev/null +++ b/lib/core/models/remote_node.dart @@ -0,0 +1,21 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'remote_node.freezed.dart'; +part 'remote_node.g.dart'; + +/// Mirrors `models.RemoteNode` — an entry in this node's registry of +/// sibling NodeMaster hosts (`/nodes/*`). +@freezed +sealed class RemoteNode with _$RemoteNode { + const factory RemoteNode({ + @Default('') String id, + required String name, + required String url, + @Default('') String status, + String? description, + @Default([]) List tags, + @JsonKey(name: 'last_seen') DateTime? lastSeen, + }) = _RemoteNode; + + factory RemoteNode.fromJson(Map json) => _$RemoteNodeFromJson(json); +} diff --git a/lib/core/models/remote_node.freezed.dart b/lib/core/models/remote_node.freezed.dart new file mode 100644 index 0000000..4542af6 --- /dev/null +++ b/lib/core/models/remote_node.freezed.dart @@ -0,0 +1,295 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'remote_node.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$RemoteNode { + + String get id; String get name; String get url; String get status; String? get description; List get tags;@JsonKey(name: 'last_seen') DateTime? get lastSeen; +/// Create a copy of RemoteNode +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RemoteNodeCopyWith get copyWith => _$RemoteNodeCopyWithImpl(this as RemoteNode, _$identity); + + /// Serializes this RemoteNode to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteNode&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.url, url) || other.url == url)&&(identical(other.status, status) || other.status == status)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.tags, tags)&&(identical(other.lastSeen, lastSeen) || other.lastSeen == lastSeen)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,url,status,description,const DeepCollectionEquality().hash(tags),lastSeen); + +@override +String toString() { + return 'RemoteNode(id: $id, name: $name, url: $url, status: $status, description: $description, tags: $tags, lastSeen: $lastSeen)'; +} + + +} + +/// @nodoc +abstract mixin class $RemoteNodeCopyWith<$Res> { + factory $RemoteNodeCopyWith(RemoteNode value, $Res Function(RemoteNode) _then) = _$RemoteNodeCopyWithImpl; +@useResult +$Res call({ + String id, String name, String url, String status, String? description, List tags,@JsonKey(name: 'last_seen') DateTime? lastSeen +}); + + + + +} +/// @nodoc +class _$RemoteNodeCopyWithImpl<$Res> + implements $RemoteNodeCopyWith<$Res> { + _$RemoteNodeCopyWithImpl(this._self, this._then); + + final RemoteNode _self; + final $Res Function(RemoteNode) _then; + +/// Create a copy of RemoteNode +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? url = null,Object? status = null,Object? description = freezed,Object? tags = null,Object? lastSeen = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable +as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable +as List,lastSeen: freezed == lastSeen ? _self.lastSeen : lastSeen // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [RemoteNode]. +extension RemoteNodePatterns on RemoteNode { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _RemoteNode value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _RemoteNode() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _RemoteNode value) $default,){ +final _that = this; +switch (_that) { +case _RemoteNode(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _RemoteNode value)? $default,){ +final _that = this; +switch (_that) { +case _RemoteNode() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, String url, String status, String? description, List tags, @JsonKey(name: 'last_seen') DateTime? lastSeen)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _RemoteNode() when $default != null: +return $default(_that.id,_that.name,_that.url,_that.status,_that.description,_that.tags,_that.lastSeen);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String name, String url, String status, String? description, List tags, @JsonKey(name: 'last_seen') DateTime? lastSeen) $default,) {final _that = this; +switch (_that) { +case _RemoteNode(): +return $default(_that.id,_that.name,_that.url,_that.status,_that.description,_that.tags,_that.lastSeen);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, String url, String status, String? description, List tags, @JsonKey(name: 'last_seen') DateTime? lastSeen)? $default,) {final _that = this; +switch (_that) { +case _RemoteNode() when $default != null: +return $default(_that.id,_that.name,_that.url,_that.status,_that.description,_that.tags,_that.lastSeen);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _RemoteNode implements RemoteNode { + const _RemoteNode({this.id = '', required this.name, required this.url, this.status = '', this.description, final List tags = const [], @JsonKey(name: 'last_seen') this.lastSeen}): _tags = tags; + factory _RemoteNode.fromJson(Map json) => _$RemoteNodeFromJson(json); + +@override@JsonKey() final String id; +@override final String name; +@override final String url; +@override@JsonKey() final String status; +@override final String? description; + final List _tags; +@override@JsonKey() List get tags { + if (_tags is EqualUnmodifiableListView) return _tags; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_tags); +} + +@override@JsonKey(name: 'last_seen') final DateTime? lastSeen; + +/// Create a copy of RemoteNode +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RemoteNodeCopyWith<_RemoteNode> get copyWith => __$RemoteNodeCopyWithImpl<_RemoteNode>(this, _$identity); + +@override +Map toJson() { + return _$RemoteNodeToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteNode&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.url, url) || other.url == url)&&(identical(other.status, status) || other.status == status)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other._tags, _tags)&&(identical(other.lastSeen, lastSeen) || other.lastSeen == lastSeen)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,url,status,description,const DeepCollectionEquality().hash(_tags),lastSeen); + +@override +String toString() { + return 'RemoteNode(id: $id, name: $name, url: $url, status: $status, description: $description, tags: $tags, lastSeen: $lastSeen)'; +} + + +} + +/// @nodoc +abstract mixin class _$RemoteNodeCopyWith<$Res> implements $RemoteNodeCopyWith<$Res> { + factory _$RemoteNodeCopyWith(_RemoteNode value, $Res Function(_RemoteNode) _then) = __$RemoteNodeCopyWithImpl; +@override @useResult +$Res call({ + String id, String name, String url, String status, String? description, List tags,@JsonKey(name: 'last_seen') DateTime? lastSeen +}); + + + + +} +/// @nodoc +class __$RemoteNodeCopyWithImpl<$Res> + implements _$RemoteNodeCopyWith<$Res> { + __$RemoteNodeCopyWithImpl(this._self, this._then); + + final _RemoteNode _self; + final $Res Function(_RemoteNode) _then; + +/// Create a copy of RemoteNode +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? url = null,Object? status = null,Object? description = freezed,Object? tags = null,Object? lastSeen = freezed,}) { + return _then(_RemoteNode( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable +as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,tags: null == tags ? _self._tags : tags // ignore: cast_nullable_to_non_nullable +as List,lastSeen: freezed == lastSeen ? _self.lastSeen : lastSeen // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/remote_node.g.dart b/lib/core/models/remote_node.g.dart new file mode 100644 index 0000000..a8f81b3 --- /dev/null +++ b/lib/core/models/remote_node.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'remote_node.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_RemoteNode _$RemoteNodeFromJson(Map json) => _RemoteNode( + id: json['id'] as String? ?? '', + name: json['name'] as String, + url: json['url'] as String, + status: json['status'] as String? ?? '', + description: json['description'] as String?, + tags: + (json['tags'] as List?)?.map((e) => e as String).toList() ?? + const [], + lastSeen: json['last_seen'] == null + ? null + : DateTime.parse(json['last_seen'] as String), +); + +Map _$RemoteNodeToJson(_RemoteNode instance) => + { + 'id': instance.id, + 'name': instance.name, + 'url': instance.url, + 'status': instance.status, + 'description': instance.description, + 'tags': instance.tags, + 'last_seen': instance.lastSeen?.toIso8601String(), + }; diff --git a/lib/core/models/run_state.dart b/lib/core/models/run_state.dart new file mode 100644 index 0000000..efa7adf --- /dev/null +++ b/lib/core/models/run_state.dart @@ -0,0 +1,24 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'backup_method.dart'; + +part 'run_state.freezed.dart'; +part 'run_state.g.dart'; + +/// Mirrors `models.RunState` — the live (in-memory, non-persisted) progress +/// of a single backup target's current or most recent run. Polled from +/// `GET /.../backup/targets/:tid/progress` while a run is in flight. +@freezed +sealed class RunState with _$RunState { + const factory RunState({ + @Default(false) bool running, + @JsonKey(name: 'target_id') @Default('') String targetId, + BackupMethod? method, + @Default(0) double percent, + String? eta, + String? message, + @JsonKey(name: 'started_at') DateTime? startedAt, + }) = _RunState; + + factory RunState.fromJson(Map json) => _$RunStateFromJson(json); +} diff --git a/lib/core/models/run_state.freezed.dart b/lib/core/models/run_state.freezed.dart new file mode 100644 index 0000000..29eb71f --- /dev/null +++ b/lib/core/models/run_state.freezed.dart @@ -0,0 +1,289 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'run_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$RunState { + + bool get running;@JsonKey(name: 'target_id') String get targetId; BackupMethod? get method; double get percent; String? get eta; String? get message;@JsonKey(name: 'started_at') DateTime? get startedAt; +/// Create a copy of RunState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RunStateCopyWith get copyWith => _$RunStateCopyWithImpl(this as RunState, _$identity); + + /// Serializes this RunState to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RunState&&(identical(other.running, running) || other.running == running)&&(identical(other.targetId, targetId) || other.targetId == targetId)&&(identical(other.method, method) || other.method == method)&&(identical(other.percent, percent) || other.percent == percent)&&(identical(other.eta, eta) || other.eta == eta)&&(identical(other.message, message) || other.message == message)&&(identical(other.startedAt, startedAt) || other.startedAt == startedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,running,targetId,method,percent,eta,message,startedAt); + +@override +String toString() { + return 'RunState(running: $running, targetId: $targetId, method: $method, percent: $percent, eta: $eta, message: $message, startedAt: $startedAt)'; +} + + +} + +/// @nodoc +abstract mixin class $RunStateCopyWith<$Res> { + factory $RunStateCopyWith(RunState value, $Res Function(RunState) _then) = _$RunStateCopyWithImpl; +@useResult +$Res call({ + bool running,@JsonKey(name: 'target_id') String targetId, BackupMethod? method, double percent, String? eta, String? message,@JsonKey(name: 'started_at') DateTime? startedAt +}); + + + + +} +/// @nodoc +class _$RunStateCopyWithImpl<$Res> + implements $RunStateCopyWith<$Res> { + _$RunStateCopyWithImpl(this._self, this._then); + + final RunState _self; + final $Res Function(RunState) _then; + +/// Create a copy of RunState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? running = null,Object? targetId = null,Object? method = freezed,Object? percent = null,Object? eta = freezed,Object? message = freezed,Object? startedAt = freezed,}) { + return _then(_self.copyWith( +running: null == running ? _self.running : running // ignore: cast_nullable_to_non_nullable +as bool,targetId: null == targetId ? _self.targetId : targetId // ignore: cast_nullable_to_non_nullable +as String,method: freezed == method ? _self.method : method // ignore: cast_nullable_to_non_nullable +as BackupMethod?,percent: null == percent ? _self.percent : percent // ignore: cast_nullable_to_non_nullable +as double,eta: freezed == eta ? _self.eta : eta // ignore: cast_nullable_to_non_nullable +as String?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,startedAt: freezed == startedAt ? _self.startedAt : startedAt // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [RunState]. +extension RunStatePatterns on RunState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _RunState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _RunState() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _RunState value) $default,){ +final _that = this; +switch (_that) { +case _RunState(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _RunState value)? $default,){ +final _that = this; +switch (_that) { +case _RunState() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool running, @JsonKey(name: 'target_id') String targetId, BackupMethod? method, double percent, String? eta, String? message, @JsonKey(name: 'started_at') DateTime? startedAt)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _RunState() when $default != null: +return $default(_that.running,_that.targetId,_that.method,_that.percent,_that.eta,_that.message,_that.startedAt);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool running, @JsonKey(name: 'target_id') String targetId, BackupMethod? method, double percent, String? eta, String? message, @JsonKey(name: 'started_at') DateTime? startedAt) $default,) {final _that = this; +switch (_that) { +case _RunState(): +return $default(_that.running,_that.targetId,_that.method,_that.percent,_that.eta,_that.message,_that.startedAt);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool running, @JsonKey(name: 'target_id') String targetId, BackupMethod? method, double percent, String? eta, String? message, @JsonKey(name: 'started_at') DateTime? startedAt)? $default,) {final _that = this; +switch (_that) { +case _RunState() when $default != null: +return $default(_that.running,_that.targetId,_that.method,_that.percent,_that.eta,_that.message,_that.startedAt);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _RunState implements RunState { + const _RunState({this.running = false, @JsonKey(name: 'target_id') this.targetId = '', this.method, this.percent = 0, this.eta, this.message, @JsonKey(name: 'started_at') this.startedAt}); + factory _RunState.fromJson(Map json) => _$RunStateFromJson(json); + +@override@JsonKey() final bool running; +@override@JsonKey(name: 'target_id') final String targetId; +@override final BackupMethod? method; +@override@JsonKey() final double percent; +@override final String? eta; +@override final String? message; +@override@JsonKey(name: 'started_at') final DateTime? startedAt; + +/// Create a copy of RunState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RunStateCopyWith<_RunState> get copyWith => __$RunStateCopyWithImpl<_RunState>(this, _$identity); + +@override +Map toJson() { + return _$RunStateToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RunState&&(identical(other.running, running) || other.running == running)&&(identical(other.targetId, targetId) || other.targetId == targetId)&&(identical(other.method, method) || other.method == method)&&(identical(other.percent, percent) || other.percent == percent)&&(identical(other.eta, eta) || other.eta == eta)&&(identical(other.message, message) || other.message == message)&&(identical(other.startedAt, startedAt) || other.startedAt == startedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,running,targetId,method,percent,eta,message,startedAt); + +@override +String toString() { + return 'RunState(running: $running, targetId: $targetId, method: $method, percent: $percent, eta: $eta, message: $message, startedAt: $startedAt)'; +} + + +} + +/// @nodoc +abstract mixin class _$RunStateCopyWith<$Res> implements $RunStateCopyWith<$Res> { + factory _$RunStateCopyWith(_RunState value, $Res Function(_RunState) _then) = __$RunStateCopyWithImpl; +@override @useResult +$Res call({ + bool running,@JsonKey(name: 'target_id') String targetId, BackupMethod? method, double percent, String? eta, String? message,@JsonKey(name: 'started_at') DateTime? startedAt +}); + + + + +} +/// @nodoc +class __$RunStateCopyWithImpl<$Res> + implements _$RunStateCopyWith<$Res> { + __$RunStateCopyWithImpl(this._self, this._then); + + final _RunState _self; + final $Res Function(_RunState) _then; + +/// Create a copy of RunState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? running = null,Object? targetId = null,Object? method = freezed,Object? percent = null,Object? eta = freezed,Object? message = freezed,Object? startedAt = freezed,}) { + return _then(_RunState( +running: null == running ? _self.running : running // ignore: cast_nullable_to_non_nullable +as bool,targetId: null == targetId ? _self.targetId : targetId // ignore: cast_nullable_to_non_nullable +as String,method: freezed == method ? _self.method : method // ignore: cast_nullable_to_non_nullable +as BackupMethod?,percent: null == percent ? _self.percent : percent // ignore: cast_nullable_to_non_nullable +as double,eta: freezed == eta ? _self.eta : eta // ignore: cast_nullable_to_non_nullable +as String?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,startedAt: freezed == startedAt ? _self.startedAt : startedAt // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/run_state.g.dart b/lib/core/models/run_state.g.dart new file mode 100644 index 0000000..85cd03d --- /dev/null +++ b/lib/core/models/run_state.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'run_state.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_RunState _$RunStateFromJson(Map json) => _RunState( + running: json['running'] as bool? ?? false, + targetId: json['target_id'] as String? ?? '', + method: $enumDecodeNullable(_$BackupMethodEnumMap, json['method']), + percent: (json['percent'] as num?)?.toDouble() ?? 0, + eta: json['eta'] as String?, + message: json['message'] as String?, + startedAt: json['started_at'] == null + ? null + : DateTime.parse(json['started_at'] as String), +); + +Map _$RunStateToJson(_RunState instance) => { + 'running': instance.running, + 'target_id': instance.targetId, + 'method': _$BackupMethodEnumMap[instance.method], + 'percent': instance.percent, + 'eta': instance.eta, + 'message': instance.message, + 'started_at': instance.startedAt?.toIso8601String(), +}; + +const _$BackupMethodEnumMap = { + BackupMethod.rsync: 'rsync', + BackupMethod.restic: 'restic', + BackupMethod.external: 'external', +}; diff --git a/lib/core/models/scan_result.dart b/lib/core/models/scan_result.dart new file mode 100644 index 0000000..1a9e702 --- /dev/null +++ b/lib/core/models/scan_result.dart @@ -0,0 +1,20 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'service_scan_info.dart'; + +part 'scan_result.freezed.dart'; +part 'scan_result.g.dart'; + +/// Mirrors `models.ScanResult`, returned by `POST /node/scan`. +@freezed +sealed class ScanResult with _$ScanResult { + const factory ScanResult({ + @JsonKey(name: 'scanned_paths') @Default([]) List scannedPaths, + @Default(0) int found, + @Default([]) List created, + @Default([]) List updated, + @Default([]) List errors, + }) = _ScanResult; + + factory ScanResult.fromJson(Map json) => _$ScanResultFromJson(json); +} diff --git a/lib/core/models/scan_result.freezed.dart b/lib/core/models/scan_result.freezed.dart new file mode 100644 index 0000000..5db05d0 --- /dev/null +++ b/lib/core/models/scan_result.freezed.dart @@ -0,0 +1,307 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'scan_result.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$ScanResult { + +@JsonKey(name: 'scanned_paths') List get scannedPaths; int get found; List get created; List get updated; List get errors; +/// Create a copy of ScanResult +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ScanResultCopyWith get copyWith => _$ScanResultCopyWithImpl(this as ScanResult, _$identity); + + /// Serializes this ScanResult to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ScanResult&&const DeepCollectionEquality().equals(other.scannedPaths, scannedPaths)&&(identical(other.found, found) || other.found == found)&&const DeepCollectionEquality().equals(other.created, created)&&const DeepCollectionEquality().equals(other.updated, updated)&&const DeepCollectionEquality().equals(other.errors, errors)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(scannedPaths),found,const DeepCollectionEquality().hash(created),const DeepCollectionEquality().hash(updated),const DeepCollectionEquality().hash(errors)); + +@override +String toString() { + return 'ScanResult(scannedPaths: $scannedPaths, found: $found, created: $created, updated: $updated, errors: $errors)'; +} + + +} + +/// @nodoc +abstract mixin class $ScanResultCopyWith<$Res> { + factory $ScanResultCopyWith(ScanResult value, $Res Function(ScanResult) _then) = _$ScanResultCopyWithImpl; +@useResult +$Res call({ +@JsonKey(name: 'scanned_paths') List scannedPaths, int found, List created, List updated, List errors +}); + + + + +} +/// @nodoc +class _$ScanResultCopyWithImpl<$Res> + implements $ScanResultCopyWith<$Res> { + _$ScanResultCopyWithImpl(this._self, this._then); + + final ScanResult _self; + final $Res Function(ScanResult) _then; + +/// Create a copy of ScanResult +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? scannedPaths = null,Object? found = null,Object? created = null,Object? updated = null,Object? errors = null,}) { + return _then(_self.copyWith( +scannedPaths: null == scannedPaths ? _self.scannedPaths : scannedPaths // ignore: cast_nullable_to_non_nullable +as List,found: null == found ? _self.found : found // ignore: cast_nullable_to_non_nullable +as int,created: null == created ? _self.created : created // ignore: cast_nullable_to_non_nullable +as List,updated: null == updated ? _self.updated : updated // ignore: cast_nullable_to_non_nullable +as List,errors: null == errors ? _self.errors : errors // ignore: cast_nullable_to_non_nullable +as List, + )); +} + +} + + +/// Adds pattern-matching-related methods to [ScanResult]. +extension ScanResultPatterns on ScanResult { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ScanResult value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ScanResult() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ScanResult value) $default,){ +final _that = this; +switch (_that) { +case _ScanResult(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ScanResult value)? $default,){ +final _that = this; +switch (_that) { +case _ScanResult() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function(@JsonKey(name: 'scanned_paths') List scannedPaths, int found, List created, List updated, List errors)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ScanResult() when $default != null: +return $default(_that.scannedPaths,_that.found,_that.created,_that.updated,_that.errors);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function(@JsonKey(name: 'scanned_paths') List scannedPaths, int found, List created, List updated, List errors) $default,) {final _that = this; +switch (_that) { +case _ScanResult(): +return $default(_that.scannedPaths,_that.found,_that.created,_that.updated,_that.errors);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function(@JsonKey(name: 'scanned_paths') List scannedPaths, int found, List created, List updated, List errors)? $default,) {final _that = this; +switch (_that) { +case _ScanResult() when $default != null: +return $default(_that.scannedPaths,_that.found,_that.created,_that.updated,_that.errors);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _ScanResult implements ScanResult { + const _ScanResult({@JsonKey(name: 'scanned_paths') final List scannedPaths = const [], this.found = 0, final List created = const [], final List updated = const [], final List errors = const []}): _scannedPaths = scannedPaths,_created = created,_updated = updated,_errors = errors; + factory _ScanResult.fromJson(Map json) => _$ScanResultFromJson(json); + + final List _scannedPaths; +@override@JsonKey(name: 'scanned_paths') List get scannedPaths { + if (_scannedPaths is EqualUnmodifiableListView) return _scannedPaths; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_scannedPaths); +} + +@override@JsonKey() final int found; + final List _created; +@override@JsonKey() List get created { + if (_created is EqualUnmodifiableListView) return _created; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_created); +} + + final List _updated; +@override@JsonKey() List get updated { + if (_updated is EqualUnmodifiableListView) return _updated; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_updated); +} + + final List _errors; +@override@JsonKey() List get errors { + if (_errors is EqualUnmodifiableListView) return _errors; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_errors); +} + + +/// Create a copy of ScanResult +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ScanResultCopyWith<_ScanResult> get copyWith => __$ScanResultCopyWithImpl<_ScanResult>(this, _$identity); + +@override +Map toJson() { + return _$ScanResultToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ScanResult&&const DeepCollectionEquality().equals(other._scannedPaths, _scannedPaths)&&(identical(other.found, found) || other.found == found)&&const DeepCollectionEquality().equals(other._created, _created)&&const DeepCollectionEquality().equals(other._updated, _updated)&&const DeepCollectionEquality().equals(other._errors, _errors)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_scannedPaths),found,const DeepCollectionEquality().hash(_created),const DeepCollectionEquality().hash(_updated),const DeepCollectionEquality().hash(_errors)); + +@override +String toString() { + return 'ScanResult(scannedPaths: $scannedPaths, found: $found, created: $created, updated: $updated, errors: $errors)'; +} + + +} + +/// @nodoc +abstract mixin class _$ScanResultCopyWith<$Res> implements $ScanResultCopyWith<$Res> { + factory _$ScanResultCopyWith(_ScanResult value, $Res Function(_ScanResult) _then) = __$ScanResultCopyWithImpl; +@override @useResult +$Res call({ +@JsonKey(name: 'scanned_paths') List scannedPaths, int found, List created, List updated, List errors +}); + + + + +} +/// @nodoc +class __$ScanResultCopyWithImpl<$Res> + implements _$ScanResultCopyWith<$Res> { + __$ScanResultCopyWithImpl(this._self, this._then); + + final _ScanResult _self; + final $Res Function(_ScanResult) _then; + +/// Create a copy of ScanResult +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? scannedPaths = null,Object? found = null,Object? created = null,Object? updated = null,Object? errors = null,}) { + return _then(_ScanResult( +scannedPaths: null == scannedPaths ? _self._scannedPaths : scannedPaths // ignore: cast_nullable_to_non_nullable +as List,found: null == found ? _self.found : found // ignore: cast_nullable_to_non_nullable +as int,created: null == created ? _self._created : created // ignore: cast_nullable_to_non_nullable +as List,updated: null == updated ? _self._updated : updated // ignore: cast_nullable_to_non_nullable +as List,errors: null == errors ? _self._errors : errors // ignore: cast_nullable_to_non_nullable +as List, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/scan_result.g.dart b/lib/core/models/scan_result.g.dart new file mode 100644 index 0000000..70e76fa --- /dev/null +++ b/lib/core/models/scan_result.g.dart @@ -0,0 +1,38 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'scan_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_ScanResult _$ScanResultFromJson(Map json) => _ScanResult( + scannedPaths: + (json['scanned_paths'] as List?) + ?.map((e) => e as String) + .toList() ?? + const [], + found: (json['found'] as num?)?.toInt() ?? 0, + created: + (json['created'] as List?) + ?.map((e) => ServiceScanInfo.fromJson(e as Map)) + .toList() ?? + const [], + updated: + (json['updated'] as List?) + ?.map((e) => ServiceScanInfo.fromJson(e as Map)) + .toList() ?? + const [], + errors: + (json['errors'] as List?)?.map((e) => e as String).toList() ?? + const [], +); + +Map _$ScanResultToJson(_ScanResult instance) => + { + 'scanned_paths': instance.scannedPaths, + 'found': instance.found, + 'created': instance.created.map((e) => e.toJson()).toList(), + 'updated': instance.updated.map((e) => e.toJson()).toList(), + 'errors': instance.errors, + }; diff --git a/lib/core/models/service.dart b/lib/core/models/service.dart new file mode 100644 index 0000000..72319a2 --- /dev/null +++ b/lib/core/models/service.dart @@ -0,0 +1,29 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'backup_config.dart'; + +part 'service.freezed.dart'; +part 'service.g.dart'; + +/// Mirrors `models.Service` — a compose-based service on this host. +/// `status` is one of "up" / "down" / "unknown" (untyped string on the Go +/// side); kept as a raw string here too and mapped to [Severity] at the +/// widget boundary rather than modeled as a closed enum, since the server +/// can emit values this client doesn't yet know about. +@freezed +sealed class Service with _$Service { + const factory Service({ + @Default('') String id, + required String name, + @JsonKey(name: 'compose_file') required String composeFile, + @JsonKey(name: 'compose_type') String? composeType, + @JsonKey(name: 'backup_folder') String? backupFolder, + @Default('unknown') String status, + @JsonKey(name: 'stop_on_backup') @Default(false) bool stopOnBackup, + @JsonKey(name: 'external_backup_stop_minutes') @Default(0) int externalBackupStopMinutes, + @JsonKey(name: 'external_backup_start_minutes') @Default(0) int externalBackupStartMinutes, + BackupConfig? backup, + }) = _Service; + + factory Service.fromJson(Map json) => _$ServiceFromJson(json); +} diff --git a/lib/core/models/service.freezed.dart b/lib/core/models/service.freezed.dart new file mode 100644 index 0000000..7276514 --- /dev/null +++ b/lib/core/models/service.freezed.dart @@ -0,0 +1,322 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'service.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$Service { + + String get id; String get name;@JsonKey(name: 'compose_file') String get composeFile;@JsonKey(name: 'compose_type') String? get composeType;@JsonKey(name: 'backup_folder') String? get backupFolder; String get status;@JsonKey(name: 'stop_on_backup') bool get stopOnBackup;@JsonKey(name: 'external_backup_stop_minutes') int get externalBackupStopMinutes;@JsonKey(name: 'external_backup_start_minutes') int get externalBackupStartMinutes; BackupConfig? get backup; +/// Create a copy of Service +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ServiceCopyWith get copyWith => _$ServiceCopyWithImpl(this as Service, _$identity); + + /// Serializes this Service to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Service&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.composeFile, composeFile) || other.composeFile == composeFile)&&(identical(other.composeType, composeType) || other.composeType == composeType)&&(identical(other.backupFolder, backupFolder) || other.backupFolder == backupFolder)&&(identical(other.status, status) || other.status == status)&&(identical(other.stopOnBackup, stopOnBackup) || other.stopOnBackup == stopOnBackup)&&(identical(other.externalBackupStopMinutes, externalBackupStopMinutes) || other.externalBackupStopMinutes == externalBackupStopMinutes)&&(identical(other.externalBackupStartMinutes, externalBackupStartMinutes) || other.externalBackupStartMinutes == externalBackupStartMinutes)&&(identical(other.backup, backup) || other.backup == backup)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,composeFile,composeType,backupFolder,status,stopOnBackup,externalBackupStopMinutes,externalBackupStartMinutes,backup); + +@override +String toString() { + return 'Service(id: $id, name: $name, composeFile: $composeFile, composeType: $composeType, backupFolder: $backupFolder, status: $status, stopOnBackup: $stopOnBackup, externalBackupStopMinutes: $externalBackupStopMinutes, externalBackupStartMinutes: $externalBackupStartMinutes, backup: $backup)'; +} + + +} + +/// @nodoc +abstract mixin class $ServiceCopyWith<$Res> { + factory $ServiceCopyWith(Service value, $Res Function(Service) _then) = _$ServiceCopyWithImpl; +@useResult +$Res call({ + String id, String name,@JsonKey(name: 'compose_file') String composeFile,@JsonKey(name: 'compose_type') String? composeType,@JsonKey(name: 'backup_folder') String? backupFolder, String status,@JsonKey(name: 'stop_on_backup') bool stopOnBackup,@JsonKey(name: 'external_backup_stop_minutes') int externalBackupStopMinutes,@JsonKey(name: 'external_backup_start_minutes') int externalBackupStartMinutes, BackupConfig? backup +}); + + +$BackupConfigCopyWith<$Res>? get backup; + +} +/// @nodoc +class _$ServiceCopyWithImpl<$Res> + implements $ServiceCopyWith<$Res> { + _$ServiceCopyWithImpl(this._self, this._then); + + final Service _self; + final $Res Function(Service) _then; + +/// Create a copy of Service +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? composeFile = null,Object? composeType = freezed,Object? backupFolder = freezed,Object? status = null,Object? stopOnBackup = null,Object? externalBackupStopMinutes = null,Object? externalBackupStartMinutes = null,Object? backup = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,composeFile: null == composeFile ? _self.composeFile : composeFile // ignore: cast_nullable_to_non_nullable +as String,composeType: freezed == composeType ? _self.composeType : composeType // ignore: cast_nullable_to_non_nullable +as String?,backupFolder: freezed == backupFolder ? _self.backupFolder : backupFolder // ignore: cast_nullable_to_non_nullable +as String?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String,stopOnBackup: null == stopOnBackup ? _self.stopOnBackup : stopOnBackup // ignore: cast_nullable_to_non_nullable +as bool,externalBackupStopMinutes: null == externalBackupStopMinutes ? _self.externalBackupStopMinutes : externalBackupStopMinutes // ignore: cast_nullable_to_non_nullable +as int,externalBackupStartMinutes: null == externalBackupStartMinutes ? _self.externalBackupStartMinutes : externalBackupStartMinutes // ignore: cast_nullable_to_non_nullable +as int,backup: freezed == backup ? _self.backup : backup // ignore: cast_nullable_to_non_nullable +as BackupConfig?, + )); +} +/// Create a copy of Service +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$BackupConfigCopyWith<$Res>? get backup { + if (_self.backup == null) { + return null; + } + + return $BackupConfigCopyWith<$Res>(_self.backup!, (value) { + return _then(_self.copyWith(backup: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [Service]. +extension ServicePatterns on Service { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Service value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Service() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Service value) $default,){ +final _that = this; +switch (_that) { +case _Service(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Service value)? $default,){ +final _that = this; +switch (_that) { +case _Service() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, @JsonKey(name: 'compose_file') String composeFile, @JsonKey(name: 'compose_type') String? composeType, @JsonKey(name: 'backup_folder') String? backupFolder, String status, @JsonKey(name: 'stop_on_backup') bool stopOnBackup, @JsonKey(name: 'external_backup_stop_minutes') int externalBackupStopMinutes, @JsonKey(name: 'external_backup_start_minutes') int externalBackupStartMinutes, BackupConfig? backup)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Service() when $default != null: +return $default(_that.id,_that.name,_that.composeFile,_that.composeType,_that.backupFolder,_that.status,_that.stopOnBackup,_that.externalBackupStopMinutes,_that.externalBackupStartMinutes,_that.backup);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String name, @JsonKey(name: 'compose_file') String composeFile, @JsonKey(name: 'compose_type') String? composeType, @JsonKey(name: 'backup_folder') String? backupFolder, String status, @JsonKey(name: 'stop_on_backup') bool stopOnBackup, @JsonKey(name: 'external_backup_stop_minutes') int externalBackupStopMinutes, @JsonKey(name: 'external_backup_start_minutes') int externalBackupStartMinutes, BackupConfig? backup) $default,) {final _that = this; +switch (_that) { +case _Service(): +return $default(_that.id,_that.name,_that.composeFile,_that.composeType,_that.backupFolder,_that.status,_that.stopOnBackup,_that.externalBackupStopMinutes,_that.externalBackupStartMinutes,_that.backup);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, @JsonKey(name: 'compose_file') String composeFile, @JsonKey(name: 'compose_type') String? composeType, @JsonKey(name: 'backup_folder') String? backupFolder, String status, @JsonKey(name: 'stop_on_backup') bool stopOnBackup, @JsonKey(name: 'external_backup_stop_minutes') int externalBackupStopMinutes, @JsonKey(name: 'external_backup_start_minutes') int externalBackupStartMinutes, BackupConfig? backup)? $default,) {final _that = this; +switch (_that) { +case _Service() when $default != null: +return $default(_that.id,_that.name,_that.composeFile,_that.composeType,_that.backupFolder,_that.status,_that.stopOnBackup,_that.externalBackupStopMinutes,_that.externalBackupStartMinutes,_that.backup);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _Service implements Service { + const _Service({this.id = '', required this.name, @JsonKey(name: 'compose_file') required this.composeFile, @JsonKey(name: 'compose_type') this.composeType, @JsonKey(name: 'backup_folder') this.backupFolder, this.status = 'unknown', @JsonKey(name: 'stop_on_backup') this.stopOnBackup = false, @JsonKey(name: 'external_backup_stop_minutes') this.externalBackupStopMinutes = 0, @JsonKey(name: 'external_backup_start_minutes') this.externalBackupStartMinutes = 0, this.backup}); + factory _Service.fromJson(Map json) => _$ServiceFromJson(json); + +@override@JsonKey() final String id; +@override final String name; +@override@JsonKey(name: 'compose_file') final String composeFile; +@override@JsonKey(name: 'compose_type') final String? composeType; +@override@JsonKey(name: 'backup_folder') final String? backupFolder; +@override@JsonKey() final String status; +@override@JsonKey(name: 'stop_on_backup') final bool stopOnBackup; +@override@JsonKey(name: 'external_backup_stop_minutes') final int externalBackupStopMinutes; +@override@JsonKey(name: 'external_backup_start_minutes') final int externalBackupStartMinutes; +@override final BackupConfig? backup; + +/// Create a copy of Service +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ServiceCopyWith<_Service> get copyWith => __$ServiceCopyWithImpl<_Service>(this, _$identity); + +@override +Map toJson() { + return _$ServiceToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Service&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.composeFile, composeFile) || other.composeFile == composeFile)&&(identical(other.composeType, composeType) || other.composeType == composeType)&&(identical(other.backupFolder, backupFolder) || other.backupFolder == backupFolder)&&(identical(other.status, status) || other.status == status)&&(identical(other.stopOnBackup, stopOnBackup) || other.stopOnBackup == stopOnBackup)&&(identical(other.externalBackupStopMinutes, externalBackupStopMinutes) || other.externalBackupStopMinutes == externalBackupStopMinutes)&&(identical(other.externalBackupStartMinutes, externalBackupStartMinutes) || other.externalBackupStartMinutes == externalBackupStartMinutes)&&(identical(other.backup, backup) || other.backup == backup)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,name,composeFile,composeType,backupFolder,status,stopOnBackup,externalBackupStopMinutes,externalBackupStartMinutes,backup); + +@override +String toString() { + return 'Service(id: $id, name: $name, composeFile: $composeFile, composeType: $composeType, backupFolder: $backupFolder, status: $status, stopOnBackup: $stopOnBackup, externalBackupStopMinutes: $externalBackupStopMinutes, externalBackupStartMinutes: $externalBackupStartMinutes, backup: $backup)'; +} + + +} + +/// @nodoc +abstract mixin class _$ServiceCopyWith<$Res> implements $ServiceCopyWith<$Res> { + factory _$ServiceCopyWith(_Service value, $Res Function(_Service) _then) = __$ServiceCopyWithImpl; +@override @useResult +$Res call({ + String id, String name,@JsonKey(name: 'compose_file') String composeFile,@JsonKey(name: 'compose_type') String? composeType,@JsonKey(name: 'backup_folder') String? backupFolder, String status,@JsonKey(name: 'stop_on_backup') bool stopOnBackup,@JsonKey(name: 'external_backup_stop_minutes') int externalBackupStopMinutes,@JsonKey(name: 'external_backup_start_minutes') int externalBackupStartMinutes, BackupConfig? backup +}); + + +@override $BackupConfigCopyWith<$Res>? get backup; + +} +/// @nodoc +class __$ServiceCopyWithImpl<$Res> + implements _$ServiceCopyWith<$Res> { + __$ServiceCopyWithImpl(this._self, this._then); + + final _Service _self; + final $Res Function(_Service) _then; + +/// Create a copy of Service +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? composeFile = null,Object? composeType = freezed,Object? backupFolder = freezed,Object? status = null,Object? stopOnBackup = null,Object? externalBackupStopMinutes = null,Object? externalBackupStartMinutes = null,Object? backup = freezed,}) { + return _then(_Service( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,composeFile: null == composeFile ? _self.composeFile : composeFile // ignore: cast_nullable_to_non_nullable +as String,composeType: freezed == composeType ? _self.composeType : composeType // ignore: cast_nullable_to_non_nullable +as String?,backupFolder: freezed == backupFolder ? _self.backupFolder : backupFolder // ignore: cast_nullable_to_non_nullable +as String?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String,stopOnBackup: null == stopOnBackup ? _self.stopOnBackup : stopOnBackup // ignore: cast_nullable_to_non_nullable +as bool,externalBackupStopMinutes: null == externalBackupStopMinutes ? _self.externalBackupStopMinutes : externalBackupStopMinutes // ignore: cast_nullable_to_non_nullable +as int,externalBackupStartMinutes: null == externalBackupStartMinutes ? _self.externalBackupStartMinutes : externalBackupStartMinutes // ignore: cast_nullable_to_non_nullable +as int,backup: freezed == backup ? _self.backup : backup // ignore: cast_nullable_to_non_nullable +as BackupConfig?, + )); +} + +/// Create a copy of Service +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$BackupConfigCopyWith<$Res>? get backup { + if (_self.backup == null) { + return null; + } + + return $BackupConfigCopyWith<$Res>(_self.backup!, (value) { + return _then(_self.copyWith(backup: value)); + }); +} +} + +// dart format on diff --git a/lib/core/models/service.g.dart b/lib/core/models/service.g.dart new file mode 100644 index 0000000..eabb9b6 --- /dev/null +++ b/lib/core/models/service.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'service.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_Service _$ServiceFromJson(Map json) => _Service( + id: json['id'] as String? ?? '', + name: json['name'] as String, + composeFile: json['compose_file'] as String, + composeType: json['compose_type'] as String?, + backupFolder: json['backup_folder'] as String?, + status: json['status'] as String? ?? 'unknown', + stopOnBackup: json['stop_on_backup'] as bool? ?? false, + externalBackupStopMinutes: + (json['external_backup_stop_minutes'] as num?)?.toInt() ?? 0, + externalBackupStartMinutes: + (json['external_backup_start_minutes'] as num?)?.toInt() ?? 0, + backup: json['backup'] == null + ? null + : BackupConfig.fromJson(json['backup'] as Map), +); + +Map _$ServiceToJson(_Service instance) => { + 'id': instance.id, + 'name': instance.name, + 'compose_file': instance.composeFile, + 'compose_type': instance.composeType, + 'backup_folder': instance.backupFolder, + 'status': instance.status, + 'stop_on_backup': instance.stopOnBackup, + 'external_backup_stop_minutes': instance.externalBackupStopMinutes, + 'external_backup_start_minutes': instance.externalBackupStartMinutes, + 'backup': instance.backup?.toJson(), +}; diff --git a/lib/core/models/service_scan_info.dart b/lib/core/models/service_scan_info.dart new file mode 100644 index 0000000..074e358 --- /dev/null +++ b/lib/core/models/service_scan_info.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'service_scan_info.freezed.dart'; +part 'service_scan_info.g.dart'; + +/// Mirrors `models.ServiceScanInfo` — one entry in a [ScanResult]'s +/// `created`/`updated` lists. +@freezed +sealed class ServiceScanInfo with _$ServiceScanInfo { + const factory ServiceScanInfo({ + required String name, + @JsonKey(name: 'compose_file') required String composeFile, + @JsonKey(name: 'backup_folder') String? backupFolder, + @Default('unknown') String status, + }) = _ServiceScanInfo; + + factory ServiceScanInfo.fromJson(Map json) => _$ServiceScanInfoFromJson(json); +} diff --git a/lib/core/models/service_scan_info.freezed.dart b/lib/core/models/service_scan_info.freezed.dart new file mode 100644 index 0000000..603aa69 --- /dev/null +++ b/lib/core/models/service_scan_info.freezed.dart @@ -0,0 +1,280 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'service_scan_info.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$ServiceScanInfo { + + String get name;@JsonKey(name: 'compose_file') String get composeFile;@JsonKey(name: 'backup_folder') String? get backupFolder; String get status; +/// Create a copy of ServiceScanInfo +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ServiceScanInfoCopyWith get copyWith => _$ServiceScanInfoCopyWithImpl(this as ServiceScanInfo, _$identity); + + /// Serializes this ServiceScanInfo to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ServiceScanInfo&&(identical(other.name, name) || other.name == name)&&(identical(other.composeFile, composeFile) || other.composeFile == composeFile)&&(identical(other.backupFolder, backupFolder) || other.backupFolder == backupFolder)&&(identical(other.status, status) || other.status == status)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,name,composeFile,backupFolder,status); + +@override +String toString() { + return 'ServiceScanInfo(name: $name, composeFile: $composeFile, backupFolder: $backupFolder, status: $status)'; +} + + +} + +/// @nodoc +abstract mixin class $ServiceScanInfoCopyWith<$Res> { + factory $ServiceScanInfoCopyWith(ServiceScanInfo value, $Res Function(ServiceScanInfo) _then) = _$ServiceScanInfoCopyWithImpl; +@useResult +$Res call({ + String name,@JsonKey(name: 'compose_file') String composeFile,@JsonKey(name: 'backup_folder') String? backupFolder, String status +}); + + + + +} +/// @nodoc +class _$ServiceScanInfoCopyWithImpl<$Res> + implements $ServiceScanInfoCopyWith<$Res> { + _$ServiceScanInfoCopyWithImpl(this._self, this._then); + + final ServiceScanInfo _self; + final $Res Function(ServiceScanInfo) _then; + +/// Create a copy of ServiceScanInfo +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? composeFile = null,Object? backupFolder = freezed,Object? status = null,}) { + return _then(_self.copyWith( +name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,composeFile: null == composeFile ? _self.composeFile : composeFile // ignore: cast_nullable_to_non_nullable +as String,backupFolder: freezed == backupFolder ? _self.backupFolder : backupFolder // ignore: cast_nullable_to_non_nullable +as String?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [ServiceScanInfo]. +extension ServiceScanInfoPatterns on ServiceScanInfo { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ServiceScanInfo value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ServiceScanInfo() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ServiceScanInfo value) $default,){ +final _that = this; +switch (_that) { +case _ServiceScanInfo(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ServiceScanInfo value)? $default,){ +final _that = this; +switch (_that) { +case _ServiceScanInfo() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String name, @JsonKey(name: 'compose_file') String composeFile, @JsonKey(name: 'backup_folder') String? backupFolder, String status)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ServiceScanInfo() when $default != null: +return $default(_that.name,_that.composeFile,_that.backupFolder,_that.status);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String name, @JsonKey(name: 'compose_file') String composeFile, @JsonKey(name: 'backup_folder') String? backupFolder, String status) $default,) {final _that = this; +switch (_that) { +case _ServiceScanInfo(): +return $default(_that.name,_that.composeFile,_that.backupFolder,_that.status);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String name, @JsonKey(name: 'compose_file') String composeFile, @JsonKey(name: 'backup_folder') String? backupFolder, String status)? $default,) {final _that = this; +switch (_that) { +case _ServiceScanInfo() when $default != null: +return $default(_that.name,_that.composeFile,_that.backupFolder,_that.status);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _ServiceScanInfo implements ServiceScanInfo { + const _ServiceScanInfo({required this.name, @JsonKey(name: 'compose_file') required this.composeFile, @JsonKey(name: 'backup_folder') this.backupFolder, this.status = 'unknown'}); + factory _ServiceScanInfo.fromJson(Map json) => _$ServiceScanInfoFromJson(json); + +@override final String name; +@override@JsonKey(name: 'compose_file') final String composeFile; +@override@JsonKey(name: 'backup_folder') final String? backupFolder; +@override@JsonKey() final String status; + +/// Create a copy of ServiceScanInfo +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ServiceScanInfoCopyWith<_ServiceScanInfo> get copyWith => __$ServiceScanInfoCopyWithImpl<_ServiceScanInfo>(this, _$identity); + +@override +Map toJson() { + return _$ServiceScanInfoToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ServiceScanInfo&&(identical(other.name, name) || other.name == name)&&(identical(other.composeFile, composeFile) || other.composeFile == composeFile)&&(identical(other.backupFolder, backupFolder) || other.backupFolder == backupFolder)&&(identical(other.status, status) || other.status == status)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,name,composeFile,backupFolder,status); + +@override +String toString() { + return 'ServiceScanInfo(name: $name, composeFile: $composeFile, backupFolder: $backupFolder, status: $status)'; +} + + +} + +/// @nodoc +abstract mixin class _$ServiceScanInfoCopyWith<$Res> implements $ServiceScanInfoCopyWith<$Res> { + factory _$ServiceScanInfoCopyWith(_ServiceScanInfo value, $Res Function(_ServiceScanInfo) _then) = __$ServiceScanInfoCopyWithImpl; +@override @useResult +$Res call({ + String name,@JsonKey(name: 'compose_file') String composeFile,@JsonKey(name: 'backup_folder') String? backupFolder, String status +}); + + + + +} +/// @nodoc +class __$ServiceScanInfoCopyWithImpl<$Res> + implements _$ServiceScanInfoCopyWith<$Res> { + __$ServiceScanInfoCopyWithImpl(this._self, this._then); + + final _ServiceScanInfo _self; + final $Res Function(_ServiceScanInfo) _then; + +/// Create a copy of ServiceScanInfo +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? composeFile = null,Object? backupFolder = freezed,Object? status = null,}) { + return _then(_ServiceScanInfo( +name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,composeFile: null == composeFile ? _self.composeFile : composeFile // ignore: cast_nullable_to_non_nullable +as String,backupFolder: freezed == backupFolder ? _self.backupFolder : backupFolder // ignore: cast_nullable_to_non_nullable +as String?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/service_scan_info.g.dart b/lib/core/models/service_scan_info.g.dart new file mode 100644 index 0000000..2848382 --- /dev/null +++ b/lib/core/models/service_scan_info.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'service_scan_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_ServiceScanInfo _$ServiceScanInfoFromJson(Map json) => + _ServiceScanInfo( + name: json['name'] as String, + composeFile: json['compose_file'] as String, + backupFolder: json['backup_folder'] as String?, + status: json['status'] as String? ?? 'unknown', + ); + +Map _$ServiceScanInfoToJson(_ServiceScanInfo instance) => + { + 'name': instance.name, + 'compose_file': instance.composeFile, + 'backup_folder': instance.backupFolder, + 'status': instance.status, + }; diff --git a/lib/core/models/severity_mapping.dart b/lib/core/models/severity_mapping.dart new file mode 100644 index 0000000..01faf9e --- /dev/null +++ b/lib/core/models/severity_mapping.dart @@ -0,0 +1,16 @@ +import '../theme/status_colors.dart'; + +/// Maps the API's untyped status strings to a [Severity] for [StatusPill] +/// rendering. Centralized here (not per-screen) since `Service.status` is +/// rendered on both the Services table and Fleet's aggregated card grid. +Severity severityForServiceStatus(String status) => switch (status) { + 'up' => Severity.good, + 'down' => Severity.critical, + _ => Severity.warning, // "unknown" or anything this client doesn't recognize yet +}; + +/// Maps a backup/update execution's `success` flag to a [Severity]. +Severity severityForSuccess(bool success) => success ? Severity.good : Severity.critical; + +/// Maps a [RemoteNode.status]/reachability signal to a [Severity]. +Severity severityForNodeReachable(bool reachable) => reachable ? Severity.good : Severity.critical; diff --git a/lib/core/models/update_record.dart b/lib/core/models/update_record.dart new file mode 100644 index 0000000..36c651c --- /dev/null +++ b/lib/core/models/update_record.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'update_record.freezed.dart'; +part 'update_record.g.dart'; + +/// Mirrors `models.UpdateRecord` — an OS package update log entry. +@freezed +sealed class UpdateRecord with _$UpdateRecord { + const factory UpdateRecord({ + @Default('') String id, + required DateTime timestamp, + required bool success, + @Default([]) List packages, + @Default('') String message, + }) = _UpdateRecord; + + factory UpdateRecord.fromJson(Map json) => _$UpdateRecordFromJson(json); +} diff --git a/lib/core/models/update_record.freezed.dart b/lib/core/models/update_record.freezed.dart new file mode 100644 index 0000000..482498a --- /dev/null +++ b/lib/core/models/update_record.freezed.dart @@ -0,0 +1,289 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'update_record.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$UpdateRecord { + + String get id; DateTime get timestamp; bool get success; List get packages; String get message; +/// Create a copy of UpdateRecord +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$UpdateRecordCopyWith get copyWith => _$UpdateRecordCopyWithImpl(this as UpdateRecord, _$identity); + + /// Serializes this UpdateRecord to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.success, success) || other.success == success)&&const DeepCollectionEquality().equals(other.packages, packages)&&(identical(other.message, message) || other.message == message)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,timestamp,success,const DeepCollectionEquality().hash(packages),message); + +@override +String toString() { + return 'UpdateRecord(id: $id, timestamp: $timestamp, success: $success, packages: $packages, message: $message)'; +} + + +} + +/// @nodoc +abstract mixin class $UpdateRecordCopyWith<$Res> { + factory $UpdateRecordCopyWith(UpdateRecord value, $Res Function(UpdateRecord) _then) = _$UpdateRecordCopyWithImpl; +@useResult +$Res call({ + String id, DateTime timestamp, bool success, List packages, String message +}); + + + + +} +/// @nodoc +class _$UpdateRecordCopyWithImpl<$Res> + implements $UpdateRecordCopyWith<$Res> { + _$UpdateRecordCopyWithImpl(this._self, this._then); + + final UpdateRecord _self; + final $Res Function(UpdateRecord) _then; + +/// Create a copy of UpdateRecord +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? timestamp = null,Object? success = null,Object? packages = null,Object? message = null,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,timestamp: null == timestamp ? _self.timestamp : timestamp // ignore: cast_nullable_to_non_nullable +as DateTime,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool,packages: null == packages ? _self.packages : packages // ignore: cast_nullable_to_non_nullable +as List,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [UpdateRecord]. +extension UpdateRecordPatterns on UpdateRecord { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _UpdateRecord value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _UpdateRecord() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _UpdateRecord value) $default,){ +final _that = this; +switch (_that) { +case _UpdateRecord(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _UpdateRecord value)? $default,){ +final _that = this; +switch (_that) { +case _UpdateRecord() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, DateTime timestamp, bool success, List packages, String message)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _UpdateRecord() when $default != null: +return $default(_that.id,_that.timestamp,_that.success,_that.packages,_that.message);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, DateTime timestamp, bool success, List packages, String message) $default,) {final _that = this; +switch (_that) { +case _UpdateRecord(): +return $default(_that.id,_that.timestamp,_that.success,_that.packages,_that.message);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, DateTime timestamp, bool success, List packages, String message)? $default,) {final _that = this; +switch (_that) { +case _UpdateRecord() when $default != null: +return $default(_that.id,_that.timestamp,_that.success,_that.packages,_that.message);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _UpdateRecord implements UpdateRecord { + const _UpdateRecord({this.id = '', required this.timestamp, required this.success, final List packages = const [], this.message = ''}): _packages = packages; + factory _UpdateRecord.fromJson(Map json) => _$UpdateRecordFromJson(json); + +@override@JsonKey() final String id; +@override final DateTime timestamp; +@override final bool success; + final List _packages; +@override@JsonKey() List get packages { + if (_packages is EqualUnmodifiableListView) return _packages; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_packages); +} + +@override@JsonKey() final String message; + +/// Create a copy of UpdateRecord +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$UpdateRecordCopyWith<_UpdateRecord> get copyWith => __$UpdateRecordCopyWithImpl<_UpdateRecord>(this, _$identity); + +@override +Map toJson() { + return _$UpdateRecordToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.success, success) || other.success == success)&&const DeepCollectionEquality().equals(other._packages, _packages)&&(identical(other.message, message) || other.message == message)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,timestamp,success,const DeepCollectionEquality().hash(_packages),message); + +@override +String toString() { + return 'UpdateRecord(id: $id, timestamp: $timestamp, success: $success, packages: $packages, message: $message)'; +} + + +} + +/// @nodoc +abstract mixin class _$UpdateRecordCopyWith<$Res> implements $UpdateRecordCopyWith<$Res> { + factory _$UpdateRecordCopyWith(_UpdateRecord value, $Res Function(_UpdateRecord) _then) = __$UpdateRecordCopyWithImpl; +@override @useResult +$Res call({ + String id, DateTime timestamp, bool success, List packages, String message +}); + + + + +} +/// @nodoc +class __$UpdateRecordCopyWithImpl<$Res> + implements _$UpdateRecordCopyWith<$Res> { + __$UpdateRecordCopyWithImpl(this._self, this._then); + + final _UpdateRecord _self; + final $Res Function(_UpdateRecord) _then; + +/// Create a copy of UpdateRecord +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? timestamp = null,Object? success = null,Object? packages = null,Object? message = null,}) { + return _then(_UpdateRecord( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,timestamp: null == timestamp ? _self.timestamp : timestamp // ignore: cast_nullable_to_non_nullable +as DateTime,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool,packages: null == packages ? _self._packages : packages // ignore: cast_nullable_to_non_nullable +as List,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/core/models/update_record.g.dart b/lib/core/models/update_record.g.dart new file mode 100644 index 0000000..29f0eed --- /dev/null +++ b/lib/core/models/update_record.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'update_record.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_UpdateRecord _$UpdateRecordFromJson(Map json) => + _UpdateRecord( + id: json['id'] as String? ?? '', + timestamp: DateTime.parse(json['timestamp'] as String), + success: json['success'] as bool, + packages: + (json['packages'] as List?) + ?.map((e) => e as String) + .toList() ?? + const [], + message: json['message'] as String? ?? '', + ); + +Map _$UpdateRecordToJson(_UpdateRecord instance) => + { + 'id': instance.id, + 'timestamp': instance.timestamp.toIso8601String(), + 'success': instance.success, + 'packages': instance.packages, + 'message': instance.message, + }; diff --git a/lib/core/network/api_client_provider.dart b/lib/core/network/api_client_provider.dart new file mode 100644 index 0000000..2f16df4 --- /dev/null +++ b/lib/core/network/api_client_provider.dart @@ -0,0 +1,41 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../connections/connection_providers.dart'; +import 'node_master_api_client.dart'; + +part 'api_client_provider.g.dart'; + +/// Rebuilt (not mutated) whenever the active connection changes, so every +/// watcher automatically rebuilds against the new host with no manual +/// invalidation elsewhere. Returns null when no connection is configured — +/// `app_router.dart` redirects to Settings in that case rather than every +/// screen needing its own "no connection" branch. +@riverpod +NodeMasterApiClient? apiClient(Ref ref) { + final connection = ref.watch(activeConnectionProvider); + if (connection == null) return null; + + final cancelToken = CancelToken(); + ref.onDispose(() => cancelToken.cancel('Active connection changed.')); + + final baseUrl = connection.baseUrl.endsWith('/') + ? connection.baseUrl.substring(0, connection.baseUrl.length - 1) + : connection.baseUrl; + + final dio = Dio( + BaseOptions( + baseUrl: '$baseUrl/v1', + connectTimeout: const Duration(seconds: 8), + sendTimeout: const Duration(seconds: 8), + receiveTimeout: const Duration(seconds: 15), + contentType: 'application/json', + ), + ); + if (kDebugMode) { + dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true)); + } + + return NodeMasterApiClient(dio, cancelToken); +} diff --git a/lib/core/network/api_client_provider.g.dart b/lib/core/network/api_client_provider.g.dart new file mode 100644 index 0000000..9bb866e --- /dev/null +++ b/lib/core/network/api_client_provider.g.dart @@ -0,0 +1,73 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'api_client_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// Rebuilt (not mutated) whenever the active connection changes, so every +/// watcher automatically rebuilds against the new host with no manual +/// invalidation elsewhere. Returns null when no connection is configured — +/// `app_router.dart` redirects to Settings in that case rather than every +/// screen needing its own "no connection" branch. + +@ProviderFor(apiClient) +const apiClientProvider = ApiClientProvider._(); + +/// Rebuilt (not mutated) whenever the active connection changes, so every +/// watcher automatically rebuilds against the new host with no manual +/// invalidation elsewhere. Returns null when no connection is configured — +/// `app_router.dart` redirects to Settings in that case rather than every +/// screen needing its own "no connection" branch. + +final class ApiClientProvider + extends + $FunctionalProvider< + NodeMasterApiClient?, + NodeMasterApiClient?, + NodeMasterApiClient? + > + with $Provider { + /// Rebuilt (not mutated) whenever the active connection changes, so every + /// watcher automatically rebuilds against the new host with no manual + /// invalidation elsewhere. Returns null when no connection is configured — + /// `app_router.dart` redirects to Settings in that case rather than every + /// screen needing its own "no connection" branch. + const ApiClientProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'apiClientProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$apiClientHash(); + + @$internal + @override + $ProviderElement $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + NodeMasterApiClient? create(Ref ref) { + return apiClient(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(NodeMasterApiClient? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$apiClientHash() => r'0a33d0449a51fd825f43bdcddd686bd413ff74ea'; diff --git a/lib/core/network/api_exception.dart b/lib/core/network/api_exception.dart new file mode 100644 index 0000000..bdf3b06 --- /dev/null +++ b/lib/core/network/api_exception.dart @@ -0,0 +1,44 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'api_exception.freezed.dart'; + +/// Typed errors thrown by [NodeMasterApiClient]/repositories. Riverpod's +/// `AsyncValue` (via `FutureProvider`/`AsyncNotifier`) already catches +/// thrown exceptions into `AsyncValue.error`, so this is the result-type +/// boundary — no separate `Result` wrapper is layered on top. +@freezed +sealed class ApiException with _$ApiException implements Exception { + /// DNS/connection failure — couldn't reach the host at all. + const factory ApiException.network(String message) = ApiNetworkException; + + /// Connect/send/receive timeout. + const factory ApiException.timeout() = ApiTimeoutException; + + /// The server responded with a 4xx/5xx and (usually) a `{"error": msg}` + /// body. + const factory ApiException.server(int statusCode, String message) = ApiServerException; + + /// The response body wasn't the JSON shape a model expected. + const factory ApiException.parse(String message) = ApiParseException; + + /// Request was cancelled (e.g. the active connection changed mid-flight). + const factory ApiException.cancelled() = ApiCancelledException; + + /// Anything else. + const factory ApiException.unknown(String message) = ApiUnknownException; +} + +extension ApiExceptionUserMessage on ApiException { + /// Short, user-facing copy — screens can use this directly or switch on + /// the exception type themselves for more specific handling. + String get userMessage => switch (this) { + ApiNetworkException() => "Can't reach this node — check the URL and that it's reachable.", + ApiTimeoutException() => 'The node took too long to respond.', + ApiServerException(:final statusCode, :final message) => statusCode == 404 + ? 'Not found.' + : 'Server error ($statusCode): $message', + ApiParseException() => 'Received an unexpected response from the node.', + ApiCancelledException() => 'Cancelled.', + ApiUnknownException(:final message) => message, + }; +} diff --git a/lib/core/network/api_exception.freezed.dart b/lib/core/network/api_exception.freezed.dart new file mode 100644 index 0000000..cc34327 --- /dev/null +++ b/lib/core/network/api_exception.freezed.dart @@ -0,0 +1,528 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'api_exception.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$ApiException { + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiException); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'ApiException()'; +} + + +} + +/// @nodoc +class $ApiExceptionCopyWith<$Res> { +$ApiExceptionCopyWith(ApiException _, $Res Function(ApiException) __); +} + + +/// Adds pattern-matching-related methods to [ApiException]. +extension ApiExceptionPatterns on ApiException { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap({TResult Function( ApiNetworkException value)? network,TResult Function( ApiTimeoutException value)? timeout,TResult Function( ApiServerException value)? server,TResult Function( ApiParseException value)? parse,TResult Function( ApiCancelledException value)? cancelled,TResult Function( ApiUnknownException value)? unknown,required TResult orElse(),}){ +final _that = this; +switch (_that) { +case ApiNetworkException() when network != null: +return network(_that);case ApiTimeoutException() when timeout != null: +return timeout(_that);case ApiServerException() when server != null: +return server(_that);case ApiParseException() when parse != null: +return parse(_that);case ApiCancelledException() when cancelled != null: +return cancelled(_that);case ApiUnknownException() when unknown != null: +return unknown(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map({required TResult Function( ApiNetworkException value) network,required TResult Function( ApiTimeoutException value) timeout,required TResult Function( ApiServerException value) server,required TResult Function( ApiParseException value) parse,required TResult Function( ApiCancelledException value) cancelled,required TResult Function( ApiUnknownException value) unknown,}){ +final _that = this; +switch (_that) { +case ApiNetworkException(): +return network(_that);case ApiTimeoutException(): +return timeout(_that);case ApiServerException(): +return server(_that);case ApiParseException(): +return parse(_that);case ApiCancelledException(): +return cancelled(_that);case ApiUnknownException(): +return unknown(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull({TResult? Function( ApiNetworkException value)? network,TResult? Function( ApiTimeoutException value)? timeout,TResult? Function( ApiServerException value)? server,TResult? Function( ApiParseException value)? parse,TResult? Function( ApiCancelledException value)? cancelled,TResult? Function( ApiUnknownException value)? unknown,}){ +final _that = this; +switch (_that) { +case ApiNetworkException() when network != null: +return network(_that);case ApiTimeoutException() when timeout != null: +return timeout(_that);case ApiServerException() when server != null: +return server(_that);case ApiParseException() when parse != null: +return parse(_that);case ApiCancelledException() when cancelled != null: +return cancelled(_that);case ApiUnknownException() when unknown != null: +return unknown(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen({TResult Function( String message)? network,TResult Function()? timeout,TResult Function( int statusCode, String message)? server,TResult Function( String message)? parse,TResult Function()? cancelled,TResult Function( String message)? unknown,required TResult orElse(),}) {final _that = this; +switch (_that) { +case ApiNetworkException() when network != null: +return network(_that.message);case ApiTimeoutException() when timeout != null: +return timeout();case ApiServerException() when server != null: +return server(_that.statusCode,_that.message);case ApiParseException() when parse != null: +return parse(_that.message);case ApiCancelledException() when cancelled != null: +return cancelled();case ApiUnknownException() when unknown != null: +return unknown(_that.message);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when({required TResult Function( String message) network,required TResult Function() timeout,required TResult Function( int statusCode, String message) server,required TResult Function( String message) parse,required TResult Function() cancelled,required TResult Function( String message) unknown,}) {final _that = this; +switch (_that) { +case ApiNetworkException(): +return network(_that.message);case ApiTimeoutException(): +return timeout();case ApiServerException(): +return server(_that.statusCode,_that.message);case ApiParseException(): +return parse(_that.message);case ApiCancelledException(): +return cancelled();case ApiUnknownException(): +return unknown(_that.message);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull({TResult? Function( String message)? network,TResult? Function()? timeout,TResult? Function( int statusCode, String message)? server,TResult? Function( String message)? parse,TResult? Function()? cancelled,TResult? Function( String message)? unknown,}) {final _that = this; +switch (_that) { +case ApiNetworkException() when network != null: +return network(_that.message);case ApiTimeoutException() when timeout != null: +return timeout();case ApiServerException() when server != null: +return server(_that.statusCode,_that.message);case ApiParseException() when parse != null: +return parse(_that.message);case ApiCancelledException() when cancelled != null: +return cancelled();case ApiUnknownException() when unknown != null: +return unknown(_that.message);case _: + return null; + +} +} + +} + +/// @nodoc + + +class ApiNetworkException implements ApiException { + const ApiNetworkException(this.message); + + + final String message; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ApiNetworkExceptionCopyWith get copyWith => _$ApiNetworkExceptionCopyWithImpl(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiNetworkException&&(identical(other.message, message) || other.message == message)); +} + + +@override +int get hashCode => Object.hash(runtimeType,message); + +@override +String toString() { + return 'ApiException.network(message: $message)'; +} + + +} + +/// @nodoc +abstract mixin class $ApiNetworkExceptionCopyWith<$Res> implements $ApiExceptionCopyWith<$Res> { + factory $ApiNetworkExceptionCopyWith(ApiNetworkException value, $Res Function(ApiNetworkException) _then) = _$ApiNetworkExceptionCopyWithImpl; +@useResult +$Res call({ + String message +}); + + + + +} +/// @nodoc +class _$ApiNetworkExceptionCopyWithImpl<$Res> + implements $ApiNetworkExceptionCopyWith<$Res> { + _$ApiNetworkExceptionCopyWithImpl(this._self, this._then); + + final ApiNetworkException _self; + final $Res Function(ApiNetworkException) _then; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') $Res call({Object? message = null,}) { + return _then(ApiNetworkException( +null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +/// @nodoc + + +class ApiTimeoutException implements ApiException { + const ApiTimeoutException(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiTimeoutException); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'ApiException.timeout()'; +} + + +} + + + + +/// @nodoc + + +class ApiServerException implements ApiException { + const ApiServerException(this.statusCode, this.message); + + + final int statusCode; + final String message; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ApiServerExceptionCopyWith get copyWith => _$ApiServerExceptionCopyWithImpl(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiServerException&&(identical(other.statusCode, statusCode) || other.statusCode == statusCode)&&(identical(other.message, message) || other.message == message)); +} + + +@override +int get hashCode => Object.hash(runtimeType,statusCode,message); + +@override +String toString() { + return 'ApiException.server(statusCode: $statusCode, message: $message)'; +} + + +} + +/// @nodoc +abstract mixin class $ApiServerExceptionCopyWith<$Res> implements $ApiExceptionCopyWith<$Res> { + factory $ApiServerExceptionCopyWith(ApiServerException value, $Res Function(ApiServerException) _then) = _$ApiServerExceptionCopyWithImpl; +@useResult +$Res call({ + int statusCode, String message +}); + + + + +} +/// @nodoc +class _$ApiServerExceptionCopyWithImpl<$Res> + implements $ApiServerExceptionCopyWith<$Res> { + _$ApiServerExceptionCopyWithImpl(this._self, this._then); + + final ApiServerException _self; + final $Res Function(ApiServerException) _then; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') $Res call({Object? statusCode = null,Object? message = null,}) { + return _then(ApiServerException( +null == statusCode ? _self.statusCode : statusCode // ignore: cast_nullable_to_non_nullable +as int,null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +/// @nodoc + + +class ApiParseException implements ApiException { + const ApiParseException(this.message); + + + final String message; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ApiParseExceptionCopyWith get copyWith => _$ApiParseExceptionCopyWithImpl(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiParseException&&(identical(other.message, message) || other.message == message)); +} + + +@override +int get hashCode => Object.hash(runtimeType,message); + +@override +String toString() { + return 'ApiException.parse(message: $message)'; +} + + +} + +/// @nodoc +abstract mixin class $ApiParseExceptionCopyWith<$Res> implements $ApiExceptionCopyWith<$Res> { + factory $ApiParseExceptionCopyWith(ApiParseException value, $Res Function(ApiParseException) _then) = _$ApiParseExceptionCopyWithImpl; +@useResult +$Res call({ + String message +}); + + + + +} +/// @nodoc +class _$ApiParseExceptionCopyWithImpl<$Res> + implements $ApiParseExceptionCopyWith<$Res> { + _$ApiParseExceptionCopyWithImpl(this._self, this._then); + + final ApiParseException _self; + final $Res Function(ApiParseException) _then; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') $Res call({Object? message = null,}) { + return _then(ApiParseException( +null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +/// @nodoc + + +class ApiCancelledException implements ApiException { + const ApiCancelledException(); + + + + + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiCancelledException); +} + + +@override +int get hashCode => runtimeType.hashCode; + +@override +String toString() { + return 'ApiException.cancelled()'; +} + + +} + + + + +/// @nodoc + + +class ApiUnknownException implements ApiException { + const ApiUnknownException(this.message); + + + final String message; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ApiUnknownExceptionCopyWith get copyWith => _$ApiUnknownExceptionCopyWithImpl(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ApiUnknownException&&(identical(other.message, message) || other.message == message)); +} + + +@override +int get hashCode => Object.hash(runtimeType,message); + +@override +String toString() { + return 'ApiException.unknown(message: $message)'; +} + + +} + +/// @nodoc +abstract mixin class $ApiUnknownExceptionCopyWith<$Res> implements $ApiExceptionCopyWith<$Res> { + factory $ApiUnknownExceptionCopyWith(ApiUnknownException value, $Res Function(ApiUnknownException) _then) = _$ApiUnknownExceptionCopyWithImpl; +@useResult +$Res call({ + String message +}); + + + + +} +/// @nodoc +class _$ApiUnknownExceptionCopyWithImpl<$Res> + implements $ApiUnknownExceptionCopyWith<$Res> { + _$ApiUnknownExceptionCopyWithImpl(this._self, this._then); + + final ApiUnknownException _self; + final $Res Function(ApiUnknownException) _then; + +/// Create a copy of ApiException +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') $Res call({Object? message = null,}) { + return _then(ApiUnknownException( +null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/core/network/dio_error_mapper.dart b/lib/core/network/dio_error_mapper.dart new file mode 100644 index 0000000..9e5aecf --- /dev/null +++ b/lib/core/network/dio_error_mapper.dart @@ -0,0 +1,39 @@ +import 'package:dio/dio.dart'; + +import 'api_exception.dart'; + +/// Maps a caught [DioException] (or any other error) to a typed +/// [ApiException]. The one place this translation happens — repositories +/// and the API client both funnel through this rather than inspecting +/// `DioException` directly. +ApiException mapDioException(Object error) { + if (error is ApiException) return error; + + if (error is! DioException) { + return ApiException.unknown(error.toString()); + } + + switch (error.type) { + case DioExceptionType.connectionTimeout: + case DioExceptionType.sendTimeout: + case DioExceptionType.receiveTimeout: + case DioExceptionType.transformTimeout: + return const ApiException.timeout(); + case DioExceptionType.connectionError: + return ApiException.network(error.message ?? 'Connection failed.'); + case DioExceptionType.cancel: + return const ApiException.cancelled(); + case DioExceptionType.badResponse: + final response = error.response; + final statusCode = response?.statusCode ?? 0; + final data = response?.data; + final message = data is Map && data['error'] is String + ? data['error'] as String + : (response?.statusMessage ?? 'Request failed.'); + return ApiException.server(statusCode, message); + case DioExceptionType.badCertificate: + return ApiException.network('Bad certificate: ${error.message ?? ''}'); + case DioExceptionType.unknown: + return ApiException.unknown(error.message ?? error.toString()); + } +} diff --git a/lib/core/network/node_master_api_client.dart b/lib/core/network/node_master_api_client.dart new file mode 100644 index 0000000..9407b55 --- /dev/null +++ b/lib/core/network/node_master_api_client.dart @@ -0,0 +1,261 @@ +import 'dart:async'; + +import 'package:dio/dio.dart'; + +import '../models/models.dart'; +import 'api_exception.dart'; +import 'dio_error_mapper.dart'; + +/// Thin typed wrapper over [Dio], one method per NodeMaster REST endpoint. +/// Every method funnels errors through [mapDioException] so callers only +/// ever see [ApiException]. Holds a [CancelToken] tied to the client's own +/// lifetime (cancelled by [apiClientProvider] when the active connection +/// changes), so a request from an abandoned connection can't resolve into +/// the next connection's UI state. +class NodeMasterApiClient { + NodeMasterApiClient(this._dio, this._cancelToken); + + final Dio _dio; + final CancelToken _cancelToken; + + /// The overall budget for one request, enforced by Dart's own + /// `Future.timeout` rather than relying solely on Dio's + /// `connectTimeout`/`receiveTimeout` `BaseOptions`. Dio's browser HTTP + /// adapter has no socket-level hooks to honor those (the underlying + /// `fetch`/`XMLHttpRequest` APIs don't expose a connect-phase timeout), + /// so on web a request to an unreachable host would otherwise hang + /// until the browser's own (much longer, sometimes unbounded) default — + /// this timeout is what actually bounds it on every platform. + static const _requestBudget = Duration(seconds: 20); + + Future _guard(Future Function() body) async { + try { + return await body().timeout(_requestBudget); + } on ApiException { + rethrow; + } on DioException catch (e) { + throw mapDioException(e); + } on TimeoutException { + throw const ApiException.timeout(); + } on TypeError catch (e) { + throw ApiException.parse(e.toString()); + } on FormatException catch (e) { + throw ApiException.parse(e.toString()); + } + } + + Future> _get(String path, {Map? query}) => _dio.get( + path, + queryParameters: query, + cancelToken: _cancelToken, + ); + + Future> _post(String path, {Object? data}) => _dio.post( + path, + data: data, + cancelToken: _cancelToken, + ); + + Future> _put(String path, {Object? data}) => _dio.put( + path, + data: data, + cancelToken: _cancelToken, + ); + + Future> _delete(String path) => _dio.delete(path, cancelToken: _cancelToken); + + // ---- node ---- + + Future getNode() => _guard(() async { + final res = await _get('/node/'); + return NodeInfo.fromJson(res.data as Map); + }); + + Future updateNode(NodeInfo node) => _guard(() async { + await _put('/node/', data: node.toJson()); + }); + + Future getNodeBackup() => _guard(() async { + final res = await _get('/node/backup'); + return BackupConfig.fromJson(res.data as Map); + }); + + Future updateNodeBackup(BackupConfig config) => _guard(() async { + await _put('/node/backup', data: config.toJson()); + }); + + Future runNodeBackup() => _guard(() async { + await _post('/node/backup/run'); + }); + + Future> getNodeBackupTargets() => _guard(() async { + final res = await _get('/node/backup/targets'); + return (res.data as List) + .map((e) => BackupTarget.fromJson(e as Map)) + .toList(); + }); + + Future addNodeBackupTarget(BackupTarget target) => _guard(() async { + final res = await _post('/node/backup/targets', data: target.toJson()); + return BackupTarget.fromJson(res.data as Map); + }); + + Future updateNodeBackupTarget(String targetId, BackupTarget target) => _guard(() async { + final res = await _put('/node/backup/targets/$targetId', data: target.toJson()); + return BackupTarget.fromJson(res.data as Map); + }); + + Future deleteNodeBackupTarget(String targetId) => _guard(() async { + await _delete('/node/backup/targets/$targetId'); + }); + + Future runNodeBackupTarget(String targetId) => _guard(() async { + await _post('/node/backup/targets/$targetId/run'); + }); + + Future getNodeBackupTargetProgress(String targetId) => _guard(() async { + final res = await _get('/node/backup/targets/$targetId/progress'); + return RunState.fromJson(res.data as Map); + }); + + Future> getNodeBackupProgress() => _guard(() async { + final res = await _get('/node/backup/progress'); + return (res.data as List) + .map((e) => RunState.fromJson(e as Map)) + .toList(); + }); + + Future scan({String? folder}) => _guard(() async { + final res = await _post('/node/scan', data: folder == null ? {} : {'folder': folder}); + return ScanResult.fromJson(res.data as Map); + }); + + Future> getUpdates() => _guard(() async { + final res = await _get('/node/updates'); + return (res.data as List) + .map((e) => UpdateRecord.fromJson(e as Map)) + .toList(); + }); + + Future addUpdate(UpdateRecord record) => _guard(() async { + final res = await _post('/node/updates', data: record.toJson()); + return UpdateRecord.fromJson(res.data as Map); + }); + + Future deleteUpdate(String id) => _guard(() async { + await _delete('/node/updates/$id'); + }); + + // ---- nodes (fleet registry) ---- + + Future> getAllRemoteNodes() => _guard(() async { + final res = await _get('/nodes/'); + return (res.data as List) + .map((e) => RemoteNode.fromJson(e as Map)) + .toList(); + }); + + Future addRemoteNode(RemoteNode node) => _guard(() async { + final res = await _post('/nodes/', data: node.toJson()); + return RemoteNode.fromJson(res.data as Map); + }); + + Future getRemoteNode(String id) => _guard(() async { + final res = await _get('/nodes/$id'); + return RemoteNode.fromJson(res.data as Map); + }); + + Future updateRemoteNode(String id, RemoteNode node) => _guard(() async { + final res = await _put('/nodes/$id', data: node.toJson()); + return RemoteNode.fromJson(res.data as Map); + }); + + Future deleteRemoteNode(String id) => _guard(() async { + await _delete('/nodes/$id'); + }); + + Future> getAggregated() => _guard(() async { + final res = await _get('/nodes/aggregated'); + return (res.data as List) + .map((e) => AggregatedNodeStatus.fromJson(e as Map)) + .toList(); + }); + + // ---- services ---- + + Future> getAllServices() => _guard(() async { + final res = await _get('/services/'); + return (res.data as List) + .map((e) => Service.fromJson(e as Map)) + .toList(); + }); + + Future addService(Service service) => _guard(() async { + final res = await _post('/services/', data: service.toJson()); + return Service.fromJson(res.data as Map); + }); + + Future getService(String id) => _guard(() async { + final res = await _get('/services/$id'); + return Service.fromJson(res.data as Map); + }); + + Future updateService(String id, Service service) => _guard(() async { + final res = await _put('/services/$id', data: service.toJson()); + return Service.fromJson(res.data as Map); + }); + + Future deleteService(String id) => _guard(() async { + await _delete('/services/$id'); + }); + + Future startService(String id) => _guard(() async { + await _post('/services/$id/start'); + }); + + Future stopService(String id) => _guard(() async { + await _post('/services/$id/stop'); + }); + + Future backupService(String id) => _guard(() async { + await _post('/services/$id/backup'); + }); + + Future> getServiceBackupTargets(String id) => _guard(() async { + final res = await _get('/services/$id/backup/targets'); + return (res.data as List) + .map((e) => BackupTarget.fromJson(e as Map)) + .toList(); + }); + + Future addServiceBackupTarget(String id, BackupTarget target) => _guard(() async { + final res = await _post('/services/$id/backup/targets', data: target.toJson()); + return BackupTarget.fromJson(res.data as Map); + }); + + Future updateServiceBackupTarget(String id, String targetId, BackupTarget target) => + _guard(() async { + final res = await _put('/services/$id/backup/targets/$targetId', data: target.toJson()); + return BackupTarget.fromJson(res.data as Map); + }); + + Future deleteServiceBackupTarget(String id, String targetId) => _guard(() async { + await _delete('/services/$id/backup/targets/$targetId'); + }); + + Future runServiceBackupTarget(String id, String targetId) => _guard(() async { + await _post('/services/$id/backup/targets/$targetId/run'); + }); + + Future getServiceBackupTargetProgress(String id, String targetId) => _guard(() async { + final res = await _get('/services/$id/backup/targets/$targetId/progress'); + return RunState.fromJson(res.data as Map); + }); + + Future> getServiceBackupProgress(String id) => _guard(() async { + final res = await _get('/services/$id/backup/progress'); + return (res.data as List) + .map((e) => RunState.fromJson(e as Map)) + .toList(); + }); +} diff --git a/lib/core/polling/polling_async_notifier.dart b/lib/core/polling/polling_async_notifier.dart new file mode 100644 index 0000000..9f538dd --- /dev/null +++ b/lib/core/polling/polling_async_notifier.dart @@ -0,0 +1,37 @@ +import 'dart:async'; + +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +/// Mixed into a `@riverpod` `AutoDisposeAsyncNotifier` to auto-refresh it on +/// a timer while it has at least one listener — Dashboard's summary and +/// Fleet's aggregated view, specifically. The API has no push/websocket, so +/// this is the honest refresh model: a manual pull plus a background +/// timer, not a live stream. +/// +/// Usage: `class FooNotifier extends _$FooNotifier with PollingMixin`, +/// implement [interval] and [fetch], and have `build()` return +/// `startPolling()`. +mixin PollingMixin on $AsyncNotifier { + /// How often to refetch while this provider has at least one listener. + Duration get interval; + + /// Performs the actual fetch. Thrown exceptions surface as + /// `AsyncValue.error` exactly like any other async provider. + Future fetch(); + + /// Call from `build()`: starts the periodic timer (cancelled on dispose) + /// and returns the initial fetch. + Future startPolling() { + final timer = Timer.periodic(interval, (_) => refresh()); + ref.onDispose(timer.cancel); + return fetch(); + } + + /// Re-fetches and updates state. Deliberately never emits an + /// intermediate `AsyncLoading` — the previous data/error stays on screen + /// until the new result lands, so a background tick never flashes the + /// UI back to a loading spinner. + Future refresh() async { + state = await AsyncValue.guard(fetch); + } +} diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart new file mode 100644 index 0000000..1933a44 --- /dev/null +++ b/lib/core/routing/app_router.dart @@ -0,0 +1,72 @@ +import 'package:flutter/foundation.dart'; +import 'package:go_router/go_router.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../features/backups/backups_screen.dart'; +import '../../features/dashboard/dashboard_screen.dart'; +import '../../features/fleet/fleet_screen.dart'; +import '../../features/services/services_screen.dart'; +import '../../features/settings/settings_screen.dart'; +import '../../features/updates/updates_screen.dart'; +import '../connections/connection_providers.dart'; +import '../widgets/nav_rail_shell.dart'; +import 'route_paths.dart'; + +part 'app_router.g.dart'; + +/// Bridges Riverpod's [activeConnectionProvider] to go_router's +/// [Listenable]-based `refreshListenable` — so removing/adding the active +/// connection re-evaluates the top-level redirect immediately, not just on +/// the next navigation. +class _ConnectionRefreshListenable extends ChangeNotifier { + _ConnectionRefreshListenable(Ref ref) { + ref.listen(activeConnectionProvider, (previous, next) => notifyListeners()); + } +} + +@Riverpod(keepAlive: true) +GoRouter appRouter(Ref ref) { + return GoRouter( + initialLocation: RoutePaths.dashboard, + refreshListenable: _ConnectionRefreshListenable(ref), + redirect: (context, state) { + final hasConnection = ref.read(activeConnectionProvider) != null; + final onSettings = state.matchedLocation == RoutePaths.settings; + if (!hasConnection && !onSettings) return RoutePaths.settings; + return null; + }, + routes: [ + ShellRoute( + builder: (context, state, child) { + return NavRailShell(currentPath: state.uri.path, child: child); + }, + routes: [ + GoRoute( + path: RoutePaths.dashboard, + builder: (context, state) => const DashboardScreen(), + ), + GoRoute( + path: RoutePaths.services, + builder: (context, state) => ServicesScreen(serviceId: state.uri.queryParameters['id']), + ), + GoRoute( + path: RoutePaths.backups, + builder: (context, state) => const BackupsScreen(), + ), + GoRoute( + path: RoutePaths.fleet, + builder: (context, state) => const FleetScreen(), + ), + GoRoute( + path: RoutePaths.updates, + builder: (context, state) => const UpdatesScreen(), + ), + GoRoute( + path: RoutePaths.settings, + builder: (context, state) => const SettingsScreen(), + ), + ], + ), + ], + ); +} diff --git a/lib/core/routing/app_router.g.dart b/lib/core/routing/app_router.g.dart new file mode 100644 index 0000000..80d58c9 --- /dev/null +++ b/lib/core/routing/app_router.g.dart @@ -0,0 +1,51 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'app_router.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(appRouter) +const appRouterProvider = AppRouterProvider._(); + +final class AppRouterProvider + extends $FunctionalProvider + with $Provider { + const AppRouterProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'appRouterProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$appRouterHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + GoRouter create(Ref ref) { + return appRouter(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(GoRouter value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$appRouterHash() => r'564cdc8b6bb47dea31d32b10bbfa1253170018af'; diff --git a/lib/core/routing/route_paths.dart b/lib/core/routing/route_paths.dart new file mode 100644 index 0000000..0f2673b --- /dev/null +++ b/lib/core/routing/route_paths.dart @@ -0,0 +1,17 @@ +/// Route path constants — the single source of truth so screens and the +/// nav rail never hand-type a path string more than once. +abstract final class RoutePaths { + static const dashboard = '/dashboard'; + static const services = '/services'; + static const backups = '/backups'; + static const fleet = '/fleet'; + static const updates = '/updates'; + static const settings = '/settings'; + + /// The service id is a query parameter (`?id=`), not a path segment. + /// `/services` and `/services?id=x` are the *same* go_router route, so + /// switching between them updates the existing page in place — no page + /// transition — which is what makes the slide-over panel read as an + /// overlay on the list rather than a navigation to a new screen. + static String serviceDetail(String id) => '/services?id=$id'; +} diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart new file mode 100644 index 0000000..d3add3d --- /dev/null +++ b/lib/core/theme/app_colors.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +/// Exact hex values from the approved design mockup. Hand-authored rather +/// than derived from [ColorScheme.fromSeed] so these values can't drift. +abstract final class AppColors { + // Light theme + static const lightBg = Color(0xFFF2F6F6); + static const lightSurface = Color(0xFFFFFFFF); + static const lightSurfaceAlt = Color(0xFFE9EFEF); + static const lightBorder = Color(0xFFDAE3E4); + static const lightText = Color(0xFF0E1B1D); + static const lightTextMuted = Color(0xFF47585B); + static const lightTextFaint = Color(0xFF7C9195); + static const lightAccent = Color(0xFF0E8E92); + static const lightAccentStrong = Color(0xFF0A7377); + static const lightAccentInk = Color(0xFFFFFFFF); + + // Dark theme + static const darkBg = Color(0xFF0C1315); + static const darkSurface = Color(0xFF121B1D); + static const darkSurfaceAlt = Color(0xFF182224); + static const darkBorder = Color(0xFF223032); + static const darkText = Color(0xFFE8F0F0); + static const darkTextMuted = Color(0xFFA9BFC2); + static const darkTextFaint = Color(0xFF6C8386); + static const darkAccent = Color(0xFF2FC6CA); + static const darkAccentStrong = Color(0xFF55D8DB); + static const darkAccentInk = Color(0xFF06282A); + + // Status — semantic, deliberately distinct from the accent hue, same + // across themes except `warning`, which is re-tuned per theme below to + // hold contrast against each surface. + static const good = Color(0xFF0CA30C); + static const warningLight = Color(0xFFC8890A); + static const warningDark = Color(0xFFE3A82B); + static const serious = Color(0xFFEC835A); + static const critical = Color(0xFFD03B3B); +} diff --git a/lib/core/theme/app_data_styles.dart b/lib/core/theme/app_data_styles.dart new file mode 100644 index 0000000..8c3f77d --- /dev/null +++ b/lib/core/theme/app_data_styles.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; + +/// Typography for "technical values" (compose paths, hostnames, ids, +/// timestamps) and for tabular numerals. These are two distinct rules from +/// the design spec, not one: +/// - `dataMono*` swaps the font *family* to JetBrains Mono. +/// - `numericTabular` keeps the UI sans (Inter) but turns on the +/// `tnum` font feature so digits line up in columns (stat tiles, +/// numeric table cells) without looking like a code snippet. +@immutable +class AppDataStyles extends ThemeExtension { + const AppDataStyles({ + required this.dataMono, + required this.dataMonoSmall, + required this.numericTabular, + }); + + final TextStyle dataMono; + final TextStyle dataMonoSmall; + final TextStyle numericTabular; + + static AppDataStyles resolve({required Color textColor, required Color mutedColor}) { + return AppDataStyles( + dataMono: TextStyle( + fontFamily: 'JetBrains Mono', + fontSize: 13, + color: mutedColor, + height: 1.3, + ), + dataMonoSmall: TextStyle( + fontFamily: 'JetBrains Mono', + fontSize: 11.5, + color: mutedColor, + height: 1.3, + ), + numericTabular: TextStyle( + fontFamily: 'Inter', + color: textColor, + fontFeatures: const [FontFeature.tabularFigures()], + ), + ); + } + + @override + AppDataStyles copyWith({ + TextStyle? dataMono, + TextStyle? dataMonoSmall, + TextStyle? numericTabular, + }) { + return AppDataStyles( + dataMono: dataMono ?? this.dataMono, + dataMonoSmall: dataMonoSmall ?? this.dataMonoSmall, + numericTabular: numericTabular ?? this.numericTabular, + ); + } + + @override + AppDataStyles lerp(ThemeExtension? other, double t) { + if (other is! AppDataStyles) return this; + return AppDataStyles( + dataMono: TextStyle.lerp(dataMono, other.dataMono, t)!, + dataMonoSmall: TextStyle.lerp(dataMonoSmall, other.dataMonoSmall, t)!, + numericTabular: TextStyle.lerp(numericTabular, other.numericTabular, t)!, + ); + } +} diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart new file mode 100644 index 0000000..60f2709 --- /dev/null +++ b/lib/core/theme/app_theme.dart @@ -0,0 +1,146 @@ +import 'package:flutter/material.dart'; + +import 'app_colors.dart'; +import 'app_data_styles.dart'; +import 'status_colors.dart'; + +/// Builds the app's light/dark [ThemeData]. Colors are constructed +/// explicitly from [AppColors] rather than via [ColorScheme.fromSeed] so the +/// approved mockup's exact hex values can't drift through algorithmic tone +/// generation. +abstract final class AppTheme { + static ThemeData light() => _build(brightness: Brightness.light); + static ThemeData dark() => _build(brightness: Brightness.dark); + + static ThemeData _build({required Brightness brightness}) { + final isDark = brightness == Brightness.dark; + + final colorScheme = isDark + ? const ColorScheme.dark( + surface: AppColors.darkBg, + onSurface: AppColors.darkText, + surfaceContainerHighest: AppColors.darkSurfaceAlt, + primary: AppColors.darkAccent, + onPrimary: AppColors.darkAccentInk, + secondary: AppColors.darkAccentStrong, + onSecondary: AppColors.darkAccentInk, + error: AppColors.critical, + onError: Colors.white, + outline: AppColors.darkBorder, + outlineVariant: AppColors.darkBorder, + ) + : const ColorScheme.light( + surface: AppColors.lightBg, + onSurface: AppColors.lightText, + surfaceContainerHighest: AppColors.lightSurfaceAlt, + primary: AppColors.lightAccent, + onPrimary: AppColors.lightAccentInk, + secondary: AppColors.lightAccentStrong, + onSecondary: AppColors.lightAccentInk, + error: AppColors.critical, + onError: Colors.white, + outline: AppColors.lightBorder, + outlineVariant: AppColors.lightBorder, + ); + + final surfaceCard = isDark ? AppColors.darkSurface : AppColors.lightSurface; + final textColor = isDark ? AppColors.darkText : AppColors.lightText; + final textMuted = isDark ? AppColors.darkTextMuted : AppColors.lightTextMuted; + final textFaint = isDark ? AppColors.darkTextFaint : AppColors.lightTextFaint; + final border = isDark ? AppColors.darkBorder : AppColors.lightBorder; + + final textTheme = TextTheme( + displaySmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 30, color: textColor, letterSpacing: -0.2), + headlineSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 22, color: textColor, letterSpacing: -0.1), + titleLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 17, color: textColor, letterSpacing: -0.1), + titleMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 14, color: textColor), + titleSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.6), + bodyLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w400, fontSize: 15, color: textColor, height: 1.5), + bodyMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w400, fontSize: 13.5, color: textColor, height: 1.45), + bodySmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w500, fontSize: 12, color: textMuted), + labelLarge: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13, color: textColor), + labelMedium: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 12, color: textMuted), + labelSmall: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.5), + ); + + return ThemeData( + useMaterial3: true, + brightness: brightness, + colorScheme: colorScheme, + scaffoldBackgroundColor: colorScheme.surface, + canvasColor: colorScheme.surface, + dividerColor: border, + fontFamily: 'Inter', + textTheme: textTheme, + cardTheme: CardThemeData( + color: surfaceCard, + elevation: 0, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide(color: border), + ), + ), + appBarTheme: AppBarTheme( + backgroundColor: colorScheme.surface, + foregroundColor: textColor, + elevation: 0, + surfaceTintColor: Colors.transparent, + ), + navigationRailTheme: NavigationRailThemeData( + backgroundColor: isDark ? AppColors.darkSurfaceAlt : AppColors.lightSurfaceAlt, + indicatorColor: colorScheme.primary.withValues(alpha: 0.16), + selectedIconTheme: IconThemeData(color: colorScheme.secondary), + unselectedIconTheme: IconThemeData(color: textMuted), + selectedLabelTextStyle: TextStyle(color: colorScheme.secondary, fontWeight: FontWeight.w700, fontSize: 12.5), + unselectedLabelTextStyle: TextStyle(color: textMuted, fontWeight: FontWeight.w500, fontSize: 12.5), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), + textStyle: const TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: textColor, + side: BorderSide(color: border), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), + textStyle: const TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600, fontSize: 13), + ), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: colorScheme.surface, + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(7), + borderSide: BorderSide(color: border), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(7), + borderSide: BorderSide(color: border), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(7), + borderSide: BorderSide(color: colorScheme.primary, width: 1.5), + ), + labelStyle: TextStyle(color: textMuted, fontSize: 12.5, fontWeight: FontWeight.w600), + ), + dataTableTheme: DataTableThemeData( + headingTextStyle: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w700, fontSize: 11, color: textFaint, letterSpacing: 0.5), + dataTextStyle: TextStyle(fontFamily: 'Inter', fontSize: 13, color: textColor), + dividerThickness: 1, + ), + extensions: [ + isDark ? StatusColors.dark : StatusColors.light, + AppDataStyles.resolve(textColor: textColor, mutedColor: textMuted), + ], + ); + } +} diff --git a/lib/core/theme/spacing.dart b/lib/core/theme/spacing.dart new file mode 100644 index 0000000..86d58fb --- /dev/null +++ b/lib/core/theme/spacing.dart @@ -0,0 +1,16 @@ +/// Spacing scale used across the app. Plain constants rather than a +/// [ThemeExtension] — spacing doesn't change between light/dark, so there's +/// no lerp behavior to gain from routing it through the theme mechanism. +abstract final class Spacing { + static const xs = 4.0; + static const sm = 8.0; + static const md = 12.0; + static const lg = 16.0; + static const xl = 20.0; + static const xxl = 28.0; + static const xxxl = 40.0; + + static const radiusSm = 6.0; + static const radiusMd = 8.0; + static const radiusLg = 10.0; +} diff --git a/lib/core/theme/status_colors.dart b/lib/core/theme/status_colors.dart new file mode 100644 index 0000000..9e7f9c9 --- /dev/null +++ b/lib/core/theme/status_colors.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +import 'app_colors.dart'; + +/// Semantic status colors (good/warning/serious/critical) used for service +/// up/down/unknown pills, backup/update success/fail pills, and stat-tile +/// severity stripes. Kept out of [ColorScheme] because Material only has a +/// single `error` role and these four are a distinct, reserved set that +/// must never be reused for anything else (e.g. a 4th categorical series). +@immutable +class StatusColors extends ThemeExtension { + const StatusColors({ + required this.good, + required this.warning, + required this.serious, + required this.critical, + }); + + final Color good; + final Color warning; + final Color serious; + final Color critical; + + static const light = StatusColors( + good: AppColors.good, + warning: AppColors.warningLight, + serious: AppColors.serious, + critical: AppColors.critical, + ); + + static const dark = StatusColors( + good: AppColors.good, + warning: AppColors.warningDark, + serious: AppColors.serious, + critical: AppColors.critical, + ); + + @override + StatusColors copyWith({ + Color? good, + Color? warning, + Color? serious, + Color? critical, + }) { + return StatusColors( + good: good ?? this.good, + warning: warning ?? this.warning, + serious: serious ?? this.serious, + critical: critical ?? this.critical, + ); + } + + @override + StatusColors lerp(ThemeExtension? other, double t) { + if (other is! StatusColors) return this; + return StatusColors( + good: Color.lerp(good, other.good, t)!, + warning: Color.lerp(warning, other.warning, t)!, + serious: Color.lerp(serious, other.serious, t)!, + critical: Color.lerp(critical, other.critical, t)!, + ); + } +} + +/// The four states a service / backup execution / update record / remote +/// node can render as a [StatusPill]. Kept as one enum shared across +/// features rather than per-feature status strings, so pill color mapping +/// lives in exactly one place. +enum Severity { good, warning, serious, critical, neutral } + +extension SeverityColor on Severity { + /// Resolves to a status color, or [muted] for [Severity.neutral] (which + /// has no reserved status hue — callers pass their theme's muted/outline + /// color for the "no data" / "not configured" case). + Color resolve(StatusColors colors, {required Color muted}) => switch (this) { + Severity.good => colors.good, + Severity.warning => colors.warning, + Severity.serious => colors.serious, + Severity.critical => colors.critical, + Severity.neutral => muted, + }; +} diff --git a/lib/core/theme/theme_mode_provider.dart b/lib/core/theme/theme_mode_provider.dart new file mode 100644 index 0000000..8029ed4 --- /dev/null +++ b/lib/core/theme/theme_mode_provider.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../utils/shared_preferences_provider.dart'; + +part 'theme_mode_provider.g.dart'; + +const _themeModeKey = 'theme_mode'; + +@Riverpod(keepAlive: true) +class ThemeModeController extends _$ThemeModeController { + @override + ThemeMode build() { + final stored = ref.watch(sharedPreferencesProvider).getString(_themeModeKey); + return switch (stored) { + 'light' => ThemeMode.light, + 'dark' => ThemeMode.dark, + _ => ThemeMode.system, + }; + } + + Future setThemeMode(ThemeMode mode) async { + state = mode; + await ref.read(sharedPreferencesProvider).setString(_themeModeKey, mode.name); + } +} diff --git a/lib/core/theme/theme_mode_provider.g.dart b/lib/core/theme/theme_mode_provider.g.dart new file mode 100644 index 0000000..0f47de9 --- /dev/null +++ b/lib/core/theme/theme_mode_provider.g.dart @@ -0,0 +1,64 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'theme_mode_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(ThemeModeController) +const themeModeControllerProvider = ThemeModeControllerProvider._(); + +final class ThemeModeControllerProvider + extends $NotifierProvider { + const ThemeModeControllerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'themeModeControllerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$themeModeControllerHash(); + + @$internal + @override + ThemeModeController create() => ThemeModeController(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(ThemeMode value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$themeModeControllerHash() => + r'521a0916c167062954c4b944c327fbeca64c294c'; + +abstract class _$ThemeModeController extends $Notifier { + ThemeMode build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + ThemeMode, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/lib/core/theme/theme_x.dart b/lib/core/theme/theme_x.dart new file mode 100644 index 0000000..5f78de5 --- /dev/null +++ b/lib/core/theme/theme_x.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +import 'app_data_styles.dart'; +import 'status_colors.dart'; + +/// Shorthand accessors so screens read `context.status.good` / +/// `context.dataStyles.dataMono` instead of the verbose +/// `Theme.of(context).extension<...>()!` at every call site. +extension ThemeX on BuildContext { + ThemeData get theme => Theme.of(this); + ColorScheme get colors => Theme.of(this).colorScheme; + TextTheme get text => Theme.of(this).textTheme; + StatusColors get status => Theme.of(this).extension()!; + AppDataStyles get dataStyles => Theme.of(this).extension()!; +} diff --git a/lib/core/utils/relative_time.dart b/lib/core/utils/relative_time.dart new file mode 100644 index 0000000..db3c2ae --- /dev/null +++ b/lib/core/utils/relative_time.dart @@ -0,0 +1,12 @@ +/// Formats a past [DateTime] as a short relative string ("2h ago", +/// "5d ago") for activity feeds and stat-tile captions — full timestamps +/// belong in tables (as [MonoText]), not here. +String formatRelative(DateTime dateTime) { + final diff = DateTime.now().difference(dateTime); + if (diff.inSeconds < 45) return 'just now'; + if (diff.inMinutes < 60) return '${diff.inMinutes}m ago'; + if (diff.inHours < 24) return '${diff.inHours}h ago'; + if (diff.inDays < 7) return '${diff.inDays}d ago'; + if (diff.inDays < 30) return '${(diff.inDays / 7).floor()}w ago'; + return '${(diff.inDays / 30).floor()}mo ago'; +} diff --git a/lib/core/utils/shared_preferences_provider.dart b/lib/core/utils/shared_preferences_provider.dart new file mode 100644 index 0000000..178a793 --- /dev/null +++ b/lib/core/utils/shared_preferences_provider.dart @@ -0,0 +1,12 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Overridden in `main()` with an instance obtained via +/// `SharedPreferences.getInstance()` before `runApp`. Left unimplemented by +/// default so a missing override fails loudly instead of silently using a +/// throwaway in-memory instance. +final sharedPreferencesProvider = Provider((ref) { + throw UnimplementedError( + 'sharedPreferencesProvider must be overridden in main() with a loaded SharedPreferences instance.', + ); +}); diff --git a/lib/core/widgets/backup_target_form_dialog.dart b/lib/core/widgets/backup_target_form_dialog.dart new file mode 100644 index 0000000..9189cb1 --- /dev/null +++ b/lib/core/widgets/backup_target_form_dialog.dart @@ -0,0 +1,146 @@ +import 'package:flutter/material.dart'; + +import '../models/models.dart'; +import '../network/api_exception.dart'; + +const _methods = BackupMethod.values; + +/// Add/edit dialog for a single [BackupTarget] — shared by the node Backups +/// screen and the Services detail panel, which otherwise only differ in +/// which repository [onSave] calls. Per-target `params` editing stays out +/// of scope here, same reasoning as the rest of the app: a power-user +/// escape hatch, not worth a nested key/value editor in v1. +Future showBackupTargetFormDialog( + BuildContext context, { + BackupTarget? existing, + required Future Function(BackupTarget draft) onSave, +}) { + return showDialog( + context: context, + builder: (context) => _BackupTargetFormDialog(existing: existing, onSave: onSave), + ); +} + +class _BackupTargetFormDialog extends StatefulWidget { + const _BackupTargetFormDialog({this.existing, required this.onSave}); + + final BackupTarget? existing; + final Future Function(BackupTarget draft) onSave; + + @override + State<_BackupTargetFormDialog> createState() => _BackupTargetFormDialogState(); +} + +class _BackupTargetFormDialogState extends State<_BackupTargetFormDialog> { + final _formKey = GlobalKey(); + late final _nameController = TextEditingController(text: widget.existing?.name); + late final _remoteController = TextEditingController(text: widget.existing?.remote); + late final _scheduleController = TextEditingController(text: widget.existing?.schedule); + late BackupMethod _method = widget.existing?.method ?? BackupMethod.restic; + bool _saving = false; + String? _error; + + @override + void dispose() { + _nameController.dispose(); + _remoteController.dispose(); + _scheduleController.dispose(); + super.dispose(); + } + + Future _save() async { + if (!(_formKey.currentState?.validate() ?? false)) return; + setState(() { + _saving = true; + _error = null; + }); + + final schedule = _scheduleController.text.trim(); + final draft = (widget.existing ?? const BackupTarget(name: '', method: BackupMethod.restic, remote: '')).copyWith( + name: _nameController.text.trim(), + method: _method, + remote: _remoteController.text.trim(), + schedule: schedule.isEmpty ? null : schedule, + ); + + try { + await widget.onSave(draft); + if (mounted) Navigator.of(context).pop(); + } catch (e) { + setState(() { + _saving = false; + _error = e is ApiException ? e.userMessage : e.toString(); + }); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.existing == null ? 'Add backup target' : 'Edit backup target'), + content: Form( + key: _formKey, + child: SizedBox( + width: 420, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: _nameController, + decoration: const InputDecoration(labelText: 'Name'), + validator: (v) => (v == null || v.trim().isEmpty) ? 'Required.' : null, + ), + const SizedBox(height: 14), + DropdownButtonFormField( + initialValue: _method, + decoration: const InputDecoration(labelText: 'Method'), + items: [for (final m in _methods) DropdownMenuItem(value: m, child: Text(m.name))], + onChanged: (v) => setState(() => _method = v ?? _method), + ), + const SizedBox(height: 14), + TextFormField( + controller: _remoteController, + decoration: InputDecoration( + labelText: 'Remote', + hintText: switch (_method) { + BackupMethod.restic => '/mnt/backup/repo or s3:s3.amazonaws.com/bucket/path or sftp:user@host:/path', + BackupMethod.rsync => 'user@host:/path', + BackupMethod.external => 'informational only — backup is managed outside the API', + }, + helperText: _method == BackupMethod.restic + ? 'Restic repository — encrypted with this node\'s restic password (Settings).' + : null, + ), + validator: (v) => (v == null || v.trim().isEmpty) ? 'Required.' : null, + ), + const SizedBox(height: 14), + TextFormField( + controller: _scheduleController, + decoration: const InputDecoration( + labelText: 'Schedule (optional)', + hintText: '0 2 * * * — standard 5-field cron; empty = manual only', + ), + ), + if (_error != null) ...[ + const SizedBox(height: 12), + Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ], + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: _saving ? null : () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + FilledButton( + onPressed: _saving ? null : _save, + child: _saving + ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) + : const Text('Save'), + ), + ], + ); + } +} diff --git a/lib/core/widgets/backup_target_row.dart b/lib/core/widgets/backup_target_row.dart new file mode 100644 index 0000000..fe19250 --- /dev/null +++ b/lib/core/widgets/backup_target_row.dart @@ -0,0 +1,190 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +import '../models/models.dart'; +import '../network/api_exception.dart'; +import '../theme/theme_x.dart'; +import 'mono_text.dart'; + +/// One backup target: identity + schedule/last-run caption, and either +/// Run/Edit/Delete actions or — while a run it triggered is in flight — a +/// live progress bar polled via [fetchProgress]. Polling is a plain local +/// [Timer] scoped to this row's lifetime, not a Riverpod provider: progress +/// only matters while this exact row is on screen and something is +/// actively running, so there's nothing to share or keep alive beyond that. +class BackupTargetRow extends StatefulWidget { + const BackupTargetRow({ + super.key, + required this.target, + required this.onRun, + required this.fetchProgress, + required this.onSettled, + required this.onEdit, + required this.onDelete, + }); + + final BackupTarget target; + final Future Function() onRun; + final Future Function() fetchProgress; + final void Function(RunState finalState) onSettled; + final VoidCallback onEdit; + final VoidCallback onDelete; + + @override + State createState() => _BackupTargetRowState(); +} + +class _BackupTargetRowState extends State { + Timer? _timer; + RunState? _runState; + bool _starting = false; + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + + Future _handleRun() async { + setState(() => _starting = true); + try { + await widget.onRun(); + setState(() { + _starting = false; + _runState = const RunState(running: true); + }); + _poll(); + _timer = Timer.periodic(const Duration(seconds: 2), (_) => _poll()); + } catch (e) { + if (!mounted) return; + setState(() => _starting = false); + final message = e is ApiException ? e.userMessage : e.toString(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } + + Future _poll() async { + final state = await widget.fetchProgress(); + if (!mounted) return; + setState(() => _runState = state); + if (!state.running) { + _timer?.cancel(); + _timer = null; + widget.onSettled(state); + } + } + + @override + Widget build(BuildContext context) { + final target = widget.target; + final running = _runState?.running ?? false; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Text(target.name, style: context.text.labelLarge), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1), + decoration: BoxDecoration( + color: context.colors.surfaceContainerHighest, + borderRadius: BorderRadius.circular(4), + ), + child: Text(target.method.name, style: context.dataStyles.dataMonoSmall), + ), + ], + ), + const SizedBox(height: 3), + MonoText(target.remote, small: true), + const SizedBox(height: 3), + Text(_caption(target), style: context.text.bodySmall), + ], + ), + ), + const SizedBox(width: 8), + if (running) + const SizedBox.shrink() + else ...[ + _starting + ? const Padding( + padding: EdgeInsets.all(8), + child: SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2)), + ) + : IconButton( + tooltip: 'Run now', + icon: const Icon(Icons.play_arrow_rounded, size: 20), + onPressed: _handleRun, + ), + IconButton( + tooltip: 'Edit', + icon: const Icon(Icons.edit_outlined, size: 18), + onPressed: widget.onEdit, + ), + IconButton( + tooltip: 'Remove', + icon: const Icon(Icons.delete_outline, size: 18), + onPressed: widget.onDelete, + ), + ], + ], + ), + if (running) ...[ + const SizedBox(height: 8), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: (_runState!.percent > 0 && _runState!.percent <= 100) + ? _runState!.percent / 100 + : null, + minHeight: 5, + ), + ), + const SizedBox(height: 4), + Row( + children: [ + Text( + '${_runState!.percent.toStringAsFixed(0)}%', + style: context.dataStyles.numericTabular.copyWith(fontSize: 12), + ), + if (_runState!.eta != null) ...[ + const SizedBox(width: 8), + Text('ETA ${_runState!.eta}', style: context.text.bodySmall), + ], + if (_runState!.message?.isNotEmpty == true) ...[ + const SizedBox(width: 8), + Expanded( + child: MonoText(_runState!.message!, small: true, maxLines: 1), + ), + ], + ], + ), + ], + ], + ), + ); + } + + String _caption(BackupTarget target) { + final dateFormat = DateFormat('MMM d, HH:mm'); + final parts = [ + target.schedule?.isNotEmpty == true ? 'Schedule: ${target.schedule}' : 'Manual only', + if (target.lastRun != null) 'last ${dateFormat.format(target.lastRun!.toLocal())}', + if (target.nextRun != null) 'next ${dateFormat.format(target.nextRun!.toLocal())}', + ]; + return parts.join(' · '); + } +} diff --git a/lib/core/widgets/backup_targets_list.dart b/lib/core/widgets/backup_targets_list.dart new file mode 100644 index 0000000..86c5561 --- /dev/null +++ b/lib/core/widgets/backup_targets_list.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +import '../models/models.dart'; +import '../theme/theme_x.dart'; +import 'backup_target_row.dart'; + +/// A list of [BackupTargetRow]s plus an "Add target" action — shared by the +/// node Backups screen and the Services detail panel. +class BackupTargetsList extends StatelessWidget { + const BackupTargetsList({ + super.key, + required this.targets, + required this.onAdd, + required this.onEdit, + required this.onDelete, + required this.onRun, + required this.fetchProgress, + required this.onSettled, + }); + + final List targets; + final VoidCallback onAdd; + final void Function(BackupTarget target) onEdit; + final void Function(BackupTarget target) onDelete; + final Future Function(BackupTarget target) onRun; + final Future Function(BackupTarget target) fetchProgress; + final void Function(BackupTarget target, RunState finalState) onSettled; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (targets.isEmpty) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Text('No backup targets configured.', style: context.text.bodySmall), + ) + else + for (var i = 0; i < targets.length; i++) ...[ + if (i > 0) const Divider(height: 1), + BackupTargetRow( + key: ValueKey(targets[i].id), + target: targets[i], + onRun: () => onRun(targets[i]), + fetchProgress: () => fetchProgress(targets[i]), + onSettled: (state) => onSettled(targets[i], state), + onEdit: () => onEdit(targets[i]), + onDelete: () => onDelete(targets[i]), + ), + ], + const SizedBox(height: 8), + OutlinedButton.icon( + onPressed: onAdd, + icon: const Icon(Icons.add, size: 16), + label: const Text('Add target'), + ), + ], + ); + } +} diff --git a/lib/core/widgets/data_table_scaffold.dart b/lib/core/widgets/data_table_scaffold.dart new file mode 100644 index 0000000..1dd64c0 --- /dev/null +++ b/lib/core/widgets/data_table_scaffold.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import '../theme/theme_x.dart'; + +/// The bordered, horizontally-scrollable card wrapper used by every dense +/// table in the app (Services, Fleet, Updates, Backup history). A thin +/// shell around [DataTable] — column/row content stays screen-specific, +/// this just guarantees the same card chrome, spacing, and empty-state +/// fallback everywhere. +class DataTableScaffold extends StatelessWidget { + const DataTableScaffold({ + super.key, + required this.columns, + required this.rows, + this.empty, + this.columnSpacing = 28, + this.horizontalMargin = 16, + }); + + final List columns; + final List rows; + final Widget? empty; + final double columnSpacing; + final double horizontalMargin; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: context.colors.surface, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: context.colors.outlineVariant), + ), + clipBehavior: Clip.antiAlias, + child: rows.isEmpty && empty != null + ? empty + : SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: MediaQuery.sizeOf(context).width), + child: DataTable( + showCheckboxColumn: false, + columns: columns, + rows: rows, + columnSpacing: columnSpacing, + horizontalMargin: horizontalMargin, + headingRowHeight: 38, + dataRowMinHeight: 46, + dataRowMaxHeight: 58, + dividerThickness: 1, + ), + ), + ), + ); + } +} diff --git a/lib/core/widgets/empty_state.dart b/lib/core/widgets/empty_state.dart new file mode 100644 index 0000000..6b404c2 --- /dev/null +++ b/lib/core/widgets/empty_state.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +import '../theme/theme_x.dart'; + +/// Centered "nothing here yet" placeholder — no services discovered, no +/// connections saved, no update history. An optional action gives the user +/// the next step rather than a dead end. +class EmptyState extends StatelessWidget { + const EmptyState({ + super.key, + required this.icon, + required this.title, + this.message, + this.actionLabel, + this.onAction, + }); + + final IconData icon; + final String title; + final String? message; + final String? actionLabel; + final VoidCallback? onAction; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 30, color: context.colors.outline), + const SizedBox(height: 12), + Text(title, style: context.text.titleMedium, textAlign: TextAlign.center), + if (message != null) ...[ + const SizedBox(height: 4), + Text( + message!, + style: context.text.bodySmall, + textAlign: TextAlign.center, + ), + ], + if (actionLabel != null && onAction != null) ...[ + const SizedBox(height: 16), + OutlinedButton(onPressed: onAction, child: Text(actionLabel!)), + ], + ], + ), + ); + } +} diff --git a/lib/core/widgets/error_banner.dart b/lib/core/widgets/error_banner.dart new file mode 100644 index 0000000..b054b43 --- /dev/null +++ b/lib/core/widgets/error_banner.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +import '../theme/theme_x.dart'; + +/// Inline error state for a failed fetch — an `AsyncValue.error` case in a +/// screen body. Deliberately takes a plain [message] string rather than an +/// `ApiException` so `core/widgets` has no dependency on `core/network`; +/// callers resolve `exception.userMessage` before passing it in. +class ErrorBanner extends StatelessWidget { + const ErrorBanner({super.key, required this.message, this.onRetry}); + + final String message; + final VoidCallback? onRetry; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Color.alphaBlend(context.status.critical.withValues(alpha: 0.08), context.colors.surface), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: context.status.critical.withValues(alpha: 0.4)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.error_outline, size: 18, color: context.status.critical), + const SizedBox(width: 12), + Expanded(child: Text(message, style: context.text.bodyMedium)), + if (onRetry != null) ...[ + const SizedBox(width: 12), + TextButton(onPressed: onRetry, child: const Text('Retry')), + ], + ], + ), + ); + } +} diff --git a/lib/core/widgets/mono_text.dart b/lib/core/widgets/mono_text.dart new file mode 100644 index 0000000..fdb56ca --- /dev/null +++ b/lib/core/widgets/mono_text.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import '../theme/theme_x.dart'; + +/// A technical value — compose file path, hostname, id, URL — rendered in +/// the monospace data face so it reads as "an exact string", distinct from +/// prose. Use for anything a user might copy-paste or match against a log. +class MonoText extends StatelessWidget { + const MonoText( + this.value, { + super.key, + this.small = false, + this.color, + this.maxLines = 1, + this.overflow = TextOverflow.ellipsis, + }); + + final String value; + final bool small; + final Color? color; + final int? maxLines; + final TextOverflow overflow; + + @override + Widget build(BuildContext context) { + final base = small ? context.dataStyles.dataMonoSmall : context.dataStyles.dataMono; + return Text( + value, + maxLines: maxLines, + overflow: overflow, + style: color == null ? base : base.copyWith(color: color), + ); + } +} diff --git a/lib/core/widgets/nav_rail_shell.dart b/lib/core/widgets/nav_rail_shell.dart new file mode 100644 index 0000000..de362d3 --- /dev/null +++ b/lib/core/widgets/nav_rail_shell.dart @@ -0,0 +1,295 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../connections/connection_providers.dart'; +import '../routing/route_paths.dart'; +import '../theme/theme_mode_provider.dart'; +import '../theme/theme_x.dart'; + +class _NavItem { + const _NavItem(this.path, this.label, this.icon, this.selectedIcon); + final String path; + final String label; + final IconData icon; + final IconData selectedIcon; +} + +const _navItems = [ + _NavItem(RoutePaths.dashboard, 'Dashboard', Icons.grid_view_outlined, Icons.grid_view_rounded), + _NavItem(RoutePaths.services, 'Services', Icons.layers_outlined, Icons.layers_rounded), + _NavItem(RoutePaths.backups, 'Backups', Icons.cloud_outlined, Icons.cloud_rounded), + _NavItem(RoutePaths.fleet, 'Fleet', Icons.hub_outlined, Icons.hub_rounded), + _NavItem(RoutePaths.updates, 'Updates', Icons.history_outlined, Icons.history_rounded), + _NavItem(RoutePaths.settings, 'Settings', Icons.tune_outlined, Icons.tune_rounded), +]; + +/// The persistent left nav rail + top bar frame wrapping every screen, per +/// the approved mockup. Wraps go_router's `ShellRoute` child. +class NavRailShell extends ConsumerWidget { + const NavRailShell({super.key, required this.currentPath, required this.child}); + + final String currentPath; + final Widget child; + + int get _selectedIndex { + final index = _navItems.indexWhere((i) => currentPath.startsWith(i.path)); + return index == -1 ? 0 : index; + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final narrow = MediaQuery.sizeOf(context).width < 860; + + return Scaffold( + body: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _Rail(selectedIndex: _selectedIndex, narrow: narrow), + const VerticalDivider(width: 1, thickness: 1), + Expanded( + child: Column( + children: [ + _TopBar(currentPath: currentPath), + const Divider(height: 1, thickness: 1), + Expanded(child: child), + ], + ), + ), + ], + ), + ); + } +} + +class _Rail extends ConsumerWidget { + const _Rail({required this.selectedIndex, required this.narrow}); + + final int selectedIndex; + final bool narrow; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final activeConnection = ref.watch(activeConnectionProvider); + + return SizedBox( + width: narrow ? 72 : 226, + child: Container( + color: context.theme.navigationRailTheme.backgroundColor, + child: SafeArea( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(14, 18, 14, 20), + child: Row( + mainAxisAlignment: narrow ? MainAxisAlignment.center : MainAxisAlignment.start, + children: [ + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: context.colors.primary, + borderRadius: BorderRadius.circular(8), + ), + child: Icon(Icons.hub_rounded, size: 16, color: context.colors.onPrimary), + ), + if (!narrow) ...[ + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('NodeMaster', style: context.text.titleMedium?.copyWith(fontSize: 14.5)), + Text('Manager', style: context.text.bodySmall), + ], + ), + ), + ], + ], + ), + ), + Expanded( + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 8), + children: [ + for (var i = 0; i < _navItems.length; i++) + _RailButton(item: _navItems[i], selected: i == selectedIndex, narrow: narrow), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(12), + child: _ConnectionChip(name: activeConnection?.name, url: activeConnection?.baseUrl, narrow: narrow), + ), + ], + ), + ), + ), + ); + } +} + +class _RailButton extends StatelessWidget { + const _RailButton({required this.item, required this.selected, required this.narrow}); + + final _NavItem item; + final bool selected; + final bool narrow; + + @override + Widget build(BuildContext context) { + final fg = selected ? context.colors.secondary : context.text.bodyMedium?.color; + final bg = selected ? context.colors.primary.withValues(alpha: 0.14) : Colors.transparent; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 1.5), + child: Material( + color: bg, + borderRadius: BorderRadius.circular(7), + child: InkWell( + borderRadius: BorderRadius.circular(7), + onTap: () => context.go(item.path), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: narrow ? 0 : 10, vertical: 9), + child: Row( + mainAxisAlignment: narrow ? MainAxisAlignment.center : MainAxisAlignment.start, + children: [ + Icon(selected ? item.selectedIcon : item.icon, size: 18, color: fg), + if (!narrow) ...[ + const SizedBox(width: 10), + Text( + item.label, + style: context.text.bodyMedium?.copyWith( + color: fg, + fontWeight: selected ? FontWeight.w700 : FontWeight.w500, + ), + ), + ], + ], + ), + ), + ), + ), + ); + } +} + +class _ConnectionChip extends StatelessWidget { + const _ConnectionChip({required this.name, required this.url, required this.narrow}); + + final String? name; + final String? url; + final bool narrow; + + @override + Widget build(BuildContext context) { + final dot = Container( + width: 7, + height: 7, + decoration: BoxDecoration( + color: name == null ? context.colors.outline : context.status.good, + shape: BoxShape.circle, + ), + ); + if (narrow) return Center(child: dot); + + return Container( + padding: const EdgeInsets.all(9), + decoration: BoxDecoration( + color: context.colors.surface, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: context.colors.outlineVariant), + ), + child: Row( + children: [ + dot, + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + name ?? 'No connection', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.text.labelLarge?.copyWith(fontSize: 12), + ), + if (url != null) + Text( + url!.replaceFirst(RegExp(r'^https?://'), ''), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.dataStyles.dataMonoSmall, + ), + ], + ), + ), + ], + ), + ); + } +} + +const _titles = { + RoutePaths.dashboard: ('Dashboard', _connSubtitle), + RoutePaths.services: ('Services', _connSubtitle), + RoutePaths.backups: ('Backups', _connSubtitle), + RoutePaths.fleet: ('Fleet', _connSubtitle), + RoutePaths.updates: ('Updates', _connSubtitle), + RoutePaths.settings: ('Settings', _connSubtitle), +}; + +String _connSubtitle(WidgetRef ref) { + final connection = ref.watch(activeConnectionProvider); + return connection == null ? 'No connection configured' : 'Connected to ${connection.name}'; +} + +class _TopBar extends ConsumerWidget { + const _TopBar({required this.currentPath}); + + final String currentPath; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final entry = _titles.entries.firstWhere( + (e) => currentPath.startsWith(e.key), + orElse: () => _titles.entries.first, + ); + final title = entry.value.$1; + final subtitle = entry.value.$2(ref); + + final mode = ref.watch(themeModeControllerProvider); + final effectiveDark = switch (mode) { + ThemeMode.dark => true, + ThemeMode.light => false, + ThemeMode.system => MediaQuery.platformBrightnessOf(context) == Brightness.dark, + }; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(title, style: context.text.titleLarge), + Text(subtitle, style: context.text.bodySmall), + ], + ), + ), + IconButton( + tooltip: effectiveDark ? 'Switch to light theme' : 'Switch to dark theme', + icon: Icon(effectiveDark ? Icons.dark_mode_outlined : Icons.light_mode_outlined), + onPressed: () => ref + .read(themeModeControllerProvider.notifier) + .setThemeMode(effectiveDark ? ThemeMode.light : ThemeMode.dark), + ), + ], + ), + ); + } +} diff --git a/lib/core/widgets/slide_over_panel.dart b/lib/core/widgets/slide_over_panel.dart new file mode 100644 index 0000000..c8e4dfe --- /dev/null +++ b/lib/core/widgets/slide_over_panel.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; + +import '../theme/theme_x.dart'; + +/// A right-anchored detail panel over the still-visible list behind it — +/// the "inspect a record without losing list context" pattern used by +/// Services (and reused by Fleet's per-node cards). Place as the last child +/// of a [Stack] alongside the screen's main content; it fills the stack +/// itself via [Positioned.fill] and no-ops when [open] is false. +class SlideOverPanel extends StatelessWidget { + const SlideOverPanel({ + super.key, + required this.open, + required this.onClose, + required this.title, + this.subtitle, + required this.body, + this.actions = const [], + this.headerActions = const [], + this.width = 420, + }); + + final bool open; + final VoidCallback onClose; + final String title; + final Widget? subtitle; + final Widget body; + final List actions; + + /// Icon buttons shown before the close button — for actions that belong + /// with the record identity (e.g. Edit) rather than the primary action + /// row at the bottom, which stays reserved for the few actions that need + /// full label width. + final List headerActions; + final double width; + + @override + Widget build(BuildContext context) { + final reduceMotion = MediaQuery.disableAnimationsOf(context); + final duration = reduceMotion ? Duration.zero : const Duration(milliseconds: 200); + + return Positioned.fill( + child: IgnorePointer( + ignoring: !open, + child: Stack( + children: [ + AnimatedOpacity( + opacity: open ? 1 : 0, + duration: duration, + child: GestureDetector( + onTap: onClose, + child: Container(color: Colors.black.withValues(alpha: 0.35)), + ), + ), + Align( + alignment: Alignment.centerRight, + child: ClipRect( + child: AnimatedSlide( + offset: open ? Offset.zero : const Offset(1, 0), + duration: duration, + curve: Curves.easeOutCubic, + child: Container( + width: width, + constraints: BoxConstraints(maxWidth: MediaQuery.sizeOf(context).width * 0.92), + height: double.infinity, + decoration: BoxDecoration( + color: context.colors.surface, + border: Border(left: BorderSide(color: context.colors.outlineVariant)), + boxShadow: [ + BoxShadow(color: Colors.black.withValues(alpha: 0.18), blurRadius: 48, offset: const Offset(-8, 0)), + ], + ), + child: SafeArea( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(20, 18, 12, 18), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(title, style: context.text.titleLarge), + if (subtitle != null) ...[ + const SizedBox(height: 6), + subtitle!, + ], + ], + ), + ), + ...headerActions, + IconButton( + onPressed: onClose, + icon: const Icon(Icons.close, size: 19), + tooltip: 'Close', + ), + ], + ), + ), + const Divider(height: 1), + Expanded(child: body), + if (actions.isNotEmpty) ...[ + const Divider(height: 1), + Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + for (var i = 0; i < actions.length; i++) ...[ + if (i > 0) const SizedBox(width: 8), + Expanded(child: actions[i]), + ], + ], + ), + ), + ], + ], + ), + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/core/widgets/sparkline.dart b/lib/core/widgets/sparkline.dart new file mode 100644 index 0000000..476cb05 --- /dev/null +++ b/lib/core/widgets/sparkline.dart @@ -0,0 +1,106 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +import '../theme/theme_x.dart'; + +/// A single-series trend line — currently just backup-duration history. +/// Hand-rolled `CustomPainter` rather than a charting dependency: one chart +/// in the whole app, a specific visual spec (faint grid, thin line, +/// emphasized endpoint), full control for near-zero cost. +class Sparkline extends StatelessWidget { + const Sparkline({super.key, required this.values, this.width = 240, this.height = 52}); + + final List values; + final double width; + final double height; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: width, + height: height, + child: CustomPaint( + painter: _SparklinePainter( + values: values, + lineColor: context.colors.primary, + gridColor: context.colors.outlineVariant, + ), + ), + ); + } +} + +class _SparklinePainter extends CustomPainter { + _SparklinePainter({required this.values, required this.lineColor, required this.gridColor}); + + final List values; + final Color lineColor; + final Color gridColor; + + static const _topPad = 6.0; + static const _bottomPad = 6.0; + + @override + void paint(Canvas canvas, Size size) { + if (values.isEmpty) return; + + final minV = values.reduce(math.min); + final maxV = values.reduce(math.max); + final range = (maxV - minV).abs() < 1e-9 ? 1.0 : maxV - minV; + final plotHeight = size.height - _topPad - _bottomPad; + + final gridPaint = Paint() + ..color = gridColor + ..strokeWidth = 1; + canvas.drawLine(Offset(0, _topPad), Offset(size.width, _topPad), gridPaint); + canvas.drawLine( + Offset(0, size.height - _bottomPad), + Offset(size.width, size.height - _bottomPad), + gridPaint, + ); + + if (values.length == 1) { + final y = _topPad + plotHeight / 2; + canvas.drawCircle(Offset(size.width, y), 3.4, Paint()..color = lineColor); + return; + } + + final dx = size.width / (values.length - 1); + final points = [ + for (var i = 0; i < values.length; i++) + Offset(i * dx, _topPad + plotHeight - ((values[i] - minV) / range) * plotHeight), + ]; + + final fillPath = Path()..moveTo(points.first.dx, size.height - _bottomPad); + for (final p in points) { + fillPath.lineTo(p.dx, p.dy); + } + fillPath + ..lineTo(points.last.dx, size.height - _bottomPad) + ..close(); + canvas.drawPath(fillPath, Paint()..color = lineColor.withValues(alpha: 0.14)); + + final linePath = Path()..moveTo(points.first.dx, points.first.dy); + for (final p in points.skip(1)) { + linePath.lineTo(p.dx, p.dy); + } + canvas.drawPath( + linePath, + Paint() + ..color = lineColor + ..style = PaintingStyle.stroke + ..strokeWidth = 2 + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round, + ); + + canvas.drawCircle(points.last, 3.4, Paint()..color = lineColor); + } + + @override + bool shouldRepaint(covariant _SparklinePainter oldDelegate) => + oldDelegate.values != values || + oldDelegate.lineColor != lineColor || + oldDelegate.gridColor != gridColor; +} diff --git a/lib/core/widgets/stat_tile.dart b/lib/core/widgets/stat_tile.dart new file mode 100644 index 0000000..6352141 --- /dev/null +++ b/lib/core/widgets/stat_tile.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +import '../theme/status_colors.dart'; +import '../theme/theme_x.dart'; + +/// A dashboard summary tile: an uppercase label, a large tabular-nums value +/// (with an optional muted trailing qualifier, e.g. "7 / 9"), a caption, and +/// a left severity stripe communicating worst-case state at a glance — +/// severity is a first-class visual channel here, not just the number. +class StatTile extends StatelessWidget { + const StatTile({ + super.key, + required this.label, + required this.value, + this.valueQualifier, + this.caption, + this.severity = Severity.good, + }); + + final String label; + final String value; + final String? valueQualifier; + final String? caption; + final Severity severity; + + @override + Widget build(BuildContext context) { + final stripeColor = severity.resolve(context.status, muted: context.colors.outline); + + return Container( + decoration: BoxDecoration( + color: context.colors.surface, + borderRadius: BorderRadius.circular(9), + border: Border.all(color: context.colors.outlineVariant), + boxShadow: [ + BoxShadow(color: Colors.black.withValues(alpha: 0.04), blurRadius: 2, offset: const Offset(0, 1)), + ], + ), + clipBehavior: Clip.antiAlias, + child: IntrinsicHeight( + child: Row( + children: [ + Container(width: 3, color: stripeColor), + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(13, 14, 13, 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(label, style: context.text.titleSmall), + const SizedBox(height: 8), + Text.rich( + TextSpan( + style: context.dataStyles.numericTabular.copyWith( + fontSize: 24, + fontWeight: FontWeight.w700, + letterSpacing: -0.3, + ), + children: [ + TextSpan(text: value), + if (valueQualifier != null) + TextSpan( + text: ' $valueQualifier', + style: context.text.bodyMedium?.copyWith(fontWeight: FontWeight.w500), + ), + ], + ), + ), + if (caption != null) ...[ + const SizedBox(height: 4), + Text( + caption!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.text.bodySmall, + ), + ], + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/core/widgets/status_pill.dart b/lib/core/widgets/status_pill.dart new file mode 100644 index 0000000..e3b1b56 --- /dev/null +++ b/lib/core/widgets/status_pill.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +import '../theme/status_colors.dart'; +import '../theme/theme_x.dart'; + +/// A dot + label badge encoding state — service up/down/unknown, backup or +/// update success/fail, node reachable/unreachable. The same component +/// everywhere a status appears so a given [Severity] always reads the same +/// color, matching the design rule that status color is reserved and never +/// doubles as the brand accent. +class StatusPill extends StatelessWidget { + const StatusPill({super.key, required this.label, required this.severity}); + + final String label; + final Severity severity; + + @override + Widget build(BuildContext context) { + final color = severity.resolve(context.status, muted: context.colors.outline); + + return Container( + padding: const EdgeInsets.fromLTRB(7, 3, 9, 3), + decoration: BoxDecoration( + // 8% tint, not 14% — measured against the dark surface, a same-hue + // tint background caps critical's text contrast around ~3.5:1 no + // matter the blend ratio (text and background converge toward the + // same hue as alpha rises, and approach the plain-surface ceiling + // as it falls); 8% is close to that ceiling while keeping + // good/warning/serious comfortably at 4.5:1+. Below AA-for-text on + // its own, mitigated the same way the design system's status + // palette always mitigates this: label text, never color alone. + color: severity == Severity.neutral + ? context.colors.surfaceContainerHighest + : Color.alphaBlend(color.withValues(alpha: 0.08), context.colors.surface), + borderRadius: BorderRadius.circular(999), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 6, + height: 6, + decoration: BoxDecoration(color: color, shape: BoxShape.circle), + ), + const SizedBox(width: 6), + Text( + label, + style: context.text.labelMedium?.copyWith( + color: severity == Severity.neutral ? context.colors.onSurfaceVariant : color, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ); + } +} diff --git a/lib/data/repositories/node_repository.dart b/lib/data/repositories/node_repository.dart new file mode 100644 index 0000000..24034ea --- /dev/null +++ b/lib/data/repositories/node_repository.dart @@ -0,0 +1,79 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../core/models/models.dart'; +import '../../core/network/api_client_provider.dart'; +import '../../core/network/node_master_api_client.dart'; + +part 'node_repository.g.dart'; + +/// Wraps the `/node/*` endpoints — this host's own identity, backup config, +/// scan trigger, and update log. +class NodeRepository { + const NodeRepository(this._client); + + final NodeMasterApiClient _client; + + Future getNode() => _client.getNode(); + Future updateNode(NodeInfo node) => _client.updateNode(node); + + Future getBackup() => _client.getNodeBackup(); + Future updateBackup(BackupConfig config) => _client.updateNodeBackup(config); + Future runBackup() => _client.runNodeBackup(); + + Future> getBackupTargets() => _client.getNodeBackupTargets(); + Future addBackupTarget(BackupTarget target) => _client.addNodeBackupTarget(target); + Future updateBackupTarget(String targetId, BackupTarget target) => + _client.updateNodeBackupTarget(targetId, target); + Future deleteBackupTarget(String targetId) => _client.deleteNodeBackupTarget(targetId); + Future runBackupTarget(String targetId) => _client.runNodeBackupTarget(targetId); + Future getBackupTargetProgress(String targetId) => + _client.getNodeBackupTargetProgress(targetId); + Future> getBackupProgress() => _client.getNodeBackupProgress(); + + Future scan({String? folder}) => _client.scan(folder: folder); + + Future> getUpdates() => _client.getUpdates(); + Future addUpdate(UpdateRecord record) => _client.addUpdate(record); + Future deleteUpdate(String id) => _client.deleteUpdate(id); +} + +/// Null when no connection is active — mirrors [apiClientProvider] so every +/// downstream provider that watches this gets the same "no connection" +/// signal without re-deriving it from the connection state itself. +@riverpod +NodeRepository? nodeRepository(Ref ref) { + final client = ref.watch(apiClientProvider); + if (client == null) return null; + return NodeRepository(client); +} + +/// The active connection's node info, or null if no connection is +/// configured. Exceptions from an unreachable/misbehaving node surface as +/// [AsyncError] — this is also the smoke-test path proving the client → +/// repository → provider chain works end to end against a live server. +@riverpod +Future currentNode(Ref ref) async { + final repo = ref.watch(nodeRepositoryProvider); + if (repo == null) return null; + return repo.getNode(); +} + +/// The node-level backup config — manual-refresh only, same reasoning as +/// `servicesListProvider`: this backs a detail-heavy screen with its own +/// edit dialog, where a silent background refresh could yank state out +/// from under an in-progress edit. +@riverpod +Future backupConfig(Ref ref) async { + final repo = ref.watch(nodeRepositoryProvider); + if (repo == null) return null; + return repo.getBackup(); +} + +/// The OS update log — manual-refresh only, same reasoning as the other +/// list providers in this app. +@riverpod +Future> updatesList(Ref ref) async { + final repo = ref.watch(nodeRepositoryProvider); + if (repo == null) return []; + return repo.getUpdates(); +} diff --git a/lib/data/repositories/node_repository.g.dart b/lib/data/repositories/node_repository.g.dart new file mode 100644 index 0000000..d35f7b5 --- /dev/null +++ b/lib/data/repositories/node_repository.g.dart @@ -0,0 +1,216 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'node_repository.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// Null when no connection is active — mirrors [apiClientProvider] so every +/// downstream provider that watches this gets the same "no connection" +/// signal without re-deriving it from the connection state itself. + +@ProviderFor(nodeRepository) +const nodeRepositoryProvider = NodeRepositoryProvider._(); + +/// Null when no connection is active — mirrors [apiClientProvider] so every +/// downstream provider that watches this gets the same "no connection" +/// signal without re-deriving it from the connection state itself. + +final class NodeRepositoryProvider + extends + $FunctionalProvider + with $Provider { + /// Null when no connection is active — mirrors [apiClientProvider] so every + /// downstream provider that watches this gets the same "no connection" + /// signal without re-deriving it from the connection state itself. + const NodeRepositoryProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'nodeRepositoryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$nodeRepositoryHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + NodeRepository? create(Ref ref) { + return nodeRepository(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(NodeRepository? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$nodeRepositoryHash() => r'c7cf7aac4b407fd4a21a56f5c3d34c81fa9abe5e'; + +/// The active connection's node info, or null if no connection is +/// configured. Exceptions from an unreachable/misbehaving node surface as +/// [AsyncError] — this is also the smoke-test path proving the client → +/// repository → provider chain works end to end against a live server. + +@ProviderFor(currentNode) +const currentNodeProvider = CurrentNodeProvider._(); + +/// The active connection's node info, or null if no connection is +/// configured. Exceptions from an unreachable/misbehaving node surface as +/// [AsyncError] — this is also the smoke-test path proving the client → +/// repository → provider chain works end to end against a live server. + +final class CurrentNodeProvider + extends + $FunctionalProvider< + AsyncValue, + NodeInfo?, + FutureOr + > + with $FutureModifier, $FutureProvider { + /// The active connection's node info, or null if no connection is + /// configured. Exceptions from an unreachable/misbehaving node surface as + /// [AsyncError] — this is also the smoke-test path proving the client → + /// repository → provider chain works end to end against a live server. + const CurrentNodeProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'currentNodeProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$currentNodeHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return currentNode(ref); + } +} + +String _$currentNodeHash() => r'e1742b62ba610695d0e8a560d554902fa29d128c'; + +/// The node-level backup config — manual-refresh only, same reasoning as +/// `servicesListProvider`: this backs a detail-heavy screen with its own +/// edit dialog, where a silent background refresh could yank state out +/// from under an in-progress edit. + +@ProviderFor(backupConfig) +const backupConfigProvider = BackupConfigProvider._(); + +/// The node-level backup config — manual-refresh only, same reasoning as +/// `servicesListProvider`: this backs a detail-heavy screen with its own +/// edit dialog, where a silent background refresh could yank state out +/// from under an in-progress edit. + +final class BackupConfigProvider + extends + $FunctionalProvider< + AsyncValue, + BackupConfig?, + FutureOr + > + with $FutureModifier, $FutureProvider { + /// The node-level backup config — manual-refresh only, same reasoning as + /// `servicesListProvider`: this backs a detail-heavy screen with its own + /// edit dialog, where a silent background refresh could yank state out + /// from under an in-progress edit. + const BackupConfigProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'backupConfigProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$backupConfigHash(); + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return backupConfig(ref); + } +} + +String _$backupConfigHash() => r'd3eeedd7536dd8610b34b922bd5658c507c91efe'; + +/// The OS update log — manual-refresh only, same reasoning as the other +/// list providers in this app. + +@ProviderFor(updatesList) +const updatesListProvider = UpdatesListProvider._(); + +/// The OS update log — manual-refresh only, same reasoning as the other +/// list providers in this app. + +final class UpdatesListProvider + extends + $FunctionalProvider< + AsyncValue>, + List, + FutureOr> + > + with + $FutureModifier>, + $FutureProvider> { + /// The OS update log — manual-refresh only, same reasoning as the other + /// list providers in this app. + const UpdatesListProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'updatesListProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$updatesListHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr> create(Ref ref) { + return updatesList(ref); + } +} + +String _$updatesListHash() => r'01c6dd3b69dfd8f98e57f29e43e3fa60d014a6e4'; diff --git a/lib/data/repositories/nodes_repository.dart b/lib/data/repositories/nodes_repository.dart new file mode 100644 index 0000000..dc5b6d9 --- /dev/null +++ b/lib/data/repositories/nodes_repository.dart @@ -0,0 +1,60 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../core/models/models.dart'; +import '../../core/network/api_client_provider.dart'; +import '../../core/network/node_master_api_client.dart'; +import '../../core/polling/polling_async_notifier.dart'; + +part 'nodes_repository.g.dart'; + +/// Wraps the `/nodes/*` endpoints — the registry of sibling NodeMaster +/// hosts and the server-side fan-out status view. +class NodesRepository { + const NodesRepository(this._client); + + final NodeMasterApiClient _client; + + Future> getAll() => _client.getAllRemoteNodes(); + Future get(String id) => _client.getRemoteNode(id); + Future add(RemoteNode node) => _client.addRemoteNode(node); + Future update(String id, RemoteNode node) => _client.updateRemoteNode(id, node); + Future delete(String id) => _client.deleteRemoteNode(id); + Future> getAggregated() => _client.getAggregated(); +} + +/// Null when no connection is active, mirroring [apiClientProvider]. +@riverpod +NodesRepository? nodesRepository(Ref ref) { + final client = ref.watch(apiClientProvider); + if (client == null) return null; + return NodesRepository(client); +} + +/// The registered-nodes list. Manual-refresh only, same reasoning as +/// `servicesListProvider` — this backs Fleet's registry table (M6). +@riverpod +Future> remoteNodesList(Ref ref) async { + final repo = ref.watch(nodesRepositoryProvider); + if (repo == null) return []; + return repo.getAll(); +} + +/// The live `/nodes/aggregated` fan-out result, polled every 25s. Shared +/// by the Dashboard's "Fleet" stat tile and the Fleet screen's card grid +/// (M6) — one poll, cached by Riverpod, rather than each screen running +/// its own duplicate timer against the same endpoint. +@riverpod +class AggregatedNodes extends _$AggregatedNodes with PollingMixin> { + @override + Duration get interval => const Duration(seconds: 25); + + @override + Future> fetch() async { + final repo = ref.watch(nodesRepositoryProvider); + if (repo == null) return []; + return repo.getAggregated(); + } + + @override + Future> build() => startPolling(); +} diff --git a/lib/data/repositories/nodes_repository.g.dart b/lib/data/repositories/nodes_repository.g.dart new file mode 100644 index 0000000..f254a77 --- /dev/null +++ b/lib/data/repositories/nodes_repository.g.dart @@ -0,0 +1,180 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'nodes_repository.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// Null when no connection is active, mirroring [apiClientProvider]. + +@ProviderFor(nodesRepository) +const nodesRepositoryProvider = NodesRepositoryProvider._(); + +/// Null when no connection is active, mirroring [apiClientProvider]. + +final class NodesRepositoryProvider + extends + $FunctionalProvider< + NodesRepository?, + NodesRepository?, + NodesRepository? + > + with $Provider { + /// Null when no connection is active, mirroring [apiClientProvider]. + const NodesRepositoryProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'nodesRepositoryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$nodesRepositoryHash(); + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + NodesRepository? create(Ref ref) { + return nodesRepository(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(NodesRepository? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$nodesRepositoryHash() => r'7dd03a46ea79ae87bb251ff273ff77a634a53f5f'; + +/// The registered-nodes list. Manual-refresh only, same reasoning as +/// `servicesListProvider` — this backs Fleet's registry table (M6). + +@ProviderFor(remoteNodesList) +const remoteNodesListProvider = RemoteNodesListProvider._(); + +/// The registered-nodes list. Manual-refresh only, same reasoning as +/// `servicesListProvider` — this backs Fleet's registry table (M6). + +final class RemoteNodesListProvider + extends + $FunctionalProvider< + AsyncValue>, + List, + FutureOr> + > + with $FutureModifier>, $FutureProvider> { + /// The registered-nodes list. Manual-refresh only, same reasoning as + /// `servicesListProvider` — this backs Fleet's registry table (M6). + const RemoteNodesListProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'remoteNodesListProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$remoteNodesListHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr> create(Ref ref) { + return remoteNodesList(ref); + } +} + +String _$remoteNodesListHash() => r'a2d3bd0b2e2f2cd54a7590bb5ace8a0c63e3d2aa'; + +/// The live `/nodes/aggregated` fan-out result, polled every 25s. Shared +/// by the Dashboard's "Fleet" stat tile and the Fleet screen's card grid +/// (M6) — one poll, cached by Riverpod, rather than each screen running +/// its own duplicate timer against the same endpoint. + +@ProviderFor(AggregatedNodes) +const aggregatedNodesProvider = AggregatedNodesProvider._(); + +/// The live `/nodes/aggregated` fan-out result, polled every 25s. Shared +/// by the Dashboard's "Fleet" stat tile and the Fleet screen's card grid +/// (M6) — one poll, cached by Riverpod, rather than each screen running +/// its own duplicate timer against the same endpoint. +final class AggregatedNodesProvider + extends + $AsyncNotifierProvider> { + /// The live `/nodes/aggregated` fan-out result, polled every 25s. Shared + /// by the Dashboard's "Fleet" stat tile and the Fleet screen's card grid + /// (M6) — one poll, cached by Riverpod, rather than each screen running + /// its own duplicate timer against the same endpoint. + const AggregatedNodesProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'aggregatedNodesProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$aggregatedNodesHash(); + + @$internal + @override + AggregatedNodes create() => AggregatedNodes(); +} + +String _$aggregatedNodesHash() => r'14dd7a1e7d4b9caf2d7c975613555977c037dceb'; + +/// The live `/nodes/aggregated` fan-out result, polled every 25s. Shared +/// by the Dashboard's "Fleet" stat tile and the Fleet screen's card grid +/// (M6) — one poll, cached by Riverpod, rather than each screen running +/// its own duplicate timer against the same endpoint. + +abstract class _$AggregatedNodes + extends $AsyncNotifier> { + FutureOr> build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = + this.ref + as $Ref< + AsyncValue>, + List + >; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier< + AsyncValue>, + List + >, + AsyncValue>, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/lib/data/repositories/services_repository.dart b/lib/data/repositories/services_repository.dart new file mode 100644 index 0000000..72fc5cf --- /dev/null +++ b/lib/data/repositories/services_repository.dart @@ -0,0 +1,57 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../core/models/models.dart'; +import '../../core/network/api_client_provider.dart'; +import '../../core/network/node_master_api_client.dart'; + +part 'services_repository.g.dart'; + +/// Wraps the `/services/*` endpoints — the compose-based services on this +/// host and their start/stop/backup actions. +class ServicesRepository { + const ServicesRepository(this._client); + + final NodeMasterApiClient _client; + + Future> getAll() => _client.getAllServices(); + Future get(String id) => _client.getService(id); + Future add(Service service) => _client.addService(service); + Future update(String id, Service service) => _client.updateService(id, service); + Future delete(String id) => _client.deleteService(id); + Future start(String id) => _client.startService(id); + Future stop(String id) => _client.stopService(id); + Future backupNow(String id) => _client.backupService(id); + + Future> getBackupTargets(String id) => _client.getServiceBackupTargets(id); + Future addBackupTarget(String id, BackupTarget target) => + _client.addServiceBackupTarget(id, target); + Future updateBackupTarget(String id, String targetId, BackupTarget target) => + _client.updateServiceBackupTarget(id, targetId, target); + Future deleteBackupTarget(String id, String targetId) => + _client.deleteServiceBackupTarget(id, targetId); + Future runBackupTarget(String id, String targetId) => + _client.runServiceBackupTarget(id, targetId); + Future getBackupTargetProgress(String id, String targetId) => + _client.getServiceBackupTargetProgress(id, targetId); + Future> getBackupProgress(String id) => _client.getServiceBackupProgress(id); +} + +/// Null when no connection is active, mirroring [apiClientProvider]. +@riverpod +ServicesRepository? servicesRepository(Ref ref) { + final client = ref.watch(apiClientProvider); + if (client == null) return null; + return ServicesRepository(client); +} + +/// The full services list for the active connection. Manual-refresh only +/// (not polled) — this is a detail-heavy table screen where a mid-edit +/// slide-over silently refreshing underneath the user would be worse than +/// slightly stale data; Dashboard's polled summary is where "did something +/// change" gets noticed at a glance. +@riverpod +Future> servicesList(Ref ref) async { + final repo = ref.watch(servicesRepositoryProvider); + if (repo == null) return []; + return repo.getAll(); +} diff --git a/lib/data/repositories/services_repository.g.dart b/lib/data/repositories/services_repository.g.dart new file mode 100644 index 0000000..daa780a --- /dev/null +++ b/lib/data/repositories/services_repository.g.dart @@ -0,0 +1,118 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'services_repository.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// Null when no connection is active, mirroring [apiClientProvider]. + +@ProviderFor(servicesRepository) +const servicesRepositoryProvider = ServicesRepositoryProvider._(); + +/// Null when no connection is active, mirroring [apiClientProvider]. + +final class ServicesRepositoryProvider + extends + $FunctionalProvider< + ServicesRepository?, + ServicesRepository?, + ServicesRepository? + > + with $Provider { + /// Null when no connection is active, mirroring [apiClientProvider]. + const ServicesRepositoryProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'servicesRepositoryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$servicesRepositoryHash(); + + @$internal + @override + $ProviderElement $createElement( + $ProviderPointer pointer, + ) => $ProviderElement(pointer); + + @override + ServicesRepository? create(Ref ref) { + return servicesRepository(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(ServicesRepository? value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$servicesRepositoryHash() => + r'6eab348f1911803394716fd8f90a079bfe5cbda1'; + +/// The full services list for the active connection. Manual-refresh only +/// (not polled) — this is a detail-heavy table screen where a mid-edit +/// slide-over silently refreshing underneath the user would be worse than +/// slightly stale data; Dashboard's polled summary is where "did something +/// change" gets noticed at a glance. + +@ProviderFor(servicesList) +const servicesListProvider = ServicesListProvider._(); + +/// The full services list for the active connection. Manual-refresh only +/// (not polled) — this is a detail-heavy table screen where a mid-edit +/// slide-over silently refreshing underneath the user would be worse than +/// slightly stale data; Dashboard's polled summary is where "did something +/// change" gets noticed at a glance. + +final class ServicesListProvider + extends + $FunctionalProvider< + AsyncValue>, + List, + FutureOr> + > + with $FutureModifier>, $FutureProvider> { + /// The full services list for the active connection. Manual-refresh only + /// (not polled) — this is a detail-heavy table screen where a mid-edit + /// slide-over silently refreshing underneath the user would be worse than + /// slightly stale data; Dashboard's polled summary is where "did something + /// change" gets noticed at a glance. + const ServicesListProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'servicesListProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$servicesListHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr> create(Ref ref) { + return servicesList(ref); + } +} + +String _$servicesListHash() => r'e9d080c9ce0c0796d39c663ba79d7b67a2cf3adc'; diff --git a/lib/features/backups/backups_screen.dart b/lib/features/backups/backups_screen.dart new file mode 100644 index 0000000..093f670 --- /dev/null +++ b/lib/features/backups/backups_screen.dart @@ -0,0 +1,267 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../../core/models/models.dart'; +import '../../core/network/api_exception.dart'; +import '../../core/theme/theme_x.dart'; +import '../../core/widgets/backup_target_form_dialog.dart'; +import '../../core/widgets/backup_targets_list.dart'; +import '../../core/widgets/empty_state.dart'; +import '../../core/widgets/error_banner.dart'; +import '../../core/widgets/sparkline.dart'; +import '../../data/repositories/node_repository.dart'; +import 'widgets/backup_config_form_dialog.dart'; +import 'widgets/backup_history_table.dart'; + +class BackupsScreen extends ConsumerWidget { + const BackupsScreen({super.key}); + + Future _runNow(BuildContext context, WidgetRef ref) async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) return; + try { + await repo.runBackup(); + ref.invalidate(backupConfigProvider); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Backup started'))); + } + } catch (e) { + final message = e is ApiException ? e.userMessage : e.toString(); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } + } + + Future _addTarget(BuildContext context, WidgetRef ref) { + return showBackupTargetFormDialog( + context, + onSave: (draft) async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) throw const ApiException.unknown('No active connection.'); + await repo.addBackupTarget(draft); + ref.invalidate(backupConfigProvider); + }, + ); + } + + Future _editTarget(BuildContext context, WidgetRef ref, BackupTarget target) { + return showBackupTargetFormDialog( + context, + existing: target, + onSave: (draft) async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) throw const ApiException.unknown('No active connection.'); + await repo.updateBackupTarget(target.id, draft); + ref.invalidate(backupConfigProvider); + }, + ); + } + + Future _deleteTarget(BuildContext context, WidgetRef ref, BackupTarget target) async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Remove backup target?'), + content: Text('This removes "${target.name}" from the node\'s backup targets.'), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: context.status.critical), + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Remove'), + ), + ], + ), + ); + if (confirmed != true) return; + + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) return; + try { + await repo.deleteBackupTarget(target.id); + ref.invalidate(backupConfigProvider); + } catch (e) { + final message = e is ApiException ? e.userMessage : e.toString(); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } + } + + Future _runTarget(WidgetRef ref, BackupTarget target) async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) throw const ApiException.unknown('No active connection.'); + await repo.runBackupTarget(target.id); + } + + Future _fetchProgress(WidgetRef ref, BackupTarget target) async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) return const RunState(); + return repo.getBackupTargetProgress(target.id); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final configAsync = ref.watch(backupConfigProvider); + + return configAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Center( + child: ErrorBanner( + message: error is ApiException ? error.userMessage : error.toString(), + onRetry: () => ref.invalidate(backupConfigProvider), + ), + ), + data: (config) { + if (config == null) { + return const Center( + child: EmptyState(icon: Icons.cloud_outlined, title: 'No connection configured'), + ); + } + return ListView( + padding: const EdgeInsets.all(24), + children: [ + _ConfigCard(config: config, onRunNow: () => _runNow(context, ref)), + const SizedBox(height: 22), + Text('BACKUP TARGETS', style: context.text.titleSmall), + const SizedBox(height: 10), + Card( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: BackupTargetsList( + targets: config.targets, + onAdd: () => _addTarget(context, ref), + onEdit: (target) => _editTarget(context, ref, target), + onDelete: (target) => _deleteTarget(context, ref, target), + onRun: (target) => _runTarget(ref, target), + fetchProgress: (target) => _fetchProgress(ref, target), + onSettled: (target, state) => ref.invalidate(backupConfigProvider), + ), + ), + ), + const SizedBox(height: 22), + _DurationTrendCard(history: config.history), + const SizedBox(height: 22), + Text('EXECUTION HISTORY', style: context.text.titleSmall), + const SizedBox(height: 10), + BackupHistoryTable(history: config.history, targets: config.targets), + ], + ); + }, + ); + } +} + +class _ConfigCard extends StatelessWidget { + const _ConfigCard({required this.config, required this.onRunNow}); + + final BackupConfig config; + final VoidCallback onRunNow; + + @override + Widget build(BuildContext context) { + final dateFormat = DateFormat('EEE, MMM d · HH:mm'); + + return Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Node backup configuration', style: context.text.titleMedium), + Row( + children: [ + OutlinedButton.icon( + onPressed: () => showBackupConfigFormDialog(context, existing: config), + icon: const Icon(Icons.edit_outlined, size: 16), + label: const Text('Edit'), + ), + const SizedBox(width: 8), + FilledButton.icon( + onPressed: onRunNow, + icon: const Icon(Icons.cloud_outlined, size: 16), + label: const Text('Run all now'), + ), + ], + ), + ], + ), + const Divider(height: 24), + _kv(context, 'Source path', config.sourcePath?.isNotEmpty == true ? config.sourcePath! : 'Not set'), + _kv( + context, + 'Next run', + config.nextRun == null ? 'Not scheduled' : dateFormat.format(config.nextRun!.toLocal()), + ), + _kv( + context, + 'Last run', + config.lastRun == null ? 'Never' : dateFormat.format(config.lastRun!.toLocal()), + ), + ], + ), + ), + ); + } + + Widget _kv(BuildContext context, String label, String value) { + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Row( + children: [ + SizedBox(width: 120, child: Text(label, style: context.text.bodySmall)), + Expanded(child: Text(value, style: context.text.bodyMedium)), + ], + ), + ); + } +} + +class _DurationTrendCard extends StatelessWidget { + const _DurationTrendCard({required this.history}); + + final List history; + + @override + Widget build(BuildContext context) { + final sorted = [...history]..sort((a, b) => a.timestamp.compareTo(b.timestamp)); + final recent = sorted.length > 10 ? sorted.sublist(sorted.length - 10) : sorted; + + if (recent.isEmpty) { + return const SizedBox.shrink(); + } + + final values = [for (final e in recent) e.durationSeconds.toDouble()]; + final latest = recent.last.durationSeconds; + final avg = values.reduce((a, b) => a + b) / values.length; + + return Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Sparkline(values: values), + const SizedBox(width: 20), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('Duration trend', style: context.text.titleMedium), + const SizedBox(height: 4), + Text( + 'Latest ${latest}s · last ${recent.length}-run avg ${avg.toStringAsFixed(1)}s', + style: context.text.bodySmall, + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/backups/widgets/backup_config_form_dialog.dart b/lib/features/backups/widgets/backup_config_form_dialog.dart new file mode 100644 index 0000000..c7533a7 --- /dev/null +++ b/lib/features/backups/widgets/backup_config_form_dialog.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/network/api_exception.dart'; +import '../../../data/repositories/node_repository.dart'; + +/// Edit dialog for the node-level [BackupConfig]'s source path. Target +/// management (add/edit/remove/run/schedule) lives in the dedicated +/// [BackupTargetsList] on the Backups screen, not in this dialog. +Future showBackupConfigFormDialog(BuildContext context, {required BackupConfig existing}) { + return showDialog( + context: context, + builder: (context) => _BackupConfigFormDialog(existing: existing), + ); +} + +class _BackupConfigFormDialog extends ConsumerStatefulWidget { + const _BackupConfigFormDialog({required this.existing}); + + final BackupConfig existing; + + @override + ConsumerState<_BackupConfigFormDialog> createState() => _BackupConfigFormDialogState(); +} + +class _BackupConfigFormDialogState extends ConsumerState<_BackupConfigFormDialog> { + late final _sourcePathController = TextEditingController(text: widget.existing.sourcePath); + bool _saving = false; + String? _error; + + @override + void dispose() { + _sourcePathController.dispose(); + super.dispose(); + } + + Future _save() async { + setState(() { + _saving = true; + _error = null; + }); + + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) { + setState(() { + _saving = false; + _error = 'No active connection.'; + }); + return; + } + + final draft = widget.existing.copyWith(sourcePath: _sourcePathController.text.trim()); + + try { + await repo.updateBackup(draft); + ref.invalidate(backupConfigProvider); + if (mounted) Navigator.of(context).pop(); + } catch (e) { + setState(() { + _saving = false; + _error = e is ApiException ? e.userMessage : e.toString(); + }); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Edit backup configuration'), + content: SizedBox( + width: 420, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: _sourcePathController, + decoration: const InputDecoration(labelText: 'Source path', hintText: '/opt'), + ), + if (_error != null) ...[ + const SizedBox(height: 12), + Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ], + ], + ), + ), + actions: [ + TextButton( + onPressed: _saving ? null : () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + FilledButton( + onPressed: _saving ? null : _save, + child: _saving + ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) + : const Text('Save'), + ), + ], + ); + } +} diff --git a/lib/features/backups/widgets/backup_history_table.dart b/lib/features/backups/widgets/backup_history_table.dart new file mode 100644 index 0000000..fc5931f --- /dev/null +++ b/lib/features/backups/widgets/backup_history_table.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/models/severity_mapping.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/widgets/data_table_scaffold.dart'; +import '../../../core/widgets/empty_state.dart'; +import '../../../core/widgets/mono_text.dart'; +import '../../../core/widgets/status_pill.dart'; + +class BackupHistoryTable extends StatelessWidget { + const BackupHistoryTable({super.key, required this.history, required this.targets}); + + final List history; + final List targets; + + String _targetName(String? targetId) { + if (targetId == null) return '—'; + for (final t in targets) { + if (t.id == targetId) return t.name; + } + return targetId; + } + + @override + Widget build(BuildContext context) { + final sorted = [...history]..sort((a, b) => b.timestamp.compareTo(a.timestamp)); + final timeFormat = DateFormat('yyyy-MM-dd HH:mm'); + + return DataTableScaffold( + empty: const EmptyState( + icon: Icons.cloud_outlined, + title: 'No backup history yet', + message: 'Run a backup to see execution results here.', + ), + columns: const [ + DataColumn(label: Text('Timestamp')), + DataColumn(label: Text('Target')), + DataColumn(label: Text('Result')), + DataColumn(label: Text('Duration')), + DataColumn(label: Text('Message')), + ], + rows: [ + for (final execution in sorted) + DataRow( + cells: [ + DataCell(MonoText(timeFormat.format(execution.timestamp.toLocal()))), + DataCell(Text(_targetName(execution.targetId))), + DataCell( + StatusPill( + label: execution.success ? 'success' : 'failed', + severity: severityForSuccess(execution.success), + ), + ), + DataCell( + Text( + '${execution.durationSeconds}s', + style: context.dataStyles.numericTabular, + ), + ), + DataCell( + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 340), + child: MonoText(execution.message, small: true, maxLines: 1), + ), + ), + ], + ), + ], + ); + } +} diff --git a/lib/features/dashboard/dashboard_screen.dart b/lib/features/dashboard/dashboard_screen.dart new file mode 100644 index 0000000..e4d9dd6 --- /dev/null +++ b/lib/features/dashboard/dashboard_screen.dart @@ -0,0 +1,175 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/models/models.dart'; +import '../../core/models/severity_mapping.dart'; +import '../../core/network/api_exception.dart'; +import '../../core/theme/status_colors.dart'; +import '../../core/theme/theme_x.dart'; +import '../../core/utils/relative_time.dart'; +import '../../core/widgets/error_banner.dart'; +import '../../core/widgets/stat_tile.dart'; +import '../../data/repositories/nodes_repository.dart'; +import 'providers/dashboard_summary.dart'; +import 'providers/dashboard_summary_provider.dart'; +import 'widgets/activity_feed.dart'; +import 'widgets/quick_actions_row.dart'; + +class DashboardScreen extends ConsumerWidget { + const DashboardScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final summaryAsync = ref.watch(dashboardSummaryProvider); + final fleetAsync = ref.watch(aggregatedNodesProvider); + + return summaryAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Center( + child: ErrorBanner( + message: error is ApiException ? error.userMessage : error.toString(), + onRetry: () => ref.invalidate(dashboardSummaryProvider), + ), + ), + data: (summary) => ListView( + padding: const EdgeInsets.all(24), + children: [ + _StatRow(summary: summary, fleetAsync: fleetAsync), + const SizedBox(height: 22), + Text('QUICK ACTIONS', style: context.text.titleSmall), + const SizedBox(height: 10), + const QuickActionsRow(), + const SizedBox(height: 22), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('RECENT ACTIVITY', style: context.text.titleSmall), + ], + ), + const SizedBox(height: 10), + Card(child: ActivityFeed(node: summary.node)), + ], + ), + ); + } +} + +class _StatRow extends StatelessWidget { + const _StatRow({required this.summary, required this.fleetAsync}); + + final DashboardSummary summary; + final AsyncValue> fleetAsync; + + @override + Widget build(BuildContext context) { + final services = summary.services; + final up = services.where((s) => s.status == 'up').length; + final down = services.where((s) => s.status == 'down').length; + final unknown = services.length - up - down; + + final servicesSeverity = down > 0 + ? Severity.critical + : unknown > 0 + ? Severity.warning + : Severity.good; + final servicesCaption = services.isEmpty + ? 'No services discovered yet' + : [ + if (down > 0) '$down down', + if (unknown > 0) '$unknown unknown', + ].isEmpty + ? 'All running' + : [if (down > 0) '$down down', if (unknown > 0) '$unknown unknown'].join(' · '); + + final history = [...summary.node.backup.history] + ..sort((a, b) => b.timestamp.compareTo(a.timestamp)); + final lastBackup = history.isEmpty ? null : history.first; + + final updates = [...summary.node.updateHistory]..sort((a, b) => b.timestamp.compareTo(a.timestamp)); + final lastUpdate = updates.isEmpty ? null : updates.first; + + return Wrap( + spacing: 12, + runSpacing: 12, + children: [ + SizedBox( + width: 260, + child: StatTile( + label: 'Services', + value: '$up', + valueQualifier: '/ ${services.length} up', + caption: servicesCaption, + severity: services.isEmpty ? Severity.neutral : servicesSeverity, + ), + ), + SizedBox( + width: 260, + child: StatTile( + label: 'Last backup', + value: lastBackup == null ? 'Never' : formatRelative(lastBackup.timestamp), + caption: lastBackup == null + ? 'No backups run yet' + : (lastBackup.success ? '${lastBackup.durationSeconds}s' : lastBackup.message), + severity: lastBackup == null ? Severity.neutral : severityForSuccess(lastBackup.success), + ), + ), + SizedBox( + width: 260, + child: StatTile( + label: 'Last system update', + value: lastUpdate == null ? 'Never' : formatRelative(lastUpdate.timestamp), + caption: lastUpdate == null + ? 'No updates recorded yet' + : '${lastUpdate.packages.length} package(s)', + severity: lastUpdate == null ? Severity.neutral : severityForSuccess(lastUpdate.success), + ), + ), + SizedBox(width: 260, child: _FleetTile(fleetAsync: fleetAsync)), + ], + ); + } +} + +class _FleetTile extends StatelessWidget { + const _FleetTile({required this.fleetAsync}); + + final AsyncValue> fleetAsync; + + @override + Widget build(BuildContext context) { + return fleetAsync.when( + loading: () => const StatTile( + label: 'Fleet', + value: '…', + severity: Severity.neutral, + ), + error: (error, stackTrace) => StatTile( + label: 'Fleet', + value: '—', + caption: error is ApiException ? error.userMessage : error.toString(), + severity: Severity.critical, + ), + data: (nodes) { + if (nodes.isEmpty) { + return const StatTile( + label: 'Fleet', + value: '0', + caption: 'No nodes registered', + severity: Severity.neutral, + ); + } + final online = nodes.where((n) => n.error == null).length; + final unreachable = nodes.where((n) => n.error != null).toList(); + return StatTile( + label: 'Fleet', + value: '$online', + valueQualifier: '/ ${nodes.length} online', + caption: unreachable.isEmpty + ? 'All nodes reachable' + : 'unreachable: ${unreachable.map((n) => n.node.name).join(', ')}', + severity: unreachable.isEmpty ? Severity.good : Severity.critical, + ); + }, + ); + } +} diff --git a/lib/features/dashboard/providers/dashboard_summary.dart b/lib/features/dashboard/providers/dashboard_summary.dart new file mode 100644 index 0000000..d4cf092 --- /dev/null +++ b/lib/features/dashboard/providers/dashboard_summary.dart @@ -0,0 +1,14 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import '../../../core/models/models.dart'; + +part 'dashboard_summary.freezed.dart'; + +/// Client-side composition of the two data sources the Dashboard needs — +/// no server endpoint returns this shape directly. Not JSON-serializable +/// (nothing here is ever sent/stored as JSON), so no `fromJson`/`toJson`. +@freezed +sealed class DashboardSummary with _$DashboardSummary { + const factory DashboardSummary({required List services, required NodeInfo node}) = + _DashboardSummary; +} diff --git a/lib/features/dashboard/providers/dashboard_summary.freezed.dart b/lib/features/dashboard/providers/dashboard_summary.freezed.dart new file mode 100644 index 0000000..b3ca207 --- /dev/null +++ b/lib/features/dashboard/providers/dashboard_summary.freezed.dart @@ -0,0 +1,292 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'dashboard_summary.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$DashboardSummary { + + List get services; NodeInfo get node; +/// Create a copy of DashboardSummary +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$DashboardSummaryCopyWith get copyWith => _$DashboardSummaryCopyWithImpl(this as DashboardSummary, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardSummary&&const DeepCollectionEquality().equals(other.services, services)&&(identical(other.node, node) || other.node == node)); +} + + +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(services),node); + +@override +String toString() { + return 'DashboardSummary(services: $services, node: $node)'; +} + + +} + +/// @nodoc +abstract mixin class $DashboardSummaryCopyWith<$Res> { + factory $DashboardSummaryCopyWith(DashboardSummary value, $Res Function(DashboardSummary) _then) = _$DashboardSummaryCopyWithImpl; +@useResult +$Res call({ + List services, NodeInfo node +}); + + +$NodeInfoCopyWith<$Res> get node; + +} +/// @nodoc +class _$DashboardSummaryCopyWithImpl<$Res> + implements $DashboardSummaryCopyWith<$Res> { + _$DashboardSummaryCopyWithImpl(this._self, this._then); + + final DashboardSummary _self; + final $Res Function(DashboardSummary) _then; + +/// Create a copy of DashboardSummary +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? services = null,Object? node = null,}) { + return _then(_self.copyWith( +services: null == services ? _self.services : services // ignore: cast_nullable_to_non_nullable +as List,node: null == node ? _self.node : node // ignore: cast_nullable_to_non_nullable +as NodeInfo, + )); +} +/// Create a copy of DashboardSummary +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NodeInfoCopyWith<$Res> get node { + + return $NodeInfoCopyWith<$Res>(_self.node, (value) { + return _then(_self.copyWith(node: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [DashboardSummary]. +extension DashboardSummaryPatterns on DashboardSummary { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _DashboardSummary value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _DashboardSummary() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _DashboardSummary value) $default,){ +final _that = this; +switch (_that) { +case _DashboardSummary(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _DashboardSummary value)? $default,){ +final _that = this; +switch (_that) { +case _DashboardSummary() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( List services, NodeInfo node)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _DashboardSummary() when $default != null: +return $default(_that.services,_that.node);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( List services, NodeInfo node) $default,) {final _that = this; +switch (_that) { +case _DashboardSummary(): +return $default(_that.services,_that.node);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( List services, NodeInfo node)? $default,) {final _that = this; +switch (_that) { +case _DashboardSummary() when $default != null: +return $default(_that.services,_that.node);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _DashboardSummary implements DashboardSummary { + const _DashboardSummary({required final List services, required this.node}): _services = services; + + + final List _services; +@override List get services { + if (_services is EqualUnmodifiableListView) return _services; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_services); +} + +@override final NodeInfo node; + +/// Create a copy of DashboardSummary +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$DashboardSummaryCopyWith<_DashboardSummary> get copyWith => __$DashboardSummaryCopyWithImpl<_DashboardSummary>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardSummary&&const DeepCollectionEquality().equals(other._services, _services)&&(identical(other.node, node) || other.node == node)); +} + + +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_services),node); + +@override +String toString() { + return 'DashboardSummary(services: $services, node: $node)'; +} + + +} + +/// @nodoc +abstract mixin class _$DashboardSummaryCopyWith<$Res> implements $DashboardSummaryCopyWith<$Res> { + factory _$DashboardSummaryCopyWith(_DashboardSummary value, $Res Function(_DashboardSummary) _then) = __$DashboardSummaryCopyWithImpl; +@override @useResult +$Res call({ + List services, NodeInfo node +}); + + +@override $NodeInfoCopyWith<$Res> get node; + +} +/// @nodoc +class __$DashboardSummaryCopyWithImpl<$Res> + implements _$DashboardSummaryCopyWith<$Res> { + __$DashboardSummaryCopyWithImpl(this._self, this._then); + + final _DashboardSummary _self; + final $Res Function(_DashboardSummary) _then; + +/// Create a copy of DashboardSummary +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? services = null,Object? node = null,}) { + return _then(_DashboardSummary( +services: null == services ? _self._services : services // ignore: cast_nullable_to_non_nullable +as List,node: null == node ? _self.node : node // ignore: cast_nullable_to_non_nullable +as NodeInfo, + )); +} + +/// Create a copy of DashboardSummary +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NodeInfoCopyWith<$Res> get node { + + return $NodeInfoCopyWith<$Res>(_self.node, (value) { + return _then(_self.copyWith(node: value)); + }); +} +} + +// dart format on diff --git a/lib/features/dashboard/providers/dashboard_summary_provider.dart b/lib/features/dashboard/providers/dashboard_summary_provider.dart new file mode 100644 index 0000000..d601a56 --- /dev/null +++ b/lib/features/dashboard/providers/dashboard_summary_provider.dart @@ -0,0 +1,39 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../../core/network/api_exception.dart'; +import '../../../core/polling/polling_async_notifier.dart'; +import '../../../data/repositories/node_repository.dart'; +import '../../../data/repositories/services_repository.dart'; +import 'dashboard_summary.dart'; + +part 'dashboard_summary_provider.g.dart'; + +/// Polled every 20s while the Dashboard is mounted — deliberately its own +/// notifier rather than watching `servicesListProvider`/`currentNodeProvider` +/// directly, so Dashboard auto-refreshes independent of whether the +/// Services screen (manual-refresh only) happens to be open too. +@riverpod +class DashboardSummaryNotifier extends _$DashboardSummaryNotifier + with PollingMixin { + @override + Duration get interval => const Duration(seconds: 20); + + @override + Future fetch() async { + final servicesRepo = ref.watch(servicesRepositoryProvider); + final nodeRepo = ref.watch(nodeRepositoryProvider); + if (servicesRepo == null || nodeRepo == null) { + // The router redirects to Settings whenever there's no active + // connection, so reaching this in the UI would mean that redirect + // hasn't fired yet — an ApiException here (vs. a raw StateError) + // keeps error handling on the same path as every other screen. + throw const ApiException.unknown('No active connection.'); + } + final services = await servicesRepo.getAll(); + final node = await nodeRepo.getNode(); + return DashboardSummary(services: services, node: node); + } + + @override + Future build() => startPolling(); +} diff --git a/lib/features/dashboard/providers/dashboard_summary_provider.g.dart b/lib/features/dashboard/providers/dashboard_summary_provider.g.dart new file mode 100644 index 0000000..29300f8 --- /dev/null +++ b/lib/features/dashboard/providers/dashboard_summary_provider.g.dart @@ -0,0 +1,75 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'dashboard_summary_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning +/// Polled every 20s while the Dashboard is mounted — deliberately its own +/// notifier rather than watching `servicesListProvider`/`currentNodeProvider` +/// directly, so Dashboard auto-refreshes independent of whether the +/// Services screen (manual-refresh only) happens to be open too. + +@ProviderFor(DashboardSummaryNotifier) +const dashboardSummaryProvider = DashboardSummaryNotifierProvider._(); + +/// Polled every 20s while the Dashboard is mounted — deliberately its own +/// notifier rather than watching `servicesListProvider`/`currentNodeProvider` +/// directly, so Dashboard auto-refreshes independent of whether the +/// Services screen (manual-refresh only) happens to be open too. +final class DashboardSummaryNotifierProvider + extends $AsyncNotifierProvider { + /// Polled every 20s while the Dashboard is mounted — deliberately its own + /// notifier rather than watching `servicesListProvider`/`currentNodeProvider` + /// directly, so Dashboard auto-refreshes independent of whether the + /// Services screen (manual-refresh only) happens to be open too. + const DashboardSummaryNotifierProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'dashboardSummaryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$dashboardSummaryNotifierHash(); + + @$internal + @override + DashboardSummaryNotifier create() => DashboardSummaryNotifier(); +} + +String _$dashboardSummaryNotifierHash() => + r'807d0df3c8b7ce9652ae56fdba542b108aa99456'; + +/// Polled every 20s while the Dashboard is mounted — deliberately its own +/// notifier rather than watching `servicesListProvider`/`currentNodeProvider` +/// directly, so Dashboard auto-refreshes independent of whether the +/// Services screen (manual-refresh only) happens to be open too. + +abstract class _$DashboardSummaryNotifier + extends $AsyncNotifier { + FutureOr build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = + this.ref as $Ref, DashboardSummary>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, DashboardSummary>, + AsyncValue, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/lib/features/dashboard/widgets/activity_feed.dart b/lib/features/dashboard/widgets/activity_feed.dart new file mode 100644 index 0000000..93c7bc8 --- /dev/null +++ b/lib/features/dashboard/widgets/activity_feed.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/models/severity_mapping.dart'; +import '../../../core/theme/status_colors.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/utils/relative_time.dart'; +import '../../../core/widgets/empty_state.dart'; + +class _FeedEntry { + const _FeedEntry({ + required this.timestamp, + required this.title, + required this.subtitle, + required this.severity, + required this.icon, + }); + + final DateTime timestamp; + final String title; + final String subtitle; + final Severity severity; + final IconData icon; +} + +/// Merges backup executions and OS update records from [NodeInfo] into one +/// reverse-chronological feed. There's no persisted scan-result log on the +/// server to fold in here — only these two histories actually exist. +class ActivityFeed extends StatelessWidget { + const ActivityFeed({super.key, required this.node, this.maxEntries = 6}); + + final NodeInfo node; + final int maxEntries; + + List<_FeedEntry> _buildEntries() { + final entries = <_FeedEntry>[ + for (final execution in node.backup.history) + _FeedEntry( + timestamp: execution.timestamp, + title: execution.success ? 'Backup completed' : 'Backup failed', + subtitle: execution.message.isEmpty + ? '${execution.durationSeconds}s' + : execution.message, + severity: severityForSuccess(execution.success), + icon: Icons.cloud_outlined, + ), + for (final update in node.updateHistory) + _FeedEntry( + timestamp: update.timestamp, + title: update.success ? 'System update applied' : 'System update failed', + subtitle: update.packages.isEmpty + ? update.message + : '${update.packages.length} package(s)${update.message.isEmpty ? '' : ' · ${update.message}'}', + severity: severityForSuccess(update.success), + icon: Icons.history_rounded, + ), + ]..sort((a, b) => b.timestamp.compareTo(a.timestamp)); + + return entries.take(maxEntries).toList(); + } + + @override + Widget build(BuildContext context) { + final entries = _buildEntries(); + + if (entries.isEmpty) { + return const EmptyState( + icon: Icons.history_rounded, + title: 'No activity yet', + message: 'Backup runs and system updates will show up here.', + ); + } + + return Column( + children: [ + for (var i = 0; i < entries.length; i++) ...[ + if (i > 0) const Divider(height: 1), + _FeedRow(entry: entries[i]), + ], + ], + ); + } +} + +class _FeedRow extends StatelessWidget { + const _FeedRow({required this.entry}); + + final _FeedEntry entry; + + @override + Widget build(BuildContext context) { + final color = entry.severity.resolve(context.status, muted: context.colors.outline); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 11), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 26, + height: 26, + decoration: BoxDecoration( + color: Color.alphaBlend(color.withValues(alpha: 0.14), context.colors.surface), + borderRadius: BorderRadius.circular(7), + ), + child: Icon(entry.icon, size: 14, color: color), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(entry.title, style: context.text.labelLarge), + Text( + entry.subtitle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.text.bodySmall, + ), + ], + ), + ), + const SizedBox(width: 12), + Text( + formatRelative(entry.timestamp), + style: context.dataStyles.numericTabular.copyWith(fontSize: 12, color: context.colors.outline), + ), + ], + ), + ); + } +} diff --git a/lib/features/dashboard/widgets/quick_actions_row.dart b/lib/features/dashboard/widgets/quick_actions_row.dart new file mode 100644 index 0000000..05a6b0e --- /dev/null +++ b/lib/features/dashboard/widgets/quick_actions_row.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/network/api_exception.dart'; +import '../../../data/repositories/node_repository.dart'; +import '../../../data/repositories/nodes_repository.dart'; +import '../providers/dashboard_summary_provider.dart'; + +class QuickActionsRow extends ConsumerStatefulWidget { + const QuickActionsRow({super.key}); + + @override + ConsumerState createState() => _QuickActionsRowState(); +} + +class _QuickActionsRowState extends ConsumerState { + bool _busy = false; + + Future _guarded(Future Function() action) async { + setState(() => _busy = true); + try { + final message = await action(); + if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } catch (e) { + final message = e is ApiException ? e.userMessage : e.toString(); + if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } finally { + if (mounted) setState(() => _busy = false); + } + } + + Future _scan() => _guarded(() async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) throw StateError('No active connection.'); + final result = await repo.scan(); + await ref.read(dashboardSummaryProvider.notifier).refresh(); + final changed = result.created.length + result.updated.length; + return changed == 0 + ? 'Scan complete — no changes (${result.found} compose file(s) found)' + : 'Scan complete — ${result.created.length} new, ${result.updated.length} updated'; + }); + + Future _runBackup() => _guarded(() async { + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) throw StateError('No active connection.'); + await repo.runBackup(); + await ref.read(dashboardSummaryProvider.notifier).refresh(); + return 'Backup started'; + }); + + Future _recheckFleet() => _guarded(() async { + await ref.read(aggregatedNodesProvider.notifier).refresh(); + return 'Fleet status refreshed'; + }); + + @override + Widget build(BuildContext context) { + return Card( + child: Padding( + padding: const EdgeInsets.all(14), + child: Wrap( + spacing: 10, + runSpacing: 10, + children: [ + FilledButton.icon( + onPressed: _busy ? null : _scan, + icon: const Icon(Icons.search_rounded, size: 17), + label: const Text('Scan for services'), + ), + OutlinedButton.icon( + onPressed: _busy ? null : _runBackup, + icon: const Icon(Icons.cloud_outlined, size: 17), + label: const Text('Run backup now'), + ), + OutlinedButton.icon( + onPressed: _busy ? null : _recheckFleet, + icon: const Icon(Icons.refresh_rounded, size: 17), + label: const Text('Re-check fleet'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/fleet/fleet_screen.dart b/lib/features/fleet/fleet_screen.dart new file mode 100644 index 0000000..d877260 --- /dev/null +++ b/lib/features/fleet/fleet_screen.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/models/models.dart'; +import '../../core/network/api_exception.dart'; +import '../../core/theme/theme_x.dart'; +import '../../core/widgets/error_banner.dart'; +import '../../data/repositories/nodes_repository.dart'; +import 'widgets/fleet_card_grid.dart'; +import 'widgets/fleet_table.dart'; +import 'widgets/node_form_dialog.dart'; + +class FleetScreen extends ConsumerWidget { + const FleetScreen({super.key}); + + Future _delete(BuildContext context, WidgetRef ref, RemoteNode node) async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Remove node?'), + content: Text('This removes "${node.name}" from the registry. It does not affect that host.'), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: context.status.critical), + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Remove'), + ), + ], + ), + ); + if (confirmed != true) return; + + final repo = ref.read(nodesRepositoryProvider); + if (repo == null) return; + try { + await repo.delete(node.id); + ref.invalidate(remoteNodesListProvider); + ref.invalidate(aggregatedNodesProvider); + } catch (e) { + final message = e is ApiException ? e.userMessage : e.toString(); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final nodesAsync = ref.watch(remoteNodesListProvider); + final aggregatedAsync = ref.watch(aggregatedNodesProvider); + + return ListView( + padding: const EdgeInsets.all(24), + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('REGISTERED NODES', style: context.text.titleSmall), + FilledButton.icon( + onPressed: () => showNodeFormDialog(context), + icon: const Icon(Icons.add, size: 17), + label: const Text('Register node'), + ), + ], + ), + const SizedBox(height: 10), + nodesAsync.when( + loading: () => const Padding( + padding: EdgeInsets.symmetric(vertical: 40), + child: Center(child: CircularProgressIndicator()), + ), + error: (error, stackTrace) => ErrorBanner( + message: error is ApiException ? error.userMessage : error.toString(), + onRetry: () => ref.invalidate(remoteNodesListProvider), + ), + data: (nodes) => FleetTable( + nodes: nodes, + aggregated: aggregatedAsync.value, + onEdit: (node) => showNodeFormDialog(context, existing: node), + onDelete: (node) => _delete(context, ref, node), + ), + ), + const SizedBox(height: 22), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('AGGREGATED SERVICES', style: context.text.titleSmall), + Text( + 'live fan-out via /nodes/aggregated', + style: context.dataStyles.dataMonoSmall, + ), + ], + ), + const SizedBox(height: 10), + aggregatedAsync.when( + loading: () => const Padding( + padding: EdgeInsets.symmetric(vertical: 40), + child: Center(child: CircularProgressIndicator()), + ), + error: (error, stackTrace) => ErrorBanner( + message: error is ApiException ? error.userMessage : error.toString(), + onRetry: () => ref.invalidate(aggregatedNodesProvider), + ), + data: (entries) => FleetCardGrid(entries: entries), + ), + ], + ); + } +} diff --git a/lib/features/fleet/widgets/fleet_card_grid.dart b/lib/features/fleet/widgets/fleet_card_grid.dart new file mode 100644 index 0000000..7bf8cd4 --- /dev/null +++ b/lib/features/fleet/widgets/fleet_card_grid.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/models/severity_mapping.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/widgets/empty_state.dart'; +import '../../../core/widgets/status_pill.dart'; + +/// One card per registered node showing its live services (via +/// `/nodes/aggregated`), or an inline error card if this particular node +/// was unreachable — a per-entry condition the local API itself reports, +/// distinct from the aggregated fetch as a whole failing. +class FleetCardGrid extends StatelessWidget { + const FleetCardGrid({super.key, required this.entries}); + + final List entries; + + @override + Widget build(BuildContext context) { + if (entries.isEmpty) { + return const EmptyState( + icon: Icons.hub_outlined, + title: 'Nothing to show yet', + message: 'Register a node to see its live services here.', + ); + } + + return GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 320, + mainAxisExtent: 180, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + itemCount: entries.length, + itemBuilder: (context, index) => _NodeCard(entry: entries[index]), + ); + } +} + +class _NodeCard extends StatelessWidget { + const _NodeCard({required this.entry}); + + final AggregatedNodeStatus entry; + + @override + Widget build(BuildContext context) { + final hasError = entry.error != null; + + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: context.colors.surface, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: hasError ? context.status.critical.withValues(alpha: 0.4) : context.colors.outlineVariant, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(entry.node.name, style: context.text.labelLarge), + const SizedBox(height: 10), + Expanded( + child: hasError + ? _ErrorCallout(message: entry.error!) + : entry.services.isEmpty + ? Text('No services reported.', style: context.text.bodySmall) + : ListView( + padding: EdgeInsets.zero, + children: [ + for (final service in entry.services) + Padding( + padding: const EdgeInsets.symmetric(vertical: 3.5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + service.name, + overflow: TextOverflow.ellipsis, + style: context.text.bodyMedium, + ), + ), + StatusPill( + label: service.status, + severity: severityForServiceStatus(service.status), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +class _ErrorCallout extends StatelessWidget { + const _ErrorCallout({required this.message}); + + final String message; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Color.alphaBlend(context.status.critical.withValues(alpha: 0.08), context.colors.surface), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: context.status.critical.withValues(alpha: 0.35)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.error_outline, size: 15, color: context.status.critical), + const SizedBox(width: 8), + Expanded( + child: Text( + message, + style: context.dataStyles.dataMonoSmall, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/fleet/widgets/fleet_table.dart b/lib/features/fleet/widgets/fleet_table.dart new file mode 100644 index 0000000..94c3bfe --- /dev/null +++ b/lib/features/fleet/widgets/fleet_table.dart @@ -0,0 +1,120 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/theme/status_colors.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/utils/relative_time.dart'; +import '../../../core/widgets/data_table_scaffold.dart'; +import '../../../core/widgets/empty_state.dart'; +import '../../../core/widgets/mono_text.dart'; +import '../../../core/widgets/status_pill.dart'; + +class FleetTable extends StatelessWidget { + const FleetTable({ + super.key, + required this.nodes, + required this.aggregated, + required this.onEdit, + required this.onDelete, + }); + + final List nodes; + + /// Null while the aggregated fetch is still loading/erroring — reachability + /// then shows as "unknown" rather than guessing. + final List? aggregated; + final void Function(RemoteNode node) onEdit; + final void Function(RemoteNode node) onDelete; + + ({String label, Severity severity}) _reachability(RemoteNode node) { + final entry = aggregated?.where((a) => a.node.id == node.id).firstOrNull; + if (entry == null) return (label: 'unknown', severity: Severity.neutral); + return entry.error == null + ? (label: 'online', severity: Severity.good) + : (label: 'unreachable', severity: Severity.critical); + } + + @override + Widget build(BuildContext context) { + return DataTableScaffold( + empty: const EmptyState( + icon: Icons.hub_outlined, + title: 'No nodes registered', + message: 'Register a sibling NodeMaster host to see it here.', + ), + columns: const [ + DataColumn(label: Text('Node')), + DataColumn(label: Text('URL')), + DataColumn(label: Text('Tags')), + DataColumn(label: Text('Status')), + DataColumn(label: Text('Last seen')), + DataColumn(label: Text('')), + ], + rows: [ + for (final node in nodes) + DataRow( + cells: [ + DataCell( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(node.name, style: context.text.labelLarge), + if (node.description?.isNotEmpty == true) + Text(node.description!, style: context.text.bodySmall), + ], + ), + ), + DataCell(MonoText(node.url)), + DataCell( + Wrap( + spacing: 4, + children: [ + for (final tag in node.tags) + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: context.colors.surfaceContainerHighest, + borderRadius: BorderRadius.circular(4), + ), + child: Text(tag, style: context.dataStyles.dataMonoSmall), + ), + ], + ), + ), + DataCell(Builder( + builder: (context) { + final r = _reachability(node); + return StatusPill(label: r.label, severity: r.severity); + }, + )), + DataCell( + Text( + node.lastSeen == null ? '—' : formatRelative(node.lastSeen!.toLocal()), + style: context.text.bodySmall, + ), + ), + DataCell( + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + tooltip: 'Edit', + icon: const Icon(Icons.edit_outlined, size: 18), + onPressed: () => onEdit(node), + ), + IconButton( + tooltip: 'Remove', + icon: const Icon(Icons.delete_outline, size: 18), + onPressed: () => onDelete(node), + ), + ], + ), + ), + ], + ), + ], + ); + } +} diff --git a/lib/features/fleet/widgets/node_form_dialog.dart b/lib/features/fleet/widgets/node_form_dialog.dart new file mode 100644 index 0000000..aa496f6 --- /dev/null +++ b/lib/features/fleet/widgets/node_form_dialog.dart @@ -0,0 +1,164 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/network/api_exception.dart'; +import '../../../data/repositories/nodes_repository.dart'; + +/// Add/edit dialog for a registered [RemoteNode]. This only edits the +/// registry entry (name/url/description/tags) — reachability is never +/// user-editable, it's always computed live via `/nodes/aggregated`. +Future showNodeFormDialog(BuildContext context, {RemoteNode? existing}) { + return showDialog( + context: context, + builder: (context) => _NodeFormDialog(existing: existing), + ); +} + +class _NodeFormDialog extends ConsumerStatefulWidget { + const _NodeFormDialog({this.existing}); + + final RemoteNode? existing; + + @override + ConsumerState<_NodeFormDialog> createState() => _NodeFormDialogState(); +} + +class _NodeFormDialogState extends ConsumerState<_NodeFormDialog> { + final _formKey = GlobalKey(); + late final _nameController = TextEditingController(text: widget.existing?.name); + late final _urlController = TextEditingController(text: widget.existing?.url); + late final _descriptionController = TextEditingController(text: widget.existing?.description); + late final _tagsController = TextEditingController(text: widget.existing?.tags.join(', ')); + bool _saving = false; + String? _error; + + @override + void dispose() { + _nameController.dispose(); + _urlController.dispose(); + _descriptionController.dispose(); + _tagsController.dispose(); + super.dispose(); + } + + String? _validateUrl(String? value) { + final trimmed = value?.trim() ?? ''; + if (trimmed.isEmpty) return 'Required.'; + final uri = Uri.tryParse(trimmed); + if (uri == null || !uri.hasScheme || !uri.hasAuthority) { + return 'Enter a full URL, e.g. https://edge-02.local:8080'; + } + return null; + } + + Future _save() async { + if (!(_formKey.currentState?.validate() ?? false)) return; + setState(() { + _saving = true; + _error = null; + }); + + final repo = ref.read(nodesRepositoryProvider); + if (repo == null) { + setState(() { + _saving = false; + _error = 'No active connection.'; + }); + return; + } + + var url = _urlController.text.trim(); + if (url.endsWith('/')) url = url.substring(0, url.length - 1); + final tags = _tagsController.text + .split(',') + .map((t) => t.trim()) + .where((t) => t.isNotEmpty) + .toList(); + final description = _descriptionController.text.trim(); + + final draft = (widget.existing ?? const RemoteNode(name: '', url: '')).copyWith( + name: _nameController.text.trim(), + url: url, + description: description.isEmpty ? null : description, + tags: tags, + ); + + try { + if (widget.existing == null) { + await repo.add(draft); + } else { + await repo.update(widget.existing!.id, draft); + } + ref.invalidate(remoteNodesListProvider); + if (mounted) Navigator.of(context).pop(); + } catch (e) { + setState(() { + _saving = false; + _error = e is ApiException ? e.userMessage : e.toString(); + }); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.existing == null ? 'Register node' : 'Edit node'), + content: Form( + key: _formKey, + child: SizedBox( + width: 420, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: _nameController, + decoration: const InputDecoration(labelText: 'Name'), + validator: (v) => (v == null || v.trim().isEmpty) ? 'Required.' : null, + ), + const SizedBox(height: 14), + TextFormField( + controller: _urlController, + decoration: const InputDecoration( + labelText: 'Base URL', + hintText: 'https://edge-02.local:8080', + ), + validator: _validateUrl, + keyboardType: TextInputType.url, + ), + const SizedBox(height: 14), + TextFormField( + controller: _descriptionController, + decoration: const InputDecoration(labelText: 'Description (optional)'), + ), + const SizedBox(height: 14), + TextFormField( + controller: _tagsController, + decoration: const InputDecoration( + labelText: 'Tags (optional, comma-separated)', + hintText: 'prod, db', + ), + ), + if (_error != null) ...[ + const SizedBox(height: 12), + Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ], + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: _saving ? null : () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + FilledButton( + onPressed: _saving ? null : _save, + child: _saving + ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) + : const Text('Save'), + ), + ], + ); + } +} diff --git a/lib/features/services/services_screen.dart b/lib/features/services/services_screen.dart new file mode 100644 index 0000000..1004797 --- /dev/null +++ b/lib/features/services/services_screen.dart @@ -0,0 +1,196 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../core/models/models.dart'; +import '../../core/models/severity_mapping.dart'; +import '../../core/network/api_exception.dart'; +import '../../core/routing/route_paths.dart'; +import '../../core/theme/theme_x.dart'; +import '../../core/widgets/error_banner.dart'; +import '../../core/widgets/slide_over_panel.dart'; +import '../../core/widgets/status_pill.dart'; +import '../../data/repositories/services_repository.dart'; +import 'widgets/service_detail_body.dart'; +import 'widgets/service_form_dialog.dart'; +import 'widgets/services_table.dart'; + +class ServicesScreen extends ConsumerStatefulWidget { + const ServicesScreen({super.key, this.serviceId}); + + /// Present when routed via `/services?id=...` — renders the slide-over + /// detail panel for this service alongside the still-visible table. + final String? serviceId; + + @override + ConsumerState createState() => _ServicesScreenState(); +} + +class _ServicesScreenState extends ConsumerState { + final _busyIds = {}; + + Future _run(String id, Future Function() action, {String? successMessage}) async { + setState(() => _busyIds.add(id)); + try { + await action(); + ref.invalidate(servicesListProvider); + if (mounted && successMessage != null) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(successMessage))); + } + } catch (e) { + if (mounted) { + final message = e is ApiException ? e.userMessage : e.toString(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } finally { + if (mounted) setState(() => _busyIds.remove(id)); + } + } + + void _toggleRunning(Service service) { + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) return; + _run(service.id, () => service.status == 'up' ? repo.stop(service.id) : repo.start(service.id)); + } + + void _backupNow(Service service) { + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) return; + _run(service.id, () => repo.backupNow(service.id), successMessage: 'Backup started'); + } + + Future _delete(Service service) async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Delete service?'), + content: Text('This removes "${service.name}" from NodeMaster. It does not stop or delete the container.'), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: context.status.critical), + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Delete'), + ), + ], + ), + ); + if (confirmed != true) return; + + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) return; + await _run(service.id, () => repo.delete(service.id)); + if (mounted) context.go(RoutePaths.services); + } + + @override + Widget build(BuildContext context) { + final servicesAsync = ref.watch(servicesListProvider); + + return Stack( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + servicesAsync.maybeWhen( + data: (services) => '${services.length} SERVICES', + orElse: () => 'SERVICES', + ), + style: context.text.titleSmall, + ), + FilledButton.icon( + onPressed: () => showServiceFormDialog(context), + icon: const Icon(Icons.add, size: 17), + label: const Text('Add service'), + ), + ], + ), + const SizedBox(height: 12), + Expanded( + child: servicesAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Align( + alignment: Alignment.topCenter, + child: ErrorBanner( + message: error is ApiException ? error.userMessage : error.toString(), + onRetry: () => ref.invalidate(servicesListProvider), + ), + ), + data: (services) => SingleChildScrollView( + child: ServicesTable( + services: services, + busyServiceIds: _busyIds, + onRowTap: (service) => context.go(RoutePaths.serviceDetail(service.id)), + onToggleRunning: _toggleRunning, + ), + ), + ), + ), + ], + ), + ), + _buildDetailPanel(servicesAsync), + ], + ); + } + + Widget _buildDetailPanel(AsyncValue> servicesAsync) { + final id = widget.serviceId; + final service = servicesAsync.maybeWhen( + data: (services) { + for (final s in services) { + if (s.id == id) return s; + } + return null; + }, + orElse: () => null, + ); + + return SlideOverPanel( + open: id != null, + onClose: () => context.go(RoutePaths.services), + title: service?.name ?? '', + subtitle: service == null + ? null + : StatusPill(label: service.status, severity: severityForServiceStatus(service.status)), + body: service == null + ? const SizedBox.shrink() + : ServiceDetailBody(service: service), + headerActions: service == null + ? const [] + : [ + IconButton( + tooltip: 'Edit', + icon: const Icon(Icons.edit_outlined, size: 18), + onPressed: () => showServiceFormDialog(context, existing: service), + ), + ], + actions: service == null + ? const [] + : [ + OutlinedButton.icon( + onPressed: () => _toggleRunning(service), + icon: Icon(service.status == 'up' ? Icons.stop_rounded : Icons.play_arrow_rounded, size: 17), + label: Text(service.status == 'up' ? 'Stop' : 'Start'), + ), + OutlinedButton.icon( + onPressed: service.backup == null ? null : () => _backupNow(service), + icon: const Icon(Icons.cloud_outlined, size: 17), + label: const Text('Backup'), + ), + OutlinedButton.icon( + style: OutlinedButton.styleFrom(foregroundColor: context.status.critical), + onPressed: () => _delete(service), + icon: const Icon(Icons.delete_outline, size: 17), + label: const Text('Delete'), + ), + ], + ); + } +} diff --git a/lib/features/services/widgets/service_detail_body.dart b/lib/features/services/widgets/service_detail_body.dart new file mode 100644 index 0000000..a675757 --- /dev/null +++ b/lib/features/services/widgets/service_detail_body.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/models/severity_mapping.dart'; +import '../../../core/network/api_exception.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/widgets/backup_target_form_dialog.dart'; +import '../../../core/widgets/backup_targets_list.dart'; +import '../../../core/widgets/mono_text.dart'; +import '../../../core/widgets/status_pill.dart'; +import '../../../data/repositories/services_repository.dart'; + +/// The scrollable body of the Services slide-over panel — everything below +/// the title/close row that [SlideOverPanel] already renders. +class ServiceDetailBody extends ConsumerWidget { + const ServiceDetailBody({super.key, required this.service}); + + final Service service; + + Future _addTarget(BuildContext context, WidgetRef ref) { + return showBackupTargetFormDialog( + context, + onSave: (draft) async { + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) throw const ApiException.unknown('No active connection.'); + await repo.addBackupTarget(service.id, draft); + ref.invalidate(servicesListProvider); + }, + ); + } + + Future _editTarget(BuildContext context, WidgetRef ref, BackupTarget target) { + return showBackupTargetFormDialog( + context, + existing: target, + onSave: (draft) async { + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) throw const ApiException.unknown('No active connection.'); + await repo.updateBackupTarget(service.id, target.id, draft); + ref.invalidate(servicesListProvider); + }, + ); + } + + Future _deleteTarget(BuildContext context, WidgetRef ref, BackupTarget target) async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Remove backup target?'), + content: Text('This removes "${target.name}" from this service\'s backup targets.'), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: context.status.critical), + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Remove'), + ), + ], + ), + ); + if (confirmed != true) return; + + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) return; + try { + await repo.deleteBackupTarget(service.id, target.id); + ref.invalidate(servicesListProvider); + } catch (e) { + final message = e is ApiException ? e.userMessage : e.toString(); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } + } + + Future _runTarget(WidgetRef ref, BackupTarget target) async { + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) throw const ApiException.unknown('No active connection.'); + await repo.runBackupTarget(service.id, target.id); + } + + Future _fetchProgress(WidgetRef ref, BackupTarget target) async { + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) return const RunState(); + return repo.getBackupTargetProgress(service.id, target.id); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final backup = service.backup; + final targets = backup?.targets ?? const []; + final history = (backup?.history ?? const []).reversed.take(5).toList(); + final timeFormat = DateFormat('MM-dd HH:mm'); + + return ListView( + padding: const EdgeInsets.fromLTRB(20, 12, 20, 20), + children: [ + _KvRow(label: 'Compose file', value: MonoText(service.composeFile)), + _KvRow( + label: 'Compose type', + value: Text(service.composeType?.isNotEmpty == true ? service.composeType! : 'docker compose'), + ), + _KvRow( + label: 'Backup folder', + value: service.backupFolder?.isNotEmpty == true + ? MonoText(service.backupFolder!) + : Text('Not set', style: context.text.bodySmall), + ), + _KvRow(label: 'Stop on backup', value: Text(service.stopOnBackup ? 'Yes' : 'No')), + const SizedBox(height: 18), + Text('BACKUP TARGETS', style: context.text.titleSmall), + const SizedBox(height: 4), + BackupTargetsList( + targets: targets, + onAdd: () => _addTarget(context, ref), + onEdit: (target) => _editTarget(context, ref, target), + onDelete: (target) => _deleteTarget(context, ref, target), + onRun: (target) => _runTarget(ref, target), + fetchProgress: (target) => _fetchProgress(ref, target), + onSettled: (target, state) => ref.invalidate(servicesListProvider), + ), + const SizedBox(height: 18), + Text('RECENT BACKUPS', style: context.text.titleSmall), + const SizedBox(height: 4), + if (history.isEmpty) + Padding( + padding: const EdgeInsets.only(top: 6), + child: Text('No backup history yet.', style: context.text.bodySmall), + ) + else + for (final execution in history) + Padding( + padding: const EdgeInsets.symmetric(vertical: 7), + child: Row( + children: [ + MonoText(timeFormat.format(execution.timestamp.toLocal()), small: true), + const Spacer(), + StatusPill( + label: execution.success ? '${execution.durationSeconds}s' : 'failed', + severity: severityForSuccess(execution.success), + ), + ], + ), + ), + ], + ); + } +} + +class _KvRow extends StatelessWidget { + const _KvRow({required this.label, required this.value}); + + final String label; + final Widget value; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 12), + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: context.colors.outlineVariant))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(width: 128, child: Text(label, style: context.text.bodySmall)), + Expanded(child: value), + ], + ), + ); + } +} diff --git a/lib/features/services/widgets/service_form_dialog.dart b/lib/features/services/widgets/service_form_dialog.dart new file mode 100644 index 0000000..c520985 --- /dev/null +++ b/lib/features/services/widgets/service_form_dialog.dart @@ -0,0 +1,161 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/network/api_exception.dart'; +import '../../../data/repositories/services_repository.dart'; + +const _composeTypes = ['docker compose', 'docker-compose', 'podman-compose']; + +/// Add/edit dialog for a [Service]'s core identity fields. Backup target +/// configuration is deliberately out of scope here — that's a node/service +/// backup-config concern owned by the Backups screen, not service CRUD. +Future showServiceFormDialog(BuildContext context, {Service? existing}) { + return showDialog( + context: context, + builder: (context) => _ServiceFormDialog(existing: existing), + ); +} + +class _ServiceFormDialog extends ConsumerStatefulWidget { + const _ServiceFormDialog({this.existing}); + + final Service? existing; + + @override + ConsumerState<_ServiceFormDialog> createState() => _ServiceFormDialogState(); +} + +class _ServiceFormDialogState extends ConsumerState<_ServiceFormDialog> { + final _formKey = GlobalKey(); + late final _nameController = TextEditingController(text: widget.existing?.name); + late final _composeFileController = TextEditingController(text: widget.existing?.composeFile); + late final _backupFolderController = TextEditingController(text: widget.existing?.backupFolder); + late String _composeType = widget.existing?.composeType?.isNotEmpty == true + ? widget.existing!.composeType! + : _composeTypes.first; + late bool _stopOnBackup = widget.existing?.stopOnBackup ?? false; + bool _saving = false; + String? _error; + + @override + void dispose() { + _nameController.dispose(); + _composeFileController.dispose(); + _backupFolderController.dispose(); + super.dispose(); + } + + Future _save() async { + if (!(_formKey.currentState?.validate() ?? false)) return; + setState(() { + _saving = true; + _error = null; + }); + + final existing = widget.existing; + final draft = (existing ?? const Service(name: '', composeFile: '')).copyWith( + name: _nameController.text.trim(), + composeFile: _composeFileController.text.trim(), + composeType: _composeType, + backupFolder: _backupFolderController.text.trim(), + stopOnBackup: _stopOnBackup, + ); + + final repo = ref.read(servicesRepositoryProvider); + if (repo == null) { + setState(() { + _saving = false; + _error = 'No active connection.'; + }); + return; + } + + try { + if (existing == null) { + await repo.add(draft); + } else { + await repo.update(existing.id, draft); + } + ref.invalidate(servicesListProvider); + if (mounted) Navigator.of(context).pop(); + } catch (e) { + setState(() { + _saving = false; + _error = e is ApiException ? e.userMessage : e.toString(); + }); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.existing == null ? 'Add service' : 'Edit service'), + content: Form( + key: _formKey, + child: SizedBox( + width: 420, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: _nameController, + decoration: const InputDecoration(labelText: 'Name'), + validator: (v) => (v == null || v.trim().isEmpty) ? 'Required.' : null, + ), + const SizedBox(height: 14), + TextFormField( + controller: _composeFileController, + decoration: const InputDecoration( + labelText: 'Compose file', + hintText: '/opt/compose/gitea/docker-compose.yml', + ), + validator: (v) => (v == null || v.trim().isEmpty) ? 'Required.' : null, + ), + const SizedBox(height: 14), + DropdownButtonFormField( + initialValue: _composeType, + decoration: const InputDecoration(labelText: 'Compose type'), + items: [ + for (final type in _composeTypes) DropdownMenuItem(value: type, child: Text(type)), + ], + onChanged: (value) => setState(() => _composeType = value ?? _composeTypes.first), + ), + const SizedBox(height: 14), + TextFormField( + controller: _backupFolderController, + decoration: const InputDecoration( + labelText: 'Backup folder (optional)', + hintText: '/opt/data/containers/gitea', + ), + ), + const SizedBox(height: 8), + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('Stop container while backing up'), + value: _stopOnBackup, + onChanged: (value) => setState(() => _stopOnBackup = value), + ), + if (_error != null) ...[ + const SizedBox(height: 8), + Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ], + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: _saving ? null : () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + FilledButton( + onPressed: _saving ? null : _save, + child: _saving + ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) + : const Text('Save'), + ), + ], + ); + } +} diff --git a/lib/features/services/widgets/services_table.dart b/lib/features/services/widgets/services_table.dart new file mode 100644 index 0000000..cbac1ad --- /dev/null +++ b/lib/features/services/widgets/services_table.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/models/severity_mapping.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/widgets/data_table_scaffold.dart'; +import '../../../core/widgets/empty_state.dart'; +import '../../../core/widgets/mono_text.dart'; +import '../../../core/widgets/status_pill.dart'; + +class ServicesTable extends StatelessWidget { + const ServicesTable({ + super.key, + required this.services, + required this.onRowTap, + required this.onToggleRunning, + required this.busyServiceIds, + }); + + final List services; + final void Function(Service service) onRowTap; + final void Function(Service service) onToggleRunning; + final Set busyServiceIds; + + String _backupSummary(Service service) { + final targets = service.backup?.targets ?? const []; + if (targets.isEmpty) return 'none'; + if (targets.length == 1) return targets.first.method.name; + return '${targets.length} targets'; + } + + @override + Widget build(BuildContext context) { + return DataTableScaffold( + empty: const EmptyState( + icon: Icons.layers_outlined, + title: 'No services yet', + message: 'Scan a compose folder from the Dashboard, or add one manually.', + ), + columns: const [ + DataColumn(label: Text('Service')), + DataColumn(label: Text('Status')), + DataColumn(label: Text('Compose file')), + DataColumn(label: Text('Backup')), + DataColumn(label: Text('')), + ], + rows: [ + for (final service in services) + DataRow( + onSelectChanged: (_) => onRowTap(service), + cells: [ + DataCell( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(service.name, style: context.text.labelLarge), + Text( + service.composeType?.isNotEmpty == true ? service.composeType! : 'docker compose', + style: context.text.bodySmall, + ), + ], + ), + ), + DataCell( + StatusPill(label: service.status, severity: severityForServiceStatus(service.status)), + ), + DataCell(MonoText(service.composeFile)), + DataCell( + _backupSummary(service) == 'none' + ? Text('none', style: context.text.bodySmall) + : Text(_backupSummary(service), style: context.text.bodyMedium), + ), + DataCell( + busyServiceIds.contains(service.id) + ? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2)) + : IconButton( + tooltip: service.status == 'up' ? 'Stop' : 'Start', + icon: Icon(service.status == 'up' ? Icons.stop_rounded : Icons.play_arrow_rounded, size: 20), + onPressed: () => onToggleRunning(service), + ), + ), + ], + ), + ], + ); + } +} diff --git a/lib/features/settings/settings_screen.dart b/lib/features/settings/settings_screen.dart new file mode 100644 index 0000000..e6f3621 --- /dev/null +++ b/lib/features/settings/settings_screen.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/network/api_exception.dart'; +import '../../core/theme/theme_x.dart'; +import '../../data/repositories/node_repository.dart'; +import 'widgets/connection_form_dialog.dart'; +import 'widgets/connections_list.dart'; +import 'widgets/no_auth_callout.dart'; +import 'widgets/node_identity_form.dart'; +import 'widgets/theme_toggle.dart'; + +class SettingsScreen extends ConsumerWidget { + const SettingsScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ListView( + padding: const EdgeInsets.all(24), + children: [ + _Block( + title: 'Connections', + trailing: TextButton.icon( + onPressed: () => showConnectionFormDialog(context), + icon: const Icon(Icons.add, size: 17), + label: const Text('Add connection'), + ), + child: const ConnectionsList(), + ), + const SizedBox(height: 22), + _Block(title: 'Node identity', child: const _NodeIdentitySection()), + const SizedBox(height: 22), + _Block( + title: 'Appearance', + child: const Padding( + padding: EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Theme'), + ThemeToggle(), + ], + ), + ), + ), + const SizedBox(height: 22), + const NoAuthCallout(), + ], + ); + } +} + +class _NodeIdentitySection extends ConsumerWidget { + const _NodeIdentitySection(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final nodeAsync = ref.watch(currentNodeProvider); + + return nodeAsync.when( + loading: () => const Padding( + padding: EdgeInsets.all(16), + child: Row( + children: [ + SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2)), + SizedBox(width: 10), + Text('Contacting node…'), + ], + ), + ), + error: (error, stackTrace) { + final message = error is ApiException ? error.userMessage : error.toString(); + return Padding( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.error_outline, size: 18, color: context.status.critical), + const SizedBox(width: 10), + Expanded( + child: Text(message, style: context.text.bodyMedium?.copyWith(color: context.status.critical)), + ), + ], + ), + ); + }, + data: (node) { + if (node == null) { + return const Padding( + padding: EdgeInsets.all(16), + child: Text('No connection selected.'), + ); + } + return NodeIdentityForm(node: node); + }, + ); + } +} + +class _Block extends StatelessWidget { + const _Block({required this.title, required this.child, this.trailing}); + + final String title; + final Widget child; + final Widget? trailing; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title, style: context.text.titleMedium), + trailing ?? const SizedBox.shrink(), + ], + ), + const SizedBox(height: 10), + Card(child: child), + ], + ); + } +} diff --git a/lib/features/settings/widgets/connection_form_dialog.dart b/lib/features/settings/widgets/connection_form_dialog.dart new file mode 100644 index 0000000..0b09809 --- /dev/null +++ b/lib/features/settings/widgets/connection_form_dialog.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/connections/connection.dart'; +import '../../../core/connections/connection_providers.dart'; + +/// Add/edit dialog for a saved [Connection]. Passing [existing] switches it +/// to edit mode; omitting it creates a new connection and makes it active +/// (the common "just point me at a node" first-run flow). +Future showConnectionFormDialog( + BuildContext context, { + Connection? existing, +}) { + return showDialog( + context: context, + builder: (context) => _ConnectionFormDialog(existing: existing), + ); +} + +class _ConnectionFormDialog extends ConsumerStatefulWidget { + const _ConnectionFormDialog({this.existing}); + + final Connection? existing; + + @override + ConsumerState<_ConnectionFormDialog> createState() => _ConnectionFormDialogState(); +} + +class _ConnectionFormDialogState extends ConsumerState<_ConnectionFormDialog> { + final _formKey = GlobalKey(); + late final _nameController = TextEditingController(text: widget.existing?.name); + late final _urlController = TextEditingController(text: widget.existing?.baseUrl); + bool _saving = false; + + @override + void dispose() { + _nameController.dispose(); + _urlController.dispose(); + super.dispose(); + } + + String? _validateUrl(String? value) { + final trimmed = value?.trim() ?? ''; + if (trimmed.isEmpty) return 'Required.'; + final uri = Uri.tryParse(trimmed); + if (uri == null || !uri.hasScheme || !uri.hasAuthority) { + return 'Enter a full URL, e.g. https://edge-01.local:8080'; + } + return null; + } + + Future _save() async { + if (!(_formKey.currentState?.validate() ?? false)) return; + setState(() => _saving = true); + + final name = _nameController.text.trim(); + var url = _urlController.text.trim(); + if (url.endsWith('/')) url = url.substring(0, url.length - 1); + + final existing = widget.existing; + if (existing != null) { + await ref + .read(savedConnectionsProvider.notifier) + .update(existing.copyWith(name: name, baseUrl: url)); + } else { + final connection = Connection( + id: 'conn_${DateTime.now().microsecondsSinceEpoch}', + name: name, + baseUrl: url, + ); + await ref.read(savedConnectionsProvider.notifier).add(connection); + await ref.read(activeConnectionIdProvider.notifier).setActive(connection.id); + } + + if (mounted) Navigator.of(context).pop(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.existing == null ? 'Add connection' : 'Edit connection'), + content: Form( + key: _formKey, + child: SizedBox( + width: 380, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: _nameController, + decoration: const InputDecoration(labelText: 'Name'), + validator: (v) => (v == null || v.trim().isEmpty) ? 'Required.' : null, + textInputAction: TextInputAction.next, + ), + const SizedBox(height: 14), + TextFormField( + controller: _urlController, + decoration: const InputDecoration( + labelText: 'Base URL', + hintText: 'https://edge-01.local:8080', + ), + validator: _validateUrl, + keyboardType: TextInputType.url, + onFieldSubmitted: (_) => _save(), + ), + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: _saving ? null : () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + FilledButton( + onPressed: _saving ? null : _save, + child: _saving + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : const Text('Save'), + ), + ], + ); + } +} diff --git a/lib/features/settings/widgets/connections_list.dart b/lib/features/settings/widgets/connections_list.dart new file mode 100644 index 0000000..14f1f68 --- /dev/null +++ b/lib/features/settings/widgets/connections_list.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/connections/connection.dart'; +import '../../../core/connections/connection_providers.dart'; +import '../../../core/theme/theme_x.dart'; +import 'connection_form_dialog.dart'; + +class ConnectionsList extends ConsumerWidget { + const ConnectionsList({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final connections = ref.watch(savedConnectionsProvider); + final activeId = ref.watch(activeConnectionIdProvider); + + if (connections.isEmpty) { + return Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'No connections saved yet.', + style: context.text.bodyMedium, + ), + const SizedBox(height: 4), + Text( + "Add one to start managing a NodeMaster host.", + style: context.text.bodySmall, + ), + ], + ), + ); + } + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final connection in connections) + _ConnectionRow( + connection: connection, + active: connection.id == activeId, + ), + ], + ); + } +} + +class _ConnectionRow extends ConsumerWidget { + const _ConnectionRow({required this.connection, required this.active}); + + final Connection connection; + final bool active; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return InkWell( + onTap: active + ? null + : () => ref.read(activeConnectionIdProvider.notifier).setActive(connection.id), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: active ? context.status.good : context.colors.outline, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + active ? '${connection.name} · active' : connection.name, + style: context.text.labelLarge, + ), + Text(connection.baseUrl, style: context.dataStyles.dataMonoSmall), + ], + ), + ), + IconButton( + tooltip: 'Edit', + icon: const Icon(Icons.edit_outlined, size: 18), + onPressed: () => showConnectionFormDialog(context, existing: connection), + ), + IconButton( + tooltip: 'Remove', + icon: const Icon(Icons.delete_outline, size: 18), + onPressed: () => ref.read(savedConnectionsProvider.notifier).remove(connection.id), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/settings/widgets/no_auth_callout.dart b/lib/features/settings/widgets/no_auth_callout.dart new file mode 100644 index 0000000..a2d3cb9 --- /dev/null +++ b/lib/features/settings/widgets/no_auth_callout.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../../../core/theme/theme_x.dart'; + +/// Static warning — NodeMaster's REST API has no built-in authentication. +/// Anyone who can reach a node's port can start/stop services or trigger +/// backups on it. Not dismissable: this is a standing property of the +/// backend, not a one-time notice. +class NoAuthCallout extends StatelessWidget { + const NoAuthCallout({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Color.alphaBlend(context.status.warning.withValues(alpha: 0.10), context.colors.surface), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: context.status.warning.withValues(alpha: 0.4)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.warning_amber_rounded, size: 18, color: context.status.warning), + const SizedBox(width: 12), + Expanded( + child: RichText( + text: TextSpan( + style: context.text.bodyMedium, + children: [ + TextSpan(text: 'No API authentication yet. ', style: const TextStyle(fontWeight: FontWeight.w700)), + const TextSpan( + text: "NodeMaster's REST API has no built-in auth — anyone who can reach a node's " + 'port can start, stop, or trigger backups on it. Until token auth ships, keep it ' + 'behind a VPN or a reverse proxy with its own access control, and treat saved ' + 'connection URLs as sensitive.', + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/settings/widgets/node_identity_form.dart b/lib/features/settings/widgets/node_identity_form.dart new file mode 100644 index 0000000..fedb546 --- /dev/null +++ b/lib/features/settings/widgets/node_identity_form.dart @@ -0,0 +1,159 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/network/api_exception.dart'; +import '../../../core/theme/status_colors.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/widgets/status_pill.dart'; +import '../../../data/repositories/node_repository.dart'; +import '../../dashboard/providers/dashboard_summary_provider.dart'; + +/// Edits `NodeInfo.hostname`/`description`/`resticPassword` (`PUT /node/`). +/// Takes the already-loaded [node] rather than watching the provider +/// itself, so the text fields have a stable initial value to build +/// controllers from — the parent only renders this once +/// `currentNodeProvider` has data. +class NodeIdentityForm extends ConsumerStatefulWidget { + const NodeIdentityForm({super.key, required this.node}); + + final NodeInfo node; + + @override + ConsumerState createState() => _NodeIdentityFormState(); +} + +class _NodeIdentityFormState extends ConsumerState { + late final _hostnameController = TextEditingController(text: widget.node.hostname); + late final _descriptionController = TextEditingController(text: widget.node.description); + final _passwordController = TextEditingController(); + bool _obscurePassword = true; + bool _saving = false; + String? _error; + + @override + void dispose() { + _hostnameController.dispose(); + _descriptionController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + Future _save() async { + setState(() { + _saving = true; + _error = null; + }); + + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) { + setState(() { + _saving = false; + _error = 'No active connection.'; + }); + return; + } + + final newPassword = _passwordController.text; + final draft = widget.node.copyWith( + hostname: _hostnameController.text.trim(), + description: _descriptionController.text.trim(), + resticPassword: newPassword.isEmpty ? null : newPassword, + ); + + try { + await repo.updateNode(draft); + // The password field is write-only — never echoed back by the API — + // so clear it locally once it's been sent rather than leaving the + // plaintext sitting in the form. + _passwordController.clear(); + ref.invalidate(currentNodeProvider); + ref.invalidate(dashboardSummaryProvider); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Saved'))); + } + } catch (e) { + setState(() { + _error = e is ApiException ? e.userMessage : e.toString(); + }); + } finally { + if (mounted) setState(() => _saving = false); + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: TextField( + controller: _hostnameController, + decoration: const InputDecoration(labelText: 'Hostname'), + ), + ), + const SizedBox(width: 14), + Expanded( + child: TextField( + controller: _descriptionController, + decoration: const InputDecoration(labelText: 'Description'), + ), + ), + ], + ), + const SizedBox(height: 18), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: TextField( + controller: _passwordController, + obscureText: _obscurePassword, + decoration: InputDecoration( + labelText: widget.node.resticPasswordSet ? 'Change restic password' : 'Set restic password', + hintText: widget.node.resticPasswordSet ? '••••••••' : 'Required to run restic backup targets', + suffixIcon: IconButton( + icon: Icon(_obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined), + onPressed: () => setState(() => _obscurePassword = !_obscurePassword), + ), + ), + ), + ), + const SizedBox(width: 14), + Padding( + padding: const EdgeInsets.only(top: 16), + child: StatusPill( + label: widget.node.resticPasswordSet ? 'Password set' : 'Not configured', + severity: widget.node.resticPasswordSet ? Severity.good : Severity.warning, + ), + ), + ], + ), + const SizedBox(height: 6), + Text( + 'Encrypts/decrypts every restic backup target on this node. Leave blank to keep the current ' + 'password — the API never sends it back, so this field always shows empty. There is no way to ' + 'unset it once configured.', + style: context.text.bodySmall?.copyWith(color: context.colors.onSurfaceVariant), + ), + if (_error != null) ...[ + const SizedBox(height: 10), + Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ], + const SizedBox(height: 14), + FilledButton( + onPressed: _saving ? null : _save, + child: _saving + ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) + : const Text('Save changes'), + ), + ], + ), + ); + } +} diff --git a/lib/features/settings/widgets/theme_toggle.dart b/lib/features/settings/widgets/theme_toggle.dart new file mode 100644 index 0000000..942a243 --- /dev/null +++ b/lib/features/settings/widgets/theme_toggle.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/theme/theme_mode_provider.dart'; + +/// The full three-way Light/Dark/System control. The top bar's quick +/// sun/moon icon (`NavRailShell`) only ever toggles between explicit +/// light/dark — this is the one place "follow system" can be chosen. +class ThemeToggle extends ConsumerWidget { + const ThemeToggle({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final mode = ref.watch(themeModeControllerProvider); + + return SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment(value: ThemeMode.light, label: Text('Light'), icon: Icon(Icons.light_mode_outlined, size: 16)), + ButtonSegment(value: ThemeMode.dark, label: Text('Dark'), icon: Icon(Icons.dark_mode_outlined, size: 16)), + ButtonSegment( + value: ThemeMode.system, + label: Text('System'), + icon: Icon(Icons.brightness_auto_outlined, size: 16), + ), + ], + selected: {mode}, + onSelectionChanged: (selected) => + ref.read(themeModeControllerProvider.notifier).setThemeMode(selected.first), + ); + } +} diff --git a/lib/features/updates/updates_screen.dart b/lib/features/updates/updates_screen.dart new file mode 100644 index 0000000..fa099c5 --- /dev/null +++ b/lib/features/updates/updates_screen.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/models/models.dart'; +import '../../core/network/api_exception.dart'; +import '../../core/theme/theme_x.dart'; +import '../../core/widgets/error_banner.dart'; +import '../../data/repositories/node_repository.dart'; +import 'widgets/updates_table.dart'; + +class UpdatesScreen extends ConsumerWidget { + const UpdatesScreen({super.key}); + + Future _delete(BuildContext context, WidgetRef ref, UpdateRecord record) async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Delete update record?'), + content: const Text('This removes the record from NodeMaster\'s log. It does not undo the update.'), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: context.status.critical), + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Delete'), + ), + ], + ), + ); + if (confirmed != true) return; + + final repo = ref.read(nodeRepositoryProvider); + if (repo == null) return; + try { + await repo.deleteUpdate(record.id); + ref.invalidate(updatesListProvider); + } catch (e) { + final message = e is ApiException ? e.userMessage : e.toString(); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final updatesAsync = ref.watch(updatesListProvider); + + return Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + updatesAsync.maybeWhen( + data: (records) => '${records.length} UPDATE RECORD(S)', + orElse: () => 'UPDATE HISTORY', + ), + style: context.text.titleSmall, + ), + const SizedBox(height: 12), + Expanded( + child: updatesAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Align( + alignment: Alignment.topCenter, + child: ErrorBanner( + message: error is ApiException ? error.userMessage : error.toString(), + onRetry: () => ref.invalidate(updatesListProvider), + ), + ), + data: (records) => SingleChildScrollView( + child: UpdatesTable( + records: records, + onDelete: (record) => _delete(context, ref, record), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/updates/widgets/updates_table.dart b/lib/features/updates/widgets/updates_table.dart new file mode 100644 index 0000000..631c111 --- /dev/null +++ b/lib/features/updates/widgets/updates_table.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +import '../../../core/models/models.dart'; +import '../../../core/models/severity_mapping.dart'; +import '../../../core/theme/theme_x.dart'; +import '../../../core/widgets/data_table_scaffold.dart'; +import '../../../core/widgets/empty_state.dart'; +import '../../../core/widgets/mono_text.dart'; +import '../../../core/widgets/status_pill.dart'; + +class UpdatesTable extends StatelessWidget { + const UpdatesTable({super.key, required this.records, required this.onDelete}); + + final List records; + final void Function(UpdateRecord record) onDelete; + + @override + Widget build(BuildContext context) { + final sorted = [...records]..sort((a, b) => b.timestamp.compareTo(a.timestamp)); + final timeFormat = DateFormat('yyyy-MM-dd HH:mm'); + + return DataTableScaffold( + empty: const EmptyState( + icon: Icons.history_rounded, + title: 'No update history yet', + message: 'OS package update runs will show up here.', + ), + columns: const [ + DataColumn(label: Text('Timestamp')), + DataColumn(label: Text('Result')), + DataColumn(label: Text('Packages')), + DataColumn(label: Text('Message')), + DataColumn(label: Text('')), + ], + rows: [ + for (final record in sorted) + DataRow( + cells: [ + DataCell(MonoText(timeFormat.format(record.timestamp.toLocal()))), + DataCell( + StatusPill( + label: record.success ? 'success' : 'failed', + severity: severityForSuccess(record.success), + ), + ), + DataCell(_PackageChips(packages: record.packages)), + DataCell( + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 320), + child: Text(record.message, maxLines: 1, overflow: TextOverflow.ellipsis), + ), + ), + DataCell( + IconButton( + tooltip: 'Delete', + icon: const Icon(Icons.delete_outline, size: 18), + onPressed: () => onDelete(record), + ), + ), + ], + ), + ], + ); + } +} + +class _PackageChips extends StatelessWidget { + const _PackageChips({required this.packages}); + + final List packages; + + static const _maxShown = 2; + + @override + Widget build(BuildContext context) { + if (packages.isEmpty) { + return Text('—', style: context.text.bodySmall); + } + final shown = packages.take(_maxShown).toList(); + final remaining = packages.length - shown.length; + + return Wrap( + spacing: 4, + runSpacing: 4, + children: [ + for (final package in shown) + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: context.colors.surfaceContainerHighest, + borderRadius: BorderRadius.circular(4), + ), + child: Text(package, style: context.dataStyles.dataMonoSmall), + ), + if (remaining > 0) + Text('+$remaining more', style: context.text.bodySmall), + ], + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..6f1857a --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'app.dart'; +import 'core/utils/shared_preferences_provider.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + final prefs = await SharedPreferences.getInstance(); + + runApp( + ProviderScope( + // Riverpod 3's container-wide default auto-retries any failed + // provider a handful of times with exponential backoff. This app + // already has its own deliberate refresh model (manual pull + + // PollingMixin for the two screens that need a timer), so the + // blanket default just adds a second, invisible retry loop behind + // every failed request — surprising, and it visibly fights the + // explicit one on a persistently-unreachable connection. Disabled + // here; opt back in per-provider with `@Riverpod(retry: ...)` if a + // specific one ever wants it. + retry: (retryCount, error) => null, + overrides: [sharedPreferencesProvider.overrideWithValue(prefs)], + child: const App(), + ), + ); +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..cd6b26c --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "manager") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.manager") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/runner/CMakeLists.txt b/linux/runner/CMakeLists.txt new file mode 100644 index 0000000..e97dabc --- /dev/null +++ b/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/linux/runner/main.cc b/linux/runner/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc new file mode 100644 index 0000000..f6a01c4 --- /dev/null +++ b/linux/runner/my_application.cc @@ -0,0 +1,148 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView* view) { + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "manager"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "manager"); + } + + gtk_window_set_default_size(window, 1280, 720); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 + // for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), + self); + gtk_widget_realize(GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/linux/runner/my_application.h b/linux/runner/my_application.h new file mode 100644 index 0000000..db16367 --- /dev/null +++ b/linux/runner/my_application.h @@ -0,0 +1,21 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, + my_application, + MY, + APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..724bb2a --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import shared_preferences_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) +} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6cac02c --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* manager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "manager.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* manager.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* manager.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.manager.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/manager.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/manager"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..3a1699d --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..b3c1761 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + +